squads-cli 0.5.0 → 0.6.0
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.
- package/README.md +161 -4
- package/dist/{chunk-HKWCBCEK.js → chunk-4CMAEQQY.js} +6 -2
- package/dist/chunk-4CMAEQQY.js.map +1 -0
- package/dist/{chunk-NA3IECJA.js → chunk-N7KDWU4W.js} +155 -58
- package/dist/chunk-N7KDWU4W.js.map +1 -0
- package/dist/{chunk-7PRYDHZW.js → chunk-NHGLXN2F.js} +8 -6
- package/dist/chunk-NHGLXN2F.js.map +1 -0
- package/dist/{chunk-QPH5OR7J.js → chunk-O7UV3FWI.js} +139 -21
- package/dist/chunk-O7UV3FWI.js.map +1 -0
- package/dist/{chunk-BV6S5AWZ.js → chunk-ZTQ7ISUR.js} +28 -109
- package/dist/chunk-ZTQ7ISUR.js.map +1 -0
- package/dist/cli.js +5493 -7665
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +110 -2
- package/dist/index.js +302 -26
- package/dist/index.js.map +1 -1
- package/dist/{memory-ZXDXF6KF.js → memory-VNF2VFRB.js} +2 -2
- package/dist/{sessions-F6LRY7EN.js → sessions-6PB7ALCE.js} +3 -3
- package/dist/{squad-parser-MSYE4PXL.js → squad-parser-4BI3G4RS.js} +4 -2
- package/dist/templates/seed/BUSINESS_BRIEF.md.template +27 -0
- package/dist/templates/seed/CLAUDE.md.template +69 -0
- package/dist/templates/seed/config/provider.yaml +4 -0
- package/dist/templates/seed/hooks/settings.json.template +31 -0
- package/dist/templates/seed/memory/company/manager/state.md +16 -0
- package/dist/templates/seed/memory/engineering/issue-solver/state.md +12 -0
- package/dist/templates/seed/memory/intelligence/intel-lead/state.md +9 -0
- package/dist/templates/seed/memory/marketing/content-drafter/state.md +12 -0
- package/dist/templates/seed/memory/operations/ops-lead/state.md +12 -0
- package/dist/templates/seed/memory/research/researcher/state.md +10 -0
- package/dist/templates/seed/skills/gh/SKILL.md +57 -0
- package/dist/templates/seed/skills/squads-cli/SKILL.md +88 -0
- package/dist/templates/seed/squads/company/SQUAD.md +49 -0
- package/dist/templates/seed/squads/company/company-critic.md +21 -0
- package/dist/templates/seed/squads/company/company-eval.md +21 -0
- package/dist/templates/seed/squads/company/event-dispatcher.md +21 -0
- package/dist/templates/seed/squads/company/goal-tracker.md +21 -0
- package/dist/templates/seed/squads/company/manager.md +66 -0
- package/dist/templates/seed/squads/engineering/SQUAD.md +48 -0
- package/dist/templates/seed/squads/engineering/code-reviewer.md +57 -0
- package/dist/templates/seed/squads/engineering/issue-solver.md +58 -0
- package/dist/templates/seed/squads/engineering/test-writer.md +50 -0
- package/dist/templates/seed/squads/intelligence/SQUAD.md +37 -0
- package/dist/templates/seed/squads/intelligence/intel-critic.md +36 -0
- package/dist/templates/seed/squads/intelligence/intel-eval.md +31 -0
- package/dist/templates/seed/squads/intelligence/intel-lead.md +71 -0
- package/dist/templates/seed/squads/marketing/SQUAD.md +47 -0
- package/dist/templates/seed/squads/marketing/content-drafter.md +71 -0
- package/dist/templates/seed/squads/marketing/growth-analyst.md +49 -0
- package/dist/templates/seed/squads/marketing/social-poster.md +44 -0
- package/dist/templates/seed/squads/operations/SQUAD.md +45 -0
- package/dist/templates/seed/squads/operations/finance-tracker.md +47 -0
- package/dist/templates/seed/squads/operations/goal-tracker.md +48 -0
- package/dist/templates/seed/squads/operations/ops-lead.md +58 -0
- package/dist/templates/seed/squads/research/SQUAD.md +38 -0
- package/dist/templates/seed/squads/research/analyst.md +27 -0
- package/dist/templates/seed/squads/research/research-critic.md +20 -0
- package/dist/templates/seed/squads/research/research-eval.md +20 -0
- package/dist/templates/seed/squads/research/researcher.md +28 -0
- package/dist/{terminal-JZSAQSN7.js → terminal-YKA4O5CX.js} +4 -2
- package/dist/{update-MAY6EXFQ.js → update-ALJKFFM7.js} +3 -2
- package/package.json +8 -21
- package/templates/seed/BUSINESS_BRIEF.md.template +27 -0
- package/templates/seed/CLAUDE.md.template +69 -0
- package/templates/seed/config/provider.yaml +4 -0
- package/templates/seed/hooks/settings.json.template +31 -0
- package/templates/seed/memory/company/manager/state.md +16 -0
- package/templates/seed/memory/engineering/issue-solver/state.md +12 -0
- package/templates/seed/memory/intelligence/intel-lead/state.md +9 -0
- package/templates/seed/memory/marketing/content-drafter/state.md +12 -0
- package/templates/seed/memory/operations/ops-lead/state.md +12 -0
- package/templates/seed/memory/research/researcher/state.md +10 -0
- package/templates/seed/skills/gh/SKILL.md +57 -0
- package/templates/seed/skills/squads-cli/SKILL.md +88 -0
- package/templates/seed/squads/company/SQUAD.md +49 -0
- package/templates/seed/squads/company/company-critic.md +21 -0
- package/templates/seed/squads/company/company-eval.md +21 -0
- package/templates/seed/squads/company/event-dispatcher.md +21 -0
- package/templates/seed/squads/company/goal-tracker.md +21 -0
- package/templates/seed/squads/company/manager.md +66 -0
- package/templates/seed/squads/engineering/SQUAD.md +48 -0
- package/templates/seed/squads/engineering/code-reviewer.md +57 -0
- package/templates/seed/squads/engineering/issue-solver.md +58 -0
- package/templates/seed/squads/engineering/test-writer.md +50 -0
- package/templates/seed/squads/intelligence/SQUAD.md +37 -0
- package/templates/seed/squads/intelligence/intel-critic.md +36 -0
- package/templates/seed/squads/intelligence/intel-eval.md +31 -0
- package/templates/seed/squads/intelligence/intel-lead.md +71 -0
- package/templates/seed/squads/marketing/SQUAD.md +47 -0
- package/templates/seed/squads/marketing/content-drafter.md +71 -0
- package/templates/seed/squads/marketing/growth-analyst.md +49 -0
- package/templates/seed/squads/marketing/social-poster.md +44 -0
- package/templates/seed/squads/operations/SQUAD.md +45 -0
- package/templates/seed/squads/operations/finance-tracker.md +47 -0
- package/templates/seed/squads/operations/goal-tracker.md +48 -0
- package/templates/seed/squads/operations/ops-lead.md +58 -0
- package/templates/seed/squads/research/SQUAD.md +38 -0
- package/templates/seed/squads/research/analyst.md +27 -0
- package/templates/seed/squads/research/research-critic.md +20 -0
- package/templates/seed/squads/research/research-eval.md +20 -0
- package/templates/seed/squads/research/researcher.md +28 -0
- package/dist/chunk-7PRYDHZW.js.map +0 -1
- package/dist/chunk-BV6S5AWZ.js.map +0 -1
- package/dist/chunk-HKWCBCEK.js.map +0 -1
- package/dist/chunk-NA3IECJA.js.map +0 -1
- package/dist/chunk-QPH5OR7J.js.map +0 -1
- package/docker/.env.example +0 -17
- package/docker/README.md +0 -92
- package/docker/docker-compose.engram.yml +0 -304
- package/docker/docker-compose.yml +0 -250
- package/docker/init-db.sql +0 -478
- package/docker/init-engram-db.sql +0 -148
- package/docker/init-langfuse-db.sh +0 -10
- package/docker/otel-collector.yaml +0 -34
- package/docker/squads-bridge/Dockerfile +0 -14
- package/docker/squads-bridge/Dockerfile.proxy +0 -14
- package/docker/squads-bridge/anthropic_proxy.py +0 -313
- package/docker/squads-bridge/requirements.txt +0 -7
- package/docker/squads-bridge/squads_bridge.py +0 -2299
- package/docker/telemetry-ping/Dockerfile +0 -10
- package/docker/telemetry-ping/deploy.sh +0 -69
- package/docker/telemetry-ping/main.py +0 -136
- package/docker/telemetry-ping/requirements.txt +0 -3
- /package/dist/{memory-ZXDXF6KF.js.map → memory-VNF2VFRB.js.map} +0 -0
- /package/dist/{sessions-F6LRY7EN.js.map → sessions-6PB7ALCE.js.map} +0 -0
- /package/dist/{squad-parser-MSYE4PXL.js.map → squad-parser-4BI3G4RS.js.map} +0 -0
- /package/dist/{terminal-JZSAQSN7.js.map → terminal-YKA4O5CX.js.map} +0 -0
- /package/dist/{update-MAY6EXFQ.js.map → update-ALJKFFM7.js.map} +0 -0
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
icons,
|
|
9
9
|
padEnd,
|
|
10
10
|
writeLine
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-N7KDWU4W.js";
|
|
12
12
|
import {
|
|
13
13
|
__require
|
|
14
14
|
} from "./chunk-7OCVIDC7.js";
|
|
@@ -160,18 +160,20 @@ async function enrichProcessesWithSquad(processes) {
|
|
|
160
160
|
squad: detectSquad(cwds[i])
|
|
161
161
|
}));
|
|
162
162
|
}
|
|
163
|
-
async function getLiveSessionSummaryAsync() {
|
|
163
|
+
async function getLiveSessionSummaryAsync(projectRoot) {
|
|
164
164
|
const processes = detectAIProcessesFast();
|
|
165
165
|
const enrichedProcesses = await enrichProcessesWithSquad(processes);
|
|
166
|
+
const root = projectRoot ?? process.cwd();
|
|
167
|
+
const projectProcesses = enrichedProcesses.filter((p) => p.cwd && p.cwd.startsWith(root));
|
|
166
168
|
const bySquad = {};
|
|
167
169
|
const byTool = {};
|
|
168
|
-
for (const proc of
|
|
170
|
+
for (const proc of projectProcesses) {
|
|
169
171
|
const squad = proc.squad || "unknown";
|
|
170
172
|
bySquad[squad] = (bySquad[squad] || 0) + 1;
|
|
171
173
|
byTool[proc.tool] = (byTool[proc.tool] || 0) + 1;
|
|
172
174
|
}
|
|
173
175
|
return {
|
|
174
|
-
totalSessions:
|
|
176
|
+
totalSessions: projectProcesses.length,
|
|
175
177
|
bySquad,
|
|
176
178
|
squadCount: Object.keys(bySquad).length,
|
|
177
179
|
byTool
|
|
@@ -604,7 +606,7 @@ async function sessionsSummaryCommand(data, options = {}) {
|
|
|
604
606
|
async function buildCurrentSessionSummary() {
|
|
605
607
|
const { existsSync: existsSync2, readdirSync: readdirSync2, statSync, readFileSync: readFileSync2 } = await import("fs");
|
|
606
608
|
const { join: join2 } = await import("path");
|
|
607
|
-
const { findMemoryDir } = await import("./memory-
|
|
609
|
+
const { findMemoryDir } = await import("./memory-VNF2VFRB.js");
|
|
608
610
|
const memoryDir = findMemoryDir();
|
|
609
611
|
const squads = [];
|
|
610
612
|
const filesUpdated = [];
|
|
@@ -764,4 +766,4 @@ export {
|
|
|
764
766
|
buildCurrentSessionSummary,
|
|
765
767
|
sessionsHistoryCommand
|
|
766
768
|
};
|
|
767
|
-
//# sourceMappingURL=chunk-
|
|
769
|
+
//# sourceMappingURL=chunk-NHGLXN2F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/sessions.ts","../src/commands/sessions.ts"],"sourcesContent":["/**\n * Session tracking for active Claude Code sessions\n * Provides heartbeat-based session state management\n *\n * Storage:\n * - Active sessions: .agents/sessions/active/{id}.json (quick lookup)\n * - Event history: .agents/sessions/history.jsonl (analytics)\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync, appendFileSync, createReadStream } from 'fs';\nimport { join, dirname } from 'path';\nimport { randomBytes } from 'crypto';\nimport { createInterface } from 'readline';\nimport { execSync } from 'child_process';\n\nexport interface SessionState {\n sessionId: string;\n squad: string | null;\n startedAt: string;\n lastHeartbeat: string;\n cwd: string;\n pid: number;\n}\n\nexport interface SessionSummary {\n totalSessions: number;\n bySquad: Record<string, number>;\n squadCount: number;\n byTool?: Record<string, number>; // Optional: breakdown by AI tool\n}\n\n// Event types for JSONL history\nexport type SessionEventType = 'start' | 'heartbeat' | 'stop' | 'stale_cleanup';\n\nexport interface SessionEvent {\n type: SessionEventType;\n sessionId: string;\n squad: string | null;\n ts: string;\n cwd?: string;\n pid?: number;\n durationMs?: number; // For stop events\n}\n\nexport interface SessionHistoryStats {\n totalSessions: number;\n totalDurationMs: number;\n avgDurationMs: number;\n bySquad: Record<string, { count: number; durationMs: number }>;\n byDate: Record<string, number>; // YYYY-MM-DD -> count\n peakConcurrent: number;\n}\n\n// Live AI coding assistant process info\nexport interface AIProcess {\n pid: number;\n tty: string;\n cwd: string;\n squad: string | null;\n tool: string; // 'claude', 'cursor', 'aider', 'gemini', etc.\n}\n\n// Supported AI coding tools (process names to detect)\nconst AI_TOOL_PATTERNS: { pattern: RegExp; name: string }[] = [\n { pattern: /^claude$/, name: 'claude' },\n { pattern: /^cursor$/i, name: 'cursor' },\n { pattern: /^aider$/, name: 'aider' },\n { pattern: /^gemini$/i, name: 'gemini' },\n { pattern: /^copilot$/i, name: 'copilot' },\n { pattern: /^cody$/i, name: 'cody' },\n { pattern: /^continue$/i, name: 'continue' },\n];\n\n// Session is stale after 5 minutes without heartbeat\nconst STALE_THRESHOLD_MS = 5 * 60 * 1000;\n\n// History file name\nconst HISTORY_FILE = 'history.jsonl';\n\n// Active sessions subdirectory\nconst ACTIVE_DIR = 'active';\n\n// Directory mapping for squad detection\nconst SQUAD_DIR_MAP: Record<string, string> = {\n 'hq': 'company',\n 'agents-squads-web': 'website',\n 'company': 'company',\n 'product': 'product',\n 'engineering': 'engineering',\n 'research': 'research',\n 'intelligence': 'intelligence',\n 'customer': 'customer',\n 'finance': 'finance',\n 'marketing': 'marketing',\n};\n\n/**\n * Find the .agents directory (sessions live at .agents/sessions/)\n */\nexport function findAgentsDir(): string | null {\n let dir = process.cwd();\n\n for (let i = 0; i < 5; i++) {\n const agentsPath = join(dir, '.agents');\n if (existsSync(agentsPath)) {\n return agentsPath;\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n return null;\n}\n\n/**\n * Get the sessions base directory\n */\nexport function getSessionsBaseDir(): string | null {\n const agentsDir = findAgentsDir();\n if (!agentsDir) return null;\n\n const sessionsDir = join(agentsDir, 'sessions');\n if (!existsSync(sessionsDir)) {\n mkdirSync(sessionsDir, { recursive: true });\n }\n return sessionsDir;\n}\n\n/**\n * Get the active sessions directory, creating it if needed\n */\nexport function getSessionsDir(): string | null {\n const baseDir = getSessionsBaseDir();\n if (!baseDir) return null;\n\n const activeDir = join(baseDir, ACTIVE_DIR);\n if (!existsSync(activeDir)) {\n mkdirSync(activeDir, { recursive: true });\n }\n return activeDir;\n}\n\n/**\n * Get the history file path\n */\nexport function getHistoryFilePath(): string | null {\n const baseDir = getSessionsBaseDir();\n if (!baseDir) return null;\n return join(baseDir, HISTORY_FILE);\n}\n\n/**\n * Append an event to the history JSONL file\n */\nfunction appendEvent(event: SessionEvent): void {\n const historyPath = getHistoryFilePath();\n if (!historyPath) return;\n\n try {\n const line = JSON.stringify(event) + '\\n';\n appendFileSync(historyPath, line);\n } catch {\n // Silently fail - history is optional\n }\n}\n\n/**\n * Detect which squad based on current working directory\n */\nexport function detectSquad(cwd: string = process.cwd()): string | null {\n // Pattern: .../agents-squads/{domain}/...\n const match = cwd.match(/agents-squads\\/([^/]+)/);\n if (match) {\n const dir = match[1];\n return SQUAD_DIR_MAP[dir] || dir;\n }\n return null;\n}\n\n/**\n * Detect running AI coding assistant processes (fast version - no lsof)\n * Returns processes immediately without cwd/squad info\n * Supports: Claude, Cursor, Aider, Gemini, Copilot, Cody, Continue\n */\nexport function detectAIProcessesFast(): AIProcess[] {\n const processes: AIProcess[] = [];\n\n try {\n // Get all processes - we'll filter for AI tools\n // Short timeout for responsiveness - this is called during dashboard render\n const psOutput = execSync('ps -eo pid,tty,comm 2>/dev/null', {\n encoding: 'utf-8',\n timeout: 2000, // Reduced from 5s for faster CLI response\n }).trim();\n\n if (!psOutput) return [];\n\n const lines = psOutput.split('\\n').filter(line => line.trim());\n\n for (const line of lines) {\n const parts = line.trim().split(/\\s+/);\n if (parts.length < 3) continue;\n\n const pid = parseInt(parts[0], 10);\n const tty = parts[1];\n const comm = parts.slice(2).join(' '); // Process name (may have spaces)\n\n if (isNaN(pid)) continue;\n\n // Check if this is an AI coding tool\n let toolName: string | null = null;\n for (const { pattern, name } of AI_TOOL_PATTERNS) {\n if (pattern.test(comm)) {\n toolName = name;\n break;\n }\n }\n\n if (!toolName) continue;\n\n processes.push({\n pid,\n tty,\n cwd: '', // No cwd in fast mode\n squad: null, // No squad detection in fast mode\n tool: toolName,\n });\n }\n } catch {\n // Process detection failed (command not found, timeout, etc.)\n // Return empty array - graceful degradation\n }\n\n return processes;\n}\n\n/**\n * Get cwd for a single process using lsof (async)\n */\nasync function getProcessCwd(pid: number): Promise<string> {\n return new Promise((resolve) => {\n try {\n const { exec } = require('child_process');\n exec(`lsof -p ${pid} 2>/dev/null | grep cwd | awk '{print $NF}'`, {\n encoding: 'utf-8',\n timeout: 3000,\n }, (error: Error | null, stdout: string) => {\n resolve(error ? '' : stdout.trim());\n });\n } catch {\n resolve('');\n }\n });\n}\n\n/**\n * Enrich processes with cwd and squad info (async, parallel lsof)\n * Call this after detectAIProcessesFast() when you need squad info\n */\nexport async function enrichProcessesWithSquad(processes: AIProcess[]): Promise<AIProcess[]> {\n if (processes.length === 0) return processes;\n\n // Run lsof for all processes in parallel\n const cwdPromises = processes.map(p => getProcessCwd(p.pid));\n const cwds = await Promise.all(cwdPromises);\n\n // Enrich each process with cwd and squad\n return processes.map((proc, i) => ({\n ...proc,\n cwd: cwds[i],\n squad: detectSquad(cwds[i]),\n }));\n}\n\n/**\n * Detect running AI coding assistant processes (full version with squad info)\n * Synchronous wrapper for backwards compatibility - calls lsof sequentially\n * For better performance, use detectAIProcessesFast() + enrichProcessesWithSquad()\n */\nexport function detectAIProcesses(): AIProcess[] {\n const processes = detectAIProcessesFast();\n\n // Synchronously enrich with cwd/squad (backwards compatible behavior)\n for (const proc of processes) {\n try {\n const lsofOutput = execSync(`lsof -p ${proc.pid} 2>/dev/null | grep cwd | awk '{print $NF}'`, {\n encoding: 'utf-8',\n timeout: 3000,\n }).trim();\n proc.cwd = lsofOutput || '';\n proc.squad = detectSquad(proc.cwd);\n } catch {\n // Keep empty cwd and null squad\n }\n }\n\n return processes;\n}\n\n// Backwards compatibility alias\nexport const detectClaudeProcesses = detectAIProcesses;\n\n/**\n * Get live session summary using real process detection (fast version)\n * Returns count immediately, squad breakdown shows 'unknown' until enriched\n */\nexport function getLiveSessionSummaryFast(): SessionSummary {\n const processes = detectAIProcessesFast();\n const bySquad: Record<string, number> = {};\n const byTool: Record<string, number> = {};\n\n for (const proc of processes) {\n const squad = proc.squad || 'unknown';\n bySquad[squad] = (bySquad[squad] || 0) + 1;\n byTool[proc.tool] = (byTool[proc.tool] || 0) + 1;\n }\n\n return {\n totalSessions: processes.length,\n bySquad,\n squadCount: Object.keys(bySquad).length,\n byTool,\n };\n}\n\n/**\n * Get live session summary with full squad detection (async, parallel lsof)\n * Use this when you need accurate squad breakdown\n */\nexport async function getLiveSessionSummaryAsync(projectRoot?: string): Promise<SessionSummary> {\n const processes = detectAIProcessesFast();\n const enrichedProcesses = await enrichProcessesWithSquad(processes);\n\n // Filter to only sessions within the current project root\n const root = projectRoot ?? process.cwd();\n const projectProcesses = enrichedProcesses.filter(p => p.cwd && p.cwd.startsWith(root));\n\n const bySquad: Record<string, number> = {};\n const byTool: Record<string, number> = {};\n\n for (const proc of projectProcesses) {\n const squad = proc.squad || 'unknown';\n bySquad[squad] = (bySquad[squad] || 0) + 1;\n byTool[proc.tool] = (byTool[proc.tool] || 0) + 1;\n }\n\n return {\n totalSessions: projectProcesses.length,\n bySquad,\n squadCount: Object.keys(bySquad).length,\n byTool,\n };\n}\n\n/**\n * Get live session summary using real process detection (synchronous, backwards compatible)\n * For better performance, use getLiveSessionSummaryFast() or getLiveSessionSummaryAsync()\n */\nexport function getLiveSessionSummary(): SessionSummary {\n const processes = detectAIProcesses();\n const bySquad: Record<string, number> = {};\n const byTool: Record<string, number> = {};\n\n for (const proc of processes) {\n const squad = proc.squad || 'unknown';\n bySquad[squad] = (bySquad[squad] || 0) + 1;\n byTool[proc.tool] = (byTool[proc.tool] || 0) + 1;\n }\n\n return {\n totalSessions: processes.length,\n bySquad,\n squadCount: Object.keys(bySquad).length,\n byTool,\n };\n}\n\n/**\n * Generate a unique session ID\n */\nfunction generateSessionId(): string {\n return randomBytes(8).toString('hex');\n}\n\n/**\n * Get current session ID from environment or generate new one\n */\nlet currentSessionId: string | null = null;\n\nexport function getSessionId(): string {\n if (currentSessionId) return currentSessionId;\n\n // Check if we have a session file for this PID\n const sessionsDir = getSessionsDir();\n if (sessionsDir) {\n const pid = process.pid;\n const files = readdirSync(sessionsDir).filter(f => f.endsWith('.json'));\n\n for (const file of files) {\n try {\n const content = readFileSync(join(sessionsDir, file), 'utf-8');\n const session = JSON.parse(content) as SessionState;\n if (session.pid === pid) {\n currentSessionId = session.sessionId;\n return currentSessionId;\n }\n } catch {\n // Ignore parse errors\n }\n }\n }\n\n // Generate new session ID\n currentSessionId = generateSessionId();\n return currentSessionId;\n}\n\n/**\n * Start a new session (write initial state file and log event)\n */\nexport function startSession(squad?: string): SessionState | null {\n const sessionsDir = getSessionsDir();\n if (!sessionsDir) return null;\n\n const sessionId = getSessionId();\n const now = new Date().toISOString();\n const cwd = process.cwd();\n const detectedSquad = squad || detectSquad(cwd);\n\n const session: SessionState = {\n sessionId,\n squad: detectedSquad,\n startedAt: now,\n lastHeartbeat: now,\n cwd,\n pid: process.pid,\n };\n\n const sessionPath = join(sessionsDir, `${sessionId}.json`);\n writeFileSync(sessionPath, JSON.stringify(session, null, 2));\n\n // Log start event to history\n appendEvent({\n type: 'start',\n sessionId,\n squad: detectedSquad,\n ts: now,\n cwd,\n pid: process.pid,\n });\n\n return session;\n}\n\n/**\n * Update heartbeat for current session\n */\nexport function updateHeartbeat(): boolean {\n const sessionsDir = getSessionsDir();\n if (!sessionsDir) return false;\n\n const sessionId = getSessionId();\n const sessionPath = join(sessionsDir, `${sessionId}.json`);\n\n if (!existsSync(sessionPath)) {\n // Session doesn't exist, start a new one\n startSession();\n return true;\n }\n\n try {\n const content = readFileSync(sessionPath, 'utf-8');\n const session = JSON.parse(content) as SessionState;\n session.lastHeartbeat = new Date().toISOString();\n writeFileSync(sessionPath, JSON.stringify(session, null, 2));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Stop current session (remove state file and log event)\n */\nexport function stopSession(): boolean {\n const sessionsDir = getSessionsDir();\n if (!sessionsDir) return false;\n\n const sessionId = getSessionId();\n const sessionPath = join(sessionsDir, `${sessionId}.json`);\n\n if (existsSync(sessionPath)) {\n // Read session to get start time and squad for duration calculation\n let squad: string | null = null;\n let durationMs: number | undefined;\n\n try {\n const content = readFileSync(sessionPath, 'utf-8');\n const session = JSON.parse(content) as SessionState;\n squad = session.squad;\n durationMs = Date.now() - new Date(session.startedAt).getTime();\n } catch {\n // Ignore parse errors\n }\n\n unlinkSync(sessionPath);\n currentSessionId = null;\n\n // Log stop event to history\n appendEvent({\n type: 'stop',\n sessionId,\n squad,\n ts: new Date().toISOString(),\n durationMs,\n });\n\n return true;\n }\n\n return false;\n}\n\n/**\n * Get all active sessions (non-stale)\n */\nexport function getActiveSessions(): SessionState[] {\n const sessionsDir = getSessionsDir();\n if (!sessionsDir) return [];\n\n const now = Date.now();\n const sessions: SessionState[] = [];\n\n try {\n const files = readdirSync(sessionsDir).filter(f => f.endsWith('.json'));\n\n for (const file of files) {\n try {\n const filePath = join(sessionsDir, file);\n const content = readFileSync(filePath, 'utf-8');\n const session = JSON.parse(content) as SessionState;\n\n // Check if session is stale\n const lastHeartbeat = new Date(session.lastHeartbeat).getTime();\n if (now - lastHeartbeat < STALE_THRESHOLD_MS) {\n sessions.push(session);\n }\n } catch {\n // Ignore parse errors\n }\n }\n } catch {\n // Directory read error\n }\n\n return sessions;\n}\n\n/**\n * Get session summary for dashboard\n */\nexport function getSessionSummary(): SessionSummary {\n const sessions = getActiveSessions();\n const bySquad: Record<string, number> = {};\n\n for (const session of sessions) {\n const squad = session.squad || 'unknown';\n bySquad[squad] = (bySquad[squad] || 0) + 1;\n }\n\n return {\n totalSessions: sessions.length,\n bySquad,\n squadCount: Object.keys(bySquad).length,\n };\n}\n\n/**\n * Clean up stale sessions (older than threshold) and log events\n */\nexport function cleanupStaleSessions(): number {\n const sessionsDir = getSessionsDir();\n if (!sessionsDir) return 0;\n\n const now = Date.now();\n const nowIso = new Date().toISOString();\n let cleaned = 0;\n\n try {\n const files = readdirSync(sessionsDir).filter(f => f.endsWith('.json'));\n\n for (const file of files) {\n try {\n const filePath = join(sessionsDir, file);\n const content = readFileSync(filePath, 'utf-8');\n const session = JSON.parse(content) as SessionState;\n\n const lastHeartbeat = new Date(session.lastHeartbeat).getTime();\n if (now - lastHeartbeat >= STALE_THRESHOLD_MS) {\n const durationMs = now - new Date(session.startedAt).getTime();\n\n unlinkSync(filePath);\n cleaned++;\n\n // Log stale cleanup event\n appendEvent({\n type: 'stale_cleanup',\n sessionId: session.sessionId,\n squad: session.squad,\n ts: nowIso,\n durationMs,\n });\n }\n } catch {\n // If we can't parse, remove the file\n try {\n unlinkSync(join(sessionsDir, file));\n cleaned++;\n } catch {\n // Ignore\n }\n }\n }\n } catch {\n // Directory read error\n }\n\n return cleaned;\n}\n\n/**\n * Read all events from history file\n */\nexport async function readSessionHistory(options: {\n since?: Date;\n until?: Date;\n squad?: string;\n type?: SessionEventType;\n limit?: number;\n} = {}): Promise<SessionEvent[]> {\n const historyPath = getHistoryFilePath();\n if (!historyPath || !existsSync(historyPath)) return [];\n\n const events: SessionEvent[] = [];\n const sinceMs = options.since?.getTime() || 0;\n const untilMs = options.until?.getTime() || Date.now();\n\n return new Promise((resolve) => {\n const rl = createInterface({\n input: createReadStream(historyPath),\n crlfDelay: Infinity,\n });\n\n rl.on('line', (line) => {\n if (!line.trim()) return;\n\n try {\n const event = JSON.parse(line) as SessionEvent;\n const eventMs = new Date(event.ts).getTime();\n\n // Apply filters\n if (eventMs < sinceMs || eventMs > untilMs) return;\n if (options.squad && event.squad !== options.squad) return;\n if (options.type && event.type !== options.type) return;\n\n events.push(event);\n } catch {\n // Ignore parse errors\n }\n });\n\n rl.on('close', () => {\n // Apply limit (from end, most recent first)\n if (options.limit && events.length > options.limit) {\n resolve(events.slice(-options.limit));\n } else {\n resolve(events);\n }\n });\n\n rl.on('error', () => {\n resolve([]);\n });\n });\n}\n\n/**\n * Get session history statistics\n */\nexport async function getSessionHistoryStats(options: {\n since?: Date;\n until?: Date;\n squad?: string;\n} = {}): Promise<SessionHistoryStats> {\n const events = await readSessionHistory(options);\n\n const stats: SessionHistoryStats = {\n totalSessions: 0,\n totalDurationMs: 0,\n avgDurationMs: 0,\n bySquad: {},\n byDate: {},\n peakConcurrent: 0,\n };\n\n // Track active sessions for peak concurrent calculation\n const activeSessions = new Map<string, { squad: string | null; startTs: number }>();\n let currentConcurrent = 0;\n\n for (const event of events) {\n const squad = event.squad || 'unknown';\n const date = event.ts.split('T')[0]; // YYYY-MM-DD\n\n if (event.type === 'start') {\n stats.totalSessions++;\n stats.byDate[date] = (stats.byDate[date] || 0) + 1;\n\n if (!stats.bySquad[squad]) {\n stats.bySquad[squad] = { count: 0, durationMs: 0 };\n }\n stats.bySquad[squad].count++;\n\n // Track for concurrent calculation\n activeSessions.set(event.sessionId, {\n squad: event.squad,\n startTs: new Date(event.ts).getTime(),\n });\n currentConcurrent++;\n stats.peakConcurrent = Math.max(stats.peakConcurrent, currentConcurrent);\n }\n\n if (event.type === 'stop' || event.type === 'stale_cleanup') {\n const duration = event.durationMs || 0;\n stats.totalDurationMs += duration;\n\n if (stats.bySquad[squad]) {\n stats.bySquad[squad].durationMs += duration;\n }\n\n // Remove from concurrent tracking\n if (activeSessions.has(event.sessionId)) {\n activeSessions.delete(event.sessionId);\n currentConcurrent = Math.max(0, currentConcurrent - 1);\n }\n }\n }\n\n // Calculate average duration\n const completedSessions = events.filter(e => e.type === 'stop' || e.type === 'stale_cleanup').length;\n if (completedSessions > 0) {\n stats.avgDurationMs = Math.round(stats.totalDurationMs / completedSessions);\n }\n\n return stats;\n}\n\n/**\n * Get recent session events (for display)\n */\nexport async function getRecentSessions(limit: number = 20): Promise<SessionEvent[]> {\n const events = await readSessionHistory({ limit: limit * 3 }); // Get more to filter\n\n // Group by session and get start/stop pairs\n const sessionEvents = new Map<string, SessionEvent[]>();\n for (const event of events) {\n if (!sessionEvents.has(event.sessionId)) {\n sessionEvents.set(event.sessionId, []);\n }\n sessionEvents.get(event.sessionId)!.push(event);\n }\n\n // Get the most recent start events\n const startEvents = events\n .filter(e => e.type === 'start')\n .slice(-limit);\n\n return startEvents.reverse(); // Most recent first\n}\n","/**\n * List active Claude Code sessions across squads\n */\n\nimport {\n getActiveSessions,\n getSessionSummary,\n cleanupStaleSessions,\n getSessionHistoryStats,\n getRecentSessions,\n SessionState,\n} from '../lib/sessions.js';\nimport {\n colors,\n bold,\n RESET,\n gradient,\n box,\n padEnd,\n icons,\n writeLine,\n} from '../lib/terminal.js';\n\ninterface SessionsOptions {\n verbose?: boolean;\n json?: boolean;\n}\n\nexport async function sessionsCommand(\n options: SessionsOptions = {}\n): Promise<void> {\n // Clean up stale sessions first\n cleanupStaleSessions();\n\n const sessions = getActiveSessions();\n const summary = getSessionSummary();\n\n // JSON output for scripts\n if (options.json) {\n console.log(JSON.stringify({ sessions, summary }, null, 2));\n return;\n }\n\n writeLine();\n writeLine(` ${gradient('squads')} ${colors.dim}sessions${RESET}`);\n writeLine();\n\n if (sessions.length === 0) {\n writeLine(` ${colors.dim}No active sessions${RESET}`);\n writeLine();\n writeLine(` ${colors.dim}Sessions are tracked automatically when Claude Code runs.${RESET}`);\n writeLine(` ${colors.dim}Each session updates its heartbeat via squads CLI commands.${RESET}`);\n writeLine();\n return;\n }\n\n // Summary line\n const squadText = summary.squadCount === 1 ? 'squad' : 'squads';\n const sessionText = summary.totalSessions === 1 ? 'session' : 'sessions';\n writeLine(` ${colors.green}${summary.totalSessions}${RESET} active ${sessionText} ${colors.dim}across${RESET} ${colors.cyan}${summary.squadCount}${RESET} ${squadText}`);\n writeLine();\n\n // Group by squad\n const bySquad: Record<string, SessionState[]> = {};\n for (const session of sessions) {\n const squad = session.squad || 'unknown';\n if (!bySquad[squad]) bySquad[squad] = [];\n bySquad[squad].push(session);\n }\n\n // Table\n const w = { squad: 16, sessions: 10, activity: 14 };\n const tableWidth = w.squad + w.sessions + w.activity + 4;\n\n writeLine(` ${colors.purple}${box.topLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.topRight}${RESET}`);\n\n const header = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${bold}${padEnd('SQUAD', w.squad)}${RESET}` +\n `${bold}${padEnd('SESSIONS', w.sessions)}${RESET}` +\n `${bold}LAST ACTIVITY${RESET}` +\n ` ${colors.purple}${box.vertical}${RESET}`;\n writeLine(header);\n\n writeLine(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);\n\n for (const [squad, squadSessions] of Object.entries(bySquad).sort()) {\n // Find most recent activity\n let mostRecent = 0;\n for (const session of squadSessions) {\n const ts = new Date(session.lastHeartbeat).getTime();\n if (ts > mostRecent) mostRecent = ts;\n }\n\n const lastActivity = formatTimeAgo(mostRecent);\n const activityColor = getActivityColor(mostRecent);\n\n const row = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${colors.cyan}${padEnd(squad, w.squad)}${RESET}` +\n `${padEnd(String(squadSessions.length), w.sessions)}` +\n `${padEnd(`${activityColor}${lastActivity}${RESET}`, w.activity)}` +\n `${colors.purple}${box.vertical}${RESET}`;\n\n writeLine(row);\n }\n\n writeLine(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);\n\n // Verbose: show individual sessions\n if (options.verbose) {\n writeLine();\n writeLine(` ${bold}Session Details${RESET}`);\n writeLine();\n\n for (const session of sessions) {\n const squad = session.squad || 'unknown';\n const ago = formatTimeAgo(new Date(session.lastHeartbeat).getTime());\n\n writeLine(` ${icons.active} ${colors.white}${session.sessionId}${RESET}`);\n writeLine(` ${colors.dim}squad: ${squad} | pid: ${session.pid} | heartbeat: ${ago}${RESET}`);\n writeLine(` ${colors.dim}cwd: ${session.cwd}${RESET}`);\n }\n }\n\n writeLine();\n writeLine(` ${colors.dim}$${RESET} squads sessions -v ${colors.dim}Show session details${RESET}`);\n writeLine();\n}\n\n/**\n * Format timestamp as \"Xm ago\" or \"Xs ago\"\n */\nfunction formatTimeAgo(timestamp: number): string {\n const now = Date.now();\n const diff = now - timestamp;\n\n const seconds = Math.floor(diff / 1000);\n const minutes = Math.floor(seconds / 60);\n\n if (minutes >= 1) {\n return `${minutes}m ago`;\n }\n return `${seconds}s ago`;\n}\n\n/**\n * Get color based on how recent the activity is\n */\nfunction getActivityColor(timestamp: number): string {\n const now = Date.now();\n const diff = now - timestamp;\n\n const minutes = Math.floor(diff / (1000 * 60));\n\n if (minutes < 1) return colors.green;\n if (minutes < 3) return colors.yellow;\n return colors.dim;\n}\n\ninterface HistoryOptions {\n days?: number;\n squad?: string;\n json?: boolean;\n}\n\ninterface SummaryOptions {\n json?: boolean;\n}\n\nexport interface SessionSummaryData {\n squads: Array<{\n name: string;\n actions: string;\n outputs: string;\n }>;\n decisions?: Array<{\n question: string;\n answer: string;\n }>;\n customer?: {\n vertical: string;\n persona: string;\n painPoints: string[];\n };\n nextActions?: Array<{\n squad: string;\n action: string;\n }>;\n filesUpdated?: string[];\n targets?: {\n metric: string;\n value: string;\n }[];\n model?: string; // e.g., \"Claude Opus 4.5\"\n duration?: string; // e.g., \"45m\"\n}\n\n/**\n * Show a pretty summary of session work\n */\nexport async function sessionsSummaryCommand(\n data: SessionSummaryData,\n options: SummaryOptions = {}\n): Promise<void> {\n if (options.json) {\n console.log(JSON.stringify(data, null, 2));\n return;\n }\n\n writeLine();\n writeLine(` ${gradient('squads')} ${colors.dim}session summary${RESET}`);\n writeLine();\n\n // Squads table\n if (data.squads.length > 0) {\n const w = { squad: 14, actions: 26, outputs: 36 };\n const tableWidth = w.squad + w.actions + w.outputs + 6;\n\n // Helper to truncate text\n const truncate = (text: string, max: number) =>\n text.length > max ? text.substring(0, max - 1) + '…' : text;\n\n writeLine(` ${colors.green}${icons.active}${RESET} ${bold}${data.squads.length} Squads Active${RESET}`);\n writeLine();\n\n writeLine(` ${colors.purple}${box.topLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.topRight}${RESET}`);\n\n const header = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${bold}${padEnd('SQUAD', w.squad)}${RESET}` +\n `${bold}${padEnd('ACTIONS', w.actions)}${RESET}` +\n `${bold}${padEnd('KEY OUTPUTS', w.outputs)}${RESET}` +\n `${colors.purple}${box.vertical}${RESET}`;\n writeLine(header);\n\n writeLine(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);\n\n for (const squad of data.squads) {\n const row = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${colors.cyan}${padEnd(truncate(squad.name, w.squad - 1), w.squad)}${RESET}` +\n `${padEnd(truncate(squad.actions, w.actions - 1), w.actions)}` +\n `${padEnd(truncate(squad.outputs, w.outputs - 1), w.outputs)}` +\n `${colors.purple}${box.vertical}${RESET}`;\n writeLine(row);\n }\n\n writeLine(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);\n }\n\n // Decisions\n if (data.decisions && data.decisions.length > 0) {\n writeLine();\n writeLine(` ${bold}Strategic Decisions${RESET}`);\n writeLine();\n\n const w = { question: 16, answer: 50 };\n const tableWidth = w.question + w.answer + 4;\n\n writeLine(` ${colors.purple}${box.topLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.topRight}${RESET}`);\n\n for (const decision of data.decisions) {\n const row = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${colors.yellow}${padEnd(decision.question, w.question)}${RESET}` +\n `${padEnd(decision.answer, w.answer)}` +\n `${colors.purple}${box.vertical}${RESET}`;\n writeLine(row);\n }\n\n writeLine(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);\n }\n\n // Target customer\n if (data.customer) {\n writeLine();\n writeLine(` ${bold}Target Customer${RESET}`);\n writeLine();\n writeLine(` ${colors.dim}Vertical:${RESET} ${colors.cyan}${data.customer.vertical}${RESET}`);\n writeLine(` ${colors.dim}Persona:${RESET} ${colors.white}${data.customer.persona}${RESET}`);\n writeLine(` ${colors.dim}Pain:${RESET} ${data.customer.painPoints.join(', ')}`);\n }\n\n // Next actions\n if (data.nextActions && data.nextActions.length > 0) {\n writeLine();\n writeLine(` ${bold}Next Actions${RESET}`);\n writeLine();\n\n for (const action of data.nextActions) {\n writeLine(` ${colors.cyan}${padEnd(action.squad, 14)}${RESET}${colors.dim}→${RESET} ${action.action}`);\n }\n }\n\n // Q1 Targets\n if (data.targets && data.targets.length > 0) {\n writeLine();\n writeLine(` ${bold}Q1 Targets${RESET}`);\n writeLine();\n\n for (const target of data.targets) {\n writeLine(` ${colors.dim}•${RESET} ${target.metric}: ${colors.green}${target.value}${RESET}`);\n }\n }\n\n // Files updated\n if (data.filesUpdated && data.filesUpdated.length > 0) {\n writeLine();\n writeLine(` ${colors.dim}Files updated:${RESET}`);\n for (const file of data.filesUpdated) {\n writeLine(` ${colors.dim} •${RESET} ${colors.cyan}${file}${RESET}`);\n }\n }\n\n writeLine();\n\n // Footer with model attribution\n const modelText = data.model ? data.model : 'Claude';\n const durationText = data.duration ? ` ${colors.dim}(${data.duration})${RESET}` : '';\n writeLine(` ${colors.dim}Generated by${RESET} ${colors.purple}${modelText}${RESET}${durationText}`);\n writeLine();\n}\n\n/**\n * Build summary from current session by detecting recent activity\n */\nexport async function buildCurrentSessionSummary(): Promise<SessionSummaryData> {\n const { existsSync, readdirSync, statSync, readFileSync } = await import('fs');\n const { join } = await import('path');\n const { findMemoryDir } = await import('../lib/memory.js');\n\n const memoryDir = findMemoryDir();\n const squads: SessionSummaryData['squads'] = [];\n const filesUpdated: string[] = [];\n\n // Look for files modified in last 2 hours (current session window)\n const sessionWindow = 2 * 60 * 60 * 1000; // 2 hours\n const now = Date.now();\n\n if (memoryDir && existsSync(memoryDir)) {\n const squadDirs = readdirSync(memoryDir, { withFileTypes: true })\n .filter(d => d.isDirectory());\n\n for (const squadDir of squadDirs) {\n const squadPath = join(memoryDir, squadDir.name);\n let squadModified = false;\n let stateContent = '';\n let executionContent = '';\n\n try {\n const agentDirs = readdirSync(squadPath, { withFileTypes: true })\n .filter(d => d.isDirectory());\n\n for (const agentDir of agentDirs) {\n const agentPath = join(squadPath, agentDir.name);\n const files = readdirSync(agentPath).filter(f => f.endsWith('.md'));\n\n for (const file of files) {\n const filePath = join(agentPath, file);\n const stats = statSync(filePath);\n\n if (now - stats.mtimeMs < sessionWindow) {\n squadModified = true;\n const relativePath = `${squadDir.name}/${agentDir.name}/${file}`;\n filesUpdated.push(relativePath);\n\n // Read content for summary\n if (file === 'state.md') {\n stateContent = readFileSync(filePath, 'utf-8');\n } else if (file === 'executions.md') {\n executionContent = readFileSync(filePath, 'utf-8');\n }\n }\n }\n }\n\n if (squadModified) {\n // Extract summary from state/execution content\n let actions = 'State updated';\n let outputs = 'Memory refreshed';\n\n // Try to extract info from execution log\n if (executionContent) {\n const lines = executionContent.split('\\n').filter(l => l.trim());\n const recentEntry = lines.slice(-10).join(' ');\n if (recentEntry.includes('completed')) {\n actions = 'Execution completed';\n }\n // Extract key points\n const keyMatch = recentEntry.match(/Key (?:findings|decisions|outputs)?:?\\s*([^.]+)/i);\n if (keyMatch) {\n outputs = keyMatch[1].substring(0, 50);\n }\n }\n\n // Try to extract from state header\n if (stateContent) {\n const updatedMatch = stateContent.match(/Updated:\\s*([^\\n]+)/);\n if (updatedMatch) {\n actions = `Updated ${updatedMatch[1]}`;\n }\n }\n\n squads.push({\n name: squadDir.name.charAt(0).toUpperCase() + squadDir.name.slice(1),\n actions,\n outputs: outputs.length > 44 ? outputs.substring(0, 41) + '...' : outputs,\n });\n }\n } catch {\n // Skip if can't read\n }\n }\n }\n\n // If no recent activity found\n if (squads.length === 0) {\n squads.push({\n name: 'No recent activity',\n actions: '—',\n outputs: 'Run squads to see activity here',\n });\n }\n\n return {\n squads,\n filesUpdated: filesUpdated.length > 0 ? filesUpdated : undefined,\n model: process.env.ANTHROPIC_MODEL || 'Claude',\n };\n}\n\n/**\n * Show session history and statistics\n */\nexport async function sessionsHistoryCommand(\n options: HistoryOptions = {}\n): Promise<void> {\n const days = options.days || 7;\n const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);\n\n const stats = await getSessionHistoryStats({\n since,\n squad: options.squad,\n });\n\n const recentSessions = await getRecentSessions(10);\n\n // JSON output\n if (options.json) {\n console.log(JSON.stringify({ stats, recentSessions }, null, 2));\n return;\n }\n\n writeLine();\n writeLine(` ${gradient('squads')} ${colors.dim}sessions history${RESET} ${colors.dim}(${days}d)${RESET}`);\n writeLine();\n\n if (stats.totalSessions === 0) {\n writeLine(` ${colors.dim}No session history found${RESET}`);\n writeLine();\n writeLine(` ${colors.dim}Session events are logged to .agents/sessions/history.jsonl${RESET}`);\n writeLine();\n return;\n }\n\n // Summary stats\n const avgMinutes = Math.round(stats.avgDurationMs / 60000);\n const totalHours = Math.round(stats.totalDurationMs / 3600000 * 10) / 10;\n\n writeLine(` ${bold}Summary${RESET}`);\n writeLine(` ${colors.cyan}${stats.totalSessions}${RESET} sessions ${colors.dim}│${RESET} ${colors.green}${totalHours}h${RESET} total ${colors.dim}│${RESET} ${colors.yellow}${avgMinutes}m${RESET} avg ${colors.dim}│${RESET} ${colors.purple}${stats.peakConcurrent}${RESET} peak`);\n writeLine();\n\n // By squad table\n const squads = Object.entries(stats.bySquad).sort((a, b) => b[1].count - a[1].count);\n\n if (squads.length > 0) {\n const w = { squad: 16, sessions: 10, duration: 12 };\n const tableWidth = w.squad + w.sessions + w.duration + 4;\n\n writeLine(` ${bold}By Squad${RESET}`);\n writeLine();\n writeLine(` ${colors.purple}${box.topLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.topRight}${RESET}`);\n\n const header = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${bold}${padEnd('SQUAD', w.squad)}${RESET}` +\n `${bold}${padEnd('SESSIONS', w.sessions)}${RESET}` +\n `${bold}DURATION${RESET}` +\n ` ${colors.purple}${box.vertical}${RESET}`;\n writeLine(header);\n\n writeLine(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);\n\n for (const [squad, data] of squads) {\n const hours = Math.round(data.durationMs / 3600000 * 10) / 10;\n\n const row = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${colors.cyan}${padEnd(squad, w.squad)}${RESET}` +\n `${padEnd(String(data.count), w.sessions)}` +\n `${padEnd(`${hours}h`, w.duration)}` +\n `${colors.purple}${box.vertical}${RESET}`;\n\n writeLine(row);\n }\n\n writeLine(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);\n }\n\n // Recent sessions\n if (recentSessions.length > 0) {\n writeLine();\n writeLine(` ${bold}Recent Sessions${RESET}`);\n writeLine();\n\n for (const event of recentSessions.slice(0, 5)) {\n const squad = event.squad || 'unknown';\n const date = new Date(event.ts);\n const timeStr = date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });\n const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\n\n writeLine(` ${colors.dim}${dateStr} ${timeStr}${RESET} ${colors.cyan}${squad}${RESET} ${colors.dim}${event.sessionId.slice(0, 8)}${RESET}`);\n }\n }\n\n // By date (last 7 days)\n const dates = Object.entries(stats.byDate)\n .sort((a, b) => b[0].localeCompare(a[0]))\n .slice(0, 7);\n\n if (dates.length > 1) {\n writeLine();\n writeLine(` ${bold}Daily Activity${RESET}`);\n writeLine();\n\n for (const [date, count] of dates) {\n const bar = '█'.repeat(Math.min(count, 20));\n writeLine(` ${colors.dim}${date}${RESET} ${colors.green}${bar}${RESET} ${count}`);\n }\n }\n\n writeLine();\n writeLine(` ${colors.dim}$${RESET} squads sessions history --days 30 ${colors.dim}Longer history${RESET}`);\n writeLine(` ${colors.dim}$${RESET} squads sessions history -s website ${colors.dim}Filter by squad${RESET}`);\n writeLine();\n}\n"],"mappings":"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","names":["existsSync","readdirSync","readFileSync","join"]}
|
|
@@ -152,9 +152,12 @@ function parseSquadFile(filePath) {
|
|
|
152
152
|
const { data: frontmatter, content: bodyContent } = matter(rawContent);
|
|
153
153
|
const fm = frontmatter;
|
|
154
154
|
const lines = bodyContent.split("\n");
|
|
155
|
+
const dirName = basename(dirname2(filePath));
|
|
155
156
|
const squad = {
|
|
156
|
-
//
|
|
157
|
-
name: fm.name ||
|
|
157
|
+
// Display name can be different from dir (e.g., "Engineering Squad")
|
|
158
|
+
name: fm.name || dirName,
|
|
159
|
+
// Directory name for file path resolution
|
|
160
|
+
dir: dirName,
|
|
158
161
|
mission: fm.mission || "",
|
|
159
162
|
agents: [],
|
|
160
163
|
pipelines: [],
|
|
@@ -377,23 +380,100 @@ function updateGoalInSquad(squadName, goalIndex, updates) {
|
|
|
377
380
|
}
|
|
378
381
|
return false;
|
|
379
382
|
}
|
|
380
|
-
function
|
|
383
|
+
function findProjectSkillsDir() {
|
|
381
384
|
const projectRoot = findProjectRoot();
|
|
382
385
|
if (!projectRoot) return null;
|
|
383
386
|
const skillsDir = join2(projectRoot, ".claude", "skills");
|
|
384
387
|
return existsSync2(skillsDir) ? skillsDir : null;
|
|
385
388
|
}
|
|
389
|
+
function findGlobalSkillsDir() {
|
|
390
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
391
|
+
if (!home) return null;
|
|
392
|
+
const skillsDir = join2(home, ".claude", "skills");
|
|
393
|
+
return existsSync2(skillsDir) ? skillsDir : null;
|
|
394
|
+
}
|
|
395
|
+
function findSquadLocalSkillsDir(squadDir) {
|
|
396
|
+
const squadsDir = findSquadsDir();
|
|
397
|
+
if (!squadsDir) return null;
|
|
398
|
+
const skillsDir = join2(squadsDir, squadDir, "skills");
|
|
399
|
+
return existsSync2(skillsDir) ? skillsDir : null;
|
|
400
|
+
}
|
|
401
|
+
function listSkillsInDir(skillsDir) {
|
|
402
|
+
if (!existsSync2(skillsDir)) return [];
|
|
403
|
+
try {
|
|
404
|
+
const entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
405
|
+
const skills = [];
|
|
406
|
+
for (const entry of entries) {
|
|
407
|
+
if (entry.isDirectory()) {
|
|
408
|
+
const skillMdPath = join2(skillsDir, entry.name, "SKILL.md");
|
|
409
|
+
if (existsSync2(skillMdPath)) {
|
|
410
|
+
skills.push(entry.name);
|
|
411
|
+
}
|
|
412
|
+
} else if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== "README.md") {
|
|
413
|
+
skills.push(entry.name.replace(".md", ""));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return skills;
|
|
417
|
+
} catch {
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
}
|
|
386
421
|
function findMemoryDir() {
|
|
387
422
|
const projectRoot = findProjectRoot();
|
|
388
423
|
if (!projectRoot) return null;
|
|
389
424
|
const memoryDir = join2(projectRoot, ".agents", "memory");
|
|
390
425
|
return existsSync2(memoryDir) ? memoryDir : null;
|
|
391
426
|
}
|
|
392
|
-
function
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
427
|
+
function resolveSkill(skillName, squadDir) {
|
|
428
|
+
if (squadDir) {
|
|
429
|
+
const squadSkillsDir = findSquadLocalSkillsDir(squadDir);
|
|
430
|
+
if (squadSkillsDir) {
|
|
431
|
+
const dirPath = join2(squadSkillsDir, skillName);
|
|
432
|
+
if (existsSync2(dirPath) && existsSync2(join2(dirPath, "SKILL.md"))) {
|
|
433
|
+
return { name: skillName, path: dirPath, source: "squad-local" };
|
|
434
|
+
}
|
|
435
|
+
const filePath = join2(squadSkillsDir, `${skillName}.md`);
|
|
436
|
+
if (existsSync2(filePath)) {
|
|
437
|
+
return { name: skillName, path: filePath, source: "squad-local" };
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const projectSkillsDir = findProjectSkillsDir();
|
|
442
|
+
if (projectSkillsDir) {
|
|
443
|
+
const dirPath = join2(projectSkillsDir, skillName);
|
|
444
|
+
if (existsSync2(dirPath) && existsSync2(join2(dirPath, "SKILL.md"))) {
|
|
445
|
+
return { name: skillName, path: dirPath, source: "project" };
|
|
446
|
+
}
|
|
447
|
+
const filePath = join2(projectSkillsDir, `${skillName}.md`);
|
|
448
|
+
if (existsSync2(filePath)) {
|
|
449
|
+
return { name: skillName, path: filePath, source: "project" };
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
const globalSkillsDir = findGlobalSkillsDir();
|
|
453
|
+
if (globalSkillsDir) {
|
|
454
|
+
const dirPath = join2(globalSkillsDir, skillName);
|
|
455
|
+
if (existsSync2(dirPath) && existsSync2(join2(dirPath, "SKILL.md"))) {
|
|
456
|
+
return { name: skillName, path: dirPath, source: "global" };
|
|
457
|
+
}
|
|
458
|
+
const filePath = join2(globalSkillsDir, `${skillName}.md`);
|
|
459
|
+
if (existsSync2(filePath)) {
|
|
460
|
+
return { name: skillName, path: filePath, source: "global" };
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
function getSquadLocalSkills(squadDir) {
|
|
466
|
+
const squadSkillsDir = findSquadLocalSkillsDir(squadDir);
|
|
467
|
+
if (!squadSkillsDir) return [];
|
|
468
|
+
const skillNames = listSkillsInDir(squadSkillsDir);
|
|
469
|
+
const skills = [];
|
|
470
|
+
for (const name of skillNames) {
|
|
471
|
+
const resolved = resolveSkill(name, squadDir);
|
|
472
|
+
if (resolved && resolved.source === "squad-local") {
|
|
473
|
+
skills.push(resolved);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return skills;
|
|
397
477
|
}
|
|
398
478
|
function resolveMemoryPaths(patterns) {
|
|
399
479
|
const memoryDir = findMemoryDir();
|
|
@@ -423,19 +503,53 @@ function resolveMemoryPaths(patterns) {
|
|
|
423
503
|
}
|
|
424
504
|
return resolved;
|
|
425
505
|
}
|
|
506
|
+
function findSquadLocalMcpConfig(squadDir) {
|
|
507
|
+
const squadsDir = findSquadsDir();
|
|
508
|
+
if (!squadsDir) return null;
|
|
509
|
+
const mcpConfigPath = join2(squadsDir, squadDir, "mcp.json");
|
|
510
|
+
return existsSync2(mcpConfigPath) ? mcpConfigPath : null;
|
|
511
|
+
}
|
|
426
512
|
function resolveExecutionContext(squad, forceRegenerate = false) {
|
|
427
513
|
const ctx = squad.context || {};
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
)
|
|
514
|
+
const squadLocalMcpConfig = findSquadLocalMcpConfig(squad.dir);
|
|
515
|
+
let mcpConfigPath;
|
|
516
|
+
let mcpSource;
|
|
517
|
+
let mcpServers = [];
|
|
518
|
+
if (squadLocalMcpConfig) {
|
|
519
|
+
mcpConfigPath = squadLocalMcpConfig;
|
|
520
|
+
mcpSource = "squad-local";
|
|
521
|
+
try {
|
|
522
|
+
const content = readFileSync2(squadLocalMcpConfig, "utf-8");
|
|
523
|
+
const config = JSON.parse(content);
|
|
524
|
+
mcpServers = Object.keys(config.mcpServers || {});
|
|
525
|
+
} catch {
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
const mcpResolution = resolveMcpConfig(
|
|
529
|
+
squad.name,
|
|
530
|
+
ctx.mcp,
|
|
531
|
+
forceRegenerate
|
|
532
|
+
);
|
|
533
|
+
mcpConfigPath = mcpResolution.path;
|
|
534
|
+
mcpSource = mcpResolution.source;
|
|
535
|
+
mcpServers = mcpResolution.servers || [];
|
|
536
|
+
}
|
|
537
|
+
const resolvedSkills = [];
|
|
433
538
|
const skillPaths = [];
|
|
539
|
+
const squadLocalSkills = getSquadLocalSkills(squad.dir);
|
|
540
|
+
for (const skill of squadLocalSkills) {
|
|
541
|
+
resolvedSkills.push(skill);
|
|
542
|
+
skillPaths.push(skill.path);
|
|
543
|
+
}
|
|
434
544
|
if (ctx.skills) {
|
|
435
|
-
for (const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
545
|
+
for (const skillName of ctx.skills) {
|
|
546
|
+
if (resolvedSkills.some((s) => s.name === skillName)) {
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
const resolved = resolveSkill(skillName, squad.dir);
|
|
550
|
+
if (resolved) {
|
|
551
|
+
resolvedSkills.push(resolved);
|
|
552
|
+
skillPaths.push(resolved.path);
|
|
439
553
|
}
|
|
440
554
|
}
|
|
441
555
|
}
|
|
@@ -447,10 +561,13 @@ function resolveExecutionContext(squad, forceRegenerate = false) {
|
|
|
447
561
|
squadName: squad.name,
|
|
448
562
|
// Add resolved paths
|
|
449
563
|
resolved: {
|
|
450
|
-
mcpConfigPath
|
|
451
|
-
mcpSource
|
|
452
|
-
mcpServers
|
|
564
|
+
mcpConfigPath,
|
|
565
|
+
mcpSource,
|
|
566
|
+
mcpServers,
|
|
453
567
|
skillPaths,
|
|
568
|
+
// Backward compatible
|
|
569
|
+
skills: resolvedSkills,
|
|
570
|
+
// New detailed skill info
|
|
454
571
|
memoryPaths
|
|
455
572
|
}
|
|
456
573
|
};
|
|
@@ -469,6 +586,7 @@ export {
|
|
|
469
586
|
parseAgentProvider,
|
|
470
587
|
addGoalToSquad,
|
|
471
588
|
updateGoalInSquad,
|
|
589
|
+
getSquadLocalSkills,
|
|
472
590
|
resolveExecutionContext
|
|
473
591
|
};
|
|
474
|
-
//# sourceMappingURL=chunk-
|
|
592
|
+
//# sourceMappingURL=chunk-O7UV3FWI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/squad-parser.ts","../src/lib/mcp-config.ts"],"sourcesContent":["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"],"mappings":"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","names":["readFileSync","existsSync","writeFileSync","join","dirname","config","join","existsSync","readFileSync","dirname","writeFileSync"]}
|