@wrongstack/cli 1.1.4 → 1.1.6

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 (103) hide show
  1. package/dist/{acp-MZ5MQBFN.js → acp-CZBSFQB7.js} +6 -5
  2. package/dist/acp-leader/acp-leader-confirm.d.ts +2 -0
  3. package/dist/acp-leader/acp-leader-host-binding.d.ts +1 -0
  4. package/dist/acp-leader/acp-leader-host.d.ts +9 -0
  5. package/dist/acp-leader/acp-leader-picker.d.ts +6 -3
  6. package/dist/acp-leader/acp-leader-provider.d.ts +10 -0
  7. package/dist/acp-leader/acp-leader-skills.d.ts +5 -0
  8. package/dist/acp-leader/acp-leader-stream.d.ts +9 -0
  9. package/dist/{auth-IHQS6AFW.js → auth-2ZG5DBHS.js} +4 -4
  10. package/dist/boot/launch-menu-arrow.d.ts +20 -0
  11. package/dist/boot/launch-menu-types.d.ts +36 -0
  12. package/dist/boot/launch-menu.d.ts +1 -57
  13. package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
  14. package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
  15. package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
  16. package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
  17. package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
  18. package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
  19. package/dist/{chunk-COGI7EQU.js → chunk-EAATL6FL.js} +713 -21
  20. package/dist/{chunk-AWRZWPJ3.js → chunk-FWUIKCCO.js} +152 -123
  21. package/dist/{chunk-TAE3EGBM.js → chunk-H5DCQFUI.js} +5 -5
  22. package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
  23. package/dist/chunk-LYWOV4OX.js +252 -0
  24. package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
  25. package/dist/{chunk-I5LJLKA3.js → chunk-MKMM5RIW.js} +12 -5
  26. package/dist/{chunk-ZF4HRR5F.js → chunk-P7MEEUGT.js} +275 -250
  27. package/dist/chunk-PGC44T7N.js +255 -0
  28. package/dist/chunk-QLYPIJ7X.js +258 -0
  29. package/dist/{chunk-JKTJSLUT.js → chunk-QRWXHGLB.js} +242 -18
  30. package/dist/chunk-RN5CFAVQ.js +292 -0
  31. package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
  32. package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
  33. package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
  34. package/dist/{chunk-2LTHDKZX.js → chunk-W7X23HI6.js} +18 -18
  35. package/dist/{chunk-GKGWPFW2.js → chunk-X4DEQLAZ.js} +1 -1
  36. package/dist/{cli-context-E44HQ4ZO.js → cli-context-6KS3HV5R.js} +247 -236
  37. package/dist/{cli-main-OT63I2FX.js → cli-main-NF3YGDME.js} +1779 -1494
  38. package/dist/cli-main-boot.d.ts +1 -1
  39. package/dist/config-doctor-known-keys.d.ts +33 -0
  40. package/dist/config-doctor.d.ts +2 -47
  41. package/dist/config-history-support.d.ts +36 -0
  42. package/dist/config-history.d.ts +1 -1
  43. package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
  44. package/dist/{execution-OK34CJKJ.js → execution-RVDUXLNT.js} +6522 -6426
  45. package/dist/execution-tui.d.ts +30 -0
  46. package/dist/fleet/acp-wrongstack-bridge.d.ts +11 -3
  47. package/dist/fleet/host-acp.d.ts +3 -0
  48. package/dist/fleet/wrongforge-client.d.ts +116 -0
  49. package/dist/fleet/wrongforge-gateway.d.ts +20 -0
  50. package/dist/fleet/wrongforge-info.d.ts +32 -0
  51. package/dist/fleet/wrongforge-quota.d.ts +10 -0
  52. package/dist/fleet/wrongforge-runner.d.ts +37 -0
  53. package/dist/fleet/wrongforge-runtime.d.ts +10 -0
  54. package/dist/{hq-JD5AZVMG.js → hq-V2M4IYCI.js} +320 -306
  55. package/dist/hq-server/login-attempt-store.d.ts +2 -0
  56. package/dist/hq-server/sweeps.d.ts +28 -0
  57. package/dist/{hq-server-BI23EPM7.js → hq-server-QM7EIVGJ.js} +3 -3
  58. package/dist/index.js +5 -5
  59. package/dist/mcp-22CFBX4H.js +236 -0
  60. package/dist/mcp-serve.d.ts +0 -63
  61. package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
  62. package/dist/{per-subcommand-help-4GYSCMLU.js → per-subcommand-help-MQGERD4E.js} +2 -2
  63. package/dist/plugin-llm-command.d.ts +9 -0
  64. package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
  65. package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
  66. package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
  67. package/dist/services/sdd/state.d.ts +2 -0
  68. package/dist/session-event-wiring-types.d.ts +62 -0
  69. package/dist/session-event-wiring.d.ts +2 -52
  70. package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
  71. package/dist/short-circuit-flags-276OLPIN.js +11 -0
  72. package/dist/slash-commands/command-context-fleet.d.ts +187 -0
  73. package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
  74. package/dist/slash-commands/command-context.d.ts +3 -282
  75. package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
  76. package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
  77. package/dist/subcommands/handlers/hq-token.d.ts +3 -0
  78. package/dist/subcommands/handlers/hq.d.ts +0 -21
  79. package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
  80. package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
  81. package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
  82. package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
  83. package/dist/{subcommands-YADUV7IJ.js → subcommands-SKJPUJ7N.js} +2 -2
  84. package/dist/tool-gateway-http.d.ts +30 -0
  85. package/dist/tool-gateway.d.ts +185 -0
  86. package/dist/tools-cli-44MLIUQO.js +509 -0
  87. package/dist/tools-serve-TRCET2M7.js +102 -0
  88. package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
  89. package/dist/tuneup-runtime-checks.d.ts +7 -0
  90. package/dist/tuneup-types.d.ts +118 -0
  91. package/dist/tuneup.d.ts +3 -122
  92. package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
  93. package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
  94. package/dist/{webui-server-CB64CFDG.js → webui-server-6I6QOECV.js} +44 -6
  95. package/dist/wiring/provider-runtime-setup.d.ts +3 -1
  96. package/dist/wiring/session-runtime.d.ts +1 -1
  97. package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
  98. package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
  99. package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
  100. package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +56 -0
  101. package/package.json +1 -1
  102. package/dist/mcp-W7A5HUXV.js +0 -560
  103. package/dist/short-circuit-flags-QEXUS3BY.js +0 -11
@@ -0,0 +1,255 @@
1
+
2
+ // src/fleet/wrongforge-client.ts
3
+ import { readFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import path from "node:path";
6
+ import { toErrorMessage } from "@wrongstack/core/utils";
7
+ var WRONGFORGE_AGENT_ID = "wrongforge";
8
+ var DEFAULT_URL = "http://127.0.0.1:8787";
9
+ var MIN_KEY_LENGTH = 16;
10
+ var POLL_WAIT_MS = 25e3;
11
+ var POLL_LIMIT = 1e3;
12
+ var MAX_POLL_FAILURES = 6;
13
+ var WRONGFORGE_READ_SIDE_TOOLS = [
14
+ "read",
15
+ "ls",
16
+ "glob",
17
+ "grep",
18
+ "web_fetch",
19
+ "todo_read",
20
+ "todo_write",
21
+ "report_progress"
22
+ ];
23
+ var WfHttpError = class extends Error {
24
+ constructor(status, code, message) {
25
+ super(message);
26
+ this.status = status;
27
+ this.code = code;
28
+ }
29
+ status;
30
+ code;
31
+ };
32
+ var WfAborted = class extends Error {
33
+ constructor() {
34
+ super("cancelled by the delegating agent");
35
+ }
36
+ };
37
+ function resolveWrongForgeEndpoint(env = process.env) {
38
+ const url = (env["WRONGFORGE_URL"]?.trim() || DEFAULT_URL).replace(/\/+$/, "");
39
+ let apiKey = env["WRONGFORGE_API_KEY"]?.trim() || env["AGENT_API_KEY"]?.trim();
40
+ if (!apiKey) {
41
+ const home = env["WRONGFORGE_HOME"]?.trim() || path.join(homedir(), ".wrongforge");
42
+ try {
43
+ apiKey = readFileSync(path.join(home, "api-key"), "utf8").replace(/^\uFEFF/, "").trim();
44
+ } catch {
45
+ return void 0;
46
+ }
47
+ }
48
+ return apiKey && apiKey.length >= MIN_KEY_LENGTH ? { url, apiKey } : void 0;
49
+ }
50
+ var safeWfId = (s) => s.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 80);
51
+ function wrongForgeCall(ep, doFetch = fetch) {
52
+ return async (method, route, body, signal) => {
53
+ let res;
54
+ try {
55
+ res = await doFetch(`${ep.url}${route}`, {
56
+ method,
57
+ headers: {
58
+ authorization: `Bearer ${ep.apiKey}`,
59
+ ...body !== void 0 ? { "content-type": "application/json" } : {}
60
+ },
61
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {},
62
+ ...signal ? { signal } : {}
63
+ });
64
+ } catch (err) {
65
+ if (signal?.aborted) throw new WfAborted();
66
+ throw new WfHttpError(
67
+ 0,
68
+ "unreachable",
69
+ `WrongForge is not reachable at ${ep.url} (${toErrorMessage(err)}); is \`npm run serve\` running?`
70
+ );
71
+ }
72
+ const text = await res.text();
73
+ let parsed;
74
+ try {
75
+ parsed = text ? JSON.parse(text) : {};
76
+ } catch {
77
+ parsed = {};
78
+ }
79
+ if (!res.ok) {
80
+ const e = parsed.error;
81
+ throw new WfHttpError(
82
+ res.status,
83
+ e?.code ?? `http_${res.status}`,
84
+ e?.message ?? text.slice(0, 300)
85
+ );
86
+ }
87
+ return parsed;
88
+ };
89
+ }
90
+ async function* followTask(call, taskId, signal, opts = {}) {
91
+ let after = 0;
92
+ let failures = 0;
93
+ for (; ; ) {
94
+ if (signal.aborted) throw new WfAborted();
95
+ let page;
96
+ try {
97
+ page = await call(
98
+ "GET",
99
+ `/v1/tasks/${encodeURIComponent(taskId)}/events?after=${after}&limit=${POLL_LIMIT}&wait_ms=${POLL_WAIT_MS}`,
100
+ void 0,
101
+ signal
102
+ );
103
+ failures = 0;
104
+ } catch (err) {
105
+ if (signal.aborted || err instanceof WfAborted) throw new WfAborted();
106
+ if (++failures >= MAX_POLL_FAILURES) throw err;
107
+ await sleep(opts.retryDelayMs ?? 2e3, signal);
108
+ continue;
109
+ }
110
+ for (const ev of page.events) yield ev;
111
+ if (page.status) opts.onPoll?.(page.status);
112
+ after = page.next_after;
113
+ if (page.terminal && page.events.length < POLL_LIMIT) return;
114
+ }
115
+ }
116
+ async function terminalTaskRecord(call, taskId, signal, opts = {}) {
117
+ let failures = 0;
118
+ for (; ; ) {
119
+ try {
120
+ return (await call(
121
+ "GET",
122
+ `/v1/tasks/${encodeURIComponent(taskId)}`,
123
+ void 0,
124
+ signal
125
+ )).task;
126
+ } catch (err) {
127
+ if (signal.aborted || err instanceof WfAborted) throw new WfAborted();
128
+ if (++failures >= MAX_POLL_FAILURES) throw err;
129
+ await sleep(opts.retryDelayMs ?? 2e3, signal);
130
+ }
131
+ }
132
+ }
133
+ function cancelTask(call, taskId, reason) {
134
+ void call("POST", `/v1/tasks/${encodeURIComponent(taskId)}/cancel`, { reason }).catch(
135
+ () => void 0
136
+ );
137
+ }
138
+ function chooseModel(requested, agentName, info) {
139
+ const id = requested?.trim();
140
+ if (!id || id === WRONGFORGE_AGENT_ID || id === agentName) return {};
141
+ if (info && info.models.length > 0) {
142
+ if (info.models.includes(id)) return { id };
143
+ return {
144
+ error: `WrongForge does not serve model "${id}"; it serves ${info.models.join(", ")}${info.defaultModel ? ` (default ${info.defaultModel})` : ""}`
145
+ };
146
+ }
147
+ return /^claude-[a-z0-9.-]+$/i.test(id) ? { id } : {};
148
+ }
149
+ function toProgress(ev) {
150
+ const d = ev.data;
151
+ const str = (v) => typeof v === "string" ? v : "";
152
+ const rec = (v) => v === void 0 ? void 0 : v && typeof v === "object" && !Array.isArray(v) ? v : { value: v };
153
+ switch (ev.type) {
154
+ case "task.started":
155
+ return [
156
+ {
157
+ type: "thought",
158
+ text: `[started] WrongForge \xB7 ${str(d["model"]) || "default model"} \xB7 ${str(d["workdir"])}
159
+ `
160
+ }
161
+ ];
162
+ case "task.queued":
163
+ return str(d["waiting_for"]) ? [
164
+ {
165
+ type: "thought",
166
+ text: `[queued] WrongForge is busy (waiting for ${str(d["waiting_for"])})
167
+ `
168
+ }
169
+ ] : [];
170
+ case "agent.text":
171
+ return str(d["text"]) ? [{ type: "message", text: str(d["text"]) }] : [];
172
+ case "agent.report":
173
+ return str(d["message"]) ? [{ type: "thought", text: `[progress] ${str(d["message"])}
174
+ ` }] : [];
175
+ case "tool.started":
176
+ return [
177
+ {
178
+ type: "tool_call",
179
+ toolCall: {
180
+ toolCallId: str(d["tool_call_id"]),
181
+ title: str(d["name"]),
182
+ status: "in_progress",
183
+ rawInput: rec(d["input"])
184
+ }
185
+ }
186
+ ];
187
+ case "tool.finished":
188
+ case "tool.failed": {
189
+ const failed = ev.type === "tool.failed" || d["ok"] === false;
190
+ return [
191
+ {
192
+ type: "tool_call_update",
193
+ toolCall: {
194
+ toolCallId: str(d["tool_call_id"]),
195
+ title: str(d["name"]),
196
+ status: failed ? "failed" : "completed",
197
+ rawOutput: ev.type === "tool.failed" ? { error: d["error"] } : rec(d["output"])
198
+ }
199
+ }
200
+ ];
201
+ }
202
+ case "agent.todos": {
203
+ const todos = Array.isArray(d["todos"]) ? d["todos"] : [];
204
+ return [
205
+ {
206
+ type: "plan",
207
+ entries: todos.map((t) => ({
208
+ content: str(t["content"]),
209
+ priority: "medium",
210
+ status: t["status"] === "completed" || t["status"] === "in_progress" ? t["status"] : "pending"
211
+ }))
212
+ }
213
+ ];
214
+ }
215
+ default:
216
+ return [];
217
+ }
218
+ }
219
+ function wfTokens(usage) {
220
+ return {
221
+ input: usage?.input_tokens ?? 0,
222
+ output: usage?.output_tokens ?? 0,
223
+ cacheRead: usage?.cache_read_tokens ?? 0,
224
+ cacheWrite: usage?.cache_write_tokens ?? 0
225
+ };
226
+ }
227
+ function sleep(ms, signal) {
228
+ if (signal.aborted) return Promise.resolve();
229
+ return new Promise((resolve) => {
230
+ const t = setTimeout(done, ms);
231
+ function done() {
232
+ clearTimeout(t);
233
+ signal.removeEventListener("abort", done);
234
+ resolve();
235
+ }
236
+ signal.addEventListener("abort", done, { once: true });
237
+ });
238
+ }
239
+
240
+ export {
241
+ WRONGFORGE_AGENT_ID,
242
+ WRONGFORGE_READ_SIDE_TOOLS,
243
+ WfHttpError,
244
+ WfAborted,
245
+ resolveWrongForgeEndpoint,
246
+ safeWfId,
247
+ wrongForgeCall,
248
+ followTask,
249
+ terminalTaskRecord,
250
+ cancelTask,
251
+ chooseModel,
252
+ toProgress,
253
+ wfTokens
254
+ };
255
+ //# sourceMappingURL=chunk-PGC44T7N.js.map
@@ -0,0 +1,258 @@
1
+ import {
2
+ createToolGateway,
3
+ parseCapabilityGrants,
4
+ resolveGatewayRegistry
5
+ } from "./chunk-LYWOV4OX.js";
6
+
7
+ // src/mcp-serve.ts
8
+ import * as fs from "node:fs/promises";
9
+ import * as path from "node:path";
10
+ import { pathToFileURL } from "node:url";
11
+ import { toErrorMessage } from "@wrongstack/core/utils";
12
+ import {
13
+ DEFAULT_MCP_INSERTION_MAX_BYTES,
14
+ MCPServer,
15
+ serveHttp,
16
+ serveStdio
17
+ } from "@wrongstack/mcp";
18
+ function parseToolsFlag(flags, positional) {
19
+ const raw = flags["tools"];
20
+ const csv = typeof raw === "string" ? raw : raw === true ? positional?.[0] ?? "" : "";
21
+ if (!csv) return null;
22
+ const set = new Set(
23
+ csv.split(",").map((s) => s.trim()).filter(Boolean)
24
+ );
25
+ return set.size > 0 ? set : null;
26
+ }
27
+ function resolveServeHttpPort(flags) {
28
+ const raw = typeof flags["port"] === "string" ? flags["port"] : typeof flags["http"] === "string" ? flags["http"] : void 0;
29
+ if (raw === void 0 || raw.trim() === "") return 0;
30
+ const port = Number(raw);
31
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
32
+ throw new Error(`invalid --port "${raw}" \u2014 expected an integer between 0 and 65535`);
33
+ }
34
+ return port;
35
+ }
36
+ async function loadSelectedMcpServeContent(flags, cwd) {
37
+ const resources = [];
38
+ const prompts = [];
39
+ const resourceUris = /* @__PURE__ */ new Set();
40
+ const promptNames = /* @__PURE__ */ new Set();
41
+ for (const selected of commaSeparatedFlag(flags, "resources")) {
42
+ const file = path.resolve(cwd, selected);
43
+ const data = await readSelectedFile(file);
44
+ const uri = pathToFileURL(file).toString();
45
+ if (resourceUris.has(uri)) throw new Error(`Duplicate MCP resource selection: ${selected}`);
46
+ resourceUris.add(uri);
47
+ const mimeType = mimeTypeFor(file);
48
+ resources.push({
49
+ uri,
50
+ name: path.basename(file),
51
+ mimeType,
52
+ size: data.byteLength,
53
+ contents: [
54
+ isTextMime(mimeType) ? { uri, mimeType, text: data.toString("utf8") } : { uri, mimeType, blob: data.toString("base64") }
55
+ ]
56
+ });
57
+ }
58
+ for (const selected of commaSeparatedFlag(flags, "prompts")) {
59
+ const file = path.resolve(cwd, selected);
60
+ const data = await readSelectedFile(file);
61
+ const name = path.basename(file, path.extname(file)).replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
62
+ if (!name) throw new Error(`Cannot derive an MCP prompt name from: ${selected}`);
63
+ if (promptNames.has(name)) throw new Error(`Duplicate MCP prompt name: ${name}`);
64
+ promptNames.add(name);
65
+ const template = data.toString("utf8");
66
+ const argumentNames = Array.from(
67
+ template.matchAll(/\{\{([A-Za-z_][A-Za-z0-9_.-]*)\}\}/g),
68
+ (match) => match[1]
69
+ ).filter((value, index, all) => !!value && all.indexOf(value) === index);
70
+ prompts.push({
71
+ name,
72
+ description: `Explicitly selected local prompt: ${path.basename(file)}`,
73
+ arguments: argumentNames.map((argument) => ({ name: argument, required: true })),
74
+ template
75
+ });
76
+ }
77
+ return { resources, prompts };
78
+ }
79
+ function commaSeparatedFlag(flags, name) {
80
+ const value = flags[name];
81
+ if (value === void 0) return [];
82
+ if (typeof value !== "string" || value.trim() === "") {
83
+ throw new Error(`--${name} requires a comma-separated file list`);
84
+ }
85
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
86
+ }
87
+ async function readSelectedFile(file) {
88
+ const stat2 = await fs.stat(file);
89
+ if (!stat2.isFile()) throw new Error(`Selected MCP content is not a regular file: ${file}`);
90
+ if (stat2.size > DEFAULT_MCP_INSERTION_MAX_BYTES) {
91
+ throw new Error(
92
+ `Selected MCP content exceeds ${DEFAULT_MCP_INSERTION_MAX_BYTES} bytes: ${file}`
93
+ );
94
+ }
95
+ return fs.readFile(file);
96
+ }
97
+ function mimeTypeFor(file) {
98
+ switch (path.extname(file).toLowerCase()) {
99
+ case ".md":
100
+ case ".mdx":
101
+ return "text/markdown";
102
+ case ".json":
103
+ return "application/json";
104
+ case ".yaml":
105
+ case ".yml":
106
+ return "application/yaml";
107
+ case ".html":
108
+ return "text/html";
109
+ case ".css":
110
+ return "text/css";
111
+ case ".js":
112
+ case ".mjs":
113
+ return "text/javascript";
114
+ case ".ts":
115
+ case ".tsx":
116
+ return "text/typescript";
117
+ case ".txt":
118
+ case ".log":
119
+ return "text/plain";
120
+ case ".png":
121
+ return "image/png";
122
+ case ".jpg":
123
+ case ".jpeg":
124
+ return "image/jpeg";
125
+ case ".gif":
126
+ return "image/gif";
127
+ case ".webp":
128
+ return "image/webp";
129
+ default:
130
+ return "application/octet-stream";
131
+ }
132
+ }
133
+ function isTextMime(mimeType) {
134
+ return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType === "application/yaml";
135
+ }
136
+ async function serveMcpStdio(deps, positional) {
137
+ const flags = deps.flags ?? {};
138
+ const yolo = flags["yolo"] === true || flags["allow-all"] === true;
139
+ const whitelist = parseToolsFlag(flags, positional);
140
+ if (flags["tools"] !== void 0 && whitelist === null) {
141
+ process.stderr.write(
142
+ "wrongstack MCP server: --tools requires a comma-separated tool list (e.g. `--tools read,grep` or `--tools=read,grep`)\n"
143
+ );
144
+ return 1;
145
+ }
146
+ const log = (m) => process.stderr.write(`${m}
147
+ `);
148
+ let selectedContent;
149
+ try {
150
+ selectedContent = await loadSelectedMcpServeContent(flags, deps.cwd);
151
+ } catch (err) {
152
+ log(`wrongstack MCP server: ${err instanceof Error ? err.message : String(err)}`);
153
+ return 1;
154
+ }
155
+ let allow;
156
+ try {
157
+ allow = parseCapabilityGrants(flags["allow"]);
158
+ } catch (err) {
159
+ log(`wrongstack MCP server: ${toErrorMessage(err)}`);
160
+ return 1;
161
+ }
162
+ const gateway = await createToolGateway({
163
+ registry: resolveGatewayRegistry(deps.toolRegistry, deps.config),
164
+ config: deps.config,
165
+ cwd: deps.cwd,
166
+ projectRoot: deps.projectRoot,
167
+ yolo,
168
+ allow,
169
+ whitelist
170
+ });
171
+ const allowed = gateway.tools;
172
+ if (allowed.length === 0) {
173
+ log(
174
+ "wrongstack MCP server: no tools to expose (all withheld by policy or filtered out). Pass --yolo to expose write/exec tools, or --tools <names> to whitelist."
175
+ );
176
+ }
177
+ const host = {
178
+ listTools: () => allowed.map((t) => ({
179
+ name: t.name,
180
+ description: t.description,
181
+ inputSchema: t.inputSchema ?? { type: "object" }
182
+ })),
183
+ callTool: async (name, callArgs) => {
184
+ const outcome = await gateway.call(name, callArgs);
185
+ if (outcome.kind === "not_exposed") {
186
+ return { content: `Tool "${name}" is not exposed by this server`, isError: true };
187
+ }
188
+ if (outcome.kind === "confirm_required") {
189
+ return {
190
+ content: `Tool "${name}" requires interactive confirmation, which is unavailable over MCP`,
191
+ isError: true
192
+ };
193
+ }
194
+ return { content: outcome.content, isError: outcome.isError };
195
+ }
196
+ };
197
+ const server = new MCPServer({
198
+ host,
199
+ resources: selectedContent.resources,
200
+ prompts: selectedContent.prompts,
201
+ logger: { warn: (m) => log(`[mcp-serve] ${m}`) }
202
+ });
203
+ const mode = gateway.mode === "yolo" ? "yolo: all tools" : gateway.mode === "granted" ? `granted: ${gateway.capabilities.join(", ")}` : "safe: read-only tools";
204
+ if (flags["http"] === true || typeof flags["http"] === "string" || flags["port"] || flags["host"]) {
205
+ let port;
206
+ try {
207
+ port = resolveServeHttpPort(flags);
208
+ } catch (err) {
209
+ log(`wrongstack MCP server: ${err instanceof Error ? err.message : String(err)}`);
210
+ return 1;
211
+ }
212
+ const httpHost = typeof flags["host"] === "string" ? flags["host"] : "127.0.0.1";
213
+ const tokenFromArg = typeof flags["token"] === "string" ? flags["token"] : void 0;
214
+ const token = tokenFromArg ?? process.env.WRONGSTACK_MCP_TOKEN?.trim() ?? void 0;
215
+ if (tokenFromArg) {
216
+ log(
217
+ "[mcp-serve] --token puts the auth token in this process command line, which other local processes can read. Prefer WRONGSTACK_MCP_TOKEN."
218
+ );
219
+ }
220
+ let handle2;
221
+ try {
222
+ handle2 = await serveHttp(server, {
223
+ port,
224
+ host: httpHost,
225
+ token,
226
+ logger: { warn: (m) => log(`[mcp-serve] ${m}`) }
227
+ });
228
+ } catch (err) {
229
+ log(`wrongstack MCP server: ${err instanceof Error ? err.message : String(err)}`);
230
+ return 1;
231
+ }
232
+ log(
233
+ `wrongstack MCP server ready at ${handle2.url} \u2014 exposing ${allowed.length} tool(s), ${selectedContent.resources.length} resource(s), ${selectedContent.prompts.length} prompt(s) (${mode})${token ? " [token auth]" : ""}.`
234
+ );
235
+ await new Promise((resolve2) => {
236
+ const stop = () => resolve2();
237
+ process.once("SIGINT", stop);
238
+ process.once("SIGTERM", stop);
239
+ });
240
+ await handle2.close();
241
+ await gateway.close();
242
+ return 0;
243
+ }
244
+ log(
245
+ `wrongstack MCP server ready on stdio \u2014 exposing ${allowed.length} tool(s), ${selectedContent.resources.length} resource(s), ${selectedContent.prompts.length} prompt(s) (${mode}).`
246
+ );
247
+ const handle = serveStdio(server);
248
+ await handle.done;
249
+ await gateway.close();
250
+ return 0;
251
+ }
252
+
253
+ export {
254
+ parseToolsFlag,
255
+ resolveServeHttpPort,
256
+ serveMcpStdio
257
+ };
258
+ //# sourceMappingURL=chunk-QLYPIJ7X.js.map