@fitlab-ai/agent-infra 0.9.10 → 0.9.12
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 +19 -9
- package/README.zh-CN.md +19 -9
- package/bin/cli.ts +17 -4
- package/dist/bin/cli.js +19 -4
- package/dist/bin/internal-cli.js +55 -4
- package/dist/lib/agent-client.js +0 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
- package/dist/lib/agent-clients/adapters/traecli.js +7 -8
- package/dist/lib/agent-clients/config.js +14 -99
- package/dist/lib/agent-clients/reconcile.js +1 -11
- package/dist/lib/agent-clients/schema.js +5 -4
- package/dist/lib/agent-clients/tokens.js +5 -15
- package/dist/lib/decide.js +14 -30
- package/dist/lib/defaults.json +8 -0
- package/dist/lib/git/workflow.js +43 -1
- package/dist/lib/init.js +3 -2
- package/dist/lib/internal/git-workflow.js +8 -1
- package/dist/lib/internal/platform-pr.js +8 -2
- package/dist/lib/internal/release-workflow.js +0 -1
- package/dist/lib/internal/task-delivery.js +59 -0
- package/dist/lib/internal/task-finalization.js +19 -9
- package/dist/lib/internal/task-ledger.js +8 -2
- package/dist/lib/internal/task-lifecycle.js +17 -93
- package/dist/lib/internal/task-orchestration.js +30 -216
- package/dist/lib/internal/task-validate.js +2 -1
- package/dist/lib/merge.js +205 -103
- package/dist/lib/platform/github-client.js +7 -1
- package/dist/lib/platform/pr-checks.js +8 -5
- package/dist/lib/platform/pr-summary.js +12 -8
- package/dist/lib/platform/pull-requests.js +393 -91
- package/dist/lib/platform/verification-required.js +13 -2
- package/dist/lib/platform/verification-sync.js +9 -4
- package/dist/lib/pr-review/evidence-grading.js +12 -4
- package/dist/lib/process-data/normalize.js +2 -9
- package/dist/lib/run/host.js +46 -16
- package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
- package/dist/lib/sandbox/commands/create.js +3 -3
- package/dist/lib/sandbox/commands/enter.js +18 -4
- package/dist/lib/sandbox/config.js +8 -12
- package/dist/lib/sandbox/control/executor.js +131 -39
- package/dist/lib/sandbox/control/lifecycle.js +55 -24
- package/dist/lib/sandbox/control/server.js +18 -16
- package/dist/lib/sandbox/mounts.js +3 -2
- package/dist/lib/sandbox/recovery.js +123 -22
- package/dist/lib/sandbox/workspace-identity.js +14 -0
- package/dist/lib/sandbox/workspace-view.js +8 -9
- package/dist/lib/self-update.js +278 -0
- package/dist/lib/server/process-state.js +2 -2
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +6 -4
- package/dist/lib/task/codex-orchestration.js +1 -1
- package/dist/lib/task/command-options.js +1 -3
- package/dist/lib/task/commands/cat.js +0 -5
- package/dist/lib/task/commands/decisions.js +76 -92
- package/dist/lib/task/commands/files.js +6 -6
- package/dist/lib/task/commands/grep.js +25 -61
- package/dist/lib/task/commands/issue-body.js +6 -6
- package/dist/lib/task/commands/log.js +25 -10
- package/dist/lib/task/commands/show.js +5 -6
- package/dist/lib/task/commands/status.js +8 -8
- package/dist/lib/task/commit-intent.js +100 -0
- package/dist/lib/task/commit-operation.js +181 -91
- package/dist/lib/task/commit-policy.js +1 -1
- package/dist/lib/task/control-authority.js +431 -0
- package/dist/lib/task/create.js +39 -8
- package/dist/lib/task/current-contract.js +35 -0
- package/dist/lib/task/decision-details.js +218 -0
- package/dist/lib/task/decision-intents.js +5 -2
- package/dist/lib/task/delivery-target.js +97 -0
- package/dist/lib/task/delivery.js +153 -0
- package/dist/lib/task/events.js +11 -5
- package/dist/lib/task/human-override.js +5 -2
- package/dist/lib/task/index.js +19 -20
- package/dist/lib/task/ledger-intents.js +8 -9
- package/dist/lib/task/ledger.js +13 -6
- package/dist/lib/task/lifecycle.js +122 -9
- package/dist/lib/task/orchestration.js +105 -75
- package/dist/lib/task/pr-delivery-fact.js +182 -0
- package/dist/lib/task/review-artifacts.js +31 -12
- package/dist/lib/task/review-finalization.js +13 -4
- package/dist/lib/task/review-fingerprint.js +8 -0
- package/dist/lib/task/verification-engine.js +86 -39
- package/dist/lib/task/verification-review.js +2 -2
- package/dist/lib/task/workspace-snapshot.js +5 -11
- package/dist/lib/task/write.js +11 -1
- package/dist/lib/update.js +13 -35
- package/dist/lib/version.js +5 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +0 -1
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
- package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +7 -8
- package/lib/agent-clients/config.ts +14 -144
- package/lib/agent-clients/reconcile.ts +1 -12
- package/lib/agent-clients/schema.ts +5 -4
- package/lib/agent-clients/tokens.ts +5 -18
- package/lib/decide.ts +12 -22
- package/lib/defaults.json +8 -0
- package/lib/git/workflow.ts +38 -1
- package/lib/init.ts +5 -2
- package/lib/internal/git-workflow.ts +7 -1
- package/lib/internal/platform-pr.ts +8 -1
- package/lib/internal/release-workflow.ts +0 -1
- package/lib/internal/task-delivery.ts +53 -0
- package/lib/internal/task-finalization.ts +27 -13
- package/lib/internal/task-ledger.ts +8 -2
- package/lib/internal/task-lifecycle.ts +33 -76
- package/lib/internal/task-orchestration.ts +33 -190
- package/lib/internal/task-validate.ts +3 -2
- package/lib/merge.ts +236 -120
- package/lib/platform/github-client.ts +8 -1
- package/lib/platform/pr-checks.ts +7 -5
- package/lib/platform/pr-summary.ts +10 -7
- package/lib/platform/pull-requests.ts +425 -87
- package/lib/platform/verification-required.ts +12 -2
- package/lib/platform/verification-sync.ts +9 -4
- package/lib/pr-review/evidence-grading.ts +7 -4
- package/lib/process-data/normalize.ts +2 -8
- package/lib/run/host.ts +63 -14
- package/lib/sandbox/agent-client-reconciler.ts +5 -15
- package/lib/sandbox/commands/create.ts +4 -4
- package/lib/sandbox/commands/enter.ts +24 -4
- package/lib/sandbox/config.ts +9 -14
- package/lib/sandbox/control/container-identity.ts +2 -2
- package/lib/sandbox/control/executor.ts +148 -51
- package/lib/sandbox/control/lifecycle.ts +59 -31
- package/lib/sandbox/control/protocol.ts +1 -3
- package/lib/sandbox/control/server.ts +15 -12
- package/lib/sandbox/control/state.ts +7 -7
- package/lib/sandbox/mounts.ts +4 -3
- package/lib/sandbox/recovery.ts +162 -34
- package/lib/sandbox/tools.ts +1 -1
- package/lib/sandbox/workspace-identity.ts +28 -0
- package/lib/sandbox/workspace-view.ts +14 -10
- package/lib/self-update.ts +341 -0
- package/lib/server/process-state.ts +2 -2
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +6 -1
- package/lib/task/artifact-lifecycle.ts +7 -5
- package/lib/task/codex-orchestration.ts +2 -1
- package/lib/task/command-options.ts +1 -2
- package/lib/task/commands/cat.ts +0 -4
- package/lib/task/commands/decisions.ts +80 -75
- package/lib/task/commands/files.ts +6 -6
- package/lib/task/commands/grep.ts +23 -51
- package/lib/task/commands/issue-body.ts +6 -6
- package/lib/task/commands/log.ts +22 -10
- package/lib/task/commands/show.ts +5 -6
- package/lib/task/commands/status.ts +8 -8
- package/lib/task/commit-intent.ts +137 -0
- package/lib/task/commit-operation.ts +161 -79
- package/lib/task/commit-policy.ts +1 -2
- package/lib/task/control-authority.ts +563 -0
- package/lib/task/create.ts +40 -10
- package/lib/task/current-contract.ts +49 -0
- package/lib/task/decision-details.ts +280 -0
- package/lib/task/decision-intents.ts +7 -2
- package/lib/task/delivery-target.ts +130 -0
- package/lib/task/delivery.ts +160 -0
- package/lib/task/events.ts +10 -5
- package/lib/task/human-override.ts +5 -2
- package/lib/task/index.ts +19 -20
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/ledger.ts +17 -7
- package/lib/task/lifecycle.ts +113 -4
- package/lib/task/orchestration.ts +102 -80
- package/lib/task/pr-delivery-fact.ts +254 -0
- package/lib/task/review-artifacts.ts +53 -15
- package/lib/task/review-finalization.ts +21 -5
- package/lib/task/review-fingerprint.ts +10 -0
- package/lib/task/verification-engine.ts +78 -46
- package/lib/task/verification-review.ts +3 -1
- package/lib/task/workspace-snapshot.ts +4 -13
- package/lib/task/write.ts +20 -1
- package/lib/update.ts +14 -38
- package/lib/version.ts +6 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +5 -3
- package/templates/.agents/README.zh-CN.md +5 -3
- package/templates/.agents/rules/README.en.md +4 -2
- package/templates/.agents/rules/README.zh-CN.md +4 -2
- package/templates/.agents/rules/compatibility-policy.en.md +5 -4
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
- package/templates/.agents/rules/create-issue.en.md +11 -2
- package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
- package/templates/.agents/rules/human-decision-context.en.md +9 -7
- package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
- package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +1 -1
- package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +26 -5
- package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
- package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
- package/templates/.agents/rules/milestone-inference.en.md +12 -2
- package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
- package/templates/.agents/rules/next-step-output.en.md +3 -3
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
- package/templates/.agents/rules/pr-sync.en.md +23 -14
- package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
- package/templates/.agents/rules/release-commands.en.md +42 -3
- package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
- package/templates/.agents/rules/review-handshake.en.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/security-alerts.en.md +17 -2
- package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
- package/templates/.agents/rules/sync-content-generation.en.md +8 -0
- package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
- package/templates/.agents/rules/task-management.en.md +2 -3
- package/templates/.agents/rules/task-management.zh-CN.md +2 -3
- package/templates/.agents/scripts/security-alerts.github.sh +151 -0
- package/templates/.agents/scripts/security-alerts.sh +31 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/commit/SKILL.en.md +30 -10
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
- package/templates/.agents/skills/commit/config/verify.json +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
- 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 +2 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- 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 +13 -13
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
- package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
- package/templates/.agents/skills/create-task/SKILL.en.md +2 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +8 -5
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
- 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/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +144 -109
- package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
- package/templates/.agents/templates/task.en.md +6 -2
- package/templates/.agents/templates/task.zh-CN.md +6 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/feature-development.en.yaml +9 -8
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/refactoring.en.yaml +9 -8
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
- package/templates/.claude/commands/analyze-task.en.md +1 -1
- package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.claude/commands/block-task.en.md +1 -1
- package/templates/.claude/commands/block-task.zh-CN.md +1 -1
- package/templates/.claude/commands/cancel-task.en.md +1 -1
- package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
- package/templates/.claude/commands/check-task.en.md +1 -1
- package/templates/.claude/commands/check-task.zh-CN.md +1 -1
- package/templates/.claude/commands/code-task.en.md +1 -1
- package/templates/.claude/commands/code-task.zh-CN.md +1 -1
- package/templates/.claude/commands/commit.en.md +1 -1
- package/templates/.claude/commands/commit.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-task.en.md +1 -1
- package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
- package/templates/.claude/commands/create-pr.en.md +1 -1
- package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
- package/templates/.claude/commands/plan-task.en.md +1 -1
- package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
- package/templates/.claude/commands/review-analysis.en.md +1 -1
- package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.claude/commands/review-code.en.md +1 -1
- package/templates/.claude/commands/review-code.zh-CN.md +1 -1
- package/templates/.claude/commands/review-plan.en.md +1 -1
- package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/run-task.en.md +1 -1
- package/templates/.claude/commands/run-task.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
- package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
- package/dist/lib/builtin-tuis.js +0 -40
- package/lib/builtin-tuis.ts +0 -56
- package/templates/.agents/rules/create-issue.github.en.md +0 -14
- package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
- package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
- package/templates/.agents/rules/issue-sync.github.en.md +0 -40
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
- package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
- package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
- package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
- package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
- package/templates/.agents/rules/pr-sync.github.en.md +0 -43
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
- package/templates/.agents/rules/release-commands.github.en.md +0 -38
- package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
- package/templates/.agents/rules/security-alerts.github.en.md +0 -43
- package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
const DECISION_ID = /^(AN|PL|CD|HD)-[1-9]\d*(?=$|[\s::])/i;
|
|
2
|
+
const HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)\s*$/;
|
|
3
|
+
const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
4
|
+
const FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
|
|
5
|
+
const ANCHOR = /^\s*<a\s+id=(['"])([^'"]+)\1\s*><\/a>\s*$/;
|
|
6
|
+
const FIELD = /^\s*[-*]\s+\*\*([^*]+)\*\*\s*[::]\s*/;
|
|
7
|
+
const OPTION_HEADING = /^(?:方案|Option)\s+[A-Z](?:\b|\s*[::])/i;
|
|
8
|
+
function sourceLines(content) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
let start = 0;
|
|
11
|
+
while (start < content.length) {
|
|
12
|
+
const newline = content.indexOf('\n', start);
|
|
13
|
+
const end = newline === -1 ? content.length : newline;
|
|
14
|
+
lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
|
|
15
|
+
start = newline === -1 ? content.length : newline + 1;
|
|
16
|
+
}
|
|
17
|
+
return lines;
|
|
18
|
+
}
|
|
19
|
+
function scanVisibleMarkdown(content) {
|
|
20
|
+
const lines = sourceLines(content);
|
|
21
|
+
const visible = [];
|
|
22
|
+
const headings = [];
|
|
23
|
+
const anchors = [];
|
|
24
|
+
let fence = null;
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
if (fence) {
|
|
27
|
+
const close = line.text.match(FENCE_CLOSE);
|
|
28
|
+
if (close && close[1][0] === fence.character && close[1].length >= fence.length)
|
|
29
|
+
fence = null;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const open = line.text.match(FENCE_OPEN);
|
|
33
|
+
const marker = open?.[1];
|
|
34
|
+
const info = open?.[2] ?? '';
|
|
35
|
+
if (marker && !(marker[0] === '`' && info.includes('`'))) {
|
|
36
|
+
fence = { character: marker[0], length: marker.length };
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
visible.push(line);
|
|
40
|
+
const heading = line.text.match(HEADING);
|
|
41
|
+
if (heading)
|
|
42
|
+
headings.push({ ...line, level: heading[1].length, text: heading[2].trim() });
|
|
43
|
+
const anchor = line.text.match(ANCHOR);
|
|
44
|
+
if (anchor)
|
|
45
|
+
anchors.push({ ...line, id: anchor[2] });
|
|
46
|
+
}
|
|
47
|
+
return { lines: visible, headings, anchors };
|
|
48
|
+
}
|
|
49
|
+
function parseDecisionDetailBlocks(content) {
|
|
50
|
+
const scanned = scanVisibleMarkdown(content);
|
|
51
|
+
const details = scanned.headings.filter((heading) => heading.level === 3 && DECISION_ID.test(heading.text));
|
|
52
|
+
return details.map((heading) => {
|
|
53
|
+
const next = scanned.headings.find((candidate) => (candidate.start > heading.start && candidate.level <= 3));
|
|
54
|
+
const end = next?.start ?? content.length;
|
|
55
|
+
return {
|
|
56
|
+
id: heading.text.match(DECISION_ID)[0],
|
|
57
|
+
heading: heading.text,
|
|
58
|
+
canonical: /\[needs-human-decision\]/i.test(heading.text),
|
|
59
|
+
start: heading.start,
|
|
60
|
+
end,
|
|
61
|
+
content: content.slice(heading.start, end).trim()
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function parseDecisionEvidence(evidence) {
|
|
66
|
+
const separator = evidence.indexOf('#');
|
|
67
|
+
if (separator <= 0 || evidence.indexOf('#', separator + 1) !== -1)
|
|
68
|
+
return { ok: false };
|
|
69
|
+
const artifact = evidence.slice(0, separator);
|
|
70
|
+
const anchor = evidence.slice(separator + 1);
|
|
71
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*\.md$/.test(artifact))
|
|
72
|
+
return { ok: false };
|
|
73
|
+
if (!anchor || /[\s\u0000-\u001f#]/.test(anchor))
|
|
74
|
+
return { ok: false };
|
|
75
|
+
return { ok: true, artifact, anchor };
|
|
76
|
+
}
|
|
77
|
+
function sameId(left, right) {
|
|
78
|
+
return left.toUpperCase() === right.toUpperCase();
|
|
79
|
+
}
|
|
80
|
+
function resolveDecisionDetail(content, rowId, anchor) {
|
|
81
|
+
const scanned = scanVisibleMarkdown(content);
|
|
82
|
+
const blocks = parseDecisionDetailBlocks(content);
|
|
83
|
+
const explicit = scanned.anchors.filter((candidate) => candidate.id === anchor);
|
|
84
|
+
if (explicit.length > 1) {
|
|
85
|
+
return { status: 'ambiguous', reason: `evidence anchor '${anchor}' occurs more than once`, candidates: [] };
|
|
86
|
+
}
|
|
87
|
+
if (explicit.length === 1) {
|
|
88
|
+
const marker = explicit[0];
|
|
89
|
+
const followingLine = scanned.lines.find((line) => line.start > marker.end && line.text.trim());
|
|
90
|
+
const followingHeading = followingLine
|
|
91
|
+
? scanned.headings.find((heading) => heading.start === followingLine.start)
|
|
92
|
+
: undefined;
|
|
93
|
+
const candidate = followingHeading?.level === 3
|
|
94
|
+
? blocks.find((block) => block.start === followingHeading.start)
|
|
95
|
+
: undefined;
|
|
96
|
+
if (!candidate || !sameId(candidate.id, rowId) || !candidate.canonical) {
|
|
97
|
+
return { status: 'missing', reason: `evidence anchor '${anchor}' does not identify a canonical ${rowId} detail` };
|
|
98
|
+
}
|
|
99
|
+
return { status: 'found', block: candidate };
|
|
100
|
+
}
|
|
101
|
+
if (!sameId(anchor, rowId)) {
|
|
102
|
+
return { status: 'missing', reason: `evidence anchor '${anchor}' is not the ledger id '${rowId}'` };
|
|
103
|
+
}
|
|
104
|
+
const matches = blocks.filter((block) => sameId(block.id, rowId) && block.canonical);
|
|
105
|
+
if (matches.length === 0)
|
|
106
|
+
return { status: 'missing', reason: `no canonical ${rowId} detail was found` };
|
|
107
|
+
if (matches.length > 1) {
|
|
108
|
+
return { status: 'ambiguous', reason: `more than one canonical ${rowId} detail was found`, candidates: matches };
|
|
109
|
+
}
|
|
110
|
+
return { status: 'found', block: matches[0] };
|
|
111
|
+
}
|
|
112
|
+
function inspectDecisionDetailDuplicates(content) {
|
|
113
|
+
const blocks = parseDecisionDetailBlocks(content);
|
|
114
|
+
const grouped = new Map();
|
|
115
|
+
for (const block of blocks) {
|
|
116
|
+
const key = block.id.toUpperCase();
|
|
117
|
+
const group = grouped.get(key) ?? [];
|
|
118
|
+
group.push(block);
|
|
119
|
+
grouped.set(key, group);
|
|
120
|
+
}
|
|
121
|
+
const duplicates = [];
|
|
122
|
+
for (const group of grouped.values()) {
|
|
123
|
+
if (group.length < 2)
|
|
124
|
+
continue;
|
|
125
|
+
duplicates.push({ id: group[0].id, blocks: group });
|
|
126
|
+
}
|
|
127
|
+
if (duplicates.length === 0)
|
|
128
|
+
return { ok: true, blocks };
|
|
129
|
+
const summary = duplicates.map((duplicate) => {
|
|
130
|
+
const types = duplicate.blocks.map((block) => block.canonical ? 'canonical' : 'informal').join(', ');
|
|
131
|
+
return `${duplicate.id} (${duplicate.blocks.length} blocks: ${types})`;
|
|
132
|
+
}).join(', ');
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
code: 'DECISION_DETAIL_DUPLICATE',
|
|
136
|
+
message: `Duplicate decision detail ids: ${summary}`,
|
|
137
|
+
blocks,
|
|
138
|
+
duplicates
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const FIELD_KEYS = {
|
|
142
|
+
'为什么现在要决定': 'why', 'why decide now': 'why', 'why a decision is needed now': 'why',
|
|
143
|
+
'要决定什么': 'decision', 'what to decide': 'decision', 'what needs a decision': 'decision',
|
|
144
|
+
'会影响什么': 'impact', 'what it affects': 'impact', 'what this affects': 'impact',
|
|
145
|
+
'如果选错或暂不处理': 'risk', 'if wrong or postponed': 'risk',
|
|
146
|
+
'what could go wrong': 'risk',
|
|
147
|
+
'打个比方': 'analogy', '例子': 'analogy', 'example': 'analogy',
|
|
148
|
+
'建议选哪个': 'recommend', 'recommended option': 'recommend', 'recommended choice': 'recommend',
|
|
149
|
+
'为什么这样建议': 'reason', 'why this recommendation': 'reason', 'why this choice': 'reason',
|
|
150
|
+
'是否需要实现': 'implementation', 'implementation required': 'implementation'
|
|
151
|
+
};
|
|
152
|
+
const TERMINAL_FIELD_KEYS = new Set(['recommend', 'reason', 'implementation']);
|
|
153
|
+
function normalizeDecisionDetailForDisplay(block) {
|
|
154
|
+
const body = block.content;
|
|
155
|
+
const scanned = scanVisibleMarkdown(body);
|
|
156
|
+
const heading = scanned.headings.find((candidate) => candidate.level === 3);
|
|
157
|
+
if (!heading)
|
|
158
|
+
return body.trim();
|
|
159
|
+
const options = scanned.headings.filter((candidate) => candidate.level === 4 && OPTION_HEADING.test(candidate.text));
|
|
160
|
+
const optionRanges = options.map((option, index) => ({
|
|
161
|
+
start: option.start,
|
|
162
|
+
end: options[index + 1]?.start
|
|
163
|
+
?? scanned.headings.find((candidate) => candidate.start > option.start && candidate.level <= 3)?.start
|
|
164
|
+
?? scanned.lines.find((line) => (line.start > option.start
|
|
165
|
+
&& FIELD.test(line.text)
|
|
166
|
+
&& TERMINAL_FIELD_KEYS.has(FIELD_KEYS[line.text.match(FIELD)[1].trim().toLowerCase()])))?.start
|
|
167
|
+
?? body.length
|
|
168
|
+
}));
|
|
169
|
+
const topLines = scanned.lines.filter((line) => (line.start > heading.end && !optionRanges.some((range) => line.start >= range.start && line.start < range.end)));
|
|
170
|
+
const fields = [];
|
|
171
|
+
for (const line of topLines) {
|
|
172
|
+
const match = line.text.match(FIELD);
|
|
173
|
+
if (!match)
|
|
174
|
+
continue;
|
|
175
|
+
const key = FIELD_KEYS[match[1].trim().toLowerCase()];
|
|
176
|
+
fields.push({ key: key ?? null, line });
|
|
177
|
+
}
|
|
178
|
+
if (fields.some((field) => field.key === null))
|
|
179
|
+
return body.trim();
|
|
180
|
+
const byKey = new Map();
|
|
181
|
+
const selectedRanges = [];
|
|
182
|
+
for (let index = 0; index < fields.length; index += 1) {
|
|
183
|
+
const field = fields[index];
|
|
184
|
+
if (!field.key)
|
|
185
|
+
continue;
|
|
186
|
+
const nextOption = optionRanges.find((range) => range.start > field.line.start)?.start ?? body.length;
|
|
187
|
+
const end = Math.min(fields[index + 1]?.line.start ?? body.length, nextOption);
|
|
188
|
+
const value = body.slice(field.line.start, end).trim();
|
|
189
|
+
byKey.set(field.key, value);
|
|
190
|
+
selectedRanges.push({ start: field.line.start, end });
|
|
191
|
+
}
|
|
192
|
+
selectedRanges.push(...optionRanges);
|
|
193
|
+
const required = ['decision', 'why', 'impact', 'recommend', 'reason'];
|
|
194
|
+
if (required.some((key) => !byKey.has(key)) || options.length < 2)
|
|
195
|
+
return body.trim();
|
|
196
|
+
const sortedRanges = selectedRanges.sort((left, right) => left.start - right.start);
|
|
197
|
+
let cursor = heading.end;
|
|
198
|
+
for (const range of sortedRanges) {
|
|
199
|
+
if (/\S/.test(body.slice(cursor, range.start)))
|
|
200
|
+
return body.trim();
|
|
201
|
+
cursor = Math.max(cursor, range.end);
|
|
202
|
+
}
|
|
203
|
+
if (/\S/.test(body.slice(cursor)))
|
|
204
|
+
return body.trim();
|
|
205
|
+
const ordered = [
|
|
206
|
+
byKey.get('decision'), byKey.get('why'), byKey.get('impact')
|
|
207
|
+
];
|
|
208
|
+
for (const optional of ['risk', 'analogy'])
|
|
209
|
+
if (byKey.has(optional))
|
|
210
|
+
ordered.push(byKey.get(optional));
|
|
211
|
+
ordered.push(...optionRanges.map((range) => body.slice(range.start, range.end).trim()));
|
|
212
|
+
ordered.push(byKey.get('recommend'), byKey.get('reason'));
|
|
213
|
+
if (byKey.has('implementation'))
|
|
214
|
+
ordered.push(byKey.get('implementation'));
|
|
215
|
+
return [body.slice(heading.start, heading.end).trim(), ...ordered].join('\n\n').trim();
|
|
216
|
+
}
|
|
217
|
+
export { inspectDecisionDetailDuplicates, normalizeDecisionDetailForDisplay, parseDecisionDetailBlocks, parseDecisionEvidence, resolveDecisionDetail };
|
|
218
|
+
//# sourceMappingURL=decision-details.js.map
|
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { appendActivityEntry, locateActivityLog } from './activity-log.js';
|
|
3
3
|
import { isDecisionItem, listDecisionItems, selectDecisionItem } from './decision-items.js';
|
|
4
4
|
import { createImplementationInput, finalizeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs, selectDeclaredImplementationInput } from './implementation-inputs.js';
|
|
5
|
-
import { LEDGER_COLUMNS, LEDGER_HEADINGS,
|
|
5
|
+
import { LEDGER_COLUMNS, LEDGER_HEADINGS, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument } from './ledger.js';
|
|
6
6
|
import { resolveTaskContext } from './resolve-ref.js';
|
|
7
7
|
import { extractSection, extractSubSection, findSectionHeading } from './sections.js';
|
|
8
8
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
@@ -64,7 +64,10 @@ function applyHumanDecision(request, options = {}) {
|
|
|
64
64
|
let rows;
|
|
65
65
|
try {
|
|
66
66
|
content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
67
|
-
|
|
67
|
+
const ledger = parseLedgerDocument(content);
|
|
68
|
+
if (!ledger.present)
|
|
69
|
+
return failed(LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, resolved.taskId);
|
|
70
|
+
rows = ledger.rows;
|
|
68
71
|
}
|
|
69
72
|
catch (error) {
|
|
70
73
|
return failed('DECISION_DOCUMENT_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
const REMOTE_RE = /^(?!-)(?!.*\.\.)(?!.*[~^:?*\\\s])[A-Za-z0-9._/-]+$/;
|
|
5
|
+
const REF_RE = /^(?!-)(?!.*\.\.)(?!.*(?:^|\/)\.)(?!.*[~^:?*\\\s])[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/;
|
|
6
|
+
function validateRemote(value) {
|
|
7
|
+
return typeof value === 'string' && value.length > 0 && REMOTE_RE.test(value);
|
|
8
|
+
}
|
|
9
|
+
function validateBaseRef(value) {
|
|
10
|
+
return typeof value === 'string' && value.length > 0 && !value.startsWith('refs/') && REF_RE.test(value);
|
|
11
|
+
}
|
|
12
|
+
function invalid(field, value) {
|
|
13
|
+
return { ok: false, code: 'DELIVERY_TARGET_INVALID', message: `${field} is invalid: ${String(value ?? '')}` };
|
|
14
|
+
}
|
|
15
|
+
function bindDeliveryTarget(input) {
|
|
16
|
+
const remote = input.existing?.remote ?? input.explicit?.remote ?? input.defaults?.remote;
|
|
17
|
+
const baseRef = input.existing?.baseRef ?? input.explicit?.baseRef ?? input.defaults?.baseRef;
|
|
18
|
+
if (input.existing?.remote !== undefined && input.explicit?.remote !== undefined && input.existing.remote !== input.explicit.remote) {
|
|
19
|
+
return { ok: false, code: 'DELIVERY_TARGET_CONFLICT', message: 'Explicit delivery remote does not match the task binding' };
|
|
20
|
+
}
|
|
21
|
+
if (input.existing?.baseRef !== undefined && input.explicit?.baseRef !== undefined && input.existing.baseRef !== input.explicit.baseRef) {
|
|
22
|
+
return { ok: false, code: 'DELIVERY_TARGET_CONFLICT', message: 'Explicit delivery base ref does not match the task binding' };
|
|
23
|
+
}
|
|
24
|
+
if (!validateRemote(remote))
|
|
25
|
+
return invalid('delivery remote', remote);
|
|
26
|
+
if (!validateBaseRef(baseRef))
|
|
27
|
+
return invalid('delivery base ref', baseRef);
|
|
28
|
+
return { ok: true, value: { remote, baseRef } };
|
|
29
|
+
}
|
|
30
|
+
function readDeliveryDefaults(repoRoot) {
|
|
31
|
+
try {
|
|
32
|
+
const configPath = path.join(repoRoot, '.agents', '.airc.json');
|
|
33
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
34
|
+
if (!config.delivery || typeof config.delivery !== 'object' || Array.isArray(config.delivery)) {
|
|
35
|
+
return { ok: false, code: 'DELIVERY_CONFIG_INVALID', message: 'delivery config must declare remote and baseRef' };
|
|
36
|
+
}
|
|
37
|
+
const delivery = config.delivery;
|
|
38
|
+
if (typeof delivery.remote !== 'string' || typeof delivery.baseRef !== 'string') {
|
|
39
|
+
return { ok: false, code: 'DELIVERY_CONFIG_INVALID', message: 'delivery config must declare remote and baseRef' };
|
|
40
|
+
}
|
|
41
|
+
return bindDeliveryTarget({
|
|
42
|
+
defaults: {
|
|
43
|
+
remote: delivery.remote,
|
|
44
|
+
baseRef: delivery.baseRef
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return { ok: false, code: 'DELIVERY_CONFIG_INVALID', message: error instanceof Error ? error.message : String(error) };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function resolveDeliveryTarget(repoRoot, existing = {}, explicit = {}) {
|
|
53
|
+
const defaults = readDeliveryDefaults(repoRoot);
|
|
54
|
+
if (!defaults.ok)
|
|
55
|
+
return defaults;
|
|
56
|
+
return bindDeliveryTarget({ defaults: defaults.value, existing, explicit });
|
|
57
|
+
}
|
|
58
|
+
function gitText(repoRoot, args) {
|
|
59
|
+
return execFileSync('git', [...args], {
|
|
60
|
+
cwd: repoRoot,
|
|
61
|
+
encoding: 'utf8',
|
|
62
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
63
|
+
}).trim();
|
|
64
|
+
}
|
|
65
|
+
function resolveTargetHead(repoRoot, target) {
|
|
66
|
+
try {
|
|
67
|
+
const output = gitText(repoRoot, ['ls-remote', '--refs', target.remote, `refs/heads/${target.baseRef}`]);
|
|
68
|
+
const head = output.split(/\s+/)[0] ?? '';
|
|
69
|
+
if (!/^[a-f0-9]{40}$/i.test(head)) {
|
|
70
|
+
return { ok: false, code: 'DELIVERY_TARGET_UNAVAILABLE', message: `Delivery target ${target.remote}/${target.baseRef} is unavailable` };
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
gitText(repoRoot, ['cat-file', '-e', `${head}^{commit}`]);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return { ok: false, code: 'DELIVERY_TARGET_OBJECT_MISSING', message: `Delivery target commit ${head} is not available locally` };
|
|
77
|
+
}
|
|
78
|
+
return { ok: true, value: target, head };
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return { ok: false, code: 'DELIVERY_TARGET_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function resolveDiffBase(repoRoot, reviewedHead, targetHead) {
|
|
85
|
+
try {
|
|
86
|
+
const diffBase = gitText(repoRoot, ['merge-base', reviewedHead, targetHead]);
|
|
87
|
+
if (!/^[a-f0-9]{40}$/i.test(diffBase)) {
|
|
88
|
+
return { ok: false, code: 'DELIVERY_DIFF_BASE_UNAVAILABLE', message: 'Git returned an invalid diff base' };
|
|
89
|
+
}
|
|
90
|
+
return { ok: true, diffBase };
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return { ok: false, code: 'DELIVERY_DIFF_BASE_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export { bindDeliveryTarget, readDeliveryDefaults, resolveDeliveryTarget, resolveTargetHead, resolveDiffBase, validateBaseRef, validateRemote };
|
|
97
|
+
//# sourceMappingURL=delivery-target.js.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
5
|
+
import { resolveTaskRef } from './resolve-ref.js';
|
|
6
|
+
import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead, validateBaseRef, validateRemote } from './delivery-target.js';
|
|
7
|
+
import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead, findAuthoritativeReviewCodeArtifact } from './review-fingerprint.js';
|
|
8
|
+
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
9
|
+
import { withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.js';
|
|
10
|
+
function classifyDeliveryState(localHead, remoteHead, lastDelivered) {
|
|
11
|
+
if (remoteHead === null)
|
|
12
|
+
return { state: 'absent', shouldPush: true };
|
|
13
|
+
if (remoteHead === localHead)
|
|
14
|
+
return { state: 'same', shouldPush: false };
|
|
15
|
+
if (lastDelivered && remoteHead === lastDelivered)
|
|
16
|
+
return { state: 'known-old', shouldPush: true };
|
|
17
|
+
return { state: 'unknown-drift', shouldPush: false };
|
|
18
|
+
}
|
|
19
|
+
function git(cwd, args) {
|
|
20
|
+
const result = spawnSync('git', [...args], { cwd, encoding: 'utf8' });
|
|
21
|
+
return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
|
|
22
|
+
}
|
|
23
|
+
function gitValue(cwd, args) {
|
|
24
|
+
const result = git(cwd, args);
|
|
25
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
26
|
+
}
|
|
27
|
+
function validateBranch(cwd, branch) {
|
|
28
|
+
return git(cwd, ['check-ref-format', `refs/heads/${branch}`]).status === 0;
|
|
29
|
+
}
|
|
30
|
+
function validateReviewTargetCompatibility(repoRoot, taskDir, target, reviewedHead) {
|
|
31
|
+
const artifact = findAuthoritativeReviewCodeArtifact(taskDir);
|
|
32
|
+
if (!artifact.ok || !artifact.path)
|
|
33
|
+
return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: 'A current review-code artifact is required before delivery' };
|
|
34
|
+
let content;
|
|
35
|
+
try {
|
|
36
|
+
content = fs.readFileSync(artifact.path, 'utf8');
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: error instanceof Error ? error.message : String(error) };
|
|
40
|
+
}
|
|
41
|
+
const savedReviewedHead = extractReviewedHead(content) || extractReviewBaseline(content);
|
|
42
|
+
const savedTargetHead = extractReviewTargetHead(content);
|
|
43
|
+
const savedDiffBase = extractReviewDiffBase(content);
|
|
44
|
+
if (!savedReviewedHead || !savedTargetHead || !savedDiffBase) {
|
|
45
|
+
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_INVALID', message: 'review-code must save reviewed head, target head, and diff base before delivery' };
|
|
46
|
+
}
|
|
47
|
+
if (savedReviewedHead !== reviewedHead) {
|
|
48
|
+
return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: 'review-code reviewed head does not match last_reviewed_commit' };
|
|
49
|
+
}
|
|
50
|
+
const saved = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
|
|
51
|
+
if (!saved.ok || saved.diffBase !== savedDiffBase) {
|
|
52
|
+
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_INVALID', message: saved.ok ? 'saved review diff base does not match saved target head' : saved.message };
|
|
53
|
+
}
|
|
54
|
+
const currentTarget = resolveTargetHead(repoRoot, target);
|
|
55
|
+
if (!currentTarget.ok)
|
|
56
|
+
return { ok: false, code: currentTarget.code, message: currentTarget.message };
|
|
57
|
+
const current = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head);
|
|
58
|
+
if (!current.ok)
|
|
59
|
+
return { ok: false, code: current.code, message: current.message };
|
|
60
|
+
if (current.diffBase !== savedDiffBase) {
|
|
61
|
+
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
|
|
62
|
+
}
|
|
63
|
+
return { ok: true };
|
|
64
|
+
}
|
|
65
|
+
function failure(taskId, code, message, retryable = false, status = retryable ? 'blocked' : 'failed') {
|
|
66
|
+
return { status, changed: false, taskId, remoteHead: null, localHead: null, state: null, error: { code, message, retryable } };
|
|
67
|
+
}
|
|
68
|
+
function deliverUnlocked(taskId, taskDir, repoRoot, options) {
|
|
69
|
+
let frontmatter;
|
|
70
|
+
try {
|
|
71
|
+
frontmatter = parseTaskFrontmatter(fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8'));
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return failure(taskId, 'TASK_CONTEXT_UNREADABLE', error instanceof Error ? error.message : String(error));
|
|
75
|
+
}
|
|
76
|
+
if (frontmatter.status !== 'active')
|
|
77
|
+
return failure(taskId, 'TASK_STATE_INVALID', 'task branch delivery requires an active task');
|
|
78
|
+
const branch = frontmatter.branch ?? '';
|
|
79
|
+
if (!branch)
|
|
80
|
+
return failure(taskId, 'DELIVERY_BRANCH_MISSING', 'task branch is missing');
|
|
81
|
+
if (!validateBranch(repoRoot, branch))
|
|
82
|
+
return failure(taskId, 'DELIVERY_BRANCH_INVALID', 'task branch is invalid');
|
|
83
|
+
if (!frontmatter.delivery_remote || !frontmatter.delivery_base_ref) {
|
|
84
|
+
return failure(taskId, 'DELIVERY_TARGET_MISSING', 'task delivery target is not bound; recreate or repair task metadata before delivery');
|
|
85
|
+
}
|
|
86
|
+
const target = resolveDeliveryTarget(repoRoot, {
|
|
87
|
+
remote: frontmatter.delivery_remote,
|
|
88
|
+
baseRef: frontmatter.delivery_base_ref
|
|
89
|
+
}, {
|
|
90
|
+
...(options.remote === undefined ? {} : { remote: options.remote }),
|
|
91
|
+
...(options.baseRef === undefined ? {} : { baseRef: options.baseRef })
|
|
92
|
+
});
|
|
93
|
+
if (!target.ok)
|
|
94
|
+
return failure(taskId, target.code, target.message);
|
|
95
|
+
if (!validateRemote(target.value.remote) || !validateBaseRef(target.value.baseRef))
|
|
96
|
+
return failure(taskId, 'DELIVERY_TARGET_INVALID', 'task delivery target is invalid');
|
|
97
|
+
const localHead = gitValue(repoRoot, ['rev-parse', 'HEAD']);
|
|
98
|
+
if (!localHead)
|
|
99
|
+
return failure(taskId, 'DELIVERY_LOCAL_HEAD_UNAVAILABLE', 'Unable to resolve local HEAD');
|
|
100
|
+
const status = git(repoRoot, ['status', '--porcelain=v1']);
|
|
101
|
+
if (status.status !== 0)
|
|
102
|
+
return failure(taskId, 'DELIVERY_GIT_INSPECT_FAILED', status.stderr.trim() || 'Unable to inspect Git state');
|
|
103
|
+
if (status.stdout.trim())
|
|
104
|
+
return failure(taskId, 'DELIVERY_WORKTREE_DIRTY', 'Working tree must be clean before branch delivery');
|
|
105
|
+
const reviewed = frontmatter.last_reviewed_commit ?? '';
|
|
106
|
+
if (reviewed !== localHead)
|
|
107
|
+
return failure(taskId, 'DELIVERY_REVIEW_REQUIRED', 'Local HEAD must equal last_reviewed_commit before delivery');
|
|
108
|
+
const reviewTarget = validateReviewTargetCompatibility(repoRoot, taskDir, target.value, reviewed);
|
|
109
|
+
if (!reviewTarget.ok)
|
|
110
|
+
return failure(taskId, reviewTarget.code, reviewTarget.message, reviewTarget.code.includes('UNAVAILABLE'), reviewTarget.code.includes('UNAVAILABLE') ? 'blocked' : 'failed');
|
|
111
|
+
const ref = `refs/heads/${branch}`;
|
|
112
|
+
const remoteOutput = git(repoRoot, ['ls-remote', '--refs', target.value.remote, ref]);
|
|
113
|
+
if (remoteOutput.status !== 0)
|
|
114
|
+
return failure(taskId, 'DELIVERY_REMOTE_UNAVAILABLE', remoteOutput.stderr.trim() || 'Unable to inspect the delivery remote', true);
|
|
115
|
+
const remoteHead = remoteOutput.stdout.trim().split(/\s+/)[0] || null;
|
|
116
|
+
const classification = classifyDeliveryState(localHead, remoteHead, frontmatter.delivery_remote_head ?? null);
|
|
117
|
+
if (classification.state === 'unknown-drift')
|
|
118
|
+
return failure(taskId, 'DELIVERY_REMOTE_DRIFT', `Remote branch ${branch} has an unexpected head; refusing to overwrite it`);
|
|
119
|
+
if (options.dryRun)
|
|
120
|
+
return { status: 'planned', changed: classification.shouldPush, taskId, remoteHead, localHead, state: classification.state, error: null };
|
|
121
|
+
if (classification.shouldPush) {
|
|
122
|
+
const args = classification.state === 'known-old'
|
|
123
|
+
? ['push', `--force-with-lease=${ref}:${frontmatter.delivery_remote_head}`, target.value.remote, `HEAD:${ref}`]
|
|
124
|
+
: ['push', `--force-with-lease=${ref}:`, target.value.remote, `HEAD:${ref}`];
|
|
125
|
+
const pushed = git(repoRoot, args);
|
|
126
|
+
if (pushed.status !== 0)
|
|
127
|
+
return failure(taskId, 'DELIVERY_PUSH_FAILED', pushed.stderr.trim() || 'Task branch delivery was rejected', true, 'blocked');
|
|
128
|
+
}
|
|
129
|
+
const verified = gitValue(repoRoot, ['ls-remote', '--refs', target.value.remote, ref])?.split(/\s+/)[0] ?? null;
|
|
130
|
+
if (verified !== localHead)
|
|
131
|
+
return failure(taskId, 'DELIVERY_REMOTE_VERIFY_FAILED', `Expected remote head ${localHead}, received ${verified ?? 'unavailable'}`, true, 'blocked');
|
|
132
|
+
const written = writeTask({
|
|
133
|
+
taskRef: taskId,
|
|
134
|
+
expectedState: 'active',
|
|
135
|
+
mutations: [{ kind: 'frontmatter', set: { delivery_remote: target.value.remote, delivery_base_ref: target.value.baseRef, delivery_remote_head: localHead, assigned_to: options.agent } }]
|
|
136
|
+
}, { repoRoot, metadataProvider: captureTaskWriteMetadata });
|
|
137
|
+
if (written.status === 'failed')
|
|
138
|
+
return failure(taskId, 'DELIVERY_TASK_SYNC_FAILED', written.error.message, true, 'blocked');
|
|
139
|
+
return { status: classification.shouldPush ? 'applied' : 'no-op', changed: classification.shouldPush || written.changed, taskId, remoteHead: localHead, localHead, state: classification.state, error: null };
|
|
140
|
+
}
|
|
141
|
+
function deliverTaskBranch(taskRef, options) {
|
|
142
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
143
|
+
if (!resolved.ok)
|
|
144
|
+
return failure(resolved.taskId, resolved.code, resolved.message);
|
|
145
|
+
try {
|
|
146
|
+
return withRepositoryMutationLock(resolved.repoRoot, () => withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-delivery', () => deliverUnlocked(resolved.taskId, resolved.taskDir, resolved.repoRoot, options)));
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return failure(resolved.taskId, 'DELIVERY_LOCK_FAILED', error instanceof Error ? error.message : String(error), true, 'blocked');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export { classifyDeliveryState, deliverTaskBranch };
|
|
153
|
+
//# sourceMappingURL=delivery.js.map
|
package/dist/lib/task/events.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { appendActivityEntry, locateActivityLog, pairEntries } from './activity-log.js';
|
|
2
|
+
import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
|
|
3
3
|
import { buildArtifactLinkSection, artifactName, inspectArtifactDirectory, parseReviewedInputReference, parseCodePlanInputReference, parseArtifactName, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
4
4
|
import { ArtifactReceiptError, sha256File, upsertArtifactReceipt } from './artifact-receipts.js';
|
|
5
5
|
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
6
6
|
import { consumeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
|
|
7
|
-
import {
|
|
8
|
-
import { parseReviewSummary } from './review-artifacts.js';
|
|
7
|
+
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
8
|
+
import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.js';
|
|
9
9
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
10
10
|
import { findSectionHeading } from './sections.js';
|
|
11
11
|
import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
@@ -101,7 +101,10 @@ function validateReviewFindingCounts(request, content, family, artifactPath) {
|
|
|
101
101
|
const stage = REVIEW_LEDGER_STAGES[family];
|
|
102
102
|
if (!stage || !artifactPath)
|
|
103
103
|
return null;
|
|
104
|
-
const
|
|
104
|
+
const ledger = parseLedgerDocument(content);
|
|
105
|
+
if (!ledger.present)
|
|
106
|
+
return { code: 'EVENT_FINDING_COUNT_MISMATCH', message: `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}` };
|
|
107
|
+
const rows = ledger.rows;
|
|
105
108
|
const invalid = validateLedgerRows(rows);
|
|
106
109
|
if (invalid)
|
|
107
110
|
return { code: 'TASK_DOCUMENT_INVALID', message: `${invalid.code}: ${invalid.message}` };
|
|
@@ -125,6 +128,9 @@ function validateReviewFindingCounts(request, content, family, artifactPath) {
|
|
|
125
128
|
message: parsed.ok ? 'review summary finding counts are not finalized' : parsed.message
|
|
126
129
|
};
|
|
127
130
|
}
|
|
131
|
+
const canonical = resolveCanonicalVerdict(parsed.summary);
|
|
132
|
+
if (!canonical.ok)
|
|
133
|
+
return { code: 'EVENT_VERDICT_INVALID', message: `${canonical.code}: ${canonical.message}` };
|
|
128
134
|
const report = parsed.summary.counts;
|
|
129
135
|
const reportVerdict = parsed.summary.verdict === 'Approved'
|
|
130
136
|
? 'approved'
|
|
@@ -352,7 +358,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
352
358
|
const section = locateActivityLog(content);
|
|
353
359
|
if (!section)
|
|
354
360
|
return failed(request, { code: 'EVENT_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
355
|
-
const rows = pairEntries(section.entries);
|
|
361
|
+
const rows = startedBackedRows(pairEntries(section.entries));
|
|
356
362
|
let normalized = request;
|
|
357
363
|
let artifactContext = null;
|
|
358
364
|
const initialParts = eventParts(request.event);
|
|
@@ -19,6 +19,7 @@ import { applyWorkflowWarningIntent } from './workflow-warning-intents.js';
|
|
|
19
19
|
import { verifyTaskEvent } from './verification.js';
|
|
20
20
|
import { verifyInProcess } from './verification-engine.js';
|
|
21
21
|
import { canonicalTimestamp, writeTask } from './write.js';
|
|
22
|
+
import { readPrDeliveryFact } from './pr-delivery-fact.js';
|
|
22
23
|
const LIFECYCLE_PRODUCER = 'lifecycle.apply';
|
|
23
24
|
const HUMAN_OVERRIDE_SECTION_ALIASES = ['人工豁免账本', 'Human Override Ledger'];
|
|
24
25
|
const HUMAN_OVERRIDE_COLUMNS = [
|
|
@@ -405,7 +406,8 @@ function resourceForSnapshot(policy, options, frontmatter) {
|
|
|
405
406
|
return number === null ? null : { kind: 'issue', number };
|
|
406
407
|
}
|
|
407
408
|
if (policy.producerId === 'platform.pull-request') {
|
|
408
|
-
const
|
|
409
|
+
const fact = frontmatter ? readPrDeliveryFact(frontmatter) : null;
|
|
410
|
+
const number = options.probePullRequestNumber ?? (fact?.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null);
|
|
409
411
|
return number === null ? null : { kind: 'pull-request', number };
|
|
410
412
|
}
|
|
411
413
|
return null;
|
|
@@ -468,7 +470,8 @@ function probePlatformFailure(taskRef, policy, resolved, options) {
|
|
|
468
470
|
return error('OVERRIDE_DOCUMENT_INVALID', cause instanceof Error ? cause.message : String(cause));
|
|
469
471
|
}
|
|
470
472
|
const issueNumber = options.probeIssueNumber ?? toPositiveInteger(frontmatter.issue_number);
|
|
471
|
-
const
|
|
473
|
+
const fact = readPrDeliveryFact(frontmatter);
|
|
474
|
+
const pullRequestNumber = options.probePullRequestNumber ?? (fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null);
|
|
472
475
|
const operation = policy.producerId === 'platform.issue'
|
|
473
476
|
? issueNumber === null
|
|
474
477
|
? null
|
package/dist/lib/task/index.js
CHANGED
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
const USAGE = `Usage: ai task <command> [options]
|
|
2
2
|
|
|
3
3
|
Commands:
|
|
4
|
-
cat [--task <ref>] <artifact | N>
|
|
5
|
-
decisions, d [--task <ref>] [-i
|
|
6
|
-
files [--task <ref>]
|
|
7
|
-
grep
|
|
8
|
-
issue-body [--task <ref>] [--template] Print a deterministic Issue body from task.md
|
|
9
|
-
log [--task <ref>]
|
|
4
|
+
cat [--task <ref> | -t <ref>] <artifact | N> Print a task artifact (by name or number)
|
|
5
|
+
decisions, d [--task <ref> | -t <ref>] [--item <selector> | -i <selector>] List review decisions, or show one item's detail
|
|
6
|
+
files [--task <ref> | -t <ref>] List artifacts in a task dir (numbered)
|
|
7
|
+
grep [--current | --task <ref> | -t <ref>] <pattern> Literal search in the current or selected task
|
|
8
|
+
issue-body [--task <ref> | -t <ref>] [--template <path>] Print a deterministic Issue body from task.md
|
|
9
|
+
log [--task <ref> | -t <ref>] Render a task's activity log as a timeline
|
|
10
10
|
ls [--all | --blocked | --completed] List tasks (default: active)
|
|
11
|
-
show [--task <ref>]
|
|
12
|
-
status [--task <ref>]
|
|
11
|
+
show [--task <ref> | -t <ref>] Print a task.md
|
|
12
|
+
status [--task <ref> | -t <ref>] Aggregated status view (metadata / artifacts / git / platform)
|
|
13
13
|
|
|
14
14
|
Examples:
|
|
15
|
-
ai task cat 11 analysis
|
|
16
15
|
ai task cat analysis
|
|
17
|
-
ai task cat 11
|
|
18
|
-
ai task decisions 11
|
|
16
|
+
ai task cat --task 11 analysis
|
|
17
|
+
ai task decisions --task 11
|
|
19
18
|
ai task decisions --item 1
|
|
20
|
-
ai task d 11 PL-3 --format markdown
|
|
21
|
-
ai task files 11
|
|
19
|
+
ai task d --task 11 --item PL-3 --format markdown
|
|
20
|
+
ai task files --task 11
|
|
22
21
|
ai task grep resolveArtifact
|
|
23
22
|
ai task grep resolveArtifact --current
|
|
24
|
-
ai task grep resolveArtifact 11
|
|
25
|
-
ai task issue-body 11
|
|
26
|
-
ai task issue-body 11 --template .github/ISSUE_TEMPLATE/05_other.yml
|
|
27
|
-
ai task log 11
|
|
23
|
+
ai task grep resolveArtifact --task 11
|
|
24
|
+
ai task issue-body --task 11
|
|
25
|
+
ai task issue-body --task 11 --template .github/ISSUE_TEMPLATE/05_other.yml
|
|
26
|
+
ai task log --task 11
|
|
28
27
|
ai task ls
|
|
29
|
-
ai task show 11
|
|
30
|
-
ai task show TASK-20260612-162737
|
|
31
|
-
ai task status 11
|
|
28
|
+
ai task show --task 11
|
|
29
|
+
ai task show --task TASK-20260612-162737
|
|
30
|
+
ai task status --task 11
|
|
32
31
|
|
|
33
32
|
Run 'ai task <command> --help' for details.`;
|
|
34
33
|
export async function runTask(args) {
|