@triedotdev/mcp 1.0.169 → 1.0.171
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 +62 -540
- package/dist/chunk-2YXOBNKW.js +619 -0
- package/dist/chunk-2YXOBNKW.js.map +1 -0
- package/dist/chunk-QR64Y5TI.js +363 -0
- package/dist/chunk-QR64Y5TI.js.map +1 -0
- package/dist/cli/main.d.ts +0 -15
- package/dist/cli/main.js +356 -3098
- package/dist/cli/main.js.map +1 -1
- package/dist/index.js +2 -34
- package/dist/index.js.map +1 -1
- package/dist/server/mcp-server.js +2 -34
- package/package.json +8 -31
- package/dist/autonomy-config-FSERX3O3.js +0 -30
- package/dist/autonomy-config-FSERX3O3.js.map +0 -1
- package/dist/chat-store-JNGNTDSN.js +0 -15
- package/dist/chat-store-JNGNTDSN.js.map +0 -1
- package/dist/chunk-2HF65EHQ.js +0 -311
- package/dist/chunk-2HF65EHQ.js.map +0 -1
- package/dist/chunk-3XR6WVAW.js +0 -4011
- package/dist/chunk-3XR6WVAW.js.map +0 -1
- package/dist/chunk-43X6JBEM.js +0 -36
- package/dist/chunk-43X6JBEM.js.map +0 -1
- package/dist/chunk-6NLHFIYA.js +0 -344
- package/dist/chunk-6NLHFIYA.js.map +0 -1
- package/dist/chunk-7IO4YUI3.js +0 -1827
- package/dist/chunk-7IO4YUI3.js.map +0 -1
- package/dist/chunk-AHD2CBQ7.js +0 -846
- package/dist/chunk-AHD2CBQ7.js.map +0 -1
- package/dist/chunk-BUTOP5EB.js +0 -931
- package/dist/chunk-BUTOP5EB.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-EFWVF6TI.js +0 -267
- package/dist/chunk-EFWVF6TI.js.map +0 -1
- package/dist/chunk-F6WFNUAY.js +0 -216
- package/dist/chunk-F6WFNUAY.js.map +0 -1
- package/dist/chunk-FBNURWRY.js +0 -662
- package/dist/chunk-FBNURWRY.js.map +0 -1
- package/dist/chunk-FQ45QP5A.js +0 -361
- package/dist/chunk-FQ45QP5A.js.map +0 -1
- package/dist/chunk-FVRO5RN3.js +0 -1306
- package/dist/chunk-FVRO5RN3.js.map +0 -1
- package/dist/chunk-G2TGF6TR.js +0 -573
- package/dist/chunk-G2TGF6TR.js.map +0 -1
- package/dist/chunk-G3I7SZLW.js +0 -354
- package/dist/chunk-G3I7SZLW.js.map +0 -1
- package/dist/chunk-GTKYBOXL.js +0 -700
- package/dist/chunk-GTKYBOXL.js.map +0 -1
- package/dist/chunk-HVCDY3AK.js +0 -850
- package/dist/chunk-HVCDY3AK.js.map +0 -1
- package/dist/chunk-I2O5OYQT.js +0 -727
- package/dist/chunk-I2O5OYQT.js.map +0 -1
- package/dist/chunk-JVMBCWKS.js +0 -348
- package/dist/chunk-JVMBCWKS.js.map +0 -1
- package/dist/chunk-KCUOWRPX.js +0 -816
- package/dist/chunk-KCUOWRPX.js.map +0 -1
- package/dist/chunk-KDHN2ZQE.js +0 -313
- package/dist/chunk-KDHN2ZQE.js.map +0 -1
- package/dist/chunk-ME2OERF5.js +0 -345
- package/dist/chunk-ME2OERF5.js.map +0 -1
- package/dist/chunk-OBQ74FOU.js +0 -27
- package/dist/chunk-OBQ74FOU.js.map +0 -1
- package/dist/chunk-Q5EKA5YA.js +0 -254
- package/dist/chunk-Q5EKA5YA.js.map +0 -1
- package/dist/chunk-Q63FFI6D.js +0 -132
- package/dist/chunk-Q63FFI6D.js.map +0 -1
- package/dist/chunk-SASNMSB5.js +0 -12597
- package/dist/chunk-SASNMSB5.js.map +0 -1
- package/dist/chunk-T63OHG4Q.js +0 -440
- package/dist/chunk-T63OHG4Q.js.map +0 -1
- package/dist/chunk-TN5WEKWI.js +0 -173
- package/dist/chunk-TN5WEKWI.js.map +0 -1
- package/dist/chunk-VUL52BQL.js +0 -402
- package/dist/chunk-VUL52BQL.js.map +0 -1
- package/dist/chunk-VVITXIHN.js +0 -189
- package/dist/chunk-VVITXIHN.js.map +0 -1
- package/dist/chunk-WCN7S3EI.js +0 -14
- package/dist/chunk-WCN7S3EI.js.map +0 -1
- package/dist/chunk-XPZZFPBZ.js +0 -491
- package/dist/chunk-XPZZFPBZ.js.map +0 -1
- package/dist/chunk-ZJF5FTBX.js +0 -1396
- package/dist/chunk-ZJF5FTBX.js.map +0 -1
- package/dist/chunk-ZV2K6M7T.js +0 -74
- package/dist/chunk-ZV2K6M7T.js.map +0 -1
- package/dist/cli/create-agent.d.ts +0 -1
- package/dist/cli/create-agent.js +0 -1050
- package/dist/cli/create-agent.js.map +0 -1
- package/dist/cli/yolo-daemon.d.ts +0 -1
- package/dist/cli/yolo-daemon.js +0 -421
- package/dist/cli/yolo-daemon.js.map +0 -1
- package/dist/client-NJPZE5JT.js +0 -28
- package/dist/client-NJPZE5JT.js.map +0 -1
- package/dist/codebase-index-VAPF32XX.js +0 -12
- package/dist/codebase-index-VAPF32XX.js.map +0 -1
- package/dist/fast-analyzer-3GCCZMLK.js +0 -216
- package/dist/fast-analyzer-3GCCZMLK.js.map +0 -1
- package/dist/git-EO5SRFMN.js +0 -28
- package/dist/git-EO5SRFMN.js.map +0 -1
- package/dist/github-ingester-ZOKK6GRS.js +0 -11
- package/dist/github-ingester-ZOKK6GRS.js.map +0 -1
- package/dist/goal-manager-QUKX2W6C.js +0 -25
- package/dist/goal-manager-QUKX2W6C.js.map +0 -1
- package/dist/goal-validator-2SFSKKVU.js +0 -24
- package/dist/goal-validator-2SFSKKVU.js.map +0 -1
- package/dist/graph-B3NA4S7I.js +0 -10
- package/dist/graph-B3NA4S7I.js.map +0 -1
- package/dist/hypothesis-KCPBR652.js +0 -23
- package/dist/hypothesis-KCPBR652.js.map +0 -1
- package/dist/incident-index-EFNUSGWL.js +0 -11
- package/dist/incident-index-EFNUSGWL.js.map +0 -1
- package/dist/insight-store-EC4PLSAW.js +0 -22
- package/dist/insight-store-EC4PLSAW.js.map +0 -1
- package/dist/issue-store-YAXTNRRY.js +0 -36
- package/dist/issue-store-YAXTNRRY.js.map +0 -1
- package/dist/ledger-TWZTGDFA.js +0 -58
- package/dist/ledger-TWZTGDFA.js.map +0 -1
- package/dist/linear-ingester-XXPAZZRW.js +0 -11
- package/dist/linear-ingester-XXPAZZRW.js.map +0 -1
- package/dist/output-manager-RVJ37XKA.js +0 -13
- package/dist/output-manager-RVJ37XKA.js.map +0 -1
- package/dist/parse-goal-violation-SACGFG3C.js +0 -8
- package/dist/parse-goal-violation-SACGFG3C.js.map +0 -1
- package/dist/pattern-discovery-F7LU5K6E.js +0 -8
- package/dist/pattern-discovery-F7LU5K6E.js.map +0 -1
- package/dist/progress-SRQ2V3BP.js +0 -18
- package/dist/progress-SRQ2V3BP.js.map +0 -1
- package/dist/project-state-AHPA77SM.js +0 -28
- package/dist/project-state-AHPA77SM.js.map +0 -1
- package/dist/sync-M2FSWPBC.js +0 -12
- package/dist/sync-M2FSWPBC.js.map +0 -1
- package/dist/terminal-spawn-5YXDMUCF.js +0 -157
- package/dist/terminal-spawn-5YXDMUCF.js.map +0 -1
- package/dist/tiered-storage-DYNC5CQ6.js +0 -13
- package/dist/tiered-storage-DYNC5CQ6.js.map +0 -1
- package/dist/trie-agent-I3HAHY2G.js +0 -26
- package/dist/trie-agent-I3HAHY2G.js.map +0 -1
- package/dist/ui/chat.html +0 -1014
- package/dist/ui/goals.html +0 -967
- package/dist/ui/hypotheses.html +0 -1011
- package/dist/ui/ledger.html +0 -954
- package/dist/ui/nudges.html +0 -995
- package/dist/vibe-code-signatures-5ZULYP3D.js +0 -987
- package/dist/vibe-code-signatures-5ZULYP3D.js.map +0 -1
- package/dist/vulnerability-signatures-2URZSXAQ.js +0 -983
- package/dist/vulnerability-signatures-2URZSXAQ.js.map +0 -1
package/dist/chunk-AHD2CBQ7.js
DELETED
|
@@ -1,846 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getStorage
|
|
3
|
-
} from "./chunk-BUTOP5EB.js";
|
|
4
|
-
import {
|
|
5
|
-
tryGetClient
|
|
6
|
-
} from "./chunk-FQ45QP5A.js";
|
|
7
|
-
import {
|
|
8
|
-
searchIssues
|
|
9
|
-
} from "./chunk-KCUOWRPX.js";
|
|
10
|
-
import {
|
|
11
|
-
BackupManager,
|
|
12
|
-
GlobalPatternsIndexSchema,
|
|
13
|
-
safeParseAndValidate
|
|
14
|
-
} from "./chunk-EFWVF6TI.js";
|
|
15
|
-
import {
|
|
16
|
-
atomicWriteJSON
|
|
17
|
-
} from "./chunk-43X6JBEM.js";
|
|
18
|
-
|
|
19
|
-
// src/memory/global-memory.ts
|
|
20
|
-
import { mkdir, writeFile, readFile, readdir } from "fs/promises";
|
|
21
|
-
import { createHash } from "crypto";
|
|
22
|
-
import { existsSync } from "fs";
|
|
23
|
-
import { join } from "path";
|
|
24
|
-
import { homedir } from "os";
|
|
25
|
-
var GLOBAL_TRIE_DIR = join(homedir(), ".trie");
|
|
26
|
-
var GLOBAL_MEMORY_DIR = join(GLOBAL_TRIE_DIR, "memory");
|
|
27
|
-
async function recordToGlobalMemory(issues, projectName, projectPath) {
|
|
28
|
-
await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
29
|
-
await mkdir(join(GLOBAL_MEMORY_DIR, "projects"), { recursive: true });
|
|
30
|
-
const patterns = await loadGlobalPatterns();
|
|
31
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
32
|
-
for (const issue of issues) {
|
|
33
|
-
const patternId = extractPatternId(issue);
|
|
34
|
-
const existing = patterns.find((p) => p.id === patternId);
|
|
35
|
-
if (existing) {
|
|
36
|
-
existing.occurrences++;
|
|
37
|
-
existing.lastSeen = now;
|
|
38
|
-
if (!existing.projects.includes(projectName)) {
|
|
39
|
-
existing.projects.push(projectName);
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
patterns.push({
|
|
43
|
-
id: patternId,
|
|
44
|
-
pattern: issue.issue.slice(0, 200),
|
|
45
|
-
description: issue.fix.slice(0, 200),
|
|
46
|
-
severity: issue.severity,
|
|
47
|
-
agent: issue.agent,
|
|
48
|
-
occurrences: 1,
|
|
49
|
-
projects: [projectName],
|
|
50
|
-
firstSeen: now,
|
|
51
|
-
lastSeen: now
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
await saveGlobalPatterns(patterns);
|
|
56
|
-
const summaryPath = join(GLOBAL_MEMORY_DIR, "projects", `${sanitizeName(projectName)}.json`);
|
|
57
|
-
const summary = {
|
|
58
|
-
name: projectName,
|
|
59
|
-
path: projectPath,
|
|
60
|
-
lastScan: now,
|
|
61
|
-
totalIssues: issues.length,
|
|
62
|
-
patterns: [...new Set(issues.map((i) => extractPatternId(i)))]
|
|
63
|
-
};
|
|
64
|
-
await atomicWriteJSON(summaryPath, summary);
|
|
65
|
-
}
|
|
66
|
-
async function findCrossProjectPatterns(minOccurrences = 2) {
|
|
67
|
-
const patterns = await loadGlobalPatterns();
|
|
68
|
-
return patterns.filter((p) => p.projects.length >= minOccurrences).sort((a, b) => b.occurrences - a.occurrences);
|
|
69
|
-
}
|
|
70
|
-
async function listTrackedProjects() {
|
|
71
|
-
const projectsDir = join(GLOBAL_MEMORY_DIR, "projects");
|
|
72
|
-
try {
|
|
73
|
-
if (!existsSync(projectsDir)) return [];
|
|
74
|
-
const files = await readdir(projectsDir);
|
|
75
|
-
const summaries = [];
|
|
76
|
-
for (const file of files) {
|
|
77
|
-
if (!file.endsWith(".json")) continue;
|
|
78
|
-
try {
|
|
79
|
-
const content = await readFile(join(projectsDir, file), "utf-8");
|
|
80
|
-
summaries.push(JSON.parse(content));
|
|
81
|
-
} catch {
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return summaries.sort(
|
|
85
|
-
(a, b) => new Date(b.lastScan).getTime() - new Date(a.lastScan).getTime()
|
|
86
|
-
);
|
|
87
|
-
} catch {
|
|
88
|
-
return [];
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
async function getGlobalMemoryStats() {
|
|
92
|
-
const patterns = await loadGlobalPatterns();
|
|
93
|
-
const projects = await listTrackedProjects();
|
|
94
|
-
const MAX_PATTERNS = 500;
|
|
95
|
-
const patternsByAgent = {};
|
|
96
|
-
for (const pattern of patterns) {
|
|
97
|
-
patternsByAgent[pattern.agent] = (patternsByAgent[pattern.agent] || 0) + 1;
|
|
98
|
-
}
|
|
99
|
-
const totalOccurrences = patterns.reduce((sum, p) => sum + p.occurrences, 0);
|
|
100
|
-
const avgOccurrences = patterns.length > 0 ? totalOccurrences / patterns.length : 0;
|
|
101
|
-
return {
|
|
102
|
-
totalPatterns: patterns.length,
|
|
103
|
-
crossProjectPatterns: patterns.filter((p) => p.projects.length >= 2).length,
|
|
104
|
-
trackedProjects: projects.length,
|
|
105
|
-
totalOccurrences,
|
|
106
|
-
fixedPatterns: patterns.filter((p) => p.fixApplied).length,
|
|
107
|
-
patternsByAgent,
|
|
108
|
-
capacityInfo: {
|
|
109
|
-
current: patterns.length,
|
|
110
|
-
max: MAX_PATTERNS,
|
|
111
|
-
percentFull: Math.round(patterns.length / MAX_PATTERNS * 100),
|
|
112
|
-
isAtCap: patterns.length >= MAX_PATTERNS
|
|
113
|
-
},
|
|
114
|
-
deduplicationStats: {
|
|
115
|
-
uniquePatterns: patterns.length,
|
|
116
|
-
averageOccurrences: Math.round(avgOccurrences * 10) / 10
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
async function updateGlobalMemoryMd() {
|
|
121
|
-
const patterns = await loadGlobalPatterns();
|
|
122
|
-
const crossProject = patterns.filter((p) => p.projects.length >= 2);
|
|
123
|
-
const projects = await listTrackedProjects();
|
|
124
|
-
const lines = [
|
|
125
|
-
"# Global Trie Memory",
|
|
126
|
-
"",
|
|
127
|
-
"> Auto-generated file tracking patterns across all your projects.",
|
|
128
|
-
`> Last updated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
129
|
-
"",
|
|
130
|
-
"## Summary",
|
|
131
|
-
"",
|
|
132
|
-
`- **Projects tracked:** ${projects.length}`,
|
|
133
|
-
`- **Total patterns:** ${patterns.length}`,
|
|
134
|
-
`- **Cross-project patterns:** ${crossProject.length}`,
|
|
135
|
-
"",
|
|
136
|
-
"## Cross-Project Patterns",
|
|
137
|
-
"",
|
|
138
|
-
"These issues appear in multiple projects:",
|
|
139
|
-
""
|
|
140
|
-
];
|
|
141
|
-
for (const p of crossProject.slice(0, 20)) {
|
|
142
|
-
lines.push(
|
|
143
|
-
`### ${p.pattern.slice(0, 60)}${p.pattern.length > 60 ? "..." : ""}`,
|
|
144
|
-
"",
|
|
145
|
-
`- **Severity:** ${p.severity}`,
|
|
146
|
-
`- **Agent:** ${p.agent}`,
|
|
147
|
-
`- **Occurrences:** ${p.occurrences} across ${p.projects.length} projects`,
|
|
148
|
-
`- **Projects:** ${p.projects.slice(0, 5).join(", ")}${p.projects.length > 5 ? "..." : ""}`
|
|
149
|
-
);
|
|
150
|
-
if (p.fixApplied) {
|
|
151
|
-
lines.push(`- **Fixed in:** ${p.fixApplied.project} on ${p.fixApplied.timestamp.split("T")[0]}`);
|
|
152
|
-
} else {
|
|
153
|
-
lines.push("- **Status:** Not fixed");
|
|
154
|
-
}
|
|
155
|
-
lines.push("");
|
|
156
|
-
}
|
|
157
|
-
lines.push(
|
|
158
|
-
"## Tracked Projects",
|
|
159
|
-
"",
|
|
160
|
-
"| Project | Last Scan | Issues |",
|
|
161
|
-
"|---------|-----------|--------|"
|
|
162
|
-
);
|
|
163
|
-
for (const p of projects.slice(0, 20)) {
|
|
164
|
-
lines.push(`| ${p.name} | ${p.lastScan.split("T")[0]} | ${p.totalIssues} |`);
|
|
165
|
-
}
|
|
166
|
-
lines.push("", "---", "", "*This file is auto-generated by Trie. Do not edit manually.*");
|
|
167
|
-
await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
168
|
-
await writeFile(join(GLOBAL_MEMORY_DIR, "GLOBAL_MEMORY.md"), lines.join("\n"));
|
|
169
|
-
}
|
|
170
|
-
async function searchGlobalPatterns(query, options = {}) {
|
|
171
|
-
const patterns = await loadGlobalPatterns();
|
|
172
|
-
const limit = options.limit || 10;
|
|
173
|
-
const queryTerms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 2);
|
|
174
|
-
const scored = patterns.filter((p) => {
|
|
175
|
-
if (options.severity && !options.severity.includes(p.severity)) return false;
|
|
176
|
-
if (options.agent && p.agent !== options.agent) return false;
|
|
177
|
-
return true;
|
|
178
|
-
}).map((p) => {
|
|
179
|
-
const text = `${p.pattern} ${p.description} ${p.agent}`.toLowerCase();
|
|
180
|
-
let score = 0;
|
|
181
|
-
for (const term of queryTerms) {
|
|
182
|
-
if (text.includes(term)) score++;
|
|
183
|
-
}
|
|
184
|
-
return { pattern: p, score };
|
|
185
|
-
}).filter((s) => s.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
186
|
-
return scored.map((s) => s.pattern);
|
|
187
|
-
}
|
|
188
|
-
async function loadGlobalPatterns() {
|
|
189
|
-
const patternsPath = join(GLOBAL_MEMORY_DIR, "global-patterns.json");
|
|
190
|
-
try {
|
|
191
|
-
if (existsSync(patternsPath)) {
|
|
192
|
-
const content = await readFile(patternsPath, "utf-8");
|
|
193
|
-
const result = safeParseAndValidate(content, GlobalPatternsIndexSchema);
|
|
194
|
-
if (result.success) {
|
|
195
|
-
return result.data;
|
|
196
|
-
}
|
|
197
|
-
const backupManager = new BackupManager(patternsPath);
|
|
198
|
-
if (await backupManager.recoverFromBackup()) {
|
|
199
|
-
const recovered = await readFile(patternsPath, "utf-8");
|
|
200
|
-
const recoveredResult = safeParseAndValidate(recovered, GlobalPatternsIndexSchema);
|
|
201
|
-
if (recoveredResult.success) {
|
|
202
|
-
return recoveredResult.data;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
} catch {
|
|
207
|
-
}
|
|
208
|
-
return [];
|
|
209
|
-
}
|
|
210
|
-
async function saveGlobalPatterns(patterns) {
|
|
211
|
-
await mkdir(GLOBAL_MEMORY_DIR, { recursive: true });
|
|
212
|
-
const patternsPath = join(GLOBAL_MEMORY_DIR, "global-patterns.json");
|
|
213
|
-
const patternMap = /* @__PURE__ */ new Map();
|
|
214
|
-
for (const pattern of patterns) {
|
|
215
|
-
const existing = patternMap.get(pattern.id);
|
|
216
|
-
if (existing) {
|
|
217
|
-
existing.occurrences += pattern.occurrences;
|
|
218
|
-
for (const proj of pattern.projects) {
|
|
219
|
-
if (!existing.projects.includes(proj)) {
|
|
220
|
-
existing.projects.push(proj);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
existing.lastSeen = pattern.lastSeen > existing.lastSeen ? pattern.lastSeen : existing.lastSeen;
|
|
224
|
-
} else {
|
|
225
|
-
patternMap.set(pattern.id, pattern);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
const deduplicated = Array.from(patternMap.values());
|
|
229
|
-
const pruned = intelligentPruneGlobalPatterns(deduplicated, 500);
|
|
230
|
-
const backupManager = new BackupManager(patternsPath);
|
|
231
|
-
await backupManager.createBackup();
|
|
232
|
-
await atomicWriteJSON(patternsPath, pruned);
|
|
233
|
-
}
|
|
234
|
-
function intelligentPruneGlobalPatterns(patterns, targetCount) {
|
|
235
|
-
if (patterns.length <= targetCount) {
|
|
236
|
-
return patterns;
|
|
237
|
-
}
|
|
238
|
-
const severityWeight = {
|
|
239
|
-
critical: 100,
|
|
240
|
-
high: 50,
|
|
241
|
-
moderate: 20,
|
|
242
|
-
low: 10,
|
|
243
|
-
info: 5
|
|
244
|
-
};
|
|
245
|
-
const scored = patterns.map((pattern) => {
|
|
246
|
-
const ageInDays = (Date.now() - new Date(pattern.lastSeen).getTime()) / (1e3 * 60 * 60 * 24);
|
|
247
|
-
const recencyScore = Math.max(0, 100 - ageInDays * 2);
|
|
248
|
-
const severityScore = severityWeight[pattern.severity] || 10;
|
|
249
|
-
const crossProjectBonus = (pattern.projects.length - 1) * 30;
|
|
250
|
-
const fixedBonus = pattern.fixApplied ? 20 : 0;
|
|
251
|
-
const occurrenceScore = Math.min(pattern.occurrences * 2, 100);
|
|
252
|
-
return {
|
|
253
|
-
pattern,
|
|
254
|
-
score: recencyScore + severityScore + crossProjectBonus + fixedBonus + occurrenceScore
|
|
255
|
-
};
|
|
256
|
-
});
|
|
257
|
-
return scored.sort((a, b) => b.score - a.score).slice(0, targetCount).map((s) => s.pattern);
|
|
258
|
-
}
|
|
259
|
-
function extractPatternId(issue) {
|
|
260
|
-
const normalized = issue.issue.toLowerCase().replace(/`[^`]+`/g, "CODE").replace(/\b\d+\b/g, "N").replace(/['"]/g, "").slice(0, 100);
|
|
261
|
-
const hash = createHash("sha256").update(normalized).digest("hex").slice(0, 12);
|
|
262
|
-
return `${issue.agent}-${issue.severity}-${hash}`;
|
|
263
|
-
}
|
|
264
|
-
function sanitizeName(name) {
|
|
265
|
-
return name.replace(/[^a-zA-Z0-9-_]/g, "-").toLowerCase();
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// src/agent/gotcha-predictor.ts
|
|
269
|
-
import fs from "fs";
|
|
270
|
-
import path from "path";
|
|
271
|
-
var GotchaPredictor = class {
|
|
272
|
-
projectPath;
|
|
273
|
-
graph;
|
|
274
|
-
constructor(projectPath, graph) {
|
|
275
|
-
this.projectPath = projectPath;
|
|
276
|
-
this.graph = graph;
|
|
277
|
-
}
|
|
278
|
-
async predictGotchas(changedFiles) {
|
|
279
|
-
const gotchas = [];
|
|
280
|
-
const tickets = (await this.graph.listNodes()).filter((n) => n.type === "linear-ticket");
|
|
281
|
-
for (const file of changedFiles) {
|
|
282
|
-
const fileGotchas = await this.predictForFile(file, tickets);
|
|
283
|
-
gotchas.push(...fileGotchas);
|
|
284
|
-
}
|
|
285
|
-
return gotchas;
|
|
286
|
-
}
|
|
287
|
-
async predictForFile(filePath, tickets) {
|
|
288
|
-
const gotchas = [];
|
|
289
|
-
const fullPath = path.resolve(this.projectPath, filePath);
|
|
290
|
-
if (!fs.existsSync(fullPath)) return [];
|
|
291
|
-
const content = fs.readFileSync(fullPath, "utf-8");
|
|
292
|
-
const storage = getStorage(this.projectPath);
|
|
293
|
-
await storage.initialize();
|
|
294
|
-
const tags = this.extractTagsFromFile(filePath, []);
|
|
295
|
-
const relevantGovernance = await storage.queryGovernance({
|
|
296
|
-
tags,
|
|
297
|
-
limit: 10
|
|
298
|
-
});
|
|
299
|
-
const activeBlockers = await storage.queryBlockers({
|
|
300
|
-
tags,
|
|
301
|
-
limit: 5
|
|
302
|
-
});
|
|
303
|
-
for (const gov of relevantGovernance) {
|
|
304
|
-
if (gov.files.some((f) => f.includes(filePath) || filePath.includes(f))) {
|
|
305
|
-
gotchas.push({
|
|
306
|
-
id: `gotcha-gov-${gov.id}`,
|
|
307
|
-
message: `Governance: ${gov.decision}`,
|
|
308
|
-
confidence: 0.85,
|
|
309
|
-
riskLevel: "medium",
|
|
310
|
-
precedentId: gov.id,
|
|
311
|
-
recommendation: gov.reasoning || "Review this governance before making changes",
|
|
312
|
-
evidence: {
|
|
313
|
-
pastIncidents: [],
|
|
314
|
-
matchingPatterns: gov.tags,
|
|
315
|
-
relatedTickets: []
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
for (const blocker of activeBlockers) {
|
|
321
|
-
gotchas.push({
|
|
322
|
-
id: `gotcha-blocker-${blocker.id}`,
|
|
323
|
-
message: `\u26A0\uFE0F Active Blocker: ${blocker.blocker}`,
|
|
324
|
-
confidence: 0.95,
|
|
325
|
-
riskLevel: blocker.impact === "critical" ? "critical" : blocker.impact === "high" ? "high" : "medium",
|
|
326
|
-
recommendation: `This area is currently blocked. Consider resolving this before making changes.`,
|
|
327
|
-
evidence: {
|
|
328
|
-
pastIncidents: [],
|
|
329
|
-
matchingPatterns: blocker.tags,
|
|
330
|
-
relatedTickets: []
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
const aiGotchas = await this.analyzeCodeWithAI(filePath, content, relevantGovernance, activeBlockers);
|
|
335
|
-
gotchas.push(...aiGotchas);
|
|
336
|
-
for (const ticket of tickets) {
|
|
337
|
-
const ticketData = ticket.data;
|
|
338
|
-
const historicalPrecedents = await this.findHistoricalPrecedents(filePath, ticketData);
|
|
339
|
-
if (historicalPrecedents) {
|
|
340
|
-
gotchas.push(historicalPrecedents);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return gotchas;
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Use AI to analyze code for potential issues
|
|
347
|
-
*/
|
|
348
|
-
async analyzeCodeWithAI(filePath, content, governance, blockers) {
|
|
349
|
-
const client = tryGetClient();
|
|
350
|
-
if (!client) return [];
|
|
351
|
-
try {
|
|
352
|
-
const governanceContext = governance.length > 0 ? `
|
|
353
|
-
|
|
354
|
-
Relevant Governance:
|
|
355
|
-
${governance.map((g) => `- ${g.decision}: ${g.reasoning || "No reasoning provided"}`).join("\n")}` : "";
|
|
356
|
-
const blockerContext = blockers.length > 0 ? `
|
|
357
|
-
|
|
358
|
-
Active Blockers:
|
|
359
|
-
${blockers.map((b) => `- [${b.impact}] ${b.blocker}`).join("\n")}` : "";
|
|
360
|
-
const systemPrompt = `You are a senior code reviewer analyzing code for potential issues before changes are shipped.
|
|
361
|
-
|
|
362
|
-
Given a file and its context (governance decisions and blockers), identify:
|
|
363
|
-
1. Potential bugs or security issues
|
|
364
|
-
2. Violations of governance decisions
|
|
365
|
-
3. Conflicts with active blockers
|
|
366
|
-
4. Anti-patterns or code smells
|
|
367
|
-
|
|
368
|
-
Return your analysis as a JSON array of issues, each with:
|
|
369
|
-
{
|
|
370
|
-
"message": "Brief description of the issue",
|
|
371
|
-
"confidence": 0.0-1.0,
|
|
372
|
-
"riskLevel": "low" | "medium" | "high" | "critical",
|
|
373
|
-
"recommendation": "Specific recommendation",
|
|
374
|
-
"lineNumber": number (if applicable)
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
IMPORTANT: Only return JSON, no other text. If no issues found, return [].`;
|
|
378
|
-
const userPrompt = `File: ${filePath}${governanceContext}${blockerContext}
|
|
379
|
-
|
|
380
|
-
Code:
|
|
381
|
-
\`\`\`
|
|
382
|
-
${content.slice(0, 5e3)}
|
|
383
|
-
\`\`\`
|
|
384
|
-
|
|
385
|
-
Analyze this code and return a JSON array of potential issues.`;
|
|
386
|
-
const response = await client.messages.create({
|
|
387
|
-
model: "claude-sonnet-4-20250514",
|
|
388
|
-
max_tokens: 4096,
|
|
389
|
-
temperature: 0.2,
|
|
390
|
-
system: systemPrompt,
|
|
391
|
-
messages: [{ role: "user", content: userPrompt }]
|
|
392
|
-
});
|
|
393
|
-
const textContent = response.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
394
|
-
const jsonMatch = textContent.match(/\[[\s\S]*\]/);
|
|
395
|
-
if (!jsonMatch) return [];
|
|
396
|
-
const issues = JSON.parse(jsonMatch[0]);
|
|
397
|
-
return issues.map((issue, idx) => ({
|
|
398
|
-
id: `gotcha-ai-${Date.now()}-${idx}`,
|
|
399
|
-
message: issue.message || "AI-detected issue",
|
|
400
|
-
confidence: issue.confidence || 0.7,
|
|
401
|
-
riskLevel: issue.riskLevel || "medium",
|
|
402
|
-
recommendation: issue.recommendation || "Review this code carefully",
|
|
403
|
-
evidence: {
|
|
404
|
-
pastIncidents: [],
|
|
405
|
-
matchingPatterns: ["ai-analysis"],
|
|
406
|
-
relatedTickets: []
|
|
407
|
-
}
|
|
408
|
-
}));
|
|
409
|
-
} catch (error) {
|
|
410
|
-
return [];
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
/**
|
|
414
|
-
* Extract tags from file path for storage queries
|
|
415
|
-
*/
|
|
416
|
-
extractTagsFromFile(filePath, _signatures) {
|
|
417
|
-
const tags = /* @__PURE__ */ new Set();
|
|
418
|
-
const normalized = filePath.toLowerCase();
|
|
419
|
-
if (normalized.includes("/auth/")) tags.add("auth");
|
|
420
|
-
if (normalized.includes("/payment/")) tags.add("payments");
|
|
421
|
-
if (normalized.includes("/api/")) tags.add("api");
|
|
422
|
-
if (normalized.includes("/frontend/") || normalized.includes("/ui/")) tags.add("ui");
|
|
423
|
-
if (normalized.includes("/backend/")) tags.add("backend");
|
|
424
|
-
if (normalized.includes("/database/") || normalized.includes("/models/")) tags.add("database");
|
|
425
|
-
return Array.from(tags);
|
|
426
|
-
}
|
|
427
|
-
async findHistoricalPrecedents(filePath, ticket) {
|
|
428
|
-
const similarIssues = await searchIssues(ticket.description, {
|
|
429
|
-
workDir: this.projectPath,
|
|
430
|
-
limit: 3
|
|
431
|
-
});
|
|
432
|
-
const relevantIssues = similarIssues.filter((r) => r.issue.file === filePath || r.issue.file.includes(path.basename(filePath)));
|
|
433
|
-
const [firstMatch] = relevantIssues;
|
|
434
|
-
if (firstMatch) {
|
|
435
|
-
const issue = firstMatch.issue;
|
|
436
|
-
return {
|
|
437
|
-
id: `gotcha-precedent-${ticket.ticketId}-${Date.now()}`,
|
|
438
|
-
message: `A similar task in the past caused an issue: "${issue.issue}"`,
|
|
439
|
-
confidence: 0.9,
|
|
440
|
-
riskLevel: "critical",
|
|
441
|
-
precedentId: issue.id,
|
|
442
|
-
recommendation: `Last time we worked on something similar here, we had to fix: "${issue.fix}". Check this first.`,
|
|
443
|
-
evidence: {
|
|
444
|
-
pastIncidents: [issue.id],
|
|
445
|
-
matchingPatterns: [],
|
|
446
|
-
relatedTickets: [ticket.ticketId]
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
return null;
|
|
451
|
-
}
|
|
452
|
-
async synthesizeGotchaExplanation(gotcha) {
|
|
453
|
-
const client = tryGetClient();
|
|
454
|
-
if (!client) return gotcha.message;
|
|
455
|
-
const prompt = `
|
|
456
|
-
You are a JIT Defect Predictor. You found a potential "gotcha" for a developer.
|
|
457
|
-
|
|
458
|
-
Ticket context: ${gotcha.evidence.relatedTickets.join(", ")}
|
|
459
|
-
Tags: ${gotcha.evidence.matchingPatterns.join(", ")}
|
|
460
|
-
Past incidents: ${gotcha.evidence.pastIncidents.join(", ")}
|
|
461
|
-
|
|
462
|
-
Raw message: ${gotcha.message}
|
|
463
|
-
Recommendation: ${gotcha.recommendation}
|
|
464
|
-
|
|
465
|
-
Explain this gotcha in a concise, human-friendly way (max 2 sentences).
|
|
466
|
-
Make it sound like a senior dev giving a helpful nudge.
|
|
467
|
-
`;
|
|
468
|
-
try {
|
|
469
|
-
const response = await client.messages.create({
|
|
470
|
-
model: "claude-3-5-sonnet-20240620",
|
|
471
|
-
max_tokens: 100,
|
|
472
|
-
messages: [{ role: "user", content: prompt }]
|
|
473
|
-
});
|
|
474
|
-
const text = response.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
475
|
-
return text.trim() || gotcha.message;
|
|
476
|
-
} catch {
|
|
477
|
-
return gotcha.message;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
};
|
|
481
|
-
|
|
482
|
-
// src/integrations/slack.ts
|
|
483
|
-
var SlackIntegration = class {
|
|
484
|
-
constructor(config) {
|
|
485
|
-
this.config = config;
|
|
486
|
-
}
|
|
487
|
-
/**
|
|
488
|
-
* Send scan completion notification
|
|
489
|
-
*/
|
|
490
|
-
async sendScanNotification(_issues, priorityReport, repositoryName, branch = "main") {
|
|
491
|
-
const { urgent, high, medium, low } = priorityReport;
|
|
492
|
-
const statusEmoji = urgent.length > 0 ? "[URGENT]" : high.length > 0 ? "[HIGH]" : "[OK]";
|
|
493
|
-
const message = {
|
|
494
|
-
blocks: [
|
|
495
|
-
{
|
|
496
|
-
type: "section",
|
|
497
|
-
text: {
|
|
498
|
-
type: "mrkdwn",
|
|
499
|
-
text: `${statusEmoji} *Trie Security Scan Complete*
|
|
500
|
-
*Repository:* ${repositoryName} (${branch})`
|
|
501
|
-
}
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
type: "section",
|
|
505
|
-
fields: [
|
|
506
|
-
{
|
|
507
|
-
type: "mrkdwn",
|
|
508
|
-
text: `*Urgent:* ${urgent.length}`
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
type: "mrkdwn",
|
|
512
|
-
text: `*High:* ${high.length}`
|
|
513
|
-
},
|
|
514
|
-
{
|
|
515
|
-
type: "mrkdwn",
|
|
516
|
-
text: `*Medium:* ${medium.length}`
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
type: "mrkdwn",
|
|
520
|
-
text: `*\u{1F539} Low:* ${low.length}`
|
|
521
|
-
}
|
|
522
|
-
]
|
|
523
|
-
}
|
|
524
|
-
],
|
|
525
|
-
attachments: []
|
|
526
|
-
};
|
|
527
|
-
if (urgent.length > 0) {
|
|
528
|
-
const urgentDetails = urgent.slice(0, 5).map(
|
|
529
|
-
(group) => `\u2022 ${group.description} (${group.count} instances)`
|
|
530
|
-
).join("\n");
|
|
531
|
-
message.attachments.push({
|
|
532
|
-
color: "danger",
|
|
533
|
-
title: "Urgent Issues - Immediate Action Required",
|
|
534
|
-
text: urgentDetails,
|
|
535
|
-
footer: urgent.length > 5 ? `... and ${urgent.length - 5} more urgent issues` : void 0
|
|
536
|
-
});
|
|
537
|
-
}
|
|
538
|
-
if (high.length > 0) {
|
|
539
|
-
const highDetails = high.slice(0, 3).map(
|
|
540
|
-
(group) => `\u2022 ${group.description} (${group.count} instances)`
|
|
541
|
-
).join("\n");
|
|
542
|
-
message.attachments.push({
|
|
543
|
-
color: "warning",
|
|
544
|
-
title: "High Priority Issues",
|
|
545
|
-
text: highDetails,
|
|
546
|
-
footer: high.length > 3 ? `... and ${high.length - 3} more high priority issues` : void 0
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
if (priorityReport.recommendations.length > 0) {
|
|
550
|
-
message.attachments.push({
|
|
551
|
-
color: "good",
|
|
552
|
-
title: "Recommendations",
|
|
553
|
-
text: priorityReport.recommendations.slice(0, 3).join("\n")
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
await this.sendMessage(message);
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* Send critical issue alert
|
|
560
|
-
*/
|
|
561
|
-
async sendCriticalAlert(issues, repositoryName) {
|
|
562
|
-
const message = {
|
|
563
|
-
text: `CRITICAL SECURITY ALERT: ${repositoryName}`,
|
|
564
|
-
blocks: [
|
|
565
|
-
{
|
|
566
|
-
type: "section",
|
|
567
|
-
text: {
|
|
568
|
-
type: "mrkdwn",
|
|
569
|
-
text: `*CRITICAL SECURITY ALERT*
|
|
570
|
-
*Repository:* ${repositoryName}
|
|
571
|
-
*Critical Issues:* ${issues.length}`
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
],
|
|
575
|
-
attachments: issues.slice(0, 5).map((issue) => ({
|
|
576
|
-
color: "danger",
|
|
577
|
-
title: `${issue.file}:${issue.line || "?"}`,
|
|
578
|
-
text: issue.issue.slice(0, 200),
|
|
579
|
-
fields: [
|
|
580
|
-
{
|
|
581
|
-
title: "Fix",
|
|
582
|
-
value: issue.fix.slice(0, 100),
|
|
583
|
-
short: false
|
|
584
|
-
}
|
|
585
|
-
],
|
|
586
|
-
footer: `Agent: ${issue.agent}`,
|
|
587
|
-
ts: Math.floor(Date.now() / 1e3)
|
|
588
|
-
}))
|
|
589
|
-
};
|
|
590
|
-
if (issues.length > 5) {
|
|
591
|
-
message.attachments.push({
|
|
592
|
-
color: "danger",
|
|
593
|
-
text: `... and ${issues.length - 5} more critical issues. View full report for details.`
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
await this.sendMessage(message);
|
|
597
|
-
}
|
|
598
|
-
/**
|
|
599
|
-
* Send team notification
|
|
600
|
-
*/
|
|
601
|
-
async sendTeamNotification(notification) {
|
|
602
|
-
const emoji = this.getNotificationEmoji(notification.type);
|
|
603
|
-
const message = {
|
|
604
|
-
blocks: [
|
|
605
|
-
{
|
|
606
|
-
type: "section",
|
|
607
|
-
text: {
|
|
608
|
-
type: "mrkdwn",
|
|
609
|
-
text: `${emoji} *${notification.title}*
|
|
610
|
-
${notification.message}`
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
]
|
|
614
|
-
};
|
|
615
|
-
if (notification.type === "assignment" && notification.data) {
|
|
616
|
-
const { assignment, issue } = notification.data;
|
|
617
|
-
message.attachments = [{
|
|
618
|
-
color: this.getAssignmentColor(assignment.priority),
|
|
619
|
-
fields: [
|
|
620
|
-
{
|
|
621
|
-
title: "Priority",
|
|
622
|
-
value: assignment.priority.toUpperCase(),
|
|
623
|
-
short: true
|
|
624
|
-
},
|
|
625
|
-
{
|
|
626
|
-
title: "Due Date",
|
|
627
|
-
value: assignment.dueDate ? new Date(assignment.dueDate).toLocaleDateString() : "Not set",
|
|
628
|
-
short: true
|
|
629
|
-
},
|
|
630
|
-
{
|
|
631
|
-
title: "File",
|
|
632
|
-
value: issue.file,
|
|
633
|
-
short: false
|
|
634
|
-
}
|
|
635
|
-
]
|
|
636
|
-
}];
|
|
637
|
-
}
|
|
638
|
-
await this.sendMessage(message);
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Send daily/weekly team summary
|
|
642
|
-
*/
|
|
643
|
-
async sendTeamSummary(period, stats) {
|
|
644
|
-
const emoji = period === "daily" ? "[DAILY]" : "[WEEKLY]";
|
|
645
|
-
const title = `${emoji} ${period.charAt(0).toUpperCase() + period.slice(1)} Security Summary`;
|
|
646
|
-
const message = {
|
|
647
|
-
blocks: [
|
|
648
|
-
{
|
|
649
|
-
type: "section",
|
|
650
|
-
text: {
|
|
651
|
-
type: "mrkdwn",
|
|
652
|
-
text: `*${title}*`
|
|
653
|
-
}
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
type: "section",
|
|
657
|
-
fields: [
|
|
658
|
-
{
|
|
659
|
-
type: "mrkdwn",
|
|
660
|
-
text: `*New Issues:* ${stats.newIssues}`
|
|
661
|
-
},
|
|
662
|
-
{
|
|
663
|
-
type: "mrkdwn",
|
|
664
|
-
text: `*\u2705 Resolved:* ${stats.resolvedIssues}`
|
|
665
|
-
},
|
|
666
|
-
{
|
|
667
|
-
type: "mrkdwn",
|
|
668
|
-
text: `*Overdue:* ${stats.overdueIssues}`
|
|
669
|
-
},
|
|
670
|
-
{
|
|
671
|
-
type: "mrkdwn",
|
|
672
|
-
text: `*Net Change:* ${stats.resolvedIssues - stats.newIssues > 0 ? "+" : ""}${stats.resolvedIssues - stats.newIssues}`
|
|
673
|
-
}
|
|
674
|
-
]
|
|
675
|
-
}
|
|
676
|
-
],
|
|
677
|
-
attachments: []
|
|
678
|
-
};
|
|
679
|
-
if (stats.topCategories.length > 0) {
|
|
680
|
-
const categoriesText = stats.topCategories.slice(0, 5).map((cat) => `\u2022 ${cat.category}: ${cat.count}`).join("\n");
|
|
681
|
-
message.attachments.push({
|
|
682
|
-
color: "good",
|
|
683
|
-
title: "Top Issue Categories",
|
|
684
|
-
text: categoriesText
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
if (stats.topContributors.length > 0) {
|
|
688
|
-
const contributorsText = stats.topContributors.slice(0, 5).map((contrib) => `\u2022 ${contrib.name}: ${contrib.resolved} resolved`).join("\n");
|
|
689
|
-
message.attachments.push({
|
|
690
|
-
color: "good",
|
|
691
|
-
title: "Top Contributors",
|
|
692
|
-
text: contributorsText
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
await this.sendMessage(message);
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* Send bulk fix notification
|
|
699
|
-
*/
|
|
700
|
-
async sendBulkFixNotification(fixedGroups, totalFixed, repositoryName) {
|
|
701
|
-
const message = {
|
|
702
|
-
blocks: [
|
|
703
|
-
{
|
|
704
|
-
type: "section",
|
|
705
|
-
text: {
|
|
706
|
-
type: "mrkdwn",
|
|
707
|
-
text: `*Bulk Fix Applied*
|
|
708
|
-
*Repository:* ${repositoryName}
|
|
709
|
-
*Issues Fixed:* ${totalFixed}`
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
],
|
|
713
|
-
attachments: [{
|
|
714
|
-
color: "good",
|
|
715
|
-
title: "\u{1F527} Fixed Issue Groups",
|
|
716
|
-
text: fixedGroups.map(
|
|
717
|
-
(group) => `\u2022 ${group.description} (${group.count} instances)`
|
|
718
|
-
).join("\n")
|
|
719
|
-
}]
|
|
720
|
-
};
|
|
721
|
-
await this.sendMessage(message);
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Send escalation notification
|
|
725
|
-
*/
|
|
726
|
-
async sendEscalationNotification(overdueAssignments) {
|
|
727
|
-
const message = {
|
|
728
|
-
text: "OVERDUE ISSUE ESCALATION",
|
|
729
|
-
blocks: [
|
|
730
|
-
{
|
|
731
|
-
type: "section",
|
|
732
|
-
text: {
|
|
733
|
-
type: "mrkdwn",
|
|
734
|
-
text: `*Overdue Issue Escalation*
|
|
735
|
-
${overdueAssignments.length} issues are overdue and require attention.`
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
],
|
|
739
|
-
attachments: overdueAssignments.map((assignment) => ({
|
|
740
|
-
color: "warning",
|
|
741
|
-
title: `Issue ${assignment.issueId}`,
|
|
742
|
-
fields: [
|
|
743
|
-
{
|
|
744
|
-
title: "Assignee",
|
|
745
|
-
value: assignment.assignee,
|
|
746
|
-
short: true
|
|
747
|
-
},
|
|
748
|
-
{
|
|
749
|
-
title: "Days Overdue",
|
|
750
|
-
value: assignment.daysOverdue.toString(),
|
|
751
|
-
short: true
|
|
752
|
-
},
|
|
753
|
-
{
|
|
754
|
-
title: "Priority",
|
|
755
|
-
value: assignment.priority.toUpperCase(),
|
|
756
|
-
short: true
|
|
757
|
-
}
|
|
758
|
-
]
|
|
759
|
-
}))
|
|
760
|
-
};
|
|
761
|
-
await this.sendMessage(message);
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Send message to Slack
|
|
765
|
-
*/
|
|
766
|
-
async sendMessage(message) {
|
|
767
|
-
const payload = {
|
|
768
|
-
...message,
|
|
769
|
-
channel: this.config.channel || message.channel,
|
|
770
|
-
username: this.config.username || "Trie Security Bot",
|
|
771
|
-
icon_emoji: this.config.iconEmoji || ":shield:"
|
|
772
|
-
};
|
|
773
|
-
try {
|
|
774
|
-
const response = await fetch(this.config.webhookUrl, {
|
|
775
|
-
method: "POST",
|
|
776
|
-
headers: {
|
|
777
|
-
"Content-Type": "application/json"
|
|
778
|
-
},
|
|
779
|
-
body: JSON.stringify(payload)
|
|
780
|
-
});
|
|
781
|
-
if (!response.ok) {
|
|
782
|
-
throw new Error(`Slack API error: ${response.status} ${response.statusText}`);
|
|
783
|
-
}
|
|
784
|
-
} catch (error) {
|
|
785
|
-
console.error("Failed to send Slack notification:", error);
|
|
786
|
-
throw error;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
/**
|
|
790
|
-
* Get emoji for notification type
|
|
791
|
-
*/
|
|
792
|
-
getNotificationEmoji(type) {
|
|
793
|
-
const emojis = {
|
|
794
|
-
assignment: "[ASSIGN]",
|
|
795
|
-
escalation: "[ESCALATE]",
|
|
796
|
-
completion: "[DONE]",
|
|
797
|
-
reminder: "[REMIND]"
|
|
798
|
-
};
|
|
799
|
-
return emojis[type] ?? "[NOTIFY]";
|
|
800
|
-
}
|
|
801
|
-
/**
|
|
802
|
-
* Get color for assignment priority
|
|
803
|
-
*/
|
|
804
|
-
getAssignmentColor(priority) {
|
|
805
|
-
const colors = {
|
|
806
|
-
urgent: "danger",
|
|
807
|
-
high: "warning",
|
|
808
|
-
medium: "good",
|
|
809
|
-
low: "#36a64f"
|
|
810
|
-
};
|
|
811
|
-
return colors[priority] ?? "good";
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Test Slack connection
|
|
815
|
-
*/
|
|
816
|
-
async testConnection() {
|
|
817
|
-
try {
|
|
818
|
-
await this.sendMessage({
|
|
819
|
-
text: "Trie Slack Integration Test",
|
|
820
|
-
blocks: [{
|
|
821
|
-
type: "section",
|
|
822
|
-
text: {
|
|
823
|
-
type: "mrkdwn",
|
|
824
|
-
text: "*Trie Slack Integration Test*\nIf you see this message, the integration is working correctly!"
|
|
825
|
-
}
|
|
826
|
-
}]
|
|
827
|
-
});
|
|
828
|
-
return true;
|
|
829
|
-
} catch (error) {
|
|
830
|
-
console.error("Slack connection test failed:", error);
|
|
831
|
-
return false;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
};
|
|
835
|
-
|
|
836
|
-
export {
|
|
837
|
-
recordToGlobalMemory,
|
|
838
|
-
findCrossProjectPatterns,
|
|
839
|
-
listTrackedProjects,
|
|
840
|
-
getGlobalMemoryStats,
|
|
841
|
-
updateGlobalMemoryMd,
|
|
842
|
-
searchGlobalPatterns,
|
|
843
|
-
SlackIntegration,
|
|
844
|
-
GotchaPredictor
|
|
845
|
-
};
|
|
846
|
-
//# sourceMappingURL=chunk-AHD2CBQ7.js.map
|