@fitlab-ai/agent-infra 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.ts +2 -2
- package/dist/bin/cli.js +2 -2
- package/dist/bin/internal-cli.js +98 -0
- package/dist/lib/decide.js +81 -90
- package/dist/lib/defaults.json +9 -0
- package/dist/lib/git/review-snapshot.js +79 -0
- package/dist/lib/git/workflow.js +101 -0
- package/dist/lib/init.js +5 -0
- package/dist/lib/internal/git-workflow.js +42 -0
- package/dist/lib/internal/platform-checks.js +119 -0
- package/dist/lib/internal/platform-comment.js +129 -0
- package/dist/lib/internal/platform-context.js +46 -0
- package/dist/lib/internal/platform-issue.js +142 -0
- package/dist/lib/internal/platform-pr.js +146 -0
- package/dist/lib/internal/release-workflow.js +217 -0
- package/dist/lib/internal/task-artifact.js +67 -0
- package/dist/lib/internal/task-context.js +43 -0
- package/dist/lib/internal/task-event.js +70 -0
- package/dist/lib/internal/task-ledger.js +114 -0
- package/dist/lib/internal/task-lifecycle.js +58 -0
- package/dist/lib/internal/task-short-id.js +83 -0
- package/dist/lib/internal/task-snapshot.js +47 -0
- package/dist/lib/internal/task-verify.js +50 -0
- package/dist/lib/internal/task-warning.js +74 -0
- package/dist/lib/platform/adapters.js +14 -0
- package/dist/lib/platform/context.js +146 -0
- package/dist/lib/platform/github-client.js +124 -0
- package/dist/lib/platform/issue-comments.js +409 -0
- package/dist/lib/platform/issue-metadata.js +183 -0
- package/dist/lib/platform/issues.js +617 -0
- package/dist/lib/platform/metadata-labels.js +28 -0
- package/dist/lib/platform/pr-checks.js +216 -0
- package/dist/lib/platform/pr-summary.js +88 -0
- package/dist/lib/platform/pull-request-metadata.js +38 -0
- package/dist/lib/platform/pull-requests.js +338 -0
- package/dist/lib/platform/releases.js +82 -0
- package/dist/lib/platform/types.js +22 -0
- package/dist/lib/platform/verification-required.js +78 -0
- package/dist/lib/platform/verification-sync.js +1014 -0
- package/dist/lib/platform/verification.js +6 -0
- package/dist/lib/release/channels.js +31 -0
- package/dist/lib/release/workflow.js +32 -0
- package/dist/lib/render.js +3 -2
- package/dist/lib/run/index.js +24 -7
- package/dist/lib/sandbox/capture.js +25 -3
- package/dist/lib/sandbox/commands/create.js +66 -26
- package/dist/lib/sandbox/commands/enter.js +34 -11
- package/dist/lib/sandbox/commands/list-running.js +10 -8
- package/dist/lib/sandbox/commands/show.js +2 -2
- package/dist/lib/sandbox/commands/start.js +53 -15
- package/dist/lib/sandbox/engine.js +33 -3
- package/dist/lib/sandbox/engines/colima.js +2 -2
- package/dist/lib/sandbox/index.js +6 -6
- package/dist/lib/sandbox/mounts.js +36 -0
- package/dist/lib/sandbox/recovery.js +562 -0
- package/dist/lib/sandbox/runtimes/base.dockerfile +4 -0
- package/dist/lib/sandbox/task-resolver.js +5 -1
- package/dist/lib/sandbox/tools.js +19 -0
- package/dist/lib/server/daemon.js +9 -0
- package/dist/lib/server/process-control.js +66 -73
- package/dist/lib/server/process-state.js +168 -0
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +61 -0
- package/dist/lib/task/artifact-lifecycle.js +427 -0
- package/dist/lib/task/command-options.js +30 -0
- package/dist/lib/task/commands/cat.js +36 -6
- package/dist/lib/task/commands/decisions.js +80 -46
- package/dist/lib/task/commands/files.js +21 -7
- package/dist/lib/task/commands/grep.js +55 -7
- package/dist/lib/task/commands/issue-body.js +23 -14
- package/dist/lib/task/commands/log.js +27 -70
- package/dist/lib/task/commands/show.js +20 -7
- package/dist/lib/task/commands/status.js +18 -8
- package/dist/lib/task/decision-intents.js +154 -0
- package/dist/lib/task/decision-items.js +53 -0
- package/dist/lib/task/events.js +318 -0
- package/dist/lib/task/frontmatter.js +159 -1
- package/dist/lib/task/implementation-inputs.js +117 -0
- package/dist/lib/task/index.js +12 -9
- package/dist/lib/task/issue-form.js +19 -6
- package/dist/lib/task/ledger-intents.js +185 -0
- package/dist/lib/task/ledger.js +65 -35
- package/dist/lib/task/lifecycle.js +484 -0
- package/dist/lib/task/resolve-ref.js +152 -32
- package/dist/lib/task/review-artifacts.js +101 -0
- package/dist/lib/task/review-fingerprint.js +46 -0
- package/dist/lib/task/sections.js +358 -4
- package/dist/lib/task/short-id.js +292 -7
- package/dist/lib/task/snapshot.js +95 -0
- package/dist/lib/task/verification-checks.js +8 -0
- package/dist/lib/task/verification-config.js +14 -0
- package/dist/lib/task/verification-engine.js +906 -0
- package/dist/lib/task/verification-review.js +3 -0
- package/dist/lib/task/verification-types.js +2 -0
- package/dist/lib/task/verification.js +113 -0
- package/dist/lib/task/workflow-warning-intents.js +131 -0
- package/dist/lib/task/workflow-warnings.js +10 -78
- package/dist/lib/task/write.js +241 -0
- package/dist/lib/update.js +2 -0
- package/lib/decide.ts +57 -97
- package/lib/defaults.json +9 -0
- package/lib/git/review-snapshot.ts +73 -0
- package/lib/git/workflow.ts +101 -0
- package/lib/init.ts +9 -0
- package/lib/internal/git-workflow.ts +37 -0
- package/lib/internal/platform-checks.ts +101 -0
- package/lib/internal/platform-comment.ts +99 -0
- package/lib/internal/platform-context.ts +33 -0
- package/lib/internal/platform-issue.ts +116 -0
- package/lib/internal/platform-pr.ts +119 -0
- package/lib/internal/release-workflow.ts +186 -0
- package/lib/internal/task-artifact.ts +47 -0
- package/lib/internal/task-context.ts +35 -0
- package/lib/internal/task-event.ts +51 -0
- package/lib/internal/task-ledger.ts +86 -0
- package/lib/internal/task-lifecycle.ts +41 -0
- package/lib/internal/task-short-id.ts +55 -0
- package/lib/internal/task-snapshot.ts +32 -0
- package/lib/internal/task-verify.ts +33 -0
- package/lib/internal/task-warning.ts +50 -0
- package/lib/platform/adapters.ts +30 -0
- package/lib/platform/context.ts +164 -0
- package/lib/platform/github-client.ts +146 -0
- package/lib/platform/issue-comments.ts +479 -0
- package/lib/platform/issue-metadata.ts +250 -0
- package/lib/platform/issues.ts +691 -0
- package/lib/platform/metadata-labels.ts +33 -0
- package/lib/platform/pr-checks.ts +246 -0
- package/lib/platform/pr-summary.ts +90 -0
- package/lib/platform/pull-request-metadata.ts +69 -0
- package/lib/platform/pull-requests.ts +380 -0
- package/lib/platform/releases.ts +71 -0
- package/lib/platform/types.ts +74 -0
- package/lib/platform/verification-required.ts +83 -0
- package/{templates/.agents/scripts/platform-adapters/platform-sync.github.js → lib/platform/verification-sync.ts} +221 -363
- package/lib/platform/verification.ts +11 -0
- package/lib/release/channels.ts +30 -0
- package/lib/release/workflow.ts +31 -0
- package/lib/render.ts +3 -2
- package/lib/run/index.ts +25 -7
- package/lib/sandbox/capture.ts +29 -3
- package/lib/sandbox/commands/create.ts +75 -28
- package/lib/sandbox/commands/enter.ts +38 -11
- package/lib/sandbox/commands/list-running.ts +10 -8
- package/lib/sandbox/commands/show.ts +2 -2
- package/lib/sandbox/commands/start.ts +50 -16
- package/lib/sandbox/engine.ts +37 -3
- package/lib/sandbox/engines/colima.ts +2 -2
- package/lib/sandbox/index.ts +6 -6
- package/lib/sandbox/mounts.ts +55 -0
- package/lib/sandbox/recovery.ts +828 -0
- package/lib/sandbox/runtimes/base.dockerfile +4 -0
- package/lib/sandbox/task-resolver.ts +5 -1
- package/lib/sandbox/tools.ts +31 -0
- package/lib/server/daemon.ts +9 -0
- package/lib/server/process-control.ts +70 -71
- package/lib/server/process-state.ts +183 -0
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +62 -0
- package/lib/task/artifact-lifecycle.ts +484 -0
- package/lib/task/command-options.ts +32 -0
- package/lib/task/commands/cat.ts +22 -9
- package/lib/task/commands/decisions.ts +49 -47
- package/lib/task/commands/files.ts +14 -6
- package/lib/task/commands/grep.ts +34 -7
- package/lib/task/commands/issue-body.ts +16 -16
- package/lib/task/commands/log.ts +17 -70
- package/lib/task/commands/show.ts +14 -6
- package/lib/task/commands/status.ts +17 -7
- package/lib/task/decision-intents.ts +163 -0
- package/lib/task/decision-items.ts +64 -0
- package/lib/task/events.ts +320 -0
- package/lib/task/frontmatter.ts +202 -2
- package/lib/task/implementation-inputs.ts +137 -0
- package/lib/task/index.ts +12 -9
- package/lib/task/issue-form.ts +20 -6
- package/lib/task/ledger-intents.ts +201 -0
- package/lib/task/ledger.ts +77 -31
- package/lib/task/lifecycle.ts +458 -0
- package/lib/task/resolve-ref.ts +197 -36
- package/{templates/.agents/scripts/lib/review-artifacts.js → lib/task/review-artifacts.ts} +11 -12
- package/{templates/.agents/scripts/lib/post-review-commit.js → lib/task/review-fingerprint.ts} +20 -10
- package/lib/task/sections.ts +486 -4
- package/lib/task/short-id.ts +403 -9
- package/lib/task/snapshot.ts +143 -0
- package/lib/task/verification-checks.ts +9 -0
- package/lib/task/verification-config.ts +20 -0
- package/{templates/.agents/scripts/validate-artifact.js → lib/task/verification-engine.ts} +257 -451
- package/lib/task/verification-review.ts +9 -0
- package/lib/task/verification-types.ts +29 -0
- package/lib/task/verification.ts +156 -0
- package/lib/task/workflow-warning-intents.ts +142 -0
- package/lib/task/workflow-warnings.ts +10 -78
- package/lib/task/write.ts +468 -0
- package/lib/update.ts +6 -0
- package/package.json +5 -2
- package/templates/.agents/README.en.md +6 -3
- package/templates/.agents/README.zh-CN.md +6 -3
- package/templates/.agents/rules/README.en.md +6 -5
- package/templates/.agents/rules/README.zh-CN.md +6 -5
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +5 -184
- package/templates/.agents/rules/create-issue.github.zh-CN.md +12 -178
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/human-decision-context.en.md +36 -0
- package/templates/.agents/rules/human-decision-context.zh-CN.md +36 -0
- package/templates/.agents/rules/issue-fields.github.en.md +4 -148
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +9 -144
- package/templates/.agents/rules/issue-pr-commands.en.md +43 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +15 -220
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +23 -213
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +45 -3
- package/templates/.agents/rules/issue-sync.en.md +1 -1
- package/templates/.agents/rules/issue-sync.github.en.md +14 -301
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +36 -287
- package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
- package/templates/.agents/rules/milestone-inference.en.md +1 -1
- package/templates/.agents/rules/milestone-inference.github.en.md +7 -119
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +8 -108
- package/templates/.agents/rules/milestone-inference.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +12 -11
- package/templates/.agents/rules/next-step-output.zh-CN.md +12 -11
- package/templates/.agents/rules/no-mid-flow-questions.en.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.en.md +16 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +10 -54
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +19 -47
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +30 -3
- package/templates/.agents/rules/pr-sync.en.md +30 -3
- package/templates/.agents/rules/pr-sync.github.en.md +12 -115
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +26 -104
- package/templates/.agents/rules/pr-sync.zh-CN.md +43 -3
- package/templates/.agents/rules/review-handshake.en.md +13 -9
- package/templates/.agents/rules/review-handshake.zh-CN.md +13 -9
- package/templates/.agents/rules/task-management.en.md +11 -2
- package/templates/.agents/rules/task-management.zh-CN.md +11 -3
- package/templates/.agents/rules/task-short-id.en.md +25 -29
- package/templates/.agents/rules/task-short-id.zh-CN.md +20 -23
- package/templates/.agents/scripts/lib/agent-infra-package.js +141 -0
- package/templates/.agents/scripts/task-short-id.js +71 -533
- package/templates/.agents/skills/analyze-task/SKILL.en.md +22 -55
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +26 -59
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -3
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +21 -47
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +23 -47
- package/templates/.agents/skills/block-task/config/verify.json +1 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +16 -45
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +16 -45
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +6 -3
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -24
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -24
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -24
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -24
- package/templates/.agents/skills/code-task/SKILL.en.md +22 -35
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +28 -44
- package/templates/.agents/skills/code-task/config/verify.en.json +3 -3
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +3 -3
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +13 -9
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +13 -9
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/SKILL.en.md +18 -14
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +18 -14
- package/templates/.agents/skills/commit/config/verify.json +1 -2
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -11
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -11
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +4 -8
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +4 -8
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +6 -18
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +5 -17
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +42 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +42 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +13 -36
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +13 -36
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -3
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +10 -54
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +10 -79
- package/templates/.agents/skills/complete-task/SKILL.en.md +59 -63
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +59 -63
- package/templates/.agents/skills/complete-task/config/verify.en.json +11 -6
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +11 -6
- package/templates/.agents/skills/create-pr/SKILL.en.md +19 -44
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +19 -43
- package/templates/.agents/skills/create-pr/config/verify.json +1 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +8 -12
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +7 -11
- package/templates/.agents/skills/create-task/SKILL.en.md +12 -13
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +12 -13
- package/templates/.agents/skills/create-task/config/verify.json +1 -2
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -2
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -2
- package/templates/.agents/skills/import-issue/SKILL.en.md +9 -13
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -14
- package/templates/.agents/skills/import-issue/config/verify.json +1 -2
- package/templates/.agents/skills/init-milestones/SKILL.en.md +2 -2
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +5 -4
- package/templates/.agents/skills/plan-task/SKILL.en.md +17 -42
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +22 -49
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -3
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/post-release/SKILL.en.md +11 -81
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +8 -78
- package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +16 -163
- package/templates/.agents/skills/release/SKILL.zh-CN.md +15 -162
- package/templates/.agents/skills/restore-task/SKILL.en.md +11 -29
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -29
- package/templates/.agents/skills/restore-task/config/verify.json +1 -2
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -25
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +13 -33
- package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +9 -12
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +9 -12
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +9 -2
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +9 -2
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +22 -36
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +23 -41
- package/templates/.agents/skills/review-code/config/verify.en.json +5 -5
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +5 -5
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +56 -18
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +56 -18
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -4
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -4
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -25
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +13 -33
- package/templates/.agents/skills/review-plan/config/verify.en.json +3 -4
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -4
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +9 -12
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +9 -12
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +9 -2
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +9 -2
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +7 -0
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +7 -0
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +6 -3
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +177 -151
- package/templates/.agents/skills/watch-pr/SKILL.en.md +24 -14
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +25 -15
- package/templates/.agents/skills/watch-pr/config/verify.json +1 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
- package/templates/.agents/templates/task.en.md +6 -1
- package/templates/.agents/templates/task.zh-CN.md +6 -1
- 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 -0
- package/templates/.claude/commands/commit.zh-CN.md +1 -0
- 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/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/block-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/check-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/code-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/commit.en.toml +4 -0
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +4 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/create-pr.en.toml +2 -0
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/plan-task.en.toml +2 -0
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-code.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/review-plan.en.toml +2 -0
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +2 -0
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +2 -0
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +2 -0
- package/templates/.git-hooks/check-large-files.cjs +92 -0
- package/templates/.git-hooks/check-version-format.sh +5 -1
- package/templates/.git-hooks/pre-commit +1 -0
- package/templates/.opencode/commands/analyze-task.en.md +1 -1
- package/templates/.opencode/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/check-task.en.md +1 -1
- package/templates/.opencode/commands/check-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/code-task.en.md +1 -1
- package/templates/.opencode/commands/code-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/commit.en.md +2 -0
- package/templates/.opencode/commands/commit.zh-CN.md +2 -0
- package/templates/.opencode/commands/complete-task.en.md +1 -1
- package/templates/.opencode/commands/complete-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/plan-task.en.md +1 -1
- package/templates/.opencode/commands/plan-task.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-analysis.en.md +1 -1
- package/templates/.opencode/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-code.en.md +1 -1
- package/templates/.opencode/commands/review-code.zh-CN.md +1 -1
- package/templates/.opencode/commands/review-plan.en.md +1 -1
- package/templates/.opencode/commands/review-plan.zh-CN.md +1 -1
- package/templates/.agents/scripts/platform-adapters/platform-sync.js +0 -13
- package/templates/.agents/scripts/review-diff-fingerprint.js +0 -99
- package/templates/.agents/scripts/workflow-warnings.js +0 -290
- package/templates/.agents/skills/code-task/scripts/detect-mode.js +0 -257
- package/templates/.agents/skills/release/scripts/manage-milestones.github.sh +0 -80
- package/templates/.agents/skills/release/scripts/manage-milestones.sh +0 -6
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// frontmatter, ledger, Git snapshot and platform domains are imported below.
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import process from "node:process";
|
|
@@ -7,16 +8,17 @@ import { fileURLToPath } from "node:url";
|
|
|
7
8
|
import {
|
|
8
9
|
extractReviewBaseline,
|
|
9
10
|
extractReviewDiffFingerprint,
|
|
11
|
+
extractReviewedSnapshotTree,
|
|
10
12
|
findAuthoritativeReviewCodeArtifact,
|
|
11
13
|
parseReviewVerdict,
|
|
12
14
|
resolvePostReviewGlobs
|
|
13
|
-
} from "./
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
15
|
+
} from "./verification-review.ts";
|
|
16
|
+
import { check as checkPlatformSync } from "../platform/verification-sync.ts";
|
|
17
|
+
import { check as checkRequiredChecks } from "../platform/verification-required.ts";
|
|
18
|
+
import { parseTypedTaskFrontmatter } from "./frontmatter.ts";
|
|
19
|
+
import { parseLedger } from "./ledger.ts";
|
|
20
|
+
import { loadVerificationConfig } from "./verification-config.ts";
|
|
21
|
+
import { snapshotReview } from "../git/review-snapshot.ts";
|
|
20
22
|
|
|
21
23
|
const TASK_ENUMS = {
|
|
22
24
|
type: ["feature", "bugfix", "refactor", "docs", "chore"],
|
|
@@ -36,7 +38,6 @@ const DEFAULT_REQUIRED_FIELDS = [
|
|
|
36
38
|
"assigned_to"
|
|
37
39
|
];
|
|
38
40
|
|
|
39
|
-
const DEFAULT_FRESHNESS_MINUTES = 30;
|
|
40
41
|
const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?$/;
|
|
41
42
|
const AGENT_INFRA_VERSION_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
42
43
|
const ACTIVITY_LOG_PATTERN = /^- (\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?) — \*\*(.+?)\*\* by (.+?) — (.+)$/;
|
|
@@ -69,25 +70,14 @@ const WORKFLOW_WARNING_SEVERITIES = new Set(["IMPORTANT", "ACTION_REQUIRED"]);
|
|
|
69
70
|
const WORKFLOW_WARNING_ID_PATTERN = /^WW-\d+$/;
|
|
70
71
|
|
|
71
72
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
72
|
-
|
|
73
|
+
let repoRoot = path.resolve(path.dirname(scriptPath), "..", "..");
|
|
73
74
|
|
|
74
|
-
const PLATFORM_ADAPTERS = {
|
|
75
|
+
const PLATFORM_ADAPTERS: Record<string, (context: any, shared: any) => any> = {
|
|
76
|
+
"platform-sync": checkPlatformSync,
|
|
77
|
+
"platform-sync-preflight": (context: any, shared: any) => ({ ...checkPlatformSync(context, shared), type: "platform-sync-preflight" }),
|
|
78
|
+
"required-checks": checkRequiredChecks
|
|
79
|
+
};
|
|
75
80
|
const OPTIONAL_PLATFORM_CHECKS = new Set(["platform-sync"]);
|
|
76
|
-
const adaptersDir = path.join(path.dirname(scriptPath), "platform-adapters");
|
|
77
|
-
|
|
78
|
-
if (fs.existsSync(adaptersDir)) {
|
|
79
|
-
for (const file of fs.readdirSync(adaptersDir)) {
|
|
80
|
-
if (!file.endsWith(".js")) {
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const adapterName = path.basename(file, ".js");
|
|
85
|
-
const mod = await import(new URL(`./platform-adapters/${file}`, import.meta.url));
|
|
86
|
-
if (typeof mod.check === "function") {
|
|
87
|
-
PLATFORM_ADAPTERS[adapterName] = mod.check;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
81
|
|
|
92
82
|
const sharedUtils = {
|
|
93
83
|
loadTask,
|
|
@@ -100,125 +90,17 @@ const sharedUtils = {
|
|
|
100
90
|
blockedResult,
|
|
101
91
|
safeStat,
|
|
102
92
|
parseIssueNumber,
|
|
103
|
-
parsePrNumber
|
|
104
|
-
repoRoot
|
|
93
|
+
parsePrNumber
|
|
105
94
|
};
|
|
106
95
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
function main(argv) {
|
|
110
|
-
const [mode, ...rest] = argv;
|
|
111
|
-
|
|
112
|
-
if (mode === "gate") {
|
|
113
|
-
runGate(rest);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (mode === "check") {
|
|
118
|
-
runSingleCheck(rest);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
printUsageAndExit();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function runGate(args) {
|
|
126
|
-
const { value: formatValue, rest: positional } = extractOption(args, "--format");
|
|
127
|
-
const format = normalizeFormat(formatValue);
|
|
128
|
-
const [skillName, taskDirArg, artifactFile] = positional;
|
|
129
|
-
|
|
130
|
-
if (!skillName || !taskDirArg) {
|
|
131
|
-
printUsageAndExit();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const taskDir = path.resolve(taskDirArg);
|
|
135
|
-
const verifyConfig = loadVerifyConfig(skillName);
|
|
136
|
-
const checks = [];
|
|
137
|
-
|
|
138
|
-
for (const [type, checkConfig] of Object.entries(verifyConfig.checks || {})) {
|
|
139
|
-
if (checkConfig === null) {
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const result = runCheck(type, {
|
|
144
|
-
skillName,
|
|
145
|
-
taskDir,
|
|
146
|
-
artifactFile,
|
|
147
|
-
config: checkConfig
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
checks.push(result);
|
|
151
|
-
|
|
152
|
-
if (result.status === "blocked") {
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const gate = summarizeGate(checks);
|
|
158
|
-
const output = {
|
|
159
|
-
gate,
|
|
160
|
-
skill: skillName,
|
|
161
|
-
checks,
|
|
162
|
-
summary: summarizeChecks(checks),
|
|
163
|
-
action: buildAction(gate, checks)
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
writeOutput(output, format);
|
|
167
|
-
process.exit(EXIT_CODE[gate]);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function runSingleCheck(args) {
|
|
171
|
-
const { value: formatValue, rest: formatArgs } = extractOption(args, "--format");
|
|
172
|
-
const format = normalizeFormat(formatValue);
|
|
173
|
-
const { value: skillName, rest: positional } = extractOption(formatArgs, "--skill");
|
|
174
|
-
|
|
175
|
-
if (!skillName) {
|
|
176
|
-
printUsageAndExit();
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const [type, taskDirArg, artifactFile] = positional;
|
|
180
|
-
|
|
181
|
-
if (!type || !taskDirArg) {
|
|
182
|
-
printUsageAndExit();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const verifyConfig = loadVerifyConfig(skillName);
|
|
186
|
-
const config = (verifyConfig.checks || {})[type];
|
|
187
|
-
|
|
188
|
-
if (config === undefined) {
|
|
189
|
-
failUsage(`Unknown check type '${type}' for skill '${skillName}'.`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (config === null) {
|
|
193
|
-
writeOutput({
|
|
194
|
-
type,
|
|
195
|
-
skill: skillName,
|
|
196
|
-
status: "pass",
|
|
197
|
-
message: `Check '${type}' is disabled for skill '${skillName}'.`
|
|
198
|
-
}, format);
|
|
199
|
-
process.exit(0);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const result = runCheck(type, {
|
|
203
|
-
skillName,
|
|
204
|
-
taskDir: path.resolve(taskDirArg),
|
|
205
|
-
artifactFile,
|
|
206
|
-
config
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
writeOutput({
|
|
210
|
-
skill: skillName,
|
|
211
|
-
...result
|
|
212
|
-
}, format);
|
|
213
|
-
process.exit(EXIT_CODE[result.status] ?? 1);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function runCheck(type, context) {
|
|
96
|
+
function runCheck(type: any, context: any, shared: any): any {
|
|
217
97
|
switch (type) {
|
|
218
98
|
case "task-meta":
|
|
219
99
|
return checkTaskMeta(context);
|
|
220
100
|
case "artifact":
|
|
221
101
|
return checkArtifact(context);
|
|
102
|
+
case "implementation-input":
|
|
103
|
+
return checkImplementationInput(context);
|
|
222
104
|
case "activity-log":
|
|
223
105
|
return checkActivityLog(context);
|
|
224
106
|
case "completion-checklist":
|
|
@@ -239,14 +121,14 @@ function runCheck(type, context) {
|
|
|
239
121
|
return failResult(type, `Unsupported check type '${type}'.`);
|
|
240
122
|
}
|
|
241
123
|
|
|
242
|
-
return adapter(context,
|
|
124
|
+
return adapter(context, shared);
|
|
243
125
|
}
|
|
244
126
|
}
|
|
245
127
|
}
|
|
246
128
|
|
|
247
129
|
// === Check Functions ===
|
|
248
130
|
|
|
249
|
-
function checkTaskMeta({ taskDir, config }) {
|
|
131
|
+
function checkTaskMeta({ taskDir, config }: any): any {
|
|
250
132
|
const task = loadTask(taskDir);
|
|
251
133
|
if (!task.ok) {
|
|
252
134
|
return failResult("task-meta", task.message);
|
|
@@ -254,8 +136,8 @@ function checkTaskMeta({ taskDir, config }) {
|
|
|
254
136
|
|
|
255
137
|
const metadata = task.metadata;
|
|
256
138
|
const requiredFields = config.required_fields || DEFAULT_REQUIRED_FIELDS;
|
|
257
|
-
const missingFields = requiredFields.filter((field) => isBlank(metadata[field]));
|
|
258
|
-
const blockingMissingFields = missingFields.filter((field) => field !== "agent_infra_version");
|
|
139
|
+
const missingFields = requiredFields.filter((field: any) => isBlank(metadata[field]));
|
|
140
|
+
const blockingMissingFields = missingFields.filter((field: any) => field !== "agent_infra_version");
|
|
259
141
|
const warnings = [];
|
|
260
142
|
if (missingFields.includes("agent_infra_version")) {
|
|
261
143
|
warnings.push("field 'agent_infra_version' missing — historical task or skipped version stamp");
|
|
@@ -348,11 +230,11 @@ function checkTaskMeta({ taskDir, config }) {
|
|
|
348
230
|
return passResult(
|
|
349
231
|
"task-meta",
|
|
350
232
|
`Task metadata valid (${requiredFields.length} required fields checked${warningSuffix})`,
|
|
351
|
-
warnings
|
|
233
|
+
warnings as string[]
|
|
352
234
|
);
|
|
353
235
|
}
|
|
354
236
|
|
|
355
|
-
function validateTaskBranch(metadata) {
|
|
237
|
+
function validateTaskBranch(metadata: any): any {
|
|
356
238
|
if (isBlank(metadata.branch)) {
|
|
357
239
|
return null;
|
|
358
240
|
}
|
|
@@ -372,7 +254,7 @@ function validateTaskBranch(metadata) {
|
|
|
372
254
|
return null;
|
|
373
255
|
}
|
|
374
256
|
|
|
375
|
-
function loadProjectName() {
|
|
257
|
+
function loadProjectName(): any {
|
|
376
258
|
const configPath = path.join(repoRoot, ".agents", ".airc.json");
|
|
377
259
|
if (!fs.existsSync(configPath)) {
|
|
378
260
|
return "";
|
|
@@ -386,7 +268,7 @@ function loadProjectName() {
|
|
|
386
268
|
}
|
|
387
269
|
}
|
|
388
270
|
|
|
389
|
-
function loadReviewConfig() {
|
|
271
|
+
function loadReviewConfig(): any {
|
|
390
272
|
const configPath = path.join(repoRoot, ".agents", ".airc.json");
|
|
391
273
|
if (!fs.existsSync(configPath)) {
|
|
392
274
|
return {};
|
|
@@ -400,7 +282,7 @@ function loadReviewConfig() {
|
|
|
400
282
|
}
|
|
401
283
|
}
|
|
402
284
|
|
|
403
|
-
function checkArtifact({ taskDir, config, artifactFile }) {
|
|
285
|
+
function checkArtifact({ taskDir, config, artifactFile }: any): any {
|
|
404
286
|
const resolvedArtifact = resolveArtifactPath(taskDir, config.file_pattern, artifactFile);
|
|
405
287
|
if (!resolvedArtifact.ok) {
|
|
406
288
|
return failResult("artifact", resolvedArtifact.message);
|
|
@@ -419,7 +301,7 @@ function checkArtifact({ taskDir, config, artifactFile }) {
|
|
|
419
301
|
const content = fs.readFileSync(artifactPath, "utf8");
|
|
420
302
|
const requiredSections = config.required_sections || [];
|
|
421
303
|
const missingSections = requiredSections.filter(
|
|
422
|
-
(section) => !new RegExp(`^##\\s+${escapeRegExp(section)}\\s*$`, "m").test(content)
|
|
304
|
+
(section: any) => !new RegExp(`^##\\s+${escapeRegExp(section)}\\s*$`, "m").test(content)
|
|
423
305
|
);
|
|
424
306
|
|
|
425
307
|
if (missingSections.length > 0) {
|
|
@@ -436,22 +318,80 @@ function checkArtifact({ taskDir, config, artifactFile }) {
|
|
|
436
318
|
}
|
|
437
319
|
}
|
|
438
320
|
|
|
439
|
-
const freshnessMinutes = Number(config.freshness_minutes ?? DEFAULT_FRESHNESS_MINUTES);
|
|
440
|
-
const ageMinutes = (Date.now() - stat.mtimeMs) / 60000;
|
|
441
|
-
if (Number.isFinite(freshnessMinutes) && ageMinutes > freshnessMinutes) {
|
|
442
|
-
return failResult(
|
|
443
|
-
"artifact",
|
|
444
|
-
`${path.basename(artifactPath)} is stale (${ageMinutes.toFixed(1)}m old, limit ${freshnessMinutes}m)`
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
321
|
return passResult(
|
|
449
322
|
"artifact",
|
|
450
|
-
`${path.basename(artifactPath)} passed (${requiredSections.length} sections
|
|
323
|
+
`${path.basename(artifactPath)} passed (${requiredSections.length} sections)`
|
|
451
324
|
);
|
|
452
325
|
}
|
|
453
326
|
|
|
454
|
-
function
|
|
327
|
+
function checkImplementationInput({ taskDir, artifactFile }: any): any {
|
|
328
|
+
const task = loadTask(taskDir);
|
|
329
|
+
if (!task.ok) return failResult("implementation-input", task.message);
|
|
330
|
+
if (!artifactFile) return failResult("implementation-input", "Artifact file is required");
|
|
331
|
+
const artifactPath = path.join(taskDir, artifactFile);
|
|
332
|
+
if (!safeStat(artifactPath)?.isFile()) return failResult("implementation-input", `Artifact not found: ${artifactFile}`);
|
|
333
|
+
|
|
334
|
+
const inputSection = getSectionContent(task.content, ["实现输入", "Implementation Inputs"]);
|
|
335
|
+
const rows = [];
|
|
336
|
+
if (inputSection) {
|
|
337
|
+
const table = inputSection.split(/\r?\n/).filter((line: any) => line.trim().startsWith("|"));
|
|
338
|
+
const cells = (line: any) => line.split("|").slice(1, -1).map((cell: any) => cell.trim());
|
|
339
|
+
const expected = ["id", "ledger_id", "decision_evidence", "stage", "needs_implementation", "decided_at", "status", "consumed_by"];
|
|
340
|
+
if (table.length < 2 || JSON.stringify(cells(table[0])) !== JSON.stringify(expected)) {
|
|
341
|
+
return failResult("implementation-input", "Implementation Inputs table schema is invalid");
|
|
342
|
+
}
|
|
343
|
+
const seen = new Set();
|
|
344
|
+
for (const line of table.slice(2)) {
|
|
345
|
+
const row = cells(line);
|
|
346
|
+
if (row.length !== 8 || !/^II-[1-9]\d*$/.test(row[0]) || seen.has(row[0])) {
|
|
347
|
+
return failResult("implementation-input", "Implementation Inputs table contains an invalid or duplicate id");
|
|
348
|
+
}
|
|
349
|
+
seen.add(row[0]);
|
|
350
|
+
rows.push({ id: row[0], ledgerId: row[1], evidence: row[2], stage: row[3], needs: row[4], status: row[6], consumedBy: row[7] });
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const logSection = getSectionContent(task.content, ["活动日志", "Activity Log"]);
|
|
355
|
+
const doneActions = logSection.split(/\r?\n/).flatMap((line: any) => {
|
|
356
|
+
const match = line.trim().match(ACTIVITY_LOG_PATTERN);
|
|
357
|
+
return match && !ACTIVITY_LOG_STARTED_RE.test(match[2]) ? [match[2]] : [];
|
|
358
|
+
});
|
|
359
|
+
const latestAction = doneActions.at(-1) || "";
|
|
360
|
+
const actionDecision = /(?:Code Task|Code) \(Round \d+, decision (II-[1-9]\d*)\)/.exec(latestAction)?.[1] || null;
|
|
361
|
+
const report = fs.readFileSync(artifactPath, "utf8");
|
|
362
|
+
const reportSection = getSectionContent(report, ["实现输入", "Implementation Input"]);
|
|
363
|
+
if (!reportSection) return failResult("implementation-input", "Implementation Input report section not found");
|
|
364
|
+
const field = (zh: any, en: any) => {
|
|
365
|
+
const pattern = "^- \\*\\*(?:" + escapeRegExp(zh) + "|" + escapeRegExp(en) + ")\\*\\*[::]\\s*`?([^`\\n]+)`?\\s*$";
|
|
366
|
+
const match = new RegExp(pattern, "m").exec(reportSection);
|
|
367
|
+
return match?.[1]?.trim() || "";
|
|
368
|
+
};
|
|
369
|
+
const reportInput = field("裁决输入", "Decision Input");
|
|
370
|
+
const reportLedger = field("账本 ID", "Ledger ID");
|
|
371
|
+
const reportEvidence = field("裁决证据", "Decision Evidence");
|
|
372
|
+
|
|
373
|
+
if (!actionDecision) {
|
|
374
|
+
if (reportInput && reportInput !== "N/A") {
|
|
375
|
+
return failResult("implementation-input", "Non-decision code action must report Decision Input as N/A");
|
|
376
|
+
}
|
|
377
|
+
return passResult("implementation-input", "Non-decision implementation input identity is consistent");
|
|
378
|
+
}
|
|
379
|
+
if (reportInput !== actionDecision) {
|
|
380
|
+
return failResult("implementation-input", `Report decision input '${reportInput}' does not match Activity Log ${actionDecision}`);
|
|
381
|
+
}
|
|
382
|
+
const matches = rows.filter((row) => row.id === actionDecision);
|
|
383
|
+
if (matches.length !== 1) return failResult("implementation-input", `${actionDecision} is missing or duplicated in task table`);
|
|
384
|
+
const row = matches[0]!;
|
|
385
|
+
if (row.stage !== "code" || row.needs !== "true" || row.status !== "consumed" || row.consumedBy !== artifactFile || !row.evidence) {
|
|
386
|
+
return failResult("implementation-input", `${actionDecision} is not a consumed input for ${artifactFile}`);
|
|
387
|
+
}
|
|
388
|
+
if (reportLedger !== row.ledgerId || reportEvidence !== row.evidence) {
|
|
389
|
+
return failResult("implementation-input", `${actionDecision} report identity does not match task table evidence`);
|
|
390
|
+
}
|
|
391
|
+
return passResult("implementation-input", `${actionDecision} matches Activity Log, report, and task table`);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function checkActivityLog({ taskDir, config }: any): any {
|
|
455
395
|
const task = loadTask(taskDir);
|
|
456
396
|
if (!task.ok) {
|
|
457
397
|
return failResult("activity-log", task.message);
|
|
@@ -464,8 +404,8 @@ function checkActivityLog({ taskDir, config }) {
|
|
|
464
404
|
|
|
465
405
|
const entries = logSection
|
|
466
406
|
.split(/\r?\n/)
|
|
467
|
-
.map((line) => line.trim())
|
|
468
|
-
.filter((line) => line.startsWith("- "));
|
|
407
|
+
.map((line: any) => line.trim())
|
|
408
|
+
.filter((line: any) => line.startsWith("- "));
|
|
469
409
|
|
|
470
410
|
if (entries.length === 0) {
|
|
471
411
|
return failResult("activity-log", "Activity Log has no entries");
|
|
@@ -489,7 +429,7 @@ function checkActivityLog({ taskDir, config }) {
|
|
|
489
429
|
previousTimestamp = timestamp;
|
|
490
430
|
// Ascending order is checked over every entry, but a `[started]` marker is
|
|
491
431
|
// not a terminal action: keep latestAction/latestTimestamp on the most
|
|
492
|
-
// recent done entry so expected_action_pattern
|
|
432
|
+
// recent done entry so expected_action_pattern sees it.
|
|
493
433
|
if (!ACTIVITY_LOG_STARTED_RE.test(action)) {
|
|
494
434
|
latestTimestamp = timestamp;
|
|
495
435
|
latestAction = action;
|
|
@@ -503,21 +443,10 @@ function checkActivityLog({ taskDir, config }) {
|
|
|
503
443
|
);
|
|
504
444
|
}
|
|
505
445
|
|
|
506
|
-
const freshnessMinutes = Number(config.freshness_minutes ?? DEFAULT_FRESHNESS_MINUTES);
|
|
507
|
-
if (Number.isFinite(freshnessMinutes)) {
|
|
508
|
-
const ageMinutes = minutesSinceTimestamp(latestTimestamp);
|
|
509
|
-
if (ageMinutes > freshnessMinutes) {
|
|
510
|
-
return failResult(
|
|
511
|
-
"activity-log",
|
|
512
|
-
`Latest Activity Log entry is stale (${ageMinutes.toFixed(1)}m old, limit ${freshnessMinutes}m)`
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
446
|
return passResult("activity-log", `Latest entry '${latestAction}' at ${latestTimestamp}`);
|
|
518
447
|
}
|
|
519
448
|
|
|
520
|
-
function checkCompletionChecklist({ taskDir, config }) {
|
|
449
|
+
function checkCompletionChecklist({ taskDir, config }: any): any {
|
|
521
450
|
const task = loadTask(taskDir);
|
|
522
451
|
if (!task.ok) {
|
|
523
452
|
return failResult("completion-checklist", task.message);
|
|
@@ -530,8 +459,8 @@ function checkCompletionChecklist({ taskDir, config }) {
|
|
|
530
459
|
|
|
531
460
|
const items = checklist
|
|
532
461
|
.split(/\r?\n/)
|
|
533
|
-
.map((line) => line.trim())
|
|
534
|
-
.filter((line) => /^- \[(?: |x|X)\] .+$/.test(line));
|
|
462
|
+
.map((line: any) => line.trim())
|
|
463
|
+
.filter((line: any) => /^- \[(?: |x|X)\] .+$/.test(line));
|
|
535
464
|
|
|
536
465
|
if (items.length === 0) {
|
|
537
466
|
return failResult("completion-checklist", "Completion Checklist has no checkbox items");
|
|
@@ -539,9 +468,9 @@ function checkCompletionChecklist({ taskDir, config }) {
|
|
|
539
468
|
|
|
540
469
|
if (config.require_all_checked) {
|
|
541
470
|
const unchecked = items
|
|
542
|
-
.map((line) => line.match(/^- \[ \] (.+)$/))
|
|
471
|
+
.map((line: any) => line.match(/^- \[ \] (.+)$/))
|
|
543
472
|
.filter(Boolean)
|
|
544
|
-
.map((match) => match[1].trim());
|
|
473
|
+
.map((match: any) => match[1].trim());
|
|
545
474
|
|
|
546
475
|
if (unchecked.length > 0) {
|
|
547
476
|
return failResult(
|
|
@@ -554,27 +483,7 @@ function checkCompletionChecklist({ taskDir, config }) {
|
|
|
554
483
|
return passResult("completion-checklist", `Completion Checklist valid (${items.length} items checked)`);
|
|
555
484
|
}
|
|
556
485
|
|
|
557
|
-
function
|
|
558
|
-
const rows = [];
|
|
559
|
-
for (const rawLine of String(section || "").split(/\r?\n/)) {
|
|
560
|
-
const line = rawLine.trim();
|
|
561
|
-
if (!line.startsWith("|")) {
|
|
562
|
-
continue;
|
|
563
|
-
}
|
|
564
|
-
if (/^\|[\s:|-]+\|?$/.test(line)) {
|
|
565
|
-
continue; // separator row
|
|
566
|
-
}
|
|
567
|
-
const inner = line.replace(/^\|/, "").replace(/\|$/, "");
|
|
568
|
-
const cells = inner.split("|").map((cell) => cell.trim());
|
|
569
|
-
if ((cells[0] || "").toLowerCase() === "id") {
|
|
570
|
-
continue; // header row
|
|
571
|
-
}
|
|
572
|
-
rows.push(cells);
|
|
573
|
-
}
|
|
574
|
-
return rows;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
function splitMarkdownTableRow(line) {
|
|
486
|
+
function splitMarkdownTableRow(line: any): any {
|
|
578
487
|
let value = String(line || "").trim();
|
|
579
488
|
if (!value.startsWith("|")) {
|
|
580
489
|
return [];
|
|
@@ -596,7 +505,7 @@ function splitMarkdownTableRow(line) {
|
|
|
596
505
|
return cells;
|
|
597
506
|
}
|
|
598
507
|
|
|
599
|
-
function unescapeMarkdownTableCell(value) {
|
|
508
|
+
function unescapeMarkdownTableCell(value: any): any {
|
|
600
509
|
let output = "";
|
|
601
510
|
for (let index = 0; index < value.length; index += 1) {
|
|
602
511
|
const char = value[index];
|
|
@@ -611,7 +520,7 @@ function unescapeMarkdownTableCell(value) {
|
|
|
611
520
|
return output;
|
|
612
521
|
}
|
|
613
522
|
|
|
614
|
-
function isEscapedAt(value, index) {
|
|
523
|
+
function isEscapedAt(value: any, index: any): any {
|
|
615
524
|
let backslashes = 0;
|
|
616
525
|
for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
|
|
617
526
|
backslashes += 1;
|
|
@@ -619,7 +528,7 @@ function isEscapedAt(value, index) {
|
|
|
619
528
|
return backslashes % 2 === 1;
|
|
620
529
|
}
|
|
621
530
|
|
|
622
|
-
function parseWorkflowWarningRows(section) {
|
|
531
|
+
function parseWorkflowWarningRows(section: any): any {
|
|
623
532
|
const rows = [];
|
|
624
533
|
for (const rawLine of String(section || "").split(/\r?\n/)) {
|
|
625
534
|
const cells = splitMarkdownTableRow(rawLine);
|
|
@@ -629,7 +538,7 @@ function parseWorkflowWarningRows(section) {
|
|
|
629
538
|
if ((cells[0] || "").toLowerCase() === "id") {
|
|
630
539
|
continue;
|
|
631
540
|
}
|
|
632
|
-
if (cells.every((cell) => /^:?-{3,}:?$/.test(cell))) {
|
|
541
|
+
if (cells.every((cell: any) => /^:?-{3,}:?$/.test(cell))) {
|
|
633
542
|
continue;
|
|
634
543
|
}
|
|
635
544
|
rows.push(cells);
|
|
@@ -637,7 +546,7 @@ function parseWorkflowWarningRows(section) {
|
|
|
637
546
|
return rows;
|
|
638
547
|
}
|
|
639
548
|
|
|
640
|
-
function validateWorkflowWarnings(content) {
|
|
549
|
+
function validateWorkflowWarnings(content: any): any {
|
|
641
550
|
const section = getSectionContent(content, WORKFLOW_WARNING_SECTION_NAMES);
|
|
642
551
|
if (!section.trim()) {
|
|
643
552
|
return [];
|
|
@@ -685,7 +594,7 @@ function validateWorkflowWarnings(content) {
|
|
|
685
594
|
return errors;
|
|
686
595
|
}
|
|
687
596
|
|
|
688
|
-
function resolveReviewSetting(config, key, fallback) {
|
|
597
|
+
function resolveReviewSetting(config: any, key: any, fallback: any): any {
|
|
689
598
|
if (config && config[key] !== undefined && config[key] !== null) {
|
|
690
599
|
return config[key];
|
|
691
600
|
}
|
|
@@ -696,7 +605,7 @@ function resolveReviewSetting(config, key, fallback) {
|
|
|
696
605
|
return fallback;
|
|
697
606
|
}
|
|
698
607
|
|
|
699
|
-
function checkReviewLedger({ taskDir, config }) {
|
|
608
|
+
function checkReviewLedger({ taskDir, config }: any): any {
|
|
700
609
|
const task = loadTask(taskDir);
|
|
701
610
|
if (!task.ok) {
|
|
702
611
|
return failResult("review-ledger", task.message);
|
|
@@ -707,7 +616,14 @@ function checkReviewLedger({ taskDir, config }) {
|
|
|
707
616
|
return passResult("review-ledger", "No disagreement ledger section; treated as no open disagreements");
|
|
708
617
|
}
|
|
709
618
|
|
|
710
|
-
|
|
619
|
+
let rows;
|
|
620
|
+
try {
|
|
621
|
+
rows = parseLedger(task.content).map((row) => [
|
|
622
|
+
row.id, row.stage, row.round, row.severity, row.status, row.evidence
|
|
623
|
+
]);
|
|
624
|
+
} catch (error) {
|
|
625
|
+
return failResult("review-ledger", `Invalid disagreement ledger: ${error instanceof Error ? error.message : String(error)}`);
|
|
626
|
+
}
|
|
711
627
|
if (rows.length === 0) {
|
|
712
628
|
return passResult("review-ledger", "Disagreement ledger has no entries");
|
|
713
629
|
}
|
|
@@ -735,23 +651,23 @@ function checkReviewLedger({ taskDir, config }) {
|
|
|
735
651
|
}
|
|
736
652
|
inScopeCount += 1;
|
|
737
653
|
|
|
738
|
-
if (!LEDGER_STATUSES.has(status)) {
|
|
654
|
+
if (!LEDGER_STATUSES.has(status!)) {
|
|
739
655
|
problems.push(`${id}: illegal status '${status}'`);
|
|
740
656
|
continue;
|
|
741
657
|
}
|
|
742
658
|
if (status !== "open" && evidence === "") {
|
|
743
659
|
problems.push(`${id}: status '${status}' requires evidence`);
|
|
744
660
|
}
|
|
745
|
-
const round = Number.parseInt(roundRaw
|
|
661
|
+
const round = Number.parseInt(roundRaw!, 10);
|
|
746
662
|
if (
|
|
747
663
|
Number.isFinite(round) &&
|
|
748
664
|
round >= maxRounds &&
|
|
749
|
-
!LEDGER_TERMINAL_OK.has(status) &&
|
|
665
|
+
!LEDGER_TERMINAL_OK.has(status!) &&
|
|
750
666
|
status !== "needs-human-decision"
|
|
751
667
|
) {
|
|
752
668
|
problems.push(`${id}: round ${round} reached limit ${maxRounds} without convergence; escalate to needs-human-decision`);
|
|
753
669
|
}
|
|
754
|
-
if (!LEDGER_TERMINAL_OK.has(status)) {
|
|
670
|
+
if (!LEDGER_TERMINAL_OK.has(status!)) {
|
|
755
671
|
problems.push(`${id}: unresolved (status '${status}')`);
|
|
756
672
|
}
|
|
757
673
|
}
|
|
@@ -764,7 +680,7 @@ function checkReviewLedger({ taskDir, config }) {
|
|
|
764
680
|
return passResult("review-ledger", `Disagreement ledger clean (${inScopeCount} in-scope entries terminal${scopeLabel})`);
|
|
765
681
|
}
|
|
766
682
|
|
|
767
|
-
function checkPostReviewCommit({ taskDir, config }) {
|
|
683
|
+
function checkPostReviewCommit({ taskDir, config }: any): any {
|
|
768
684
|
const reviewArtifact = findAuthoritativeReviewCodeArtifact(taskDir);
|
|
769
685
|
if (!reviewArtifact.ok) {
|
|
770
686
|
return passResult("post-review-commit", "No review-code artifact; check inactive");
|
|
@@ -778,13 +694,11 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
778
694
|
}
|
|
779
695
|
|
|
780
696
|
const task = loadTask(taskDir);
|
|
781
|
-
const content = fs.readFileSync(reviewArtifact.path
|
|
782
|
-
const reviewBaseline = extractReviewBaseline(content);
|
|
697
|
+
const content = fs.readFileSync(reviewArtifact.path!, "utf8");
|
|
783
698
|
const lastReviewedCommit = task.ok ? (task.metadata.last_reviewed_commit || "").trim() : "";
|
|
784
699
|
const baselineSource = resolvePostReviewBaseline({
|
|
785
700
|
gitRoot,
|
|
786
701
|
lastReviewedCommit,
|
|
787
|
-
reviewBaseline,
|
|
788
702
|
reviewArtifact: reviewArtifact.fileName
|
|
789
703
|
});
|
|
790
704
|
if (!baselineSource.ok) {
|
|
@@ -805,10 +719,14 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
805
719
|
return passResult("post-review-commit", `No post-review commits to code/rule paths since ${sha.slice(0, 8)}`);
|
|
806
720
|
}
|
|
807
721
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
722
|
+
let exempt = false;
|
|
723
|
+
try {
|
|
724
|
+
exempt = task.ok && parseLedger(task.content).some(
|
|
725
|
+
(row) => row.stage === POST_REVIEW_COMMIT_STAGE && row.status === "human-decided"
|
|
726
|
+
);
|
|
727
|
+
} catch {
|
|
728
|
+
return failResult("post-review-commit", "Invalid disagreement ledger");
|
|
729
|
+
}
|
|
812
730
|
if (exempt) {
|
|
813
731
|
return passResult(
|
|
814
732
|
"post-review-commit",
|
|
@@ -818,11 +736,11 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
818
736
|
|
|
819
737
|
return failResult(
|
|
820
738
|
"post-review-commit",
|
|
821
|
-
`${commits.length} commit(s) to code/rule paths after
|
|
739
|
+
`${commits.length} commit(s) to code/rule paths after reviewed commit ${sha.slice(0, 8)}; re-run review-code or record a human-decided exemption`
|
|
822
740
|
);
|
|
823
741
|
}
|
|
824
742
|
|
|
825
|
-
function checkReviewFact({ taskDir, artifactFile }) {
|
|
743
|
+
function checkReviewFact({ taskDir, artifactFile }: any): any {
|
|
826
744
|
const resolvedArtifact = resolveArtifactPath(
|
|
827
745
|
taskDir,
|
|
828
746
|
"review-code.md|review-code-r{N}.md",
|
|
@@ -841,6 +759,7 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
841
759
|
const verdict = parseReviewVerdict(content);
|
|
842
760
|
const reviewBaseline = extractReviewBaseline(content);
|
|
843
761
|
const reviewedFingerprint = extractReviewDiffFingerprint(content);
|
|
762
|
+
const reviewedTree = extractReviewedSnapshotTree(content);
|
|
844
763
|
|
|
845
764
|
if (!["通过", "需要修改", "拒绝", "Approved", "Changes Requested", "Rejected"].includes(verdict)) {
|
|
846
765
|
return failResult("review-fact", `Unsupported review verdict '${verdict}'`);
|
|
@@ -867,13 +786,9 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
867
786
|
);
|
|
868
787
|
}
|
|
869
788
|
|
|
870
|
-
let
|
|
789
|
+
let actualSnapshot;
|
|
871
790
|
try {
|
|
872
|
-
|
|
873
|
-
process.execPath,
|
|
874
|
-
[path.join(repoRoot, ".agents", "scripts", "review-diff-fingerprint.js"), "worktree", baseline],
|
|
875
|
-
{ cwd: gitRoot, encoding: "utf8" }
|
|
876
|
-
).trim();
|
|
791
|
+
actualSnapshot = snapshotReview({ cwd: gitRoot, mode: "worktree", baseline, globs: resolvePostReviewGlobs({}, loadReviewConfig()) });
|
|
877
792
|
} catch {
|
|
878
793
|
return blockedResult(
|
|
879
794
|
"review-fact",
|
|
@@ -881,26 +796,37 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
881
796
|
);
|
|
882
797
|
}
|
|
883
798
|
|
|
884
|
-
if (
|
|
799
|
+
if (actualSnapshot.fingerprint !== reviewedFingerprint) {
|
|
885
800
|
return failResult(
|
|
886
801
|
"review-fact",
|
|
887
802
|
`Reviewed diff fingerprint does not match the current worktree for baseline ${baseline.slice(0, 8)}; re-run review-code`
|
|
888
803
|
);
|
|
889
804
|
}
|
|
890
805
|
|
|
806
|
+
if (!reviewedTree || actualSnapshot.tree !== reviewedTree) {
|
|
807
|
+
return failResult(
|
|
808
|
+
"review-fact",
|
|
809
|
+
`Reviewed snapshot tree does not match the current worktree for baseline ${baseline.slice(0, 8)}; re-run review-code`
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
|
|
891
813
|
if (["通过", "Approved"].includes(verdict)) {
|
|
892
814
|
const lastReviewedCommit = String(task.metadata.last_reviewed_commit || "").trim();
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
815
|
+
const cleanSnapshot = actualSnapshot.tree === execFileSync(
|
|
816
|
+
"git",
|
|
817
|
+
["-C", gitRoot, "rev-parse", `${baseline}^{tree}`],
|
|
818
|
+
{ encoding: "utf8" }
|
|
819
|
+
).trim();
|
|
820
|
+
if (cleanSnapshot && lastReviewedCommit !== baseline) {
|
|
821
|
+
return failResult(
|
|
822
|
+
"review-fact",
|
|
823
|
+
`Approved clean review must set task last_reviewed_commit to baseline ${baseline.slice(0, 8)}`
|
|
824
|
+
);
|
|
898
825
|
}
|
|
899
|
-
|
|
900
|
-
if (reviewedCommit !== baseline) {
|
|
826
|
+
if (!cleanSnapshot && lastReviewedCommit) {
|
|
901
827
|
return failResult(
|
|
902
828
|
"review-fact",
|
|
903
|
-
|
|
829
|
+
"Approved review with uncommitted changes must remain unanchored until commit"
|
|
904
830
|
);
|
|
905
831
|
}
|
|
906
832
|
}
|
|
@@ -911,38 +837,21 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
911
837
|
);
|
|
912
838
|
}
|
|
913
839
|
|
|
914
|
-
function resolvePostReviewBaseline({ gitRoot, lastReviewedCommit,
|
|
915
|
-
if (lastReviewedCommit) {
|
|
916
|
-
|
|
917
|
-
return { ok: true, sha: lastReviewedCommit };
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
if (!reviewBaseline) {
|
|
922
|
-
return {
|
|
923
|
-
ok: false,
|
|
924
|
-
result: passResult(
|
|
925
|
-
"post-review-commit",
|
|
926
|
-
`${reviewArtifact} predates baseline-commit anchoring; skipped (legacy artifact)`,
|
|
927
|
-
[`${reviewArtifact} has no 审查基线提交 / Review Baseline Commit field`]
|
|
928
|
-
)
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
if (!SHA_PATTERN.test(reviewBaseline)) {
|
|
933
|
-
return {
|
|
934
|
-
ok: false,
|
|
935
|
-
result: blockedResult(
|
|
936
|
-
"post-review-commit",
|
|
937
|
-
`${reviewArtifact} has an empty or malformed 审查基线提交 SHA ('${reviewBaseline}'); manual remediation required`
|
|
938
|
-
)
|
|
939
|
-
};
|
|
840
|
+
function resolvePostReviewBaseline({ gitRoot, lastReviewedCommit, reviewArtifact }: any): any {
|
|
841
|
+
if (SHA_PATTERN.test(lastReviewedCommit) && gitCommitExists(gitRoot, lastReviewedCommit)) {
|
|
842
|
+
return { ok: true, sha: lastReviewedCommit };
|
|
940
843
|
}
|
|
941
844
|
|
|
942
|
-
return {
|
|
845
|
+
return {
|
|
846
|
+
ok: false,
|
|
847
|
+
result: blockedResult(
|
|
848
|
+
"post-review-commit",
|
|
849
|
+
`${reviewArtifact}: reviewed snapshot was not anchored to a valid commit; re-run commit or review-code`
|
|
850
|
+
)
|
|
851
|
+
};
|
|
943
852
|
}
|
|
944
853
|
|
|
945
|
-
function gitCommitExists(gitRoot, sha) {
|
|
854
|
+
function gitCommitExists(gitRoot: any, sha: any): any {
|
|
946
855
|
try {
|
|
947
856
|
execFileSync("git", ["-C", gitRoot, "cat-file", "-e", `${sha}^{commit}`], { encoding: "utf8" });
|
|
948
857
|
return true;
|
|
@@ -953,31 +862,24 @@ function gitCommitExists(gitRoot, sha) {
|
|
|
953
862
|
|
|
954
863
|
// === File & Config Loaders ===
|
|
955
864
|
|
|
956
|
-
function
|
|
957
|
-
const verifyPath = path.join(repoRoot, ".agents", "skills", skillName, "config", "verify.json");
|
|
958
|
-
if (!fs.existsSync(verifyPath)) {
|
|
959
|
-
failUsage(`config/verify.json not found for skill '${skillName}'`);
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
return JSON.parse(fs.readFileSync(verifyPath, "utf8"));
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
function loadTask(taskDir) {
|
|
865
|
+
function loadTask(taskDir: any): any {
|
|
966
866
|
const taskPath = path.join(taskDir, "task.md");
|
|
967
867
|
if (!fs.existsSync(taskPath)) {
|
|
968
868
|
return { ok: false, message: `Task file not found: ${taskPath}` };
|
|
969
869
|
}
|
|
970
870
|
|
|
971
871
|
const content = fs.readFileSync(taskPath, "utf8");
|
|
972
|
-
|
|
973
|
-
|
|
872
|
+
let metadata;
|
|
873
|
+
try {
|
|
874
|
+
metadata = Object.fromEntries(Object.entries(parseTypedTaskFrontmatter(content)).map(([key, value]) => [key, value === null ? "" : String(value)]));
|
|
875
|
+
} catch {
|
|
974
876
|
return { ok: false, message: "task.md frontmatter not found or invalid" };
|
|
975
877
|
}
|
|
976
878
|
|
|
977
879
|
return { ok: true, content, metadata };
|
|
978
880
|
}
|
|
979
881
|
|
|
980
|
-
function resolveArtifactPath(taskDir, filePattern, artifactFile) {
|
|
882
|
+
function resolveArtifactPath(taskDir: any, filePattern: any, artifactFile: any): any {
|
|
981
883
|
if (artifactFile) {
|
|
982
884
|
return { ok: true, path: path.join(taskDir, artifactFile) };
|
|
983
885
|
}
|
|
@@ -989,7 +891,7 @@ function resolveArtifactPath(taskDir, filePattern, artifactFile) {
|
|
|
989
891
|
const entries = fs.existsSync(taskDir) ? fs.readdirSync(taskDir) : [];
|
|
990
892
|
const matches = [];
|
|
991
893
|
|
|
992
|
-
for (const pattern of filePattern.split("|").map((value) => value.trim()).filter(Boolean)) {
|
|
894
|
+
for (const pattern of filePattern.split("|").map((value: any) => value.trim()).filter(Boolean)) {
|
|
993
895
|
const regex = new RegExp(`^${escapePattern(pattern)}$`);
|
|
994
896
|
for (const entry of entries) {
|
|
995
897
|
const match = entry.match(regex);
|
|
@@ -1009,54 +911,53 @@ function resolveArtifactPath(taskDir, filePattern, artifactFile) {
|
|
|
1009
911
|
}
|
|
1010
912
|
|
|
1011
913
|
matches.sort((left, right) => right.round - left.round || left.fileName.localeCompare(right.fileName));
|
|
1012
|
-
return { ok: true, path: path.join(taskDir, matches[0]
|
|
914
|
+
return { ok: true, path: path.join(taskDir, matches[0]!.fileName) };
|
|
1013
915
|
}
|
|
1014
916
|
|
|
1015
|
-
function
|
|
1016
|
-
const
|
|
1017
|
-
if (!match) {
|
|
1018
|
-
return null;
|
|
1019
|
-
}
|
|
917
|
+
function getSectionContent(content: any, names: any): any {
|
|
918
|
+
const lines = content.split(/\r?\n/);
|
|
1020
919
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
920
|
+
function visibleHeadings(): any {
|
|
921
|
+
const headings = [];
|
|
922
|
+
let fence = null;
|
|
923
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
924
|
+
const line = lines[index];
|
|
925
|
+
if (fence) {
|
|
926
|
+
const closer = line.match(/^ {0,3}(`+|~+)\s*$/);
|
|
927
|
+
if (closer && closer[1][0] === fence.character && closer[1].length >= fence.length) {
|
|
928
|
+
fence = null;
|
|
929
|
+
}
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
const opener = line.match(/^ {0,3}(`{3,}|~{3,})(?:[^`~].*)?$/);
|
|
933
|
+
if (opener) {
|
|
934
|
+
fence = { character: opener[1][0], length: opener[1].length };
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
if (line.startsWith("## ")) {
|
|
938
|
+
headings.push({ index, text: line.trim() });
|
|
939
|
+
}
|
|
1026
940
|
}
|
|
1027
|
-
|
|
1028
|
-
const [, key, rawValue] = parsed;
|
|
1029
|
-
metadata[key] = rawValue.trim().replace(/^['"]|['"]$/g, "");
|
|
941
|
+
return headings;
|
|
1030
942
|
}
|
|
1031
943
|
|
|
1032
|
-
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
function getSectionContent(content, names) {
|
|
1036
|
-
const lines = content.split(/\r?\n/);
|
|
944
|
+
const headings = visibleHeadings();
|
|
1037
945
|
|
|
1038
946
|
for (const name of names) {
|
|
1039
947
|
const heading = `## ${name}`;
|
|
1040
|
-
const
|
|
1041
|
-
if (
|
|
948
|
+
const position = headings.findIndex((item: any) => item.text === heading);
|
|
949
|
+
if (position === -1) {
|
|
1042
950
|
continue;
|
|
1043
951
|
}
|
|
1044
|
-
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1047
|
-
if (lines[index].startsWith("## ")) {
|
|
1048
|
-
break;
|
|
1049
|
-
}
|
|
1050
|
-
sectionLines.push(lines[index]);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
return sectionLines.join("\n").trim();
|
|
952
|
+
const startIndex = headings[position]!.index;
|
|
953
|
+
const endIndex = headings[position + 1]?.index ?? lines.length;
|
|
954
|
+
return lines.slice(startIndex + 1, endIndex).join("\n").trim();
|
|
1054
955
|
}
|
|
1055
956
|
|
|
1056
957
|
return "";
|
|
1057
958
|
}
|
|
1058
959
|
|
|
1059
|
-
function getCheckedRequirements(content) {
|
|
960
|
+
function getCheckedRequirements(content: any): any {
|
|
1060
961
|
const section = getSectionContent(content, ["需求", "Requirements"]);
|
|
1061
962
|
if (!section) {
|
|
1062
963
|
return [];
|
|
@@ -1064,13 +965,13 @@ function getCheckedRequirements(content) {
|
|
|
1064
965
|
|
|
1065
966
|
return section
|
|
1066
967
|
.split(/\r?\n/)
|
|
1067
|
-
.map((line) => line.trim())
|
|
1068
|
-
.map((line) => line.match(/^- \[x\] (.+)$/i))
|
|
968
|
+
.map((line: any) => line.trim())
|
|
969
|
+
.map((line: any) => line.match(/^- \[x\] (.+)$/i))
|
|
1069
970
|
.filter(Boolean)
|
|
1070
|
-
.map((match) => match[1].trim());
|
|
971
|
+
.map((match: any) => match[1].trim());
|
|
1071
972
|
}
|
|
1072
973
|
|
|
1073
|
-
function parseIssueNumber(value) {
|
|
974
|
+
function parseIssueNumber(value: any): any {
|
|
1074
975
|
if (isBlank(value) || value === "N/A") {
|
|
1075
976
|
return null;
|
|
1076
977
|
}
|
|
@@ -1079,53 +980,43 @@ function parseIssueNumber(value) {
|
|
|
1079
980
|
return match ? Number(match[0]) : null;
|
|
1080
981
|
}
|
|
1081
982
|
|
|
1082
|
-
function parsePrNumber(value) {
|
|
983
|
+
function parsePrNumber(value: any): any {
|
|
1083
984
|
return parseIssueNumber(value);
|
|
1084
985
|
}
|
|
1085
986
|
|
|
1086
987
|
// === Utilities ===
|
|
1087
988
|
|
|
1088
|
-
function normalizeContent(text) {
|
|
989
|
+
function normalizeContent(text: any): any {
|
|
1089
990
|
return String(text || "")
|
|
1090
991
|
.replace(/\r\n/g, "\n")
|
|
1091
992
|
.replace(/\n{3,}/g, "\n\n")
|
|
1092
993
|
.trim();
|
|
1093
994
|
}
|
|
1094
995
|
|
|
1095
|
-
function
|
|
1096
|
-
const normalized = timestamp.includes("T") ? timestamp : timestamp.replace(" ", "T");
|
|
1097
|
-
const parsed = Date.parse(normalized);
|
|
1098
|
-
if (Number.isNaN(parsed)) {
|
|
1099
|
-
return Number.POSITIVE_INFINITY;
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
return (Date.now() - parsed) / 60000;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
function interpolate(template, taskDir, artifactFile) {
|
|
996
|
+
function interpolate(template: any, taskDir: any, artifactFile: any): any {
|
|
1106
997
|
const artifactStem = artifactFile ? path.basename(artifactFile, path.extname(artifactFile)) : "";
|
|
1107
998
|
return template
|
|
1108
999
|
.replace(/\{task-id\}/g, path.basename(taskDir))
|
|
1109
1000
|
.replace(/\{artifact-stem\}/g, artifactStem);
|
|
1110
1001
|
}
|
|
1111
1002
|
|
|
1112
|
-
function summarizeGate(checks) {
|
|
1113
|
-
if (checks.some((check) => check.status === "blocked")) {
|
|
1003
|
+
function summarizeGate(checks: any): any {
|
|
1004
|
+
if (checks.some((check: any) => check.status === "blocked")) {
|
|
1114
1005
|
return "blocked";
|
|
1115
1006
|
}
|
|
1116
1007
|
|
|
1117
|
-
if (checks.some((check) => check.status === "fail")) {
|
|
1008
|
+
if (checks.some((check: any) => check.status === "fail")) {
|
|
1118
1009
|
return "fail";
|
|
1119
1010
|
}
|
|
1120
1011
|
|
|
1121
1012
|
return "pass";
|
|
1122
1013
|
}
|
|
1123
1014
|
|
|
1124
|
-
function summarizeChecks(checks) {
|
|
1015
|
+
function summarizeChecks(checks: any): any {
|
|
1125
1016
|
const counts = {
|
|
1126
|
-
pass: checks.filter((check) => check.status === "pass").length,
|
|
1127
|
-
fail: checks.filter((check) => check.status === "fail").length,
|
|
1128
|
-
blocked: checks.filter((check) => check.status === "blocked").length
|
|
1017
|
+
pass: checks.filter((check: any) => check.status === "pass").length,
|
|
1018
|
+
fail: checks.filter((check: any) => check.status === "fail").length,
|
|
1019
|
+
blocked: checks.filter((check: any) => check.status === "blocked").length
|
|
1129
1020
|
};
|
|
1130
1021
|
|
|
1131
1022
|
if (counts.blocked > 0) {
|
|
@@ -1135,12 +1026,12 @@ function summarizeChecks(checks) {
|
|
|
1135
1026
|
return `${counts.pass} passed, ${counts.fail} failed`;
|
|
1136
1027
|
}
|
|
1137
1028
|
|
|
1138
|
-
function buildAction(gate, checks) {
|
|
1029
|
+
function buildAction(gate: any, checks: any): any {
|
|
1139
1030
|
if (gate === "pass") {
|
|
1140
1031
|
return "All declared checks passed";
|
|
1141
1032
|
}
|
|
1142
1033
|
|
|
1143
|
-
const firstFailure = checks.find((check) => check.status !== "pass");
|
|
1034
|
+
const firstFailure = checks.find((check: any) => check.status !== "pass");
|
|
1144
1035
|
if (!firstFailure) {
|
|
1145
1036
|
return "Review validation output";
|
|
1146
1037
|
}
|
|
@@ -1152,47 +1043,23 @@ function buildAction(gate, checks) {
|
|
|
1152
1043
|
return `Fix ${firstFailure.type} issues and re-run gate`;
|
|
1153
1044
|
}
|
|
1154
1045
|
|
|
1155
|
-
function
|
|
1156
|
-
|
|
1157
|
-
return "Requested check passed";
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
if (result.status === "blocked") {
|
|
1161
|
-
return `Resolve blocked ${result.type} check and re-run check`;
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
return `Fix ${result.type} issues and re-run check`;
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
function buildSingleCheckSummary(status) {
|
|
1168
|
-
if (status === "pass") {
|
|
1169
|
-
return "1 passed, 0 failed";
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
if (status === "blocked") {
|
|
1173
|
-
return "0 passed, 0 failed, 1 blocked";
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
return "0 passed, 1 failed";
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
function passResult(type, message, warnings = []) {
|
|
1180
|
-
const result = { type, status: "pass", message };
|
|
1046
|
+
function passResult(type: any, message: any, warnings: string[] = []): any {
|
|
1047
|
+
const result: { type: any; status: string; message: any; warnings?: string[] } = { type, status: "pass", message };
|
|
1181
1048
|
if (warnings.length > 0) {
|
|
1182
1049
|
result.warnings = warnings;
|
|
1183
1050
|
}
|
|
1184
1051
|
return result;
|
|
1185
1052
|
}
|
|
1186
1053
|
|
|
1187
|
-
function failResult(type, message, failType = "check_failed") {
|
|
1054
|
+
function failResult(type: any, message: any, failType = "check_failed"): any {
|
|
1188
1055
|
return { type, status: "fail", fail_type: failType, message };
|
|
1189
1056
|
}
|
|
1190
1057
|
|
|
1191
|
-
function blockedResult(type, message, failType = "network_error") {
|
|
1058
|
+
function blockedResult(type: any, message: any, failType = "network_error"): any {
|
|
1192
1059
|
return { type, status: "blocked", fail_type: failType, message };
|
|
1193
1060
|
}
|
|
1194
1061
|
|
|
1195
|
-
function safeStat(filePath) {
|
|
1062
|
+
function safeStat(filePath: any): any {
|
|
1196
1063
|
try {
|
|
1197
1064
|
return fs.statSync(filePath);
|
|
1198
1065
|
} catch {
|
|
@@ -1200,105 +1067,44 @@ function safeStat(filePath) {
|
|
|
1200
1067
|
}
|
|
1201
1068
|
}
|
|
1202
1069
|
|
|
1203
|
-
function escapePattern(pattern) {
|
|
1070
|
+
function escapePattern(pattern: any): any {
|
|
1204
1071
|
return escapeRegExp(pattern)
|
|
1205
1072
|
.replace(/\\\{N\\\}/g, "(\\d+)");
|
|
1206
1073
|
}
|
|
1207
1074
|
|
|
1208
|
-
function escapeRegExp(value) {
|
|
1075
|
+
function escapeRegExp(value: any): any {
|
|
1209
1076
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1210
1077
|
}
|
|
1211
1078
|
|
|
1212
|
-
function isBlank(value) {
|
|
1079
|
+
function isBlank(value: any): any {
|
|
1213
1080
|
return value === undefined || value === null || String(value).trim() === "";
|
|
1214
1081
|
}
|
|
1215
1082
|
|
|
1216
|
-
function
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
if (arg === name) {
|
|
1223
|
-
value = args[index + 1];
|
|
1224
|
-
index += 1;
|
|
1225
|
-
continue;
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
const inlinePrefix = `${name}=`;
|
|
1229
|
-
if (arg.startsWith(inlinePrefix)) {
|
|
1230
|
-
value = arg.slice(inlinePrefix.length);
|
|
1231
|
-
continue;
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
rest.push(arg);
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
return { value, rest };
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
function normalizeFormat(value) {
|
|
1241
|
-
return value === "text" ? "text" : "json";
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
function formatStatusLabel(status) {
|
|
1245
|
-
if (status === "fail") {
|
|
1246
|
-
return "FAIL";
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
if (status === "blocked") {
|
|
1250
|
-
return "BLOCKED";
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
return "pass";
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
function writeOutput(value, format) {
|
|
1257
|
-
if (format === "text") {
|
|
1258
|
-
writeText(value);
|
|
1259
|
-
return;
|
|
1083
|
+
function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: requestedChecks, repositoryRoot }: any): any {
|
|
1084
|
+
if (repositoryRoot) repoRoot = path.resolve(repositoryRoot);
|
|
1085
|
+
else {
|
|
1086
|
+
let cursor = path.resolve(taskDir);
|
|
1087
|
+
while (path.dirname(cursor) !== cursor && !fs.existsSync(path.join(cursor, ".agents"))) cursor = path.dirname(cursor);
|
|
1088
|
+
if (fs.existsSync(path.join(cursor, ".agents"))) repoRoot = cursor;
|
|
1260
1089
|
}
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
lines.push("");
|
|
1271
|
-
for (const check of value.checks) {
|
|
1272
|
-
lines.push(` [${formatStatusLabel(check.status)}] ${check.type} - ${check.message}`);
|
|
1090
|
+
const verifyConfig = loadVerificationConfig(repoRoot, skillName);
|
|
1091
|
+
const shared = { ...sharedUtils, repoRoot };
|
|
1092
|
+
if (mode === "gate") {
|
|
1093
|
+
const checks = [];
|
|
1094
|
+
for (const [type, checkConfig] of Object.entries(verifyConfig.checks || {})) {
|
|
1095
|
+
if (checkConfig === null) continue;
|
|
1096
|
+
const result = runCheck(type, { skillName, taskDir: path.resolve(taskDir), artifactFile, config: checkConfig }, shared);
|
|
1097
|
+
checks.push(result);
|
|
1098
|
+
if (result.status === "blocked") break;
|
|
1273
1099
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
} else {
|
|
1277
|
-
lines.push(`Check: ${value.status} | Skill: ${value.skill} | Type: ${value.type}`);
|
|
1278
|
-
lines.push("");
|
|
1279
|
-
lines.push(` [${formatStatusLabel(value.status)}] ${value.type} - ${value.message}`);
|
|
1280
|
-
lines.push("");
|
|
1281
|
-
lines.push(`Result: ${buildSingleCheckSummary(value.status)} - ${buildCheckAction(value)}`);
|
|
1100
|
+
const gate = summarizeGate(checks);
|
|
1101
|
+
return { gate, skill: skillName, checks, summary: summarizeChecks(checks), action: buildAction(gate, checks) };
|
|
1282
1102
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
function printUsageAndExit() {
|
|
1292
|
-
failUsage(
|
|
1293
|
-
"Usage:\n" +
|
|
1294
|
-
" node .agents/scripts/validate-artifact.js gate <skill-name> <task-dir> [artifact-file] [--format json|text]\n" +
|
|
1295
|
-
" node .agents/scripts/validate-artifact.js check <type> <task-dir> [artifact-file] --skill <skill-name> [--format json|text]"
|
|
1296
|
-
);
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
function failUsage(message) {
|
|
1300
|
-
process.stderr.write(`${message}\n`);
|
|
1301
|
-
process.exit(1);
|
|
1103
|
+
const type = requestedChecks[0];
|
|
1104
|
+
const config = (verifyConfig.checks || {})[type];
|
|
1105
|
+
if (config === undefined) return { skill: skillName, ...failResult(type, `Unknown check type '${type}' for skill '${skillName}'.`) };
|
|
1106
|
+
if (config === null) return { skill: skillName, ...passResult(type, `Check '${type}' is disabled for skill '${skillName}'.`) };
|
|
1107
|
+
return { skill: skillName, ...runCheck(type, { skillName, taskDir: path.resolve(taskDir), artifactFile, config }, shared) };
|
|
1302
1108
|
}
|
|
1303
1109
|
|
|
1304
|
-
|
|
1110
|
+
export { verifyInProcess };
|