squads-cli 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +161 -4
  2. package/dist/{chunk-HKWCBCEK.js → chunk-4CMAEQQY.js} +6 -2
  3. package/dist/chunk-4CMAEQQY.js.map +1 -0
  4. package/dist/{chunk-NA3IECJA.js → chunk-N7KDWU4W.js} +155 -58
  5. package/dist/chunk-N7KDWU4W.js.map +1 -0
  6. package/dist/{chunk-7PRYDHZW.js → chunk-NHGLXN2F.js} +8 -6
  7. package/dist/chunk-NHGLXN2F.js.map +1 -0
  8. package/dist/{chunk-QPH5OR7J.js → chunk-O7UV3FWI.js} +139 -21
  9. package/dist/chunk-O7UV3FWI.js.map +1 -0
  10. package/dist/{chunk-BV6S5AWZ.js → chunk-ZTQ7ISUR.js} +28 -109
  11. package/dist/chunk-ZTQ7ISUR.js.map +1 -0
  12. package/dist/cli.js +5493 -7665
  13. package/dist/cli.js.map +1 -1
  14. package/dist/index.d.ts +110 -2
  15. package/dist/index.js +302 -26
  16. package/dist/index.js.map +1 -1
  17. package/dist/{memory-ZXDXF6KF.js → memory-VNF2VFRB.js} +2 -2
  18. package/dist/{sessions-F6LRY7EN.js → sessions-6PB7ALCE.js} +3 -3
  19. package/dist/{squad-parser-MSYE4PXL.js → squad-parser-4BI3G4RS.js} +4 -2
  20. package/dist/templates/seed/BUSINESS_BRIEF.md.template +27 -0
  21. package/dist/templates/seed/CLAUDE.md.template +69 -0
  22. package/dist/templates/seed/config/provider.yaml +4 -0
  23. package/dist/templates/seed/hooks/settings.json.template +31 -0
  24. package/dist/templates/seed/memory/company/manager/state.md +16 -0
  25. package/dist/templates/seed/memory/engineering/issue-solver/state.md +12 -0
  26. package/dist/templates/seed/memory/intelligence/intel-lead/state.md +9 -0
  27. package/dist/templates/seed/memory/marketing/content-drafter/state.md +12 -0
  28. package/dist/templates/seed/memory/operations/ops-lead/state.md +12 -0
  29. package/dist/templates/seed/memory/research/researcher/state.md +10 -0
  30. package/dist/templates/seed/skills/gh/SKILL.md +57 -0
  31. package/dist/templates/seed/skills/squads-cli/SKILL.md +88 -0
  32. package/dist/templates/seed/squads/company/SQUAD.md +49 -0
  33. package/dist/templates/seed/squads/company/company-critic.md +21 -0
  34. package/dist/templates/seed/squads/company/company-eval.md +21 -0
  35. package/dist/templates/seed/squads/company/event-dispatcher.md +21 -0
  36. package/dist/templates/seed/squads/company/goal-tracker.md +21 -0
  37. package/dist/templates/seed/squads/company/manager.md +66 -0
  38. package/dist/templates/seed/squads/engineering/SQUAD.md +48 -0
  39. package/dist/templates/seed/squads/engineering/code-reviewer.md +57 -0
  40. package/dist/templates/seed/squads/engineering/issue-solver.md +58 -0
  41. package/dist/templates/seed/squads/engineering/test-writer.md +50 -0
  42. package/dist/templates/seed/squads/intelligence/SQUAD.md +37 -0
  43. package/dist/templates/seed/squads/intelligence/intel-critic.md +36 -0
  44. package/dist/templates/seed/squads/intelligence/intel-eval.md +31 -0
  45. package/dist/templates/seed/squads/intelligence/intel-lead.md +71 -0
  46. package/dist/templates/seed/squads/marketing/SQUAD.md +47 -0
  47. package/dist/templates/seed/squads/marketing/content-drafter.md +71 -0
  48. package/dist/templates/seed/squads/marketing/growth-analyst.md +49 -0
  49. package/dist/templates/seed/squads/marketing/social-poster.md +44 -0
  50. package/dist/templates/seed/squads/operations/SQUAD.md +45 -0
  51. package/dist/templates/seed/squads/operations/finance-tracker.md +47 -0
  52. package/dist/templates/seed/squads/operations/goal-tracker.md +48 -0
  53. package/dist/templates/seed/squads/operations/ops-lead.md +58 -0
  54. package/dist/templates/seed/squads/research/SQUAD.md +38 -0
  55. package/dist/templates/seed/squads/research/analyst.md +27 -0
  56. package/dist/templates/seed/squads/research/research-critic.md +20 -0
  57. package/dist/templates/seed/squads/research/research-eval.md +20 -0
  58. package/dist/templates/seed/squads/research/researcher.md +28 -0
  59. package/dist/{terminal-JZSAQSN7.js → terminal-YKA4O5CX.js} +4 -2
  60. package/dist/{update-MAY6EXFQ.js → update-ALJKFFM7.js} +3 -2
  61. package/package.json +9 -22
  62. package/templates/seed/BUSINESS_BRIEF.md.template +27 -0
  63. package/templates/seed/CLAUDE.md.template +69 -0
  64. package/templates/seed/config/provider.yaml +4 -0
  65. package/templates/seed/hooks/settings.json.template +31 -0
  66. package/templates/seed/memory/company/manager/state.md +16 -0
  67. package/templates/seed/memory/engineering/issue-solver/state.md +12 -0
  68. package/templates/seed/memory/intelligence/intel-lead/state.md +9 -0
  69. package/templates/seed/memory/marketing/content-drafter/state.md +12 -0
  70. package/templates/seed/memory/operations/ops-lead/state.md +12 -0
  71. package/templates/seed/memory/research/researcher/state.md +10 -0
  72. package/templates/seed/skills/gh/SKILL.md +57 -0
  73. package/templates/seed/skills/squads-cli/SKILL.md +88 -0
  74. package/templates/seed/squads/company/SQUAD.md +49 -0
  75. package/templates/seed/squads/company/company-critic.md +21 -0
  76. package/templates/seed/squads/company/company-eval.md +21 -0
  77. package/templates/seed/squads/company/event-dispatcher.md +21 -0
  78. package/templates/seed/squads/company/goal-tracker.md +21 -0
  79. package/templates/seed/squads/company/manager.md +66 -0
  80. package/templates/seed/squads/engineering/SQUAD.md +48 -0
  81. package/templates/seed/squads/engineering/code-reviewer.md +57 -0
  82. package/templates/seed/squads/engineering/issue-solver.md +58 -0
  83. package/templates/seed/squads/engineering/test-writer.md +50 -0
  84. package/templates/seed/squads/intelligence/SQUAD.md +37 -0
  85. package/templates/seed/squads/intelligence/intel-critic.md +36 -0
  86. package/templates/seed/squads/intelligence/intel-eval.md +31 -0
  87. package/templates/seed/squads/intelligence/intel-lead.md +71 -0
  88. package/templates/seed/squads/marketing/SQUAD.md +47 -0
  89. package/templates/seed/squads/marketing/content-drafter.md +71 -0
  90. package/templates/seed/squads/marketing/growth-analyst.md +49 -0
  91. package/templates/seed/squads/marketing/social-poster.md +44 -0
  92. package/templates/seed/squads/operations/SQUAD.md +45 -0
  93. package/templates/seed/squads/operations/finance-tracker.md +47 -0
  94. package/templates/seed/squads/operations/goal-tracker.md +48 -0
  95. package/templates/seed/squads/operations/ops-lead.md +58 -0
  96. package/templates/seed/squads/research/SQUAD.md +38 -0
  97. package/templates/seed/squads/research/analyst.md +27 -0
  98. package/templates/seed/squads/research/research-critic.md +20 -0
  99. package/templates/seed/squads/research/research-eval.md +20 -0
  100. package/templates/seed/squads/research/researcher.md +28 -0
  101. package/dist/chunk-7PRYDHZW.js.map +0 -1
  102. package/dist/chunk-BV6S5AWZ.js.map +0 -1
  103. package/dist/chunk-HKWCBCEK.js.map +0 -1
  104. package/dist/chunk-NA3IECJA.js.map +0 -1
  105. package/dist/chunk-QPH5OR7J.js.map +0 -1
  106. package/docker/.env.example +0 -17
  107. package/docker/README.md +0 -92
  108. package/docker/docker-compose.engram.yml +0 -304
  109. package/docker/docker-compose.yml +0 -250
  110. package/docker/init-db.sql +0 -478
  111. package/docker/init-engram-db.sql +0 -148
  112. package/docker/init-langfuse-db.sh +0 -10
  113. package/docker/otel-collector.yaml +0 -34
  114. package/docker/squads-bridge/Dockerfile +0 -14
  115. package/docker/squads-bridge/Dockerfile.proxy +0 -14
  116. package/docker/squads-bridge/anthropic_proxy.py +0 -313
  117. package/docker/squads-bridge/requirements.txt +0 -7
  118. package/docker/squads-bridge/squads_bridge.py +0 -2299
  119. package/docker/telemetry-ping/Dockerfile +0 -10
  120. package/docker/telemetry-ping/deploy.sh +0 -69
  121. package/docker/telemetry-ping/main.py +0 -136
  122. package/docker/telemetry-ping/requirements.txt +0 -3
  123. /package/dist/{memory-ZXDXF6KF.js.map → memory-VNF2VFRB.js.map} +0 -0
  124. /package/dist/{sessions-F6LRY7EN.js.map → sessions-6PB7ALCE.js.map} +0 -0
  125. /package/dist/{squad-parser-MSYE4PXL.js.map → squad-parser-4BI3G4RS.js.map} +0 -0
  126. /package/dist/{terminal-JZSAQSN7.js.map → terminal-YKA4O5CX.js.map} +0 -0
  127. /package/dist/{update-MAY6EXFQ.js.map → update-ALJKFFM7.js.map} +0 -0
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/lib/squad-parser.ts","../src/lib/condenser/tokens.ts","../src/lib/condenser/deduplication.ts","../src/lib/condenser/pruning.ts","../src/lib/condenser/summarizer.ts","../src/lib/condenser/index.ts"],"sourcesContent":["import { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json');\nexport const version: string = pkg.version;\n","import { readFileSync, existsSync, readdirSync, writeFileSync } from 'fs';\nimport { join, basename, dirname } from 'path';\nimport matter from 'gray-matter';\nimport { resolveMcpConfig, type McpResolution } from './mcp-config.js';\n\nexport type EffortLevel = 'high' | 'medium' | 'low';\n\n// Context schema for frontmatter\nexport interface SquadContext {\n mcp?: string[];\n skills?: string[];\n memory?: {\n load?: string[];\n };\n model?: {\n default?: string;\n expensive?: string;\n cheap?: string;\n };\n budget?: {\n daily?: number;\n weekly?: number;\n perExecution?: number;\n };\n /** Cooldown between executions in seconds */\n cooldown?: number;\n}\n\n// Multi-LLM provider configuration\nexport interface SquadProviders {\n /** Default provider for all agents (default: anthropic) */\n default?: string;\n /** Provider for vision/image tasks */\n vision?: string;\n /** Provider for real-time data access */\n realtime?: string;\n /** Provider for high-volume/cheap operations */\n cheap?: string;\n /** Custom provider mappings by purpose */\n [key: string]: string | undefined;\n}\n\n// Frontmatter schema\nexport interface SquadFrontmatter {\n name?: string;\n mission?: string;\n repo?: string;\n stack?: string;\n context?: SquadContext;\n effort?: EffortLevel;\n /** Multi-LLM provider configuration */\n providers?: SquadProviders;\n}\n\nexport interface Agent {\n name: string;\n role: string;\n trigger: string;\n status?: string;\n filePath?: string;\n squad?: string;\n effort?: EffortLevel;\n /** LLM provider override (from agent file frontmatter) */\n provider?: string;\n /** Agent purpose (short description) */\n purpose?: string;\n /** Cron schedule for scheduled agents */\n schedule?: string;\n /** Output destinations */\n outputs?: string[];\n}\n\nexport interface Pipeline {\n name: string;\n agents: string[];\n}\n\nexport interface Goal {\n description: string;\n completed: boolean;\n progress?: string;\n metrics?: string[];\n}\n\n/**\n * Routine definition for autonomous scheduled execution.\n * Defined in SQUAD.md under ### Routines yaml block.\n */\nexport interface Routine {\n /** Unique name for the routine */\n name: string;\n /** Cron schedule (e.g., \"0 8 * * *\" for daily 8am) */\n schedule: string;\n /** Agents to run in this batch */\n agents: string[];\n /** Model to use (defaults to squad default or sonnet) */\n model?: 'opus' | 'sonnet' | 'haiku';\n /** Whether the routine is enabled */\n enabled?: boolean;\n /** Priority for execution ordering (lower = higher priority) */\n priority?: number;\n /** Minimum cooldown between runs (e.g., \"6 hours\") */\n cooldown?: string;\n}\n\nexport interface Squad {\n name: string;\n mission: string;\n agents: Agent[];\n pipelines: Pipeline[];\n triggers: {\n scheduled: string[];\n event: string[];\n manual: string[];\n };\n /** Autonomous routines for scheduled batch execution */\n routines: Routine[];\n dependencies: string[];\n outputPath: string;\n goals: Goal[];\n effort?: EffortLevel; // Squad-level default effort\n context?: SquadContext; // Frontmatter context block\n repo?: string;\n stack?: string;\n /** Multi-LLM provider configuration */\n providers?: SquadProviders;\n /** Domain this squad operates in */\n domain?: string;\n /** Permissions for this squad */\n permissions?: Record<string, boolean>;\n /** Raw frontmatter for accessing KPIs and other custom fields */\n frontmatter?: Record<string, unknown>;\n}\n\n/**\n * Resolved execution context with paths and metadata.\n * Extends SquadContext with resolved paths for MCP, skills, and memory.\n */\nexport interface ExecutionContext extends SquadContext {\n /** Squad name this context belongs to */\n squadName: string;\n /** Resolved paths and metadata */\n resolved: {\n /** Path to MCP config file to use */\n mcpConfigPath: string;\n /** Source of MCP config resolution */\n mcpSource: 'user-override' | 'generated' | 'fallback';\n /** List of MCP servers in the config */\n mcpServers: string[];\n /** Resolved skill directory paths */\n skillPaths: string[];\n /** Resolved memory file paths */\n memoryPaths: string[];\n };\n}\n\nexport function findSquadsDir(): string | null {\n // Look for .agents/squads in current directory or parent directories\n let dir = process.cwd();\n\n for (let i = 0; i < 5; i++) {\n const squadsPath = join(dir, '.agents', 'squads');\n if (existsSync(squadsPath)) {\n return squadsPath;\n }\n const parent = join(dir, '..');\n if (parent === dir) break;\n dir = parent;\n }\n\n return null;\n}\n\nexport function findProjectRoot(): string | null {\n // Find the root of the squads project (where .agents/ lives)\n const squadsDir = findSquadsDir();\n if (!squadsDir) return null;\n // squadsDir is /path/to/.agents/squads, so go up 2 levels\n return join(squadsDir, '..', '..');\n}\n\nexport function hasLocalInfraConfig(): boolean {\n // Check if the project has a local .env file with infra config\n const projectRoot = findProjectRoot();\n if (!projectRoot) return false;\n\n const envPath = join(projectRoot, '.env');\n if (!existsSync(envPath)) return false;\n\n // Check if .env has any infra-related keys\n const content = readFileSync(envPath, 'utf-8');\n const infraKeys = ['LANGFUSE_', 'SQUADS_BRIDGE', 'SQUADS_POSTGRES', 'SQUADS_REDIS'];\n return infraKeys.some(key => content.includes(key));\n}\n\nexport function listSquads(squadsDir: string): string[] {\n const squads: string[] = [];\n\n const entries = readdirSync(squadsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('_')) {\n const squadFile = join(squadsDir, entry.name, 'SQUAD.md');\n if (existsSync(squadFile)) {\n squads.push(entry.name);\n }\n }\n }\n\n return squads;\n}\n\nexport function listAgents(squadsDir: string, squadName?: string): Agent[] {\n const agents: Agent[] = [];\n\n const dirs = squadName\n ? [squadName]\n : readdirSync(squadsDir, { withFileTypes: true })\n .filter(e => e.isDirectory() && !e.name.startsWith('_'))\n .map(e => e.name);\n\n for (const dir of dirs) {\n const squadPath = join(squadsDir, dir);\n if (!existsSync(squadPath)) continue;\n\n const files = readdirSync(squadPath);\n for (const file of files) {\n if (file.endsWith('.md') && file !== 'SQUAD.md') {\n const agentName = file.replace('.md', '');\n agents.push({\n name: agentName,\n role: `Agent in ${dir}`,\n trigger: 'manual',\n filePath: join(squadPath, file)\n });\n }\n }\n }\n\n return agents;\n}\n\nexport function parseSquadFile(filePath: string): Squad {\n const rawContent = readFileSync(filePath, 'utf-8');\n\n // Parse frontmatter with gray-matter\n const { data: frontmatter, content: bodyContent } = matter(rawContent);\n const fm = frontmatter as SquadFrontmatter;\n\n const lines = bodyContent.split('\\n');\n\n const squad: Squad = {\n // Use directory name (e.g., \"demo\" from \".agents/squads/demo/SQUAD.md\")\n name: fm.name || basename(dirname(filePath)),\n mission: fm.mission || '',\n agents: [],\n pipelines: [],\n triggers: { scheduled: [], event: [], manual: [] },\n routines: [],\n dependencies: [],\n outputPath: '',\n goals: [],\n // Apply frontmatter fields\n effort: fm.effort,\n context: fm.context,\n repo: fm.repo,\n stack: fm.stack,\n providers: fm.providers,\n // Preserve raw frontmatter for KPIs and other custom fields\n frontmatter: frontmatter as Record<string, unknown>,\n };\n\n let currentSection = '';\n let inTable = false;\n let tableHeaders: string[] = [];\n\n for (const line of lines) {\n // Extract squad name from title\n if (line.startsWith('# Squad:')) {\n squad.name = line.replace('# Squad:', '').trim().toLowerCase();\n continue;\n }\n\n // Track sections\n if (line.startsWith('## ')) {\n currentSection = line.replace('## ', '').trim().toLowerCase();\n inTable = false;\n continue;\n }\n\n // Extract mission\n if (currentSection === 'mission' && line.trim() && !line.startsWith('#')) {\n if (!squad.mission) {\n squad.mission = line.trim();\n }\n }\n\n // Extract squad-level effort (e.g., \"effort: medium\" in Context section)\n const effortMatch = line.match(/^effort:\\s*(high|medium|low)/i);\n if (effortMatch && !squad.effort) {\n squad.effort = effortMatch[1].toLowerCase() as EffortLevel;\n }\n\n // Parse agent tables\n if (currentSection.includes('agent') || currentSection.includes('orchestrator') ||\n currentSection.includes('evaluator') || currentSection.includes('builder') ||\n currentSection.includes('priority')) {\n\n if (line.includes('|') && line.includes('Agent')) {\n inTable = true;\n tableHeaders = line.split('|').map(h => h.trim().toLowerCase());\n continue;\n }\n\n if (inTable && line.includes('|') && !line.includes('---')) {\n const cells = line.split('|').map(c => c.trim().replace(/`/g, ''));\n const agentIdx = tableHeaders.findIndex(h => h === 'agent');\n const roleIdx = tableHeaders.findIndex(h => h === 'role');\n const triggerIdx = tableHeaders.findIndex(h => h === 'trigger');\n const statusIdx = tableHeaders.findIndex(h => h === 'status');\n const effortIdx = tableHeaders.findIndex(h => h === 'effort');\n\n if (agentIdx >= 0 && cells[agentIdx]) {\n const effortValue = effortIdx >= 0 ? cells[effortIdx]?.toLowerCase() : undefined;\n const effort = ['high', 'medium', 'low'].includes(effortValue || '')\n ? effortValue as EffortLevel\n : undefined;\n\n squad.agents.push({\n name: cells[agentIdx],\n role: roleIdx >= 0 ? cells[roleIdx] : '',\n trigger: triggerIdx >= 0 ? cells[triggerIdx] : 'manual',\n status: statusIdx >= 0 ? cells[statusIdx] : 'active',\n effort\n });\n }\n }\n }\n\n // Parse pipelines (looking for patterns like: agent1 → agent2 → agent3)\n if (line.includes('→') && line.includes('`')) {\n const pipelineMatch = line.match(/`([^`]+)`\\s*→\\s*`([^`]+)`/g);\n if (pipelineMatch) {\n const agentNames = line.match(/`([^`]+)`/g)?.map(m => m.replace(/`/g, '')) || [];\n if (agentNames.length >= 2) {\n squad.pipelines.push({\n name: 'default',\n agents: agentNames\n });\n }\n }\n }\n\n // Also look for Pipeline: format\n if (line.toLowerCase().includes('pipeline:')) {\n const pipelineContent = line.split(':')[1];\n if (pipelineContent && pipelineContent.includes('→')) {\n const agentNames = pipelineContent.match(/`([^`]+)`/g)?.map(m => m.replace(/`/g, '')) || [];\n if (agentNames.length >= 2) {\n squad.pipelines.push({\n name: 'default',\n agents: agentNames\n });\n }\n }\n }\n\n // Extract output path\n if (line.toLowerCase().includes('primary') && line.includes('`')) {\n const match = line.match(/`([^`]+)`/);\n if (match) {\n squad.outputPath = match[1].replace(/\\/$/, '');\n }\n }\n\n // Parse goals (checkbox format: - [ ] or - [x])\n if (currentSection === 'goals') {\n const goalMatch = line.match(/^-\\s*\\[([ x])\\]\\s*(.+)$/);\n if (goalMatch) {\n const completed = goalMatch[1] === 'x';\n let description = goalMatch[2].trim();\n let progress: string | undefined;\n\n // Check for progress annotation\n const progressMatch = description.match(/\\(progress:\\s*([^)]+)\\)/i);\n if (progressMatch) {\n progress = progressMatch[1];\n description = description.replace(progressMatch[0], '').trim();\n }\n\n squad.goals.push({\n description,\n completed,\n progress\n });\n }\n }\n }\n\n return squad;\n}\n\nexport function loadSquad(squadName: string): Squad | null {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return null;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return null;\n\n return parseSquadFile(squadFile);\n}\n\nexport function loadAgentDefinition(agentPath: string): string {\n if (!existsSync(agentPath)) return '';\n return readFileSync(agentPath, 'utf-8');\n}\n\n/**\n * Parse provider from an agent definition file.\n *\n * Looks for:\n * 1. Frontmatter: `provider: xai`\n * 2. Header syntax: `## Provider\\nxai`\n *\n * @returns Provider ID or undefined if not specified\n */\nexport function parseAgentProvider(agentPath: string): string | undefined {\n if (!existsSync(agentPath)) return undefined;\n\n const content = readFileSync(agentPath, 'utf-8');\n\n // Try parsing frontmatter\n try {\n const { data: frontmatter } = matter(content);\n if (frontmatter?.provider && typeof frontmatter.provider === 'string') {\n return frontmatter.provider.toLowerCase();\n }\n } catch {\n // Ignore frontmatter parsing errors\n }\n\n // Try header syntax: ## Provider\\n<provider>\n const providerHeaderMatch = content.match(/##\\s*Provider\\s*\\n+([a-zA-Z0-9_-]+)/i);\n if (providerHeaderMatch) {\n return providerHeaderMatch[1].toLowerCase();\n }\n\n return undefined;\n}\n\nexport function addGoalToSquad(squadName: string, goal: string): boolean {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return false;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return false;\n\n let content = readFileSync(squadFile, 'utf-8');\n\n // Check if Goals section exists\n if (!content.includes('## Goals')) {\n // Add Goals section before Dependencies or at end\n const insertPoint = content.indexOf('## Dependencies');\n if (insertPoint > 0) {\n content = content.slice(0, insertPoint) + `## Goals\\n\\n- [ ] ${goal}\\n\\n` + content.slice(insertPoint);\n } else {\n content += `\\n## Goals\\n\\n- [ ] ${goal}\\n`;\n }\n } else {\n // Add to existing Goals section\n const goalsIdx = content.indexOf('## Goals');\n const nextSectionIdx = content.indexOf('\\n## ', goalsIdx + 1);\n const endIdx = nextSectionIdx > 0 ? nextSectionIdx : content.length;\n\n // Find last goal line or section header\n const goalsSection = content.slice(goalsIdx, endIdx);\n const lastGoalMatch = goalsSection.match(/^-\\s*\\[[ x]\\].+$/gm);\n\n if (lastGoalMatch) {\n // Add after last goal\n const lastGoal = lastGoalMatch[lastGoalMatch.length - 1];\n const lastGoalIdx = content.lastIndexOf(lastGoal, endIdx);\n const insertPos = lastGoalIdx + lastGoal.length;\n content = content.slice(0, insertPos) + `\\n- [ ] ${goal}` + content.slice(insertPos);\n } else {\n // No goals yet, add after section header\n const headerEnd = goalsIdx + '## Goals'.length;\n content = content.slice(0, headerEnd) + `\\n\\n- [ ] ${goal}` + content.slice(headerEnd);\n }\n }\n\n writeFileSync(squadFile, content);\n return true;\n}\n\nexport function updateGoalInSquad(\n squadName: string,\n goalIndex: number,\n updates: { completed?: boolean; progress?: string }\n): boolean {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return false;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return false;\n\n const content = readFileSync(squadFile, 'utf-8');\n const lines = content.split('\\n');\n\n let currentSection = '';\n let goalCount = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n if (line.startsWith('## ')) {\n currentSection = line.replace('## ', '').trim().toLowerCase();\n continue;\n }\n\n if (currentSection === 'goals') {\n const goalMatch = line.match(/^-\\s*\\[([ x])\\]\\s*(.+)$/);\n if (goalMatch) {\n if (goalCount === goalIndex) {\n let newLine = '- [' + (updates.completed ? 'x' : ' ') + '] ' + goalMatch[2];\n\n // Handle progress update\n if (updates.progress !== undefined) {\n // Remove existing progress annotation\n newLine = newLine.replace(/\\s*\\(progress:\\s*[^)]+\\)/i, '');\n if (updates.progress) {\n newLine += ` (progress: ${updates.progress})`;\n }\n }\n\n lines[i] = newLine;\n writeFileSync(squadFile, lines.join('\\n'));\n return true;\n }\n goalCount++;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Find the skills directory (.claude/skills)\n */\nfunction findSkillsDir(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n\n const skillsDir = join(projectRoot, '.claude', 'skills');\n return existsSync(skillsDir) ? skillsDir : null;\n}\n\n/**\n * Find the memory directory (.agents/memory)\n */\nfunction findMemoryDir(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n\n const memoryDir = join(projectRoot, '.agents', 'memory');\n return existsSync(memoryDir) ? memoryDir : null;\n}\n\n/**\n * Resolve a skill name to its directory path.\n */\nfunction resolveSkillPath(skillName: string): string | null {\n const skillsDir = findSkillsDir();\n if (!skillsDir) return null;\n\n const skillPath = join(skillsDir, skillName);\n return existsSync(skillPath) ? skillPath : null;\n}\n\n/**\n * Resolve memory glob patterns to actual file paths.\n */\nfunction resolveMemoryPaths(patterns: string[]): string[] {\n const memoryDir = findMemoryDir();\n if (!memoryDir) return [];\n\n const resolved: string[] = [];\n\n for (const pattern of patterns) {\n // Handle simple patterns like \"intelligence/*\" or \"research/*\"\n if (pattern.endsWith('/*')) {\n const subdir = pattern.slice(0, -2);\n const subdirPath = join(memoryDir, subdir);\n if (existsSync(subdirPath)) {\n // Add all .md files in the subdirectory\n try {\n const files = readdirSync(subdirPath);\n for (const file of files) {\n if (file.endsWith('.md')) {\n resolved.push(join(subdirPath, file));\n }\n }\n } catch {\n // Ignore read errors\n }\n }\n } else {\n // Direct path\n const fullPath = join(memoryDir, pattern);\n if (existsSync(fullPath)) {\n resolved.push(fullPath);\n }\n }\n }\n\n return resolved;\n}\n\n/**\n * Resolve execution context for a squad.\n *\n * Takes a Squad object and resolves all context references to actual paths:\n * - MCP config path (three-tier resolution)\n * - Skill directory paths\n * - Memory file paths\n *\n * @param squad - The squad to resolve context for\n * @param forceRegenerate - Force MCP config regeneration\n * @returns Resolved execution context with all paths\n */\nexport function resolveExecutionContext(\n squad: Squad,\n forceRegenerate = false\n): ExecutionContext {\n const ctx = squad.context || {};\n\n // Resolve MCP config\n const mcpResolution: McpResolution = resolveMcpConfig(\n squad.name,\n ctx.mcp,\n forceRegenerate\n );\n\n // Resolve skill paths\n const skillPaths: string[] = [];\n if (ctx.skills) {\n for (const skill of ctx.skills) {\n const path = resolveSkillPath(skill);\n if (path) {\n skillPaths.push(path);\n }\n }\n }\n\n // Resolve memory paths\n const memoryPaths = ctx.memory?.load\n ? resolveMemoryPaths(ctx.memory.load)\n : [];\n\n return {\n // Copy all SquadContext fields\n ...ctx,\n // Add squad name\n squadName: squad.name,\n // Add resolved paths\n resolved: {\n mcpConfigPath: mcpResolution.path,\n mcpSource: mcpResolution.source,\n mcpServers: mcpResolution.servers || [],\n skillPaths,\n memoryPaths,\n },\n };\n}\n","/**\n * Token estimation and tracking for context compression.\n *\n * Uses character-based heuristics for speed (no API calls needed).\n * ~4 characters per token is a reasonable approximation for English text.\n */\n\n// Token-to-character ratios by content type\nconst RATIOS = {\n english: 4.0, // Standard English text\n code: 3.5, // Code tends to have more tokens per char\n json: 3.0, // JSON has many punctuation tokens\n mixed: 3.75, // Default for mixed content\n} as const;\n\nexport type ContentType = keyof typeof RATIOS;\n\n/**\n * Estimate token count from text content.\n *\n * @param text - The text to estimate tokens for\n * @param type - Content type hint for better accuracy\n * @returns Estimated token count\n */\nexport function estimateTokens(text: string, type: ContentType = 'mixed'): number {\n if (!text) return 0;\n const ratio = RATIOS[type];\n return Math.ceil(text.length / ratio);\n}\n\n/**\n * Estimate tokens for a message object (handles different formats).\n */\nexport function estimateMessageTokens(message: {\n role?: string;\n content?: string | Array<{ type: string; text?: string }>;\n}): number {\n // Role overhead (~2-4 tokens)\n let tokens = 4;\n\n if (typeof message.content === 'string') {\n tokens += estimateTokens(message.content);\n } else if (Array.isArray(message.content)) {\n for (const part of message.content) {\n if (part.text) {\n tokens += estimateTokens(part.text);\n }\n // Tool results, images, etc. add overhead\n tokens += 10;\n }\n }\n\n return tokens;\n}\n\n/**\n * Model context limits (in tokens).\n * These are the input context windows, not output limits.\n */\nexport const MODEL_LIMITS: Record<string, number> = {\n // Anthropic models\n 'claude-opus-4-5-20251101': 200_000,\n 'claude-sonnet-4-20250514': 200_000,\n 'claude-3-5-haiku-20241022': 200_000,\n // Aliases\n opus: 200_000,\n sonnet: 200_000,\n haiku: 200_000,\n // Default fallback\n default: 200_000,\n};\n\n/**\n * Get the context limit for a model.\n */\nexport function getModelLimit(model: string): number {\n return MODEL_LIMITS[model] ?? MODEL_LIMITS.default;\n}\n\n/**\n * Token usage tracker for a session.\n */\nexport interface TokenTracker {\n /** Total tokens used so far */\n used: number;\n /** Model's context limit */\n limit: number;\n /** Usage as percentage (0-1) */\n percentage: number;\n /** Breakdown by category */\n breakdown: {\n system: number;\n user: number;\n assistant: number;\n tools: number;\n };\n}\n\n/**\n * Create a new token tracker for a session.\n *\n * @param model - Model name to determine context limit\n * @returns Fresh token tracker\n */\nexport function createTracker(model: string = 'default'): TokenTracker {\n return {\n used: 0,\n limit: getModelLimit(model),\n percentage: 0,\n breakdown: {\n system: 0,\n user: 0,\n assistant: 0,\n tools: 0,\n },\n };\n}\n\n/**\n * Update tracker with new content.\n *\n * @param tracker - Tracker to update (mutated in place)\n * @param content - Content to add\n * @param category - Category for breakdown tracking\n */\nexport function updateTracker(\n tracker: TokenTracker,\n content: string,\n category: keyof TokenTracker['breakdown'] = 'assistant'\n): void {\n const tokens = estimateTokens(content);\n tracker.used += tokens;\n tracker.breakdown[category] += tokens;\n tracker.percentage = tracker.used / tracker.limit;\n}\n\n/**\n * Update tracker from a message object.\n */\nexport function updateTrackerFromMessage(\n tracker: TokenTracker,\n message: { role?: string; content?: string | Array<{ type: string; text?: string }> }\n): void {\n const tokens = estimateMessageTokens(message);\n const category = mapRoleToCategory(message.role);\n tracker.used += tokens;\n tracker.breakdown[category] += tokens;\n tracker.percentage = tracker.used / tracker.limit;\n}\n\nfunction mapRoleToCategory(role?: string): keyof TokenTracker['breakdown'] {\n switch (role) {\n case 'system':\n return 'system';\n case 'user':\n return 'user';\n case 'assistant':\n return 'assistant';\n case 'tool':\n case 'tool_result':\n return 'tools';\n default:\n return 'assistant';\n }\n}\n\n/**\n * Check if compression is needed based on thresholds.\n */\nexport type CompressionLevel = 'none' | 'light' | 'medium' | 'heavy';\n\nexport interface ThresholdConfig {\n light: number; // Default: 0.70\n medium: number; // Default: 0.85\n heavy: number; // Default: 0.95\n}\n\nconst DEFAULT_THRESHOLDS: ThresholdConfig = {\n light: 0.7,\n medium: 0.85,\n heavy: 0.95,\n};\n\n/**\n * Determine what level of compression is needed.\n *\n * @param tracker - Current token tracker state\n * @param thresholds - Custom thresholds (optional)\n * @returns Compression level needed\n */\nexport function getCompressionLevel(\n tracker: TokenTracker,\n thresholds: ThresholdConfig = DEFAULT_THRESHOLDS\n): CompressionLevel {\n if (tracker.percentage >= thresholds.heavy) return 'heavy';\n if (tracker.percentage >= thresholds.medium) return 'medium';\n if (tracker.percentage >= thresholds.light) return 'light';\n return 'none';\n}\n\n/**\n * Format tracker status for display.\n */\nexport function formatTrackerStatus(tracker: TokenTracker): string {\n const pct = (tracker.percentage * 100).toFixed(1);\n const used = (tracker.used / 1000).toFixed(1);\n const limit = (tracker.limit / 1000).toFixed(0);\n const level = getCompressionLevel(tracker);\n\n const levelIndicator =\n level === 'none' ? '' : level === 'light' ? ' [!]' : level === 'medium' ? ' [!!]' : ' [!!!]';\n\n return `${used}K / ${limit}K tokens (${pct}%)${levelIndicator}`;\n}\n","/**\n * File deduplication for context compression.\n *\n * Tracks file reads across a conversation and replaces duplicate\n * reads with concise references to save tokens.\n *\n * Based on Cline's approach: keeping only the latest version of each\n * file prevents LLM confusion during edit operations.\n */\n\nimport { estimateTokens } from './tokens.js';\n\n/**\n * Record of a file read in the conversation.\n */\nexport interface FileReadRecord {\n /** File path that was read */\n path: string;\n /** Turn index where the read occurred */\n turnIndex: number;\n /** Estimated token count of the content */\n tokenCount: number;\n /** Hash of content for change detection */\n contentHash: string;\n}\n\n/**\n * Simple content hash for change detection.\n */\nfunction hashContent(content: string): string {\n // Simple hash - just use first 100 chars + length\n // Full crypto hash would be overkill here\n const prefix = content.slice(0, 100);\n return `${content.length}:${prefix}`;\n}\n\n/**\n * Tracks file reads across a conversation for deduplication.\n */\nexport class FileDeduplicator {\n /** Map of file path to all reads of that file */\n private reads: Map<string, FileReadRecord[]> = new Map();\n\n /** Current turn index */\n private currentTurn = 0;\n\n /**\n * Record a file read.\n *\n * @param path - File path that was read\n * @param content - File content that was read\n */\n trackRead(path: string, content: string): void {\n const record: FileReadRecord = {\n path,\n turnIndex: this.currentTurn,\n tokenCount: estimateTokens(content, 'code'),\n contentHash: hashContent(content),\n };\n\n const existing = this.reads.get(path) || [];\n existing.push(record);\n this.reads.set(path, existing);\n }\n\n /**\n * Advance to next turn.\n */\n nextTurn(): void {\n this.currentTurn++;\n }\n\n /**\n * Get current turn index.\n */\n getTurn(): number {\n return this.currentTurn;\n }\n\n /**\n * Check if a file has been read before.\n *\n * @param path - File path to check\n * @returns Previous read record if exists\n */\n getPreviousRead(path: string): FileReadRecord | undefined {\n const reads = this.reads.get(path);\n if (!reads || reads.length === 0) return undefined;\n return reads[reads.length - 1];\n }\n\n /**\n * Get all files that have been read multiple times.\n *\n * @returns Map of path to read count\n */\n getDuplicateReads(): Map<string, number> {\n const duplicates = new Map<string, number>();\n for (const [path, reads] of this.reads) {\n if (reads.length > 1) {\n duplicates.set(path, reads.length);\n }\n }\n return duplicates;\n }\n\n /**\n * Calculate potential token savings from deduplication.\n */\n getPotentialSavings(): number {\n let savings = 0;\n for (const reads of this.reads.values()) {\n if (reads.length > 1) {\n // Can save all but the most recent read\n for (let i = 0; i < reads.length - 1; i++) {\n savings += reads[i].tokenCount;\n }\n }\n }\n return savings;\n }\n\n /**\n * Generate a deduplication reference message.\n *\n * @param path - File path\n * @param previousTurn - Turn where file was previously read\n */\n static createReference(path: string, previousTurn: number): string {\n return `[File \"${path}\" was read at turn ${previousTurn}. Content unchanged.]`;\n }\n\n /**\n * Reset tracker state.\n */\n reset(): void {\n this.reads.clear();\n this.currentTurn = 0;\n }\n\n /**\n * Get statistics for debugging.\n */\n getStats(): {\n filesTracked: number;\n totalReads: number;\n duplicateReads: number;\n potentialSavings: number;\n } {\n let totalReads = 0;\n let duplicateReads = 0;\n for (const reads of this.reads.values()) {\n totalReads += reads.length;\n if (reads.length > 1) {\n duplicateReads += reads.length - 1;\n }\n }\n\n return {\n filesTracked: this.reads.size,\n totalReads,\n duplicateReads,\n potentialSavings: this.getPotentialSavings(),\n };\n }\n}\n\n/**\n * Message type for deduplication processing.\n */\nexport interface DeduplicatableMessage {\n role: string;\n content: string | Array<{ type: string; text?: string; tool_use_id?: string }>;\n}\n\n/**\n * Extract file paths from tool results.\n */\nexport function extractFileReads(message: DeduplicatableMessage): Array<{ path: string; content: string }> {\n const reads: Array<{ path: string; content: string }> = [];\n\n if (typeof message.content === 'string') {\n // Look for file read patterns in text\n const fileReadPattern = /(?:Reading|Read|Contents of) (?:file )?[`\"']?([^`\"'\\n]+)[`\"']?[\\s\\S]*?(?:```[\\w]*\\n([\\s\\S]*?)```|^(\\d+[→│].+)$)/gm;\n let match;\n while ((match = fileReadPattern.exec(message.content)) !== null) {\n const path = match[1];\n const content = match[2] || match[3] || '';\n if (path && content) {\n reads.push({ path, content });\n }\n }\n } else if (Array.isArray(message.content)) {\n for (const part of message.content) {\n if (part.type === 'tool_result' && part.text) {\n // Tool results often contain file contents\n // Look for common patterns from Read tool\n const lines = part.text.split('\\n');\n const firstLine = lines[0] || '';\n\n // Pattern: \"Reading /path/to/file\"\n if (firstLine.includes('→') || firstLine.match(/^\\s*\\d+[│|]/)) {\n // This looks like file content with line numbers\n // Try to extract path from context\n // (In practice, we'd get this from the tool input)\n }\n }\n }\n }\n\n return reads;\n}\n\n/**\n * Check if content represents a file that was previously read.\n *\n * @param deduplicator - Deduplicator instance\n * @param path - File path\n * @param content - Current content\n * @param savingsThreshold - Minimum savings ratio to deduplicate (default: 0.3 = 30%)\n * @returns Reference message if should deduplicate, null otherwise\n */\nexport function shouldDeduplicate(\n deduplicator: FileDeduplicator,\n path: string,\n content: string,\n savingsThreshold = 0.3\n): string | null {\n const previous = deduplicator.getPreviousRead(path);\n\n if (!previous) {\n // First read - track it\n deduplicator.trackRead(path, content);\n return null;\n }\n\n const currentHash = hashContent(content);\n const currentTokens = estimateTokens(content, 'code');\n\n // Check if content changed\n if (previous.contentHash !== currentHash) {\n // Content changed - keep the new version, track it\n deduplicator.trackRead(path, content);\n return null;\n }\n\n // Calculate savings\n const referenceTokens = estimateTokens(FileDeduplicator.createReference(path, previous.turnIndex));\n const savings = (currentTokens - referenceTokens) / currentTokens;\n\n if (savings < savingsThreshold) {\n // Not worth deduplicating (Cline uses 30% threshold)\n deduplicator.trackRead(path, content);\n return null;\n }\n\n // Track this read but return reference\n deduplicator.trackRead(path, content);\n return FileDeduplicator.createReference(path, previous.turnIndex);\n}\n","/**\n * Token-based pruning for context compression.\n *\n * Based on OpenCode's approach: protect recent tool outputs (40K tokens)\n * while pruning older outputs that exceed the threshold.\n *\n * Key insight: Recent context is critical for coherence. Older tool\n * outputs can be removed entirely without summarization.\n */\n\nimport { estimateTokens, estimateMessageTokens } from './tokens.js';\n\n/**\n * Configuration for token pruning.\n */\nexport interface PruneConfig {\n /** Tokens to protect from pruning (recent window). Default: 40000 */\n protectRecent: number;\n /** Minimum tokens that must be prunable before we prune. Default: 20000 */\n minimumPrunable: number;\n /** Tool types that should never be pruned */\n protectedTools: string[];\n}\n\nconst DEFAULT_CONFIG: PruneConfig = {\n protectRecent: 40_000,\n minimumPrunable: 20_000,\n protectedTools: ['skill', 'memory', 'goal'],\n};\n\n/**\n * Message structure for pruning.\n */\nexport interface PrunableMessage {\n role: string;\n content: string | Array<MessagePart>;\n /** Internal: marks message as prunable */\n _prunable?: boolean;\n /** Internal: token count for this message */\n _tokens?: number;\n}\n\nexport interface MessagePart {\n type: string;\n text?: string;\n tool_use_id?: string;\n name?: string;\n /** Internal: marks part as pruned */\n _pruned?: boolean;\n /** Internal: timestamp when pruned */\n _prunedAt?: number;\n}\n\n/**\n * Placeholder for pruned tool output.\n */\nexport function createPrunedPlaceholder(toolName: string, tokensSaved: number): string {\n return `[Tool output pruned: ${toolName} (~${Math.round(tokensSaved / 1000)}K tokens)]`;\n}\n\n/**\n * Token pruner for conversation context.\n */\nexport class TokenPruner {\n private config: PruneConfig;\n\n constructor(config: Partial<PruneConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Prune messages to reduce token count.\n *\n * Strategy:\n * 1. Scan messages backward from newest to oldest\n * 2. Accumulate tokens for tool outputs\n * 3. Mark outputs beyond protection window for pruning\n * 4. Replace pruned outputs with placeholders\n *\n * @param messages - Messages to prune\n * @returns Pruned messages (new array, originals not mutated)\n */\n pruneMessages(messages: PrunableMessage[]): PrunableMessage[] {\n // First pass: calculate tokens for each message\n const annotated = messages.map((msg) => ({\n ...msg,\n _tokens: estimateMessageTokens(msg),\n }));\n\n // Calculate what's prunable\n const analysis = this.analyzePrunability(annotated);\n\n if (analysis.prunableTokens < this.config.minimumPrunable) {\n // Not enough to prune - return unchanged\n return messages;\n }\n\n // Second pass: prune from oldest to newest, stopping at protection window\n return this.applyPruning(annotated, analysis.protectionIndex);\n }\n\n /**\n * Analyze which messages can be pruned.\n */\n private analyzePrunability(messages: Array<PrunableMessage & { _tokens: number }>): {\n prunableTokens: number;\n protectedTokens: number;\n protectionIndex: number;\n } {\n let protectedTokens = 0;\n let protectionIndex = messages.length;\n\n // Scan backward to find protection boundary\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n\n // Only tool results are prunable\n if (!this.isToolResult(msg)) {\n protectedTokens += msg._tokens;\n continue;\n }\n\n // Check if tool is protected type\n if (this.isProtectedTool(msg)) {\n protectedTokens += msg._tokens;\n continue;\n }\n\n // Add to protected until we hit threshold\n if (protectedTokens + msg._tokens <= this.config.protectRecent) {\n protectedTokens += msg._tokens;\n } else {\n // Found the boundary\n protectionIndex = i + 1;\n break;\n }\n }\n\n // Calculate prunable tokens (everything before protection index that's a tool result)\n let prunableTokens = 0;\n for (let i = 0; i < protectionIndex; i++) {\n const msg = messages[i];\n if (this.isToolResult(msg) && !this.isProtectedTool(msg)) {\n prunableTokens += msg._tokens;\n }\n }\n\n return { prunableTokens, protectedTokens, protectionIndex };\n }\n\n /**\n * Apply pruning to messages before the protection index.\n */\n private applyPruning(\n messages: Array<PrunableMessage & { _tokens: number }>,\n protectionIndex: number\n ): PrunableMessage[] {\n return messages.map((msg, i) => {\n // Messages at or after protection index are kept as-is\n if (i >= protectionIndex) {\n // Remove internal annotations\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { _tokens, _prunable, ...clean } = msg;\n return clean;\n }\n\n // Non-tool messages are kept\n if (!this.isToolResult(msg)) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { _tokens, _prunable, ...clean } = msg;\n return clean;\n }\n\n // Protected tools are kept\n if (this.isProtectedTool(msg)) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { _tokens, _prunable, ...clean } = msg;\n return clean;\n }\n\n // Prune this tool result\n return this.createPrunedMessage(msg);\n });\n }\n\n /**\n * Create a pruned version of a message.\n */\n private createPrunedMessage(msg: PrunableMessage & { _tokens: number }): PrunableMessage {\n const toolName = this.getToolName(msg);\n const placeholder = createPrunedPlaceholder(toolName, msg._tokens);\n\n if (typeof msg.content === 'string') {\n return {\n role: msg.role,\n content: placeholder,\n };\n }\n\n // For array content, replace tool result parts\n const content = msg.content.map((part) => {\n if (part.type === 'tool_result' && part.text) {\n return {\n ...part,\n text: placeholder,\n _pruned: true,\n _prunedAt: Date.now(),\n };\n }\n return part;\n });\n\n return {\n role: msg.role,\n content,\n };\n }\n\n /**\n * Check if a message is a tool result.\n */\n private isToolResult(msg: PrunableMessage): boolean {\n if (msg.role === 'tool') return true;\n\n if (Array.isArray(msg.content)) {\n return msg.content.some((part) => part.type === 'tool_result');\n }\n\n return false;\n }\n\n /**\n * Check if a tool is in the protected list.\n */\n private isProtectedTool(msg: PrunableMessage): boolean {\n const toolName = this.getToolName(msg);\n return this.config.protectedTools.includes(toolName.toLowerCase());\n }\n\n /**\n * Extract tool name from a message.\n */\n private getToolName(msg: PrunableMessage): string {\n if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n if (part.name) return part.name;\n }\n }\n\n // Try to extract from content\n if (typeof msg.content === 'string') {\n const match = msg.content.match(/Tool (?:output|result).*?:\\s*(\\w+)/i);\n if (match) return match[1];\n }\n\n return 'unknown';\n }\n\n /**\n * Get statistics about potential pruning.\n */\n getStats(messages: PrunableMessage[]): {\n totalTokens: number;\n prunableTokens: number;\n protectedTokens: number;\n savingsPercentage: number;\n } {\n const annotated = messages.map((msg) => ({\n ...msg,\n _tokens: estimateMessageTokens(msg),\n }));\n\n const totalTokens = annotated.reduce((sum, msg) => sum + msg._tokens, 0);\n const analysis = this.analyzePrunability(annotated);\n\n return {\n totalTokens,\n prunableTokens: analysis.prunableTokens,\n protectedTokens: analysis.protectedTokens,\n savingsPercentage: totalTokens > 0 ? (analysis.prunableTokens / totalTokens) * 100 : 0,\n };\n }\n}\n","/**\n * LLM-based summarization for heavy context compression.\n *\n * Based on OpenHands' Context Condenser approach:\n * - Keep first N events (initial context)\n * - Keep last M events (recent context)\n * - Summarize the middle section via LLM\n *\n * This is the \"last resort\" compression - only used when at 95%+ context.\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport { estimateTokens } from './tokens.js';\n\n/**\n * Configuration for LLM summarization.\n */\nexport interface SummaryConfig {\n /** Number of messages to preserve from start. Default: 4 */\n keepFirst: number;\n /** Number of messages to preserve from end. Default: 20 */\n keepLast: number;\n /** Model to use for summarization. Default: 'claude-3-5-haiku-20241022' */\n model: string;\n /** Maximum tokens for summary output. Default: 2000 */\n maxSummaryTokens: number;\n}\n\nconst DEFAULT_CONFIG: SummaryConfig = {\n keepFirst: 4,\n keepLast: 20,\n model: 'claude-3-5-haiku-20241022', // Cheap and fast\n maxSummaryTokens: 2000,\n};\n\n/**\n * Message structure for summarization.\n */\nexport interface SummarizableMessage {\n role: string;\n content: string | Array<{ type: string; text?: string }>;\n}\n\n/**\n * Summarization prompt template.\n */\nconst SUMMARIZATION_PROMPT = `You are summarizing a conversation between a user and an AI assistant to reduce context length while preserving essential information.\n\n<conversation_to_summarize>\n{{MIDDLE_CONTENT}}\n</conversation_to_summarize>\n\nCreate a concise summary that preserves:\n1. **User's goals** - What the user is trying to accomplish\n2. **Progress made** - Key actions taken and their outcomes\n3. **Current state** - What's done vs. what still needs to be done\n4. **Critical context** - File paths, error messages, decisions made\n5. **Blockers** - Any issues that need to be resolved\n\nFormat your summary as a structured note that the assistant can reference to continue the work.\n\nKeep the summary under {{MAX_TOKENS}} tokens. Focus on actionable information.`;\n\n/**\n * LLM-based conversation summarizer.\n */\nexport class ConversationSummarizer {\n private config: SummaryConfig;\n private client: Anthropic | null = null;\n\n constructor(config: Partial<SummaryConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Get or create Anthropic client.\n */\n private getClient(): Anthropic {\n if (!this.client) {\n this.client = new Anthropic();\n }\n return this.client;\n }\n\n /**\n * Summarize messages to reduce token count.\n *\n * Strategy:\n * 1. Keep first N messages (system prompt, initial context)\n * 2. Keep last M messages (recent context, current task)\n * 3. Summarize everything in between\n *\n * @param messages - Messages to summarize\n * @returns Summarized messages\n */\n async summarize(messages: SummarizableMessage[]): Promise<SummarizableMessage[]> {\n if (messages.length <= this.config.keepFirst + this.config.keepLast) {\n // Not enough messages to summarize\n return messages;\n }\n\n const firstMessages = messages.slice(0, this.config.keepFirst);\n const middleMessages = messages.slice(this.config.keepFirst, -this.config.keepLast);\n const lastMessages = messages.slice(-this.config.keepLast);\n\n // Generate summary of middle section\n const summary = await this.generateSummary(middleMessages);\n\n // Create summary message\n const summaryMessage: SummarizableMessage = {\n role: 'user',\n content: `[Context Summary - ${middleMessages.length} messages condensed]\\n\\n${summary}`,\n };\n\n return [...firstMessages, summaryMessage, ...lastMessages];\n }\n\n /**\n * Generate a summary of the middle messages.\n */\n private async generateSummary(messages: SummarizableMessage[]): Promise<string> {\n const middleContent = this.formatMessagesForSummary(messages);\n\n const prompt = SUMMARIZATION_PROMPT.replace('{{MIDDLE_CONTENT}}', middleContent).replace(\n '{{MAX_TOKENS}}',\n String(this.config.maxSummaryTokens)\n );\n\n try {\n const client = this.getClient();\n const response = await client.messages.create({\n model: this.config.model,\n max_tokens: this.config.maxSummaryTokens,\n messages: [\n {\n role: 'user',\n content: prompt,\n },\n ],\n });\n\n // Extract text from response\n const textBlock = response.content.find((block) => block.type === 'text');\n if (textBlock && 'text' in textBlock) {\n return textBlock.text;\n }\n\n return '[Summary generation failed - no text in response]';\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return `[Summary generation failed: ${message}]`;\n }\n }\n\n /**\n * Format messages for the summarization prompt.\n */\n private formatMessagesForSummary(messages: SummarizableMessage[]): string {\n return messages\n .map((msg, i) => {\n const role = msg.role.toUpperCase();\n const content = this.extractContent(msg);\n return `[${i + 1}] ${role}:\\n${content}`;\n })\n .join('\\n\\n---\\n\\n');\n }\n\n /**\n * Extract text content from a message.\n */\n private extractContent(msg: SummarizableMessage): string {\n if (typeof msg.content === 'string') {\n return this.truncateContent(msg.content);\n }\n\n const parts: string[] = [];\n for (const part of msg.content) {\n if (part.text) {\n parts.push(this.truncateContent(part.text));\n }\n }\n return parts.join('\\n');\n }\n\n /**\n * Truncate very long content for summary input.\n */\n private truncateContent(content: string, maxChars = 2000): string {\n if (content.length <= maxChars) return content;\n return content.slice(0, maxChars) + `\\n[...truncated ${content.length - maxChars} chars]`;\n }\n\n /**\n * Estimate the cost of summarization.\n *\n * @param messages - Messages that would be summarized\n * @returns Estimated cost in USD\n */\n estimateCost(messages: SummarizableMessage[]): number {\n if (messages.length <= this.config.keepFirst + this.config.keepLast) {\n return 0;\n }\n\n const middleMessages = messages.slice(this.config.keepFirst, -this.config.keepLast);\n const inputTokens = middleMessages.reduce((sum, msg) => {\n const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n return sum + estimateTokens(content);\n }, 0);\n\n // Haiku pricing: $0.25/MTok input, $1.25/MTok output\n const inputCost = (inputTokens / 1_000_000) * 0.25;\n const outputCost = (this.config.maxSummaryTokens / 1_000_000) * 1.25;\n\n return inputCost + outputCost;\n }\n\n /**\n * Get statistics about potential summarization.\n */\n getStats(messages: SummarizableMessage[]): {\n totalMessages: number;\n wouldKeep: number;\n wouldSummarize: number;\n estimatedCost: number;\n } {\n const wouldKeep = Math.min(messages.length, this.config.keepFirst + this.config.keepLast);\n const wouldSummarize = Math.max(0, messages.length - wouldKeep);\n\n return {\n totalMessages: messages.length,\n wouldKeep,\n wouldSummarize,\n estimatedCost: this.estimateCost(messages),\n };\n }\n}\n\n/**\n * Create a summary message without LLM call (for testing/fallback).\n */\nexport function createFallbackSummary(messages: SummarizableMessage[]): string {\n const actions: string[] = [];\n const files: Set<string> = new Set();\n const errors: string[] = [];\n\n for (const msg of messages) {\n const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n\n // Extract file paths\n const fileMatches = content.match(/(?:\\/[\\w.\\-/]+\\.[\\w]+)/g);\n if (fileMatches) {\n fileMatches.forEach((f) => files.add(f));\n }\n\n // Extract errors\n if (content.toLowerCase().includes('error')) {\n const errorMatch = content.match(/error[:\\s]+([^\\n]+)/i);\n if (errorMatch && errors.length < 3) {\n errors.push(errorMatch[1].slice(0, 100));\n }\n }\n\n // Extract key actions (from assistant messages)\n if (msg.role === 'assistant' && content.length < 500) {\n actions.push(content.slice(0, 100));\n }\n }\n\n const parts: string[] = ['## Conversation Summary (Fallback)', ''];\n\n if (files.size > 0) {\n parts.push('### Files Referenced');\n parts.push([...files].slice(0, 10).map((f) => `- ${f}`).join('\\n'));\n parts.push('');\n }\n\n if (actions.length > 0) {\n parts.push('### Key Actions');\n parts.push(actions.slice(0, 5).map((a) => `- ${a}`).join('\\n'));\n parts.push('');\n }\n\n if (errors.length > 0) {\n parts.push('### Errors Encountered');\n parts.push(errors.map((e) => `- ${e}`).join('\\n'));\n }\n\n return parts.join('\\n');\n}\n","/**\n * Context Condenser - Main Pipeline\n *\n * Coordinates the three compression strategies:\n * 1. Deduplication (70% threshold) - Replace duplicate file reads\n * 2. Pruning (85% threshold) - Remove old tool outputs\n * 3. Summarization (95% threshold) - LLM-based middle section summary\n *\n * Based on patterns from OpenCode, OpenHands, and Cline.\n */\n\nimport {\n TokenTracker,\n createTracker,\n updateTrackerFromMessage,\n getCompressionLevel,\n CompressionLevel,\n ThresholdConfig,\n formatTrackerStatus,\n} from './tokens.js';\nimport { FileDeduplicator } from './deduplication.js';\nimport { TokenPruner, PruneConfig, PrunableMessage } from './pruning.js';\nimport { ConversationSummarizer, SummaryConfig, SummarizableMessage } from './summarizer.js';\n\n// Re-export types\nexport { TokenTracker, CompressionLevel, ThresholdConfig };\nexport { FileDeduplicator } from './deduplication.js';\nexport { TokenPruner, PruneConfig } from './pruning.js';\nexport { ConversationSummarizer, SummaryConfig } from './summarizer.js';\nexport * from './tokens.js';\n\n/**\n * Configuration for the context condenser.\n */\nexport interface CondenserConfig {\n /** Whether context compression is enabled */\n enabled: boolean;\n /** Threshold for light compression (deduplication) */\n lightThreshold: number;\n /** Threshold for medium compression (pruning) */\n mediumThreshold: number;\n /** Threshold for heavy compression (summarization) */\n heavyThreshold: number;\n /** Model context limit */\n modelLimit: number;\n /** Model name for tracking */\n model: string;\n /** Pruning configuration */\n pruning: Partial<PruneConfig>;\n /** Summarization configuration */\n summarization: Partial<SummaryConfig>;\n}\n\nconst DEFAULT_CONFIG: CondenserConfig = {\n enabled: true,\n lightThreshold: 0.7,\n mediumThreshold: 0.85,\n heavyThreshold: 0.95,\n modelLimit: 200_000,\n model: 'claude-sonnet-4-20250514',\n pruning: {},\n summarization: {},\n};\n\n/**\n * Message type for the condenser pipeline.\n */\nexport interface CondenserMessage extends PrunableMessage, SummarizableMessage {\n role: string;\n content: string | Array<{ type: string; text?: string; tool_use_id?: string; name?: string }>;\n}\n\n/**\n * Result of a condense operation.\n */\nexport interface CondenserResult {\n /** Condensed messages */\n messages: CondenserMessage[];\n /** Compression level applied */\n level: CompressionLevel;\n /** Tokens before compression */\n tokensBefore: number;\n /** Tokens after compression */\n tokensAfter: number;\n /** Savings percentage */\n savingsPercentage: number;\n /** Duration in milliseconds */\n durationMs: number;\n}\n\n/**\n * Context Condenser - Main class.\n */\nexport class ContextCondenser {\n private config: CondenserConfig;\n private tracker: TokenTracker;\n private deduplicator: FileDeduplicator;\n private pruner: TokenPruner;\n private summarizer: ConversationSummarizer;\n\n /** Metrics for tracking */\n private metrics = {\n condensationCount: 0,\n tokensRecovered: 0,\n lastLevel: 'none' as CompressionLevel,\n };\n\n constructor(config: Partial<CondenserConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.tracker = createTracker(this.config.model);\n this.tracker.limit = this.config.modelLimit;\n this.deduplicator = new FileDeduplicator();\n this.pruner = new TokenPruner(this.config.pruning);\n this.summarizer = new ConversationSummarizer(this.config.summarization);\n }\n\n /**\n * Main entry point - condense messages if needed.\n *\n * @param messages - Current conversation messages\n * @returns Condensed messages and metadata\n */\n async condense(messages: CondenserMessage[]): Promise<CondenserResult> {\n const startTime = Date.now();\n\n if (!this.config.enabled) {\n return this.createResult(messages, messages, 'none', startTime);\n }\n\n // Update tracker with all messages\n this.updateTrackerFromMessages(messages);\n\n const level = getCompressionLevel(this.tracker, {\n light: this.config.lightThreshold,\n medium: this.config.mediumThreshold,\n heavy: this.config.heavyThreshold,\n });\n\n if (level === 'none') {\n return this.createResult(messages, messages, level, startTime);\n }\n\n let condensed: CondenserMessage[];\n\n switch (level) {\n case 'light':\n condensed = this.applyDeduplication(messages);\n break;\n case 'medium':\n condensed = this.applyPruning(messages);\n break;\n case 'heavy':\n condensed = await this.applySummarization(messages);\n break;\n }\n\n // Update metrics\n this.metrics.condensationCount++;\n this.metrics.lastLevel = level;\n\n return this.createResult(messages, condensed, level, startTime);\n }\n\n /**\n * Apply light compression (deduplication).\n */\n private applyDeduplication(messages: CondenserMessage[]): CondenserMessage[] {\n // For now, just return messages as-is\n // Full deduplication requires tracking reads across tool calls\n // which we'd integrate with the actual tool execution layer\n return messages;\n }\n\n /**\n * Apply medium compression (pruning).\n */\n private applyPruning(messages: CondenserMessage[]): CondenserMessage[] {\n return this.pruner.pruneMessages(messages) as CondenserMessage[];\n }\n\n /**\n * Apply heavy compression (summarization).\n */\n private async applySummarization(messages: CondenserMessage[]): Promise<CondenserMessage[]> {\n // First apply pruning\n const pruned = this.applyPruning(messages);\n\n // Then summarize if still needed\n const summarized = await this.summarizer.summarize(pruned);\n\n return summarized as CondenserMessage[];\n }\n\n /**\n * Update tracker from messages.\n */\n private updateTrackerFromMessages(messages: CondenserMessage[]): void {\n // Reset tracker\n this.tracker = createTracker(this.config.model);\n this.tracker.limit = this.config.modelLimit;\n\n // Add all messages\n for (const msg of messages) {\n updateTrackerFromMessage(this.tracker, msg);\n }\n }\n\n /**\n * Create result object.\n */\n private createResult(\n before: CondenserMessage[],\n after: CondenserMessage[],\n level: CompressionLevel,\n startTime: number\n ): CondenserResult {\n const tokensBefore = this.estimateTokens(before);\n const tokensAfter = this.estimateTokens(after);\n const savings = tokensBefore > 0 ? ((tokensBefore - tokensAfter) / tokensBefore) * 100 : 0;\n\n if (level !== 'none') {\n this.metrics.tokensRecovered += tokensBefore - tokensAfter;\n }\n\n return {\n messages: after,\n level,\n tokensBefore,\n tokensAfter,\n savingsPercentage: savings,\n durationMs: Date.now() - startTime,\n };\n }\n\n /**\n * Estimate tokens for messages.\n */\n private estimateTokens(messages: CondenserMessage[]): number {\n const tempTracker = createTracker(this.config.model);\n for (const msg of messages) {\n updateTrackerFromMessage(tempTracker, msg);\n }\n return tempTracker.used;\n }\n\n /**\n * Get current tracker status.\n */\n getStatus(): string {\n return formatTrackerStatus(this.tracker);\n }\n\n /**\n * Get tracker for external monitoring.\n */\n getTracker(): TokenTracker {\n return { ...this.tracker };\n }\n\n /**\n * Get metrics.\n */\n getMetrics(): typeof this.metrics {\n return { ...this.metrics };\n }\n\n /**\n * Check if compression is needed.\n */\n needsCompression(): CompressionLevel {\n return getCompressionLevel(this.tracker, {\n light: this.config.lightThreshold,\n medium: this.config.mediumThreshold,\n heavy: this.config.heavyThreshold,\n });\n }\n\n /**\n * Reset condenser state.\n */\n reset(): void {\n this.tracker = createTracker(this.config.model);\n this.tracker.limit = this.config.modelLimit;\n this.deduplicator.reset();\n this.metrics = {\n condensationCount: 0,\n tokensRecovered: 0,\n lastLevel: 'none',\n };\n }\n\n /**\n * Get file deduplicator for integration with tool layer.\n */\n getDeduplicator(): FileDeduplicator {\n return this.deduplicator;\n }\n}\n\n/**\n * Create a condenser with squad-specific configuration.\n */\nexport function createCondenser(squadConfig?: {\n condenser?: {\n enabled?: boolean;\n light_threshold?: number;\n medium_threshold?: number;\n heavy_threshold?: number;\n protect_recent?: number;\n };\n model?: {\n default?: string;\n };\n}): ContextCondenser {\n const condenserConfig = squadConfig?.condenser || {};\n const modelConfig = squadConfig?.model || {};\n\n return new ContextCondenser({\n enabled: condenserConfig.enabled ?? true,\n lightThreshold: condenserConfig.light_threshold ?? 0.7,\n mediumThreshold: condenserConfig.medium_threshold ?? 0.85,\n heavyThreshold: condenserConfig.heavy_threshold ?? 0.95,\n model: modelConfig.default ?? 'claude-sonnet-4-20250514',\n pruning: {\n protectRecent: condenserConfig.protect_recent ?? 40_000,\n },\n });\n}\n"],"mappings":"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","names":["require","DEFAULT_CONFIG","DEFAULT_CONFIG"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/lib/squad-parser.ts","../src/lib/mcp-config.ts","../src/lib/condenser/tokens.ts","../src/lib/condenser/deduplication.ts","../src/lib/condenser/pruning.ts","../src/lib/condenser/summarizer.ts","../src/lib/condenser/index.ts"],"sourcesContent":["import { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json');\nexport const version: string = pkg.version;\n","import { readFileSync, existsSync, readdirSync, writeFileSync } from 'fs';\nimport { join, basename, dirname } from 'path';\nimport matter from 'gray-matter';\nimport { resolveMcpConfig, type McpResolution } from './mcp-config.js';\n\nexport type EffortLevel = 'high' | 'medium' | 'low';\n\n// Context schema for frontmatter\nexport interface SquadContext {\n mcp?: string[];\n skills?: string[];\n memory?: {\n load?: string[];\n };\n model?: {\n default?: string;\n expensive?: string;\n cheap?: string;\n };\n budget?: {\n daily?: number;\n weekly?: number;\n perExecution?: number;\n };\n /** Cooldown between executions in seconds */\n cooldown?: number;\n}\n\n/**\n * Resolved skill with path and source information.\n */\nexport interface ResolvedSkill {\n /** Skill name (directory or reference name) */\n name: string;\n /** Absolute path to the skill directory */\n path: string;\n /** Where the skill was found */\n source: 'squad-local' | 'project' | 'global';\n}\n\n// Multi-LLM provider configuration\nexport interface SquadProviders {\n /** Default provider for all agents (default: anthropic) */\n default?: string;\n /** Provider for vision/image tasks */\n vision?: string;\n /** Provider for real-time data access */\n realtime?: string;\n /** Provider for high-volume/cheap operations */\n cheap?: string;\n /** Custom provider mappings by purpose */\n [key: string]: string | undefined;\n}\n\n// Frontmatter schema\nexport interface SquadFrontmatter {\n name?: string;\n mission?: string;\n repo?: string;\n stack?: string;\n context?: SquadContext;\n effort?: EffortLevel;\n /** Multi-LLM provider configuration */\n providers?: SquadProviders;\n}\n\nexport interface Agent {\n name: string;\n role: string;\n trigger: string;\n status?: string;\n filePath?: string;\n squad?: string;\n effort?: EffortLevel;\n /** LLM provider override (from agent file frontmatter) */\n provider?: string;\n /** Agent purpose (short description) */\n purpose?: string;\n /** Cron schedule for scheduled agents */\n schedule?: string;\n /** Output destinations */\n outputs?: string[];\n}\n\nexport interface Pipeline {\n name: string;\n agents: string[];\n}\n\nexport interface Goal {\n description: string;\n completed: boolean;\n progress?: string;\n metrics?: string[];\n}\n\n/**\n * Routine definition for autonomous scheduled execution.\n * Defined in SQUAD.md under ### Routines yaml block.\n */\nexport interface Routine {\n /** Unique name for the routine */\n name: string;\n /** Cron schedule (e.g., \"0 8 * * *\" for daily 8am) */\n schedule: string;\n /** Agents to run in this batch */\n agents: string[];\n /** Model to use (defaults to squad default or sonnet) */\n model?: string;\n /** Whether the routine is enabled */\n enabled?: boolean;\n /** Priority for execution ordering (lower = higher priority) */\n priority?: number;\n /** Minimum cooldown between runs (e.g., \"6 hours\") */\n cooldown?: string;\n}\n\nexport interface Squad {\n name: string;\n /** Directory name for file path resolution (e.g., \"engineering\") */\n dir: string;\n mission: string;\n agents: Agent[];\n pipelines: Pipeline[];\n triggers: {\n scheduled: string[];\n event: string[];\n manual: string[];\n };\n /** Autonomous routines for scheduled batch execution */\n routines: Routine[];\n dependencies: string[];\n outputPath: string;\n goals: Goal[];\n effort?: EffortLevel; // Squad-level default effort\n context?: SquadContext; // Frontmatter context block\n repo?: string;\n stack?: string;\n /** Multi-LLM provider configuration */\n providers?: SquadProviders;\n /** Domain this squad operates in */\n domain?: string;\n /** Permissions for this squad */\n permissions?: Record<string, boolean>;\n /** Raw frontmatter for accessing KPIs and other custom fields */\n frontmatter?: Record<string, unknown>;\n}\n\n/**\n * Resolved execution context with paths and metadata.\n * Extends SquadContext with resolved paths for MCP, skills, and memory.\n */\nexport interface ExecutionContext extends SquadContext {\n /** Squad name this context belongs to */\n squadName: string;\n /** Resolved paths and metadata */\n resolved: {\n /** Path to MCP config file to use */\n mcpConfigPath: string;\n /** Source of MCP config resolution */\n mcpSource: 'user-override' | 'generated' | 'fallback' | 'squad-local';\n /** List of MCP servers in the config */\n mcpServers: string[];\n /** Resolved skill directory paths (deprecated, use skills instead) */\n skillPaths: string[];\n /** Resolved skills with source information */\n skills: ResolvedSkill[];\n /** Resolved memory file paths */\n memoryPaths: string[];\n };\n}\n\n/**\n * Find the .agents/squads directory by searching current directory and parents.\n * Searches up to 5 parent directories.\n * @returns Path to squads directory or null if not found\n */\nexport function findSquadsDir(): string | null {\n // Look for .agents/squads in current directory or parent directories\n let dir = process.cwd();\n\n for (let i = 0; i < 5; i++) {\n const squadsPath = join(dir, '.agents', 'squads');\n if (existsSync(squadsPath)) {\n return squadsPath;\n }\n const parent = join(dir, '..');\n if (parent === dir) break;\n dir = parent;\n }\n\n return null;\n}\n\n/**\n * Find the root directory of the squads project (where .agents/ lives).\n * @returns Path to project root or null if not in a squads project\n */\nexport function findProjectRoot(): string | null {\n // Find the root of the squads project (where .agents/ lives)\n const squadsDir = findSquadsDir();\n if (!squadsDir) return null;\n // squadsDir is /path/to/.agents/squads, so go up 2 levels\n return join(squadsDir, '..', '..');\n}\n\n/**\n * Check if the project has local infrastructure configuration.\n * Looks for .env file with infra-related keys (LANGFUSE_, SQUADS_BRIDGE, etc).\n * @returns True if local infra config exists\n */\nexport function hasLocalInfraConfig(): boolean {\n // Check if the project has a local .env file with infra config\n const projectRoot = findProjectRoot();\n if (!projectRoot) return false;\n\n const envPath = join(projectRoot, '.env');\n if (!existsSync(envPath)) return false;\n\n // Check if .env has any infra-related keys\n const content = readFileSync(envPath, 'utf-8');\n const infraKeys = ['LANGFUSE_', 'SQUADS_BRIDGE', 'SQUADS_POSTGRES', 'SQUADS_REDIS'];\n return infraKeys.some(key => content.includes(key));\n}\n\n/**\n * List all squad names in the given squads directory.\n * Only includes directories containing a SQUAD.md file.\n * @param squadsDir - Path to the .agents/squads directory\n * @returns Array of squad directory names\n */\nexport function listSquads(squadsDir: string): string[] {\n const squads: string[] = [];\n\n const entries = readdirSync(squadsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && !entry.name.startsWith('_')) {\n const squadFile = join(squadsDir, entry.name, 'SQUAD.md');\n if (existsSync(squadFile)) {\n squads.push(entry.name);\n }\n }\n }\n\n return squads;\n}\n\n/**\n * List all agents in the squads directory or a specific squad.\n * Agents are markdown files (excluding SQUAD.md) in squad directories.\n * @param squadsDir - Path to the .agents/squads directory\n * @param squadName - Optional squad name to filter agents\n * @returns Array of Agent objects with basic metadata\n */\nexport function listAgents(squadsDir: string, squadName?: string): Agent[] {\n const agents: Agent[] = [];\n\n const dirs = squadName\n ? [squadName]\n : readdirSync(squadsDir, { withFileTypes: true })\n .filter(e => e.isDirectory() && !e.name.startsWith('_'))\n .map(e => e.name);\n\n for (const dir of dirs) {\n const squadPath = join(squadsDir, dir);\n if (!existsSync(squadPath)) continue;\n\n const files = readdirSync(squadPath);\n for (const file of files) {\n if (file.endsWith('.md') && file !== 'SQUAD.md') {\n const agentName = file.replace('.md', '');\n agents.push({\n name: agentName,\n role: `Agent in ${dir}`,\n trigger: 'manual',\n filePath: join(squadPath, file)\n });\n }\n }\n }\n\n return agents;\n}\n\n/**\n * Parse a SQUAD.md file into a Squad object.\n * Extracts frontmatter metadata, agents, pipelines, goals, and routines.\n * @param filePath - Path to the SQUAD.md file\n * @returns Parsed Squad object with all extracted data\n */\nexport function parseSquadFile(filePath: string): Squad {\n const rawContent = readFileSync(filePath, 'utf-8');\n\n // Parse frontmatter with gray-matter\n const { data: frontmatter, content: bodyContent } = matter(rawContent);\n const fm = frontmatter as SquadFrontmatter;\n\n const lines = bodyContent.split('\\n');\n\n // Directory name is used for file paths (e.g., \"engineering\", \"marketing\")\n const dirName = basename(dirname(filePath));\n\n const squad: Squad = {\n // Display name can be different from dir (e.g., \"Engineering Squad\")\n name: fm.name || dirName,\n // Directory name for file path resolution\n dir: dirName,\n mission: fm.mission || '',\n agents: [],\n pipelines: [],\n triggers: { scheduled: [], event: [], manual: [] },\n routines: [],\n dependencies: [],\n outputPath: '',\n goals: [],\n // Apply frontmatter fields\n effort: fm.effort,\n context: fm.context,\n repo: fm.repo,\n stack: fm.stack,\n providers: fm.providers,\n // Preserve raw frontmatter for KPIs and other custom fields\n frontmatter: frontmatter as Record<string, unknown>,\n };\n\n let currentSection = '';\n let inTable = false;\n let tableHeaders: string[] = [];\n\n for (const line of lines) {\n // Extract squad name from title\n if (line.startsWith('# Squad:')) {\n squad.name = line.replace('# Squad:', '').trim().toLowerCase();\n continue;\n }\n\n // Track sections\n if (line.startsWith('## ')) {\n currentSection = line.replace('## ', '').trim().toLowerCase();\n inTable = false;\n continue;\n }\n\n // Extract mission\n if (currentSection === 'mission' && line.trim() && !line.startsWith('#')) {\n if (!squad.mission) {\n squad.mission = line.trim();\n }\n }\n\n // Extract squad-level effort (e.g., \"effort: medium\" in Context section)\n const effortMatch = line.match(/^effort:\\s*(high|medium|low)/i);\n if (effortMatch && !squad.effort) {\n squad.effort = effortMatch[1].toLowerCase() as EffortLevel;\n }\n\n // Parse agent tables\n if (currentSection.includes('agent') || currentSection.includes('orchestrator') ||\n currentSection.includes('evaluator') || currentSection.includes('builder') ||\n currentSection.includes('priority')) {\n\n if (line.includes('|') && line.includes('Agent')) {\n inTable = true;\n tableHeaders = line.split('|').map(h => h.trim().toLowerCase());\n continue;\n }\n\n if (inTable && line.includes('|') && !line.includes('---')) {\n const cells = line.split('|').map(c => c.trim().replace(/`/g, ''));\n const agentIdx = tableHeaders.findIndex(h => h === 'agent');\n const roleIdx = tableHeaders.findIndex(h => h === 'role');\n const triggerIdx = tableHeaders.findIndex(h => h === 'trigger');\n const statusIdx = tableHeaders.findIndex(h => h === 'status');\n const effortIdx = tableHeaders.findIndex(h => h === 'effort');\n\n if (agentIdx >= 0 && cells[agentIdx]) {\n const effortValue = effortIdx >= 0 ? cells[effortIdx]?.toLowerCase() : undefined;\n const effort = ['high', 'medium', 'low'].includes(effortValue || '')\n ? effortValue as EffortLevel\n : undefined;\n\n squad.agents.push({\n name: cells[agentIdx],\n role: roleIdx >= 0 ? cells[roleIdx] : '',\n trigger: triggerIdx >= 0 ? cells[triggerIdx] : 'manual',\n status: statusIdx >= 0 ? cells[statusIdx] : 'active',\n effort\n });\n }\n }\n }\n\n // Parse pipelines (looking for patterns like: agent1 → agent2 → agent3)\n if (line.includes('→') && line.includes('`')) {\n const pipelineMatch = line.match(/`([^`]+)`\\s*→\\s*`([^`]+)`/g);\n if (pipelineMatch) {\n const agentNames = line.match(/`([^`]+)`/g)?.map(m => m.replace(/`/g, '')) || [];\n if (agentNames.length >= 2) {\n squad.pipelines.push({\n name: 'default',\n agents: agentNames\n });\n }\n }\n }\n\n // Also look for Pipeline: format\n if (line.toLowerCase().includes('pipeline:')) {\n const pipelineContent = line.split(':')[1];\n if (pipelineContent && pipelineContent.includes('→')) {\n const agentNames = pipelineContent.match(/`([^`]+)`/g)?.map(m => m.replace(/`/g, '')) || [];\n if (agentNames.length >= 2) {\n squad.pipelines.push({\n name: 'default',\n agents: agentNames\n });\n }\n }\n }\n\n // Extract output path\n if (line.toLowerCase().includes('primary') && line.includes('`')) {\n const match = line.match(/`([^`]+)`/);\n if (match) {\n squad.outputPath = match[1].replace(/\\/$/, '');\n }\n }\n\n // Parse goals (checkbox format: - [ ] or - [x])\n if (currentSection === 'goals') {\n const goalMatch = line.match(/^-\\s*\\[([ x])\\]\\s*(.+)$/);\n if (goalMatch) {\n const completed = goalMatch[1] === 'x';\n let description = goalMatch[2].trim();\n let progress: string | undefined;\n\n // Check for progress annotation\n const progressMatch = description.match(/\\(progress:\\s*([^)]+)\\)/i);\n if (progressMatch) {\n progress = progressMatch[1];\n description = description.replace(progressMatch[0], '').trim();\n }\n\n squad.goals.push({\n description,\n completed,\n progress\n });\n }\n }\n }\n\n return squad;\n}\n\n/**\n * Load and parse a squad by name.\n * Convenience function that finds the squads directory and parses the squad file.\n * @param squadName - Name of the squad directory (e.g., \"engineering\")\n * @returns Parsed Squad object or null if not found\n */\nexport function loadSquad(squadName: string): Squad | null {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return null;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return null;\n\n return parseSquadFile(squadFile);\n}\n\n/**\n * Load raw content of an agent definition file.\n * @param agentPath - Path to the agent markdown file\n * @returns Raw file content or empty string if file doesn't exist\n */\nexport function loadAgentDefinition(agentPath: string): string {\n if (!existsSync(agentPath)) return '';\n return readFileSync(agentPath, 'utf-8');\n}\n\n/**\n * Parse provider from an agent definition file.\n *\n * Looks for:\n * 1. Frontmatter: `provider: xai`\n * 2. Header syntax: `## Provider\\nxai`\n *\n * @returns Provider ID or undefined if not specified\n */\nexport function parseAgentProvider(agentPath: string): string | undefined {\n if (!existsSync(agentPath)) return undefined;\n\n const content = readFileSync(agentPath, 'utf-8');\n\n // Try parsing frontmatter\n try {\n const { data: frontmatter } = matter(content);\n if (frontmatter?.provider && typeof frontmatter.provider === 'string') {\n return frontmatter.provider.toLowerCase();\n }\n } catch {\n // Ignore frontmatter parsing errors\n }\n\n // Try header syntax: ## Provider\\n<provider>\n const providerHeaderMatch = content.match(/##\\s*Provider\\s*\\n+([a-zA-Z0-9_-]+)/i);\n if (providerHeaderMatch) {\n return providerHeaderMatch[1].toLowerCase();\n }\n\n return undefined;\n}\n\n/**\n * Add a new goal to a squad's SQUAD.md file.\n * Creates the Goals section if it doesn't exist.\n * @param squadName - Name of the squad directory\n * @param goal - Goal description text\n * @returns True if goal was added successfully\n */\nexport function addGoalToSquad(squadName: string, goal: string): boolean {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return false;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return false;\n\n let content = readFileSync(squadFile, 'utf-8');\n\n // Check if Goals section exists\n if (!content.includes('## Goals')) {\n // Add Goals section before Dependencies or at end\n const insertPoint = content.indexOf('## Dependencies');\n if (insertPoint > 0) {\n content = content.slice(0, insertPoint) + `## Goals\\n\\n- [ ] ${goal}\\n\\n` + content.slice(insertPoint);\n } else {\n content += `\\n## Goals\\n\\n- [ ] ${goal}\\n`;\n }\n } else {\n // Add to existing Goals section\n const goalsIdx = content.indexOf('## Goals');\n const nextSectionIdx = content.indexOf('\\n## ', goalsIdx + 1);\n const endIdx = nextSectionIdx > 0 ? nextSectionIdx : content.length;\n\n // Find last goal line or section header\n const goalsSection = content.slice(goalsIdx, endIdx);\n const lastGoalMatch = goalsSection.match(/^-\\s*\\[[ x]\\].+$/gm);\n\n if (lastGoalMatch) {\n // Add after last goal\n const lastGoal = lastGoalMatch[lastGoalMatch.length - 1];\n const lastGoalIdx = content.lastIndexOf(lastGoal, endIdx);\n const insertPos = lastGoalIdx + lastGoal.length;\n content = content.slice(0, insertPos) + `\\n- [ ] ${goal}` + content.slice(insertPos);\n } else {\n // No goals yet, add after section header\n const headerEnd = goalsIdx + '## Goals'.length;\n content = content.slice(0, headerEnd) + `\\n\\n- [ ] ${goal}` + content.slice(headerEnd);\n }\n }\n\n writeFileSync(squadFile, content);\n return true;\n}\n\n/**\n * Update an existing goal in a squad's SQUAD.md file.\n * Can mark goal as completed or update progress text.\n * @param squadName - Name of the squad directory\n * @param goalIndex - Zero-based index of the goal to update\n * @param updates - Object with optional completed and progress fields\n * @returns True if goal was updated successfully\n */\nexport function updateGoalInSquad(\n squadName: string,\n goalIndex: number,\n updates: { completed?: boolean; progress?: string }\n): boolean {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return false;\n\n const squadFile = join(squadsDir, squadName, 'SQUAD.md');\n if (!existsSync(squadFile)) return false;\n\n const content = readFileSync(squadFile, 'utf-8');\n const lines = content.split('\\n');\n\n let currentSection = '';\n let goalCount = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n if (line.startsWith('## ')) {\n currentSection = line.replace('## ', '').trim().toLowerCase();\n continue;\n }\n\n if (currentSection === 'goals') {\n const goalMatch = line.match(/^-\\s*\\[([ x])\\]\\s*(.+)$/);\n if (goalMatch) {\n if (goalCount === goalIndex) {\n let newLine = '- [' + (updates.completed ? 'x' : ' ') + '] ' + goalMatch[2];\n\n // Handle progress update\n if (updates.progress !== undefined) {\n // Remove existing progress annotation\n newLine = newLine.replace(/\\s*\\(progress:\\s*[^)]+\\)/i, '');\n if (updates.progress) {\n newLine += ` (progress: ${updates.progress})`;\n }\n }\n\n lines[i] = newLine;\n writeFileSync(squadFile, lines.join('\\n'));\n return true;\n }\n goalCount++;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Find the project-level skills directory (.claude/skills)\n */\nfunction findProjectSkillsDir(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n\n const skillsDir = join(projectRoot, '.claude', 'skills');\n return existsSync(skillsDir) ? skillsDir : null;\n}\n\n/**\n * Find the global skills directory (~/.claude/skills)\n */\nfunction findGlobalSkillsDir(): string | null {\n const home = process.env.HOME || process.env.USERPROFILE || '';\n if (!home) return null;\n\n const skillsDir = join(home, '.claude', 'skills');\n return existsSync(skillsDir) ? skillsDir : null;\n}\n\n/**\n * Find squad-local skills directory (.agents/squads/<squad>/skills)\n */\nfunction findSquadLocalSkillsDir(squadDir: string): string | null {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return null;\n\n const skillsDir = join(squadsDir, squadDir, 'skills');\n return existsSync(skillsDir) ? skillsDir : null;\n}\n\n/**\n * List all skills in a directory.\n * Skills are subdirectories containing SKILL.md or .md files.\n */\nfunction listSkillsInDir(skillsDir: string): string[] {\n if (!existsSync(skillsDir)) return [];\n\n try {\n const entries = readdirSync(skillsDir, { withFileTypes: true });\n const skills: string[] = [];\n\n for (const entry of entries) {\n if (entry.isDirectory()) {\n // Check if directory contains SKILL.md\n const skillMdPath = join(skillsDir, entry.name, 'SKILL.md');\n if (existsSync(skillMdPath)) {\n skills.push(entry.name);\n }\n } else if (entry.isFile() && entry.name.endsWith('.md') && entry.name !== 'README.md') {\n // Single-file skill\n skills.push(entry.name.replace('.md', ''));\n }\n }\n\n return skills;\n } catch {\n return [];\n }\n}\n\n/**\n * Find the memory directory (.agents/memory)\n */\nfunction findMemoryDir(): string | null {\n const projectRoot = findProjectRoot();\n if (!projectRoot) return null;\n\n const memoryDir = join(projectRoot, '.agents', 'memory');\n return existsSync(memoryDir) ? memoryDir : null;\n}\n\n/**\n * Resolve a skill name to its path using three-tier resolution:\n * 1. Squad-local: .agents/squads/<squad>/skills/<skill>\n * 2. Project: .claude/skills/<skill>\n * 3. Global: ~/.claude/skills/<skill>\n *\n * @param skillName - Name of the skill to resolve\n * @param squadDir - Squad directory name for squad-local lookup\n * @returns Resolved skill with path and source, or null if not found\n */\nfunction resolveSkill(skillName: string, squadDir?: string): ResolvedSkill | null {\n // 1. Squad-local skills (highest priority)\n if (squadDir) {\n const squadSkillsDir = findSquadLocalSkillsDir(squadDir);\n if (squadSkillsDir) {\n // Check for directory-style skill\n const dirPath = join(squadSkillsDir, skillName);\n if (existsSync(dirPath) && existsSync(join(dirPath, 'SKILL.md'))) {\n return { name: skillName, path: dirPath, source: 'squad-local' };\n }\n // Check for single-file skill\n const filePath = join(squadSkillsDir, `${skillName}.md`);\n if (existsSync(filePath)) {\n return { name: skillName, path: filePath, source: 'squad-local' };\n }\n }\n }\n\n // 2. Project-level skills\n const projectSkillsDir = findProjectSkillsDir();\n if (projectSkillsDir) {\n const dirPath = join(projectSkillsDir, skillName);\n if (existsSync(dirPath) && existsSync(join(dirPath, 'SKILL.md'))) {\n return { name: skillName, path: dirPath, source: 'project' };\n }\n const filePath = join(projectSkillsDir, `${skillName}.md`);\n if (existsSync(filePath)) {\n return { name: skillName, path: filePath, source: 'project' };\n }\n }\n\n // 3. Global skills\n const globalSkillsDir = findGlobalSkillsDir();\n if (globalSkillsDir) {\n const dirPath = join(globalSkillsDir, skillName);\n if (existsSync(dirPath) && existsSync(join(dirPath, 'SKILL.md'))) {\n return { name: skillName, path: dirPath, source: 'global' };\n }\n const filePath = join(globalSkillsDir, `${skillName}.md`);\n if (existsSync(filePath)) {\n return { name: skillName, path: filePath, source: 'global' };\n }\n }\n\n return null;\n}\n\n/**\n * Get all available squad-local skills for a squad.\n * Scans .agents/squads/<squad>/skills/ directory.\n */\nexport function getSquadLocalSkills(squadDir: string): ResolvedSkill[] {\n const squadSkillsDir = findSquadLocalSkillsDir(squadDir);\n if (!squadSkillsDir) return [];\n\n const skillNames = listSkillsInDir(squadSkillsDir);\n const skills: ResolvedSkill[] = [];\n\n for (const name of skillNames) {\n const resolved = resolveSkill(name, squadDir);\n if (resolved && resolved.source === 'squad-local') {\n skills.push(resolved);\n }\n }\n\n return skills;\n}\n\n/**\n * Resolve memory glob patterns to actual file paths.\n */\nfunction resolveMemoryPaths(patterns: string[]): string[] {\n const memoryDir = findMemoryDir();\n if (!memoryDir) return [];\n\n const resolved: string[] = [];\n\n for (const pattern of patterns) {\n // Handle simple patterns like \"intelligence/*\" or \"research/*\"\n if (pattern.endsWith('/*')) {\n const subdir = pattern.slice(0, -2);\n const subdirPath = join(memoryDir, subdir);\n if (existsSync(subdirPath)) {\n // Add all .md files in the subdirectory\n try {\n const files = readdirSync(subdirPath);\n for (const file of files) {\n if (file.endsWith('.md')) {\n resolved.push(join(subdirPath, file));\n }\n }\n } catch {\n // Ignore read errors\n }\n }\n } else {\n // Direct path\n const fullPath = join(memoryDir, pattern);\n if (existsSync(fullPath)) {\n resolved.push(fullPath);\n }\n }\n }\n\n return resolved;\n}\n\n/**\n * Check for squad-local MCP config (.agents/squads/<squad>/mcp.json)\n */\nfunction findSquadLocalMcpConfig(squadDir: string): string | null {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return null;\n\n const mcpConfigPath = join(squadsDir, squadDir, 'mcp.json');\n return existsSync(mcpConfigPath) ? mcpConfigPath : null;\n}\n\n/**\n * Resolve execution context for a squad.\n *\n * Takes a Squad object and resolves all context references to actual paths:\n * - MCP config path (four-tier resolution: squad-local, user-override, generated, fallback)\n * - Skill directory paths (three-tier: squad-local, project, global)\n * - Memory file paths\n *\n * @param squad - The squad to resolve context for\n * @param forceRegenerate - Force MCP config regeneration\n * @returns Resolved execution context with all paths\n */\nexport function resolveExecutionContext(\n squad: Squad,\n forceRegenerate = false\n): ExecutionContext {\n const ctx = squad.context || {};\n\n // Check for squad-local MCP config first (highest priority)\n const squadLocalMcpConfig = findSquadLocalMcpConfig(squad.dir);\n\n let mcpConfigPath: string;\n let mcpSource: 'squad-local' | 'user-override' | 'generated' | 'fallback';\n let mcpServers: string[] = [];\n\n if (squadLocalMcpConfig) {\n // Use squad-local MCP config\n mcpConfigPath = squadLocalMcpConfig;\n mcpSource = 'squad-local';\n\n // Try to read server names from the config\n try {\n const content = readFileSync(squadLocalMcpConfig, 'utf-8');\n const config = JSON.parse(content);\n mcpServers = Object.keys(config.mcpServers || {});\n } catch {\n // Ignore parse errors\n }\n } else {\n // Fall back to existing resolution (user-override, generated, fallback)\n const mcpResolution: McpResolution = resolveMcpConfig(\n squad.name,\n ctx.mcp,\n forceRegenerate\n );\n mcpConfigPath = mcpResolution.path;\n mcpSource = mcpResolution.source;\n mcpServers = mcpResolution.servers || [];\n }\n\n // Resolve skills with three-tier resolution\n const resolvedSkills: ResolvedSkill[] = [];\n const skillPaths: string[] = []; // For backward compatibility\n\n // First, add any squad-local skills that exist (auto-discovered)\n const squadLocalSkills = getSquadLocalSkills(squad.dir);\n for (const skill of squadLocalSkills) {\n resolvedSkills.push(skill);\n skillPaths.push(skill.path);\n }\n\n // Then, resolve explicitly listed skills from context\n if (ctx.skills) {\n for (const skillName of ctx.skills) {\n // Check if already resolved as squad-local\n if (resolvedSkills.some(s => s.name === skillName)) {\n continue;\n }\n\n const resolved = resolveSkill(skillName, squad.dir);\n if (resolved) {\n resolvedSkills.push(resolved);\n skillPaths.push(resolved.path);\n }\n }\n }\n\n // Resolve memory paths\n const memoryPaths = ctx.memory?.load\n ? resolveMemoryPaths(ctx.memory.load)\n : [];\n\n return {\n // Copy all SquadContext fields\n ...ctx,\n // Add squad name\n squadName: squad.name,\n // Add resolved paths\n resolved: {\n mcpConfigPath,\n mcpSource,\n mcpServers,\n skillPaths, // Backward compatible\n skills: resolvedSkills, // New detailed skill info\n memoryPaths,\n },\n };\n}\n","/**\n * MCP Config Generation and Resolution\n *\n * Provides dynamic MCP config generation based on squad context.\n * Three-tier resolution:\n * 1. User override: ~/.claude/mcp-configs/{squad}.json\n * 2. Generated from context.mcp: ~/.claude/contexts/{squad}.mcp.json\n * 3. Fallback: ~/.claude.json\n */\n\nimport { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';\nimport { join, dirname } from 'path';\n\n/**\n * MCP server definition structure (matches Claude's .mcp.json format)\n */\nexport interface McpServerDef {\n type: 'stdio';\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\n/**\n * Full MCP config structure\n */\nexport interface McpConfig {\n mcpServers: Record<string, McpServerDef>;\n}\n\n/**\n * Registry of known MCP servers with their configurations.\n * These can be referenced by name in SQUAD.md context.mcp arrays.\n *\n * NOTE: We prefer CLI tools over MCP when possible (less complexity).\n * MCP is reserved for APIs that don't have good CLI alternatives.\n */\n/**\n * MCP Registry is now empty - we prefer CLI tools over MCP.\n * If you need to add MCP servers, add them here.\n */\nconst SERVER_REGISTRY: Record<string, McpServerDef> = {};\n\n/**\n * Get the home directory path.\n */\nfunction getHome(): string {\n return process.env.HOME || process.env.USERPROFILE || '';\n}\n\n/**\n * Get the path to generated contexts directory.\n */\nexport function getContextsDir(): string {\n return join(getHome(), '.claude', 'contexts');\n}\n\n/**\n * Get the path to user MCP configs directory.\n */\nexport function getMcpConfigsDir(): string {\n return join(getHome(), '.claude', 'mcp-configs');\n}\n\n/**\n * Check if a server is in the registry.\n */\nexport function isKnownServer(serverName: string): boolean {\n return serverName in SERVER_REGISTRY;\n}\n\n/**\n * Get server definition from registry.\n */\nexport function getServerDef(serverName: string): McpServerDef | undefined {\n return SERVER_REGISTRY[serverName];\n}\n\n/**\n * List all known servers in the registry.\n */\nexport function listKnownServers(): string[] {\n return Object.keys(SERVER_REGISTRY);\n}\n\n/**\n * Generate an MCP config from a list of server names.\n *\n * @param mcpServers - Array of server names to include\n * @returns Generated MCP config object\n */\nexport function generateMcpConfig(mcpServers: string[]): McpConfig {\n const config: McpConfig = { mcpServers: {} };\n\n for (const server of mcpServers) {\n const def = SERVER_REGISTRY[server];\n if (def) {\n config.mcpServers[server] = def;\n }\n // Unknown servers are silently skipped - they may be custom user servers\n }\n\n return config;\n}\n\n/**\n * Write an MCP config to a file.\n *\n * @param config - MCP config to write\n * @param path - Destination path\n */\nexport function writeMcpConfig(config: McpConfig, path: string): void {\n const dir = dirname(path);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(path, JSON.stringify(config, null, 2));\n}\n\n/**\n * Read an existing MCP config file.\n *\n * @param path - Path to config file\n * @returns Parsed config or null if not found\n */\nexport function readMcpConfig(path: string): McpConfig | null {\n if (!existsSync(path)) return null;\n try {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content) as McpConfig;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolution result with metadata.\n */\nexport interface McpResolution {\n /** Path to the MCP config to use */\n path: string;\n /** How the config was resolved */\n source: 'user-override' | 'generated' | 'fallback';\n /** Servers included (if generated or read) */\n servers?: string[];\n /** Whether config was freshly generated */\n generated?: boolean;\n}\n\n/**\n * Resolve the MCP config path for a squad using three-tier resolution:\n *\n * 1. User override: ~/.claude/mcp-configs/{squad}.json\n * 2. Generated from context.mcp: ~/.claude/contexts/{squad}.mcp.json\n * 3. Fallback: ~/.claude.json\n *\n * @param squadName - Name of the squad\n * @param mcpServers - Array of MCP server names from squad context (optional)\n * @param forceRegenerate - Force regeneration even if file exists\n * @returns Resolution result with path and metadata\n */\nexport function resolveMcpConfig(\n squadName: string,\n mcpServers?: string[],\n forceRegenerate = false\n): McpResolution {\n const home = getHome();\n\n // Tier 1: User override\n const userOverride = join(getMcpConfigsDir(), `${squadName}.json`);\n if (existsSync(userOverride)) {\n const config = readMcpConfig(userOverride);\n return {\n path: userOverride,\n source: 'user-override',\n servers: config ? Object.keys(config.mcpServers) : undefined,\n };\n }\n\n // Tier 2: Generate from context.mcp\n if (mcpServers && mcpServers.length > 0) {\n const generatedPath = join(getContextsDir(), `${squadName}.mcp.json`);\n\n // Check if we need to regenerate\n const shouldGenerate = forceRegenerate || !existsSync(generatedPath);\n\n if (shouldGenerate) {\n const config = generateMcpConfig(mcpServers);\n writeMcpConfig(config, generatedPath);\n\n return {\n path: generatedPath,\n source: 'generated',\n servers: Object.keys(config.mcpServers),\n generated: true,\n };\n }\n\n // Use existing generated config\n const config = readMcpConfig(generatedPath);\n return {\n path: generatedPath,\n source: 'generated',\n servers: config ? Object.keys(config.mcpServers) : mcpServers,\n generated: false,\n };\n }\n\n // Tier 3: Fallback to default\n return {\n path: join(home, '.claude.json'),\n source: 'fallback',\n };\n}\n\n/**\n * Convenience function to just get the path (for backward compatibility).\n */\nexport function resolveMcpConfigPath(\n squadName: string,\n mcpServers?: string[]\n): string {\n return resolveMcpConfig(squadName, mcpServers).path;\n}\n","/**\n * Token estimation and tracking for context compression.\n *\n * Uses character-based heuristics for speed (no API calls needed).\n * ~4 characters per token is a reasonable approximation for English text.\n */\n\n// Token-to-character ratios by content type\nconst RATIOS = {\n english: 4.0, // Standard English text\n code: 3.5, // Code tends to have more tokens per char\n json: 3.0, // JSON has many punctuation tokens\n mixed: 3.75, // Default for mixed content\n} as const;\n\nexport type ContentType = keyof typeof RATIOS;\n\n/**\n * Estimate token count from text content.\n *\n * @param text - The text to estimate tokens for\n * @param type - Content type hint for better accuracy\n * @returns Estimated token count\n */\nexport function estimateTokens(text: string, type: ContentType = 'mixed'): number {\n if (!text) return 0;\n const ratio = RATIOS[type];\n return Math.ceil(text.length / ratio);\n}\n\n/**\n * Estimate tokens for a message object (handles different formats).\n */\nexport function estimateMessageTokens(message: {\n role?: string;\n content?: string | Array<{ type: string; text?: string }>;\n}): number {\n // Role overhead (~2-4 tokens)\n let tokens = 4;\n\n if (typeof message.content === 'string') {\n tokens += estimateTokens(message.content);\n } else if (Array.isArray(message.content)) {\n for (const part of message.content) {\n if (part.text) {\n tokens += estimateTokens(part.text);\n }\n // Tool results, images, etc. add overhead\n tokens += 10;\n }\n }\n\n return tokens;\n}\n\n/**\n * Model context limits (in tokens).\n * These are the input context windows, not output limits.\n */\nexport const MODEL_LIMITS: Record<string, number> = {\n // Anthropic models\n 'claude-opus-4-5-20251101': 200_000,\n 'claude-sonnet-4-20250514': 200_000,\n 'claude-3-5-haiku-20241022': 200_000,\n // Aliases\n opus: 200_000,\n sonnet: 200_000,\n haiku: 200_000,\n // Default fallback\n default: 200_000,\n};\n\n/**\n * Get the context limit for a model.\n */\nexport function getModelLimit(model: string): number {\n return MODEL_LIMITS[model] ?? MODEL_LIMITS.default;\n}\n\n/**\n * Token usage tracker for a session.\n */\nexport interface TokenTracker {\n /** Total tokens used so far */\n used: number;\n /** Model's context limit */\n limit: number;\n /** Usage as percentage (0-1) */\n percentage: number;\n /** Breakdown by category */\n breakdown: {\n system: number;\n user: number;\n assistant: number;\n tools: number;\n };\n}\n\n/**\n * Create a new token tracker for a session.\n *\n * @param model - Model name to determine context limit\n * @returns Fresh token tracker\n */\nexport function createTracker(model: string = 'default'): TokenTracker {\n return {\n used: 0,\n limit: getModelLimit(model),\n percentage: 0,\n breakdown: {\n system: 0,\n user: 0,\n assistant: 0,\n tools: 0,\n },\n };\n}\n\n/**\n * Update tracker with new content.\n *\n * @param tracker - Tracker to update (mutated in place)\n * @param content - Content to add\n * @param category - Category for breakdown tracking\n */\nexport function updateTracker(\n tracker: TokenTracker,\n content: string,\n category: keyof TokenTracker['breakdown'] = 'assistant'\n): void {\n const tokens = estimateTokens(content);\n tracker.used += tokens;\n tracker.breakdown[category] += tokens;\n tracker.percentage = tracker.used / tracker.limit;\n}\n\n/**\n * Update tracker from a message object.\n */\nexport function updateTrackerFromMessage(\n tracker: TokenTracker,\n message: { role?: string; content?: string | Array<{ type: string; text?: string }> }\n): void {\n const tokens = estimateMessageTokens(message);\n const category = mapRoleToCategory(message.role);\n tracker.used += tokens;\n tracker.breakdown[category] += tokens;\n tracker.percentage = tracker.used / tracker.limit;\n}\n\nfunction mapRoleToCategory(role?: string): keyof TokenTracker['breakdown'] {\n switch (role) {\n case 'system':\n return 'system';\n case 'user':\n return 'user';\n case 'assistant':\n return 'assistant';\n case 'tool':\n case 'tool_result':\n return 'tools';\n default:\n return 'assistant';\n }\n}\n\n/**\n * Check if compression is needed based on thresholds.\n */\nexport type CompressionLevel = 'none' | 'light' | 'medium' | 'heavy';\n\nexport interface ThresholdConfig {\n light: number; // Default: 0.70\n medium: number; // Default: 0.85\n heavy: number; // Default: 0.95\n}\n\nconst DEFAULT_THRESHOLDS: ThresholdConfig = {\n light: 0.7,\n medium: 0.85,\n heavy: 0.95,\n};\n\n/**\n * Determine what level of compression is needed.\n *\n * @param tracker - Current token tracker state\n * @param thresholds - Custom thresholds (optional)\n * @returns Compression level needed\n */\nexport function getCompressionLevel(\n tracker: TokenTracker,\n thresholds: ThresholdConfig = DEFAULT_THRESHOLDS\n): CompressionLevel {\n if (tracker.percentage >= thresholds.heavy) return 'heavy';\n if (tracker.percentage >= thresholds.medium) return 'medium';\n if (tracker.percentage >= thresholds.light) return 'light';\n return 'none';\n}\n\n/**\n * Format tracker status for display.\n */\nexport function formatTrackerStatus(tracker: TokenTracker): string {\n const pct = (tracker.percentage * 100).toFixed(1);\n const used = (tracker.used / 1000).toFixed(1);\n const limit = (tracker.limit / 1000).toFixed(0);\n const level = getCompressionLevel(tracker);\n\n const levelIndicator =\n level === 'none' ? '' : level === 'light' ? ' [!]' : level === 'medium' ? ' [!!]' : ' [!!!]';\n\n return `${used}K / ${limit}K tokens (${pct}%)${levelIndicator}`;\n}\n","/**\n * File deduplication for context compression.\n *\n * Tracks file reads across a conversation and replaces duplicate\n * reads with concise references to save tokens.\n *\n * Based on Cline's approach: keeping only the latest version of each\n * file prevents LLM confusion during edit operations.\n */\n\nimport { estimateTokens } from './tokens.js';\n\n/**\n * Record of a file read in the conversation.\n */\nexport interface FileReadRecord {\n /** File path that was read */\n path: string;\n /** Turn index where the read occurred */\n turnIndex: number;\n /** Estimated token count of the content */\n tokenCount: number;\n /** Hash of content for change detection */\n contentHash: string;\n}\n\n/**\n * Simple content hash for change detection.\n */\nfunction hashContent(content: string): string {\n // Simple hash - just use first 100 chars + length\n // Full crypto hash would be overkill here\n const prefix = content.slice(0, 100);\n return `${content.length}:${prefix}`;\n}\n\n/**\n * Tracks file reads across a conversation for deduplication.\n */\nexport class FileDeduplicator {\n /** Map of file path to all reads of that file */\n private reads: Map<string, FileReadRecord[]> = new Map();\n\n /** Current turn index */\n private currentTurn = 0;\n\n /**\n * Record a file read.\n *\n * @param path - File path that was read\n * @param content - File content that was read\n */\n trackRead(path: string, content: string): void {\n const record: FileReadRecord = {\n path,\n turnIndex: this.currentTurn,\n tokenCount: estimateTokens(content, 'code'),\n contentHash: hashContent(content),\n };\n\n const existing = this.reads.get(path) || [];\n existing.push(record);\n this.reads.set(path, existing);\n }\n\n /**\n * Advance to next turn.\n */\n nextTurn(): void {\n this.currentTurn++;\n }\n\n /**\n * Get current turn index.\n */\n getTurn(): number {\n return this.currentTurn;\n }\n\n /**\n * Check if a file has been read before.\n *\n * @param path - File path to check\n * @returns Previous read record if exists\n */\n getPreviousRead(path: string): FileReadRecord | undefined {\n const reads = this.reads.get(path);\n if (!reads || reads.length === 0) return undefined;\n return reads[reads.length - 1];\n }\n\n /**\n * Get all files that have been read multiple times.\n *\n * @returns Map of path to read count\n */\n getDuplicateReads(): Map<string, number> {\n const duplicates = new Map<string, number>();\n for (const [path, reads] of this.reads) {\n if (reads.length > 1) {\n duplicates.set(path, reads.length);\n }\n }\n return duplicates;\n }\n\n /**\n * Calculate potential token savings from deduplication.\n */\n getPotentialSavings(): number {\n let savings = 0;\n for (const reads of this.reads.values()) {\n if (reads.length > 1) {\n // Can save all but the most recent read\n for (let i = 0; i < reads.length - 1; i++) {\n savings += reads[i].tokenCount;\n }\n }\n }\n return savings;\n }\n\n /**\n * Generate a deduplication reference message.\n *\n * @param path - File path\n * @param previousTurn - Turn where file was previously read\n */\n static createReference(path: string, previousTurn: number): string {\n return `[File \"${path}\" was read at turn ${previousTurn}. Content unchanged.]`;\n }\n\n /**\n * Reset tracker state.\n */\n reset(): void {\n this.reads.clear();\n this.currentTurn = 0;\n }\n\n /**\n * Get statistics for debugging.\n */\n getStats(): {\n filesTracked: number;\n totalReads: number;\n duplicateReads: number;\n potentialSavings: number;\n } {\n let totalReads = 0;\n let duplicateReads = 0;\n for (const reads of this.reads.values()) {\n totalReads += reads.length;\n if (reads.length > 1) {\n duplicateReads += reads.length - 1;\n }\n }\n\n return {\n filesTracked: this.reads.size,\n totalReads,\n duplicateReads,\n potentialSavings: this.getPotentialSavings(),\n };\n }\n}\n\n/**\n * Message type for deduplication processing.\n */\nexport interface DeduplicatableMessage {\n role: string;\n content: string | Array<{ type: string; text?: string; tool_use_id?: string }>;\n}\n\n/**\n * Extract file paths from tool results.\n */\nexport function extractFileReads(message: DeduplicatableMessage): Array<{ path: string; content: string }> {\n const reads: Array<{ path: string; content: string }> = [];\n\n if (typeof message.content === 'string') {\n // Look for file read patterns in text\n const fileReadPattern = /(?:Reading|Read|Contents of) (?:file )?[`\"']?([^`\"'\\n]+)[`\"']?[\\s\\S]*?(?:```[\\w]*\\n([\\s\\S]*?)```|^(\\d+[→│].+)$)/gm;\n let match;\n while ((match = fileReadPattern.exec(message.content)) !== null) {\n const path = match[1];\n const content = match[2] || match[3] || '';\n if (path && content) {\n reads.push({ path, content });\n }\n }\n } else if (Array.isArray(message.content)) {\n for (const part of message.content) {\n if (part.type === 'tool_result' && part.text) {\n // Tool results often contain file contents\n // Look for common patterns from Read tool\n const lines = part.text.split('\\n');\n const firstLine = lines[0] || '';\n\n // Pattern: \"Reading /path/to/file\"\n if (firstLine.includes('→') || firstLine.match(/^\\s*\\d+[│|]/)) {\n // This looks like file content with line numbers\n // Try to extract path from context\n // (In practice, we'd get this from the tool input)\n }\n }\n }\n }\n\n return reads;\n}\n\n/**\n * Check if content represents a file that was previously read.\n *\n * @param deduplicator - Deduplicator instance\n * @param path - File path\n * @param content - Current content\n * @param savingsThreshold - Minimum savings ratio to deduplicate (default: 0.3 = 30%)\n * @returns Reference message if should deduplicate, null otherwise\n */\nexport function shouldDeduplicate(\n deduplicator: FileDeduplicator,\n path: string,\n content: string,\n savingsThreshold = 0.3\n): string | null {\n const previous = deduplicator.getPreviousRead(path);\n\n if (!previous) {\n // First read - track it\n deduplicator.trackRead(path, content);\n return null;\n }\n\n const currentHash = hashContent(content);\n const currentTokens = estimateTokens(content, 'code');\n\n // Check if content changed\n if (previous.contentHash !== currentHash) {\n // Content changed - keep the new version, track it\n deduplicator.trackRead(path, content);\n return null;\n }\n\n // Calculate savings\n const referenceTokens = estimateTokens(FileDeduplicator.createReference(path, previous.turnIndex));\n const savings = (currentTokens - referenceTokens) / currentTokens;\n\n if (savings < savingsThreshold) {\n // Not worth deduplicating (Cline uses 30% threshold)\n deduplicator.trackRead(path, content);\n return null;\n }\n\n // Track this read but return reference\n deduplicator.trackRead(path, content);\n return FileDeduplicator.createReference(path, previous.turnIndex);\n}\n","/**\n * Token-based pruning for context compression.\n *\n * Based on OpenCode's approach: protect recent tool outputs (40K tokens)\n * while pruning older outputs that exceed the threshold.\n *\n * Key insight: Recent context is critical for coherence. Older tool\n * outputs can be removed entirely without summarization.\n */\n\nimport { estimateMessageTokens } from './tokens.js';\n\n/**\n * Configuration for token pruning.\n */\nexport interface PruneConfig {\n /** Tokens to protect from pruning (recent window). Default: 40000 */\n protectRecent: number;\n /** Minimum tokens that must be prunable before we prune. Default: 20000 */\n minimumPrunable: number;\n /** Tool types that should never be pruned */\n protectedTools: string[];\n}\n\nconst DEFAULT_CONFIG: PruneConfig = {\n protectRecent: 40_000,\n minimumPrunable: 20_000,\n protectedTools: ['skill', 'memory', 'goal'],\n};\n\n/**\n * Message structure for pruning.\n */\nexport interface PrunableMessage {\n role: string;\n content: string | Array<MessagePart>;\n /** Internal: marks message as prunable */\n _prunable?: boolean;\n /** Internal: token count for this message */\n _tokens?: number;\n}\n\nexport interface MessagePart {\n type: string;\n text?: string;\n tool_use_id?: string;\n name?: string;\n /** Internal: marks part as pruned */\n _pruned?: boolean;\n /** Internal: timestamp when pruned */\n _prunedAt?: number;\n}\n\n/**\n * Placeholder for pruned tool output.\n */\nexport function createPrunedPlaceholder(toolName: string, tokensSaved: number): string {\n return `[Tool output pruned: ${toolName} (~${Math.round(tokensSaved / 1000)}K tokens)]`;\n}\n\n/**\n * Token pruner for conversation context.\n */\nexport class TokenPruner {\n private config: PruneConfig;\n\n constructor(config: Partial<PruneConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Prune messages to reduce token count.\n *\n * Strategy:\n * 1. Scan messages backward from newest to oldest\n * 2. Accumulate tokens for tool outputs\n * 3. Mark outputs beyond protection window for pruning\n * 4. Replace pruned outputs with placeholders\n *\n * @param messages - Messages to prune\n * @returns Pruned messages (new array, originals not mutated)\n */\n pruneMessages(messages: PrunableMessage[]): PrunableMessage[] {\n // First pass: calculate tokens for each message\n const annotated = messages.map((msg) => ({\n ...msg,\n _tokens: estimateMessageTokens(msg),\n }));\n\n // Calculate what's prunable\n const analysis = this.analyzePrunability(annotated);\n\n if (analysis.prunableTokens < this.config.minimumPrunable) {\n // Not enough to prune - return unchanged\n return messages;\n }\n\n // Second pass: prune from oldest to newest, stopping at protection window\n return this.applyPruning(annotated, analysis.protectionIndex);\n }\n\n /**\n * Analyze which messages can be pruned.\n */\n private analyzePrunability(messages: Array<PrunableMessage & { _tokens: number }>): {\n prunableTokens: number;\n protectedTokens: number;\n protectionIndex: number;\n } {\n let protectedTokens = 0;\n let protectionIndex = messages.length;\n\n // Scan backward to find protection boundary\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n\n // Only tool results are prunable\n if (!this.isToolResult(msg)) {\n protectedTokens += msg._tokens;\n continue;\n }\n\n // Check if tool is protected type\n if (this.isProtectedTool(msg)) {\n protectedTokens += msg._tokens;\n continue;\n }\n\n // Add to protected until we hit threshold\n if (protectedTokens + msg._tokens <= this.config.protectRecent) {\n protectedTokens += msg._tokens;\n } else {\n // Found the boundary\n protectionIndex = i + 1;\n break;\n }\n }\n\n // Calculate prunable tokens (everything before protection index that's a tool result)\n let prunableTokens = 0;\n for (let i = 0; i < protectionIndex; i++) {\n const msg = messages[i];\n if (this.isToolResult(msg) && !this.isProtectedTool(msg)) {\n prunableTokens += msg._tokens;\n }\n }\n\n return { prunableTokens, protectedTokens, protectionIndex };\n }\n\n /**\n * Apply pruning to messages before the protection index.\n */\n private applyPruning(\n messages: Array<PrunableMessage & { _tokens: number }>,\n protectionIndex: number\n ): PrunableMessage[] {\n return messages.map((msg, i) => {\n // Messages at or after protection index are kept as-is\n if (i >= protectionIndex) {\n // Remove internal annotations\n \n const { _tokens, _prunable, ...clean } = msg;\n return clean;\n }\n\n // Non-tool messages are kept\n if (!this.isToolResult(msg)) {\n \n const { _tokens, _prunable, ...clean } = msg;\n return clean;\n }\n\n // Protected tools are kept\n if (this.isProtectedTool(msg)) {\n \n const { _tokens, _prunable, ...clean } = msg;\n return clean;\n }\n\n // Prune this tool result\n return this.createPrunedMessage(msg);\n });\n }\n\n /**\n * Create a pruned version of a message.\n */\n private createPrunedMessage(msg: PrunableMessage & { _tokens: number }): PrunableMessage {\n const toolName = this.getToolName(msg);\n const placeholder = createPrunedPlaceholder(toolName, msg._tokens);\n\n if (typeof msg.content === 'string') {\n return {\n role: msg.role,\n content: placeholder,\n };\n }\n\n // For array content, replace tool result parts\n const content = msg.content.map((part) => {\n if (part.type === 'tool_result' && part.text) {\n return {\n ...part,\n text: placeholder,\n _pruned: true,\n _prunedAt: Date.now(),\n };\n }\n return part;\n });\n\n return {\n role: msg.role,\n content,\n };\n }\n\n /**\n * Check if a message is a tool result.\n */\n private isToolResult(msg: PrunableMessage): boolean {\n if (msg.role === 'tool') return true;\n\n if (Array.isArray(msg.content)) {\n return msg.content.some((part) => part.type === 'tool_result');\n }\n\n return false;\n }\n\n /**\n * Check if a tool is in the protected list.\n */\n private isProtectedTool(msg: PrunableMessage): boolean {\n const toolName = this.getToolName(msg);\n return this.config.protectedTools.includes(toolName.toLowerCase());\n }\n\n /**\n * Extract tool name from a message.\n */\n private getToolName(msg: PrunableMessage): string {\n if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n if (part.name) return part.name;\n }\n }\n\n // Try to extract from content\n if (typeof msg.content === 'string') {\n const match = msg.content.match(/Tool (?:output|result).*?:\\s*(\\w+)/i);\n if (match) return match[1];\n }\n\n return 'unknown';\n }\n\n /**\n * Get statistics about potential pruning.\n */\n getStats(messages: PrunableMessage[]): {\n totalTokens: number;\n prunableTokens: number;\n protectedTokens: number;\n savingsPercentage: number;\n } {\n const annotated = messages.map((msg) => ({\n ...msg,\n _tokens: estimateMessageTokens(msg),\n }));\n\n const totalTokens = annotated.reduce((sum, msg) => sum + msg._tokens, 0);\n const analysis = this.analyzePrunability(annotated);\n\n return {\n totalTokens,\n prunableTokens: analysis.prunableTokens,\n protectedTokens: analysis.protectedTokens,\n savingsPercentage: totalTokens > 0 ? (analysis.prunableTokens / totalTokens) * 100 : 0,\n };\n }\n}\n","/**\n * LLM-based summarization for heavy context compression.\n *\n * Based on OpenHands' Context Condenser approach:\n * - Keep first N events (initial context)\n * - Keep last M events (recent context)\n * - Summarize the middle section via LLM\n *\n * This is the \"last resort\" compression - only used when at 95%+ context.\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport { estimateTokens } from './tokens.js';\n\n/**\n * Configuration for LLM summarization.\n */\nexport interface SummaryConfig {\n /** Number of messages to preserve from start. Default: 4 */\n keepFirst: number;\n /** Number of messages to preserve from end. Default: 20 */\n keepLast: number;\n /** Model to use for summarization. Default: 'claude-3-5-haiku-20241022' */\n model: string;\n /** Maximum tokens for summary output. Default: 2000 */\n maxSummaryTokens: number;\n}\n\nconst DEFAULT_CONFIG: SummaryConfig = {\n keepFirst: 4,\n keepLast: 20,\n model: 'claude-3-5-haiku-20241022', // Cheap and fast\n maxSummaryTokens: 2000,\n};\n\n/**\n * Message structure for summarization.\n */\nexport interface SummarizableMessage {\n role: string;\n content: string | Array<{ type: string; text?: string }>;\n}\n\n/**\n * Summarization prompt template.\n */\nconst SUMMARIZATION_PROMPT = `You are summarizing a conversation between a user and an AI assistant to reduce context length while preserving essential information.\n\n<conversation_to_summarize>\n{{MIDDLE_CONTENT}}\n</conversation_to_summarize>\n\nCreate a concise summary that preserves:\n1. **User's goals** - What the user is trying to accomplish\n2. **Progress made** - Key actions taken and their outcomes\n3. **Current state** - What's done vs. what still needs to be done\n4. **Critical context** - File paths, error messages, decisions made\n5. **Blockers** - Any issues that need to be resolved\n\nFormat your summary as a structured note that the assistant can reference to continue the work.\n\nKeep the summary under {{MAX_TOKENS}} tokens. Focus on actionable information.`;\n\n/**\n * LLM-based conversation summarizer.\n */\nexport class ConversationSummarizer {\n private config: SummaryConfig;\n private client: Anthropic | null = null;\n\n constructor(config: Partial<SummaryConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Get or create Anthropic client.\n */\n private getClient(): Anthropic {\n if (!this.client) {\n this.client = new Anthropic();\n }\n return this.client;\n }\n\n /**\n * Summarize messages to reduce token count.\n *\n * Strategy:\n * 1. Keep first N messages (system prompt, initial context)\n * 2. Keep last M messages (recent context, current task)\n * 3. Summarize everything in between\n *\n * @param messages - Messages to summarize\n * @returns Summarized messages\n */\n async summarize(messages: SummarizableMessage[]): Promise<SummarizableMessage[]> {\n if (messages.length <= this.config.keepFirst + this.config.keepLast) {\n // Not enough messages to summarize\n return messages;\n }\n\n const firstMessages = messages.slice(0, this.config.keepFirst);\n const middleMessages = messages.slice(this.config.keepFirst, -this.config.keepLast);\n const lastMessages = messages.slice(-this.config.keepLast);\n\n // Generate summary of middle section\n const summary = await this.generateSummary(middleMessages);\n\n // Create summary message\n const summaryMessage: SummarizableMessage = {\n role: 'user',\n content: `[Context Summary - ${middleMessages.length} messages condensed]\\n\\n${summary}`,\n };\n\n return [...firstMessages, summaryMessage, ...lastMessages];\n }\n\n /**\n * Generate a summary of the middle messages.\n */\n private async generateSummary(messages: SummarizableMessage[]): Promise<string> {\n const middleContent = this.formatMessagesForSummary(messages);\n\n const prompt = SUMMARIZATION_PROMPT.replace('{{MIDDLE_CONTENT}}', middleContent).replace(\n '{{MAX_TOKENS}}',\n String(this.config.maxSummaryTokens)\n );\n\n try {\n const client = this.getClient();\n const response = await client.messages.create({\n model: this.config.model,\n max_tokens: this.config.maxSummaryTokens,\n messages: [\n {\n role: 'user',\n content: prompt,\n },\n ],\n });\n\n // Extract text from response\n const textBlock = response.content.find((block) => block.type === 'text');\n if (textBlock && 'text' in textBlock) {\n return textBlock.text;\n }\n\n return '[Summary generation failed - no text in response]';\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return `[Summary generation failed: ${message}]`;\n }\n }\n\n /**\n * Format messages for the summarization prompt.\n */\n private formatMessagesForSummary(messages: SummarizableMessage[]): string {\n return messages\n .map((msg, i) => {\n const role = msg.role.toUpperCase();\n const content = this.extractContent(msg);\n return `[${i + 1}] ${role}:\\n${content}`;\n })\n .join('\\n\\n---\\n\\n');\n }\n\n /**\n * Extract text content from a message.\n */\n private extractContent(msg: SummarizableMessage): string {\n if (typeof msg.content === 'string') {\n return this.truncateContent(msg.content);\n }\n\n const parts: string[] = [];\n for (const part of msg.content) {\n if (part.text) {\n parts.push(this.truncateContent(part.text));\n }\n }\n return parts.join('\\n');\n }\n\n /**\n * Truncate very long content for summary input.\n */\n private truncateContent(content: string, maxChars = 2000): string {\n if (content.length <= maxChars) return content;\n return content.slice(0, maxChars) + `\\n[...truncated ${content.length - maxChars} chars]`;\n }\n\n /**\n * Estimate the cost of summarization.\n *\n * @param messages - Messages that would be summarized\n * @returns Estimated cost in USD\n */\n estimateCost(messages: SummarizableMessage[]): number {\n if (messages.length <= this.config.keepFirst + this.config.keepLast) {\n return 0;\n }\n\n const middleMessages = messages.slice(this.config.keepFirst, -this.config.keepLast);\n const inputTokens = middleMessages.reduce((sum, msg) => {\n const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n return sum + estimateTokens(content);\n }, 0);\n\n // Haiku pricing: $0.25/MTok input, $1.25/MTok output\n const inputCost = (inputTokens / 1_000_000) * 0.25;\n const outputCost = (this.config.maxSummaryTokens / 1_000_000) * 1.25;\n\n return inputCost + outputCost;\n }\n\n /**\n * Get statistics about potential summarization.\n */\n getStats(messages: SummarizableMessage[]): {\n totalMessages: number;\n wouldKeep: number;\n wouldSummarize: number;\n estimatedCost: number;\n } {\n const wouldKeep = Math.min(messages.length, this.config.keepFirst + this.config.keepLast);\n const wouldSummarize = Math.max(0, messages.length - wouldKeep);\n\n return {\n totalMessages: messages.length,\n wouldKeep,\n wouldSummarize,\n estimatedCost: this.estimateCost(messages),\n };\n }\n}\n\n/**\n * Create a summary message without LLM call (for testing/fallback).\n */\nexport function createFallbackSummary(messages: SummarizableMessage[]): string {\n const actions: string[] = [];\n const files: Set<string> = new Set();\n const errors: string[] = [];\n\n for (const msg of messages) {\n const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);\n\n // Extract file paths\n const fileMatches = content.match(/(?:\\/[\\w.\\-/]+\\.[\\w]+)/g);\n if (fileMatches) {\n fileMatches.forEach((f) => files.add(f));\n }\n\n // Extract errors\n if (content.toLowerCase().includes('error')) {\n const errorMatch = content.match(/error[:\\s]+([^\\n]+)/i);\n if (errorMatch && errors.length < 3) {\n errors.push(errorMatch[1].slice(0, 100));\n }\n }\n\n // Extract key actions (from assistant messages)\n if (msg.role === 'assistant' && content.length < 500) {\n actions.push(content.slice(0, 100));\n }\n }\n\n const parts: string[] = ['## Conversation Summary (Fallback)', ''];\n\n if (files.size > 0) {\n parts.push('### Files Referenced');\n parts.push([...files].slice(0, 10).map((f) => `- ${f}`).join('\\n'));\n parts.push('');\n }\n\n if (actions.length > 0) {\n parts.push('### Key Actions');\n parts.push(actions.slice(0, 5).map((a) => `- ${a}`).join('\\n'));\n parts.push('');\n }\n\n if (errors.length > 0) {\n parts.push('### Errors Encountered');\n parts.push(errors.map((e) => `- ${e}`).join('\\n'));\n }\n\n return parts.join('\\n');\n}\n","/**\n * Context Condenser - Main Pipeline\n *\n * Coordinates the three compression strategies:\n * 1. Deduplication (70% threshold) - Replace duplicate file reads\n * 2. Pruning (85% threshold) - Remove old tool outputs\n * 3. Summarization (95% threshold) - LLM-based middle section summary\n *\n * Based on patterns from OpenCode, OpenHands, and Cline.\n */\n\nimport {\n TokenTracker,\n createTracker,\n updateTrackerFromMessage,\n getCompressionLevel,\n CompressionLevel,\n ThresholdConfig,\n formatTrackerStatus,\n} from './tokens.js';\nimport { FileDeduplicator } from './deduplication.js';\nimport { TokenPruner, PruneConfig, PrunableMessage } from './pruning.js';\nimport { ConversationSummarizer, SummaryConfig, SummarizableMessage } from './summarizer.js';\n\n// Re-export types\nexport { TokenTracker, CompressionLevel, ThresholdConfig };\nexport { FileDeduplicator } from './deduplication.js';\nexport { TokenPruner, PruneConfig } from './pruning.js';\nexport { ConversationSummarizer, SummaryConfig } from './summarizer.js';\nexport * from './tokens.js';\n\n/**\n * Configuration for the context condenser.\n */\nexport interface CondenserConfig {\n /** Whether context compression is enabled */\n enabled: boolean;\n /** Threshold for light compression (deduplication) */\n lightThreshold: number;\n /** Threshold for medium compression (pruning) */\n mediumThreshold: number;\n /** Threshold for heavy compression (summarization) */\n heavyThreshold: number;\n /** Model context limit */\n modelLimit: number;\n /** Model name for tracking */\n model: string;\n /** Pruning configuration */\n pruning: Partial<PruneConfig>;\n /** Summarization configuration */\n summarization: Partial<SummaryConfig>;\n}\n\nconst DEFAULT_CONFIG: CondenserConfig = {\n enabled: true,\n lightThreshold: 0.7,\n mediumThreshold: 0.85,\n heavyThreshold: 0.95,\n modelLimit: 200_000,\n model: 'claude-sonnet-4-20250514',\n pruning: {},\n summarization: {},\n};\n\n/**\n * Message type for the condenser pipeline.\n */\nexport interface CondenserMessage extends PrunableMessage, SummarizableMessage {\n role: string;\n content: string | Array<{ type: string; text?: string; tool_use_id?: string; name?: string }>;\n}\n\n/**\n * Result of a condense operation.\n */\nexport interface CondenserResult {\n /** Condensed messages */\n messages: CondenserMessage[];\n /** Compression level applied */\n level: CompressionLevel;\n /** Tokens before compression */\n tokensBefore: number;\n /** Tokens after compression */\n tokensAfter: number;\n /** Savings percentage */\n savingsPercentage: number;\n /** Duration in milliseconds */\n durationMs: number;\n}\n\n/**\n * Context Condenser - Main class.\n */\nexport class ContextCondenser {\n private config: CondenserConfig;\n private tracker: TokenTracker;\n private deduplicator: FileDeduplicator;\n private pruner: TokenPruner;\n private summarizer: ConversationSummarizer;\n\n /** Metrics for tracking */\n private metrics = {\n condensationCount: 0,\n tokensRecovered: 0,\n lastLevel: 'none' as CompressionLevel,\n };\n\n constructor(config: Partial<CondenserConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.tracker = createTracker(this.config.model);\n this.tracker.limit = this.config.modelLimit;\n this.deduplicator = new FileDeduplicator();\n this.pruner = new TokenPruner(this.config.pruning);\n this.summarizer = new ConversationSummarizer(this.config.summarization);\n }\n\n /**\n * Main entry point - condense messages if needed.\n *\n * @param messages - Current conversation messages\n * @returns Condensed messages and metadata\n */\n async condense(messages: CondenserMessage[]): Promise<CondenserResult> {\n const startTime = Date.now();\n\n if (!this.config.enabled) {\n return this.createResult(messages, messages, 'none', startTime);\n }\n\n // Update tracker with all messages\n this.updateTrackerFromMessages(messages);\n\n const level = getCompressionLevel(this.tracker, {\n light: this.config.lightThreshold,\n medium: this.config.mediumThreshold,\n heavy: this.config.heavyThreshold,\n });\n\n if (level === 'none') {\n return this.createResult(messages, messages, level, startTime);\n }\n\n let condensed: CondenserMessage[];\n\n switch (level) {\n case 'light':\n condensed = this.applyDeduplication(messages);\n break;\n case 'medium':\n condensed = this.applyPruning(messages);\n break;\n case 'heavy':\n condensed = await this.applySummarization(messages);\n break;\n }\n\n // Update metrics\n this.metrics.condensationCount++;\n this.metrics.lastLevel = level;\n\n return this.createResult(messages, condensed, level, startTime);\n }\n\n /**\n * Apply light compression (deduplication).\n */\n private applyDeduplication(messages: CondenserMessage[]): CondenserMessage[] {\n // For now, just return messages as-is\n // Full deduplication requires tracking reads across tool calls\n // which we'd integrate with the actual tool execution layer\n return messages;\n }\n\n /**\n * Apply medium compression (pruning).\n */\n private applyPruning(messages: CondenserMessage[]): CondenserMessage[] {\n return this.pruner.pruneMessages(messages) as CondenserMessage[];\n }\n\n /**\n * Apply heavy compression (summarization).\n */\n private async applySummarization(messages: CondenserMessage[]): Promise<CondenserMessage[]> {\n // First apply pruning\n const pruned = this.applyPruning(messages);\n\n // Then summarize if still needed\n const summarized = await this.summarizer.summarize(pruned);\n\n return summarized as CondenserMessage[];\n }\n\n /**\n * Update tracker from messages.\n */\n private updateTrackerFromMessages(messages: CondenserMessage[]): void {\n // Reset tracker\n this.tracker = createTracker(this.config.model);\n this.tracker.limit = this.config.modelLimit;\n\n // Add all messages\n for (const msg of messages) {\n updateTrackerFromMessage(this.tracker, msg);\n }\n }\n\n /**\n * Create result object.\n */\n private createResult(\n before: CondenserMessage[],\n after: CondenserMessage[],\n level: CompressionLevel,\n startTime: number\n ): CondenserResult {\n const tokensBefore = this.estimateTokens(before);\n const tokensAfter = this.estimateTokens(after);\n const savings = tokensBefore > 0 ? ((tokensBefore - tokensAfter) / tokensBefore) * 100 : 0;\n\n if (level !== 'none') {\n this.metrics.tokensRecovered += tokensBefore - tokensAfter;\n }\n\n return {\n messages: after,\n level,\n tokensBefore,\n tokensAfter,\n savingsPercentage: savings,\n durationMs: Date.now() - startTime,\n };\n }\n\n /**\n * Estimate tokens for messages.\n */\n private estimateTokens(messages: CondenserMessage[]): number {\n const tempTracker = createTracker(this.config.model);\n for (const msg of messages) {\n updateTrackerFromMessage(tempTracker, msg);\n }\n return tempTracker.used;\n }\n\n /**\n * Get current tracker status.\n */\n getStatus(): string {\n return formatTrackerStatus(this.tracker);\n }\n\n /**\n * Get tracker for external monitoring.\n */\n getTracker(): TokenTracker {\n return { ...this.tracker };\n }\n\n /**\n * Get metrics.\n */\n getMetrics(): typeof this.metrics {\n return { ...this.metrics };\n }\n\n /**\n * Check if compression is needed.\n */\n needsCompression(): CompressionLevel {\n return getCompressionLevel(this.tracker, {\n light: this.config.lightThreshold,\n medium: this.config.mediumThreshold,\n heavy: this.config.heavyThreshold,\n });\n }\n\n /**\n * Reset condenser state.\n */\n reset(): void {\n this.tracker = createTracker(this.config.model);\n this.tracker.limit = this.config.modelLimit;\n this.deduplicator.reset();\n this.metrics = {\n condensationCount: 0,\n tokensRecovered: 0,\n lastLevel: 'none',\n };\n }\n\n /**\n * Get file deduplicator for integration with tool layer.\n */\n getDeduplicator(): FileDeduplicator {\n return this.deduplicator;\n }\n}\n\n/**\n * Create a condenser with squad-specific configuration.\n */\nexport function createCondenser(squadConfig?: {\n condenser?: {\n enabled?: boolean;\n light_threshold?: number;\n medium_threshold?: number;\n heavy_threshold?: number;\n protect_recent?: number;\n };\n model?: {\n default?: string;\n };\n}): ContextCondenser {\n const condenserConfig = squadConfig?.condenser || {};\n const modelConfig = squadConfig?.model || {};\n\n return new ContextCondenser({\n enabled: condenserConfig.enabled ?? true,\n lightThreshold: condenserConfig.light_threshold ?? 0.7,\n mediumThreshold: condenserConfig.medium_threshold ?? 0.85,\n heavyThreshold: condenserConfig.heavy_threshold ?? 0.95,\n model: modelConfig.default ?? 'claude-sonnet-4-20250514',\n pruning: {\n protectRecent: condenserConfig.protect_recent ?? 40_000,\n },\n });\n}\n"],"mappings":"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","names":["require","readFileSync","existsSync","writeFileSync","join","dirname","config","join","existsSync","join","existsSync","readFileSync","dirname","join","existsSync","readFileSync","writeFileSync","DEFAULT_CONFIG","DEFAULT_CONFIG"]}
@@ -8,7 +8,7 @@ import {
8
8
  searchMemory,
9
9
  updateMemory,
10
10
  updateMemorySync
11
- } from "./chunk-BV6S5AWZ.js";
11
+ } from "./chunk-ZTQ7ISUR.js";
12
12
  import "./chunk-7OCVIDC7.js";
13
13
  export {
14
14
  appendToMemory,
@@ -20,4 +20,4 @@ export {
20
20
  updateMemory,
21
21
  updateMemorySync
22
22
  };
23
- //# sourceMappingURL=memory-ZXDXF6KF.js.map
23
+ //# sourceMappingURL=memory-VNF2VFRB.js.map
@@ -4,8 +4,8 @@ import {
4
4
  sessionsCommand,
5
5
  sessionsHistoryCommand,
6
6
  sessionsSummaryCommand
7
- } from "./chunk-7PRYDHZW.js";
8
- import "./chunk-NA3IECJA.js";
7
+ } from "./chunk-NHGLXN2F.js";
8
+ import "./chunk-N7KDWU4W.js";
9
9
  import "./chunk-7OCVIDC7.js";
10
10
  export {
11
11
  buildCurrentSessionSummary,
@@ -13,4 +13,4 @@ export {
13
13
  sessionsHistoryCommand,
14
14
  sessionsSummaryCommand
15
15
  };
16
- //# sourceMappingURL=sessions-F6LRY7EN.js.map
16
+ //# sourceMappingURL=sessions-6PB7ALCE.js.map
@@ -3,6 +3,7 @@ import {
3
3
  addGoalToSquad,
4
4
  findProjectRoot,
5
5
  findSquadsDir,
6
+ getSquadLocalSkills,
6
7
  hasLocalInfraConfig,
7
8
  listAgents,
8
9
  listSquads,
@@ -12,12 +13,13 @@ import {
12
13
  parseSquadFile,
13
14
  resolveExecutionContext,
14
15
  updateGoalInSquad
15
- } from "./chunk-QPH5OR7J.js";
16
+ } from "./chunk-O7UV3FWI.js";
16
17
  import "./chunk-7OCVIDC7.js";
17
18
  export {
18
19
  addGoalToSquad,
19
20
  findProjectRoot,
20
21
  findSquadsDir,
22
+ getSquadLocalSkills,
21
23
  hasLocalInfraConfig,
22
24
  listAgents,
23
25
  listSquads,
@@ -28,4 +30,4 @@ export {
28
30
  resolveExecutionContext,
29
31
  updateGoalInSquad
30
32
  };
31
- //# sourceMappingURL=squad-parser-MSYE4PXL.js.map
33
+ //# sourceMappingURL=squad-parser-4BI3G4RS.js.map
@@ -0,0 +1,27 @@
1
+ # Business Brief — {{BUSINESS_NAME}}
2
+
3
+ ## About
4
+
5
+ {{BUSINESS_DESCRIPTION}}
6
+
7
+ ## Research Focus
8
+
9
+ {{BUSINESS_FOCUS}}
10
+
11
+ ## Priority
12
+
13
+ **#1**: Deliver value in the research focus above.
14
+
15
+ ## Pressure
16
+
17
+ LOW — Learning phase. Explore, experiment, report findings.
18
+
19
+ ## Decision Framework
20
+
21
+ 1. Does this advance our focus area?
22
+ 2. Is there a simpler approach for 80% of the value?
23
+ 3. What's the opportunity cost?
24
+
25
+ ---
26
+
27
+ *Read by `squads context` for business alignment. Updated by the manager agent as understanding deepens.*
@@ -0,0 +1,69 @@
1
+ # AI Workforce Operating Manual
2
+
3
+ This project uses [Agents Squads](https://agents-squads.com) to run an AI workforce.
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ .agents/
9
+ ├── BUSINESS_BRIEF.md # What this workforce focuses on
10
+ ├── squads/ # Squad definitions
11
+ │ ├── company/ # Operations squad
12
+ │ │ ├── SQUAD.md # Squad config
13
+ │ │ ├── manager.md # AI manager (orchestrates everything)
14
+ │ │ ├── event-dispatcher.md # Routes events to squads
15
+ │ │ ├── goal-tracker.md # Tracks business objectives
16
+ │ │ ├── company-eval.md # Evaluates outputs
17
+ │ │ └── company-critic.md # Identifies improvements
18
+ │ └── research/ # Research squad
19
+ │ ├── SQUAD.md # Squad config
20
+ │ ├── researcher.md # Market/competitor research
21
+ │ ├── analyst.md # Synthesizes insights
22
+ │ ├── research-eval.md # Evaluates quality
23
+ │ └── research-critic.md # Critiques methodology
24
+ ├── memory/ # Persistent state
25
+ │ ├── company/manager/state.md
26
+ │ └── research/researcher/state.md
27
+ ├── skills/
28
+ │ └── squads-cli/SKILL.md # CLI operations manual
29
+ └── config/
30
+ └── provider.yaml # LLM provider config
31
+ ```
32
+
33
+ ## How It Works
34
+
35
+ A **manager agent** coordinates your AI workforce using the `squads` CLI:
36
+ 1. Reads business context from `BUSINESS_BRIEF.md`
37
+ 2. Checks status: `squads dash --json`
38
+ 3. Dispatches work to squad agents
39
+ 4. Tracks results: `squads goal progress`, `squads kpi record`
40
+ 5. Persists learnings: `squads memory write`
41
+
42
+ ## For Humans
43
+
44
+ Talk to Claude naturally. It knows about the squads system via this file.
45
+
46
+ Common commands:
47
+ - `squads status` — What's happening now
48
+ - `squads dash` — Full dashboard
49
+ - `squads run company/manager` — Run the AI manager
50
+ - `squads memory read <squad>` — What has the squad learned
51
+ - `squads goal list` — Business objectives
52
+
53
+ ## For Agents
54
+
55
+ All agents should use the `squads-cli` skill for CLI operations.
56
+ Memory persists across sessions via `.agents/memory/`.
57
+ Git is the coordination layer — commit everything.
58
+
59
+ ## Decision Framework
60
+
61
+ - **Execute**: Clear task, low risk → do it
62
+ - **Escalate**: Ambiguous scope, high cost → ask the human
63
+ - **Learn**: Record what worked/didn't → `squads memory write`
64
+
65
+ ## Commit Signature
66
+
67
+ ```
68
+ Co-Authored-By: Claude <noreply@anthropic.com>
69
+ ```
@@ -0,0 +1,4 @@
1
+ # Squads Provider Configuration
2
+ # Selected during: squads init
3
+
4
+ provider: {{PROVIDER}}