@a-company/paradigm 3.0.3 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{triage-RM5KNG5V.js → chunk-4LGLU2LO.js} +1035 -663
- package/dist/{chunk-4WR7X3FE.js → chunk-AQZSUGL3.js} +42 -6
- package/dist/{chunk-27OSFWHG.js → chunk-MVXJVRFI.js} +98 -1
- package/dist/{chunk-S65LENNL.js → chunk-VZ7CXFRZ.js} +248 -3
- package/dist/delete-W67IVTLJ.js +45 -0
- package/dist/dist-GPQ4LAY3.js +42 -0
- package/dist/edit-Y7XPYSMK.js +63 -0
- package/dist/habits-FA65W77Y.js +1153 -0
- package/dist/{hooks-7TQIRXXS.js → hooks-YXPQV4SP.js} +1 -1
- package/dist/index.js +84 -31
- package/dist/{list-QMUE7DPK.js → list-R3QWW4SC.js} +3 -1
- package/dist/{lore-server-3TAIUZ3Y.js → lore-server-RQH5REZV.js} +166 -41
- package/dist/mcp.js +1608 -117
- package/dist/{record-5CTCDFUO.js → record-OHQNWOUP.js} +7 -2
- package/dist/{review-QEDNQAIO.js → review-RUHX25A5.js} +1 -1
- package/dist/{sentinel-RSEXIRXM.js → sentinel-WB7GIK4V.js} +1 -1
- package/dist/{serve-WCIRW244.js → serve-H7ZBMODT.js} +1 -1
- package/dist/{server-NXG5N7JE.js → server-MV4HNFVF.js} +1 -1
- package/dist/{shift-NABNKPGL.js → shift-JDBRTHWO.js} +1 -1
- package/dist/{show-S653P3TO.js → show-WTOJXUTN.js} +1 -1
- package/dist/timeline-P7BARFLI.js +110 -0
- package/dist/triage-TBIWJA6R.js +671 -0
- package/dist/university-content/courses/para-401.json +1 -1
- package/dist/university-content/courses/para-501.json +486 -0
- package/dist/university-content/plsat/v3.0.json +233 -0
- package/dist/university-content/reference.json +61 -0
- package/lore-ui/dist/assets/index-BB3P4Cok.js +56 -0
- package/lore-ui/dist/assets/index-DI0Q6NmX.css +1 -0
- package/lore-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/lore-ui/dist/assets/index-DcT8TINz.js +0 -56
- package/lore-ui/dist/assets/index-DyJhpQ5w.css +0 -1
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
PatternImporter,
|
|
4
|
+
PatternMatcher,
|
|
5
|
+
PatternSuggester,
|
|
6
|
+
StatsCalculator,
|
|
7
|
+
TimelineBuilder,
|
|
8
|
+
loadAllSeedPatterns
|
|
9
|
+
} from "./chunk-4LGLU2LO.js";
|
|
10
|
+
import {
|
|
11
|
+
SentinelStorage
|
|
12
|
+
} from "./chunk-VZ7CXFRZ.js";
|
|
13
|
+
import "./chunk-MO4EEYFW.js";
|
|
14
|
+
|
|
15
|
+
// src/commands/triage/index.ts
|
|
16
|
+
import chalk2 from "chalk";
|
|
17
|
+
import ora from "ora";
|
|
18
|
+
|
|
19
|
+
// src/commands/triage/utils/format.ts
|
|
20
|
+
import chalk from "chalk";
|
|
21
|
+
function formatHeader() {
|
|
22
|
+
return `
|
|
23
|
+
${chalk.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
|
|
24
|
+
${chalk.cyan("\u2551")} ${chalk.bold.white("PARADIGM SENTINEL TRIAGE")} ${chalk.cyan("\u2551")}
|
|
25
|
+
${chalk.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
28
|
+
function formatSummaryBar(stats) {
|
|
29
|
+
return `${chalk.cyan("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557")}
|
|
30
|
+
${chalk.cyan("\u2551")} Open: ${chalk.yellow(String(stats.open).padEnd(4))} \u2502 Investigating: ${chalk.blue(String(stats.investigating).padEnd(3))} \u2502 Resolved: ${chalk.green(String(stats.resolved).padEnd(4))} \u2502 Today: ${chalk.magenta(`+${stats.today}`)} ${chalk.cyan("\u2551")}
|
|
31
|
+
${chalk.cyan("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D")}
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
function formatIncident(incident, matches) {
|
|
35
|
+
const lines = [];
|
|
36
|
+
const statusColor = getStatusColor(incident.status);
|
|
37
|
+
lines.push(
|
|
38
|
+
chalk.gray("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510")
|
|
39
|
+
);
|
|
40
|
+
lines.push(
|
|
41
|
+
chalk.gray("\u2502 ") + chalk.bold(`[${incident.id}] `) + statusColor(incident.status.toUpperCase().padEnd(12)) + chalk.gray(incident.timestamp.substring(0, 19).replace("T", " ").padStart(19)) + chalk.gray(" \u2502")
|
|
42
|
+
);
|
|
43
|
+
lines.push(
|
|
44
|
+
chalk.gray("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524")
|
|
45
|
+
);
|
|
46
|
+
lines.push(chalk.gray("\u2502 ") + chalk.red("Error: ") + truncate(incident.error.message, 55) + chalk.gray(" \u2502"));
|
|
47
|
+
lines.push(chalk.gray("\u2502") + " ".repeat(65) + chalk.gray("\u2502"));
|
|
48
|
+
lines.push(chalk.gray("\u2502 ") + chalk.cyan("Symbolic Context:") + " ".repeat(47) + chalk.gray("\u2502"));
|
|
49
|
+
const symbols = formatSymbols(incident.symbols);
|
|
50
|
+
for (const sym of symbols) {
|
|
51
|
+
lines.push(chalk.gray("\u2502 ") + sym.padEnd(61) + chalk.gray(" \u2502"));
|
|
52
|
+
}
|
|
53
|
+
lines.push(chalk.gray("\u2502") + " ".repeat(65) + chalk.gray("\u2502"));
|
|
54
|
+
const envLine = `Environment: ${chalk.yellow(incident.environment)} \u2502 Service: ${chalk.yellow(incident.service || "N/A")} \u2502 v${incident.version || "N/A"}`;
|
|
55
|
+
lines.push(chalk.gray("\u2502 ") + envLine.substring(0, 63).padEnd(63) + chalk.gray(" \u2502"));
|
|
56
|
+
if (matches && matches.length > 0) {
|
|
57
|
+
lines.push(chalk.gray("\u2502") + " ".repeat(65) + chalk.gray("\u2502"));
|
|
58
|
+
lines.push(
|
|
59
|
+
chalk.gray("\u2502 \u250C\u2500 ") + chalk.cyan("Matched Patterns") + chalk.gray(" \u2500".repeat(22) + "\u2510 \u2502")
|
|
60
|
+
);
|
|
61
|
+
for (let i = 0; i < Math.min(matches.length, 3); i++) {
|
|
62
|
+
const match = matches[i];
|
|
63
|
+
const icon = i === 0 ? chalk.yellow("\u2605") : chalk.gray("\u25CB");
|
|
64
|
+
const conf = `${match.confidence}% confidence`;
|
|
65
|
+
lines.push(
|
|
66
|
+
chalk.gray("\u2502 \u2502 ") + icon + " " + chalk.bold(truncate(match.pattern.id, 30).padEnd(30)) + chalk.gray(conf.padStart(15)) + chalk.gray(" \u2502 \u2502")
|
|
67
|
+
);
|
|
68
|
+
lines.push(
|
|
69
|
+
chalk.gray("\u2502 \u2502 ") + chalk.italic(truncate(match.pattern.description, 45).padEnd(45)) + chalk.gray(" \u2502 \u2502")
|
|
70
|
+
);
|
|
71
|
+
lines.push(
|
|
72
|
+
chalk.gray("\u2502 \u2502 Strategy: ") + chalk.cyan(match.pattern.resolution.strategy.padEnd(40)) + chalk.gray(" \u2502 \u2502")
|
|
73
|
+
);
|
|
74
|
+
if (i < Math.min(matches.length, 3) - 1) {
|
|
75
|
+
lines.push(chalk.gray("\u2502 \u2502") + " ".repeat(59) + chalk.gray("\u2502 \u2502"));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
lines.push(
|
|
79
|
+
chalk.gray("\u2502 \u2514") + "\u2500".repeat(59) + chalk.gray("\u2518 \u2502")
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
lines.push(chalk.gray("\u2502") + " ".repeat(65) + chalk.gray("\u2502"));
|
|
83
|
+
lines.push(chalk.gray("\u2502 ") + chalk.dim("Actions:") + " ".repeat(56) + chalk.gray("\u2502"));
|
|
84
|
+
lines.push(
|
|
85
|
+
chalk.gray("\u2502 ") + chalk.dim(`paradigm triage resolve ${incident.id}`) + " ".repeat(35 - incident.id.length) + chalk.gray("\u2502")
|
|
86
|
+
);
|
|
87
|
+
lines.push(
|
|
88
|
+
chalk.gray("\u2502 ") + chalk.dim(`paradigm triage show ${incident.id} --timeline`) + " ".repeat(28 - incident.id.length) + chalk.gray("\u2502")
|
|
89
|
+
);
|
|
90
|
+
lines.push(
|
|
91
|
+
chalk.gray("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518")
|
|
92
|
+
);
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
95
|
+
function formatIncidentCompact(incident) {
|
|
96
|
+
const statusColor = getStatusColor(incident.status);
|
|
97
|
+
const status = statusColor(incident.status.substring(0, 4).toUpperCase().padEnd(4));
|
|
98
|
+
const timestamp = incident.timestamp.substring(5, 16).replace("T", " ");
|
|
99
|
+
const error = truncate(incident.error.message, 40);
|
|
100
|
+
const symbols = Object.values(incident.symbols).filter(Boolean).join(" ");
|
|
101
|
+
return `${chalk.bold(incident.id)} ${status} ${chalk.gray(timestamp)} ${error}
|
|
102
|
+
${chalk.cyan(truncate(symbols, 60))}`;
|
|
103
|
+
}
|
|
104
|
+
function formatPattern(pattern) {
|
|
105
|
+
const lines = [];
|
|
106
|
+
lines.push(chalk.bold.cyan(`Pattern: ${pattern.id}`));
|
|
107
|
+
lines.push(chalk.white(` Name: ${pattern.name}`));
|
|
108
|
+
lines.push(chalk.gray(` Description: ${pattern.description}`));
|
|
109
|
+
lines.push("");
|
|
110
|
+
lines.push(chalk.yellow(" Matching Criteria:"));
|
|
111
|
+
if (pattern.pattern.symbols) {
|
|
112
|
+
for (const [key, value] of Object.entries(pattern.pattern.symbols)) {
|
|
113
|
+
if (value) {
|
|
114
|
+
const v = Array.isArray(value) ? value.join(", ") : value;
|
|
115
|
+
lines.push(` ${chalk.cyan(key)}: ${v}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (pattern.pattern.errorContains) {
|
|
120
|
+
lines.push(
|
|
121
|
+
` ${chalk.cyan("errorContains")}: ${pattern.pattern.errorContains.join(", ")}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (pattern.pattern.missingSignals) {
|
|
125
|
+
lines.push(
|
|
126
|
+
` ${chalk.cyan("missingSignals")}: ${pattern.pattern.missingSignals.join(", ")}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
lines.push("");
|
|
130
|
+
lines.push(chalk.green(" Resolution:"));
|
|
131
|
+
lines.push(` ${chalk.white(pattern.resolution.description)}`);
|
|
132
|
+
lines.push(
|
|
133
|
+
` Strategy: ${chalk.cyan(pattern.resolution.strategy)} Priority: ${getPriorityColor(pattern.resolution.priority)(pattern.resolution.priority)}`
|
|
134
|
+
);
|
|
135
|
+
if (pattern.resolution.codeHint) {
|
|
136
|
+
lines.push(` ${chalk.dim("Hint: " + pattern.resolution.codeHint)}`);
|
|
137
|
+
}
|
|
138
|
+
lines.push("");
|
|
139
|
+
lines.push(chalk.blue(" Confidence:"));
|
|
140
|
+
lines.push(
|
|
141
|
+
` Score: ${getConfidenceColor(pattern.confidence.score)(pattern.confidence.score + "%")} Matched: ${pattern.confidence.timesMatched} Resolved: ${pattern.confidence.timesResolved} Recurred: ${pattern.confidence.timesRecurred}`
|
|
142
|
+
);
|
|
143
|
+
lines.push("");
|
|
144
|
+
lines.push(
|
|
145
|
+
chalk.gray(
|
|
146
|
+
` Source: ${pattern.source} Tags: ${pattern.tags.join(", ") || "none"}`
|
|
147
|
+
)
|
|
148
|
+
);
|
|
149
|
+
return lines.join("\n");
|
|
150
|
+
}
|
|
151
|
+
function formatPatternCompact(pattern) {
|
|
152
|
+
const confidence = getConfidenceColor(pattern.confidence.score)(
|
|
153
|
+
`${pattern.confidence.score}%`
|
|
154
|
+
);
|
|
155
|
+
const tags = pattern.tags.slice(0, 3).join(", ");
|
|
156
|
+
return `${chalk.bold(pattern.id.padEnd(30))} ${confidence.padStart(8)} ${chalk.gray(pattern.source.padEnd(10))} ${chalk.dim(tags)}
|
|
157
|
+
${chalk.white(truncate(pattern.name, 60))}`;
|
|
158
|
+
}
|
|
159
|
+
function formatSymbols(symbols) {
|
|
160
|
+
const result = [];
|
|
161
|
+
for (const [key, value] of Object.entries(symbols)) {
|
|
162
|
+
if (value) {
|
|
163
|
+
const color = getSymbolColor(key);
|
|
164
|
+
result.push(`${color(value.padEnd(20))} ${chalk.dim(key)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
function getStatusColor(status) {
|
|
170
|
+
switch (status) {
|
|
171
|
+
case "open":
|
|
172
|
+
return chalk.red;
|
|
173
|
+
case "investigating":
|
|
174
|
+
return chalk.yellow;
|
|
175
|
+
case "resolved":
|
|
176
|
+
return chalk.green;
|
|
177
|
+
case "wont-fix":
|
|
178
|
+
return chalk.gray;
|
|
179
|
+
default:
|
|
180
|
+
return chalk.white;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function getSymbolColor(type) {
|
|
184
|
+
switch (type) {
|
|
185
|
+
case "feature":
|
|
186
|
+
return chalk.magenta;
|
|
187
|
+
case "component":
|
|
188
|
+
return chalk.blue;
|
|
189
|
+
case "flow":
|
|
190
|
+
return chalk.cyan;
|
|
191
|
+
case "gate":
|
|
192
|
+
return chalk.yellow;
|
|
193
|
+
case "signal":
|
|
194
|
+
return chalk.green;
|
|
195
|
+
case "state":
|
|
196
|
+
return chalk.red;
|
|
197
|
+
case "integration":
|
|
198
|
+
return chalk.white;
|
|
199
|
+
default:
|
|
200
|
+
return chalk.gray;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function getPriorityColor(priority) {
|
|
204
|
+
switch (priority) {
|
|
205
|
+
case "critical":
|
|
206
|
+
return chalk.red.bold;
|
|
207
|
+
case "high":
|
|
208
|
+
return chalk.red;
|
|
209
|
+
case "medium":
|
|
210
|
+
return chalk.yellow;
|
|
211
|
+
case "low":
|
|
212
|
+
return chalk.gray;
|
|
213
|
+
default:
|
|
214
|
+
return chalk.white;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function getConfidenceColor(score) {
|
|
218
|
+
if (score >= 80) return chalk.green;
|
|
219
|
+
if (score >= 60) return chalk.yellow;
|
|
220
|
+
if (score >= 40) return chalk.red;
|
|
221
|
+
return chalk.gray;
|
|
222
|
+
}
|
|
223
|
+
function truncate(str, maxLen) {
|
|
224
|
+
if (str.length <= maxLen) return str;
|
|
225
|
+
return str.substring(0, maxLen - 3) + "...";
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/commands/triage/index.ts
|
|
229
|
+
import * as fs from "fs";
|
|
230
|
+
var storage = null;
|
|
231
|
+
function getStorage() {
|
|
232
|
+
if (!storage) {
|
|
233
|
+
storage = new SentinelStorage();
|
|
234
|
+
}
|
|
235
|
+
return storage;
|
|
236
|
+
}
|
|
237
|
+
async function triageListCommand(options) {
|
|
238
|
+
const store = getStorage();
|
|
239
|
+
const matcher = new PatternMatcher(store);
|
|
240
|
+
const limit = parseInt(options.limit || "10", 10);
|
|
241
|
+
const status = options.status;
|
|
242
|
+
const incidents = store.getRecentIncidents({
|
|
243
|
+
limit,
|
|
244
|
+
status: status || "all",
|
|
245
|
+
symbol: options.symbol,
|
|
246
|
+
environment: options.env,
|
|
247
|
+
search: options.search,
|
|
248
|
+
dateFrom: options.from,
|
|
249
|
+
dateTo: options.to
|
|
250
|
+
});
|
|
251
|
+
if (options.json) {
|
|
252
|
+
const result = incidents.map((i) => ({
|
|
253
|
+
incident: i,
|
|
254
|
+
matches: matcher.match(i, { maxResults: 3 })
|
|
255
|
+
}));
|
|
256
|
+
console.log(JSON.stringify(result, null, 2));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const stats = new StatsCalculator(store).getStats(7);
|
|
260
|
+
const todayStart = /* @__PURE__ */ new Date();
|
|
261
|
+
todayStart.setHours(0, 0, 0, 0);
|
|
262
|
+
const todayCount = store.getIncidentCount({
|
|
263
|
+
dateFrom: todayStart.toISOString()
|
|
264
|
+
});
|
|
265
|
+
console.log(formatHeader());
|
|
266
|
+
console.log(
|
|
267
|
+
formatSummaryBar({
|
|
268
|
+
open: stats.incidents.open,
|
|
269
|
+
investigating: stats.incidents.total - stats.incidents.open - stats.incidents.resolved,
|
|
270
|
+
resolved: stats.incidents.resolved,
|
|
271
|
+
today: todayCount
|
|
272
|
+
})
|
|
273
|
+
);
|
|
274
|
+
console.log("");
|
|
275
|
+
if (incidents.length === 0) {
|
|
276
|
+
console.log(chalk2.gray("No incidents found."));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
for (const incident of incidents) {
|
|
280
|
+
const matches = matcher.match(incident, { maxResults: 3 });
|
|
281
|
+
console.log(formatIncident(incident, matches));
|
|
282
|
+
console.log("");
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async function triageShowCommand(incidentId, options) {
|
|
286
|
+
const store = getStorage();
|
|
287
|
+
const matcher = new PatternMatcher(store);
|
|
288
|
+
const incident = store.getIncident(incidentId);
|
|
289
|
+
if (!incident) {
|
|
290
|
+
console.log(chalk2.red(`Incident ${incidentId} not found.`));
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const matches = matcher.match(incident, { maxResults: 5 });
|
|
294
|
+
if (options.json) {
|
|
295
|
+
const result = { incident, matches };
|
|
296
|
+
if (options.timeline && incident.flowPosition) {
|
|
297
|
+
const timeline = new TimelineBuilder().build(incident);
|
|
298
|
+
result.timeline = timeline ? new TimelineBuilder().renderStructured(timeline) : null;
|
|
299
|
+
}
|
|
300
|
+
console.log(JSON.stringify(result, null, 2));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
console.log(formatIncident(incident, matches));
|
|
304
|
+
if (options.timeline && incident.flowPosition) {
|
|
305
|
+
const timeline = new TimelineBuilder().build(incident);
|
|
306
|
+
if (timeline) {
|
|
307
|
+
console.log("");
|
|
308
|
+
console.log(chalk2.cyan.bold("Flow Timeline"));
|
|
309
|
+
console.log(chalk2.gray("\u2500".repeat(50)));
|
|
310
|
+
console.log(new TimelineBuilder().renderAscii(timeline));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (incident.notes.length > 0) {
|
|
314
|
+
console.log("");
|
|
315
|
+
console.log(chalk2.cyan.bold("Notes"));
|
|
316
|
+
console.log(chalk2.gray("\u2500".repeat(50)));
|
|
317
|
+
for (const note of incident.notes) {
|
|
318
|
+
console.log(
|
|
319
|
+
chalk2.gray(note.timestamp.substring(0, 16)) + " " + (note.author ? chalk2.yellow(note.author + ": ") : "") + note.content
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (incident.relatedIncidents.length > 0) {
|
|
324
|
+
console.log("");
|
|
325
|
+
console.log(chalk2.cyan.bold("Related Incidents"));
|
|
326
|
+
console.log(chalk2.gray("\u2500".repeat(50)));
|
|
327
|
+
console.log(" " + incident.relatedIncidents.join(", "));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function triageResolveCommand(incidentId, options) {
|
|
331
|
+
const store = getStorage();
|
|
332
|
+
const incident = store.getIncident(incidentId);
|
|
333
|
+
if (!incident) {
|
|
334
|
+
console.log(chalk2.red(`Incident ${incidentId} not found.`));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (incident.status === "resolved" || incident.status === "wont-fix") {
|
|
338
|
+
console.log(chalk2.yellow(`Incident ${incidentId} is already ${incident.status}.`));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (options.wontFix) {
|
|
342
|
+
store.updateIncident(incidentId, {
|
|
343
|
+
status: "wont-fix",
|
|
344
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
345
|
+
resolvedBy: "manual",
|
|
346
|
+
resolution: {
|
|
347
|
+
notes: options.notes
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
console.log(chalk2.gray(`Incident ${incidentId} marked as won't fix.`));
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
store.recordResolution({
|
|
354
|
+
incidentId,
|
|
355
|
+
patternId: options.pattern,
|
|
356
|
+
commitHash: options.commit,
|
|
357
|
+
prUrl: options.pr,
|
|
358
|
+
notes: options.notes
|
|
359
|
+
});
|
|
360
|
+
console.log(chalk2.green(`Incident ${incidentId} resolved.`));
|
|
361
|
+
if (options.pattern) {
|
|
362
|
+
console.log(chalk2.gray(` Pattern: ${options.pattern}`));
|
|
363
|
+
}
|
|
364
|
+
if (options.commit) {
|
|
365
|
+
console.log(chalk2.gray(` Commit: ${options.commit}`));
|
|
366
|
+
}
|
|
367
|
+
if (options.pr) {
|
|
368
|
+
console.log(chalk2.gray(` PR: ${options.pr}`));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async function triageNoteCommand(incidentId, note) {
|
|
372
|
+
const store = getStorage();
|
|
373
|
+
const incident = store.getIncident(incidentId);
|
|
374
|
+
if (!incident) {
|
|
375
|
+
console.log(chalk2.red(`Incident ${incidentId} not found.`));
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
store.addIncidentNote(incidentId, {
|
|
379
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
380
|
+
content: note
|
|
381
|
+
});
|
|
382
|
+
console.log(chalk2.green(`Note added to ${incidentId}.`));
|
|
383
|
+
}
|
|
384
|
+
async function triageLinkCommand(incidentId1, incidentId2) {
|
|
385
|
+
const store = getStorage();
|
|
386
|
+
const inc1 = store.getIncident(incidentId1);
|
|
387
|
+
const inc2 = store.getIncident(incidentId2);
|
|
388
|
+
if (!inc1) {
|
|
389
|
+
console.log(chalk2.red(`Incident ${incidentId1} not found.`));
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (!inc2) {
|
|
393
|
+
console.log(chalk2.red(`Incident ${incidentId2} not found.`));
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
store.linkIncidents(incidentId1, incidentId2);
|
|
397
|
+
console.log(chalk2.green(`Linked ${incidentId1} and ${incidentId2}.`));
|
|
398
|
+
}
|
|
399
|
+
async function triagePatternsListCommand(options) {
|
|
400
|
+
const store = getStorage();
|
|
401
|
+
const patterns = store.getAllPatterns({
|
|
402
|
+
source: options.source,
|
|
403
|
+
minConfidence: options.minConfidence ? parseInt(options.minConfidence, 10) : void 0,
|
|
404
|
+
includePrivate: true
|
|
405
|
+
});
|
|
406
|
+
if (options.json) {
|
|
407
|
+
console.log(JSON.stringify(patterns, null, 2));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
console.log(chalk2.cyan.bold("\nFailure Patterns"));
|
|
411
|
+
console.log(chalk2.gray("\u2500".repeat(70)));
|
|
412
|
+
if (patterns.length === 0) {
|
|
413
|
+
console.log(chalk2.gray("No patterns found. Run `paradigm triage patterns seed` to load defaults."));
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
for (const pattern of patterns) {
|
|
417
|
+
console.log(formatPatternCompact(pattern));
|
|
418
|
+
console.log("");
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
async function triagePatternsShowCommand(patternId, options) {
|
|
422
|
+
const store = getStorage();
|
|
423
|
+
const pattern = store.getPattern(patternId);
|
|
424
|
+
if (!pattern) {
|
|
425
|
+
console.log(chalk2.red(`Pattern ${patternId} not found.`));
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (options.json) {
|
|
429
|
+
console.log(JSON.stringify(pattern, null, 2));
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
console.log(formatPattern(pattern));
|
|
433
|
+
}
|
|
434
|
+
async function triagePatternsAddCommand(options) {
|
|
435
|
+
const store = getStorage();
|
|
436
|
+
if (options.fromIncident) {
|
|
437
|
+
const incident = store.getIncident(options.fromIncident);
|
|
438
|
+
if (!incident) {
|
|
439
|
+
console.log(chalk2.red(`Incident ${options.fromIncident} not found.`));
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const suggester = new PatternSuggester(store);
|
|
443
|
+
const suggested = suggester.suggestFromIncident(incident);
|
|
444
|
+
console.log(chalk2.cyan("\nSuggested Pattern:"));
|
|
445
|
+
console.log(JSON.stringify(suggested, null, 2));
|
|
446
|
+
console.log(
|
|
447
|
+
chalk2.gray(
|
|
448
|
+
'\nEdit and add with: paradigm triage patterns add --id <id> --name "..." ...'
|
|
449
|
+
)
|
|
450
|
+
);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const symbols = {};
|
|
454
|
+
if (options.symbols) {
|
|
455
|
+
const pairs = options.symbols.split(",");
|
|
456
|
+
for (const pair of pairs) {
|
|
457
|
+
const [key, value] = pair.split(":").map((s) => s.trim());
|
|
458
|
+
if (key && value) {
|
|
459
|
+
symbols[key] = value;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const input = {
|
|
464
|
+
id: options.id,
|
|
465
|
+
name: options.name,
|
|
466
|
+
description: options.description || "",
|
|
467
|
+
pattern: {
|
|
468
|
+
symbols,
|
|
469
|
+
errorContains: options.errorContains?.split(",").map((s) => s.trim()),
|
|
470
|
+
missingSignals: options.missingSignals?.split(",").map((s) => s.trim())
|
|
471
|
+
},
|
|
472
|
+
resolution: {
|
|
473
|
+
description: "Resolution TBD",
|
|
474
|
+
strategy: options.strategy || "fix-code",
|
|
475
|
+
priority: options.priority || "medium",
|
|
476
|
+
codeHint: options.codeHint
|
|
477
|
+
},
|
|
478
|
+
source: "manual",
|
|
479
|
+
private: false,
|
|
480
|
+
tags: options.tags?.split(",").map((s) => s.trim()) || []
|
|
481
|
+
};
|
|
482
|
+
store.addPattern(input);
|
|
483
|
+
console.log(chalk2.green(`Pattern ${options.id} created.`));
|
|
484
|
+
}
|
|
485
|
+
async function triagePatternsDeleteCommand(patternId) {
|
|
486
|
+
const store = getStorage();
|
|
487
|
+
const pattern = store.getPattern(patternId);
|
|
488
|
+
if (!pattern) {
|
|
489
|
+
console.log(chalk2.red(`Pattern ${patternId} not found.`));
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
store.deletePattern(patternId);
|
|
493
|
+
console.log(chalk2.green(`Pattern ${patternId} deleted.`));
|
|
494
|
+
}
|
|
495
|
+
async function triagePatternsTestCommand(patternId, options) {
|
|
496
|
+
const store = getStorage();
|
|
497
|
+
const matcher = new PatternMatcher(store);
|
|
498
|
+
const pattern = store.getPattern(patternId);
|
|
499
|
+
if (!pattern) {
|
|
500
|
+
console.log(chalk2.red(`Pattern ${patternId} not found.`));
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const limit = parseInt(options.limit || "100", 10);
|
|
504
|
+
const result = matcher.testPattern(pattern, limit);
|
|
505
|
+
if (options.json) {
|
|
506
|
+
console.log(JSON.stringify(result, null, 2));
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
console.log(chalk2.cyan.bold(`
|
|
510
|
+
Pattern Test: ${patternId}`));
|
|
511
|
+
console.log(chalk2.gray("\u2500".repeat(50)));
|
|
512
|
+
console.log(`Would match: ${chalk2.yellow(result.matchCount)} incidents`);
|
|
513
|
+
console.log(`Average score: ${chalk2.yellow(result.avgScore + "%")}`);
|
|
514
|
+
if (result.wouldMatch.length > 0) {
|
|
515
|
+
console.log("");
|
|
516
|
+
console.log(chalk2.cyan("Sample matches:"));
|
|
517
|
+
for (const incident of result.wouldMatch.slice(0, 5)) {
|
|
518
|
+
console.log(formatIncidentCompact(incident));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
async function triagePatternsSeedCommand() {
|
|
523
|
+
const store = getStorage();
|
|
524
|
+
const spinner = ora("Loading seed patterns...").start();
|
|
525
|
+
try {
|
|
526
|
+
const seedData = loadAllSeedPatterns();
|
|
527
|
+
const result = store.importPatterns(seedData, { overwrite: false });
|
|
528
|
+
spinner.succeed(
|
|
529
|
+
`Loaded ${result.imported} patterns (${result.skipped} skipped).`
|
|
530
|
+
);
|
|
531
|
+
} catch (error) {
|
|
532
|
+
spinner.fail(`Failed to load seed patterns: ${error}`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async function triageExportCommand(type, options) {
|
|
536
|
+
const store = getStorage();
|
|
537
|
+
let data;
|
|
538
|
+
let defaultFilename;
|
|
539
|
+
if (type === "patterns") {
|
|
540
|
+
data = store.exportPatterns({
|
|
541
|
+
includePrivate: options.includePrivate
|
|
542
|
+
});
|
|
543
|
+
defaultFilename = "sentinel-patterns.json";
|
|
544
|
+
} else {
|
|
545
|
+
data = store.exportBackup();
|
|
546
|
+
defaultFilename = "sentinel-backup.json";
|
|
547
|
+
}
|
|
548
|
+
const outputPath = options.output || defaultFilename;
|
|
549
|
+
fs.writeFileSync(outputPath, JSON.stringify(data, null, 2));
|
|
550
|
+
console.log(chalk2.green(`Exported to ${outputPath}`));
|
|
551
|
+
}
|
|
552
|
+
async function triageImportCommand(filePath, options) {
|
|
553
|
+
const store = getStorage();
|
|
554
|
+
const importer = new PatternImporter();
|
|
555
|
+
const spinner = ora(`Importing from ${filePath}...`).start();
|
|
556
|
+
try {
|
|
557
|
+
const data = importer.loadFromFile(filePath);
|
|
558
|
+
const result = store.importPatterns(data, {
|
|
559
|
+
overwrite: options.overwrite
|
|
560
|
+
});
|
|
561
|
+
spinner.succeed(
|
|
562
|
+
`Imported ${result.imported} patterns (${result.skipped} skipped).`
|
|
563
|
+
);
|
|
564
|
+
} catch (error) {
|
|
565
|
+
spinner.fail(`Import failed: ${error}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async function triageRestoreCommand(filePath) {
|
|
569
|
+
const store = getStorage();
|
|
570
|
+
const spinner = ora(`Restoring from ${filePath}...`).start();
|
|
571
|
+
try {
|
|
572
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
573
|
+
const data = JSON.parse(content);
|
|
574
|
+
store.importBackup(data);
|
|
575
|
+
spinner.succeed("Backup restored.");
|
|
576
|
+
} catch (error) {
|
|
577
|
+
spinner.fail(`Restore failed: ${error}`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async function triageStatsCommand(options) {
|
|
581
|
+
const store = getStorage();
|
|
582
|
+
const calculator = new StatsCalculator(store);
|
|
583
|
+
let periodDays = 7;
|
|
584
|
+
if (options.period) {
|
|
585
|
+
const match = options.period.match(/^(\d+)d$/);
|
|
586
|
+
if (match) {
|
|
587
|
+
periodDays = parseInt(match[1], 10);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (options.symbol) {
|
|
591
|
+
const health = calculator.getSymbolHealth(options.symbol);
|
|
592
|
+
if (options.json) {
|
|
593
|
+
console.log(JSON.stringify(health, null, 2));
|
|
594
|
+
} else {
|
|
595
|
+
console.log(chalk2.cyan.bold(`
|
|
596
|
+
Symbol Health: ${options.symbol}`));
|
|
597
|
+
console.log(chalk2.gray("\u2500".repeat(50)));
|
|
598
|
+
console.log(` Incidents: ${health.incidentCount}`);
|
|
599
|
+
console.log(` Avg Time to Resolve: ${health.avgTimeToResolve}m`);
|
|
600
|
+
console.log("");
|
|
601
|
+
console.log(chalk2.cyan(" Top Patterns:"));
|
|
602
|
+
for (const { patternId, count } of health.topPatterns) {
|
|
603
|
+
console.log(` ${patternId}: ${count} times`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const stats = calculator.getStats(periodDays);
|
|
609
|
+
if (options.json) {
|
|
610
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
console.log(calculator.generateDashboard(periodDays));
|
|
614
|
+
}
|
|
615
|
+
async function triageRecordCommand(options) {
|
|
616
|
+
const store = getStorage();
|
|
617
|
+
const matcher = new PatternMatcher(store);
|
|
618
|
+
const incidentId = store.recordIncident({
|
|
619
|
+
error: {
|
|
620
|
+
message: options.error,
|
|
621
|
+
stack: options.stack
|
|
622
|
+
},
|
|
623
|
+
symbols: {
|
|
624
|
+
feature: options.feature,
|
|
625
|
+
component: options.component,
|
|
626
|
+
flow: options.flow,
|
|
627
|
+
gate: options.gate,
|
|
628
|
+
signal: options.signal,
|
|
629
|
+
state: options.state,
|
|
630
|
+
integration: options.integration
|
|
631
|
+
},
|
|
632
|
+
environment: options.env,
|
|
633
|
+
service: options.service,
|
|
634
|
+
version: options.version
|
|
635
|
+
});
|
|
636
|
+
const incident = store.getIncident(incidentId);
|
|
637
|
+
const matches = incident ? matcher.match(incident, { maxResults: 3 }) : [];
|
|
638
|
+
if (options.json) {
|
|
639
|
+
console.log(JSON.stringify({ incidentId, matches }, null, 2));
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
console.log(chalk2.green(`Recorded incident ${incidentId}`));
|
|
643
|
+
if (matches.length > 0) {
|
|
644
|
+
console.log("");
|
|
645
|
+
console.log(chalk2.cyan("Matched patterns:"));
|
|
646
|
+
for (const match of matches) {
|
|
647
|
+
console.log(
|
|
648
|
+
` ${chalk2.yellow("\u2605")} ${match.pattern.id} (${match.confidence}% confidence)`
|
|
649
|
+
);
|
|
650
|
+
console.log(chalk2.gray(` ${match.pattern.resolution.description}`));
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
export {
|
|
655
|
+
triageExportCommand,
|
|
656
|
+
triageImportCommand,
|
|
657
|
+
triageLinkCommand,
|
|
658
|
+
triageListCommand,
|
|
659
|
+
triageNoteCommand,
|
|
660
|
+
triagePatternsAddCommand,
|
|
661
|
+
triagePatternsDeleteCommand,
|
|
662
|
+
triagePatternsListCommand,
|
|
663
|
+
triagePatternsSeedCommand,
|
|
664
|
+
triagePatternsShowCommand,
|
|
665
|
+
triagePatternsTestCommand,
|
|
666
|
+
triageRecordCommand,
|
|
667
|
+
triageResolveCommand,
|
|
668
|
+
triageRestoreCommand,
|
|
669
|
+
triageShowCommand,
|
|
670
|
+
triageStatsCommand
|
|
671
|
+
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
{
|
|
7
7
|
"id": "mcp-tools-overview",
|
|
8
8
|
"title": "MCP Tools Overview",
|
|
9
|
-
"content": "## MCP Tools Overview\n\nModel Context Protocol (MCP) tools are the primary interface between AI agents and the Paradigm framework. Rather than reading raw files to understand project structure, agents call MCP tools that return structured, token-efficient responses. Understanding the full tool inventory and when to use each tool is fundamental to effective Paradigm orchestration.\n\nParadigm exposes approximately 15 tool modules, organized into four categories:\n\n### Discovery Tools\nThese tools help agents understand the codebase without reading files directly.\n\n- **`paradigm_search`** (~150 tokens) -- Fuzzy search across symbol names, descriptions, and tags. Supports type filtering (component, flow, gate, signal, aspect).\n- **`paradigm_navigate`** (~200 tokens) -- Three intents: `find` (symbol lookup), `explore` (area browsing), `context` (task-based discovery).\n- **`paradigm_ripple`** (~300 tokens) -- Dependency analysis showing what depends on a symbol, 1-5 levels deep.\n- **`paradigm_related`** (~200 tokens) -- All symbols connected to a given symbol, both upstream and downstream.\n\n### Knowledge Tools\nThese tools access the project's institutional memory.\n\n- **`paradigm_wisdom_context`** -- Retrieves preferences, antipatterns, and decisions for specified symbols.\n- **`paradigm_wisdom_record`** -- Captures new antipatterns or architectural decisions.\n- **`paradigm_wisdom_expert`** -- Identifies human experts for symbols or areas.\n- **`paradigm_history_context`** -- Retrieves implementation history for symbols.\n- **`paradigm_history_record`** -- Logs implementation events.\n- **`paradigm_history_fragility`** -- Checks stability scores.\n\n### Validation Tools\nThese tools verify metadata integrity.\n\n- **`paradigm_purpose_validate`** -- Validates `.purpose` files and optionally `portal.yaml`.\n- **`paradigm_flow_validate`** -- Validates flow definitions against the codebase.\n- **`paradigm_aspect_check`** -- Verifies that aspects have valid code anchors.\n\n### Management Tools\nThese tools modify Paradigm metadata.\n\n- **`paradigm_purpose_add_component`**, **`paradigm_purpose_add_signal`**, **`paradigm_purpose_add_flow`**, etc. -- Add symbols to `.purpose` files.\n- **`paradigm_portal_add_gate`**, **`paradigm_portal_add_route`** -- Manage `portal.yaml` gates and routes.\n- **`paradigm_purpose_rename`** -- Rename symbols across all `.purpose` files.\n- **`paradigm_tags`**, **`paradigm_tags_suggest`** -- Manage the tag bank.\n\n### Token Economics\n\nEvery tool call has a token cost. The general principle is that MCP queries are 5-20x cheaper than reading files:\n\n| Operation | Approximate Cost |\n|---|---|\n| `paradigm_status` | ~100 tokens |\n| `paradigm_search` | ~150 tokens |\n| `paradigm_navigate` | ~200 tokens |\n| `paradigm_ripple` | ~300 tokens |\n| Reading a small file | ~500 tokens |\n| Reading a large file | ~2000+ tokens |\n\nThe rule of thumb: **use MCP tools for discovery and knowledge retrieval; use file reads only when you need exact source code for implementation.** An agent that reads 10 files to understand a feature (10,000+ tokens) versus one that calls `paradigm_navigate` with context intent (200 tokens) has a 50x cost difference for the same information.",
|
|
9
|
+
"content": "## MCP Tools Overview\n\nModel Context Protocol (MCP) tools are the primary interface between AI agents and the Paradigm framework. Rather than reading raw files to understand project structure, agents call MCP tools that return structured, token-efficient responses. Understanding the full tool inventory and when to use each tool is fundamental to effective Paradigm orchestration.\n\nParadigm exposes approximately 15 tool modules, organized into four categories:\n\n### Discovery Tools\nThese tools help agents understand the codebase without reading files directly.\n\n- **`paradigm_search`** (~150 tokens) -- Fuzzy search across symbol names, descriptions, and tags. Supports type filtering (component, flow, gate, signal, aspect).\n- **`paradigm_navigate`** (~200 tokens) -- Three intents: `find` (symbol lookup), `explore` (area browsing), `context` (task-based discovery).\n- **`paradigm_ripple`** (~300 tokens) -- Dependency analysis showing what depends on a symbol, 1-5 levels deep.\n- **`paradigm_related`** (~200 tokens) -- All symbols connected to a given symbol, both upstream and downstream.\n\n### Knowledge Tools\nThese tools access the project's institutional memory.\n\n- **`paradigm_wisdom_context`** -- Retrieves preferences, antipatterns, and decisions for specified symbols.\n- **`paradigm_wisdom_record`** -- Captures new antipatterns or architectural decisions.\n- **`paradigm_wisdom_expert`** -- Identifies human experts for symbols or areas.\n- **`paradigm_history_context`** -- Retrieves implementation history for symbols.\n- **`paradigm_history_record`** -- Logs implementation events.\n- **`paradigm_history_fragility`** -- Checks stability scores.\n\n### Validation Tools\nThese tools verify metadata integrity.\n\n- **`paradigm_purpose_validate`** -- Validates `.purpose` files and optionally `portal.yaml`.\n- **`paradigm_flow_validate`** -- Validates flow definitions against the codebase.\n- **`paradigm_aspect_check`** -- Verifies that aspects have valid code anchors.\n\n### Management Tools\nThese tools modify Paradigm metadata.\n\n- **`paradigm_purpose_add_component`**, **`paradigm_purpose_add_signal`**, **`paradigm_purpose_add_flow`**, etc. -- Add symbols to `.purpose` files.\n- **`paradigm_portal_add_gate`**, **`paradigm_portal_add_route`** -- Manage `portal.yaml` gates and routes.\n- **`paradigm_purpose_rename`** -- Rename symbols across all `.purpose` files.\n- **`paradigm_tags`**, **`paradigm_tags_suggest`** -- Manage the tag bank.\n\n### Token Economics\n\nEvery tool call has a token cost. The general principle is that MCP queries are 5-20x cheaper than reading files:\n\n| Operation | Approximate Cost |\n|---|---|\n| `paradigm_status` | ~100 tokens |\n| `paradigm_search` | ~150 tokens |\n| `paradigm_navigate` | ~200 tokens |\n| `paradigm_ripple` | ~300 tokens |\n| Reading a small file | ~500 tokens |\n| Reading a large file | ~2000+ tokens |\n\nThe rule of thumb: **use MCP tools for discovery and knowledge retrieval; use file reads only when you need exact source code for implementation.** An agent that reads 10 files to understand a feature (10,000+ tokens) versus one that calls `paradigm_navigate` with context intent (200 tokens) has a 50x cost difference for the same information.\n\n### Practice Tools\n\nThese tools manage behavioral discipline and project memory.\n\n**Habits Tools:**\n- **`paradigm_habits_list`** -- List habit definitions with filters (category, trigger, severity, enabled status).\n- **`paradigm_habits_check`** -- Evaluate and record practice compliance. Triggers: `preflight`, `postflight`, `on-stop`, `on-commit`.\n- **`paradigm_habits_status`** -- Practice profile with compliance rates, category breakdowns, trends, and incident correlations.\n- **`paradigm_practice_context`** -- Proactive habit warnings before modifying symbols. Returns relevant habits and recent compliance rates.\n\n**Lore Tools:**\n- **`paradigm_lore_search`** -- Search lore entries by symbol, author, date range, tags, type, and review status.\n- **`paradigm_lore_record`** -- Record new entries (agent sessions, decisions, milestones, incidents, reviews).\n- **`paradigm_lore_get`** -- Fetch a single entry by ID with full detail.\n- **`paradigm_lore_update`** -- Update an existing entry's fields (title, summary, type, symbols, tags, learnings).\n- **`paradigm_lore_delete`** -- Delete an entry by ID. Requires `confirm: true` to prevent accidental deletion.\n- **`paradigm_lore_timeline`** -- Timeline overview with recent entries, hot symbols, and active authors.",
|
|
10
10
|
"keyConcepts": [
|
|
11
11
|
"Four tool categories: discovery, knowledge, validation, management",
|
|
12
12
|
"Token economics of MCP vs file reads",
|