@fitlab-ai/agent-infra 0.9.2 → 0.9.4
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 +12 -9
- package/README.zh-CN.md +12 -9
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +134 -108
- package/dist/lib/agent-clients/adapter.js +137 -4
- package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
- package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
- package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
- package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +27 -4
- package/dist/lib/agent-clients/adapters/codex.js +9 -0
- package/dist/lib/agent-clients/adapters/index.js +2 -2
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
- package/dist/lib/agent-clients/adapters/opencode.js +24 -3
- package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/dist/lib/agent-clients/config.js +42 -4
- package/dist/lib/agent-clients/next-steps.js +11 -2
- package/dist/lib/agent-clients/reconcile.js +2 -1
- package/dist/lib/agent-clients/registry.js +16 -2
- package/dist/lib/agent-clients/schema.js +18 -0
- package/dist/lib/agent-clients/tokens.js +49 -0
- package/dist/lib/agent-clients/types.js +1 -1
- package/dist/lib/defaults.json +1 -1
- package/dist/lib/git/workflow.js +54 -1
- package/dist/lib/internal/agent-client.js +61 -4
- package/dist/lib/internal/git-workflow.js +6 -1
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +29 -2
- package/dist/lib/internal/platform-issue.js +7 -0
- package/dist/lib/internal/platform-pr-review.js +132 -0
- package/dist/lib/internal/platform-pr.js +20 -2
- package/dist/lib/internal/platform-release-notes.js +52 -16
- package/dist/lib/internal/pr-review-grade.js +140 -0
- package/dist/lib/internal/release-workflow.js +13 -6
- package/dist/lib/internal/sandbox-control.js +24 -0
- package/dist/lib/internal/task-activity.js +105 -0
- package/dist/lib/internal/task-event.js +17 -1
- package/dist/lib/internal/task-ledger.js +12 -3
- package/dist/lib/internal/task-lifecycle.js +26 -1
- package/dist/lib/internal/task-orchestration.js +167 -18
- package/dist/lib/internal/task-review.js +8 -3
- package/dist/lib/platform/adapters.js +8 -1
- package/dist/lib/platform/completion-backfill.js +122 -0
- package/dist/lib/platform/github-client.js +19 -2
- package/dist/lib/platform/issue-comments.js +3 -2
- package/dist/lib/platform/merged-pr-equivalence.js +115 -5
- package/dist/lib/platform/pr-checks.js +65 -42
- package/dist/lib/platform/pr-review.js +166 -0
- package/dist/lib/platform/pull-requests.js +371 -4
- package/dist/lib/platform/release-notes.js +85 -3
- package/dist/lib/platform/releases.js +13 -5
- package/dist/lib/pr-review/evidence-grading.js +229 -0
- package/dist/lib/process-data/index.js +239 -0
- package/dist/lib/process-data/normalize.js +198 -0
- package/dist/lib/process-data/privacy.js +33 -0
- package/dist/lib/process-data/reconcile.js +95 -0
- package/dist/lib/process-data/repair.js +58 -0
- package/dist/lib/process-data/sources.js +345 -0
- package/dist/lib/process-data/store.js +282 -0
- package/dist/lib/process-data/types.js +2 -0
- package/dist/lib/run/index.js +4 -3
- package/dist/lib/run/tui.js +10 -13
- package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
- package/dist/lib/sandbox/capture.js +7 -2
- package/dist/lib/sandbox/commands/create.js +109 -248
- package/dist/lib/sandbox/commands/enter.js +15 -47
- package/dist/lib/sandbox/commands/list-running.js +16 -6
- package/dist/lib/sandbox/commands/ls.js +10 -6
- package/dist/lib/sandbox/commands/rebuild.js +4 -3
- package/dist/lib/sandbox/commands/refresh.js +4 -84
- package/dist/lib/sandbox/commands/rm.js +24 -3
- package/dist/lib/sandbox/commands/show.js +13 -4
- package/dist/lib/sandbox/commands/start.js +7 -4
- package/dist/lib/sandbox/config.js +2 -0
- package/dist/lib/sandbox/constants.js +6 -0
- package/dist/lib/sandbox/control/client.js +44 -0
- package/dist/lib/sandbox/control/protocol.js +35 -0
- package/dist/lib/sandbox/control/server.js +109 -0
- package/dist/lib/sandbox/dockerfile.js +36 -13
- package/dist/lib/sandbox/mounts.js +17 -4
- package/dist/lib/sandbox/recovery.js +128 -5
- package/dist/lib/sandbox/redaction.js +12 -0
- package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/dist/lib/sandbox/workspace-identity.js +95 -0
- package/dist/lib/sandbox/workspace-view.js +103 -0
- package/dist/lib/task/activity-intent.js +307 -0
- package/dist/lib/task/artifact-lifecycle.js +2 -1
- package/dist/lib/task/commands/log.js +15 -14
- package/dist/lib/task/commit-finalization.js +267 -0
- package/dist/lib/task/commit-intent.js +165 -0
- package/dist/lib/task/decision-intents.js +31 -17
- package/dist/lib/task/delegation-receipts.js +25 -4
- package/dist/lib/task/events.js +67 -28
- package/dist/lib/task/finalization-artifacts.js +35 -0
- package/dist/lib/task/implementation-inputs.js +54 -7
- package/dist/lib/task/ledger-intents.js +35 -1
- package/dist/lib/task/lifecycle-execution.js +46 -0
- package/dist/lib/task/orchestration.js +753 -54
- package/dist/lib/task/resolve-ref.js +41 -1
- package/dist/lib/task/review-finalization.js +29 -9
- package/dist/lib/task/task-execution-lock.js +231 -0
- package/dist/lib/task/verification-checks.js +1 -1
- package/dist/lib/task/verification-engine.js +218 -2
- package/dist/lib/task/verification.js +4 -3
- package/dist/lib/task/workspace-snapshot.js +17 -5
- package/lib/agent-clients/adapter.ts +201 -4
- package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
- package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
- package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
- package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
- package/lib/agent-clients/adapters/claude-code.ts +31 -4
- package/lib/agent-clients/adapters/codex.ts +9 -0
- package/lib/agent-clients/adapters/index.ts +2 -2
- package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
- package/lib/agent-clients/adapters/opencode.ts +27 -3
- package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/lib/agent-clients/config.ts +49 -4
- package/lib/agent-clients/next-steps.ts +15 -2
- package/lib/agent-clients/reconcile.ts +2 -1
- package/lib/agent-clients/registry.ts +19 -1
- package/lib/agent-clients/schema.ts +18 -0
- package/lib/agent-clients/tokens.ts +53 -0
- package/lib/agent-clients/types.ts +15 -1
- package/lib/defaults.json +1 -1
- package/lib/git/workflow.ts +66 -2
- package/lib/internal/agent-client.ts +59 -4
- package/lib/internal/git-workflow.ts +5 -1
- package/lib/internal/platform-checks.ts +4 -4
- package/lib/internal/platform-comment.ts +17 -2
- package/lib/internal/platform-issue.ts +4 -0
- package/lib/internal/platform-pr-review.ts +104 -0
- package/lib/internal/platform-pr.ts +14 -1
- package/lib/internal/platform-release-notes.ts +59 -16
- package/lib/internal/pr-review-grade.ts +130 -0
- package/lib/internal/release-workflow.ts +13 -6
- package/lib/internal/sandbox-control.ts +24 -0
- package/lib/internal/task-activity.ts +91 -0
- package/lib/internal/task-event.ts +9 -1
- package/lib/internal/task-ledger.ts +12 -3
- package/lib/internal/task-lifecycle.ts +26 -1
- package/lib/internal/task-orchestration.ts +123 -14
- package/lib/internal/task-review.ts +8 -3
- package/lib/platform/adapters.ts +29 -2
- package/lib/platform/completion-backfill.ts +134 -0
- package/lib/platform/github-client.ts +20 -2
- package/lib/platform/issue-comments.ts +3 -2
- package/lib/platform/merged-pr-equivalence.ts +127 -5
- package/lib/platform/pr-checks.ts +73 -46
- package/lib/platform/pr-review.ts +201 -0
- package/lib/platform/pull-requests.ts +444 -3
- package/lib/platform/release-notes.ts +100 -4
- package/lib/platform/releases.ts +16 -4
- package/lib/pr-review/evidence-grading.ts +318 -0
- package/lib/process-data/index.ts +224 -0
- package/lib/process-data/normalize.ts +202 -0
- package/lib/process-data/privacy.ts +41 -0
- package/lib/process-data/reconcile.ts +110 -0
- package/lib/process-data/repair.ts +64 -0
- package/lib/process-data/sources.ts +354 -0
- package/lib/process-data/store.ts +315 -0
- package/lib/process-data/types.ts +135 -0
- package/lib/run/index.ts +5 -3
- package/lib/run/tui.ts +14 -12
- package/lib/sandbox/agent-client-reconciler.ts +44 -2
- package/lib/sandbox/capture.ts +9 -2
- package/lib/sandbox/commands/create.ts +126 -280
- package/lib/sandbox/commands/enter.ts +13 -59
- package/lib/sandbox/commands/list-running.ts +23 -6
- package/lib/sandbox/commands/ls.ts +22 -6
- package/lib/sandbox/commands/rebuild.ts +4 -3
- package/lib/sandbox/commands/refresh.ts +4 -126
- package/lib/sandbox/commands/rm.ts +24 -3
- package/lib/sandbox/commands/show.ts +30 -4
- package/lib/sandbox/commands/start.ts +6 -4
- package/lib/sandbox/config.ts +4 -0
- package/lib/sandbox/constants.ts +8 -0
- package/lib/sandbox/control/client.ts +55 -0
- package/lib/sandbox/control/protocol.ts +75 -0
- package/lib/sandbox/control/server.ts +117 -0
- package/lib/sandbox/dockerfile.ts +66 -13
- package/lib/sandbox/mounts.ts +24 -4
- package/lib/sandbox/recovery.ts +163 -6
- package/lib/sandbox/redaction.ts +14 -0
- package/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/lib/sandbox/tool-types.ts +16 -0
- package/lib/sandbox/workspace-identity.ts +123 -0
- package/lib/sandbox/workspace-view.ts +143 -0
- package/lib/server/server.schema.json +2 -2
- package/lib/task/activity-intent.ts +417 -0
- package/lib/task/artifact-lifecycle.ts +2 -1
- package/lib/task/commands/log.ts +15 -14
- package/lib/task/commit-finalization.ts +356 -0
- package/lib/task/commit-intent.ts +230 -0
- package/lib/task/decision-intents.ts +32 -12
- package/lib/task/delegation-receipts.ts +34 -5
- package/lib/task/events.ts +75 -30
- package/lib/task/finalization-artifacts.ts +58 -0
- package/lib/task/implementation-inputs.ts +62 -8
- package/lib/task/ledger-intents.ts +43 -3
- package/lib/task/lifecycle-execution.ts +106 -0
- package/lib/task/orchestration.ts +1019 -72
- package/lib/task/resolve-ref.ts +46 -1
- package/lib/task/review-finalization.ts +40 -9
- package/lib/task/task-execution-lock.ts +296 -0
- package/lib/task/verification-checks.ts +1 -1
- package/lib/task/verification-engine.ts +249 -2
- package/lib/task/verification.ts +5 -3
- package/lib/task/workspace-snapshot.ts +26 -5
- package/package.json +3 -3
- package/templates/.agents/QUICKSTART.en.md +25 -8
- package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
- package/templates/.agents/README.en.md +10 -7
- package/templates/.agents/README.zh-CN.md +10 -7
- package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +4 -2
- package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
- package/templates/.agents/rules/human-decision-context.en.md +11 -2
- package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
- package/templates/.agents/rules/issue-fields.github.en.md +3 -1
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-sync.github.en.md +7 -3
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
- package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
- package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
- package/templates/.agents/rules/next-step-output.en.md +3 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
- package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-sync.en.md +3 -1
- package/templates/.agents/rules/pr-sync.github.en.md +11 -1
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
- package/templates/.agents/rules/release-commands.en.md +1 -1
- package/templates/.agents/rules/release-commands.github.en.md +12 -2
- package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
- package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +13 -0
- package/templates/.agents/rules/task-management.zh-CN.md +13 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
- package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +12 -9
- package/templates/.agents/skills/commit/SKILL.en.md +23 -11
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +29 -11
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +22 -7
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +22 -7
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -5
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
- package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/release/SKILL.en.md +16 -10
- package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
- package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-pr/SKILL.en.md +186 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +186 -0
- package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +98 -107
- package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
- package/templates/.agents/workflows/code-review.en.yaml +1 -1
- package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +4 -4
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
- package/templates/.agents/workflows/refactoring.en.yaml +3 -3
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
- package/templates/.claude/commands/post-release.en.md +1 -0
- package/templates/.claude/commands/post-release.zh-CN.md +1 -0
- package/templates/.claude/commands/review-pr.en.md +8 -0
- package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.claude/rules/tool-preferences.en.md +20 -0
- package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
- package/templates/.opencode/commands/post-release.en.md +2 -0
- package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
- package/templates/.opencode/commands/review-pr.en.md +11 -0
- package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
- package/templates/.opencode/commands/watch-pr.en.md +1 -1
- package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
- package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
- package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
- package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
- package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
- package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/release.en.toml +0 -8
- package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/test.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
- package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
- package/templates/.gemini/settings.json +0 -3
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
3
|
+
import { resolveTaskRef } from './resolve-ref.js';
|
|
4
|
+
import { locateActivityLog, appendActivityEntry } from './activity-log.js';
|
|
5
|
+
import { assertWritableInventory, buildArtifactLinkSection, inspectTaskArtifacts, parseArtifactName } from './artifact-lifecycle.js';
|
|
6
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
7
|
+
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
8
|
+
const HEAD_RE = /^[0-9a-f]{40}$/;
|
|
9
|
+
const REVIEW_ACTION_RE = /^Review PR \(Round ([1-9]\d*)\)( \[started\])?$/;
|
|
10
|
+
const VERDICTS = new Set(['approved', 'changes-requested', 'commented']);
|
|
11
|
+
const OUTCOMES = new Set(['aborted', 'superseded']);
|
|
12
|
+
const SUCCESS_STATUSES = new Set(['applied', 'no-op']);
|
|
13
|
+
const ARTIFACT_STATUSES = new Set([
|
|
14
|
+
'pending', 'applied', 'no-op', 'blocked', 'failed', 'aborted', 'superseded'
|
|
15
|
+
]);
|
|
16
|
+
function failed(intent, code, message, taskId = null) {
|
|
17
|
+
return {
|
|
18
|
+
status: 'failed', changed: false, intent: intent.kind, taskId,
|
|
19
|
+
artifact: intent.artifact, operations: [], error: { code, message }
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function inspectionFailed(code, message, taskId = null) {
|
|
23
|
+
return {
|
|
24
|
+
status: 'failed', changed: false, intent: 'pr-review-inspect', taskId,
|
|
25
|
+
snapshot: null, error: { code, message }
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function parseArtifactRecord(identity) {
|
|
29
|
+
let content;
|
|
30
|
+
try {
|
|
31
|
+
content = fs.readFileSync(identity.path, 'utf8');
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return { code: 'ACTIVITY_ARTIFACT_READ_FAILED', message: error instanceof Error ? error.message : String(error) };
|
|
35
|
+
}
|
|
36
|
+
const heads = [...content.matchAll(/^- \*\*(?:被审 head SHA|Reviewed Head SHA)\*\*[::]\s*([0-9a-f]{40})\s*$/gm)];
|
|
37
|
+
if (heads.length !== 1) {
|
|
38
|
+
return { code: 'ACTIVITY_ARTIFACT_INVALID', message: `${identity.name} must contain exactly one reviewed head SHA` };
|
|
39
|
+
}
|
|
40
|
+
const statuses = [...content.matchAll(/^- \*\*(?:正式 Review 状态|Formal Review Status)\*\*[::][ \t]*([a-z-]+)[^\r\n]*$/gm)];
|
|
41
|
+
const status = statuses[0]?.[1];
|
|
42
|
+
if (statuses.length !== 1 || !status || !ARTIFACT_STATUSES.has(status)) {
|
|
43
|
+
return { code: 'ACTIVITY_ARTIFACT_INVALID', message: `${identity.name} must contain exactly one supported formal review status` };
|
|
44
|
+
}
|
|
45
|
+
return { identity, head: heads[0][1], status: status };
|
|
46
|
+
}
|
|
47
|
+
function toIdentity(state, terminal = false) {
|
|
48
|
+
return {
|
|
49
|
+
round: state.identity.round,
|
|
50
|
+
artifact: state.identity.name,
|
|
51
|
+
head: state.head,
|
|
52
|
+
agent: terminal ? state.terminalAgent : state.startedAgent,
|
|
53
|
+
artifactStatus: state.status,
|
|
54
|
+
note: terminal ? state.terminalNote : null
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function readSnapshot(taskRef, repoRoot) {
|
|
58
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot });
|
|
59
|
+
if (!resolved.ok)
|
|
60
|
+
return { ok: false, code: resolved.code, message: resolved.message, taskId: resolved.taskId };
|
|
61
|
+
if (resolved.state !== 'active') {
|
|
62
|
+
return { ok: false, code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected active`, taskId: resolved.taskId };
|
|
63
|
+
}
|
|
64
|
+
const inventory = inspectTaskArtifacts(taskRef, 'pr-review', { repoRoot: resolved.repoRoot });
|
|
65
|
+
if (inventory.status === 'failed') {
|
|
66
|
+
return { ok: false, code: inventory.error?.code ?? 'ACTIVITY_ARTIFACT_INVALID', message: inventory.error?.message ?? 'artifact inspection failed', taskId: resolved.taskId };
|
|
67
|
+
}
|
|
68
|
+
const topology = assertWritableInventory(inventory);
|
|
69
|
+
if (topology)
|
|
70
|
+
return { ok: false, code: topology.code, message: topology.message, taskId: resolved.taskId };
|
|
71
|
+
let content;
|
|
72
|
+
try {
|
|
73
|
+
content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return { ok: false, code: 'TASK_READ_FAILED', message: error instanceof Error ? error.message : String(error), taskId: resolved.taskId };
|
|
77
|
+
}
|
|
78
|
+
const activity = locateActivityLog(content);
|
|
79
|
+
if (!activity)
|
|
80
|
+
return { ok: false, code: 'ACTIVITY_SECTION_MISSING', message: 'task has no unique Activity Log section', taskId: resolved.taskId };
|
|
81
|
+
const knownRounds = new Set(inventory.artifacts.map((artifact) => artifact.round));
|
|
82
|
+
for (const entry of activity.entries) {
|
|
83
|
+
const match = REVIEW_ACTION_RE.exec(entry.step);
|
|
84
|
+
if (match && !knownRounds.has(Number(match[1]))) {
|
|
85
|
+
return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `Activity Log round ${match[1]} has no canonical pr-review artifact`, taskId: resolved.taskId };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const states = [];
|
|
89
|
+
for (const identity of inventory.artifacts) {
|
|
90
|
+
const parsed = parseArtifactRecord(identity);
|
|
91
|
+
if ('code' in parsed)
|
|
92
|
+
return { ok: false, ...parsed, taskId: resolved.taskId };
|
|
93
|
+
const base = `Review PR (Round ${identity.round})`;
|
|
94
|
+
const started = activity.entries.filter((entry) => entry.step === `${base} [started]`);
|
|
95
|
+
const terminal = activity.entries.filter((entry) => entry.step === base);
|
|
96
|
+
if (started.length > 1 || terminal.length > 1) {
|
|
97
|
+
return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} has duplicate started or terminal rows`, taskId: resolved.taskId };
|
|
98
|
+
}
|
|
99
|
+
if (terminal.length === 1 && started.length === 0 && !terminal[0].note.startsWith('receipt ')) {
|
|
100
|
+
return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} has a terminal row without a matching started row`, taskId: resolved.taskId };
|
|
101
|
+
}
|
|
102
|
+
if (terminal.length === 1) {
|
|
103
|
+
const note = terminal[0].note;
|
|
104
|
+
const terminalMatchesStatus = (note.startsWith('Verdict: ') && SUCCESS_STATUSES.has(parsed.status))
|
|
105
|
+
|| (note.startsWith('Outcome: Aborted, ') && parsed.status === 'aborted')
|
|
106
|
+
|| (note.startsWith('Outcome: Superseded, ') && parsed.status === 'superseded')
|
|
107
|
+
|| (note.startsWith('receipt ') && SUCCESS_STATUSES.has(parsed.status));
|
|
108
|
+
if (!terminalMatchesStatus) {
|
|
109
|
+
return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: `${base} terminal note conflicts with artifact status ${parsed.status}`, taskId: resolved.taskId };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
states.push({
|
|
113
|
+
...parsed,
|
|
114
|
+
startedAgent: started[0]?.agent ?? null,
|
|
115
|
+
terminalAgent: terminal[0]?.agent ?? null,
|
|
116
|
+
terminalNote: terminal[0]?.note ?? null
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const openStates = states.filter((state) => state.startedAgent !== null && state.terminalNote === null);
|
|
120
|
+
const preparedStates = states.filter((state) => state.status === 'pending' && state.startedAgent === null && state.terminalNote === null);
|
|
121
|
+
if (openStates.length > 1 || preparedStates.length > 1) {
|
|
122
|
+
return { ok: false, code: 'ACTIVITY_STATE_CONFLICT', message: 'multiple prepared or open PR review rounds exist', taskId: resolved.taskId };
|
|
123
|
+
}
|
|
124
|
+
const terminalStates = states.filter((state) => state.terminalNote !== null);
|
|
125
|
+
const successfulStates = states.filter((state) => SUCCESS_STATUSES.has(state.status));
|
|
126
|
+
const latestTerminal = terminalStates.at(-1) ?? null;
|
|
127
|
+
const latestSuccessful = successfulStates.at(-1) ?? null;
|
|
128
|
+
return {
|
|
129
|
+
ok: true,
|
|
130
|
+
taskId: resolved.taskId,
|
|
131
|
+
taskDir: resolved.taskDir,
|
|
132
|
+
taskMdPath: resolved.taskMdPath,
|
|
133
|
+
repoRoot: resolved.repoRoot,
|
|
134
|
+
content,
|
|
135
|
+
states,
|
|
136
|
+
snapshot: {
|
|
137
|
+
nextRound: inventory.next?.round ?? 1,
|
|
138
|
+
prepared: preparedStates[0] ? toIdentity(preparedStates[0]) : null,
|
|
139
|
+
open: openStates[0] ? toIdentity(openStates[0]) : null,
|
|
140
|
+
latestTerminal: latestTerminal ? toIdentity(latestTerminal, true) : null,
|
|
141
|
+
latestSuccessful: latestSuccessful ? toIdentity(latestSuccessful, true) : null
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
export function inspectPrReviewActivity(intent, options = {}) {
|
|
146
|
+
const state = readSnapshot(intent.taskRef, options.repoRoot);
|
|
147
|
+
if (!state.ok)
|
|
148
|
+
return inspectionFailed(state.code, state.message, state.taskId);
|
|
149
|
+
return { status: 'ready', changed: false, intent: intent.kind, taskId: state.taskId, snapshot: state.snapshot, error: null };
|
|
150
|
+
}
|
|
151
|
+
function validateIntent(intent) {
|
|
152
|
+
const agent = normalizeAgentToken(intent.agent);
|
|
153
|
+
if (!agent)
|
|
154
|
+
return { code: 'ACTIVITY_INTENT_INVALID', message: 'agent must be a supported collaborator token' };
|
|
155
|
+
const artifact = parseArtifactName(intent.artifact);
|
|
156
|
+
if (!artifact || artifact.family !== 'pr-review') {
|
|
157
|
+
return { code: 'ACTIVITY_ARTIFACT_INVALID', message: `artifact '${intent.artifact}' is not a canonical pr-review artifact` };
|
|
158
|
+
}
|
|
159
|
+
if (!HEAD_RE.test(intent.head))
|
|
160
|
+
return { code: 'ACTIVITY_INTENT_INVALID', message: 'head must be a lowercase 40-character SHA' };
|
|
161
|
+
if (intent.kind === 'pr-review-complete') {
|
|
162
|
+
if (!VERDICTS.has(intent.verdict))
|
|
163
|
+
return { code: 'ACTIVITY_INTENT_INVALID', message: 'verdict is invalid' };
|
|
164
|
+
if (![intent.blockers, intent.major, intent.minor].every((value) => Number.isSafeInteger(value) && value >= 0)) {
|
|
165
|
+
return { code: 'ACTIVITY_INTENT_INVALID', message: 'finding counts must be non-negative safe integers' };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (intent.kind === 'pr-review-terminate') {
|
|
169
|
+
if (!OUTCOMES.has(intent.outcome))
|
|
170
|
+
return { code: 'ACTIVITY_INTENT_INVALID', message: 'outcome is invalid' };
|
|
171
|
+
if (!intent.reason.trim() || /[\r\n]/.test(intent.reason) || intent.reason !== intent.reason.trim()) {
|
|
172
|
+
return { code: 'ACTIVITY_INTENT_INVALID', message: 'reason must be a non-empty trimmed single-line value' };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { agent, artifact };
|
|
176
|
+
}
|
|
177
|
+
function verdictLabel(verdict) {
|
|
178
|
+
if (verdict === 'approved')
|
|
179
|
+
return 'Approved';
|
|
180
|
+
if (verdict === 'changes-requested')
|
|
181
|
+
return 'Changes Requested';
|
|
182
|
+
return 'Commented';
|
|
183
|
+
}
|
|
184
|
+
function outcomeLabel(outcome) {
|
|
185
|
+
return outcome === 'aborted' ? 'Aborted' : 'Superseded';
|
|
186
|
+
}
|
|
187
|
+
function terminalNote(intent) {
|
|
188
|
+
if (intent.kind === 'pr-review-complete') {
|
|
189
|
+
return `Verdict: ${verdictLabel(intent.verdict)}, blockers: ${intent.blockers}, major: ${intent.major}, minor: ${intent.minor} → ${intent.artifact}`;
|
|
190
|
+
}
|
|
191
|
+
return `Outcome: ${outcomeLabel(intent.outcome)}, reason: ${intent.reason} → ${intent.artifact}`;
|
|
192
|
+
}
|
|
193
|
+
function applyLocked(intent, normalizedAgent, options) {
|
|
194
|
+
const state = readSnapshot(intent.taskRef, options.repoRoot);
|
|
195
|
+
if (!state.ok)
|
|
196
|
+
return failed(intent, state.code, state.message, state.taskId);
|
|
197
|
+
const parsed = parseArtifactName(intent.artifact);
|
|
198
|
+
const roundState = state.states.find((item) => item.identity.name === parsed.name);
|
|
199
|
+
if (!roundState)
|
|
200
|
+
return failed(intent, 'ACTIVITY_ARTIFACT_INVALID', `artifact '${intent.artifact}' is not landed`, state.taskId);
|
|
201
|
+
if (roundState.head !== intent.head) {
|
|
202
|
+
return failed(intent, 'ACTIVITY_IDENTITY_MISMATCH', `head does not match ${intent.artifact}`, state.taskId);
|
|
203
|
+
}
|
|
204
|
+
const base = `Review PR (Round ${parsed.round})`;
|
|
205
|
+
let step;
|
|
206
|
+
let note;
|
|
207
|
+
let linkArtifact = false;
|
|
208
|
+
if (intent.kind === 'pr-review-start') {
|
|
209
|
+
if (roundState.terminalNote !== null) {
|
|
210
|
+
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${base} is already terminal`, state.taskId);
|
|
211
|
+
}
|
|
212
|
+
if (roundState.startedAgent !== null) {
|
|
213
|
+
return { status: 'no-op', changed: false, intent: intent.kind, taskId: state.taskId, artifact: intent.artifact, operations: [], error: null };
|
|
214
|
+
}
|
|
215
|
+
if (roundState.status !== 'pending') {
|
|
216
|
+
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} must be pending before start`, state.taskId);
|
|
217
|
+
}
|
|
218
|
+
if (state.snapshot.open && state.snapshot.open.artifact !== intent.artifact) {
|
|
219
|
+
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `another PR review round is open: ${state.snapshot.open.artifact}`, state.taskId);
|
|
220
|
+
}
|
|
221
|
+
step = `${base} [started]`;
|
|
222
|
+
note = 'started';
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
note = terminalNote(intent);
|
|
226
|
+
if (roundState.terminalNote !== null) {
|
|
227
|
+
if (roundState.terminalNote === note) {
|
|
228
|
+
return { status: 'no-op', changed: false, intent: intent.kind, taskId: state.taskId, artifact: intent.artifact, operations: [], error: null };
|
|
229
|
+
}
|
|
230
|
+
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${base} has a different terminal payload`, state.taskId);
|
|
231
|
+
}
|
|
232
|
+
if (roundState.startedAgent === null) {
|
|
233
|
+
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${base} has no matching started row`, state.taskId);
|
|
234
|
+
}
|
|
235
|
+
if (intent.kind === 'pr-review-complete' && !SUCCESS_STATUSES.has(roundState.status)) {
|
|
236
|
+
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} must be applied or no-op before completion`, state.taskId);
|
|
237
|
+
}
|
|
238
|
+
if (intent.kind === 'pr-review-terminate' && roundState.status !== intent.outcome) {
|
|
239
|
+
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} status must match outcome ${intent.outcome}`, state.taskId);
|
|
240
|
+
}
|
|
241
|
+
step = base;
|
|
242
|
+
linkArtifact = true;
|
|
243
|
+
}
|
|
244
|
+
let metadata;
|
|
245
|
+
try {
|
|
246
|
+
metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
return failed(intent, 'METADATA_CAPTURE_FAILED', error instanceof Error ? error.message : String(error), state.taskId);
|
|
250
|
+
}
|
|
251
|
+
const section = locateActivityLog(state.content);
|
|
252
|
+
const mutations = [{
|
|
253
|
+
kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading,
|
|
254
|
+
body: appendActivityEntry(section, { time: metadata.timestamp, step, agent: normalizedAgent, note })
|
|
255
|
+
}];
|
|
256
|
+
if (linkArtifact) {
|
|
257
|
+
const link = buildArtifactLinkSection(state.content, roundState.identity);
|
|
258
|
+
mutations.push({ kind: 'section', aliases: link.aliases, heading: link.heading, body: link.body });
|
|
259
|
+
}
|
|
260
|
+
const writeResult = writeTask({
|
|
261
|
+
taskRef: intent.taskRef,
|
|
262
|
+
expectedState: 'active',
|
|
263
|
+
mutations,
|
|
264
|
+
dryRun: intent.dryRun
|
|
265
|
+
}, {
|
|
266
|
+
...options,
|
|
267
|
+
taskLocation: {
|
|
268
|
+
repoRoot: state.repoRoot,
|
|
269
|
+
taskId: state.taskId,
|
|
270
|
+
taskMdPath: state.taskMdPath,
|
|
271
|
+
state: 'active'
|
|
272
|
+
},
|
|
273
|
+
metadataProvider: () => metadata
|
|
274
|
+
});
|
|
275
|
+
if (writeResult.status === 'failed') {
|
|
276
|
+
return failed(intent, writeResult.error.code, writeResult.error.message, writeResult.taskId);
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
status: writeResult.status,
|
|
280
|
+
changed: writeResult.changed,
|
|
281
|
+
intent: intent.kind,
|
|
282
|
+
taskId: writeResult.taskId,
|
|
283
|
+
artifact: intent.artifact,
|
|
284
|
+
operations: writeResult.operations,
|
|
285
|
+
error: null
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
export function applyPrReviewActivityIntent(intent, options = {}) {
|
|
289
|
+
const validated = validateIntent(intent);
|
|
290
|
+
if ('code' in validated)
|
|
291
|
+
return failed(intent, validated.code, validated.message);
|
|
292
|
+
const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
|
|
293
|
+
if (!resolved.ok)
|
|
294
|
+
return failed(intent, resolved.code, resolved.message, resolved.taskId);
|
|
295
|
+
if (resolved.state !== 'active') {
|
|
296
|
+
return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-activity:${intent.kind}`, () => applyLocked(intent, validated.agent, { ...options, repoRoot: resolved.repoRoot }), options.lockOptions);
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (error instanceof TaskExecutionLockError)
|
|
303
|
+
return failed(intent, error.code, error.message, resolved.taskId);
|
|
304
|
+
return failed(intent, 'ACTIVITY_LOCK_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=activity-intent.js.map
|
|
@@ -12,7 +12,8 @@ const artifactFamilyCatalog = [
|
|
|
12
12
|
{ family: 'review-plan', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['技术方案审查', 'Plan Review'] },
|
|
13
13
|
{ family: 'code', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['实现报告', 'Implementation Report'] },
|
|
14
14
|
{ family: 'review-code', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['代码审查', 'Code Review'] },
|
|
15
|
-
{ family: 'manual-validation', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['人工验证', 'Manual Validation'] }
|
|
15
|
+
{ family: 'manual-validation', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['人工验证', 'Manual Validation'] },
|
|
16
|
+
{ family: 'pr-review', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['PR 审查报告', 'PR Review Report'] }
|
|
16
17
|
];
|
|
17
18
|
const FAMILY_SET = new Set(artifactFamilyCatalog.map((item) => item.family));
|
|
18
19
|
const BLOCKING_DIAGNOSTICS = new Set([
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import { classifyAgent } from '../../agent-clients/tokens.js';
|
|
2
3
|
import { formatTable } from '../../table.js';
|
|
3
4
|
import { parseTaskScope } from '../command-options.js';
|
|
4
5
|
import { resolveTaskContext } from '../resolve-ref.js';
|
|
@@ -30,11 +31,11 @@ const TABLE_HEADERS = ['#', 'STEP', 'AGENT', 'STARTED', 'DONE', 'NOTE'];
|
|
|
30
31
|
// with ` [started]`; the matching done entry carries the identical base action
|
|
31
32
|
// without the suffix. Pairing therefore keys on the base action (including any
|
|
32
33
|
// `(Round N)`), so every round and every repeated execution pairs on its own.
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
34
|
+
// Known AI tokens, long-name mapping, and loose rendering classification live
|
|
35
|
+
// in lib/agent-clients/tokens.ts (single source of truth shared with the
|
|
36
|
+
// write-side internal commands). Any other executor token (a human name,
|
|
37
|
+
// possibly CJK, or an unknown value) is rendered as human with a visible
|
|
38
|
+
// `(unknown)` marker.
|
|
38
39
|
const REVIEW_STAGE_PREFIXES = [
|
|
39
40
|
{ prefix: 'Review Analysis', stage: 'analysis' },
|
|
40
41
|
{ prefix: 'Review Plan', stage: 'plan' },
|
|
@@ -83,12 +84,11 @@ function humanValidationCount(note) {
|
|
|
83
84
|
function isHumanCountField(field) {
|
|
84
85
|
return fieldNumber(field, 'manual-validation') !== undefined || fieldNumber(field, 'human-decision') !== undefined;
|
|
85
86
|
}
|
|
86
|
-
// A step is human-executed when its agent token is not a known AI token.
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
function isHumanAgent(agent) {
|
|
90
|
-
|
|
91
|
-
return token !== '' && !KNOWN_AI_AGENTS.has(token);
|
|
87
|
+
// A step is human-executed when its agent token is not a known AI token.
|
|
88
|
+
// Known long names (claude-code -> claude, gemini-cli -> gemini) classify as
|
|
89
|
+
// AI; empty or unknown tokens classify as non-AI (human).
|
|
90
|
+
export function isHumanAgent(agent) {
|
|
91
|
+
return classifyAgent(agent).status !== 'ai';
|
|
92
92
|
}
|
|
93
93
|
// Fold the two human counts into a review row's verdict NOTE: comma-joined, right
|
|
94
94
|
// after the blockers/major/minor list and before the ` → artifact` link, mirroring
|
|
@@ -148,8 +148,9 @@ function log(args = []) {
|
|
|
148
148
|
const note = stage
|
|
149
149
|
? foldHumanCounts(s.note, humanDecisionCounts.get(stage) ?? 0, humanValidationCount(s.note))
|
|
150
150
|
: s.note;
|
|
151
|
-
const
|
|
152
|
-
const
|
|
151
|
+
const { status, display } = classifyAgent(s.agent);
|
|
152
|
+
const human = status !== 'ai';
|
|
153
|
+
const agent = status === 'unknown' ? `${display} (unknown)` : display;
|
|
153
154
|
const started = s.started || (human ? '-' : '');
|
|
154
155
|
return [String(idx + 1), s.step, agent, started, s.done || (s.started ? '(in progress)' : ''), note];
|
|
155
156
|
});
|
|
@@ -158,5 +159,5 @@ function log(args = []) {
|
|
|
158
159
|
}
|
|
159
160
|
process.stdout.write(`Total: ${steps.length} steps\n`);
|
|
160
161
|
}
|
|
161
|
-
export { log
|
|
162
|
+
export { log };
|
|
162
163
|
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { locateActivityLog, appendActivityEntry, pairEntries } from './activity-log.js';
|
|
5
|
+
import { commitIntentPath, digest, readCommitIntent } from './commit-intent.js';
|
|
6
|
+
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
7
|
+
import { parseReviewSummary } from './review-artifacts.js';
|
|
8
|
+
import { extractReviewBaseline, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact } from './review-fingerprint.js';
|
|
9
|
+
const SHA_RE = /^[a-f0-9]{40,64}$/;
|
|
10
|
+
function git(repoRoot, args) {
|
|
11
|
+
return execFileSync('git', args, {
|
|
12
|
+
cwd: repoRoot,
|
|
13
|
+
encoding: 'utf8',
|
|
14
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
15
|
+
}).trim();
|
|
16
|
+
}
|
|
17
|
+
function commitExists(repoRoot, value) {
|
|
18
|
+
return SHA_RE.test(value) && spawnSync('git', ['cat-file', '-e', `${value}^{commit}`], {
|
|
19
|
+
cwd: repoRoot,
|
|
20
|
+
stdio: 'ignore'
|
|
21
|
+
}).status === 0;
|
|
22
|
+
}
|
|
23
|
+
function isAncestor(repoRoot, ancestor, descendant) {
|
|
24
|
+
return spawnSync('git', ['merge-base', '--is-ancestor', ancestor, descendant], {
|
|
25
|
+
cwd: repoRoot,
|
|
26
|
+
stdio: 'ignore'
|
|
27
|
+
}).status === 0;
|
|
28
|
+
}
|
|
29
|
+
function outcome(disposition, overrides = {}) {
|
|
30
|
+
return {
|
|
31
|
+
disposition,
|
|
32
|
+
code: null,
|
|
33
|
+
message: disposition,
|
|
34
|
+
currentHead: '',
|
|
35
|
+
committedHead: null,
|
|
36
|
+
commitNote: null,
|
|
37
|
+
needsAnchor: false,
|
|
38
|
+
needsLog: false,
|
|
39
|
+
intent: null,
|
|
40
|
+
intentDigest: null,
|
|
41
|
+
...overrides
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function invalid(message, currentHead = '', code = 'COMMIT_FINALIZATION_EVIDENCE_INVALID', overrides = {}) {
|
|
45
|
+
return outcome('invalid', { code, message, currentHead, ...overrides });
|
|
46
|
+
}
|
|
47
|
+
function inspectCommitFinalization(taskDir, repoRoot, taskId) {
|
|
48
|
+
let currentHead;
|
|
49
|
+
let taskContent;
|
|
50
|
+
let metadata;
|
|
51
|
+
try {
|
|
52
|
+
currentHead = git(repoRoot, ['rev-parse', 'HEAD']);
|
|
53
|
+
taskContent = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
|
|
54
|
+
metadata = parseTypedTaskFrontmatter(taskContent);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return invalid(error instanceof Error ? error.message : String(error));
|
|
58
|
+
}
|
|
59
|
+
const activity = locateActivityLog(taskContent);
|
|
60
|
+
if (!activity)
|
|
61
|
+
return invalid('task activity log is missing or ambiguous', currentHead);
|
|
62
|
+
const commitRows = pairEntries(activity.entries).filter((row) => row.step === 'Commit');
|
|
63
|
+
const openRows = commitRows.filter((row) => row.started !== '' && row.done === '');
|
|
64
|
+
const file = commitIntentPath(taskDir);
|
|
65
|
+
if (!fs.existsSync(file)) {
|
|
66
|
+
if (openRows.length > 0) {
|
|
67
|
+
return outcome('orphaned-start', {
|
|
68
|
+
code: 'COMMIT_FINALIZATION_EVIDENCE_MISSING',
|
|
69
|
+
message: 'an open Commit activity entry exists without a commit intent',
|
|
70
|
+
currentHead
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return outcome('idle', { message: 'no active commit finalization exists', currentHead });
|
|
74
|
+
}
|
|
75
|
+
let raw;
|
|
76
|
+
let intent;
|
|
77
|
+
try {
|
|
78
|
+
raw = fs.readFileSync(file, 'utf8');
|
|
79
|
+
intent = readCommitIntent(taskDir, taskId);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return invalid(error instanceof Error ? error.message : String(error), currentHead);
|
|
83
|
+
}
|
|
84
|
+
const intentDigest = digest(raw);
|
|
85
|
+
const shared = { currentHead, intent, intentDigest, committedHead: intent.committedHead };
|
|
86
|
+
if (!commitExists(repoRoot, intent.baselineHead)) {
|
|
87
|
+
return invalid('commit intent baseline is not a valid Git commit', currentHead, 'COMMIT_FINALIZATION_EVIDENCE_INVALID', shared);
|
|
88
|
+
}
|
|
89
|
+
if (intent.phase === 'prepared') {
|
|
90
|
+
if (intent.committedHead !== null || intent.pushEvidence !== null) {
|
|
91
|
+
return invalid('prepared commit intent contains commit or push evidence', currentHead, 'COMMIT_FINALIZATION_EVIDENCE_INVALID', shared);
|
|
92
|
+
}
|
|
93
|
+
if (currentHead !== intent.baselineHead || openRows.length !== 1) {
|
|
94
|
+
return outcome('conflict', {
|
|
95
|
+
...shared,
|
|
96
|
+
code: 'COMMIT_FINALIZATION_CONFLICT',
|
|
97
|
+
message: 'prepared commit intent does not match HEAD and exactly one open Commit entry'
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return outcome('prepared', { ...shared, message: 'prepared commit intent can be safely retried' });
|
|
101
|
+
}
|
|
102
|
+
if (!intent.committedHead || !commitExists(repoRoot, intent.committedHead)) {
|
|
103
|
+
return invalid('committed intent head is missing or invalid', currentHead, 'COMMIT_FINALIZATION_EVIDENCE_INVALID', shared);
|
|
104
|
+
}
|
|
105
|
+
if (intent.phase === 'committed' && intent.pushEvidence !== null) {
|
|
106
|
+
return invalid('committed intent contains premature push evidence', currentHead, 'COMMIT_FINALIZATION_EVIDENCE_INVALID', shared);
|
|
107
|
+
}
|
|
108
|
+
if (intent.phase === 'pushed'
|
|
109
|
+
&& (intent.pushEvidence === null
|
|
110
|
+
|| intent.pushEvidence.head !== intent.committedHead
|
|
111
|
+
|| !intent.pushEvidence.remote
|
|
112
|
+
|| !intent.pushEvidence.ref)) {
|
|
113
|
+
return invalid('pushed intent evidence is missing or inconsistent', currentHead, 'COMMIT_FINALIZATION_EVIDENCE_INVALID', shared);
|
|
114
|
+
}
|
|
115
|
+
if (currentHead !== intent.committedHead || !isAncestor(repoRoot, intent.baselineHead, intent.committedHead)) {
|
|
116
|
+
return outcome('conflict', {
|
|
117
|
+
...shared,
|
|
118
|
+
code: 'COMMIT_FINALIZATION_CONFLICT',
|
|
119
|
+
message: 'repository HEAD or ancestry conflicts with the commit intent'
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const review = findAuthoritativeReviewCodeArtifact(taskDir);
|
|
123
|
+
let needsAnchor = false;
|
|
124
|
+
if (review.ok && review.path) {
|
|
125
|
+
let reviewContent;
|
|
126
|
+
try {
|
|
127
|
+
reviewContent = fs.readFileSync(review.path, 'utf8');
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return invalid(error instanceof Error ? error.message : String(error), currentHead, 'COMMIT_FINALIZATION_EVIDENCE_MISSING', shared);
|
|
131
|
+
}
|
|
132
|
+
const summary = parseReviewSummary(reviewContent);
|
|
133
|
+
const reviewBaseline = extractReviewBaseline(reviewContent);
|
|
134
|
+
const reviewTree = extractReviewedSnapshotTree(reviewContent);
|
|
135
|
+
if (!summary.ok || summary.summary.verdict !== 'Approved' || !commitExists(repoRoot, reviewBaseline) || !SHA_RE.test(reviewTree)) {
|
|
136
|
+
return invalid('authoritative review-code evidence is not a valid approved snapshot', currentHead, 'COMMIT_FINALIZATION_EVIDENCE_INVALID', shared);
|
|
137
|
+
}
|
|
138
|
+
const committedTree = git(repoRoot, ['rev-parse', `${intent.committedHead}^{tree}`]);
|
|
139
|
+
if (reviewBaseline !== intent.baselineHead || reviewTree !== committedTree) {
|
|
140
|
+
return outcome('conflict', {
|
|
141
|
+
...shared,
|
|
142
|
+
code: 'COMMIT_FINALIZATION_CONFLICT',
|
|
143
|
+
message: 'review baseline or snapshot tree conflicts with the committed intent'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
const anchor = String(metadata.last_reviewed_commit ?? '').trim();
|
|
147
|
+
if (anchor && anchor !== intent.committedHead) {
|
|
148
|
+
return outcome('conflict', {
|
|
149
|
+
...shared,
|
|
150
|
+
code: 'COMMIT_FINALIZATION_CONFLICT',
|
|
151
|
+
message: 'task review anchor conflicts with the committed intent'
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
needsAnchor = anchor === '';
|
|
155
|
+
}
|
|
156
|
+
const commitNote = git(repoRoot, ['show', '-s', '--format=%h%x20%s', intent.committedHead]);
|
|
157
|
+
const matchingDone = commitRows.filter((row) => row.done !== '' && row.note === commitNote);
|
|
158
|
+
const needsLog = matchingDone.length === 0 && openRows.length === 1;
|
|
159
|
+
if (!(needsLog || (matchingDone.length === 1 && openRows.length === 0))) {
|
|
160
|
+
return outcome('conflict', {
|
|
161
|
+
...shared,
|
|
162
|
+
code: 'COMMIT_FINALIZATION_CONFLICT',
|
|
163
|
+
message: 'Commit activity identity is missing, duplicated, or ambiguous',
|
|
164
|
+
commitNote
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return outcome('recoverable', {
|
|
168
|
+
...shared,
|
|
169
|
+
message: 'commit finalization can be recovered',
|
|
170
|
+
commitNote,
|
|
171
|
+
needsAnchor,
|
|
172
|
+
needsLog
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function planCommitTaskFinalization(taskDir, inspection, agent, timestamp) {
|
|
176
|
+
if (inspection.disposition !== 'recoverable' || !inspection.committedHead || !inspection.commitNote) {
|
|
177
|
+
throw new Error('commit finalization task mutations require a recoverable inspection');
|
|
178
|
+
}
|
|
179
|
+
const taskContent = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
|
|
180
|
+
const frontmatter = { assigned_to: agent };
|
|
181
|
+
if (inspection.needsAnchor)
|
|
182
|
+
frontmatter.last_reviewed_commit = inspection.committedHead;
|
|
183
|
+
const mutations = [{
|
|
184
|
+
kind: 'frontmatter',
|
|
185
|
+
set: frontmatter
|
|
186
|
+
}];
|
|
187
|
+
if (inspection.needsLog) {
|
|
188
|
+
const activity = locateActivityLog(taskContent);
|
|
189
|
+
if (!activity)
|
|
190
|
+
throw new Error('task activity log is missing or ambiguous');
|
|
191
|
+
mutations.push({
|
|
192
|
+
kind: 'section',
|
|
193
|
+
aliases: ['活动日志', 'Activity Log'],
|
|
194
|
+
heading: activity.heading,
|
|
195
|
+
body: appendActivityEntry(activity, {
|
|
196
|
+
time: timestamp,
|
|
197
|
+
step: 'Commit',
|
|
198
|
+
agent,
|
|
199
|
+
note: inspection.commitNote
|
|
200
|
+
})
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return { mutations, commitNote: inspection.commitNote };
|
|
204
|
+
}
|
|
205
|
+
function inspectCreatePrCommitGate(taskDir, repoRoot, taskId) {
|
|
206
|
+
const lifecycle = inspectCommitFinalization(taskDir, repoRoot, taskId);
|
|
207
|
+
if (lifecycle.disposition !== 'idle') {
|
|
208
|
+
const pending = lifecycle.disposition === 'prepared' || lifecycle.disposition === 'recoverable';
|
|
209
|
+
return {
|
|
210
|
+
allowed: false,
|
|
211
|
+
code: pending ? 'COMMIT_FINALIZATION_PENDING' : lifecycle.code ?? 'COMMIT_FINALIZATION_CONFLICT',
|
|
212
|
+
message: pending ? 'commit finalization must complete before creating a pull request' : lifecycle.message,
|
|
213
|
+
disposition: lifecycle.disposition,
|
|
214
|
+
action: 'rerun-commit'
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
const review = findAuthoritativeReviewCodeArtifact(taskDir);
|
|
218
|
+
if (!review.ok || !review.path) {
|
|
219
|
+
return { allowed: true, code: null, message: 'no review-code history requires an anchor', disposition: 'idle', action: 'continue' };
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const content = fs.readFileSync(review.path, 'utf8');
|
|
223
|
+
const summary = parseReviewSummary(content);
|
|
224
|
+
if (!summary.ok || summary.summary.verdict !== 'Approved') {
|
|
225
|
+
return {
|
|
226
|
+
allowed: false,
|
|
227
|
+
code: 'COMMIT_FINALIZATION_EVIDENCE_INVALID',
|
|
228
|
+
message: 'latest authoritative review-code is not approved',
|
|
229
|
+
disposition: 'idle',
|
|
230
|
+
action: 'rerun-review-code'
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const task = parseTypedTaskFrontmatter(fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8'));
|
|
234
|
+
const currentHead = git(repoRoot, ['rev-parse', 'HEAD']);
|
|
235
|
+
const anchor = String(task.last_reviewed_commit ?? '').trim();
|
|
236
|
+
if (!anchor || !commitExists(repoRoot, anchor)) {
|
|
237
|
+
return {
|
|
238
|
+
allowed: false,
|
|
239
|
+
code: 'COMMIT_FINALIZATION_EVIDENCE_MISSING',
|
|
240
|
+
message: 'approved review snapshot is not anchored to a Git commit',
|
|
241
|
+
disposition: 'idle',
|
|
242
|
+
action: 'rerun-commit'
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (anchor !== currentHead || extractReviewedSnapshotTree(content) !== git(repoRoot, ['rev-parse', 'HEAD^{tree}'])) {
|
|
246
|
+
return {
|
|
247
|
+
allowed: false,
|
|
248
|
+
code: 'COMMIT_FINALIZATION_CONFLICT',
|
|
249
|
+
message: 'approved review anchor or tree does not match repository HEAD',
|
|
250
|
+
disposition: 'idle',
|
|
251
|
+
action: 'rerun-review-code'
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return { allowed: true, code: null, message: 'commit finalization evidence is complete', disposition: 'idle', action: 'continue' };
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
return {
|
|
258
|
+
allowed: false,
|
|
259
|
+
code: 'COMMIT_FINALIZATION_EVIDENCE_INVALID',
|
|
260
|
+
message: error instanceof Error ? error.message : String(error),
|
|
261
|
+
disposition: 'idle',
|
|
262
|
+
action: 'rerun-review-code'
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export { inspectCommitFinalization, inspectCreatePrCommitGate, planCommitTaskFinalization };
|
|
267
|
+
//# sourceMappingURL=commit-finalization.js.map
|