@fitlab-ai/agent-infra 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -4
- package/README.zh-CN.md +16 -4
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +15 -0
- package/dist/lib/agent-client.js +163 -0
- package/dist/lib/agent-clients/adapter.js +294 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
- package/dist/lib/agent-clients/adapters/codex.js +54 -0
- package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
- package/dist/lib/agent-clients/adapters/index.js +20 -0
- package/dist/lib/agent-clients/adapters/opencode.js +64 -0
- package/dist/lib/agent-clients/config.js +177 -0
- package/dist/lib/agent-clients/custom-tuis.js +99 -0
- package/dist/lib/agent-clients/invocation.js +9 -0
- package/dist/lib/agent-clients/next-steps.js +38 -0
- package/dist/lib/agent-clients/project-assets.js +45 -0
- package/dist/lib/agent-clients/reconcile.js +235 -0
- package/dist/lib/agent-clients/registry.js +72 -0
- package/dist/lib/agent-clients/schema.js +57 -0
- package/dist/lib/agent-clients/types.js +29 -0
- package/dist/lib/builtin-tuis.js +9 -14
- package/dist/lib/defaults.json +85 -73
- package/dist/lib/git/review-snapshot.js +25 -10
- package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
- package/dist/lib/init.js +47 -91
- package/dist/lib/internal/agent-client.js +117 -0
- package/dist/lib/internal/git-workflow.js +1 -1
- package/dist/lib/internal/release-workflow.js +71 -16
- package/dist/lib/internal/task-orchestration.js +151 -0
- package/dist/lib/internal/task-review.js +65 -0
- package/dist/lib/merge.js +2 -1
- package/dist/lib/platform/adapters.js +38 -1
- package/dist/lib/platform/change-request-git-evidence.js +113 -0
- package/dist/lib/platform/merged-pr-equivalence.js +41 -11
- package/dist/lib/platform/pr-checks.js +38 -10
- package/dist/lib/platform/pull-requests.js +110 -6
- package/dist/lib/platform/releases.js +1 -1
- package/dist/lib/platform/verification-required.js +8 -10
- package/dist/lib/prompt.js +15 -3
- package/dist/lib/release/channels.js +4 -1
- package/dist/lib/release/workflow.js +4 -2
- package/dist/lib/remove-dir.js +25 -0
- package/dist/lib/run/tui.js +8 -2
- package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
- package/dist/lib/sandbox/clipboard/linux.js +2 -1
- package/dist/lib/sandbox/commands/create.js +76 -180
- package/dist/lib/sandbox/commands/enter.js +17 -1
- package/dist/lib/sandbox/commands/prune.js +2 -2
- package/dist/lib/sandbox/commands/rm.js +3 -2
- package/dist/lib/sandbox/config.js +21 -4
- package/dist/lib/sandbox/constants.js +3 -0
- package/dist/lib/sandbox/dockerfile.js +2 -1
- package/dist/lib/sandbox/managed-fs.js +10 -5
- package/dist/lib/sandbox/recovery.js +52 -36
- package/dist/lib/sandbox/shell.js +1 -1
- package/dist/lib/sandbox/tool-types.js +8 -0
- package/dist/lib/sandbox/tools.js +2 -126
- package/dist/lib/task/artifact-lifecycle.js +1 -49
- package/dist/lib/task/commands/decisions.js +50 -20
- package/dist/lib/task/delegation-receipts.js +107 -0
- package/dist/lib/task/events.js +100 -1
- package/dist/lib/task/orchestration.js +420 -0
- package/dist/lib/task/review-artifacts.js +143 -59
- package/dist/lib/task/review-finalization.js +166 -0
- package/dist/lib/task/review-fingerprint.js +4 -0
- package/dist/lib/task/verification-checks.js +2 -1
- package/dist/lib/task/verification-engine.js +156 -28
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +3 -1
- package/dist/lib/task/workspace-snapshot.js +34 -0
- package/dist/lib/update.js +42 -161
- package/lib/agent-client.ts +214 -0
- package/lib/agent-clients/adapter.ts +461 -0
- package/lib/agent-clients/adapters/claude-code.ts +70 -0
- package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
- package/lib/agent-clients/adapters/codex.ts +58 -0
- package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
- package/lib/agent-clients/adapters/index.ts +27 -0
- package/lib/agent-clients/adapters/opencode.ts +66 -0
- package/lib/agent-clients/config.ts +278 -0
- package/lib/agent-clients/custom-tuis.ts +149 -0
- package/lib/agent-clients/invocation.ts +19 -0
- package/lib/agent-clients/next-steps.ts +72 -0
- package/lib/agent-clients/project-assets.ts +96 -0
- package/lib/agent-clients/reconcile.ts +324 -0
- package/lib/agent-clients/registry.ts +144 -0
- package/lib/agent-clients/schema.ts +62 -0
- package/lib/agent-clients/types.ts +78 -0
- package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
- package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
- package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
- package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
- package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
- package/lib/builtin-tuis.ts +16 -15
- package/lib/defaults.json +24 -12
- package/lib/git/review-snapshot.ts +39 -9
- package/lib/git/reviewed-commit-equivalence.ts +135 -0
- package/lib/init.ts +53 -121
- package/lib/internal/agent-client.ts +131 -0
- package/lib/internal/git-workflow.ts +1 -1
- package/lib/internal/release-workflow.ts +68 -16
- package/lib/internal/task-orchestration.ts +132 -0
- package/lib/internal/task-review.ts +65 -0
- package/lib/merge.ts +2 -1
- package/lib/platform/adapters.ts +142 -3
- package/lib/platform/change-request-git-evidence.ts +159 -0
- package/lib/platform/merged-pr-equivalence.ts +51 -14
- package/lib/platform/pr-checks.ts +50 -22
- package/lib/platform/pull-requests.ts +134 -25
- package/lib/platform/releases.ts +1 -1
- package/lib/platform/verification-required.ts +12 -10
- package/lib/prompt.ts +16 -3
- package/lib/release/channels.ts +4 -1
- package/lib/release/workflow.ts +4 -3
- package/lib/remove-dir.ts +28 -0
- package/lib/run/tui.ts +9 -1
- package/lib/sandbox/agent-client-reconciler.ts +378 -0
- package/lib/sandbox/clipboard/linux.ts +2 -1
- package/lib/sandbox/commands/create.ts +111 -192
- package/lib/sandbox/commands/enter.ts +23 -1
- package/lib/sandbox/commands/prune.ts +2 -2
- package/lib/sandbox/commands/rm.ts +3 -2
- package/lib/sandbox/config.ts +31 -4
- package/lib/sandbox/constants.ts +6 -0
- package/lib/sandbox/dockerfile.ts +2 -1
- package/lib/sandbox/managed-fs.ts +18 -5
- package/lib/sandbox/recovery.ts +98 -67
- package/lib/sandbox/shell.ts +1 -1
- package/lib/sandbox/tool-types.ts +143 -0
- package/lib/sandbox/tools.ts +5 -157
- package/lib/task/artifact-lifecycle.ts +1 -38
- package/lib/task/commands/decisions.ts +53 -20
- package/lib/task/delegation-receipts.ts +170 -0
- package/lib/task/events.ts +110 -2
- package/lib/task/orchestration.ts +543 -0
- package/lib/task/review-artifacts.ts +201 -69
- package/lib/task/review-finalization.ts +275 -0
- package/lib/task/review-fingerprint.ts +5 -0
- package/lib/task/verification-checks.ts +2 -1
- package/lib/task/verification-engine.ts +177 -28
- package/lib/task/verification-review.ts +1 -0
- package/lib/task/verification.ts +5 -2
- package/lib/task/workspace-snapshot.ts +36 -0
- package/lib/update.ts +48 -184
- package/package.json +2 -2
- package/templates/.agents/README.en.md +21 -20
- package/templates/.agents/README.zh-CN.md +21 -20
- package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- package/templates/.agents/rules/commit-and-pr.en.md +1 -0
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
- package/templates/.agents/rules/human-decision-context.en.md +14 -12
- package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
- package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
- package/templates/.agents/rules/next-step-output.en.md +16 -0
- package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
- package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
- package/templates/.agents/rules/release-commands.github.en.md +11 -5
- package/templates/.agents/rules/release-commands.github.zh-CN.md +11 -5
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/rules/review-method.en.md +113 -0
- package/templates/.agents/rules/review-method.zh-CN.md +98 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
- package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
- package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/SKILL.en.md +11 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
- package/templates/.agents/skills/create-task/SKILL.en.md +11 -11
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +11 -11
- package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
- package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
- package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
- package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
- package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
- package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
- package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
- package/templates/.agents/skills/test/SKILL.en.md +4 -4
- package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
- package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
- package/templates/.claude/commands/run-task.en.md +8 -0
- package/templates/.claude/commands/run-task.zh-CN.md +8 -0
- package/templates/.claude/settings.json +24 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
- package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
- package/templates/.opencode/commands/run-task.en.md +11 -0
- package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
|
@@ -139,10 +139,58 @@ function runOptionalDemo(cwd, run = command) {
|
|
|
139
139
|
writeDigestAtomically(cwd, digest);
|
|
140
140
|
return { status: 'recorded', reasonCode: null, message: null, outputPath };
|
|
141
141
|
}
|
|
142
|
-
function git(cwd, args) {
|
|
143
|
-
const result =
|
|
142
|
+
function git(cwd, args, run = command) {
|
|
143
|
+
const result = run(cwd, 'git', args);
|
|
144
144
|
return result.status === 0 ? String(result.stdout).trim() : null;
|
|
145
145
|
}
|
|
146
|
+
function inspectLocalReleaseFacts(cwd, version, run = command) {
|
|
147
|
+
const tag = `v${version}`;
|
|
148
|
+
const head = git(cwd, ['rev-parse', 'HEAD'], run);
|
|
149
|
+
const tagCommit = git(cwd, ['rev-parse', '--verify', `${tag}^{commit}`], run);
|
|
150
|
+
const localTag = Boolean(tagCommit && tagCommit === head);
|
|
151
|
+
const localTagAncestor = Boolean(tagCommit && head && !localTag
|
|
152
|
+
&& run(cwd, 'git', ['merge-base', '--is-ancestor', tagCommit, head]).status === 0);
|
|
153
|
+
const localTagConflict = Boolean(tagCommit && !localTag && !localTagAncestor);
|
|
154
|
+
const postSubject = `chore: prepare next dev iteration after v${version}`;
|
|
155
|
+
const postLog = localTag || localTagAncestor
|
|
156
|
+
? git(cwd, ['log', '--format=%s', `${tag}..HEAD`], run) ?? ''
|
|
157
|
+
: '';
|
|
158
|
+
const postCommit = postLog.split('\n').some((subject) => subject === postSubject);
|
|
159
|
+
return { localTag, localTagAncestor, localTagConflict, postCommit };
|
|
160
|
+
}
|
|
161
|
+
function releaseSmokeStatus(workflows, version, tagCommit) {
|
|
162
|
+
const manualTitle = `Post-Release Smoke v${version}`;
|
|
163
|
+
const targets = workflows.filter((run) => {
|
|
164
|
+
if (String(run.workflowName || run.name) !== 'Post-Release Smoke')
|
|
165
|
+
return false;
|
|
166
|
+
if (String(run.event) === 'workflow_run')
|
|
167
|
+
return Boolean(tagCommit && String(run.headSha) === tagCommit);
|
|
168
|
+
return String(run.event) === 'workflow_dispatch' && String(run.displayTitle) === manualTitle;
|
|
169
|
+
}).sort((left, right) => {
|
|
170
|
+
const leftOrder = [Date.parse(String(left.createdAt || '')) || 0, Number(left.databaseId) || 0, Number(left.attempt) || 0];
|
|
171
|
+
const rightOrder = [Date.parse(String(right.createdAt || '')) || 0, Number(right.databaseId) || 0, Number(right.attempt) || 0];
|
|
172
|
+
for (let index = 0; index < leftOrder.length; index += 1) {
|
|
173
|
+
if (leftOrder[index] !== rightOrder[index])
|
|
174
|
+
return rightOrder[index] - leftOrder[index];
|
|
175
|
+
}
|
|
176
|
+
return 0;
|
|
177
|
+
});
|
|
178
|
+
const latest = targets[0];
|
|
179
|
+
if (!latest)
|
|
180
|
+
return null;
|
|
181
|
+
if (String(latest.status) !== 'completed')
|
|
182
|
+
return 'pending';
|
|
183
|
+
return String(latest.conclusion) === 'success' ? 'success' : 'failed';
|
|
184
|
+
}
|
|
185
|
+
function inspectPostWorktree(cwd) {
|
|
186
|
+
const inspected = inspectGitWorkflow(cwd);
|
|
187
|
+
if (!inspected.snapshot)
|
|
188
|
+
return inspected.error ?? { code: 'GIT_INSPECT_FAILED', message: 'Unable to inspect Git repository' };
|
|
189
|
+
if (inspected.snapshot.worktree.length || inspected.snapshot.staged.length) {
|
|
190
|
+
return { code: 'WORKTREE_DIRTY', message: 'Release post requires a clean worktree' };
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
146
194
|
async function inspectFacts(cwd, version) {
|
|
147
195
|
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
148
196
|
const config = JSON.parse(fs.readFileSync(path.join(cwd, '.agents', '.airc.json'), 'utf8'));
|
|
@@ -150,6 +198,7 @@ async function inspectFacts(cwd, version) {
|
|
|
150
198
|
const branch = git(cwd, ['branch', '--show-current']) || '';
|
|
151
199
|
const head = git(cwd, ['rev-parse', 'HEAD']);
|
|
152
200
|
const localTagTarget = git(cwd, ['rev-parse', '--verify', `${tag}^{commit}`]);
|
|
201
|
+
const local = inspectLocalReleaseFacts(cwd, version);
|
|
153
202
|
const remoteBranch = branch ? git(cwd, ['ls-remote', '--heads', 'origin', branch]) : null;
|
|
154
203
|
const remoteTag = git(cwd, ['ls-remote', '--tags', 'origin', `refs/tags/${tag}`]);
|
|
155
204
|
const platform = inspectPlatformRelease(tag, { cwd });
|
|
@@ -157,18 +206,14 @@ async function inspectFacts(cwd, version) {
|
|
|
157
206
|
const formulaUrl = `https://raw.githubusercontent.com/${config.org}/homebrew-tap/main/Formula/${config.project}.rb`;
|
|
158
207
|
const homebrew = await inspectHomebrewChannel(formulaUrl, version);
|
|
159
208
|
const workflows = platform.workflows;
|
|
160
|
-
const
|
|
161
|
-
const smoke = smokeRun ? String(smokeRun.conclusion) === 'success' ? 'success' : String(smokeRun.status) === 'completed' ? 'failed' : 'pending' : null;
|
|
162
|
-
const postMessage = git(cwd, ['log', '-1', '--pretty=%s']) || '';
|
|
209
|
+
const smoke = releaseSmokeStatus(workflows, version, localTagTarget);
|
|
163
210
|
return {
|
|
164
|
-
|
|
165
|
-
localTagConflict: Boolean(localTagTarget && localTagTarget !== head),
|
|
211
|
+
...local,
|
|
166
212
|
remoteBranch: Boolean(remoteBranch && remoteBranch.split(/\s+/)[0] === head), remoteTag: Boolean(remoteTag),
|
|
167
213
|
githubRelease: platform.platform.type !== 'github'
|
|
168
214
|
? true
|
|
169
215
|
: platform.status === 'blocked' ? null : Boolean(platform.release?.published),
|
|
170
|
-
npm: npm.published, homebrew: homebrew.published, smoke
|
|
171
|
-
postCommit: postMessage.includes(`after v${version}`)
|
|
216
|
+
npm: npm.published, homebrew: homebrew.published, smoke
|
|
172
217
|
};
|
|
173
218
|
}
|
|
174
219
|
function changedPaths(cwd) {
|
|
@@ -208,12 +253,12 @@ async function releaseWorkflow(args = []) {
|
|
|
208
253
|
process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
|
|
209
254
|
return;
|
|
210
255
|
}
|
|
256
|
+
if (before.facts.localTagConflict) {
|
|
257
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'GIT_TAG_CONFLICT', message: `Tag v${version} is not reachable from HEAD` } })}\n`);
|
|
258
|
+
process.exitCode = 1;
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
211
261
|
if (action === 'prepare') {
|
|
212
|
-
if (before.facts.localTagConflict) {
|
|
213
|
-
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'GIT_TAG_CONFLICT', message: `Tag v${version} does not point to HEAD` } })}\n`);
|
|
214
|
-
process.exitCode = 1;
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
262
|
if (before.phase !== 'unprepared') {
|
|
218
263
|
const milestones = reconcileReleaseMilestones(version, { cwd });
|
|
219
264
|
process.stdout.write(`${JSON.stringify({ ...milestones, snapshot: before })}\n`);
|
|
@@ -267,7 +312,7 @@ async function releaseWorkflow(args = []) {
|
|
|
267
312
|
return;
|
|
268
313
|
}
|
|
269
314
|
if (action === 'publish') {
|
|
270
|
-
if (!['prepared', 'partially-published'].includes(before.phase)) {
|
|
315
|
+
if (!before.facts.localTag || !['prepared', 'partially-published'].includes(before.phase)) {
|
|
271
316
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_PHASE_INVALID', message: `Cannot publish from ${before.phase}` } })}\n`);
|
|
272
317
|
process.exitCode = 1;
|
|
273
318
|
return;
|
|
@@ -279,12 +324,22 @@ async function releaseWorkflow(args = []) {
|
|
|
279
324
|
process.exitCode = result.status === 'failed' ? 1 : result.status === 'degraded' ? 2 : 0;
|
|
280
325
|
return;
|
|
281
326
|
}
|
|
327
|
+
if (before.phase === 'complete') {
|
|
328
|
+
process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
282
331
|
const channelsComplete = before.facts.githubRelease === true && before.facts.npm === true && before.facts.homebrew === true;
|
|
283
332
|
if (!['published', 'post-pending'].includes(before.phase) || !channelsComplete || before.facts.smoke !== 'success') {
|
|
284
333
|
process.stdout.write(`${JSON.stringify({ status: before.facts.smoke === 'failed' ? 'failed' : 'blocked', changed: false, snapshot: before, error: { code: 'RELEASE_CHANNELS_PENDING', message: 'Release channels or smoke workflow are not complete' } })}\n`);
|
|
285
334
|
process.exitCode = before.facts.smoke === 'failed' ? 1 : 2;
|
|
286
335
|
return;
|
|
287
336
|
}
|
|
337
|
+
const worktreeError = inspectPostWorktree(cwd);
|
|
338
|
+
if (worktreeError) {
|
|
339
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: worktreeError })}\n`);
|
|
340
|
+
process.exitCode = 1;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
288
343
|
const built = command(cwd, 'npm', ['run', 'build']);
|
|
289
344
|
if (built.status !== 0) {
|
|
290
345
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: true, snapshot: before, error: { code: 'RELEASE_POST_COMMAND_FAILED', message: String(built.stderr || built.stdout) } })}\n`);
|
|
@@ -316,5 +371,5 @@ async function releaseWorkflow(args = []) {
|
|
|
316
371
|
process.stdout.write(`${JSON.stringify({ ...pushed, demo, snapshot: releaseSnapshot(version, await inspectFacts(cwd, version)) })}\n`);
|
|
317
372
|
process.exitCode = pushed.status === 'failed' ? 1 : pushed.status === 'degraded' ? 2 : 0;
|
|
318
373
|
}
|
|
319
|
-
export { computeDemoInputDigest, inspectFacts, releaseWorkflow, runOptionalDemo };
|
|
374
|
+
export { computeDemoInputDigest, inspectFacts, inspectLocalReleaseFacts, inspectPostWorktree, releaseSmokeStatus, releaseWorkflow, runOptionalDemo };
|
|
320
375
|
//# sourceMappingURL=release-workflow.js.map
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginOrResumeOrchestration, completeCommitOrchestrationStage, pauseOrchestration, prepareOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration } from '../task/orchestration.js';
|
|
2
|
+
const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|hook-start|stage-completed|hook-stop|advance|pause|status> [options]\n';
|
|
3
|
+
function usageFailure(message) {
|
|
4
|
+
process.stdout.write(`${JSON.stringify({
|
|
5
|
+
status: 'failed', changed: false,
|
|
6
|
+
error: { code: 'ORCHESTRATION_PAYLOAD_INVALID', message }
|
|
7
|
+
})}\n`);
|
|
8
|
+
process.stderr.write(USAGE);
|
|
9
|
+
process.exitCode = 2;
|
|
10
|
+
}
|
|
11
|
+
function taskOrchestration(args = []) {
|
|
12
|
+
if (args[0] === '--help' || args[0] === '-h') {
|
|
13
|
+
process.stdout.write(USAGE);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (args.length < 2) {
|
|
17
|
+
usageFailure('task ref and intent are required');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const [taskRef, intent] = args;
|
|
21
|
+
if (!['begin-or-resume', 'route', 'prepare', 'hook-start', 'stage-completed', 'hook-stop', 'advance', 'pause', 'status'].includes(intent)) {
|
|
22
|
+
usageFailure(`unknown intent '${intent}'`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const values = {};
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
28
|
+
const flag = args[index];
|
|
29
|
+
if (![
|
|
30
|
+
'--max-steps', '--client', '--requested-model', '--parent-id', '--before-fingerprint',
|
|
31
|
+
'--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--fallback-reason',
|
|
32
|
+
'--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent'
|
|
33
|
+
].includes(flag)) {
|
|
34
|
+
usageFailure(`unknown option '${flag}'`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (seen.has(flag)) {
|
|
38
|
+
usageFailure(`duplicate option '${flag}'`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
seen.add(flag);
|
|
42
|
+
const value = args[++index];
|
|
43
|
+
if (!value || value.startsWith('--')) {
|
|
44
|
+
usageFailure(`option '${flag}' requires a value`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
values[flag] = value;
|
|
48
|
+
}
|
|
49
|
+
const requireValues = (flags) => flags.find((flag) => values[flag] === undefined);
|
|
50
|
+
let result;
|
|
51
|
+
if (intent === 'begin-or-resume') {
|
|
52
|
+
const maxSteps = values['--max-steps'] === undefined ? undefined : Number(values['--max-steps']);
|
|
53
|
+
if (maxSteps !== undefined && (!Number.isInteger(maxSteps) || maxSteps < 1)) {
|
|
54
|
+
usageFailure('--max-steps must be a positive integer');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
result = beginOrResumeOrchestration(taskRef, { maxSteps });
|
|
58
|
+
}
|
|
59
|
+
else if (intent === 'route') {
|
|
60
|
+
result = routeOrchestration(taskRef);
|
|
61
|
+
}
|
|
62
|
+
else if (intent === 'status') {
|
|
63
|
+
result = statusOrchestration(taskRef);
|
|
64
|
+
}
|
|
65
|
+
else if (intent === 'prepare') {
|
|
66
|
+
const missing = requireValues(['--client']);
|
|
67
|
+
if (missing) {
|
|
68
|
+
usageFailure(`intent 'prepare' requires '${missing}'`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
result = prepareOrchestrationDelegation(taskRef, {
|
|
72
|
+
client: values['--client'],
|
|
73
|
+
requestedModel: values['--requested-model']
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else if (intent === 'hook-start') {
|
|
77
|
+
const missing = requireValues([
|
|
78
|
+
...(taskRef === 'auto' ? ['--client'] : []),
|
|
79
|
+
'--native-agent', '--child-id', '--parent-id', '--spawn-mode'
|
|
80
|
+
]);
|
|
81
|
+
if (missing) {
|
|
82
|
+
usageFailure(`intent 'hook-start' requires '${missing}'`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const event = {
|
|
86
|
+
nativeAgent: values['--native-agent'], childId: values['--child-id'],
|
|
87
|
+
parentId: values['--parent-id'], spawnMode: values['--spawn-mode'],
|
|
88
|
+
actualModel: values['--actual-model'], modelFallbackReason: values['--fallback-reason']
|
|
89
|
+
};
|
|
90
|
+
result = taskRef === 'auto'
|
|
91
|
+
? activateMatchingOrchestrationDelegation(values['--client'], event)
|
|
92
|
+
: activateOrchestrationDelegation(taskRef, event);
|
|
93
|
+
}
|
|
94
|
+
else if (intent === 'hook-stop') {
|
|
95
|
+
if (taskRef === 'auto') {
|
|
96
|
+
const missing = requireValues(['--client', '--native-agent', '--child-id']);
|
|
97
|
+
if (missing) {
|
|
98
|
+
usageFailure(`intent 'hook-stop' requires '${missing}'`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
result = sealMatchingOrchestrationDelegation(values['--client'], {
|
|
102
|
+
nativeAgent: values['--native-agent'], childId: values['--child-id']
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const missing = requireValues(['--child-id', '--exit-code', '--after-fingerprint']);
|
|
107
|
+
if (missing) {
|
|
108
|
+
usageFailure(`intent 'hook-stop' requires '${missing}'`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const exitCode = Number(values['--exit-code']);
|
|
112
|
+
if (!Number.isInteger(exitCode)) {
|
|
113
|
+
usageFailure('--exit-code must be an integer');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
result = sealOrchestrationDelegation(taskRef, {
|
|
117
|
+
childId: values['--child-id'], exitCode,
|
|
118
|
+
afterFingerprint: values['--after-fingerprint'],
|
|
119
|
+
changedPaths: values['--changed-paths'] ? values['--changed-paths'].split(',').filter(Boolean) : []
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (intent === 'stage-completed') {
|
|
124
|
+
const missing = requireValues(['--agent']);
|
|
125
|
+
if (missing) {
|
|
126
|
+
usageFailure(`intent 'stage-completed' requires '${missing}'`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
result = completeCommitOrchestrationStage(taskRef, values['--agent']);
|
|
130
|
+
}
|
|
131
|
+
else if (intent === 'advance') {
|
|
132
|
+
result = advanceOrchestration(taskRef);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const missing = requireValues(['--code', '--message', '--recoverable']);
|
|
136
|
+
if (missing) {
|
|
137
|
+
usageFailure(`intent 'pause' requires '${missing}'`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!['true', 'false'].includes(values['--recoverable'])) {
|
|
141
|
+
usageFailure('--recoverable must be true or false');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
result = pauseOrchestration(taskRef, values['--code'], values['--message'], values['--recoverable'] === 'true');
|
|
145
|
+
}
|
|
146
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
147
|
+
if (result.status === 'failed')
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
}
|
|
150
|
+
export { taskOrchestration };
|
|
151
|
+
//# sourceMappingURL=task-orchestration.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { finalizeReviewSummary } from '../task/review-finalization.js';
|
|
2
|
+
const USAGE = 'Usage: agent-infra-internal task-review <task-ref> finalize-summary --stage <analysis|plan|code> --artifact <review-*.md> [--dry-run]\n';
|
|
3
|
+
function failUsage(message) {
|
|
4
|
+
process.stdout.write(`${JSON.stringify({
|
|
5
|
+
status: 'failed',
|
|
6
|
+
changed: false,
|
|
7
|
+
intent: 'finalize-summary',
|
|
8
|
+
error: { code: 'REVIEW_PAYLOAD_INVALID', message }
|
|
9
|
+
})}\n`);
|
|
10
|
+
process.stderr.write(USAGE);
|
|
11
|
+
process.exitCode = 2;
|
|
12
|
+
}
|
|
13
|
+
function taskReview(args = []) {
|
|
14
|
+
if (args[0] === '--help' || args[0] === '-h') {
|
|
15
|
+
process.stdout.write(USAGE);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (args.length < 2) {
|
|
19
|
+
failUsage('task ref and intent are required');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (args[1] !== 'finalize-summary') {
|
|
23
|
+
failUsage(`unknown intent '${args[1]}'`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let stage = '';
|
|
27
|
+
let artifact = '';
|
|
28
|
+
let dryRun = false;
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
31
|
+
const flag = args[index];
|
|
32
|
+
if (!['--stage', '--artifact', '--dry-run'].includes(flag)) {
|
|
33
|
+
failUsage(`unknown option '${flag}'`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (seen.has(flag)) {
|
|
37
|
+
failUsage(`duplicate option '${flag}'`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
seen.add(flag);
|
|
41
|
+
if (flag === '--dry-run') {
|
|
42
|
+
dryRun = true;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const value = args[++index];
|
|
46
|
+
if (!value || value.startsWith('--')) {
|
|
47
|
+
failUsage(`option '${flag}' requires a value`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (flag === '--stage')
|
|
51
|
+
stage = value;
|
|
52
|
+
else
|
|
53
|
+
artifact = value;
|
|
54
|
+
}
|
|
55
|
+
if (!stage || !artifact) {
|
|
56
|
+
failUsage("options '--stage' and '--artifact' are required");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const result = finalizeReviewSummary({ taskRef: args[0], stage, artifact, dryRun });
|
|
60
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
61
|
+
if (result.status === 'failed')
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
}
|
|
64
|
+
export { taskReview };
|
|
65
|
+
//# sourceMappingURL=task-review.js.map
|
package/dist/lib/merge.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { info, ok } from './log.js';
|
|
4
|
+
import { removeDirRecursive } from './remove-dir.js';
|
|
4
5
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
5
6
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
6
7
|
const TITLE_RE = /^# (.+)$/m;
|
|
@@ -527,7 +528,7 @@ function mergeMutableSections({ sourceWorkspace, localWorkspace, backupRoot, rep
|
|
|
527
528
|
if (comparison > 0) {
|
|
528
529
|
backupTaskDir(backupRoot, localMatch.section, localMatch.taskDir, localMatch.taskId);
|
|
529
530
|
report.backupCount += 1;
|
|
530
|
-
|
|
531
|
+
removeDirRecursive(localMatch.taskDir);
|
|
531
532
|
const destinationDir = copyTaskToSection(sourceTask, localWorkspace);
|
|
532
533
|
localIndex.set(sourceTask.taskId, {
|
|
533
534
|
taskId: sourceTask.taskId,
|
|
@@ -7,8 +7,45 @@ function registerPlatformAdapter(adapter) {
|
|
|
7
7
|
function getPlatformAdapter(type) {
|
|
8
8
|
return type ? adapters.get(type) ?? null : null;
|
|
9
9
|
}
|
|
10
|
+
function registerPlatformCapabilities(type, capabilities) {
|
|
11
|
+
const adapter = adapters.get(type);
|
|
12
|
+
if (!adapter)
|
|
13
|
+
throw new Error(`Platform adapter '${type}' must be registered before its capabilities`);
|
|
14
|
+
adapters.set(type, { ...adapter, ...capabilities });
|
|
15
|
+
}
|
|
16
|
+
function hasPlatformCapability(type, capability) {
|
|
17
|
+
const adapter = getPlatformAdapter(type);
|
|
18
|
+
if (capability === 'change-request')
|
|
19
|
+
return typeof adapter?.inspectChangeRequest === 'function';
|
|
20
|
+
if (capability === 'required-checks')
|
|
21
|
+
return typeof adapter?.inspectRequiredChecks === 'function';
|
|
22
|
+
return typeof adapter?.resolveChangeRequestGitEvidence === 'function';
|
|
23
|
+
}
|
|
24
|
+
function unsupported(type, capability) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
error: {
|
|
28
|
+
code: 'PLATFORM_CAPABILITY_UNSUPPORTED',
|
|
29
|
+
message: `Platform '${type || 'none'}' does not provide ${capability}`,
|
|
30
|
+
retryable: false
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function inspectPlatformChangeRequest(type, context) {
|
|
35
|
+
const adapter = getPlatformAdapter(type);
|
|
36
|
+
return adapter?.inspectChangeRequest?.(context) ?? unsupported(type, 'change-request inspection');
|
|
37
|
+
}
|
|
38
|
+
function inspectPlatformRequiredChecks(type, context) {
|
|
39
|
+
const adapter = getPlatformAdapter(type);
|
|
40
|
+
return adapter?.inspectRequiredChecks?.(context) ?? unsupported(type, 'required-checks inspection');
|
|
41
|
+
}
|
|
42
|
+
function resolvePlatformChangeRequestGitEvidence(type, context) {
|
|
43
|
+
const adapter = getPlatformAdapter(type);
|
|
44
|
+
return adapter?.resolveChangeRequestGitEvidence?.(context) ??
|
|
45
|
+
unsupported(type, 'change-request Git evidence');
|
|
46
|
+
}
|
|
10
47
|
function listPlatformAdapters() {
|
|
11
48
|
return [...adapters.keys()].sort();
|
|
12
49
|
}
|
|
13
|
-
export { getPlatformAdapter, listPlatformAdapters, registerPlatformAdapter };
|
|
50
|
+
export { getPlatformAdapter, hasPlatformCapability, inspectPlatformChangeRequest, inspectPlatformRequiredChecks, listPlatformAdapters, registerPlatformAdapter, registerPlatformCapabilities, resolvePlatformChangeRequestGitEvidence };
|
|
14
51
|
//# sourceMappingURL=adapters.js.map
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
5
|
+
import { resolvePlatformChangeRequestGitEvidence } from "./adapters.js";
|
|
6
|
+
import { resolveReviewedHeadRelation } from "./merged-pr-equivalence.js";
|
|
7
|
+
const SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
8
|
+
const REVIEWED_HEAD_REF = "refs/agent-infra/reviewed-head";
|
|
9
|
+
const TARGET_HEAD_REF = "refs/agent-infra/target-head";
|
|
10
|
+
function git(cwd, args) {
|
|
11
|
+
return execFileSync("git", args, {
|
|
12
|
+
cwd,
|
|
13
|
+
encoding: "utf8",
|
|
14
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
15
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16
|
+
}).trim();
|
|
17
|
+
}
|
|
18
|
+
function validRef(ref) {
|
|
19
|
+
return spawnSync("git", ["check-ref-format", ref], {
|
|
20
|
+
encoding: "utf8",
|
|
21
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
22
|
+
}).status === 0;
|
|
23
|
+
}
|
|
24
|
+
function blocked(code, message) {
|
|
25
|
+
return { status: "blocked", code, message };
|
|
26
|
+
}
|
|
27
|
+
function failed(code, message) {
|
|
28
|
+
return { status: "failed", code, message };
|
|
29
|
+
}
|
|
30
|
+
function validMergeIdentity(lastReviewedCommit, pullRequest) {
|
|
31
|
+
if (pullRequest.state !== "closed" ||
|
|
32
|
+
!pullRequest.mergedAt ||
|
|
33
|
+
!pullRequest.mergeCommitSha ||
|
|
34
|
+
!SHA_PATTERN.test(lastReviewedCommit) ||
|
|
35
|
+
lastReviewedCommit !== pullRequest.head.sha) {
|
|
36
|
+
return failed("PR_MERGE_IDENTITY_INVALID", "PR merge identity does not match the reviewed head");
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
function resolveMaterializedReviewedHeadRelation(input) {
|
|
41
|
+
const identityFailure = validMergeIdentity(input.lastReviewedCommit, input.pullRequest);
|
|
42
|
+
if (identityFailure)
|
|
43
|
+
return identityFailure;
|
|
44
|
+
const source = resolvePlatformChangeRequestGitEvidence(input.platformType, {
|
|
45
|
+
cwd: input.cwd,
|
|
46
|
+
repository: input.pullRequest.repository,
|
|
47
|
+
number: input.pullRequest.number,
|
|
48
|
+
pullRequest: input.pullRequest
|
|
49
|
+
});
|
|
50
|
+
if (!source.ok || !source.value) {
|
|
51
|
+
return blocked(source.error?.code || "PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE", source.error?.message || "Unable to resolve authoritative PR Git evidence");
|
|
52
|
+
}
|
|
53
|
+
if (!source.value.remoteUrl.trim() ||
|
|
54
|
+
source.value.remoteUrl.trimStart().startsWith("-") ||
|
|
55
|
+
!validRef(source.value.reviewedHeadRef) ||
|
|
56
|
+
!validRef(source.value.targetHeadRef)) {
|
|
57
|
+
return blocked("PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE", "Platform returned an invalid PR Git evidence source");
|
|
58
|
+
}
|
|
59
|
+
let evidenceRoot = null;
|
|
60
|
+
let phase = "prepare";
|
|
61
|
+
let result = blocked("PR_MERGE_GIT_EVIDENCE_UNAVAILABLE", "Unable to prepare isolated PR Git evidence");
|
|
62
|
+
try {
|
|
63
|
+
evidenceRoot = fs.mkdtempSync(path.join(os.tmpdir(), "agent-infra-pr-evidence-"));
|
|
64
|
+
execFileSync("git", ["init", "--bare", "-q", evidenceRoot], {
|
|
65
|
+
cwd: input.cwd,
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
68
|
+
});
|
|
69
|
+
phase = "fetch";
|
|
70
|
+
git(evidenceRoot, [
|
|
71
|
+
"fetch",
|
|
72
|
+
"--no-tags",
|
|
73
|
+
"--no-write-fetch-head",
|
|
74
|
+
source.value.remoteUrl,
|
|
75
|
+
`+${source.value.reviewedHeadRef}:${REVIEWED_HEAD_REF}`,
|
|
76
|
+
`+${source.value.targetHeadRef}:${TARGET_HEAD_REF}`
|
|
77
|
+
]);
|
|
78
|
+
phase = "resolve";
|
|
79
|
+
const fetchedReviewedHead = git(evidenceRoot, ["rev-parse", `${REVIEWED_HEAD_REF}^{commit}`]);
|
|
80
|
+
const comparisonHead = git(evidenceRoot, ["rev-parse", `${TARGET_HEAD_REF}^{commit}`]);
|
|
81
|
+
if (fetchedReviewedHead !== input.pullRequest.head.sha) {
|
|
82
|
+
result = failed("PR_MERGE_FETCHED_HEAD_MISMATCH", "Fetched PR head does not match the authoritative platform snapshot");
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
result = resolveReviewedHeadRelation({
|
|
86
|
+
gitRoot: evidenceRoot,
|
|
87
|
+
comparisonHead,
|
|
88
|
+
lastReviewedCommit: input.lastReviewedCommit,
|
|
89
|
+
pullRequest: input.pullRequest
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
result = phase === "fetch"
|
|
95
|
+
? blocked("PR_MERGE_EVIDENCE_FETCH_FAILED", "Unable to fetch PR Git evidence; verify Git credentials and platform refs")
|
|
96
|
+
: phase === "resolve"
|
|
97
|
+
? blocked("PR_MERGE_OBJECT_MISSING", "Fetched PR Git evidence is incomplete")
|
|
98
|
+
: blocked("PR_MERGE_GIT_EVIDENCE_UNAVAILABLE", "Unable to prepare isolated PR Git evidence");
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
if (evidenceRoot) {
|
|
102
|
+
try {
|
|
103
|
+
fs.rmSync(evidenceRoot, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
result = blocked("PR_MERGE_EVIDENCE_CLEANUP_FAILED", "Unable to clean up isolated PR Git evidence");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
export { resolveMaterializedReviewedHeadRelation };
|
|
113
|
+
//# sourceMappingURL=change-request-git-evidence.js.map
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import { execFileSync } from "node:child_process";
|
|
1
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
2
|
const SHA_PATTERN = /^[0-9a-f]{40}$/i;
|
|
3
3
|
function git(cwd, args) {
|
|
4
|
-
return execFileSync("git", args, {
|
|
4
|
+
return execFileSync("git", args, {
|
|
5
|
+
cwd,
|
|
6
|
+
encoding: "utf8",
|
|
7
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
8
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
9
|
+
});
|
|
5
10
|
}
|
|
6
11
|
function commitExists(gitRoot, sha) {
|
|
7
12
|
try {
|
|
@@ -12,21 +17,49 @@ function commitExists(gitRoot, sha) {
|
|
|
12
17
|
return false;
|
|
13
18
|
}
|
|
14
19
|
}
|
|
20
|
+
function isAncestor(gitRoot, ancestor, descendant) {
|
|
21
|
+
const result = spawnSync("git", ["merge-base", "--is-ancestor", ancestor, descendant], {
|
|
22
|
+
cwd: gitRoot,
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
25
|
+
});
|
|
26
|
+
if (result.status === 0)
|
|
27
|
+
return { status: "yes" };
|
|
28
|
+
if (result.status === 1)
|
|
29
|
+
return { status: "no" };
|
|
30
|
+
return { status: "error" };
|
|
31
|
+
}
|
|
15
32
|
export function resolveReviewedHeadRelation(input) {
|
|
16
|
-
const { gitRoot,
|
|
17
|
-
if (
|
|
33
|
+
const { gitRoot, comparisonHead, lastReviewedCommit, pullRequest } = input;
|
|
34
|
+
if (comparisonHead === lastReviewedCommit &&
|
|
35
|
+
lastReviewedCommit === pullRequest.head.sha &&
|
|
36
|
+
!(pullRequest.state === "closed" && pullRequest.mergedAt && pullRequest.mergeCommitSha)) {
|
|
18
37
|
return { status: "strict", reviewedHead: lastReviewedCommit };
|
|
19
38
|
}
|
|
20
39
|
if (pullRequest.state !== "closed" || !pullRequest.mergedAt || !pullRequest.mergeCommitSha ||
|
|
21
40
|
lastReviewedCommit !== pullRequest.head.sha) {
|
|
22
41
|
return { status: "failed", code: "PR_MERGE_IDENTITY_INVALID", message: "PR merge identity does not match the reviewed head" };
|
|
23
42
|
}
|
|
24
|
-
const shas = [pullRequest.base.sha, pullRequest.head.sha, pullRequest.mergeCommitSha,
|
|
43
|
+
const shas = [pullRequest.base.sha, pullRequest.head.sha, pullRequest.mergeCommitSha, comparisonHead];
|
|
25
44
|
if (shas.some((sha) => !SHA_PATTERN.test(sha) || !commitExists(gitRoot, sha))) {
|
|
26
45
|
return { status: "blocked", code: "PR_MERGE_OBJECT_MISSING", message: "Required PR merge Git objects are unavailable" };
|
|
27
46
|
}
|
|
47
|
+
const ancestry = isAncestor(gitRoot, pullRequest.mergeCommitSha, comparisonHead);
|
|
48
|
+
if (ancestry.status === "no") {
|
|
49
|
+
return {
|
|
50
|
+
status: "failed",
|
|
51
|
+
code: "PR_MERGE_TARGET_MISMATCH",
|
|
52
|
+
message: "PR merge commit is not in the authoritative target history"
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (ancestry.status === "error") {
|
|
56
|
+
return {
|
|
57
|
+
status: "blocked",
|
|
58
|
+
code: "PR_MERGE_GIT_EVIDENCE_UNAVAILABLE",
|
|
59
|
+
message: "Unable to verify PR merge ancestry"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
28
62
|
try {
|
|
29
|
-
git(gitRoot, ["merge-base", "--is-ancestor", pullRequest.mergeCommitSha, localHead]);
|
|
30
63
|
const parents = git(gitRoot, ["rev-list", "--parents", "-n", "1", pullRequest.mergeCommitSha]).trim().split(/\s+/);
|
|
31
64
|
if (parents.length !== 2) {
|
|
32
65
|
return { status: "failed", code: "PR_MERGE_TOPOLOGY_INVALID", message: "PR merge commit is not a single-parent squash commit" };
|
|
@@ -37,10 +70,6 @@ export function resolveReviewedHeadRelation(input) {
|
|
|
37
70
|
if (reviewedDiff !== mergedDiff) {
|
|
38
71
|
return { status: "failed", code: "PR_MERGE_CONTENT_MISMATCH", message: "Squash merge content differs from the reviewed PR changes" };
|
|
39
72
|
}
|
|
40
|
-
const postMerge = git(gitRoot, ["rev-list", `${pullRequest.mergeCommitSha}..${localHead}`, "--", ...pathspecs]).trim();
|
|
41
|
-
if (postMerge) {
|
|
42
|
-
return { status: "failed", code: "POST_MERGE_CHANGES", message: "Protected paths changed after the squash merge" };
|
|
43
|
-
}
|
|
44
73
|
}
|
|
45
74
|
catch {
|
|
46
75
|
return { status: "blocked", code: "PR_MERGE_GIT_EVIDENCE_UNAVAILABLE", message: "Unable to verify PR merge topology or content" };
|
|
@@ -48,7 +77,8 @@ export function resolveReviewedHeadRelation(input) {
|
|
|
48
77
|
return {
|
|
49
78
|
status: "merged-equivalent",
|
|
50
79
|
reviewedHead: lastReviewedCommit,
|
|
51
|
-
mergeCommit: pullRequest.mergeCommitSha
|
|
80
|
+
mergeCommit: pullRequest.mergeCommitSha,
|
|
81
|
+
comparisonHead
|
|
52
82
|
};
|
|
53
83
|
}
|
|
54
84
|
//# sourceMappingURL=merged-pr-equivalence.js.map
|