@ai-sdlc/orchestrator 0.6.0 → 0.10.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.
- package/dist/__test-helpers/git-env.d.ts +40 -0
- package/dist/__test-helpers/git-env.js +73 -0
- package/dist/admission-composite.d.ts +89 -0
- package/dist/admission-composite.js +241 -0
- package/dist/admission-enrichment.d.ts +142 -0
- package/dist/admission-enrichment.js +341 -0
- package/dist/admission-hc.d.ts +81 -0
- package/dist/admission-hc.js +94 -0
- package/dist/admission-score.d.ts +109 -5
- package/dist/admission-score.js +90 -11
- package/dist/analysis/analyzer.js +3 -2
- package/dist/analysis/convention-detector.d.ts +85 -2
- package/dist/analysis/convention-detector.js +375 -70
- package/dist/analysis/diff-analyzer.d.ts +33 -0
- package/dist/analysis/diff-analyzer.js +122 -0
- package/dist/analysis/hotspot-analyzer.js +3 -1
- package/dist/analysis/index.d.ts +2 -1
- package/dist/analysis/index.js +2 -1
- package/dist/artifacts/index.d.ts +65 -0
- package/dist/artifacts/index.js +142 -0
- package/dist/backlog-adapter.d.ts +121 -0
- package/dist/backlog-adapter.js +438 -0
- package/dist/calibration.d.ts +43 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/agents.d.ts +24 -0
- package/dist/cli/commands/agents.js +66 -1
- package/dist/cli/commands/git-remote.d.ts +74 -0
- package/dist/cli/commands/git-remote.js +170 -0
- package/dist/cli/commands/health.d.ts +4 -0
- package/dist/cli/commands/health.js +13 -1
- package/dist/cli/commands/init-features.d.ts +208 -0
- package/dist/cli/commands/init-features.js +473 -0
- package/dist/cli/commands/init-templates.d.ts +104 -0
- package/dist/cli/commands/init-templates.js +399 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.js +322 -23
- package/dist/cli/commands/mcp-setup.d.ts +31 -0
- package/dist/cli/commands/mcp-setup.js +78 -8
- package/dist/cli/formatters/table.js +15 -2
- package/dist/cli/index.d.ts +14 -1
- package/dist/cli/index.js +81 -20
- package/dist/cli/versions.d.ts +57 -0
- package/dist/cli/versions.js +128 -0
- package/dist/code-area-classifier.d.ts +21 -0
- package/dist/code-area-classifier.js +48 -0
- package/dist/config.d.ts +33 -1
- package/dist/config.js +78 -8
- package/dist/database/adapters/external.d.ts +24 -0
- package/dist/database/adapters/external.js +80 -0
- package/dist/database/adapters/neon.d.ts +41 -0
- package/dist/database/adapters/neon.js +98 -0
- package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
- package/dist/database/adapters/pg-snapshot-restore.js +68 -0
- package/dist/database/adapters/sqlite-copy.d.ts +32 -0
- package/dist/database/adapters/sqlite-copy.js +145 -0
- package/dist/database/connection-injection.d.ts +35 -0
- package/dist/database/connection-injection.js +93 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.js +23 -0
- package/dist/database/registry.d.ts +13 -0
- package/dist/database/registry.js +27 -0
- package/dist/database/topology.d.ts +52 -0
- package/dist/database/topology.js +44 -0
- package/dist/database/types.d.ts +89 -0
- package/dist/database/types.js +26 -0
- package/dist/design-authority.d.ts +53 -0
- package/dist/design-authority.js +84 -0
- package/dist/design-lookahead.d.ts +64 -0
- package/dist/design-lookahead.js +86 -0
- package/dist/design-quality-trend.d.ts +87 -0
- package/dist/design-quality-trend.js +190 -0
- package/dist/design-system-context.d.ts +46 -0
- package/dist/design-system-context.js +80 -0
- package/dist/design-system-correction-loop.d.ts +64 -0
- package/dist/design-system-correction-loop.js +128 -0
- package/dist/design-system-metrics.d.ts +61 -0
- package/dist/design-system-metrics.js +104 -0
- package/dist/design-system-stewardship.d.ts +22 -0
- package/dist/design-system-stewardship.js +85 -0
- package/dist/design-system-validation.d.ts +37 -0
- package/dist/design-system-validation.js +88 -0
- package/dist/dispatch/index.d.ts +4 -0
- package/dist/dispatch/index.js +4 -0
- package/dist/dispatch/merge-gate.d.ts +46 -0
- package/dist/dispatch/merge-gate.js +90 -0
- package/dist/dispatch/requeue.d.ts +57 -0
- package/dist/dispatch/requeue.js +131 -0
- package/dist/dispatch/worker-pool.d.ts +62 -0
- package/dist/dispatch/worker-pool.js +60 -0
- package/dist/execute.d.ts +40 -0
- package/dist/execute.js +188 -15
- package/dist/fix-ci.js +8 -1
- package/dist/fix-review.js +8 -1
- package/dist/harness/adapters/claude-code.d.ts +29 -0
- package/dist/harness/adapters/claude-code.js +191 -0
- package/dist/harness/adapters/codex.d.ts +25 -0
- package/dist/harness/adapters/codex.js +61 -0
- package/dist/harness/independence.d.ts +51 -0
- package/dist/harness/independence.js +67 -0
- package/dist/harness/index.d.ts +14 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/registry.d.ts +17 -0
- package/dist/harness/registry.js +31 -0
- package/dist/harness/types.d.ts +123 -0
- package/dist/harness/types.js +8 -0
- package/dist/harness/version-probe.d.ts +14 -0
- package/dist/harness/version-probe.js +123 -0
- package/dist/index.d.ts +30 -4
- package/dist/index.js +35 -2
- package/dist/models/classifier.d.ts +78 -0
- package/dist/models/classifier.js +277 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +3 -0
- package/dist/models/registry.d.ts +97 -0
- package/dist/models/registry.js +173 -0
- package/dist/pillar-breakdown.d.ts +85 -0
- package/dist/pillar-breakdown.js +162 -0
- package/dist/priority.d.ts +5 -0
- package/dist/priority.js +18 -5
- package/dist/review-meta.d.ts +65 -0
- package/dist/review-meta.js +149 -0
- package/dist/review.d.ts +4 -0
- package/dist/review.js +12 -2
- package/dist/runners/claude-code-sdk.d.ts +31 -0
- package/dist/runners/claude-code-sdk.js +238 -0
- package/dist/runners/claude-code.d.ts +3 -0
- package/dist/runners/claude-code.js +30 -66
- package/dist/runners/codex.js +4 -1
- package/dist/runners/copilot.js +4 -1
- package/dist/runners/cursor.js +4 -1
- package/dist/runners/git-utils.d.ts +81 -0
- package/dist/runners/git-utils.js +201 -0
- package/dist/runners/index.d.ts +3 -0
- package/dist/runners/index.js +3 -0
- package/dist/runners/review-agent.d.ts +20 -0
- package/dist/runners/review-agent.js +171 -41
- package/dist/runners/runner-registry.js +10 -0
- package/dist/runners/sdk-review-runner.d.ts +65 -0
- package/dist/runners/sdk-review-runner.js +185 -0
- package/dist/runners/security-triage.d.ts +20 -4
- package/dist/runners/security-triage.js +39 -15
- package/dist/runners/types.d.ts +6 -0
- package/dist/runtime/attestations.d.ts +766 -0
- package/dist/runtime/attestations.js +1195 -0
- package/dist/runtime/git-env.d.ts +53 -0
- package/dist/runtime/git-env.js +60 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/parallelism-flag.d.ts +28 -0
- package/dist/runtime/parallelism-flag.js +39 -0
- package/dist/runtime/port-allocator.d.ts +32 -0
- package/dist/runtime/port-allocator.js +96 -0
- package/dist/runtime/worktree-pool.d.ts +86 -0
- package/dist/runtime/worktree-pool.js +204 -0
- package/dist/runtime/worktree.d.ts +25 -0
- package/dist/runtime/worktree.js +111 -0
- package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
- package/dist/sa-scoring/auto-calibrate.js +107 -0
- package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
- package/dist/sa-scoring/c1-sa2-computable.js +59 -0
- package/dist/sa-scoring/composite.d.ts +107 -0
- package/dist/sa-scoring/composite.js +139 -0
- package/dist/sa-scoring/depparse-client.d.ts +79 -0
- package/dist/sa-scoring/depparse-client.js +187 -0
- package/dist/sa-scoring/did-compiler.d.ts +122 -0
- package/dist/sa-scoring/did-compiler.js +286 -0
- package/dist/sa-scoring/drift-monitor.d.ts +84 -0
- package/dist/sa-scoring/drift-monitor.js +186 -0
- package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
- package/dist/sa-scoring/exemplar-bank.js +154 -0
- package/dist/sa-scoring/feedback-store.d.ts +100 -0
- package/dist/sa-scoring/feedback-store.js +156 -0
- package/dist/sa-scoring/index.d.ts +71 -0
- package/dist/sa-scoring/index.js +158 -0
- package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
- package/dist/sa-scoring/layer1-deterministic.js +298 -0
- package/dist/sa-scoring/layer2-structural.d.ts +71 -0
- package/dist/sa-scoring/layer2-structural.js +151 -0
- package/dist/sa-scoring/layer3-llm.d.ts +86 -0
- package/dist/sa-scoring/layer3-llm.js +282 -0
- package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
- package/dist/sa-scoring/rescore-orchestrator.js +47 -0
- package/dist/scheduling/burn-down.d.ts +27 -0
- package/dist/scheduling/burn-down.js +43 -0
- package/dist/scheduling/calibration.d.ts +42 -0
- package/dist/scheduling/calibration.js +150 -0
- package/dist/scheduling/index.d.ts +8 -0
- package/dist/scheduling/index.js +8 -0
- package/dist/scheduling/ledger.d.ts +59 -0
- package/dist/scheduling/ledger.js +216 -0
- package/dist/scheduling/off-peak.d.ts +27 -0
- package/dist/scheduling/off-peak.js +112 -0
- package/dist/scheduling/schedule-decision.d.ts +39 -0
- package/dist/scheduling/schedule-decision.js +89 -0
- package/dist/scheduling/tier-analysis.d.ts +47 -0
- package/dist/scheduling/tier-analysis.js +81 -0
- package/dist/scheduling/types.d.ts +140 -0
- package/dist/scheduling/types.js +11 -0
- package/dist/shared.d.ts +13 -0
- package/dist/shared.js +32 -0
- package/dist/state/schema.d.ts +5 -1
- package/dist/state/schema.js +223 -1
- package/dist/state/store.d.ts +55 -1
- package/dist/state/store.js +342 -7
- package/dist/state/types.d.ts +139 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate-agent-output.js +4 -1
- package/dist/watch.js +6 -0
- package/package.json +3 -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
|
package/dist/runners/index.d.ts
CHANGED
|
@@ -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
|
package/dist/runners/index.js
CHANGED
|
@@ -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:
|
|
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. **
|
|
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
|
|
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
|
-
{
|
|
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
|
|
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:
|
|
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. **
|
|
41
|
-
3. **Error handling**: Missing error cases
|
|
42
|
-
4. **
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
|
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
|
|
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
|
|
186
|
-
? parsed.findings.map((f) =>
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
: 'minor',
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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')) {
|