@ai-sdlc/orchestrator 0.6.0 → 0.9.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 (203) hide show
  1. package/dist/admission-composite.d.ts +67 -0
  2. package/dist/admission-composite.js +158 -0
  3. package/dist/admission-enrichment.d.ts +142 -0
  4. package/dist/admission-enrichment.js +331 -0
  5. package/dist/admission-hc.d.ts +62 -0
  6. package/dist/admission-hc.js +83 -0
  7. package/dist/admission-score.d.ts +95 -5
  8. package/dist/admission-score.js +84 -11
  9. package/dist/analysis/analyzer.js +3 -2
  10. package/dist/analysis/convention-detector.d.ts +85 -2
  11. package/dist/analysis/convention-detector.js +375 -70
  12. package/dist/analysis/diff-analyzer.d.ts +33 -0
  13. package/dist/analysis/diff-analyzer.js +122 -0
  14. package/dist/analysis/hotspot-analyzer.js +3 -1
  15. package/dist/analysis/index.d.ts +2 -1
  16. package/dist/analysis/index.js +2 -1
  17. package/dist/artifacts/index.d.ts +65 -0
  18. package/dist/artifacts/index.js +142 -0
  19. package/dist/backlog-adapter.d.ts +121 -0
  20. package/dist/backlog-adapter.js +438 -0
  21. package/dist/calibration.d.ts +43 -0
  22. package/dist/calibration.js +76 -0
  23. package/dist/cli/commands/agents.d.ts +24 -0
  24. package/dist/cli/commands/agents.js +66 -1
  25. package/dist/cli/commands/git-remote.d.ts +49 -0
  26. package/dist/cli/commands/git-remote.js +91 -0
  27. package/dist/cli/commands/health.d.ts +4 -0
  28. package/dist/cli/commands/health.js +13 -1
  29. package/dist/cli/commands/init.d.ts +26 -0
  30. package/dist/cli/commands/init.js +164 -22
  31. package/dist/cli/commands/mcp-setup.d.ts +31 -0
  32. package/dist/cli/commands/mcp-setup.js +78 -8
  33. package/dist/cli/formatters/table.js +15 -2
  34. package/dist/cli/index.d.ts +14 -1
  35. package/dist/cli/index.js +81 -20
  36. package/dist/cli/versions.d.ts +57 -0
  37. package/dist/cli/versions.js +128 -0
  38. package/dist/code-area-classifier.d.ts +21 -0
  39. package/dist/code-area-classifier.js +48 -0
  40. package/dist/config.d.ts +33 -1
  41. package/dist/config.js +78 -8
  42. package/dist/database/adapters/external.d.ts +24 -0
  43. package/dist/database/adapters/external.js +80 -0
  44. package/dist/database/adapters/neon.d.ts +41 -0
  45. package/dist/database/adapters/neon.js +98 -0
  46. package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
  47. package/dist/database/adapters/pg-snapshot-restore.js +68 -0
  48. package/dist/database/adapters/sqlite-copy.d.ts +32 -0
  49. package/dist/database/adapters/sqlite-copy.js +145 -0
  50. package/dist/database/connection-injection.d.ts +35 -0
  51. package/dist/database/connection-injection.js +93 -0
  52. package/dist/database/index.d.ts +12 -0
  53. package/dist/database/index.js +23 -0
  54. package/dist/database/registry.d.ts +13 -0
  55. package/dist/database/registry.js +27 -0
  56. package/dist/database/topology.d.ts +52 -0
  57. package/dist/database/topology.js +44 -0
  58. package/dist/database/types.d.ts +89 -0
  59. package/dist/database/types.js +26 -0
  60. package/dist/design-authority.d.ts +40 -0
  61. package/dist/design-authority.js +71 -0
  62. package/dist/design-lookahead.d.ts +64 -0
  63. package/dist/design-lookahead.js +86 -0
  64. package/dist/design-quality-trend.d.ts +87 -0
  65. package/dist/design-quality-trend.js +190 -0
  66. package/dist/design-system-context.d.ts +46 -0
  67. package/dist/design-system-context.js +80 -0
  68. package/dist/design-system-correction-loop.d.ts +64 -0
  69. package/dist/design-system-correction-loop.js +128 -0
  70. package/dist/design-system-metrics.d.ts +61 -0
  71. package/dist/design-system-metrics.js +104 -0
  72. package/dist/design-system-stewardship.d.ts +22 -0
  73. package/dist/design-system-stewardship.js +85 -0
  74. package/dist/design-system-validation.d.ts +37 -0
  75. package/dist/design-system-validation.js +88 -0
  76. package/dist/dispatch/index.d.ts +4 -0
  77. package/dist/dispatch/index.js +4 -0
  78. package/dist/dispatch/merge-gate.d.ts +46 -0
  79. package/dist/dispatch/merge-gate.js +90 -0
  80. package/dist/dispatch/requeue.d.ts +57 -0
  81. package/dist/dispatch/requeue.js +131 -0
  82. package/dist/dispatch/worker-pool.d.ts +62 -0
  83. package/dist/dispatch/worker-pool.js +60 -0
  84. package/dist/execute.d.ts +40 -0
  85. package/dist/execute.js +185 -15
  86. package/dist/fix-ci.js +8 -1
  87. package/dist/fix-review.js +8 -1
  88. package/dist/harness/adapters/claude-code.d.ts +29 -0
  89. package/dist/harness/adapters/claude-code.js +191 -0
  90. package/dist/harness/adapters/codex.d.ts +25 -0
  91. package/dist/harness/adapters/codex.js +61 -0
  92. package/dist/harness/independence.d.ts +51 -0
  93. package/dist/harness/independence.js +67 -0
  94. package/dist/harness/index.d.ts +14 -0
  95. package/dist/harness/index.js +20 -0
  96. package/dist/harness/registry.d.ts +17 -0
  97. package/dist/harness/registry.js +31 -0
  98. package/dist/harness/types.d.ts +123 -0
  99. package/dist/harness/types.js +8 -0
  100. package/dist/harness/version-probe.d.ts +14 -0
  101. package/dist/harness/version-probe.js +123 -0
  102. package/dist/index.d.ts +30 -4
  103. package/dist/index.js +35 -2
  104. package/dist/models/classifier.d.ts +76 -0
  105. package/dist/models/classifier.js +221 -0
  106. package/dist/models/index.d.ts +3 -0
  107. package/dist/models/index.js +3 -0
  108. package/dist/models/registry.d.ts +97 -0
  109. package/dist/models/registry.js +173 -0
  110. package/dist/pillar-breakdown.d.ts +63 -0
  111. package/dist/pillar-breakdown.js +153 -0
  112. package/dist/priority.d.ts +5 -0
  113. package/dist/priority.js +18 -5
  114. package/dist/review-meta.d.ts +65 -0
  115. package/dist/review-meta.js +149 -0
  116. package/dist/review.d.ts +4 -0
  117. package/dist/review.js +12 -2
  118. package/dist/runners/claude-code-sdk.d.ts +31 -0
  119. package/dist/runners/claude-code-sdk.js +238 -0
  120. package/dist/runners/claude-code.d.ts +3 -0
  121. package/dist/runners/claude-code.js +30 -66
  122. package/dist/runners/codex.js +4 -1
  123. package/dist/runners/copilot.js +4 -1
  124. package/dist/runners/cursor.js +4 -1
  125. package/dist/runners/git-utils.d.ts +81 -0
  126. package/dist/runners/git-utils.js +201 -0
  127. package/dist/runners/index.d.ts +3 -0
  128. package/dist/runners/index.js +3 -0
  129. package/dist/runners/review-agent.d.ts +20 -0
  130. package/dist/runners/review-agent.js +171 -41
  131. package/dist/runners/runner-registry.js +10 -0
  132. package/dist/runners/sdk-review-runner.d.ts +65 -0
  133. package/dist/runners/sdk-review-runner.js +185 -0
  134. package/dist/runners/security-triage.d.ts +20 -4
  135. package/dist/runners/security-triage.js +39 -15
  136. package/dist/runners/types.d.ts +6 -0
  137. package/dist/runtime/attestations.d.ts +274 -0
  138. package/dist/runtime/attestations.js +460 -0
  139. package/dist/runtime/git-env.d.ts +53 -0
  140. package/dist/runtime/git-env.js +60 -0
  141. package/dist/runtime/index.d.ts +7 -0
  142. package/dist/runtime/index.js +7 -0
  143. package/dist/runtime/parallelism-flag.d.ts +10 -0
  144. package/dist/runtime/parallelism-flag.js +18 -0
  145. package/dist/runtime/port-allocator.d.ts +21 -0
  146. package/dist/runtime/port-allocator.js +66 -0
  147. package/dist/runtime/worktree-pool.d.ts +86 -0
  148. package/dist/runtime/worktree-pool.js +204 -0
  149. package/dist/runtime/worktree.d.ts +25 -0
  150. package/dist/runtime/worktree.js +111 -0
  151. package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
  152. package/dist/sa-scoring/auto-calibrate.js +107 -0
  153. package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
  154. package/dist/sa-scoring/c1-sa2-computable.js +59 -0
  155. package/dist/sa-scoring/composite.d.ts +107 -0
  156. package/dist/sa-scoring/composite.js +139 -0
  157. package/dist/sa-scoring/depparse-client.d.ts +79 -0
  158. package/dist/sa-scoring/depparse-client.js +187 -0
  159. package/dist/sa-scoring/did-compiler.d.ts +122 -0
  160. package/dist/sa-scoring/did-compiler.js +286 -0
  161. package/dist/sa-scoring/drift-monitor.d.ts +84 -0
  162. package/dist/sa-scoring/drift-monitor.js +186 -0
  163. package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
  164. package/dist/sa-scoring/exemplar-bank.js +154 -0
  165. package/dist/sa-scoring/feedback-store.d.ts +100 -0
  166. package/dist/sa-scoring/feedback-store.js +156 -0
  167. package/dist/sa-scoring/index.d.ts +71 -0
  168. package/dist/sa-scoring/index.js +158 -0
  169. package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
  170. package/dist/sa-scoring/layer1-deterministic.js +298 -0
  171. package/dist/sa-scoring/layer2-structural.d.ts +71 -0
  172. package/dist/sa-scoring/layer2-structural.js +151 -0
  173. package/dist/sa-scoring/layer3-llm.d.ts +86 -0
  174. package/dist/sa-scoring/layer3-llm.js +282 -0
  175. package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
  176. package/dist/sa-scoring/rescore-orchestrator.js +47 -0
  177. package/dist/scheduling/burn-down.d.ts +27 -0
  178. package/dist/scheduling/burn-down.js +43 -0
  179. package/dist/scheduling/calibration.d.ts +42 -0
  180. package/dist/scheduling/calibration.js +150 -0
  181. package/dist/scheduling/index.d.ts +8 -0
  182. package/dist/scheduling/index.js +8 -0
  183. package/dist/scheduling/ledger.d.ts +59 -0
  184. package/dist/scheduling/ledger.js +216 -0
  185. package/dist/scheduling/off-peak.d.ts +27 -0
  186. package/dist/scheduling/off-peak.js +112 -0
  187. package/dist/scheduling/schedule-decision.d.ts +39 -0
  188. package/dist/scheduling/schedule-decision.js +89 -0
  189. package/dist/scheduling/tier-analysis.d.ts +47 -0
  190. package/dist/scheduling/tier-analysis.js +81 -0
  191. package/dist/scheduling/types.d.ts +140 -0
  192. package/dist/scheduling/types.js +11 -0
  193. package/dist/shared.d.ts +13 -0
  194. package/dist/shared.js +32 -0
  195. package/dist/state/schema.d.ts +5 -1
  196. package/dist/state/schema.js +223 -1
  197. package/dist/state/store.d.ts +55 -1
  198. package/dist/state/store.js +342 -7
  199. package/dist/state/types.d.ts +139 -0
  200. package/dist/types.d.ts +1 -1
  201. package/dist/validate-agent-output.js +4 -1
  202. package/dist/watch.js +6 -0
  203. package/package.json +2 -2
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Shared git utilities for agent runners.
3
+ */
4
+ /**
5
+ * Run a git command in the given directory.
6
+ *
7
+ * Always passes `-c core.quotePath=false` so paths containing non-ASCII
8
+ * characters (e.g. ↔, é, 中) come back as raw UTF-8 instead of git's default
9
+ * octal-escaped form (`"file with \342\206\224.md"`). Without this, the AISDLC-68
10
+ * task file (which has ↔ in its name) showed up in `git diff --name-only`
11
+ * as a quoted+escaped string and the subsequent `git add -- <file>` rejected
12
+ * with "pathspec did not match".
13
+ *
14
+ * Uses `cleanGitEnv()` (AISDLC-72) so git resolves against `workDir`'s own
15
+ * .git rather than whatever a parent process (husky pre-push hook running
16
+ * in another worktree) leaked into GIT_DIR / GIT_WORK_TREE / GIT_INDEX_FILE.
17
+ */
18
+ export declare function gitExec(workDir: string, args: string[]): Promise<string>;
19
+ export interface DetectedChanges {
20
+ filesChanged: string[];
21
+ agentAlreadyCommitted: boolean;
22
+ }
23
+ export interface WorktreeBaseline {
24
+ /** Set of paths that were untracked BEFORE the agent ran. */
25
+ untracked: Set<string>;
26
+ /** Set of paths that had unstaged modifications BEFORE the agent ran. */
27
+ modified: Set<string>;
28
+ }
29
+ /**
30
+ * Snapshot the untracked + modified file lists in the worktree. Captured before
31
+ * the agent runs so detectChangedFiles can subtract pre-existing noise (SQLite
32
+ * working files, draft RFCs the user hasn't decided to commit yet, in-flight
33
+ * edits on the previous branch). Without this, `git add -A` sweeps everything
34
+ * into the agent's commit (the AISDLC-68 incident).
35
+ *
36
+ * Failures degrade gracefully to an empty baseline — never block the pipeline
37
+ * because the snapshot couldn't run.
38
+ */
39
+ export declare function snapshotWorktree(workDir: string): Promise<WorktreeBaseline>;
40
+ /**
41
+ * Detect files changed by an agent. Three signals contribute:
42
+ *
43
+ * - **Unstaged working-tree changes** (`git diff --name-only`)
44
+ * - **Staged but uncommitted changes** (`git diff --name-only --cached`) — the
45
+ * agent sometimes runs `git add` itself before yielding control. Without
46
+ * this signal the orchestrator returned "Agent made no changes" and bailed
47
+ * even though the agent's work was sitting in the index (the AISDLC-68
48
+ * fourth-rerun bug).
49
+ * - **Untracked files** (`git ls-files --others`)
50
+ *
51
+ * When `baseline` is provided, untracked files that existed BEFORE the agent
52
+ * ran are excluded — they belong to the user, not the agent's diff.
53
+ *
54
+ * Also checks whether the agent self-committed (compares HEAD to merge-base
55
+ * with origin/main). Self-committed runs short-circuit the orchestrator's
56
+ * commit step.
57
+ */
58
+ export declare function detectChangedFiles(workDir: string, baseline?: WorktreeBaseline): Promise<DetectedChanges>;
59
+ export interface CrossRepoWrite {
60
+ /** Sibling repository absolute path. */
61
+ repoPath: string;
62
+ /** Files modified or added in that sibling, relative to its root. */
63
+ files: string[];
64
+ }
65
+ /**
66
+ * Detect writes the agent made into sibling git repositories (i.e., directories
67
+ * adjacent to `workDir` that are themselves git repos). Surfaced as a warning,
68
+ * not a hard failure — the AISDLC-68 task LEGITIMATELY needed to sync into
69
+ * `../ai-sdlc-io/`, and we don't want to forbid that. The orchestrator just
70
+ * needs to surface that the changes exist so the operator knows to commit them
71
+ * separately in the sibling repo.
72
+ *
73
+ * Returns one entry per dirty sibling repo, with the list of changed files.
74
+ * Empty array when no siblings are dirty (the common case).
75
+ */
76
+ export declare function detectCrossRepoWrites(workDir: string): Promise<CrossRepoWrite[]>;
77
+ /**
78
+ * Run lint and format auto-fix commands (best-effort, non-fatal).
79
+ */
80
+ export declare function runAutoFix(workDir: string, lintCmd?: string, fmtCmd?: string): Promise<void>;
81
+ //# sourceMappingURL=git-utils.d.ts.map
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Shared git utilities for agent runners.
3
+ */
4
+ import { execFile } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+ import { cleanGitEnv } from '../runtime/git-env.js';
7
+ const execFileAsync = promisify(execFile);
8
+ /**
9
+ * Run a git command in the given directory.
10
+ *
11
+ * Always passes `-c core.quotePath=false` so paths containing non-ASCII
12
+ * characters (e.g. ↔, é, 中) come back as raw UTF-8 instead of git's default
13
+ * octal-escaped form (`"file with \342\206\224.md"`). Without this, the AISDLC-68
14
+ * task file (which has ↔ in its name) showed up in `git diff --name-only`
15
+ * as a quoted+escaped string and the subsequent `git add -- <file>` rejected
16
+ * with "pathspec did not match".
17
+ *
18
+ * Uses `cleanGitEnv()` (AISDLC-72) so git resolves against `workDir`'s own
19
+ * .git rather than whatever a parent process (husky pre-push hook running
20
+ * in another worktree) leaked into GIT_DIR / GIT_WORK_TREE / GIT_INDEX_FILE.
21
+ */
22
+ export async function gitExec(workDir, args) {
23
+ const { stdout } = await execFileAsync('git', ['-c', 'core.quotePath=false', ...args], {
24
+ cwd: workDir,
25
+ env: cleanGitEnv(),
26
+ });
27
+ return stdout.trim();
28
+ }
29
+ /**
30
+ * Snapshot the untracked + modified file lists in the worktree. Captured before
31
+ * the agent runs so detectChangedFiles can subtract pre-existing noise (SQLite
32
+ * working files, draft RFCs the user hasn't decided to commit yet, in-flight
33
+ * edits on the previous branch). Without this, `git add -A` sweeps everything
34
+ * into the agent's commit (the AISDLC-68 incident).
35
+ *
36
+ * Failures degrade gracefully to an empty baseline — never block the pipeline
37
+ * because the snapshot couldn't run.
38
+ */
39
+ export async function snapshotWorktree(workDir) {
40
+ try {
41
+ const [untrackedOutput, modifiedOutput] = await Promise.all([
42
+ gitExec(workDir, ['ls-files', '--others', '--exclude-standard']),
43
+ gitExec(workDir, ['diff', '--name-only']),
44
+ ]);
45
+ return {
46
+ untracked: new Set(untrackedOutput.split('\n').filter(Boolean)),
47
+ modified: new Set(modifiedOutput.split('\n').filter(Boolean)),
48
+ };
49
+ }
50
+ catch {
51
+ return { untracked: new Set(), modified: new Set() };
52
+ }
53
+ }
54
+ /**
55
+ * Detect files changed by an agent. Three signals contribute:
56
+ *
57
+ * - **Unstaged working-tree changes** (`git diff --name-only`)
58
+ * - **Staged but uncommitted changes** (`git diff --name-only --cached`) — the
59
+ * agent sometimes runs `git add` itself before yielding control. Without
60
+ * this signal the orchestrator returned "Agent made no changes" and bailed
61
+ * even though the agent's work was sitting in the index (the AISDLC-68
62
+ * fourth-rerun bug).
63
+ * - **Untracked files** (`git ls-files --others`)
64
+ *
65
+ * When `baseline` is provided, untracked files that existed BEFORE the agent
66
+ * ran are excluded — they belong to the user, not the agent's diff.
67
+ *
68
+ * Also checks whether the agent self-committed (compares HEAD to merge-base
69
+ * with origin/main). Self-committed runs short-circuit the orchestrator's
70
+ * commit step.
71
+ */
72
+ export async function detectChangedFiles(workDir, baseline) {
73
+ const [diffOutput, stagedOutput, untrackedOutput] = await Promise.all([
74
+ gitExec(workDir, ['diff', '--name-only']),
75
+ gitExec(workDir, ['diff', '--name-only', '--cached']),
76
+ gitExec(workDir, ['ls-files', '--others', '--exclude-standard']),
77
+ ]);
78
+ const allUntracked = untrackedOutput.split('\n').filter(Boolean);
79
+ // Untracked files that existed pre-agent are user state — exclude them.
80
+ const agentUntracked = baseline
81
+ ? allUntracked.filter((f) => !baseline.untracked.has(f))
82
+ : allUntracked;
83
+ // Combine + dedupe: a file may show up in both unstaged and staged diffs
84
+ // (partial-stage scenario), and untracked files are mutually exclusive
85
+ // with diffs but include for completeness.
86
+ const uncommittedSet = new Set([
87
+ ...diffOutput.split('\n').filter(Boolean),
88
+ ...stagedOutput.split('\n').filter(Boolean),
89
+ ...agentUntracked,
90
+ ]);
91
+ const uncommittedFiles = [...uncommittedSet];
92
+ // Check if agent already committed — compare against merge base with main
93
+ let committedFiles = [];
94
+ let agentAlreadyCommitted = false;
95
+ try {
96
+ const mergeBase = (await gitExec(workDir, ['merge-base', 'HEAD', 'origin/main'])).trim();
97
+ if (mergeBase) {
98
+ const commitDiff = await gitExec(workDir, ['diff', '--name-only', `${mergeBase}..HEAD`]);
99
+ committedFiles = commitDiff.split('\n').filter(Boolean);
100
+ agentAlreadyCommitted = committedFiles.length > 0 && uncommittedFiles.length === 0;
101
+ }
102
+ }
103
+ catch {
104
+ // merge-base may fail if main doesn't exist locally — that's fine
105
+ }
106
+ return {
107
+ filesChanged: agentAlreadyCommitted ? committedFiles : uncommittedFiles,
108
+ agentAlreadyCommitted,
109
+ };
110
+ }
111
+ /**
112
+ * Detect writes the agent made into sibling git repositories (i.e., directories
113
+ * adjacent to `workDir` that are themselves git repos). Surfaced as a warning,
114
+ * not a hard failure — the AISDLC-68 task LEGITIMATELY needed to sync into
115
+ * `../ai-sdlc-io/`, and we don't want to forbid that. The orchestrator just
116
+ * needs to surface that the changes exist so the operator knows to commit them
117
+ * separately in the sibling repo.
118
+ *
119
+ * Returns one entry per dirty sibling repo, with the list of changed files.
120
+ * Empty array when no siblings are dirty (the common case).
121
+ */
122
+ export async function detectCrossRepoWrites(workDir) {
123
+ const { readdir, stat } = await import('node:fs/promises');
124
+ const { dirname, join, resolve } = await import('node:path');
125
+ let workTreeRoot;
126
+ try {
127
+ workTreeRoot = await gitExec(workDir, ['rev-parse', '--show-toplevel']);
128
+ }
129
+ catch {
130
+ return [];
131
+ }
132
+ const parent = dirname(resolve(workTreeRoot));
133
+ let entries;
134
+ try {
135
+ entries = await readdir(parent);
136
+ }
137
+ catch {
138
+ return [];
139
+ }
140
+ const writes = [];
141
+ for (const entry of entries) {
142
+ const candidate = join(parent, entry);
143
+ if (resolve(candidate) === resolve(workTreeRoot))
144
+ continue; // skip self
145
+ try {
146
+ const s = await stat(candidate);
147
+ if (!s.isDirectory())
148
+ continue;
149
+ // Quick git-repo check via `rev-parse --is-inside-work-tree`.
150
+ await gitExec(candidate, ['rev-parse', '--is-inside-work-tree']);
151
+ }
152
+ catch {
153
+ continue;
154
+ }
155
+ try {
156
+ const status = await gitExec(candidate, ['status', '--porcelain']);
157
+ if (!status.trim())
158
+ continue;
159
+ // Porcelain v1: first 2 chars are status (XY), then 1+ whitespace, then
160
+ // path. Cannot use slice(3) — gitExec already trimmed the leading space
161
+ // when X is unmodified ("M " becomes "M" after trim, dropping a column
162
+ // and eating the first filename character).
163
+ const files = status
164
+ .split('\n')
165
+ .filter(Boolean)
166
+ .map((line) => {
167
+ const m = line.match(/^.{1,2}\s+(.+)$/);
168
+ return m ? m[1] : line;
169
+ });
170
+ writes.push({ repoPath: candidate, files });
171
+ }
172
+ catch {
173
+ // Couldn't read status — skip silently.
174
+ }
175
+ }
176
+ return writes;
177
+ }
178
+ /**
179
+ * Run lint and format auto-fix commands (best-effort, non-fatal).
180
+ */
181
+ export async function runAutoFix(workDir, lintCmd, fmtCmd) {
182
+ if (fmtCmd) {
183
+ try {
184
+ const [bin, ...args] = fmtCmd.split(' ');
185
+ await execFileAsync(bin, args, { cwd: workDir });
186
+ }
187
+ catch {
188
+ // Format failures are non-fatal
189
+ }
190
+ }
191
+ if (lintCmd) {
192
+ try {
193
+ const [bin, ...args] = lintCmd.split(' ');
194
+ await execFileAsync(bin, args, { cwd: workDir });
195
+ }
196
+ catch {
197
+ // Lint --fix failures are non-fatal
198
+ }
199
+ }
200
+ }
201
+ //# sourceMappingURL=git-utils.js.map
@@ -1,5 +1,7 @@
1
1
  export type { AgentRunner, AgentContext, AgentResult, AgentProgressEvent, TokenUsage, } from './types.js';
2
2
  export { ClaudeCodeRunner, GitHubActionsRunner } from './claude-code.js';
3
+ export { ClaudeCodeSdkRunner } from './claude-code-sdk.js';
4
+ export { gitExec, detectChangedFiles, runAutoFix, type DetectedChanges } from './git-utils.js';
3
5
  export { GenericLLMRunner, type GenericLLMConfig, type ChatCompletionResponse, } from './generic-llm.js';
4
6
  export { CopilotRunner } from './copilot.js';
5
7
  export { CursorRunner } from './cursor.js';
@@ -7,4 +9,5 @@ export { CodexRunner } from './codex.js';
7
9
  export { RunnerRegistry, createRunnerRegistry, type RegisteredRunner } from './runner-registry.js';
8
10
  export { SecurityTriageRunner, type SecurityTriageConfig, type TriageVerdict, TRIAGE_SYSTEM_PROMPT, } from './security-triage.js';
9
11
  export { ReviewAgentRunner, REVIEW_PROMPTS, type ReviewAgentConfig, type ReviewType, type ReviewFinding, type ReviewVerdict, } from './review-agent.js';
12
+ export { runParallelSdkReviews, DEFAULT_REVIEW_CONFIGS, type SdkReviewConfig, type SdkParallelReviewOptions, type SdkParallelReviewResult, } from './sdk-review-runner.js';
10
13
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,6 @@
1
1
  export { ClaudeCodeRunner, GitHubActionsRunner } from './claude-code.js';
2
+ export { ClaudeCodeSdkRunner } from './claude-code-sdk.js';
3
+ export { gitExec, detectChangedFiles, runAutoFix } from './git-utils.js';
2
4
  export { GenericLLMRunner, } from './generic-llm.js';
3
5
  export { CopilotRunner } from './copilot.js';
4
6
  export { CursorRunner } from './cursor.js';
@@ -6,4 +8,5 @@ export { CodexRunner } from './codex.js';
6
8
  export { RunnerRegistry, createRunnerRegistry } from './runner-registry.js';
7
9
  export { SecurityTriageRunner, TRIAGE_SYSTEM_PROMPT, } from './security-triage.js';
8
10
  export { ReviewAgentRunner, REVIEW_PROMPTS, } from './review-agent.js';
11
+ export { runParallelSdkReviews, DEFAULT_REVIEW_CONFIGS, } from './sdk-review-runner.js';
9
12
  //# sourceMappingURL=index.js.map
@@ -13,6 +13,15 @@ export interface ReviewFinding {
13
13
  file?: string;
14
14
  line?: number;
15
15
  message: string;
16
+ /** Self-assessed confidence score (0-1). Findings below 0.5 are suppressed. */
17
+ confidence?: number;
18
+ /** Category of the finding. */
19
+ category?: 'logic-error' | 'security' | 'design' | 'performance' | 'testing' | 'other';
20
+ /** Evidence supporting the finding. Required for critical/major. */
21
+ evidence?: {
22
+ codePathTraced?: string;
23
+ failureScenario?: string;
24
+ };
16
25
  }
17
26
  export interface ReviewVerdict {
18
27
  type: ReviewType;
@@ -27,6 +36,17 @@ export interface ReviewAgentConfig {
27
36
  apiKey?: string;
28
37
  /** Model to use. Defaults to claude-sonnet-4-5. */
29
38
  model?: string;
39
+ /**
40
+ * Model to escalate to when the input exceeds the large-context threshold.
41
+ * Defaults to AI_SDLC_REVIEW_LARGE_MODEL env var, then claude-opus-4-7.
42
+ */
43
+ largeContextModel?: string;
44
+ /**
45
+ * Char-count threshold above which the runner switches to `largeContextModel`
46
+ * and sets the Anthropic 1M-context beta header. Default ~150k tokens
47
+ * (the standard Anthropic context limit) at the 4-chars-per-token heuristic.
48
+ */
49
+ largeContextThresholdChars?: number;
30
50
  /** Request timeout in ms. Defaults to 120_000. */
31
51
  timeoutMs?: number;
32
52
  /** Which review perspective to use. */
@@ -7,59 +7,140 @@
7
7
  * pattern exactly.
8
8
  */
9
9
  import { DEFAULT_ANTHROPIC_API_URL, DEFAULT_ANTHROPIC_MODEL, DEFAULT_LLM_TIMEOUT_MS, } from '../defaults.js';
10
+ /** Minimum confidence to include a finding in the verdict. */
11
+ const CONFIDENCE_THRESHOLD = 0.5;
12
+ /**
13
+ * Default escalation threshold. Anthropic's standard context window is 200k tokens;
14
+ * we leave headroom for the system prompt + response and trigger escalation around
15
+ * 150k tokens (≈ 600k chars at the 4-char/token heuristic). The user's recurring
16
+ * "PR too large for review" failure on PR #67 happened above this threshold.
17
+ */
18
+ const DEFAULT_LARGE_CONTEXT_THRESHOLD_CHARS = 600_000;
19
+ const DEFAULT_LARGE_CONTEXT_MODEL = process.env.AI_SDLC_REVIEW_LARGE_MODEL ?? 'claude-opus-4-7';
20
+ /** Anthropic 1M-context beta header. Required when sending > 200k tokens. */
21
+ const ANTHROPIC_LONG_CONTEXT_BETA = 'context-1m-2025-08-07';
22
+ // ── CI boundary ─────────────────────────────────────────────────────
23
+ /**
24
+ * Declarative CI boundary — tells review agents what CI already validates.
25
+ * Agents MUST NOT duplicate findings for issues CI catches deterministically.
26
+ * Prepended to every REVIEW_PROMPTS entry.
27
+ */
28
+ const CI_BOUNDARY_PREAMBLE = `## CI Boundary — What You Must NOT Flag
29
+
30
+ The following checks run deterministically in CI on every PR. They are authoritative.
31
+ Do NOT flag issues that these checks catch — they run independently and will pass or fail
32
+ on their own. If CI covers it, it is OUT OF YOUR SCOPE.
33
+
34
+ **CI checks (deterministic, authoritative):**
35
+ - **Lint (ESLint)**: All lint violations, unused imports, naming conventions
36
+ - **Format (Prettier)**: All formatting — whitespace, semicolons, commas, line length
37
+ - **TypeScript typecheck (pnpm build)**: Type errors, missing types, generics
38
+ - **Unit tests (Vitest)**: Test failures, broken assertions
39
+ - **Coverage (Codecov patch)**: Line coverage on changed code (80% patch target)
40
+ - **Schema validation**: YAML/JSON schema conformance
41
+
42
+ **Your job is to find issues CI CANNOT catch:**
43
+ - Logic errors that pass type checking but produce wrong results
44
+ - Security vulnerabilities (injection, auth bypass, credential exposure)
45
+ - Missing error handling for edge cases that tests don't cover
46
+ - Design problems (wrong abstraction, pattern violations)
47
+ - Race conditions and concurrency issues
48
+ - Performance anti-patterns (N+1 queries, unbounded allocations)
49
+ - Acceptance criteria not addressed by the implementation
50
+
51
+ **If unsure whether CI catches something, do NOT flag it.**
52
+
53
+ `;
10
54
  // ── System prompts ───────────────────────────────────────────────────
11
55
  const REVIEW_PROMPTS = {
12
- testing: `You are a testing review agent analyzing a pull request diff. Your job is to verify that the changes are well-tested and that acceptance criteria are met.
56
+ testing: `${CI_BOUNDARY_PREAMBLE}You are a testing review agent analyzing a pull request diff. Your job is to verify that the changes are well-tested and that acceptance criteria are met.
13
57
 
14
58
  Analyze the diff and any provided acceptance criteria. Check for:
15
- 1. **Test coverage**: Are new/changed code paths covered by tests?
59
+ 1. **Untested logic paths**: Are there logic branches that existing tests don't exercise? (Do NOT flag coverage percentages — Codecov handles that.)
16
60
  2. **Acceptance criteria**: If provided, are all acceptance criteria addressed?
17
61
  3. **Edge cases**: Are boundary conditions and error paths tested?
18
62
  4. **Test quality**: Are tests meaningful (not just asserting true)?
19
- 5. **Missing tests**: Are there obvious test gaps for the changed code?
63
+ 5. **Missing edge-case tests**: Are there missing tests for error paths and boundary conditions that the test suite cannot catch?
64
+
65
+ Do NOT flag: coverage percentages, missing tests for config/YAML files, type-only files, or barrel exports. Codecov and CI handle these.
66
+
67
+ ## Structured Output Format
20
68
 
21
- Respond with ONLY a JSON object (no markdown, no code fences):
69
+ Respond with ONLY a JSON object (no markdown, no code fences).
70
+
71
+ Each finding MUST include a confidence score (0.0-1.0) and a category.
72
+ For critical/major findings, you MUST include evidence with a concrete failure scenario.
73
+ **No evidence = no critical/major finding.** If you cannot trace the code path to a failure, downgrade to minor/suggestion.
74
+ Findings below 0.5 confidence will be automatically suppressed.
22
75
 
23
76
  {
24
77
  "approved": true/false,
25
78
  "findings": [
26
- {"severity": "critical|major|minor|suggestion", "file": "path/to/file.ts", "line": 42, "message": "description"}
79
+ {
80
+ "severity": "critical|major|minor|suggestion",
81
+ "confidence": 0.0-1.0,
82
+ "category": "testing|logic-error|security|design|performance|other",
83
+ "file": "path/to/file.ts",
84
+ "line": 42,
85
+ "evidence": {
86
+ "codePathTraced": "Function X calls Y which can return null when Z",
87
+ "failureScenario": "When input is empty, line 42 throws TypeError"
88
+ },
89
+ "message": "Concise description of the issue"
90
+ }
27
91
  ],
28
92
  "summary": "1-2 sentence overall assessment"
29
93
  }
30
94
 
31
95
  Severity guide:
32
- - critical: Missing tests for critical paths, acceptance criteria not met
33
- - major: Significant test gaps for changed code
96
+ - critical: Missing tests for critical logic paths, acceptance criteria not met. MUST have failureScenario.
97
+ - major: Significant untested logic branches. MUST have failureScenario.
34
98
  - minor: Minor test improvements possible
35
99
  - suggestion: Nice-to-have test additions`,
36
- critic: `You are a code quality review agent analyzing a pull request diff. Your job is to identify code quality issues, logic errors, and design problems.
100
+ critic: `${CI_BOUNDARY_PREAMBLE}You are a code quality review agent analyzing a pull request diff. Your job is to identify logic errors and design problems.
37
101
 
38
102
  Analyze the diff for:
39
103
  1. **Logic errors**: Incorrect conditions, off-by-one errors, race conditions
40
- 2. **Code quality**: Naming, readability, unnecessary complexity
41
- 3. **Error handling**: Missing error cases, swallowed exceptions
42
- 4. **Design patterns**: Violations of existing project patterns/conventions
43
- 5. **Performance**: Obvious inefficiencies (N+1 queries, unnecessary allocations)
104
+ 2. **Design issues**: Wrong abstraction, unnecessary complexity, pattern violations
105
+ 3. **Error handling**: Missing error cases at system boundaries
106
+ 4. **Performance**: Obvious inefficiencies (N+1 queries, unbounded allocations)
107
+
108
+ Do NOT flag: style issues (formatting, whitespace, import order), type errors, lint violations, or naming conventions. ESLint, Prettier, and TypeScript handle these deterministically.
44
109
 
45
- Do NOT flag style-only issues (formatting, trailing whitespace). Focus on substantive issues.
110
+ ## Structured Output Format
46
111
 
47
- Respond with ONLY a JSON object (no markdown, no code fences):
112
+ Respond with ONLY a JSON object (no markdown, no code fences).
113
+
114
+ Each finding MUST include a confidence score (0.0-1.0) and a category.
115
+ For critical/major findings, you MUST include evidence with a concrete failure scenario.
116
+ **No evidence = no critical/major finding.** If you cannot trace the code path to a failure, downgrade to minor/suggestion.
117
+ Findings below 0.5 confidence will be automatically suppressed.
48
118
 
49
119
  {
50
120
  "approved": true/false,
51
121
  "findings": [
52
- {"severity": "critical|major|minor|suggestion", "file": "path/to/file.ts", "line": 42, "message": "description"}
122
+ {
123
+ "severity": "critical|major|minor|suggestion",
124
+ "confidence": 0.0-1.0,
125
+ "category": "logic-error|design|performance|other",
126
+ "file": "path/to/file.ts",
127
+ "line": 42,
128
+ "evidence": {
129
+ "codePathTraced": "Function X calls Y which can return null when Z",
130
+ "failureScenario": "When input is empty, line 42 throws TypeError"
131
+ },
132
+ "message": "Concise description of the issue"
133
+ }
53
134
  ],
54
135
  "summary": "1-2 sentence overall assessment"
55
136
  }
56
137
 
57
138
  Severity guide:
58
- - critical: Logic errors, data loss risks, broken functionality
59
- - major: Significant quality issues that should be fixed before merge
139
+ - critical: Logic errors, data loss risks, broken functionality. MUST have failureScenario.
140
+ - major: Significant design issues that should be fixed before merge. MUST have failureScenario.
60
141
  - minor: Improvements that would make the code better
61
142
  - suggestion: Optional enhancements`,
62
- security: `You are a security review agent analyzing a pull request diff. Your job is to identify security vulnerabilities in the changed code.
143
+ security: `${CI_BOUNDARY_PREAMBLE}You are a security review agent analyzing a pull request diff. Your job is to identify security vulnerabilities in the changed code.
63
144
 
64
145
  Analyze the diff for:
65
146
  1. **Injection vulnerabilities**: SQL injection, command injection, XSS, template injection
@@ -68,21 +149,40 @@ Analyze the diff for:
68
149
  4. **Path traversal**: Unsanitized file paths, directory traversal
69
150
  5. **Unsafe deserialization**: JSON.parse on untrusted input without validation
70
151
  6. **Dependency issues**: Known vulnerable patterns, unsafe API usage
71
- 7. **Information disclosure**: Verbose error messages, stack traces in responses
72
152
 
73
- Respond with ONLY a JSON object (no markdown, no code fences):
153
+ Do NOT flag: type safety issues (TypeScript handles these), or issues in trusted internal code paths (config files, env vars set by the platform).
154
+
155
+ ## Structured Output Format
156
+
157
+ Respond with ONLY a JSON object (no markdown, no code fences).
158
+
159
+ Each finding MUST include a confidence score (0.0-1.0) and a category.
160
+ For critical/major findings, you MUST include evidence with a concrete failure scenario.
161
+ **No evidence = no critical/major finding.** If you cannot describe a realistic attack vector, downgrade to minor/suggestion.
162
+ Findings below 0.5 confidence will be automatically suppressed.
74
163
 
75
164
  {
76
165
  "approved": true/false,
77
166
  "findings": [
78
- {"severity": "critical|major|minor|suggestion", "file": "path/to/file.ts", "line": 42, "message": "description"}
167
+ {
168
+ "severity": "critical|major|minor|suggestion",
169
+ "confidence": 0.0-1.0,
170
+ "category": "security|other",
171
+ "file": "path/to/file.ts",
172
+ "line": 42,
173
+ "evidence": {
174
+ "codePathTraced": "User input flows from X to Y without sanitization",
175
+ "failureScenario": "Attacker sends malicious input via Z, causing command injection at line 42"
176
+ },
177
+ "message": "Concise description of the vulnerability"
178
+ }
79
179
  ],
80
180
  "summary": "1-2 sentence overall assessment"
81
181
  }
82
182
 
83
183
  Severity guide:
84
- - critical: Exploitable vulnerability (injection, credential leak, auth bypass)
85
- - major: Security weakness that should be fixed (missing validation, unsafe patterns)
184
+ - critical: Exploitable vulnerability (injection, credential leak, auth bypass). MUST have failureScenario with attack vector.
185
+ - major: Security weakness that should be fixed (missing validation, unsafe patterns). MUST have failureScenario.
86
186
  - minor: Defense-in-depth improvement
87
187
  - suggestion: Security hardening opportunity`,
88
188
  };
@@ -135,24 +235,37 @@ export class ReviewAgentRunner {
135
235
  }
136
236
  async callAPI(apiKey, userContent) {
137
237
  const apiUrl = this.config.apiUrl ?? DEFAULT_ANTHROPIC_API_URL;
138
- const model = this.config.model ?? DEFAULT_ANTHROPIC_MODEL;
238
+ const baseModel = this.config.model ?? DEFAULT_ANTHROPIC_MODEL;
139
239
  const timeoutMs = this.config.timeoutMs ?? DEFAULT_LLM_TIMEOUT_MS;
240
+ const system = this.config.reviewPolicy
241
+ ? `${this.config.reviewPolicy}\n\n---\n\n${REVIEW_PROMPTS[this.config.reviewType]}`
242
+ : REVIEW_PROMPTS[this.config.reviewType];
243
+ // Escalate to a 1M-context model when the input is large enough to risk
244
+ // overflowing the standard 200k-token window. The signal we use is char count
245
+ // of (system + user) since precise tokenization isn't available client-side.
246
+ const threshold = this.config.largeContextThresholdChars ?? DEFAULT_LARGE_CONTEXT_THRESHOLD_CHARS;
247
+ const inputChars = system.length + userContent.length;
248
+ const escalate = inputChars > threshold;
249
+ const model = escalate
250
+ ? (this.config.largeContextModel ?? DEFAULT_LARGE_CONTEXT_MODEL)
251
+ : baseModel;
140
252
  const controller = new AbortController();
141
253
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
142
254
  try {
255
+ const headers = {
256
+ 'Content-Type': 'application/json',
257
+ 'x-api-key': apiKey,
258
+ 'anthropic-version': '2023-06-01',
259
+ };
260
+ if (escalate)
261
+ headers['anthropic-beta'] = ANTHROPIC_LONG_CONTEXT_BETA;
143
262
  const res = await fetch(apiUrl, {
144
263
  method: 'POST',
145
- headers: {
146
- 'Content-Type': 'application/json',
147
- 'x-api-key': apiKey,
148
- 'anthropic-version': '2023-06-01',
149
- },
264
+ headers,
150
265
  body: JSON.stringify({
151
266
  model,
152
267
  max_tokens: 4096,
153
- system: this.config.reviewPolicy
154
- ? `${this.config.reviewPolicy}\n\n---\n\n${REVIEW_PROMPTS[this.config.reviewType]}`
155
- : REVIEW_PROMPTS[this.config.reviewType],
268
+ system,
156
269
  messages: [{ role: 'user', content: userContent }],
157
270
  }),
158
271
  signal: controller.signal,
@@ -182,16 +295,33 @@ export class ReviewAgentRunner {
182
295
  const cleaned = text.replace(/^```(?:json)?\s*/m, '').replace(/\s*```$/m, '');
183
296
  try {
184
297
  const parsed = JSON.parse(cleaned);
185
- const findings = Array.isArray(parsed.findings)
186
- ? parsed.findings.map((f) => ({
187
- severity: ['critical', 'major', 'minor', 'suggestion'].includes(String(f.severity))
188
- ? String(f.severity)
189
- : 'minor',
190
- file: f.file ? String(f.file) : undefined,
191
- line: typeof f.line === 'number' ? f.line : undefined,
192
- message: String(f.message ?? ''),
193
- }))
298
+ const rawFindings = Array.isArray(parsed.findings)
299
+ ? parsed.findings.map((f) => {
300
+ const evidence = f.evidence;
301
+ return {
302
+ severity: ['critical', 'major', 'minor', 'suggestion'].includes(String(f.severity))
303
+ ? String(f.severity)
304
+ : 'minor',
305
+ file: f.file ? String(f.file) : undefined,
306
+ line: typeof f.line === 'number' ? f.line : undefined,
307
+ message: String(f.message ?? ''),
308
+ confidence: typeof f.confidence === 'number' ? f.confidence : undefined,
309
+ category: f.category ? String(f.category) : undefined,
310
+ evidence: evidence
311
+ ? {
312
+ codePathTraced: evidence.codePathTraced
313
+ ? String(evidence.codePathTraced)
314
+ : undefined,
315
+ failureScenario: evidence.failureScenario
316
+ ? String(evidence.failureScenario)
317
+ : undefined,
318
+ }
319
+ : undefined,
320
+ };
321
+ })
194
322
  : [];
323
+ // Filter out low-confidence findings
324
+ const findings = rawFindings.filter((f) => f.confidence === undefined || f.confidence >= CONFIDENCE_THRESHOLD);
195
325
  return {
196
326
  type: this.config.reviewType,
197
327
  approved: Boolean(parsed.approved),
@@ -4,6 +4,7 @@
4
4
  * Design decision D4: Registry auto-discovers available runners from environment.
5
5
  */
6
6
  import { ClaudeCodeRunner } from './claude-code.js';
7
+ import { ClaudeCodeSdkRunner } from './claude-code-sdk.js';
7
8
  import { GenericLLMRunner } from './generic-llm.js';
8
9
  import { CopilotRunner } from './copilot.js';
9
10
  import { CursorRunner } from './cursor.js';
@@ -70,6 +71,15 @@ export class RunnerRegistry {
70
71
  source: 'built-in',
71
72
  });
72
73
  }
74
+ // Claude Code SDK runner — available when @anthropic-ai/claude-agent-sdk is installed
75
+ if (!this.runners.has('claude-code-sdk')) {
76
+ this.runners.set('claude-code-sdk', {
77
+ name: 'claude-code-sdk',
78
+ runner: new ClaudeCodeSdkRunner(),
79
+ available: true,
80
+ source: 'built-in',
81
+ });
82
+ }
73
83
  // OpenAI-compatible runner from env
74
84
  const openaiKey = env.OPENAI_API_KEY;
75
85
  if (openaiKey && !this.runners.has('openai')) {