@triedotdev/mcp 1.0.125 → 1.0.127
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-6VIMBFUZ.js +216 -0
- package/dist/chunk-6VIMBFUZ.js.map +1 -0
- package/dist/{chunk-BNVH2LY7.js → chunk-WBNYLTW6.js} +164 -56
- package/dist/chunk-WBNYLTW6.js.map +1 -0
- package/dist/cli/yolo-daemon.js +1 -1
- package/dist/codebase-index-CR6Q2HEI.js +12 -0
- package/dist/codebase-index-CR6Q2HEI.js.map +1 -0
- package/dist/goal-validator-NLOJJ7FF.js +291 -0
- package/dist/goal-validator-NLOJJ7FF.js.map +1 -0
- package/dist/index.js +157 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-BNVH2LY7.js.map +0 -1
- package/dist/goal-validator-CKFKJ46J.js +0 -188
- package/dist/goal-validator-CKFKJ46J.js.map +0 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Trie
|
|
3
|
+
} from "./chunk-6NLHFIYA.js";
|
|
4
|
+
import {
|
|
5
|
+
atomicWriteJSON
|
|
6
|
+
} from "./chunk-43X6JBEM.js";
|
|
7
|
+
import {
|
|
8
|
+
getTrieDirectory
|
|
9
|
+
} from "./chunk-45Y5TLQZ.js";
|
|
10
|
+
import {
|
|
11
|
+
__require
|
|
12
|
+
} from "./chunk-DGUM43GV.js";
|
|
13
|
+
|
|
14
|
+
// src/context/codebase-index.ts
|
|
15
|
+
import { readFile, stat } from "fs/promises";
|
|
16
|
+
import { createHash } from "crypto";
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
import { existsSync } from "fs";
|
|
19
|
+
var CodebaseIndex = class {
|
|
20
|
+
trie = new Trie();
|
|
21
|
+
projectPath;
|
|
22
|
+
indexPath;
|
|
23
|
+
constructor(projectPath) {
|
|
24
|
+
this.projectPath = projectPath;
|
|
25
|
+
this.indexPath = join(getTrieDirectory(projectPath), "codebase-index.json");
|
|
26
|
+
this.load();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load index from disk if it exists
|
|
30
|
+
*/
|
|
31
|
+
load() {
|
|
32
|
+
if (!existsSync(this.indexPath)) return;
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(__require("fs").readFileSync(this.indexPath, "utf-8"));
|
|
35
|
+
if (raw.trie) {
|
|
36
|
+
this.trie = Trie.fromJSON(raw.trie);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error("Failed to load codebase index:", error);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Save index to disk
|
|
44
|
+
*/
|
|
45
|
+
async save() {
|
|
46
|
+
const data = {
|
|
47
|
+
version: 1,
|
|
48
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49
|
+
fileCount: this.trie.getWithPrefix("").length,
|
|
50
|
+
trie: this.trie.toJSON()
|
|
51
|
+
};
|
|
52
|
+
await atomicWriteJSON(this.indexPath, data);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Add or update a file in the index
|
|
56
|
+
* Returns null if the file doesn't exist or can't be read
|
|
57
|
+
*/
|
|
58
|
+
async indexFile(filePath) {
|
|
59
|
+
if (filePath.startsWith("/")) {
|
|
60
|
+
if (!filePath.toLowerCase().startsWith(this.projectPath.toLowerCase())) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const absolutePath = filePath.startsWith("/") ? filePath : join(this.projectPath, filePath);
|
|
65
|
+
try {
|
|
66
|
+
const content = await readFile(absolutePath, "utf-8");
|
|
67
|
+
const stats = await stat(absolutePath);
|
|
68
|
+
const hash = createHash("sha256").update(content).digest("hex");
|
|
69
|
+
const metadata = {
|
|
70
|
+
path: filePath,
|
|
71
|
+
hash,
|
|
72
|
+
size: stats.size,
|
|
73
|
+
lastModified: stats.mtimeMs,
|
|
74
|
+
type: filePath.split(".").pop() || ""
|
|
75
|
+
};
|
|
76
|
+
const existing = this.trie.search(filePath);
|
|
77
|
+
if (existing.found && existing.value) {
|
|
78
|
+
if (existing.value.hash === hash) {
|
|
79
|
+
metadata.violations = existing.value.violations;
|
|
80
|
+
metadata.lastScanned = existing.value.lastScanned;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
this.trie.insert(filePath, metadata);
|
|
84
|
+
return metadata;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get file metadata from index
|
|
91
|
+
*/
|
|
92
|
+
getFile(filePath) {
|
|
93
|
+
const result = this.trie.search(filePath);
|
|
94
|
+
return result.found && result.value ? result.value : null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if file has changed since last index
|
|
98
|
+
*/
|
|
99
|
+
async hasChanged(filePath) {
|
|
100
|
+
if (filePath.startsWith("/") && !filePath.toLowerCase().startsWith(this.projectPath.toLowerCase())) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
const metadata = this.getFile(filePath);
|
|
104
|
+
if (!metadata) return true;
|
|
105
|
+
const absolutePath = filePath.startsWith("/") ? filePath : join(this.projectPath, filePath);
|
|
106
|
+
try {
|
|
107
|
+
const stats = await stat(absolutePath);
|
|
108
|
+
if (stats.mtimeMs !== metadata.lastModified) return true;
|
|
109
|
+
const content = await readFile(absolutePath, "utf-8");
|
|
110
|
+
const hash = createHash("sha256").update(content).digest("hex");
|
|
111
|
+
return hash !== metadata.hash;
|
|
112
|
+
} catch {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get files in a directory
|
|
118
|
+
*/
|
|
119
|
+
getDirectoryFiles(directoryPath) {
|
|
120
|
+
const prefix = directoryPath.endsWith("/") ? directoryPath : `${directoryPath}/`;
|
|
121
|
+
const matches = this.trie.getWithPrefix(prefix);
|
|
122
|
+
return matches.map((m) => m.value).filter((v) => v !== null);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get files that match a pattern (uses Trie prefix matching)
|
|
126
|
+
*/
|
|
127
|
+
getFilesWithPrefix(prefix) {
|
|
128
|
+
const matches = this.trie.getWithPrefix(prefix);
|
|
129
|
+
return matches.map((m) => m.value).filter((v) => v !== null);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get files by type (extension)
|
|
133
|
+
*/
|
|
134
|
+
getFilesByType(type) {
|
|
135
|
+
const allFiles = this.trie.getWithPrefix("");
|
|
136
|
+
return allFiles.map((m) => m.value).filter((v) => v !== null && v.type === type);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Record goal violation scan result for a file
|
|
140
|
+
*/
|
|
141
|
+
recordViolation(filePath, goalId, goalDescription, found, details, confidence) {
|
|
142
|
+
const metadata = this.getFile(filePath);
|
|
143
|
+
if (!metadata) return;
|
|
144
|
+
metadata.lastScanned = Date.now();
|
|
145
|
+
if (!metadata.violations) metadata.violations = [];
|
|
146
|
+
metadata.violations = metadata.violations.filter((v) => v.goalId !== goalId);
|
|
147
|
+
metadata.violations.push({
|
|
148
|
+
goalId,
|
|
149
|
+
goalDescription,
|
|
150
|
+
found,
|
|
151
|
+
details,
|
|
152
|
+
confidence,
|
|
153
|
+
timestamp: Date.now()
|
|
154
|
+
});
|
|
155
|
+
this.trie.insert(filePath, metadata);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get cached violation results for a file
|
|
159
|
+
*/
|
|
160
|
+
getCachedViolations(filePath, goalId) {
|
|
161
|
+
const metadata = this.getFile(filePath);
|
|
162
|
+
if (!metadata || !metadata.violations) return void 0;
|
|
163
|
+
if (goalId) {
|
|
164
|
+
return metadata.violations.filter((v) => v.goalId === goalId);
|
|
165
|
+
}
|
|
166
|
+
return metadata.violations;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check if the index is empty (needs initial indexing)
|
|
170
|
+
*/
|
|
171
|
+
isEmpty() {
|
|
172
|
+
return this.trie.getWithPrefix("").length === 0;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get statistics about the index
|
|
176
|
+
*/
|
|
177
|
+
getStats() {
|
|
178
|
+
const allFiles = this.trie.getWithPrefix("").map((m) => m.value).filter((v) => v !== null);
|
|
179
|
+
const stats = {
|
|
180
|
+
totalFiles: allFiles.length,
|
|
181
|
+
totalSize: allFiles.reduce((sum, f) => sum + f.size, 0),
|
|
182
|
+
filesByType: {},
|
|
183
|
+
scannedFiles: allFiles.filter((f) => f.lastScanned).length,
|
|
184
|
+
filesWithViolations: allFiles.filter((f) => f.violations && f.violations.some((v) => v.found)).length
|
|
185
|
+
};
|
|
186
|
+
for (const file of allFiles) {
|
|
187
|
+
stats.filesByType[file.type] = (stats.filesByType[file.type] || 0) + 1;
|
|
188
|
+
}
|
|
189
|
+
return stats;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Clear stale cached results (older than maxAgeMs)
|
|
193
|
+
*/
|
|
194
|
+
clearStaleCache(maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
|
|
195
|
+
const allFiles = this.trie.getWithPrefix("").map((m) => m.value).filter((v) => v !== null);
|
|
196
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
197
|
+
let cleared = 0;
|
|
198
|
+
for (const file of allFiles) {
|
|
199
|
+
if (file.violations) {
|
|
200
|
+
const before = file.violations.length;
|
|
201
|
+
file.violations = file.violations.filter((v) => v.timestamp > cutoff);
|
|
202
|
+
cleared += before - file.violations.length;
|
|
203
|
+
if (file.violations.length === 0) {
|
|
204
|
+
file.lastScanned = void 0;
|
|
205
|
+
}
|
|
206
|
+
this.trie.insert(file.path, file);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return cleared;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export {
|
|
214
|
+
CodebaseIndex
|
|
215
|
+
};
|
|
216
|
+
//# sourceMappingURL=chunk-6VIMBFUZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/codebase-index.ts"],"sourcesContent":["/**\n * Codebase Index - Fast file indexing for goal violation detection\n * \n * Uses Trie structure for efficient file lookup and caching to avoid\n * re-scanning unchanged files.\n * \n * For large codebases, this dramatically improves goal check performance:\n * - Indexes file hashes to detect changes\n * - Caches AI analysis results\n * - Uses Trie for O(m) lookups where m = path length\n * - Stores file metadata (size, type, last modified, etc.)\n */\n\nimport { readFile, stat } from 'fs/promises';\nimport { createHash } from 'crypto';\nimport { join } from 'path';\nimport { existsSync } from 'fs';\nimport { Trie } from '../trie/trie.js';\nimport { getTrieDirectory } from '../utils/workspace.js';\nimport { atomicWriteJSON } from '../utils/atomic-write.js';\n\nexport interface FileMetadata {\n path: string;\n hash: string; // SHA-256 of content\n size: number;\n lastModified: number;\n type: string; // Extension\n lastScanned?: number; // Timestamp of last AI scan\n violations?: {\n goalId: string;\n goalDescription: string;\n found: boolean;\n details?: string;\n confidence?: number;\n timestamp: number;\n }[];\n}\n\nexport interface CodebaseIndexData {\n version: number;\n lastUpdated: string;\n fileCount: number;\n trie: any; // Serialized Trie\n}\n\nexport class CodebaseIndex {\n private trie: Trie<FileMetadata> = new Trie();\n private projectPath: string;\n private indexPath: string;\n\n constructor(projectPath: string) {\n this.projectPath = projectPath;\n this.indexPath = join(getTrieDirectory(projectPath), 'codebase-index.json');\n this.load();\n }\n\n /**\n * Load index from disk if it exists\n */\n private load(): void {\n if (!existsSync(this.indexPath)) return;\n\n try {\n const raw = JSON.parse(require('fs').readFileSync(this.indexPath, 'utf-8'));\n if (raw.trie) {\n this.trie = Trie.fromJSON<FileMetadata>(raw.trie);\n }\n } catch (error) {\n console.error('Failed to load codebase index:', error);\n }\n }\n\n /**\n * Save index to disk\n */\n async save(): Promise<void> {\n const data: CodebaseIndexData = {\n version: 1,\n lastUpdated: new Date().toISOString(),\n fileCount: this.trie.getWithPrefix('').length,\n trie: this.trie.toJSON(),\n };\n\n await atomicWriteJSON(this.indexPath, data);\n }\n\n /**\n * Add or update a file in the index\n * Returns null if the file doesn't exist or can't be read\n */\n async indexFile(filePath: string): Promise<FileMetadata | null> {\n // Safety check: if filePath is already absolute and doesn't start with projectPath,\n // this is likely a bug (stale cache entry from different project)\n if (filePath.startsWith('/')) {\n // Already absolute - check if it's within our project\n if (!filePath.toLowerCase().startsWith(this.projectPath.toLowerCase())) {\n return null; // Wrong project, skip silently\n }\n // It's absolute but within our project - use as-is\n }\n \n // Build absolute path: if filePath is relative, join with projectPath\n // If filePath is absolute (and within project), use it directly\n const absolutePath = filePath.startsWith('/') ? filePath : join(this.projectPath, filePath);\n \n try {\n const content = await readFile(absolutePath, 'utf-8');\n const stats = await stat(absolutePath);\n const hash = createHash('sha256').update(content).digest('hex');\n\n const metadata: FileMetadata = {\n path: filePath,\n hash,\n size: stats.size,\n lastModified: stats.mtimeMs,\n type: filePath.split('.').pop() || '',\n };\n\n // Check if file exists in index\n const existing = this.trie.search(filePath);\n if (existing.found && existing.value) {\n // File exists - preserve violations if hash matches\n if (existing.value.hash === hash) {\n metadata.violations = existing.value.violations;\n metadata.lastScanned = existing.value.lastScanned;\n }\n }\n\n this.trie.insert(filePath, metadata);\n return metadata;\n } catch (error) {\n // File doesn't exist, was deleted, or can't be read - return null\n return null;\n }\n }\n\n /**\n * Get file metadata from index\n */\n getFile(filePath: string): FileMetadata | null {\n const result = this.trie.search(filePath);\n return result.found && result.value ? result.value : null;\n }\n\n /**\n * Check if file has changed since last index\n */\n async hasChanged(filePath: string): Promise<boolean> {\n // Safety check for absolute paths from wrong project\n if (filePath.startsWith('/') && !filePath.toLowerCase().startsWith(this.projectPath.toLowerCase())) {\n return true; // Wrong project, treat as changed (will be skipped later)\n }\n \n const metadata = this.getFile(filePath);\n if (!metadata) return true; // Not indexed = changed\n\n // Build absolute path correctly\n const absolutePath = filePath.startsWith('/') ? filePath : join(this.projectPath, filePath);\n try {\n const stats = await stat(absolutePath);\n if (stats.mtimeMs !== metadata.lastModified) return true;\n\n // Double-check with hash for certainty\n const content = await readFile(absolutePath, 'utf-8');\n const hash = createHash('sha256').update(content).digest('hex');\n return hash !== metadata.hash;\n } catch {\n return true; // Error = assume changed\n }\n }\n\n /**\n * Get files in a directory\n */\n getDirectoryFiles(directoryPath: string): FileMetadata[] {\n const prefix = directoryPath.endsWith('/') ? directoryPath : `${directoryPath}/`;\n const matches = this.trie.getWithPrefix(prefix);\n return matches.map(m => m.value).filter((v): v is FileMetadata => v !== null);\n }\n\n /**\n * Get files that match a pattern (uses Trie prefix matching)\n */\n getFilesWithPrefix(prefix: string): FileMetadata[] {\n const matches = this.trie.getWithPrefix(prefix);\n return matches.map(m => m.value).filter((v): v is FileMetadata => v !== null);\n }\n\n /**\n * Get files by type (extension)\n */\n getFilesByType(type: string): FileMetadata[] {\n const allFiles = this.trie.getWithPrefix('');\n return allFiles\n .map(m => m.value)\n .filter((v): v is FileMetadata => v !== null && v.type === type);\n }\n\n /**\n * Record goal violation scan result for a file\n */\n recordViolation(\n filePath: string,\n goalId: string,\n goalDescription: string,\n found: boolean,\n details?: string,\n confidence?: number\n ): void {\n const metadata = this.getFile(filePath);\n if (!metadata) return;\n\n metadata.lastScanned = Date.now();\n if (!metadata.violations) metadata.violations = [];\n\n // Remove existing violation for this goal\n metadata.violations = metadata.violations.filter(v => v.goalId !== goalId);\n\n // Add new violation result\n metadata.violations.push({\n goalId,\n goalDescription,\n found,\n details,\n confidence,\n timestamp: Date.now(),\n });\n\n this.trie.insert(filePath, metadata);\n }\n\n /**\n * Get cached violation results for a file\n */\n getCachedViolations(filePath: string, goalId?: string): FileMetadata['violations'] {\n const metadata = this.getFile(filePath);\n if (!metadata || !metadata.violations) return undefined;\n\n if (goalId) {\n return metadata.violations.filter(v => v.goalId === goalId);\n }\n\n return metadata.violations;\n }\n\n /**\n * Check if the index is empty (needs initial indexing)\n */\n isEmpty(): boolean {\n return this.trie.getWithPrefix('').length === 0;\n }\n\n /**\n * Get statistics about the index\n */\n getStats(): {\n totalFiles: number;\n totalSize: number;\n filesByType: Record<string, number>;\n scannedFiles: number;\n filesWithViolations: number;\n } {\n const allFiles = this.trie.getWithPrefix('').map(m => m.value).filter((v): v is FileMetadata => v !== null);\n\n const stats = {\n totalFiles: allFiles.length,\n totalSize: allFiles.reduce((sum, f) => sum + f.size, 0),\n filesByType: {} as Record<string, number>,\n scannedFiles: allFiles.filter(f => f.lastScanned).length,\n filesWithViolations: allFiles.filter(f => f.violations && f.violations.some(v => v.found)).length,\n };\n\n for (const file of allFiles) {\n stats.filesByType[file.type] = (stats.filesByType[file.type] || 0) + 1;\n }\n\n return stats;\n }\n\n /**\n * Clear stale cached results (older than maxAgeMs)\n */\n clearStaleCache(maxAgeMs: number = 7 * 24 * 60 * 60 * 1000): number {\n const allFiles = this.trie.getWithPrefix('').map(m => m.value).filter((v): v is FileMetadata => v !== null);\n const cutoff = Date.now() - maxAgeMs;\n let cleared = 0;\n\n for (const file of allFiles) {\n if (file.violations) {\n const before = file.violations.length;\n file.violations = file.violations.filter(v => v.timestamp > cutoff);\n cleared += before - file.violations.length;\n if (file.violations.length === 0) {\n file.lastScanned = undefined;\n }\n this.trie.insert(file.path, file);\n }\n }\n\n return cleared;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -294,7 +294,7 @@ import { render } from "ink";
|
|
|
294
294
|
import React11 from "react";
|
|
295
295
|
|
|
296
296
|
// src/cli/dashboard/App.tsx
|
|
297
|
-
import { useState as
|
|
297
|
+
import { useState as useState3, useEffect as useEffect5, useCallback as useCallback7, useRef as useRef2 } from "react";
|
|
298
298
|
import { Box as Box14, useInput as useInput10, useApp } from "ink";
|
|
299
299
|
|
|
300
300
|
// src/cli/dashboard/state.tsx
|
|
@@ -667,6 +667,8 @@ function dashboardReducer(state, action) {
|
|
|
667
667
|
timestamp: Date.now()
|
|
668
668
|
};
|
|
669
669
|
if (action.toolCalls && action.toolCalls.length > 0) msg.toolCalls = action.toolCalls;
|
|
670
|
+
if (action.pendingFix) msg.pendingFix = action.pendingFix;
|
|
671
|
+
if (action.pendingFixes && action.pendingFixes.length > 0) msg.pendingFixes = action.pendingFixes;
|
|
670
672
|
return {
|
|
671
673
|
...state,
|
|
672
674
|
chatState: {
|
|
@@ -1884,8 +1886,15 @@ function GoalsView() {
|
|
|
1884
1886
|
const goal = goalsPanel.goals.find((g) => g.id === goalId);
|
|
1885
1887
|
if (!goal) return;
|
|
1886
1888
|
dispatch({ type: "ADD_ACTIVITY", message: `Checking goal: ${goal.description.slice(0, 30)}...` });
|
|
1887
|
-
|
|
1888
|
-
const
|
|
1889
|
+
const { CodebaseIndex } = await import("./codebase-index-CR6Q2HEI.js");
|
|
1890
|
+
const codebaseIndex = new CodebaseIndex(workDir);
|
|
1891
|
+
if (codebaseIndex.isEmpty()) {
|
|
1892
|
+
dispatch({ type: "SHOW_NOTIFICATION", message: `Building codebase index (one-time)... This will speed up future checks.`, severity: "info", autoHideMs: 1e4 });
|
|
1893
|
+
dispatch({ type: "ADD_ACTIVITY", message: `Building codebase index...` });
|
|
1894
|
+
} else {
|
|
1895
|
+
dispatch({ type: "SHOW_NOTIFICATION", message: `Scanning files...`, severity: "info", autoHideMs: 5e3 });
|
|
1896
|
+
}
|
|
1897
|
+
const { checkFilesForGoalViolations } = await import("./goal-validator-NLOJJ7FF.js");
|
|
1889
1898
|
const violations = await checkFilesForGoalViolations([goal], workDir);
|
|
1890
1899
|
if (violations.length === 0) {
|
|
1891
1900
|
dispatch({ type: "SHOW_NOTIFICATION", message: `\u2713 No violations found for: ${goal.description.slice(0, 40)}`, severity: "info", autoHideMs: 5e3 });
|
|
@@ -2086,7 +2095,14 @@ function HypothesesView() {
|
|
|
2086
2095
|
const hypo = hypothesesPanel.hypotheses.find((h) => h.id === hypoId);
|
|
2087
2096
|
if (!hypo) return;
|
|
2088
2097
|
dispatch({ type: "ADD_ACTIVITY", message: `Testing hypothesis: ${hypo.statement.slice(0, 30)}...` });
|
|
2089
|
-
|
|
2098
|
+
const { CodebaseIndex } = await import("./codebase-index-CR6Q2HEI.js");
|
|
2099
|
+
const codebaseIndex = new CodebaseIndex(workDir);
|
|
2100
|
+
if (codebaseIndex.isEmpty()) {
|
|
2101
|
+
dispatch({ type: "SHOW_NOTIFICATION", message: `Building codebase index (one-time)...`, severity: "info", autoHideMs: 8e3 });
|
|
2102
|
+
dispatch({ type: "ADD_ACTIVITY", message: `Building codebase index...` });
|
|
2103
|
+
} else {
|
|
2104
|
+
dispatch({ type: "SHOW_NOTIFICATION", message: `Gathering evidence for hypothesis...`, severity: "info", autoHideMs: 3e3 });
|
|
2105
|
+
}
|
|
2090
2106
|
const { gatherEvidenceForHypothesis } = await import("./hypothesis-HFYZNIMZ.js");
|
|
2091
2107
|
const evidence = await gatherEvidenceForHypothesis(hypoId, workDir);
|
|
2092
2108
|
if (evidence.length === 0) {
|
|
@@ -2435,7 +2451,7 @@ function RawLogView() {
|
|
|
2435
2451
|
}
|
|
2436
2452
|
|
|
2437
2453
|
// src/cli/dashboard/views/ChatView.tsx
|
|
2438
|
-
import { useCallback as useCallback5, useRef, useEffect as useEffect3 } from "react";
|
|
2454
|
+
import { useCallback as useCallback5, useRef, useEffect as useEffect3, useState as useState2 } from "react";
|
|
2439
2455
|
import { Box as Box12, Text as Text12, useInput as useInput8 } from "ink";
|
|
2440
2456
|
|
|
2441
2457
|
// src/tools/tell.ts
|
|
@@ -5247,7 +5263,8 @@ ${truncated}`;
|
|
|
5247
5263
|
case "trie_scan_for_goal_violations": {
|
|
5248
5264
|
const goalId = input.goalId ? String(input.goalId).trim() : void 0;
|
|
5249
5265
|
try {
|
|
5250
|
-
const { checkFilesForGoalViolations, getActiveGoals } = await import("./goal-validator-
|
|
5266
|
+
const { checkFilesForGoalViolations, getActiveGoals } = await import("./goal-validator-NLOJJ7FF.js");
|
|
5267
|
+
const { CodebaseIndex } = await import("./codebase-index-CR6Q2HEI.js");
|
|
5251
5268
|
const agentState = getGuardianState(directory);
|
|
5252
5269
|
await agentState.load();
|
|
5253
5270
|
const allGoals = await getActiveGoals(directory);
|
|
@@ -5255,11 +5272,13 @@ ${truncated}`;
|
|
|
5255
5272
|
if (goalsToCheck.length === 0) {
|
|
5256
5273
|
return goalId ? `No active goal found with ID: ${goalId}` : "No active goals to check. Add goals in the Goals view first.";
|
|
5257
5274
|
}
|
|
5275
|
+
const codebaseIndex = new CodebaseIndex(directory);
|
|
5276
|
+
const indexMessage = codebaseIndex.isEmpty() ? "\u{1F4CB} Building codebase index (one-time operation)...\n\n" : "";
|
|
5258
5277
|
const violations = await checkFilesForGoalViolations(goalsToCheck, directory);
|
|
5259
5278
|
if (violations.length === 0) {
|
|
5260
|
-
return
|
|
5279
|
+
return `${indexMessage}\u2713 Scan complete! No violations found for ${goalsToCheck.length} goal(s).`;
|
|
5261
5280
|
}
|
|
5262
|
-
let result =
|
|
5281
|
+
let result = `${indexMessage}Found ${violations.length} violation(s):
|
|
5263
5282
|
|
|
5264
5283
|
`;
|
|
5265
5284
|
for (const v of violations) {
|
|
@@ -5286,6 +5305,7 @@ ${truncated}`;
|
|
|
5286
5305
|
|
|
5287
5306
|
// src/cli/dashboard/views/ChatView.tsx
|
|
5288
5307
|
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
5308
|
+
var VISIBLE_MESSAGES = 8;
|
|
5289
5309
|
async function buildContext(workDir, dashboardState) {
|
|
5290
5310
|
const parts = [];
|
|
5291
5311
|
if (dashboardState?.agentInsights) {
|
|
@@ -5424,6 +5444,21 @@ function ChatView() {
|
|
|
5424
5444
|
const { chatState } = state;
|
|
5425
5445
|
const { messages, inputBuffer, loading, currentSessionId, currentSessionTitle } = chatState;
|
|
5426
5446
|
const loadingRef = useRef(false);
|
|
5447
|
+
const [cursorVisible, setCursorVisible] = useState2(true);
|
|
5448
|
+
const [scrollOffset, setScrollOffset] = useState2(0);
|
|
5449
|
+
useEffect3(() => {
|
|
5450
|
+
if (loading) {
|
|
5451
|
+
setCursorVisible(true);
|
|
5452
|
+
return;
|
|
5453
|
+
}
|
|
5454
|
+
const interval = setInterval(() => {
|
|
5455
|
+
setCursorVisible((v) => !v);
|
|
5456
|
+
}, 500);
|
|
5457
|
+
return () => clearInterval(interval);
|
|
5458
|
+
}, [loading]);
|
|
5459
|
+
useEffect3(() => {
|
|
5460
|
+
setScrollOffset(0);
|
|
5461
|
+
}, [messages.length]);
|
|
5427
5462
|
useEffect3(() => {
|
|
5428
5463
|
if (messages.length === 0) return;
|
|
5429
5464
|
const saveChat = async () => {
|
|
@@ -5454,47 +5489,82 @@ function ChatView() {
|
|
|
5454
5489
|
dispatch({ type: "SET_CHAT_LOADING", loading: true });
|
|
5455
5490
|
try {
|
|
5456
5491
|
const workDir = getWorkingDirectory(void 0, true);
|
|
5492
|
+
const response = question.trim().toLowerCase();
|
|
5493
|
+
const isYesToAll = response === "yes to all" || response === "y to all" || response === "yes all";
|
|
5494
|
+
const isYes = response === "yes" || response === "y";
|
|
5495
|
+
const isNo = response === "no" || response === "n";
|
|
5496
|
+
const collectPendingFixes = () => {
|
|
5497
|
+
const fixes = [];
|
|
5498
|
+
const recentAssistantMessages = messages.filter((m) => m.role === "assistant").slice(-5);
|
|
5499
|
+
for (const msg of recentAssistantMessages) {
|
|
5500
|
+
if (msg.pendingFixes && msg.pendingFixes.length > 0) {
|
|
5501
|
+
fixes.push(...msg.pendingFixes);
|
|
5502
|
+
} else if (msg.pendingFix) {
|
|
5503
|
+
fixes.push(msg.pendingFix);
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
return fixes;
|
|
5507
|
+
};
|
|
5508
|
+
const allPendingFixes = collectPendingFixes();
|
|
5457
5509
|
const lastAssistantMessage = messages.filter((m) => m.role === "assistant").pop();
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
if (
|
|
5510
|
+
const lastFixes = lastAssistantMessage?.pendingFixes || (lastAssistantMessage?.pendingFix ? [lastAssistantMessage.pendingFix] : []);
|
|
5511
|
+
if (allPendingFixes.length > 0 && (isYes || isYesToAll || isNo)) {
|
|
5512
|
+
if (isNo) {
|
|
5513
|
+
dispatch({
|
|
5514
|
+
type: "ADD_CHAT_MESSAGE",
|
|
5515
|
+
role: "assistant",
|
|
5516
|
+
content: "Fix cancelled. Let me know if you need anything else!"
|
|
5517
|
+
});
|
|
5518
|
+
dispatch({ type: "SET_CHAT_LOADING", loading: false });
|
|
5519
|
+
loadingRef.current = false;
|
|
5520
|
+
return;
|
|
5521
|
+
}
|
|
5522
|
+
const fixesToApply = isYesToAll ? allPendingFixes : lastFixes;
|
|
5523
|
+
if (fixesToApply.length > 0) {
|
|
5461
5524
|
const { spawnClaudeCodeFix } = await import("./terminal-spawn-P5M5PHAV.js");
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5525
|
+
const results = [];
|
|
5526
|
+
const errors = [];
|
|
5527
|
+
for (const fix of fixesToApply) {
|
|
5528
|
+
try {
|
|
5529
|
+
await spawnClaudeCodeFix({
|
|
5530
|
+
...fix,
|
|
5531
|
+
cwd: fix.directory
|
|
5532
|
+
});
|
|
5533
|
+
results.push(`"${fix.file}"`);
|
|
5534
|
+
} catch (error) {
|
|
5535
|
+
errors.push(`${fix.file}: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
let message = "";
|
|
5539
|
+
if (results.length > 0) {
|
|
5540
|
+
if (results.length === 1 && fixesToApply[0]) {
|
|
5541
|
+
const fix = fixesToApply[0];
|
|
5542
|
+
message = `Spawned Claude Code in a new terminal to fix ${results[0]}.
|
|
5471
5543
|
|
|
5472
5544
|
Claude Code will:
|
|
5473
5545
|
1. Review the file
|
|
5474
|
-
2. Understand the goal: "${
|
|
5475
|
-
3. Fix the violation: "${
|
|
5546
|
+
2. Understand the goal: "${fix.goal}"
|
|
5547
|
+
3. Fix the violation: "${fix.violation}"
|
|
5476
5548
|
4. Preserve all functionality
|
|
5477
5549
|
|
|
5478
|
-
Check the new terminal window to see the fix in progress
|
|
5479
|
-
}
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
return;
|
|
5550
|
+
Check the new terminal window to see the fix in progress.`;
|
|
5551
|
+
} else {
|
|
5552
|
+
message = `Spawned Claude Code to fix ${results.length} files:
|
|
5553
|
+
${results.map((r) => `- ${r}`).join("\n")}
|
|
5554
|
+
|
|
5555
|
+
Check the new terminal windows to see the fixes in progress.`;
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
if (errors.length > 0) {
|
|
5559
|
+
message += `
|
|
5560
|
+
|
|
5561
|
+
Failed to spawn fixes for:
|
|
5562
|
+
${errors.map((e) => `- ${e}`).join("\n")}`;
|
|
5492
5563
|
}
|
|
5493
|
-
} else if (response === "no" || response === "n") {
|
|
5494
5564
|
dispatch({
|
|
5495
5565
|
type: "ADD_CHAT_MESSAGE",
|
|
5496
5566
|
role: "assistant",
|
|
5497
|
-
content:
|
|
5567
|
+
content: message
|
|
5498
5568
|
});
|
|
5499
5569
|
dispatch({ type: "SET_CHAT_LOADING", loading: false });
|
|
5500
5570
|
loadingRef.current = false;
|
|
@@ -5524,15 +5594,28 @@ ${contextBlock}`;
|
|
|
5524
5594
|
role: "assistant",
|
|
5525
5595
|
content: result.content
|
|
5526
5596
|
};
|
|
5527
|
-
const
|
|
5528
|
-
|
|
5597
|
+
const pendingFixRegex = /\[PENDING_FIX:(.+?)\]/g;
|
|
5598
|
+
const pendingFixes = [];
|
|
5599
|
+
let match;
|
|
5600
|
+
while ((match = pendingFixRegex.exec(result.content)) !== null) {
|
|
5529
5601
|
try {
|
|
5530
|
-
const
|
|
5531
|
-
|
|
5532
|
-
|
|
5602
|
+
const jsonStr = match[1];
|
|
5603
|
+
if (jsonStr) {
|
|
5604
|
+
const fixData = JSON.parse(jsonStr);
|
|
5605
|
+
pendingFixes.push(fixData);
|
|
5606
|
+
}
|
|
5533
5607
|
} catch {
|
|
5534
5608
|
}
|
|
5535
5609
|
}
|
|
5610
|
+
if (pendingFixes.length > 0) {
|
|
5611
|
+
action.content = result.content.replace(/\[PENDING_FIX:.+?\]/g, "").trim();
|
|
5612
|
+
const firstFix = pendingFixes[0];
|
|
5613
|
+
if (pendingFixes.length === 1 && firstFix) {
|
|
5614
|
+
action.pendingFix = firstFix;
|
|
5615
|
+
} else {
|
|
5616
|
+
action.pendingFixes = pendingFixes;
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5536
5619
|
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
5537
5620
|
action.toolCalls = result.toolCalls;
|
|
5538
5621
|
const toolNames = new Set(result.toolCalls.map((tc) => tc.name));
|
|
@@ -5586,6 +5669,14 @@ ${contextBlock}`;
|
|
|
5586
5669
|
}, [dispatch, messages, state]);
|
|
5587
5670
|
useInput8((input, key) => {
|
|
5588
5671
|
if (loading) return;
|
|
5672
|
+
if (key.upArrow) {
|
|
5673
|
+
const maxScroll = Math.max(0, messages.length - VISIBLE_MESSAGES);
|
|
5674
|
+
setScrollOffset((prev) => Math.min(prev + 1, maxScroll));
|
|
5675
|
+
return;
|
|
5676
|
+
} else if (key.downArrow) {
|
|
5677
|
+
setScrollOffset((prev) => Math.max(prev - 1, 0));
|
|
5678
|
+
return;
|
|
5679
|
+
}
|
|
5589
5680
|
if (key.return && inputBuffer.trim().length > 0) {
|
|
5590
5681
|
void sendMessage(inputBuffer.trim());
|
|
5591
5682
|
} else if (key.escape) {
|
|
@@ -5602,18 +5693,34 @@ ${contextBlock}`;
|
|
|
5602
5693
|
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " AI is not available. Press s to open settings and add your Anthropic API key." })
|
|
5603
5694
|
] });
|
|
5604
5695
|
}
|
|
5605
|
-
|
|
5606
|
-
|
|
5696
|
+
const visibleMessages = (() => {
|
|
5697
|
+
if (messages.length <= VISIBLE_MESSAGES) return messages;
|
|
5698
|
+
const endIdx = messages.length - scrollOffset;
|
|
5699
|
+
const startIdx = Math.max(0, endIdx - VISIBLE_MESSAGES);
|
|
5700
|
+
return messages.slice(startIdx, endIdx);
|
|
5701
|
+
})();
|
|
5702
|
+
const canScrollUp = scrollOffset < messages.length - VISIBLE_MESSAGES;
|
|
5703
|
+
const canScrollDown = scrollOffset > 0;
|
|
5704
|
+
const cursor = cursorVisible ? "|" : " ";
|
|
5705
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, flexGrow: 1, flexShrink: 1, children: [
|
|
5706
|
+
/* @__PURE__ */ jsx13(Box12, { flexShrink: 0, children: /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
5607
5707
|
/* @__PURE__ */ jsx13(Text12, { bold: true, children: "Chat" }),
|
|
5608
5708
|
currentSessionTitle && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
5609
5709
|
" ",
|
|
5610
5710
|
currentSessionTitle
|
|
5611
5711
|
] }),
|
|
5612
|
-
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " (h for history)" })
|
|
5613
|
-
|
|
5614
|
-
|
|
5712
|
+
/* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " (h for history)" }),
|
|
5713
|
+
messages.length > VISIBLE_MESSAGES && /* @__PURE__ */ jsxs12(Text12, { dimColor: true, children: [
|
|
5714
|
+
" [",
|
|
5715
|
+
canScrollUp ? "\u2191" : " ",
|
|
5716
|
+
canScrollDown ? "\u2193" : " ",
|
|
5717
|
+
"]"
|
|
5718
|
+
] })
|
|
5719
|
+
] }) }),
|
|
5720
|
+
/* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", flexGrow: 1, flexShrink: 1, overflow: "hidden", marginTop: 1, marginBottom: 1, children: [
|
|
5615
5721
|
messages.length === 0 && !loading && /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " Ask about your codebase, decisions, patterns, or risks." }),
|
|
5616
|
-
|
|
5722
|
+
canScrollUp && /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " \u2191 more messages above" }),
|
|
5723
|
+
visibleMessages.map((msg, idx) => /* @__PURE__ */ jsx13(Box12, { flexDirection: "column", marginTop: idx === 0 ? 0 : 1, children: msg.role === "user" ? /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
5617
5724
|
" ",
|
|
5618
5725
|
/* @__PURE__ */ jsx13(Text12, { bold: true, color: "green", children: "You:" }),
|
|
5619
5726
|
" ",
|
|
@@ -5634,11 +5741,12 @@ ${contextBlock}`;
|
|
|
5634
5741
|
line
|
|
5635
5742
|
] }, li))
|
|
5636
5743
|
] }) }, idx)),
|
|
5744
|
+
canScrollDown && /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " \u2193 more messages below" }),
|
|
5637
5745
|
loading && /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: " Thinking..." })
|
|
5638
5746
|
] }),
|
|
5639
|
-
/* @__PURE__ */ jsx13(Box12, { borderStyle: "single", borderColor: "green", paddingX: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
5640
|
-
|
|
5641
|
-
/* @__PURE__ */ jsx13(Text12, {
|
|
5747
|
+
/* @__PURE__ */ jsx13(Box12, { borderStyle: "single", borderColor: "green", paddingX: 1, flexShrink: 0, flexGrow: 0, children: /* @__PURE__ */ jsxs12(Text12, { children: [
|
|
5748
|
+
/* @__PURE__ */ jsx13(Text12, { bold: true, color: "green", children: cursor }),
|
|
5749
|
+
inputBuffer || /* @__PURE__ */ jsx13(Text12, { dimColor: true, children: "Ask a question..." })
|
|
5642
5750
|
] }) })
|
|
5643
5751
|
] });
|
|
5644
5752
|
}
|
|
@@ -5903,7 +6011,7 @@ ${content}
|
|
|
5903
6011
|
fixedContent = fixedContent.replace(/^```\w*\n?/, "").replace(/\n?```$/, "");
|
|
5904
6012
|
}
|
|
5905
6013
|
await writeFile(fullPath, fixedContent, "utf-8");
|
|
5906
|
-
const { recordGoalViolationFixed, getActiveGoals } = await import("./goal-validator-
|
|
6014
|
+
const { recordGoalViolationFixed, getActiveGoals } = await import("./goal-validator-NLOJJ7FF.js");
|
|
5907
6015
|
const goals = await getActiveGoals(projectPath);
|
|
5908
6016
|
const matchedGoal = goals.find((g) => g.description === fix.goalDescription);
|
|
5909
6017
|
if (matchedGoal) {
|
|
@@ -5919,8 +6027,8 @@ ${content}
|
|
|
5919
6027
|
function DashboardApp({ onReady }) {
|
|
5920
6028
|
const { state, dispatch } = useDashboard();
|
|
5921
6029
|
const { exit } = useApp();
|
|
5922
|
-
const [showConfig, setShowConfig] =
|
|
5923
|
-
const [showHelp, setShowHelp] =
|
|
6030
|
+
const [showConfig, setShowConfig] = useState3(false);
|
|
6031
|
+
const [showHelp, setShowHelp] = useState3(false);
|
|
5924
6032
|
const dispatchRef = useRef2(dispatch);
|
|
5925
6033
|
dispatchRef.current = dispatch;
|
|
5926
6034
|
const stateRef = useRef2(state);
|
|
@@ -6265,4 +6373,4 @@ export {
|
|
|
6265
6373
|
handleCheckpointTool,
|
|
6266
6374
|
InteractiveDashboard
|
|
6267
6375
|
};
|
|
6268
|
-
//# sourceMappingURL=chunk-
|
|
6376
|
+
//# sourceMappingURL=chunk-WBNYLTW6.js.map
|