@triedotdev/mcp 1.0.93 → 1.0.97

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.
Files changed (84) hide show
  1. package/README.md +145 -137
  2. package/dist/chunk-APMV77PU.js +313 -0
  3. package/dist/chunk-APMV77PU.js.map +1 -0
  4. package/dist/{chunk-MZI46HQT.js → chunk-B3MNN3XB.js} +13 -18
  5. package/dist/{chunk-MZI46HQT.js.map → chunk-B3MNN3XB.js.map} +1 -1
  6. package/dist/{chunk-5Z7O66DE.js → chunk-F4NJ4CBP.js} +2 -2
  7. package/dist/{chunk-YTJXD664.js → chunk-FNCCZ3XB.js} +1222 -75
  8. package/dist/chunk-FNCCZ3XB.js.map +1 -0
  9. package/dist/chunk-G76DYVGX.js +136 -0
  10. package/dist/chunk-G76DYVGX.js.map +1 -0
  11. package/dist/chunk-HSNE46VE.js +956 -0
  12. package/dist/chunk-HSNE46VE.js.map +1 -0
  13. package/dist/{chunk-LVVG2DMW.js → chunk-IXO4G4D3.js} +2 -2
  14. package/dist/{chunk-LP4MVJDW.js → chunk-JDHR5BDR.js} +2 -3
  15. package/dist/chunk-NIASHOAB.js +1304 -0
  16. package/dist/chunk-NIASHOAB.js.map +1 -0
  17. package/dist/{chunk-NMGINYYX.js → chunk-OVRG5RP3.js} +6 -7
  18. package/dist/chunk-OVRG5RP3.js.map +1 -0
  19. package/dist/{chunk-T5UOH56R.js → chunk-R3I2GCZC.js} +3 -3
  20. package/dist/{chunk-RDOJCRKJ.js → chunk-R4AAPFXC.js} +2 -2
  21. package/dist/{chunk-R6AUYN3R.js → chunk-SLL2MDJD.js} +786 -4668
  22. package/dist/chunk-SLL2MDJD.js.map +1 -0
  23. package/dist/cli/create-agent.js +931 -7
  24. package/dist/cli/create-agent.js.map +1 -1
  25. package/dist/cli/main.js +151 -383
  26. package/dist/cli/main.js.map +1 -1
  27. package/dist/cli/yolo-daemon.js +13 -20
  28. package/dist/cli/yolo-daemon.js.map +1 -1
  29. package/dist/{goal-manager-ESJCJXFS.js → goal-manager-LAOT4QQX.js} +6 -6
  30. package/dist/guardian-agent-M352CBE5.js +19 -0
  31. package/dist/index.js +1025 -1550
  32. package/dist/index.js.map +1 -1
  33. package/dist/{issue-store-JZ2LCQEG.js → issue-store-W2X33X2X.js} +4 -4
  34. package/dist/{progress-PH6NNWZM.js → progress-PQVEM7BR.js} +2 -2
  35. package/dist/{vibe-code-signatures-K4UIWKJZ.js → vibe-code-signatures-ELEWJFGZ.js} +3 -3
  36. package/dist/{vulnerability-signatures-ZKVLMBRG.js → vulnerability-signatures-EIJQX2TS.js} +3 -3
  37. package/dist/workers/agent-worker.js +2 -11
  38. package/dist/workers/agent-worker.js.map +1 -1
  39. package/package.json +2 -2
  40. package/dist/agent-smith-QYDXPFPJ.js +0 -14
  41. package/dist/agent-smith-runner-GXGDJTSR.js +0 -573
  42. package/dist/agent-smith-runner-GXGDJTSR.js.map +0 -1
  43. package/dist/cache-manager-7SKX3IGO.js +0 -10
  44. package/dist/chunk-74NPKTZV.js +0 -141
  45. package/dist/chunk-74NPKTZV.js.map +0 -1
  46. package/dist/chunk-BG2BHWCC.js +0 -10879
  47. package/dist/chunk-BG2BHWCC.js.map +0 -1
  48. package/dist/chunk-CUXXRM3T.js +0 -2124
  49. package/dist/chunk-CUXXRM3T.js.map +0 -1
  50. package/dist/chunk-D25EIBPO.js +0 -183
  51. package/dist/chunk-D25EIBPO.js.map +0 -1
  52. package/dist/chunk-F55XBLIA.js +0 -536
  53. package/dist/chunk-F55XBLIA.js.map +0 -1
  54. package/dist/chunk-HFQ5ORON.js +0 -279
  55. package/dist/chunk-HFQ5ORON.js.map +0 -1
  56. package/dist/chunk-IOUOVBJZ.js +0 -175
  57. package/dist/chunk-IOUOVBJZ.js.map +0 -1
  58. package/dist/chunk-KWDNYWOR.js +0 -2270
  59. package/dist/chunk-KWDNYWOR.js.map +0 -1
  60. package/dist/chunk-LT7MKIXU.js +0 -266
  61. package/dist/chunk-LT7MKIXU.js.map +0 -1
  62. package/dist/chunk-MURGTWG4.js +0 -279
  63. package/dist/chunk-MURGTWG4.js.map +0 -1
  64. package/dist/chunk-NMGINYYX.js.map +0 -1
  65. package/dist/chunk-R6AUYN3R.js.map +0 -1
  66. package/dist/chunk-SJFJ6GLR.js +0 -955
  67. package/dist/chunk-SJFJ6GLR.js.map +0 -1
  68. package/dist/chunk-YTJXD664.js.map +0 -1
  69. package/dist/git-PZV3BBYI.js +0 -29
  70. package/dist/guardian-agent-ZHJXLBOU.js +0 -21
  71. package/dist/progress-PH6NNWZM.js.map +0 -1
  72. package/dist/vibe-code-signatures-K4UIWKJZ.js.map +0 -1
  73. package/dist/vulnerability-signatures-ZKVLMBRG.js.map +0 -1
  74. /package/dist/{chunk-5Z7O66DE.js.map → chunk-F4NJ4CBP.js.map} +0 -0
  75. /package/dist/{chunk-LVVG2DMW.js.map → chunk-IXO4G4D3.js.map} +0 -0
  76. /package/dist/{chunk-LP4MVJDW.js.map → chunk-JDHR5BDR.js.map} +0 -0
  77. /package/dist/{chunk-T5UOH56R.js.map → chunk-R3I2GCZC.js.map} +0 -0
  78. /package/dist/{chunk-RDOJCRKJ.js.map → chunk-R4AAPFXC.js.map} +0 -0
  79. /package/dist/{agent-smith-QYDXPFPJ.js.map → goal-manager-LAOT4QQX.js.map} +0 -0
  80. /package/dist/{cache-manager-7SKX3IGO.js.map → guardian-agent-M352CBE5.js.map} +0 -0
  81. /package/dist/{git-PZV3BBYI.js.map → issue-store-W2X33X2X.js.map} +0 -0
  82. /package/dist/{goal-manager-ESJCJXFS.js.map → progress-PQVEM7BR.js.map} +0 -0
  83. /package/dist/{guardian-agent-ZHJXLBOU.js.map → vibe-code-signatures-ELEWJFGZ.js.map} +0 -0
  84. /package/dist/{issue-store-JZ2LCQEG.js.map → vulnerability-signatures-EIJQX2TS.js.map} +0 -0
@@ -1,266 +0,0 @@
1
- import {
2
- BackupManager,
3
- GlobalPatternsIndexSchema,
4
- atomicWriteJSON,
5
- safeParseAndValidate
6
- } from "./chunk-LP4MVJDW.js";
7
-
8
- // src/memory/global-memory.ts
9
- import { mkdir, writeFile, readFile, readdir } from "fs/promises";
10
- import { createHash } from "crypto";
11
- import { existsSync } from "fs";
12
- import { join } from "path";
13
- import { homedir } from "os";
14
- var GLOBAL_TRIE_DIR = join(homedir(), ".trie");
15
- var GLOBAL_MEMORY_DIR = join(GLOBAL_TRIE_DIR, "memory");
16
- async function recordToGlobalMemory(issues, projectName, projectPath, healthScore = 0) {
17
- await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });
18
- await mkdir(join(GLOBAL_MEMORY_DIR, "projects"), { recursive: true });
19
- const patterns = await loadGlobalPatterns();
20
- const now = (/* @__PURE__ */ new Date()).toISOString();
21
- for (const issue of issues) {
22
- const patternId = extractPatternId(issue);
23
- const existing = patterns.find((p) => p.id === patternId);
24
- if (existing) {
25
- existing.occurrences++;
26
- existing.lastSeen = now;
27
- if (!existing.projects.includes(projectName)) {
28
- existing.projects.push(projectName);
29
- }
30
- } else {
31
- patterns.push({
32
- id: patternId,
33
- pattern: issue.issue.slice(0, 200),
34
- description: issue.fix.slice(0, 200),
35
- severity: issue.severity,
36
- agent: issue.agent,
37
- occurrences: 1,
38
- projects: [projectName],
39
- firstSeen: now,
40
- lastSeen: now
41
- });
42
- }
43
- }
44
- await saveGlobalPatterns(patterns);
45
- const summaryPath = join(GLOBAL_MEMORY_DIR, "projects", `${sanitizeName(projectName)}.json`);
46
- const summary = {
47
- name: projectName,
48
- path: projectPath,
49
- lastScan: now,
50
- healthScore,
51
- totalIssues: issues.length,
52
- patterns: [...new Set(issues.map((i) => extractPatternId(i)))]
53
- };
54
- await atomicWriteJSON(summaryPath, summary);
55
- }
56
- async function findCrossProjectPatterns(minOccurrences = 2) {
57
- const patterns = await loadGlobalPatterns();
58
- return patterns.filter((p) => p.projects.length >= minOccurrences).sort((a, b) => b.occurrences - a.occurrences);
59
- }
60
- async function listTrackedProjects() {
61
- const projectsDir = join(GLOBAL_MEMORY_DIR, "projects");
62
- try {
63
- if (!existsSync(projectsDir)) return [];
64
- const files = await readdir(projectsDir);
65
- const summaries = [];
66
- for (const file of files) {
67
- if (!file.endsWith(".json")) continue;
68
- try {
69
- const content = await readFile(join(projectsDir, file), "utf-8");
70
- summaries.push(JSON.parse(content));
71
- } catch {
72
- }
73
- }
74
- return summaries.sort(
75
- (a, b) => new Date(b.lastScan).getTime() - new Date(a.lastScan).getTime()
76
- );
77
- } catch {
78
- return [];
79
- }
80
- }
81
- async function getGlobalMemoryStats() {
82
- const patterns = await loadGlobalPatterns();
83
- const projects = await listTrackedProjects();
84
- const MAX_PATTERNS = 500;
85
- const patternsByAgent = {};
86
- for (const pattern of patterns) {
87
- patternsByAgent[pattern.agent] = (patternsByAgent[pattern.agent] || 0) + 1;
88
- }
89
- const totalOccurrences = patterns.reduce((sum, p) => sum + p.occurrences, 0);
90
- const avgOccurrences = patterns.length > 0 ? totalOccurrences / patterns.length : 0;
91
- return {
92
- totalPatterns: patterns.length,
93
- crossProjectPatterns: patterns.filter((p) => p.projects.length >= 2).length,
94
- trackedProjects: projects.length,
95
- totalOccurrences,
96
- fixedPatterns: patterns.filter((p) => p.fixApplied).length,
97
- patternsByAgent,
98
- capacityInfo: {
99
- current: patterns.length,
100
- max: MAX_PATTERNS,
101
- percentFull: Math.round(patterns.length / MAX_PATTERNS * 100),
102
- isAtCap: patterns.length >= MAX_PATTERNS
103
- },
104
- deduplicationStats: {
105
- uniquePatterns: patterns.length,
106
- averageOccurrences: Math.round(avgOccurrences * 10) / 10
107
- }
108
- };
109
- }
110
- async function updateGlobalMemoryMd() {
111
- const patterns = await loadGlobalPatterns();
112
- const crossProject = patterns.filter((p) => p.projects.length >= 2);
113
- const projects = await listTrackedProjects();
114
- const lines = [
115
- "# Global Trie Memory",
116
- "",
117
- "> Auto-generated file tracking patterns across all your projects.",
118
- `> Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
119
- "",
120
- "## Summary",
121
- "",
122
- `- **Projects tracked:** ${projects.length}`,
123
- `- **Total patterns:** ${patterns.length}`,
124
- `- **Cross-project patterns:** ${crossProject.length}`,
125
- "",
126
- "## Cross-Project Patterns",
127
- "",
128
- "These issues appear in multiple projects:",
129
- ""
130
- ];
131
- for (const p of crossProject.slice(0, 20)) {
132
- lines.push(
133
- `### ${p.pattern.slice(0, 60)}${p.pattern.length > 60 ? "..." : ""}`,
134
- "",
135
- `- **Severity:** ${p.severity}`,
136
- `- **Agent:** ${p.agent}`,
137
- `- **Occurrences:** ${p.occurrences} across ${p.projects.length} projects`,
138
- `- **Projects:** ${p.projects.slice(0, 5).join(", ")}${p.projects.length > 5 ? "..." : ""}`
139
- );
140
- if (p.fixApplied) {
141
- lines.push(`- **Fixed in:** ${p.fixApplied.project} on ${p.fixApplied.timestamp.split("T")[0]}`);
142
- } else {
143
- lines.push("- **Status:** Not fixed");
144
- }
145
- lines.push("");
146
- }
147
- lines.push(
148
- "## Tracked Projects",
149
- "",
150
- "| Project | Last Scan | Health | Issues |",
151
- "|---------|-----------|--------|--------|"
152
- );
153
- for (const p of projects.slice(0, 20)) {
154
- lines.push(`| ${p.name} | ${p.lastScan.split("T")[0]} | ${p.healthScore}% | ${p.totalIssues} |`);
155
- }
156
- lines.push("", "---", "", "*This file is auto-generated by Trie. Do not edit manually.*");
157
- await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });
158
- await writeFile(join(GLOBAL_MEMORY_DIR, "GLOBAL_MEMORY.md"), lines.join("\n"));
159
- }
160
- async function searchGlobalPatterns(query, options = {}) {
161
- const patterns = await loadGlobalPatterns();
162
- const limit = options.limit || 10;
163
- const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
164
- const scored = patterns.filter((p) => {
165
- if (options.severity && !options.severity.includes(p.severity)) return false;
166
- if (options.agent && p.agent !== options.agent) return false;
167
- return true;
168
- }).map((p) => {
169
- const text = `${p.pattern} ${p.description} ${p.agent}`.toLowerCase();
170
- let score = 0;
171
- for (const term of queryTerms) {
172
- if (text.includes(term)) score++;
173
- }
174
- return { pattern: p, score };
175
- }).filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
176
- return scored.map((s) => s.pattern);
177
- }
178
- async function loadGlobalPatterns() {
179
- const patternsPath = join(GLOBAL_MEMORY_DIR, "global-patterns.json");
180
- try {
181
- if (existsSync(patternsPath)) {
182
- const content = await readFile(patternsPath, "utf-8");
183
- const result = safeParseAndValidate(content, GlobalPatternsIndexSchema);
184
- if (result.success) {
185
- return result.data;
186
- }
187
- const backupManager = new BackupManager(patternsPath);
188
- if (await backupManager.recoverFromBackup()) {
189
- const recovered = await readFile(patternsPath, "utf-8");
190
- const recoveredResult = safeParseAndValidate(recovered, GlobalPatternsIndexSchema);
191
- if (recoveredResult.success) {
192
- return recoveredResult.data;
193
- }
194
- }
195
- }
196
- } catch {
197
- }
198
- return [];
199
- }
200
- async function saveGlobalPatterns(patterns) {
201
- await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });
202
- const patternsPath = join(GLOBAL_MEMORY_DIR, "global-patterns.json");
203
- const patternMap = /* @__PURE__ */ new Map();
204
- for (const pattern of patterns) {
205
- const existing = patternMap.get(pattern.id);
206
- if (existing) {
207
- existing.occurrences += pattern.occurrences;
208
- for (const proj of pattern.projects) {
209
- if (!existing.projects.includes(proj)) {
210
- existing.projects.push(proj);
211
- }
212
- }
213
- existing.lastSeen = pattern.lastSeen > existing.lastSeen ? pattern.lastSeen : existing.lastSeen;
214
- } else {
215
- patternMap.set(pattern.id, pattern);
216
- }
217
- }
218
- const deduplicated = Array.from(patternMap.values());
219
- const pruned = intelligentPruneGlobalPatterns(deduplicated, 500);
220
- const backupManager = new BackupManager(patternsPath);
221
- await backupManager.createBackup();
222
- await atomicWriteJSON(patternsPath, pruned);
223
- }
224
- function intelligentPruneGlobalPatterns(patterns, targetCount) {
225
- if (patterns.length <= targetCount) {
226
- return patterns;
227
- }
228
- const severityWeight = {
229
- critical: 100,
230
- high: 50,
231
- moderate: 20,
232
- low: 10,
233
- info: 5
234
- };
235
- const scored = patterns.map((pattern) => {
236
- const ageInDays = (Date.now() - new Date(pattern.lastSeen).getTime()) / (1e3 * 60 * 60 * 24);
237
- const recencyScore = Math.max(0, 100 - ageInDays * 2);
238
- const severityScore = severityWeight[pattern.severity] || 10;
239
- const crossProjectBonus = (pattern.projects.length - 1) * 30;
240
- const fixedBonus = pattern.fixApplied ? 20 : 0;
241
- const occurrenceScore = Math.min(pattern.occurrences * 2, 100);
242
- return {
243
- pattern,
244
- score: recencyScore + severityScore + crossProjectBonus + fixedBonus + occurrenceScore
245
- };
246
- });
247
- return scored.sort((a, b) => b.score - a.score).slice(0, targetCount).map((s) => s.pattern);
248
- }
249
- function extractPatternId(issue) {
250
- const normalized = issue.issue.toLowerCase().replace(/`[^`]+`/g, "CODE").replace(/\b\d+\b/g, "N").replace(/['"]/g, "").slice(0, 100);
251
- const hash = createHash("sha256").update(normalized).digest("hex").slice(0, 12);
252
- return `${issue.agent}-${issue.severity}-${hash}`;
253
- }
254
- function sanitizeName(name) {
255
- return name.replace(/[^a-zA-Z0-9-_]/g, "-").toLowerCase();
256
- }
257
-
258
- export {
259
- recordToGlobalMemory,
260
- findCrossProjectPatterns,
261
- listTrackedProjects,
262
- getGlobalMemoryStats,
263
- updateGlobalMemoryMd,
264
- searchGlobalPatterns
265
- };
266
- //# sourceMappingURL=chunk-LT7MKIXU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/memory/global-memory.ts"],"sourcesContent":["/**\n * Cross-Project Memory\n * \n * Stores and retrieves patterns across all projects.\n * Location: ~/.trie/memory/\n * \n * Phase 1 Hardening:\n * - SHA256 hashing for pattern IDs\n * - Atomic writes to prevent corruption\n * - Rotational backups for recovery\n * - Zod validation for data integrity\n */\n\nimport { mkdir, writeFile, readFile, readdir } from 'fs/promises';\nimport { createHash } from 'crypto';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport type { Issue } from '../types/index.js';\nimport { atomicWriteJSON } from '../utils/atomic-write.js';\nimport { BackupManager } from '../utils/backup-manager.js';\nimport { GlobalPatternsIndexSchema, safeParseAndValidate } from './validation.js';\n\nconst GLOBAL_TRIE_DIR = join(homedir(), '.trie');\nconst GLOBAL_MEMORY_DIR = join(GLOBAL_TRIE_DIR, 'memory');\n\nexport interface GlobalPattern {\n id: string;\n pattern: string;\n description: string;\n severity: string;\n agent: string;\n occurrences: number;\n projects: string[];\n firstSeen: string;\n lastSeen: string;\n fixApplied?: {\n project: string;\n timestamp: string;\n fix: string;\n };\n}\n\nexport interface ProjectSummary {\n name: string;\n path: string;\n lastScan: string;\n healthScore: number;\n totalIssues: number;\n patterns: string[];\n}\n\n/**\n * Record issues to global memory\n */\nexport async function recordToGlobalMemory(\n issues: Issue[],\n projectName: string,\n projectPath: string,\n healthScore: number = 0\n): Promise<void> {\n await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });\n await mkdir(join(GLOBAL_MEMORY_DIR, 'projects'), { recursive: true });\n\n const patterns = await loadGlobalPatterns();\n const now = new Date().toISOString();\n\n for (const issue of issues) {\n const patternId = extractPatternId(issue);\n const existing = patterns.find(p => p.id === patternId);\n\n if (existing) {\n existing.occurrences++;\n existing.lastSeen = now;\n if (!existing.projects.includes(projectName)) {\n existing.projects.push(projectName);\n }\n } else {\n patterns.push({\n id: patternId,\n pattern: issue.issue.slice(0, 200),\n description: issue.fix.slice(0, 200),\n severity: issue.severity,\n agent: issue.agent,\n occurrences: 1,\n projects: [projectName],\n firstSeen: now,\n lastSeen: now,\n });\n }\n }\n\n await saveGlobalPatterns(patterns);\n\n const summaryPath = join(GLOBAL_MEMORY_DIR, 'projects', `${sanitizeName(projectName)}.json`);\n const summary: ProjectSummary = {\n name: projectName,\n path: projectPath,\n lastScan: now,\n healthScore,\n totalIssues: issues.length,\n patterns: [...new Set(issues.map(i => extractPatternId(i)))],\n };\n \n // Use atomic write for project summary\n await atomicWriteJSON(summaryPath, summary);\n}\n\n/**\n * Find patterns that appear across multiple projects\n */\nexport async function findCrossProjectPatterns(\n minOccurrences: number = 2\n): Promise<GlobalPattern[]> {\n const patterns = await loadGlobalPatterns();\n return patterns\n .filter(p => p.projects.length >= minOccurrences)\n .sort((a, b) => b.occurrences - a.occurrences);\n}\n\n/**\n * Check if an issue has been fixed in another project\n */\nexport async function findFixFromOtherProjects(\n issue: Issue\n): Promise<GlobalPattern | null> {\n const patterns = await loadGlobalPatterns();\n const patternId = extractPatternId(issue);\n const pattern = patterns.find(p => p.id === patternId);\n\n if (pattern?.fixApplied) {\n return pattern;\n }\n\n return null;\n}\n\n/**\n * Record that a pattern was fixed\n */\nexport async function recordPatternFix(\n issue: Issue,\n projectName: string,\n fix: string\n): Promise<void> {\n const patterns = await loadGlobalPatterns();\n const patternId = extractPatternId(issue);\n const pattern = patterns.find(p => p.id === patternId);\n\n if (pattern && !pattern.fixApplied) {\n pattern.fixApplied = {\n project: projectName,\n timestamp: new Date().toISOString(),\n fix: fix.slice(0, 500),\n };\n await saveGlobalPatterns(patterns);\n }\n}\n\n/**\n * Get all tracked projects\n */\nexport async function listTrackedProjects(): Promise<ProjectSummary[]> {\n const projectsDir = join(GLOBAL_MEMORY_DIR, 'projects');\n \n try {\n if (!existsSync(projectsDir)) return [];\n \n const files = await readdir(projectsDir);\n const summaries: ProjectSummary[] = [];\n \n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n try {\n const content = await readFile(join(projectsDir, file), 'utf-8');\n summaries.push(JSON.parse(content));\n } catch {\n // Skip invalid files\n }\n }\n \n return summaries.sort((a, b) => \n new Date(b.lastScan).getTime() - new Date(a.lastScan).getTime()\n );\n } catch {\n return [];\n }\n}\n\n/**\n * Get global memory statistics\n */\nexport async function getGlobalMemoryStats(): Promise<{\n totalPatterns: number;\n crossProjectPatterns: number;\n trackedProjects: number;\n totalOccurrences: number;\n fixedPatterns: number;\n patternsByAgent: Record<string, number>;\n capacityInfo: {\n current: number;\n max: number;\n percentFull: number;\n isAtCap: boolean;\n };\n deduplicationStats: {\n uniquePatterns: number;\n averageOccurrences: number;\n };\n}> {\n const patterns = await loadGlobalPatterns();\n const projects = await listTrackedProjects();\n \n const MAX_PATTERNS = 500;\n \n // Count patterns by agent type\n const patternsByAgent: Record<string, number> = {};\n for (const pattern of patterns) {\n patternsByAgent[pattern.agent] = (patternsByAgent[pattern.agent] || 0) + 1;\n }\n \n const totalOccurrences = patterns.reduce((sum, p) => sum + p.occurrences, 0);\n const avgOccurrences = patterns.length > 0 ? totalOccurrences / patterns.length : 0;\n \n return {\n totalPatterns: patterns.length,\n crossProjectPatterns: patterns.filter(p => p.projects.length >= 2).length,\n trackedProjects: projects.length,\n totalOccurrences,\n fixedPatterns: patterns.filter(p => p.fixApplied).length,\n patternsByAgent,\n capacityInfo: {\n current: patterns.length,\n max: MAX_PATTERNS,\n percentFull: Math.round((patterns.length / MAX_PATTERNS) * 100),\n isAtCap: patterns.length >= MAX_PATTERNS,\n },\n deduplicationStats: {\n uniquePatterns: patterns.length,\n averageOccurrences: Math.round(avgOccurrences * 10) / 10,\n },\n };\n}\n\n/**\n * Update GLOBAL_MEMORY.md with current patterns\n */\nexport async function updateGlobalMemoryMd(): Promise<void> {\n const patterns = await loadGlobalPatterns();\n const crossProject = patterns.filter(p => p.projects.length >= 2);\n const projects = await listTrackedProjects();\n\n const lines: string[] = [\n '# Global Trie Memory',\n '',\n '> Auto-generated file tracking patterns across all your projects.',\n `> Last updated: ${new Date().toISOString()}`,\n '',\n '## Summary',\n '',\n `- **Projects tracked:** ${projects.length}`,\n `- **Total patterns:** ${patterns.length}`,\n `- **Cross-project patterns:** ${crossProject.length}`,\n '',\n '## Cross-Project Patterns',\n '',\n 'These issues appear in multiple projects:',\n '',\n ];\n \n for (const p of crossProject.slice(0, 20)) {\n lines.push(\n `### ${p.pattern.slice(0, 60)}${p.pattern.length > 60 ? '...' : ''}`,\n '',\n `- **Severity:** ${p.severity}`,\n `- **Agent:** ${p.agent}`,\n `- **Occurrences:** ${p.occurrences} across ${p.projects.length} projects`,\n `- **Projects:** ${p.projects.slice(0, 5).join(', ')}${p.projects.length > 5 ? '...' : ''}`,\n );\n if (p.fixApplied) {\n lines.push(`- **Fixed in:** ${p.fixApplied.project} on ${p.fixApplied.timestamp.split('T')[0]}`);\n } else {\n lines.push('- **Status:** Not fixed');\n }\n lines.push('');\n }\n\n lines.push(\n '## Tracked Projects',\n '',\n '| Project | Last Scan | Health | Issues |',\n '|---------|-----------|--------|--------|',\n );\n \n for (const p of projects.slice(0, 20)) {\n lines.push(`| ${p.name} | ${p.lastScan.split('T')[0]} | ${p.healthScore}% | ${p.totalIssues} |`);\n }\n\n lines.push('', '---', '', '*This file is auto-generated by Trie. Do not edit manually.*');\n\n await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });\n await writeFile(join(GLOBAL_MEMORY_DIR, 'GLOBAL_MEMORY.md'), lines.join('\\n'));\n}\n\n/**\n * Search global patterns\n */\nexport async function searchGlobalPatterns(\n query: string,\n options: {\n limit?: number;\n severity?: string[];\n agent?: string;\n } = {}\n): Promise<GlobalPattern[]> {\n const patterns = await loadGlobalPatterns();\n const limit = options.limit || 10;\n const queryTerms = query.toLowerCase().split(/\\s+/).filter(t => t.length > 2);\n \n const scored = patterns\n .filter(p => {\n if (options.severity && !options.severity.includes(p.severity)) return false;\n if (options.agent && p.agent !== options.agent) return false;\n return true;\n })\n .map(p => {\n const text = `${p.pattern} ${p.description} ${p.agent}`.toLowerCase();\n let score = 0;\n for (const term of queryTerms) {\n if (text.includes(term)) score++;\n }\n return { pattern: p, score };\n })\n .filter(s => s.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n \n return scored.map(s => s.pattern);\n}\n\n// Private helpers\n\n/**\n * Load global patterns with validation and auto-recovery\n */\nasync function loadGlobalPatterns(): Promise<GlobalPattern[]> {\n const patternsPath = join(GLOBAL_MEMORY_DIR, 'global-patterns.json');\n \n try {\n if (existsSync(patternsPath)) {\n const content = await readFile(patternsPath, 'utf-8');\n const result = safeParseAndValidate(content, GlobalPatternsIndexSchema);\n \n if (result.success) {\n return result.data as GlobalPattern[];\n }\n \n // Validation failed - attempt recovery\n const backupManager = new BackupManager(patternsPath);\n if (await backupManager.recoverFromBackup()) {\n const recovered = await readFile(patternsPath, 'utf-8');\n const recoveredResult = safeParseAndValidate(recovered, GlobalPatternsIndexSchema);\n if (recoveredResult.success) {\n return recoveredResult.data as GlobalPattern[];\n }\n }\n }\n } catch {\n // File doesn't exist or recovery failed\n }\n \n return [];\n}\n\n/**\n * Save global patterns with backup and atomic write\n */\nasync function saveGlobalPatterns(patterns: GlobalPattern[]): Promise<void> {\n await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });\n const patternsPath = join(GLOBAL_MEMORY_DIR, 'global-patterns.json');\n \n // Intelligent deduplication: merge patterns with same ID\n const patternMap = new Map<string, GlobalPattern>();\n for (const pattern of patterns) {\n const existing = patternMap.get(pattern.id);\n if (existing) {\n // Merge: update occurrences, projects, and timestamps\n existing.occurrences += pattern.occurrences;\n for (const proj of pattern.projects) {\n if (!existing.projects.includes(proj)) {\n existing.projects.push(proj);\n }\n }\n existing.lastSeen = pattern.lastSeen > existing.lastSeen ? pattern.lastSeen : existing.lastSeen;\n } else {\n patternMap.set(pattern.id, pattern);\n }\n }\n \n const deduplicated = Array.from(patternMap.values());\n \n // Intelligent pruning: prioritize recent, high-severity, cross-project patterns\n const pruned = intelligentPruneGlobalPatterns(deduplicated, 500);\n \n // Create backup before writing\n const backupManager = new BackupManager(patternsPath);\n await backupManager.createBackup();\n \n // Atomic write\n await atomicWriteJSON(patternsPath, pruned);\n}\n\n/**\n * Intelligently prune global patterns to target count\n * Prioritizes: cross-project, high severity, recent, fixed patterns\n */\nfunction intelligentPruneGlobalPatterns(patterns: GlobalPattern[], targetCount: number): GlobalPattern[] {\n if (patterns.length <= targetCount) {\n return patterns;\n }\n \n const severityWeight: Record<string, number> = {\n critical: 100,\n high: 50,\n moderate: 20,\n low: 10,\n info: 5,\n };\n \n const scored = patterns.map(pattern => {\n const ageInDays = (Date.now() - new Date(pattern.lastSeen).getTime()) / (1000 * 60 * 60 * 24);\n const recencyScore = Math.max(0, 100 - ageInDays * 2);\n const severityScore = severityWeight[pattern.severity] || 10;\n const crossProjectBonus = (pattern.projects.length - 1) * 30; // More projects = higher score\n const fixedBonus = pattern.fixApplied ? 20 : 0;\n const occurrenceScore = Math.min(pattern.occurrences * 2, 100);\n \n return {\n pattern,\n score: recencyScore + severityScore + crossProjectBonus + fixedBonus + occurrenceScore,\n };\n });\n \n return scored\n .sort((a, b) => b.score - a.score)\n .slice(0, targetCount)\n .map(s => s.pattern);\n}\n\n/**\n * Extract a pattern ID using SHA256 for proper deduplication\n * \n * Normalizes the issue text to group similar patterns:\n * - Lowercases\n * - Replaces code snippets with 'CODE'\n * - Replaces numbers with 'N'\n * - Removes quotes\n * \n * Uses SHA256 truncated to 12 chars for collision-free IDs.\n */\nfunction extractPatternId(issue: Issue): string {\n const normalized = issue.issue\n .toLowerCase()\n .replace(/`[^`]+`/g, 'CODE')\n .replace(/\\b\\d+\\b/g, 'N')\n .replace(/['\"]/g, '')\n .slice(0, 100);\n \n const hash = createHash('sha256')\n .update(normalized)\n .digest('hex')\n .slice(0, 12);\n \n return `${issue.agent}-${issue.severity}-${hash}`;\n}\n\nfunction sanitizeName(name: string): string {\n return name.replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase();\n}\n"],"mappings":"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","names":[]}
@@ -1,279 +0,0 @@
1
- import {
2
- getTrieDirectory,
3
- getWorkingDirectory
4
- } from "./chunk-RDOJCRKJ.js";
5
-
6
- // src/utils/command-runner.ts
7
- import { exec, execFile, execSync } from "child_process";
8
- import { promisify } from "util";
9
-
10
- // src/skills/audit-logger.ts
11
- import { writeFile, mkdir, readdir, readFile } from "fs/promises";
12
- import { join } from "path";
13
- async function logSkillExecution(execution) {
14
- const workDir = getWorkingDirectory(void 0, true);
15
- const auditDir = join(getTrieDirectory(workDir), "audit");
16
- await mkdir(auditDir, { recursive: true });
17
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
18
- const safeSkillName = execution.skillName.replace(/[^a-z0-9-]/gi, "_");
19
- const filename = `${timestamp}_${safeSkillName}.json`;
20
- const filepath = join(auditDir, filename);
21
- if (!execution.endTime) {
22
- execution.endTime = Date.now();
23
- }
24
- if (!execution.duration) {
25
- execution.duration = execution.endTime - execution.startTime;
26
- }
27
- await writeFile(filepath, JSON.stringify(execution, null, 2), "utf-8");
28
- }
29
- function createAuditEntry(skillName, skillSource, triggeredBy, targetPath) {
30
- return {
31
- skillName,
32
- skillSource,
33
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
34
- startTime: Date.now(),
35
- success: false,
36
- triggeredBy,
37
- targetPath,
38
- commands: [],
39
- networkCalls: [],
40
- filesAccessed: [],
41
- filesModified: []
42
- };
43
- }
44
- function completeAuditEntry(entry, success, error) {
45
- const base = {
46
- ...entry,
47
- success,
48
- endTime: Date.now(),
49
- duration: Date.now() - entry.startTime
50
- };
51
- return error ? { ...base, error } : base;
52
- }
53
- async function getRecentAuditLogs(limit = 10) {
54
- const workDir = getWorkingDirectory(void 0, true);
55
- const auditDir = join(getTrieDirectory(workDir), "audit");
56
- try {
57
- const files = await readdir(auditDir);
58
- const sorted = files.filter((f) => f.endsWith(".json")).sort().reverse().slice(0, limit);
59
- const logs = [];
60
- for (const file of sorted) {
61
- try {
62
- const content = await readFile(join(auditDir, file), "utf-8");
63
- logs.push(JSON.parse(content));
64
- } catch {
65
- }
66
- }
67
- return logs;
68
- } catch {
69
- return [];
70
- }
71
- }
72
- async function getSkillAuditLogs(skillName) {
73
- const workDir = getWorkingDirectory(void 0, true);
74
- const auditDir = join(getTrieDirectory(workDir), "audit");
75
- const safeSkillName = skillName.replace(/[^a-z0-9-]/gi, "_");
76
- try {
77
- const files = await readdir(auditDir);
78
- const skillFiles = files.filter(
79
- (f) => f.includes(`_${safeSkillName}.json`)
80
- );
81
- const logs = [];
82
- for (const file of skillFiles) {
83
- try {
84
- const content = await readFile(join(auditDir, file), "utf-8");
85
- logs.push(JSON.parse(content));
86
- } catch {
87
- }
88
- }
89
- return logs.sort(
90
- (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
91
- );
92
- } catch {
93
- return [];
94
- }
95
- }
96
- async function getAuditStatistics() {
97
- const logs = await getRecentAuditLogs(1e3);
98
- const skills = /* @__PURE__ */ new Set();
99
- let totalCommands = 0;
100
- let totalNetworkCalls = 0;
101
- let blockedCommands = 0;
102
- let blockedNetworkCalls = 0;
103
- for (const log of logs) {
104
- skills.add(log.skillName);
105
- totalCommands += log.commands?.length ?? 0;
106
- totalNetworkCalls += log.networkCalls?.length ?? 0;
107
- blockedCommands += log.commands?.filter((c) => c.blockedBy).length ?? 0;
108
- blockedNetworkCalls += log.networkCalls?.filter((c) => c.blocked).length ?? 0;
109
- }
110
- return {
111
- totalExecutions: logs.length,
112
- successfulExecutions: logs.filter((l) => l.success).length,
113
- failedExecutions: logs.filter((l) => !l.success).length,
114
- uniqueSkills: skills.size,
115
- totalCommands,
116
- totalNetworkCalls,
117
- blockedCommands,
118
- blockedNetworkCalls
119
- };
120
- }
121
- function formatAuditLog(log) {
122
- const lines = [];
123
- const status = log.success ? "\u2705" : "\u274C";
124
- const duration = log.duration ? `${log.duration}ms` : "unknown";
125
- lines.push(`${status} ${log.skillName} (${duration})`);
126
- lines.push(` Time: ${new Date(log.timestamp).toLocaleString()}`);
127
- lines.push(` Triggered by: ${log.triggeredBy}`);
128
- lines.push(` Target: ${log.targetPath}`);
129
- if (log.commands && log.commands.length > 0) {
130
- lines.push(` Commands executed: ${log.commands.length}`);
131
- const blocked = log.commands.filter((c) => c.blockedBy).length;
132
- if (blocked > 0) {
133
- lines.push(` \u26A0\uFE0F Commands blocked: ${blocked}`);
134
- }
135
- }
136
- if (log.networkCalls && log.networkCalls.length > 0) {
137
- lines.push(` Network calls: ${log.networkCalls.length}`);
138
- const blocked = log.networkCalls.filter((c) => c.blocked).length;
139
- if (blocked > 0) {
140
- lines.push(` \u26A0\uFE0F Network calls blocked: ${blocked}`);
141
- }
142
- }
143
- if (log.error) {
144
- lines.push(` Error: ${log.error}`);
145
- }
146
- return lines.join("\n");
147
- }
148
-
149
- // src/utils/command-runner.ts
150
- var execAsync = promisify(exec);
151
- var execFileAsync = promisify(execFile);
152
- function redact(text) {
153
- return text.replace(/\b(AWS|ANTHROPIC|OPENAI|GITHUB)_[A-Z0-9_]*\s*=\s*([^\s"'`]+)/gi, "$1_<REDACTED>=<REDACTED>").replace(/\bBearer\s+[A-Za-z0-9\-._~+/]+=*\b/g, "Bearer <REDACTED>").replace(/\bghp_[A-Za-z0-9]{20,}\b/g, "ghp_<REDACTED>").replace(/\b(?:xox[baprs]-)[A-Za-z0-9-]{10,}\b/g, "<REDACTED_SLACK_TOKEN>").replace(/\bAKIA[0-9A-Z]{16}\b/g, "AKIA<REDACTED>");
154
- }
155
- function clampOutput(text, maxChars) {
156
- if (text.length <= maxChars) return text;
157
- return text.slice(0, maxChars) + `
158
- \u2026(truncated ${text.length - maxChars} chars)`;
159
- }
160
- function buildCommandRecord(command) {
161
- return {
162
- command,
163
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
164
- };
165
- }
166
- async function finalizeAndWrite(entry, cmd, outcome, options) {
167
- const duration = Date.now() - outcome.startedAt;
168
- cmd.duration = duration;
169
- if (outcome.exitCode !== void 0) {
170
- cmd.exitCode = outcome.exitCode;
171
- }
172
- const captureOutput = options?.captureOutput ?? false;
173
- const redactOutput = options?.redactOutput ?? true;
174
- const maxOutputChars = options?.maxOutputChars ?? 2e3;
175
- if (captureOutput) {
176
- const out = outcome.stdout ?? "";
177
- const err = outcome.stderr ?? "";
178
- cmd.stdout = redactOutput ? redact(clampOutput(out, maxOutputChars)) : clampOutput(out, maxOutputChars);
179
- cmd.stderr = redactOutput ? redact(clampOutput(err, maxOutputChars)) : clampOutput(err, maxOutputChars);
180
- }
181
- const completed = completeAuditEntry(entry, outcome.success, outcome.error);
182
- await logSkillExecution(completed);
183
- }
184
- async function runShellCommand(command, audit, options) {
185
- const startedAt = Date.now();
186
- const entry = createAuditEntry(audit.actor, audit.source ?? "trie", audit.triggeredBy, audit.targetPath);
187
- const cmd = buildCommandRecord(command);
188
- entry.commands?.push(cmd);
189
- try {
190
- const { stdout, stderr } = await execAsync(command, {
191
- cwd: options?.cwd,
192
- timeout: options?.timeoutMs,
193
- maxBuffer: options?.maxBuffer
194
- });
195
- await finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout, stderr, startedAt }, options);
196
- return { stdout: stdout ?? "", stderr: stderr ?? "", exitCode: 0 };
197
- } catch (e) {
198
- const err = e;
199
- const stdout = typeof err.stdout === "string" ? err.stdout : "";
200
- const stderr = typeof err.stderr === "string" ? err.stderr : "";
201
- const exitCode = typeof err.code === "number" ? err.code : 1;
202
- await finalizeAndWrite(
203
- entry,
204
- cmd,
205
- { success: false, exitCode, stdout, stderr, error: err.message, startedAt },
206
- // Capture output for failures by default (so audits are useful)
207
- { ...options, captureOutput: options?.captureOutput ?? true }
208
- );
209
- return { stdout, stderr, exitCode };
210
- }
211
- }
212
- function runShellCommandSync(command, audit, options) {
213
- const startedAt = Date.now();
214
- const entry = createAuditEntry(audit.actor, audit.source ?? "trie", audit.triggeredBy, audit.targetPath);
215
- const cmd = buildCommandRecord(command);
216
- entry.commands?.push(cmd);
217
- try {
218
- const stdout = execSync(command, {
219
- cwd: options?.cwd,
220
- timeout: options?.timeoutMs,
221
- maxBuffer: options?.maxBuffer,
222
- encoding: "utf-8",
223
- stdio: ["pipe", "pipe", "pipe"]
224
- });
225
- void finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout, stderr: "", startedAt }, options);
226
- return { stdout: stdout ?? "", exitCode: 0 };
227
- } catch (e) {
228
- const err = e;
229
- const stdout = typeof err.stdout === "string" ? err.stdout : "";
230
- const stderr = typeof err.stderr === "string" ? err.stderr : "";
231
- const exitCode = typeof err.status === "number" ? err.status : 1;
232
- void finalizeAndWrite(
233
- entry,
234
- cmd,
235
- { success: false, exitCode, stdout, stderr, error: err.message, startedAt },
236
- { ...options, captureOutput: options?.captureOutput ?? true }
237
- );
238
- return { stdout, exitCode };
239
- }
240
- }
241
- async function runExecFile(file, args, audit, options) {
242
- const startedAt = Date.now();
243
- const command = [file, ...args].join(" ");
244
- const entry = createAuditEntry(audit.actor, audit.source ?? "trie", audit.triggeredBy, audit.targetPath);
245
- const cmd = buildCommandRecord(command);
246
- entry.commands?.push(cmd);
247
- try {
248
- const { stdout, stderr } = await execFileAsync(file, args, {
249
- cwd: options?.cwd,
250
- timeout: options?.timeoutMs,
251
- maxBuffer: options?.maxBuffer
252
- });
253
- await finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout: String(stdout ?? ""), stderr: String(stderr ?? ""), startedAt }, options);
254
- return { stdout: String(stdout ?? ""), stderr: String(stderr ?? ""), exitCode: 0 };
255
- } catch (e) {
256
- const err = e;
257
- const stdout = typeof err.stdout === "string" ? err.stdout : "";
258
- const stderr = typeof err.stderr === "string" ? err.stderr : "";
259
- const exitCode = typeof err.code === "number" ? err.code : 1;
260
- await finalizeAndWrite(
261
- entry,
262
- cmd,
263
- { success: false, exitCode, stdout, stderr, error: err.message, startedAt },
264
- { ...options, captureOutput: options?.captureOutput ?? true }
265
- );
266
- return { stdout, stderr, exitCode };
267
- }
268
- }
269
-
270
- export {
271
- getRecentAuditLogs,
272
- getSkillAuditLogs,
273
- getAuditStatistics,
274
- formatAuditLog,
275
- runShellCommand,
276
- runShellCommandSync,
277
- runExecFile
278
- };
279
- //# sourceMappingURL=chunk-MURGTWG4.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/command-runner.ts","../src/skills/audit-logger.ts"],"sourcesContent":["/**\n * Command Runner (with audit logging)\n *\n * Goal: Whenever Trie runs a shell command, record:\n * - command string\n * - exit code\n * - duration\n * - optional (redacted) stdout/stderr\n */\nimport { exec, execFile, execSync, type ExecException } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nimport {\n createAuditEntry,\n completeAuditEntry,\n logSkillExecution,\n type ExecutedCommand,\n type SkillExecution,\n} from '../skills/audit-logger.js';\n\nconst execAsync = promisify(exec);\nconst execFileAsync = promisify(execFile);\n\nexport type AuditTriggeredBy = SkillExecution['triggeredBy'];\n\nexport interface CommandAuditContext {\n /** Shown as `skillName` in audit logs; use something like `tool:pr-review` */\n actor: string;\n /** Where this came from in the product flow */\n triggeredBy: AuditTriggeredBy;\n /** Usually the working directory / repo path */\n targetPath: string;\n /** Optional string for `skillSource` */\n source?: string;\n}\n\nexport interface RunCommandOptions {\n cwd?: string;\n timeoutMs?: number;\n maxBuffer?: number;\n\n /** Capture stdout/stderr in the audit log */\n captureOutput?: boolean;\n /** Redact obvious secrets from captured output */\n redactOutput?: boolean;\n /** Max chars to keep per output stream */\n maxOutputChars?: number;\n}\n\nfunction redact(text: string): string {\n // Keep this conservative to avoid false redaction and avoid expensive processing.\n return text\n // Common key=value secrets\n .replace(/\\b(AWS|ANTHROPIC|OPENAI|GITHUB)_[A-Z0-9_]*\\s*=\\s*([^\\s\"'`]+)/gi, '$1_<REDACTED>=<REDACTED>')\n // Bearer tokens\n .replace(/\\bBearer\\s+[A-Za-z0-9\\-._~+/]+=*\\b/g, 'Bearer <REDACTED>')\n // GitHub tokens / generic tokens\n .replace(/\\bghp_[A-Za-z0-9]{20,}\\b/g, 'ghp_<REDACTED>')\n .replace(/\\b(?:xox[baprs]-)[A-Za-z0-9-]{10,}\\b/g, '<REDACTED_SLACK_TOKEN>')\n // AWS access key id (best-effort)\n .replace(/\\bAKIA[0-9A-Z]{16}\\b/g, 'AKIA<REDACTED>');\n}\n\nfunction clampOutput(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text;\n return text.slice(0, maxChars) + `\\n…(truncated ${text.length - maxChars} chars)`;\n}\n\nfunction buildCommandRecord(command: string): ExecutedCommand {\n return {\n command,\n timestamp: new Date().toISOString(),\n };\n}\n\nasync function finalizeAndWrite(\n entry: SkillExecution,\n cmd: ExecutedCommand,\n outcome: { success: boolean; exitCode?: number; stdout?: string; stderr?: string; error?: string; startedAt: number },\n options?: Pick<RunCommandOptions, 'captureOutput' | 'redactOutput' | 'maxOutputChars'>\n): Promise<void> {\n const duration = Date.now() - outcome.startedAt;\n cmd.duration = duration;\n if (outcome.exitCode !== undefined) {\n cmd.exitCode = outcome.exitCode;\n }\n\n const captureOutput = options?.captureOutput ?? false;\n const redactOutput = options?.redactOutput ?? true;\n const maxOutputChars = options?.maxOutputChars ?? 2000;\n\n if (captureOutput) {\n const out = outcome.stdout ?? '';\n const err = outcome.stderr ?? '';\n cmd.stdout = redactOutput ? redact(clampOutput(out, maxOutputChars)) : clampOutput(out, maxOutputChars);\n cmd.stderr = redactOutput ? redact(clampOutput(err, maxOutputChars)) : clampOutput(err, maxOutputChars);\n }\n\n const completed = completeAuditEntry(entry, outcome.success, outcome.error);\n await logSkillExecution(completed);\n}\n\nexport async function runShellCommand(\n command: string,\n audit: CommandAuditContext,\n options?: RunCommandOptions\n): Promise<{ stdout: string; stderr: string; exitCode: number }> {\n const startedAt = Date.now();\n const entry = createAuditEntry(audit.actor, audit.source ?? 'trie', audit.triggeredBy, audit.targetPath);\n const cmd = buildCommandRecord(command);\n entry.commands?.push(cmd);\n\n try {\n const { stdout, stderr } = await execAsync(command, {\n cwd: options?.cwd,\n timeout: options?.timeoutMs,\n maxBuffer: options?.maxBuffer,\n });\n\n await finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout, stderr, startedAt }, options);\n return { stdout: stdout ?? '', stderr: stderr ?? '', exitCode: 0 };\n } catch (e) {\n const err = e as ExecException & { stdout?: unknown; stderr?: unknown; code?: unknown };\n const stdout = typeof err.stdout === 'string' ? err.stdout : '';\n const stderr = typeof err.stderr === 'string' ? err.stderr : '';\n const exitCode = typeof err.code === 'number' ? err.code : 1;\n\n await finalizeAndWrite(\n entry,\n cmd,\n { success: false, exitCode, stdout, stderr, error: err.message, startedAt },\n // Capture output for failures by default (so audits are useful)\n { ...options, captureOutput: options?.captureOutput ?? true }\n );\n\n return { stdout, stderr, exitCode };\n }\n}\n\nexport function runShellCommandSync(\n command: string,\n audit: CommandAuditContext,\n options?: Omit<RunCommandOptions, 'timeoutMs'> & { timeoutMs?: number }\n): { stdout: string; exitCode: number } {\n const startedAt = Date.now();\n const entry = createAuditEntry(audit.actor, audit.source ?? 'trie', audit.triggeredBy, audit.targetPath);\n const cmd = buildCommandRecord(command);\n entry.commands?.push(cmd);\n\n try {\n const stdout = execSync(command, {\n cwd: options?.cwd,\n timeout: options?.timeoutMs,\n maxBuffer: options?.maxBuffer,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n // Fire-and-forget write; sync APIs can’t await.\n void finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout, stderr: '', startedAt }, options);\n return { stdout: stdout ?? '', exitCode: 0 };\n } catch (e) {\n const err = e as ExecException & { stdout?: unknown; stderr?: unknown; status?: unknown };\n const stdout = typeof err.stdout === 'string' ? err.stdout : '';\n const stderr = typeof err.stderr === 'string' ? err.stderr : '';\n const exitCode = typeof err.status === 'number' ? err.status : 1;\n\n void finalizeAndWrite(\n entry,\n cmd,\n { success: false, exitCode, stdout, stderr, error: err.message, startedAt },\n { ...options, captureOutput: options?.captureOutput ?? true }\n );\n\n return { stdout, exitCode };\n }\n}\n\nexport async function runExecFile(\n file: string,\n args: string[],\n audit: CommandAuditContext,\n options?: Omit<RunCommandOptions, 'timeoutMs'> & { timeoutMs?: number }\n): Promise<{ stdout: string; stderr: string; exitCode: number }> {\n const startedAt = Date.now();\n const command = [file, ...args].join(' ');\n const entry = createAuditEntry(audit.actor, audit.source ?? 'trie', audit.triggeredBy, audit.targetPath);\n const cmd = buildCommandRecord(command);\n entry.commands?.push(cmd);\n\n try {\n const { stdout, stderr } = await execFileAsync(file, args, {\n cwd: options?.cwd,\n timeout: options?.timeoutMs,\n maxBuffer: options?.maxBuffer,\n });\n\n await finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout: String(stdout ?? ''), stderr: String(stderr ?? ''), startedAt }, options);\n return { stdout: String(stdout ?? ''), stderr: String(stderr ?? ''), exitCode: 0 };\n } catch (e) {\n const err = e as ExecException & { stdout?: unknown; stderr?: unknown; code?: unknown };\n const stdout = typeof err.stdout === 'string' ? err.stdout : '';\n const stderr = typeof err.stderr === 'string' ? err.stderr : '';\n const exitCode = typeof err.code === 'number' ? err.code : 1;\n\n await finalizeAndWrite(\n entry,\n cmd,\n { success: false, exitCode, stdout, stderr, error: err.message, startedAt },\n { ...options, captureOutput: options?.captureOutput ?? true }\n );\n return { stdout, stderr, exitCode };\n }\n}\n\n","/**\n * Skill Audit Logger\n * \n * Logs all skill operations for security auditing\n */\n\nimport { writeFile, mkdir, readdir, readFile } from 'fs/promises';\nimport { join } from 'path';\nimport { getWorkingDirectory, getTrieDirectory } from '../utils/workspace.js';\n\nexport interface SkillExecution {\n skillName: string;\n skillSource: string;\n timestamp: string;\n startTime: number;\n endTime?: number;\n duration?: number;\n success: boolean;\n error?: string;\n \n // What the skill did\n commands?: ExecutedCommand[];\n networkCalls?: NetworkCall[];\n filesAccessed?: string[];\n filesModified?: string[];\n \n // Context\n triggeredBy: 'scan' | 'check' | 'manual' | 'watch';\n targetPath: string;\n}\n\nexport interface ExecutedCommand {\n command: string;\n timestamp: string;\n exitCode?: number;\n duration?: number;\n stdout?: string;\n stderr?: string;\n blockedBy?: string; // If blocked, what rule blocked it\n}\n\nexport interface NetworkCall {\n url: string;\n method: string;\n timestamp: string;\n duration?: number;\n statusCode?: number;\n blocked?: boolean;\n blockedReason?: string;\n}\n\n/**\n * Log a skill execution for audit trail\n */\nexport async function logSkillExecution(execution: SkillExecution): Promise<void> {\n const workDir = getWorkingDirectory(undefined, true);\n const auditDir = join(getTrieDirectory(workDir), 'audit');\n \n await mkdir(auditDir, { recursive: true });\n \n // Create filename with timestamp and skill name\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const safeSkillName = execution.skillName.replace(/[^a-z0-9-]/gi, '_');\n const filename = `${timestamp}_${safeSkillName}.json`;\n const filepath = join(auditDir, filename);\n \n // Add end time if not set\n if (!execution.endTime) {\n execution.endTime = Date.now();\n }\n \n // Calculate duration\n if (!execution.duration) {\n execution.duration = execution.endTime - execution.startTime;\n }\n \n await writeFile(filepath, JSON.stringify(execution, null, 2), 'utf-8');\n}\n\n/**\n * Create audit log entry for a skill execution start\n */\nexport function createAuditEntry(\n skillName: string,\n skillSource: string,\n triggeredBy: SkillExecution['triggeredBy'],\n targetPath: string\n): SkillExecution {\n return {\n skillName,\n skillSource,\n timestamp: new Date().toISOString(),\n startTime: Date.now(),\n success: false,\n triggeredBy,\n targetPath,\n commands: [],\n networkCalls: [],\n filesAccessed: [],\n filesModified: [],\n };\n}\n\n/**\n * Mark execution as complete\n */\nexport function completeAuditEntry(\n entry: SkillExecution,\n success: boolean,\n error?: string\n): SkillExecution {\n const base: SkillExecution = {\n ...entry,\n success,\n endTime: Date.now(),\n duration: Date.now() - entry.startTime,\n };\n return error ? { ...base, error } : base;\n}\n\n/**\n * Get recent audit logs\n */\nexport async function getRecentAuditLogs(limit: number = 10): Promise<SkillExecution[]> {\n const workDir = getWorkingDirectory(undefined, true);\n const auditDir = join(getTrieDirectory(workDir), 'audit');\n \n try {\n const files = await readdir(auditDir);\n \n // Sort by filename (which includes timestamp) descending\n const sorted = files\n .filter(f => f.endsWith('.json'))\n .sort()\n .reverse()\n .slice(0, limit);\n \n const logs: SkillExecution[] = [];\n \n for (const file of sorted) {\n try {\n const content = await readFile(join(auditDir, file), 'utf-8');\n logs.push(JSON.parse(content));\n } catch {\n // Skip malformed logs\n }\n }\n \n return logs;\n } catch {\n // Audit directory doesn't exist yet\n return [];\n }\n}\n\n/**\n * Get audit logs for a specific skill\n */\nexport async function getSkillAuditLogs(skillName: string): Promise<SkillExecution[]> {\n const workDir = getWorkingDirectory(undefined, true);\n const auditDir = join(getTrieDirectory(workDir), 'audit');\n const safeSkillName = skillName.replace(/[^a-z0-9-]/gi, '_');\n \n try {\n const files = await readdir(auditDir);\n \n const skillFiles = files.filter(f => \n f.includes(`_${safeSkillName}.json`)\n );\n \n const logs: SkillExecution[] = [];\n \n for (const file of skillFiles) {\n try {\n const content = await readFile(join(auditDir, file), 'utf-8');\n logs.push(JSON.parse(content));\n } catch {\n // Skip malformed logs\n }\n }\n \n return logs.sort((a, b) => \n new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()\n );\n } catch {\n return [];\n }\n}\n\n/**\n * Get audit statistics\n */\nexport async function getAuditStatistics(): Promise<{\n totalExecutions: number;\n successfulExecutions: number;\n failedExecutions: number;\n uniqueSkills: number;\n totalCommands: number;\n totalNetworkCalls: number;\n blockedCommands: number;\n blockedNetworkCalls: number;\n}> {\n const logs = await getRecentAuditLogs(1000); // Last 1000 executions\n \n const skills = new Set<string>();\n let totalCommands = 0;\n let totalNetworkCalls = 0;\n let blockedCommands = 0;\n let blockedNetworkCalls = 0;\n \n for (const log of logs) {\n skills.add(log.skillName);\n totalCommands += log.commands?.length ?? 0;\n totalNetworkCalls += log.networkCalls?.length ?? 0;\n blockedCommands += log.commands?.filter(c => c.blockedBy).length ?? 0;\n blockedNetworkCalls += log.networkCalls?.filter(c => c.blocked).length ?? 0;\n }\n \n return {\n totalExecutions: logs.length,\n successfulExecutions: logs.filter(l => l.success).length,\n failedExecutions: logs.filter(l => !l.success).length,\n uniqueSkills: skills.size,\n totalCommands,\n totalNetworkCalls,\n blockedCommands,\n blockedNetworkCalls,\n };\n}\n\n/**\n * Format audit log for display\n */\nexport function formatAuditLog(log: SkillExecution): string {\n const lines: string[] = [];\n \n const status = log.success ? '✅' : '❌';\n const duration = log.duration ? `${log.duration}ms` : 'unknown';\n \n lines.push(`${status} ${log.skillName} (${duration})`);\n lines.push(` Time: ${new Date(log.timestamp).toLocaleString()}`);\n lines.push(` Triggered by: ${log.triggeredBy}`);\n lines.push(` Target: ${log.targetPath}`);\n \n if (log.commands && log.commands.length > 0) {\n lines.push(` Commands executed: ${log.commands.length}`);\n const blocked = log.commands.filter(c => c.blockedBy).length;\n if (blocked > 0) {\n lines.push(` ⚠️ Commands blocked: ${blocked}`);\n }\n }\n \n if (log.networkCalls && log.networkCalls.length > 0) {\n lines.push(` Network calls: ${log.networkCalls.length}`);\n const blocked = log.networkCalls.filter(c => c.blocked).length;\n if (blocked > 0) {\n lines.push(` ⚠️ Network calls blocked: ${blocked}`);\n }\n }\n \n if (log.error) {\n lines.push(` Error: ${log.error}`);\n }\n \n return lines.join('\\n');\n}\n"],"mappings":"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","names":[]}