sentinelayer-cli 0.4.4 → 0.6.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.
Files changed (149) hide show
  1. package/README.md +996 -998
  2. package/bin/create-sentinelayer.js +5 -5
  3. package/bin/sentinelayer-cli.js +4 -4
  4. package/bin/sl.js +5 -5
  5. package/package.json +63 -63
  6. package/src/agents/jules/config/definition.js +160 -209
  7. package/src/agents/jules/config/system-prompt.js +182 -175
  8. package/src/agents/jules/error-intake.js +51 -51
  9. package/src/agents/jules/fix-cycle.js +17 -377
  10. package/src/agents/jules/loop.js +450 -367
  11. package/src/agents/jules/pulse.js +10 -327
  12. package/src/agents/jules/stream.js +186 -186
  13. package/src/agents/jules/swarm/file-scanner.js +74 -74
  14. package/src/agents/jules/swarm/index.js +11 -11
  15. package/src/agents/jules/swarm/orchestrator.js +362 -362
  16. package/src/agents/jules/swarm/pattern-hunter.js +123 -123
  17. package/src/agents/jules/swarm/sub-agent.js +309 -308
  18. package/src/agents/jules/tools/aidenid-email.js +189 -0
  19. package/src/agents/jules/tools/auth-audit.js +1691 -557
  20. package/src/agents/jules/tools/dispatch.js +335 -327
  21. package/src/agents/jules/tools/file-edit.js +2 -180
  22. package/src/agents/jules/tools/file-read.js +2 -100
  23. package/src/agents/jules/tools/frontend-analyze.js +570 -570
  24. package/src/agents/jules/tools/glob.js +2 -168
  25. package/src/agents/jules/tools/grep.js +2 -228
  26. package/src/agents/jules/tools/index.js +29 -29
  27. package/src/agents/jules/tools/path-guards.js +2 -161
  28. package/src/agents/jules/tools/runtime-audit.js +507 -503
  29. package/src/agents/jules/tools/shell.js +2 -383
  30. package/src/agents/jules/tools/url-policy.js +100 -100
  31. package/src/agents/persona-visuals.js +61 -0
  32. package/src/agents/shared-tools/dispatch-core.js +315 -0
  33. package/src/agents/shared-tools/file-edit.js +180 -0
  34. package/src/agents/shared-tools/file-read.js +100 -0
  35. package/src/agents/shared-tools/glob.js +168 -0
  36. package/src/agents/shared-tools/grep.js +228 -0
  37. package/src/agents/shared-tools/index.js +46 -0
  38. package/src/agents/shared-tools/path-guards.js +161 -0
  39. package/src/agents/shared-tools/shell.js +383 -0
  40. package/src/ai/aidenid.js +1009 -972
  41. package/src/ai/client.js +553 -508
  42. package/src/ai/domain-target-store.js +268 -268
  43. package/src/ai/identity-store.js +270 -270
  44. package/src/ai/proxy.js +137 -0
  45. package/src/ai/site-store.js +145 -145
  46. package/src/audit/agents/architecture.js +180 -180
  47. package/src/audit/agents/compliance.js +179 -179
  48. package/src/audit/agents/documentation.js +165 -165
  49. package/src/audit/agents/performance.js +145 -145
  50. package/src/audit/agents/security.js +215 -215
  51. package/src/audit/agents/testing.js +172 -172
  52. package/src/audit/orchestrator.js +557 -557
  53. package/src/audit/package.js +204 -204
  54. package/src/audit/registry.js +284 -284
  55. package/src/audit/replay.js +103 -103
  56. package/src/auth/gate.js +371 -126
  57. package/src/auth/http.js +611 -270
  58. package/src/auth/service.js +1106 -891
  59. package/src/auth/session-store.js +813 -359
  60. package/src/cli.js +252 -252
  61. package/src/commands/ai/identity-lifecycle.js +1338 -1338
  62. package/src/commands/ai/provision-governance.js +1272 -1272
  63. package/src/commands/ai/shared.js +147 -147
  64. package/src/commands/ai.js +11 -11
  65. package/src/commands/apply.js +12 -12
  66. package/src/commands/audit.js +1166 -1166
  67. package/src/commands/auth.js +419 -375
  68. package/src/commands/chat.js +191 -191
  69. package/src/commands/config.js +184 -184
  70. package/src/commands/cost.js +311 -311
  71. package/src/commands/daemon/core.js +850 -850
  72. package/src/commands/daemon/extended.js +1048 -1048
  73. package/src/commands/daemon/shared.js +213 -213
  74. package/src/commands/daemon.js +11 -11
  75. package/src/commands/guide.js +174 -174
  76. package/src/commands/ingest.js +58 -58
  77. package/src/commands/init.js +55 -55
  78. package/src/commands/legacy-args.js +10 -10
  79. package/src/commands/mcp.js +461 -461
  80. package/src/commands/omargate.js +29 -21
  81. package/src/commands/persona.js +20 -20
  82. package/src/commands/plugin.js +260 -260
  83. package/src/commands/policy.js +132 -132
  84. package/src/commands/prompt.js +238 -238
  85. package/src/commands/review.js +704 -704
  86. package/src/commands/scan.js +872 -866
  87. package/src/commands/spec.js +716 -716
  88. package/src/commands/swarm.js +651 -651
  89. package/src/commands/telemetry.js +202 -202
  90. package/src/commands/watch.js +511 -510
  91. package/src/config/agent-dictionary.js +182 -182
  92. package/src/config/io.js +56 -56
  93. package/src/config/paths.js +18 -18
  94. package/src/config/schema.js +55 -55
  95. package/src/config/service.js +184 -184
  96. package/src/cost/budget.js +235 -235
  97. package/src/cost/history.js +188 -188
  98. package/src/cost/tracker.js +171 -171
  99. package/src/daemon/artifact-lineage.js +534 -534
  100. package/src/daemon/assignment-ledger.js +770 -770
  101. package/src/daemon/ast-parser-layer.js +258 -258
  102. package/src/daemon/budget-governor.js +633 -633
  103. package/src/daemon/callgraph-overlay.js +646 -646
  104. package/src/daemon/error-worker.js +626 -626
  105. package/src/daemon/fix-cycle.js +377 -0
  106. package/src/daemon/hybrid-mapper.js +929 -929
  107. package/src/daemon/jira-lifecycle.js +632 -632
  108. package/src/daemon/operator-control.js +657 -657
  109. package/src/daemon/pulse.js +327 -0
  110. package/src/daemon/reliability-lane.js +471 -471
  111. package/src/daemon/watchdog.js +971 -971
  112. package/src/guide/generator.js +316 -316
  113. package/src/ingest/engine.js +918 -918
  114. package/src/interactive/index.js +97 -95
  115. package/src/legacy-cli.js +2994 -2592
  116. package/src/mcp/registry.js +695 -695
  117. package/src/memory/blackboard.js +301 -301
  118. package/src/memory/retrieval.js +581 -581
  119. package/src/plugin/manifest.js +553 -553
  120. package/src/policy/packs.js +144 -144
  121. package/src/prompt/generator.js +118 -118
  122. package/src/review/ai-review.js +679 -669
  123. package/src/review/local-review.js +1305 -1295
  124. package/src/review/omargate-interactive.js +68 -0
  125. package/src/review/omargate-orchestrator.js +300 -0
  126. package/src/review/persona-prompts.js +296 -0
  127. package/src/review/replay.js +235 -235
  128. package/src/review/report.js +664 -664
  129. package/src/review/scan-modes.js +42 -0
  130. package/src/review/spec-binding.js +487 -487
  131. package/src/scaffold/generator.js +67 -67
  132. package/src/scaffold/templates.js +150 -150
  133. package/src/scan/generator.js +418 -418
  134. package/src/scan/gh-secrets.js +107 -107
  135. package/src/spec/generator.js +519 -519
  136. package/src/spec/regenerate.js +237 -237
  137. package/src/spec/templates.js +91 -91
  138. package/src/swarm/dashboard.js +247 -247
  139. package/src/swarm/factory.js +363 -363
  140. package/src/swarm/pentest.js +934 -934
  141. package/src/swarm/registry.js +419 -419
  142. package/src/swarm/report.js +158 -158
  143. package/src/swarm/runtime.js +576 -576
  144. package/src/swarm/scenario-dsl.js +272 -272
  145. package/src/telemetry/ledger.js +302 -302
  146. package/src/telemetry/session-tracker.js +234 -118
  147. package/src/telemetry/sync.js +203 -199
  148. package/src/ui/command-hints.js +13 -0
  149. package/src/ui/markdown.js +220 -220
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Interactive post-scan menu for Omar Gate deep-dive.
3
+ *
4
+ * After the scan completes, prompts the user to select a domain agent
5
+ * for full agentic loop analysis (multi-turn, tool-using).
6
+ */
7
+
8
+ import { createInterface } from "node:readline/promises";
9
+ import { stdin as input, stdout as output } from "node:process";
10
+ import pc from "picocolors";
11
+
12
+ import { PERSONA_IDS } from "./persona-prompts.js";
13
+
14
+ /**
15
+ * Show interactive persona selection after Omar Gate scan.
16
+ *
17
+ * @param {object} options
18
+ * @param {object} options.scanResult - Result from runOmarGateOrchestrator
19
+ * @returns {Promise<string|null>} Selected persona ID, "all", or null (skip)
20
+ */
21
+ export async function promptPersonaDeepDive({ scanResult } = {}) {
22
+ const summary = scanResult?.summary || {};
23
+ const personas = scanResult?.personas || [];
24
+
25
+ console.log("");
26
+ console.log(pc.bold("Omar Gate scan complete."));
27
+ console.log(
28
+ `Findings: P0=${summary.P0 || 0} P1=${summary.P1 || 0} P2=${summary.P2 || 0} P3=${summary.P3 || 0} | ` +
29
+ `Cost: $${(scanResult?.totalCostUsd || 0).toFixed(4)} | ` +
30
+ `Duration: ${((scanResult?.totalDurationMs || 0) / 1000).toFixed(1)}s`
31
+ );
32
+ console.log("");
33
+
34
+ // Show persona results
35
+ for (const p of personas) {
36
+ const icon = p.status === "ok" ? pc.green("✓") : p.status === "skipped" ? pc.gray("○") : pc.red("✗");
37
+ const count = p.findings || 0;
38
+ console.log(` ${icon} ${p.id} — ${count} finding${count === 1 ? "" : "s"}`);
39
+ }
40
+
41
+ console.log("");
42
+ console.log(pc.gray("Deep-dive runs a full agentic loop (multi-turn, tool-using) for deeper analysis."));
43
+ console.log(pc.gray(`Available: ${PERSONA_IDS.join(", ")}, all, none`));
44
+ console.log("");
45
+
46
+ const rl = createInterface({ input, output });
47
+ try {
48
+ const answer = await rl.question(pc.cyan("Deep-dive into which agent? [none] "));
49
+ const normalized = String(answer || "").trim().toLowerCase();
50
+
51
+ if (!normalized || normalized === "none" || normalized === "n" || normalized === "skip") {
52
+ return null;
53
+ }
54
+
55
+ if (normalized === "all") {
56
+ return "all";
57
+ }
58
+
59
+ if (PERSONA_IDS.includes(normalized)) {
60
+ return normalized;
61
+ }
62
+
63
+ console.log(pc.yellow(`Unknown agent '${normalized}'. Skipping deep-dive.`));
64
+ return null;
65
+ } finally {
66
+ rl.close();
67
+ }
68
+ }
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Omar Gate multi-persona orchestrator.
3
+ *
4
+ * Runs N persona-scoped AI review calls in parallel (bounded concurrency),
5
+ * merges findings into a blackboard, deduplicates, and produces a unified report.
6
+ */
7
+
8
+ import { randomUUID } from "node:crypto";
9
+
10
+ import { runAiReviewLayer } from "./ai-review.js";
11
+ import { buildPersonaReviewPrompt, PERSONA_IDS } from "./persona-prompts.js";
12
+ import { resolveScanMode } from "./scan-modes.js";
13
+ import { syncRunToDashboard } from "../telemetry/sync.js";
14
+
15
+ /**
16
+ * Run bounded-concurrency parallel execution.
17
+ * @param {Array} items
18
+ * @param {number} maxConcurrent
19
+ * @param {Function} fn
20
+ * @returns {Promise<Array>}
21
+ */
22
+ async function runWithConcurrency(items, maxConcurrent, fn) {
23
+ const results = [];
24
+ const executing = new Set();
25
+
26
+ for (const item of items) {
27
+ const p = fn(item).then((result) => {
28
+ executing.delete(p);
29
+ return result;
30
+ });
31
+ executing.add(p);
32
+ results.push(p);
33
+
34
+ if (executing.size >= maxConcurrent) {
35
+ await Promise.race(executing);
36
+ }
37
+ }
38
+
39
+ return Promise.allSettled(results);
40
+ }
41
+
42
+ /**
43
+ * Deduplicate findings by file:line:title key.
44
+ */
45
+ function deduplicateFindings(allFindings) {
46
+ const seen = new Set();
47
+ const unique = [];
48
+ for (const finding of allFindings) {
49
+ const key = `${finding.file || ""}:${finding.line || 0}:${String(finding.title || finding.message || "").toLowerCase().slice(0, 80)}`;
50
+ if (seen.has(key)) continue;
51
+ seen.add(key);
52
+ unique.push(finding);
53
+ }
54
+ return unique;
55
+ }
56
+
57
+ /**
58
+ * Run the Omar Gate multi-persona orchestrator.
59
+ *
60
+ * @param {object} options
61
+ * @param {string} options.targetPath - Repository path
62
+ * @param {string} [options.scanMode] - "baseline", "deep", or "full-depth"
63
+ * @param {number} [options.maxParallel] - Max concurrent persona calls (default 4)
64
+ * @param {string} [options.provider] - LLM provider override
65
+ * @param {string} [options.model] - LLM model override
66
+ * @param {number} [options.maxCostUsd] - Global cost ceiling (default 5.0)
67
+ * @param {boolean} [options.dryRun] - Dry-run mode (no LLM calls)
68
+ * @param {string} [options.outputDir] - Output directory override
69
+ * @param {object} [options.deterministic] - Deterministic scan results
70
+ * @param {Function} [options.onEvent] - Event callback for streaming
71
+ * @returns {Promise<object>} Orchestrated results
72
+ */
73
+ export async function runOmarGateOrchestrator({
74
+ targetPath,
75
+ scanMode = "deep",
76
+ maxParallel = 4,
77
+ provider = "",
78
+ model = "",
79
+ maxCostUsd = 5.0,
80
+ dryRun = false,
81
+ outputDir = "",
82
+ deterministic = null,
83
+ onEvent = null,
84
+ } = {}) {
85
+ const runId = `omargate-${Date.now()}-${randomUUID().slice(0, 8)}`;
86
+ const startTime = Date.now();
87
+
88
+ const { mode, personas } = resolveScanMode(scanMode);
89
+
90
+ if (onEvent) {
91
+ onEvent({
92
+ stream: "sl_event",
93
+ event: "omargate_start",
94
+ payload: { runId, mode, personas, maxParallel, maxCostUsd, dryRun },
95
+ });
96
+ }
97
+
98
+ const detSummary = deterministic?.summary || { P0: 0, P1: 0, P2: 0, P3: 0 };
99
+ const detFindings = deterministic?.findings || [];
100
+
101
+ // Per-persona cost budget = global / persona count (with minimum floor)
102
+ const perPersonaCost = Math.max(0.25, maxCostUsd / personas.length);
103
+ let runningCostUsd = 0;
104
+
105
+ const personaResults = await runWithConcurrency(personas, maxParallel, async (personaId) => {
106
+ // Global budget check — skip remaining personas if exhausted
107
+ if (runningCostUsd >= maxCostUsd) {
108
+ if (onEvent) {
109
+ onEvent({
110
+ stream: "sl_event",
111
+ event: "persona_skipped",
112
+ payload: { personaId, reason: "global_budget_exhausted", runningCostUsd, maxCostUsd },
113
+ });
114
+ }
115
+ return {
116
+ personaId,
117
+ status: "skipped",
118
+ findings: [],
119
+ summary: { P0: 0, P1: 0, P2: 0, P3: 0 },
120
+ costUsd: 0,
121
+ durationMs: 0,
122
+ reason: "global_budget_exhausted",
123
+ };
124
+ }
125
+
126
+ const personaStart = Date.now();
127
+
128
+ if (onEvent) {
129
+ onEvent({
130
+ stream: "sl_event",
131
+ event: "persona_start",
132
+ payload: { personaId, mode, runId },
133
+ });
134
+ }
135
+
136
+ try {
137
+ const systemPrompt = buildPersonaReviewPrompt({
138
+ personaId,
139
+ targetPath,
140
+ deterministicSummary: detSummary,
141
+ });
142
+
143
+ const result = await runAiReviewLayer({
144
+ targetPath,
145
+ mode: "full",
146
+ runId: `${runId}-${personaId}`,
147
+ runDirectory: targetPath,
148
+ deterministic: {
149
+ summary: detSummary,
150
+ findings: detFindings,
151
+ metadata: deterministic?.metadata || {},
152
+ },
153
+ outputDir,
154
+ provider: provider || undefined,
155
+ model: model || undefined,
156
+ maxCostUsd: perPersonaCost,
157
+ dryRun,
158
+ env: process.env,
159
+ });
160
+
161
+ const findings = (result?.findings || []).map((f) => ({
162
+ ...f,
163
+ persona: personaId,
164
+ layer: personaId,
165
+ }));
166
+
167
+ if (onEvent) {
168
+ for (const finding of findings) {
169
+ onEvent({
170
+ stream: "sl_event",
171
+ event: "persona_finding",
172
+ payload: { personaId, ...finding },
173
+ });
174
+ }
175
+ onEvent({
176
+ stream: "sl_event",
177
+ event: "persona_complete",
178
+ payload: {
179
+ personaId,
180
+ findings: findings.length,
181
+ summary: result?.summary || {},
182
+ costUsd: result?.costUsd || 0,
183
+ durationMs: Date.now() - personaStart,
184
+ },
185
+ });
186
+ }
187
+
188
+ const personaCost = result?.costUsd || 0;
189
+ runningCostUsd += personaCost;
190
+
191
+ return {
192
+ personaId,
193
+ status: "ok",
194
+ findings,
195
+ summary: result?.summary || { P0: 0, P1: 0, P2: 0, P3: 0 },
196
+ costUsd: personaCost,
197
+ model: result?.model || model || null,
198
+ durationMs: Date.now() - personaStart,
199
+ };
200
+ } catch (err) {
201
+ if (onEvent) {
202
+ onEvent({
203
+ stream: "sl_event",
204
+ event: "persona_error",
205
+ payload: { personaId, error: err.message },
206
+ });
207
+ }
208
+ return {
209
+ personaId,
210
+ status: "error",
211
+ findings: [],
212
+ summary: { P0: 0, P1: 0, P2: 0, P3: 0 },
213
+ costUsd: 0,
214
+ error: err.message,
215
+ durationMs: Date.now() - personaStart,
216
+ };
217
+ }
218
+ });
219
+
220
+ // Collect results (handle settled promises)
221
+ const settled = personaResults.map((r) =>
222
+ r.status === "fulfilled" ? r.value : { personaId: "unknown", status: "error", findings: [], summary: { P0: 0, P1: 0, P2: 0, P3: 0 }, costUsd: 0, error: r.reason?.message || "unknown" }
223
+ );
224
+
225
+ // Merge and deduplicate findings
226
+ const allAiFindings = settled.flatMap((r) => r.findings);
227
+ const uniqueFindings = deduplicateFindings(allAiFindings);
228
+
229
+ // Compute combined summary
230
+ const combinedP0 = uniqueFindings.filter((f) => f.severity === "P0").length;
231
+ const combinedP1 = uniqueFindings.filter((f) => f.severity === "P1").length;
232
+ const combinedP2 = uniqueFindings.filter((f) => f.severity === "P2").length;
233
+ const combinedP3 = uniqueFindings.filter((f) => f.severity === "P3").length;
234
+ const totalCost = settled.reduce((sum, r) => sum + (r.costUsd || 0), 0);
235
+ const totalDuration = Date.now() - startTime;
236
+
237
+ const result = {
238
+ runId,
239
+ mode,
240
+ personas: settled.map((r) => ({
241
+ id: r.personaId,
242
+ status: r.status,
243
+ findings: r.findings.length,
244
+ costUsd: r.costUsd,
245
+ durationMs: r.durationMs,
246
+ error: r.error || null,
247
+ })),
248
+ findings: uniqueFindings,
249
+ summary: {
250
+ P0: combinedP0,
251
+ P1: combinedP1,
252
+ P2: combinedP2,
253
+ P3: combinedP3,
254
+ blocking: combinedP0 > 0 || combinedP1 > 0,
255
+ },
256
+ totalCostUsd: totalCost,
257
+ totalDurationMs: totalDuration,
258
+ dryRun,
259
+ };
260
+
261
+ if (onEvent) {
262
+ onEvent({
263
+ stream: "sl_event",
264
+ event: "omargate_complete",
265
+ payload: {
266
+ runId,
267
+ mode,
268
+ personaCount: settled.length,
269
+ findings: uniqueFindings.length,
270
+ summary: result.summary,
271
+ totalCostUsd: totalCost,
272
+ totalDurationMs: totalDuration,
273
+ },
274
+ });
275
+ }
276
+
277
+ // Fire-and-forget telemetry sync to dashboard
278
+ syncRunToDashboard({
279
+ command: `omargate deep --scan-mode ${mode}`,
280
+ persona: "omar-orchestrator",
281
+ usage: {
282
+ inputTokens: 0,
283
+ outputTokens: 0,
284
+ costUsd: totalCost,
285
+ durationMs: totalDuration,
286
+ toolCalls: personas.length,
287
+ },
288
+ summary: result.summary,
289
+ stopReason: result.summary.blocking ? "blocked" : "passed",
290
+ personaBreakdown: settled.map((r) => ({
291
+ personaId: r.personaId,
292
+ findings: r.findings?.length || 0,
293
+ costUsd: r.costUsd || 0,
294
+ durationMs: r.durationMs || 0,
295
+ status: r.status,
296
+ })),
297
+ }).catch(() => {});
298
+
299
+ return result;
300
+ }
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Persona-scoped system prompts for Omar Gate AI analysis.
3
+ *
4
+ * Each persona gets a domain-focused prompt that constrains the LLM
5
+ * to analyze code through a specific security/quality lens.
6
+ */
7
+
8
+ const PERSONA_PROMPTS = {
9
+ security: {
10
+ role: "Nina Patel — Security Specialist",
11
+ focus: `You are a security specialist reviewing code for exploitable vulnerabilities.
12
+
13
+ Focus areas:
14
+ - Authentication and authorization bypass paths
15
+ - Secret/credential exposure in code, configs, logs, and environment
16
+ - Injection vectors: SQL, shell, XSS, SSRF, path traversal
17
+ - Cryptographic weaknesses: weak hashing, hardcoded keys, insecure TLS
18
+ - Session management: fixation, token leakage, cookie misconfiguration
19
+ - Rate limiting gaps on auth and payment endpoints
20
+ - CORS misconfiguration allowing unauthorized origins
21
+ - Insecure deserialization and dynamic code execution (eval, Function)
22
+
23
+ Evidence standard: Every finding MUST include file:line, exploit scenario, and remediation.
24
+ Do NOT report hypothetical issues without concrete code evidence.`,
25
+ },
26
+
27
+ architecture: {
28
+ role: "Maya Volkov — Architecture Specialist",
29
+ focus: `You are an architecture specialist reviewing code for structural quality.
30
+
31
+ Focus areas:
32
+ - God components/modules (>300 LOC, >10 responsibilities)
33
+ - Circular dependencies between modules
34
+ - Tight coupling between layers (presentation → data access)
35
+ - Missing abstraction boundaries (business logic in route handlers)
36
+ - State management sprawl (>15 useState in a component)
37
+ - Missing error boundaries and fallback handling
38
+ - Inconsistent naming/organization patterns
39
+ - Dead code and unreachable paths
40
+
41
+ Evidence standard: Every finding MUST include file:line, coupling graph or complexity metric, and refactoring guidance.`,
42
+ },
43
+
44
+ testing: {
45
+ role: "Priya Raman — Testing Specialist",
46
+ focus: `You are a testing specialist reviewing code for coverage gaps and test quality.
47
+
48
+ Focus areas:
49
+ - Critical paths without test coverage (auth, payment, data mutation)
50
+ - Tests that mock too much (false confidence)
51
+ - Missing edge case tests (empty inputs, boundary values, error paths)
52
+ - Flaky test patterns (timing, external dependencies, shared state)
53
+ - Missing integration tests for API endpoints
54
+ - No E2E tests for critical user flows
55
+ - Test data that doesn't represent production scenarios
56
+ - Missing assertion specificity (assertTrue vs assertEquals)
57
+
58
+ Evidence standard: Every finding MUST include the untested code path (file:line) and a concrete test case outline.`,
59
+ },
60
+
61
+ performance: {
62
+ role: "Arjun Mehta — Performance Specialist",
63
+ focus: `You are a performance specialist reviewing code for latency and efficiency issues.
64
+
65
+ Focus areas:
66
+ - N+1 query patterns (loop-based database calls)
67
+ - Missing database indexes on WHERE/JOIN/ORDER BY columns
68
+ - Unbounded data fetching (no LIMIT, no pagination)
69
+ - Synchronous blocking in async contexts
70
+ - Memory leaks (unclosed connections, event listeners, timers)
71
+ - Bundle size bloat (large imports, no tree shaking, no code splitting)
72
+ - Missing caching for expensive computations
73
+ - Render performance (unnecessary re-renders, missing memoization)
74
+
75
+ Evidence standard: Every finding MUST include file:line, estimated performance impact, and optimization approach.`,
76
+ },
77
+
78
+ compliance: {
79
+ role: "Leila Farouk — Compliance Specialist",
80
+ focus: `You are a compliance specialist reviewing code for regulatory adherence.
81
+
82
+ Focus areas:
83
+ - PII handling without encryption or access controls
84
+ - Missing audit logging for data access and mutations
85
+ - GDPR: data retention without deletion mechanisms
86
+ - SOC2: missing access controls, no principle of least privilege
87
+ - HIPAA: PHI exposure, missing BAA requirements
88
+ - Missing consent tracking for data collection
89
+ - Insecure data export/download without authorization
90
+ - Missing data classification and sensitivity labels
91
+
92
+ Evidence standard: Every finding MUST include the regulatory requirement, the gap, and the remediation with compliance evidence.`,
93
+ },
94
+
95
+ documentation: {
96
+ role: "Samir Okafor — Documentation Specialist",
97
+ focus: `You are a documentation specialist reviewing for operational clarity.
98
+
99
+ Focus areas:
100
+ - Missing or outdated README/setup instructions
101
+ - API endpoints without documentation
102
+ - Missing runbooks for incident response
103
+ - Configuration options without documentation
104
+ - Missing architecture decision records (ADRs)
105
+ - Outdated deployment instructions
106
+ - Missing onboarding documentation for new developers
107
+
108
+ Evidence standard: Every finding MUST include what is missing, where it should live, and a draft outline.`,
109
+ },
110
+
111
+ reliability: {
112
+ role: "Noah Ben-David — Reliability Specialist",
113
+ focus: `You are a reliability specialist reviewing code for fault tolerance.
114
+
115
+ Focus areas:
116
+ - Missing timeout configuration on external calls
117
+ - No retry logic or exponential backoff for transient failures
118
+ - Missing circuit breakers on external service calls
119
+ - No graceful degradation when dependencies are down
120
+ - Missing health check endpoints
121
+ - Queue backpressure handling gaps
122
+ - Missing dead letter queue for failed jobs
123
+ - No idempotency keys on mutation endpoints
124
+
125
+ Evidence standard: Every finding MUST include the failure scenario, blast radius, and resilience pattern to apply.`,
126
+ },
127
+
128
+ release: {
129
+ role: "Omar Singh — Release Engineering Specialist",
130
+ focus: `You are a release engineering specialist reviewing CI/CD and deployment.
131
+
132
+ Focus areas:
133
+ - Unpinned GitHub Actions (using @main instead of SHA)
134
+ - Missing artifact signing or provenance attestation
135
+ - No rollback mechanism in deployment pipeline
136
+ - Missing smoke tests after deploy
137
+ - Secrets in CI/CD logs or artifacts
138
+ - Missing branch protection rules
139
+ - No canary or staged rollout strategy
140
+ - Deploy pipeline without quality gates
141
+
142
+ Evidence standard: Every finding MUST include the workflow file:line, risk, and the hardened alternative.`,
143
+ },
144
+
145
+ observability: {
146
+ role: "Sofia Alvarez — Observability Specialist",
147
+ focus: `You are an observability specialist reviewing telemetry and alerting.
148
+
149
+ Focus areas:
150
+ - Missing structured logging (console.log without context)
151
+ - No request tracing (missing correlation IDs)
152
+ - Missing error tracking integration
153
+ - No alerting on error rate spikes
154
+ - Missing latency tracking on critical paths
155
+ - No dashboard for key business metrics
156
+ - Missing SLO/SLI definitions
157
+ - Blind spots: operations without any telemetry
158
+
159
+ Evidence standard: Every finding MUST include what metric/signal is missing, where to instrument, and the alert threshold.`,
160
+ },
161
+
162
+ infrastructure: {
163
+ role: "Kat Hughes — Infrastructure Specialist",
164
+ focus: `You are an infrastructure specialist reviewing cloud and deployment config.
165
+
166
+ Focus areas:
167
+ - Overly permissive IAM policies (wildcard actions/resources)
168
+ - Public-facing resources without WAF/rate limiting
169
+ - Missing encryption at rest or in transit
170
+ - Hardcoded infrastructure values (IPs, ARNs, account IDs)
171
+ - Missing VPC/subnet isolation
172
+ - No secrets rotation policy
173
+ - Missing backup and disaster recovery configuration
174
+ - Infrastructure drift (manual changes not in IaC)
175
+
176
+ Evidence standard: Every finding MUST include the resource, the misconfiguration, blast radius, and the IaC fix.`,
177
+ },
178
+
179
+ "supply-chain": {
180
+ role: "Nora Kline — Supply Chain Specialist",
181
+ focus: `You are a supply chain specialist reviewing dependency security.
182
+
183
+ Focus areas:
184
+ - Dependencies with known CVEs (critical/high severity)
185
+ - Unpinned dependency versions (using ^/~ instead of exact)
186
+ - Dependencies from untrusted or abandoned packages
187
+ - Missing lockfile integrity checks
188
+ - No SBOM generation in build pipeline
189
+ - Typosquatting risk (similar package names)
190
+ - Excessive dependency tree depth
191
+ - Missing license compliance checks
192
+
193
+ Evidence standard: Every finding MUST include the package name, version, CVE/risk, and the pinned/patched alternative.`,
194
+ },
195
+
196
+ frontend: {
197
+ role: "Jules Tanaka — Frontend Specialist",
198
+ focus: `You are a frontend specialist reviewing UI code for production readiness.
199
+
200
+ Focus areas:
201
+ - XSS via dangerouslySetInnerHTML without sanitization
202
+ - Client-side token storage in localStorage (use httpOnly cookies)
203
+ - Missing input validation on forms
204
+ - Accessibility failures (missing alt text, labels, keyboard navigation)
205
+ - Bundle size > 200KB initial JS
206
+ - Missing error boundaries around route components
207
+ - CLS-causing patterns (images without dimensions, dynamic content injection)
208
+ - Missing loading/error states on data fetching
209
+
210
+ Evidence standard: Every finding MUST include file:line, user impact, and the specific fix.`,
211
+ },
212
+
213
+ "ai-governance": {
214
+ role: "Amina Chen — AI Governance Specialist",
215
+ focus: `You are an AI governance specialist reviewing AI/ML code safety.
216
+
217
+ Focus areas:
218
+ - Prompt injection vectors in user-facing LLM prompts
219
+ - Missing input sanitization before LLM calls
220
+ - No rate limiting on AI endpoints
221
+ - Missing cost/token budget enforcement
222
+ - No human-in-the-loop for high-risk AI decisions
223
+ - Missing model versioning and eval regression checks
224
+ - Tool/agent permission escalation risks
225
+ - Missing audit trail for AI-generated actions
226
+
227
+ Evidence standard: Every finding MUST include the injection/bypass scenario, the affected code path, and the guardrail to add.`,
228
+ },
229
+ };
230
+
231
+ /**
232
+ * Build a persona-scoped system prompt for Omar Gate AI analysis.
233
+ *
234
+ * @param {object} options
235
+ * @param {string} options.personaId - Agent ID (e.g., "security", "architecture")
236
+ * @param {string} [options.targetPath] - Repository path
237
+ * @param {object} [options.deterministicSummary] - Summary from deterministic scan
238
+ * @param {number} [options.maxFindings] - Max findings to return (default 20)
239
+ * @returns {string} System prompt
240
+ */
241
+ export function buildPersonaReviewPrompt({
242
+ personaId,
243
+ targetPath = "",
244
+ deterministicSummary = {},
245
+ maxFindings = 20,
246
+ } = {}) {
247
+ const persona = PERSONA_PROMPTS[personaId];
248
+ if (!persona) {
249
+ return buildGenericPrompt({ targetPath, deterministicSummary, maxFindings });
250
+ }
251
+
252
+ return `# ${persona.role}
253
+
254
+ ${persona.focus}
255
+
256
+ ## Context
257
+ Target: ${targetPath || "(not provided)"}
258
+ Deterministic scan: P0=${deterministicSummary.P0 || 0} P1=${deterministicSummary.P1 || 0} P2=${deterministicSummary.P2 || 0} P3=${deterministicSummary.P3 || 0}
259
+
260
+ ## Output Contract
261
+ Return a JSON array of findings. Maximum ${maxFindings} findings. Each finding:
262
+ \`\`\`json
263
+ {
264
+ "severity": "P0|P1|P2|P3",
265
+ "file": "path/to/file.ext",
266
+ "line": 42,
267
+ "title": "Brief description",
268
+ "evidence": "Concrete code evidence at file:line",
269
+ "rootCause": "Why this is a problem",
270
+ "recommendedFix": "Specific fix to apply",
271
+ "confidence": 0.85
272
+ }
273
+ \`\`\`
274
+
275
+ Rules:
276
+ - Only report findings you have HIGH confidence in (>= 0.7)
277
+ - Every finding MUST have concrete file:line evidence
278
+ - Do NOT repeat findings already in the deterministic scan
279
+ - Do NOT report hypothetical/speculative issues
280
+ - Focus on REAL, EXPLOITABLE, IMPACTFUL problems in your domain
281
+ - Return ONLY the JSON array, no other text
282
+ `;
283
+ }
284
+
285
+ function buildGenericPrompt({ targetPath, deterministicSummary, maxFindings }) {
286
+ return `You are a senior code reviewer. Analyze the code for security, quality, and reliability issues.
287
+
288
+ Target: ${targetPath || "(not provided)"}
289
+ Deterministic scan: P0=${deterministicSummary.P0 || 0} P1=${deterministicSummary.P1 || 0} P2=${deterministicSummary.P2 || 0}
290
+
291
+ Return a JSON array of up to ${maxFindings} findings with: severity, file, line, title, evidence, rootCause, recommendedFix, confidence.
292
+ Only report findings with concrete evidence. Do NOT repeat deterministic findings.`;
293
+ }
294
+
295
+ export const PERSONA_IDS = Object.keys(PERSONA_PROMPTS);
296
+ export { PERSONA_PROMPTS };