klypix-mcp 1.13.0 → 1.13.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.
@@ -58,13 +58,16 @@ const flatten = (code) => code
58
58
  .replace(/from '\.\.\/src\/klypix-core\.mjs'/g, "from './klypix-core.mjs'")
59
59
  .replace(/from '\.\.\/src\/klypix-format\.mjs'/g, "from './klypix-format.mjs'")
60
60
  .replace(/\.\.\/src\/klypix-(core|format)\.mjs/g, './klypix-$1.mjs')
61
+ // brain-doctor + agent-rules (the server's lazy `import('../src/brain-doctor.mjs')`
62
+ // for the brain_doctor tool) → flat sibling refs in the runtime layout.
63
+ .replace(/\.\.\/src\/(brain-doctor|agent-rules)\.mjs/g, './$1.mjs')
61
64
  .replace(/const PKG_VERSION = \(\(\) => \{[\s\S]*?\}\)\(\);/, `const PKG_VERSION = '${VERSION}'; // baked at install (flat layout has no package.json)`);
62
65
 
63
66
  try {
64
67
  fs.mkdirSync(BRAIN_DIR, { recursive: true });
65
68
  // 1) flat engine + hook scripts (their imports are already './…' → verbatim)
66
69
  let n = 0;
67
- for (const f of ['global-brain-hook.mjs', 'brain-semantic.mjs', 'brain-note.mjs', 'brain-git-hook.mjs', 'klypix-format.mjs', 'klypix-core.mjs']) {
70
+ for (const f of ['global-brain-hook.mjs', 'brain-semantic.mjs', 'brain-note.mjs', 'brain-git-hook.mjs', 'klypix-format.mjs', 'klypix-core.mjs', 'agent-rules.mjs', 'brain-doctor.mjs']) {
68
71
  const s = path.join(SRC, f); if (exists(s)) { fs.writeFileSync(path.join(BRAIN_DIR, f), fs.readFileSync(s, 'utf8')); n++; }
69
72
  }
70
73
  // 2) the two servers, flattened to the *-server.mjs names the runtime/config expect
@@ -199,6 +199,30 @@ server.registerTool('brain_note', {
199
199
  return toContent(await opBrainNote({ vault: VAULT, canvas, text, area, marker: marker || '', closes, via }));
200
200
  });
201
201
 
202
+ server.registerTool('brain_doctor', {
203
+ title: 'Brain doctor — is this brain current, wired, and in sync?',
204
+ description: 'Read-only self-check of the installed klypix brain, as ONE verdict: VERSION (the deployed brain-core version + optional npm-latest currency), HOOKS (are all 4 Claude Code hooks wired — liveness vs readiness), TOOLS (the discoverable MCP verb manifest), PEERS (other live sessions on this project\'s brain right now), and HARNESS (per-file projection drift: ok/stale/hand-edited/missing). Use to answer "is my brain current + correctly installed + in sync, and who else is live?" without file-spelunking. Never writes. The MCP-callable twin of `npx klypix-mcp doctor`.',
205
+ inputSchema: {
206
+ project: z.string().optional().describe('Project dir to audit harness + peers for. Defaults to the server\'s working directory.'),
207
+ check_npm: z.boolean().optional().describe('Also fetch npm latest to flag a stale brain (default false — this one does a network `npm view`).'),
208
+ },
209
+ }, async ({ project, check_npm }) => {
210
+ try {
211
+ // Lazy import so a flat runtime missing brain-doctor.mjs can't crash server STARTUP —
212
+ // the tool degrades gracefully (errors only when called) instead of taking the server down.
213
+ const { inspect, render } = await import('../src/brain-doctor.mjs');
214
+ let npmLatest = null;
215
+ if (check_npm) {
216
+ try { const { execSync } = await import('child_process'); npmLatest = execSync('npm view klypix-mcp version', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 8000 }).trim(); }
217
+ catch { npmLatest = '(offline)'; }
218
+ }
219
+ const report = inspect({ projectDir: project ? path.resolve(project) : process.cwd(), npmLatest });
220
+ return { content: [{ type: 'text', text: render(report, { color: false }) }] };
221
+ } catch (e) {
222
+ return { content: [{ type: 'text', text: `brain_doctor unavailable: ${e?.message || e}` }], isError: true };
223
+ }
224
+ });
225
+
202
226
  const transport = new StdioServerTransport();
203
227
  await server.connect(transport);
204
228
  log(`ready · vault=${VAULT}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klypix-mcp",
3
- "version": "1.13.0",
3
+ "version": "1.13.1",
4
4
  "description": "An open, local-first, agent-neutral canvas file your AI reads and writes over MCP — works with Claude, Cursor, Cline, any model.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -152,13 +152,22 @@ function mergeMcpJson(file, wrapKey, withType) {
152
152
  return { action: before === undefined ? 'created' : 'updated' };
153
153
  }
154
154
 
155
- // Check-only classifier for an MCP json: is the klypix-canvas server wired?
155
+ // Check-only classifier for an MCP json: is the klypix-canvas server wired AND does
156
+ // its entry still actually launch klypix-mcp? Presence alone isn't enough — a hand-edit
157
+ // that mangles the command/args (so the server never starts) reads as "wired" but is
158
+ // broken. We DON'T strict-hash the entry: a customized `--vault <path>` is legitimate,
159
+ // so only the invocation (npx/node … klypix-mcp …) must survive; if it doesn't, that's
160
+ // drift the user needs to re-`link`.
156
161
  function classifyMcp(file, wrapKey) {
157
162
  if (!exists(file)) return { status: 'missing' };
158
- try {
159
- const cfg = JSON.parse(fs.readFileSync(file, 'utf8') || '{}');
160
- return cfg?.[wrapKey]?.['klypix-canvas'] ? { status: 'ok' } : { status: 'missing' };
161
- } catch { return { status: 'hand-edited', why: 'invalid JSON' }; }
163
+ let cfg;
164
+ try { cfg = JSON.parse(fs.readFileSync(file, 'utf8') || '{}'); }
165
+ catch { return { status: 'hand-edited', why: 'invalid JSON' }; }
166
+ const entry = cfg?.[wrapKey]?.['klypix-canvas'];
167
+ if (!entry) return { status: 'missing' };
168
+ const args = Array.isArray(entry.args) ? entry.args.map(String) : [];
169
+ const launches = (entry.command === 'npx' || entry.command === 'node') && args.some(a => a.includes('klypix-mcp'));
170
+ return launches ? { status: 'ok' } : { status: 'hand-edited', why: 'entry no longer launches klypix-mcp' };
162
171
  }
163
172
 
164
173
  // The projection map — the single source of truth shared by WRITE (linkProject) and