sentinelayer-cli 0.6.2 → 0.8.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.
- package/README.md +1009 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +64 -63
- package/src/agents/ai-governance/index.js +12 -0
- package/src/agents/ai-governance/tools/base.js +171 -0
- package/src/agents/ai-governance/tools/eval-regression.js +47 -0
- package/src/agents/ai-governance/tools/hitl-audit.js +81 -0
- package/src/agents/ai-governance/tools/index.js +52 -0
- package/src/agents/ai-governance/tools/prompt-drift.js +42 -0
- package/src/agents/ai-governance/tools/provenance-check.js +69 -0
- package/src/agents/backend/index.js +12 -0
- package/src/agents/backend/tools/base.js +189 -0
- package/src/agents/backend/tools/circuit-breaker-check.js +123 -0
- package/src/agents/backend/tools/idempotency-audit.js +105 -0
- package/src/agents/backend/tools/index.js +87 -0
- package/src/agents/backend/tools/retry-audit.js +132 -0
- package/src/agents/backend/tools/timeout-audit.js +144 -0
- package/src/agents/code-quality/index.js +12 -0
- package/src/agents/code-quality/tools/base.js +159 -0
- package/src/agents/code-quality/tools/complexity-measure.js +197 -0
- package/src/agents/code-quality/tools/coupling-analysis.js +81 -0
- package/src/agents/code-quality/tools/cycle-detect.js +49 -0
- package/src/agents/code-quality/tools/dep-graph.js +196 -0
- package/src/agents/code-quality/tools/index.js +89 -0
- package/src/agents/data-layer/index.js +12 -0
- package/src/agents/data-layer/tools/base.js +181 -0
- package/src/agents/data-layer/tools/index-audit.js +165 -0
- package/src/agents/data-layer/tools/index.js +83 -0
- package/src/agents/data-layer/tools/migration-scan.js +135 -0
- package/src/agents/data-layer/tools/query-explain.js +120 -0
- package/src/agents/data-layer/tools/tenancy-scan.js +166 -0
- package/src/agents/documentation/index.js +12 -0
- package/src/agents/documentation/tools/api-diff.js +91 -0
- package/src/agents/documentation/tools/base.js +151 -0
- package/src/agents/documentation/tools/dead-link-check.js +58 -0
- package/src/agents/documentation/tools/docstring-coverage.js +78 -0
- package/src/agents/documentation/tools/index.js +52 -0
- package/src/agents/documentation/tools/readme-freshness.js +61 -0
- package/src/agents/envelope/fix-cycle.js +45 -0
- package/src/agents/envelope/index.js +31 -0
- package/src/agents/envelope/loop.js +150 -0
- package/src/agents/envelope/pulse.js +18 -0
- package/src/agents/envelope/stream.js +40 -0
- package/src/agents/infrastructure/index.js +12 -0
- package/src/agents/infrastructure/tools/base.js +171 -0
- package/src/agents/infrastructure/tools/checkov-run.js +32 -0
- package/src/agents/infrastructure/tools/drift-detect.js +59 -0
- package/src/agents/infrastructure/tools/iam-least-priv-check.js +78 -0
- package/src/agents/infrastructure/tools/index.js +52 -0
- package/src/agents/infrastructure/tools/tflint-run.js +31 -0
- package/src/agents/jules/config/definition.js +160 -160
- package/src/agents/jules/config/system-prompt.js +182 -182
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +17 -17
- package/src/agents/jules/loop.js +460 -450
- package/src/agents/jules/pulse.js +10 -10
- package/src/agents/jules/stream.js +187 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +315 -309
- package/src/agents/jules/tools/aidenid-email.js +189 -189
- package/src/agents/jules/tools/auth-audit.js +1708 -1691
- package/src/agents/jules/tools/dispatch.js +340 -335
- package/src/agents/jules/tools/file-edit.js +2 -2
- package/src/agents/jules/tools/file-read.js +2 -2
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +2 -2
- package/src/agents/jules/tools/grep.js +2 -2
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +2 -2
- package/src/agents/jules/tools/runtime-audit.js +507 -507
- package/src/agents/jules/tools/shell.js +2 -2
- package/src/agents/jules/tools/url-policy.js +100 -100
- package/src/agents/mode.js +113 -0
- package/src/agents/observability/index.js +12 -0
- package/src/agents/observability/tools/alert-audit.js +39 -0
- package/src/agents/observability/tools/base.js +181 -0
- package/src/agents/observability/tools/dashboard-gap.js +42 -0
- package/src/agents/observability/tools/index.js +54 -0
- package/src/agents/observability/tools/log-schema-check.js +74 -0
- package/src/agents/observability/tools/span-coverage.js +74 -0
- package/src/agents/persona-visuals.js +102 -61
- package/src/agents/release/index.js +12 -0
- package/src/agents/release/tools/base.js +181 -0
- package/src/agents/release/tools/changelog-diff.js +86 -0
- package/src/agents/release/tools/feature-flag-audit.js +126 -0
- package/src/agents/release/tools/index.js +61 -0
- package/src/agents/release/tools/rollback-verify.js +129 -0
- package/src/agents/release/tools/semver-check.js +109 -0
- package/src/agents/reliability/index.js +12 -0
- package/src/agents/reliability/tools/backpressure-check.js +129 -0
- package/src/agents/reliability/tools/base.js +181 -0
- package/src/agents/reliability/tools/chaos-probe.js +109 -0
- package/src/agents/reliability/tools/graceful-degradation-check.js +114 -0
- package/src/agents/reliability/tools/health-check-audit.js +111 -0
- package/src/agents/reliability/tools/index.js +87 -0
- package/src/agents/run-persona.js +109 -0
- package/src/agents/security/index.js +12 -0
- package/src/agents/security/tools/authz-audit.js +134 -0
- package/src/agents/security/tools/base.js +190 -0
- package/src/agents/security/tools/crypto-review.js +175 -0
- package/src/agents/security/tools/index.js +97 -0
- package/src/agents/security/tools/sast-scan.js +175 -0
- package/src/agents/security/tools/secrets-scan.js +216 -0
- package/src/agents/shared-tools/dispatch-core.js +320 -315
- package/src/agents/shared-tools/file-edit.js +180 -180
- package/src/agents/shared-tools/file-read.js +100 -100
- package/src/agents/shared-tools/glob.js +168 -168
- package/src/agents/shared-tools/grep.js +228 -228
- package/src/agents/shared-tools/index.js +46 -46
- package/src/agents/shared-tools/path-guards.js +161 -161
- package/src/agents/shared-tools/shell.js +383 -383
- package/src/agents/supply-chain/index.js +12 -0
- package/src/agents/supply-chain/tools/attestation-check.js +42 -0
- package/src/agents/supply-chain/tools/base.js +151 -0
- package/src/agents/supply-chain/tools/index.js +52 -0
- package/src/agents/supply-chain/tools/lockfile-integrity.js +73 -0
- package/src/agents/supply-chain/tools/package-verify.js +56 -0
- package/src/agents/supply-chain/tools/sbom-diff.js +34 -0
- package/src/agents/testing/index.js +12 -0
- package/src/agents/testing/tools/base.js +202 -0
- package/src/agents/testing/tools/coverage-gap.js +144 -0
- package/src/agents/testing/tools/flake-detect.js +125 -0
- package/src/agents/testing/tools/index.js +85 -0
- package/src/agents/testing/tools/mutation-test.js +143 -0
- package/src/agents/testing/tools/snapshot-diff.js +103 -0
- package/src/ai/aidenid.js +1021 -1009
- package/src/ai/client.js +553 -553
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/proxy.js +137 -137
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +428 -371
- package/src/auth/http.js +681 -611
- package/src/auth/service.js +1106 -1106
- package/src/auth/session-store.js +813 -813
- package/src/cli.js +257 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1338
- package/src/commands/ai/provision-governance.js +1272 -1272
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1171 -1166
- package/src/commands/auth.js +419 -419
- package/src/commands/chat.js +184 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +20 -10
- package/src/commands/mcp.js +461 -461
- package/src/commands/omargate.js +63 -29
- package/src/commands/persona.js +65 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +865 -872
- package/src/commands/session.js +1238 -0
- package/src/commands/spec.js +771 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +511 -511
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/coord/events-log.js +141 -0
- package/src/coord/handshake.js +719 -0
- package/src/coord/index.js +35 -0
- package/src/coord/paths.js +84 -0
- package/src/coord/priority.js +62 -0
- package/src/coord/tarjan.js +157 -0
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tokenizer.js +160 -0
- package/src/cost/tracker.js +232 -171
- package/src/daemon/artifact-lineage.js +896 -534
- package/src/daemon/assignment-ledger.js +1083 -770
- package/src/daemon/ast-drift.js +496 -0
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +1209 -626
- package/src/daemon/fix-cycle.js +384 -377
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/ingest-refresh.js +79 -11
- package/src/daemon/jira-lifecycle.js +767 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/pulse.js +327 -327
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/scope-engine.js +1068 -0
- package/src/daemon/watchdog.js +971 -971
- package/src/events/schema.js +190 -0
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +933 -918
- package/src/ingest/ownership.js +380 -0
- package/src/interactive/index.js +97 -97
- package/src/legacy-cli.js +3228 -2994
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/orchestrator/kai-chen.js +126 -0
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +136 -118
- package/src/review/ai-review.js +672 -679
- package/src/review/compliance-pack.js +389 -0
- package/src/review/investor-dd-config.js +54 -0
- package/src/review/investor-dd-file-loop.js +303 -0
- package/src/review/investor-dd-file-router.js +406 -0
- package/src/review/investor-dd-html-report.js +233 -0
- package/src/review/investor-dd-notification.js +120 -0
- package/src/review/investor-dd-orchestrator.js +405 -0
- package/src/review/investor-dd-persona-runner.js +275 -0
- package/src/review/live-validator.js +253 -0
- package/src/review/local-review.js +1351 -1305
- package/src/review/omargate-interactive.js +68 -68
- package/src/review/omargate-orchestrator.js +492 -300
- package/src/review/persona-prompts.js +484 -296
- package/src/review/reconciliation-rules.js +329 -0
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/reproducibility-chain.js +136 -0
- package/src/review/scan-modes.js +147 -42
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -67
- package/src/scaffold/templates.js +150 -150
- package/src/scan/generator.js +418 -418
- package/src/scan/gh-secrets.js +107 -107
- package/src/session/agent-registry.js +359 -0
- package/src/session/analytics.js +479 -0
- package/src/session/daemon.js +1396 -0
- package/src/session/file-locks.js +666 -0
- package/src/session/paths.js +37 -0
- package/src/session/recap.js +567 -0
- package/src/session/redact.js +82 -0
- package/src/session/runtime-bridge.js +762 -0
- package/src/session/scoring.js +406 -0
- package/src/session/setup-guides.js +304 -0
- package/src/session/store.js +704 -0
- package/src/session/stream.js +333 -0
- package/src/session/sync.js +753 -0
- package/src/session/tasks.js +1054 -0
- package/src/session/templates.js +188 -0
- package/src/spec/generator.js +619 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +569 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/session-tracker.js +234 -234
- package/src/telemetry/sync.js +203 -203
- package/src/ui/command-hints.js +13 -13
- package/src/ui/markdown.js +220 -220
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reproducibility chain (#investor-dd-17).
|
|
3
|
+
*
|
|
4
|
+
* For each finding emitted by the investor-DD run, attach a `replay`
|
|
5
|
+
* block that captures:
|
|
6
|
+
*
|
|
7
|
+
* - replayCommand — a single-line bash command that re-runs the
|
|
8
|
+
* exact evidence-gathering step (tool dispatch
|
|
9
|
+
* against the same file), so an auditor or the
|
|
10
|
+
* caller can verify without re-running the
|
|
11
|
+
* full investor-DD flow.
|
|
12
|
+
* - filesAtTime — SHA-256 of every file involved in the finding
|
|
13
|
+
* at finding time, so if the repo has changed
|
|
14
|
+
* since, the mismatch is detectable.
|
|
15
|
+
* - runId / timestamp for cross-reference.
|
|
16
|
+
*
|
|
17
|
+
* The module is a pure decorator: it does not mutate findings, it
|
|
18
|
+
* returns new ones enriched with reproducibility metadata.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import crypto from "node:crypto";
|
|
22
|
+
import fsp from "node:fs/promises";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Compute SHA-256 of a file on disk. Returns null if unreadable so
|
|
27
|
+
* the chain never blocks the report on transient fs failures.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} absPath
|
|
30
|
+
* @returns {Promise<string | null>}
|
|
31
|
+
*/
|
|
32
|
+
export async function sha256File(absPath) {
|
|
33
|
+
try {
|
|
34
|
+
const data = await fsp.readFile(absPath);
|
|
35
|
+
return crypto.createHash("sha256").update(data).digest("hex");
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Build a single finding's replay block.
|
|
43
|
+
*
|
|
44
|
+
* @param {object} params
|
|
45
|
+
* @param {object} params.finding - Must have .personaId, .tool, .file.
|
|
46
|
+
* @param {string} params.rootPath - Repo root (used to resolve finding.file).
|
|
47
|
+
* @param {string} params.runId
|
|
48
|
+
* @param {string} [params.cliName="sl"] - Display name in replayCommand.
|
|
49
|
+
* @returns {Promise<{replayCommand: string, filesAtTime: Record<string, string|null>, runId: string, timestamp: string}>}
|
|
50
|
+
*/
|
|
51
|
+
export async function buildReplayBlock({
|
|
52
|
+
finding,
|
|
53
|
+
rootPath,
|
|
54
|
+
runId,
|
|
55
|
+
cliName = "sl",
|
|
56
|
+
} = {}) {
|
|
57
|
+
if (!finding) throw new TypeError("buildReplayBlock requires finding");
|
|
58
|
+
if (!rootPath) throw new TypeError("buildReplayBlock requires rootPath");
|
|
59
|
+
if (!runId) throw new TypeError("buildReplayBlock requires runId");
|
|
60
|
+
|
|
61
|
+
const file = finding.file || "";
|
|
62
|
+
const tool = finding.tool || finding.kind || "";
|
|
63
|
+
const personaId = finding.personaId || "";
|
|
64
|
+
|
|
65
|
+
const replayCommand = [
|
|
66
|
+
cliName,
|
|
67
|
+
"/review",
|
|
68
|
+
"show",
|
|
69
|
+
"--run",
|
|
70
|
+
runId,
|
|
71
|
+
"--persona",
|
|
72
|
+
personaId,
|
|
73
|
+
"--tool",
|
|
74
|
+
tool,
|
|
75
|
+
"--file",
|
|
76
|
+
file,
|
|
77
|
+
]
|
|
78
|
+
.filter(Boolean)
|
|
79
|
+
.join(" ");
|
|
80
|
+
|
|
81
|
+
const filesAtTime = {};
|
|
82
|
+
if (file) {
|
|
83
|
+
const abs = path.isAbsolute(file) ? file : path.join(rootPath, file);
|
|
84
|
+
filesAtTime[file] = await sha256File(abs);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
replayCommand,
|
|
89
|
+
filesAtTime,
|
|
90
|
+
runId,
|
|
91
|
+
timestamp: new Date().toISOString(),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Decorate an array of findings with reproducibility chains. Runs in
|
|
97
|
+
* parallel but bounded concurrency so we don't thrash disk on huge
|
|
98
|
+
* finding sets.
|
|
99
|
+
*
|
|
100
|
+
* @param {object} params
|
|
101
|
+
* @param {Array<object>} params.findings
|
|
102
|
+
* @param {string} params.rootPath
|
|
103
|
+
* @param {string} params.runId
|
|
104
|
+
* @param {string} [params.cliName]
|
|
105
|
+
* @param {number} [params.concurrency=8]
|
|
106
|
+
* @returns {Promise<Array<object>>} Findings with `.reproducibility` attached.
|
|
107
|
+
*/
|
|
108
|
+
export async function attachReproducibilityChain({
|
|
109
|
+
findings = [],
|
|
110
|
+
rootPath,
|
|
111
|
+
runId,
|
|
112
|
+
cliName = "sl",
|
|
113
|
+
concurrency = 8,
|
|
114
|
+
} = {}) {
|
|
115
|
+
if (!Array.isArray(findings)) throw new TypeError("findings must be an array");
|
|
116
|
+
if (!rootPath) throw new TypeError("rootPath required");
|
|
117
|
+
if (!runId) throw new TypeError("runId required");
|
|
118
|
+
|
|
119
|
+
const result = [];
|
|
120
|
+
let cursor = 0;
|
|
121
|
+
async function worker() {
|
|
122
|
+
while (cursor < findings.length) {
|
|
123
|
+
const idx = cursor;
|
|
124
|
+
cursor += 1;
|
|
125
|
+
const finding = findings[idx];
|
|
126
|
+
const replay = await buildReplayBlock({ finding, rootPath, runId, cliName });
|
|
127
|
+
result[idx] = { ...finding, reproducibility: replay };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const workers = [];
|
|
131
|
+
for (let i = 0; i < Math.max(1, Math.min(concurrency, findings.length)); i += 1) {
|
|
132
|
+
workers.push(worker());
|
|
133
|
+
}
|
|
134
|
+
await Promise.all(workers);
|
|
135
|
+
return result;
|
|
136
|
+
}
|
package/src/review/scan-modes.js
CHANGED
|
@@ -1,42 +1,147 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Scan mode → persona list resolution for Omar Gate.
|
|
3
|
-
*
|
|
4
|
-
* baseline:
|
|
5
|
-
* deep:
|
|
6
|
-
* audit:
|
|
7
|
-
* full-depth: all 13 personas (~5min)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Scan mode → persona list resolution for Omar Gate.
|
|
3
|
+
*
|
|
4
|
+
* baseline: security only (~30s)
|
|
5
|
+
* deep: all 13 personas (~3-5min) — alias of full-depth from v0.7+
|
|
6
|
+
* audit: alias for full-depth
|
|
7
|
+
* full-depth: all 13 personas (~3-5min)
|
|
8
|
+
*
|
|
9
|
+
* v0.7 change (2026-04-16): `deep` was a 6-persona subset and missed
|
|
10
|
+
* domain specialists (Maya/backend, Jules/frontend, Nora/supply-chain,
|
|
11
|
+
* Samir/documentation, etc.). Users running `sl /omargate deep` expected
|
|
12
|
+
* a full dispatch. Deep now matches full-depth to prevent silent coverage
|
|
13
|
+
* gaps; run `baseline` when you only need security.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// AUTHORITATIVE dispatch list. Anything not in this array does not run,
|
|
17
|
+
// regardless of whether it has a persona prompt or visual identity elsewhere.
|
|
18
|
+
//
|
|
19
|
+
// Cross-reference (keep in sync):
|
|
20
|
+
// - src/review/persona-prompts.js PERSONA_PROMPTS keys → must match this list
|
|
21
|
+
// - src/review/persona-prompts.js SWE_FRAMEWORK_CHECKLIST keys → must match this list
|
|
22
|
+
// - src/agents/persona-visuals.js → may define SUPERSET (visuals for sub-specialties
|
|
23
|
+
// are allowed, e.g. Maya Volkov has both "architecture" and "backend" visuals
|
|
24
|
+
// because both roles fold into her architecture dispatch).
|
|
25
|
+
//
|
|
26
|
+
// Canon update (2026-04-18): "backend" (Maya Volkov, ex-AWS Platform),
|
|
27
|
+
// "code-quality" (Ethan Park, ex-Meta Code Health), and "data-layer"
|
|
28
|
+
// (Dr. Linh Tran, ex-Netflix Data Platforms) are now dispatched personas
|
|
29
|
+
// per Carter's canonical 13.
|
|
30
|
+
// Removed:
|
|
31
|
+
// - "architecture" — structural concerns fold into code-quality; runtime
|
|
32
|
+
// concerns fold into backend.
|
|
33
|
+
// - "performance" — concerns distributed across backend / data-layer /
|
|
34
|
+
// frontend / observability / reliability.
|
|
35
|
+
// - "compliance" — cross-cutting; distributed across security / ai-governance /
|
|
36
|
+
// supply-chain / infrastructure / documentation via subagent dispatch.
|
|
37
|
+
const FULL_DEPTH_PERSONAS = [
|
|
38
|
+
"security",
|
|
39
|
+
"backend",
|
|
40
|
+
"code-quality",
|
|
41
|
+
"testing",
|
|
42
|
+
"data-layer",
|
|
43
|
+
"reliability",
|
|
44
|
+
"release",
|
|
45
|
+
"observability",
|
|
46
|
+
"infrastructure",
|
|
47
|
+
"supply-chain",
|
|
48
|
+
"frontend",
|
|
49
|
+
"documentation",
|
|
50
|
+
"ai-governance",
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const SCAN_MODES = {
|
|
54
|
+
baseline: ["security"],
|
|
55
|
+
deep: FULL_DEPTH_PERSONAS,
|
|
56
|
+
"full-depth": FULL_DEPTH_PERSONAS,
|
|
57
|
+
audit: FULL_DEPTH_PERSONAS,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export function resolveScanMode(mode = "deep") {
|
|
61
|
+
const normalized = String(mode || "deep").trim().toLowerCase();
|
|
62
|
+
const personas = SCAN_MODES[normalized];
|
|
63
|
+
if (!personas) {
|
|
64
|
+
throw new Error(`Unknown scan mode '${mode}'. Use: ${Object.keys(SCAN_MODES).join(", ")}`);
|
|
65
|
+
}
|
|
66
|
+
return { mode: normalized, personas: [...personas] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const AVAILABLE_SCAN_MODES = Object.keys(SCAN_MODES);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parse a comma-separated persona ID list from CLI input.
|
|
73
|
+
*
|
|
74
|
+
* Trims whitespace, lowercases, drops empty entries, deduplicates.
|
|
75
|
+
*
|
|
76
|
+
* @param {string | string[] | null | undefined} value
|
|
77
|
+
* @returns {string[]} Cleaned persona ID list.
|
|
78
|
+
*/
|
|
79
|
+
export function parsePersonaCsv(value) {
|
|
80
|
+
if (value == null) return [];
|
|
81
|
+
const raw = Array.isArray(value) ? value.join(",") : String(value);
|
|
82
|
+
const cleaned = raw
|
|
83
|
+
.split(",")
|
|
84
|
+
.map((s) => s.trim().toLowerCase())
|
|
85
|
+
.filter((s) => s.length > 0);
|
|
86
|
+
return [...new Set(cleaned)];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve a scan mode and then filter the persona list by opt-in / opt-out lists.
|
|
91
|
+
*
|
|
92
|
+
* Precedence:
|
|
93
|
+
* 1. Start with the base persona list for `mode`.
|
|
94
|
+
* 2. If `includeOnly` is a non-empty array, restrict to those IDs (in the
|
|
95
|
+
* order they appear in the base list, for deterministic dispatch).
|
|
96
|
+
* 3. Remove any IDs in `skipPersonas`.
|
|
97
|
+
*
|
|
98
|
+
* If a caller passes `includeOnly` IDs that aren't in the base list, they
|
|
99
|
+
* are silently dropped (not errored) so typos don't block a run. The caller
|
|
100
|
+
* can check `result.unknown` to surface warnings in the UI.
|
|
101
|
+
*
|
|
102
|
+
* @param {string} [mode="deep"]
|
|
103
|
+
* @param {object} [options]
|
|
104
|
+
* @param {string[]} [options.includeOnly] Only run these personas.
|
|
105
|
+
* @param {string[]} [options.skipPersonas] Skip these personas.
|
|
106
|
+
* @returns {{mode: string, personas: string[], dropped: string[], unknown: string[]}}
|
|
107
|
+
*/
|
|
108
|
+
export function resolveFilteredPersonas(mode = "deep", options = {}) {
|
|
109
|
+
const { includeOnly = null, skipPersonas = [] } = options || {};
|
|
110
|
+
const { mode: normalized, personas: base } = resolveScanMode(mode);
|
|
111
|
+
|
|
112
|
+
const basePersonas = [...base];
|
|
113
|
+
let filtered = basePersonas;
|
|
114
|
+
const unknown = [];
|
|
115
|
+
|
|
116
|
+
if (Array.isArray(includeOnly) && includeOnly.length > 0) {
|
|
117
|
+
const normalizedInclude = new Set(
|
|
118
|
+
includeOnly.map((p) => String(p).trim().toLowerCase()).filter(Boolean),
|
|
119
|
+
);
|
|
120
|
+
// Surface any include entries that aren't in the base mode list.
|
|
121
|
+
for (const id of normalizedInclude) {
|
|
122
|
+
if (!basePersonas.includes(id)) unknown.push(id);
|
|
123
|
+
}
|
|
124
|
+
filtered = basePersonas.filter((id) => normalizedInclude.has(id));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const dropped = [];
|
|
128
|
+
if (Array.isArray(skipPersonas) && skipPersonas.length > 0) {
|
|
129
|
+
const normalizedSkip = new Set(
|
|
130
|
+
skipPersonas.map((p) => String(p).trim().toLowerCase()).filter(Boolean),
|
|
131
|
+
);
|
|
132
|
+
filtered = filtered.filter((id) => {
|
|
133
|
+
if (normalizedSkip.has(id)) {
|
|
134
|
+
dropped.push(id);
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
mode: normalized,
|
|
143
|
+
personas: filtered,
|
|
144
|
+
dropped,
|
|
145
|
+
unknown,
|
|
146
|
+
};
|
|
147
|
+
}
|