knit-mcp 0.11.2 → 0.11.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 +3 -1
- package/dist/{cache-3LPETDUT.js → cache-7HSMIYDJ.js} +4 -3
- package/dist/{chunk-RZOVZYTF.js → chunk-GATMQQK5.js} +2 -54
- package/dist/{chunk-TWHNYJAJ.js → chunk-I63UMEBF.js} +9 -7
- package/dist/{chunk-ORKWLA33.js → chunk-OINYMLOV.js} +10 -2
- package/dist/chunk-POXT5OYN.js +66 -0
- package/dist/cli.js +17 -6
- package/dist/{export-CGSEUYZA.js → export-4BO6HCXP.js} +1 -1
- package/dist/{install-agents-OBDCWCPB.js → install-agents-WDBQBWMN.js} +4 -3
- package/dist/{refresh-SMJ2NGIW.js → refresh-S62AZ3QA.js} +15 -3
- package/dist/{tools-MIROTK2A.js → tools-ECHCPLCB.js} +111 -50
- package/dist/update-check-GQVDVT2N.js +14 -0
- package/package.json +1 -1
- package/dist/{status-VJDB75X2.js → status-2SEITNIE.js} +3 -3
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<a href="https://github.com/PDgit12/knit/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/PDgit12/knit/ci.yml?style=for-the-badge&label=CI&color=10b981" alt="CI" /></a>
|
|
4
4
|
<img src="https://img.shields.io/badge/license-MIT-3b82f6?style=for-the-badge" alt="license" />
|
|
5
5
|
<img src="https://img.shields.io/badge/node-%E2%89%A518-339933?style=for-the-badge&logo=node.js&logoColor=white" alt="node" />
|
|
6
|
-
<img src="https://img.shields.io/badge/tests-
|
|
6
|
+
<img src="https://img.shields.io/badge/tests-665%20passing-22c55e?style=for-the-badge" alt="tests" />
|
|
7
7
|
<img src="https://img.shields.io/badge/MCP%20tools-53-7c3aed?style=for-the-badge" alt="tools" />
|
|
8
8
|
</p>
|
|
9
9
|
|
|
@@ -452,6 +452,8 @@ 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.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
|
+
| **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. |
|
|
455
457
|
| **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. |
|
|
456
458
|
| **v0.11.1** | Audit-driven hardening · 3 CRITICAL (source_id path traversal, post-edit tsc shell injection, live calibration bug) + 10 HIGH fixes from a 5-agent audit, implemented in 3 parallel `knit_spawn_team_worktree` teams. HOOKS_VERSION 11 (auto-upgrades existing users). New `knit_delete_requirements` tool. Honest comparison vs mem0/Letta added. 53 tools, 636 tests. |
|
|
457
459
|
| **v0.11.0** | Verify Layer + auto-config foundation · mandatory `knit_verify_claim` REVIEW gate · post-edit diff verify + universal `tsc` check · drift detector · self-healing classifier (per-project calibration) · `knit_index_requirements` + `knit_generate_test_cases` (BM25 over long specs) · `knit_get_fingerprint` + `knit_infer_domains` + `knit_compose_template` (zero-config CLAUDE.md). 52 tools, 625 tests. |
|
|
@@ -2,15 +2,16 @@ import {
|
|
|
2
2
|
detectProjectRoot,
|
|
3
3
|
getBrain,
|
|
4
4
|
refreshBrain
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-I63UMEBF.js";
|
|
6
6
|
import "./chunk-HROSQ5MS.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-GATMQQK5.js";
|
|
8
|
+
import "./chunk-WKQHCLLO.js";
|
|
8
9
|
import "./chunk-MOOVNMIN.js";
|
|
9
10
|
import "./chunk-ST4X7LZT.js";
|
|
10
11
|
import "./chunk-M3YZOJNW.js";
|
|
12
|
+
import "./chunk-POXT5OYN.js";
|
|
11
13
|
import "./chunk-VB2TIR6L.js";
|
|
12
14
|
import "./chunk-7UFS67HP.js";
|
|
13
|
-
import "./chunk-WKQHCLLO.js";
|
|
14
15
|
import "./chunk-27TA2ZQZ.js";
|
|
15
16
|
export {
|
|
16
17
|
detectProjectRoot,
|
|
@@ -62,7 +62,7 @@ async function fetchAgent(name, opts = {}) {
|
|
|
62
62
|
throw new AgentFetchError(`Unknown agent: "${name}". Not in engram's registry.`);
|
|
63
63
|
}
|
|
64
64
|
const cachePath = agentsCacheFile(ref, cat, bare);
|
|
65
|
-
if (existsSync(cachePath)) {
|
|
65
|
+
if (!opts.refresh && existsSync(cachePath)) {
|
|
66
66
|
return readFileSync(cachePath, "utf-8");
|
|
67
67
|
}
|
|
68
68
|
if (process.env.KNIT_OFFLINE === "1" || process.env.ENGRAM_OFFLINE === "1") {
|
|
@@ -250,7 +250,7 @@ async function installAgentsForProject(rootPath, config, knowledge, knowledgeBas
|
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
try {
|
|
253
|
-
const baseMd = await fetchAgent(name, opts.refresh ? { ref: void 0 } : {});
|
|
253
|
+
const baseMd = await fetchAgent(name, opts.refresh ? { ref: void 0, refresh: true } : {});
|
|
254
254
|
const relevant = knowledgeBase ? selectRelevantLearnings(knowledgeBase.entries, name) : [];
|
|
255
255
|
const personalized = personalizeAgent(baseMd, {
|
|
256
256
|
config,
|
|
@@ -285,55 +285,6 @@ function agentsNeededByProject(config) {
|
|
|
285
285
|
return Array.from(names);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
// src/mcp/update-check.ts
|
|
289
|
-
var REGISTRY_DIST_TAGS_URL = "https://registry.npmjs.org/-/package/knit-mcp/dist-tags";
|
|
290
|
-
var FETCH_TIMEOUT_MS = 2e3;
|
|
291
|
-
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
292
|
-
var cachedLatest = null;
|
|
293
|
-
var lastCheckedAt = 0;
|
|
294
|
-
var inFlight = null;
|
|
295
|
-
function getCachedLatestVersion() {
|
|
296
|
-
if (Date.now() - lastCheckedAt > CACHE_TTL_MS) {
|
|
297
|
-
prewarmLatestVersion();
|
|
298
|
-
}
|
|
299
|
-
return cachedLatest;
|
|
300
|
-
}
|
|
301
|
-
function prewarmLatestVersion() {
|
|
302
|
-
if (inFlight) return;
|
|
303
|
-
if (Date.now() - lastCheckedAt < CACHE_TTL_MS && cachedLatest !== null) return;
|
|
304
|
-
inFlight = doFetch().finally(() => {
|
|
305
|
-
inFlight = null;
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
async function doFetch() {
|
|
309
|
-
const controller = new AbortController();
|
|
310
|
-
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
311
|
-
try {
|
|
312
|
-
const res = await fetch(REGISTRY_DIST_TAGS_URL, { signal: controller.signal });
|
|
313
|
-
if (!res.ok) return;
|
|
314
|
-
const data = await res.json();
|
|
315
|
-
if (typeof data.latest === "string" && data.latest.length > 0) {
|
|
316
|
-
cachedLatest = data.latest;
|
|
317
|
-
lastCheckedAt = Date.now();
|
|
318
|
-
}
|
|
319
|
-
} catch {
|
|
320
|
-
} finally {
|
|
321
|
-
clearTimeout(timeout);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
function isNewerVersion(latest, current) {
|
|
325
|
-
const parse = (v) => {
|
|
326
|
-
const stripped = v.replace(/[-+].*$/, "");
|
|
327
|
-
const parts = stripped.split(".").map((n) => parseInt(n, 10) || 0);
|
|
328
|
-
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
329
|
-
};
|
|
330
|
-
const [a1, a2, a3] = parse(latest);
|
|
331
|
-
const [b1, b2, b3] = parse(current);
|
|
332
|
-
if (a1 !== b1) return a1 > b1;
|
|
333
|
-
if (a2 !== b2) return a2 > b2;
|
|
334
|
-
return a3 > b3;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
288
|
// src/engine/sessions.ts
|
|
338
289
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, appendFileSync, readFileSync as readFileSync3, statSync, writeFileSync as writeFileSync3, renameSync } from "fs";
|
|
339
290
|
import { dirname as dirname2 } from "path";
|
|
@@ -466,9 +417,6 @@ function parseLine(line) {
|
|
|
466
417
|
|
|
467
418
|
export {
|
|
468
419
|
installAgentsForProject,
|
|
469
|
-
getCachedLatestVersion,
|
|
470
|
-
prewarmLatestVersion,
|
|
471
|
-
isNewerVersion,
|
|
472
420
|
appendSession,
|
|
473
421
|
searchSessions,
|
|
474
422
|
getRecentSessions,
|
|
@@ -4,9 +4,13 @@ import {
|
|
|
4
4
|
} from "./chunk-HROSQ5MS.js";
|
|
5
5
|
import {
|
|
6
6
|
installAgentsForProject,
|
|
7
|
-
prewarmLatestVersion,
|
|
8
7
|
pruneSessionsByAge
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-GATMQQK5.js";
|
|
9
|
+
import {
|
|
10
|
+
importFromMarkdown,
|
|
11
|
+
loadKnowledgeBaseSafe,
|
|
12
|
+
saveKnowledgeBase
|
|
13
|
+
} from "./chunk-WKQHCLLO.js";
|
|
10
14
|
import {
|
|
11
15
|
buildKnowledge,
|
|
12
16
|
buildReverseDependencies
|
|
@@ -17,6 +21,9 @@ import {
|
|
|
17
21
|
import {
|
|
18
22
|
readLearnings
|
|
19
23
|
} from "./chunk-M3YZOJNW.js";
|
|
24
|
+
import {
|
|
25
|
+
prewarmLatestVersion
|
|
26
|
+
} from "./chunk-POXT5OYN.js";
|
|
20
27
|
import {
|
|
21
28
|
persistScanResult,
|
|
22
29
|
scanIntegrations
|
|
@@ -26,11 +33,6 @@ import {
|
|
|
26
33
|
generateClaudeMd,
|
|
27
34
|
spliceKnitBlock
|
|
28
35
|
} from "./chunk-7UFS67HP.js";
|
|
29
|
-
import {
|
|
30
|
-
importFromMarkdown,
|
|
31
|
-
loadKnowledgeBaseSafe,
|
|
32
|
-
saveKnowledgeBase
|
|
33
|
-
} from "./chunk-WKQHCLLO.js";
|
|
34
36
|
import {
|
|
35
37
|
knowledgePath,
|
|
36
38
|
knowledgebasePath,
|
|
@@ -9,8 +9,16 @@ import { existsSync, mkdirSync, appendFileSync, readFileSync, statSync } from "f
|
|
|
9
9
|
import { dirname, basename } from "path";
|
|
10
10
|
function appendGlobalLearning(entry) {
|
|
11
11
|
const path = globalLearningsPath();
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
try {
|
|
13
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
14
|
+
appendFileSync(path, JSON.stringify(entry) + "\n", "utf-8");
|
|
15
|
+
} catch (err) {
|
|
16
|
+
process.stderr.write(
|
|
17
|
+
`[knit] global learning append failed at ${path}: ${err.message}
|
|
18
|
+
`
|
|
19
|
+
);
|
|
20
|
+
throw err;
|
|
21
|
+
}
|
|
14
22
|
}
|
|
15
23
|
function searchGlobalLearnings(query, limit = 10) {
|
|
16
24
|
const lines = readAllLines();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/mcp/update-check.ts
|
|
2
|
+
var REGISTRY_DIST_TAGS_URL = "https://registry.npmjs.org/-/package/knit-mcp/dist-tags";
|
|
3
|
+
var FETCH_TIMEOUT_MS = 2e3;
|
|
4
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
5
|
+
var cachedLatest = null;
|
|
6
|
+
var lastCheckedAt = 0;
|
|
7
|
+
var inFlight = null;
|
|
8
|
+
function getCachedLatestVersion() {
|
|
9
|
+
if (Date.now() - lastCheckedAt > CACHE_TTL_MS) {
|
|
10
|
+
prewarmLatestVersion();
|
|
11
|
+
}
|
|
12
|
+
return cachedLatest;
|
|
13
|
+
}
|
|
14
|
+
function prewarmLatestVersion() {
|
|
15
|
+
if (inFlight) return;
|
|
16
|
+
if (Date.now() - lastCheckedAt < CACHE_TTL_MS && cachedLatest !== null) return;
|
|
17
|
+
inFlight = doFetch().finally(() => {
|
|
18
|
+
inFlight = null;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function doFetch() {
|
|
22
|
+
const controller = new AbortController();
|
|
23
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
24
|
+
try {
|
|
25
|
+
const res = await fetch(REGISTRY_DIST_TAGS_URL, { signal: controller.signal });
|
|
26
|
+
if (!res.ok) return;
|
|
27
|
+
const data = await res.json();
|
|
28
|
+
if (typeof data.latest === "string" && data.latest.length > 0) {
|
|
29
|
+
cachedLatest = data.latest;
|
|
30
|
+
lastCheckedAt = Date.now();
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
} finally {
|
|
34
|
+
clearTimeout(timeout);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function isNewerVersion(latest, current) {
|
|
38
|
+
const parse = (v) => {
|
|
39
|
+
const stripped = v.replace(/[-+].*$/, "");
|
|
40
|
+
const parts = stripped.split(".").map((n) => parseInt(n, 10) || 0);
|
|
41
|
+
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
42
|
+
};
|
|
43
|
+
const [a1, a2, a3] = parse(latest);
|
|
44
|
+
const [b1, b2, b3] = parse(current);
|
|
45
|
+
if (a1 !== b1) return a1 > b1;
|
|
46
|
+
if (a2 !== b2) return a2 > b2;
|
|
47
|
+
return a3 > b3;
|
|
48
|
+
}
|
|
49
|
+
function __setCachedLatestForTests(version, checkedAtMs = Date.now()) {
|
|
50
|
+
cachedLatest = version;
|
|
51
|
+
lastCheckedAt = checkedAtMs;
|
|
52
|
+
inFlight = null;
|
|
53
|
+
}
|
|
54
|
+
function __resetUpdateCheckForTests() {
|
|
55
|
+
cachedLatest = null;
|
|
56
|
+
lastCheckedAt = 0;
|
|
57
|
+
inFlight = null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
getCachedLatestVersion,
|
|
62
|
+
prewarmLatestVersion,
|
|
63
|
+
isNewerVersion,
|
|
64
|
+
__setCachedLatestForTests,
|
|
65
|
+
__resetUpdateCheckForTests
|
|
66
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -20,10 +20,10 @@ async function runCLI() {
|
|
|
20
20
|
const gradient = (await import("gradient-string")).default;
|
|
21
21
|
const chalk = (await import("chalk")).default;
|
|
22
22
|
const { setupCommand } = await import("./setup-5TUUWLIJ.js");
|
|
23
|
-
const { statusCommand } = await import("./status-
|
|
24
|
-
const { refreshCommand } = await import("./refresh-
|
|
25
|
-
const { installAgentsCommand } = await import("./install-agents-
|
|
26
|
-
const { exportCommand } = await import("./export-
|
|
23
|
+
const { statusCommand } = await import("./status-2SEITNIE.js");
|
|
24
|
+
const { refreshCommand } = await import("./refresh-S62AZ3QA.js");
|
|
25
|
+
const { installAgentsCommand } = await import("./install-agents-WDBQBWMN.js");
|
|
26
|
+
const { exportCommand } = await import("./export-4BO6HCXP.js");
|
|
27
27
|
const { doctorCommand } = await import("./doctor-4DN2P2JR.js");
|
|
28
28
|
const ENGRAM_GRADIENT = gradient(["#7c3aed", "#2563eb", "#06b6d4"]);
|
|
29
29
|
const banner = `
|
|
@@ -98,13 +98,24 @@ 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-
|
|
101
|
+
const { getBrain, detectProjectRoot, refreshBrain } = await import("./cache-7HSMIYDJ.js");
|
|
102
|
+
const { getActiveToolDefinitionsForBrain, handleToolCall } = await import("./tools-ECHCPLCB.js");
|
|
103
103
|
const { buildInstructions } = await import("./instructions-JARSXQPO.js");
|
|
104
104
|
const { registerToolsListChangedNotifier } = await import("./notifier-4L27HKHI.js");
|
|
105
105
|
const { loadScanResult } = await import("./integration-scanner-LBD2PIZ3.js");
|
|
106
|
+
const { prewarmLatestVersion, getCachedLatestVersion, isNewerVersion } = await import("./update-check-GQVDVT2N.js");
|
|
106
107
|
const ROOT_PATH = detectProjectRoot();
|
|
107
108
|
const PER_PROJECT_INSTRUCTIONS = buildInstructions(loadScanResult(ROOT_PATH));
|
|
109
|
+
prewarmLatestVersion();
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
const latest = getCachedLatestVersion();
|
|
112
|
+
if (latest && isNewerVersion(latest, VERSION)) {
|
|
113
|
+
process.stderr.write(
|
|
114
|
+
`[knit] update available: v${VERSION} installed, v${latest} on npm \u2014 restart Claude Code to upgrade (clear npx cache if needed: \`rm -rf ~/.npm/_npx/\`). Changelog: https://github.com/PDgit12/knit/blob/main/CHANGELOG.md
|
|
115
|
+
`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}, 250);
|
|
108
119
|
const server = new Server(
|
|
109
120
|
{ name: "knit-brain", version: VERSION },
|
|
110
121
|
{
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getBrain
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-I63UMEBF.js";
|
|
4
4
|
import "./chunk-HROSQ5MS.js";
|
|
5
5
|
import {
|
|
6
6
|
installAgentsForProject
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GATMQQK5.js";
|
|
8
|
+
import "./chunk-WKQHCLLO.js";
|
|
8
9
|
import "./chunk-MOOVNMIN.js";
|
|
9
10
|
import "./chunk-ST4X7LZT.js";
|
|
10
11
|
import "./chunk-M3YZOJNW.js";
|
|
12
|
+
import "./chunk-POXT5OYN.js";
|
|
11
13
|
import "./chunk-VB2TIR6L.js";
|
|
12
14
|
import "./chunk-7UFS67HP.js";
|
|
13
|
-
import "./chunk-WKQHCLLO.js";
|
|
14
15
|
import "./chunk-27TA2ZQZ.js";
|
|
15
16
|
|
|
16
17
|
// src/commands/install-agents.ts
|
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
findFalsePositives
|
|
9
9
|
} from "./chunk-M3YZOJNW.js";
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
KNIT_MARKER_START,
|
|
12
|
+
generateClaudeMd,
|
|
13
|
+
spliceKnitBlock
|
|
12
14
|
} from "./chunk-7UFS67HP.js";
|
|
13
15
|
import {
|
|
14
16
|
knowledgePath,
|
|
@@ -57,9 +59,19 @@ async function refreshCommand(targetDir) {
|
|
|
57
59
|
tokenOptimization: "standard"
|
|
58
60
|
};
|
|
59
61
|
const genSpinner = ora({ text: chalk.dim("Regenerating CLAUDE.md..."), spinner: "dots" }).start();
|
|
60
|
-
|
|
62
|
+
const claudeMdPath = join(rootPath, "CLAUDE.md");
|
|
63
|
+
const newBlock = generateClaudeMd(config, knowledge, falsePositives);
|
|
64
|
+
const existing = existsSync(claudeMdPath) ? readFileSync(claudeMdPath, "utf-8") : "";
|
|
65
|
+
if (existing && existing.includes(KNIT_MARKER_START)) {
|
|
66
|
+
const { content } = spliceKnitBlock(existing, newBlock);
|
|
67
|
+
writeFileSync(claudeMdPath, content, "utf-8");
|
|
68
|
+
} else if (!existing) {
|
|
69
|
+
writeFileSync(claudeMdPath, newBlock, "utf-8");
|
|
70
|
+
} else {
|
|
71
|
+
genSpinner.warn(chalk.yellow("CLAUDE.md is user-curated (no Knit markers). Skipping write to avoid clobber."));
|
|
72
|
+
}
|
|
61
73
|
writeFileSync(knowledgePath(rootPath), JSON.stringify(knowledge, null, 2), "utf-8");
|
|
62
|
-
genSpinner.succeed(chalk.dim("CLAUDE.md + knowledge.json updated"));
|
|
74
|
+
if (genSpinner.isSpinning) genSpinner.succeed(chalk.dim("CLAUDE.md + knowledge.json updated"));
|
|
63
75
|
console.log();
|
|
64
76
|
console.log(chalk.bold(" Refresh complete"));
|
|
65
77
|
if (falsePositives.length > 0) {
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appendGlobalLearning,
|
|
3
|
+
buildGlobalLearning,
|
|
4
|
+
getRecentGlobalLearnings,
|
|
5
|
+
loadAllGlobalLearnings,
|
|
6
|
+
searchGlobalLearnings
|
|
7
|
+
} from "./chunk-OINYMLOV.js";
|
|
1
8
|
import {
|
|
2
9
|
notifyToolsListChanged
|
|
3
10
|
} from "./chunk-WMESQUZU.js";
|
|
@@ -9,32 +16,13 @@ import {
|
|
|
9
16
|
} from "./chunk-UTVFELXS.js";
|
|
10
17
|
import {
|
|
11
18
|
appendSession,
|
|
12
|
-
getCachedLatestVersion,
|
|
13
19
|
getRecentSessions,
|
|
14
20
|
installAgentsForProject,
|
|
15
|
-
isNewerVersion,
|
|
16
21
|
loadAllSessions,
|
|
17
22
|
pruneSessionsByAge,
|
|
18
23
|
searchSessions,
|
|
19
24
|
sessionCount
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import {
|
|
22
|
-
scanProject,
|
|
23
|
-
scanProjectFingerprint
|
|
24
|
-
} from "./chunk-ST4X7LZT.js";
|
|
25
|
-
import {
|
|
26
|
-
loadScanResult,
|
|
27
|
-
persistScanResult,
|
|
28
|
-
scanIntegrations
|
|
29
|
-
} from "./chunk-VB2TIR6L.js";
|
|
30
|
-
import "./chunk-7UFS67HP.js";
|
|
31
|
-
import {
|
|
32
|
-
appendGlobalLearning,
|
|
33
|
-
buildGlobalLearning,
|
|
34
|
-
getRecentGlobalLearnings,
|
|
35
|
-
loadAllGlobalLearnings,
|
|
36
|
-
searchGlobalLearnings
|
|
37
|
-
} from "./chunk-ORKWLA33.js";
|
|
25
|
+
} from "./chunk-GATMQQK5.js";
|
|
38
26
|
import {
|
|
39
27
|
addEntry,
|
|
40
28
|
bumpClassificationTier,
|
|
@@ -45,6 +33,20 @@ import {
|
|
|
45
33
|
recordCacheHit,
|
|
46
34
|
saveKnowledgeBase
|
|
47
35
|
} from "./chunk-WKQHCLLO.js";
|
|
36
|
+
import {
|
|
37
|
+
scanProject,
|
|
38
|
+
scanProjectFingerprint
|
|
39
|
+
} from "./chunk-ST4X7LZT.js";
|
|
40
|
+
import {
|
|
41
|
+
getCachedLatestVersion,
|
|
42
|
+
isNewerVersion
|
|
43
|
+
} from "./chunk-POXT5OYN.js";
|
|
44
|
+
import {
|
|
45
|
+
loadScanResult,
|
|
46
|
+
persistScanResult,
|
|
47
|
+
scanIntegrations
|
|
48
|
+
} from "./chunk-VB2TIR6L.js";
|
|
49
|
+
import "./chunk-7UFS67HP.js";
|
|
48
50
|
import {
|
|
49
51
|
calibrationPath,
|
|
50
52
|
canonicalRepoRoot,
|
|
@@ -395,7 +397,48 @@ function tools(_) {
|
|
|
395
397
|
| \`knit_define_team\` | Create a custom team. |
|
|
396
398
|
| \`knit_start_team_review\` | Start a parallel review board. |
|
|
397
399
|
| \`knit_post_team_findings\` | Each team posts to the shared board. |
|
|
398
|
-
| \`knit_get_board_summary\` | Cross-team findings, severity-gated.
|
|
400
|
+
| \`knit_get_board_summary\` | Cross-team findings, severity-gated. |
|
|
401
|
+
| \`knit_spawn_team_worktree\` | Spawn a git worktree for a team to do isolated parallel writes. |
|
|
402
|
+
| \`knit_list_team_worktrees\` | List active team worktrees. |
|
|
403
|
+
| \`knit_finalize_team_worktree\` | Merge or discard a team worktree branch. |
|
|
404
|
+
|
|
405
|
+
**Cross-project memory** (v0.4+):
|
|
406
|
+
| Tool | Use when |
|
|
407
|
+
|------|----------|
|
|
408
|
+
| \`knit_record_global_learning\` | Cross-project insight (applies beyond current repo). |
|
|
409
|
+
| \`knit_search_global_learnings\` | Search learnings across all your projects. |
|
|
410
|
+
| \`knit_reflect\` | Surface candidate patterns from session history. |
|
|
411
|
+
| \`knit_get_suggestions\` | Adaptive warnings derived from past patterns for given domains. |
|
|
412
|
+
| \`knit_install_agent\` | Fetch a specialized VoltAgent on demand. |
|
|
413
|
+
| \`knit_prune_sessions\` | Garbage-collect old session log entries. |
|
|
414
|
+
| \`knit_consolidate_learnings\` | Promote stable patterns to consolidated tier. |
|
|
415
|
+
| \`knit_get_learning\` | Fetch a single learning by id. |
|
|
416
|
+
|
|
417
|
+
**Feature gating + protocol guard** (v0.5+):
|
|
418
|
+
| Tool | Use when |
|
|
419
|
+
|------|----------|
|
|
420
|
+
| \`knit_list_features\` | See which tools are hidden behind tier gates and how to enable them. |
|
|
421
|
+
| \`knit_enable_feature\` / \`knit_disable_feature\` | Toggle a feature on/off for this project. |
|
|
422
|
+
| \`knit_set_protocol_strictness\` / \`knit_get_protocol_strictness\` | off / warn / block \u2014 controls Protocol Guard hook. |
|
|
423
|
+
| \`knit_scan_integrations\` | Detect external integrations the project uses. |
|
|
424
|
+
| \`knit_compounding_metrics\` | Per-session token + hit-rate metrics. |
|
|
425
|
+
| \`knit_get_metrics_history\` | History of compounding metrics for trend lines. |
|
|
426
|
+
| \`knit_verify_claim\` | Cheap fact-check against the knowledge graph before quoting code. |
|
|
427
|
+
|
|
428
|
+
**Self-healing classifier** (v0.11):
|
|
429
|
+
| Tool | Use when |
|
|
430
|
+
|------|----------|
|
|
431
|
+
| \`knit_get_calibration\` / \`knit_reset_calibration\` | Inspect or reset per-project classifier tuning. |
|
|
432
|
+
| \`knit_get_fingerprint\` | Project fingerprint used for shape-aware tier gating. |
|
|
433
|
+
| \`knit_infer_domains\` | Auto-derive domain tags from files. |
|
|
434
|
+
| \`knit_compose_template\` | Compose a generated artifact from a template. |
|
|
435
|
+
|
|
436
|
+
**Requirements ingestion** (v0.11):
|
|
437
|
+
| Tool | Use when |
|
|
438
|
+
|------|----------|
|
|
439
|
+
| \`knit_index_requirements\` | Chunk + BM25-index a long spec for retrieval. |
|
|
440
|
+
| \`knit_generate_test_cases\` | Retrieve relevant chunks for a feature query (token-bounded). |
|
|
441
|
+
| \`knit_list_requirements\` / \`knit_delete_requirements\` | Manage indexed sources. |`;
|
|
399
442
|
}
|
|
400
443
|
|
|
401
444
|
// src/engine/worktrees.ts
|
|
@@ -639,7 +682,6 @@ function classifyOrigin(supporting) {
|
|
|
639
682
|
else global = true;
|
|
640
683
|
if (local && global) return "mixed";
|
|
641
684
|
}
|
|
642
|
-
if (local && global) return "mixed";
|
|
643
685
|
return local ? "local" : "global";
|
|
644
686
|
}
|
|
645
687
|
function getAdaptiveSuggestions(kb, taskDomains) {
|
|
@@ -1487,6 +1529,9 @@ function chunkRequirements(content, minChars = 50) {
|
|
|
1487
1529
|
return chunks;
|
|
1488
1530
|
}
|
|
1489
1531
|
function saveSource(rootPath, source) {
|
|
1532
|
+
if (typeof source.sourceId !== "string" || !/^[A-Za-z0-9._-]{1,80}$/.test(source.sourceId)) {
|
|
1533
|
+
throw new Error(`[knit] saveSource: invalid sourceId "${source.sourceId}"`);
|
|
1534
|
+
}
|
|
1490
1535
|
const path = requirementSourcePath(rootPath, source.sourceId);
|
|
1491
1536
|
mkdirSync5(dirname5(path), { recursive: true });
|
|
1492
1537
|
const tmp = `${path}.tmp`;
|
|
@@ -1494,6 +1539,7 @@ function saveSource(rootPath, source) {
|
|
|
1494
1539
|
renameSync3(tmp, path);
|
|
1495
1540
|
}
|
|
1496
1541
|
function loadSource(rootPath, sourceId) {
|
|
1542
|
+
if (typeof sourceId !== "string" || !/^[A-Za-z0-9._-]{1,80}$/.test(sourceId)) return null;
|
|
1497
1543
|
const path = requirementSourcePath(rootPath, sourceId);
|
|
1498
1544
|
if (!existsSync5(path)) return null;
|
|
1499
1545
|
try {
|
|
@@ -1883,8 +1929,8 @@ function handleSearchLearnings(params, brain) {
|
|
|
1883
1929
|
const limit = Math.max(1, Math.min(50, parseInt(params.limit || "10", 10) || 10));
|
|
1884
1930
|
if (!query && domains.length === 0) {
|
|
1885
1931
|
return JSON.stringify({
|
|
1932
|
+
status: "error",
|
|
1886
1933
|
error: "Provide either query (BM25 free-text) or domains (tag filter), or both. query=auth domains=#api filters BM25 results to entries tagged #api.",
|
|
1887
|
-
query: [],
|
|
1888
1934
|
results: [],
|
|
1889
1935
|
count: 0
|
|
1890
1936
|
});
|
|
@@ -2889,9 +2935,7 @@ function handleGetLearning(params, brain) {
|
|
|
2889
2935
|
if (!id) return errorResponse("id parameter is required");
|
|
2890
2936
|
const entry = brain.knowledgeBase.entries.find((e) => e.id === id);
|
|
2891
2937
|
if (!entry) {
|
|
2892
|
-
return
|
|
2893
|
-
error: `No learning with id="${id}". List active ones via knit_search_learnings (default returns id + summary).`
|
|
2894
|
-
});
|
|
2938
|
+
return errorResponse(`No learning with id="${id}". List active ones via knit_search_learnings (default returns id + summary).`);
|
|
2895
2939
|
}
|
|
2896
2940
|
recordCacheHit(brain.knowledgeBase);
|
|
2897
2941
|
return JSON.stringify({
|
|
@@ -2914,11 +2958,11 @@ function handleRecordLearning(params, brain) {
|
|
|
2914
2958
|
const entry = {
|
|
2915
2959
|
date,
|
|
2916
2960
|
summary: redactSecrets(params.summary || "Untitled learning"),
|
|
2917
|
-
domains: (params.domains || "general").split(",").map((d) => d.trim()),
|
|
2961
|
+
domains: (params.domains || "general").split(",").map((d) => redactSecrets(d.trim())),
|
|
2918
2962
|
approach: redactSecrets(params.approach || ""),
|
|
2919
2963
|
outcome: ["success", "partial", "failure"].includes(params.outcome) ? params.outcome : "success",
|
|
2920
2964
|
lesson: redactSecrets(params.lesson || ""),
|
|
2921
|
-
tags: (params.tags || "").split(/\s+/).filter((t) => t.startsWith("#"))
|
|
2965
|
+
tags: (params.tags || "").split(/\s+/).filter((t) => t.startsWith("#")).map((t) => redactSecrets(t))
|
|
2922
2966
|
};
|
|
2923
2967
|
addEntry(brain.knowledgeBase, entry);
|
|
2924
2968
|
saveKnowledgeBase(knowledgebasePath(brain.rootPath), brain.knowledgeBase);
|
|
@@ -2946,7 +2990,7 @@ function handleRecordLearning(params, brain) {
|
|
|
2946
2990
|
}
|
|
2947
2991
|
function handleRecordFalsePositive(params, brain) {
|
|
2948
2992
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2949
|
-
const tags = [...(params.tags || "").split(/\s+/).filter((t) => t.startsWith("#")), "#false-positive"];
|
|
2993
|
+
const tags = [...(params.tags || "").split(/\s+/).filter((t) => t.startsWith("#")).map((t) => redactSecrets(t)), "#false-positive"];
|
|
2950
2994
|
const entry = {
|
|
2951
2995
|
date,
|
|
2952
2996
|
summary: redactSecrets(params.summary || "Untitled FP"),
|
|
@@ -3085,7 +3129,8 @@ function handleIndexRequirements(params, brain) {
|
|
|
3085
3129
|
return JSON.stringify({ status: "error", error: "Invalid source_id \u2014 must be 1-80 chars, alphanumeric + . _ - only" });
|
|
3086
3130
|
}
|
|
3087
3131
|
const sourceId = userSourceId || slugifySourceId(filePath);
|
|
3088
|
-
const
|
|
3132
|
+
const rawLabel = (params.label || "").trim();
|
|
3133
|
+
const label = rawLabel ? redactSecrets(rawLabel) : void 0;
|
|
3089
3134
|
const source = {
|
|
3090
3135
|
sourceId,
|
|
3091
3136
|
sourcePath: filePath,
|
|
@@ -3142,8 +3187,13 @@ function handleGenerateTestCases(params, brain) {
|
|
|
3142
3187
|
if (s) sourcesToSearch.push(s);
|
|
3143
3188
|
}
|
|
3144
3189
|
}
|
|
3190
|
+
const MAX_RESPONSE_BYTES = 100 * 1024;
|
|
3145
3191
|
const hits = retrieveTopChunks(sourcesToSearch, feature, topN);
|
|
3146
|
-
|
|
3192
|
+
let contextBytes = hits.reduce((s, h) => s + Buffer.byteLength(h.chunk.text, "utf-8"), 0);
|
|
3193
|
+
while (contextBytes > MAX_RESPONSE_BYTES && hits.length > 1) {
|
|
3194
|
+
const dropped = hits.pop();
|
|
3195
|
+
contextBytes -= Buffer.byteLength(dropped.chunk.text, "utf-8");
|
|
3196
|
+
}
|
|
3147
3197
|
const totalBytes = sourcesToSearch.reduce((s, src) => s + src.sourceBytes, 0);
|
|
3148
3198
|
const reductionPct = totalBytes > 0 ? Math.round(100 - contextBytes / totalBytes * 100) : 0;
|
|
3149
3199
|
return JSON.stringify({
|
|
@@ -3581,6 +3631,16 @@ function handleLoadSession(params, brain) {
|
|
|
3581
3631
|
if (wantAll || include.has("patterns")) {
|
|
3582
3632
|
patterns = reflect(brain.knowledgeBase).slice(0, 3).map((p) => ({ type: p.type, description: p.description, confidence: p.confidence }));
|
|
3583
3633
|
}
|
|
3634
|
+
let updateAvailable;
|
|
3635
|
+
const cachedLatest = getCachedLatestVersion();
|
|
3636
|
+
if (cachedLatest && isNewerVersion(cachedLatest, VERSION)) {
|
|
3637
|
+
updateAvailable = {
|
|
3638
|
+
current: VERSION,
|
|
3639
|
+
latest: cachedLatest,
|
|
3640
|
+
upgrade: "Restart Claude Code (quit fully + reopen) to spawn a fresh MCP. If npx serves cache, run: rm -rf ~/.npm/_npx/$(ls ~/.npm/_npx | head -1) then reopen.",
|
|
3641
|
+
changelog: "https://github.com/PDgit12/knit/blob/main/CHANGELOG.md"
|
|
3642
|
+
};
|
|
3643
|
+
}
|
|
3584
3644
|
const response = {
|
|
3585
3645
|
session_context: {
|
|
3586
3646
|
last_session: lastSession,
|
|
@@ -3598,6 +3658,7 @@ function handleLoadSession(params, brain) {
|
|
|
3598
3658
|
...metrics !== void 0 ? { metrics } : {},
|
|
3599
3659
|
...recentSessions !== void 0 ? { recent_sessions: recentSessions } : {}
|
|
3600
3660
|
},
|
|
3661
|
+
...updateAvailable ? { update_available: updateAvailable } : {},
|
|
3601
3662
|
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."
|
|
3602
3663
|
};
|
|
3603
3664
|
return JSON.stringify(response);
|
|
@@ -3611,7 +3672,7 @@ function handleSaveSessionSummary(params, brain) {
|
|
|
3611
3672
|
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
3612
3673
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3613
3674
|
summary: redactSecrets((params.summary || "").slice(0, 500)),
|
|
3614
|
-
tags: (params.tags || "").split(/\s+/).filter((t) => t.startsWith("#")),
|
|
3675
|
+
tags: (params.tags || "").split(/\s+/).filter((t) => t.startsWith("#")).map((t) => redactSecrets(t)),
|
|
3615
3676
|
outcome,
|
|
3616
3677
|
filesTouched: params.files_touched ? params.files_touched.split(",").map((f) => f.trim()).filter(Boolean) : void 0,
|
|
3617
3678
|
domainsTouched: params.domains ? params.domains.split(",").map((d) => d.trim()).filter(Boolean) : void 0
|
|
@@ -3813,32 +3874,32 @@ function getToolDefinitions() {
|
|
|
3813
3874
|
// ── Query (read the brain) ───────────────────────────────────
|
|
3814
3875
|
{
|
|
3815
3876
|
name: "knit_query_imports",
|
|
3816
|
-
description: "Reverse deps
|
|
3877
|
+
description: "[GRAPH] Reverse deps \u2014 WHO IMPORTS this file. Use to find blast radius before editing.",
|
|
3817
3878
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
3818
3879
|
},
|
|
3819
3880
|
{
|
|
3820
3881
|
name: "knit_query_dependents",
|
|
3821
|
-
description: "Forward deps
|
|
3882
|
+
description: "[GRAPH] Forward deps \u2014 WHAT THIS FILE IMPORTS. Opposite of knit_query_imports (who imports it).",
|
|
3822
3883
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
3823
3884
|
},
|
|
3824
3885
|
{
|
|
3825
3886
|
name: "knit_query_exports",
|
|
3826
|
-
description: "Exports from a file: functions, classes, types, constants.",
|
|
3887
|
+
description: "[GRAPH] Exports from a file: functions, classes, types, constants. Use when verifying a claim or finding the canonical definition.",
|
|
3827
3888
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
3828
3889
|
},
|
|
3829
3890
|
{
|
|
3830
3891
|
name: "knit_query_tests",
|
|
3831
|
-
description: 'Tests
|
|
3892
|
+
description: '[GRAPH] Tests covering a file, OR list all untested files with filter="untested". Use before declaring "tested" on changed code.',
|
|
3832
3893
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path (optional)." }, filter: { type: "string", description: '"untested" to list all untested files.' } } }
|
|
3833
3894
|
},
|
|
3834
3895
|
{
|
|
3835
3896
|
name: "knit_find_fanout",
|
|
3836
|
-
description: "High-fanout files \u2014 imported by many others.",
|
|
3897
|
+
description: "[GRAPH] High-fanout files \u2014 imported by many others. Editing these is high-risk; surfaces in classify_task risk_tier.",
|
|
3837
3898
|
inputSchema: { type: "object", properties: { min_importers: { type: "string", description: "Minimum importers to qualify (default: 3)." } } }
|
|
3838
3899
|
},
|
|
3839
3900
|
{
|
|
3840
3901
|
name: "knit_search_learnings",
|
|
3841
|
-
description:
|
|
3902
|
+
description: "[MEMORY] Search THIS PROJECT's learnings. Use BEFORE re-investigating. Companions: knit_search_global_learnings (cross-project), knit_search_sessions (past tasks). BM25 + graph boost via files=.",
|
|
3842
3903
|
inputSchema: {
|
|
3843
3904
|
type: "object",
|
|
3844
3905
|
properties: {
|
|
@@ -3862,7 +3923,7 @@ function getToolDefinitions() {
|
|
|
3862
3923
|
// ── Update (write to the brain) ──────────────────────────────
|
|
3863
3924
|
{
|
|
3864
3925
|
name: "knit_classify_task",
|
|
3865
|
-
description: "
|
|
3926
|
+
description: "[PROTOCOL] BEFORE any Edit/Write. Returns risk_tier (drives plan mode), scope_tier (drives phases), change_kind, auto_plan_mode. Optional context_budget_remaining (0-100) downgrades gracefully.",
|
|
3866
3927
|
inputSchema: { type: "object", properties: { files_to_touch: { type: "string", description: 'Comma-separated files, or "unknown" for new projects.' }, description: { type: "string", description: "Brief task description." }, verbose: { type: "string", description: '"true" to include reasoning + cross_domain_ripple + files_count (debug fields).' }, context_budget_remaining: { type: "string", description: "Integer 0\u2013100 \u2014 percent of host agent context window remaining. <30 triggers scope downgrade + skips OPTIMIZE phase. Defaults to 100." } }, required: ["files_to_touch"] }
|
|
3867
3928
|
},
|
|
3868
3929
|
{
|
|
@@ -3872,7 +3933,7 @@ function getToolDefinitions() {
|
|
|
3872
3933
|
},
|
|
3873
3934
|
{
|
|
3874
3935
|
name: "knit_record_learning",
|
|
3875
|
-
description: "Record a non-obvious
|
|
3936
|
+
description: "[MEMORY-WRITE] Record a non-obvious THIS-PROJECT insight. Skip substring repeats. For cross-project: knit_record_global_learning. For FPs: knit_record_false_positive.",
|
|
3876
3937
|
inputSchema: { type: "object", properties: { summary: { type: "string", description: "One-line summary." }, domains: { type: "string", description: "Comma-separated domains." }, approach: { type: "string", description: "What approach was taken." }, outcome: { type: "string", description: "success | partial | failure." }, lesson: { type: "string", description: "What to repeat or avoid." }, tags: { type: "string", description: 'Space-separated tags (e.g. "#api #auth").' } }, required: ["summary", "lesson", "tags"] }
|
|
3877
3938
|
},
|
|
3878
3939
|
{
|
|
@@ -3927,7 +3988,7 @@ function getToolDefinitions() {
|
|
|
3927
3988
|
},
|
|
3928
3989
|
{
|
|
3929
3990
|
name: "knit_save_handoff",
|
|
3930
|
-
description: "Save state
|
|
3991
|
+
description: "[END OF SESSION \u2014 UNFINISHED] Save state when work is incomplete or context degraded. failed_attempts is the load-bearing field. For finished work use knit_save_session_summary.",
|
|
3931
3992
|
inputSchema: { type: "object", properties: { goal: { type: "string", description: "What we're trying to accomplish." }, current_state: { type: "string", description: "Where we are now." }, files_in_flight: { type: "string", description: "Files being modified." }, what_changed: { type: "string", description: "Commits and edits." }, failed_attempts: { type: "string", description: "What was tried and why it failed." }, decisions_made: { type: "string", description: "Important choices." }, next_step: { type: "string", description: "ONE most important next thing." } }, required: ["goal", "current_state", "failed_attempts", "next_step"] }
|
|
3932
3993
|
},
|
|
3933
3994
|
{
|
|
@@ -3978,12 +4039,12 @@ function getToolDefinitions() {
|
|
|
3978
4039
|
// ── Session memory ───────────────────────────────────────────
|
|
3979
4040
|
{
|
|
3980
4041
|
name: "knit_load_session",
|
|
3981
|
-
description: "Call at session start. Returns handoff, top learnings,
|
|
4042
|
+
description: "[PROTOCOL FIRST] Call once at session start. Returns handoff, top learnings, FPs, update_available. Opt in via include=patterns,teams,metrics,recent_sessions,full_learnings,all.",
|
|
3982
4043
|
inputSchema: { type: "object", properties: { include: { type: "string", description: "Comma-separated optional sections." } } }
|
|
3983
4044
|
},
|
|
3984
4045
|
{
|
|
3985
4046
|
name: "knit_save_session_summary",
|
|
3986
|
-
description: "
|
|
4047
|
+
description: "[END OF SESSION] Record a session summary so future knit_search_sessions can find this work. Pair with knit_save_handoff when work is unfinished.",
|
|
3987
4048
|
inputSchema: {
|
|
3988
4049
|
type: "object",
|
|
3989
4050
|
properties: {
|
|
@@ -4008,7 +4069,7 @@ function getToolDefinitions() {
|
|
|
4008
4069
|
},
|
|
4009
4070
|
{
|
|
4010
4071
|
name: "knit_search_sessions",
|
|
4011
|
-
description: '
|
|
4072
|
+
description: '[MEMORY] "Have I done this task before?" \u2014 search past SESSION summaries. Different from knit_search_learnings (lessons) and knit_search_global_learnings (cross-project).',
|
|
4012
4073
|
inputSchema: {
|
|
4013
4074
|
type: "object",
|
|
4014
4075
|
properties: {
|
|
@@ -4055,7 +4116,7 @@ function getToolDefinitions() {
|
|
|
4055
4116
|
// ── Cross-project learnings (Model C — global pool) ─────────
|
|
4056
4117
|
{
|
|
4057
4118
|
name: "knit_record_global_learning",
|
|
4058
|
-
description: "
|
|
4119
|
+
description: "[MEMORY-WRITE] Record a learning that generalizes across MULTIPLE projects. Sparingly \u2014 most learnings are project-specific. Companion: knit_record_learning (this project only).",
|
|
4059
4120
|
inputSchema: {
|
|
4060
4121
|
type: "object",
|
|
4061
4122
|
properties: {
|
|
@@ -4069,7 +4130,7 @@ function getToolDefinitions() {
|
|
|
4069
4130
|
},
|
|
4070
4131
|
{
|
|
4071
4132
|
name: "knit_search_global_learnings",
|
|
4072
|
-
description: "Search
|
|
4133
|
+
description: "[MEMORY] Search learnings ACROSS ALL projects on this machine. Use when starting a new domain. Companion: knit_search_learnings (this project only).",
|
|
4073
4134
|
inputSchema: {
|
|
4074
4135
|
type: "object",
|
|
4075
4136
|
properties: {
|
|
@@ -4166,12 +4227,12 @@ function getToolDefinitions() {
|
|
|
4166
4227
|
},
|
|
4167
4228
|
{
|
|
4168
4229
|
name: "knit_verify_claim",
|
|
4169
|
-
description: 'Fact-check one claim
|
|
4230
|
+
description: '[REVIEW] Fact-check one claim before LEARN on standard/complex scope. Patterns: "A imports B", "X exports Y", "A is tested by B", "X exists". Verdict: verified | contradicted | unparseable.',
|
|
4170
4231
|
inputSchema: { type: "object", properties: { claim: { type: "string", description: "One claim about the codebase to verify." } }, required: ["claim"] }
|
|
4171
4232
|
},
|
|
4172
4233
|
{
|
|
4173
4234
|
name: "knit_get_learning",
|
|
4174
|
-
description: "
|
|
4235
|
+
description: "[MEMORY] Expand ONE learning by id (from a prior knit_search_learnings hit). Hierarchical retrieval \u2014 search returns headlines, this fetches details. Saves tokens vs. dumping full bodies.",
|
|
4175
4236
|
inputSchema: { type: "object", properties: { id: { type: "string", description: "Learning id from a prior knit_search_learnings result." } }, required: ["id"] }
|
|
4176
4237
|
},
|
|
4177
4238
|
{
|
|
@@ -4260,7 +4321,7 @@ function handleToolCall(toolName, params, brain) {
|
|
|
4260
4321
|
const allowAbsolute = toolName === "knit_index_requirements";
|
|
4261
4322
|
const bad = decoded.includes("..") || decoded.includes("\0") || !allowAbsolute && (decoded.startsWith("/") || /^[A-Za-z]:\//.test(decoded));
|
|
4262
4323
|
if (bad) {
|
|
4263
|
-
return JSON.stringify({ error: "Invalid file path \u2014 no traversal or absolute paths allowed" });
|
|
4324
|
+
return JSON.stringify({ status: "error", error: "Invalid file path \u2014 no traversal or absolute paths allowed" });
|
|
4264
4325
|
}
|
|
4265
4326
|
params.file_path = decoded;
|
|
4266
4327
|
}
|
|
@@ -4271,7 +4332,7 @@ function handleToolCall(toolName, params, brain) {
|
|
|
4271
4332
|
}
|
|
4272
4333
|
const handler = handlers[toolName];
|
|
4273
4334
|
if (!handler) {
|
|
4274
|
-
return JSON.stringify({ error: `Unknown tool: ${toolName}` });
|
|
4335
|
+
return JSON.stringify({ status: "error", error: `Unknown tool: ${toolName}` });
|
|
4275
4336
|
}
|
|
4276
4337
|
return handler(params, brain);
|
|
4277
4338
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__resetUpdateCheckForTests,
|
|
3
|
+
__setCachedLatestForTests,
|
|
4
|
+
getCachedLatestVersion,
|
|
5
|
+
isNewerVersion,
|
|
6
|
+
prewarmLatestVersion
|
|
7
|
+
} from "./chunk-POXT5OYN.js";
|
|
8
|
+
export {
|
|
9
|
+
__resetUpdateCheckForTests,
|
|
10
|
+
__setCachedLatestForTests,
|
|
11
|
+
getCachedLatestVersion,
|
|
12
|
+
isNewerVersion,
|
|
13
|
+
prewarmLatestVersion
|
|
14
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
readLearnings
|
|
3
|
-
} from "./chunk-M3YZOJNW.js";
|
|
4
1
|
import {
|
|
5
2
|
getKBSummary,
|
|
6
3
|
getStaleEntries,
|
|
7
4
|
getTopEntries,
|
|
8
5
|
loadKnowledgeBase
|
|
9
6
|
} from "./chunk-WKQHCLLO.js";
|
|
7
|
+
import {
|
|
8
|
+
readLearnings
|
|
9
|
+
} from "./chunk-M3YZOJNW.js";
|
|
10
10
|
import {
|
|
11
11
|
knowledgePath,
|
|
12
12
|
knowledgebasePath,
|