kimetsu-pi 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -11,53 +11,66 @@
11
11
  **kimetsu-pi** brings [Kimetsu](https://kimetsu.dev) — a local-first memory brain
12
12
  for coding agents — to the [Pi](https://pi.dev) coding agent.
13
13
 
14
- Coding agents are brilliant and forgetful: every session starts from zero. Kimetsu
15
- is a sidecar brain that captures the lessons your agent earns, learns which ones
16
- actually help, and hands them back before the next task. The memory pipeline makes
17
- **no LLM calls** — storage and retrieval are 100% local, free, and offline-capable.
14
+ Kimetsu carries useful lessons between sessions and retrieves relevant project
15
+ knowledge before a task. Storage and retrieval run locally without LLM API calls.
16
+ Automatic lesson extraction can use a configured model; its cost and connectivity
17
+ depend on that configuration.
18
18
 
19
19
  Learn more at **[kimetsu.dev](https://kimetsu.dev)**.
20
20
 
21
21
  ## What it is
22
22
 
23
- Pi has no MCP layer, so this package brings Kimetsu to Pi through Pi's own
24
- extension points:
23
+ This package connects Kimetsu through Pi's native extension and skill interfaces:
25
24
 
26
25
  - **Extension** (`extensions/kimetsu.ts`) — a TypeScript Pi extension that hooks
27
- Pi lifecycle events (`session_start`, `agent_end`, `session_shutdown`) and shells
28
- out to the `kimetsu` binary to warm, load, and persist brain context around each
29
- session. Each call is capped by a timeout, so a slow or hung binary never stalls
30
- Pi. If the binary is not on `PATH`, every hook silently no-ops and Pi is
26
+ Pi lifecycle events and invokes the `kimetsu` binary to warm, load, and persist
27
+ brain context around each session. Interactive hooks have a 10-second timeout;
28
+ session saving has a
29
+ separate five-minute limit for model-based distillation. If the binary is not
30
+ on `PATH`, every hook silently no-ops and Pi is
31
31
  completely unaffected.
32
32
  - **Skill** (`skills/kimetsu-brain/SKILL.md`) — a Pi skill that teaches the agent
33
- when and how to consult and record memories during a task.
33
+ to reuse injected context, correct outdated memories, and cite memories that
34
+ actually helped during a task.
35
+
36
+ ## What's new in 0.1.4
37
+
38
+ - Fresh memory per task: older raw injections are excluded from model requests
39
+ and new summaries, including after corrections, queued user messages, and
40
+ branch changes.
41
+ - Session saving gets up to five minutes, while interactive hooks retain their
42
+ 10-second limits.
43
+ - Updated guidance for memory reuse, corrections, and usefulness feedback.
44
+ - Verified with Pi 0.85.1 and Kimetsu 2.8.0 on Linux, macOS, and Windows; updated
45
+ dependencies, isolated integration tests, and protected CI and release gates.
34
46
 
35
47
  ## Why Kimetsu
36
48
 
37
- - **Remembers what matters** — conventions, failure patterns, the exact command
38
- that regenerates your schema. Captured once, retrieved by meaning.
49
+ - **Remembers project knowledge** — conventions, failure patterns, and useful
50
+ commands stay available across sessions.
39
51
  - **Speaks first** — proactive session-start digests, episodic resumes, and
40
52
  pre-task context, so the agent's first turn already knows your repo.
41
- - **Learns what helps** — cited memories get promoted; stale advice decays and is
42
- pruned.
43
- - **Model-free retrieval** — FTS5 + local embeddings + local reranking. Zero API
44
- cost, works offline.
45
- - **Stays yours** — one SQLite file per project. No cloud, no vector DB, no
46
- telemetry. Back it up with `cp`.
53
+ - **Learns what helps** — citations provide usefulness feedback; maintenance
54
+ identifies memories that may need pruning.
55
+ - **Local retrieval** — lexical search in the default lean build, with optional
56
+ local embeddings and reranking. No LLM API calls for retrieval.
57
+ - **Stays yours** — a local SQLite brain per project, with optional user-wide
58
+ memory. No hosted vector database is required.
59
+
60
+ For Kimetsu's benchmark results, datasets, and evaluation limits, see the
61
+ [core project's benchmark notes](https://github.com/RodCor/kimetsu#benchmarks-vs-other-memory-systems).
62
+ Those evaluate the memory system; this package's CI verifies its Pi integration.
47
63
 
48
- ## Benchmarks
64
+ ## Prerequisites
49
65
 
50
- | | |
51
- |---:|---|
52
- | **89.4%** | LoCoMo, the long-conversation memory benchmark |
53
- | **83.0%** | LongMemEval, the public long-term-memory benchmark |
54
- | **73.3%** | BEAM 100K memory benchmark |
55
- | **13×** | cheaper per solved task ($0.19 vs $2.47 on a Terminal-Bench slice) |
56
- | **~1M** | memories in ~3 GB RAM with sub-2s retrieval, one SQLite file |
66
+ Install current Pi (Node.js 22.19 or newer):
57
67
 
58
- ## Prerequisite
68
+ ```sh
69
+ npm install -g @earendil-works/pi-coding-agent
70
+ ```
59
71
 
60
- The `kimetsu` binary must be on `PATH`. Install it with:
72
+ The `kimetsu` binary must be on `PATH`. Kimetsu 2.8.0 is the tested release for
73
+ this package; the CLI integration requires at least 2.7.0. Install it with:
61
74
 
62
75
  ```sh
63
76
  npm install -g kimetsu-ai
@@ -73,22 +86,98 @@ absent, the extension silently no-ops and Pi is unaffected.
73
86
  pi install npm:kimetsu-pi
74
87
  ```
75
88
 
89
+ From the Git project whose memory you want to use, initialize Kimetsu if that
90
+ project does not already have a brain:
91
+
92
+ ```sh
93
+ cd /path/to/your/project
94
+ kimetsu init
95
+ kimetsu brain status
96
+ ```
97
+
98
+ Start Pi in that project, or run `/reload` in an existing Pi session to load the
99
+ extension and skill. Confirm installation with `pi list`.
100
+
101
+ ## Upgrade
102
+
103
+ For an installation that follows the latest npm version:
104
+
105
+ ```sh
106
+ pi update npm:kimetsu-pi
107
+ ```
108
+
109
+ For a pinned installation, select this version explicitly:
110
+
111
+ ```sh
112
+ pi install npm:kimetsu-pi@0.1.4
113
+ ```
114
+
115
+ Then restart Pi or run `/reload`. Pi package updates and Kimetsu CLI updates are
116
+ separate; use `npm install -g kimetsu-ai@latest` if the CLI also needs updating.
117
+
76
118
  ## What it does on each event
77
119
 
78
120
  | Pi lifecycle event | Kimetsu command run |
79
121
  | --- | --- |
80
- | `session_start` | `kimetsu brain warm` then `kimetsu brain context-hook` |
122
+ | `session_start` | `kimetsu brain warm` |
123
+ | `before_agent_start` | `kimetsu brain context-hook --warm-on-first-prompt` |
124
+ | `context` | Filters earlier Kimetsu injections out of each model request |
125
+ | `session_before_compact`, `session_before_tree` | Excludes Kimetsu injections from new summaries |
126
+ | `session_tree` | Expires the previous branch's active injection |
81
127
  | `agent_end` | `kimetsu brain stop-hook` |
82
128
  | `session_shutdown` | `kimetsu brain session-end-hook` |
83
129
 
130
+ The extension uses Pi's `SessionManager` identity, so `/new`, `/resume`, and
131
+ `/fork` keep separate Kimetsu warm-start and deduplication state. It also passes
132
+ Pi's persisted JSONL transcript to the stop and session-end hooks, enabling
133
+ Kimetsu's configured distiller and automatic work-episode capture.
134
+
135
+ Only the current task's Kimetsu injection is sent to the model. Older injections
136
+ remain in the saved transcript but are filtered from subsequent model requests,
137
+ including when retrieval returns no context. Ordinary conversation and other
138
+ extensions' messages are preserved. Queued user messages expire the previous
139
+ injection; tool continuations keep it. New compaction and branch summaries omit
140
+ raw Kimetsu injections. This does not rewrite earlier assistant answers or
141
+ existing summaries.
142
+
143
+ Session saving can make two model calls, so it may take longer than retrieval.
144
+ Pi awaits it when closing or switching a session, for up to five minutes; it
145
+ normally finishes sooner. The longer deadline applies only to
146
+ `session-end-hook`. Prompt retrieval, warm-up, and `stop-hook` retain their
147
+ 10-second limits. Saving remains best-effort and needs a clean shutdown.
148
+
149
+ The bundled skill teaches the agent to reuse relevant injected context, fetch
150
+ missing evidence, correct existing memories, and cite only memories that
151
+ actually helped. Run `kimetsu brain status` from the project directory to inspect
152
+ the brain. Automatic model-based distillation requires configuration.
153
+
84
154
  ## Development
85
155
 
86
156
  ```sh
87
- npm install
157
+ npm ci
88
158
  npm test
89
159
  npm run typecheck
90
160
  ```
91
161
 
162
+ To run the optional CLI integration test, set `KIMETSU_TEST_BINARY` to the
163
+ absolute path of a Kimetsu 2.8.0+ executable named `kimetsu` (`kimetsu.exe` on
164
+ Windows), then run `npm run test:integration`. This command fails if the binary
165
+ is missing. It creates an isolated temporary brain, uses
166
+ lexical retrieval without model calls, and verifies correction, context
167
+ filtering, citation, and session persistence.
168
+
169
+ The fixture anchors a new Git repository and gives subprocesses temporary homes,
170
+ caches, and user-brain paths. It removes inherited Git/Kimetsu overrides and
171
+ credentials before initialization. Never run integration commands against your
172
+ normal project or home brain.
173
+
174
+ CI requires the real CLI test on Linux, macOS, and Windows using checksum-pinned
175
+ Kimetsu 2.8.0 binaries and the minimum supported Node version. The `required checks`
176
+ gate also requires typechecking, tests, the dependency audit, and package dry-run.
177
+ Release tags must match the package version and point to a commit on `main` with
178
+ successful CI. Wait for that commit's CI before tagging; a release started too
179
+ early must be rerun after CI passes.
180
+
92
181
  ## Links
93
182
 
94
183
  - **Website & docs:** [kimetsu.dev](https://kimetsu.dev)
@@ -1,57 +1,270 @@
1
1
  // Kimetsu brain extension for Pi (earendil-works/pi).
2
- // Published as the `kimetsu-pi` npm package. Shells out to the kimetsu binary
3
- // on Pi lifecycle events to load brain context at session start and record
4
- // audit markers on session end. If kimetsu is not on PATH the exec silently
5
- // fails; Pi startup is unaffected.
2
+ //
3
+ // CANONICAL SOURCE: kimetsu/crates/kimetsu-chat/assets/pi-extension.ts
4
+ // `kimetsu plugin install pi` writes this file verbatim, and the published
5
+ // `kimetsu-pi` npm package vendors a byte-identical copy (CI diffs the two).
6
+ // Edit it here — never in the installed or published copy.
7
+ //
8
+ // Pi exposes no MCP surface, so Kimetsu integrates by shelling out to the
9
+ // binary on lifecycle events. `before_agent_start` is the injection point:
10
+ // the hook payload goes in on stdin, the `additionalContext` block comes back
11
+ // on stdout, and Pi carries it into the turn as a context message.
12
+ //
13
+ // Every failure mode is a silent no-op: a missing binary, a hung binary, a
14
+ // crash, unparseable output. Kimetsu is a sidecar — it must never break Pi.
6
15
 
7
16
  import { spawn } from "node:child_process";
17
+ import { randomUUID } from "node:crypto";
18
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
8
19
 
9
- function kimetsuExec(args: string[]): Promise<void> {
20
+ /** Interactive hooks must not leave a turn waiting on a hung binary. */
21
+ const EXEC_TIMEOUT_MS = 10000;
22
+
23
+ /** Session saving may distill lessons and an episode in two model calls
24
+ * (120s each by default). Leave time for both plus local persistence. */
25
+ const SESSION_SAVE_TIMEOUT_MS = 300000;
26
+
27
+ /** Fallback session id when Pi's context does not expose one. Stable per process,
28
+ * which is what the brain's per-session dedupe and refractory windows need. */
29
+ const FALLBACK_SESSION_ID = `pi-${process.pid}`;
30
+
31
+ /**
32
+ * Run `kimetsu <args>`, optionally writing `input` to its stdin, and resolve
33
+ * with whatever it printed to stdout ("" on any failure).
34
+ *
35
+ * stdout is PIPED, not ignored: the context hook communicates entirely through
36
+ * it. stderr stays ignored so diagnostics never mix into the parsed payload.
37
+ */
38
+ function kimetsuRun(args: string[], input?: string, timeoutMs = EXEC_TIMEOUT_MS): Promise<string> {
10
39
  return new Promise((resolve) => {
11
40
  let settled = false;
12
41
  let timer: ReturnType<typeof setTimeout> | undefined;
42
+ let stdout = "";
13
43
  const done = () => {
14
44
  if (settled) return;
15
45
  settled = true;
16
46
  if (timer !== undefined) clearTimeout(timer);
17
- resolve();
47
+ resolve(stdout);
18
48
  };
19
49
  try {
20
50
  const child = spawn("kimetsu", args, {
21
- stdio: "ignore",
51
+ stdio: ["pipe", "pipe", "ignore"],
22
52
  shell: false,
23
53
  windowsHide: true,
24
54
  });
25
- // A hung binary must never stall the lifecycle hook: cap the wait and
26
- // kill the child if it overruns. unref() so the timer alone can't keep
27
- // the host process alive.
55
+ // Cap the wait and kill the child if it overruns. unref() so the timer
56
+ // alone can never keep the host process alive.
28
57
  timer = setTimeout(() => {
29
58
  child.kill();
30
59
  done();
31
- }, 10000);
60
+ }, timeoutMs);
32
61
  if (typeof timer.unref === "function") timer.unref();
62
+
63
+ child.stdout?.setEncoding("utf8");
64
+ child.stdout?.on("data", (chunk: string) => {
65
+ stdout += chunk;
66
+ });
67
+ child.stdout?.on("error", () => {}); // torn pipe — resolve with what we have
68
+ child.stdin?.on("error", () => {}); // EPIPE when the child exits early
69
+
33
70
  child.on("error", done); // binary not on PATH — silent no-op
34
- child.on("close", done); // finished, or killed by the timeout above
71
+ child.on("close", done); // 'close' (not 'exit') so stdout is complete
72
+
73
+ child.stdin?.end(input ?? "");
35
74
  } catch {
36
75
  done(); // any unexpected error — silent no-op
37
76
  }
38
77
  });
39
78
  }
40
79
 
41
- export default function (pi: any) {
80
+ /**
81
+ * Pull `hookSpecificOutput.additionalContext` out of a hook's stdout.
82
+ *
83
+ * The hook prints a single JSON line, but scanning from the end tolerates any
84
+ * stray output ahead of it. Anything unparseable yields `undefined`, which the
85
+ * callers treat as "nothing to inject".
86
+ */
87
+ function parseAdditionalContext(stdout: string): string | undefined {
88
+ const lines = stdout
89
+ .split("\n")
90
+ .map((line) => line.trim())
91
+ .filter((line) => line !== "")
92
+ .reverse();
93
+ for (const line of lines) {
94
+ try {
95
+ const parsed = JSON.parse(line);
96
+ const context = parsed?.hookSpecificOutput?.additionalContext;
97
+ if (typeof context === "string" && context.trim() !== "") return context;
98
+ } catch {
99
+ // Not JSON — keep looking at earlier lines.
100
+ }
101
+ }
102
+ return undefined;
103
+ }
104
+
105
+ /** Best-effort session id from Pi's handler context, across naming variants. */
106
+ function sessionIdOf(ctx: any): string {
107
+ // Current Pi exposes the durable id through SessionManager. Prefer it over
108
+ // historical context-field variants so /new, /resume, and /fork each get a
109
+ // distinct Kimetsu session even when they happen in the same Pi process.
110
+ const getSessionId = ctx?.sessionManager?.getSessionId;
111
+ if (typeof getSessionId === "function") {
112
+ try {
113
+ const id = getSessionId.call(ctx.sessionManager);
114
+ if (typeof id === "string" && id.trim() !== "") return id;
115
+ } catch {
116
+ // A third-party/legacy SessionManager must not break the host.
117
+ }
118
+ }
119
+ const candidates = [ctx?.sessionId, ctx?.sessionID, ctx?.session_id, ctx?.session?.id];
120
+ for (const candidate of candidates) {
121
+ if (typeof candidate === "string" && candidate.trim() !== "") return candidate;
122
+ }
123
+ return FALLBACK_SESSION_ID;
124
+ }
125
+
126
+ /** Current Pi's persisted JSONL transcript, when the session is not ephemeral. */
127
+ function transcriptPathOf(ctx: any): string | undefined {
128
+ const getSessionFile = ctx?.sessionManager?.getSessionFile;
129
+ if (typeof getSessionFile !== "function") return undefined;
130
+ try {
131
+ const path = getSessionFile.call(ctx.sessionManager);
132
+ return typeof path === "string" && path.trim() !== "" ? path : undefined;
133
+ } catch {
134
+ return undefined;
135
+ }
136
+ }
137
+
138
+ /** Host-neutral hook payload understood by Kimetsu v2.7. */
139
+ function lifecyclePayload(ctx: any, transcript?: unknown[]): string {
140
+ const payload: Record<string, unknown> = { session_id: sessionIdOf(ctx) };
141
+ const transcriptPath = transcriptPathOf(ctx);
142
+ if (transcriptPath !== undefined) payload.transcript_path = transcriptPath;
143
+ else if (Array.isArray(transcript)) payload.transcript = transcript;
144
+ return JSON.stringify(payload);
145
+ }
146
+
147
+ /** `--workspace <cwd>` when Pi tells us the working directory, else nothing
148
+ * (the CLI then defaults to its own cwd). */
149
+ function workspaceArgs(ctx: any): string[] {
150
+ const cwd = ctx?.cwd;
151
+ return typeof cwd === "string" && cwd.trim() !== "" ? ["--workspace", cwd] : [];
152
+ }
153
+
154
+ export default function (pi: ExtensionAPI) {
155
+ // Pi persists injected messages, including display:false messages. Only the
156
+ // current task's injection belongs in future model calls. A unique marker
157
+ // survives Pi's message cloning without trusting content or timestamps.
158
+ let activeContext: { id: string; sessionId: string } | undefined;
159
+
42
160
  // session_start fires once when Pi starts up or a new session begins.
43
- pi.on("session_start", async (_event: any, _ctx: any) => {
44
- await kimetsuExec(["brain", "warm"]);
45
- await kimetsuExec(["brain", "context-hook"]);
161
+ // Warming spawns the embedder daemon so the first real retrieval is semantic
162
+ // rather than falling back to lexical FTS.
163
+ // (`brain warm` takes no --workspace: it resolves the project from its cwd.)
164
+ pi.on("session_start", async (_event, _ctx) => {
165
+ activeContext = undefined;
166
+ await kimetsuRun(["brain", "warm"]);
167
+ });
168
+
169
+ // before_agent_start fires with the user's prompt, before the model is
170
+ // called, and can return a message that joins the turn. This is where brain
171
+ // context is injected. Pi has no session-start context surface, so
172
+ // --warm-on-first-prompt folds the repo digest and episodic resume into the
173
+ // first turn of each session.
174
+ pi.on("before_agent_start", async (event, ctx) => {
175
+ const request = { id: randomUUID(), sessionId: sessionIdOf(ctx) };
176
+ // Expire the last task immediately, even if retrieval is empty or fails.
177
+ activeContext = request;
178
+ const payload = JSON.stringify({
179
+ session_id: request.sessionId,
180
+ prompt: typeof event?.prompt === "string" ? event.prompt : "",
181
+ });
182
+ const stdout = await kimetsuRun(
183
+ ["brain", "context-hook", "--warm-on-first-prompt", ...workspaceArgs(ctx)],
184
+ payload,
185
+ );
186
+ // A session switch or a newer prompt can supersede an in-flight request.
187
+ if (activeContext !== request) return;
188
+ const content = parseAdditionalContext(stdout);
189
+ if (content === undefined) return; // nothing relevant — zero tokens
190
+ return {
191
+ message: {
192
+ customType: "kimetsu-brain",
193
+ content,
194
+ display: false,
195
+ details: { kimetsuContextId: request.id },
196
+ },
197
+ };
198
+ });
199
+
200
+ pi.on("context", async (event, ctx) => {
201
+ const context = activeContext;
202
+ const currentIndex = context && context.sessionId === sessionIdOf(ctx)
203
+ ? event.messages.findIndex((message) => {
204
+ if (message.role !== "custom" || message.customType !== "kimetsu-brain") return false;
205
+ const details = message.details as { kimetsuContextId?: unknown } | undefined;
206
+ return details?.kimetsuContextId === context.id;
207
+ })
208
+ : -1;
209
+ // Queued steering/follow-up messages bypass before_agent_start. Expire
210
+ // the old injection when a newer user message arrives; tool results alone
211
+ // do not end the current task's context.
212
+ if (currentIndex >= 0 && event.messages.some((message, index) =>
213
+ index > currentIndex && message.role === "user"
214
+ )) activeContext = undefined;
215
+
216
+ // Filter the model's copy only; preserve the persisted session history.
217
+ return {
218
+ messages: event.messages.filter((message, index) =>
219
+ message.role !== "custom" || message.customType !== "kimetsu-brain"
220
+ || (activeContext !== undefined && index === currentIndex)
221
+ ),
222
+ };
223
+ });
224
+
225
+ pi.on("session_before_compact", async (event) => {
226
+ // Summarization bypasses the context event. Do not turn retrieved evidence
227
+ // into a durable summary that could outlive a correction or invalidation.
228
+ event.preparation.messagesToSummarize = event.preparation.messagesToSummarize.filter(
229
+ (message) => message.role !== "custom" || message.customType !== "kimetsu-brain",
230
+ );
231
+ event.preparation.turnPrefixMessages = event.preparation.turnPrefixMessages.filter(
232
+ (message) => message.role !== "custom" || message.customType !== "kimetsu-brain",
233
+ );
234
+ });
235
+
236
+ pi.on("session_before_tree", async (event) => {
237
+ // Pi retains a reference to this temporary summary input array, so filter
238
+ // in place. The persisted session entries themselves are left untouched.
239
+ const entries = event.preparation.entriesToSummarize;
240
+ let kept = 0;
241
+ for (const entry of entries) {
242
+ if (entry.type !== "custom_message" || entry.customType !== "kimetsu-brain") {
243
+ entries[kept++] = entry;
244
+ }
245
+ }
246
+ entries.length = kept;
247
+ });
248
+
249
+ pi.on("session_tree", async () => {
250
+ activeContext = undefined;
46
251
  });
47
252
 
48
253
  // agent_end fires after the LLM turn completes (maps to Kimetsu stop-hook).
49
- pi.on("agent_end", async (_event: any, _ctx: any) => {
50
- await kimetsuExec(["brain", "stop-hook"]);
254
+ pi.on("agent_end", async (event, ctx) => {
255
+ await kimetsuRun(
256
+ ["brain", "stop-hook", ...workspaceArgs(ctx)],
257
+ lifecyclePayload(ctx, event.messages),
258
+ );
51
259
  });
52
260
 
53
261
  // session_shutdown fires on clean session close (maps to session-end-hook).
54
- pi.on("session_shutdown", async (_event: any, _ctx: any) => {
55
- await kimetsuExec(["brain", "session-end-hook"]);
262
+ pi.on("session_shutdown", async (_event, ctx) => {
263
+ activeContext = undefined;
264
+ await kimetsuRun(
265
+ ["brain", "session-end-hook", ...workspaceArgs(ctx)],
266
+ lifecyclePayload(ctx),
267
+ SESSION_SAVE_TIMEOUT_MS,
268
+ );
56
269
  });
57
270
  }
package/package.json CHANGED
@@ -1,26 +1,55 @@
1
1
  {
2
2
  "name": "kimetsu-pi",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Kimetsu brain as a Pi.dev package — local-first, cross-session memory for the Pi coding agent that gets sharper every run.",
5
- "keywords": ["pi-package", "pi", "kimetsu", "memory", "brain", "rag", "mcp", "extension", "skill"],
5
+ "keywords": [
6
+ "pi-package",
7
+ "pi",
8
+ "kimetsu",
9
+ "memory",
10
+ "brain",
11
+ "rag",
12
+ "mcp",
13
+ "extension",
14
+ "skill"
15
+ ],
6
16
  "homepage": "https://kimetsu.dev",
7
- "repository": { "type": "git", "url": "git+https://github.com/RodCor/kimetsu-pi.git" },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/RodCor/kimetsu-pi.git"
20
+ },
8
21
  "license": "MIT OR Apache-2.0",
9
22
  "type": "module",
10
23
  "pi": {
11
- "extensions": ["./extensions"],
12
- "skills": ["./skills"],
24
+ "extensions": [
25
+ "./extensions"
26
+ ],
27
+ "skills": [
28
+ "./skills"
29
+ ],
13
30
  "image": "https://raw.githubusercontent.com/RodCor/kimetsu/main/docs/assets/kimetsu-logo.png"
14
31
  },
15
- "files": ["extensions/", "skills/", "README.md", "LICENSE"],
16
- "engines": { "node": ">=16" },
32
+ "files": [
33
+ "extensions/",
34
+ "skills/",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "engines": {
39
+ "node": ">=22.19.0"
40
+ },
17
41
  "scripts": {
18
42
  "typecheck": "tsc --noEmit",
19
- "test": "vitest run"
43
+ "test": "vitest run",
44
+ "test:integration": "node scripts/test-integration.mjs"
20
45
  },
21
46
  "devDependencies": {
22
- "@types/node": "^20.0.0",
23
- "typescript": "^5.5.0",
24
- "vitest": "^2.0.0"
47
+ "@earendil-works/pi-coding-agent": "^0.85.1",
48
+ "@types/node": "^26.0.0",
49
+ "typescript": "^7.0.0",
50
+ "vitest": "5.0.0"
51
+ },
52
+ "peerDependencies": {
53
+ "@earendil-works/pi-coding-agent": "*"
25
54
  }
26
55
  }
@@ -1,19 +1,64 @@
1
1
  ---
2
2
  name: kimetsu-brain
3
- description: Use Kimetsu brain shell commands as a persistent memory sidecar across Pi sessions.
3
+ description: Use when Pi tasks benefit from prior session knowledge, durable lessons, memory corrections, or feedback on helpful memories.
4
4
  ---
5
- Kimetsu is a persistent brain sidecar accessible via the `kimetsu` CLI. Use it
6
- when the task may benefit from prior session knowledge, workflow memory, or
7
- durable cross-session context.
8
-
9
- Brain-first workflow:
10
- 1. Before planning or editing broad coding, review, debugging, or setup tasks,
11
- run `kimetsu brain context <query>` and read the returned capsules as working
12
- context before deciding on a plan.
13
- 2. After solving a non-obvious problem, run `kimetsu brain record` with a
14
- concrete, actionable lesson and 2-5 domain tags so future sessions benefit.
15
- 3. Run `kimetsu brain status` when you need to know whether the brain is
16
- initialized, has accepted memories, or has pending proposals.
17
-
18
- Optional mode: Kimetsu brain context is a preferred first step for non-trivial
19
- work. If the binary is unavailable, note the absence and continue normally.
5
+ Kimetsu is a persistent memory sidecar accessed through the `kimetsu` CLI.
6
+ Run commands from the relevant project directory. If the binary is unavailable,
7
+ note the absence and continue normally.
8
+
9
+ ## Use the context already provided
10
+
11
+ The Pi extension retrieves context before each task. Read that injection first.
12
+ When it covers the current question, proceed without repeating the same lookup.
13
+ Use `kimetsu brain context "<specific question>"` when no useful context was
14
+ injected, the task changes, or a missing detail or correction needs fresh evidence.
15
+ An empty result is a reason to inspect the repository, not repeat the same query.
16
+
17
+ Memory is evidence from earlier work. Check conflicts against current files and
18
+ the user's instructions. Respect project, environment, and version boundaries;
19
+ partial or conflicting evidence does not justify filling gaps with assumptions.
20
+
21
+ ## Record and correct durable lessons
22
+
23
+ After verifying a reusable lesson, record it with
24
+ `kimetsu brain memory add --scope project --kind <kind> "<lesson>"`.
25
+ Choose `fact`, `preference`, `convention`, `command`, or `failure_pattern`.
26
+ Include the subject and any environment/version limits in the text.
27
+
28
+ For a correction to an existing claim, update its actual memory ID instead of
29
+ adding a contradictory duplicate:
30
+
31
+ ```sh
32
+ kimetsu brain memory edit <memory-id> --text "Production gateway port is 4000. Development gateway port remains 3000."
33
+ ```
34
+
35
+ Preserve still-valid parts of the claim. Different environments or historical
36
+ versions can both be valid; a production correction does not retire development
37
+ guidance. When the entire memory is obsolete or false, use
38
+ `kimetsu brain memory invalidate <memory-id> --reason "<verified reason>"`.
39
+
40
+ Find actual IDs with `kimetsu brain context "<specific question>" --json` or
41
+ `kimetsu brain memory list --json`; match the text and scope before editing,
42
+ invalidating, or citing. For a `memory:<id>` expansion handle, use only `<id>`.
43
+ Never invent an ID or treat a file capsule as a memory.
44
+ The edit/invalidate commands above operate on the current workspace brain.
45
+ Listings may also include portable user-brain memories; those commands cannot
46
+ correct them. For a user-brain claim, report the correction and this limitation
47
+ instead of claiming the portable memory was updated.
48
+
49
+ ## Credit explicit usefulness
50
+
51
+ When a particular memory materially helped, record that reliance:
52
+
53
+ ```sh
54
+ kimetsu brain cite --memory-id <memory-id> --query "<task it helped with>" --note "<how it helped>"
55
+ ```
56
+
57
+ Cite only memories actually used. Being injected, or having tests pass, is not
58
+ enough to credit a memory. A citation records usefulness, not proof of truth;
59
+ correct wrong claims using the commands above. Do not cite unused memories or
60
+ repeat credit for the same use.
61
+
62
+ `kimetsu brain status` reports initialization, accepted memories, and pending
63
+ proposals. Automatic session saving is separate from these deliberate actions;
64
+ model-based lesson distillation requires a configured distiller.