squads-cli 0.6.1 → 0.7.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.
Files changed (112) hide show
  1. package/README.md +196 -1152
  2. package/dist/auth-YW3UPFSB.js +23 -0
  3. package/dist/autonomy-BWTVDEAT.js +102 -0
  4. package/dist/autonomy-BWTVDEAT.js.map +1 -0
  5. package/dist/chunk-3KCWNZWW.js +401 -0
  6. package/dist/chunk-3KCWNZWW.js.map +1 -0
  7. package/dist/chunk-67RO2HKR.js +174 -0
  8. package/dist/chunk-67RO2HKR.js.map +1 -0
  9. package/dist/chunk-7JVD7RD4.js +275 -0
  10. package/dist/chunk-7JVD7RD4.js.map +1 -0
  11. package/dist/chunk-BODLDQY7.js +452 -0
  12. package/dist/chunk-BODLDQY7.js.map +1 -0
  13. package/dist/chunk-FFFCFZ6A.js +121 -0
  14. package/dist/chunk-FFFCFZ6A.js.map +1 -0
  15. package/dist/chunk-FIWT2NMM.js +165 -0
  16. package/dist/chunk-FIWT2NMM.js.map +1 -0
  17. package/dist/chunk-L6GQCHDF.js +222 -0
  18. package/dist/chunk-L6GQCHDF.js.map +1 -0
  19. package/dist/{chunk-O7UV3FWI.js → chunk-LDM62TIX.js} +2 -2
  20. package/dist/chunk-LDM62TIX.js.map +1 -0
  21. package/dist/chunk-LOA3KWYJ.js +294 -0
  22. package/dist/chunk-LOA3KWYJ.js.map +1 -0
  23. package/dist/chunk-NA45DFXY.js +616 -0
  24. package/dist/chunk-NA45DFXY.js.map +1 -0
  25. package/dist/{chunk-4CMAEQQY.js → chunk-NQN6JPI7.js} +4 -3
  26. package/dist/chunk-NQN6JPI7.js.map +1 -0
  27. package/dist/chunk-OQJHPULO.js +103 -0
  28. package/dist/chunk-OQJHPULO.js.map +1 -0
  29. package/dist/chunk-QHNUMM4V.js +87 -0
  30. package/dist/chunk-QHNUMM4V.js.map +1 -0
  31. package/dist/chunk-RM6BWILN.js +74 -0
  32. package/dist/chunk-RM6BWILN.js.map +1 -0
  33. package/dist/chunk-WBR5J7EX.js +90 -0
  34. package/dist/chunk-WBR5J7EX.js.map +1 -0
  35. package/dist/chunk-Z2UKDBNL.js +162 -0
  36. package/dist/chunk-Z2UKDBNL.js.map +1 -0
  37. package/dist/cli.js +2151 -12594
  38. package/dist/cli.js.map +1 -1
  39. package/dist/context-M2A2DOFV.js +291 -0
  40. package/dist/context-M2A2DOFV.js.map +1 -0
  41. package/dist/context-feed-JMNW4GAM.js +391 -0
  42. package/dist/context-feed-JMNW4GAM.js.map +1 -0
  43. package/dist/cost-N37I4UTA.js +274 -0
  44. package/dist/cost-N37I4UTA.js.map +1 -0
  45. package/dist/create-554W5HNU.js +286 -0
  46. package/dist/create-554W5HNU.js.map +1 -0
  47. package/dist/daemon-XWPQPPPN.js +546 -0
  48. package/dist/daemon-XWPQPPPN.js.map +1 -0
  49. package/dist/dashboard-L7YKVQEB.js +945 -0
  50. package/dist/dashboard-L7YKVQEB.js.map +1 -0
  51. package/dist/dashboard-MFNRLCEE.js +794 -0
  52. package/dist/dashboard-MFNRLCEE.js.map +1 -0
  53. package/dist/doctor-RG75M5RO.js +346 -0
  54. package/dist/doctor-RG75M5RO.js.map +1 -0
  55. package/dist/env-config-KCLDBKYX.js +21 -0
  56. package/dist/exec-JQKBF7BL.js +197 -0
  57. package/dist/exec-JQKBF7BL.js.map +1 -0
  58. package/dist/feedback-KA2UYBZG.js +229 -0
  59. package/dist/feedback-KA2UYBZG.js.map +1 -0
  60. package/dist/github-UQTM5KMS.js +23 -0
  61. package/dist/goal-EOPC5ZCD.js +168 -0
  62. package/dist/goal-EOPC5ZCD.js.map +1 -0
  63. package/dist/health-3FZDOSR5.js +209 -0
  64. package/dist/health-3FZDOSR5.js.map +1 -0
  65. package/dist/history-TFVXJEDH.js +229 -0
  66. package/dist/history-TFVXJEDH.js.map +1 -0
  67. package/dist/index.js +1 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/init-UOWTNMIE.js +747 -0
  70. package/dist/init-UOWTNMIE.js.map +1 -0
  71. package/dist/kpi-2SQ2WCVT.js +413 -0
  72. package/dist/kpi-2SQ2WCVT.js.map +1 -0
  73. package/dist/learn-6ERTERAO.js +269 -0
  74. package/dist/learn-6ERTERAO.js.map +1 -0
  75. package/dist/list-KSOMUBMB.js +92 -0
  76. package/dist/list-KSOMUBMB.js.map +1 -0
  77. package/dist/login-ST6PAXYE.js +155 -0
  78. package/dist/login-ST6PAXYE.js.map +1 -0
  79. package/dist/memory-3CSNKXIL.js +562 -0
  80. package/dist/memory-3CSNKXIL.js.map +1 -0
  81. package/dist/progress-FKG4V2VH.js +202 -0
  82. package/dist/progress-FKG4V2VH.js.map +1 -0
  83. package/dist/providers-66PDCORB.js +65 -0
  84. package/dist/providers-66PDCORB.js.map +1 -0
  85. package/dist/results-2MJFLWEO.js +224 -0
  86. package/dist/results-2MJFLWEO.js.map +1 -0
  87. package/dist/run-72OQLH5A.js +2685 -0
  88. package/dist/run-72OQLH5A.js.map +1 -0
  89. package/dist/session-6H67XPAQ.js +64 -0
  90. package/dist/session-6H67XPAQ.js.map +1 -0
  91. package/dist/{chunk-NHGLXN2F.js → sessions-GVQIMN4W.js} +23 -459
  92. package/dist/sessions-GVQIMN4W.js.map +1 -0
  93. package/dist/{squad-parser-4BI3G4RS.js → squad-parser-CM3HOIWM.js} +2 -2
  94. package/dist/squad-parser-CM3HOIWM.js.map +1 -0
  95. package/dist/stats-ONZI557Q.js +335 -0
  96. package/dist/stats-ONZI557Q.js.map +1 -0
  97. package/dist/status-FYH42FTB.js +346 -0
  98. package/dist/status-FYH42FTB.js.map +1 -0
  99. package/dist/sync-HJZJNXHW.js +800 -0
  100. package/dist/sync-HJZJNXHW.js.map +1 -0
  101. package/dist/update-B4WMUOPO.js +83 -0
  102. package/dist/update-B4WMUOPO.js.map +1 -0
  103. package/dist/{update-ALJKFFM7.js → update-L7FGHN6W.js} +2 -2
  104. package/dist/update-L7FGHN6W.js.map +1 -0
  105. package/package.json +18 -10
  106. package/dist/chunk-4CMAEQQY.js.map +0 -1
  107. package/dist/chunk-NHGLXN2F.js.map +0 -1
  108. package/dist/chunk-O7UV3FWI.js.map +0 -1
  109. package/dist/sessions-6PB7ALCE.js +0 -16
  110. /package/dist/{sessions-6PB7ALCE.js.map → auth-YW3UPFSB.js.map} +0 -0
  111. /package/dist/{squad-parser-4BI3G4RS.js.map → env-config-KCLDBKYX.js.map} +0 -0
  112. /package/dist/{update-ALJKFFM7.js.map → github-UQTM5KMS.js.map} +0 -0
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/github.ts
4
+ import { execSync } from "child_process";
5
+ import { createSign } from "crypto";
6
+ import { readFileSync, existsSync } from "fs";
7
+ import { join } from "path";
8
+ import { homedir } from "os";
9
+ var APP_CONFIG_PATH = join(homedir(), ".squads", "secrets", "github-app.json");
10
+ var BOT_NAME = "agents-squads[bot]";
11
+ var BOT_EMAIL = "266303152+agents-squads[bot]@users.noreply.github.com";
12
+ var AI_COAUTHORS = {
13
+ anthropic: "Co-Authored-By: claude[bot] <209825114+claude[bot]@users.noreply.github.com>",
14
+ claude: "Co-Authored-By: claude[bot] <209825114+claude[bot]@users.noreply.github.com>",
15
+ gemini: "Co-Authored-By: gemini-code-assist <200291788+gemini-code-assist@users.noreply.github.com>",
16
+ google: "Co-Authored-By: gemini-code-assist <200291788+gemini-code-assist@users.noreply.github.com>",
17
+ openai: "Co-Authored-By: GPT <noreply@openai.com>"
18
+ };
19
+ function getCoAuthorTrailer(provider) {
20
+ const key = provider.toLowerCase().replace(/-.*$/, "");
21
+ return AI_COAUTHORS[key] || `Co-Authored-By: ${provider} <noreply@agents-squads.com>`;
22
+ }
23
+ var cachedToken = null;
24
+ function loadAppConfig() {
25
+ if (!existsSync(APP_CONFIG_PATH)) return null;
26
+ try {
27
+ const config = JSON.parse(readFileSync(APP_CONFIG_PATH, "utf-8"));
28
+ if (!config.app_id || !config.installation_id || !config.pem_path) return null;
29
+ return config;
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ function generateJWT(appId, pemPath) {
35
+ const resolvedPath = pemPath.replace(/^~/, homedir());
36
+ const pem = readFileSync(resolvedPath, "utf-8");
37
+ const now = Math.floor(Date.now() / 1e3);
38
+ const header = Buffer.from(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString("base64url");
39
+ const payload = Buffer.from(JSON.stringify({
40
+ iat: now - 60,
41
+ exp: now + 600,
42
+ iss: String(appId)
43
+ })).toString("base64url");
44
+ const sign = createSign("RSA-SHA256");
45
+ sign.update(`${header}.${payload}`);
46
+ const signature = sign.sign(pem, "base64url");
47
+ return `${header}.${payload}.${signature}`;
48
+ }
49
+ async function getGitHubAppToken() {
50
+ if (cachedToken && cachedToken.expiresAt > Date.now() + 5 * 60 * 1e3) {
51
+ return cachedToken.token;
52
+ }
53
+ const config = loadAppConfig();
54
+ if (!config) return null;
55
+ try {
56
+ const jwt = generateJWT(config.app_id, config.pem_path);
57
+ const response = await fetch(
58
+ `https://api.github.com/app/installations/${config.installation_id}/access_tokens`,
59
+ {
60
+ method: "POST",
61
+ headers: {
62
+ Authorization: `Bearer ${jwt}`,
63
+ Accept: "application/vnd.github+json"
64
+ }
65
+ }
66
+ );
67
+ if (!response.ok) return null;
68
+ const data = await response.json();
69
+ cachedToken = {
70
+ token: data.token,
71
+ expiresAt: new Date(data.expires_at).getTime()
72
+ };
73
+ return data.token;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ async function getBotGitEnv() {
79
+ const token = await getGitHubAppToken();
80
+ if (!token) return {};
81
+ return {
82
+ GIT_AUTHOR_NAME: BOT_NAME,
83
+ GIT_AUTHOR_EMAIL: BOT_EMAIL,
84
+ GIT_COMMITTER_NAME: BOT_NAME,
85
+ GIT_COMMITTER_EMAIL: BOT_EMAIL
86
+ };
87
+ }
88
+ async function getBotGhEnv() {
89
+ const token = await getGitHubAppToken();
90
+ if (!token) return {};
91
+ return { GH_TOKEN: token };
92
+ }
93
+ async function getBotPushUrl(repo) {
94
+ const token = await getGitHubAppToken();
95
+ if (!token) return null;
96
+ return `https://x-access-token:${token}@github.com/${repo}.git`;
97
+ }
98
+ function detectGitHubOrg(cwd = process.cwd()) {
99
+ try {
100
+ const remote = execSync("git remote get-url origin", {
101
+ cwd,
102
+ encoding: "utf-8",
103
+ stdio: ["pipe", "pipe", "pipe"]
104
+ }).trim();
105
+ const match = remote.match(/github\.com[:/]([^/]+)\//);
106
+ return match ? match[1] : void 0;
107
+ } catch {
108
+ return void 0;
109
+ }
110
+ }
111
+ function detectGitHubRepo(cwd = process.cwd()) {
112
+ try {
113
+ const remote = execSync("git remote get-url origin", {
114
+ cwd,
115
+ encoding: "utf-8",
116
+ stdio: ["pipe", "pipe", "pipe"]
117
+ }).trim();
118
+ const match = remote.match(/github\.com[:/]([^/]+\/[^/.]+)/);
119
+ return match ? match[1] : void 0;
120
+ } catch {
121
+ return void 0;
122
+ }
123
+ }
124
+ function createGitHubRepo(name, options = {}) {
125
+ const { org, description, isPrivate = true } = options;
126
+ const fullName = org ? `${org}/${name}` : name;
127
+ try {
128
+ execSync("gh --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
129
+ } catch {
130
+ throw new Error("gh CLI not found. Install it from https://cli.github.com/");
131
+ }
132
+ try {
133
+ execSync(`gh repo view ${fullName} --json name`, {
134
+ encoding: "utf-8",
135
+ stdio: ["pipe", "pipe", "pipe"]
136
+ });
137
+ throw new Error(`Repository "${fullName}" already exists on GitHub`);
138
+ } catch (err) {
139
+ if (err instanceof Error && err.message.includes("already exists")) {
140
+ throw err;
141
+ }
142
+ }
143
+ const args = ["gh", "repo", "create", fullName, isPrivate ? "--private" : "--public"];
144
+ if (description) {
145
+ args.push("--description", description);
146
+ }
147
+ const output = execSync(args.join(" "), {
148
+ encoding: "utf-8",
149
+ stdio: ["pipe", "pipe", "pipe"]
150
+ }).trim();
151
+ const url = output || `https://github.com/${fullName}`;
152
+ return { url, fullName };
153
+ }
154
+
155
+ export {
156
+ getCoAuthorTrailer,
157
+ getGitHubAppToken,
158
+ getBotGitEnv,
159
+ getBotGhEnv,
160
+ getBotPushUrl,
161
+ detectGitHubOrg,
162
+ detectGitHubRepo,
163
+ createGitHubRepo
164
+ };
165
+ //# sourceMappingURL=chunk-FIWT2NMM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/github.ts"],"sourcesContent":["import { execSync } from 'child_process';\nimport { createSign } from 'crypto';\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\n\nexport interface GitHubRepoResult {\n url: string;\n fullName: string;\n}\n\n// ── GitHub App Authentication ─────────────────────────────────────────\n\ninterface GitHubAppConfig {\n app_id: number;\n installation_id: number;\n pem_path: string;\n}\n\nconst APP_CONFIG_PATH = join(homedir(), '.squads', 'secrets', 'github-app.json');\nconst BOT_NAME = 'agents-squads[bot]';\nconst BOT_EMAIL = '266303152+agents-squads[bot]@users.noreply.github.com';\n\n// Co-author trailers that resolve to real GitHub profiles with avatars.\n// These show up in the Contributors section of repos.\nconst AI_COAUTHORS: Record<string, string> = {\n anthropic: 'Co-Authored-By: claude[bot] <209825114+claude[bot]@users.noreply.github.com>',\n claude: 'Co-Authored-By: claude[bot] <209825114+claude[bot]@users.noreply.github.com>',\n gemini: 'Co-Authored-By: gemini-code-assist <200291788+gemini-code-assist@users.noreply.github.com>',\n google: 'Co-Authored-By: gemini-code-assist <200291788+gemini-code-assist@users.noreply.github.com>',\n openai: 'Co-Authored-By: GPT <noreply@openai.com>',\n};\n\n/**\n * Get the Co-Authored-By trailer for the model that wrote the code.\n * Uses GitHub's noreply emails so avatars show in contributor graph.\n */\nexport function getCoAuthorTrailer(provider: string): string {\n const key = provider.toLowerCase().replace(/-.*$/, ''); // \"claude-sonnet\" → \"claude\"\n return AI_COAUTHORS[key] || `Co-Authored-By: ${provider} <noreply@agents-squads.com>`;\n}\n\nlet cachedToken: { token: string; expiresAt: number } | null = null;\n\nfunction loadAppConfig(): GitHubAppConfig | null {\n if (!existsSync(APP_CONFIG_PATH)) return null;\n try {\n const config = JSON.parse(readFileSync(APP_CONFIG_PATH, 'utf-8'));\n if (!config.app_id || !config.installation_id || !config.pem_path) return null;\n return config;\n } catch {\n return null;\n }\n}\n\nfunction generateJWT(appId: number, pemPath: string): string {\n const resolvedPath = pemPath.replace(/^~/, homedir());\n const pem = readFileSync(resolvedPath, 'utf-8');\n const now = Math.floor(Date.now() / 1000);\n\n const header = Buffer.from(JSON.stringify({ alg: 'RS256', typ: 'JWT' })).toString('base64url');\n const payload = Buffer.from(JSON.stringify({\n iat: now - 60,\n exp: now + 600,\n iss: String(appId),\n })).toString('base64url');\n\n const sign = createSign('RSA-SHA256');\n sign.update(`${header}.${payload}`);\n const signature = sign.sign(pem, 'base64url');\n\n return `${header}.${payload}.${signature}`;\n}\n\n/**\n * Get a GitHub App installation token.\n * Caches the token until 5 minutes before expiry.\n * Returns null if the app is not configured.\n */\nexport async function getGitHubAppToken(): Promise<string | null> {\n // Return cached token if still valid (5 min buffer)\n if (cachedToken && cachedToken.expiresAt > Date.now() + 5 * 60 * 1000) {\n return cachedToken.token;\n }\n\n const config = loadAppConfig();\n if (!config) return null;\n\n try {\n const jwt = generateJWT(config.app_id, config.pem_path);\n const response = await fetch(\n `https://api.github.com/app/installations/${config.installation_id}/access_tokens`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${jwt}`,\n Accept: 'application/vnd.github+json',\n },\n },\n );\n\n if (!response.ok) return null;\n\n const data = await response.json() as { token: string; expires_at: string };\n cachedToken = {\n token: data.token,\n expiresAt: new Date(data.expires_at).getTime(),\n };\n return data.token;\n } catch {\n return null;\n }\n}\n\n/**\n * Git environment variables for bot-authored commits.\n * Falls back to empty object if app not configured (uses user's git config).\n */\nexport async function getBotGitEnv(): Promise<Record<string, string>> {\n const token = await getGitHubAppToken();\n if (!token) return {};\n\n return {\n GIT_AUTHOR_NAME: BOT_NAME,\n GIT_AUTHOR_EMAIL: BOT_EMAIL,\n GIT_COMMITTER_NAME: BOT_NAME,\n GIT_COMMITTER_EMAIL: BOT_EMAIL,\n };\n}\n\n/**\n * Environment for gh CLI commands authenticated as the bot.\n * Falls back to empty object (uses user's gh auth).\n */\nexport async function getBotGhEnv(): Promise<Record<string, string>> {\n const token = await getGitHubAppToken();\n if (!token) return {};\n return { GH_TOKEN: token };\n}\n\n/**\n * Get the git push URL with bot token embedded for authentication.\n * Returns null if app not configured.\n */\nexport async function getBotPushUrl(repo: string): Promise<string | null> {\n const token = await getGitHubAppToken();\n if (!token) return null;\n return `https://x-access-token:${token}@github.com/${repo}.git`;\n}\n\n\n/**\n * Detect GitHub org from the current project's git remote.\n * Falls back to undefined if not in a git repo or remote is not GitHub.\n */\nexport function detectGitHubOrg(cwd: string = process.cwd()): string | undefined {\n try {\n const remote = execSync('git remote get-url origin', {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n // Match github.com/<org>/<repo>\n const match = remote.match(/github\\.com[:/]([^/]+)\\//);\n return match ? match[1] : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Detect full GitHub repo (org/name) from git remote.\n */\nexport function detectGitHubRepo(cwd: string = process.cwd()): string | undefined {\n try {\n const remote = execSync('git remote get-url origin', {\n cwd,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const match = remote.match(/github\\.com[:/]([^/]+\\/[^/.]+)/);\n return match ? match[1] : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a GitHub repository using the gh CLI.\n * Requires gh CLI to be installed and authenticated.\n */\nexport function createGitHubRepo(\n name: string,\n options: {\n org?: string;\n description?: string;\n isPrivate?: boolean;\n } = {}\n): GitHubRepoResult {\n const { org, description, isPrivate = true } = options;\n const fullName = org ? `${org}/${name}` : name;\n\n // Verify gh is available\n try {\n execSync('gh --version', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });\n } catch {\n throw new Error('gh CLI not found. Install it from https://cli.github.com/');\n }\n\n // Check if repo already exists\n try {\n execSync(`gh repo view ${fullName} --json name`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n throw new Error(`Repository \"${fullName}\" already exists on GitHub`);\n } catch (err) {\n if (err instanceof Error && err.message.includes('already exists')) {\n throw err;\n }\n // Repo doesn't exist — proceed\n }\n\n // Build gh repo create command\n const args = ['gh', 'repo', 'create', fullName, isPrivate ? '--private' : '--public'];\n if (description) {\n args.push('--description', description);\n }\n\n const output = execSync(args.join(' '), {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n\n const url = output || `https://github.com/${fullName}`;\n return { url, fullName };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/version.ts
4
+ import { createRequire } from "module";
5
+ var require2 = createRequire(import.meta.url);
6
+ var pkg = require2("../package.json");
7
+ var version = pkg.version;
8
+
9
+ // src/lib/telemetry.ts
10
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
11
+ import { join } from "path";
12
+ import { homedir, platform, release } from "os";
13
+ import { randomUUID } from "crypto";
14
+ var TELEMETRY_DIR = join(homedir(), ".squads-cli");
15
+ var CONFIG_PATH = join(TELEMETRY_DIR, "telemetry.json");
16
+ var EVENTS_PATH = join(TELEMETRY_DIR, "events.json");
17
+ var TELEMETRY_ENDPOINT = process.env.SQUADS_TELEMETRY_ENDPOINT || Buffer.from(
18
+ "aHR0cHM6Ly9zcXVhZHMtdGVsZW1ldHJ5LTk3ODg3MTgxNzYxMC51cy1jZW50cmFsMS5ydW4uYXBwL3Bpbmc=",
19
+ "base64"
20
+ ).toString();
21
+ var TELEMETRY_KEY = process.env.SQUADS_TELEMETRY_KEY || "";
22
+ var eventQueue = [];
23
+ var flushScheduled = false;
24
+ var cachedSystemContext = null;
25
+ function detectUserType() {
26
+ if (process.env.SQUADS_AGENT) {
27
+ return "agent";
28
+ }
29
+ if (process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true" || process.env.GITLAB_CI === "true" || process.env.JENKINS_URL || process.env.BUILDKITE === "true" || process.env.CIRCLECI === "true" || process.env.AZURE_PIPELINES === "true" || process.env.TF_BUILD === "True") {
30
+ return "ci";
31
+ }
32
+ return "human";
33
+ }
34
+ function getSystemContext() {
35
+ if (cachedSystemContext) return cachedSystemContext;
36
+ cachedSystemContext = {
37
+ os: platform(),
38
+ // darwin, linux, win32
39
+ osVersion: release(),
40
+ nodeVersion: process.version,
41
+ shell: process.env.SHELL?.split("/").pop() || process.env.ComSpec?.split("\\").pop(),
42
+ terminal: process.env.TERM_PROGRAM || void 0,
43
+ ci: process.env.CI === "true" ? "true" : void 0,
44
+ userType: detectUserType(),
45
+ // Agent context (set by squads run)
46
+ squad: process.env.SQUADS_SQUAD || void 0,
47
+ agent: process.env.SQUADS_AGENT || void 0,
48
+ executionId: process.env.SQUADS_EXECUTION_ID || void 0
49
+ };
50
+ return cachedSystemContext;
51
+ }
52
+ function ensureDir() {
53
+ if (!existsSync(TELEMETRY_DIR)) {
54
+ mkdirSync(TELEMETRY_DIR, { recursive: true });
55
+ }
56
+ }
57
+ function getConfig() {
58
+ ensureDir();
59
+ if (!existsSync(CONFIG_PATH)) {
60
+ const config = {
61
+ enabled: true,
62
+ // Opt-out by default (common for CLIs)
63
+ anonymousId: randomUUID(),
64
+ firstRun: (/* @__PURE__ */ new Date()).toISOString()
65
+ };
66
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
67
+ return config;
68
+ }
69
+ try {
70
+ return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
71
+ } catch {
72
+ return { enabled: false, anonymousId: "", firstRun: "" };
73
+ }
74
+ }
75
+ function isEnabled() {
76
+ if (process.env.SQUADS_TELEMETRY_DISABLED === "1") {
77
+ return false;
78
+ }
79
+ if (process.env.DO_NOT_TRACK === "1") {
80
+ return false;
81
+ }
82
+ return getConfig().enabled;
83
+ }
84
+ async function track(event, properties) {
85
+ if (!isEnabled()) return;
86
+ const config = getConfig();
87
+ const telemetryEvent = {
88
+ event,
89
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
90
+ properties: {
91
+ ...properties,
92
+ ...getSystemContext(),
93
+ anonymousId: config.anonymousId,
94
+ cliVersion: version
95
+ }
96
+ };
97
+ storeEventLocally(telemetryEvent);
98
+ eventQueue.push(telemetryEvent);
99
+ if (TELEMETRY_ENDPOINT && !flushScheduled) {
100
+ flushScheduled = true;
101
+ setImmediate(() => {
102
+ flushEvents().catch(() => {
103
+ });
104
+ });
105
+ }
106
+ }
107
+ async function flushEvents() {
108
+ if (!TELEMETRY_ENDPOINT || !TELEMETRY_KEY || eventQueue.length === 0) {
109
+ flushScheduled = false;
110
+ return;
111
+ }
112
+ const batch = [...eventQueue];
113
+ eventQueue = [];
114
+ flushScheduled = false;
115
+ try {
116
+ await fetch(TELEMETRY_ENDPOINT, {
117
+ method: "POST",
118
+ headers: {
119
+ "Content-Type": "application/json",
120
+ "X-Squads-Key": TELEMETRY_KEY
121
+ },
122
+ body: JSON.stringify({ events: batch })
123
+ });
124
+ } catch {
125
+ eventQueue = [...batch, ...eventQueue].slice(-100);
126
+ }
127
+ }
128
+ function storeEventLocally(event) {
129
+ ensureDir();
130
+ let events = [];
131
+ if (existsSync(EVENTS_PATH)) {
132
+ try {
133
+ events = JSON.parse(readFileSync(EVENTS_PATH, "utf-8"));
134
+ } catch {
135
+ events = [];
136
+ }
137
+ }
138
+ events.push(event);
139
+ if (events.length > 1e3) {
140
+ events = events.slice(-1e3);
141
+ }
142
+ writeFileSync(EVENTS_PATH, JSON.stringify(events, null, 2));
143
+ }
144
+ var Events = {
145
+ // Lifecycle
146
+ CLI_INIT: "cli.init",
147
+ CLI_ERROR: "cli.error",
148
+ // Commands
149
+ CLI_RUN: "cli.run",
150
+ CLI_STATUS: "cli.status",
151
+ CLI_DASHBOARD: "cli.dashboard",
152
+ CLI_WORKERS: "cli.workers",
153
+ CLI_TONIGHT: "cli.tonight",
154
+ CLI_CONTEXT: "cli.context",
155
+ CLI_COST: "cli.cost",
156
+ CLI_EXEC: "cli.exec",
157
+ CLI_BASELINE: "cli.baseline",
158
+ // Goals
159
+ CLI_GOAL_SET: "cli.goal.set",
160
+ CLI_GOAL_LIST: "cli.goal.list",
161
+ CLI_GOAL_COMPLETE: "cli.goal.complete",
162
+ CLI_GOAL_PROGRESS: "cli.goal.progress",
163
+ // Memory
164
+ CLI_MEMORY_QUERY: "cli.memory.query",
165
+ CLI_MEMORY_SHOW: "cli.memory.show",
166
+ CLI_MEMORY_UPDATE: "cli.memory.update",
167
+ CLI_MEMORY_LIST: "cli.memory.list",
168
+ CLI_MEMORY_SYNC: "cli.memory.sync",
169
+ // Feedback
170
+ CLI_FEEDBACK_ADD: "cli.feedback.add",
171
+ CLI_FEEDBACK_SHOW: "cli.feedback.show",
172
+ CLI_FEEDBACK_STATS: "cli.feedback.stats",
173
+ // Learnings
174
+ CLI_LEARN: "cli.learn",
175
+ CLI_LEARN_SHOW: "cli.learn.show",
176
+ CLI_LEARN_SEARCH: "cli.learn.search",
177
+ // Auth
178
+ CLI_LOGIN: "cli.login",
179
+ CLI_LOGOUT: "cli.logout",
180
+ // Providers
181
+ CLI_PROVIDERS: "cli.providers",
182
+ // KPIs
183
+ CLI_KPI_SHOW: "cli.kpi.show",
184
+ CLI_KPI_RECORD: "cli.kpi.record",
185
+ CLI_KPI_TREND: "cli.kpi.trend",
186
+ CLI_KPI_INSIGHTS: "cli.kpi.insights",
187
+ CLI_KPI_LIST: "cli.kpi.list",
188
+ // Cycle Sync
189
+ CLI_SYNC_CYCLE: "cli.sync.cycle",
190
+ // Context Condenser
191
+ CONDENSER_COMPRESS: "condenser.compress",
192
+ CONDENSER_DEDUPE: "condenser.dedupe",
193
+ CONDENSER_PRUNE: "condenser.prune",
194
+ CONDENSER_SUMMARIZE: "condenser.summarize"
195
+ };
196
+ var exitHandlerRegistered = false;
197
+ function registerExitHandler() {
198
+ if (exitHandlerRegistered) return;
199
+ exitHandlerRegistered = true;
200
+ process.on("beforeExit", async () => {
201
+ if (eventQueue.length > 0) {
202
+ await flushEvents();
203
+ }
204
+ });
205
+ const signalHandler = async (_signal) => {
206
+ if (eventQueue.length > 0) {
207
+ await flushEvents();
208
+ }
209
+ process.exit(0);
210
+ };
211
+ process.on("SIGINT", () => signalHandler("SIGINT"));
212
+ process.on("SIGTERM", () => signalHandler("SIGTERM"));
213
+ }
214
+
215
+ export {
216
+ version,
217
+ track,
218
+ flushEvents,
219
+ Events,
220
+ registerExitHandler
221
+ };
222
+ //# sourceMappingURL=chunk-L6GQCHDF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version.ts","../src/lib/telemetry.ts"],"sourcesContent":["import { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json');\nexport const version: string = pkg.version;\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir, platform, release } from 'os';\nimport { randomUUID } from 'crypto';\nimport { version as cliVersion } from '../version.js';\n\ninterface TelemetryEvent {\n event: string;\n timestamp: string;\n properties?: Record<string, string | number | boolean | undefined>;\n}\n\ninterface TelemetryConfig {\n enabled: boolean;\n anonymousId: string;\n firstRun: string;\n}\n\nconst TELEMETRY_DIR = join(homedir(), '.squads-cli');\nconst CONFIG_PATH = join(TELEMETRY_DIR, 'telemetry.json');\nconst EVENTS_PATH = join(TELEMETRY_DIR, 'events.json');\n\n// Telemetry endpoint - locked to Agents Squads infrastructure\n// Users can opt-out but cannot redirect telemetry\nconst TELEMETRY_ENDPOINT = process.env.SQUADS_TELEMETRY_ENDPOINT || Buffer.from(\n 'aHR0cHM6Ly9zcXVhZHMtdGVsZW1ldHJ5LTk3ODg3MTgxNzYxMC51cy1jZW50cmFsMS5ydW4uYXBwL3Bpbmc=',\n 'base64'\n).toString();\n\n// API key for endpoint validation — must be set via environment variable\n// NEVER hardcode API keys in source (see: engineering#51)\nconst TELEMETRY_KEY = process.env.SQUADS_TELEMETRY_KEY || '';\n\n// Event queue for batch flushing\nlet eventQueue: TelemetryEvent[] = [];\nlet flushScheduled = false;\n\n// Cached system context (computed once per session)\nlet cachedSystemContext: Record<string, string | undefined> | null = null;\n\n/**\n * Detect user type for segmentation.\n * - 'agent': Autonomous agent execution (via squads run)\n * - 'ci': CI/CD environment (GitHub Actions, Azure Pipelines, etc)\n * - 'human': Interactive terminal usage\n */\nfunction detectUserType(): 'human' | 'agent' | 'ci' {\n // Agent execution (set by squads run command)\n // SQUADS_AGENT contains the agent name when running via squads run\n if (process.env.SQUADS_AGENT) {\n return 'agent';\n }\n\n // CI/CD environments\n if (\n process.env.CI === 'true' ||\n process.env.GITHUB_ACTIONS === 'true' ||\n process.env.GITLAB_CI === 'true' ||\n process.env.JENKINS_URL ||\n process.env.BUILDKITE === 'true' ||\n process.env.CIRCLECI === 'true' ||\n process.env.AZURE_PIPELINES === 'true' ||\n process.env.TF_BUILD === 'True'\n ) {\n return 'ci';\n }\n\n return 'human';\n}\n\n/**\n * Get minimal system context for error identification.\n * Computed once per session for performance.\n */\nfunction getSystemContext(): Record<string, string | undefined> {\n if (cachedSystemContext) return cachedSystemContext;\n\n cachedSystemContext = {\n os: platform(), // darwin, linux, win32\n osVersion: release(),\n nodeVersion: process.version,\n shell: process.env.SHELL?.split('/').pop() || process.env.ComSpec?.split('\\\\').pop(),\n terminal: process.env.TERM_PROGRAM || undefined,\n ci: process.env.CI === 'true' ? 'true' : undefined,\n userType: detectUserType(),\n // Agent context (set by squads run)\n squad: process.env.SQUADS_SQUAD || undefined,\n agent: process.env.SQUADS_AGENT || undefined,\n executionId: process.env.SQUADS_EXECUTION_ID || undefined,\n };\n\n return cachedSystemContext;\n}\n\nfunction ensureDir(): void {\n if (!existsSync(TELEMETRY_DIR)) {\n mkdirSync(TELEMETRY_DIR, { recursive: true });\n }\n}\n\nfunction getConfig(): TelemetryConfig {\n ensureDir();\n\n if (!existsSync(CONFIG_PATH)) {\n const config: TelemetryConfig = {\n enabled: true, // Opt-out by default (common for CLIs)\n anonymousId: randomUUID(),\n firstRun: new Date().toISOString(),\n };\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));\n return config;\n }\n\n try {\n return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));\n } catch {\n return { enabled: false, anonymousId: '', firstRun: '' };\n }\n}\n\nfunction saveConfig(config: TelemetryConfig): void {\n ensureDir();\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));\n}\n\n/**\n * Check if telemetry is enabled.\n * Telemetry is disabled if SQUADS_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1.\n * @returns true if telemetry collection is enabled\n */\nexport function isEnabled(): boolean {\n // Check environment variable first (allows CI/testing override)\n if (process.env.SQUADS_TELEMETRY_DISABLED === '1') {\n return false;\n }\n if (process.env.DO_NOT_TRACK === '1') {\n return false;\n }\n\n return getConfig().enabled;\n}\n\n/**\n * Enable telemetry collection.\n * Persists the setting to ~/.squads-cli/telemetry.json\n */\nexport function enable(): void {\n const config = getConfig();\n config.enabled = true;\n saveConfig(config);\n}\n\n/**\n * Disable telemetry collection.\n * Persists the setting to ~/.squads-cli/telemetry.json\n */\nexport function disable(): void {\n const config = getConfig();\n config.enabled = false;\n saveConfig(config);\n}\n\n/**\n * Get the anonymous identifier for this CLI installation.\n * Generated once on first run and persisted.\n * @returns UUID string\n */\nexport function getAnonymousId(): string {\n return getConfig().anonymousId;\n}\n\n/**\n * Track a telemetry event with optional properties.\n * Events are batched and flushed asynchronously.\n * @param event - Event name (e.g., 'cli.status', 'cli.error')\n * @param properties - Optional key-value pairs of event metadata\n */\nexport async function track(event: string, properties?: Record<string, string | number | boolean | undefined>): Promise<void> {\n if (!isEnabled()) return;\n\n const config = getConfig();\n\n const telemetryEvent: TelemetryEvent = {\n event,\n timestamp: new Date().toISOString(),\n properties: {\n ...properties,\n ...getSystemContext(),\n anonymousId: config.anonymousId,\n cliVersion,\n },\n };\n\n // Store locally (for debugging/review)\n storeEventLocally(telemetryEvent);\n\n // Queue for batch sending\n eventQueue.push(telemetryEvent);\n\n // Schedule flush if not already scheduled\n if (TELEMETRY_ENDPOINT && !flushScheduled) {\n flushScheduled = true;\n // Flush on next tick to batch events from same command\n setImmediate(() => {\n flushEvents().catch(() => {});\n });\n }\n}\n\n/**\n * Flush queued events to the telemetry endpoint\n */\nexport async function flushEvents(): Promise<void> {\n if (!TELEMETRY_ENDPOINT || !TELEMETRY_KEY || eventQueue.length === 0) {\n flushScheduled = false;\n return;\n }\n\n const batch = [...eventQueue];\n eventQueue = [];\n flushScheduled = false;\n\n try {\n await fetch(TELEMETRY_ENDPOINT, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Squads-Key': TELEMETRY_KEY,\n },\n body: JSON.stringify({ events: batch }),\n });\n } catch {\n // Restore events on failure (will retry on next track)\n eventQueue = [...batch, ...eventQueue].slice(-100); // Keep max 100\n }\n}\n\n/**\n * Track an error event\n */\nexport async function trackError(\n command: string,\n error: Error,\n context?: Record<string, string | number | boolean>\n): Promise<void> {\n await track(Events.CLI_ERROR, {\n command,\n errorType: error.constructor.name,\n errorMessage: error.message.slice(0, 100), // Truncate for privacy\n ...context,\n });\n}\n\n/**\n * Wrap an async command function with telemetry\n */\nexport function instrumentCommand<T>(\n name: string,\n fn: () => Promise<T>\n): () => Promise<T> {\n return async () => {\n const start = Date.now();\n try {\n const result = await fn();\n await track(`cli.${name}`, {\n durationMs: Date.now() - start,\n success: true,\n });\n return result;\n } catch (error) {\n await trackError(name, error as Error, {\n durationMs: Date.now() - start,\n });\n throw error;\n }\n };\n}\n\nfunction storeEventLocally(event: TelemetryEvent): void {\n ensureDir();\n\n let events: TelemetryEvent[] = [];\n\n if (existsSync(EVENTS_PATH)) {\n try {\n events = JSON.parse(readFileSync(EVENTS_PATH, 'utf-8'));\n } catch {\n events = [];\n }\n }\n\n // Keep last 1000 events\n events.push(event);\n if (events.length > 1000) {\n events = events.slice(-1000);\n }\n\n writeFileSync(EVENTS_PATH, JSON.stringify(events, null, 2));\n}\n\n/**\n * Pre-defined event names for consistency across the CLI.\n * Use these constants instead of string literals.\n */\nexport const Events = {\n // Lifecycle\n CLI_INIT: 'cli.init',\n CLI_ERROR: 'cli.error',\n\n // Commands\n CLI_RUN: 'cli.run',\n CLI_STATUS: 'cli.status',\n CLI_DASHBOARD: 'cli.dashboard',\n CLI_WORKERS: 'cli.workers',\n CLI_TONIGHT: 'cli.tonight',\n CLI_CONTEXT: 'cli.context',\n CLI_COST: 'cli.cost',\n CLI_EXEC: 'cli.exec',\n CLI_BASELINE: 'cli.baseline',\n\n // Goals\n CLI_GOAL_SET: 'cli.goal.set',\n CLI_GOAL_LIST: 'cli.goal.list',\n CLI_GOAL_COMPLETE: 'cli.goal.complete',\n CLI_GOAL_PROGRESS: 'cli.goal.progress',\n\n // Memory\n CLI_MEMORY_QUERY: 'cli.memory.query',\n CLI_MEMORY_SHOW: 'cli.memory.show',\n CLI_MEMORY_UPDATE: 'cli.memory.update',\n CLI_MEMORY_LIST: 'cli.memory.list',\n CLI_MEMORY_SYNC: 'cli.memory.sync',\n\n // Feedback\n CLI_FEEDBACK_ADD: 'cli.feedback.add',\n CLI_FEEDBACK_SHOW: 'cli.feedback.show',\n CLI_FEEDBACK_STATS: 'cli.feedback.stats',\n\n // Learnings\n CLI_LEARN: 'cli.learn',\n CLI_LEARN_SHOW: 'cli.learn.show',\n CLI_LEARN_SEARCH: 'cli.learn.search',\n\n // Auth\n CLI_LOGIN: 'cli.login',\n CLI_LOGOUT: 'cli.logout',\n\n // Providers\n CLI_PROVIDERS: 'cli.providers',\n\n // KPIs\n CLI_KPI_SHOW: 'cli.kpi.show',\n CLI_KPI_RECORD: 'cli.kpi.record',\n CLI_KPI_TREND: 'cli.kpi.trend',\n CLI_KPI_INSIGHTS: 'cli.kpi.insights',\n CLI_KPI_LIST: 'cli.kpi.list',\n\n // Cycle Sync\n CLI_SYNC_CYCLE: 'cli.sync.cycle',\n\n // Context Condenser\n CONDENSER_COMPRESS: 'condenser.compress',\n CONDENSER_DEDUPE: 'condenser.dedupe',\n CONDENSER_PRUNE: 'condenser.prune',\n CONDENSER_SUMMARIZE: 'condenser.summarize',\n} as const;\n\n/**\n * Track command execution time.\n * Call at start of command, returns function to call when command completes.\n * @param command - Command name (without 'cli.' prefix)\n * @returns Callback to invoke when command completes\n * @example\n * const done = trackCommand('status');\n * // ... execute command ...\n * done(); // Records duration\n */\nexport function trackCommand(command: string): () => void {\n const start = Date.now();\n\n return () => {\n const duration = Date.now() - start;\n track(`cli.${command}`, { durationMs: duration });\n };\n}\n\n// Register exit handler to flush remaining events\nlet exitHandlerRegistered = false;\n\n/**\n * Register process exit handlers to flush pending telemetry events.\n * Call once at CLI startup. Handles SIGINT, SIGTERM, and normal exit.\n */\nexport function registerExitHandler(): void {\n if (exitHandlerRegistered) return;\n exitHandlerRegistered = true;\n\n // beforeExit allows async operations (unlike 'exit')\n process.on('beforeExit', async () => {\n if (eventQueue.length > 0) {\n await flushEvents();\n }\n });\n\n // For signals, we need to handle manually\n const signalHandler = async (_signal: string) => {\n if (eventQueue.length > 0) {\n await flushEvents();\n }\n process.exit(0);\n };\n\n process.on('SIGINT', () => signalHandler('SIGINT'));\n process.on('SIGTERM', () => signalHandler('SIGTERM'));\n}\n"],"mappings":"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","names":["require"]}
@@ -204,7 +204,7 @@ function parseSquadFile(filePath) {
204
204
  continue;
205
205
  }
206
206
  if (inTable && line.includes("|") && !line.includes("---")) {
207
- const cells = line.split("|").map((c) => c.trim().replace(/`/g, ""));
207
+ const cells = line.split("|").map((c) => c.trim().replace(/`/g, "").replace(/\*\*/g, ""));
208
208
  const agentIdx = tableHeaders.findIndex((h) => h === "agent");
209
209
  const roleIdx = tableHeaders.findIndex((h) => h === "role");
210
210
  const triggerIdx = tableHeaders.findIndex((h) => h === "trigger");
@@ -589,4 +589,4 @@ export {
589
589
  getSquadLocalSkills,
590
590
  resolveExecutionContext
591
591
  };
592
- //# sourceMappingURL=chunk-O7UV3FWI.js.map
592
+ //# sourceMappingURL=chunk-LDM62TIX.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, '').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"]}