@wrongstack/cli 1.0.33 → 1.0.36

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 (145) hide show
  1. package/dist/{acp-EDGUWU7N.js → acp-QLU7T5WX.js} +470 -466
  2. package/dist/{auth-SUHKFFO4.js → auth-AED7RW7D.js} +7 -7
  3. package/dist/auth-menu/openai-codex-oauth.d.ts +1 -1
  4. package/dist/auth-menu/panel-provider-edits.d.ts +15 -0
  5. package/dist/auth-menu/panel-service.d.ts +1 -1
  6. package/dist/{automation-MCDFCIMV.js → automation-3E3AUPBF.js} +19 -4
  7. package/dist/boot/governance-evidence-candidate.d.ts +1 -2
  8. package/dist/boot/system-prompt-builder.d.ts +1 -1
  9. package/dist/boot/tui-project-spawn.d.ts +0 -1
  10. package/dist/boot/tui-session-resume-contracts.d.ts +95 -0
  11. package/dist/boot/tui-session-resume.d.ts +2 -88
  12. package/dist/boot/tui-settings-read.d.ts +3 -0
  13. package/dist/{boot-auto-thin-K2DHHYIW.js → boot-auto-thin-AGJ6TAWY.js} +3 -3
  14. package/dist/boot-launch-choices.d.ts +22 -0
  15. package/dist/boot-provider-gate.d.ts +28 -0
  16. package/dist/boot-subcommand-dispatch.d.ts +24 -0
  17. package/dist/boot.d.ts +1 -1
  18. package/dist/chimera-cascade-evidence.d.ts +1 -1
  19. package/dist/{chunk-TI7KLHYX.js → chunk-2CMMWRQH.js} +81 -80
  20. package/dist/{chunk-4BX2YDTQ.js → chunk-4JMAWKLA.js} +45 -4
  21. package/dist/{chunk-TWSQTQKR.js → chunk-4R6TVMYA.js} +2 -2
  22. package/dist/{chunk-HHRYIDOW.js → chunk-ACW64W4M.js} +34 -7
  23. package/dist/{chunk-5EA6OTLJ.js → chunk-AFOH6CHO.js} +2 -2
  24. package/dist/{chunk-WHEYHDIE.js → chunk-AKESWZH5.js} +2 -1
  25. package/dist/{chunk-JHUJUIOH.js → chunk-AT2A7RJZ.js} +4 -16
  26. package/dist/{chunk-LKFKO4JW.js → chunk-B6EHK772.js} +2 -2
  27. package/dist/{chunk-U3SS4NRH.js → chunk-BHXAKTBE.js} +1 -1
  28. package/dist/{chunk-4UWCMBPG.js → chunk-BJH44J7G.js} +3 -3
  29. package/dist/{chunk-MPQCHBDJ.js → chunk-CJSHS2K7.js} +5 -1
  30. package/dist/{chunk-RQ4UBKYH.js → chunk-GF7LUWQS.js} +5 -11
  31. package/dist/{chunk-TXAFIERF.js → chunk-L5NWFP5R.js} +2 -2
  32. package/dist/{chunk-24C6VVLM.js → chunk-LPGZ3NUF.js} +23 -23
  33. package/dist/{chunk-UQUYBDMG.js → chunk-M4G5FO7F.js} +1 -1
  34. package/dist/{chunk-P3GRLV47.js → chunk-NEWXDNF2.js} +212 -145
  35. package/dist/{chunk-3LEFXLAW.js → chunk-NOZJIN46.js} +36 -37
  36. package/dist/{chunk-4MDRHMX4.js → chunk-RE73FP2X.js} +4 -27
  37. package/dist/{chunk-5UZBUV2H.js → chunk-RKU66D2O.js} +6 -4
  38. package/dist/{chunk-SWOPJHXI.js → chunk-RYTSPBNM.js} +4 -4
  39. package/dist/{chunk-WTJDSTYD.js → chunk-T5KR6SNO.js} +2 -2
  40. package/dist/chunk-TGSPCFBT.js +1 -0
  41. package/dist/{chunk-ZYACRPLN.js → chunk-ULYAI7CP.js} +6 -3
  42. package/dist/{chunk-HLSHFXTO.js → chunk-WY74MMDB.js} +2 -2
  43. package/dist/{chunk-PGNERAZL.js → chunk-ZWBI7ORM.js} +3 -3
  44. package/dist/{cli-context-7HK67B2Z.js → cli-context-I6HJCVCV.js} +1607 -1495
  45. package/dist/cli-initial-tools.d.ts +54 -0
  46. package/dist/{cli-main-TQGLKB6D.js → cli-main-X7KD7IOC.js} +38223 -37136
  47. package/dist/cli-main-boot.d.ts +133 -0
  48. package/dist/cli-main-orchestration.d.ts +170 -0
  49. package/dist/cli-main-state.d.ts +19 -0
  50. package/dist/config-doctor.d.ts +1 -1
  51. package/dist/{config-transfer-UE37X4B5.js → config-transfer-YPD7HYXH.js} +7 -5
  52. package/dist/{execution-C2IECWF7.js → execution-M7D4KNBB.js} +171 -132
  53. package/dist/fleet/host-director-setup.d.ts +41 -0
  54. package/dist/fleet/host-spawn-types.d.ts +17 -0
  55. package/dist/fleet/host.d.ts +2 -1
  56. package/dist/goal-host-prompts.d.ts +5 -0
  57. package/dist/goal-host-types.d.ts +94 -0
  58. package/dist/goal-host.d.ts +2 -91
  59. package/dist/{hq-KBFDKTDD.js → hq-BBLGEEUH.js} +5 -6
  60. package/dist/hq-server/handle-types.d.ts +1 -1
  61. package/dist/hq-server/routes/auth-handlers.d.ts +1 -1
  62. package/dist/hq-server/routes.d.ts +1 -13
  63. package/dist/hq-server/utils.d.ts +1 -1
  64. package/dist/{hq-server-AVNKZ2PV.js → hq-server-5TR6NBGS.js} +3 -3
  65. package/dist/{hq-service-FGB7BY2D.js → hq-service-4Q64WFNT.js} +3 -3
  66. package/dist/{import-claude-code-AYG24RG2.js → import-claude-code-GHIS2F7Y.js} +3 -3
  67. package/dist/index.js +5 -5
  68. package/dist/live-settings-input.d.ts +2 -0
  69. package/dist/mailbox-bridge-bootstrap.d.ts +1 -4
  70. package/dist/{mailbox-serve-S536N66F.js → mailbox-serve-DRWJVMYU.js} +2 -2
  71. package/dist/{modeldiag-NEPTLFZW.js → modeldiag-F3LUF4VJ.js} +3 -3
  72. package/dist/picker-effort.d.ts +32 -0
  73. package/dist/picker-model-picker.d.ts +13 -2
  74. package/dist/picker-provider-list.d.ts +66 -0
  75. package/dist/picker.d.ts +12 -12
  76. package/dist/plugin-config.d.ts +6 -0
  77. package/dist/{plugin-usage-3TZGROK7.js → plugin-usage-EM752WHG.js} +3 -3
  78. package/dist/{plugins-ITH4SLO6.js → plugins-U6HQFJ7N.js} +4 -4
  79. package/dist/{providers-models-O75MKLN2.js → providers-models-S3S6VXME.js} +249 -239
  80. package/dist/repl-suggestions.d.ts +0 -5
  81. package/dist/{sage-FFKUOCQL.js → sage-BMRDJCDI.js} +9 -5
  82. package/dist/{sage-sync-ZMTHB477.js → sage-sync-PCWCNEUK.js} +2 -2
  83. package/dist/{sdd-runtime-PTOSPYI4.js → sdd-runtime-PF72WHLO.js} +3 -20
  84. package/dist/services/sdd/task-manager.d.ts +1 -12
  85. package/dist/services/sdd-runtime.d.ts +3 -4
  86. package/dist/services/statusline-config.d.ts +0 -2
  87. package/dist/{sessions-config-VE26SB56.js → sessions-config-RQRQDW5Q.js} +2 -2
  88. package/dist/settings-behavior-sections.d.ts +0 -1
  89. package/dist/short-circuit-flags-IEV3FEOT.js +10 -0
  90. package/dist/{skill-suggest-L2EVWB6R.js → skill-suggest-THANK3LV.js} +1 -1
  91. package/dist/slash-commands/command-context.d.ts +2 -0
  92. package/dist/slash-commands/doctor.d.ts +0 -9
  93. package/dist/slash-commands/fallback-chain-view.d.ts +3 -0
  94. package/dist/slash-commands/fallback-ref-validation.d.ts +34 -0
  95. package/dist/slash-commands/goal-refiner.d.ts +10 -2
  96. package/dist/slash-commands/goals.d.ts +6 -0
  97. package/dist/slash-commands/kanban-task-completion.d.ts +17 -0
  98. package/dist/slash-commands/mcp.d.ts +0 -1
  99. package/dist/slash-commands/memory-purge-domain-terms.d.ts +5 -0
  100. package/dist/slash-commands/nextsteps-mode.d.ts +13 -0
  101. package/dist/slash-commands/session-live-status.d.ts +21 -0
  102. package/dist/slash-commands/session.d.ts +1 -1
  103. package/dist/slash-commands/settings-feature-ui-mutations.d.ts +8 -0
  104. package/dist/slash-commands/settings-hq-autonomy-mutations.d.ts +8 -0
  105. package/dist/slash-commands/settings-persist-deps.d.ts +10 -0
  106. package/dist/slash-commands/settings-runtime-mutations.d.ts +12 -0
  107. package/dist/slash-commands/shadow.d.ts +0 -1
  108. package/dist/slash-commands/tool-auto-thin-commands.d.ts +15 -0
  109. package/dist/{standalone-auto-update-VQC4GYCM.js → standalone-auto-update-QUXOZ4B5.js} +13 -2
  110. package/dist/subcommands/handlers/acp-agents.d.ts +9 -0
  111. package/dist/subcommands/handlers/acp-server.d.ts +7 -0
  112. package/dist/subcommands/handlers/hq.d.ts +0 -1
  113. package/dist/subcommands/handlers/providers-models-custom.d.ts +33 -0
  114. package/dist/subcommands/handlers/providers-models-visibility.d.ts +29 -0
  115. package/dist/subcommands/handlers/skill-suggest.d.ts +0 -2
  116. package/dist/{subcommands-CLI6QTLU.js → subcommands-75IB23LS.js} +2 -2
  117. package/dist/{typesafe-NHC322W5.js → typesafe-KW2XHOG3.js} +2 -2
  118. package/dist/{update-J5WHAFPE.js → update-LHCWAWEZ.js} +3 -3
  119. package/dist/utils/win32-cmd.d.ts +1 -1
  120. package/dist/{version-help-ATOKGMTK.js → version-help-JQGRPDJW.js} +2 -2
  121. package/dist/webui-server/credential-watcher.d.ts +3 -1
  122. package/dist/webui-server/host-settings.d.ts +17 -0
  123. package/dist/webui-server/http-bridge.d.ts +32 -0
  124. package/dist/webui-server/instance-registration.d.ts +19 -0
  125. package/dist/webui-server/kanban-services.d.ts +9 -0
  126. package/dist/webui-server/message-router.d.ts +14 -0
  127. package/dist/webui-server/prefs-seeding.d.ts +1 -2
  128. package/dist/webui-server/provider-config.d.ts +0 -1
  129. package/dist/webui-server/provider-plane.d.ts +14 -0
  130. package/dist/webui-server/session-agents.d.ts +18 -0
  131. package/dist/webui-server/session-start-live.d.ts +11 -0
  132. package/dist/webui-server/stream-coalescer.d.ts +1 -1
  133. package/dist/webui-server/transport.d.ts +12 -0
  134. package/dist/{webui-server-GQOVLQS3.js → webui-server-CUIFA25W.js} +1472 -1335
  135. package/dist/webui-session-maintenance.d.ts +16 -0
  136. package/dist/wiring/brain-and-orchestration.d.ts +1 -1
  137. package/dist/wiring/cli-slash-commands-setup.d.ts +1 -0
  138. package/dist/wiring/command-host-state.d.ts +11 -2
  139. package/dist/wiring/dep-watcher-bridge.d.ts +8 -1
  140. package/dist/wiring/management-tools.d.ts +9 -1
  141. package/dist/wiring/session-command-handlers.d.ts +2 -1
  142. package/dist/worktree-command-host.d.ts +31 -0
  143. package/package.json +31 -31
  144. package/dist/chunk-WHOIR577.js +0 -1
  145. package/dist/short-circuit-flags-D6VAG47Y.js +0 -10
@@ -10,13 +10,11 @@ import {
10
10
  } from "./chunk-NRXWOMK7.js";
11
11
  import {
12
12
  startCliHqConnection
13
- } from "./chunk-4UWCMBPG.js";
13
+ } from "./chunk-BJH44J7G.js";
14
14
  import "./chunk-SDJCLEK6.js";
15
15
  import "./chunk-F5DCFAKI.js";
16
16
 
17
- // src/subcommands/handlers/acp.ts
18
- import { randomBytes } from "node:crypto";
19
- import * as path2 from "node:path";
17
+ // src/subcommands/handlers/acp-agents.ts
20
18
  import {
21
19
  defaultPermissionPolicy,
22
20
  EnsembleRegistry,
@@ -27,6 +25,304 @@ import {
27
25
  runEnsemble,
28
26
  runOneAcpTask
29
27
  } from "@wrongstack/acp";
28
+ function acpOverrides(deps) {
29
+ return deps.config.acp?.agents;
30
+ }
31
+ async function loadLive(deps) {
32
+ return deps.paths ? loadCachedAcpRegistry(deps.paths) : null;
33
+ }
34
+ async function listACPAgents(deps) {
35
+ const detected = await new EnsembleRegistry().list();
36
+ const live = await loadLive(deps);
37
+ deps.renderer.write(`${formatAcpAgentList({ live, detected })}
38
+ `);
39
+ return 0;
40
+ }
41
+ async function syncACPRegistry(deps) {
42
+ if (!deps.paths) {
43
+ deps.renderer.writeError("Cannot sync: no cache directory available.\n");
44
+ return 1;
45
+ }
46
+ deps.renderer.writeInfo("Fetching the official ACP registry\u2026\n");
47
+ try {
48
+ const { count, location } = await refreshAcpRegistry(deps.paths);
49
+ deps.renderer.write(`Synced ${count} agents from the official ACP registry.
50
+ `);
51
+ deps.renderer.writeInfo(`Cached at ${location}
52
+ `);
53
+ return 0;
54
+ } catch (err) {
55
+ deps.renderer.writeError(
56
+ `ACP registry sync failed: ${err instanceof Error ? err.message : String(err)}
57
+ `
58
+ );
59
+ deps.renderer.write("The bundled offline catalog is still available via `wstack acp list`.\n");
60
+ return 1;
61
+ }
62
+ }
63
+ async function spawnACPAgent(args, deps) {
64
+ const [subagentId, ...taskParts] = args;
65
+ if (!subagentId) {
66
+ deps.renderer.writeError("Usage: wstack acp spawn <agent-id> <task>\n");
67
+ deps.renderer.write("Run `wstack acp list` to see available agents.\n");
68
+ return 1;
69
+ }
70
+ const task = taskParts.join(" ");
71
+ if (!task) {
72
+ deps.renderer.writeError("Usage: wstack acp spawn <agent-id> <task>\n");
73
+ deps.renderer.write("Task description is required.\n");
74
+ return 1;
75
+ }
76
+ const live = await loadLive(deps);
77
+ const cmd = resolveAcpAgentCommand(subagentId, acpOverrides(deps), live?.byId);
78
+ if (!cmd) {
79
+ deps.renderer.writeError(`Unknown ACP agent: ${subagentId}
80
+ `);
81
+ deps.renderer.write("Run `wstack acp list` (or `wstack acp sync`) to see available agents.\n");
82
+ return 1;
83
+ }
84
+ deps.renderer.writeInfo(`Spawning ACP agent '${subagentId}'\u2026
85
+ `);
86
+ const ac = new AbortController();
87
+ const cleanup = () => ac.abort();
88
+ process.on("SIGINT", cleanup);
89
+ process.on("SIGTERM", cleanup);
90
+ try {
91
+ deps.renderer.writeInfo("Running task\u2026\n");
92
+ const result = await runOneAcpTask({
93
+ command: cmd.command,
94
+ ...cmd.args !== void 0 ? { args: cmd.args } : {},
95
+ ...cmd.env !== void 0 ? { env: cmd.env } : {},
96
+ role: subagentId,
97
+ task,
98
+ signal: ac.signal,
99
+ permissionPolicy: defaultPermissionPolicy,
100
+ onProgress: (event) => {
101
+ const line = formatProgress(event);
102
+ if (line) deps.renderer.writeInfo(` ${line}
103
+ `);
104
+ }
105
+ });
106
+ deps.renderer.write("\n--- Result ---\n");
107
+ deps.renderer.write(result.result.length > 0 ? result.result : "no result");
108
+ deps.renderer.write("\n---------------\n");
109
+ deps.renderer.writeInfo(
110
+ `Done. iterations=${result.iterations} toolCalls=${result.toolCalls}
111
+ `
112
+ );
113
+ return 0;
114
+ } catch (err) {
115
+ const e = err;
116
+ const detail = e.kind ? `[${e.kind}] ` : "";
117
+ const message = e.message ?? (err instanceof Error ? err.message : String(err));
118
+ deps.renderer.writeError(`ACP agent error: ${detail}${message}
119
+ `);
120
+ return 1;
121
+ } finally {
122
+ process.off("SIGINT", cleanup);
123
+ process.off("SIGTERM", cleanup);
124
+ }
125
+ }
126
+ function formatProgress(event) {
127
+ switch (event.type) {
128
+ case "tool_call":
129
+ return `\u25B8 ${event.toolCall.title} (${event.toolCall.status})`;
130
+ case "tool_call_update":
131
+ return event.toolCall.status === "completed" || event.toolCall.status === "failed" ? ` \u21B3 ${event.toolCall.title}: ${event.toolCall.status}` : "";
132
+ case "diff":
133
+ return `\u270E ${event.diff.path}${event.diff.oldText === null ? " (new)" : ""}`;
134
+ case "plan":
135
+ return `\u2630 plan: ${event.entries.length} step(s)`;
136
+ default:
137
+ return "";
138
+ }
139
+ }
140
+ async function parallelACPAgents(args, deps) {
141
+ const [csv, ...taskParts] = args;
142
+ if (!csv) {
143
+ deps.renderer.writeError("Usage: wstack acp parallel <agent-id-csv> <task>\n");
144
+ deps.renderer.write('Example: wstack acp parallel claude-code,gemini-cli "review this diff"\n');
145
+ return 1;
146
+ }
147
+ const task = taskParts.join(" ");
148
+ if (!task) {
149
+ deps.renderer.writeError("Usage: wstack acp parallel <agent-id-csv> <task>\n");
150
+ deps.renderer.writeError("Task description is required.\n");
151
+ return 1;
152
+ }
153
+ const ac = new AbortController();
154
+ const onSignal = () => ac.abort();
155
+ process.on("SIGINT", onSignal);
156
+ process.on("SIGTERM", onSignal);
157
+ try {
158
+ const overrides = acpOverrides(deps);
159
+ const live = await loadLive(deps);
160
+ const result = await runEnsemble({
161
+ agentIds: csv,
162
+ task,
163
+ resolveCmd: (id) => resolveAcpAgentCommand(id, overrides, live?.byId),
164
+ signal: ac.signal,
165
+ onProgress: (agentId, event) => {
166
+ const line = formatProgress(event);
167
+ if (line) deps.renderer.writeInfo(` [${agentId}] ${line}
168
+ `);
169
+ }
170
+ });
171
+ const skipped = result.results.filter((r) => r.status === "skipped");
172
+ if (skipped.length > 0) {
173
+ deps.renderer.writeWarning(
174
+ `Skipping ${skipped.length} agent(s) not installed: ${skipped.map((s) => `${s.agentId} (${s.reason ?? "not installed"})`).join(", ")}
175
+ `
176
+ );
177
+ }
178
+ if (result.summary.succeeded + result.summary.failed + result.summary.cancelled === 0) {
179
+ deps.renderer.writeError("No installed agents to run.\n");
180
+ deps.renderer.write("Run `wstack acp list` to see what is available.\n");
181
+ return 1;
182
+ }
183
+ const fannedOut = result.results.filter((r) => r.status !== "skipped").map((r) => r.agentId).join(", ");
184
+ deps.renderer.writeInfo(
185
+ `Fanning out to ${result.summary.succeeded + result.summary.failed + result.summary.cancelled} agent(s): ${fannedOut}
186
+ `
187
+ );
188
+ deps.renderer.writeInfo(`Task: ${result.task}
189
+
190
+ `);
191
+ for (const r of result.results) {
192
+ if (r.status === "skipped") continue;
193
+ deps.renderer.write(`
194
+ === ${r.agentId} ===
195
+ `);
196
+ if (r.status === "success") {
197
+ deps.renderer.write(r.result && r.result.length > 0 ? r.result : "(no result)");
198
+ deps.renderer.write(
199
+ `
200
+ [${r.agentId}] success ${r.durationMs}ms iterations=${r.iterations} toolCalls=${r.toolCalls}
201
+ `
202
+ );
203
+ } else if (r.status === "failed") {
204
+ deps.renderer.writeError(
205
+ `[${r.error?.kind ?? "unknown"}] ${r.error?.message ?? "failed"}
206
+ `
207
+ );
208
+ deps.renderer.write(`[${r.agentId}] failed ${r.durationMs}ms
209
+ `);
210
+ } else {
211
+ deps.renderer.writeError(
212
+ `[${r.error?.kind ?? "aborted"}] ${r.error?.message ?? "cancelled"}
213
+ `
214
+ );
215
+ deps.renderer.write(`[${r.agentId}] cancelled ${r.durationMs}ms
216
+ `);
217
+ }
218
+ }
219
+ const { succeeded, failed, cancelled, skipped: skip } = result.summary;
220
+ deps.renderer.write(
221
+ `
222
+ Parallel summary: ${succeeded} succeeded, ${failed} failed, ${cancelled} cancelled, ${skip} skipped.
223
+ `
224
+ );
225
+ return succeeded > 0 ? 0 : 1;
226
+ } finally {
227
+ process.off("SIGINT", onSignal);
228
+ process.off("SIGTERM", onSignal);
229
+ }
230
+ }
231
+ async function probeACPAgents(args, deps) {
232
+ const csv = args.join(" ");
233
+ let ids;
234
+ if (csv) {
235
+ ids = csv.split(",").flatMap((s) => s.split(/\s+/)).map((s) => s.trim()).filter(Boolean);
236
+ } else {
237
+ const detected = await new EnsembleRegistry().list();
238
+ ids = detected.filter((a) => a.installed).map((a) => a.id);
239
+ }
240
+ if (ids.length === 0) {
241
+ deps.renderer.writeError("No installed agents to probe.\n");
242
+ return 1;
243
+ }
244
+ const overrides = acpOverrides(deps);
245
+ const live = await loadLive(deps);
246
+ deps.renderer.writeInfo(
247
+ `Probing ${ids.length} agent(s)\u2026 (npx-based agents may download on first run)
248
+ `
249
+ );
250
+ const results = await probeAcpAgents({
251
+ agentIds: ids,
252
+ resolveCmd: (id) => resolveAcpAgentCommand(id, overrides, live?.byId),
253
+ projectRoot: deps.cwd ?? process.cwd(),
254
+ onProgress: (id, r) => deps.renderer.writeInfo(` ${r.ok ? "\u2713" : "\u2717"} ${id} (${r.ms}ms)
255
+ `)
256
+ });
257
+ deps.renderer.write("\nACP handshake probe:\n\n");
258
+ for (const r of results) {
259
+ if (r.ok) {
260
+ const info = r.agentInfo ? ` \u2014 ${r.agentInfo.name} ${r.agentInfo.version}` : "";
261
+ deps.renderer.write(` \u2713 ${r.id.padEnd(16)} ok ${r.ms}ms${info}
262
+ `);
263
+ } else {
264
+ deps.renderer.write(` \u2717 ${r.id.padEnd(16)} ${r.error ?? "failed"} (${r.ms}ms)
265
+ `);
266
+ }
267
+ }
268
+ const ok = results.filter((r) => r.ok).length;
269
+ deps.renderer.write(`
270
+ ${ok} of ${results.length} agents completed the ACP handshake.
271
+ `);
272
+ return ok > 0 ? 0 : 1;
273
+ }
274
+ async function benchACPAgents(args, deps) {
275
+ const checkFs = deps.flags?.fs === true || deps.flags?.fs === "true" || args.includes("--fs");
276
+ const csv = args.filter((a) => a !== "--fs").join(" ");
277
+ let ids;
278
+ if (csv) {
279
+ ids = csv.split(",").flatMap((s) => s.split(/\s+/)).map((s) => s.trim()).filter(Boolean);
280
+ } else {
281
+ const detected = await new EnsembleRegistry().list();
282
+ ids = detected.filter((a) => a.installed).map((a) => a.id);
283
+ }
284
+ if (ids.length === 0) {
285
+ deps.renderer.writeError("No installed agents to bench.\n");
286
+ deps.renderer.write("Pass ids explicitly: `wstack acp bench gemini-cli,codex-cli`.\n");
287
+ return 1;
288
+ }
289
+ const ac = new AbortController();
290
+ const onSignal = () => ac.abort();
291
+ process.on("SIGINT", onSignal);
292
+ process.on("SIGTERM", onSignal);
293
+ try {
294
+ const overrides = acpOverrides(deps);
295
+ const live = await loadLive(deps);
296
+ deps.renderer.writeInfo(
297
+ `Benching ${ids.length} agent(s)${checkFs ? " (with fs check)" : ""}: ${ids.join(", ")}\u2026
298
+ `
299
+ );
300
+ const result = await runAcpBench({
301
+ agentIds: ids,
302
+ resolveCmd: (id) => resolveAcpAgentCommand(id, overrides, live?.byId),
303
+ projectRoot: deps.cwd ?? process.cwd(),
304
+ checkFs,
305
+ signal: ac.signal,
306
+ onProgress: (agentId, phase, r) => {
307
+ if (phase === "start") deps.renderer.writeInfo(` \u25B8 ${agentId}\u2026
308
+ `);
309
+ else if (r) deps.renderer.writeInfo(` \u21B3 ${agentId}: ${r.status}
310
+ `);
311
+ }
312
+ });
313
+ deps.renderer.write(`
314
+ ${renderAcpBenchText(result)}
315
+ `);
316
+ return result.summary.pass > 0 ? 0 : 1;
317
+ } finally {
318
+ process.off("SIGINT", onSignal);
319
+ process.off("SIGTERM", onSignal);
320
+ }
321
+ }
322
+
323
+ // src/subcommands/handlers/acp-server.ts
324
+ import { randomBytes } from "node:crypto";
325
+ import * as path2 from "node:path";
30
326
  import {
31
327
  ACPProtocolHandler,
32
328
  ACPSessionStore,
@@ -564,122 +860,34 @@ function createAcpConnectionGate(opts) {
564
860
  };
565
861
  const tokenOk = (supplied) => {
566
862
  if (!supplied) return false;
567
- const a = Buffer.from(supplied);
568
- if (a.length !== expected.length) return false;
569
- return timingSafeEqual(a, expected);
570
- };
571
- return {
572
- check(req, liveConnections) {
573
- const origin = req.headers.origin;
574
- if (origin && origin !== `http://${host}:${port}` && origin !== `ws://${host}:${port}`) {
575
- return { ok: false, code: 1008, reason: "cross-origin forbidden" };
576
- }
577
- if (!isLoopbackHostHeader(req.headers.host ?? "")) {
578
- return { ok: false, code: 1008, reason: "untrusted host header" };
579
- }
580
- if (!tokenOk(tokenFromRequest(req))) {
581
- return { ok: false, code: 1008, reason: "unauthorized" };
582
- }
583
- if (liveConnections >= maxConnections) {
584
- return { ok: false, code: 1013, reason: "too many connections" };
585
- }
586
- return OK;
587
- }
588
- };
589
- }
590
-
591
- // src/subcommands/handlers/acp.ts
592
- function pendingLeaderDeliveries(sessionId) {
593
- return leaderDeliveryHub.pending(sessionId);
594
- }
595
- function acpOverrides(deps) {
596
- return deps.config.acp?.agents;
597
- }
598
- async function loadLive(deps) {
599
- return deps.paths ? loadCachedAcpRegistry(deps.paths) : null;
600
- }
601
- var acpCmd = async (args, deps) => {
602
- const sub = args[0];
603
- const loginIndex = sub === "auth" ? 0 : (sub === "server" || sub === "serve") && args[1] === "auth" ? 1 : -1;
604
- if (loginIndex >= 0) {
605
- const { authCmd } = await import("./auth-SUHKFFO4.js");
606
- return authCmd(args.slice(loginIndex + 1), deps);
607
- }
608
- if (!sub || sub === "server" || sub === "serve") {
609
- return runACPServer(deps);
610
- }
611
- if (sub === "help") {
612
- deps.renderer.write(`wstack acp \u2014 ACP (Agent Client Protocol) integration
613
-
614
- Usage:
615
- wstack acp Start WrongStack as an ACP server (blocks)
616
- wstack acp server Same as above
617
- wstack acp list List available ACP agents
618
- wstack acp sync Pull the official agentclientprotocol/registry into cache
619
- wstack acp spawn <id> <task>
620
- Spawn an ACP agent as a subagent and wait for result
621
- wstack acp parallel <agent-id-csv> <task>
622
- Fan a task out to multiple ACP agents in parallel
623
- and aggregate the results
624
- wstack acp probe [agent-id-csv]
625
- Handshake-test agents (bounded concurrency). Defaults
626
- to all installed agents.
627
- wstack acp bench [agent-id-csv] [--fs]
628
- End-to-end verify each agent (handshake \u2192 prompt \u2192
629
- marker, optional fs check) and print a graded report.
630
- Defaults to all installed agents.
631
- wstack acp help Show this help
632
-
633
- ACP Mode:
634
- When run as \`wstack acp\`. WrongStack acts as an ACP-compatible agent driven
635
- by your configured model provider. ACP clients (Zed, JetBrains, VS Code)
636
- spawn it as a subprocess and communicate over stdio JSON-RPC. Run
637
- \`wstack auth\` first to configure a provider, or pass \`--echo\` for a no-op
638
- connectivity test that needs no provider. Press Ctrl+C to stop.
639
-
640
- Transports:
641
- (default) stdio JSON-RPC (the usual editor-spawned-subprocess mode)
642
- --ws[=port] serve over WebSocket on 127.0.0.1:<port> (default 8889) \u2014
643
- full-duplex, so updates/permission prompts stream live.
644
-
645
- spawn:
646
- Spawns a named ACP agent (claude-code, gemini-cli, codex-cli, copilot,
647
- cline, goose, openhands, qwen-code, kiro-cli, opencode, mistral-vibe,
648
- cursor) with the given task and waits for its result.
649
- Example: wstack acp spawn cline "fix the login bug"
650
-
651
- parallel:
652
- Runs the same task on a comma-separated list of ACP agents concurrently.
653
- Example: wstack acp parallel claude-code,gemini-cli,codex-cli "review this diff"
654
- Each agent's result is rendered under a clearly-marked header. Returns 0
655
- if at least one agent succeeds, 1 if all fail. Agents that aren't
656
- installed are skipped with a warning.
657
- `);
658
- return 0;
659
- }
660
- if (sub === "list") {
661
- return listACPAgents(deps);
662
- }
663
- if (sub === "sync") {
664
- return syncACPRegistry(deps);
665
- }
666
- if (sub === "spawn") {
667
- return spawnACPAgent(args.slice(1), deps);
668
- }
669
- if (sub === "parallel") {
670
- return parallelACPAgents(args.slice(1), deps);
671
- }
672
- if (sub === "probe") {
673
- return probeACPAgents(args.slice(1), deps);
674
- }
675
- if (sub === "bench") {
676
- return benchACPAgents(args.slice(1), deps);
677
- }
678
- deps.renderer.writeError(`Unknown acp subcommand: ${sub}
679
- `);
680
- deps.renderer.write("Run `wstack acp help` for usage.\n");
681
- return 1;
682
- };
863
+ const a = Buffer.from(supplied);
864
+ if (a.length !== expected.length) return false;
865
+ return timingSafeEqual(a, expected);
866
+ };
867
+ return {
868
+ check(req, liveConnections) {
869
+ const origin = req.headers.origin;
870
+ if (origin && origin !== `http://${host}:${port}` && origin !== `ws://${host}:${port}`) {
871
+ return { ok: false, code: 1008, reason: "cross-origin forbidden" };
872
+ }
873
+ if (!isLoopbackHostHeader(req.headers.host ?? "")) {
874
+ return { ok: false, code: 1008, reason: "untrusted host header" };
875
+ }
876
+ if (!tokenOk(tokenFromRequest(req))) {
877
+ return { ok: false, code: 1008, reason: "unauthorized" };
878
+ }
879
+ if (liveConnections >= maxConnections) {
880
+ return { ok: false, code: 1013, reason: "too many connections" };
881
+ }
882
+ return OK;
883
+ }
884
+ };
885
+ }
886
+
887
+ // src/subcommands/handlers/acp-server.ts
888
+ function pendingLeaderDeliveries(sessionId) {
889
+ return leaderDeliveryHub.pending(sessionId);
890
+ }
683
891
  function parseWsPort(flag) {
684
892
  if (flag === void 0 || flag === false) return null;
685
893
  if (flag === true || flag === "true") return 8889;
@@ -823,370 +1031,166 @@ async function runACPServer(deps) {
823
1031
  deps.renderer.writeError(
824
1032
  "No model provider configured. Run `wstack auth` before starting ACP, or use `--echo` for a connectivity test.\n"
825
1033
  );
826
- return 1;
827
- }
828
- let stdioHqTelemetry;
829
- const server = new WrongStackACPServer(
830
- echo ? {} : (() => {
831
- let agentFor;
832
- try {
833
- agentFor = buildAcpServerAgentFactory(deps);
834
- } catch (err) {
835
- if (err instanceof AcpServerConfigError) {
836
- deps.renderer.writeError(`${err.message}
837
- `);
838
- }
839
- throw err;
840
- }
841
- stdioHqTelemetry = startAcpHqTelemetry({
842
- projectRoot: deps.cwd ?? process.cwd(),
843
- projectName: path2.basename(deps.cwd ?? process.cwd()),
844
- appConfig: deps.config
845
- });
846
- const stdioAgentFactory = agentFor;
847
- const turn = makeACPServerAgentTurn({
848
- agentFor: stdioHqTelemetry.wrapAgentFactory(agentFor),
849
- pendingDeliveries: pendingLeaderDeliveries
850
- });
851
- const store = deps.paths?.projectDir ? new ACPSessionStore({ dir: path2.join(deps.paths.projectDir, "acp-sessions") }) : void 0;
852
- return {
853
- runTurn: turn,
854
- replayFor: turn.replay,
855
- seedFor: turn.seed,
856
- disposeFor: composeDispose(
857
- stdioHqTelemetry.wrapDispose(turn.dispose),
858
- stdioAgentFactory
859
- ),
860
- ...store ? { store } : {}
861
- };
862
- })()
863
- );
864
- if (echo) {
865
- deps.renderer.writeInfo(
866
- "ACP server starting in --echo mode (no-op turn; no provider needed).\n"
867
- );
868
- } else {
869
- deps.renderer.writeInfo(
870
- `Starting WrongStack ACP server (${deps.config.provider}/${deps.config.model})\u2026
871
- `
872
- );
873
- deps.renderer.writeInfo(
874
- "Waiting for an ACP client connection on stdin/stdout. Press Ctrl+C to stop.\n"
875
- );
876
- }
877
- createGracefulShutdown({
878
- run: async () => {
879
- try {
880
- await server.stop();
881
- } catch (err) {
882
- deps.renderer.writeError(
883
- `ACP stop failed: ${err instanceof Error ? err.message : String(err)}
884
- `
885
- );
886
- }
887
- stdioHqTelemetry?.stop();
888
- }
889
- }).install();
890
- try {
891
- await server.start();
892
- } catch (err) {
893
- deps.renderer.writeError(
894
- `ACP server error: ${err instanceof Error ? err.message : String(err)}
895
- `
896
- );
897
- return 1;
898
- }
899
- return 0;
900
- }
901
- async function listACPAgents(deps) {
902
- const detected = await new EnsembleRegistry().list();
903
- const live = await loadLive(deps);
904
- deps.renderer.write(`${formatAcpAgentList({ live, detected })}
905
- `);
906
- return 0;
907
- }
908
- async function syncACPRegistry(deps) {
909
- if (!deps.paths) {
910
- deps.renderer.writeError("Cannot sync: no cache directory available.\n");
911
- return 1;
912
- }
913
- deps.renderer.writeInfo("Fetching the official ACP registry\u2026\n");
914
- try {
915
- const { count, location } = await refreshAcpRegistry(deps.paths);
916
- deps.renderer.write(`Synced ${count} agents from the official ACP registry.
917
- `);
918
- deps.renderer.writeInfo(`Cached at ${location}
919
- `);
920
- return 0;
921
- } catch (err) {
922
- deps.renderer.writeError(
923
- `ACP registry sync failed: ${err instanceof Error ? err.message : String(err)}
924
- `
925
- );
926
- deps.renderer.write("The bundled offline catalog is still available via `wstack acp list`.\n");
927
- return 1;
928
- }
929
- }
930
- async function spawnACPAgent(args, deps) {
931
- const [subagentId, ...taskParts] = args;
932
- if (!subagentId) {
933
- deps.renderer.writeError("Usage: wstack acp spawn <agent-id> <task>\n");
934
- deps.renderer.write("Run `wstack acp list` to see available agents.\n");
935
- return 1;
936
- }
937
- const task = taskParts.join(" ");
938
- if (!task) {
939
- deps.renderer.writeError("Usage: wstack acp spawn <agent-id> <task>\n");
940
- deps.renderer.write("Task description is required.\n");
941
- return 1;
942
- }
943
- const live = await loadLive(deps);
944
- const cmd = resolveAcpAgentCommand(subagentId, acpOverrides(deps), live?.byId);
945
- if (!cmd) {
946
- deps.renderer.writeError(`Unknown ACP agent: ${subagentId}
947
- `);
948
- deps.renderer.write("Run `wstack acp list` (or `wstack acp sync`) to see available agents.\n");
949
- return 1;
950
- }
951
- deps.renderer.writeInfo(`Spawning ACP agent '${subagentId}'\u2026
952
- `);
953
- const ac = new AbortController();
954
- const cleanup = () => ac.abort();
955
- process.on("SIGINT", cleanup);
956
- process.on("SIGTERM", cleanup);
957
- try {
958
- deps.renderer.writeInfo("Running task\u2026\n");
959
- const result = await runOneAcpTask({
960
- command: cmd.command,
961
- ...cmd.args !== void 0 ? { args: cmd.args } : {},
962
- ...cmd.env !== void 0 ? { env: cmd.env } : {},
963
- role: subagentId,
964
- task,
965
- signal: ac.signal,
966
- permissionPolicy: defaultPermissionPolicy,
967
- onProgress: (event) => {
968
- const line = formatProgress(event);
969
- if (line) deps.renderer.writeInfo(` ${line}
970
- `);
971
- }
972
- });
973
- deps.renderer.write("\n--- Result ---\n");
974
- deps.renderer.write(result.result.length > 0 ? result.result : "no result");
975
- deps.renderer.write("\n---------------\n");
976
- deps.renderer.writeInfo(
977
- `Done. iterations=${result.iterations} toolCalls=${result.toolCalls}
978
- `
979
- );
980
- return 0;
981
- } catch (err) {
982
- const e = err;
983
- const detail = e.kind ? `[${e.kind}] ` : "";
984
- const message = e.message ?? (err instanceof Error ? err.message : String(err));
985
- deps.renderer.writeError(`ACP agent error: ${detail}${message}
986
- `);
987
- return 1;
988
- } finally {
989
- process.off("SIGINT", cleanup);
990
- process.off("SIGTERM", cleanup);
991
- }
992
- }
993
- function formatProgress(event) {
994
- switch (event.type) {
995
- case "tool_call":
996
- return `\u25B8 ${event.toolCall.title} (${event.toolCall.status})`;
997
- case "tool_call_update":
998
- return event.toolCall.status === "completed" || event.toolCall.status === "failed" ? ` \u21B3 ${event.toolCall.title}: ${event.toolCall.status}` : "";
999
- case "diff":
1000
- return `\u270E ${event.diff.path}${event.diff.oldText === null ? " (new)" : ""}`;
1001
- case "plan":
1002
- return `\u2630 plan: ${event.entries.length} step(s)`;
1003
- default:
1004
- return "";
1005
- }
1006
- }
1007
- async function parallelACPAgents(args, deps) {
1008
- const [csv, ...taskParts] = args;
1009
- if (!csv) {
1010
- deps.renderer.writeError("Usage: wstack acp parallel <agent-id-csv> <task>\n");
1011
- deps.renderer.write('Example: wstack acp parallel claude-code,gemini-cli "review this diff"\n');
1012
- return 1;
1013
- }
1014
- const task = taskParts.join(" ");
1015
- if (!task) {
1016
- deps.renderer.writeError("Usage: wstack acp parallel <agent-id-csv> <task>\n");
1017
- deps.renderer.writeError("Task description is required.\n");
1018
- return 1;
1019
- }
1020
- const ac = new AbortController();
1021
- const onSignal = () => ac.abort();
1022
- process.on("SIGINT", onSignal);
1023
- process.on("SIGTERM", onSignal);
1024
- try {
1025
- const overrides = acpOverrides(deps);
1026
- const live = await loadLive(deps);
1027
- const result = await runEnsemble({
1028
- agentIds: csv,
1029
- task,
1030
- resolveCmd: (id) => resolveAcpAgentCommand(id, overrides, live?.byId),
1031
- signal: ac.signal,
1032
- onProgress: (agentId, event) => {
1033
- const line = formatProgress(event);
1034
- if (line) deps.renderer.writeInfo(` [${agentId}] ${line}
1035
- `);
1036
- }
1037
- });
1038
- const skipped = result.results.filter((r) => r.status === "skipped");
1039
- if (skipped.length > 0) {
1040
- deps.renderer.writeWarning(
1041
- `Skipping ${skipped.length} agent(s) not installed: ${skipped.map((s) => `${s.agentId} (${s.reason ?? "not installed"})`).join(", ")}
1042
- `
1043
- );
1044
- }
1045
- if (result.summary.succeeded + result.summary.failed + result.summary.cancelled === 0) {
1046
- deps.renderer.writeError("No installed agents to run.\n");
1047
- deps.renderer.write("Run `wstack acp list` to see what is available.\n");
1048
- return 1;
1049
- }
1050
- const fannedOut = result.results.filter((r) => r.status !== "skipped").map((r) => r.agentId).join(", ");
1051
- deps.renderer.writeInfo(
1052
- `Fanning out to ${result.summary.succeeded + result.summary.failed + result.summary.cancelled} agent(s): ${fannedOut}
1053
- `
1054
- );
1055
- deps.renderer.writeInfo(`Task: ${result.task}
1056
-
1057
- `);
1058
- for (const r of result.results) {
1059
- if (r.status === "skipped") continue;
1060
- deps.renderer.write(`
1061
- === ${r.agentId} ===
1034
+ return 1;
1035
+ }
1036
+ let stdioHqTelemetry;
1037
+ const server = new WrongStackACPServer(
1038
+ echo ? {} : (() => {
1039
+ let agentFor;
1040
+ try {
1041
+ agentFor = buildAcpServerAgentFactory(deps);
1042
+ } catch (err) {
1043
+ if (err instanceof AcpServerConfigError) {
1044
+ deps.renderer.writeError(`${err.message}
1062
1045
  `);
1063
- if (r.status === "success") {
1064
- deps.renderer.write(r.result && r.result.length > 0 ? r.result : "(no result)");
1065
- deps.renderer.write(
1066
- `
1067
- [${r.agentId}] success ${r.durationMs}ms iterations=${r.iterations} toolCalls=${r.toolCalls}
1068
- `
1069
- );
1070
- } else if (r.status === "failed") {
1071
- deps.renderer.writeError(
1072
- `[${r.error?.kind ?? "unknown"}] ${r.error?.message ?? "failed"}
1046
+ }
1047
+ throw err;
1048
+ }
1049
+ stdioHqTelemetry = startAcpHqTelemetry({
1050
+ projectRoot: deps.cwd ?? process.cwd(),
1051
+ projectName: path2.basename(deps.cwd ?? process.cwd()),
1052
+ appConfig: deps.config
1053
+ });
1054
+ const stdioAgentFactory = agentFor;
1055
+ const turn = makeACPServerAgentTurn({
1056
+ agentFor: stdioHqTelemetry.wrapAgentFactory(agentFor),
1057
+ pendingDeliveries: pendingLeaderDeliveries
1058
+ });
1059
+ const store = deps.paths?.projectDir ? new ACPSessionStore({ dir: path2.join(deps.paths.projectDir, "acp-sessions") }) : void 0;
1060
+ return {
1061
+ runTurn: turn,
1062
+ replayFor: turn.replay,
1063
+ seedFor: turn.seed,
1064
+ disposeFor: composeDispose(
1065
+ stdioHqTelemetry.wrapDispose(turn.dispose),
1066
+ stdioAgentFactory
1067
+ ),
1068
+ ...store ? { store } : {}
1069
+ };
1070
+ })()
1071
+ );
1072
+ if (echo) {
1073
+ deps.renderer.writeInfo(
1074
+ "ACP server starting in --echo mode (no-op turn; no provider needed).\n"
1075
+ );
1076
+ } else {
1077
+ deps.renderer.writeInfo(
1078
+ `Starting WrongStack ACP server (${deps.config.provider}/${deps.config.model})\u2026
1073
1079
  `
1074
- );
1075
- deps.renderer.write(`[${r.agentId}] failed ${r.durationMs}ms
1076
- `);
1077
- } else {
1080
+ );
1081
+ deps.renderer.writeInfo(
1082
+ "Waiting for an ACP client connection on stdin/stdout. Press Ctrl+C to stop.\n"
1083
+ );
1084
+ }
1085
+ createGracefulShutdown({
1086
+ run: async () => {
1087
+ try {
1088
+ await server.stop();
1089
+ } catch (err) {
1078
1090
  deps.renderer.writeError(
1079
- `[${r.error?.kind ?? "aborted"}] ${r.error?.message ?? "cancelled"}
1091
+ `ACP stop failed: ${err instanceof Error ? err.message : String(err)}
1080
1092
  `
1081
1093
  );
1082
- deps.renderer.write(`[${r.agentId}] cancelled ${r.durationMs}ms
1083
- `);
1084
1094
  }
1095
+ stdioHqTelemetry?.stop();
1085
1096
  }
1086
- const { succeeded, failed, cancelled, skipped: skip } = result.summary;
1087
- deps.renderer.write(
1088
- `
1089
- Parallel summary: ${succeeded} succeeded, ${failed} failed, ${cancelled} cancelled, ${skip} skipped.
1097
+ }).install();
1098
+ try {
1099
+ await server.start();
1100
+ } catch (err) {
1101
+ deps.renderer.writeError(
1102
+ `ACP server error: ${err instanceof Error ? err.message : String(err)}
1090
1103
  `
1091
1104
  );
1092
- return succeeded > 0 ? 0 : 1;
1093
- } finally {
1094
- process.off("SIGINT", onSignal);
1095
- process.off("SIGTERM", onSignal);
1105
+ return 1;
1096
1106
  }
1107
+ return 0;
1097
1108
  }
1098
- async function probeACPAgents(args, deps) {
1099
- const csv = args.join(" ");
1100
- let ids;
1101
- if (csv) {
1102
- ids = csv.split(",").flatMap((s) => s.split(/\s+/)).map((s) => s.trim()).filter(Boolean);
1103
- } else {
1104
- const detected = await new EnsembleRegistry().list();
1105
- ids = detected.filter((a) => a.installed).map((a) => a.id);
1109
+
1110
+ // src/subcommands/handlers/acp.ts
1111
+ var acpCmd = async (args, deps) => {
1112
+ const sub = args[0];
1113
+ const loginIndex = sub === "auth" ? 0 : (sub === "server" || sub === "serve") && args[1] === "auth" ? 1 : -1;
1114
+ if (loginIndex >= 0) {
1115
+ const { authCmd } = await import("./auth-AED7RW7D.js");
1116
+ return authCmd(args.slice(loginIndex + 1), deps);
1106
1117
  }
1107
- if (ids.length === 0) {
1108
- deps.renderer.writeError("No installed agents to probe.\n");
1109
- return 1;
1118
+ if (!sub || sub === "server" || sub === "serve") {
1119
+ return runACPServer(deps);
1110
1120
  }
1111
- const overrides = acpOverrides(deps);
1112
- const live = await loadLive(deps);
1113
- deps.renderer.writeInfo(
1114
- `Probing ${ids.length} agent(s)\u2026 (npx-based agents may download on first run)
1115
- `
1116
- );
1117
- const results = await probeAcpAgents({
1118
- agentIds: ids,
1119
- resolveCmd: (id) => resolveAcpAgentCommand(id, overrides, live?.byId),
1120
- projectRoot: deps.cwd ?? process.cwd(),
1121
- onProgress: (id, r) => deps.renderer.writeInfo(` ${r.ok ? "\u2713" : "\u2717"} ${id} (${r.ms}ms)
1122
- `)
1123
- });
1124
- deps.renderer.write("\nACP handshake probe:\n\n");
1125
- for (const r of results) {
1126
- if (r.ok) {
1127
- const info = r.agentInfo ? ` \u2014 ${r.agentInfo.name} ${r.agentInfo.version}` : "";
1128
- deps.renderer.write(` \u2713 ${r.id.padEnd(16)} ok ${r.ms}ms${info}
1129
- `);
1130
- } else {
1131
- deps.renderer.write(` \u2717 ${r.id.padEnd(16)} ${r.error ?? "failed"} (${r.ms}ms)
1121
+ if (sub === "help") {
1122
+ deps.renderer.write(`wstack acp \u2014 ACP (Agent Client Protocol) integration
1123
+
1124
+ Usage:
1125
+ wstack acp Start WrongStack as an ACP server (blocks)
1126
+ wstack acp server Same as above
1127
+ wstack acp list List available ACP agents
1128
+ wstack acp sync Pull the official agentclientprotocol/registry into cache
1129
+ wstack acp spawn <id> <task>
1130
+ Spawn an ACP agent as a subagent and wait for result
1131
+ wstack acp parallel <agent-id-csv> <task>
1132
+ Fan a task out to multiple ACP agents in parallel
1133
+ and aggregate the results
1134
+ wstack acp probe [agent-id-csv]
1135
+ Handshake-test agents (bounded concurrency). Defaults
1136
+ to all installed agents.
1137
+ wstack acp bench [agent-id-csv] [--fs]
1138
+ End-to-end verify each agent (handshake \u2192 prompt \u2192
1139
+ marker, optional fs check) and print a graded report.
1140
+ Defaults to all installed agents.
1141
+ wstack acp help Show this help
1142
+
1143
+ ACP Mode:
1144
+ When run as \`wstack acp\`. WrongStack acts as an ACP-compatible agent driven
1145
+ by your configured model provider. ACP clients (Zed, JetBrains, VS Code)
1146
+ spawn it as a subprocess and communicate over stdio JSON-RPC. Run
1147
+ \`wstack auth\` first to configure a provider, or pass \`--echo\` for a no-op
1148
+ connectivity test that needs no provider. Press Ctrl+C to stop.
1149
+
1150
+ Transports:
1151
+ (default) stdio JSON-RPC (the usual editor-spawned-subprocess mode)
1152
+ --ws[=port] serve over WebSocket on 127.0.0.1:<port> (default 8889) \u2014
1153
+ full-duplex, so updates/permission prompts stream live.
1154
+
1155
+ spawn:
1156
+ Spawns a named ACP agent (claude-code, gemini-cli, codex-cli, copilot,
1157
+ cline, goose, openhands, qwen-code, kiro-cli, opencode, mistral-vibe,
1158
+ cursor) with the given task and waits for its result.
1159
+ Example: wstack acp spawn cline "fix the login bug"
1160
+
1161
+ parallel:
1162
+ Runs the same task on a comma-separated list of ACP agents concurrently.
1163
+ Example: wstack acp parallel claude-code,gemini-cli,codex-cli "review this diff"
1164
+ Each agent's result is rendered under a clearly-marked header. Returns 0
1165
+ if at least one agent succeeds, 1 if all fail. Agents that aren't
1166
+ installed are skipped with a warning.
1132
1167
  `);
1133
- }
1168
+ return 0;
1134
1169
  }
1135
- const ok = results.filter((r) => r.ok).length;
1136
- deps.renderer.write(`
1137
- ${ok} of ${results.length} agents completed the ACP handshake.
1138
- `);
1139
- return ok > 0 ? 0 : 1;
1140
- }
1141
- async function benchACPAgents(args, deps) {
1142
- const checkFs = deps.flags?.fs === true || deps.flags?.fs === "true" || args.includes("--fs");
1143
- const csv = args.filter((a) => a !== "--fs").join(" ");
1144
- let ids;
1145
- if (csv) {
1146
- ids = csv.split(",").flatMap((s) => s.split(/\s+/)).map((s) => s.trim()).filter(Boolean);
1147
- } else {
1148
- const detected = await new EnsembleRegistry().list();
1149
- ids = detected.filter((a) => a.installed).map((a) => a.id);
1170
+ if (sub === "list") {
1171
+ return listACPAgents(deps);
1150
1172
  }
1151
- if (ids.length === 0) {
1152
- deps.renderer.writeError("No installed agents to bench.\n");
1153
- deps.renderer.write("Pass ids explicitly: `wstack acp bench gemini-cli,codex-cli`.\n");
1154
- return 1;
1173
+ if (sub === "sync") {
1174
+ return syncACPRegistry(deps);
1155
1175
  }
1156
- const ac = new AbortController();
1157
- const onSignal = () => ac.abort();
1158
- process.on("SIGINT", onSignal);
1159
- process.on("SIGTERM", onSignal);
1160
- try {
1161
- const overrides = acpOverrides(deps);
1162
- const live = await loadLive(deps);
1163
- deps.renderer.writeInfo(
1164
- `Benching ${ids.length} agent(s)${checkFs ? " (with fs check)" : ""}: ${ids.join(", ")}\u2026
1165
- `
1166
- );
1167
- const result = await runAcpBench({
1168
- agentIds: ids,
1169
- resolveCmd: (id) => resolveAcpAgentCommand(id, overrides, live?.byId),
1170
- projectRoot: deps.cwd ?? process.cwd(),
1171
- checkFs,
1172
- signal: ac.signal,
1173
- onProgress: (agentId, phase, r) => {
1174
- if (phase === "start") deps.renderer.writeInfo(` \u25B8 ${agentId}\u2026
1175
- `);
1176
- else if (r) deps.renderer.writeInfo(` \u21B3 ${agentId}: ${r.status}
1177
- `);
1178
- }
1179
- });
1180
- deps.renderer.write(`
1181
- ${renderAcpBenchText(result)}
1182
- `);
1183
- return result.summary.pass > 0 ? 0 : 1;
1184
- } finally {
1185
- process.off("SIGINT", onSignal);
1186
- process.off("SIGTERM", onSignal);
1176
+ if (sub === "spawn") {
1177
+ return spawnACPAgent(args.slice(1), deps);
1187
1178
  }
1188
- }
1179
+ if (sub === "parallel") {
1180
+ return parallelACPAgents(args.slice(1), deps);
1181
+ }
1182
+ if (sub === "probe") {
1183
+ return probeACPAgents(args.slice(1), deps);
1184
+ }
1185
+ if (sub === "bench") {
1186
+ return benchACPAgents(args.slice(1), deps);
1187
+ }
1188
+ deps.renderer.writeError(`Unknown acp subcommand: ${sub}
1189
+ `);
1190
+ deps.renderer.write("Run `wstack acp help` for usage.\n");
1191
+ return 1;
1192
+ };
1189
1193
  export {
1190
1194
  acpCmd
1191
1195
  };
1192
- //# sourceMappingURL=acp-EDGUWU7N.js.map
1196
+ //# sourceMappingURL=acp-QLU7T5WX.js.map