@monoes/monomindcli 1.10.30 → 1.10.32

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 (63) hide show
  1. package/.claude/commands/browse.md +6 -17
  2. package/.claude/helpers/handlers/adr-draft-handler.cjs +64 -0
  3. package/.claude/helpers/handlers/agent-start-handler.cjs +99 -0
  4. package/.claude/helpers/handlers/budget-status-handler.cjs +14 -0
  5. package/.claude/helpers/handlers/compact-handler.cjs +33 -0
  6. package/.claude/helpers/handlers/graph-status-handler.cjs +38 -0
  7. package/.claude/helpers/handlers/loops-status-handler.cjs +45 -0
  8. package/.claude/helpers/handlers/session-restore-handler.cjs +66 -55
  9. package/.claude/helpers/handlers/stats-handler.cjs +14 -0
  10. package/.claude/helpers/hook-handler.cjs +16 -228
  11. package/.claude/skills/agent-browser-testing/SKILL.md +149 -151
  12. package/.claude/skills/monomind/browse-agentcore.md +20 -21
  13. package/.claude/skills/monomind/browse-electron.md +45 -46
  14. package/.claude/skills/monomind/browse-qa.md +29 -30
  15. package/.claude/skills/monomind/browse-references/authentication.md +39 -40
  16. package/.claude/skills/monomind/browse-references/trust-boundaries.md +1 -2
  17. package/.claude/skills/monomind/browse-references/video-recording.md +23 -24
  18. package/.claude/skills/monomind/browse-slack.md +52 -53
  19. package/.claude/skills/monomind/browse-vercel.md +26 -27
  20. package/.claude/skills/monomind/browse.md +273 -273
  21. package/dist/src/browser/actions.d.ts +15 -0
  22. package/dist/src/browser/actions.d.ts.map +1 -1
  23. package/dist/src/browser/actions.js +91 -0
  24. package/dist/src/browser/actions.js.map +1 -1
  25. package/dist/src/browser/batch.d.ts +13 -0
  26. package/dist/src/browser/batch.d.ts.map +1 -0
  27. package/dist/src/browser/batch.js +11 -0
  28. package/dist/src/browser/batch.js.map +1 -0
  29. package/dist/src/browser/console-log.d.ts +22 -0
  30. package/dist/src/browser/console-log.d.ts.map +1 -0
  31. package/dist/src/browser/console-log.js +55 -0
  32. package/dist/src/browser/console-log.js.map +1 -0
  33. package/dist/src/browser/dialog.d.ts +11 -0
  34. package/dist/src/browser/dialog.d.ts.map +1 -0
  35. package/dist/src/browser/dialog.js +36 -0
  36. package/dist/src/browser/dialog.js.map +1 -0
  37. package/dist/src/browser/emulation.d.ts +15 -0
  38. package/dist/src/browser/emulation.d.ts.map +1 -0
  39. package/dist/src/browser/emulation.js +62 -0
  40. package/dist/src/browser/emulation.js.map +1 -0
  41. package/dist/src/browser/find.d.ts +21 -0
  42. package/dist/src/browser/find.d.ts.map +1 -0
  43. package/dist/src/browser/find.js +118 -0
  44. package/dist/src/browser/find.js.map +1 -0
  45. package/dist/src/browser/index.d.ts +7 -0
  46. package/dist/src/browser/index.d.ts.map +1 -1
  47. package/dist/src/browser/index.js +7 -0
  48. package/dist/src/browser/index.js.map +1 -1
  49. package/dist/src/browser/pdf.d.ts +15 -0
  50. package/dist/src/browser/pdf.d.ts.map +1 -0
  51. package/dist/src/browser/pdf.js +27 -0
  52. package/dist/src/browser/pdf.js.map +1 -0
  53. package/dist/src/browser/storage.d.ts +11 -0
  54. package/dist/src/browser/storage.d.ts.map +1 -0
  55. package/dist/src/browser/storage.js +43 -0
  56. package/dist/src/browser/storage.js.map +1 -0
  57. package/dist/src/commands/browse.d.ts.map +1 -1
  58. package/dist/src/commands/browse.js +939 -18
  59. package/dist/src/commands/browse.js.map +1 -1
  60. package/dist/src/ui/dashboard-v2.html +581 -19
  61. package/dist/src/ui/server.mjs +56 -0
  62. package/dist/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +1 -1
@@ -1,32 +1,21 @@
1
1
  ---
2
2
  name: browse
3
- description: Launch agent-browser for web UI testing and automation — installs if missing, then runs the agent-browser-testing skill
3
+ description: Launch monomind browse for web UI testing and automation — native TypeScript CDP client, no external binary needed
4
4
  ---
5
5
 
6
6
  Invoke the agent-browser-testing skill to test or automate a web UI.
7
7
 
8
- Run this shell command immediately without asking for confirmation:
9
-
10
- ```bash
11
- if ! command -v agent-browser &>/dev/null; then
12
- echo "Installing agent-browser..."
13
- npm install -g agent-browser
14
- else
15
- echo "agent-browser $(agent-browser --version) ready"
16
- fi
17
- ```
18
-
19
- After confirming agent-browser is available, invoke the skill:
8
+ `monomind browse` is built-in no install needed. Invoke the skill:
20
9
 
21
10
  Skill("agent-browser-testing")
22
11
 
23
12
  Pass the user's argument (if any) as the URL or task description: $1
24
13
 
25
- Use the full agent-browser workflow:
26
- 1. `agent-browser open <url>`
27
- 2. `agent-browser snapshot -i`
14
+ Use the full monomind browse workflow:
15
+ 1. `npx monomind browse open <url>`
16
+ 2. `npx monomind browse snapshot -i`
28
17
  3. Act using element refs (@e1, @e2, ...)
29
18
  4. Re-snapshot to verify
30
- 5. Report results (PASS / FAIL / ⚠️ WARN)
19
+ 5. Report results (PASS / FAIL / WARN)
31
20
 
32
21
  Examples: `/browse`, `/browse https://example.com`, `/browse test the login flow`
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+ // Extracted from hook-handler.cjs — handles 'adr-draft' command.
3
+ // Drafts an ADR from accumulated decision markers in .monomind/decisions.jsonl.
4
+ // Receives hCtx from dispatcher. See route-handler.cjs for hCtx field docs.
5
+
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+
9
+ module.exports = {
10
+ handle: function(hCtx) {
11
+ var CWD = hCtx.CWD;
12
+
13
+ var jsonl = path.join(CWD, '.monomind', 'decisions.jsonl');
14
+ if (!fs.existsSync(jsonl)) {
15
+ console.log('[ADR] No decisions recorded yet. Type prompts containing markers like "let\'s go with X", "we chose Y", "decision: Z" to populate the log.');
16
+ return;
17
+ }
18
+ var lines = fs.readFileSync(jsonl, 'utf-8').trim().split('\n').filter(Boolean);
19
+ if (lines.length === 0) {
20
+ console.log('[ADR] decisions.jsonl is empty.');
21
+ return;
22
+ }
23
+ // Group decisions captured in the last 7 days.
24
+ var cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
25
+ var recent = lines
26
+ .map(function(l) { try { return JSON.parse(l); } catch (_) { return null; } })
27
+ .filter(function(d) { return d && d.ts >= cutoff; });
28
+ if (recent.length === 0) {
29
+ console.log('[ADR] No decisions in the last 7 days. Older entries: ' + lines.length + '.');
30
+ return;
31
+ }
32
+
33
+ var adrsDir = path.join(CWD, 'docs', 'adrs');
34
+ try { fs.mkdirSync(adrsDir, { recursive: true }); } catch (_) {}
35
+ var existing = [];
36
+ try { existing = fs.readdirSync(adrsDir).filter(function(f) { return /^ADR-\d{4}/.test(f); }); } catch (_) {}
37
+ var nextNum = existing.length + 1;
38
+ var num = String(nextNum).padStart(4, '0');
39
+ var stamp = new Date().toISOString().slice(0, 10);
40
+ var fname = 'ADR-' + num + '-' + stamp + '-session-decisions.md';
41
+ var outPath = path.join(adrsDir, fname);
42
+
43
+ var body = '# ADR-' + num + ': Session decisions (' + stamp + ')\n\n' +
44
+ '**Status:** Proposed\n**Date:** ' + stamp + '\n\n' +
45
+ '## Context\n\n' +
46
+ 'During recent sessions, the following decision markers were captured ' +
47
+ 'from user prompts. Each excerpt is the surrounding sentence at the time.\n\n' +
48
+ '## Decisions\n\n';
49
+ for (var i = 0; i < recent.length; i++) {
50
+ var d = recent[i];
51
+ var date = new Date(d.ts).toISOString().slice(0, 16).replace('T', ' ');
52
+ body += '### ' + (i + 1) + '. ' + date + '\n\n';
53
+ for (var j = 0; j < d.excerpts.length; j++) {
54
+ body += '> ' + d.excerpts[j].trim() + '\n\n';
55
+ }
56
+ if (d.prompt) body += '_Prompt:_ ' + d.prompt.slice(0, 200) + (d.prompt.length > 200 ? '…' : '') + '\n\n';
57
+ }
58
+ body += '## Consequences\n\n_(fill in after review)_\n\n' +
59
+ '## Status\n\nProposed — awaiting human review and refinement.\n';
60
+ fs.writeFileSync(outPath, body);
61
+ console.log('[ADR_DRAFT] Wrote ' + recent.length + ' decision(s) to ' + outPath);
62
+ console.log(' Edit the file to fill in Context and Consequences, then change Status to Accepted/Rejected.');
63
+ },
64
+ };
@@ -0,0 +1,99 @@
1
+ 'use strict';
2
+ // Extracted from hook-handler.cjs — handles 'agent-start' (SubagentStart) hook event.
3
+ // Receives hCtx from dispatcher. See route-handler.cjs for hCtx field docs.
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+
8
+ module.exports = {
9
+ handle: function(hCtx) {
10
+ var hookInput = hCtx.hookInput;
11
+ var CWD = hCtx.CWD;
12
+ var _openMonographDb = hCtx._openMonographDb;
13
+ var getMonographSuggestions = hCtx.getMonographSuggestions;
14
+
15
+ // Register this agent so the statusline can count active agents.
16
+ const regDir = path.join(CWD, '.monomind', 'agents', 'registrations');
17
+ try {
18
+ fs.mkdirSync(regDir, { recursive: true });
19
+ const id = Date.now() + '-' + Math.random().toString(36).slice(2, 6);
20
+ const regFile = path.join(regDir, 'agent-' + id + '.json');
21
+ fs.writeFileSync(regFile, JSON.stringify({
22
+ agentId: id,
23
+ startedAt: new Date().toISOString(),
24
+ pid: process.pid,
25
+ }));
26
+ // Refresh swarm-activity.json within the 5-min staleness window.
27
+ const activityDir = path.join(CWD, '.monomind', 'metrics');
28
+ fs.mkdirSync(activityDir, { recursive: true });
29
+ const activityPath = path.join(activityDir, 'swarm-activity.json');
30
+ const active = fs.readdirSync(regDir).filter(f => f.endsWith('.json')).length;
31
+ // Preserve lastActive (peak) so statusline shows non-zero after completion.
32
+ let prevLastActive = 0;
33
+ try { prevLastActive = (JSON.parse(fs.readFileSync(activityPath, 'utf-8'))?.swarm?.lastActive) || 0; } catch { /* ignore */ }
34
+ fs.writeFileSync(activityPath, JSON.stringify({
35
+ timestamp: new Date().toISOString(),
36
+ swarm: {
37
+ active: active > 0,
38
+ agent_count: active,
39
+ coordination_active: active > 0,
40
+ lastActive: Math.max(active, prevLastActive),
41
+ },
42
+ }));
43
+
44
+ // Write last-dispatch.json so the route handler can suppress redundant
45
+ // suggestions on the next turn when the same agent type is recommended.
46
+ const agentType = hookInput.subagent_type || hookInput.agentType || hookInput.agent_type || hookInput.agentSlug || 'unknown';
47
+ const agentDesc = hookInput.description || hookInput.prompt_description || '';
48
+ fs.writeFileSync(
49
+ path.join(CWD, '.monomind', 'last-dispatch.json'),
50
+ JSON.stringify({
51
+ agentType: agentType,
52
+ description: agentDesc.substring(0, 120),
53
+ dispatchedAt: new Date().toISOString(),
54
+ }),
55
+ 'utf-8'
56
+ );
57
+ } catch (e) { /* non-fatal — never block a subagent from starting */ }
58
+
59
+ // Subagent context inheritance — inject graph god nodes + parent's last
60
+ // pre-resolved suggestions so the spawned agent inherits spatial map.
61
+ try {
62
+ var subDb = _openMonographDb();
63
+ if (subDb) {
64
+ try {
65
+ var godRows = subDb.prepare(
66
+ "SELECT n.name, n.label, n.file_path AS file, " +
67
+ "(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg " +
68
+ "FROM nodes n " +
69
+ "WHERE n.label NOT IN ('Concept') AND n.file_path IS NOT NULL AND n.file_path != '' " +
70
+ "ORDER BY deg DESC LIMIT 5"
71
+ ).all();
72
+ if (godRows.length > 0) {
73
+ console.log('[MONOGRAPH_SUBAGENT_CTX] Graph map inherited from parent:');
74
+ for (var gi = 0; gi < godRows.length; gi++) {
75
+ var gr = godRows[gi];
76
+ console.log(' · ' + gr.name + ' [' + gr.label + '] — ' + (gr.file || '') + ' (deg ' + gr.deg + ')');
77
+ }
78
+ try {
79
+ var subAgentDesc = hookInput.description || hookInput.prompt_description || '';
80
+ if (subAgentDesc && subAgentDesc.length > 8) {
81
+ var subHints = getMonographSuggestions(subAgentDesc, 3);
82
+ if (subHints.length > 0) {
83
+ console.log(' Top files for this subagent task:');
84
+ for (var si2 = 0; si2 < subHints.length; si2++) {
85
+ var sh = subHints[si2];
86
+ console.log(' · ' + sh.name + ' [' + sh.label + '] — ' + (sh.file || ''));
87
+ }
88
+ }
89
+ }
90
+ } catch (_) {}
91
+ console.log(' Use mcp__monomind__monograph_suggest / monograph_query in this subagent before grepping.');
92
+ }
93
+ } catch (e) { /* non-fatal */ }
94
+ }
95
+ } catch (e) { /* non-fatal */ }
96
+
97
+ console.log('[OK] Agent registered');
98
+ },
99
+ };
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+ // Extracted from hook-handler.cjs — handles 'budget-status' command.
3
+
4
+ module.exports = {
5
+ handle: function(hCtx) {
6
+ var _getBudgetStatus = hCtx._getBudgetStatus;
7
+ var b = _getBudgetStatus();
8
+ if (!b) { console.log('No budget data yet — token tracking not initialized.'); return; }
9
+ console.log('Today: $' + b.todayCost.toFixed(2) + ' / $' + b.dailyLimit + ' (' + b.dailyPct + '%)' + (b.autoTuned ? ' [auto-tuned]' : ''));
10
+ console.log('Month: $' + b.monthCost.toFixed(2) + ' / $' + b.monthlyLimit + ' (' + b.monthlyPct + '%)');
11
+ console.log('Status: ' + (b.breached ? 'BREACHED' : b.spike ? 'SPIKE' : b.alert ? 'ALERT' : 'OK'));
12
+ console.log('Edit .monomind/budget.json to adjust. Delete to re-tune.');
13
+ },
14
+ };
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+ // Extracted from hook-handler.cjs — handles 'compact-manual' and 'compact-auto' events.
3
+ // Receives hCtx and mode ('manual'|'auto') from dispatcher.
4
+
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+
8
+ module.exports = {
9
+ handle: async function(hCtx, mode) {
10
+ var intelligence = hCtx.intelligence;
11
+ var runWithTimeout = hCtx.runWithTimeout;
12
+ var _injectCompactGraphMap = hCtx._injectCompactGraphMap;
13
+ var CWD = hCtx.CWD;
14
+
15
+ if (intelligence && intelligence.consolidate) {
16
+ try { await runWithTimeout(function() { return intelligence.consolidate(); }, 'intelligence.consolidate()'); } catch (e) { /* non-fatal */ }
17
+ }
18
+ try {
19
+ var lastRoute = path.join(CWD, '.monomind', 'last-route.json');
20
+ if (fs.existsSync(lastRoute)) {
21
+ var route = JSON.parse(fs.readFileSync(lastRoute, 'utf-8'));
22
+ console.log('[COMPACT_CONTEXT] Last route: ' + route.agent + ' (' + (route.confidence != null ? (route.confidence * 100).toFixed(0) : '?') + '%)');
23
+ }
24
+ } catch (e) { /* non-fatal */ }
25
+ _injectCompactGraphMap();
26
+ if (mode === 'auto') {
27
+ console.log('[COMPACT] Auto compaction — intelligence consolidated, context preserved');
28
+ console.log('GOLDEN RULE: 1 message = all parallel operations');
29
+ } else {
30
+ console.log('[COMPACT] Manual compaction — intelligence consolidated, context preserved');
31
+ }
32
+ },
33
+ };
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+ // Extracted from hook-handler.cjs — handles 'graph-status' command.
3
+ // Shows monograph node/edge counts and graph-vs-grep usage ratio.
4
+ // Receives hCtx from dispatcher. See route-handler.cjs for hCtx field docs.
5
+
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+
9
+ module.exports = {
10
+ handle: function(hCtx) {
11
+ var CWD = hCtx.CWD;
12
+ var _openMonographDb = hCtx._openMonographDb;
13
+
14
+ var db = _openMonographDb();
15
+ if (!db) {
16
+ console.log('No monograph.db found. Run /monomind:understand to build.');
17
+ return;
18
+ }
19
+ try {
20
+ var n = db.prepare("SELECT COUNT(*) AS c FROM nodes").get().c;
21
+ var e = db.prepare("SELECT COUNT(*) AS c FROM edges").get().c;
22
+ var usage = (function() {
23
+ try { return JSON.parse(fs.readFileSync(path.join(CWD, '.monomind', 'metrics', 'graph-usage.json'), 'utf-8')); }
24
+ catch (_) { return {}; }
25
+ })();
26
+ var wins = (usage.monograph_call || 0) + (usage.preresolve_hit || 0)
27
+ + (usage.graph_assist_search || 0) + (usage.graph_assist_neighbors || 0);
28
+ var search = (usage.grep_call || 0) + (usage.glob_call || 0)
29
+ + (usage.bash_grep_call || 0) + (usage.bash_find_call || 0);
30
+ var pct = (wins + search) > 0 ? Math.round((wins / (wins + search)) * 100) : 0;
31
+ var saved = usage.dollars_saved || 0;
32
+ console.log('Monograph: ' + n.toLocaleString() + ' nodes · ' + e.toLocaleString() + ' edges');
33
+ console.log('Usage: ' + pct + '% graph · ' + (100 - pct) + '% grep · ' +
34
+ 'wins=' + wins + ' search=' + search +
35
+ (saved > 0 ? ' · saved $' + saved.toFixed(2) : ''));
36
+ } catch (err) { console.log('Error: ' + err.message); }
37
+ },
38
+ };
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+ // Extracted from hook-handler.cjs — handles 'loops-status' command.
3
+
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ module.exports = {
8
+ handle: function(hCtx) {
9
+ var CWD = hCtx.CWD;
10
+ var loopsDir = path.join(CWD, '.monomind', 'loops');
11
+ if (!fs.existsSync(loopsDir)) { console.log('No loops directory.'); return; }
12
+ var files = fs.readdirSync(loopsDir).filter(function(f) {
13
+ return f.endsWith('.json') && !f.includes('-hil') && !f.endsWith('.stop');
14
+ });
15
+ var STALE_MS = 6 * 60 * 60 * 1000;
16
+ var now = Date.now();
17
+ var active = [], stale = [];
18
+ files.forEach(function(f) {
19
+ try {
20
+ var d = JSON.parse(fs.readFileSync(path.join(loopsDir, f), 'utf-8'));
21
+ var last = d.lastRunAt || d.startedAt || 0;
22
+ var ageMs = last ? (now - last) : Infinity;
23
+ if (ageMs > STALE_MS) stale.push({ d: d, ageH: Math.round(ageMs / 3600000) });
24
+ else active.push(d);
25
+ } catch (_) {}
26
+ });
27
+ if (active.length === 0 && stale.length === 0) {
28
+ console.log('No loops.'); return;
29
+ }
30
+ if (active.length > 0) {
31
+ console.log('Active (' + active.length + '):');
32
+ active.forEach(function(d) {
33
+ console.log(' · ' + (d.command || '?') + ' [' + (d.type || '?') + '] run ' + (d.currentRep || 0) +
34
+ (d.maxReps ? '/' + d.maxReps : '') + ' · ' + (d.status || '?'));
35
+ });
36
+ }
37
+ if (stale.length > 0) {
38
+ console.log('Stale (' + stale.length + ' >6h):');
39
+ stale.forEach(function(s) {
40
+ console.log(' · ' + (s.d.command || '?') + ' run ' + (s.d.currentRep || 0) +
41
+ ' · ' + s.ageH + 'h ago · ' + (s.d.status || '?'));
42
+ });
43
+ }
44
+ },
45
+ };
@@ -31,50 +31,55 @@ module.exports = {
31
31
  } catch (e) { console.log('[WARN] Session restore failed: ' + e.message); }
32
32
 
33
33
  // Stale helper detection — warn when project helpers drift from the bundled npm copy.
34
+ // Skip when running inside the monomind dev repo itself: local helpers ARE the
35
+ // source of truth there, so any diff vs. the npm global install is expected.
34
36
  try {
35
- var crypto = require('crypto');
36
- function _findBundledHelpers() {
37
- var helperPaths = [
38
- path.join(helpersDir),
39
- path.join(CWD, 'node_modules', 'monomind', '.claude', 'helpers'),
40
- path.join(CWD, 'node_modules', '@monoes', 'monomindcli', '.claude', 'helpers'),
41
- ];
42
- try {
43
- var globalRoot = require('child_process')
44
- .execSync('npm root -g 2>/dev/null', { encoding: 'utf-8', timeout: 2000 })
45
- .trim();
46
- if (globalRoot) {
47
- helperPaths.push(path.join(globalRoot, 'monomind', '.claude', 'helpers'));
48
- helperPaths.push(path.join(globalRoot, '@monoes', 'monomindcli', '.claude', 'helpers'));
49
- }
50
- } catch (_) {}
51
- for (var i = 0; i < helperPaths.length; i++) {
52
- if (fs.existsSync(path.join(helperPaths[i], 'hook-handler.cjs')) &&
53
- helperPaths[i] !== path.join(CWD, '.claude', 'helpers')) {
54
- return helperPaths[i];
55
- }
56
- }
57
- return null;
58
- }
59
-
60
- var bundledDir = _findBundledHelpers();
61
- if (bundledDir) {
62
- var helpersToCheck = ['hook-handler.cjs', 'statusline.cjs'];
63
- var stale = [];
64
- for (var hi = 0; hi < helpersToCheck.length; hi++) {
65
- var hName = helpersToCheck[hi];
66
- var localF = path.join(CWD, '.claude', 'helpers', hName);
67
- var bundledF = path.join(bundledDir, hName);
68
- if (!fs.existsSync(localF) || !fs.existsSync(bundledF)) continue;
37
+ var _isDevRepo = fs.existsSync(path.join(CWD, 'packages', '@monomind', 'cli', 'package.json'));
38
+ if (!_isDevRepo) {
39
+ var crypto = require('crypto');
40
+ function _findBundledHelpers() {
41
+ var helperPaths = [
42
+ path.join(helpersDir),
43
+ path.join(CWD, 'node_modules', 'monomind', '.claude', 'helpers'),
44
+ path.join(CWD, 'node_modules', '@monoes', 'monomindcli', '.claude', 'helpers'),
45
+ ];
69
46
  try {
70
- var hashL = crypto.createHash('sha256').update(fs.readFileSync(localF)).digest('hex');
71
- var hashB = crypto.createHash('sha256').update(fs.readFileSync(bundledF)).digest('hex');
72
- if (hashL !== hashB) stale.push(hName);
47
+ var globalRoot = require('child_process')
48
+ .execSync('npm root -g 2>/dev/null', { encoding: 'utf-8', timeout: 2000 })
49
+ .trim();
50
+ if (globalRoot) {
51
+ helperPaths.push(path.join(globalRoot, 'monomind', '.claude', 'helpers'));
52
+ helperPaths.push(path.join(globalRoot, '@monoes', 'monomindcli', '.claude', 'helpers'));
53
+ }
73
54
  } catch (_) {}
55
+ for (var i = 0; i < helperPaths.length; i++) {
56
+ if (fs.existsSync(path.join(helperPaths[i], 'hook-handler.cjs')) &&
57
+ helperPaths[i] !== path.join(CWD, '.claude', 'helpers')) {
58
+ return helperPaths[i];
59
+ }
60
+ }
61
+ return null;
74
62
  }
75
- if (stale.length > 0) {
76
- console.log('[STALE_HELPERS] Project helpers differ from bundled version: ' + stale.join(', '));
77
- console.log(' Run `npx monomind@latest init upgrade` to refresh and pick up the latest features.');
63
+
64
+ var bundledDir = _findBundledHelpers();
65
+ if (bundledDir) {
66
+ var helpersToCheck = ['hook-handler.cjs', 'statusline.cjs'];
67
+ var stale = [];
68
+ for (var hi = 0; hi < helpersToCheck.length; hi++) {
69
+ var hName = helpersToCheck[hi];
70
+ var localF = path.join(CWD, '.claude', 'helpers', hName);
71
+ var bundledF = path.join(bundledDir, hName);
72
+ if (!fs.existsSync(localF) || !fs.existsSync(bundledF)) continue;
73
+ try {
74
+ var hashL = crypto.createHash('sha256').update(fs.readFileSync(localF)).digest('hex');
75
+ var hashB = crypto.createHash('sha256').update(fs.readFileSync(bundledF)).digest('hex');
76
+ if (hashL !== hashB) stale.push(hName);
77
+ } catch (_) {}
78
+ }
79
+ if (stale.length > 0) {
80
+ console.log('[STALE_HELPERS] Project helpers differ from bundled version: ' + stale.join(', '));
81
+ console.log(' Run `npx monomind@latest init upgrade` to refresh and pick up the latest features.');
82
+ }
78
83
  }
79
84
  }
80
85
  } catch (e) { /* non-fatal */ }
@@ -313,21 +318,27 @@ module.exports = {
313
318
  }
314
319
  } catch (e) { /* non-fatal */ }
315
320
 
316
- // Monomind Control UI Status.
317
- try {
318
- var http = require('http');
319
- var controlPort = 4242;
320
- var req = http.get('http://localhost:' + controlPort + '/', function(res) {
321
- if (res.statusCode === 200) {
322
- console.log('[CONTROL_UI] UP — http://localhost:' + controlPort);
323
- }
324
- res.resume();
325
- });
326
- req.on('error', function() {
327
- console.log('[CONTROL_UI] offlinerun: npx monomind mcp start');
328
- });
329
- req.setTimeout(800, function() { req.destroy(); });
330
- } catch (e) { /* non-fatal */ }
321
+ // Monomind Control UI Status — only probe when a daemon has previously run
322
+ // (indicated by daemon.pid or monomind.config.json). Skips silently in fresh
323
+ // environments and test fixtures that have no daemon history.
324
+ var _controlUiShouldProbe = fs.existsSync(path.join(CWD, '.monomind', 'daemon.pid'))
325
+ || fs.existsSync(path.join(CWD, 'monomind.config.json'));
326
+ if (_controlUiShouldProbe) {
327
+ try {
328
+ var http = require('http');
329
+ var controlPort = 4242;
330
+ var req = http.get('http://localhost:' + controlPort + '/', function(res) {
331
+ if (res.statusCode === 200) {
332
+ console.log('[CONTROL_UI] UPhttp://localhost:' + controlPort);
333
+ }
334
+ res.resume();
335
+ });
336
+ req.on('error', function() {
337
+ console.log('[CONTROL_UI] offline — run: npx monomind mcp start');
338
+ });
339
+ req.setTimeout(800, function() { req.destroy(); });
340
+ } catch (e) { /* non-fatal */ }
341
+ }
331
342
 
332
343
  // Worker Queue Resume (SR-003).
333
344
  try {
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+ // Extracted from hook-handler.cjs — handles 'stats' command.
3
+
4
+ module.exports = {
5
+ handle: async function(hCtx) {
6
+ var intelligence = hCtx.intelligence;
7
+ var args = hCtx.args;
8
+ if (intelligence && intelligence.stats) {
9
+ await Promise.resolve(intelligence.stats(args.includes('--json')));
10
+ } else {
11
+ console.log('[WARN] Intelligence module not available. Run session-restore first.');
12
+ }
13
+ },
14
+ };