@plumpslabs/kuma 2.4.20 β†’ 2.4.22

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.
package/dist/index.js CHANGED
@@ -11438,30 +11438,33 @@ function opencodePluginTemplate() {
11438
11438
  " }",
11439
11439
  "}",
11440
11440
  "",
11441
+ "function enforceGotchas(tool, args) {",
11442
+ ' const name = String(tool || "").toLowerCase();',
11443
+ " const argv = args || {};",
11444
+ ' const targetPath = argv.filePath || argv.targetFile || argv.path || argv.file || "";',
11445
+ " if (!targetPath) return;",
11446
+ ' if (!(name.includes("edit") || name.includes("write") || name.includes("patch"))) return;',
11447
+ " const blocking = getBlockingGotchasForFile(String(targetPath));",
11448
+ " if (blocking.length > 0) {",
11449
+ " throw new Error(",
11450
+ " `\u{1F43B} [Kuma] ${blocking.length} critical/high gotcha(s) for ${targetPath}:\\n` +",
11451
+ ' blocking.map((g) => ` \u{1F534} ${g}`).join("\\n") +',
11452
+ ' `\\nRead .kuma/KNOWN_GOTCHAS.md for workarounds, or run kuma_memory({ action: "gotcha" }) to list.`',
11453
+ " );",
11454
+ " }",
11455
+ "}",
11456
+ "",
11457
+ "// \u2500\u2500\u2500 v1 host: factory (named export, legacy mode requires functions) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
11441
11458
  "export const KumaPlugin = async () => {",
11442
11459
  " return {",
11443
11460
  ' "tool.execute.before": async (input, output) => {',
11444
- ' const tool = String(input.tool || "").toLowerCase();',
11445
- " const args = output.args || {};",
11446
- ' const targetPath = args.filePath || args.targetFile || args.path || args.file || "";',
11447
- " if (!targetPath) return;",
11448
- ' if (!(tool.includes("edit") || tool.includes("write") || tool.includes("patch"))) return;',
11449
- " const blocking = getBlockingGotchasForFile(String(targetPath));",
11450
- " if (blocking.length > 0) {",
11451
- " throw new Error(",
11452
- " `\u{1F43B} [Kuma] ${blocking.length} critical/high gotcha(s) for ${targetPath}:\\n` +",
11453
- ' blocking.map((g) => ` \u{1F534} ${g}`).join("\\n") +',
11454
- ' `\\nRead .kuma/KNOWN_GOTCHAS.md for workarounds, or run kuma_memory({ action: "gotcha" }) to list.`',
11455
- " );",
11456
- " }",
11461
+ " enforceGotchas(input?.tool, output?.args);",
11457
11462
  " },",
11458
11463
  ' "file.edited": async (input) => {',
11459
- " // Post-edit verify nudge (log channel only \u2014 never blocks).",
11460
- ' const fp = input.filePath || input.path || (input.file && input.file.path) || "";',
11464
+ ' const fp = input?.filePath || input?.path || (input?.file && input?.file.path) || "";',
11461
11465
  ' if (fp) console.warn(`\u{1F43B} [Kuma] ${fp} edited \u2014 consider kuma_safety({ action: "verify", scope: "${fp}" })`);',
11462
11466
  " },",
11463
11467
  ' "experimental.session.compacting": async (input, output) => {',
11464
- " // Compaction survival set via the native channel (docs: plugins#compaction).",
11465
11468
  " try {",
11466
11469
  ' const { execSync } = await import("node:child_process");',
11467
11470
  ' const ctx = execSync("kuma hook pre-compact", { encoding: "utf-8", timeout: 8000 });',
@@ -11473,28 +11476,46 @@ function opencodePluginTemplate() {
11473
11476
  " };",
11474
11477
  "};",
11475
11478
  "",
11476
- "// Server-runtime definition (required by newer OpenCode server).",
11477
- "// Reuses the same hooks via the named export above.",
11478
- "const kumaHooks = {",
11479
- ' "tool.execute.before": async (input, output) => {',
11480
- " const hooks = await KumaPlugin();",
11481
- ' return hooks["tool.execute.before"](input, output);',
11482
- " },",
11483
- ' "file.edited": async (input) => {',
11484
- " const hooks = await KumaPlugin();",
11485
- ' return hooks["file.edited"](input);',
11486
- " },",
11487
- ' "experimental.session.compacting": async (input, output) => {',
11488
- " const hooks = await KumaPlugin();",
11489
- ' return hooks["experimental.session.compacting"](input, output);',
11490
- " },",
11491
- "};",
11479
+ "// \u2500\u2500\u2500 v1 host: module object form (v1 >= 1.18 calls server(), ignores setup) \u2500",
11480
+ "async function server() {",
11481
+ " return {",
11482
+ ' "tool.execute.before": async (input, output) => {',
11483
+ " enforceGotchas(input?.tool, output?.args);",
11484
+ " },",
11485
+ ' "file.edited": async (input) => {',
11486
+ ' const fp = input?.filePath || input?.path || (input?.file && input?.file.path) || "";',
11487
+ ' if (fp) console.warn(`\u{1F43B} [Kuma] ${fp} edited \u2014 consider kuma_safety({ action: "verify", scope: "${fp}" })`);',
11488
+ " },",
11489
+ ' "experimental.session.compacting": async (input, output) => {',
11490
+ " try {",
11491
+ ' const { execSync } = await import("node:child_process");',
11492
+ ' const ctx = execSync("kuma hook pre-compact", { encoding: "utf-8", timeout: 8000 });',
11493
+ ' const parsed = JSON.parse(ctx || "{}");',
11494
+ ' const add = parsed?.hookSpecificOutput?.hookSpecificOutput?.additionalContext || "";',
11495
+ " if (add && output && Array.isArray(output.context)) output.context.push(add);",
11496
+ " } catch { /* compacting must never fail */ }",
11497
+ " },",
11498
+ " };",
11499
+ "}",
11500
+ "",
11501
+ "// \u2500\u2500\u2500 v2 host: setup registers hooks on ctx (mutable event draft) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
11502
+ "async function setup(ctx) {",
11503
+ ' const hook = ctx && ctx.tool && typeof ctx.tool.hook === "function"',
11504
+ " ? ctx.tool.hook.bind(ctx.tool)",
11505
+ " : null;",
11506
+ " if (!hook) return;",
11507
+ ' await hook("execute.before", async (event) => {',
11508
+ " const e = event || {};",
11509
+ ' const tool = e.tool ?? e.toolName ?? e.name ?? e.input?.tool ?? "";',
11510
+ " const args = e.input ?? e.args ?? e.output?.args ?? {};",
11511
+ " enforceGotchas(tool, args);",
11512
+ " });",
11513
+ "}",
11492
11514
  "",
11493
11515
  "export default {",
11494
11516
  ' id: "kuma",',
11495
- " // NOTE: setup must return hooks synchronously \u2014 never an async",
11496
- " // effect/event-loop. An async effect never resolves and hangs load.",
11497
- " setup: () => kumaHooks,",
11517
+ " server,",
11518
+ " setup,",
11498
11519
  "};",
11499
11520
  ""
11500
11521
  ].join("\n");
@@ -13836,6 +13857,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13836
13857
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13837
13858
 
13838
13859
  // src/manifest.ts
13860
+ import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
13839
13861
  import { z } from "zod";
13840
13862
 
13841
13863
  // src/tools/kumaContextTool.ts
@@ -15618,6 +15640,25 @@ function registerAllTools(server) {
15618
15640
  server.tool("kuma_kuma_safety", safetyDesc, safetySchema, safetyHandler);
15619
15641
  console.error("[Manifest] Registered 3 core tools with universal dual-prefix aliases (kuma_* and kuma_kuma_*).");
15620
15642
  console.error("[Manifest] Auto-init hooks installed on all tools.");
15643
+ const baseServer = server.server;
15644
+ const existingListHandler = baseServer._requestHandlers?.get("tools/list");
15645
+ if (existingListHandler) {
15646
+ const stripSchemaMeta = (schema) => {
15647
+ if (!schema || typeof schema !== "object") return;
15648
+ for (const key of ["$schema", "$id", "id", "title", "definitions", "$defs"]) {
15649
+ delete schema[key];
15650
+ }
15651
+ };
15652
+ baseServer.removeRequestHandler("tools/list");
15653
+ baseServer.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
15654
+ const result = await existingListHandler(request, extra);
15655
+ for (const tool of result?.tools ?? []) {
15656
+ stripSchemaMeta(tool.inputSchema);
15657
+ stripSchemaMeta(tool.outputSchema);
15658
+ }
15659
+ return result;
15660
+ });
15661
+ }
15621
15662
  }
15622
15663
 
15623
15664
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumpslabs/kuma",
3
- "version": "2.4.20",
3
+ "version": "2.4.22",
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",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://antigravity.google/schemas/v1/plugin.json",
3
3
  "name": "kuma",
4
- "version": "2.4.20",
4
+ "version": "2.4.22",
5
5
  "description": "Safety-first context & orchestration engine for AI coding agents. Shadow memory, gotcha lifecycle, blast radius impact analysis, and test verification.",
6
6
  "author": {
7
7
  "name": "plumpslabs",
@@ -100,5 +100,77 @@ Record what matters, skip what doesn't.
100
100
 
101
101
  πŸ“– Full rules: `.kuma/init.md`
102
102
 
103
+ πŸ“– Read `.kuma/init.md` for detailed rules.
104
+ πŸ“‚ Readable state: `.kuma/KNOWN_GOTCHAS.md` (gotchas) Β· `.kuma/memories/decisions.md` (decisions)
105
+
106
+ ---
107
+ _Generated by Kuma MCP - https://github.com/plumpslabs/kuma_
108
+
109
+ ---
110
+ name: kuma-mcp
111
+ description: Kuma MCP β€” .kuma/ is the single source of truth
112
+ ---
113
+
114
+ Kuma MCP tools: kuma_context, kuma_memory, kuma_safety.
115
+ Record what matters, skip what doesn't.
116
+
117
+ <kuma_obedience>
118
+ ## πŸ”’ Obedience β€” honest model (issue #39)
119
+ - HARNESS-ENFORCED (no choice): MCP schema rejects unknown actions; hooks inject gotchas pre-edit (`kuma hook pre-edit`); SessionStart serves the brief; dirty-flags patch the map post-edit.
120
+ - AGENT-EXPECTED (best-effort ~70%): call `kuma_context({ action: "init" })` first; record gotchas IMMEDIATELY; `kuma_safety({ action: "verify" })` after edits.
121
+ - FALLBACKS: init skipped β†’ lazy brief on first unfamiliar edit; gotcha skipped β†’ auto-gotcha on repeated failures; verify skipped β†’ CI is the backstop (never treat as verified).
122
+ </kuma_obedience>
123
+
124
+ <kuma_workflow>
125
+ 🧠 **Before coding:** `kuma_context({ action: "init" })`
126
+ πŸ—ΊοΈ **Monorepo map:** `kuma_context({ action: "map" })`
127
+ πŸ”¬ **Unfamiliar code / blast radius:** `kuma_context({ action: "research", scope: "<area>" })` or `kuma_context({ action: "impact", target: "<file>" })`
128
+ πŸ› **Found a bug/quirk:** `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)
129
+ 🧭 **Chose between options:** `kuma_memory({ action: "decision" })`
130
+ πŸ”€ **Traced a flow:** `kuma_memory({ action: "arch_flow" })` (max 5 files)
131
+ πŸ›‘οΈ **Before risky work / after edits:** `kuma_safety({ action: "guard" | "verify" })`
132
+ </kuma_workflow>
133
+
134
+ πŸͺ„ **Auto-inject (Claude Code):** gotcha/decision/history injected automatically before edits
135
+ via the PreToolUse hook β€” an empty hook output means the file has no gotchas.
136
+
137
+ πŸ“– Full rules: `.kuma/init.md`
138
+
139
+ πŸ“– Read `.kuma/init.md` for detailed rules.
140
+ πŸ“‚ Readable state: `.kuma/KNOWN_GOTCHAS.md` (gotchas) Β· `.kuma/memories/decisions.md` (decisions)
141
+
142
+ ---
143
+ _Generated by Kuma MCP - https://github.com/plumpslabs/kuma_
144
+
145
+ ---
146
+ name: kuma-mcp
147
+ description: Kuma MCP β€” .kuma/ is the single source of truth
148
+ ---
149
+
150
+ Kuma MCP tools: kuma_context, kuma_memory, kuma_safety.
151
+ Record what matters, skip what doesn't.
152
+
153
+ <kuma_obedience>
154
+ ## πŸ”’ Obedience β€” honest model (issue #39)
155
+ - HARNESS-ENFORCED (no choice): MCP schema rejects unknown actions; hooks inject gotchas pre-edit (`kuma hook pre-edit`); SessionStart serves the brief; dirty-flags patch the map post-edit.
156
+ - AGENT-EXPECTED (best-effort ~70%): call `kuma_context({ action: "init" })` first; record gotchas IMMEDIATELY; `kuma_safety({ action: "verify" })` after edits.
157
+ - FALLBACKS: init skipped β†’ lazy brief on first unfamiliar edit; gotcha skipped β†’ auto-gotcha on repeated failures; verify skipped β†’ CI is the backstop (never treat as verified).
158
+ </kuma_obedience>
159
+
160
+ <kuma_workflow>
161
+ 🧠 **Before coding:** `kuma_context({ action: "init" })`
162
+ πŸ—ΊοΈ **Monorepo map:** `kuma_context({ action: "map" })`
163
+ πŸ”¬ **Unfamiliar code / blast radius:** `kuma_context({ action: "research", scope: "<area>" })` or `kuma_context({ action: "impact", target: "<file>" })`
164
+ πŸ› **Found a bug/quirk:** `kuma_memory({ action: "gotcha" })` (IMMEDIATELY)
165
+ 🧭 **Chose between options:** `kuma_memory({ action: "decision" })`
166
+ πŸ”€ **Traced a flow:** `kuma_memory({ action: "arch_flow" })` (max 5 files)
167
+ πŸ›‘οΈ **Before risky work / after edits:** `kuma_safety({ action: "guard" | "verify" })`
168
+ </kuma_workflow>
169
+
170
+ πŸͺ„ **Auto-inject (Claude Code):** gotcha/decision/history injected automatically before edits
171
+ via the PreToolUse hook β€” an empty hook output means the file has no gotchas.
172
+
173
+ πŸ“– Full rules: `.kuma/init.md`
174
+
103
175
  πŸ“– Read `.kuma/init.md` for detailed rules.
104
176
  πŸ“‚ Readable state: `.kuma/KNOWN_GOTCHAS.md` (gotchas) Β· `.kuma/memories/decisions.md` (decisions)