muonroi-cli 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +122 -122
- package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
- package/dist/src/agent-harness/__tests__/mock-model.spec.js +48 -1
- package/dist/src/agent-harness/mock-model.d.ts +11 -0
- package/dist/src/agent-harness/mock-model.js +21 -0
- package/dist/src/cli/cost-forensics.js +12 -12
- package/dist/src/council/__tests__/clarification-prompt.test.js +51 -0
- package/dist/src/council/__tests__/clarifier-ready-gate.test.js +32 -0
- package/dist/src/council/__tests__/decisions-lock.test.js +17 -1
- package/dist/src/council/__tests__/oauth-reachable.test.d.ts +1 -0
- package/dist/src/council/__tests__/oauth-reachable.test.js +31 -0
- package/dist/src/council/__tests__/parse-outcome-fallback.test.js +11 -0
- package/dist/src/council/clarifier.js +9 -1
- package/dist/src/council/debate.js +5 -1
- package/dist/src/council/decisions-lock.js +3 -3
- package/dist/src/council/index.js +12 -5
- package/dist/src/council/leader.d.ts +0 -17
- package/dist/src/council/leader.js +22 -15
- package/dist/src/council/planner.js +1 -1
- package/dist/src/council/prompts.js +63 -57
- package/dist/src/council/types.d.ts +7 -0
- package/dist/src/ee/__tests__/ee-onboarding.test.d.ts +1 -0
- package/dist/src/ee/__tests__/ee-onboarding.test.js +32 -0
- package/dist/src/ee/auth.d.ts +9 -0
- package/dist/src/ee/auth.js +19 -0
- package/dist/src/ee/ee-onboarding.d.ts +5 -0
- package/dist/src/ee/ee-onboarding.js +76 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/headless/output.js +6 -4
- package/dist/src/headless/output.test.js +4 -3
- package/dist/src/index.js +20 -1
- package/dist/src/mcp/__tests__/auto-setup.test.js +74 -0
- package/dist/src/mcp/__tests__/client-pool.spec.d.ts +1 -0
- package/dist/src/mcp/__tests__/client-pool.spec.js +98 -0
- package/dist/src/mcp/__tests__/parallel-build.spec.d.ts +1 -0
- package/dist/src/mcp/__tests__/parallel-build.spec.js +67 -0
- package/dist/src/mcp/__tests__/smart-filter.test.js +56 -0
- package/dist/src/mcp/auto-setup.js +56 -2
- package/dist/src/mcp/client-pool.d.ts +46 -0
- package/dist/src/mcp/client-pool.js +212 -0
- package/dist/src/mcp/oauth-callback.js +2 -2
- package/dist/src/mcp/parse-headers.test.js +14 -14
- package/dist/src/mcp/runtime.d.ts +28 -0
- package/dist/src/mcp/runtime.js +117 -51
- package/dist/src/mcp/self-verify-runner.d.ts +14 -0
- package/dist/src/mcp/self-verify-runner.js +38 -0
- package/dist/src/mcp/setup-guide-text.d.ts +9 -0
- package/dist/src/mcp/setup-guide-text.js +84 -0
- package/dist/src/mcp/smart-filter.js +49 -0
- package/dist/src/mcp/smoke.test.js +43 -43
- package/dist/src/mcp/tools-server.d.ts +7 -0
- package/dist/src/mcp/tools-server.js +19 -22
- package/dist/src/models/catalog.json +349 -349
- package/dist/src/ops/__tests__/doctor-ee-health.test.js +21 -0
- package/dist/src/ops/doctor.d.ts +3 -2
- package/dist/src/ops/doctor.js +47 -11
- package/dist/src/ops/doctor.test.js +4 -3
- package/dist/src/orchestrator/__tests__/mcp-capability-block.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/mcp-capability-block.test.js +39 -0
- package/dist/src/orchestrator/__tests__/project-stack.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/project-stack.test.js +65 -0
- package/dist/src/orchestrator/batch-turn-runner.js +7 -11
- package/dist/src/orchestrator/message-processor.js +57 -27
- package/dist/src/orchestrator/orchestrator.js +26 -0
- package/dist/src/orchestrator/prompts.d.ts +51 -0
- package/dist/src/orchestrator/prompts.js +257 -134
- package/dist/src/orchestrator/scope-ceiling.js +6 -1
- package/dist/src/orchestrator/stream-runner.js +20 -15
- package/dist/src/orchestrator/text-tool-call-detector.test.js +13 -13
- package/dist/src/pil/__tests__/clarity-gate.test.js +24 -215
- package/dist/src/pil/__tests__/config.test.js +1 -17
- package/dist/src/pil/__tests__/discovery.test.js +144 -11
- package/dist/src/pil/__tests__/layer1-intent-trace.test.js +7 -2
- package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
- package/dist/src/pil/__tests__/layer16-clarity.test.js +32 -116
- package/dist/src/pil/__tests__/layer4-gsd.test.js +37 -0
- package/dist/src/pil/__tests__/layer6-output.test.js +137 -18
- package/dist/src/pil/__tests__/llm-classify.test.js +49 -2
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +2 -0
- package/dist/src/pil/agent-operating-contract.test.js +7 -2
- package/dist/src/pil/cheap-model-playbook.js +35 -35
- package/dist/src/pil/cheap-model-workbooks.js +16 -13
- package/dist/src/pil/clarity-gate.d.ts +21 -19
- package/dist/src/pil/clarity-gate.js +26 -153
- package/dist/src/pil/config.d.ts +9 -1
- package/dist/src/pil/config.js +15 -4
- package/dist/src/pil/discovery.js +211 -136
- package/dist/src/pil/layer1-intent.d.ts +12 -0
- package/dist/src/pil/layer1-intent.js +283 -38
- package/dist/src/pil/layer1-intent.test.js +210 -4
- package/dist/src/pil/layer16-clarity.d.ts +25 -11
- package/dist/src/pil/layer16-clarity.js +19 -306
- package/dist/src/pil/layer4-gsd.js +18 -6
- package/dist/src/pil/layer6-output.d.ts +2 -0
- package/dist/src/pil/layer6-output.js +137 -22
- package/dist/src/pil/llm-classify.d.ts +26 -0
- package/dist/src/pil/llm-classify.js +34 -5
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +82 -76
- package/dist/src/pil/schema.d.ts +8 -0
- package/dist/src/pil/schema.js +12 -1
- package/dist/src/pil/task-tier-map.js +4 -0
- package/dist/src/pil/types.d.ts +11 -1
- package/dist/src/product-loop/done-gate.js +3 -3
- package/dist/src/product-loop/loop-driver.js +18 -18
- package/dist/src/product-loop/progress-snapshot.js +4 -4
- package/dist/src/providers/auth/gemini-oauth.js +6 -15
- package/dist/src/providers/auth/grok-oauth.js +6 -15
- package/dist/src/providers/auth/openai-oauth.js +6 -15
- package/dist/src/providers/mcp-vision-bridge.js +48 -48
- package/dist/src/reporter/index.js +1 -1
- package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.js +60 -60
- package/dist/src/scaffold/init-new.js +453 -453
- package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
- package/dist/src/self-qa/agentic-loop.js +24 -19
- package/dist/src/self-qa/spec-emitter.js +26 -23
- package/dist/src/storage/__tests__/migrations.test.js +2 -2
- package/dist/src/storage/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +122 -122
- package/dist/src/storage/sessions.js +42 -42
- package/dist/src/storage/transcript.js +91 -84
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- package/dist/src/tools/__tests__/native-tools.test.d.ts +1 -0
- package/dist/src/tools/__tests__/native-tools.test.js +53 -0
- package/dist/src/tools/git-safety.d.ts +61 -0
- package/dist/src/tools/git-safety.js +141 -0
- package/dist/src/tools/git-safety.test.d.ts +1 -0
- package/dist/src/tools/git-safety.test.js +111 -0
- package/dist/src/tools/native-tools.d.ts +31 -0
- package/dist/src/tools/native-tools.js +273 -0
- package/dist/src/tools/registry-git-safety.test.d.ts +7 -0
- package/dist/src/tools/registry-git-safety.test.js +92 -0
- package/dist/src/tools/registry.js +39 -4
- package/dist/src/ui/__tests__/markdown-render.test.d.ts +1 -0
- package/dist/src/ui/__tests__/markdown-render.test.js +48 -0
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/components/message-view.js +4 -1
- package/dist/src/ui/components/structured-response-view.js +7 -3
- package/dist/src/ui/components/tool-group.js +7 -1
- package/dist/src/ui/markdown-render.d.ts +41 -0
- package/dist/src/ui/markdown-render.js +223 -0
- package/dist/src/ui/markdown.d.ts +10 -0
- package/dist/src/ui/markdown.js +12 -35
- package/dist/src/ui/slash/council-inspect.js +4 -4
- package/dist/src/ui/slash/export.js +4 -4
- package/dist/src/ui/utils/text.d.ts +8 -0
- package/dist/src/ui/utils/text.js +16 -0
- package/dist/src/ui/utils/text.test.d.ts +1 -0
- package/dist/src/ui/utils/text.test.js +23 -0
- package/dist/src/usage/ledger.js +48 -15
- package/dist/src/utils/__tests__/footprint-gitignore.test.d.ts +1 -0
- package/dist/src/utils/__tests__/footprint-gitignore.test.js +50 -0
- package/dist/src/utils/clipboard-image.js +23 -23
- package/dist/src/utils/open-url.d.ts +56 -0
- package/dist/src/utils/open-url.js +58 -0
- package/dist/src/utils/open-url.test.d.ts +1 -0
- package/dist/src/utils/open-url.test.js +86 -0
- package/dist/src/utils/settings.d.ts +12 -0
- package/dist/src/utils/settings.js +48 -0
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
- package/dist/src/verify/environment.js +2 -1
- package/package.json +1 -1
- package/dist/src/pil/layer16-clarity.test.js +0 -31
- /package/dist/src/{pil/layer16-clarity.test.d.ts → council/__tests__/clarification-prompt.test.d.ts} +0 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/mcp/client-pool.ts
|
|
3
|
+
*
|
|
4
|
+
* Cross-turn MCP client pool. The orchestrator rebuilds its tool set every turn
|
|
5
|
+
* (and closes it in a `finally`), which previously cold-spawned EVERY stdio MCP
|
|
6
|
+
* server (npx filesystem/playwright/fetch/tavily/…) on every turn — each spawn
|
|
7
|
+
* costs ~1-3s and raced the build deadline. This pool connects each server ONCE
|
|
8
|
+
* and reuses the live client across turns: only the first turn that needs a
|
|
9
|
+
* server pays the cold-start; later turns select its (already-built) tools
|
|
10
|
+
* instantly. Real teardown happens once on orchestrator/process shutdown.
|
|
11
|
+
*
|
|
12
|
+
* Per-turn smart-filtering is unchanged — the caller still passes only the
|
|
13
|
+
* servers relevant to this message; the pool just avoids re-spawning the ones
|
|
14
|
+
* it has already connected.
|
|
15
|
+
*
|
|
16
|
+
* Self-healing: a server that fails to connect is evicted (not cached as a
|
|
17
|
+
* rejection), so a later turn retries. A live client whose child process dies
|
|
18
|
+
* later is evicted when one of its tool calls hits a transport/connection error,
|
|
19
|
+
* so the next turn reconnects fresh.
|
|
20
|
+
*/
|
|
21
|
+
import { connectOneServer, getMcpBuildDeadlineMs, } from "./runtime.js";
|
|
22
|
+
import { validateMcpServerConfig } from "./validate.js";
|
|
23
|
+
const pool = new Map();
|
|
24
|
+
/**
|
|
25
|
+
* Stable identity for a connected server. Includes cwd (stdio servers like
|
|
26
|
+
* filesystem inherit it) + command/args/url/env so a config or cwd change
|
|
27
|
+
* reconnects instead of reusing a stale client.
|
|
28
|
+
*/
|
|
29
|
+
function serverKey(s) {
|
|
30
|
+
return JSON.stringify({
|
|
31
|
+
id: s.id,
|
|
32
|
+
transport: s.transport,
|
|
33
|
+
command: s.command ?? null,
|
|
34
|
+
args: s.args ?? null,
|
|
35
|
+
url: s.url ?? null,
|
|
36
|
+
headers: s.headers ?? null,
|
|
37
|
+
env: s.env ?? null,
|
|
38
|
+
cwd: s.cwd ?? process.cwd(),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** Tear down one pooled entry (best-effort) and remove it. */
|
|
42
|
+
function evict(key) {
|
|
43
|
+
const entry = pool.get(key);
|
|
44
|
+
if (!entry)
|
|
45
|
+
return;
|
|
46
|
+
pool.delete(key);
|
|
47
|
+
void entry.promise.then((cs) => {
|
|
48
|
+
cs.cleanup?.();
|
|
49
|
+
void cs.client.close().catch(() => { });
|
|
50
|
+
}, () => { });
|
|
51
|
+
}
|
|
52
|
+
/** Heuristic: does this error mean the MCP transport/child is gone? */
|
|
53
|
+
function isConnectionError(e) {
|
|
54
|
+
const msg = (e instanceof Error ? e.message : String(e)).toLowerCase();
|
|
55
|
+
return (msg.includes("closed") ||
|
|
56
|
+
msg.includes("disconnect") ||
|
|
57
|
+
msg.includes("econnrefused") ||
|
|
58
|
+
msg.includes("epipe") ||
|
|
59
|
+
msg.includes("transport") ||
|
|
60
|
+
msg.includes("not connected") ||
|
|
61
|
+
msg.includes("terminated"));
|
|
62
|
+
}
|
|
63
|
+
/** Connect a server (or reuse the live cached client). Evicts on connect failure. */
|
|
64
|
+
function getOrConnect(server, opts) {
|
|
65
|
+
const key = serverKey(server);
|
|
66
|
+
const existing = pool.get(key);
|
|
67
|
+
if (existing)
|
|
68
|
+
return existing.promise;
|
|
69
|
+
const promise = connectOneServer(server, opts);
|
|
70
|
+
const entry = { key, promise };
|
|
71
|
+
pool.set(key, entry);
|
|
72
|
+
// Cache a rejection only transiently: evict so the next turn retries rather
|
|
73
|
+
// than returning the same failed promise forever.
|
|
74
|
+
promise.catch(() => {
|
|
75
|
+
if (pool.get(key) === entry)
|
|
76
|
+
pool.delete(key);
|
|
77
|
+
});
|
|
78
|
+
return promise;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Wrap each tool's execute so a transport/connection failure evicts the pooled
|
|
82
|
+
* client (next turn reconnects). The MCP child may die after a successful
|
|
83
|
+
* connect; without this the dead client would be reused on every later turn.
|
|
84
|
+
*/
|
|
85
|
+
function wrapForSelfHeal(tools, key) {
|
|
86
|
+
const out = {};
|
|
87
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
88
|
+
const base = tool.execute;
|
|
89
|
+
if (typeof base !== "function") {
|
|
90
|
+
out[name] = tool;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
out[name] = {
|
|
94
|
+
...tool,
|
|
95
|
+
execute: async (args, options) => {
|
|
96
|
+
try {
|
|
97
|
+
return await base(args, options);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
if (isConnectionError(e)) {
|
|
101
|
+
console.error(`[mcp:pool] '${name}' hit a connection error — evicting cached client so the next turn reconnects`);
|
|
102
|
+
evict(key);
|
|
103
|
+
}
|
|
104
|
+
throw e;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Acquire the tool set for `servers`, reusing pooled clients where possible.
|
|
113
|
+
* Mirrors buildMcpToolSet's parallel + partial-at-deadline contract, but only
|
|
114
|
+
* FIRST-connects can be slow — already-pooled servers resolve instantly. The
|
|
115
|
+
* returned bundle's `close()` is a no-op RELEASE: pooled clients stay alive for
|
|
116
|
+
* the next turn. Use closeAllMcpClients() for real teardown.
|
|
117
|
+
*/
|
|
118
|
+
export async function acquireMcpTools(servers, opts) {
|
|
119
|
+
const tools = {};
|
|
120
|
+
const errors = [];
|
|
121
|
+
const enabled = servers.filter((s) => s.enabled);
|
|
122
|
+
const slots = enabled.map((s) => ({ label: s.label, key: serverKey(s), done: false }));
|
|
123
|
+
const attempts = enabled.map((server, i) => {
|
|
124
|
+
const validation = validateMcpServerConfig(server);
|
|
125
|
+
if (!validation.ok) {
|
|
126
|
+
slots[i] = { ...slots[i], done: true, error: validation.error };
|
|
127
|
+
return Promise.resolve();
|
|
128
|
+
}
|
|
129
|
+
return getOrConnect(server, opts).then((result) => {
|
|
130
|
+
slots[i] = { ...slots[i], done: true, result };
|
|
131
|
+
}, (error) => {
|
|
132
|
+
slots[i] = { ...slots[i], done: true, error: error instanceof Error ? error.message : String(error) };
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
const deadlineMs = getMcpBuildDeadlineMs();
|
|
136
|
+
let deadlineTimer;
|
|
137
|
+
const deadline = new Promise((resolve) => {
|
|
138
|
+
deadlineTimer = setTimeout(resolve, deadlineMs);
|
|
139
|
+
deadlineTimer.unref?.();
|
|
140
|
+
});
|
|
141
|
+
await Promise.race([Promise.allSettled(attempts), deadline]);
|
|
142
|
+
if (deadlineTimer)
|
|
143
|
+
clearTimeout(deadlineTimer);
|
|
144
|
+
for (const slot of slots) {
|
|
145
|
+
if (slot.done) {
|
|
146
|
+
if (slot.error) {
|
|
147
|
+
errors.push(`${slot.label}: ${slot.error}`);
|
|
148
|
+
}
|
|
149
|
+
else if (slot.result) {
|
|
150
|
+
Object.assign(tools, wrapForSelfHeal(slot.result.tools, slot.key));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Still connecting at the deadline (a cold first-connect). It stays in the
|
|
155
|
+
// pool and will be ready for a later turn — just excluded from THIS turn.
|
|
156
|
+
errors.push(`${slot.label}: not ready within ${deadlineMs}ms (still connecting — available next turn)`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (errors.length > 0) {
|
|
160
|
+
console.error(`[mcp:pool] ${errors.length} server(s) unavailable this turn: ${errors.join(" | ")}`);
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
tools,
|
|
164
|
+
errors,
|
|
165
|
+
// Release, not close: pooled clients persist across turns by design.
|
|
166
|
+
async close() { },
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Fire-and-forget pre-connect: start connecting `servers` in the background so
|
|
171
|
+
* they are pooled BEFORE the first turn needs them. npx stdio servers
|
|
172
|
+
* (filesystem/memory) cold-start >2.5s and would otherwise miss the first turn's
|
|
173
|
+
* build deadline — warming them at startup means they're usually ready by the
|
|
174
|
+
* first prompt. No deadline, no return; per-turn acquireMcpTools reuses whatever
|
|
175
|
+
* has connected. Idempotent (cached entries are reused); a failed connect is
|
|
176
|
+
* evicted by getOrConnect so a real turn retries.
|
|
177
|
+
*/
|
|
178
|
+
export function warmMcpClients(servers) {
|
|
179
|
+
for (const s of servers) {
|
|
180
|
+
if (!s.enabled)
|
|
181
|
+
continue;
|
|
182
|
+
if (!validateMcpServerConfig(s).ok)
|
|
183
|
+
continue;
|
|
184
|
+
void getOrConnect(s).catch(() => {
|
|
185
|
+
/* warm is best-effort — the eviction in getOrConnect lets a real turn retry */
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Tear down every pooled client. Call on orchestrator/process shutdown. */
|
|
190
|
+
export async function closeAllMcpClients() {
|
|
191
|
+
const entries = [...pool.values()];
|
|
192
|
+
pool.clear();
|
|
193
|
+
await Promise.all(entries.map(async (e) => {
|
|
194
|
+
try {
|
|
195
|
+
const cs = await e.promise;
|
|
196
|
+
cs.cleanup?.();
|
|
197
|
+
await cs.client.close().catch(() => { });
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
/* a never-connected entry has nothing to close */
|
|
201
|
+
}
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
/** Test-only: reset pool state between cases. */
|
|
205
|
+
export function __resetMcpClientPoolForTests() {
|
|
206
|
+
pool.clear();
|
|
207
|
+
}
|
|
208
|
+
/** Test-only: number of pooled (connecting or connected) entries. */
|
|
209
|
+
export function __mcpClientPoolSize() {
|
|
210
|
+
return pool.size;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=client-pool.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
2
|
import { URL } from "node:url";
|
|
3
|
-
const SUCCESS_HTML = `<!DOCTYPE html><html><body style="font-family:system-ui;text-align:center;padding:60px">
|
|
4
|
-
<h2>Authorization successful</h2><p>You can close this tab and return to the terminal.</p>
|
|
3
|
+
const SUCCESS_HTML = `<!DOCTYPE html><html><body style="font-family:system-ui;text-align:center;padding:60px">
|
|
4
|
+
<h2>Authorization successful</h2><p>You can close this tab and return to the terminal.</p>
|
|
5
5
|
</body></html>`;
|
|
6
6
|
export function startOAuthCallbackServer(opts) {
|
|
7
7
|
const callbackPath = opts.path ?? "/callback";
|
|
@@ -2,19 +2,19 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { parseEnvLines, parseHeaderLines } from "./parse-headers.js";
|
|
3
3
|
describe("parseHeaderLines", () => {
|
|
4
4
|
it("parses colon-separated headers and trims whitespace", () => {
|
|
5
|
-
expect(parseHeaderLines(`
|
|
6
|
-
Authorization: Bearer token
|
|
7
|
-
X-Trace-Id: abc123
|
|
5
|
+
expect(parseHeaderLines(`
|
|
6
|
+
Authorization: Bearer token
|
|
7
|
+
X-Trace-Id: abc123
|
|
8
8
|
`)).toEqual({
|
|
9
9
|
Authorization: "Bearer token",
|
|
10
10
|
"X-Trace-Id": "abc123",
|
|
11
11
|
});
|
|
12
12
|
});
|
|
13
13
|
it("ignores blank and malformed lines while preserving later colons in values", () => {
|
|
14
|
-
expect(parseHeaderLines(`
|
|
15
|
-
invalid
|
|
16
|
-
: missing-name
|
|
17
|
-
Host: example.com:443
|
|
14
|
+
expect(parseHeaderLines(`
|
|
15
|
+
invalid
|
|
16
|
+
: missing-name
|
|
17
|
+
Host: example.com:443
|
|
18
18
|
`)).toEqual({
|
|
19
19
|
Host: "example.com:443",
|
|
20
20
|
});
|
|
@@ -22,19 +22,19 @@ describe("parseHeaderLines", () => {
|
|
|
22
22
|
});
|
|
23
23
|
describe("parseEnvLines", () => {
|
|
24
24
|
it("parses equals-separated env assignments and trims whitespace", () => {
|
|
25
|
-
expect(parseEnvLines(`
|
|
26
|
-
API_KEY = secret
|
|
27
|
-
MODE= production
|
|
25
|
+
expect(parseEnvLines(`
|
|
26
|
+
API_KEY = secret
|
|
27
|
+
MODE= production
|
|
28
28
|
`)).toEqual({
|
|
29
29
|
API_KEY: "secret",
|
|
30
30
|
MODE: "production",
|
|
31
31
|
});
|
|
32
32
|
});
|
|
33
33
|
it("ignores blank and malformed lines while preserving later equals in values", () => {
|
|
34
|
-
expect(parseEnvLines(`
|
|
35
|
-
missing
|
|
36
|
-
= no-name
|
|
37
|
-
URL=https://example.com?a=b
|
|
34
|
+
expect(parseEnvLines(`
|
|
35
|
+
missing
|
|
36
|
+
= no-name
|
|
37
|
+
URL=https://example.com?a=b
|
|
38
38
|
`)).toEqual({
|
|
39
39
|
URL: "https://example.com?a=b",
|
|
40
40
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type MCPClient } from "@ai-sdk/mcp";
|
|
1
2
|
import type { ToolSet } from "ai";
|
|
2
3
|
import type { McpServerConfig } from "../utils/settings.js";
|
|
3
4
|
export interface McpToolBundle {
|
|
@@ -8,4 +9,31 @@ export interface McpToolBundle {
|
|
|
8
9
|
export interface McpBuildOptions {
|
|
9
10
|
onOAuthRequired?: (serverId: string, url: URL) => void;
|
|
10
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Total wall-clock budget for building the MCP tool set. Servers connect in
|
|
14
|
+
* PARALLEL and whatever has connected by the deadline is returned; slower
|
|
15
|
+
* servers are reported in `.errors` (and closed if they connect late) instead
|
|
16
|
+
* of sinking the whole bundle. Default 2500ms; override with
|
|
17
|
+
* MUONROI_MCP_BUILD_DEADLINE_MS (500–20000).
|
|
18
|
+
*
|
|
19
|
+
* Phase 1c — the OLD design built servers SEQUENTIALLY under an outer race
|
|
20
|
+
* (message-processor) that discarded EVERYTHING on timeout, so one slow `npx`
|
|
21
|
+
* stdio spawn starved a fast HTTP server and left the agent blind to MCP tools
|
|
22
|
+
* that were actually reachable (live: muonroi-docs ~300ms dropped behind slow
|
|
23
|
+
* npx servers, session f6f7881a5fae). Parallel + partial-at-deadline fixes it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getMcpBuildDeadlineMs(): number;
|
|
26
|
+
export interface ConnectedServer {
|
|
27
|
+
tools: ToolSet;
|
|
28
|
+
client: MCPClient;
|
|
29
|
+
/** OAuth provider teardown, when one was created for this server. */
|
|
30
|
+
cleanup?: () => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Connect ONE server and build its prefixed, output-capped tool set. Throws on
|
|
34
|
+
* any failure; the caller owns lifecycle of the returned client/cleanup.
|
|
35
|
+
* Exported so the cross-turn client pool (client-pool.ts) can reuse it as its
|
|
36
|
+
* connect primitive.
|
|
37
|
+
*/
|
|
38
|
+
export declare function connectOneServer(rawServer: McpServerConfig, opts?: McpBuildOptions): Promise<ConnectedServer>;
|
|
11
39
|
export declare function buildMcpToolSet(servers: McpServerConfig[], opts?: McpBuildOptions): Promise<McpToolBundle>;
|
package/dist/src/mcp/runtime.js
CHANGED
|
@@ -82,70 +82,136 @@ function toTransport(server, authProvider) {
|
|
|
82
82
|
...(authProvider ? { authProvider: authProvider } : {}),
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Total wall-clock budget for building the MCP tool set. Servers connect in
|
|
87
|
+
* PARALLEL and whatever has connected by the deadline is returned; slower
|
|
88
|
+
* servers are reported in `.errors` (and closed if they connect late) instead
|
|
89
|
+
* of sinking the whole bundle. Default 2500ms; override with
|
|
90
|
+
* MUONROI_MCP_BUILD_DEADLINE_MS (500–20000).
|
|
91
|
+
*
|
|
92
|
+
* Phase 1c — the OLD design built servers SEQUENTIALLY under an outer race
|
|
93
|
+
* (message-processor) that discarded EVERYTHING on timeout, so one slow `npx`
|
|
94
|
+
* stdio spawn starved a fast HTTP server and left the agent blind to MCP tools
|
|
95
|
+
* that were actually reachable (live: muonroi-docs ~300ms dropped behind slow
|
|
96
|
+
* npx servers, session f6f7881a5fae). Parallel + partial-at-deadline fixes it.
|
|
97
|
+
*/
|
|
98
|
+
export function getMcpBuildDeadlineMs() {
|
|
99
|
+
const v = Number(process.env.MUONROI_MCP_BUILD_DEADLINE_MS);
|
|
100
|
+
if (Number.isFinite(v) && v >= 500 && v <= 20_000)
|
|
101
|
+
return v;
|
|
102
|
+
return 2500;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Connect ONE server and build its prefixed, output-capped tool set. Throws on
|
|
106
|
+
* any failure; the caller owns lifecycle of the returned client/cleanup.
|
|
107
|
+
* Exported so the cross-turn client pool (client-pool.ts) can reuse it as its
|
|
108
|
+
* connect primitive.
|
|
109
|
+
*/
|
|
110
|
+
export async function connectOneServer(rawServer, opts) {
|
|
111
|
+
// Hydrate env vars from the OS keychain before spawning — e.g. inject
|
|
112
|
+
// TAVILY_API_KEY for the tavily MCP if stored via the research-onboarding wizard.
|
|
113
|
+
const server = await hydrateServerEnv(rawServer);
|
|
114
|
+
let authProvider;
|
|
115
|
+
let cleanup;
|
|
116
|
+
if (server.transport !== "stdio" && opts?.onOAuthRequired) {
|
|
117
|
+
const oauthResult = await createOAuthProviderWithCallback({
|
|
118
|
+
serverId: server.id,
|
|
119
|
+
onAuthorizationUrl: (url) => opts.onOAuthRequired(server.id, url),
|
|
120
|
+
});
|
|
121
|
+
authProvider = oauthResult.provider;
|
|
122
|
+
cleanup = oauthResult.close;
|
|
123
|
+
}
|
|
124
|
+
const client = await createMCPClient({
|
|
125
|
+
transport: toTransport(server, authProvider),
|
|
126
|
+
name: `muonroi-cli-${server.id}`,
|
|
127
|
+
version: "1.0.0",
|
|
128
|
+
});
|
|
129
|
+
const mcpTools = await client.tools();
|
|
130
|
+
const prefix = mcpToolPrefix(server);
|
|
131
|
+
const tools = {};
|
|
132
|
+
for (const [name, tool] of Object.entries(mcpTools)) {
|
|
133
|
+
// OpenAI/DeepSeek function-name regex: ^[a-zA-Z0-9_-]+$. MCP spec does not
|
|
134
|
+
// restrict server-side tool names, so we sanitize here. The tool's execute()
|
|
135
|
+
// closure still calls the MCP server with the original name.
|
|
136
|
+
const safeName = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
137
|
+
const prefixedName = `${prefix}__${safeName}`;
|
|
138
|
+
const stripped = stripMcpInputSchema(tool);
|
|
139
|
+
// Cap MCP tool output the same way built-in tools are capped so the raw
|
|
140
|
+
// server payload doesn't stream into context uncapped. See cap-tool-result.ts.
|
|
141
|
+
const baseExecute = stripped.execute;
|
|
142
|
+
tools[prefixedName] = {
|
|
143
|
+
...stripped,
|
|
144
|
+
description: `[MCP ${server.label}] ${tool.description ?? name}`,
|
|
145
|
+
...(typeof baseExecute === "function"
|
|
146
|
+
? { execute: async (args, options) => capMcpToolResult(await baseExecute(args, options)) }
|
|
147
|
+
: {}),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return { tools, client, cleanup };
|
|
151
|
+
}
|
|
85
152
|
export async function buildMcpToolSet(servers, opts) {
|
|
86
153
|
const tools = {};
|
|
87
154
|
const errors = [];
|
|
88
155
|
const clients = [];
|
|
89
156
|
const cleanups = [];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
157
|
+
const enabled = servers.filter((s) => s.enabled);
|
|
158
|
+
const slots = enabled.map((s) => ({ label: s.label, done: false }));
|
|
159
|
+
const attempts = enabled.map((rawServer, i) => {
|
|
93
160
|
const validation = validateMcpServerConfig(rawServer);
|
|
94
161
|
if (!validation.ok) {
|
|
95
|
-
|
|
96
|
-
|
|
162
|
+
slots[i] = { label: rawServer.label, done: true, error: validation.error };
|
|
163
|
+
return Promise.resolve();
|
|
97
164
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
165
|
+
return connectOneServer(rawServer, opts).then((result) => {
|
|
166
|
+
slots[i] = { label: rawServer.label, done: true, result };
|
|
167
|
+
}, (error) => {
|
|
168
|
+
slots[i] = {
|
|
169
|
+
label: rawServer.label,
|
|
170
|
+
done: true,
|
|
171
|
+
error: error instanceof Error ? error.message : String(error),
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
const deadlineMs = getMcpBuildDeadlineMs();
|
|
176
|
+
let deadlineTimer;
|
|
177
|
+
const deadline = new Promise((resolve) => {
|
|
178
|
+
deadlineTimer = setTimeout(resolve, deadlineMs);
|
|
179
|
+
deadlineTimer.unref?.();
|
|
180
|
+
});
|
|
181
|
+
await Promise.race([Promise.allSettled(attempts), deadline]);
|
|
182
|
+
if (deadlineTimer)
|
|
183
|
+
clearTimeout(deadlineTimer);
|
|
184
|
+
for (let i = 0; i < slots.length; i++) {
|
|
185
|
+
const slot = slots[i];
|
|
186
|
+
if (slot.done) {
|
|
187
|
+
if (slot.error) {
|
|
188
|
+
errors.push(`${slot.label}: ${slot.error}`);
|
|
111
189
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
clients.push(client);
|
|
118
|
-
const mcpTools = await client.tools();
|
|
119
|
-
const prefix = mcpToolPrefix(server);
|
|
120
|
-
for (const [name, tool] of Object.entries(mcpTools)) {
|
|
121
|
-
// OpenAI/DeepSeek function-name regex: ^[a-zA-Z0-9_-]+$. MCP spec
|
|
122
|
-
// does not restrict server-side tool names, so we sanitize here.
|
|
123
|
-
// The tool's execute() closure still calls the MCP server with the
|
|
124
|
-
// original name — we only rename what the LLM sees.
|
|
125
|
-
const safeName = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
126
|
-
const prefixedName = `${prefix}__${safeName}`;
|
|
127
|
-
const stripped = stripMcpInputSchema(tool);
|
|
128
|
-
// Cap MCP tool output the same way built-in tools are capped
|
|
129
|
-
// (`truncateOutput` / MAX_TOOL_OUTPUT_CHARS). Without this wrap the raw
|
|
130
|
-
// server payload streams into the model context uncapped — a cost leak
|
|
131
|
-
// that hits cheap models hardest. See cap-tool-result.ts.
|
|
132
|
-
const baseExecute = stripped.execute;
|
|
133
|
-
tools[prefixedName] = {
|
|
134
|
-
...stripped,
|
|
135
|
-
description: `[MCP ${server.label}] ${tool.description ?? name}`,
|
|
136
|
-
...(typeof baseExecute === "function"
|
|
137
|
-
? {
|
|
138
|
-
execute: async (args, options) => capMcpToolResult(await baseExecute(args, options)),
|
|
139
|
-
}
|
|
140
|
-
: {}),
|
|
141
|
-
};
|
|
190
|
+
else if (slot.result) {
|
|
191
|
+
Object.assign(tools, slot.result.tools);
|
|
192
|
+
clients.push(slot.result.client);
|
|
193
|
+
if (slot.result.cleanup)
|
|
194
|
+
cleanups.push(slot.result.cleanup);
|
|
142
195
|
}
|
|
143
196
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
197
|
+
else {
|
|
198
|
+
// Still connecting at the deadline: report it and close it if/when it
|
|
199
|
+
// eventually connects so the child process / socket doesn't leak.
|
|
200
|
+
errors.push(`${slot.label}: not ready within ${deadlineMs}ms (slow MCP server — excluded this turn)`);
|
|
201
|
+
void attempts[i]?.then(() => {
|
|
202
|
+
const late = slots[i]?.result;
|
|
203
|
+
if (late) {
|
|
204
|
+
late.cleanup?.();
|
|
205
|
+
void late.client.close().catch(() => { });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
147
208
|
}
|
|
148
209
|
}
|
|
210
|
+
// Surface (not swallow) any server that didn't make it — never silently
|
|
211
|
+
// degrade to "builtins only" without a trace.
|
|
212
|
+
if (errors.length > 0) {
|
|
213
|
+
console.error(`[MCP] ${errors.length} server(s) unavailable this turn: ${errors.join(" | ")}`);
|
|
214
|
+
}
|
|
149
215
|
return {
|
|
150
216
|
tools,
|
|
151
217
|
errors,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/mcp/self-verify-runner.ts
|
|
3
|
+
*
|
|
4
|
+
* The default self-verify Runner (drives runSelfVerify / runAgenticLoop in
|
|
5
|
+
* process) plus a process-shared JobManager singleton. Shared by BOTH surfaces:
|
|
6
|
+
* the native in-CLI selfverify_* builtins (src/tools/native-tools.ts) and the
|
|
7
|
+
* muonroi-tools MCP server (src/mcp/tools-server.ts, for external agents) — so a
|
|
8
|
+
* run started on either surface is visible to both, and there is one job space.
|
|
9
|
+
*/
|
|
10
|
+
import { JobManager, type Runner } from "./self-verify-jobs.js";
|
|
11
|
+
/** Default runner: drives the real self-verify functions in-process. */
|
|
12
|
+
export declare const defaultRunner: Runner;
|
|
13
|
+
/** Process-shared self-verify JobManager (created lazily on first use). */
|
|
14
|
+
export declare function getSelfVerifyJobManager(): JobManager;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/mcp/self-verify-runner.ts
|
|
3
|
+
*
|
|
4
|
+
* The default self-verify Runner (drives runSelfVerify / runAgenticLoop in
|
|
5
|
+
* process) plus a process-shared JobManager singleton. Shared by BOTH surfaces:
|
|
6
|
+
* the native in-CLI selfverify_* builtins (src/tools/native-tools.ts) and the
|
|
7
|
+
* muonroi-tools MCP server (src/mcp/tools-server.ts, for external agents) — so a
|
|
8
|
+
* run started on either surface is visible to both, and there is one job space.
|
|
9
|
+
*/
|
|
10
|
+
import { JobManager } from "./self-verify-jobs.js";
|
|
11
|
+
/** Default runner: drives the real self-verify functions in-process. */
|
|
12
|
+
export const defaultRunner = {
|
|
13
|
+
async tier1(opts, log) {
|
|
14
|
+
// signal intentionally not forwarded: runSelfVerify/runAgenticLoop do not yet
|
|
15
|
+
// accept an AbortSignal. cancel() marks the job and discards the late result.
|
|
16
|
+
const { runSelfVerify } = await import("../self-qa/index.js");
|
|
17
|
+
return runSelfVerify({
|
|
18
|
+
baseRef: opts.since,
|
|
19
|
+
maxScenarios: opts.max,
|
|
20
|
+
emitSpecs: opts.emit,
|
|
21
|
+
specOutDir: opts.out,
|
|
22
|
+
log,
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
async agentic(opts, log) {
|
|
26
|
+
const { createLLMBrain, runAgenticLoop } = await import("../self-qa/agentic-loop.js");
|
|
27
|
+
const brain = await createLLMBrain({ modelId: opts.llm });
|
|
28
|
+
return runAgenticLoop({ goal: opts.goal, brain, maxTurns: opts.turns ?? 20, log });
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
let shared = null;
|
|
32
|
+
/** Process-shared self-verify JobManager (created lazily on first use). */
|
|
33
|
+
export function getSelfVerifyJobManager() {
|
|
34
|
+
if (!shared)
|
|
35
|
+
shared = new JobManager(defaultRunner);
|
|
36
|
+
return shared;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=self-verify-runner.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/mcp/setup-guide-text.ts
|
|
3
|
+
*
|
|
4
|
+
* Single source of the muonroi-cli setup guide, shared by BOTH surfaces that
|
|
5
|
+
* expose it: the native in-CLI `setup_guide` builtin (src/tools/native-tools.ts)
|
|
6
|
+
* and the muonroi-tools MCP server (src/mcp/tools-server.ts, for external agents).
|
|
7
|
+
* Keeping it here avoids duplicating ~70 lines across the two.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SETUP_GUIDE_TEXT = "# muonroi-cli Setup Guide\n\n## Install (zero runtime deps \u2014 recommended)\nLinux / macOS:\n curl -fsSL https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.sh | bash\n\nWindows PowerShell:\n irm https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.ps1 | iex\n\nBun (requires Bun >= 1.3):\n bun add -g muonroi-cli\n # (npm install -g is NOT supported \u2014 TUI engine uses Bun-only ESM features)\n\nThe installers fetch a pre-compiled single binary from GitHub Releases.\n\n## First run\n- Wizard appears automatically.\n- Lists supported providers (DeepSeek + SiliconFlow ready; others via BYOK).\n- Four credential options: paste key, Bitwarden sync (B in /providers), keys export/import (encrypted bundle), or skip for later.\n- Keys land in OS keychain (keytar). Settings written to ~/.muonroi-cli/user-settings.json.\n- Role routing (leader/implement/verify/research) is configured for you.\n\nAfter setup: run `muonroi-cli doctor` to validate.\n\n## Essential commands\n- Interactive TUI: `muonroi-cli` (or `node dist/index.js` after build)\n- Headless one-shot: `muonroi-cli --prompt \"your task\" --max-tool-rounds 8`\n- Health + MCP nudge: `muonroi-cli doctor`\n- Update: `muonroi-cli update` (or set \"autoUpdate\": true in user-settings)\n- Keys move between machines: `muonroi-cli keys export file.json` then import on target\n- Native tools MCP (for external agents): `muonroi-cli tools-mcp` (stdio)\n- Harness driver MCP: `muonroi-cli mcp-driver`\n\n## MCP integration (for Claude Desktop, Cursor, other agents)\nAdd to your MCP client config:\n\n{\n \"mcpServers\": {\n \"muonroi-tools\": {\n \"command\": \"bun\",\n \"args\": [\"run\", \"/absolute/path/to/muonroi-cli/src/index.ts\", \"tools-mcp\"]\n }\n }\n}\n\n(Use absolute path. After `bun run build`: \"node\", \"dist/index.js\", \"tools-mcp\")\n\nThe CLI's OWN inner agent exposes these as NATIVE in-process tools (no MCP self-spawn):\n- setup_guide (this document)\n- ee_query / ee_health / ee_feedback \u2014 Experience Engine semantic recall + compaction checkpoints + feedback for learning\n- usage_forensics <id-prefix> \u2014 per-session cost/token forensics (peak input, cache hits, anomalies)\n- lsp_query \u2014 goToDefinition, findReferences, hover, symbols, call hierarchy etc.\n- selfverify_* \u2014 Tier-1 heuristic + Tier-2 agentic self-QA harness runs (start/poll/result/cancel/list)\n\nFor BB/.NET template recipes and package docs, also connect an external \"muonroi-docs\" MCP server if available (provides docs_search + setup_guide for the templates).\n\n## Development\ngit clone https://github.com/muonroi/muonroi-cli.git\ncd muonroi-cli && bun install\n\nbun run dev # run from source (TUI)\nbun run typecheck # tsc --noEmit\nbun run test # vitest (unit + headless)\nbunx vitest -c vitest.harness.config.ts run tests/harness/ # TUI E2E (named-pipes on Win, fd3/4 on POSIX)\nbun run build # or build:binary for standalone exe\n\nSee AGENTS.md (quick ref + rules), CLAUDE.md (harness verification), README.md.\n\n## Verify\nmuonroi-cli doctor\n# Checks runtimes, catalog load, keychain, MCP servers enabled, council research MCP nudge, EE reachability, recent error rate.\n# Any \"warn\" entries tell you exactly what to enable (e.g. tavily for web research in council).\n\nFor BB-aware scaffolding (/ideal on a muonroi-building-block target): ensure dotnet SDK + the three Muonroi.*.Template packages are installed via NuGet; doctor surfaces missing feed/template cases.\n";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/mcp/setup-guide-text.ts
|
|
3
|
+
*
|
|
4
|
+
* Single source of the muonroi-cli setup guide, shared by BOTH surfaces that
|
|
5
|
+
* expose it: the native in-CLI `setup_guide` builtin (src/tools/native-tools.ts)
|
|
6
|
+
* and the muonroi-tools MCP server (src/mcp/tools-server.ts, for external agents).
|
|
7
|
+
* Keeping it here avoids duplicating ~70 lines across the two.
|
|
8
|
+
*/
|
|
9
|
+
export const SETUP_GUIDE_TEXT = `# muonroi-cli Setup Guide
|
|
10
|
+
|
|
11
|
+
## Install (zero runtime deps — recommended)
|
|
12
|
+
Linux / macOS:
|
|
13
|
+
curl -fsSL https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.sh | bash
|
|
14
|
+
|
|
15
|
+
Windows PowerShell:
|
|
16
|
+
irm https://raw.githubusercontent.com/muonroi/muonroi-cli/master/install.ps1 | iex
|
|
17
|
+
|
|
18
|
+
Bun (requires Bun >= 1.3):
|
|
19
|
+
bun add -g muonroi-cli
|
|
20
|
+
# (npm install -g is NOT supported — TUI engine uses Bun-only ESM features)
|
|
21
|
+
|
|
22
|
+
The installers fetch a pre-compiled single binary from GitHub Releases.
|
|
23
|
+
|
|
24
|
+
## First run
|
|
25
|
+
- Wizard appears automatically.
|
|
26
|
+
- Lists supported providers (DeepSeek + SiliconFlow ready; others via BYOK).
|
|
27
|
+
- Four credential options: paste key, Bitwarden sync (B in /providers), keys export/import (encrypted bundle), or skip for later.
|
|
28
|
+
- Keys land in OS keychain (keytar). Settings written to ~/.muonroi-cli/user-settings.json.
|
|
29
|
+
- Role routing (leader/implement/verify/research) is configured for you.
|
|
30
|
+
|
|
31
|
+
After setup: run \`muonroi-cli doctor\` to validate.
|
|
32
|
+
|
|
33
|
+
## Essential commands
|
|
34
|
+
- Interactive TUI: \`muonroi-cli\` (or \`node dist/index.js\` after build)
|
|
35
|
+
- Headless one-shot: \`muonroi-cli --prompt "your task" --max-tool-rounds 8\`
|
|
36
|
+
- Health + MCP nudge: \`muonroi-cli doctor\`
|
|
37
|
+
- Update: \`muonroi-cli update\` (or set "autoUpdate": true in user-settings)
|
|
38
|
+
- Keys move between machines: \`muonroi-cli keys export file.json\` then import on target
|
|
39
|
+
- Native tools MCP (for external agents): \`muonroi-cli tools-mcp\` (stdio)
|
|
40
|
+
- Harness driver MCP: \`muonroi-cli mcp-driver\`
|
|
41
|
+
|
|
42
|
+
## MCP integration (for Claude Desktop, Cursor, other agents)
|
|
43
|
+
Add to your MCP client config:
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"muonroi-tools": {
|
|
48
|
+
"command": "bun",
|
|
49
|
+
"args": ["run", "/absolute/path/to/muonroi-cli/src/index.ts", "tools-mcp"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
(Use absolute path. After \`bun run build\`: "node", "dist/index.js", "tools-mcp")
|
|
55
|
+
|
|
56
|
+
The CLI's OWN inner agent exposes these as NATIVE in-process tools (no MCP self-spawn):
|
|
57
|
+
- setup_guide (this document)
|
|
58
|
+
- ee_query / ee_health / ee_feedback — Experience Engine semantic recall + compaction checkpoints + feedback for learning
|
|
59
|
+
- usage_forensics <id-prefix> — per-session cost/token forensics (peak input, cache hits, anomalies)
|
|
60
|
+
- lsp_query — goToDefinition, findReferences, hover, symbols, call hierarchy etc.
|
|
61
|
+
- selfverify_* — Tier-1 heuristic + Tier-2 agentic self-QA harness runs (start/poll/result/cancel/list)
|
|
62
|
+
|
|
63
|
+
For BB/.NET template recipes and package docs, also connect an external "muonroi-docs" MCP server if available (provides docs_search + setup_guide for the templates).
|
|
64
|
+
|
|
65
|
+
## Development
|
|
66
|
+
git clone https://github.com/muonroi/muonroi-cli.git
|
|
67
|
+
cd muonroi-cli && bun install
|
|
68
|
+
|
|
69
|
+
bun run dev # run from source (TUI)
|
|
70
|
+
bun run typecheck # tsc --noEmit
|
|
71
|
+
bun run test # vitest (unit + headless)
|
|
72
|
+
bunx vitest -c vitest.harness.config.ts run tests/harness/ # TUI E2E (named-pipes on Win, fd3/4 on POSIX)
|
|
73
|
+
bun run build # or build:binary for standalone exe
|
|
74
|
+
|
|
75
|
+
See AGENTS.md (quick ref + rules), CLAUDE.md (harness verification), README.md.
|
|
76
|
+
|
|
77
|
+
## Verify
|
|
78
|
+
muonroi-cli doctor
|
|
79
|
+
# Checks runtimes, catalog load, keychain, MCP servers enabled, council research MCP nudge, EE reachability, recent error rate.
|
|
80
|
+
# Any "warn" entries tell you exactly what to enable (e.g. tavily for web research in council).
|
|
81
|
+
|
|
82
|
+
For BB-aware scaffolding (/ideal on a muonroi-building-block target): ensure dotnet SDK + the three Muonroi.*.Template packages are installed via NuGet; doctor surfaces missing feed/template cases.
|
|
83
|
+
`;
|
|
84
|
+
//# sourceMappingURL=setup-guide-text.js.map
|