knit-mcp 0.11.4 → 0.12.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 +1 -0
- package/dist/{cache-7HSMIYDJ.js → cache-46OKHDRR.js} +2 -2
- package/dist/{chunk-I63UMEBF.js → chunk-KKLAJLPO.js} +4 -4
- package/dist/{chunk-LV73YTVN.js → chunk-QQNHF4XY.js} +28 -4
- package/dist/{doctor-4DN2P2JR.js → chunk-TE6NP6BZ.js} +47 -4
- package/dist/{chunk-HROSQ5MS.js → chunk-WADRF26Z.js} +34 -1
- package/dist/cli.js +6 -6
- package/dist/doctor-NI22FPXV.js +12 -0
- package/dist/{install-agents-WDBQBWMN.js → install-agents-AH7EBB4J.js} +2 -2
- package/dist/{instructions-JARSXQPO.js → instructions-CF6K57Z2.js} +5 -1
- package/dist/{setup-5TUUWLIJ.js → setup-62ZH7GEI.js} +31 -1
- package/dist/{tools-ECHCPLCB.js → tools-ZQHRWMVK.js} +79 -21
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -452,6 +452,7 @@ LongMemEval-S R@5/R@10 + LOCOMO LLM-as-Judge runs are on the roadmap (v0.13+). U
|
|
|
452
452
|
|
|
453
453
|
| Version | Headline |
|
|
454
454
|
|---|---|
|
|
455
|
+
| **v0.12.0** | **Picture Perfect: Structural Enforcement.** Diagnostic → enforcing. Budget verdict surfaces in the MCP `instructions` field at handshake (before any tool description is read). `knit_load_session` carries `budget_health` + `learnings_health` nudges. `engram doctor` exits non-zero on over-budget; `engram setup` runs doctor as final step. New PostToolUse hook warns immediately on over-budget CLAUDE.md edits (HOOKS_VERSION 11→12; auto-rolls to existing users). This repo dogfoods: hand-curated 16KB CLAUDE.md migrated to lean 3.8KB + `.claude/MARKETING.md` sidecar. New `npm run bench:tokens` measures real MCP-on vs MCP-off cost: 93% smaller per-recall call, 50% smaller per-classify, payback at 3 recall calls. 53 tools, 705 tests. |
|
|
455
456
|
| **v0.11.4** | Dogfood audit · ran a full audit of Knit's own codebase using its own `knit_spawn_team_worktree` primitive (4 parallel teams: Core Logic, Infrastructure, UI, Quality Assurance). Fixes: HIGH `engram refresh` no longer clobbers user-curated CLAUDE.md (now uses `spliceKnitBlock` like `cache.ts`); `saveSource`/`loadSource` validate `sourceId`; `appendGlobalLearning` propagates write failures; `redactSecrets` applied to `label`/`tags`/`domains` across all persistence boundaries; 100KB response ceiling on `knit_generate_test_cases`; full v0.11 tool surface now documented in `workflow-protocol.ts` generator (was frozen at the v0.4 surface). Plus: 16 key tools reclassified with `[PROTOCOL]`/`[REVIEW]`/`[MEMORY]`/`[GRAPH]` prefixes so the LLM picks the right tool reliably. 53 tools, 687 tests. |
|
|
456
457
|
| **v0.11.3** | Propagation patch · `update_available` flag now surfaces in `knit_load_session` response (≈100% session reach vs. brain_status' low reach) + startup stderr nag on stale versions. Helps FUTURE upgrades land faster; doesn't retroactively reach v0.10.x users. 53 tools, 665 tests. |
|
|
457
458
|
| **v0.11.2** | Pre-publish polish · chunk cap (2000) + `errorResponse` envelope across handlers + CLAUDE.md generator surfaces v0.11 tools · new `engram doctor` install health-check CLI · upgrade-path smoke test caught + fixed a data-loss bug in cache.ts (Case B was wiping user permissions on upgrade) · 11 real exploit-payload integration tests prove C1/C2/H1 fixes hold · `npm run bench` ships a synthetic retrieval harness (50 Q&A) measuring 86% top-1 / 96% R@5. 53 tools, 664 tests. |
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
detectProjectRoot,
|
|
3
3
|
getBrain,
|
|
4
4
|
refreshBrain
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-HROSQ5MS.js";
|
|
5
|
+
} from "./chunk-KKLAJLPO.js";
|
|
7
6
|
import "./chunk-GATMQQK5.js";
|
|
7
|
+
import "./chunk-WADRF26Z.js";
|
|
8
8
|
import "./chunk-WKQHCLLO.js";
|
|
9
9
|
import "./chunk-MOOVNMIN.js";
|
|
10
10
|
import "./chunk-ST4X7LZT.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
HOOKS_VERSION,
|
|
3
|
-
generateSettings
|
|
4
|
-
} from "./chunk-HROSQ5MS.js";
|
|
5
1
|
import {
|
|
6
2
|
installAgentsForProject,
|
|
7
3
|
pruneSessionsByAge
|
|
8
4
|
} from "./chunk-GATMQQK5.js";
|
|
5
|
+
import {
|
|
6
|
+
HOOKS_VERSION,
|
|
7
|
+
generateSettings
|
|
8
|
+
} from "./chunk-WADRF26Z.js";
|
|
9
9
|
import {
|
|
10
10
|
importFromMarkdown,
|
|
11
11
|
loadKnowledgeBaseSafe,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// src/mcp/instructions.ts
|
|
2
|
+
import { statSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
2
4
|
var KNIT_INSTRUCTIONS_BASE = `Knit is a memory + workflow layer for this project. It provides per-project memory across sessions, a knowledge graph (imports/exports/tests), and a tier-routed workflow protocol.
|
|
3
5
|
|
|
4
6
|
ALWAYS at session start:
|
|
@@ -32,8 +34,28 @@ Knit provides inputs; you make the calls. When in doubt, under-classify \u2014 e
|
|
|
32
34
|
|
|
33
35
|
Citation rule: when you state a fact about this codebase ("file X imports Y", "function Z is defined in W", "tests for A live in B"), cite the Knit tool result that verified it \u2014 e.g. "(per knit_query_imports)". If you can't cite a tool result, mark the claim as 'unverified' explicitly. This makes hallucinations visible at the claim level instead of letting them ship as confident-sounding prose. The verifier exists; use it.`;
|
|
34
36
|
var KNIT_INSTRUCTIONS = KNIT_INSTRUCTIONS_BASE;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
var CLAUDE_MD_BUDGET_BYTES = 6500;
|
|
38
|
+
function buildBudgetVerdict(rootPath) {
|
|
39
|
+
let bytes = 0;
|
|
40
|
+
try {
|
|
41
|
+
bytes = statSync(join(rootPath, "CLAUDE.md")).size;
|
|
42
|
+
} catch {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
if (bytes <= CLAUDE_MD_BUDGET_BYTES) return "";
|
|
46
|
+
const verdict = bytes > CLAUDE_MD_BUDGET_BYTES * 1.25 ? "over-budget" : "warn";
|
|
47
|
+
const kb = Math.round(bytes / 1024 * 10) / 10;
|
|
48
|
+
const targetKb = Math.round(CLAUDE_MD_BUDGET_BYTES / 1024 * 10) / 10;
|
|
49
|
+
return `BUDGET ${verdict}: CLAUDE.md is ${kb}KB / ${targetKb}KB target. Run \`engram doctor\` to see the full per-surface report and \`engram refresh\` to regenerate the marker block.`;
|
|
50
|
+
}
|
|
51
|
+
function buildInstructions(scan, rootPath) {
|
|
52
|
+
const budgetLine = rootPath ? buildBudgetVerdict(rootPath) : "";
|
|
53
|
+
const budgetSuffix = budgetLine ? `
|
|
54
|
+
|
|
55
|
+
\u2014 Budget check \u2014
|
|
56
|
+
|
|
57
|
+
${budgetLine}` : "";
|
|
58
|
+
if (!scan) return KNIT_INSTRUCTIONS_BASE + budgetSuffix;
|
|
37
59
|
const addenda = [];
|
|
38
60
|
if (scan.detected.ruflo.present) {
|
|
39
61
|
addenda.push(
|
|
@@ -60,12 +82,14 @@ function buildInstructions(scan) {
|
|
|
60
82
|
`DETECTED: this project's CLAUDE.md has user-curated workflow sections (${scan.detected.custom_workflow_sections.join("; ")}). Treat that as the canonical workflow doc for project-specific phases; Knit's tier protocol applies underneath as the routing layer.`
|
|
61
83
|
);
|
|
62
84
|
}
|
|
63
|
-
if (addenda.length === 0) return KNIT_INSTRUCTIONS_BASE;
|
|
64
|
-
return KNIT_INSTRUCTIONS_BASE + "\n\n\u2014 Per-project integrations \u2014\n\n" + addenda.join("\n\n") + "\n\nGeneral rule: when an integration above provides a higher-level routing primitive (slash command, swarm orchestrator, methodology framework), use it. Knit handles the substrate it doesn't cover: memory, classification, and the workflow protocol for tasks the integration doesn't route.";
|
|
85
|
+
if (addenda.length === 0) return KNIT_INSTRUCTIONS_BASE + budgetSuffix;
|
|
86
|
+
return KNIT_INSTRUCTIONS_BASE + "\n\n\u2014 Per-project integrations \u2014\n\n" + addenda.join("\n\n") + "\n\nGeneral rule: when an integration above provides a higher-level routing primitive (slash command, swarm orchestrator, methodology framework), use it. Knit handles the substrate it doesn't cover: memory, classification, and the workflow protocol for tasks the integration doesn't route." + budgetSuffix;
|
|
65
87
|
}
|
|
66
88
|
|
|
67
89
|
export {
|
|
68
90
|
KNIT_INSTRUCTIONS_BASE,
|
|
69
91
|
KNIT_INSTRUCTIONS,
|
|
92
|
+
CLAUDE_MD_BUDGET_BYTES,
|
|
93
|
+
buildBudgetVerdict,
|
|
70
94
|
buildInstructions
|
|
71
95
|
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HOOKS_VERSION
|
|
3
|
+
} from "./chunk-WADRF26Z.js";
|
|
1
4
|
import {
|
|
2
5
|
VERSION
|
|
3
6
|
} from "./chunk-UTVFELXS.js";
|
|
4
7
|
import {
|
|
5
|
-
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
CLAUDE_MD_BUDGET_BYTES
|
|
9
|
+
} from "./chunk-QQNHF4XY.js";
|
|
7
10
|
import {
|
|
8
11
|
knowledgebasePath,
|
|
9
12
|
projectDataDir
|
|
@@ -125,6 +128,45 @@ function runDoctor(rootPath) {
|
|
|
125
128
|
} catch {
|
|
126
129
|
}
|
|
127
130
|
}
|
|
131
|
+
const claudeMdPath = join(rootPath, "CLAUDE.md");
|
|
132
|
+
if (existsSync(claudeMdPath)) {
|
|
133
|
+
try {
|
|
134
|
+
const bytes = statSync(claudeMdPath).size;
|
|
135
|
+
const kb = Math.round(bytes / 1024 * 10) / 10;
|
|
136
|
+
const targetKb = Math.round(CLAUDE_MD_BUDGET_BYTES / 1024 * 10) / 10;
|
|
137
|
+
if (bytes <= CLAUDE_MD_BUDGET_BYTES) {
|
|
138
|
+
checks.push({
|
|
139
|
+
name: "Token budget",
|
|
140
|
+
status: "ok",
|
|
141
|
+
detail: `CLAUDE.md ${kb}KB / ${targetKb}KB target \u2014 healthy`
|
|
142
|
+
});
|
|
143
|
+
} else if (bytes <= CLAUDE_MD_BUDGET_BYTES * 1.25) {
|
|
144
|
+
checks.push({
|
|
145
|
+
name: "Token budget",
|
|
146
|
+
status: "warn",
|
|
147
|
+
detail: `CLAUDE.md ${kb}KB / ${targetKb}KB target \u2014 over budget, within 25% slack. Run \`engram refresh\` or trim the file.`
|
|
148
|
+
});
|
|
149
|
+
} else {
|
|
150
|
+
checks.push({
|
|
151
|
+
name: "Token budget",
|
|
152
|
+
status: "error",
|
|
153
|
+
detail: `CLAUDE.md ${kb}KB / ${targetKb}KB target \u2014 over budget by >25%. Move long-form content to .claude/MARKETING.md or run \`engram refresh\`.`
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
checks.push({
|
|
158
|
+
name: "Token budget",
|
|
159
|
+
status: "warn",
|
|
160
|
+
detail: `CLAUDE.md unreadable: ${err.message}`
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
checks.push({
|
|
165
|
+
name: "Token budget",
|
|
166
|
+
status: "info",
|
|
167
|
+
detail: "no CLAUDE.md yet \u2014 created on first MCP call"
|
|
168
|
+
});
|
|
169
|
+
}
|
|
128
170
|
const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
|
|
129
171
|
if (Number.isFinite(nodeMajor) && nodeMajor < 18) {
|
|
130
172
|
checks.push({
|
|
@@ -173,7 +215,8 @@ async function doctorCommand(directory) {
|
|
|
173
215
|
console.log(chalk.green(" All checks passed \u2014 install is healthy."));
|
|
174
216
|
}
|
|
175
217
|
}
|
|
218
|
+
|
|
176
219
|
export {
|
|
177
|
-
|
|
178
|
-
|
|
220
|
+
runDoctor,
|
|
221
|
+
doctorCommand
|
|
179
222
|
};
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "./chunk-27TA2ZQZ.js";
|
|
16
16
|
|
|
17
17
|
// src/generators/settings.ts
|
|
18
|
-
var HOOKS_VERSION =
|
|
18
|
+
var HOOKS_VERSION = 12;
|
|
19
19
|
function generateSettings(config, rootPath) {
|
|
20
20
|
return {
|
|
21
21
|
mcpServers: {
|
|
@@ -288,6 +288,39 @@ function generateHooks(config, rootPath) {
|
|
|
288
288
|
}
|
|
289
289
|
]
|
|
290
290
|
});
|
|
291
|
+
hooks.PostToolUse.push({
|
|
292
|
+
_knitOwned: true,
|
|
293
|
+
matcher: "Write|Edit|MultiEdit",
|
|
294
|
+
hooks: [
|
|
295
|
+
{
|
|
296
|
+
type: "command",
|
|
297
|
+
command: nodeHook(`
|
|
298
|
+
let d = "";
|
|
299
|
+
process.stdin.on("data", (c) => d += c);
|
|
300
|
+
process.stdin.on("end", () => {
|
|
301
|
+
try {
|
|
302
|
+
const fs = require("fs");
|
|
303
|
+
const path = require("path");
|
|
304
|
+
const i = JSON.parse(d);
|
|
305
|
+
const ti = i.tool_input || {};
|
|
306
|
+
const f = ti.file_path || (i.tool_response && i.tool_response.filePath) || "";
|
|
307
|
+
if (!f) return;
|
|
308
|
+
if (path.basename(f) !== "CLAUDE.md") return;
|
|
309
|
+
const TARGET = 6500;
|
|
310
|
+
const SLACK = 6500 * 1.25;
|
|
311
|
+
let size = 0;
|
|
312
|
+
try { size = fs.statSync(f).size; } catch { return; }
|
|
313
|
+
if (size <= TARGET) return;
|
|
314
|
+
const kb = Math.round(size/1024*10)/10;
|
|
315
|
+
const verdict = size > SLACK ? "over-budget" : "warn";
|
|
316
|
+
process.stderr.write("[knit] BUDGET " + verdict + ": " + f + " is now " + kb + "KB (target 6.5KB). Move long-form content to .claude/MARKETING.md or run \\\`engram refresh\\\`.\\n");
|
|
317
|
+
} catch (e) { try { process.stderr.write('[knit] claude-md size watch hook failed: ' + (e && e.message ? e.message : e) + '\\n'); } catch {} }
|
|
318
|
+
});
|
|
319
|
+
`),
|
|
320
|
+
timeout: 5
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
});
|
|
291
324
|
hooks.PostToolUse.push({
|
|
292
325
|
_knitOwned: true,
|
|
293
326
|
matcher: "Write|Edit|MultiEdit",
|
package/dist/cli.js
CHANGED
|
@@ -19,12 +19,12 @@ if (hasSubcommand) {
|
|
|
19
19
|
async function runCLI() {
|
|
20
20
|
const gradient = (await import("gradient-string")).default;
|
|
21
21
|
const chalk = (await import("chalk")).default;
|
|
22
|
-
const { setupCommand } = await import("./setup-
|
|
22
|
+
const { setupCommand } = await import("./setup-62ZH7GEI.js");
|
|
23
23
|
const { statusCommand } = await import("./status-2SEITNIE.js");
|
|
24
24
|
const { refreshCommand } = await import("./refresh-S62AZ3QA.js");
|
|
25
|
-
const { installAgentsCommand } = await import("./install-agents-
|
|
25
|
+
const { installAgentsCommand } = await import("./install-agents-AH7EBB4J.js");
|
|
26
26
|
const { exportCommand } = await import("./export-4BO6HCXP.js");
|
|
27
|
-
const { doctorCommand } = await import("./doctor-
|
|
27
|
+
const { doctorCommand } = await import("./doctor-NI22FPXV.js");
|
|
28
28
|
const ENGRAM_GRADIENT = gradient(["#7c3aed", "#2563eb", "#06b6d4"]);
|
|
29
29
|
const banner = `
|
|
30
30
|
\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
|
|
@@ -98,9 +98,9 @@ async function runMCP() {
|
|
|
98
98
|
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
99
99
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
100
100
|
const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
|
|
101
|
-
const { getBrain, detectProjectRoot, refreshBrain } = await import("./cache-
|
|
102
|
-
const { getActiveToolDefinitionsForBrain, handleToolCall } = await import("./tools-
|
|
103
|
-
const { buildInstructions } = await import("./instructions-
|
|
101
|
+
const { getBrain, detectProjectRoot, refreshBrain } = await import("./cache-46OKHDRR.js");
|
|
102
|
+
const { getActiveToolDefinitionsForBrain, handleToolCall } = await import("./tools-ZQHRWMVK.js");
|
|
103
|
+
const { buildInstructions } = await import("./instructions-CF6K57Z2.js");
|
|
104
104
|
const { registerToolsListChangedNotifier } = await import("./notifier-4L27HKHI.js");
|
|
105
105
|
const { loadScanResult } = await import("./integration-scanner-LBD2PIZ3.js");
|
|
106
106
|
const { prewarmLatestVersion, getCachedLatestVersion, isNewerVersion } = await import("./update-check-GQVDVT2N.js");
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getBrain
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-HROSQ5MS.js";
|
|
3
|
+
} from "./chunk-KKLAJLPO.js";
|
|
5
4
|
import {
|
|
6
5
|
installAgentsForProject
|
|
7
6
|
} from "./chunk-GATMQQK5.js";
|
|
7
|
+
import "./chunk-WADRF26Z.js";
|
|
8
8
|
import "./chunk-WKQHCLLO.js";
|
|
9
9
|
import "./chunk-MOOVNMIN.js";
|
|
10
10
|
import "./chunk-ST4X7LZT.js";
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CLAUDE_MD_BUDGET_BYTES,
|
|
2
3
|
KNIT_INSTRUCTIONS,
|
|
3
4
|
KNIT_INSTRUCTIONS_BASE,
|
|
5
|
+
buildBudgetVerdict,
|
|
4
6
|
buildInstructions
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-QQNHF4XY.js";
|
|
6
8
|
export {
|
|
9
|
+
CLAUDE_MD_BUDGET_BYTES,
|
|
7
10
|
KNIT_INSTRUCTIONS,
|
|
8
11
|
KNIT_INSTRUCTIONS_BASE,
|
|
12
|
+
buildBudgetVerdict,
|
|
9
13
|
buildInstructions
|
|
10
14
|
};
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runDoctor
|
|
3
|
+
} from "./chunk-TE6NP6BZ.js";
|
|
4
|
+
import "./chunk-WADRF26Z.js";
|
|
5
|
+
import "./chunk-UTVFELXS.js";
|
|
6
|
+
import "./chunk-QQNHF4XY.js";
|
|
7
|
+
import "./chunk-27TA2ZQZ.js";
|
|
8
|
+
|
|
1
9
|
// src/commands/setup.ts
|
|
2
10
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
3
11
|
import { join, dirname } from "path";
|
|
@@ -89,7 +97,7 @@ For new projects, call \`knit_brain_status\` first \u2014 triggers auto-initiali
|
|
|
89
97
|
console.log(chalk.bold(" How it works"));
|
|
90
98
|
console.log(` ${chalk.cyan("1.")} Open ${chalk.bold("any project")} in Claude Code`);
|
|
91
99
|
console.log(` ${chalk.cyan("2.")} Agent calls \`knit_classify_task\` \u2192 brain auto-initializes`);
|
|
92
|
-
console.log(` ${chalk.cyan("3.")} Agent gets
|
|
100
|
+
console.log(` ${chalk.cyan("3.")} Agent gets 53+ tools: imports, exports, tests, learnings, teams, requirements`);
|
|
93
101
|
console.log(` ${chalk.cyan("4.")} Brain compounds with every session \u2014 gets smarter over time`);
|
|
94
102
|
console.log();
|
|
95
103
|
console.log(chalk.dim(" No CLI needed after this. The MCP server handles everything."));
|
|
@@ -98,6 +106,28 @@ For new projects, call \`knit_brain_status\` first \u2014 triggers auto-initiali
|
|
|
98
106
|
console.log(chalk.dim(` Config written to: ${settingsPath}`));
|
|
99
107
|
}
|
|
100
108
|
console.log();
|
|
109
|
+
console.log(chalk.bold(" Install health check"));
|
|
110
|
+
console.log();
|
|
111
|
+
try {
|
|
112
|
+
const report = runDoctor(process.cwd());
|
|
113
|
+
for (const c of report.checks) {
|
|
114
|
+
const icon = c.status === "ok" ? chalk.green("\u2713") : c.status === "warn" ? chalk.yellow("\u26A0") : c.status === "error" ? chalk.red("\u2717") : chalk.gray("\xB7");
|
|
115
|
+
console.log(` ${icon} ${c.name.padEnd(22)} ${chalk.dim(c.detail)}`);
|
|
116
|
+
}
|
|
117
|
+
const errors = report.checks.filter((c) => c.status === "error").length;
|
|
118
|
+
const warnings = report.checks.filter((c) => c.status === "warn").length;
|
|
119
|
+
console.log();
|
|
120
|
+
if (errors > 0) {
|
|
121
|
+
console.log(chalk.red(` ${errors} error(s) \u2014 run \`engram doctor\` for full details + fix commands.`));
|
|
122
|
+
} else if (warnings > 0) {
|
|
123
|
+
console.log(chalk.yellow(` ${warnings} warning(s) \u2014 setup complete, but check items above.`));
|
|
124
|
+
} else {
|
|
125
|
+
console.log(chalk.green(" All checks passed \u2014 install is healthy."));
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.log(chalk.dim(` (doctor skipped: ${err.message})`));
|
|
129
|
+
}
|
|
130
|
+
console.log();
|
|
101
131
|
}
|
|
102
132
|
export {
|
|
103
133
|
setupCommand
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appendSession,
|
|
3
|
+
getRecentSessions,
|
|
4
|
+
installAgentsForProject,
|
|
5
|
+
loadAllSessions,
|
|
6
|
+
pruneSessionsByAge,
|
|
7
|
+
searchSessions,
|
|
8
|
+
sessionCount
|
|
9
|
+
} from "./chunk-GATMQQK5.js";
|
|
1
10
|
import {
|
|
2
11
|
appendGlobalLearning,
|
|
3
12
|
buildGlobalLearning,
|
|
@@ -8,21 +17,12 @@ import {
|
|
|
8
17
|
import {
|
|
9
18
|
notifyToolsListChanged
|
|
10
19
|
} from "./chunk-WMESQUZU.js";
|
|
11
|
-
import {
|
|
12
|
-
KNIT_INSTRUCTIONS
|
|
13
|
-
} from "./chunk-LV73YTVN.js";
|
|
14
20
|
import {
|
|
15
21
|
VERSION
|
|
16
22
|
} from "./chunk-UTVFELXS.js";
|
|
17
23
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
installAgentsForProject,
|
|
21
|
-
loadAllSessions,
|
|
22
|
-
pruneSessionsByAge,
|
|
23
|
-
searchSessions,
|
|
24
|
-
sessionCount
|
|
25
|
-
} from "./chunk-GATMQQK5.js";
|
|
24
|
+
KNIT_INSTRUCTIONS
|
|
25
|
+
} from "./chunk-QQNHF4XY.js";
|
|
26
26
|
import {
|
|
27
27
|
addEntry,
|
|
28
28
|
bumpClassificationTier,
|
|
@@ -2012,20 +2012,23 @@ var TOKEN_BUDGETS = {
|
|
|
2012
2012
|
/** Generated CLAUDE.md block. v0.7 trim landed at ~2KB on typical projects;
|
|
2013
2013
|
* 6.5KB target allows for projects with many domains / large project map. */
|
|
2014
2014
|
claude_md_bytes: 6500,
|
|
2015
|
-
/** Tier-gated tools/list response. v0.12 typical:
|
|
2016
|
-
* bytes ≈
|
|
2017
|
-
*
|
|
2018
|
-
*
|
|
2019
|
-
tool_registry_bytes:
|
|
2015
|
+
/** Tier-gated tools/list response. v0.12 typical: 40 Tier-1 active × ~280
|
|
2016
|
+
* bytes ≈ 11.2KB. 12KB target gives Tier-1 healthy headroom; full
|
|
2017
|
+
* 51-tool exposure (everything enabled) sits in warn range, surfacing
|
|
2018
|
+
* the bloat. Bumped from 11000 in v0.12 to reflect actual Tier-1 size. */
|
|
2019
|
+
tool_registry_bytes: 12e3,
|
|
2020
2020
|
/** MCP server `instructions` field — sent at handshake. v0.11.1 surfaces
|
|
2021
2021
|
* 9 new tools (verify_claim, calibration, requirements ingestion,
|
|
2022
|
-
* fingerprint, infer_domains, compose_template) → ~3.5KB.
|
|
2023
|
-
*
|
|
2022
|
+
* fingerprint, infer_domains, compose_template) → ~3.5KB. v0.12 may
|
|
2023
|
+
* append a one-line budget verdict (~200B) when CLAUDE.md is over
|
|
2024
|
+
* budget. The discoverability-vs-budget trade-off favors surfacing
|
|
2025
|
+
* real tools. */
|
|
2024
2026
|
instructions_bytes: 4e3,
|
|
2025
2027
|
/** Sum of the three above — the per-session fixed cost Knit imposes.
|
|
2026
|
-
* v0.12 typical: ~
|
|
2027
|
-
*
|
|
2028
|
-
|
|
2028
|
+
* v0.12 typical: ~15KB (CLAUDE.md ~2KB + tools ~11.2KB + instructions
|
|
2029
|
+
* ~2.6KB); 22KB target covers the union with slack as more tools
|
|
2030
|
+
* come online. Bumped from 20000 alongside tool_registry. */
|
|
2031
|
+
per_session_overhead_bytes: 22e3
|
|
2029
2032
|
};
|
|
2030
2033
|
function verdict(actual, target) {
|
|
2031
2034
|
if (actual <= target) return "healthy";
|
|
@@ -3560,6 +3563,57 @@ function parseLoadSessionInclude(raw) {
|
|
|
3560
3563
|
raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean)
|
|
3561
3564
|
);
|
|
3562
3565
|
}
|
|
3566
|
+
function computeBudgetHealth(brain) {
|
|
3567
|
+
let claudeMdBytes = 0;
|
|
3568
|
+
try {
|
|
3569
|
+
claudeMdBytes = statSync3(join3(brain.rootPath, "CLAUDE.md")).size;
|
|
3570
|
+
} catch {
|
|
3571
|
+
}
|
|
3572
|
+
const shape = detectProjectShape(brain);
|
|
3573
|
+
const listing = computeFeatureListing(shape);
|
|
3574
|
+
const toolRegistryBytes = listing.totals.active * 280;
|
|
3575
|
+
const instructionsBytes = KNIT_INSTRUCTIONS.length;
|
|
3576
|
+
const perSessionOverheadBytes = claudeMdBytes + toolRegistryBytes + instructionsBytes;
|
|
3577
|
+
const cv = verdict(claudeMdBytes, TOKEN_BUDGETS.claude_md_bytes);
|
|
3578
|
+
const tv = verdict(toolRegistryBytes, TOKEN_BUDGETS.tool_registry_bytes);
|
|
3579
|
+
const iv = verdict(instructionsBytes, TOKEN_BUDGETS.instructions_bytes);
|
|
3580
|
+
const ov = verdict(perSessionOverheadBytes, TOKEN_BUDGETS.per_session_overhead_bytes);
|
|
3581
|
+
const verdicts = [cv, tv, iv, ov];
|
|
3582
|
+
const overall = verdicts.includes("over-budget") ? "over-budget" : verdicts.includes("warn") ? "warn" : "healthy";
|
|
3583
|
+
if (overall === "healthy") return void 0;
|
|
3584
|
+
const rank = (v) => v === "over-budget" ? 2 : v === "warn" ? 1 : 0;
|
|
3585
|
+
const surfaces = [
|
|
3586
|
+
["claude_md", cv],
|
|
3587
|
+
["tool_registry", tv],
|
|
3588
|
+
["instructions", iv]
|
|
3589
|
+
];
|
|
3590
|
+
surfaces.sort((a, b) => rank(b[1]) - rank(a[1]));
|
|
3591
|
+
const worst = surfaces[0][0];
|
|
3592
|
+
const suggestions = {
|
|
3593
|
+
claude_md: "CLAUDE.md is over the 6.5KB target \u2014 run `engram refresh` to splice the lean marker-block, or check that the file is using the generator (not hand-curated).",
|
|
3594
|
+
tool_registry: "Tool registry over budget \u2014 call knit_list_features to see which Tier-2/3 tools are active and disable any you do not need.",
|
|
3595
|
+
instructions: "Instructions block over budget \u2014 likely a v0.x \u2192 v0.y growth. Restart Claude Code to pick up the trimmed instructions."
|
|
3596
|
+
};
|
|
3597
|
+
return {
|
|
3598
|
+
verdict: overall,
|
|
3599
|
+
per_session_kb: Math.round(perSessionOverheadBytes / 1024 * 10) / 10,
|
|
3600
|
+
worst_surface: worst,
|
|
3601
|
+
suggestion: suggestions[worst]
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
function computeLearningsHealth(brain) {
|
|
3605
|
+
const total = brain.knowledgeBase.entries.length;
|
|
3606
|
+
if (total < 5) return void 0;
|
|
3607
|
+
const accessed = brain.knowledgeBase.entries.filter((e) => e.accessCount > 0).length;
|
|
3608
|
+
const pct = total > 0 ? Math.round(accessed / total * 100) : 0;
|
|
3609
|
+
if (pct >= 30) return { total, accessed_pct: pct, verdict: "healthy" };
|
|
3610
|
+
return {
|
|
3611
|
+
total,
|
|
3612
|
+
accessed_pct: pct,
|
|
3613
|
+
verdict: "low-utilization",
|
|
3614
|
+
suggestion: `${total} learnings recorded but only ${pct}% have been recalled. Either call knit_search_learnings before re-investigating, or prune stale entries with knit_consolidate_learnings.`
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3563
3617
|
function handleLoadSession(params, brain) {
|
|
3564
3618
|
const include = parseLoadSessionInclude(params.include);
|
|
3565
3619
|
const wantAll = include.has("all");
|
|
@@ -3641,6 +3695,8 @@ function handleLoadSession(params, brain) {
|
|
|
3641
3695
|
changelog: "https://github.com/PDgit12/knit/blob/main/CHANGELOG.md"
|
|
3642
3696
|
};
|
|
3643
3697
|
}
|
|
3698
|
+
const budgetHealth = computeBudgetHealth(brain);
|
|
3699
|
+
const learningsHealth = computeLearningsHealth(brain);
|
|
3644
3700
|
const response = {
|
|
3645
3701
|
session_context: {
|
|
3646
3702
|
last_session: lastSession,
|
|
@@ -3659,6 +3715,8 @@ function handleLoadSession(params, brain) {
|
|
|
3659
3715
|
...recentSessions !== void 0 ? { recent_sessions: recentSessions } : {}
|
|
3660
3716
|
},
|
|
3661
3717
|
...updateAvailable ? { update_available: updateAvailable } : {},
|
|
3718
|
+
...budgetHealth ? { budget_health: budgetHealth } : {},
|
|
3719
|
+
...learningsHealth && learningsHealth.verdict === "low-utilization" ? { learnings_health: learningsHealth } : {},
|
|
3662
3720
|
instruction: handoff2 ? "UNFINISHED WORK DETECTED. Read the handoff above \u2014 pick up where the last session left off. Do NOT start fresh." : topLearnings.length > 0 ? `Session loaded. ${topLearnings.length} key learnings, ${fps.length} false positives. Call knit_classify_task to begin. Use include=patterns,teams,metrics,recent_sessions,full_learnings,full_knowledge for more.` : "Fresh brain \u2014 no past learnings yet. Call knit_classify_task to begin."
|
|
3663
3721
|
};
|
|
3664
3722
|
return JSON.stringify(response);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knit-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
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": {
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
"lint": "eslint src/ tests/ --ext .ts",
|
|
15
15
|
"test": "vitest run",
|
|
16
16
|
"test:watch": "vitest",
|
|
17
|
-
"bench": "
|
|
17
|
+
"bench": "npm run bench:retrieval",
|
|
18
|
+
"bench:retrieval": "tsx benchmarks/retrieval-synthetic.ts",
|
|
19
|
+
"bench:tokens": "tsx benchmarks/token-economy.ts",
|
|
20
|
+
"bench:all": "npm run bench:retrieval && npm run bench:tokens",
|
|
18
21
|
"prepublishOnly": "npm run typecheck && npm run lint && npm run test && npm run build"
|
|
19
22
|
},
|
|
20
23
|
"keywords": [
|