@plumpslabs/kuma 2.3.34 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +15 -22
  2. package/dist/index.js +240 -610
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -89,7 +89,6 @@ Kuma provides **3 coarse-grained tools** with impactful actions. Removed gimmick
89
89
  | `verify` | Auto-run scoped tests after edits | 🔴 High |
90
90
  | `check` | Pre-execution safety check | 🟡 Linear |
91
91
  | `audit` | Query safety audit trail | 🟡 Linear |
92
- | `health` | Project health score (optional/cosmetic) | 🟢 Skip |
93
92
  | `security` | Scan for leaked secrets/tokens | 🟡 Linear |
94
93
  | `gc` | Garbage collect stale data | 🟢 Skip |
95
94
  | `ast` / `validate` | AST-based code validation | 🟡 Linear |
@@ -98,32 +97,26 @@ Kuma provides **3 coarse-grained tools** with impactful actions. Removed gimmick
98
97
 
99
98
  ---
100
99
 
101
- ## V3 Changes: What's New
100
+ ## Architecture
102
101
 
103
- Kuma V3 is a major evolution focusing on **simplicity, safety, and agent-native workflows**.
102
+ Kuma exposes exactly **3 coarse-grained tools** — the agent picks an *action*, Kuma runs the internal workflow:
104
103
 
105
- ### Dropped Tools (Now Handled by Agents)
104
+ | Tool | Core Actions | Purpose |
105
+ |------|--------------|---------|
106
+ | `kuma_context` | `init`, `research`, `history` | Load project context, understand unfamiliar code |
107
+ | `kuma_memory` | `gotcha`, `decision`, `arch_flow`, `research_save` | Persistent knowledge that saves future sessions |
108
+ | `kuma_safety` | `guard`, `verify` | Pre-risk check, post-edit verification |
106
109
 
107
- | Dropped Tool | Why Dropped |
108
- |--------------|-------------|
109
- | `precise_diff_editor` | Agent has native edit tools |
110
- | `safe_terminal_exec` | Agent executes commands natively |
111
- | `smart_grep` | Agent searches natively (ripgrep, semantic) |
112
- | `batch_file_writer` | Agent creates files natively |
113
- | `ast_validator` (standalone) | Merged into `kuma_safety` as `ast`/`validate` action |
114
- | `git_diff_analyzer` | Merged into `kuma_context` as `impact` action |
110
+ Everything else is an internal action for power users. The agent uses its **own native tools** for editing, searching, and execution — Kuma is memory & safety, not a code manager.
115
111
 
116
- ### New Features in V3
112
+ ### What Kuma Provides
117
113
 
118
- | Feature | Description |
119
- |---------|-------------|
120
- | **3 Coarse-Grained Tools** | Simplified API: `kuma_context`, `kuma_memory`, `kuma_safety` |
121
- | **Pipeline-Driven Actions** | Each action triggers internal multi-step workflows |
122
- | **Knowledge Graph** | SQLite + FTS5 full-text search with WASM engine (derived cache) |
123
- | **Session Memory** | Track tool calls, recordings, and efficiency per session |
124
- | **Guard System** | Real-time monitoring with blocking warnings for anti-patterns |
125
- | **Kuma Studio** | Visual dashboard with graph, gotchas, and injection metrics |
126
- | **Checkpoint/Rollback** | Atomic snapshots before major refactors |
114
+ - **Knowledge Graph** — SQLite + FTS5 full-text search (derived cache)
115
+ - **Session Memory** — track tool calls, recordings, and efficiency per session
116
+ - **Guard System** — real-time monitoring with blocking warnings for anti-patterns
117
+ - **Shadow Injection** — gotchas injected before edits via hooks (zero token waste when clean)
118
+ - **Kuma Studio** — visual dashboard with graph, gotchas, and injection metrics
119
+ - **Checkpoint/Rollback** — atomic snapshots before major refactors
127
120
 
128
121
  ---
129
122
 
package/dist/index.js CHANGED
@@ -246,7 +246,6 @@ __export(kumaDb_exports, {
246
246
  saveBenchmark: () => saveBenchmark,
247
247
  saveDb: () => saveDb,
248
248
  saveFileSummary: () => saveFileSummary,
249
- saveHealthSnapshot: () => saveHealthSnapshot,
250
249
  saveResearchCache: () => saveResearchCache,
251
250
  saveVerification: () => saveVerification,
252
251
  updateDecisionStatus: () => updateDecisionStatus
@@ -442,14 +441,6 @@ function createSchema(db) {
442
441
  current_content TEXT DEFAULT NULL,
443
442
  created_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
444
443
  )`);
445
- db.run(`CREATE TABLE IF NOT EXISTS health_snapshots (
446
- id INTEGER PRIMARY KEY AUTOINCREMENT,
447
- score INTEGER NOT NULL,
448
- risk_level TEXT NOT NULL DEFAULT 'low',
449
- checks TEXT DEFAULT '[]',
450
- summary TEXT,
451
- created_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
452
- )`);
453
444
  try {
454
445
  db.run(`CREATE TABLE IF NOT EXISTS safety_audit (
455
446
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -698,7 +689,6 @@ function createSchema(db) {
698
689
  db.run(`CREATE INDEX IF NOT EXISTS idx_change_log_session ON change_log(session_id)`);
699
690
  db.run(`CREATE INDEX IF NOT EXISTS idx_change_log_file ON change_log(file_path)`);
700
691
  db.run(`CREATE INDEX IF NOT EXISTS idx_change_log_created ON change_log(created_at)`);
701
- db.run(`CREATE INDEX IF NOT EXISTS idx_health_created ON health_snapshots(created_at)`);
702
692
  db.run(`CREATE INDEX IF NOT EXISTS idx_todos_status ON todos(status)`);
703
693
  db.run(`CREATE INDEX IF NOT EXISTS idx_todos_scope ON todos(scope)`);
704
694
  db.run(`CREATE INDEX IF NOT EXISTS idx_security_file ON security_findings(file_path)`);
@@ -1091,7 +1081,7 @@ async function runGarbageCollection() {
1091
1081
  };
1092
1082
  const orphanResult = db.exec(`DELETE FROM nodes WHERE id NOT IN (SELECT source_id FROM edges UNION SELECT target_id FROM edges) AND file_path IS NULL AND updated_at < strftime('%s','now','-30 days')`);
1093
1083
  removed += orphanResult[0]?.values?.length || 0;
1094
- const staleEdges = db.exec(`DELETE FROM edges WHERE weight < 0.1 AND updated_at < strftime('%s','now','-30 days')`);
1084
+ const staleEdges = db.exec(`DELETE FROM edges WHERE weight < 0.1 AND created_at < strftime('%s','now','-30 days')`);
1095
1085
  removed += staleEdges[0]?.values?.length || 0;
1096
1086
  db.exec(`DELETE FROM tool_calls WHERE created_at < strftime('%s','now','-90 days')`);
1097
1087
  db.exec(`DELETE FROM experiences WHERE created_at < strftime('%s','now','-90 days')`);
@@ -1103,13 +1093,9 @@ async function runGarbageCollection() {
1103
1093
  if (verifCount > 100) {
1104
1094
  db.exec(`DELETE FROM verifications WHERE id IN (SELECT id FROM verifications ORDER BY created_at ASC LIMIT ${verifCount - 100})`);
1105
1095
  }
1106
- const healthCount = getCount("SELECT COUNT(*) FROM health_snapshots");
1107
- if (healthCount > 50) {
1108
- db.exec(`DELETE FROM health_snapshots WHERE id IN (SELECT id FROM health_snapshots ORDER BY created_at ASC LIMIT ${healthCount - 50})`);
1109
- }
1110
1096
  const auditCount = getCount("SELECT COUNT(*) FROM safety_audit");
1111
1097
  if (auditCount > 200) {
1112
- db.exec(`DELETE FROM safety_audit WHERE id IN (SELECT id FROM safety_audit ORDER BY created_at ASC LIMIT ${auditCount - 200})`);
1098
+ db.exec(`DELETE FROM safety_audit WHERE id IN (SELECT id FROM safety_audit ORDER BY timestamp ASC LIMIT ${auditCount - 200})`);
1113
1099
  }
1114
1100
  db.exec(`DELETE FROM research_cache WHERE updated_at < strftime('%s','now','-60 days')`);
1115
1101
  const secCount = getCount("SELECT COUNT(*) FROM security_findings");
@@ -1163,7 +1149,7 @@ async function runDoctor() {
1163
1149
  } catch {
1164
1150
  checks.push("**Database Integrity**: \u274C check failed");
1165
1151
  }
1166
- const allTables = ["nodes", "edges", "sessions", "research_cache", "change_log", "safety_audit", "todos", "security_findings", "context_notes", "benchmarks", "decision_log", "file_summaries", "verifications", "health_snapshots", "tool_calls", "experiences", "experience_patterns", "patterns", "api_endpoints", "otel_config", "cost_tracking", "scratch_entries", "portability_entries", "blackboard_events", "known_gotchas", "trajectories", "distilled_skills"];
1152
+ const allTables = ["nodes", "edges", "sessions", "research_cache", "change_log", "safety_audit", "todos", "security_findings", "context_notes", "benchmarks", "decision_log", "file_summaries", "verifications", "tool_calls", "experiences", "experience_patterns", "patterns", "api_endpoints", "otel_config", "cost_tracking", "scratch_entries", "portability_entries", "blackboard_events", "known_gotchas", "trajectories", "distilled_skills"];
1167
1153
  let existing = 0;
1168
1154
  for (const t of allTables) {
1169
1155
  try {
@@ -1266,18 +1252,6 @@ async function ensureGitignore() {
1266
1252
  return `\u274C Failed: ${err}`;
1267
1253
  }
1268
1254
  }
1269
- async function saveHealthSnapshot(score, riskLevel, checks, summary) {
1270
- try {
1271
- const db = await getDb();
1272
- db.run(
1273
- `INSERT INTO health_snapshots (score, risk_level, checks, summary) VALUES (?, ?, ?, ?)`,
1274
- [score, riskLevel, checks, summary]
1275
- );
1276
- saveDb();
1277
- } catch (err) {
1278
- console.error(`[KumaDB] Failed to save health snapshot: ${err}`);
1279
- }
1280
- }
1281
1255
  async function saveVerification(scope, runner, command, passed, output, durationMs = 0) {
1282
1256
  try {
1283
1257
  const db = await getDb();
@@ -2078,7 +2052,7 @@ No unresolved issues.`;
2078
2052
  };
2079
2053
  }
2080
2054
  // ============================================================
2081
- // V3: Change Tracking (Selective Undo)
2055
+ // Change Tracking (Selective Undo)
2082
2056
  // ============================================================
2083
2057
  /**
2084
2058
  * Track a file change with symbol-level detail.
@@ -3059,7 +3033,6 @@ async function clearGraph() {
3059
3033
  db.run("DELETE FROM edges;");
3060
3034
  db.run("DELETE FROM known_gotchas;");
3061
3035
  db.run("DELETE FROM trajectories;");
3062
- db.run("DELETE FROM health_snapshots;");
3063
3036
  db.run("DELETE FROM research_cache;");
3064
3037
  flushDb(db);
3065
3038
  return 0;
@@ -3878,6 +3851,117 @@ var init_kumaGraph = __esm({
3878
3851
  }
3879
3852
  });
3880
3853
 
3854
+ // src/utils/kumaShared.ts
3855
+ import { execSync as execSync3 } from "child_process";
3856
+ function isEditTool(toolName) {
3857
+ const n = (toolName || "").toLowerCase();
3858
+ return n.includes("edit") || n.includes("write") || n.includes("patch") || n.includes("apply") || n.includes("replace");
3859
+ }
3860
+ function isTestTool(toolName) {
3861
+ const n = (toolName || "").toLowerCase();
3862
+ return n.includes("test") || n.includes("typecheck");
3863
+ }
3864
+ function isReadTool(toolName) {
3865
+ const n = (toolName || "").toLowerCase();
3866
+ return n.includes("read") || n.includes("grep") || n.includes("glob") || n.includes("search") || n.includes("list");
3867
+ }
3868
+ function isBashTool(toolName) {
3869
+ const n = (toolName || "").toLowerCase();
3870
+ return n === "bash" || n.startsWith("bash") || n.includes("command") || n.includes("shell") || n.includes("terminal");
3871
+ }
3872
+ function getSessionStats(inputGoal) {
3873
+ const summary = sessionMemory.getSummary();
3874
+ const goal = inputGoal || summary.currentGoal || "";
3875
+ const modifiedFiles = sessionMemory.getModifiedFiles();
3876
+ const toolCalls = sessionMemory.getToolCallHistory(50);
3877
+ const failedFiles = sessionMemory.getFailedFiles();
3878
+ const loop = sessionMemory.detectLoop();
3879
+ return {
3880
+ goal,
3881
+ modifiedFiles,
3882
+ toolCalls,
3883
+ toolCallCount: toolCalls.length,
3884
+ failedFiles,
3885
+ hasLoop: loop.isLooping,
3886
+ loopMessage: loop.message,
3887
+ hasRunTests: toolCalls.some((c) => isTestTool(c.toolName))
3888
+ };
3889
+ }
3890
+ function getGitDiffStat(timeout = 3e3) {
3891
+ try {
3892
+ const root = getProjectRoot();
3893
+ return execSync3("git diff --stat", {
3894
+ cwd: root,
3895
+ encoding: "utf-8",
3896
+ timeout
3897
+ }).trim();
3898
+ } catch {
3899
+ return "";
3900
+ }
3901
+ }
3902
+ function getUnresolvedCount(failedFiles) {
3903
+ let count = 0;
3904
+ for (const f of failedFiles) {
3905
+ for (const ff of f.failures) {
3906
+ if (!ff.resolved) count++;
3907
+ }
3908
+ }
3909
+ return count;
3910
+ }
3911
+ function buildDriftMessages(modifiedFiles, hasRunTests, unresolvedCount, gitStat, loopMessage) {
3912
+ const drifts = [];
3913
+ if (modifiedFiles > 0 && !hasRunTests) {
3914
+ drifts.push(`${modifiedFiles} file(s) edited but no test run`);
3915
+ }
3916
+ if (loopMessage) {
3917
+ drifts.push(loopMessage);
3918
+ }
3919
+ if (unresolvedCount > 0) {
3920
+ drifts.push(`${unresolvedCount} unresolved failure(s)`);
3921
+ }
3922
+ if (gitStat) {
3923
+ drifts.push(`Git diff: ${gitStat}`);
3924
+ }
3925
+ return drifts;
3926
+ }
3927
+ function getPrioritySuggestion(goal, warnings, hasLoop, unresolvedCount, modifiedFiles, hasRunTests, editCalls) {
3928
+ if (warnings.some((w) => w.severity === "high" && w.pattern === "script-patching")) {
3929
+ return "Remove patch scripts and use your native edit tools for all file modifications";
3930
+ }
3931
+ if (hasLoop) {
3932
+ return "Switch approach \u2014 current tool is not making progress";
3933
+ }
3934
+ if (warnings.some((w) => w.pattern === "no-test-after-edit") || modifiedFiles > 0 && !hasRunTests) {
3935
+ return "Run tests to verify your changes before continuing";
3936
+ }
3937
+ if (unresolvedCount > 0) {
3938
+ return "Fix unresolved failures before continuing";
3939
+ }
3940
+ if (warnings.some((w) => w.pattern === "bash-grep")) {
3941
+ return "Use your native grep tool for code search instead of bash grep";
3942
+ }
3943
+ if (warnings.some((w) => w.pattern === "excessive-edits") || editCalls > 10) {
3944
+ return "Consider if refactoring can be simplified \u2014 fewer files = fewer bugs";
3945
+ }
3946
+ if (!goal) {
3947
+ return "No goal set \u2014 use goal parameter or setGoal to track intent";
3948
+ }
3949
+ if (modifiedFiles === 0) {
3950
+ return "Start by exploring what exists before writing code";
3951
+ }
3952
+ return "On track \u2014 continue with current approach";
3953
+ }
3954
+ function countEditCalls(toolCalls) {
3955
+ return toolCalls.filter((c) => isEditTool(c.toolName)).length;
3956
+ }
3957
+ var init_kumaShared = __esm({
3958
+ "src/utils/kumaShared.ts"() {
3959
+ "use strict";
3960
+ init_sessionMemory();
3961
+ init_pathValidator();
3962
+ }
3963
+ });
3964
+
3881
3965
  // src/engine/kumaMemory.ts
3882
3966
  var kumaMemory_exports = {};
3883
3967
  __export(kumaMemory_exports, {
@@ -4001,7 +4085,7 @@ async function recordDecisionToGraph(decision) {
4001
4085
  }
4002
4086
  function shouldRecordDecision() {
4003
4087
  const history = sessionMemory.getToolCallHistory(30);
4004
- const edits = history.filter((c) => c.toolName === "precise_diff_editor");
4088
+ const edits = history.filter((c) => isEditTool(c.toolName));
4005
4089
  if (edits.length > decisionCooldown + 10 && edits.length >= 10) {
4006
4090
  decisionCooldown = edits.length;
4007
4091
  return {
@@ -4047,6 +4131,7 @@ var init_kumaMemory = __esm({
4047
4131
  "use strict";
4048
4132
  init_sessionMemory();
4049
4133
  init_pathValidator();
4134
+ init_kumaShared();
4050
4135
  MEMORY_DIR = ".kuma/memories";
4051
4136
  decisionCooldown = 0;
4052
4137
  }
@@ -5731,7 +5816,7 @@ function formatScanResult(result) {
5731
5816
  lines.push(
5732
5817
  "",
5733
5818
  "\u{1F4A1} The knowledge graph now has richer code structure data.",
5734
- "\u{1F4A1} Use kuma_context({ action: 'visualize' }) to see the graph."
5819
+ "\u{1F4A1} Use kuma_context({ action: 'digest' }) to see the graph overview."
5735
5820
  );
5736
5821
  return lines.join("\n");
5737
5822
  }
@@ -6075,7 +6160,7 @@ async function generateContextDigest() {
6075
6160
  if (estimatedTokens > 450) {
6076
6161
  lines.push(`\u{1F4CA} *~${estimatedTokens} tokens, fits under 500 limit*`);
6077
6162
  }
6078
- return lines.join("\n") + "\n\n\u{1F4A1} Use kuma_context({ action: 'sync' }) for full state.";
6163
+ return lines.join("\n") + "\n\n\u{1F4A1} Use kuma_context({ action: 'digest' }) for the full state.";
6079
6164
  }
6080
6165
  var init_contextDigest = __esm({
6081
6166
  "src/engine/contextDigest.ts"() {
@@ -6497,7 +6582,7 @@ __export(kumaMiner_exports, {
6497
6582
  });
6498
6583
  import fs17 from "fs";
6499
6584
  import path18 from "path";
6500
- import { execSync as execSync3 } from "child_process";
6585
+ import { execSync as execSync4 } from "child_process";
6501
6586
  import fastGlob2 from "fast-glob";
6502
6587
  async function mineHistoricalDecisions(options = {}) {
6503
6588
  const root = process.cwd();
@@ -6506,7 +6591,7 @@ async function mineHistoricalDecisions(options = {}) {
6506
6591
  try {
6507
6592
  const sinceFlag = options.since ? `--since="${options.since}"` : `--since="1 year"`;
6508
6593
  const gitCmd = `git log ${sinceFlag} -n 100 --pretty=format:"%h|%an|%ad|%s" --date=short`;
6509
- const gitOutput = execSync3(gitCmd, { cwd: root, encoding: "utf-8", timeout: 15e3 });
6594
+ const gitOutput = execSync4(gitCmd, { cwd: root, encoding: "utf-8", timeout: 15e3 });
6510
6595
  const keywords = ["fix", "revert", "hack", "workaround", "urgent", "hotfix", "don't touch", "temporary", "deprecated", "workaround"];
6511
6596
  const lines = gitOutput.split("\n").filter(Boolean);
6512
6597
  const matchingHashes = [];
@@ -6525,7 +6610,7 @@ async function mineHistoricalDecisions(options = {}) {
6525
6610
  if (matchingHashes.length > 0) {
6526
6611
  try {
6527
6612
  const hashesStr = matchingHashes.map((h) => h.hash).join(" ");
6528
- const batchOutput = execSync3(
6613
+ const batchOutput = execSync4(
6529
6614
  `git show --name-only --oneline ${hashesStr} 2>/dev/null | head -200`,
6530
6615
  { cwd: root, encoding: "utf-8", timeout: 1e4, maxBuffer: 256 * 1024 }
6531
6616
  );
@@ -6552,7 +6637,7 @@ async function mineHistoricalDecisions(options = {}) {
6552
6637
  let changedFiles = changedFilesMap.get(hash) || "";
6553
6638
  if (!changedFiles) {
6554
6639
  try {
6555
- changedFiles = execSync3(`git show --name-only --oneline ${hash}`, { cwd: root, encoding: "utf-8", timeout: 5e3 }).split("\n").slice(1).filter(Boolean).slice(0, 3).join(", ");
6640
+ changedFiles = execSync4(`git show --name-only --oneline ${hash}`, { cwd: root, encoding: "utf-8", timeout: 5e3 }).split("\n").slice(1).filter(Boolean).slice(0, 3).join(", ");
6556
6641
  } catch {
6557
6642
  }
6558
6643
  candidates.push({
@@ -6659,403 +6744,6 @@ var init_kumaMiner = __esm({
6659
6744
  }
6660
6745
  });
6661
6746
 
6662
- // src/utils/kumaShared.ts
6663
- import { execSync as execSync6 } from "child_process";
6664
- function getSessionStats(inputGoal) {
6665
- const summary = sessionMemory.getSummary();
6666
- const goal = inputGoal || summary.currentGoal || "";
6667
- const modifiedFiles = sessionMemory.getModifiedFiles();
6668
- const toolCalls = sessionMemory.getToolCallHistory(50);
6669
- const failedFiles = sessionMemory.getFailedFiles();
6670
- const loop = sessionMemory.detectLoop();
6671
- return {
6672
- goal,
6673
- modifiedFiles,
6674
- toolCalls,
6675
- toolCallCount: toolCalls.length,
6676
- failedFiles,
6677
- hasLoop: loop.isLooping,
6678
- loopMessage: loop.message,
6679
- hasRunTests: toolCalls.some((c) => c.toolName === "execute_safe_test")
6680
- };
6681
- }
6682
- function getGitDiffStat2(timeout = 3e3) {
6683
- try {
6684
- const root = getProjectRoot();
6685
- return execSync6("git diff --stat", {
6686
- cwd: root,
6687
- encoding: "utf-8",
6688
- timeout
6689
- }).trim();
6690
- } catch {
6691
- return "";
6692
- }
6693
- }
6694
- function getUnresolvedCount(failedFiles) {
6695
- let count = 0;
6696
- for (const f of failedFiles) {
6697
- for (const ff of f.failures) {
6698
- if (!ff.resolved) count++;
6699
- }
6700
- }
6701
- return count;
6702
- }
6703
- function buildDriftMessages(modifiedFiles, hasRunTests, unresolvedCount, gitStat, loopMessage) {
6704
- const drifts = [];
6705
- if (modifiedFiles > 0 && !hasRunTests) {
6706
- drifts.push(`${modifiedFiles} file(s) edited but no test run`);
6707
- }
6708
- if (loopMessage) {
6709
- drifts.push(loopMessage);
6710
- }
6711
- if (unresolvedCount > 0) {
6712
- drifts.push(`${unresolvedCount} unresolved failure(s)`);
6713
- }
6714
- if (gitStat) {
6715
- drifts.push(`Git diff: ${gitStat}`);
6716
- }
6717
- return drifts;
6718
- }
6719
- var init_kumaShared = __esm({
6720
- "src/utils/kumaShared.ts"() {
6721
- "use strict";
6722
- init_sessionMemory();
6723
- init_pathValidator();
6724
- }
6725
- });
6726
-
6727
- // src/engine/safetyScore.ts
6728
- var safetyScore_exports = {};
6729
- __export(safetyScore_exports, {
6730
- computeSafetyScore: () => computeSafetyScore,
6731
- formatSafetyScore: () => formatSafetyScore
6732
- });
6733
- async function computeSafetyScore(inputGoal) {
6734
- const stats = getSessionStats(inputGoal);
6735
- const checks = [];
6736
- let totalScore = 0;
6737
- const maxScore = 100;
6738
- const gitStat = getGitDiffStat2();
6739
- if (gitStat && gitStat !== "clean") {
6740
- const lines = gitStat.split("\n").filter(Boolean).length;
6741
- if (lines === 0) {
6742
- checks.push({
6743
- label: "Git Clean",
6744
- status: "pass",
6745
- message: "Working tree is clean",
6746
- weight: 20
6747
- });
6748
- totalScore += 20;
6749
- } else if (lines <= 3) {
6750
- checks.push({
6751
- label: "Git Clean",
6752
- status: "warn",
6753
- message: `${lines} file(s) modified`,
6754
- weight: 15
6755
- });
6756
- totalScore += 15;
6757
- } else {
6758
- checks.push({
6759
- label: "Git Clean",
6760
- status: "warn",
6761
- message: `${lines} file(s) modified \u2014 consider committing or stashing`,
6762
- weight: 10
6763
- });
6764
- totalScore += 10;
6765
- }
6766
- } else if (gitStat === "clean") {
6767
- checks.push({
6768
- label: "Git Clean",
6769
- status: "pass",
6770
- message: "Working tree is clean",
6771
- weight: 20
6772
- });
6773
- totalScore += 20;
6774
- } else {
6775
- checks.push({
6776
- label: "Git Status",
6777
- status: "pass",
6778
- message: "Not a git repository or git unavailable",
6779
- weight: 20
6780
- });
6781
- totalScore += 20;
6782
- }
6783
- try {
6784
- const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
6785
- const db = await getDb2();
6786
- const nodeCount = db.exec("SELECT COUNT(*) as c FROM nodes")[0]?.values[0][0] ?? 0;
6787
- const edgeCount = db.exec("SELECT COUNT(*) as c FROM edges")[0]?.values[0][0] ?? 0;
6788
- if (nodeCount > 0) {
6789
- checks.push({
6790
- label: "Graph Health",
6791
- status: "pass",
6792
- message: `${nodeCount} nodes, ${edgeCount} edges`,
6793
- weight: 10
6794
- });
6795
- totalScore += 10;
6796
- } else {
6797
- checks.push({
6798
- label: "Graph Health",
6799
- status: "warn",
6800
- message: "Empty knowledge graph \u2014 run research first",
6801
- weight: 5
6802
- });
6803
- totalScore += 5;
6804
- }
6805
- } catch {
6806
- checks.push({
6807
- label: "Graph Health",
6808
- status: "warn",
6809
- message: "Could not check graph health",
6810
- weight: 5
6811
- });
6812
- totalScore += 5;
6813
- }
6814
- try {
6815
- const { getDb: getDb2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
6816
- const db = await getDb2();
6817
- const researchCount = db.exec("SELECT COUNT(*) as c FROM research_cache")[0]?.values[0][0] ?? 0;
6818
- checks.push({
6819
- label: "Research Cached",
6820
- status: researchCount > 0 ? "pass" : "warn",
6821
- message: researchCount > 0 ? `${researchCount} research scope(s) cached` : "No research cached \u2014 run kuma_context research first",
6822
- weight: 10
6823
- });
6824
- totalScore += researchCount > 0 ? 10 : 5;
6825
- } catch {
6826
- checks.push({
6827
- label: "Research Cached",
6828
- status: "warn",
6829
- message: "Could not check research cache",
6830
- weight: 5
6831
- });
6832
- totalScore += 5;
6833
- }
6834
- const unresolvedCount = getUnresolvedCount(stats.failedFiles);
6835
- const allFailures = stats.failedFiles.flatMap((f) => f.failures);
6836
- const testFailures = allFailures.filter((f) => f.error.toLowerCase().includes("test") || f.error.toLowerCase().includes("fail"));
6837
- const hasRunTests = stats.hasRunTests;
6838
- let latestVerif = null;
6839
- try {
6840
- const { getLatestVerifications: getLatestVerifications2 } = await Promise.resolve().then(() => (init_kumaDb(), kumaDb_exports));
6841
- const verifs = await getLatestVerifications2(1);
6842
- if (verifs.length > 0) latestVerif = verifs[0];
6843
- } catch {
6844
- }
6845
- if (!hasRunTests && !latestVerif) {
6846
- checks.push({
6847
- label: "Tests Status",
6848
- status: "warn",
6849
- message: "No tests run yet this session \u2014 run kuma_safety({ action: 'verify' })",
6850
- weight: 10
6851
- });
6852
- totalScore += 10;
6853
- } else if (latestVerif) {
6854
- if (latestVerif.passed) {
6855
- checks.push({
6856
- label: "Tests Status",
6857
- status: "pass",
6858
- message: `Auto-verified passed (${latestVerif.scope})`,
6859
- weight: 15
6860
- });
6861
- totalScore += 15;
6862
- } else {
6863
- checks.push({
6864
- label: "Tests Status",
6865
- status: "fail",
6866
- message: `Auto-verification failed (${latestVerif.scope}) \u2014 fix before shipping`,
6867
- weight: 0
6868
- });
6869
- }
6870
- } else if (testFailures.length === 0 && unresolvedCount === 0) {
6871
- checks.push({
6872
- label: "Tests Status",
6873
- status: "pass",
6874
- message: "All tests passing",
6875
- weight: 15
6876
- });
6877
- totalScore += 15;
6878
- } else if (testFailures.length <= 2) {
6879
- checks.push({
6880
- label: "Tests Status",
6881
- status: "warn",
6882
- message: `${testFailures.length} test failure(s) \u2014 needs attention`,
6883
- weight: 8
6884
- });
6885
- totalScore += 8;
6886
- } else {
6887
- checks.push({
6888
- label: "Tests Status",
6889
- status: "fail",
6890
- message: `${testFailures.length} test failure(s) \u2014 fix before proceeding`,
6891
- weight: 3
6892
- });
6893
- totalScore += 3;
6894
- }
6895
- const modifiedCount = stats.modifiedFiles.length;
6896
- if (modifiedCount === 0) {
6897
- checks.push({
6898
- label: "Modified Files",
6899
- status: "pass",
6900
- message: "No files modified yet",
6901
- weight: 15
6902
- });
6903
- totalScore += 15;
6904
- } else if (modifiedCount <= 3) {
6905
- checks.push({
6906
- label: "Modified Files",
6907
- status: "warn",
6908
- message: `${modifiedCount} file(s) modified`,
6909
- weight: 12
6910
- });
6911
- totalScore += 12;
6912
- } else if (modifiedCount <= 8) {
6913
- checks.push({
6914
- label: "Modified Files",
6915
- status: "warn",
6916
- message: `${modifiedCount} file(s) modified \u2014 consider a checkpoint`,
6917
- weight: 8
6918
- });
6919
- totalScore += 8;
6920
- } else {
6921
- checks.push({
6922
- label: "Modified Files",
6923
- status: "fail",
6924
- message: `${modifiedCount} file(s) modified \u2014 consider committing`,
6925
- weight: 4
6926
- });
6927
- totalScore += 4;
6928
- }
6929
- const loop = sessionMemory.detectLoop();
6930
- if (loop.isLooping) {
6931
- checks.push({
6932
- label: "Loop Detection",
6933
- status: "fail",
6934
- message: loop.message || "Potential tool call loop detected",
6935
- weight: 0
6936
- });
6937
- } else {
6938
- checks.push({
6939
- label: "Loop Detection",
6940
- status: "pass",
6941
- message: "No loops detected",
6942
- weight: 10
6943
- });
6944
- totalScore += 10;
6945
- }
6946
- if (unresolvedCount === 0) {
6947
- checks.push({
6948
- label: "Unresolved Failures",
6949
- status: "pass",
6950
- message: "No unresolved failures",
6951
- weight: 10
6952
- });
6953
- totalScore += 10;
6954
- } else if (unresolvedCount <= 2) {
6955
- checks.push({
6956
- label: "Unresolved Failures",
6957
- status: "warn",
6958
- message: `${unresolvedCount} unresolved failure(s)`,
6959
- weight: 6
6960
- });
6961
- totalScore += 6;
6962
- } else {
6963
- checks.push({
6964
- label: "Unresolved Failures",
6965
- status: "fail",
6966
- message: `${unresolvedCount} unresolved failure(s) \u2014 fix before continuing`,
6967
- weight: 2
6968
- });
6969
- totalScore += 2;
6970
- }
6971
- const hasConventions = !!sessionMemory.getConventions();
6972
- if (hasConventions) {
6973
- checks.push({
6974
- label: "Project Detected",
6975
- status: "pass",
6976
- message: "Framework, test runner, and conventions detected",
6977
- weight: 5
6978
- });
6979
- totalScore += 5;
6980
- } else {
6981
- checks.push({
6982
- label: "Project Detected",
6983
- status: "warn",
6984
- message: "Run project_conventions() to detect stack",
6985
- weight: 2
6986
- });
6987
- totalScore += 2;
6988
- }
6989
- const goal = inputGoal || sessionMemory.getSummary().currentGoal || "";
6990
- if (goal) {
6991
- checks.push({
6992
- label: "Goal Set",
6993
- status: "pass",
6994
- message: `Current goal: "${goal.substring(0, 60)}"`,
6995
- weight: 5
6996
- });
6997
- totalScore += 5;
6998
- } else {
6999
- checks.push({
7000
- label: "Goal Set",
7001
- status: "warn",
7002
- message: "No goal set \u2014 use goal parameter or setGoal to track intent",
7003
- weight: 2
7004
- });
7005
- totalScore += 2;
7006
- }
7007
- let risk;
7008
- if (totalScore >= 85) risk = "LOW";
7009
- else if (totalScore >= 65) risk = "MEDIUM";
7010
- else if (totalScore >= 40) risk = "HIGH";
7011
- else risk = "CRITICAL";
7012
- const passCount = checks.filter((c) => c.status === "pass").length;
7013
- const warnCount = checks.filter((c) => c.status === "warn").length;
7014
- const failCount = checks.filter((c) => c.status === "fail").length;
7015
- const summaryParts = [];
7016
- if (passCount > 0) summaryParts.push(`${passCount} check(s) passed`);
7017
- if (warnCount > 0) summaryParts.push(`${warnCount} warning(s)`);
7018
- if (failCount > 0) summaryParts.push(`${failCount} failure(s)`);
7019
- return {
7020
- score: totalScore,
7021
- maxScore,
7022
- risk,
7023
- checks,
7024
- summary: summaryParts.join(", "),
7025
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
7026
- };
7027
- }
7028
- function formatSafetyScore(report) {
7029
- const barLength = 20;
7030
- const filledBars = Math.round(report.score / report.maxScore * barLength);
7031
- const emptyBars = barLength - filledBars;
7032
- const bar = "\u2588".repeat(filledBars) + "\u2591".repeat(emptyBars);
7033
- const riskEmoji = report.risk === "LOW" ? "\u{1F7E2}" : report.risk === "MEDIUM" ? "\u{1F7E1}" : report.risk === "HIGH" ? "\u{1F7E0}" : "\u{1F534}";
7034
- const lines = [
7035
- `\u{1F6E1}\uFE0F **Safety Score: ${report.score}/${report.maxScore}** ${riskEmoji}`,
7036
- ` ${bar}`,
7037
- ` Risk: **${report.risk}** \u2014 ${report.summary}`,
7038
- "",
7039
- "**Checks:**"
7040
- ];
7041
- for (const check of report.checks) {
7042
- const icon = check.status === "pass" ? "\u2705" : check.status === "warn" ? "\u26A0\uFE0F" : "\u274C";
7043
- lines.push(` ${icon} **${check.label}:** ${check.message}`);
7044
- }
7045
- lines.push(
7046
- "",
7047
- "\u{1F4A1} Run kuma_safety_score() at any time to re-evaluate project health."
7048
- );
7049
- return lines.join("\n");
7050
- }
7051
- var init_safetyScore = __esm({
7052
- "src/engine/safetyScore.ts"() {
7053
- "use strict";
7054
- init_sessionMemory();
7055
- init_kumaShared();
7056
- }
7057
- });
7058
-
7059
6747
  // src/engine/kumaAstValidator.ts
7060
6748
  var kumaAstValidator_exports = {};
7061
6749
  __export(kumaAstValidator_exports, {
@@ -7963,23 +7651,15 @@ function aiderTemplate() {
7963
7651
  }
7964
7652
  function opencodeAgentsMdTemplate() {
7965
7653
  const opencodeLines = [
7966
- "Kuma MCP tools are installed (kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety).",
7967
- '**Before coding, call `kuma_kuma_context({ action: "init" })`** to load project context.',
7968
- "",
7969
- "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
7970
- ' 1. `kuma_kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
7971
- " 2. *(edit/read using native tools)*",
7972
- ' 3. `kuma_kuma_context({ action: "changes" })` \u2014 Review session',
7973
- "",
7974
- "\u{1F4DD} **RECORD when needed (don't force it):**",
7975
- ' \u2022 Found a bug? \u2192 `kuma_kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
7976
- ' \u2022 Traced a flow? \u2192 `kuma_kuma_memory({ action: "arch_flow" })` (max 5 files)',
7977
- ' \u2022 Chose between options? \u2192 `kuma_kuma_memory({ action: "decision" })`',
7654
+ "Kuma MCP tools: kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety.",
7655
+ "Record what matters, skip what doesn't.",
7978
7656
  "",
7979
- "\u{1F527} **ON-DEMAND (use when appropriate):**",
7980
- ' \u2022 `kuma_kuma_safety({ action: "guard" })` \u2014 before risky ops',
7981
- ' \u2022 `kuma_kuma_context({ action: "research" })` \u2014 before editing unfamiliar code',
7982
- ' \u2022 `kuma_kuma_safety({ action: "verify" })` \u2014 after edits',
7657
+ '\u{1F9E0} **Before coding:** `kuma_kuma_context({ action: "init" })`',
7658
+ '\u{1F52C} **Unfamiliar code:** `kuma_kuma_context({ action: "research", scope: "<area>" })`',
7659
+ '\u{1F41B} **Found a bug/quirk:** `kuma_kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
7660
+ '\u{1F9ED} **Chose between options:** `kuma_kuma_memory({ action: "decision" })`',
7661
+ '\u{1F500} **Traced a flow:** `kuma_kuma_memory({ action: "arch_flow" })` (max 5 files)',
7662
+ '\u{1F6E1}\uFE0F **Before risky work / after edits:** `kuma_kuma_safety({ action: "guard" | "verify" })`',
7983
7663
  "",
7984
7664
  "\u26A0\uFE0F **OpenCode note:** Tool names use `kuma_kuma_*` prefix.",
7985
7665
  ""
@@ -8113,26 +7793,14 @@ function generateInitMdContent() {
8113
7793
  "",
8114
7794
  "## SESSION WORKFLOW",
8115
7795
  "",
8116
- "### DEFAULT: 3-Step Lean Workflow",
8117
- "",
8118
- "**For most tasks (edits, reads, simple changes):**",
8119
- "",
8120
- '1. `kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8121
- "2. *(edit/read using native tools)*",
8122
- '3. `kuma_context({ action: "changes" })` \u2014 Review session',
7796
+ "### 6 Core Actions",
8123
7797
  "",
8124
- "### ON-DEMAND: Extended Actions",
8125
- "",
8126
- "**Record only when something important happens:**",
8127
- '- Found a bug/quirk? \u2192 `kuma_memory({ action: "gotcha" })` IMMEDIATELY',
8128
- '- Traced a complete flow? \u2192 `kuma_memory({ action: "arch_flow" })` (max 5 core files)',
8129
- '- Chose between options? \u2192 `kuma_memory({ action: "decision" })` to preserve rationale',
8130
- '- Explored new area? \u2192 `kuma_memory({ action: "research_save", scope: "<file>" })`',
8131
- "",
8132
- "**Safety checks (use when appropriate):**",
8133
- '- Before risky edits: `kuma_safety({ action: "guard", guardGoal: "..." })`',
8134
- '- After edits: `kuma_safety({ action: "verify", scope: "<area>" })`',
8135
- '- Before research: `kuma_context({ action: "research", scope: "<area>" })`',
7798
+ '\u{1F9E0} **Before coding:** `kuma_context({ action: "init" })`',
7799
+ '\u{1F52C} **Unfamiliar code:** `kuma_context({ action: "research", scope: "<area>" })`',
7800
+ '\u{1F41B} **Found a bug/quirk:** `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
7801
+ '\u{1F9ED} **Chose between options:** `kuma_memory({ action: "decision" })`',
7802
+ '\u{1F500} **Traced a flow:** `kuma_memory({ action: "arch_flow" })` (max 5 files)',
7803
+ '\u{1F6E1}\uFE0F **Before risky work / after edits:** `kuma_safety({ action: "guard" | "verify" })`',
8136
7804
  "",
8137
7805
  "### Recording Rules",
8138
7806
  "",
@@ -8275,18 +7943,15 @@ function handleOpencodeSecondary(root, results) {
8275
7943
  "description: Kuma MCP \u2014 safety toolkit for AI coding agents. Research, memory, and safety guard.",
8276
7944
  "---",
8277
7945
  "",
8278
- "Kuma MCP tools are installed (kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety).",
8279
- '**Before coding, call `kuma_kuma_context({ action: "init" })`** to load project context.',
8280
- "",
8281
- "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
8282
- ' 1. `kuma_kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8283
- " 2. *(edit/read using native tools)*",
8284
- ' 3. `kuma_kuma_context({ action: "changes" })` \u2014 Review session',
7946
+ "Kuma MCP tools: kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety.",
7947
+ "Record what matters, skip what doesn't.",
8285
7948
  "",
8286
- "\u{1F4DD} **RECORD when needed:**",
8287
- ' \u2022 Bug? \u2192 `kuma_kuma_memory({ action: "gotcha" })` | Flow? \u2192 `kuma_kuma_memory({ action: "arch_flow" })` | Decision? \u2192 `kuma_kuma_memory({ action: "decision" })`',
8288
- "",
8289
- "\u{1F527} **ON-DEMAND:** guard (risky ops) | research (unfamiliar code) | verify (after edits)",
7949
+ '\u{1F9E0} **Before coding:** `kuma_kuma_context({ action: "init" })`',
7950
+ '\u{1F52C} **Unfamiliar code:** `kuma_kuma_context({ action: "research", scope: "<area>" })`',
7951
+ '\u{1F41B} **Found a bug/quirk:** `kuma_kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
7952
+ '\u{1F9ED} **Chose between options:** `kuma_kuma_memory({ action: "decision" })`',
7953
+ '\u{1F500} **Traced a flow:** `kuma_kuma_memory({ action: "arch_flow" })` (max 5 files)',
7954
+ '\u{1F6E1}\uFE0F **Before risky work / after edits:** `kuma_kuma_safety({ action: "guard" | "verify" })`',
8290
7955
  "",
8291
7956
  "\u26A0\uFE0F **OpenCode note:** Tool names use `kuma_kuma_*` prefix.",
8292
7957
  "",
@@ -8398,21 +8063,14 @@ function getCombinedAgentsMd(selectedTypes) {
8398
8063
  "",
8399
8064
  "## SESSION WORKFLOW",
8400
8065
  "",
8401
- "**DEFAULT: 3-Step Lean Workflow** (for most tasks):",
8402
- "",
8403
- ' 1. `[kuma_]context({ action: "init" })` \u2014 Load context (START HERE)',
8404
- " 2. *(edit/read using native tools)*",
8405
- ' 3. `[kuma_]context({ action: "changes" })` \u2014 Review session',
8066
+ "### 6 Core Actions",
8406
8067
  "",
8407
- "**Record only when something important happens:**",
8408
- '- Found a bug/quirk? \u2192 `[kuma_]memory({ action: "gotcha" })` IMMEDIATELY',
8409
- '- Traced a complete flow? \u2192 `[kuma_]memory({ action: "arch_flow" })` (max 5 core files)',
8410
- '- Chose between options? \u2192 `[kuma_]memory({ action: "decision" })`',
8411
- "",
8412
- "**Safety checks (use when appropriate):**",
8413
- '- Before risky edits: `[kuma_]safety({ action: "guard", guardGoal: "..." })`',
8414
- '- After edits: `[kuma_]safety({ action: "verify", scope: "<area>" })`',
8415
- '- Before research: `[kuma_]context({ action: "research", scope: "<area>" })`',
8068
+ '\u{1F9E0} **Before coding:** `[kuma_]context({ action: "init" })`',
8069
+ '\u{1F52C} **Unfamiliar code:** `[kuma_]context({ action: "research", scope: "<area>" })`',
8070
+ '\u{1F41B} **Found a bug/quirk:** `[kuma_]memory({ action: "gotcha" })` (IMMEDIATELY)',
8071
+ '\u{1F9ED} **Chose between options:** `[kuma_]memory({ action: "decision" })`',
8072
+ '\u{1F500} **Traced a flow:** `[kuma_]memory({ action: "arch_flow" })` (max 5 files)',
8073
+ '\u{1F6E1}\uFE0F **Before risky work / after edits:** `[kuma_]safety({ action: "guard" | "verify" })`',
8416
8074
  "",
8417
8075
  "\u{1F5D1}\uFE0F **Delete/Clear:** `delete_node` (delete node/gotcha/todo/decision) | `clear` (wipe entire graph)",
8418
8076
  "",
@@ -8955,29 +8613,19 @@ var init_init = __esm({
8955
8613
  codewhale: "CodeWhale (skills/ + .codewhale/mcp.json)"
8956
8614
  };
8957
8615
  BOOTSTRAP_LINES = [
8958
- "Kuma MCP tools are installed (kuma_context, kuma_memory, kuma_safety).",
8959
- '**Before coding, call `kuma_context({ action: "init" })`** to load project context.',
8960
- "",
8961
- "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
8962
- ' 1. `kuma_context({ action: "init" })` \u2014 Load context (START HERE)',
8963
- " 2. *(edit/read using native tools)*",
8964
- ' 3. `kuma_context({ action: "changes" })` \u2014 Review session',
8616
+ "Kuma MCP tools: kuma_context, kuma_memory, kuma_safety.",
8617
+ "Record what matters, skip what doesn't.",
8965
8618
  "",
8966
- "\u{1F4DD} **RECORD when needed (don't force it):**",
8967
- ' \u2022 Found a bug? \u2192 `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
8968
- ' \u2022 Traced a flow? \u2192 `kuma_memory({ action: "arch_flow" })` (max 5 files)',
8969
- ' \u2022 Chose between options? \u2192 `kuma_memory({ action: "decision" })`',
8970
- "",
8971
- "\u{1F527} **ON-DEMAND (use when appropriate):**",
8972
- ' \u2022 `kuma_safety({ action: "guard" })` \u2014 before risky ops (deletions, refactors)',
8973
- ' \u2022 `kuma_context({ action: "research" })` \u2014 before editing unfamiliar code',
8974
- ' \u2022 `kuma_context({ action: "history", target: "<file>" })` \u2014 WHY this file is written this way',
8975
- ' \u2022 `kuma_safety({ action: "verify" })` \u2014 after edits to confirm nothing broken',
8619
+ '\u{1F9E0} **Before coding:** `kuma_context({ action: "init" })`',
8620
+ '\u{1F52C} **Unfamiliar code:** `kuma_context({ action: "research", scope: "<area>" })`',
8621
+ '\u{1F41B} **Found a bug/quirk:** `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
8622
+ '\u{1F9ED} **Chose between options:** `kuma_memory({ action: "decision" })`',
8623
+ '\u{1F500} **Traced a flow:** `kuma_memory({ action: "arch_flow" })` (max 5 files)',
8624
+ '\u{1F6E1}\uFE0F **Before risky work / after edits:** `kuma_safety({ action: "guard" | "verify" })`',
8976
8625
  "",
8977
8626
  "\u{1FA84} **Auto-inject (Claude Code):** gotcha/decision/history injected automatically before edits",
8978
8627
  " via the PreToolUse hook \u2014 an empty hook output means the file has no gotchas.",
8979
8628
  "",
8980
- "\u{1F5D1}\uFE0F **Delete/Clear:** `delete_node` | `clear`",
8981
8629
  "\u{1F4D6} Full rules: `.kuma/init.md`"
8982
8630
  ].join("\n");
8983
8631
  KUMA_CORE_INSTRUCTIONS = BOOTSTRAP_LINES;
@@ -9475,39 +9123,32 @@ function generateCodewhaleMcpConfig() {
9475
9123
  }
9476
9124
  }, null, 2) + "\n";
9477
9125
  }
9478
- var BOOTSTRAP, BOOTSTRAP_OPENCODE;
9126
+ var CORE_ACTIONS, BOOTSTRAP, PREFIX_TOOLS, BOOTSTRAP_OPENCODE;
9479
9127
  var init_skillGenerator = __esm({
9480
9128
  "src/utils/skillGenerator.ts"() {
9481
9129
  "use strict";
9130
+ CORE_ACTIONS = [
9131
+ '\u{1F9E0} **Before coding:** `kuma_context({ action: "init" })`',
9132
+ '\u{1F52C} **Unfamiliar code:** `kuma_context({ action: "research", scope: "<area>" })`',
9133
+ '\u{1F41B} **Found a bug/quirk:** `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
9134
+ '\u{1F9ED} **Chose between options:** `kuma_memory({ action: "decision" })`',
9135
+ '\u{1F500} **Traced a flow:** `kuma_memory({ action: "arch_flow" })` (max 5 files)',
9136
+ '\u{1F6E1}\uFE0F **Before risky work / after edits:** `kuma_safety({ action: "guard" | "verify" })`'
9137
+ ].join("\n");
9482
9138
  BOOTSTRAP = [
9483
- "Kuma MCP tools are available (kuma_context, kuma_memory, kuma_safety).",
9484
- '**Before coding, call `kuma_context({ action: "init" })`** to load project context.',
9139
+ "Kuma MCP tools: kuma_context, kuma_memory, kuma_safety.",
9140
+ "Record what matters, skip what doesn't.",
9485
9141
  "",
9486
- "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
9487
- " 1. `init` \u2014 load context",
9488
- " 2. *(edit/read using native tools)*",
9489
- " 3. `changes` \u2014 review session",
9490
- "",
9491
- "\u{1F4DD} **RECORD when needed (don't force it):**",
9492
- ' \u2022 Found a bug? \u2192 `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
9493
- ' \u2022 Traced a flow? \u2192 `kuma_memory({ action: "arch_flow" })` (max 5 files)',
9494
- ' \u2022 Chose between options? \u2192 `kuma_memory({ action: "decision" })`',
9142
+ CORE_ACTIONS,
9495
9143
  "",
9496
9144
  "\u{1F4D6} Full rules: `.kuma/init.md`"
9497
9145
  ].join("\n");
9146
+ PREFIX_TOOLS = (s, prefix) => s.split(prefix).join("kuma_" + prefix);
9498
9147
  BOOTSTRAP_OPENCODE = [
9499
- "Kuma MCP tools are available (kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety).",
9500
- '**Before coding, call `kuma_kuma_context({ action: "init" })`** to load project context.',
9501
- "",
9502
- "\u26A1 **DEFAULT WORKFLOW (Lean \u2014 3 steps):**",
9503
- " 1. `init` \u2014 load context",
9504
- " 2. *(edit/read using native tools)*",
9505
- " 3. `changes` \u2014 review session",
9148
+ "Kuma MCP tools: kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety.",
9149
+ "Record what matters, skip what doesn't.",
9506
9150
  "",
9507
- "\u{1F4DD} **RECORD when needed:**",
9508
- ' \u2022 Found a bug? \u2192 `kuma_kuma_memory({ action: "gotcha" })` (IMMEDIATELY)',
9509
- ' \u2022 Traced a flow? \u2192 `kuma_kuma_memory({ action: "arch_flow" })` (max 5 files)',
9510
- ' \u2022 Chose between options? \u2192 `kuma_kuma_memory({ action: "decision" })`',
9151
+ ["kuma_context", "kuma_memory", "kuma_safety"].reduce((acc, tool) => PREFIX_TOOLS(acc, tool), CORE_ACTIONS),
9511
9152
  "",
9512
9153
  "\u{1F4D6} Full rules: `.kuma/init.md`"
9513
9154
  ].join("\n");
@@ -10283,21 +9924,25 @@ async function handleArchFlow(params) {
10283
9924
  return readLayer2("arch_flow");
10284
9925
  }
10285
9926
  async function handleGotchaAction(params) {
10286
- if (params.content && params.scope) {
10287
- if (!params.scope.trim()) return "\u274C **gotcha format error:** scope (file path) is required.";
10288
- if (!params.content.trim()) return "\u274C **gotcha format error:** content (description) is required.";
10289
- const normalizedScope = normalizeScope(params.scope) || params.scope;
9927
+ const content = (params.content ?? params.description ?? "").trim();
9928
+ const scope = (params.scope ?? "").trim();
9929
+ if (content && scope) {
9930
+ const normalizedScope = normalizeScope(scope) || scope;
10290
9931
  const { addGotcha: addGotcha2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
10291
9932
  const result = await addGotcha2({
10292
9933
  filePath: normalizedScope,
10293
- description: params.content,
9934
+ description: content,
10294
9935
  severity: params.status || "medium",
10295
- workaround: params.description,
9936
+ // `description` doubles as workaround only when the canonical `content` field was used.
9937
+ workaround: params.content ? params.description : void 0,
10296
9938
  triggerCommand: params.trigger_command
10297
9939
  });
10298
9940
  sessionMemory.recordMemoryAction("gotcha");
10299
9941
  return result;
10300
9942
  }
9943
+ if (scope || params.title || params.status || params.trigger_command || params.description) {
9944
+ return '\u274C **Gotcha NOT saved** \u2014 missing `content` (bug description).\n\u2705 Use: kuma_memory({ action: "gotcha", scope: "<file_path>", content: "<what went wrong>", status: "high" })\n- `scope` \u2014 file path where the bug was found\n- `content` \u2014 bug description (required to save)\n- `status` \u2014 low | medium | high | critical';
9945
+ }
10301
9946
  const { listGotchas: listGotchas2, syncGotchasToDb: syncGotchasToDb2 } = await Promise.resolve().then(() => (init_kumaGotchas(), kumaGotchas_exports));
10302
9947
  await syncGotchasToDb2();
10303
9948
  return await listGotchas2({ filePath: params.scope, severity: params.status });
@@ -10942,9 +10587,10 @@ init_sessionMemory();
10942
10587
  // src/guards/antiPatternDetector.ts
10943
10588
  init_pathValidator();
10944
10589
  init_sessionMemory();
10590
+ init_kumaShared();
10945
10591
  import fs20 from "fs";
10946
10592
  import path21 from "path";
10947
- import { execSync as execSync4 } from "child_process";
10593
+ import { execSync as execSync5 } from "child_process";
10948
10594
  var SCRIPT_PATCH_PATTERNS = [
10949
10595
  "writeFileSync",
10950
10596
  "writeFile",
@@ -10984,7 +10630,7 @@ function findPatchScripts(projectRoot) {
10984
10630
  severity: "high",
10985
10631
  pattern: "script-patching",
10986
10632
  message: `Created script file that modifies other files: ${path21.basename(file)}`,
10987
- suggestion: "Use **precise_diff_editor** instead \u2014 it has fuzzy matching, auto-backup, and rollback support",
10633
+ suggestion: "Use your native edit tools instead \u2014 targeted edits with auto-backup and rollback",
10988
10634
  evidence: `File: ${relativePath} contains '${matchedPattern}'`,
10989
10635
  filePath: relativePath
10990
10636
  });
@@ -10998,15 +10644,15 @@ function detectBashGrepUsage() {
10998
10644
  const warnings = [];
10999
10645
  const toolCalls = sessionMemory.getToolCallHistory(100);
11000
10646
  for (const call of toolCalls) {
11001
- if (call.toolName !== "execute_safe_test") continue;
10647
+ if (!isBashTool(call.toolName)) continue;
11002
10648
  const cmd = call.params?.customCommand || call.params?.command || "";
11003
10649
  const matchedPattern = BASH_GREP_PATTERNS.find((p) => cmd.toLowerCase().includes(p));
11004
10650
  if (matchedPattern) {
11005
10651
  warnings.push({
11006
10652
  severity: "medium",
11007
10653
  pattern: "bash-grep",
11008
- message: `Used bash grep instead of smart_grep`,
11009
- suggestion: "Use **smart_grep** \u2014 it returns line numbers + context, caches results, respects .gitignore",
10654
+ message: `Used bash grep instead of the native grep tool`,
10655
+ suggestion: "Use your native grep tool \u2014 it returns line numbers + context and respects .gitignore",
11010
10656
  evidence: `Command: ${cmd.substring(0, 120)}`
11011
10657
  });
11012
10658
  break;
@@ -11038,7 +10684,7 @@ function scanRecentFiles(projectRoot) {
11038
10684
  function detectGitPatchScripts(projectRoot) {
11039
10685
  const warnings = [];
11040
10686
  try {
11041
- const statusStdout = execSync4("git status --porcelain", {
10687
+ const statusStdout = execSync5("git status --porcelain", {
11042
10688
  cwd: projectRoot,
11043
10689
  encoding: "utf-8",
11044
10690
  timeout: 3e3
@@ -11063,7 +10709,7 @@ function detectGitPatchScripts(projectRoot) {
11063
10709
  severity: "high",
11064
10710
  pattern: "script-patching",
11065
10711
  message: `Patch script detected: ${file}`,
11066
- suggestion: "Use **precise_diff_editor** instead \u2014 it has fuzzy matching, auto-backup, and rollback support",
10712
+ suggestion: "Use your native edit tools instead \u2014 targeted edits with auto-backup and rollback",
11067
10713
  evidence: `File: ${file} (contains '${matchedPattern}')`,
11068
10714
  filePath: file
11069
10715
  });
@@ -11071,7 +10717,7 @@ function detectGitPatchScripts(projectRoot) {
11071
10717
  }
11072
10718
  }
11073
10719
  }
11074
- const deletedStdout = execSync4("git diff --name-only --diff-filter=D HEAD", {
10720
+ const deletedStdout = execSync5("git diff --name-only --diff-filter=D HEAD", {
11075
10721
  cwd: projectRoot,
11076
10722
  encoding: "utf-8",
11077
10723
  timeout: 3e3
@@ -11085,7 +10731,7 @@ function detectGitPatchScripts(projectRoot) {
11085
10731
  severity: "high",
11086
10732
  pattern: "script-patching",
11087
10733
  message: `Patch script was tracked then deleted: ${file}`,
11088
- suggestion: "Use **precise_diff_editor** instead of creating disposable scripts. It has auto-backup + rollback.",
10734
+ suggestion: "Use your native edit tools instead of creating disposable scripts. They have auto-backup + rollback.",
11089
10735
  evidence: `Git shows ${file} was deleted`,
11090
10736
  filePath: file
11091
10737
  });
@@ -11113,14 +10759,14 @@ function detectBashSedUsage() {
11113
10759
  "printf '%s' >"
11114
10760
  ];
11115
10761
  for (const call of toolCalls) {
11116
- if (call.toolName !== "execute_safe_test") continue;
10762
+ if (!isBashTool(call.toolName)) continue;
11117
10763
  const cmd = call.params?.customCommand || call.params?.command || "";
11118
10764
  if (SED_PATTERNS.some((p) => cmd.includes(p))) {
11119
10765
  warnings.push({
11120
10766
  severity: "high",
11121
10767
  pattern: "bash-sed-editing",
11122
- message: "Used bash sed/awk to edit source files instead of precise_diff_editor",
11123
- suggestion: 'Use **precise_diff_editor** for all file modifications \u2014 it has fuzzy matching, auto-backup, and rollback.\n\n\u2705 Correct format:\nprecise_diff_editor({\n filePath: "src/file.ts",\n edits: [\n { searchBlock: "old code", replaceBlock: "new code" }\n ]\n})',
10768
+ message: "Used bash sed/awk to edit source files instead of native edit tools",
10769
+ suggestion: "Use your native edit tools for all file modifications \u2014 targeted edits with auto-backup and rollback.",
11124
10770
  evidence: `Command: ${cmd.substring(0, 150)}`
11125
10771
  });
11126
10772
  break;
@@ -11143,7 +10789,7 @@ init_sessionMemory();
11143
10789
  init_pathValidator();
11144
10790
  import fs21 from "fs";
11145
10791
  import path22 from "path";
11146
- import { execSync as execSync5 } from "child_process";
10792
+ import { execSync as execSync6 } from "child_process";
11147
10793
  var SNAPSHOT_DIR = "context-snapshots";
11148
10794
  function snapshotDir() {
11149
10795
  return path22.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
@@ -11171,7 +10817,7 @@ function saveSnapshot(goal) {
11171
10817
  modifiedFiles: summary.modifiedFiles || [],
11172
10818
  unresolvedFailures: summary.unresolvedFailures || [],
11173
10819
  toolCallCount: summary.toolCallCount || 0,
11174
- gitDiffStat: getGitDiffStat(),
10820
+ gitDiffStat: getGitDiffStat2(),
11175
10821
  completedSteps: summary.completedSteps || [],
11176
10822
  hasConventions: !!summary.hasConventions
11177
10823
  };
@@ -11212,10 +10858,10 @@ function formatSnapshot(snapshot) {
11212
10858
  lines.push('\u{1F4A1} Use kuma_guard({ check: "context" }) to create a new snapshot.');
11213
10859
  return lines.join("\n");
11214
10860
  }
11215
- function getGitDiffStat() {
10861
+ function getGitDiffStat2() {
11216
10862
  try {
11217
10863
  const root = getProjectRoot();
11218
- const stdout = execSync5("git diff --stat", {
10864
+ const stdout = execSync6("git diff --stat", {
11219
10865
  cwd: root,
11220
10866
  encoding: "utf-8",
11221
10867
  timeout: 3e3
@@ -11242,16 +10888,14 @@ async function handleKumaGuard(params) {
11242
10888
  severity: "high",
11243
10889
  pattern: "tool-loop",
11244
10890
  message: loop.message ?? "Detected potential tool call loop",
11245
- suggestion: "Switch approach \u2014 try reading the file first with smart_file_picker"
10891
+ suggestion: "Switch approach \u2014 try reading the file first with your native read/search tools"
11246
10892
  });
11247
10893
  }
11248
10894
  const drifts = [];
11249
10895
  if (check === "all" || check === "drift") {
11250
10896
  const unresolvedCount = getUnresolvedCount(stats.failedFiles);
11251
- const gitStat = getGitDiffStat2();
11252
- const editCalls = stats.toolCalls.filter(
11253
- (c) => c.toolName === "precise_diff_editor" || c.toolName === "batch_file_writer"
11254
- ).length;
10897
+ const gitStat = getGitDiffStat();
10898
+ const editCalls = stats.toolCalls.filter((c) => isEditTool(c.toolName)).length;
11255
10899
  drifts.push(...buildDriftMessages(
11256
10900
  stats.modifiedFiles.length,
11257
10901
  stats.hasRunTests,
@@ -11263,7 +10907,7 @@ async function handleKumaGuard(params) {
11263
10907
  severity: "medium",
11264
10908
  pattern: "no-test-after-edit",
11265
10909
  message: `${stats.modifiedFiles.length} file(s) modified without running tests`,
11266
- suggestion: 'Run execute_safe_test({ task: "typecheck" }) to verify changes'
10910
+ suggestion: "Run the project's test/typecheck command to verify changes"
11267
10911
  });
11268
10912
  }
11269
10913
  if (editCalls > 5) {
@@ -11277,9 +10921,7 @@ async function handleKumaGuard(params) {
11277
10921
  }
11278
10922
  const recordingSummary = sessionMemory.getRecordingSummary();
11279
10923
  if (check === "all" || check === "drift") {
11280
- const readCalls = stats.toolCalls.filter(
11281
- (c) => c.toolName === "read" || c.toolName === "grep" || c.toolName === "glob" || c.toolName === "smart_file_picker"
11282
- ).length;
10924
+ const readCalls = stats.toolCalls.filter((c) => isReadTool(c.toolName)).length;
11283
10925
  if (stats.toolCallCount >= 10 && !recordingSummary.hasAnyRecordings) {
11284
10926
  warnings.push({
11285
10927
  severity: "high",
@@ -11305,7 +10947,7 @@ async function handleKumaGuard(params) {
11305
10947
  if (recordingSummary.total > 0) {
11306
10948
  }
11307
10949
  const errorCalls = stats.toolCalls.filter(
11308
- (c) => c.toolName === "execute_safe_command" && JSON.stringify(c.params).includes("error")
10950
+ (c) => isBashTool(c.toolName) && JSON.stringify(c.params).includes("error")
11309
10951
  ).length;
11310
10952
  if (errorCalls >= 2 && recordingSummary.gotchas === 0) {
11311
10953
  warnings.push({
@@ -11342,22 +10984,15 @@ async function handleKumaGuard(params) {
11342
10984
  const hasWarnings = warnings.length > 0;
11343
10985
  const hasDrifts = drifts.length > 0;
11344
10986
  const onTrack = !hasWarnings && !hasDrifts;
11345
- let suggestion;
11346
- if (warnings.some((w) => w.severity === "high" && w.pattern === "script-patching")) {
11347
- suggestion = "Remove patch scripts and use precise_diff_editor for all file modifications";
11348
- } else if (warnings.some((w) => w.pattern === "tool-loop")) {
11349
- suggestion = "Switch approach \u2014 current tool is not making progress";
11350
- } else if (warnings.some((w) => w.pattern === "no-test-after-edit")) {
11351
- suggestion = "Run tests to verify your changes before continuing";
11352
- } else if (warnings.some((w) => w.pattern === "bash-grep")) {
11353
- suggestion = "Use smart_grep for code search instead of bash grep";
11354
- } else if (warnings.some((w) => w.pattern === "excessive-edits")) {
11355
- suggestion = "Pause and review: are all these edits necessary?";
11356
- } else if (!stats.goal) {
11357
- suggestion = "No goal set \u2014 use goal parameter or setGoal to track intent";
11358
- } else {
11359
- suggestion = "On track \u2014 continue with current approach";
11360
- }
10987
+ const suggestion = getPrioritySuggestion(
10988
+ stats.goal,
10989
+ warnings,
10990
+ loop.isLooping,
10991
+ getUnresolvedCount(stats.failedFiles),
10992
+ stats.modifiedFiles.length,
10993
+ stats.hasRunTests,
10994
+ countEditCalls(stats.toolCalls)
10995
+ );
11361
10996
  const report = {
11362
10997
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11363
10998
  onTrack,
@@ -11406,8 +11041,6 @@ async function handleSafety(params) {
11406
11041
  return handleCheck(params);
11407
11042
  case "audit":
11408
11043
  return handleAudit(params);
11409
- case "health":
11410
- return handleHealth(params);
11411
11044
  case "security":
11412
11045
  return handleSecurity(params);
11413
11046
  case "gc":
@@ -11446,16 +11079,6 @@ async function handleAudit(params) {
11446
11079
  since: params.since
11447
11080
  });
11448
11081
  }
11449
- async function handleHealth(_params) {
11450
- try {
11451
- const { computeSafetyScore: computeSafetyScore2, formatSafetyScore: formatSafetyScore2 } = await Promise.resolve().then(() => (init_safetyScore(), safetyScore_exports));
11452
- const score = await computeSafetyScore2();
11453
- await saveHealthSnapshot(score.score, score.risk, JSON.stringify(score.checks), score.summary);
11454
- return formatSafetyScore2(score);
11455
- } catch (err) {
11456
- return `Error computing health: ${err}`;
11457
- }
11458
- }
11459
11082
  async function handleSecurity(params) {
11460
11083
  sessionMemory.recordToolCall("kuma_safety_security", {});
11461
11084
  if (params.filePath) {
@@ -11626,14 +11249,17 @@ async function doInit() {
11626
11249
  }
11627
11250
 
11628
11251
  // src/manifest.ts
11252
+ var CORE_NOTE = "\n\nCORE ACTIONS (use these): ";
11629
11253
  function registerAllTools(server) {
11630
11254
  server.tool(
11631
11255
  "kuma_context",
11632
- "**Call FIRST every session.** Understand your project before making changes.\n\nWORKFLOW:\n\u2022 STEP 1 (ALWAYS): `init` \u2014 lean project brief + session restore (<500 tokens)\n\u2022 During edits: hooks (`kuma hook pre-edit` / `pre-bash`) auto-inject gotchas, decisions & history \u2014 zero extra steps\n\u2022 Editing an unfamiliar file: `history` \u2014 why is this file written this way (cross-session trace)\n\u2022 STEP 8 (END of session): `changes` \u2014 review what you modified\n\nOther actions: flow (hash-verified derived flow cache), impact (analyze changes), digest (compact briefing), drift (detect staleness), rollback (undo a change).",
11256
+ "Context & memory recall. Call FIRST each session. Lean \u2014 each call returns only what you need." + CORE_NOTE + "`init` (start of session: project brief + session state), `research` (before editing unfamiliar code), `history` (why is this file written this way). Other actions are internal: flow, impact, navigate, digest, drift, rollback, changes, resume.",
11633
11257
  {
11634
- action: z.enum(["init", "research", "impact", "navigate", "flow", "history", "changes", "rollback", "digest", "drift", "resume"]).describe("Action: init=lean project brief, research=research pipeline, impact=analyze change effects, navigate=trace code flow, flow=hash-verified derived flow cache (F13), history=why is this file written this way (cross-session trace + fresh gotchas), changes=view change log, rollback=undo a change by ID, digest=ultra-compact project briefing, drift=detect memory staleness, resume=load previous session context"),
11258
+ action: z.enum(["init", "research", "impact", "navigate", "flow", "history", "changes", "rollback", "digest", "drift", "resume"]).describe(
11259
+ "init=project brief + session restore (CORE), research=research pipeline (CORE), history=file rationale + fresh gotchas (CORE); internal (avoid unless needed): impact, navigate, flow, changes, rollback, digest, drift, resume"
11260
+ ),
11635
11261
  scope: z.string().optional().describe("Research scope for research action"),
11636
- target: z.string().optional().describe("Target symbol/file for impact/navigate/changes"),
11262
+ target: z.string().optional().describe("Target symbol/file for impact/navigate/changes/history"),
11637
11263
  goal: z.string().optional().describe("Current goal (for init)"),
11638
11264
  since: z.number().optional().describe("Unix timestamp filter for changes")
11639
11265
  },
@@ -11655,11 +11281,13 @@ function registerAllTools(server) {
11655
11281
  );
11656
11282
  server.tool(
11657
11283
  "kuma_memory",
11658
- "**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 after exploring area (creates search cache).\n\u2022 STEP 6: `gotcha` \u2014 IMMEDIATELY when you discover bugs/quirks. No re-research.\n\u2022 STEP 7: `arch_flow` \u2014 AFTER tracing COMPLETE flow (max 5 core 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, search, changes.",
11284
+ "Persistent knowledge. Record what matters, skip what doesn't. This is what saves time in future sessions. Don't record what grep/glob answers faster (functions, imports, types, components)." + CORE_NOTE + "`gotcha` (IMMEDIATELY when you find a bug/quirk), `decision` (when choosing between options), `arch_flow` (after tracing a complete flow, max 5 core files), `research_save` (after exploring an area). Other actions are internal: session, search, changes, mine, delete_node, clear, goal_progress.",
11659
11285
  {
11660
- action: z.enum(["decision", "mine", "research_save", "session", "search", "changes", "arch_flow", "gotcha", "delete_node", "clear", "goal_progress"]).describe("Memory action: decision=ADR, mine=mine git log, research_save=save findings, session=summary, search=search memory + graph, changes=change log, arch_flow=record architecture flow, gotcha=record bug/quirk, delete_node=delete node/gotcha, clear=wipe all nodes, goal_progress=update goal progress"),
11286
+ action: z.enum(["decision", "mine", "research_save", "session", "search", "changes", "arch_flow", "gotcha", "delete_node", "clear", "goal_progress"]).describe(
11287
+ "gotcha=record bug/quirk (CORE), decision=ADR (CORE), arch_flow=record architecture flow (CORE), research_save=save findings (CORE); internal (avoid unless needed): mine, session, search, changes, delete_node, clear, goal_progress"
11288
+ ),
11661
11289
  scope: z.string().optional().describe("Scope for research_save/search/mine \u2014 or file path for gotcha/delete_node"),
11662
- trigger_command: z.string().optional().describe("Gotcha trigger: shell command that hits this gotcha (e.g. 'npm run seed'). Injected by the Bash PreToolUse hook"),
11290
+ trigger_command: z.string().optional().describe("Gotcha trigger: shell command that hits this gotcha. Injected by the Bash PreToolUse hook"),
11663
11291
  query: z.string().optional().describe("Search query for search action"),
11664
11292
  content: z.string().optional().describe("Content/notes for research_save / gotcha description / arch_flow record"),
11665
11293
  record: z.string().optional().describe("JSON record string for research_save"),
@@ -11669,8 +11297,8 @@ function registerAllTools(server) {
11669
11297
  context: z.string().optional().describe("Decision context"),
11670
11298
  rationale: z.string().optional().describe("Decision rationale (required for decision)"),
11671
11299
  outcome: z.string().optional().describe("Decision outcome (default: implemented)"),
11672
- status: z.string().optional().describe("Gotcha severity for gotcha action (low|medium|high|critical)"),
11673
- description: z.string().optional().describe("Gotcha workaround for gotcha action"),
11300
+ status: z.string().optional().describe("Gotcha severity (low|medium|high|critical)"),
11301
+ description: z.string().optional().describe("Gotcha workaround"),
11674
11302
  topic: z.string().optional().describe("Memory topic for session action"),
11675
11303
  limit: z.number().min(1).max(100).optional().describe("Result limit for search/mine"),
11676
11304
  target: z.string().optional().describe("File path for changes / node ID for delete_node"),
@@ -11707,9 +11335,11 @@ function registerAllTools(server) {
11707
11335
  );
11708
11336
  server.tool(
11709
11337
  "kuma_safety",
11710
- "Safety checks, policy enforcement, and auto-verification.\n\nWORKFLOW:\n\u2022 STEP 2 (BEFORE work): `guard` \u2014 detect anti-patterns, drift, runaway loops\n\u2022 STEP 9 (AFTER research/edits): `verify` \u2014 auto-run scoped tests + AST validation\n\nOther actions: check (pre-exec safety), audit (query trail), health (score 0-100), ast (code validation), checkpoint (snapshot before refactors), gotcha_staleness (verify recorded gotchas are still valid).",
11338
+ "Safety & verification. Use at task boundaries, not on every edit." + CORE_NOTE + "`guard` (before risky work: anti-patterns, drift, loops), `verify` (after edits: scoped tests + validation). Other actions are internal/deprecated: check, audit, security, gc, ast, validate, checkpoint, rollback_label, checkpoint_list, gotcha_staleness.",
11711
11339
  {
11712
- action: z.enum(["guard", "verify", "check", "audit", "health", "security", "gc", "ast", "validate", "checkpoint", "rollback_label", "checkpoint_list", "gotcha_staleness"]).describe("Safety action: guard=anti-patterns, verify=auto-run tests, check=pre-exec safety, audit=query trail, health=project score, security=scan leaks, gc=garbage collect, ast/validate=AST validation, checkpoint=create snapshot, rollback_label=restore from checkpoint, checkpoint_list=list checkpoints, gotcha_staleness=verify gotcha file refs still valid"),
11340
+ action: z.enum(["guard", "verify", "check", "audit", "security", "gc", "ast", "validate", "checkpoint", "rollback_label", "checkpoint_list", "gotcha_staleness"]).describe(
11341
+ "guard=anti-patterns/drift/loops before risky work (CORE), verify=scoped tests after edits (CORE); internal/deprecated (avoid unless needed): check, audit, security, gc, ast, validate, checkpoint, rollback_label, checkpoint_list, gotcha_staleness"
11342
+ ),
11713
11343
  // Verify params
11714
11344
  scope: z.string().optional().describe("Scope for verify/ast/validate (e.g. 'auth', file path)"),
11715
11345
  // Guard params
@@ -11725,7 +11355,7 @@ function registerAllTools(server) {
11725
11355
  limit: z.number().min(1).max(100).optional().describe("Audit result limit"),
11726
11356
  since: z.number().optional().describe("Timestamp filter for audit"),
11727
11357
  force: z.boolean().optional().describe("Force re-run even if cache is fresh (verify)"),
11728
- // Checkpoint params (#29)
11358
+ // Checkpoint params
11729
11359
  label: z.string().optional().describe("Checkpoint label for checkpoint/rollback_label actions"),
11730
11360
  description: z.string().optional().describe("Description for checkpoint")
11731
11361
  },
@@ -11754,9 +11384,9 @@ function registerAllTools(server) {
11754
11384
  }
11755
11385
  }
11756
11386
  );
11757
- console.error("[Manifest] Registered 3 V3 coarse-grained tools (kuma_context, kuma_memory, kuma_safety).");
11758
- console.error("[Manifest] Pilar 1: Auto-init hooks installed on all tools.");
11759
- console.error("[Manifest] Pilar 2: Namespace aliases active (kuma_kuma_* \u2192 kuma_*, context \u2192 kuma_context).");
11387
+ console.error("[Manifest] Registered 3 coarse-grained tools (kuma_context, kuma_memory, kuma_safety).");
11388
+ console.error("[Manifest] Auto-init hooks installed on all tools.");
11389
+ console.error("[Manifest] Namespace aliases active (kuma_kuma_* \u2192 kuma_*, context \u2192 kuma_context).");
11760
11390
  }
11761
11391
 
11762
11392
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumpslabs/kuma",
3
- "version": "2.3.34",
3
+ "version": "2.4.2",
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",