klypix-mcp 1.106.2 → 1.107.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.
package/A2A.md CHANGED
@@ -55,7 +55,7 @@ the skills below.
55
55
  |---|---|---|
56
56
  | `make_board` | Create a new `.klypix` from cards + connections | a `.klypix` **FilePart** + summary |
57
57
  | `remember` | Append cards/decisions to an existing canvas (positions preserved) | result summary; flat card appends also return a `.klypix` FilePart |
58
- | `learn_skill` | Capture a reusable how-to / gotcha as a 🛠 skill card — resurfaces every session | result summary |
58
+ | `learn_skill` | Capture a reusable how-to / gotcha as a 🛠 skill card — kept until corrected; shown first to sessions that touch the files it names | result summary |
59
59
  | `recall` | Search card text/titles/`#tags` across the vault | matching cards (text) |
60
60
  | `read_canvas` | Read one canvas (cards, graph, `[[links]]`) + its images | markdown + image FileParts |
61
61
  | `list_canvases` | List canvases with counts | text |
package/README.md CHANGED
@@ -56,8 +56,8 @@ You need **Node.js 20 or newer** (`node -v`).
56
56
  `brain_sync` in other hosts. A correction replaces the old decision, and the old card stays as
57
57
  history.
58
58
  - **A heads-up when work overlaps.** Each session says what it is doing and which files it expects
59
- to touch. When two sessions name the same file, both are warned. KLYPIX warns; it never blocks an
60
- edit. This works on one computer. With [KLYPIX network](https://klypix.com/network)
59
+ to touch. When two sessions name the same file, both are warned. For overlapping work KLYPIX
60
+ warns; it never blocks an edit. This works on one computer. With [KLYPIX network](https://klypix.com/network)
61
61
  installed on both computers (klypix-mcp 1.106.0 or later), it also lists sessions in the same
62
62
  project on a teammate's computer.
63
63
  - **Notes and handoffs between sessions.** An agent can leave a note for another session, even one
@@ -69,6 +69,11 @@ You need **Node.js 20 or newer** (`node -v`).
69
69
  git command that would sweep another live session's uncommitted work into your commit is stopped.
70
70
  A command that would throw work away saves a rescue copy first, then asks you. It covers git
71
71
  commands run through Claude Code's tools, on one computer.
72
+ - **Lessons that catch the repeat mistake.** A lesson the brain recorded can become a project rule
73
+ that a person approves: a check of the diff, with no AI at check time. A rule warns when a session
74
+ plans its files, while it edits and at commit. Only a rule the person approves as blocking stops
75
+ the work: Claude Code before it writes the forbidden line (Codex is warned), and an opt-in git
76
+ hook at commit, for any tool or person. Rules check the diff; they do not run your tests.
72
77
  - **A file you own.** The brain is one `.klypix` file in your repo: a plain ZIP, versioned with
73
78
  Git. The engine is Apache-2.0, runs on your machine, and works without the KLYPIX app or an
74
79
  account. Brain data stays in your files; your agent's model provider still receives what the
@@ -88,7 +93,7 @@ has the details.
88
93
  | **Claude Desktop** | A one-time config entry | The KLYPIX app's Settings, or by hand |
89
94
  | **Claude, as a plugin** | One pinned version, keyword search, no hooks | [klypix-plugin](https://github.com/dahshanlabs/klypix-plugin) |
90
95
 
91
- klypix-mcp 1.106.0 has 26 MCP tools on Linux and macOS and 27 on Windows, where `show_in_klypix` is
96
+ klypix-mcp has 27 MCP tools on Linux and macOS and 28 on Windows, where `show_in_klypix` is
92
97
  added. `npx klypix-mcp doctor` shows the count for your install.
93
98
 
94
99
  > **One project. Many agents. One current understanding.**
@@ -118,14 +123,15 @@ tool's official documentation and says that KLYPIX wrote it) · [klypix.com/klyp
118
123
  - **[Reference](#reference):** [How the brain works](#how-the-brain-works) ·
119
124
  [Supported hosts](#supported-hosts-and-their-integration-level) ·
120
125
  [Running as a Claude plugin](#running-as-a-claude-plugin) · [Task briefing](#task-briefing) ·
121
- [Capture and corrections](#capture-and-corrections) · [Presence and task intent](#presence-and-task-intent) ·
126
+ [Capture and corrections](#capture-and-corrections) · [Project rules](#project-rules-lessons-that-stop-the-repeat) ·
127
+ [Presence and task intent](#presence-and-task-intent) ·
122
128
  [Overlap warnings](#overlap-warnings) ·
123
129
  [Teammates on other computers](#teammates-on-other-computers-klypix-network) ·
124
130
  [Handoffs and messages](#handoffs-and-messages) · [Evidence-gated completion](#evidence-gated-completion) ·
125
131
  [Human control in KLYPIX](#human-control-in-klypix) · [Measure it yourself](#measure-it-yourself) ·
126
132
  [Git and concurrency](#git-and-concurrency) ·
127
133
  [The git guard for a shared checkout](#whose-is-this-the-git-guard-for-a-shared-checkout) ·
128
- [The command line](#the-command-line) · [The verbs](#the-verbs-26-and-27-on-windows) ·
134
+ [The command line](#the-command-line) · [The verbs](#the-verbs-27-and-28-on-windows) ·
129
135
  [One file you can hold](#one-file-you-can-hold) · [Use it as a library](#use-it-as-a-library) ·
130
136
  [A2A](#also-speaks-a2a-protocol-v030--experimental) ·
131
137
  [Updates](#updates--the-propagation-contract) · [Security and permissions](#security-and-permissions) ·
@@ -561,9 +567,9 @@ on cards, and, on a call that needs the running app, the app's bridge token (abo
561
567
 
562
568
  | Where | What | Why |
563
569
  |---|---|---|
564
- | Your project | Only what a tool call asks for: `brain.klypix` (`brain_note` and the other brain tools), canvases (`create_canvas`, `add_to_canvas`), `klypix-map/graph.json` when `project_map_scan` is called, and `.klypix/claims/<owner>.json` when `brain_sync` is asked to publish a release claim. During a brain write it holds `.claude/brain-capture.lock`, creating the `.claude/` folder if the project has none. The lock file is deleted after the write; the folder stays. Creating a canvas briefly holds `.klypix-create.lock` in the folder. | The KLYPIX app and every other session that writes the brain use the same locks, so two writers never overwrite each other. |
570
+ | Your project | Only what a tool call asks for: `brain.klypix` (`brain_note` and the other brain tools), canvases (`create_canvas`, `add_to_canvas`), `klypix-map/graph.json` when `project_map_scan` is called, `.klypix/claims/<owner>.json` when `brain_sync` is asked to publish a release claim, and `.klypix/rules.json` when `brain_rules` adds, approves or retires a rule. Only when you run `npx klypix-mcp check --install-hook`: a marked block in `.git/hooks/pre-commit`, `pre-push` and `post-commit`; `npx klypix-mcp rules allow` keeps its one-hour exception in `.git/klypix-rules-allow.json`. During a brain write it holds `.claude/brain-capture.lock`, creating the `.claude/` folder if the project has none. The lock file is deleted after the write; the folder stays. Creating a canvas briefly holds `.klypix-create.lock` in the folder. | The KLYPIX app and every other session that writes the brain use the same locks, so two writers never overwrite each other. |
565
571
  | The plugin's data folder (`KLYPIX_PLUGIN_DATA`) | Connection receipts (`.supervisors/`), the running-server heartbeat (`.running-servers.json`), the list of projects whose brains you used (`registry.json`, which `search_all_brains` reads), and the last version and git tag seen in each project (`ship-observations/`). Also `extracted/`: copies of the PDFs, Office and other files `read_card_contents` hands to your AI tool as a local path, taken from the canvas you asked about. | Only this server uses these files. If `KLYPIX_PLUGIN_DATA` is not set, or still contains a `${...}` that was never filled in, it uses `CLAUDE_PLUGIN_DATA`. If neither is usable, the files go in `~/.claude/project-brain`, except `extracted/`, which then goes in `<system temp>/klypix-mcp/extracted` (where it always goes outside plugin mode). |
566
- | `~/.claude/project-brain` (shared) | Presence lanes (`sessions/`), write locks (`locks/`), restore points (`history/`), and small records built from your brain: `.capture-gap.json`, and `enrichment/`, `provenance/`, `.brief-cache-*` and `.guards-*` when the tools that use them run. | Your other KLYPIX sessions on this computer (Claude Code, Codex, Cursor, the app) use the same files on purpose. Through them, a plugin session and a terminal session on the same project see each other, get warnings when they plan to edit the same files, and pass notes. Restore points are kept here so that `npx klypix-mcp brain-history` can still undo a brain write after you remove the plugin. |
572
+ | `~/.claude/project-brain` (shared) | Presence lanes (`sessions/`), write locks (`locks/`), restore points (`history/`), and small records built from your brain: `.capture-gap.json`, and `enrichment/`, `provenance/`, `.brief-cache-*` and `.guards-*` when the tools that use them run. `rules/` counts how often each project rule fired, blocked or was overridden on this computer. | Your other KLYPIX sessions on this computer (Claude Code, Codex, Cursor, the app) use the same files on purpose. Through them, a plugin session and a terminal session on the same project see each other, get warnings when they plan to edit the same files, and pass notes. Restore points are kept here so that `npx klypix-mcp brain-history` can still undo a brain write after you remove the plugin. |
567
573
 
568
574
  **`brain_reopen`.** Sometimes a session leaves a note for another session that has already closed.
569
575
  An agent can then call `brain_reopen`, and KLYPIX asks you first: in chat with *Reopen* and *Not
@@ -586,7 +592,10 @@ Every other host gets a bounded ~2.8KB task capsule from one `brain_sync` call,
586
592
  always-loaded `AGENTS.md` block that tells the agent to make that call at task start, when scope
587
593
  changes, and on completion. The gateway capsule is lexical-fast by design. A newly captured open
588
594
  gap can claim a labeled `RECENT OPEN` slot only after clearing the normal lexical-relevance floor,
589
- so fresh relevant findings are not crowded out by older area vocabulary.
595
+ so fresh relevant findings are not crowded out by older area vocabulary. When the call declares
596
+ `files`, the lessons that name those files come first, under "Lessons for your files": at most
597
+ four, ranked exact path, then file name, glob and folder. The Claude Code brief does the same for
598
+ the files the checkout is changing (uncommitted, and the last three commits), then lists the newest.
590
599
 
591
600
  Briefs are **not** injected automatically on Cursor, Cline, Copilot, Gemini CLI or Antigravity —
592
601
  there are no lifecycle hooks on those hosts.
@@ -648,7 +657,9 @@ On Claude Code, decisions are captured automatically at turn end from inline `
648
657
  markers in the transcript, deduped, under a capture lock.
649
658
 
650
659
  A marker can end with optional suffixes, in any order: `closes: <card title or [[wikilink]]>`,
651
- `ev: <file[:line]>, PR#<n>`, `verify: <command>` and `q: <the question this answers?>`. They count
660
+ `ev: <file[:line]>, PR#<n>`, `verify: <command>`, `q: <the question this answers?>` and
661
+ `rule: <glob>[,<glob>] never "<text>"` (or `never /<regex>/`, `must "<text>"`,
662
+ `listed-in <file>[,<file>]`, `keep-eol`, `costly`). They count
652
663
  only as one run at the end of the line, written lowercase as `key: value`, and each value must
653
664
  have its key's shape: references for `ev:`, a command for `verify:`, a question (question word
654
665
  first, `?` last) for `q:`. Anything else is kept as card text, so "a Q: and A: layout", "the ev:
@@ -663,6 +674,13 @@ content words and under half the card's) is appended to that card as a dated `(~
663
674
  instead. The card keeps its date, colour and probe, and previews show the newest amendment first.
664
675
  The correction lands and nothing is lost.
665
676
 
677
+ A `rule:` suffix makes a new lesson (a decision, `+` or `!`) rule-ready. Its globs must name files
678
+ tracked in the repository and a `/regex/` must compile; the rule lands as a visible
679
+ `⚙ rule candidate:` line on a 🛠️ card, and `brain_rules` propose reads it before any prose. It
680
+ checks nothing until a person approves the rule. A rule that fails those checks is not recorded,
681
+ the lesson is, and the hook says why on the next prompt. `brain_note` takes the same suffix at the
682
+ end of its text.
683
+
666
684
  The Stop hook reads the whole transcript every time, so a `~` or `✓` line applies once. A marker
667
685
  that an older hook (1.85 or 1.86.0) already captured is not captured again after an upgrade. If
668
686
  1.86.0 cut the note short, the stub is restored to the full text in place. When a marker does not
@@ -684,6 +702,27 @@ only (explicit correction cues, opposite-polarity pairs), never mere topical sim
684
702
  means no contradiction signal was found — not verified consistency. A memory that cannot disagree
685
703
  with you is flattery.
686
704
 
705
+ ### Project rules: lessons that stop the repeat
706
+
707
+ Some lessons can be checked in a diff: *every new route must also be in the route list*, *tests
708
+ in this folder must import shared code relatively*, *this file's line endings must not change*. `brain_rules` turns those
709
+ lessons into rules in `.klypix/rules.json`, each citing the card it came from. A person approves
710
+ each rule with a code only they can print (`npx klypix-mcp rules code promote <ids>`). Until then
711
+ the rule is a candidate and checks nothing.
712
+
713
+ An active rule runs in three places: `brain_sync` names the rules for a session's files, the edit
714
+ hook reports findings to Claude Code and Codex as they edit, and the opt-in git hooks
715
+ (`npx klypix-mcp check --install-hook`) print them at commit and push. Rules warn by default. A
716
+ rule the person approves with `--block` stops the commit or push for any tool or person, and stops
717
+ Claude Code before it writes a forbidden line; Codex is only warned, because its hooks cannot
718
+ deny. A check is glob, regex and line-ending work over the diff: it takes
719
+ milliseconds, runs offline and uses no model. Overrides, bypasses and false alarms are recorded in
720
+ the brain, and `npx klypix-mcp rules` shows each rule's blocks, overrides and false alarms.
721
+
722
+ How to write, approve and retire rules: [docs/brain-rules.md](docs/brain-rules.md). To see what a
723
+ set of rules would have flagged on a repository's own history, `scripts/brain-rules-proof.mjs`
724
+ replays them over its pull requests and failed Actions logs, with what those runs cost.
725
+
687
726
  ### Presence and task intent
688
727
 
689
728
  An active session means an authorized MCP connection or host lifecycle adapter that heartbeated
@@ -1164,10 +1203,12 @@ The MCP verbs below are what agents call. These are what **you** call:
1164
1203
  | `npx klypix-mcp diff [ref]` | Card-level brain diff against a git ref, as markdown |
1165
1204
  | `npx klypix-mcp pr-brief [ref]` | Brain cards referencing the files changed since a ref, as markdown |
1166
1205
  | `npx klypix-mcp garden-code` | Print the human approval code `brain_garden` requires |
1206
+ | `npx klypix-mcp check` | Run this project's rules (`.klypix/rules.json`) on the working tree, `--staged`, or `--base <ref>`; `--install-hook` wires the opt-in pre-commit, pre-push and post-commit hooks |
1207
+ | `npx klypix-mcp rules` | The person's side of project rules: per-rule counts and suggestions, `code promote\|retire <ids>` (the approval code), `allow <id> --reason` (one hour), `false-alarm <id> --reason` |
1167
1208
  | `npx klypix-mcp rescue` | Rescue copies the git guard saved before a discard: `list`, `show <id>`, `restore <id> [paths…]`, `prune` |
1168
1209
  | `npx klypix-mcp sessions` | This project's closed sessions and the notes waiting for them; `sessions reopen <id>` reopens one |
1169
1210
 
1170
- ### The verbs: 26, and 27 on Windows
1211
+ ### The verbs: 27, and 28 on Windows
1171
1212
 
1172
1213
  | Tool | What it does |
1173
1214
  |---|---|
@@ -1178,6 +1219,7 @@ The MCP verbs below are what agents call. These are what **you** call:
1178
1219
  | `brain_insights` | Hubs, orphaned decisions, stale questions, area sizes |
1179
1220
  | `brain_lens` | Machine-readable freshness, provenance, activity, timeline, orrery and unresolved views; on a canvas open in KLYPIX (Windows), the first five come from KLYPIX's own lens code |
1180
1221
  | `brain_garden` | Maintenance pass — proposes first; consolidation cannot apply without an approval code the human generates. The separate `repair:"duplicate-partials"` pass is dry-run first and needs no code (it removes only exact repeats and archives nothing) |
1222
+ | `brain_rules` | Project rules made from the brain's lessons: `propose` routes each lesson to rule, instruction or note; `add` writes candidates to `.klypix/rules.json`; `promote` and `retire` need the person's approval code, and a rule warns unless the person approved it with `--block`; `check` runs the rules on a diff; `status` shows blocks, overrides, false alarms and retirement suggestions. No AI at check time ([docs/brain-rules.md](docs/brain-rules.md)) |
1181
1223
  | `brain_doctor` | Self-diagnosis: version, core/enhanced host adapters, active sessions, tool count, projection drift |
1182
1224
  | `brain_message` | Session-to-session coordination notes — to a live session, or queued for one that is not running until it next starts — with a fixed send-time audience and per-recipient pending / offer / acknowledgement / consumption / failure receipts (a directed note is kept 7 days, a broadcast 24h; never written into the brain) |
1183
1225
  | `brain_message_receipt` | Explicitly record model-side consumption using the exact message id and per-recipient offer token; acknowledgement alone never consumes a note |
@@ -1199,7 +1241,7 @@ The MCP verbs below are what agents call. These are what **you** call:
1199
1241
  | `add_to_canvas` | Append cards/connections (positions preserved), bordered and readable on KLYPIX's dark and Paper themes; a card's `group` puts it in that titled box. On a canvas open in KLYPIX (Windows) the cards go to KLYPIX itself: live, attributed, one undo. When KLYPIX cannot take them it refuses (`OPEN_IN_APP`, `ACCESS_OFF`, `BLOCKED`), a box locked from AI tools (`SCOPE_LOCKED`) or a frozen box (`FROZEN`), and writes nothing; project brains are the exception to the first. Returns the new card ids |
1200
1242
  | `list_canvases` | List every `.klypix` in the vault |
1201
1243
 
1202
- Exactly 26 on Linux and macOS and 27 on Windows, where `show_in_klypix` is registered; machine-verifiable with `npx klypix-mcp doctor`, whose TOOLS line applies the same rule and whose App bridge line says whether KLYPIX is running, with access on or off.
1244
+ Exactly 27 on Linux and macOS and 28 on Windows, where `show_in_klypix` is registered; machine-verifiable with `npx klypix-mcp doctor`, whose TOOLS line applies the same rule and whose App bridge line says whether KLYPIX is running, with access on or off.
1203
1245
 
1204
1246
  > **`canvas_view`:** no MCP Apps host has been observed rendering the UI resource yet — there is no
1205
1247
  > screenshot and no host-level test. Hosts without the extension get clean text, which is the path
@@ -1413,6 +1455,11 @@ Read this section before you build on any of it.
1413
1455
  `src/auth/token.ts` does not match a rename. Folder overlap is advisory. The merge test sees
1414
1456
  committed work only, needs git 2.38 or newer, and runs only for sessions on different local
1415
1457
  branches of the same repository.
1458
+ - **Project rules check the diff, not behaviour.** They see file names, added lines, line endings
1459
+ and paths; they do not run code or tests. They warn unless the person approved them with
1460
+ `--block`; Codex is warned, not stopped, while it edits, and the commit hooks are opt-in per
1461
+ clone. The approval code and the refusal inside agent shells prevent accidents; they do not stop
1462
+ a deliberate workaround.
1416
1463
  - **Overlap warnings are advisory.** Nothing is blocked. One severity string in the payload reads
1417
1464
  `blocking`; the mechanism is not.
1418
1465
  - **Codex has no automatic capture**, with or without `--codex-hooks`. The Codex hook never writes
@@ -132,7 +132,7 @@ function agentCard(publicUrl) {
132
132
  {
133
133
  id: 'learn_skill',
134
134
  name: 'Learn a reusable skill (how-to / gotcha)',
135
- description: 'Record a 🛠️ SKILL — a reusable how-to, gotcha, or convention ("always dedup zKeys before REORDER") that should resurface every session and never age out, distinct from a one-time decision. Routes through the capture engine with the "+" marker. A delegating agent uses this to teach the shared brain how work is done here, so every future agent inherits it. Plain "remember" auto-promotes to a skill when the text reads as a general rule.',
135
+ description: 'Record a 🛠️ SKILL — a reusable how-to, gotcha, or convention ("always dedup zKeys before REORDER") that is kept until corrected and shown first to sessions that touch the files it names, distinct from a one-time decision. Routes through the capture engine with the "+" marker. A delegating agent uses this to teach the shared brain how work is done here, so every future agent inherits it. Plain "remember" auto-promotes to a skill when the text reads as a general rule.',
136
136
  tags: ['memory', 'skill', 'how-to', 'procedure', 'brain', 'learn'],
137
137
  examples: ['Learn this gotcha: never set backgroundThrottling false — it breaks visibility detection', 'Remember the convention: Electron main imports must stay at top'],
138
138
  inputModes: ['text/plain', 'application/json'],
@@ -134,7 +134,8 @@ try {
134
134
  metrics.startAttempts = { first: aStarted.attempt, second: bStarted.attempt };
135
135
  const aStructured = aStart.structuredContent || {};
136
136
  const bStructured = bStart.structuredContent || {};
137
- checks.taskMemory = Array.isArray(aStructured.context?.hits) && aStructured.context.hits.length > 0;
137
+ checks.taskMemory = (Array.isArray(aStructured.context?.hits) && aStructured.context.hits.length > 0)
138
+ || (Array.isArray(aStructured.context?.fileLessons) && aStructured.context.fileLessons.length > 0);
138
139
  checks.truthfulTaskCount = bStructured.counts?.activeTasks === 2
139
140
  && bStructured.peers?.length === 1;
140
141
  checks.exactBlockingOverlap = bStructured.conflicts?.length === 1
@@ -349,7 +349,9 @@ const flatten = (code) => code
349
349
  // app bridge, which imports the protocol.
350
350
  // session-handoff (1.99.1): the worker's brain_sync imports CONTINUE_RE and
351
351
  // formatHandoffCards from it directly (1.99.0 only reached it via core).
352
- .replace(/\.\.\/src\/(bench|brain-doctor|agent-presence|agent-rules|capture-gap|enrichment|finding-routing|mcp-presence|mcp-supervisor|mcp-auto-update|presence-relay|repo-state|semantic-memory|runtime-inspector|project-graph|git-capture-install|app-lease|app-tools|app-bridge-protocol|app-bridge-client|session-handoff)\.mjs/g, './$1.mjs')
352
+ // brain-rules (project rules): the worker's brain_rules tool and the
353
+ // dispatcher's `check` / `rules` verbs import it lazily.
354
+ .replace(/\.\.\/src\/(bench|brain-doctor|agent-presence|agent-rules|capture-gap|enrichment|finding-routing|mcp-presence|mcp-supervisor|mcp-auto-update|presence-relay|repo-state|semantic-memory|runtime-inspector|project-graph|git-capture-install|app-lease|app-tools|app-bridge-protocol|app-bridge-client|session-handoff|brain-rules)\.mjs/g, './$1.mjs')
353
355
  .replace(/klypix-worker\.mjs/g, 'klypix-mcp-worker.mjs')
354
356
  .replace(/const PKG_VERSION = \(\(\) => \{[\s\S]*?\}\)\(\);/, `const PKG_VERSION = '${VERSION}'; // baked at install (flat layout has no package.json)`);
355
357
 
@@ -495,7 +497,7 @@ try {
495
497
  // a newer klypix-format cannot. merge-brains and the driver ship here so
496
498
  // brain-history's restore merge, the KLYPIX core and the git driver all
497
499
  // find one engine in this directory.
498
- for (const f of ['global-brain-hook.mjs', 'klypix-format.mjs', 'brain-graveyard.mjs', 'merge-brains.mjs', 'klypix-merge-driver.mjs', 'capture-gap.mjs', 'brain-semantic.mjs', 'semantic-memory.mjs', 'enrichment.mjs', 'provenance.mjs', 'brain-note.mjs', 'brain-evidence.mjs', 'brain-git-hook.mjs', 'git-capture-install.mjs', 'brain-history.mjs', 'app-lease.mjs', 'klypix-core.mjs', 'app-bridge-protocol.mjs', 'app-bridge-client.mjs', 'app-tools.mjs', 'card-files.mjs', 'brain-write-lock.mjs', 'agent-rules.mjs', 'brain-doctor.mjs', 'editor-detect.mjs', 'agent-presence.mjs', 'mcp-presence.mjs', 'repo-state.mjs', 'result-reconcile.mjs', 'finding-routing.mjs', 'presence-relay.mjs', 'network-bridge.mjs', 'mcp-supervisor.mjs', 'mcp-auto-update.mjs', 'runtime-inspector.mjs', 'project-graph.mjs', 'bench.mjs', 'codex-brain-hook.mjs', 'codex-hooks.mjs', 'session-handoff.mjs', 'live-projection.mjs', 'whose.mjs', 'whose-classify.mjs', 'whose-ledger.mjs', 'whose-rescue.mjs', 'canvas-view-app.html']) {
500
+ for (const f of ['global-brain-hook.mjs', 'klypix-format.mjs', 'brain-graveyard.mjs', 'merge-brains.mjs', 'klypix-merge-driver.mjs', 'capture-gap.mjs', 'brain-semantic.mjs', 'semantic-memory.mjs', 'enrichment.mjs', 'provenance.mjs', 'brain-note.mjs', 'brain-evidence.mjs', 'brain-git-hook.mjs', 'git-capture-install.mjs', 'brain-history.mjs', 'app-lease.mjs', 'klypix-core.mjs', 'app-bridge-protocol.mjs', 'app-bridge-client.mjs', 'app-tools.mjs', 'card-files.mjs', 'brain-write-lock.mjs', 'brain-rules.mjs', 'agent-rules.mjs', 'brain-doctor.mjs', 'editor-detect.mjs', 'agent-presence.mjs', 'mcp-presence.mjs', 'repo-state.mjs', 'result-reconcile.mjs', 'finding-routing.mjs', 'presence-relay.mjs', 'network-bridge.mjs', 'mcp-supervisor.mjs', 'mcp-auto-update.mjs', 'runtime-inspector.mjs', 'project-graph.mjs', 'bench.mjs', 'codex-brain-hook.mjs', 'codex-hooks.mjs', 'session-handoff.mjs', 'live-projection.mjs', 'whose.mjs', 'whose-classify.mjs', 'whose-ledger.mjs', 'whose-rescue.mjs', 'canvas-view-app.html']) {
499
501
  const s = path.join(SRC, f); if (exists(s)) staged.push({ dst: f, content: fs.readFileSync(s, 'utf8') });
500
502
  }
501
503
  for (const [src, dst] of [
@@ -572,7 +574,9 @@ try {
572
574
  // Guard cards (2026-08-24): pre-action lane. Fast path — reads only the
573
575
  // compiled guard sidecar; 'warn' injects context, 'block' denies with
574
576
  // the card's message. Absent guards = stat + one tiny read, then no-op.
575
- ['PreToolUse', { matcher: 'Bash|PowerShell|Edit|Write', hooks: [{ type: 'command', command: brainCmd('--guard'), timeout: 10 }] }],
577
+ // Project rules (.klypix/rules.json) ride the same lane for edits: a
578
+ // forbidden line is denied before it is written (MultiEdit included).
579
+ ['PreToolUse', { matcher: 'Bash|PowerShell|Edit|Write|MultiEdit', hooks: [{ type: 'command', command: brainCmd('--guard'), timeout: 10 }] }],
576
580
  // Session end (1.105.0): a closed or cut-off Claude Code session leaves
577
581
  // the lane at once. Before this, only the 10-minute TTL removed it, so the
578
582
  // next session read a dead peer as "live" and refused to continue its
@@ -19,7 +19,7 @@ const PKG_VERSION = (() => {
19
19
  }
20
20
  })();
21
21
 
22
- const DIRECT = new Set(['install', 'link', 'doctor', 'runtime', 'conformance', 'garden-code', 'init', 'git-driver', 'git-hook', 'brain-history', 'brain-deleted', 'orphans', 'diff', 'pr-brief', 'uninstall', 'bench', 'sessions', 'rescue']);
22
+ const DIRECT = new Set(['install', 'link', 'doctor', 'runtime', 'conformance', 'garden-code', 'init', 'git-driver', 'git-hook', 'brain-history', 'brain-deleted', 'orphans', 'diff', 'pr-brief', 'uninstall', 'bench', 'sessions', 'rescue', 'check', 'rules']);
23
23
 
24
24
  const USAGE = [
25
25
  `klypix-mcp ${PKG_VERSION} — shared project brain + MCP coordination server.`,
@@ -36,6 +36,11 @@ const USAGE = [
36
36
  ' bench [--quick] [--json] [--out F] reproducible benchmark: concurrent-write safety, latency, soak, crash',
37
37
  ' init seed a starter ./brain.klypix + print an MCP config',
38
38
  ' garden-code [brain] print the human approval code for brain_garden',
39
+ ' check [--staged|--base B|--range A..B] [--strict] [--json]',
40
+ ' run this project\'s rules (.klypix/rules.json) on a diff — milliseconds, no install/tests',
41
+ ' check --install-hook|--remove-hook opt-in git pre-commit/pre-push/post-commit hooks for those rules',
42
+ ' rules [list|code promote|retire <ids>|promote|retire <ids> --code C|allow <id> --reason R|false-alarm <id> --reason R]',
43
+ ' the human side of rules: approval codes, one-hour overrides, false alarms',
39
44
  ' uninstall [--check|--yes|unlink] remove this install from the machine (--check inventories first; never deletes a .klypix)',
40
45
  ' git-driver [install|status] [repo] register the card-level .klypix merge driver for a repo (zero-command teams)',
41
46
  ' git-hook [install|remove|status] wire the agent-neutral commit-capture hook (any agent/branch/worktree → brain cards)',
@@ -78,6 +83,9 @@ if (verb === 'bench') {
78
83
  console.log(process.argv.includes('--json') ? JSON.stringify(report, null, 2) : markdown);
79
84
  const c = report.scenarios?.concurrentWriters;
80
85
  process.exit(c?.verdict === 'no-loss' ? 0 : 1);
86
+ } else if (verb === 'check' || verb === 'rules') {
87
+ const { runRulesCli } = await import('../src/brain-rules.mjs');
88
+ process.exit(await runRulesCli(verb === 'rules' ? process.argv.slice(2) : process.argv.slice(3)));
81
89
  } else if (verb === 'runtime') {
82
90
  process.argv.splice(2, 1);
83
91
  await import('./klypix-runtime.mjs');
@@ -228,7 +228,7 @@ if (process.argv[2] === 'init') {
228
228
  { title: 'Goal', cards: [{ text: '❓ What is this project for, and for whom?\nAgent: survey the repo on your first session and replace this with the real goal.' }] },
229
229
  { title: 'Architecture', cards: [{ text: '❓ Key components and how they fit.\nAgent: record the actual shape from the repo — only what a new session must know.' }] },
230
230
  { title: 'Decisions', cards: [{ text: 'Decisions land here automatically: agents emit `🧠 BRAIN [Area]: …` markers; a new decision that replaces an old one archives it (superseded). Resolve finished items with `✓`, correct in place with `~`. Drag any card into 📌 Focus to make it lead every session brief.' }] },
231
- { title: '🛠️ Skills', cards: [{ text: '🛠️ Reusable how-tos, gotchas & conventions land here — emit `🧠 BRAIN [Area] +: <skill>` (or just state a rule like "always X / never Y" and it auto-promotes). Skills resurface every session and never age out, unlike one-time decisions. This is "how we work here", inherited by every future agent.' }] },
231
+ { title: '🛠️ Skills', cards: [{ text: '🛠️ Reusable how-tos, gotchas & conventions land here — emit `🧠 BRAIN [Area] +: <skill>` (or just state a rule like "always X / never Y" and it auto-promotes). Skills are kept until corrected and shown first to sessions that touch the files they name, unlike one-time decisions. This is "how we work here", inherited by every future agent.' }] },
232
232
  { title: 'Pending / next', cards: [{ text: 'What is in flight and what comes next. Close finished items with the ✓ marker.' }] },
233
233
  { title: 'Open questions', cards: [{ text: 'Unresolved questions (the ❓ marker) live here — the session brief surfaces them first.' }] },
234
234
  { title: '📌 Focus', cards: [{ text: 'Drag any card into this area to make it lead every session brief — steer your agent by moving cards.' }] },
@@ -812,7 +812,7 @@ try {
812
812
 
813
813
  server.registerTool('brain_note', {
814
814
  title: 'Write a deliberate note to the project brain (decision / question / milestone / skill / resolve / update)',
815
- description: 'Record something in the project brain ON DEMAND — the agent-neutral twin of the Claude-Code capture hook, so any client (Cursor / Cline / Desktop) can write the brain, not just read it. Unlike add_to_canvas (a flat append), this routes through the brain\'s capture engine, so a new decision SUPERSEDES a heavily-overlapping older one, ✓ RESOLVES/archives a matching card, closes: resolves the strategy/question a milestone fulfils, and ~ UPDATES a card in place — the full decision lifecycle, with dedup. Use marker "+" to record a 🛠️ SKILL — a reusable how-to/gotcha/convention ("always dedup zKeys before REORDER") that should resurface every session and never age out, distinct from a one-time decision. Use it to remember a decision, ask an open question, mark a milestone, log a skill, resolve a finished item, or correct a card. Defaults to the project brain ("brain").',
815
+ description: 'Record something in the project brain ON DEMAND — the agent-neutral twin of the Claude-Code capture hook, so any client (Cursor / Cline / Desktop) can write the brain, not just read it. Unlike add_to_canvas (a flat append), this routes through the brain\'s capture engine, so a new decision SUPERSEDES a heavily-overlapping older one, ✓ RESOLVES/archives a matching card, closes: resolves the strategy/question a milestone fulfils, and ~ UPDATES a card in place — the full decision lifecycle, with dedup. Use marker "+" to record a 🛠️ SKILL — a reusable how-to/gotcha/convention ("always dedup zKeys before REORDER") kept until corrected and shown first to sessions that touch the files it names, distinct from a one-time decision. When the mistake is mechanical, end the text with a `rule:` suffix to make the lesson RULE-READY: repo globs, then `never "<text>"`, `never /<regex>/`, `must "<text>"`, `listed-in <file>[,<file>]`, `keep-eol` or `costly` — e.g. `rule: web/tests/**/*.test.ts never "from \'@/"`. The globs must name tracked files; it lands as a visible "⚙ rule candidate" line that nothing checks until brain_rules adds it and a person approves it. Use it to remember a decision, ask an open question, mark a milestone, log a skill, resolve a finished item, or correct a card. Defaults to the project brain ("brain").',
816
816
  inputSchema: {
817
817
  text: z.string().describe('The note — one concise idea; the first line becomes the card title.'),
818
818
  marker: z.enum(['', '?', '!', '+', '✓', '~']).optional().describe('(none)=decision · ?=open question · !=milestone · +=🛠️ skill (reusable how-to/gotcha; always resurfaces, never ages out) · ✓=resolve+archive the best-matching card · ~=update the matching card in place. Default: decision.'),
@@ -1540,6 +1540,38 @@ server.registerTool('brain_sync', {
1540
1540
  };
1541
1541
  });
1542
1542
 
1543
+ server.registerTool('brain_rules', {
1544
+ title: 'Project rules from the brain\'s lessons — enforced before CI, with no AI at check time',
1545
+ description: 'Turns lessons this brain recorded ("every new route must also be in PRIVATE_ROUTES", "a new *.test.ts must be listed in package.json", "this file is mixed CRLF/LF") into DETERMINISTIC diff checks, versioned in the repo as .klypix/rules.json, each citing the card it came from. Checked before the code reaches CI: brain_sync shows the rules for the files you declare; the edit hook (Claude Code; Codex gets the same) and an opt-in git pre-commit/pre-push hook report findings. Rules WARN by default; only a rule the human approved with --block stops an edit (Claude Code) or a commit, and a commit that skips a blocking rule (--no-verify) is recorded. Actions: "propose" routes each lesson to rule / instruction / note and drafts the checkable ones; "add" writes completed drafts as CANDIDATES (inert); "promote" and "retire" need the human\'s 8-character approval code (`npx klypix-mcp rules code promote|retire <ids>` — never shown to you, never guess it; a rule that blocks you is lifted only by the human); "check" runs the rules on the working tree, the staged change, or a range (include_candidates:true tests candidates before approval); "status" lists rules with fires / blocks / overrides / false alarms and retirement suggestions; "false_alarm" records that a finding was wrong (it does not lift the rule). Kinds: listed-in, forbid-pattern, preserve-eol, cloud-required. Checks are string/glob/regex work over the diff — milliseconds, offline; the project\'s own tests stay with its CI.',
1546
+ inputSchema: {
1547
+ action: z.enum(['propose', 'add', 'check', 'status', 'promote', 'retire', 'false_alarm']).describe('What to do. Default flow: propose → add → (human) promote; check any time; status for counts and suggestions.'),
1548
+ project: z.string().optional().describe('Absolute project root (the folder holding brain.klypix and .klypix/). Defaults to this session\'s project brain.'),
1549
+ query: z.string().optional().describe('propose: only lessons containing this text.'),
1550
+ limit: z.number().int().min(1).max(100).optional().describe('propose: at most this many lessons (default 30).'),
1551
+ rules: z.array(z.record(z.string(), z.any())).max(32).optional().describe('add: complete rules — {id, kind, severity, why, fix, source:{card}, files:[globs], …kind fields}. listed-in: when "added-files"|"added-lines", registry:[files], needle (template: {file} {basename} {name} {relpath} {1}), pattern for added-lines (capture group 1 = the value to find). forbid-pattern: pattern. preserve-eol: files only. cloud-required: files, severity "advise".'),
1552
+ ids: z.array(z.string()).max(32).optional().describe('promote / retire / false_alarm: rule ids.'),
1553
+ approve: z.string().optional().describe('promote / retire: the human\'s 8-character code. Never guess or fabricate it.'),
1554
+ reason: z.string().max(300).optional().describe('retire / false_alarm: why, in one line.'),
1555
+ mode: z.enum(['worktree', 'staged', 'range']).optional().describe('check: the working tree vs HEAD (default), the staged change, or a commit range (with base).'),
1556
+ base: z.string().optional().describe('check: base ref for a range (merge-base with head is used).'),
1557
+ head: z.string().optional().describe('check: head ref for a range (default HEAD).'),
1558
+ files: z.array(z.string()).max(50).optional().describe('check: just list the rules these files fall under (no diff).'),
1559
+ include_candidates: z.boolean().optional().describe('check: also run candidate rules — to see what a rule would catch before the human approves it.'),
1560
+ block: z.boolean().optional().describe('promote: true only when the human printed the code with --block, making these rules stop an edit or a commit instead of warning. Rules warn by default.'),
1561
+ },
1562
+ }, async ({ action, project, query, limit, rules, ids, approve, reason, mode, base, head, files, include_candidates, block }, extra) => {
1563
+ try {
1564
+ const { brainRulesTool } = await import('../src/brain-rules.mjs');
1565
+ const brainPath = project ? path.join(path.resolve(project), 'brain.klypix') : (mcpPresence.brainPath || path.join(mcpPresence.vault, 'brain.klypix'));
1566
+ return toContent(await brainRulesTool({
1567
+ projectRoot: path.dirname(brainPath), brainPath, action, query, limit, rules, ids, approve, reason, mode, base, head, files,
1568
+ includeCandidates: include_candidates === true, block: block === true, pluginMode: PLUGIN_MODE, proposedBy: extra?.klypixClientName || '',
1569
+ }));
1570
+ } catch (error) {
1571
+ return toContent({ blocks: [{ kind: 'text', text: `brain_rules failed: ${error?.message || error}` }], isError: true });
1572
+ }
1573
+ });
1574
+
1543
1575
  server.registerTool('brain_doctor', {
1544
1576
  title: 'Brain doctor — is this brain current, wired, and in sync?',
1545
1577
  description: 'Read-only self-check of the installed klypix brain, as ONE verdict: VERSION (deployed brain-core + optional npm currency), CLAUDE (existing 5-hook capture readiness), CODEX (automatic MCP presence plus optional enhanced-hook status), TOOLS (discoverable MCP verbs), SESSIONS (all active presence-adapter sessions across hosts, never recent-chat history), and HARNESS (projection drift). Use to answer "is my brain current, correctly installed, in sync, and who is actually live?" without file-spelunking. Never writes: the only side effects are read-only subprocess queries (git rev-parse / tag --list / log / merge-base with fixed argument arrays, and `npm view` only when check_npm is true) — it creates, edits, and deletes nothing. SCOPE: only CLAUDE and CODEX get behavioural verdicts. HARNESS classifies the projected config/rules FILES on disk — a project can read fully ok while no other host has ever actually loaded them, so do not report a clean HARNESS as "Cursor/Cline/Windsurf/Copilot is working". The MCP-callable twin of `npx klypix-mcp doctor`. Besides the text, the result carries structuredContent {verdict, layers, version, autoUpdate, supervisors, readinessWarnings, actions}: the same verdict as data, including when the next automatic update check runs and what it will do.',
@@ -1808,6 +1840,43 @@ server.server.oninitialized = () => {
1808
1840
  };
1809
1841
  server.server.onclose = stopRuntimePresence;
1810
1842
  process.once('exit', stopRuntimePresence);
1843
+ // Published tool schemas are a compatibility contract: a running supervisor
1844
+ // compares them byte for byte before it hot-swaps a worker, and the frozen
1845
+ // snapshots in test/fixtures pin them. zod 4.6 writes a union of plain types as
1846
+ // `type: ["number","string"]` where every snapshot and every supervisor already
1847
+ // running knows `anyOf: [{type:"number"},{type:"string"}]` — the same meaning in
1848
+ // other bytes. Rewrite each type list back into anyOf, FIRST in its object as
1849
+ // zod 4.4 wrote it (before description), so a zod bump never drifts a frozen
1850
+ // schema.
1851
+ const isTypeList = (value) => Array.isArray(value) && value.length > 1 && value.every((t) => typeof t === 'string');
1852
+ function frozenSchemaShape(node) {
1853
+ if (Array.isArray(node)) return node.map(frozenSchemaShape);
1854
+ if (!node || typeof node !== 'object') return node;
1855
+ const out = {};
1856
+ if (isTypeList(node.type)) out.anyOf = node.type.map((type) => ({ type }));
1857
+ for (const [key, value] of Object.entries(node)) {
1858
+ if (key === 'type' && isTypeList(value)) continue;
1859
+ out[key] = frozenSchemaShape(value);
1860
+ }
1861
+ return out;
1862
+ }
1863
+ const sdkListTools = server.server._requestHandlers?.get?.('tools/list');
1864
+ if (typeof sdkListTools === 'function') {
1865
+ server.server._requestHandlers.set('tools/list', async (request, extra) => {
1866
+ const result = await sdkListTools(request, extra);
1867
+ if (!result || !Array.isArray(result.tools)) return result;
1868
+ return {
1869
+ ...result,
1870
+ tools: result.tools.map((tool) => ({
1871
+ ...tool,
1872
+ ...(tool.inputSchema ? { inputSchema: frozenSchemaShape(tool.inputSchema) } : {}),
1873
+ ...(tool.outputSchema ? { outputSchema: frozenSchemaShape(tool.outputSchema) } : {}),
1874
+ })),
1875
+ };
1876
+ });
1877
+ } else {
1878
+ log('tools/list schema shaping unavailable in this SDK; published schemas follow zod as-is');
1879
+ }
1811
1880
  await server.connect(transport);
1812
1881
  // Most sessions use brain_sync/presence and should not pay for a private model
1813
1882
  // they never query. Legacy mode restores the old eager lifecycle immediately.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klypix-mcp",
3
- "version": "1.106.2",
3
+ "version": "1.107.1",
4
4
  "mcpName": "io.github.dahshanlabs/klypix-mcp",
5
5
  "description": "Shared project memory for Claude Code, Codex and Cursor: decisions, corrections, overlap warnings.",
6
6
  "type": "module",
@@ -97,7 +97,7 @@
97
97
  "bench": "node bin/klypix-mcp.mjs bench",
98
98
  "test:bench": "node test/bench.mjs",
99
99
  "pretest": "node test/publish-workflow.mjs",
100
- "test": "node test/publish-verdict.mjs && node test/npx-owned-names.mjs && node test/project-graph.mjs && node test/project-map-cli.mjs && node test/mcp-auto-update.mjs && node test/mcp-supervisor.mjs && node test/runtime-inspector.mjs && node test/codex-hooks.mjs && node test/request-identity.mjs && node test/session-identity-core.mjs && node test/agent-presence.mjs && node test/message-delivery-v3.mjs && node test/claude-message-delivery-v3.mjs && node test/session-mailbox.mjs && node test/session-reopen.mjs && node test/session-handoff.mjs && node test/lock-notes.mjs && node test/whose-classify.mjs && node test/whose-ledger.mjs && node test/whose-rescue.mjs && node test/whose-hook.mjs && node test/live-projection.mjs && node test/lane-write-retry.mjs && node test/result-reconcile.mjs && node test/evidence-publication-gate.mjs && node test/release-evidence-cli.mjs && node test/intent-guard.mjs && node test/git-capture-install.mjs && node test/brain-history.mjs && node test/brain-graveyard.mjs && node test/archived-visibility.mjs && node test/finding-routing.mjs && node test/finding-routing-hook.mjs && node test/presence-relay.mjs && node test/network-bridge.mjs && node test/install-version.mjs && node test/install-rename-backoff.mjs && node test/project-binding-rebind.mjs && node test/context-gateway.mjs && node test/repo-state.mjs && node test/released-tag-guard.mjs && node test/conformance.mjs && node test/brain-doctor.mjs && node test/version-currency.mjs && node test/ship-capture.mjs && node test/capture-gap.mjs && node test/lane-message.mjs && node test/brain-quality.mjs && node test/brain-connect-orphans.mjs && node test/orphan-gardener.mjs && node test/brief-and-recall.mjs && node test/guard-cards.mjs && node test/layout-cluster.mjs && node test/brain-ask.mjs && node test/brain-ask-words.mjs && node test/retrieval-fusion.mjs && node test/eval-retrieval.mjs && node test/field-report-2026-07-04.mjs && node test/autoprop.mjs && node test/overlay-recency-2026-07-12.mjs && node test/brain-challenge.mjs && node test/brain-lens.mjs && node test/brain-kind.mjs && node test/rule-drafts.mjs && node test/claim-engine.mjs && node test/partial-notes.mjs && node test/lifecycle-prefix.mjs && node test/close-link-safety.mjs && node test/resolve-ledger.mjs && node test/plan-fulfillment.mjs && node test/arrange-receipts.mjs && node test/skill-staleness.mjs && node test/canvas-view.mjs && node test/status-completeness.mjs && node test/semantic-security.mjs && node test/semantic-gate.mjs && node test/memory-runtime.mjs && node test/semantic-cache.mjs && node test/semantic-preparation.mjs && node test/semantic-hash-parity.mjs && node test/enrichment.mjs && node test/provenance.mjs && node test/confirm-trail.mjs && node test/hook-fallback.mjs && node test/eval-hook-lane.mjs && node test/hook-unified-lane.mjs && node test/decay-status.mjs && node test/decay-hook.mjs && node test/marker-suffix-grammar.mjs && node test/marker-after-colon.mjs && node test/evidence-anchors.mjs && node test/brain-evidence.mjs && node test/presence-visibility.mjs && node test/undeclared-active.mjs && node test/presence-liveness.mjs && node test/observed-scope.mjs && node test/overlap-tiers.mjs && node test/coordination-bench.mjs && node test/release-lease.mjs && node test/release-reconcile.mjs && node test/release-ancestry.mjs && node test/release-claim-join.mjs && node test/release-claims.mjs && node test/release-handshake.mjs && node test/completion-guard.mjs && node test/results-not-manifests.mjs && node test/merge-brains.mjs && node test/merge-readings.mjs && node test/revival-map.mjs && node test/merge-scale.mjs && node test/concurrent-writes.mjs && node test/lock-interop.mjs && node test/capture-write-failure.mjs && node test/a2a-smoke.mjs && node test/one-command-setup.mjs && node test/cli-args.mjs && node test/format-guard.mjs && node test/canvas-groups.mjs && node test/git-tools.mjs && node test/link-compat.mjs && node test/uninstall.mjs && node test/current-guidance.mjs && node test/current-first.mjs && node test/status-shape.mjs && node test/status-hook.mjs && node test/read-canvas-saved-readings.mjs && node test/document-readings.mjs && node test/open-lease.mjs && node test/agent-card-style.mjs && node test/tool-schema-freeze.mjs && node test/app-lease-vectors.mjs && node test/app-tools-stdio.mjs && node test/card-contents-files.mjs && node test/codex-app-table.mjs && node test/plugin-mode.mjs && node test/app-bridge-vectors.mjs && node test/app-bridge-client.mjs && node test/app-tools-static.mjs && node test/app-tools-stdio-live.mjs",
100
+ "test": "node test/publish-verdict.mjs && node test/npx-owned-names.mjs && node test/project-graph.mjs && node test/project-map-cli.mjs && node test/mcp-auto-update.mjs && node test/mcp-supervisor.mjs && node test/runtime-inspector.mjs && node test/codex-hooks.mjs && node test/request-identity.mjs && node test/session-identity-core.mjs && node test/agent-presence.mjs && node test/message-delivery-v3.mjs && node test/claude-message-delivery-v3.mjs && node test/session-mailbox.mjs && node test/session-reopen.mjs && node test/session-handoff.mjs && node test/lock-notes.mjs && node test/whose-classify.mjs && node test/whose-ledger.mjs && node test/whose-rescue.mjs && node test/whose-hook.mjs && node test/live-projection.mjs && node test/lane-write-retry.mjs && node test/result-reconcile.mjs && node test/evidence-publication-gate.mjs && node test/release-evidence-cli.mjs && node test/intent-guard.mjs && node test/git-capture-install.mjs && node test/brain-history.mjs && node test/brain-graveyard.mjs && node test/archived-visibility.mjs && node test/finding-routing.mjs && node test/finding-routing-hook.mjs && node test/presence-relay.mjs && node test/network-bridge.mjs && node test/brain-rules.mjs && node test/brain-recall.mjs && node test/install-version.mjs && node test/install-rename-backoff.mjs && node test/project-binding-rebind.mjs && node test/context-gateway.mjs && node test/repo-state.mjs && node test/released-tag-guard.mjs && node test/conformance.mjs && node test/brain-doctor.mjs && node test/version-currency.mjs && node test/ship-capture.mjs && node test/capture-gap.mjs && node test/lane-message.mjs && node test/brain-quality.mjs && node test/brain-connect-orphans.mjs && node test/orphan-gardener.mjs && node test/brief-and-recall.mjs && node test/guard-cards.mjs && node test/layout-cluster.mjs && node test/brain-ask.mjs && node test/brain-ask-words.mjs && node test/retrieval-fusion.mjs && node test/eval-retrieval.mjs && node test/field-report-2026-07-04.mjs && node test/autoprop.mjs && node test/overlay-recency-2026-07-12.mjs && node test/brain-challenge.mjs && node test/brain-lens.mjs && node test/brain-kind.mjs && node test/rule-drafts.mjs && node test/claim-engine.mjs && node test/partial-notes.mjs && node test/lifecycle-prefix.mjs && node test/close-link-safety.mjs && node test/resolve-ledger.mjs && node test/plan-fulfillment.mjs && node test/arrange-receipts.mjs && node test/skill-staleness.mjs && node test/canvas-view.mjs && node test/status-completeness.mjs && node test/semantic-security.mjs && node test/semantic-gate.mjs && node test/memory-runtime.mjs && node test/semantic-cache.mjs && node test/semantic-preparation.mjs && node test/semantic-hash-parity.mjs && node test/enrichment.mjs && node test/provenance.mjs && node test/confirm-trail.mjs && node test/hook-fallback.mjs && node test/eval-hook-lane.mjs && node test/hook-unified-lane.mjs && node test/decay-status.mjs && node test/decay-hook.mjs && node test/marker-suffix-grammar.mjs && node test/marker-after-colon.mjs && node test/evidence-anchors.mjs && node test/brain-evidence.mjs && node test/presence-visibility.mjs && node test/undeclared-active.mjs && node test/presence-liveness.mjs && node test/observed-scope.mjs && node test/overlap-tiers.mjs && node test/coordination-bench.mjs && node test/release-lease.mjs && node test/release-reconcile.mjs && node test/release-ancestry.mjs && node test/release-claim-join.mjs && node test/release-claims.mjs && node test/release-handshake.mjs && node test/completion-guard.mjs && node test/results-not-manifests.mjs && node test/merge-brains.mjs && node test/merge-readings.mjs && node test/revival-map.mjs && node test/merge-scale.mjs && node test/concurrent-writes.mjs && node test/lock-interop.mjs && node test/capture-write-failure.mjs && node test/a2a-smoke.mjs && node test/one-command-setup.mjs && node test/cli-args.mjs && node test/format-guard.mjs && node test/canvas-groups.mjs && node test/git-tools.mjs && node test/link-compat.mjs && node test/uninstall.mjs && node test/current-guidance.mjs && node test/current-first.mjs && node test/status-shape.mjs && node test/status-hook.mjs && node test/read-canvas-saved-readings.mjs && node test/document-readings.mjs && node test/open-lease.mjs && node test/agent-card-style.mjs && node test/tool-schema-freeze.mjs && node test/app-lease-vectors.mjs && node test/app-tools-stdio.mjs && node test/card-contents-files.mjs && node test/codex-app-table.mjs && node test/plugin-mode.mjs && node test/app-bridge-vectors.mjs && node test/app-bridge-client.mjs && node test/app-tools-static.mjs && node test/app-tools-stdio-live.mjs",
101
101
  "test:memory": "node test/memory-runtime.mjs",
102
102
  "test:memory:soak": "node --expose-gc test/memory-soak.mjs",
103
103
  "runtime": "node bin/klypix-runtime.mjs",
@@ -111,7 +111,7 @@
111
111
  "fractional-indexing": "^3.2.0",
112
112
  "jpeg-js": "^0.4.4",
113
113
  "jszip": "^3.10.1",
114
- "zod": "^4.3.6"
114
+ "zod": "^4.6.7"
115
115
  },
116
116
  "devDependencies": {
117
117
  "@huggingface/transformers": "^4.2.0"