@triedotdev/mcp 1.0.49 → 1.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +545 -406
- package/dist/agent-smith-BECRZH73.js +12 -0
- package/dist/{agent-smith-runner-ZTDCJJQG.js → agent-smith-runner-LZRXM2Q2.js} +7 -7
- package/dist/agent-smith-runner-LZRXM2Q2.js.map +1 -0
- package/dist/{chunk-KQOMSIVR.js → chunk-A43476GB.js} +13 -9
- package/dist/chunk-A43476GB.js.map +1 -0
- package/dist/{chunk-IMFD4SJC.js → chunk-ASGSTVVF.js} +1 -1
- package/dist/chunk-ASGSTVVF.js.map +1 -0
- package/dist/chunk-C3AS5OXW.js +1177 -0
- package/dist/chunk-C3AS5OXW.js.map +1 -0
- package/dist/chunk-IEFAQFDQ.js +2061 -0
- package/dist/chunk-IEFAQFDQ.js.map +1 -0
- package/dist/{chunk-GLC62PGD.js → chunk-KB5ZN6K2.js} +2 -2
- package/dist/{chunk-VZYCZXEQ.js → chunk-TOE75CFZ.js} +2034 -391
- package/dist/chunk-TOE75CFZ.js.map +1 -0
- package/dist/{chunk-JDICQHNT.js → chunk-YKUCIKTU.js} +171 -1245
- package/dist/chunk-YKUCIKTU.js.map +1 -0
- package/dist/cli/create-agent.js +2 -2
- package/dist/cli/main.js +428 -71
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +32 -20
- package/dist/cli/yolo-daemon.js.map +1 -1
- package/dist/comprehension-46F7ZNKL.js +821 -0
- package/dist/comprehension-46F7ZNKL.js.map +1 -0
- package/dist/index.js +478 -122
- package/dist/index.js.map +1 -1
- package/dist/workers/agent-worker.js +11 -11
- package/dist/workers/agent-worker.js.map +1 -1
- package/package.json +3 -1
- package/dist/agent-smith-5QOZXLMV.js +0 -11
- package/dist/agent-smith-runner-ZTDCJJQG.js.map +0 -1
- package/dist/chunk-6T7S77U7.js +0 -852
- package/dist/chunk-6T7S77U7.js.map +0 -1
- package/dist/chunk-IMFD4SJC.js.map +0 -1
- package/dist/chunk-JDICQHNT.js.map +0 -1
- package/dist/chunk-KQOMSIVR.js.map +0 -1
- package/dist/chunk-PZDQIFKO.js +0 -1598
- package/dist/chunk-PZDQIFKO.js.map +0 -1
- package/dist/chunk-VZYCZXEQ.js.map +0 -1
- /package/dist/{agent-smith-5QOZXLMV.js.map → agent-smith-BECRZH73.js.map} +0 -0
- /package/dist/{chunk-GLC62PGD.js.map → chunk-KB5ZN6K2.js.map} +0 -0
|
@@ -1,24 +1,235 @@
|
|
|
1
|
-
import {
|
|
2
|
-
listInstalledSkills,
|
|
3
|
-
parseSkillMd,
|
|
4
|
-
recordSkillUsage
|
|
5
|
-
} from "./chunk-PZDQIFKO.js";
|
|
6
1
|
import {
|
|
7
2
|
checkFileLevelIssues,
|
|
8
3
|
getVibeCodeTrie,
|
|
9
4
|
scanForVibeCodeIssues
|
|
10
5
|
} from "./chunk-3CS6Z2SL.js";
|
|
11
6
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
AgentSmithSkill,
|
|
8
|
+
BaseSkill,
|
|
14
9
|
isAIAvailable,
|
|
15
10
|
runAIAnalysis
|
|
16
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-A43476GB.js";
|
|
17
12
|
import {
|
|
18
13
|
getWorkingDirectory
|
|
19
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ASGSTVVF.js";
|
|
15
|
+
|
|
16
|
+
// src/utils/project-info.ts
|
|
17
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
18
|
+
import { existsSync } from "fs";
|
|
19
|
+
import { join } from "path";
|
|
20
|
+
var PROJECT_MD_PATH = ".trie/PROJECT.md";
|
|
21
|
+
function getProjectTemplate() {
|
|
22
|
+
return `# Project Information
|
|
23
|
+
|
|
24
|
+
> This file stores important project context for AI assistants.
|
|
25
|
+
> Edit freely - this file is yours, not auto-generated.
|
|
26
|
+
> Available via MCP resource: \`trie://project\`
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Project Overview
|
|
31
|
+
|
|
32
|
+
<!-- Describe your project's purpose and goals -->
|
|
33
|
+
|
|
34
|
+
[Add project description here]
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Technology Stack
|
|
39
|
+
|
|
40
|
+
<!-- List frameworks, languages, databases, cloud services, etc. -->
|
|
41
|
+
|
|
42
|
+
- **Language:**
|
|
43
|
+
- **Framework:**
|
|
44
|
+
- **Database:**
|
|
45
|
+
- **Hosting:**
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Architecture
|
|
50
|
+
|
|
51
|
+
<!-- Key patterns, architectural decisions, and system design -->
|
|
52
|
+
|
|
53
|
+
[Describe your architecture here]
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Coding Conventions
|
|
58
|
+
|
|
59
|
+
<!-- Style guidelines, naming conventions, patterns to follow -->
|
|
60
|
+
|
|
61
|
+
-
|
|
62
|
+
-
|
|
63
|
+
-
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Environment
|
|
68
|
+
|
|
69
|
+
<!-- URLs, API endpoints, deployment info -->
|
|
70
|
+
|
|
71
|
+
| Environment | URL | Notes |
|
|
72
|
+
|-------------|-----|-------|
|
|
73
|
+
| Development | | |
|
|
74
|
+
| Staging | | |
|
|
75
|
+
| Production | | |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Team
|
|
80
|
+
|
|
81
|
+
<!-- Ownership, contacts, responsibilities -->
|
|
82
|
+
|
|
83
|
+
- **Owner:**
|
|
84
|
+
- **Team:**
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Compliance
|
|
89
|
+
|
|
90
|
+
<!-- HIPAA, SOC2, GDPR, PCI-DSS requirements if applicable -->
|
|
91
|
+
|
|
92
|
+
- [ ] GDPR
|
|
93
|
+
- [ ] SOC2
|
|
94
|
+
- [ ] HIPAA
|
|
95
|
+
- [ ] PCI-DSS
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## AI Instructions
|
|
100
|
+
|
|
101
|
+
<!-- Special instructions for AI assistants working on this project -->
|
|
102
|
+
|
|
103
|
+
When working on this project, AI assistants should:
|
|
104
|
+
|
|
105
|
+
1.
|
|
106
|
+
2.
|
|
107
|
+
3.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
*This file is read by Trie agents and exposed via \`trie://project\` MCP resource.*
|
|
112
|
+
*Edit this file to provide context to Claude Code, Cursor, GitHub Actions, and other AI tools.*
|
|
113
|
+
`;
|
|
114
|
+
}
|
|
115
|
+
function projectInfoExists(workDir) {
|
|
116
|
+
const dir = workDir || getWorkingDirectory(void 0, true);
|
|
117
|
+
const projectPath = join(dir, PROJECT_MD_PATH);
|
|
118
|
+
return existsSync(projectPath);
|
|
119
|
+
}
|
|
120
|
+
async function loadProjectInfo(workDir) {
|
|
121
|
+
const dir = workDir || getWorkingDirectory(void 0, true);
|
|
122
|
+
const projectPath = join(dir, PROJECT_MD_PATH);
|
|
123
|
+
try {
|
|
124
|
+
if (!existsSync(projectPath)) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return await readFile(projectPath, "utf-8");
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function saveProjectInfo(content, workDir) {
|
|
133
|
+
const dir = workDir || getWorkingDirectory(void 0, true);
|
|
134
|
+
const trieDir = join(dir, ".trie");
|
|
135
|
+
const projectPath = join(dir, PROJECT_MD_PATH);
|
|
136
|
+
await mkdir(trieDir, { recursive: true });
|
|
137
|
+
await writeFile(projectPath, content, "utf-8");
|
|
138
|
+
}
|
|
139
|
+
async function initProjectInfo(workDir) {
|
|
140
|
+
const dir = workDir || getWorkingDirectory(void 0, true);
|
|
141
|
+
const projectPath = join(dir, PROJECT_MD_PATH);
|
|
142
|
+
if (existsSync(projectPath)) {
|
|
143
|
+
return { created: false, path: projectPath };
|
|
144
|
+
}
|
|
145
|
+
await saveProjectInfo(getProjectTemplate(), dir);
|
|
146
|
+
return { created: true, path: projectPath };
|
|
147
|
+
}
|
|
148
|
+
async function getProjectSection(sectionName, workDir) {
|
|
149
|
+
const content = await loadProjectInfo(workDir);
|
|
150
|
+
if (!content) return null;
|
|
151
|
+
const sectionRegex = new RegExp(
|
|
152
|
+
`## ${escapeRegex(sectionName)}\\s*\\n([\\s\\S]*?)(?=\\n## |\\n---\\s*$|$)`,
|
|
153
|
+
"i"
|
|
154
|
+
);
|
|
155
|
+
const match = content.match(sectionRegex);
|
|
156
|
+
if (match?.[1]) {
|
|
157
|
+
return match[1].trim();
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
async function updateProjectSection(sectionName, newContent, workDir) {
|
|
162
|
+
let content = await loadProjectInfo(workDir);
|
|
163
|
+
if (!content) {
|
|
164
|
+
await initProjectInfo(workDir);
|
|
165
|
+
content = await loadProjectInfo(workDir);
|
|
166
|
+
if (!content) return false;
|
|
167
|
+
}
|
|
168
|
+
const sectionRegex = new RegExp(
|
|
169
|
+
`(## ${escapeRegex(sectionName)}\\s*\\n)([\\s\\S]*?)((?=\\n## )|(?=\\n---\\s*$)|$)`,
|
|
170
|
+
"i"
|
|
171
|
+
);
|
|
172
|
+
if (content.match(sectionRegex)) {
|
|
173
|
+
const updatedContent = content.replace(sectionRegex, `$1
|
|
174
|
+
${newContent}
|
|
175
|
+
|
|
176
|
+
$3`);
|
|
177
|
+
await saveProjectInfo(updatedContent, workDir);
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
async function appendToSection(sectionName, contentToAdd, workDir) {
|
|
183
|
+
const currentContent = await getProjectSection(sectionName, workDir);
|
|
184
|
+
if (currentContent === null) return false;
|
|
185
|
+
const newContent = currentContent + "\n" + contentToAdd;
|
|
186
|
+
return updateProjectSection(sectionName, newContent, workDir);
|
|
187
|
+
}
|
|
188
|
+
async function getProjectSections(workDir) {
|
|
189
|
+
const content = await loadProjectInfo(workDir);
|
|
190
|
+
if (!content) return [];
|
|
191
|
+
const sectionRegex = /^## (.+)$/gm;
|
|
192
|
+
const sections = [];
|
|
193
|
+
let match;
|
|
194
|
+
while ((match = sectionRegex.exec(content)) !== null) {
|
|
195
|
+
if (match[1]) {
|
|
196
|
+
sections.push(match[1].trim());
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return sections;
|
|
200
|
+
}
|
|
201
|
+
async function getProjectInfoStructured(workDir) {
|
|
202
|
+
const dir = workDir || getWorkingDirectory(void 0, true);
|
|
203
|
+
const projectPath = join(dir, PROJECT_MD_PATH);
|
|
204
|
+
const content = await loadProjectInfo(dir);
|
|
205
|
+
if (!content) {
|
|
206
|
+
return {
|
|
207
|
+
exists: false,
|
|
208
|
+
path: projectPath,
|
|
209
|
+
sections: {},
|
|
210
|
+
raw: null
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const sectionNames = await getProjectSections(dir);
|
|
214
|
+
const sections = {};
|
|
215
|
+
for (const name of sectionNames) {
|
|
216
|
+
const sectionContent = await getProjectSection(name, dir);
|
|
217
|
+
if (sectionContent) {
|
|
218
|
+
sections[name] = sectionContent;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
exists: true,
|
|
223
|
+
path: projectPath,
|
|
224
|
+
sections,
|
|
225
|
+
raw: content
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function escapeRegex(str) {
|
|
229
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
230
|
+
}
|
|
20
231
|
|
|
21
|
-
// src/
|
|
232
|
+
// src/skills/built-in/security.ts
|
|
22
233
|
var ALWAYS_SKIP_FILES = [
|
|
23
234
|
/vulnerability-signatures\.[jt]s$/,
|
|
24
235
|
/vibe-code-signatures\.[jt]s$/,
|
|
@@ -75,10 +286,11 @@ var CRITICAL_PATTERNS = [
|
|
|
75
286
|
{ pattern: /sk_live_[A-Za-z0-9]{24,}/, severity: "critical", issue: "Stripe live API key exposed", fix: "Rotate Stripe key immediately. Use environment variables." },
|
|
76
287
|
{ pattern: /xox[baprs]-[A-Za-z0-9-]{10,}/, severity: "critical", issue: "Slack token exposed", fix: "Revoke Slack token and use environment variables." }
|
|
77
288
|
];
|
|
78
|
-
var
|
|
289
|
+
var SecuritySkill = class extends BaseSkill {
|
|
79
290
|
name = "security";
|
|
80
291
|
description = "AI-powered security analysis: vulnerabilities, injection risks, authentication issues";
|
|
81
292
|
version = "2.0.0";
|
|
293
|
+
author = "Trie Agent";
|
|
82
294
|
shouldActivate(context) {
|
|
83
295
|
return context.touchesAuth || context.touchesDatabase || context.touchesAPI || context.touchesUserData || context.touchesPayments || context.touchesSecurityConfig;
|
|
84
296
|
}
|
|
@@ -297,7 +509,7 @@ Output STRICT JSON:
|
|
|
297
509
|
}
|
|
298
510
|
};
|
|
299
511
|
|
|
300
|
-
// src/
|
|
512
|
+
// src/skills/built-in/privacy.ts
|
|
301
513
|
var PRIVACY_INDICATORS = {
|
|
302
514
|
high: [
|
|
303
515
|
{ pattern: /email|phone|ssn|social.*security|passport|driver.*license/i, reason: "PII fields" },
|
|
@@ -341,10 +553,11 @@ var CRITICAL_PRIVACY_PATTERNS = [
|
|
|
341
553
|
regulation: "PCI DSS"
|
|
342
554
|
}
|
|
343
555
|
];
|
|
344
|
-
var
|
|
556
|
+
var PrivacySkill = class extends BaseSkill {
|
|
345
557
|
name = "privacy";
|
|
346
558
|
description = "AI-powered privacy analysis: GDPR, CCPA, PCI-DSS compliance";
|
|
347
559
|
version = "2.0.0";
|
|
560
|
+
author = "Trie Agent";
|
|
348
561
|
shouldActivate(context) {
|
|
349
562
|
const hasUserDataSignals = context.touchesUserData && (context.touchesDatabase || context.touchesAuth || context.patterns?.hasFormHandling || context.patterns?.hasEmailHandling || context.touchesThirdPartyAPI);
|
|
350
563
|
const fileNameSignals = context.filePatterns.some(
|
|
@@ -591,7 +804,7 @@ Output STRICT JSON:
|
|
|
591
804
|
}
|
|
592
805
|
};
|
|
593
806
|
|
|
594
|
-
// src/
|
|
807
|
+
// src/skills/built-in/typecheck.ts
|
|
595
808
|
import { extname } from "path";
|
|
596
809
|
var SKIP_FILES = [
|
|
597
810
|
/node_modules\//,
|
|
@@ -602,10 +815,11 @@ var SKIP_FILES = [
|
|
|
602
815
|
/\.test\.[jt]sx?$/,
|
|
603
816
|
/\.spec\.[jt]sx?$/
|
|
604
817
|
];
|
|
605
|
-
var
|
|
818
|
+
var TypeCheckSkill = class extends BaseSkill {
|
|
606
819
|
name = "typecheck";
|
|
607
820
|
description = "Catches type errors and ensures type safety";
|
|
608
821
|
version = "1.0.0";
|
|
822
|
+
author = "Trie Agent";
|
|
609
823
|
shouldActivate(_context) {
|
|
610
824
|
return true;
|
|
611
825
|
}
|
|
@@ -676,11 +890,12 @@ var TypeCheckAgent = class extends BaseAgent {
|
|
|
676
890
|
}
|
|
677
891
|
};
|
|
678
892
|
|
|
679
|
-
// src/
|
|
680
|
-
var
|
|
893
|
+
// src/skills/built-in/comprehension.ts
|
|
894
|
+
var ComprehensionSkill = class extends BaseSkill {
|
|
681
895
|
name = "comprehension";
|
|
682
896
|
description = "Explains what code does in plain language for non-technical builders";
|
|
683
897
|
version = "1.0.0";
|
|
898
|
+
author = "Trie Agent";
|
|
684
899
|
shouldActivate(_context) {
|
|
685
900
|
return true;
|
|
686
901
|
}
|
|
@@ -893,7 +1108,7 @@ var ComprehensionAgent = class extends BaseAgent {
|
|
|
893
1108
|
}
|
|
894
1109
|
};
|
|
895
1110
|
|
|
896
|
-
// src/
|
|
1111
|
+
// src/skills/built-in/accessibility.ts
|
|
897
1112
|
var WCAG_CRITERIA = {
|
|
898
1113
|
// Perceivable
|
|
899
1114
|
"1.1.1": { name: "Non-text Content", level: "A", description: "All non-text content has text alternatives" },
|
|
@@ -920,6 +1135,7 @@ var WCAG_CRITERIA = {
|
|
|
920
1135
|
// Understandable
|
|
921
1136
|
"3.2.1": { name: "On Focus", level: "A", description: "Focus does not trigger unexpected context changes" },
|
|
922
1137
|
"3.2.2": { name: "On Input", level: "A", description: "Input does not trigger unexpected context changes" },
|
|
1138
|
+
"3.2.5": { name: "Change on Request", level: "AAA", description: "Changes of context are initiated only by user request" },
|
|
923
1139
|
"3.3.1": { name: "Error Identification", level: "A", description: "Errors are identified and described in text" },
|
|
924
1140
|
"3.3.2": { name: "Labels or Instructions", level: "A", description: "Labels or instructions are provided for user input" },
|
|
925
1141
|
// Robust
|
|
@@ -956,10 +1172,11 @@ var REQUIRED_ARIA_ATTRIBUTES = {
|
|
|
956
1172
|
"treegrid": [],
|
|
957
1173
|
"treeitem": []
|
|
958
1174
|
};
|
|
959
|
-
var
|
|
1175
|
+
var AccessibilitySkill = class extends BaseSkill {
|
|
960
1176
|
name = "accessibility";
|
|
961
1177
|
description = "WCAG 2.1 AA compliance: screen readers, keyboard nav, color contrast, touch targets, semantic HTML, ARIA patterns";
|
|
962
1178
|
version = "2.0.0";
|
|
1179
|
+
author = "Trie Agent";
|
|
963
1180
|
shouldActivate(context) {
|
|
964
1181
|
return context.touchesUI;
|
|
965
1182
|
}
|
|
@@ -990,12 +1207,13 @@ var AccessibilityAgent = class extends BaseAgent {
|
|
|
990
1207
|
const score = this.calculateAccessibilityScore(issues);
|
|
991
1208
|
issues.push(this.createIssue(
|
|
992
1209
|
this.generateIssueId(),
|
|
993
|
-
|
|
1210
|
+
"low",
|
|
1211
|
+
// Summary is informational, not a blocker
|
|
994
1212
|
`Accessibility Score: ${score}/100 \u2014 ${criticalCount} critical, ${seriousCount} serious issues`,
|
|
995
1213
|
`Review and fix accessibility issues starting with critical problems. Use axe-core or Lighthouse for additional validation.`,
|
|
996
1214
|
files[0] || "project",
|
|
997
1215
|
void 0,
|
|
998
|
-
0.
|
|
1216
|
+
0.6,
|
|
999
1217
|
void 0,
|
|
1000
1218
|
false
|
|
1001
1219
|
));
|
|
@@ -1078,7 +1296,7 @@ var AccessibilityAgent = class extends BaseAgent {
|
|
|
1078
1296
|
}
|
|
1079
1297
|
}
|
|
1080
1298
|
if (/<svg\s/i.test(line)) {
|
|
1081
|
-
const svgContext = this.getMultiLineElement(lines, i, "svg");
|
|
1299
|
+
const svgContext = this.getMultiLineElement(lines, i, "svg") ?? line;
|
|
1082
1300
|
if (!/aria-label|aria-labelledby|<title>|role=["']img["']/i.test(svgContext) && !/aria-hidden=["']true["']/i.test(svgContext)) {
|
|
1083
1301
|
issues.push(this.createIssue(
|
|
1084
1302
|
this.generateIssueId(),
|
|
@@ -1106,7 +1324,7 @@ var AccessibilityAgent = class extends BaseAgent {
|
|
|
1106
1324
|
const line = lines[i];
|
|
1107
1325
|
const lineNumber = i + 1;
|
|
1108
1326
|
if (/<button/i.test(line) || /Button\s/i.test(line)) {
|
|
1109
|
-
const buttonContext = this.getMultiLineElement(lines, i, "button")
|
|
1327
|
+
const buttonContext = this.getMultiLineElement(lines, i, "button") ?? this.getMultiLineElement(lines, i, "Button") ?? line;
|
|
1110
1328
|
const hasIconOnly = />\s*<(Icon|svg|i\s|img)[^>]*>\s*<\/(button|Button)/i.test(buttonContext) || />\s*<[A-Z][a-zA-Z]*Icon[^>]*\s*\/>\s*<\/(button|Button)/i.test(buttonContext) || /<(Icon|svg)[^>]*\/>\s*<\/(button|Button)/i.test(buttonContext);
|
|
1111
1329
|
const hasAccessibleName = /aria-label|aria-labelledby|title=|sr-only|visually-hidden/i.test(buttonContext);
|
|
1112
1330
|
if (hasIconOnly && !hasAccessibleName) {
|
|
@@ -1157,7 +1375,7 @@ var AccessibilityAgent = class extends BaseAgent {
|
|
|
1157
1375
|
}
|
|
1158
1376
|
}
|
|
1159
1377
|
if (/<a\s/i.test(line) || /<Link\s/i.test(line)) {
|
|
1160
|
-
const linkContext = this.getMultiLineElement(lines, i, "a")
|
|
1378
|
+
const linkContext = this.getMultiLineElement(lines, i, "a") ?? this.getMultiLineElement(lines, i, "Link") ?? line;
|
|
1161
1379
|
if (!/href=|to=/i.test(linkContext)) {
|
|
1162
1380
|
issues.push(this.createIssue(
|
|
1163
1381
|
this.generateIssueId(),
|
|
@@ -1931,7 +2149,7 @@ Review code for comprehensive accessibility compliance.
|
|
|
1931
2149
|
}
|
|
1932
2150
|
};
|
|
1933
2151
|
|
|
1934
|
-
// src/
|
|
2152
|
+
// src/skills/built-in/design-engineer.ts
|
|
1935
2153
|
var DOMAIN_DESIGN_RULES = {
|
|
1936
2154
|
fitness: {
|
|
1937
2155
|
defaultMode: "dark",
|
|
@@ -2149,10 +2367,11 @@ var PROJECT_CONTEXTS = {
|
|
|
2149
2367
|
effects: ["image reveals", "cursor effects", "page transitions", "scroll-linked galleries"]
|
|
2150
2368
|
}
|
|
2151
2369
|
};
|
|
2152
|
-
var
|
|
2370
|
+
var DesignEngineerSkill = class extends BaseSkill {
|
|
2153
2371
|
name = "design-engineer";
|
|
2154
2372
|
description = "Award-winning frontend craft: design systems, motion design, creative CSS, modern color palettes, Awwwards-level polish";
|
|
2155
2373
|
version = "2.0.0";
|
|
2374
|
+
author = "Trie Agent";
|
|
2156
2375
|
shouldActivate(context) {
|
|
2157
2376
|
return context.touchesUI;
|
|
2158
2377
|
}
|
|
@@ -2187,12 +2406,14 @@ var DesignEngineerAgent = class extends BaseAgent {
|
|
|
2187
2406
|
const domainRecommendation = this.getDomainRecommendations(designContext);
|
|
2188
2407
|
issues.push(this.createIssue(
|
|
2189
2408
|
this.generateIssueId(),
|
|
2190
|
-
|
|
2409
|
+
"low",
|
|
2410
|
+
// Design scores are informational, not blockers
|
|
2191
2411
|
`Design Health Score: ${healthScore.score}/100 | Slop Score: ${healthScore.slopScore}/100`,
|
|
2192
2412
|
`Breakdown: Token adoption ${healthScore.breakdown.tokenAdoption}%, Contrast ${healthScore.breakdown.contrastCompliance}%, Spacing ${healthScore.breakdown.spacingConsistency}%, Typography ${healthScore.breakdown.typographySystem}%, Surface hierarchy ${healthScore.breakdown.surfaceHierarchy}%. ${domainRecommendation}`,
|
|
2193
2413
|
files[0] || "project",
|
|
2194
2414
|
void 0,
|
|
2195
|
-
0.
|
|
2415
|
+
0.6,
|
|
2416
|
+
// Lower confidence for aggregate scores
|
|
2196
2417
|
void 0,
|
|
2197
2418
|
false
|
|
2198
2419
|
));
|
|
@@ -2921,12 +3142,13 @@ var DesignEngineerAgent = class extends BaseAgent {
|
|
|
2921
3142
|
const suggestedFix = this.suggestLighterSurface(bgColors[0], 10);
|
|
2922
3143
|
issues.push(this.createIssue(
|
|
2923
3144
|
this.generateIssueId(),
|
|
2924
|
-
"
|
|
3145
|
+
"moderate",
|
|
3146
|
+
// Design issues should not be critical - reserved for security/data loss
|
|
2925
3147
|
`AI SLOP: Surfaces too similar (${minDelta.toFixed(1)}% lightness delta). Min 8% required.`,
|
|
2926
3148
|
`Change surface color to create visible hierarchy. Suggestion: use ${suggestedFix} for elevated surfaces. Reference: zinc scale at tailwindcss.com/docs/customizing-colors`,
|
|
2927
3149
|
file,
|
|
2928
3150
|
void 0,
|
|
2929
|
-
0.
|
|
3151
|
+
0.75,
|
|
2930
3152
|
void 0,
|
|
2931
3153
|
true
|
|
2932
3154
|
));
|
|
@@ -2937,12 +3159,13 @@ var DesignEngineerAgent = class extends BaseAgent {
|
|
|
2937
3159
|
if (accentHues.length > AI_SLOP_BLOCKERS.accents.maxAccentHues + 1) {
|
|
2938
3160
|
issues.push(this.createIssue(
|
|
2939
3161
|
this.generateIssueId(),
|
|
2940
|
-
"
|
|
3162
|
+
"low",
|
|
3163
|
+
// Design preference, not a real issue
|
|
2941
3164
|
`AI SLOP: ${accentHues.length} accent color families detected. Use ONE primary accent.`,
|
|
2942
3165
|
`Standardize on a single accent hue family. Semantic colors (success/warning/error) are exempt. Reference: radix-ui.com/colors for cohesive scales`,
|
|
2943
3166
|
file,
|
|
2944
3167
|
void 0,
|
|
2945
|
-
0.
|
|
3168
|
+
0.65,
|
|
2946
3169
|
void 0,
|
|
2947
3170
|
true
|
|
2948
3171
|
));
|
|
@@ -2972,12 +3195,14 @@ var DesignEngineerAgent = class extends BaseAgent {
|
|
|
2972
3195
|
const desaturated = this.desaturateColor(color, 60);
|
|
2973
3196
|
issues.push(this.createIssue(
|
|
2974
3197
|
this.generateIssueId(),
|
|
2975
|
-
"
|
|
3198
|
+
"low",
|
|
3199
|
+
// Design suggestions should be low severity, not serious
|
|
2976
3200
|
`AI SLOP: Neon color detected (${color}, ${saturation.toFixed(0)}% saturation)`,
|
|
2977
3201
|
`Desaturate to ${desaturated}. Max recommended saturation: 75%. Source colors from radix-ui.com/colors for contrast-safe alternatives.`,
|
|
2978
3202
|
file,
|
|
2979
3203
|
void 0,
|
|
2980
|
-
0.
|
|
3204
|
+
0.7,
|
|
3205
|
+
// Lower confidence for design opinions
|
|
2981
3206
|
void 0,
|
|
2982
3207
|
true
|
|
2983
3208
|
));
|
|
@@ -3649,11 +3874,12 @@ Output STRICT JSON:
|
|
|
3649
3874
|
}
|
|
3650
3875
|
};
|
|
3651
3876
|
|
|
3652
|
-
// src/
|
|
3653
|
-
var
|
|
3877
|
+
// src/skills/built-in/legal.ts
|
|
3878
|
+
var LegalSkill = class extends BaseSkill {
|
|
3654
3879
|
name = "legal";
|
|
3655
3880
|
description = "Comprehensive legal compliance for app development: licensing, ToS, accessibility, IP, data protection, e-commerce, and regulatory requirements";
|
|
3656
3881
|
version = "2.0.0";
|
|
3882
|
+
author = "Trie Agent";
|
|
3657
3883
|
shouldActivate(context) {
|
|
3658
3884
|
return context.touchesUserData || context.touchesPayments || context.touchesAuth || context.touchesUI || context.touchesAPI || context.touchesThirdPartyAPI || context.touchesHealthData;
|
|
3659
3885
|
}
|
|
@@ -4347,18 +4573,23 @@ var LegalAgent = class extends BaseAgent {
|
|
|
4347
4573
|
checkAgeRestrictions(content, file) {
|
|
4348
4574
|
const issues = [];
|
|
4349
4575
|
const lines = content.split("\n");
|
|
4576
|
+
const isReactChildrenUsage = /children\s*[:\?})]|React\.ReactNode|PropsWithChildren|\.children/i.test(content);
|
|
4350
4577
|
for (let i = 0; i < lines.length; i++) {
|
|
4351
4578
|
const line = lines[i];
|
|
4352
|
-
|
|
4579
|
+
const childPattern = /\b(kids|minor|under.*13|age.*13)\b/i;
|
|
4580
|
+
const childrenPropPattern = /children\s*[:\?})\]=]|\.children|ReactNode/i;
|
|
4581
|
+
if (childPattern.test(line) && !childrenPropPattern.test(line) && !isReactChildrenUsage) {
|
|
4353
4582
|
if (!/coppa|parentalConsent|verifiableConsent/i.test(content)) {
|
|
4354
4583
|
issues.push(this.createIssue(
|
|
4355
4584
|
this.generateIssueId(),
|
|
4356
|
-
"
|
|
4357
|
-
|
|
4358
|
-
"
|
|
4585
|
+
"serious",
|
|
4586
|
+
// Downgraded from critical - needs human review
|
|
4587
|
+
"Potential child-directed content without COPPA compliance",
|
|
4588
|
+
"COPPA requires verifiable parental consent before collecting data from children under 13. Review if this applies to your app.",
|
|
4359
4589
|
file,
|
|
4360
4590
|
i + 1,
|
|
4361
|
-
0.
|
|
4591
|
+
0.7,
|
|
4592
|
+
// Lower confidence - needs human verification
|
|
4362
4593
|
"COPPA - Children's Online Privacy Protection Act",
|
|
4363
4594
|
false
|
|
4364
4595
|
));
|
|
@@ -4636,13 +4867,14 @@ var LegalAgent = class extends BaseAgent {
|
|
|
4636
4867
|
}
|
|
4637
4868
|
};
|
|
4638
4869
|
|
|
4639
|
-
// src/
|
|
4870
|
+
// src/skills/built-in/test.ts
|
|
4640
4871
|
import { basename, dirname } from "path";
|
|
4641
|
-
import { existsSync } from "fs";
|
|
4642
|
-
var
|
|
4872
|
+
import { existsSync as existsSync2 } from "fs";
|
|
4873
|
+
var TestSkill = class extends BaseSkill {
|
|
4643
4874
|
name = "test";
|
|
4644
4875
|
description = "Test coverage analysis, test quality, and testing best practices";
|
|
4645
4876
|
version = "1.0.0";
|
|
4877
|
+
author = "Trie Agent";
|
|
4646
4878
|
shouldActivate(context) {
|
|
4647
4879
|
return context.isNewFeature || context.touchesAuth || context.touchesPayments || context.touchesAPI;
|
|
4648
4880
|
}
|
|
@@ -4666,13 +4898,21 @@ var TestAgent = class extends BaseAgent {
|
|
|
4666
4898
|
const issues = [];
|
|
4667
4899
|
const fileName = basename(file);
|
|
4668
4900
|
const fileDir = dirname(file);
|
|
4901
|
+
if (/index\.[jt]sx?$|types?\.[jt]s$|constants?\.[jt]s$|config\.[jt]s$/.test(fileName)) {
|
|
4902
|
+
return issues;
|
|
4903
|
+
}
|
|
4904
|
+
if (/\.(tsx|jsx)$/.test(fileName) && !/\.test\.|\.spec\./.test(fileName)) {
|
|
4905
|
+
if (!/auth|login|payment|checkout|form/i.test(content)) {
|
|
4906
|
+
return issues;
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4669
4909
|
const testPatterns = [
|
|
4670
4910
|
file.replace(/\.(ts|js|tsx|jsx)$/, ".test.$1"),
|
|
4671
4911
|
file.replace(/\.(ts|js|tsx|jsx)$/, ".spec.$1"),
|
|
4672
4912
|
`${fileDir}/__tests__/${fileName}`,
|
|
4673
4913
|
`${fileDir}/../__tests__/${fileName}`
|
|
4674
4914
|
];
|
|
4675
|
-
const hasTestFile = testPatterns.some((pattern) =>
|
|
4915
|
+
const hasTestFile = testPatterns.some((pattern) => existsSync2(pattern));
|
|
4676
4916
|
if (!hasTestFile) {
|
|
4677
4917
|
const severity = this.determineTestSeverity(content);
|
|
4678
4918
|
issues.push(this.createIssue(
|
|
@@ -4682,7 +4922,8 @@ var TestAgent = class extends BaseAgent {
|
|
|
4682
4922
|
`Create test file: ${fileName.replace(/\.(ts|js|tsx|jsx)$/, ".test.$1")}`,
|
|
4683
4923
|
file,
|
|
4684
4924
|
void 0,
|
|
4685
|
-
0.
|
|
4925
|
+
0.7,
|
|
4926
|
+
// Lower confidence - we can't know if tests exist elsewhere
|
|
4686
4927
|
void 0,
|
|
4687
4928
|
false
|
|
4688
4929
|
));
|
|
@@ -4700,52 +4941,8 @@ var TestAgent = class extends BaseAgent {
|
|
|
4700
4941
|
}
|
|
4701
4942
|
checkTestablePatterns(content, file) {
|
|
4702
4943
|
const issues = [];
|
|
4703
|
-
const lines = content.split("\n");
|
|
4704
4944
|
const exports = content.match(/export\s+(function|class|const|async function)/g);
|
|
4705
4945
|
const exportCount = exports?.length || 0;
|
|
4706
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4707
|
-
const line = lines[i];
|
|
4708
|
-
const lineNumber = i + 1;
|
|
4709
|
-
if ((line.match(/if|else|switch|\?.*:/g) || []).length >= 3) {
|
|
4710
|
-
issues.push(this.createIssue(
|
|
4711
|
-
this.generateIssueId(),
|
|
4712
|
-
"low",
|
|
4713
|
-
"Complex conditional logic should have unit tests",
|
|
4714
|
-
"Write tests covering different branches of this logic",
|
|
4715
|
-
file,
|
|
4716
|
-
lineNumber,
|
|
4717
|
-
0.7,
|
|
4718
|
-
void 0,
|
|
4719
|
-
false
|
|
4720
|
-
));
|
|
4721
|
-
}
|
|
4722
|
-
if (/catch\s*\(|\.catch\(/.test(line)) {
|
|
4723
|
-
issues.push(this.createIssue(
|
|
4724
|
-
this.generateIssueId(),
|
|
4725
|
-
"low",
|
|
4726
|
-
"Error handling path should be tested",
|
|
4727
|
-
"Write tests that trigger and verify error handling behavior",
|
|
4728
|
-
file,
|
|
4729
|
-
lineNumber,
|
|
4730
|
-
0.65,
|
|
4731
|
-
void 0,
|
|
4732
|
-
false
|
|
4733
|
-
));
|
|
4734
|
-
}
|
|
4735
|
-
if (/async\s+function|await\s+/.test(line) && /fetch|axios|database|query/i.test(line)) {
|
|
4736
|
-
issues.push(this.createIssue(
|
|
4737
|
-
this.generateIssueId(),
|
|
4738
|
-
"moderate",
|
|
4739
|
-
"Async operation should have integration tests",
|
|
4740
|
-
"Mock external dependencies and test async behavior",
|
|
4741
|
-
file,
|
|
4742
|
-
lineNumber,
|
|
4743
|
-
0.75,
|
|
4744
|
-
void 0,
|
|
4745
|
-
false
|
|
4746
|
-
));
|
|
4747
|
-
}
|
|
4748
|
-
}
|
|
4749
4946
|
if (exportCount > 5) {
|
|
4750
4947
|
issues.push(this.createIssue(
|
|
4751
4948
|
this.generateIssueId(),
|
|
@@ -4763,11 +4960,12 @@ var TestAgent = class extends BaseAgent {
|
|
|
4763
4960
|
}
|
|
4764
4961
|
};
|
|
4765
4962
|
|
|
4766
|
-
// src/
|
|
4767
|
-
var
|
|
4963
|
+
// src/skills/built-in/software-architect.ts
|
|
4964
|
+
var SoftwareArchitectSkill = class extends BaseSkill {
|
|
4768
4965
|
name = "software-architect";
|
|
4769
4966
|
description = "Architecture patterns, code organization, SOLID principles, and scalability";
|
|
4770
4967
|
version = "1.0.0";
|
|
4968
|
+
author = "Trie Agent";
|
|
4771
4969
|
shouldActivate(context) {
|
|
4772
4970
|
return context.isNewFeature || context.touchesDatabase || context.touchesAPI || context.linesChanged > 200;
|
|
4773
4971
|
}
|
|
@@ -4791,18 +4989,23 @@ var SoftwareArchitectAgent = class extends BaseAgent {
|
|
|
4791
4989
|
for (let i = 0; i < lines.length; i++) {
|
|
4792
4990
|
const line = lines[i];
|
|
4793
4991
|
const lineNumber = i + 1;
|
|
4794
|
-
if (/\.(tsx|jsx)$/.test(file)
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4992
|
+
if (/\.(tsx|jsx)$/.test(file)) {
|
|
4993
|
+
const hasOrmImport = /from\s+['"]prisma|from\s+['"]mongoose|from\s+['"]sequelize|from\s+['"]typeorm|from\s+['"]@prisma/i.test(line);
|
|
4994
|
+
const hasDirectQuery = /\.query\s*\(|\.execute\s*\(|\.raw\s*\(/.test(line);
|
|
4995
|
+
if (hasOrmImport || hasDirectQuery) {
|
|
4996
|
+
issues.push(this.createIssue(
|
|
4997
|
+
this.generateIssueId(),
|
|
4998
|
+
"moderate",
|
|
4999
|
+
// Downgraded - sometimes this is intentional (Next.js server components)
|
|
5000
|
+
"Direct database access in UI component",
|
|
5001
|
+
"Move database logic to API layer or server action",
|
|
5002
|
+
file,
|
|
5003
|
+
lineNumber,
|
|
5004
|
+
0.75,
|
|
5005
|
+
void 0,
|
|
5006
|
+
false
|
|
5007
|
+
));
|
|
5008
|
+
}
|
|
4806
5009
|
}
|
|
4807
5010
|
if (/controller|handler|route/i.test(file) && lines.slice(i, i + 20).join("\n").split("if").length > 5) {
|
|
4808
5011
|
issues.push(this.createIssue(
|
|
@@ -4852,18 +5055,19 @@ var SoftwareArchitectAgent = class extends BaseAgent {
|
|
|
4852
5055
|
checkCodeOrganization(content, file) {
|
|
4853
5056
|
const issues = [];
|
|
4854
5057
|
const hasUI = /jsx|tsx|render|component|React|useState/i.test(content);
|
|
4855
|
-
const hasAPI = /fetch|axios
|
|
4856
|
-
const hasDB = /prisma|mongoose|query
|
|
5058
|
+
const hasAPI = /fetch\(|axios\.|api\/|endpoint|route/i.test(content);
|
|
5059
|
+
const hasDB = /from\s+['"]prisma|from\s+['"]mongoose|from\s+['"]sequelize|\.query\s*\(/i.test(content);
|
|
4857
5060
|
const concernCount = [hasUI, hasAPI, hasDB].filter(Boolean).length;
|
|
4858
|
-
if (concernCount >=
|
|
5061
|
+
if (concernCount >= 3) {
|
|
4859
5062
|
issues.push(this.createIssue(
|
|
4860
5063
|
this.generateIssueId(),
|
|
4861
|
-
"
|
|
5064
|
+
"low",
|
|
5065
|
+
// Downgraded - this is more of a suggestion
|
|
4862
5066
|
"Multiple concerns mixed in single file",
|
|
4863
|
-
"
|
|
5067
|
+
"Consider separating UI, API, and data access layers",
|
|
4864
5068
|
file,
|
|
4865
5069
|
void 0,
|
|
4866
|
-
0.
|
|
5070
|
+
0.6,
|
|
4867
5071
|
void 0,
|
|
4868
5072
|
false
|
|
4869
5073
|
));
|
|
@@ -4986,11 +5190,12 @@ Output STRICT JSON:
|
|
|
4986
5190
|
}
|
|
4987
5191
|
};
|
|
4988
5192
|
|
|
4989
|
-
// src/
|
|
4990
|
-
var
|
|
5193
|
+
// src/skills/built-in/devops.ts
|
|
5194
|
+
var DevOpsSkill = class extends BaseSkill {
|
|
4991
5195
|
name = "devops";
|
|
4992
5196
|
description = "Infrastructure, deployment, configuration, and operational concerns";
|
|
4993
5197
|
version = "1.0.0";
|
|
5198
|
+
author = "Trie Agent";
|
|
4994
5199
|
shouldActivate(context) {
|
|
4995
5200
|
return context.touchesSecurityConfig || context.touchesDatabase || context.touchesPayments || context.touchesHealthData;
|
|
4996
5201
|
}
|
|
@@ -5191,7 +5396,7 @@ Output STRICT JSON:
|
|
|
5191
5396
|
}
|
|
5192
5397
|
};
|
|
5193
5398
|
|
|
5194
|
-
// src/
|
|
5399
|
+
// src/skills/built-in/bug-finding.ts
|
|
5195
5400
|
var BUG_INDICATORS = {
|
|
5196
5401
|
high: [
|
|
5197
5402
|
{ pattern: /async|await|promise/i, reason: "async code" },
|
|
@@ -5227,10 +5432,11 @@ var CRITICAL_BUG_PATTERNS = [
|
|
|
5227
5432
|
fix: "Use === for comparison instead of ="
|
|
5228
5433
|
}
|
|
5229
5434
|
];
|
|
5230
|
-
var
|
|
5435
|
+
var BugFindingSkill = class extends BaseSkill {
|
|
5231
5436
|
name = "bug-finding";
|
|
5232
5437
|
description = "AI-powered bug detection: null safety, edge cases, async issues, runtime errors";
|
|
5233
5438
|
version = "2.0.0";
|
|
5439
|
+
author = "Trie Agent";
|
|
5234
5440
|
shouldActivate(context) {
|
|
5235
5441
|
return context.touchesPayments || context.isNewFeature || context.touchesDatabase || context.touchesAPI;
|
|
5236
5442
|
}
|
|
@@ -5421,11 +5627,12 @@ Output STRICT JSON:
|
|
|
5421
5627
|
}
|
|
5422
5628
|
};
|
|
5423
5629
|
|
|
5424
|
-
// src/
|
|
5425
|
-
var
|
|
5630
|
+
// src/skills/built-in/user-testing.ts
|
|
5631
|
+
var UserTestingSkill = class extends BaseSkill {
|
|
5426
5632
|
name = "user-testing";
|
|
5427
5633
|
description = "Simulates user personas (happy path, security tester, confused user) to find vulnerabilities";
|
|
5428
5634
|
version = "2.0.0";
|
|
5635
|
+
author = "Trie Agent";
|
|
5429
5636
|
get priority() {
|
|
5430
5637
|
return {
|
|
5431
5638
|
name: this.name,
|
|
@@ -5891,11 +6098,12 @@ var UserTestingAgent = class extends BaseAgent {
|
|
|
5891
6098
|
}
|
|
5892
6099
|
};
|
|
5893
6100
|
|
|
5894
|
-
// src/
|
|
5895
|
-
var
|
|
6101
|
+
// src/skills/built-in/trie-clean.ts
|
|
6102
|
+
var TrieCleanSkill = class extends BaseSkill {
|
|
5896
6103
|
name = "trie_clean";
|
|
5897
6104
|
description = "Reviews AI-generated code for common mistakes and best practices (for non-technical users)";
|
|
5898
6105
|
version = "1.0.0";
|
|
6106
|
+
author = "Trie Agent";
|
|
5899
6107
|
get priority() {
|
|
5900
6108
|
return {
|
|
5901
6109
|
name: this.name,
|
|
@@ -5975,7 +6183,7 @@ var TrieCleanAgent = class extends BaseAgent {
|
|
|
5975
6183
|
}
|
|
5976
6184
|
};
|
|
5977
6185
|
|
|
5978
|
-
// src/
|
|
6186
|
+
// src/skills/built-in/soc2.ts
|
|
5979
6187
|
var SOC2_PATTERNS = {
|
|
5980
6188
|
// CC6 - Logical Access Controls
|
|
5981
6189
|
hardcodedSecrets: [
|
|
@@ -6042,10 +6250,11 @@ var REGULATION_DESCRIPTIONS = {
|
|
|
6042
6250
|
"CC7.3": "Incident Detection - Proper error handling and alerting",
|
|
6043
6251
|
"CC8.1": "Change Management - Controlled changes with proper review"
|
|
6044
6252
|
};
|
|
6045
|
-
var
|
|
6253
|
+
var SOC2Skill = class extends BaseSkill {
|
|
6046
6254
|
name = "soc2";
|
|
6047
6255
|
description = "SOC 2 Type II compliance: access controls, encryption, logging, change management";
|
|
6048
6256
|
version = "1.0.0";
|
|
6257
|
+
author = "Trie Agent";
|
|
6049
6258
|
shouldActivate(context) {
|
|
6050
6259
|
return context.touchesAuth || context.touchesAPI || context.touchesDatabase || context.touchesUserData || context.touchesSecurityConfig || context.touchesLogging;
|
|
6051
6260
|
}
|
|
@@ -6189,11 +6398,12 @@ Output STRICT JSON:
|
|
|
6189
6398
|
}
|
|
6190
6399
|
};
|
|
6191
6400
|
|
|
6192
|
-
// src/
|
|
6193
|
-
var
|
|
6401
|
+
// src/skills/built-in/super-reviewer.ts
|
|
6402
|
+
var SuperReviewerSkill = class extends BaseSkill {
|
|
6194
6403
|
name = "super-reviewer";
|
|
6195
6404
|
description = "Interactive PR review: walks through changes file-by-file, explains each chunk, waits for cross-examination";
|
|
6196
6405
|
version = "1.0.0";
|
|
6406
|
+
author = "Trie Agent";
|
|
6197
6407
|
shouldActivate(_context) {
|
|
6198
6408
|
return false;
|
|
6199
6409
|
}
|
|
@@ -6339,11 +6549,12 @@ var CRITICAL_REVIEW_CHECKLIST = {
|
|
|
6339
6549
|
]
|
|
6340
6550
|
};
|
|
6341
6551
|
|
|
6342
|
-
// src/
|
|
6343
|
-
var
|
|
6552
|
+
// src/skills/built-in/performance.ts
|
|
6553
|
+
var PerformanceSkill = class extends BaseSkill {
|
|
6344
6554
|
name = "performance";
|
|
6345
6555
|
description = "Surfaces performance patterns for human review: memory, renders, bundles, queries";
|
|
6346
6556
|
version = "1.0.0";
|
|
6557
|
+
author = "Trie Agent";
|
|
6347
6558
|
shouldActivate(context) {
|
|
6348
6559
|
return context.touchesUI || context.touchesDatabase || context.touchesAPI || context.patterns.hasAsyncCode || context.linesChanged > 100;
|
|
6349
6560
|
}
|
|
@@ -6638,11 +6849,12 @@ var PerformanceAgent = class extends BaseAgent {
|
|
|
6638
6849
|
}
|
|
6639
6850
|
};
|
|
6640
6851
|
|
|
6641
|
-
// src/
|
|
6642
|
-
var
|
|
6852
|
+
// src/skills/built-in/e2e.ts
|
|
6853
|
+
var E2ESkill = class extends BaseSkill {
|
|
6643
6854
|
name = "e2e";
|
|
6644
6855
|
description = "Identifies E2E test gaps and flaky test patterns for human review";
|
|
6645
6856
|
version = "1.0.0";
|
|
6857
|
+
author = "Trie Agent";
|
|
6646
6858
|
shouldActivate(context) {
|
|
6647
6859
|
return context.touchesUI || context.isNewFeature || context.patterns.hasFormHandling;
|
|
6648
6860
|
}
|
|
@@ -6870,11 +7082,12 @@ var E2EAgent = class extends BaseAgent {
|
|
|
6870
7082
|
}
|
|
6871
7083
|
};
|
|
6872
7084
|
|
|
6873
|
-
// src/
|
|
6874
|
-
var
|
|
7085
|
+
// src/skills/built-in/visual-qa.ts
|
|
7086
|
+
var VisualQASkill = class extends BaseSkill {
|
|
6875
7087
|
name = "visual-qa";
|
|
6876
7088
|
description = "Surfaces potential visual/layout issues in CSS for human review";
|
|
6877
7089
|
version = "1.0.0";
|
|
7090
|
+
author = "Trie Agent";
|
|
6878
7091
|
shouldActivate(context) {
|
|
6879
7092
|
return context.touchesUI || context.filePatterns.some((f) => /\.(css|scss|sass|less|styled)/.test(f));
|
|
6880
7093
|
}
|
|
@@ -7205,11 +7418,12 @@ var VisualQAAgent = class extends BaseAgent {
|
|
|
7205
7418
|
}
|
|
7206
7419
|
};
|
|
7207
7420
|
|
|
7208
|
-
// src/
|
|
7209
|
-
var
|
|
7421
|
+
// src/skills/built-in/data-flow.ts
|
|
7422
|
+
var DataFlowSkill = class extends BaseSkill {
|
|
7210
7423
|
name = "data-flow";
|
|
7211
7424
|
description = "Detects schema mismatches, placeholder data, and data integrity issues";
|
|
7212
7425
|
version = "1.0.0";
|
|
7426
|
+
author = "Trie Agent";
|
|
7213
7427
|
shouldActivate(context) {
|
|
7214
7428
|
return context.touchesAPI || context.touchesDatabase || context.isNewFeature || context.patterns.hasFormHandling;
|
|
7215
7429
|
}
|
|
@@ -7240,13 +7454,9 @@ var DataFlowAgent = class extends BaseAgent {
|
|
|
7240
7454
|
const placeholderPatterns = [
|
|
7241
7455
|
{ pattern: /["']lorem ipsum/i, desc: "Lorem ipsum placeholder text" },
|
|
7242
7456
|
{ pattern: /["']test@(test|example)\.com["']/i, desc: "Test email address" },
|
|
7243
|
-
{ pattern: /["']
|
|
7244
|
-
{ pattern: /["']xxx["']|["']yyy["']|["']zzz["']/i, desc: "Placeholder string (xxx/yyy/zzz)" },
|
|
7245
|
-
{ pattern: /["']foo["']|["']bar["']|["']baz["']/, desc: "Common placeholder names" },
|
|
7246
|
-
{ pattern: /["']asdf["']|["']qwerty["']/i, desc: "Keyboard mash placeholder" },
|
|
7457
|
+
{ pattern: /["']PLACEHOLDER["']/i, desc: "PLACEHOLDER string value" },
|
|
7247
7458
|
{ pattern: /["']changeme["']|["']replace[-_]?me["']/i, desc: "Replace-me placeholder" },
|
|
7248
|
-
{ pattern: /["']your[-_]
|
|
7249
|
-
{ pattern: /price:\s*0[,\s]|amount:\s*0[,\s]|total:\s*0[,\s]/i, desc: "Zero price/amount (placeholder?)" }
|
|
7459
|
+
{ pattern: /["']your[-_]?(api[-_]?key|token|password)[-_]?here["']/i, desc: "Your-X-here placeholder" }
|
|
7250
7460
|
];
|
|
7251
7461
|
for (let i = 0; i < lines.length; i++) {
|
|
7252
7462
|
const line = lines[i] || "";
|
|
@@ -7351,45 +7561,17 @@ var DataFlowAgent = class extends BaseAgent {
|
|
|
7351
7561
|
for (let i = 0; i < lines.length; i++) {
|
|
7352
7562
|
const line = lines[i] || "";
|
|
7353
7563
|
const lineNumber = i + 1;
|
|
7354
|
-
const optionalChainCount = (line.match(/\?\./g) || []).length;
|
|
7355
|
-
if (optionalChainCount >= 3) {
|
|
7356
|
-
issues.push(this.createIssue(
|
|
7357
|
-
this.generateIssueId(),
|
|
7358
|
-
"low",
|
|
7359
|
-
"Heavy optional chaining may indicate schema uncertainty",
|
|
7360
|
-
"Review: if data structure is well-defined, some ?. may be unnecessary",
|
|
7361
|
-
file,
|
|
7362
|
-
lineNumber,
|
|
7363
|
-
0.5,
|
|
7364
|
-
void 0,
|
|
7365
|
-
false,
|
|
7366
|
-
{ category: "schema", effort: "medium" }
|
|
7367
|
-
));
|
|
7368
|
-
}
|
|
7369
|
-
if (/as\s+\w+(\[\])?[,;\s)]/.test(line) && !/as\s+const/.test(line)) {
|
|
7370
|
-
issues.push(this.createIssue(
|
|
7371
|
-
this.generateIssueId(),
|
|
7372
|
-
"low",
|
|
7373
|
-
"Type assertion may hide actual type mismatch",
|
|
7374
|
-
"Prefer type guards or validation for runtime safety",
|
|
7375
|
-
file,
|
|
7376
|
-
lineNumber,
|
|
7377
|
-
0.45,
|
|
7378
|
-
void 0,
|
|
7379
|
-
false,
|
|
7380
|
-
{ category: "type-assertion", effort: "medium" }
|
|
7381
|
-
));
|
|
7382
|
-
}
|
|
7383
7564
|
if (/response\.(data|body)\.(\w+)\.(\w+)\.(\w+)/.test(line)) {
|
|
7384
7565
|
if (!/\?\./.test(line)) {
|
|
7385
7566
|
issues.push(this.createIssue(
|
|
7386
7567
|
this.generateIssueId(),
|
|
7387
|
-
"
|
|
7568
|
+
"low",
|
|
7569
|
+
// Downgraded - optional chaining is good but not critical
|
|
7388
7570
|
"Deep property access on API response without null checks",
|
|
7389
7571
|
"Add optional chaining or validate response structure",
|
|
7390
7572
|
file,
|
|
7391
7573
|
lineNumber,
|
|
7392
|
-
0.
|
|
7574
|
+
0.6,
|
|
7393
7575
|
void 0,
|
|
7394
7576
|
false,
|
|
7395
7577
|
{ category: "null-access", effort: "easy" }
|
|
@@ -7438,23 +7620,6 @@ var DataFlowAgent = class extends BaseAgent {
|
|
|
7438
7620
|
));
|
|
7439
7621
|
}
|
|
7440
7622
|
}
|
|
7441
|
-
if (/Number\(|parseFloat\(|\+\s*\w+/.test(line)) {
|
|
7442
|
-
const context = lines.slice(i, Math.min(i + 3, lines.length)).join("\n");
|
|
7443
|
-
if (!/isNaN|Number\.isFinite|isFinite|\|\|\s*0/.test(context)) {
|
|
7444
|
-
issues.push(this.createIssue(
|
|
7445
|
-
this.generateIssueId(),
|
|
7446
|
-
"low",
|
|
7447
|
-
"Number conversion without NaN check",
|
|
7448
|
-
"Check for NaN: isNaN(result) or provide fallback: value || 0",
|
|
7449
|
-
file,
|
|
7450
|
-
lineNumber,
|
|
7451
|
-
0.55,
|
|
7452
|
-
void 0,
|
|
7453
|
-
false,
|
|
7454
|
-
{ category: "nan", effort: "easy" }
|
|
7455
|
-
));
|
|
7456
|
-
}
|
|
7457
|
-
}
|
|
7458
7623
|
}
|
|
7459
7624
|
return issues;
|
|
7460
7625
|
}
|
|
@@ -7464,45 +7629,40 @@ var DataFlowAgent = class extends BaseAgent {
|
|
|
7464
7629
|
for (let i = 0; i < lines.length; i++) {
|
|
7465
7630
|
const line = lines[i] || "";
|
|
7466
7631
|
const lineNumber = i + 1;
|
|
7467
|
-
if (
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
));
|
|
7481
|
-
}
|
|
7632
|
+
if (/==\s*null/.test(line) || /==\s*undefined/.test(line)) {
|
|
7633
|
+
issues.push(this.createIssue(
|
|
7634
|
+
this.generateIssueId(),
|
|
7635
|
+
"moderate",
|
|
7636
|
+
"Loose equality check against null/undefined",
|
|
7637
|
+
"Use strict equality (===) to avoid accidental truthiness",
|
|
7638
|
+
file,
|
|
7639
|
+
lineNumber,
|
|
7640
|
+
0.75,
|
|
7641
|
+
void 0,
|
|
7642
|
+
true,
|
|
7643
|
+
{ category: "equality", effort: "trivial" }
|
|
7644
|
+
));
|
|
7482
7645
|
}
|
|
7483
|
-
if (
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
{ category: "falsy", effort: "easy" }
|
|
7497
|
-
));
|
|
7498
|
-
}
|
|
7646
|
+
if (/==\s*["']['"]/.test(line) || /==\s*0[^.]/.test(line)) {
|
|
7647
|
+
issues.push(this.createIssue(
|
|
7648
|
+
this.generateIssueId(),
|
|
7649
|
+
"low",
|
|
7650
|
+
"Loose equality with empty string or zero - type coercion risk",
|
|
7651
|
+
"Use strict equality (===) to avoid unexpected behavior",
|
|
7652
|
+
file,
|
|
7653
|
+
lineNumber,
|
|
7654
|
+
0.65,
|
|
7655
|
+
void 0,
|
|
7656
|
+
true,
|
|
7657
|
+
{ category: "equality", effort: "trivial" }
|
|
7658
|
+
));
|
|
7499
7659
|
}
|
|
7500
7660
|
}
|
|
7501
7661
|
return issues;
|
|
7502
7662
|
}
|
|
7503
7663
|
};
|
|
7504
7664
|
|
|
7505
|
-
// src/
|
|
7665
|
+
// src/skills/built-in/moneybags.ts
|
|
7506
7666
|
var MONEYBAGS_ASCII = `
|
|
7507
7667
|
\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557
|
|
7508
7668
|
\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D
|
|
@@ -7526,66 +7686,66 @@ var MONEYBAGS_QUOTES = [
|
|
|
7526
7686
|
];
|
|
7527
7687
|
var BASE_COST_BY_SEVERITY = {
|
|
7528
7688
|
critical: 5e3,
|
|
7529
|
-
// Critical bugs found in dev -
|
|
7689
|
+
// Critical bugs found in dev - high impact, deep fixes
|
|
7530
7690
|
serious: 2e3,
|
|
7531
7691
|
// Serious issues need careful remediation
|
|
7532
7692
|
moderate: 500,
|
|
7533
7693
|
// Moderate issues are quicker fixes
|
|
7534
|
-
low:
|
|
7694
|
+
low: 150
|
|
7535
7695
|
// Low severity - mostly cleanup
|
|
7536
7696
|
};
|
|
7537
7697
|
var PRODUCTION_MULTIPLIER = {
|
|
7538
|
-
critical:
|
|
7698
|
+
critical: 12,
|
|
7539
7699
|
// Critical bugs can cause outages, breaches
|
|
7540
|
-
serious:
|
|
7700
|
+
serious: 8,
|
|
7541
7701
|
// Serious bugs cause significant user impact
|
|
7542
|
-
moderate:
|
|
7702
|
+
moderate: 4,
|
|
7543
7703
|
// Moderate bugs require hotfixes, user support
|
|
7544
|
-
low:
|
|
7704
|
+
low: 2
|
|
7545
7705
|
// Low bugs accumulate tech debt
|
|
7546
7706
|
};
|
|
7547
7707
|
var CATEGORY_MULTIPLIERS = {
|
|
7548
|
-
// Security categories -
|
|
7549
|
-
"security": { multiplier:
|
|
7550
|
-
"authentication": { multiplier:
|
|
7551
|
-
"authorization": { multiplier:
|
|
7552
|
-
"injection": { multiplier: 12, reason: "SQL/Command injection
|
|
7553
|
-
"xss": { multiplier:
|
|
7554
|
-
"secrets": { multiplier:
|
|
7555
|
-
"cryptography": { multiplier:
|
|
7708
|
+
// Security categories - important but realistic
|
|
7709
|
+
"security": { multiplier: 6, reason: "Security vulnerabilities require careful remediation" },
|
|
7710
|
+
"authentication": { multiplier: 8, reason: "Auth bypass is serious but fixable" },
|
|
7711
|
+
"authorization": { multiplier: 6, reason: "Authorization flaws expose sensitive data" },
|
|
7712
|
+
"injection": { multiplier: 12, reason: "SQL/Command injection is severe - prioritize fix" },
|
|
7713
|
+
"xss": { multiplier: 4, reason: "XSS needs fixing but React mitigates most cases" },
|
|
7714
|
+
"secrets": { multiplier: 8, reason: "Exposed secrets require rotation" },
|
|
7715
|
+
"cryptography": { multiplier: 4, reason: "Weak crypto should be updated" },
|
|
7556
7716
|
// Data integrity categories
|
|
7557
|
-
"data-loss": { multiplier:
|
|
7558
|
-
"data-corruption": { multiplier:
|
|
7559
|
-
"privacy": { multiplier:
|
|
7717
|
+
"data-loss": { multiplier: 10, reason: "Data loss is serious - ensure backups exist" },
|
|
7718
|
+
"data-corruption": { multiplier: 6, reason: "Data corruption requires recovery work" },
|
|
7719
|
+
"privacy": { multiplier: 8, reason: "Privacy violations carry compliance risk" },
|
|
7560
7720
|
// Financial categories
|
|
7561
|
-
"payment": { multiplier: 25, reason: "Payment bugs
|
|
7562
|
-
"billing": { multiplier:
|
|
7563
|
-
"financial-calculation": { multiplier:
|
|
7721
|
+
"payment": { multiplier: 25, reason: "Payment bugs need immediate attention" },
|
|
7722
|
+
"billing": { multiplier: 8, reason: "Billing errors erode trust" },
|
|
7723
|
+
"financial-calculation": { multiplier: 6, reason: "Incorrect calculations compound over time" },
|
|
7564
7724
|
// Reliability categories
|
|
7565
|
-
"crash": { multiplier:
|
|
7566
|
-
"memory-leak": { multiplier:
|
|
7567
|
-
"deadlock": { multiplier:
|
|
7568
|
-
"race-condition": { multiplier:
|
|
7725
|
+
"crash": { multiplier: 4, reason: "Crashes hurt user experience" },
|
|
7726
|
+
"memory-leak": { multiplier: 3, reason: "Memory leaks cause gradual degradation" },
|
|
7727
|
+
"deadlock": { multiplier: 4, reason: "Deadlocks require restarts" },
|
|
7728
|
+
"race-condition": { multiplier: 4, reason: "Race conditions are hard to debug" },
|
|
7569
7729
|
// User experience
|
|
7570
|
-
"accessibility": { multiplier:
|
|
7571
|
-
"performance": { multiplier: 2, reason: "Performance issues hurt conversion rates
|
|
7572
|
-
"ux": { multiplier: 1.
|
|
7730
|
+
"accessibility": { multiplier: 1.5, reason: "Accessibility issues should be addressed" },
|
|
7731
|
+
"performance": { multiplier: 2, reason: "Performance issues hurt conversion rates" },
|
|
7732
|
+
"ux": { multiplier: 1.2, reason: "UX bugs increase support costs" },
|
|
7573
7733
|
// Code quality
|
|
7574
|
-
"bug": { multiplier:
|
|
7575
|
-
"type-error": { multiplier:
|
|
7576
|
-
"logic-error": { multiplier:
|
|
7734
|
+
"bug": { multiplier: 1, reason: "General bugs require developer time" },
|
|
7735
|
+
"type-error": { multiplier: 0.8, reason: "Type errors caught early save debugging time" },
|
|
7736
|
+
"logic-error": { multiplier: 1.5, reason: "Logic errors produce incorrect outcomes" },
|
|
7577
7737
|
// Default
|
|
7578
|
-
"default": { multiplier:
|
|
7738
|
+
"default": { multiplier: 1, reason: "General code issues" }
|
|
7579
7739
|
};
|
|
7580
7740
|
var CONTEXT_MULTIPLIERS = {
|
|
7581
|
-
touchesPayments: { multiplier:
|
|
7582
|
-
touchesAuth: { multiplier:
|
|
7583
|
-
touchesUserData: { multiplier:
|
|
7584
|
-
touchesHealthData: { multiplier:
|
|
7585
|
-
touchesDatabase: { multiplier:
|
|
7586
|
-
touchesAPI: { multiplier:
|
|
7587
|
-
touchesCrypto: { multiplier:
|
|
7588
|
-
touchesFileSystem: { multiplier:
|
|
7741
|
+
touchesPayments: { multiplier: 1.3, description: "Payment processing code" },
|
|
7742
|
+
touchesAuth: { multiplier: 1.2, description: "Authentication/authorization code" },
|
|
7743
|
+
touchesUserData: { multiplier: 1.15, description: "User PII handling" },
|
|
7744
|
+
touchesHealthData: { multiplier: 1.3, description: "Health data (HIPAA liability)" },
|
|
7745
|
+
touchesDatabase: { multiplier: 1.1, description: "Database operations" },
|
|
7746
|
+
touchesAPI: { multiplier: 1.1, description: "External API integrations" },
|
|
7747
|
+
touchesCrypto: { multiplier: 1.15, description: "Cryptographic operations" },
|
|
7748
|
+
touchesFileSystem: { multiplier: 1.05, description: "File system operations" }
|
|
7589
7749
|
};
|
|
7590
7750
|
var EFFORT_HOURS = {
|
|
7591
7751
|
trivial: 0.5,
|
|
@@ -7596,16 +7756,17 @@ var EFFORT_HOURS = {
|
|
|
7596
7756
|
var DEVELOPER_HOURLY_RATE = 150;
|
|
7597
7757
|
var DEFAULT_USER_COUNT = 250;
|
|
7598
7758
|
var USER_COUNT_MULTIPLIERS = [
|
|
7599
|
-
{ threshold: 0, multiplier: 0.
|
|
7600
|
-
{ threshold: 50, multiplier: 0.
|
|
7759
|
+
{ threshold: 0, multiplier: 0.2, label: "Pre-launch (0 users)" },
|
|
7760
|
+
{ threshold: 50, multiplier: 0.5, label: "MVP (50 users)" },
|
|
7601
7761
|
{ threshold: 250, multiplier: 1, label: "Early stage (250 users)" },
|
|
7602
7762
|
// Baseline (default)
|
|
7603
|
-
{ threshold: 1e3, multiplier:
|
|
7604
|
-
{ threshold: 5e3, multiplier:
|
|
7605
|
-
{ threshold: 25e3, multiplier:
|
|
7763
|
+
{ threshold: 1e3, multiplier: 3, label: "Growing (1K users)" },
|
|
7764
|
+
{ threshold: 5e3, multiplier: 6, label: "Traction (5K users)" },
|
|
7765
|
+
{ threshold: 25e3, multiplier: 10, label: "Scale-up (25K users)" },
|
|
7606
7766
|
{ threshold: 1e5, multiplier: 15, label: "Growth (100K users)" },
|
|
7607
|
-
{ threshold: 5e5, multiplier:
|
|
7608
|
-
{ threshold: 1e6, multiplier:
|
|
7767
|
+
{ threshold: 5e5, multiplier: 18, label: "Large (500K users)" },
|
|
7768
|
+
{ threshold: 1e6, multiplier: 20, label: "Enterprise (1M+ users)" }
|
|
7769
|
+
// Capped to avoid runaway estimates
|
|
7609
7770
|
];
|
|
7610
7771
|
var PER_USER_COSTS = {
|
|
7611
7772
|
"data-loss": 5,
|
|
@@ -7621,10 +7782,11 @@ var PER_USER_COSTS = {
|
|
|
7621
7782
|
"accessibility": 0.1
|
|
7622
7783
|
// $0.10 per user (support costs)
|
|
7623
7784
|
};
|
|
7624
|
-
var
|
|
7785
|
+
var MoneybagSkill = class extends BaseSkill {
|
|
7625
7786
|
name = "moneybags";
|
|
7626
7787
|
description = "Estimates the dollar cost of bugs based on severity, category, and user scale. Uses industry research (IBM, NIST, Ponemon) with configurable user count scaling.";
|
|
7627
7788
|
version = "1.1.0";
|
|
7789
|
+
author = "Trie Agent";
|
|
7628
7790
|
bannerShown = false;
|
|
7629
7791
|
config = {
|
|
7630
7792
|
userCount: DEFAULT_USER_COUNT,
|
|
@@ -7751,7 +7913,7 @@ var MoneybagAgent = class extends BaseAgent {
|
|
|
7751
7913
|
contextFactors.push(data.description);
|
|
7752
7914
|
}
|
|
7753
7915
|
}
|
|
7754
|
-
contextMultiplier = Math.min(contextMultiplier,
|
|
7916
|
+
contextMultiplier = Math.min(contextMultiplier, 2);
|
|
7755
7917
|
const userScale = this.getUserScaleMultiplier();
|
|
7756
7918
|
const userScaleMultiplier = userScale.multiplier;
|
|
7757
7919
|
const userScaleLabel = userScale.label;
|
|
@@ -7773,14 +7935,15 @@ var MoneybagAgent = class extends BaseAgent {
|
|
|
7773
7935
|
const fixHours = EFFORT_HOURS[effort] || 8;
|
|
7774
7936
|
const developerRate = this.config.developerRate ?? DEVELOPER_HOURLY_RATE;
|
|
7775
7937
|
const fixCost = fixHours * developerRate;
|
|
7776
|
-
const
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
const
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7938
|
+
const BASE_NOW_CAP = 25e3;
|
|
7939
|
+
const BASE_PROD_CAP = 25e4;
|
|
7940
|
+
const maxNowCost = BASE_NOW_CAP * userScaleMultiplier;
|
|
7941
|
+
const maxProdCost = BASE_PROD_CAP * userScaleMultiplier;
|
|
7942
|
+
const rawNowCost = baseCost * categoryMultiplier * contextMultiplier + fixCost;
|
|
7943
|
+
const totalNowCost = Math.round(Math.min(rawNowCost, maxNowCost));
|
|
7944
|
+
const rawProductionCost = baseCost * productionMultiplier * categoryMultiplier * contextMultiplier + perUserCost + // Add per-user costs for production impact
|
|
7945
|
+
fixCost * 2;
|
|
7946
|
+
const totalProductionCost = Math.round(Math.min(rawProductionCost, maxProdCost));
|
|
7784
7947
|
const savings = totalProductionCost - totalNowCost;
|
|
7785
7948
|
const summary = this.generateCostSummary(
|
|
7786
7949
|
issue,
|
|
@@ -8100,7 +8263,7 @@ Default: 250 users. Scale with: trie scan --users 10000
|
|
|
8100
8263
|
}
|
|
8101
8264
|
};
|
|
8102
8265
|
|
|
8103
|
-
// src/
|
|
8266
|
+
// src/skills/built-in/production-ready.ts
|
|
8104
8267
|
var PRODUCTION_READY_ASCII = `
|
|
8105
8268
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
8106
8269
|
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
@@ -8123,66 +8286,76 @@ var PRODUCTION_QUOTES = [
|
|
|
8123
8286
|
"Production is where your code meets reality. Is yours ready?"
|
|
8124
8287
|
];
|
|
8125
8288
|
var PRODUCTION_PATTERNS = {
|
|
8126
|
-
// Health & Reliability
|
|
8289
|
+
// Health & Reliability - BACKEND ONLY
|
|
8127
8290
|
healthEndpoint: {
|
|
8128
8291
|
pattern: /\/health|healthcheck|health-check|readiness|liveness/i,
|
|
8129
8292
|
category: "reliability",
|
|
8130
8293
|
requirement: "Health check endpoint",
|
|
8131
|
-
severity: "
|
|
8294
|
+
severity: "moderate",
|
|
8295
|
+
// Downgraded - not critical for all apps
|
|
8296
|
+
backendOnly: true
|
|
8132
8297
|
},
|
|
8133
8298
|
gracefulShutdown: {
|
|
8134
8299
|
pattern: /SIGTERM|SIGINT|process\.on\s*\(\s*['"]SIG/i,
|
|
8135
8300
|
category: "reliability",
|
|
8136
8301
|
requirement: "Graceful shutdown handling",
|
|
8137
|
-
severity: "
|
|
8302
|
+
severity: "low",
|
|
8303
|
+
backendOnly: true
|
|
8138
8304
|
},
|
|
8139
|
-
// Scalability
|
|
8305
|
+
// Scalability - BACKEND ONLY
|
|
8140
8306
|
connectionPooling: {
|
|
8141
8307
|
pattern: /connectionLimit|pool|poolSize|max_connections|maxConnections/i,
|
|
8142
8308
|
category: "scalability",
|
|
8143
8309
|
requirement: "Database connection pooling",
|
|
8144
|
-
severity: "
|
|
8310
|
+
severity: "moderate",
|
|
8311
|
+
backendOnly: true
|
|
8145
8312
|
},
|
|
8146
8313
|
inMemorySession: {
|
|
8147
8314
|
pattern: /express-session.*(?!redis|memcached|mongo)|session\s*=\s*\{\}/i,
|
|
8148
8315
|
category: "scalability",
|
|
8149
8316
|
requirement: "External session store (not in-memory)",
|
|
8150
|
-
severity: "
|
|
8317
|
+
severity: "moderate",
|
|
8318
|
+
backendOnly: true
|
|
8151
8319
|
},
|
|
8152
|
-
// Error Handling
|
|
8320
|
+
// Error Handling - BACKEND ONLY
|
|
8153
8321
|
globalErrorHandler: {
|
|
8154
8322
|
pattern: /app\.use\s*\(\s*\(?err|errorHandler|process\.on\s*\(\s*['"]uncaughtException/i,
|
|
8155
8323
|
category: "reliability",
|
|
8156
8324
|
requirement: "Global error handler",
|
|
8157
|
-
severity: "
|
|
8325
|
+
severity: "moderate",
|
|
8326
|
+
backendOnly: true
|
|
8158
8327
|
},
|
|
8159
|
-
// Security Headers
|
|
8328
|
+
// Security Headers - BACKEND ONLY (frontend relies on hosting platform)
|
|
8160
8329
|
securityHeaders: {
|
|
8161
8330
|
pattern: /helmet|contentSecurityPolicy|X-Frame-Options|X-Content-Type|Strict-Transport/i,
|
|
8162
8331
|
category: "security",
|
|
8163
8332
|
requirement: "Security headers (CSP, HSTS, etc.)",
|
|
8164
|
-
severity: "
|
|
8333
|
+
severity: "moderate",
|
|
8334
|
+
backendOnly: true
|
|
8165
8335
|
},
|
|
8166
|
-
// Rate Limiting
|
|
8336
|
+
// Rate Limiting - BACKEND ONLY
|
|
8167
8337
|
rateLimiting: {
|
|
8168
8338
|
pattern: /rateLimit|rate-limit|throttle|express-rate-limit|slowDown/i,
|
|
8169
8339
|
category: "security",
|
|
8170
8340
|
requirement: "API rate limiting",
|
|
8171
|
-
severity: "
|
|
8341
|
+
severity: "low",
|
|
8342
|
+
backendOnly: true
|
|
8172
8343
|
},
|
|
8173
|
-
// Logging
|
|
8344
|
+
// Logging - applies to both but less critical for frontend
|
|
8174
8345
|
structuredLogging: {
|
|
8175
8346
|
pattern: /pino|winston|bunyan|structured.*log|log.*json/i,
|
|
8176
8347
|
category: "observability",
|
|
8177
8348
|
requirement: "Structured logging",
|
|
8178
|
-
severity: "
|
|
8349
|
+
severity: "low",
|
|
8350
|
+
backendOnly: false
|
|
8179
8351
|
},
|
|
8180
|
-
// Monitoring
|
|
8352
|
+
// Monitoring - applies to both
|
|
8181
8353
|
monitoring: {
|
|
8182
8354
|
pattern: /prometheus|datadog|newrelic|sentry|bugsnag|opentelemetry|@sentry/i,
|
|
8183
8355
|
category: "observability",
|
|
8184
8356
|
requirement: "Error/performance monitoring",
|
|
8185
|
-
severity: "
|
|
8357
|
+
severity: "low",
|
|
8358
|
+
backendOnly: false
|
|
8186
8359
|
}
|
|
8187
8360
|
};
|
|
8188
8361
|
var PRODUCTION_ANTIPATTERNS = [
|
|
@@ -8252,10 +8425,11 @@ var PRODUCTION_CONFIG_FILES = [
|
|
|
8252
8425
|
/vercel\.json$/,
|
|
8253
8426
|
/netlify\.toml$/
|
|
8254
8427
|
];
|
|
8255
|
-
var
|
|
8428
|
+
var ProductionReadySkill = class extends BaseSkill {
|
|
8256
8429
|
name = "production-ready";
|
|
8257
8430
|
description = "Production readiness checker: health endpoints, graceful shutdown, connection pooling, security headers, monitoring, and deployment gates";
|
|
8258
8431
|
version = "1.0.0";
|
|
8432
|
+
author = "Trie Agent";
|
|
8259
8433
|
bannerShown = false;
|
|
8260
8434
|
foundRequirements = /* @__PURE__ */ new Set();
|
|
8261
8435
|
get priority() {
|
|
@@ -8363,7 +8537,12 @@ var ProductionReadyAgent = class extends BaseAgent {
|
|
|
8363
8537
|
}
|
|
8364
8538
|
}
|
|
8365
8539
|
const combinedContent = allContent.join("\n");
|
|
8540
|
+
const isFrontendOnly = this.detectFrontendOnlyApp(combinedContent, files);
|
|
8366
8541
|
for (const [key, config] of Object.entries(PRODUCTION_PATTERNS)) {
|
|
8542
|
+
const patternConfig = config;
|
|
8543
|
+
if (isFrontendOnly && patternConfig.backendOnly) {
|
|
8544
|
+
continue;
|
|
8545
|
+
}
|
|
8367
8546
|
if (!this.foundRequirements.has(key)) {
|
|
8368
8547
|
if (!config.pattern.test(combinedContent)) {
|
|
8369
8548
|
this.progress?.found(config.severity, `Missing: ${config.requirement}`);
|
|
@@ -8374,7 +8553,8 @@ var ProductionReadyAgent = class extends BaseAgent {
|
|
|
8374
8553
|
this.getRequirementFix(key),
|
|
8375
8554
|
"project",
|
|
8376
8555
|
void 0,
|
|
8377
|
-
0.
|
|
8556
|
+
0.6,
|
|
8557
|
+
// Lower confidence for missing checks
|
|
8378
8558
|
void 0,
|
|
8379
8559
|
false,
|
|
8380
8560
|
{ category: config.category }
|
|
@@ -8385,6 +8565,35 @@ var ProductionReadyAgent = class extends BaseAgent {
|
|
|
8385
8565
|
this.logReadinessSummary(issues);
|
|
8386
8566
|
return issues;
|
|
8387
8567
|
}
|
|
8568
|
+
/**
|
|
8569
|
+
* Detect if this is a frontend-only app (React, Vue, Svelte, etc.) without a backend
|
|
8570
|
+
* Frontend apps don't need health endpoints, database pooling, session stores, etc.
|
|
8571
|
+
*/
|
|
8572
|
+
detectFrontendOnlyApp(content, files) {
|
|
8573
|
+
const frontendContentPatterns = [
|
|
8574
|
+
/vite\.config|next\.config|nuxt\.config|svelte\.config/i,
|
|
8575
|
+
/from ['"]react['"]|from ['"]vue['"]|from ['"]svelte['"]/i,
|
|
8576
|
+
/createRoot|ReactDOM|createApp/i
|
|
8577
|
+
];
|
|
8578
|
+
const frontendFilePatterns = [
|
|
8579
|
+
/\.tsx$/,
|
|
8580
|
+
/\.jsx$/,
|
|
8581
|
+
/\.vue$/,
|
|
8582
|
+
/\.svelte$/
|
|
8583
|
+
];
|
|
8584
|
+
const backendIndicators = [
|
|
8585
|
+
/express|fastify|koa|hapi|nest|@nestjs/i,
|
|
8586
|
+
/createServer|http\.Server|https\.Server/i,
|
|
8587
|
+
/app\.listen\s*\(|server\.listen\s*\(/i,
|
|
8588
|
+
/middleware|router\.get|router\.post|app\.get\s*\(/i,
|
|
8589
|
+
/prisma|sequelize|mongoose|knex|typeorm/i
|
|
8590
|
+
];
|
|
8591
|
+
const hasFrontendContent = frontendContentPatterns.some((p) => p.test(content));
|
|
8592
|
+
const hasFrontendFiles = files.some((f) => frontendFilePatterns.some((p) => p.test(f)));
|
|
8593
|
+
const hasFrontendSignals = hasFrontendContent || hasFrontendFiles;
|
|
8594
|
+
const hasBackendSignals = backendIndicators.some((p) => p.test(content));
|
|
8595
|
+
return hasFrontendSignals && !hasBackendSignals;
|
|
8596
|
+
}
|
|
8388
8597
|
getRequirementFix(key) {
|
|
8389
8598
|
const fixes = {
|
|
8390
8599
|
healthEndpoint: 'Add health check endpoint: app.get("/health", (req, res) => res.json({ status: "ok" }))',
|
|
@@ -8480,10 +8689,1376 @@ Output STRICT JSON:
|
|
|
8480
8689
|
}
|
|
8481
8690
|
};
|
|
8482
8691
|
|
|
8483
|
-
// src/skills/
|
|
8484
|
-
import {
|
|
8485
|
-
import {
|
|
8486
|
-
import {
|
|
8692
|
+
// src/skills/installer.ts
|
|
8693
|
+
import { mkdir as mkdir2, rm, writeFile as writeFile2, readdir, readFile as readFile3, access, cp } from "fs/promises";
|
|
8694
|
+
import { join as join3 } from "path";
|
|
8695
|
+
import { exec } from "child_process";
|
|
8696
|
+
import { promisify } from "util";
|
|
8697
|
+
|
|
8698
|
+
// src/skills/parser.ts
|
|
8699
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
8700
|
+
import { join as join2 } from "path";
|
|
8701
|
+
async function parseSkillMd(skillPath) {
|
|
8702
|
+
const skillMdPath = join2(skillPath, "SKILL.md");
|
|
8703
|
+
const rawContent = await readFile2(skillMdPath, "utf-8");
|
|
8704
|
+
const frontmatterMatch = rawContent.match(/^---\n([\s\S]*?)\n---/);
|
|
8705
|
+
if (!frontmatterMatch || !frontmatterMatch[1]) {
|
|
8706
|
+
throw new Error("Invalid SKILL.md: missing YAML frontmatter");
|
|
8707
|
+
}
|
|
8708
|
+
const frontmatter = parseYamlFrontmatter(frontmatterMatch[1]);
|
|
8709
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
8710
|
+
throw new Error("Invalid SKILL.md: missing required name or description in frontmatter");
|
|
8711
|
+
}
|
|
8712
|
+
const content = rawContent.slice(frontmatterMatch[0].length).trim();
|
|
8713
|
+
return {
|
|
8714
|
+
frontmatter,
|
|
8715
|
+
content,
|
|
8716
|
+
rawContent
|
|
8717
|
+
};
|
|
8718
|
+
}
|
|
8719
|
+
function parseYamlFrontmatter(yaml) {
|
|
8720
|
+
const result = {};
|
|
8721
|
+
const lines = yaml.split("\n");
|
|
8722
|
+
for (const line of lines) {
|
|
8723
|
+
const trimmed = line.trim();
|
|
8724
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
8725
|
+
const colonIndex = trimmed.indexOf(":");
|
|
8726
|
+
if (colonIndex === -1) continue;
|
|
8727
|
+
const key = trimmed.slice(0, colonIndex).trim();
|
|
8728
|
+
let value = trimmed.slice(colonIndex + 1).trim();
|
|
8729
|
+
if (value === "") continue;
|
|
8730
|
+
if (typeof value === "string") {
|
|
8731
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
8732
|
+
value = value.slice(1, -1);
|
|
8733
|
+
}
|
|
8734
|
+
if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) {
|
|
8735
|
+
value = value.slice(1, -1).split(",").map((s) => s.trim().replace(/^["']|["']$/g, ""));
|
|
8736
|
+
}
|
|
8737
|
+
}
|
|
8738
|
+
result[key] = value;
|
|
8739
|
+
}
|
|
8740
|
+
return result;
|
|
8741
|
+
}
|
|
8742
|
+
|
|
8743
|
+
// src/skills/installer.ts
|
|
8744
|
+
var execAsync = promisify(exec);
|
|
8745
|
+
async function installSkill(source, skillName) {
|
|
8746
|
+
const parts = source.split("/");
|
|
8747
|
+
if (parts.length < 2) {
|
|
8748
|
+
return { success: false, name: "unknown", error: "Invalid source format. Use owner/repo or owner/repo/skill-name" };
|
|
8749
|
+
}
|
|
8750
|
+
const owner = parts[0];
|
|
8751
|
+
const repo = parts[1];
|
|
8752
|
+
const specifiedSkill = skillName || parts[2];
|
|
8753
|
+
const skillsDir = join3(getWorkingDirectory(void 0, true), ".trie", "skills");
|
|
8754
|
+
const tempDir = join3(skillsDir, `.temp-${Date.now()}`);
|
|
8755
|
+
try {
|
|
8756
|
+
await mkdir2(skillsDir, { recursive: true });
|
|
8757
|
+
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
8758
|
+
await execAsync(`git clone --depth 1 "${repoUrl}" "${tempDir}"`, { timeout: 6e4 });
|
|
8759
|
+
const sourcePath = await findSkillPath(tempDir, specifiedSkill);
|
|
8760
|
+
if (!sourcePath) {
|
|
8761
|
+
throw new Error(`SKILL.md not found in repository. Searched in: root, skills/, ${specifiedSkill || "subdirectories"}`);
|
|
8762
|
+
}
|
|
8763
|
+
const parsed = await parseSkillMd(sourcePath);
|
|
8764
|
+
const name = parsed.frontmatter.name;
|
|
8765
|
+
const targetPath = join3(skillsDir, name);
|
|
8766
|
+
await rm(targetPath, { recursive: true, force: true });
|
|
8767
|
+
await cp(sourcePath, targetPath, { recursive: true });
|
|
8768
|
+
await writeFile2(join3(targetPath, ".installed.json"), JSON.stringify({
|
|
8769
|
+
installedFrom: source,
|
|
8770
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8771
|
+
repository: `${owner}/${repo}`
|
|
8772
|
+
}, null, 2));
|
|
8773
|
+
return { success: true, name, path: targetPath };
|
|
8774
|
+
} catch (error) {
|
|
8775
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8776
|
+
return { success: false, name: skillName || "unknown", error: message };
|
|
8777
|
+
} finally {
|
|
8778
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
8779
|
+
});
|
|
8780
|
+
}
|
|
8781
|
+
}
|
|
8782
|
+
async function findSkillPath(repoPath, skillName) {
|
|
8783
|
+
const searchPaths = [];
|
|
8784
|
+
if (skillName) {
|
|
8785
|
+
searchPaths.push(
|
|
8786
|
+
join3(repoPath, "skills", skillName),
|
|
8787
|
+
join3(repoPath, skillName)
|
|
8788
|
+
);
|
|
8789
|
+
}
|
|
8790
|
+
searchPaths.push(
|
|
8791
|
+
repoPath,
|
|
8792
|
+
join3(repoPath, "skill")
|
|
8793
|
+
);
|
|
8794
|
+
if (!skillName) {
|
|
8795
|
+
try {
|
|
8796
|
+
const skillsSubdir = join3(repoPath, "skills");
|
|
8797
|
+
await access(skillsSubdir);
|
|
8798
|
+
const entries = await readdir(skillsSubdir, { withFileTypes: true });
|
|
8799
|
+
for (const entry of entries) {
|
|
8800
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
8801
|
+
searchPaths.push(join3(skillsSubdir, entry.name));
|
|
8802
|
+
}
|
|
8803
|
+
}
|
|
8804
|
+
} catch {
|
|
8805
|
+
}
|
|
8806
|
+
}
|
|
8807
|
+
for (const searchPath of searchPaths) {
|
|
8808
|
+
try {
|
|
8809
|
+
await parseSkillMd(searchPath);
|
|
8810
|
+
return searchPath;
|
|
8811
|
+
} catch {
|
|
8812
|
+
}
|
|
8813
|
+
}
|
|
8814
|
+
return null;
|
|
8815
|
+
}
|
|
8816
|
+
async function listInstalledSkills() {
|
|
8817
|
+
const skillsDir = join3(getWorkingDirectory(void 0, true), ".trie", "skills");
|
|
8818
|
+
const skills = [];
|
|
8819
|
+
try {
|
|
8820
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
8821
|
+
for (const entry of entries) {
|
|
8822
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
8823
|
+
const skillPath = join3(skillsDir, entry.name);
|
|
8824
|
+
try {
|
|
8825
|
+
const parsed = await parseSkillMd(skillPath);
|
|
8826
|
+
const metaPath = join3(skillPath, ".installed.json");
|
|
8827
|
+
let meta = { installedFrom: "unknown", installedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
8828
|
+
try {
|
|
8829
|
+
meta = JSON.parse(await readFile3(metaPath, "utf-8"));
|
|
8830
|
+
} catch {
|
|
8831
|
+
}
|
|
8832
|
+
skills.push({
|
|
8833
|
+
name: parsed.frontmatter.name,
|
|
8834
|
+
description: parsed.frontmatter.description,
|
|
8835
|
+
path: skillPath,
|
|
8836
|
+
installedFrom: meta.installedFrom,
|
|
8837
|
+
installedAt: meta.installedAt
|
|
8838
|
+
});
|
|
8839
|
+
} catch {
|
|
8840
|
+
}
|
|
8841
|
+
}
|
|
8842
|
+
} catch {
|
|
8843
|
+
}
|
|
8844
|
+
return skills;
|
|
8845
|
+
}
|
|
8846
|
+
async function removeSkill(skillName) {
|
|
8847
|
+
const skillsDir = join3(getWorkingDirectory(void 0, true), ".trie", "skills");
|
|
8848
|
+
const skillPath = join3(skillsDir, skillName);
|
|
8849
|
+
try {
|
|
8850
|
+
await rm(skillPath, { recursive: true });
|
|
8851
|
+
return true;
|
|
8852
|
+
} catch {
|
|
8853
|
+
return false;
|
|
8854
|
+
}
|
|
8855
|
+
}
|
|
8856
|
+
|
|
8857
|
+
// src/utils/context-state.ts
|
|
8858
|
+
import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
|
|
8859
|
+
import { existsSync as existsSync6 } from "fs";
|
|
8860
|
+
import { join as join7, basename as basename2 } from "path";
|
|
8861
|
+
|
|
8862
|
+
// src/memory/issue-store.ts
|
|
8863
|
+
import { mkdir as mkdir4, writeFile as writeFile4, readFile as readFile5, readdir as readdir2 } from "fs/promises";
|
|
8864
|
+
import { existsSync as existsSync4 } from "fs";
|
|
8865
|
+
import { join as join5 } from "path";
|
|
8866
|
+
|
|
8867
|
+
// src/memory/bm25.ts
|
|
8868
|
+
var BM25Index = class _BM25Index {
|
|
8869
|
+
documents = /* @__PURE__ */ new Map();
|
|
8870
|
+
termFrequencies = /* @__PURE__ */ new Map();
|
|
8871
|
+
documentFrequencies = /* @__PURE__ */ new Map();
|
|
8872
|
+
documentLengths = /* @__PURE__ */ new Map();
|
|
8873
|
+
avgDocLength = 0;
|
|
8874
|
+
k1 = 1.5;
|
|
8875
|
+
b = 0.75;
|
|
8876
|
+
/**
|
|
8877
|
+
* Add a document to the index
|
|
8878
|
+
*/
|
|
8879
|
+
addDocument(doc) {
|
|
8880
|
+
const tokens = this.tokenize(doc.text);
|
|
8881
|
+
this.documents.set(doc.id, doc);
|
|
8882
|
+
this.documentLengths.set(doc.id, tokens.length);
|
|
8883
|
+
const termFreq = /* @__PURE__ */ new Map();
|
|
8884
|
+
const seenTerms = /* @__PURE__ */ new Set();
|
|
8885
|
+
for (const token of tokens) {
|
|
8886
|
+
termFreq.set(token, (termFreq.get(token) || 0) + 1);
|
|
8887
|
+
if (!seenTerms.has(token)) {
|
|
8888
|
+
seenTerms.add(token);
|
|
8889
|
+
this.documentFrequencies.set(token, (this.documentFrequencies.get(token) || 0) + 1);
|
|
8890
|
+
}
|
|
8891
|
+
}
|
|
8892
|
+
this.termFrequencies.set(doc.id, termFreq);
|
|
8893
|
+
this.updateAvgDocLength();
|
|
8894
|
+
}
|
|
8895
|
+
/**
|
|
8896
|
+
* Add multiple documents
|
|
8897
|
+
*/
|
|
8898
|
+
addDocuments(docs) {
|
|
8899
|
+
for (const doc of docs) {
|
|
8900
|
+
this.addDocument(doc);
|
|
8901
|
+
}
|
|
8902
|
+
}
|
|
8903
|
+
/**
|
|
8904
|
+
* Search the index
|
|
8905
|
+
*/
|
|
8906
|
+
search(query, limit = 10) {
|
|
8907
|
+
const queryTokens = this.tokenize(query);
|
|
8908
|
+
const scores = /* @__PURE__ */ new Map();
|
|
8909
|
+
const N = this.documents.size;
|
|
8910
|
+
for (const [docId] of this.documents) {
|
|
8911
|
+
let score = 0;
|
|
8912
|
+
const docLength = this.documentLengths.get(docId) || 0;
|
|
8913
|
+
const termFreqs = this.termFrequencies.get(docId);
|
|
8914
|
+
if (!termFreqs) continue;
|
|
8915
|
+
for (const term of queryTokens) {
|
|
8916
|
+
const tf = termFreqs.get(term) || 0;
|
|
8917
|
+
if (tf === 0) continue;
|
|
8918
|
+
const df = this.documentFrequencies.get(term) || 0;
|
|
8919
|
+
const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);
|
|
8920
|
+
const numerator = tf * (this.k1 + 1);
|
|
8921
|
+
const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / this.avgDocLength));
|
|
8922
|
+
score += idf * (numerator / denominator);
|
|
8923
|
+
}
|
|
8924
|
+
if (score > 0) {
|
|
8925
|
+
scores.set(docId, score);
|
|
8926
|
+
}
|
|
8927
|
+
}
|
|
8928
|
+
return Array.from(scores.entries()).sort((a, b) => b[1] - a[1]).slice(0, limit).map(([id, score]) => {
|
|
8929
|
+
const metadata = this.documents.get(id)?.metadata;
|
|
8930
|
+
const result = { id, score };
|
|
8931
|
+
if (metadata !== void 0) {
|
|
8932
|
+
result.metadata = metadata;
|
|
8933
|
+
}
|
|
8934
|
+
return result;
|
|
8935
|
+
});
|
|
8936
|
+
}
|
|
8937
|
+
/**
|
|
8938
|
+
* Get document count
|
|
8939
|
+
*/
|
|
8940
|
+
get size() {
|
|
8941
|
+
return this.documents.size;
|
|
8942
|
+
}
|
|
8943
|
+
/**
|
|
8944
|
+
* Clear the index
|
|
8945
|
+
*/
|
|
8946
|
+
clear() {
|
|
8947
|
+
this.documents.clear();
|
|
8948
|
+
this.termFrequencies.clear();
|
|
8949
|
+
this.documentFrequencies.clear();
|
|
8950
|
+
this.documentLengths.clear();
|
|
8951
|
+
this.avgDocLength = 0;
|
|
8952
|
+
}
|
|
8953
|
+
/**
|
|
8954
|
+
* Serialize the index to JSON
|
|
8955
|
+
*/
|
|
8956
|
+
serialize() {
|
|
8957
|
+
return JSON.stringify({
|
|
8958
|
+
documents: Array.from(this.documents.entries()),
|
|
8959
|
+
termFrequencies: Array.from(this.termFrequencies.entries()).map(([k, v]) => [k, Array.from(v.entries())]),
|
|
8960
|
+
documentFrequencies: Array.from(this.documentFrequencies.entries()),
|
|
8961
|
+
documentLengths: Array.from(this.documentLengths.entries()),
|
|
8962
|
+
avgDocLength: this.avgDocLength
|
|
8963
|
+
});
|
|
8964
|
+
}
|
|
8965
|
+
/**
|
|
8966
|
+
* Load from serialized JSON
|
|
8967
|
+
*/
|
|
8968
|
+
static deserialize(json) {
|
|
8969
|
+
const data = JSON.parse(json);
|
|
8970
|
+
const index = new _BM25Index();
|
|
8971
|
+
index.documents = new Map(data.documents);
|
|
8972
|
+
index.termFrequencies = new Map(data.termFrequencies.map(([k, v]) => [k, new Map(v)]));
|
|
8973
|
+
index.documentFrequencies = new Map(data.documentFrequencies);
|
|
8974
|
+
index.documentLengths = new Map(data.documentLengths);
|
|
8975
|
+
index.avgDocLength = data.avgDocLength;
|
|
8976
|
+
return index;
|
|
8977
|
+
}
|
|
8978
|
+
tokenize(text) {
|
|
8979
|
+
return text.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter((token) => token.length > 2 && !this.isStopWord(token));
|
|
8980
|
+
}
|
|
8981
|
+
isStopWord(word) {
|
|
8982
|
+
const stopWords = /* @__PURE__ */ new Set([
|
|
8983
|
+
"the",
|
|
8984
|
+
"be",
|
|
8985
|
+
"to",
|
|
8986
|
+
"of",
|
|
8987
|
+
"and",
|
|
8988
|
+
"a",
|
|
8989
|
+
"in",
|
|
8990
|
+
"that",
|
|
8991
|
+
"have",
|
|
8992
|
+
"i",
|
|
8993
|
+
"it",
|
|
8994
|
+
"for",
|
|
8995
|
+
"not",
|
|
8996
|
+
"on",
|
|
8997
|
+
"with",
|
|
8998
|
+
"he",
|
|
8999
|
+
"as",
|
|
9000
|
+
"you",
|
|
9001
|
+
"do",
|
|
9002
|
+
"at",
|
|
9003
|
+
"this",
|
|
9004
|
+
"but",
|
|
9005
|
+
"his",
|
|
9006
|
+
"by",
|
|
9007
|
+
"from",
|
|
9008
|
+
"they",
|
|
9009
|
+
"we",
|
|
9010
|
+
"say",
|
|
9011
|
+
"her",
|
|
9012
|
+
"she",
|
|
9013
|
+
"or",
|
|
9014
|
+
"an",
|
|
9015
|
+
"will",
|
|
9016
|
+
"my",
|
|
9017
|
+
"one",
|
|
9018
|
+
"all",
|
|
9019
|
+
"would",
|
|
9020
|
+
"there",
|
|
9021
|
+
"their",
|
|
9022
|
+
"what",
|
|
9023
|
+
"so",
|
|
9024
|
+
"up",
|
|
9025
|
+
"out",
|
|
9026
|
+
"if",
|
|
9027
|
+
"about",
|
|
9028
|
+
"who",
|
|
9029
|
+
"get",
|
|
9030
|
+
"which",
|
|
9031
|
+
"go",
|
|
9032
|
+
"me",
|
|
9033
|
+
"when",
|
|
9034
|
+
"make",
|
|
9035
|
+
"can",
|
|
9036
|
+
"like",
|
|
9037
|
+
"time",
|
|
9038
|
+
"no",
|
|
9039
|
+
"just",
|
|
9040
|
+
"him",
|
|
9041
|
+
"know",
|
|
9042
|
+
"take",
|
|
9043
|
+
"into",
|
|
9044
|
+
"year",
|
|
9045
|
+
"your",
|
|
9046
|
+
"some",
|
|
9047
|
+
"could",
|
|
9048
|
+
"them",
|
|
9049
|
+
"see",
|
|
9050
|
+
"other",
|
|
9051
|
+
"than",
|
|
9052
|
+
"then",
|
|
9053
|
+
"now",
|
|
9054
|
+
"look",
|
|
9055
|
+
"only",
|
|
9056
|
+
"come",
|
|
9057
|
+
"its",
|
|
9058
|
+
"over",
|
|
9059
|
+
"also",
|
|
9060
|
+
"back",
|
|
9061
|
+
"after",
|
|
9062
|
+
"use",
|
|
9063
|
+
"two",
|
|
9064
|
+
"how",
|
|
9065
|
+
"our",
|
|
9066
|
+
"first",
|
|
9067
|
+
"way",
|
|
9068
|
+
"even",
|
|
9069
|
+
"new",
|
|
9070
|
+
"want",
|
|
9071
|
+
"because",
|
|
9072
|
+
"any",
|
|
9073
|
+
"these",
|
|
9074
|
+
"give",
|
|
9075
|
+
"day",
|
|
9076
|
+
"most",
|
|
9077
|
+
"us",
|
|
9078
|
+
"should",
|
|
9079
|
+
"been",
|
|
9080
|
+
"has",
|
|
9081
|
+
"was",
|
|
9082
|
+
"are"
|
|
9083
|
+
]);
|
|
9084
|
+
return stopWords.has(word);
|
|
9085
|
+
}
|
|
9086
|
+
updateAvgDocLength() {
|
|
9087
|
+
if (this.documentLengths.size === 0) {
|
|
9088
|
+
this.avgDocLength = 0;
|
|
9089
|
+
return;
|
|
9090
|
+
}
|
|
9091
|
+
const total = Array.from(this.documentLengths.values()).reduce((a, b) => a + b, 0);
|
|
9092
|
+
this.avgDocLength = total / this.documentLengths.size;
|
|
9093
|
+
}
|
|
9094
|
+
};
|
|
9095
|
+
|
|
9096
|
+
// src/memory/compactor.ts
|
|
9097
|
+
import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile4 } from "fs/promises";
|
|
9098
|
+
import { existsSync as existsSync3 } from "fs";
|
|
9099
|
+
import { join as join4 } from "path";
|
|
9100
|
+
async function compactOldIssues(issues, options = {}) {
|
|
9101
|
+
const keepDays = options.keepDays ?? 30;
|
|
9102
|
+
const minIssues = options.minIssuesToCompact ?? 100;
|
|
9103
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
9104
|
+
cutoffDate.setDate(cutoffDate.getDate() - keepDays);
|
|
9105
|
+
const oldIssues = issues.filter((i) => new Date(i.timestamp) < cutoffDate);
|
|
9106
|
+
const recentIssues = issues.filter((i) => new Date(i.timestamp) >= cutoffDate);
|
|
9107
|
+
if (oldIssues.length < minIssues) {
|
|
9108
|
+
return { summary: null, remaining: issues };
|
|
9109
|
+
}
|
|
9110
|
+
const summary = buildSummary(oldIssues);
|
|
9111
|
+
return { summary, remaining: recentIssues };
|
|
9112
|
+
}
|
|
9113
|
+
function buildSummary(issues) {
|
|
9114
|
+
const sorted = issues.sort(
|
|
9115
|
+
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
9116
|
+
);
|
|
9117
|
+
const bySeverity = {};
|
|
9118
|
+
const byAgent = {};
|
|
9119
|
+
const patternMap = /* @__PURE__ */ new Map();
|
|
9120
|
+
const fileCount = /* @__PURE__ */ new Map();
|
|
9121
|
+
for (const issue of issues) {
|
|
9122
|
+
bySeverity[issue.severity] = (bySeverity[issue.severity] || 0) + 1;
|
|
9123
|
+
byAgent[issue.agent] = (byAgent[issue.agent] || 0) + 1;
|
|
9124
|
+
const patternKey = normalizePattern(issue.issue);
|
|
9125
|
+
const existing = patternMap.get(patternKey);
|
|
9126
|
+
if (existing) {
|
|
9127
|
+
existing.count++;
|
|
9128
|
+
} else {
|
|
9129
|
+
patternMap.set(patternKey, { count: 1, issue });
|
|
9130
|
+
}
|
|
9131
|
+
const fileName = issue.file.split("/").pop() || issue.file;
|
|
9132
|
+
fileCount.set(fileName, (fileCount.get(fileName) || 0) + 1);
|
|
9133
|
+
}
|
|
9134
|
+
const topPatterns = Array.from(patternMap.entries()).sort((a, b) => b[1].count - a[1].count).slice(0, 10).map(([pattern, data]) => ({
|
|
9135
|
+
pattern: pattern.slice(0, 100),
|
|
9136
|
+
count: data.count,
|
|
9137
|
+
severity: data.issue.severity,
|
|
9138
|
+
agent: data.issue.agent,
|
|
9139
|
+
exampleFix: data.issue.fix.slice(0, 200)
|
|
9140
|
+
}));
|
|
9141
|
+
const hotFiles = Array.from(fileCount.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([file, count]) => ({ file, count }));
|
|
9142
|
+
return {
|
|
9143
|
+
period: `${sorted[0]?.timestamp.split("T")[0]} to ${sorted[sorted.length - 1]?.timestamp.split("T")[0]}`,
|
|
9144
|
+
startDate: sorted[0]?.timestamp || "",
|
|
9145
|
+
endDate: sorted[sorted.length - 1]?.timestamp || "",
|
|
9146
|
+
totalIssues: issues.length,
|
|
9147
|
+
resolvedCount: issues.filter((i) => i.resolved).length,
|
|
9148
|
+
bySeverity,
|
|
9149
|
+
byAgent,
|
|
9150
|
+
topPatterns,
|
|
9151
|
+
hotFiles,
|
|
9152
|
+
compactedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9153
|
+
};
|
|
9154
|
+
}
|
|
9155
|
+
function normalizePattern(text) {
|
|
9156
|
+
return text.toLowerCase().replace(/`[^`]+`/g, "CODE").replace(/\b\d+\b/g, "N").replace(/["']/g, "").replace(/\s+/g, " ").trim().slice(0, 150);
|
|
9157
|
+
}
|
|
9158
|
+
async function saveCompactedSummary(summary, projectDir) {
|
|
9159
|
+
const memoryDir = join4(projectDir, ".trie", "memory");
|
|
9160
|
+
await mkdir3(memoryDir, { recursive: true });
|
|
9161
|
+
const summaryPath = join4(memoryDir, "compacted-summaries.json");
|
|
9162
|
+
let summaries = [];
|
|
9163
|
+
try {
|
|
9164
|
+
if (existsSync3(summaryPath)) {
|
|
9165
|
+
summaries = JSON.parse(await readFile4(summaryPath, "utf-8"));
|
|
9166
|
+
}
|
|
9167
|
+
} catch {
|
|
9168
|
+
summaries = [];
|
|
9169
|
+
}
|
|
9170
|
+
summaries.push(summary);
|
|
9171
|
+
if (summaries.length > 12) {
|
|
9172
|
+
summaries = summaries.slice(-12);
|
|
9173
|
+
}
|
|
9174
|
+
await writeFile3(summaryPath, JSON.stringify(summaries, null, 2));
|
|
9175
|
+
}
|
|
9176
|
+
async function loadCompactedSummaries(projectDir) {
|
|
9177
|
+
const summaryPath = join4(projectDir, ".trie", "memory", "compacted-summaries.json");
|
|
9178
|
+
try {
|
|
9179
|
+
if (existsSync3(summaryPath)) {
|
|
9180
|
+
return JSON.parse(await readFile4(summaryPath, "utf-8"));
|
|
9181
|
+
}
|
|
9182
|
+
} catch {
|
|
9183
|
+
}
|
|
9184
|
+
return [];
|
|
9185
|
+
}
|
|
9186
|
+
async function getHistoricalInsights(projectDir) {
|
|
9187
|
+
const summaries = await loadCompactedSummaries(projectDir);
|
|
9188
|
+
if (summaries.length === 0) {
|
|
9189
|
+
return {
|
|
9190
|
+
totalHistoricalIssues: 0,
|
|
9191
|
+
recurringPatterns: [],
|
|
9192
|
+
improvementTrend: "unknown"
|
|
9193
|
+
};
|
|
9194
|
+
}
|
|
9195
|
+
const totalHistoricalIssues = summaries.reduce((sum, s) => sum + s.totalIssues, 0);
|
|
9196
|
+
const patternCounts = /* @__PURE__ */ new Map();
|
|
9197
|
+
for (const summary of summaries) {
|
|
9198
|
+
for (const pattern of summary.topPatterns) {
|
|
9199
|
+
const key = pattern.pattern;
|
|
9200
|
+
const existing = patternCounts.get(key);
|
|
9201
|
+
if (existing) {
|
|
9202
|
+
existing.count += pattern.count;
|
|
9203
|
+
existing.appearances++;
|
|
9204
|
+
} else {
|
|
9205
|
+
patternCounts.set(key, { ...pattern, appearances: 1 });
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
}
|
|
9209
|
+
const recurringPatterns = Array.from(patternCounts.values()).filter((p) => p.appearances >= 2).sort((a, b) => b.count - a.count).slice(0, 5);
|
|
9210
|
+
let improvementTrend = "unknown";
|
|
9211
|
+
if (summaries.length >= 2) {
|
|
9212
|
+
const recent = summaries.slice(-2);
|
|
9213
|
+
const olderCount = recent[0]?.totalIssues || 0;
|
|
9214
|
+
const newerCount = recent[1]?.totalIssues || 0;
|
|
9215
|
+
if (newerCount < olderCount * 0.8) {
|
|
9216
|
+
improvementTrend = "improving";
|
|
9217
|
+
} else if (newerCount > olderCount * 1.2) {
|
|
9218
|
+
improvementTrend = "declining";
|
|
9219
|
+
} else {
|
|
9220
|
+
improvementTrend = "stable";
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
9223
|
+
return {
|
|
9224
|
+
totalHistoricalIssues,
|
|
9225
|
+
recurringPatterns,
|
|
9226
|
+
improvementTrend
|
|
9227
|
+
};
|
|
9228
|
+
}
|
|
9229
|
+
|
|
9230
|
+
// src/memory/issue-store.ts
|
|
9231
|
+
async function storeIssues(issues, project, workDir) {
|
|
9232
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
9233
|
+
const memoryDir = join5(projectDir, ".trie", "memory");
|
|
9234
|
+
await mkdir4(memoryDir, { recursive: true });
|
|
9235
|
+
const stored = [];
|
|
9236
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9237
|
+
for (const issue of issues) {
|
|
9238
|
+
const hash = hashIssue(issue);
|
|
9239
|
+
const storedIssue = {
|
|
9240
|
+
id: issue.id,
|
|
9241
|
+
hash,
|
|
9242
|
+
severity: issue.severity,
|
|
9243
|
+
issue: issue.issue,
|
|
9244
|
+
fix: issue.fix,
|
|
9245
|
+
file: issue.file,
|
|
9246
|
+
agent: issue.agent,
|
|
9247
|
+
timestamp: now,
|
|
9248
|
+
project,
|
|
9249
|
+
resolved: false
|
|
9250
|
+
};
|
|
9251
|
+
if (issue.line !== void 0) {
|
|
9252
|
+
storedIssue.line = issue.line;
|
|
9253
|
+
}
|
|
9254
|
+
if (issue.category !== void 0) {
|
|
9255
|
+
storedIssue.category = issue.category;
|
|
9256
|
+
}
|
|
9257
|
+
stored.push(storedIssue);
|
|
9258
|
+
}
|
|
9259
|
+
await appendToDailyLog(stored, projectDir);
|
|
9260
|
+
await updateIssueIndex(stored, projectDir);
|
|
9261
|
+
return stored.length;
|
|
9262
|
+
}
|
|
9263
|
+
async function searchIssues(query, options = {}) {
|
|
9264
|
+
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
9265
|
+
const limit = options.limit || 10;
|
|
9266
|
+
const allIssues = await loadIssueIndex(projectDir);
|
|
9267
|
+
if (allIssues.length === 0) {
|
|
9268
|
+
return [];
|
|
9269
|
+
}
|
|
9270
|
+
const filteredIssues = allIssues.filter((issue) => {
|
|
9271
|
+
if (options.project && issue.project !== options.project) return false;
|
|
9272
|
+
if (options.severity && !options.severity.includes(issue.severity)) return false;
|
|
9273
|
+
if (options.agent && issue.agent !== options.agent) return false;
|
|
9274
|
+
if (!options.includeResolved && issue.resolved) return false;
|
|
9275
|
+
return true;
|
|
9276
|
+
});
|
|
9277
|
+
if (filteredIssues.length === 0) {
|
|
9278
|
+
return [];
|
|
9279
|
+
}
|
|
9280
|
+
const bm25 = new BM25Index();
|
|
9281
|
+
const issueMap = /* @__PURE__ */ new Map();
|
|
9282
|
+
for (const issue of filteredIssues) {
|
|
9283
|
+
const searchText = `${issue.issue} ${issue.fix} ${issue.file} ${issue.agent} ${issue.category || ""} ${issue.severity}`;
|
|
9284
|
+
bm25.addDocument({
|
|
9285
|
+
id: issue.id,
|
|
9286
|
+
text: searchText
|
|
9287
|
+
});
|
|
9288
|
+
issueMap.set(issue.id, issue);
|
|
9289
|
+
}
|
|
9290
|
+
const bm25Results = bm25.search(query, limit);
|
|
9291
|
+
return bm25Results.map((result) => ({
|
|
9292
|
+
issue: issueMap.get(result.id),
|
|
9293
|
+
score: result.score,
|
|
9294
|
+
matchType: "bm25"
|
|
9295
|
+
}));
|
|
9296
|
+
}
|
|
9297
|
+
async function findSimilarIssues(issue, options = {}) {
|
|
9298
|
+
const query = `${issue.issue} ${issue.fix} ${issue.agent}`;
|
|
9299
|
+
const searchOptions = {
|
|
9300
|
+
limit: (options.limit || 5) + 5,
|
|
9301
|
+
// Get extra to account for filtering
|
|
9302
|
+
includeResolved: true
|
|
9303
|
+
};
|
|
9304
|
+
if (options.workDir !== void 0) {
|
|
9305
|
+
searchOptions.workDir = options.workDir;
|
|
9306
|
+
}
|
|
9307
|
+
const results = await searchIssues(query, searchOptions);
|
|
9308
|
+
let filtered = results.filter((r) => r.issue.id !== issue.id);
|
|
9309
|
+
if (options.excludeSameFile) {
|
|
9310
|
+
filtered = filtered.filter((r) => r.issue.file !== issue.file);
|
|
9311
|
+
}
|
|
9312
|
+
return filtered.slice(0, options.limit || 5);
|
|
9313
|
+
}
|
|
9314
|
+
async function markIssueResolved(issueId, workDir) {
|
|
9315
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
9316
|
+
const index = await loadIssueIndex(projectDir);
|
|
9317
|
+
const issue = index.find((i) => i.id === issueId);
|
|
9318
|
+
if (!issue) return false;
|
|
9319
|
+
issue.resolved = true;
|
|
9320
|
+
issue.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9321
|
+
await saveIssueIndex(index, projectDir);
|
|
9322
|
+
return true;
|
|
9323
|
+
}
|
|
9324
|
+
async function getMemoryStats(workDir) {
|
|
9325
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
9326
|
+
const index = await loadIssueIndex(projectDir);
|
|
9327
|
+
const historical = await getHistoricalInsights(projectDir);
|
|
9328
|
+
const stats = {
|
|
9329
|
+
totalIssues: index.length,
|
|
9330
|
+
issuesByAgent: {},
|
|
9331
|
+
issuesBySeverity: {},
|
|
9332
|
+
resolvedCount: 0,
|
|
9333
|
+
historicalIssues: historical.totalHistoricalIssues,
|
|
9334
|
+
improvementTrend: historical.improvementTrend
|
|
9335
|
+
};
|
|
9336
|
+
for (const issue of index) {
|
|
9337
|
+
stats.issuesByAgent[issue.agent] = (stats.issuesByAgent[issue.agent] || 0) + 1;
|
|
9338
|
+
stats.issuesBySeverity[issue.severity] = (stats.issuesBySeverity[issue.severity] || 0) + 1;
|
|
9339
|
+
if (issue.resolved) stats.resolvedCount++;
|
|
9340
|
+
}
|
|
9341
|
+
if (index.length > 0) {
|
|
9342
|
+
const sorted = [...index].sort(
|
|
9343
|
+
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
9344
|
+
);
|
|
9345
|
+
const oldest = sorted[0]?.timestamp;
|
|
9346
|
+
const newest = sorted[sorted.length - 1]?.timestamp;
|
|
9347
|
+
if (oldest !== void 0) {
|
|
9348
|
+
stats.oldestIssue = oldest;
|
|
9349
|
+
}
|
|
9350
|
+
if (newest !== void 0) {
|
|
9351
|
+
stats.newestIssue = newest;
|
|
9352
|
+
}
|
|
9353
|
+
}
|
|
9354
|
+
return stats;
|
|
9355
|
+
}
|
|
9356
|
+
async function getRecentIssues(options = {}) {
|
|
9357
|
+
const projectDir = options.workDir || getWorkingDirectory(void 0, true);
|
|
9358
|
+
const index = await loadIssueIndex(projectDir);
|
|
9359
|
+
const limit = options.limit || 20;
|
|
9360
|
+
const daysBack = options.daysBack || 7;
|
|
9361
|
+
const cutoff = /* @__PURE__ */ new Date();
|
|
9362
|
+
cutoff.setDate(cutoff.getDate() - daysBack);
|
|
9363
|
+
return index.filter((i) => new Date(i.timestamp) >= cutoff).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, limit);
|
|
9364
|
+
}
|
|
9365
|
+
async function getDailyLogs(workDir) {
|
|
9366
|
+
const projectDir = workDir || getWorkingDirectory(void 0, true);
|
|
9367
|
+
const memoryDir = join5(projectDir, ".trie", "memory");
|
|
9368
|
+
try {
|
|
9369
|
+
if (!existsSync4(memoryDir)) return [];
|
|
9370
|
+
const files = await readdir2(memoryDir);
|
|
9371
|
+
return files.filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort().reverse();
|
|
9372
|
+
} catch {
|
|
9373
|
+
return [];
|
|
9374
|
+
}
|
|
9375
|
+
}
|
|
9376
|
+
async function appendToDailyLog(issues, projectDir) {
|
|
9377
|
+
const memoryDir = join5(projectDir, ".trie", "memory");
|
|
9378
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9379
|
+
const logPath = join5(memoryDir, `${today}.md`);
|
|
9380
|
+
let content = "";
|
|
9381
|
+
try {
|
|
9382
|
+
if (existsSync4(logPath)) {
|
|
9383
|
+
content = await readFile5(logPath, "utf-8");
|
|
9384
|
+
} else {
|
|
9385
|
+
content = `# Issue Log: ${today}
|
|
9386
|
+
|
|
9387
|
+
`;
|
|
9388
|
+
}
|
|
9389
|
+
} catch {
|
|
9390
|
+
content = `# Issue Log: ${today}
|
|
9391
|
+
|
|
9392
|
+
`;
|
|
9393
|
+
}
|
|
9394
|
+
const time = (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0];
|
|
9395
|
+
const newEntries = issues.map(
|
|
9396
|
+
(i) => `## [${time}] ${i.severity.toUpperCase()}: ${i.issue.slice(0, 80)}${i.issue.length > 80 ? "..." : ""}
|
|
9397
|
+
- **File:** \`${i.file}\`${i.line ? `:${i.line}` : ""}
|
|
9398
|
+
- **Agent:** ${i.agent}
|
|
9399
|
+
- **Fix:** ${i.fix.slice(0, 200)}${i.fix.length > 200 ? "..." : ""}
|
|
9400
|
+
`
|
|
9401
|
+
).join("\n");
|
|
9402
|
+
content += newEntries + "\n";
|
|
9403
|
+
await writeFile4(logPath, content);
|
|
9404
|
+
}
|
|
9405
|
+
async function loadIssueIndex(projectDir) {
|
|
9406
|
+
const indexPath = join5(projectDir, ".trie", "memory", "issues.json");
|
|
9407
|
+
try {
|
|
9408
|
+
if (existsSync4(indexPath)) {
|
|
9409
|
+
const content = await readFile5(indexPath, "utf-8");
|
|
9410
|
+
return JSON.parse(content);
|
|
9411
|
+
}
|
|
9412
|
+
} catch {
|
|
9413
|
+
}
|
|
9414
|
+
return [];
|
|
9415
|
+
}
|
|
9416
|
+
async function updateIssueIndex(newIssues, projectDir) {
|
|
9417
|
+
const memoryDir = join5(projectDir, ".trie", "memory");
|
|
9418
|
+
await mkdir4(memoryDir, { recursive: true });
|
|
9419
|
+
let existing = await loadIssueIndex(projectDir);
|
|
9420
|
+
const hashSet = new Set(existing.map((i) => i.hash));
|
|
9421
|
+
const toAdd = newIssues.filter((i) => !hashSet.has(i.hash));
|
|
9422
|
+
existing = [...existing, ...toAdd];
|
|
9423
|
+
if (existing.length > 500) {
|
|
9424
|
+
const { summary, remaining } = await compactOldIssues(existing, {
|
|
9425
|
+
keepDays: 30,
|
|
9426
|
+
minIssuesToCompact: 100
|
|
9427
|
+
});
|
|
9428
|
+
if (summary) {
|
|
9429
|
+
await saveCompactedSummary(summary, projectDir);
|
|
9430
|
+
existing = remaining;
|
|
9431
|
+
}
|
|
9432
|
+
}
|
|
9433
|
+
if (existing.length > 1e3) {
|
|
9434
|
+
existing = existing.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).slice(0, 1e3);
|
|
9435
|
+
}
|
|
9436
|
+
await saveIssueIndex(existing, projectDir);
|
|
9437
|
+
}
|
|
9438
|
+
async function saveIssueIndex(issues, projectDir) {
|
|
9439
|
+
const indexPath = join5(projectDir, ".trie", "memory", "issues.json");
|
|
9440
|
+
await writeFile4(indexPath, JSON.stringify(issues, null, 2));
|
|
9441
|
+
}
|
|
9442
|
+
function hashIssue(issue) {
|
|
9443
|
+
const content = `${issue.issue}|${issue.file}|${issue.severity}|${issue.agent}`;
|
|
9444
|
+
let hash = 0;
|
|
9445
|
+
for (let i = 0; i < content.length; i++) {
|
|
9446
|
+
const char = content.charCodeAt(i);
|
|
9447
|
+
hash = (hash << 5) - hash + char;
|
|
9448
|
+
hash = hash & hash;
|
|
9449
|
+
}
|
|
9450
|
+
return Math.abs(hash).toString(36);
|
|
9451
|
+
}
|
|
9452
|
+
|
|
9453
|
+
// src/memory/global-memory.ts
|
|
9454
|
+
import { mkdir as mkdir5, writeFile as writeFile5, readFile as readFile6, readdir as readdir3 } from "fs/promises";
|
|
9455
|
+
import { existsSync as existsSync5 } from "fs";
|
|
9456
|
+
import { join as join6 } from "path";
|
|
9457
|
+
import { homedir } from "os";
|
|
9458
|
+
var GLOBAL_TRIE_DIR = join6(homedir(), ".trie");
|
|
9459
|
+
var GLOBAL_MEMORY_DIR = join6(GLOBAL_TRIE_DIR, "memory");
|
|
9460
|
+
async function recordToGlobalMemory(issues, projectName, projectPath, healthScore = 0) {
|
|
9461
|
+
await mkdir5(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
9462
|
+
await mkdir5(join6(GLOBAL_MEMORY_DIR, "projects"), { recursive: true });
|
|
9463
|
+
const patterns = await loadGlobalPatterns();
|
|
9464
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9465
|
+
for (const issue of issues) {
|
|
9466
|
+
const patternId = extractPatternId(issue);
|
|
9467
|
+
const existing = patterns.find((p) => p.id === patternId);
|
|
9468
|
+
if (existing) {
|
|
9469
|
+
existing.occurrences++;
|
|
9470
|
+
existing.lastSeen = now;
|
|
9471
|
+
if (!existing.projects.includes(projectName)) {
|
|
9472
|
+
existing.projects.push(projectName);
|
|
9473
|
+
}
|
|
9474
|
+
} else {
|
|
9475
|
+
patterns.push({
|
|
9476
|
+
id: patternId,
|
|
9477
|
+
pattern: issue.issue.slice(0, 200),
|
|
9478
|
+
description: issue.fix.slice(0, 200),
|
|
9479
|
+
severity: issue.severity,
|
|
9480
|
+
agent: issue.agent,
|
|
9481
|
+
occurrences: 1,
|
|
9482
|
+
projects: [projectName],
|
|
9483
|
+
firstSeen: now,
|
|
9484
|
+
lastSeen: now
|
|
9485
|
+
});
|
|
9486
|
+
}
|
|
9487
|
+
}
|
|
9488
|
+
await saveGlobalPatterns(patterns);
|
|
9489
|
+
const summaryPath = join6(GLOBAL_MEMORY_DIR, "projects", `${sanitizeName(projectName)}.json`);
|
|
9490
|
+
const summary = {
|
|
9491
|
+
name: projectName,
|
|
9492
|
+
path: projectPath,
|
|
9493
|
+
lastScan: now,
|
|
9494
|
+
healthScore,
|
|
9495
|
+
totalIssues: issues.length,
|
|
9496
|
+
patterns: [...new Set(issues.map((i) => extractPatternId(i)))]
|
|
9497
|
+
};
|
|
9498
|
+
await writeFile5(summaryPath, JSON.stringify(summary, null, 2));
|
|
9499
|
+
}
|
|
9500
|
+
async function findCrossProjectPatterns(minOccurrences = 2) {
|
|
9501
|
+
const patterns = await loadGlobalPatterns();
|
|
9502
|
+
return patterns.filter((p) => p.projects.length >= minOccurrences).sort((a, b) => b.occurrences - a.occurrences);
|
|
9503
|
+
}
|
|
9504
|
+
async function listTrackedProjects() {
|
|
9505
|
+
const projectsDir = join6(GLOBAL_MEMORY_DIR, "projects");
|
|
9506
|
+
try {
|
|
9507
|
+
if (!existsSync5(projectsDir)) return [];
|
|
9508
|
+
const files = await readdir3(projectsDir);
|
|
9509
|
+
const summaries = [];
|
|
9510
|
+
for (const file of files) {
|
|
9511
|
+
if (!file.endsWith(".json")) continue;
|
|
9512
|
+
try {
|
|
9513
|
+
const content = await readFile6(join6(projectsDir, file), "utf-8");
|
|
9514
|
+
summaries.push(JSON.parse(content));
|
|
9515
|
+
} catch {
|
|
9516
|
+
}
|
|
9517
|
+
}
|
|
9518
|
+
return summaries.sort(
|
|
9519
|
+
(a, b) => new Date(b.lastScan).getTime() - new Date(a.lastScan).getTime()
|
|
9520
|
+
);
|
|
9521
|
+
} catch {
|
|
9522
|
+
return [];
|
|
9523
|
+
}
|
|
9524
|
+
}
|
|
9525
|
+
async function getGlobalMemoryStats() {
|
|
9526
|
+
const patterns = await loadGlobalPatterns();
|
|
9527
|
+
const projects = await listTrackedProjects();
|
|
9528
|
+
const patternsByAgent = {};
|
|
9529
|
+
for (const pattern of patterns) {
|
|
9530
|
+
patternsByAgent[pattern.agent] = (patternsByAgent[pattern.agent] || 0) + 1;
|
|
9531
|
+
}
|
|
9532
|
+
return {
|
|
9533
|
+
totalPatterns: patterns.length,
|
|
9534
|
+
crossProjectPatterns: patterns.filter((p) => p.projects.length >= 2).length,
|
|
9535
|
+
trackedProjects: projects.length,
|
|
9536
|
+
totalOccurrences: patterns.reduce((sum, p) => sum + p.occurrences, 0),
|
|
9537
|
+
fixedPatterns: patterns.filter((p) => p.fixApplied).length,
|
|
9538
|
+
patternsByAgent
|
|
9539
|
+
};
|
|
9540
|
+
}
|
|
9541
|
+
async function updateGlobalMemoryMd() {
|
|
9542
|
+
const patterns = await loadGlobalPatterns();
|
|
9543
|
+
const crossProject = patterns.filter((p) => p.projects.length >= 2);
|
|
9544
|
+
const projects = await listTrackedProjects();
|
|
9545
|
+
const lines = [
|
|
9546
|
+
"# Global Trie Memory",
|
|
9547
|
+
"",
|
|
9548
|
+
"> Auto-generated file tracking patterns across all your projects.",
|
|
9549
|
+
`> Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
9550
|
+
"",
|
|
9551
|
+
"## Summary",
|
|
9552
|
+
"",
|
|
9553
|
+
`- **Projects tracked:** ${projects.length}`,
|
|
9554
|
+
`- **Total patterns:** ${patterns.length}`,
|
|
9555
|
+
`- **Cross-project patterns:** ${crossProject.length}`,
|
|
9556
|
+
"",
|
|
9557
|
+
"## Cross-Project Patterns",
|
|
9558
|
+
"",
|
|
9559
|
+
"These issues appear in multiple projects:",
|
|
9560
|
+
""
|
|
9561
|
+
];
|
|
9562
|
+
for (const p of crossProject.slice(0, 20)) {
|
|
9563
|
+
lines.push(
|
|
9564
|
+
`### ${p.pattern.slice(0, 60)}${p.pattern.length > 60 ? "..." : ""}`,
|
|
9565
|
+
"",
|
|
9566
|
+
`- **Severity:** ${p.severity}`,
|
|
9567
|
+
`- **Agent:** ${p.agent}`,
|
|
9568
|
+
`- **Occurrences:** ${p.occurrences} across ${p.projects.length} projects`,
|
|
9569
|
+
`- **Projects:** ${p.projects.slice(0, 5).join(", ")}${p.projects.length > 5 ? "..." : ""}`
|
|
9570
|
+
);
|
|
9571
|
+
if (p.fixApplied) {
|
|
9572
|
+
lines.push(`- **Fixed in:** ${p.fixApplied.project} on ${p.fixApplied.timestamp.split("T")[0]}`);
|
|
9573
|
+
} else {
|
|
9574
|
+
lines.push("- **Status:** Not fixed");
|
|
9575
|
+
}
|
|
9576
|
+
lines.push("");
|
|
9577
|
+
}
|
|
9578
|
+
lines.push(
|
|
9579
|
+
"## Tracked Projects",
|
|
9580
|
+
"",
|
|
9581
|
+
"| Project | Last Scan | Health | Issues |",
|
|
9582
|
+
"|---------|-----------|--------|--------|"
|
|
9583
|
+
);
|
|
9584
|
+
for (const p of projects.slice(0, 20)) {
|
|
9585
|
+
lines.push(`| ${p.name} | ${p.lastScan.split("T")[0]} | ${p.healthScore}% | ${p.totalIssues} |`);
|
|
9586
|
+
}
|
|
9587
|
+
lines.push("", "---", "", "*This file is auto-generated by Trie. Do not edit manually.*");
|
|
9588
|
+
await mkdir5(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
9589
|
+
await writeFile5(join6(GLOBAL_MEMORY_DIR, "GLOBAL_MEMORY.md"), lines.join("\n"));
|
|
9590
|
+
}
|
|
9591
|
+
async function searchGlobalPatterns(query, options = {}) {
|
|
9592
|
+
const patterns = await loadGlobalPatterns();
|
|
9593
|
+
const limit = options.limit || 10;
|
|
9594
|
+
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
9595
|
+
const scored = patterns.filter((p) => {
|
|
9596
|
+
if (options.severity && !options.severity.includes(p.severity)) return false;
|
|
9597
|
+
if (options.agent && p.agent !== options.agent) return false;
|
|
9598
|
+
return true;
|
|
9599
|
+
}).map((p) => {
|
|
9600
|
+
const text = `${p.pattern} ${p.description} ${p.agent}`.toLowerCase();
|
|
9601
|
+
let score = 0;
|
|
9602
|
+
for (const term of queryTerms) {
|
|
9603
|
+
if (text.includes(term)) score++;
|
|
9604
|
+
}
|
|
9605
|
+
return { pattern: p, score };
|
|
9606
|
+
}).filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
9607
|
+
return scored.map((s) => s.pattern);
|
|
9608
|
+
}
|
|
9609
|
+
async function loadGlobalPatterns() {
|
|
9610
|
+
const patternsPath = join6(GLOBAL_MEMORY_DIR, "global-patterns.json");
|
|
9611
|
+
try {
|
|
9612
|
+
if (existsSync5(patternsPath)) {
|
|
9613
|
+
const content = await readFile6(patternsPath, "utf-8");
|
|
9614
|
+
return JSON.parse(content);
|
|
9615
|
+
}
|
|
9616
|
+
} catch {
|
|
9617
|
+
}
|
|
9618
|
+
return [];
|
|
9619
|
+
}
|
|
9620
|
+
async function saveGlobalPatterns(patterns) {
|
|
9621
|
+
await mkdir5(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
9622
|
+
const patternsPath = join6(GLOBAL_MEMORY_DIR, "global-patterns.json");
|
|
9623
|
+
const pruned = patterns.sort((a, b) => new Date(b.lastSeen).getTime() - new Date(a.lastSeen).getTime()).slice(0, 500);
|
|
9624
|
+
await writeFile5(patternsPath, JSON.stringify(pruned, null, 2));
|
|
9625
|
+
}
|
|
9626
|
+
function extractPatternId(issue) {
|
|
9627
|
+
const normalized = issue.issue.toLowerCase().replace(/`[^`]+`/g, "CODE").replace(/\b\d+\b/g, "N").replace(/['"]/g, "").slice(0, 100);
|
|
9628
|
+
let hash = 0;
|
|
9629
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
9630
|
+
const char = normalized.charCodeAt(i);
|
|
9631
|
+
hash = (hash << 5) - hash + char;
|
|
9632
|
+
hash = hash & hash;
|
|
9633
|
+
}
|
|
9634
|
+
return `${issue.agent}-${issue.severity}-${Math.abs(hash).toString(36)}`;
|
|
9635
|
+
}
|
|
9636
|
+
function sanitizeName(name) {
|
|
9637
|
+
return name.replace(/[^a-zA-Z0-9-_]/g, "-").toLowerCase();
|
|
9638
|
+
}
|
|
9639
|
+
|
|
9640
|
+
// src/utils/context-state.ts
|
|
9641
|
+
var AGENTS_MD_PATH = ".trie/AGENTS.md";
|
|
9642
|
+
var STATE_JSON_PATH = ".trie/state.json";
|
|
9643
|
+
async function loadContextState() {
|
|
9644
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
9645
|
+
const statePath = join7(workDir, STATE_JSON_PATH);
|
|
9646
|
+
const defaults = getDefaultState();
|
|
9647
|
+
try {
|
|
9648
|
+
if (existsSync6(statePath)) {
|
|
9649
|
+
const content = await readFile7(statePath, "utf-8");
|
|
9650
|
+
const loaded = JSON.parse(content);
|
|
9651
|
+
return {
|
|
9652
|
+
...defaults,
|
|
9653
|
+
...loaded,
|
|
9654
|
+
skills: loaded.skills || defaults.skills
|
|
9655
|
+
};
|
|
9656
|
+
}
|
|
9657
|
+
} catch {
|
|
9658
|
+
}
|
|
9659
|
+
return defaults;
|
|
9660
|
+
}
|
|
9661
|
+
async function saveContextState(state) {
|
|
9662
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
9663
|
+
const trieDir = join7(workDir, ".trie");
|
|
9664
|
+
const statePath = join7(workDir, STATE_JSON_PATH);
|
|
9665
|
+
await mkdir6(trieDir, { recursive: true });
|
|
9666
|
+
await writeFile6(statePath, JSON.stringify(state, null, 2));
|
|
9667
|
+
}
|
|
9668
|
+
async function updateContextAfterScan(results, filesScanned, contextSignals, duration) {
|
|
9669
|
+
const state = await loadContextState();
|
|
9670
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
9671
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9672
|
+
const allIssues = results.flatMap((r) => r.issues);
|
|
9673
|
+
const issueCounts = {
|
|
9674
|
+
critical: allIssues.filter((i) => i.severity === "critical").length,
|
|
9675
|
+
serious: allIssues.filter((i) => i.severity === "serious").length,
|
|
9676
|
+
moderate: allIssues.filter((i) => i.severity === "moderate").length,
|
|
9677
|
+
low: allIssues.filter((i) => i.severity === "low").length,
|
|
9678
|
+
total: allIssues.length
|
|
9679
|
+
};
|
|
9680
|
+
const fileIssueMap = /* @__PURE__ */ new Map();
|
|
9681
|
+
for (const issue of allIssues) {
|
|
9682
|
+
const count = fileIssueMap.get(issue.file) || 0;
|
|
9683
|
+
fileIssueMap.set(issue.file, count + 1);
|
|
9684
|
+
}
|
|
9685
|
+
const hotFiles = Array.from(fileIssueMap.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([file, issueCount]) => ({ file, issueCount }));
|
|
9686
|
+
const scanSummary = {
|
|
9687
|
+
timestamp: now,
|
|
9688
|
+
agents: results.map((r) => r.agent),
|
|
9689
|
+
filesScanned,
|
|
9690
|
+
issues: issueCounts,
|
|
9691
|
+
duration,
|
|
9692
|
+
hotFiles
|
|
9693
|
+
};
|
|
9694
|
+
for (const result of results) {
|
|
9695
|
+
state.agentStatus[result.agent] = {
|
|
9696
|
+
lastRun: now,
|
|
9697
|
+
issuesFound: result.issues.length
|
|
9698
|
+
};
|
|
9699
|
+
}
|
|
9700
|
+
const criticalPenalty = issueCounts.critical * 25;
|
|
9701
|
+
const seriousPenalty = issueCounts.serious * 10;
|
|
9702
|
+
const moderatePenalty = issueCounts.moderate * 3;
|
|
9703
|
+
const lowPenalty = issueCounts.low * 1;
|
|
9704
|
+
const totalPenalty = Math.min(100, criticalPenalty + seriousPenalty + moderatePenalty + lowPenalty);
|
|
9705
|
+
state.healthScore = Math.max(0, 100 - totalPenalty);
|
|
9706
|
+
state.activePriorities = generatePriorities(issueCounts, contextSignals);
|
|
9707
|
+
state.contextSignals = { ...state.contextSignals, ...contextSignals };
|
|
9708
|
+
state.scanHistory = [scanSummary, ...state.scanHistory.slice(0, 19)];
|
|
9709
|
+
state.lastScan = scanSummary;
|
|
9710
|
+
await saveContextState(state);
|
|
9711
|
+
await updateAgentsMd(state);
|
|
9712
|
+
if (allIssues.length > 0) {
|
|
9713
|
+
const projectName = basename2(workDir);
|
|
9714
|
+
try {
|
|
9715
|
+
await storeIssues(allIssues, projectName, workDir);
|
|
9716
|
+
await recordToGlobalMemory(allIssues, projectName, workDir, state.healthScore);
|
|
9717
|
+
await updateGlobalMemoryMd();
|
|
9718
|
+
} catch {
|
|
9719
|
+
}
|
|
9720
|
+
}
|
|
9721
|
+
}
|
|
9722
|
+
async function updateAgentsMd(state) {
|
|
9723
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
9724
|
+
const mdPath = join7(workDir, AGENTS_MD_PATH);
|
|
9725
|
+
let content;
|
|
9726
|
+
try {
|
|
9727
|
+
content = await readFile7(mdPath, "utf-8");
|
|
9728
|
+
} catch {
|
|
9729
|
+
content = getAgentsMdTemplate();
|
|
9730
|
+
}
|
|
9731
|
+
content = updateSection(content, "Project State", generateProjectStateTable(state));
|
|
9732
|
+
content = updateSection(content, "Active Priorities", generatePrioritiesList(state));
|
|
9733
|
+
content = updateSection(content, "Agent Status", generateAgentStatusTable(state));
|
|
9734
|
+
content = updateSection(content, "Recent Scan History", generateScanHistoryTable(state));
|
|
9735
|
+
content = updateSection(content, "Context Signals Detected", generateContextSignals(state));
|
|
9736
|
+
content = updateSection(content, "Risk Assessment", generateRiskAssessment(state));
|
|
9737
|
+
content = updateSection(content, "Hot Files", generateHotFilesSection(state));
|
|
9738
|
+
content = content.replace(
|
|
9739
|
+
/Last updated:.*$/m,
|
|
9740
|
+
`Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`
|
|
9741
|
+
);
|
|
9742
|
+
await writeFile6(mdPath, content);
|
|
9743
|
+
}
|
|
9744
|
+
function updateSection(content, sectionName, newContent) {
|
|
9745
|
+
const sectionRegex = new RegExp(
|
|
9746
|
+
`(### ${sectionName}[\\s\\S]*?)(?=###|---|
|
|
9747
|
+
## |$)`,
|
|
9748
|
+
"g"
|
|
9749
|
+
);
|
|
9750
|
+
const replacement = `### ${sectionName}
|
|
9751
|
+
${newContent}
|
|
9752
|
+
|
|
9753
|
+
`;
|
|
9754
|
+
if (content.match(sectionRegex)) {
|
|
9755
|
+
return content.replace(sectionRegex, replacement);
|
|
9756
|
+
}
|
|
9757
|
+
return content;
|
|
9758
|
+
}
|
|
9759
|
+
function generateProjectStateTable(state) {
|
|
9760
|
+
const lastScan = state.lastScan;
|
|
9761
|
+
const lastScanDate = lastScan ? new Date(lastScan.timestamp).toLocaleString() : "Never";
|
|
9762
|
+
const criticalCount = lastScan?.issues.critical ?? 0;
|
|
9763
|
+
const totalTasks = lastScan?.issues.total ?? 0;
|
|
9764
|
+
return `| Metric | Value | Updated |
|
|
9765
|
+
|--------|-------|---------|
|
|
9766
|
+
| Last Scan | ${lastScanDate} | ${lastScan ? "Auto" : "-"} |
|
|
9767
|
+
| Critical Issues | ${criticalCount} | ${lastScan ? "Auto" : "-"} |
|
|
9768
|
+
| Open Tasks | ${totalTasks} | ${lastScan ? "Auto" : "-"} |
|
|
9769
|
+
| Health Score | ${state.healthScore}% | ${lastScan ? "Auto" : "-"} |`;
|
|
9770
|
+
}
|
|
9771
|
+
function generatePrioritiesList(state) {
|
|
9772
|
+
if (state.activePriorities.length === 0) {
|
|
9773
|
+
return "_No active priorities. Run a scan to identify issues._";
|
|
9774
|
+
}
|
|
9775
|
+
return state.activePriorities.map((p, i) => `${i + 1}. ${p}`).join("\n");
|
|
9776
|
+
}
|
|
9777
|
+
function generatePriorities(issues, contextSignals) {
|
|
9778
|
+
const priorities = [];
|
|
9779
|
+
if (issues.critical > 0) {
|
|
9780
|
+
priorities.push(`\u{1F6A8} Fix ${issues.critical} critical security issue${issues.critical > 1 ? "s" : ""} immediately`);
|
|
9781
|
+
}
|
|
9782
|
+
if (issues.serious > 0) {
|
|
9783
|
+
priorities.push(`\u26A0\uFE0F Address ${issues.serious} serious issue${issues.serious > 1 ? "s" : ""} before deployment`);
|
|
9784
|
+
}
|
|
9785
|
+
if (contextSignals.touchesAuth && issues.critical === 0) {
|
|
9786
|
+
priorities.push("\u2705 Auth code reviewed - continue monitoring");
|
|
9787
|
+
}
|
|
9788
|
+
if (contextSignals.touchesPayments) {
|
|
9789
|
+
priorities.push("\u{1F4B3} Payment code detected - ensure PCI compliance");
|
|
9790
|
+
}
|
|
9791
|
+
if (contextSignals.touchesUserData) {
|
|
9792
|
+
priorities.push("\u{1F510} User data handling detected - verify privacy compliance");
|
|
9793
|
+
}
|
|
9794
|
+
if (issues.moderate > 5) {
|
|
9795
|
+
priorities.push(`\u{1F4CB} Schedule time to address ${issues.moderate} moderate issues`);
|
|
9796
|
+
}
|
|
9797
|
+
if (priorities.length === 0) {
|
|
9798
|
+
priorities.push("\u2728 No critical issues - focus on feature development");
|
|
9799
|
+
}
|
|
9800
|
+
return priorities.slice(0, 5);
|
|
9801
|
+
}
|
|
9802
|
+
function generateAgentStatusTable(state) {
|
|
9803
|
+
const builtInAgents = [
|
|
9804
|
+
"security",
|
|
9805
|
+
"privacy",
|
|
9806
|
+
"legal",
|
|
9807
|
+
"accessibility",
|
|
9808
|
+
"bugs",
|
|
9809
|
+
"design",
|
|
9810
|
+
"architecture",
|
|
9811
|
+
"performance",
|
|
9812
|
+
"devops",
|
|
9813
|
+
"soc2",
|
|
9814
|
+
"e2e",
|
|
9815
|
+
"typecheck",
|
|
9816
|
+
"visual-qa",
|
|
9817
|
+
"data-flow"
|
|
9818
|
+
];
|
|
9819
|
+
let table = `| Agent | Status | Last Run | Issues Found |
|
|
9820
|
+
|-------|--------|----------|--------------|`;
|
|
9821
|
+
for (const agent of builtInAgents) {
|
|
9822
|
+
const status = state.agentStatus[agent];
|
|
9823
|
+
const lastRun = status?.lastRun ? new Date(status.lastRun).toLocaleDateString() : "Never";
|
|
9824
|
+
const issues = status?.issuesFound ?? "-";
|
|
9825
|
+
const statusEmoji = status ? "\u2705" : "\u23F8\uFE0F";
|
|
9826
|
+
table += `
|
|
9827
|
+
| ${agent} | ${statusEmoji} Ready | ${lastRun} | ${issues} |`;
|
|
9828
|
+
}
|
|
9829
|
+
return table;
|
|
9830
|
+
}
|
|
9831
|
+
function generateScanHistoryTable(state) {
|
|
9832
|
+
if (state.scanHistory.length === 0) {
|
|
9833
|
+
return `| Date | Agents | Files | Issues | Duration |
|
|
9834
|
+
|------|--------|-------|--------|----------|
|
|
9835
|
+
| - | - | - | - | - |`;
|
|
9836
|
+
}
|
|
9837
|
+
let table = `| Date | Agents | Files | Issues | Duration |
|
|
9838
|
+
|------|--------|-------|--------|----------|`;
|
|
9839
|
+
for (const scan of state.scanHistory.slice(0, 10)) {
|
|
9840
|
+
const date = new Date(scan.timestamp).toLocaleDateString();
|
|
9841
|
+
const agents = scan.agents.slice(0, 3).join(", ") + (scan.agents.length > 3 ? "..." : "");
|
|
9842
|
+
const duration = `${(scan.duration / 1e3).toFixed(1)}s`;
|
|
9843
|
+
table += `
|
|
9844
|
+
| ${date} | ${agents} | ${scan.filesScanned} | ${scan.issues.total} | ${duration} |`;
|
|
9845
|
+
}
|
|
9846
|
+
return table;
|
|
9847
|
+
}
|
|
9848
|
+
function generateContextSignals(state) {
|
|
9849
|
+
const signals = [
|
|
9850
|
+
"touchesAuth",
|
|
9851
|
+
"touchesPayments",
|
|
9852
|
+
"touchesUserData",
|
|
9853
|
+
"touchesAPI",
|
|
9854
|
+
"touchesDatabase",
|
|
9855
|
+
"touchesCrypto"
|
|
9856
|
+
];
|
|
9857
|
+
return signals.map((s) => {
|
|
9858
|
+
const value = state.contextSignals[s];
|
|
9859
|
+
const emoji = value === true ? "\u2705" : value === false ? "\u274C" : "\u2753";
|
|
9860
|
+
return `- \`${s}\`: ${emoji} ${value === void 0 ? "Unknown" : value ? "Yes" : "No"}`;
|
|
9861
|
+
}).join("\n");
|
|
9862
|
+
}
|
|
9863
|
+
function generateRiskAssessment(state) {
|
|
9864
|
+
const score = state.healthScore;
|
|
9865
|
+
let riskLevel;
|
|
9866
|
+
let confidence;
|
|
9867
|
+
if (state.lastScan === null) {
|
|
9868
|
+
return `- Overall Risk: Unknown
|
|
9869
|
+
- Confidence: 0%`;
|
|
9870
|
+
}
|
|
9871
|
+
if (score >= 90) {
|
|
9872
|
+
riskLevel = "\u{1F7E2} Low";
|
|
9873
|
+
confidence = 95;
|
|
9874
|
+
} else if (score >= 70) {
|
|
9875
|
+
riskLevel = "\u{1F7E1} Medium";
|
|
9876
|
+
confidence = 85;
|
|
9877
|
+
} else if (score >= 50) {
|
|
9878
|
+
riskLevel = "\u{1F7E0} High";
|
|
9879
|
+
confidence = 80;
|
|
9880
|
+
} else {
|
|
9881
|
+
riskLevel = "\u{1F534} Critical";
|
|
9882
|
+
confidence = 90;
|
|
9883
|
+
}
|
|
9884
|
+
return `- Overall Risk: ${riskLevel}
|
|
9885
|
+
- Health Score: ${score}%
|
|
9886
|
+
- Confidence: ${confidence}%`;
|
|
9887
|
+
}
|
|
9888
|
+
function generateHotFilesSection(state) {
|
|
9889
|
+
if (!state.lastScan || state.lastScan.hotFiles.length === 0) {
|
|
9890
|
+
return "_Run a scan to identify hot files._";
|
|
9891
|
+
}
|
|
9892
|
+
return state.lastScan.hotFiles.map((f) => `- \`${f.file}\` - ${f.issueCount} issue${f.issueCount > 1 ? "s" : ""}`).join("\n");
|
|
9893
|
+
}
|
|
9894
|
+
function getDefaultState() {
|
|
9895
|
+
return {
|
|
9896
|
+
lastScan: null,
|
|
9897
|
+
healthScore: 0,
|
|
9898
|
+
activePriorities: [
|
|
9899
|
+
"Initial setup required - run first scan with `trie scan`",
|
|
9900
|
+
"Configure agents in `.trie/config.json`",
|
|
9901
|
+
"Set up CI/CD integration"
|
|
9902
|
+
],
|
|
9903
|
+
contextSignals: {},
|
|
9904
|
+
agentStatus: {},
|
|
9905
|
+
scanHistory: [],
|
|
9906
|
+
customAgents: [],
|
|
9907
|
+
skills: {},
|
|
9908
|
+
environment: detectEnvironment()
|
|
9909
|
+
};
|
|
9910
|
+
}
|
|
9911
|
+
function detectEnvironment() {
|
|
9912
|
+
if (process.env.GITHUB_ACTIONS) return "github-actions";
|
|
9913
|
+
if (process.env.GITLAB_CI) return "gitlab-ci";
|
|
9914
|
+
if (process.env.CI) return "ci";
|
|
9915
|
+
const parent = process.env._ || "";
|
|
9916
|
+
if (parent.includes("cursor")) return "cursor";
|
|
9917
|
+
if (parent.includes("claude")) return "claude-code";
|
|
9918
|
+
return "cli";
|
|
9919
|
+
}
|
|
9920
|
+
async function recordSkillInstalled(params) {
|
|
9921
|
+
const state = await loadContextState();
|
|
9922
|
+
state.skills[params.name] = {
|
|
9923
|
+
source: params.source,
|
|
9924
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9925
|
+
timesApplied: 0,
|
|
9926
|
+
appliedBy: []
|
|
9927
|
+
};
|
|
9928
|
+
await saveContextState(state);
|
|
9929
|
+
}
|
|
9930
|
+
async function recordSkillUsage(params) {
|
|
9931
|
+
const state = await loadContextState();
|
|
9932
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9933
|
+
for (const skillName of params.skillNames) {
|
|
9934
|
+
const skillRecord = state.skills[skillName];
|
|
9935
|
+
if (skillRecord) {
|
|
9936
|
+
skillRecord.timesApplied++;
|
|
9937
|
+
skillRecord.lastApplied = now;
|
|
9938
|
+
if (!skillRecord.appliedBy.includes(params.agentName)) {
|
|
9939
|
+
skillRecord.appliedBy.push(params.agentName);
|
|
9940
|
+
}
|
|
9941
|
+
}
|
|
9942
|
+
}
|
|
9943
|
+
const agentStatus = state.agentStatus[params.agentName];
|
|
9944
|
+
if (agentStatus) {
|
|
9945
|
+
agentStatus.skillsApplied = params.skillNames;
|
|
9946
|
+
}
|
|
9947
|
+
await saveContextState(state);
|
|
9948
|
+
}
|
|
9949
|
+
function getAgentsMdTemplate() {
|
|
9950
|
+
return `# Trie Agent Context
|
|
9951
|
+
|
|
9952
|
+
> **Auto-generated file** - Updated automatically when agents run.
|
|
9953
|
+
> Last updated: Never (initial state)
|
|
9954
|
+
|
|
9955
|
+
This file provides prioritized context for all AI coding assistants working with this codebase.
|
|
9956
|
+
Agents should read this file first and update it after completing scans.
|
|
9957
|
+
|
|
9958
|
+
---
|
|
9959
|
+
|
|
9960
|
+
## Quick Context (Read First)
|
|
9961
|
+
|
|
9962
|
+
### Project State
|
|
9963
|
+
| Metric | Value | Updated |
|
|
9964
|
+
|--------|-------|---------|
|
|
9965
|
+
| Last Scan | Never | - |
|
|
9966
|
+
| Critical Issues | 0 | - |
|
|
9967
|
+
| Open Tasks | 0 | - |
|
|
9968
|
+
| Health Score | Unknown | - |
|
|
9969
|
+
|
|
9970
|
+
### Active Priorities
|
|
9971
|
+
1. Initial setup required - run first scan with \`trie scan\`
|
|
9972
|
+
2. Configure agents in \`.trie/config.json\`
|
|
9973
|
+
3. Set up CI/CD integration
|
|
9974
|
+
|
|
9975
|
+
### Hot Files
|
|
9976
|
+
_Run a scan to identify hot files._
|
|
9977
|
+
|
|
9978
|
+
---
|
|
9979
|
+
|
|
9980
|
+
## Agent Status
|
|
9981
|
+
|
|
9982
|
+
### Agent Status
|
|
9983
|
+
| Agent | Status | Last Run | Issues Found |
|
|
9984
|
+
|-------|--------|----------|--------------|
|
|
9985
|
+
| security | Ready | Never | - |
|
|
9986
|
+
| privacy | Ready | Never | - |
|
|
9987
|
+
| bugs | Ready | Never | - |
|
|
9988
|
+
|
|
9989
|
+
### Recent Scan History
|
|
9990
|
+
| Date | Agents | Files | Issues | Duration |
|
|
9991
|
+
|------|--------|-------|--------|----------|
|
|
9992
|
+
| - | - | - | - | - |
|
|
9993
|
+
|
|
9994
|
+
---
|
|
9995
|
+
|
|
9996
|
+
## Context Analysis
|
|
9997
|
+
|
|
9998
|
+
### Context Signals Detected
|
|
9999
|
+
- \`touchesAuth\`: Unknown
|
|
10000
|
+
- \`touchesPayments\`: Unknown
|
|
10001
|
+
- \`touchesUserData\`: Unknown
|
|
10002
|
+
- \`touchesAPI\`: Unknown
|
|
10003
|
+
- \`touchesDatabase\`: Unknown
|
|
10004
|
+
- \`touchesCrypto\`: Unknown
|
|
10005
|
+
|
|
10006
|
+
### Risk Assessment
|
|
10007
|
+
- Overall Risk: Unknown
|
|
10008
|
+
- Confidence: 0%
|
|
10009
|
+
|
|
10010
|
+
---
|
|
10011
|
+
|
|
10012
|
+
*This file is maintained by Trie agents. Manual edits will be preserved in non-auto sections.*
|
|
10013
|
+
`;
|
|
10014
|
+
}
|
|
10015
|
+
async function getContextForAI() {
|
|
10016
|
+
const state = await loadContextState();
|
|
10017
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
10018
|
+
const lines = [];
|
|
10019
|
+
if (projectInfoExists(workDir)) {
|
|
10020
|
+
const projectInfo = await loadProjectInfo(workDir);
|
|
10021
|
+
if (projectInfo) {
|
|
10022
|
+
lines.push(projectInfo);
|
|
10023
|
+
lines.push("");
|
|
10024
|
+
lines.push("---");
|
|
10025
|
+
lines.push("");
|
|
10026
|
+
}
|
|
10027
|
+
}
|
|
10028
|
+
lines.push(
|
|
10029
|
+
"## Trie Scan Context",
|
|
10030
|
+
"",
|
|
10031
|
+
`**Health Score:** ${state.healthScore}%`,
|
|
10032
|
+
`**Last Scan:** ${state.lastScan ? new Date(state.lastScan.timestamp).toLocaleString() : "Never"}`,
|
|
10033
|
+
"",
|
|
10034
|
+
"**Active Priorities:**",
|
|
10035
|
+
...state.activePriorities.map((p) => `- ${p}`),
|
|
10036
|
+
""
|
|
10037
|
+
);
|
|
10038
|
+
if (state.lastScan) {
|
|
10039
|
+
lines.push(
|
|
10040
|
+
"**Recent Issues:**",
|
|
10041
|
+
`- Critical: ${state.lastScan.issues.critical}`,
|
|
10042
|
+
`- Serious: ${state.lastScan.issues.serious}`,
|
|
10043
|
+
`- Moderate: ${state.lastScan.issues.moderate}`,
|
|
10044
|
+
`- Low: ${state.lastScan.issues.low}`,
|
|
10045
|
+
""
|
|
10046
|
+
);
|
|
10047
|
+
if (state.lastScan.hotFiles.length > 0) {
|
|
10048
|
+
lines.push(
|
|
10049
|
+
"**Hot Files (most issues):**",
|
|
10050
|
+
...state.lastScan.hotFiles.slice(0, 5).map((f) => `- ${f.file}: ${f.issueCount} issues`),
|
|
10051
|
+
""
|
|
10052
|
+
);
|
|
10053
|
+
}
|
|
10054
|
+
}
|
|
10055
|
+
return lines.join("\n");
|
|
10056
|
+
}
|
|
10057
|
+
|
|
10058
|
+
// src/skills/gating.ts
|
|
10059
|
+
import { existsSync as existsSync7 } from "fs";
|
|
10060
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
10061
|
+
import { join as join8 } from "path";
|
|
8487
10062
|
import { execSync } from "child_process";
|
|
8488
10063
|
async function checkSkillRequirements(frontmatter, projectDir) {
|
|
8489
10064
|
const result = { allowed: true };
|
|
@@ -8540,7 +10115,7 @@ async function checkSkillRequirements(frontmatter, projectDir) {
|
|
|
8540
10115
|
}
|
|
8541
10116
|
}
|
|
8542
10117
|
if (reqs.configFiles && reqs.configFiles.length > 0) {
|
|
8543
|
-
const missing = reqs.configFiles.filter((file) => !
|
|
10118
|
+
const missing = reqs.configFiles.filter((file) => !existsSync7(join8(projectDir, file)));
|
|
8544
10119
|
if (missing.length > 0) {
|
|
8545
10120
|
result.allowed = false;
|
|
8546
10121
|
result.missingConfigs = missing;
|
|
@@ -8552,11 +10127,11 @@ async function checkSkillRequirements(frontmatter, projectDir) {
|
|
|
8552
10127
|
}
|
|
8553
10128
|
async function getProjectDependencies(projectDir) {
|
|
8554
10129
|
try {
|
|
8555
|
-
const pkgPath =
|
|
8556
|
-
if (!
|
|
10130
|
+
const pkgPath = join8(projectDir, "package.json");
|
|
10131
|
+
if (!existsSync7(pkgPath)) {
|
|
8557
10132
|
return /* @__PURE__ */ new Set();
|
|
8558
10133
|
}
|
|
8559
|
-
const pkg = JSON.parse(await
|
|
10134
|
+
const pkg = JSON.parse(await readFile8(pkgPath, "utf-8"));
|
|
8560
10135
|
return /* @__PURE__ */ new Set([
|
|
8561
10136
|
...Object.keys(pkg.dependencies || {}),
|
|
8562
10137
|
...Object.keys(pkg.devDependencies || {})
|
|
@@ -8592,11 +10167,12 @@ function formatGatingReason(result) {
|
|
|
8592
10167
|
return `Missing: ${parts.join("; ")}`;
|
|
8593
10168
|
}
|
|
8594
10169
|
|
|
8595
|
-
// src/
|
|
8596
|
-
var
|
|
10170
|
+
// src/skills/built-in/skill-review.ts
|
|
10171
|
+
var SkillReviewSkill = class extends BaseSkill {
|
|
8597
10172
|
name = "skill-review";
|
|
8598
10173
|
description = "Applies installed skills from GitHub repositories to code review";
|
|
8599
10174
|
version = "1.0.0";
|
|
10175
|
+
author = "Trie Agent";
|
|
8600
10176
|
skills = [];
|
|
8601
10177
|
loadedSkillContent = /* @__PURE__ */ new Map();
|
|
8602
10178
|
filteredSkills = /* @__PURE__ */ new Map();
|
|
@@ -8799,11 +10375,13 @@ ${parsed.rawContent}`);
|
|
|
8799
10375
|
}
|
|
8800
10376
|
};
|
|
8801
10377
|
|
|
8802
|
-
// src/
|
|
8803
|
-
var CustomSkill = class extends
|
|
10378
|
+
// src/skills/built-in/custom-skill.ts
|
|
10379
|
+
var CustomSkill = class extends BaseSkill {
|
|
8804
10380
|
name;
|
|
8805
10381
|
description;
|
|
8806
10382
|
version;
|
|
10383
|
+
author = void 0;
|
|
10384
|
+
authorUrl = void 0;
|
|
8807
10385
|
config;
|
|
8808
10386
|
constructor(config) {
|
|
8809
10387
|
super();
|
|
@@ -8811,6 +10389,14 @@ var CustomSkill = class extends BaseAgent {
|
|
|
8811
10389
|
this.name = config.name;
|
|
8812
10390
|
this.description = config.description;
|
|
8813
10391
|
this.version = config.version;
|
|
10392
|
+
const authorAttr = config.author;
|
|
10393
|
+
if (authorAttr !== void 0) {
|
|
10394
|
+
this.author = authorAttr;
|
|
10395
|
+
}
|
|
10396
|
+
const authorUrlAttr = config.authorUrl;
|
|
10397
|
+
if (authorUrlAttr !== void 0) {
|
|
10398
|
+
this.authorUrl = authorUrlAttr;
|
|
10399
|
+
}
|
|
8814
10400
|
}
|
|
8815
10401
|
get priority() {
|
|
8816
10402
|
return {
|
|
@@ -9006,72 +10592,75 @@ var CustomSkill = class extends BaseAgent {
|
|
|
9006
10592
|
}
|
|
9007
10593
|
};
|
|
9008
10594
|
|
|
9009
|
-
// src/
|
|
9010
|
-
import { readdir, readFile as
|
|
9011
|
-
import { join as
|
|
9012
|
-
var
|
|
9013
|
-
|
|
9014
|
-
|
|
10595
|
+
// src/skills/built-in/registry.ts
|
|
10596
|
+
import { readdir as readdir4, readFile as readFile9 } from "fs/promises";
|
|
10597
|
+
import { join as join9 } from "path";
|
|
10598
|
+
var SkillRegistryImpl = class {
|
|
10599
|
+
skills = /* @__PURE__ */ new Map();
|
|
10600
|
+
customSkillsLoaded = false;
|
|
9015
10601
|
initialized = false;
|
|
9016
10602
|
constructor() {
|
|
9017
|
-
this.
|
|
10603
|
+
this.registerBuiltinSkills();
|
|
9018
10604
|
}
|
|
9019
|
-
|
|
10605
|
+
registerBuiltinSkills() {
|
|
9020
10606
|
if (this.initialized) return;
|
|
9021
10607
|
this.initialized = true;
|
|
9022
|
-
const
|
|
9023
|
-
// Core
|
|
9024
|
-
new
|
|
9025
|
-
new
|
|
9026
|
-
new
|
|
9027
|
-
new
|
|
9028
|
-
// Specialized
|
|
9029
|
-
new
|
|
9030
|
-
new
|
|
9031
|
-
new
|
|
9032
|
-
new
|
|
9033
|
-
new
|
|
9034
|
-
new
|
|
9035
|
-
new
|
|
9036
|
-
new
|
|
9037
|
-
new
|
|
9038
|
-
new
|
|
9039
|
-
new
|
|
9040
|
-
new
|
|
9041
|
-
// New
|
|
9042
|
-
new
|
|
9043
|
-
new
|
|
9044
|
-
new
|
|
9045
|
-
new
|
|
9046
|
-
// Cost analysis
|
|
9047
|
-
new
|
|
10608
|
+
const builtinSkills = [
|
|
10609
|
+
// Core skills (always available)
|
|
10610
|
+
new SecuritySkill(),
|
|
10611
|
+
new PrivacySkill(),
|
|
10612
|
+
new TypeCheckSkill(),
|
|
10613
|
+
new ComprehensionSkill(),
|
|
10614
|
+
// Specialized skills
|
|
10615
|
+
new AccessibilitySkill(),
|
|
10616
|
+
new DesignEngineerSkill(),
|
|
10617
|
+
new LegalSkill(),
|
|
10618
|
+
new TestSkill(),
|
|
10619
|
+
new SoftwareArchitectSkill(),
|
|
10620
|
+
new DevOpsSkill(),
|
|
10621
|
+
new BugFindingSkill(),
|
|
10622
|
+
new UserTestingSkill(),
|
|
10623
|
+
new TrieCleanSkill(),
|
|
10624
|
+
new SOC2Skill(),
|
|
10625
|
+
new SuperReviewerSkill(),
|
|
10626
|
+
new AgentSmithSkill(),
|
|
10627
|
+
// New skills (inspired by Turkey Build)
|
|
10628
|
+
new PerformanceSkill(),
|
|
10629
|
+
new E2ESkill(),
|
|
10630
|
+
new VisualQASkill(),
|
|
10631
|
+
new DataFlowSkill(),
|
|
10632
|
+
// Cost analysis skill
|
|
10633
|
+
new MoneybagSkill(),
|
|
9048
10634
|
// Production readiness gate
|
|
9049
|
-
new
|
|
9050
|
-
// Skill review
|
|
9051
|
-
new
|
|
10635
|
+
new ProductionReadySkill(),
|
|
10636
|
+
// Skill review skill (applies external skills)
|
|
10637
|
+
new SkillReviewSkill()
|
|
9052
10638
|
];
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
10639
|
+
const isWorker = typeof process !== "undefined" && (process.env.TRIE_WORKER === "true" || process.env.NODE_ENV === "test");
|
|
10640
|
+
if (!isWorker && !process.env.TRIE_QUIET) {
|
|
10641
|
+
console.error(`Loaded config for ${builtinSkills.length} built-in skills`);
|
|
10642
|
+
}
|
|
10643
|
+
for (const skill of builtinSkills) {
|
|
10644
|
+
this.skills.set(skill.name, skill);
|
|
9056
10645
|
}
|
|
9057
10646
|
}
|
|
9058
10647
|
/**
|
|
9059
10648
|
* Load custom skills from .trie/agents/ directory
|
|
9060
10649
|
*/
|
|
9061
10650
|
async loadCustomSkills() {
|
|
9062
|
-
if (this.
|
|
10651
|
+
if (this.customSkillsLoaded) return;
|
|
9063
10652
|
try {
|
|
9064
|
-
const skillsDir =
|
|
9065
|
-
const files = await
|
|
10653
|
+
const skillsDir = join9(getWorkingDirectory(void 0, true), ".trie", "agents");
|
|
10654
|
+
const files = await readdir4(skillsDir);
|
|
9066
10655
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
9067
10656
|
let loadedCount = 0;
|
|
9068
10657
|
for (const file of jsonFiles) {
|
|
9069
10658
|
try {
|
|
9070
|
-
const configPath =
|
|
9071
|
-
const content = await
|
|
10659
|
+
const configPath = join9(skillsDir, file);
|
|
10660
|
+
const content = await readFile9(configPath, "utf-8");
|
|
9072
10661
|
const config = JSON.parse(content);
|
|
9073
10662
|
const skill = new CustomSkill(config);
|
|
9074
|
-
this.
|
|
10663
|
+
this.skills.set(skill.name, skill);
|
|
9075
10664
|
loadedCount++;
|
|
9076
10665
|
} catch (error) {
|
|
9077
10666
|
console.error(`Failed to load custom skill from ${file}:`, error);
|
|
@@ -9080,22 +10669,22 @@ var AgentRegistryImpl = class {
|
|
|
9080
10669
|
if (loadedCount > 0) {
|
|
9081
10670
|
console.error(`Loaded ${loadedCount} custom skill(s) from .trie/agents/`);
|
|
9082
10671
|
}
|
|
9083
|
-
this.
|
|
10672
|
+
this.customSkillsLoaded = true;
|
|
9084
10673
|
} catch (error) {
|
|
9085
|
-
this.
|
|
10674
|
+
this.customSkillsLoaded = true;
|
|
9086
10675
|
}
|
|
9087
|
-
await this.
|
|
10676
|
+
await this.loadSkillsForSkill();
|
|
9088
10677
|
}
|
|
9089
10678
|
// Backward compatibility alias
|
|
9090
10679
|
async loadCustomAgents() {
|
|
9091
10680
|
return this.loadCustomSkills();
|
|
9092
10681
|
}
|
|
9093
10682
|
/**
|
|
9094
|
-
* Load installed skills for the
|
|
10683
|
+
* Load installed skills for the SkillReviewSkill
|
|
9095
10684
|
*/
|
|
9096
|
-
async
|
|
9097
|
-
const skillReviewAgent = this.
|
|
9098
|
-
if (skillReviewAgent && skillReviewAgent instanceof
|
|
10685
|
+
async loadSkillsForSkill() {
|
|
10686
|
+
const skillReviewAgent = this.skills.get("skill-review");
|
|
10687
|
+
if (skillReviewAgent && skillReviewAgent instanceof SkillReviewSkill) {
|
|
9099
10688
|
await skillReviewAgent.loadSkills();
|
|
9100
10689
|
if (skillReviewAgent.hasSkills()) {
|
|
9101
10690
|
console.error(`Loaded ${skillReviewAgent.getAppliedSkillNames().length} skill(s) for skill-review agent`);
|
|
@@ -9106,40 +10695,56 @@ var AgentRegistryImpl = class {
|
|
|
9106
10695
|
* Reload custom skills (useful after creating new ones)
|
|
9107
10696
|
*/
|
|
9108
10697
|
async reloadCustomSkills() {
|
|
9109
|
-
for (const [name, agent] of this.
|
|
10698
|
+
for (const [name, agent] of this.skills.entries()) {
|
|
9110
10699
|
if (agent instanceof CustomSkill) {
|
|
9111
|
-
this.
|
|
10700
|
+
this.skills.delete(name);
|
|
9112
10701
|
}
|
|
9113
10702
|
}
|
|
9114
|
-
this.
|
|
10703
|
+
this.customSkillsLoaded = false;
|
|
9115
10704
|
await this.loadCustomSkills();
|
|
9116
10705
|
}
|
|
9117
10706
|
// Backward compatibility alias
|
|
9118
10707
|
async reloadCustomAgents() {
|
|
9119
10708
|
return this.reloadCustomSkills();
|
|
9120
10709
|
}
|
|
10710
|
+
getSkill(name) {
|
|
10711
|
+
return this.skills.get(name);
|
|
10712
|
+
}
|
|
10713
|
+
// Backward compatibility alias
|
|
9121
10714
|
getAgent(name) {
|
|
9122
|
-
return this.
|
|
10715
|
+
return this.getSkill(name);
|
|
9123
10716
|
}
|
|
10717
|
+
getSkillsByNames(names) {
|
|
10718
|
+
return names.map((name) => this.getSkill(name)).filter((skill) => skill !== void 0);
|
|
10719
|
+
}
|
|
10720
|
+
// Backward compatibility alias
|
|
9124
10721
|
getAgentsByNames(names) {
|
|
9125
|
-
return
|
|
10722
|
+
return this.getSkillsByNames(names);
|
|
9126
10723
|
}
|
|
10724
|
+
getAllSkills() {
|
|
10725
|
+
return Array.from(this.skills.values());
|
|
10726
|
+
}
|
|
10727
|
+
// Backward compatibility alias
|
|
9127
10728
|
getAllAgents() {
|
|
9128
|
-
return
|
|
10729
|
+
return this.getAllSkills();
|
|
9129
10730
|
}
|
|
9130
10731
|
/**
|
|
9131
|
-
* Get only built-in
|
|
10732
|
+
* Get only built-in skills
|
|
9132
10733
|
*/
|
|
9133
|
-
|
|
9134
|
-
return Array.from(this.
|
|
9135
|
-
(
|
|
10734
|
+
getBuiltinSkills() {
|
|
10735
|
+
return Array.from(this.skills.values()).filter(
|
|
10736
|
+
(skill) => !(skill instanceof CustomSkill)
|
|
9136
10737
|
);
|
|
9137
10738
|
}
|
|
10739
|
+
// Backward compatibility alias
|
|
10740
|
+
getBuiltinAgents() {
|
|
10741
|
+
return this.getBuiltinSkills();
|
|
10742
|
+
}
|
|
9138
10743
|
/**
|
|
9139
10744
|
* Get only custom skills
|
|
9140
10745
|
*/
|
|
9141
10746
|
getCustomSkills() {
|
|
9142
|
-
return Array.from(this.
|
|
10747
|
+
return Array.from(this.skills.values()).filter(
|
|
9143
10748
|
(agent) => agent instanceof CustomSkill
|
|
9144
10749
|
);
|
|
9145
10750
|
}
|
|
@@ -9147,32 +10752,40 @@ var AgentRegistryImpl = class {
|
|
|
9147
10752
|
getCustomAgents() {
|
|
9148
10753
|
return this.getCustomSkills();
|
|
9149
10754
|
}
|
|
9150
|
-
|
|
9151
|
-
this.
|
|
9152
|
-
console.error(`Registered custom skill: ${
|
|
10755
|
+
registerSkill(skill) {
|
|
10756
|
+
this.skills.set(skill.name, skill);
|
|
10757
|
+
console.error(`Registered custom skill: ${skill.name}`);
|
|
10758
|
+
}
|
|
10759
|
+
// Backward compatibility alias
|
|
10760
|
+
registerAgent(skill) {
|
|
10761
|
+
this.registerSkill(skill);
|
|
9153
10762
|
}
|
|
9154
10763
|
/**
|
|
9155
|
-
* Unregister
|
|
10764
|
+
* Unregister a skill by name
|
|
9156
10765
|
*/
|
|
10766
|
+
unregisterSkill(name) {
|
|
10767
|
+
return this.skills.delete(name);
|
|
10768
|
+
}
|
|
10769
|
+
// Backward compatibility alias
|
|
9157
10770
|
unregisterAgent(name) {
|
|
9158
|
-
return this.
|
|
10771
|
+
return this.unregisterSkill(name);
|
|
9159
10772
|
}
|
|
9160
|
-
|
|
9161
|
-
return Array.from(this.
|
|
10773
|
+
getSkillNames() {
|
|
10774
|
+
return Array.from(this.skills.keys());
|
|
9162
10775
|
}
|
|
9163
|
-
|
|
9164
|
-
return Array.from(this.
|
|
9165
|
-
name:
|
|
9166
|
-
description:
|
|
9167
|
-
isCustom:
|
|
10776
|
+
getSkillDescriptions() {
|
|
10777
|
+
return Array.from(this.skills.values()).map((skill) => ({
|
|
10778
|
+
name: skill.name,
|
|
10779
|
+
description: skill.description,
|
|
10780
|
+
isCustom: skill instanceof CustomSkill
|
|
9168
10781
|
}));
|
|
9169
10782
|
}
|
|
9170
10783
|
/**
|
|
9171
|
-
* Check if
|
|
10784
|
+
* Check if a skill is custom
|
|
9172
10785
|
*/
|
|
9173
10786
|
isCustomSkill(name) {
|
|
9174
|
-
const
|
|
9175
|
-
return
|
|
10787
|
+
const skill = this.skills.get(name);
|
|
10788
|
+
return skill instanceof CustomSkill;
|
|
9176
10789
|
}
|
|
9177
10790
|
// Backward compatibility alias
|
|
9178
10791
|
isCustomAgent(name) {
|
|
@@ -9182,9 +10795,9 @@ var AgentRegistryImpl = class {
|
|
|
9182
10795
|
* Get custom skill metadata
|
|
9183
10796
|
*/
|
|
9184
10797
|
getCustomSkillMetadata(name) {
|
|
9185
|
-
const
|
|
9186
|
-
if (
|
|
9187
|
-
return
|
|
10798
|
+
const skill = this.skills.get(name);
|
|
10799
|
+
if (skill instanceof CustomSkill) {
|
|
10800
|
+
return skill.getMetadata();
|
|
9188
10801
|
}
|
|
9189
10802
|
return null;
|
|
9190
10803
|
}
|
|
@@ -9193,19 +10806,49 @@ var AgentRegistryImpl = class {
|
|
|
9193
10806
|
return this.getCustomSkillMetadata(name);
|
|
9194
10807
|
}
|
|
9195
10808
|
};
|
|
9196
|
-
var SINGLETON_KEY = "
|
|
9197
|
-
|
|
10809
|
+
var SINGLETON_KEY = "__TRIE_SKILL_REGISTRY__";
|
|
10810
|
+
var LEGACY_SINGLETON_KEY = "__TRIE_AGENT_REGISTRY__";
|
|
10811
|
+
function getSkillRegistry() {
|
|
9198
10812
|
const global = globalThis;
|
|
9199
10813
|
if (!global[SINGLETON_KEY]) {
|
|
9200
|
-
global[
|
|
10814
|
+
const existing = global[LEGACY_SINGLETON_KEY];
|
|
10815
|
+
global[SINGLETON_KEY] = existing ?? new SkillRegistryImpl();
|
|
10816
|
+
global[LEGACY_SINGLETON_KEY] = global[SINGLETON_KEY];
|
|
9201
10817
|
}
|
|
9202
10818
|
return global[SINGLETON_KEY];
|
|
9203
10819
|
}
|
|
9204
10820
|
|
|
9205
10821
|
export {
|
|
9206
|
-
|
|
10822
|
+
SuperReviewerSkill,
|
|
9207
10823
|
CRITICAL_REVIEW_CHECKLIST,
|
|
10824
|
+
installSkill,
|
|
10825
|
+
listInstalledSkills,
|
|
10826
|
+
removeSkill,
|
|
10827
|
+
projectInfoExists,
|
|
10828
|
+
loadProjectInfo,
|
|
10829
|
+
initProjectInfo,
|
|
10830
|
+
getProjectSection,
|
|
10831
|
+
updateProjectSection,
|
|
10832
|
+
appendToSection,
|
|
10833
|
+
getProjectSections,
|
|
10834
|
+
getProjectInfoStructured,
|
|
10835
|
+
getHistoricalInsights,
|
|
10836
|
+
searchIssues,
|
|
10837
|
+
findSimilarIssues,
|
|
10838
|
+
markIssueResolved,
|
|
10839
|
+
getMemoryStats,
|
|
10840
|
+
getRecentIssues,
|
|
10841
|
+
getDailyLogs,
|
|
10842
|
+
findCrossProjectPatterns,
|
|
10843
|
+
listTrackedProjects,
|
|
10844
|
+
getGlobalMemoryStats,
|
|
10845
|
+
updateGlobalMemoryMd,
|
|
10846
|
+
searchGlobalPatterns,
|
|
10847
|
+
loadContextState,
|
|
10848
|
+
updateContextAfterScan,
|
|
10849
|
+
recordSkillInstalled,
|
|
10850
|
+
getContextForAI,
|
|
9208
10851
|
CustomSkill,
|
|
9209
|
-
|
|
10852
|
+
getSkillRegistry
|
|
9210
10853
|
};
|
|
9211
|
-
//# sourceMappingURL=chunk-
|
|
10854
|
+
//# sourceMappingURL=chunk-TOE75CFZ.js.map
|