squads-cli 0.6.2 → 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 +2136 -12600
  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,274 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ detectPlan,
4
+ fetchBridgeStats
5
+ } from "./chunk-NA45DFXY.js";
6
+ import "./chunk-LOA3KWYJ.js";
7
+ import {
8
+ Events,
9
+ track
10
+ } from "./chunk-L6GQCHDF.js";
11
+ import {
12
+ findSquadsDir,
13
+ listSquads,
14
+ loadSquad
15
+ } from "./chunk-LDM62TIX.js";
16
+ import {
17
+ RESET,
18
+ bold,
19
+ box,
20
+ colors,
21
+ gradient,
22
+ padEnd,
23
+ writeLine
24
+ } from "./chunk-N7KDWU4W.js";
25
+ import "./chunk-7OCVIDC7.js";
26
+
27
+ // src/commands/cost.ts
28
+ function getBudgetStatus(squadName, spent, dailyBudget, weeklyBudget) {
29
+ let status = "no-budget";
30
+ let dailyPercent = null;
31
+ let weeklyPercent = null;
32
+ if (dailyBudget !== null) {
33
+ dailyPercent = spent / dailyBudget * 100;
34
+ if (dailyPercent >= 100) {
35
+ status = "over";
36
+ } else if (dailyPercent >= 80) {
37
+ status = "warning";
38
+ } else {
39
+ status = "ok";
40
+ }
41
+ }
42
+ if (weeklyBudget !== null) {
43
+ weeklyPercent = spent / weeklyBudget * 100;
44
+ }
45
+ return {
46
+ squad: squadName,
47
+ spent,
48
+ dailyBudget,
49
+ weeklyBudget,
50
+ dailyPercent,
51
+ weeklyPercent,
52
+ status
53
+ };
54
+ }
55
+ async function costCommand(options = {}) {
56
+ await track(Events.CLI_COST, { squad: options.squad || "all" });
57
+ const stats = await fetchBridgeStats();
58
+ const plan = detectPlan();
59
+ if (options.json) {
60
+ const result = buildJsonOutput(stats, plan, options.squad);
61
+ console.log(JSON.stringify(result, null, 2));
62
+ return;
63
+ }
64
+ writeLine();
65
+ writeLine(` ${gradient("squads")} ${colors.dim}cost${RESET}${options.squad ? ` ${colors.cyan}${options.squad}${RESET}` : ""}`);
66
+ writeLine();
67
+ if (!stats) {
68
+ writeLine(` ${colors.yellow}\u26A0 Bridge unavailable${RESET}`);
69
+ writeLine(` ${colors.dim}Run \`squads stack up\` to start infrastructure${RESET}`);
70
+ writeLine();
71
+ return;
72
+ }
73
+ const todaySection = ` ${bold}Today${RESET}`;
74
+ writeLine(todaySection);
75
+ writeLine(` ${colors.cyan}$${stats.today.costUsd.toFixed(2)}${RESET} ${colors.dim}|${RESET} ${stats.today.generations.toLocaleString()} calls ${colors.dim}|${RESET} ${formatTokens(stats.today.inputTokens + stats.today.outputTokens)} tokens`);
76
+ if (stats.byModel && stats.byModel.length > 0) {
77
+ const modelParts = stats.byModel.filter((m) => m.costUsd > 0).sort((a, b) => b.costUsd - a.costUsd).slice(0, 4).map((m) => `${colors.dim}${shortModelName(m.model)}${RESET} $${m.costUsd.toFixed(0)}`);
78
+ if (modelParts.length > 0) {
79
+ writeLine(` ${colors.dim}Models:${RESET} ${modelParts.join(" \xB7 ")}`);
80
+ }
81
+ }
82
+ writeLine();
83
+ if (stats.week) {
84
+ writeLine(` ${bold}Week${RESET}`);
85
+ writeLine(` ${colors.purple}$${stats.week.costUsd.toFixed(2)}${RESET} ${colors.dim}|${RESET} ${stats.week.generations.toLocaleString()} calls ${colors.dim}|${RESET} ${formatTokens(stats.week.inputTokens + stats.week.outputTokens)} tokens`);
86
+ writeLine();
87
+ }
88
+ writeLine(` ${bold}Budget${RESET} ${colors.dim}(daily)${RESET}`);
89
+ const budgetBar = createBudgetBar(stats.budget.usedPct);
90
+ const budgetColor = stats.budget.usedPct >= 100 ? colors.red : stats.budget.usedPct >= 80 ? colors.yellow : colors.green;
91
+ writeLine(` ${budgetBar} ${budgetColor}$${stats.budget.used.toFixed(0)}${RESET}/${colors.dim}$${stats.budget.daily}${RESET} (${stats.budget.usedPct.toFixed(0)}%)`);
92
+ writeLine();
93
+ if (stats.bySquad && stats.bySquad.length > 0 && !options.squad) {
94
+ writeLine(` ${bold}By Squad${RESET}`);
95
+ writeLine();
96
+ const squadsDir = findSquadsDir();
97
+ const squadBudgets = /* @__PURE__ */ new Map();
98
+ if (squadsDir) {
99
+ for (const name of listSquads(squadsDir)) {
100
+ const squad = loadSquad(name);
101
+ if (squad?.context?.budget) {
102
+ squadBudgets.set(name, {
103
+ daily: squad.context.budget.daily || null,
104
+ weekly: squad.context.budget.weekly || null
105
+ });
106
+ }
107
+ }
108
+ }
109
+ const tableWidth = 52;
110
+ writeLine(` ${colors.purple}${box.topLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.topRight}${RESET}`);
111
+ const w = { name: 14, spent: 10, budget: 12, status: 14 };
112
+ const header = ` ${colors.purple}${box.vertical}${RESET} ${bold}${padEnd("SQUAD", w.name)}${RESET}${bold}${padEnd("SPENT", w.spent)}${RESET}${bold}${padEnd("BUDGET", w.budget)}${RESET}${bold}STATUS${RESET} ${colors.purple}${box.vertical}${RESET}`;
113
+ writeLine(header);
114
+ writeLine(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);
115
+ for (const sq of stats.bySquad.sort((a, b) => b.costUsd - a.costUsd)) {
116
+ const budgetInfo = squadBudgets.get(sq.squad);
117
+ const dailyBudget = budgetInfo?.daily || null;
118
+ const spentStr = `$${sq.costUsd.toFixed(2)}`;
119
+ const budgetStr = dailyBudget ? `$${dailyBudget}/d` : `${colors.dim}\u2014${RESET}`;
120
+ let statusStr;
121
+ if (dailyBudget) {
122
+ const pct = sq.costUsd / dailyBudget * 100;
123
+ if (pct >= 100) {
124
+ statusStr = `${colors.red}\u25CF OVER${RESET}`;
125
+ } else if (pct >= 80) {
126
+ statusStr = `${colors.yellow}\u25CF ${pct.toFixed(0)}%${RESET}`;
127
+ } else {
128
+ statusStr = `${colors.green}\u2713 ${pct.toFixed(0)}%${RESET}`;
129
+ }
130
+ } else {
131
+ statusStr = `${colors.dim}no budget${RESET}`;
132
+ }
133
+ const row = ` ${colors.purple}${box.vertical}${RESET} ${colors.cyan}${padEnd(sq.squad, w.name)}${RESET}${padEnd(spentStr, w.spent)}${padEnd(budgetStr, w.budget)}${padEnd(statusStr, w.status)}${colors.purple}${box.vertical}${RESET}`;
134
+ writeLine(row);
135
+ }
136
+ writeLine(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);
137
+ writeLine();
138
+ }
139
+ if (options.squad) {
140
+ const squadStats = stats.bySquad?.find((s) => s.squad === options.squad);
141
+ const squad = loadSquad(options.squad);
142
+ if (squadStats) {
143
+ writeLine(` ${bold}Squad: ${options.squad}${RESET}`);
144
+ writeLine(` Spent today: ${colors.cyan}$${squadStats.costUsd.toFixed(2)}${RESET}`);
145
+ writeLine(` Calls: ${squadStats.generations.toLocaleString()}`);
146
+ if (squad?.context?.budget) {
147
+ const daily = squad.context.budget.daily;
148
+ const weekly = squad.context.budget.weekly;
149
+ if (daily) {
150
+ const pct = squadStats.costUsd / daily * 100;
151
+ const statusIcon = pct >= 100 ? `${colors.red}\u25CF${RESET}` : pct >= 80 ? `${colors.yellow}\u25CF${RESET}` : `${colors.green}\u2713${RESET}`;
152
+ writeLine(` Daily budget: ${statusIcon} $${squadStats.costUsd.toFixed(2)}/$${daily} (${pct.toFixed(0)}%)`);
153
+ }
154
+ if (weekly) {
155
+ writeLine(` Weekly budget: $${weekly}/week`);
156
+ }
157
+ } else {
158
+ writeLine(` ${colors.dim}No budget defined in SQUAD.md frontmatter${RESET}`);
159
+ }
160
+ } else {
161
+ writeLine(` ${colors.dim}No cost data for squad "${options.squad}"${RESET}`);
162
+ }
163
+ writeLine();
164
+ }
165
+ writeLine(` ${colors.dim}Plan: ${plan.plan} (${plan.reason})${RESET}`);
166
+ writeLine();
167
+ }
168
+ async function budgetCheckCommand(squadName, options = {}) {
169
+ await track(Events.CLI_COST, { action: "budget-check", squad: squadName });
170
+ const stats = await fetchBridgeStats();
171
+ const squad = loadSquad(squadName);
172
+ if (!stats) {
173
+ if (options.json) {
174
+ console.log(JSON.stringify({ error: "Bridge unavailable" }));
175
+ } else {
176
+ writeLine(` ${colors.yellow}\u26A0 Bridge unavailable${RESET}`);
177
+ }
178
+ return;
179
+ }
180
+ const squadStats = stats.bySquad?.find((s) => s.squad === squadName);
181
+ const spent = squadStats?.costUsd || 0;
182
+ const dailyBudget = squad?.context?.budget?.daily || null;
183
+ const weeklyBudget = squad?.context?.budget?.weekly || null;
184
+ const status = getBudgetStatus(squadName, spent, dailyBudget, weeklyBudget);
185
+ if (options.json) {
186
+ console.log(JSON.stringify(status, null, 2));
187
+ return;
188
+ }
189
+ writeLine();
190
+ if (status.status === "no-budget") {
191
+ writeLine(` ${colors.dim}\u25CB${RESET} ${colors.cyan}${squadName}${RESET}: No budget defined`);
192
+ writeLine(` ${colors.dim}Add budget to SQUAD.md frontmatter:${RESET}`);
193
+ writeLine(` ${colors.dim} context:${RESET}`);
194
+ writeLine(` ${colors.dim} budget: { daily: 50 }${RESET}`);
195
+ } else if (status.status === "over") {
196
+ writeLine(` ${colors.red}\u25CF${RESET} ${colors.cyan}${squadName}${RESET}: ${colors.red}OVER BUDGET${RESET}`);
197
+ writeLine(` $${spent.toFixed(2)}/$${dailyBudget} daily (${status.dailyPercent?.toFixed(0)}%)`);
198
+ } else if (status.status === "warning") {
199
+ writeLine(` ${colors.yellow}\u25CF${RESET} ${colors.cyan}${squadName}${RESET}: ${colors.yellow}Approaching limit${RESET}`);
200
+ writeLine(` $${spent.toFixed(2)}/$${dailyBudget} daily (${status.dailyPercent?.toFixed(0)}%)`);
201
+ } else {
202
+ writeLine(` ${colors.green}\u2713${RESET} ${colors.cyan}${squadName}${RESET}: OK to proceed`);
203
+ writeLine(` $${spent.toFixed(2)}/$${dailyBudget} daily (${status.dailyPercent?.toFixed(0)}%)`);
204
+ }
205
+ writeLine();
206
+ }
207
+ function buildJsonOutput(stats, plan, squadFilter) {
208
+ if (!stats) {
209
+ return { error: "Bridge unavailable", plan };
210
+ }
211
+ const squadsDir = findSquadsDir();
212
+ const squadBudgets = {};
213
+ if (squadsDir) {
214
+ for (const name of listSquads(squadsDir)) {
215
+ const squad = loadSquad(name);
216
+ if (squad?.context?.budget) {
217
+ squadBudgets[name] = {
218
+ daily: squad.context.budget.daily || null,
219
+ weekly: squad.context.budget.weekly || null
220
+ };
221
+ }
222
+ }
223
+ }
224
+ const bySquadWithBudget = stats.bySquad?.map((sq) => ({
225
+ ...sq,
226
+ budget: squadBudgets[sq.squad] || null,
227
+ budgetStatus: squadBudgets[sq.squad]?.daily ? getBudgetStatus(sq.squad, sq.costUsd, squadBudgets[sq.squad].daily, squadBudgets[sq.squad].weekly) : null
228
+ }));
229
+ if (squadFilter) {
230
+ const filtered = bySquadWithBudget?.find((s) => s.squad === squadFilter);
231
+ return {
232
+ squad: squadFilter,
233
+ ...filtered,
234
+ plan
235
+ };
236
+ }
237
+ return {
238
+ today: stats.today,
239
+ week: stats.week,
240
+ budget: stats.budget,
241
+ bySquad: bySquadWithBudget,
242
+ byModel: stats.byModel,
243
+ plan,
244
+ source: stats.source
245
+ };
246
+ }
247
+ function shortModelName(model) {
248
+ if (model.includes("opus")) return "opus";
249
+ if (model.includes("sonnet")) return "sonnet";
250
+ if (model.includes("haiku")) return "haiku";
251
+ return model.split("-")[0];
252
+ }
253
+ function formatTokens(tokens) {
254
+ if (tokens >= 1e6) {
255
+ return `${(tokens / 1e6).toFixed(1)}M`;
256
+ }
257
+ if (tokens >= 1e3) {
258
+ return `${(tokens / 1e3).toFixed(0)}k`;
259
+ }
260
+ return tokens.toString();
261
+ }
262
+ function createBudgetBar(percent, width = 10) {
263
+ const filled = Math.min(Math.round(percent / 100 * width), width);
264
+ const empty = width - filled;
265
+ const filledChar = "\u2501";
266
+ const emptyChar = "\u2501";
267
+ const color = percent >= 100 ? colors.red : percent >= 80 ? colors.yellow : colors.green;
268
+ return `${color}${filledChar.repeat(filled)}${colors.dim}${emptyChar.repeat(empty)}${RESET}`;
269
+ }
270
+ export {
271
+ budgetCheckCommand,
272
+ costCommand
273
+ };
274
+ //# sourceMappingURL=cost-N37I4UTA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/cost.ts"],"sourcesContent":["import {\n fetchBridgeStats,\n BridgeStats,\n detectPlan,\n PlanDetection,\n} from '../lib/costs.js';\nimport {\n findSquadsDir,\n loadSquad,\n listSquads,\n} from '../lib/squad-parser.js';\nimport { track, Events } from '../lib/telemetry.js';\nimport {\n colors,\n bold,\n RESET,\n gradient,\n box,\n padEnd,\n writeLine,\n} from '../lib/terminal.js';\n\ninterface CostOptions {\n squad?: string;\n json?: boolean;\n}\n\ninterface BudgetStatus {\n squad: string;\n spent: number;\n dailyBudget: number | null;\n weeklyBudget: number | null;\n dailyPercent: number | null;\n weeklyPercent: number | null;\n status: 'ok' | 'warning' | 'over' | 'no-budget';\n}\n\nfunction getBudgetStatus(\n squadName: string,\n spent: number,\n dailyBudget: number | null,\n weeklyBudget: number | null\n): BudgetStatus {\n let status: BudgetStatus['status'] = 'no-budget';\n let dailyPercent: number | null = null;\n let weeklyPercent: number | null = null;\n\n if (dailyBudget !== null) {\n dailyPercent = (spent / dailyBudget) * 100;\n if (dailyPercent >= 100) {\n status = 'over';\n } else if (dailyPercent >= 80) {\n status = 'warning';\n } else {\n status = 'ok';\n }\n }\n\n if (weeklyBudget !== null) {\n weeklyPercent = (spent / weeklyBudget) * 100;\n }\n\n return {\n squad: squadName,\n spent,\n dailyBudget,\n weeklyBudget,\n dailyPercent,\n weeklyPercent,\n status,\n };\n}\n\nexport async function costCommand(options: CostOptions = {}): Promise<void> {\n await track(Events.CLI_COST, { squad: options.squad || 'all' });\n\n const stats = await fetchBridgeStats();\n const plan = detectPlan();\n\n if (options.json) {\n const result = buildJsonOutput(stats, plan, options.squad);\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n writeLine();\n writeLine(` ${gradient('squads')} ${colors.dim}cost${RESET}${options.squad ? ` ${colors.cyan}${options.squad}${RESET}` : ''}`);\n writeLine();\n\n if (!stats) {\n writeLine(` ${colors.yellow}⚠ Bridge unavailable${RESET}`);\n writeLine(` ${colors.dim}Run \\`squads stack up\\` to start infrastructure${RESET}`);\n writeLine();\n return;\n }\n\n // Today summary\n const todaySection = ` ${bold}Today${RESET}`;\n writeLine(todaySection);\n writeLine(` ${colors.cyan}$${stats.today.costUsd.toFixed(2)}${RESET} ${colors.dim}|${RESET} ${stats.today.generations.toLocaleString()} calls ${colors.dim}|${RESET} ${formatTokens(stats.today.inputTokens + stats.today.outputTokens)} tokens`);\n\n // Model breakdown for today\n if (stats.byModel && stats.byModel.length > 0) {\n const modelParts = stats.byModel\n .filter(m => m.costUsd > 0)\n .sort((a, b) => b.costUsd - a.costUsd)\n .slice(0, 4)\n .map(m => `${colors.dim}${shortModelName(m.model)}${RESET} $${m.costUsd.toFixed(0)}`);\n if (modelParts.length > 0) {\n writeLine(` ${colors.dim}Models:${RESET} ${modelParts.join(' · ')}`);\n }\n }\n\n writeLine();\n\n // Week summary (if available)\n if (stats.week) {\n writeLine(` ${bold}Week${RESET}`);\n writeLine(` ${colors.purple}$${stats.week.costUsd.toFixed(2)}${RESET} ${colors.dim}|${RESET} ${stats.week.generations.toLocaleString()} calls ${colors.dim}|${RESET} ${formatTokens(stats.week.inputTokens + stats.week.outputTokens)} tokens`);\n writeLine();\n }\n\n // Budget status\n writeLine(` ${bold}Budget${RESET} ${colors.dim}(daily)${RESET}`);\n const budgetBar = createBudgetBar(stats.budget.usedPct);\n const budgetColor = stats.budget.usedPct >= 100 ? colors.red :\n stats.budget.usedPct >= 80 ? colors.yellow : colors.green;\n writeLine(` ${budgetBar} ${budgetColor}$${stats.budget.used.toFixed(0)}${RESET}/${colors.dim}$${stats.budget.daily}${RESET} (${stats.budget.usedPct.toFixed(0)}%)`);\n writeLine();\n\n // Per-squad breakdown with budget comparison\n if (stats.bySquad && stats.bySquad.length > 0 && !options.squad) {\n writeLine(` ${bold}By Squad${RESET}`);\n writeLine();\n\n const squadsDir = findSquadsDir();\n const squadBudgets = new Map<string, { daily: number | null; weekly: number | null }>();\n\n if (squadsDir) {\n for (const name of listSquads(squadsDir)) {\n const squad = loadSquad(name);\n if (squad?.context?.budget) {\n squadBudgets.set(name, {\n daily: squad.context.budget.daily || null,\n weekly: squad.context.budget.weekly || null,\n });\n }\n }\n }\n\n const tableWidth = 52;\n writeLine(` ${colors.purple}${box.topLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.topRight}${RESET}`);\n\n const w = { name: 14, spent: 10, budget: 12, status: 14 };\n const header = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${bold}${padEnd('SQUAD', w.name)}${RESET}` +\n `${bold}${padEnd('SPENT', w.spent)}${RESET}` +\n `${bold}${padEnd('BUDGET', w.budget)}${RESET}` +\n `${bold}STATUS${RESET}` +\n ` ${colors.purple}${box.vertical}${RESET}`;\n writeLine(header);\n\n writeLine(` ${colors.purple}${box.teeRight}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.teeLeft}${RESET}`);\n\n for (const sq of stats.bySquad.sort((a, b) => b.costUsd - a.costUsd)) {\n const budgetInfo = squadBudgets.get(sq.squad);\n const dailyBudget = budgetInfo?.daily || null;\n\n const spentStr = `$${sq.costUsd.toFixed(2)}`;\n const budgetStr = dailyBudget ? `$${dailyBudget}/d` : `${colors.dim}—${RESET}`;\n\n let statusStr: string;\n if (dailyBudget) {\n const pct = (sq.costUsd / dailyBudget) * 100;\n if (pct >= 100) {\n statusStr = `${colors.red}● OVER${RESET}`;\n } else if (pct >= 80) {\n statusStr = `${colors.yellow}● ${pct.toFixed(0)}%${RESET}`;\n } else {\n statusStr = `${colors.green}✓ ${pct.toFixed(0)}%${RESET}`;\n }\n } else {\n statusStr = `${colors.dim}no budget${RESET}`;\n }\n\n const row = ` ${colors.purple}${box.vertical}${RESET} ` +\n `${colors.cyan}${padEnd(sq.squad, w.name)}${RESET}` +\n `${padEnd(spentStr, w.spent)}` +\n `${padEnd(budgetStr, w.budget)}` +\n `${padEnd(statusStr, w.status)}` +\n `${colors.purple}${box.vertical}${RESET}`;\n\n writeLine(row);\n }\n\n writeLine(` ${colors.purple}${box.bottomLeft}${colors.dim}${box.horizontal.repeat(tableWidth)}${colors.purple}${box.bottomRight}${RESET}`);\n writeLine();\n }\n\n // Single squad detail\n if (options.squad) {\n const squadStats = stats.bySquad?.find(s => s.squad === options.squad);\n const squad = loadSquad(options.squad);\n\n if (squadStats) {\n writeLine(` ${bold}Squad: ${options.squad}${RESET}`);\n writeLine(` Spent today: ${colors.cyan}$${squadStats.costUsd.toFixed(2)}${RESET}`);\n writeLine(` Calls: ${squadStats.generations.toLocaleString()}`);\n\n if (squad?.context?.budget) {\n const daily = squad.context.budget.daily;\n const weekly = squad.context.budget.weekly;\n\n if (daily) {\n const pct = (squadStats.costUsd / daily) * 100;\n const statusIcon = pct >= 100 ? `${colors.red}●${RESET}` :\n pct >= 80 ? `${colors.yellow}●${RESET}` : `${colors.green}✓${RESET}`;\n writeLine(` Daily budget: ${statusIcon} $${squadStats.costUsd.toFixed(2)}/$${daily} (${pct.toFixed(0)}%)`);\n }\n\n if (weekly) {\n writeLine(` Weekly budget: $${weekly}/week`);\n }\n } else {\n writeLine(` ${colors.dim}No budget defined in SQUAD.md frontmatter${RESET}`);\n }\n } else {\n writeLine(` ${colors.dim}No cost data for squad \"${options.squad}\"${RESET}`);\n }\n writeLine();\n }\n\n // Plan info\n writeLine(` ${colors.dim}Plan: ${plan.plan} (${plan.reason})${RESET}`);\n writeLine();\n}\n\nexport async function budgetCheckCommand(\n squadName: string,\n options: { json?: boolean } = {}\n): Promise<void> {\n await track(Events.CLI_COST, { action: 'budget-check', squad: squadName });\n\n const stats = await fetchBridgeStats();\n const squad = loadSquad(squadName);\n\n if (!stats) {\n if (options.json) {\n console.log(JSON.stringify({ error: 'Bridge unavailable' }));\n } else {\n writeLine(` ${colors.yellow}⚠ Bridge unavailable${RESET}`);\n }\n return;\n }\n\n const squadStats = stats.bySquad?.find(s => s.squad === squadName);\n const spent = squadStats?.costUsd || 0;\n const dailyBudget = squad?.context?.budget?.daily || null;\n const weeklyBudget = squad?.context?.budget?.weekly || null;\n\n const status = getBudgetStatus(squadName, spent, dailyBudget, weeklyBudget);\n\n if (options.json) {\n console.log(JSON.stringify(status, null, 2));\n return;\n }\n\n writeLine();\n\n if (status.status === 'no-budget') {\n writeLine(` ${colors.dim}○${RESET} ${colors.cyan}${squadName}${RESET}: No budget defined`);\n writeLine(` ${colors.dim}Add budget to SQUAD.md frontmatter:${RESET}`);\n writeLine(` ${colors.dim} context:${RESET}`);\n writeLine(` ${colors.dim} budget: { daily: 50 }${RESET}`);\n } else if (status.status === 'over') {\n writeLine(` ${colors.red}●${RESET} ${colors.cyan}${squadName}${RESET}: ${colors.red}OVER BUDGET${RESET}`);\n writeLine(` $${spent.toFixed(2)}/$${dailyBudget} daily (${status.dailyPercent?.toFixed(0)}%)`);\n } else if (status.status === 'warning') {\n writeLine(` ${colors.yellow}●${RESET} ${colors.cyan}${squadName}${RESET}: ${colors.yellow}Approaching limit${RESET}`);\n writeLine(` $${spent.toFixed(2)}/$${dailyBudget} daily (${status.dailyPercent?.toFixed(0)}%)`);\n } else {\n writeLine(` ${colors.green}✓${RESET} ${colors.cyan}${squadName}${RESET}: OK to proceed`);\n writeLine(` $${spent.toFixed(2)}/$${dailyBudget} daily (${status.dailyPercent?.toFixed(0)}%)`);\n }\n\n writeLine();\n}\n\nfunction buildJsonOutput(\n stats: BridgeStats | null,\n plan: PlanDetection,\n squadFilter?: string\n): object {\n if (!stats) {\n return { error: 'Bridge unavailable', plan };\n }\n\n const squadsDir = findSquadsDir();\n const squadBudgets: Record<string, { daily: number | null; weekly: number | null }> = {};\n\n if (squadsDir) {\n for (const name of listSquads(squadsDir)) {\n const squad = loadSquad(name);\n if (squad?.context?.budget) {\n squadBudgets[name] = {\n daily: squad.context.budget.daily || null,\n weekly: squad.context.budget.weekly || null,\n };\n }\n }\n }\n\n const bySquadWithBudget = stats.bySquad?.map(sq => ({\n ...sq,\n budget: squadBudgets[sq.squad] || null,\n budgetStatus: squadBudgets[sq.squad]?.daily\n ? getBudgetStatus(sq.squad, sq.costUsd, squadBudgets[sq.squad].daily, squadBudgets[sq.squad].weekly)\n : null,\n }));\n\n if (squadFilter) {\n const filtered = bySquadWithBudget?.find(s => s.squad === squadFilter);\n return {\n squad: squadFilter,\n ...filtered,\n plan,\n };\n }\n\n return {\n today: stats.today,\n week: stats.week,\n budget: stats.budget,\n bySquad: bySquadWithBudget,\n byModel: stats.byModel,\n plan,\n source: stats.source,\n };\n}\n\nfunction shortModelName(model: string): string {\n if (model.includes('opus')) return 'opus';\n if (model.includes('sonnet')) return 'sonnet';\n if (model.includes('haiku')) return 'haiku';\n return model.split('-')[0];\n}\n\nfunction formatTokens(tokens: number): string {\n if (tokens >= 1_000_000) {\n return `${(tokens / 1_000_000).toFixed(1)}M`;\n }\n if (tokens >= 1_000) {\n return `${(tokens / 1_000).toFixed(0)}k`;\n }\n return tokens.toString();\n}\n\nfunction createBudgetBar(percent: number, width = 10): string {\n const filled = Math.min(Math.round((percent / 100) * width), width);\n const empty = width - filled;\n\n const filledChar = '━';\n const emptyChar = '━';\n\n const color = percent >= 100 ? colors.red :\n percent >= 80 ? colors.yellow : colors.green;\n\n return `${color}${filledChar.repeat(filled)}${colors.dim}${emptyChar.repeat(empty)}${RESET}`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,SAAS,gBACP,WACA,OACA,aACA,cACc;AACd,MAAI,SAAiC;AACrC,MAAI,eAA8B;AAClC,MAAI,gBAA+B;AAEnC,MAAI,gBAAgB,MAAM;AACxB,mBAAgB,QAAQ,cAAe;AACvC,QAAI,gBAAgB,KAAK;AACvB,eAAS;AAAA,IACX,WAAW,gBAAgB,IAAI;AAC7B,eAAS;AAAA,IACX,OAAO;AACL,eAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,iBAAiB,MAAM;AACzB,oBAAiB,QAAQ,eAAgB;AAAA,EAC3C;AAEA,SAAO;AAAA,IACL,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,YAAY,UAAuB,CAAC,GAAkB;AAC1E,QAAM,MAAM,OAAO,UAAU,EAAE,OAAO,QAAQ,SAAS,MAAM,CAAC;AAE9D,QAAM,QAAQ,MAAM,iBAAiB;AACrC,QAAM,OAAO,WAAW;AAExB,MAAI,QAAQ,MAAM;AAChB,UAAM,SAAS,gBAAgB,OAAO,MAAM,QAAQ,KAAK;AACzD,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C;AAAA,EACF;AAEA,YAAU;AACV,YAAU,KAAK,SAAS,QAAQ,CAAC,IAAI,OAAO,GAAG,OAAO,KAAK,GAAG,QAAQ,QAAQ,IAAI,OAAO,IAAI,GAAG,QAAQ,KAAK,GAAG,KAAK,KAAK,EAAE,EAAE;AAC9H,YAAU;AAEV,MAAI,CAAC,OAAO;AACV,cAAU,KAAK,OAAO,MAAM,4BAAuB,KAAK,EAAE;AAC1D,cAAU,KAAK,OAAO,GAAG,kDAAkD,KAAK,EAAE;AAClF,cAAU;AACV;AAAA,EACF;AAGA,QAAM,eAAe,KAAK,IAAI,QAAQ,KAAK;AAC3C,YAAU,YAAY;AACtB,YAAU,KAAK,OAAO,IAAI,IAAI,MAAM,MAAM,QAAQ,QAAQ,CAAC,CAAC,GAAG,KAAK,IAAI,OAAO,GAAG,IAAI,KAAK,IAAI,MAAM,MAAM,YAAY,eAAe,CAAC,UAAU,OAAO,GAAG,IAAI,KAAK,IAAI,aAAa,MAAM,MAAM,cAAc,MAAM,MAAM,YAAY,CAAC,SAAS;AAGjP,MAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG;AAC7C,UAAM,aAAa,MAAM,QACtB,OAAO,OAAK,EAAE,UAAU,CAAC,EACzB,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,EACpC,MAAM,GAAG,CAAC,EACV,IAAI,OAAK,GAAG,OAAO,GAAG,GAAG,eAAe,EAAE,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,QAAQ,QAAQ,CAAC,CAAC,EAAE;AACtF,QAAI,WAAW,SAAS,GAAG;AACzB,gBAAU,KAAK,OAAO,GAAG,UAAU,KAAK,IAAI,WAAW,KAAK,QAAK,CAAC,EAAE;AAAA,IACtE;AAAA,EACF;AAEA,YAAU;AAGV,MAAI,MAAM,MAAM;AACd,cAAU,KAAK,IAAI,OAAO,KAAK,EAAE;AACjC,cAAU,KAAK,OAAO,MAAM,IAAI,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,GAAG,KAAK,IAAI,OAAO,GAAG,IAAI,KAAK,IAAI,MAAM,KAAK,YAAY,eAAe,CAAC,UAAU,OAAO,GAAG,IAAI,KAAK,IAAI,aAAa,MAAM,KAAK,cAAc,MAAM,KAAK,YAAY,CAAC,SAAS;AAC/O,cAAU;AAAA,EACZ;AAGA,YAAU,KAAK,IAAI,SAAS,KAAK,IAAI,OAAO,GAAG,UAAU,KAAK,EAAE;AAChE,QAAM,YAAY,gBAAgB,MAAM,OAAO,OAAO;AACtD,QAAM,cAAc,MAAM,OAAO,WAAW,MAAM,OAAO,MACrC,MAAM,OAAO,WAAW,KAAK,OAAO,SAAS,OAAO;AACxE,YAAU,KAAK,SAAS,IAAI,WAAW,IAAI,MAAM,OAAO,KAAK,QAAQ,CAAC,CAAC,GAAG,KAAK,IAAI,OAAO,GAAG,IAAI,MAAM,OAAO,KAAK,GAAG,KAAK,KAAK,MAAM,OAAO,QAAQ,QAAQ,CAAC,CAAC,IAAI;AACnK,YAAU;AAGV,MAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,KAAK,CAAC,QAAQ,OAAO;AAC/D,cAAU,KAAK,IAAI,WAAW,KAAK,EAAE;AACrC,cAAU;AAEV,UAAM,YAAY,cAAc;AAChC,UAAM,eAAe,oBAAI,IAA6D;AAEtF,QAAI,WAAW;AACb,iBAAW,QAAQ,WAAW,SAAS,GAAG;AACxC,cAAM,QAAQ,UAAU,IAAI;AAC5B,YAAI,OAAO,SAAS,QAAQ;AAC1B,uBAAa,IAAI,MAAM;AAAA,YACrB,OAAO,MAAM,QAAQ,OAAO,SAAS;AAAA,YACrC,QAAQ,MAAM,QAAQ,OAAO,UAAU;AAAA,UACzC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,UAAM,aAAa;AACnB,cAAU,KAAK,OAAO,MAAM,GAAG,IAAI,OAAO,GAAG,OAAO,GAAG,GAAG,IAAI,WAAW,OAAO,UAAU,CAAC,GAAG,OAAO,MAAM,GAAG,IAAI,QAAQ,GAAG,KAAK,EAAE;AAEpI,UAAM,IAAI,EAAE,MAAM,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,GAAG;AACxD,UAAM,SAAS,KAAK,OAAO,MAAM,GAAG,IAAI,QAAQ,GAAG,KAAK,IACnD,IAAI,GAAG,OAAO,SAAS,EAAE,IAAI,CAAC,GAAG,KAAK,GACtC,IAAI,GAAG,OAAO,SAAS,EAAE,KAAK,CAAC,GAAG,KAAK,GACvC,IAAI,GAAG,OAAO,UAAU,EAAE,MAAM,CAAC,GAAG,KAAK,GACzC,IAAI,SAAS,KAAK,IACjB,OAAO,MAAM,GAAG,IAAI,QAAQ,GAAG,KAAK;AAC1C,cAAU,MAAM;AAEhB,cAAU,KAAK,OAAO,MAAM,GAAG,IAAI,QAAQ,GAAG,OAAO,GAAG,GAAG,IAAI,WAAW,OAAO,UAAU,CAAC,GAAG,OAAO,MAAM,GAAG,IAAI,OAAO,GAAG,KAAK,EAAE;AAEpI,eAAW,MAAM,MAAM,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG;AACpE,YAAM,aAAa,aAAa,IAAI,GAAG,KAAK;AAC5C,YAAM,cAAc,YAAY,SAAS;AAEzC,YAAM,WAAW,IAAI,GAAG,QAAQ,QAAQ,CAAC,CAAC;AAC1C,YAAM,YAAY,cAAc,IAAI,WAAW,OAAO,GAAG,OAAO,GAAG,SAAI,KAAK;AAE5E,UAAI;AACJ,UAAI,aAAa;AACf,cAAM,MAAO,GAAG,UAAU,cAAe;AACzC,YAAI,OAAO,KAAK;AACd,sBAAY,GAAG,OAAO,GAAG,cAAS,KAAK;AAAA,QACzC,WAAW,OAAO,IAAI;AACpB,sBAAY,GAAG,OAAO,MAAM,UAAK,IAAI,QAAQ,CAAC,CAAC,IAAI,KAAK;AAAA,QAC1D,OAAO;AACL,sBAAY,GAAG,OAAO,KAAK,UAAK,IAAI,QAAQ,CAAC,CAAC,IAAI,KAAK;AAAA,QACzD;AAAA,MACF,OAAO;AACL,oBAAY,GAAG,OAAO,GAAG,YAAY,KAAK;AAAA,MAC5C;AAEA,YAAM,MAAM,KAAK,OAAO,MAAM,GAAG,IAAI,QAAQ,GAAG,KAAK,IAChD,OAAO,IAAI,GAAG,OAAO,GAAG,OAAO,EAAE,IAAI,CAAC,GAAG,KAAK,GAC9C,OAAO,UAAU,EAAE,KAAK,CAAC,GACzB,OAAO,WAAW,EAAE,MAAM,CAAC,GAC3B,OAAO,WAAW,EAAE,MAAM,CAAC,GAC3B,OAAO,MAAM,GAAG,IAAI,QAAQ,GAAG,KAAK;AAEzC,gBAAU,GAAG;AAAA,IACf;AAEA,cAAU,KAAK,OAAO,MAAM,GAAG,IAAI,UAAU,GAAG,OAAO,GAAG,GAAG,IAAI,WAAW,OAAO,UAAU,CAAC,GAAG,OAAO,MAAM,GAAG,IAAI,WAAW,GAAG,KAAK,EAAE;AAC1I,cAAU;AAAA,EACZ;AAGA,MAAI,QAAQ,OAAO;AACjB,UAAM,aAAa,MAAM,SAAS,KAAK,OAAK,EAAE,UAAU,QAAQ,KAAK;AACrE,UAAM,QAAQ,UAAU,QAAQ,KAAK;AAErC,QAAI,YAAY;AACd,gBAAU,KAAK,IAAI,UAAU,QAAQ,KAAK,GAAG,KAAK,EAAE;AACpD,gBAAU,kBAAkB,OAAO,IAAI,IAAI,WAAW,QAAQ,QAAQ,CAAC,CAAC,GAAG,KAAK,EAAE;AAClF,gBAAU,YAAY,WAAW,YAAY,eAAe,CAAC,EAAE;AAE/D,UAAI,OAAO,SAAS,QAAQ;AAC1B,cAAM,QAAQ,MAAM,QAAQ,OAAO;AACnC,cAAM,SAAS,MAAM,QAAQ,OAAO;AAEpC,YAAI,OAAO;AACT,gBAAM,MAAO,WAAW,UAAU,QAAS;AAC3C,gBAAM,aAAa,OAAO,MAAM,GAAG,OAAO,GAAG,SAAI,KAAK,KACpC,OAAO,KAAK,GAAG,OAAO,MAAM,SAAI,KAAK,KAAK,GAAG,OAAO,KAAK,SAAI,KAAK;AACpF,oBAAU,mBAAmB,UAAU,KAAK,WAAW,QAAQ,QAAQ,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,QAAQ,CAAC,CAAC,IAAI;AAAA,QAC5G;AAEA,YAAI,QAAQ;AACV,oBAAU,qBAAqB,MAAM,OAAO;AAAA,QAC9C;AAAA,MACF,OAAO;AACL,kBAAU,KAAK,OAAO,GAAG,4CAA4C,KAAK,EAAE;AAAA,MAC9E;AAAA,IACF,OAAO;AACL,gBAAU,KAAK,OAAO,GAAG,2BAA2B,QAAQ,KAAK,IAAI,KAAK,EAAE;AAAA,IAC9E;AACA,cAAU;AAAA,EACZ;AAGA,YAAU,KAAK,OAAO,GAAG,SAAS,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,EAAE;AACtE,YAAU;AACZ;AAEA,eAAsB,mBACpB,WACA,UAA8B,CAAC,GAChB;AACf,QAAM,MAAM,OAAO,UAAU,EAAE,QAAQ,gBAAgB,OAAO,UAAU,CAAC;AAEzE,QAAM,QAAQ,MAAM,iBAAiB;AACrC,QAAM,QAAQ,UAAU,SAAS;AAEjC,MAAI,CAAC,OAAO;AACV,QAAI,QAAQ,MAAM;AAChB,cAAQ,IAAI,KAAK,UAAU,EAAE,OAAO,qBAAqB,CAAC,CAAC;AAAA,IAC7D,OAAO;AACL,gBAAU,KAAK,OAAO,MAAM,4BAAuB,KAAK,EAAE;AAAA,IAC5D;AACA;AAAA,EACF;AAEA,QAAM,aAAa,MAAM,SAAS,KAAK,OAAK,EAAE,UAAU,SAAS;AACjE,QAAM,QAAQ,YAAY,WAAW;AACrC,QAAM,cAAc,OAAO,SAAS,QAAQ,SAAS;AACrD,QAAM,eAAe,OAAO,SAAS,QAAQ,UAAU;AAEvD,QAAM,SAAS,gBAAgB,WAAW,OAAO,aAAa,YAAY;AAE1E,MAAI,QAAQ,MAAM;AAChB,YAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC3C;AAAA,EACF;AAEA,YAAU;AAEV,MAAI,OAAO,WAAW,aAAa;AACjC,cAAU,KAAK,OAAO,GAAG,SAAI,KAAK,IAAI,OAAO,IAAI,GAAG,SAAS,GAAG,KAAK,qBAAqB;AAC1F,cAAU,KAAK,OAAO,GAAG,sCAAsC,KAAK,EAAE;AACtE,cAAU,KAAK,OAAO,GAAG,aAAa,KAAK,EAAE;AAC7C,cAAU,KAAK,OAAO,GAAG,4BAA4B,KAAK,EAAE;AAAA,EAC9D,WAAW,OAAO,WAAW,QAAQ;AACnC,cAAU,KAAK,OAAO,GAAG,SAAI,KAAK,IAAI,OAAO,IAAI,GAAG,SAAS,GAAG,KAAK,KAAK,OAAO,GAAG,cAAc,KAAK,EAAE;AACzG,cAAU,MAAM,MAAM,QAAQ,CAAC,CAAC,KAAK,WAAW,WAAW,OAAO,cAAc,QAAQ,CAAC,CAAC,IAAI;AAAA,EAChG,WAAW,OAAO,WAAW,WAAW;AACtC,cAAU,KAAK,OAAO,MAAM,SAAI,KAAK,IAAI,OAAO,IAAI,GAAG,SAAS,GAAG,KAAK,KAAK,OAAO,MAAM,oBAAoB,KAAK,EAAE;AACrH,cAAU,MAAM,MAAM,QAAQ,CAAC,CAAC,KAAK,WAAW,WAAW,OAAO,cAAc,QAAQ,CAAC,CAAC,IAAI;AAAA,EAChG,OAAO;AACL,cAAU,KAAK,OAAO,KAAK,SAAI,KAAK,IAAI,OAAO,IAAI,GAAG,SAAS,GAAG,KAAK,iBAAiB;AACxF,cAAU,MAAM,MAAM,QAAQ,CAAC,CAAC,KAAK,WAAW,WAAW,OAAO,cAAc,QAAQ,CAAC,CAAC,IAAI;AAAA,EAChG;AAEA,YAAU;AACZ;AAEA,SAAS,gBACP,OACA,MACA,aACQ;AACR,MAAI,CAAC,OAAO;AACV,WAAO,EAAE,OAAO,sBAAsB,KAAK;AAAA,EAC7C;AAEA,QAAM,YAAY,cAAc;AAChC,QAAM,eAAgF,CAAC;AAEvF,MAAI,WAAW;AACb,eAAW,QAAQ,WAAW,SAAS,GAAG;AACxC,YAAM,QAAQ,UAAU,IAAI;AAC5B,UAAI,OAAO,SAAS,QAAQ;AAC1B,qBAAa,IAAI,IAAI;AAAA,UACnB,OAAO,MAAM,QAAQ,OAAO,SAAS;AAAA,UACrC,QAAQ,MAAM,QAAQ,OAAO,UAAU;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,oBAAoB,MAAM,SAAS,IAAI,SAAO;AAAA,IAClD,GAAG;AAAA,IACH,QAAQ,aAAa,GAAG,KAAK,KAAK;AAAA,IAClC,cAAc,aAAa,GAAG,KAAK,GAAG,QAClC,gBAAgB,GAAG,OAAO,GAAG,SAAS,aAAa,GAAG,KAAK,EAAE,OAAO,aAAa,GAAG,KAAK,EAAE,MAAM,IACjG;AAAA,EACN,EAAE;AAEF,MAAI,aAAa;AACf,UAAM,WAAW,mBAAmB,KAAK,OAAK,EAAE,UAAU,WAAW;AACrE,WAAO;AAAA,MACL,OAAO;AAAA,MACP,GAAG;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,MAAM;AAAA,IACb,MAAM,MAAM;AAAA,IACZ,QAAQ,MAAM;AAAA,IACd,SAAS;AAAA,IACT,SAAS,MAAM;AAAA,IACf;AAAA,IACA,QAAQ,MAAM;AAAA,EAChB;AACF;AAEA,SAAS,eAAe,OAAuB;AAC7C,MAAI,MAAM,SAAS,MAAM,EAAG,QAAO;AACnC,MAAI,MAAM,SAAS,QAAQ,EAAG,QAAO;AACrC,MAAI,MAAM,SAAS,OAAO,EAAG,QAAO;AACpC,SAAO,MAAM,MAAM,GAAG,EAAE,CAAC;AAC3B;AAEA,SAAS,aAAa,QAAwB;AAC5C,MAAI,UAAU,KAAW;AACvB,WAAO,IAAI,SAAS,KAAW,QAAQ,CAAC,CAAC;AAAA,EAC3C;AACA,MAAI,UAAU,KAAO;AACnB,WAAO,IAAI,SAAS,KAAO,QAAQ,CAAC,CAAC;AAAA,EACvC;AACA,SAAO,OAAO,SAAS;AACzB;AAEA,SAAS,gBAAgB,SAAiB,QAAQ,IAAY;AAC5D,QAAM,SAAS,KAAK,IAAI,KAAK,MAAO,UAAU,MAAO,KAAK,GAAG,KAAK;AAClE,QAAM,QAAQ,QAAQ;AAEtB,QAAM,aAAa;AACnB,QAAM,YAAY;AAElB,QAAM,QAAQ,WAAW,MAAM,OAAO,MACxB,WAAW,KAAK,OAAO,SAAS,OAAO;AAErD,SAAO,GAAG,KAAK,GAAG,WAAW,OAAO,MAAM,CAAC,GAAG,OAAO,GAAG,GAAG,UAAU,OAAO,KAAK,CAAC,GAAG,KAAK;AAC5F;","names":[]}
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadTemplate,
4
+ toKebabCase,
5
+ toTitleCase
6
+ } from "./chunk-FFFCFZ6A.js";
7
+ import {
8
+ createGitHubRepo,
9
+ detectGitHubOrg
10
+ } from "./chunk-FIWT2NMM.js";
11
+ import {
12
+ track
13
+ } from "./chunk-L6GQCHDF.js";
14
+ import {
15
+ findProjectRoot,
16
+ findSquadsDir,
17
+ listSquads
18
+ } from "./chunk-LDM62TIX.js";
19
+ import "./chunk-7OCVIDC7.js";
20
+
21
+ // src/commands/create.ts
22
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
23
+ import { join } from "path";
24
+ import chalk from "chalk";
25
+
26
+ // src/lib/slack.ts
27
+ var SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN;
28
+ async function slackApi(method, endpoint, body) {
29
+ if (!SLACK_BOT_TOKEN) {
30
+ throw new Error("SLACK_BOT_TOKEN not set in environment");
31
+ }
32
+ const url = `https://slack.com/api/${endpoint}`;
33
+ const options = {
34
+ method,
35
+ headers: {
36
+ Authorization: `Bearer ${SLACK_BOT_TOKEN}`,
37
+ "Content-Type": "application/json"
38
+ }
39
+ };
40
+ if (body) {
41
+ options.body = JSON.stringify(body);
42
+ }
43
+ const res = await fetch(url, options);
44
+ const data = await res.json();
45
+ if (!data.ok) {
46
+ throw new Error(`Slack API error: ${data.error}`);
47
+ }
48
+ return data;
49
+ }
50
+ function isSlackConfigured() {
51
+ return !!SLACK_BOT_TOKEN;
52
+ }
53
+ async function getSquadChannelId(squad) {
54
+ const channelName = `squad-${squad}`;
55
+ try {
56
+ const response = await slackApi(
57
+ "GET",
58
+ "conversations.list?types=public_channel&limit=200"
59
+ );
60
+ const channel = response.channels?.find((c) => c.name === channelName);
61
+ return channel?.id || null;
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+ async function createSquadChannel(squadId, topic) {
67
+ if (!isSlackConfigured()) return null;
68
+ const channelName = `squad-${squadId}`;
69
+ try {
70
+ const response = await slackApi("POST", "conversations.create", {
71
+ name: channelName,
72
+ is_private: false
73
+ });
74
+ const channelId = response.channel?.id || null;
75
+ if (channelId && topic) {
76
+ await slackApi("POST", "conversations.setTopic", {
77
+ channel: channelId,
78
+ topic
79
+ }).catch(() => {
80
+ });
81
+ }
82
+ return channelId;
83
+ } catch (err) {
84
+ const message = err instanceof Error ? err.message : String(err);
85
+ if (message.includes("name_taken")) {
86
+ return getSquadChannelId(squadId);
87
+ }
88
+ return null;
89
+ }
90
+ }
91
+
92
+ // src/commands/create.ts
93
+ async function createCommand(name, options) {
94
+ const squadId = toKebabCase(name);
95
+ const squadName = toTitleCase(squadId);
96
+ if (!squadId) {
97
+ console.error(chalk.red("\n Invalid squad name. Use letters, numbers, and hyphens.\n"));
98
+ process.exit(1);
99
+ }
100
+ const projectRoot = findProjectRoot();
101
+ if (!projectRoot) {
102
+ console.error(chalk.red("\n Not in a squads project. Run `squads init` first.\n"));
103
+ process.exit(1);
104
+ }
105
+ const squadsDir = findSquadsDir();
106
+ if (!squadsDir) {
107
+ console.error(chalk.red("\n No .agents/squads directory found. Run `squads init` first.\n"));
108
+ process.exit(1);
109
+ }
110
+ const squadDir = join(squadsDir, squadId);
111
+ if (existsSync(join(squadDir, "SQUAD.md")) && !options.force) {
112
+ console.error(chalk.red(`
113
+ Squad "${squadId}" already exists. Use --force to overwrite.
114
+ `));
115
+ process.exit(1);
116
+ }
117
+ let description = options.description;
118
+ let goal = options.goal;
119
+ if (!options.yes && (!description || !goal)) {
120
+ const inquirer = await import("inquirer");
121
+ if (!description) {
122
+ const answer = await inquirer.default.prompt([{
123
+ type: "input",
124
+ name: "description",
125
+ message: "Squad mission (one sentence):",
126
+ default: `The ${squadName} squad handles ${squadId}-related tasks.`
127
+ }]);
128
+ description = answer.description;
129
+ }
130
+ if (!goal) {
131
+ const answer = await inquirer.default.prompt([{
132
+ type: "input",
133
+ name: "goal",
134
+ message: "First goal:",
135
+ default: `Define ${squadId} squad objectives and deliver first results`
136
+ }]);
137
+ goal = answer.goal;
138
+ }
139
+ }
140
+ description = description || `The ${squadName} squad handles ${squadId}-related tasks.`;
141
+ goal = goal || `Define ${squadId} squad objectives and deliver first results`;
142
+ const model = options.model || "sonnet";
143
+ const vars = {
144
+ SQUAD_NAME: squadName,
145
+ SQUAD_ID: squadId,
146
+ SQUAD_DESCRIPTION: description,
147
+ GOAL: goal
148
+ };
149
+ mkdirSync(squadDir, { recursive: true });
150
+ let squadContent;
151
+ try {
152
+ squadContent = loadTemplate("first-squad/SQUAD.md.template", vars);
153
+ } catch {
154
+ squadContent = `# ${squadName}
155
+
156
+ ${description}
157
+
158
+ ## Goals
159
+
160
+ - [ ] ${goal}
161
+
162
+ ## Agents
163
+
164
+ | Agent | Purpose |
165
+ |-------|--------|
166
+ | lead | Orchestrates the squad and coordinates work |
167
+
168
+ ## Pipeline
169
+
170
+ \`lead\` (orchestrates all work)
171
+
172
+ ## Usage
173
+
174
+ \`\`\`bash
175
+ squads run ${squadId} --execute
176
+ \`\`\`
177
+ `;
178
+ }
179
+ writeFileSync(join(squadDir, "SQUAD.md"), squadContent);
180
+ let leadContent;
181
+ try {
182
+ leadContent = loadTemplate("first-squad/lead.md.template", vars);
183
+ } catch {
184
+ leadContent = `# Lead Agent
185
+
186
+ ## Purpose
187
+ Orchestrate the ${squadName} squad to achieve its goals.
188
+
189
+ ## Model
190
+ ${model}
191
+
192
+ ## Tools
193
+ - Read
194
+ - Write
195
+ - Edit
196
+ - Bash
197
+ - WebSearch
198
+ - WebFetch
199
+ - Task
200
+
201
+ ## Instructions
202
+
203
+ You are the lead agent for the ${squadName} squad.
204
+
205
+ **Goal**: ${goal}
206
+
207
+ ### Approach
208
+
209
+ 1. **Understand the goal** - Break down what needs to be accomplished
210
+ 2. **Plan the work** - Create a clear execution plan
211
+ 3. **Execute** - Work through the plan step by step
212
+ 4. **Verify** - Confirm the goal is achieved
213
+ 5. **Document** - Update memory with learnings
214
+
215
+ ## Output
216
+ Progress updates and work artifacts as appropriate.
217
+
218
+ ## Labels
219
+ - lead
220
+ - orchestration
221
+ `;
222
+ }
223
+ writeFileSync(join(squadDir, "lead.md"), leadContent);
224
+ const memoryDir = join(projectRoot, ".agents", "memory", squadId, "lead");
225
+ mkdirSync(memoryDir, { recursive: true });
226
+ await track("cli.create", {
227
+ squad: squadId,
228
+ hasDescription: !!options.description,
229
+ hasGoal: !!options.goal,
230
+ force: !!options.force,
231
+ repo: !!options.repo
232
+ }).catch(() => {
233
+ });
234
+ let slackChannelId = null;
235
+ if (options.slack) {
236
+ slackChannelId = await createSquadChannel(squadId, `Channel for the ${squadName} squad`);
237
+ if (!slackChannelId) {
238
+ console.error(chalk.red("\n Slack channel creation failed (check SLACK_BOT_TOKEN).\n"));
239
+ console.error(chalk.dim(" Local squad was created. Create the channel manually.\n"));
240
+ }
241
+ }
242
+ let repoUrl;
243
+ if (options.repo) {
244
+ const org = options.org ?? detectGitHubOrg();
245
+ try {
246
+ const result = createGitHubRepo(squadId, { org, description });
247
+ repoUrl = result.url;
248
+ } catch (err) {
249
+ const message = err instanceof Error ? err.message : String(err);
250
+ console.error(chalk.red(`
251
+ GitHub repo creation failed: ${message}
252
+ `));
253
+ console.error(chalk.dim(" Local squad was created. Create the repo manually with:"));
254
+ console.error(chalk.dim(` gh repo create ${org ? `${org}/` : ""}${squadId} --private
255
+ `));
256
+ }
257
+ }
258
+ const existing = listSquads(squadsDir);
259
+ console.log();
260
+ console.log(chalk.green(" \u2713 Squad created:"), chalk.cyan(squadId));
261
+ console.log();
262
+ console.log(chalk.dim(" Files:"));
263
+ console.log(` .agents/squads/${squadId}/SQUAD.md`);
264
+ console.log(` .agents/squads/${squadId}/lead.md`);
265
+ console.log(` .agents/memory/${squadId}/lead/`);
266
+ if (slackChannelId) {
267
+ console.log();
268
+ console.log(chalk.dim(" Slack channel:"));
269
+ console.log(` ${chalk.cyan(`#squad-${squadId}`)}`);
270
+ }
271
+ if (repoUrl) {
272
+ console.log();
273
+ console.log(chalk.dim(" GitHub repo:"));
274
+ console.log(` ${chalk.cyan(repoUrl)}`);
275
+ }
276
+ console.log();
277
+ console.log(chalk.dim(" Next steps:"));
278
+ console.log(` ${chalk.cyan("$")} squads run ${squadId} ${chalk.dim("# run the squad")}`);
279
+ console.log(` ${chalk.cyan("$")} squads status ${squadId} ${chalk.dim("# check status")}`);
280
+ console.log(` ${chalk.cyan("$")} squads list ${chalk.dim(`# ${existing.length} squads total`)}`);
281
+ console.log();
282
+ }
283
+ export {
284
+ createCommand
285
+ };
286
+ //# sourceMappingURL=create-554W5HNU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/create.ts","../src/lib/slack.ts"],"sourcesContent":["/**\n * squads create <name> — Create a new squad with directory structure and starter files.\n *\n * Creates:\n * .agents/squads/<name>/SQUAD.md\n * .agents/squads/<name>/lead.md\n * .agents/memory/<name>/lead/ (empty, ready for state)\n *\n * Squad discovery is filesystem-based (squad-parser.ts), so creating the\n * directory + SQUAD.md is all that's needed for `squads list` to find it.\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport chalk from 'chalk';\nimport { findSquadsDir, findProjectRoot, listSquads } from '../lib/squad-parser.js';\nimport { loadTemplate, toKebabCase, toTitleCase, type TemplateVariables } from '../lib/templates.js';\nimport { track } from '../lib/telemetry.js';\nimport { createGitHubRepo, detectGitHubOrg } from '../lib/github.js';\nimport { createSquadChannel } from '../lib/slack.js';\n\ninterface CreateOptions {\n description?: string;\n goal?: string;\n model?: string;\n force?: boolean;\n yes?: boolean;\n repo?: boolean;\n org?: string;\n slack?: boolean;\n}\n\nexport async function createCommand(name: string, options: CreateOptions): Promise<void> {\n const squadId = toKebabCase(name);\n const squadName = toTitleCase(squadId);\n\n if (!squadId) {\n console.error(chalk.red('\\n Invalid squad name. Use letters, numbers, and hyphens.\\n'));\n process.exit(1);\n }\n\n // Find project root (where .agents/ lives)\n const projectRoot = findProjectRoot();\n if (!projectRoot) {\n console.error(chalk.red('\\n Not in a squads project. Run `squads init` first.\\n'));\n process.exit(1);\n }\n\n const squadsDir = findSquadsDir();\n if (!squadsDir) {\n console.error(chalk.red('\\n No .agents/squads directory found. Run `squads init` first.\\n'));\n process.exit(1);\n }\n\n // Check if squad already exists\n const squadDir = join(squadsDir, squadId);\n if (existsSync(join(squadDir, 'SQUAD.md')) && !options.force) {\n console.error(chalk.red(`\\n Squad \"${squadId}\" already exists. Use --force to overwrite.\\n`));\n process.exit(1);\n }\n\n // Collect description and goal — prompt interactively if not provided via flags\n let description = options.description;\n let goal = options.goal;\n\n if (!options.yes && (!description || !goal)) {\n const inquirer = await import('inquirer');\n\n if (!description) {\n const answer = await inquirer.default.prompt([{\n type: 'input',\n name: 'description',\n message: 'Squad mission (one sentence):',\n default: `The ${squadName} squad handles ${squadId}-related tasks.`,\n }]);\n description = answer.description;\n }\n\n if (!goal) {\n const answer = await inquirer.default.prompt([{\n type: 'input',\n name: 'goal',\n message: 'First goal:',\n default: `Define ${squadId} squad objectives and deliver first results`,\n }]);\n goal = answer.goal;\n }\n }\n\n // Defaults for non-interactive mode\n description = description || `The ${squadName} squad handles ${squadId}-related tasks.`;\n goal = goal || `Define ${squadId} squad objectives and deliver first results`;\n const model = options.model || 'sonnet';\n\n // Template variables\n const vars: TemplateVariables = {\n SQUAD_NAME: squadName,\n SQUAD_ID: squadId,\n SQUAD_DESCRIPTION: description,\n GOAL: goal,\n };\n\n // 1. Create squad directory\n mkdirSync(squadDir, { recursive: true });\n\n // 2. Create SQUAD.md from template\n let squadContent: string;\n try {\n squadContent = loadTemplate('first-squad/SQUAD.md.template', vars);\n } catch {\n // Fallback if template not found\n squadContent = `# ${squadName}\\n\\n${description}\\n\\n## Goals\\n\\n- [ ] ${goal}\\n\\n## Agents\\n\\n| Agent | Purpose |\\n|-------|--------|\\n| lead | Orchestrates the squad and coordinates work |\\n\\n## Pipeline\\n\\n\\`lead\\` (orchestrates all work)\\n\\n## Usage\\n\\n\\`\\`\\`bash\\nsquads run ${squadId} --execute\\n\\`\\`\\`\\n`;\n }\n writeFileSync(join(squadDir, 'SQUAD.md'), squadContent);\n\n // 3. Create lead agent from template\n let leadContent: string;\n try {\n leadContent = loadTemplate('first-squad/lead.md.template', vars);\n } catch {\n // Fallback if template not found\n leadContent = `# Lead Agent\\n\\n## Purpose\\nOrchestrate the ${squadName} squad to achieve its goals.\\n\\n## Model\\n${model}\\n\\n## Tools\\n- Read\\n- Write\\n- Edit\\n- Bash\\n- WebSearch\\n- WebFetch\\n- Task\\n\\n## Instructions\\n\\nYou are the lead agent for the ${squadName} squad.\\n\\n**Goal**: ${goal}\\n\\n### Approach\\n\\n1. **Understand the goal** - Break down what needs to be accomplished\\n2. **Plan the work** - Create a clear execution plan\\n3. **Execute** - Work through the plan step by step\\n4. **Verify** - Confirm the goal is achieved\\n5. **Document** - Update memory with learnings\\n\\n## Output\\nProgress updates and work artifacts as appropriate.\\n\\n## Labels\\n- lead\\n- orchestration\\n`;\n }\n writeFileSync(join(squadDir, 'lead.md'), leadContent);\n\n // 4. Create memory directories\n const memoryDir = join(projectRoot, '.agents', 'memory', squadId, 'lead');\n mkdirSync(memoryDir, { recursive: true });\n\n // Track creation event\n await track('cli.create', {\n squad: squadId,\n hasDescription: !!options.description,\n hasGoal: !!options.goal,\n force: !!options.force,\n repo: !!options.repo,\n }).catch(() => {});\n\n // 5. Create Slack channel if --slack flag is set\n let slackChannelId: string | null = null;\n if (options.slack) {\n slackChannelId = await createSquadChannel(squadId, `Channel for the ${squadName} squad`);\n if (!slackChannelId) {\n console.error(chalk.red('\\n Slack channel creation failed (check SLACK_BOT_TOKEN).\\n'));\n console.error(chalk.dim(' Local squad was created. Create the channel manually.\\n'));\n }\n }\n\n // 6. Create GitHub repo if --repo flag is set\n let repoUrl: string | undefined;\n if (options.repo) {\n const org = options.org ?? detectGitHubOrg();\n try {\n const result = createGitHubRepo(squadId, { org, description });\n repoUrl = result.url;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(chalk.red(`\\n GitHub repo creation failed: ${message}\\n`));\n console.error(chalk.dim(' Local squad was created. Create the repo manually with:'));\n console.error(chalk.dim(` gh repo create ${org ? `${org}/` : ''}${squadId} --private\\n`));\n }\n }\n\n // 7. Show success\n const existing = listSquads(squadsDir);\n console.log();\n console.log(chalk.green(' ✓ Squad created:'), chalk.cyan(squadId));\n console.log();\n console.log(chalk.dim(' Files:'));\n console.log(` .agents/squads/${squadId}/SQUAD.md`);\n console.log(` .agents/squads/${squadId}/lead.md`);\n console.log(` .agents/memory/${squadId}/lead/`);\n if (slackChannelId) {\n console.log();\n console.log(chalk.dim(' Slack channel:'));\n console.log(` ${chalk.cyan(`#squad-${squadId}`)}`);\n }\n if (repoUrl) {\n console.log();\n console.log(chalk.dim(' GitHub repo:'));\n console.log(` ${chalk.cyan(repoUrl)}`);\n }\n console.log();\n console.log(chalk.dim(' Next steps:'));\n console.log(` ${chalk.cyan('$')} squads run ${squadId} ${chalk.dim('# run the squad')}`);\n console.log(` ${chalk.cyan('$')} squads status ${squadId} ${chalk.dim('# check status')}`);\n console.log(` ${chalk.cyan('$')} squads list ${chalk.dim(`# ${existing.length} squads total`)}`);\n console.log();\n}\n","/**\n * Slack integration for squad notifications and approvals.\n *\n * Provides:\n * - Session notifications (start/complete/fail)\n * - Approval requests with interactive buttons\n * - Approval polling (wait for human response)\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { findSquadsDir } from './squad-parser.js';\n\nconst SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN;\n\n// Approval tiers from SQUAD.md\nexport type ApprovalTier = 'auto' | 'notify' | 'approve' | 'confirm';\n\nexport interface SlackChannel {\n id: string;\n name: string;\n}\n\ninterface SlackApiResponse {\n ok: boolean;\n error?: string;\n ts?: string;\n channel?: SlackChannel;\n channels?: SlackChannel[];\n messages?: SlackMessage[];\n}\n\ninterface SlackMessage {\n ts: string;\n text: string;\n blocks?: SlackBlock[];\n reactions?: { name: string }[];\n}\n\ninterface SlackBlock {\n type: string;\n elements?: { text?: string }[];\n}\n\n/**\n * Make a Slack API call\n */\nexport async function slackApi<T = SlackApiResponse>(\n method: 'GET' | 'POST',\n endpoint: string,\n body?: Record<string, unknown>\n): Promise<T> {\n if (!SLACK_BOT_TOKEN) {\n throw new Error('SLACK_BOT_TOKEN not set in environment');\n }\n\n const url = `https://slack.com/api/${endpoint}`;\n const options: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${SLACK_BOT_TOKEN}`,\n 'Content-Type': 'application/json',\n },\n };\n\n if (body) {\n options.body = JSON.stringify(body);\n }\n\n const res = await fetch(url, options);\n const data = await res.json() as SlackApiResponse;\n\n if (!data.ok) {\n throw new Error(`Slack API error: ${data.error}`);\n }\n\n return data as T;\n}\n\n/**\n * Check if Slack is configured\n */\nexport function isSlackConfigured(): boolean {\n return !!SLACK_BOT_TOKEN;\n}\n\n/**\n * Get channel ID for a squad (format: squad-<name>)\n */\nexport async function getSquadChannelId(squad: string): Promise<string | null> {\n const channelName = `squad-${squad}`;\n\n try {\n const response = await slackApi<SlackApiResponse>(\n 'GET',\n 'conversations.list?types=public_channel&limit=200'\n );\n\n const channel = response.channels?.find((c) => c.name === channelName);\n return channel?.id || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse approval tier for an action from SQUAD.md\n */\nexport function getApprovalTier(squad: string, action: string): ApprovalTier {\n const squadsDir = findSquadsDir();\n if (!squadsDir) return 'approve'; // Safe default\n\n const squadFile = join(squadsDir, squad, 'SQUAD.md');\n if (!existsSync(squadFile)) return 'approve';\n\n const content = readFileSync(squadFile, 'utf-8');\n\n // Extract YAML block (between ```yaml and ```)\n const yamlMatch = content.match(/```yaml\\n([\\s\\S]*?)```/);\n if (!yamlMatch) return 'approve';\n\n const yaml = yamlMatch[1];\n\n // Check each tier (order matters - most restrictive first)\n if (yaml.includes('confirm:') && yaml.includes(`- ${action}`)) {\n const confirmSection = yaml.split('confirm:')[1]?.split(/\\n\\s*\\w+:/)[0];\n if (confirmSection?.includes(`- ${action}`)) return 'confirm';\n }\n\n if (yaml.includes('approve:') && yaml.includes(`- ${action}`)) {\n const approveSection = yaml.split('approve:')[1]?.split(/\\n\\s*\\w+:/)[0];\n if (approveSection?.includes(`- ${action}`)) return 'approve';\n }\n\n if (yaml.includes('notify:') && yaml.includes(`- ${action}`)) {\n const notifySection = yaml.split('notify:')[1]?.split(/\\n\\s*\\w+:/)[0];\n if (notifySection?.includes(`- ${action}`)) return 'notify';\n }\n\n if (yaml.includes('auto:') && yaml.includes(`- ${action}`)) {\n const autoSection = yaml.split('auto:')[1]?.split(/\\n\\s*\\w+:/)[0];\n if (autoSection?.includes(`- ${action}`)) return 'auto';\n }\n\n // Default: require approval (safe default)\n return 'approve';\n}\n\n/**\n * Post a notification to a squad channel (no approval needed)\n */\nexport async function postNotification(\n squad: string,\n message: string,\n options?: {\n emoji?: string;\n context?: string;\n }\n): Promise<string | null> {\n if (!isSlackConfigured()) return null;\n\n const channelId = await getSquadChannelId(squad);\n if (!channelId) return null;\n\n const emoji = options?.emoji || ':robot_face:';\n // Type blocks array to accept different Slack block types\n const blocks: Array<Record<string, unknown>> = [\n {\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `${emoji} ${message}`,\n },\n },\n ];\n\n if (options?.context) {\n blocks.push({\n type: 'context',\n elements: [{ type: 'mrkdwn', text: options.context }],\n });\n }\n\n try {\n const response = await slackApi<SlackApiResponse>('POST', 'chat.postMessage', {\n channel: channelId,\n text: message,\n blocks,\n });\n return response.ts || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Post an approval request with interactive buttons\n */\nexport async function postApprovalRequest(\n squad: string,\n action: string,\n description: string,\n options?: {\n agent?: string;\n tier?: ApprovalTier;\n }\n): Promise<{ ts: string; channelId: string } | null> {\n if (!isSlackConfigured()) return null;\n\n const channelId = await getSquadChannelId(squad);\n if (!channelId) return null;\n\n const tier = options?.tier || getApprovalTier(squad, action);\n\n // Tier-specific formatting\n let emoji = ':rotating_light:';\n let title = 'Approval Required';\n\n if (tier === 'confirm') {\n emoji = ':warning:';\n title = 'Confirmation Required';\n } else if (tier === 'notify') {\n emoji = ':bell:';\n title = 'Notification';\n }\n\n // Build fields\n const fields = [\n { type: 'mrkdwn', text: `*Squad:*\\n${squad}` },\n { type: 'mrkdwn', text: `*Action:*\\n\\`${action}\\`` },\n ];\n\n if (options?.agent) {\n fields.splice(1, 0, { type: 'mrkdwn', text: `*Agent:*\\n${options.agent}` });\n }\n\n // Build blocks\n const blocks: Record<string, unknown>[] = [\n {\n type: 'header',\n text: { type: 'plain_text', text: `${emoji} ${title}` },\n },\n {\n type: 'section',\n fields,\n },\n {\n type: 'section',\n text: { type: 'mrkdwn', text: description },\n },\n ];\n\n // Add buttons for approve/confirm tiers, not for notify\n if (tier !== 'notify') {\n blocks.push({\n type: 'actions',\n elements: [\n {\n type: 'button',\n text: { type: 'plain_text', text: 'Approve' },\n style: 'primary',\n value: `approve_${squad}_${action}`,\n action_id: 'approve_action',\n },\n {\n type: 'button',\n text: { type: 'plain_text', text: 'Reject' },\n style: 'danger',\n value: `reject_${squad}_${action}`,\n action_id: 'reject_action',\n },\n ],\n });\n } else {\n blocks.push({\n type: 'context',\n elements: [{ type: 'mrkdwn', text: '_Auto-proceeding (notify tier)_' }],\n });\n }\n\n try {\n const response = await slackApi<SlackApiResponse>('POST', 'chat.postMessage', {\n channel: channelId,\n text: `${title}: ${action}`,\n blocks,\n });\n\n if (response.ts) {\n return { ts: response.ts, channelId };\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Wait for approval by polling message state.\n * Returns true if approved, false if rejected, throws on timeout.\n */\nexport async function waitForApproval(\n channelId: string,\n messageTs: string,\n timeoutMs = 300000 // 5 minutes default\n): Promise<boolean> {\n const startTime = Date.now();\n const pollInterval = 3000; // 3 seconds\n\n while (Date.now() - startTime < timeoutMs) {\n try {\n const response = await slackApi<SlackApiResponse>(\n 'GET',\n `conversations.history?channel=${channelId}&oldest=${messageTs}&latest=${messageTs}&inclusive=true&limit=1`\n );\n\n const message = response.messages?.[0];\n if (!message) {\n await sleep(pollInterval);\n continue;\n }\n\n // Check if buttons were replaced (decision made via interactions endpoint)\n const hasActions = message.blocks?.some((b) => b.type === 'actions');\n const contextText = message.blocks\n ?.filter((b) => b.type === 'context')\n ?.flatMap((b) => b.elements?.map((e) => e.text) || [])\n ?.join(' ') || '';\n\n if (!hasActions) {\n // Buttons removed = decision made\n if (contextText.toLowerCase().includes('approved')) {\n return true;\n }\n if (contextText.toLowerCase().includes('rejected')) {\n return false;\n }\n }\n\n // Also check emoji reactions as backup\n const reactions = message.reactions?.map((r) => r.name) || [];\n if (reactions.includes('white_check_mark') || reactions.includes('heavy_check_mark')) {\n return true;\n }\n if (reactions.includes('x') || reactions.includes('no_entry')) {\n return false;\n }\n\n await sleep(pollInterval);\n } catch {\n await sleep(pollInterval);\n }\n }\n\n throw new Error(`Approval timeout after ${timeoutMs / 1000}s`);\n}\n\n/**\n * Request approval and wait for response.\n * Returns true if approved/auto, false if rejected.\n * For notify tier, posts notification and returns true immediately.\n */\nexport async function requestApprovalAndWait(\n squad: string,\n action: string,\n description: string,\n options?: {\n agent?: string;\n timeoutMs?: number;\n }\n): Promise<boolean> {\n const tier = getApprovalTier(squad, action);\n\n // Auto tier: no Slack interaction needed\n if (tier === 'auto') {\n return true;\n }\n\n // Notify tier: post and continue\n if (tier === 'notify') {\n await postApprovalRequest(squad, action, description, {\n agent: options?.agent,\n tier,\n });\n return true;\n }\n\n // Approve/Confirm tier: post and wait\n const result = await postApprovalRequest(squad, action, description, {\n agent: options?.agent,\n tier,\n });\n\n if (!result) {\n // If Slack isn't available, default to auto-approve\n console.warn(`[Slack] Could not post approval request, defaulting to approved`);\n return true;\n }\n\n return waitForApproval(result.channelId, result.ts, options?.timeoutMs);\n}\n\n// Helper\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Create a Slack channel for a squad (format: squad-<name>).\n * Returns the channel ID string, or null if creation fails.\n */\nexport async function createSquadChannel(squadId: string, topic?: string): Promise<string | null> {\n if (!isSlackConfigured()) return null;\n\n const channelName = `squad-${squadId}`;\n\n try {\n const response = await slackApi<SlackApiResponse & { channel?: SlackChannel }>('POST', 'conversations.create', {\n name: channelName,\n is_private: false,\n });\n const channelId = response.channel?.id || null;\n\n if (channelId && topic) {\n await slackApi('POST', 'conversations.setTopic', {\n channel: channelId,\n topic,\n }).catch(() => {});\n }\n\n return channelId;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (message.includes('name_taken')) {\n return getSquadChannelId(squadId);\n }\n return null;\n }\n}\n\n/**\n * Post a \"tonight session started\" notification\n */\nexport async function notifyTonightStart(\n targets: string[],\n config: { costCap: number; stopAt: string }\n): Promise<void> {\n // Post to all relevant squad channels\n const squadsSeen = new Set<string>();\n\n for (const target of targets) {\n const squad = target.split('/')[0];\n if (squadsSeen.has(squad)) continue;\n squadsSeen.add(squad);\n\n await postNotification(\n squad,\n `*Tonight mode started*\\nTargets: ${targets.filter((t) => t.startsWith(squad)).join(', ')}`,\n {\n emoji: ':crescent_moon:',\n context: `Cost cap: $${config.costCap} | Stop at: ${config.stopAt}`,\n }\n );\n }\n}\n\n/**\n * Post a \"tonight session completed\" notification\n */\nexport async function notifyTonightComplete(\n targets: string[],\n stats: { duration: number; cost: number; completed: number; failed: number }\n): Promise<void> {\n const squadsSeen = new Set<string>();\n\n for (const target of targets) {\n const squad = target.split('/')[0];\n if (squadsSeen.has(squad)) continue;\n squadsSeen.add(squad);\n\n const emoji = stats.failed > 0 ? ':warning:' : ':white_check_mark:';\n await postNotification(\n squad,\n `*Tonight mode completed*\\n${stats.completed} completed, ${stats.failed} failed`,\n {\n emoji,\n context: `Duration: ${stats.duration}min | Cost: $${stats.cost.toFixed(2)}`,\n }\n );\n }\n}\n"],"mappings":"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","names":[]}