auditor-lambda 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.md +173 -0
  2. package/audit-code-wrapper-lib.mjs +905 -0
  3. package/audit-code.mjs +13 -0
  4. package/dist/adapters/coverageSummary.d.ts +8 -0
  5. package/dist/adapters/coverageSummary.js +13 -0
  6. package/dist/adapters/eslint.d.ts +13 -0
  7. package/dist/adapters/eslint.js +21 -0
  8. package/dist/adapters/normalizeExternal.d.ts +12 -0
  9. package/dist/adapters/normalizeExternal.js +19 -0
  10. package/dist/adapters/npmAudit.d.ts +15 -0
  11. package/dist/adapters/npmAudit.js +12 -0
  12. package/dist/adapters/semgrep.d.ts +22 -0
  13. package/dist/adapters/semgrep.js +14 -0
  14. package/dist/cli.d.ts +1 -0
  15. package/dist/cli.js +724 -0
  16. package/dist/coverage.d.ts +11 -0
  17. package/dist/coverage.js +102 -0
  18. package/dist/extractors/bucketing.d.ts +7 -0
  19. package/dist/extractors/bucketing.js +72 -0
  20. package/dist/extractors/disposition.d.ts +4 -0
  21. package/dist/extractors/disposition.js +41 -0
  22. package/dist/extractors/fileInventory.d.ts +7 -0
  23. package/dist/extractors/fileInventory.js +44 -0
  24. package/dist/extractors/flows.d.ts +5 -0
  25. package/dist/extractors/flows.js +125 -0
  26. package/dist/extractors/fsIntake.d.ts +8 -0
  27. package/dist/extractors/fsIntake.js +66 -0
  28. package/dist/extractors/graph.d.ts +4 -0
  29. package/dist/extractors/graph.js +46 -0
  30. package/dist/extractors/ignore.d.ts +1 -0
  31. package/dist/extractors/ignore.js +17 -0
  32. package/dist/extractors/risk.d.ts +5 -0
  33. package/dist/extractors/risk.js +45 -0
  34. package/dist/extractors/surfaces.d.ts +4 -0
  35. package/dist/extractors/surfaces.js +40 -0
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/io/artifacts.d.ts +38 -0
  39. package/dist/io/artifacts.js +100 -0
  40. package/dist/io/json.d.ts +8 -0
  41. package/dist/io/json.js +96 -0
  42. package/dist/io/runArtifacts.d.ts +14 -0
  43. package/dist/io/runArtifacts.js +37 -0
  44. package/dist/orchestrator/advance.d.ts +24 -0
  45. package/dist/orchestrator/advance.js +104 -0
  46. package/dist/orchestrator/artifactMetadata.d.ts +4 -0
  47. package/dist/orchestrator/artifactMetadata.js +111 -0
  48. package/dist/orchestrator/autoFixExecutor.d.ts +3 -0
  49. package/dist/orchestrator/autoFixExecutor.js +63 -0
  50. package/dist/orchestrator/chunking.d.ts +5 -0
  51. package/dist/orchestrator/chunking.js +13 -0
  52. package/dist/orchestrator/dependencyMap.d.ts +1 -0
  53. package/dist/orchestrator/dependencyMap.js +82 -0
  54. package/dist/orchestrator/executors.d.ts +6 -0
  55. package/dist/orchestrator/executors.js +52 -0
  56. package/dist/orchestrator/flowCoverage.d.ts +4 -0
  57. package/dist/orchestrator/flowCoverage.js +44 -0
  58. package/dist/orchestrator/flowPlanning.d.ts +3 -0
  59. package/dist/orchestrator/flowPlanning.js +42 -0
  60. package/dist/orchestrator/flowRequeue.d.ts +5 -0
  61. package/dist/orchestrator/flowRequeue.js +58 -0
  62. package/dist/orchestrator/internalExecutors.d.ts +16 -0
  63. package/dist/orchestrator/internalExecutors.js +212 -0
  64. package/dist/orchestrator/nextStep.d.ts +9 -0
  65. package/dist/orchestrator/nextStep.js +44 -0
  66. package/dist/orchestrator/planning.d.ts +4 -0
  67. package/dist/orchestrator/planning.js +62 -0
  68. package/dist/orchestrator/requeue.d.ts +3 -0
  69. package/dist/orchestrator/requeue.js +25 -0
  70. package/dist/orchestrator/requeueCommand.d.ts +10 -0
  71. package/dist/orchestrator/requeueCommand.js +27 -0
  72. package/dist/orchestrator/resultIngestion.d.ts +2 -0
  73. package/dist/orchestrator/resultIngestion.js +13 -0
  74. package/dist/orchestrator/runtimeValidation.d.ts +7 -0
  75. package/dist/orchestrator/runtimeValidation.js +103 -0
  76. package/dist/orchestrator/runtimeValidationUpdate.d.ts +2 -0
  77. package/dist/orchestrator/runtimeValidationUpdate.js +52 -0
  78. package/dist/orchestrator/staleness.d.ts +2 -0
  79. package/dist/orchestrator/staleness.js +83 -0
  80. package/dist/orchestrator/state.d.ts +3 -0
  81. package/dist/orchestrator/state.js +85 -0
  82. package/dist/orchestrator/syntaxResolutionExecutor.d.ts +3 -0
  83. package/dist/orchestrator/syntaxResolutionExecutor.js +99 -0
  84. package/dist/orchestrator/taskBuilder.d.ts +12 -0
  85. package/dist/orchestrator/taskBuilder.js +154 -0
  86. package/dist/orchestrator/unitBuilder.d.ts +3 -0
  87. package/dist/orchestrator/unitBuilder.js +145 -0
  88. package/dist/orchestrator.d.ts +6 -0
  89. package/dist/orchestrator.js +33 -0
  90. package/dist/prompts/renderWorkerPrompt.d.ts +2 -0
  91. package/dist/prompts/renderWorkerPrompt.js +19 -0
  92. package/dist/providers/claudeCodeProvider.d.ts +8 -0
  93. package/dist/providers/claudeCodeProvider.js +20 -0
  94. package/dist/providers/index.d.ts +7 -0
  95. package/dist/providers/index.js +77 -0
  96. package/dist/providers/localSubprocessProvider.d.ts +5 -0
  97. package/dist/providers/localSubprocessProvider.js +13 -0
  98. package/dist/providers/opencodeProvider.d.ts +8 -0
  99. package/dist/providers/opencodeProvider.js +15 -0
  100. package/dist/providers/spawnLoggedCommand.d.ts +2 -0
  101. package/dist/providers/spawnLoggedCommand.js +48 -0
  102. package/dist/providers/subprocessTemplateProvider.d.ts +8 -0
  103. package/dist/providers/subprocessTemplateProvider.js +41 -0
  104. package/dist/providers/types.d.ts +22 -0
  105. package/dist/providers/types.js +1 -0
  106. package/dist/providers/vscodeTaskProvider.d.ts +8 -0
  107. package/dist/providers/vscodeTaskProvider.js +14 -0
  108. package/dist/reporting/mergeFindings.d.ts +4 -0
  109. package/dist/reporting/mergeFindings.js +136 -0
  110. package/dist/reporting/rootCause.d.ts +11 -0
  111. package/dist/reporting/rootCause.js +69 -0
  112. package/dist/reporting/synthesis.d.ts +21 -0
  113. package/dist/reporting/synthesis.js +55 -0
  114. package/dist/supervisor/operatorHandoff.d.ts +37 -0
  115. package/dist/supervisor/operatorHandoff.js +144 -0
  116. package/dist/supervisor/runLedger.d.ts +3 -0
  117. package/dist/supervisor/runLedger.js +17 -0
  118. package/dist/supervisor/sessionConfig.d.ts +4 -0
  119. package/dist/supervisor/sessionConfig.js +26 -0
  120. package/dist/types/artifactMetadata.d.ts +8 -0
  121. package/dist/types/artifactMetadata.js +1 -0
  122. package/dist/types/auditState.d.ts +14 -0
  123. package/dist/types/auditState.js +1 -0
  124. package/dist/types/disposition.d.ts +9 -0
  125. package/dist/types/disposition.js +1 -0
  126. package/dist/types/externalAnalyzer.d.ts +16 -0
  127. package/dist/types/externalAnalyzer.js +1 -0
  128. package/dist/types/flowCoverage.d.ts +11 -0
  129. package/dist/types/flowCoverage.js +1 -0
  130. package/dist/types/flows.d.ts +11 -0
  131. package/dist/types/flows.js +1 -0
  132. package/dist/types/graph.d.ts +18 -0
  133. package/dist/types/graph.js +1 -0
  134. package/dist/types/risk.d.ts +9 -0
  135. package/dist/types/risk.js +1 -0
  136. package/dist/types/runLedger.d.ts +13 -0
  137. package/dist/types/runLedger.js +1 -0
  138. package/dist/types/runtimeValidation.d.ts +22 -0
  139. package/dist/types/runtimeValidation.js +1 -0
  140. package/dist/types/sessionConfig.d.ts +27 -0
  141. package/dist/types/sessionConfig.js +1 -0
  142. package/dist/types/surfaces.d.ts +11 -0
  143. package/dist/types/surfaces.js +1 -0
  144. package/dist/types/workerResult.d.ts +13 -0
  145. package/dist/types/workerResult.js +1 -0
  146. package/dist/types/workerSession.d.ts +13 -0
  147. package/dist/types/workerSession.js +1 -0
  148. package/dist/types.d.ts +104 -0
  149. package/dist/types.js +1 -0
  150. package/dist/validation/artifacts.d.ts +3 -0
  151. package/dist/validation/artifacts.js +191 -0
  152. package/dist/validation/basic.d.ts +5 -0
  153. package/dist/validation/basic.js +9 -0
  154. package/dist/validation/sessionConfig.d.ts +6 -0
  155. package/dist/validation/sessionConfig.js +139 -0
  156. package/docs/agent-integrations.md +237 -0
  157. package/docs/agent-roles.md +69 -0
  158. package/docs/architecture.md +90 -0
  159. package/docs/artifacts.md +69 -0
  160. package/docs/bootstrap-install.md +79 -0
  161. package/docs/contract.md +140 -0
  162. package/docs/github-copilot.md +50 -0
  163. package/docs/model-selection.md +86 -0
  164. package/docs/next-steps.md +161 -0
  165. package/docs/packaging.md +88 -0
  166. package/docs/pipeline.md +152 -0
  167. package/docs/product-direction.md +111 -0
  168. package/docs/production-launch-bar.md +83 -0
  169. package/docs/production-readiness.md +52 -0
  170. package/docs/repo-layout.md +30 -0
  171. package/docs/run-flow.md +49 -0
  172. package/docs/session-config.md +232 -0
  173. package/docs/supervisor.md +83 -0
  174. package/docs/usage.md +172 -0
  175. package/docs/windows-setup.md +146 -0
  176. package/package.json +56 -0
  177. package/schemas/audit-code-v1alpha1.schema.json +191 -0
  178. package/schemas/audit_result.schema.json +48 -0
  179. package/schemas/audit_state.schema.json +36 -0
  180. package/schemas/audit_task.schema.json +49 -0
  181. package/schemas/blind_spot_register.schema.json +40 -0
  182. package/schemas/coverage_matrix.schema.json +50 -0
  183. package/schemas/critical_flows.schema.json +38 -0
  184. package/schemas/external_analyzer_results.schema.json +31 -0
  185. package/schemas/file_disposition.schema.json +33 -0
  186. package/schemas/finding.schema.json +62 -0
  187. package/schemas/flow_coverage.schema.json +44 -0
  188. package/schemas/graph_bundle.schema.json +55 -0
  189. package/schemas/merged_findings.schema.json +14 -0
  190. package/schemas/repo_manifest.schema.json +37 -0
  191. package/schemas/risk_register.schema.json +30 -0
  192. package/schemas/root_cause_clusters.schema.json +31 -0
  193. package/schemas/runtime_validation_report.schema.json +34 -0
  194. package/schemas/runtime_validation_tasks.schema.json +36 -0
  195. package/schemas/surface_manifest.schema.json +32 -0
  196. package/schemas/synthesis_report.schema.json +61 -0
  197. package/schemas/unit_manifest.schema.json +36 -0
  198. package/skills/audit-code/SKILL.md +54 -0
  199. package/skills/audit-code/audit-code.prompt.md +66 -0
@@ -0,0 +1,154 @@
1
+ function chunkRanges(totalLines, chunkSize) {
2
+ const ranges = [];
3
+ let start = 1;
4
+ while (start <= totalLines) {
5
+ const end = Math.min(start + chunkSize - 1, totalLines);
6
+ ranges.push({ start, end });
7
+ start = end + 1;
8
+ }
9
+ return ranges;
10
+ }
11
+ function taskPriority(hasExternalSignal, lens) {
12
+ if (hasExternalSignal &&
13
+ (lens === "security" || lens === "data_integrity" || lens === "reliability")) {
14
+ return "high";
15
+ }
16
+ if (hasExternalSignal) {
17
+ return "medium";
18
+ }
19
+ return lens === "security" || lens === "data_integrity" ? "medium" : "low";
20
+ }
21
+ function priorityRank(priority) {
22
+ switch (priority) {
23
+ case "high":
24
+ return 3;
25
+ case "medium":
26
+ return 2;
27
+ case "low":
28
+ default:
29
+ return 1;
30
+ }
31
+ }
32
+ function pickAnalyzerLens(category) {
33
+ const normalized = category.toLowerCase();
34
+ if (normalized.includes("security") ||
35
+ normalized.includes("secret") ||
36
+ normalized.includes("dependency"))
37
+ return "security";
38
+ if (normalized.includes("data"))
39
+ return "data_integrity";
40
+ if (normalized.includes("tests") || normalized.includes("coverage"))
41
+ return "tests";
42
+ if (normalized.includes("reliability") || normalized.includes("concurrency"))
43
+ return "reliability";
44
+ if (normalized.includes("maintainability") ||
45
+ normalized.includes("lint") ||
46
+ normalized.includes("style"))
47
+ return "maintainability";
48
+ return "correctness";
49
+ }
50
+ export function buildChunkedAuditTasks(unitManifest, unitLineIndex, options = {}) {
51
+ const chunkSize = options.chunk_size ?? 200;
52
+ const allowed = new Set(options.limit_lenses ?? []);
53
+ const enforceLensFilter = allowed.size > 0;
54
+ const tasks = [];
55
+ const seen = new Set();
56
+ const externalPaths = new Set((options.external_analyzer_results?.results ?? []).map((item) => item.path));
57
+ for (const unit of unitManifest.units) {
58
+ const minimumLenses = ["correctness", "security"];
59
+ const lensesToRun = Array.isArray(unit.required_lenses) && unit.required_lenses.length >= 2
60
+ ? unit.required_lenses
61
+ : minimumLenses;
62
+ for (const lens of lensesToRun) {
63
+ if (enforceLensFilter && !allowed.has(lens)) {
64
+ continue;
65
+ }
66
+ for (const filePath of unit.files) {
67
+ const hasExternalSignal = externalPaths.has(filePath);
68
+ const priority = taskPriority(hasExternalSignal, lens);
69
+ const tags = hasExternalSignal ? ["external_analyzer_signal"] : [];
70
+ const lineCount = unitLineIndex[filePath] ?? 0;
71
+ const ranges = chunkRanges(lineCount, chunkSize);
72
+ if (ranges.length === 0) {
73
+ const id = `${unit.unit_id}:${lens}:${filePath}:full`;
74
+ if (seen.has(id))
75
+ continue;
76
+ seen.add(id);
77
+ tasks.push({
78
+ task_id: id,
79
+ unit_id: unit.unit_id,
80
+ pass_id: `pass:${lens}`,
81
+ lens,
82
+ file_paths: [filePath],
83
+ rationale: `Audit ${filePath} under the ${lens} lens.${hasExternalSignal ? " External analyzer signals raise priority for this path." : ""}`,
84
+ priority,
85
+ tags,
86
+ });
87
+ continue;
88
+ }
89
+ for (const range of ranges) {
90
+ const id = `${unit.unit_id}:${lens}:${filePath}:${range.start}-${range.end}`;
91
+ if (seen.has(id))
92
+ continue;
93
+ seen.add(id);
94
+ tasks.push({
95
+ task_id: id,
96
+ unit_id: unit.unit_id,
97
+ pass_id: `pass:${lens}`,
98
+ lens,
99
+ file_paths: [filePath],
100
+ line_ranges: [
101
+ { path: filePath, start: range.start, end: range.end },
102
+ ],
103
+ rationale: `Audit ${filePath} lines ${range.start}-${range.end} under the ${lens} lens.${hasExternalSignal ? " External analyzer signals raise priority for this path." : ""}`,
104
+ priority,
105
+ tags,
106
+ });
107
+ }
108
+ }
109
+ }
110
+ }
111
+ return tasks.sort((a, b) => {
112
+ const priorityDelta = priorityRank(b.priority) - priorityRank(a.priority);
113
+ if (priorityDelta !== 0)
114
+ return priorityDelta;
115
+ return a.task_id.localeCompare(b.task_id);
116
+ });
117
+ }
118
+ export function buildExternalSignalTasks(coverageMatrix, unitLineIndex, externalAnalyzerResults) {
119
+ if (!externalAnalyzerResults) {
120
+ return [];
121
+ }
122
+ const tasks = [];
123
+ const seen = new Set();
124
+ for (const result of externalAnalyzerResults.results) {
125
+ const lens = pickAnalyzerLens(result.category);
126
+ const coverage = coverageMatrix.files.find((file) => file.path === result.path);
127
+ if (!coverage || coverage.audit_status === "excluded") {
128
+ continue;
129
+ }
130
+ const lineCount = unitLineIndex[result.path] ?? 0;
131
+ const id = `analyzer:${externalAnalyzerResults.tool}:${lens}:${result.path}:${result.id}`;
132
+ if (seen.has(id)) {
133
+ continue;
134
+ }
135
+ seen.add(id);
136
+ tasks.push({
137
+ task_id: id,
138
+ unit_id: coverage.unit_ids[0] ?? `analyzer:${result.path}`,
139
+ pass_id: `analyzer:${externalAnalyzerResults.tool}:${lens}`,
140
+ lens,
141
+ file_paths: [result.path],
142
+ line_ranges: lineCount > 0
143
+ ? [{ path: result.path, start: 1, end: lineCount }]
144
+ : undefined,
145
+ rationale: `Analyzer follow-up for ${result.path} under the ${lens} lens because ${externalAnalyzerResults.tool} reported: ${result.summary}`,
146
+ priority: "high",
147
+ tags: [
148
+ "external_analyzer_signal",
149
+ `external_tool:${externalAnalyzerResults.tool}`,
150
+ ],
151
+ });
152
+ }
153
+ return tasks.sort((a, b) => a.task_id.localeCompare(b.task_id));
154
+ }
@@ -0,0 +1,3 @@
1
+ import type { RepoManifest, UnitManifest } from "../types.js";
2
+ import type { FileDisposition } from "../types/disposition.js";
3
+ export declare function buildUnitManifest(repoManifest: RepoManifest, disposition?: FileDisposition): UnitManifest;
@@ -0,0 +1,145 @@
1
+ import { bucketFile } from "../extractors/bucketing.js";
2
+ import { isAuditExcludedStatus } from "../extractors/disposition.js";
3
+ const LENS_MAP = {
4
+ runtime: ["correctness", "maintainability", "tests"],
5
+ interface: ["correctness", "security", "reliability", "tests"],
6
+ data_layer: ["correctness", "data_integrity", "reliability", "tests"],
7
+ security_sensitive: ["security", "correctness", "reliability", "tests"],
8
+ concurrency_state: ["reliability", "performance", "correctness", "tests"],
9
+ tests: ["tests", "maintainability"],
10
+ tooling_scripts: ["correctness", "operability", "config_deployment"],
11
+ config_deployment: ["config_deployment", "reliability", "operability"],
12
+ docs_specs: ["maintainability"],
13
+ generated_vendor: ["maintainability"],
14
+ unknown: ["correctness"],
15
+ };
16
+ function inferUnitKind(path) {
17
+ const normalized = path.toLowerCase();
18
+ if (normalized.startsWith("apps/") || normalized.startsWith("services/"))
19
+ return "service";
20
+ if (normalized.startsWith("packages/"))
21
+ return "package";
22
+ if (normalized.startsWith("infra/"))
23
+ return "infrastructure";
24
+ if (normalized.startsWith("scripts/") || normalized.startsWith("bin/"))
25
+ return "script";
26
+ if (normalized.includes("test") || normalized.includes("spec"))
27
+ return "test";
28
+ if (normalized.includes("api/") ||
29
+ normalized.includes("route") ||
30
+ normalized.includes("controller"))
31
+ return "interface";
32
+ if (normalized.includes("model") ||
33
+ normalized.includes("schema") ||
34
+ normalized.includes("migration") ||
35
+ normalized.includes("db/"))
36
+ return "data";
37
+ return "module";
38
+ }
39
+ function inferUnitId(path, kind) {
40
+ const parts = path.split("/");
41
+ const normalized = path.toLowerCase();
42
+ if ((parts[0] === "src" || parts[0] === "lib") && parts.length >= 3) {
43
+ if (["api", "routes", "controllers", "models", "db", "services"].includes(parts[1])) {
44
+ return `${parts[0]}-${parts[1]}-${parts[2]}`.replace(/[^a-zA-Z0-9_-]/g, "-");
45
+ }
46
+ }
47
+ if ((parts[0] === "src" || parts[0] === "lib") && parts.length >= 2) {
48
+ return `${parts[0]}-${parts[1]}`.replace(/[^a-zA-Z0-9_-]/g, "-");
49
+ }
50
+ if ((parts[0] === "tests" || parts[0] === "test") && parts.length >= 2) {
51
+ return `tests-${parts[1]}`.replace(/[^a-zA-Z0-9_-]/g, "-");
52
+ }
53
+ if (parts.length >= 2) {
54
+ return `${parts[0]}-${parts[1]}`.replace(/[^a-zA-Z0-9_-]/g, "-");
55
+ }
56
+ if (normalized.includes("docker") ||
57
+ normalized.endsWith(".yml") ||
58
+ normalized.endsWith(".yaml")) {
59
+ return "infra-config";
60
+ }
61
+ return `${kind}-${path.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
62
+ }
63
+ function sortLenses(lenses) {
64
+ const order = [
65
+ "security",
66
+ "correctness",
67
+ "reliability",
68
+ "data_integrity",
69
+ "performance",
70
+ "operability",
71
+ "config_deployment",
72
+ "maintainability",
73
+ "tests",
74
+ ];
75
+ const set = new Set(lenses);
76
+ return order.filter((lens) => set.has(lens));
77
+ }
78
+ function inferCriticalFlows(files, requiredLenses) {
79
+ const flows = new Set();
80
+ const combined = files.join(" ").toLowerCase();
81
+ if (combined.includes("auth") ||
82
+ combined.includes("session") ||
83
+ combined.includes("token")) {
84
+ flows.add("auth-session");
85
+ }
86
+ if (combined.includes("billing") ||
87
+ combined.includes("invoice") ||
88
+ combined.includes("payment")) {
89
+ flows.add("billing-payment");
90
+ }
91
+ if (combined.includes("queue") ||
92
+ combined.includes("worker") ||
93
+ combined.includes("job") ||
94
+ requiredLenses.includes("reliability")) {
95
+ flows.add("async-processing");
96
+ }
97
+ if (combined.includes("deploy") ||
98
+ combined.includes("docker") ||
99
+ requiredLenses.includes("config_deployment")) {
100
+ flows.add("deployment-config");
101
+ }
102
+ return [...flows];
103
+ }
104
+ export function buildUnitManifest(repoManifest, disposition) {
105
+ const units = new Map();
106
+ const dispositionMap = new Map(disposition?.files.map((item) => [item.path, item.status]) ?? []);
107
+ for (const file of repoManifest.files) {
108
+ const status = dispositionMap.get(file.path);
109
+ if (file.excluded || (status && isAuditExcludedStatus(status))) {
110
+ continue;
111
+ }
112
+ const kind = inferUnitKind(file.path);
113
+ const unitId = inferUnitId(file.path, kind);
114
+ const existing = units.get(unitId) ?? {
115
+ unit_id: unitId,
116
+ name: unitId,
117
+ kind,
118
+ files: [],
119
+ required_lenses: [],
120
+ };
121
+ if (!existing.files.includes(file.path)) {
122
+ existing.files.push(file.path);
123
+ }
124
+ const assignment = bucketFile(file.path);
125
+ const required = new Set(existing.required_lenses);
126
+ for (const bucket of assignment.buckets) {
127
+ for (const lens of LENS_MAP[bucket]) {
128
+ required.add(lens);
129
+ }
130
+ }
131
+ existing.required_lenses = sortLenses(required);
132
+ const riskScore = new Set(assignment.buckets).size +
133
+ (assignment.buckets.includes("security_sensitive") ? 3 : 0) +
134
+ (assignment.buckets.includes("interface") ? 1 : 0) +
135
+ (assignment.buckets.includes("data_layer") ? 1 : 0);
136
+ existing.risk_score = Math.max(existing.risk_score ?? 0, riskScore);
137
+ existing.files = existing.files.sort((a, b) => a.localeCompare(b));
138
+ existing.critical_flows = inferCriticalFlows(existing.files, existing.required_lenses);
139
+ units.set(unitId, existing);
140
+ }
141
+ return {
142
+ units: [...units.values()].sort((a, b) => (b.risk_score ?? 0) - (a.risk_score ?? 0) ||
143
+ a.unit_id.localeCompare(b.unit_id)),
144
+ };
145
+ }
@@ -0,0 +1,6 @@
1
+ import type { AuditTask, Lens, UnitManifest } from "./types.js";
2
+ export interface TaskBuildOptions {
3
+ pass_prefix?: string;
4
+ limit_lenses?: Lens[];
5
+ }
6
+ export declare function buildAuditTasks(unitManifest: UnitManifest, options?: TaskBuildOptions): AuditTask[];
@@ -0,0 +1,33 @@
1
+ const DEFAULT_LENS_ORDER = [
2
+ "correctness",
3
+ "architecture",
4
+ "maintainability",
5
+ "security",
6
+ "reliability",
7
+ "performance",
8
+ "data_integrity",
9
+ "tests",
10
+ "operability",
11
+ "config_deployment",
12
+ ];
13
+ export function buildAuditTasks(unitManifest, options = {}) {
14
+ const allowed = new Set(options.limit_lenses ?? DEFAULT_LENS_ORDER);
15
+ const passPrefix = options.pass_prefix ?? "pass";
16
+ const tasks = [];
17
+ for (const unit of unitManifest.units) {
18
+ for (const lens of unit.required_lenses) {
19
+ if (!allowed.has(lens)) {
20
+ continue;
21
+ }
22
+ tasks.push({
23
+ task_id: `${unit.unit_id}:${lens}`,
24
+ unit_id: unit.unit_id,
25
+ pass_id: `${passPrefix}:${lens}`,
26
+ lens,
27
+ file_paths: unit.files,
28
+ rationale: `Audit ${unit.name} under the ${lens} lens.`,
29
+ });
30
+ }
31
+ }
32
+ return tasks;
33
+ }
@@ -0,0 +1,2 @@
1
+ import type { WorkerTask } from "../types/workerSession.js";
2
+ export declare function renderWorkerPrompt(task: WorkerTask): string;
@@ -0,0 +1,19 @@
1
+ function shellQuote(arg) {
2
+ return JSON.stringify(arg);
3
+ }
4
+ export function renderWorkerPrompt(task) {
5
+ const command = task.worker_command.map(shellQuote).join(" ");
6
+ return [
7
+ "You are executing one bounded audit step for audit-code.",
8
+ `Run ID: ${task.run_id}`,
9
+ `Repository root: ${task.repo_root}`,
10
+ `Obligation: ${task.obligation_id ?? "unknown"}`,
11
+ `Executor: ${task.preferred_executor}`,
12
+ "Execute the following command exactly, without modification:",
13
+ command,
14
+ "Do not continue the audit recursively.",
15
+ "Do not choose another task.",
16
+ `The command must write the worker result JSON to: ${task.result_path}`,
17
+ "After the command completes, stop.",
18
+ ].join("\n");
19
+ }
@@ -0,0 +1,8 @@
1
+ import type { FreshSessionProvider, LaunchFreshSessionInput } from "./types.js";
2
+ import type { ClaudeCodeConfig } from "../types/sessionConfig.js";
3
+ export declare class ClaudeCodeProvider implements FreshSessionProvider {
4
+ name: string;
5
+ private readonly config;
6
+ constructor(config?: ClaudeCodeConfig);
7
+ launch(input: LaunchFreshSessionInput): Promise<import("./types.js").LaunchFreshSessionResult>;
8
+ }
@@ -0,0 +1,20 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
3
+ export class ClaudeCodeProvider {
4
+ name = "claude-code";
5
+ config;
6
+ constructor(config = {}) {
7
+ this.config = config;
8
+ }
9
+ async launch(input) {
10
+ const prompt = await readFile(input.promptPath, "utf8");
11
+ const command = this.config.command ?? "claude";
12
+ const args = [
13
+ "-p",
14
+ prompt,
15
+ ...(this.config.extra_args ?? []),
16
+ "--dangerously-skip-permissions",
17
+ ];
18
+ return await spawnLoggedCommand(command, args, input);
19
+ }
20
+ }
@@ -0,0 +1,7 @@
1
+ import type { FreshSessionProvider } from "./types.js";
2
+ import type { ResolvedProviderName, SessionConfig } from "../types/sessionConfig.js";
3
+ export declare function resolveFreshSessionProviderName(name: string | undefined, sessionConfig?: SessionConfig, options?: {
4
+ env?: NodeJS.ProcessEnv;
5
+ commandExists?: (command: string) => boolean;
6
+ }): ResolvedProviderName;
7
+ export declare function createFreshSessionProvider(name: string | undefined, sessionConfig?: SessionConfig): FreshSessionProvider;
@@ -0,0 +1,77 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { LocalSubprocessProvider } from "./localSubprocessProvider.js";
3
+ import { SubprocessTemplateProvider } from "./subprocessTemplateProvider.js";
4
+ import { ClaudeCodeProvider } from "./claudeCodeProvider.js";
5
+ import { OpenCodeProvider } from "./opencodeProvider.js";
6
+ import { VSCodeTaskProvider } from "./vscodeTaskProvider.js";
7
+ function hasEntries(values) {
8
+ return (values?.length ?? 0) > 0;
9
+ }
10
+ function hasConfiguredClaudeCode(sessionConfig) {
11
+ return (Boolean(sessionConfig.claude_code?.command?.trim()) ||
12
+ hasEntries(sessionConfig.claude_code?.extra_args));
13
+ }
14
+ function hasConfiguredOpenCode(sessionConfig) {
15
+ return (Boolean(sessionConfig.opencode?.command?.trim()) ||
16
+ hasEntries(sessionConfig.opencode?.extra_args));
17
+ }
18
+ function commandExists(command) {
19
+ const lookupCommand = process.platform === "win32" ? "where" : "which";
20
+ const result = spawnSync(lookupCommand, [command], { stdio: "ignore" });
21
+ return result.status === 0;
22
+ }
23
+ export function resolveFreshSessionProviderName(name, sessionConfig = {}, options = {}) {
24
+ const requestedProvider = name ?? sessionConfig.provider ?? "local-subprocess";
25
+ if (requestedProvider !== "auto") {
26
+ return requestedProvider;
27
+ }
28
+ const env = options.env ?? process.env;
29
+ const lookupCommand = options.commandExists ?? commandExists;
30
+ const inVSCode = (env.TERM_PROGRAM ?? "").toLowerCase() === "vscode";
31
+ if (inVSCode && hasEntries(sessionConfig.vscode_task?.command_template)) {
32
+ return "vscode-task";
33
+ }
34
+ if (hasEntries(sessionConfig.subprocess_template?.command_template)) {
35
+ return "subprocess-template";
36
+ }
37
+ const claudeCommand = sessionConfig.claude_code?.command ?? "claude";
38
+ const opencodeCommand = sessionConfig.opencode?.command ?? "opencode";
39
+ const claudeAvailable = lookupCommand(claudeCommand);
40
+ const opencodeAvailable = lookupCommand(opencodeCommand);
41
+ if (hasConfiguredClaudeCode(sessionConfig) && claudeAvailable) {
42
+ return "claude-code";
43
+ }
44
+ if (hasConfiguredOpenCode(sessionConfig) && opencodeAvailable) {
45
+ return "opencode";
46
+ }
47
+ if (claudeAvailable && !opencodeAvailable) {
48
+ return "claude-code";
49
+ }
50
+ if (opencodeAvailable && !claudeAvailable) {
51
+ return "opencode";
52
+ }
53
+ return "local-subprocess";
54
+ }
55
+ export function createFreshSessionProvider(name, sessionConfig = {}) {
56
+ const providerName = resolveFreshSessionProviderName(name, sessionConfig);
57
+ switch (providerName) {
58
+ case "local-subprocess":
59
+ return new LocalSubprocessProvider();
60
+ case "subprocess-template":
61
+ if (!sessionConfig.subprocess_template?.command_template?.length) {
62
+ throw new Error("subprocess-template provider requires session-config.json with subprocess_template.command_template.");
63
+ }
64
+ return new SubprocessTemplateProvider(sessionConfig.subprocess_template);
65
+ case "claude-code":
66
+ return new ClaudeCodeProvider(sessionConfig.claude_code);
67
+ case "opencode":
68
+ return new OpenCodeProvider(sessionConfig.opencode);
69
+ case "vscode-task":
70
+ if (!sessionConfig.vscode_task?.command_template?.length) {
71
+ throw new Error("vscode-task provider requires session-config.json with vscode_task.command_template.");
72
+ }
73
+ return new VSCodeTaskProvider(sessionConfig.vscode_task);
74
+ default:
75
+ throw new Error(`Unknown provider: ${providerName}`);
76
+ }
77
+ }
@@ -0,0 +1,5 @@
1
+ import type { FreshSessionProvider, LaunchFreshSessionInput } from "./types.js";
2
+ export declare class LocalSubprocessProvider implements FreshSessionProvider {
3
+ name: string;
4
+ launch(input: LaunchFreshSessionInput): Promise<import("./types.js").LaunchFreshSessionResult>;
5
+ }
@@ -0,0 +1,13 @@
1
+ import { readJsonFile } from "../io/json.js";
2
+ import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
3
+ export class LocalSubprocessProvider {
4
+ name = "local-subprocess";
5
+ async launch(input) {
6
+ const task = await readJsonFile(input.taskPath);
7
+ if (!task.worker_command.length) {
8
+ throw new Error("local-subprocess provider requires task.worker_command.");
9
+ }
10
+ const [command, ...args] = task.worker_command;
11
+ return await spawnLoggedCommand(command, args, input);
12
+ }
13
+ }
@@ -0,0 +1,8 @@
1
+ import type { FreshSessionProvider, LaunchFreshSessionInput } from "./types.js";
2
+ import type { OpenCodeConfig } from "../types/sessionConfig.js";
3
+ export declare class OpenCodeProvider implements FreshSessionProvider {
4
+ name: string;
5
+ private readonly config;
6
+ constructor(config?: OpenCodeConfig);
7
+ launch(input: LaunchFreshSessionInput): Promise<import("./types.js").LaunchFreshSessionResult>;
8
+ }
@@ -0,0 +1,15 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
3
+ export class OpenCodeProvider {
4
+ name = "opencode";
5
+ config;
6
+ constructor(config = {}) {
7
+ this.config = config;
8
+ }
9
+ async launch(input) {
10
+ const prompt = await readFile(input.promptPath, "utf8");
11
+ const command = this.config.command ?? "opencode";
12
+ const args = ["run", prompt, ...(this.config.extra_args ?? [])];
13
+ return await spawnLoggedCommand(command, args, input);
14
+ }
15
+ }
@@ -0,0 +1,2 @@
1
+ import type { LaunchFreshSessionInput, LaunchFreshSessionResult } from "./types.js";
2
+ export declare function spawnLoggedCommand(command: string, args: string[], input: LaunchFreshSessionInput, env?: Record<string, string>): Promise<LaunchFreshSessionResult>;
@@ -0,0 +1,48 @@
1
+ import { createWriteStream } from "node:fs";
2
+ import { spawn } from "node:child_process";
3
+ function tee(write, chunk) {
4
+ write.write(chunk);
5
+ }
6
+ export async function spawnLoggedCommand(command, args, input, env) {
7
+ return await new Promise((resolve, reject) => {
8
+ const stdoutLog = createWriteStream(input.stdoutPath, { flags: "a" });
9
+ const stderrLog = createWriteStream(input.stderrPath, { flags: "a" });
10
+ const child = spawn(command, args, {
11
+ cwd: input.repoRoot,
12
+ env: { ...process.env, ...env },
13
+ stdio: ["ignore", "pipe", "pipe"],
14
+ });
15
+ const timer = setTimeout(() => {
16
+ child.kill("SIGTERM");
17
+ }, input.timeoutMs);
18
+ child.stdout.on("data", (chunk) => {
19
+ tee(stdoutLog, chunk);
20
+ if (input.uiMode === "visible") {
21
+ process.stdout.write(chunk);
22
+ }
23
+ });
24
+ child.stderr.on("data", (chunk) => {
25
+ tee(stderrLog, chunk);
26
+ if (input.uiMode === "visible") {
27
+ process.stderr.write(chunk);
28
+ }
29
+ });
30
+ child.on("error", (error) => {
31
+ clearTimeout(timer);
32
+ stdoutLog.end();
33
+ stderrLog.end();
34
+ reject(error);
35
+ });
36
+ child.on("exit", (code, signal) => {
37
+ clearTimeout(timer);
38
+ stdoutLog.end();
39
+ stderrLog.end();
40
+ resolve({
41
+ accepted: true,
42
+ processId: child.pid,
43
+ exitCode: code,
44
+ signal,
45
+ });
46
+ });
47
+ });
48
+ }
@@ -0,0 +1,8 @@
1
+ import type { FreshSessionProvider, LaunchFreshSessionInput } from "./types.js";
2
+ import type { SubprocessTemplateConfig } from "../types/sessionConfig.js";
3
+ export declare class SubprocessTemplateProvider implements FreshSessionProvider {
4
+ name: string;
5
+ private readonly config;
6
+ constructor(config: SubprocessTemplateConfig, name?: string);
7
+ launch(input: LaunchFreshSessionInput): Promise<import("./types.js").LaunchFreshSessionResult>;
8
+ }
@@ -0,0 +1,41 @@
1
+ import { readJsonFile } from "../io/json.js";
2
+ import { spawnLoggedCommand } from "./spawnLoggedCommand.js";
3
+ function shellQuote(arg) {
4
+ return JSON.stringify(arg);
5
+ }
6
+ function applyTemplate(template, input, task) {
7
+ const workerCommandShell = task.worker_command.map(shellQuote).join(" ");
8
+ const workerCommandJson = JSON.stringify(task.worker_command);
9
+ const values = {
10
+ repoRoot: input.repoRoot,
11
+ runId: input.runId,
12
+ obligationId: input.obligationId ?? "",
13
+ promptPath: input.promptPath,
14
+ taskPath: input.taskPath,
15
+ resultPath: input.resultPath,
16
+ stdoutPath: input.stdoutPath,
17
+ stderrPath: input.stderrPath,
18
+ workerCommandShell,
19
+ workerCommandJson,
20
+ uiMode: input.uiMode,
21
+ timeoutMs: String(input.timeoutMs),
22
+ };
23
+ return template.replace(/\{([A-Za-z0-9_]+)\}/g, (_match, key) => values[key] ?? "");
24
+ }
25
+ export class SubprocessTemplateProvider {
26
+ name;
27
+ config;
28
+ constructor(config, name = "subprocess-template") {
29
+ this.config = config;
30
+ this.name = name;
31
+ }
32
+ async launch(input) {
33
+ const task = await readJsonFile(input.taskPath);
34
+ if (!this.config.command_template.length) {
35
+ throw new Error(`${this.name} provider requires a non-empty command_template.`);
36
+ }
37
+ const rendered = this.config.command_template.map((entry) => applyTemplate(entry, input, task));
38
+ const [command, ...args] = rendered;
39
+ return await spawnLoggedCommand(command, args, input, this.config.env);
40
+ }
41
+ }
@@ -0,0 +1,22 @@
1
+ export interface LaunchFreshSessionInput {
2
+ repoRoot: string;
3
+ runId: string;
4
+ obligationId: string | null;
5
+ promptPath: string;
6
+ taskPath: string;
7
+ resultPath: string;
8
+ stdoutPath: string;
9
+ stderrPath: string;
10
+ uiMode: "visible" | "headless";
11
+ timeoutMs: number;
12
+ }
13
+ export interface LaunchFreshSessionResult {
14
+ accepted: boolean;
15
+ processId?: number;
16
+ exitCode?: number | null;
17
+ signal?: string | null;
18
+ }
19
+ export interface FreshSessionProvider {
20
+ name: string;
21
+ launch(input: LaunchFreshSessionInput): Promise<LaunchFreshSessionResult>;
22
+ }
@@ -0,0 +1 @@
1
+ export {};