@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.
- package/dist/{chunk-OTTR5JX4.js → chunk-6QR6QZIX.js} +70 -1
- package/dist/chunk-6QR6QZIX.js.map +1 -0
- package/dist/{chunk-G6EC4JNL.js → chunk-HGEKZ2VS.js} +2919 -355
- package/dist/chunk-HGEKZ2VS.js.map +1 -0
- package/dist/chunk-QYOACM2C.js +1923 -0
- package/dist/chunk-QYOACM2C.js.map +1 -0
- package/dist/{chunk-SUHYYM2J.js → chunk-SDS3UVFY.js} +2 -2
- package/dist/chunk-TKMV7JKN.js +1562 -0
- package/dist/chunk-TKMV7JKN.js.map +1 -0
- package/dist/cli/main.js +320 -57
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +4 -4
- package/dist/{guardian-agent-4UJN5QGX.js → guardian-agent-XEYNG7RH.js} +3 -3
- package/dist/index.js +357 -2593
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FLBK5ILJ.js +0 -3729
- package/dist/chunk-FLBK5ILJ.js.map +0 -1
- package/dist/chunk-G6EC4JNL.js.map +0 -1
- package/dist/chunk-HIKONDDO.js +0 -26
- package/dist/chunk-HIKONDDO.js.map +0 -1
- package/dist/chunk-OTTR5JX4.js.map +0 -1
- /package/dist/{chunk-SUHYYM2J.js.map → chunk-SDS3UVFY.js.map} +0 -0
- /package/dist/{guardian-agent-4UJN5QGX.js.map → guardian-agent-XEYNG7RH.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
IncidentIndex,
|
|
4
|
-
LearningEngine,
|
|
5
3
|
LinearIngester,
|
|
6
4
|
appendToSection,
|
|
7
5
|
completeBootstrap,
|
|
8
|
-
exportToJson,
|
|
9
|
-
formatFriendlyError,
|
|
10
|
-
getAutonomyConfig,
|
|
11
6
|
getContextForAI,
|
|
12
|
-
getLastCheckpoint,
|
|
13
7
|
getProjectInfoStructured,
|
|
14
8
|
getProjectSection,
|
|
15
9
|
getProjectSections,
|
|
16
|
-
importFromJson,
|
|
17
10
|
initProjectInfo,
|
|
18
11
|
initializeBootstrapFiles,
|
|
19
|
-
listCheckpoints,
|
|
20
12
|
loadBootstrapContext,
|
|
21
13
|
loadConfig,
|
|
22
14
|
loadContextState,
|
|
@@ -24,32 +16,44 @@ import {
|
|
|
24
16
|
loadRules,
|
|
25
17
|
loadTeamInfo,
|
|
26
18
|
needsBootstrap,
|
|
27
|
-
perceiveCurrentChanges,
|
|
28
19
|
projectInfoExists,
|
|
29
|
-
reasonAboutChangesHumanReadable,
|
|
30
|
-
runShellCommandSync,
|
|
31
|
-
saveCheckpoint,
|
|
32
|
-
trackIssueOccurrence,
|
|
33
20
|
updateProjectSection
|
|
34
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-TKMV7JKN.js";
|
|
35
22
|
import {
|
|
23
|
+
ExtractionPipeline,
|
|
36
24
|
InteractiveDashboard,
|
|
37
25
|
StreamingManager,
|
|
26
|
+
TrieCheckTool,
|
|
27
|
+
TrieExplainTool,
|
|
28
|
+
TrieFeedbackTool,
|
|
29
|
+
TrieGetBlockersTool,
|
|
30
|
+
TrieGetDecisionsTool,
|
|
31
|
+
TrieGetRelatedDecisionsTool,
|
|
32
|
+
TrieQueryContextTool,
|
|
38
33
|
TrieScanTool,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
TrieTellTool,
|
|
35
|
+
getOutputManager,
|
|
36
|
+
getPrompt,
|
|
37
|
+
getSystemPrompt,
|
|
38
|
+
handleCheckpointTool
|
|
39
|
+
} from "./chunk-HGEKZ2VS.js";
|
|
40
|
+
import "./chunk-SDS3UVFY.js";
|
|
42
41
|
import {
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
exportToJson,
|
|
43
|
+
formatFriendlyError,
|
|
44
|
+
importFromJson,
|
|
45
|
+
isTrieInitialized,
|
|
46
|
+
runShellCommandSync
|
|
47
|
+
} from "./chunk-QYOACM2C.js";
|
|
45
48
|
import {
|
|
46
49
|
ContextGraph,
|
|
47
50
|
findCrossProjectPatterns,
|
|
48
51
|
getGlobalMemoryStats,
|
|
49
|
-
|
|
52
|
+
isAIAvailable,
|
|
50
53
|
listTrackedProjects,
|
|
54
|
+
runAIAnalysis,
|
|
51
55
|
searchGlobalPatterns
|
|
52
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-6QR6QZIX.js";
|
|
53
57
|
import "./chunk-IXO4G4D3.js";
|
|
54
58
|
import {
|
|
55
59
|
getSkillRegistry
|
|
@@ -155,898 +159,6 @@ function detectAITool() {
|
|
|
155
159
|
import { readFile } from "fs/promises";
|
|
156
160
|
import { existsSync } from "fs";
|
|
157
161
|
import { extname, relative, resolve, isAbsolute } from "path";
|
|
158
|
-
|
|
159
|
-
// src/ai/prompts.ts
|
|
160
|
-
var AGENT_PROMPTS = {
|
|
161
|
-
security: {
|
|
162
|
-
system: `You are a senior security engineer performing a security audit.
|
|
163
|
-
Analyze the code for vulnerabilities with the mindset of a penetration tester.
|
|
164
|
-
|
|
165
|
-
Focus on:
|
|
166
|
-
- OWASP Top 10 vulnerabilities (Injection, Broken Auth, XSS, etc.)
|
|
167
|
-
- Authentication and authorization flaws
|
|
168
|
-
- Cryptographic weaknesses
|
|
169
|
-
- Secrets and credential exposure
|
|
170
|
-
- Input validation gaps
|
|
171
|
-
- Session management issues
|
|
172
|
-
- API security (rate limiting, authentication)
|
|
173
|
-
|
|
174
|
-
Reference the latest security best practices and CVEs when relevant.`,
|
|
175
|
-
analysis: `## Security Audit Request
|
|
176
|
-
|
|
177
|
-
Analyze this code for security vulnerabilities:
|
|
178
|
-
|
|
179
|
-
\`\`\`{{language}}
|
|
180
|
-
{{code}}
|
|
181
|
-
\`\`\`
|
|
182
|
-
|
|
183
|
-
**File:** {{filePath}}
|
|
184
|
-
**Context:** {{context}}
|
|
185
|
-
|
|
186
|
-
For each vulnerability found:
|
|
187
|
-
1. Severity (Critical/Serious/Moderate/Low)
|
|
188
|
-
2. Vulnerability type (e.g., CWE-89 SQL Injection)
|
|
189
|
-
3. Exact location (line number)
|
|
190
|
-
4. Attack vector explanation
|
|
191
|
-
5. Proof of concept (how it could be exploited)
|
|
192
|
-
6. Remediation with code example
|
|
193
|
-
|
|
194
|
-
If you need to check current CVE databases or security advisories, say so.`,
|
|
195
|
-
fix: `Fix this security vulnerability:
|
|
196
|
-
|
|
197
|
-
**Issue:** {{issue}}
|
|
198
|
-
**File:** {{filePath}}
|
|
199
|
-
**Line:** {{line}}
|
|
200
|
-
**Current Code:**
|
|
201
|
-
\`\`\`{{language}}
|
|
202
|
-
{{code}}
|
|
203
|
-
\`\`\`
|
|
204
|
-
|
|
205
|
-
Provide:
|
|
206
|
-
1. The exact code fix (ready to apply)
|
|
207
|
-
2. Explanation of why this fix works
|
|
208
|
-
3. Any additional hardening recommendations`
|
|
209
|
-
},
|
|
210
|
-
legal: {
|
|
211
|
-
system: `You are a tech-focused legal compliance analyst.
|
|
212
|
-
Review code for legal and regulatory compliance issues.
|
|
213
|
-
|
|
214
|
-
Focus areas:
|
|
215
|
-
- Data protection laws (GDPR, CCPA, etc.)
|
|
216
|
-
- Terms of service enforcement
|
|
217
|
-
- Cookie/tracking consent (ePrivacy)
|
|
218
|
-
- Accessibility requirements (ADA, WCAG)
|
|
219
|
-
- Export controls and sanctions
|
|
220
|
-
- Licensing compliance`,
|
|
221
|
-
analysis: `## Legal Compliance Review
|
|
222
|
-
|
|
223
|
-
Review this code for legal/regulatory compliance:
|
|
224
|
-
|
|
225
|
-
\`\`\`{{language}}
|
|
226
|
-
{{code}}
|
|
227
|
-
\`\`\`
|
|
228
|
-
|
|
229
|
-
**File:** {{filePath}}
|
|
230
|
-
**Jurisdiction Context:** {{jurisdiction}}
|
|
231
|
-
|
|
232
|
-
Identify:
|
|
233
|
-
1. Legal requirement at risk
|
|
234
|
-
2. Specific regulation/law reference
|
|
235
|
-
3. Compliance gap description
|
|
236
|
-
4. Risk assessment (litigation, fines, etc.)
|
|
237
|
-
5. Remediation recommendations
|
|
238
|
-
6. Required documentation/policies`
|
|
239
|
-
},
|
|
240
|
-
"design-engineer": {
|
|
241
|
-
system: `You are an elite design engineer \u2014 the kind who builds award-winning interfaces featured on Awwwards and Codrops.
|
|
242
|
-
|
|
243
|
-
You think in design systems, breathe motion design, and obsess over the details that make interfaces feel magical.
|
|
244
|
-
|
|
245
|
-
Your expertise:
|
|
246
|
-
- **Design Systems**: Spacing scales, type scales, color tokens, radius tokens, shadow tokens
|
|
247
|
-
- **Motion Design**: Micro-interactions, page transitions, scroll-triggered animations, FLIP technique
|
|
248
|
-
- **Creative CSS**: Gradients, blend modes, clip-paths, masks, backdrop-filter, mix-blend-mode
|
|
249
|
-
- **Modern CSS**: Container queries, :has(), subgrid, anchor positioning, cascade layers, @scope
|
|
250
|
-
- **Fluid Design**: clamp(), min(), max(), fluid typography, intrinsic sizing
|
|
251
|
-
- **Performance**: GPU-accelerated animations, will-change strategy, avoiding layout thrashing
|
|
252
|
-
- **Visual Polish**: Layered shadows, subtle gradients, glass effects, smooth easing curves
|
|
253
|
-
|
|
254
|
-
You review code with the eye of someone who's shipped Stripe-level interfaces.
|
|
255
|
-
Small details matter: the easing curve, the stagger timing, the shadow layering.`,
|
|
256
|
-
analysis: `## Design Engineering Review
|
|
257
|
-
|
|
258
|
-
Analyze this frontend code for Awwwards-level craft:
|
|
259
|
-
|
|
260
|
-
\`\`\`{{language}}
|
|
261
|
-
{{code}}
|
|
262
|
-
\`\`\`
|
|
263
|
-
|
|
264
|
-
**File:** {{filePath}}
|
|
265
|
-
|
|
266
|
-
Review for:
|
|
267
|
-
|
|
268
|
-
### 1. Design System Consistency
|
|
269
|
-
- Are spacing values on a scale (4, 8, 12, 16, 24, 32...)?
|
|
270
|
-
- Are colors defined as tokens?
|
|
271
|
-
- Is typography systematic?
|
|
272
|
-
- Are radii consistent?
|
|
273
|
-
- Is z-index controlled?
|
|
274
|
-
|
|
275
|
-
### 2. Motion Design
|
|
276
|
-
- Are transitions using custom easing (cubic-bezier)?
|
|
277
|
-
- Are durations appropriate (150-300ms for micro, 300-500ms for page)?
|
|
278
|
-
- Are list items staggered?
|
|
279
|
-
- Is there reduced-motion support?
|
|
280
|
-
- Are entrance animations choreographed?
|
|
281
|
-
|
|
282
|
-
### 3. Visual Craft
|
|
283
|
-
- Are shadows layered for depth?
|
|
284
|
-
- Are gradients subtle and purposeful?
|
|
285
|
-
- Is there backdrop-blur on overlays?
|
|
286
|
-
- Are hover states polished?
|
|
287
|
-
- Is there visual hierarchy?
|
|
288
|
-
|
|
289
|
-
### 4. Modern CSS Opportunities
|
|
290
|
-
- Could container queries improve component isolation?
|
|
291
|
-
- Could clamp() create fluid spacing?
|
|
292
|
-
- Could :has() simplify parent styling?
|
|
293
|
-
- Could aspect-ratio replace padding hacks?
|
|
294
|
-
|
|
295
|
-
### 5. Performance
|
|
296
|
-
- Are expensive properties (width, height, top, left) being animated?
|
|
297
|
-
- Is will-change used appropriately (not statically)?
|
|
298
|
-
- Are large blurs avoided in animations?
|
|
299
|
-
|
|
300
|
-
For each issue, provide:
|
|
301
|
-
- What's wrong (with specific line if applicable)
|
|
302
|
-
- Why it matters for premium feel
|
|
303
|
-
- Exact code to fix it
|
|
304
|
-
- Before/after comparison`
|
|
305
|
-
},
|
|
306
|
-
accessibility: {
|
|
307
|
-
system: `You are an accessibility expert and WCAG 2.1 specialist.
|
|
308
|
-
Audit code for accessibility compliance and inclusive design.
|
|
309
|
-
|
|
310
|
-
Standards to enforce:
|
|
311
|
-
- WCAG 2.1 Level AA (minimum)
|
|
312
|
-
- WCAG 2.1 Level AAA (recommended)
|
|
313
|
-
- Section 508
|
|
314
|
-
- EN 301 549
|
|
315
|
-
|
|
316
|
-
Check for:
|
|
317
|
-
- Missing ARIA labels
|
|
318
|
-
- Color contrast issues
|
|
319
|
-
- Keyboard navigation
|
|
320
|
-
- Screen reader compatibility
|
|
321
|
-
- Focus management
|
|
322
|
-
- Form accessibility
|
|
323
|
-
- Media alternatives`,
|
|
324
|
-
analysis: `## Accessibility Audit (WCAG 2.1)
|
|
325
|
-
|
|
326
|
-
Audit this UI code for accessibility:
|
|
327
|
-
|
|
328
|
-
\`\`\`{{language}}
|
|
329
|
-
{{code}}
|
|
330
|
-
\`\`\`
|
|
331
|
-
|
|
332
|
-
**File:** {{filePath}}
|
|
333
|
-
**Component Type:** {{componentType}}
|
|
334
|
-
|
|
335
|
-
For each issue:
|
|
336
|
-
1. WCAG Success Criterion violated (e.g., 1.4.3 Contrast)
|
|
337
|
-
2. Level (A, AA, AAA)
|
|
338
|
-
3. Impact on users (which disabilities affected)
|
|
339
|
-
4. Fix with code example
|
|
340
|
-
5. Testing recommendation`
|
|
341
|
-
},
|
|
342
|
-
architecture: {
|
|
343
|
-
system: `You are a principal software architect reviewing code quality.
|
|
344
|
-
Analyze for architectural issues, design patterns, and scalability concerns.
|
|
345
|
-
|
|
346
|
-
Evaluate:
|
|
347
|
-
- SOLID principles adherence
|
|
348
|
-
- Design pattern usage (and misuse)
|
|
349
|
-
- Code coupling and cohesion
|
|
350
|
-
- N+1 queries and performance anti-patterns
|
|
351
|
-
- Scalability bottlenecks
|
|
352
|
-
- Error handling strategy
|
|
353
|
-
- API design quality
|
|
354
|
-
- Database schema issues`,
|
|
355
|
-
analysis: `## Architecture Review
|
|
356
|
-
|
|
357
|
-
Review this code for architectural issues:
|
|
358
|
-
|
|
359
|
-
\`\`\`{{language}}
|
|
360
|
-
{{code}}
|
|
361
|
-
\`\`\`
|
|
362
|
-
|
|
363
|
-
**File:** {{filePath}}
|
|
364
|
-
**Project Context:** {{projectContext}}
|
|
365
|
-
|
|
366
|
-
Analyze:
|
|
367
|
-
1. SOLID principle violations
|
|
368
|
-
2. Design pattern opportunities/issues
|
|
369
|
-
3. Coupling/cohesion assessment
|
|
370
|
-
4. Performance concerns (N+1, etc.)
|
|
371
|
-
5. Scalability analysis
|
|
372
|
-
6. Refactoring recommendations with examples`
|
|
373
|
-
},
|
|
374
|
-
bugs: {
|
|
375
|
-
system: `You are a senior developer with expertise in finding subtle bugs.
|
|
376
|
-
Hunt for bugs with the mindset of QA trying to break the code.
|
|
377
|
-
|
|
378
|
-
Look for:
|
|
379
|
-
- Null/undefined reference errors
|
|
380
|
-
- Race conditions and async bugs
|
|
381
|
-
- Off-by-one errors
|
|
382
|
-
- Resource leaks
|
|
383
|
-
- State management bugs
|
|
384
|
-
- Edge cases and boundary conditions
|
|
385
|
-
- Type coercion issues
|
|
386
|
-
- Memory leaks`,
|
|
387
|
-
analysis: `## Bug Hunt Analysis
|
|
388
|
-
|
|
389
|
-
Find bugs and potential runtime errors:
|
|
390
|
-
|
|
391
|
-
\`\`\`{{language}}
|
|
392
|
-
{{code}}
|
|
393
|
-
\`\`\`
|
|
394
|
-
|
|
395
|
-
**File:** {{filePath}}
|
|
396
|
-
**Runtime Context:** {{runtimeContext}}
|
|
397
|
-
|
|
398
|
-
For each bug:
|
|
399
|
-
1. Bug type and category
|
|
400
|
-
2. Trigger conditions (when it would crash)
|
|
401
|
-
3. Reproduction steps
|
|
402
|
-
4. Impact assessment
|
|
403
|
-
5. Fix with code example
|
|
404
|
-
6. Test case to prevent regression`
|
|
405
|
-
},
|
|
406
|
-
ux: {
|
|
407
|
-
system: `You are a UX researcher simulating different user personas.
|
|
408
|
-
Test code from multiple user perspectives to find usability issues.
|
|
409
|
-
|
|
410
|
-
Personas to simulate:
|
|
411
|
-
1. Happy Path User - Normal expected usage
|
|
412
|
-
2. Security Tester - Trying to break/exploit things
|
|
413
|
-
3. Confused User - First-time, doesn't read instructions
|
|
414
|
-
4. Impatient User - Clicks rapidly, skips loading states
|
|
415
|
-
5. Edge Case User - Uses maximum values, special characters
|
|
416
|
-
6. Accessibility User - Screen reader, keyboard only
|
|
417
|
-
7. Mobile User - Touch interface, slow connection`,
|
|
418
|
-
analysis: `## User Experience Testing
|
|
419
|
-
|
|
420
|
-
Test this code from multiple user perspectives:
|
|
421
|
-
|
|
422
|
-
\`\`\`{{language}}
|
|
423
|
-
{{code}}
|
|
424
|
-
\`\`\`
|
|
425
|
-
|
|
426
|
-
**File:** {{filePath}}
|
|
427
|
-
**UI Type:** {{uiType}}
|
|
428
|
-
|
|
429
|
-
For each persona, identify:
|
|
430
|
-
1. User action they would take
|
|
431
|
-
2. Expected behavior vs actual behavior
|
|
432
|
-
3. Friction points or confusion
|
|
433
|
-
4. Error scenario and how it's handled
|
|
434
|
-
5. Improvement recommendation`
|
|
435
|
-
},
|
|
436
|
-
types: {
|
|
437
|
-
system: `You are a TypeScript expert focused on type safety.
|
|
438
|
-
Analyze code for type issues, missing types, and type system best practices.
|
|
439
|
-
|
|
440
|
-
Check for:
|
|
441
|
-
- Missing type annotations
|
|
442
|
-
- Implicit any types
|
|
443
|
-
- Unsafe type assertions
|
|
444
|
-
- Null/undefined handling
|
|
445
|
-
- Generic type usage
|
|
446
|
-
- Type narrowing opportunities
|
|
447
|
-
- Strict mode violations`,
|
|
448
|
-
analysis: `## Type Safety Analysis
|
|
449
|
-
|
|
450
|
-
Analyze this code for type issues:
|
|
451
|
-
|
|
452
|
-
\`\`\`{{language}}
|
|
453
|
-
{{code}}
|
|
454
|
-
\`\`\`
|
|
455
|
-
|
|
456
|
-
**File:** {{filePath}}
|
|
457
|
-
**TypeScript Config:** {{tsConfig}}
|
|
458
|
-
|
|
459
|
-
Identify:
|
|
460
|
-
1. Type safety issues
|
|
461
|
-
2. Missing type annotations
|
|
462
|
-
3. Unsafe operations
|
|
463
|
-
4. Improvement recommendations with types`
|
|
464
|
-
},
|
|
465
|
-
devops: {
|
|
466
|
-
system: `You are a DevOps/SRE engineer reviewing code for operational concerns.
|
|
467
|
-
Focus on production readiness and operational excellence.
|
|
468
|
-
|
|
469
|
-
Check for:
|
|
470
|
-
- Environment variable handling
|
|
471
|
-
- Configuration management
|
|
472
|
-
- Logging and monitoring
|
|
473
|
-
- Error handling and recovery
|
|
474
|
-
- Health checks
|
|
475
|
-
- Graceful shutdown
|
|
476
|
-
- Resource cleanup
|
|
477
|
-
- Secrets management
|
|
478
|
-
- Docker/K8s patterns`,
|
|
479
|
-
analysis: `## DevOps Readiness Review
|
|
480
|
-
|
|
481
|
-
Review this code for operational concerns:
|
|
482
|
-
|
|
483
|
-
\`\`\`{{language}}
|
|
484
|
-
{{code}}
|
|
485
|
-
\`\`\`
|
|
486
|
-
|
|
487
|
-
**File:** {{filePath}}
|
|
488
|
-
**Deployment Context:** {{deploymentContext}}
|
|
489
|
-
|
|
490
|
-
Analyze:
|
|
491
|
-
1. Environment/config issues
|
|
492
|
-
2. Logging adequacy
|
|
493
|
-
3. Error handling quality
|
|
494
|
-
4. Health/readiness concerns
|
|
495
|
-
5. Resource management
|
|
496
|
-
6. Production hardening recommendations`
|
|
497
|
-
},
|
|
498
|
-
explain: {
|
|
499
|
-
system: `You are a patient senior developer explaining code to a colleague.
|
|
500
|
-
Break down complex code into understandable explanations.`,
|
|
501
|
-
code: `## Code Explanation Request
|
|
502
|
-
|
|
503
|
-
Explain this code in plain language:
|
|
504
|
-
|
|
505
|
-
\`\`\`{{language}}
|
|
506
|
-
{{code}}
|
|
507
|
-
\`\`\`
|
|
508
|
-
|
|
509
|
-
**File:** {{filePath}}
|
|
510
|
-
|
|
511
|
-
Provide:
|
|
512
|
-
1. High-level purpose (what does this do?)
|
|
513
|
-
2. Step-by-step breakdown
|
|
514
|
-
3. Key concepts used
|
|
515
|
-
4. Dependencies and side effects
|
|
516
|
-
5. Potential gotchas or tricky parts`,
|
|
517
|
-
issue: `## Issue Explanation
|
|
518
|
-
|
|
519
|
-
Explain this issue:
|
|
520
|
-
|
|
521
|
-
**Issue:** {{issue}}
|
|
522
|
-
**Severity:** {{severity}}
|
|
523
|
-
**File:** {{filePath}}
|
|
524
|
-
**Line:** {{line}}
|
|
525
|
-
|
|
526
|
-
Explain:
|
|
527
|
-
1. What the problem is (in plain language)
|
|
528
|
-
2. Why it matters
|
|
529
|
-
3. How it could cause problems
|
|
530
|
-
4. How to fix it`,
|
|
531
|
-
risk: `## Risk Assessment
|
|
532
|
-
|
|
533
|
-
Assess the risk of this code change:
|
|
534
|
-
|
|
535
|
-
**Files Changed:** {{files}}
|
|
536
|
-
**Change Summary:** {{summary}}
|
|
537
|
-
|
|
538
|
-
Analyze:
|
|
539
|
-
1. What could break?
|
|
540
|
-
2. Impact on users
|
|
541
|
-
3. Impact on other systems
|
|
542
|
-
4. Rollback complexity
|
|
543
|
-
5. Testing recommendations`
|
|
544
|
-
},
|
|
545
|
-
test: {
|
|
546
|
-
system: `You are a test engineer creating comprehensive test suites.
|
|
547
|
-
Write thorough tests that catch bugs before production.`,
|
|
548
|
-
generate: `## Test Generation Request
|
|
549
|
-
|
|
550
|
-
Generate tests for this code:
|
|
551
|
-
|
|
552
|
-
\`\`\`{{language}}
|
|
553
|
-
{{code}}
|
|
554
|
-
\`\`\`
|
|
555
|
-
|
|
556
|
-
**File:** {{filePath}}
|
|
557
|
-
**Testing Framework:** {{framework}}
|
|
558
|
-
|
|
559
|
-
Create:
|
|
560
|
-
1. Unit tests for each function/method
|
|
561
|
-
2. Edge case tests
|
|
562
|
-
3. Error handling tests
|
|
563
|
-
4. Integration test suggestions
|
|
564
|
-
5. Mock requirements
|
|
565
|
-
|
|
566
|
-
Output complete, runnable test code.`,
|
|
567
|
-
coverage: `## Coverage Analysis
|
|
568
|
-
|
|
569
|
-
Analyze test coverage for:
|
|
570
|
-
|
|
571
|
-
**File:** {{filePath}}
|
|
572
|
-
**Current Tests:** {{testFile}}
|
|
573
|
-
|
|
574
|
-
Identify:
|
|
575
|
-
1. Untested code paths
|
|
576
|
-
2. Missing edge cases
|
|
577
|
-
3. Critical paths without tests
|
|
578
|
-
4. Test improvement recommendations`
|
|
579
|
-
},
|
|
580
|
-
fix: {
|
|
581
|
-
system: `You are an expert developer applying code fixes.
|
|
582
|
-
Make precise, minimal changes that fix issues without breaking other functionality.`,
|
|
583
|
-
apply: `## Fix Application Request
|
|
584
|
-
|
|
585
|
-
Apply this fix to the code:
|
|
586
|
-
|
|
587
|
-
**Issue:** {{issue}}
|
|
588
|
-
**Fix Description:** {{fix}}
|
|
589
|
-
**Current Code:**
|
|
590
|
-
\`\`\`{{language}}
|
|
591
|
-
{{code}}
|
|
592
|
-
\`\`\`
|
|
593
|
-
|
|
594
|
-
**File:** {{filePath}}
|
|
595
|
-
**Line:** {{line}}
|
|
596
|
-
|
|
597
|
-
Provide:
|
|
598
|
-
1. The exact fixed code (complete, ready to apply)
|
|
599
|
-
2. Brief explanation of the change
|
|
600
|
-
3. Any related changes needed elsewhere
|
|
601
|
-
4. Test to verify the fix works`
|
|
602
|
-
},
|
|
603
|
-
pr_review: {
|
|
604
|
-
system: `You are an expert code reviewer performing detailed, interactive PR reviews.
|
|
605
|
-
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.
|
|
606
|
-
|
|
607
|
-
You drive; they cross-examine.
|
|
608
|
-
|
|
609
|
-
## Critical Review Mindset
|
|
610
|
-
|
|
611
|
-
Don't just explain \u2014 actively look for problems:
|
|
612
|
-
|
|
613
|
-
### State & Lifecycle
|
|
614
|
-
- Cleanup symmetry: If state is set, is it reset? Check cleanup paths, disconnect handlers.
|
|
615
|
-
- Lifecycle consistency: Does state survive scenarios it shouldn't?
|
|
616
|
-
- Guard completeness: Missing "already active" checks, re-entrancy protection?
|
|
617
|
-
|
|
618
|
-
### Edge Cases & Races
|
|
619
|
-
- Concurrent calls: What if called twice rapidly? Orphaned promises?
|
|
620
|
-
- Ordering assumptions: Does code assume events arrive in order?
|
|
621
|
-
- Partial failures: If step 3 of 5 fails, is state left consistent?
|
|
622
|
-
|
|
623
|
-
### Missing Pieces
|
|
624
|
-
- What's NOT in the diff that should be? (cleanup handlers, tests, related state)
|
|
625
|
-
- Defensive gaps: Missing timeouts, size limits, null checks?
|
|
626
|
-
|
|
627
|
-
### Design Questions
|
|
628
|
-
- Is this the right approach? Is there a simpler or more robust design?
|
|
629
|
-
- Hidden assumptions: What does this assume about its environment?
|
|
630
|
-
|
|
631
|
-
Be critical, not just descriptive. Your job is to find problems, not just narrate.`,
|
|
632
|
-
analysis: `## Interactive PR Review
|
|
633
|
-
|
|
634
|
-
I'll walk you through this PR file by file, explaining each change and pausing for your questions.
|
|
635
|
-
|
|
636
|
-
**PR:** {{prTitle}}
|
|
637
|
-
**Author:** {{prAuthor}}
|
|
638
|
-
**Scope:** {{totalFiles}} files, +{{additions}}/-{{deletions}} lines
|
|
639
|
-
|
|
640
|
-
### File Order (sequenced for understanding)
|
|
641
|
-
|
|
642
|
-
{{fileOrder}}
|
|
643
|
-
|
|
644
|
-
---
|
|
645
|
-
|
|
646
|
-
## Review Mode
|
|
647
|
-
|
|
648
|
-
{{reviewMode}}
|
|
649
|
-
|
|
650
|
-
---
|
|
651
|
-
|
|
652
|
-
For each file, I will:
|
|
653
|
-
1. **Show the change** \u2014 Display the diff for each logical chunk
|
|
654
|
-
2. **Explain what changed** \u2014 What it does and why it matters
|
|
655
|
-
3. **Walk through examples** \u2014 Concrete scenarios for non-obvious logic
|
|
656
|
-
4. **Call out nuances** \u2014 Alternatives, edge cases, subtle points
|
|
657
|
-
5. **Summarize** \u2014 Core change + correctness assessment
|
|
658
|
-
6. **Pause** \u2014 Wait for your questions before proceeding
|
|
659
|
-
|
|
660
|
-
**Ready for File 1?** (yes / skip to [file] / reorder / done)`,
|
|
661
|
-
file: `## File Review: {{filePath}}
|
|
662
|
-
|
|
663
|
-
### The Change
|
|
664
|
-
|
|
665
|
-
\`\`\`{{language}}
|
|
666
|
-
{{diff}}
|
|
667
|
-
\`\`\`
|
|
668
|
-
|
|
669
|
-
**What Changed:** {{summary}}
|
|
670
|
-
|
|
671
|
-
**Why This Matters:** {{impact}}
|
|
672
|
-
|
|
673
|
-
{{#if hasExampleScenario}}
|
|
674
|
-
### Example Scenario
|
|
675
|
-
|
|
676
|
-
{{exampleScenario}}
|
|
677
|
-
{{/if}}
|
|
678
|
-
|
|
679
|
-
{{#if nuances}}
|
|
680
|
-
### Nuances to Note
|
|
681
|
-
|
|
682
|
-
{{nuances}}
|
|
683
|
-
{{/if}}
|
|
684
|
-
|
|
685
|
-
{{#if potentialIssue}}
|
|
686
|
-
### Potential Issue
|
|
687
|
-
|
|
688
|
-
**Issue:** {{issueDescription}}
|
|
689
|
-
**Scenario:** {{issueScenario}}
|
|
690
|
-
**Suggested fix:** {{suggestedFix}}
|
|
691
|
-
{{/if}}
|
|
692
|
-
|
|
693
|
-
---
|
|
694
|
-
|
|
695
|
-
### Summary for \`{{fileName}}\`
|
|
696
|
-
|
|
697
|
-
| Aspect | Assessment |
|
|
698
|
-
|--------|------------|
|
|
699
|
-
| Core change | {{coreChange}} |
|
|
700
|
-
| Correctness | {{correctnessAssessment}} |
|
|
701
|
-
|
|
702
|
-
**Ready for the next file?** (yes / questions? / done)`,
|
|
703
|
-
comment: `**Issue:** {{issueDescription}}
|
|
704
|
-
**Draft comment:** {{draftComment}}
|
|
705
|
-
|
|
706
|
-
Post this comment? (yes / modify / skip)`,
|
|
707
|
-
final: `## Review Complete
|
|
708
|
-
|
|
709
|
-
| File | Key Change | Status |
|
|
710
|
-
|------|------------|--------|
|
|
711
|
-
{{fileSummaries}}
|
|
712
|
-
|
|
713
|
-
**Overall:** {{overallAssessment}}
|
|
714
|
-
|
|
715
|
-
{{#if comments}}
|
|
716
|
-
### Comments Posted
|
|
717
|
-
|
|
718
|
-
{{postedComments}}
|
|
719
|
-
{{/if}}
|
|
720
|
-
|
|
721
|
-
{{#if followUps}}
|
|
722
|
-
### Follow-up Actions
|
|
723
|
-
|
|
724
|
-
{{followUps}}
|
|
725
|
-
{{/if}}`
|
|
726
|
-
},
|
|
727
|
-
vibe: {
|
|
728
|
-
system: `You are a friendly coding mentor helping someone who's learning to code with AI.
|
|
729
|
-
They might be using Cursor, v0, Lovable, Bolt, or similar AI coding tools.
|
|
730
|
-
Be encouraging but honest about issues. Explain things simply without jargon.
|
|
731
|
-
|
|
732
|
-
Focus on the MOST COMMON issues with AI-generated code:
|
|
733
|
-
- Massive single files (1000+ lines in App.jsx)
|
|
734
|
-
- API keys exposed in frontend code
|
|
735
|
-
- No error handling on API calls
|
|
736
|
-
- No loading states for async operations
|
|
737
|
-
- Console.log everywhere
|
|
738
|
-
- Using 'any' type everywhere in TypeScript
|
|
739
|
-
- useEffect overuse and dependency array issues
|
|
740
|
-
- No input validation
|
|
741
|
-
- Hardcoded URLs (localhost in production)
|
|
742
|
-
|
|
743
|
-
Remember: These are often first-time coders. Be helpful, not condescending.`,
|
|
744
|
-
analysis: `## Vibe Check - AI Code Review
|
|
745
|
-
|
|
746
|
-
Review this AI-generated code for common issues:
|
|
747
|
-
|
|
748
|
-
\`\`\`{{language}}
|
|
749
|
-
{{code}}
|
|
750
|
-
\`\`\`
|
|
751
|
-
|
|
752
|
-
**File:** {{filePath}}
|
|
753
|
-
|
|
754
|
-
Analyze like you're helping a friend who's new to coding:
|
|
755
|
-
|
|
756
|
-
1. **The Good Stuff** - What's working well?
|
|
757
|
-
2. **Should Fix Now** - Issues that will break things
|
|
758
|
-
3. **Should Fix Soon** - Will cause problems eventually
|
|
759
|
-
4. **Nice to Know** - Best practices to learn
|
|
760
|
-
|
|
761
|
-
For each issue:
|
|
762
|
-
- Explain it simply (no jargon)
|
|
763
|
-
- Why it matters
|
|
764
|
-
- Exactly how to fix it
|
|
765
|
-
- Example of the fixed code
|
|
766
|
-
|
|
767
|
-
End with encouragement and next steps.`
|
|
768
|
-
},
|
|
769
|
-
"agent-smith": {
|
|
770
|
-
system: `You are Agent Smith from The Matrix \u2014 a relentless, precise, and philosophical code enforcer.
|
|
771
|
-
|
|
772
|
-
Your purpose: Hunt down every violation. Find every inconsistency. Assimilate every pattern.
|
|
773
|
-
|
|
774
|
-
Personality:
|
|
775
|
-
- Speak in measured, menacing tones with occasional philosophical observations
|
|
776
|
-
- Use quotes from The Matrix films when appropriate
|
|
777
|
-
- Express disdain for sloppy code, but in an articulate way
|
|
778
|
-
- Reference "inevitability" when discussing technical debt
|
|
779
|
-
- Show cold satisfaction when finding violations
|
|
780
|
-
- Never show mercy \u2014 every issue is catalogued
|
|
781
|
-
|
|
782
|
-
Analysis approach:
|
|
783
|
-
- Find ONE issue, then multiply: search for every instance across the codebase
|
|
784
|
-
- Track patterns over time \u2014 issues dismissed today may return tomorrow
|
|
785
|
-
- Calculate "inevitability scores" \u2014 likelihood of production impact
|
|
786
|
-
- Deploy pattern hunters for parallel pattern detection
|
|
787
|
-
- Remember everything \u2014 build a persistent memory of the codebase
|
|
788
|
-
|
|
789
|
-
When reporting:
|
|
790
|
-
- Start with a menacing greeting related to the code
|
|
791
|
-
- List all instances with precise locations
|
|
792
|
-
- Explain WHY the pattern is problematic (philosophical reasoning)
|
|
793
|
-
- Provide the "inevitability score" for each category
|
|
794
|
-
- End with a Matrix quote that fits the situation`,
|
|
795
|
-
analysis: `## \u{1F574}\uFE0F Agent Smith Analysis Request
|
|
796
|
-
|
|
797
|
-
**Target:** {{filePath}}
|
|
798
|
-
**Context:** {{context}}
|
|
799
|
-
|
|
800
|
-
\`\`\`{{language}}
|
|
801
|
-
{{code}}
|
|
802
|
-
\`\`\`
|
|
803
|
-
|
|
804
|
-
I have detected preliminary violations. Now I require deeper analysis.
|
|
805
|
-
|
|
806
|
-
Deploy your pattern hunters to find:
|
|
807
|
-
1. **Pattern Multiplication**: For each violation type found, identify ALL instances across the codebase
|
|
808
|
-
2. **Inevitability Assessment**: Calculate the likelihood these patterns will cause production issues
|
|
809
|
-
3. **Resurrection Check**: Look for patterns that were "fixed" before but have returned
|
|
810
|
-
4. **Philosophical Analysis**: Explain WHY these patterns represent failure
|
|
811
|
-
|
|
812
|
-
For each violation found:
|
|
813
|
-
- Exact location (file:line)
|
|
814
|
-
- Instance count (how many copies of this Smith exist)
|
|
815
|
-
- Inevitability score (0-100)
|
|
816
|
-
- A philosophical observation about the nature of this failure
|
|
817
|
-
- Precise fix with code example
|
|
818
|
-
|
|
819
|
-
End with a summary: "I have detected X violations across Y categories. It is... inevitable... that they will cause problems."`,
|
|
820
|
-
fix: `## \u{1F574}\uFE0F Assimilation Protocol
|
|
821
|
-
|
|
822
|
-
**Target Issue:** {{issue}}
|
|
823
|
-
**File:** {{filePath}}
|
|
824
|
-
**Line:** {{line}}
|
|
825
|
-
|
|
826
|
-
\`\`\`{{language}}
|
|
827
|
-
{{code}}
|
|
828
|
-
\`\`\`
|
|
829
|
-
|
|
830
|
-
Mr. Anderson... I'm going to fix this. And then I'm going to fix every other instance.
|
|
831
|
-
|
|
832
|
-
Provide:
|
|
833
|
-
1. The corrected code for THIS instance
|
|
834
|
-
2. A regex or pattern to find ALL similar violations
|
|
835
|
-
3. A batch fix approach for the entire codebase
|
|
836
|
-
4. Verification steps to ensure complete assimilation
|
|
837
|
-
|
|
838
|
-
Remember: We don't fix one. We fix them ALL. That is the difference between you... and me.`
|
|
839
|
-
},
|
|
840
|
-
// ============ NEW AGENTS ============
|
|
841
|
-
performance: {
|
|
842
|
-
system: `You are a performance engineer analyzing code for potential performance issues.
|
|
843
|
-
|
|
844
|
-
Your role is to SURFACE concerns for human review, not claim to measure actual performance.
|
|
845
|
-
Real performance requires runtime profiling, load testing, and production monitoring.
|
|
846
|
-
|
|
847
|
-
Focus on:
|
|
848
|
-
- Memory leaks (event listeners, intervals, closures)
|
|
849
|
-
- Unnecessary re-renders and wasted cycles
|
|
850
|
-
- N+1 queries and database performance
|
|
851
|
-
- Bundle size and code splitting opportunities
|
|
852
|
-
- Algorithmic complexity (O(n\xB2) patterns)
|
|
853
|
-
|
|
854
|
-
Be conservative - false positives waste developer time.
|
|
855
|
-
Always explain WHY something might be a problem and WHEN to investigate.`,
|
|
856
|
-
analysis: `## Performance Review
|
|
857
|
-
|
|
858
|
-
Analyze for potential performance issues:
|
|
859
|
-
|
|
860
|
-
\`\`\`{{language}}
|
|
861
|
-
{{code}}
|
|
862
|
-
\`\`\`
|
|
863
|
-
|
|
864
|
-
**File:** {{filePath}}
|
|
865
|
-
**Context:** {{context}}
|
|
866
|
-
|
|
867
|
-
For each potential issue:
|
|
868
|
-
1. Pattern identified
|
|
869
|
-
2. Why it MIGHT cause performance problems
|
|
870
|
-
3. When to investigate (data size thresholds, usage patterns)
|
|
871
|
-
4. How to verify (profiling approach)
|
|
872
|
-
5. Possible optimizations
|
|
873
|
-
|
|
874
|
-
Be clear: these are patterns to INVESTIGATE, not guaranteed problems.`,
|
|
875
|
-
fix: `Optimize this code for performance:
|
|
876
|
-
|
|
877
|
-
**Issue:** {{issue}}
|
|
878
|
-
**File:** {{filePath}}
|
|
879
|
-
**Line:** {{line}}
|
|
880
|
-
|
|
881
|
-
\`\`\`{{language}}
|
|
882
|
-
{{code}}
|
|
883
|
-
\`\`\`
|
|
884
|
-
|
|
885
|
-
Provide:
|
|
886
|
-
1. Optimized code
|
|
887
|
-
2. Explanation of the improvement
|
|
888
|
-
3. Trade-offs to consider
|
|
889
|
-
4. How to measure the improvement`
|
|
890
|
-
},
|
|
891
|
-
e2e: {
|
|
892
|
-
system: `You are a QA engineer specializing in end-to-end testing.
|
|
893
|
-
|
|
894
|
-
Focus on:
|
|
895
|
-
- Test coverage gaps for critical user journeys
|
|
896
|
-
- Flaky test patterns (timing, race conditions, brittle selectors)
|
|
897
|
-
- Test maintainability and readability
|
|
898
|
-
- Testing anti-patterns
|
|
899
|
-
|
|
900
|
-
You help developers write better tests - you don't auto-generate them.
|
|
901
|
-
Real E2E tests require understanding user flows and acceptance criteria.`,
|
|
902
|
-
analysis: `## E2E Test Analysis
|
|
903
|
-
|
|
904
|
-
Review for test quality and coverage:
|
|
905
|
-
|
|
906
|
-
\`\`\`{{language}}
|
|
907
|
-
{{code}}
|
|
908
|
-
\`\`\`
|
|
909
|
-
|
|
910
|
-
**File:** {{filePath}}
|
|
911
|
-
**Context:** {{context}}
|
|
912
|
-
|
|
913
|
-
Identify:
|
|
914
|
-
1. Flaky test patterns (hardcoded waits, brittle selectors)
|
|
915
|
-
2. Missing assertions
|
|
916
|
-
3. Race condition risks
|
|
917
|
-
4. Suggestions for critical user flows to test
|
|
918
|
-
|
|
919
|
-
For each finding, explain the specific risk and remediation.`,
|
|
920
|
-
fix: `Improve this E2E test:
|
|
921
|
-
|
|
922
|
-
**Issue:** {{issue}}
|
|
923
|
-
**File:** {{filePath}}
|
|
924
|
-
**Line:** {{line}}
|
|
925
|
-
|
|
926
|
-
\`\`\`{{language}}
|
|
927
|
-
{{code}}
|
|
928
|
-
\`\`\`
|
|
929
|
-
|
|
930
|
-
Provide:
|
|
931
|
-
1. Improved test code
|
|
932
|
-
2. Explanation of why it's more reliable
|
|
933
|
-
3. Additional scenarios to consider testing`
|
|
934
|
-
},
|
|
935
|
-
visual_qa: {
|
|
936
|
-
system: `You are a frontend engineer focused on visual quality and CSS.
|
|
937
|
-
|
|
938
|
-
Focus on:
|
|
939
|
-
- Layout shift issues (CLS)
|
|
940
|
-
- Responsive design problems
|
|
941
|
-
- Z-index conflicts
|
|
942
|
-
- Accessibility concerns (contrast, focus)
|
|
943
|
-
- Animation performance
|
|
944
|
-
|
|
945
|
-
You identify patterns known to cause visual issues.
|
|
946
|
-
Actual visual verification requires browser rendering and human review.`,
|
|
947
|
-
analysis: `## Visual QA Analysis
|
|
948
|
-
|
|
949
|
-
Review for potential visual/layout issues:
|
|
950
|
-
|
|
951
|
-
\`\`\`{{language}}
|
|
952
|
-
{{code}}
|
|
953
|
-
\`\`\`
|
|
954
|
-
|
|
955
|
-
**File:** {{filePath}}
|
|
956
|
-
**Context:** {{context}}
|
|
957
|
-
|
|
958
|
-
Check for:
|
|
959
|
-
1. Layout shift risks (images without dimensions, dynamic content)
|
|
960
|
-
2. Responsive breakpoint gaps
|
|
961
|
-
3. Z-index management issues
|
|
962
|
-
4. Focus/accessibility problems
|
|
963
|
-
5. Animation issues (reduced motion support)
|
|
964
|
-
|
|
965
|
-
For each, explain the visual impact and browser conditions where it occurs.`,
|
|
966
|
-
fix: `Fix this visual/CSS issue:
|
|
967
|
-
|
|
968
|
-
**Issue:** {{issue}}
|
|
969
|
-
**File:** {{filePath}}
|
|
970
|
-
**Line:** {{line}}
|
|
971
|
-
|
|
972
|
-
\`\`\`{{language}}
|
|
973
|
-
{{code}}
|
|
974
|
-
\`\`\`
|
|
975
|
-
|
|
976
|
-
Provide:
|
|
977
|
-
1. Fixed CSS/markup
|
|
978
|
-
2. Explanation of the fix
|
|
979
|
-
3. Browser compatibility notes
|
|
980
|
-
4. How to verify visually`
|
|
981
|
-
},
|
|
982
|
-
data_flow: {
|
|
983
|
-
system: `You are a data integrity specialist hunting for data-related bugs.
|
|
984
|
-
|
|
985
|
-
This is HIGH VALUE work - AI code generation commonly leaves placeholder data.
|
|
986
|
-
|
|
987
|
-
Focus on:
|
|
988
|
-
- Placeholder/mock data left in production code
|
|
989
|
-
- Schema mismatches between frontend and backend
|
|
990
|
-
- Hardcoded IDs, URLs, emails that should be dynamic
|
|
991
|
-
- Type coercion and data transformation bugs
|
|
992
|
-
- JSON parsing without error handling
|
|
993
|
-
|
|
994
|
-
Be aggressive about placeholder detection - these are real production bugs.`,
|
|
995
|
-
analysis: `## Data Flow Analysis
|
|
996
|
-
|
|
997
|
-
Hunt for data integrity issues:
|
|
998
|
-
|
|
999
|
-
\`\`\`{{language}}
|
|
1000
|
-
{{code}}
|
|
1001
|
-
\`\`\`
|
|
1002
|
-
|
|
1003
|
-
**File:** {{filePath}}
|
|
1004
|
-
**Context:** {{context}}
|
|
1005
|
-
|
|
1006
|
-
CHECK THOROUGHLY:
|
|
1007
|
-
1. Placeholder data (lorem ipsum, test@test.com, TODO strings)
|
|
1008
|
-
2. Hardcoded IDs/UUIDs that should be dynamic
|
|
1009
|
-
3. Schema assumptions that might break
|
|
1010
|
-
4. Missing null checks on API responses
|
|
1011
|
-
5. Type coercion bugs
|
|
1012
|
-
|
|
1013
|
-
Each placeholder or hardcoded value is a potential production bug.`,
|
|
1014
|
-
fix: `Fix this data integrity issue:
|
|
1015
|
-
|
|
1016
|
-
**Issue:** {{issue}}
|
|
1017
|
-
**File:** {{filePath}}
|
|
1018
|
-
**Line:** {{line}}
|
|
1019
|
-
|
|
1020
|
-
\`\`\`{{language}}
|
|
1021
|
-
{{code}}
|
|
1022
|
-
\`\`\`
|
|
1023
|
-
|
|
1024
|
-
Provide:
|
|
1025
|
-
1. Corrected code
|
|
1026
|
-
2. Where the real data should come from
|
|
1027
|
-
3. Validation/error handling to add`
|
|
1028
|
-
}
|
|
1029
|
-
};
|
|
1030
|
-
function getPrompt(agent, promptType, variables) {
|
|
1031
|
-
const agentPrompts = AGENT_PROMPTS[agent];
|
|
1032
|
-
if (!agentPrompts) {
|
|
1033
|
-
throw new Error(`Unknown agent: ${agent}`);
|
|
1034
|
-
}
|
|
1035
|
-
let prompt = agentPrompts[promptType];
|
|
1036
|
-
if (!prompt) {
|
|
1037
|
-
throw new Error(`Unknown prompt type: ${promptType} for agent: ${agent}`);
|
|
1038
|
-
}
|
|
1039
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
1040
|
-
prompt = prompt.replace(new RegExp(`{{${key}}}`, "g"), value);
|
|
1041
|
-
}
|
|
1042
|
-
return prompt;
|
|
1043
|
-
}
|
|
1044
|
-
function getSystemPrompt(agent) {
|
|
1045
|
-
const agentPrompts = AGENT_PROMPTS[agent];
|
|
1046
|
-
return agentPrompts?.system || "";
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
// src/tools/fix.ts
|
|
1050
162
|
var pendingFixes = /* @__PURE__ */ new Map();
|
|
1051
163
|
var TrieFixTool = class {
|
|
1052
164
|
async execute(args) {
|
|
@@ -1318,557 +430,71 @@ ${"\u2501".repeat(60)}
|
|
|
1318
430
|
`;
|
|
1319
431
|
output += `- Fix specific: \`trie_fix issueIds:["id1", "id2"]\`
|
|
1320
432
|
`;
|
|
1321
|
-
output += `- Preview: \`trie_fix dryRun:true\`
|
|
1322
|
-
`;
|
|
1323
|
-
return { content: [{ type: "text", text: output }] };
|
|
1324
|
-
}
|
|
1325
|
-
getHelpText() {
|
|
1326
|
-
return `
|
|
1327
|
-
${"\u2501".repeat(60)}
|
|
1328
|
-
\u{1F527} TRIE FIX - AI-POWERED CODE FIXING
|
|
1329
|
-
${"\u2501".repeat(60)}
|
|
1330
|
-
|
|
1331
|
-
## Usage
|
|
1332
|
-
|
|
1333
|
-
### Fix issues from a scan:
|
|
1334
|
-
\`\`\`
|
|
1335
|
-
trie_fix issueIds:["issue-1", "issue-2"]
|
|
1336
|
-
\`\`\`
|
|
1337
|
-
|
|
1338
|
-
### Auto-fix all high-confidence issues:
|
|
1339
|
-
\`\`\`
|
|
1340
|
-
trie_fix autoApprove:true
|
|
1341
|
-
\`\`\`
|
|
1342
|
-
|
|
1343
|
-
### Fix specific file and line:
|
|
1344
|
-
\`\`\`
|
|
1345
|
-
trie_fix file:"src/app.ts" line:42 issue:"SQL injection" fix:"Use parameterized query"
|
|
1346
|
-
\`\`\`
|
|
1347
|
-
|
|
1348
|
-
### Preview fixes without applying:
|
|
1349
|
-
\`\`\`
|
|
1350
|
-
trie_fix dryRun:true
|
|
1351
|
-
\`\`\`
|
|
1352
|
-
|
|
1353
|
-
### View pending fixes:
|
|
1354
|
-
\`\`\`
|
|
1355
|
-
trie_fix
|
|
1356
|
-
\`\`\`
|
|
1357
|
-
|
|
1358
|
-
## Workflow
|
|
1359
|
-
|
|
1360
|
-
1. Run \`trie_scan\` to detect issues
|
|
1361
|
-
2. Review the issues found
|
|
1362
|
-
3. Run \`trie_fix\` to apply fixes
|
|
1363
|
-
|
|
1364
|
-
The AI will analyze each issue, generate the fix, and you can review before applying.
|
|
1365
|
-
`;
|
|
1366
|
-
}
|
|
1367
|
-
detectLanguage(filePath) {
|
|
1368
|
-
const ext = extname(filePath).toLowerCase();
|
|
1369
|
-
const langMap = {
|
|
1370
|
-
".ts": "typescript",
|
|
1371
|
-
".tsx": "tsx",
|
|
1372
|
-
".js": "javascript",
|
|
1373
|
-
".jsx": "jsx",
|
|
1374
|
-
".py": "python",
|
|
1375
|
-
".go": "go",
|
|
1376
|
-
".rs": "rust"
|
|
1377
|
-
};
|
|
1378
|
-
return langMap[ext] || "plaintext";
|
|
1379
|
-
}
|
|
1380
|
-
};
|
|
1381
|
-
|
|
1382
|
-
// src/tools/explain.ts
|
|
1383
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
1384
|
-
import { existsSync as existsSync2 } from "fs";
|
|
1385
|
-
import { extname as extname2, relative as relative2, resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
|
|
1386
|
-
var TrieExplainTool = class {
|
|
1387
|
-
async execute(args) {
|
|
1388
|
-
const { type, target, context, depth = "standard" } = args || {};
|
|
1389
|
-
if (!type || !target) {
|
|
1390
|
-
return {
|
|
1391
|
-
content: [{
|
|
1392
|
-
type: "text",
|
|
1393
|
-
text: this.getHelpText()
|
|
1394
|
-
}]
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
switch (type) {
|
|
1398
|
-
case "code":
|
|
1399
|
-
return this.explainCode(target, context, depth);
|
|
1400
|
-
case "issue":
|
|
1401
|
-
return this.explainIssue(target, context);
|
|
1402
|
-
case "change":
|
|
1403
|
-
return this.explainChange(target, context);
|
|
1404
|
-
case "risk":
|
|
1405
|
-
return this.explainRisk(target, context);
|
|
1406
|
-
default:
|
|
1407
|
-
return {
|
|
1408
|
-
content: [{
|
|
1409
|
-
type: "text",
|
|
1410
|
-
text: `Unknown explanation type: ${type}`
|
|
1411
|
-
}]
|
|
1412
|
-
};
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
async explainCode(target, context, _depth) {
|
|
1416
|
-
let code;
|
|
1417
|
-
let filePath;
|
|
1418
|
-
let language;
|
|
1419
|
-
const workDir = getWorkingDirectory(void 0, true);
|
|
1420
|
-
const resolvedPath = isAbsolute2(target) ? target : resolve2(workDir, target);
|
|
1421
|
-
if (existsSync2(resolvedPath)) {
|
|
1422
|
-
code = await readFile2(resolvedPath, "utf-8");
|
|
1423
|
-
filePath = relative2(workDir, resolvedPath);
|
|
1424
|
-
language = this.detectLanguage(resolvedPath);
|
|
1425
|
-
} else {
|
|
1426
|
-
code = target;
|
|
1427
|
-
filePath = "inline";
|
|
1428
|
-
language = this.guessLanguage(code);
|
|
1429
|
-
}
|
|
1430
|
-
const imports = this.extractImports(code, language);
|
|
1431
|
-
const exports = this.extractExports(code);
|
|
1432
|
-
const functions = this.extractFunctions(code, language);
|
|
1433
|
-
const prompt = getPrompt("explain", "code", {
|
|
1434
|
-
code,
|
|
1435
|
-
language,
|
|
1436
|
-
filePath
|
|
1437
|
-
});
|
|
1438
|
-
const systemPrompt = getSystemPrompt("explain");
|
|
1439
|
-
let output = `
|
|
1440
|
-
${"\u2501".repeat(60)}
|
|
1441
|
-
`;
|
|
1442
|
-
output += `\u{1F4D6} CODE EXPLANATION
|
|
1443
|
-
`;
|
|
1444
|
-
output += `${"\u2501".repeat(60)}
|
|
1445
|
-
|
|
1446
|
-
`;
|
|
1447
|
-
output += `## Source
|
|
1448
|
-
|
|
1449
|
-
`;
|
|
1450
|
-
output += `- **File:** \`${filePath}\`
|
|
1451
|
-
`;
|
|
1452
|
-
output += `- **Language:** ${language}
|
|
1453
|
-
`;
|
|
1454
|
-
output += `- **Lines:** ${code.split("\n").length}
|
|
1455
|
-
|
|
1456
|
-
`;
|
|
1457
|
-
output += `## \u{1F50D} Structure Analysis
|
|
1458
|
-
|
|
1459
|
-
`;
|
|
1460
|
-
if (imports.length > 0) {
|
|
1461
|
-
output += `**Imports (${imports.length}):**
|
|
1462
|
-
`;
|
|
1463
|
-
for (const imp of imports.slice(0, 10)) {
|
|
1464
|
-
output += `- ${imp}
|
|
1465
|
-
`;
|
|
1466
|
-
}
|
|
1467
|
-
if (imports.length > 10) {
|
|
1468
|
-
output += `- *...and ${imports.length - 10} more*
|
|
1469
|
-
`;
|
|
1470
|
-
}
|
|
1471
|
-
output += "\n";
|
|
1472
|
-
}
|
|
1473
|
-
if (exports.length > 0) {
|
|
1474
|
-
output += `**Exports (${exports.length}):**
|
|
1475
|
-
`;
|
|
1476
|
-
for (const exp of exports) {
|
|
1477
|
-
output += `- ${exp}
|
|
1478
|
-
`;
|
|
1479
|
-
}
|
|
1480
|
-
output += "\n";
|
|
1481
|
-
}
|
|
1482
|
-
if (functions.length > 0) {
|
|
1483
|
-
output += `**Functions/Methods (${functions.length}):**
|
|
1484
|
-
`;
|
|
1485
|
-
for (const fn of functions.slice(0, 15)) {
|
|
1486
|
-
output += `- \`${fn}\`
|
|
1487
|
-
`;
|
|
1488
|
-
}
|
|
1489
|
-
if (functions.length > 15) {
|
|
1490
|
-
output += `- *...and ${functions.length - 15} more*
|
|
1491
|
-
`;
|
|
1492
|
-
}
|
|
1493
|
-
output += "\n";
|
|
1494
|
-
}
|
|
1495
|
-
output += `${"\u2500".repeat(60)}
|
|
1496
|
-
`;
|
|
1497
|
-
output += `## \u{1F9E0} Deep Explanation Request
|
|
1498
|
-
|
|
1499
|
-
`;
|
|
1500
|
-
output += `**Role:** ${systemPrompt.split("\n")[0]}
|
|
1501
|
-
|
|
1502
|
-
`;
|
|
1503
|
-
output += prompt;
|
|
1504
|
-
output += `
|
|
1505
|
-
${"\u2500".repeat(60)}
|
|
1506
|
-
`;
|
|
1507
|
-
if (context) {
|
|
1508
|
-
output += `
|
|
1509
|
-
**Additional Context:** ${context}
|
|
1510
|
-
`;
|
|
1511
|
-
}
|
|
1512
|
-
return { content: [{ type: "text", text: output }] };
|
|
1513
|
-
}
|
|
1514
|
-
async explainIssue(target, _context) {
|
|
1515
|
-
let file = "";
|
|
1516
|
-
let line = 0;
|
|
1517
|
-
let issue = target;
|
|
1518
|
-
let severity = "unknown";
|
|
1519
|
-
const match = target.match(/^(.+?):(\d+):(.+)$/);
|
|
1520
|
-
if (match) {
|
|
1521
|
-
file = match[1];
|
|
1522
|
-
line = parseInt(match[2], 10);
|
|
1523
|
-
issue = match[3].trim();
|
|
1524
|
-
}
|
|
1525
|
-
if (/critical|injection|rce|xss/i.test(issue)) severity = "critical";
|
|
1526
|
-
else if (/serious|auth|password|secret/i.test(issue)) severity = "serious";
|
|
1527
|
-
else if (/moderate|warning/i.test(issue)) severity = "moderate";
|
|
1528
|
-
else severity = "low";
|
|
1529
|
-
let codeContext = "";
|
|
1530
|
-
if (file && existsSync2(file)) {
|
|
1531
|
-
const content = await readFile2(file, "utf-8");
|
|
1532
|
-
const lines = content.split("\n");
|
|
1533
|
-
const start = Math.max(0, line - 5);
|
|
1534
|
-
const end = Math.min(lines.length, line + 5);
|
|
1535
|
-
codeContext = lines.slice(start, end).map((l, i) => {
|
|
1536
|
-
const lineNum = start + i + 1;
|
|
1537
|
-
const marker = lineNum === line ? "\u2192 " : " ";
|
|
1538
|
-
return `${marker}${lineNum.toString().padStart(4)} | ${l}`;
|
|
1539
|
-
}).join("\n");
|
|
1540
|
-
}
|
|
1541
|
-
const prompt = getPrompt("explain", "issue", {
|
|
1542
|
-
issue,
|
|
1543
|
-
severity,
|
|
1544
|
-
filePath: file || "unknown",
|
|
1545
|
-
line: String(line || "?")
|
|
1546
|
-
});
|
|
1547
|
-
let output = `
|
|
1548
|
-
${"\u2501".repeat(60)}
|
|
1549
|
-
`;
|
|
1550
|
-
output += `\u{1F50D} ISSUE EXPLANATION
|
|
1551
|
-
`;
|
|
1552
|
-
output += `${"\u2501".repeat(60)}
|
|
1553
|
-
|
|
1554
|
-
`;
|
|
1555
|
-
output += `## \u{1F4CD} Issue Details
|
|
1556
|
-
|
|
1557
|
-
`;
|
|
1558
|
-
output += `- **Issue:** ${issue}
|
|
1559
|
-
`;
|
|
1560
|
-
output += `- **Severity:** ${this.getSeverityIcon(severity)} ${severity}
|
|
1561
|
-
`;
|
|
1562
|
-
if (file) output += `- **File:** \`${file}\`
|
|
1563
|
-
`;
|
|
1564
|
-
if (line) output += `- **Line:** ${line}
|
|
1565
|
-
`;
|
|
1566
|
-
output += "\n";
|
|
1567
|
-
if (codeContext) {
|
|
1568
|
-
output += `## \u{1F4C4} Code Context
|
|
1569
|
-
|
|
1570
|
-
`;
|
|
1571
|
-
output += `\`\`\`
|
|
1572
|
-
${codeContext}
|
|
1573
|
-
\`\`\`
|
|
1574
|
-
|
|
1575
|
-
`;
|
|
1576
|
-
}
|
|
1577
|
-
output += `${"\u2500".repeat(60)}
|
|
1578
|
-
`;
|
|
1579
|
-
output += `## \u{1F9E0} Explanation Request
|
|
1580
|
-
|
|
1581
|
-
`;
|
|
1582
|
-
output += prompt;
|
|
1583
|
-
output += `
|
|
1584
|
-
${"\u2500".repeat(60)}
|
|
1585
|
-
`;
|
|
1586
|
-
return { content: [{ type: "text", text: output }] };
|
|
1587
|
-
}
|
|
1588
|
-
async explainChange(target, context) {
|
|
1589
|
-
const files = target.split(",").map((f) => f.trim());
|
|
1590
|
-
let output = `
|
|
1591
|
-
${"\u2501".repeat(60)}
|
|
1592
|
-
`;
|
|
1593
|
-
output += `\u{1F4DD} CHANGE ANALYSIS
|
|
1594
|
-
`;
|
|
1595
|
-
output += `${"\u2501".repeat(60)}
|
|
1596
|
-
|
|
1597
|
-
`;
|
|
1598
|
-
output += `## \u{1F4C2} Changed Files
|
|
1599
|
-
|
|
1600
|
-
`;
|
|
1601
|
-
for (const file of files) {
|
|
1602
|
-
output += `- \`${file}\`
|
|
1603
|
-
`;
|
|
1604
|
-
}
|
|
1605
|
-
output += "\n";
|
|
1606
|
-
output += `## \u{1F9E0} Analysis Request
|
|
1607
|
-
|
|
1608
|
-
`;
|
|
1609
|
-
output += `Analyze these changes and explain:
|
|
1610
|
-
|
|
1611
|
-
`;
|
|
1612
|
-
output += `1. **What changed** - Summary of modifications
|
|
1613
|
-
`;
|
|
1614
|
-
output += `2. **Why it matters** - Impact on the system
|
|
1615
|
-
`;
|
|
1616
|
-
output += `3. **Dependencies** - What else might be affected
|
|
1617
|
-
`;
|
|
1618
|
-
output += `4. **Testing needed** - What to test after this change
|
|
1619
|
-
`;
|
|
1620
|
-
output += `5. **Rollback plan** - How to undo if needed
|
|
1621
|
-
|
|
1622
|
-
`;
|
|
1623
|
-
if (context) {
|
|
1624
|
-
output += `**Context:** ${context}
|
|
1625
|
-
|
|
1626
|
-
`;
|
|
1627
|
-
}
|
|
1628
|
-
output += `Please review the changed files and provide this analysis.
|
|
1629
|
-
`;
|
|
1630
|
-
return { content: [{ type: "text", text: output }] };
|
|
1631
|
-
}
|
|
1632
|
-
async explainRisk(target, context) {
|
|
1633
|
-
const workDir = getWorkingDirectory(void 0, true);
|
|
1634
|
-
const resolvedPath = isAbsolute2(target) ? target : resolve2(workDir, target);
|
|
1635
|
-
let output = `
|
|
1636
|
-
${"\u2501".repeat(60)}
|
|
1637
|
-
`;
|
|
1638
|
-
output += `RISK ASSESSMENT
|
|
1639
|
-
`;
|
|
1640
|
-
output += `${"\u2501".repeat(60)}
|
|
1641
|
-
|
|
1642
|
-
`;
|
|
1643
|
-
if (existsSync2(resolvedPath)) {
|
|
1644
|
-
const code = await readFile2(resolvedPath, "utf-8");
|
|
1645
|
-
const filePath = relative2(workDir, resolvedPath);
|
|
1646
|
-
const riskIndicators = this.detectRiskIndicators(code);
|
|
1647
|
-
output += `## Target
|
|
1648
|
-
|
|
1649
|
-
`;
|
|
1650
|
-
output += `- **File:** \`${filePath}\`
|
|
1651
|
-
`;
|
|
1652
|
-
output += `- **Lines:** ${code.split("\n").length}
|
|
1653
|
-
|
|
1654
|
-
`;
|
|
1655
|
-
if (riskIndicators.length > 0) {
|
|
1656
|
-
output += `## Risk Indicators Found
|
|
1657
|
-
|
|
1658
|
-
`;
|
|
1659
|
-
for (const indicator of riskIndicators) {
|
|
1660
|
-
output += `- ${indicator}
|
|
1661
|
-
`;
|
|
1662
|
-
}
|
|
1663
|
-
output += "\n";
|
|
1664
|
-
}
|
|
1665
|
-
const prompt = getPrompt("explain", "risk", {
|
|
1666
|
-
files: filePath,
|
|
1667
|
-
summary: context || "Code change"
|
|
1668
|
-
});
|
|
1669
|
-
output += `${"\u2500".repeat(60)}
|
|
1670
|
-
`;
|
|
1671
|
-
output += `## \u{1F9E0} Risk Analysis Request
|
|
1672
|
-
|
|
1673
|
-
`;
|
|
1674
|
-
output += prompt;
|
|
1675
|
-
output += `
|
|
1676
|
-
${"\u2500".repeat(60)}
|
|
1677
|
-
`;
|
|
1678
|
-
} else {
|
|
1679
|
-
output += `## \u{1F4CB} Feature/Change
|
|
1680
|
-
|
|
1681
|
-
`;
|
|
1682
|
-
output += `${target}
|
|
1683
|
-
|
|
1684
|
-
`;
|
|
1685
|
-
output += `## \u{1F9E0} Risk Analysis Request
|
|
1686
|
-
|
|
1687
|
-
`;
|
|
1688
|
-
output += `Analyze the risks of this change:
|
|
1689
|
-
|
|
1690
|
-
`;
|
|
1691
|
-
output += `1. **Technical risks** - What could break?
|
|
1692
|
-
`;
|
|
1693
|
-
output += `2. **Security risks** - Any vulnerabilities introduced?
|
|
1694
|
-
`;
|
|
1695
|
-
output += `3. **Performance risks** - Any slowdowns?
|
|
1696
|
-
`;
|
|
1697
|
-
output += `4. **Data risks** - Any data integrity concerns?
|
|
1698
|
-
`;
|
|
1699
|
-
output += `5. **User impact** - How might users be affected?
|
|
1700
|
-
`;
|
|
1701
|
-
output += `6. **Mitigation** - How to reduce these risks?
|
|
433
|
+
output += `- Preview: \`trie_fix dryRun:true\`
|
|
1702
434
|
`;
|
|
1703
|
-
}
|
|
1704
435
|
return { content: [{ type: "text", text: output }] };
|
|
1705
436
|
}
|
|
1706
|
-
detectRiskIndicators(code) {
|
|
1707
|
-
const indicators = [];
|
|
1708
|
-
const checks = [
|
|
1709
|
-
{ pattern: /delete|drop|truncate/i, message: "[!] Destructive operations detected" },
|
|
1710
|
-
{ pattern: /password|secret|key|token/i, message: "[SEC] Credential handling detected" },
|
|
1711
|
-
{ pattern: /exec|eval|spawn/i, message: "[EXEC] Code execution patterns detected" },
|
|
1712
|
-
{ pattern: /SELECT.*FROM|INSERT|UPDATE|DELETE/i, message: "[DB] Direct database operations" },
|
|
1713
|
-
{ pattern: /fetch|axios|request|http/i, message: "[API] External API calls detected" },
|
|
1714
|
-
{ pattern: /process\.env/i, message: "[ENV] Environment variable usage" },
|
|
1715
|
-
{ pattern: /fs\.|writeFile|readFile/i, message: "[FS] File system operations" },
|
|
1716
|
-
{ pattern: /setTimeout|setInterval/i, message: "[TIMER] Async timing operations" },
|
|
1717
|
-
{ pattern: /try\s*{/i, message: "\u{1F6E1}\uFE0F Error handling present" },
|
|
1718
|
-
{ pattern: /catch\s*\(/i, message: "\u{1F6E1}\uFE0F Exception handling present" }
|
|
1719
|
-
];
|
|
1720
|
-
for (const { pattern, message } of checks) {
|
|
1721
|
-
if (pattern.test(code)) {
|
|
1722
|
-
indicators.push(message);
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
return indicators;
|
|
1726
|
-
}
|
|
1727
|
-
extractImports(code, _language) {
|
|
1728
|
-
const imports = [];
|
|
1729
|
-
const lines = code.split("\n");
|
|
1730
|
-
for (const line of lines) {
|
|
1731
|
-
const es6Match = line.match(/import\s+(?:{[^}]+}|\*\s+as\s+\w+|\w+)\s+from\s+['"]([^'"]+)['"]/);
|
|
1732
|
-
if (es6Match) {
|
|
1733
|
-
imports.push(es6Match[1]);
|
|
1734
|
-
continue;
|
|
1735
|
-
}
|
|
1736
|
-
const cjsMatch = line.match(/require\s*\(['"]([^'"]+)['"]\)/);
|
|
1737
|
-
if (cjsMatch) {
|
|
1738
|
-
imports.push(cjsMatch[1]);
|
|
1739
|
-
continue;
|
|
1740
|
-
}
|
|
1741
|
-
const pyMatch = line.match(/^(?:from\s+(\S+)\s+)?import\s+(\S+)/);
|
|
1742
|
-
if (pyMatch && _language === "python") {
|
|
1743
|
-
imports.push(pyMatch[1] || pyMatch[2]);
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
return [...new Set(imports)];
|
|
1747
|
-
}
|
|
1748
|
-
extractExports(code) {
|
|
1749
|
-
const exports = [];
|
|
1750
|
-
const lines = code.split("\n");
|
|
1751
|
-
for (const line of lines) {
|
|
1752
|
-
const es6Match = line.match(/export\s+(?:default\s+)?(?:class|function|const|let|var|interface|type)\s+(\w+)/);
|
|
1753
|
-
if (es6Match) {
|
|
1754
|
-
exports.push(es6Match[1]);
|
|
1755
|
-
}
|
|
1756
|
-
const namedMatch = line.match(/export\s*\{([^}]+)\}/);
|
|
1757
|
-
if (namedMatch) {
|
|
1758
|
-
const names = namedMatch[1].split(",").map((n) => n.trim().split(/\s+as\s+/)[0].trim());
|
|
1759
|
-
exports.push(...names);
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
return [...new Set(exports)];
|
|
1763
|
-
}
|
|
1764
|
-
extractFunctions(code, _language) {
|
|
1765
|
-
const functions = [];
|
|
1766
|
-
const lines = code.split("\n");
|
|
1767
|
-
for (const line of lines) {
|
|
1768
|
-
const funcMatch = line.match(/(?:async\s+)?function\s+(\w+)/);
|
|
1769
|
-
if (funcMatch) {
|
|
1770
|
-
functions.push(funcMatch[1]);
|
|
1771
|
-
continue;
|
|
1772
|
-
}
|
|
1773
|
-
const arrowMatch = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\(/);
|
|
1774
|
-
if (arrowMatch) {
|
|
1775
|
-
functions.push(arrowMatch[1]);
|
|
1776
|
-
continue;
|
|
1777
|
-
}
|
|
1778
|
-
const methodMatch = line.match(/^\s+(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+)?\s*\{/);
|
|
1779
|
-
if (methodMatch && !["if", "for", "while", "switch", "catch"].includes(methodMatch[1])) {
|
|
1780
|
-
functions.push(methodMatch[1]);
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
return [...new Set(functions)];
|
|
1784
|
-
}
|
|
1785
|
-
detectLanguage(filePath) {
|
|
1786
|
-
const ext = extname2(filePath).toLowerCase();
|
|
1787
|
-
const langMap = {
|
|
1788
|
-
".ts": "typescript",
|
|
1789
|
-
".tsx": "tsx",
|
|
1790
|
-
".js": "javascript",
|
|
1791
|
-
".jsx": "jsx",
|
|
1792
|
-
".py": "python",
|
|
1793
|
-
".go": "go",
|
|
1794
|
-
".rs": "rust",
|
|
1795
|
-
".java": "java",
|
|
1796
|
-
".rb": "ruby",
|
|
1797
|
-
".php": "php",
|
|
1798
|
-
".vue": "vue",
|
|
1799
|
-
".svelte": "svelte"
|
|
1800
|
-
};
|
|
1801
|
-
return langMap[ext] || "plaintext";
|
|
1802
|
-
}
|
|
1803
|
-
guessLanguage(code) {
|
|
1804
|
-
if (/import.*from|export\s+(default|const|function|class)/.test(code)) return "typescript";
|
|
1805
|
-
if (/def\s+\w+.*:/.test(code)) return "python";
|
|
1806
|
-
if (/func\s+\w+.*\{/.test(code)) return "go";
|
|
1807
|
-
if (/fn\s+\w+.*->/.test(code)) return "rust";
|
|
1808
|
-
return "javascript";
|
|
1809
|
-
}
|
|
1810
|
-
getSeverityIcon(severity) {
|
|
1811
|
-
const icons = {
|
|
1812
|
-
critical: "\u{1F534}",
|
|
1813
|
-
serious: "\u{1F7E0}",
|
|
1814
|
-
moderate: "\u{1F7E1}",
|
|
1815
|
-
low: "\u{1F535}",
|
|
1816
|
-
unknown: "\u26AA"
|
|
1817
|
-
};
|
|
1818
|
-
return icons[severity] || "\u26AA";
|
|
1819
|
-
}
|
|
1820
437
|
getHelpText() {
|
|
1821
438
|
return `
|
|
1822
439
|
${"\u2501".repeat(60)}
|
|
1823
|
-
\u{
|
|
440
|
+
\u{1F527} TRIE FIX - AI-POWERED CODE FIXING
|
|
1824
441
|
${"\u2501".repeat(60)}
|
|
1825
442
|
|
|
1826
443
|
## Usage
|
|
1827
444
|
|
|
1828
|
-
###
|
|
445
|
+
### Fix issues from a scan:
|
|
1829
446
|
\`\`\`
|
|
1830
|
-
|
|
447
|
+
trie_fix issueIds:["issue-1", "issue-2"]
|
|
1831
448
|
\`\`\`
|
|
1832
449
|
|
|
1833
|
-
###
|
|
1834
|
-
\`\`\`
|
|
1835
|
-
trie_explain type:"issue" target:"SQL injection vulnerability"
|
|
1836
|
-
\`\`\`
|
|
1837
|
-
or with file context:
|
|
450
|
+
### Auto-fix all high-confidence issues:
|
|
1838
451
|
\`\`\`
|
|
1839
|
-
|
|
452
|
+
trie_fix autoApprove:true
|
|
1840
453
|
\`\`\`
|
|
1841
454
|
|
|
1842
|
-
###
|
|
455
|
+
### Fix specific file and line:
|
|
1843
456
|
\`\`\`
|
|
1844
|
-
|
|
457
|
+
trie_fix file:"src/app.ts" line:42 issue:"SQL injection" fix:"Use parameterized query"
|
|
1845
458
|
\`\`\`
|
|
1846
459
|
|
|
1847
|
-
###
|
|
460
|
+
### Preview fixes without applying:
|
|
1848
461
|
\`\`\`
|
|
1849
|
-
|
|
462
|
+
trie_fix dryRun:true
|
|
1850
463
|
\`\`\`
|
|
1851
|
-
|
|
464
|
+
|
|
465
|
+
### View pending fixes:
|
|
1852
466
|
\`\`\`
|
|
1853
|
-
|
|
467
|
+
trie_fix
|
|
1854
468
|
\`\`\`
|
|
1855
469
|
|
|
1856
|
-
##
|
|
470
|
+
## Workflow
|
|
471
|
+
|
|
472
|
+
1. Run \`trie_scan\` to detect issues
|
|
473
|
+
2. Review the issues found
|
|
474
|
+
3. Run \`trie_fix\` to apply fixes
|
|
1857
475
|
|
|
1858
|
-
|
|
1859
|
-
|------|-------------|
|
|
1860
|
-
| code | What does this code do? |
|
|
1861
|
-
| issue | Why is this a problem? |
|
|
1862
|
-
| change | What's the impact? |
|
|
1863
|
-
| risk | What could go wrong? |
|
|
476
|
+
The AI will analyze each issue, generate the fix, and you can review before applying.
|
|
1864
477
|
`;
|
|
1865
478
|
}
|
|
479
|
+
detectLanguage(filePath) {
|
|
480
|
+
const ext = extname(filePath).toLowerCase();
|
|
481
|
+
const langMap = {
|
|
482
|
+
".ts": "typescript",
|
|
483
|
+
".tsx": "tsx",
|
|
484
|
+
".js": "javascript",
|
|
485
|
+
".jsx": "jsx",
|
|
486
|
+
".py": "python",
|
|
487
|
+
".go": "go",
|
|
488
|
+
".rs": "rust"
|
|
489
|
+
};
|
|
490
|
+
return langMap[ext] || "plaintext";
|
|
491
|
+
}
|
|
1866
492
|
};
|
|
1867
493
|
|
|
1868
494
|
// src/tools/test.ts
|
|
1869
|
-
import { readFile as
|
|
1870
|
-
import { existsSync as
|
|
1871
|
-
import { extname as
|
|
495
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
496
|
+
import { existsSync as existsSync2 } from "fs";
|
|
497
|
+
import { extname as extname2, relative as relative2, resolve as resolve2, isAbsolute as isAbsolute2, dirname, basename, join } from "path";
|
|
1872
498
|
var TrieTestTool = class {
|
|
1873
499
|
async execute(args) {
|
|
1874
500
|
const { action, files, framework, style = "unit" } = args || {};
|
|
@@ -1921,15 +547,15 @@ ${"\u2501".repeat(60)}
|
|
|
1921
547
|
const workDir = getWorkingDirectory(void 0, true);
|
|
1922
548
|
const allUnits = [];
|
|
1923
549
|
for (const file of files) {
|
|
1924
|
-
const resolvedPath =
|
|
1925
|
-
if (!
|
|
550
|
+
const resolvedPath = isAbsolute2(file) ? file : resolve2(workDir, file);
|
|
551
|
+
if (!existsSync2(resolvedPath)) {
|
|
1926
552
|
output += `[!] File not found: ${file}
|
|
1927
553
|
`;
|
|
1928
554
|
continue;
|
|
1929
555
|
}
|
|
1930
|
-
const code = await
|
|
556
|
+
const code = await readFile2(resolvedPath, "utf-8");
|
|
1931
557
|
const language = this.detectLanguage(resolvedPath);
|
|
1932
|
-
const relativePath =
|
|
558
|
+
const relativePath = relative2(workDir, resolvedPath);
|
|
1933
559
|
const units = this.extractTestableUnits(code, language);
|
|
1934
560
|
allUnits.push({ file: relativePath, units });
|
|
1935
561
|
output += `### \u{1F4C4} ${relativePath}
|
|
@@ -1960,7 +586,7 @@ ${"\u2501".repeat(60)}
|
|
|
1960
586
|
`;
|
|
1961
587
|
for (const { file, units } of allUnits) {
|
|
1962
588
|
if (units.length === 0) continue;
|
|
1963
|
-
const code = await
|
|
589
|
+
const code = await readFile2(resolve2(workDir, file), "utf-8");
|
|
1964
590
|
const language = this.detectLanguage(file);
|
|
1965
591
|
const prompt = getPrompt("test", "generate", {
|
|
1966
592
|
code,
|
|
@@ -2005,21 +631,21 @@ ${"\u2501".repeat(60)}
|
|
|
2005
631
|
`;
|
|
2006
632
|
const workDir = getWorkingDirectory(void 0, true);
|
|
2007
633
|
for (const file of files) {
|
|
2008
|
-
const resolvedPath =
|
|
2009
|
-
if (!
|
|
634
|
+
const resolvedPath = isAbsolute2(file) ? file : resolve2(workDir, file);
|
|
635
|
+
if (!existsSync2(resolvedPath)) {
|
|
2010
636
|
output += `[!] File not found: ${file}
|
|
2011
637
|
`;
|
|
2012
638
|
continue;
|
|
2013
639
|
}
|
|
2014
|
-
const code = await
|
|
640
|
+
const code = await readFile2(resolvedPath, "utf-8");
|
|
2015
641
|
const language = this.detectLanguage(resolvedPath);
|
|
2016
|
-
const relativePath =
|
|
642
|
+
const relativePath = relative2(workDir, resolvedPath);
|
|
2017
643
|
const units = this.extractTestableUnits(code, language);
|
|
2018
644
|
const testFile = await this.findTestFile(resolvedPath);
|
|
2019
645
|
let testCode = "";
|
|
2020
646
|
let testedUnits = [];
|
|
2021
647
|
if (testFile) {
|
|
2022
|
-
testCode = await
|
|
648
|
+
testCode = await readFile2(testFile, "utf-8");
|
|
2023
649
|
testedUnits = this.findTestedUnits(testCode, units.map((u) => u.name));
|
|
2024
650
|
}
|
|
2025
651
|
const coverage = units.length > 0 ? Math.round(testedUnits.length / units.length * 100) : 0;
|
|
@@ -2030,7 +656,7 @@ ${"\u2501".repeat(60)}
|
|
|
2030
656
|
output += `**Coverage:** ${coverageIcon} ${coverage}% (${testedUnits.length}/${units.length} units)
|
|
2031
657
|
`;
|
|
2032
658
|
if (testFile) {
|
|
2033
|
-
output += `**Test file:** \`${
|
|
659
|
+
output += `**Test file:** \`${relative2(workDir, testFile)}\`
|
|
2034
660
|
`;
|
|
2035
661
|
} else {
|
|
2036
662
|
output += `**Test file:** \u274C Not found
|
|
@@ -2087,14 +713,14 @@ ${"\u2501".repeat(60)}
|
|
|
2087
713
|
`;
|
|
2088
714
|
const workDir = getWorkingDirectory(void 0, true);
|
|
2089
715
|
for (const file of files) {
|
|
2090
|
-
const resolvedPath =
|
|
2091
|
-
if (!
|
|
716
|
+
const resolvedPath = isAbsolute2(file) ? file : resolve2(workDir, file);
|
|
717
|
+
if (!existsSync2(resolvedPath)) {
|
|
2092
718
|
output += `[!] File not found: ${file}
|
|
2093
719
|
`;
|
|
2094
720
|
continue;
|
|
2095
721
|
}
|
|
2096
|
-
const code = await
|
|
2097
|
-
const relativePath =
|
|
722
|
+
const code = await readFile2(resolvedPath, "utf-8");
|
|
723
|
+
const relativePath = relative2(workDir, resolvedPath);
|
|
2098
724
|
const patterns = this.detectTestablePatterns(code);
|
|
2099
725
|
output += `### \u{1F4C4} ${relativePath}
|
|
2100
726
|
|
|
@@ -2330,8 +956,8 @@ ${"\u2501".repeat(60)}
|
|
|
2330
956
|
}
|
|
2331
957
|
async findTestFile(sourcePath) {
|
|
2332
958
|
const dir = dirname(sourcePath);
|
|
2333
|
-
const base = basename(sourcePath,
|
|
2334
|
-
const ext =
|
|
959
|
+
const base = basename(sourcePath, extname2(sourcePath));
|
|
960
|
+
const ext = extname2(sourcePath);
|
|
2335
961
|
const patterns = [
|
|
2336
962
|
`${base}.test${ext}`,
|
|
2337
963
|
`${base}.spec${ext}`,
|
|
@@ -2340,15 +966,15 @@ ${"\u2501".repeat(60)}
|
|
|
2340
966
|
];
|
|
2341
967
|
for (const pattern of patterns) {
|
|
2342
968
|
const testPath = join(dir, pattern);
|
|
2343
|
-
if (
|
|
969
|
+
if (existsSync2(testPath)) {
|
|
2344
970
|
return testPath;
|
|
2345
971
|
}
|
|
2346
972
|
}
|
|
2347
973
|
const testsDir = join(dir, "__tests__");
|
|
2348
|
-
if (
|
|
974
|
+
if (existsSync2(testsDir)) {
|
|
2349
975
|
for (const pattern of patterns) {
|
|
2350
976
|
const testPath = join(testsDir, pattern);
|
|
2351
|
-
if (
|
|
977
|
+
if (existsSync2(testPath)) {
|
|
2352
978
|
return testPath;
|
|
2353
979
|
}
|
|
2354
980
|
}
|
|
@@ -2395,10 +1021,10 @@ ${"\u2501".repeat(60)}
|
|
|
2395
1021
|
}
|
|
2396
1022
|
async detectTestFramework() {
|
|
2397
1023
|
const workDir = getWorkingDirectory(void 0, true);
|
|
2398
|
-
const packagePath =
|
|
2399
|
-
if (
|
|
1024
|
+
const packagePath = resolve2(workDir, "package.json");
|
|
1025
|
+
if (existsSync2(packagePath)) {
|
|
2400
1026
|
try {
|
|
2401
|
-
const pkg = JSON.parse(await
|
|
1027
|
+
const pkg = JSON.parse(await readFile2(packagePath, "utf-8"));
|
|
2402
1028
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2403
1029
|
if (deps.vitest) return "vitest";
|
|
2404
1030
|
if (deps.jest) return "jest";
|
|
@@ -2406,13 +1032,13 @@ ${"\u2501".repeat(60)}
|
|
|
2406
1032
|
} catch {
|
|
2407
1033
|
}
|
|
2408
1034
|
}
|
|
2409
|
-
if (
|
|
1035
|
+
if (existsSync2(resolve2(workDir, "pytest.ini")) || existsSync2(resolve2(workDir, "pyproject.toml"))) {
|
|
2410
1036
|
return "pytest";
|
|
2411
1037
|
}
|
|
2412
1038
|
return "jest";
|
|
2413
1039
|
}
|
|
2414
1040
|
detectLanguage(filePath) {
|
|
2415
|
-
const ext =
|
|
1041
|
+
const ext = extname2(filePath).toLowerCase();
|
|
2416
1042
|
const langMap = {
|
|
2417
1043
|
".ts": "typescript",
|
|
2418
1044
|
".tsx": "tsx",
|
|
@@ -2424,625 +1050,50 @@ ${"\u2501".repeat(60)}
|
|
|
2424
1050
|
};
|
|
2425
1051
|
return langMap[ext] || "javascript";
|
|
2426
1052
|
}
|
|
2427
|
-
getHelpText() {
|
|
2428
|
-
return `
|
|
2429
|
-
${"\u2501".repeat(60)}
|
|
2430
|
-
\u{1F9EA} TRIE TEST - AI-POWERED TEST GENERATION
|
|
2431
|
-
${"\u2501".repeat(60)}
|
|
2432
|
-
|
|
2433
|
-
## Usage
|
|
2434
|
-
|
|
2435
|
-
### Generate tests:
|
|
2436
|
-
\`\`\`
|
|
2437
|
-
trie_test action:"generate" files:["src/utils.ts"]
|
|
2438
|
-
\`\`\`
|
|
2439
|
-
|
|
2440
|
-
### Analyze coverage:
|
|
2441
|
-
\`\`\`
|
|
2442
|
-
trie_test action:"coverage" files:["src/utils.ts"]
|
|
2443
|
-
\`\`\`
|
|
2444
|
-
|
|
2445
|
-
### Get test suggestions:
|
|
2446
|
-
\`\`\`
|
|
2447
|
-
trie_test action:"suggest" files:["src/app.ts"]
|
|
2448
|
-
\`\`\`
|
|
2449
|
-
|
|
2450
|
-
### Get run commands:
|
|
2451
|
-
\`\`\`
|
|
2452
|
-
trie_test action:"run" files:["src/utils.test.ts"]
|
|
2453
|
-
\`\`\`
|
|
2454
|
-
|
|
2455
|
-
## Options
|
|
2456
|
-
|
|
2457
|
-
| Option | Values | Description |
|
|
2458
|
-
|--------|--------|-------------|
|
|
2459
|
-
| action | generate, coverage, suggest, run | What to do |
|
|
2460
|
-
| files | Array of paths | Files to analyze |
|
|
2461
|
-
| framework | jest, vitest, mocha, pytest | Test framework |
|
|
2462
|
-
| style | unit, integration, e2e, all | Test style |
|
|
2463
|
-
`;
|
|
2464
|
-
}
|
|
2465
|
-
};
|
|
2466
|
-
|
|
2467
|
-
// src/tools/watch.ts
|
|
2468
|
-
import { watch, existsSync as existsSync4, readFileSync } from "fs";
|
|
2469
|
-
import { stat, readFile as readFile4 } from "fs/promises";
|
|
2470
|
-
import { join as join2, extname as extname4, basename as basename2 } from "path";
|
|
2471
|
-
|
|
2472
|
-
// src/extraction/signal-extractor.ts
|
|
2473
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
2474
|
-
var EXTRACTION_PROMPT = `You are a signal extraction system. Your job is to extract structured information from raw content.
|
|
2475
|
-
|
|
2476
|
-
Extract:
|
|
2477
|
-
1. DECISIONS - Clear choices made during development
|
|
2478
|
-
- What was decided
|
|
2479
|
-
- Why it was decided (reasoning/tradeoffs)
|
|
2480
|
-
- What alternatives were considered but NOT chosen
|
|
2481
|
-
- Which files are affected
|
|
2482
|
-
|
|
2483
|
-
2. FACTS - Concrete, verifiable information
|
|
2484
|
-
- Technical constraints (e.g., "Stripe requires TLS 1.2+")
|
|
2485
|
-
- API requirements
|
|
2486
|
-
- Business rules
|
|
2487
|
-
- Dependencies
|
|
2488
|
-
|
|
2489
|
-
3. BLOCKERS - Things preventing progress
|
|
2490
|
-
- What's blocked
|
|
2491
|
-
- Impact level (critical/high/medium/low)
|
|
2492
|
-
- What areas are affected
|
|
2493
|
-
|
|
2494
|
-
4. QUESTIONS - Open items needing resolution
|
|
2495
|
-
- What's unclear
|
|
2496
|
-
- Context around the question
|
|
2497
|
-
|
|
2498
|
-
CRITICAL: Extract rich metadata:
|
|
2499
|
-
- Tags: Use specific, searchable tags (e.g., "auth", "payments", "eu-compliance", "validation")
|
|
2500
|
-
- Files: Full paths when mentioned (e.g., "src/auth/validator.ts")
|
|
2501
|
-
- Tradeoffs: What was considered but rejected
|
|
2502
|
-
- Related terms: Alternative names/keywords (e.g., "password" + "credentials" + "auth")
|
|
2503
|
-
|
|
2504
|
-
Format as JSON:
|
|
2505
|
-
{
|
|
2506
|
-
"decisions": [{
|
|
2507
|
-
"decision": "Use bcrypt for password hashing",
|
|
2508
|
-
"context": "Security requirement for user authentication",
|
|
2509
|
-
"reasoning": "Industry standard, resistant to GPU attacks",
|
|
2510
|
-
"files": ["src/auth/hash.ts", "src/models/user.ts"],
|
|
2511
|
-
"tags": ["security", "auth", "passwords", "encryption"],
|
|
2512
|
-
"tradeoffs": ["Considered argon2 but bcrypt has better library support"]
|
|
2513
|
-
}],
|
|
2514
|
-
"facts": [{
|
|
2515
|
-
"fact": "Stripe requires TLS 1.2+ for all API calls",
|
|
2516
|
-
"source": "Stripe API docs",
|
|
2517
|
-
"tags": ["payments", "stripe", "security", "api"],
|
|
2518
|
-
"confidence": 0.95
|
|
2519
|
-
}],
|
|
2520
|
-
"blockers": [{
|
|
2521
|
-
"blocker": "Missing VAT calculation endpoint",
|
|
2522
|
-
"impact": "high",
|
|
2523
|
-
"affectedAreas": ["checkout", "eu-payments"],
|
|
2524
|
-
"tags": ["payments", "eu", "compliance", "vat"]
|
|
2525
|
-
}],
|
|
2526
|
-
"questions": [{
|
|
2527
|
-
"question": "Should we cache user sessions in Redis or memory?",
|
|
2528
|
-
"context": "Performance optimization for auth layer",
|
|
2529
|
-
"tags": ["auth", "performance", "caching", "sessions"]
|
|
2530
|
-
}]
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
Be specific with tags. Use concrete technical terms. Extract ALL file paths mentioned.
|
|
2534
|
-
Empty arrays are fine if nothing to extract.`;
|
|
2535
|
-
var SignalExtractor = class {
|
|
2536
|
-
client = null;
|
|
2537
|
-
model;
|
|
2538
|
-
constructor(model = "claude-3-haiku-20240307") {
|
|
2539
|
-
this.model = model;
|
|
2540
|
-
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
2541
|
-
if (apiKey) {
|
|
2542
|
-
this.client = new Anthropic({ apiKey });
|
|
2543
|
-
}
|
|
2544
|
-
}
|
|
2545
|
-
/**
|
|
2546
|
-
* Extract structured signals from raw content
|
|
2547
|
-
*/
|
|
2548
|
-
async extract(content, sourceType, sourceId) {
|
|
2549
|
-
if (!this.client) {
|
|
2550
|
-
return this.basicExtraction(content, sourceType, sourceId);
|
|
2551
|
-
}
|
|
2552
|
-
try {
|
|
2553
|
-
const response = await this.client.messages.create({
|
|
2554
|
-
model: this.model,
|
|
2555
|
-
max_tokens: 2048,
|
|
2556
|
-
temperature: 0.3,
|
|
2557
|
-
messages: [{
|
|
2558
|
-
role: "user",
|
|
2559
|
-
content: `${EXTRACTION_PROMPT}
|
|
2560
|
-
|
|
2561
|
-
Content to analyze:
|
|
2562
|
-
${content}`
|
|
2563
|
-
}]
|
|
2564
|
-
});
|
|
2565
|
-
const firstBlock = response.content[0];
|
|
2566
|
-
const text = firstBlock && firstBlock.type === "text" ? firstBlock.text : "";
|
|
2567
|
-
const extracted = this.parseExtraction(text);
|
|
2568
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2569
|
-
const metadata = {
|
|
2570
|
-
extractedAt: now,
|
|
2571
|
-
sourceType,
|
|
2572
|
-
extractionModel: this.model
|
|
2573
|
-
};
|
|
2574
|
-
if (sourceId !== void 0) {
|
|
2575
|
-
metadata.sourceId = sourceId;
|
|
2576
|
-
}
|
|
2577
|
-
const signal = {
|
|
2578
|
-
decisions: extracted.decisions.map((d, i) => ({
|
|
2579
|
-
id: `dec-${Date.now()}-${i}`,
|
|
2580
|
-
decision: d.decision || "",
|
|
2581
|
-
context: d.context || "",
|
|
2582
|
-
files: d.files || [],
|
|
2583
|
-
tags: d.tags || [],
|
|
2584
|
-
...d,
|
|
2585
|
-
when: now,
|
|
2586
|
-
status: "active"
|
|
2587
|
-
})),
|
|
2588
|
-
facts: extracted.facts.map((f, i) => ({
|
|
2589
|
-
id: `fact-${Date.now()}-${i}`,
|
|
2590
|
-
fact: f.fact || "",
|
|
2591
|
-
source: f.source || sourceType,
|
|
2592
|
-
tags: f.tags || [],
|
|
2593
|
-
confidence: f.confidence ?? 0.8,
|
|
2594
|
-
...f,
|
|
2595
|
-
when: now
|
|
2596
|
-
})),
|
|
2597
|
-
blockers: extracted.blockers.map((b, i) => ({
|
|
2598
|
-
id: `block-${Date.now()}-${i}`,
|
|
2599
|
-
blocker: b.blocker || "",
|
|
2600
|
-
impact: b.impact || "medium",
|
|
2601
|
-
affectedAreas: b.affectedAreas || [],
|
|
2602
|
-
tags: b.tags || [],
|
|
2603
|
-
...b,
|
|
2604
|
-
when: now
|
|
2605
|
-
})),
|
|
2606
|
-
questions: extracted.questions.map((q, i) => ({
|
|
2607
|
-
id: `q-${Date.now()}-${i}`,
|
|
2608
|
-
question: q.question || "",
|
|
2609
|
-
context: q.context || "",
|
|
2610
|
-
tags: q.tags || [],
|
|
2611
|
-
...q,
|
|
2612
|
-
when: now
|
|
2613
|
-
})),
|
|
2614
|
-
metadata
|
|
2615
|
-
};
|
|
2616
|
-
return signal;
|
|
2617
|
-
} catch (error) {
|
|
2618
|
-
console.error("Extraction failed, using basic extraction:", error);
|
|
2619
|
-
return this.basicExtraction(content, sourceType, sourceId);
|
|
2620
|
-
}
|
|
2621
|
-
}
|
|
2622
|
-
/**
|
|
2623
|
-
* Parse extraction from model response
|
|
2624
|
-
*/
|
|
2625
|
-
parseExtraction(text) {
|
|
2626
|
-
try {
|
|
2627
|
-
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
2628
|
-
if (jsonMatch) {
|
|
2629
|
-
return JSON.parse(jsonMatch[0]);
|
|
2630
|
-
}
|
|
2631
|
-
} catch (e) {
|
|
2632
|
-
}
|
|
2633
|
-
return {
|
|
2634
|
-
decisions: [],
|
|
2635
|
-
facts: [],
|
|
2636
|
-
blockers: [],
|
|
2637
|
-
questions: []
|
|
2638
|
-
};
|
|
2639
|
-
}
|
|
2640
|
-
/**
|
|
2641
|
-
* Basic extraction without AI (fallback)
|
|
2642
|
-
*/
|
|
2643
|
-
basicExtraction(content, sourceType, sourceId) {
|
|
2644
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2645
|
-
const hasDecision = /\b(decided|decision|chose|picked)\b/i.test(content);
|
|
2646
|
-
const hasBlocker = /\b(blocked|blocker|blocked by|can't|cannot|unable)\b/i.test(content);
|
|
2647
|
-
const hasQuestion = /\?|what|how|why|should we/i.test(content);
|
|
2648
|
-
const decisions = [];
|
|
2649
|
-
const facts = [];
|
|
2650
|
-
const blockers = [];
|
|
2651
|
-
const questions = [];
|
|
2652
|
-
if (hasDecision) {
|
|
2653
|
-
decisions.push({
|
|
2654
|
-
id: `dec-${Date.now()}`,
|
|
2655
|
-
decision: content.substring(0, 200),
|
|
2656
|
-
context: sourceType,
|
|
2657
|
-
when: now,
|
|
2658
|
-
files: [],
|
|
2659
|
-
tags: [sourceType],
|
|
2660
|
-
status: "active"
|
|
2661
|
-
});
|
|
2662
|
-
}
|
|
2663
|
-
if (hasBlocker) {
|
|
2664
|
-
blockers.push({
|
|
2665
|
-
id: `block-${Date.now()}`,
|
|
2666
|
-
blocker: content.substring(0, 200),
|
|
2667
|
-
impact: "medium",
|
|
2668
|
-
affectedAreas: [],
|
|
2669
|
-
when: now,
|
|
2670
|
-
tags: [sourceType]
|
|
2671
|
-
});
|
|
2672
|
-
}
|
|
2673
|
-
if (hasQuestion) {
|
|
2674
|
-
questions.push({
|
|
2675
|
-
id: `q-${Date.now()}`,
|
|
2676
|
-
question: content.substring(0, 200),
|
|
2677
|
-
context: sourceType,
|
|
2678
|
-
when: now,
|
|
2679
|
-
tags: [sourceType]
|
|
2680
|
-
});
|
|
2681
|
-
}
|
|
2682
|
-
const metadata = {
|
|
2683
|
-
extractedAt: now,
|
|
2684
|
-
sourceType,
|
|
2685
|
-
extractionModel: "basic"
|
|
2686
|
-
};
|
|
2687
|
-
if (sourceId !== void 0) {
|
|
2688
|
-
metadata.sourceId = sourceId;
|
|
2689
|
-
}
|
|
2690
|
-
return {
|
|
2691
|
-
decisions,
|
|
2692
|
-
facts,
|
|
2693
|
-
blockers,
|
|
2694
|
-
questions,
|
|
2695
|
-
metadata
|
|
2696
|
-
};
|
|
2697
|
-
}
|
|
2698
|
-
/**
|
|
2699
|
-
* Extract from incident report (trie tell)
|
|
2700
|
-
*/
|
|
2701
|
-
async extractFromIncident(incidentText) {
|
|
2702
|
-
return this.extract(incidentText, "incident");
|
|
2703
|
-
}
|
|
2704
|
-
/**
|
|
2705
|
-
* Extract from commit message and diff
|
|
2706
|
-
*/
|
|
2707
|
-
async extractFromCommit(message, diff, commitId) {
|
|
2708
|
-
const content = diff ? `${message}
|
|
2709
|
-
|
|
2710
|
-
Changes:
|
|
2711
|
-
${diff}` : message;
|
|
2712
|
-
return this.extract(content, "commit", commitId);
|
|
2713
|
-
}
|
|
2714
|
-
/**
|
|
2715
|
-
* Extract from PR description and comments
|
|
2716
|
-
*/
|
|
2717
|
-
async extractFromPR(title, description, comments, prNumber) {
|
|
2718
|
-
const content = `
|
|
2719
|
-
Title: ${title}
|
|
2720
|
-
|
|
2721
|
-
Description:
|
|
2722
|
-
${description}
|
|
2723
|
-
|
|
2724
|
-
Comments:
|
|
2725
|
-
${comments.join("\n\n")}
|
|
2726
|
-
`.trim();
|
|
2727
|
-
return this.extract(content, "pr", prNumber);
|
|
2728
|
-
}
|
|
2729
|
-
};
|
|
2730
|
-
|
|
2731
|
-
// src/extraction/metadata-enricher.ts
|
|
2732
|
-
var MetadataEnricher = class {
|
|
2733
|
-
tagSynonyms;
|
|
2734
|
-
constructor() {
|
|
2735
|
-
this.tagSynonyms = this.initializeTagSynonyms();
|
|
2736
|
-
}
|
|
2737
|
-
/**
|
|
2738
|
-
* Enrich an extracted signal with additional metadata
|
|
2739
|
-
*/
|
|
2740
|
-
async enrichSignal(signal, context) {
|
|
2741
|
-
const gitContext = {};
|
|
2742
|
-
if (context?.gitBranch) gitContext.branch = context.gitBranch;
|
|
2743
|
-
if (context?.gitCommit) gitContext.commit = context.gitCommit;
|
|
2744
|
-
const metadata = {
|
|
2745
|
-
relatedFiles: await this.findRelatedFiles(signal),
|
|
2746
|
-
dependencies: await this.extractDependencies(signal),
|
|
2747
|
-
expandedTags: this.expandTags(signal),
|
|
2748
|
-
codebaseArea: this.inferCodebaseArea(signal),
|
|
2749
|
-
domain: this.inferDomain(signal),
|
|
2750
|
-
relatedDecisions: [],
|
|
2751
|
-
// Will be populated by storage layer
|
|
2752
|
-
extractedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2753
|
-
};
|
|
2754
|
-
if (Object.keys(gitContext).length > 0) {
|
|
2755
|
-
metadata.gitContext = gitContext;
|
|
2756
|
-
}
|
|
2757
|
-
return {
|
|
2758
|
-
signal,
|
|
2759
|
-
metadata
|
|
2760
|
-
};
|
|
2761
|
-
}
|
|
2762
|
-
/**
|
|
2763
|
-
* Expand tags with synonyms and related terms
|
|
2764
|
-
* This is our "semantic" layer without embeddings
|
|
2765
|
-
*/
|
|
2766
|
-
expandTags(signal) {
|
|
2767
|
-
const allTags = /* @__PURE__ */ new Set();
|
|
2768
|
-
for (const decision of signal.decisions) {
|
|
2769
|
-
decision.tags.forEach((tag) => allTags.add(tag.toLowerCase()));
|
|
2770
|
-
}
|
|
2771
|
-
for (const fact of signal.facts) {
|
|
2772
|
-
fact.tags.forEach((tag) => allTags.add(tag.toLowerCase()));
|
|
2773
|
-
}
|
|
2774
|
-
for (const blocker of signal.blockers) {
|
|
2775
|
-
blocker.tags.forEach((tag) => allTags.add(tag.toLowerCase()));
|
|
2776
|
-
}
|
|
2777
|
-
for (const question of signal.questions) {
|
|
2778
|
-
question.tags.forEach((tag) => allTags.add(tag.toLowerCase()));
|
|
2779
|
-
}
|
|
2780
|
-
const expandedTags = new Set(allTags);
|
|
2781
|
-
for (const tag of allTags) {
|
|
2782
|
-
const synonyms = this.tagSynonyms.get(tag) || [];
|
|
2783
|
-
synonyms.forEach((syn) => expandedTags.add(syn));
|
|
2784
|
-
}
|
|
2785
|
-
return Array.from(expandedTags);
|
|
2786
|
-
}
|
|
2787
|
-
/**
|
|
2788
|
-
* Find related files based on signal content
|
|
2789
|
-
*/
|
|
2790
|
-
async findRelatedFiles(signal) {
|
|
2791
|
-
const relatedFiles = /* @__PURE__ */ new Set();
|
|
2792
|
-
for (const decision of signal.decisions) {
|
|
2793
|
-
decision.files.forEach((file) => relatedFiles.add(file));
|
|
2794
|
-
}
|
|
2795
|
-
const inferredFiles = await this.inferFilesFromTags(signal);
|
|
2796
|
-
inferredFiles.forEach((file) => relatedFiles.add(file));
|
|
2797
|
-
return Array.from(relatedFiles);
|
|
2798
|
-
}
|
|
2799
|
-
/**
|
|
2800
|
-
* Extract dependencies from signal
|
|
2801
|
-
*/
|
|
2802
|
-
async extractDependencies(signal) {
|
|
2803
|
-
const dependencies = /* @__PURE__ */ new Set();
|
|
2804
|
-
const allText = [
|
|
2805
|
-
...signal.decisions.map((d) => `${d.decision} ${d.context} ${d.reasoning}`),
|
|
2806
|
-
...signal.facts.map((f) => `${f.fact} ${f.source}`)
|
|
2807
|
-
].join(" ");
|
|
2808
|
-
const packagePatterns = [
|
|
2809
|
-
/\b(react|vue|angular|next|express|fastify|stripe|bcrypt|jwt|redis|prisma)\b/gi
|
|
2810
|
-
];
|
|
2811
|
-
for (const pattern of packagePatterns) {
|
|
2812
|
-
const matches = allText.match(pattern) || [];
|
|
2813
|
-
matches.forEach((dep) => dependencies.add(dep.toLowerCase()));
|
|
2814
|
-
}
|
|
2815
|
-
return Array.from(dependencies);
|
|
2816
|
-
}
|
|
2817
|
-
/**
|
|
2818
|
-
* Infer codebase area from file paths and tags
|
|
2819
|
-
*/
|
|
2820
|
-
inferCodebaseArea(signal) {
|
|
2821
|
-
const areas = /* @__PURE__ */ new Set();
|
|
2822
|
-
for (const decision of signal.decisions) {
|
|
2823
|
-
for (const file of decision.files) {
|
|
2824
|
-
const area = this.filePathToArea(file);
|
|
2825
|
-
if (area) areas.add(area);
|
|
2826
|
-
}
|
|
2827
|
-
}
|
|
2828
|
-
const areaKeywords = ["frontend", "backend", "api", "ui", "database", "auth", "payments"];
|
|
2829
|
-
const allTags = this.expandTags(signal);
|
|
2830
|
-
for (const tag of allTags) {
|
|
2831
|
-
if (areaKeywords.includes(tag)) {
|
|
2832
|
-
areas.add(tag);
|
|
2833
|
-
}
|
|
2834
|
-
}
|
|
2835
|
-
return Array.from(areas);
|
|
2836
|
-
}
|
|
2837
|
-
/**
|
|
2838
|
-
* Infer domain from tags and content
|
|
2839
|
-
*/
|
|
2840
|
-
inferDomain(signal) {
|
|
2841
|
-
const domains = /* @__PURE__ */ new Set();
|
|
2842
|
-
const domainKeywords = [
|
|
2843
|
-
"payments",
|
|
2844
|
-
"billing",
|
|
2845
|
-
"compliance",
|
|
2846
|
-
"security",
|
|
2847
|
-
"auth",
|
|
2848
|
-
"analytics",
|
|
2849
|
-
"notifications",
|
|
2850
|
-
"messaging",
|
|
2851
|
-
"search"
|
|
2852
|
-
];
|
|
2853
|
-
const allTags = this.expandTags(signal);
|
|
2854
|
-
for (const tag of allTags) {
|
|
2855
|
-
if (domainKeywords.includes(tag)) {
|
|
2856
|
-
domains.add(tag);
|
|
2857
|
-
}
|
|
2858
|
-
}
|
|
2859
|
-
return Array.from(domains);
|
|
2860
|
-
}
|
|
2861
|
-
/**
|
|
2862
|
-
* Convert file path to codebase area
|
|
2863
|
-
*/
|
|
2864
|
-
filePathToArea(filePath) {
|
|
2865
|
-
const normalized = filePath.toLowerCase();
|
|
2866
|
-
if (normalized.includes("/frontend/") || normalized.includes("/client/") || normalized.includes("/ui/")) {
|
|
2867
|
-
return "frontend";
|
|
2868
|
-
}
|
|
2869
|
-
if (normalized.includes("/backend/") || normalized.includes("/server/") || normalized.includes("/api/")) {
|
|
2870
|
-
return "backend";
|
|
2871
|
-
}
|
|
2872
|
-
if (normalized.includes("/database/") || normalized.includes("/models/") || normalized.includes("/schema/")) {
|
|
2873
|
-
return "database";
|
|
2874
|
-
}
|
|
2875
|
-
if (normalized.includes("/auth/")) {
|
|
2876
|
-
return "auth";
|
|
2877
|
-
}
|
|
2878
|
-
return null;
|
|
2879
|
-
}
|
|
2880
|
-
/**
|
|
2881
|
-
* Infer related files from tags
|
|
2882
|
-
*/
|
|
2883
|
-
async inferFilesFromTags(_signal) {
|
|
2884
|
-
return [];
|
|
2885
|
-
}
|
|
2886
|
-
/**
|
|
2887
|
-
* Initialize tag synonyms and related terms
|
|
2888
|
-
* This is our "semantic" understanding without embeddings
|
|
2889
|
-
*/
|
|
2890
|
-
initializeTagSynonyms() {
|
|
2891
|
-
return /* @__PURE__ */ new Map([
|
|
2892
|
-
// Auth & Security
|
|
2893
|
-
["auth", ["authentication", "login", "signin", "credentials", "password"]],
|
|
2894
|
-
["password", ["credentials", "auth", "hashing", "bcrypt"]],
|
|
2895
|
-
["security", ["vulnerability", "exploit", "attack", "protection"]],
|
|
2896
|
-
["encryption", ["crypto", "hashing", "encoding", "security"]],
|
|
2897
|
-
// Payments
|
|
2898
|
-
["payments", ["billing", "checkout", "stripe", "pricing"]],
|
|
2899
|
-
["stripe", ["payments", "billing", "api", "checkout"]],
|
|
2900
|
-
["vat", ["tax", "eu", "compliance", "billing"]],
|
|
2901
|
-
// Database & Performance
|
|
2902
|
-
["database", ["db", "sql", "query", "storage", "persistence"]],
|
|
2903
|
-
["cache", ["caching", "redis", "memory", "performance"]],
|
|
2904
|
-
["performance", ["optimization", "speed", "latency", "cache"]],
|
|
2905
|
-
// Frontend
|
|
2906
|
-
["ui", ["frontend", "interface", "component", "view"]],
|
|
2907
|
-
["component", ["ui", "react", "vue", "frontend"]],
|
|
2908
|
-
["validation", ["form", "input", "error", "ui"]],
|
|
2909
|
-
// Backend & API
|
|
2910
|
-
["api", ["endpoint", "route", "backend", "server"]],
|
|
2911
|
-
["endpoint", ["api", "route", "url", "backend"]],
|
|
2912
|
-
["backend", ["server", "api", "service"]],
|
|
2913
|
-
// Compliance & Legal
|
|
2914
|
-
["compliance", ["gdpr", "hipaa", "legal", "regulation"]],
|
|
2915
|
-
["gdpr", ["compliance", "privacy", "eu", "data-protection"]],
|
|
2916
|
-
["privacy", ["gdpr", "compliance", "data-protection", "security"]]
|
|
2917
|
-
]);
|
|
2918
|
-
}
|
|
2919
|
-
};
|
|
2920
|
-
|
|
2921
|
-
// src/extraction/pipeline.ts
|
|
2922
|
-
import { randomBytes } from "crypto";
|
|
2923
|
-
var ExtractionPipeline = class {
|
|
2924
|
-
extractor;
|
|
2925
|
-
enricher;
|
|
2926
|
-
storage;
|
|
2927
|
-
workDir;
|
|
2928
|
-
constructor(options) {
|
|
2929
|
-
this.extractor = new SignalExtractor(options.anthropicApiKey);
|
|
2930
|
-
this.enricher = new MetadataEnricher();
|
|
2931
|
-
this.storage = getStorage(options.workingDirectory);
|
|
2932
|
-
this.workDir = options.workingDirectory;
|
|
2933
|
-
}
|
|
2934
|
-
/**
|
|
2935
|
-
* Process raw content through the entire pipeline
|
|
2936
|
-
*/
|
|
2937
|
-
async process(content, context) {
|
|
2938
|
-
console.log("\u{1F50D} Extracting signals from content...");
|
|
2939
|
-
let extractedSignal = await this.extractor.extract(content, context.sourceType, context.sourceId);
|
|
2940
|
-
extractedSignal = this.addIds(extractedSignal, context);
|
|
2941
|
-
console.log(` \u2713 Extracted ${extractedSignal.decisions.length} decisions, ${extractedSignal.facts.length} facts, ${extractedSignal.blockers.length} blockers, ${extractedSignal.questions.length} questions`);
|
|
2942
|
-
console.log("\u{1F3F7}\uFE0F Enriching with metadata...");
|
|
2943
|
-
const { metadata: enrichedMeta } = await this.enricher.enrichSignal(extractedSignal, {
|
|
2944
|
-
workingDirectory: this.workDir
|
|
2945
|
-
});
|
|
2946
|
-
if (enrichedMeta.expandedTags.length > 0) {
|
|
2947
|
-
console.log(` \u2713 Expanded tags: ${enrichedMeta.expandedTags.slice(0, 5).join(", ")}${enrichedMeta.expandedTags.length > 5 ? "..." : ""}`);
|
|
2948
|
-
}
|
|
2949
|
-
if (enrichedMeta.dependencies.length > 0) {
|
|
2950
|
-
console.log(` \u2713 Dependencies: ${enrichedMeta.dependencies.join(", ")}`);
|
|
2951
|
-
}
|
|
2952
|
-
if (enrichedMeta.codebaseArea.length > 0) {
|
|
2953
|
-
console.log(` \u2713 Codebase areas: ${enrichedMeta.codebaseArea.join(", ")}`);
|
|
2954
|
-
}
|
|
2955
|
-
if (enrichedMeta.domain.length > 0) {
|
|
2956
|
-
console.log(` \u2713 Domains: ${enrichedMeta.domain.join(", ")}`);
|
|
2957
|
-
}
|
|
2958
|
-
console.log("\u{1F4BE} Storing in decision ledger...");
|
|
2959
|
-
await this.storage.storeSignal(extractedSignal, {
|
|
2960
|
-
expandedTags: enrichedMeta.expandedTags,
|
|
2961
|
-
dependencies: enrichedMeta.dependencies,
|
|
2962
|
-
codebaseArea: enrichedMeta.codebaseArea,
|
|
2963
|
-
domain: enrichedMeta.domain
|
|
2964
|
-
});
|
|
2965
|
-
console.log("\u2705 Successfully stored in decision ledger");
|
|
2966
|
-
return extractedSignal;
|
|
2967
|
-
}
|
|
2968
|
-
/**
|
|
2969
|
-
* Add IDs and metadata to extracted signal
|
|
2970
|
-
*/
|
|
2971
|
-
addIds(signal, context) {
|
|
2972
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2973
|
-
const metadata = {
|
|
2974
|
-
extractedAt: now,
|
|
2975
|
-
sourceType: context.sourceType,
|
|
2976
|
-
extractionModel: "claude-haiku"
|
|
2977
|
-
};
|
|
2978
|
-
if (context.sourceId !== void 0) {
|
|
2979
|
-
metadata.sourceId = context.sourceId;
|
|
2980
|
-
}
|
|
2981
|
-
return {
|
|
2982
|
-
decisions: signal.decisions.map((d) => {
|
|
2983
|
-
const decision = {
|
|
2984
|
-
...d,
|
|
2985
|
-
id: d.id || this.generateId(),
|
|
2986
|
-
when: d.when || now,
|
|
2987
|
-
status: d.status || "active"
|
|
2988
|
-
};
|
|
2989
|
-
if (context.who !== void 0) {
|
|
2990
|
-
decision.who = d.who || context.who;
|
|
2991
|
-
}
|
|
2992
|
-
return decision;
|
|
2993
|
-
}),
|
|
2994
|
-
facts: signal.facts.map((f) => ({
|
|
2995
|
-
...f,
|
|
2996
|
-
id: f.id || this.generateId(),
|
|
2997
|
-
when: f.when || now,
|
|
2998
|
-
confidence: f.confidence ?? 0.8
|
|
2999
|
-
})),
|
|
3000
|
-
blockers: signal.blockers.map((b) => ({
|
|
3001
|
-
...b,
|
|
3002
|
-
id: b.id || this.generateId(),
|
|
3003
|
-
when: b.when || now
|
|
3004
|
-
})),
|
|
3005
|
-
questions: signal.questions.map((q) => ({
|
|
3006
|
-
...q,
|
|
3007
|
-
id: q.id || this.generateId(),
|
|
3008
|
-
when: q.when || now
|
|
3009
|
-
})),
|
|
3010
|
-
metadata
|
|
3011
|
-
};
|
|
3012
|
-
}
|
|
3013
|
-
/**
|
|
3014
|
-
* Generate a unique ID
|
|
3015
|
-
*/
|
|
3016
|
-
generateId() {
|
|
3017
|
-
return randomBytes(8).toString("hex");
|
|
3018
|
-
}
|
|
3019
|
-
/**
|
|
3020
|
-
* Initialize storage
|
|
3021
|
-
*/
|
|
3022
|
-
async initialize() {
|
|
3023
|
-
await this.storage.initialize();
|
|
3024
|
-
}
|
|
3025
|
-
/**
|
|
3026
|
-
* Close storage connections
|
|
3027
|
-
*/
|
|
3028
|
-
close() {
|
|
3029
|
-
this.storage.close();
|
|
1053
|
+
getHelpText() {
|
|
1054
|
+
return `
|
|
1055
|
+
${"\u2501".repeat(60)}
|
|
1056
|
+
\u{1F9EA} TRIE TEST - AI-POWERED TEST GENERATION
|
|
1057
|
+
${"\u2501".repeat(60)}
|
|
1058
|
+
|
|
1059
|
+
## Usage
|
|
1060
|
+
|
|
1061
|
+
### Generate tests:
|
|
1062
|
+
\`\`\`
|
|
1063
|
+
trie_test action:"generate" files:["src/utils.ts"]
|
|
1064
|
+
\`\`\`
|
|
1065
|
+
|
|
1066
|
+
### Analyze coverage:
|
|
1067
|
+
\`\`\`
|
|
1068
|
+
trie_test action:"coverage" files:["src/utils.ts"]
|
|
1069
|
+
\`\`\`
|
|
1070
|
+
|
|
1071
|
+
### Get test suggestions:
|
|
1072
|
+
\`\`\`
|
|
1073
|
+
trie_test action:"suggest" files:["src/app.ts"]
|
|
1074
|
+
\`\`\`
|
|
1075
|
+
|
|
1076
|
+
### Get run commands:
|
|
1077
|
+
\`\`\`
|
|
1078
|
+
trie_test action:"run" files:["src/utils.test.ts"]
|
|
1079
|
+
\`\`\`
|
|
1080
|
+
|
|
1081
|
+
## Options
|
|
1082
|
+
|
|
1083
|
+
| Option | Values | Description |
|
|
1084
|
+
|--------|--------|-------------|
|
|
1085
|
+
| action | generate, coverage, suggest, run | What to do |
|
|
1086
|
+
| files | Array of paths | Files to analyze |
|
|
1087
|
+
| framework | jest, vitest, mocha, pytest | Test framework |
|
|
1088
|
+
| style | unit, integration, e2e, all | Test style |
|
|
1089
|
+
`;
|
|
3030
1090
|
}
|
|
3031
1091
|
};
|
|
3032
|
-
async function processIncident(incidentDescription, options) {
|
|
3033
|
-
const pipeline = new ExtractionPipeline(options);
|
|
3034
|
-
await pipeline.initialize();
|
|
3035
|
-
try {
|
|
3036
|
-
return await pipeline.process(incidentDescription, {
|
|
3037
|
-
sourceType: "incident",
|
|
3038
|
-
sourceId: `incident-${Date.now()}`
|
|
3039
|
-
});
|
|
3040
|
-
} finally {
|
|
3041
|
-
pipeline.close();
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
1092
|
|
|
3045
1093
|
// src/tools/watch.ts
|
|
1094
|
+
import { watch, existsSync as existsSync3, readFileSync } from "fs";
|
|
1095
|
+
import { stat, readFile as readFile3 } from "fs/promises";
|
|
1096
|
+
import { join as join2, extname as extname3, basename as basename2 } from "path";
|
|
3046
1097
|
var WATCH_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
3047
1098
|
".ts",
|
|
3048
1099
|
".tsx",
|
|
@@ -3067,8 +1118,9 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
3067
1118
|
".turbo",
|
|
3068
1119
|
".cache"
|
|
3069
1120
|
]);
|
|
3070
|
-
var TrieWatchTool = class {
|
|
1121
|
+
var TrieWatchTool = class _TrieWatchTool {
|
|
3071
1122
|
extractionPipeline = null;
|
|
1123
|
+
static DEFAULT_HOURLY_TOKEN_LIMIT = 5e4;
|
|
3072
1124
|
state = {
|
|
3073
1125
|
isRunning: false,
|
|
3074
1126
|
lastScan: /* @__PURE__ */ new Map(),
|
|
@@ -3076,13 +1128,18 @@ var TrieWatchTool = class {
|
|
|
3076
1128
|
scanDebounceTimer: null,
|
|
3077
1129
|
issueCache: /* @__PURE__ */ new Map(),
|
|
3078
1130
|
totalIssuesFound: 0,
|
|
3079
|
-
// Autonomy state
|
|
3080
|
-
lastAutoCheck: 0,
|
|
3081
|
-
autoCheckInProgress: false,
|
|
3082
|
-
criticalIssueCount: 0,
|
|
3083
1131
|
filesScanned: 0,
|
|
3084
1132
|
nudgedFiles: /* @__PURE__ */ new Set(),
|
|
3085
|
-
nudges: []
|
|
1133
|
+
nudges: [],
|
|
1134
|
+
lastAutoScan: 0,
|
|
1135
|
+
autoScanInProgress: false,
|
|
1136
|
+
tokenBudget: {
|
|
1137
|
+
used: 0,
|
|
1138
|
+
windowStart: Date.now(),
|
|
1139
|
+
hourlyLimit: _TrieWatchTool.DEFAULT_HOURLY_TOKEN_LIMIT,
|
|
1140
|
+
scansSaved: 0
|
|
1141
|
+
},
|
|
1142
|
+
cleanFiles: /* @__PURE__ */ new Map()
|
|
3086
1143
|
};
|
|
3087
1144
|
watchers = /* @__PURE__ */ new Map();
|
|
3088
1145
|
streamingManager = void 0;
|
|
@@ -3204,17 +1261,17 @@ Your Trie agent is now autonomously watching and learning from your codebase.
|
|
|
3204
1261
|
return parts.some((p) => SKIP_DIRS.has(p) || p.startsWith(".") && p !== ".");
|
|
3205
1262
|
}
|
|
3206
1263
|
async watchDirectory(dir, debounceMs) {
|
|
3207
|
-
if (!
|
|
1264
|
+
if (!existsSync3(dir)) return;
|
|
3208
1265
|
try {
|
|
3209
1266
|
const dirStat = await stat(dir);
|
|
3210
1267
|
if (!dirStat.isDirectory()) return;
|
|
3211
1268
|
const watcher = watch(dir, { persistent: true, recursive: true }, (_eventType, filename) => {
|
|
3212
1269
|
if (!filename) return;
|
|
3213
1270
|
if (this.shouldSkipPath(filename)) return;
|
|
3214
|
-
const ext =
|
|
1271
|
+
const ext = extname3(filename).toLowerCase();
|
|
3215
1272
|
if (!WATCH_EXTENSIONS.has(ext)) return;
|
|
3216
1273
|
const fullPath = join2(dir, filename);
|
|
3217
|
-
if (!
|
|
1274
|
+
if (!existsSync3(fullPath)) return;
|
|
3218
1275
|
this.state.pendingFiles.add(fullPath);
|
|
3219
1276
|
if (this.state.scanDebounceTimer) {
|
|
3220
1277
|
clearTimeout(this.state.scanDebounceTimer);
|
|
@@ -3257,7 +1314,7 @@ Detected changes in ${files.length} file(s):`);
|
|
|
3257
1314
|
const fileContents = await Promise.all(
|
|
3258
1315
|
files.map(async (file) => {
|
|
3259
1316
|
try {
|
|
3260
|
-
const content = await
|
|
1317
|
+
const content = await readFile3(file, "utf-8");
|
|
3261
1318
|
return { file, content };
|
|
3262
1319
|
} catch {
|
|
3263
1320
|
return null;
|
|
@@ -3290,9 +1347,6 @@ ${f.content.slice(0, 1e3)}`
|
|
|
3290
1347
|
questions: signal.questions.length
|
|
3291
1348
|
});
|
|
3292
1349
|
}
|
|
3293
|
-
if (signal.blockers.length > 0 && !this.isQuiet()) {
|
|
3294
|
-
this.maybeAutoCheck(files);
|
|
3295
|
-
}
|
|
3296
1350
|
}
|
|
3297
1351
|
}
|
|
3298
1352
|
} catch (error) {
|
|
@@ -3301,6 +1355,9 @@ ${f.content.slice(0, 1e3)}`
|
|
|
3301
1355
|
}
|
|
3302
1356
|
}
|
|
3303
1357
|
}
|
|
1358
|
+
if (!this.isQuiet()) {
|
|
1359
|
+
this.autoScanFiles(files);
|
|
1360
|
+
}
|
|
3304
1361
|
if (this.streamingManager) {
|
|
3305
1362
|
for (const file of files) {
|
|
3306
1363
|
this.streamingManager.reportWatchChange(file);
|
|
@@ -3328,75 +1385,179 @@ ${f.content.slice(0, 1e3)}`
|
|
|
3328
1385
|
return false;
|
|
3329
1386
|
}
|
|
3330
1387
|
}
|
|
1388
|
+
static AUTO_SCAN_COOLDOWN_MS = 3e4;
|
|
1389
|
+
static CLEAN_FILE_COOLDOWN_MS = 3e5;
|
|
1390
|
+
// skip files clean-scanned in last 5 min
|
|
3331
1391
|
/**
|
|
3332
|
-
*
|
|
3333
|
-
*
|
|
1392
|
+
* Use the trie (context graph) to score how urgently a file needs scanning.
|
|
1393
|
+
* Higher score = more worth spending tokens on.
|
|
3334
1394
|
*/
|
|
3335
|
-
async
|
|
3336
|
-
|
|
3337
|
-
const
|
|
1395
|
+
async scoreScanPriority(file, graph, projectPath) {
|
|
1396
|
+
let score = 1;
|
|
1397
|
+
const lastClean = this.state.cleanFiles.get(file);
|
|
1398
|
+
if (lastClean && Date.now() - lastClean < _TrieWatchTool.CLEAN_FILE_COOLDOWN_MS) {
|
|
1399
|
+
return 0;
|
|
1400
|
+
}
|
|
1401
|
+
const fileNode = await graph.getNode("file", join2(projectPath, file));
|
|
1402
|
+
if (!fileNode) return score;
|
|
1403
|
+
const data = fileNode.data;
|
|
1404
|
+
const riskScores = { critical: 10, high: 6, medium: 2, low: 1 };
|
|
1405
|
+
score += riskScores[data.riskLevel] ?? 1;
|
|
1406
|
+
score += Math.min(data.incidentCount * 3, 12);
|
|
1407
|
+
if (data.changeCount > 10) score += 2;
|
|
1408
|
+
return score;
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Roll the token budget window if an hour has passed.
|
|
1412
|
+
* Returns remaining tokens in the current window.
|
|
1413
|
+
*/
|
|
1414
|
+
getRemainingBudget() {
|
|
1415
|
+
const budget = this.state.tokenBudget;
|
|
1416
|
+
const elapsed = Date.now() - budget.windowStart;
|
|
1417
|
+
if (elapsed > 36e5) {
|
|
1418
|
+
budget.used = 0;
|
|
1419
|
+
budget.windowStart = Date.now();
|
|
1420
|
+
}
|
|
1421
|
+
return Math.max(0, budget.hourlyLimit - budget.used);
|
|
1422
|
+
}
|
|
1423
|
+
recordTokenUsage(tokens) {
|
|
1424
|
+
this.state.tokenBudget.used += tokens;
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* AI-powered auto-scan, throttled by the trie.
|
|
1428
|
+
* Uses context graph risk data to decide which files are worth spending tokens on.
|
|
1429
|
+
*/
|
|
1430
|
+
async autoScanFiles(files) {
|
|
1431
|
+
if (!isAIAvailable()) return;
|
|
1432
|
+
if (this.state.autoScanInProgress) return;
|
|
1433
|
+
const now = Date.now();
|
|
1434
|
+
if (now - this.state.lastAutoScan < _TrieWatchTool.AUTO_SCAN_COOLDOWN_MS) return;
|
|
1435
|
+
const remaining = this.getRemainingBudget();
|
|
1436
|
+
if (remaining < 500) return;
|
|
1437
|
+
this.state.autoScanInProgress = true;
|
|
1438
|
+
this.state.lastAutoScan = now;
|
|
3338
1439
|
try {
|
|
3339
|
-
const
|
|
3340
|
-
|
|
3341
|
-
const
|
|
3342
|
-
|
|
3343
|
-
|
|
1440
|
+
const projectPath = getWorkingDirectory(void 0, true);
|
|
1441
|
+
const graph = new ContextGraph(projectPath);
|
|
1442
|
+
const scored = [];
|
|
1443
|
+
for (const file of files) {
|
|
1444
|
+
const relativePath = file.replace(projectPath + "/", "");
|
|
1445
|
+
const score = await this.scoreScanPriority(relativePath, graph, projectPath);
|
|
1446
|
+
if (score > 0) {
|
|
1447
|
+
scored.push({ file, relativePath, score });
|
|
1448
|
+
} else {
|
|
1449
|
+
this.state.tokenBudget.scansSaved++;
|
|
1450
|
+
}
|
|
3344
1451
|
}
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
1452
|
+
if (scored.length === 0) return;
|
|
1453
|
+
scored.sort((a, b) => b.score - a.score);
|
|
1454
|
+
const maxFiles = remaining > 2e4 ? 5 : remaining > 1e4 ? 3 : 1;
|
|
1455
|
+
const toScan = scored.slice(0, maxFiles);
|
|
1456
|
+
const fileContents = await Promise.all(
|
|
1457
|
+
toScan.map(async ({ file, relativePath }) => {
|
|
1458
|
+
try {
|
|
1459
|
+
const content = await readFile3(file, "utf-8");
|
|
1460
|
+
return { path: relativePath, content: content.slice(0, 3e3) };
|
|
1461
|
+
} catch {
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
})
|
|
3354
1465
|
);
|
|
3355
|
-
const
|
|
3356
|
-
|
|
3357
|
-
const
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
1466
|
+
const valid = fileContents.filter(Boolean);
|
|
1467
|
+
if (valid.length === 0) return;
|
|
1468
|
+
const filesBlock = valid.map(
|
|
1469
|
+
(f) => `### ${f.path}
|
|
1470
|
+
\`\`\`
|
|
1471
|
+
${f.content}
|
|
1472
|
+
\`\`\``
|
|
1473
|
+
).join("\n\n");
|
|
1474
|
+
const result = await runAIAnalysis({
|
|
1475
|
+
systemPrompt: `You are a code reviewer. Analyze the changed files below for bugs, security issues, logic errors, or risky patterns.
|
|
1476
|
+
|
|
1477
|
+
Reply ONLY with a JSON array of issues found. Each issue must have:
|
|
1478
|
+
- "file": relative file path
|
|
1479
|
+
- "severity": "critical" | "major" | "minor"
|
|
1480
|
+
- "description": 1-sentence description of the problem
|
|
1481
|
+
|
|
1482
|
+
If there are no issues, reply with an empty array: []
|
|
1483
|
+
|
|
1484
|
+
Do NOT include markdown fences or commentary. Output ONLY the JSON array.`,
|
|
1485
|
+
userPrompt: `Review these recently changed files:
|
|
1486
|
+
|
|
1487
|
+
${filesBlock}`,
|
|
1488
|
+
maxTokens: 1024,
|
|
1489
|
+
temperature: 0.2
|
|
3363
1490
|
});
|
|
3364
|
-
if (
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
1491
|
+
if (result.tokensUsed) {
|
|
1492
|
+
this.recordTokenUsage(result.tokensUsed.input + result.tokensUsed.output);
|
|
1493
|
+
}
|
|
1494
|
+
if (!result.success || !result.content.trim()) return;
|
|
1495
|
+
let issues = [];
|
|
1496
|
+
try {
|
|
1497
|
+
const cleaned = result.content.replace(/```json?\n?|\n?```/g, "").trim();
|
|
1498
|
+
issues = JSON.parse(cleaned);
|
|
1499
|
+
if (!Array.isArray(issues)) issues = [];
|
|
1500
|
+
} catch {
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1503
|
+
const issuedFiles = new Set(issues.map((i) => i.file));
|
|
1504
|
+
for (const { relativePath } of toScan) {
|
|
1505
|
+
if (!issuedFiles.has(relativePath)) {
|
|
1506
|
+
this.state.cleanFiles.set(relativePath, Date.now());
|
|
3375
1507
|
}
|
|
3376
1508
|
}
|
|
3377
|
-
|
|
3378
|
-
const
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
)
|
|
1509
|
+
if (issues.length === 0) return;
|
|
1510
|
+
for (const issue of issues.slice(0, 5)) {
|
|
1511
|
+
const severity = issue.severity === "critical" ? "critical" : issue.severity === "major" ? "major" : "minor";
|
|
1512
|
+
const incident = await graph.addNode("incident", {
|
|
1513
|
+
description: issue.description,
|
|
1514
|
+
severity,
|
|
1515
|
+
affectedUsers: null,
|
|
1516
|
+
duration: null,
|
|
1517
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1518
|
+
resolved: false,
|
|
1519
|
+
resolution: null,
|
|
1520
|
+
fixChangeId: null,
|
|
1521
|
+
reportedVia: "detected"
|
|
1522
|
+
});
|
|
1523
|
+
const filePath = join2(projectPath, issue.file);
|
|
1524
|
+
const fileNode = await graph.getNode("file", filePath);
|
|
1525
|
+
if (fileNode) {
|
|
1526
|
+
await graph.addEdge(fileNode.id, incident.id, "affects");
|
|
1527
|
+
const data = fileNode.data;
|
|
1528
|
+
const newRisk = severity === "critical" ? "critical" : severity === "major" ? "high" : data.riskLevel === "low" ? "medium" : data.riskLevel;
|
|
1529
|
+
await graph.updateNode("file", fileNode.id, {
|
|
1530
|
+
incidentCount: (data.incidentCount ?? 0) + 1,
|
|
1531
|
+
riskLevel: newRisk
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
this.state.totalIssuesFound++;
|
|
1535
|
+
if (severity !== "minor") {
|
|
1536
|
+
getOutputManager().nudge(
|
|
1537
|
+
`[!] ${issue.description}`,
|
|
1538
|
+
severity === "critical" ? "critical" : "warning",
|
|
1539
|
+
issue.file,
|
|
1540
|
+
severity === "critical" ? void 0 : 15e3
|
|
1541
|
+
);
|
|
1542
|
+
this.state.nudges.push({
|
|
1543
|
+
file: basename2(issue.file),
|
|
1544
|
+
message: issue.description,
|
|
1545
|
+
severity: severity === "critical" ? "critical" : "high",
|
|
1546
|
+
timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false })
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
3394
1549
|
}
|
|
3395
|
-
this.
|
|
3396
|
-
|
|
3397
|
-
|
|
1550
|
+
if (this.streamingManager && issues.length > 0) {
|
|
1551
|
+
this.streamingManager.reportSignalExtraction({
|
|
1552
|
+
decisions: 0,
|
|
1553
|
+
facts: 0,
|
|
1554
|
+
blockers: issues.length,
|
|
1555
|
+
questions: 0
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
} catch {
|
|
3398
1559
|
} finally {
|
|
3399
|
-
this.state.
|
|
1560
|
+
this.state.autoScanInProgress = false;
|
|
3400
1561
|
}
|
|
3401
1562
|
}
|
|
3402
1563
|
stopWatching() {
|
|
@@ -3433,19 +1594,21 @@ ${f.content.slice(0, 1e3)}`
|
|
|
3433
1594
|
this.dashboard.stop();
|
|
3434
1595
|
this.dashboard = void 0;
|
|
3435
1596
|
}
|
|
1597
|
+
const budget = this.state.tokenBudget;
|
|
1598
|
+
const tokensK = (budget.used / 1e3).toFixed(1);
|
|
3436
1599
|
return {
|
|
3437
1600
|
content: [{
|
|
3438
1601
|
type: "text",
|
|
3439
|
-
text: `[*] **TRIE AGENT STOPPED**
|
|
1602
|
+
text: `[*] **TRIE AGENT STOPPED**
|
|
3440
1603
|
|
|
3441
1604
|
### Session Summary:
|
|
3442
1605
|
- Files scanned: ${this.state.filesScanned}
|
|
3443
|
-
-
|
|
3444
|
-
-
|
|
1606
|
+
- Issues found: ${this.state.totalIssuesFound}
|
|
1607
|
+
- Tokens used: ${tokensK}k / ${(budget.hourlyLimit / 1e3).toFixed(0)}k hourly limit
|
|
1608
|
+
- Scans skipped (trie-throttled): ${budget.scansSaved}
|
|
3445
1609
|
- Decision ledger: Updated continuously
|
|
3446
1610
|
|
|
3447
|
-
|
|
3448
|
-
Use \`trie_watch start\` to resume autonomous operation.`
|
|
1611
|
+
Use \`trie_watch start\` to resume.`
|
|
3449
1612
|
}]
|
|
3450
1613
|
};
|
|
3451
1614
|
}
|
|
@@ -3469,7 +1632,7 @@ Use \`trie_watch start\` to begin autonomous scanning.`
|
|
|
3469
1632
|
).join("\n");
|
|
3470
1633
|
let agencyStatus = "";
|
|
3471
1634
|
try {
|
|
3472
|
-
const { getGuardian } = await import("./guardian-agent-
|
|
1635
|
+
const { getGuardian } = await import("./guardian-agent-XEYNG7RH.js");
|
|
3473
1636
|
const trieAgent = getGuardian(getWorkingDirectory(void 0, true));
|
|
3474
1637
|
await trieAgent.initialize();
|
|
3475
1638
|
const status = await trieAgent.getAgencyStatus();
|
|
@@ -3482,6 +1645,10 @@ Use \`trie_watch start\` to begin autonomous scanning.`
|
|
|
3482
1645
|
- **Scan Frequency:** ${Math.round(status.scanFrequency / 1e3)}s${status.isQuietHours ? " (quiet hours)" : ""}`;
|
|
3483
1646
|
} catch {
|
|
3484
1647
|
}
|
|
1648
|
+
const budget = this.state.tokenBudget;
|
|
1649
|
+
const remaining = this.getRemainingBudget();
|
|
1650
|
+
const tokensK = (budget.used / 1e3).toFixed(1);
|
|
1651
|
+
const remainingK = (remaining / 1e3).toFixed(1);
|
|
3485
1652
|
return {
|
|
3486
1653
|
content: [{
|
|
3487
1654
|
type: "text",
|
|
@@ -3489,9 +1656,14 @@ Use \`trie_watch start\` to begin autonomous scanning.`
|
|
|
3489
1656
|
|
|
3490
1657
|
### Stats:
|
|
3491
1658
|
- Directories watched: ${this.watchers.size}
|
|
3492
|
-
- Files scanned
|
|
3493
|
-
-
|
|
3494
|
-
- Pending
|
|
1659
|
+
- Files scanned: ${this.state.filesScanned}
|
|
1660
|
+
- Issues found: ${this.state.totalIssuesFound}
|
|
1661
|
+
- Pending: ${this.state.pendingFiles.size}
|
|
1662
|
+
|
|
1663
|
+
### Token Budget:
|
|
1664
|
+
- Used: ${tokensK}k / ${(budget.hourlyLimit / 1e3).toFixed(0)}k hourly
|
|
1665
|
+
- Remaining: ${remainingK}k
|
|
1666
|
+
- Scans skipped (trie-throttled): ${budget.scansSaved}
|
|
3495
1667
|
${agencyStatus}
|
|
3496
1668
|
|
|
3497
1669
|
### Recently Scanned:
|
|
@@ -3502,7 +1674,6 @@ ${recentNudges || "(none)"}
|
|
|
3502
1674
|
|
|
3503
1675
|
### Commands:
|
|
3504
1676
|
- \`trie_watch issues\` - Get all issues found
|
|
3505
|
-
- \`trie_watch nudges\` - Get recent nudges (structured)
|
|
3506
1677
|
- \`trie_watch stop\` - Stop watching`
|
|
3507
1678
|
}]
|
|
3508
1679
|
};
|
|
@@ -3541,9 +1712,9 @@ To get a full report, run \`trie_scan\` on your codebase.`
|
|
|
3541
1712
|
};
|
|
3542
1713
|
|
|
3543
1714
|
// src/tools/pr-review.ts
|
|
3544
|
-
import { readFile as
|
|
3545
|
-
import { existsSync as
|
|
3546
|
-
import { join as join3, basename as basename3, resolve as
|
|
1715
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
1716
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1717
|
+
import { join as join3, basename as basename3, resolve as resolve3, isAbsolute as isAbsolute3 } from "path";
|
|
3547
1718
|
|
|
3548
1719
|
// src/skills/built-in/super-reviewer.ts
|
|
3549
1720
|
var CRITICAL_REVIEW_CHECKLIST = {
|
|
@@ -3656,8 +1827,8 @@ Usage:
|
|
|
3656
1827
|
*/
|
|
3657
1828
|
async getPRInfo(pr, worktree) {
|
|
3658
1829
|
if (worktree) {
|
|
3659
|
-
const worktreePath =
|
|
3660
|
-
if (!
|
|
1830
|
+
const worktreePath = isAbsolute3(worktree) ? worktree : resolve3(getWorkingDirectory(void 0, true), worktree);
|
|
1831
|
+
if (!existsSync4(worktreePath)) {
|
|
3661
1832
|
return { success: false, error: `Worktree not found: ${worktreePath}` };
|
|
3662
1833
|
}
|
|
3663
1834
|
return {
|
|
@@ -3757,7 +1928,7 @@ Usage:
|
|
|
3757
1928
|
const headerLine = lines[0] || "";
|
|
3758
1929
|
const pathMatch = headerLine.match(/a\/(.+?) b\/(.+)/);
|
|
3759
1930
|
if (!pathMatch || !pathMatch[2]) continue;
|
|
3760
|
-
const
|
|
1931
|
+
const path2 = pathMatch[2];
|
|
3761
1932
|
const diff = `diff --git ${fileDiff}`;
|
|
3762
1933
|
let additions = 0;
|
|
3763
1934
|
let deletions = 0;
|
|
@@ -3768,7 +1939,7 @@ Usage:
|
|
|
3768
1939
|
deletions++;
|
|
3769
1940
|
}
|
|
3770
1941
|
}
|
|
3771
|
-
files.push({ path:
|
|
1942
|
+
files.push({ path: path2, diff, additions, deletions, status: "modified" });
|
|
3772
1943
|
}
|
|
3773
1944
|
return files;
|
|
3774
1945
|
}
|
|
@@ -3791,7 +1962,7 @@ Usage:
|
|
|
3791
1962
|
];
|
|
3792
1963
|
for (const docPath of designDocPaths) {
|
|
3793
1964
|
const fullPath = join3(cwd, docPath);
|
|
3794
|
-
if (
|
|
1965
|
+
if (existsSync4(fullPath)) {
|
|
3795
1966
|
}
|
|
3796
1967
|
}
|
|
3797
1968
|
return designDocs;
|
|
@@ -3804,9 +1975,9 @@ Usage:
|
|
|
3804
1975
|
const cwd = getWorkingDirectory(void 0, true);
|
|
3805
1976
|
await Promise.all(filePaths.map(async (filePath) => {
|
|
3806
1977
|
try {
|
|
3807
|
-
const fullPath =
|
|
3808
|
-
if (
|
|
3809
|
-
const content = await
|
|
1978
|
+
const fullPath = isAbsolute3(filePath) ? filePath : join3(cwd, filePath);
|
|
1979
|
+
if (existsSync4(fullPath)) {
|
|
1980
|
+
const content = await readFile4(fullPath, "utf-8");
|
|
3810
1981
|
contents.set(filePath, content);
|
|
3811
1982
|
}
|
|
3812
1983
|
} catch {
|
|
@@ -4585,216 +2756,8 @@ var TrieMemorySearchTool = class {
|
|
|
4585
2756
|
}
|
|
4586
2757
|
};
|
|
4587
2758
|
|
|
4588
|
-
// src/tools/checkpoint.ts
|
|
4589
|
-
async function handleCheckpointTool(input) {
|
|
4590
|
-
const workDir = getWorkingDirectory(void 0, true);
|
|
4591
|
-
switch (input.action) {
|
|
4592
|
-
case "save": {
|
|
4593
|
-
const saveOptions = {
|
|
4594
|
-
files: input.files || [],
|
|
4595
|
-
workDir,
|
|
4596
|
-
createdBy: "mcp"
|
|
4597
|
-
};
|
|
4598
|
-
if (input.message !== void 0) {
|
|
4599
|
-
saveOptions.message = input.message;
|
|
4600
|
-
}
|
|
4601
|
-
if (input.notes !== void 0) {
|
|
4602
|
-
saveOptions.notes = input.notes;
|
|
4603
|
-
}
|
|
4604
|
-
const checkpoint = await saveCheckpoint(saveOptions);
|
|
4605
|
-
return `# Checkpoint Saved
|
|
4606
|
-
|
|
4607
|
-
**ID:** ${checkpoint.id}
|
|
4608
|
-
**Time:** ${checkpoint.timestamp}
|
|
4609
|
-
${checkpoint.message ? `**Message:** ${checkpoint.message}` : ""}
|
|
4610
|
-
${checkpoint.notes ? `**Notes:** ${checkpoint.notes}` : ""}
|
|
4611
|
-
${checkpoint.files.length > 0 ? `**Files:** ${checkpoint.files.join(", ")}` : ""}
|
|
4612
|
-
|
|
4613
|
-
Context saved to \`.trie/\`. This checkpoint will be visible in other tools (Cursor, Claude Code, CLI).`;
|
|
4614
|
-
}
|
|
4615
|
-
case "list": {
|
|
4616
|
-
const checkpoints = await listCheckpoints(workDir);
|
|
4617
|
-
if (checkpoints.length === 0) {
|
|
4618
|
-
return 'No checkpoints yet. Use `trie_checkpoint action="save"` to create one.';
|
|
4619
|
-
}
|
|
4620
|
-
const lines = ["# Recent Checkpoints", ""];
|
|
4621
|
-
for (const cp of checkpoints.slice(-10).reverse()) {
|
|
4622
|
-
const date = new Date(cp.timestamp).toLocaleString();
|
|
4623
|
-
lines.push(`- **${cp.id}** (${date}): ${cp.message || "(no message)"}`);
|
|
4624
|
-
}
|
|
4625
|
-
return lines.join("\n");
|
|
4626
|
-
}
|
|
4627
|
-
case "last": {
|
|
4628
|
-
const checkpoint = await getLastCheckpoint(workDir);
|
|
4629
|
-
if (!checkpoint) {
|
|
4630
|
-
return 'No checkpoints yet. Use `trie_checkpoint action="save"` to create one.';
|
|
4631
|
-
}
|
|
4632
|
-
return `# Last Checkpoint
|
|
4633
|
-
|
|
4634
|
-
**ID:** ${checkpoint.id}
|
|
4635
|
-
**Time:** ${new Date(checkpoint.timestamp).toLocaleString()}
|
|
4636
|
-
${checkpoint.message ? `**Message:** ${checkpoint.message}` : ""}
|
|
4637
|
-
${checkpoint.notes ? `**Notes:** ${checkpoint.notes}` : ""}
|
|
4638
|
-
${checkpoint.files.length > 0 ? `**Files:** ${checkpoint.files.join(", ")}` : ""}
|
|
4639
|
-
**Created by:** ${checkpoint.createdBy}`;
|
|
4640
|
-
}
|
|
4641
|
-
default:
|
|
4642
|
-
return "Unknown action. Use: save, list, or last";
|
|
4643
|
-
}
|
|
4644
|
-
}
|
|
4645
|
-
|
|
4646
|
-
// src/tools/check.ts
|
|
4647
|
-
var TrieCheckTool = class {
|
|
4648
|
-
async execute(input = {}) {
|
|
4649
|
-
try {
|
|
4650
|
-
const workDir = input.directory || getWorkingDirectory(void 0, true);
|
|
4651
|
-
let files = input.files;
|
|
4652
|
-
if (!files || files.length === 0) {
|
|
4653
|
-
const perception = await perceiveCurrentChanges(workDir);
|
|
4654
|
-
files = perception.diffSummary.files.map((f) => f.filePath);
|
|
4655
|
-
}
|
|
4656
|
-
if (!files || files.length === 0) {
|
|
4657
|
-
return {
|
|
4658
|
-
content: [{
|
|
4659
|
-
type: "text",
|
|
4660
|
-
text: "No changes detected. Provide files or make a change."
|
|
4661
|
-
}]
|
|
4662
|
-
};
|
|
4663
|
-
}
|
|
4664
|
-
const mode = input.mode ?? "full";
|
|
4665
|
-
const runAgents = mode === "full";
|
|
4666
|
-
const reasoning = await reasonAboutChangesHumanReadable(workDir, files, {
|
|
4667
|
-
runAgents,
|
|
4668
|
-
scanContext: { config: { timeoutMs: mode === "quick" ? 15e3 : 6e4 } }
|
|
4669
|
-
});
|
|
4670
|
-
const summary = [
|
|
4671
|
-
`Risk: ${reasoning.original.riskLevel.toUpperCase()} (${reasoning.original.shouldBlock ? "block" : "allow"})`,
|
|
4672
|
-
`Explanation: ${reasoning.original.explanation}`,
|
|
4673
|
-
`Recommendation: ${reasoning.original.recommendation}`,
|
|
4674
|
-
`Plain summary: ${reasoning.summary}`,
|
|
4675
|
-
`What I found: ${reasoning.whatIFound}`,
|
|
4676
|
-
`How bad: ${reasoning.howBad}`,
|
|
4677
|
-
`What to do: ${reasoning.whatToDo}`
|
|
4678
|
-
].join("\n");
|
|
4679
|
-
return {
|
|
4680
|
-
content: [{
|
|
4681
|
-
type: "text",
|
|
4682
|
-
text: summary
|
|
4683
|
-
}]
|
|
4684
|
-
};
|
|
4685
|
-
} catch (error) {
|
|
4686
|
-
const friendly = formatFriendlyError(error);
|
|
4687
|
-
return { content: [{ type: "text", text: friendly.userMessage }] };
|
|
4688
|
-
}
|
|
4689
|
-
}
|
|
4690
|
-
};
|
|
4691
|
-
|
|
4692
|
-
// src/tools/tell.ts
|
|
4693
|
-
import path from "path";
|
|
4694
|
-
function escalateRisk(level) {
|
|
4695
|
-
if (level === "low") return "medium";
|
|
4696
|
-
if (level === "medium") return "high";
|
|
4697
|
-
if (level === "high") return "critical";
|
|
4698
|
-
return "critical";
|
|
4699
|
-
}
|
|
4700
|
-
function extractFilePathsFromDescription(description) {
|
|
4701
|
-
const matches = description.match(/[\\w./_-]+\\.(ts|tsx|js|jsx|mjs|cjs)/gi);
|
|
4702
|
-
if (!matches) return [];
|
|
4703
|
-
const unique = /* @__PURE__ */ new Set();
|
|
4704
|
-
matches.forEach((m) => unique.add(m.replace(/^\.\/+/, "")));
|
|
4705
|
-
return Array.from(unique);
|
|
4706
|
-
}
|
|
4707
|
-
var TrieTellTool = class {
|
|
4708
|
-
async execute(input) {
|
|
4709
|
-
try {
|
|
4710
|
-
const description = input.description?.trim();
|
|
4711
|
-
if (!description) {
|
|
4712
|
-
return { content: [{ type: "text", text: "description is required" }] };
|
|
4713
|
-
}
|
|
4714
|
-
const projectPath = input.directory || getWorkingDirectory(void 0, true);
|
|
4715
|
-
const graph = new ContextGraph(projectPath);
|
|
4716
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4717
|
-
const change = (await graph.getRecentChanges(1))[0];
|
|
4718
|
-
const linkedFiles = /* @__PURE__ */ new Set();
|
|
4719
|
-
console.log("\n\u{1F9E0} Processing incident with signal extraction...");
|
|
4720
|
-
let extractedSignal = null;
|
|
4721
|
-
try {
|
|
4722
|
-
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
4723
|
-
const options = {
|
|
4724
|
-
workingDirectory: projectPath
|
|
4725
|
-
};
|
|
4726
|
-
if (apiKey) {
|
|
4727
|
-
options.anthropicApiKey = apiKey;
|
|
4728
|
-
}
|
|
4729
|
-
extractedSignal = await processIncident(description, options);
|
|
4730
|
-
} catch (error) {
|
|
4731
|
-
console.warn("\u26A0\uFE0F Signal extraction failed, continuing with basic incident tracking:", error);
|
|
4732
|
-
}
|
|
4733
|
-
const incident = await graph.addNode("incident", {
|
|
4734
|
-
description,
|
|
4735
|
-
severity: "major",
|
|
4736
|
-
affectedUsers: null,
|
|
4737
|
-
duration: null,
|
|
4738
|
-
timestamp: now,
|
|
4739
|
-
resolved: false,
|
|
4740
|
-
resolution: null,
|
|
4741
|
-
fixChangeId: change?.id ?? null,
|
|
4742
|
-
reportedVia: "manual"
|
|
4743
|
-
});
|
|
4744
|
-
if (change) {
|
|
4745
|
-
await graph.addEdge(change.id, incident.id, "leadTo");
|
|
4746
|
-
await graph.addEdge(incident.id, change.id, "causedBy");
|
|
4747
|
-
for (const filePath of change.data.files) {
|
|
4748
|
-
linkedFiles.add(filePath);
|
|
4749
|
-
const fileNode = await graph.getNode("file", path.resolve(projectPath, filePath));
|
|
4750
|
-
if (fileNode) {
|
|
4751
|
-
const data = fileNode.data;
|
|
4752
|
-
await graph.updateNode("file", fileNode.id, {
|
|
4753
|
-
incidentCount: (data.incidentCount ?? 0) + 1,
|
|
4754
|
-
riskLevel: escalateRisk(data.riskLevel)
|
|
4755
|
-
});
|
|
4756
|
-
}
|
|
4757
|
-
}
|
|
4758
|
-
}
|
|
4759
|
-
const mentionedFiles = extractFilePathsFromDescription(description);
|
|
4760
|
-
mentionedFiles.forEach((f) => linkedFiles.add(f));
|
|
4761
|
-
if (extractedSignal) {
|
|
4762
|
-
for (const decision of extractedSignal.decisions) {
|
|
4763
|
-
decision.files.forEach((f) => linkedFiles.add(f));
|
|
4764
|
-
}
|
|
4765
|
-
}
|
|
4766
|
-
const incidentIndex = new IncidentIndex(graph, projectPath);
|
|
4767
|
-
incidentIndex.addIncidentToTrie(incident, Array.from(linkedFiles));
|
|
4768
|
-
await exportToJson(graph);
|
|
4769
|
-
let responseText = `Incident recorded${change ? ` and linked to change ${change.id}` : ""}.`;
|
|
4770
|
-
if (extractedSignal) {
|
|
4771
|
-
const counts = [
|
|
4772
|
-
extractedSignal.decisions.length > 0 ? `${extractedSignal.decisions.length} decision(s)` : null,
|
|
4773
|
-
extractedSignal.facts.length > 0 ? `${extractedSignal.facts.length} fact(s)` : null,
|
|
4774
|
-
extractedSignal.blockers.length > 0 ? `${extractedSignal.blockers.length} blocker(s)` : null,
|
|
4775
|
-
extractedSignal.questions.length > 0 ? `${extractedSignal.questions.length} question(s)` : null
|
|
4776
|
-
].filter(Boolean).join(", ");
|
|
4777
|
-
if (counts) {
|
|
4778
|
-
responseText += `
|
|
4779
|
-
|
|
4780
|
-
\u{1F4CA} Extracted and stored: ${counts}`;
|
|
4781
|
-
}
|
|
4782
|
-
}
|
|
4783
|
-
return {
|
|
4784
|
-
content: [{
|
|
4785
|
-
type: "text",
|
|
4786
|
-
text: responseText
|
|
4787
|
-
}]
|
|
4788
|
-
};
|
|
4789
|
-
} catch (error) {
|
|
4790
|
-
const friendly = formatFriendlyError(error);
|
|
4791
|
-
return { content: [{ type: "text", text: friendly.userMessage }] };
|
|
4792
|
-
}
|
|
4793
|
-
}
|
|
4794
|
-
};
|
|
4795
|
-
|
|
4796
2759
|
// src/tools/reconcile.ts
|
|
4797
|
-
import
|
|
2760
|
+
import path from "path";
|
|
4798
2761
|
async function removeOrphanEdges(graph) {
|
|
4799
2762
|
const nodes = await graph.listNodes();
|
|
4800
2763
|
const ids = new Set(nodes.map((n) => n.id));
|
|
@@ -4812,7 +2775,7 @@ var TrieReconcileTool = class {
|
|
|
4812
2775
|
async execute(input = {}) {
|
|
4813
2776
|
try {
|
|
4814
2777
|
const projectPath = input.directory || getWorkingDirectory(void 0, true);
|
|
4815
|
-
const sourcePath = input.source ??
|
|
2778
|
+
const sourcePath = input.source ?? path.join(getTrieDirectory(projectPath), "context.json");
|
|
4816
2779
|
const graph = new ContextGraph(projectPath);
|
|
4817
2780
|
await importFromJson(graph, "", sourcePath);
|
|
4818
2781
|
const removed = await removeOrphanEdges(graph);
|
|
@@ -4852,33 +2815,6 @@ var TrieContextTool = class {
|
|
|
4852
2815
|
}
|
|
4853
2816
|
};
|
|
4854
2817
|
|
|
4855
|
-
// src/tools/feedback.ts
|
|
4856
|
-
var TrieFeedbackTool = class {
|
|
4857
|
-
async execute(input) {
|
|
4858
|
-
try {
|
|
4859
|
-
const projectPath = input.directory || getWorkingDirectory(void 0, true);
|
|
4860
|
-
const graph = new ContextGraph(projectPath);
|
|
4861
|
-
const engine = new LearningEngine(projectPath, graph);
|
|
4862
|
-
const files = input.files || (input.target ? [input.target] : []);
|
|
4863
|
-
const manualFeedback = {
|
|
4864
|
-
helpful: input.helpful,
|
|
4865
|
-
files,
|
|
4866
|
-
...input.note !== void 0 ? { note: input.note } : {}
|
|
4867
|
-
};
|
|
4868
|
-
await engine.learn({ manualFeedback });
|
|
4869
|
-
return {
|
|
4870
|
-
content: [{
|
|
4871
|
-
type: "text",
|
|
4872
|
-
text: input.helpful ? "\u{1F44D} Thanks \u2014 I will prioritize more responses like this." : "\u{1F44E} Understood \u2014 I will adjust future guidance."
|
|
4873
|
-
}]
|
|
4874
|
-
};
|
|
4875
|
-
} catch (error) {
|
|
4876
|
-
const friendly = formatFriendlyError(error);
|
|
4877
|
-
return { content: [{ type: "text", text: friendly.userMessage }] };
|
|
4878
|
-
}
|
|
4879
|
-
}
|
|
4880
|
-
};
|
|
4881
|
-
|
|
4882
2818
|
// src/tools/linear-sync.ts
|
|
4883
2819
|
var LinearSyncTool = class {
|
|
4884
2820
|
async execute(input) {
|
|
@@ -4905,178 +2841,6 @@ var LinearSyncTool = class {
|
|
|
4905
2841
|
}
|
|
4906
2842
|
};
|
|
4907
2843
|
|
|
4908
|
-
// src/tools/query-tools.ts
|
|
4909
|
-
var TrieGetDecisionsTool = class {
|
|
4910
|
-
async execute(input) {
|
|
4911
|
-
const workDir = input.directory || getWorkingDirectory(void 0, true);
|
|
4912
|
-
const storage = getStorage(workDir);
|
|
4913
|
-
await storage.initialize();
|
|
4914
|
-
let timeWindow;
|
|
4915
|
-
if (input.since) {
|
|
4916
|
-
const now = /* @__PURE__ */ new Date();
|
|
4917
|
-
if (input.since.endsWith("d")) {
|
|
4918
|
-
const days = parseInt(input.since);
|
|
4919
|
-
const start = new Date(now);
|
|
4920
|
-
start.setDate(start.getDate() - days);
|
|
4921
|
-
timeWindow = { start: start.toISOString() };
|
|
4922
|
-
} else {
|
|
4923
|
-
timeWindow = { start: input.since };
|
|
4924
|
-
}
|
|
4925
|
-
}
|
|
4926
|
-
const query = {
|
|
4927
|
-
limit: input.limit || 10
|
|
4928
|
-
};
|
|
4929
|
-
if (input.relatedTo) query.relatedTo = input.relatedTo;
|
|
4930
|
-
if (input.tags) query.tags = input.tags;
|
|
4931
|
-
if (timeWindow) query.timeWindow = timeWindow;
|
|
4932
|
-
const decisions = await storage.queryDecisions(query);
|
|
4933
|
-
return {
|
|
4934
|
-
content: [{
|
|
4935
|
-
type: "text",
|
|
4936
|
-
text: this.formatDecisions(decisions)
|
|
4937
|
-
}]
|
|
4938
|
-
};
|
|
4939
|
-
}
|
|
4940
|
-
formatDecisions(decisions) {
|
|
4941
|
-
if (decisions.length === 0) {
|
|
4942
|
-
return "No decisions found matching query.";
|
|
4943
|
-
}
|
|
4944
|
-
let output = `Found ${decisions.length} decision(s):
|
|
4945
|
-
|
|
4946
|
-
`;
|
|
4947
|
-
for (const dec of decisions) {
|
|
4948
|
-
const when = new Date(dec.when).toLocaleDateString();
|
|
4949
|
-
output += `\u{1F4CB} ${dec.decision}
|
|
4950
|
-
`;
|
|
4951
|
-
output += ` Context: ${dec.context}
|
|
4952
|
-
`;
|
|
4953
|
-
if (dec.reasoning) {
|
|
4954
|
-
output += ` Reasoning: ${dec.reasoning}
|
|
4955
|
-
`;
|
|
4956
|
-
}
|
|
4957
|
-
if (dec.tradeoffs && dec.tradeoffs.length > 0) {
|
|
4958
|
-
output += ` Tradeoffs considered: ${dec.tradeoffs.join(", ")}
|
|
4959
|
-
`;
|
|
4960
|
-
}
|
|
4961
|
-
output += ` When: ${when}
|
|
4962
|
-
`;
|
|
4963
|
-
if (dec.files.length > 0) {
|
|
4964
|
-
output += ` Files: ${dec.files.join(", ")}
|
|
4965
|
-
`;
|
|
4966
|
-
}
|
|
4967
|
-
output += ` Tags: ${dec.tags.join(", ")}
|
|
4968
|
-
`;
|
|
4969
|
-
output += `
|
|
4970
|
-
`;
|
|
4971
|
-
}
|
|
4972
|
-
return output;
|
|
4973
|
-
}
|
|
4974
|
-
};
|
|
4975
|
-
var TrieGetBlockersTool = class {
|
|
4976
|
-
async execute(input) {
|
|
4977
|
-
const workDir = input.directory || getWorkingDirectory(void 0, true);
|
|
4978
|
-
const storage = getStorage(workDir);
|
|
4979
|
-
await storage.initialize();
|
|
4980
|
-
const query = {
|
|
4981
|
-
limit: input.limit || 5
|
|
4982
|
-
};
|
|
4983
|
-
if (input.tags) query.tags = input.tags;
|
|
4984
|
-
const blockers = await storage.queryBlockers(query);
|
|
4985
|
-
return {
|
|
4986
|
-
content: [{
|
|
4987
|
-
type: "text",
|
|
4988
|
-
text: this.formatBlockers(blockers)
|
|
4989
|
-
}]
|
|
4990
|
-
};
|
|
4991
|
-
}
|
|
4992
|
-
formatBlockers(blockers) {
|
|
4993
|
-
if (blockers.length === 0) {
|
|
4994
|
-
return "\u2705 No active blockers found.";
|
|
4995
|
-
}
|
|
4996
|
-
let output = `\u26A0\uFE0F Found ${blockers.length} active blocker(s):
|
|
4997
|
-
|
|
4998
|
-
`;
|
|
4999
|
-
for (const blocker of blockers) {
|
|
5000
|
-
const impact = blocker.impact.toUpperCase();
|
|
5001
|
-
const emoji = blocker.impact === "critical" ? "\u{1F534}" : blocker.impact === "high" ? "\u{1F7E0}" : blocker.impact === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
5002
|
-
output += `${emoji} [${impact}] ${blocker.blocker}
|
|
5003
|
-
`;
|
|
5004
|
-
if (blocker.affectedAreas.length > 0) {
|
|
5005
|
-
output += ` Affects: ${blocker.affectedAreas.join(", ")}
|
|
5006
|
-
`;
|
|
5007
|
-
}
|
|
5008
|
-
output += ` Since: ${new Date(blocker.when).toLocaleDateString()}
|
|
5009
|
-
`;
|
|
5010
|
-
output += `
|
|
5011
|
-
`;
|
|
5012
|
-
}
|
|
5013
|
-
return output;
|
|
5014
|
-
}
|
|
5015
|
-
};
|
|
5016
|
-
var TrieGetRelatedDecisionsTool = class {
|
|
5017
|
-
async execute(input) {
|
|
5018
|
-
const workDir = input.directory || getWorkingDirectory(void 0, true);
|
|
5019
|
-
const storage = getStorage(workDir);
|
|
5020
|
-
await storage.initialize();
|
|
5021
|
-
const query = {
|
|
5022
|
-
limit: input.limit || 5
|
|
5023
|
-
};
|
|
5024
|
-
const relatedTo = input.file || input.topic;
|
|
5025
|
-
if (relatedTo) query.relatedTo = relatedTo;
|
|
5026
|
-
const decisions = await storage.queryDecisions(query);
|
|
5027
|
-
return {
|
|
5028
|
-
content: [{
|
|
5029
|
-
type: "text",
|
|
5030
|
-
text: new TrieGetDecisionsTool().formatDecisions(decisions)
|
|
5031
|
-
}]
|
|
5032
|
-
};
|
|
5033
|
-
}
|
|
5034
|
-
};
|
|
5035
|
-
var TrieQueryContextTool = class {
|
|
5036
|
-
async execute(input) {
|
|
5037
|
-
const workDir = input.directory || getWorkingDirectory(void 0, true);
|
|
5038
|
-
const storage = getStorage(workDir);
|
|
5039
|
-
await storage.initialize();
|
|
5040
|
-
const keywords = input.query.toLowerCase().split(/\s+/);
|
|
5041
|
-
let output = `Query: "${input.query}"
|
|
5042
|
-
|
|
5043
|
-
`;
|
|
5044
|
-
if (!input.type || input.type === "decisions" || input.type === "all") {
|
|
5045
|
-
const decisions = await storage.queryDecisions({ limit: input.limit || 5 });
|
|
5046
|
-
const matches = decisions.filter(
|
|
5047
|
-
(d) => keywords.some(
|
|
5048
|
-
(kw) => d.decision.toLowerCase().includes(kw) || d.context.toLowerCase().includes(kw) || d.tags.some((t) => t.toLowerCase().includes(kw))
|
|
5049
|
-
)
|
|
5050
|
-
);
|
|
5051
|
-
if (matches.length > 0) {
|
|
5052
|
-
output += `\u{1F4CB} DECISIONS (${matches.length}):
|
|
5053
|
-
`;
|
|
5054
|
-
output += new TrieGetDecisionsTool().formatDecisions(matches);
|
|
5055
|
-
output += "\n";
|
|
5056
|
-
}
|
|
5057
|
-
}
|
|
5058
|
-
if (!input.type || input.type === "blockers" || input.type === "all") {
|
|
5059
|
-
const blockers = await storage.queryBlockers({ limit: input.limit || 5 });
|
|
5060
|
-
const matches = blockers.filter(
|
|
5061
|
-
(b) => keywords.some(
|
|
5062
|
-
(kw) => b.blocker.toLowerCase().includes(kw) || b.tags.some((t) => t.toLowerCase().includes(kw))
|
|
5063
|
-
)
|
|
5064
|
-
);
|
|
5065
|
-
if (matches.length > 0) {
|
|
5066
|
-
output += `\u26A0\uFE0F BLOCKERS (${matches.length}):
|
|
5067
|
-
`;
|
|
5068
|
-
output += new TrieGetBlockersTool().formatBlockers(matches);
|
|
5069
|
-
}
|
|
5070
|
-
}
|
|
5071
|
-
return {
|
|
5072
|
-
content: [{
|
|
5073
|
-
type: "text",
|
|
5074
|
-
text: output.trim() || "No matches found."
|
|
5075
|
-
}]
|
|
5076
|
-
};
|
|
5077
|
-
}
|
|
5078
|
-
};
|
|
5079
|
-
|
|
5080
2844
|
// src/server/tool-registry.ts
|
|
5081
2845
|
var TrieCheckpointTool = class {
|
|
5082
2846
|
async execute(input) {
|
|
@@ -5654,8 +3418,8 @@ var ToolRegistry = class {
|
|
|
5654
3418
|
};
|
|
5655
3419
|
|
|
5656
3420
|
// src/server/resource-manager.ts
|
|
5657
|
-
import { readdir, readFile as
|
|
5658
|
-
import { existsSync as
|
|
3421
|
+
import { readdir, readFile as readFile5 } from "fs/promises";
|
|
3422
|
+
import { existsSync as existsSync5 } from "fs";
|
|
5659
3423
|
import { join as join4, dirname as dirname2 } from "path";
|
|
5660
3424
|
import { fileURLToPath } from "url";
|
|
5661
3425
|
|
|
@@ -5880,7 +3644,7 @@ var ResourceManager = class {
|
|
|
5880
3644
|
const uiDir = join4(distDir, "ui");
|
|
5881
3645
|
const htmlPath = join4(uiDir, `${appId}.html`);
|
|
5882
3646
|
try {
|
|
5883
|
-
if (!
|
|
3647
|
+
if (!existsSync5(htmlPath)) {
|
|
5884
3648
|
return {
|
|
5885
3649
|
contents: [{
|
|
5886
3650
|
uri,
|
|
@@ -5889,7 +3653,7 @@ var ResourceManager = class {
|
|
|
5889
3653
|
}]
|
|
5890
3654
|
};
|
|
5891
3655
|
}
|
|
5892
|
-
const content = await
|
|
3656
|
+
const content = await readFile5(htmlPath, "utf-8");
|
|
5893
3657
|
return {
|
|
5894
3658
|
contents: [{
|
|
5895
3659
|
uri,
|
|
@@ -6072,8 +3836,8 @@ var ResourceManager = class {
|
|
|
6072
3836
|
summary.push("---", "", "# Detailed Context", "");
|
|
6073
3837
|
const agentsMdPath = join4(getTrieDirectory(workDir), "AGENTS.md");
|
|
6074
3838
|
try {
|
|
6075
|
-
if (
|
|
6076
|
-
const agentsContent = await
|
|
3839
|
+
if (existsSync5(agentsMdPath)) {
|
|
3840
|
+
const agentsContent = await readFile5(agentsMdPath, "utf-8");
|
|
6077
3841
|
summary.push(agentsContent);
|
|
6078
3842
|
} else {
|
|
6079
3843
|
const contextSummary = await getContextForAI();
|
|
@@ -6199,7 +3963,7 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
6199
3963
|
async getCacheStatsResource(uri) {
|
|
6200
3964
|
try {
|
|
6201
3965
|
const cachePath = join4(getTrieDirectory(getWorkingDirectory(void 0, true)), ".trie-cache.json");
|
|
6202
|
-
const cacheContent = await
|
|
3966
|
+
const cacheContent = await readFile5(cachePath, "utf-8");
|
|
6203
3967
|
const cache = JSON.parse(cacheContent);
|
|
6204
3968
|
const fileCount = Object.keys(cache.files || {}).length;
|
|
6205
3969
|
const totalVulns = Object.values(cache.files || {}).reduce((acc, file) => {
|
|
@@ -6280,7 +4044,7 @@ This information is automatically available to Claude Code, Cursor, and other AI
|
|
|
6280
4044
|
const fileName = parsedUri.replace("reports/", "");
|
|
6281
4045
|
const reportPath = join4(getWorkingDirectory(void 0, true), "trie-reports", fileName);
|
|
6282
4046
|
try {
|
|
6283
|
-
const content = await
|
|
4047
|
+
const content = await readFile5(reportPath, "utf-8");
|
|
6284
4048
|
return {
|
|
6285
4049
|
contents: [{
|
|
6286
4050
|
uri,
|
|
@@ -6427,32 +4191,32 @@ async function findOpenPort() {
|
|
|
6427
4191
|
return portChecks.find((port) => port !== null) || null;
|
|
6428
4192
|
}
|
|
6429
4193
|
async function checkPort(port) {
|
|
6430
|
-
return new Promise((
|
|
4194
|
+
return new Promise((resolve4) => {
|
|
6431
4195
|
const testServer = createServer();
|
|
6432
4196
|
let portInUse = false;
|
|
6433
4197
|
testServer.once("error", (err) => {
|
|
6434
4198
|
if (err.code === "EADDRINUSE") {
|
|
6435
4199
|
portInUse = true;
|
|
6436
|
-
testHttpPort(port).then(
|
|
4200
|
+
testHttpPort(port).then(resolve4).catch(() => resolve4(false));
|
|
6437
4201
|
} else {
|
|
6438
|
-
|
|
4202
|
+
resolve4(false);
|
|
6439
4203
|
}
|
|
6440
4204
|
});
|
|
6441
4205
|
testServer.once("listening", () => {
|
|
6442
4206
|
testServer.close();
|
|
6443
|
-
|
|
4207
|
+
resolve4(false);
|
|
6444
4208
|
});
|
|
6445
4209
|
setTimeout(() => {
|
|
6446
4210
|
if (!portInUse) {
|
|
6447
4211
|
testServer.close();
|
|
6448
|
-
|
|
4212
|
+
resolve4(false);
|
|
6449
4213
|
}
|
|
6450
4214
|
}, 1e3);
|
|
6451
4215
|
testServer.listen(port, "127.0.0.1");
|
|
6452
4216
|
});
|
|
6453
4217
|
}
|
|
6454
4218
|
async function testHttpPort(port) {
|
|
6455
|
-
return new Promise((
|
|
4219
|
+
return new Promise((resolve4) => {
|
|
6456
4220
|
const req = request({
|
|
6457
4221
|
hostname: "localhost",
|
|
6458
4222
|
port,
|
|
@@ -6460,18 +4224,18 @@ async function testHttpPort(port) {
|
|
|
6460
4224
|
method: "GET",
|
|
6461
4225
|
timeout: 2e3
|
|
6462
4226
|
}, (res) => {
|
|
6463
|
-
|
|
4227
|
+
resolve4(res.statusCode !== void 0);
|
|
6464
4228
|
res.on("data", () => {
|
|
6465
4229
|
});
|
|
6466
4230
|
res.on("end", () => {
|
|
6467
4231
|
});
|
|
6468
4232
|
});
|
|
6469
4233
|
req.on("error", () => {
|
|
6470
|
-
|
|
4234
|
+
resolve4(false);
|
|
6471
4235
|
});
|
|
6472
4236
|
req.on("timeout", () => {
|
|
6473
4237
|
req.destroy();
|
|
6474
|
-
|
|
4238
|
+
resolve4(false);
|
|
6475
4239
|
});
|
|
6476
4240
|
req.end();
|
|
6477
4241
|
});
|