monomind 2.2.0 → 2.3.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/package.json +2 -2
- package/packages/@monomind/cli/.claude/commands/mastermind/createorg.md +24 -16
- package/packages/@monomind/cli/.claude/commands/mastermind/master.md +23 -7
- package/packages/@monomind/cli/.claude/commands/mastermind/runorg.md +3 -1
- package/packages/@monomind/cli/.claude/commands/mastermind/stoporg.md +6 -3
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/code-quality-reviewer-prompt.md +3 -3
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/createorg.md +19 -24
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/debug.md +49 -4
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/design.md +11 -10
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/final-reviewer-prompt.md +144 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/implementer-prompt.md +9 -4
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/org-settings.md +20 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgs.md +40 -19
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/orgstatus.md +90 -41
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/plan.md +19 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/skill-builder.md +171 -0
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/spec-reviewer-prompt.md +19 -2
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/stoporg.md +7 -5
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/taskdev.md +83 -15
- package/packages/@monomind/cli/.claude/skills/mastermind-skills/tdd.md +30 -0
- package/packages/@monomind/cli/dist/src/autopilot-state.js +6 -4
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +4 -1
- package/packages/@monomind/cli/dist/src/capabilities/manager.js +3 -1
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +26 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +28 -46
- package/packages/@monomind/cli/dist/src/commands/cleanup.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/cleanup.js +120 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +13 -4
- package/packages/@monomind/cli/dist/src/commands/hooks-routing-commands.js +34 -18
- package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +8 -2
- package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/mcp.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +7 -1
- package/packages/@monomind/cli/dist/src/commands/org-observe.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/org-observe.js +295 -0
- package/packages/@monomind/cli/dist/src/commands/org.d.ts +9 -1
- package/packages/@monomind/cli/dist/src/commands/org.js +191 -9
- package/packages/@monomind/cli/dist/src/commands/performance.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/platforms.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/security-scan.js +8 -2
- package/packages/@monomind/cli/dist/src/commands/start.js +4 -1
- package/packages/@monomind/cli/dist/src/commands/swarm.js +3 -2
- package/packages/@monomind/cli/dist/src/index.js +13 -3
- package/packages/@monomind/cli/dist/src/init/executor.js +19 -5
- package/packages/@monomind/cli/dist/src/knowledge/document-pipeline.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +63 -20
- package/packages/@monomind/cli/dist/src/mcp-tools/claims-tools.js +3 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/config-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/daa-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/embeddings-tools.js +6 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +35 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +24 -7
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-embedding.js +13 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-intelligence.js +9 -3
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.d.ts +1 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-routing.js +112 -10
- package/packages/@monomind/cli/dist/src/mcp-tools/hooks-tools.js +2 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +8 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/performance-tools.js +3 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/session-tools.js +15 -5
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +4 -1
- package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +6 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.d.ts +20 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.js +38 -16
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/mcp-tools/terminal-tools.js +41 -24
- package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.js +6 -2
- package/packages/@monomind/cli/dist/src/memory/intelligence.js +32 -10
- package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +16 -4
- package/packages/@monomind/cli/dist/src/memory/memory-crud.js +26 -2
- package/packages/@monomind/cli/dist/src/memory/memory-initializer.js +3 -2
- package/packages/@monomind/cli/dist/src/memory/memory-migrations.js +2 -0
- package/packages/@monomind/cli/dist/src/monovector/command-outcomes.js +4 -1
- package/packages/@monomind/cli/dist/src/orgrt/bus.js +4 -1
- package/packages/@monomind/cli/dist/src/orgrt/daemon.js +74 -19
- package/packages/@monomind/cli/dist/src/orgrt/inbox.js +3 -1
- package/packages/@monomind/cli/dist/src/orgrt/reporting.d.ts +40 -0
- package/packages/@monomind/cli/dist/src/orgrt/reporting.js +127 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +2 -0
- package/packages/@monomind/cli/dist/src/orgrt/session.js +10 -2
- package/packages/@monomind/cli/dist/src/orgrt/templates.d.ts +16 -0
- package/packages/@monomind/cli/dist/src/orgrt/templates.js +57 -0
- package/packages/@monomind/cli/dist/src/services/config-file-manager.js +10 -1
- package/packages/@monomind/cli/dist/src/transfer/storage/gcs.js +6 -2
- package/packages/@monomind/cli/dist/src/ui/orgs.html +32 -0
- package/packages/@monomind/cli/dist/src/ui/server.mjs +25 -0
- package/packages/@monomind/cli/dist/src/update/executor.js +3 -1
- package/packages/@monomind/cli/dist/src/update/rate-limiter.js +3 -1
- package/packages/@monomind/cli/package.json +2 -2
|
@@ -13,6 +13,8 @@ Execute a plan by dispatching a fresh subagent per task, with two-stage review a
|
|
|
13
13
|
|
|
14
14
|
**Core principle:** Fresh subagent per task + two-stage review (spec then quality) = high quality, fast iteration
|
|
15
15
|
|
|
16
|
+
**Narration:** between tool calls, narrate at most one short line — the progress ledger and the tool results carry the record.
|
|
17
|
+
|
|
16
18
|
**Continuous execution:** Do not pause to check in with your human partner between tasks. Execute all tasks from the plan without stopping. The only reasons to stop are: BLOCKED status you cannot resolve, ambiguity that genuinely prevents progress, or all tasks complete. "Should I continue?" prompts and progress summaries waste their time — they asked you to execute the plan, so execute it.
|
|
17
19
|
|
|
18
20
|
---
|
|
@@ -75,12 +77,12 @@ digraph process {
|
|
|
75
77
|
"Mark task complete in TodoWrite" [shape=box];
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
"Read plan,
|
|
80
|
+
"Read plan, note context and global constraints, create TodoWrite" [shape=box];
|
|
79
81
|
"More tasks remain?" [shape=diamond];
|
|
80
82
|
"Dispatch final code reviewer subagent for entire implementation" [shape=box];
|
|
81
83
|
"Use mastermind:finish" [shape=box style=filled fillcolor=lightgreen];
|
|
82
84
|
|
|
83
|
-
"Read plan,
|
|
85
|
+
"Read plan, note context and global constraints, create TodoWrite" -> "Dispatch implementer subagent (./implementer-prompt.md)";
|
|
84
86
|
"Dispatch implementer subagent (./implementer-prompt.md)" -> "Implementer subagent asks questions?";
|
|
85
87
|
"Implementer subagent asks questions?" -> "Answer questions, provide context" [label="yes"];
|
|
86
88
|
"Answer questions, provide context" -> "Dispatch implementer subagent (./implementer-prompt.md)";
|
|
@@ -103,6 +105,17 @@ digraph process {
|
|
|
103
105
|
|
|
104
106
|
---
|
|
105
107
|
|
|
108
|
+
## Pre-Flight Plan Review
|
|
109
|
+
|
|
110
|
+
Before dispatching Task 1, scan the plan once for conflicts:
|
|
111
|
+
|
|
112
|
+
- tasks that contradict each other or the plan's Global Constraints
|
|
113
|
+
- anything the plan explicitly mandates that the review rubric treats as a defect (a test that asserts nothing, verbatim duplication of a logic block)
|
|
114
|
+
|
|
115
|
+
Present everything you find to your human partner as one batched question — each finding beside the plan text that mandates it, asking which governs — before execution begins, not one interrupt per discovery mid-plan. If the scan is clean, proceed without comment. The review loop remains the net for conflicts that only emerge from implementation. (In auto mode: resolve each conflict yourself, state the resolution explicitly, and log it as a decision with confidence.)
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
106
119
|
## Model Selection
|
|
107
120
|
|
|
108
121
|
Use the least powerful model that can handle each role to conserve cost and increase speed.
|
|
@@ -111,7 +124,13 @@ Use the least powerful model that can handle each role to conserve cost and incr
|
|
|
111
124
|
|
|
112
125
|
**Integration and judgment tasks** (multi-file coordination, pattern matching, debugging): use a standard model.
|
|
113
126
|
|
|
114
|
-
**Architecture
|
|
127
|
+
**Architecture and design tasks**: use the most capable available model. The final whole-branch review is one of these — dispatch it on the most capable available model, not the session default.
|
|
128
|
+
|
|
129
|
+
**Review tasks**: choose the model with the same judgment, scaled to the diff's size, complexity, and risk. A small mechanical diff does not need the most capable model; a subtle concurrency change does.
|
|
130
|
+
|
|
131
|
+
**Always specify the model explicitly when dispatching a subagent.** An omitted model inherits your session's model — often the most capable and most expensive — which silently defeats this section.
|
|
132
|
+
|
|
133
|
+
**Turn count beats token price.** Wall-clock and context cost scale with how many turns a subagent takes, and the cheapest models routinely take 2-3× the turns on multi-step work — costing more overall. Use a mid-tier model as the floor for reviewers and for implementers working from prose descriptions. When the task's plan text contains the complete code to write, the implementation is transcription plus testing: use the cheapest tier for that implementer. Single-file mechanical fixes also take the cheapest tier.
|
|
115
134
|
|
|
116
135
|
**Task complexity signals:**
|
|
117
136
|
- Touches 1-2 files with a complete spec → cheap model
|
|
@@ -124,7 +143,7 @@ Use the least powerful model that can handle each role to conserve cost and incr
|
|
|
124
143
|
|
|
125
144
|
Implementer subagents report one of four statuses. Handle each appropriately:
|
|
126
145
|
|
|
127
|
-
**DONE:**
|
|
146
|
+
**DONE:** Generate the task's diff file from the BASE you recorded before dispatching the implementer (never `HEAD~1` — it silently drops all but the last commit of a multi-commit task), then proceed to spec compliance review with the brief, report, and diff file paths.
|
|
128
147
|
|
|
129
148
|
**DONE_WITH_CONCERNS:** The implementer completed the work but flagged doubts. Read the concerns before proceeding. If the concerns are about correctness or scope, address them before review. If they're observations (e.g., "this file is getting large"), note them and proceed to review.
|
|
130
149
|
|
|
@@ -140,11 +159,58 @@ Implementer subagents report one of four statuses. Handle each appropriately:
|
|
|
140
159
|
|
|
141
160
|
---
|
|
142
161
|
|
|
162
|
+
## Handling Reviewer ⚠️ Items
|
|
163
|
+
|
|
164
|
+
A reviewer may report "⚠️ Cannot verify from diff" items — requirements that live in unchanged code or span tasks. These do not block the rest of the review, but you must resolve each one yourself before marking the task complete: you hold the plan and cross-task context the reviewer lacks. If you confirm an item is a real gap, treat it as a failed spec review — send it back to the implementer and re-review.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Constructing Reviewer Prompts
|
|
169
|
+
|
|
170
|
+
Per-task reviews are task-scoped gates. The broad review happens once, at the final whole-branch review. When you fill a reviewer template:
|
|
171
|
+
|
|
172
|
+
- Do not add open-ended directives like "check all uses" or "run race tests if useful" without a concrete, task-specific reason
|
|
173
|
+
- Do not ask a reviewer to re-run tests the implementer already ran on the same code — the implementer's report carries the test evidence
|
|
174
|
+
- Do not pre-judge findings for the reviewer — never instruct a reviewer to ignore or not flag a specific issue. If you believe a finding would be a false positive, let the reviewer raise it and adjudicate it in the review loop. If the prompt you are writing contains "do not flag," "don't treat X as a defect," "at most Minor," or "the plan chose" — stop: you are pre-judging, usually to spare yourself a review loop.
|
|
175
|
+
- The global-constraints block you hand the reviewer is its attention lens. Copy the binding requirements verbatim from the plan's Global Constraints section or the spec: exact values, exact formats, and the stated relationships between components ("same layout as X", "matches Y"). The reviewer's template already carries the process rules — the constraints block is for what THIS project's spec demands.
|
|
176
|
+
- Hand the reviewer its diff as a file: run `git log --oneline BASE..HEAD`, `git diff --stat BASE..HEAD`, and `git diff -U10 BASE..HEAD`, redirected to one uniquely named file (e.g. `.monomind/taskdev/task-N-review.diff`), and pass the reviewer that path. The output never enters your own context, and the reviewer sees the commit list, stat summary, and full diff with context in one Read call. Use the BASE you recorded before dispatching the implementer — never `HEAD~1`, which silently truncates multi-commit tasks.
|
|
177
|
+
- A dispatch prompt describes one task, not the session's history. Do not paste accumulated prior-task summaries ("state after Tasks 1-3") into later dispatches. A fresh subagent needs its task, the interfaces it touches, and the global constraints. Nothing else.
|
|
178
|
+
- Dispatch fix subagents for Critical and Important findings. Record Minor findings in the progress ledger as you go, and point the final whole-branch review at that list so it can triage which must be fixed before merge. A roll-up nobody reads is a silent discard.
|
|
179
|
+
- A finding labeled plan-mandated — or any finding that conflicts with what the plan's text requires — is the human's decision, like any plan contradiction: present the finding and the plan text, ask which governs. Do not dismiss the finding because the plan mandates it, and do not dispatch a fix that contradicts the plan without asking. (In auto mode: resolve it explicitly, log the decision, and prefer the spec over the plan's example code.)
|
|
180
|
+
- The final whole-branch review gets a diff file too: generate it from the merge base (`git merge-base main HEAD`) to HEAD and include the path in the final review dispatch, so the final reviewer reads one file instead of re-deriving the branch diff with git commands.
|
|
181
|
+
- Every fix dispatch carries the implementer contract: the fix subagent re-runs the tests covering its change and reports the results. Name the covering test files in the dispatch — a one-line fix does not need the whole suite. Before re-dispatching the reviewer, confirm the fix report contains the covering tests, the command run, and the output.
|
|
182
|
+
- If the final whole-branch review returns findings, dispatch ONE fix subagent with the complete findings list — not one fixer per finding. Per-finding fixers each rebuild context and re-run suites; a real session's final-review fix wave cost more than all its tasks combined.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## File Handoffs
|
|
187
|
+
|
|
188
|
+
Everything you paste into a dispatch prompt — and everything a subagent prints back — stays resident in your context for the rest of the session and is re-read on every later turn. Hand artifacts over as files:
|
|
189
|
+
|
|
190
|
+
- **Task brief:** before dispatching an implementer, extract the task's full text from the plan into a uniquely named file (e.g. `.monomind/taskdev/task-N-brief.md`). Compose the dispatch so the brief stays the single source of requirements. Your dispatch should contain: (1) one line on where this task fits in the project; (2) the brief path, introduced as "read this first — it is your requirements, with the exact values to use verbatim"; (3) interfaces and decisions from earlier tasks that the brief cannot know; (4) your resolution of any ambiguity you noticed in the brief; (5) the report-file path and report contract. Exact values (numbers, magic strings, signatures, test cases) appear only in the brief.
|
|
191
|
+
- **Report file:** name the implementer's report file after the brief (brief `…/task-N-brief.md` → report `…/task-N-report.md`) and put it in the dispatch prompt. The implementer writes the full report there and returns only status, commits, a one-line test summary, and concerns.
|
|
192
|
+
- **Reviewer inputs:** each reviewer gets three paths — the same brief file, the report file, and the diff file — plus the global constraints that bind the task.
|
|
193
|
+
- Fix dispatches append their fix report (with test results) to the same report file and return a short summary; re-reviews read the updated file.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Durable Progress
|
|
198
|
+
|
|
199
|
+
Conversation memory does not survive compaction. Controllers that lost their place have re-dispatched entire completed task sequences — the single most expensive failure observed. Track progress in a ledger file, not only in todos.
|
|
200
|
+
|
|
201
|
+
- At skill start, check for a ledger: `cat "$(git rev-parse --show-toplevel)/.monomind/taskdev/progress.md"`. Tasks listed there as complete are DONE — do not re-dispatch them; resume at the first task not marked complete.
|
|
202
|
+
- When a task's reviews come back clean, append one line to the ledger in the same message as your other bookkeeping: `Task N: complete (commits <base7>..<head7>, spec ✅ quality ✅)`.
|
|
203
|
+
- The ledger is your recovery map: the commits it names exist in git even when your context no longer remembers creating them. After compaction, trust the ledger and `git log` over your own recollection.
|
|
204
|
+
- The ledger is git-ignored scratch; if it's destroyed, recover from `git log`.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
143
208
|
## Prompt Templates
|
|
144
209
|
|
|
145
210
|
- `./implementer-prompt.md` - Dispatch implementer subagent
|
|
146
211
|
- `./spec-reviewer-prompt.md` - Dispatch spec compliance reviewer subagent
|
|
147
212
|
- `./code-quality-reviewer-prompt.md` - Dispatch code quality reviewer subagent
|
|
213
|
+
- `./final-reviewer-prompt.md` - Dispatch the final whole-branch reviewer (most capable model, diff as a file)
|
|
148
214
|
|
|
149
215
|
---
|
|
150
216
|
|
|
@@ -154,13 +220,13 @@ Implementer subagents report one of four statuses. Handle each appropriately:
|
|
|
154
220
|
You: I'm using mastermind:taskdev to execute this plan.
|
|
155
221
|
|
|
156
222
|
[Read plan file once: docs/mastermind/plans/feature-plan.md]
|
|
157
|
-
[
|
|
223
|
+
[Check ledger .monomind/taskdev/progress.md — empty, starting fresh]
|
|
158
224
|
[Create TodoWrite with all tasks]
|
|
159
225
|
|
|
160
226
|
Task 1: Hook installation script
|
|
161
227
|
|
|
162
|
-
[
|
|
163
|
-
[Dispatch
|
|
228
|
+
[Extract Task 1 brief to .monomind/taskdev/task-1-brief.md; record BASE]
|
|
229
|
+
[Dispatch implementer with brief + report paths + scene-setting context]
|
|
164
230
|
|
|
165
231
|
Implementer: "Before I begin - should the hook be installed at user or system level?"
|
|
166
232
|
|
|
@@ -173,18 +239,18 @@ Implementer: "Got it. Implementing now..."
|
|
|
173
239
|
- Self-review: Found I missed --force flag, added it
|
|
174
240
|
- Committed
|
|
175
241
|
|
|
176
|
-
[
|
|
242
|
+
[Generate diff file from recorded BASE, dispatch spec compliance reviewer]
|
|
177
243
|
Spec reviewer: ✅ Spec compliant - all requirements met, nothing extra
|
|
178
244
|
|
|
179
|
-
[
|
|
245
|
+
[Dispatch code quality reviewer with the same diff file]
|
|
180
246
|
Code reviewer: Strengths: Good test coverage, clean. Issues: None. Approved.
|
|
181
247
|
|
|
182
248
|
[Mark Task 1 complete]
|
|
183
249
|
|
|
184
250
|
Task 2: Recovery modes
|
|
185
251
|
|
|
186
|
-
[
|
|
187
|
-
[Dispatch
|
|
252
|
+
[Extract Task 2 brief to .monomind/taskdev/task-2-brief.md; record BASE]
|
|
253
|
+
[Dispatch implementer with brief + report paths + Task 1 interfaces]
|
|
188
254
|
|
|
189
255
|
Implementer: [No questions, proceeds]
|
|
190
256
|
Implementer:
|
|
@@ -240,8 +306,7 @@ Done!
|
|
|
240
306
|
- Review checkpoints automatic
|
|
241
307
|
|
|
242
308
|
**Efficiency gains:**
|
|
243
|
-
-
|
|
244
|
-
- Controller curates exactly what context is needed
|
|
309
|
+
- Controller curates exactly what context is needed; bulk artifacts move as files (briefs, reports, diffs), not pasted text
|
|
245
310
|
- Subagent gets complete information upfront
|
|
246
311
|
- Questions surfaced before work begins (not after)
|
|
247
312
|
|
|
@@ -267,14 +332,17 @@ Done!
|
|
|
267
332
|
- Skip reviews (spec compliance OR code quality)
|
|
268
333
|
- Proceed with unfixed issues
|
|
269
334
|
- Dispatch multiple implementation subagents in parallel (conflicts)
|
|
270
|
-
- Make subagent read plan file (
|
|
335
|
+
- Make a subagent read the whole plan file (hand it its task-brief file instead)
|
|
271
336
|
- Skip scene-setting context (subagent needs to understand where task fits)
|
|
272
337
|
- Ignore subagent questions (answer before letting them proceed)
|
|
273
338
|
- Accept "close enough" on spec compliance (spec reviewer found issues = not done)
|
|
274
339
|
- Skip review loops (reviewer found issues = implementer fixes = review again)
|
|
275
340
|
- Let implementer self-review replace actual review (both are needed)
|
|
276
341
|
- **Start code quality review before spec compliance is ✅** (wrong order)
|
|
277
|
-
- Move to next task while either review has open issues
|
|
342
|
+
- Move to next task while either review has open Critical/Important issues
|
|
343
|
+
- Tell a reviewer what not to flag, or pre-rate a finding's severity in the dispatch prompt ("treat it as Minor at most") — the plan's example code is a starting point, not evidence that its weaknesses were chosen
|
|
344
|
+
- Dispatch a reviewer without a diff file — generate it first from the recorded BASE and name the path in the prompt
|
|
345
|
+
- Re-dispatch a task the progress ledger already marks complete — check the ledger (and `git log`) after any compaction or resume
|
|
278
346
|
|
|
279
347
|
**If subagent asks questions:**
|
|
280
348
|
- Answer clearly and completely
|
|
@@ -376,6 +376,36 @@ Can't check all boxes? You skipped TDD. Start over.
|
|
|
376
376
|
|
|
377
377
|
---
|
|
378
378
|
|
|
379
|
+
## Testing Anti-Patterns
|
|
380
|
+
|
|
381
|
+
Tests must verify real behavior, not mock behavior. Mocks are a means to isolate, not the thing being tested. **Test what the code does, not what the mocks do.**
|
|
382
|
+
|
|
383
|
+
**The Iron Laws of mocking:**
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
1. NEVER test mock behavior
|
|
387
|
+
2. NEVER add test-only methods to production classes
|
|
388
|
+
3. NEVER mock without understanding dependencies
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
| Anti-pattern | The violation | The fix |
|
|
392
|
+
|---|---|---|
|
|
393
|
+
| **Testing mock behavior** | Asserting a `*-mock` element or mocked return exists — the test verifies the mock works, not the component | Test the real component, or don't assert on the mock at all |
|
|
394
|
+
| **Test-only methods in production** | A method (e.g. `destroy()`) that only tests call, living on a production class | Move it to test utilities; production classes carry only production API |
|
|
395
|
+
| **Mocking without understanding** | Mocking a method whose side effect the test depends on ("I'll mock this to be safe") — the test passes for the wrong reason or fails mysteriously | Run the test against the real implementation FIRST, observe what it needs, then mock minimally at the lowest slow/external level |
|
|
396
|
+
| **Incomplete mocks** | Mocking only the fields your immediate test uses — downstream code reads omitted fields and fails silently in integration | Mirror the COMPLETE real data structure; if uncertain, include all documented fields |
|
|
397
|
+
| **Tests as afterthought** | "Implementation complete, ready for testing" | Testing is part of implementation — TDD, tests first |
|
|
398
|
+
|
|
399
|
+
**Gate before any mock:** What side effects does the real method have? Does this test depend on any of them? Do I fully understand what this test needs? If unsure — run against the real implementation first.
|
|
400
|
+
|
|
401
|
+
**When mocks become too complex** (setup longer than test logic, mock missing methods real components have, test breaks when mock changes): ask "do we need a mock here at all?" — integration tests with real components are often simpler than complex mocks.
|
|
402
|
+
|
|
403
|
+
**Red flags:** assertions on `*-mock` IDs · methods only called from test files · mock setup >50% of the test · test fails when you remove the mock · can't explain why the mock is needed · mocking "just to be safe".
|
|
404
|
+
|
|
405
|
+
If TDD reveals you're testing mock behavior, you added mocks without watching the test fail against real code first — test real behavior or question why you're mocking at all.
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
379
409
|
## Debugging Integration
|
|
380
410
|
|
|
381
411
|
Bug found? Write failing test reproducing it. Follow TDD cycle. Test proves fix and prevents regression.
|
|
@@ -109,8 +109,9 @@ export function loadState() {
|
|
|
109
109
|
return merged;
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
-
catch {
|
|
113
|
-
|
|
112
|
+
catch (e) {
|
|
113
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
114
|
+
console.error('[autopilot-state] failed to load state file, using defaults:', e);
|
|
114
115
|
}
|
|
115
116
|
return defaults;
|
|
116
117
|
}
|
|
@@ -214,8 +215,9 @@ export function loadLog() {
|
|
|
214
215
|
return out;
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
|
-
catch {
|
|
218
|
-
|
|
218
|
+
catch (e) {
|
|
219
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
220
|
+
console.error('[autopilot-state] failed to load log file, returning empty:', e);
|
|
219
221
|
}
|
|
220
222
|
return [];
|
|
221
223
|
}
|
|
@@ -125,7 +125,10 @@ export function getDashboardServer(port = DEFAULT_PORT) {
|
|
|
125
125
|
client.send(body);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
catch {
|
|
128
|
+
catch (e) {
|
|
129
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
130
|
+
console.error('[dashboard] /api/mastermind/event received invalid JSON, not broadcast:', e);
|
|
131
|
+
}
|
|
129
132
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
130
133
|
res.end('{"ok":true}');
|
|
131
134
|
});
|
|
@@ -45,8 +45,10 @@ export class CapabilityManager {
|
|
|
45
45
|
fs.writeFileSync(tmpPath, JSON.stringify({ active: [...this.active.keys()] }, null, 2));
|
|
46
46
|
fs.renameSync(tmpPath, capsPath);
|
|
47
47
|
}
|
|
48
|
-
catch {
|
|
48
|
+
catch (e) {
|
|
49
49
|
// best-effort persistence; activation state still holds in-memory
|
|
50
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
51
|
+
console.error('[capabilities] failed to persist capabilities.json:', e);
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
isActive(name) {
|
|
@@ -131,6 +131,16 @@ export const spawnCommand = {
|
|
|
131
131
|
priority: 'normal',
|
|
132
132
|
metadata: { name: agentName, capabilities: getAgentCapabilities(agentType) },
|
|
133
133
|
});
|
|
134
|
+
// agent_spawn resolves (doesn't throw) on a tool-level failure like
|
|
135
|
+
// "agent store is unreadable" or "agent already exists" — callMCPTool
|
|
136
|
+
// only throws for registry/infra errors (tool not found/disabled), not
|
|
137
|
+
// for a handler's own {success:false} response. Without this check the
|
|
138
|
+
// CLI silently rendered a "spawned successfully" table full of
|
|
139
|
+
// undefined fields for a spawn that never actually happened.
|
|
140
|
+
if (result.success === false) {
|
|
141
|
+
output.printError(`Failed to spawn agent: ${result.error || 'unknown error'}`);
|
|
142
|
+
return { success: false, exitCode: 1 };
|
|
143
|
+
}
|
|
134
144
|
output.writeln();
|
|
135
145
|
output.printTable({
|
|
136
146
|
columns: [
|
|
@@ -230,6 +240,14 @@ export const statusCommand = {
|
|
|
230
240
|
}
|
|
231
241
|
try {
|
|
232
242
|
const status = await callMCPTool('agent_status', { agentId, includeMetrics: true, includeHistory: false });
|
|
243
|
+
// agent_status resolves (doesn't throw) with {status:'not_found', error}
|
|
244
|
+
// for a nonexistent agent — callMCPTool only throws for registry/infra
|
|
245
|
+
// errors, not a handler's own not-found response. Without this check
|
|
246
|
+
// the CLI reported success:true for an agent that was never found.
|
|
247
|
+
if (status.error) {
|
|
248
|
+
output.printError(`Failed to get agent status: ${status.error}`);
|
|
249
|
+
return { success: false, exitCode: 1 };
|
|
250
|
+
}
|
|
233
251
|
if (ctx.flags.format === 'json') {
|
|
234
252
|
output.printJson(status);
|
|
235
253
|
return { success: true, data: status };
|
|
@@ -296,6 +314,14 @@ export const stopCommand = {
|
|
|
296
314
|
const result = await callMCPTool('agent_terminate', {
|
|
297
315
|
agentId, graceful: !force, reason: 'Stopped by user via CLI',
|
|
298
316
|
});
|
|
317
|
+
// agent_terminate resolves (doesn't throw) with {success:false, error}
|
|
318
|
+
// for a nonexistent agent or an unreadable store — callMCPTool only
|
|
319
|
+
// throws for registry/infra errors. Without this check the CLI printed
|
|
320
|
+
// "stopped successfully" for an agent that was never actually stopped.
|
|
321
|
+
if (result.success === false) {
|
|
322
|
+
output.printError(`Failed to stop agent: ${result.error || 'unknown error'}`);
|
|
323
|
+
return { success: false, exitCode: 1 };
|
|
324
|
+
}
|
|
299
325
|
if (!force) {
|
|
300
326
|
output.writeln(output.dim(' Completing current task...'));
|
|
301
327
|
output.writeln(output.dim(' Saving state...'));
|
|
@@ -9,52 +9,36 @@ export const metricsCommand = {
|
|
|
9
9
|
name: 'metrics',
|
|
10
10
|
description: 'Show agent performance metrics',
|
|
11
11
|
action: async (ctx) => {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// Previously read from .swarm/agents/*.json (one file per agent) — a
|
|
13
|
+
// directory nothing in the codebase has ever written to. agent_spawn
|
|
14
|
+
// (agent-tools.ts) persists to a single store.json at
|
|
15
|
+
// getMonomindDataRoot()/agents/store.json, keyed by agentId. This
|
|
16
|
+
// command was structurally incapable of reflecting a single real
|
|
17
|
+
// spawned agent; always reported zero regardless of activity. Read the
|
|
18
|
+
// real store instead.
|
|
19
|
+
const { loadAgentStore } = await import('../mcp-tools/agent-tools.js');
|
|
20
|
+
const store = loadAgentStore();
|
|
21
|
+
const agents = Object.values(store.agents);
|
|
14
22
|
let totalAgents = 0;
|
|
15
23
|
let activeAgents = 0;
|
|
16
24
|
let tasksCompleted = 0;
|
|
17
25
|
const typeCounts = {};
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (data.status === 'active' || data.status === 'running')
|
|
35
|
-
activeAgents++;
|
|
36
|
-
if (data.tasksCompleted) {
|
|
37
|
-
typeCounts[agType].tasks += data.tasksCompleted;
|
|
38
|
-
tasksCompleted += data.tasksCompleted;
|
|
39
|
-
}
|
|
40
|
-
if (data.successCount)
|
|
41
|
-
typeCounts[agType].success += data.successCount;
|
|
42
|
-
}
|
|
43
|
-
catch { /* skip malformed */ }
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch { /* no agents dir */ }
|
|
47
|
-
}
|
|
48
|
-
const activityFile = join(swarmDir, 'swarm-activity.json');
|
|
49
|
-
if (existsSync(activityFile) && statSync(activityFile).size <= 10 * 1024 * 1024) {
|
|
50
|
-
try {
|
|
51
|
-
const activity = JSON.parse(readFileSync(activityFile, 'utf-8'));
|
|
52
|
-
if (activity.totalAgents && totalAgents === 0)
|
|
53
|
-
totalAgents = activity.totalAgents;
|
|
54
|
-
if (activity.activeAgents && activeAgents === 0)
|
|
55
|
-
activeAgents = activity.activeAgents;
|
|
56
|
-
}
|
|
57
|
-
catch { /* ignore */ }
|
|
26
|
+
for (const agent of agents) {
|
|
27
|
+
totalAgents++;
|
|
28
|
+
const agType = agent.agentType || 'unknown';
|
|
29
|
+
if (!typeCounts[agType])
|
|
30
|
+
typeCounts[agType] = { count: 0, tasks: 0 };
|
|
31
|
+
typeCounts[agType].count++;
|
|
32
|
+
if (agent.status !== 'terminated')
|
|
33
|
+
activeAgents++;
|
|
34
|
+
// AgentRecord tracks a single taskCount, not a completed/failed
|
|
35
|
+
// breakdown — this is the best available proxy for "task activity",
|
|
36
|
+
// not literally "completed" tasks. Success-rate data doesn't exist
|
|
37
|
+
// anywhere in the current agent store schema, so it's reported as
|
|
38
|
+
// N/A below rather than fabricated.
|
|
39
|
+
const taskCount = agent.taskCount || 0;
|
|
40
|
+
typeCounts[agType].tasks += taskCount;
|
|
41
|
+
tasksCompleted += taskCount;
|
|
58
42
|
}
|
|
59
43
|
let vectorCount = 0;
|
|
60
44
|
try {
|
|
@@ -65,11 +49,9 @@ export const metricsCommand = {
|
|
|
65
49
|
catch { /* backend unavailable */ }
|
|
66
50
|
const byType = Object.entries(typeCounts).map(([type, data]) => ({
|
|
67
51
|
type, count: data.count, tasks: data.tasks,
|
|
68
|
-
successRate:
|
|
52
|
+
successRate: 'N/A', // not tracked in the current agent store schema
|
|
69
53
|
}));
|
|
70
|
-
const avgSuccessRate =
|
|
71
|
-
? `${Math.round(Object.values(typeCounts).reduce((a, d) => a + d.success, 0) / tasksCompleted * 100)}%`
|
|
72
|
-
: 'N/A';
|
|
54
|
+
const avgSuccessRate = 'N/A'; // not tracked in the current agent store schema
|
|
73
55
|
const metrics = {
|
|
74
56
|
summary: {
|
|
75
57
|
totalAgents, activeAgents, tasksCompleted, avgSuccessRate, vectorCount,
|
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
* github.com/monoes/monomind
|
|
6
6
|
*/
|
|
7
7
|
import type { Command } from '../types.js';
|
|
8
|
+
/** A single stale-scratch candidate returned by {@link findStaleScratch}. */
|
|
9
|
+
interface StaleScratchItem {
|
|
10
|
+
path: string;
|
|
11
|
+
description: string;
|
|
12
|
+
size: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Find stale mastermind scratch under `.monomind/taskdev/` and `.monomind/loops/`.
|
|
16
|
+
* Exported for tests. Never returns `progress.md` (the taskdev recovery ledger),
|
|
17
|
+
* directories, or loop JSON it cannot parse — deleting the unclassifiable loses data.
|
|
18
|
+
*
|
|
19
|
+
* A loop JSON is only ever classified as abandoned when it has a real, positive,
|
|
20
|
+
* numeric `nextRunAt` timestamp that is more than a day in the past. Live producers
|
|
21
|
+
* write `nextRunAt: 0` (real /do loops) or `nextRunAt: null` (dashboard) — those,
|
|
22
|
+
* along with missing/non-numeric values, are never eligible for deletion regardless
|
|
23
|
+
* of `status`, since a crashed daemon can leave a stale `status: 'running'` behind.
|
|
24
|
+
*/
|
|
25
|
+
export declare function findStaleScratch(cwd: string, now: number): StaleScratchItem[];
|
|
8
26
|
/**
|
|
9
27
|
* Cleanup command definition
|
|
10
28
|
*/
|
|
@@ -29,6 +29,94 @@ const KEEP_CONFIG_PATHS = [
|
|
|
29
29
|
'monomind.config.json',
|
|
30
30
|
join('.claude', 'settings.json'),
|
|
31
31
|
];
|
|
32
|
+
/** Scratch pruning (--scratch): taskdev handoff files and loop state. */
|
|
33
|
+
const SCRATCH_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // taskdev scratch older than this is stale
|
|
34
|
+
const LOOP_STALE_GRACE_MS = 24 * 60 * 60 * 1000; // a live loop reschedules every <=1h; overdue by a day = abandoned
|
|
35
|
+
/**
|
|
36
|
+
* Find stale mastermind scratch under `.monomind/taskdev/` and `.monomind/loops/`.
|
|
37
|
+
* Exported for tests. Never returns `progress.md` (the taskdev recovery ledger),
|
|
38
|
+
* directories, or loop JSON it cannot parse — deleting the unclassifiable loses data.
|
|
39
|
+
*
|
|
40
|
+
* A loop JSON is only ever classified as abandoned when it has a real, positive,
|
|
41
|
+
* numeric `nextRunAt` timestamp that is more than a day in the past. Live producers
|
|
42
|
+
* write `nextRunAt: 0` (real /do loops) or `nextRunAt: null` (dashboard) — those,
|
|
43
|
+
* along with missing/non-numeric values, are never eligible for deletion regardless
|
|
44
|
+
* of `status`, since a crashed daemon can leave a stale `status: 'running'` behind.
|
|
45
|
+
*/
|
|
46
|
+
export function findStaleScratch(cwd, now) {
|
|
47
|
+
const out = [];
|
|
48
|
+
const taskdevDir = join(cwd, '.monomind', 'taskdev');
|
|
49
|
+
if (existsSync(taskdevDir)) {
|
|
50
|
+
for (const f of readdirSync(taskdevDir)) {
|
|
51
|
+
if (f === 'progress.md')
|
|
52
|
+
continue; // the ledger is the recovery map — never auto-prune
|
|
53
|
+
try {
|
|
54
|
+
const st = lstatSync(join(taskdevDir, f));
|
|
55
|
+
if (st.isFile() && now - st.mtimeMs > SCRATCH_MAX_AGE_MS) {
|
|
56
|
+
out.push({ path: join('.monomind', 'taskdev', f), description: 'stale taskdev scratch', size: st.size });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch { /* raced away or unreadable — leave it */ }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const loopsDir = join(cwd, '.monomind', 'loops');
|
|
63
|
+
if (existsSync(loopsDir)) {
|
|
64
|
+
const entries = readdirSync(loopsDir);
|
|
65
|
+
const jsonStems = new Set(entries.filter(f => f.endsWith('.json')).map(f => f.replace(/\.json$/, '')));
|
|
66
|
+
for (const f of entries) {
|
|
67
|
+
try {
|
|
68
|
+
const st = lstatSync(join(loopsDir, f));
|
|
69
|
+
if (!st.isFile())
|
|
70
|
+
continue;
|
|
71
|
+
if (f.endsWith('.json')) {
|
|
72
|
+
const parsed = JSON.parse(readFileSync(join(loopsDir, f), 'utf8'));
|
|
73
|
+
const { nextRunAt } = parsed;
|
|
74
|
+
const isStale = typeof nextRunAt === 'number' &&
|
|
75
|
+
Number.isFinite(nextRunAt) &&
|
|
76
|
+
nextRunAt > 0 &&
|
|
77
|
+
now - nextRunAt > LOOP_STALE_GRACE_MS;
|
|
78
|
+
if (isStale) {
|
|
79
|
+
out.push({ path: join('.monomind', 'loops', f), description: 'abandoned loop state', size: st.size });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (f.endsWith('.stop') && !jsonStems.has(f.replace(/\.stop$/, ''))) {
|
|
83
|
+
out.push({ path: join('.monomind', 'loops', f), description: 'orphaned loop stopfile', size: st.size });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { /* unparseable or unreadable — never delete what we cannot classify */ }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Report (dry run) or delete a batch of file/dir items, printing the same
|
|
93
|
+
* `[would remove]` / `[removed]` / `[failed]` lines used across cleanup modes.
|
|
94
|
+
* Shared by the scratch branch; the main artifact loop keeps its own copy
|
|
95
|
+
* because of the `--keep-config` `.claude/settings.json` preservation special case.
|
|
96
|
+
*/
|
|
97
|
+
function removeOrReport(cwd, items, dryRun) {
|
|
98
|
+
let removed = 0;
|
|
99
|
+
let removedSize = 0;
|
|
100
|
+
for (const item of items) {
|
|
101
|
+
const sizeStr = formatSize(item.size);
|
|
102
|
+
const typeLabel = item.type === 'dir' ? 'dir ' : 'file';
|
|
103
|
+
if (dryRun) {
|
|
104
|
+
output.writeln(output.warning(` [would remove] ${typeLabel} ${item.path} (${sizeStr}) - ${item.description}`));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
try {
|
|
108
|
+
rmSync(join(cwd, item.path), { recursive: item.type === 'dir', force: true });
|
|
109
|
+
output.writeln(output.success(` [removed] ${typeLabel} ${item.path} (${sizeStr}) - ${item.description}`));
|
|
110
|
+
removed++;
|
|
111
|
+
removedSize += item.size;
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
output.writeln(output.error(` [failed] ${typeLabel} ${item.path} - ${err instanceof Error ? err.message : String(err)}`));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { removed, removedSize };
|
|
119
|
+
}
|
|
32
120
|
/**
|
|
33
121
|
* Maximum directory recursion depth for size calculation.
|
|
34
122
|
* Prevents stack overflow on deeply-nested or circular-symlink trees.
|
|
@@ -112,6 +200,13 @@ export const cleanupCommand = {
|
|
|
112
200
|
type: 'boolean',
|
|
113
201
|
default: false,
|
|
114
202
|
},
|
|
203
|
+
{
|
|
204
|
+
name: 'scratch',
|
|
205
|
+
short: 's',
|
|
206
|
+
description: 'Prune only stale mastermind scratch (.monomind/taskdev, abandoned .monomind/loops state)',
|
|
207
|
+
type: 'boolean',
|
|
208
|
+
default: false,
|
|
209
|
+
},
|
|
115
210
|
],
|
|
116
211
|
examples: [
|
|
117
212
|
{
|
|
@@ -126,12 +221,37 @@ export const cleanupCommand = {
|
|
|
126
221
|
command: 'cleanup --force --keep-config',
|
|
127
222
|
description: 'Remove artifacts but keep configuration files',
|
|
128
223
|
},
|
|
224
|
+
{
|
|
225
|
+
command: 'cleanup --scratch --force',
|
|
226
|
+
description: 'Delete stale taskdev scratch and abandoned loop state',
|
|
227
|
+
},
|
|
129
228
|
],
|
|
130
229
|
action: async (ctx) => {
|
|
131
230
|
const force = ctx.flags.force === true;
|
|
132
231
|
const keepConfig = ctx.flags['keep-config'] === true;
|
|
133
232
|
const cwd = ctx.cwd;
|
|
134
233
|
const dryRun = !force;
|
|
234
|
+
if (ctx.flags.scratch === true) {
|
|
235
|
+
const now = Date.now();
|
|
236
|
+
output.writeln();
|
|
237
|
+
output.writeln(output.bold(dryRun ? 'Monomind Scratch Cleanup (dry run)' : 'Monomind Scratch Cleanup'));
|
|
238
|
+
output.writeln();
|
|
239
|
+
const stale = findStaleScratch(cwd, now);
|
|
240
|
+
if (stale.length === 0) {
|
|
241
|
+
output.writeln(output.info('No stale scratch found.'));
|
|
242
|
+
return { success: true, message: 'Nothing to clean' };
|
|
243
|
+
}
|
|
244
|
+
const { removed, removedSize } = removeOrReport(cwd, stale.map(item => ({ ...item, type: 'file' })), dryRun);
|
|
245
|
+
output.writeln();
|
|
246
|
+
if (dryRun) {
|
|
247
|
+
output.writeln(output.dim(` ${stale.length} stale file(s). This was a dry run. Use --force to delete.`));
|
|
248
|
+
output.writeln();
|
|
249
|
+
return { success: true, message: `Dry run: ${stale.length} stale scratch file(s) found`, data: { found: stale, dryRun } };
|
|
250
|
+
}
|
|
251
|
+
output.writeln(` Removed ${removed} file(s) totaling ${formatSize(removedSize)}`);
|
|
252
|
+
output.writeln();
|
|
253
|
+
return { success: true, message: `Removed ${removed} stale scratch file(s)`, data: { found: stale, removedCount: removed, removedSize, dryRun } };
|
|
254
|
+
}
|
|
135
255
|
output.writeln();
|
|
136
256
|
output.writeln(output.bold(dryRun
|
|
137
257
|
? 'Monomind Cleanup (dry run)'
|
|
@@ -302,7 +302,10 @@ export async function fixStaleHelpers() {
|
|
|
302
302
|
copyFileSync(bundled, local);
|
|
303
303
|
fixed++;
|
|
304
304
|
}
|
|
305
|
-
catch {
|
|
305
|
+
catch (e) {
|
|
306
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
307
|
+
console.error(`[fixStaleHelpers] failed to copy ${name}:`, e);
|
|
308
|
+
}
|
|
306
309
|
}
|
|
307
310
|
}
|
|
308
311
|
return fixed > 0;
|
|
@@ -337,7 +340,9 @@ async function routingAccuracyLine() {
|
|
|
337
340
|
const trend = acc.recentVsPrior === null ? '' : ` trend ${acc.recentVsPrior >= 0 ? '+' : ''}${Math.round(acc.recentVsPrior * 100)}%`;
|
|
338
341
|
return `routing accuracy (last ${acc.window}): ${fmtPct(acc.accuracy)} [native ${fmtPct(acc.byMode.native)} / js ${fmtPct(acc.byMode.js)}]${trend}${adhStr}`;
|
|
339
342
|
}
|
|
340
|
-
catch {
|
|
343
|
+
catch (e) {
|
|
344
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
345
|
+
console.error('[routingAccuracyLine] compute failed:', e);
|
|
341
346
|
return 'routing accuracy (last 100): no outcome data yet';
|
|
342
347
|
}
|
|
343
348
|
}
|
|
@@ -374,7 +379,9 @@ function routingFeedbackFallback() {
|
|
|
374
379
|
return { message: `${base}; no evidence-based success flags yet`, degenerate: true };
|
|
375
380
|
return { message: `${base}; success rate ${Math.round((trueCount / flags.length) * 100)}% (n=${flags.length})`, degenerate: false };
|
|
376
381
|
}
|
|
377
|
-
catch {
|
|
382
|
+
catch (e) {
|
|
383
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
384
|
+
console.error('[routingFeedbackFallback] parse failed:', e);
|
|
378
385
|
return null;
|
|
379
386
|
}
|
|
380
387
|
}
|
|
@@ -511,7 +518,9 @@ function readMetricsJSON(name) {
|
|
|
511
518
|
return null;
|
|
512
519
|
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
513
520
|
}
|
|
514
|
-
catch {
|
|
521
|
+
catch (e) {
|
|
522
|
+
if (process.env.DEBUG || process.env.MONOMIND_DEBUG)
|
|
523
|
+
console.error(`[readMetricsJSON] failed to read ${name}:`, e);
|
|
515
524
|
return null;
|
|
516
525
|
}
|
|
517
526
|
}
|