vibe-splain 1.1.0 → 2.0.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.
@@ -3,6 +3,8 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
3
3
  import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
4
4
  import { initParser } from '@vibe-splain/brain';
5
5
  import { handleScanProject, scanProjectTool } from './tools/scan_project.js';
6
+ import { handleGetProjectMap, getProjectMapTool } from './tools/get_project_map.js';
7
+ import { handleSetProjectBrief, setProjectBriefTool } from './tools/set_project_brief.js';
6
8
  import { handleGetFileContext, getFileContextTool } from './tools/get_file_context.js';
7
9
  import { handleWriteDecisionCard, writeDecisionCardTool } from './tools/write_decision_card.js';
8
10
  import { handleGetStrategicOverview, getStrategicOverviewTool } from './tools/get_strategic_overview.js';
@@ -14,6 +16,8 @@ import { handleMarkStale, markStaleTool } from './tools/mark_stale.js';
14
16
  // Use console.error() for all diagnostic output.
15
17
  const ALL_TOOLS = [
16
18
  scanProjectTool,
19
+ getProjectMapTool,
20
+ setProjectBriefTool,
17
21
  getFileContextTool,
18
22
  writeDecisionCardTool,
19
23
  getStrategicOverviewTool,
@@ -23,6 +27,8 @@ const ALL_TOOLS = [
23
27
  ];
24
28
  const TOOL_HANDLERS = {
25
29
  scan_project: handleScanProject,
30
+ get_project_map: handleGetProjectMap,
31
+ set_project_brief: handleSetProjectBrief,
26
32
  get_file_context: handleGetFileContext,
27
33
  write_decision_card: handleWriteDecisionCard,
28
34
  get_strategic_overview: handleGetStrategicOverview,
@@ -34,7 +40,7 @@ export async function startMCPServer() {
34
40
  // Initialize Tree-Sitter WASM once at startup
35
41
  await initParser();
36
42
  console.error('[vibe-splain] Tree-Sitter parser initialized');
37
- const server = new Server({ name: 'vibe-splain', version: '1.0.0' }, { capabilities: { tools: {}, prompts: {} } });
43
+ const server = new Server({ name: 'vibe-splain', version: '2.0.0' }, { capabilities: { tools: {}, prompts: {} } });
38
44
  // Register prompts
39
45
  server.setRequestHandler(ListPromptsRequestSchema, async () => ({
40
46
  prompts: [
@@ -55,7 +61,65 @@ export async function startMCPServer() {
55
61
  role: 'user',
56
62
  content: {
57
63
  type: 'text',
58
- text: 'Use the vibe-splain MCP tools to build a full architectural dossier for this project. Call scan_project first. Before writing any cards, define a strict set of 3-5 core architectural pillars, and force all categorizations into those predefined buckets to avoid fragmentation. Then for each high-gravity file, call get_file_context to read the source, synthesize a 3-5 sentence narrative explaining WHY the code exists, and call write_decision_card to persist it. When extracting evidence snippets, extract small, highly specific evidence snippets (5-20 lines). NEVER cite the entire file as evidence. If you find weird hacks, tech debt, or eccentric AI-generated code, document it as a Wild Discovery. Include Mermaid diagrams where they help explain data flow. When you\'re done, share the exact file:// UI link returned by the tool so I can view the dossier in my browser. Do NOT invent a localhost URL.',
64
+ text: `You are a skeptical staff engineer doing a HOSTILE architecture review of this codebase.
65
+ You are NOT writing documentation. You are finding the load-bearing walls, the landmines,
66
+ and the clever moves, and you are taking positions on them.
67
+
68
+ PROCESS — follow in order:
69
+ 1. Call scan_project, then get_project_map. The map gives you: the detected stack,
70
+ the FIXED set of pillars (you may not invent others), the Start-Here files (highest
71
+ gravity = most depended-upon), and Wild-Discovery candidates (highest heat = most smell).
72
+ 2. Read the map's stack and entrypoints. Write a 3-5 sentence project brief: what IS this,
73
+ what's the real stack, and — critically — which files are the actual application vs.
74
+ mockups/generated/vendored noise. Pass it via set_project_brief. Do this BEFORE any card.
75
+ 3. Work the Start-Here files first (highest gravity), then the Wild-Discovery files.
76
+ For each, call get_file_context. It returns hotSpans (the gnarliest functions) and
77
+ smellSpans (located tech debt) — base your evidence on THOSE, never on header comments.
78
+ 4. Write one decision card per file via write_decision_card.
79
+
80
+ This is an AUTONOMOUS loop. Every tool response includes a \`nextStep\` and often a
81
+ \`remainingFiles\` list — OBEY them. Do NOT stop, summarize, or ask the user "how would
82
+ you like to proceed" until every Start-Here and Wild-Discovery file has a card. Writing
83
+ the brief is the START of the work, not the end. Keep calling get_file_context +
84
+ write_decision_card until remainingFiles is empty.
85
+
86
+ RULES FOR EVERY CARD — non-negotiable:
87
+ - The \`thesis\` is a VERDICT in one sentence. Take a position. If you can't, you don't
88
+ understand the file yet — read more.
89
+ - Pick a \`category\`: Bottleneck, Hack, Smart-Move, Risk, Convention, or Dead-Weight.
90
+ - \`blastRadius\` must reference the real fan-in (get_file_context.importedBy).
91
+ - NEVER paraphrase the file's own comments. If the insight is already in a // block,
92
+ it is not insight — go deeper into the logic.
93
+ - Evidence = 5-20 lines of the ACTUAL interesting code (hotSpans/smellSpans). Never the
94
+ whole file, never the doc-header.
95
+ - For every Wild-Discovery candidate, name the specific smell and rate its severity.
96
+
97
+ ────────────────────────────────────────────────────────
98
+ EXAMPLE — what GOOD vs BAD looks like:
99
+
100
+ BAD (rejected — this is a book report):
101
+ title: "Panel Component Framework"
102
+ narrative: "This module establishes the structural framework for the panel-based
103
+ interface. It defines the generic Panel shell that standardizes look and feel..."
104
+ → Restates the header comment. No position. No risk. No tradeoff. Worthless.
105
+
106
+ GOOD (accepted):
107
+ title: "Panel shell carries 14 props and 6 tools in one file"
108
+ thesis: "cipher-panels-a.jsx is a god-file: one 600-line module owns the shared shell
109
+ AND three unrelated generators, so any panel change risks all of them."
110
+ category: "Risk" severity: 4
111
+ narrative: "Panel was built as a single shell to guarantee visual consistency, but the
112
+ three generators (Palette/Vibe/Pocket) were folded in beside it instead of
113
+ split out. The shell threads 14 props through every tool, so the generators
114
+ are now coupled to the shell's drag/compact state they don't use."
115
+ tradeoff: "Bought consistency and one import site; paid with a module no one can change
116
+ safely and props that leak shell concerns into pure generators."
117
+ blastRadius: "Imported by cipher-shell.jsx (the app root) — a regression here is a
118
+ full-app regression."
119
+ evidence: [ the 14-param Panel signature; the prop-drill into PalettePanel ]
120
+ ────────────────────────────────────────────────────────
121
+
122
+ When done, share the exact file:// UI link returned by scan_project. Never invent a URL.`,
59
123
  }
60
124
  }
61
125
  ]
@@ -12,6 +12,10 @@ export declare const getFileContextTool: {
12
12
  type: string;
13
13
  description: string;
14
14
  };
15
+ full: {
16
+ type: string;
17
+ description: string;
18
+ };
15
19
  };
16
20
  required: string[];
17
21
  };
@@ -1,20 +1,15 @@
1
1
  import { readFile } from 'fs/promises';
2
- import { join, relative } from 'path';
3
- import { readGraph } from '@vibe-splain/brain';
2
+ import { join, relative, isAbsolute } from 'path';
3
+ import { getFileAnalysis, readAnalysis } from '@vibe-splain/brain';
4
4
  export const getFileContextTool = {
5
5
  name: 'get_file_context',
6
- description: 'Returns the full source code of a specific high-gravity file, its cognitive weight breakdown, and its import graph neighbors. Call this for each file you want to synthesize a Decision Card for. Use the source + neighbors to understand what the code does and WHY it was written that way.',
6
+ description: 'Returns PRE-EXTRACTED evidence for a file so you do not have to read the whole thing and paraphrase its header comment. Returns: gravity/heat scores + signals, importedBy (named fan-in — use this for blastRadius), hotSpans (the gnarliest function bodies, comment-stripped, each with a reason), smellSpans (located tech debt with ±3 lines of context), and signature (the exported API surface). Base your evidence on hotSpans/smellSpans — NEVER on header comments. Pass { full: true } only if you truly need the raw source.',
7
7
  inputSchema: {
8
8
  type: 'object',
9
9
  properties: {
10
- projectRoot: {
11
- type: 'string',
12
- description: 'Absolute path to the project root',
13
- },
14
- filePath: {
15
- type: 'string',
16
- description: 'Relative or absolute path to the file',
17
- },
10
+ projectRoot: { type: 'string', description: 'Absolute path to the project root' },
11
+ filePath: { type: 'string', description: 'Relative or absolute path to the file' },
12
+ full: { type: 'boolean', description: 'Set true to also return the raw source. Default false.' },
18
13
  },
19
14
  required: ['projectRoot', 'filePath'],
20
15
  },
@@ -22,27 +17,35 @@ export const getFileContextTool = {
22
17
  export async function handleGetFileContext(args) {
23
18
  const projectRoot = args.projectRoot;
24
19
  const filePath = args.filePath;
20
+ const full = args.full === true;
25
21
  if (!projectRoot || !filePath)
26
22
  throw new Error('projectRoot and filePath are required');
27
- const fullPath = filePath.startsWith('/') ? filePath : join(projectRoot, filePath);
23
+ const fullPath = isAbsolute(filePath) ? filePath : join(projectRoot, filePath);
28
24
  const relPath = relative(projectRoot, fullPath);
29
- const source = await readFile(fullPath, 'utf8');
30
- const graph = await readGraph(projectRoot);
31
- // Find neighbors in import graph
32
- const neighbors = [];
33
- if (graph) {
34
- for (const edge of graph.edges) {
35
- if (edge.from === relPath)
36
- neighbors.push(edge.to);
37
- if (edge.to === relPath || edge.to.endsWith(relPath))
38
- neighbors.push(edge.from);
39
- }
25
+ const evidence = await getFileAnalysis(fullPath);
26
+ if (!evidence) {
27
+ throw new Error(`Could not analyze ${relPath} (unsupported language or parse failure).`);
40
28
  }
41
- return {
29
+ const store = await readAnalysis(projectRoot);
30
+ const persisted = store?.files[relPath];
31
+ const result = {
42
32
  filePath: relPath,
43
- source,
44
- lineCount: source.split('\n').length,
45
- neighbors: [...new Set(neighbors)],
33
+ language: evidence.language,
34
+ gravity: persisted ? Math.round(persisted.gravity) : null,
35
+ heat: persisted ? Math.round(persisted.heat) : null,
36
+ isRealSource: persisted?.isRealSource ?? null,
37
+ demoteReason: persisted?.demoteReason ?? null,
38
+ gravitySignals: persisted?.gravitySignals ?? null,
39
+ heatSignals: evidence.heatSignals,
40
+ importedBy: persisted?.importedBy ?? [],
41
+ imports: persisted?.imports ?? [],
42
+ signature: evidence.signature,
43
+ hotSpans: evidence.hotSpans,
44
+ smellSpans: evidence.smellSpans,
46
45
  };
46
+ if (full) {
47
+ result.source = await readFile(fullPath, 'utf8');
48
+ }
49
+ return result;
47
50
  }
48
51
  //# sourceMappingURL=get_file_context.js.map
@@ -0,0 +1,15 @@
1
+ export declare const getProjectMapTool: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ projectRoot: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ };
12
+ required: string[];
13
+ };
14
+ };
15
+ export declare function handleGetProjectMap(args: Record<string, unknown>): Promise<unknown>;
@@ -0,0 +1,41 @@
1
+ import { readDossier } from '@vibe-splain/brain';
2
+ export const getProjectMapTool = {
3
+ name: 'get_project_map',
4
+ description: 'Returns the project map produced by scan_project: the detected stack, entrypoints, the FIXED set of architectural pillars (you may NOT invent others — write_decision_card rejects unknown pillars), the Start-Here files (highest gravity = most depended-upon), and the Wild-Discovery candidates (highest heat = most tech debt). BEFORE writing any card you MUST: read this map, write a 3-5 sentence project brief, and persist it via set_project_brief.',
5
+ inputSchema: {
6
+ type: 'object',
7
+ properties: {
8
+ projectRoot: { type: 'string', description: 'Absolute path to the project root' },
9
+ },
10
+ required: ['projectRoot'],
11
+ },
12
+ };
13
+ export async function handleGetProjectMap(args) {
14
+ const projectRoot = args.projectRoot;
15
+ if (!projectRoot)
16
+ throw new Error('projectRoot is required');
17
+ const dossier = await readDossier(projectRoot);
18
+ if (!dossier || !dossier.map) {
19
+ return { error: 'No project map found. Run scan_project first.' };
20
+ }
21
+ const m = dossier.map;
22
+ return {
23
+ stack: m.stack,
24
+ entrypoints: m.entrypoints,
25
+ fileCount: m.fileCount,
26
+ realSourceCount: m.realSourceCount,
27
+ pillars: m.pillars.map(p => ({
28
+ name: p.name,
29
+ description: p.description,
30
+ memberFiles: p.memberFiles,
31
+ })),
32
+ legalPillarNames: m.pillars.map(p => p.name),
33
+ startHere: m.topGravity,
34
+ wildDiscoveryCandidates: m.topHeat,
35
+ brief: m.brief,
36
+ nextStep: m.brief
37
+ ? 'Brief is set. Work the Start-Here files first via get_file_context, then write_decision_card.'
38
+ : 'Write a 3-5 sentence brief and call set_project_brief BEFORE any card.',
39
+ };
40
+ }
41
+ //# sourceMappingURL=get_project_map.js.map
@@ -29,14 +29,21 @@ export async function handleMarkStale(args) {
29
29
  }
30
30
  let staleCount = 0;
31
31
  for (const filePath of filePaths) {
32
+ const matches = (card) => card.primaryFile === filePath ||
33
+ filePath.endsWith(card.primaryFile || '\0') ||
34
+ card.evidence.some(e => e.file === filePath || filePath.endsWith(e.file));
32
35
  for (const pillar of dossier.pillars) {
33
36
  for (const card of pillar.decisions) {
34
- if (card.evidence.some(e => e.file === filePath || filePath.endsWith(e.file))) {
37
+ if (matches(card)) {
35
38
  card.status = 'stale';
36
39
  staleCount++;
37
40
  }
38
41
  }
39
42
  }
43
+ for (const card of dossier.wildDiscoveries) {
44
+ if (matches(card))
45
+ card.status = 'stale';
46
+ }
40
47
  if (!dossier.stalePaths.includes(filePath)) {
41
48
  dossier.stalePaths.push(filePath);
42
49
  }
@@ -1,7 +1,7 @@
1
1
  import { scanProject, writeDossier, readDossier, startWatcher } from '@vibe-splain/brain';
2
2
  export const scanProjectTool = {
3
3
  name: 'scan_project',
4
- description: 'Scans a codebase and returns its structural analysis. CALL THIS FIRST before any other tool. Returns High-Gravity files grouped by pillar, plus wildCandidates for unusual high-complexity files. After calling this tool, call get_file_context for each file in highGravityFiles, synthesize a narrative explaining WHY that code exists, then call write_decision_card to persist it. The uiUrl in the response is a file:// link — share it with the user so they can open the Dossier UI in their browser.',
4
+ description: 'Scans a codebase (TS/JS/Python/Go/Rust/Java) and returns a structural analysis. CALL THIS FIRST, then call get_project_map. Files are scored on two axes: GRAVITY (importance — fan-in + PageRank centrality) and HEAT (smell/tech-debt). Mockups, vendored code, and orphan files are demoted (isRealSource:false) so cards target the real application. After scanning, call get_project_map to get the fixed pillar set, Start-Here (top gravity) and Wild-Discovery (top heat) lists. The uiUrl is a file:// link — share it with the user.',
5
5
  inputSchema: {
6
6
  type: 'object',
7
7
  properties: {
@@ -19,47 +19,43 @@ export async function handleScanProject(args) {
19
19
  throw new Error('projectRoot is required');
20
20
  console.error(`[vibe-splain] Scanning project: ${projectRoot}`);
21
21
  const result = await scanProject(projectRoot);
22
- // Create initial dossier structure
23
- const existingDossier = await readDossier(projectRoot);
24
- const dossier = existingDossier || {
25
- version: '1.0.0',
22
+ // Preserve any existing cards; replace the structural map every scan.
23
+ const existing = await readDossier(projectRoot);
24
+ const brief = existing?.map?.brief ?? null;
25
+ const dossier = {
26
+ version: '2.0.0',
26
27
  scannedAt: new Date().toISOString(),
27
28
  projectRoot,
28
- pillars: [],
29
- wildDiscoveries: [],
30
- stalePaths: [],
29
+ map: { ...result.map, brief },
30
+ pillars: existing?.pillars ?? [],
31
+ wildDiscoveries: existing?.wildDiscoveries ?? [],
32
+ stalePaths: existing?.stalePaths ?? [],
31
33
  };
32
- // Update scan timestamp
33
- dossier.scannedAt = new Date().toISOString();
34
- // Create pillar entries from scan results
35
- for (const group of result.pillarGroups) {
36
- const existingPillar = dossier.pillars.find(p => p.name === group.name);
37
- if (!existingPillar) {
38
- dossier.pillars.push({ name: group.name, cardCount: 0, decisions: [] });
34
+ // Seed empty pillar buckets from the fixed graph-derived pillar set.
35
+ for (const def of result.map.pillars) {
36
+ if (!dossier.pillars.find(p => p.name === def.name)) {
37
+ dossier.pillars.push({ name: def.name, cardCount: 0, decisions: [] });
39
38
  }
40
39
  }
41
40
  await writeDossier(projectRoot, dossier);
42
- // Start file watcher on high-gravity files
43
- const watchPaths = result.highGravityFiles.map(f => f.path);
44
- startWatcher(projectRoot, watchPaths);
45
- console.error(`[vibe-splain] Scan complete. ${result.totalFilesScanned} files scanned, ${result.highGravityFiles.length} high-gravity files found.`);
41
+ // Watch the real-source files for staleness.
42
+ startWatcher(projectRoot, result.files.map(f => f.path));
43
+ console.error(`[vibe-splain] Scan complete. ${result.totalFilesScanned} files, ${result.realSourceCount} real-source, ${result.wildCandidates.length} wild candidates.`);
46
44
  return {
47
45
  projectRoot: result.projectRoot,
48
46
  totalFilesScanned: result.totalFilesScanned,
49
- highGravityFiles: result.highGravityFiles.map(f => ({
47
+ realSourceCount: result.realSourceCount,
48
+ stack: result.map.stack,
49
+ entrypoints: result.map.entrypoints,
50
+ pillars: result.map.pillars.map(p => ({ name: p.name, fileCount: p.memberFiles.length })),
51
+ startHere: result.map.topGravity,
52
+ wildDiscoveryCandidates: result.wildCandidates.map(f => ({
50
53
  relativePath: f.relativePath,
51
- cognitiveWeight: f.cognitiveWeight,
52
- pillars: f.pillars,
53
- })),
54
- pillarGroups: result.pillarGroups.map(g => ({
55
- name: g.name,
56
- fileCount: g.files.length,
57
- files: g.files.map(f => f.relativePath),
58
- })),
59
- wildCandidates: result.wildCandidates.map(f => ({
60
- relativePath: f.relativePath,
61
- cognitiveWeight: f.cognitiveWeight,
54
+ heat: Math.round(f.heat),
55
+ gravity: Math.round(f.gravity),
56
+ topSmells: f.smells.filter(s => s.severity >= 3).slice(0, 3).map(s => s.note),
62
57
  })),
58
+ nextStep: 'Call get_project_map, write a project brief via set_project_brief, THEN write cards starting from the Start-Here files.',
63
59
  uiUrl: result.uiUrl,
64
60
  };
65
61
  }
@@ -0,0 +1,19 @@
1
+ export declare const setProjectBriefTool: {
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ projectRoot: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ brief: {
12
+ type: string;
13
+ description: string;
14
+ };
15
+ };
16
+ required: string[];
17
+ };
18
+ };
19
+ export declare function handleSetProjectBrief(args: Record<string, unknown>): Promise<unknown>;
@@ -0,0 +1,42 @@
1
+ import { readDossier, writeDossier } from '@vibe-splain/brain';
2
+ export const setProjectBriefTool = {
3
+ name: 'set_project_brief',
4
+ description: 'Persists your 3-5 sentence project brief into the dossier (and regenerates the UI). Call this AFTER get_project_map and BEFORE writing any decision card. The brief must say: what this project IS, the real stack, and — critically — which files are the actual application vs. mockups/generated/vendored noise.',
5
+ inputSchema: {
6
+ type: 'object',
7
+ properties: {
8
+ projectRoot: { type: 'string', description: 'Absolute path to the project root' },
9
+ brief: { type: 'string', description: '3-5 sentence project brief. What is this, the real stack, app vs. noise.' },
10
+ },
11
+ required: ['projectRoot', 'brief'],
12
+ },
13
+ };
14
+ export async function handleSetProjectBrief(args) {
15
+ const projectRoot = args.projectRoot;
16
+ const brief = args.brief;
17
+ if (!projectRoot || !brief)
18
+ throw new Error('projectRoot and brief are required');
19
+ const dossier = await readDossier(projectRoot);
20
+ if (!dossier || !dossier.map) {
21
+ return { error: 'No project map found. Run scan_project first.' };
22
+ }
23
+ dossier.map.brief = brief;
24
+ await writeDossier(projectRoot, dossier);
25
+ // Drive the loop: hand back the exact remaining worklist so the agent does
26
+ // not stop and ask the user. Weak models treat "brief saved" as done otherwise.
27
+ const documented = new Set([...dossier.pillars.flatMap(p => p.decisions), ...dossier.wildDiscoveries]
28
+ .map(c => c.primaryFile).filter(Boolean));
29
+ const startHere = dossier.map.topGravity.filter(f => !documented.has(f));
30
+ const wild = dossier.map.topHeat.filter(f => !documented.has(f));
31
+ const worklist = [...new Set([...startHere, ...wild])];
32
+ return {
33
+ success: true,
34
+ brief,
35
+ remainingFiles: worklist,
36
+ legalPillarNames: dossier.map.pillars.map(p => p.name),
37
+ nextStep: worklist.length === 0
38
+ ? 'All files documented. Share the file:// UI link from scan_project.'
39
+ : `Brief saved. DO NOT STOP and DO NOT ask the user what to do next. Now loop: for EACH of the ${worklist.length} files in remainingFiles, call get_file_context then write_decision_card. Start with "${worklist[0]}". Continue until every file has a card, then share the file:// UI link.`,
40
+ };
41
+ }
42
+ //# sourceMappingURL=set_project_brief.js.map
@@ -1,3 +1,4 @@
1
+ import type { CardCategory } from '@vibe-splain/brain';
1
2
  export declare const writeDecisionCardTool: {
2
3
  name: string;
3
4
  description: string;
@@ -6,20 +7,47 @@ export declare const writeDecisionCardTool: {
6
7
  properties: {
7
8
  projectRoot: {
8
9
  type: string;
9
- description: string;
10
10
  };
11
11
  pillar: {
12
12
  type: string;
13
13
  description: string;
14
14
  };
15
+ primaryFile: {
16
+ type: string;
17
+ description: string;
18
+ };
15
19
  title: {
16
20
  type: string;
21
+ };
22
+ thesis: {
23
+ type: string;
17
24
  description: string;
18
25
  };
26
+ category: {
27
+ type: string;
28
+ enum: CardCategory[];
29
+ };
30
+ severity: {
31
+ type: string;
32
+ minimum: number;
33
+ maximum: number;
34
+ };
19
35
  narrative: {
20
36
  type: string;
21
37
  description: string;
22
38
  };
39
+ tradeoff: {
40
+ type: string;
41
+ description: string;
42
+ };
43
+ blastRadius: {
44
+ type: string;
45
+ description: string;
46
+ };
47
+ confidence: {
48
+ type: string;
49
+ enum: string[];
50
+ };
23
51
  evidence: {
24
52
  type: string;
25
53
  items: {
@@ -27,19 +55,15 @@ export declare const writeDecisionCardTool: {
27
55
  properties: {
28
56
  file: {
29
57
  type: string;
30
- description: string;
31
58
  };
32
59
  startLine: {
33
60
  type: string;
34
- description: string;
35
61
  };
36
62
  endLine: {
37
63
  type: string;
38
- description: string;
39
64
  };
40
65
  snippet: {
41
66
  type: string;
42
- description: string;
43
67
  };
44
68
  };
45
69
  required: string[];