@plumpslabs/kuma 2.3.17 → 2.3.18

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.
@@ -76,14 +76,16 @@ var BOOTSTRAP_LINES = [
76
76
  ' 2. `kuma_safety({ action: "guard" })` \u2014 Safety check before work',
77
77
  ' 3. `kuma_context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
78
78
  " 4. *(edit/read using native tools)*",
79
- ' 5. `kuma_memory({ action: "research_save", ... })` \u2014 IMMEDIATELY after every read/grep (inline)',
80
- ' 6. `kuma_memory({ action: "gotcha" })` \u2014 IMMEDIATELY when bug/quirks found (inline)',
81
- ' 7. `kuma_memory({ action: "arch_flow" })` \u2014 IMMEDIATELY after tracing each flow hop (inline)',
82
- ' 8. `kuma_memory({ action: "decision" })` \u2014 IMMEDIATELY when choosing between options (inline)',
79
+ ' 5. `kuma_memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
80
+ ' 6. `kuma_memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential value)',
81
+ ' 7. `kuma_memory({ action: "arch_flow" })` \u2014 \u{1F525} IMMEDIATELY after each flow hop (exponential value)',
82
+ ' 8. `kuma_memory({ action: "decision" })` \u2014 When choosing between options (preserves rationale)',
83
83
  ' 9. `kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
84
84
  ' 10. `kuma_context({ action: "changes" })` \u2014 Review session activity',
85
85
  "",
86
- "\u{1F504} Record findings INLINE as you work \u2014 not just at steps 5-8. Every read/grep = mini research_save.",
86
+ "\u{1F525} **Power Curve:** `arch_flow` + `gotcha` are EXPONENTIAL \u2014 each record saves 5-10 files next session.",
87
+ " The more you record, the more overpowered the agent becomes. These are the highest-value actions.",
88
+ "\u{1F7E2} **SKIP** recording function/class/component nodes \u2014 grep/glob is faster.",
87
89
  "\u{1F9E0} Knowledge graph is persistent \u2014 nodes/edges accumulate across sessions, getting richer over time.",
88
90
  "",
89
91
  "\u26D4 Do NOT skip steps 5-10 after research \u2014 even if no code was changed.",
@@ -309,20 +311,43 @@ function generateInitMdContent() {
309
311
  "> \u26A0\uFE0F **Research-only sessions included.** Workflow ini wajib meskipun session hanya membaca/riset tanpa code changes.",
310
312
  "",
311
313
  "> \u{1F9E0} **Graph Philosophy:** Record findings INLINE as you work, not just at the end. Every time you read a",
312
- "> file, grep a pattern, or trace a flow \u2014 call `research_save` immediately to persist file/func/import nodes.",
314
+ "> file, grep a pattern, or trace a flow \u2014 call `research_save` immediately to persist what you learned.",
313
315
  "> The knowledge graph accumulates across sessions. The more you use it, the richer it becomes.",
314
316
  "",
315
- "**AUTO-RECORDED nodes/edges** (Kuma scanner detects these automatically):",
316
- "- Files, Functions, Classes \u2192 created as graph nodes",
317
- "- Imports, Calls, Extends \u2192 created as graph edges",
318
- "- Variables, Components, Routes \u2192 created when detected",
319
- "",
320
- "**RECORD INLINE \u2014 Don't wait for steps 5-8:**",
321
- "- After EVERY `read` that finds new files \u2192 call `research_save` mini (persist file nodes)",
322
- "- After EVERY `grep` that discovers new functions \u2192 call `research_save` (persist function nodes)",
323
- "- When you discover a bug/quirk \u2192 call `gotcha` IMMEDIATELY (not at step 6)",
324
- "- When you trace a data flow hop \u2192 call `arch_flow` IMMEDIATELY (not at step 7)",
325
- "- When you choose between options \u2192 call `decision` IMMEDIATELY (not at step 8)",
317
+ "> \u{1F52C} **Scanner vs Inline Recording:**",
318
+ "> \u2022 Scanner = cold start only. Auto-detects structure in TS/JS/TSX/JSX files during research step 3.",
319
+ "> \u2022 Inline Recording = primary method. You (the agent) record findings as you read/grep/edit.",
320
+ "> \u2022 Non-TS/JS projects (Go, Ruby, Python, Rust, PHP) rely ENTIRELY on inline recording.",
321
+ "",
322
+ "**AUTO-RECORDED (cold start scanner \u2014 TS/JS only):**",
323
+ "- Files, Functions, Classes \u2192 scanned during research step 3",
324
+ "- Imports, Calls, Extends \u2192 scanned edges",
325
+ "- Components, Routes \u2192 scanned when patterns match",
326
+ "",
327
+ "\u{1F534} **MUST RECORD \u2014 High Impact (use MCP):**",
328
+ "- `arch_flow` \u2014 IMMEDIATELY after each flow hop. Saves reading 5-10 files next session.",
329
+ "- `gotcha` \u2014 IMMEDIATELY when bug/quirk found. No re-research needed.",
330
+ "- `decision` \u2014 IMMEDIATELY when choosing. Preserves rationale context.",
331
+ "- `research_save` \u2014 After exploring area. Creates searchable cache + graph file node.",
332
+ "",
333
+ "\u{1F7E2} **SKIP \u2014 Low Impact (agent can grep faster):**",
334
+ "- Function/class nodes \u2192 grep `funcName(` / `class ClassName`",
335
+ "- Component nodes \u2192 glob `**/*ComponentName*`",
336
+ "- Import edges \u2192 check imports directly",
337
+ "- Route nodes \u2192 check router file directly",
338
+ "- Visual graph \u2192 for humans, not AI agents",
339
+ "",
340
+ "**RECORD INLINE \u2014 Focus on high-impact actions:**",
341
+ "- Finished tracing a flow? \u2192 `arch_flow` IMMEDIATELY (saves reading 5-10 files next session)",
342
+ "- Found a bug/quirk? \u2192 `gotcha` IMMEDIATELY (no re-research needed)",
343
+ "- Chose between options? \u2192 `decision` IMMEDIATELY (preserves rationale)",
344
+ "- Explored a new area? \u2192 `research_save` (creates searchable cache)",
345
+ "",
346
+ "**SKIP recording these \u2014 grep/glob is faster:**",
347
+ "- Function/class nodes \u2192 just `grep funcName(` or `grep class ClassName`",
348
+ "- Component/route nodes \u2192 glob directly",
349
+ "- Import edges \u2192 read import statements",
350
+ "- Visual graph \u2192 for humans, not AI",
326
351
  "",
327
352
  "### STEP 5: Save Research (RECORD INLINE \u2014 After Every Read/Grep)",
328
353
  "```",
@@ -398,6 +423,7 @@ function generateInitMdContent() {
398
423
  "| `todo` | Persistent todo CRUD | When managing tasks |",
399
424
  "| `context` | Inject context notes | Additional notes when needed |",
400
425
  "| `domain_rules` | Layer 1: business rules | Record business rules when discovered |",
426
+ "| `add_node` | Create structural nodes (low impact \u2014 grep is faster) | Rarely needed; only for visualize debugging |",
401
427
  "| `layers` | Show all 3 layers summary | Overview memory layers |",
402
428
  "",
403
429
  "### \u{1F6E1}\uFE0F kuma_safety \u2014 Safety & Policy",
@@ -457,14 +483,15 @@ function generateInitMdContent() {
457
483
  " 2. kuma_safety \u2192 guard \u2014 Safety check",
458
484
  " 3. kuma_context \u2192 research \u2014 Research target area",
459
485
  " --- EDIT/READ (use native tools) ---",
460
- " 5. kuma_memory \u2192 research_save \u2014 Save findings (inline after every read/grep)",
461
- " 6. kuma_memory \u2192 gotcha \u2014 Record bugs/quirks (inline when found)",
462
- " 7. kuma_memory \u2192 arch_flow \u2014 Record data flow (inline after each hop)",
463
- " 8. kuma_memory \u2192 decision \u2014 Record key decisions (inline when choosing)",
486
+ " 5. kuma_memory \u2192 research_save \u2014 After exploring area (creates search cache)",
487
+ " 6. kuma_memory \u2192 gotcha \u2014 \u{1F525} IMMEDIATELY when bug found (exponential)",
488
+ " 7. kuma_memory \u2192 arch_flow \u2014 \u{1F525} IMMEDIATELY after each flow hop (exponential)",
489
+ " 8. kuma_memory \u2192 decision \u2014 When choosing (preserves rationale)",
464
490
  " 9. kuma_safety \u2192 verify \u2014 Run tests / confirm nothing broken",
465
491
  " 10. kuma_context \u2192 changes \u2014 Review session activity",
466
492
  "",
467
- "\u{1F504} Record INLINE as you work \u2014 not just at steps 5-8. Every read/grep = mini research_save.",
493
+ "\u{1F525} `arch_flow` + `gotcha` are EXPONENTIAL \u2014 each record saves 5-10 files next session.",
494
+ "\u{1F7E2} SKIP recording function/class/component nodes \u2014 grep/glob is faster.",
468
495
  "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
469
496
  "",
470
497
  "ON DISCOVERY (When needed):",
@@ -472,14 +499,19 @@ function generateInitMdContent() {
472
499
  " kuma_memory \u2192 context \u2014 Additional notes",
473
500
  " kuma_memory \u2192 todo \u2014 Task management",
474
501
  " kuma_memory \u2192 mine \u2014 Historical context",
502
+ " kuma_memory \u2192 add_node \u2014 Manual function/class/component node (for visualize)",
475
503
  "```",
476
504
  "",
477
505
  "## \u{1F9E0} How the Scanner Works",
478
506
  "",
479
- "Kuma's code scanner (`kumaCodeScanner.ts`) uses **regex-based pattern matching**",
480
- "to detect code structure. It is NOT a full AST parser (no Babel/TypeScript compiler dependency).",
507
+ "Kuma's code scanner (`kumaCodeScanner.ts`) uses **lightweight regex-based pattern matching**",
508
+ "for **cold start** \u2014 giving new agents basic project structure on first session.",
509
+ "It is NOT a full AST parser (no Babel/TypeScript compiler dependency).",
510
+ "",
511
+ "**Scope:** TS/JS/TSX/JSX only. Non-TypeScript projects (Go, Ruby, Python, Rust, PHP, etc.)",
512
+ "rely entirely on **AI inline recording** \u2014 the agent records what it discovers as it works.",
481
513
  "",
482
- "**What it detects (line-by-line regex):**",
514
+ "**What auto-scanner detects (line-by-line regex, TS/JS only):**",
483
515
  "- `function` declarations (named + async + generator)",
484
516
  "- Arrow function assignments (`const foo = (...) => ...`)",
485
517
  "- Typed arrow functions (`const foo: Type = (...) => ...`)",
@@ -491,41 +523,41 @@ function generateInitMdContent() {
491
523
  "- Test files (`.test.`, `.spec.`, `__tests__/`)",
492
524
  "",
493
525
  "**Known limitations:**",
494
- "- Regex-based = can miss multi-line constructs or non-standard patterns",
526
+ "- Regex-based: can miss multi-line constructs or non-standard patterns",
495
527
  "- Complex generics or deeply nested syntax may not match",
496
528
  "- TypeScript advanced types may be partially parsed",
497
- "- Scanner runs automatically during `kuma_context research` step 3",
529
+ "- Scanner runs during `kuma_context research` step 3 (max 200 files)",
498
530
  "- If scanner returns 0 structural nodes, the regex patterns didn't match your code style",
499
531
  "",
500
- "**For richer code analysis:**",
501
- "- Record findings INLINE as you read/grep \u2014 this builds the graph manually",
502
- "- Call `research_save` after exploring files to persist function/class/import nodes",
503
- "- The more you use Kuma, the richer the graph becomes (persistent across sessions)",
532
+ "**Key principle: Scanner = cold start, Inline Recording = primary.**",
533
+ "The scanner populates initial graph structure. Every subsequent session, the agent's",
534
+ "inline recording (research_save, gotcha, arch_flow, decision) enriches the graph further.",
535
+ "The more you use Kuma, the richer the graph becomes \u2014 persistent across sessions.",
504
536
  "",
505
537
  "---",
506
538
  "",
507
- "## \u{1F4BE} How to Read Memory & Search the Graph",
539
+ "## \u{1F4BE} How to Retrieve Past Research \u2014 research_save \u2192 search",
508
540
  "",
509
- "Kuma stores knowledge in two places:",
541
+ "Kuma stores knowledge in two places. The primary retrieval method for AI agents",
542
+ "is **`search`**, not `visualize`. Visualize is for human debugging, not agent consumption.",
510
543
  "",
511
- "**1. Knowledge Graph (SQLite - `.kuma/kuma.db`)**",
512
- "- Nodes: files, functions, classes, components, routes, tests, gotchas, decisions",
513
- "- Edges: imports, calls, extends, contains, composes, routes",
514
- '- Query: `kuma_memory({ action: "search", query: "<term>" })`',
515
- "- Falls back to codebase grep if graph is empty",
544
+ "**Flow: research_save \u2192 search (for agents)**",
545
+ '- Save: `kuma_memory({ action: "research_save", scope: "<file or area>", confidence: 0.8 })`',
546
+ '- Retrieve: `kuma_memory({ action: "search", query: "<term>" })`',
547
+ '- Auto-load: `kuma_context({ action: "research", scope: "<area>" })`',
548
+ '- List cache: `kuma_context({ action: "researches" })`',
516
549
  "",
517
- "**2. Research Cache (SQLite - `.kuma/kuma.db`)**",
518
- '- Saved via `kuma_memory({ action: "research_save", scope: "<area>", confidence: 0.8 })`',
519
- '- List: `kuma_context({ action: "researches" })`',
520
- '- Auto-loaded during `kuma_context({ action: "research", scope: "<area>" })`',
550
+ "**Key design:** `research_save` now creates BOTH research cache entries AND",
551
+ "knowledge graph nodes (file + research scope). This means `search` finds",
552
+ "past research even if the graph scanner didn't detect your code style.",
521
553
  "",
522
- "**Quick memory reading tips:**",
554
+ "**Quick retrieval tips:**",
555
+ '- `kuma_memory({ action: "search", query: "<term>" })` \u2014 **primary method**. Finds past research + graph nodes + codebase',
556
+ '- `kuma_context({ action: "research", scope: "<area>" })` \u2014 deep 5-step research, auto-loads past cache',
523
557
  '- `kuma_context({ action: "init" })` \u2014 shows graph stats + recent research + health score',
524
- '- `kuma_context({ action: "research", scope: "<area>" })` \u2014 deep research with graph query',
525
- '- `kuma_memory({ action: "search", query: "<term>" })` \u2014 search all memories + graph',
526
- '- `kuma_context({ action: "visualize" })` \u2014 Mermaid diagram of the graph',
527
558
  '- `kuma_context({ action: "drift" })` \u2014 check if memories are stale vs actual code',
528
559
  '- `kuma_context({ action: "changes" })` \u2014 view session activity log',
560
+ '- `kuma_context({ action: "visualize" })` \u2014 (human debugging) Mermaid diagram of the graph',
529
561
  "",
530
562
  "---",
531
563
  "",
@@ -598,14 +630,15 @@ function handleOpencodeSecondary(root, results) {
598
630
  ' 2. `kuma_kuma_safety({ action: "guard" })` \u2014 Safety check before work',
599
631
  ' 3. `kuma_kuma_context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
600
632
  " 4. *(edit/read using native tools)*",
601
- ' 5. `kuma_kuma_memory({ action: "research_save", ... })` \u2014 Save findings (inline after every read/grep)',
602
- ' 6. `kuma_kuma_memory({ action: "gotcha" })` \u2014 Record bugs/quirks (inline when found)',
603
- ' 7. `kuma_kuma_memory({ action: "arch_flow" })` \u2014 Record data flow (inline after each hop)',
604
- ' 8. `kuma_kuma_memory({ action: "decision" })` \u2014 Record key decisions (inline when choosing)',
633
+ ' 5. `kuma_kuma_memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
634
+ ' 6. `kuma_kuma_memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential)',
635
+ ' 7. `kuma_kuma_memory({ action: "arch_flow" })` \u2014 \u{1F525} IMMEDIATELY after each flow hop (exponential)',
636
+ ' 8. `kuma_kuma_memory({ action: "decision" })` \u2014 When choosing (preserves rationale)',
605
637
  ' 9. `kuma_kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
606
638
  ' 10. `kuma_kuma_context({ action: "changes" })` \u2014 Review session activity',
607
639
  "",
608
- "\u{1F504} Record findings INLINE as you work \u2014 Every read/grep = mini research_save.",
640
+ "\u{1F525} `arch_flow` + `gotcha` = EXPONENTIAL value. Each record saves 5-10 files next session.",
641
+ "\u{1F7E2} SKIP recording function/class/component nodes \u2014 grep/glob is faster.",
609
642
  "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
610
643
  "",
611
644
  "\u26A0\uFE0F **OpenCode platform note:** Tool names are prefixed with `kuma_kuma_*`",
@@ -729,14 +762,15 @@ function getCombinedAgentsMd(selectedTypes) {
729
762
  ' 2. `[kuma_]safety({ action: "guard" })` \u2014 Safety check before editing',
730
763
  ' 3. `[kuma_]context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
731
764
  " 4. *(edit/read using native tools)*",
732
- ' 5. `[kuma_]memory({ action: "research_save", ... })` \u2014 Save all findings',
733
- ' 6. `[kuma_]memory({ action: "gotcha" })` \u2014 Record bugs/quirks found',
734
- ' 7. `[kuma_]memory({ action: "arch_flow" })` \u2014 Record data flow if mapped',
735
- ' 8. `[kuma_]memory({ action: "decision" })` \u2014 Record key findings/decisions',
765
+ ' 5. `[kuma_]memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
766
+ ' 6. `[kuma_]memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential)',
767
+ ' 7. `[kuma_]memory({ action: "arch_flow" })` \u2014 \u{1F525} IMMEDIATELY after each flow hop (exponential)',
768
+ ' 8. `[kuma_]memory({ action: "decision" })` \u2014 When choosing (preserves rationale)',
736
769
  ' 9. `[kuma_]safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
737
770
  ' 10. `[kuma_]context({ action: "changes" })` \u2014 Review session activity',
738
771
  "",
739
- "\u{1F504} Record findings INLINE as you work \u2014 Every read/grep = mini research_save.",
772
+ "\u{1F525} `arch_flow` + `gotcha` = EXPONENTIAL value. Each record saves 5-10 files next session.",
773
+ "\u{1F7E2} SKIP recording function/class/component nodes \u2014 grep/glob is faster.",
740
774
  "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
741
775
  "",
742
776
  "\u26D4 Do NOT skip steps 5-10 after research \u2014 even if no code was changed.",
@@ -946,15 +980,16 @@ function handleQuickrefGeneration(root, results) {
946
980
  ' 2. `kuma_safety({ action: "guard" })` \u2014 Safety check',
947
981
  ' 3. `kuma_context({ action: "research", scope: "<area>" })` \u2014 Research',
948
982
  " 4. *(edit/read using native tools)*",
949
- ' 5. `kuma_memory({ action: "research_save", ... })` \u2014 Save findings (inline after every read)',
950
- ' 6. `kuma_memory({ action: "gotcha" })` \u2014 Gotchas (inline when found)',
951
- ' 7. `kuma_memory({ action: "arch_flow" })` \u2014 Arch flow (inline after each hop)',
952
- ' 8. `kuma_memory({ action: "decision" })` \u2014 Decision (inline when choosing)',
953
- ' 9. `kuma_safety({ action: "verify", ... })` \u2014 Verify',
954
- ' 10. `kuma_context({ action: "changes" })` \u2014 Review changes',
955
- "",
956
- "\u{1F504} Record INLINE as you work \u2014 Every read/grep = mini research_save.",
957
- "\u{1F9E0} Graph is persistent \u2014 accumulates across sessions.",
983
+ ' 5. `kuma_memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
984
+ ' 6. `kuma_memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential)',
985
+ ' 7. `kuma_memory({ action: "arch_flow" })` \u2014 \u{1F525} IMMEDIATELY after each flow hop (exponential)',
986
+ ' 8. `kuma_memory({ action: "decision" })` \u2014 When choosing (preserves rationale)',
987
+ ' 9. `kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
988
+ ' 10. `kuma_context({ action: "changes" })` \u2014 Review session activity',
989
+ "",
990
+ "\u{1F525} `arch_flow` + `gotcha` = EXPONENTIAL value. Each record saves 5-10 files next session.",
991
+ "\u{1F7E2} SKIP recording function/class/component nodes \u2014 grep/glob is faster.",
992
+ "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
958
993
  "",
959
994
  "\u26D4 Steps 5-10 are mandatory even for research-only sessions!",
960
995
  "",
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  ALL_CONFIG_TYPES,
9
9
  formatInitResults,
10
10
  runInit
11
- } from "./chunk-AJ7HHUDW.js";
11
+ } from "./chunk-ZIXY2COZ.js";
12
12
  import {
13
13
  formatDecisionTemplate,
14
14
  getProactiveMemories,
@@ -336,7 +336,7 @@ async function handleResearch(params) {
336
336
  lines.push(" \u26A0\uFE0F Graph query failed");
337
337
  }
338
338
  try {
339
- const { scanCodebase } = await import("./kumaCodeScanner-QYQUL4W6.js");
339
+ const { scanCodebase } = await import("./kumaCodeScanner-2RDHZ5F4.js");
340
340
  const scanResult = await scanCodebase({ scope, maxFiles: 100 });
341
341
  if (scanResult.nodeCount > 0 || scanResult.edgeCount > 0) {
342
342
  lines.push(` \u{1F52C} Auto-scanned ${scanResult.filesScanned} files \u2192 ${scanResult.nodeCount} nodes, ${scanResult.edgeCount} edges`);
@@ -670,7 +670,13 @@ var MEMORY_ALIASES = {
670
670
  // Skills synonyms
671
671
  "skills": "skills",
672
672
  "distilled-skills": "skills",
673
- "skill-list": "skills"
673
+ "skill-list": "skills",
674
+ // Add node synonyms
675
+ "add_node": "add_node",
676
+ "add-node": "add_node",
677
+ "node": "add_node",
678
+ "create-node": "add_node",
679
+ "record-node": "add_node"
674
680
  };
675
681
  async function handleMemory(params) {
676
682
  const rawAction = params.action || "session";
@@ -716,6 +722,8 @@ async function handleMemory(params) {
716
722
  return handleTrajectoryList(params);
717
723
  case "skills":
718
724
  return handleSkillsList(params);
725
+ case "add_node":
726
+ return handleAddNode(params);
719
727
  default:
720
728
  return `Unknown action "${action}".`;
721
729
  }
@@ -754,6 +762,17 @@ async function handleResearchSave(params) {
754
762
  validatedAt: (/* @__PURE__ */ new Date()).toISOString()
755
763
  });
756
764
  await saveResearchCache(scope, record, void 0, params.confidence);
765
+ try {
766
+ const { upsertNode, nodeId } = await import("./kumaGraph-GUQM75WL.js");
767
+ await upsertNode({ id: nodeId("file", scope), type: "file", name: scope });
768
+ await upsertNode({
769
+ id: `research::${scope}`,
770
+ type: "variable",
771
+ name: `research:${scope}`,
772
+ metadata: { confidence: params.confidence || 0.8 }
773
+ });
774
+ } catch {
775
+ }
757
776
  try {
758
777
  const researchDir = path2.join(getProjectRoot(), ".kuma", "research");
759
778
  if (!fs2.existsSync(researchDir)) fs2.mkdirSync(researchDir, { recursive: true });
@@ -955,6 +974,39 @@ async function handleSkillsList(_params) {
955
974
  const { listDistilledSkills } = await import("./kumaTrajectory-T6DXGOG4.js");
956
975
  return await listDistilledSkills();
957
976
  }
977
+ async function handleAddNode(params) {
978
+ const type = params.title;
979
+ const name = params.content;
980
+ const filePath = params.scope;
981
+ if (!type || !name) {
982
+ return "\u26A0\uFE0F `title` (node type: function/class/component/file/api_route/test) and `content` (node name) required.\nExample: kuma_memory({ action: 'add_node', title: 'function', content: 'sendMessage', scope: 'ChatService.ts' })";
983
+ }
984
+ const validTypes = ["function", "class", "component", "file", "api_route", "test"];
985
+ if (!validTypes.includes(type)) {
986
+ return `\u26A0\uFE0F Invalid type "${type}". Valid types: ${validTypes.join(", ")}`;
987
+ }
988
+ try {
989
+ const { upsertNode, nodeId, addEdge } = await import("./kumaGraph-GUQM75WL.js");
990
+ const nodeIdStr = filePath ? `${type}::${filePath}::${name}` : nodeId(type, name);
991
+ await upsertNode({
992
+ id: nodeIdStr,
993
+ type,
994
+ name,
995
+ filePath
996
+ });
997
+ if (filePath) {
998
+ const fileNodeId = nodeId("file", filePath);
999
+ await upsertNode({ id: fileNodeId, type: "file", name: filePath });
1000
+ try {
1001
+ await addEdge({ sourceId: fileNodeId, targetId: nodeIdStr, type: "contains" });
1002
+ } catch {
1003
+ }
1004
+ }
1005
+ return `\u2705 Node created: **${name}** (${type})${filePath ? ` \u2014 ${filePath}` : ""}`;
1006
+ } catch (err) {
1007
+ return `\u274C Failed to create node: ${err}`;
1008
+ }
1009
+ }
958
1010
  async function handleLayersSummary(_params) {
959
1011
  const { getLayersSummary } = await import("./domainRules-ED6EOFVY.js");
960
1012
  return getLayersSummary();
@@ -2190,9 +2242,9 @@ function registerAllTools(server) {
2190
2242
  );
2191
2243
  server.tool(
2192
2244
  "kuma_memory",
2193
- "**Call AFTER research/editing every session** (including research-only). Record INLINE as you work.\n\nRECORD INLINE (call IMMEDIATELY during research):\n\u2022 STEP 5: `research_save` \u2014 every read/grep that finds new files/functions. Don't wait!\n\u2022 STEP 6: `gotcha` \u2014 IMMEDIATELY when you discover bugs/quirks. Record INLINE, not at the end.\n\u2022 STEP 7: `arch_flow` \u2014 IMMEDIATELY after EACH flow hop trace. Record before next task.\n\u2022 STEP 8: `decision` \u2014 IMMEDIATELY when choosing between options or finding architecture patterns.\n\nON DISCOVERY (when you encounter something):\n\u2022 `domain_rules` \u2014 uncovered business rules? Save them!\n\u2022 `context` \u2014 need to inject extra notes? Use this!\n\u2022 `todo` \u2014 create persistent task items\n\u2022 `mine` \u2014 extract historical context from git log\n\nOther actions: session, heal, search, changes, benchmark, layers.",
2245
+ "**Call after research/editing** (including research-only). Record what matters, skip what doesn't.\n\n\u{1F534} MUST RECORD \u2014 High Impact (saves agent time next session):\n\u2022 STEP 5: `research_save` \u2014 every read/grep that finds new files. Creates searchable cache.\n\u2022 STEP 6: `gotcha` \u2014 IMMEDIATELY when you discover bugs/quirks. No re-research.\n\u2022 STEP 7: `arch_flow` \u2014 IMMEDIATELY after EACH flow hop. Saves reading 5-10 files.\n\u2022 STEP 8: `decision` \u2014 IMMEDIATELY when choosing between options. Preserves rationale.\n\n\u{1F7E2} SKIP using MCP (agent native tools are faster):\n\u2022 Function/class nodes \u2192 grep\n\u2022 Component/route nodes \u2192 glob or check directly\n\u2022 Import edges \u2192 read imports directly\n\u2022 Visual graph \u2192 for humans, not AI agents\n\nOther actions: session, heal, search, changes, benchmark, layers.",
2194
2246
  {
2195
- action: z.enum(["decision", "mine", "research_save", "session", "heal", "search", "changes", "todo", "context", "benchmark", "decision_log", "domain_rules", "arch_flow", "gotcha", "layers", "federated", "gen_test", "trajectory", "skills"]).describe("Memory action: decision=ADR, mine=mine git log & comments, research_save=save, session=summary, heal=repair, search=search, changes=log, todo=manage todos, context=inject notes, benchmark=capture/diff, decision_log=manage decisions, domain_rules=Layer 1 business rules (Issue #17), arch_flow=Layer 2 architecture flow (Issue #17), gotcha=Layer 3 known gotchas (Issue #17/#21), layers=all 3 layers summary, federated=resolve federated kuma:// URI (Issue #27), gen_test=generate test from trajectory (Issue #28), trajectory=list trajectories, skills=list distilled skills"),
2247
+ action: z.enum(["decision", "mine", "research_save", "session", "heal", "search", "changes", "todo", "context", "benchmark", "decision_log", "domain_rules", "arch_flow", "gotcha", "layers", "federated", "gen_test", "trajectory", "skills", "add_node"]).describe("Memory action: decision=ADR, mine=mine git log & comments, research_save=save (creates file + graph node), session=summary, heal=repair, search=search, changes=log, todo=manage todos, context=inject notes, benchmark=capture/diff, decision_log=manage decisions, domain_rules=Layer 1 business rules (Issue #17), arch_flow=Layer 2 architecture flow (Issue #17), gotcha=Layer 3 known gotchas (Issue #17/#21), layers=all 3 layers summary, federated=resolve federated kuma:// URI (Issue #27), gen_test=generate test from trajectory (Issue #28), trajectory=list trajectories, skills=list distilled skills, add_node=manually create function/class/component structural nodes"),
2196
2248
  scope: z.string().optional().describe("Scope for research_save/search/todo/context/mine/federated"),
2197
2249
  query: z.string().optional().describe("Search query for search action"),
2198
2250
  content: z.string().optional().describe("Content/notes for research_save / context"),
@@ -2571,7 +2623,7 @@ async function main() {
2571
2623
  });
2572
2624
  (async () => {
2573
2625
  try {
2574
- const { generateInitMdContent } = await import("./init-BC7FG4C2.js");
2626
+ const { generateInitMdContent } = await import("./init-RUKOPMX6.js");
2575
2627
  const fs7 = await import("fs");
2576
2628
  const path7 = await import("path");
2577
2629
  const initMdPath = path7.resolve(process.cwd(), ".kuma/init.md");
@@ -2588,7 +2640,7 @@ async function main() {
2588
2640
  (async () => {
2589
2641
  try {
2590
2642
  const { detectAgent, getSkillPath, getAgentLabel } = await import("./agentDetector-YOWQVJFR.js");
2591
- const { generateSkill, getSecondaryFiles } = await import("./skillGenerator-GYEDLFZP.js");
2643
+ const { generateSkill, getSecondaryFiles } = await import("./skillGenerator-PWEJKZNX.js");
2592
2644
  const fs7 = await import("fs");
2593
2645
  const path7 = await import("path");
2594
2646
  const detection = detectAgent();
@@ -4,7 +4,7 @@ import {
4
4
  formatInitResults,
5
5
  generateInitMdContent,
6
6
  runInit
7
- } from "./chunk-AJ7HHUDW.js";
7
+ } from "./chunk-ZIXY2COZ.js";
8
8
  import "./chunk-E2KFPEBT.js";
9
9
  export {
10
10
  ALL_CONFIG_TYPES,
@@ -27,11 +27,12 @@ var CLASS_RE = /(?:export\s+)?(?:abstract\s+)?class\s+(\w+)(?:\s+extends\s+(\w+)
27
27
  var IMPORT_RE = /import\s+(?:\{([^}]*)\}\s+from\s+)?(?:\w+\s+from\s+)?(?:\*\s+as\s+\w+\s+from\s+)?['"]([^'"]+)['"]/g;
28
28
  var JSX_RETURN_RE = /return\s*\(?\s*</;
29
29
  var JSX_ELEMENT_RE = /<([A-Z]\w+)[\s/>]/g;
30
+ var JSX_IMPLICIT_RE = /=>\s*(?:\(\s*)?</;
30
31
  var EXPRESS_ROUTE_RE = /\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]\s*,\s*(\w+)/g;
31
32
  var HONO_ROUTE_RE = /c\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/g;
32
- var TEST_PATTERNS = [".test.", ".spec.", "__tests__", "/test/"];
33
33
  var EXPORT_RE = /export\s+(?:default\s+)?(\w+)/g;
34
34
  var CALL_RE = /(\w+)\s*\(/g;
35
+ var TEST_PATTERNS = [".test.", ".spec.", "__tests__", "/test/"];
35
36
  var knownComponents = /* @__PURE__ */ new Set();
36
37
  var knownFunctions = /* @__PURE__ */ new Set();
37
38
  var symbolLocations = /* @__PURE__ */ new Map();
@@ -236,11 +237,9 @@ function parseFile(filePath, content) {
236
237
  }
237
238
  }
238
239
  }
239
- const JSX_IMPLICIT_RE = /=>\s*(?:\(\s*)?</;
240
240
  if (hasJSX) {
241
241
  for (const fn of [...result.functions]) {
242
242
  const fnStartLine = fn.line - 1;
243
- const fnLine = lines[fnStartLine];
244
243
  const endLine = Math.min(fnStartLine + 50, lines.length);
245
244
  let isComponent = false;
246
245
  for (let i = fnStartLine; i < endLine; i++) {
@@ -249,12 +248,10 @@ function parseFile(filePath, content) {
249
248
  break;
250
249
  }
251
250
  }
252
- if (!isComponent && JSX_IMPLICIT_RE.test(fnLine)) isComponent = true;
253
- if (!isComponent && fnLine.includes("=>") && fnStartLine + 1 < endLine) {
251
+ if (!isComponent && JSX_IMPLICIT_RE.test(lines[fnStartLine])) isComponent = true;
252
+ if (!isComponent && fnStartLine + 1 < endLine) {
254
253
  const nextLine = lines[fnStartLine + 1].trim();
255
- if (nextLine.startsWith("<") || nextLine.startsWith("(") || nextLine.startsWith("<>")) {
256
- isComponent = true;
257
- }
254
+ if (nextLine.startsWith("<") || nextLine.startsWith("(") || nextLine.startsWith("<>")) isComponent = true;
258
255
  }
259
256
  if (isComponent) {
260
257
  result.components.push({ name: fn.name, line: fn.line });
@@ -10,14 +10,16 @@ var BOOTSTRAP = [
10
10
  ' 2. `kuma_safety({ action: "guard" })` \u2014 Safety check before work',
11
11
  ' 3. `kuma_context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
12
12
  " 4. *(edit/read using native tools)*",
13
- ' 5. `kuma_memory({ action: "research_save", ... })` \u2014 Save findings (inline after every read/grep)',
14
- ' 6. `kuma_memory({ action: "gotcha" })` \u2014 Record bugs/quirks (inline when found)',
15
- ' 7. `kuma_memory({ action: "arch_flow" })` \u2014 Record data flow (inline after each hop)',
16
- ' 8. `kuma_memory({ action: "decision" })` \u2014 Record key decisions (inline when choosing)',
13
+ ' 5. `kuma_memory({ action: "research_save", ... })` \u2014 After exploring area (creates search cache)',
14
+ ' 6. `kuma_memory({ action: "gotcha" })` \u2014 \u{1F525} IMMEDIATELY when bug found (exponential value)',
15
+ ' 7. `kuma_memory({ action: "arch_flow" })` \u2014 \u{1F525} IMMEDIATELY after each flow hop (exponential value)',
16
+ ' 8. `kuma_memory({ action: "decision" })` \u2014 When choosing between options (preserves rationale)',
17
17
  ' 9. `kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
18
18
  ' 10. `kuma_context({ action: "changes" })` \u2014 Review session activity',
19
19
  "",
20
- "\u{1F504} Record findings INLINE as you work \u2014 Every read/grep = mini research_save.",
20
+ "\u{1F525} **Power Curve:** `arch_flow` + `gotcha` are EXPONENTIAL \u2014 each record saves 5-10 files next session.",
21
+ " The more you record, the more overpowered the agent becomes. These are the highest-value actions.",
22
+ "\u{1F7E2} **SKIP** recording function/class/component nodes \u2014 grep/glob is faster.",
21
23
  "\u{1F9E0} Knowledge graph is persistent \u2014 nodes/edges accumulate across sessions, getting richer over time.",
22
24
  "",
23
25
  "\u26D4 Do NOT skip steps 5-10 after research \u2014 even if no code was changed.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumpslabs/kuma",
3
- "version": "2.3.17",
3
+ "version": "2.3.18",
4
4
  "description": "Safety-first context & orchestration engine for AI coding agents. MCP server with mandatory research pipeline, knowledge graph, impact analysis, decision memory, and safety guard — works with any MCP client.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",