@wrongstack/cli 1.1.2 → 1.1.5

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 (117) hide show
  1. package/dist/{acp-HCGUTFNE.js → acp-NKUEYJDZ.js} +14 -5
  2. package/dist/acp-leader/acp-leader-confirm.d.ts +20 -0
  3. package/dist/acp-leader/acp-leader-context.d.ts +29 -0
  4. package/dist/acp-leader/acp-leader-fleet.d.ts +26 -0
  5. package/dist/acp-leader/acp-leader-host-binding.d.ts +17 -0
  6. package/dist/acp-leader/acp-leader-host.d.ts +82 -0
  7. package/dist/acp-leader/acp-leader-picker.d.ts +24 -0
  8. package/dist/acp-leader/acp-leader-provider.d.ts +85 -0
  9. package/dist/acp-leader/acp-leader-resume.d.ts +10 -0
  10. package/dist/acp-leader/acp-leader-stream.d.ts +83 -0
  11. package/dist/{auth-ANOVZVHE.js → auth-2ZG5DBHS.js} +62 -66
  12. package/dist/auth-menu/index.d.ts +1 -2
  13. package/dist/boot/dispatch-webui.d.ts +16 -0
  14. package/dist/boot/launch-menu-arrow.d.ts +20 -0
  15. package/dist/boot/launch-menu-types.d.ts +36 -0
  16. package/dist/boot/launch-menu.d.ts +1 -57
  17. package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
  18. package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
  19. package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
  20. package/dist/chunk-3LEIFO5D.js +65 -0
  21. package/dist/chunk-5SGMUDP5.js +752 -0
  22. package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
  23. package/dist/{chunk-GRAGMA4B.js → chunk-7BM4LGKQ.js} +18 -18
  24. package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
  25. package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
  26. package/dist/{chunk-5TRZZHMN.js → chunk-JCZ2D3VV.js} +4 -65
  27. package/dist/{chunk-AWRZWPJ3.js → chunk-JFYQGMMM.js} +99 -75
  28. package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
  29. package/dist/chunk-LUEO5HLZ.js +50 -0
  30. package/dist/chunk-LYWOV4OX.js +252 -0
  31. package/dist/chunk-M7FNHCE3.js +2070 -0
  32. package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
  33. package/dist/{chunk-NMXQFQ26.js → chunk-MEBXWI7U.js} +5 -5
  34. package/dist/{chunk-RAWHRRUJ.js → chunk-MKMM5RIW.js} +209 -201
  35. package/dist/{chunk-3R3OADC5.js → chunk-P7MEEUGT.js} +281 -252
  36. package/dist/chunk-QLYPIJ7X.js +258 -0
  37. package/dist/chunk-RN5CFAVQ.js +292 -0
  38. package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
  39. package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
  40. package/dist/chunk-VEESMPTX.js +254 -0
  41. package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
  42. package/dist/{cli-context-YIT4FEDM.js → cli-context-CQYGIWOU.js} +254 -237
  43. package/dist/{cli-main-JIADJ5OL.js → cli-main-VRUJNUBX.js} +2081 -1594
  44. package/dist/cli-main-boot.d.ts +1 -1
  45. package/dist/config-doctor-known-keys.d.ts +33 -0
  46. package/dist/config-doctor.d.ts +2 -47
  47. package/dist/config-history-support.d.ts +36 -0
  48. package/dist/config-history.d.ts +1 -1
  49. package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
  50. package/dist/{execution-BAP566QC.js → execution-IV72RUSM.js} +5354 -5258
  51. package/dist/execution-tui.d.ts +30 -0
  52. package/dist/fleet/acp-live-steer.d.ts +28 -0
  53. package/dist/fleet/acp-permission-bridge.d.ts +49 -0
  54. package/dist/fleet/acp-quota-hold.d.ts +3 -0
  55. package/dist/fleet/acp-quota-reading.d.ts +3 -0
  56. package/dist/fleet/acp-turn-reminders.d.ts +15 -0
  57. package/dist/fleet/acp-wrongstack-bridge.d.ts +50 -0
  58. package/dist/fleet/host-acp.d.ts +23 -2
  59. package/dist/fleet/wrongforge-client.d.ts +116 -0
  60. package/dist/fleet/wrongforge-gateway.d.ts +20 -0
  61. package/dist/fleet/wrongforge-info.d.ts +32 -0
  62. package/dist/fleet/wrongforge-quota.d.ts +10 -0
  63. package/dist/fleet/wrongforge-runner.d.ts +37 -0
  64. package/dist/fleet/wrongforge-runtime.d.ts +10 -0
  65. package/dist/{hq-JD5AZVMG.js → hq-54PQYR5O.js} +311 -307
  66. package/dist/hq-server/sweeps.d.ts +28 -0
  67. package/dist/{hq-server-BI23EPM7.js → hq-server-YK7FBLL6.js} +3 -3
  68. package/dist/index.js +8 -5
  69. package/dist/mcp-22CFBX4H.js +236 -0
  70. package/dist/mcp-serve-project.d.ts +18 -0
  71. package/dist/mcp-serve.d.ts +0 -63
  72. package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
  73. package/dist/{per-subcommand-help-D6J4ZXW2.js → per-subcommand-help-MQGERD4E.js} +2 -2
  74. package/dist/plugin-llm-command.d.ts +9 -0
  75. package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
  76. package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
  77. package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
  78. package/dist/services/sdd/state.d.ts +2 -0
  79. package/dist/session-event-wiring-types.d.ts +62 -0
  80. package/dist/session-event-wiring.d.ts +2 -52
  81. package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
  82. package/dist/short-circuit-flags-AGHURHMQ.js +11 -0
  83. package/dist/slash-commands/command-context-fleet.d.ts +187 -0
  84. package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
  85. package/dist/slash-commands/command-context.d.ts +3 -282
  86. package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
  87. package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
  88. package/dist/subcommands/handlers/hq-token.d.ts +3 -0
  89. package/dist/subcommands/handlers/hq.d.ts +0 -21
  90. package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
  91. package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
  92. package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
  93. package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
  94. package/dist/{subcommands-VTMIZQBP.js → subcommands-OCJNO57E.js} +2 -2
  95. package/dist/tool-gateway-http.d.ts +30 -0
  96. package/dist/tool-gateway.d.ts +185 -0
  97. package/dist/tools-cli-44MLIUQO.js +509 -0
  98. package/dist/tools-serve-TRCET2M7.js +102 -0
  99. package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
  100. package/dist/tuneup-runtime-checks.d.ts +7 -0
  101. package/dist/tuneup-types.d.ts +118 -0
  102. package/dist/tuneup.d.ts +3 -122
  103. package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
  104. package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
  105. package/dist/{webui-server-HIUY3RHD.js → webui-server-V2R5WQUK.js} +79 -3
  106. package/dist/wiring/provider-runtime-setup.d.ts +3 -1
  107. package/dist/wiring/session-runtime.d.ts +1 -1
  108. package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
  109. package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
  110. package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
  111. package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +49 -0
  112. package/package.json +34 -30
  113. package/dist/auth-menu/anthropic-oauth.d.ts +0 -43
  114. package/dist/chunk-XT3NAHRX.js +0 -337
  115. package/dist/chunk-ZNNFEKDZ.js +0 -319
  116. package/dist/mcp-2XWBJEQN.js +0 -464
  117. package/dist/short-circuit-flags-CPCT5MWB.js +0 -11
@@ -0,0 +1,509 @@
1
+ import {
2
+ createToolGateway,
3
+ parseCapabilityGrants,
4
+ resolveGatewayRegistry,
5
+ withheldReason
6
+ } from "./chunk-LYWOV4OX.js";
7
+ import "./chunk-W23PY4IJ.js";
8
+
9
+ // src/subcommands/handlers/tools-cli.ts
10
+ import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
11
+
12
+ // src/subcommands/handlers/tools-cli-args.ts
13
+ import * as fs from "node:fs/promises";
14
+ import * as path from "node:path";
15
+ import { toErrorMessage } from "@wrongstack/core/utils";
16
+ var ToolsCliUsageError = class extends Error {
17
+ };
18
+ var TOOLS_CLI_MAX_INPUT_BYTES = 8 * 1024 * 1024;
19
+ var TOOLS_CLI_MAX_BATCH_CALLS = 500;
20
+ async function readInputText(raw, cwd, readStdin) {
21
+ if (raw === void 0 || raw === false) return void 0;
22
+ let text;
23
+ let source;
24
+ if (raw === true || raw === "-") {
25
+ text = await readStdin();
26
+ source = "stdin";
27
+ } else if (raw.startsWith("@")) {
28
+ const file = path.resolve(cwd, raw.slice(1));
29
+ const stat2 = await fs.stat(file).catch((err) => {
30
+ throw new ToolsCliUsageError(`--input ${raw}: ${toErrorMessage(err)}`);
31
+ });
32
+ if (!stat2.isFile()) throw new ToolsCliUsageError(`--input ${raw}: not a regular file`);
33
+ if (stat2.size > TOOLS_CLI_MAX_INPUT_BYTES) {
34
+ throw new ToolsCliUsageError(
35
+ `--input ${raw}: larger than ${TOOLS_CLI_MAX_INPUT_BYTES} bytes`
36
+ );
37
+ }
38
+ text = await fs.readFile(file, "utf8");
39
+ source = raw;
40
+ } else {
41
+ text = raw;
42
+ source = "--input";
43
+ }
44
+ if (text.charCodeAt(0) === 65279) text = text.slice(1);
45
+ return { text, source };
46
+ }
47
+ function parseInputObject(text, source) {
48
+ const trimmed = text.trim();
49
+ if (trimmed === "") return {};
50
+ let parsed;
51
+ try {
52
+ parsed = JSON.parse(trimmed);
53
+ } catch (err) {
54
+ throw new ToolsCliUsageError(`${source} is not valid JSON: ${toErrorMessage(err)}`);
55
+ }
56
+ if (!isPlainObject(parsed)) {
57
+ throw new ToolsCliUsageError(
58
+ `${source} must be a JSON object (the tool's input), not ${kindOf(parsed)}`
59
+ );
60
+ }
61
+ return parsed;
62
+ }
63
+ function parseSchemaArgs(tokens, schema) {
64
+ const properties = schema?.properties ?? {};
65
+ const out = {};
66
+ for (let i = 0; i < tokens.length; i++) {
67
+ const token = tokens[i] ?? "";
68
+ if (!token.startsWith("--") || token === "--") {
69
+ throw new ToolsCliUsageError(
70
+ `unexpected argument "${token}" \u2014 tool fields are passed as --<field> <value>`
71
+ );
72
+ }
73
+ const eq = token.indexOf("=");
74
+ let key = eq === -1 ? token.slice(2) : token.slice(2, eq);
75
+ let inline = eq === -1 ? void 0 : token.slice(eq + 1);
76
+ let field = resolveField(key, properties);
77
+ if (!field && key.startsWith("no-") && inline === void 0) {
78
+ const negated = resolveField(key.slice(3), properties);
79
+ if (negated && primaryType(negated.schema) === "boolean") {
80
+ field = negated;
81
+ key = key.slice(3);
82
+ inline = "false";
83
+ }
84
+ }
85
+ if (!field) {
86
+ const names = Object.keys(properties);
87
+ throw new ToolsCliUsageError(
88
+ `unknown input field --${key}${names.length > 0 ? ` (fields: ${names.join(", ")})` : ""}`
89
+ );
90
+ }
91
+ const type = primaryType(field.schema);
92
+ let value = inline;
93
+ if (value === void 0) {
94
+ const next = tokens[i + 1];
95
+ if (type === "boolean" && (next === void 0 || next.startsWith("--"))) value = "true";
96
+ else if (next === void 0 || next.startsWith("--")) {
97
+ throw new ToolsCliUsageError(`--${key} needs a value`);
98
+ } else {
99
+ value = next;
100
+ i++;
101
+ }
102
+ }
103
+ const coerced = coerce(value, field.schema, `--${key}`);
104
+ if (type === "array" && !value.trim().startsWith("[")) {
105
+ const prior = out[field.name];
106
+ out[field.name] = Array.isArray(prior) ? [...prior, ...coerced] : coerced;
107
+ } else {
108
+ out[field.name] = coerced;
109
+ }
110
+ }
111
+ return out;
112
+ }
113
+ function parseBatchLines(text) {
114
+ const entries = [];
115
+ const lines = text.split(/\r?\n/);
116
+ for (let index = 0; index < lines.length; index++) {
117
+ const raw = lines[index]?.trim() ?? "";
118
+ if (raw === "") continue;
119
+ const line = index + 1;
120
+ if (entries.length >= TOOLS_CLI_MAX_BATCH_CALLS) {
121
+ throw new ToolsCliUsageError(
122
+ `batch has more than ${TOOLS_CLI_MAX_BATCH_CALLS} calls; split it into several runs`
123
+ );
124
+ }
125
+ let parsed;
126
+ try {
127
+ parsed = JSON.parse(raw);
128
+ } catch (err) {
129
+ entries.push({ line, error: `line ${line} is not valid JSON: ${toErrorMessage(err)}` });
130
+ continue;
131
+ }
132
+ if (!isPlainObject(parsed)) {
133
+ entries.push({ line, error: `line ${line} must be a JSON object, not ${kindOf(parsed)}` });
134
+ continue;
135
+ }
136
+ const id = parsed["id"];
137
+ const tool = parsed["tool"];
138
+ const input = parsed["input"] ?? {};
139
+ if (typeof tool !== "string" || tool === "") {
140
+ entries.push({ line, id, error: `line ${line} needs a "tool" name` });
141
+ } else if (!isPlainObject(input)) {
142
+ entries.push({ line, id, error: `line ${line}: "input" must be a JSON object` });
143
+ } else {
144
+ entries.push({ line, id, tool, input });
145
+ }
146
+ }
147
+ return entries;
148
+ }
149
+ async function readStream(stream, maxBytes) {
150
+ const chunks = [];
151
+ let total = 0;
152
+ for await (const chunk of stream) {
153
+ const buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
154
+ total += buf.byteLength;
155
+ if (total > maxBytes) throw new ToolsCliUsageError(`stdin input larger than ${maxBytes} bytes`);
156
+ chunks.push(buf);
157
+ }
158
+ return Buffer.concat(chunks).toString("utf8");
159
+ }
160
+ function resolveField(key, properties) {
161
+ if (Object.hasOwn(properties, key)) return { name: key, schema: properties[key] ?? {} };
162
+ const camel = key.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
163
+ if (Object.hasOwn(properties, camel)) return { name: camel, schema: properties[camel] ?? {} };
164
+ const snake = key.replace(/-/g, "_");
165
+ if (Object.hasOwn(properties, snake)) return { name: snake, schema: properties[snake] ?? {} };
166
+ return void 0;
167
+ }
168
+ function primaryType(schema) {
169
+ const type = schema.type;
170
+ if (typeof type === "string") return type;
171
+ if (Array.isArray(type))
172
+ return type.find((t) => typeof t === "string" && t !== "null");
173
+ return void 0;
174
+ }
175
+ function coerce(value, schema, label) {
176
+ switch (primaryType(schema)) {
177
+ case "string":
178
+ return value;
179
+ case "integer":
180
+ case "number": {
181
+ const n = Number(value);
182
+ const integer = primaryType(schema) === "integer";
183
+ if (value.trim() === "" || !Number.isFinite(n) || integer && !Number.isInteger(n)) {
184
+ throw new ToolsCliUsageError(
185
+ `${label} expects ${integer ? "an integer" : "a number"}, got "${value}"`
186
+ );
187
+ }
188
+ return n;
189
+ }
190
+ case "boolean":
191
+ if (value === "true") return true;
192
+ if (value === "false") return false;
193
+ throw new ToolsCliUsageError(`${label} expects true or false, got "${value}"`);
194
+ case "array": {
195
+ if (value.trim().startsWith("[")) return parseJsonValue(value, label);
196
+ const items = schema.items ?? {};
197
+ return [coerce(value, items, label)];
198
+ }
199
+ case "object":
200
+ return parseJsonValue(value, label);
201
+ default: {
202
+ try {
203
+ return JSON.parse(value);
204
+ } catch {
205
+ return value;
206
+ }
207
+ }
208
+ }
209
+ }
210
+ function parseJsonValue(value, label) {
211
+ try {
212
+ return JSON.parse(value);
213
+ } catch (err) {
214
+ throw new ToolsCliUsageError(`${label} expects JSON: ${toErrorMessage(err)}`);
215
+ }
216
+ }
217
+ function isPlainObject(value) {
218
+ return typeof value === "object" && value !== null && !Array.isArray(value);
219
+ }
220
+ function kindOf(value) {
221
+ if (value === null) return "null";
222
+ if (Array.isArray(value)) return "an array";
223
+ return `a ${typeof value}`;
224
+ }
225
+
226
+ // src/subcommands/handlers/tools-cli.ts
227
+ var TOOLS_CLI_EXIT = {
228
+ ok: 0,
229
+ toolFailed: 1,
230
+ usage: 2,
231
+ notExposed: 3,
232
+ confirmRequired: 4
233
+ };
234
+ var processIo = {
235
+ stdout: (text) => process.stdout.write(text),
236
+ stderr: (text) => process.stderr.write(text),
237
+ readStdin: async () => {
238
+ if (process.stdin.isTTY) {
239
+ throw new ToolsCliUsageError("a bare --input reads from stdin, but stdin is a terminal");
240
+ }
241
+ return readStream(process.stdin, TOOLS_CLI_MAX_INPUT_BYTES);
242
+ }
243
+ };
244
+ async function runToolsCli(verb, args, deps, io = processIo) {
245
+ if (verb === "serve") {
246
+ const { serveToolsCommand } = await import("./tools-serve-TRCET2M7.js");
247
+ return serveToolsCommand(args, deps, io);
248
+ }
249
+ const flags = deps.flags ?? {};
250
+ const json = flags["json"] === true;
251
+ let allow;
252
+ try {
253
+ allow = parseCapabilityGrants(flags["allow"]);
254
+ } catch (err) {
255
+ io.stderr(`${toErrorMessage2(err)}
256
+ `);
257
+ return TOOLS_CLI_EXIT.usage;
258
+ }
259
+ const registry = resolveGatewayRegistry(deps.toolRegistry, deps.config);
260
+ const gateway = await createToolGateway({
261
+ registry,
262
+ config: deps.config,
263
+ cwd: deps.cwd,
264
+ projectRoot: deps.projectRoot,
265
+ yolo: flags["yolo"] === true || flags["allow-all"] === true,
266
+ allow,
267
+ whitelist: null,
268
+ oneShot: true,
269
+ onWarning: (message) => io.stderr(`wstack tools: ${message}
270
+ `)
271
+ });
272
+ try {
273
+ if (verb === "list") return listTools(gateway, json, io);
274
+ if (verb === "agent-guide") {
275
+ io.stdout(toolsAgentGuide(gateway));
276
+ return TOOLS_CLI_EXIT.ok;
277
+ }
278
+ if (verb === "batch") return await runBatch(gateway, registry, flags, deps.cwd, io);
279
+ const [name, ...rest] = args;
280
+ if (!name) {
281
+ io.stderr(
282
+ `Usage: wstack tools ${verb} <tool>${verb === "run" ? " [--input <json>] [-- --<field> <value> ...]" : ""}
283
+ `
284
+ );
285
+ return TOOLS_CLI_EXIT.usage;
286
+ }
287
+ if (verb === "describe") return describeTool(gateway, registry, name, json, io);
288
+ const fieldArgs = flags["input"] === true && rest[0] === "-" ? rest.slice(1) : rest;
289
+ const outcome = await runOne(gateway, registry, name, fieldArgs, flags, deps.cwd, io);
290
+ printRun(outcome, json, io);
291
+ return outcome.code;
292
+ } finally {
293
+ await gateway.close();
294
+ }
295
+ }
296
+ function listTools(gateway, json, io) {
297
+ if (json) {
298
+ io.stdout(
299
+ `${JSON.stringify({
300
+ mode: gateway.mode,
301
+ capabilities: gateway.capabilities,
302
+ tools: gateway.tools.map((t) => ({
303
+ name: t.name,
304
+ permission: t.permission,
305
+ capabilities: t.capabilities ?? [],
306
+ summary: summaryOf(t)
307
+ }))
308
+ })}
309
+ `
310
+ );
311
+ return TOOLS_CLI_EXIT.ok;
312
+ }
313
+ const width = Math.max(0, ...gateway.tools.map((t) => t.name.length)) + 2;
314
+ for (const t of gateway.tools) io.stdout(`${t.name.padEnd(width)}${summaryOf(t)}
315
+ `);
316
+ io.stderr(
317
+ `${gateway.tools.length} tool(s) exposed (${modeLabel(gateway)}). \`wstack tools describe <tool>\` shows the input schema.
318
+ `
319
+ );
320
+ return TOOLS_CLI_EXIT.ok;
321
+ }
322
+ var GUIDE_INDEX_TOOLS = [
323
+ ["codebase-context", '--query "<task>"', "files and declarations a task touches"],
324
+ ["codebase-search", "--query <name>", "where something is defined"],
325
+ ["codebase-incoming-calls", "--symbol <name>", "who calls it"],
326
+ ["codebase-outgoing-calls", "--symbol <name>", "what it calls"],
327
+ ["codebase-read-symbol", "--file <path> --symbol <name>", "one symbol's source"],
328
+ ["codebase-skeleton", "--path <file-or-dir>", "an outline without bodies"],
329
+ ["codebase-impact-analysis", "--symbol <name>", "what a change affects"]
330
+ ];
331
+ function toolsAgentGuide(gateway) {
332
+ const exposed = new Set(gateway.tools.map((t) => t.name));
333
+ const index = GUIDE_INDEX_TOOLS.filter(([name]) => exposed.has(name));
334
+ const grant = gateway.mode === "safe" ? "Read-only by default. Writes and commands need an explicit grant, e.g. `--allow fs.write`." : `Exposed with ${gateway.mode === "yolo" ? "--yolo" : `--allow ${gateway.capabilities.join(",")}`}; pass the same flag on every call.`;
335
+ return [
336
+ "## WrongStack tools",
337
+ "",
338
+ "This is a WrongStack project. WrongStack's built-in tools run from the shell, one call per command:",
339
+ "",
340
+ "```sh",
341
+ "wstack tools list # names and one-line summaries",
342
+ "wstack tools describe <tool> # a tool's fields (JSON schema)",
343
+ "wstack tools run <tool> -- --<field> <value> # fields after `--`, typed by the schema",
344
+ "wstack tools run <tool> --input '<json>' # or --input @call.json, or a bare --input for stdin",
345
+ 'wstack tools batch < calls.jsonl # {"tool":\u2026,"input":\u2026,"id":\u2026} per line, one process',
346
+ "```",
347
+ "",
348
+ ...index.length > 0 ? [
349
+ "For code navigation prefer the codebase index over grep/glob and whole-file reads; it is",
350
+ "caught up with the disk on every call:",
351
+ "",
352
+ ...index.map(
353
+ ([name, fields, use]) => `- \`wstack tools run ${name} -- ${fields}\` \u2014 ${use}`
354
+ ),
355
+ ""
356
+ ] : [],
357
+ `- ${grant} The user's deny rules always apply; deleting outside the project, history-rewriting git and download-and-run commands are refused.`,
358
+ "- Exit codes: 0 ok \xB7 1 tool failed \xB7 2 bad input \xB7 3 not exposed or denied \xB7 4 needs a confirmation. Add `--json` for a `{ok, settlement, content}` envelope.",
359
+ "",
360
+ `Tools exposed here (${gateway.tools.length}): ${gateway.tools.map((t) => t.name).join(", ")}.`,
361
+ ""
362
+ ].join("\n");
363
+ }
364
+ function describeTool(gateway, registry, name, json, io) {
365
+ const tool = gateway.get(name);
366
+ if (!tool) {
367
+ const outcome = notExposed(gateway, registry, name);
368
+ printRun(outcome, json, io);
369
+ return outcome.code;
370
+ }
371
+ const inputSchema = tool.inputSchema ?? { type: "object" };
372
+ if (json) {
373
+ io.stdout(
374
+ `${JSON.stringify({
375
+ name: tool.name,
376
+ permission: tool.permission,
377
+ capabilities: tool.capabilities ?? [],
378
+ description: tool.description,
379
+ inputSchema
380
+ })}
381
+ `
382
+ );
383
+ return TOOLS_CLI_EXIT.ok;
384
+ }
385
+ io.stdout(
386
+ `${tool.name} (${tool.permission})
387
+
388
+ ${tool.description.trim()}
389
+
390
+ Input schema:
391
+ ${JSON.stringify(inputSchema, null, 2)}
392
+
393
+ Run: wstack tools run ${tool.name} --input '<json>' (or --input @file.json, a bare --input for stdin,
394
+ or schema fields after --: wstack tools run ${tool.name} -- --<field> <value>)
395
+ `
396
+ );
397
+ return TOOLS_CLI_EXIT.ok;
398
+ }
399
+ async function runOne(gateway, registry, name, fieldArgs, flags, cwd, io) {
400
+ const tool = gateway.get(name);
401
+ if (!tool) return notExposed(gateway, registry, name);
402
+ let input;
403
+ try {
404
+ const raw = await readInputText(flags["input"], cwd, () => io.readStdin());
405
+ input = {
406
+ ...raw ? parseInputObject(raw.text, raw.source) : {},
407
+ ...parseSchemaArgs(fieldArgs, tool.inputSchema)
408
+ };
409
+ } catch (err) {
410
+ return failure(name, "invalid_input", toErrorMessage2(err), TOOLS_CLI_EXIT.usage);
411
+ }
412
+ return call(gateway, registry, name, input);
413
+ }
414
+ async function call(gateway, registry, name, input) {
415
+ const outcome = await gateway.call(name, input);
416
+ if (outcome.kind === "not_exposed") return notExposed(gateway, registry, name);
417
+ if (outcome.kind === "confirm_required") {
418
+ return failure(
419
+ name,
420
+ "confirm_required",
421
+ `Tool "${name}" needs an interactive confirmation for this input, which \`wstack tools\` cannot give.`,
422
+ TOOLS_CLI_EXIT.confirmRequired
423
+ );
424
+ }
425
+ const code = exitCodeFor(outcome.settlement, outcome.isError);
426
+ return {
427
+ code,
428
+ report: {
429
+ ok: code === TOOLS_CLI_EXIT.ok,
430
+ tool: name,
431
+ settlement: outcome.settlement,
432
+ durationMs: outcome.durationMs,
433
+ content: outcome.content
434
+ }
435
+ };
436
+ }
437
+ async function runBatch(gateway, registry, flags, cwd, io) {
438
+ let entries;
439
+ try {
440
+ const raw = await readInputText(flags["input"] ?? true, cwd, () => io.readStdin());
441
+ entries = parseBatchLines(raw?.text ?? "");
442
+ } catch (err) {
443
+ io.stderr(`${toErrorMessage2(err)}
444
+ `);
445
+ return TOOLS_CLI_EXIT.usage;
446
+ }
447
+ let exit = TOOLS_CLI_EXIT.ok;
448
+ for (const entry of entries) {
449
+ const outcome = "error" in entry ? failure("", "invalid_input", entry.error, TOOLS_CLI_EXIT.usage) : await call(gateway, registry, entry.tool, entry.input);
450
+ if (exit === TOOLS_CLI_EXIT.ok) exit = outcome.code;
451
+ const id = entry.id === void 0 ? {} : { id: entry.id };
452
+ io.stdout(`${JSON.stringify({ line: entry.line, ...id, ...outcome.report })}
453
+ `);
454
+ }
455
+ return exit;
456
+ }
457
+ function printRun(outcome, json, io) {
458
+ const { report } = outcome;
459
+ if (json) {
460
+ io.stdout(`${JSON.stringify(report)}
461
+ `);
462
+ return;
463
+ }
464
+ const text = "content" in report ? report.content : report.message;
465
+ const line = text.endsWith("\n") ? text : `${text}
466
+ `;
467
+ if (outcome.code === TOOLS_CLI_EXIT.ok) io.stdout(line);
468
+ else io.stderr(line);
469
+ }
470
+ function exitCodeFor(settlement, isError) {
471
+ switch (settlement) {
472
+ case "completed":
473
+ return isError ? TOOLS_CLI_EXIT.toolFailed : TOOLS_CLI_EXIT.ok;
474
+ case "invalid_input":
475
+ return TOOLS_CLI_EXIT.usage;
476
+ case "unknown_tool":
477
+ case "denied_by_policy":
478
+ case "blocked_by_hook":
479
+ return TOOLS_CLI_EXIT.notExposed;
480
+ case "declined":
481
+ return TOOLS_CLI_EXIT.confirmRequired;
482
+ default:
483
+ return TOOLS_CLI_EXIT.toolFailed;
484
+ }
485
+ }
486
+ function notExposed(gateway, registry, name) {
487
+ return failure(
488
+ name,
489
+ "not_exposed",
490
+ withheldReason(gateway, registry, name),
491
+ TOOLS_CLI_EXIT.notExposed
492
+ );
493
+ }
494
+ function failure(tool, error, message, code) {
495
+ return { code, report: { ok: false, tool, error, message } };
496
+ }
497
+ function summaryOf(tool) {
498
+ const first = tool.description.trim().split("\n")[0]?.trim() ?? "";
499
+ return first.length > 160 ? `${first.slice(0, 157)}...` : first;
500
+ }
501
+ function modeLabel(gateway) {
502
+ if (gateway.mode === "yolo") return "yolo: every capability";
503
+ if (gateway.mode === "granted") return `granted: ${gateway.capabilities.join(", ")}`;
504
+ return "safe: read-only";
505
+ }
506
+ export {
507
+ runToolsCli
508
+ };
509
+ //# sourceMappingURL=tools-cli-44MLIUQO.js.map
@@ -0,0 +1,102 @@
1
+ import {
2
+ serveToolGatewayHttp
3
+ } from "./chunk-RN5CFAVQ.js";
4
+ import {
5
+ parseToolsFlag,
6
+ resolveServeHttpPort
7
+ } from "./chunk-QLYPIJ7X.js";
8
+ import {
9
+ createToolGateway,
10
+ parseCapabilityGrants,
11
+ resolveGatewayRegistry
12
+ } from "./chunk-LYWOV4OX.js";
13
+ import "./chunk-W23PY4IJ.js";
14
+
15
+ // src/subcommands/handlers/tools-serve.ts
16
+ import { randomBytes } from "node:crypto";
17
+ import { toErrorMessage } from "@wrongstack/core/utils";
18
+ async function serveToolsCommand(args, deps, io, waitForShutdown = untilSignal) {
19
+ const flags = deps.flags ?? {};
20
+ let allow;
21
+ let port;
22
+ try {
23
+ allow = parseCapabilityGrants(flags["allow"]);
24
+ port = resolveServeHttpPort(flags);
25
+ } catch (err) {
26
+ io.stderr(`wstack tools serve: ${toErrorMessage(err)}
27
+ `);
28
+ return 2;
29
+ }
30
+ const whitelist = parseToolsFlag(flags, args);
31
+ if (flags["tools"] !== void 0 && whitelist === null) {
32
+ io.stderr("wstack tools serve: --tools needs a comma-separated tool list\n");
33
+ return 2;
34
+ }
35
+ const host = typeof flags["host"] === "string" ? flags["host"] : "127.0.0.1";
36
+ const envToken = process.env.WRONGSTACK_TOOLS_TOKEN?.trim();
37
+ const flagToken = typeof flags["token"] === "string" ? flags["token"] : void 0;
38
+ if (flagToken) {
39
+ io.stderr(
40
+ "wstack tools serve: --token puts the token on the command line, where other local processes can read it. Prefer WRONGSTACK_TOOLS_TOKEN.\n"
41
+ );
42
+ }
43
+ const generated = !flagToken && !envToken;
44
+ const token = flagToken ?? envToken ?? randomBytes(24).toString("hex");
45
+ const registry = resolveGatewayRegistry(deps.toolRegistry, deps.config);
46
+ const gateway = await createToolGateway({
47
+ registry,
48
+ config: deps.config,
49
+ cwd: deps.cwd,
50
+ projectRoot: deps.projectRoot,
51
+ yolo: flags["yolo"] === true || flags["allow-all"] === true,
52
+ allow,
53
+ whitelist,
54
+ remoteAgent: true,
55
+ onWarning: (message) => io.stderr(`wstack tools serve: ${message}
56
+ `)
57
+ });
58
+ try {
59
+ const handle = await serveToolGatewayHttp({
60
+ gateway,
61
+ registry,
62
+ token,
63
+ port,
64
+ host,
65
+ allowRemoteCallbacks: flags["allow-remote-callbacks"] === true,
66
+ log: (message) => io.stderr(`${message}
67
+ `)
68
+ });
69
+ io.stdout(
70
+ `${JSON.stringify({
71
+ url: handle.url,
72
+ ...generated ? { token } : {},
73
+ mode: gateway.mode,
74
+ tools: gateway.tools.length
75
+ })}
76
+ `
77
+ );
78
+ io.stderr(
79
+ `wstack tools serve: ${gateway.tools.length} tool(s) at ${handle.url} (${gateway.mode}). GET /v1/tools, POST /v1/tools/<name>/call with Authorization: Bearer <token>.
80
+ `
81
+ );
82
+ await waitForShutdown();
83
+ await handle.close();
84
+ return 0;
85
+ } catch (err) {
86
+ io.stderr(`wstack tools serve: ${toErrorMessage(err)}
87
+ `);
88
+ return 1;
89
+ } finally {
90
+ await gateway.close();
91
+ }
92
+ }
93
+ function untilSignal() {
94
+ return new Promise((resolve) => {
95
+ process.once("SIGINT", () => resolve());
96
+ process.once("SIGTERM", () => resolve());
97
+ });
98
+ }
99
+ export {
100
+ serveToolsCommand
101
+ };
102
+ //# sourceMappingURL=tools-serve-TRCET2M7.js.map
@@ -2,7 +2,12 @@ import "./chunk-W23PY4IJ.js";
2
2
 
3
3
  // src/subcommands/handlers/tools-skills.ts
4
4
  import { color } from "@wrongstack/core/utils";
5
- var toolsCmd = async (_args, deps) => {
5
+ var toolsCmd = async (args, deps) => {
6
+ const verb = args[0] ?? "";
7
+ if (["list", "describe", "run", "batch", "agent-guide", "serve"].includes(verb)) {
8
+ const { runToolsCli } = await import("./tools-cli-44MLIUQO.js");
9
+ return runToolsCli(verb, args.slice(1), deps);
10
+ }
6
11
  const reg = deps.toolRegistry;
7
12
  if (!reg) return 0;
8
13
  for (const { tool, owner } of reg.listWithOwner())
@@ -26,4 +31,4 @@ export {
26
31
  skillsCmd,
27
32
  toolsCmd
28
33
  };
29
- //# sourceMappingURL=tools-skills-55QVTMSK.js.map
34
+ //# sourceMappingURL=tools-skills-WDTMCE46.js.map
@@ -0,0 +1,7 @@
1
+ import type { TuneupFinding, TuneupInput } from './tuneup-types.js';
2
+ /** True when `command` is safely read-only by our conservative heuristic. */
3
+ export declare function isReadOnlyCommand(command: string): boolean;
4
+ export declare function checkPermissions(input: TuneupInput): TuneupFinding[];
5
+ export declare function checkPerformance(input: TuneupInput): TuneupFinding[];
6
+ export declare function checkReliability(input: TuneupInput): TuneupFinding[];
7
+ //# sourceMappingURL=tuneup-runtime-checks.d.ts.map