auditor-lambda 0.2.8 → 0.2.10
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 +6 -0
- package/audit-code-wrapper-lib.mjs +1 -1
- package/dist/adapters/eslint.js +9 -5
- package/dist/cli.d.ts +42 -1
- package/dist/cli.js +234 -63
- package/dist/extractors/bucketing.d.ts +4 -0
- package/dist/extractors/bucketing.js +6 -2
- package/dist/extractors/disposition.d.ts +4 -0
- package/dist/extractors/disposition.js +6 -2
- package/dist/extractors/fileInventory.js +24 -28
- package/dist/extractors/flows.d.ts +5 -0
- package/dist/extractors/flows.js +18 -38
- package/dist/extractors/pathPatterns.d.ts +10 -3
- package/dist/extractors/pathPatterns.js +109 -61
- package/dist/extractors/surfaces.d.ts +4 -0
- package/dist/extractors/surfaces.js +11 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/io/artifacts.d.ts +55 -40
- package/dist/io/artifacts.js +73 -110
- package/dist/io/json.js +52 -21
- package/dist/io/runArtifacts.d.ts +1 -1
- package/dist/io/runArtifacts.js +26 -3
- package/dist/orchestrator/advance.js +83 -62
- package/dist/orchestrator/autoFixExecutor.js +32 -15
- package/dist/orchestrator/flowCoverage.js +11 -5
- package/dist/orchestrator/flowPlanning.d.ts +7 -2
- package/dist/orchestrator/flowPlanning.js +46 -21
- package/dist/orchestrator/flowRequeue.js +28 -8
- package/dist/orchestrator/internalExecutors.js +12 -8
- package/dist/orchestrator/localCommands.d.ts +14 -0
- package/dist/orchestrator/localCommands.js +124 -0
- package/dist/orchestrator/planning.js +25 -3
- package/dist/orchestrator/requeue.js +11 -1
- package/dist/orchestrator/syntaxResolutionExecutor.js +60 -59
- package/dist/orchestrator/taskBuilder.d.ts +4 -2
- package/dist/orchestrator/taskBuilder.js +153 -52
- package/dist/orchestrator/unitBuilder.d.ts +3 -1
- package/dist/orchestrator/unitBuilder.js +24 -16
- package/dist/prompts/renderWorkerPrompt.d.ts +1 -1
- package/dist/prompts/renderWorkerPrompt.js +16 -8
- package/dist/providers/claudeCodeProvider.d.ts +4 -1
- package/dist/providers/claudeCodeProvider.js +8 -5
- package/dist/providers/localSubprocessProvider.d.ts +4 -0
- package/dist/providers/localSubprocessProvider.js +7 -2
- package/dist/providers/spawnLoggedCommand.d.ts +9 -1
- package/dist/providers/spawnLoggedCommand.js +77 -29
- package/dist/reporting/synthesis.d.ts +2 -0
- package/dist/reporting/synthesis.js +12 -9
- package/dist/supervisor/operatorHandoff.d.ts +1 -1
- package/dist/supervisor/operatorHandoff.js +56 -18
- package/dist/supervisor/runLedger.d.ts +1 -1
- package/dist/supervisor/runLedger.js +112 -5
- package/dist/supervisor/sessionConfig.js +10 -10
- package/dist/types/externalAnalyzer.d.ts +3 -0
- package/dist/types/flowCoverage.d.ts +5 -1
- package/dist/types/flowCoverage.js +5 -1
- package/dist/types/flows.d.ts +5 -1
- package/dist/types/flows.js +1 -1
- package/dist/types/runLedger.d.ts +5 -1
- package/dist/types/runLedger.js +6 -1
- package/dist/types/runtimeValidation.d.ts +12 -3
- package/dist/types/runtimeValidation.js +16 -1
- package/dist/types/sessionConfig.d.ts +15 -2
- package/dist/types/sessionConfig.js +15 -1
- package/dist/types/surfaces.d.ts +4 -1
- package/dist/types/surfaces.js +1 -1
- package/dist/types/workerSession.d.ts +9 -0
- package/dist/types/workerSession.js +5 -1
- package/dist/validation/artifacts.d.ts +1 -1
- package/dist/validation/artifacts.js +33 -20
- package/dist/validation/auditResults.d.ts +2 -2
- package/dist/validation/auditResults.js +7 -15
- package/dist/validation/basic.d.ts +9 -1
- package/dist/validation/basic.js +40 -3
- package/dist/validation/sessionConfig.d.ts +4 -2
- package/dist/validation/sessionConfig.js +62 -15
- package/docs/agent-integrations.md +31 -11
- package/docs/next-steps.md +21 -4
- package/docs/packaging.md +14 -0
- package/docs/product-direction.md +22 -0
- package/docs/production-launch-bar.md +2 -0
- package/docs/releasing.md +17 -0
- package/docs/remediation-baseline.md +75 -0
- package/docs/run-flow.md +23 -11
- package/docs/session-config.md +53 -6
- package/docs/supervisor.md +7 -0
- package/docs/workflow-refactor-brief.md +177 -0
- package/package.json +1 -1
- package/schemas/audit-code-v1alpha1.schema.json +1 -0
- package/schemas/audit_result.schema.json +4 -1
- package/schemas/audit_task.schema.json +3 -1
- package/schemas/coverage_matrix.schema.json +3 -3
- package/schemas/critical_flows.schema.json +6 -2
- package/schemas/file_disposition.schema.json +2 -2
- package/schemas/finding.schema.json +9 -4
- package/schemas/flow_coverage.schema.json +2 -2
- package/schemas/repo_manifest.schema.json +4 -4
- package/schemas/risk_register.schema.json +2 -2
- package/schemas/runtime_validation_report.schema.json +2 -2
- package/schemas/runtime_validation_tasks.schema.json +8 -2
- package/schemas/surface_manifest.schema.json +6 -3
- package/schemas/unit_manifest.schema.json +3 -2
- package/skills/audit-code/SKILL.md +5 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { requireKeys } from "./basic.js";
|
|
1
|
+
import { pushValidationIssue, requireKeys, } from "./basic.js";
|
|
2
2
|
function pushIssue(issues, path, message) {
|
|
3
|
-
issues
|
|
3
|
+
pushValidationIssue(issues, path, message);
|
|
4
|
+
}
|
|
5
|
+
function asArray(value) {
|
|
6
|
+
return Array.isArray(value) ? value : [];
|
|
4
7
|
}
|
|
5
8
|
export function validateArtifactBundle(bundle) {
|
|
6
9
|
const issues = [];
|
|
@@ -37,14 +40,24 @@ export function validateArtifactBundle(bundle) {
|
|
|
37
40
|
if (bundle.external_analyzer_results) {
|
|
38
41
|
issues.push(...requireKeys(bundle.external_analyzer_results, "external_analyzer_results", ["tool", "results"]));
|
|
39
42
|
}
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
43
|
+
const repoManifestFiles = asArray(bundle.repo_manifest?.files);
|
|
44
|
+
const fileDispositionEntries = asArray(bundle.file_disposition?.files);
|
|
45
|
+
const unitManifestUnits = asArray(bundle.unit_manifest?.units);
|
|
46
|
+
const criticalFlows = asArray(bundle.critical_flows?.flows);
|
|
47
|
+
const flowCoverageEntries = asArray(bundle.flow_coverage?.flows);
|
|
48
|
+
const riskRegisterItems = asArray(bundle.risk_register?.items);
|
|
49
|
+
const surfaceEntries = asArray(bundle.surface_manifest?.surfaces);
|
|
50
|
+
const runtimeValidationTasks = asArray(bundle.runtime_validation_tasks?.tasks);
|
|
51
|
+
const runtimeValidationResults = asArray(bundle.runtime_validation_report?.results);
|
|
52
|
+
const externalAnalyzerResults = asArray(bundle.external_analyzer_results?.results);
|
|
53
|
+
const coverageFiles = asArray(bundle.coverage_matrix?.files);
|
|
54
|
+
const repoPaths = new Set(repoManifestFiles.map((file) => file.path));
|
|
55
|
+
const dispositionMap = new Map(fileDispositionEntries.map((item) => [item.path, item.status]));
|
|
56
|
+
const unitIds = new Set(unitManifestUnits.map((unit) => unit.unit_id));
|
|
57
|
+
const flowIds = new Set(criticalFlows.map((flow) => flow.id));
|
|
58
|
+
const runtimeTaskIds = new Set(runtimeValidationTasks.map((task) => task.id));
|
|
46
59
|
if (bundle.repo_manifest && bundle.coverage_matrix) {
|
|
47
|
-
const coveragePaths = new Set(
|
|
60
|
+
const coveragePaths = new Set(coverageFiles.map((file) => file.path));
|
|
48
61
|
for (const path of repoPaths) {
|
|
49
62
|
if (!coveragePaths.has(path)) {
|
|
50
63
|
pushIssue(issues, "coverage_matrix", `Missing coverage entry for ${path}`);
|
|
@@ -52,7 +65,7 @@ export function validateArtifactBundle(bundle) {
|
|
|
52
65
|
}
|
|
53
66
|
}
|
|
54
67
|
if (bundle.repo_manifest && bundle.file_disposition) {
|
|
55
|
-
const dispositionPaths = new Set(
|
|
68
|
+
const dispositionPaths = new Set(fileDispositionEntries.map((file) => file.path));
|
|
56
69
|
for (const path of repoPaths) {
|
|
57
70
|
if (!dispositionPaths.has(path)) {
|
|
58
71
|
pushIssue(issues, "file_disposition", `Missing disposition entry for ${path}`);
|
|
@@ -60,7 +73,7 @@ export function validateArtifactBundle(bundle) {
|
|
|
60
73
|
}
|
|
61
74
|
}
|
|
62
75
|
if (bundle.unit_manifest) {
|
|
63
|
-
for (const unit of
|
|
76
|
+
for (const unit of unitManifestUnits) {
|
|
64
77
|
if (unit.files.length === 0) {
|
|
65
78
|
pushIssue(issues, `unit_manifest:${unit.unit_id}`, "Unit has no files");
|
|
66
79
|
}
|
|
@@ -79,7 +92,7 @@ export function validateArtifactBundle(bundle) {
|
|
|
79
92
|
}
|
|
80
93
|
}
|
|
81
94
|
if (bundle.coverage_matrix && bundle.unit_manifest) {
|
|
82
|
-
for (const file of
|
|
95
|
+
for (const file of coverageFiles) {
|
|
83
96
|
if (!repoPaths.has(file.path)) {
|
|
84
97
|
pushIssue(issues, "coverage_matrix", `Coverage contains unknown file ${file.path}`);
|
|
85
98
|
}
|
|
@@ -103,7 +116,7 @@ export function validateArtifactBundle(bundle) {
|
|
|
103
116
|
}
|
|
104
117
|
}
|
|
105
118
|
if (bundle.critical_flows) {
|
|
106
|
-
for (const flow of
|
|
119
|
+
for (const flow of criticalFlows) {
|
|
107
120
|
if (flow.paths.length === 0) {
|
|
108
121
|
pushIssue(issues, `critical_flows:${flow.id}`, "Flow has no paths");
|
|
109
122
|
}
|
|
@@ -122,7 +135,7 @@ export function validateArtifactBundle(bundle) {
|
|
|
122
135
|
}
|
|
123
136
|
}
|
|
124
137
|
if (bundle.flow_coverage && bundle.critical_flows) {
|
|
125
|
-
for (const flow of
|
|
138
|
+
for (const flow of flowCoverageEntries) {
|
|
126
139
|
if (!flowIds.has(flow.flow_id)) {
|
|
127
140
|
pushIssue(issues, `flow_coverage:${flow.flow_id}`, `Flow coverage references unknown flow ${flow.flow_id}`);
|
|
128
141
|
}
|
|
@@ -143,15 +156,15 @@ export function validateArtifactBundle(bundle) {
|
|
|
143
156
|
}
|
|
144
157
|
}
|
|
145
158
|
if (bundle.risk_register && bundle.unit_manifest) {
|
|
146
|
-
const riskUnitIds = new Set(
|
|
147
|
-
for (const unit of
|
|
159
|
+
const riskUnitIds = new Set(riskRegisterItems.map((item) => item.unit_id));
|
|
160
|
+
for (const unit of unitManifestUnits) {
|
|
148
161
|
if (!riskUnitIds.has(unit.unit_id)) {
|
|
149
162
|
pushIssue(issues, "risk_register", `Missing risk entry for unit ${unit.unit_id}`);
|
|
150
163
|
}
|
|
151
164
|
}
|
|
152
165
|
}
|
|
153
166
|
if (bundle.surface_manifest) {
|
|
154
|
-
for (const surface of
|
|
167
|
+
for (const surface of surfaceEntries) {
|
|
155
168
|
if (!repoPaths.has(surface.entrypoint)) {
|
|
156
169
|
pushIssue(issues, `surface_manifest:${surface.id}`, `Surface references unknown entrypoint ${surface.entrypoint}`);
|
|
157
170
|
}
|
|
@@ -162,7 +175,7 @@ export function validateArtifactBundle(bundle) {
|
|
|
162
175
|
}
|
|
163
176
|
}
|
|
164
177
|
if (bundle.runtime_validation_tasks) {
|
|
165
|
-
for (const task of
|
|
178
|
+
for (const task of runtimeValidationTasks) {
|
|
166
179
|
if (task.target_paths.length === 0) {
|
|
167
180
|
pushIssue(issues, `runtime_validation_tasks:${task.id}`, "Runtime validation task has no target paths");
|
|
168
181
|
}
|
|
@@ -174,14 +187,14 @@ export function validateArtifactBundle(bundle) {
|
|
|
174
187
|
}
|
|
175
188
|
}
|
|
176
189
|
if (bundle.runtime_validation_report) {
|
|
177
|
-
for (const result of
|
|
190
|
+
for (const result of runtimeValidationResults) {
|
|
178
191
|
if (!runtimeTaskIds.has(result.task_id)) {
|
|
179
192
|
pushIssue(issues, `runtime_validation_report:${result.task_id}`, `Runtime validation result references unknown task ${result.task_id}`);
|
|
180
193
|
}
|
|
181
194
|
}
|
|
182
195
|
}
|
|
183
196
|
if (bundle.external_analyzer_results) {
|
|
184
|
-
for (const item of
|
|
197
|
+
for (const item of externalAnalyzerResults) {
|
|
185
198
|
if (!repoPaths.has(item.path) && bundle.repo_manifest) {
|
|
186
199
|
pushIssue(issues, `external_analyzer_results:${item.id}`, `External analyzer result references unknown path ${item.path}`);
|
|
187
200
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { AuditTask } from "../types.js";
|
|
2
|
+
import { type ValidationIssue } from "./basic.js";
|
|
2
3
|
export type IssueSeverity = "error" | "warning";
|
|
3
|
-
export interface AuditResultIssue {
|
|
4
|
+
export interface AuditResultIssue extends ValidationIssue {
|
|
4
5
|
result_index: number;
|
|
5
6
|
task_id: string;
|
|
6
7
|
severity: IssueSeverity;
|
|
7
8
|
field: string;
|
|
8
|
-
message: string;
|
|
9
9
|
}
|
|
10
10
|
export interface ValidateAuditResultOptions {
|
|
11
11
|
lineIndex?: Record<string, number>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describeValue, formatValidationIssues, isRecord, } from "./basic.js";
|
|
1
2
|
const REQUIRED_FINDING_FIELDS = [
|
|
2
3
|
"id",
|
|
3
4
|
"title",
|
|
@@ -24,21 +25,10 @@ const VALID_LENSES = new Set([
|
|
|
24
25
|
function pushIssue(issues, params) {
|
|
25
26
|
issues.push({
|
|
26
27
|
...params,
|
|
28
|
+
path: params.path ?? params.field,
|
|
27
29
|
severity: params.severity ?? "error",
|
|
28
30
|
});
|
|
29
31
|
}
|
|
30
|
-
function describeValue(value) {
|
|
31
|
-
if (Array.isArray(value)) {
|
|
32
|
-
return "array";
|
|
33
|
-
}
|
|
34
|
-
if (value === null) {
|
|
35
|
-
return "null";
|
|
36
|
-
}
|
|
37
|
-
return typeof value;
|
|
38
|
-
}
|
|
39
|
-
function isRecord(value) {
|
|
40
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
|
-
}
|
|
42
32
|
function isNonEmptyString(value) {
|
|
43
33
|
return typeof value === "string" && value.trim().length > 0;
|
|
44
34
|
}
|
|
@@ -404,7 +394,9 @@ export function validateAuditResults(results, tasks, options = {}) {
|
|
|
404
394
|
return issues;
|
|
405
395
|
}
|
|
406
396
|
export function formatAuditResultIssues(issues) {
|
|
407
|
-
return issues
|
|
408
|
-
|
|
409
|
-
.
|
|
397
|
+
return formatValidationIssues(issues.map((issue) => ({
|
|
398
|
+
path: `${issue.task_id} / ${issue.field}`,
|
|
399
|
+
message: issue.message,
|
|
400
|
+
severity: issue.severity,
|
|
401
|
+
})));
|
|
410
402
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
export type ValidationSeverity = "error" | "warning";
|
|
1
2
|
export interface ValidationIssue {
|
|
2
3
|
path: string;
|
|
3
4
|
message: string;
|
|
5
|
+
severity: ValidationSeverity;
|
|
4
6
|
}
|
|
5
|
-
export declare function
|
|
7
|
+
export declare function describeValue(value: unknown): string;
|
|
8
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
9
|
+
export declare function createValidationIssue(path: string, message: string, severity?: ValidationSeverity): ValidationIssue;
|
|
10
|
+
export declare function pushValidationIssue(issues: ValidationIssue[], path: string, message: string, severity?: ValidationSeverity): void;
|
|
11
|
+
export declare function prefixValidationIssues(prefix: string, issues: ValidationIssue[]): ValidationIssue[];
|
|
12
|
+
export declare function formatValidationIssues(issues: ValidationIssue[]): string;
|
|
13
|
+
export declare function requireKeys(value: unknown, path: string, keys: readonly string[]): ValidationIssue[];
|
package/dist/validation/basic.js
CHANGED
|
@@ -1,8 +1,45 @@
|
|
|
1
|
-
export function
|
|
1
|
+
export function describeValue(value) {
|
|
2
|
+
if (Array.isArray(value)) {
|
|
3
|
+
return "array";
|
|
4
|
+
}
|
|
5
|
+
if (value === null) {
|
|
6
|
+
return "null";
|
|
7
|
+
}
|
|
8
|
+
return typeof value;
|
|
9
|
+
}
|
|
10
|
+
export function isRecord(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
export function createValidationIssue(path, message, severity = "error") {
|
|
14
|
+
return { path, message, severity };
|
|
15
|
+
}
|
|
16
|
+
export function pushValidationIssue(issues, path, message, severity = "error") {
|
|
17
|
+
issues.push(createValidationIssue(path, message, severity));
|
|
18
|
+
}
|
|
19
|
+
export function prefixValidationIssues(prefix, issues) {
|
|
20
|
+
return issues.map((issue) => ({
|
|
21
|
+
...issue,
|
|
22
|
+
path: issue.path.length === 0
|
|
23
|
+
? prefix
|
|
24
|
+
: issue.path === prefix || issue.path.startsWith(`${prefix}.`)
|
|
25
|
+
? issue.path
|
|
26
|
+
: `${prefix}.${issue.path}`,
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
export function formatValidationIssues(issues) {
|
|
30
|
+
return issues
|
|
31
|
+
.map((issue) => ` [${issue.severity}] ${issue.path}: ${issue.message}`)
|
|
32
|
+
.join("\n");
|
|
33
|
+
}
|
|
34
|
+
export function requireKeys(value, path, keys) {
|
|
2
35
|
const issues = [];
|
|
36
|
+
if (!isRecord(value)) {
|
|
37
|
+
pushValidationIssue(issues, path, `Expected an object, got ${describeValue(value)}.`);
|
|
38
|
+
return issues;
|
|
39
|
+
}
|
|
3
40
|
for (const key of keys) {
|
|
4
|
-
if (!(key in
|
|
5
|
-
issues
|
|
41
|
+
if (!(key in value)) {
|
|
42
|
+
pushValidationIssue(issues, path, `Missing required key: ${key}`);
|
|
6
43
|
}
|
|
7
44
|
}
|
|
8
45
|
return issues;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
1
|
+
import { type SessionConfig } from "../types/sessionConfig.js";
|
|
2
|
+
import { type ValidationIssue } from "./basic.js";
|
|
3
3
|
export declare function validateSessionConfig(value: unknown): ValidationIssue[];
|
|
4
4
|
export declare function validateConfiguredProviderEnvironment(sessionConfig: SessionConfig, options?: {
|
|
5
5
|
commandExists?: (command: string) => boolean;
|
|
6
|
+
pathExists?: (commandPath: string) => boolean;
|
|
6
7
|
}): ValidationIssue[];
|
|
8
|
+
export { formatValidationIssues } from "./basic.js";
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"opencode",
|
|
8
|
-
"vscode-task",
|
|
9
|
-
]);
|
|
10
|
-
const VALID_UI_MODES = new Set(["headless", "visible"]);
|
|
2
|
+
import { accessSync, constants } from "node:fs";
|
|
3
|
+
import { PROVIDER_NAMES, SESSION_UI_MODES, } from "../types/sessionConfig.js";
|
|
4
|
+
import { isRecord, pushValidationIssue, } from "./basic.js";
|
|
5
|
+
const VALID_PROVIDERS = new Set(PROVIDER_NAMES);
|
|
6
|
+
const VALID_UI_MODES = new Set(SESSION_UI_MODES);
|
|
11
7
|
function pushIssue(issues, path, message) {
|
|
12
|
-
issues
|
|
13
|
-
}
|
|
14
|
-
function isRecord(value) {
|
|
15
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
pushValidationIssue(issues, path, message);
|
|
16
9
|
}
|
|
17
10
|
function validateStringArray(value, path, label, issues, options = {}) {
|
|
18
11
|
if (!Array.isArray(value)) {
|
|
@@ -74,6 +67,9 @@ function validateAgentProviderSection(value, path, issues) {
|
|
|
74
67
|
if (typeof value.command !== "string" || value.command.trim().length === 0) {
|
|
75
68
|
pushIssue(issues, `${path}.command`, "command must be a non-empty string when provided.");
|
|
76
69
|
}
|
|
70
|
+
else if (!isSupportedConfiguredCommand(value.command)) {
|
|
71
|
+
pushIssue(issues, `${path}.command`, "command must be a bare executable name or direct executable path. Put CLI flags in extra_args.");
|
|
72
|
+
}
|
|
77
73
|
}
|
|
78
74
|
if (value.extra_args !== undefined) {
|
|
79
75
|
validateStringArray(value.extra_args, `${path}.extra_args`, "extra_args", issues, { allowEmptyArray: true });
|
|
@@ -84,6 +80,43 @@ function commandExists(command) {
|
|
|
84
80
|
const result = spawnSync(lookupCommand, [command], { stdio: "ignore" });
|
|
85
81
|
return result.status === 0;
|
|
86
82
|
}
|
|
83
|
+
function configuredPathExists(commandPath) {
|
|
84
|
+
try {
|
|
85
|
+
accessSync(commandPath, constants.F_OK);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function startsWithPathPrefix(command) {
|
|
93
|
+
return (command.startsWith(".") ||
|
|
94
|
+
command.startsWith("/") ||
|
|
95
|
+
command.startsWith("\\\\") ||
|
|
96
|
+
/^[A-Za-z]:[\\/]/.test(command));
|
|
97
|
+
}
|
|
98
|
+
function containsForbiddenCommandSyntax(command) {
|
|
99
|
+
return /[\r\n"'`|&;<>]/.test(command);
|
|
100
|
+
}
|
|
101
|
+
function isBareExecutableName(command) {
|
|
102
|
+
return (command.length > 0 &&
|
|
103
|
+
!/\s/.test(command) &&
|
|
104
|
+
!containsForbiddenCommandSyntax(command) &&
|
|
105
|
+
!/[\\/]/.test(command) &&
|
|
106
|
+
!/^[A-Za-z]:/.test(command));
|
|
107
|
+
}
|
|
108
|
+
function isDirectExecutablePath(command) {
|
|
109
|
+
return (command.length > 0 &&
|
|
110
|
+
!containsForbiddenCommandSyntax(command) &&
|
|
111
|
+
startsWithPathPrefix(command));
|
|
112
|
+
}
|
|
113
|
+
function isSupportedConfiguredCommand(command) {
|
|
114
|
+
const trimmed = command.trim();
|
|
115
|
+
if (trimmed.length === 0 || trimmed !== command) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return isBareExecutableName(trimmed) || isDirectExecutablePath(trimmed);
|
|
119
|
+
}
|
|
87
120
|
export function validateSessionConfig(value) {
|
|
88
121
|
const issues = [];
|
|
89
122
|
if (value === undefined) {
|
|
@@ -122,18 +155,32 @@ export function validateSessionConfig(value) {
|
|
|
122
155
|
export function validateConfiguredProviderEnvironment(sessionConfig, options = {}) {
|
|
123
156
|
const issues = [];
|
|
124
157
|
const lookupCommand = options.commandExists ?? commandExists;
|
|
158
|
+
const lookupPath = options.pathExists ?? configuredPathExists;
|
|
125
159
|
const provider = sessionConfig.provider ?? "local-subprocess";
|
|
126
160
|
if (provider === "claude-code") {
|
|
127
161
|
const command = sessionConfig.claude_code?.command ?? "claude";
|
|
128
|
-
if (!lookupCommand(command)) {
|
|
162
|
+
if (isBareExecutableName(command) && !lookupCommand(command)) {
|
|
129
163
|
pushIssue(issues, "claude_code.command", `Configured claude-code executable was not found on PATH: ${command}.`);
|
|
130
164
|
}
|
|
165
|
+
else if (isDirectExecutablePath(command) && !lookupPath(command)) {
|
|
166
|
+
pushIssue(issues, "claude_code.command", `Configured claude-code executable path does not exist: ${command}.`);
|
|
167
|
+
}
|
|
168
|
+
else if (!isSupportedConfiguredCommand(command)) {
|
|
169
|
+
pushIssue(issues, "claude_code.command", "Configured claude-code command must be a bare executable name or direct path. Put CLI flags in extra_args.");
|
|
170
|
+
}
|
|
131
171
|
}
|
|
132
172
|
if (provider === "opencode") {
|
|
133
173
|
const command = sessionConfig.opencode?.command ?? "opencode";
|
|
134
|
-
if (!lookupCommand(command)) {
|
|
174
|
+
if (isBareExecutableName(command) && !lookupCommand(command)) {
|
|
135
175
|
pushIssue(issues, "opencode.command", `Configured opencode executable was not found on PATH: ${command}.`);
|
|
136
176
|
}
|
|
177
|
+
else if (isDirectExecutablePath(command) && !lookupPath(command)) {
|
|
178
|
+
pushIssue(issues, "opencode.command", `Configured opencode executable path does not exist: ${command}.`);
|
|
179
|
+
}
|
|
180
|
+
else if (!isSupportedConfiguredCommand(command)) {
|
|
181
|
+
pushIssue(issues, "opencode.command", "Configured opencode command must be a bare executable name or direct path. Put CLI flags in extra_args.");
|
|
182
|
+
}
|
|
137
183
|
}
|
|
138
184
|
return issues;
|
|
139
185
|
}
|
|
186
|
+
export { formatValidationIssues } from "./basic.js";
|
|
@@ -14,6 +14,17 @@ Normal product usage should:
|
|
|
14
14
|
- avoid manual `--root`, provider flags, and model selection in normal use
|
|
15
15
|
- let the supervisor advance the audit automatically until it completes or no further automatic progress is possible
|
|
16
16
|
|
|
17
|
+
## Review ownership rule
|
|
18
|
+
|
|
19
|
+
Semantic review should stay with the active conversation agent by default.
|
|
20
|
+
|
|
21
|
+
That means:
|
|
22
|
+
|
|
23
|
+
- use the current host conversation as the normal owner of review work
|
|
24
|
+
- if the host agent can delegate to subagents in parallel, let the host runtime make that decision
|
|
25
|
+
- do not treat `.audit-artifacts/session-config.json` as the normal way to choose a second LLM for review
|
|
26
|
+
- treat backend provider adapters as compatibility bridges for fallback CLI usage only
|
|
27
|
+
|
|
17
28
|
## Conversation-first setup
|
|
18
29
|
|
|
19
30
|
The canonical prompt asset is:
|
|
@@ -136,7 +147,11 @@ Terminal interpretation:
|
|
|
136
147
|
- `audit_state.status === "complete"` means the audit finished end to end.
|
|
137
148
|
- `audit_state.status === "blocked"` means the wrapper exhausted automatic work and the remaining review still needs imported results or a provider-capable continuation path.
|
|
138
149
|
|
|
139
|
-
|
|
150
|
+
Current implementation note:
|
|
151
|
+
|
|
152
|
+
- the backend fallback still supports explicit provider bridges such as `claude-code`, `opencode`, `subprocess-template`, and `vscode-task`
|
|
153
|
+
- those bridges are compatibility modes, not the intended default review owner
|
|
154
|
+
- the intended long-term workflow is documented in [docs/workflow-refactor-brief.md](/C:/Code/auditor-lambda/docs/workflow-refactor-brief.md)
|
|
140
155
|
|
|
141
156
|
When additional evidence exists, pass it into the same wrapper:
|
|
142
157
|
|
|
@@ -149,6 +164,7 @@ audit-code --external-analyzer-results /path/to/external_analyzer_results.json
|
|
|
149
164
|
Each response also refreshes `.audit-artifacts/operator-handoff.json` and `.audit-artifacts/operator-handoff.md` so operators can see the pending obligations, suggested import paths, and session-config continuation hint without reconstructing the state manually.
|
|
150
165
|
|
|
151
166
|
Everything below is backend fallback guidance, not the primary product path.
|
|
167
|
+
Use it when the current host cannot keep review inside the active conversation, not as the first choice for semantic-review ownership.
|
|
152
168
|
|
|
153
169
|
## Provider matrix
|
|
154
170
|
|
|
@@ -156,9 +172,9 @@ Everything below is backend fallback guidance, not the primary product path.
|
|
|
156
172
|
|
|
157
173
|
Use when you want the supervisor to stay entirely local.
|
|
158
174
|
|
|
159
|
-
This requires no external agent CLI.
|
|
175
|
+
This requires no external agent CLI. Deterministic executors run in-process during normal wrapper runs, and the supervisor only stops once the remaining work is genuinely semantic review.
|
|
160
176
|
|
|
161
|
-
When
|
|
177
|
+
When that review boundary is reached, `local-subprocess` stops in a terminal blocked handoff instead of pretending more automatic progress is available. Use `--results <file>` for a single batch or `--batch-results <dir>` when the active conversation agent reviewed multiple task batches before ingestion.
|
|
162
178
|
|
|
163
179
|
This is the safest default backend when the repository is already available locally.
|
|
164
180
|
|
|
@@ -166,19 +182,17 @@ This is the safest default backend when the repository is already available loca
|
|
|
166
182
|
|
|
167
183
|
Use when Claude Code is installed and authenticated on the machine.
|
|
168
184
|
|
|
169
|
-
The
|
|
170
|
-
When audit-task review is pending, the provider prompt now asks Claude Code to write structured audit results and then hand back to the bounded worker command so the same wrapper invocation can continue.
|
|
185
|
+
The current implementation can launch a fresh Claude Code print-mode session for each worker run.
|
|
171
186
|
|
|
172
|
-
|
|
187
|
+
Treat this as a compatibility bridge only, not as the intended default review owner.
|
|
173
188
|
|
|
174
189
|
### opencode
|
|
175
190
|
|
|
176
191
|
Use when OpenCode is installed and authenticated on the machine.
|
|
177
192
|
|
|
178
|
-
The
|
|
179
|
-
When audit-task review is pending, the provider prompt now asks OpenCode to write structured audit results and then hand back to the bounded worker command so the same wrapper invocation can continue.
|
|
193
|
+
The current implementation can launch a fresh `opencode run ...` session for each worker run.
|
|
180
194
|
|
|
181
|
-
|
|
195
|
+
Treat this as a compatibility bridge only, not as the intended default review owner.
|
|
182
196
|
|
|
183
197
|
### subprocess-template
|
|
184
198
|
|
|
@@ -195,11 +209,15 @@ Treat this as an advanced backend adapter rather than the default path.
|
|
|
195
209
|
|
|
196
210
|
### Claude Code
|
|
197
211
|
|
|
198
|
-
Use
|
|
212
|
+
Use `/audit-code` in the active conversation as the primary path.
|
|
213
|
+
|
|
214
|
+
Only use the repo-local `audit-code` wrapper with `provider: "claude-code"` in `.audit-artifacts/session-config.json` when you intentionally want backend fallback bridging into Claude Code.
|
|
199
215
|
|
|
200
216
|
### OpenCode
|
|
201
217
|
|
|
202
|
-
Use
|
|
218
|
+
Use `/audit-code` in the active conversation as the primary path.
|
|
219
|
+
|
|
220
|
+
Only use the repo-local `audit-code` wrapper with `provider: "opencode"` when you intentionally want backend fallback bridging into OpenCode.
|
|
203
221
|
|
|
204
222
|
### VS Code
|
|
205
223
|
|
|
@@ -249,3 +267,5 @@ For a polished operator experience today:
|
|
|
249
267
|
3. use `audit-code` as the repo-local backend fallback
|
|
250
268
|
4. prefer `local-subprocess` unless you want interactive review to continue automatically through agent tasks
|
|
251
269
|
5. use `subprocess-template` only when integrating a non-native editor or launcher surface
|
|
270
|
+
|
|
271
|
+
If you intentionally want the backend fallback to bridge semantic review into another process, re-run with an explicit `--provider` flag after configuring the matching section in `.audit-artifacts/session-config.json`.
|
package/docs/next-steps.md
CHANGED
|
@@ -36,7 +36,24 @@ That means the current release is suitable for a controlled alpha or beta skill-
|
|
|
36
36
|
|
|
37
37
|
## Near-term priorities
|
|
38
38
|
|
|
39
|
-
### 1.
|
|
39
|
+
### 1. Realign review dispatch with the conversation-owned workflow
|
|
40
|
+
|
|
41
|
+
The highest-priority product refactor is to move semantic-review ownership back to the active conversation agent and to replace the current unit-first review fan-out with non-overlapping lens-aware review blocks.
|
|
42
|
+
|
|
43
|
+
Near-term work should focus on:
|
|
44
|
+
|
|
45
|
+
- making the active conversation agent the default owner of semantic review
|
|
46
|
+
- keeping `agent_task_batch_size` at one review block per task
|
|
47
|
+
- treating backend provider adapters as compatibility bridges rather than the default review owner
|
|
48
|
+
- replacing the current unit-first task planner with a non-overlapping lens-block planner
|
|
49
|
+
- deleting the stale audit state and rerunning the audit only after that refactor lands
|
|
50
|
+
|
|
51
|
+
The current handoff for this work is:
|
|
52
|
+
|
|
53
|
+
- `docs/workflow-refactor-brief.md`
|
|
54
|
+
- `docs/remediation-baseline.md`
|
|
55
|
+
|
|
56
|
+
### 2. Verify the shipped host integrations end to end
|
|
40
57
|
|
|
41
58
|
The biggest remaining gap is not raw feature presence anymore. It is host-by-host proof that the generated assets work in the actual products they target.
|
|
42
59
|
|
|
@@ -48,7 +65,7 @@ Near-term work should focus on:
|
|
|
48
65
|
- validating the VS Code prompt, agent, and `.vscode/mcp.json` flow inside a real workspace
|
|
49
66
|
- validating that the Antigravity planning-mode guidance is accurate and does not over-promise a native saved-workflow surface
|
|
50
67
|
|
|
51
|
-
###
|
|
68
|
+
### 3. Close the remaining host-native UX gaps
|
|
52
69
|
|
|
53
70
|
The product goal is still conversational first, not fallback-CLI first, and some shipped surfaces are still guidance-heavy rather than truly native.
|
|
54
71
|
|
|
@@ -59,7 +76,7 @@ Near-term work should focus on:
|
|
|
59
76
|
- deciding whether OpenCode and VS Code need any smaller UX refinements after smoke-testing, rather than assuming the first generated surfaces are final
|
|
60
77
|
- keeping Antigravity framed as a workflow-and-artifacts host until Google documents a stable project-local config surface
|
|
61
78
|
|
|
62
|
-
###
|
|
79
|
+
### 4. Polish continuation through assisted review
|
|
63
80
|
|
|
64
81
|
The repo-local backend fallback still intentionally stops in blocked state under `local-subprocess`, but configured provider bridges can now continue the audit-task review phase automatically.
|
|
65
82
|
|
|
@@ -69,7 +86,7 @@ Near-term work should focus on:
|
|
|
69
86
|
- less operator guesswork when a configured provider fails to return usable results
|
|
70
87
|
- stronger host-specific guidance for provider-assisted bridges
|
|
71
88
|
|
|
72
|
-
###
|
|
89
|
+
### 5. Harden publish and release operations
|
|
73
90
|
|
|
74
91
|
The packaged install story is in place, but release operations still need finishing work.
|
|
75
92
|
|
package/docs/packaging.md
CHANGED
|
@@ -31,6 +31,18 @@ npm install
|
|
|
31
31
|
npm run verify:release
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
For live child-process output during packaged smoke debugging:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
AUDIT_CODE_VERBOSE=1 npm run smoke:packaged-audit-code
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
For the linked-install variant:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
AUDIT_CODE_VERBOSE=1 npm run smoke:linked-audit-code
|
|
44
|
+
```
|
|
45
|
+
|
|
34
46
|
That script:
|
|
35
47
|
|
|
36
48
|
- creates a tarball with `npm pack`
|
|
@@ -41,6 +53,8 @@ That script:
|
|
|
41
53
|
- invokes the installed `audit-code` binary from `node_modules/.bin`
|
|
42
54
|
- validates emitted JSON against `schemas/audit-code-v1alpha1.schema.json`
|
|
43
55
|
- exercises the same evidence-import and completion flow used by the linked-command smoke test
|
|
56
|
+
- strips inherited `npm_config_*`, `NODE_AUTH_TOKEN`, and `NPM_TOKEN` values before nested npm operations so `npm publish --dry-run` does not accidentally suppress tarball generation or leak publish credentials into the smoke environment
|
|
57
|
+
- emits step-by-step progress plus a final success summary so CI logs show where the run failed or completed across both the linked and packaged smoke paths
|
|
44
58
|
|
|
45
59
|
## CI
|
|
46
60
|
|
|
@@ -78,11 +78,32 @@ For repo-local backend usage:
|
|
|
78
78
|
- `provider: "auto"` is the explicit opt-in mode for best-effort routing across configured or detected backends
|
|
79
79
|
- explicit provider names should remain available for operators who want a specific backend
|
|
80
80
|
|
|
81
|
+
## Semantic review ownership
|
|
82
|
+
|
|
83
|
+
The intended semantic-review owner is the active conversation agent.
|
|
84
|
+
|
|
85
|
+
That means:
|
|
86
|
+
|
|
87
|
+
- `/audit-code` should hand review work to the current conversation or host agent session first
|
|
88
|
+
- if that active agent can delegate to subagents in parallel, that fan-out belongs to the host runtime rather than to repo-local backend defaults
|
|
89
|
+
- backend provider adapters are compatibility bridges for fallback CLI usage, not the default owner of review work
|
|
90
|
+
- session-config should not be the normal mechanism for redirecting semantic review into a second external LLM CLI
|
|
91
|
+
|
|
92
|
+
## Task-planning rule
|
|
93
|
+
|
|
94
|
+
The intended review planner should:
|
|
95
|
+
|
|
96
|
+
- determine which files require which lenses
|
|
97
|
+
- partition unresolved review into non-overlapping review blocks
|
|
98
|
+
- prefer lens-homogeneous blocks when practical
|
|
99
|
+
- keep the default dispatch granularity to one review block per task
|
|
100
|
+
|
|
81
101
|
## Default context & model rules
|
|
82
102
|
|
|
83
103
|
1. By default, the current ChatGPT project conversation and its files should be treated as the primary context.
|
|
84
104
|
2. The user should not need to supply `--root`, provider names, or backend-specific settings in normal usage.
|
|
85
105
|
3. For backend CLI delegation, let the chosen provider own its own model-selection behavior unless explicitly configured.
|
|
106
|
+
4. Backend fallback settings should not cap the active conversation agent's own subagent parallelism model.
|
|
86
107
|
|
|
87
108
|
## Development rule
|
|
88
109
|
|
|
@@ -92,6 +113,7 @@ Future development should optimize for the native skill UX first:
|
|
|
92
113
|
- packaged installs should help users reach the prompt asset for that conversation route
|
|
93
114
|
- the CLI and supervisor are implementation details and fallback harnesses
|
|
94
115
|
- provider adapters are backend internals, not the primary product concept
|
|
116
|
+
- semantic-review dispatch belongs to the active conversation agent, not to an externally spawned fallback provider by default
|
|
95
117
|
- docs, tests, and examples should present the skill-first flow before any CLI flow
|
|
96
118
|
|
|
97
119
|
If documentation or implementation details conflict, prefer the skill-first contract above over the CLI-first backend shape.
|
|
@@ -20,6 +20,8 @@ Anything below `dist/index.js` remains a backend or development interface rather
|
|
|
20
20
|
### Node and package runtime
|
|
21
21
|
|
|
22
22
|
- Node `>=20` is the minimum supported runtime from `package.json`
|
|
23
|
+
- GitHub Actions currently exercises the test suite on Node `20` and Node `22`
|
|
24
|
+
- release and publish automation stays pinned to Node `22.14.0`
|
|
23
25
|
- packaged installs must include:
|
|
24
26
|
- `audit-code`
|
|
25
27
|
- `audit-code-wrapper-lib.mjs`
|
package/docs/releasing.md
CHANGED
|
@@ -13,7 +13,9 @@ That workflow already:
|
|
|
13
13
|
- pins Node `22.14.0`
|
|
14
14
|
- upgrades npm to `>=11.5.1`
|
|
15
15
|
- runs `npm run verify:release` before any publish attempt
|
|
16
|
+
- previews the packed tarball with `npm pack --dry-run` before any live publish attempt
|
|
16
17
|
- defaults semver prerelease versions to the `next` dist-tag unless manual dispatch overrides it
|
|
18
|
+
- uploads npm debug logs when a publish-path step fails
|
|
17
19
|
|
|
18
20
|
## One-time npm setup
|
|
19
21
|
|
|
@@ -39,6 +41,12 @@ npm publish --dry-run
|
|
|
39
41
|
|
|
40
42
|
The local dry run is still valuable even though the real publish happens in GitHub Actions. It proves the packed artifact, lifecycle hooks, and publish metadata before you spend a release on a broken workflow run.
|
|
41
43
|
|
|
44
|
+
## Supported Node lines
|
|
45
|
+
|
|
46
|
+
Routine CI currently exercises the repository on Node `20` and Node `22`.
|
|
47
|
+
|
|
48
|
+
Release and publish workflows stay pinned to Node `22.14.0` because that is the line this repository uses for npm Trusted Publishing and release smoke verification.
|
|
49
|
+
|
|
42
50
|
## GitHub release paths
|
|
43
51
|
|
|
44
52
|
### Manual dry run
|
|
@@ -68,6 +76,15 @@ This is the safest path for the first trusted-publishing release because it lets
|
|
|
68
76
|
|
|
69
77
|
After the manual path is proven, publishing a GitHub Release will trigger the same workflow and publish the package.
|
|
70
78
|
|
|
79
|
+
## Workflow troubleshooting
|
|
80
|
+
|
|
81
|
+
If a GitHub Actions run fails:
|
|
82
|
+
|
|
83
|
+
1. download the uploaded `*-npm-logs` artifact from the failed run
|
|
84
|
+
2. rerun `npm ci` and `npm run verify:release` locally from the same commit
|
|
85
|
+
3. for publish failures, rerun `publish-package.yml` with `dry_run=true` before retrying a live publish
|
|
86
|
+
4. if publish still fails, confirm npm Trusted Publishing is still configured for `publish-package.yml`
|
|
87
|
+
|
|
71
88
|
## Post-publish checks
|
|
72
89
|
|
|
73
90
|
After a live publish, verify the result from a clean shell:
|