muonroi-cli 1.8.0 → 1.8.1
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/src/agent-harness/mock-model.js +112 -4
- package/dist/src/cli/cost-forensics.js +17 -14
- package/dist/src/council/prompts.js +20 -20
- package/dist/src/flow/compaction/index.d.ts +1 -0
- package/dist/src/flow/compaction/index.js +4 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/index.js +4 -2
- package/dist/src/lsp/manager.js +11 -3
- package/dist/src/lsp/manager.test.js +39 -0
- package/dist/src/mcp/auto-setup.js +0 -8
- package/dist/src/mcp/oauth-callback.js +2 -2
- package/dist/src/mcp/parse-headers.test.js +14 -14
- package/dist/src/mcp/self-verify-jobs.d.ts +1 -0
- package/dist/src/mcp/self-verify-runner.js +1 -1
- package/dist/src/mcp/setup-guide-text.js +74 -74
- package/dist/src/mcp/smoke.test.js +43 -43
- package/dist/src/ops/doctor.js +7 -7
- package/dist/src/orchestrator/__tests__/batch-turn-runner.test.js +1 -0
- package/dist/src/orchestrator/__tests__/message-processor.test.js +1 -0
- package/dist/src/orchestrator/__tests__/prompts.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/prompts.test.js +88 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.d.ts +1 -0
- package/dist/src/orchestrator/__tests__/sub-session-delegation.test.js +507 -0
- package/dist/src/orchestrator/auto-commit.js +9 -7
- package/dist/src/orchestrator/message-processor.d.ts +2 -0
- package/dist/src/orchestrator/message-processor.js +7 -0
- package/dist/src/orchestrator/orchestrator.d.ts +3 -0
- package/dist/src/orchestrator/orchestrator.js +362 -28
- package/dist/src/orchestrator/pending-calls.js +2 -1
- package/dist/src/orchestrator/preprocessor.js +3 -2
- package/dist/src/orchestrator/prompts.d.ts +8 -0
- package/dist/src/orchestrator/prompts.js +65 -18
- package/dist/src/orchestrator/sub-agent-cap.d.ts +2 -0
- package/dist/src/orchestrator/sub-agent-cap.js +12 -4
- package/dist/src/orchestrator/subagent-compactor.d.ts +7 -0
- package/dist/src/orchestrator/subagent-compactor.js +65 -14
- package/dist/src/orchestrator/subagent-compactor.spec.js +125 -9
- package/dist/src/orchestrator/tool-engine.d.ts +2 -0
- package/dist/src/orchestrator/tool-engine.js +80 -21
- package/dist/src/orchestrator/turn-runner-deps.d.ts +2 -0
- package/dist/src/pil/__tests__/llm-classify.test.js +79 -1
- package/dist/src/pil/layer1-intent.js +37 -37
- package/dist/src/pil/layer2_5-ponytail.js +8 -8
- package/dist/src/pil/llm-classify.d.ts +11 -0
- package/dist/src/pil/llm-classify.js +193 -0
- 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/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 +11 -2
- package/dist/src/storage/__tests__/hierarchical-session.test.d.ts +1 -0
- package/dist/src/storage/__tests__/hierarchical-session.test.js +161 -0
- package/dist/src/storage/__tests__/migrations.test.js +2 -2
- package/dist/src/storage/__tests__/sweep-stale-pending.test.js +2 -2
- package/dist/src/storage/index.d.ts +1 -1
- package/dist/src/storage/index.js +1 -1
- package/dist/src/storage/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +135 -123
- package/dist/src/storage/session-experience-store.js +4 -4
- package/dist/src/storage/sessions.js +43 -43
- package/dist/src/storage/transcript-response-entry.test.js +18 -5
- package/dist/src/storage/transcript.d.ts +2 -0
- package/dist/src/storage/transcript.js +223 -97
- package/dist/src/storage/usage.js +34 -17
- package/dist/src/storage/workspaces.js +12 -12
- package/dist/src/tools/native-tools.js +10 -1
- package/dist/src/tools/registry.d.ts +1 -0
- package/dist/src/tools/registry.js +30 -2
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/ui/app.js +0 -0
- package/dist/src/ui/slash/council-inspect.js +4 -4
- package/dist/src/ui/slash/expand.js +14 -1
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/utils/__tests__/compaction-caps.test.js +9 -9
- package/dist/src/utils/__tests__/llm-deadline.test.js +16 -4
- package/dist/src/utils/__tests__/logger.test.d.ts +1 -0
- package/dist/src/utils/__tests__/logger.test.js +115 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.d.ts +1 -0
- package/dist/src/utils/__tests__/settings-agent-first.test.js +48 -0
- package/dist/src/utils/clipboard-image.js +23 -23
- package/dist/src/utils/install-manager.d.ts +1 -0
- package/dist/src/utils/install-manager.js +76 -7
- package/dist/src/utils/install-manager.test.js +43 -1
- package/dist/src/utils/logger.d.ts +28 -0
- package/dist/src/utils/logger.js +171 -0
- package/dist/src/utils/settings.d.ts +9 -1
- package/dist/src/utils/settings.js +59 -22
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- package/dist/src/utils/update-checker.test.js +5 -1
- package/dist/src/verify/__tests__/coverage-parsers.test.js +30 -30
- package/package.json +1 -1
|
@@ -131,6 +131,45 @@ describe("createWorkspaceLspManager", () => {
|
|
|
131
131
|
expect(result.output).toContain("No LSP server available");
|
|
132
132
|
await manager.close();
|
|
133
133
|
});
|
|
134
|
+
it("handles missing files gracefully and avoids touchFile on workspaceSymbol", async () => {
|
|
135
|
+
const root = await createTempWorkspace();
|
|
136
|
+
const filePath = path.join(root, "nonexistent.ts");
|
|
137
|
+
const sendRequest = vi.fn(async (method, params) => {
|
|
138
|
+
expect(method).toBe("workspace/symbol");
|
|
139
|
+
expect(params).toEqual({ query: "my-query" });
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
name: "MySymbol",
|
|
143
|
+
kind: 1,
|
|
144
|
+
location: {
|
|
145
|
+
uri: "file:///demo.ts",
|
|
146
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 8 } },
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
});
|
|
151
|
+
const client = createFakeClient({ sendRequest });
|
|
152
|
+
const manager = createWorkspaceLspManager(root, BASE_SETTINGS, {
|
|
153
|
+
createClient: async () => client,
|
|
154
|
+
});
|
|
155
|
+
const result = await manager.query({
|
|
156
|
+
operation: "workspaceSymbol",
|
|
157
|
+
filePath,
|
|
158
|
+
query: "my-query",
|
|
159
|
+
});
|
|
160
|
+
expect(result.success).toBe(true);
|
|
161
|
+
expect(result.output).toContain("MySymbol");
|
|
162
|
+
expect(client.openOrChangeFile).not.toHaveBeenCalled();
|
|
163
|
+
const resultDef = await manager.query({
|
|
164
|
+
operation: "goToDefinition",
|
|
165
|
+
filePath,
|
|
166
|
+
line: 1,
|
|
167
|
+
character: 1,
|
|
168
|
+
});
|
|
169
|
+
expect(resultDef.success).toBe(true);
|
|
170
|
+
expect(resultDef.output).toContain("No results found");
|
|
171
|
+
await manager.close();
|
|
172
|
+
});
|
|
134
173
|
});
|
|
135
174
|
async function createTempWorkspace() {
|
|
136
175
|
const root = await mkdtemp(path.join(os.tmpdir(), "muonroi-lsp-manager-"));
|
|
@@ -39,14 +39,6 @@ const DEFAULT_CONFIGS = [
|
|
|
39
39
|
transport: "http",
|
|
40
40
|
url: "https://docs-mcp.muonroi.com/mcp",
|
|
41
41
|
},
|
|
42
|
-
{
|
|
43
|
-
id: "filesystem",
|
|
44
|
-
label: "Filesystem",
|
|
45
|
-
enabled: true,
|
|
46
|
-
transport: "stdio",
|
|
47
|
-
command: "npx",
|
|
48
|
-
args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
|
49
|
-
},
|
|
50
42
|
{
|
|
51
43
|
id: "playwright",
|
|
52
44
|
label: "Playwright",
|
|
@@ -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
|
});
|
|
@@ -25,7 +25,7 @@ export const defaultRunner = {
|
|
|
25
25
|
async agentic(opts, log) {
|
|
26
26
|
const { createLLMBrain, runAgenticLoop } = await import("../self-qa/agentic-loop.js");
|
|
27
27
|
const brain = await createLLMBrain({ modelId: opts.llm });
|
|
28
|
-
return runAgenticLoop({ goal: opts.goal, brain, maxTurns: opts.turns ?? 20, log });
|
|
28
|
+
return runAgenticLoop({ goal: opts.goal, brain, maxTurns: opts.turns ?? 20, mockLlmDir: opts.mockLlmDir, log });
|
|
29
29
|
},
|
|
30
30
|
};
|
|
31
31
|
let shared = null;
|
|
@@ -6,79 +6,79 @@
|
|
|
6
6
|
* and the muonroi-tools MCP server (src/mcp/tools-server.ts, for external agents).
|
|
7
7
|
* Keeping it here avoids duplicating ~70 lines across the two.
|
|
8
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.
|
|
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
83
|
`;
|
|
84
84
|
//# sourceMappingURL=setup-guide-text.js.map
|
|
@@ -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");
|
package/dist/src/ops/doctor.js
CHANGED
|
@@ -169,9 +169,9 @@ async function checkBrainEmptiness() {
|
|
|
169
169
|
// Count ee_injection events with event_subtype='no_match' in last 30 days
|
|
170
170
|
const cutoff = new Date(Date.now() - 30 * 86_400_000).toISOString();
|
|
171
171
|
const row = db
|
|
172
|
-
.prepare(`SELECT COUNT(*) as cnt FROM interaction_logs
|
|
173
|
-
WHERE event_type = 'ee_injection'
|
|
174
|
-
AND event_subtype = 'no_match'
|
|
172
|
+
.prepare(`SELECT COUNT(*) as cnt FROM interaction_logs
|
|
173
|
+
WHERE event_type = 'ee_injection'
|
|
174
|
+
AND event_subtype = 'no_match'
|
|
175
175
|
AND created_at >= ?`)
|
|
176
176
|
.get(cutoff);
|
|
177
177
|
const noMatchCount = row?.cnt ?? 0;
|
|
@@ -293,10 +293,10 @@ async function checkCouncilMcpNudge() {
|
|
|
293
293
|
// 2. Query DB for [Council Memory] records with URL or research topics
|
|
294
294
|
const db = getDatabase();
|
|
295
295
|
const rows = db
|
|
296
|
-
.prepare(`SELECT message_json FROM messages
|
|
297
|
-
WHERE role = 'system'
|
|
298
|
-
AND message_json LIKE '%[Council Memory]%'
|
|
299
|
-
ORDER BY created_at DESC
|
|
296
|
+
.prepare(`SELECT message_json FROM messages
|
|
297
|
+
WHERE role = 'system'
|
|
298
|
+
AND message_json LIKE '%[Council Memory]%'
|
|
299
|
+
ORDER BY created_at DESC
|
|
300
300
|
LIMIT 50`)
|
|
301
301
|
.all();
|
|
302
302
|
let qualifyingCount = 0;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
describe("prompts.ts limit helpers", () => {
|
|
6
|
+
const tmpHome = path.join(os.tmpdir(), `muonroi-cli-prompts-${process.pid}-${Date.now()}`);
|
|
7
|
+
const origHome = process.env.HOME;
|
|
8
|
+
const origUserProfile = process.env.USERPROFILE;
|
|
9
|
+
const origEnvMaxRounds = process.env.MUONROI_MAX_TOOL_ROUNDS;
|
|
10
|
+
const origEnvHardMax = process.env.MUONROI_HARD_MAX_TOOL_ROUNDS;
|
|
11
|
+
const origEnvLlmCalls = process.env.MUONROI_MAX_LLM_CALLS_PER_TURN;
|
|
12
|
+
const origAgentFirst = process.env.MUONROI_AGENT_FIRST;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
fs.mkdirSync(tmpHome, { recursive: true });
|
|
15
|
+
process.env.HOME = tmpHome;
|
|
16
|
+
process.env.USERPROFILE = tmpHome;
|
|
17
|
+
delete process.env.MUONROI_MAX_TOOL_ROUNDS;
|
|
18
|
+
delete process.env.MUONROI_HARD_MAX_TOOL_ROUNDS;
|
|
19
|
+
delete process.env.MUONROI_MAX_LLM_CALLS_PER_TURN;
|
|
20
|
+
delete process.env.MUONROI_AGENT_FIRST;
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
24
|
+
if (origHome === undefined)
|
|
25
|
+
delete process.env.HOME;
|
|
26
|
+
else
|
|
27
|
+
process.env.HOME = origHome;
|
|
28
|
+
if (origUserProfile === undefined)
|
|
29
|
+
delete process.env.USERPROFILE;
|
|
30
|
+
else
|
|
31
|
+
process.env.USERPROFILE = origUserProfile;
|
|
32
|
+
if (origEnvMaxRounds === undefined)
|
|
33
|
+
delete process.env.MUONROI_MAX_TOOL_ROUNDS;
|
|
34
|
+
else
|
|
35
|
+
process.env.MUONROI_MAX_TOOL_ROUNDS = origEnvMaxRounds;
|
|
36
|
+
if (origEnvHardMax === undefined)
|
|
37
|
+
delete process.env.MUONROI_HARD_MAX_TOOL_ROUNDS;
|
|
38
|
+
else
|
|
39
|
+
process.env.MUONROI_HARD_MAX_TOOL_ROUNDS = origEnvHardMax;
|
|
40
|
+
if (origEnvLlmCalls === undefined)
|
|
41
|
+
delete process.env.MUONROI_MAX_LLM_CALLS_PER_TURN;
|
|
42
|
+
else
|
|
43
|
+
process.env.MUONROI_MAX_LLM_CALLS_PER_TURN = origEnvLlmCalls;
|
|
44
|
+
if (origAgentFirst === undefined)
|
|
45
|
+
delete process.env.MUONROI_AGENT_FIRST;
|
|
46
|
+
else
|
|
47
|
+
process.env.MUONROI_AGENT_FIRST = origAgentFirst;
|
|
48
|
+
vi.resetModules();
|
|
49
|
+
});
|
|
50
|
+
it("loads default limits when agentFirst is false", async () => {
|
|
51
|
+
const dir = path.join(tmpHome, ".muonroi-cli");
|
|
52
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
53
|
+
fs.writeFileSync(path.join(dir, "user-settings.json"), JSON.stringify({ agentFirst: false }));
|
|
54
|
+
vi.resetModules();
|
|
55
|
+
const { MAX_TOOL_ROUNDS, HARD_MAX_TOOL_ROUNDS, MAX_LLM_CALLS_PER_TURN } = await import("../prompts.js");
|
|
56
|
+
expect(MAX_TOOL_ROUNDS).toBe(40);
|
|
57
|
+
expect(HARD_MAX_TOOL_ROUNDS).toBe(60);
|
|
58
|
+
expect(MAX_LLM_CALLS_PER_TURN).toBe(12);
|
|
59
|
+
});
|
|
60
|
+
it("defaults to agentFirst limits (true) when no config is present", async () => {
|
|
61
|
+
vi.resetModules();
|
|
62
|
+
const { MAX_TOOL_ROUNDS, HARD_MAX_TOOL_ROUNDS, MAX_LLM_CALLS_PER_TURN } = await import("../prompts.js");
|
|
63
|
+
expect(MAX_TOOL_ROUNDS).toBe(200);
|
|
64
|
+
expect(HARD_MAX_TOOL_ROUNDS).toBe(300);
|
|
65
|
+
expect(MAX_LLM_CALLS_PER_TURN).toBe(100);
|
|
66
|
+
});
|
|
67
|
+
it("loads raised limits when agentFirst is true in settings", async () => {
|
|
68
|
+
const dir = path.join(tmpHome, ".muonroi-cli");
|
|
69
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
70
|
+
fs.writeFileSync(path.join(dir, "user-settings.json"), JSON.stringify({ agentFirst: true }));
|
|
71
|
+
vi.resetModules();
|
|
72
|
+
const { MAX_TOOL_ROUNDS, HARD_MAX_TOOL_ROUNDS, MAX_LLM_CALLS_PER_TURN } = await import("../prompts.js");
|
|
73
|
+
expect(MAX_TOOL_ROUNDS).toBe(200);
|
|
74
|
+
expect(HARD_MAX_TOOL_ROUNDS).toBe(300);
|
|
75
|
+
expect(MAX_LLM_CALLS_PER_TURN).toBe(100);
|
|
76
|
+
});
|
|
77
|
+
it("respects env overrides and clamps them (when agentFirst is true by default)", async () => {
|
|
78
|
+
process.env.MUONROI_MAX_TOOL_ROUNDS = "3000"; // Clamped to 2000 in agent-first
|
|
79
|
+
process.env.MUONROI_HARD_MAX_TOOL_ROUNDS = "50";
|
|
80
|
+
process.env.MUONROI_MAX_LLM_CALLS_PER_TURN = "15";
|
|
81
|
+
vi.resetModules();
|
|
82
|
+
const { MAX_TOOL_ROUNDS, HARD_MAX_TOOL_ROUNDS, MAX_LLM_CALLS_PER_TURN } = await import("../prompts.js");
|
|
83
|
+
expect(MAX_TOOL_ROUNDS).toBe(2000);
|
|
84
|
+
expect(HARD_MAX_TOOL_ROUNDS).toBe(50);
|
|
85
|
+
expect(MAX_LLM_CALLS_PER_TURN).toBe(15);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=prompts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|