klypix-mcp 1.69.0 → 1.71.0

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
@@ -85,28 +85,55 @@ It verifies 15 required coordination behaviours — not the 26 tools, and not th
85
85
 
86
86
  ## Quick start
87
87
 
88
- **Claude Code + Codex:**
88
+ Run this **inside your project**:
89
89
 
90
90
  ```bash
91
91
  npx klypix-mcp install
92
92
  ```
93
93
 
94
- This copies the engine and a local MCP runtime into `~/.claude/project-brain`, wires Claude Code's
95
- four lifecycle hooks, writes Codex's global `~/.codex/AGENTS.md` guidance block, and wires Codex's
96
- MCP connection **for the project you run it in**.
94
+ One command, every editor. It finds the project root (walking up, so running it from `src/` is
95
+ fine), gives the project a brain if it doesn't have one, wires the agent tools you actually have
96
+ installed, registers the lossless `.klypix` merge driver if it's a git repo, and then **proves the
97
+ result** before it exits:
98
+
99
+ ```text
100
+ project E:\work\api (git repository root)
101
+ brain created brain.klypix — a starter brain, ready for its first decision
102
+ editors Claude Code · Cursor · Codex · Gemini CLI · Antigravity · VS Code
103
+ wired 9 file(s) · 9 updated (skipped 5 for tools you don't have)
104
+ git lossless .klypix merge driver registered
105
+ verified ✓ 26 tools reachable via .mcp.json (892ms)
106
+ ```
107
+
108
+ That last line is the point. MCP config fails **silently** — a wrong entry means the server never
109
+ starts, the agent quietly loses every brain verb, and nothing reports an error. So `install` opens
110
+ a real stdio handshake against the config it just wrote and counts the tools that answered. A
111
+ broken entry dies in ~100ms with `Connection closed` and is reported, not shipped.
112
+
113
+ What goes where:
114
+
115
+ - **Machine-global, once** — the engine + runtime in `~/.claude/project-brain`, Claude Code's four
116
+ lifecycle hooks in `~/.claude/settings.json`, and the `~/.codex/AGENTS.md` guidance block. Claude
117
+ Code is therefore covered in every project on that machine that has a `./brain.klypix`.
118
+ - **Per project** — MCP config and rules for Cursor, Codex, Cline, Windsurf, Copilot, Gemini CLI /
119
+ Antigravity and Aider. Run `install` once inside each project.
97
120
 
98
- Be precise about what "machine-global" covers:
121
+ Three things it deliberately will **not** do:
99
122
 
100
- - **Machine-global** the engine + runtime in `~/.claude/project-brain`, the four Claude Code
101
- hooks in `~/.claude/settings.json`, and the `~/.codex/AGENTS.md` guidance. Claude Code is
102
- therefore covered in every project on that machine that has a `./brain.klypix`.
103
- - **Per project** — Codex's MCP connection. `install` writes it into `<cwd>/.codex/config.toml`,
104
- only when that directory has a `brain.klypix`, and it deliberately **removes** any *global*
105
- `~/.codex/config.toml` KLYPIX entry (a global entry resolves its `--vault` from the wrong
106
- directory and binds the wrong brain). Run `install` — or `link` — once inside each brain project
107
- you want Codex wired to.
123
+ - **Write for editors you don't have.** Config is projected only for hosts detected on this
124
+ machine a two-person team using one editor no longer commits rules for six they never opened.
125
+ A file your project *already* carries stays maintained regardless, so you can't silently stop
126
+ updating your team's committed configs.
127
+ - **Wire a directory that isn't a project.** It refuses your home folder, a drive root, and
128
+ anything with no brain, no git repo and no project manifest. A mistyped command can't seed a
129
+ brain into `C:\Users\you`.
130
+ - **Replace a project-owned server.** A repo-relative launch like
131
+ `node scripts/klypix-mcp-server.mjs` is deliberate — it resolves offline and rides a bundle the
132
+ repo version-gates — so it's left byte-identical and reported. An explicit `link` still rewrites
133
+ everything: an action you didn't ask for stays more conservative than one you did.
108
134
 
109
- It does **not** set up Cursor, Cline, Windsurf, Copilot, Gemini CLI or Aider — those need `link`.
135
+ Opt out with `--no-project` (CI images, scripted provisioning). `--json` emits the report as
136
+ structured data; `--verify-all` handshakes every written config instead of one.
110
137
 
111
138
  Optional, opt-in, and approved inside Codex itself:
112
139
 
@@ -119,15 +146,16 @@ file-overlap warning. Codex owns the trust decision and will ask you to review t
119
146
  `brain_doctor` reports this layer separately as off, execution-unverified, or active. Even with it
120
147
  on, **Codex never captures decisions automatically** — the Codex hook never writes the brain.
121
148
 
122
- **Every other agent tool — one command per project:**
149
+ **Re-project everything explicitly:**
123
150
 
124
151
  ```bash
125
152
  npx klypix-mcp link
126
153
  ```
127
154
 
128
- Writes 14 managed, hash-stamped files: MCP server config for six hosts, plus rules files for
129
- eight. Managed blocks are merged into your existing instruction files and never clobber your
130
- content.
155
+ `install` already does this for the editors you have. Reach for `link` when you want all 14
156
+ managed, hash-stamped files regardless of what's installed MCP server config for six hosts plus
157
+ rules files for eight — or to repair drift. Managed blocks are merged into your existing
158
+ instruction files and never clobber your content.
131
159
 
132
160
  ```bash
133
161
  npx klypix-mcp link --check # audits without writing; exits non-zero on drift
@@ -481,8 +509,8 @@ The MCP verbs below are what agents call. These are what **you** call:
481
509
  | Command | What it does |
482
510
  |---|---|
483
511
  | `npx klypix-mcp init` | Seed a starter `brain.klypix` here and print an MCP config |
484
- | `npx klypix-mcp install` | Install the engine + Claude Code hooks on this machine (see Quick start) |
485
- | `npx klypix-mcp link` | Wire this project for Cursor, Cline, Windsurf, Copilot, Gemini CLI, Aider (`--check` audits) |
512
+ | `npx klypix-mcp install` | Set up everything: machine engine + hooks, then this project — brain, config for the editors you have, merge driver, verified (see Quick start) |
513
+ | `npx klypix-mcp link` | Re-project all 14 managed files regardless of what is installed (`--check` audits) |
486
514
  | `npx klypix-mcp doctor` | One verdict: version, hosts, live sessions, tool count, drift. Exits non-zero — usable as a CI gate |
487
515
  | `npx klypix-mcp runtime` | Passive per-connection process/RAM attribution (`--json`, optional `--watch seconds`); never kills or deduplicates |
488
516
  | `npx klypix-mcp conformance` | Launch two real MCP clients against this build and verify coordination behaviour |
@@ -34,6 +34,7 @@ import {
34
34
  import { brainInstallDecision, deploySourceDecision } from '../src/install-version.mjs';
35
35
  import { acquireInstallLockSync, releaseInstallLockSync } from '../src/install-lock.mjs';
36
36
  import { collectRepoState } from '../src/repo-state.mjs';
37
+ import { runSetup, renderBrief } from '../src/setup.mjs';
37
38
 
38
39
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
39
40
  const PKG_ROOT = path.resolve(__dirname, '..');
@@ -48,6 +49,12 @@ const VERSION = (() => { try { return JSON.parse(fs.readFileSync(path.join(PKG_R
48
49
  const FORCE = process.argv.includes('--force');
49
50
  const CODEX_HOOKS = process.argv.includes('--codex-hooks');
50
51
  const RUNTIME_ONLY = process.argv.includes('--runtime-only');
52
+ // Project wiring is the default because it is the step users did not know
53
+ // existed. These opt OUT for the cases that genuinely want machine-only:
54
+ // CI images, scripted provisioning, and anyone wiring the project by hand.
55
+ const NO_PROJECT = process.argv.includes('--no-project');
56
+ const VERIFY_ALL = process.argv.includes('--verify-all');
57
+ const SETUP_JSON = process.argv.includes('--json');
51
58
  // Released-tag deploy-guard acknowledgement. Deliberately a SEPARATE axis from
52
59
  // --force: --force is destination authority (overwrite what is installed),
53
60
  // this is source authority (knowingly deploy an untagged working tree).
@@ -480,6 +487,24 @@ try {
480
487
  console.log(' Enhanced Codex auto-context + pre-edit overlap guard: re-run with `--codex-hooks`, then approve/review KLYPIX once in a Codex surface that supports hook trust.');
481
488
  }
482
489
  console.log(' Compatible brain-core updates hot-swap behind the same MCP connection. Only the one-time legacy→supervisor migration, a supervisor change, or an intentionally breaking tool/protocol change needs reconnect.');
490
+
491
+ // 9) PROJECT setup (1.71.0) — the step users never knew they had to take.
492
+ // `install` wired the machine; without this it wired nothing a Cursor,
493
+ // Antigravity, Codex, Cline or Copilot user could see, and the failure
494
+ // was silent. A runtime-only refresh deliberately skips it: that path
495
+ // exists to preserve every host/project config byte.
496
+ if (!RUNTIME_ONLY && !NO_PROJECT) {
497
+ try {
498
+ const report = await runSetup({ verifyAll: VERIFY_ALL });
499
+ if (SETUP_JSON) console.log(JSON.stringify(report, null, 2));
500
+ else console.log(renderBrief(report));
501
+ } catch (e) {
502
+ // The machine install already succeeded and is independently
503
+ // useful; a project-wiring failure must report itself, not undo it.
504
+ console.error(`⚠ project setup could not finish: ${e?.message || e}`);
505
+ console.error(' The machine install is intact — re-run inside your project, or use `npx klypix-mcp link`.');
506
+ }
507
+ }
483
508
  console.log(' Verify anytime: `npx klypix-mcp doctor`; prove two-client behavior with `npx klypix-mcp conformance`.');
484
509
  } catch (e) {
485
510
  releaseInstallLockSync(installLock);
@@ -19,13 +19,14 @@ 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', 'diff', 'pr-brief', 'uninstall', 'bench']);
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']);
23
23
 
24
24
  const USAGE = [
25
25
  `klypix-mcp ${PKG_VERSION} — shared project brain + MCP coordination server.`,
26
26
  '',
27
27
  'Verbs:',
28
- ' install [--force] [--codex-hooks] install/update this machine\'s brain engine + Claude Code hooks',
28
+ ' install [--force] [--codex-hooks] set up everything: machine engine + Claude hooks, then THIS project —',
29
+ ' [--no-project] [--json] brain, config for the editors you actually have, merge driver, verified',
29
30
  ' link [dir] [--check] project this project\'s 14 managed agent config files (--check audits, writes nothing, exits 1 on drift)',
30
31
  ' doctor [--npm] [--all] [--json] read-only self-check; exits 1 on drift',
31
32
  ' runtime [--json] [--watch seconds] passive MCP process/RAM attribution; never terminates a process',
@@ -38,6 +39,7 @@ const USAGE = [
38
39
  ' git-hook [install|remove|status] wire the agent-neutral commit-capture hook (any agent/branch/worktree → brain cards)',
39
40
  ' brain-history [list|restore <id>] restore points for this brain — undo an accidental delete, edit, or overwrite',
40
41
  ' brain-deleted [list|restore|purge] recycle bin for this brain — cards you deleted, kept recoverable',
42
+ ' orphans [brain] [--apply|--areas] orphan gardener backfill: report unconnected cards, link the confident subset (dry-run default)',
41
43
  ' diff [ref] [--brain <path>] readable brain diff vs a git ref (default HEAD) — markdown to stdout',
42
44
  ' pr-brief [baseRef] [--brain <path>] brain decisions touching the files changed since baseRef — PR-comment markdown',
43
45
  '',
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ // klypix-orphans — `npx klypix-mcp orphans`. The orphan gardener's backfill pass:
3
+ // report how many live decision/milestone cards have NO connections, and link the
4
+ // CONFIDENT subset in one pass using the same selector capture uses
5
+ // (proposeOrphanAnchorLinks): at most ONE lexical-anchor edge per card — exact
6
+ // [[wikilink]], exact file-slug tag, or an unambiguous ≥0.6 title overlap —
7
+ // never a fan-out, never a guess among multiple candidates.
8
+ //
9
+ // npx klypix-mcp orphans [brain] # DRY RUN (default) — report, write nothing
10
+ // npx klypix-mcp orphans --apply # draw the confident edges (restore point first)
11
+ // npx klypix-mcp orphans --areas # ALSO include each orphan's area-container edge
12
+ // npx klypix-mcp orphans --json # machine-readable report
13
+ //
14
+ // Contract: additive + lossless — no card is archived, rewritten, or moved; every
15
+ // arrow remains individually removable in KLYPIX. `--apply` takes a FORCED restore
16
+ // point via the brain-history machinery before writing, so the whole pass is one
17
+ // `brain-history restore` away from undone. Dry-run is the default because a
18
+ // backfill touches history, not just this session's capture.
19
+ //
20
+ // `--areas` is opt-in for backfill (unlike capture, where the area edge is part
21
+ // of the no-new-orphans contract): mass-adding hundreds of containment edges to
22
+ // an old brain is a layout decision the human should make deliberately.
23
+ //
24
+ // Synchronous top-level by design: the dispatcher does `await import(this);
25
+ // process.exit(0)`, so all work must complete during module evaluation. ARGV is
26
+ // the standalone shape — the dispatcher splices its verb out first (runVerb).
27
+ import fs from 'fs';
28
+ import path from 'path';
29
+ import { parseKlypix, brainInsights, proposeOrphanAnchorLinks, addBrainConnections, atomicWrite } from '../src/klypix-format.mjs';
30
+ import { snapshotBrain } from '../src/brain-history.mjs';
31
+ import { withAdvisoryWriteLock, brainCaptureLockPath } from '../src/brain-write-lock.mjs';
32
+
33
+ const isDir = (p) => { try { return fs.statSync(p).isDirectory(); } catch { return false; } };
34
+
35
+ try {
36
+ const raw = process.argv.slice(2);
37
+ const argv = (raw[0] === 'orphans' && !isDir(path.resolve(raw[0]))) ? raw.slice(1) : raw;
38
+ const apply = argv.includes('--apply');
39
+ const withAreas = argv.includes('--areas');
40
+ const json = argv.includes('--json');
41
+ const pathArg = argv.find(a => !a.startsWith('-'));
42
+ const file = path.resolve(process.cwd(), pathArg || 'brain.klypix');
43
+ if (!fs.existsSync(file)) {
44
+ console.error(`No brain at ${file} — run from the project folder (or pass a path).`);
45
+ process.exit(1);
46
+ }
47
+
48
+ const { struct } = await parseKlypix(fs.readFileSync(file));
49
+ const before = brainInsights(struct).orphans;
50
+ const byId = new Map(struct.cards.map(c => [c.id, c]));
51
+ const flat = (id) => {
52
+ const c = byId.get(id);
53
+ if (!c) return id;
54
+ const s = c.type === 'container' ? `[area] ${c.title || ''}` : String(c.text || '').replace(/\s+/g, ' ').trim();
55
+ return s.slice(0, 70);
56
+ };
57
+
58
+ const proposals = proposeOrphanAnchorLinks(struct);
59
+ const anchored = proposals.filter(p => p.anchor);
60
+ // The confident subset: anchor edges always; area edges only with --areas.
61
+ // Reversed twins (A anchors B, B anchors A) collapse to ONE edge here so the
62
+ // dry-run list, the drawn count, and the apply receipt all agree exactly.
63
+ const seenPair = new Set();
64
+ const edges = [];
65
+ for (const p of proposals) {
66
+ if (p.anchor) {
67
+ const key = [p.cardId, p.anchor.toId].sort().join('|');
68
+ if (!seenPair.has(key)) {
69
+ seenPair.add(key);
70
+ edges.push({ fromId: p.cardId, toId: p.anchor.toId, relationship: 'relates_to', label: 'auto', why: p.anchor.why });
71
+ }
72
+ }
73
+ if (withAreas && p.areaId) edges.push({ fromId: p.cardId, toId: p.areaId, relationship: 'relates_to', label: 'in area', color: 'rgba(120,120,135,0.45)', width: 1, style: 'dashed', why: 'area container' });
74
+ }
75
+ // An anchor edge between two orphans repairs BOTH endpoints — project on the
76
+ // orphan set, not just the source card.
77
+ const orphanIdSet = new Set(before.map(o => o.id));
78
+ const repairable = new Set();
79
+ for (const e of edges) {
80
+ if (orphanIdSet.has(e.fromId)) repairable.add(e.fromId);
81
+ if (orphanIdSet.has(e.toId)) repairable.add(e.toId);
82
+ }
83
+ const projected = Math.max(0, before.length - repairable.size);
84
+
85
+ if (json && !apply) {
86
+ console.log(JSON.stringify({
87
+ brain: file, dryRun: true, orphans: before.length, confident: anchored.length,
88
+ projectedOrphans: projected,
89
+ edges: edges.map(e => ({ fromId: e.fromId, toId: e.toId, label: e.label, why: e.why })),
90
+ }, null, 2));
91
+ process.exit(0);
92
+ }
93
+
94
+ if (!apply) {
95
+ console.log(`# orphan gardener — ${path.basename(file)} (DRY RUN, nothing written)`);
96
+ console.log('');
97
+ console.log(`Orphans now: ${before.length} live decision/milestone card(s) with no connections.`);
98
+ console.log(`Confident subset: ${anchored.length} card(s) have exactly one unambiguous lexical anchor${withAreas ? `; --areas adds ${proposals.filter(p => p.areaId).length} area-container edge(s)` : ''}.`);
99
+ console.log(`Projected after --apply: ${projected} orphan(s) remain (the rest need brain_connect review or a human).`);
100
+ if (edges.length) {
101
+ console.log('');
102
+ for (const e of edges) console.log(` - ${flat(e.fromId)} → ${flat(e.toId)} (${e.why})`);
103
+ console.log('');
104
+ console.log('Re-run with --apply to draw these. Additive only: no card is archived or rewritten;');
105
+ console.log('a forced restore point is taken first (undo: npx klypix-mcp brain-history).');
106
+ } else {
107
+ console.log('');
108
+ console.log(before.length
109
+ ? 'No edge clears the confidence bar — review the rest with brain_connect (dry-run receipts).'
110
+ : 'Nothing to do — this brain has no orphaned decision/milestone cards.');
111
+ }
112
+ process.exit(0);
113
+ }
114
+
115
+ if (!edges.length) {
116
+ console.log(before.length
117
+ ? `No edge clears the confidence bar — ${before.length} orphan(s) remain for brain_connect review. Nothing written.`
118
+ : 'Nothing to do — this brain has no orphaned decision/milestone cards. Nothing written.');
119
+ process.exit(0);
120
+ }
121
+
122
+ // APPLY — one locked snapshot + read-modify-write. The restore point is taken
123
+ // INSIDE the advisory lock (forced: an additive pass never trips the shrink
124
+ // rule, and the throttle must not skip the one snapshot that makes a whole
125
+ // backfill reversible): taken outside it, a concurrent capture landing between
126
+ // snapshot and write would be silently rolled back by a later restore.
127
+ let snap = null;
128
+ const result = await withAdvisoryWriteLock(brainCaptureLockPath(file), async (locked) => {
129
+ if (!locked) return { ok: false, error: 'brain is busy (another writer holds the capture lock) — retry in a moment; nothing written' };
130
+ snap = snapshotBrain(file, { reason: 'orphan-backfill', force: true });
131
+ const { buffer, added } = await addBrainConnections(fs.readFileSync(file), edges);
132
+ if (!added) return { ok: true, added: 0, after: before.length };
133
+ await atomicWrite(file, buffer, { snapshot: false }); // the forced pre-apply snapshot above is the restore point
134
+ let after = projected;
135
+ try { after = brainInsights((await parseKlypix(buffer)).struct).orphans.length; } catch { /* projected stays the honest fallback */ }
136
+ return { ok: true, added, after };
137
+ });
138
+ if (!result.ok) { console.error(`✗ ${result.error}`); process.exit(1); }
139
+
140
+ // A drawn count below the planned count means the brain changed between the
141
+ // dry-run read and the locked write (a pair already present, an endpoint
142
+ // gone) — slicing the planned list by count would then name the WRONG edges,
143
+ // so the per-edge list is only printed when it is exact.
144
+ const exact = result.added === edges.length;
145
+ // An 'unchanged' skip means the newest restore point already holds these
146
+ // exact bytes — the pass is just as reversible as a fresh snapshot.
147
+ const covered = snap && (snap.saved || snap.skipped === 'unchanged');
148
+ if (json) {
149
+ console.log(JSON.stringify({
150
+ brain: file, dryRun: false, added: result.added,
151
+ orphansBefore: before.length, orphansAfter: result.after,
152
+ restorePoint: covered ? (snap.id || true) : null,
153
+ edges: exact ? edges.map(e => ({ fromId: e.fromId, toId: e.toId, label: e.label, why: e.why })) : null,
154
+ ...(exact ? {} : { edgesNote: `${edges.length - result.added} planned edge(s) were skipped (already present or endpoint missing) — re-run the dry run for the current pair list` }),
155
+ }, null, 2));
156
+ process.exit(0);
157
+ }
158
+ console.log(`✓ Drew ${result.added} additive connection(s). Orphan receipt: ${before.length} → ${result.after} (${Math.max(0, before.length - result.after)} repaired).`);
159
+ console.log(covered
160
+ ? 'Restore point taken first — undo the whole pass: npx klypix-mcp brain-history'
161
+ : 'Restore point: not saved (best-effort) — arrows remain individually removable in KLYPIX.');
162
+ if (exact) for (const e of edges) console.log(` - ${flat(e.fromId)} → ${flat(e.toId)} (${e.why})`);
163
+ else console.log(` (${edges.length - result.added} planned edge(s) were already present or lost an endpoint — re-run the dry run for the current pair list)`);
164
+ process.exit(0);
165
+ } catch (e) {
166
+ console.error(`✗ orphans failed (brain unchanged): ${e?.message || e}`);
167
+ process.exit(1);
168
+ }
@@ -131,6 +131,11 @@ await runVerb('brain-history', './klypix-brain-history.mjs');
131
131
  // the card's bytes to graveyard/ instead of destroying them; this lists, restores
132
132
  // and (permanently) purges them.
133
133
  await runVerb('brain-deleted', './klypix-brain-deleted.mjs');
134
+ // `npx klypix-mcp orphans` — the orphan gardener's backfill: report how many live
135
+ // cards sit outside the graph, link the CONFIDENT subset (one unambiguous lexical
136
+ // anchor each — never a fan-out). Dry-run by default; --apply takes a forced
137
+ // restore point first, so the whole pass is one brain-history restore from undone.
138
+ await runVerb('orphans', './klypix-orphans.mjs');
134
139
  await runVerb('diff', './klypix-diff.mjs');
135
140
  await runVerb('pr-brief', './klypix-pr-brief.mjs');
136
141
 
@@ -868,8 +873,12 @@ server.registerTool('brain_sync', {
868
873
  // marker before rejecting them. A strict transport schema rejected first,
869
874
  // allowing a later result-less completion to bypass that state entirely.
870
875
  results: z.unknown().optional().describe('On phase complete, 1-8 result manifests for stable claim keys. The in-handler versioned validator rejects malformed, empty, unknown-field, or incomparable evidence and retains task scope.'),
876
+ releaseIntent: z.object({
877
+ version: z.string().max(64).describe('The version this session intends to release (e.g. "1.70.0").'),
878
+ ref: z.string().max(200).describe('The git ref (branch or tag) the release will be cut from.'),
879
+ }).optional().describe('Declare EXCLUSIVE intent to prepare a release of this project. The first declarer takes a ~2h lease (refreshed by checkpoints, freed by phase "complete", by expiry, or when the holder session ends); a second declarer gets a structured hard conflict naming the holder, version, and ref. While any lease is active every peer\'s sync gains a "release in preparation" footer line.'),
871
880
  },
872
- }, async ({ project, intent, files, phase, include_context, results }, extra) => {
881
+ }, async ({ project, intent, files, phase, include_context, results, releaseIntent }, extra) => {
873
882
  const totalStartedAt = Date.now();
874
883
  const report = mcpPresence.sync({
875
884
  project,
@@ -877,6 +886,7 @@ server.registerTool('brain_sync', {
877
886
  files,
878
887
  phase,
879
888
  results,
889
+ releaseIntent,
880
890
  deliverMessages: include_context !== false,
881
891
  actionId: extra?.klypixRequestIdentity?.actionId || '',
882
892
  preflight: extra?.klypixBrainSyncPreflight,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klypix-mcp",
3
- "version": "1.69.0",
3
+ "version": "1.71.0",
4
4
  "description": "Shared project brain and MCP coordination server for multi-agent coding.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -44,7 +44,8 @@
44
44
  "klypix-append": "bin/klypix-append.mjs",
45
45
  "klypix-install": "bin/klypix-install.mjs",
46
46
  "klypix-uninstall": "bin/klypix-uninstall.mjs",
47
- "klypix-project-map": "bin/klypix-project-map.mjs"
47
+ "klypix-project-map": "bin/klypix-project-map.mjs",
48
+ "klypix-orphans": "bin/klypix-orphans.mjs"
48
49
  },
49
50
  "main": "index.mjs",
50
51
  "exports": {
@@ -83,7 +84,7 @@
83
84
  "bench": "node bin/klypix-mcp.mjs bench",
84
85
  "test:bench": "node test/bench.mjs",
85
86
  "pretest": "node test/publish-workflow.mjs",
86
- "test": "node test/publish-verdict.mjs && node test/remote-client.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/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/install-version.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/lane-message.mjs && node test/brain-quality.mjs && node test/brain-connect-orphans.mjs && node test/brief-and-recall.mjs && node test/layout-cluster.mjs && node test/brain-ask.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/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/decay-status.mjs && node test/decay-hook.mjs && node test/evidence-anchors.mjs && node test/presence-visibility.mjs && node test/presence-liveness.mjs && node test/completion-guard.mjs && node test/merge-brains.mjs && node test/concurrent-writes.mjs && node test/lock-interop.mjs && node test/capture-write-failure.mjs && node test/a2a-smoke.mjs && node test/cli-args.mjs && node test/format-guard.mjs && node test/git-tools.mjs && node test/uninstall.mjs",
87
+ "test": "node test/publish-verdict.mjs && node test/remote-client.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/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/install-version.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/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/layout-cluster.mjs && node test/brain-ask.mjs && node test/retrieval-fusion.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/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/decay-status.mjs && node test/decay-hook.mjs && node test/evidence-anchors.mjs && node test/presence-visibility.mjs && node test/undeclared-active.mjs && node test/presence-liveness.mjs && node test/observed-scope.mjs && node test/release-lease.mjs && node test/completion-guard.mjs && node test/merge-brains.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/git-tools.mjs && node test/uninstall.mjs",
87
88
  "test:memory": "node test/memory-runtime.mjs",
88
89
  "test:memory:soak": "node --expose-gc test/memory-soak.mjs",
89
90
  "runtime": "node bin/klypix-runtime.mjs"