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.
Files changed (172) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +122 -122
  3. package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
  4. package/dist/src/agent-harness/__tests__/mock-model.spec.js +48 -1
  5. package/dist/src/agent-harness/mock-model.d.ts +11 -0
  6. package/dist/src/agent-harness/mock-model.js +21 -0
  7. package/dist/src/cli/cost-forensics.js +12 -12
  8. package/dist/src/council/__tests__/clarification-prompt.test.js +51 -0
  9. package/dist/src/council/__tests__/clarifier-ready-gate.test.js +32 -0
  10. package/dist/src/council/__tests__/decisions-lock.test.js +17 -1
  11. package/dist/src/council/__tests__/oauth-reachable.test.d.ts +1 -0
  12. package/dist/src/council/__tests__/oauth-reachable.test.js +31 -0
  13. package/dist/src/council/__tests__/parse-outcome-fallback.test.js +11 -0
  14. package/dist/src/council/clarifier.js +9 -1
  15. package/dist/src/council/debate.js +5 -1
  16. package/dist/src/council/decisions-lock.js +3 -3
  17. package/dist/src/council/index.js +12 -5
  18. package/dist/src/council/leader.d.ts +0 -17
  19. package/dist/src/council/leader.js +22 -15
  20. package/dist/src/council/planner.js +1 -1
  21. package/dist/src/council/prompts.js +63 -57
  22. package/dist/src/council/types.d.ts +7 -0
  23. package/dist/src/ee/__tests__/ee-onboarding.test.d.ts +1 -0
  24. package/dist/src/ee/__tests__/ee-onboarding.test.js +32 -0
  25. package/dist/src/ee/auth.d.ts +9 -0
  26. package/dist/src/ee/auth.js +19 -0
  27. package/dist/src/ee/ee-onboarding.d.ts +5 -0
  28. package/dist/src/ee/ee-onboarding.js +76 -0
  29. package/dist/src/generated/version.d.ts +1 -1
  30. package/dist/src/generated/version.js +1 -1
  31. package/dist/src/headless/output.js +6 -4
  32. package/dist/src/headless/output.test.js +4 -3
  33. package/dist/src/index.js +20 -1
  34. package/dist/src/mcp/__tests__/auto-setup.test.js +74 -0
  35. package/dist/src/mcp/__tests__/client-pool.spec.d.ts +1 -0
  36. package/dist/src/mcp/__tests__/client-pool.spec.js +98 -0
  37. package/dist/src/mcp/__tests__/parallel-build.spec.d.ts +1 -0
  38. package/dist/src/mcp/__tests__/parallel-build.spec.js +67 -0
  39. package/dist/src/mcp/__tests__/smart-filter.test.js +56 -0
  40. package/dist/src/mcp/auto-setup.js +56 -2
  41. package/dist/src/mcp/client-pool.d.ts +46 -0
  42. package/dist/src/mcp/client-pool.js +212 -0
  43. package/dist/src/mcp/oauth-callback.js +2 -2
  44. package/dist/src/mcp/parse-headers.test.js +14 -14
  45. package/dist/src/mcp/runtime.d.ts +28 -0
  46. package/dist/src/mcp/runtime.js +117 -51
  47. package/dist/src/mcp/self-verify-runner.d.ts +14 -0
  48. package/dist/src/mcp/self-verify-runner.js +38 -0
  49. package/dist/src/mcp/setup-guide-text.d.ts +9 -0
  50. package/dist/src/mcp/setup-guide-text.js +84 -0
  51. package/dist/src/mcp/smart-filter.js +49 -0
  52. package/dist/src/mcp/smoke.test.js +43 -43
  53. package/dist/src/mcp/tools-server.d.ts +7 -0
  54. package/dist/src/mcp/tools-server.js +19 -22
  55. package/dist/src/models/catalog.json +349 -349
  56. package/dist/src/ops/__tests__/doctor-ee-health.test.js +21 -0
  57. package/dist/src/ops/doctor.d.ts +3 -2
  58. package/dist/src/ops/doctor.js +47 -11
  59. package/dist/src/ops/doctor.test.js +4 -3
  60. package/dist/src/orchestrator/__tests__/mcp-capability-block.test.d.ts +1 -0
  61. package/dist/src/orchestrator/__tests__/mcp-capability-block.test.js +39 -0
  62. package/dist/src/orchestrator/__tests__/project-stack.test.d.ts +1 -0
  63. package/dist/src/orchestrator/__tests__/project-stack.test.js +65 -0
  64. package/dist/src/orchestrator/batch-turn-runner.js +7 -11
  65. package/dist/src/orchestrator/message-processor.js +57 -27
  66. package/dist/src/orchestrator/orchestrator.js +26 -0
  67. package/dist/src/orchestrator/prompts.d.ts +51 -0
  68. package/dist/src/orchestrator/prompts.js +257 -134
  69. package/dist/src/orchestrator/scope-ceiling.js +6 -1
  70. package/dist/src/orchestrator/stream-runner.js +20 -15
  71. package/dist/src/orchestrator/text-tool-call-detector.test.js +13 -13
  72. package/dist/src/pil/__tests__/clarity-gate.test.js +24 -215
  73. package/dist/src/pil/__tests__/config.test.js +1 -17
  74. package/dist/src/pil/__tests__/discovery.test.js +144 -11
  75. package/dist/src/pil/__tests__/layer1-intent-trace.test.js +7 -2
  76. package/dist/src/pil/__tests__/layer1-intent.test.js +3 -0
  77. package/dist/src/pil/__tests__/layer16-clarity.test.js +32 -116
  78. package/dist/src/pil/__tests__/layer4-gsd.test.js +37 -0
  79. package/dist/src/pil/__tests__/layer6-output.test.js +137 -18
  80. package/dist/src/pil/__tests__/llm-classify.test.js +49 -2
  81. package/dist/src/pil/agent-operating-contract.d.ts +1 -1
  82. package/dist/src/pil/agent-operating-contract.js +2 -0
  83. package/dist/src/pil/agent-operating-contract.test.js +7 -2
  84. package/dist/src/pil/cheap-model-playbook.js +35 -35
  85. package/dist/src/pil/cheap-model-workbooks.js +16 -13
  86. package/dist/src/pil/clarity-gate.d.ts +21 -19
  87. package/dist/src/pil/clarity-gate.js +26 -153
  88. package/dist/src/pil/config.d.ts +9 -1
  89. package/dist/src/pil/config.js +15 -4
  90. package/dist/src/pil/discovery.js +211 -136
  91. package/dist/src/pil/layer1-intent.d.ts +12 -0
  92. package/dist/src/pil/layer1-intent.js +283 -38
  93. package/dist/src/pil/layer1-intent.test.js +210 -4
  94. package/dist/src/pil/layer16-clarity.d.ts +25 -11
  95. package/dist/src/pil/layer16-clarity.js +19 -306
  96. package/dist/src/pil/layer4-gsd.js +18 -6
  97. package/dist/src/pil/layer6-output.d.ts +2 -0
  98. package/dist/src/pil/layer6-output.js +137 -22
  99. package/dist/src/pil/llm-classify.d.ts +26 -0
  100. package/dist/src/pil/llm-classify.js +34 -5
  101. package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
  102. package/dist/src/pil/native-capabilities-workbook.js +82 -76
  103. package/dist/src/pil/schema.d.ts +8 -0
  104. package/dist/src/pil/schema.js +12 -1
  105. package/dist/src/pil/task-tier-map.js +4 -0
  106. package/dist/src/pil/types.d.ts +11 -1
  107. package/dist/src/product-loop/done-gate.js +3 -3
  108. package/dist/src/product-loop/loop-driver.js +18 -18
  109. package/dist/src/product-loop/progress-snapshot.js +4 -4
  110. package/dist/src/providers/auth/gemini-oauth.js +6 -15
  111. package/dist/src/providers/auth/grok-oauth.js +6 -15
  112. package/dist/src/providers/auth/openai-oauth.js +6 -15
  113. package/dist/src/providers/mcp-vision-bridge.js +48 -48
  114. package/dist/src/reporter/index.js +1 -1
  115. package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
  116. package/dist/src/scaffold/bb-quality-gate.js +5 -5
  117. package/dist/src/scaffold/continuation-prompt.js +60 -60
  118. package/dist/src/scaffold/init-new.js +453 -453
  119. package/dist/src/self-qa/__tests__/scenario-planner.test.js +3 -3
  120. package/dist/src/self-qa/agentic-loop.js +24 -19
  121. package/dist/src/self-qa/spec-emitter.js +26 -23
  122. package/dist/src/storage/__tests__/migrations.test.js +2 -2
  123. package/dist/src/storage/interaction-log.js +5 -5
  124. package/dist/src/storage/migrations.js +122 -122
  125. package/dist/src/storage/sessions.js +42 -42
  126. package/dist/src/storage/transcript.js +91 -84
  127. package/dist/src/storage/usage.js +14 -14
  128. package/dist/src/storage/workspaces.js +12 -12
  129. package/dist/src/tools/__tests__/native-tools.test.d.ts +1 -0
  130. package/dist/src/tools/__tests__/native-tools.test.js +53 -0
  131. package/dist/src/tools/git-safety.d.ts +61 -0
  132. package/dist/src/tools/git-safety.js +141 -0
  133. package/dist/src/tools/git-safety.test.d.ts +1 -0
  134. package/dist/src/tools/git-safety.test.js +111 -0
  135. package/dist/src/tools/native-tools.d.ts +31 -0
  136. package/dist/src/tools/native-tools.js +273 -0
  137. package/dist/src/tools/registry-git-safety.test.d.ts +7 -0
  138. package/dist/src/tools/registry-git-safety.test.js +92 -0
  139. package/dist/src/tools/registry.js +39 -4
  140. package/dist/src/ui/__tests__/markdown-render.test.d.ts +1 -0
  141. package/dist/src/ui/__tests__/markdown-render.test.js +48 -0
  142. package/dist/src/ui/app.js +0 -0
  143. package/dist/src/ui/components/message-view.js +4 -1
  144. package/dist/src/ui/components/structured-response-view.js +7 -3
  145. package/dist/src/ui/components/tool-group.js +7 -1
  146. package/dist/src/ui/markdown-render.d.ts +41 -0
  147. package/dist/src/ui/markdown-render.js +223 -0
  148. package/dist/src/ui/markdown.d.ts +10 -0
  149. package/dist/src/ui/markdown.js +12 -35
  150. package/dist/src/ui/slash/council-inspect.js +4 -4
  151. package/dist/src/ui/slash/export.js +4 -4
  152. package/dist/src/ui/utils/text.d.ts +8 -0
  153. package/dist/src/ui/utils/text.js +16 -0
  154. package/dist/src/ui/utils/text.test.d.ts +1 -0
  155. package/dist/src/ui/utils/text.test.js +23 -0
  156. package/dist/src/usage/ledger.js +48 -15
  157. package/dist/src/utils/__tests__/footprint-gitignore.test.d.ts +1 -0
  158. package/dist/src/utils/__tests__/footprint-gitignore.test.js +50 -0
  159. package/dist/src/utils/clipboard-image.js +23 -23
  160. package/dist/src/utils/open-url.d.ts +56 -0
  161. package/dist/src/utils/open-url.js +58 -0
  162. package/dist/src/utils/open-url.test.d.ts +1 -0
  163. package/dist/src/utils/open-url.test.js +86 -0
  164. package/dist/src/utils/settings.d.ts +12 -0
  165. package/dist/src/utils/settings.js +48 -0
  166. package/dist/src/utils/side-question.js +2 -2
  167. package/dist/src/utils/skills.js +3 -3
  168. package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
  169. package/dist/src/verify/environment.js +2 -1
  170. package/package.json +1 -1
  171. package/dist/src/pil/layer16-clarity.test.js +0 -31
  172. /package/dist/src/{pil/layer16-clarity.test.d.ts → council/__tests__/clarification-prompt.test.d.ts} +0 -0
@@ -47,6 +47,37 @@ function hasDocsSignal(message) {
47
47
  return (/https?:\/\/\S+/i.test(message) ||
48
48
  /\b(docs?|documentation|api|sdk|library|libraries|framework|package|npm|pip|cargo|crate|gem|maven|nuget|install|migrat\w*|changelog|release\s*notes?|reference|usage|fetch|download|http|web|google|search\s+(the\s+)?web|news|weather|headlines|look\s*up|online|internet)\b/i.test(message));
49
49
  }
50
+ /**
51
+ * Matches a question ABOUT the Muonroi ecosystem — where muonroi-docs (the
52
+ * authoritative ecosystem source: BB/.NET recipes, package docs, open-core
53
+ * boundary) is exactly what's needed, even though the message carries no generic
54
+ * docs/api keyword. Deliberately ecosystem-specific so it only ever KEEPS
55
+ * muonroi-docs (never other docs servers). EN + VI.
56
+ */
57
+ function hasEcosystemSignal(message) {
58
+ return /\bmuonroi\b|\becosystem\b|hệ\s*sinh\s*thái|he\s*sinh\s*thai|building[-\s]?block|\bbb\b|open[-\s]?core/i.test(message);
59
+ }
60
+ /**
61
+ * Explicit "use a tool / MCP tool" intent. The filter only sees server *ids*,
62
+ * not their tool lists (MCP tools are fetched lazily at build time), so when the
63
+ * user asks to call a specific tool by name we cannot tell which server owns it.
64
+ * Dropping any optional server then risks stripping the exact tool requested.
65
+ *
66
+ * Live miss (session f6f7881a5fae): "bạn thử call tool setup_guide ... ( call
67
+ * tool chứ không phải đọc code )" carried no docs keyword, so `muonroi-docs`
68
+ * (id matches /docs/) was dropped — the model had no `setup_guide` tool and
69
+ * resorted to driving the server by hand over bash JSON-RPC, fabricating output.
70
+ *
71
+ * When this fires we keep ALL optional servers for the turn. Over-keeping costs
72
+ * tokens but never removes capability — this module's documented safe direction.
73
+ * EN + VI: "call/use/invoke/run the X tool", "tool call", "gọi/dùng/chạy tool".
74
+ */
75
+ function hasExplicitToolIntent(message) {
76
+ return (/\b(?:call|use|invoke|run|exercise|trigger|try)\s+(?:the\s+)?(?:mcp\s+)?(?:[a-z0-9_.-]+\s+)?tool(?:s)?\b/i.test(message) ||
77
+ /\btool[\s_-]?call\b/i.test(message) ||
78
+ /\bmcp\b[^\n]*\btool/i.test(message) ||
79
+ /\b(?:gọi|dùng|chạy|thử)\s+(?:tool|mcp)\b/i.test(message));
80
+ }
50
81
  /**
51
82
  * Filesystem-MCP tool names that 1:1 duplicate a first-class BUILTIN file tool.
52
83
  * The builtin `read_file`/`write_file`/`edit_file` are strictly better (read-
@@ -102,9 +133,27 @@ export function dropRedundantFsMcpTools(mcpTools, builtinToolNames) {
102
133
  export function filterMcpServersByMessage(servers, userMessage, opts = {}) {
103
134
  if (opts.disabled)
104
135
  return servers;
136
+ // Explicit "call the X tool" intent → keep every server this turn. We can't map
137
+ // a named tool back to its server from config alone (tool lists load lazily),
138
+ // so dropping any optional server risks stripping the exact tool requested.
139
+ if (hasExplicitToolIntent(userMessage))
140
+ return servers;
105
141
  const browser = hasBrowserSignal(userMessage);
106
142
  const docs = hasDocsSignal(userMessage);
143
+ const ecosystem = hasEcosystemSignal(userMessage);
144
+ const lower = userMessage.toLowerCase();
107
145
  return servers.filter((s) => {
146
+ // A server named outright in the message ("check the muonroi-docs MCP") is
147
+ // always relevant — never let a category skip override an explicit mention.
148
+ if (s.id && lower.includes(s.id.toLowerCase()))
149
+ return true;
150
+ // muonroi-docs is the AUTHORITATIVE ecosystem source. A question about the
151
+ // Muonroi ecosystem ("hệ sinh thái muonroi", "building-block", "bb rule
152
+ // engine") matches no generic docs/api keyword, so SKIP_WHEN_NO_DOCS would
153
+ // wrongly drop it and the agent falls back to guessing from files (live
154
+ // session dbe408937a3d turn 1). Keep it whenever the turn is ecosystem-about.
155
+ if (ecosystem && /(^|[-_])docs([-_]|$)/.test(s.id) && /muonroi/i.test(s.id))
156
+ return true;
108
157
  if (!browser && SKIP_WHEN_NO_BROWSER.test(s.id))
109
158
  return false;
110
159
  if (!docs && SKIP_WHEN_NO_DOCS.test(s.id))
@@ -86,49 +86,49 @@ describe("MCP smoke test — buildMcpToolSet", () => {
86
86
  it.skipIf(process.platform === "win32" || !!process.env.CI)("discovers tools from stdio MCP echo stub", async () => {
87
87
  // Inline MCP echo server script — handles initialize, notifications/initialized,
88
88
  // and tools/list using Content-Length framing per the MCP JSON-RPC spec.
89
- const echoServerScript = `
90
- const { stdin, stdout } = require('process');
91
- let buf = '';
92
- stdin.setEncoding('utf8');
93
- stdin.on('data', (chunk) => {
94
- buf += chunk;
95
- while (true) {
96
- const headerEnd = buf.indexOf('\\r\\n\\r\\n');
97
- if (headerEnd === -1) break;
98
- const header = buf.slice(0, headerEnd);
99
- const match = header.match(/Content-Length:\\s*(\\d+)/i);
100
- if (!match) { buf = buf.slice(headerEnd + 4); continue; }
101
- const len = parseInt(match[1], 10);
102
- const bodyStart = headerEnd + 4;
103
- if (buf.length < bodyStart + len) break;
104
- const body = buf.slice(bodyStart, bodyStart + len);
105
- buf = buf.slice(bodyStart + len);
106
- handleMessage(JSON.parse(body));
107
- }
108
- });
109
- function send(obj) {
110
- const s = JSON.stringify(obj);
111
- stdout.write('Content-Length: ' + Buffer.byteLength(s) + '\\r\\n\\r\\n' + s);
112
- }
113
- function handleMessage(msg) {
114
- if (msg.method === 'initialize') {
115
- send({ jsonrpc: '2.0', id: msg.id, result: {
116
- protocolVersion: '2024-11-05',
117
- capabilities: { tools: { listChanged: false } },
118
- serverInfo: { name: 'echo-stub', version: '1.0.0' }
119
- }});
120
- } else if (msg.method === 'notifications/initialized') {
121
- // no response needed
122
- } else if (msg.method === 'tools/list') {
123
- send({ jsonrpc: '2.0', id: msg.id, result: {
124
- tools: [{
125
- name: 'echo',
126
- description: 'Echoes input',
127
- inputSchema: { type: 'object', properties: { message: { type: 'string' } }, required: ['message'] }
128
- }]
129
- }});
130
- }
131
- }
89
+ const echoServerScript = `
90
+ const { stdin, stdout } = require('process');
91
+ let buf = '';
92
+ stdin.setEncoding('utf8');
93
+ stdin.on('data', (chunk) => {
94
+ buf += chunk;
95
+ while (true) {
96
+ const headerEnd = buf.indexOf('\\r\\n\\r\\n');
97
+ if (headerEnd === -1) break;
98
+ const header = buf.slice(0, headerEnd);
99
+ const match = header.match(/Content-Length:\\s*(\\d+)/i);
100
+ if (!match) { buf = buf.slice(headerEnd + 4); continue; }
101
+ const len = parseInt(match[1], 10);
102
+ const bodyStart = headerEnd + 4;
103
+ if (buf.length < bodyStart + len) break;
104
+ const body = buf.slice(bodyStart, bodyStart + len);
105
+ buf = buf.slice(bodyStart + len);
106
+ handleMessage(JSON.parse(body));
107
+ }
108
+ });
109
+ function send(obj) {
110
+ const s = JSON.stringify(obj);
111
+ stdout.write('Content-Length: ' + Buffer.byteLength(s) + '\\r\\n\\r\\n' + s);
112
+ }
113
+ function handleMessage(msg) {
114
+ if (msg.method === 'initialize') {
115
+ send({ jsonrpc: '2.0', id: msg.id, result: {
116
+ protocolVersion: '2024-11-05',
117
+ capabilities: { tools: { listChanged: false } },
118
+ serverInfo: { name: 'echo-stub', version: '1.0.0' }
119
+ }});
120
+ } else if (msg.method === 'notifications/initialized') {
121
+ // no response needed
122
+ } else if (msg.method === 'tools/list') {
123
+ send({ jsonrpc: '2.0', id: msg.id, result: {
124
+ tools: [{
125
+ name: 'echo',
126
+ description: 'Echoes input',
127
+ inputSchema: { type: 'object', properties: { message: { type: 'string' } }, required: ['message'] }
128
+ }]
129
+ }});
130
+ }
131
+ }
132
132
  `;
133
133
  const tmpDir = await mkdtemp(join(tmpdir(), "mcp-echo-stub-"));
134
134
  const scriptPath = join(tmpDir, "echo-server.js");
@@ -6,9 +6,16 @@
6
6
  * immediately; poll with selfverify.status; fetch selfverify.result when done.
7
7
  *
8
8
  * Lives at the app layer (NOT agent-harness-core) so it may import src/self-qa.
9
+ *
10
+ * NOTE: this server is for EXTERNAL agents (Claude Code etc.). The CLI's OWN
11
+ * inner agent now exposes the same capabilities as NATIVE in-process builtins
12
+ * (src/tools/native-tools.ts) — it no longer self-spawns this server. The two
13
+ * surfaces share their cores (self-verify-runner.ts, setup-guide-text.ts, the
14
+ * ee/forensics/lsp modules) so behaviour is identical.
9
15
  */
10
16
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
17
  import { JobManager, type Runner } from "./self-verify-jobs.js";
12
18
  export declare function registerSelfVerifyTools(server: McpServer, jm: JobManager): void;
19
+ export declare function registerSetupGuideTool(server: McpServer): void;
13
20
  export declare function createToolsServer(runner?: Runner): McpServer;
14
21
  export declare function runToolsMcpServer(): Promise<void>;
@@ -6,6 +6,12 @@
6
6
  * immediately; poll with selfverify.status; fetch selfverify.result when done.
7
7
  *
8
8
  * Lives at the app layer (NOT agent-harness-core) so it may import src/self-qa.
9
+ *
10
+ * NOTE: this server is for EXTERNAL agents (Claude Code etc.). The CLI's OWN
11
+ * inner agent now exposes the same capabilities as NATIVE in-process builtins
12
+ * (src/tools/native-tools.ts) — it no longer self-spawns this server. The two
13
+ * surfaces share their cores (self-verify-runner.ts, setup-guide-text.ts, the
14
+ * ee/forensics/lsp modules) so behaviour is identical.
9
15
  */
10
16
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
17
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -14,29 +20,9 @@ import { registerEETools } from "./ee-tools.js";
14
20
  import { registerForensicsTools } from "./forensics-tools.js";
15
21
  import { registerLspTools } from "./lsp-tools.js";
16
22
  import { JobManager } from "./self-verify-jobs.js";
23
+ import { defaultRunner } from "./self-verify-runner.js";
24
+ import { SETUP_GUIDE_TEXT } from "./setup-guide-text.js";
17
25
  const LOG_TAIL = 40;
18
- /** Default runner: drives the real self-verify functions in-process. */
19
- const defaultRunner = {
20
- async tier1(opts, log) {
21
- // signal intentionally not forwarded: runSelfVerify/runAgenticLoop do not yet
22
- // accept an AbortSignal. cancel() marks the job and discards the late result.
23
- const { runSelfVerify } = await import("../self-qa/index.js");
24
- return runSelfVerify({
25
- baseRef: opts.since,
26
- maxScenarios: opts.max,
27
- emitSpecs: opts.emit,
28
- specOutDir: opts.out,
29
- log,
30
- });
31
- },
32
- async agentic(opts, log) {
33
- // signal intentionally not forwarded: runSelfVerify/runAgenticLoop do not yet
34
- // accept an AbortSignal. cancel() marks the job and discards the late result.
35
- const { createLLMBrain, runAgenticLoop } = await import("../self-qa/agentic-loop.js");
36
- const brain = await createLLMBrain({ modelId: opts.llm });
37
- return runAgenticLoop({ goal: opts.goal, brain, maxTurns: opts.turns ?? 20, log });
38
- },
39
- };
40
26
  function ok(data) {
41
27
  return { content: [{ type: "text", text: JSON.stringify(data) }] };
42
28
  }
@@ -129,6 +115,16 @@ export function registerSelfVerifyTools(server, jm) {
129
115
  });
130
116
  server.registerTool("selfverify_cancel", { description: "Cancel a running self-verify run (best-effort).", inputSchema: { runId: z.string() } }, async ({ runId }) => ok({ cancelled: jm.cancel(runId) }));
131
117
  }
118
+ export function registerSetupGuideTool(server) {
119
+ server.registerTool("setup_guide", {
120
+ description: "Returns a concise, up-to-date setup, install, first-run, MCP wiring, and verification guide for muonroi-cli. " +
121
+ "Call this directly (setup_guide) when the user asks for setup instructions, onboarding, or 'how do I start' — " +
122
+ "instead of guessing, reading files, or shelling commands. Keeps agents on the happy path.",
123
+ inputSchema: {},
124
+ }, async () => {
125
+ return { content: [{ type: "text", text: SETUP_GUIDE_TEXT }] };
126
+ });
127
+ }
132
128
  export function createToolsServer(runner = defaultRunner) {
133
129
  const server = new McpServer({ name: "muonroi-tools", version: "0.1.0" });
134
130
  const jm = new JobManager(runner);
@@ -136,6 +132,7 @@ export function createToolsServer(runner = defaultRunner) {
136
132
  registerEETools(server);
137
133
  registerForensicsTools(server);
138
134
  registerLspTools(server);
135
+ registerSetupGuideTool(server);
139
136
  return server;
140
137
  }
141
138
  export async function runToolsMcpServer() {