knit-mcp 0.6.1 → 0.6.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
@@ -193,8 +193,9 @@ knit install-agents --all # install every known agent
193
193
  knit install-agents --refresh # re-fetch from network even if cached
194
194
  ```
195
195
 
196
- `ENGRAM_OFFLINE=1` disables network fetches (bundled-core still works).
197
- `ENGRAM_AGENT_REGISTRY_REF=main` overrides the pinned VoltAgent SHA.
196
+ `KNIT_OFFLINE=1` disables network fetches (bundled-core still works).
197
+ `KNIT_AGENT_REGISTRY_REF=main` overrides the pinned VoltAgent SHA.
198
+ (Legacy `ENGRAM_OFFLINE` / `ENGRAM_AGENT_REGISTRY_REF` are still honored.)
198
199
 
199
200
  ## Parallel team worktrees
200
201
 
@@ -1,6 +1,6 @@
1
1
  # Third-Party Notices
2
2
 
3
- This file lists open-source components redistributed by engram and the licenses
3
+ This file lists open-source components redistributed by Knit and the licenses
4
4
  they ship under. Each section satisfies the attribution requirements of the
5
5
  upstream license.
6
6
 
@@ -13,9 +13,9 @@ upstream license.
13
13
  - **License:** MIT
14
14
  - **Pinned commit:** `6f804f0cfab22fb62668855aa3d62ee3a1453077`
15
15
 
16
- Engram bundles a curated subset of these subagent definitions in
16
+ Knit bundles a curated subset of these subagent definitions in
17
17
  `dist/agents/core/` and fetches additional ones on demand into
18
- `~/.engram/agents/cache/`. Each redistributed file carries an attribution
18
+ `~/.knit/agents/cache/`. Each redistributed file carries an attribution
19
19
  comment immediately after its YAML frontmatter pointing back to the upstream
20
20
  source at the pinned commit. The original frontmatter and prompt content are
21
21
  unmodified.
@@ -2,11 +2,11 @@ import {
2
2
  detectProjectRoot,
3
3
  getBrain,
4
4
  refreshBrain
5
- } from "./chunk-NZXLCN4Q.js";
5
+ } from "./chunk-QU46A5ZT.js";
6
6
  import "./chunk-QMICM263.js";
7
- import "./chunk-GRSYI2RR.js";
8
- import "./chunk-TH5QPD5E.js";
9
- import "./chunk-LW6NOFHF.js";
7
+ import "./chunk-M3YZOJNW.js";
8
+ import "./chunk-Y3I4NAKM.js";
9
+ import "./chunk-7PPC6IG6.js";
10
10
  import "./chunk-BAUQEFYY.js";
11
11
  import "./chunk-YI37OAJ7.js";
12
12
  export {
@@ -5,7 +5,7 @@ import { execSync } from "child_process";
5
5
 
6
6
  // src/engine/agent-registry.ts
7
7
  var VOLTAGENT_PINNED_SHA = "6f804f0cfab22fb62668855aa3d62ee3a1453077";
8
- var VOLTAGENT_REF = process.env.ENGRAM_AGENT_REGISTRY_REF || VOLTAGENT_PINNED_SHA;
8
+ var VOLTAGENT_REF = process.env.KNIT_AGENT_REGISTRY_REF || process.env.ENGRAM_AGENT_REGISTRY_REF || VOLTAGENT_PINNED_SHA;
9
9
  var VOLTAGENT_RAW_BASE = "https://raw.githubusercontent.com/VoltAgent/awesome-claude-code-subagents";
10
10
  var AGENT_CATALOG = {
11
11
  // 02 — Language specialists
@@ -1,5 +1,5 @@
1
1
  // src/engine/learnings.ts
2
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
2
+ import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from "fs";
3
3
  import { dirname } from "path";
4
4
  function readLearnings(filePath) {
5
5
  if (!existsSync(filePath)) return [];
@@ -7,14 +7,14 @@ import {
7
7
  } from "./chunk-QMICM263.js";
8
8
  import {
9
9
  readLearnings
10
- } from "./chunk-GRSYI2RR.js";
10
+ } from "./chunk-M3YZOJNW.js";
11
11
  import {
12
12
  installAgentsForProject,
13
13
  pruneSessionsByAge
14
- } from "./chunk-TH5QPD5E.js";
14
+ } from "./chunk-Y3I4NAKM.js";
15
15
  import {
16
16
  scanProject
17
- } from "./chunk-LW6NOFHF.js";
17
+ } from "./chunk-7PPC6IG6.js";
18
18
  import {
19
19
  importFromMarkdown,
20
20
  loadKnowledgeBase,
@@ -65,13 +65,13 @@ function generateLearningsContent(config) {
65
65
  }
66
66
 
67
67
  // src/generators/settings.ts
68
- var HOOKS_VERSION = 3;
68
+ var HOOKS_VERSION = 6;
69
69
  function generateSettings(config, rootPath) {
70
70
  return {
71
71
  mcpServers: {
72
72
  "knit-brain": {
73
73
  command: "npx",
74
- args: ["-y", "@piyushdua/engram-dev@latest"]
74
+ args: ["-y", "knit-mcp@latest"]
75
75
  }
76
76
  },
77
77
  hooks: generateHooks(config, rootPath),
@@ -83,7 +83,9 @@ function jsLit(s) {
83
83
  }
84
84
  function nodeHook(script) {
85
85
  const compact = script.split("\n").map((l) => l.replace(/\/\/.*$/, "").trim()).filter((l) => l.length > 0).join(" ");
86
- return `node -e '${compact}'`;
86
+ const wrapped = `(() => { ${compact} })();`;
87
+ const escaped = wrapped.replace(/'/g, `'\\''`);
88
+ return `node -e '${escaped}'`;
87
89
  }
88
90
  var REPO_ROOT_JS = `
89
91
  const __getRoot = () => {
@@ -169,7 +171,7 @@ function generateHooks(config, rootPath) {
169
171
  const i = JSON.parse(d);
170
172
  const c = (i.tool_input && i.tool_input.command) || "";
171
173
  if (/^git\\s+(push\\b.*\\s(--force|-f)|reset\\s+--hard|commit.*--no-verify)/.test(c)) {
172
- console.log(JSON.stringify({ decision: "block", reason: "Destructive git operation blocked by Engram. Ask the user first." }));
174
+ console.log(JSON.stringify({ decision: "block", reason: "Destructive git operation blocked by Knit. Ask the user first." }));
173
175
  }
174
176
  } catch (e) {}
175
177
  });
@@ -344,7 +346,7 @@ function generateHooks(config, rootPath) {
344
346
  }
345
347
  `),
346
348
  timeout: 120,
347
- statusMessage: "Engram: final build verification..."
349
+ statusMessage: "Knit: final build verification..."
348
350
  }
349
351
  ]
350
352
  });
@@ -375,7 +377,7 @@ function generateHooks(config, rootPath) {
375
377
  } catch (e) {}
376
378
  `),
377
379
  timeout: 10,
378
- statusMessage: "Engram: capturing session state..."
380
+ statusMessage: "Knit: capturing session state..."
379
381
  }
380
382
  ]
381
383
  });
@@ -409,7 +411,7 @@ function generateHooks(config, rootPath) {
409
411
  } catch (e) {}
410
412
  `),
411
413
  timeout: 10,
412
- statusMessage: "Engram: recording session tuple..."
414
+ statusMessage: "Knit: recording session tuple..."
413
415
  }
414
416
  ]
415
417
  });
@@ -426,14 +428,14 @@ function generateHooks(config, rootPath) {
426
428
  const ageSec = (Date.now() - fs.statSync(file).mtimeMs) / 1000;
427
429
  if (ageSec > 300) {
428
430
  console.log("");
429
- console.log("[Engram] LEARN was not recorded this session. That's fine if nothing reusable surfaced.");
431
+ console.log("[Knit] LEARN was not recorded this session. That's fine if nothing reusable surfaced.");
430
432
  console.log(" If something did, call knit_record_learning in your next session.");
431
433
  console.log("");
432
434
  }
433
435
  } catch (e) {}
434
436
  `),
435
437
  timeout: 5,
436
- statusMessage: "Engram: checking LEARN compliance..."
438
+ statusMessage: "Knit: checking LEARN compliance..."
437
439
  }
438
440
  ]
439
441
  });
@@ -467,7 +469,7 @@ function generateHooks(config, rootPath) {
467
469
  } catch (e) {}
468
470
  `),
469
471
  timeout: 10,
470
- statusMessage: "Engram: updating session metrics..."
472
+ statusMessage: "Knit: updating session metrics..."
471
473
  }
472
474
  ]
473
475
  });
@@ -484,7 +486,7 @@ function maybeRefreshHooks(rootPath, config) {
484
486
  if (!existsSync(settingsPath)) return;
485
487
  try {
486
488
  const existing = JSON.parse(readFileSync(settingsPath, "utf-8"));
487
- const storedVersion = existing?._knitHooks?.version ?? 0;
489
+ const storedVersion = existing?._knitHooks?.version ?? (existing?._engramHooks ? 0 : 0);
488
490
  if (storedVersion < HOOKS_VERSION) {
489
491
  writeKnitHooks(rootPath, config);
490
492
  }
@@ -660,7 +662,7 @@ function writeKnitHooks(rootPath, config) {
660
662
  } catch {
661
663
  return;
662
664
  }
663
- if ("_knitHooks" in existing) {
665
+ if ("_knitHooks" in existing || "_engramHooks" in existing) {
664
666
  mkdirSync(claudeDir, { recursive: true });
665
667
  writeFileSync(settingsPath, JSON.stringify(fresh, null, 2), "utf-8");
666
668
  return;
@@ -680,7 +682,9 @@ function writeKnitHooks(rootPath, config) {
680
682
  for (const event of Object.keys(fresh.hooks)) {
681
683
  const userEntries = Array.isArray(userHooks[event]) ? userHooks[event] : [];
682
684
  const preserved = userEntries.filter((entry) => {
683
- return !(entry && typeof entry === "object" && entry._knitOwned === true);
685
+ if (!entry || typeof entry !== "object") return true;
686
+ const e = entry;
687
+ return e._knitOwned !== true && e._engramOwned !== true;
684
688
  });
685
689
  userHooks[event] = [...preserved, ...fresh.hooks[event]];
686
690
  }
@@ -689,6 +693,7 @@ function writeKnitHooks(rootPath, config) {
689
693
  hooks: userHooks,
690
694
  _knitHooks: { ...fresh._knitHooks, merged: true }
691
695
  };
696
+ delete merged._engramHooks;
692
697
  mkdirSync(claudeDir, { recursive: true });
693
698
  writeFileSync(settingsPath, JSON.stringify(merged, null, 2), "utf-8");
694
699
  }
@@ -5,7 +5,7 @@ import {
5
5
  isBundledCore,
6
6
  knownAgents,
7
7
  rawAgentUrl
8
- } from "./chunk-LW6NOFHF.js";
8
+ } from "./chunk-7PPC6IG6.js";
9
9
  import {
10
10
  agentsCacheFile,
11
11
  projectAgentFile,
@@ -65,9 +65,9 @@ async function fetchAgent(name, opts = {}) {
65
65
  if (existsSync(cachePath)) {
66
66
  return readFileSync(cachePath, "utf-8");
67
67
  }
68
- if (process.env.ENGRAM_OFFLINE === "1") {
68
+ if (process.env.KNIT_OFFLINE === "1" || process.env.ENGRAM_OFFLINE === "1") {
69
69
  throw new AgentFetchError(
70
- `Agent "${name}" not bundled and not cached, and ENGRAM_OFFLINE=1 is set. Either unset ENGRAM_OFFLINE or run \`engram install-agents\` when online to populate the cache.`
70
+ `Agent "${name}" not bundled and not cached, and KNIT_OFFLINE=1 is set. Either unset KNIT_OFFLINE (and legacy ENGRAM_OFFLINE) or run \`knit install-agents\` when online to populate the cache.`
71
71
  );
72
72
  }
73
73
  const url = rawAgentUrl(bare, ref);
package/dist/cli.js CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
+
6
+ // src/version.ts
7
+ import { createRequire } from "module";
8
+ var require2 = createRequire(import.meta.url);
9
+ var pkg = require2("../package.json");
10
+ var VERSION = pkg.version;
11
+
12
+ // src/cli.ts
5
13
  var args = process.argv.slice(2);
6
14
  var hasSubcommand = args.length > 0 && ["setup", "status", "refresh", "install-agents", "export", "--help", "-h", "--version", "-V"].includes(args[0]);
7
15
  var isTTY = process.stdin.isTTY;
@@ -16,11 +24,11 @@ if (hasSubcommand) {
16
24
  async function runCLI() {
17
25
  const gradient = (await import("gradient-string")).default;
18
26
  const chalk = (await import("chalk")).default;
19
- const { setupCommand } = await import("./setup-EQMYVVZ6.js");
20
- const { statusCommand } = await import("./status-56MCC7KE.js");
21
- const { refreshCommand } = await import("./refresh-3UK7NS5A.js");
22
- const { installAgentsCommand } = await import("./install-agents-2UVEAP2W.js");
23
- const { exportCommand } = await import("./export-3MA272OR.js");
27
+ const { setupCommand } = await import("./setup-5TUUWLIJ.js");
28
+ const { statusCommand } = await import("./status-H2CU72CE.js");
29
+ const { refreshCommand } = await import("./refresh-UNT4HGYT.js");
30
+ const { installAgentsCommand } = await import("./install-agents-JS7WB5E6.js");
31
+ const { exportCommand } = await import("./export-D2FXGKBO.js");
24
32
  const ENGRAM_GRADIENT = gradient(["#7c3aed", "#2563eb", "#06b6d4"]);
25
33
  const banner = `
26
34
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
@@ -36,7 +44,7 @@ async function runCLI() {
36
44
  \u2551 \u2551
37
45
  \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`;
38
46
  const program = new Command();
39
- program.name("knit").description("The second brain for Claude Code \u2014 MCP server + analytics dashboard").version("0.4.1").hook("preAction", () => {
47
+ program.name("knit").description("The second brain for Claude Code \u2014 MCP server + analytics dashboard").version(VERSION).hook("preAction", () => {
40
48
  console.log(ENGRAM_GRADIENT.multiline(banner));
41
49
  console.log();
42
50
  });
@@ -86,11 +94,11 @@ async function runMCP() {
86
94
  const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
87
95
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
88
96
  const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
89
- const { getBrain, detectProjectRoot, refreshBrain } = await import("./cache-C6LI7UVN.js");
90
- const { getToolDefinitions, handleToolCall } = await import("./tools-VHBH4PPR.js");
97
+ const { getBrain, detectProjectRoot, refreshBrain } = await import("./cache-7HTB4MTJ.js");
98
+ const { getToolDefinitions, handleToolCall } = await import("./tools-PB7IDFNS.js");
91
99
  const ROOT_PATH = detectProjectRoot();
92
100
  const server = new Server(
93
- { name: "knit-brain", version: "0.4.1" },
101
+ { name: "knit-brain", version: VERSION },
94
102
  { capabilities: { tools: {} } }
95
103
  );
96
104
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
@@ -94,7 +94,7 @@ async function exportObsidian(vaultPath, options) {
94
94
  renderIndex(exported, perProjectCount, globalCount, projectEntryCounts),
95
95
  "utf-8"
96
96
  );
97
- if (!process.env.ENGRAM_EXPORT_QUIET) {
97
+ if (!process.env.KNIT_EXPORT_QUIET && !process.env.ENGRAM_EXPORT_QUIET) {
98
98
  console.log(chalk.green(" \u2713"), `Exported ${perProjectCount} per-project + ${globalCount} global learnings to ${vaultPath}`);
99
99
  }
100
100
  }
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  getBrain
3
- } from "./chunk-NZXLCN4Q.js";
3
+ } from "./chunk-QU46A5ZT.js";
4
4
  import "./chunk-QMICM263.js";
5
- import "./chunk-GRSYI2RR.js";
5
+ import "./chunk-M3YZOJNW.js";
6
6
  import {
7
7
  installAgentsForProject
8
- } from "./chunk-TH5QPD5E.js";
9
- import "./chunk-LW6NOFHF.js";
8
+ } from "./chunk-Y3I4NAKM.js";
9
+ import "./chunk-7PPC6IG6.js";
10
10
  import "./chunk-BAUQEFYY.js";
11
11
  import "./chunk-YI37OAJ7.js";
12
12
 
@@ -19,7 +19,7 @@ async function installAgentsCommand(targetDir, options) {
19
19
  const rootPath = targetDir === "." ? process.cwd() : targetDir;
20
20
  if (!existsSync(join(rootPath, "CLAUDE.md"))) {
21
21
  console.log(chalk.yellow(" No CLAUDE.md found in this directory."));
22
- console.log(chalk.dim(" Open this project in Claude Code with engram MCP first \u2014 it auto-initializes on first tool call."));
22
+ console.log(chalk.dim(" Open this project in Claude Code with the Knit MCP first \u2014 it auto-initializes on first tool call."));
23
23
  process.exit(1);
24
24
  }
25
25
  const spinner = ora({ text: chalk.dim("Loading brain\u2026"), spinner: "dots" }).start();
@@ -53,7 +53,7 @@ async function installAgentsCommand(targetDir, options) {
53
53
  console.log();
54
54
  }
55
55
  if (result.skippedUserCurated.length > 0) {
56
- console.log(chalk.bold(" Skipped (user-curated files; engram won't clobber)"));
56
+ console.log(chalk.bold(" Skipped (user-curated files; Knit won't clobber)"));
57
57
  for (const name of result.skippedUserCurated) {
58
58
  console.log(` ${chalk.yellow("!")} knit-${name}.md`);
59
59
  }
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-QMICM263.js";
5
5
  import {
6
6
  findFalsePositives
7
- } from "./chunk-GRSYI2RR.js";
7
+ } from "./chunk-M3YZOJNW.js";
8
8
  import {
9
9
  scanProject
10
- } from "./chunk-LW6NOFHF.js";
10
+ } from "./chunk-7PPC6IG6.js";
11
11
  import {
12
12
  knowledgePath,
13
13
  learningsDir,
@@ -22,7 +22,7 @@ import ora from "ora";
22
22
  async function refreshCommand(targetDir) {
23
23
  const rootPath = targetDir === "." ? process.cwd() : targetDir;
24
24
  if (!existsSync(join(rootPath, "CLAUDE.md")) || !existsSync(projectDataDir(rootPath))) {
25
- console.log(chalk.red(" No Engram setup found. Open this project in Claude Code with the Knit MCP \u2014 it will auto-initialize."));
25
+ console.log(chalk.red(" No Knit setup found. Open this project in Claude Code with the Knit MCP \u2014 it will auto-initialize."));
26
26
  process.exit(1);
27
27
  }
28
28
  const spinner = ora({ text: chalk.dim("Re-scanning project..."), spinner: "dots" }).start();
@@ -7,7 +7,7 @@ import ora from "ora";
7
7
  var MCP_CONFIG = {
8
8
  "knit-brain": {
9
9
  command: "npx",
10
- args: ["-y", "@piyushdua/engram-dev@latest"]
10
+ args: ["-y", "knit-mcp@latest"]
11
11
  }
12
12
  };
13
13
  async function setupCommand(options) {
@@ -60,9 +60,9 @@ async function setupCommand(options) {
60
60
  console.log(` ${chalk.green("\u2713")} Auto-initializes on first use \u2014 no per-project setup needed`);
61
61
  console.log();
62
62
  const globalClaudeMd = join(homedir(), ".claude", "CLAUDE.md");
63
- const engramInstruction = `
63
+ const knitInstruction = `
64
64
 
65
- ## Engram Brain (MCP)
65
+ ## Knit Brain (MCP)
66
66
 
67
67
  You have the Knit MCP server connected. USE IT on every task:
68
68
 
@@ -75,21 +75,21 @@ For new projects, call \`knit_brain_status\` first \u2014 triggers auto-initiali
75
75
  `;
76
76
  if (existsSync(globalClaudeMd)) {
77
77
  const existing = readFileSync(globalClaudeMd, "utf-8");
78
- if (!existing.includes("Engram Brain (MCP)")) {
79
- writeFileSync(globalClaudeMd, existing + engramInstruction, "utf-8");
80
- console.log(` ${chalk.green("\u2713")} Engram instructions added to ${chalk.cyan("~/.claude/CLAUDE.md")}`);
78
+ if (!existing.includes("Knit Brain (MCP)") && !existing.includes("Engram Brain (MCP)")) {
79
+ writeFileSync(globalClaudeMd, existing + knitInstruction, "utf-8");
80
+ console.log(` ${chalk.green("\u2713")} Knit instructions added to ${chalk.cyan("~/.claude/CLAUDE.md")}`);
81
81
  }
82
82
  } else {
83
83
  const dir2 = join(homedir(), ".claude");
84
84
  if (!existsSync(dir2)) mkdirSync(dir2, { recursive: true });
85
- writeFileSync(globalClaudeMd, `# Claude Code Global Instructions${engramInstruction}`, "utf-8");
86
- console.log(` ${chalk.green("\u2713")} Created ${chalk.cyan("~/.claude/CLAUDE.md")} with Engram instructions`);
85
+ writeFileSync(globalClaudeMd, `# Claude Code Global Instructions${knitInstruction}`, "utf-8");
86
+ console.log(` ${chalk.green("\u2713")} Created ${chalk.cyan("~/.claude/CLAUDE.md")} with Knit instructions`);
87
87
  }
88
88
  console.log();
89
89
  console.log(chalk.bold(" How it works"));
90
90
  console.log(` ${chalk.cyan("1.")} Open ${chalk.bold("any project")} in Claude Code`);
91
91
  console.log(` ${chalk.cyan("2.")} Agent calls \`knit_classify_task\` \u2192 brain auto-initializes`);
92
- console.log(` ${chalk.cyan("3.")} Agent gets 20 tools: imports, exports, tests, learnings, teams`);
92
+ console.log(` ${chalk.cyan("3.")} Agent gets 35 tools: imports, exports, tests, learnings, teams`);
93
93
  console.log(` ${chalk.cyan("4.")} Brain compounds with every session \u2014 gets smarter over time`);
94
94
  console.log();
95
95
  console.log(chalk.dim(" No CLI needed after this. The MCP server handles everything."));
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  readLearnings
3
- } from "./chunk-GRSYI2RR.js";
3
+ } from "./chunk-M3YZOJNW.js";
4
4
  import {
5
5
  getKBSummary,
6
6
  getStaleEntries,
@@ -22,7 +22,7 @@ async function statusCommand(targetDir) {
22
22
  const kbPath = knowledgebasePath(rootPath);
23
23
  const knowledgeIndexPath = knowledgePath(rootPath);
24
24
  if (!existsSync(kbPath) && !existsSync(knowledgeIndexPath)) {
25
- console.log(chalk.yellow(" No Engram data found. The brain will auto-initialize when you open this project in Claude Code."));
25
+ console.log(chalk.yellow(" No Knit data found. The brain will auto-initialize when you open this project in Claude Code."));
26
26
  console.log();
27
27
  return;
28
28
  }
@@ -5,10 +5,10 @@ import {
5
5
  pruneSessionsByAge,
6
6
  searchSessions,
7
7
  sessionCount
8
- } from "./chunk-TH5QPD5E.js";
8
+ } from "./chunk-Y3I4NAKM.js";
9
9
  import {
10
10
  scanProject
11
- } from "./chunk-LW6NOFHF.js";
11
+ } from "./chunk-7PPC6IG6.js";
12
12
  import {
13
13
  appendGlobalLearning,
14
14
  buildGlobalLearning,
@@ -670,6 +670,28 @@ function findTagPairs(entries) {
670
670
  return Object.entries(pairs).sort((a, b) => b[1] - a[1]);
671
671
  }
672
672
 
673
+ // src/mcp/sanitize.ts
674
+ var PATTERNS = [
675
+ { name: "anthropic-key", regex: /sk-ant-[A-Za-z0-9\-_]{20,}/g },
676
+ { name: "openai-key", regex: /sk-[A-Za-z0-9]{32,}/g },
677
+ { name: "github-pat", regex: /ghp_[A-Za-z0-9]{20,}/g },
678
+ { name: "github-pat-fine", regex: /github_pat_[A-Za-z0-9_]{20,}/g },
679
+ { name: "github-oauth", regex: /gho_[A-Za-z0-9]{20,}/g },
680
+ { name: "gitlab-pat", regex: /glpat-[A-Za-z0-9\-_]{20,}/g },
681
+ { name: "aws-access-key-id", regex: /AKIA[A-Z0-9]{16}/g },
682
+ { name: "slack-token", regex: /xox[abopr]-[A-Za-z0-9-]{20,}/g },
683
+ { name: "npm-token", regex: /npm_[A-Za-z0-9]{36,}/g },
684
+ { name: "pem-private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]+?-----END [A-Z ]*PRIVATE KEY-----/g }
685
+ ];
686
+ function redactSecrets(input) {
687
+ if (!input) return input;
688
+ let out = input;
689
+ for (const { name, regex } of PATTERNS) {
690
+ out = out.replace(regex, `[REDACTED:${name}]`);
691
+ }
692
+ return out;
693
+ }
694
+
673
695
  // src/engine/teams.ts
674
696
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
675
697
  import { dirname as dirname2 } from "path";
@@ -1126,11 +1148,11 @@ function handleRecordLearning(params, brain) {
1126
1148
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1127
1149
  const entry = {
1128
1150
  date,
1129
- summary: params.summary || "Untitled learning",
1151
+ summary: redactSecrets(params.summary || "Untitled learning"),
1130
1152
  domains: (params.domains || "general").split(",").map((d) => d.trim()),
1131
- approach: params.approach || "",
1153
+ approach: redactSecrets(params.approach || ""),
1132
1154
  outcome: ["success", "partial", "failure"].includes(params.outcome) ? params.outcome : "success",
1133
- lesson: params.lesson || "",
1155
+ lesson: redactSecrets(params.lesson || ""),
1134
1156
  tags: (params.tags || "").split(/\s+/).filter((t) => t.startsWith("#"))
1135
1157
  };
1136
1158
  addEntry(brain.knowledgeBase, entry);
@@ -1161,11 +1183,11 @@ function handleRecordFalsePositive(params, brain) {
1161
1183
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1162
1184
  const entry = {
1163
1185
  date,
1164
- summary: params.summary || "Untitled FP",
1186
+ summary: redactSecrets(params.summary || "Untitled FP"),
1165
1187
  domains: ["General"],
1166
1188
  approach: "Verified manually",
1167
1189
  outcome: "success",
1168
- lesson: params.reason || "Confirmed non-issue",
1190
+ lesson: redactSecrets(params.reason || "Confirmed non-issue"),
1169
1191
  tags: [...(params.tags || "").split(/\s+/).filter((t) => t.startsWith("#")), "#false-positive"]
1170
1192
  };
1171
1193
  addEntry(brain.knowledgeBase, entry);
@@ -1179,22 +1201,23 @@ function handleRecordFalsePositive(params, brain) {
1179
1201
  }
1180
1202
  function handleSaveHandoff(params, brain) {
1181
1203
  const handoffPath = join2(brain.rootPath, "handoff.md");
1204
+ const r = (k, fallback) => redactSecrets(params[k] || fallback);
1182
1205
  const content = `# Session Handoff
1183
1206
 
1184
- **Goal:** ${params.goal || "Not specified"}
1207
+ **Goal:** ${r("goal", "Not specified")}
1185
1208
 
1186
- **Current State:** ${params.current_state || "Not specified"}
1209
+ **Current State:** ${r("current_state", "Not specified")}
1187
1210
 
1188
- **Files in Flight:** ${params.files_in_flight || "None"}
1211
+ **Files in Flight:** ${r("files_in_flight", "None")}
1189
1212
 
1190
- **What Changed:** ${params.what_changed || "Nothing"}
1213
+ **What Changed:** ${r("what_changed", "Nothing")}
1191
1214
 
1192
1215
  **Failed Attempts:**
1193
- ${params.failed_attempts || "None documented"}
1216
+ ${r("failed_attempts", "None documented")}
1194
1217
 
1195
- **Decisions Made:** ${params.decisions_made || "None"}
1218
+ **Decisions Made:** ${r("decisions_made", "None")}
1196
1219
 
1197
- **Next Step:** ${params.next_step || "Not specified"}
1220
+ **Next Step:** ${r("next_step", "Not specified")}
1198
1221
 
1199
1222
  ---
1200
1223
  *Saved: ${(/* @__PURE__ */ new Date()).toISOString()}*
@@ -1431,8 +1454,8 @@ function handleGetSuggestions(params, brain) {
1431
1454
  });
1432
1455
  }
1433
1456
  function handleRecordGlobalLearning(params, brain) {
1434
- const summary = (params.summary || "").slice(0, 500);
1435
- const lesson = (params.lesson || "").slice(0, 2e3);
1457
+ const summary = redactSecrets((params.summary || "").slice(0, 500));
1458
+ const lesson = redactSecrets((params.lesson || "").slice(0, 2e3));
1436
1459
  const tags = (params.tags || "").split(/\s+/).filter((t) => t.startsWith("#"));
1437
1460
  const outcomeRaw = (params.outcome || "").toLowerCase();
1438
1461
  const outcome = ["success", "partial", "failure"].includes(outcomeRaw) ? outcomeRaw : void 0;
@@ -1604,7 +1627,7 @@ function handleInstallAgent(params, brain) {
1604
1627
  status: "queued",
1605
1628
  agent: name,
1606
1629
  target: `<project>/.claude/agents/knit-${name}.md`,
1607
- instruction: "Install started in background. File will be ready within a few seconds. If it fails, see stderr \u2014 engram does not throw from this handler."
1630
+ instruction: "Install started in background. File will be ready within a few seconds. If it fails, see stderr \u2014 Knit does not throw from this handler."
1608
1631
  });
1609
1632
  }
1610
1633
  function handleSpawnTeamWorktree(params, brain) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knit-mcp",
3
- "version": "0.6.1",
3
+ "version": "0.6.4",
4
4
  "description": "Knit — second brain for Claude Code. MCP server giving any AI agent project-scoped memory, tiered workflow protocol, and parallel team worktrees.",
5
5
  "type": "module",
6
6
  "bin": {