knit-mcp 0.6.5 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/{cache-MHJP4WGF.js → cache-WGZ2C7BZ.js} +4 -4
- package/dist/{chunk-HWEH27E7.js → chunk-3XR77YJM.js} +1 -1
- package/dist/{chunk-YI37OAJ7.js → chunk-HBMF62U4.js} +4 -0
- package/dist/{chunk-IG4VRBYW.js → chunk-QMH2DT6K.js} +3 -3
- package/dist/{chunk-5F7DSVJY.js → chunk-SLN5ABF5.js} +36 -65
- package/dist/{chunk-FEOG4WTP.js → chunk-TRZ3LD6B.js} +1 -1
- package/dist/cli.js +9 -8
- package/dist/{export-EEBXKMHR.js → export-IKPBLZOO.js} +2 -2
- package/dist/{install-agents-Q64MWT3V.js → install-agents-AXT6DMYM.js} +4 -4
- package/dist/instructions-33TUHLTK.js +29 -0
- package/dist/{refresh-K7G7HRF7.js → refresh-MSN5YNPS.js} +2 -2
- package/dist/{status-H2CU72CE.js → status-XN6VHO66.js} +1 -1
- package/dist/{tools-25CPMJKY.js → tools-KRPOUYNT.js} +387 -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-
|
|
5
|
+
} from "./chunk-QMH2DT6K.js";
|
|
6
|
+
import "./chunk-SLN5ABF5.js";
|
|
7
7
|
import "./chunk-M3YZOJNW.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-3XR77YJM.js";
|
|
9
9
|
import "./chunk-7PPC6IG6.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,
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
buildReverseDependencies,
|
|
5
5
|
generateClaudeMd,
|
|
6
6
|
spliceKnitBlock
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SLN5ABF5.js";
|
|
8
8
|
import {
|
|
9
9
|
readLearnings
|
|
10
10
|
} from "./chunk-M3YZOJNW.js";
|
|
11
11
|
import {
|
|
12
12
|
installAgentsForProject,
|
|
13
13
|
pruneSessionsByAge
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-3XR77YJM.js";
|
|
15
15
|
import {
|
|
16
16
|
scanProject
|
|
17
17
|
} from "./chunk-7PPC6IG6.js";
|
|
@@ -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";
|
|
@@ -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 {
|
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-MSN5YNPS.js");
|
|
30
|
+
const { installAgentsCommand } = await import("./install-agents-AXT6DMYM.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,16 @@ 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-WGZ2C7BZ.js");
|
|
98
|
+
const { getActiveToolDefinitionsForBrain, handleToolCall } = await import("./tools-KRPOUYNT.js");
|
|
99
|
+
const { KNIT_INSTRUCTIONS } = await import("./instructions-33TUHLTK.js");
|
|
99
100
|
const ROOT_PATH = detectProjectRoot();
|
|
100
101
|
const server = new Server(
|
|
101
102
|
{ name: "knit-brain", version: VERSION },
|
|
102
|
-
{ capabilities: { tools: {} } }
|
|
103
|
+
{ capabilities: { tools: {} }, instructions: KNIT_INSTRUCTIONS }
|
|
103
104
|
);
|
|
104
105
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
105
|
-
tools:
|
|
106
|
+
tools: getActiveToolDefinitionsForBrain(getBrain(ROOT_PATH))
|
|
106
107
|
}));
|
|
107
108
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
108
109
|
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-
|
|
3
|
+
} from "./chunk-QMH2DT6K.js";
|
|
4
|
+
import "./chunk-SLN5ABF5.js";
|
|
5
5
|
import "./chunk-M3YZOJNW.js";
|
|
6
6
|
import {
|
|
7
7
|
installAgentsForProject
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-3XR77YJM.js";
|
|
9
9
|
import "./chunk-7PPC6IG6.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,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildKnowledge,
|
|
3
3
|
generateClaudeMd
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SLN5ABF5.js";
|
|
5
5
|
import {
|
|
6
6
|
findFalsePositives
|
|
7
7
|
} from "./chunk-M3YZOJNW.js";
|
|
@@ -12,7 +12,7 @@ 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,19 @@ 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";
|
|
37
39
|
|
|
38
40
|
// src/mcp/handlers.ts
|
|
39
|
-
import { writeFileSync as writeFileSync4, readFileSync as readFileSync4, readdirSync, existsSync as existsSync4 } from "fs";
|
|
41
|
+
import { writeFileSync as writeFileSync4, readFileSync as readFileSync4, readdirSync, existsSync as existsSync4, renameSync as renameSync2, unlinkSync } from "fs";
|
|
40
42
|
import { join as join2 } from "path";
|
|
41
43
|
import { statSync as statSync2 } from "fs";
|
|
42
44
|
|
|
@@ -692,6 +694,108 @@ function redactSecrets(input) {
|
|
|
692
694
|
return out;
|
|
693
695
|
}
|
|
694
696
|
|
|
697
|
+
// src/mcp/features.ts
|
|
698
|
+
var TOOL_REGISTRY = [
|
|
699
|
+
// ── Tier 1 — Memory + retrieval (8) ─────────────────────────────
|
|
700
|
+
{ tool: "knit_load_session", tier: 1, category: "memory", rationale: "Session-start primer; universal" },
|
|
701
|
+
{ tool: "knit_search_learnings", tier: 1, category: "memory", rationale: "Project-local learnings lookup; universal" },
|
|
702
|
+
{ tool: "knit_search_global_learnings", tier: 1, category: "memory", rationale: "Cross-project learnings pool; seeded by default" },
|
|
703
|
+
{ tool: "knit_search_sessions", tier: 1, category: "memory", rationale: '"Have I done this before?" \u2014 universal' },
|
|
704
|
+
{ tool: "knit_record_learning", tier: 1, category: "memory", rationale: "LEARN step persistence; universal" },
|
|
705
|
+
{ tool: "knit_record_global_learning", tier: 1, category: "memory", rationale: "Cross-project memory write; useful from day one" },
|
|
706
|
+
{ tool: "knit_save_session_summary", tier: 1, category: "memory", rationale: "Session-end persistence; universal" },
|
|
707
|
+
{ tool: "knit_save_handoff", tier: 1, category: "memory", rationale: "Context-degradation handoff; universal" },
|
|
708
|
+
// ── Tier 1 — Knowledge graph (5) ────────────────────────────────
|
|
709
|
+
{ tool: "knit_query_imports", tier: 1, category: "knowledge-graph", rationale: "Core differentiator; returns empty honestly on docs-only projects" },
|
|
710
|
+
{ tool: "knit_query_exports", tier: 1, category: "knowledge-graph", rationale: "Core differentiator; same honest-empty behavior" },
|
|
711
|
+
{ tool: "knit_query_dependents", tier: 1, category: "knowledge-graph", rationale: "Core differentiator" },
|
|
712
|
+
{ tool: "knit_query_tests", tier: 1, category: "knowledge-graph", rationale: "Core differentiator" },
|
|
713
|
+
{ tool: "knit_find_fanout", tier: 1, category: "knowledge-graph", rationale: "Core differentiator" },
|
|
714
|
+
// ── Tier 1 — Workflow + classification (4) ──────────────────────
|
|
715
|
+
{ tool: "knit_classify_task", tier: 1, category: "workflow", rationale: "Tier router; called before any non-trivial task" },
|
|
716
|
+
{ tool: "knit_build_context", tier: 1, category: "workflow", rationale: "Domain Context Object builder" },
|
|
717
|
+
{ tool: "knit_get_workflow", tier: 1, category: "workflow", rationale: "On-demand phase depth fetcher" },
|
|
718
|
+
{ tool: "knit_get_suggestions", tier: 1, category: "workflow", rationale: "Adaptive warnings from past patterns" },
|
|
719
|
+
// ── Tier 1 — False positives + reflection (3) ───────────────────
|
|
720
|
+
{ tool: "knit_record_false_positive", tier: 1, category: "fp-reflection", rationale: "Universal \u2014 reviewer agents flag FPs on any project" },
|
|
721
|
+
{ tool: "knit_get_false_positives", tier: 1, category: "fp-reflection", rationale: "Universal" },
|
|
722
|
+
{ tool: "knit_reflect", tier: 1, category: "fp-reflection", rationale: 'Returns "not enough data" on sparse projects; always available' },
|
|
723
|
+
// ── Tier 1 — Protocol Guard config (2) ──────────────────────────
|
|
724
|
+
{ tool: "knit_set_protocol_strictness", tier: 1, category: "protocol-config", rationale: "Universal \u2014 every install ships Protocol Guard" },
|
|
725
|
+
{ tool: "knit_get_protocol_strictness", tier: 1, category: "protocol-config", rationale: "Universal" },
|
|
726
|
+
// ── Tier 1 — Diagnostics + meta (4) ─────────────────────────────
|
|
727
|
+
{ tool: "knit_brain_status", tier: 1, category: "diagnostics", rationale: "Health + token-accounting; universal" },
|
|
728
|
+
{ tool: "knit_list_features", tier: 1, category: "diagnostics", rationale: "The discoverability escape hatch itself" },
|
|
729
|
+
{ 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" },
|
|
730
|
+
{ tool: "knit_disable_feature", tier: 1, category: "diagnostics", rationale: "Flip off a previously-enabled feature flag" },
|
|
731
|
+
// ── Tier 2 — Team worktrees (9) ─────────────────────────────────
|
|
732
|
+
{ 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' },
|
|
733
|
+
{ tool: "knit_finalize_team_worktree", tier: 2, category: "teams", rationale: "Merge/discard a team worktree", enable_via: 'knit_enable_feature("teams")' },
|
|
734
|
+
{ tool: "knit_list_team_worktrees", tier: 2, category: "teams", rationale: "Active team worktree registry", enable_via: 'knit_enable_feature("teams")' },
|
|
735
|
+
{ tool: "knit_define_team", tier: 2, category: "teams", rationale: "Custom team definition", enable_via: 'knit_enable_feature("teams")' },
|
|
736
|
+
{ tool: "knit_get_teams", tier: 2, category: "teams", rationale: "List configured teams", enable_via: 'knit_enable_feature("teams")' },
|
|
737
|
+
{ tool: "knit_get_team_prompt", tier: 2, category: "teams", rationale: "Team-specific agent prompt", enable_via: 'knit_enable_feature("teams")' },
|
|
738
|
+
{ tool: "knit_start_team_review", tier: 2, category: "teams", rationale: "Begin a multi-team review", enable_via: 'knit_enable_feature("teams")' },
|
|
739
|
+
{ tool: "knit_post_team_findings", tier: 2, category: "teams", rationale: "Submit team findings to shared board", enable_via: 'knit_enable_feature("teams")' },
|
|
740
|
+
{ tool: "knit_get_board_summary", tier: 2, category: "teams", rationale: "Roll up team findings", enable_via: 'knit_enable_feature("teams")' },
|
|
741
|
+
// ── Tier 2 — Subagents (1) ──────────────────────────────────────
|
|
742
|
+
{ 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")' },
|
|
743
|
+
// ── Tier 3 — Admin (1) ──────────────────────────────────────────
|
|
744
|
+
{ tool: "knit_prune_sessions", tier: 3, category: "admin", rationale: "Manual session pruning; auto-prune handles this normally", enable_via: 'knit_enable_feature("admin")' },
|
|
745
|
+
// ── Tier 3 — One-time setup (1) ─────────────────────────────────
|
|
746
|
+
{ 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' }
|
|
747
|
+
];
|
|
748
|
+
function isToolActive(info, shape) {
|
|
749
|
+
if (info.tier === 1) return true;
|
|
750
|
+
if (info.tier === 3) return shape.enabledFeatures.has("admin");
|
|
751
|
+
if (info.category === "teams") {
|
|
752
|
+
return shape.domainCount >= 3 || shape.enabledFeatures.has("teams");
|
|
753
|
+
}
|
|
754
|
+
if (info.category === "subagents") {
|
|
755
|
+
return shape.hasInstalledSubagents || shape.enabledFeatures.has("subagents");
|
|
756
|
+
}
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
function computeFeatureListing(shape) {
|
|
760
|
+
const active = [];
|
|
761
|
+
const available = [];
|
|
762
|
+
const by_category = {
|
|
763
|
+
memory: { active: 0, available: 0 },
|
|
764
|
+
"knowledge-graph": { active: 0, available: 0 },
|
|
765
|
+
workflow: { active: 0, available: 0 },
|
|
766
|
+
"fp-reflection": { active: 0, available: 0 },
|
|
767
|
+
"protocol-config": { active: 0, available: 0 },
|
|
768
|
+
diagnostics: { active: 0, available: 0 },
|
|
769
|
+
teams: { active: 0, available: 0 },
|
|
770
|
+
subagents: { active: 0, available: 0 },
|
|
771
|
+
admin: { active: 0, available: 0 }
|
|
772
|
+
};
|
|
773
|
+
for (const info of TOOL_REGISTRY) {
|
|
774
|
+
if (isToolActive(info, shape)) {
|
|
775
|
+
active.push({ name: info.tool, tier: info.tier, category: info.category });
|
|
776
|
+
by_category[info.category].active++;
|
|
777
|
+
} else {
|
|
778
|
+
available.push({
|
|
779
|
+
name: info.tool,
|
|
780
|
+
tier: info.tier,
|
|
781
|
+
category: info.category,
|
|
782
|
+
reason: info.rationale,
|
|
783
|
+
enable_via: info.enable_via
|
|
784
|
+
});
|
|
785
|
+
by_category[info.category].available++;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
return {
|
|
789
|
+
active,
|
|
790
|
+
available,
|
|
791
|
+
totals: { active: active.length, available: available.length, total: TOOL_REGISTRY.length },
|
|
792
|
+
by_category
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
function isEnableableFeature(name) {
|
|
796
|
+
return name === "teams" || name === "subagents" || name === "admin";
|
|
797
|
+
}
|
|
798
|
+
|
|
695
799
|
// src/engine/teams.ts
|
|
696
800
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, statSync, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
697
801
|
import { dirname as dirname2 } from "path";
|
|
@@ -1059,6 +1163,112 @@ function handleBrainStatus(_params, brain) {
|
|
|
1059
1163
|
instruction: "Brain is ready. Next: call knit_classify_task with the files you plan to touch to get your tier and phases."
|
|
1060
1164
|
});
|
|
1061
1165
|
}
|
|
1166
|
+
function loadEnabledFeatures(rootPath) {
|
|
1167
|
+
const enabled = /* @__PURE__ */ new Set();
|
|
1168
|
+
try {
|
|
1169
|
+
const path = featuresConfigPath(rootPath);
|
|
1170
|
+
if (!existsSync4(path)) return enabled;
|
|
1171
|
+
const parsed = JSON.parse(readFileSync4(path, "utf-8"));
|
|
1172
|
+
if (Array.isArray(parsed?.enabled)) {
|
|
1173
|
+
for (const name of parsed.enabled) {
|
|
1174
|
+
if (isEnableableFeature(name)) enabled.add(name);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
} catch {
|
|
1178
|
+
}
|
|
1179
|
+
return enabled;
|
|
1180
|
+
}
|
|
1181
|
+
function saveEnabledFeatures(rootPath, enabled) {
|
|
1182
|
+
const path = featuresConfigPath(rootPath);
|
|
1183
|
+
const payload = {
|
|
1184
|
+
enabled: [...enabled].sort(),
|
|
1185
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1186
|
+
};
|
|
1187
|
+
const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
1188
|
+
try {
|
|
1189
|
+
writeFileSync4(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
1190
|
+
renameSync2(tmpPath, path);
|
|
1191
|
+
} catch (err) {
|
|
1192
|
+
try {
|
|
1193
|
+
unlinkSync(tmpPath);
|
|
1194
|
+
} catch {
|
|
1195
|
+
}
|
|
1196
|
+
throw err;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
function detectProjectShape(brain) {
|
|
1200
|
+
return {
|
|
1201
|
+
hasAnalyzableCode: brain.knowledge.summary.totalFiles >= 10,
|
|
1202
|
+
domainCount: brain.config.domains?.length ?? 0,
|
|
1203
|
+
hasInstalledSubagents: existsSync4(projectAgentsDir(brain.rootPath)),
|
|
1204
|
+
sessionCount: sessionCount(brain.rootPath),
|
|
1205
|
+
enabledFeatures: loadEnabledFeatures(brain.rootPath)
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
function handleListFeatures(_params, brain) {
|
|
1209
|
+
const shape = detectProjectShape(brain);
|
|
1210
|
+
const listing = computeFeatureListing(shape);
|
|
1211
|
+
return JSON.stringify({
|
|
1212
|
+
...listing,
|
|
1213
|
+
project_shape: {
|
|
1214
|
+
has_analyzable_code: shape.hasAnalyzableCode,
|
|
1215
|
+
domain_count: shape.domainCount,
|
|
1216
|
+
has_installed_subagents: shape.hasInstalledSubagents,
|
|
1217
|
+
session_count: shape.sessionCount,
|
|
1218
|
+
enabled_features: [...shape.enabledFeatures]
|
|
1219
|
+
},
|
|
1220
|
+
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" }).'
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
function handleEnableFeature(params, brain) {
|
|
1224
|
+
const feature = (params.feature || "").trim().toLowerCase();
|
|
1225
|
+
if (!isEnableableFeature(feature)) {
|
|
1226
|
+
return JSON.stringify({
|
|
1227
|
+
status: "error",
|
|
1228
|
+
error: `Invalid feature: "${params.feature}". Valid values: teams, subagents, admin.`
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
const enabled = loadEnabledFeatures(brain.rootPath);
|
|
1232
|
+
const wasAlreadyOn = enabled.has(feature);
|
|
1233
|
+
enabled.add(feature);
|
|
1234
|
+
if (!wasAlreadyOn) {
|
|
1235
|
+
saveEnabledFeatures(brain.rootPath, enabled);
|
|
1236
|
+
}
|
|
1237
|
+
return JSON.stringify({
|
|
1238
|
+
status: wasAlreadyOn ? "already-enabled" : "enabled",
|
|
1239
|
+
feature,
|
|
1240
|
+
enabled_features: [...enabled].sort(),
|
|
1241
|
+
instruction: "New tools may now appear in tools/list on the next request. Call knit_list_features to confirm."
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
function handleDisableFeature(params, brain) {
|
|
1245
|
+
const feature = (params.feature || "").trim().toLowerCase();
|
|
1246
|
+
if (!isEnableableFeature(feature)) {
|
|
1247
|
+
return JSON.stringify({
|
|
1248
|
+
status: "error",
|
|
1249
|
+
error: `Invalid feature: "${params.feature}". Valid values: teams, subagents, admin.`
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
const enabled = loadEnabledFeatures(brain.rootPath);
|
|
1253
|
+
const wasOn = enabled.delete(feature);
|
|
1254
|
+
if (wasOn) {
|
|
1255
|
+
saveEnabledFeatures(brain.rootPath, enabled);
|
|
1256
|
+
}
|
|
1257
|
+
return JSON.stringify({
|
|
1258
|
+
status: wasOn ? "disabled" : "already-disabled",
|
|
1259
|
+
feature,
|
|
1260
|
+
enabled_features: [...enabled].sort(),
|
|
1261
|
+
instruction: "Disabled tools will be filtered out of tools/list on the next request."
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
function detectsInquiryIntent(description) {
|
|
1265
|
+
if (!description) return false;
|
|
1266
|
+
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;
|
|
1267
|
+
const inquiryVerb = /\b(audit|explain|investigate|analy[sz]e|review|examine|describe|summari[sz]e|enumerate|inspect)\b/i;
|
|
1268
|
+
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;
|
|
1269
|
+
if (actionDirective.test(description)) return false;
|
|
1270
|
+
return inquiryStart.test(description) || inquiryVerb.test(description);
|
|
1271
|
+
}
|
|
1062
1272
|
function handleClassifyTask(params, brain) {
|
|
1063
1273
|
const rawFiles = (params.files_to_touch || "").split(",").map((f) => f.trim()).filter(Boolean);
|
|
1064
1274
|
const files = rawFiles.filter((f) => f !== "unknown");
|
|
@@ -1069,6 +1279,27 @@ function handleClassifyTask(params, brain) {
|
|
|
1069
1279
|
const importers = brain.reverseDeps[file] || [];
|
|
1070
1280
|
if (importers.length >= 3) crossDomainRipple.push(`${file} is high-fanout (${importers.length} dependents)`);
|
|
1071
1281
|
}
|
|
1282
|
+
if (detectsInquiryIntent(params.description || "")) {
|
|
1283
|
+
try {
|
|
1284
|
+
writeClassificationMarker(brain.rootPath, {
|
|
1285
|
+
turnId: `${Date.now()}-${process.pid}`,
|
|
1286
|
+
classifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1287
|
+
tier: "inquiry",
|
|
1288
|
+
files
|
|
1289
|
+
});
|
|
1290
|
+
} catch {
|
|
1291
|
+
}
|
|
1292
|
+
return JSON.stringify({
|
|
1293
|
+
tier: "inquiry",
|
|
1294
|
+
affected_domains: [...domains],
|
|
1295
|
+
phases: [],
|
|
1296
|
+
files_count: files.length,
|
|
1297
|
+
cross_domain_ripple: crossDomainRipple,
|
|
1298
|
+
auto_plan_mode: false,
|
|
1299
|
+
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.",
|
|
1300
|
+
reasoning: `Inquiry: read-only intent detected${files.length > 0 ? `, ${files.length} file(s) referenced for context` : ""}`
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1072
1303
|
const isTypes = files.some((f) => f.includes("types") || f.includes("schema"));
|
|
1073
1304
|
const isAuth = files.some((f) => f.includes("auth") || f.includes("security"));
|
|
1074
1305
|
const isNewProject = files.length === 0 || rawFiles.includes("unknown");
|
|
@@ -1085,14 +1316,21 @@ function handleClassifyTask(params, brain) {
|
|
|
1085
1316
|
});
|
|
1086
1317
|
} catch {
|
|
1087
1318
|
}
|
|
1088
|
-
|
|
1319
|
+
const verbose = params.verbose === "true" || params.verbose === "1";
|
|
1320
|
+
const base = {
|
|
1089
1321
|
tier: tier2,
|
|
1090
1322
|
affected_domains: [...domains],
|
|
1091
1323
|
phases: phases2,
|
|
1324
|
+
auto_plan_mode: tier2 === "complex",
|
|
1325
|
+
instruction
|
|
1326
|
+
};
|
|
1327
|
+
if (!verbose) {
|
|
1328
|
+
return JSON.stringify(base);
|
|
1329
|
+
}
|
|
1330
|
+
return JSON.stringify({
|
|
1331
|
+
...base,
|
|
1092
1332
|
files_count: files.length,
|
|
1093
1333
|
cross_domain_ripple: crossDomainRipple,
|
|
1094
|
-
auto_plan_mode: tier2 === "complex",
|
|
1095
|
-
instruction,
|
|
1096
1334
|
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
1335
|
});
|
|
1098
1336
|
}
|
|
@@ -1493,7 +1731,15 @@ function handleSearchGlobalLearnings(params, _brain) {
|
|
|
1493
1731
|
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
1732
|
});
|
|
1495
1733
|
}
|
|
1496
|
-
function
|
|
1734
|
+
function parseLoadSessionInclude(raw) {
|
|
1735
|
+
if (!raw) return /* @__PURE__ */ new Set();
|
|
1736
|
+
return new Set(
|
|
1737
|
+
raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean)
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
function handleLoadSession(params, brain) {
|
|
1741
|
+
const include = parseLoadSessionInclude(params.include);
|
|
1742
|
+
const wantAll = include.has("all");
|
|
1497
1743
|
const root = brain.rootPath;
|
|
1498
1744
|
const sessionsFile = sessionsLogPath(root);
|
|
1499
1745
|
let lastSession = null;
|
|
@@ -1502,45 +1748,67 @@ function handleLoadSession(_params, brain) {
|
|
|
1502
1748
|
const sessions = content.split(/^## Session/m).slice(1);
|
|
1503
1749
|
if (sessions.length > 0) {
|
|
1504
1750
|
const last = sessions[sessions.length - 1].trim();
|
|
1505
|
-
lastSession = last.slice(0,
|
|
1751
|
+
lastSession = last.slice(0, 200);
|
|
1506
1752
|
}
|
|
1507
1753
|
}
|
|
1508
1754
|
const handoffPath = join2(root, "handoff.md");
|
|
1509
1755
|
let handoff2 = null;
|
|
1510
1756
|
if (existsSync4(handoffPath)) {
|
|
1511
|
-
handoff2 = readFileSync4(handoffPath, "utf-8").slice(0,
|
|
1512
|
-
}
|
|
1513
|
-
const topLearnings = brain.knowledgeBase.entries.filter((e) => e.accessCount > 0).sort((a, b) => b.accessCount - a.accessCount).slice(0, 5).map((e) => ({ summary: e.summary, lesson: e.lesson, tags: e.tags, accessed: e.accessCount }));
|
|
1514
|
-
const fps = brain.knowledgeBase.entries.filter((e) => e.tags.includes("#false-positive")).map((e) => ({ summary: e.summary, lesson: e.lesson }));
|
|
1515
|
-
const teamsFile = teamsPath(root);
|
|
1516
|
-
let teams = [];
|
|
1517
|
-
if (existsSync4(teamsFile)) {
|
|
1518
|
-
try {
|
|
1519
|
-
const t = JSON.parse(readFileSync4(teamsFile, "utf-8"));
|
|
1520
|
-
teams = t.map((team) => team.name);
|
|
1521
|
-
} catch {
|
|
1522
|
-
}
|
|
1757
|
+
handoff2 = readFileSync4(handoffPath, "utf-8").slice(0, 1500);
|
|
1523
1758
|
}
|
|
1524
|
-
const
|
|
1759
|
+
const learningsLimit = wantAll || include.has("full_learnings") ? 5 : 3;
|
|
1760
|
+
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 }));
|
|
1761
|
+
const fpsLimit = wantAll || include.has("full_learnings") ? 50 : 5;
|
|
1762
|
+
const fps = brain.knowledgeBase.entries.filter((e) => e.tags.includes("#false-positive")).slice(0, fpsLimit).map((e) => ({ summary: e.summary, lesson: e.lesson }));
|
|
1763
|
+
const wantFullKnowledge = wantAll || include.has("full_knowledge");
|
|
1764
|
+
const knowledge = wantFullKnowledge ? {
|
|
1525
1765
|
files: brain.knowledge.summary.totalFiles,
|
|
1526
1766
|
imports: Object.keys(brain.knowledge.importGraph).length,
|
|
1527
1767
|
high_fanout: brain.knowledge.summary.highFanoutFiles,
|
|
1528
1768
|
untested: brain.knowledge.summary.untestedFiles.slice(0, 5)
|
|
1769
|
+
} : {
|
|
1770
|
+
files: brain.knowledge.summary.totalFiles,
|
|
1771
|
+
imports: Object.keys(brain.knowledge.importGraph).length,
|
|
1772
|
+
high_fanout_count: brain.knowledge.summary.highFanoutFiles.length,
|
|
1773
|
+
untested_count: brain.knowledge.summary.untestedFiles.length
|
|
1529
1774
|
};
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1775
|
+
let teams;
|
|
1776
|
+
if (wantAll || include.has("teams")) {
|
|
1777
|
+
const teamsFile = teamsPath(root);
|
|
1778
|
+
if (existsSync4(teamsFile)) {
|
|
1779
|
+
try {
|
|
1780
|
+
const t = JSON.parse(readFileSync4(teamsFile, "utf-8"));
|
|
1781
|
+
teams = t.map((team) => team.name);
|
|
1782
|
+
} catch {
|
|
1783
|
+
teams = [];
|
|
1784
|
+
}
|
|
1785
|
+
} else {
|
|
1786
|
+
teams = [];
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
let metrics;
|
|
1790
|
+
if (wantAll || include.has("metrics")) {
|
|
1791
|
+
metrics = {
|
|
1792
|
+
total_sessions: brain.knowledgeBase.metrics.totalSessions,
|
|
1793
|
+
total_learnings: brain.knowledgeBase.entries.length,
|
|
1794
|
+
cache_hits: brain.knowledgeBase.metrics.cacheHits
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
let recentSessions;
|
|
1798
|
+
if (wantAll || include.has("recent_sessions")) {
|
|
1799
|
+
recentSessions = getRecentSessions(root, 3).map((s) => ({
|
|
1800
|
+
date: s.date,
|
|
1801
|
+
branch: s.branch ?? null,
|
|
1802
|
+
summary: s.summary ?? "",
|
|
1803
|
+
tags: s.tags ?? [],
|
|
1804
|
+
outcome: s.outcome
|
|
1805
|
+
}));
|
|
1806
|
+
}
|
|
1807
|
+
let patterns;
|
|
1808
|
+
if (wantAll || include.has("patterns")) {
|
|
1809
|
+
patterns = reflect(brain.knowledgeBase).slice(0, 3).map((p) => ({ type: p.type, description: p.description, confidence: p.confidence }));
|
|
1810
|
+
}
|
|
1811
|
+
const response = {
|
|
1544
1812
|
session_context: {
|
|
1545
1813
|
last_session: lastSession,
|
|
1546
1814
|
handoff: handoff2,
|
|
@@ -1549,16 +1817,17 @@ function handleLoadSession(_params, brain) {
|
|
|
1549
1817
|
intelligence: {
|
|
1550
1818
|
top_learnings: topLearnings,
|
|
1551
1819
|
false_positives: fps,
|
|
1552
|
-
patterns
|
|
1820
|
+
...patterns !== void 0 ? { patterns } : {}
|
|
1553
1821
|
},
|
|
1554
1822
|
project: {
|
|
1555
1823
|
knowledge,
|
|
1556
|
-
teams,
|
|
1557
|
-
metrics,
|
|
1558
|
-
recent_sessions: recentSessions
|
|
1824
|
+
...teams !== void 0 ? { teams } : {},
|
|
1825
|
+
...metrics !== void 0 ? { metrics } : {},
|
|
1826
|
+
...recentSessions !== void 0 ? { recent_sessions: recentSessions } : {}
|
|
1559
1827
|
},
|
|
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
|
-
}
|
|
1828
|
+
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."
|
|
1829
|
+
};
|
|
1830
|
+
return JSON.stringify(response);
|
|
1562
1831
|
}
|
|
1563
1832
|
function handleSaveSessionSummary(params, brain) {
|
|
1564
1833
|
const validOutcomes = ["shipped", "wip", "failed", "unknown"];
|
|
@@ -1741,73 +2010,73 @@ function getToolDefinitions() {
|
|
|
1741
2010
|
// ── Query (read the brain) ───────────────────────────────────
|
|
1742
2011
|
{
|
|
1743
2012
|
name: "knit_query_imports",
|
|
1744
|
-
description: "
|
|
2013
|
+
description: "Reverse deps for a file \u2014 who imports it.",
|
|
1745
2014
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
1746
2015
|
},
|
|
1747
2016
|
{
|
|
1748
2017
|
name: "knit_query_dependents",
|
|
1749
|
-
description: "
|
|
2018
|
+
description: "Forward deps for a file \u2014 what it imports.",
|
|
1750
2019
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
1751
2020
|
},
|
|
1752
2021
|
{
|
|
1753
2022
|
name: "knit_query_exports",
|
|
1754
|
-
description: "
|
|
2023
|
+
description: "Exports from a file: functions, classes, types, constants.",
|
|
1755
2024
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path." } }, required: ["file_path"] }
|
|
1756
2025
|
},
|
|
1757
2026
|
{
|
|
1758
2027
|
name: "knit_query_tests",
|
|
1759
|
-
description: '
|
|
2028
|
+
description: 'Tests for a file, or list all untested files with filter="untested".',
|
|
1760
2029
|
inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Relative file path (optional)." }, filter: { type: "string", description: '"untested" to list all untested files.' } } }
|
|
1761
2030
|
},
|
|
1762
2031
|
{
|
|
1763
2032
|
name: "knit_find_fanout",
|
|
1764
|
-
description: "High-fanout files \u2014 imported by many others.
|
|
2033
|
+
description: "High-fanout files \u2014 imported by many others.",
|
|
1765
2034
|
inputSchema: { type: "object", properties: { min_importers: { type: "string", description: "Minimum importers to qualify (default: 3)." } } }
|
|
1766
2035
|
},
|
|
1767
2036
|
{
|
|
1768
2037
|
name: "knit_search_learnings",
|
|
1769
|
-
description: "Search
|
|
2038
|
+
description: "Search learnings by domain tag.",
|
|
1770
2039
|
inputSchema: { type: "object", properties: { domains: { type: "string", description: "Comma-separated domain tags." } }, required: ["domains"] }
|
|
1771
2040
|
},
|
|
1772
2041
|
{
|
|
1773
2042
|
name: "knit_get_false_positives",
|
|
1774
|
-
description: "
|
|
2043
|
+
description: "List confirmed non-issues to suppress in review prompts.",
|
|
1775
2044
|
inputSchema: { type: "object", properties: {} }
|
|
1776
2045
|
},
|
|
1777
2046
|
{
|
|
1778
2047
|
name: "knit_brain_status",
|
|
1779
|
-
description: "Brain health
|
|
2048
|
+
description: "Brain health: learnings, hit rate, CLAUDE.md size, session count.",
|
|
1780
2049
|
inputSchema: { type: "object", properties: {} }
|
|
1781
2050
|
},
|
|
1782
2051
|
// ── Update (write to the brain) ──────────────────────────────
|
|
1783
2052
|
{
|
|
1784
2053
|
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"] }
|
|
2054
|
+
description: "Call first on every task. Returns tier (inquiry/trivial/standard/complex), phases, auto_plan_mode.",
|
|
2055
|
+
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
2056
|
},
|
|
1788
2057
|
{
|
|
1789
2058
|
name: "knit_build_context",
|
|
1790
|
-
description: "Build
|
|
2059
|
+
description: "Build the Domain Context Object: affected domains, ripple, pitfalls, false positives.",
|
|
1791
2060
|
inputSchema: { type: "object", properties: { files_to_touch: { type: "string", description: "Comma-separated files." } }, required: ["files_to_touch"] }
|
|
1792
2061
|
},
|
|
1793
2062
|
{
|
|
1794
2063
|
name: "knit_record_learning",
|
|
1795
|
-
description: "Record a non-obvious, reusable insight.
|
|
2064
|
+
description: "Record a non-obvious, reusable insight. Skip if a future search wouldn't be glad it exists.",
|
|
1796
2065
|
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
2066
|
},
|
|
1798
2067
|
{
|
|
1799
2068
|
name: "knit_record_false_positive",
|
|
1800
|
-
description: "Mark a finding as
|
|
2069
|
+
description: "Mark a finding as confirmed non-issue so future reviewers suppress it.",
|
|
1801
2070
|
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
2071
|
},
|
|
1803
2072
|
{
|
|
1804
2073
|
name: "knit_save_handoff",
|
|
1805
|
-
description: "Save state for the next session
|
|
2074
|
+
description: "Save state for the next session. failed_attempts is the load-bearing field.",
|
|
1806
2075
|
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
2076
|
},
|
|
1808
2077
|
{
|
|
1809
2078
|
name: "knit_setup_project",
|
|
1810
|
-
description: "
|
|
2079
|
+
description: "Bootstrap domain teams for a non-code project (research/legal/marketing).",
|
|
1811
2080
|
inputSchema: {
|
|
1812
2081
|
type: "object",
|
|
1813
2082
|
properties: {
|
|
@@ -1822,12 +2091,12 @@ function getToolDefinitions() {
|
|
|
1822
2091
|
// ── Teams (parallel review board) ────────────────────────────
|
|
1823
2092
|
{
|
|
1824
2093
|
name: "knit_get_teams",
|
|
1825
|
-
description: "List
|
|
2094
|
+
description: "List teams configured for this project.",
|
|
1826
2095
|
inputSchema: { type: "object", properties: {} }
|
|
1827
2096
|
},
|
|
1828
2097
|
{
|
|
1829
2098
|
name: "knit_define_team",
|
|
1830
|
-
description: "Create or update a custom
|
|
2099
|
+
description: "Create or update a custom team.",
|
|
1831
2100
|
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
2101
|
},
|
|
1833
2102
|
{
|
|
@@ -1837,12 +2106,12 @@ function getToolDefinitions() {
|
|
|
1837
2106
|
},
|
|
1838
2107
|
{
|
|
1839
2108
|
name: "knit_get_team_prompt",
|
|
1840
|
-
description: "Get
|
|
2109
|
+
description: "Get a team's prompt with other teams' findings included.",
|
|
1841
2110
|
inputSchema: { type: "object", properties: { team_name: { type: "string", description: "Team name." }, files_to_review: { type: "string", description: "Comma-separated files." } }, required: ["team_name"] }
|
|
1842
2111
|
},
|
|
1843
2112
|
{
|
|
1844
2113
|
name: "knit_post_team_findings",
|
|
1845
|
-
description: "Post
|
|
2114
|
+
description: "Post team findings to the shared board.",
|
|
1846
2115
|
inputSchema: { type: "object", properties: { team_name: { type: "string", description: "Team posting." }, findings: { type: "string", description: "JSON array of findings." } }, required: ["team_name", "findings"] }
|
|
1847
2116
|
},
|
|
1848
2117
|
{
|
|
@@ -1853,12 +2122,12 @@ function getToolDefinitions() {
|
|
|
1853
2122
|
// ── Session memory ───────────────────────────────────────────
|
|
1854
2123
|
{
|
|
1855
2124
|
name: "knit_load_session",
|
|
1856
|
-
description: "Call at session start. Returns
|
|
1857
|
-
inputSchema: { type: "object", properties: {} }
|
|
2125
|
+
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.",
|
|
2126
|
+
inputSchema: { type: "object", properties: { include: { type: "string", description: "Comma-separated optional sections." } } }
|
|
1858
2127
|
},
|
|
1859
2128
|
{
|
|
1860
2129
|
name: "knit_save_session_summary",
|
|
1861
|
-
description: "Opt-in. Record a
|
|
2130
|
+
description: "Opt-in. Record a session summary a future search would want to find.",
|
|
1862
2131
|
inputSchema: {
|
|
1863
2132
|
type: "object",
|
|
1864
2133
|
properties: {
|
|
@@ -1873,7 +2142,7 @@ function getToolDefinitions() {
|
|
|
1873
2142
|
},
|
|
1874
2143
|
{
|
|
1875
2144
|
name: "knit_prune_sessions",
|
|
1876
|
-
description: "Prune
|
|
2145
|
+
description: "Prune sessions older than max_age_days (default 90). Atomic rewrite.",
|
|
1877
2146
|
inputSchema: {
|
|
1878
2147
|
type: "object",
|
|
1879
2148
|
properties: {
|
|
@@ -1883,7 +2152,7 @@ function getToolDefinitions() {
|
|
|
1883
2152
|
},
|
|
1884
2153
|
{
|
|
1885
2154
|
name: "knit_search_sessions",
|
|
1886
|
-
description:
|
|
2155
|
+
description: 'Search past sessions by free text. "Have I done this before?"',
|
|
1887
2156
|
inputSchema: {
|
|
1888
2157
|
type: "object",
|
|
1889
2158
|
properties: {
|
|
@@ -1896,7 +2165,7 @@ function getToolDefinitions() {
|
|
|
1896
2165
|
// ── Workflow on demand ───────────────────────────────────────
|
|
1897
2166
|
{
|
|
1898
2167
|
name: "knit_get_workflow",
|
|
1899
|
-
description: "Fetch
|
|
2168
|
+
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
2169
|
inputSchema: {
|
|
1901
2170
|
type: "object",
|
|
1902
2171
|
properties: {
|
|
@@ -1907,7 +2176,7 @@ function getToolDefinitions() {
|
|
|
1907
2176
|
// ── Parallel team worktrees ──────────────────────────────────
|
|
1908
2177
|
{
|
|
1909
2178
|
name: "knit_spawn_team_worktree",
|
|
1910
|
-
description: "Create a git worktree for a team
|
|
2179
|
+
description: "Create a git worktree for a team so they can write in parallel without colliding.",
|
|
1911
2180
|
inputSchema: {
|
|
1912
2181
|
type: "object",
|
|
1913
2182
|
properties: {
|
|
@@ -1919,7 +2188,7 @@ function getToolDefinitions() {
|
|
|
1919
2188
|
},
|
|
1920
2189
|
{
|
|
1921
2190
|
name: "knit_list_team_worktrees",
|
|
1922
|
-
description: "List active team worktrees.
|
|
2191
|
+
description: "List active team worktrees. include_finalized=true for full history.",
|
|
1923
2192
|
inputSchema: {
|
|
1924
2193
|
type: "object",
|
|
1925
2194
|
properties: {
|
|
@@ -1930,7 +2199,7 @@ function getToolDefinitions() {
|
|
|
1930
2199
|
// ── Cross-project learnings (Model C — global pool) ─────────
|
|
1931
2200
|
{
|
|
1932
2201
|
name: "knit_record_global_learning",
|
|
1933
|
-
description: "Opt-in. Record a learning to the cross-project pool when
|
|
2202
|
+
description: "Opt-in. Record a learning to the cross-project pool when it generalizes beyond this project.",
|
|
1934
2203
|
inputSchema: {
|
|
1935
2204
|
type: "object",
|
|
1936
2205
|
properties: {
|
|
@@ -1944,7 +2213,7 @@ function getToolDefinitions() {
|
|
|
1944
2213
|
},
|
|
1945
2214
|
{
|
|
1946
2215
|
name: "knit_search_global_learnings",
|
|
1947
|
-
description: "Search the cross-project learnings pool
|
|
2216
|
+
description: "Search the cross-project learnings pool across all projects on this machine.",
|
|
1948
2217
|
inputSchema: {
|
|
1949
2218
|
type: "object",
|
|
1950
2219
|
properties: {
|
|
@@ -1957,17 +2226,17 @@ function getToolDefinitions() {
|
|
|
1957
2226
|
// ── Pattern reflection (now backed by Model C, useful with ≥3 entries) ──
|
|
1958
2227
|
{
|
|
1959
2228
|
name: "knit_reflect",
|
|
1960
|
-
description: "Detect patterns across
|
|
2229
|
+
description: "Detect patterns across learnings. Needs \u22653 entries to surface anything.",
|
|
1961
2230
|
inputSchema: { type: "object", properties: {} }
|
|
1962
2231
|
},
|
|
1963
2232
|
{
|
|
1964
2233
|
name: "knit_get_suggestions",
|
|
1965
|
-
description: 'Adaptive
|
|
2234
|
+
description: 'Adaptive warnings from past patterns. "Based on history, watch out for X."',
|
|
1966
2235
|
inputSchema: { type: "object", properties: { domains: { type: "string", description: "Comma-separated domains for this task." } }, required: ["domains"] }
|
|
1967
2236
|
},
|
|
1968
2237
|
{
|
|
1969
2238
|
name: "knit_install_agent",
|
|
1970
|
-
description: "Install
|
|
2239
|
+
description: "Install one VoltAgent subagent into .claude/agents/, personalized with project context.",
|
|
1971
2240
|
inputSchema: {
|
|
1972
2241
|
type: "object",
|
|
1973
2242
|
properties: {
|
|
@@ -1979,7 +2248,7 @@ function getToolDefinitions() {
|
|
|
1979
2248
|
},
|
|
1980
2249
|
{
|
|
1981
2250
|
name: "knit_finalize_team_worktree",
|
|
1982
|
-
description: "Merge or discard a team's worktree.
|
|
2251
|
+
description: "Merge or discard a team's worktree. Surfaces conflicts without destroying the worktree.",
|
|
1983
2252
|
inputSchema: {
|
|
1984
2253
|
type: "object",
|
|
1985
2254
|
properties: {
|
|
@@ -1992,16 +2261,51 @@ function getToolDefinitions() {
|
|
|
1992
2261
|
// ── Protocol Guard ───────────────────────────────────────────
|
|
1993
2262
|
{
|
|
1994
2263
|
name: "knit_set_protocol_strictness",
|
|
1995
|
-
description: "Set Protocol Guard strictness
|
|
2264
|
+
description: "Set Protocol Guard strictness: off | warn (default) | block.",
|
|
1996
2265
|
inputSchema: { type: "object", properties: { level: { type: "string", description: "One of: off | warn | block." } }, required: ["level"] }
|
|
1997
2266
|
},
|
|
1998
2267
|
{
|
|
1999
2268
|
name: "knit_get_protocol_strictness",
|
|
2000
|
-
description: "Read current Protocol Guard strictness
|
|
2269
|
+
description: "Read current Protocol Guard strictness.",
|
|
2270
|
+
inputSchema: { type: "object", properties: {} }
|
|
2271
|
+
},
|
|
2272
|
+
// ── Meta — feature discoverability ───────────────────────────
|
|
2273
|
+
{
|
|
2274
|
+
name: "knit_list_features",
|
|
2275
|
+
description: "List active vs hidden Knit tools and why. Call when a tool you expect isn't available.",
|
|
2001
2276
|
inputSchema: { type: "object", properties: {} }
|
|
2277
|
+
},
|
|
2278
|
+
{
|
|
2279
|
+
name: "knit_enable_feature",
|
|
2280
|
+
description: "Enable a Tier-2/3 feature flag (teams, subagents, admin). Persisted.",
|
|
2281
|
+
inputSchema: {
|
|
2282
|
+
type: "object",
|
|
2283
|
+
properties: { feature: { type: "string", description: "One of: teams, subagents, admin." } },
|
|
2284
|
+
required: ["feature"]
|
|
2285
|
+
}
|
|
2286
|
+
},
|
|
2287
|
+
{
|
|
2288
|
+
name: "knit_disable_feature",
|
|
2289
|
+
description: "Disable a feature flag. Auto-exposed tools stay visible regardless.",
|
|
2290
|
+
inputSchema: {
|
|
2291
|
+
type: "object",
|
|
2292
|
+
properties: { feature: { type: "string", description: "One of: teams, subagents, admin." } },
|
|
2293
|
+
required: ["feature"]
|
|
2294
|
+
}
|
|
2002
2295
|
}
|
|
2003
2296
|
];
|
|
2004
2297
|
}
|
|
2298
|
+
function getActiveToolDefinitions(shape) {
|
|
2299
|
+
const all = getToolDefinitions();
|
|
2300
|
+
if (!shape) return all;
|
|
2301
|
+
const activeNames = new Set(
|
|
2302
|
+
TOOL_REGISTRY.filter((info) => isToolActive(info, shape)).map((info) => info.tool)
|
|
2303
|
+
);
|
|
2304
|
+
return all.filter((def) => activeNames.has(def.name));
|
|
2305
|
+
}
|
|
2306
|
+
function getActiveToolDefinitionsForBrain(brain) {
|
|
2307
|
+
return getActiveToolDefinitions(detectProjectShape(brain));
|
|
2308
|
+
}
|
|
2005
2309
|
var handlers = {
|
|
2006
2310
|
knit_query_imports: handleQueryImports,
|
|
2007
2311
|
knit_query_dependents: handleQueryDependents,
|
|
@@ -2037,7 +2341,10 @@ var handlers = {
|
|
|
2037
2341
|
knit_get_suggestions: handleGetSuggestions,
|
|
2038
2342
|
knit_install_agent: handleInstallAgent,
|
|
2039
2343
|
knit_set_protocol_strictness: handleSetProtocolStrictness,
|
|
2040
|
-
knit_get_protocol_strictness: handleGetProtocolStrictness
|
|
2344
|
+
knit_get_protocol_strictness: handleGetProtocolStrictness,
|
|
2345
|
+
knit_list_features: handleListFeatures,
|
|
2346
|
+
knit_enable_feature: handleEnableFeature,
|
|
2347
|
+
knit_disable_feature: handleDisableFeature
|
|
2041
2348
|
};
|
|
2042
2349
|
function handleToolCall(toolName, params, brain) {
|
|
2043
2350
|
if (params.file_path) {
|
|
@@ -2059,6 +2366,8 @@ function handleToolCall(toolName, params, brain) {
|
|
|
2059
2366
|
return handler(params, brain);
|
|
2060
2367
|
}
|
|
2061
2368
|
export {
|
|
2369
|
+
getActiveToolDefinitions,
|
|
2370
|
+
getActiveToolDefinitionsForBrain,
|
|
2062
2371
|
getToolDefinitions,
|
|
2063
2372
|
handleToolCall
|
|
2064
2373
|
};
|
package/package.json
CHANGED