squads-cli 0.2.0 → 0.2.2
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 +521 -288
- package/dist/auth-YW3UPFSB.js +23 -0
- package/dist/auth-YW3UPFSB.js.map +1 -0
- package/dist/autonomy-GARI6J2J.js +105 -0
- package/dist/autonomy-GARI6J2J.js.map +1 -0
- package/dist/chunk-67RO2HKR.js +174 -0
- package/dist/chunk-67RO2HKR.js.map +1 -0
- package/dist/chunk-7OCVIDC7.js +12 -0
- package/dist/chunk-7OCVIDC7.js.map +1 -0
- package/dist/chunk-BODLDQY7.js +452 -0
- package/dist/chunk-BODLDQY7.js.map +1 -0
- package/dist/chunk-EHQJHRIW.js +103 -0
- package/dist/chunk-EHQJHRIW.js.map +1 -0
- package/dist/chunk-FFFCFZ6A.js +121 -0
- package/dist/chunk-FFFCFZ6A.js.map +1 -0
- package/dist/chunk-FIWT2NMM.js +165 -0
- package/dist/chunk-FIWT2NMM.js.map +1 -0
- package/dist/chunk-HF4WR7RA.js +154 -0
- package/dist/chunk-HF4WR7RA.js.map +1 -0
- package/dist/chunk-J6QF4ZQX.js +230 -0
- package/dist/chunk-J6QF4ZQX.js.map +1 -0
- package/dist/chunk-LOA3KWYJ.js +294 -0
- package/dist/chunk-LOA3KWYJ.js.map +1 -0
- package/dist/chunk-M5FXNY6Y.js +384 -0
- package/dist/chunk-M5FXNY6Y.js.map +1 -0
- package/dist/chunk-NP5BDPE6.js +240 -0
- package/dist/chunk-NP5BDPE6.js.map +1 -0
- package/dist/chunk-O632SBON.js +62 -0
- package/dist/chunk-O632SBON.js.map +1 -0
- package/dist/chunk-QJ7C7CMB.js +223 -0
- package/dist/chunk-QJ7C7CMB.js.map +1 -0
- package/dist/chunk-QRNR4GIT.js +88 -0
- package/dist/chunk-QRNR4GIT.js.map +1 -0
- package/dist/chunk-RM6BWILN.js +74 -0
- package/dist/chunk-RM6BWILN.js.map +1 -0
- package/dist/chunk-TYFTF53O.js +613 -0
- package/dist/chunk-TYFTF53O.js.map +1 -0
- package/dist/chunk-TZXD6WFN.js +420 -0
- package/dist/chunk-TZXD6WFN.js.map +1 -0
- package/dist/chunk-WVOIY5GW.js +621 -0
- package/dist/chunk-WVOIY5GW.js.map +1 -0
- package/dist/chunk-XTHZT53Y.js +364 -0
- package/dist/chunk-XTHZT53Y.js.map +1 -0
- package/dist/chunk-Z2UKDBNL.js +162 -0
- package/dist/chunk-Z2UKDBNL.js.map +1 -0
- package/dist/chunk-ZTQ7ISUR.js +338 -0
- package/dist/chunk-ZTQ7ISUR.js.map +1 -0
- package/dist/cli.js +3371 -5852
- package/dist/cli.js.map +1 -1
- package/dist/context-PYTO2UQG.js +291 -0
- package/dist/context-PYTO2UQG.js.map +1 -0
- package/dist/context-feed-TLVZZ24S.js +394 -0
- package/dist/context-feed-TLVZZ24S.js.map +1 -0
- package/dist/cost-OALPURUQ.js +275 -0
- package/dist/cost-OALPURUQ.js.map +1 -0
- package/dist/create-BLFGG6PF.js +286 -0
- package/dist/create-BLFGG6PF.js.map +1 -0
- package/dist/dashboard-HQIEHTZC.js +951 -0
- package/dist/dashboard-HQIEHTZC.js.map +1 -0
- package/dist/dashboard-RMK2BOD2.js +794 -0
- package/dist/dashboard-RMK2BOD2.js.map +1 -0
- package/dist/doctor-TWHMR23W.js +374 -0
- package/dist/doctor-TWHMR23W.js.map +1 -0
- package/dist/env-config-SQEI3Y7Y.js +21 -0
- package/dist/env-config-SQEI3Y7Y.js.map +1 -0
- package/dist/exec-DYLI4TXY.js +223 -0
- package/dist/exec-DYLI4TXY.js.map +1 -0
- package/dist/feedback-5AEACUX6.js +229 -0
- package/dist/feedback-5AEACUX6.js.map +1 -0
- package/dist/github-UQTM5KMS.js +23 -0
- package/dist/github-UQTM5KMS.js.map +1 -0
- package/dist/goal-XUNV3CKV.js +168 -0
- package/dist/goal-XUNV3CKV.js.map +1 -0
- package/dist/health-ZF3HSA4W.js +218 -0
- package/dist/health-ZF3HSA4W.js.map +1 -0
- package/dist/history-WP6R5BNG.js +232 -0
- package/dist/history-WP6R5BNG.js.map +1 -0
- package/dist/index.d.ts +736 -8
- package/dist/index.js +1312 -6
- package/dist/index.js.map +1 -1
- package/dist/init-BQSCG57S.js +921 -0
- package/dist/init-BQSCG57S.js.map +1 -0
- package/dist/kpi-VBGDO4GI.js +413 -0
- package/dist/kpi-VBGDO4GI.js.map +1 -0
- package/dist/learn-C4B2PQ5J.js +269 -0
- package/dist/learn-C4B2PQ5J.js.map +1 -0
- package/dist/login-F6ITE7PR.js +155 -0
- package/dist/login-F6ITE7PR.js.map +1 -0
- package/dist/memory-33HYD6AN.js +560 -0
- package/dist/memory-33HYD6AN.js.map +1 -0
- package/dist/memory-VNF2VFRB.js +23 -0
- package/dist/memory-VNF2VFRB.js.map +1 -0
- package/dist/observability-CL23L7LD.js +20 -0
- package/dist/observability-CL23L7LD.js.map +1 -0
- package/dist/org-cycle-Q74OT4I4.js +130 -0
- package/dist/org-cycle-Q74OT4I4.js.map +1 -0
- package/dist/progress-P2EIZBKP.js +202 -0
- package/dist/progress-P2EIZBKP.js.map +1 -0
- package/dist/providers-LE744DM6.js +65 -0
- package/dist/providers-LE744DM6.js.map +1 -0
- package/dist/repo-enforcement-JJQMKDAU.js +75 -0
- package/dist/repo-enforcement-JJQMKDAU.js.map +1 -0
- package/dist/results-6TH33HPN.js +224 -0
- package/dist/results-6TH33HPN.js.map +1 -0
- package/dist/run-DOY5SGF3.js +4074 -0
- package/dist/run-DOY5SGF3.js.map +1 -0
- package/dist/run-context-GB6GUCKZ.js +26 -0
- package/dist/run-context-GB6GUCKZ.js.map +1 -0
- package/dist/session-HBU6KZOD.js +64 -0
- package/dist/session-HBU6KZOD.js.map +1 -0
- package/dist/sessions-CK25VGPL.js +333 -0
- package/dist/sessions-CK25VGPL.js.map +1 -0
- package/dist/squad-parser-DCG65BJS.js +35 -0
- package/dist/squad-parser-DCG65BJS.js.map +1 -0
- package/dist/stats-G6NAU5BD.js +334 -0
- package/dist/stats-G6NAU5BD.js.map +1 -0
- package/dist/status-PFFB2NV6.js +352 -0
- package/dist/status-PFFB2NV6.js.map +1 -0
- package/dist/sync-FR6LQJ4C.js +836 -0
- package/dist/sync-FR6LQJ4C.js.map +1 -0
- package/dist/templates/core/AGENTS.md.template +51 -0
- package/dist/templates/core/BUSINESS_BRIEF.md.template +29 -0
- package/dist/templates/core/CLAUDE.md.template +48 -0
- package/dist/templates/core/provider.yaml.template +5 -0
- package/dist/templates/first-squad/SQUAD.md.template +23 -0
- package/dist/templates/first-squad/lead.md.template +44 -0
- package/dist/templates/memory/getting-started/state.md.template +19 -0
- package/dist/templates/seed/BUSINESS_BRIEF.md.template +27 -0
- package/dist/templates/seed/CLAUDE.md.template +119 -0
- package/dist/templates/seed/README.md.template +42 -0
- package/dist/templates/seed/config/SYSTEM.md +58 -0
- package/dist/templates/seed/config/provider.yaml +4 -0
- package/dist/templates/seed/hooks/settings.json.template +31 -0
- package/dist/templates/seed/idp/catalog/service.yaml.template +25 -0
- package/dist/templates/seed/memory/_squad/goals.md +23 -0
- package/dist/templates/seed/memory/_squad/priorities.md +25 -0
- package/dist/templates/seed/memory/company/company.md +31 -0
- package/dist/templates/seed/memory/company/directives.md +37 -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/product/lead/state.md +14 -0
- package/dist/templates/seed/memory/research/lead/state.md +14 -0
- package/dist/templates/seed/skills/gh/SKILL.md +57 -0
- package/dist/templates/seed/skills/squads-cli/SKILL.md +329 -0
- package/dist/templates/seed/skills/squads-cli/references/commands.md +181 -0
- package/dist/templates/seed/squads/company/SQUAD.md +51 -0
- package/dist/templates/seed/squads/company/company-critic.md +57 -0
- package/dist/templates/seed/squads/company/company-eval.md +57 -0
- package/dist/templates/seed/squads/company/event-dispatcher.md +53 -0
- package/dist/templates/seed/squads/company/goal-tracker.md +51 -0
- package/dist/templates/seed/squads/company/manager.md +60 -0
- package/dist/templates/seed/squads/engineering/SQUAD.md +48 -0
- package/dist/templates/seed/squads/engineering/code-reviewer.md +69 -0
- package/dist/templates/seed/squads/engineering/issue-solver.md +66 -0
- package/dist/templates/seed/squads/engineering/test-writer.md +60 -0
- package/dist/templates/seed/squads/intelligence/SQUAD.md +38 -0
- package/dist/templates/seed/squads/intelligence/intel-critic.md +53 -0
- package/dist/templates/seed/squads/intelligence/intel-eval.md +48 -0
- package/dist/templates/seed/squads/intelligence/intel-lead.md +79 -0
- package/dist/templates/seed/squads/marketing/SQUAD.md +47 -0
- package/dist/templates/seed/squads/marketing/content-drafter.md +81 -0
- package/dist/templates/seed/squads/marketing/growth-analyst.md +61 -0
- package/dist/templates/seed/squads/marketing/social-poster.md +56 -0
- package/dist/templates/seed/squads/operations/SQUAD.md +45 -0
- package/dist/templates/seed/squads/operations/finance-tracker.md +55 -0
- package/dist/templates/seed/squads/operations/goal-tracker.md +60 -0
- package/dist/templates/seed/squads/operations/ops-lead.md +68 -0
- package/dist/templates/seed/squads/product/SQUAD.md +41 -0
- package/dist/templates/seed/squads/product/lead.md +64 -0
- package/dist/templates/seed/squads/product/scanner.md +58 -0
- package/dist/templates/seed/squads/product/worker.md +63 -0
- package/dist/templates/seed/squads/research/SQUAD.md +38 -0
- package/dist/templates/seed/squads/research/analyst.md +58 -0
- package/dist/templates/seed/squads/research/lead.md +58 -0
- package/dist/templates/seed/squads/research/synthesizer.md +67 -0
- package/dist/templates/skills/squads-learn/SKILL.md +86 -0
- package/dist/templates/skills/squads-workflow/instruction.md +70 -0
- package/dist/terminal-FBQFQTKZ.js +55 -0
- package/dist/terminal-FBQFQTKZ.js.map +1 -0
- package/dist/tier-detect-YX2HPNNR.js +15 -0
- package/dist/tier-detect-YX2HPNNR.js.map +1 -0
- package/dist/update-D7CGIZ3M.js +18 -0
- package/dist/update-D7CGIZ3M.js.map +1 -0
- package/dist/update-STU276HR.js +83 -0
- package/dist/update-STU276HR.js.map +1 -0
- package/package.json +31 -13
- package/templates/core/AGENTS.md.template +51 -0
- package/templates/core/BUSINESS_BRIEF.md.template +29 -0
- package/templates/core/CLAUDE.md.template +48 -0
- package/templates/core/provider.yaml.template +5 -0
- package/templates/first-squad/SQUAD.md.template +23 -0
- package/templates/first-squad/lead.md.template +44 -0
- package/templates/memory/getting-started/state.md.template +19 -0
- package/templates/seed/BUSINESS_BRIEF.md.template +27 -0
- package/templates/seed/CLAUDE.md.template +119 -0
- package/templates/seed/README.md.template +42 -0
- package/templates/seed/config/SYSTEM.md +58 -0
- package/templates/seed/config/provider.yaml +4 -0
- package/templates/seed/hooks/settings.json.template +31 -0
- package/templates/seed/idp/catalog/service.yaml.template +25 -0
- package/templates/seed/memory/_squad/goals.md +23 -0
- package/templates/seed/memory/_squad/priorities.md +25 -0
- package/templates/seed/memory/company/company.md +31 -0
- package/templates/seed/memory/company/directives.md +37 -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/product/lead/state.md +14 -0
- package/templates/seed/memory/research/lead/state.md +14 -0
- package/templates/seed/skills/gh/SKILL.md +57 -0
- package/templates/seed/skills/squads-cli/SKILL.md +329 -0
- package/templates/seed/skills/squads-cli/references/commands.md +181 -0
- package/templates/seed/squads/company/SQUAD.md +51 -0
- package/templates/seed/squads/company/company-critic.md +57 -0
- package/templates/seed/squads/company/company-eval.md +57 -0
- package/templates/seed/squads/company/event-dispatcher.md +53 -0
- package/templates/seed/squads/company/goal-tracker.md +51 -0
- package/templates/seed/squads/company/manager.md +60 -0
- package/templates/seed/squads/engineering/SQUAD.md +48 -0
- package/templates/seed/squads/engineering/code-reviewer.md +69 -0
- package/templates/seed/squads/engineering/issue-solver.md +66 -0
- package/templates/seed/squads/engineering/test-writer.md +60 -0
- package/templates/seed/squads/intelligence/SQUAD.md +38 -0
- package/templates/seed/squads/intelligence/intel-critic.md +53 -0
- package/templates/seed/squads/intelligence/intel-eval.md +48 -0
- package/templates/seed/squads/intelligence/intel-lead.md +79 -0
- package/templates/seed/squads/marketing/SQUAD.md +47 -0
- package/templates/seed/squads/marketing/content-drafter.md +81 -0
- package/templates/seed/squads/marketing/growth-analyst.md +61 -0
- package/templates/seed/squads/marketing/social-poster.md +56 -0
- package/templates/seed/squads/operations/SQUAD.md +45 -0
- package/templates/seed/squads/operations/finance-tracker.md +55 -0
- package/templates/seed/squads/operations/goal-tracker.md +60 -0
- package/templates/seed/squads/operations/ops-lead.md +68 -0
- package/templates/seed/squads/product/SQUAD.md +41 -0
- package/templates/seed/squads/product/lead.md +64 -0
- package/templates/seed/squads/product/scanner.md +58 -0
- package/templates/seed/squads/product/worker.md +63 -0
- package/templates/seed/squads/research/SQUAD.md +38 -0
- package/templates/seed/squads/research/analyst.md +58 -0
- package/templates/seed/squads/research/lead.md +58 -0
- package/templates/seed/squads/research/synthesizer.md +67 -0
- package/templates/skills/squads-learn/SKILL.md +86 -0
- package/templates/skills/squads-workflow/instruction.md +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/plan.ts","../src/lib/costs.ts"],"sourcesContent":["/**\n * Anthropic plan detection\n * Determines whether the user is on a Max (flat-fee) or Usage (pay-per-token) plan.\n */\n\n/**\n * Anthropic plan types:\n * - 'max': Flat fee subscription ($200/mo), no overage - only rate limits matter\n * - 'usage': Pay-per-token, budget tracking matters\n * - 'unknown': Not configured yet\n */\nexport type PlanType = 'max' | 'usage' | 'unknown';\n\n/**\n * Plan detection result with confidence and reason\n */\nexport interface PlanDetection {\n plan: PlanType;\n confidence: 'explicit' | 'inferred';\n reason: string;\n}\n\n/**\n * Detect the Anthropic plan type using multiple signals:\n *\n * Priority order:\n * 1. Explicit SQUADS_PLAN_TYPE env var (highest confidence)\n * 2. ANTHROPIC_BUDGET_DAILY set → usage plan (user cares about budget)\n * 3. Tier 4 + no budget → likely Max plan (heavy user)\n * 4. Low tier (1-2) → usage plan (new user, pay-as-you-go)\n * 5. Default: max (assumes professional use)\n */\nexport function detectPlan(): PlanDetection {\n // 1. Explicit configuration (highest priority)\n const explicitPlan = process.env.SQUADS_PLAN_TYPE?.toLowerCase();\n if (explicitPlan === 'usage') {\n return { plan: 'usage', confidence: 'explicit', reason: 'SQUADS_PLAN_TYPE=usage' };\n }\n if (explicitPlan === 'max') {\n return { plan: 'max', confidence: 'explicit', reason: 'SQUADS_PLAN_TYPE=max' };\n }\n\n // 2. Budget explicitly set → user cares about costs → usage plan\n const budgetSet = process.env.ANTHROPIC_BUDGET_DAILY || process.env.SQUADS_DAILY_BUDGET;\n if (budgetSet) {\n return { plan: 'usage', confidence: 'inferred', reason: `Budget set ($${budgetSet}/day)` };\n }\n\n // 3. Check tier - Tier 4 usually indicates Max plan user\n const tier = parseInt(process.env.ANTHROPIC_TIER || '0', 10);\n if (tier >= 4) {\n return { plan: 'max', confidence: 'inferred', reason: `Tier ${tier} (high usage)` };\n }\n\n // 4. Low tier (1-2) → likely new user on usage plan\n if (tier >= 1 && tier <= 2) {\n return { plan: 'usage', confidence: 'inferred', reason: `Tier ${tier} (new user)` };\n }\n\n // 5. No API key = OAuth (Claude Code subscription) → treat as Max plan\n // Users authenticated via OAuth have a flat subscription and don't need cost tracking.\n if (!process.env.ANTHROPIC_API_KEY) {\n return { plan: 'max', confidence: 'inferred', reason: 'OAuth (Claude Code subscription)' };\n }\n\n // 6. API key set but no other signals → usage plan (pay-per-token)\n return { plan: 'usage', confidence: 'inferred', reason: 'API key (pay-per-token)' };\n}\n\n/**\n * Get the current Anthropic plan type\n * Use detectPlan() for full details including confidence\n */\nexport function getPlanType(): PlanType {\n return detectPlan().plan;\n}\n\n/**\n * Check if we're on a flat-fee plan where budget doesn't matter\n */\nexport function isMaxPlan(): boolean {\n return getPlanType() === 'max';\n}\n\n/**\n * Get human-readable plan description for dashboard display\n */\nexport function getPlanDescription(): string {\n const detection = detectPlan();\n const planName = detection.plan === 'max'\n ? 'Max ($200 flat)'\n : detection.plan === 'usage'\n ? 'Usage (pay-per-token)'\n : 'Unknown';\n const confidence = detection.confidence === 'explicit' ? '' : ` [${detection.reason}]`;\n return `${planName}${confidence}`;\n}\n","/**\n * Cost tracking via API or Langfuse\n * Primary: Squads API → PostgreSQL\n * Fallback: Langfuse API (if API unavailable)\n */\n\nimport {\n ProviderName,\n ProviderDetection,\n detectProviderFromModel,\n detectProvidersFromEnv,\n calcCost as calcProviderCost,\n getProviderDisplayName,\n} from './providers.js';\nimport { getEnv } from './env-config.js';\n\n// Re-export provider types for convenience\nexport { ProviderName, ProviderDetection, detectProviderFromModel, detectProvidersFromEnv, getProviderDisplayName };\n\n// Re-export plan detection from dedicated module\nexport { PlanType, PlanDetection, detectPlan, getPlanType, isMaxPlan, getPlanDescription } from './plan.js';\n\ninterface SquadCosts {\n squad: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n cachedTokens: number;\n cost: number;\n models: Record<string, number>;\n}\n\nexport interface ProviderCosts {\n provider: ProviderName;\n displayName: string;\n calls: number;\n inputTokens: number;\n outputTokens: number;\n cost: number;\n plan?: string;\n confidence?: 'explicit' | 'inferred';\n reason?: string;\n}\n\nexport interface CostSummary {\n totalCost: number;\n dailyBudget: number;\n usedPercent: number;\n idleBudget: number;\n totalCalls: number;\n dailyCallLimit: number;\n callsPercent: number;\n totalCachedTokens: number;\n totalInputTokens: number;\n cacheHitRate: number;\n bySquad: SquadCosts[];\n byProvider: ProviderCosts[];\n source: 'postgres' | 'langfuse' | 'none';\n}\n\n// Legacy MODEL_PRICING kept for backward compatibility\n// New code should use getModelPricing() from providers.ts\nconst _MODEL_PRICING: Record<string, { input: number; output: number }> = {\n 'claude-opus-4-5-20251101': { input: 15.0, output: 75.0 },\n 'claude-sonnet-4-20250514': { input: 3.0, output: 15.0 },\n 'claude-haiku-4-5-20251001': { input: 0.80, output: 4.0 },\n 'claude-3-5-sonnet-20241022': { input: 3.0, output: 15.0 },\n 'claude-3-5-haiku-20241022': { input: 0.80, output: 4.0 },\n default: { input: 3.0, output: 15.0 },\n};\n\nconst DEFAULT_DAILY_BUDGET = 200.0;\nconst DEFAULT_DAILY_CALL_LIMIT = 1000; // Default API call limit per day\nconst BRIDGE_URL = getEnv().bridge_url;\nconst FETCH_TIMEOUT_MS = 2000; // 2 second timeout for all fetch calls\n\n/**\n * Fetch with timeout to prevent hanging when services are down\n */\nasync function fetchWithTimeout(url: string, options: RequestInit = {}, timeoutMs = FETCH_TIMEOUT_MS): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, { ...options, signal: controller.signal });\n clearTimeout(timeoutId);\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n}\n\nfunction calcCost(model: string, inputTokens: number, outputTokens: number): number {\n // Use provider-aware pricing\n const provider = detectProviderFromModel(model);\n return calcProviderCost(provider, model, inputTokens, outputTokens);\n}\n\n/**\n * Fetch cost summary from Squads Bridge (postgres)\n */\nasync function fetchFromBridge(period: 'day' | 'week' | 'month' = 'day'): Promise<CostSummary | null> {\n try {\n const response = await fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=${period}`, {\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json() as { totals?: { cost_usd?: number }; by_squad?: Record<string, unknown>[] };\n const dailyBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || '') || DEFAULT_DAILY_BUDGET;\n const totalCost = data.totals?.cost_usd || 0;\n\n const bySquad: SquadCosts[] = (data.by_squad || []).map((s: Record<string, unknown>) => ({\n squad: s.squad as string,\n calls: (s.generations as number) || 0,\n inputTokens: (s.input_tokens as number) || 0,\n outputTokens: (s.output_tokens as number) || 0,\n cachedTokens: (s.cached_tokens as number) || 0,\n cost: (s.cost_usd as number) || 0,\n models: {},\n }));\n\n const totalCalls = bySquad.reduce((sum, s) => sum + s.calls, 0);\n const dailyCallLimit = parseFloat(process.env.SQUADS_DAILY_CALL_LIMIT || '') || DEFAULT_DAILY_CALL_LIMIT;\n const totalCachedTokens = bySquad.reduce((sum, s) => sum + s.cachedTokens, 0);\n const totalInputTokens = bySquad.reduce((sum, s) => sum + s.inputTokens, 0);\n const totalAllInput = totalInputTokens + totalCachedTokens;\n const cacheHitRate = totalAllInput > 0 ? (totalCachedTokens / totalAllInput) * 100 : 0;\n\n // Build provider summary from detected providers in env\n const detectedProviders = detectProvidersFromEnv();\n const byProvider: ProviderCosts[] = detectedProviders.map((p) => ({\n provider: p.provider,\n displayName: getProviderDisplayName(p.provider),\n calls: 0, // Bridge doesn't track by provider yet\n inputTokens: 0,\n outputTokens: 0,\n cost: p.provider === 'anthropic' ? totalCost : 0, // Assume all cost is Anthropic for now\n plan: p.plan,\n confidence: p.confidence,\n reason: p.reason,\n }));\n\n return {\n totalCost,\n dailyBudget,\n usedPercent: (totalCost / dailyBudget) * 100,\n idleBudget: dailyBudget - totalCost,\n totalCalls,\n dailyCallLimit,\n callsPercent: (totalCalls / dailyCallLimit) * 100,\n totalCachedTokens,\n totalInputTokens,\n cacheHitRate,\n bySquad,\n byProvider,\n source: 'postgres',\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Fetch cost summary from Langfuse API (fallback)\n */\nasync function fetchFromLangfuse(limit = 100): Promise<CostSummary | null> {\n const publicKey = process.env.LANGFUSE_PUBLIC_KEY;\n const secretKey = process.env.LANGFUSE_SECRET_KEY;\n const host = process.env.LANGFUSE_HOST || process.env.LANGFUSE_BASE_URL || 'https://us.cloud.langfuse.com';\n\n if (!publicKey || !secretKey) {\n return null;\n }\n\n try {\n const auth = Buffer.from(`${publicKey}:${secretKey}`).toString('base64');\n const url = `${host}/api/public/observations?limit=${limit}`;\n\n const response = await fetchWithTimeout(url, {\n headers: {\n Authorization: `Basic ${auth}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n return null;\n }\n\n interface LangfuseObs {\n type?: string;\n model?: string;\n metadata?: { squad?: string };\n usage?: { input?: number; output?: number };\n }\n const data = await response.json() as { data?: LangfuseObs[] };\n const observations = data.data || [];\n\n // Group by squad\n const bySquad: Record<string, SquadCosts> = {};\n\n for (const obs of observations) {\n if (obs.type !== 'GENERATION') continue;\n\n const metadata = obs.metadata || {};\n const squad = metadata.squad || 'unknown';\n const model = obs.model || 'unknown';\n const usage = obs.usage || {};\n\n const inputTokens = usage.input || 0;\n const outputTokens = usage.output || 0;\n const cost = calcCost(model, inputTokens, outputTokens);\n\n if (!bySquad[squad]) {\n bySquad[squad] = {\n squad,\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cachedTokens: 0,\n cost: 0,\n models: {},\n };\n }\n\n bySquad[squad].calls += 1;\n bySquad[squad].inputTokens += inputTokens;\n bySquad[squad].outputTokens += outputTokens;\n bySquad[squad].cost += cost;\n bySquad[squad].models[model] = (bySquad[squad].models[model] || 0) + 1;\n }\n\n const squadList = Object.values(bySquad).sort((a, b) => b.cost - a.cost);\n const totalCost = squadList.reduce((sum, s) => sum + s.cost, 0);\n const dailyBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || '') || DEFAULT_DAILY_BUDGET;\n\n const totalCalls = squadList.reduce((sum, s) => sum + s.calls, 0);\n const dailyCallLimit = parseFloat(process.env.SQUADS_DAILY_CALL_LIMIT || '') || DEFAULT_DAILY_CALL_LIMIT;\n const totalCachedTokens = squadList.reduce((sum, s) => sum + s.cachedTokens, 0);\n const totalInputTokens = squadList.reduce((sum, s) => sum + s.inputTokens, 0);\n const totalAllInput = totalInputTokens + totalCachedTokens;\n const cacheHitRate = totalAllInput > 0 ? (totalCachedTokens / totalAllInput) * 100 : 0;\n\n // Build provider summary - Langfuse can track by model, so group by provider\n const providerMap: Record<string, ProviderCosts> = {};\n for (const obs of observations) {\n if (obs.type !== 'GENERATION') continue;\n const model = obs.model || 'unknown';\n const provider = detectProviderFromModel(model);\n const usage = obs.usage || {};\n const inputTokens = usage.input || 0;\n const outputTokens = usage.output || 0;\n const cost = calcCost(model, inputTokens, outputTokens);\n\n if (!providerMap[provider]) {\n const detection = detectProvidersFromEnv().find((p) => p.provider === provider);\n providerMap[provider] = {\n provider,\n displayName: getProviderDisplayName(provider),\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cost: 0,\n plan: detection?.plan,\n confidence: detection?.confidence,\n reason: detection?.reason,\n };\n }\n providerMap[provider].calls += 1;\n providerMap[provider].inputTokens += inputTokens;\n providerMap[provider].outputTokens += outputTokens;\n providerMap[provider].cost += cost;\n }\n const byProvider = Object.values(providerMap).sort((a, b) => b.cost - a.cost);\n\n return {\n totalCost,\n dailyBudget,\n usedPercent: (totalCost / dailyBudget) * 100,\n idleBudget: dailyBudget - totalCost,\n totalCalls,\n dailyCallLimit,\n callsPercent: (totalCalls / dailyCallLimit) * 100,\n totalCachedTokens,\n totalInputTokens,\n cacheHitRate,\n bySquad: squadList,\n byProvider,\n source: 'langfuse',\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Fetch cost summary - tries postgres first, falls back to Langfuse\n */\nexport async function fetchCostSummary(\n limit = 100,\n period: 'day' | 'week' | 'month' = 'day'\n): Promise<CostSummary | null> {\n // Try postgres (via bridge) first\n const bridgeResult = await fetchFromBridge(period);\n if (bridgeResult) {\n return bridgeResult;\n }\n\n // Fall back to Langfuse\n const langfuseResult = await fetchFromLangfuse(limit);\n if (langfuseResult) {\n return langfuseResult;\n }\n\n // No data source available - still detect providers from env\n const defaultBudget = parseFloat(process.env.SQUADS_DAILY_BUDGET || '') || DEFAULT_DAILY_BUDGET;\n const detectedProviders = detectProvidersFromEnv();\n const byProvider: ProviderCosts[] = detectedProviders.map((p) => ({\n provider: p.provider,\n displayName: getProviderDisplayName(p.provider),\n calls: 0,\n inputTokens: 0,\n outputTokens: 0,\n cost: 0,\n plan: p.plan,\n confidence: p.confidence,\n reason: p.reason,\n }));\n\n return {\n totalCost: 0,\n dailyBudget: defaultBudget,\n usedPercent: 0,\n idleBudget: defaultBudget,\n totalCalls: 0,\n dailyCallLimit: DEFAULT_DAILY_CALL_LIMIT,\n callsPercent: 0,\n totalCachedTokens: 0,\n totalInputTokens: 0,\n cacheHitRate: 0,\n bySquad: [],\n byProvider,\n source: 'none',\n };\n}\n\nexport function formatCostBar(usedPercent: number, width = 20): string {\n const filled = Math.min(Math.round((usedPercent / 100) * width), width);\n const empty = width - filled;\n return '█'.repeat(filled) + '░'.repeat(empty);\n}\n\n/**\n * Bridge stats from /stats endpoint (Redis real-time or Postgres fallback)\n */\nexport interface BridgeStats {\n status: string;\n source: 'redis' | 'postgres' | 'none';\n today: {\n generations: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n };\n week?: {\n generations: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n byModel?: Array<{\n model: string;\n generations: number;\n costUsd: number;\n }>;\n };\n budget: {\n daily: number;\n used: number;\n remaining: number;\n usedPct: number;\n };\n bySquad: Array<{\n squad: string;\n costUsd: number;\n generations: number;\n }>;\n byModel?: Array<{\n model: string;\n generations: number;\n costUsd: number;\n }>;\n health: {\n postgres: string;\n redis: string;\n langfuse: string;\n };\n}\n\n/**\n * Fetch real-time stats from Squads Bridge\n * All HTTP calls are made in parallel for optimal performance\n */\nexport async function fetchBridgeStats(): Promise<BridgeStats | null> {\n try {\n interface StatsData {\n status?: string;\n source?: string;\n today?: {\n generations?: number;\n input_tokens?: number;\n output_tokens?: number;\n cost_usd?: number;\n };\n budget?: {\n daily?: number;\n used?: number;\n remaining?: number;\n used_pct?: number;\n };\n by_squad?: Array<{\n squad?: string;\n cost_usd?: number;\n generations?: number;\n }>;\n }\n\n interface HealthData {\n postgres?: string;\n redis?: string;\n langfuse?: string;\n }\n\n interface CostData {\n totals?: {\n generations?: number;\n input_tokens?: number;\n output_tokens?: number;\n cost_usd?: number;\n };\n by_model?: Array<{\n model?: string;\n generations?: number;\n cost_usd?: number;\n }>;\n }\n\n // Fetch ALL endpoints in parallel (4 requests -> 1 round trip)\n const [statsResponse, healthResponse, costResponse, weekResponse] = await Promise.all([\n fetchWithTimeout(`${BRIDGE_URL}/stats`, {\n headers: { 'Content-Type': 'application/json' },\n }),\n fetchWithTimeout(`${BRIDGE_URL}/health`, {\n headers: { 'Content-Type': 'application/json' },\n }),\n fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=day`, {\n headers: { 'Content-Type': 'application/json' },\n }),\n fetchWithTimeout(`${BRIDGE_URL}/api/cost/summary?period=week`, {\n headers: { 'Content-Type': 'application/json' },\n }),\n ]);\n\n if (!statsResponse.ok) {\n return null;\n }\n\n // Parse all responses in parallel\n const [stats, health, costData, weekData] = await Promise.all([\n statsResponse.json() as Promise<StatsData>,\n healthResponse.ok ? healthResponse.json() as Promise<HealthData> : Promise.resolve({} as HealthData),\n costResponse.ok ? costResponse.json() as Promise<CostData> : Promise.resolve({} as CostData),\n weekResponse.ok ? weekResponse.json() as Promise<CostData> : Promise.resolve({} as CostData),\n ]);\n\n return {\n status: stats.status || 'unknown',\n source: (stats.source as 'redis' | 'postgres' | 'none') || 'none',\n today: {\n generations: stats.today?.generations || 0,\n inputTokens: stats.today?.input_tokens || 0,\n outputTokens: stats.today?.output_tokens || 0,\n costUsd: stats.today?.cost_usd || 0,\n },\n week: weekData.totals ? {\n generations: weekData.totals.generations || 0,\n inputTokens: weekData.totals.input_tokens || 0,\n outputTokens: weekData.totals.output_tokens || 0,\n costUsd: weekData.totals.cost_usd || 0,\n byModel: (weekData.by_model || []).map(m => ({\n model: m.model || 'unknown',\n generations: m.generations || 0,\n costUsd: m.cost_usd || 0,\n })),\n } : undefined,\n budget: {\n daily: stats.budget?.daily || DEFAULT_DAILY_BUDGET,\n used: stats.budget?.used || 0,\n remaining: stats.budget?.remaining || DEFAULT_DAILY_BUDGET,\n usedPct: stats.budget?.used_pct || 0,\n },\n bySquad: (stats.by_squad || []).map(s => ({\n squad: s.squad || 'unknown',\n costUsd: s.cost_usd || 0,\n generations: s.generations || 0,\n })),\n byModel: (costData.by_model || []).map(m => ({\n model: m.model || 'unknown',\n generations: m.generations || 0,\n costUsd: m.cost_usd || 0,\n })),\n health: {\n postgres: health.postgres || 'unknown',\n redis: health.redis || 'unknown',\n langfuse: health.langfuse || 'unknown',\n },\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Monthly quota/autonomy data from bridge\n */\nexport interface QuotaInfo {\n monthlyUsed: number;\n monthlyQuota: number;\n quotaPct: number;\n autonomyScore: number;\n confidenceLevel: string;\n learningCount: number;\n}\n\nexport async function fetchQuotaInfo(): Promise<QuotaInfo | null> {\n try {\n const response = await fetchWithTimeout(`${BRIDGE_URL}/api/autonomy/score`);\n if (!response.ok) return null;\n\n const data = await response.json() as {\n overall_score: number;\n confidence_level: string;\n execution_stats: {\n monthly_used: number;\n monthly_quota: number;\n quota_pct: number;\n learning_count: number;\n };\n };\n\n return {\n monthlyUsed: data.execution_stats.monthly_used,\n monthlyQuota: data.execution_stats.monthly_quota,\n quotaPct: data.execution_stats.quota_pct,\n autonomyScore: data.overall_score,\n confidenceLevel: data.confidence_level,\n learningCount: data.execution_stats.learning_count,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Rate limit data from Anthropic API headers\n */\nexport interface RateLimitInfo {\n model: string;\n requestsLimit: number;\n requestsRemaining: number;\n requestsReset?: string;\n tokensLimit: number;\n tokensRemaining: number;\n tokensReset?: string;\n inputTokensLimit?: number;\n inputTokensRemaining?: number;\n outputTokensLimit?: number;\n outputTokensRemaining?: number;\n capturedAt: string;\n}\n\nexport interface RateLimits {\n limits: Record<string, RateLimitInfo>;\n source: 'proxy' | 'none';\n}\n\n/**\n * Fetch real rate limits from the Anthropic proxy (via bridge)\n */\nexport async function fetchRateLimits(): Promise<RateLimits> {\n try {\n const response = await fetchWithTimeout(`${BRIDGE_URL}/api/rate-limits`, {\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n return { limits: {}, source: 'none' };\n }\n\n interface RateLimitResponse {\n rate_limits?: Record<string, {\n model?: string;\n requests_limit?: number;\n requests_remaining?: number;\n requests_reset?: string;\n tokens_limit?: number;\n tokens_remaining?: number;\n tokens_reset?: string;\n input_tokens_limit?: number;\n input_tokens_remaining?: number;\n output_tokens_limit?: number;\n output_tokens_remaining?: number;\n captured_at?: string;\n }>;\n }\n\n const data = await response.json() as RateLimitResponse;\n const rateLimits = data.rate_limits || {};\n\n const limits: Record<string, RateLimitInfo> = {};\n for (const [key, value] of Object.entries(rateLimits)) {\n limits[key] = {\n model: value.model || key,\n requestsLimit: value.requests_limit || 0,\n requestsRemaining: value.requests_remaining || 0,\n requestsReset: value.requests_reset,\n tokensLimit: value.tokens_limit || 0,\n tokensRemaining: value.tokens_remaining || 0,\n tokensReset: value.tokens_reset,\n inputTokensLimit: value.input_tokens_limit,\n inputTokensRemaining: value.input_tokens_remaining,\n outputTokensLimit: value.output_tokens_limit,\n outputTokensRemaining: value.output_tokens_remaining,\n capturedAt: value.captured_at || new Date().toISOString(),\n };\n }\n\n return { limits, source: 'proxy' };\n } catch {\n return { limits: {}, source: 'none' };\n }\n}\n\n/**\n * Task and quality insights\n */\nexport interface TaskMetrics {\n squad: string;\n tasksTotal: number;\n tasksCompleted: number;\n tasksFailed: number;\n successRate: number;\n totalRetries: number;\n tasksWithRetries: number;\n avgRetries: number;\n avgDurationMs: number;\n avgTokens: number;\n avgCost: number;\n avgContextPct: number;\n maxContextTokens: number;\n}\n\nexport interface ToolMetrics {\n toolName: string;\n usageCount: number;\n successRate: number;\n avgDurationMs: number;\n}\n\nexport interface QualityMetrics {\n squad: string;\n feedbackCount: number;\n avgQuality: number;\n helpfulPct: number;\n fixRequiredPct: number;\n}\n\nexport interface Insights {\n period: string;\n days: number;\n taskMetrics: TaskMetrics[];\n qualityMetrics: QualityMetrics[];\n topTools: ToolMetrics[];\n toolFailureRate: number;\n source: 'bridge' | 'none';\n}\n\n/**\n * Fetch insights from the bridge\n */\nexport async function fetchInsights(period: 'day' | 'week' | 'month' = 'week'): Promise<Insights> {\n try {\n const response = await fetchWithTimeout(`${BRIDGE_URL}/api/insights?period=${period}`, {\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n return {\n period,\n days: period === 'day' ? 1 : period === 'week' ? 7 : 30,\n taskMetrics: [],\n qualityMetrics: [],\n topTools: [],\n toolFailureRate: 0,\n source: 'none',\n };\n }\n\n interface InsightsResponse {\n period: string;\n days: number;\n task_metrics?: Array<{\n squad: string;\n tasks_total: number;\n tasks_completed: number;\n tasks_failed: number;\n success_rate: number;\n total_retries: number;\n tasks_with_retries: number;\n avg_retries: number;\n avg_duration_ms: number;\n avg_tokens: number;\n avg_cost: number;\n avg_context_pct: number;\n max_context_tokens: number;\n }>;\n quality_metrics?: Array<{\n squad: string;\n feedback_count: number;\n avg_quality: number;\n helpful_pct: number;\n fix_required_pct: number;\n }>;\n top_tools?: Array<{\n tool_name: string;\n usage_count: number;\n success_rate: number;\n avg_duration_ms: number;\n }>;\n tool_failure_rate?: number;\n }\n\n const data = await response.json() as InsightsResponse;\n\n return {\n period: data.period || period,\n days: data.days || 7,\n taskMetrics: (data.task_metrics || []).map(t => ({\n squad: t.squad,\n tasksTotal: t.tasks_total || 0,\n tasksCompleted: t.tasks_completed || 0,\n tasksFailed: t.tasks_failed || 0,\n successRate: t.success_rate || 0,\n totalRetries: t.total_retries || 0,\n tasksWithRetries: t.tasks_with_retries || 0,\n avgRetries: t.avg_retries || 0,\n avgDurationMs: t.avg_duration_ms || 0,\n avgTokens: t.avg_tokens || 0,\n avgCost: t.avg_cost || 0,\n avgContextPct: t.avg_context_pct || 0,\n maxContextTokens: t.max_context_tokens || 0,\n })),\n qualityMetrics: (data.quality_metrics || []).map(q => ({\n squad: q.squad,\n feedbackCount: q.feedback_count || 0,\n avgQuality: q.avg_quality || 0,\n helpfulPct: q.helpful_pct || 0,\n fixRequiredPct: q.fix_required_pct || 0,\n })),\n topTools: (data.top_tools || []).map(t => ({\n toolName: t.tool_name,\n usageCount: t.usage_count || 0,\n successRate: t.success_rate || 0,\n avgDurationMs: t.avg_duration_ms || 0,\n })),\n toolFailureRate: data.tool_failure_rate || 0,\n source: 'bridge',\n };\n } catch {\n return {\n period,\n days: period === 'day' ? 1 : period === 'week' ? 7 : 30,\n taskMetrics: [],\n qualityMetrics: [],\n topTools: [],\n toolFailureRate: 0,\n source: 'none',\n };\n }\n}\n\n// === NPM Stats for Acquisition Tracking ===\n\nexport interface NpmStats {\n package: string;\n downloads: {\n lastDay: number;\n lastWeek: number;\n lastMonth: number;\n };\n weekOverWeek: number; // percentage change\n}\n\nexport async function fetchNpmStats(packageName: string = process.env.SQUADS_NPM_PACKAGE || 'squads-cli'): Promise<NpmStats | null> {\n try {\n const [dayRes, weekRes, monthRes] = await Promise.all([\n fetchWithTimeout(`https://api.npmjs.org/downloads/point/last-day/${packageName}`, {}, 3000),\n fetchWithTimeout(`https://api.npmjs.org/downloads/point/last-week/${packageName}`, {}, 3000),\n fetchWithTimeout(`https://api.npmjs.org/downloads/point/last-month/${packageName}`, {}, 3000),\n ]);\n\n if (!dayRes.ok || !weekRes.ok || !monthRes.ok) return null;\n\n const [dayData, weekData, monthData] = await Promise.all([\n dayRes.json() as Promise<{ downloads: number }>,\n weekRes.json() as Promise<{ downloads: number }>,\n monthRes.json() as Promise<{ downloads: number }>,\n ]);\n\n // Calculate week-over-week growth (rough estimate: this week vs avg of month)\n const avgWeeklyFromMonth = monthData.downloads / 4;\n const weekOverWeek = avgWeeklyFromMonth > 0\n ? Math.round(((weekData.downloads - avgWeeklyFromMonth) / avgWeeklyFromMonth) * 100)\n : 0;\n\n return {\n package: packageName,\n downloads: {\n lastDay: dayData.downloads,\n lastWeek: weekData.downloads,\n lastMonth: monthData.downloads,\n },\n weekOverWeek,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Claude Code subscription capacity data\n * Read from ~/.claude/stats-cache.json\n */\nexport interface ClaudeCodeCapacity {\n // Weekly capacity (from stats-cache.json token data)\n weeklyTokensUsed: number;\n weeklyTokensLimit: number;\n weeklyCapacityPct: number;\n // Session capacity (estimated from current session)\n sessionTokensUsed: number;\n sessionTokensLimit: number;\n sessionCapacityPct: number;\n // By model breakdown\n opusTokensUsed: number;\n sonnetTokensUsed: number;\n haikuTokensUsed: number;\n // Reset info\n weeklyResetDate: string;\n sessionResetTime: string;\n}\n\n// Claude Code subscription capacity estimation\n// The actual limits use \"usage units\" not raw tokens\n// Opus costs ~5x more usage units than Sonnet/Haiku\n// Based on user reports: Max 20x gives ~24-40 hours Opus/week\n// Conservative estimate: ~4M \"weighted tokens\" per week for Max 20x\nconst OPUS_WEIGHT = 5; // Opus costs ~5x more against limit\nconst SONNET_WEIGHT = 1; // Sonnet is the baseline\nconst HAIKU_WEIGHT = 0.25; // Haiku is cheaper\n\n// Effective weekly limit in weighted tokens (conservative for Max 20x)\n// Adjust via SQUADS_WEEKLY_LIMIT env var if your plan differs\nconst DEFAULT_WEEKLY_WEIGHTED_LIMIT = 4_000_000;\nconst MAX5_SESSION_TOKEN_LIMIT = 2_000_000; // ~2M tokens/session (raw)\n\n/**\n * Fetch Claude Code capacity from stats-cache.json\n */\nexport async function fetchClaudeCodeCapacity(): Promise<ClaudeCodeCapacity | null> {\n const { readFile } = await import('fs/promises');\n const { homedir } = await import('os');\n const { join } = await import('path');\n\n try {\n const cacheFile = join(homedir(), '.claude', 'stats-cache.json');\n const content = await readFile(cacheFile, 'utf-8');\n const data = JSON.parse(content) as {\n dailyModelTokens?: Array<{\n date: string;\n tokensByModel: Record<string, number>;\n }>;\n modelUsage?: Record<string, {\n inputTokens?: number;\n outputTokens?: number;\n cacheReadInputTokens?: number;\n cacheCreationInputTokens?: number;\n }>;\n };\n\n // Calculate weekly token usage (last 7 days)\n const now = new Date();\n const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\n const weekStart = weekAgo.toISOString().split('T')[0];\n\n let weeklyOpus = 0;\n\n if (data.dailyModelTokens) {\n for (const day of data.dailyModelTokens) {\n if (day.date >= weekStart) {\n for (const [model, tokens] of Object.entries(day.tokensByModel)) {\n if (model.includes('opus')) {\n weeklyOpus += tokens;\n }\n }\n }\n }\n }\n\n // Calculate weighted usage (Opus costs 5x more against limit)\n // This better reflects actual subscription capacity consumption\n const weeklyLimit = parseInt(process.env.SQUADS_WEEKLY_LIMIT || '', 10) || DEFAULT_WEEKLY_WEIGHTED_LIMIT;\n\n // Calculate session usage (today's tokens as proxy)\n const today = now.toISOString().split('T')[0];\n let sessionTokens = 0;\n if (data.dailyModelTokens) {\n const todayData = data.dailyModelTokens.find(d => d.date === today);\n if (todayData) {\n sessionTokens = Object.values(todayData.tokensByModel).reduce((a, b) => a + b, 0);\n }\n }\n\n // Calculate weekly reset (next Sunday 9:59pm in local timezone)\n const daysUntilSunday = (7 - now.getDay()) % 7 || 7;\n const nextSunday = new Date(now);\n nextSunday.setDate(now.getDate() + daysUntilSunday);\n nextSunday.setHours(21, 59, 0, 0);\n\n // Session reset (end of current session - approximate as end of day)\n const sessionReset = new Date(now);\n sessionReset.setHours(18, 59, 0, 0);\n if (sessionReset < now) {\n sessionReset.setDate(sessionReset.getDate() + 1);\n }\n\n // Calculate Sonnet/Haiku usage\n let sonnetTokens = 0;\n let haikuTokens = 0;\n if (data.dailyModelTokens) {\n for (const day of data.dailyModelTokens) {\n if (day.date >= weekStart) {\n for (const [model, tokens] of Object.entries(day.tokensByModel)) {\n if (model.includes('sonnet')) {\n sonnetTokens += tokens;\n } else if (model.includes('haiku')) {\n haikuTokens += tokens;\n }\n }\n }\n }\n }\n\n // Calculate weighted weekly usage\n const weeklyWeighted = Math.round(\n (weeklyOpus * OPUS_WEIGHT) +\n (sonnetTokens * SONNET_WEIGHT) +\n (haikuTokens * HAIKU_WEIGHT)\n );\n\n return {\n weeklyTokensUsed: weeklyWeighted, // Now weighted, not raw\n weeklyTokensLimit: weeklyLimit,\n weeklyCapacityPct: Math.round((weeklyWeighted / weeklyLimit) * 100),\n sessionTokensUsed: sessionTokens,\n sessionTokensLimit: MAX5_SESSION_TOKEN_LIMIT,\n sessionCapacityPct: Math.round((sessionTokens / MAX5_SESSION_TOKEN_LIMIT) * 100),\n opusTokensUsed: weeklyOpus,\n sonnetTokensUsed: sonnetTokens,\n haikuTokensUsed: haikuTokens,\n weeklyResetDate: nextSunday.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),\n sessionResetTime: sessionReset.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }),\n };\n } catch {\n return null;\n }\n}\n\n// === ROI and Cost Projection Types ===\n\n/**\n * ROI metrics for measuring value delivered vs cost\n */\nexport interface ROIMetrics {\n totalCostUsd: number;\n costPerGoal: number;\n costPerCommit: number;\n costPerPR: number;\n estimatedValueUsd: number;\n roiMultiplier: number;\n dailyProjectedCost: number;\n weeklyProjectedCost: number;\n monthlyProjectedCost: number;\n hoursTracked: number;\n costPerHour: number;\n}\n\n/**\n * Before/after comparison for a period\n */\nexport interface BeforeAfterMetrics {\n periodStart: string;\n periodEnd: string;\n baselineCostUsd: number;\n baselineGoals: number;\n baselineCommits: number;\n baselinePRs: number;\n baselineTokens: number;\n currentCostUsd: number;\n currentGoals: number;\n currentCommits: number;\n currentPRs: number;\n currentTokens: number;\n costDelta: number;\n goalsDelta: number;\n commitsDelta: number;\n prsDelta: number;\n tokensDelta: number;\n costPerGoalBefore: number;\n costPerGoalAfter: number;\n efficiencyImprovement: number;\n}\n\n/**\n * Squad-level cost projection\n */\nexport interface SquadCostProjection {\n squad: string;\n currentDailyCost: number;\n projectedDailyCost: number;\n projectedWeeklyCost: number;\n projectedMonthlyCost: number;\n costTrend: 'increasing' | 'stable' | 'decreasing';\n trendPct: number;\n}\n\n/**\n * Calculate ROI metrics from cost and activity data\n */\nexport function calculateROIMetrics(\n costs: CostSummary | null,\n goalsCompleted: number,\n commits: number,\n prsMerged: number,\n hoursTracked: number = 0\n): ROIMetrics {\n const totalCost = costs?.totalCost || 0;\n const costPerGoal = goalsCompleted > 0 ? totalCost / goalsCompleted : 0;\n const costPerCommit = commits > 0 ? totalCost / commits : 0;\n const costPerPR = prsMerged > 0 ? totalCost / prsMerged : 0;\n\n const GOAL_VALUE = parseFloat(process.env.SQUADS_GOAL_VALUE || '100');\n const PR_VALUE = parseFloat(process.env.SQUADS_PR_VALUE || '200');\n const COMMIT_VALUE = parseFloat(process.env.SQUADS_COMMIT_VALUE || '25');\n\n const estimatedValue = (goalsCompleted * GOAL_VALUE) + (prsMerged * PR_VALUE) + (commits * COMMIT_VALUE);\n const roiMultiplier = totalCost > 0 ? estimatedValue / totalCost : 0;\n\n const now = new Date();\n const hoursElapsed = hoursTracked > 0 ? hoursTracked : Math.max(now.getHours() + now.getMinutes() / 60, 1);\n const costPerHour = totalCost / hoursElapsed;\n\n return {\n totalCostUsd: totalCost,\n costPerGoal,\n costPerCommit,\n costPerPR,\n estimatedValueUsd: estimatedValue,\n roiMultiplier,\n dailyProjectedCost: costPerHour * 24,\n weeklyProjectedCost: costPerHour * 24 * 7,\n monthlyProjectedCost: costPerHour * 24 * 30,\n hoursTracked: hoursElapsed,\n costPerHour,\n };\n}\n\n/**\n * Calculate cost projections per squad\n */\nexport function calculateSquadCostProjections(\n bridgeStats: BridgeStats | null,\n _history: Array<{ squad: string; costUsd: number }[]> | null\n): SquadCostProjection[] {\n if (!bridgeStats || bridgeStats.bySquad.length === 0) {\n return [];\n }\n\n const now = new Date();\n const hoursElapsed = Math.max(now.getHours() + now.getMinutes() / 60, 1);\n\n return bridgeStats.bySquad.map(squad => {\n const hourlyRate = squad.costUsd / hoursElapsed;\n return {\n squad: squad.squad,\n currentDailyCost: squad.costUsd,\n projectedDailyCost: hourlyRate * 24,\n projectedWeeklyCost: hourlyRate * 24 * 7,\n projectedMonthlyCost: hourlyRate * 24 * 30,\n costTrend: 'stable' as const,\n trendPct: 0,\n };\n });\n}\n"],"mappings":"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","names":["calcCost"]}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
RESET,
|
|
4
|
+
colors,
|
|
5
|
+
writeLine
|
|
6
|
+
} from "./chunk-M5FXNY6Y.js";
|
|
7
|
+
import {
|
|
8
|
+
findMemoryDir
|
|
9
|
+
} from "./chunk-ZTQ7ISUR.js";
|
|
10
|
+
import {
|
|
11
|
+
findSquadsDir
|
|
12
|
+
} from "./chunk-TYFTF53O.js";
|
|
13
|
+
|
|
14
|
+
// src/lib/run-context.ts
|
|
15
|
+
import { join, dirname } from "path";
|
|
16
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
17
|
+
import { execSync } from "child_process";
|
|
18
|
+
var ROLE_BUDGETS = {
|
|
19
|
+
scanner: 4e3,
|
|
20
|
+
// ~1000 tokens — company + priorities + goals + agent + state
|
|
21
|
+
worker: 12e3,
|
|
22
|
+
// ~3000 tokens — + feedback
|
|
23
|
+
lead: 24e3,
|
|
24
|
+
// ~6000 tokens — all layers
|
|
25
|
+
coo: 32e3,
|
|
26
|
+
// ~8000 tokens — all layers + expanded
|
|
27
|
+
verifier: 12e3
|
|
28
|
+
// similar needs to worker
|
|
29
|
+
};
|
|
30
|
+
var ROLE_SECTIONS = {
|
|
31
|
+
scanner: /* @__PURE__ */ new Set([1, 2, 3, 4, 5]),
|
|
32
|
+
// identity + focus + role + memory
|
|
33
|
+
worker: /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 6]),
|
|
34
|
+
// + feedback
|
|
35
|
+
lead: /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 6, 7, 8]),
|
|
36
|
+
// + daily briefing + cross-squad
|
|
37
|
+
coo: /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 6, 7, 8]),
|
|
38
|
+
// all layers + expanded budget
|
|
39
|
+
verifier: /* @__PURE__ */ new Set([1, 2, 3, 4, 5, 6])
|
|
40
|
+
// same as worker
|
|
41
|
+
};
|
|
42
|
+
function parseAgentFrontmatter(agentPath) {
|
|
43
|
+
if (!agentPath || !existsSync(agentPath)) return {};
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = readFileSync(agentPath, "utf-8");
|
|
47
|
+
} catch {
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
if (!content) return {};
|
|
51
|
+
const lines = content.split("\n");
|
|
52
|
+
let inFrontmatter = false;
|
|
53
|
+
const yamlLines = [];
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
if (line.trim() === "---") {
|
|
56
|
+
if (inFrontmatter) break;
|
|
57
|
+
inFrontmatter = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (inFrontmatter) {
|
|
61
|
+
yamlLines.push(line);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (yamlLines.length === 0) return {};
|
|
65
|
+
const yaml = yamlLines.join("\n");
|
|
66
|
+
const result = {};
|
|
67
|
+
const contextMatch = yaml.match(/context_from:\s*\[([^\]]+)\]/);
|
|
68
|
+
if (contextMatch) {
|
|
69
|
+
result.context_from = contextMatch[1].split(",").map((s) => s.trim());
|
|
70
|
+
}
|
|
71
|
+
const criteriaMatch = yaml.match(/acceptance_criteria:\s*\|\n((?:\s+.+\n?)*)/);
|
|
72
|
+
if (criteriaMatch) {
|
|
73
|
+
result.acceptance_criteria = criteriaMatch[1].replace(/^ {2}/gm, "").trim();
|
|
74
|
+
}
|
|
75
|
+
const retriesMatch = yaml.match(/max_retries:\s*(\d+)/);
|
|
76
|
+
if (retriesMatch) {
|
|
77
|
+
result.max_retries = parseInt(retriesMatch[1], 10);
|
|
78
|
+
}
|
|
79
|
+
const cooldownMatch = yaml.match(/cooldown:\s*["']?([^"'\n]+)["']?/);
|
|
80
|
+
if (cooldownMatch) {
|
|
81
|
+
result.cooldown = cooldownMatch[1].trim();
|
|
82
|
+
}
|
|
83
|
+
for (const line of yamlLines) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (!trimmed.startsWith("role:")) continue;
|
|
86
|
+
let value = trimmed.slice("role:".length).trim();
|
|
87
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
88
|
+
value = value.slice(1, -1).trim();
|
|
89
|
+
}
|
|
90
|
+
if (value) {
|
|
91
|
+
result.agent_role = value;
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
function extractMcpServersFromDefinition(definition) {
|
|
98
|
+
const servers = /* @__PURE__ */ new Set();
|
|
99
|
+
const knownServers = [
|
|
100
|
+
"chrome-devtools",
|
|
101
|
+
"firecrawl",
|
|
102
|
+
"context7",
|
|
103
|
+
"huggingface"
|
|
104
|
+
];
|
|
105
|
+
for (const server of knownServers) {
|
|
106
|
+
if (definition.toLowerCase().includes(server)) {
|
|
107
|
+
servers.add(server);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const mcpMatch = definition.match(/mcp:\s*\n((?:\s*-\s*\S+\s*\n?)+)/i);
|
|
111
|
+
if (mcpMatch) {
|
|
112
|
+
const lines = mcpMatch[1].split("\n");
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
const serverMatch = line.match(/^\s*-\s*(\S+)/);
|
|
115
|
+
if (serverMatch) {
|
|
116
|
+
servers.add(serverMatch[1]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return Array.from(servers);
|
|
121
|
+
}
|
|
122
|
+
function readAgentsFile(relativePath, warnLabel) {
|
|
123
|
+
const squadsDir = findSquadsDir();
|
|
124
|
+
if (!squadsDir) return "";
|
|
125
|
+
const filePath = join(dirname(squadsDir), relativePath);
|
|
126
|
+
if (!existsSync(filePath)) return "";
|
|
127
|
+
try {
|
|
128
|
+
return readFileSync(filePath, "utf-8").trim();
|
|
129
|
+
} catch (e) {
|
|
130
|
+
writeLine(` ${colors.dim}warn: failed reading ${warnLabel}: ${e instanceof Error ? e.message : String(e)}${RESET}`);
|
|
131
|
+
return "";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function loadSystemProtocol() {
|
|
135
|
+
const systemMd = readAgentsFile("SYSTEM.md", "SYSTEM.md");
|
|
136
|
+
if (systemMd) return systemMd;
|
|
137
|
+
const legacyMd = readAgentsFile("config/SYSTEM.md", "SYSTEM.md (legacy)");
|
|
138
|
+
if (legacyMd) return legacyMd;
|
|
139
|
+
return loadApprovalInstructions();
|
|
140
|
+
}
|
|
141
|
+
function loadCompanyContext() {
|
|
142
|
+
const companyMd = readAgentsFile("company.md", "company.md");
|
|
143
|
+
if (companyMd) return companyMd;
|
|
144
|
+
const memoryDir = findMemoryDir();
|
|
145
|
+
if (memoryDir) {
|
|
146
|
+
const directivesFile = join(memoryDir, "company", "directives.md");
|
|
147
|
+
const content = safeRead(directivesFile);
|
|
148
|
+
if (content) return content;
|
|
149
|
+
}
|
|
150
|
+
return "";
|
|
151
|
+
}
|
|
152
|
+
function loadApprovalInstructions() {
|
|
153
|
+
return readAgentsFile("config/approval-instructions.md", "approval instructions");
|
|
154
|
+
}
|
|
155
|
+
function loadPostExecution(squadName, agentName) {
|
|
156
|
+
const template = readAgentsFile("config/post-execution.md", "post-execution template");
|
|
157
|
+
if (template) {
|
|
158
|
+
return template.replace(/\{\{squadName\}\}/g, squadName).replace(/\{\{agentName\}\}/g, agentName);
|
|
159
|
+
}
|
|
160
|
+
return "";
|
|
161
|
+
}
|
|
162
|
+
function safeRead(path) {
|
|
163
|
+
try {
|
|
164
|
+
return existsSync(path) ? readFileSync(path, "utf-8").trim() : "";
|
|
165
|
+
} catch {
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function stripYamlFrontmatter(markdown) {
|
|
170
|
+
const lines = markdown.split("\n");
|
|
171
|
+
let dashCount = 0;
|
|
172
|
+
let endIdx = -1;
|
|
173
|
+
for (let i = 0; i < lines.length; i++) {
|
|
174
|
+
if (lines[i].trim() === "---") {
|
|
175
|
+
dashCount++;
|
|
176
|
+
if (dashCount === 2) {
|
|
177
|
+
endIdx = i;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (endIdx >= 0) return lines.slice(endIdx + 1).join("\n").trim();
|
|
183
|
+
return markdown.trim();
|
|
184
|
+
}
|
|
185
|
+
function scoreByTokens(text, tokens) {
|
|
186
|
+
const lower = text.toLowerCase();
|
|
187
|
+
let score = 0;
|
|
188
|
+
for (const t of tokens) {
|
|
189
|
+
if (!t) continue;
|
|
190
|
+
if (lower.includes(t)) score += 1;
|
|
191
|
+
}
|
|
192
|
+
return score;
|
|
193
|
+
}
|
|
194
|
+
function resolveContextRoleFromAgent(agentPath, agentName) {
|
|
195
|
+
const fm = parseAgentFrontmatter(agentPath);
|
|
196
|
+
const roleText = fm.agent_role || "";
|
|
197
|
+
const normalized = roleText.trim().toLowerCase();
|
|
198
|
+
const directRoles = ["scanner", "worker", "lead", "verifier"];
|
|
199
|
+
for (const r of directRoles) {
|
|
200
|
+
if (normalized === r) return r;
|
|
201
|
+
}
|
|
202
|
+
if (normalized === "coo") return "coo";
|
|
203
|
+
const scannerTokens = ["scan", "monitor", "detect", "find", "opportun", "scout", "gap", "bottleneck"];
|
|
204
|
+
const workerTokens = ["execute", "implement", "write", "create", "build", "prototype", "file", "issue", "worker"];
|
|
205
|
+
const leadTokens = ["lead", "orchestrate", "own", "strategy", "roadmap", "coordinate", "triage", "review", "mvp"];
|
|
206
|
+
const verifierTokens = ["verify", "validation", "compliance", "audit", "approve", "reject", "check", "test", "critic", "verifier"];
|
|
207
|
+
const scored = [
|
|
208
|
+
["scanner", scoreByTokens(normalized, scannerTokens)],
|
|
209
|
+
["worker", scoreByTokens(normalized, workerTokens)],
|
|
210
|
+
["lead", scoreByTokens(normalized, leadTokens)],
|
|
211
|
+
["verifier", scoreByTokens(normalized, verifierTokens)]
|
|
212
|
+
];
|
|
213
|
+
scored.sort((a, b) => b[1] - a[1]);
|
|
214
|
+
const best = scored[0];
|
|
215
|
+
const second = scored[1];
|
|
216
|
+
const clean = best[1] > 0 && (!second || second[1] === 0);
|
|
217
|
+
if (clean) return best[0];
|
|
218
|
+
const llmEnabled = process.env.SQUADS_CONTEXT_ROLE_LLM === "1";
|
|
219
|
+
if (!llmEnabled) return "worker";
|
|
220
|
+
try {
|
|
221
|
+
const raw = safeRead(agentPath);
|
|
222
|
+
const body = stripYamlFrontmatter(raw);
|
|
223
|
+
const excerpt = body.slice(0, 1600);
|
|
224
|
+
const prompt = [
|
|
225
|
+
"Classify the agent into exactly ONE Agents Squads context role.",
|
|
226
|
+
"Return EXACTLY one token from: scanner, worker, lead, verifier.",
|
|
227
|
+
"",
|
|
228
|
+
`Agent name: ${agentName}`,
|
|
229
|
+
`Agent frontmatter role: ${roleText || "(missing)"}`,
|
|
230
|
+
"",
|
|
231
|
+
"Agent definition excerpt:",
|
|
232
|
+
excerpt
|
|
233
|
+
].join("\n");
|
|
234
|
+
const escapedPrompt = prompt.replace(/'/g, "'\\''");
|
|
235
|
+
const model = process.env.SQUADS_CONTEXT_ROLE_LLM_MODEL || "claude-haiku-4-5";
|
|
236
|
+
const out = execSync(
|
|
237
|
+
`claude --print --dangerously-skip-permissions --disable-slash-commands --model ${model} -- '${escapedPrompt}'`,
|
|
238
|
+
{ encoding: "utf-8", timeout: 6e4, maxBuffer: 2 * 1024 * 1024 }
|
|
239
|
+
).trim().toLowerCase();
|
|
240
|
+
const tokens = ["scanner", "worker", "lead", "verifier"];
|
|
241
|
+
for (const t of tokens) {
|
|
242
|
+
if (out === t || out.includes(t)) return t;
|
|
243
|
+
}
|
|
244
|
+
return "worker";
|
|
245
|
+
} catch {
|
|
246
|
+
return "worker";
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function gatherSquadContext(squadName, agentName, options = {}) {
|
|
250
|
+
const squadsDir = findSquadsDir();
|
|
251
|
+
if (!squadsDir) return "";
|
|
252
|
+
const memoryDir = findMemoryDir();
|
|
253
|
+
const role = options.role || "worker";
|
|
254
|
+
const budget = options.maxTokens ? options.maxTokens * 4 : ROLE_BUDGETS[role] ?? ROLE_BUDGETS.worker;
|
|
255
|
+
const allowedSections = ROLE_SECTIONS[role] ?? ROLE_SECTIONS.worker;
|
|
256
|
+
const sections = [];
|
|
257
|
+
let usedChars = 0;
|
|
258
|
+
function addLayer(layerNum, header, content, maxChars) {
|
|
259
|
+
if (!allowedSections.has(layerNum)) return false;
|
|
260
|
+
if (!content) return false;
|
|
261
|
+
let text = content;
|
|
262
|
+
const remaining = Math.max(0, budget - usedChars);
|
|
263
|
+
const cap = maxChars !== void 0 ? Math.min(maxChars, remaining) : remaining;
|
|
264
|
+
if (text.length > cap) {
|
|
265
|
+
text = text.substring(0, cap) + "\n...";
|
|
266
|
+
}
|
|
267
|
+
if (usedChars + text.length > budget) {
|
|
268
|
+
if (options.verbose) {
|
|
269
|
+
writeLine(` ${colors.dim}Context budget exhausted at layer ${layerNum} (${header})${RESET}`);
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
sections.push(`## ${header}
|
|
274
|
+
${text}`);
|
|
275
|
+
usedChars += text.length;
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
const companyContext = loadCompanyContext();
|
|
279
|
+
if (companyContext) {
|
|
280
|
+
addLayer(1, "Company", stripYamlFrontmatter(companyContext));
|
|
281
|
+
}
|
|
282
|
+
if (memoryDir) {
|
|
283
|
+
const prioritiesFile = join(memoryDir, squadName, "priorities.md");
|
|
284
|
+
const content = safeRead(prioritiesFile);
|
|
285
|
+
if (content) {
|
|
286
|
+
addLayer(2, "Priorities", stripYamlFrontmatter(content));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (memoryDir) {
|
|
290
|
+
const goalsFile = join(memoryDir, squadName, "goals.md");
|
|
291
|
+
const content = safeRead(goalsFile);
|
|
292
|
+
if (content) {
|
|
293
|
+
addLayer(3, "Goals", stripYamlFrontmatter(content));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (options.agentPath) {
|
|
297
|
+
const agentContent = safeRead(options.agentPath);
|
|
298
|
+
if (agentContent) {
|
|
299
|
+
const body = stripYamlFrontmatter(agentContent);
|
|
300
|
+
addLayer(4, `Agent: ${agentName}`, body);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (memoryDir) {
|
|
304
|
+
const stateFile = join(memoryDir, squadName, agentName, "state.md");
|
|
305
|
+
const content = safeRead(stateFile);
|
|
306
|
+
if (content) {
|
|
307
|
+
const body = stripYamlFrontmatter(content);
|
|
308
|
+
const stateCap = role === "scanner" || role === "verifier" ? 2e3 : void 0;
|
|
309
|
+
addLayer(5, "Previous State", body, stateCap);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (memoryDir) {
|
|
313
|
+
const feedbackFile = join(memoryDir, squadName, "feedback.md");
|
|
314
|
+
const content = safeRead(feedbackFile);
|
|
315
|
+
if (content) {
|
|
316
|
+
addLayer(6, "Feedback", content);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (memoryDir) {
|
|
320
|
+
const dailyFile = join(memoryDir, "daily-briefing.md");
|
|
321
|
+
const content = safeRead(dailyFile);
|
|
322
|
+
if (content) {
|
|
323
|
+
addLayer(7, "Daily Briefing", content);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (memoryDir) {
|
|
327
|
+
const frontmatter = options.agentPath ? parseAgentFrontmatter(options.agentPath) : {};
|
|
328
|
+
const contextSquads = frontmatter.context_from || [];
|
|
329
|
+
const learningParts = [];
|
|
330
|
+
for (const ctx of contextSquads) {
|
|
331
|
+
const learningsFile = join(memoryDir, ctx, "shared", "learnings.md");
|
|
332
|
+
const content = safeRead(learningsFile);
|
|
333
|
+
if (content) {
|
|
334
|
+
learningParts.push(`### ${ctx}
|
|
335
|
+
${content}`);
|
|
336
|
+
} else if (options.verbose) {
|
|
337
|
+
writeLine(` ${colors.dim}context_from: no learnings found for squad '${ctx}'${RESET}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (learningParts.length > 0) {
|
|
341
|
+
addLayer(8, "Cross-Squad Learnings", learningParts.join("\n\n"));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (sections.length === 0) return "";
|
|
345
|
+
if (options.verbose) {
|
|
346
|
+
writeLine(` ${colors.dim}Context: ${sections.length} layers, ~${Math.ceil(usedChars / 4)} tokens (${role} role, budget: ~${Math.ceil(budget / 4)})${RESET}`);
|
|
347
|
+
}
|
|
348
|
+
return `
|
|
349
|
+
# CONTEXT
|
|
350
|
+
${sections.join("\n\n")}
|
|
351
|
+
`;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export {
|
|
355
|
+
parseAgentFrontmatter,
|
|
356
|
+
extractMcpServersFromDefinition,
|
|
357
|
+
loadSystemProtocol,
|
|
358
|
+
loadCompanyContext,
|
|
359
|
+
loadApprovalInstructions,
|
|
360
|
+
loadPostExecution,
|
|
361
|
+
resolveContextRoleFromAgent,
|
|
362
|
+
gatherSquadContext
|
|
363
|
+
};
|
|
364
|
+
//# sourceMappingURL=chunk-XTHZT53Y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/run-context.ts"],"sourcesContent":["/**\n * run-context.ts\n *\n * Squad Context System — context assembly for agent execution.\n *\n * Layers flow from general to particular (no overrides, each answers a different question):\n * L0: SYSTEM.md — How (system, tools, principles — immutable, outside budget)\n * L1: company.md — Why (company identity, alignment)\n * L2: priorities.md — Where (current focus, urgency)\n * L3: goals.md — What (measurable targets)\n * L4: agent.md — You (agent role, specific instructions)\n * L5: state.md — Memory (continuity from last run)\n * L6+: Supporting — feedback, daily-briefing, cross-squad learnings\n *\n * SQUAD.md is metadata only (repo, agents, config) — NOT injected into prompt.\n * Each layer adds a unique dimension. No layer contradicts another.\n * Role determines which layers are included and the total token budget.\n */\n\nimport { join, dirname } from 'path';\nimport { existsSync, readFileSync, readdirSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { findSquadsDir } from './squad-parser.js';\nimport { findMemoryDir } from './memory.js';\nimport { colors, RESET, writeLine } from './terminal.js';\n\n// ── Types ────────────────────────────────────────────────────────────\n\nexport type ContextRole = 'scanner' | 'worker' | 'lead' | 'coo' | 'verifier';\n\n// ── Token Budgets (chars, ~4 chars/token) ────────────────────────────\n\nconst ROLE_BUDGETS: Record<ContextRole, number> = {\n scanner: 4000, // ~1000 tokens — company + priorities + goals + agent + state\n worker: 12000, // ~3000 tokens — + feedback\n lead: 24000, // ~6000 tokens — all layers\n coo: 32000, // ~8000 tokens — all layers + expanded\n verifier: 12000, // similar needs to worker\n};\n\n/**\n * Which layers each role gets access to.\n * Numbers correspond to layer order in the Squad Context System:\n * 1=company, 2=priorities, 3=goals, 4=agent, 5=state, 6=feedback, 7=daily-briefing, 8=cross-squad\n */\nconst ROLE_SECTIONS: Record<ContextRole, Set<number>> = {\n scanner: new Set([1, 2, 3, 4, 5]), // identity + focus + role + memory\n worker: new Set([1, 2, 3, 4, 5, 6]), // + feedback\n lead: new Set([1, 2, 3, 4, 5, 6, 7, 8]), // + daily briefing + cross-squad\n coo: new Set([1, 2, 3, 4, 5, 6, 7, 8]), // all layers + expanded budget\n verifier: new Set([1, 2, 3, 4, 5, 6]), // same as worker\n};\n\n// ── Agent Frontmatter ─────────────────────────────────────────────────\n\n/**\n * Parsed fields from an agent definition's YAML frontmatter.\n */\nexport interface AgentFrontmatter {\n context_from?: string[];\n acceptance_criteria?: string;\n max_retries?: number;\n cooldown?: string;\n /**\n * `role:` field from agent YAML frontmatter (free text).\n * Used as the primary signal for context-role selection.\n */\n agent_role?: string;\n}\n\n/**\n * Parse frontmatter fields from an agent definition file.\n * Handles non-standard format where frontmatter appears after a heading.\n */\nexport function parseAgentFrontmatter(agentPath: string): AgentFrontmatter {\n if (!agentPath || !existsSync(agentPath)) return {};\n\n let content: string;\n try {\n content = readFileSync(agentPath, 'utf-8');\n } catch {\n return {};\n }\n if (!content) return {};\n const lines = content.split('\\n');\n let inFrontmatter = false;\n const yamlLines: string[] = [];\n\n for (const line of lines) {\n if (line.trim() === '---') {\n if (inFrontmatter) break;\n inFrontmatter = true;\n continue;\n }\n if (inFrontmatter) {\n yamlLines.push(line);\n }\n }\n\n if (yamlLines.length === 0) return {};\n\n const yaml = yamlLines.join('\\n');\n const result: AgentFrontmatter = {};\n\n // context_from: [operations, finance, product, growth]\n const contextMatch = yaml.match(/context_from:\\s*\\[([^\\]]+)\\]/);\n if (contextMatch) {\n result.context_from = contextMatch[1].split(',').map(s => s.trim());\n }\n\n // acceptance_criteria: |\\n - criteria1\\n - criteria2\n const criteriaMatch = yaml.match(/acceptance_criteria:\\s*\\|\\n((?:\\s+.+\\n?)*)/);\n if (criteriaMatch) {\n result.acceptance_criteria = criteriaMatch[1].replace(/^ {2}/gm, '').trim();\n }\n\n // max_retries: 2\n const retriesMatch = yaml.match(/max_retries:\\s*(\\d+)/);\n if (retriesMatch) {\n result.max_retries = parseInt(retriesMatch[1], 10);\n }\n\n // cooldown: \"30m\" or \"6h\" or \"2 hours\"\n const cooldownMatch = yaml.match(/cooldown:\\s*[\"']?([^\"'\\n]+)[\"']?/);\n if (cooldownMatch) {\n result.cooldown = cooldownMatch[1].trim();\n }\n\n // role: <free-text>\n // Primary signal for mapping to context role (scanner/worker/lead/verifier).\n for (const line of yamlLines) {\n const trimmed = line.trim();\n if (!trimmed.startsWith('role:')) continue;\n let value = trimmed.slice('role:'.length).trim();\n // Strip wrapping quotes if present.\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith('\\'') && value.endsWith('\\''))\n ) {\n value = value.slice(1, -1).trim();\n }\n if (value) {\n result.agent_role = value;\n }\n break;\n }\n\n return result;\n}\n\n// ── MCP Server Discovery ──────────────────────────────────────────────\n\n/**\n * Extract MCP servers mentioned in an agent definition.\n * Looks for patterns like: mcp-server-name, chrome-devtools, firecrawl, etc.\n */\nexport function extractMcpServersFromDefinition(definition: string): string[] {\n const servers: Set<string> = new Set();\n\n // Common MCP server patterns\n const knownServers = [\n 'chrome-devtools',\n 'firecrawl',\n 'context7',\n 'huggingface',\n ];\n\n // Check for known servers in the definition\n for (const server of knownServers) {\n if (definition.toLowerCase().includes(server)) {\n servers.add(server);\n }\n }\n\n // Look for mcp: blocks in YAML\n const mcpMatch = definition.match(/mcp:\\s*\\n((?:\\s*-\\s*\\S+\\s*\\n?)+)/i);\n if (mcpMatch) {\n const lines = mcpMatch[1].split('\\n');\n for (const line of lines) {\n const serverMatch = line.match(/^\\s*-\\s*(\\S+)/);\n if (serverMatch) {\n servers.add(serverMatch[1]);\n }\n }\n }\n\n return Array.from(servers);\n}\n\n// ── Shared Config File Reader ─────────────────────────────────────────\n\n/**\n * Read a config file relative to the .agents directory.\n * Returns file content trimmed, or empty string if missing/unreadable.\n */\nfunction readAgentsFile(relativePath: string, warnLabel: string): string {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return '';\n\n const filePath = join(dirname(squadsDir), relativePath);\n if (!existsSync(filePath)) return '';\n\n try {\n return readFileSync(filePath, 'utf-8').trim();\n } catch (e) {\n writeLine(` ${colors.dim}warn: failed reading ${warnLabel}: ${e instanceof Error ? e.message : String(e)}${RESET}`);\n return '';\n }\n}\n\n// ── System Protocol ───────────────────────────────────────────────────\n\n/**\n * Load SYSTEM.md (L0) — the immutable base protocol for all agents.\n * Path: .agents/SYSTEM.md (top-level, next to squads/ and memory/)\n * Falls back to legacy config/SYSTEM.md, then approval-instructions.md.\n */\nexport function loadSystemProtocol(): string {\n // Primary: .agents/SYSTEM.md\n const systemMd = readAgentsFile('SYSTEM.md', 'SYSTEM.md');\n if (systemMd) return systemMd;\n\n // Fallback: legacy path\n const legacyMd = readAgentsFile('config/SYSTEM.md', 'SYSTEM.md (legacy)');\n if (legacyMd) return legacyMd;\n\n return loadApprovalInstructions();\n}\n\n/**\n * Load company.md (L1) — company context and strategic direction.\n * Path: .agents/company.md\n * This is the \"why\" layer — frames everything that follows.\n */\nexport function loadCompanyContext(): string {\n // Primary: .agents/company.md\n const companyMd = readAgentsFile('company.md', 'company.md');\n if (companyMd) return companyMd;\n\n // Fallback: legacy directives.md (for backward compat during migration)\n const memoryDir = findMemoryDir();\n if (memoryDir) {\n const directivesFile = join(memoryDir, 'company', 'directives.md');\n const content = safeRead(directivesFile);\n if (content) return content;\n }\n\n return '';\n}\n\n/**\n * Legacy: load approval instructions. Kept for backward compat — prefer SYSTEM.md.\n * @deprecated Absorbed into SYSTEM.md. Used as fallback when SYSTEM.md absent.\n */\nexport function loadApprovalInstructions(): string {\n return readAgentsFile('config/approval-instructions.md', 'approval instructions');\n}\n\n/**\n * Legacy: load post-execution instructions.\n * @deprecated Absorbed into SYSTEM.md. Used as fallback when SYSTEM.md absent.\n */\nexport function loadPostExecution(squadName: string, agentName: string): string {\n const template = readAgentsFile('config/post-execution.md', 'post-execution template');\n if (template) {\n return template\n .replace(/\\{\\{squadName\\}\\}/g, squadName)\n .replace(/\\{\\{agentName\\}\\}/g, agentName);\n }\n return '';\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\n/** Safely read a file, returning empty string on failure */\nfunction safeRead(path: string): string {\n try {\n return existsSync(path) ? readFileSync(path, 'utf-8').trim() : '';\n } catch {\n return '';\n }\n}\n\nfunction stripYamlFrontmatter(markdown: string): string {\n const lines = markdown.split('\\n');\n let dashCount = 0;\n let endIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].trim() === '---') {\n dashCount++;\n if (dashCount === 2) {\n endIdx = i;\n break;\n }\n }\n }\n if (endIdx >= 0) return lines.slice(endIdx + 1).join('\\n').trim();\n return markdown.trim();\n}\n\nfunction scoreByTokens(text: string, tokens: string[]): number {\n const lower = text.toLowerCase();\n let score = 0;\n for (const t of tokens) {\n if (!t) continue;\n if (lower.includes(t)) score += 1;\n }\n return score;\n}\n\n/**\n * Primary context-role resolver.\n *\n * Uses the agent YAML frontmatter `role:` free-text as the signal.\n * Only when ambiguous and enabled (env var) will it ask an LLM to pick\n * one of: scanner | worker | lead | verifier.\n */\nexport function resolveContextRoleFromAgent(agentPath: string, agentName: string): ContextRole {\n const fm = parseAgentFrontmatter(agentPath);\n const roleText = fm.agent_role || '';\n const normalized = roleText.trim().toLowerCase();\n\n // Direct match — new structured schema uses exact role values\n const directRoles: ContextRole[] = ['scanner', 'worker', 'lead', 'verifier'];\n for (const r of directRoles) {\n if (normalized === r) return r;\n }\n // COO is a lead with expanded budget\n if (normalized === 'coo') return 'coo';\n\n // Deterministic mapping from role text. Avoids brittle regex coupling.\n const scannerTokens = ['scan', 'monitor', 'detect', 'find', 'opportun', 'scout', 'gap', 'bottleneck'];\n const workerTokens = ['execute', 'implement', 'write', 'create', 'build', 'prototype', 'file', 'issue', 'worker'];\n const leadTokens = ['lead', 'orchestrate', 'own', 'strategy', 'roadmap', 'coordinate', 'triage', 'review', 'mvp'];\n const verifierTokens = ['verify', 'validation', 'compliance', 'audit', 'approve', 'reject', 'check', 'test', 'critic', 'verifier'];\n\n const scored: Array<[ContextRole, number]> = [\n ['scanner', scoreByTokens(normalized, scannerTokens)],\n ['worker', scoreByTokens(normalized, workerTokens)],\n ['lead', scoreByTokens(normalized, leadTokens)],\n ['verifier', scoreByTokens(normalized, verifierTokens)],\n ];\n\n scored.sort((a, b) => b[1] - a[1]);\n const best = scored[0];\n const second = scored[1];\n\n // Clean mapping => unique non-zero best score.\n const clean = best[1] > 0 && (!second || second[1] === 0);\n if (clean) return best[0];\n\n const llmEnabled = process.env.SQUADS_CONTEXT_ROLE_LLM === '1';\n if (!llmEnabled) return 'worker';\n\n // LLM fallback: best-effort classification. If it fails, return worker.\n try {\n const raw = safeRead(agentPath);\n const body = stripYamlFrontmatter(raw);\n const excerpt = body.slice(0, 1600);\n\n const prompt = [\n 'Classify the agent into exactly ONE Agents Squads context role.',\n 'Return EXACTLY one token from: scanner, worker, lead, verifier.',\n '',\n `Agent name: ${agentName}`,\n `Agent frontmatter role: ${roleText || '(missing)'}`,\n '',\n 'Agent definition excerpt:',\n excerpt,\n ].join('\\n');\n\n const escapedPrompt = prompt.replace(/'/g, \"'\\\\''\");\n const model = process.env.SQUADS_CONTEXT_ROLE_LLM_MODEL || 'claude-haiku-4-5';\n const out = execSync(\n `claude --print --dangerously-skip-permissions --disable-slash-commands --model ${model} -- '${escapedPrompt}'`,\n { encoding: 'utf-8', timeout: 60_000, maxBuffer: 2 * 1024 * 1024 }\n ).trim().toLowerCase();\n\n const tokens: ContextRole[] = ['scanner', 'worker', 'lead', 'verifier'];\n for (const t of tokens) {\n if (out === t || out.includes(t)) return t;\n }\n\n return 'worker';\n } catch {\n return 'worker';\n }\n}\n\n/** Read all .md files from a directory, concatenated */\nfunction readDirMd(dirPath: string, maxChars: number): string {\n if (!existsSync(dirPath)) return '';\n try {\n const files = readdirSync(dirPath).filter(f => f.endsWith('.md')).sort();\n const parts: string[] = [];\n let totalChars = 0;\n for (const file of files) {\n const content = safeRead(join(dirPath, file));\n if (!content) continue;\n if (totalChars + content.length > maxChars) break;\n parts.push(content);\n totalChars += content.length;\n }\n return parts.join('\\n\\n');\n } catch {\n return '';\n }\n}\n\n// ── Squad Context System Assembly ─────────────────────────────────────\n\n/**\n * Gather context for agent execution.\n *\n * Layers flow general → particular (each adds a unique dimension):\n * 1. company.md — Why (company identity, alignment)\n * 2. priorities.md — Where (current focus, urgency)\n * 3. goals.md — What (measurable targets)\n * 4. agent.md — You (agent role, instructions)\n * 5. state.md — Memory (continuity from last run)\n * 6. feedback.md — Supporting (squad feedback)\n * 7. daily-briefing — Supporting (org pulse, leads+coo only)\n * 8. cross-squad — Supporting (learnings from other squads)\n *\n * SQUAD.md is NOT injected — it's metadata for the CLI (repo, agents, config).\n * Missing files are skipped gracefully — no crashes on first run or new squads.\n */\nexport function gatherSquadContext(\n squadName: string,\n agentName: string,\n options: { verbose?: boolean; maxTokens?: number; agentPath?: string; role?: ContextRole } = {}\n): string {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return '';\n\n const memoryDir = findMemoryDir();\n const role = options.role || 'worker';\n const budget = options.maxTokens ? options.maxTokens * 4 : (ROLE_BUDGETS[role] ?? ROLE_BUDGETS.worker);\n const allowedSections = ROLE_SECTIONS[role] ?? ROLE_SECTIONS.worker;\n const sections: string[] = [];\n let usedChars = 0;\n\n /** Try to add a layer. Returns true if added, false if budget exceeded or not allowed. */\n function addLayer(layerNum: number, header: string, content: string, maxChars?: number): boolean {\n if (!allowedSections.has(layerNum)) return false;\n if (!content) return false;\n\n let text = content;\n const remaining = Math.max(0, budget - usedChars);\n const cap = maxChars !== undefined ? Math.min(maxChars, remaining) : remaining;\n if (text.length > cap) {\n text = text.substring(0, cap) + '\\n...';\n }\n\n if (usedChars + text.length > budget) {\n if (options.verbose) {\n writeLine(` ${colors.dim}Context budget exhausted at layer ${layerNum} (${header})${RESET}`);\n }\n return false;\n }\n\n sections.push(`## ${header}\\n${text}`);\n usedChars += text.length;\n return true;\n }\n\n // ── L1: company.md — Why (company identity, alignment) ──\n const companyContext = loadCompanyContext();\n if (companyContext) {\n addLayer(1, 'Company', stripYamlFrontmatter(companyContext));\n }\n\n // ── L2: priorities.md — Where (current focus, urgency) ──\n if (memoryDir) {\n const prioritiesFile = join(memoryDir, squadName, 'priorities.md');\n const content = safeRead(prioritiesFile);\n if (content) {\n addLayer(2, 'Priorities', stripYamlFrontmatter(content));\n }\n }\n\n // ── L3: goals.md — What (measurable targets) ──\n if (memoryDir) {\n const goalsFile = join(memoryDir, squadName, 'goals.md');\n const content = safeRead(goalsFile);\n if (content) {\n addLayer(3, 'Goals', stripYamlFrontmatter(content));\n }\n }\n\n // ── L4: agent.md — You (agent role, instructions) ──\n if (options.agentPath) {\n const agentContent = safeRead(options.agentPath);\n if (agentContent) {\n // Strip YAML frontmatter — inject the markdown body only\n const body = stripYamlFrontmatter(agentContent);\n addLayer(4, `Agent: ${agentName}`, body);\n }\n }\n\n // ── L5: state.md — Memory (continuity from last run) ──\n if (memoryDir) {\n const stateFile = join(memoryDir, squadName, agentName, 'state.md');\n const content = safeRead(stateFile);\n if (content) {\n // Strip frontmatter — LLM gets the body (Current/Blockers/Carry Forward)\n const body = stripYamlFrontmatter(content);\n const stateCap = (role === 'scanner' || role === 'verifier') ? 2000 : undefined;\n addLayer(5, 'Previous State', body, stateCap);\n }\n }\n\n // ── L6: feedback.md — Supporting (squad-level feedback) ──\n if (memoryDir) {\n const feedbackFile = join(memoryDir, squadName, 'feedback.md');\n const content = safeRead(feedbackFile);\n if (content) {\n addLayer(6, 'Feedback', content);\n }\n }\n\n // ── L7: Daily briefing — Supporting (org pulse, leads+coo only) ──\n if (memoryDir) {\n const dailyFile = join(memoryDir, 'daily-briefing.md');\n const content = safeRead(dailyFile);\n if (content) {\n addLayer(7, 'Daily Briefing', content);\n }\n }\n\n // ── L8: Cross-squad learnings — Supporting (from context_from agents) ──\n if (memoryDir) {\n const frontmatter = options.agentPath ? parseAgentFrontmatter(options.agentPath) : {};\n const contextSquads = frontmatter.context_from || [];\n const learningParts: string[] = [];\n for (const ctx of contextSquads) {\n const learningsFile = join(memoryDir, ctx, 'shared', 'learnings.md');\n const content = safeRead(learningsFile);\n if (content) {\n learningParts.push(`### ${ctx}\\n${content}`);\n } else if (options.verbose) {\n writeLine(` ${colors.dim}context_from: no learnings found for squad '${ctx}'${RESET}`);\n }\n }\n if (learningParts.length > 0) {\n addLayer(8, 'Cross-Squad Learnings', learningParts.join('\\n\\n'));\n }\n }\n\n if (sections.length === 0) return '';\n\n if (options.verbose) {\n writeLine(` ${colors.dim}Context: ${sections.length} layers, ~${Math.ceil(usedChars / 4)} tokens (${role} role, budget: ~${Math.ceil(budget / 4)})${RESET}`);\n }\n\n return `\\n# CONTEXT\\n${sections.join('\\n\\n')}\\n`;\n}\n"],"mappings":"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","names":[]}
|