@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.
- package/dist/admission-composite.d.ts +67 -0
- package/dist/admission-composite.js +158 -0
- package/dist/admission-enrichment.d.ts +142 -0
- package/dist/admission-enrichment.js +331 -0
- package/dist/admission-hc.d.ts +62 -0
- package/dist/admission-hc.js +83 -0
- package/dist/admission-score.d.ts +95 -5
- package/dist/admission-score.js +84 -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 +49 -0
- package/dist/cli/commands/git-remote.js +91 -0
- package/dist/cli/commands/health.d.ts +4 -0
- package/dist/cli/commands/health.js +13 -1
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.js +164 -22
- 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 +40 -0
- package/dist/design-authority.js +71 -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 +185 -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 +76 -0
- package/dist/models/classifier.js +221 -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 +63 -0
- package/dist/pillar-breakdown.js +153 -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 +274 -0
- package/dist/runtime/attestations.js +460 -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 +10 -0
- package/dist/runtime/parallelism-flag.js +18 -0
- package/dist/runtime/port-allocator.d.ts +21 -0
- package/dist/runtime/port-allocator.js +66 -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 +2 -2
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Meta-review pass — filters medium-confidence findings through a
|
|
3
|
+
* lightweight LLM call (Haiku) to reduce false positives.
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* High confidence (>0.8) → post directly
|
|
7
|
+
* Medium (0.5-0.8) → meta-review decides keep/drop
|
|
8
|
+
* Low (<0.5) → already suppressed by parseVerdict()
|
|
9
|
+
*/
|
|
10
|
+
// ── Thresholds ───────────────────────────────────────────────────────
|
|
11
|
+
const HIGH_CONFIDENCE = 0.8;
|
|
12
|
+
// ── Meta-reviewer ────────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Run a meta-review pass on medium-confidence findings.
|
|
15
|
+
*
|
|
16
|
+
* @param verdict - The original verdict from a review agent
|
|
17
|
+
* @param principles - Review principles text for context
|
|
18
|
+
* @param callLLM - Function to make a single-turn LLM call (injectable for testing)
|
|
19
|
+
*/
|
|
20
|
+
export async function metaReview(verdict, principles, callLLM) {
|
|
21
|
+
const highConfidence = [];
|
|
22
|
+
const mediumConfidence = [];
|
|
23
|
+
for (const finding of verdict.findings) {
|
|
24
|
+
const conf = finding.confidence ?? 1.0; // legacy findings without confidence pass through
|
|
25
|
+
if (conf >= HIGH_CONFIDENCE) {
|
|
26
|
+
highConfidence.push(finding);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
mediumConfidence.push(finding);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// If no medium-confidence findings, return as-is
|
|
33
|
+
if (mediumConfidence.length === 0) {
|
|
34
|
+
return {
|
|
35
|
+
verdict,
|
|
36
|
+
decisions: [],
|
|
37
|
+
suppressed: 0,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Review each medium-confidence finding
|
|
41
|
+
const decisions = [];
|
|
42
|
+
const kept = [...highConfidence];
|
|
43
|
+
for (const finding of mediumConfidence) {
|
|
44
|
+
try {
|
|
45
|
+
const decision = await reviewSingleFinding(finding, principles, callLLM);
|
|
46
|
+
decisions.push({ finding, decision });
|
|
47
|
+
if (decision.keep) {
|
|
48
|
+
kept.push({
|
|
49
|
+
...finding,
|
|
50
|
+
severity: decision.adjustedSeverity ?? finding.severity,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Meta-review failure = keep the finding (conservative)
|
|
56
|
+
kept.push(finding);
|
|
57
|
+
decisions.push({
|
|
58
|
+
finding,
|
|
59
|
+
decision: { keep: true, reason: 'Meta-review failed, keeping conservatively' },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
verdict: {
|
|
65
|
+
...verdict,
|
|
66
|
+
findings: kept,
|
|
67
|
+
// If all findings were suppressed, approve
|
|
68
|
+
approved: kept.length === 0 ? true : verdict.approved,
|
|
69
|
+
},
|
|
70
|
+
decisions,
|
|
71
|
+
suppressed: Math.max(0, mediumConfidence.length - (kept.length - highConfidence.length)),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function reviewSingleFinding(finding, principles, callLLM) {
|
|
75
|
+
const prompt = `You are a meta-reviewer evaluating whether a code review finding should be posted to a pull request.
|
|
76
|
+
|
|
77
|
+
## Review Principles
|
|
78
|
+
${principles}
|
|
79
|
+
|
|
80
|
+
## Finding to evaluate
|
|
81
|
+
- Severity: ${finding.severity}
|
|
82
|
+
- Confidence: ${finding.confidence}
|
|
83
|
+
- Category: ${finding.category ?? 'unknown'}
|
|
84
|
+
- File: ${finding.file ?? 'N/A'}
|
|
85
|
+
- Message: ${finding.message}
|
|
86
|
+
${finding.evidence?.failureScenario ? `- Failure scenario: ${finding.evidence.failureScenario}` : '- No failure scenario provided'}
|
|
87
|
+
${finding.evidence?.codePathTraced ? `- Code path: ${finding.evidence.codePathTraced}` : ''}
|
|
88
|
+
|
|
89
|
+
## Decision criteria
|
|
90
|
+
- Is this a real issue or noise?
|
|
91
|
+
- Does the evidence support the severity?
|
|
92
|
+
- Would a senior engineer flag this?
|
|
93
|
+
- Is this something CI already catches?
|
|
94
|
+
|
|
95
|
+
Respond with ONLY a JSON object:
|
|
96
|
+
{"keep": true/false, "adjustedSeverity": "critical|major|minor|suggestion" (optional), "reason": "brief explanation"}`;
|
|
97
|
+
const response = await callLLM(prompt);
|
|
98
|
+
// Parse response
|
|
99
|
+
const cleaned = response.replace(/^```(?:json)?\s*/m, '').replace(/\s*```$/m, '');
|
|
100
|
+
const parsed = JSON.parse(cleaned);
|
|
101
|
+
return {
|
|
102
|
+
keep: Boolean(parsed.keep),
|
|
103
|
+
adjustedSeverity: parsed.adjustedSeverity ?? undefined,
|
|
104
|
+
reason: String(parsed.reason ?? ''),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Store for tracking human feedback on review findings.
|
|
109
|
+
* Used to calibrate confidence thresholds over time.
|
|
110
|
+
*/
|
|
111
|
+
export class ReviewFeedbackStore {
|
|
112
|
+
entries = [];
|
|
113
|
+
record(feedback) {
|
|
114
|
+
this.entries.push(feedback);
|
|
115
|
+
}
|
|
116
|
+
/** Get all feedback entries. */
|
|
117
|
+
getAll() {
|
|
118
|
+
return [...this.entries];
|
|
119
|
+
}
|
|
120
|
+
/** Get precision: accepted / (accepted + dismissed). */
|
|
121
|
+
precision() {
|
|
122
|
+
const accepted = this.entries.filter((e) => e.signal === 'accepted').length;
|
|
123
|
+
const dismissed = this.entries.filter((e) => e.signal === 'dismissed').length;
|
|
124
|
+
const total = accepted + dismissed;
|
|
125
|
+
return total === 0 ? 1.0 : accepted / total;
|
|
126
|
+
}
|
|
127
|
+
/** Get feedback by category. */
|
|
128
|
+
byCategory() {
|
|
129
|
+
const result = {};
|
|
130
|
+
for (const entry of this.entries) {
|
|
131
|
+
const cat = entry.finding.category ?? 'other';
|
|
132
|
+
if (!result[cat])
|
|
133
|
+
result[cat] = { accepted: 0, dismissed: 0, ignored: 0 };
|
|
134
|
+
result[cat][entry.signal]++;
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
/** Categories with high false-positive rate (dismissed > 50%). */
|
|
139
|
+
highFalsePositiveCategories() {
|
|
140
|
+
const cats = this.byCategory();
|
|
141
|
+
return Object.entries(cats)
|
|
142
|
+
.filter(([, v]) => {
|
|
143
|
+
const total = v.accepted + v.dismissed;
|
|
144
|
+
return total >= 3 && v.dismissed / total > 0.5;
|
|
145
|
+
})
|
|
146
|
+
.map(([k]) => k);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=review-meta.js.map
|
package/dist/review.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export interface ReviewOptions {
|
|
|
16
16
|
logger?: Logger;
|
|
17
17
|
/** Inject runner for testing. */
|
|
18
18
|
runner?: ReviewAgentRunner;
|
|
19
|
+
/** Review principles text for meta-review context. */
|
|
20
|
+
principles?: string;
|
|
21
|
+
/** LLM caller for meta-review pass. If provided, medium-confidence findings are filtered. */
|
|
22
|
+
metaReviewLLM?: (prompt: string) => Promise<string>;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
21
25
|
* Execute a single review agent against a PR diff.
|
package/dist/review.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* that handles context assembly and verdict extraction.
|
|
4
4
|
*/
|
|
5
5
|
import { ReviewAgentRunner, } from './runners/review-agent.js';
|
|
6
|
+
import { metaReview } from './review-meta.js';
|
|
6
7
|
// ── Public API ───────────────────────────────────────────────────────
|
|
7
8
|
/**
|
|
8
9
|
* Execute a single review agent against a PR diff.
|
|
@@ -52,9 +53,18 @@ export async function executeReview(prNumber, diff, reviewType, context, options
|
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
try {
|
|
55
|
-
|
|
56
|
+
let verdict = JSON.parse(result.summary);
|
|
57
|
+
verdict = { ...verdict, type: reviewType };
|
|
58
|
+
// Run meta-review on medium-confidence findings if LLM caller provided
|
|
59
|
+
if (options?.metaReviewLLM && options?.principles) {
|
|
60
|
+
const metaResult = await metaReview(verdict, options.principles, options.metaReviewLLM);
|
|
61
|
+
if (metaResult.suppressed > 0) {
|
|
62
|
+
logger?.info?.(`${reviewType} meta-review: ${metaResult.suppressed} finding(s) suppressed`);
|
|
63
|
+
}
|
|
64
|
+
verdict = metaResult.verdict;
|
|
65
|
+
}
|
|
56
66
|
logger?.info?.(`${reviewType} review complete: ${verdict.approved ? 'APPROVED' : 'CHANGES REQUESTED'} (${verdict.findings.length} findings)`);
|
|
57
|
-
return
|
|
67
|
+
return verdict;
|
|
58
68
|
}
|
|
59
69
|
catch {
|
|
60
70
|
logger?.error?.(`Failed to parse ${reviewType} verdict from runner output`);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code SDK runner — invokes Claude Code via the Agent SDK's query() API
|
|
3
|
+
* instead of spawning a CLI subprocess.
|
|
4
|
+
*
|
|
5
|
+
* Advantages over ClaudeCodeRunner:
|
|
6
|
+
* - maxBudgetUsd / maxTurns — hard limits enforced by the engine
|
|
7
|
+
* - allowedTools / disallowedTools — fine-grained tool filtering with glob patterns
|
|
8
|
+
* - appendSystemPrompt — inject governance without replacing Claude Code defaults
|
|
9
|
+
* - Structured NDJSON messages — no stdout parsing
|
|
10
|
+
*/
|
|
11
|
+
import type { AgentRunner, AgentContext, AgentResult } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Map agent-role tool names to SDK-compatible tool filter patterns.
|
|
14
|
+
*/
|
|
15
|
+
/** @internal Exported for testing. */
|
|
16
|
+
export declare function mapToolsToSdkFormat(allowedTools?: string[]): string[] | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Build tool deny-list from blocked actions.
|
|
19
|
+
* Converts blockedActions glob patterns to SDK disallowedTools format.
|
|
20
|
+
*/
|
|
21
|
+
/** @internal Exported for testing. */
|
|
22
|
+
export declare function mapBlockedActionsToSdkDenyList(blockedActions?: string[]): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Build governance system prompt appendix from constraints.
|
|
25
|
+
*/
|
|
26
|
+
/** @internal Exported for testing. */
|
|
27
|
+
export declare function buildGovernancePrompt(ctx: AgentContext): string;
|
|
28
|
+
export declare class ClaudeCodeSdkRunner implements AgentRunner {
|
|
29
|
+
run(ctx: AgentContext): Promise<AgentResult>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=claude-code-sdk.d.ts.map
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code SDK runner — invokes Claude Code via the Agent SDK's query() API
|
|
3
|
+
* instead of spawning a CLI subprocess.
|
|
4
|
+
*
|
|
5
|
+
* Advantages over ClaudeCodeRunner:
|
|
6
|
+
* - maxBudgetUsd / maxTurns — hard limits enforced by the engine
|
|
7
|
+
* - allowedTools / disallowedTools — fine-grained tool filtering with glob patterns
|
|
8
|
+
* - appendSystemPrompt — inject governance without replacing Claude Code defaults
|
|
9
|
+
* - Structured NDJSON messages — no stdout parsing
|
|
10
|
+
*/
|
|
11
|
+
import { buildPrompt } from './claude-code.js';
|
|
12
|
+
import { detectChangedFiles, gitExec, runAutoFix, snapshotWorktree } from './git-utils.js';
|
|
13
|
+
import { DEFAULT_MODEL, DEFAULT_ALLOWED_TOOLS, DEFAULT_LINT_COMMAND, DEFAULT_FORMAT_COMMAND, DEFAULT_COMMIT_MESSAGE_TEMPLATE, DEFAULT_COMMIT_CO_AUTHOR, } from '../defaults.js';
|
|
14
|
+
/** Default budget cap per agent run. */
|
|
15
|
+
const DEFAULT_MAX_BUDGET_USD = 5.0;
|
|
16
|
+
/** Default turn limit per agent run. */
|
|
17
|
+
const DEFAULT_MAX_TURNS = 100;
|
|
18
|
+
/**
|
|
19
|
+
* Map agent-role tool names to SDK-compatible tool filter patterns.
|
|
20
|
+
*/
|
|
21
|
+
/** @internal Exported for testing. */
|
|
22
|
+
export function mapToolsToSdkFormat(allowedTools) {
|
|
23
|
+
if (!allowedTools)
|
|
24
|
+
return undefined;
|
|
25
|
+
// SDK accepts tool names directly or glob patterns like Bash(git:*)
|
|
26
|
+
return allowedTools;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build tool deny-list from blocked actions.
|
|
30
|
+
* Converts blockedActions glob patterns to SDK disallowedTools format.
|
|
31
|
+
*/
|
|
32
|
+
/** @internal Exported for testing. */
|
|
33
|
+
export function mapBlockedActionsToSdkDenyList(blockedActions) {
|
|
34
|
+
if (!blockedActions || blockedActions.length === 0)
|
|
35
|
+
return [];
|
|
36
|
+
// Map each blocked action pattern to a Bash() deny rule
|
|
37
|
+
return blockedActions.map((pattern) => `Bash(${pattern})`);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build governance system prompt appendix from constraints.
|
|
41
|
+
*/
|
|
42
|
+
/** @internal Exported for testing. */
|
|
43
|
+
export function buildGovernancePrompt(ctx) {
|
|
44
|
+
const lines = ['## AI-SDLC Governance Constraints\n'];
|
|
45
|
+
if (ctx.constraints.blockedPaths.length > 0) {
|
|
46
|
+
lines.push(`Blocked paths (do NOT modify): ${ctx.constraints.blockedPaths.join(', ')}`);
|
|
47
|
+
}
|
|
48
|
+
if (ctx.constraints.blockedActions && ctx.constraints.blockedActions.length > 0) {
|
|
49
|
+
lines.push(`Blocked actions (NEVER execute): ${ctx.constraints.blockedActions.map((a) => `\`${a}\``).join(', ')}`);
|
|
50
|
+
}
|
|
51
|
+
lines.push(`Max files per change: ${ctx.constraints.maxFilesPerChange}`);
|
|
52
|
+
if (ctx.constraints.requireTests) {
|
|
53
|
+
lines.push('Tests required: every new module must have tests.');
|
|
54
|
+
}
|
|
55
|
+
lines.push('');
|
|
56
|
+
lines.push('**NEVER merge PRs. Only humans merge.**');
|
|
57
|
+
return lines.join('\n');
|
|
58
|
+
}
|
|
59
|
+
export class ClaudeCodeSdkRunner {
|
|
60
|
+
async run(ctx) {
|
|
61
|
+
// Dynamic import — SDK is an optional peer dependency
|
|
62
|
+
let query;
|
|
63
|
+
/* v8 ignore start — dynamic import fails in unit tests (SDK not installed) */
|
|
64
|
+
try {
|
|
65
|
+
const sdk = await import('@anthropic-ai/claude-agent-sdk');
|
|
66
|
+
query = sdk.query;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
filesChanged: [],
|
|
72
|
+
summary: '',
|
|
73
|
+
error: '@anthropic-ai/claude-agent-sdk is not installed. Install it to use the SDK runner: pnpm add @anthropic-ai/claude-agent-sdk',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/* v8 ignore stop */
|
|
77
|
+
const prompt = buildPrompt(ctx);
|
|
78
|
+
const model = ctx.model ?? DEFAULT_MODEL;
|
|
79
|
+
const maxTurns = ctx.constraints.maxTurns ?? DEFAULT_MAX_TURNS;
|
|
80
|
+
const maxBudgetUsd = ctx.constraints.maxBudgetUsd ?? DEFAULT_MAX_BUDGET_USD;
|
|
81
|
+
const allowedTools = mapToolsToSdkFormat(ctx.allowedTools ?? DEFAULT_ALLOWED_TOOLS.split(','));
|
|
82
|
+
const disallowedTools = mapBlockedActionsToSdkDenyList(ctx.constraints.blockedActions);
|
|
83
|
+
const governancePrompt = buildGovernancePrompt(ctx);
|
|
84
|
+
let summary = '';
|
|
85
|
+
let tokenUsage;
|
|
86
|
+
// Capture worktree state before invoking the agent so untracked noise
|
|
87
|
+
// doesn't get swept into the eventual `git add`.
|
|
88
|
+
const baseline = await snapshotWorktree(ctx.workDir);
|
|
89
|
+
/* v8 ignore start — SDK streaming loop requires real SDK connection */
|
|
90
|
+
try {
|
|
91
|
+
const result = query({
|
|
92
|
+
prompt,
|
|
93
|
+
options: {
|
|
94
|
+
model,
|
|
95
|
+
maxTurns,
|
|
96
|
+
maxBudgetUsd,
|
|
97
|
+
appendSystemPrompt: governancePrompt,
|
|
98
|
+
allowedTools,
|
|
99
|
+
disallowedTools: disallowedTools.length > 0 ? disallowedTools : undefined,
|
|
100
|
+
permissionMode: 'acceptEdits',
|
|
101
|
+
cwd: ctx.workDir,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
for await (const msg of result) {
|
|
105
|
+
const msgType = msg.type;
|
|
106
|
+
// Emit progress events
|
|
107
|
+
if (msgType === 'assistant' && ctx.onProgress) {
|
|
108
|
+
const blocks = msg.message?.content;
|
|
109
|
+
if (blocks) {
|
|
110
|
+
for (const block of blocks) {
|
|
111
|
+
if (block.type === 'tool_use') {
|
|
112
|
+
ctx.onProgress({
|
|
113
|
+
type: 'tool_start',
|
|
114
|
+
tool: block.name,
|
|
115
|
+
message: `${block.name}`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else if (block.type === 'text') {
|
|
119
|
+
summary = (block.text ?? '').slice(0, 2000);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Parse result event for token usage and cost
|
|
125
|
+
if (msgType === 'result') {
|
|
126
|
+
const usage = msg.usage;
|
|
127
|
+
const resultModel = msg.model ?? model;
|
|
128
|
+
if (usage) {
|
|
129
|
+
tokenUsage = {
|
|
130
|
+
inputTokens: usage.input_tokens ?? 0,
|
|
131
|
+
outputTokens: usage.output_tokens ?? 0,
|
|
132
|
+
cacheReadTokens: usage.cache_read_input_tokens,
|
|
133
|
+
model: resultModel,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (ctx.onProgress) {
|
|
137
|
+
ctx.onProgress({
|
|
138
|
+
type: 'cost',
|
|
139
|
+
costUsd: msg.cost_usd,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Check exit reason
|
|
143
|
+
const subtype = msg.subtype;
|
|
144
|
+
if (subtype === 'error_max_turns') {
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
filesChanged: [],
|
|
148
|
+
summary: `Agent hit turn limit (${maxTurns})`,
|
|
149
|
+
error: `Max turns (${maxTurns}) exceeded`,
|
|
150
|
+
tokenUsage,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (subtype === 'error_max_budget_usd') {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
filesChanged: [],
|
|
157
|
+
summary: `Agent hit budget limit ($${maxBudgetUsd})`,
|
|
158
|
+
error: `Max budget ($${maxBudgetUsd}) exceeded`,
|
|
159
|
+
tokenUsage,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
filesChanged: [],
|
|
169
|
+
summary: '',
|
|
170
|
+
error: `SDK runner error: ${err.message}`,
|
|
171
|
+
tokenUsage,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/* v8 ignore stop */
|
|
175
|
+
/* v8 ignore start — post-SDK commit/lint logic only runs when SDK succeeds */
|
|
176
|
+
// Detect changed files (same logic as ClaudeCodeRunner), excluding any
|
|
177
|
+
// untracked noise that pre-existed the agent invocation.
|
|
178
|
+
const { filesChanged, agentAlreadyCommitted } = await detectChangedFiles(ctx.workDir, baseline);
|
|
179
|
+
if (filesChanged.length === 0) {
|
|
180
|
+
return {
|
|
181
|
+
success: false,
|
|
182
|
+
filesChanged: [],
|
|
183
|
+
summary: 'Agent made no changes',
|
|
184
|
+
error: 'No files were modified',
|
|
185
|
+
tokenUsage,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (agentAlreadyCommitted) {
|
|
189
|
+
return {
|
|
190
|
+
success: true,
|
|
191
|
+
filesChanged,
|
|
192
|
+
summary,
|
|
193
|
+
tokenUsage,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Stage only the files the agent touched (NOT `git add -A` — the
|
|
197
|
+
// AISDLC-68 incident showed `add -A` sweeps in pre-existing untracked
|
|
198
|
+
// noise like sqlite working files and unrelated draft files).
|
|
199
|
+
await gitExec(ctx.workDir, ['add', '--', ...filesChanged]);
|
|
200
|
+
const lintCmd = ctx.lintCommand ?? DEFAULT_LINT_COMMAND;
|
|
201
|
+
const fmtCmd = ctx.formatCommand ?? DEFAULT_FORMAT_COMMAND;
|
|
202
|
+
await runAutoFix(ctx.workDir, lintCmd, fmtCmd);
|
|
203
|
+
await gitExec(ctx.workDir, ['add', '--', ...filesChanged]);
|
|
204
|
+
const tmpl = ctx.commitMessageTemplate ?? DEFAULT_COMMIT_MESSAGE_TEMPLATE;
|
|
205
|
+
const coAuthor = ctx.commitCoAuthor ?? DEFAULT_COMMIT_CO_AUTHOR;
|
|
206
|
+
const commitMsg = tmpl
|
|
207
|
+
.replace(/\{issueNumber\}/g, ctx.issueId)
|
|
208
|
+
.replace(/\{issueTitle\}/g, ctx.issueTitle);
|
|
209
|
+
try {
|
|
210
|
+
await gitExec(ctx.workDir, ['commit', '-m', `${commitMsg}\n\nCo-Authored-By: ${coAuthor}`]);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Pre-commit hook failure — auto-fix and retry once
|
|
214
|
+
await runAutoFix(ctx.workDir, lintCmd, fmtCmd);
|
|
215
|
+
await gitExec(ctx.workDir, ['add', '--', ...filesChanged]);
|
|
216
|
+
try {
|
|
217
|
+
await gitExec(ctx.workDir, ['commit', '-m', `${commitMsg}\n\nCo-Authored-By: ${coAuthor}`]);
|
|
218
|
+
}
|
|
219
|
+
catch (retryErr) {
|
|
220
|
+
return {
|
|
221
|
+
success: false,
|
|
222
|
+
filesChanged,
|
|
223
|
+
summary,
|
|
224
|
+
error: `Commit failed after auto-fix retry: ${retryErr.message}`,
|
|
225
|
+
tokenUsage,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
success: true,
|
|
231
|
+
filesChanged,
|
|
232
|
+
summary,
|
|
233
|
+
tokenUsage,
|
|
234
|
+
};
|
|
235
|
+
/* v8 ignore stop */
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=claude-code-sdk.js.map
|
|
@@ -9,6 +9,9 @@ export declare function buildPrompt(ctx: AgentContext): string;
|
|
|
9
9
|
* Claude Code CLI outputs token info to stderr in various formats.
|
|
10
10
|
*/
|
|
11
11
|
export declare function parseTokenUsage(stderr: string, model: string): TokenUsage | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Run lint --fix and format commands (best-effort) so pre-commit hooks pass.
|
|
14
|
+
*/
|
|
12
15
|
export declare class ClaudeCodeRunner implements AgentRunner {
|
|
13
16
|
run(ctx: AgentContext): Promise<AgentResult>;
|
|
14
17
|
}
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* Claude Code runner — invokes Claude Code CLI in --print mode
|
|
3
3
|
* and collects the result via git diff.
|
|
4
4
|
*/
|
|
5
|
-
import { spawn
|
|
6
|
-
import { promisify } from 'node:util';
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
7
6
|
import { DEFAULT_MODEL, DEFAULT_ALLOWED_TOOLS, DEFAULT_RUNNER_TIMEOUT_MS, DEFAULT_LINT_COMMAND, DEFAULT_FORMAT_COMMAND, DEFAULT_COMMIT_MESSAGE_TEMPLATE, DEFAULT_COMMIT_CO_AUTHOR, } from '../defaults.js';
|
|
8
7
|
import { formatContextForPrompt } from '../analysis/context-builder.js';
|
|
9
|
-
|
|
8
|
+
import { gitExec, detectChangedFiles, runAutoFix, snapshotWorktree, detectCrossRepoWrites, } from './git-utils.js';
|
|
10
9
|
/**
|
|
11
10
|
* Build verification step instructions (lint, format, typecheck).
|
|
12
11
|
* Returns an array of numbered step strings starting from the given step number.
|
|
@@ -90,10 +89,6 @@ export function buildPrompt(ctx) {
|
|
|
90
89
|
}
|
|
91
90
|
return lines.join('\n');
|
|
92
91
|
}
|
|
93
|
-
async function gitExec(workDir, args) {
|
|
94
|
-
const { stdout } = await execFileAsync('git', args, { cwd: workDir });
|
|
95
|
-
return stdout.trim();
|
|
96
|
-
}
|
|
97
92
|
/**
|
|
98
93
|
* Format a stream-json event into a concise, human-readable log line.
|
|
99
94
|
*/
|
|
@@ -402,30 +397,15 @@ export function parseTokenUsage(stderr, model) {
|
|
|
402
397
|
/**
|
|
403
398
|
* Run lint --fix and format commands (best-effort) so pre-commit hooks pass.
|
|
404
399
|
*/
|
|
405
|
-
async function runAutoFix(workDir, lintCmd, fmtCmd) {
|
|
406
|
-
if (fmtCmd) {
|
|
407
|
-
try {
|
|
408
|
-
const [bin, ...args] = fmtCmd.split(' ');
|
|
409
|
-
await execFileAsync(bin, args, { cwd: workDir });
|
|
410
|
-
}
|
|
411
|
-
catch {
|
|
412
|
-
// Format failures are non-fatal — the commit hook will catch remaining issues
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
if (lintCmd) {
|
|
416
|
-
try {
|
|
417
|
-
const [bin, ...args] = lintCmd.split(' ');
|
|
418
|
-
await execFileAsync(bin, args, { cwd: workDir });
|
|
419
|
-
}
|
|
420
|
-
catch {
|
|
421
|
-
// Lint --fix failures are non-fatal
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
400
|
export class ClaudeCodeRunner {
|
|
426
401
|
async run(ctx) {
|
|
427
402
|
const prompt = buildPrompt(ctx);
|
|
428
403
|
try {
|
|
404
|
+
// Snapshot the worktree BEFORE invoking the agent so we can subtract any
|
|
405
|
+
// pre-existing untracked noise (sqlite working files, draft files the user
|
|
406
|
+
// hasn't decided to commit yet, in-flight edits) from the agent's diff.
|
|
407
|
+
// Without this, `git add` would sweep them into the agent's commit.
|
|
408
|
+
const baseline = await snapshotWorktree(ctx.workDir);
|
|
429
409
|
// Invoke Claude Code CLI with stream-json for real-time progress
|
|
430
410
|
const result = await runClaude(prompt, ctx.workDir, {
|
|
431
411
|
allowedTools: ctx.allowedTools,
|
|
@@ -436,39 +416,9 @@ export class ClaudeCodeRunner {
|
|
|
436
416
|
});
|
|
437
417
|
// Token usage from stream-json result event (falls back to stderr parsing)
|
|
438
418
|
const tokenUsage = parseTokenUsage(result.stderr, result.model);
|
|
439
|
-
// Collect changed files
|
|
440
|
-
//
|
|
441
|
-
const
|
|
442
|
-
const untrackedOutput = await gitExec(ctx.workDir, [
|
|
443
|
-
'ls-files',
|
|
444
|
-
'--others',
|
|
445
|
-
'--exclude-standard',
|
|
446
|
-
]);
|
|
447
|
-
const uncommittedFiles = [
|
|
448
|
-
...diffOutput.split('\n').filter(Boolean),
|
|
449
|
-
...untrackedOutput.split('\n').filter(Boolean),
|
|
450
|
-
];
|
|
451
|
-
// Check if agent already committed (and possibly pushed) —
|
|
452
|
-
// compare against the merge base with the target branch (main)
|
|
453
|
-
let committedFiles = [];
|
|
454
|
-
let agentAlreadyCommitted = false;
|
|
455
|
-
try {
|
|
456
|
-
// Find the merge base with main to see all new commits on this branch
|
|
457
|
-
const mergeBase = (await gitExec(ctx.workDir, ['merge-base', 'HEAD', 'origin/main'])).trim();
|
|
458
|
-
if (mergeBase) {
|
|
459
|
-
const commitDiff = await gitExec(ctx.workDir, [
|
|
460
|
-
'diff',
|
|
461
|
-
'--name-only',
|
|
462
|
-
`${mergeBase}..HEAD`,
|
|
463
|
-
]);
|
|
464
|
-
committedFiles = commitDiff.split('\n').filter(Boolean);
|
|
465
|
-
agentAlreadyCommitted = committedFiles.length > 0 && uncommittedFiles.length === 0;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
catch {
|
|
469
|
-
// merge-base may fail if main doesn't exist locally — that's fine
|
|
470
|
-
}
|
|
471
|
-
const filesChanged = agentAlreadyCommitted ? committedFiles : uncommittedFiles;
|
|
419
|
+
// Collect changed files (uncommitted + agent-committed), excluding
|
|
420
|
+
// pre-existing untracked noise.
|
|
421
|
+
const { filesChanged, agentAlreadyCommitted } = await detectChangedFiles(ctx.workDir, baseline);
|
|
472
422
|
if (filesChanged.length === 0) {
|
|
473
423
|
return {
|
|
474
424
|
success: false,
|
|
@@ -487,14 +437,17 @@ export class ClaudeCodeRunner {
|
|
|
487
437
|
tokenUsage,
|
|
488
438
|
};
|
|
489
439
|
}
|
|
490
|
-
// Stage
|
|
491
|
-
|
|
440
|
+
// Stage only the files the agent touched (NOT `git add -A`, which
|
|
441
|
+
// would include pre-existing untracked noise per the AISDLC-68 incident).
|
|
442
|
+
await gitExec(ctx.workDir, ['add', '--', ...filesChanged]);
|
|
492
443
|
// Run lint and format before committing to avoid pre-commit hook failures
|
|
493
444
|
const lintCmd = ctx.lintCommand ?? DEFAULT_LINT_COMMAND;
|
|
494
445
|
const fmtCmd = ctx.formatCommand ?? DEFAULT_FORMAT_COMMAND;
|
|
495
446
|
await runAutoFix(ctx.workDir, lintCmd, fmtCmd);
|
|
496
|
-
// Re-stage
|
|
497
|
-
|
|
447
|
+
// Re-stage the same set in case auto-fix re-modified them. Auto-fix should
|
|
448
|
+
// not touch files outside the agent's diff, but if it does, those changes
|
|
449
|
+
// are the user's problem to commit separately.
|
|
450
|
+
await gitExec(ctx.workDir, ['add', '--', ...filesChanged]);
|
|
498
451
|
const tmpl = ctx.commitMessageTemplate ?? DEFAULT_COMMIT_MESSAGE_TEMPLATE;
|
|
499
452
|
const coAuthor = ctx.commitCoAuthor ?? DEFAULT_COMMIT_CO_AUTHOR;
|
|
500
453
|
const commitMsg = tmpl
|
|
@@ -504,11 +457,22 @@ export class ClaudeCodeRunner {
|
|
|
504
457
|
await gitExec(ctx.workDir, ['commit', '-m', `${commitMsg}\n\nCo-Authored-By: ${coAuthor}`]);
|
|
505
458
|
}
|
|
506
459
|
catch {
|
|
507
|
-
// Pre-commit hook may have auto-fixed files — re-stage
|
|
460
|
+
// Pre-commit hook may have auto-fixed files — re-stage the agent's
|
|
461
|
+
// file set and retry once.
|
|
508
462
|
await runAutoFix(ctx.workDir, lintCmd, fmtCmd);
|
|
509
|
-
await gitExec(ctx.workDir, ['add', '
|
|
463
|
+
await gitExec(ctx.workDir, ['add', '--', ...filesChanged]);
|
|
510
464
|
await gitExec(ctx.workDir, ['commit', '-m', `${commitMsg}\n\nCo-Authored-By: ${coAuthor}`]);
|
|
511
465
|
}
|
|
466
|
+
// Detect cross-repo writes (e.g., agent edited files in ../sister-repo)
|
|
467
|
+
// and surface them as a stderr warning so the operator knows to commit
|
|
468
|
+
// them in the sibling repo separately. Not a hard failure — sometimes
|
|
469
|
+
// cross-repo work is intended (the AISDLC-68 docs sync, for example).
|
|
470
|
+
const crossRepoWrites = await detectCrossRepoWrites(ctx.workDir);
|
|
471
|
+
for (const write of crossRepoWrites) {
|
|
472
|
+
process.stderr.write(`[ai-sdlc:runner] WARNING: agent wrote ${write.files.length} file(s) into sibling repo ` +
|
|
473
|
+
`${write.repoPath} — those changes are NOT in this PR. ` +
|
|
474
|
+
`Files: ${write.files.slice(0, 5).join(', ')}${write.files.length > 5 ? ` (+${write.files.length - 5} more)` : ''}\n`);
|
|
475
|
+
}
|
|
512
476
|
return {
|
|
513
477
|
success: true,
|
|
514
478
|
filesChanged,
|
package/dist/runners/codex.js
CHANGED
|
@@ -6,10 +6,13 @@ import { spawn, execFile } from 'node:child_process';
|
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { DEFAULT_RUNNER_TIMEOUT_MS, DEFAULT_CODEX_MODEL, DEFAULT_COMMIT_MESSAGE_TEMPLATE, DEFAULT_COMMIT_CO_AUTHOR, } from '../defaults.js';
|
|
8
8
|
import { buildPrompt } from './claude-code.js';
|
|
9
|
+
import { cleanGitEnv } from '../runtime/git-env.js';
|
|
9
10
|
export { buildPrompt };
|
|
10
11
|
const execFileAsync = promisify(execFile);
|
|
12
|
+
// Strip GIT_DIR / GIT_WORK_TREE / GIT_INDEX_FILE so git resolves against
|
|
13
|
+
// `workDir`'s own .git rather than whatever a parent process leaked (AISDLC-72).
|
|
11
14
|
async function gitExec(workDir, args) {
|
|
12
|
-
const { stdout } = await execFileAsync('git', args, { cwd: workDir });
|
|
15
|
+
const { stdout } = await execFileAsync('git', args, { cwd: workDir, env: cleanGitEnv() });
|
|
13
16
|
return stdout.trim();
|
|
14
17
|
}
|
|
15
18
|
/**
|
package/dist/runners/copilot.js
CHANGED
|
@@ -6,10 +6,13 @@ import { spawn, execFile } from 'node:child_process';
|
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { DEFAULT_RUNNER_TIMEOUT_MS, DEFAULT_COPILOT_MODEL, DEFAULT_COMMIT_MESSAGE_TEMPLATE, DEFAULT_COMMIT_CO_AUTHOR, } from '../defaults.js';
|
|
8
8
|
import { buildPrompt } from './claude-code.js';
|
|
9
|
+
import { cleanGitEnv } from '../runtime/git-env.js';
|
|
9
10
|
export { buildPrompt };
|
|
10
11
|
const execFileAsync = promisify(execFile);
|
|
12
|
+
// Strip GIT_DIR / GIT_WORK_TREE / GIT_INDEX_FILE so git resolves against
|
|
13
|
+
// `workDir`'s own .git rather than whatever a parent process leaked (AISDLC-72).
|
|
11
14
|
async function gitExec(workDir, args) {
|
|
12
|
-
const { stdout } = await execFileAsync('git', args, { cwd: workDir });
|
|
15
|
+
const { stdout } = await execFileAsync('git', args, { cwd: workDir, env: cleanGitEnv() });
|
|
13
16
|
return stdout.trim();
|
|
14
17
|
}
|
|
15
18
|
/**
|
package/dist/runners/cursor.js
CHANGED
|
@@ -6,10 +6,13 @@ import { spawn, execFile } from 'node:child_process';
|
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { DEFAULT_RUNNER_TIMEOUT_MS, DEFAULT_CURSOR_MODEL, DEFAULT_COMMIT_MESSAGE_TEMPLATE, DEFAULT_COMMIT_CO_AUTHOR, } from '../defaults.js';
|
|
8
8
|
import { buildPrompt } from './claude-code.js';
|
|
9
|
+
import { cleanGitEnv } from '../runtime/git-env.js';
|
|
9
10
|
export { buildPrompt };
|
|
10
11
|
const execFileAsync = promisify(execFile);
|
|
12
|
+
// Strip GIT_DIR / GIT_WORK_TREE / GIT_INDEX_FILE so git resolves against
|
|
13
|
+
// `workDir`'s own .git rather than whatever a parent process leaked (AISDLC-72).
|
|
11
14
|
async function gitExec(workDir, args) {
|
|
12
|
-
const { stdout } = await execFileAsync('git', args, { cwd: workDir });
|
|
15
|
+
const { stdout } = await execFileAsync('git', args, { cwd: workDir, env: cleanGitEnv() });
|
|
13
16
|
return stdout.trim();
|
|
14
17
|
}
|
|
15
18
|
/**
|