knit-mcp 0.8.0 → 0.9.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/dist/{cache-RRKF2IZ5.js → cache-JSN6ETUF.js} +6 -4
- package/dist/{chunk-WN4AQFMO.js → chunk-4K4FHOKE.js} +165 -16
- package/dist/chunk-5NCSZYRJ.js +66 -0
- package/dist/{chunk-JJ367RK5.js → chunk-7PPC6IG6.js} +1 -163
- package/dist/{chunk-TSIXVZCT.js → chunk-B2KZ5UR7.js} +1 -1
- package/dist/{chunk-N7R4P42P.js → chunk-BU3VHX3W.js} +9 -203
- package/dist/chunk-FLNV2IQC.js +201 -0
- package/dist/chunk-KLNUEE3O.js +164 -0
- package/dist/{chunk-SBJMHDBM.js → chunk-XFS2XGZI.js} +4 -0
- package/dist/cli.js +10 -8
- package/dist/{export-KALLYKWG.js → export-I5Y26WUL.js} +2 -2
- package/dist/{install-agents-DLSH3FNC.js → install-agents-D2KJQUH3.js} +6 -4
- package/dist/instructions-4FI32YZU.js +10 -0
- package/dist/integration-scanner-PS47AHGM.js +15 -0
- package/dist/{refresh-N4O2QLYS.js → refresh-BXN32CNA.js} +5 -3
- package/dist/{status-YK2KBG57.js → status-RQWRIM2Y.js} +1 -1
- package/dist/{tools-GN6GM55U.js → tools-EISDGPS5.js} +440 -20
- package/package.json +1 -1
- package/dist/chunk-4PFEG4GQ.js +0 -30
- package/dist/instructions-YPXOZZHI.js +0 -6
|
@@ -2,13 +2,15 @@ import {
|
|
|
2
2
|
detectProjectRoot,
|
|
3
3
|
getBrain,
|
|
4
4
|
refreshBrain
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-4K4FHOKE.js";
|
|
6
|
+
import "./chunk-BU3VHX3W.js";
|
|
7
7
|
import "./chunk-MOOVNMIN.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-7PPC6IG6.js";
|
|
9
9
|
import "./chunk-M3YZOJNW.js";
|
|
10
|
+
import "./chunk-FLNV2IQC.js";
|
|
11
|
+
import "./chunk-KLNUEE3O.js";
|
|
10
12
|
import "./chunk-BAUQEFYY.js";
|
|
11
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-XFS2XGZI.js";
|
|
12
14
|
export {
|
|
13
15
|
detectProjectRoot,
|
|
14
16
|
getBrain,
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installAgentsForProject,
|
|
3
|
-
persistScanResult,
|
|
4
3
|
prewarmLatestVersion,
|
|
5
|
-
pruneSessionsByAge
|
|
6
|
-
|
|
7
|
-
} from "./chunk-N7R4P42P.js";
|
|
4
|
+
pruneSessionsByAge
|
|
5
|
+
} from "./chunk-BU3VHX3W.js";
|
|
8
6
|
import {
|
|
9
7
|
buildKnowledge,
|
|
10
8
|
buildReverseDependencies
|
|
11
9
|
} from "./chunk-MOOVNMIN.js";
|
|
12
10
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
scanProject,
|
|
16
|
-
spliceKnitBlock
|
|
17
|
-
} from "./chunk-JJ367RK5.js";
|
|
11
|
+
scanProject
|
|
12
|
+
} from "./chunk-7PPC6IG6.js";
|
|
18
13
|
import {
|
|
19
14
|
readLearnings
|
|
20
15
|
} from "./chunk-M3YZOJNW.js";
|
|
16
|
+
import {
|
|
17
|
+
persistScanResult,
|
|
18
|
+
scanIntegrations
|
|
19
|
+
} from "./chunk-FLNV2IQC.js";
|
|
20
|
+
import {
|
|
21
|
+
KNIT_MARKER_START,
|
|
22
|
+
generateClaudeMd,
|
|
23
|
+
spliceKnitBlock
|
|
24
|
+
} from "./chunk-KLNUEE3O.js";
|
|
21
25
|
import {
|
|
22
26
|
importFromMarkdown,
|
|
23
27
|
loadKnowledgeBase,
|
|
@@ -37,11 +41,12 @@ import {
|
|
|
37
41
|
migrationBreadcrumbPath,
|
|
38
42
|
projectDataDir,
|
|
39
43
|
protocolConfigPath,
|
|
44
|
+
searchMarkerPath,
|
|
40
45
|
sessionMarkerPath,
|
|
41
46
|
sessionsJsonlPath,
|
|
42
47
|
sessionsLogPath,
|
|
43
48
|
teamsPath
|
|
44
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-XFS2XGZI.js";
|
|
45
50
|
|
|
46
51
|
// src/mcp/cache.ts
|
|
47
52
|
import { execSync } from "child_process";
|
|
@@ -68,7 +73,7 @@ function generateLearningsContent(config) {
|
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
// src/generators/settings.ts
|
|
71
|
-
var HOOKS_VERSION =
|
|
76
|
+
var HOOKS_VERSION = 7;
|
|
72
77
|
function generateSettings(config, rootPath) {
|
|
73
78
|
return {
|
|
74
79
|
mcpServers: {
|
|
@@ -114,6 +119,9 @@ function generateHooks(config, rootPath) {
|
|
|
114
119
|
const PROTOCOL_CONFIG = protocolConfigPath(rootPath);
|
|
115
120
|
const CLASSIFIED_MARKER = classificationMarkerPath(rootPath);
|
|
116
121
|
const SESSION_MARKER = sessionMarkerPath(rootPath);
|
|
122
|
+
const SEARCH_MARKER = searchMarkerPath(rootPath);
|
|
123
|
+
const CLAUDE_MD = `${rootPath}/CLAUDE.md`;
|
|
124
|
+
void knowledgePath;
|
|
117
125
|
const hooks = {
|
|
118
126
|
SessionStart: [
|
|
119
127
|
// Protocol Guard layer 1: drop a marker that knit_load_session
|
|
@@ -152,6 +160,10 @@ function generateHooks(config, rootPath) {
|
|
|
152
160
|
const fs = require("fs");
|
|
153
161
|
const p = ${jsLit(CLASSIFIED_MARKER)};
|
|
154
162
|
if (fs.existsSync(p)) fs.rmSync(p, { force: true });
|
|
163
|
+
// v0.9 #5: per-turn search marker is also cleared at the turn boundary
|
|
164
|
+
// so the next non-trivial task has to call knit_search_learnings again.
|
|
165
|
+
const sm = ${jsLit(SEARCH_MARKER)};
|
|
166
|
+
if (fs.existsSync(sm)) fs.rmSync(sm, { force: true });
|
|
155
167
|
} catch (e) {}
|
|
156
168
|
`),
|
|
157
169
|
timeout: 5
|
|
@@ -195,6 +207,7 @@ function generateHooks(config, rootPath) {
|
|
|
195
207
|
const fs = require("fs");
|
|
196
208
|
const cfgPath = ${jsLit(PROTOCOL_CONFIG)};
|
|
197
209
|
const markerPath = ${jsLit(CLASSIFIED_MARKER)};
|
|
210
|
+
const searchMarkerPath = ${jsLit(SEARCH_MARKER)};
|
|
198
211
|
let level = "warn";
|
|
199
212
|
if (fs.existsSync(cfgPath)) {
|
|
200
213
|
try {
|
|
@@ -205,13 +218,33 @@ function generateHooks(config, rootPath) {
|
|
|
205
218
|
}
|
|
206
219
|
}
|
|
207
220
|
if (level === "off") return;
|
|
221
|
+
// Classification gate (v0.5).
|
|
208
222
|
const hasMarker = fs.existsSync(markerPath);
|
|
209
|
-
if (hasMarker)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
223
|
+
if (!hasMarker) {
|
|
224
|
+
if (level === "block") {
|
|
225
|
+
console.error("[knit] BLOCKED: call knit_classify_task before Edit/Write. The Protocol Guard prevents implementation without classification.");
|
|
226
|
+
process.exit(2);
|
|
227
|
+
}
|
|
228
|
+
console.error("[knit] reminder: call knit_classify_task before Edit/Write. Set strictness=block via knit_set_protocol_strictness to make this a hard gate.");
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
// v0.9 #5: search gate. For standard/complex tasks, knit_search_learnings
|
|
232
|
+
// (or knit_search_global_learnings) must run before the Edit lands \u2014
|
|
233
|
+
// otherwise the agent is re-investigating without checking memory.
|
|
234
|
+
try {
|
|
235
|
+
const marker = JSON.parse(fs.readFileSync(markerPath, "utf-8"));
|
|
236
|
+
if (marker && (marker.tier === "standard" || marker.tier === "complex")) {
|
|
237
|
+
if (!fs.existsSync(searchMarkerPath)) {
|
|
238
|
+
if (level === "block") {
|
|
239
|
+
console.error("[knit] BLOCKED: " + marker.tier + " task \u2014 call knit_search_learnings or knit_search_global_learnings before Edit/Write so memory is checked before re-investigation.");
|
|
240
|
+
process.exit(2);
|
|
241
|
+
}
|
|
242
|
+
console.error("[knit] reminder: " + marker.tier + " task \u2014 call knit_search_learnings before Edit/Write. Skipping memory check means re-doing work the project already learned.");
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} catch (markerErr) {
|
|
246
|
+
// Marker exists but JSON unreadable \u2014 be lenient.
|
|
213
247
|
}
|
|
214
|
-
console.error("[knit] reminder: call knit_classify_task before Edit/Write. Set strictness=block via knit_set_protocol_strictness to make this a hard gate.");
|
|
215
248
|
} catch (hookErr) {
|
|
216
249
|
console.error("[knit] protocol-guard hook crashed, allowing tool through:", hookErr && hookErr.message ? hookErr.message : hookErr);
|
|
217
250
|
}
|
|
@@ -219,11 +252,106 @@ function generateHooks(config, rootPath) {
|
|
|
219
252
|
timeout: 5
|
|
220
253
|
}
|
|
221
254
|
]
|
|
255
|
+
},
|
|
256
|
+
// v0.9 #9 — Pre-write content inspection. Reads the proposed Write/Edit
|
|
257
|
+
// content from tool_input, parses local import statements, and reports
|
|
258
|
+
// any relative paths that don't resolve on disk. Warn-level by default
|
|
259
|
+
// (the existing classification gate handles block mode); soft signal,
|
|
260
|
+
// never blocks on its own.
|
|
261
|
+
{
|
|
262
|
+
_knitOwned: true,
|
|
263
|
+
matcher: "Write|Edit|MultiEdit",
|
|
264
|
+
hooks: [
|
|
265
|
+
{
|
|
266
|
+
type: "command",
|
|
267
|
+
command: nodeHook(`
|
|
268
|
+
let d = "";
|
|
269
|
+
process.stdin.on("data", (c) => d += c);
|
|
270
|
+
process.stdin.on("end", () => {
|
|
271
|
+
try {
|
|
272
|
+
const fs = require("fs");
|
|
273
|
+
const path = require("path");
|
|
274
|
+
const i = JSON.parse(d);
|
|
275
|
+
const filePath = (i.tool_input && i.tool_input.file_path) || "";
|
|
276
|
+
if (!/\\.(?:ts|tsx|js|jsx|mjs|cjs)$/.test(filePath)) return;
|
|
277
|
+
// Pull proposed content from any of the Edit/Write shapes.
|
|
278
|
+
let content = (i.tool_input && (i.tool_input.content || i.tool_input.new_string)) || "";
|
|
279
|
+
if (i.tool_input && Array.isArray(i.tool_input.edits)) {
|
|
280
|
+
content = i.tool_input.edits.map((e) => e && e.new_string ? e.new_string : "").join("\\n");
|
|
281
|
+
}
|
|
282
|
+
if (!content) return;
|
|
283
|
+
const dir = path.dirname(filePath);
|
|
284
|
+
const re = /^import\\s+(?:[^'"]+?\\s+from\\s+)?['"]([^'"]+)['"]/gm;
|
|
285
|
+
const unresolved = [];
|
|
286
|
+
let m;
|
|
287
|
+
while ((m = re.exec(content)) !== null) {
|
|
288
|
+
const target = m[1];
|
|
289
|
+
if (!target.startsWith(".") && !target.startsWith("/")) continue;
|
|
290
|
+
const candidates = [target, target + ".ts", target + ".tsx", target + ".js", target + ".jsx", target + "/index.ts", target + "/index.tsx", target + "/index.js"];
|
|
291
|
+
let resolved = false;
|
|
292
|
+
for (const c of candidates) {
|
|
293
|
+
const abs = path.resolve(dir, c);
|
|
294
|
+
if (fs.existsSync(abs)) { resolved = true; break; }
|
|
295
|
+
}
|
|
296
|
+
if (!resolved) unresolved.push(target);
|
|
297
|
+
}
|
|
298
|
+
if (unresolved.length > 0) {
|
|
299
|
+
console.error("[knit] heads-up: proposed edit references " + unresolved.length + " unresolved relative import(s): " + unresolved.join(", ") + ". Likely hallucinated paths \u2014 verify with knit_query_imports or knit_verify_claim before relying on them.");
|
|
300
|
+
}
|
|
301
|
+
} catch (e) {}
|
|
302
|
+
});
|
|
303
|
+
`),
|
|
304
|
+
timeout: 5
|
|
305
|
+
}
|
|
306
|
+
]
|
|
222
307
|
}
|
|
223
308
|
],
|
|
224
309
|
PostToolUse: [],
|
|
225
310
|
Stop: []
|
|
226
311
|
};
|
|
312
|
+
hooks.PostToolUse.push({
|
|
313
|
+
_knitOwned: true,
|
|
314
|
+
matcher: "Write|Edit|MultiEdit",
|
|
315
|
+
hooks: [
|
|
316
|
+
{
|
|
317
|
+
type: "command",
|
|
318
|
+
command: nodeHook(`
|
|
319
|
+
let d = "";
|
|
320
|
+
process.stdin.on("data", (c) => d += c);
|
|
321
|
+
process.stdin.on("end", () => {
|
|
322
|
+
try {
|
|
323
|
+
const fs = require("fs");
|
|
324
|
+
const path = require("path");
|
|
325
|
+
const i = JSON.parse(d);
|
|
326
|
+
const f = (i.tool_input && i.tool_input.file_path) || (i.tool_response && i.tool_response.filePath) || "";
|
|
327
|
+
if (!/\\.(?:ts|tsx|js|jsx|mjs|cjs)$/.test(f)) return;
|
|
328
|
+
if (!fs.existsSync(f)) return;
|
|
329
|
+
const content = fs.readFileSync(f, "utf-8");
|
|
330
|
+
const dir = path.dirname(f);
|
|
331
|
+
const re = /^import\\s+(?:[^'"]+?\\s+from\\s+)?['"]([^'"]+)['"]/gm;
|
|
332
|
+
const unresolved = [];
|
|
333
|
+
let m;
|
|
334
|
+
while ((m = re.exec(content)) !== null) {
|
|
335
|
+
const target = m[1];
|
|
336
|
+
if (!target.startsWith(".") && !target.startsWith("/")) continue;
|
|
337
|
+
const candidates = [target, target + ".ts", target + ".tsx", target + ".js", target + ".jsx", target + "/index.ts", target + "/index.tsx", target + "/index.js"];
|
|
338
|
+
let resolved = false;
|
|
339
|
+
for (const c of candidates) {
|
|
340
|
+
if (fs.existsSync(path.resolve(dir, c))) { resolved = true; break; }
|
|
341
|
+
}
|
|
342
|
+
if (!resolved) unresolved.push(target);
|
|
343
|
+
}
|
|
344
|
+
if (unresolved.length > 0) {
|
|
345
|
+
console.error("[knit] post-write check: " + f + " has " + unresolved.length + " unresolved relative import(s): " + unresolved.join(", ") + ". Run typecheck before relying on this file.");
|
|
346
|
+
}
|
|
347
|
+
} catch (e) {}
|
|
348
|
+
});
|
|
349
|
+
`),
|
|
350
|
+
timeout: 5,
|
|
351
|
+
statusMessage: "Knit: validating imports..."
|
|
352
|
+
}
|
|
353
|
+
]
|
|
354
|
+
});
|
|
227
355
|
if (config.stack.language === "typescript" && config.stack.typecheckCommand) {
|
|
228
356
|
hooks.PostToolUse.push({
|
|
229
357
|
_knitOwned: true,
|
|
@@ -354,6 +482,27 @@ function generateHooks(config, rootPath) {
|
|
|
354
482
|
]
|
|
355
483
|
});
|
|
356
484
|
}
|
|
485
|
+
hooks.Stop.push({
|
|
486
|
+
_knitOwned: true,
|
|
487
|
+
hooks: [
|
|
488
|
+
{
|
|
489
|
+
type: "command",
|
|
490
|
+
command: nodeHook(`
|
|
491
|
+
try {
|
|
492
|
+
const fs = require("fs");
|
|
493
|
+
const p = ${jsLit(CLAUDE_MD)};
|
|
494
|
+
if (!fs.existsSync(p)) return;
|
|
495
|
+
const size = fs.statSync(p).size;
|
|
496
|
+
if (size > 12500) {
|
|
497
|
+
console.error("[knit] budget watch: CLAUDE.md is " + Math.round(size/1024*10)/10 + "KB (target 6.5KB; over-budget threshold 12.5KB). Call knit_brain_status to confirm and consider regenerating via knit refresh.");
|
|
498
|
+
}
|
|
499
|
+
} catch (e) {}
|
|
500
|
+
`),
|
|
501
|
+
timeout: 5,
|
|
502
|
+
statusMessage: "Knit: budget check..."
|
|
503
|
+
}
|
|
504
|
+
]
|
|
505
|
+
});
|
|
357
506
|
hooks.Stop.push({
|
|
358
507
|
_knitOwned: true,
|
|
359
508
|
hooks: [
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/mcp/instructions.ts
|
|
2
|
+
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
|
+
|
|
4
|
+
ALWAYS at session start:
|
|
5
|
+
1. Call knit_load_session \u2014 returns prior handoff, top learnings, false positives. If has_unfinished_work is true, resume that handoff instead of starting fresh.
|
|
6
|
+
2. For any non-trivial task, call knit_classify_task BEFORE editing or writing \u2014 returns tier (inquiry / trivial / standard / complex) and phases.
|
|
7
|
+
3. If tier=complex with auto_plan_mode=true, call EnterPlanMode immediately. Do not start editing.
|
|
8
|
+
4. If tier=inquiry, just answer \u2014 no plan mode, no phases. Re-classify only if scope grows into writes.
|
|
9
|
+
5. Before reporting a task done, call knit_record_learning if anything non-obvious surfaced (not a substring restatement of prior learnings).
|
|
10
|
+
|
|
11
|
+
When to reach for other Knit tools:
|
|
12
|
+
- knit_query_imports / knit_query_exports / knit_query_dependents / knit_query_tests \u2014 use instead of grep when the knowledge index is fresh.
|
|
13
|
+
- knit_search_learnings \u2014 call before re-investigating a domain. The point of memory is to skip what you've already learned.
|
|
14
|
+
- knit_search_sessions \u2014 answers "have I done this before?"
|
|
15
|
+
- knit_search_global_learnings \u2014 same, but across all your projects (Knit's cross-project pool).
|
|
16
|
+
- knit_get_workflow({phase}) \u2014 fetch protocol depth for one phase on demand. Do not try to remember the workflow; ask for it.
|
|
17
|
+
- knit_list_features \u2014 if you want to do X but the tool isn't visible, this surfaces what's hidden and how to enable it.
|
|
18
|
+
- knit_save_handoff \u2014 call when context degrades or session ends so the next session resumes cleanly.
|
|
19
|
+
|
|
20
|
+
The protocol enforces a 4-tier classifier:
|
|
21
|
+
- Inquiry: read-only "what / where / audit / explain" \u2014 just answer.
|
|
22
|
+
- Trivial: single-file obvious change \u2014 EXECUTE \u2192 VERIFY \u2192 LEARN.
|
|
23
|
+
- Standard: bug fix or single-domain feature \u2014 RESEARCH \u2192 EXECUTE \u2192 OPTIMIZE \u2192 REVIEW \u2192 LEARN.
|
|
24
|
+
- Complex: cross-domain, types/auth-touching, high-fanout, or any task spanning more than one commit \u2014 full 6 phases with auto plan mode on RESEARCH.
|
|
25
|
+
|
|
26
|
+
Knit provides inputs; you make the calls. When in doubt, under-classify \u2014 easier to escalate mid-task than to downgrade.
|
|
27
|
+
|
|
28
|
+
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.`;
|
|
29
|
+
var KNIT_INSTRUCTIONS = KNIT_INSTRUCTIONS_BASE;
|
|
30
|
+
function buildInstructions(scan) {
|
|
31
|
+
if (!scan) return KNIT_INSTRUCTIONS_BASE;
|
|
32
|
+
const addenda = [];
|
|
33
|
+
if (scan.detected.ruflo.present) {
|
|
34
|
+
addenda.push(
|
|
35
|
+
"DETECTED: Ruflo (multi-agent orchestration) is installed alongside Knit on this project. For multi-agent swarms, federation, or large-scale orchestration, defer to Ruflo's tools (`memory_store`, `swarm_init`, `agent_spawn`, etc). Knit's domain in this project: per-project memory + tier-routed classification + token discipline. Do NOT duplicate Ruflo's routing logic with Knit's tier protocol when Ruflo is driving the workflow."
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
if (scan.detected.gstack.present) {
|
|
39
|
+
addenda.push(
|
|
40
|
+
"DETECTED: gstack slash commands are installed. For routing decisions (`/plan`, `/ship`, `/qa`, `/cso`, `/investigate`), prefer the gstack command. Knit operates underneath as the memory + classification layer; the gstack command can invoke Knit tools internally."
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
if (scan.detected.codetour.present) {
|
|
44
|
+
addenda.push(
|
|
45
|
+
"DETECTED: CodeTour is configured (.tours/*.tour). When asked to walk through code or explain architecture, surface relevant tours via the CodeTour extension rather than reconstructing the explanation from scratch."
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (scan.detected.conductor.present) {
|
|
49
|
+
addenda.push(
|
|
50
|
+
"DETECTED: Conductor is installed. For cross-workspace handoff and context-restore flows, defer to Conductor's primitives; Knit's `knit_save_handoff` / `knit_load_session` continue to handle the per-project memory layer."
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
if (scan.detected.custom_workflow_sections.length > 0) {
|
|
54
|
+
addenda.push(
|
|
55
|
+
`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.`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (addenda.length === 0) return KNIT_INSTRUCTIONS_BASE;
|
|
59
|
+
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.";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
KNIT_INSTRUCTIONS_BASE,
|
|
64
|
+
KNIT_INSTRUCTIONS,
|
|
65
|
+
buildInstructions
|
|
66
|
+
};
|
|
@@ -271,162 +271,6 @@ function detectGit(root) {
|
|
|
271
271
|
return { isRepo, defaultBranch, hasRemote };
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
// src/generators/claude-md.ts
|
|
275
|
-
var KNIT_MARKER_START = "<!-- knit:start -->";
|
|
276
|
-
var KNIT_MARKER_END = "<!-- knit:end -->";
|
|
277
|
-
var LEGACY_ENGRAM_MARKER_START = "<!-- engram:start -->";
|
|
278
|
-
var LEGACY_ENGRAM_MARKER_END = "<!-- engram:end -->";
|
|
279
|
-
function generateClaudeMd(config, knowledge, falsePositives) {
|
|
280
|
-
const sections = [
|
|
281
|
-
generateHeader(config),
|
|
282
|
-
generateSessionStartup(),
|
|
283
|
-
knowledge ? generateProjectMap(knowledge) : null,
|
|
284
|
-
generateDomainArchitecture(config),
|
|
285
|
-
falsePositives && falsePositives.length > 0 ? generateFalsePositives(falsePositives) : null,
|
|
286
|
-
generateBuildGates(config),
|
|
287
|
-
generateTierVocabulary(),
|
|
288
|
-
generateWorkflowPointer()
|
|
289
|
-
];
|
|
290
|
-
const body = sections.filter(Boolean).join("\n\n---\n\n");
|
|
291
|
-
return `${KNIT_MARKER_START}
|
|
292
|
-
|
|
293
|
-
${body}
|
|
294
|
-
|
|
295
|
-
${KNIT_MARKER_END}
|
|
296
|
-
`;
|
|
297
|
-
}
|
|
298
|
-
function spliceKnitBlock(existing, newBlock) {
|
|
299
|
-
const markerPairs = [
|
|
300
|
-
[KNIT_MARKER_START, KNIT_MARKER_END],
|
|
301
|
-
[LEGACY_ENGRAM_MARKER_START, LEGACY_ENGRAM_MARKER_END]
|
|
302
|
-
];
|
|
303
|
-
for (const [startMarker, endMarker] of markerPairs) {
|
|
304
|
-
const startIdx = existing.indexOf(startMarker);
|
|
305
|
-
const endIdx = existing.indexOf(endMarker);
|
|
306
|
-
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
307
|
-
const before = existing.slice(0, startIdx);
|
|
308
|
-
const after = existing.slice(endIdx + endMarker.length);
|
|
309
|
-
return { content: before + newBlock.trimEnd() + after, mode: "replaced" };
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
return { content: existing, mode: "sidecar-needed" };
|
|
313
|
-
}
|
|
314
|
-
function generateHeader(config) {
|
|
315
|
-
const stackParts = [
|
|
316
|
-
config.stack.language !== "unknown" ? config.stack.language : null,
|
|
317
|
-
config.stack.framework
|
|
318
|
-
].filter(Boolean);
|
|
319
|
-
const stackDesc = stackParts.length > 0 ? `${stackParts.join(" + ")} project. ` : "";
|
|
320
|
-
return `# ${config.name}
|
|
321
|
-
|
|
322
|
-
${stackDesc}Knit-powered workflow. The protocol depth is fetched on demand via \`knit_get_workflow({phase})\` \u2014 this file holds only project-specific facts.`;
|
|
323
|
-
}
|
|
324
|
-
function generateSessionStartup() {
|
|
325
|
-
return `## Session start
|
|
326
|
-
|
|
327
|
-
First action: call \`knit_load_session\`. One MCP call returns last sessions, handoff, learnings, false positives. If \`handoff.md\` exists at the repo root, resume that work first.
|
|
328
|
-
|
|
329
|
-
Protocol Guard runs in \`warn\` mode by default \u2014 adjust with \`knit_set_protocol_strictness\`.`;
|
|
330
|
-
}
|
|
331
|
-
function generateProjectMap(knowledge) {
|
|
332
|
-
const { summary } = knowledge;
|
|
333
|
-
let content = `## Project Map (auto-generated)
|
|
334
|
-
|
|
335
|
-
`;
|
|
336
|
-
if (summary.entryPoints.length > 0) {
|
|
337
|
-
content += `**Entry points:** \`${summary.entryPoints.join("`, `")}\`
|
|
338
|
-
`;
|
|
339
|
-
}
|
|
340
|
-
if (summary.highFanoutFiles.length > 0) {
|
|
341
|
-
const shown = summary.highFanoutFiles.slice(0, 5);
|
|
342
|
-
content += `**High-fanout (change carefully):** \`${shown.join("`, `")}\``;
|
|
343
|
-
if (summary.highFanoutFiles.length > 5) {
|
|
344
|
-
content += ` (+${summary.highFanoutFiles.length - 5} more \u2014 \`knit_find_fanout\`)`;
|
|
345
|
-
}
|
|
346
|
-
content += "\n";
|
|
347
|
-
}
|
|
348
|
-
if (summary.untestedFiles.length > 0) {
|
|
349
|
-
const shown = summary.untestedFiles.slice(0, 3);
|
|
350
|
-
content += `**Untested:** \`${shown.join("`, `")}\``;
|
|
351
|
-
if (summary.untestedFiles.length > 3) {
|
|
352
|
-
content += ` (+${summary.untestedFiles.length - 3} more \u2014 \`knit_query_tests({filter:"untested"})\`)`;
|
|
353
|
-
}
|
|
354
|
-
content += "\n";
|
|
355
|
-
}
|
|
356
|
-
if (summary.largestFiles.length > 0) {
|
|
357
|
-
const top3 = summary.largestFiles.slice(0, 3);
|
|
358
|
-
const list = top3.map((f) => `\`${f.path}\` (${f.lines})`).join(", ");
|
|
359
|
-
content += `**Largest:** ${list}
|
|
360
|
-
`;
|
|
361
|
-
}
|
|
362
|
-
content += `
|
|
363
|
-
**Stats:** ${summary.totalFiles} files, ${summary.totalLines.toLocaleString()} lines`;
|
|
364
|
-
const langs = Object.entries(summary.languageBreakdown).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([ext, count]) => `${ext}: ${count}`);
|
|
365
|
-
if (langs.length > 0) content += ` (${langs.join(", ")})`;
|
|
366
|
-
return content;
|
|
367
|
-
}
|
|
368
|
-
function generateDomainArchitecture(config) {
|
|
369
|
-
if (!config.domains || config.domains.length === 0) {
|
|
370
|
-
return `## Domain Architecture
|
|
371
|
-
|
|
372
|
-
No domains detected. Use \`knit_setup_project\` to describe your project \u2014 Knit will configure domains and review agents.`;
|
|
373
|
-
}
|
|
374
|
-
const rows = config.domains.map((d) => {
|
|
375
|
-
const patterns = d.filePatterns.slice(0, 3).join(", ");
|
|
376
|
-
const agents = d.agents.join(", ");
|
|
377
|
-
return `### ${d.name}
|
|
378
|
-
**Files:** \`${patterns}\`
|
|
379
|
-
**Concern:** ${d.description}
|
|
380
|
-
**Review agents:** \`${agents}\``;
|
|
381
|
-
}).join("\n\n");
|
|
382
|
-
return `## Domain Architecture
|
|
383
|
-
|
|
384
|
-
${rows}`;
|
|
385
|
-
}
|
|
386
|
-
function generateFalsePositives(fps) {
|
|
387
|
-
const items = fps.slice(0, 10).map((fp) => `- **${fp.summary}** \u2014 ${fp.lesson}`).join("\n");
|
|
388
|
-
return `## Known False Positives
|
|
389
|
-
|
|
390
|
-
Review agents should NOT re-flag these \u2014 they're confirmed non-issues from prior sessions:
|
|
391
|
-
|
|
392
|
-
${items}`;
|
|
393
|
-
}
|
|
394
|
-
function generateBuildGates(config) {
|
|
395
|
-
const gates = [];
|
|
396
|
-
if (config.stack.typecheckCommand) gates.push(`- \`${config.stack.typecheckCommand}\``);
|
|
397
|
-
if (config.stack.lintCommand) gates.push(`- \`${config.stack.lintCommand}\``);
|
|
398
|
-
if (config.stack.testFramework) {
|
|
399
|
-
const pm = config.packageManager === "unknown" ? "npm" : config.packageManager;
|
|
400
|
-
gates.push(`- \`${pm} test\``);
|
|
401
|
-
}
|
|
402
|
-
if (config.stack.buildCommand) gates.push(`- \`${config.stack.buildCommand}\``);
|
|
403
|
-
if (gates.length === 0) {
|
|
404
|
-
return `## Build Gates
|
|
405
|
-
|
|
406
|
-
No build gates auto-detected. Add typecheck/lint/test/build commands to your package.json.`;
|
|
407
|
-
}
|
|
408
|
-
return `## Build Gates
|
|
409
|
-
|
|
410
|
-
All must pass before commit:
|
|
411
|
-
|
|
412
|
-
${gates.join("\n")}`;
|
|
413
|
-
}
|
|
414
|
-
function generateTierVocabulary() {
|
|
415
|
-
return `## Tier vocabulary
|
|
416
|
-
|
|
417
|
-
| Tier | When |
|
|
418
|
-
|------|------|
|
|
419
|
-
| **Inquiry** | Read-only ("what", "where", "audit") \u2014 just answer. |
|
|
420
|
-
| **Trivial** | One-line fix \u2014 execute \u2192 verify. |
|
|
421
|
-
| **Standard** | Single-domain bug fix or feature \u2014 research \u2192 execute \u2192 review. |
|
|
422
|
-
| **Complex** | Cross-domain, touches types/auth, high-fanout, or multi-commit arc \u2014 full 6 phases + auto plan mode. |`;
|
|
423
|
-
}
|
|
424
|
-
function generateWorkflowPointer() {
|
|
425
|
-
return `## Workflow on demand
|
|
426
|
-
|
|
427
|
-
Fetch any phase via \`knit_get_workflow({phase})\`. Call with no phase to list available sections.`;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
274
|
export {
|
|
431
275
|
VOLTAGENT_PINNED_SHA,
|
|
432
276
|
VOLTAGENT_REF,
|
|
@@ -434,11 +278,5 @@ export {
|
|
|
434
278
|
categoryOf,
|
|
435
279
|
rawAgentUrl,
|
|
436
280
|
isBundledCore,
|
|
437
|
-
scanProject
|
|
438
|
-
KNIT_MARKER_START,
|
|
439
|
-
KNIT_MARKER_END,
|
|
440
|
-
LEGACY_ENGRAM_MARKER_START,
|
|
441
|
-
LEGACY_ENGRAM_MARKER_END,
|
|
442
|
-
generateClaudeMd,
|
|
443
|
-
spliceKnitBlock
|
|
281
|
+
scanProject
|
|
444
282
|
};
|