@plumpslabs/kuma 2.3.16 → 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,8 +499,85 @@ 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
  "",
505
+ "## \u{1F9E0} How the Scanner Works",
506
+ "",
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.",
513
+ "",
514
+ "**What auto-scanner detects (line-by-line regex, TS/JS only):**",
515
+ "- `function` declarations (named + async + generator)",
516
+ "- Arrow function assignments (`const foo = (...) => ...`)",
517
+ "- Typed arrow functions (`const foo: Type = (...) => ...`)",
518
+ "- Class declarations (includes extends/implements)",
519
+ "- React/Vue components (functions returning JSX)",
520
+ "- API route handlers (Express + Hono patterns)",
521
+ "- Import statements (local file imports only)",
522
+ "- Function calls cross-file (`calls` edges)",
523
+ "- Test files (`.test.`, `.spec.`, `__tests__/`)",
524
+ "",
525
+ "**Known limitations:**",
526
+ "- Regex-based: can miss multi-line constructs or non-standard patterns",
527
+ "- Complex generics or deeply nested syntax may not match",
528
+ "- TypeScript advanced types may be partially parsed",
529
+ "- Scanner runs during `kuma_context research` step 3 (max 200 files)",
530
+ "- If scanner returns 0 structural nodes, the regex patterns didn't match your code style",
531
+ "",
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.",
536
+ "",
537
+ "---",
538
+ "",
539
+ "## \u{1F4BE} How to Retrieve Past Research \u2014 research_save \u2192 search",
540
+ "",
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.",
543
+ "",
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" })`',
549
+ "",
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.",
553
+ "",
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',
557
+ '- `kuma_context({ action: "init" })` \u2014 shows graph stats + recent research + health score',
558
+ '- `kuma_context({ action: "drift" })` \u2014 check if memories are stale vs actual code',
559
+ '- `kuma_context({ action: "changes" })` \u2014 view session activity log',
560
+ '- `kuma_context({ action: "visualize" })` \u2014 (human debugging) Mermaid diagram of the graph',
561
+ "",
562
+ "---",
563
+ "",
564
+ "## \u{1F52C} How Verification Works",
565
+ "",
566
+ '`kuma_safety({ action: "verify", scope: "<area>" })` \u2014 STEP 9',
567
+ "",
568
+ "**Auto-detection logic:**",
569
+ "1. Checks `package.json` \u2192 `scripts.test` \u2192 uses npm/pnpm/yarn test",
570
+ "2. If no test script, checks for Makefile with `test:` target",
571
+ "3. Falls back to Python (pytest), Rust (cargo test), Go (go test)",
572
+ "4. Last resort: `npx tsc --noEmit` (TypeScript syntax check) or `node -c`",
573
+ "5. If nothing found: returns informative message instead of failing",
574
+ "",
575
+ "**Important:** Verification is NEVER auto-triggered. You must call it explicitly.",
576
+ "The 5-layer safety guard (concurrency lock, rate limiting, staleness cache,",
577
+ "runaway detection, hard timeout) prevents resource exhaustion.",
578
+ "",
579
+ "---",
580
+ "",
477
581
  "## \u{1F6AB} What Kuma Does NOT Do (Use Agent Native Tools)",
478
582
  "",
479
583
  "| Function | Correct Tool |",
@@ -526,14 +630,15 @@ function handleOpencodeSecondary(root, results) {
526
630
  ' 2. `kuma_kuma_safety({ action: "guard" })` \u2014 Safety check before work',
527
631
  ' 3. `kuma_kuma_context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
528
632
  " 4. *(edit/read using native tools)*",
529
- ' 5. `kuma_kuma_memory({ action: "research_save", ... })` \u2014 Save findings (inline after every read/grep)',
530
- ' 6. `kuma_kuma_memory({ action: "gotcha" })` \u2014 Record bugs/quirks (inline when found)',
531
- ' 7. `kuma_kuma_memory({ action: "arch_flow" })` \u2014 Record data flow (inline after each hop)',
532
- ' 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)',
533
637
  ' 9. `kuma_kuma_safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
534
638
  ' 10. `kuma_kuma_context({ action: "changes" })` \u2014 Review session activity',
535
639
  "",
536
- "\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.",
537
642
  "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
538
643
  "",
539
644
  "\u26A0\uFE0F **OpenCode platform note:** Tool names are prefixed with `kuma_kuma_*`",
@@ -657,14 +762,15 @@ function getCombinedAgentsMd(selectedTypes) {
657
762
  ' 2. `[kuma_]safety({ action: "guard" })` \u2014 Safety check before editing',
658
763
  ' 3. `[kuma_]context({ action: "research", scope: "<area>" })` \u2014 Research before editing',
659
764
  " 4. *(edit/read using native tools)*",
660
- ' 5. `[kuma_]memory({ action: "research_save", ... })` \u2014 Save all findings',
661
- ' 6. `[kuma_]memory({ action: "gotcha" })` \u2014 Record bugs/quirks found',
662
- ' 7. `[kuma_]memory({ action: "arch_flow" })` \u2014 Record data flow if mapped',
663
- ' 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)',
664
769
  ' 9. `[kuma_]safety({ action: "verify", ... })` \u2014 Run tests / confirm nothing broken',
665
770
  ' 10. `[kuma_]context({ action: "changes" })` \u2014 Review session activity',
666
771
  "",
667
- "\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.",
668
774
  "\u{1F9E0} Graph is persistent \u2014 nodes/edges accumulate across sessions.",
669
775
  "",
670
776
  "\u26D4 Do NOT skip steps 5-10 after research \u2014 even if no code was changed.",
@@ -874,15 +980,16 @@ function handleQuickrefGeneration(root, results) {
874
980
  ' 2. `kuma_safety({ action: "guard" })` \u2014 Safety check',
875
981
  ' 3. `kuma_context({ action: "research", scope: "<area>" })` \u2014 Research',
876
982
  " 4. *(edit/read using native tools)*",
877
- ' 5. `kuma_memory({ action: "research_save", ... })` \u2014 Save findings (inline after every read)',
878
- ' 6. `kuma_memory({ action: "gotcha" })` \u2014 Gotchas (inline when found)',
879
- ' 7. `kuma_memory({ action: "arch_flow" })` \u2014 Arch flow (inline after each hop)',
880
- ' 8. `kuma_memory({ action: "decision" })` \u2014 Decision (inline when choosing)',
881
- ' 9. `kuma_safety({ action: "verify", ... })` \u2014 Verify',
882
- ' 10. `kuma_context({ action: "changes" })` \u2014 Review changes',
883
- "",
884
- "\u{1F504} Record INLINE as you work \u2014 Every read/grep = mini research_save.",
885
- "\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.",
886
993
  "",
887
994
  "\u26D4 Steps 5-10 are mandatory even for research-only sessions!",
888
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-S7BQGNHQ.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-LFJGPJNH.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();
@@ -2149,7 +2201,7 @@ async function handleVerify(params) {
2149
2201
  }
2150
2202
  _lastVerifyCall = now;
2151
2203
  sessionMemory.recordToolCall("kuma_safety_verify", { scope: params.scope || params.filePath });
2152
- const { runAutoVerification } = await import("./kumaVerifier-KAYJHR5K.js");
2204
+ const { runAutoVerification } = await import("./kumaVerifier-6NTJQU7B.js");
2153
2205
  return await runAutoVerification({
2154
2206
  scope: params.scope || params.filePath,
2155
2207
  force: params.force,
@@ -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"),
@@ -2362,7 +2414,7 @@ async function main() {
2362
2414
  console.error("");
2363
2415
  let killedCount = 0;
2364
2416
  try {
2365
- const { getRunningVerificationPid } = await import("./kumaVerifier-KAYJHR5K.js");
2417
+ const { getRunningVerificationPid } = await import("./kumaVerifier-6NTJQU7B.js");
2366
2418
  const pid = getRunningVerificationPid();
2367
2419
  if (pid) {
2368
2420
  try {
@@ -2571,7 +2623,7 @@ async function main() {
2571
2623
  });
2572
2624
  (async () => {
2573
2625
  try {
2574
- const { generateInitMdContent } = await import("./init-P3DAC23E.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-S7BQGNHQ.js";
7
+ } from "./chunk-ZIXY2COZ.js";
8
8
  import "./chunk-E2KFPEBT.js";
9
9
  export {
10
10
  ALL_CONFIG_TYPES,
@@ -21,17 +21,18 @@ function getRoot() {
21
21
  return _cachedRoot;
22
22
  }
23
23
  var FUNCTION_DECL_RE = /(?:export\s+)?(?:async\s+)?function\s*(?:\*\s*)?(\w+)\s*\(/g;
24
- var ARROW_FN_RE = /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*(?::\s*\w+(?:<[^>]*>)?)?\s*=>/g;
25
- var TYPED_ARROW_RE = /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*:\s*(?:\w+(?:<[^>]*>)?)?\s*=\s*(?:async\s*)?\(/g;
24
+ var ARROW_FN_RE = /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*[=:]\s*(?:async\s*)?(?:<[^>]+>\s*)?(?:\([\s\S]*?\)|\w+)\s*(?::\s*\w+(?:<[^>]*>)?)?\s*=>/g;
25
+ var TYPED_ARROW_RE = /(?:export\s+)?(?:const|let|var)\s+(\w+)\s*:\s*(?:\w+(?:<[^>]*>)?)?\s*=\s*(?:async\s*)?(?:<[^>]+>\s*)?\(/g;
26
26
  var CLASS_RE = /(?:export\s+)?(?:abstract\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w,\s]+))?/g;
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 });
@@ -93,7 +93,14 @@ function detectTestRunner(root = process.cwd()) {
93
93
  if (fs.existsSync(path.join(root, "pytest.ini")) || fs.existsSync(path.join(root, "pyproject.toml"))) return { runner: "pytest", baseCommand: "pytest" };
94
94
  if (fs.existsSync(path.join(root, "Cargo.toml"))) return { runner: "cargo", baseCommand: "cargo test" };
95
95
  if (fs.existsSync(path.join(root, "go.mod"))) return { runner: "go", baseCommand: "go test ./..." };
96
- if (fs.existsSync(path.join(root, "Makefile"))) return { runner: "make", baseCommand: "make test" };
96
+ if (fs.existsSync(path.join(root, "Makefile"))) {
97
+ try {
98
+ const makefileContent = fs.readFileSync(path.join(root, "Makefile"), "utf-8");
99
+ const hasTestTarget = /^test:/m.test(makefileContent) || /^\s+test:/.test(makefileContent);
100
+ if (hasTestTarget) return { runner: "make", baseCommand: "make test" };
101
+ } catch {
102
+ }
103
+ }
97
104
  if (fs.existsSync(path.join(root, "package.json"))) {
98
105
  try {
99
106
  const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
@@ -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.16",
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",