@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.
- package/dist/{acp-MZ5MQBFN.js → acp-CZBSFQB7.js} +6 -5
- package/dist/acp-leader/acp-leader-confirm.d.ts +2 -0
- package/dist/acp-leader/acp-leader-host-binding.d.ts +1 -0
- package/dist/acp-leader/acp-leader-host.d.ts +9 -0
- package/dist/acp-leader/acp-leader-picker.d.ts +6 -3
- package/dist/acp-leader/acp-leader-provider.d.ts +10 -0
- package/dist/acp-leader/acp-leader-skills.d.ts +5 -0
- package/dist/acp-leader/acp-leader-stream.d.ts +9 -0
- package/dist/{auth-IHQS6AFW.js → auth-2ZG5DBHS.js} +4 -4
- package/dist/boot/launch-menu-arrow.d.ts +20 -0
- package/dist/boot/launch-menu-types.d.ts +36 -0
- package/dist/boot/launch-menu.d.ts +1 -57
- package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
- package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
- package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
- package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
- package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
- package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
- package/dist/{chunk-COGI7EQU.js → chunk-EAATL6FL.js} +713 -21
- package/dist/{chunk-AWRZWPJ3.js → chunk-FWUIKCCO.js} +152 -123
- package/dist/{chunk-TAE3EGBM.js → chunk-H5DCQFUI.js} +5 -5
- package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
- package/dist/chunk-LYWOV4OX.js +252 -0
- package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
- package/dist/{chunk-I5LJLKA3.js → chunk-MKMM5RIW.js} +12 -5
- package/dist/{chunk-ZF4HRR5F.js → chunk-P7MEEUGT.js} +275 -250
- package/dist/chunk-PGC44T7N.js +255 -0
- package/dist/chunk-QLYPIJ7X.js +258 -0
- package/dist/{chunk-JKTJSLUT.js → chunk-QRWXHGLB.js} +242 -18
- package/dist/chunk-RN5CFAVQ.js +292 -0
- package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
- package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
- package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
- package/dist/{chunk-2LTHDKZX.js → chunk-W7X23HI6.js} +18 -18
- package/dist/{chunk-GKGWPFW2.js → chunk-X4DEQLAZ.js} +1 -1
- package/dist/{cli-context-E44HQ4ZO.js → cli-context-6KS3HV5R.js} +247 -236
- package/dist/{cli-main-OT63I2FX.js → cli-main-NF3YGDME.js} +1779 -1494
- package/dist/cli-main-boot.d.ts +1 -1
- package/dist/config-doctor-known-keys.d.ts +33 -0
- package/dist/config-doctor.d.ts +2 -47
- package/dist/config-history-support.d.ts +36 -0
- package/dist/config-history.d.ts +1 -1
- package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
- package/dist/{execution-OK34CJKJ.js → execution-RVDUXLNT.js} +6522 -6426
- package/dist/execution-tui.d.ts +30 -0
- package/dist/fleet/acp-wrongstack-bridge.d.ts +11 -3
- package/dist/fleet/host-acp.d.ts +3 -0
- package/dist/fleet/wrongforge-client.d.ts +116 -0
- package/dist/fleet/wrongforge-gateway.d.ts +20 -0
- package/dist/fleet/wrongforge-info.d.ts +32 -0
- package/dist/fleet/wrongforge-quota.d.ts +10 -0
- package/dist/fleet/wrongforge-runner.d.ts +37 -0
- package/dist/fleet/wrongforge-runtime.d.ts +10 -0
- package/dist/{hq-JD5AZVMG.js → hq-V2M4IYCI.js} +320 -306
- package/dist/hq-server/login-attempt-store.d.ts +2 -0
- package/dist/hq-server/sweeps.d.ts +28 -0
- package/dist/{hq-server-BI23EPM7.js → hq-server-QM7EIVGJ.js} +3 -3
- package/dist/index.js +5 -5
- package/dist/mcp-22CFBX4H.js +236 -0
- package/dist/mcp-serve.d.ts +0 -63
- package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
- package/dist/{per-subcommand-help-4GYSCMLU.js → per-subcommand-help-MQGERD4E.js} +2 -2
- package/dist/plugin-llm-command.d.ts +9 -0
- package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
- package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
- package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
- package/dist/services/sdd/state.d.ts +2 -0
- package/dist/session-event-wiring-types.d.ts +62 -0
- package/dist/session-event-wiring.d.ts +2 -52
- package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
- package/dist/short-circuit-flags-276OLPIN.js +11 -0
- package/dist/slash-commands/command-context-fleet.d.ts +187 -0
- package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
- package/dist/slash-commands/command-context.d.ts +3 -282
- package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
- package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
- package/dist/subcommands/handlers/hq-token.d.ts +3 -0
- package/dist/subcommands/handlers/hq.d.ts +0 -21
- package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
- package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
- package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
- package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
- package/dist/{subcommands-YADUV7IJ.js → subcommands-SKJPUJ7N.js} +2 -2
- package/dist/tool-gateway-http.d.ts +30 -0
- package/dist/tool-gateway.d.ts +185 -0
- package/dist/tools-cli-44MLIUQO.js +509 -0
- package/dist/tools-serve-TRCET2M7.js +102 -0
- package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
- package/dist/tuneup-runtime-checks.d.ts +7 -0
- package/dist/tuneup-types.d.ts +118 -0
- package/dist/tuneup.d.ts +3 -122
- package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
- package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
- package/dist/{webui-server-CB64CFDG.js → webui-server-6I6QOECV.js} +44 -6
- package/dist/wiring/provider-runtime-setup.d.ts +3 -1
- package/dist/wiring/session-runtime.d.ts +1 -1
- package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
- package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
- package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
- package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +56 -0
- package/package.json +1 -1
- package/dist/mcp-W7A5HUXV.js +0 -560
- package/dist/short-circuit-flags-QEXUS3BY.js +0 -11
package/dist/mcp-W7A5HUXV.js
DELETED
|
@@ -1,560 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
activeProfileConfigPath
|
|
3
|
-
} from "./chunk-SPSHEZ46.js";
|
|
4
|
-
import {
|
|
5
|
-
restoreFlags
|
|
6
|
-
} from "./chunk-QC6CLAK7.js";
|
|
7
|
-
import "./chunk-W23PY4IJ.js";
|
|
8
|
-
|
|
9
|
-
// src/subcommands/handlers/mcp.ts
|
|
10
|
-
import { allServers, resolveMcpServerConfig } from "@wrongstack/core/infrastructure";
|
|
11
|
-
import {
|
|
12
|
-
expectDefined,
|
|
13
|
-
jsonObjectFileExists,
|
|
14
|
-
readJsonObjectFile,
|
|
15
|
-
removeJsonPath,
|
|
16
|
-
setJsonPath,
|
|
17
|
-
updateJsonObjectFile
|
|
18
|
-
} from "@wrongstack/core/utils";
|
|
19
|
-
|
|
20
|
-
// src/mcp-serve.ts
|
|
21
|
-
import * as fs from "node:fs/promises";
|
|
22
|
-
import * as path from "node:path";
|
|
23
|
-
import { pathToFileURL } from "node:url";
|
|
24
|
-
import { Context } from "@wrongstack/core/agent";
|
|
25
|
-
import { ToolExecutor } from "@wrongstack/core/execution";
|
|
26
|
-
import { ToolRegistry } from "@wrongstack/core/registry";
|
|
27
|
-
import {
|
|
28
|
-
AutoApprovePermissionPolicy,
|
|
29
|
-
DefaultSecretScrubber,
|
|
30
|
-
ToolCapabilities
|
|
31
|
-
} from "@wrongstack/core/security";
|
|
32
|
-
import { normalizeTokenSavingTier } from "@wrongstack/core/types";
|
|
33
|
-
import {
|
|
34
|
-
DEFAULT_MCP_INSERTION_MAX_BYTES,
|
|
35
|
-
MCPServer,
|
|
36
|
-
serveHttp,
|
|
37
|
-
serveStdio
|
|
38
|
-
} from "@wrongstack/mcp";
|
|
39
|
-
import { registerBuiltinToolTier } from "@wrongstack/tools/tool-tier";
|
|
40
|
-
import { wireKanbanPorts } from "@wrongstack/runtime";
|
|
41
|
-
function yoloServePolicy() {
|
|
42
|
-
return new AutoApprovePermissionPolicy(Object.values(ToolCapabilities));
|
|
43
|
-
}
|
|
44
|
-
function parseToolsFlag(flags, positional) {
|
|
45
|
-
const raw = flags["tools"];
|
|
46
|
-
const csv = typeof raw === "string" ? raw : raw === true ? positional?.[0] ?? "" : "";
|
|
47
|
-
if (!csv) return null;
|
|
48
|
-
const set = new Set(
|
|
49
|
-
csv.split(",").map((s) => s.trim()).filter(Boolean)
|
|
50
|
-
);
|
|
51
|
-
return set.size > 0 ? set : null;
|
|
52
|
-
}
|
|
53
|
-
function resolveServeHttpPort(flags) {
|
|
54
|
-
const raw = typeof flags["port"] === "string" ? flags["port"] : typeof flags["http"] === "string" ? flags["http"] : void 0;
|
|
55
|
-
if (raw === void 0 || raw.trim() === "") return 0;
|
|
56
|
-
const port = Number(raw);
|
|
57
|
-
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
58
|
-
throw new Error(`invalid --port "${raw}" \u2014 expected an integer between 0 and 65535`);
|
|
59
|
-
}
|
|
60
|
-
return port;
|
|
61
|
-
}
|
|
62
|
-
async function loadSelectedMcpServeContent(flags, cwd) {
|
|
63
|
-
const resources = [];
|
|
64
|
-
const prompts = [];
|
|
65
|
-
const resourceUris = /* @__PURE__ */ new Set();
|
|
66
|
-
const promptNames = /* @__PURE__ */ new Set();
|
|
67
|
-
for (const selected of commaSeparatedFlag(flags, "resources")) {
|
|
68
|
-
const file = path.resolve(cwd, selected);
|
|
69
|
-
const data = await readSelectedFile(file);
|
|
70
|
-
const uri = pathToFileURL(file).toString();
|
|
71
|
-
if (resourceUris.has(uri)) throw new Error(`Duplicate MCP resource selection: ${selected}`);
|
|
72
|
-
resourceUris.add(uri);
|
|
73
|
-
const mimeType = mimeTypeFor(file);
|
|
74
|
-
resources.push({
|
|
75
|
-
uri,
|
|
76
|
-
name: path.basename(file),
|
|
77
|
-
mimeType,
|
|
78
|
-
size: data.byteLength,
|
|
79
|
-
contents: [
|
|
80
|
-
isTextMime(mimeType) ? { uri, mimeType, text: data.toString("utf8") } : { uri, mimeType, blob: data.toString("base64") }
|
|
81
|
-
]
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
for (const selected of commaSeparatedFlag(flags, "prompts")) {
|
|
85
|
-
const file = path.resolve(cwd, selected);
|
|
86
|
-
const data = await readSelectedFile(file);
|
|
87
|
-
const name = path.basename(file, path.extname(file)).replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
88
|
-
if (!name) throw new Error(`Cannot derive an MCP prompt name from: ${selected}`);
|
|
89
|
-
if (promptNames.has(name)) throw new Error(`Duplicate MCP prompt name: ${name}`);
|
|
90
|
-
promptNames.add(name);
|
|
91
|
-
const template = data.toString("utf8");
|
|
92
|
-
const argumentNames = Array.from(
|
|
93
|
-
template.matchAll(/\{\{([A-Za-z_][A-Za-z0-9_.-]*)\}\}/g),
|
|
94
|
-
(match) => match[1]
|
|
95
|
-
).filter((value, index, all) => !!value && all.indexOf(value) === index);
|
|
96
|
-
prompts.push({
|
|
97
|
-
name,
|
|
98
|
-
description: `Explicitly selected local prompt: ${path.basename(file)}`,
|
|
99
|
-
arguments: argumentNames.map((argument) => ({ name: argument, required: true })),
|
|
100
|
-
template
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
return { resources, prompts };
|
|
104
|
-
}
|
|
105
|
-
function commaSeparatedFlag(flags, name) {
|
|
106
|
-
const value = flags[name];
|
|
107
|
-
if (value === void 0) return [];
|
|
108
|
-
if (typeof value !== "string" || value.trim() === "") {
|
|
109
|
-
throw new Error(`--${name} requires a comma-separated file list`);
|
|
110
|
-
}
|
|
111
|
-
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
112
|
-
}
|
|
113
|
-
async function readSelectedFile(file) {
|
|
114
|
-
const stat2 = await fs.stat(file);
|
|
115
|
-
if (!stat2.isFile()) throw new Error(`Selected MCP content is not a regular file: ${file}`);
|
|
116
|
-
if (stat2.size > DEFAULT_MCP_INSERTION_MAX_BYTES) {
|
|
117
|
-
throw new Error(
|
|
118
|
-
`Selected MCP content exceeds ${DEFAULT_MCP_INSERTION_MAX_BYTES} bytes: ${file}`
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
return fs.readFile(file);
|
|
122
|
-
}
|
|
123
|
-
function mimeTypeFor(file) {
|
|
124
|
-
switch (path.extname(file).toLowerCase()) {
|
|
125
|
-
case ".md":
|
|
126
|
-
case ".mdx":
|
|
127
|
-
return "text/markdown";
|
|
128
|
-
case ".json":
|
|
129
|
-
return "application/json";
|
|
130
|
-
case ".yaml":
|
|
131
|
-
case ".yml":
|
|
132
|
-
return "application/yaml";
|
|
133
|
-
case ".html":
|
|
134
|
-
return "text/html";
|
|
135
|
-
case ".css":
|
|
136
|
-
return "text/css";
|
|
137
|
-
case ".js":
|
|
138
|
-
case ".mjs":
|
|
139
|
-
return "text/javascript";
|
|
140
|
-
case ".ts":
|
|
141
|
-
case ".tsx":
|
|
142
|
-
return "text/typescript";
|
|
143
|
-
case ".txt":
|
|
144
|
-
case ".log":
|
|
145
|
-
return "text/plain";
|
|
146
|
-
case ".png":
|
|
147
|
-
return "image/png";
|
|
148
|
-
case ".jpg":
|
|
149
|
-
case ".jpeg":
|
|
150
|
-
return "image/jpeg";
|
|
151
|
-
case ".gif":
|
|
152
|
-
return "image/gif";
|
|
153
|
-
case ".webp":
|
|
154
|
-
return "image/webp";
|
|
155
|
-
default:
|
|
156
|
-
return "application/octet-stream";
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function isTextMime(mimeType) {
|
|
160
|
-
return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType === "application/yaml";
|
|
161
|
-
}
|
|
162
|
-
function resolveServeFsRestriction(config) {
|
|
163
|
-
return config.tools?.restrictToProjectRoot ?? true;
|
|
164
|
-
}
|
|
165
|
-
function makeServeContext(cwd, projectRoot, signal, restrictFsToRoot = true) {
|
|
166
|
-
const provider = {
|
|
167
|
-
id: "mcp-serve",
|
|
168
|
-
capabilities: { maxContext: 0 },
|
|
169
|
-
complete: async () => {
|
|
170
|
-
throw new Error("no model provider in `mcp serve` mode");
|
|
171
|
-
},
|
|
172
|
-
stream: () => {
|
|
173
|
-
throw new Error("no model provider in `mcp serve` mode");
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
const session = { append: async () => {
|
|
177
|
-
} };
|
|
178
|
-
const tokenCounter = {
|
|
179
|
-
account: () => {
|
|
180
|
-
},
|
|
181
|
-
total: () => ({ input: 0, output: 0 }),
|
|
182
|
-
estimateCost: () => ({ total: 0 })
|
|
183
|
-
};
|
|
184
|
-
return new Context({
|
|
185
|
-
systemPrompt: [],
|
|
186
|
-
provider,
|
|
187
|
-
session,
|
|
188
|
-
signal,
|
|
189
|
-
tokenCounter,
|
|
190
|
-
cwd,
|
|
191
|
-
projectRoot,
|
|
192
|
-
allowOutsideProjectRoot: !restrictFsToRoot,
|
|
193
|
-
model: "mcp-serve",
|
|
194
|
-
tools: []
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
async function selectExposedTools(registry, ctx, policy, whitelist) {
|
|
198
|
-
const allowed = [];
|
|
199
|
-
for (const tool of registry.list()) {
|
|
200
|
-
if (whitelist && !whitelist.has(tool.name)) continue;
|
|
201
|
-
const decision = await policy.evaluate(tool, {}, ctx);
|
|
202
|
-
if (decision.permission === "auto") allowed.push(tool);
|
|
203
|
-
}
|
|
204
|
-
return allowed;
|
|
205
|
-
}
|
|
206
|
-
async function serveMcpStdio(deps, positional) {
|
|
207
|
-
const flags = deps.flags ?? {};
|
|
208
|
-
const yolo = flags["yolo"] === true || flags["allow-all"] === true;
|
|
209
|
-
const whitelist = parseToolsFlag(flags, positional);
|
|
210
|
-
if (flags["tools"] !== void 0 && whitelist === null) {
|
|
211
|
-
process.stderr.write(
|
|
212
|
-
"wrongstack MCP server: --tools requires a comma-separated tool list (e.g. `--tools read,grep` or `--tools=read,grep`)\n"
|
|
213
|
-
);
|
|
214
|
-
return 1;
|
|
215
|
-
}
|
|
216
|
-
const log = (m) => process.stderr.write(`${m}
|
|
217
|
-
`);
|
|
218
|
-
let selectedContent;
|
|
219
|
-
try {
|
|
220
|
-
selectedContent = await loadSelectedMcpServeContent(flags, deps.cwd);
|
|
221
|
-
} catch (err) {
|
|
222
|
-
log(`wrongstack MCP server: ${err instanceof Error ? err.message : String(err)}`);
|
|
223
|
-
return 1;
|
|
224
|
-
}
|
|
225
|
-
wireKanbanPorts();
|
|
226
|
-
let registry = deps.toolRegistry;
|
|
227
|
-
if (!registry) {
|
|
228
|
-
registry = new ToolRegistry();
|
|
229
|
-
registerBuiltinToolTier({
|
|
230
|
-
registry,
|
|
231
|
-
tier: normalizeTokenSavingTier(deps.config.features.tokenSavingMode)
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
const controller = new AbortController();
|
|
235
|
-
const selectionCtx = makeServeContext(
|
|
236
|
-
deps.cwd,
|
|
237
|
-
deps.projectRoot,
|
|
238
|
-
controller.signal,
|
|
239
|
-
resolveServeFsRestriction(deps.config)
|
|
240
|
-
);
|
|
241
|
-
const permissionPolicy = yolo ? yoloServePolicy() : new AutoApprovePermissionPolicy();
|
|
242
|
-
const executor = new ToolExecutor(registry, {
|
|
243
|
-
permissionPolicy,
|
|
244
|
-
secretScrubber: new DefaultSecretScrubber(),
|
|
245
|
-
maxToolTimeoutMs: deps.config.tools?.maxToolTimeoutMs ?? 3e5,
|
|
246
|
-
perIterationOutputCapBytes: 1e6,
|
|
247
|
-
// Kanban tracks work; it does not gate it. Off unless the operator opts in.
|
|
248
|
-
// See wiring/pipeline.ts for why all four hosts must agree.
|
|
249
|
-
requireKanbanGovernance: deps.config.tools?.kanbanGovernance ?? false
|
|
250
|
-
});
|
|
251
|
-
const allowed = await selectExposedTools(registry, selectionCtx, permissionPolicy, whitelist);
|
|
252
|
-
const allowedNames = new Set(allowed.map((t) => t.name));
|
|
253
|
-
if (allowed.length === 0) {
|
|
254
|
-
log(
|
|
255
|
-
"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."
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
let counter = 0;
|
|
259
|
-
const host = {
|
|
260
|
-
listTools: () => allowed.map((t) => ({
|
|
261
|
-
name: t.name,
|
|
262
|
-
description: t.description,
|
|
263
|
-
inputSchema: t.inputSchema ?? { type: "object" }
|
|
264
|
-
})),
|
|
265
|
-
callTool: async (name, callArgs) => {
|
|
266
|
-
if (!allowedNames.has(name)) {
|
|
267
|
-
return { content: `Tool "${name}" is not exposed by this server`, isError: true };
|
|
268
|
-
}
|
|
269
|
-
const use = {
|
|
270
|
-
type: "tool_use",
|
|
271
|
-
id: `srv_${++counter}`,
|
|
272
|
-
name,
|
|
273
|
-
input: callArgs
|
|
274
|
-
};
|
|
275
|
-
const requestCtx = makeServeContext(
|
|
276
|
-
deps.cwd,
|
|
277
|
-
deps.projectRoot,
|
|
278
|
-
controller.signal,
|
|
279
|
-
resolveServeFsRestriction(deps.config)
|
|
280
|
-
);
|
|
281
|
-
const batch = await executor.executeBatch([use], requestCtx, "sequential");
|
|
282
|
-
const result = batch.outputs[0]?.result;
|
|
283
|
-
if (!result || result.type === "tool_confirm_pending") {
|
|
284
|
-
return {
|
|
285
|
-
content: `Tool "${name}" requires interactive confirmation, which is unavailable over MCP`,
|
|
286
|
-
isError: true
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
return { content: result.content, isError: Boolean(result.is_error) };
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
const server = new MCPServer({
|
|
293
|
-
host,
|
|
294
|
-
resources: selectedContent.resources,
|
|
295
|
-
prompts: selectedContent.prompts,
|
|
296
|
-
logger: { warn: (m) => log(`[mcp-serve] ${m}`) }
|
|
297
|
-
});
|
|
298
|
-
const mode = yolo ? "yolo: all tools" : "safe: read-only tools";
|
|
299
|
-
if (flags["http"] === true || typeof flags["http"] === "string" || flags["port"] || flags["host"]) {
|
|
300
|
-
let port;
|
|
301
|
-
try {
|
|
302
|
-
port = resolveServeHttpPort(flags);
|
|
303
|
-
} catch (err) {
|
|
304
|
-
log(`wrongstack MCP server: ${err instanceof Error ? err.message : String(err)}`);
|
|
305
|
-
return 1;
|
|
306
|
-
}
|
|
307
|
-
const httpHost = typeof flags["host"] === "string" ? flags["host"] : "127.0.0.1";
|
|
308
|
-
const tokenFromArg = typeof flags["token"] === "string" ? flags["token"] : void 0;
|
|
309
|
-
const token = tokenFromArg ?? process.env.WRONGSTACK_MCP_TOKEN?.trim() ?? void 0;
|
|
310
|
-
if (tokenFromArg) {
|
|
311
|
-
log(
|
|
312
|
-
"[mcp-serve] --token puts the auth token in this process command line, which other local processes can read. Prefer WRONGSTACK_MCP_TOKEN."
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
let handle2;
|
|
316
|
-
try {
|
|
317
|
-
handle2 = await serveHttp(server, {
|
|
318
|
-
port,
|
|
319
|
-
host: httpHost,
|
|
320
|
-
token,
|
|
321
|
-
logger: { warn: (m) => log(`[mcp-serve] ${m}`) }
|
|
322
|
-
});
|
|
323
|
-
} catch (err) {
|
|
324
|
-
log(`wrongstack MCP server: ${err instanceof Error ? err.message : String(err)}`);
|
|
325
|
-
return 1;
|
|
326
|
-
}
|
|
327
|
-
log(
|
|
328
|
-
`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]" : ""}.`
|
|
329
|
-
);
|
|
330
|
-
await new Promise((resolve2) => {
|
|
331
|
-
const stop = () => resolve2();
|
|
332
|
-
process.once("SIGINT", stop);
|
|
333
|
-
process.once("SIGTERM", stop);
|
|
334
|
-
});
|
|
335
|
-
await handle2.close();
|
|
336
|
-
controller.abort();
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
log(
|
|
340
|
-
`wrongstack MCP server ready on stdio \u2014 exposing ${allowed.length} tool(s), ${selectedContent.resources.length} resource(s), ${selectedContent.prompts.length} prompt(s) (${mode}).`
|
|
341
|
-
);
|
|
342
|
-
const handle = serveStdio(server);
|
|
343
|
-
await handle.done;
|
|
344
|
-
controller.abort();
|
|
345
|
-
return 0;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// src/mcp-serve-project.ts
|
|
349
|
-
var PROJECT_MCP_SERVERS = [
|
|
350
|
-
"mailbox",
|
|
351
|
-
"kanban",
|
|
352
|
-
"codebase-index",
|
|
353
|
-
"requirement-intake",
|
|
354
|
-
"sage"
|
|
355
|
-
];
|
|
356
|
-
function isProjectMcpServer(value) {
|
|
357
|
-
return PROJECT_MCP_SERVERS.includes(value);
|
|
358
|
-
}
|
|
359
|
-
var SERVER_FLAGS = [
|
|
360
|
-
"project-root",
|
|
361
|
-
"actor",
|
|
362
|
-
"session-id",
|
|
363
|
-
"name",
|
|
364
|
-
"role",
|
|
365
|
-
"writable",
|
|
366
|
-
"admin",
|
|
367
|
-
"destructive",
|
|
368
|
-
"http",
|
|
369
|
-
"stdio",
|
|
370
|
-
"port",
|
|
371
|
-
"host",
|
|
372
|
-
"token",
|
|
373
|
-
"index-dir",
|
|
374
|
-
"origin",
|
|
375
|
-
"watch"
|
|
376
|
-
];
|
|
377
|
-
var SWITCHES = /* @__PURE__ */ new Set([
|
|
378
|
-
"--writable",
|
|
379
|
-
"--admin",
|
|
380
|
-
"--destructive",
|
|
381
|
-
"--http",
|
|
382
|
-
"--stdio",
|
|
383
|
-
"--watch"
|
|
384
|
-
]);
|
|
385
|
-
function normalizeSwitches(args) {
|
|
386
|
-
const out = [];
|
|
387
|
-
for (let i = 0; i < args.length; i++) {
|
|
388
|
-
const arg = args[i];
|
|
389
|
-
const eq = arg.indexOf("=");
|
|
390
|
-
const flag = eq === -1 ? arg : arg.slice(0, eq);
|
|
391
|
-
if (!SWITCHES.has(flag)) {
|
|
392
|
-
out.push(arg);
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
const value = eq === -1 ? void 0 : arg.slice(eq + 1);
|
|
396
|
-
if (value === "false") continue;
|
|
397
|
-
out.push(flag);
|
|
398
|
-
if (eq === -1 && args[i + 1] === "true") i++;
|
|
399
|
-
}
|
|
400
|
-
return out;
|
|
401
|
-
}
|
|
402
|
-
function optionValue(args, name) {
|
|
403
|
-
const flag = `--${name}`;
|
|
404
|
-
for (let i = 0; i < args.length; i++) {
|
|
405
|
-
const arg = args[i];
|
|
406
|
-
if (arg === flag) {
|
|
407
|
-
const next = args[i + 1];
|
|
408
|
-
return next !== void 0 && !next.startsWith("--") ? next : void 0;
|
|
409
|
-
}
|
|
410
|
-
if (arg.startsWith(`${flag}=`)) return arg.slice(flag.length + 1);
|
|
411
|
-
}
|
|
412
|
-
return void 0;
|
|
413
|
-
}
|
|
414
|
-
async function serveProjectMcp(server, rawArgs, deps) {
|
|
415
|
-
const args = normalizeSwitches(restoreFlags(rawArgs, deps, SERVER_FLAGS));
|
|
416
|
-
if (server === "sage") {
|
|
417
|
-
const { serveAttachedSageMcpStdio } = await import("@wrongstack/sage-mcp");
|
|
418
|
-
await serveAttachedSageMcpStdio({
|
|
419
|
-
// A client may start this server from any directory; an explicit root wins.
|
|
420
|
-
projectRoot: optionValue(args, "project-root") ?? deps.projectRoot,
|
|
421
|
-
directory: deps.config.Sage?.storage?.directory,
|
|
422
|
-
origin: optionValue(args, "origin")?.trim() || "mcp-client"
|
|
423
|
-
});
|
|
424
|
-
return 0;
|
|
425
|
-
}
|
|
426
|
-
const argv = optionValue(args, "project-root") === void 0 ? [...args, "--project-root", deps.projectRoot] : args;
|
|
427
|
-
if (server === "mailbox") {
|
|
428
|
-
const { runMailboxMcpServer } = await import("@wrongstack/mailbox-mcp");
|
|
429
|
-
return runMailboxMcpServer(argv);
|
|
430
|
-
}
|
|
431
|
-
if (server === "kanban") {
|
|
432
|
-
const { runKanbanMcpServer } = await import("@wrongstack/kanban-mcp");
|
|
433
|
-
return runKanbanMcpServer(argv);
|
|
434
|
-
}
|
|
435
|
-
if (server === "requirement-intake") {
|
|
436
|
-
const { runRequirementIntakeMcpServer } = await import("@wrongstack/requirement-intake-mcp");
|
|
437
|
-
return runRequirementIntakeMcpServer(argv);
|
|
438
|
-
}
|
|
439
|
-
const { runCodebaseIndexMcpServer } = await import("@wrongstack/codebase-index-mcp");
|
|
440
|
-
return runCodebaseIndexMcpServer(argv);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// src/subcommands/handlers/mcp.ts
|
|
444
|
-
var BUILT_IN_MCP = allServers();
|
|
445
|
-
var mcpCmd = async (args, deps) => {
|
|
446
|
-
args = restoreFlags(args, deps, ["enable", "e", "transport", "url", "command"]);
|
|
447
|
-
const sub = args[0];
|
|
448
|
-
if (sub === "serve") {
|
|
449
|
-
if (isProjectMcpServer(args[1])) return serveProjectMcp(args[1], args.slice(2), deps);
|
|
450
|
-
return serveMcpStdio(deps, args.slice(1));
|
|
451
|
-
}
|
|
452
|
-
if (!sub || sub === "list") {
|
|
453
|
-
const servers = deps.config.mcpServers ?? {};
|
|
454
|
-
if (Object.keys(servers).length === 0) {
|
|
455
|
-
deps.renderer.write("No MCP servers configured.\n");
|
|
456
|
-
deps.renderer.write("Use `wstack mcp add <name>` or set mcpServers in your config.\n");
|
|
457
|
-
return 0;
|
|
458
|
-
}
|
|
459
|
-
for (const [name, entry] of Object.entries(servers)) {
|
|
460
|
-
const cfg = resolveMcpServerConfig(name, entry);
|
|
461
|
-
const status = entry?.enabled === false ? "disabled" : "enabled";
|
|
462
|
-
const description = cfg?.description ?? entry?.description;
|
|
463
|
-
const desc = description ? ` # ${description}` : "";
|
|
464
|
-
const transport = cfg?.transport ?? "invalid (no transport)";
|
|
465
|
-
deps.renderer.write(` ${name.padEnd(20)} ${transport.padEnd(16)} ${status}${desc}
|
|
466
|
-
`);
|
|
467
|
-
}
|
|
468
|
-
return 0;
|
|
469
|
-
}
|
|
470
|
-
if (sub === "add") {
|
|
471
|
-
return addMcpServer(args, deps);
|
|
472
|
-
}
|
|
473
|
-
if (sub === "remove") {
|
|
474
|
-
const name = args[1];
|
|
475
|
-
if (!name) {
|
|
476
|
-
deps.renderer.writeError("Usage: wstack mcp remove <name>\n");
|
|
477
|
-
return 1;
|
|
478
|
-
}
|
|
479
|
-
return removeMcpServer(name, deps);
|
|
480
|
-
}
|
|
481
|
-
if (sub === "restart") {
|
|
482
|
-
deps.renderer.writeWarning(
|
|
483
|
-
"mcp restart is only available in REPL mode. Use /mcp restart instead."
|
|
484
|
-
);
|
|
485
|
-
return 0;
|
|
486
|
-
}
|
|
487
|
-
deps.renderer.writeError(`Unknown mcp subcommand: ${sub}`);
|
|
488
|
-
return 1;
|
|
489
|
-
};
|
|
490
|
-
async function addMcpServer(args, deps) {
|
|
491
|
-
const configPath = activeProfileConfigPath(deps.paths, deps.config);
|
|
492
|
-
const name = args[1];
|
|
493
|
-
const enable = args.includes("--enable") || args.includes("-e");
|
|
494
|
-
if (!name) {
|
|
495
|
-
deps.renderer.writeError("Usage: wstack mcp add <name>\n");
|
|
496
|
-
deps.renderer.write("Available servers:\n");
|
|
497
|
-
for (const [sname, scfg] of Object.entries(deps.config.mcpServers ?? {}))
|
|
498
|
-
deps.renderer.write(` ${sname.padEnd(20)} ${scfg.description ?? scfg.transport}
|
|
499
|
-
`);
|
|
500
|
-
if (Object.keys(deps.config.mcpServers ?? {}).length === 0)
|
|
501
|
-
for (const k of Object.keys(BUILT_IN_MCP)) {
|
|
502
|
-
const s = expectDefined(BUILT_IN_MCP[k]);
|
|
503
|
-
deps.renderer.write(` ${k.padEnd(20)} ${s.description}
|
|
504
|
-
`);
|
|
505
|
-
}
|
|
506
|
-
deps.renderer.write("\nRun `wstack mcp add <name> --enable` to enable immediately.\n");
|
|
507
|
-
return 1;
|
|
508
|
-
}
|
|
509
|
-
const factory = Object.hasOwn(BUILT_IN_MCP, name) ? BUILT_IN_MCP[name] : void 0;
|
|
510
|
-
if (!factory) {
|
|
511
|
-
deps.renderer.writeError(
|
|
512
|
-
`Unknown server "${name}". Run \`wstack mcp add\` without args to see available servers.
|
|
513
|
-
`
|
|
514
|
-
);
|
|
515
|
-
return 1;
|
|
516
|
-
}
|
|
517
|
-
const serverCfg = { ...factory };
|
|
518
|
-
serverCfg.enabled = enable;
|
|
519
|
-
const existing = await readJsonObjectFile(configPath);
|
|
520
|
-
const mcpServers = isRecord(existing.mcpServers) ? existing.mcpServers : {};
|
|
521
|
-
if (Object.hasOwn(mcpServers, name))
|
|
522
|
-
deps.renderer.writeWarning(`Server "${name}" already in config. Updating.
|
|
523
|
-
`);
|
|
524
|
-
await updateJsonObjectFile(configPath, (config) => {
|
|
525
|
-
setJsonPath(config, ["mcpServers", name], serverCfg);
|
|
526
|
-
});
|
|
527
|
-
const verb = enable ? "Enabled" : "Added (disabled \u2014 set enabled:true to activate)";
|
|
528
|
-
deps.renderer.writeInfo(
|
|
529
|
-
`${verb} "${name}" (${serverCfg.transport}). Config written to ${configPath}.
|
|
530
|
-
`
|
|
531
|
-
);
|
|
532
|
-
return 0;
|
|
533
|
-
}
|
|
534
|
-
async function removeMcpServer(name, deps) {
|
|
535
|
-
const configPath = activeProfileConfigPath(deps.paths, deps.config);
|
|
536
|
-
if (!await jsonObjectFileExists(configPath)) {
|
|
537
|
-
deps.renderer.writeError("No config file found.\n");
|
|
538
|
-
return 1;
|
|
539
|
-
}
|
|
540
|
-
const existing = await readJsonObjectFile(configPath);
|
|
541
|
-
const mcpServers = isRecord(existing.mcpServers) ? existing.mcpServers : {};
|
|
542
|
-
if (!Object.hasOwn(mcpServers, name)) {
|
|
543
|
-
deps.renderer.writeError(`Server "${name}" not in config.
|
|
544
|
-
`);
|
|
545
|
-
return 1;
|
|
546
|
-
}
|
|
547
|
-
await updateJsonObjectFile(configPath, (config) => {
|
|
548
|
-
removeJsonPath(config, ["mcpServers", name]);
|
|
549
|
-
});
|
|
550
|
-
deps.renderer.writeInfo(`Removed "${name}" from config.
|
|
551
|
-
`);
|
|
552
|
-
return 0;
|
|
553
|
-
}
|
|
554
|
-
function isRecord(value) {
|
|
555
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
556
|
-
}
|
|
557
|
-
export {
|
|
558
|
-
mcpCmd
|
|
559
|
-
};
|
|
560
|
-
//# sourceMappingURL=mcp-W7A5HUXV.js.map
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
handleHelpVersionShortCircuit
|
|
3
|
-
} from "./chunk-TAE3EGBM.js";
|
|
4
|
-
import "./chunk-75OLD563.js";
|
|
5
|
-
import "./chunk-6VPWQ4TG.js";
|
|
6
|
-
import "./chunk-HKMOIIPQ.js";
|
|
7
|
-
import "./chunk-W23PY4IJ.js";
|
|
8
|
-
export {
|
|
9
|
-
handleHelpVersionShortCircuit
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=short-circuit-flags-QEXUS3BY.js.map
|