@plumpslabs/kuma 2.3.34 → 2.4.1

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 +228 -602
  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)`);
@@ -1103,10 +1093,6 @@ 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
1098
  db.exec(`DELETE FROM safety_audit WHERE id IN (SELECT id FROM safety_audit ORDER BY created_at ASC LIMIT ${auditCount - 200})`);
@@ -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',
7654
+ "Kuma MCP tools: kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety.",
7655
+ "Record what matters, skip what doesn't.",
7973
7656
  "",
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" })`',
7978
- "",
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):**",
7796
+ "### 6 Core Actions",
8119
7797
  "",
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',
8123
- "",
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',
8285
- "",
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" })`',
7946
+ "Kuma MCP tools: kuma_kuma_context, kuma_kuma_memory, kuma_kuma_safety.",
7947
+ "Record what matters, skip what doesn't.",
8288
7948
  "",
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):",
8066
+ "### 6 Core Actions",
8402
8067
  "",
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',
8406
- "",
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',
8965
- "",
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" })`',
8616
+ "Kuma MCP tools: kuma_context, kuma_memory, kuma_safety.",
8617
+ "Record what matters, skip what doesn't.",
8970
8618
  "",
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.',
9485
- "",
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",
9139
+ "Kuma MCP tools: kuma_context, kuma_memory, kuma_safety.",
9140
+ "Record what matters, skip what doesn't.",
9490
9141
  "",
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");
@@ -10942,9 +10583,10 @@ init_sessionMemory();
10942
10583
  // src/guards/antiPatternDetector.ts
10943
10584
  init_pathValidator();
10944
10585
  init_sessionMemory();
10586
+ init_kumaShared();
10945
10587
  import fs20 from "fs";
10946
10588
  import path21 from "path";
10947
- import { execSync as execSync4 } from "child_process";
10589
+ import { execSync as execSync5 } from "child_process";
10948
10590
  var SCRIPT_PATCH_PATTERNS = [
10949
10591
  "writeFileSync",
10950
10592
  "writeFile",
@@ -10984,7 +10626,7 @@ function findPatchScripts(projectRoot) {
10984
10626
  severity: "high",
10985
10627
  pattern: "script-patching",
10986
10628
  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",
10629
+ suggestion: "Use your native edit tools instead \u2014 targeted edits with auto-backup and rollback",
10988
10630
  evidence: `File: ${relativePath} contains '${matchedPattern}'`,
10989
10631
  filePath: relativePath
10990
10632
  });
@@ -10998,15 +10640,15 @@ function detectBashGrepUsage() {
10998
10640
  const warnings = [];
10999
10641
  const toolCalls = sessionMemory.getToolCallHistory(100);
11000
10642
  for (const call of toolCalls) {
11001
- if (call.toolName !== "execute_safe_test") continue;
10643
+ if (!isBashTool(call.toolName)) continue;
11002
10644
  const cmd = call.params?.customCommand || call.params?.command || "";
11003
10645
  const matchedPattern = BASH_GREP_PATTERNS.find((p) => cmd.toLowerCase().includes(p));
11004
10646
  if (matchedPattern) {
11005
10647
  warnings.push({
11006
10648
  severity: "medium",
11007
10649
  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",
10650
+ message: `Used bash grep instead of the native grep tool`,
10651
+ suggestion: "Use your native grep tool \u2014 it returns line numbers + context and respects .gitignore",
11010
10652
  evidence: `Command: ${cmd.substring(0, 120)}`
11011
10653
  });
11012
10654
  break;
@@ -11038,7 +10680,7 @@ function scanRecentFiles(projectRoot) {
11038
10680
  function detectGitPatchScripts(projectRoot) {
11039
10681
  const warnings = [];
11040
10682
  try {
11041
- const statusStdout = execSync4("git status --porcelain", {
10683
+ const statusStdout = execSync5("git status --porcelain", {
11042
10684
  cwd: projectRoot,
11043
10685
  encoding: "utf-8",
11044
10686
  timeout: 3e3
@@ -11063,7 +10705,7 @@ function detectGitPatchScripts(projectRoot) {
11063
10705
  severity: "high",
11064
10706
  pattern: "script-patching",
11065
10707
  message: `Patch script detected: ${file}`,
11066
- suggestion: "Use **precise_diff_editor** instead \u2014 it has fuzzy matching, auto-backup, and rollback support",
10708
+ suggestion: "Use your native edit tools instead \u2014 targeted edits with auto-backup and rollback",
11067
10709
  evidence: `File: ${file} (contains '${matchedPattern}')`,
11068
10710
  filePath: file
11069
10711
  });
@@ -11071,7 +10713,7 @@ function detectGitPatchScripts(projectRoot) {
11071
10713
  }
11072
10714
  }
11073
10715
  }
11074
- const deletedStdout = execSync4("git diff --name-only --diff-filter=D HEAD", {
10716
+ const deletedStdout = execSync5("git diff --name-only --diff-filter=D HEAD", {
11075
10717
  cwd: projectRoot,
11076
10718
  encoding: "utf-8",
11077
10719
  timeout: 3e3
@@ -11085,7 +10727,7 @@ function detectGitPatchScripts(projectRoot) {
11085
10727
  severity: "high",
11086
10728
  pattern: "script-patching",
11087
10729
  message: `Patch script was tracked then deleted: ${file}`,
11088
- suggestion: "Use **precise_diff_editor** instead of creating disposable scripts. It has auto-backup + rollback.",
10730
+ suggestion: "Use your native edit tools instead of creating disposable scripts. They have auto-backup + rollback.",
11089
10731
  evidence: `Git shows ${file} was deleted`,
11090
10732
  filePath: file
11091
10733
  });
@@ -11113,14 +10755,14 @@ function detectBashSedUsage() {
11113
10755
  "printf '%s' >"
11114
10756
  ];
11115
10757
  for (const call of toolCalls) {
11116
- if (call.toolName !== "execute_safe_test") continue;
10758
+ if (!isBashTool(call.toolName)) continue;
11117
10759
  const cmd = call.params?.customCommand || call.params?.command || "";
11118
10760
  if (SED_PATTERNS.some((p) => cmd.includes(p))) {
11119
10761
  warnings.push({
11120
10762
  severity: "high",
11121
10763
  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})',
10764
+ message: "Used bash sed/awk to edit source files instead of native edit tools",
10765
+ suggestion: "Use your native edit tools for all file modifications \u2014 targeted edits with auto-backup and rollback.",
11124
10766
  evidence: `Command: ${cmd.substring(0, 150)}`
11125
10767
  });
11126
10768
  break;
@@ -11143,7 +10785,7 @@ init_sessionMemory();
11143
10785
  init_pathValidator();
11144
10786
  import fs21 from "fs";
11145
10787
  import path22 from "path";
11146
- import { execSync as execSync5 } from "child_process";
10788
+ import { execSync as execSync6 } from "child_process";
11147
10789
  var SNAPSHOT_DIR = "context-snapshots";
11148
10790
  function snapshotDir() {
11149
10791
  return path22.join(getProjectRoot(), ".kuma", SNAPSHOT_DIR);
@@ -11171,7 +10813,7 @@ function saveSnapshot(goal) {
11171
10813
  modifiedFiles: summary.modifiedFiles || [],
11172
10814
  unresolvedFailures: summary.unresolvedFailures || [],
11173
10815
  toolCallCount: summary.toolCallCount || 0,
11174
- gitDiffStat: getGitDiffStat(),
10816
+ gitDiffStat: getGitDiffStat2(),
11175
10817
  completedSteps: summary.completedSteps || [],
11176
10818
  hasConventions: !!summary.hasConventions
11177
10819
  };
@@ -11212,10 +10854,10 @@ function formatSnapshot(snapshot) {
11212
10854
  lines.push('\u{1F4A1} Use kuma_guard({ check: "context" }) to create a new snapshot.');
11213
10855
  return lines.join("\n");
11214
10856
  }
11215
- function getGitDiffStat() {
10857
+ function getGitDiffStat2() {
11216
10858
  try {
11217
10859
  const root = getProjectRoot();
11218
- const stdout = execSync5("git diff --stat", {
10860
+ const stdout = execSync6("git diff --stat", {
11219
10861
  cwd: root,
11220
10862
  encoding: "utf-8",
11221
10863
  timeout: 3e3
@@ -11242,16 +10884,14 @@ async function handleKumaGuard(params) {
11242
10884
  severity: "high",
11243
10885
  pattern: "tool-loop",
11244
10886
  message: loop.message ?? "Detected potential tool call loop",
11245
- suggestion: "Switch approach \u2014 try reading the file first with smart_file_picker"
10887
+ suggestion: "Switch approach \u2014 try reading the file first with your native read/search tools"
11246
10888
  });
11247
10889
  }
11248
10890
  const drifts = [];
11249
10891
  if (check === "all" || check === "drift") {
11250
10892
  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;
10893
+ const gitStat = getGitDiffStat();
10894
+ const editCalls = stats.toolCalls.filter((c) => isEditTool(c.toolName)).length;
11255
10895
  drifts.push(...buildDriftMessages(
11256
10896
  stats.modifiedFiles.length,
11257
10897
  stats.hasRunTests,
@@ -11263,7 +10903,7 @@ async function handleKumaGuard(params) {
11263
10903
  severity: "medium",
11264
10904
  pattern: "no-test-after-edit",
11265
10905
  message: `${stats.modifiedFiles.length} file(s) modified without running tests`,
11266
- suggestion: 'Run execute_safe_test({ task: "typecheck" }) to verify changes'
10906
+ suggestion: "Run the project's test/typecheck command to verify changes"
11267
10907
  });
11268
10908
  }
11269
10909
  if (editCalls > 5) {
@@ -11277,9 +10917,7 @@ async function handleKumaGuard(params) {
11277
10917
  }
11278
10918
  const recordingSummary = sessionMemory.getRecordingSummary();
11279
10919
  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;
10920
+ const readCalls = stats.toolCalls.filter((c) => isReadTool(c.toolName)).length;
11283
10921
  if (stats.toolCallCount >= 10 && !recordingSummary.hasAnyRecordings) {
11284
10922
  warnings.push({
11285
10923
  severity: "high",
@@ -11305,7 +10943,7 @@ async function handleKumaGuard(params) {
11305
10943
  if (recordingSummary.total > 0) {
11306
10944
  }
11307
10945
  const errorCalls = stats.toolCalls.filter(
11308
- (c) => c.toolName === "execute_safe_command" && JSON.stringify(c.params).includes("error")
10946
+ (c) => isBashTool(c.toolName) && JSON.stringify(c.params).includes("error")
11309
10947
  ).length;
11310
10948
  if (errorCalls >= 2 && recordingSummary.gotchas === 0) {
11311
10949
  warnings.push({
@@ -11342,22 +10980,15 @@ async function handleKumaGuard(params) {
11342
10980
  const hasWarnings = warnings.length > 0;
11343
10981
  const hasDrifts = drifts.length > 0;
11344
10982
  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
- }
10983
+ const suggestion = getPrioritySuggestion(
10984
+ stats.goal,
10985
+ warnings,
10986
+ loop.isLooping,
10987
+ getUnresolvedCount(stats.failedFiles),
10988
+ stats.modifiedFiles.length,
10989
+ stats.hasRunTests,
10990
+ countEditCalls(stats.toolCalls)
10991
+ );
11361
10992
  const report = {
11362
10993
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11363
10994
  onTrack,
@@ -11406,8 +11037,6 @@ async function handleSafety(params) {
11406
11037
  return handleCheck(params);
11407
11038
  case "audit":
11408
11039
  return handleAudit(params);
11409
- case "health":
11410
- return handleHealth(params);
11411
11040
  case "security":
11412
11041
  return handleSecurity(params);
11413
11042
  case "gc":
@@ -11446,16 +11075,6 @@ async function handleAudit(params) {
11446
11075
  since: params.since
11447
11076
  });
11448
11077
  }
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
11078
  async function handleSecurity(params) {
11460
11079
  sessionMemory.recordToolCall("kuma_safety_security", {});
11461
11080
  if (params.filePath) {
@@ -11626,14 +11245,17 @@ async function doInit() {
11626
11245
  }
11627
11246
 
11628
11247
  // src/manifest.ts
11248
+ var CORE_NOTE = "\n\nCORE ACTIONS (use these): ";
11629
11249
  function registerAllTools(server) {
11630
11250
  server.tool(
11631
11251
  "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).",
11252
+ "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
11253
  {
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"),
11254
+ action: z.enum(["init", "research", "impact", "navigate", "flow", "history", "changes", "rollback", "digest", "drift", "resume"]).describe(
11255
+ "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"
11256
+ ),
11635
11257
  scope: z.string().optional().describe("Research scope for research action"),
11636
- target: z.string().optional().describe("Target symbol/file for impact/navigate/changes"),
11258
+ target: z.string().optional().describe("Target symbol/file for impact/navigate/changes/history"),
11637
11259
  goal: z.string().optional().describe("Current goal (for init)"),
11638
11260
  since: z.number().optional().describe("Unix timestamp filter for changes")
11639
11261
  },
@@ -11655,11 +11277,13 @@ function registerAllTools(server) {
11655
11277
  );
11656
11278
  server.tool(
11657
11279
  "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.",
11280
+ "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
11281
  {
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"),
11282
+ action: z.enum(["decision", "mine", "research_save", "session", "search", "changes", "arch_flow", "gotcha", "delete_node", "clear", "goal_progress"]).describe(
11283
+ "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"
11284
+ ),
11661
11285
  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"),
11286
+ trigger_command: z.string().optional().describe("Gotcha trigger: shell command that hits this gotcha. Injected by the Bash PreToolUse hook"),
11663
11287
  query: z.string().optional().describe("Search query for search action"),
11664
11288
  content: z.string().optional().describe("Content/notes for research_save / gotcha description / arch_flow record"),
11665
11289
  record: z.string().optional().describe("JSON record string for research_save"),
@@ -11669,8 +11293,8 @@ function registerAllTools(server) {
11669
11293
  context: z.string().optional().describe("Decision context"),
11670
11294
  rationale: z.string().optional().describe("Decision rationale (required for decision)"),
11671
11295
  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"),
11296
+ status: z.string().optional().describe("Gotcha severity (low|medium|high|critical)"),
11297
+ description: z.string().optional().describe("Gotcha workaround"),
11674
11298
  topic: z.string().optional().describe("Memory topic for session action"),
11675
11299
  limit: z.number().min(1).max(100).optional().describe("Result limit for search/mine"),
11676
11300
  target: z.string().optional().describe("File path for changes / node ID for delete_node"),
@@ -11707,9 +11331,11 @@ function registerAllTools(server) {
11707
11331
  );
11708
11332
  server.tool(
11709
11333
  "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).",
11334
+ "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
11335
  {
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"),
11336
+ action: z.enum(["guard", "verify", "check", "audit", "security", "gc", "ast", "validate", "checkpoint", "rollback_label", "checkpoint_list", "gotcha_staleness"]).describe(
11337
+ "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"
11338
+ ),
11713
11339
  // Verify params
11714
11340
  scope: z.string().optional().describe("Scope for verify/ast/validate (e.g. 'auth', file path)"),
11715
11341
  // Guard params
@@ -11725,7 +11351,7 @@ function registerAllTools(server) {
11725
11351
  limit: z.number().min(1).max(100).optional().describe("Audit result limit"),
11726
11352
  since: z.number().optional().describe("Timestamp filter for audit"),
11727
11353
  force: z.boolean().optional().describe("Force re-run even if cache is fresh (verify)"),
11728
- // Checkpoint params (#29)
11354
+ // Checkpoint params
11729
11355
  label: z.string().optional().describe("Checkpoint label for checkpoint/rollback_label actions"),
11730
11356
  description: z.string().optional().describe("Description for checkpoint")
11731
11357
  },
@@ -11754,9 +11380,9 @@ function registerAllTools(server) {
11754
11380
  }
11755
11381
  }
11756
11382
  );
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).");
11383
+ console.error("[Manifest] Registered 3 coarse-grained tools (kuma_context, kuma_memory, kuma_safety).");
11384
+ console.error("[Manifest] Auto-init hooks installed on all tools.");
11385
+ console.error("[Manifest] Namespace aliases active (kuma_kuma_* \u2192 kuma_*, context \u2192 kuma_context).");
11760
11386
  }
11761
11387
 
11762
11388
  // 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.1",
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",