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.
@@ -2,13 +2,15 @@ import {
2
2
  detectProjectRoot,
3
3
  getBrain,
4
4
  refreshBrain
5
- } from "./chunk-WN4AQFMO.js";
6
- import "./chunk-N7R4P42P.js";
5
+ } from "./chunk-4K4FHOKE.js";
6
+ import "./chunk-BU3VHX3W.js";
7
7
  import "./chunk-MOOVNMIN.js";
8
- import "./chunk-JJ367RK5.js";
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-SBJMHDBM.js";
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
- scanIntegrations
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
- KNIT_MARKER_START,
14
- generateClaudeMd,
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-SBJMHDBM.js";
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 = 6;
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) return;
210
- if (level === "block") {
211
- console.error("[knit] BLOCKED: call knit_classify_task before Edit/Write. The Protocol Guard prevents implementation without classification.");
212
- process.exit(2);
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
  };
@@ -2,7 +2,7 @@ import {
2
2
  canonicalRepoRoot,
3
3
  globalLearningsPath,
4
4
  projectId
5
- } from "./chunk-SBJMHDBM.js";
5
+ } from "./chunk-XFS2XGZI.js";
6
6
 
7
7
  // src/engine/global-learnings.ts
8
8
  import { existsSync, mkdirSync, appendFileSync, readFileSync, statSync } from "fs";