iosm-cli 0.1.3 → 0.2.1

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 (132) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +88 -46
  3. package/dist/core/agent-teams.d.ts.map +1 -1
  4. package/dist/core/agent-teams.js +38 -11
  5. package/dist/core/agent-teams.js.map +1 -1
  6. package/dist/core/blast.d.ts +62 -0
  7. package/dist/core/blast.d.ts.map +1 -0
  8. package/dist/core/blast.js +448 -0
  9. package/dist/core/blast.js.map +1 -0
  10. package/dist/core/contract.d.ts +54 -0
  11. package/dist/core/contract.d.ts.map +1 -0
  12. package/dist/core/contract.js +300 -0
  13. package/dist/core/contract.js.map +1 -0
  14. package/dist/core/failure-retrospective.d.ts +12 -0
  15. package/dist/core/failure-retrospective.d.ts.map +1 -0
  16. package/dist/core/failure-retrospective.js +115 -0
  17. package/dist/core/failure-retrospective.js.map +1 -0
  18. package/dist/core/project-index/index.d.ts +17 -0
  19. package/dist/core/project-index/index.d.ts.map +1 -0
  20. package/dist/core/project-index/index.js +323 -0
  21. package/dist/core/project-index/index.js.map +1 -0
  22. package/dist/core/project-index/types.d.ts +34 -0
  23. package/dist/core/project-index/types.d.ts.map +1 -0
  24. package/dist/core/project-index/types.js +2 -0
  25. package/dist/core/project-index/types.js.map +1 -0
  26. package/dist/core/sdk.d.ts.map +1 -1
  27. package/dist/core/sdk.js +8 -0
  28. package/dist/core/sdk.js.map +1 -1
  29. package/dist/core/semantic/config.d.ts.map +1 -1
  30. package/dist/core/semantic/config.js +5 -0
  31. package/dist/core/semantic/config.js.map +1 -1
  32. package/dist/core/semantic/index.d.ts +1 -1
  33. package/dist/core/semantic/index.d.ts.map +1 -1
  34. package/dist/core/semantic/index.js +1 -1
  35. package/dist/core/semantic/index.js.map +1 -1
  36. package/dist/core/semantic/runtime.d.ts.map +1 -1
  37. package/dist/core/semantic/runtime.js +12 -1
  38. package/dist/core/semantic/runtime.js.map +1 -1
  39. package/dist/core/semantic/types.d.ts +6 -0
  40. package/dist/core/semantic/types.d.ts.map +1 -1
  41. package/dist/core/semantic/types.js +6 -0
  42. package/dist/core/semantic/types.js.map +1 -1
  43. package/dist/core/shadow-guard.d.ts +30 -0
  44. package/dist/core/shadow-guard.d.ts.map +1 -0
  45. package/dist/core/shadow-guard.js +81 -0
  46. package/dist/core/shadow-guard.js.map +1 -0
  47. package/dist/core/shared-memory.d.ts +46 -0
  48. package/dist/core/shared-memory.d.ts.map +1 -0
  49. package/dist/core/shared-memory.js +253 -0
  50. package/dist/core/shared-memory.js.map +1 -0
  51. package/dist/core/singular.d.ts +73 -0
  52. package/dist/core/singular.d.ts.map +1 -0
  53. package/dist/core/singular.js +413 -0
  54. package/dist/core/singular.js.map +1 -0
  55. package/dist/core/slash-commands.d.ts.map +1 -1
  56. package/dist/core/slash-commands.js +14 -2
  57. package/dist/core/slash-commands.js.map +1 -1
  58. package/dist/core/subagents.js +1 -1
  59. package/dist/core/subagents.js.map +1 -1
  60. package/dist/core/swarm/gates.d.ts +9 -0
  61. package/dist/core/swarm/gates.d.ts.map +1 -0
  62. package/dist/core/swarm/gates.js +65 -0
  63. package/dist/core/swarm/gates.js.map +1 -0
  64. package/dist/core/swarm/index.d.ts +9 -0
  65. package/dist/core/swarm/index.d.ts.map +1 -0
  66. package/dist/core/swarm/index.js +9 -0
  67. package/dist/core/swarm/index.js.map +1 -0
  68. package/dist/core/swarm/locks.d.ts +21 -0
  69. package/dist/core/swarm/locks.d.ts.map +1 -0
  70. package/dist/core/swarm/locks.js +93 -0
  71. package/dist/core/swarm/locks.js.map +1 -0
  72. package/dist/core/swarm/planner.d.ts +16 -0
  73. package/dist/core/swarm/planner.d.ts.map +1 -0
  74. package/dist/core/swarm/planner.js +137 -0
  75. package/dist/core/swarm/planner.js.map +1 -0
  76. package/dist/core/swarm/retry.d.ts +16 -0
  77. package/dist/core/swarm/retry.d.ts.map +1 -0
  78. package/dist/core/swarm/retry.js +32 -0
  79. package/dist/core/swarm/retry.js.map +1 -0
  80. package/dist/core/swarm/scheduler.d.ts +48 -0
  81. package/dist/core/swarm/scheduler.d.ts.map +1 -0
  82. package/dist/core/swarm/scheduler.js +554 -0
  83. package/dist/core/swarm/scheduler.js.map +1 -0
  84. package/dist/core/swarm/spawn.d.ts +16 -0
  85. package/dist/core/swarm/spawn.d.ts.map +1 -0
  86. package/dist/core/swarm/spawn.js +42 -0
  87. package/dist/core/swarm/spawn.js.map +1 -0
  88. package/dist/core/swarm/state-store.d.ts +35 -0
  89. package/dist/core/swarm/state-store.d.ts.map +1 -0
  90. package/dist/core/swarm/state-store.js +106 -0
  91. package/dist/core/swarm/state-store.js.map +1 -0
  92. package/dist/core/swarm/types.d.ts +116 -0
  93. package/dist/core/swarm/types.d.ts.map +1 -0
  94. package/dist/core/swarm/types.js +2 -0
  95. package/dist/core/swarm/types.js.map +1 -0
  96. package/dist/core/system-prompt.d.ts.map +1 -1
  97. package/dist/core/system-prompt.js +6 -3
  98. package/dist/core/system-prompt.js.map +1 -1
  99. package/dist/core/tools/semantic-search.d.ts.map +1 -1
  100. package/dist/core/tools/semantic-search.js +1 -0
  101. package/dist/core/tools/semantic-search.js.map +1 -1
  102. package/dist/core/tools/shared-memory.d.ts +23 -0
  103. package/dist/core/tools/shared-memory.d.ts.map +1 -0
  104. package/dist/core/tools/shared-memory.js +134 -0
  105. package/dist/core/tools/shared-memory.js.map +1 -0
  106. package/dist/core/tools/task.d.ts +8 -1
  107. package/dist/core/tools/task.d.ts.map +1 -1
  108. package/dist/core/tools/task.js +664 -123
  109. package/dist/core/tools/task.js.map +1 -1
  110. package/dist/main.d.ts.map +1 -1
  111. package/dist/main.js +8 -1
  112. package/dist/main.js.map +1 -1
  113. package/dist/modes/interactive/components/custom-editor.d.ts +8 -0
  114. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  115. package/dist/modes/interactive/components/custom-editor.js +70 -1
  116. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  117. package/dist/modes/interactive/components/login-dialog.d.ts +1 -0
  118. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  119. package/dist/modes/interactive/components/login-dialog.js +27 -4
  120. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  121. package/dist/modes/interactive/components/subagent-message.d.ts.map +1 -1
  122. package/dist/modes/interactive/components/subagent-message.js +14 -0
  123. package/dist/modes/interactive/components/subagent-message.js.map +1 -1
  124. package/dist/modes/interactive/interactive-mode.d.ts +81 -0
  125. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  126. package/dist/modes/interactive/interactive-mode.js +3481 -870
  127. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  128. package/docs/cli-reference.md +29 -1
  129. package/docs/configuration.md +5 -0
  130. package/docs/interactive-mode.md +171 -2
  131. package/docs/orchestration-and-subagents.md +96 -169
  132. package/package.json +4 -3
@@ -0,0 +1,448 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
+ import { join, relative, sep } from "node:path";
3
+ const SCAN_TEXT_EXTENSIONS = new Set([
4
+ ".ts",
5
+ ".tsx",
6
+ ".js",
7
+ ".jsx",
8
+ ".mjs",
9
+ ".cjs",
10
+ ".py",
11
+ ".go",
12
+ ".rs",
13
+ ".java",
14
+ ".json",
15
+ ".yaml",
16
+ ".yml",
17
+ ".toml",
18
+ ".md",
19
+ ".sh",
20
+ ".env",
21
+ ".sql",
22
+ ".html",
23
+ ".css",
24
+ ]);
25
+ const EXCLUDED_DIR_NAMES = new Set([".git", "node_modules", "dist", "build", ".iosm", ".next", "coverage"]);
26
+ function toPosixPath(value) {
27
+ return value.split(sep).join("/");
28
+ }
29
+ function getExtension(filePath) {
30
+ const normalized = filePath.toLowerCase();
31
+ const index = normalized.lastIndexOf(".");
32
+ return index >= 0 ? normalized.slice(index) : "";
33
+ }
34
+ function containsAny(text, terms) {
35
+ const normalized = text.toLowerCase();
36
+ return terms.some((term) => normalized.includes(term.toLowerCase()));
37
+ }
38
+ function countSeverity(findings, severity) {
39
+ return findings.filter((item) => item.severity === severity).length;
40
+ }
41
+ function clampFindings(findings, max) {
42
+ if (findings.length <= max)
43
+ return findings;
44
+ return findings.slice(0, max);
45
+ }
46
+ function serializeFinding(finding) {
47
+ const location = finding.path ? ` (${finding.path}${finding.line ? `:${finding.line}` : ""})` : "";
48
+ return `- [${finding.severity.toUpperCase()}] ${finding.title}${location}\n ${finding.detail}${finding.recommendation ? `\n fix: ${finding.recommendation}` : ""}`;
49
+ }
50
+ function nowIso() {
51
+ return new Date().toISOString();
52
+ }
53
+ function buildRunId(date = new Date()) {
54
+ const year = date.getUTCFullYear();
55
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
56
+ const day = String(date.getUTCDate()).padStart(2, "0");
57
+ const hours = String(date.getUTCHours()).padStart(2, "0");
58
+ const minutes = String(date.getUTCMinutes()).padStart(2, "0");
59
+ const seconds = String(date.getUTCSeconds()).padStart(2, "0");
60
+ return `${year}-${month}-${day}-${hours}${minutes}${seconds}`;
61
+ }
62
+ export class BlastService {
63
+ constructor(options) {
64
+ this.cwd = options.cwd;
65
+ }
66
+ getAuditsRoot() {
67
+ return join(this.cwd, ".iosm", "audits");
68
+ }
69
+ getLastRun() {
70
+ const root = this.getAuditsRoot();
71
+ if (!existsSync(root))
72
+ return undefined;
73
+ const candidates = readdirSync(root, { withFileTypes: true })
74
+ .filter((entry) => entry.isDirectory())
75
+ .map((entry) => entry.name)
76
+ .sort((a, b) => a.localeCompare(b));
77
+ const runId = candidates[candidates.length - 1];
78
+ if (!runId)
79
+ return undefined;
80
+ const runDir = join(root, runId);
81
+ const reportPath = join(runDir, "report.md");
82
+ const findingsPath = join(runDir, "findings.json");
83
+ if (!existsSync(reportPath) || !existsSync(findingsPath)) {
84
+ return undefined;
85
+ }
86
+ const metaPath = join(runDir, "meta.json");
87
+ let summary;
88
+ let profile;
89
+ let completedAt;
90
+ let findingsCount;
91
+ if (existsSync(metaPath)) {
92
+ try {
93
+ const parsed = JSON.parse(readFileSync(metaPath, "utf8"));
94
+ summary = typeof parsed.summary === "string" ? parsed.summary : undefined;
95
+ profile =
96
+ parsed.profile === "quick" || parsed.profile === "full" ? parsed.profile : undefined;
97
+ completedAt = typeof parsed.completedAt === "string" ? parsed.completedAt : undefined;
98
+ findingsCount = typeof parsed.findings === "number" ? parsed.findings : undefined;
99
+ }
100
+ catch {
101
+ // Keep metadata optional.
102
+ }
103
+ }
104
+ return {
105
+ runId,
106
+ reportPath,
107
+ findingsPath,
108
+ metaPath: existsSync(metaPath) ? metaPath : undefined,
109
+ summary,
110
+ profile,
111
+ completedAt,
112
+ findings: findingsCount,
113
+ };
114
+ }
115
+ async run(options) {
116
+ const startedAt = nowIso();
117
+ const started = Date.now();
118
+ const profile = options.profile;
119
+ const runId = buildRunId(new Date());
120
+ const autosave = options.autosave !== false;
121
+ const contract = options.contract ? { ...options.contract } : {};
122
+ const { files, findings, counters } = this.scanRepository(profile, contract);
123
+ const findingsLimited = clampFindings(findings, profile === "full" ? 150 : 80);
124
+ const summary = this.buildSummary(counters, findingsLimited);
125
+ const nextSteps = this.buildNextSteps(findingsLimited, contract);
126
+ const completedAt = nowIso();
127
+ const durationMs = Date.now() - started;
128
+ const reportMarkdown = this.buildReportMarkdown({
129
+ runId,
130
+ profile,
131
+ startedAt,
132
+ completedAt,
133
+ durationMs,
134
+ counters,
135
+ findings: findingsLimited,
136
+ summary,
137
+ nextSteps,
138
+ contract,
139
+ });
140
+ const result = {
141
+ runId,
142
+ profile,
143
+ startedAt,
144
+ completedAt,
145
+ durationMs,
146
+ scannedFiles: counters.files,
147
+ scannedLines: counters.lines,
148
+ findings: findingsLimited,
149
+ summary,
150
+ nextSteps,
151
+ reportMarkdown,
152
+ contract,
153
+ autosaved: false,
154
+ };
155
+ if (autosave) {
156
+ const saved = this.saveRunArtifacts(result);
157
+ result.autosaved = true;
158
+ result.reportPath = saved.reportPath;
159
+ result.findingsPath = saved.findingsPath;
160
+ }
161
+ return result;
162
+ }
163
+ saveRunArtifacts(result) {
164
+ const runDir = join(this.getAuditsRoot(), result.runId);
165
+ mkdirSync(runDir, { recursive: true });
166
+ const reportPath = join(runDir, "report.md");
167
+ const findingsPath = join(runDir, "findings.json");
168
+ const metaPath = join(runDir, "meta.json");
169
+ writeFileSync(reportPath, `${result.reportMarkdown}\n`, "utf8");
170
+ writeFileSync(findingsPath, `${JSON.stringify(result.findings, null, 2)}\n`, "utf8");
171
+ writeFileSync(metaPath, `${JSON.stringify({
172
+ runId: result.runId,
173
+ profile: result.profile,
174
+ summary: result.summary,
175
+ startedAt: result.startedAt,
176
+ completedAt: result.completedAt,
177
+ durationMs: result.durationMs,
178
+ files: result.scannedFiles,
179
+ lines: result.scannedLines,
180
+ findings: result.findings.length,
181
+ }, null, 2)}\n`, "utf8");
182
+ return { reportPath, findingsPath };
183
+ }
184
+ scanRepository(profile, contract) {
185
+ const counters = {
186
+ files: 0,
187
+ lines: 0,
188
+ todoCount: 0,
189
+ debugCount: 0,
190
+ unsafeEvalCount: 0,
191
+ anyTypeCount: 0,
192
+ tsIgnoreCount: 0,
193
+ testFiles: 0,
194
+ largeFiles: 0,
195
+ };
196
+ const findings = [];
197
+ const files = this.walkFiles(profile);
198
+ const maxFileBytes = profile === "full" ? 512_000 : 256_000;
199
+ for (const absolutePath of files) {
200
+ const relativePath = toPosixPath(relative(this.cwd, absolutePath));
201
+ const stat = statSync(absolutePath);
202
+ if (stat.size > maxFileBytes) {
203
+ counters.largeFiles += 1;
204
+ findings.push({
205
+ id: `large-file:${relativePath}`,
206
+ title: "Large file in scan scope",
207
+ severity: "medium",
208
+ category: "maintainability",
209
+ detail: `File size ${stat.size} bytes exceeds ${maxFileBytes} bytes threshold for ${profile} profile.`,
210
+ path: relativePath,
211
+ recommendation: "Split file into smaller modules or exclude it from scope if generated.",
212
+ });
213
+ continue;
214
+ }
215
+ let content;
216
+ try {
217
+ content = readFileSync(absolutePath, "utf8");
218
+ }
219
+ catch {
220
+ continue;
221
+ }
222
+ if (content.includes("\u0000"))
223
+ continue;
224
+ const lines = content.split(/\r?\n/);
225
+ counters.files += 1;
226
+ counters.lines += lines.length;
227
+ if (/(^|\/)(test|tests|__tests__)\//i.test(relativePath) || /\.test\./i.test(relativePath)) {
228
+ counters.testFiles += 1;
229
+ }
230
+ for (let i = 0; i < lines.length; i++) {
231
+ const line = lines[i] ?? "";
232
+ const lineNumber = i + 1;
233
+ if (/(TODO|FIXME|HACK)\b/i.test(line)) {
234
+ counters.todoCount += 1;
235
+ if (findings.length < 220) {
236
+ findings.push({
237
+ id: `todo:${relativePath}:${lineNumber}`,
238
+ title: "Outstanding TODO/FIXME marker",
239
+ severity: "low",
240
+ category: "maintainability",
241
+ detail: line.trim(),
242
+ path: relativePath,
243
+ line: lineNumber,
244
+ recommendation: "Convert to tracked issue or resolve before release.",
245
+ });
246
+ }
247
+ }
248
+ if (/\bconsole\.log\(|\bdebugger\b/.test(line)) {
249
+ counters.debugCount += 1;
250
+ if (findings.length < 220) {
251
+ findings.push({
252
+ id: `debug:${relativePath}:${lineNumber}`,
253
+ title: "Debug artifact in code path",
254
+ severity: "medium",
255
+ category: "quality",
256
+ detail: line.trim(),
257
+ path: relativePath,
258
+ line: lineNumber,
259
+ recommendation: "Remove debug call or guard behind explicit debug flag.",
260
+ });
261
+ }
262
+ }
263
+ if (/\beval\s*\(|\bnew Function\s*\(|child_process\.(exec|execSync)\s*\(/.test(line)) {
264
+ counters.unsafeEvalCount += 1;
265
+ if (findings.length < 220) {
266
+ findings.push({
267
+ id: `unsafe:${relativePath}:${lineNumber}`,
268
+ title: "Potentially unsafe dynamic execution",
269
+ severity: "high",
270
+ category: "security",
271
+ detail: line.trim(),
272
+ path: relativePath,
273
+ line: lineNumber,
274
+ recommendation: "Replace with safer static alternatives or strictly sanitize inputs.",
275
+ });
276
+ }
277
+ }
278
+ if (/:\s*any\b|<any>/.test(line)) {
279
+ counters.anyTypeCount += 1;
280
+ }
281
+ if (/@ts-ignore\b/.test(line)) {
282
+ counters.tsIgnoreCount += 1;
283
+ }
284
+ }
285
+ }
286
+ if (counters.anyTypeCount > 15) {
287
+ findings.push({
288
+ id: "types:any-overuse",
289
+ title: "High usage of `any` type",
290
+ severity: "medium",
291
+ category: "type-safety",
292
+ detail: `Detected ${counters.anyTypeCount} occurrences of explicit any type usage.`,
293
+ recommendation: "Replace broad any usage with domain types or unknown + narrowing.",
294
+ });
295
+ }
296
+ if (counters.tsIgnoreCount > 5) {
297
+ findings.push({
298
+ id: "types:ts-ignore-overuse",
299
+ title: "Excessive @ts-ignore usage",
300
+ severity: "medium",
301
+ category: "type-safety",
302
+ detail: `Detected ${counters.tsIgnoreCount} @ts-ignore directives.`,
303
+ recommendation: "Audit and remove ignored type errors; keep only documented exceptions.",
304
+ });
305
+ }
306
+ const gates = contract.quality_gates ?? [];
307
+ if (containsAny(gates.join(" "), ["test", "coverage"]) && counters.testFiles === 0) {
308
+ findings.push({
309
+ id: "contract:tests-missing",
310
+ title: "Contract gate mismatch: tests expected",
311
+ severity: "high",
312
+ category: "contract",
313
+ detail: "Quality gates mention tests/coverage but no test files were detected in scan scope.",
314
+ recommendation: "Add coverage-aligned tests or adjust contract gates before implementation.",
315
+ });
316
+ }
317
+ if (containsAny(gates.join(" "), ["no todo", "todo=0", "todo:0"]) && counters.todoCount > 0) {
318
+ findings.push({
319
+ id: "contract:todo-mismatch",
320
+ title: "Contract gate mismatch: TODO markers present",
321
+ severity: "medium",
322
+ category: "contract",
323
+ detail: `Contract requires TODO cleanup but ${counters.todoCount} markers were found.`,
324
+ recommendation: "Resolve TODO/FIXME markers or relax gate for current cycle.",
325
+ });
326
+ }
327
+ return { files, findings, counters };
328
+ }
329
+ walkFiles(profile) {
330
+ const maxFiles = profile === "full" ? 18_000 : 6_000;
331
+ const stack = [this.cwd];
332
+ const files = [];
333
+ while (stack.length > 0 && files.length < maxFiles) {
334
+ const dir = stack.pop();
335
+ if (!dir)
336
+ break;
337
+ let entries;
338
+ try {
339
+ entries = readdirSync(dir, { withFileTypes: true });
340
+ }
341
+ catch {
342
+ continue;
343
+ }
344
+ for (const entry of entries) {
345
+ const absolutePath = join(dir, entry.name);
346
+ if (entry.isDirectory()) {
347
+ if (EXCLUDED_DIR_NAMES.has(entry.name))
348
+ continue;
349
+ stack.push(absolutePath);
350
+ continue;
351
+ }
352
+ if (!entry.isFile())
353
+ continue;
354
+ if (!SCAN_TEXT_EXTENSIONS.has(getExtension(entry.name)))
355
+ continue;
356
+ files.push(absolutePath);
357
+ if (files.length >= maxFiles)
358
+ break;
359
+ }
360
+ }
361
+ return files.sort((a, b) => a.localeCompare(b));
362
+ }
363
+ buildSummary(counters, findings) {
364
+ const high = countSeverity(findings, "high");
365
+ const medium = countSeverity(findings, "medium");
366
+ const low = countSeverity(findings, "low");
367
+ const highestRisk = findings.find((item) => item.severity === "high") ?? findings[0];
368
+ const highestLine = highestRisk
369
+ ? ` Highest risk: ${highestRisk.title}${highestRisk.path ? ` (${highestRisk.path})` : ""}.`
370
+ : "";
371
+ return `Scanned ${counters.files} files (${counters.lines} lines). Findings: ${high} high, ${medium} medium, ${low} low.${highestLine}`;
372
+ }
373
+ buildNextSteps(findings, contract) {
374
+ const steps = [];
375
+ const high = findings.filter((item) => item.severity === "high");
376
+ const medium = findings.filter((item) => item.severity === "medium");
377
+ if (high.length > 0) {
378
+ steps.push("Address all HIGH findings first and add regression checks before refactors.");
379
+ }
380
+ if (medium.length > 0) {
381
+ steps.push("Batch MEDIUM findings by module and apply low-blast-radius fixes incrementally.");
382
+ }
383
+ if ((contract.quality_gates ?? []).length > 0) {
384
+ steps.push("Re-run /blast after changes to verify contract quality gates.");
385
+ }
386
+ if (steps.length === 0) {
387
+ steps.push("No major risks detected; proceed with planned changes and keep /blast as pre-merge audit.");
388
+ }
389
+ return steps.slice(0, 3);
390
+ }
391
+ buildReportMarkdown(payload) {
392
+ const high = countSeverity(payload.findings, "high");
393
+ const medium = countSeverity(payload.findings, "medium");
394
+ const low = countSeverity(payload.findings, "low");
395
+ const topFindings = payload.findings
396
+ .sort((a, b) => {
397
+ const score = (severity) => (severity === "high" ? 3 : severity === "medium" ? 2 : 1);
398
+ return score(b.severity) - score(a.severity);
399
+ })
400
+ .slice(0, 20);
401
+ const contractLines = [];
402
+ if (payload.contract.goal)
403
+ contractLines.push(`- goal: ${payload.contract.goal}`);
404
+ if (payload.contract.quality_gates && payload.contract.quality_gates.length > 0) {
405
+ contractLines.push(`- quality_gates: ${payload.contract.quality_gates.join("; ")}`);
406
+ }
407
+ if (payload.contract.constraints && payload.contract.constraints.length > 0) {
408
+ contractLines.push(`- constraints: ${payload.contract.constraints.join("; ")}`);
409
+ }
410
+ const contractSection = contractLines.length > 0 ? contractLines.join("\n") : "- none";
411
+ return [
412
+ `# Blast Audit Report`,
413
+ ``,
414
+ `- run_id: ${payload.runId}`,
415
+ `- profile: ${payload.profile}`,
416
+ `- started_at: ${payload.startedAt}`,
417
+ `- completed_at: ${payload.completedAt}`,
418
+ `- duration_ms: ${payload.durationMs}`,
419
+ ``,
420
+ `## Executive Summary`,
421
+ `${payload.summary}`,
422
+ ``,
423
+ `## Scan Metrics`,
424
+ `- files_scanned: ${payload.counters.files}`,
425
+ `- lines_scanned: ${payload.counters.lines}`,
426
+ `- test_files: ${payload.counters.testFiles}`,
427
+ `- TODO/FIXME markers: ${payload.counters.todoCount}`,
428
+ `- debug artifacts: ${payload.counters.debugCount}`,
429
+ `- unsafe dynamic execution markers: ${payload.counters.unsafeEvalCount}`,
430
+ `- large files skipped: ${payload.counters.largeFiles}`,
431
+ ``,
432
+ `## Findings Overview`,
433
+ `- high: ${high}`,
434
+ `- medium: ${medium}`,
435
+ `- low: ${low}`,
436
+ ``,
437
+ `## Contract Context`,
438
+ `${contractSection}`,
439
+ ``,
440
+ `## Top Findings`,
441
+ topFindings.length > 0 ? topFindings.map(serializeFinding).join("\n") : "- none",
442
+ ``,
443
+ `## Recommended Next Steps`,
444
+ payload.nextSteps.map((step, index) => `${index + 1}. ${step}`).join("\n"),
445
+ ].join("\n");
446
+ }
447
+ }
448
+ //# sourceMappingURL=blast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blast.js","sourceRoot":"","sources":["../../src/core/blast.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\nimport type { EngineeringContract } from \"./contract.js\";\n\nexport type BlastProfile = \"quick\" | \"full\";\nexport type BlastSeverity = \"low\" | \"medium\" | \"high\";\n\nexport interface BlastFinding {\n\tid: string;\n\ttitle: string;\n\tseverity: BlastSeverity;\n\tcategory: string;\n\tdetail: string;\n\tpath?: string;\n\tline?: number;\n\trecommendation?: string;\n}\n\nexport interface BlastRunOptions {\n\tprofile: BlastProfile;\n\tautosave?: boolean;\n\tcontract?: EngineeringContract;\n}\n\nexport interface BlastRunResult {\n\trunId: string;\n\tprofile: BlastProfile;\n\tstartedAt: string;\n\tcompletedAt: string;\n\tdurationMs: number;\n\tscannedFiles: number;\n\tscannedLines: number;\n\tfindings: BlastFinding[];\n\tsummary: string;\n\tnextSteps: string[];\n\treportMarkdown: string;\n\tcontract: EngineeringContract;\n\tautosaved: boolean;\n\treportPath?: string;\n\tfindingsPath?: string;\n}\n\nexport interface BlastLastRun {\n\trunId: string;\n\treportPath: string;\n\tfindingsPath: string;\n\tmetaPath?: string;\n\tsummary?: string;\n\tprofile?: BlastProfile;\n\tcompletedAt?: string;\n\tfindings?: number;\n}\n\ntype BlastScanCounters = {\n\tfiles: number;\n\tlines: number;\n\ttodoCount: number;\n\tdebugCount: number;\n\tunsafeEvalCount: number;\n\tanyTypeCount: number;\n\ttsIgnoreCount: number;\n\ttestFiles: number;\n\tlargeFiles: number;\n};\n\nconst SCAN_TEXT_EXTENSIONS = new Set([\n\t\".ts\",\n\t\".tsx\",\n\t\".js\",\n\t\".jsx\",\n\t\".mjs\",\n\t\".cjs\",\n\t\".py\",\n\t\".go\",\n\t\".rs\",\n\t\".java\",\n\t\".json\",\n\t\".yaml\",\n\t\".yml\",\n\t\".toml\",\n\t\".md\",\n\t\".sh\",\n\t\".env\",\n\t\".sql\",\n\t\".html\",\n\t\".css\",\n]);\n\nconst EXCLUDED_DIR_NAMES = new Set([\".git\", \"node_modules\", \"dist\", \"build\", \".iosm\", \".next\", \"coverage\"]);\n\nfunction toPosixPath(value: string): string {\n\treturn value.split(sep).join(\"/\");\n}\n\nfunction getExtension(filePath: string): string {\n\tconst normalized = filePath.toLowerCase();\n\tconst index = normalized.lastIndexOf(\".\");\n\treturn index >= 0 ? normalized.slice(index) : \"\";\n}\n\nfunction containsAny(text: string, terms: string[]): boolean {\n\tconst normalized = text.toLowerCase();\n\treturn terms.some((term) => normalized.includes(term.toLowerCase()));\n}\n\nfunction countSeverity(findings: BlastFinding[], severity: BlastSeverity): number {\n\treturn findings.filter((item) => item.severity === severity).length;\n}\n\nfunction clampFindings(findings: BlastFinding[], max: number): BlastFinding[] {\n\tif (findings.length <= max) return findings;\n\treturn findings.slice(0, max);\n}\n\nfunction serializeFinding(finding: BlastFinding): string {\n\tconst location = finding.path ? ` (${finding.path}${finding.line ? `:${finding.line}` : \"\"})` : \"\";\n\treturn `- [${finding.severity.toUpperCase()}] ${finding.title}${location}\\n ${finding.detail}${finding.recommendation ? `\\n fix: ${finding.recommendation}` : \"\"}`;\n}\n\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nfunction buildRunId(date = new Date()): string {\n\tconst year = date.getUTCFullYear();\n\tconst month = String(date.getUTCMonth() + 1).padStart(2, \"0\");\n\tconst day = String(date.getUTCDate()).padStart(2, \"0\");\n\tconst hours = String(date.getUTCHours()).padStart(2, \"0\");\n\tconst minutes = String(date.getUTCMinutes()).padStart(2, \"0\");\n\tconst seconds = String(date.getUTCSeconds()).padStart(2, \"0\");\n\treturn `${year}-${month}-${day}-${hours}${minutes}${seconds}`;\n}\n\nexport interface BlastServiceOptions {\n\tcwd: string;\n}\n\nexport class BlastService {\n\tprivate readonly cwd: string;\n\n\tconstructor(options: BlastServiceOptions) {\n\t\tthis.cwd = options.cwd;\n\t}\n\n\tgetAuditsRoot(): string {\n\t\treturn join(this.cwd, \".iosm\", \"audits\");\n\t}\n\n\tgetLastRun(): BlastLastRun | undefined {\n\t\tconst root = this.getAuditsRoot();\n\t\tif (!existsSync(root)) return undefined;\n\t\tconst candidates = readdirSync(root, { withFileTypes: true })\n\t\t\t.filter((entry) => entry.isDirectory())\n\t\t\t.map((entry) => entry.name)\n\t\t\t.sort((a, b) => a.localeCompare(b));\n\t\tconst runId = candidates[candidates.length - 1];\n\t\tif (!runId) return undefined;\n\n\t\tconst runDir = join(root, runId);\n\t\tconst reportPath = join(runDir, \"report.md\");\n\t\tconst findingsPath = join(runDir, \"findings.json\");\n\t\tif (!existsSync(reportPath) || !existsSync(findingsPath)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst metaPath = join(runDir, \"meta.json\");\n\t\tlet summary: string | undefined;\n\t\tlet profile: BlastProfile | undefined;\n\t\tlet completedAt: string | undefined;\n\t\tlet findingsCount: number | undefined;\n\t\tif (existsSync(metaPath)) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(readFileSync(metaPath, \"utf8\")) as Record<string, unknown>;\n\t\t\t\tsummary = typeof parsed.summary === \"string\" ? parsed.summary : undefined;\n\t\t\t\tprofile =\n\t\t\t\t\tparsed.profile === \"quick\" || parsed.profile === \"full\" ? (parsed.profile as BlastProfile) : undefined;\n\t\t\t\tcompletedAt = typeof parsed.completedAt === \"string\" ? parsed.completedAt : undefined;\n\t\t\t\tfindingsCount = typeof parsed.findings === \"number\" ? parsed.findings : undefined;\n\t\t\t} catch {\n\t\t\t\t// Keep metadata optional.\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\trunId,\n\t\t\treportPath,\n\t\t\tfindingsPath,\n\t\t\tmetaPath: existsSync(metaPath) ? metaPath : undefined,\n\t\t\tsummary,\n\t\t\tprofile,\n\t\t\tcompletedAt,\n\t\t\tfindings: findingsCount,\n\t\t};\n\t}\n\n\tasync run(options: BlastRunOptions): Promise<BlastRunResult> {\n\t\tconst startedAt = nowIso();\n\t\tconst started = Date.now();\n\t\tconst profile = options.profile;\n\t\tconst runId = buildRunId(new Date());\n\t\tconst autosave = options.autosave !== false;\n\t\tconst contract = options.contract ? { ...options.contract } : {};\n\n\t\tconst { files, findings, counters } = this.scanRepository(profile, contract);\n\t\tconst findingsLimited = clampFindings(findings, profile === \"full\" ? 150 : 80);\n\t\tconst summary = this.buildSummary(counters, findingsLimited);\n\t\tconst nextSteps = this.buildNextSteps(findingsLimited, contract);\n\t\tconst completedAt = nowIso();\n\t\tconst durationMs = Date.now() - started;\n\t\tconst reportMarkdown = this.buildReportMarkdown({\n\t\t\trunId,\n\t\t\tprofile,\n\t\t\tstartedAt,\n\t\t\tcompletedAt,\n\t\t\tdurationMs,\n\t\t\tcounters,\n\t\t\tfindings: findingsLimited,\n\t\t\tsummary,\n\t\t\tnextSteps,\n\t\t\tcontract,\n\t\t});\n\n\t\tconst result: BlastRunResult = {\n\t\t\trunId,\n\t\t\tprofile,\n\t\t\tstartedAt,\n\t\t\tcompletedAt,\n\t\t\tdurationMs,\n\t\t\tscannedFiles: counters.files,\n\t\t\tscannedLines: counters.lines,\n\t\t\tfindings: findingsLimited,\n\t\t\tsummary,\n\t\t\tnextSteps,\n\t\t\treportMarkdown,\n\t\t\tcontract,\n\t\t\tautosaved: false,\n\t\t};\n\n\t\tif (autosave) {\n\t\t\tconst saved = this.saveRunArtifacts(result);\n\t\t\tresult.autosaved = true;\n\t\t\tresult.reportPath = saved.reportPath;\n\t\t\tresult.findingsPath = saved.findingsPath;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate saveRunArtifacts(result: BlastRunResult): { reportPath: string; findingsPath: string } {\n\t\tconst runDir = join(this.getAuditsRoot(), result.runId);\n\t\tmkdirSync(runDir, { recursive: true });\n\n\t\tconst reportPath = join(runDir, \"report.md\");\n\t\tconst findingsPath = join(runDir, \"findings.json\");\n\t\tconst metaPath = join(runDir, \"meta.json\");\n\n\t\twriteFileSync(reportPath, `${result.reportMarkdown}\\n`, \"utf8\");\n\t\twriteFileSync(findingsPath, `${JSON.stringify(result.findings, null, 2)}\\n`, \"utf8\");\n\t\twriteFileSync(\n\t\t\tmetaPath,\n\t\t\t`${JSON.stringify(\n\t\t\t\t{\n\t\t\t\t\trunId: result.runId,\n\t\t\t\t\tprofile: result.profile,\n\t\t\t\t\tsummary: result.summary,\n\t\t\t\t\tstartedAt: result.startedAt,\n\t\t\t\t\tcompletedAt: result.completedAt,\n\t\t\t\t\tdurationMs: result.durationMs,\n\t\t\t\t\tfiles: result.scannedFiles,\n\t\t\t\t\tlines: result.scannedLines,\n\t\t\t\t\tfindings: result.findings.length,\n\t\t\t\t},\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t)}\\n`,\n\t\t\t\"utf8\",\n\t\t);\n\n\t\treturn { reportPath, findingsPath };\n\t}\n\n\tprivate scanRepository(\n\t\tprofile: BlastProfile,\n\t\tcontract: EngineeringContract,\n\t): { files: string[]; findings: BlastFinding[]; counters: BlastScanCounters } {\n\t\tconst counters: BlastScanCounters = {\n\t\t\tfiles: 0,\n\t\t\tlines: 0,\n\t\t\ttodoCount: 0,\n\t\t\tdebugCount: 0,\n\t\t\tunsafeEvalCount: 0,\n\t\t\tanyTypeCount: 0,\n\t\t\ttsIgnoreCount: 0,\n\t\t\ttestFiles: 0,\n\t\t\tlargeFiles: 0,\n\t\t};\n\n\t\tconst findings: BlastFinding[] = [];\n\t\tconst files = this.walkFiles(profile);\n\t\tconst maxFileBytes = profile === \"full\" ? 512_000 : 256_000;\n\n\t\tfor (const absolutePath of files) {\n\t\t\tconst relativePath = toPosixPath(relative(this.cwd, absolutePath));\n\t\t\tconst stat = statSync(absolutePath);\n\t\t\tif (stat.size > maxFileBytes) {\n\t\t\t\tcounters.largeFiles += 1;\n\t\t\t\tfindings.push({\n\t\t\t\t\tid: `large-file:${relativePath}`,\n\t\t\t\t\ttitle: \"Large file in scan scope\",\n\t\t\t\t\tseverity: \"medium\",\n\t\t\t\t\tcategory: \"maintainability\",\n\t\t\t\t\tdetail: `File size ${stat.size} bytes exceeds ${maxFileBytes} bytes threshold for ${profile} profile.`,\n\t\t\t\t\tpath: relativePath,\n\t\t\t\t\trecommendation: \"Split file into smaller modules or exclude it from scope if generated.\",\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet content: string;\n\t\t\ttry {\n\t\t\t\tcontent = readFileSync(absolutePath, \"utf8\");\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (content.includes(\"\\u0000\")) continue;\n\n\t\t\tconst lines = content.split(/\\r?\\n/);\n\t\t\tcounters.files += 1;\n\t\t\tcounters.lines += lines.length;\n\t\t\tif (/(^|\\/)(test|tests|__tests__)\\//i.test(relativePath) || /\\.test\\./i.test(relativePath)) {\n\t\t\t\tcounters.testFiles += 1;\n\t\t\t}\n\n\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\tconst line = lines[i] ?? \"\";\n\t\t\t\tconst lineNumber = i + 1;\n\n\t\t\t\tif (/(TODO|FIXME|HACK)\\b/i.test(line)) {\n\t\t\t\t\tcounters.todoCount += 1;\n\t\t\t\t\tif (findings.length < 220) {\n\t\t\t\t\t\tfindings.push({\n\t\t\t\t\t\t\tid: `todo:${relativePath}:${lineNumber}`,\n\t\t\t\t\t\t\ttitle: \"Outstanding TODO/FIXME marker\",\n\t\t\t\t\t\t\tseverity: \"low\",\n\t\t\t\t\t\t\tcategory: \"maintainability\",\n\t\t\t\t\t\t\tdetail: line.trim(),\n\t\t\t\t\t\t\tpath: relativePath,\n\t\t\t\t\t\t\tline: lineNumber,\n\t\t\t\t\t\t\trecommendation: \"Convert to tracked issue or resolve before release.\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (/\\bconsole\\.log\\(|\\bdebugger\\b/.test(line)) {\n\t\t\t\t\tcounters.debugCount += 1;\n\t\t\t\t\tif (findings.length < 220) {\n\t\t\t\t\t\tfindings.push({\n\t\t\t\t\t\t\tid: `debug:${relativePath}:${lineNumber}`,\n\t\t\t\t\t\t\ttitle: \"Debug artifact in code path\",\n\t\t\t\t\t\t\tseverity: \"medium\",\n\t\t\t\t\t\t\tcategory: \"quality\",\n\t\t\t\t\t\t\tdetail: line.trim(),\n\t\t\t\t\t\t\tpath: relativePath,\n\t\t\t\t\t\t\tline: lineNumber,\n\t\t\t\t\t\t\trecommendation: \"Remove debug call or guard behind explicit debug flag.\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (/\\beval\\s*\\(|\\bnew Function\\s*\\(|child_process\\.(exec|execSync)\\s*\\(/.test(line)) {\n\t\t\t\t\tcounters.unsafeEvalCount += 1;\n\t\t\t\t\tif (findings.length < 220) {\n\t\t\t\t\t\tfindings.push({\n\t\t\t\t\t\t\tid: `unsafe:${relativePath}:${lineNumber}`,\n\t\t\t\t\t\t\ttitle: \"Potentially unsafe dynamic execution\",\n\t\t\t\t\t\t\tseverity: \"high\",\n\t\t\t\t\t\t\tcategory: \"security\",\n\t\t\t\t\t\t\tdetail: line.trim(),\n\t\t\t\t\t\t\tpath: relativePath,\n\t\t\t\t\t\t\tline: lineNumber,\n\t\t\t\t\t\t\trecommendation: \"Replace with safer static alternatives or strictly sanitize inputs.\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (/:\\s*any\\b|<any>/.test(line)) {\n\t\t\t\t\tcounters.anyTypeCount += 1;\n\t\t\t\t}\n\n\t\t\t\tif (/@ts-ignore\\b/.test(line)) {\n\t\t\t\t\tcounters.tsIgnoreCount += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (counters.anyTypeCount > 15) {\n\t\t\tfindings.push({\n\t\t\t\tid: \"types:any-overuse\",\n\t\t\t\ttitle: \"High usage of `any` type\",\n\t\t\t\tseverity: \"medium\",\n\t\t\t\tcategory: \"type-safety\",\n\t\t\t\tdetail: `Detected ${counters.anyTypeCount} occurrences of explicit any type usage.`,\n\t\t\t\trecommendation: \"Replace broad any usage with domain types or unknown + narrowing.\",\n\t\t\t});\n\t\t}\n\t\tif (counters.tsIgnoreCount > 5) {\n\t\t\tfindings.push({\n\t\t\t\tid: \"types:ts-ignore-overuse\",\n\t\t\t\ttitle: \"Excessive @ts-ignore usage\",\n\t\t\t\tseverity: \"medium\",\n\t\t\t\tcategory: \"type-safety\",\n\t\t\t\tdetail: `Detected ${counters.tsIgnoreCount} @ts-ignore directives.`,\n\t\t\t\trecommendation: \"Audit and remove ignored type errors; keep only documented exceptions.\",\n\t\t\t});\n\t\t}\n\n\t\tconst gates = contract.quality_gates ?? [];\n\t\tif (containsAny(gates.join(\" \"), [\"test\", \"coverage\"]) && counters.testFiles === 0) {\n\t\t\tfindings.push({\n\t\t\t\tid: \"contract:tests-missing\",\n\t\t\t\ttitle: \"Contract gate mismatch: tests expected\",\n\t\t\t\tseverity: \"high\",\n\t\t\t\tcategory: \"contract\",\n\t\t\t\tdetail: \"Quality gates mention tests/coverage but no test files were detected in scan scope.\",\n\t\t\t\trecommendation: \"Add coverage-aligned tests or adjust contract gates before implementation.\",\n\t\t\t});\n\t\t}\n\t\tif (containsAny(gates.join(\" \"), [\"no todo\", \"todo=0\", \"todo:0\"]) && counters.todoCount > 0) {\n\t\t\tfindings.push({\n\t\t\t\tid: \"contract:todo-mismatch\",\n\t\t\t\ttitle: \"Contract gate mismatch: TODO markers present\",\n\t\t\t\tseverity: \"medium\",\n\t\t\t\tcategory: \"contract\",\n\t\t\t\tdetail: `Contract requires TODO cleanup but ${counters.todoCount} markers were found.`,\n\t\t\t\trecommendation: \"Resolve TODO/FIXME markers or relax gate for current cycle.\",\n\t\t\t});\n\t\t}\n\n\t\treturn { files, findings, counters };\n\t}\n\n\tprivate walkFiles(profile: BlastProfile): string[] {\n\t\tconst maxFiles = profile === \"full\" ? 18_000 : 6_000;\n\t\tconst stack = [this.cwd];\n\t\tconst files: string[] = [];\n\n\t\twhile (stack.length > 0 && files.length < maxFiles) {\n\t\t\tconst dir = stack.pop();\n\t\t\tif (!dir) break;\n\n\t\t\tlet entries;\n\t\t\ttry {\n\t\t\t\tentries = readdirSync(dir, { withFileTypes: true });\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tconst absolutePath = join(dir, entry.name);\n\t\t\t\tif (entry.isDirectory()) {\n\t\t\t\t\tif (EXCLUDED_DIR_NAMES.has(entry.name)) continue;\n\t\t\t\t\tstack.push(absolutePath);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (!entry.isFile()) continue;\n\t\t\t\tif (!SCAN_TEXT_EXTENSIONS.has(getExtension(entry.name))) continue;\n\t\t\t\tfiles.push(absolutePath);\n\t\t\t\tif (files.length >= maxFiles) break;\n\t\t\t}\n\t\t}\n\n\t\treturn files.sort((a, b) => a.localeCompare(b));\n\t}\n\n\tprivate buildSummary(counters: BlastScanCounters, findings: BlastFinding[]): string {\n\t\tconst high = countSeverity(findings, \"high\");\n\t\tconst medium = countSeverity(findings, \"medium\");\n\t\tconst low = countSeverity(findings, \"low\");\n\t\tconst highestRisk = findings.find((item) => item.severity === \"high\") ?? findings[0];\n\n\t\tconst highestLine = highestRisk\n\t\t\t? ` Highest risk: ${highestRisk.title}${highestRisk.path ? ` (${highestRisk.path})` : \"\"}.`\n\t\t\t: \"\";\n\t\treturn `Scanned ${counters.files} files (${counters.lines} lines). Findings: ${high} high, ${medium} medium, ${low} low.${highestLine}`;\n\t}\n\n\tprivate buildNextSteps(findings: BlastFinding[], contract: EngineeringContract): string[] {\n\t\tconst steps: string[] = [];\n\t\tconst high = findings.filter((item) => item.severity === \"high\");\n\t\tconst medium = findings.filter((item) => item.severity === \"medium\");\n\n\t\tif (high.length > 0) {\n\t\t\tsteps.push(\"Address all HIGH findings first and add regression checks before refactors.\");\n\t\t}\n\t\tif (medium.length > 0) {\n\t\t\tsteps.push(\"Batch MEDIUM findings by module and apply low-blast-radius fixes incrementally.\");\n\t\t}\n\t\tif ((contract.quality_gates ?? []).length > 0) {\n\t\t\tsteps.push(\"Re-run /blast after changes to verify contract quality gates.\");\n\t\t}\n\t\tif (steps.length === 0) {\n\t\t\tsteps.push(\"No major risks detected; proceed with planned changes and keep /blast as pre-merge audit.\");\n\t\t}\n\t\treturn steps.slice(0, 3);\n\t}\n\n\tprivate buildReportMarkdown(payload: {\n\t\trunId: string;\n\t\tprofile: BlastProfile;\n\t\tstartedAt: string;\n\t\tcompletedAt: string;\n\t\tdurationMs: number;\n\t\tcounters: BlastScanCounters;\n\t\tfindings: BlastFinding[];\n\t\tsummary: string;\n\t\tnextSteps: string[];\n\t\tcontract: EngineeringContract;\n\t}): string {\n\t\tconst high = countSeverity(payload.findings, \"high\");\n\t\tconst medium = countSeverity(payload.findings, \"medium\");\n\t\tconst low = countSeverity(payload.findings, \"low\");\n\t\tconst topFindings = payload.findings\n\t\t\t.sort((a, b) => {\n\t\t\t\tconst score = (severity: BlastSeverity): number => (severity === \"high\" ? 3 : severity === \"medium\" ? 2 : 1);\n\t\t\t\treturn score(b.severity) - score(a.severity);\n\t\t\t})\n\t\t\t.slice(0, 20);\n\n\t\tconst contractLines: string[] = [];\n\t\tif (payload.contract.goal) contractLines.push(`- goal: ${payload.contract.goal}`);\n\t\tif (payload.contract.quality_gates && payload.contract.quality_gates.length > 0) {\n\t\t\tcontractLines.push(`- quality_gates: ${payload.contract.quality_gates.join(\"; \")}`);\n\t\t}\n\t\tif (payload.contract.constraints && payload.contract.constraints.length > 0) {\n\t\t\tcontractLines.push(`- constraints: ${payload.contract.constraints.join(\"; \")}`);\n\t\t}\n\t\tconst contractSection = contractLines.length > 0 ? contractLines.join(\"\\n\") : \"- none\";\n\n\t\treturn [\n\t\t\t`# Blast Audit Report`,\n\t\t\t``,\n\t\t\t`- run_id: ${payload.runId}`,\n\t\t\t`- profile: ${payload.profile}`,\n\t\t\t`- started_at: ${payload.startedAt}`,\n\t\t\t`- completed_at: ${payload.completedAt}`,\n\t\t\t`- duration_ms: ${payload.durationMs}`,\n\t\t\t``,\n\t\t\t`## Executive Summary`,\n\t\t\t`${payload.summary}`,\n\t\t\t``,\n\t\t\t`## Scan Metrics`,\n\t\t\t`- files_scanned: ${payload.counters.files}`,\n\t\t\t`- lines_scanned: ${payload.counters.lines}`,\n\t\t\t`- test_files: ${payload.counters.testFiles}`,\n\t\t\t`- TODO/FIXME markers: ${payload.counters.todoCount}`,\n\t\t\t`- debug artifacts: ${payload.counters.debugCount}`,\n\t\t\t`- unsafe dynamic execution markers: ${payload.counters.unsafeEvalCount}`,\n\t\t\t`- large files skipped: ${payload.counters.largeFiles}`,\n\t\t\t``,\n\t\t\t`## Findings Overview`,\n\t\t\t`- high: ${high}`,\n\t\t\t`- medium: ${medium}`,\n\t\t\t`- low: ${low}`,\n\t\t\t``,\n\t\t\t`## Contract Context`,\n\t\t\t`${contractSection}`,\n\t\t\t``,\n\t\t\t`## Top Findings`,\n\t\t\ttopFindings.length > 0 ? topFindings.map(serializeFinding).join(\"\\n\") : \"- none\",\n\t\t\t``,\n\t\t\t`## Recommended Next Steps`,\n\t\t\tpayload.nextSteps.map((step, index) => `${index + 1}. ${step}`).join(\"\\n\"),\n\t\t].join(\"\\n\");\n\t}\n}\n"]}
@@ -0,0 +1,54 @@
1
+ export type ContractScope = "project" | "session";
2
+ export interface EngineeringContract {
3
+ goal?: string;
4
+ scope_include?: string[];
5
+ scope_exclude?: string[];
6
+ constraints?: string[];
7
+ quality_gates?: string[];
8
+ definition_of_done?: string[];
9
+ assumptions?: string[];
10
+ non_goals?: string[];
11
+ risks?: string[];
12
+ deliverables?: string[];
13
+ success_metrics?: string[];
14
+ stakeholders?: string[];
15
+ owner?: string;
16
+ timebox?: string;
17
+ notes?: string;
18
+ }
19
+ export interface ContractState {
20
+ projectPath: string;
21
+ hasProjectFile: boolean;
22
+ project: EngineeringContract;
23
+ sessionOverlay: EngineeringContract;
24
+ effective: EngineeringContract;
25
+ }
26
+ export declare class ContractValidationError extends Error {
27
+ constructor(message: string);
28
+ }
29
+ export declare class ContractParseError extends Error {
30
+ readonly filePath: string;
31
+ constructor(filePath: string, message: string);
32
+ }
33
+ export declare function normalizeEngineeringContract(payload: unknown): EngineeringContract;
34
+ export declare function deepMergeContracts(base: EngineeringContract, override: EngineeringContract): EngineeringContract;
35
+ export interface ContractServiceOptions {
36
+ cwd: string;
37
+ }
38
+ export declare class ContractService {
39
+ private readonly cwd;
40
+ private sessionOverlay;
41
+ constructor(options: ContractServiceOptions);
42
+ getProjectPath(): string;
43
+ getSessionOverlay(): EngineeringContract;
44
+ setSessionOverlay(payload: unknown): EngineeringContract;
45
+ clearSessionOverlay(): void;
46
+ loadProjectContract(): EngineeringContract;
47
+ saveProjectContract(payload: unknown): EngineeringContract;
48
+ clearProjectContract(): boolean;
49
+ save(scope: ContractScope, payload: unknown): EngineeringContract;
50
+ clear(scope: ContractScope): boolean;
51
+ getState(): ContractState;
52
+ buildPromptContext(maxItemsPerList?: number): string | undefined;
53
+ }
54
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/core/contract.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,mBAAmB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,cAAc,EAAE,mBAAmB,CAAC;IACpC,SAAS,EAAE,mBAAmB,CAAC;CAC/B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE,MAAM;CAI3B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK7C;AAsID,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAyClF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,mBAAmB,CAWhH;AA4BD,MAAM,WAAW,sBAAsB;IACtC,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,cAAc,CAA2B;gBAErC,OAAO,EAAE,sBAAsB;IAI3C,cAAc,IAAI,MAAM;IAIxB,iBAAiB,IAAI,mBAAmB;IAIxC,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB;IAMxD,mBAAmB,IAAI,IAAI;IAI3B,mBAAmB,IAAI,mBAAmB;IAI1C,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB;IAM1D,oBAAoB,IAAI,OAAO;IAO/B,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB;IAOjE,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IAQpC,QAAQ,IAAI,aAAa;IAezB,kBAAkB,CAAC,eAAe,SAAI,GAAG,MAAM,GAAG,SAAS;CAkC3D"}