@@ -1186,6 +1186,7 @@ load_base_config() {
11861186 BASE_USE_CLAUDE_CODE_SUBAGENTS=$( get_yaml_value " $BASE_DIR /config.yml" " use_claude_code_subagents" " true" )
11871187 BASE_AGENT_OS_COMMANDS=$( get_yaml_value " $BASE_DIR /config.yml" " agent_os_commands" " false" )
11881188 BASE_STANDARDS_AS_CLAUDE_CODE_SKILLS=$( get_yaml_value " $BASE_DIR /config.yml" " standards_as_claude_code_skills" " true" )
1189+ BASE_FACTORY_AI_DROIDS=$( get_yaml_value " $BASE_DIR /config.yml" " factory_ai_droids" " false" )
11891190
11901191 # Check for old config flags to set variables for validation
11911192 MULTI_AGENT_MODE=$( get_yaml_value " $BASE_DIR /config.yml" " multi_agent_mode" " " )
@@ -1201,6 +1202,7 @@ load_project_config() {
12011202 PROJECT_USE_CLAUDE_CODE_SUBAGENTS=$( get_project_config " $PROJECT_DIR " " use_claude_code_subagents" )
12021203 PROJECT_AGENT_OS_COMMANDS=$( get_project_config " $PROJECT_DIR " " agent_os_commands" )
12031204 PROJECT_STANDARDS_AS_CLAUDE_CODE_SKILLS=$( get_project_config " $PROJECT_DIR " " standards_as_claude_code_skills" )
1205+ PROJECT_FACTORY_AI_DROIDS=$( get_project_config " $PROJECT_DIR " " factory_ai_droids" )
12041206
12051207 # Check for old config flags to set variables for validation
12061208 MULTI_AGENT_MODE=$( get_project_config " $PROJECT_DIR " " multi_agent_mode" )
@@ -1256,6 +1258,7 @@ write_project_config() {
12561258 local use_claude_code_subagents=$4
12571259 local agent_os_commands=$5
12581260 local standards_as_claude_code_skills=$6
1261+ local factory_ai_droids=$7
12591262 local dest=" $PROJECT_DIR /agent-os/config.yml"
12601263
12611264 local config_content=" version: $version
@@ -1270,7 +1273,8 @@ profile: $profile
12701273claude_code_commands: $claude_code_commands
12711274use_claude_code_subagents: $use_claude_code_subagents
12721275agent_os_commands: $agent_os_commands
1273- standards_as_claude_code_skills: $standards_as_claude_code_skills "
1276+ standards_as_claude_code_skills: $standards_as_claude_code_skills
1277+ factory_ai_droids: $factory_ai_droids "
12741278
12751279 local result=$( write_file " $config_content " " $dest " )
12761280 if [[ " $DRY_RUN " == " true" ]]; then
@@ -1466,3 +1470,269 @@ install_improve_skills_command() {
14661470 fi
14671471 fi
14681472}
1473+
1474+ # -----------------------------------------------------------------------------
1475+ # Factory AI Droid Functions
1476+ # -----------------------------------------------------------------------------
1477+
1478+ # Map agent-os tool names to Factory AI tool names
1479+ map_tools_to_factory () {
1480+ local tools=$1
1481+ # Remove the brackets and split by comma
1482+ local tools_array=$( echo " $tools " | sed ' s/\[//g' | sed ' s/\]//g' | sed ' s/, */ /g' )
1483+ local factory_tools=()
1484+
1485+ for tool in $tools_array ; do
1486+ case " $tool " in
1487+ " Write" )
1488+ # Map Write to standard Factory edit tools + exploration tools
1489+ factory_tools+=(" Read" " LS" " Grep" " Glob" " Create" " Edit" " MultiEdit" )
1490+ ;;
1491+ " Read" )
1492+ # Map Read to Factory read-only tools
1493+ factory_tools+=(" Read" " LS" " Grep" " Glob" )
1494+ ;;
1495+ " Bash" )
1496+ # Map Bash to Factory Execute
1497+ factory_tools+=(" Execute" )
1498+ ;;
1499+ " WebFetch" )
1500+ # Map WebFetch to Factory web tools (but exclude for most droids)
1501+ # Don't add by default - let droids that need it specify explicitly
1502+ ;;
1503+ " Playwright" )
1504+ # Don't include Playwright tools - not standard Factory tools
1505+ # Browsers should be used via WebSearch/FetchUrl instead
1506+ ;;
1507+ * )
1508+ # Pass through any other tool names as-is
1509+ factory_tools+=(" $tool " )
1510+ ;;
1511+ esac
1512+ done
1513+
1514+ # Remove duplicates and format as JSON array
1515+ local unique_tools=($( echo " ${factory_tools[@]} " | tr ' ' ' \n' | sort -u | tr ' \n' ' ' ) )
1516+ local json_tools=" ["
1517+ local first=true
1518+ for tool in " ${unique_tools[@]} " ; do
1519+ if [[ " $first " == " true" ]]; then
1520+ json_tools+=" \" $tool \" "
1521+ first=false
1522+ else
1523+ json_tools+=" , \" $tool \" "
1524+ fi
1525+ done
1526+ json_tools+=" ]"
1527+ echo " $json_tools "
1528+ }
1529+
1530+ # Create a Factory AI droid from an agent file
1531+ # Args: $1=agent file path (relative to profile, e.g., "agents/implementer.md")
1532+ # $2=dest base directory (project directory)
1533+ # $3=base directory (~/agent-os)
1534+ # $4=profile name
1535+ create_factory_droid () {
1536+ local agent_file=$1
1537+ local dest_base=$2
1538+ local base_dir=$3
1539+ local profile=$4
1540+
1541+ # Get the full path to the agent file
1542+ local source_file=$( get_profile_file " $profile " " $agent_file " " $base_dir " )
1543+ if [[ ! -f " $source_file " ]]; then
1544+ print_error " Agent file not found: $source_file "
1545+ return 1
1546+ fi
1547+
1548+ # Extract agent name from filename
1549+ local agent_name=$( basename " $agent_file " .md)
1550+
1551+ # Read the agent file and extract frontmatter
1552+ local agent_content=$( cat " $source_file " )
1553+ local description=" "
1554+ local tools=" "
1555+ local model=" inherit"
1556+
1557+ # Extract frontmatter using awk - extract everything between first and second ---
1558+ description=$( awk ' BEGIN{count=0} /^---$/{count++; next} count==1 && /^description:/{sub(/^description:[[:space:]]*/, ""); print; exit}' " $source_file " )
1559+ tools=$( awk ' BEGIN{count=0} /^---$/{count++; next} count==1 && /^tools:/{sub(/^tools:[[:space:]]*/, ""); print; exit}' " $source_file " )
1560+
1561+ # Extract content after frontmatter (everything after second ---)
1562+ local droid_content=$( echo " $agent_content " | awk ' /^---$/ {count++; next} count >= 2 {print}' )
1563+
1564+ # Compile the droid content (process workflows, standards, conditionals)
1565+ # First write to a temp file
1566+ local temp_file=$( mktemp)
1567+ echo " $droid_content " > " $temp_file "
1568+
1569+ # Now compile it
1570+ local compiled_content=$( compile_agent " $temp_file " " $temp_file .compiled" " $base_dir " " $profile " " " " " )
1571+ compiled_content=$( cat " $temp_file .compiled" 2> /dev/null || echo " $droid_content " )
1572+ rm -f " $temp_file " " $temp_file .compiled"
1573+
1574+ # Map tools to Factory AI tool names
1575+ local factory_tools=$( map_tools_to_factory " $tools " )
1576+
1577+ # Get the droid template
1578+ local template_file=$( get_profile_file " $profile " " factory-ai-droid-template.md" " $base_dir " )
1579+ if [[ ! -f " $template_file " ]]; then
1580+ print_error " Factory AI droid template not found: $template_file "
1581+ return 1
1582+ fi
1583+
1584+ # Read template and replace placeholders
1585+ local droid_file_content=$( cat " $template_file " )
1586+ droid_file_content=$( echo " $droid_file_content " | sed " s|{{agent_name}}|$agent_name |g" )
1587+ droid_file_content=$( echo " $droid_file_content " | sed " s|{{agent_description}}|$description |g" )
1588+ droid_file_content=$( echo " $droid_file_content " | sed " s|{{agent_tools}}|$factory_tools |g" )
1589+
1590+ # Replace content placeholder (handle multiline)
1591+ local temp_template=$( mktemp)
1592+ local temp_content=$( mktemp)
1593+ echo " $droid_file_content " > " $temp_template "
1594+ echo " $compiled_content " > " $temp_content "
1595+
1596+ droid_file_content=$( perl -e '
1597+ use strict;
1598+ use warnings;
1599+
1600+ my $content_file = $ARGV[0];
1601+ my $template_file = $ARGV[1];
1602+
1603+ # Read compiled content
1604+ open(my $fh, "<", $content_file) or die $!;
1605+ my $content = do { local $/; <$fh> };
1606+ close($fh);
1607+ chomp $content;
1608+
1609+ # Read template
1610+ open($fh, "<", $template_file) or die $!;
1611+ my $template = do { local $/; <$fh> };
1612+ close($fh);
1613+
1614+ # Replace placeholder
1615+ $template =~ s/\{\{agent_content\}\}/$content/g;
1616+
1617+ print $template;
1618+ ' " $temp_content " " $temp_template " )
1619+
1620+ rm -f " $temp_template " " $temp_content "
1621+
1622+ # Create droid directory
1623+ local droid_dir=" $dest_base /.factory/droids"
1624+ ensure_dir " $droid_dir "
1625+
1626+ # Write droid file
1627+ local droid_file=" $droid_dir /$agent_name .md"
1628+ if [[ " $DRY_RUN " == " true" ]]; then
1629+ echo " $droid_file "
1630+ else
1631+ echo " $droid_file_content " > " $droid_file "
1632+ print_verbose " Created Factory AI droid: $droid_file "
1633+ fi
1634+ }
1635+
1636+ # Create a Factory AI command that delegates to a droid
1637+ # Args: $1=command file path (relative to profile)
1638+ # $2=dest base directory (project directory)
1639+ # $3=base directory (~/agent-os)
1640+ # $4=profile name
1641+ create_factory_command () {
1642+ local command_file=$1
1643+ local dest_base=$2
1644+ local base_dir=$3
1645+ local profile=$4
1646+
1647+ # Get the full path to the command file
1648+ local source_file=$( get_profile_file " $profile " " $command_file " " $base_dir " )
1649+ if [[ ! -f " $source_file " ]]; then
1650+ print_verbose " Command file not found: $source_file "
1651+ return 0
1652+ fi
1653+
1654+ # Extract command name from path
1655+ # e.g., commands/plan-product/multi-agent/plan-product.md -> plan-product
1656+ local command_name=$( echo " $command_file " | cut -d' /' -f2)
1657+
1658+ # Create command directory
1659+ local command_dir=" $dest_base /.factory/commands"
1660+ ensure_dir " $command_dir "
1661+
1662+ # Compile the command content (process workflows, standards, conditionals)
1663+ local temp_file=$( mktemp)
1664+ compile_command " $source_file " " $temp_file " " $base_dir " " $profile " " "
1665+ local command_content=$( cat " $temp_file " )
1666+ rm -f " $temp_file "
1667+
1668+ # Generate description from command name
1669+ local description=$( echo " $command_name " | sed ' s/-/ /g' | awk ' {for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) tolower(substr($i,2))}1' )
1670+
1671+ # Add YAML frontmatter for Factory AI slash command
1672+ local dest_file=" $command_dir /$command_name .md"
1673+ if [[ " $DRY_RUN " == " true" ]]; then
1674+ echo " $dest_file "
1675+ else
1676+ cat > " $dest_file " << EOF
1677+ ---
1678+ name: $command_name
1679+ description: $description
1680+ ---
1681+
1682+ $command_content
1683+ EOF
1684+ print_verbose " Created Factory AI command: $dest_file "
1685+ fi
1686+ }
1687+
1688+ # Install Factory AI droids and commands
1689+ install_factory_droids () {
1690+ # Only install droids if Factory AI is enabled
1691+ if [[ " $EFFECTIVE_FACTORY_AI_DROIDS " != " true" ]]; then
1692+ return 0
1693+ fi
1694+
1695+ if [[ " $DRY_RUN " != " true" ]]; then
1696+ print_status " Installing Factory AI droids and commands..."
1697+ fi
1698+
1699+ local droids_count=0
1700+ local commands_count=0
1701+
1702+ # Install droids from agent files
1703+ while read file; do
1704+ if [[ " $file " == agents/* ]] && [[ " $file " == * .md ]]; then
1705+ # Create droid from this agent file
1706+ create_factory_droid " $file " " $PROJECT_DIR " " $BASE_DIR " " $EFFECTIVE_PROFILE "
1707+
1708+ # Track the droid file for dry run
1709+ local agent_name=$( basename " $file " .md)
1710+ local droid_file=" $PROJECT_DIR /.factory/droids/$agent_name .md"
1711+ if [[ " $DRY_RUN " == " true" ]]; then
1712+ INSTALLED_FILES+=(" $droid_file " )
1713+ fi
1714+ (( droids_count++ )) || true
1715+ fi
1716+ done < <( get_profile_files " $EFFECTIVE_PROFILE " " $BASE_DIR " " agents" )
1717+
1718+ # Install commands (slash commands that delegate to droids)
1719+ while read file; do
1720+ # Use multi-agent commands for Factory AI (they use Task tool delegation)
1721+ if [[ " $file " == commands/* /multi-agent/* ]] || [[ " $file " == commands/orchestrate-tasks/orchestrate-tasks.md ]]; then
1722+ create_factory_command " $file " " $PROJECT_DIR " " $BASE_DIR " " $EFFECTIVE_PROFILE "
1723+
1724+ if [[ " $DRY_RUN " == " true" ]]; then
1725+ local command_name=$( echo " $file " | cut -d' /' -f2)
1726+ INSTALLED_FILES+=(" $PROJECT_DIR /.factory/commands/$command_name .md" )
1727+ fi
1728+ (( commands_count++ )) || true
1729+ fi
1730+ done < <( get_profile_files " $EFFECTIVE_PROFILE " " $BASE_DIR " " commands" )
1731+
1732+ if [[ " $DRY_RUN " != " true" ]]; then
1733+ if [[ $droids_count -gt 0 ]] || [[ $commands_count -gt 0 ]]; then
1734+ echo " ✓ Installed $droids_count Factory AI droids and $commands_count commands"
1735+ echo -e " ${YELLOW} 👉 Enable Custom Droids in Factory AI settings, then use slash commands like /plan-product${NC} "
1736+ fi
1737+ fi
1738+ }
0 commit comments