knit-mcp 0.6.5 → 0.7.1
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 +23 -0
- package/dist/{cache-MHJP4WGF.js → cache-BVFD5F5Y.js} +5 -5
- package/dist/{chunk-HWEH27E7.js → chunk-3XR77YJM.js} +1 -1
- package/dist/{chunk-YI37OAJ7.js → chunk-HBMF62U4.js} +4 -0
- package/dist/{chunk-5F7DSVJY.js → chunk-SLN5ABF5.js} +36 -65
- package/dist/{chunk-FEOG4WTP.js → chunk-TRZ3LD6B.js} +1 -1
- package/dist/chunk-WMESQUZU.js +25 -0
- package/dist/{chunk-IG4VRBYW.js → chunk-X6TGTET3.js} +9 -9
- package/dist/cli.js +17 -8
- package/dist/{export-EEBXKMHR.js → export-IKPBLZOO.js} +2 -2
- package/dist/{install-agents-Q64MWT3V.js → install-agents-HXVYULKK.js} +5 -5
- package/dist/instructions-33TUHLTK.js +29 -0
- package/dist/notifier-4L27HKHI.js +10 -0
- package/dist/{refresh-K7G7HRF7.js → refresh-JJUSH2J5.js} +5 -5
- package/dist/{status-H2CU72CE.js → status-XN6VHO66.js} +1 -1
- package/dist/{tools-25CPMJKY.js → tools-NZUUKPYI.js} +392 -78
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,29 @@ Knit makes Claude Code do the right thing automatically because it can't predict
|
|
|
26
26
|
|
|
27
27
|
It's a **single product**, not three. Every design choice has to win on memory + tokens + workflow together.
|
|
28
28
|
|
|
29
|
+
## What's new in v0.7.0
|
|
30
|
+
|
|
31
|
+
- **Universal protocol injection.** Knit sets the MCP server-level `instructions` field, so every MCP client (Claude Code, Cursor, Codex) sees Knit's flow at session start — *before* tool descriptions. Session 1 follows the protocol instead of stumbling onto it.
|
|
32
|
+
- **Tier-gated tool surface.** 38 tools split into three tiers: Tier 1 (26 universal — memory, knowledge graph, workflow, classification, false-positive suppression, reflection, Protocol Guard config, diagnostics) is always exposed. Tier 2 (team worktrees, subagent installer) auto-activates when the project shape matches (≥3 detected domains, `.claude/agents/` exists) or via explicit opt-in. Tier 3 (admin/setup) is opt-in only. Solo-domain projects no longer see 9 team-worktree tools cluttering their decision space.
|
|
33
|
+
- **`knit_list_features`** is the discoverability escape hatch — always available, always tells you what's hidden and exactly how to enable it (`knit_enable_feature({feature: "teams" | "subagents" | "admin"})`). Persisted to `~/.knit/projects/<hash>/features.json` so the choice survives sessions.
|
|
34
|
+
- **Inquiry tier in the classifier.** Read-only "what / where / audit / explain" tasks now route to `tier: "inquiry"` with no plan mode and no phases — fixes a long-standing over-routing bug where audit-style questions hijacked Complex.
|
|
35
|
+
- **CLAUDE.md cut ~88%** (16.7 KB → ~2 KB on typical projects). The per-turn context tax dropped sharply; all project-specific content (header, project map, domain architecture, build gates, false positives) stays intact.
|
|
36
|
+
- **Lazy / minimal response modes.** `knit_load_session` returns the lean core by default; opt into more via `include=patterns,teams,metrics,recent_sessions,full_learnings,full_knowledge,all`. `knit_classify_task` returns the minimal shape by default; pass `verbose=true` for the diagnostic fields.
|
|
37
|
+
- **Legacy CLAUDE.md migration.** Users upgrading from v0.5.x with `<!-- engram:start -->/<!-- engram:end -->` markers are auto-migrated — the legacy block is replaced cleanly with the new lean block instead of leaving an orphan.
|
|
38
|
+
|
|
39
|
+
### Per-session token-budget table
|
|
40
|
+
|
|
41
|
+
| Surface | v0.6.5 | v0.7.0 | Cut |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| CLAUDE.md per-turn | ~16.7 KB | ~2 KB | 88% |
|
|
44
|
+
| Tool registry (typical project) | ~6–8 KB | ~3–4 KB | ~50% |
|
|
45
|
+
| `knit_classify_task` response | ~500 tok | ~150 tok | 70% |
|
|
46
|
+
| `knit_load_session` response | ~3–5 KB | ~1.5 KB | ~60% |
|
|
47
|
+
|
|
48
|
+
### Upgrade note
|
|
49
|
+
|
|
50
|
+
After running `npx knit-mcp@latest setup` (or just updating the version pin), **restart Claude Code**. The MCP server's `instructions` field and tier-gated `tools/list` only flow into the system prompt at handshake — the cached process from before the upgrade keeps the v0.6.5 behavior until restart.
|
|
51
|
+
|
|
29
52
|
## Setup (one time)
|
|
30
53
|
|
|
31
54
|
```bash
|
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
detectProjectRoot,
|
|
3
3
|
getBrain,
|
|
4
4
|
refreshBrain
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-HWEH27E7.js";
|
|
5
|
+
} from "./chunk-X6TGTET3.js";
|
|
6
|
+
import "./chunk-3XR77YJM.js";
|
|
7
|
+
import "./chunk-SLN5ABF5.js";
|
|
9
8
|
import "./chunk-7PPC6IG6.js";
|
|
9
|
+
import "./chunk-M3YZOJNW.js";
|
|
10
10
|
import "./chunk-BAUQEFYY.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-HBMF62U4.js";
|
|
12
12
|
export {
|
|
13
13
|
detectProjectRoot,
|
|
14
14
|
getBrain,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
projectAgentFile,
|
|
12
12
|
projectAgentsDir,
|
|
13
13
|
sessionsJsonlPath
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HBMF62U4.js";
|
|
15
15
|
|
|
16
16
|
// src/engine/install-agents.ts
|
|
17
17
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -87,6 +87,9 @@ function classificationMarkerPath(rootPath) {
|
|
|
87
87
|
function sessionMarkerPath(rootPath) {
|
|
88
88
|
return join2(projectDataDir(rootPath), ".session-loaded");
|
|
89
89
|
}
|
|
90
|
+
function featuresConfigPath(rootPath) {
|
|
91
|
+
return join2(projectDataDir(rootPath), "features.json");
|
|
92
|
+
}
|
|
90
93
|
function learningsDir(rootPath) {
|
|
91
94
|
return join2(projectDataDir(rootPath), "learnings");
|
|
92
95
|
}
|
|
@@ -133,6 +136,7 @@ export {
|
|
|
133
136
|
protocolConfigPath,
|
|
134
137
|
classificationMarkerPath,
|
|
135
138
|
sessionMarkerPath,
|
|
139
|
+
featuresConfigPath,
|
|
136
140
|
learningsDir,
|
|
137
141
|
learningsFilePath,
|
|
138
142
|
sessionsLogPath,
|
|
@@ -361,6 +361,8 @@ function buildReverseDependencies(importGraph) {
|
|
|
361
361
|
// src/generators/claude-md.ts
|
|
362
362
|
var KNIT_MARKER_START = "<!-- knit:start -->";
|
|
363
363
|
var KNIT_MARKER_END = "<!-- knit:end -->";
|
|
364
|
+
var LEGACY_ENGRAM_MARKER_START = "<!-- engram:start -->";
|
|
365
|
+
var LEGACY_ENGRAM_MARKER_END = "<!-- engram:end -->";
|
|
364
366
|
function generateClaudeMd(config, knowledge, falsePositives) {
|
|
365
367
|
const sections = [
|
|
366
368
|
generateHeader(config),
|
|
@@ -370,8 +372,7 @@ function generateClaudeMd(config, knowledge, falsePositives) {
|
|
|
370
372
|
falsePositives && falsePositives.length > 0 ? generateFalsePositives(falsePositives) : null,
|
|
371
373
|
generateBuildGates(config),
|
|
372
374
|
generateTierVocabulary(),
|
|
373
|
-
generateWorkflowPointer()
|
|
374
|
-
generatePhaseStatus()
|
|
375
|
+
generateWorkflowPointer()
|
|
375
376
|
];
|
|
376
377
|
const body = sections.filter(Boolean).join("\n\n---\n\n");
|
|
377
378
|
return `${KNIT_MARKER_START}
|
|
@@ -382,12 +383,18 @@ ${KNIT_MARKER_END}
|
|
|
382
383
|
`;
|
|
383
384
|
}
|
|
384
385
|
function spliceKnitBlock(existing, newBlock) {
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
386
|
+
const markerPairs = [
|
|
387
|
+
[KNIT_MARKER_START, KNIT_MARKER_END],
|
|
388
|
+
[LEGACY_ENGRAM_MARKER_START, LEGACY_ENGRAM_MARKER_END]
|
|
389
|
+
];
|
|
390
|
+
for (const [startMarker, endMarker] of markerPairs) {
|
|
391
|
+
const startIdx = existing.indexOf(startMarker);
|
|
392
|
+
const endIdx = existing.indexOf(endMarker);
|
|
393
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
394
|
+
const before = existing.slice(0, startIdx);
|
|
395
|
+
const after = existing.slice(endIdx + endMarker.length);
|
|
396
|
+
return { content: before + newBlock.trimEnd() + after, mode: "replaced" };
|
|
397
|
+
}
|
|
391
398
|
}
|
|
392
399
|
return { content: existing, mode: "sidecar-needed" };
|
|
393
400
|
}
|
|
@@ -404,18 +411,9 @@ ${stackDesc}Knit-powered workflow. The protocol depth is fetched on demand via \
|
|
|
404
411
|
function generateSessionStartup() {
|
|
405
412
|
return `## Session start
|
|
406
413
|
|
|
407
|
-
First action: call \`knit_load_session\`. One MCP call returns last sessions, handoff, learnings, false positives,
|
|
408
|
-
|
|
409
|
-
After that, state readiness in one line:
|
|
410
|
-
> Session loaded. Learnings: N. Handoff: yes/no. Branch: X. Recent sessions: N.
|
|
411
|
-
|
|
412
|
-
If \`handoff.md\` exists at the repo root, resume that work first.
|
|
414
|
+
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.
|
|
413
415
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
## Protocol Guard
|
|
417
|
-
|
|
418
|
-
This project ships with runtime enforcement of the Knit protocol via PreToolUse and SessionStart hooks. Strictness levels: \`off\` (no checks), \`warn\` (reminder, default), \`block\` (hard-fail Edit/Write without prior \`knit_classify_task\`). Change via \`knit_set_protocol_strictness({ level })\`. Inspect via \`knit_get_protocol_strictness\`. The gate exists because protocol compliance is structurally easy to skip \u2014 make it impossible instead.`;
|
|
416
|
+
Protocol Guard runs in \`warn\` mode by default \u2014 adjust with \`knit_set_protocol_strictness\`.`;
|
|
419
417
|
}
|
|
420
418
|
function generateProjectMap(knowledge) {
|
|
421
419
|
const { summary } = knowledge;
|
|
@@ -427,30 +425,30 @@ function generateProjectMap(knowledge) {
|
|
|
427
425
|
`;
|
|
428
426
|
}
|
|
429
427
|
if (summary.highFanoutFiles.length > 0) {
|
|
430
|
-
const shown = summary.highFanoutFiles.slice(0,
|
|
431
|
-
content += `**High-fanout
|
|
432
|
-
if (summary.highFanoutFiles.length >
|
|
433
|
-
content += ` (+${summary.highFanoutFiles.length -
|
|
428
|
+
const shown = summary.highFanoutFiles.slice(0, 5);
|
|
429
|
+
content += `**High-fanout (change carefully):** \`${shown.join("`, `")}\``;
|
|
430
|
+
if (summary.highFanoutFiles.length > 5) {
|
|
431
|
+
content += ` (+${summary.highFanoutFiles.length - 5} more \u2014 \`knit_find_fanout\`)`;
|
|
434
432
|
}
|
|
435
433
|
content += "\n";
|
|
436
434
|
}
|
|
437
435
|
if (summary.untestedFiles.length > 0) {
|
|
438
|
-
const shown = summary.untestedFiles.slice(0,
|
|
439
|
-
content += `**Untested
|
|
440
|
-
if (summary.untestedFiles.length >
|
|
441
|
-
content += ` (+${summary.untestedFiles.length -
|
|
436
|
+
const shown = summary.untestedFiles.slice(0, 3);
|
|
437
|
+
content += `**Untested:** \`${shown.join("`, `")}\``;
|
|
438
|
+
if (summary.untestedFiles.length > 3) {
|
|
439
|
+
content += ` (+${summary.untestedFiles.length - 3} more \u2014 \`knit_query_tests({filter:"untested"})\`)`;
|
|
442
440
|
}
|
|
443
441
|
content += "\n";
|
|
444
442
|
}
|
|
445
443
|
if (summary.largestFiles.length > 0) {
|
|
446
444
|
const top3 = summary.largestFiles.slice(0, 3);
|
|
447
|
-
const list = top3.map((f) => `\`${f.path}\` (${f.lines}
|
|
448
|
-
content += `**Largest
|
|
445
|
+
const list = top3.map((f) => `\`${f.path}\` (${f.lines})`).join(", ");
|
|
446
|
+
content += `**Largest:** ${list}
|
|
449
447
|
`;
|
|
450
448
|
}
|
|
451
449
|
content += `
|
|
452
450
|
**Stats:** ${summary.totalFiles} files, ${summary.totalLines.toLocaleString()} lines`;
|
|
453
|
-
const langs = Object.entries(summary.languageBreakdown).sort((a, b) => b[1] - a[1]).slice(0,
|
|
451
|
+
const langs = Object.entries(summary.languageBreakdown).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([ext, count]) => `${ext}: ${count}`);
|
|
454
452
|
if (langs.length > 0) content += ` (${langs.join(", ")})`;
|
|
455
453
|
return content;
|
|
456
454
|
}
|
|
@@ -501,46 +499,19 @@ All must pass before commit:
|
|
|
501
499
|
${gates.join("\n")}`;
|
|
502
500
|
}
|
|
503
501
|
function generateTierVocabulary() {
|
|
504
|
-
return `## Tier vocabulary
|
|
505
|
-
|
|
506
|
-
You classify each task. No regex, no auto-rules.
|
|
502
|
+
return `## Tier vocabulary
|
|
507
503
|
|
|
508
|
-
| Tier |
|
|
509
|
-
|
|
510
|
-
| **Inquiry** | Read-only
|
|
511
|
-
| **Trivial** | One-line fix
|
|
512
|
-
| **Standard** |
|
|
513
|
-
| **Complex** | Cross-domain, touches types/auth
|
|
514
|
-
|
|
515
|
-
Default to under-classifying. Escalate mid-task if needed.
|
|
516
|
-
|
|
517
|
-
Call \`knit_get_workflow({phase: "tier"})\` for the full decision aid.`;
|
|
504
|
+
| Tier | When |
|
|
505
|
+
|------|------|
|
|
506
|
+
| **Inquiry** | Read-only ("what", "where", "audit") \u2014 just answer. |
|
|
507
|
+
| **Trivial** | One-line fix \u2014 execute \u2192 verify. |
|
|
508
|
+
| **Standard** | Single-domain bug fix or feature \u2014 research \u2192 execute \u2192 review. |
|
|
509
|
+
| **Complex** | Cross-domain, touches types/auth, high-fanout, or multi-commit arc \u2014 full 6 phases + auto plan mode. |`;
|
|
518
510
|
}
|
|
519
511
|
function generateWorkflowPointer() {
|
|
520
512
|
return `## Workflow on demand
|
|
521
513
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
\`\`\`
|
|
525
|
-
knit_get_workflow({phase: "research"}) // RESEARCH phase details
|
|
526
|
-
knit_get_workflow({phase: "plan"}) // PLAN phase + plan-mode rules
|
|
527
|
-
knit_get_workflow({phase: "execute"}) // EXECUTE + TDD
|
|
528
|
-
knit_get_workflow({phase: "optimize"}) // OPTIMIZE + role briefings
|
|
529
|
-
knit_get_workflow({phase: "review"}) // REVIEW gates
|
|
530
|
-
knit_get_workflow({phase: "learn"}) // LEARN quality gate
|
|
531
|
-
knit_get_workflow({phase: "handoff"}) // session handoff
|
|
532
|
-
knit_get_workflow({phase: "ship"}) // commit + ship + production
|
|
533
|
-
knit_get_workflow({phase: "tdd"}) // RED \u2192 GREEN \u2192 REFACTOR
|
|
534
|
-
knit_get_workflow({phase: "tools"}) // Knit MCP tools reference
|
|
535
|
-
\`\`\`
|
|
536
|
-
|
|
537
|
-
Call with no \`phase\` to list all sections.`;
|
|
538
|
-
}
|
|
539
|
-
function generatePhaseStatus() {
|
|
540
|
-
return `## Phase Status
|
|
541
|
-
|
|
542
|
-
- **Setup:** \u2705 Knit-generated
|
|
543
|
-
- **Active development:** \u{1F680} In progress`;
|
|
514
|
+
Fetch any phase via \`knit_get_workflow({phase})\`. Call with no phase to list available sections.`;
|
|
544
515
|
}
|
|
545
516
|
|
|
546
517
|
export {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/mcp/notifier.ts
|
|
2
|
+
var notifierImpl = null;
|
|
3
|
+
function registerToolsListChangedNotifier(fn) {
|
|
4
|
+
notifierImpl = fn;
|
|
5
|
+
}
|
|
6
|
+
function notifyToolsListChanged() {
|
|
7
|
+
if (!notifierImpl) return;
|
|
8
|
+
try {
|
|
9
|
+
const result = notifierImpl();
|
|
10
|
+
if (result && typeof result.then === "function") {
|
|
11
|
+
result.catch(() => {
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
} catch {
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function __resetNotifierForTests() {
|
|
18
|
+
notifierImpl = null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
registerToolsListChangedNotifier,
|
|
23
|
+
notifyToolsListChanged,
|
|
24
|
+
__resetNotifierForTests
|
|
25
|
+
};
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
installAgentsForProject,
|
|
3
|
+
pruneSessionsByAge
|
|
4
|
+
} from "./chunk-3XR77YJM.js";
|
|
1
5
|
import {
|
|
2
6
|
KNIT_MARKER_START,
|
|
3
7
|
buildKnowledge,
|
|
4
8
|
buildReverseDependencies,
|
|
5
9
|
generateClaudeMd,
|
|
6
10
|
spliceKnitBlock
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import {
|
|
9
|
-
readLearnings
|
|
10
|
-
} from "./chunk-M3YZOJNW.js";
|
|
11
|
-
import {
|
|
12
|
-
installAgentsForProject,
|
|
13
|
-
pruneSessionsByAge
|
|
14
|
-
} from "./chunk-HWEH27E7.js";
|
|
11
|
+
} from "./chunk-SLN5ABF5.js";
|
|
15
12
|
import {
|
|
16
13
|
scanProject
|
|
17
14
|
} from "./chunk-7PPC6IG6.js";
|
|
15
|
+
import {
|
|
16
|
+
readLearnings
|
|
17
|
+
} from "./chunk-M3YZOJNW.js";
|
|
18
18
|
import {
|
|
19
19
|
importFromMarkdown,
|
|
20
20
|
loadKnowledgeBase,
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
sessionsJsonlPath,
|
|
39
39
|
sessionsLogPath,
|
|
40
40
|
teamsPath
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-HBMF62U4.js";
|
|
42
42
|
|
|
43
43
|
// src/mcp/cache.ts
|
|
44
44
|
import { execSync } from "child_process";
|
package/dist/cli.js
CHANGED
|
@@ -25,10 +25,10 @@ async function runCLI() {
|
|
|
25
25
|
const gradient = (await import("gradient-string")).default;
|
|
26
26
|
const chalk = (await import("chalk")).default;
|
|
27
27
|
const { setupCommand } = await import("./setup-5TUUWLIJ.js");
|
|
28
|
-
const { statusCommand } = await import("./status-
|
|
29
|
-
const { refreshCommand } = await import("./refresh-
|
|
30
|
-
const { installAgentsCommand } = await import("./install-agents-
|
|
31
|
-
const { exportCommand } = await import("./export-
|
|
28
|
+
const { statusCommand } = await import("./status-XN6VHO66.js");
|
|
29
|
+
const { refreshCommand } = await import("./refresh-JJUSH2J5.js");
|
|
30
|
+
const { installAgentsCommand } = await import("./install-agents-HXVYULKK.js");
|
|
31
|
+
const { exportCommand } = await import("./export-IKPBLZOO.js");
|
|
32
32
|
const ENGRAM_GRADIENT = gradient(["#7c3aed", "#2563eb", "#06b6d4"]);
|
|
33
33
|
const banner = `
|
|
34
34
|
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
@@ -94,15 +94,24 @@ async function runMCP() {
|
|
|
94
94
|
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
95
95
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
96
96
|
const { ListToolsRequestSchema, CallToolRequestSchema } = await import("@modelcontextprotocol/sdk/types.js");
|
|
97
|
-
const { getBrain, detectProjectRoot, refreshBrain } = await import("./cache-
|
|
98
|
-
const {
|
|
97
|
+
const { getBrain, detectProjectRoot, refreshBrain } = await import("./cache-BVFD5F5Y.js");
|
|
98
|
+
const { getActiveToolDefinitionsForBrain, handleToolCall } = await import("./tools-NZUUKPYI.js");
|
|
99
|
+
const { KNIT_INSTRUCTIONS } = await import("./instructions-33TUHLTK.js");
|
|
100
|
+
const { registerToolsListChangedNotifier } = await import("./notifier-4L27HKHI.js");
|
|
99
101
|
const ROOT_PATH = detectProjectRoot();
|
|
100
102
|
const server = new Server(
|
|
101
103
|
{ name: "knit-brain", version: VERSION },
|
|
102
|
-
{
|
|
104
|
+
{
|
|
105
|
+
capabilities: { tools: { listChanged: true } },
|
|
106
|
+
instructions: KNIT_INSTRUCTIONS
|
|
107
|
+
}
|
|
103
108
|
);
|
|
109
|
+
registerToolsListChangedNotifier(() => {
|
|
110
|
+
void server.sendToolListChanged().catch(() => {
|
|
111
|
+
});
|
|
112
|
+
});
|
|
104
113
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
105
|
-
tools:
|
|
114
|
+
tools: getActiveToolDefinitionsForBrain(getBrain(ROOT_PATH))
|
|
106
115
|
}));
|
|
107
116
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
108
117
|
const { name, arguments: params } = request.params;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getRecentGlobalLearnings
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TRZ3LD6B.js";
|
|
4
4
|
import {
|
|
5
5
|
loadKnowledgeBase
|
|
6
6
|
} from "./chunk-BAUQEFYY.js";
|
|
7
7
|
import {
|
|
8
8
|
globalLearningsPath,
|
|
9
9
|
knitRoot
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HBMF62U4.js";
|
|
11
11
|
|
|
12
12
|
// src/commands/export.ts
|
|
13
13
|
import { existsSync, mkdirSync, readdirSync, writeFileSync, statSync } from "fs";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getBrain
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-5F7DSVJY.js";
|
|
5
|
-
import "./chunk-M3YZOJNW.js";
|
|
3
|
+
} from "./chunk-X6TGTET3.js";
|
|
6
4
|
import {
|
|
7
5
|
installAgentsForProject
|
|
8
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-3XR77YJM.js";
|
|
7
|
+
import "./chunk-SLN5ABF5.js";
|
|
9
8
|
import "./chunk-7PPC6IG6.js";
|
|
9
|
+
import "./chunk-M3YZOJNW.js";
|
|
10
10
|
import "./chunk-BAUQEFYY.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-HBMF62U4.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/install-agents.ts
|
|
14
14
|
import { existsSync } from "fs";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/mcp/instructions.ts
|
|
2
|
+
var KNIT_INSTRUCTIONS = `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
|
+
export {
|
|
28
|
+
KNIT_INSTRUCTIONS
|
|
29
|
+
};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildKnowledge,
|
|
3
3
|
generateClaudeMd
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
findFalsePositives
|
|
7
|
-
} from "./chunk-M3YZOJNW.js";
|
|
4
|
+
} from "./chunk-SLN5ABF5.js";
|
|
8
5
|
import {
|
|
9
6
|
scanProject
|
|
10
7
|
} from "./chunk-7PPC6IG6.js";
|
|
8
|
+
import {
|
|
9
|
+
findFalsePositives
|
|
10
|
+
} from "./chunk-M3YZOJNW.js";
|
|
11
11
|
import {
|
|
12
12
|
knowledgePath,
|
|
13
13
|
learningsDir,
|
|
14
14
|
projectDataDir
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-HBMF62U4.js";
|
|
16
16
|
|
|
17
17
|
// src/commands/refresh.ts
|
|
18
18
|
import { readFileSync, readdirSync, writeFileSync, existsSync } from "fs";
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
pruneSessionsByAge,
|
|
6
6
|
searchSessions,
|
|
7
7
|
sessionCount
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3XR77YJM.js";
|
|
9
9
|
import {
|
|
10
10
|
scanProject
|
|
11
11
|
} from "./chunk-7PPC6IG6.js";
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
buildGlobalLearning,
|
|
15
15
|
getRecentGlobalLearnings,
|
|
16
16
|
searchGlobalLearnings
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-TRZ3LD6B.js";
|
|
18
18
|
import {
|
|
19
19
|
addEntry,
|
|
20
20
|
getFalsePositives,
|
|
@@ -26,17 +26,22 @@ import {
|
|
|
26
26
|
import {
|
|
27
27
|
canonicalRepoRoot,
|
|
28
28
|
classificationMarkerPath,
|
|
29
|
+
featuresConfigPath,
|
|
29
30
|
knowledgebasePath,
|
|
30
31
|
learningsDir,
|
|
32
|
+
projectAgentsDir,
|
|
31
33
|
projectDataDir,
|
|
32
34
|
protocolConfigPath,
|
|
33
35
|
sessionsLogPath,
|
|
34
36
|
teamsPath,
|
|
35
37
|
worktreesRegistryPath
|
|
36
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-HBMF62U4.js";
|
|
39
|
+
import {
|
|
40
|
+
notifyToolsListChanged
|
|
41
|
+
} from "./chunk-WMESQUZU.js";
|
|
37
42
|
|
|
38
43
|
// src/mcp/handlers.ts
|
|
39
|
-
import { writeFileSync as writeFileSync4, readFileSync as readFileSync4, readdirSync, existsSync as existsSync4 } from "fs";
|
|
44
|
+
import { writeFileSync as writeFileSync4, readFileSync as readFileSync4, readdirSync, existsSync as existsSync4, renameSync as renameSync2, unlinkSync } from "fs";
|
|
40
45
|
import { join as join2 } from "path";
|
|
41
46
|
import { statSync as statSync2 } from "fs";
|
|
42
47
|
|
|
@@ -692,6 +697,108 @@ function redactSecrets(input) {
|
|
|
692
697
|
return out;
|
|
693
698
|
}
|
|
694
699
|
|
|
700
|
+
// src/mcp/features.ts
|
|
701
|
+
var TOOL_REGISTRY = [
|
|
702
|
+
// ── Tier 1 — Memory + retrieval (8) ─────────────────────────────
|
|
703
|
+
{ tool: "knit_load_session", tier: 1, category: "memory", rationale: "Session-start primer; universal" },
|
|
704
|
+
{ tool: "knit_search_learnings", tier: 1, category: "memory", rationale: "Project-local learnings lookup; universal" },
|
|
705
|
+
{ tool: "knit_search_global_learnings", tier: 1, category: "memory", rationale: "Cross-project learnings pool; seeded by default" },
|
|
706
|
+
{ tool: "knit_search_sessions", tier: 1, category: "memory", rationale: '"Have I done this before?" \u2014 universal' },
|
|
707
|
+
{ tool: "knit_record_learning", tier: 1, category: "memory", rationale: "LEARN step persistence; universal" },
|
|
708
|
+
{ tool: "knit_record_global_learning", tier: 1, category: "memory", rationale: "Cross-project memory write; useful from day one" },
|
|
709
|
+
{ tool: "knit_save_session_summary", tier: 1, category: "memory", rationale: "Session-end persistence; universal" },
|
|
710
|
+
{ tool: "knit_save_handoff", tier: 1, category: "memory", rationale: "Context-degradation handoff; universal" },
|
|
711
|
+
// ── Tier 1 — Knowledge graph (5) ────────────────────────────────
|
|
712
|
+
{ tool: "knit_query_imports", tier: 1, category: "knowledge-graph", rationale: "Core differentiator; returns empty honestly on docs-only projects" },
|
|
713
|
+
{ tool: "knit_query_exports", tier: 1, category: "knowledge-graph", rationale: "Core differentiator; same honest-empty behavior" },
|
|
714
|
+
{ tool: "knit_query_dependents", tier: 1, category: "knowledge-graph", rationale: "Core differentiator" },
|
|
715
|
+
{ tool: "knit_query_tests", tier: 1, category: "knowledge-graph", rationale: "Core differentiator" },
|
|
716
|
+
{ tool: "knit_find_fanout", tier: 1, category: "knowledge-graph", rationale: "Core differentiator" },
|
|
717
|
+
// ── Tier 1 — Workflow + classification (4) ──────────────────────
|
|
718
|
+
{ tool: "knit_classify_task", tier: 1, category: "workflow", rationale: "Tier router; called before any non-trivial task" },
|
|
719
|
+
{ tool: "knit_build_context", tier: 1, category: "workflow", rationale: "Domain Context Object builder" },
|
|
720
|
+
{ tool: "knit_get_workflow", tier: 1, category: "workflow", rationale: "On-demand phase depth fetcher" },
|
|
721
|
+
{ tool: "knit_get_suggestions", tier: 1, category: "workflow", rationale: "Adaptive warnings from past patterns" },
|
|
722
|
+
// ── Tier 1 — False positives + reflection (3) ───────────────────
|
|
723
|
+
{ tool: "knit_record_false_positive", tier: 1, category: "fp-reflection", rationale: "Universal \u2014 reviewer agents flag FPs on any project" },
|
|
724
|
+
{ tool: "knit_get_false_positives", tier: 1, category: "fp-reflection", rationale: "Universal" },
|
|
725
|
+
{ tool: "knit_reflect", tier: 1, category: "fp-reflection", rationale: 'Returns "not enough data" on sparse projects; always available' },
|
|
726
|
+
// ── Tier 1 — Protocol Guard config (2) ──────────────────────────
|
|
727
|
+
{ tool: "knit_set_protocol_strictness", tier: 1, category: "protocol-config", rationale: "Universal \u2014 every install ships Protocol Guard" },
|
|
728
|
+
{ tool: "knit_get_protocol_strictness", tier: 1, category: "protocol-config", rationale: "Universal" },
|
|
729
|
+
// ── Tier 1 — Diagnostics + meta (4) ─────────────────────────────
|
|
730
|
+
{ tool: "knit_brain_status", tier: 1, category: "diagnostics", rationale: "Health + token-accounting; universal" },
|
|
731
|
+
{ tool: "knit_list_features", tier: 1, category: "diagnostics", rationale: "The discoverability escape hatch itself" },
|
|
732
|
+
{ tool: "knit_enable_feature", tier: 1, category: "diagnostics", rationale: "Flip on a Tier-2/3 feature flag \u2014 must always be reachable so hidden tools are recoverable" },
|
|
733
|
+
{ tool: "knit_disable_feature", tier: 1, category: "diagnostics", rationale: "Flip off a previously-enabled feature flag" },
|
|
734
|
+
// ── Tier 2 — Team worktrees (9) ─────────────────────────────────
|
|
735
|
+
{ tool: "knit_spawn_team_worktree", tier: 2, category: "teams", rationale: "Multi-domain parallel write orchestration", enable_via: 'knit_enable_feature("teams") or auto-exposed when \u22653 domains detected' },
|
|
736
|
+
{ tool: "knit_finalize_team_worktree", tier: 2, category: "teams", rationale: "Merge/discard a team worktree", enable_via: 'knit_enable_feature("teams")' },
|
|
737
|
+
{ tool: "knit_list_team_worktrees", tier: 2, category: "teams", rationale: "Active team worktree registry", enable_via: 'knit_enable_feature("teams")' },
|
|
738
|
+
{ tool: "knit_define_team", tier: 2, category: "teams", rationale: "Custom team definition", enable_via: 'knit_enable_feature("teams")' },
|
|
739
|
+
{ tool: "knit_get_teams", tier: 2, category: "teams", rationale: "List configured teams", enable_via: 'knit_enable_feature("teams")' },
|
|
740
|
+
{ tool: "knit_get_team_prompt", tier: 2, category: "teams", rationale: "Team-specific agent prompt", enable_via: 'knit_enable_feature("teams")' },
|
|
741
|
+
{ tool: "knit_start_team_review", tier: 2, category: "teams", rationale: "Begin a multi-team review", enable_via: 'knit_enable_feature("teams")' },
|
|
742
|
+
{ tool: "knit_post_team_findings", tier: 2, category: "teams", rationale: "Submit team findings to shared board", enable_via: 'knit_enable_feature("teams")' },
|
|
743
|
+
{ tool: "knit_get_board_summary", tier: 2, category: "teams", rationale: "Roll up team findings", enable_via: 'knit_enable_feature("teams")' },
|
|
744
|
+
// ── Tier 2 — Subagents (1) ──────────────────────────────────────
|
|
745
|
+
{ tool: "knit_install_agent", tier: 2, category: "subagents", rationale: "VoltAgent subagent installer", enable_via: 'Auto-exposed when .claude/agents/ exists or knit_enable_feature("subagents")' },
|
|
746
|
+
// ── Tier 3 — Admin (1) ──────────────────────────────────────────
|
|
747
|
+
{ tool: "knit_prune_sessions", tier: 3, category: "admin", rationale: "Manual session pruning; auto-prune handles this normally", enable_via: 'knit_enable_feature("admin")' },
|
|
748
|
+
// ── Tier 3 — One-time setup (1) ─────────────────────────────────
|
|
749
|
+
{ tool: "knit_setup_project", tier: 3, category: "admin", rationale: "Initial bootstrap only; hidden after first run", enable_via: 'knit_enable_feature("admin") or pass through auto-init' }
|
|
750
|
+
];
|
|
751
|
+
function isToolActive(info, shape) {
|
|
752
|
+
if (info.tier === 1) return true;
|
|
753
|
+
if (info.tier === 3) return shape.enabledFeatures.has("admin");
|
|
754
|
+
if (info.category === "teams") {
|
|
755
|
+
return shape.domainCount >= 3 || shape.enabledFeatures.has("teams");
|
|
756
|
+
}
|
|
757
|
+
if (info.category === "subagents") {
|
|
758
|
+
return shape.hasInstalledSubagents || shape.enabledFeatures.has("subagents");
|
|
759
|
+
}
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
function computeFeatureListing(shape) {
|
|
763
|
+
const active = [];
|
|
764
|
+
const available = [];
|
|
765
|
+
const by_category = {
|
|
766
|
+
memory: { active: 0, available: 0 },
|
|
767
|
+
"knowledge-graph": { active: 0, available: 0 },
|
|
768
|
+
workflow: { active: 0, available: 0 },
|
|
769
|
+
"fp-reflection": { active: 0, available: 0 },
|
|
770
|
+
"protocol-config": { active: 0, available: 0 },
|
|
771
|
+
diagnostics: { active: 0, available: 0 },
|
|
772
|
+
teams: { active: 0, available: 0 },
|
|
773
|
+
subagents: { active: 0, available: 0 },
|
|
774
|
+
admin: { active: 0, available: 0 }
|
|
775
|
+
};
|
|
776
|
+
for (const info of TOOL_REGISTRY) {
|
|
777
|
+
if (isToolActive(info, shape)) {
|
|
778
|
+
active.push({ name: info.tool, tier: info.tier, category: info.category });
|
|
779
|
+
by_category[info.category].active++;
|
|
780
|
+
} else {
|
|
781
|
+
available.push({
|
|
782
|
+
name: info.tool,
|
|
783
|
+
tier: info.tier,
|
|
784
|
+
category: info.category,
|
|
785
|
+
reason: info.rationale,
|
|
786
|
+
enable_via: info.enable_via
|
|
787
|
+
});
|
|
788
|
+
by_category[info.category].available++;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return {
|
|
792
|
+
active,
|
|
793
|
+
available,
|
|
794
|
+
totals: { active: active.length, available: available.length, total: TOOL_REGISTRY.length },
|
|
795
|
+
by_category
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function isEnableableFeature(name) {
|
|
799
|
+
return name === "teams" || name === "subagents" || name === "admin";
|
|
800
|
+
}
|
|
801
|
+
|
|
695
802
|
// src/engine/teams.ts
|
|
696
803
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
697
804
|
import { dirname as dirname2 } from "path";
|
|
@@ -1059,6 +1166,114 @@ function handleBrainStatus(_params, brain) {
|
|
|
1059
1166
|
instruction: "Brain is ready. Next: call knit_classify_task with the files you plan to touch to get your tier and phases."
|
|
1060
1167
|
});
|
|
1061
1168
|
}
|
|
1169
|
+
function loadEnabledFeatures(rootPath) {
|
|
1170
|
+
const enabled = /* @__PURE__ */ new Set();
|
|
1171
|
+
try {
|
|
1172
|
+
const path = featuresConfigPath(rootPath);
|
|
1173
|
+
if (!existsSync4(path)) return enabled;
|
|
1174
|
+
const parsed = JSON.parse(readFileSync4(path, "utf-8"));
|
|
1175
|
+
if (Array.isArray(parsed?.enabled)) {
|
|
1176
|
+
for (const name of parsed.enabled) {
|
|
1177
|
+
if (isEnableableFeature(name)) enabled.add(name);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
} catch {
|
|
1181
|
+
}
|
|
1182
|
+
return enabled;
|
|
1183
|
+
}
|
|
1184
|
+
function saveEnabledFeatures(rootPath, enabled) {
|
|
1185
|
+
const path = featuresConfigPath(rootPath);
|
|
1186
|
+
const payload = {
|
|
1187
|
+
enabled: [...enabled].sort(),
|
|
1188
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1189
|
+
};
|
|
1190
|
+
const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
1191
|
+
try {
|
|
1192
|
+
writeFileSync4(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
1193
|
+
renameSync2(tmpPath, path);
|
|
1194
|
+
} catch (err) {
|
|
1195
|
+
try {
|
|
1196
|
+
unlinkSync(tmpPath);
|
|
1197
|
+
} catch {
|
|
1198
|
+
}
|
|
1199
|
+
throw err;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
function detectProjectShape(brain) {
|
|
1203
|
+
return {
|
|
1204
|
+
hasAnalyzableCode: brain.knowledge.summary.totalFiles >= 10,
|
|
1205
|
+
domainCount: brain.config.domains?.length ?? 0,
|
|
1206
|
+
hasInstalledSubagents: existsSync4(projectAgentsDir(brain.rootPath)),
|
|
1207
|
+
sessionCount: sessionCount(brain.rootPath),
|
|
1208
|
+
enabledFeatures: loadEnabledFeatures(brain.rootPath)
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
function handleListFeatures(_params, brain) {
|
|
1212
|
+
const shape = detectProjectShape(brain);
|
|
1213
|
+
const listing = computeFeatureListing(shape);
|
|
1214
|
+
return JSON.stringify({
|
|
1215
|
+
...listing,
|
|
1216
|
+
project_shape: {
|
|
1217
|
+
has_analyzable_code: shape.hasAnalyzableCode,
|
|
1218
|
+
domain_count: shape.domainCount,
|
|
1219
|
+
has_installed_subagents: shape.hasInstalledSubagents,
|
|
1220
|
+
session_count: shape.sessionCount,
|
|
1221
|
+
enabled_features: [...shape.enabledFeatures]
|
|
1222
|
+
},
|
|
1223
|
+
instruction: 'If a tool you want is in `available` rather than `active`, the `enable_via` field tells you how to switch it on. Most commonly: knit_enable_feature({ feature: "teams" | "subagents" | "admin" }).'
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
function handleEnableFeature(params, brain) {
|
|
1227
|
+
const feature = (params.feature || "").trim().toLowerCase();
|
|
1228
|
+
if (!isEnableableFeature(feature)) {
|
|
1229
|
+
return JSON.stringify({
|
|
1230
|
+
status: "error",
|
|
1231
|
+
error: `Invalid feature: "${params.feature}". Valid values: teams, subagents, admin.`
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
const enabled = loadEnabledFeatures(brain.rootPath);
|
|
1235
|
+
const wasAlreadyOn = enabled.has(feature);
|
|
1236
|
+
enabled.add(feature);
|
|
1237
|
+
if (!wasAlreadyOn) {
|
|
1238
|
+
saveEnabledFeatures(brain.rootPath, enabled);
|
|
1239
|
+
notifyToolsListChanged();
|
|
1240
|
+
}
|
|
1241
|
+
return JSON.stringify({
|
|
1242
|
+
status: wasAlreadyOn ? "already-enabled" : "enabled",
|
|
1243
|
+
feature,
|
|
1244
|
+
enabled_features: [...enabled].sort(),
|
|
1245
|
+
instruction: wasAlreadyOn ? "Already enabled. Call knit_list_features to see the active tool list." : "Tools list updated for this session. The newly-enabled tools should be available immediately \u2014 call knit_list_features to confirm."
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
function handleDisableFeature(params, brain) {
|
|
1249
|
+
const feature = (params.feature || "").trim().toLowerCase();
|
|
1250
|
+
if (!isEnableableFeature(feature)) {
|
|
1251
|
+
return JSON.stringify({
|
|
1252
|
+
status: "error",
|
|
1253
|
+
error: `Invalid feature: "${params.feature}". Valid values: teams, subagents, admin.`
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
const enabled = loadEnabledFeatures(brain.rootPath);
|
|
1257
|
+
const wasOn = enabled.delete(feature);
|
|
1258
|
+
if (wasOn) {
|
|
1259
|
+
saveEnabledFeatures(brain.rootPath, enabled);
|
|
1260
|
+
notifyToolsListChanged();
|
|
1261
|
+
}
|
|
1262
|
+
return JSON.stringify({
|
|
1263
|
+
status: wasOn ? "disabled" : "already-disabled",
|
|
1264
|
+
feature,
|
|
1265
|
+
enabled_features: [...enabled].sort(),
|
|
1266
|
+
instruction: wasOn ? "Tools list updated for this session. Opt-in-only tools for this feature are no longer visible." : "Already disabled. Auto-exposed tools (e.g. teams when \u22653 domains) stay visible regardless of this flag."
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
function detectsInquiryIntent(description) {
|
|
1270
|
+
if (!description) return false;
|
|
1271
|
+
const inquiryStart = /^\s*(what|where|how|why|when|which|who|can|could|should|does|do|is|are|will|would|tell\s+me|show\s+me|find|list|status\s+of|audit|explain|investigate|analyze|review|describe|summari[sz]e|inspect)\b/i;
|
|
1272
|
+
const inquiryVerb = /\b(audit|explain|investigate|analy[sz]e|review|examine|describe|summari[sz]e|enumerate|inspect)\b/i;
|
|
1273
|
+
const actionDirective = /\b(fix|implement|build|add|refactor|ship|deploy|write|create|update|modify|change|edit|migrate|rename|delete|remove|install|setup|configure|merge|publish|release|patch)\s+(this|that|it|the|a|an|all|every|my|our|your)\b/i;
|
|
1274
|
+
if (actionDirective.test(description)) return false;
|
|
1275
|
+
return inquiryStart.test(description) || inquiryVerb.test(description);
|
|
1276
|
+
}
|
|
1062
1277
|
function handleClassifyTask(params, brain) {
|
|
1063
1278
|
const rawFiles = (params.files_to_touch || "").split(",").map((f) => f.trim()).filter(Boolean);
|
|
1064
1279
|
const files = rawFiles.filter((f) => f !== "unknown");
|
|
@@ -1069,6 +1284,27 @@ function handleClassifyTask(params, brain) {
|
|
|
1069
1284
|
const importers = brain.reverseDeps[file] || [];
|
|
1070
1285
|
if (importers.length >= 3) crossDomainRipple.push(`${file} is high-fanout (${importers.length} dependents)`);
|
|
1071
1286
|
}
|
|
1287
|
+
if (detectsInquiryIntent(params.description || "")) {
|
|
1288
|
+
try {
|
|
1289
|
+
writeClassificationMarker(brain.rootPath, {
|
|
1290
|
+
turnId: `${Date.now()}-${process.pid}`,
|
|
1291
|
+
classifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1292
|
+
tier: "inquiry",
|
|
1293
|
+
files
|
|
1294
|
+
});
|
|
1295
|
+
} catch {
|
|
1296
|
+
}
|
|
1297
|
+
return JSON.stringify({
|
|
1298
|
+
tier: "inquiry",
|
|
1299
|
+
affected_domains: [...domains],
|
|
1300
|
+
phases: [],
|
|
1301
|
+
files_count: files.length,
|
|
1302
|
+
cross_domain_ripple: crossDomainRipple,
|
|
1303
|
+
auto_plan_mode: false,
|
|
1304
|
+
instruction: "Read-only task. Answer directly \u2014 no plan mode, no LEARN unless something durable surfaced. If scope grows into writes, re-classify with knit_classify_task.",
|
|
1305
|
+
reasoning: `Inquiry: read-only intent detected${files.length > 0 ? `, ${files.length} file(s) referenced for context` : ""}`
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1072
1308
|
const isTypes = files.some((f) => f.includes("types") || f.includes("schema"));
|
|
1073
1309
|
const isAuth = files.some((f) => f.includes("auth") || f.includes("security"));
|
|
1074
1310
|
const isNewProject = files.length === 0 || rawFiles.includes("unknown");
|
|
@@ -1085,14 +1321,21 @@ function handleClassifyTask(params, brain) {
|
|
|
1085
1321
|
});
|
|
1086
1322
|
} catch {
|
|
1087
1323
|
}
|
|
1088
|
-
|
|
1324
|
+
const verbose = params.verbose === "true" || params.verbose === "1";
|
|
1325
|
+
const base = {
|
|
1089
1326
|
tier: tier2,
|
|
1090
1327
|
affected_domains: [...domains],
|
|
1091
1328
|
phases: phases2,
|
|
1329
|
+
auto_plan_mode: tier2 === "complex",
|
|
1330
|
+
instruction
|
|
1331
|
+
};
|
|
1332
|
+
if (!verbose) {
|
|
1333
|
+
return JSON.stringify(base);
|
|
1334
|
+
}
|
|
1335
|
+
return JSON.stringify({
|
|
1336
|
+
...base,
|
|
1092
1337
|
files_count: files.length,
|
|
1093
1338
|
cross_domain_ripple: crossDomainRipple,
|
|
1094
|
-
auto_plan_mode: tier2 === "complex",
|
|
1095
|
-
instruction,
|
|
1096
1339
|
reasoning: tier2 === "complex" ? `Complex: ${domains.size} domains affected${isTypes ? ", touches shared types" : ""}${isAuth ? ", security-sensitive" : ""}` : tier2 === "standard" ? `Standard: ${domains.size} domain(s), ${files.length} file(s)` : `Trivial: 1 domain, simple change`
|
|
1097
1340
|
});
|
|
1098
1341
|
}
|
|
@@ -1493,7 +1736,15 @@ function handleSearchGlobalLearnings(params, _brain) {
|
|
|
1493
1736
|
instruction: matches.length === 0 ? "No cross-project matches. This area might be new across all your projects." : `Found ${matches.length} cross-project learning(s). Review before duplicating work.`
|
|
1494
1737
|
});
|
|
1495
1738
|
}
|
|
1496
|
-
function
|
|
1739
|
+
function parseLoadSessionInclude(raw) {
|
|
1740
|
+
if (!raw) return /* @__PURE__ */ new Set();
|
|
1741
|
+
return new Set(
|
|
1742
|
+
raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean)
|
|
1743
|
+
);
|
|
1744
|
+
}
|
|
1745
|
+
function handleLoadSession(params, brain) {
|
|
1746
|
+
const include = parseLoadSessionInclude(params.include);
|
|
1747
|
+
const wantAll = include.has("all");
|
|
1497
1748
|
const root = brain.rootPath;
|
|
1498
1749
|
const sessionsFile = sessionsLogPath(root);
|
|
1499
1750
|
let lastSession = null;
|
|
@@ -1502,45 +1753,67 @@ function handleLoadSession(_params, brain) {
|
|
|
1502
1753
|
const sessions = content.split(/^## Session/m).slice(1);
|
|
1503
1754
|
if (sessions.length > 0) {
|
|
1504
1755
|
const last = sessions[sessions.length - 1].trim();
|
|
1505
|
-
lastSession = last.slice(0,
|
|
1756
|
+
lastSession = last.slice(0, 200);
|
|
1506
1757
|
}
|
|
1507
1758
|
}
|
|
1508
1759
|
const handoffPath = join2(root, "handoff.md");
|
|
1509
1760
|
let handoff2 = null;
|
|
1510
1761
|
if (existsSync4(handoffPath)) {
|
|
1511
|
-
handoff2 = readFileSync4(handoffPath, "utf-8").slice(0,
|
|
1762
|
+
handoff2 = readFileSync4(handoffPath, "utf-8").slice(0, 1500);
|
|
1512
1763
|
}
|
|
1513
|
-
const
|
|
1514
|
-
const
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
const t = JSON.parse(readFileSync4(teamsFile, "utf-8"));
|
|
1520
|
-
teams = t.map((team) => team.name);
|
|
1521
|
-
} catch {
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
const knowledge = {
|
|
1764
|
+
const learningsLimit = wantAll || include.has("full_learnings") ? 5 : 3;
|
|
1765
|
+
const topLearnings = brain.knowledgeBase.entries.filter((e) => e.accessCount > 0).sort((a, b) => b.accessCount - a.accessCount).slice(0, learningsLimit).map((e) => ({ summary: e.summary, lesson: e.lesson, tags: e.tags, accessed: e.accessCount }));
|
|
1766
|
+
const fpsLimit = wantAll || include.has("full_learnings") ? 50 : 5;
|
|
1767
|
+
const fps = brain.knowledgeBase.entries.filter((e) => e.tags.includes("#false-positive")).slice(0, fpsLimit).map((e) => ({ summary: e.summary, lesson: e.lesson }));
|
|
1768
|
+
const wantFullKnowledge = wantAll || include.has("full_knowledge");
|
|
1769
|
+
const knowledge = wantFullKnowledge ? {
|
|
1525
1770
|
files: brain.knowledge.summary.totalFiles,
|
|
1526
1771
|
imports: Object.keys(brain.knowledge.importGraph).length,
|
|
1527
1772
|
high_fanout: brain.knowledge.summary.highFanoutFiles,
|
|
1528
1773
|
untested: brain.knowledge.summary.untestedFiles.slice(0, 5)
|
|
1774
|
+
} : {
|
|
1775
|
+
files: brain.knowledge.summary.totalFiles,
|
|
1776
|
+
imports: Object.keys(brain.knowledge.importGraph).length,
|
|
1777
|
+
high_fanout_count: brain.knowledge.summary.highFanoutFiles.length,
|
|
1778
|
+
untested_count: brain.knowledge.summary.untestedFiles.length
|
|
1529
1779
|
};
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1780
|
+
let teams;
|
|
1781
|
+
if (wantAll || include.has("teams")) {
|
|
1782
|
+
const teamsFile = teamsPath(root);
|
|
1783
|
+
if (existsSync4(teamsFile)) {
|
|
1784
|
+
try {
|
|
1785
|
+
const t = JSON.parse(readFileSync4(teamsFile, "utf-8"));
|
|
1786
|
+
teams = t.map((team) => team.name);
|
|
1787
|
+
} catch {
|
|
1788
|
+
teams = [];
|
|
1789
|
+
}
|
|
1790
|
+
} else {
|
|
1791
|
+
teams = [];
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
let metrics;
|
|
1795
|
+
if (wantAll || include.has("metrics")) {
|
|
1796
|
+
metrics = {
|
|
1797
|
+
total_sessions: brain.knowledgeBase.metrics.totalSessions,
|
|
1798
|
+
total_learnings: brain.knowledgeBase.entries.length,
|
|
1799
|
+
cache_hits: brain.knowledgeBase.metrics.cacheHits
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
let recentSessions;
|
|
1803
|
+
if (wantAll || include.has("recent_sessions")) {
|
|
1804
|
+
recentSessions = getRecentSessions(root, 3).map((s) => ({
|
|
1805
|
+
date: s.date,
|
|
1806
|
+
branch: s.branch ?? null,
|
|
1807
|
+
summary: s.summary ?? "",
|
|
1808
|
+
tags: s.tags ?? [],
|
|
1809
|
+
outcome: s.outcome
|
|
1810
|
+
}));
|
|
1811
|
+
}
|
|
1812
|
+
let patterns;
|
|
1813
|
+
if (wantAll || include.has("patterns")) {
|
|
1814
|
+
patterns = reflect(brain.knowledgeBase).slice(0, 3).map((p) => ({ type: p.type, description: p.description, confidence: p.confidence }));
|
|
1815
|
+
}
|
|
1816
|
+
const response = {
|
|
1544
1817
|
session_context: {
|
|
1545
1818
|
last_session: lastSession,
|
|
1546
1819
|
handoff: handoff2,
|
|
@@ -1549,16 +1822,17 @@ function handleLoadSession(_params, brain) {
|
|
|
1549
1822
|
intelligence: {
|
|
1550
1823
|
top_learnings: topLearnings,
|
|
1551
1824
|
false_positives: fps,
|
|
1552
|
-
patterns
|
|
1825
|
+
...patterns !== void 0 ? { patterns } : {}
|
|
1553
1826
|
},
|
|
1554
1827
|
project: {
|
|
1555
1828
|
knowledge,
|
|
1556
|
-
teams,
|
|
1557
|
-
metrics,
|
|
1558
|
-
recent_sessions: recentSessions
|
|
1829
|
+
...teams !== void 0 ? { teams } : {},
|
|
1830
|
+
...metrics !== void 0 ? { metrics } : {},
|
|
1831
|
+
...recentSessions !== void 0 ? { recent_sessions: recentSessions } : {}
|
|
1559
1832
|
},
|
|
1560
|
-
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
|
|
1561
|
-
}
|
|
1833
|
+
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."
|
|
1834
|
+
};
|
|
1835
|
+
return JSON.stringify(response);
|
|
1562
1836
|
}
|
|
1563
1837
|
function handleSaveSessionSummary(params, brain) {
|
|
1564
1838
|
const validOutcomes = ["shipped", "wip", "failed", "unknown"];
|
|
@@ -1741,73 +2015,73 @@ function getToolDefinitions() {
|
|
|
1741
2015
|
// ── Query (read the brain) ───────────────────────────────────
|
|
1742
2016
|
{
|
|
1743
2017
|
name: "knit_query_imports",
|
|
1744
|
-
description: "
|
|
2018
|
+
description: "Reverse deps for a file \u2014 who imports it.",
|
|
1745
2019
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
1746
2020
|
},
|
|
1747
2021
|
{
|
|
1748
2022
|
name: "knit_query_dependents",
|
|
1749
|
-
description: "
|
|
2023
|
+
description: "Forward deps for a file \u2014 what it imports.",
|
|
1750
2024
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
1751
2025
|
},
|
|
1752
2026
|
{
|
|
1753
2027
|
name: "knit_query_exports",
|
|
1754
|
-
description: "
|
|
2028
|
+
description: "Exports from a file: functions, classes, types, constants.",
|
|
1755
2029
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
1756
2030
|
},
|
|
1757
2031
|
{
|
|
1758
2032
|
name: "knit_query_tests",
|
|
1759
|
-
description: '
|
|
2033
|
+
description: 'Tests for a file, or list all untested files with filter="untested".',
|
|
1760
2034
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path (optional)." }, filter: { type: "string", description: '"untested" to list all untested files.' } } }
|
|
1761
2035
|
},
|
|
1762
2036
|
{
|
|
1763
2037
|
name: "knit_find_fanout",
|
|
1764
|
-
description: "High-fanout files \u2014 imported by many others.
|
|
2038
|
+
description: "High-fanout files \u2014 imported by many others.",
|
|
1765
2039
|
inputSchema: { type: "object", properties: { min_importers: { type: "string", description: "Minimum importers to qualify (default: 3)." } } }
|
|
1766
2040
|
},
|
|
1767
2041
|
{
|
|
1768
2042
|
name: "knit_search_learnings",
|
|
1769
|
-
description: "Search
|
|
2043
|
+
description: "Search learnings by domain tag.",
|
|
1770
2044
|
inputSchema: { type: "object", properties: { domains: { type: "string", description: "Comma-separated domain tags." } }, required: ["domains"] }
|
|
1771
2045
|
},
|
|
1772
2046
|
{
|
|
1773
2047
|
name: "knit_get_false_positives",
|
|
1774
|
-
description: "
|
|
2048
|
+
description: "List confirmed non-issues to suppress in review prompts.",
|
|
1775
2049
|
inputSchema: { type: "object", properties: {} }
|
|
1776
2050
|
},
|
|
1777
2051
|
{
|
|
1778
2052
|
name: "knit_brain_status",
|
|
1779
|
-
description: "Brain health
|
|
2053
|
+
description: "Brain health: learnings, hit rate, CLAUDE.md size, session count.",
|
|
1780
2054
|
inputSchema: { type: "object", properties: {} }
|
|
1781
2055
|
},
|
|
1782
2056
|
// ── Update (write to the brain) ──────────────────────────────
|
|
1783
2057
|
{
|
|
1784
2058
|
name: "knit_classify_task",
|
|
1785
|
-
description: "Call first on every task.
|
|
1786
|
-
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." } }, required: ["files_to_touch"] }
|
|
2059
|
+
description: "Call first on every task. Returns tier (inquiry/trivial/standard/complex), phases, auto_plan_mode.",
|
|
2060
|
+
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).' } }, required: ["files_to_touch"] }
|
|
1787
2061
|
},
|
|
1788
2062
|
{
|
|
1789
2063
|
name: "knit_build_context",
|
|
1790
|
-
description: "Build
|
|
2064
|
+
description: "Build the Domain Context Object: affected domains, ripple, pitfalls, false positives.",
|
|
1791
2065
|
inputSchema: { type: "object", properties: { files_to_touch: { type: "string", description: "Comma-separated files." } }, required: ["files_to_touch"] }
|
|
1792
2066
|
},
|
|
1793
2067
|
{
|
|
1794
2068
|
name: "knit_record_learning",
|
|
1795
|
-
description: "Record a non-obvious, reusable insight.
|
|
2069
|
+
description: "Record a non-obvious, reusable insight. Skip if a future search wouldn't be glad it exists.",
|
|
1796
2070
|
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"] }
|
|
1797
2071
|
},
|
|
1798
2072
|
{
|
|
1799
2073
|
name: "knit_record_false_positive",
|
|
1800
|
-
description: "Mark a finding as
|
|
2074
|
+
description: "Mark a finding as confirmed non-issue so future reviewers suppress it.",
|
|
1801
2075
|
inputSchema: { type: "object", properties: { summary: { type: "string", description: "What was flagged." }, reason: { type: "string", description: "Why it's not a real issue." }, tags: { type: "string", description: "Domain tags." } }, required: ["summary", "reason"] }
|
|
1802
2076
|
},
|
|
1803
2077
|
{
|
|
1804
2078
|
name: "knit_save_handoff",
|
|
1805
|
-
description: "Save state for the next session
|
|
2079
|
+
description: "Save state for the next session. failed_attempts is the load-bearing field.",
|
|
1806
2080
|
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"] }
|
|
1807
2081
|
},
|
|
1808
2082
|
{
|
|
1809
2083
|
name: "knit_setup_project",
|
|
1810
|
-
description: "
|
|
2084
|
+
description: "Bootstrap domain teams for a non-code project (research/legal/marketing).",
|
|
1811
2085
|
inputSchema: {
|
|
1812
2086
|
type: "object",
|
|
1813
2087
|
properties: {
|
|
@@ -1822,12 +2096,12 @@ function getToolDefinitions() {
|
|
|
1822
2096
|
// ── Teams (parallel review board) ────────────────────────────
|
|
1823
2097
|
{
|
|
1824
2098
|
name: "knit_get_teams",
|
|
1825
|
-
description: "List
|
|
2099
|
+
description: "List teams configured for this project.",
|
|
1826
2100
|
inputSchema: { type: "object", properties: {} }
|
|
1827
2101
|
},
|
|
1828
2102
|
{
|
|
1829
2103
|
name: "knit_define_team",
|
|
1830
|
-
description: "Create or update a custom
|
|
2104
|
+
description: "Create or update a custom team.",
|
|
1831
2105
|
inputSchema: { type: "object", properties: { name: { type: "string", description: "Team name." }, role: { type: "string", description: "Team role." }, focus: { type: "string", description: "Team focus area." }, agents: { type: "string", description: "Comma-separated agent types." }, file_patterns: { type: "string", description: "Comma-separated globs." }, checklist: { type: "string", description: "Pipe-separated review items." } }, required: ["name", "role", "focus"] }
|
|
1832
2106
|
},
|
|
1833
2107
|
{
|
|
@@ -1837,12 +2111,12 @@ function getToolDefinitions() {
|
|
|
1837
2111
|
},
|
|
1838
2112
|
{
|
|
1839
2113
|
name: "knit_get_team_prompt",
|
|
1840
|
-
description: "Get
|
|
2114
|
+
description: "Get a team's prompt with other teams' findings included.",
|
|
1841
2115
|
inputSchema: { type: "object", properties: { team_name: { type: "string", description: "Team name." }, files_to_review: { type: "string", description: "Comma-separated files." } }, required: ["team_name"] }
|
|
1842
2116
|
},
|
|
1843
2117
|
{
|
|
1844
2118
|
name: "knit_post_team_findings",
|
|
1845
|
-
description: "Post
|
|
2119
|
+
description: "Post team findings to the shared board.",
|
|
1846
2120
|
inputSchema: { type: "object", properties: { team_name: { type: "string", description: "Team posting." }, findings: { type: "string", description: "JSON array of findings." } }, required: ["team_name", "findings"] }
|
|
1847
2121
|
},
|
|
1848
2122
|
{
|
|
@@ -1853,12 +2127,12 @@ function getToolDefinitions() {
|
|
|
1853
2127
|
// ── Session memory ───────────────────────────────────────────
|
|
1854
2128
|
{
|
|
1855
2129
|
name: "knit_load_session",
|
|
1856
|
-
description: "Call at session start. Returns
|
|
1857
|
-
inputSchema: { type: "object", properties: {} }
|
|
2130
|
+
description: "Call at session start. Returns handoff, top learnings, false positives by default. Opt in to more via include=patterns,teams,metrics,recent_sessions,full_learnings,full_knowledge,all.",
|
|
2131
|
+
inputSchema: { type: "object", properties: { include: { type: "string", description: "Comma-separated optional sections." } } }
|
|
1858
2132
|
},
|
|
1859
2133
|
{
|
|
1860
2134
|
name: "knit_save_session_summary",
|
|
1861
|
-
description: "Opt-in. Record a
|
|
2135
|
+
description: "Opt-in. Record a session summary a future search would want to find.",
|
|
1862
2136
|
inputSchema: {
|
|
1863
2137
|
type: "object",
|
|
1864
2138
|
properties: {
|
|
@@ -1873,7 +2147,7 @@ function getToolDefinitions() {
|
|
|
1873
2147
|
},
|
|
1874
2148
|
{
|
|
1875
2149
|
name: "knit_prune_sessions",
|
|
1876
|
-
description: "Prune
|
|
2150
|
+
description: "Prune sessions older than max_age_days (default 90). Atomic rewrite.",
|
|
1877
2151
|
inputSchema: {
|
|
1878
2152
|
type: "object",
|
|
1879
2153
|
properties: {
|
|
@@ -1883,7 +2157,7 @@ function getToolDefinitions() {
|
|
|
1883
2157
|
},
|
|
1884
2158
|
{
|
|
1885
2159
|
name: "knit_search_sessions",
|
|
1886
|
-
description:
|
|
2160
|
+
description: 'Search past sessions by free text. "Have I done this before?"',
|
|
1887
2161
|
inputSchema: {
|
|
1888
2162
|
type: "object",
|
|
1889
2163
|
properties: {
|
|
@@ -1896,7 +2170,7 @@ function getToolDefinitions() {
|
|
|
1896
2170
|
// ── Workflow on demand ───────────────────────────────────────
|
|
1897
2171
|
{
|
|
1898
2172
|
name: "knit_get_workflow",
|
|
1899
|
-
description: "Fetch
|
|
2173
|
+
description: "Fetch one workflow section: overview, tier, phases, research, ideate, plan, execute, optimize, review, tdd, learn, handoff, ship, tools. Omit phase to list all.",
|
|
1900
2174
|
inputSchema: {
|
|
1901
2175
|
type: "object",
|
|
1902
2176
|
properties: {
|
|
@@ -1907,7 +2181,7 @@ function getToolDefinitions() {
|
|
|
1907
2181
|
// ── Parallel team worktrees ──────────────────────────────────
|
|
1908
2182
|
{
|
|
1909
2183
|
name: "knit_spawn_team_worktree",
|
|
1910
|
-
description: "Create a git worktree for a team
|
|
2184
|
+
description: "Create a git worktree for a team so they can write in parallel without colliding.",
|
|
1911
2185
|
inputSchema: {
|
|
1912
2186
|
type: "object",
|
|
1913
2187
|
properties: {
|
|
@@ -1919,7 +2193,7 @@ function getToolDefinitions() {
|
|
|
1919
2193
|
},
|
|
1920
2194
|
{
|
|
1921
2195
|
name: "knit_list_team_worktrees",
|
|
1922
|
-
description: "List active team worktrees.
|
|
2196
|
+
description: "List active team worktrees. include_finalized=true for full history.",
|
|
1923
2197
|
inputSchema: {
|
|
1924
2198
|
type: "object",
|
|
1925
2199
|
properties: {
|
|
@@ -1930,7 +2204,7 @@ function getToolDefinitions() {
|
|
|
1930
2204
|
// ── Cross-project learnings (Model C — global pool) ─────────
|
|
1931
2205
|
{
|
|
1932
2206
|
name: "knit_record_global_learning",
|
|
1933
|
-
description: "Opt-in. Record a learning to the cross-project pool when
|
|
2207
|
+
description: "Opt-in. Record a learning to the cross-project pool when it generalizes beyond this project.",
|
|
1934
2208
|
inputSchema: {
|
|
1935
2209
|
type: "object",
|
|
1936
2210
|
properties: {
|
|
@@ -1944,7 +2218,7 @@ function getToolDefinitions() {
|
|
|
1944
2218
|
},
|
|
1945
2219
|
{
|
|
1946
2220
|
name: "knit_search_global_learnings",
|
|
1947
|
-
description: "Search the cross-project learnings pool
|
|
2221
|
+
description: "Search the cross-project learnings pool across all projects on this machine.",
|
|
1948
2222
|
inputSchema: {
|
|
1949
2223
|
type: "object",
|
|
1950
2224
|
properties: {
|
|
@@ -1957,17 +2231,17 @@ function getToolDefinitions() {
|
|
|
1957
2231
|
// ── Pattern reflection (now backed by Model C, useful with ≥3 entries) ──
|
|
1958
2232
|
{
|
|
1959
2233
|
name: "knit_reflect",
|
|
1960
|
-
description: "Detect patterns across
|
|
2234
|
+
description: "Detect patterns across learnings. Needs \u22653 entries to surface anything.",
|
|
1961
2235
|
inputSchema: { type: "object", properties: {} }
|
|
1962
2236
|
},
|
|
1963
2237
|
{
|
|
1964
2238
|
name: "knit_get_suggestions",
|
|
1965
|
-
description: 'Adaptive
|
|
2239
|
+
description: 'Adaptive warnings from past patterns. "Based on history, watch out for X."',
|
|
1966
2240
|
inputSchema: { type: "object", properties: { domains: { type: "string", description: "Comma-separated domains for this task." } }, required: ["domains"] }
|
|
1967
2241
|
},
|
|
1968
2242
|
{
|
|
1969
2243
|
name: "knit_install_agent",
|
|
1970
|
-
description: "Install
|
|
2244
|
+
description: "Install one VoltAgent subagent into .claude/agents/, personalized with project context.",
|
|
1971
2245
|
inputSchema: {
|
|
1972
2246
|
type: "object",
|
|
1973
2247
|
properties: {
|
|
@@ -1979,7 +2253,7 @@ function getToolDefinitions() {
|
|
|
1979
2253
|
},
|
|
1980
2254
|
{
|
|
1981
2255
|
name: "knit_finalize_team_worktree",
|
|
1982
|
-
description: "Merge or discard a team's worktree.
|
|
2256
|
+
description: "Merge or discard a team's worktree. Surfaces conflicts without destroying the worktree.",
|
|
1983
2257
|
inputSchema: {
|
|
1984
2258
|
type: "object",
|
|
1985
2259
|
properties: {
|
|
@@ -1992,16 +2266,51 @@ function getToolDefinitions() {
|
|
|
1992
2266
|
// ── Protocol Guard ───────────────────────────────────────────
|
|
1993
2267
|
{
|
|
1994
2268
|
name: "knit_set_protocol_strictness",
|
|
1995
|
-
description: "Set Protocol Guard strictness
|
|
2269
|
+
description: "Set Protocol Guard strictness: off | warn (default) | block.",
|
|
1996
2270
|
inputSchema: { type: "object", properties: { level: { type: "string", description: "One of: off | warn | block." } }, required: ["level"] }
|
|
1997
2271
|
},
|
|
1998
2272
|
{
|
|
1999
2273
|
name: "knit_get_protocol_strictness",
|
|
2000
|
-
description: "Read current Protocol Guard strictness
|
|
2274
|
+
description: "Read current Protocol Guard strictness.",
|
|
2275
|
+
inputSchema: { type: "object", properties: {} }
|
|
2276
|
+
},
|
|
2277
|
+
// ── Meta — feature discoverability ───────────────────────────
|
|
2278
|
+
{
|
|
2279
|
+
name: "knit_list_features",
|
|
2280
|
+
description: "List active vs hidden Knit tools and why. Call when a tool you expect isn't available.",
|
|
2001
2281
|
inputSchema: { type: "object", properties: {} }
|
|
2282
|
+
},
|
|
2283
|
+
{
|
|
2284
|
+
name: "knit_enable_feature",
|
|
2285
|
+
description: "Enable a Tier-2/3 feature flag (teams, subagents, admin). Persisted.",
|
|
2286
|
+
inputSchema: {
|
|
2287
|
+
type: "object",
|
|
2288
|
+
properties: { feature: { type: "string", description: "One of: teams, subagents, admin." } },
|
|
2289
|
+
required: ["feature"]
|
|
2290
|
+
}
|
|
2291
|
+
},
|
|
2292
|
+
{
|
|
2293
|
+
name: "knit_disable_feature",
|
|
2294
|
+
description: "Disable a feature flag. Auto-exposed tools stay visible regardless.",
|
|
2295
|
+
inputSchema: {
|
|
2296
|
+
type: "object",
|
|
2297
|
+
properties: { feature: { type: "string", description: "One of: teams, subagents, admin." } },
|
|
2298
|
+
required: ["feature"]
|
|
2299
|
+
}
|
|
2002
2300
|
}
|
|
2003
2301
|
];
|
|
2004
2302
|
}
|
|
2303
|
+
function getActiveToolDefinitions(shape) {
|
|
2304
|
+
const all = getToolDefinitions();
|
|
2305
|
+
if (!shape) return all;
|
|
2306
|
+
const activeNames = new Set(
|
|
2307
|
+
TOOL_REGISTRY.filter((info) => isToolActive(info, shape)).map((info) => info.tool)
|
|
2308
|
+
);
|
|
2309
|
+
return all.filter((def) => activeNames.has(def.name));
|
|
2310
|
+
}
|
|
2311
|
+
function getActiveToolDefinitionsForBrain(brain) {
|
|
2312
|
+
return getActiveToolDefinitions(detectProjectShape(brain));
|
|
2313
|
+
}
|
|
2005
2314
|
var handlers = {
|
|
2006
2315
|
knit_query_imports: handleQueryImports,
|
|
2007
2316
|
knit_query_dependents: handleQueryDependents,
|
|
@@ -2037,7 +2346,10 @@ var handlers = {
|
|
|
2037
2346
|
knit_get_suggestions: handleGetSuggestions,
|
|
2038
2347
|
knit_install_agent: handleInstallAgent,
|
|
2039
2348
|
knit_set_protocol_strictness: handleSetProtocolStrictness,
|
|
2040
|
-
knit_get_protocol_strictness: handleGetProtocolStrictness
|
|
2349
|
+
knit_get_protocol_strictness: handleGetProtocolStrictness,
|
|
2350
|
+
knit_list_features: handleListFeatures,
|
|
2351
|
+
knit_enable_feature: handleEnableFeature,
|
|
2352
|
+
knit_disable_feature: handleDisableFeature
|
|
2041
2353
|
};
|
|
2042
2354
|
function handleToolCall(toolName, params, brain) {
|
|
2043
2355
|
if (params.file_path) {
|
|
@@ -2059,6 +2371,8 @@ function handleToolCall(toolName, params, brain) {
|
|
|
2059
2371
|
return handler(params, brain);
|
|
2060
2372
|
}
|
|
2061
2373
|
export {
|
|
2374
|
+
getActiveToolDefinitions,
|
|
2375
|
+
getActiveToolDefinitionsForBrain,
|
|
2062
2376
|
getToolDefinitions,
|
|
2063
2377
|
handleToolCall
|
|
2064
2378
|
};
|
package/package.json
CHANGED