@monoes/monomindcli 1.6.9 → 1.7.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.
@@ -267,112 +267,43 @@ Also move any `skipElaboration: true` ideas directly to `Approved`.
267
267
 
268
268
  ## Step 7: Task Decomposer — Break Improvements into Subtasks
269
269
 
270
- ### Task Board Setup
271
- - Check if a `monomind-task` board exists in the space (same lookup method as Step 3).
272
- - If not, create it with these columns:
273
- - `Backlog`
274
- - `Todo`
275
- - `In Progress`
276
- - `Review`
277
- - `Human in Loop`
278
- - `Done`
279
- - Store column IDs.
280
-
281
- ### Decomposition into Professional Task Cards
270
+ ### Generate the TASKS Array
282
271
 
283
272
  Spawn a single `Software Architect` agent via the Agent tool. Provide it with:
284
273
  - All ideas in the `Approved` column (titles, descriptions, and all comments including implementation paths and research)
285
274
  - The `COMPONENT_ANALYSIS` from Step 2
286
275
  - The `PROJECT_CONTEXT`
276
+ - **The Task Grouping Rules and Card Format from `monomind-task-engine` skill (Sections 1 & 2)** — include them verbatim in the agent prompt so it produces correctly structured tasks
287
277
 
288
- For each approved improvement, the agent must:
278
+ The agent MUST produce a `TASKS` array following the `monomind-task-engine` card format (Section 2). Each task must comply with all 7 grouping rules (Section 1). For each approved improvement, decompose into 2-6 subtasks.
289
279
 
290
- 1. **Analyze and decompose** into 2-6 subtasks. Each subtask must be a professional task card:
291
-
292
- ```json
293
- {
294
- "title": "Action-oriented title (verb + noun + context)",
295
- "description": "## What\nExact deliverable (new file, modified function, endpoint, etc.).\n\n## Why\nBusiness or technical motivation — what breaks without this?\n\n## Where\nFile paths, module boundaries, related components.\n\n## Patterns\nExisting conventions to follow (naming, error handling, test style).",
296
- "definition_of_done": [
297
- "Specific, binary, verifiable condition (include HTTP codes, error shapes, edge cases)",
298
- "Quantified thresholds where applicable (rate limits, timeouts, sizes)"
299
- ],
300
- "testing_criteria": {
301
- "unit_tests": ["function(input) → expected outcome"],
302
- "integration_tests": ["endpoint + method → status + response shape"],
303
- "edge_cases": ["boundary condition → expected behavior"]
304
- },
305
- "checklist": [
306
- "Write failing test for [specific behavior]",
307
- "Implement [function/class] in [file path]",
308
- "Run tests — verify green",
309
- "Commit: '[type]: [description]'"
310
- ],
311
- "agent_type": "best-fit agent from 230+ roster",
312
- "priority": "critical | high | medium | low",
313
- "effort": "1-10 (1=trivial, 10=full day)",
314
- "dependencies": ["titles of prerequisite tasks, or empty"]
315
- }
316
- ```
317
-
318
- **Task generation rules:**
319
- - Tasks MUST be ordered so dependencies come first
320
- - Each task: 5-30 minutes for a single agent
321
- - Split anything larger
322
- - Every task starts with writing a test (TDD)
323
- - DOD items must be binary (pass/fail, not "looks good")
324
- - Testing criteria must name specific functions, endpoints, inputs
325
-
326
- 2. **Create each subtask** as a card in `Backlog` (has deps) or `Todo` (no deps):
327
- ```bash
328
- monotask card create $TASK_BOARD_ID $COLUMN_ID "<title>" --json
329
- monotask card tag add $TASK_BOARD_ID $CARD_ID "monomind-improve"
330
- ```
331
-
332
- 3. **Set description** with full context block:
333
- ```bash
334
- monotask card set-description $TASK_BOARD_ID $CARD_ID "<description with What/Why/Where/Patterns>"
335
- ```
336
-
337
- 4. **Add DOD comment**:
338
- ```bash
339
- monotask card comment add $TASK_BOARD_ID $CARD_ID "## Definition of Done\n- [ ] <condition 1>\n- [ ] <condition 2>\n..."
340
- ```
341
-
342
- 5. **Add testing criteria comment**:
343
- ```bash
344
- monotask card comment add $TASK_BOARD_ID $CARD_ID "## Testing Criteria\n\n### Unit Tests\n- <test 1>\n\n### Integration Tests\n- <test 1>\n\n### Edge Cases\n- <case 1>"
345
- ```
346
-
347
- 6. **Add agent assignment + metadata**:
348
- ```bash
349
- monotask card comment add $TASK_BOARD_ID $CARD_ID "Assigned agent: <agent_type>\nPriority: <priority>\nEffort: <effort>/10\nDependencies: <dep titles or none>\nSource: monomind-improve"
350
- ```
280
+ **If the architect has doubts** about decomposing an improvement (unclear scope, missing info):
281
+ - Add a comment with the question
282
+ - Move the improvement to `Deferred` instead of `Tasked`
351
283
 
352
- 7. **Set priority**: `monotask card set-priority $TASK_BOARD_ID $CARD_ID <1-4>`
284
+ Store the result as `TASKS` array.
353
285
 
354
- 8. **Create checklist** (TDD implementation steps):
355
- ```bash
356
- monotask checklist add $TASK_BOARD_ID $CARD_ID "Implementation Steps" --json
357
- ```
358
- Then for each step:
359
- ```bash
360
- monotask checklist item-add $TASK_BOARD_ID $CARD_ID $CHECKLIST_ID "<step>"
361
- ```
286
+ ### Invoke the Unified Task Engine
362
287
 
363
- 9. **Comment on original improvement card** listing all subtask titles:
364
- ```bash
365
- monotask card comment add $BOARD_ID $IMPROVE_CARD_ID "Subtasks created:\n- <title> (agent: <type>, effort: <N>/10)\n- <title> (agent: <type>, effort: <N>/10)\n..."
366
- ```
288
+ Invoke the `monomind-task-engine` skill (Sections 3-7) with these parameters:
367
289
 
368
- 10. **Move the improvement card** to `Tasked`:
369
- ```bash
370
- monotask card move $BOARD_ID $IMPROVE_CARD_ID $COL_TASKED --json
371
- ```
290
+ | Parameter | Value |
291
+ |-----------|-------|
292
+ | `TASKS` | The array from the architect agent |
293
+ | `TASK_BOARD_ID` | From Step 3 (or let the engine set up the board) |
294
+ | `REPO_NAME` | From Step 1 |
295
+ | `SOURCE_TAG` | `"monomind-improve"` |
296
+ | `SOURCE_SUMMARY` | First 100 chars of `$ARGUMENTS` |
297
+ | `PARENT_CARD_ID` | Each `IMPROVE_CARD_ID` in the Approved column |
298
+ | `PARENT_BOARD_ID` | `$BOARD_ID` (the monomind-improve board) |
299
+ | `PARENT_DONE_COLUMN` | `$COL_TASKED` |
372
300
 
373
- **If the architect has doubts** about decomposing an improvement (unclear scope, missing info):
374
- - Add a comment with the question
375
- - Move the improvement to `Deferred` instead of `Tasked`
301
+ The engine will:
302
+ 1. Create all cards on the monotask board (Section 4)
303
+ 2. Store execution strategy in session memory (Section 5)
304
+ 3. Run the **Final Dependency & Critical Path Review** (Section 6) — a fresh Code Reviewer agent validates prerequisites, context groups, critical path, parallel safety, and agent assignments
305
+ 4. Fix any blocker issues automatically, present warnings to user
306
+ 5. Present execution offer with mode recommendation (Section 7)
376
307
 
377
308
  ---
378
309
 
@@ -412,13 +343,10 @@ Output board references:
412
343
 
413
344
  ## Step 9: Offer to Execute Tasks
414
345
 
415
- If there are any tasked improvements (subtasks in Backlog/Todo), ask the user:
346
+ The `monomind-task-engine` (Section 7) already presents the execution offer after the final review passes. If the user picks a mode there, it invokes `monomind-do` automatically.
416
347
 
417
- > **M subtasks are ready.** Want me to start executing them now?
418
- >
419
- > Say **yes** to launch `/monomind:do` — it will pick up tasks one by one, execute them with the assigned agent, review for bugs, and loop until the queue is empty.
348
+ If the engine's execution offer was skipped or the user deferred, and there are tasks in Todo, offer:
420
349
 
421
- If the user says yes, invoke:
422
350
  ```
423
- Skill("monomind-do", "--space $SPACE_ID --board $TASK_BOARD_ID")
351
+ Skill("monomind-do", "--space $SPACE_ID --board $TASK_BOARD_ID --mode <parallel|minimal|sequential>")
424
352
  ```
@@ -1,118 +1,33 @@
1
1
  'use strict';
2
- // Runs at SessionStart — rebuilds the knowledge graph for the current project in the background.
2
+ // Runs at SessionStart — rebuilds the knowledge graph using graphify (Python) in the background.
3
3
  // Fire-and-forget: spawns detached child, logs start, exits immediately without blocking session.
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
+ const { spawn, execSync } = require('child_process');
6
7
 
7
8
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
8
9
  const graphDir = path.join(projectDir, '.monomind', 'graph');
9
- const statsFile = path.join(graphDir, 'stats.json');
10
10
 
11
- // Locate @monomind/graph check local, global npm, bundled-graph, pnpm
12
- function findGraphPkg(base) {
13
- const candidates = [
14
- path.join(base, 'node_modules', '@monomind', 'graph', 'dist', 'src', 'index.js'),
15
- path.join(base, 'packages', '@monomind', 'cli', 'node_modules', '@monomind', 'graph', 'dist', 'src', 'index.js'),
16
- path.join(base, 'packages', '@monomind', 'graph', 'dist', 'src', 'index.js'),
17
- ];
18
- // Check global npm install paths (monomind umbrella ships bundled-graph)
19
- try {
20
- const globalRoot = require('child_process').execSync('npm root -g', { encoding: 'utf-8' }).trim();
21
- candidates.push(
22
- path.join(globalRoot, 'monomind', 'packages', '@monomind', 'cli', 'bundled-graph', 'dist', 'src', 'index.js'),
23
- path.join(globalRoot, 'monomind', 'node_modules', '@monoes', 'monomindcli', 'bundled-graph', 'dist', 'src', 'index.js'),
24
- );
25
- } catch {}
26
- for (const c of candidates) {
27
- if (fs.existsSync(c)) return c;
28
- }
29
- // pnpm: glob for @monomind+graph in .pnpm
30
- const pnpmDir = path.join(base, 'node_modules', '.pnpm');
31
- if (fs.existsSync(pnpmDir)) {
32
- for (const entry of fs.readdirSync(pnpmDir)) {
33
- if (entry.startsWith('@monomind+graph')) {
34
- const p = path.join(pnpmDir, entry, 'node_modules', '@monomind', 'graph', 'dist', 'src', 'index.js');
35
- if (fs.existsSync(p)) return p;
36
- }
37
- }
38
- }
39
- return null;
40
- }
41
-
42
- const graphPkg = findGraphPkg(projectDir);
43
-
44
- if (!graphPkg) {
45
- console.log('[graph] skip: @monomind/graph not found');
11
+ // Check if graphify CLI is available
12
+ try {
13
+ execSync('graphify --help', { encoding: 'utf-8', stdio: 'ignore' });
14
+ } catch {
15
+ console.log('[graph] skip: graphify not installed (run: uv tool install graphifyy)');
46
16
  process.exit(0);
47
17
  }
48
18
 
49
19
  fs.mkdirSync(graphDir, { recursive: true });
50
20
 
51
- // Locate the enricher script — works for both monorepo layout and npm-install layout
52
- const enricherCandidates = [
53
- path.join(projectDir, 'packages', '@monomind', 'cli', 'dist', 'src', 'graph', 'enrich.mjs'),
54
- path.join(projectDir, 'node_modules', '@monomind', 'cli', 'dist', 'src', 'graph', 'enrich.mjs'),
55
- ];
56
- const enricherPath = enricherCandidates.find(p => fs.existsSync(p)) ?? null;
57
- const hasEnricher = enricherPath !== null;
58
-
59
- const { spawn } = require('child_process');
60
- const script = [
61
- `import { buildGraph } from ${JSON.stringify('file://' + graphPkg)};`,
62
- `import fs from 'fs';`,
63
- `import path from 'path';`,
64
- `const projectDir = ${JSON.stringify(projectDir)};`,
65
- `const graphDir = ${JSON.stringify(graphDir)};`,
66
- `const statsFile = ${JSON.stringify(statsFile)};`,
67
- `buildGraph(projectDir, { codeOnly: true, outputDir: graphDir })`,
68
- `.then(async r => {`,
69
- ` fs.writeFileSync(statsFile, JSON.stringify({ nodes: r.analysis?.stats?.nodes, edges: r.analysis?.stats?.edges, files: r.filesProcessed, builtAt: Date.now() }));`,
70
- ` console.log('[graph] built: ' + r.filesProcessed + ' files, ' + (r.analysis?.stats?.nodes ?? '?') + ' nodes');`,
71
- hasEnricher ? [
72
- ` try {`,
73
- ` const { enrichGraph } = await import(${JSON.stringify('file://' + enricherPath)});`,
74
- ` const er = await enrichGraph(projectDir, { graphDir });`,
75
- ` console.log('[graph] enriched: ' + er.metrics.enrichedNodes + '/' + er.metrics.totalNodes + ' nodes');`,
76
- ` } catch (ee) { console.error('[graph] enrichment failed:', ee.message); }`,
77
- ].join('\n') : '',
78
- // Normalize graph.json: add snake_case field aliases expected by the MCP tools
79
- ` try {`,
80
- ` const graphPath = path.join(graphDir, 'graph.json');`,
81
- ` const raw = JSON.parse(fs.readFileSync(graphPath, 'utf-8'));`,
82
- ` if (Array.isArray(raw.nodes)) {`,
83
- ` for (const n of raw.nodes) {`,
84
- ` n.source_file = n.sourceFile || '';`,
85
- ` n.source_location = n.sourceLocation || '';`,
86
- ` n.file_type = n.fileType || '';`,
87
- ` // Zero out degree for external symbols so they don't dominate god_nodes results`,
88
- ` if (!n.source_file) n.degree = 0;`,
89
- ` }`,
90
- ` fs.writeFileSync(graphPath, JSON.stringify(raw));`,
91
- ` console.log('[graph] normalized: added MCP field aliases to ' + raw.nodes.length + ' nodes');`,
92
- ` }`,
93
- ` } catch (ne) { console.error('[graph] normalize failed:', ne.message); }`,
94
- // Ensure .monomind/graph symlink exists so the MCP server can locate the graph
95
- ` try {`,
96
- ` const monomindDir = path.join(projectDir, '.monomind');`,
97
- ` fs.mkdirSync(monomindDir, { recursive: true });`,
98
- ` const symlinkTarget = path.join(monomindDir, 'graph');`,
99
- ` let exists = false;`,
100
- ` try { fs.lstatSync(symlinkTarget); exists = true; } catch {}`,
101
- ` if (!exists) { fs.symlinkSync(graphDir, symlinkTarget); console.log('[graph] created .monomind/graph symlink'); }`,
102
- ` } catch (se) { console.error('[graph] symlink setup failed:', se.message); }`,
103
- `})`,
104
- `.catch(e => console.error('[graph] build failed:', e.message));`,
105
- ].join('\n');
106
-
107
21
  const logPath = path.join(graphDir, 'build.log');
108
22
  let logFd;
109
23
  try { logFd = fs.openSync(logPath, 'a'); } catch { logFd = 'ignore'; }
110
- const child = spawn(process.execPath, ['--input-type=module'], {
24
+
25
+ // graphify update <path> — re-extracts code files and rebuilds graph.json
26
+ const child = spawn('graphify', ['update', projectDir], {
111
27
  detached: true,
112
- stdio: ['pipe', logFd, logFd],
28
+ stdio: ['ignore', logFd, logFd],
29
+ cwd: projectDir,
113
30
  });
114
- child.stdin.write(script);
115
- child.stdin.end();
116
31
  child.unref();
117
32
 
118
33
  console.log('[graph] background build started for ' + projectDir);
@@ -961,6 +961,22 @@ const handlers = {
961
961
  }
962
962
  } catch (e) { /* non-fatal */ }
963
963
 
964
+ // ── Monomind Control UI Status ────────────────────────────────────────
965
+ try {
966
+ var http = require('http');
967
+ var controlPort = 4242;
968
+ var req = http.get('http://localhost:' + controlPort + '/', function(res) {
969
+ if (res.statusCode === 200) {
970
+ console.log('[CONTROL_UI] UP — http://localhost:' + controlPort);
971
+ }
972
+ res.resume();
973
+ });
974
+ req.on('error', function() {
975
+ console.log('[CONTROL_UI] offline — run: npx monomind mcp start');
976
+ });
977
+ req.setTimeout(800, function() { req.destroy(); });
978
+ } catch (e) { /* non-fatal */ }
979
+
964
980
  // ── Worker Queue Resume (SR-003) ────────────────────────────────────
965
981
  try {
966
982
  var dispatchDir = path.join(CWD, '.monomind', 'worker-dispatch');
@@ -104,6 +104,18 @@ function safeStat(filePath) {
104
104
  return null;
105
105
  }
106
106
 
107
+ // Project identifier — github owner/repo from git remote, else folder name
108
+ function getProjectName() {
109
+ try {
110
+ const remote = safeExec('git remote get-url origin 2>/dev/null', 2000).trim();
111
+ if (remote) {
112
+ const m = remote.match(/[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?$/);
113
+ if (m) return `${m[1]}/${m[2]}`;
114
+ }
115
+ } catch { /* ignore */ }
116
+ return path.basename(CWD);
117
+ }
118
+
107
119
  // Shared settings cache — read once, used by multiple functions
108
120
  let _settingsCache = undefined;
109
121
  function getSettings() {
@@ -565,37 +577,56 @@ function getAgentDBStats() {
565
577
  let namespaces = 0;
566
578
  let hasHnsw = false;
567
579
 
568
- // 0. PRIMARY: Count drawers from Memory Palace (this is where memories actually live)
580
+ // Count all memory entries across sources (sum, not max)
581
+ // 0. Palace drawers
569
582
  const drawersPath = path.join(CWD, '.monomind', 'palace', 'drawers.jsonl');
570
583
  const drawersStat = safeStat(drawersPath);
571
584
  if (drawersStat) {
572
585
  dbSizeKB += drawersStat.size / 1024;
573
586
  try {
574
- const lines = fs.readFileSync(drawersPath, 'utf-8').split('\n').filter(Boolean);
575
- if (lines.length > vectorCount) vectorCount = lines.length;
587
+ vectorCount += fs.readFileSync(drawersPath, 'utf-8').split('\n').filter(Boolean).length;
588
+ } catch { /* ignore */ }
589
+ }
590
+
591
+ // 1. Palace closets
592
+ const closetsPath = path.join(CWD, '.monomind', 'palace', 'closets.jsonl');
593
+ const closetsStat = safeStat(closetsPath);
594
+ if (closetsStat) {
595
+ dbSizeKB += closetsStat.size / 1024;
596
+ try {
597
+ vectorCount += fs.readFileSync(closetsPath, 'utf-8').split('\n').filter(Boolean).length;
576
598
  } catch { /* ignore */ }
577
599
  }
578
600
 
579
- // 1. Count real entries from auto-memory-store.json (intelligence layer)
601
+ // 2. Knowledge chunks
602
+ const chunksPath = path.join(CWD, '.monomind', 'knowledge', 'chunks.jsonl');
603
+ const chunksStat = safeStat(chunksPath);
604
+ if (chunksStat) {
605
+ dbSizeKB += chunksStat.size / 1024;
606
+ try {
607
+ vectorCount += fs.readFileSync(chunksPath, 'utf-8').split('\n').filter(Boolean).length;
608
+ } catch { /* ignore */ }
609
+ }
610
+
611
+ // 3. Auto-memory store (intelligence layer)
580
612
  const storePath = path.join(CWD, '.monomind', 'data', 'auto-memory-store.json');
581
613
  const storeStat = safeStat(storePath);
582
614
  if (storeStat) {
583
615
  dbSizeKB += storeStat.size / 1024;
584
616
  try {
585
617
  const store = JSON.parse(fs.readFileSync(storePath, 'utf-8'));
586
- const storeCount = Array.isArray(store) ? store.length : (store?.entries?.length || 0);
587
- if (storeCount > vectorCount) vectorCount = storeCount;
588
- } catch { /* fall back to size estimate */ }
618
+ vectorCount += Array.isArray(store) ? store.length : (store?.entries?.length || 0);
619
+ } catch { /* ignore */ }
589
620
  }
590
621
 
591
- // 2. Count entries from ranked-context.json
622
+ // 4. Ranked context
592
623
  const rankedPath = path.join(CWD, '.monomind', 'data', 'ranked-context.json');
593
624
  try {
594
625
  const ranked = readJSON(rankedPath);
595
- if (ranked?.entries?.length > vectorCount) vectorCount = ranked.entries.length;
626
+ if (ranked?.entries?.length) vectorCount += ranked.entries.length;
596
627
  } catch { /* ignore */ }
597
628
 
598
- // 3. Add DB file sizes
629
+ // 5. DB file sizes
599
630
  const dbFiles = [
600
631
  path.join(CWD, 'data', 'memory.db'),
601
632
  path.join(CWD, '.monomind', 'memory.db'),
@@ -603,31 +634,17 @@ function getAgentDBStats() {
603
634
  ];
604
635
  for (const f of dbFiles) {
605
636
  const stat = safeStat(f);
606
- if (stat) {
607
- dbSizeKB += stat.size / 1024;
608
- namespaces++;
609
- }
637
+ if (stat) { dbSizeKB += stat.size / 1024; namespaces++; }
610
638
  }
611
639
 
612
- // 4. Check for graph data
613
- const graphPath = path.join(CWD, 'data', 'memory.graph');
614
- const graphStat = safeStat(graphPath);
615
- if (graphStat) dbSizeKB += graphStat.size / 1024;
616
-
617
- // 5. HNSW index
640
+ // 6. HNSW index
618
641
  const hnswPaths = [
619
642
  path.join(CWD, '.swarm', 'hnsw.index'),
620
643
  path.join(CWD, '.monomind', 'hnsw.index'),
621
644
  ];
622
645
  for (const p of hnswPaths) {
623
- const stat = safeStat(p);
624
- if (stat) {
625
- hasHnsw = true;
626
- break;
627
- }
646
+ if (safeStat(p)) { hasHnsw = true; break; }
628
647
  }
629
-
630
- // HNSW is available if memory package is present
631
648
  if (!hasHnsw) {
632
649
  const memPkgPaths = [
633
650
  path.join(CWD, 'packages', '@monomind', 'memory', 'dist'),
@@ -641,6 +658,26 @@ function getAgentDBStats() {
641
658
  return { vectorCount, dbSizeKB: Math.floor(dbSizeKB), namespaces, hasHnsw };
642
659
  }
643
660
 
661
+ // Graphify knowledge graph stats
662
+ function getGraphifyStats() {
663
+ const statsPath = path.join(CWD, '.monomind', 'graph', 'stats.json');
664
+ const graphPath = path.join(CWD, '.monomind', 'graph', 'graph.json');
665
+ try {
666
+ const s = readJSON(statsPath);
667
+ if (s && s.nodes !== undefined) return { nodes: s.nodes, edges: s.edges || 0, exists: true };
668
+ } catch { /* ignore */ }
669
+ try {
670
+ const stat = safeStat(graphPath);
671
+ if (stat && stat.size < 10 * 1024 * 1024) {
672
+ const g = JSON.parse(fs.readFileSync(graphPath, 'utf-8'));
673
+ const nodes = Array.isArray(g.nodes) ? g.nodes.length : 0;
674
+ const edges = (Array.isArray(g.edges) ? g.edges : (Array.isArray(g.links) ? g.links : [])).length;
675
+ return { nodes, edges, exists: true };
676
+ }
677
+ } catch { /* ignore */ }
678
+ return { nodes: 0, edges: 0, exists: false };
679
+ }
680
+
644
681
  // Memory Palace stats — drawers.jsonl + kg.json (the real persistent memory)
645
682
  function getMemoryPalaceStats() {
646
683
  const palaceDir = path.join(CWD, '.monomind', 'palace');
@@ -907,9 +944,10 @@ function generateStatusline() {
907
944
  const tokens = getTokenStats();
908
945
  const parts = [];
909
946
 
910
- // Brand + swarm dot
947
+ // Brand + project + swarm dot
911
948
  const swarmDot = swarm.coordinationActive ? `${x.green}●${x.reset}` : `${x.slate}○${x.reset}`;
912
- parts.push(`${x.bold}${x.purple}▊ Monomind${x.reset} ${swarmDot}`);
949
+ const projName = getProjectName();
950
+ parts.push(`${x.bold}${x.purple}▊ MonoMind${x.reset} ${x.teal}${projName}${x.reset} ${swarmDot}`);
913
951
 
914
952
  // Git branch + changes (compact)
915
953
  if (git.gitBranch) {
@@ -958,12 +996,18 @@ function generateStatusline() {
958
996
  parts.push(`${x.purple}🧠 ${autoMemCompact.count}m${x.reset}${typeSuffix}`);
959
997
  }
960
998
 
961
- // Knowledge chunks (Task 28) — show when populated
999
+ // Knowledge chunks — show when populated
962
1000
  if (knowledge.chunks > 0) {
963
1001
  parts.push(`${x.teal}📚 ${knowledge.chunks}k${x.reset}`);
964
1002
  }
965
1003
 
966
- // Triggers (Task 32) — show when populated
1004
+ // Graphify code graph
1005
+ const gfCompact = getGraphifyStats();
1006
+ if (gfCompact.exists) {
1007
+ parts.push(`${x.sky}🔗 ${gfCompact.nodes}n ${gfCompact.edges}e${x.reset}`);
1008
+ }
1009
+
1010
+ // Triggers — show when populated
967
1011
  if (triggers.triggers > 0) {
968
1012
  parts.push(`${x.mint}🎯 ${triggers.triggers}t${x.reset}`);
969
1013
  }
@@ -997,7 +1041,7 @@ function generateDashboard() {
997
1041
  const security = getSecurityStatus();
998
1042
  const swarm = getSwarmStatus();
999
1043
  const system = getSystemMetrics();
1000
- const adrs = getADRStatus();
1044
+ // adrs removed — internal dev metric
1001
1045
  const hooks = getHooksStatus();
1002
1046
  const agentdb = getAgentDBStats();
1003
1047
  const tests = getTestStats();
@@ -1015,7 +1059,8 @@ function generateDashboard() {
1015
1059
 
1016
1060
  // ── Header: brand + git + model + session ────────────────────
1017
1061
  const swarmDot = swarm.coordinationActive ? `${x.green}● LIVE${x.reset}` : `${x.slate}○ IDLE${x.reset}`;
1018
- let hdr = `${x.bold}${x.purple}▊ Monomind ${VERSION}${x.reset} ${swarmDot} ${x.teal}${x.bold}${git.name}${x.reset}`;
1062
+ const projName = getProjectName();
1063
+ let hdr = `${x.bold}${x.purple}▊ MonoMind${x.reset} ${x.dim}${VERSION}${x.reset} ${swarmDot} ${x.teal}${x.bold}${projName}${x.reset}`;
1019
1064
 
1020
1065
  if (git.gitBranch) {
1021
1066
  hdr += ` ${DIV} ${x.sky}⎇ ${x.bold}${git.gitBranch}${x.reset}`;
@@ -1036,30 +1081,29 @@ function generateDashboard() {
1036
1081
  lines.push(hdr);
1037
1082
  lines.push(SEP);
1038
1083
 
1039
- // ── Row 1: Intelligence & Learning ───────────────────────────
1040
- const intellCol = pctColor(system.intelligencePct);
1041
- const intellBar = blockBar(system.intelligencePct, 100, 6);
1042
-
1043
- // Knowledge (Task 28)
1084
+ // ── Row 1: Knowledge & Graphify ──────────────────────────────
1044
1085
  const knowStr = knowledge.chunks > 0
1045
1086
  ? `${x.teal}📚 ${x.bold}${knowledge.chunks}${x.reset}${x.slate} chunks${x.reset}`
1046
1087
  : `${x.slate}📚 no chunks${x.reset}`;
1047
1088
 
1048
- // Skills (Task 45)
1049
1089
  const skillStr = knowledge.skills > 0
1050
1090
  ? ` ${x.mint}✦ ${knowledge.skills} skills${x.reset}`
1051
1091
  : '';
1052
1092
 
1053
- // Patterns
1054
1093
  const patStr = progress.patternsLearned > 0
1055
1094
  ? `${x.gold}${progress.patternsLearned >= 1000 ? (progress.patternsLearned / 1000).toFixed(1) + 'k' : progress.patternsLearned} patterns${x.reset}`
1056
1095
  : `${x.slate}0 patterns${x.reset}`;
1057
1096
 
1097
+ const gf = getGraphifyStats();
1098
+ const gfStr = gf.exists
1099
+ ? `${x.sky}🔗 ${x.bold}${gf.nodes}${x.reset}${x.slate} nodes · ${x.reset}${x.sky}${x.bold}${gf.edges}${x.reset}${x.slate} edges${x.reset}`
1100
+ : `${x.slate}🔗 no graph${x.reset}`;
1101
+
1058
1102
  lines.push(
1059
1103
  `${x.purple}💡 INTEL${x.reset} ` +
1060
- `${intellCol}${intellBar} ${x.bold}${system.intelligencePct}%${x.reset} ${DIV} ` +
1061
1104
  `${knowStr}${skillStr} ${DIV} ` +
1062
- patStr
1105
+ `${patStr} ${DIV} ` +
1106
+ gfStr
1063
1107
  );
1064
1108
  lines.push(SEP);
1065
1109
 
@@ -1112,26 +1156,14 @@ function generateDashboard() {
1112
1156
  );
1113
1157
  lines.push(SEP);
1114
1158
 
1115
- // ── Row 3: Architecture & Security ───────────────────────────
1116
- const adrCol = adrs.count > 0
1117
- ? (adrs.implemented >= adrs.count ? x.green : x.gold)
1118
- : x.slate;
1119
- const adrStr = adrs.count > 0
1120
- ? `${adrCol}${x.bold}${adrs.implemented}${x.reset}${x.slate}/${x.reset}${x.white}${adrs.count}${x.reset} ADRs`
1121
- : `${x.slate}no ADRs${x.reset}`;
1122
-
1123
- const dddCol = pctColor(progress.dddProgress);
1124
- const dddBar = blockBar(progress.dddProgress, 100, 5);
1125
-
1159
+ // ── Row 3: Security ──────────────────────────────────────────
1126
1160
  const cveStatus = security.totalCves === 0
1127
1161
  ? (security.status === 'NONE' ? `${x.slate}not scanned${x.reset}` : `${x.green}✔ clean${x.reset}`)
1128
1162
  : `${x.coral}${security.cvesFixed}/${security.totalCves} fixed${x.reset}`;
1129
1163
 
1130
1164
  lines.push(
1131
- `${x.purple}🧩 ARCH${x.reset} ` +
1132
- `${adrStr} ${DIV} ` +
1133
- `DDD ${dddBar} ${dddCol}${x.bold}${progress.dddProgress}%${x.reset} ${DIV} ` +
1134
- `🛡️ ${sec.col}${sec.label}${x.reset} ${DIV} ` +
1165
+ `${x.purple}🛡️ SECURITY${x.reset} ` +
1166
+ `${sec.col}${sec.label}${x.reset} ${DIV} ` +
1135
1167
  `CVE ${cveStatus}`
1136
1168
  );
1137
1169
  lines.push(SEP);
@@ -1190,13 +1222,6 @@ function generateDashboard() {
1190
1222
  siStr = `${x.slate}📄 no shared instructions${x.reset}`;
1191
1223
  }
1192
1224
 
1193
- // Domains
1194
- const domCol = progress.domainsCompleted >= 4 ? x.green
1195
- : progress.domainsCompleted >= 2 ? x.gold
1196
- : progress.domainsCompleted >= 1 ? x.orange
1197
- : x.slate;
1198
- const domBar = blockBar(progress.domainsCompleted, progress.totalDomains);
1199
-
1200
1225
  let monthStr = '';
1201
1226
  if (tokens) {
1202
1227
  const mFmt = tokens.monthCost >= 100 ? `$${tokens.monthCost.toFixed(2)}` : tokens.monthCost >= 1 ? `$${tokens.monthCost.toFixed(3)}` : `$${tokens.monthCost.toFixed(4)}`;
@@ -1205,7 +1230,6 @@ function generateDashboard() {
1205
1230
  lines.push(
1206
1231
  `${x.slate}📋 CONTEXT${x.reset} ` +
1207
1232
  `${siStr} ${DIV} ` +
1208
- `${x.teal}🏗 ${domBar} ${domCol}${x.bold}${progress.domainsCompleted}${x.reset}${x.slate}/${x.reset}${x.white}${progress.totalDomains}${x.reset} domains ${DIV} ` +
1209
1233
  `${x.dim}💾 ${system.memoryMB} MB RAM${x.reset}` +
1210
1234
  monthStr
1211
1235
  );