sentinelayer-cli 0.25.0 → 0.25.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/package.json +1 -1
- package/src/audit/agents/architecture.js +4 -1
- package/src/audit/agents/documentation.js +4 -4
- package/src/audit/agents/path-classification.js +120 -0
- package/src/audit/agents/performance.js +4 -1
- package/src/audit/agents/testing.js +5 -7
- package/src/commands/action-options.js +27 -0
- package/src/commands/audit.js +84 -71
- package/src/commands/review.js +47 -40
package/package.json
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isAuditSourceFile } from "./path-classification.js";
|
|
2
|
+
|
|
1
3
|
function normalizeString(value) {
|
|
2
4
|
return String(value || "").trim();
|
|
3
5
|
}
|
|
@@ -21,6 +23,7 @@ function summarizeSeverity(findings = []) {
|
|
|
21
23
|
function buildHotspots(ingest = {}) {
|
|
22
24
|
const indexed = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
23
25
|
return indexed
|
|
26
|
+
.filter((file) => isAuditSourceFile(file))
|
|
24
27
|
.filter((file) => Number(file.loc || 0) >= 300)
|
|
25
28
|
.sort((left, right) => Number(right.loc || 0) - Number(left.loc || 0))
|
|
26
29
|
.slice(0, 20)
|
|
@@ -50,7 +53,7 @@ function deriveArchitectureFindings({ findings = [], hotspots = [] } = {}) {
|
|
|
50
53
|
continue;
|
|
51
54
|
}
|
|
52
55
|
derived.push({
|
|
53
|
-
severity:
|
|
56
|
+
severity: "P2",
|
|
54
57
|
file: hotspot.path,
|
|
55
58
|
line: 1,
|
|
56
59
|
message: `Large architectural hotspot detected (${hotspot.loc} LOC).`,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isAuditSourceFile } from "./path-classification.js";
|
|
2
|
+
|
|
1
3
|
function normalizeString(value) {
|
|
2
4
|
return String(value || "").trim();
|
|
3
5
|
}
|
|
@@ -26,9 +28,7 @@ function buildDocumentationInventory(ingest = {}) {
|
|
|
26
28
|
language: file.language,
|
|
27
29
|
}));
|
|
28
30
|
const docFiles = normalized.filter((file) => /(^|\/)(docs?|guides?|adr|readme)/i.test(file.path));
|
|
29
|
-
const codeFiles = normalized.filter(
|
|
30
|
-
(file) => file.loc > 0 && !/(^|\/)(docs?|guides?|adr|readme)/i.test(file.path)
|
|
31
|
-
);
|
|
31
|
+
const codeFiles = normalized.filter((file) => file.loc > 0 && isAuditSourceFile(file));
|
|
32
32
|
const docDensity = codeFiles.length > 0 ? docFiles.length / codeFiles.length : 0;
|
|
33
33
|
const undocumentedHotspots = codeFiles
|
|
34
34
|
.filter((file) => file.loc >= 320)
|
|
@@ -42,7 +42,7 @@ function buildDocumentationInventory(ingest = {}) {
|
|
|
42
42
|
path: file.path,
|
|
43
43
|
loc: file.loc,
|
|
44
44
|
language: file.language,
|
|
45
|
-
severity:
|
|
45
|
+
severity: "P2",
|
|
46
46
|
}));
|
|
47
47
|
|
|
48
48
|
return {
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
2
|
+
".c",
|
|
3
|
+
".cc",
|
|
4
|
+
".cpp",
|
|
5
|
+
".cs",
|
|
6
|
+
".css",
|
|
7
|
+
".go",
|
|
8
|
+
".graphql",
|
|
9
|
+
".h",
|
|
10
|
+
".hpp",
|
|
11
|
+
".java",
|
|
12
|
+
".js",
|
|
13
|
+
".jsx",
|
|
14
|
+
".kt",
|
|
15
|
+
".mjs",
|
|
16
|
+
".php",
|
|
17
|
+
".py",
|
|
18
|
+
".rb",
|
|
19
|
+
".rs",
|
|
20
|
+
".scss",
|
|
21
|
+
".sql",
|
|
22
|
+
".svelte",
|
|
23
|
+
".swift",
|
|
24
|
+
".tf",
|
|
25
|
+
".ts",
|
|
26
|
+
".tsx",
|
|
27
|
+
".vue",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
const SOURCE_LANGUAGES = new Set([
|
|
31
|
+
"c",
|
|
32
|
+
"c++",
|
|
33
|
+
"c#",
|
|
34
|
+
"css",
|
|
35
|
+
"dockerfile",
|
|
36
|
+
"go",
|
|
37
|
+
"graphql",
|
|
38
|
+
"java",
|
|
39
|
+
"javascript",
|
|
40
|
+
"jsx",
|
|
41
|
+
"kotlin",
|
|
42
|
+
"php",
|
|
43
|
+
"python",
|
|
44
|
+
"ruby",
|
|
45
|
+
"rust",
|
|
46
|
+
"scss",
|
|
47
|
+
"sql",
|
|
48
|
+
"svelte",
|
|
49
|
+
"swift",
|
|
50
|
+
"terraform",
|
|
51
|
+
"typescript",
|
|
52
|
+
"tsx",
|
|
53
|
+
"vue",
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
const LOCKFILE_NAMES = new Set([
|
|
57
|
+
"cargo.lock",
|
|
58
|
+
"composer.lock",
|
|
59
|
+
"go.sum",
|
|
60
|
+
"package-lock.json",
|
|
61
|
+
"pipfile.lock",
|
|
62
|
+
"pnpm-lock.yaml",
|
|
63
|
+
"poetry.lock",
|
|
64
|
+
"yarn.lock",
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
function normalizePath(value) {
|
|
68
|
+
return String(value || "").replace(/\\/g, "/").replace(/^\.\/+/, "").toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function basename(filePath) {
|
|
72
|
+
const normalized = normalizePath(filePath);
|
|
73
|
+
return normalized.slice(normalized.lastIndexOf("/") + 1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function extension(filePath) {
|
|
77
|
+
const name = basename(filePath);
|
|
78
|
+
const dot = name.lastIndexOf(".");
|
|
79
|
+
return dot > 0 ? name.slice(dot) : "";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function isAuditTestPath(filePath) {
|
|
83
|
+
const normalized = normalizePath(filePath);
|
|
84
|
+
return /(^|\/)(__tests__|tests?|specs?)(\/|$)|\.(test|spec)\.[^.\/]+$/i.test(normalized);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function isAuditSupportPath(filePath) {
|
|
88
|
+
const normalized = normalizePath(filePath);
|
|
89
|
+
if (!normalized) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (LOCKFILE_NAMES.has(basename(normalized))) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
if (/(^|\/)(node_modules|vendor|fixtures?|__fixtures__|mocks?|__mocks__|snapshots?|__snapshots__)(\/|$)/i.test(normalized)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (/(^|\/)(generated|__generated__|auto-generated|coverage|dist|build|out|\.next|\.nuxt|\.turbo|\.cache)(\/|$)/i.test(normalized)) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
if (/^(docs?|guides?|adr|tasks?|\.github|\.vscode|\.husky|public)(\/|$)/i.test(normalized)) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
if (/(\.min|\.bundle|generated)\.[^.\/]+$/i.test(normalized)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function isAuditSourceFile(file = {}) {
|
|
111
|
+
const filePath = typeof file === "string" ? file : file.path;
|
|
112
|
+
const normalized = normalizePath(filePath);
|
|
113
|
+
if (!normalized || isAuditTestPath(normalized) || isAuditSupportPath(normalized)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (SOURCE_EXTENSIONS.has(extension(normalized))) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return SOURCE_LANGUAGES.has(String(file.language || "").trim().toLowerCase());
|
|
120
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isAuditSourceFile } from "./path-classification.js";
|
|
2
|
+
|
|
1
3
|
function normalizeString(value) {
|
|
2
4
|
return String(value || "").trim();
|
|
3
5
|
}
|
|
@@ -21,6 +23,7 @@ function summarizeSeverity(findings = []) {
|
|
|
21
23
|
function buildRuntimeHotspots(ingest = {}) {
|
|
22
24
|
const indexed = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
23
25
|
return indexed
|
|
26
|
+
.filter((file) => isAuditSourceFile(file))
|
|
24
27
|
.filter((file) => Number(file.loc || 0) >= 280)
|
|
25
28
|
.sort((left, right) => Number(right.loc || 0) - Number(left.loc || 0))
|
|
26
29
|
.slice(0, 20)
|
|
@@ -28,7 +31,7 @@ function buildRuntimeHotspots(ingest = {}) {
|
|
|
28
31
|
path: toPosixPath(file.path),
|
|
29
32
|
loc: Number(file.loc || 0),
|
|
30
33
|
language: file.language,
|
|
31
|
-
severity:
|
|
34
|
+
severity: "P2",
|
|
32
35
|
}));
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isAuditSourceFile, isAuditTestPath } from "./path-classification.js";
|
|
2
|
+
|
|
1
3
|
function normalizeString(value) {
|
|
2
4
|
return String(value || "").trim();
|
|
3
5
|
}
|
|
@@ -18,10 +20,6 @@ function summarizeSeverity(findings = []) {
|
|
|
18
20
|
return summary;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
function isTestPath(filePath) {
|
|
22
|
-
return /(^|\/)(__tests__|tests?|spec)(\/|$)|\.(test|spec)\.[^.]+$/i.test(filePath);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
23
|
function buildCoverageInventory(ingest = {}) {
|
|
26
24
|
const indexedFiles = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
27
25
|
const normalized = indexedFiles.map((file) => ({
|
|
@@ -30,8 +28,8 @@ function buildCoverageInventory(ingest = {}) {
|
|
|
30
28
|
loc: Number(file.loc || 0),
|
|
31
29
|
}));
|
|
32
30
|
|
|
33
|
-
const testFiles = normalized.filter((file) =>
|
|
34
|
-
const nonTestCodeFiles = normalized.filter((file) =>
|
|
31
|
+
const testFiles = normalized.filter((file) => isAuditTestPath(file.path));
|
|
32
|
+
const nonTestCodeFiles = normalized.filter((file) => isAuditSourceFile(file) && file.loc > 0);
|
|
35
33
|
const ratio = nonTestCodeFiles.length > 0 ? testFiles.length / nonTestCodeFiles.length : 0;
|
|
36
34
|
const likelyGaps = nonTestCodeFiles
|
|
37
35
|
.filter((file) => file.loc >= 260)
|
|
@@ -45,7 +43,7 @@ function buildCoverageInventory(ingest = {}) {
|
|
|
45
43
|
path: file.path,
|
|
46
44
|
loc: file.loc,
|
|
47
45
|
language: file.language,
|
|
48
|
-
severity:
|
|
46
|
+
severity: "P2",
|
|
49
47
|
}));
|
|
50
48
|
|
|
51
49
|
return {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function resolveActionOptions(options = {}, command = null) {
|
|
2
|
+
const localOptions = options && typeof options.opts === "function" ? options.opts() : { ...options };
|
|
3
|
+
const parent = command?.parent;
|
|
4
|
+
if (!parent || typeof parent.opts !== "function") {
|
|
5
|
+
return localOptions;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const parentOptions = parent.opts();
|
|
9
|
+
const merged = {
|
|
10
|
+
...parentOptions,
|
|
11
|
+
...localOptions,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
for (const [key, value] of Object.entries(parentOptions)) {
|
|
15
|
+
const parentSource =
|
|
16
|
+
typeof parent.getOptionValueSource === "function" ? parent.getOptionValueSource(key) : null;
|
|
17
|
+
const localSource =
|
|
18
|
+
typeof command?.getOptionValueSource === "function" ? command.getOptionValueSource(key) : null;
|
|
19
|
+
const parentWasProvided = parentSource && parentSource !== "default";
|
|
20
|
+
const localIsOnlyDefault = localSource === "default" || localOptions[key] === undefined;
|
|
21
|
+
if (parentWasProvided && localIsOnlyDefault) {
|
|
22
|
+
merged[key] = value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return merged;
|
|
27
|
+
}
|
package/src/commands/audit.js
CHANGED
|
@@ -10,6 +10,7 @@ import { loadAuditRegistry, selectAuditAgents } from "../audit/registry.js";
|
|
|
10
10
|
import { resolveOutputRoot } from "../config/service.js";
|
|
11
11
|
import { createAgentEvent } from "../events/schema.js";
|
|
12
12
|
import { createAuditSessionReporter, resolveAuditSessionId } from "../session/audit-reporter.js";
|
|
13
|
+
import { resolveActionOptions } from "./action-options.js";
|
|
13
14
|
import { buildLegacyArgs } from "./legacy-args.js";
|
|
14
15
|
|
|
15
16
|
function shouldEmitJson(options, command) {
|
|
@@ -213,16 +214,17 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
213
214
|
.option("--output-dir <path>", "Optional artifact output root override")
|
|
214
215
|
.option("--json", "Emit machine-readable output")
|
|
215
216
|
.action(async (options, command) => {
|
|
216
|
-
const
|
|
217
|
-
const
|
|
217
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
218
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
219
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
218
220
|
const outputRoot = await resolveOutputRoot({
|
|
219
221
|
cwd: targetPath,
|
|
220
|
-
outputDirOverride:
|
|
222
|
+
outputDirOverride: actionOptions.outputDir,
|
|
221
223
|
env: process.env,
|
|
222
224
|
});
|
|
223
225
|
const runDirectory = await resolveAuditRunDirectory({
|
|
224
226
|
outputRoot,
|
|
225
|
-
runId:
|
|
227
|
+
runId: actionOptions.runId,
|
|
226
228
|
});
|
|
227
229
|
const { report } = await loadAuditRunReport(runDirectory);
|
|
228
230
|
const ddPackage = await writeDdPackage({
|
|
@@ -264,11 +266,12 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
264
266
|
.option("--refresh", "Refresh CODEBASE_INGEST before replay")
|
|
265
267
|
.option("--json", "Emit machine-readable output")
|
|
266
268
|
.action(async (runId, options, command) => {
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
270
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
271
|
+
const initialTargetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
269
272
|
const outputRoot = await resolveOutputRoot({
|
|
270
273
|
cwd: initialTargetPath,
|
|
271
|
-
outputDirOverride:
|
|
274
|
+
outputDirOverride: actionOptions.outputDir,
|
|
272
275
|
env: process.env,
|
|
273
276
|
});
|
|
274
277
|
const baseRunDirectory = await resolveAuditRunDirectory({
|
|
@@ -276,12 +279,12 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
276
279
|
runId,
|
|
277
280
|
});
|
|
278
281
|
const { report: baseReport } = await loadAuditRunReport(baseRunDirectory);
|
|
279
|
-
const targetPath =
|
|
280
|
-
? path.resolve(process.cwd(), String(
|
|
282
|
+
const targetPath = actionOptions.path
|
|
283
|
+
? path.resolve(process.cwd(), String(actionOptions.path || "."))
|
|
281
284
|
: path.resolve(String(baseReport.targetPath || "."));
|
|
282
285
|
|
|
283
286
|
const registry = await loadAuditRegistry({
|
|
284
|
-
registryFile:
|
|
287
|
+
registryFile: actionOptions.registryFile,
|
|
285
288
|
});
|
|
286
289
|
const selected = selectAuditAgents(registry.agents, (baseReport.selectedAgents || []).join(","));
|
|
287
290
|
if (selected.selected.length === 0) {
|
|
@@ -292,9 +295,9 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
292
295
|
targetPath,
|
|
293
296
|
agents: selected.selected,
|
|
294
297
|
maxParallel: Number(baseReport.maxParallel || 1),
|
|
295
|
-
outputDir:
|
|
298
|
+
outputDir: actionOptions.outputDir,
|
|
296
299
|
dryRun: Boolean(baseReport.dryRun),
|
|
297
|
-
refreshIngest: Boolean(
|
|
300
|
+
refreshIngest: Boolean(actionOptions.refresh),
|
|
298
301
|
isolation: baseReport.isolation || "strict",
|
|
299
302
|
seedFromDeterministic: baseReport.seedFromDeterministic !== false,
|
|
300
303
|
});
|
|
@@ -341,11 +344,12 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
341
344
|
.option("--output-dir <path>", "Optional artifact output root override")
|
|
342
345
|
.option("--json", "Emit machine-readable output")
|
|
343
346
|
.action(async (baseRunId, candidateRunId, options, command) => {
|
|
344
|
-
const
|
|
345
|
-
const
|
|
347
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
348
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
349
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
346
350
|
const outputRoot = await resolveOutputRoot({
|
|
347
351
|
cwd: targetPath,
|
|
348
|
-
outputDirOverride:
|
|
352
|
+
outputDirOverride: actionOptions.outputDir,
|
|
349
353
|
env: process.env,
|
|
350
354
|
});
|
|
351
355
|
const baseRunDirectory = await resolveAuditRunDirectory({
|
|
@@ -388,9 +392,10 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
388
392
|
.option("--registry-file <path>", "Optional custom audit registry file")
|
|
389
393
|
.option("--json", "Emit machine-readable output")
|
|
390
394
|
.action(async (options, command) => {
|
|
391
|
-
const
|
|
395
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
396
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
392
397
|
const registry = await loadAuditRegistry({
|
|
393
|
-
registryFile:
|
|
398
|
+
registryFile: actionOptions.registryFile,
|
|
394
399
|
});
|
|
395
400
|
const payload = {
|
|
396
401
|
command: "audit registry",
|
|
@@ -422,10 +427,11 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
422
427
|
.option("--dry-run", "Skip deterministic baseline and run security planning only")
|
|
423
428
|
.option("--json", "Emit machine-readable output")
|
|
424
429
|
.action(async (options, command) => {
|
|
425
|
-
const
|
|
426
|
-
const
|
|
430
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
431
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
432
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
427
433
|
const registry = await loadAuditRegistry({
|
|
428
|
-
registryFile:
|
|
434
|
+
registryFile: actionOptions.registryFile,
|
|
429
435
|
});
|
|
430
436
|
const selected = selectAuditAgents(registry.agents, "security");
|
|
431
437
|
if (selected.selected.length !== 1) {
|
|
@@ -436,9 +442,9 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
436
442
|
targetPath,
|
|
437
443
|
agents: selected.selected,
|
|
438
444
|
maxParallel: 1,
|
|
439
|
-
outputDir:
|
|
440
|
-
dryRun: Boolean(
|
|
441
|
-
refreshIngest: Boolean(
|
|
445
|
+
outputDir: actionOptions.outputDir,
|
|
446
|
+
dryRun: Boolean(actionOptions.dryRun),
|
|
447
|
+
refreshIngest: Boolean(actionOptions.refresh),
|
|
442
448
|
});
|
|
443
449
|
const securityAgent = result.agentResults.find((agent) => agent.agentId === "security") || null;
|
|
444
450
|
|
|
@@ -486,10 +492,11 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
486
492
|
.option("--dry-run", "Skip deterministic baseline and run architecture planning only")
|
|
487
493
|
.option("--json", "Emit machine-readable output")
|
|
488
494
|
.action(async (options, command) => {
|
|
489
|
-
const
|
|
490
|
-
const
|
|
495
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
496
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
497
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
491
498
|
const registry = await loadAuditRegistry({
|
|
492
|
-
registryFile:
|
|
499
|
+
registryFile: actionOptions.registryFile,
|
|
493
500
|
});
|
|
494
501
|
const selected = selectAuditAgents(registry.agents, "architecture");
|
|
495
502
|
if (selected.selected.length !== 1) {
|
|
@@ -500,9 +507,9 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
500
507
|
targetPath,
|
|
501
508
|
agents: selected.selected,
|
|
502
509
|
maxParallel: 1,
|
|
503
|
-
outputDir:
|
|
504
|
-
dryRun: Boolean(
|
|
505
|
-
refreshIngest: Boolean(
|
|
510
|
+
outputDir: actionOptions.outputDir,
|
|
511
|
+
dryRun: Boolean(actionOptions.dryRun),
|
|
512
|
+
refreshIngest: Boolean(actionOptions.refresh),
|
|
506
513
|
});
|
|
507
514
|
const architectureAgent =
|
|
508
515
|
result.agentResults.find((agent) => agent.agentId === "architecture") || null;
|
|
@@ -551,10 +558,11 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
551
558
|
.option("--dry-run", "Skip deterministic baseline and run testing planning only")
|
|
552
559
|
.option("--json", "Emit machine-readable output")
|
|
553
560
|
.action(async (options, command) => {
|
|
554
|
-
const
|
|
555
|
-
const
|
|
561
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
562
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
563
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
556
564
|
const registry = await loadAuditRegistry({
|
|
557
|
-
registryFile:
|
|
565
|
+
registryFile: actionOptions.registryFile,
|
|
558
566
|
});
|
|
559
567
|
const selected = selectAuditAgents(registry.agents, "testing");
|
|
560
568
|
if (selected.selected.length !== 1) {
|
|
@@ -565,9 +573,9 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
565
573
|
targetPath,
|
|
566
574
|
agents: selected.selected,
|
|
567
575
|
maxParallel: 1,
|
|
568
|
-
outputDir:
|
|
569
|
-
dryRun: Boolean(
|
|
570
|
-
refreshIngest: Boolean(
|
|
576
|
+
outputDir: actionOptions.outputDir,
|
|
577
|
+
dryRun: Boolean(actionOptions.dryRun),
|
|
578
|
+
refreshIngest: Boolean(actionOptions.refresh),
|
|
571
579
|
});
|
|
572
580
|
const testingAgent = result.agentResults.find((agent) => agent.agentId === "testing") || null;
|
|
573
581
|
|
|
@@ -615,10 +623,11 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
615
623
|
.option("--dry-run", "Skip deterministic baseline and run performance planning only")
|
|
616
624
|
.option("--json", "Emit machine-readable output")
|
|
617
625
|
.action(async (options, command) => {
|
|
618
|
-
const
|
|
619
|
-
const
|
|
626
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
627
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
628
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
620
629
|
const registry = await loadAuditRegistry({
|
|
621
|
-
registryFile:
|
|
630
|
+
registryFile: actionOptions.registryFile,
|
|
622
631
|
});
|
|
623
632
|
const selected = selectAuditAgents(registry.agents, "performance");
|
|
624
633
|
if (selected.selected.length !== 1) {
|
|
@@ -629,9 +638,9 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
629
638
|
targetPath,
|
|
630
639
|
agents: selected.selected,
|
|
631
640
|
maxParallel: 1,
|
|
632
|
-
outputDir:
|
|
633
|
-
dryRun: Boolean(
|
|
634
|
-
refreshIngest: Boolean(
|
|
641
|
+
outputDir: actionOptions.outputDir,
|
|
642
|
+
dryRun: Boolean(actionOptions.dryRun),
|
|
643
|
+
refreshIngest: Boolean(actionOptions.refresh),
|
|
635
644
|
});
|
|
636
645
|
const performanceAgent =
|
|
637
646
|
result.agentResults.find((agent) => agent.agentId === "performance") || null;
|
|
@@ -680,10 +689,11 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
680
689
|
.option("--dry-run", "Skip deterministic baseline and run compliance planning only")
|
|
681
690
|
.option("--json", "Emit machine-readable output")
|
|
682
691
|
.action(async (options, command) => {
|
|
683
|
-
const
|
|
684
|
-
const
|
|
692
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
693
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
694
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
685
695
|
const registry = await loadAuditRegistry({
|
|
686
|
-
registryFile:
|
|
696
|
+
registryFile: actionOptions.registryFile,
|
|
687
697
|
});
|
|
688
698
|
const selected = selectAuditAgents(registry.agents, "compliance");
|
|
689
699
|
if (selected.selected.length !== 1) {
|
|
@@ -694,9 +704,9 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
694
704
|
targetPath,
|
|
695
705
|
agents: selected.selected,
|
|
696
706
|
maxParallel: 1,
|
|
697
|
-
outputDir:
|
|
698
|
-
dryRun: Boolean(
|
|
699
|
-
refreshIngest: Boolean(
|
|
707
|
+
outputDir: actionOptions.outputDir,
|
|
708
|
+
dryRun: Boolean(actionOptions.dryRun),
|
|
709
|
+
refreshIngest: Boolean(actionOptions.refresh),
|
|
700
710
|
});
|
|
701
711
|
const complianceAgent =
|
|
702
712
|
result.agentResults.find((agent) => agent.agentId === "compliance") || null;
|
|
@@ -745,10 +755,11 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
745
755
|
.option("--dry-run", "Skip deterministic baseline and run documentation planning only")
|
|
746
756
|
.option("--json", "Emit machine-readable output")
|
|
747
757
|
.action(async (options, command) => {
|
|
748
|
-
const
|
|
749
|
-
const
|
|
758
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
759
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
760
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
750
761
|
const registry = await loadAuditRegistry({
|
|
751
|
-
registryFile:
|
|
762
|
+
registryFile: actionOptions.registryFile,
|
|
752
763
|
});
|
|
753
764
|
const selected = selectAuditAgents(registry.agents, "documentation");
|
|
754
765
|
if (selected.selected.length !== 1) {
|
|
@@ -759,9 +770,9 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
759
770
|
targetPath,
|
|
760
771
|
agents: selected.selected,
|
|
761
772
|
maxParallel: 1,
|
|
762
|
-
outputDir:
|
|
763
|
-
dryRun: Boolean(
|
|
764
|
-
refreshIngest: Boolean(
|
|
773
|
+
outputDir: actionOptions.outputDir,
|
|
774
|
+
dryRun: Boolean(actionOptions.dryRun),
|
|
775
|
+
refreshIngest: Boolean(actionOptions.refresh),
|
|
765
776
|
});
|
|
766
777
|
const documentationAgent =
|
|
767
778
|
result.agentResults.find((agent) => agent.agentId === "documentation") || null;
|
|
@@ -808,8 +819,9 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
808
819
|
.option("--reuse-omargate <runId>", "Reuse deterministic findings from an OmarGate run id or latest")
|
|
809
820
|
.option("--json", "Emit machine-readable output")
|
|
810
821
|
.action(async (options, command) => {
|
|
822
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
811
823
|
const legacyArgs = buildLegacyArgs(["/audit"], {
|
|
812
|
-
commandOptions:
|
|
824
|
+
commandOptions: actionOptions,
|
|
813
825
|
command,
|
|
814
826
|
});
|
|
815
827
|
await invokeLegacy(legacyArgs);
|
|
@@ -848,9 +860,10 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
848
860
|
.option("--output-dir <path>", "Artifact output root override")
|
|
849
861
|
.option("--json", "Emit machine-readable final output")
|
|
850
862
|
.action(async (options, command) => {
|
|
851
|
-
const
|
|
852
|
-
const
|
|
853
|
-
const
|
|
863
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
864
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
865
|
+
const emitStream = Boolean(actionOptions.stream);
|
|
866
|
+
const targetPath = path.resolve(process.cwd(), String(actionOptions.path || "."));
|
|
854
867
|
|
|
855
868
|
// Lazy-load modules
|
|
856
869
|
const { julesAuditLoop } = await import("../agents/jules/loop.js");
|
|
@@ -861,7 +874,7 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
861
874
|
const { runDeterministicReviewPipeline } = await import("../review/local-review.js");
|
|
862
875
|
const fsp = await import("node:fs/promises");
|
|
863
876
|
|
|
864
|
-
const outputRoot = await resolveOut({ targetPath, outputDir:
|
|
877
|
+
const outputRoot = await resolveOut({ targetPath, outputDir: actionOptions.outputDir });
|
|
865
878
|
const artifactDir = path.join(outputRoot, "reports", "jules-" + Date.now());
|
|
866
879
|
await fsp.mkdir(artifactDir, { recursive: true });
|
|
867
880
|
|
|
@@ -876,7 +889,7 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
876
889
|
try {
|
|
877
890
|
ingest = await generateCodebaseIngest({
|
|
878
891
|
rootPath: targetPath, outputDir: outputRoot,
|
|
879
|
-
refresh: Boolean(
|
|
892
|
+
refresh: Boolean(actionOptions.refresh),
|
|
880
893
|
});
|
|
881
894
|
} catch {
|
|
882
895
|
ingest = await collectCodebaseIngest({ rootPath: targetPath });
|
|
@@ -887,7 +900,7 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
887
900
|
|
|
888
901
|
// ── [2] OMAR BASELINE (blind — Jules won't see until reconciliation) ──
|
|
889
902
|
let omarBaseline = null;
|
|
890
|
-
if (!
|
|
903
|
+
if (!actionOptions.skipBaseline) {
|
|
891
904
|
emitProgress(onEvent, JULES_DEFINITION, "Running Omar 7-layer deterministic baseline...");
|
|
892
905
|
try {
|
|
893
906
|
omarBaseline = await runDeterministicReviewPipeline({
|
|
@@ -925,14 +938,14 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
925
938
|
|
|
926
939
|
// ── [5] PROVIDER ──────────────────────────────────────────────
|
|
927
940
|
const providerConfig = {};
|
|
928
|
-
if (
|
|
929
|
-
if (
|
|
930
|
-
if (
|
|
941
|
+
if (actionOptions.provider) providerConfig.provider = actionOptions.provider;
|
|
942
|
+
if (actionOptions.model) providerConfig.model = actionOptions.model;
|
|
943
|
+
if (actionOptions.apiKey) providerConfig.apiKey = actionOptions.apiKey;
|
|
931
944
|
|
|
932
945
|
// ── [6] SYSTEM PROMPT (full production prompt) ─────────────────
|
|
933
946
|
const { buildJulesProductionPrompt } = await import("../agents/jules/config/system-prompt.js");
|
|
934
947
|
const systemPrompt = buildJulesProductionPrompt({
|
|
935
|
-
mode:
|
|
948
|
+
mode: actionOptions.mode,
|
|
936
949
|
framework: ingest?.frameworks?.[0] || "unknown",
|
|
937
950
|
componentCount: ingest?.indexedFiles?.files?.filter(
|
|
938
951
|
f => /\.(tsx|jsx|vue|svelte)$/.test(f.path || ""),
|
|
@@ -952,14 +965,14 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
952
965
|
blackboard,
|
|
953
966
|
memory: memoryIndex,
|
|
954
967
|
budget: {
|
|
955
|
-
maxCostUsd: parseFloat(
|
|
968
|
+
maxCostUsd: parseFloat(actionOptions.maxCost) || 5.0,
|
|
956
969
|
maxOutputTokens: JULES_DEFINITION.budget.maxOutputTokens,
|
|
957
970
|
maxRuntimeMs: JULES_DEFINITION.budget.maxRuntimeMs,
|
|
958
971
|
maxToolCalls: JULES_DEFINITION.budget.maxToolCalls,
|
|
959
972
|
},
|
|
960
973
|
provider: providerConfig,
|
|
961
|
-
mode:
|
|
962
|
-
maxTurns: parseInt(
|
|
974
|
+
mode: actionOptions.mode,
|
|
975
|
+
maxTurns: parseInt(actionOptions.maxTurns) || 25,
|
|
963
976
|
onEvent,
|
|
964
977
|
});
|
|
965
978
|
|
|
@@ -973,8 +986,8 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
973
986
|
|
|
974
987
|
// ── [7.5] RUNTIME AUDIT (if --url provided or URL detected) ────
|
|
975
988
|
let runtimeResult = null;
|
|
976
|
-
const runtimeUrl =
|
|
977
|
-
if (runtimeUrl || !
|
|
989
|
+
const runtimeUrl = actionOptions.url || null;
|
|
990
|
+
if (runtimeUrl || !actionOptions.skipRuntime) {
|
|
978
991
|
emitProgress(onEvent, JULES_DEFINITION, "Checking for deployed URL...");
|
|
979
992
|
try {
|
|
980
993
|
const { runtimeAudit: runRT } = await import("../agents/jules/tools/runtime-audit.js");
|
|
@@ -1057,7 +1070,7 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
1057
1070
|
command: "audit frontend",
|
|
1058
1071
|
persona: JULES_DEFINITION.persona,
|
|
1059
1072
|
targetPath,
|
|
1060
|
-
mode:
|
|
1073
|
+
mode: actionOptions.mode,
|
|
1061
1074
|
framework: ingest?.frameworks?.[0] || "unknown",
|
|
1062
1075
|
findings: allFindings,
|
|
1063
1076
|
summary: { total: allFindings.length, ...severityCounts, blocking: severityCounts.P0 > 0 || severityCounts.P1 > 0 },
|
|
@@ -1109,7 +1122,7 @@ export function registerAuditCommand(program, invokeLegacy) {
|
|
|
1109
1122
|
const syncResult = await syncRunToDashboard({
|
|
1110
1123
|
command: "audit frontend",
|
|
1111
1124
|
persona: JULES_DEFINITION.persona,
|
|
1112
|
-
mode:
|
|
1125
|
+
mode: actionOptions.mode,
|
|
1113
1126
|
framework: ingest?.frameworks?.[0] || "unknown",
|
|
1114
1127
|
summary: { total: allFindings.length, ...severityCounts, blocking: severityCounts.P0 > 0 || severityCounts.P1 > 0 },
|
|
1115
1128
|
usage: report?.usage || {},
|
package/src/commands/review.js
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
writeReviewComparisonArtifact,
|
|
26
26
|
writeReviewRunContext,
|
|
27
27
|
} from "../review/replay.js";
|
|
28
|
+
import { resolveActionOptions } from "./action-options.js";
|
|
28
29
|
|
|
29
30
|
function shouldEmitJson(options, command) {
|
|
30
31
|
const local = Boolean(options && options.json);
|
|
@@ -236,16 +237,17 @@ function registerVerdictCommand(review, verdict) {
|
|
|
236
237
|
.option("--actor <id>", "Operator identifier for audit trail", "")
|
|
237
238
|
.option("--json", "Emit machine-readable output")
|
|
238
239
|
.action(async (findingId, options, command) => {
|
|
239
|
-
const
|
|
240
|
-
const
|
|
240
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
241
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
242
|
+
const targetPath = resolveTargetPath(".", actionOptions);
|
|
241
243
|
const result = await recordReviewDecision({
|
|
242
244
|
targetPath,
|
|
243
|
-
runId:
|
|
244
|
-
outputDir:
|
|
245
|
+
runId: actionOptions.runId,
|
|
246
|
+
outputDir: actionOptions.outputDir,
|
|
245
247
|
findingId,
|
|
246
248
|
verdict,
|
|
247
|
-
note:
|
|
248
|
-
actor:
|
|
249
|
+
note: actionOptions.note,
|
|
250
|
+
actor: actionOptions.actor,
|
|
249
251
|
env: process.env,
|
|
250
252
|
});
|
|
251
253
|
|
|
@@ -374,12 +376,13 @@ export function registerReviewCommand(program) {
|
|
|
374
376
|
.option("--output-dir <path>", "Optional artifact output root override")
|
|
375
377
|
.option("--json", "Emit machine-readable output")
|
|
376
378
|
.action(async (options, command) => {
|
|
377
|
-
const
|
|
378
|
-
const
|
|
379
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
380
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
381
|
+
const targetPath = resolveTargetPath(".", actionOptions);
|
|
379
382
|
const loaded = await loadUnifiedReviewReport({
|
|
380
383
|
targetPath,
|
|
381
|
-
runId:
|
|
382
|
-
outputDir:
|
|
384
|
+
runId: actionOptions.runId,
|
|
385
|
+
outputDir: actionOptions.outputDir,
|
|
383
386
|
env: process.env,
|
|
384
387
|
});
|
|
385
388
|
|
|
@@ -412,14 +415,15 @@ export function registerReviewCommand(program) {
|
|
|
412
415
|
.option("--output-file <path>", "Optional custom export output path")
|
|
413
416
|
.option("--json", "Emit machine-readable output")
|
|
414
417
|
.action(async (options, command) => {
|
|
415
|
-
const
|
|
416
|
-
const
|
|
418
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
419
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
420
|
+
const targetPath = resolveTargetPath(".", actionOptions);
|
|
417
421
|
const exported = await exportUnifiedReviewReport({
|
|
418
422
|
targetPath,
|
|
419
|
-
runId:
|
|
420
|
-
outputDir:
|
|
421
|
-
format:
|
|
422
|
-
outputFile:
|
|
423
|
+
runId: actionOptions.runId,
|
|
424
|
+
outputDir: actionOptions.outputDir,
|
|
425
|
+
format: actionOptions.format,
|
|
426
|
+
outputFile: actionOptions.outputFile,
|
|
423
427
|
env: process.env,
|
|
424
428
|
});
|
|
425
429
|
|
|
@@ -453,12 +457,13 @@ export function registerReviewCommand(program) {
|
|
|
453
457
|
.option("--ai-dry-run", "Force AI dry-run during replay")
|
|
454
458
|
.option("--json", "Emit machine-readable output")
|
|
455
459
|
.action(async (runId, options, command) => {
|
|
456
|
-
const
|
|
457
|
-
const
|
|
460
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
461
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
462
|
+
const lookupPath = resolveTargetPath(".", actionOptions);
|
|
458
463
|
const source = await loadUnifiedReviewReport({
|
|
459
464
|
targetPath: lookupPath,
|
|
460
465
|
runId,
|
|
461
|
-
outputDir:
|
|
466
|
+
outputDir: actionOptions.outputDir,
|
|
462
467
|
env: process.env,
|
|
463
468
|
});
|
|
464
469
|
|
|
@@ -470,20 +475,20 @@ export function registerReviewCommand(program) {
|
|
|
470
475
|
}
|
|
471
476
|
|
|
472
477
|
const invocation = sourceContext?.context?.invocation || {};
|
|
473
|
-
const replayTargetPath =
|
|
474
|
-
? resolveTargetPath(".",
|
|
478
|
+
const replayTargetPath = actionOptions.path
|
|
479
|
+
? resolveTargetPath(".", actionOptions)
|
|
475
480
|
: path.resolve(sourceContext?.context?.targetPath || source.report.targetPath || lookupPath);
|
|
476
|
-
const aiEnabled =
|
|
481
|
+
const aiEnabled = actionOptions.noAi ? false : Boolean(invocation.aiEnabled);
|
|
477
482
|
|
|
478
483
|
const replayOutcome = await executeReviewRun({
|
|
479
484
|
targetPath: replayTargetPath,
|
|
480
485
|
mode: sourceContext?.context?.mode || source.report.mode || "full",
|
|
481
|
-
outputDir:
|
|
482
|
-
specFile:
|
|
483
|
-
refreshIngest: Boolean(
|
|
486
|
+
outputDir: actionOptions.outputDir || invocation.outputDir || "",
|
|
487
|
+
specFile: actionOptions.spec || invocation.specFile || "",
|
|
488
|
+
refreshIngest: Boolean(actionOptions.refresh || invocation.refreshIngest),
|
|
484
489
|
aiConfig: {
|
|
485
490
|
enable: aiEnabled,
|
|
486
|
-
aiDryRun: Boolean(
|
|
491
|
+
aiDryRun: Boolean(actionOptions.aiDryRun || invocation.aiDryRun),
|
|
487
492
|
provider: invocation.provider,
|
|
488
493
|
model: invocation.model,
|
|
489
494
|
sessionId: invocation.sessionId,
|
|
@@ -542,18 +547,19 @@ export function registerReviewCommand(program) {
|
|
|
542
547
|
.option("--output-file <path>", "Optional custom output path for comparison JSON")
|
|
543
548
|
.option("--json", "Emit machine-readable output")
|
|
544
549
|
.action(async (baseRunId, candidateRunId, options, command) => {
|
|
545
|
-
const
|
|
546
|
-
const
|
|
550
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
551
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
552
|
+
const targetPath = resolveTargetPath(".", actionOptions);
|
|
547
553
|
const base = await loadUnifiedReviewReport({
|
|
548
554
|
targetPath,
|
|
549
555
|
runId: baseRunId,
|
|
550
|
-
outputDir:
|
|
556
|
+
outputDir: actionOptions.outputDir,
|
|
551
557
|
env: process.env,
|
|
552
558
|
});
|
|
553
559
|
const candidate = await loadUnifiedReviewReport({
|
|
554
560
|
targetPath,
|
|
555
561
|
runId: candidateRunId,
|
|
556
|
-
outputDir:
|
|
562
|
+
outputDir: actionOptions.outputDir,
|
|
557
563
|
env: process.env,
|
|
558
564
|
});
|
|
559
565
|
|
|
@@ -566,8 +572,8 @@ export function registerReviewCommand(program) {
|
|
|
566
572
|
});
|
|
567
573
|
|
|
568
574
|
let outputPath = artifact.artifactPath;
|
|
569
|
-
if (
|
|
570
|
-
outputPath = path.resolve(targetPath, String(
|
|
575
|
+
if (actionOptions.outputFile) {
|
|
576
|
+
outputPath = path.resolve(targetPath, String(actionOptions.outputFile));
|
|
571
577
|
await fsp.mkdir(path.dirname(outputPath), { recursive: true });
|
|
572
578
|
await fsp.writeFile(outputPath, `${JSON.stringify(artifact.payload, null, 2)}\n`, "utf-8");
|
|
573
579
|
}
|
|
@@ -611,13 +617,14 @@ export function registerReviewCommand(program) {
|
|
|
611
617
|
.option("--refresh", "Refresh CODEBASE_INGEST prior to compatibility scan")
|
|
612
618
|
.option("--json", "Emit machine-readable output")
|
|
613
619
|
.action(async (options, command) => {
|
|
614
|
-
const
|
|
615
|
-
const
|
|
620
|
+
const actionOptions = resolveActionOptions(options, command);
|
|
621
|
+
const emitJson = shouldEmitJson(actionOptions, command);
|
|
622
|
+
const targetPath = resolveTargetPath(".", actionOptions);
|
|
616
623
|
let ingestRefresh = null;
|
|
617
|
-
if (
|
|
624
|
+
if (actionOptions.refresh) {
|
|
618
625
|
const ingestResolution = await resolveCodebaseIngest({
|
|
619
626
|
rootPath: targetPath,
|
|
620
|
-
outputDir:
|
|
627
|
+
outputDir: actionOptions.outputDir,
|
|
621
628
|
refresh: true,
|
|
622
629
|
});
|
|
623
630
|
ingestRefresh = {
|
|
@@ -630,13 +637,13 @@ export function registerReviewCommand(program) {
|
|
|
630
637
|
contentHash: ingestResolution.fingerprint?.contentHash || "",
|
|
631
638
|
};
|
|
632
639
|
}
|
|
633
|
-
const mode = resolveModeFromOptions(
|
|
634
|
-
defaultMode: String(
|
|
640
|
+
const mode = resolveModeFromOptions(actionOptions, {
|
|
641
|
+
defaultMode: String(actionOptions.mode || "full").trim().toLowerCase() || "full",
|
|
635
642
|
});
|
|
636
643
|
const scan = await runLocalReviewScan({
|
|
637
644
|
targetPath,
|
|
638
645
|
mode,
|
|
639
|
-
specFile:
|
|
646
|
+
specFile: actionOptions.spec,
|
|
640
647
|
});
|
|
641
648
|
|
|
642
649
|
const report = `# Local Review Scan
|
|
@@ -660,7 +667,7 @@ ${formatFindingsMarkdown(scan.findings)}
|
|
|
660
667
|
const reportPath = await writeReviewReport({
|
|
661
668
|
targetPath: scan.targetPath,
|
|
662
669
|
mode: scan.mode,
|
|
663
|
-
outputDir:
|
|
670
|
+
outputDir: actionOptions.outputDir,
|
|
664
671
|
reportMarkdown: report,
|
|
665
672
|
});
|
|
666
673
|
|