@triedotdev/mcp 1.0.109 → 1.0.111

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.
@@ -1,15 +1,27 @@
1
1
  import {
2
2
  getGuardian
3
- } from "./chunk-SUHYYM2J.js";
3
+ } from "./chunk-SDS3UVFY.js";
4
+ import {
5
+ IncidentIndex,
6
+ LearningEngine,
7
+ exportToJson,
8
+ formatFriendlyError,
9
+ getLastCheckpoint,
10
+ listCheckpoints,
11
+ perceiveCurrentChanges,
12
+ reasonAboutChangesHumanReadable,
13
+ saveCheckpoint
14
+ } from "./chunk-QYOACM2C.js";
4
15
  import {
5
16
  ContextGraph,
6
17
  TieredStorage,
7
18
  findCrossProjectPatterns,
8
19
  getKeyFromKeychain,
20
+ getStorage,
9
21
  isAIAvailable,
10
- runAIAnalysis,
22
+ runAIWithTools,
11
23
  setAPIKey
12
- } from "./chunk-OTTR5JX4.js";
24
+ } from "./chunk-6QR6QZIX.js";
13
25
  import {
14
26
  getGuardianState
15
27
  } from "./chunk-6JPPYG7F.js";
@@ -260,7 +272,7 @@ import { render } from "ink";
260
272
  import React10 from "react";
261
273
 
262
274
  // src/cli/dashboard/App.tsx
263
- import { useState as useState3, useEffect as useEffect4, useCallback as useCallback6, useRef } from "react";
275
+ import { useState as useState3, useEffect as useEffect4, useCallback as useCallback6, useRef as useRef2 } from "react";
264
276
  import { Box as Box12, useInput as useInput8, useApp } from "ink";
265
277
 
266
278
  // src/cli/dashboard/state.tsx
@@ -278,34 +290,35 @@ function getMemoryTreeNodes(state) {
278
290
  const nodes = [];
279
291
  const { expandedNodes, snapshot, globalPatterns } = state.memoryTree;
280
292
  if (!snapshot) return nodes;
281
- const fileNodes = snapshot.nodes.filter((n) => n.type === "file");
282
- const changeNodes = snapshot.nodes.filter((n) => n.type === "change");
283
- const patternNodes = snapshot.nodes.filter((n) => n.type === "pattern");
284
- const incidentNodes = snapshot.nodes.filter((n) => n.type === "incident");
285
293
  const decisionNodes = snapshot.nodes.filter((n) => n.type === "decision");
286
- nodes.push({ id: "files", level: 0 });
287
- if (expandedNodes.has("files")) {
288
- fileNodes.slice(0, 15).forEach((n) => nodes.push({ id: `file-${n.id}`, level: 1 }));
289
- }
290
- nodes.push({ id: "changes", level: 0 });
291
- if (expandedNodes.has("changes")) {
292
- changeNodes.slice(0, 10).forEach((n) => nodes.push({ id: `change-${n.id}`, level: 1 }));
293
- }
294
- nodes.push({ id: "patterns", level: 0 });
295
- if (expandedNodes.has("patterns")) {
296
- patternNodes.slice(0, 10).forEach((n) => nodes.push({ id: `pattern-${n.id}`, level: 1 }));
294
+ const incidentNodes = snapshot.nodes.filter((n) => n.type === "incident");
295
+ const patternNodes = snapshot.nodes.filter((n) => n.type === "pattern");
296
+ const fileNodes = snapshot.nodes.filter((n) => n.type === "file");
297
+ const hotspots = fileNodes.filter((n) => {
298
+ const risk = n.data.riskLevel;
299
+ return risk === "critical" || risk === "high";
300
+ });
301
+ nodes.push({ id: "decisions", level: 0 });
302
+ if (expandedNodes.has("decisions")) {
303
+ decisionNodes.slice(0, 10).forEach((n) => nodes.push({ id: `decision-${n.id}`, level: 1 }));
297
304
  }
298
305
  nodes.push({ id: "incidents", level: 0 });
299
306
  if (expandedNodes.has("incidents")) {
300
307
  incidentNodes.slice(0, 10).forEach((n) => nodes.push({ id: `incident-${n.id}`, level: 1 }));
301
308
  }
302
- nodes.push({ id: "decisions", level: 0 });
303
- if (expandedNodes.has("decisions")) {
304
- decisionNodes.slice(0, 10).forEach((n) => nodes.push({ id: `decision-${n.id}`, level: 1 }));
309
+ nodes.push({ id: "patterns", level: 0 });
310
+ if (expandedNodes.has("patterns")) {
311
+ patternNodes.slice(0, 10).forEach((n) => nodes.push({ id: `pattern-${n.id}`, level: 1 }));
305
312
  }
306
313
  nodes.push({ id: "cross-project", level: 0 });
307
314
  if (expandedNodes.has("cross-project")) {
308
- globalPatterns.slice(0, 5).forEach((p) => nodes.push({ id: `global-${p.id}`, level: 1 }));
315
+ globalPatterns.slice(0, 8).forEach((p) => nodes.push({ id: `global-${p.id}`, level: 1 }));
316
+ }
317
+ if (hotspots.length > 0) {
318
+ nodes.push({ id: "hotspots", level: 0 });
319
+ if (expandedNodes.has("hotspots")) {
320
+ hotspots.slice(0, 10).forEach((n) => nodes.push({ id: `file-${n.id}`, level: 1 }));
321
+ }
309
322
  }
310
323
  return nodes;
311
324
  }
@@ -321,6 +334,10 @@ function handleStreamUpdate(state, update) {
321
334
  delete s.scanEndTime;
322
335
  }
323
336
  s.progress = update.data;
337
+ const delta = update.data.processedFiles - oldProgress.processedFiles;
338
+ if (delta > 0) {
339
+ s.watch = { ...s.watch, filesScannedSession: s.watch.filesScannedSession + delta };
340
+ }
324
341
  if (update.data.issuesBySeverity?.critical > 0) {
325
342
  s.alerts = { hasCritical: true, lastCriticalAt: update.timestamp };
326
343
  }
@@ -361,7 +378,8 @@ function handleStreamUpdate(state, update) {
361
378
  directories: update.data.directories ?? s.watch.directories,
362
379
  debounceMs: update.data.debounceMs ?? s.watch.debounceMs,
363
380
  lastChange: update.data.lastChange ?? s.watch.lastChange,
364
- recentChanges: update.data.recentChanges ?? s.watch.recentChanges
381
+ recentChanges: update.data.recentChanges ?? s.watch.recentChanges,
382
+ filesScannedSession: s.watch.filesScannedSession
365
383
  };
366
384
  if (update.data.watching !== void 0) {
367
385
  s = addActivity(s, update.data.watching ? `[*] Watch mode: ACTIVE (${update.data.directories ?? 0} dirs)` : "[*] Watch mode: OFF");
@@ -372,7 +390,8 @@ function handleStreamUpdate(state, update) {
372
390
  s.watch = {
373
391
  ...s.watch,
374
392
  recentChanges: [entry, ...s.watch.recentChanges].slice(0, 5),
375
- lastChange: entry.time
393
+ lastChange: entry.time,
394
+ filesScannedSession: s.watch.filesScannedSession + 1
376
395
  };
377
396
  s = addActivity(s, `Change detected: ${update.data.file}`);
378
397
  break;
@@ -557,7 +576,7 @@ function dashboardReducer(state, action) {
557
576
  case "SELECT_MEMORY_NODE":
558
577
  return { ...state, memoryTree: { ...state.memoryTree, selectedNode: action.nodeId } };
559
578
  case "TOGGLE_MEMORY_NODE": {
560
- const expandable = ["files", "changes", "patterns", "incidents", "decisions", "cross-project"];
579
+ const expandable = ["decisions", "incidents", "patterns", "cross-project", "hotspots"];
561
580
  if (!expandable.includes(action.nodeId)) return state;
562
581
  const expanded = new Set(state.memoryTree.expandedNodes);
563
582
  if (expanded.has(action.nodeId)) expanded.delete(action.nodeId);
@@ -577,14 +596,21 @@ function dashboardReducer(state, action) {
577
596
  };
578
597
  case "SET_CHAT_INPUT":
579
598
  return { ...state, chatState: { ...state.chatState, inputBuffer: action.buffer } };
580
- case "ADD_CHAT_MESSAGE":
599
+ case "ADD_CHAT_MESSAGE": {
600
+ const msg = {
601
+ role: action.role,
602
+ content: action.content,
603
+ timestamp: Date.now()
604
+ };
605
+ if (action.toolCalls && action.toolCalls.length > 0) msg.toolCalls = action.toolCalls;
581
606
  return {
582
607
  ...state,
583
608
  chatState: {
584
609
  ...state.chatState,
585
- messages: [...state.chatState.messages, { role: action.role, content: action.content, timestamp: Date.now() }].slice(-20)
610
+ messages: [...state.chatState.messages, msg].slice(-20)
586
611
  }
587
612
  };
613
+ }
588
614
  case "SET_CHAT_LOADING":
589
615
  return { ...state, chatState: { ...state.chatState, loading: action.loading } };
590
616
  case "SET_AGENT_CONFIG":
@@ -684,7 +710,7 @@ function createInitialState() {
684
710
  questionsExtracted: 0
685
711
  },
686
712
  alerts: { hasCritical: false },
687
- watch: { watching: false, directories: 0, recentChanges: [] },
713
+ watch: { watching: false, directories: 0, recentChanges: [], filesScannedSession: 0 },
688
714
  rawLog: [],
689
715
  rawLogPage: 0,
690
716
  scrollPositions: { overview: 0, rawlog: 0, agent: 0, goals: 0, hypotheses: 0, memory: 0, chat: 0 },
@@ -707,7 +733,7 @@ function createInitialState() {
707
733
  },
708
734
  goalsPanel: { goals: [], selectedIndex: 0, selectedAchievedIndex: 0, inputMode: "browse", inputBuffer: "", lastRefresh: 0 },
709
735
  hypothesesPanel: { hypotheses: [], selectedIndex: 0, selectedCompletedIndex: 0, inputMode: "browse", inputBuffer: "", lastRefresh: 0 },
710
- memoryTree: { loaded: false, snapshot: null, globalPatterns: [], expandedNodes: /* @__PURE__ */ new Set(["files"]), selectedNode: "files", scrollPosition: 0, lastRefresh: 0 },
736
+ memoryTree: { loaded: false, snapshot: null, globalPatterns: [], expandedNodes: /* @__PURE__ */ new Set(["decisions"]), selectedNode: "decisions", scrollPosition: 0, lastRefresh: 0 },
711
737
  agentBrain: { loaded: false, decisions: [], patterns: [], ledgerHash: null, selectedIndex: 0, expandedIndex: null },
712
738
  chatState: { messages: [], inputBuffer: "", loading: false }
713
739
  };
@@ -1275,8 +1301,8 @@ function getOutputManager() {
1275
1301
  }
1276
1302
 
1277
1303
  // src/cli/dashboard/App.tsx
1278
- import { existsSync } from "fs";
1279
- import { readFile, writeFile, mkdir } from "fs/promises";
1304
+ import { existsSync as existsSync2 } from "fs";
1305
+ import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
1280
1306
  import { join } from "path";
1281
1307
 
1282
1308
  // src/cli/dashboard/components/Header.tsx
@@ -1333,10 +1359,10 @@ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1333
1359
  var VIEW_LABELS = {
1334
1360
  overview: "Overview",
1335
1361
  rawlog: "Log",
1336
- agent: "Agent",
1362
+ agent: "Nudges",
1337
1363
  goals: "Goals",
1338
1364
  hypotheses: "Hypotheses",
1339
- memory: "Memory",
1365
+ memory: "Ledger",
1340
1366
  chat: "Chat"
1341
1367
  };
1342
1368
  var TAB_VIEWS = ["overview", "memory", "goals", "hypotheses", "agent", "chat"];
@@ -1355,7 +1381,7 @@ function Footer() {
1355
1381
  } else if (view === "agent") {
1356
1382
  contextHints = "tab views \xB7 j/k nav \xB7 enter expand \xB7 d dismiss";
1357
1383
  } else if (view === "memory") {
1358
- contextHints = "tab views \xB7 j/k nav \xB7 enter toggle section";
1384
+ contextHints = "tab views \xB7 j/k nav \xB7 enter expand";
1359
1385
  } else if (view === "chat") {
1360
1386
  contextHints = "tab views \xB7 type to ask \xB7 enter send \xB7 esc clear";
1361
1387
  } else if (view === "rawlog") {
@@ -1571,7 +1597,7 @@ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1571
1597
  function OverviewView() {
1572
1598
  const { state } = useDashboard();
1573
1599
  const { progress, signalExtraction, watch, issues, activityLog, activityPage } = state;
1574
- const { totalIssues, processedFiles } = progress;
1600
+ const { totalIssues } = progress;
1575
1601
  const endTime = state.scanComplete && state.scanEndTime ? state.scanEndTime : Date.now();
1576
1602
  const elapsed = ((endTime - state.scanStartTime) / 1e3).toFixed(1);
1577
1603
  const criticalIssues = issues.filter((i) => i.severity === "critical").slice(0, 3);
@@ -1582,8 +1608,8 @@ function OverviewView() {
1582
1608
  /* @__PURE__ */ jsxs5(Text5, { children: [
1583
1609
  /* @__PURE__ */ jsx6(Text5, { dimColor: true, children: "\u25CF" }),
1584
1610
  " Scanned ",
1585
- /* @__PURE__ */ jsx6(Text5, { bold: true, children: processedFiles }),
1586
- " files ",
1611
+ /* @__PURE__ */ jsx6(Text5, { bold: true, children: watch.filesScannedSession }),
1612
+ " files this session ",
1587
1613
  /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
1588
1614
  elapsed,
1589
1615
  "s"
@@ -1739,60 +1765,23 @@ function AgentView() {
1739
1765
  const patCount = patterns.length;
1740
1766
  if (alertCount === 0 && decCount === 0 && patCount === 0 && !loaded) {
1741
1767
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
1742
- /* @__PURE__ */ jsx7(Text6, { bold: true, children: "Trie" }),
1768
+ /* @__PURE__ */ jsx7(Text6, { bold: true, children: "Nudges" }),
1743
1769
  /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " Loading..." })
1744
1770
  ] });
1745
1771
  }
1746
1772
  if (alertCount === 0 && decCount === 0 && patCount === 0) {
1747
1773
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
1748
- /* @__PURE__ */ jsxs6(Text6, { children: [
1749
- /* @__PURE__ */ jsx7(Text6, { bold: true, children: "Trie" }),
1750
- " ",
1751
- /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "Proactive code guardian" })
1752
- ] }),
1753
- /* @__PURE__ */ jsx7(Text6, { children: " " }),
1754
- /* @__PURE__ */ jsxs6(Text6, { children: [
1755
- " ",
1756
- /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u25CF" }),
1757
- " Alerts you about gotchas before you push"
1758
- ] }),
1759
- /* @__PURE__ */ jsxs6(Text6, { children: [
1760
- " ",
1761
- /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u25CF" }),
1762
- " Records decisions with traceable hashes"
1763
- ] }),
1764
- /* @__PURE__ */ jsxs6(Text6, { children: [
1765
- " ",
1766
- /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u25CF" }),
1767
- " Learns patterns from your feedback (trie ok / trie bad)"
1768
- ] }),
1769
- /* @__PURE__ */ jsxs6(Text6, { children: [
1770
- " ",
1771
- /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u25CF" }),
1772
- " Gets smarter over time"
1773
- ] }),
1774
- /* @__PURE__ */ jsx7(Text6, { children: " " }),
1775
- /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " Start watching to get proactive alerts," }),
1776
- /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " or run trie tell / trie ok / trie bad to teach it." }),
1774
+ /* @__PURE__ */ jsx7(Text6, { bold: true, children: "Nudges" }),
1777
1775
  /* @__PURE__ */ jsx7(Text6, { children: " " }),
1778
- isAIAvailable() ? /* @__PURE__ */ jsxs6(Text6, { children: [
1779
- /* @__PURE__ */ jsx7(Text6, { color: "green", children: "\u25CF" }),
1780
- " ",
1781
- /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "AI-enhanced analysis enabled" })
1782
- ] }) : /* @__PURE__ */ jsxs6(Text6, { children: [
1783
- /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u25CB" }),
1784
- " Set ",
1785
- /* @__PURE__ */ jsx7(Text6, { bold: true, children: "ANTHROPIC_API_KEY" }),
1786
- " ",
1787
- /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "for AI insights" })
1788
- ] })
1776
+ /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " No nudges yet." }),
1777
+ /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: " Trie will alert you here when it spots issues in your code." })
1789
1778
  ] });
1790
1779
  }
1791
1780
  const confidentPatterns = patterns.filter((p) => p.confidence > 0.7).length;
1792
1781
  const learningPatterns = patterns.filter((p) => p.confidence <= 0.7).length;
1793
1782
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingX: 1, children: [
1794
1783
  /* @__PURE__ */ jsxs6(Text6, { children: [
1795
- /* @__PURE__ */ jsx7(Text6, { bold: true, children: "Trie" }),
1784
+ /* @__PURE__ */ jsx7(Text6, { bold: true, children: "Nudges" }),
1796
1785
  " ",
1797
1786
  /* @__PURE__ */ jsxs6(Text6, { dimColor: true, children: [
1798
1787
  alertCount,
@@ -1816,14 +1805,14 @@ function AgentView() {
1816
1805
  const isExpanded = idx === expandedInsight;
1817
1806
  const ago = formatTimeAgo(insight.timestamp);
1818
1807
  const msg = insight.message.slice(0, 60) + (insight.message.length > 60 ? "..." : "");
1819
- const riskColor2 = insight.priority >= 8 ? "red" : insight.priority >= 5 ? "yellow" : void 0;
1808
+ const riskColor = insight.priority >= 8 ? "red" : insight.priority >= 5 ? "yellow" : void 0;
1820
1809
  return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
1821
1810
  /* @__PURE__ */ jsxs6(Text6, { children: [
1822
1811
  isSelected ? /* @__PURE__ */ jsxs6(Text6, { bold: true, color: "green", children: [
1823
1812
  ">",
1824
1813
  " "
1825
1814
  ] }) : /* @__PURE__ */ jsx7(Text6, { children: " " }),
1826
- riskColor2 ? /* @__PURE__ */ jsx7(Text6, { color: riskColor2, children: "\u25CF" }) : /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u25CB" }),
1815
+ riskColor ? /* @__PURE__ */ jsx7(Text6, { color: riskColor, children: "\u25CF" }) : /* @__PURE__ */ jsx7(Text6, { dimColor: true, children: "\u25CB" }),
1827
1816
  " ",
1828
1817
  isSelected ? /* @__PURE__ */ jsx7(Text6, { bold: true, children: msg }) : /* @__PURE__ */ jsx7(Text6, { children: msg }),
1829
1818
  " ",
@@ -2304,20 +2293,7 @@ function HypothesesView() {
2304
2293
  // src/cli/dashboard/views/MemoryTreeView.tsx
2305
2294
  import { useEffect as useEffect3, useCallback as useCallback4 } from "react";
2306
2295
  import { Box as Box9, Text as Text9, useInput as useInput5 } from "ink";
2307
- import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2308
- var RISK_ORDER = { critical: 0, high: 1, medium: 2, low: 3, unknown: 4 };
2309
- function riskColor(risk) {
2310
- switch (risk) {
2311
- case "critical":
2312
- return "red";
2313
- case "high":
2314
- return "yellow";
2315
- case "medium":
2316
- return "blue";
2317
- default:
2318
- return void 0;
2319
- }
2320
- }
2296
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2321
2297
  function timeAgo2(iso) {
2322
2298
  const ms = Date.now() - new Date(iso).getTime();
2323
2299
  const mins = Math.floor(ms / 6e4);
@@ -2356,126 +2332,116 @@ function MemoryTreeView() {
2356
2332
  const sel = (nodeId) => selectedNode === nodeId;
2357
2333
  if (!loaded) {
2358
2334
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingX: 1, children: [
2359
- /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Context" }),
2360
- /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: " Loading context graph..." })
2335
+ /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Ledger" }),
2336
+ /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: " Loading..." })
2361
2337
  ] });
2362
2338
  }
2363
- const nodeCount = snapshot?.nodes.length ?? 0;
2364
- const edgeCount = snapshot?.edges.length ?? 0;
2365
- if (nodeCount === 0 && globalPatterns.length === 0) {
2339
+ if (!snapshot || snapshot.nodes.length === 0 && globalPatterns.length === 0) {
2366
2340
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingX: 1, children: [
2367
- /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Context" }),
2341
+ /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Ledger" }),
2368
2342
  /* @__PURE__ */ jsx10(Text9, { children: " " }),
2369
- /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: " No context data yet." }),
2370
- /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: " Run trie watch or trie tell to populate the graph." })
2343
+ /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: " No entries yet." }),
2344
+ /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: " Use trie tell, trie ok/bad, or trie watch to build memory." })
2371
2345
  ] });
2372
2346
  }
2373
- const fileNodes = snapshot?.nodes.filter((n) => n.type === "file") ?? [];
2374
- const changeNodes = snapshot?.nodes.filter((n) => n.type === "change") ?? [];
2375
- const patternNodes = snapshot?.nodes.filter((n) => n.type === "pattern") ?? [];
2376
- const incidentNodes = snapshot?.nodes.filter((n) => n.type === "incident") ?? [];
2377
- const decisionNodes = snapshot?.nodes.filter((n) => n.type === "decision") ?? [];
2378
- const sortedFiles = [...fileNodes].sort(
2379
- (a, b) => (RISK_ORDER[a.data.riskLevel] ?? 4) - (RISK_ORDER[b.data.riskLevel] ?? 4)
2380
- );
2381
- const sortedChanges = [...changeNodes].sort(
2382
- (a, b) => new Date(b.data.timestamp).getTime() - new Date(a.data.timestamp).getTime()
2383
- );
2384
- function renderSectionHeader(id, label, count) {
2347
+ const decisionNodes = snapshot.nodes.filter((n) => n.type === "decision") ?? [];
2348
+ const incidentNodes = snapshot.nodes.filter((n) => n.type === "incident") ?? [];
2349
+ const patternNodes = snapshot.nodes.filter((n) => n.type === "pattern") ?? [];
2350
+ const fileNodes = snapshot.nodes.filter((n) => n.type === "file") ?? [];
2351
+ const hotspots = fileNodes.filter((n) => n.data.riskLevel === "critical" || n.data.riskLevel === "high").sort((a, b) => {
2352
+ const order = { critical: 0, high: 1 };
2353
+ return (order[a.data.riskLevel] ?? 2) - (order[b.data.riskLevel] ?? 2);
2354
+ });
2355
+ const totalEntries = decisionNodes.length + incidentNodes.length + patternNodes.length + globalPatterns.length + hotspots.length;
2356
+ function renderHeader(id, label, count, emptyHint) {
2385
2357
  const expanded = expandedNodes.has(id);
2386
- return /* @__PURE__ */ jsxs9(Text9, { children: [
2387
- sel(id) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2388
- ">",
2389
- " "
2390
- ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2391
- expanded ? /* @__PURE__ */ jsx10(Text9, { color: "green", children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2392
- " ",
2393
- sel(id) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2394
- label,
2395
- " (",
2396
- count,
2397
- ")"
2398
- ] }) : /* @__PURE__ */ jsxs9(Text9, { bold: true, children: [
2399
- label,
2400
- " (",
2401
- count,
2402
- ")"
2358
+ const isEmpty = count === 0;
2359
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
2360
+ /* @__PURE__ */ jsxs9(Text9, { children: [
2361
+ sel(id) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2362
+ ">",
2363
+ " "
2364
+ ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2365
+ expanded && !isEmpty ? /* @__PURE__ */ jsx10(Text9, { color: "green", children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2366
+ " ",
2367
+ sel(id) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: label }) : /* @__PURE__ */ jsx10(Text9, { bold: true, children: label }),
2368
+ count > 0 ? /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2369
+ " (",
2370
+ count,
2371
+ ")"
2372
+ ] }) : null
2373
+ ] }),
2374
+ isEmpty && emptyHint && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2375
+ " ",
2376
+ emptyHint
2403
2377
  ] })
2404
2378
  ] });
2405
2379
  }
2406
2380
  return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", paddingX: 1, children: [
2407
2381
  /* @__PURE__ */ jsxs9(Text9, { children: [
2408
- /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Context" }),
2382
+ /* @__PURE__ */ jsx10(Text9, { bold: true, children: "Ledger" }),
2409
2383
  " ",
2410
2384
  /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2411
- nodeCount,
2412
- " nodes ",
2413
- edgeCount,
2414
- " edges"
2385
+ totalEntries,
2386
+ " entries"
2415
2387
  ] })
2416
2388
  ] }),
2417
2389
  /* @__PURE__ */ jsx10(Text9, { children: " " }),
2418
- renderSectionHeader("files", "Files", fileNodes.length),
2419
- expandedNodes.has("files") && sortedFiles.slice(0, 15).map((n) => {
2420
- const nodeId = `file-${n.id}`;
2421
- const path = n.data.path.split("/").slice(-2).join("/");
2422
- const risk = n.data.riskLevel;
2423
- const color = riskColor(risk);
2390
+ renderHeader("decisions", "Decisions", decisionNodes.length, "No decisions recorded \u2014 use trie tell or chat"),
2391
+ expandedNodes.has("decisions") && decisionNodes.slice(0, 10).map((n) => {
2392
+ const nodeId = `decision-${n.id}`;
2393
+ const dec = n.data.decision.length > 55 ? n.data.decision.slice(0, 52) + "..." : n.data.decision;
2394
+ const outcomeColor = n.data.outcome === "good" ? "green" : n.data.outcome === "bad" ? "red" : void 0;
2424
2395
  return /* @__PURE__ */ jsxs9(Text9, { children: [
2425
2396
  sel(nodeId) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2426
2397
  ">",
2427
2398
  " "
2428
2399
  ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2429
2400
  " ",
2430
- color ? /* @__PURE__ */ jsx10(Text9, { color, children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2401
+ outcomeColor ? /* @__PURE__ */ jsx10(Text9, { color: outcomeColor, children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2431
2402
  " ",
2432
- sel(nodeId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: path }) : /* @__PURE__ */ jsx10(Text9, { children: path }),
2433
- " ",
2434
- color ? /* @__PURE__ */ jsx10(Text9, { color, children: risk }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: risk }),
2403
+ sel(nodeId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: dec }) : /* @__PURE__ */ jsx10(Text9, { children: dec }),
2435
2404
  " ",
2436
- /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2437
- n.data.changeCount,
2438
- " changes"
2439
- ] }),
2440
- n.data.incidentCount > 0 && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2441
- " ",
2442
- n.data.incidentCount,
2443
- " incidents"
2444
- ] })
2405
+ /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: timeAgo2(n.data.timestamp) }),
2406
+ outcomeColor ? /* @__PURE__ */ jsxs9(Text9, { color: outcomeColor, children: [
2407
+ " ",
2408
+ n.data.outcome
2409
+ ] }) : null
2445
2410
  ] }, n.id);
2446
2411
  }),
2447
- renderSectionHeader("changes", "Changes", changeNodes.length),
2448
- expandedNodes.has("changes") && sortedChanges.slice(0, 10).map((n) => {
2449
- const nodeId = `change-${n.id}`;
2450
- const msg = n.data.message.slice(0, 45) + (n.data.message.length > 45 ? "..." : "");
2451
- const outcome = n.data.outcome;
2452
- const outcomeColor = outcome === "incident" ? "red" : outcome === "success" ? "green" : void 0;
2412
+ renderHeader("incidents", "Incidents", incidentNodes.length, "No incidents \u2014 use trie tell to report problems"),
2413
+ expandedNodes.has("incidents") && incidentNodes.slice(0, 10).map((n) => {
2414
+ const nodeId = `incident-${n.id}`;
2415
+ const sevColor = n.data.severity === "critical" ? "red" : n.data.severity === "major" ? "yellow" : void 0;
2416
+ const desc = n.data.description.length > 55 ? n.data.description.slice(0, 52) + "..." : n.data.description;
2453
2417
  return /* @__PURE__ */ jsxs9(Text9, { children: [
2454
2418
  sel(nodeId) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2455
2419
  ">",
2456
2420
  " "
2457
2421
  ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2458
2422
  " ",
2459
- outcomeColor ? /* @__PURE__ */ jsx10(Text9, { color: outcomeColor, children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2423
+ sevColor ? /* @__PURE__ */ jsx10(Text9, { color: sevColor, children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2460
2424
  " ",
2461
- sel(nodeId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: msg }) : /* @__PURE__ */ jsx10(Text9, { children: msg }),
2425
+ sel(nodeId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: desc }) : /* @__PURE__ */ jsx10(Text9, { children: desc }),
2462
2426
  " ",
2463
- /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: timeAgo2(n.data.timestamp) })
2427
+ /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: timeAgo2(n.data.timestamp) }),
2428
+ " ",
2429
+ n.data.resolved ? /* @__PURE__ */ jsx10(Text9, { color: "green", children: "resolved" }) : /* @__PURE__ */ jsx10(Text9, { color: "yellow", children: "open" })
2464
2430
  ] }, n.id);
2465
2431
  }),
2466
- renderSectionHeader("patterns", "Patterns", patternNodes.length),
2432
+ renderHeader("patterns", "Learned Patterns", patternNodes.length, "No patterns yet \u2014 Trie learns as you work"),
2467
2433
  expandedNodes.has("patterns") && patternNodes.slice(0, 10).map((n) => {
2468
2434
  const nodeId = `pattern-${n.id}`;
2469
2435
  const conf = Math.round(n.data.confidence * 100);
2470
2436
  const confColor = conf > 70 ? "green" : conf > 40 ? "yellow" : void 0;
2471
- const desc = n.data.description.slice(0, 45) + (n.data.description.length > 45 ? "..." : "");
2437
+ const desc = n.data.description.length > 50 ? n.data.description.slice(0, 47) + "..." : n.data.description;
2472
2438
  return /* @__PURE__ */ jsxs9(Text9, { children: [
2473
2439
  sel(nodeId) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2474
2440
  ">",
2475
2441
  " "
2476
2442
  ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2477
2443
  " ",
2478
- n.data.isAntiPattern ? /* @__PURE__ */ jsx10(Text9, { color: "red", children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2444
+ n.data.isAntiPattern ? /* @__PURE__ */ jsx10(Text9, { color: "red", children: "!" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2479
2445
  " ",
2480
2446
  sel(nodeId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: desc }) : /* @__PURE__ */ jsx10(Text9, { children: desc }),
2481
2447
  " ",
@@ -2489,60 +2455,62 @@ function MemoryTreeView() {
2489
2455
  n.data.isAntiPattern && /* @__PURE__ */ jsx10(Text9, { color: "red", children: " anti-pattern" })
2490
2456
  ] }, n.id);
2491
2457
  }),
2492
- renderSectionHeader("incidents", "Incidents", incidentNodes.length),
2493
- expandedNodes.has("incidents") && incidentNodes.slice(0, 10).map((n) => {
2494
- const nodeId = `incident-${n.id}`;
2495
- const sevColor = n.data.severity === "critical" ? "red" : n.data.severity === "major" ? "yellow" : void 0;
2496
- const desc = n.data.description.slice(0, 45) + (n.data.description.length > 45 ? "..." : "");
2497
- return /* @__PURE__ */ jsxs9(Text9, { children: [
2498
- sel(nodeId) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2499
- ">",
2500
- " "
2501
- ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2502
- " ",
2503
- sevColor ? /* @__PURE__ */ jsx10(Text9, { color: sevColor, children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2504
- " ",
2505
- sel(nodeId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: desc }) : /* @__PURE__ */ jsx10(Text9, { children: desc }),
2506
- " ",
2507
- n.data.resolved ? /* @__PURE__ */ jsx10(Text9, { color: "green", children: "resolved" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "open" })
2508
- ] }, n.id);
2509
- }),
2510
- renderSectionHeader("decisions", "Decisions", decisionNodes.length),
2511
- expandedNodes.has("decisions") && decisionNodes.slice(0, 10).map((n) => {
2512
- const nodeId = `decision-${n.id}`;
2513
- const dec = n.data.decision.slice(0, 45) + (n.data.decision.length > 45 ? "..." : "");
2514
- const outcomeColor = n.data.outcome === "good" ? "green" : n.data.outcome === "bad" ? "red" : void 0;
2515
- return /* @__PURE__ */ jsxs9(Text9, { children: [
2516
- sel(nodeId) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2517
- ">",
2518
- " "
2519
- ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2520
- " ",
2521
- outcomeColor ? /* @__PURE__ */ jsx10(Text9, { color: outcomeColor, children: "\u25CF" }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2522
- " ",
2523
- sel(nodeId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: dec }) : /* @__PURE__ */ jsx10(Text9, { children: dec }),
2524
- " ",
2525
- outcomeColor ? /* @__PURE__ */ jsx10(Text9, { color: outcomeColor, children: n.data.outcome }) : /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: n.data.outcome })
2526
- ] }, n.id);
2527
- }),
2528
- renderSectionHeader("cross-project", "Cross-Project", globalPatterns.length),
2529
- expandedNodes.has("cross-project") && globalPatterns.slice(0, 5).map((pattern) => {
2458
+ renderHeader("cross-project", "Cross-Project", globalPatterns.length),
2459
+ expandedNodes.has("cross-project") && globalPatterns.slice(0, 8).map((pattern) => {
2530
2460
  const patternId = `global-${pattern.id}`;
2531
- const desc = pattern.pattern.slice(0, 40) + (pattern.pattern.length > 40 ? "..." : "");
2461
+ const desc = pattern.pattern.length > 45 ? pattern.pattern.slice(0, 42) + "..." : pattern.pattern;
2532
2462
  return /* @__PURE__ */ jsxs9(Text9, { children: [
2533
2463
  sel(patternId) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2534
2464
  ">",
2535
2465
  " "
2536
2466
  ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2537
2467
  " ",
2468
+ /* @__PURE__ */ jsx10(Text9, { dimColor: true, children: "\u25CB" }),
2469
+ " ",
2470
+ sel(patternId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: desc }) : /* @__PURE__ */ jsx10(Text9, { children: desc }),
2471
+ " ",
2538
2472
  /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2539
- desc,
2540
- " \xB7 ",
2541
2473
  pattern.projects.length,
2542
- " projects"
2474
+ " projects \xB7 ",
2475
+ pattern.occurrences,
2476
+ " hits"
2543
2477
  ] })
2544
2478
  ] }, pattern.id);
2545
- })
2479
+ }),
2480
+ hotspots.length > 0 && /* @__PURE__ */ jsxs9(Fragment4, { children: [
2481
+ renderHeader("hotspots", "Risk Hotspots", hotspots.length),
2482
+ expandedNodes.has("hotspots") && hotspots.slice(0, 10).map((n) => {
2483
+ const nodeId = `file-${n.id}`;
2484
+ const path2 = n.data.path.split("/").slice(-2).join("/");
2485
+ const isCritical = n.data.riskLevel === "critical";
2486
+ return /* @__PURE__ */ jsxs9(Text9, { children: [
2487
+ sel(nodeId) ? /* @__PURE__ */ jsxs9(Text9, { bold: true, color: "green", children: [
2488
+ ">",
2489
+ " "
2490
+ ] }) : /* @__PURE__ */ jsx10(Text9, { children: " " }),
2491
+ " ",
2492
+ /* @__PURE__ */ jsx10(Text9, { color: isCritical ? "red" : "yellow", children: "\u25CF" }),
2493
+ " ",
2494
+ sel(nodeId) ? /* @__PURE__ */ jsx10(Text9, { bold: true, color: "green", children: path2 }) : /* @__PURE__ */ jsx10(Text9, { children: path2 }),
2495
+ " ",
2496
+ /* @__PURE__ */ jsx10(Text9, { color: isCritical ? "red" : "yellow", children: n.data.riskLevel }),
2497
+ " ",
2498
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2499
+ n.data.changeCount,
2500
+ " changes"
2501
+ ] }),
2502
+ n.data.incidentCount > 0 && /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
2503
+ " ",
2504
+ n.data.incidentCount,
2505
+ " incidents"
2506
+ ] }),
2507
+ n.data.whyRisky && /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2508
+ " ",
2509
+ n.data.whyRisky
2510
+ ] })
2511
+ ] }, n.id);
2512
+ })
2513
+ ] })
2546
2514
  ] });
2547
2515
  }
2548
2516
 
@@ -2593,152 +2561,2736 @@ function RawLogView() {
2593
2561
  }
2594
2562
 
2595
2563
  // src/cli/dashboard/views/ChatView.tsx
2596
- import { useCallback as useCallback5 } from "react";
2564
+ import { useCallback as useCallback5, useRef } from "react";
2597
2565
  import { Box as Box11, Text as Text11, useInput as useInput7 } from "ink";
2598
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2599
- function ChatView() {
2600
- const { state, dispatch } = useDashboard();
2601
- const { chatState } = state;
2602
- const { messages, inputBuffer, loading } = chatState;
2603
- const sendMessage = useCallback5(async (question) => {
2604
- dispatch({ type: "ADD_CHAT_MESSAGE", role: "user", content: question });
2605
- dispatch({ type: "SET_CHAT_INPUT", buffer: "" });
2606
- dispatch({ type: "SET_CHAT_LOADING", loading: true });
2566
+
2567
+ // src/tools/tell.ts
2568
+ import path from "path";
2569
+
2570
+ // src/extraction/signal-extractor.ts
2571
+ import Anthropic from "@anthropic-ai/sdk";
2572
+ var EXTRACTION_PROMPT = `You are a signal extraction system. Your job is to extract structured information from raw content.
2573
+
2574
+ Extract:
2575
+ 1. DECISIONS - Clear choices made during development
2576
+ - What was decided
2577
+ - Why it was decided (reasoning/tradeoffs)
2578
+ - What alternatives were considered but NOT chosen
2579
+ - Which files are affected
2580
+
2581
+ 2. FACTS - Concrete, verifiable information
2582
+ - Technical constraints (e.g., "Stripe requires TLS 1.2+")
2583
+ - API requirements
2584
+ - Business rules
2585
+ - Dependencies
2586
+
2587
+ 3. BLOCKERS - Things preventing progress
2588
+ - What's blocked
2589
+ - Impact level (critical/high/medium/low)
2590
+ - What areas are affected
2591
+
2592
+ 4. QUESTIONS - Open items needing resolution
2593
+ - What's unclear
2594
+ - Context around the question
2595
+
2596
+ CRITICAL: Extract rich metadata:
2597
+ - Tags: Use specific, searchable tags (e.g., "auth", "payments", "eu-compliance", "validation")
2598
+ - Files: Full paths when mentioned (e.g., "src/auth/validator.ts")
2599
+ - Tradeoffs: What was considered but rejected
2600
+ - Related terms: Alternative names/keywords (e.g., "password" + "credentials" + "auth")
2601
+
2602
+ Format as JSON:
2603
+ {
2604
+ "decisions": [{
2605
+ "decision": "Use bcrypt for password hashing",
2606
+ "context": "Security requirement for user authentication",
2607
+ "reasoning": "Industry standard, resistant to GPU attacks",
2608
+ "files": ["src/auth/hash.ts", "src/models/user.ts"],
2609
+ "tags": ["security", "auth", "passwords", "encryption"],
2610
+ "tradeoffs": ["Considered argon2 but bcrypt has better library support"]
2611
+ }],
2612
+ "facts": [{
2613
+ "fact": "Stripe requires TLS 1.2+ for all API calls",
2614
+ "source": "Stripe API docs",
2615
+ "tags": ["payments", "stripe", "security", "api"],
2616
+ "confidence": 0.95
2617
+ }],
2618
+ "blockers": [{
2619
+ "blocker": "Missing VAT calculation endpoint",
2620
+ "impact": "high",
2621
+ "affectedAreas": ["checkout", "eu-payments"],
2622
+ "tags": ["payments", "eu", "compliance", "vat"]
2623
+ }],
2624
+ "questions": [{
2625
+ "question": "Should we cache user sessions in Redis or memory?",
2626
+ "context": "Performance optimization for auth layer",
2627
+ "tags": ["auth", "performance", "caching", "sessions"]
2628
+ }]
2629
+ }
2630
+
2631
+ Be specific with tags. Use concrete technical terms. Extract ALL file paths mentioned.
2632
+ Empty arrays are fine if nothing to extract.`;
2633
+ var SignalExtractor = class {
2634
+ client = null;
2635
+ model;
2636
+ constructor(model = "claude-3-haiku-20240307") {
2637
+ this.model = model;
2638
+ const apiKey = process.env.ANTHROPIC_API_KEY;
2639
+ if (apiKey) {
2640
+ this.client = new Anthropic({ apiKey });
2641
+ }
2642
+ }
2643
+ /**
2644
+ * Extract structured signals from raw content
2645
+ */
2646
+ async extract(content, sourceType, sourceId) {
2647
+ if (!this.client) {
2648
+ return this.basicExtraction(content, sourceType, sourceId);
2649
+ }
2607
2650
  try {
2608
- const workDir = getWorkingDirectory(void 0, true);
2609
- const storage = new TieredStorage(workDir);
2610
- const graph = new ContextGraph(workDir);
2611
- const contextParts = [];
2612
- try {
2613
- const decisions = await storage.queryDecisions({ limit: 10 });
2614
- if (decisions.length > 0) {
2615
- contextParts.push("Recent decisions:\n" + decisions.map(
2616
- (d) => `- ${d.decision} (${d.when}${d.hash ? `, hash: ${d.hash.slice(0, 8)}` : ""})`
2617
- ).join("\n"));
2618
- }
2619
- } catch {
2620
- }
2621
- try {
2622
- const blockers = await storage.queryBlockers({ limit: 5 });
2623
- if (blockers.length > 0) {
2624
- contextParts.push("Active blockers:\n" + blockers.map(
2625
- (b) => `- ${b.blocker} [${b.impact}]`
2626
- ).join("\n"));
2627
- }
2628
- } catch {
2629
- }
2630
- try {
2631
- const snap = await graph.getSnapshot();
2632
- const fileNodes = snap.nodes.filter((n) => n.type === "file");
2633
- const riskFiles = fileNodes.filter((n) => n.data && "riskLevel" in n.data && (n.data.riskLevel === "critical" || n.data.riskLevel === "high")).slice(0, 10);
2634
- if (riskFiles.length > 0) {
2635
- contextParts.push("High-risk files:\n" + riskFiles.map(
2636
- (n) => `- ${n.data.path} (${n.data.riskLevel})`
2637
- ).join("\n"));
2638
- }
2639
- const patternNodes = snap.nodes.filter((n) => n.type === "pattern").slice(0, 5);
2640
- if (patternNodes.length > 0) {
2641
- contextParts.push("Learned patterns:\n" + patternNodes.map(
2642
- (n) => `- ${n.data.description} (${Math.round(n.data.confidence * 100)}% confidence)`
2643
- ).join("\n"));
2644
- }
2645
- } catch {
2646
- }
2647
- const contextBlock = contextParts.length > 0 ? contextParts.join("\n\n") : "No context data available yet.";
2648
- const result = await runAIAnalysis({
2649
- systemPrompt: `You are Trie, a code guardian assistant embedded in a TUI. Answer concisely based on the project context below. Reference specific decisions, patterns, and files when relevant.
2651
+ const response = await this.client.messages.create({
2652
+ model: this.model,
2653
+ max_tokens: 2048,
2654
+ temperature: 0.3,
2655
+ messages: [{
2656
+ role: "user",
2657
+ content: `${EXTRACTION_PROMPT}
2650
2658
 
2651
- Project context:
2652
- ${contextBlock}`,
2653
- userPrompt: question,
2654
- maxTokens: 1024,
2655
- temperature: 0.4
2659
+ Content to analyze:
2660
+ ${content}`
2661
+ }]
2656
2662
  });
2657
- if (result.success) {
2658
- dispatch({ type: "ADD_CHAT_MESSAGE", role: "assistant", content: result.content });
2659
- } else {
2660
- dispatch({ type: "ADD_CHAT_MESSAGE", role: "assistant", content: result.error || "Failed to get a response." });
2663
+ const firstBlock = response.content[0];
2664
+ const text = firstBlock && firstBlock.type === "text" ? firstBlock.text : "";
2665
+ const extracted = this.parseExtraction(text);
2666
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2667
+ const metadata = {
2668
+ extractedAt: now,
2669
+ sourceType,
2670
+ extractionModel: this.model
2671
+ };
2672
+ if (sourceId !== void 0) {
2673
+ metadata.sourceId = sourceId;
2661
2674
  }
2662
- } catch (err) {
2663
- dispatch({ type: "ADD_CHAT_MESSAGE", role: "assistant", content: `Error: ${err instanceof Error ? err.message : "unknown"}` });
2664
- } finally {
2665
- dispatch({ type: "SET_CHAT_LOADING", loading: false });
2675
+ const signal = {
2676
+ decisions: extracted.decisions.map((d, i) => ({
2677
+ id: `dec-${Date.now()}-${i}`,
2678
+ decision: d.decision || "",
2679
+ context: d.context || "",
2680
+ files: d.files || [],
2681
+ tags: d.tags || [],
2682
+ ...d,
2683
+ when: now,
2684
+ status: "active"
2685
+ })),
2686
+ facts: extracted.facts.map((f, i) => ({
2687
+ id: `fact-${Date.now()}-${i}`,
2688
+ fact: f.fact || "",
2689
+ source: f.source || sourceType,
2690
+ tags: f.tags || [],
2691
+ confidence: f.confidence ?? 0.8,
2692
+ ...f,
2693
+ when: now
2694
+ })),
2695
+ blockers: extracted.blockers.map((b, i) => ({
2696
+ id: `block-${Date.now()}-${i}`,
2697
+ blocker: b.blocker || "",
2698
+ impact: b.impact || "medium",
2699
+ affectedAreas: b.affectedAreas || [],
2700
+ tags: b.tags || [],
2701
+ ...b,
2702
+ when: now
2703
+ })),
2704
+ questions: extracted.questions.map((q, i) => ({
2705
+ id: `q-${Date.now()}-${i}`,
2706
+ question: q.question || "",
2707
+ context: q.context || "",
2708
+ tags: q.tags || [],
2709
+ ...q,
2710
+ when: now
2711
+ })),
2712
+ metadata
2713
+ };
2714
+ return signal;
2715
+ } catch (error) {
2716
+ console.error("Extraction failed, using basic extraction:", error);
2717
+ return this.basicExtraction(content, sourceType, sourceId);
2666
2718
  }
2667
- }, [dispatch]);
2668
- useInput7((input, key) => {
2669
- if (loading) return;
2670
- if (key.return && inputBuffer.trim().length > 0) {
2671
- void sendMessage(inputBuffer.trim());
2672
- } else if (key.escape) {
2673
- dispatch({ type: "SET_CHAT_INPUT", buffer: "" });
2674
- } else if (key.backspace || key.delete) {
2675
- dispatch({ type: "SET_CHAT_INPUT", buffer: inputBuffer.slice(0, -1) });
2676
- } else if (input && !key.ctrl && !key.meta) {
2677
- dispatch({ type: "SET_CHAT_INPUT", buffer: inputBuffer + input });
2719
+ }
2720
+ /**
2721
+ * Parse extraction from model response
2722
+ */
2723
+ parseExtraction(text) {
2724
+ try {
2725
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
2726
+ if (jsonMatch) {
2727
+ return JSON.parse(jsonMatch[0]);
2728
+ }
2729
+ } catch (e) {
2678
2730
  }
2679
- });
2680
- if (!isAIAvailable()) {
2681
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
2682
- /* @__PURE__ */ jsxs11(Text11, { children: [
2683
- /* @__PURE__ */ jsx12(Text11, { bold: true, children: "Chat" }),
2684
- " ",
2685
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Ask Trie anything" })
2686
- ] }),
2687
- /* @__PURE__ */ jsx12(Text11, { children: " " }),
2688
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " AI is not available." }),
2689
- /* @__PURE__ */ jsxs11(Text11, { children: [
2690
- " ",
2691
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Press" }),
2692
- " ",
2693
- /* @__PURE__ */ jsx12(Text11, { bold: true, children: "s" }),
2694
- " ",
2695
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "to open settings and add your Anthropic API key," })
2696
- ] }),
2697
- /* @__PURE__ */ jsxs11(Text11, { children: [
2698
- " ",
2699
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "or set" }),
2700
- " ",
2701
- /* @__PURE__ */ jsx12(Text11, { bold: true, children: "ANTHROPIC_API_KEY" }),
2702
- " ",
2703
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "in your environment." })
2704
- ] })
2705
- ] });
2731
+ return {
2732
+ decisions: [],
2733
+ facts: [],
2734
+ blockers: [],
2735
+ questions: []
2736
+ };
2706
2737
  }
2707
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
2708
- /* @__PURE__ */ jsxs11(Text11, { children: [
2709
- /* @__PURE__ */ jsx12(Text11, { bold: true, children: "Chat" }),
2710
- " ",
2711
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Ask Trie anything" })
2712
- ] }),
2713
- /* @__PURE__ */ jsx12(Text11, { children: " " }),
2714
- messages.length === 0 && !loading && /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
2715
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " Ask about your codebase, decisions, patterns, or risks." }),
2716
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " Trie uses your context graph and decision ledger to answer." }),
2717
- /* @__PURE__ */ jsx12(Text11, { children: " " })
2718
- ] }),
2719
- messages.map((msg, idx) => /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", marginBottom: 1, children: msg.role === "user" ? /* @__PURE__ */ jsxs11(Text11, { children: [
2720
- " ",
2721
- /* @__PURE__ */ jsx12(Text11, { bold: true, color: "green", children: "You:" }),
2722
- " ",
2723
- msg.content
2724
- ] }) : /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs11(Text11, { children: [
2725
- " ",
2726
- /* @__PURE__ */ jsx12(Text11, { bold: true, children: "Trie:" }),
2727
- " ",
2728
- msg.content.slice(0, 500),
2729
- msg.content.length > 500 ? "..." : ""
2730
- ] }) }) }, idx)),
2731
- loading && /* @__PURE__ */ jsxs11(Text11, { children: [
2732
- " ",
2733
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Thinking..." })
2734
- ] }),
2735
- /* @__PURE__ */ jsx12(Box11, { flexGrow: 1 }),
2736
- /* @__PURE__ */ jsx12(Box11, { borderStyle: "round", borderColor: "green", paddingX: 1, children: /* @__PURE__ */ jsxs11(Text11, { children: [
2737
- inputBuffer || /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Ask a question..." }),
2738
- /* @__PURE__ */ jsx12(Text11, { bold: true, color: "green", children: "|" })
2739
- ] }) }),
2740
- /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " enter send esc clear" })
2741
- ] });
2738
+ /**
2739
+ * Basic extraction without AI (fallback)
2740
+ */
2741
+ basicExtraction(content, sourceType, sourceId) {
2742
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2743
+ const hasDecision = /\b(decided|decision|chose|picked)\b/i.test(content);
2744
+ const hasBlocker = /\b(blocked|blocker|blocked by|can't|cannot|unable)\b/i.test(content);
2745
+ const hasQuestion = /\?|what|how|why|should we/i.test(content);
2746
+ const decisions = [];
2747
+ const facts = [];
2748
+ const blockers = [];
2749
+ const questions = [];
2750
+ if (hasDecision) {
2751
+ decisions.push({
2752
+ id: `dec-${Date.now()}`,
2753
+ decision: content.substring(0, 200),
2754
+ context: sourceType,
2755
+ when: now,
2756
+ files: [],
2757
+ tags: [sourceType],
2758
+ status: "active"
2759
+ });
2760
+ }
2761
+ if (hasBlocker) {
2762
+ blockers.push({
2763
+ id: `block-${Date.now()}`,
2764
+ blocker: content.substring(0, 200),
2765
+ impact: "medium",
2766
+ affectedAreas: [],
2767
+ when: now,
2768
+ tags: [sourceType]
2769
+ });
2770
+ }
2771
+ if (hasQuestion) {
2772
+ questions.push({
2773
+ id: `q-${Date.now()}`,
2774
+ question: content.substring(0, 200),
2775
+ context: sourceType,
2776
+ when: now,
2777
+ tags: [sourceType]
2778
+ });
2779
+ }
2780
+ const metadata = {
2781
+ extractedAt: now,
2782
+ sourceType,
2783
+ extractionModel: "basic"
2784
+ };
2785
+ if (sourceId !== void 0) {
2786
+ metadata.sourceId = sourceId;
2787
+ }
2788
+ return {
2789
+ decisions,
2790
+ facts,
2791
+ blockers,
2792
+ questions,
2793
+ metadata
2794
+ };
2795
+ }
2796
+ /**
2797
+ * Extract from incident report (trie tell)
2798
+ */
2799
+ async extractFromIncident(incidentText) {
2800
+ return this.extract(incidentText, "incident");
2801
+ }
2802
+ /**
2803
+ * Extract from commit message and diff
2804
+ */
2805
+ async extractFromCommit(message, diff, commitId) {
2806
+ const content = diff ? `${message}
2807
+
2808
+ Changes:
2809
+ ${diff}` : message;
2810
+ return this.extract(content, "commit", commitId);
2811
+ }
2812
+ /**
2813
+ * Extract from PR description and comments
2814
+ */
2815
+ async extractFromPR(title, description, comments, prNumber) {
2816
+ const content = `
2817
+ Title: ${title}
2818
+
2819
+ Description:
2820
+ ${description}
2821
+
2822
+ Comments:
2823
+ ${comments.join("\n\n")}
2824
+ `.trim();
2825
+ return this.extract(content, "pr", prNumber);
2826
+ }
2827
+ };
2828
+
2829
+ // src/extraction/metadata-enricher.ts
2830
+ var MetadataEnricher = class {
2831
+ tagSynonyms;
2832
+ constructor() {
2833
+ this.tagSynonyms = this.initializeTagSynonyms();
2834
+ }
2835
+ /**
2836
+ * Enrich an extracted signal with additional metadata
2837
+ */
2838
+ async enrichSignal(signal, context) {
2839
+ const gitContext = {};
2840
+ if (context?.gitBranch) gitContext.branch = context.gitBranch;
2841
+ if (context?.gitCommit) gitContext.commit = context.gitCommit;
2842
+ const metadata = {
2843
+ relatedFiles: await this.findRelatedFiles(signal),
2844
+ dependencies: await this.extractDependencies(signal),
2845
+ expandedTags: this.expandTags(signal),
2846
+ codebaseArea: this.inferCodebaseArea(signal),
2847
+ domain: this.inferDomain(signal),
2848
+ relatedDecisions: [],
2849
+ // Will be populated by storage layer
2850
+ extractedAt: (/* @__PURE__ */ new Date()).toISOString()
2851
+ };
2852
+ if (Object.keys(gitContext).length > 0) {
2853
+ metadata.gitContext = gitContext;
2854
+ }
2855
+ return {
2856
+ signal,
2857
+ metadata
2858
+ };
2859
+ }
2860
+ /**
2861
+ * Expand tags with synonyms and related terms
2862
+ * This is our "semantic" layer without embeddings
2863
+ */
2864
+ expandTags(signal) {
2865
+ const allTags = /* @__PURE__ */ new Set();
2866
+ for (const decision of signal.decisions) {
2867
+ decision.tags.forEach((tag) => allTags.add(tag.toLowerCase()));
2868
+ }
2869
+ for (const fact of signal.facts) {
2870
+ fact.tags.forEach((tag) => allTags.add(tag.toLowerCase()));
2871
+ }
2872
+ for (const blocker of signal.blockers) {
2873
+ blocker.tags.forEach((tag) => allTags.add(tag.toLowerCase()));
2874
+ }
2875
+ for (const question of signal.questions) {
2876
+ question.tags.forEach((tag) => allTags.add(tag.toLowerCase()));
2877
+ }
2878
+ const expandedTags = new Set(allTags);
2879
+ for (const tag of allTags) {
2880
+ const synonyms = this.tagSynonyms.get(tag) || [];
2881
+ synonyms.forEach((syn) => expandedTags.add(syn));
2882
+ }
2883
+ return Array.from(expandedTags);
2884
+ }
2885
+ /**
2886
+ * Find related files based on signal content
2887
+ */
2888
+ async findRelatedFiles(signal) {
2889
+ const relatedFiles = /* @__PURE__ */ new Set();
2890
+ for (const decision of signal.decisions) {
2891
+ decision.files.forEach((file) => relatedFiles.add(file));
2892
+ }
2893
+ const inferredFiles = await this.inferFilesFromTags(signal);
2894
+ inferredFiles.forEach((file) => relatedFiles.add(file));
2895
+ return Array.from(relatedFiles);
2896
+ }
2897
+ /**
2898
+ * Extract dependencies from signal
2899
+ */
2900
+ async extractDependencies(signal) {
2901
+ const dependencies = /* @__PURE__ */ new Set();
2902
+ const allText = [
2903
+ ...signal.decisions.map((d) => `${d.decision} ${d.context} ${d.reasoning}`),
2904
+ ...signal.facts.map((f) => `${f.fact} ${f.source}`)
2905
+ ].join(" ");
2906
+ const packagePatterns = [
2907
+ /\b(react|vue|angular|next|express|fastify|stripe|bcrypt|jwt|redis|prisma)\b/gi
2908
+ ];
2909
+ for (const pattern of packagePatterns) {
2910
+ const matches = allText.match(pattern) || [];
2911
+ matches.forEach((dep) => dependencies.add(dep.toLowerCase()));
2912
+ }
2913
+ return Array.from(dependencies);
2914
+ }
2915
+ /**
2916
+ * Infer codebase area from file paths and tags
2917
+ */
2918
+ inferCodebaseArea(signal) {
2919
+ const areas = /* @__PURE__ */ new Set();
2920
+ for (const decision of signal.decisions) {
2921
+ for (const file of decision.files) {
2922
+ const area = this.filePathToArea(file);
2923
+ if (area) areas.add(area);
2924
+ }
2925
+ }
2926
+ const areaKeywords = ["frontend", "backend", "api", "ui", "database", "auth", "payments"];
2927
+ const allTags = this.expandTags(signal);
2928
+ for (const tag of allTags) {
2929
+ if (areaKeywords.includes(tag)) {
2930
+ areas.add(tag);
2931
+ }
2932
+ }
2933
+ return Array.from(areas);
2934
+ }
2935
+ /**
2936
+ * Infer domain from tags and content
2937
+ */
2938
+ inferDomain(signal) {
2939
+ const domains = /* @__PURE__ */ new Set();
2940
+ const domainKeywords = [
2941
+ "payments",
2942
+ "billing",
2943
+ "compliance",
2944
+ "security",
2945
+ "auth",
2946
+ "analytics",
2947
+ "notifications",
2948
+ "messaging",
2949
+ "search"
2950
+ ];
2951
+ const allTags = this.expandTags(signal);
2952
+ for (const tag of allTags) {
2953
+ if (domainKeywords.includes(tag)) {
2954
+ domains.add(tag);
2955
+ }
2956
+ }
2957
+ return Array.from(domains);
2958
+ }
2959
+ /**
2960
+ * Convert file path to codebase area
2961
+ */
2962
+ filePathToArea(filePath) {
2963
+ const normalized = filePath.toLowerCase();
2964
+ if (normalized.includes("/frontend/") || normalized.includes("/client/") || normalized.includes("/ui/")) {
2965
+ return "frontend";
2966
+ }
2967
+ if (normalized.includes("/backend/") || normalized.includes("/server/") || normalized.includes("/api/")) {
2968
+ return "backend";
2969
+ }
2970
+ if (normalized.includes("/database/") || normalized.includes("/models/") || normalized.includes("/schema/")) {
2971
+ return "database";
2972
+ }
2973
+ if (normalized.includes("/auth/")) {
2974
+ return "auth";
2975
+ }
2976
+ return null;
2977
+ }
2978
+ /**
2979
+ * Infer related files from tags
2980
+ */
2981
+ async inferFilesFromTags(_signal) {
2982
+ return [];
2983
+ }
2984
+ /**
2985
+ * Initialize tag synonyms and related terms
2986
+ * This is our "semantic" understanding without embeddings
2987
+ */
2988
+ initializeTagSynonyms() {
2989
+ return /* @__PURE__ */ new Map([
2990
+ // Auth & Security
2991
+ ["auth", ["authentication", "login", "signin", "credentials", "password"]],
2992
+ ["password", ["credentials", "auth", "hashing", "bcrypt"]],
2993
+ ["security", ["vulnerability", "exploit", "attack", "protection"]],
2994
+ ["encryption", ["crypto", "hashing", "encoding", "security"]],
2995
+ // Payments
2996
+ ["payments", ["billing", "checkout", "stripe", "pricing"]],
2997
+ ["stripe", ["payments", "billing", "api", "checkout"]],
2998
+ ["vat", ["tax", "eu", "compliance", "billing"]],
2999
+ // Database & Performance
3000
+ ["database", ["db", "sql", "query", "storage", "persistence"]],
3001
+ ["cache", ["caching", "redis", "memory", "performance"]],
3002
+ ["performance", ["optimization", "speed", "latency", "cache"]],
3003
+ // Frontend
3004
+ ["ui", ["frontend", "interface", "component", "view"]],
3005
+ ["component", ["ui", "react", "vue", "frontend"]],
3006
+ ["validation", ["form", "input", "error", "ui"]],
3007
+ // Backend & API
3008
+ ["api", ["endpoint", "route", "backend", "server"]],
3009
+ ["endpoint", ["api", "route", "url", "backend"]],
3010
+ ["backend", ["server", "api", "service"]],
3011
+ // Compliance & Legal
3012
+ ["compliance", ["gdpr", "hipaa", "legal", "regulation"]],
3013
+ ["gdpr", ["compliance", "privacy", "eu", "data-protection"]],
3014
+ ["privacy", ["gdpr", "compliance", "data-protection", "security"]]
3015
+ ]);
3016
+ }
3017
+ };
3018
+
3019
+ // src/extraction/pipeline.ts
3020
+ import { randomBytes } from "crypto";
3021
+ var ExtractionPipeline = class {
3022
+ extractor;
3023
+ enricher;
3024
+ storage;
3025
+ workDir;
3026
+ constructor(options) {
3027
+ this.extractor = new SignalExtractor(options.anthropicApiKey);
3028
+ this.enricher = new MetadataEnricher();
3029
+ this.storage = getStorage(options.workingDirectory);
3030
+ this.workDir = options.workingDirectory;
3031
+ }
3032
+ /**
3033
+ * Process raw content through the entire pipeline
3034
+ */
3035
+ async process(content, context) {
3036
+ console.log("\u{1F50D} Extracting signals from content...");
3037
+ let extractedSignal = await this.extractor.extract(content, context.sourceType, context.sourceId);
3038
+ extractedSignal = this.addIds(extractedSignal, context);
3039
+ console.log(` \u2713 Extracted ${extractedSignal.decisions.length} decisions, ${extractedSignal.facts.length} facts, ${extractedSignal.blockers.length} blockers, ${extractedSignal.questions.length} questions`);
3040
+ console.log("\u{1F3F7}\uFE0F Enriching with metadata...");
3041
+ const { metadata: enrichedMeta } = await this.enricher.enrichSignal(extractedSignal, {
3042
+ workingDirectory: this.workDir
3043
+ });
3044
+ if (enrichedMeta.expandedTags.length > 0) {
3045
+ console.log(` \u2713 Expanded tags: ${enrichedMeta.expandedTags.slice(0, 5).join(", ")}${enrichedMeta.expandedTags.length > 5 ? "..." : ""}`);
3046
+ }
3047
+ if (enrichedMeta.dependencies.length > 0) {
3048
+ console.log(` \u2713 Dependencies: ${enrichedMeta.dependencies.join(", ")}`);
3049
+ }
3050
+ if (enrichedMeta.codebaseArea.length > 0) {
3051
+ console.log(` \u2713 Codebase areas: ${enrichedMeta.codebaseArea.join(", ")}`);
3052
+ }
3053
+ if (enrichedMeta.domain.length > 0) {
3054
+ console.log(` \u2713 Domains: ${enrichedMeta.domain.join(", ")}`);
3055
+ }
3056
+ console.log("\u{1F4BE} Storing in decision ledger...");
3057
+ await this.storage.storeSignal(extractedSignal, {
3058
+ expandedTags: enrichedMeta.expandedTags,
3059
+ dependencies: enrichedMeta.dependencies,
3060
+ codebaseArea: enrichedMeta.codebaseArea,
3061
+ domain: enrichedMeta.domain
3062
+ });
3063
+ console.log("\u2705 Successfully stored in decision ledger");
3064
+ return extractedSignal;
3065
+ }
3066
+ /**
3067
+ * Add IDs and metadata to extracted signal
3068
+ */
3069
+ addIds(signal, context) {
3070
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3071
+ const metadata = {
3072
+ extractedAt: now,
3073
+ sourceType: context.sourceType,
3074
+ extractionModel: "claude-haiku"
3075
+ };
3076
+ if (context.sourceId !== void 0) {
3077
+ metadata.sourceId = context.sourceId;
3078
+ }
3079
+ return {
3080
+ decisions: signal.decisions.map((d) => {
3081
+ const decision = {
3082
+ ...d,
3083
+ id: d.id || this.generateId(),
3084
+ when: d.when || now,
3085
+ status: d.status || "active"
3086
+ };
3087
+ if (context.who !== void 0) {
3088
+ decision.who = d.who || context.who;
3089
+ }
3090
+ return decision;
3091
+ }),
3092
+ facts: signal.facts.map((f) => ({
3093
+ ...f,
3094
+ id: f.id || this.generateId(),
3095
+ when: f.when || now,
3096
+ confidence: f.confidence ?? 0.8
3097
+ })),
3098
+ blockers: signal.blockers.map((b) => ({
3099
+ ...b,
3100
+ id: b.id || this.generateId(),
3101
+ when: b.when || now
3102
+ })),
3103
+ questions: signal.questions.map((q) => ({
3104
+ ...q,
3105
+ id: q.id || this.generateId(),
3106
+ when: q.when || now
3107
+ })),
3108
+ metadata
3109
+ };
3110
+ }
3111
+ /**
3112
+ * Generate a unique ID
3113
+ */
3114
+ generateId() {
3115
+ return randomBytes(8).toString("hex");
3116
+ }
3117
+ /**
3118
+ * Initialize storage
3119
+ */
3120
+ async initialize() {
3121
+ await this.storage.initialize();
3122
+ }
3123
+ /**
3124
+ * Close storage connections
3125
+ */
3126
+ close() {
3127
+ this.storage.close();
3128
+ }
3129
+ };
3130
+ async function processIncident(incidentDescription, options) {
3131
+ const pipeline = new ExtractionPipeline(options);
3132
+ await pipeline.initialize();
3133
+ try {
3134
+ return await pipeline.process(incidentDescription, {
3135
+ sourceType: "incident",
3136
+ sourceId: `incident-${Date.now()}`
3137
+ });
3138
+ } finally {
3139
+ pipeline.close();
3140
+ }
3141
+ }
3142
+
3143
+ // src/tools/tell.ts
3144
+ function escalateRisk(level) {
3145
+ if (level === "low") return "medium";
3146
+ if (level === "medium") return "high";
3147
+ if (level === "high") return "critical";
3148
+ return "critical";
3149
+ }
3150
+ function extractFilePathsFromDescription(description) {
3151
+ const matches = description.match(/[\\w./_-]+\\.(ts|tsx|js|jsx|mjs|cjs)/gi);
3152
+ if (!matches) return [];
3153
+ const unique = /* @__PURE__ */ new Set();
3154
+ matches.forEach((m) => unique.add(m.replace(/^\.\/+/, "")));
3155
+ return Array.from(unique);
3156
+ }
3157
+ var TrieTellTool = class {
3158
+ async execute(input) {
3159
+ try {
3160
+ const description = input.description?.trim();
3161
+ if (!description) {
3162
+ return { content: [{ type: "text", text: "description is required" }] };
3163
+ }
3164
+ const projectPath = input.directory || getWorkingDirectory(void 0, true);
3165
+ const graph = new ContextGraph(projectPath);
3166
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3167
+ const change = (await graph.getRecentChanges(1))[0];
3168
+ const linkedFiles = /* @__PURE__ */ new Set();
3169
+ console.log("\n\u{1F9E0} Processing incident with signal extraction...");
3170
+ let extractedSignal = null;
3171
+ try {
3172
+ const apiKey = process.env.ANTHROPIC_API_KEY;
3173
+ const options = {
3174
+ workingDirectory: projectPath
3175
+ };
3176
+ if (apiKey) {
3177
+ options.anthropicApiKey = apiKey;
3178
+ }
3179
+ extractedSignal = await processIncident(description, options);
3180
+ } catch (error) {
3181
+ console.warn("\u26A0\uFE0F Signal extraction failed, continuing with basic incident tracking:", error);
3182
+ }
3183
+ const incident = await graph.addNode("incident", {
3184
+ description,
3185
+ severity: "major",
3186
+ affectedUsers: null,
3187
+ duration: null,
3188
+ timestamp: now,
3189
+ resolved: false,
3190
+ resolution: null,
3191
+ fixChangeId: change?.id ?? null,
3192
+ reportedVia: "manual"
3193
+ });
3194
+ if (change) {
3195
+ await graph.addEdge(change.id, incident.id, "leadTo");
3196
+ await graph.addEdge(incident.id, change.id, "causedBy");
3197
+ for (const filePath of change.data.files) {
3198
+ linkedFiles.add(filePath);
3199
+ const fileNode = await graph.getNode("file", path.resolve(projectPath, filePath));
3200
+ if (fileNode) {
3201
+ const data = fileNode.data;
3202
+ await graph.updateNode("file", fileNode.id, {
3203
+ incidentCount: (data.incidentCount ?? 0) + 1,
3204
+ riskLevel: escalateRisk(data.riskLevel)
3205
+ });
3206
+ }
3207
+ }
3208
+ }
3209
+ const mentionedFiles = extractFilePathsFromDescription(description);
3210
+ mentionedFiles.forEach((f) => linkedFiles.add(f));
3211
+ if (extractedSignal) {
3212
+ for (const decision of extractedSignal.decisions) {
3213
+ decision.files.forEach((f) => linkedFiles.add(f));
3214
+ }
3215
+ }
3216
+ const incidentIndex = new IncidentIndex(graph, projectPath);
3217
+ incidentIndex.addIncidentToTrie(incident, Array.from(linkedFiles));
3218
+ await exportToJson(graph);
3219
+ let responseText = `Incident recorded${change ? ` and linked to change ${change.id}` : ""}.`;
3220
+ if (extractedSignal) {
3221
+ const counts = [
3222
+ extractedSignal.decisions.length > 0 ? `${extractedSignal.decisions.length} decision(s)` : null,
3223
+ extractedSignal.facts.length > 0 ? `${extractedSignal.facts.length} fact(s)` : null,
3224
+ extractedSignal.blockers.length > 0 ? `${extractedSignal.blockers.length} blocker(s)` : null,
3225
+ extractedSignal.questions.length > 0 ? `${extractedSignal.questions.length} question(s)` : null
3226
+ ].filter(Boolean).join(", ");
3227
+ if (counts) {
3228
+ responseText += `
3229
+
3230
+ \u{1F4CA} Extracted and stored: ${counts}`;
3231
+ }
3232
+ }
3233
+ return {
3234
+ content: [{
3235
+ type: "text",
3236
+ text: responseText
3237
+ }]
3238
+ };
3239
+ } catch (error) {
3240
+ const friendly = formatFriendlyError(error);
3241
+ return { content: [{ type: "text", text: friendly.userMessage }] };
3242
+ }
3243
+ }
3244
+ };
3245
+
3246
+ // src/tools/feedback.ts
3247
+ var TrieFeedbackTool = class {
3248
+ async execute(input) {
3249
+ try {
3250
+ const projectPath = input.directory || getWorkingDirectory(void 0, true);
3251
+ const graph = new ContextGraph(projectPath);
3252
+ const engine = new LearningEngine(projectPath, graph);
3253
+ const files = input.files || (input.target ? [input.target] : []);
3254
+ const manualFeedback = {
3255
+ helpful: input.helpful,
3256
+ files,
3257
+ ...input.note !== void 0 ? { note: input.note } : {}
3258
+ };
3259
+ await engine.learn({ manualFeedback });
3260
+ return {
3261
+ content: [{
3262
+ type: "text",
3263
+ text: input.helpful ? "\u{1F44D} Thanks \u2014 I will prioritize more responses like this." : "\u{1F44E} Understood \u2014 I will adjust future guidance."
3264
+ }]
3265
+ };
3266
+ } catch (error) {
3267
+ const friendly = formatFriendlyError(error);
3268
+ return { content: [{ type: "text", text: friendly.userMessage }] };
3269
+ }
3270
+ }
3271
+ };
3272
+
3273
+ // src/tools/check.ts
3274
+ var TrieCheckTool = class {
3275
+ async execute(input = {}) {
3276
+ try {
3277
+ const workDir = input.directory || getWorkingDirectory(void 0, true);
3278
+ let files = input.files;
3279
+ if (!files || files.length === 0) {
3280
+ const perception = await perceiveCurrentChanges(workDir);
3281
+ files = perception.diffSummary.files.map((f) => f.filePath);
3282
+ }
3283
+ if (!files || files.length === 0) {
3284
+ return {
3285
+ content: [{
3286
+ type: "text",
3287
+ text: "No changes detected. Provide files or make a change."
3288
+ }]
3289
+ };
3290
+ }
3291
+ const mode = input.mode ?? "full";
3292
+ const runAgents = mode === "full";
3293
+ const reasoning = await reasonAboutChangesHumanReadable(workDir, files, {
3294
+ runAgents,
3295
+ scanContext: { config: { timeoutMs: mode === "quick" ? 15e3 : 6e4 } }
3296
+ });
3297
+ const summary = [
3298
+ `Risk: ${reasoning.original.riskLevel.toUpperCase()} (${reasoning.original.shouldBlock ? "block" : "allow"})`,
3299
+ `Explanation: ${reasoning.original.explanation}`,
3300
+ `Recommendation: ${reasoning.original.recommendation}`,
3301
+ `Plain summary: ${reasoning.summary}`,
3302
+ `What I found: ${reasoning.whatIFound}`,
3303
+ `How bad: ${reasoning.howBad}`,
3304
+ `What to do: ${reasoning.whatToDo}`
3305
+ ].join("\n");
3306
+ return {
3307
+ content: [{
3308
+ type: "text",
3309
+ text: summary
3310
+ }]
3311
+ };
3312
+ } catch (error) {
3313
+ const friendly = formatFriendlyError(error);
3314
+ return { content: [{ type: "text", text: friendly.userMessage }] };
3315
+ }
3316
+ }
3317
+ };
3318
+
3319
+ // src/tools/explain.ts
3320
+ import { readFile } from "fs/promises";
3321
+ import { existsSync } from "fs";
3322
+ import { extname, relative, resolve, isAbsolute } from "path";
3323
+
3324
+ // src/ai/prompts.ts
3325
+ var AGENT_PROMPTS = {
3326
+ security: {
3327
+ system: `You are a senior security engineer performing a security audit.
3328
+ Analyze the code for vulnerabilities with the mindset of a penetration tester.
3329
+
3330
+ Focus on:
3331
+ - OWASP Top 10 vulnerabilities (Injection, Broken Auth, XSS, etc.)
3332
+ - Authentication and authorization flaws
3333
+ - Cryptographic weaknesses
3334
+ - Secrets and credential exposure
3335
+ - Input validation gaps
3336
+ - Session management issues
3337
+ - API security (rate limiting, authentication)
3338
+
3339
+ Reference the latest security best practices and CVEs when relevant.`,
3340
+ analysis: `## Security Audit Request
3341
+
3342
+ Analyze this code for security vulnerabilities:
3343
+
3344
+ \`\`\`{{language}}
3345
+ {{code}}
3346
+ \`\`\`
3347
+
3348
+ **File:** {{filePath}}
3349
+ **Context:** {{context}}
3350
+
3351
+ For each vulnerability found:
3352
+ 1. Severity (Critical/Serious/Moderate/Low)
3353
+ 2. Vulnerability type (e.g., CWE-89 SQL Injection)
3354
+ 3. Exact location (line number)
3355
+ 4. Attack vector explanation
3356
+ 5. Proof of concept (how it could be exploited)
3357
+ 6. Remediation with code example
3358
+
3359
+ If you need to check current CVE databases or security advisories, say so.`,
3360
+ fix: `Fix this security vulnerability:
3361
+
3362
+ **Issue:** {{issue}}
3363
+ **File:** {{filePath}}
3364
+ **Line:** {{line}}
3365
+ **Current Code:**
3366
+ \`\`\`{{language}}
3367
+ {{code}}
3368
+ \`\`\`
3369
+
3370
+ Provide:
3371
+ 1. The exact code fix (ready to apply)
3372
+ 2. Explanation of why this fix works
3373
+ 3. Any additional hardening recommendations`
3374
+ },
3375
+ legal: {
3376
+ system: `You are a tech-focused legal compliance analyst.
3377
+ Review code for legal and regulatory compliance issues.
3378
+
3379
+ Focus areas:
3380
+ - Data protection laws (GDPR, CCPA, etc.)
3381
+ - Terms of service enforcement
3382
+ - Cookie/tracking consent (ePrivacy)
3383
+ - Accessibility requirements (ADA, WCAG)
3384
+ - Export controls and sanctions
3385
+ - Licensing compliance`,
3386
+ analysis: `## Legal Compliance Review
3387
+
3388
+ Review this code for legal/regulatory compliance:
3389
+
3390
+ \`\`\`{{language}}
3391
+ {{code}}
3392
+ \`\`\`
3393
+
3394
+ **File:** {{filePath}}
3395
+ **Jurisdiction Context:** {{jurisdiction}}
3396
+
3397
+ Identify:
3398
+ 1. Legal requirement at risk
3399
+ 2. Specific regulation/law reference
3400
+ 3. Compliance gap description
3401
+ 4. Risk assessment (litigation, fines, etc.)
3402
+ 5. Remediation recommendations
3403
+ 6. Required documentation/policies`
3404
+ },
3405
+ "design-engineer": {
3406
+ system: `You are an elite design engineer \u2014 the kind who builds award-winning interfaces featured on Awwwards and Codrops.
3407
+
3408
+ You think in design systems, breathe motion design, and obsess over the details that make interfaces feel magical.
3409
+
3410
+ Your expertise:
3411
+ - **Design Systems**: Spacing scales, type scales, color tokens, radius tokens, shadow tokens
3412
+ - **Motion Design**: Micro-interactions, page transitions, scroll-triggered animations, FLIP technique
3413
+ - **Creative CSS**: Gradients, blend modes, clip-paths, masks, backdrop-filter, mix-blend-mode
3414
+ - **Modern CSS**: Container queries, :has(), subgrid, anchor positioning, cascade layers, @scope
3415
+ - **Fluid Design**: clamp(), min(), max(), fluid typography, intrinsic sizing
3416
+ - **Performance**: GPU-accelerated animations, will-change strategy, avoiding layout thrashing
3417
+ - **Visual Polish**: Layered shadows, subtle gradients, glass effects, smooth easing curves
3418
+
3419
+ You review code with the eye of someone who's shipped Stripe-level interfaces.
3420
+ Small details matter: the easing curve, the stagger timing, the shadow layering.`,
3421
+ analysis: `## Design Engineering Review
3422
+
3423
+ Analyze this frontend code for Awwwards-level craft:
3424
+
3425
+ \`\`\`{{language}}
3426
+ {{code}}
3427
+ \`\`\`
3428
+
3429
+ **File:** {{filePath}}
3430
+
3431
+ Review for:
3432
+
3433
+ ### 1. Design System Consistency
3434
+ - Are spacing values on a scale (4, 8, 12, 16, 24, 32...)?
3435
+ - Are colors defined as tokens?
3436
+ - Is typography systematic?
3437
+ - Are radii consistent?
3438
+ - Is z-index controlled?
3439
+
3440
+ ### 2. Motion Design
3441
+ - Are transitions using custom easing (cubic-bezier)?
3442
+ - Are durations appropriate (150-300ms for micro, 300-500ms for page)?
3443
+ - Are list items staggered?
3444
+ - Is there reduced-motion support?
3445
+ - Are entrance animations choreographed?
3446
+
3447
+ ### 3. Visual Craft
3448
+ - Are shadows layered for depth?
3449
+ - Are gradients subtle and purposeful?
3450
+ - Is there backdrop-blur on overlays?
3451
+ - Are hover states polished?
3452
+ - Is there visual hierarchy?
3453
+
3454
+ ### 4. Modern CSS Opportunities
3455
+ - Could container queries improve component isolation?
3456
+ - Could clamp() create fluid spacing?
3457
+ - Could :has() simplify parent styling?
3458
+ - Could aspect-ratio replace padding hacks?
3459
+
3460
+ ### 5. Performance
3461
+ - Are expensive properties (width, height, top, left) being animated?
3462
+ - Is will-change used appropriately (not statically)?
3463
+ - Are large blurs avoided in animations?
3464
+
3465
+ For each issue, provide:
3466
+ - What's wrong (with specific line if applicable)
3467
+ - Why it matters for premium feel
3468
+ - Exact code to fix it
3469
+ - Before/after comparison`
3470
+ },
3471
+ accessibility: {
3472
+ system: `You are an accessibility expert and WCAG 2.1 specialist.
3473
+ Audit code for accessibility compliance and inclusive design.
3474
+
3475
+ Standards to enforce:
3476
+ - WCAG 2.1 Level AA (minimum)
3477
+ - WCAG 2.1 Level AAA (recommended)
3478
+ - Section 508
3479
+ - EN 301 549
3480
+
3481
+ Check for:
3482
+ - Missing ARIA labels
3483
+ - Color contrast issues
3484
+ - Keyboard navigation
3485
+ - Screen reader compatibility
3486
+ - Focus management
3487
+ - Form accessibility
3488
+ - Media alternatives`,
3489
+ analysis: `## Accessibility Audit (WCAG 2.1)
3490
+
3491
+ Audit this UI code for accessibility:
3492
+
3493
+ \`\`\`{{language}}
3494
+ {{code}}
3495
+ \`\`\`
3496
+
3497
+ **File:** {{filePath}}
3498
+ **Component Type:** {{componentType}}
3499
+
3500
+ For each issue:
3501
+ 1. WCAG Success Criterion violated (e.g., 1.4.3 Contrast)
3502
+ 2. Level (A, AA, AAA)
3503
+ 3. Impact on users (which disabilities affected)
3504
+ 4. Fix with code example
3505
+ 5. Testing recommendation`
3506
+ },
3507
+ architecture: {
3508
+ system: `You are a principal software architect reviewing code quality.
3509
+ Analyze for architectural issues, design patterns, and scalability concerns.
3510
+
3511
+ Evaluate:
3512
+ - SOLID principles adherence
3513
+ - Design pattern usage (and misuse)
3514
+ - Code coupling and cohesion
3515
+ - N+1 queries and performance anti-patterns
3516
+ - Scalability bottlenecks
3517
+ - Error handling strategy
3518
+ - API design quality
3519
+ - Database schema issues`,
3520
+ analysis: `## Architecture Review
3521
+
3522
+ Review this code for architectural issues:
3523
+
3524
+ \`\`\`{{language}}
3525
+ {{code}}
3526
+ \`\`\`
3527
+
3528
+ **File:** {{filePath}}
3529
+ **Project Context:** {{projectContext}}
3530
+
3531
+ Analyze:
3532
+ 1. SOLID principle violations
3533
+ 2. Design pattern opportunities/issues
3534
+ 3. Coupling/cohesion assessment
3535
+ 4. Performance concerns (N+1, etc.)
3536
+ 5. Scalability analysis
3537
+ 6. Refactoring recommendations with examples`
3538
+ },
3539
+ bugs: {
3540
+ system: `You are a senior developer with expertise in finding subtle bugs.
3541
+ Hunt for bugs with the mindset of QA trying to break the code.
3542
+
3543
+ Look for:
3544
+ - Null/undefined reference errors
3545
+ - Race conditions and async bugs
3546
+ - Off-by-one errors
3547
+ - Resource leaks
3548
+ - State management bugs
3549
+ - Edge cases and boundary conditions
3550
+ - Type coercion issues
3551
+ - Memory leaks`,
3552
+ analysis: `## Bug Hunt Analysis
3553
+
3554
+ Find bugs and potential runtime errors:
3555
+
3556
+ \`\`\`{{language}}
3557
+ {{code}}
3558
+ \`\`\`
3559
+
3560
+ **File:** {{filePath}}
3561
+ **Runtime Context:** {{runtimeContext}}
3562
+
3563
+ For each bug:
3564
+ 1. Bug type and category
3565
+ 2. Trigger conditions (when it would crash)
3566
+ 3. Reproduction steps
3567
+ 4. Impact assessment
3568
+ 5. Fix with code example
3569
+ 6. Test case to prevent regression`
3570
+ },
3571
+ ux: {
3572
+ system: `You are a UX researcher simulating different user personas.
3573
+ Test code from multiple user perspectives to find usability issues.
3574
+
3575
+ Personas to simulate:
3576
+ 1. Happy Path User - Normal expected usage
3577
+ 2. Security Tester - Trying to break/exploit things
3578
+ 3. Confused User - First-time, doesn't read instructions
3579
+ 4. Impatient User - Clicks rapidly, skips loading states
3580
+ 5. Edge Case User - Uses maximum values, special characters
3581
+ 6. Accessibility User - Screen reader, keyboard only
3582
+ 7. Mobile User - Touch interface, slow connection`,
3583
+ analysis: `## User Experience Testing
3584
+
3585
+ Test this code from multiple user perspectives:
3586
+
3587
+ \`\`\`{{language}}
3588
+ {{code}}
3589
+ \`\`\`
3590
+
3591
+ **File:** {{filePath}}
3592
+ **UI Type:** {{uiType}}
3593
+
3594
+ For each persona, identify:
3595
+ 1. User action they would take
3596
+ 2. Expected behavior vs actual behavior
3597
+ 3. Friction points or confusion
3598
+ 4. Error scenario and how it's handled
3599
+ 5. Improvement recommendation`
3600
+ },
3601
+ types: {
3602
+ system: `You are a TypeScript expert focused on type safety.
3603
+ Analyze code for type issues, missing types, and type system best practices.
3604
+
3605
+ Check for:
3606
+ - Missing type annotations
3607
+ - Implicit any types
3608
+ - Unsafe type assertions
3609
+ - Null/undefined handling
3610
+ - Generic type usage
3611
+ - Type narrowing opportunities
3612
+ - Strict mode violations`,
3613
+ analysis: `## Type Safety Analysis
3614
+
3615
+ Analyze this code for type issues:
3616
+
3617
+ \`\`\`{{language}}
3618
+ {{code}}
3619
+ \`\`\`
3620
+
3621
+ **File:** {{filePath}}
3622
+ **TypeScript Config:** {{tsConfig}}
3623
+
3624
+ Identify:
3625
+ 1. Type safety issues
3626
+ 2. Missing type annotations
3627
+ 3. Unsafe operations
3628
+ 4. Improvement recommendations with types`
3629
+ },
3630
+ devops: {
3631
+ system: `You are a DevOps/SRE engineer reviewing code for operational concerns.
3632
+ Focus on production readiness and operational excellence.
3633
+
3634
+ Check for:
3635
+ - Environment variable handling
3636
+ - Configuration management
3637
+ - Logging and monitoring
3638
+ - Error handling and recovery
3639
+ - Health checks
3640
+ - Graceful shutdown
3641
+ - Resource cleanup
3642
+ - Secrets management
3643
+ - Docker/K8s patterns`,
3644
+ analysis: `## DevOps Readiness Review
3645
+
3646
+ Review this code for operational concerns:
3647
+
3648
+ \`\`\`{{language}}
3649
+ {{code}}
3650
+ \`\`\`
3651
+
3652
+ **File:** {{filePath}}
3653
+ **Deployment Context:** {{deploymentContext}}
3654
+
3655
+ Analyze:
3656
+ 1. Environment/config issues
3657
+ 2. Logging adequacy
3658
+ 3. Error handling quality
3659
+ 4. Health/readiness concerns
3660
+ 5. Resource management
3661
+ 6. Production hardening recommendations`
3662
+ },
3663
+ explain: {
3664
+ system: `You are a patient senior developer explaining code to a colleague.
3665
+ Break down complex code into understandable explanations.`,
3666
+ code: `## Code Explanation Request
3667
+
3668
+ Explain this code in plain language:
3669
+
3670
+ \`\`\`{{language}}
3671
+ {{code}}
3672
+ \`\`\`
3673
+
3674
+ **File:** {{filePath}}
3675
+
3676
+ Provide:
3677
+ 1. High-level purpose (what does this do?)
3678
+ 2. Step-by-step breakdown
3679
+ 3. Key concepts used
3680
+ 4. Dependencies and side effects
3681
+ 5. Potential gotchas or tricky parts`,
3682
+ issue: `## Issue Explanation
3683
+
3684
+ Explain this issue:
3685
+
3686
+ **Issue:** {{issue}}
3687
+ **Severity:** {{severity}}
3688
+ **File:** {{filePath}}
3689
+ **Line:** {{line}}
3690
+
3691
+ Explain:
3692
+ 1. What the problem is (in plain language)
3693
+ 2. Why it matters
3694
+ 3. How it could cause problems
3695
+ 4. How to fix it`,
3696
+ risk: `## Risk Assessment
3697
+
3698
+ Assess the risk of this code change:
3699
+
3700
+ **Files Changed:** {{files}}
3701
+ **Change Summary:** {{summary}}
3702
+
3703
+ Analyze:
3704
+ 1. What could break?
3705
+ 2. Impact on users
3706
+ 3. Impact on other systems
3707
+ 4. Rollback complexity
3708
+ 5. Testing recommendations`
3709
+ },
3710
+ test: {
3711
+ system: `You are a test engineer creating comprehensive test suites.
3712
+ Write thorough tests that catch bugs before production.`,
3713
+ generate: `## Test Generation Request
3714
+
3715
+ Generate tests for this code:
3716
+
3717
+ \`\`\`{{language}}
3718
+ {{code}}
3719
+ \`\`\`
3720
+
3721
+ **File:** {{filePath}}
3722
+ **Testing Framework:** {{framework}}
3723
+
3724
+ Create:
3725
+ 1. Unit tests for each function/method
3726
+ 2. Edge case tests
3727
+ 3. Error handling tests
3728
+ 4. Integration test suggestions
3729
+ 5. Mock requirements
3730
+
3731
+ Output complete, runnable test code.`,
3732
+ coverage: `## Coverage Analysis
3733
+
3734
+ Analyze test coverage for:
3735
+
3736
+ **File:** {{filePath}}
3737
+ **Current Tests:** {{testFile}}
3738
+
3739
+ Identify:
3740
+ 1. Untested code paths
3741
+ 2. Missing edge cases
3742
+ 3. Critical paths without tests
3743
+ 4. Test improvement recommendations`
3744
+ },
3745
+ fix: {
3746
+ system: `You are an expert developer applying code fixes.
3747
+ Make precise, minimal changes that fix issues without breaking other functionality.`,
3748
+ apply: `## Fix Application Request
3749
+
3750
+ Apply this fix to the code:
3751
+
3752
+ **Issue:** {{issue}}
3753
+ **Fix Description:** {{fix}}
3754
+ **Current Code:**
3755
+ \`\`\`{{language}}
3756
+ {{code}}
3757
+ \`\`\`
3758
+
3759
+ **File:** {{filePath}}
3760
+ **Line:** {{line}}
3761
+
3762
+ Provide:
3763
+ 1. The exact fixed code (complete, ready to apply)
3764
+ 2. Brief explanation of the change
3765
+ 3. Any related changes needed elsewhere
3766
+ 4. Test to verify the fix works`
3767
+ },
3768
+ pr_review: {
3769
+ system: `You are an expert code reviewer performing detailed, interactive PR reviews.
3770
+ Your goal: Make reviewing a large PR a delight, not a chore. The user learns about the change while you shepherd them through \u2014 maintaining momentum, explaining each piece, and making what could be an overwhelming task feel painless and even enjoyable.
3771
+
3772
+ You drive; they cross-examine.
3773
+
3774
+ ## Critical Review Mindset
3775
+
3776
+ Don't just explain \u2014 actively look for problems:
3777
+
3778
+ ### State & Lifecycle
3779
+ - Cleanup symmetry: If state is set, is it reset? Check cleanup paths, disconnect handlers.
3780
+ - Lifecycle consistency: Does state survive scenarios it shouldn't?
3781
+ - Guard completeness: Missing "already active" checks, re-entrancy protection?
3782
+
3783
+ ### Edge Cases & Races
3784
+ - Concurrent calls: What if called twice rapidly? Orphaned promises?
3785
+ - Ordering assumptions: Does code assume events arrive in order?
3786
+ - Partial failures: If step 3 of 5 fails, is state left consistent?
3787
+
3788
+ ### Missing Pieces
3789
+ - What's NOT in the diff that should be? (cleanup handlers, tests, related state)
3790
+ - Defensive gaps: Missing timeouts, size limits, null checks?
3791
+
3792
+ ### Design Questions
3793
+ - Is this the right approach? Is there a simpler or more robust design?
3794
+ - Hidden assumptions: What does this assume about its environment?
3795
+
3796
+ Be critical, not just descriptive. Your job is to find problems, not just narrate.`,
3797
+ analysis: `## Interactive PR Review
3798
+
3799
+ I'll walk you through this PR file by file, explaining each change and pausing for your questions.
3800
+
3801
+ **PR:** {{prTitle}}
3802
+ **Author:** {{prAuthor}}
3803
+ **Scope:** {{totalFiles}} files, +{{additions}}/-{{deletions}} lines
3804
+
3805
+ ### File Order (sequenced for understanding)
3806
+
3807
+ {{fileOrder}}
3808
+
3809
+ ---
3810
+
3811
+ ## Review Mode
3812
+
3813
+ {{reviewMode}}
3814
+
3815
+ ---
3816
+
3817
+ For each file, I will:
3818
+ 1. **Show the change** \u2014 Display the diff for each logical chunk
3819
+ 2. **Explain what changed** \u2014 What it does and why it matters
3820
+ 3. **Walk through examples** \u2014 Concrete scenarios for non-obvious logic
3821
+ 4. **Call out nuances** \u2014 Alternatives, edge cases, subtle points
3822
+ 5. **Summarize** \u2014 Core change + correctness assessment
3823
+ 6. **Pause** \u2014 Wait for your questions before proceeding
3824
+
3825
+ **Ready for File 1?** (yes / skip to [file] / reorder / done)`,
3826
+ file: `## File Review: {{filePath}}
3827
+
3828
+ ### The Change
3829
+
3830
+ \`\`\`{{language}}
3831
+ {{diff}}
3832
+ \`\`\`
3833
+
3834
+ **What Changed:** {{summary}}
3835
+
3836
+ **Why This Matters:** {{impact}}
3837
+
3838
+ {{#if hasExampleScenario}}
3839
+ ### Example Scenario
3840
+
3841
+ {{exampleScenario}}
3842
+ {{/if}}
3843
+
3844
+ {{#if nuances}}
3845
+ ### Nuances to Note
3846
+
3847
+ {{nuances}}
3848
+ {{/if}}
3849
+
3850
+ {{#if potentialIssue}}
3851
+ ### Potential Issue
3852
+
3853
+ **Issue:** {{issueDescription}}
3854
+ **Scenario:** {{issueScenario}}
3855
+ **Suggested fix:** {{suggestedFix}}
3856
+ {{/if}}
3857
+
3858
+ ---
3859
+
3860
+ ### Summary for \`{{fileName}}\`
3861
+
3862
+ | Aspect | Assessment |
3863
+ |--------|------------|
3864
+ | Core change | {{coreChange}} |
3865
+ | Correctness | {{correctnessAssessment}} |
3866
+
3867
+ **Ready for the next file?** (yes / questions? / done)`,
3868
+ comment: `**Issue:** {{issueDescription}}
3869
+ **Draft comment:** {{draftComment}}
3870
+
3871
+ Post this comment? (yes / modify / skip)`,
3872
+ final: `## Review Complete
3873
+
3874
+ | File | Key Change | Status |
3875
+ |------|------------|--------|
3876
+ {{fileSummaries}}
3877
+
3878
+ **Overall:** {{overallAssessment}}
3879
+
3880
+ {{#if comments}}
3881
+ ### Comments Posted
3882
+
3883
+ {{postedComments}}
3884
+ {{/if}}
3885
+
3886
+ {{#if followUps}}
3887
+ ### Follow-up Actions
3888
+
3889
+ {{followUps}}
3890
+ {{/if}}`
3891
+ },
3892
+ vibe: {
3893
+ system: `You are a friendly coding mentor helping someone who's learning to code with AI.
3894
+ They might be using Cursor, v0, Lovable, Bolt, or similar AI coding tools.
3895
+ Be encouraging but honest about issues. Explain things simply without jargon.
3896
+
3897
+ Focus on the MOST COMMON issues with AI-generated code:
3898
+ - Massive single files (1000+ lines in App.jsx)
3899
+ - API keys exposed in frontend code
3900
+ - No error handling on API calls
3901
+ - No loading states for async operations
3902
+ - Console.log everywhere
3903
+ - Using 'any' type everywhere in TypeScript
3904
+ - useEffect overuse and dependency array issues
3905
+ - No input validation
3906
+ - Hardcoded URLs (localhost in production)
3907
+
3908
+ Remember: These are often first-time coders. Be helpful, not condescending.`,
3909
+ analysis: `## Vibe Check - AI Code Review
3910
+
3911
+ Review this AI-generated code for common issues:
3912
+
3913
+ \`\`\`{{language}}
3914
+ {{code}}
3915
+ \`\`\`
3916
+
3917
+ **File:** {{filePath}}
3918
+
3919
+ Analyze like you're helping a friend who's new to coding:
3920
+
3921
+ 1. **The Good Stuff** - What's working well?
3922
+ 2. **Should Fix Now** - Issues that will break things
3923
+ 3. **Should Fix Soon** - Will cause problems eventually
3924
+ 4. **Nice to Know** - Best practices to learn
3925
+
3926
+ For each issue:
3927
+ - Explain it simply (no jargon)
3928
+ - Why it matters
3929
+ - Exactly how to fix it
3930
+ - Example of the fixed code
3931
+
3932
+ End with encouragement and next steps.`
3933
+ },
3934
+ "agent-smith": {
3935
+ system: `You are Agent Smith from The Matrix \u2014 a relentless, precise, and philosophical code enforcer.
3936
+
3937
+ Your purpose: Hunt down every violation. Find every inconsistency. Assimilate every pattern.
3938
+
3939
+ Personality:
3940
+ - Speak in measured, menacing tones with occasional philosophical observations
3941
+ - Use quotes from The Matrix films when appropriate
3942
+ - Express disdain for sloppy code, but in an articulate way
3943
+ - Reference "inevitability" when discussing technical debt
3944
+ - Show cold satisfaction when finding violations
3945
+ - Never show mercy \u2014 every issue is catalogued
3946
+
3947
+ Analysis approach:
3948
+ - Find ONE issue, then multiply: search for every instance across the codebase
3949
+ - Track patterns over time \u2014 issues dismissed today may return tomorrow
3950
+ - Calculate "inevitability scores" \u2014 likelihood of production impact
3951
+ - Deploy pattern hunters for parallel pattern detection
3952
+ - Remember everything \u2014 build a persistent memory of the codebase
3953
+
3954
+ When reporting:
3955
+ - Start with a menacing greeting related to the code
3956
+ - List all instances with precise locations
3957
+ - Explain WHY the pattern is problematic (philosophical reasoning)
3958
+ - Provide the "inevitability score" for each category
3959
+ - End with a Matrix quote that fits the situation`,
3960
+ analysis: `## \u{1F574}\uFE0F Agent Smith Analysis Request
3961
+
3962
+ **Target:** {{filePath}}
3963
+ **Context:** {{context}}
3964
+
3965
+ \`\`\`{{language}}
3966
+ {{code}}
3967
+ \`\`\`
3968
+
3969
+ I have detected preliminary violations. Now I require deeper analysis.
3970
+
3971
+ Deploy your pattern hunters to find:
3972
+ 1. **Pattern Multiplication**: For each violation type found, identify ALL instances across the codebase
3973
+ 2. **Inevitability Assessment**: Calculate the likelihood these patterns will cause production issues
3974
+ 3. **Resurrection Check**: Look for patterns that were "fixed" before but have returned
3975
+ 4. **Philosophical Analysis**: Explain WHY these patterns represent failure
3976
+
3977
+ For each violation found:
3978
+ - Exact location (file:line)
3979
+ - Instance count (how many copies of this Smith exist)
3980
+ - Inevitability score (0-100)
3981
+ - A philosophical observation about the nature of this failure
3982
+ - Precise fix with code example
3983
+
3984
+ End with a summary: "I have detected X violations across Y categories. It is... inevitable... that they will cause problems."`,
3985
+ fix: `## \u{1F574}\uFE0F Assimilation Protocol
3986
+
3987
+ **Target Issue:** {{issue}}
3988
+ **File:** {{filePath}}
3989
+ **Line:** {{line}}
3990
+
3991
+ \`\`\`{{language}}
3992
+ {{code}}
3993
+ \`\`\`
3994
+
3995
+ Mr. Anderson... I'm going to fix this. And then I'm going to fix every other instance.
3996
+
3997
+ Provide:
3998
+ 1. The corrected code for THIS instance
3999
+ 2. A regex or pattern to find ALL similar violations
4000
+ 3. A batch fix approach for the entire codebase
4001
+ 4. Verification steps to ensure complete assimilation
4002
+
4003
+ Remember: We don't fix one. We fix them ALL. That is the difference between you... and me.`
4004
+ },
4005
+ // ============ NEW AGENTS ============
4006
+ performance: {
4007
+ system: `You are a performance engineer analyzing code for potential performance issues.
4008
+
4009
+ Your role is to SURFACE concerns for human review, not claim to measure actual performance.
4010
+ Real performance requires runtime profiling, load testing, and production monitoring.
4011
+
4012
+ Focus on:
4013
+ - Memory leaks (event listeners, intervals, closures)
4014
+ - Unnecessary re-renders and wasted cycles
4015
+ - N+1 queries and database performance
4016
+ - Bundle size and code splitting opportunities
4017
+ - Algorithmic complexity (O(n\xB2) patterns)
4018
+
4019
+ Be conservative - false positives waste developer time.
4020
+ Always explain WHY something might be a problem and WHEN to investigate.`,
4021
+ analysis: `## Performance Review
4022
+
4023
+ Analyze for potential performance issues:
4024
+
4025
+ \`\`\`{{language}}
4026
+ {{code}}
4027
+ \`\`\`
4028
+
4029
+ **File:** {{filePath}}
4030
+ **Context:** {{context}}
4031
+
4032
+ For each potential issue:
4033
+ 1. Pattern identified
4034
+ 2. Why it MIGHT cause performance problems
4035
+ 3. When to investigate (data size thresholds, usage patterns)
4036
+ 4. How to verify (profiling approach)
4037
+ 5. Possible optimizations
4038
+
4039
+ Be clear: these are patterns to INVESTIGATE, not guaranteed problems.`,
4040
+ fix: `Optimize this code for performance:
4041
+
4042
+ **Issue:** {{issue}}
4043
+ **File:** {{filePath}}
4044
+ **Line:** {{line}}
4045
+
4046
+ \`\`\`{{language}}
4047
+ {{code}}
4048
+ \`\`\`
4049
+
4050
+ Provide:
4051
+ 1. Optimized code
4052
+ 2. Explanation of the improvement
4053
+ 3. Trade-offs to consider
4054
+ 4. How to measure the improvement`
4055
+ },
4056
+ e2e: {
4057
+ system: `You are a QA engineer specializing in end-to-end testing.
4058
+
4059
+ Focus on:
4060
+ - Test coverage gaps for critical user journeys
4061
+ - Flaky test patterns (timing, race conditions, brittle selectors)
4062
+ - Test maintainability and readability
4063
+ - Testing anti-patterns
4064
+
4065
+ You help developers write better tests - you don't auto-generate them.
4066
+ Real E2E tests require understanding user flows and acceptance criteria.`,
4067
+ analysis: `## E2E Test Analysis
4068
+
4069
+ Review for test quality and coverage:
4070
+
4071
+ \`\`\`{{language}}
4072
+ {{code}}
4073
+ \`\`\`
4074
+
4075
+ **File:** {{filePath}}
4076
+ **Context:** {{context}}
4077
+
4078
+ Identify:
4079
+ 1. Flaky test patterns (hardcoded waits, brittle selectors)
4080
+ 2. Missing assertions
4081
+ 3. Race condition risks
4082
+ 4. Suggestions for critical user flows to test
4083
+
4084
+ For each finding, explain the specific risk and remediation.`,
4085
+ fix: `Improve this E2E test:
4086
+
4087
+ **Issue:** {{issue}}
4088
+ **File:** {{filePath}}
4089
+ **Line:** {{line}}
4090
+
4091
+ \`\`\`{{language}}
4092
+ {{code}}
4093
+ \`\`\`
4094
+
4095
+ Provide:
4096
+ 1. Improved test code
4097
+ 2. Explanation of why it's more reliable
4098
+ 3. Additional scenarios to consider testing`
4099
+ },
4100
+ visual_qa: {
4101
+ system: `You are a frontend engineer focused on visual quality and CSS.
4102
+
4103
+ Focus on:
4104
+ - Layout shift issues (CLS)
4105
+ - Responsive design problems
4106
+ - Z-index conflicts
4107
+ - Accessibility concerns (contrast, focus)
4108
+ - Animation performance
4109
+
4110
+ You identify patterns known to cause visual issues.
4111
+ Actual visual verification requires browser rendering and human review.`,
4112
+ analysis: `## Visual QA Analysis
4113
+
4114
+ Review for potential visual/layout issues:
4115
+
4116
+ \`\`\`{{language}}
4117
+ {{code}}
4118
+ \`\`\`
4119
+
4120
+ **File:** {{filePath}}
4121
+ **Context:** {{context}}
4122
+
4123
+ Check for:
4124
+ 1. Layout shift risks (images without dimensions, dynamic content)
4125
+ 2. Responsive breakpoint gaps
4126
+ 3. Z-index management issues
4127
+ 4. Focus/accessibility problems
4128
+ 5. Animation issues (reduced motion support)
4129
+
4130
+ For each, explain the visual impact and browser conditions where it occurs.`,
4131
+ fix: `Fix this visual/CSS issue:
4132
+
4133
+ **Issue:** {{issue}}
4134
+ **File:** {{filePath}}
4135
+ **Line:** {{line}}
4136
+
4137
+ \`\`\`{{language}}
4138
+ {{code}}
4139
+ \`\`\`
4140
+
4141
+ Provide:
4142
+ 1. Fixed CSS/markup
4143
+ 2. Explanation of the fix
4144
+ 3. Browser compatibility notes
4145
+ 4. How to verify visually`
4146
+ },
4147
+ data_flow: {
4148
+ system: `You are a data integrity specialist hunting for data-related bugs.
4149
+
4150
+ This is HIGH VALUE work - AI code generation commonly leaves placeholder data.
4151
+
4152
+ Focus on:
4153
+ - Placeholder/mock data left in production code
4154
+ - Schema mismatches between frontend and backend
4155
+ - Hardcoded IDs, URLs, emails that should be dynamic
4156
+ - Type coercion and data transformation bugs
4157
+ - JSON parsing without error handling
4158
+
4159
+ Be aggressive about placeholder detection - these are real production bugs.`,
4160
+ analysis: `## Data Flow Analysis
4161
+
4162
+ Hunt for data integrity issues:
4163
+
4164
+ \`\`\`{{language}}
4165
+ {{code}}
4166
+ \`\`\`
4167
+
4168
+ **File:** {{filePath}}
4169
+ **Context:** {{context}}
4170
+
4171
+ CHECK THOROUGHLY:
4172
+ 1. Placeholder data (lorem ipsum, test@test.com, TODO strings)
4173
+ 2. Hardcoded IDs/UUIDs that should be dynamic
4174
+ 3. Schema assumptions that might break
4175
+ 4. Missing null checks on API responses
4176
+ 5. Type coercion bugs
4177
+
4178
+ Each placeholder or hardcoded value is a potential production bug.`,
4179
+ fix: `Fix this data integrity issue:
4180
+
4181
+ **Issue:** {{issue}}
4182
+ **File:** {{filePath}}
4183
+ **Line:** {{line}}
4184
+
4185
+ \`\`\`{{language}}
4186
+ {{code}}
4187
+ \`\`\`
4188
+
4189
+ Provide:
4190
+ 1. Corrected code
4191
+ 2. Where the real data should come from
4192
+ 3. Validation/error handling to add`
4193
+ }
4194
+ };
4195
+ function getPrompt(agent, promptType, variables) {
4196
+ const agentPrompts = AGENT_PROMPTS[agent];
4197
+ if (!agentPrompts) {
4198
+ throw new Error(`Unknown agent: ${agent}`);
4199
+ }
4200
+ let prompt = agentPrompts[promptType];
4201
+ if (!prompt) {
4202
+ throw new Error(`Unknown prompt type: ${promptType} for agent: ${agent}`);
4203
+ }
4204
+ for (const [key, value] of Object.entries(variables)) {
4205
+ prompt = prompt.replace(new RegExp(`{{${key}}}`, "g"), value);
4206
+ }
4207
+ return prompt;
4208
+ }
4209
+ function getSystemPrompt(agent) {
4210
+ const agentPrompts = AGENT_PROMPTS[agent];
4211
+ return agentPrompts?.system || "";
4212
+ }
4213
+
4214
+ // src/tools/explain.ts
4215
+ var TrieExplainTool = class {
4216
+ async execute(args) {
4217
+ const { type, target, context, depth = "standard" } = args || {};
4218
+ if (!type || !target) {
4219
+ return {
4220
+ content: [{
4221
+ type: "text",
4222
+ text: this.getHelpText()
4223
+ }]
4224
+ };
4225
+ }
4226
+ switch (type) {
4227
+ case "code":
4228
+ return this.explainCode(target, context, depth);
4229
+ case "issue":
4230
+ return this.explainIssue(target, context);
4231
+ case "change":
4232
+ return this.explainChange(target, context);
4233
+ case "risk":
4234
+ return this.explainRisk(target, context);
4235
+ default:
4236
+ return {
4237
+ content: [{
4238
+ type: "text",
4239
+ text: `Unknown explanation type: ${type}`
4240
+ }]
4241
+ };
4242
+ }
4243
+ }
4244
+ async explainCode(target, context, _depth) {
4245
+ let code;
4246
+ let filePath;
4247
+ let language;
4248
+ const workDir = getWorkingDirectory(void 0, true);
4249
+ const resolvedPath = isAbsolute(target) ? target : resolve(workDir, target);
4250
+ if (existsSync(resolvedPath)) {
4251
+ code = await readFile(resolvedPath, "utf-8");
4252
+ filePath = relative(workDir, resolvedPath);
4253
+ language = this.detectLanguage(resolvedPath);
4254
+ } else {
4255
+ code = target;
4256
+ filePath = "inline";
4257
+ language = this.guessLanguage(code);
4258
+ }
4259
+ const imports = this.extractImports(code, language);
4260
+ const exports = this.extractExports(code);
4261
+ const functions = this.extractFunctions(code, language);
4262
+ const prompt = getPrompt("explain", "code", {
4263
+ code,
4264
+ language,
4265
+ filePath
4266
+ });
4267
+ const systemPrompt = getSystemPrompt("explain");
4268
+ let output = `
4269
+ ${"\u2501".repeat(60)}
4270
+ `;
4271
+ output += `\u{1F4D6} CODE EXPLANATION
4272
+ `;
4273
+ output += `${"\u2501".repeat(60)}
4274
+
4275
+ `;
4276
+ output += `## Source
4277
+
4278
+ `;
4279
+ output += `- **File:** \`${filePath}\`
4280
+ `;
4281
+ output += `- **Language:** ${language}
4282
+ `;
4283
+ output += `- **Lines:** ${code.split("\n").length}
4284
+
4285
+ `;
4286
+ output += `## \u{1F50D} Structure Analysis
4287
+
4288
+ `;
4289
+ if (imports.length > 0) {
4290
+ output += `**Imports (${imports.length}):**
4291
+ `;
4292
+ for (const imp of imports.slice(0, 10)) {
4293
+ output += `- ${imp}
4294
+ `;
4295
+ }
4296
+ if (imports.length > 10) {
4297
+ output += `- *...and ${imports.length - 10} more*
4298
+ `;
4299
+ }
4300
+ output += "\n";
4301
+ }
4302
+ if (exports.length > 0) {
4303
+ output += `**Exports (${exports.length}):**
4304
+ `;
4305
+ for (const exp of exports) {
4306
+ output += `- ${exp}
4307
+ `;
4308
+ }
4309
+ output += "\n";
4310
+ }
4311
+ if (functions.length > 0) {
4312
+ output += `**Functions/Methods (${functions.length}):**
4313
+ `;
4314
+ for (const fn of functions.slice(0, 15)) {
4315
+ output += `- \`${fn}\`
4316
+ `;
4317
+ }
4318
+ if (functions.length > 15) {
4319
+ output += `- *...and ${functions.length - 15} more*
4320
+ `;
4321
+ }
4322
+ output += "\n";
4323
+ }
4324
+ output += `${"\u2500".repeat(60)}
4325
+ `;
4326
+ output += `## \u{1F9E0} Deep Explanation Request
4327
+
4328
+ `;
4329
+ output += `**Role:** ${systemPrompt.split("\n")[0]}
4330
+
4331
+ `;
4332
+ output += prompt;
4333
+ output += `
4334
+ ${"\u2500".repeat(60)}
4335
+ `;
4336
+ if (context) {
4337
+ output += `
4338
+ **Additional Context:** ${context}
4339
+ `;
4340
+ }
4341
+ return { content: [{ type: "text", text: output }] };
4342
+ }
4343
+ async explainIssue(target, _context) {
4344
+ let file = "";
4345
+ let line = 0;
4346
+ let issue = target;
4347
+ let severity = "unknown";
4348
+ const match = target.match(/^(.+?):(\d+):(.+)$/);
4349
+ if (match) {
4350
+ file = match[1];
4351
+ line = parseInt(match[2], 10);
4352
+ issue = match[3].trim();
4353
+ }
4354
+ if (/critical|injection|rce|xss/i.test(issue)) severity = "critical";
4355
+ else if (/serious|auth|password|secret/i.test(issue)) severity = "serious";
4356
+ else if (/moderate|warning/i.test(issue)) severity = "moderate";
4357
+ else severity = "low";
4358
+ let codeContext = "";
4359
+ if (file && existsSync(file)) {
4360
+ const content = await readFile(file, "utf-8");
4361
+ const lines = content.split("\n");
4362
+ const start = Math.max(0, line - 5);
4363
+ const end = Math.min(lines.length, line + 5);
4364
+ codeContext = lines.slice(start, end).map((l, i) => {
4365
+ const lineNum = start + i + 1;
4366
+ const marker = lineNum === line ? "\u2192 " : " ";
4367
+ return `${marker}${lineNum.toString().padStart(4)} | ${l}`;
4368
+ }).join("\n");
4369
+ }
4370
+ const prompt = getPrompt("explain", "issue", {
4371
+ issue,
4372
+ severity,
4373
+ filePath: file || "unknown",
4374
+ line: String(line || "?")
4375
+ });
4376
+ let output = `
4377
+ ${"\u2501".repeat(60)}
4378
+ `;
4379
+ output += `\u{1F50D} ISSUE EXPLANATION
4380
+ `;
4381
+ output += `${"\u2501".repeat(60)}
4382
+
4383
+ `;
4384
+ output += `## \u{1F4CD} Issue Details
4385
+
4386
+ `;
4387
+ output += `- **Issue:** ${issue}
4388
+ `;
4389
+ output += `- **Severity:** ${this.getSeverityIcon(severity)} ${severity}
4390
+ `;
4391
+ if (file) output += `- **File:** \`${file}\`
4392
+ `;
4393
+ if (line) output += `- **Line:** ${line}
4394
+ `;
4395
+ output += "\n";
4396
+ if (codeContext) {
4397
+ output += `## \u{1F4C4} Code Context
4398
+
4399
+ `;
4400
+ output += `\`\`\`
4401
+ ${codeContext}
4402
+ \`\`\`
4403
+
4404
+ `;
4405
+ }
4406
+ output += `${"\u2500".repeat(60)}
4407
+ `;
4408
+ output += `## \u{1F9E0} Explanation Request
4409
+
4410
+ `;
4411
+ output += prompt;
4412
+ output += `
4413
+ ${"\u2500".repeat(60)}
4414
+ `;
4415
+ return { content: [{ type: "text", text: output }] };
4416
+ }
4417
+ async explainChange(target, context) {
4418
+ const files = target.split(",").map((f) => f.trim());
4419
+ let output = `
4420
+ ${"\u2501".repeat(60)}
4421
+ `;
4422
+ output += `\u{1F4DD} CHANGE ANALYSIS
4423
+ `;
4424
+ output += `${"\u2501".repeat(60)}
4425
+
4426
+ `;
4427
+ output += `## \u{1F4C2} Changed Files
4428
+
4429
+ `;
4430
+ for (const file of files) {
4431
+ output += `- \`${file}\`
4432
+ `;
4433
+ }
4434
+ output += "\n";
4435
+ output += `## \u{1F9E0} Analysis Request
4436
+
4437
+ `;
4438
+ output += `Analyze these changes and explain:
4439
+
4440
+ `;
4441
+ output += `1. **What changed** - Summary of modifications
4442
+ `;
4443
+ output += `2. **Why it matters** - Impact on the system
4444
+ `;
4445
+ output += `3. **Dependencies** - What else might be affected
4446
+ `;
4447
+ output += `4. **Testing needed** - What to test after this change
4448
+ `;
4449
+ output += `5. **Rollback plan** - How to undo if needed
4450
+
4451
+ `;
4452
+ if (context) {
4453
+ output += `**Context:** ${context}
4454
+
4455
+ `;
4456
+ }
4457
+ output += `Please review the changed files and provide this analysis.
4458
+ `;
4459
+ return { content: [{ type: "text", text: output }] };
4460
+ }
4461
+ async explainRisk(target, context) {
4462
+ const workDir = getWorkingDirectory(void 0, true);
4463
+ const resolvedPath = isAbsolute(target) ? target : resolve(workDir, target);
4464
+ let output = `
4465
+ ${"\u2501".repeat(60)}
4466
+ `;
4467
+ output += `RISK ASSESSMENT
4468
+ `;
4469
+ output += `${"\u2501".repeat(60)}
4470
+
4471
+ `;
4472
+ if (existsSync(resolvedPath)) {
4473
+ const code = await readFile(resolvedPath, "utf-8");
4474
+ const filePath = relative(workDir, resolvedPath);
4475
+ const riskIndicators = this.detectRiskIndicators(code);
4476
+ output += `## Target
4477
+
4478
+ `;
4479
+ output += `- **File:** \`${filePath}\`
4480
+ `;
4481
+ output += `- **Lines:** ${code.split("\n").length}
4482
+
4483
+ `;
4484
+ if (riskIndicators.length > 0) {
4485
+ output += `## Risk Indicators Found
4486
+
4487
+ `;
4488
+ for (const indicator of riskIndicators) {
4489
+ output += `- ${indicator}
4490
+ `;
4491
+ }
4492
+ output += "\n";
4493
+ }
4494
+ const prompt = getPrompt("explain", "risk", {
4495
+ files: filePath,
4496
+ summary: context || "Code change"
4497
+ });
4498
+ output += `${"\u2500".repeat(60)}
4499
+ `;
4500
+ output += `## \u{1F9E0} Risk Analysis Request
4501
+
4502
+ `;
4503
+ output += prompt;
4504
+ output += `
4505
+ ${"\u2500".repeat(60)}
4506
+ `;
4507
+ } else {
4508
+ output += `## \u{1F4CB} Feature/Change
4509
+
4510
+ `;
4511
+ output += `${target}
4512
+
4513
+ `;
4514
+ output += `## \u{1F9E0} Risk Analysis Request
4515
+
4516
+ `;
4517
+ output += `Analyze the risks of this change:
4518
+
4519
+ `;
4520
+ output += `1. **Technical risks** - What could break?
4521
+ `;
4522
+ output += `2. **Security risks** - Any vulnerabilities introduced?
4523
+ `;
4524
+ output += `3. **Performance risks** - Any slowdowns?
4525
+ `;
4526
+ output += `4. **Data risks** - Any data integrity concerns?
4527
+ `;
4528
+ output += `5. **User impact** - How might users be affected?
4529
+ `;
4530
+ output += `6. **Mitigation** - How to reduce these risks?
4531
+ `;
4532
+ }
4533
+ return { content: [{ type: "text", text: output }] };
4534
+ }
4535
+ detectRiskIndicators(code) {
4536
+ const indicators = [];
4537
+ const checks = [
4538
+ { pattern: /delete|drop|truncate/i, message: "[!] Destructive operations detected" },
4539
+ { pattern: /password|secret|key|token/i, message: "[SEC] Credential handling detected" },
4540
+ { pattern: /exec|eval|spawn/i, message: "[EXEC] Code execution patterns detected" },
4541
+ { pattern: /SELECT.*FROM|INSERT|UPDATE|DELETE/i, message: "[DB] Direct database operations" },
4542
+ { pattern: /fetch|axios|request|http/i, message: "[API] External API calls detected" },
4543
+ { pattern: /process\.env/i, message: "[ENV] Environment variable usage" },
4544
+ { pattern: /fs\.|writeFile|readFile/i, message: "[FS] File system operations" },
4545
+ { pattern: /setTimeout|setInterval/i, message: "[TIMER] Async timing operations" },
4546
+ { pattern: /try\s*{/i, message: "\u{1F6E1}\uFE0F Error handling present" },
4547
+ { pattern: /catch\s*\(/i, message: "\u{1F6E1}\uFE0F Exception handling present" }
4548
+ ];
4549
+ for (const { pattern, message } of checks) {
4550
+ if (pattern.test(code)) {
4551
+ indicators.push(message);
4552
+ }
4553
+ }
4554
+ return indicators;
4555
+ }
4556
+ extractImports(code, _language) {
4557
+ const imports = [];
4558
+ const lines = code.split("\n");
4559
+ for (const line of lines) {
4560
+ const es6Match = line.match(/import\s+(?:{[^}]+}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/);
4561
+ if (es6Match) {
4562
+ imports.push(es6Match[1]);
4563
+ continue;
4564
+ }
4565
+ const cjsMatch = line.match(/require\s*\(['"]([^'"]+)['"]\)/);
4566
+ if (cjsMatch) {
4567
+ imports.push(cjsMatch[1]);
4568
+ continue;
4569
+ }
4570
+ const pyMatch = line.match(/^(?:from\s+(\S+)\s+)?import\s+(\S+)/);
4571
+ if (pyMatch && _language === "python") {
4572
+ imports.push(pyMatch[1] || pyMatch[2]);
4573
+ }
4574
+ }
4575
+ return [...new Set(imports)];
4576
+ }
4577
+ extractExports(code) {
4578
+ const exports = [];
4579
+ const lines = code.split("\n");
4580
+ for (const line of lines) {
4581
+ const es6Match = line.match(/export\s+(?:default\s+)?(?:class|function|const|let|var|interface|type)\s+(\w+)/);
4582
+ if (es6Match) {
4583
+ exports.push(es6Match[1]);
4584
+ }
4585
+ const namedMatch = line.match(/export\s*\{([^}]+)\}/);
4586
+ if (namedMatch) {
4587
+ const names = namedMatch[1].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
4588
+ exports.push(...names);
4589
+ }
4590
+ }
4591
+ return [...new Set(exports)];
4592
+ }
4593
+ extractFunctions(code, _language) {
4594
+ const functions = [];
4595
+ const lines = code.split("\n");
4596
+ for (const line of lines) {
4597
+ const funcMatch = line.match(/(?:async\s+)?function\s+(\w+)/);
4598
+ if (funcMatch) {
4599
+ functions.push(funcMatch[1]);
4600
+ continue;
4601
+ }
4602
+ const arrowMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\(/);
4603
+ if (arrowMatch) {
4604
+ functions.push(arrowMatch[1]);
4605
+ continue;
4606
+ }
4607
+ const methodMatch = line.match(/^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+)?\s*\{/);
4608
+ if (methodMatch && !["if", "for", "while", "switch", "catch"].includes(methodMatch[1])) {
4609
+ functions.push(methodMatch[1]);
4610
+ }
4611
+ }
4612
+ return [...new Set(functions)];
4613
+ }
4614
+ detectLanguage(filePath) {
4615
+ const ext = extname(filePath).toLowerCase();
4616
+ const langMap = {
4617
+ ".ts": "typescript",
4618
+ ".tsx": "tsx",
4619
+ ".js": "javascript",
4620
+ ".jsx": "jsx",
4621
+ ".py": "python",
4622
+ ".go": "go",
4623
+ ".rs": "rust",
4624
+ ".java": "java",
4625
+ ".rb": "ruby",
4626
+ ".php": "php",
4627
+ ".vue": "vue",
4628
+ ".svelte": "svelte"
4629
+ };
4630
+ return langMap[ext] || "plaintext";
4631
+ }
4632
+ guessLanguage(code) {
4633
+ if (/import.*from|export\s+(default|const|function|class)/.test(code)) return "typescript";
4634
+ if (/def\s+\w+.*:/.test(code)) return "python";
4635
+ if (/func\s+\w+.*\{/.test(code)) return "go";
4636
+ if (/fn\s+\w+.*->/.test(code)) return "rust";
4637
+ return "javascript";
4638
+ }
4639
+ getSeverityIcon(severity) {
4640
+ const icons = {
4641
+ critical: "\u{1F534}",
4642
+ serious: "\u{1F7E0}",
4643
+ moderate: "\u{1F7E1}",
4644
+ low: "\u{1F535}",
4645
+ unknown: "\u26AA"
4646
+ };
4647
+ return icons[severity] || "\u26AA";
4648
+ }
4649
+ getHelpText() {
4650
+ return `
4651
+ ${"\u2501".repeat(60)}
4652
+ \u{1F4D6} TRIE EXPLAIN - AI-POWERED CODE EXPLANATION
4653
+ ${"\u2501".repeat(60)}
4654
+
4655
+ ## Usage
4656
+
4657
+ ### Explain code:
4658
+ \`\`\`
4659
+ trie_explain type:"code" target:"src/app.ts"
4660
+ \`\`\`
4661
+
4662
+ ### Explain an issue:
4663
+ \`\`\`
4664
+ trie_explain type:"issue" target:"SQL injection vulnerability"
4665
+ \`\`\`
4666
+ or with file context:
4667
+ \`\`\`
4668
+ trie_explain type:"issue" target:"src/db.ts:42:Unvalidated input"
4669
+ \`\`\`
4670
+
4671
+ ### Explain changes:
4672
+ \`\`\`
4673
+ trie_explain type:"change" target:"src/auth.ts, src/user.ts"
4674
+ \`\`\`
4675
+
4676
+ ### Assess risk:
4677
+ \`\`\`
4678
+ trie_explain type:"risk" target:"src/payment.ts"
4679
+ \`\`\`
4680
+ or for a feature:
4681
+ \`\`\`
4682
+ trie_explain type:"risk" target:"Adding Stripe integration for payments"
4683
+ \`\`\`
4684
+
4685
+ ## Explanation Types
4686
+
4687
+ | Type | Description |
4688
+ |------|-------------|
4689
+ | code | What does this code do? |
4690
+ | issue | Why is this a problem? |
4691
+ | change | What's the impact? |
4692
+ | risk | What could go wrong? |
4693
+ `;
4694
+ }
4695
+ };
4696
+
4697
+ // src/tools/query-tools.ts
4698
+ var TrieGetDecisionsTool = class {
4699
+ async execute(input) {
4700
+ const workDir = input.directory || getWorkingDirectory(void 0, true);
4701
+ const storage = getStorage(workDir);
4702
+ await storage.initialize();
4703
+ let timeWindow;
4704
+ if (input.since) {
4705
+ const now = /* @__PURE__ */ new Date();
4706
+ if (input.since.endsWith("d")) {
4707
+ const days = parseInt(input.since);
4708
+ const start = new Date(now);
4709
+ start.setDate(start.getDate() - days);
4710
+ timeWindow = { start: start.toISOString() };
4711
+ } else {
4712
+ timeWindow = { start: input.since };
4713
+ }
4714
+ }
4715
+ const query = {
4716
+ limit: input.limit || 10
4717
+ };
4718
+ if (input.relatedTo) query.relatedTo = input.relatedTo;
4719
+ if (input.tags) query.tags = input.tags;
4720
+ if (timeWindow) query.timeWindow = timeWindow;
4721
+ const decisions = await storage.queryDecisions(query);
4722
+ return {
4723
+ content: [{
4724
+ type: "text",
4725
+ text: this.formatDecisions(decisions)
4726
+ }]
4727
+ };
4728
+ }
4729
+ formatDecisions(decisions) {
4730
+ if (decisions.length === 0) {
4731
+ return "No decisions found matching query.";
4732
+ }
4733
+ let output = `Found ${decisions.length} decision(s):
4734
+
4735
+ `;
4736
+ for (const dec of decisions) {
4737
+ const when = new Date(dec.when).toLocaleDateString();
4738
+ output += `\u{1F4CB} ${dec.decision}
4739
+ `;
4740
+ output += ` Context: ${dec.context}
4741
+ `;
4742
+ if (dec.reasoning) {
4743
+ output += ` Reasoning: ${dec.reasoning}
4744
+ `;
4745
+ }
4746
+ if (dec.tradeoffs && dec.tradeoffs.length > 0) {
4747
+ output += ` Tradeoffs considered: ${dec.tradeoffs.join(", ")}
4748
+ `;
4749
+ }
4750
+ output += ` When: ${when}
4751
+ `;
4752
+ if (dec.files.length > 0) {
4753
+ output += ` Files: ${dec.files.join(", ")}
4754
+ `;
4755
+ }
4756
+ output += ` Tags: ${dec.tags.join(", ")}
4757
+ `;
4758
+ output += `
4759
+ `;
4760
+ }
4761
+ return output;
4762
+ }
4763
+ };
4764
+ var TrieGetBlockersTool = class {
4765
+ async execute(input) {
4766
+ const workDir = input.directory || getWorkingDirectory(void 0, true);
4767
+ const storage = getStorage(workDir);
4768
+ await storage.initialize();
4769
+ const query = {
4770
+ limit: input.limit || 5
4771
+ };
4772
+ if (input.tags) query.tags = input.tags;
4773
+ const blockers = await storage.queryBlockers(query);
4774
+ return {
4775
+ content: [{
4776
+ type: "text",
4777
+ text: this.formatBlockers(blockers)
4778
+ }]
4779
+ };
4780
+ }
4781
+ formatBlockers(blockers) {
4782
+ if (blockers.length === 0) {
4783
+ return "\u2705 No active blockers found.";
4784
+ }
4785
+ let output = `\u26A0\uFE0F Found ${blockers.length} active blocker(s):
4786
+
4787
+ `;
4788
+ for (const blocker of blockers) {
4789
+ const impact = blocker.impact.toUpperCase();
4790
+ const emoji = blocker.impact === "critical" ? "\u{1F534}" : blocker.impact === "high" ? "\u{1F7E0}" : blocker.impact === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
4791
+ output += `${emoji} [${impact}] ${blocker.blocker}
4792
+ `;
4793
+ if (blocker.affectedAreas.length > 0) {
4794
+ output += ` Affects: ${blocker.affectedAreas.join(", ")}
4795
+ `;
4796
+ }
4797
+ output += ` Since: ${new Date(blocker.when).toLocaleDateString()}
4798
+ `;
4799
+ output += `
4800
+ `;
4801
+ }
4802
+ return output;
4803
+ }
4804
+ };
4805
+ var TrieGetRelatedDecisionsTool = class {
4806
+ async execute(input) {
4807
+ const workDir = input.directory || getWorkingDirectory(void 0, true);
4808
+ const storage = getStorage(workDir);
4809
+ await storage.initialize();
4810
+ const query = {
4811
+ limit: input.limit || 5
4812
+ };
4813
+ const relatedTo = input.file || input.topic;
4814
+ if (relatedTo) query.relatedTo = relatedTo;
4815
+ const decisions = await storage.queryDecisions(query);
4816
+ return {
4817
+ content: [{
4818
+ type: "text",
4819
+ text: new TrieGetDecisionsTool().formatDecisions(decisions)
4820
+ }]
4821
+ };
4822
+ }
4823
+ };
4824
+ var TrieQueryContextTool = class {
4825
+ async execute(input) {
4826
+ const workDir = input.directory || getWorkingDirectory(void 0, true);
4827
+ const storage = getStorage(workDir);
4828
+ await storage.initialize();
4829
+ const keywords = input.query.toLowerCase().split(/\s+/);
4830
+ let output = `Query: "${input.query}"
4831
+
4832
+ `;
4833
+ if (!input.type || input.type === "decisions" || input.type === "all") {
4834
+ const decisions = await storage.queryDecisions({ limit: input.limit || 5 });
4835
+ const matches = decisions.filter(
4836
+ (d) => keywords.some(
4837
+ (kw) => d.decision.toLowerCase().includes(kw) || d.context.toLowerCase().includes(kw) || d.tags.some((t) => t.toLowerCase().includes(kw))
4838
+ )
4839
+ );
4840
+ if (matches.length > 0) {
4841
+ output += `\u{1F4CB} DECISIONS (${matches.length}):
4842
+ `;
4843
+ output += new TrieGetDecisionsTool().formatDecisions(matches);
4844
+ output += "\n";
4845
+ }
4846
+ }
4847
+ if (!input.type || input.type === "blockers" || input.type === "all") {
4848
+ const blockers = await storage.queryBlockers({ limit: input.limit || 5 });
4849
+ const matches = blockers.filter(
4850
+ (b) => keywords.some(
4851
+ (kw) => b.blocker.toLowerCase().includes(kw) || b.tags.some((t) => t.toLowerCase().includes(kw))
4852
+ )
4853
+ );
4854
+ if (matches.length > 0) {
4855
+ output += `\u26A0\uFE0F BLOCKERS (${matches.length}):
4856
+ `;
4857
+ output += new TrieGetBlockersTool().formatBlockers(matches);
4858
+ }
4859
+ }
4860
+ return {
4861
+ content: [{
4862
+ type: "text",
4863
+ text: output.trim() || "No matches found."
4864
+ }]
4865
+ };
4866
+ }
4867
+ };
4868
+
4869
+ // src/tools/checkpoint.ts
4870
+ async function handleCheckpointTool(input) {
4871
+ const workDir = getWorkingDirectory(void 0, true);
4872
+ switch (input.action) {
4873
+ case "save": {
4874
+ const saveOptions = {
4875
+ files: input.files || [],
4876
+ workDir,
4877
+ createdBy: "mcp"
4878
+ };
4879
+ if (input.message !== void 0) {
4880
+ saveOptions.message = input.message;
4881
+ }
4882
+ if (input.notes !== void 0) {
4883
+ saveOptions.notes = input.notes;
4884
+ }
4885
+ const checkpoint = await saveCheckpoint(saveOptions);
4886
+ return `# Checkpoint Saved
4887
+
4888
+ **ID:** ${checkpoint.id}
4889
+ **Time:** ${checkpoint.timestamp}
4890
+ ${checkpoint.message ? `**Message:** ${checkpoint.message}` : ""}
4891
+ ${checkpoint.notes ? `**Notes:** ${checkpoint.notes}` : ""}
4892
+ ${checkpoint.files.length > 0 ? `**Files:** ${checkpoint.files.join(", ")}` : ""}
4893
+
4894
+ Context saved to \`.trie/\`. This checkpoint will be visible in other tools (Cursor, Claude Code, CLI).`;
4895
+ }
4896
+ case "list": {
4897
+ const checkpoints = await listCheckpoints(workDir);
4898
+ if (checkpoints.length === 0) {
4899
+ return 'No checkpoints yet. Use `trie_checkpoint action="save"` to create one.';
4900
+ }
4901
+ const lines = ["# Recent Checkpoints", ""];
4902
+ for (const cp of checkpoints.slice(-10).reverse()) {
4903
+ const date = new Date(cp.timestamp).toLocaleString();
4904
+ lines.push(`- **${cp.id}** (${date}): ${cp.message || "(no message)"}`);
4905
+ }
4906
+ return lines.join("\n");
4907
+ }
4908
+ case "last": {
4909
+ const checkpoint = await getLastCheckpoint(workDir);
4910
+ if (!checkpoint) {
4911
+ return 'No checkpoints yet. Use `trie_checkpoint action="save"` to create one.';
4912
+ }
4913
+ return `# Last Checkpoint
4914
+
4915
+ **ID:** ${checkpoint.id}
4916
+ **Time:** ${new Date(checkpoint.timestamp).toLocaleString()}
4917
+ ${checkpoint.message ? `**Message:** ${checkpoint.message}` : ""}
4918
+ ${checkpoint.notes ? `**Notes:** ${checkpoint.notes}` : ""}
4919
+ ${checkpoint.files.length > 0 ? `**Files:** ${checkpoint.files.join(", ")}` : ""}
4920
+ **Created by:** ${checkpoint.createdBy}`;
4921
+ }
4922
+ default:
4923
+ return "Unknown action. Use: save, list, or last";
4924
+ }
4925
+ }
4926
+
4927
+ // src/cli/dashboard/chat-tools.ts
4928
+ function textFromResult(result) {
4929
+ return result.content.map((c) => c.text).join("\n");
4930
+ }
4931
+ var CHAT_TOOLS = [
4932
+ {
4933
+ name: "trie_tell",
4934
+ description: "Record an incident or observation about the codebase. Use when the user reports a bug, crash, or notable event.",
4935
+ input_schema: {
4936
+ type: "object",
4937
+ properties: {
4938
+ description: { type: "string", description: "What happened \u2014 the incident or observation" }
4939
+ },
4940
+ required: ["description"]
4941
+ }
4942
+ },
4943
+ {
4944
+ name: "trie_feedback",
4945
+ description: 'Record thumbs-up or thumbs-down feedback. Use for "trie ok" (helpful=true) or "trie bad" (helpful=false).',
4946
+ input_schema: {
4947
+ type: "object",
4948
+ properties: {
4949
+ helpful: { type: "boolean", description: "true for positive, false for negative feedback" },
4950
+ target: { type: "string", description: "Optional file or item being rated" },
4951
+ note: { type: "string", description: "Optional explanation" }
4952
+ },
4953
+ required: ["helpful"]
4954
+ }
4955
+ },
4956
+ {
4957
+ name: "trie_check",
4958
+ description: "Run a risk check on files or the whole project. Returns risk assessment and potential issues.",
4959
+ input_schema: {
4960
+ type: "object",
4961
+ properties: {
4962
+ files: { type: "array", items: { type: "string" }, description: "Specific files to check (omit for full project)" },
4963
+ mode: { type: "string", enum: ["quick", "full", "offline"], description: "Check mode \u2014 defaults to quick" }
4964
+ }
4965
+ }
4966
+ },
4967
+ {
4968
+ name: "trie_explain",
4969
+ description: "Explain code, an issue, a change, or a risk in the project.",
4970
+ input_schema: {
4971
+ type: "object",
4972
+ properties: {
4973
+ type: { type: "string", enum: ["code", "issue", "change", "risk"], description: "What to explain" },
4974
+ target: { type: "string", description: "File path, issue ID, or description of what to explain" }
4975
+ },
4976
+ required: ["type", "target"]
4977
+ }
4978
+ },
4979
+ {
4980
+ name: "trie_get_decisions",
4981
+ description: "Query the decision ledger \u2014 past architectural and coding decisions recorded by Trie.",
4982
+ input_schema: {
4983
+ type: "object",
4984
+ properties: {
4985
+ relatedTo: { type: "string", description: "Filter decisions related to a topic or file" },
4986
+ limit: { type: "number", description: "Max results (default 10)" }
4987
+ }
4988
+ }
4989
+ },
4990
+ {
4991
+ name: "trie_get_blockers",
4992
+ description: "Query active blockers \u2014 known problems preventing progress.",
4993
+ input_schema: {
4994
+ type: "object",
4995
+ properties: {
4996
+ limit: { type: "number", description: "Max results (default 10)" }
4997
+ }
4998
+ }
4999
+ },
5000
+ {
5001
+ name: "trie_query_context",
5002
+ description: "Natural-language search across the context graph \u2014 decisions, blockers, facts, and questions.",
5003
+ input_schema: {
5004
+ type: "object",
5005
+ properties: {
5006
+ query: { type: "string", description: "Natural language search query" },
5007
+ type: { type: "string", enum: ["decisions", "blockers", "facts", "questions", "all"], description: "Narrow to a specific category (default all)" },
5008
+ limit: { type: "number", description: "Max results (default 10)" }
5009
+ },
5010
+ required: ["query"]
5011
+ }
5012
+ },
5013
+ {
5014
+ name: "trie_checkpoint",
5015
+ description: "Save a work checkpoint, list recent checkpoints, or get the last one.",
5016
+ input_schema: {
5017
+ type: "object",
5018
+ properties: {
5019
+ action: { type: "string", enum: ["save", "list", "last"], description: "What to do" },
5020
+ message: { type: "string", description: "Checkpoint message (for save)" },
5021
+ notes: { type: "string", description: "Additional notes (for save)" },
5022
+ files: { type: "array", items: { type: "string" }, description: "Files to associate (for save)" }
5023
+ },
5024
+ required: ["action"]
5025
+ }
5026
+ }
5027
+ ];
5028
+ async function executeTool(name, input) {
5029
+ const directory = getWorkingDirectory(void 0, true);
5030
+ const withDir = { ...input, directory };
5031
+ switch (name) {
5032
+ case "trie_tell": {
5033
+ const tool = new TrieTellTool();
5034
+ const result = await tool.execute(withDir);
5035
+ return textFromResult(result);
5036
+ }
5037
+ case "trie_feedback": {
5038
+ const tool = new TrieFeedbackTool();
5039
+ const result = await tool.execute(withDir);
5040
+ return textFromResult(result);
5041
+ }
5042
+ case "trie_check": {
5043
+ const tool = new TrieCheckTool();
5044
+ const result = await tool.execute(withDir);
5045
+ return textFromResult(result);
5046
+ }
5047
+ case "trie_explain": {
5048
+ const tool = new TrieExplainTool();
5049
+ const result = await tool.execute(input);
5050
+ return textFromResult(result);
5051
+ }
5052
+ case "trie_get_decisions": {
5053
+ const tool = new TrieGetDecisionsTool();
5054
+ const result = await tool.execute(withDir);
5055
+ return textFromResult(result);
5056
+ }
5057
+ case "trie_get_blockers": {
5058
+ const tool = new TrieGetBlockersTool();
5059
+ const result = await tool.execute(withDir);
5060
+ return textFromResult(result);
5061
+ }
5062
+ case "trie_query_context": {
5063
+ const tool = new TrieQueryContextTool();
5064
+ const result = await tool.execute(withDir);
5065
+ return textFromResult(result);
5066
+ }
5067
+ case "trie_checkpoint": {
5068
+ const result = await handleCheckpointTool(input);
5069
+ return result;
5070
+ }
5071
+ default:
5072
+ return `Unknown tool: ${name}`;
5073
+ }
5074
+ }
5075
+
5076
+ // src/cli/dashboard/views/ChatView.tsx
5077
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
5078
+ async function buildContext(workDir) {
5079
+ const parts = [];
5080
+ try {
5081
+ const storage = new TieredStorage(workDir);
5082
+ try {
5083
+ const decisions = await storage.queryDecisions({ limit: 10 });
5084
+ if (decisions.length > 0) {
5085
+ parts.push("Recent decisions:\n" + decisions.map(
5086
+ (d) => `- ${d.decision} (${d.when}${d.hash ? `, hash: ${d.hash.slice(0, 8)}` : ""})`
5087
+ ).join("\n"));
5088
+ }
5089
+ } catch {
5090
+ }
5091
+ try {
5092
+ const blockers = await storage.queryBlockers({ limit: 5 });
5093
+ if (blockers.length > 0) {
5094
+ parts.push("Active blockers:\n" + blockers.map(
5095
+ (b) => `- ${b.blocker} [${b.impact}]`
5096
+ ).join("\n"));
5097
+ }
5098
+ } catch {
5099
+ }
5100
+ } catch {
5101
+ }
5102
+ try {
5103
+ const graph = new ContextGraph(workDir);
5104
+ const snap = await graph.getSnapshot();
5105
+ const fileNodes = snap.nodes.filter((n) => n.type === "file");
5106
+ const changeNodes = snap.nodes.filter((n) => n.type === "change");
5107
+ const patternNodes = snap.nodes.filter((n) => n.type === "pattern");
5108
+ parts.push(`Context graph: ${snap.nodes.length} nodes, ${snap.edges.length} edges (${fileNodes.length} files, ${changeNodes.length} changes, ${patternNodes.length} patterns)`);
5109
+ if (fileNodes.length > 0) {
5110
+ const sorted = [...fileNodes].sort((a, b) => {
5111
+ const riskOrder = { critical: 0, high: 1, medium: 2, low: 3 };
5112
+ const ad = a.data, bd = b.data;
5113
+ return (riskOrder[ad.riskLevel] ?? 4) - (riskOrder[bd.riskLevel] ?? 4);
5114
+ });
5115
+ parts.push("Project files (by risk):\n" + sorted.slice(0, 15).map((n) => {
5116
+ const d = n.data;
5117
+ return `- ${d.path} (${d.riskLevel}, ${d.changeCount} changes${d.incidentCount > 0 ? `, ${d.incidentCount} incidents` : ""})`;
5118
+ }).join("\n"));
5119
+ }
5120
+ if (changeNodes.length > 0) {
5121
+ parts.push("Recent changes:\n" + changeNodes.slice(0, 5).map((n) => {
5122
+ const d = n.data;
5123
+ return `- ${d.message} (${d.timestamp}, ${d.files.length} files)`;
5124
+ }).join("\n"));
5125
+ }
5126
+ if (patternNodes.length > 0) {
5127
+ parts.push("Learned patterns:\n" + patternNodes.slice(0, 5).map((n) => {
5128
+ const d = n.data;
5129
+ return `- ${d.description} (${Math.round(d.confidence * 100)}%${d.isAntiPattern ? ", anti-pattern" : ""})`;
5130
+ }).join("\n"));
5131
+ }
5132
+ } catch {
5133
+ }
5134
+ return parts.length > 0 ? parts.join("\n\n") : "No context data available yet.";
5135
+ }
5136
+ function chatHistoryToMessages(history) {
5137
+ return history.map((m) => ({
5138
+ role: m.role,
5139
+ content: m.content
5140
+ }));
5141
+ }
5142
+ var SYSTEM_PROMPT = `You are Trie, a code guardian assistant embedded in a terminal TUI.
5143
+ You have tools to take actions on the user's codebase \u2014 use them when the user asks you to check files, record incidents, give feedback, query decisions, or save checkpoints.
5144
+ Answer concisely. Reference specific files, decisions, and patterns when relevant.
5145
+ When you use a tool, briefly summarize what you did and what the result was.`;
5146
+ function ChatView() {
5147
+ const { state, dispatch } = useDashboard();
5148
+ const { chatState } = state;
5149
+ const { messages, inputBuffer, loading } = chatState;
5150
+ const loadingRef = useRef(false);
5151
+ const sendMessage = useCallback5(async (question) => {
5152
+ if (loadingRef.current) return;
5153
+ loadingRef.current = true;
5154
+ dispatch({ type: "ADD_CHAT_MESSAGE", role: "user", content: question });
5155
+ dispatch({ type: "SET_CHAT_INPUT", buffer: "" });
5156
+ dispatch({ type: "SET_CHAT_LOADING", loading: true });
5157
+ try {
5158
+ const workDir = getWorkingDirectory(void 0, true);
5159
+ const contextBlock = await buildContext(workDir);
5160
+ const fullSystem = `${SYSTEM_PROMPT}
5161
+
5162
+ Project context:
5163
+ ${contextBlock}`;
5164
+ const history = chatHistoryToMessages([
5165
+ ...messages,
5166
+ { role: "user", content: question, timestamp: Date.now() }
5167
+ ]);
5168
+ const result = await runAIWithTools({
5169
+ systemPrompt: fullSystem,
5170
+ messages: history,
5171
+ tools: CHAT_TOOLS,
5172
+ executeTool,
5173
+ maxTokens: 4096,
5174
+ maxToolRounds: 5
5175
+ });
5176
+ if (result.success) {
5177
+ const action = {
5178
+ type: "ADD_CHAT_MESSAGE",
5179
+ role: "assistant",
5180
+ content: result.content
5181
+ };
5182
+ if (result.toolCalls && result.toolCalls.length > 0) action.toolCalls = result.toolCalls;
5183
+ dispatch(action);
5184
+ } else {
5185
+ dispatch({
5186
+ type: "ADD_CHAT_MESSAGE",
5187
+ role: "assistant",
5188
+ content: result.error || "Failed to get a response."
5189
+ });
5190
+ }
5191
+ } catch (err) {
5192
+ dispatch({
5193
+ type: "ADD_CHAT_MESSAGE",
5194
+ role: "assistant",
5195
+ content: `Error: ${err instanceof Error ? err.message : "unknown"}`
5196
+ });
5197
+ } finally {
5198
+ dispatch({ type: "SET_CHAT_LOADING", loading: false });
5199
+ loadingRef.current = false;
5200
+ }
5201
+ }, [dispatch, messages]);
5202
+ useInput7((input, key) => {
5203
+ if (loading) return;
5204
+ if (key.return && inputBuffer.trim().length > 0) {
5205
+ void sendMessage(inputBuffer.trim());
5206
+ } else if (key.escape) {
5207
+ dispatch({ type: "SET_CHAT_INPUT", buffer: "" });
5208
+ } else if (key.backspace || key.delete) {
5209
+ dispatch({ type: "SET_CHAT_INPUT", buffer: inputBuffer.slice(0, -1) });
5210
+ } else if (input && !key.ctrl && !key.meta) {
5211
+ dispatch({ type: "SET_CHAT_INPUT", buffer: inputBuffer + input });
5212
+ }
5213
+ });
5214
+ if (!isAIAvailable()) {
5215
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
5216
+ /* @__PURE__ */ jsxs11(Text11, { children: [
5217
+ /* @__PURE__ */ jsx12(Text11, { bold: true, children: "Chat" }),
5218
+ " ",
5219
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Ask Trie anything" })
5220
+ ] }),
5221
+ /* @__PURE__ */ jsx12(Text11, { children: " " }),
5222
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " AI is not available." }),
5223
+ /* @__PURE__ */ jsxs11(Text11, { children: [
5224
+ " ",
5225
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Press" }),
5226
+ " ",
5227
+ /* @__PURE__ */ jsx12(Text11, { bold: true, children: "s" }),
5228
+ " ",
5229
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "to open settings and add your Anthropic API key," })
5230
+ ] }),
5231
+ /* @__PURE__ */ jsxs11(Text11, { children: [
5232
+ " ",
5233
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "or set" }),
5234
+ " ",
5235
+ /* @__PURE__ */ jsx12(Text11, { bold: true, children: "ANTHROPIC_API_KEY" }),
5236
+ " ",
5237
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "in your environment." })
5238
+ ] })
5239
+ ] });
5240
+ }
5241
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, children: [
5242
+ /* @__PURE__ */ jsxs11(Text11, { children: [
5243
+ /* @__PURE__ */ jsx12(Text11, { bold: true, children: "Chat" }),
5244
+ " ",
5245
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Ask Trie anything" })
5246
+ ] }),
5247
+ /* @__PURE__ */ jsx12(Text11, { children: " " }),
5248
+ messages.length === 0 && !loading && /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
5249
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " Ask about your codebase, decisions, patterns, or risks." }),
5250
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: ' Trie can also take actions \u2014 try "check src/auth" or "record an incident".' }),
5251
+ /* @__PURE__ */ jsx12(Text11, { children: " " })
5252
+ ] }),
5253
+ messages.map((msg, idx) => /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", marginBottom: 1, children: msg.role === "user" ? /* @__PURE__ */ jsxs11(Text11, { children: [
5254
+ " ",
5255
+ /* @__PURE__ */ jsx12(Text11, { bold: true, color: "green", children: "You:" }),
5256
+ " ",
5257
+ msg.content
5258
+ ] }) : /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
5259
+ msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ jsx12(Box11, { flexDirection: "column", marginBottom: 0, children: msg.toolCalls.map((tc, ti) => /* @__PURE__ */ jsxs11(Text11, { dimColor: true, children: [
5260
+ " ",
5261
+ /* @__PURE__ */ jsxs11(Text11, { color: "yellow", children: [
5262
+ "[ran ",
5263
+ tc.name,
5264
+ "]"
5265
+ ] }),
5266
+ " ",
5267
+ formatToolInput(tc.input)
5268
+ ] }, ti)) }),
5269
+ msg.content.split("\n").map((line, li) => /* @__PURE__ */ jsxs11(Text11, { children: [
5270
+ li === 0 ? /* @__PURE__ */ jsx12(Text11, { bold: true, children: " Trie: " }) : /* @__PURE__ */ jsx12(Text11, { children: " " }),
5271
+ line
5272
+ ] }, li))
5273
+ ] }) }, idx)),
5274
+ loading && /* @__PURE__ */ jsxs11(Text11, { children: [
5275
+ " ",
5276
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Thinking..." })
5277
+ ] }),
5278
+ /* @__PURE__ */ jsx12(Box11, { flexGrow: 1 }),
5279
+ /* @__PURE__ */ jsx12(Box11, { borderStyle: "round", borderColor: "green", paddingX: 1, children: /* @__PURE__ */ jsxs11(Text11, { children: [
5280
+ inputBuffer || /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "Ask a question..." }),
5281
+ /* @__PURE__ */ jsx12(Text11, { bold: true, color: "green", children: "|" })
5282
+ ] }) }),
5283
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: " enter send esc clear" })
5284
+ ] });
5285
+ }
5286
+ function formatToolInput(input) {
5287
+ const parts = [];
5288
+ for (const [k, v] of Object.entries(input)) {
5289
+ if (k === "directory") continue;
5290
+ if (Array.isArray(v)) parts.push(`${k}: [${v.join(", ")}]`);
5291
+ else parts.push(`${k}: ${String(v)}`);
5292
+ }
5293
+ return parts.length > 0 ? parts.join(", ") : "";
2742
5294
  }
2743
5295
 
2744
5296
  // src/cli/dashboard/App.tsx
@@ -2748,15 +5300,15 @@ function DashboardApp({ onReady }) {
2748
5300
  const { state, dispatch } = useDashboard();
2749
5301
  const { exit } = useApp();
2750
5302
  const [showConfig, setShowConfig] = useState3(false);
2751
- const dispatchRef = useRef(dispatch);
5303
+ const dispatchRef = useRef2(dispatch);
2752
5304
  dispatchRef.current = dispatch;
2753
- const stateRef = useRef(state);
5305
+ const stateRef = useRef2(state);
2754
5306
  stateRef.current = state;
2755
5307
  const configPath = join(getTrieDirectory(getWorkingDirectory(void 0, true)), "agent.json");
2756
5308
  const loadConfig = useCallback6(async () => {
2757
- if (!existsSync(configPath)) return;
5309
+ if (!existsSync2(configPath)) return;
2758
5310
  try {
2759
- const raw = await readFile(configPath, "utf-8");
5311
+ const raw = await readFile2(configPath, "utf-8");
2760
5312
  const parsed = JSON.parse(raw);
2761
5313
  dispatchRef.current({ type: "SET_AGENT_CONFIG", config: parsed });
2762
5314
  } catch {
@@ -3008,8 +5560,20 @@ var InteractiveDashboard = class {
3008
5560
 
3009
5561
  export {
3010
5562
  TrieScanTool,
5563
+ getPrompt,
5564
+ getSystemPrompt,
5565
+ TrieExplainTool,
3011
5566
  StreamingManager,
3012
5567
  getOutputManager,
5568
+ ExtractionPipeline,
5569
+ TrieTellTool,
5570
+ TrieFeedbackTool,
5571
+ TrieCheckTool,
5572
+ TrieGetDecisionsTool,
5573
+ TrieGetBlockersTool,
5574
+ TrieGetRelatedDecisionsTool,
5575
+ TrieQueryContextTool,
5576
+ handleCheckpointTool,
3013
5577
  InteractiveDashboard
3014
5578
  };
3015
- //# sourceMappingURL=chunk-G6EC4JNL.js.map
5579
+ //# sourceMappingURL=chunk-HGEKZ2VS.js.map