@fitlab-ai/agent-infra 0.9.12 → 0.9.14
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 +11 -11
- package/README.zh-CN.md +11 -11
- package/bin/cli.ts +26 -9
- package/dist/bin/cli.js +173 -157
- package/dist/bin/internal-cli.js +66 -40
- package/dist/lib/agent-client.js +5 -0
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/internal/agent-client.js +5 -2
- package/dist/lib/internal/cli-route-inventory.js +152 -0
- package/dist/lib/internal/codex-lifecycle.js +9 -6
- package/dist/lib/internal/codex-sandbox-controller.js +8 -2
- package/dist/lib/internal/git-workflow.js +10 -6
- package/dist/lib/internal/platform-checks.js +19 -7
- package/dist/lib/internal/platform-comment.js +25 -13
- package/dist/lib/internal/platform-context.js +6 -3
- package/dist/lib/internal/platform-issue.js +20 -13
- package/dist/lib/internal/platform-metadata.js +88 -0
- package/dist/lib/internal/platform-pr-review.js +26 -13
- package/dist/lib/internal/platform-pr.js +84 -31
- package/dist/lib/internal/platform-release-notes.js +9 -6
- package/dist/lib/internal/platform-security.js +93 -0
- package/dist/lib/internal/pr-review-grade.js +25 -12
- package/dist/lib/internal/release-workflow.js +23 -9
- package/dist/lib/internal/sandbox-control.js +50 -7
- package/dist/lib/internal/task-activity.js +16 -7
- package/dist/lib/internal/task-artifact.js +235 -24
- package/dist/lib/internal/task-context.js +4 -1
- package/dist/lib/internal/task-create.js +50 -17
- package/dist/lib/internal/task-delivery.js +4 -1
- package/dist/lib/internal/task-event.js +10 -4
- package/dist/lib/internal/task-finalization.js +6 -3
- package/dist/lib/internal/task-invalidation.js +63 -0
- package/dist/lib/internal/task-ledger.js +20 -6
- package/dist/lib/internal/task-lifecycle.js +6 -3
- package/dist/lib/internal/task-operation-registry.js +421 -0
- package/dist/lib/internal/task-orchestration.js +12 -0
- package/dist/lib/internal/task-override.js +14 -7
- package/dist/lib/internal/task-qualification.js +90 -0
- package/dist/lib/internal/task-review.js +7 -4
- package/dist/lib/internal/task-short-id.js +11 -1
- package/dist/lib/internal/task-snapshot.js +4 -1
- package/dist/lib/internal/task-validate.js +7 -2
- package/dist/lib/internal/task-verify.js +11 -7
- package/dist/lib/internal/task-warning.js +11 -4
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/comment-safety.js +226 -0
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +1 -1
- package/dist/lib/platform/github-provider.js +1130 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +237 -72
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +545 -0
- package/dist/lib/platform/pr-checks.js +119 -104
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +541 -50
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +553 -0
- package/dist/lib/platform/provider-contract.js +99 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +577 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +734 -543
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/repository-metadata.js +377 -0
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/security-alerts.js +155 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +11 -12
- package/dist/lib/platform/verification-sync.js +129 -248
- package/dist/lib/pr-review/evidence-grading.js +19 -6
- package/dist/lib/process-data/index.js +7 -0
- package/dist/lib/run/index.js +68 -23
- package/dist/lib/sandbox/commands/control-status.js +22 -0
- package/dist/lib/sandbox/commands/create.js +32 -6
- package/dist/lib/sandbox/commands/ls.js +5 -0
- package/dist/lib/sandbox/commands/rm.js +827 -120
- package/dist/lib/sandbox/commands/show.js +16 -0
- package/dist/lib/sandbox/control/client.js +174 -19
- package/dist/lib/sandbox/control/completed-reentry.js +85 -0
- package/dist/lib/sandbox/control/container-identity.js +28 -31
- package/dist/lib/sandbox/control/executor.js +151 -16
- package/dist/lib/sandbox/control/lifecycle.js +621 -127
- package/dist/lib/sandbox/control/native-file-lock.js +177 -0
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +677 -34
- package/dist/lib/sandbox/control/state.js +209 -4
- package/dist/lib/sandbox/control/task-view.js +183 -0
- package/dist/lib/sandbox/engines/authority.js +289 -0
- package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
- package/dist/lib/sandbox/index.js +5 -0
- package/dist/lib/sandbox/recovery.js +21 -1
- package/dist/lib/sandbox/workspace-view.js +2 -1
- package/dist/lib/sandbox/worktree-safety.js +4 -1
- package/dist/lib/server/index.js +8 -0
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +75 -18
- package/dist/lib/task/artifact-operations.js +520 -0
- package/dist/lib/task/artifact-repair-intent.js +59 -0
- package/dist/lib/task/artifact-schema.js +135 -0
- package/dist/lib/task/capabilities.js +310 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-operation.js +12 -1
- package/dist/lib/task/control-authority.js +12 -5
- package/dist/lib/task/create-service.js +111 -12
- package/dist/lib/task/create.js +25 -0
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +1 -1
- package/dist/lib/task/events.js +416 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +58 -36
- package/dist/lib/task/index.js +11 -3
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/local-artifact-finalization.js +299 -0
- package/dist/lib/task/orchestration.js +240 -144
- package/dist/lib/task/pr-delivery-fact.js +102 -101
- package/dist/lib/task/qualification-audit.js +490 -0
- package/dist/lib/task/qualification-confirmation.js +109 -0
- package/dist/lib/task/qualification-intents.js +148 -0
- package/dist/lib/task/review-artifacts.js +22 -23
- package/dist/lib/task/review-finalization.js +121 -4
- package/dist/lib/task/review-fingerprint.js +24 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/sections.js +6 -4
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-config.js +11 -0
- package/dist/lib/task/verification-engine.js +93 -16
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +9 -0
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/init.ts +1 -1
- package/lib/internal/agent-client.ts +4 -2
- package/lib/internal/cli-route-inventory.ts +140 -0
- package/lib/internal/codex-lifecycle.ts +8 -6
- package/lib/internal/codex-sandbox-controller.ts +7 -2
- package/lib/internal/git-workflow.ts +9 -6
- package/lib/internal/platform-checks.ts +14 -6
- package/lib/internal/platform-comment.ts +20 -12
- package/lib/internal/platform-context.ts +5 -3
- package/lib/internal/platform-issue.ts +17 -11
- package/lib/internal/platform-metadata.ts +70 -0
- package/lib/internal/platform-pr-review.ts +23 -12
- package/lib/internal/platform-pr.ts +65 -27
- package/lib/internal/platform-release-notes.ts +8 -6
- package/lib/internal/platform-security.ts +83 -0
- package/lib/internal/pr-review-grade.ts +20 -11
- package/lib/internal/release-workflow.ts +21 -9
- package/lib/internal/sandbox-control.ts +44 -6
- package/lib/internal/task-activity.ts +15 -7
- package/lib/internal/task-artifact.ts +204 -22
- package/lib/internal/task-context.ts +3 -1
- package/lib/internal/task-create.ts +78 -17
- package/lib/internal/task-delivery.ts +3 -1
- package/lib/internal/task-event.ts +9 -4
- package/lib/internal/task-finalization.ts +6 -4
- package/lib/internal/task-invalidation.ts +41 -0
- package/lib/internal/task-ledger.ts +21 -8
- package/lib/internal/task-lifecycle.ts +5 -3
- package/lib/internal/task-operation-registry.ts +482 -0
- package/lib/internal/task-orchestration.ts +12 -0
- package/lib/internal/task-override.ts +15 -9
- package/lib/internal/task-qualification.ts +61 -0
- package/lib/internal/task-review.ts +6 -4
- package/lib/internal/task-short-id.ts +10 -1
- package/lib/internal/task-snapshot.ts +3 -1
- package/lib/internal/task-validate.ts +6 -2
- package/lib/internal/task-verify.ts +10 -7
- package/lib/internal/task-warning.ts +10 -4
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/comment-safety.ts +250 -0
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +2 -2
- package/lib/platform/github-provider.ts +1132 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +255 -80
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +639 -0
- package/lib/platform/pr-checks.ts +126 -106
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +581 -53
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +611 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +611 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +781 -550
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/repository-metadata.ts +453 -0
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/security-alerts.ts +191 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +11 -13
- package/lib/platform/verification-sync.ts +128 -304
- package/lib/pr-review/evidence-grading.ts +25 -8
- package/lib/process-data/index.ts +7 -0
- package/lib/run/index.ts +62 -22
- package/lib/sandbox/commands/control-status.ts +26 -0
- package/lib/sandbox/commands/create.ts +46 -6
- package/lib/sandbox/commands/ls.ts +7 -0
- package/lib/sandbox/commands/rm.ts +1000 -87
- package/lib/sandbox/commands/show.ts +16 -0
- package/lib/sandbox/control/client.ts +203 -19
- package/lib/sandbox/control/completed-reentry.ts +98 -0
- package/lib/sandbox/control/container-identity.ts +27 -43
- package/lib/sandbox/control/executor.ts +180 -15
- package/lib/sandbox/control/lifecycle.ts +629 -17
- package/lib/sandbox/control/native-file-lock.ts +209 -0
- package/lib/sandbox/control/protocol.ts +118 -2
- package/lib/sandbox/control/server.ts +724 -34
- package/lib/sandbox/control/state.ts +257 -4
- package/lib/sandbox/control/task-view.ts +240 -0
- package/lib/sandbox/engines/authority.ts +365 -0
- package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
- package/lib/sandbox/index.ts +7 -0
- package/lib/sandbox/recovery.ts +21 -1
- package/lib/sandbox/workspace-view.ts +9 -3
- package/lib/sandbox/worktree-safety.ts +4 -1
- package/lib/server/index.ts +10 -0
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +70 -22
- package/lib/task/artifact-operations.ts +626 -0
- package/lib/task/artifact-repair-intent.ts +71 -0
- package/lib/task/artifact-schema.ts +192 -0
- package/lib/task/capabilities.ts +331 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-operation.ts +12 -1
- package/lib/task/control-authority.ts +22 -9
- package/lib/task/create-service.ts +139 -14
- package/lib/task/create.ts +27 -0
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +6 -2
- package/lib/task/events.ts +392 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +70 -46
- package/lib/task/index.ts +16 -3
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/local-artifact-finalization.ts +432 -0
- package/lib/task/orchestration.ts +223 -133
- package/lib/task/pr-delivery-fact.ts +131 -148
- package/lib/task/qualification-audit.ts +599 -0
- package/lib/task/qualification-confirmation.ts +117 -0
- package/lib/task/qualification-intents.ts +150 -0
- package/lib/task/review-artifacts.ts +21 -22
- package/lib/task/review-finalization.ts +133 -4
- package/lib/task/review-fingerprint.ts +23 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/sections.ts +6 -4
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-config.ts +12 -0
- package/lib/task/verification-engine.ts +103 -17
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +1 -1
- package/lib/version.ts +15 -1
- package/package.json +12 -1
- package/templates/.agents/rules/README.en.md +3 -1
- package/templates/.agents/rules/README.zh-CN.md +3 -1
- package/templates/.agents/rules/decision-qualification.en.md +22 -0
- package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
- package/templates/.agents/rules/evidence-reporting.en.md +28 -0
- package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
- package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
- package/templates/.agents/rules/local-artifact-repair.en.md +13 -2
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -2
- package/templates/.agents/rules/pr-sync.en.md +10 -4
- package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
- package/templates/.agents/rules/security-alerts.en.md +8 -8
- package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +28 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +29 -5
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +41 -9
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +43 -11
- package/templates/.agents/skills/code-task/config/verify.en.json +6 -14
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +6 -14
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +10 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +10 -4
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +26 -21
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +25 -21
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
- 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/init-labels/SKILL.en.md +8 -8
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/plan-task/SKILL.en.md +27 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +26 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
- package/templates/.agents/skills/review-analysis/SKILL.en.md +17 -4
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +17 -4
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-code/SKILL.en.md +18 -5
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +18 -5
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
- package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-plan/SKILL.en.md +17 -4
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +17 -4
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +9 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +9 -3
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
- package/templates/.agents/skills/test/SKILL.en.md +13 -2
- package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +24 -1
- package/templates/.agents/templates/task.zh-CN.md +24 -1
- package/templates/.github/workflows/pr-label.yml +7 -26
- package/templates/.agents/scripts/security-alerts.github.sh +0 -151
- package/templates/.agents/scripts/security-alerts.sh +0 -31
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
|
@@ -3,13 +3,29 @@ import path from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
import { parseTaskFrontmatter } from '../task/frontmatter.ts';
|
|
5
5
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
6
|
-
import {
|
|
7
|
-
import type {
|
|
8
|
-
import { resolvePlatformContext } from './context.ts';
|
|
6
|
+
import { resolvePlatformProviderContext } from './context.ts';
|
|
7
|
+
import type { PlatformClient } from './context.ts';
|
|
9
8
|
import { platformResult } from './types.ts';
|
|
10
9
|
import type { PlatformOperation, PlatformResult } from './types.ts';
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
import {
|
|
11
|
+
providerError,
|
|
12
|
+
providerOperationContext,
|
|
13
|
+
providerStatus,
|
|
14
|
+
providerResourceToken,
|
|
15
|
+
unsupportedProviderOperation
|
|
16
|
+
} from './provider-bridge.ts';
|
|
17
|
+
import { resourceIdentityNumber } from './resource-identity.ts';
|
|
18
|
+
import { taskIssueIdentity, taskIssueIdentityError } from './task-identities.ts';
|
|
19
|
+
import {
|
|
20
|
+
CONTROL_MARKER_PATTERN,
|
|
21
|
+
escapeHtmlText,
|
|
22
|
+
fenceRanges,
|
|
23
|
+
renderSafeCodeFence,
|
|
24
|
+
sanitizeMarkdownDocument
|
|
25
|
+
} from './comment-safety.ts';
|
|
26
|
+
import type { FenceRange } from './comment-safety.ts';
|
|
27
|
+
|
|
28
|
+
type RemoteComment = { id: number | string; body: string; user?: { login?: string } };
|
|
13
29
|
type RenderedChunk = { marker: string; body: string; content: string; part: number; total: number };
|
|
14
30
|
type CommentKind = 'task' | 'artifact' | 'summary' | 'cancel';
|
|
15
31
|
type SyncOptions = {
|
|
@@ -19,9 +35,14 @@ type SyncOptions = {
|
|
|
19
35
|
body?: string;
|
|
20
36
|
cwd?: string;
|
|
21
37
|
backfill?: boolean;
|
|
22
|
-
client?:
|
|
38
|
+
client?: PlatformClient;
|
|
39
|
+
runtimeVersion?: string;
|
|
23
40
|
};
|
|
24
41
|
|
|
42
|
+
function providerCommentId(id: string, provider: { identity?: { comment?: string } }): number | string {
|
|
43
|
+
return provider.identity?.comment === 'number' && /^\d+$/.test(id) ? Number(id) : id;
|
|
44
|
+
}
|
|
45
|
+
|
|
25
46
|
const MARKERS = {
|
|
26
47
|
task: (taskId: string) => `<!-- sync-issue:${taskId}:task -->`,
|
|
27
48
|
artifact: (taskId: string, stem: string) => `<!-- sync-issue:${taskId}:${stem} -->`,
|
|
@@ -53,15 +74,22 @@ function splitFrontmatter(content: string): { frontmatter: string | null; body:
|
|
|
53
74
|
return { frontmatter: match[1]!, body: content.slice(match[0].length).replace(/^\r?\n/, '') };
|
|
54
75
|
}
|
|
55
76
|
|
|
77
|
+
function sanitizeCommentBody(value: string, label: string): string {
|
|
78
|
+
const result = sanitizeMarkdownDocument(value, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
|
|
79
|
+
if (!result.ok) throw new Error(`${result.error.code}: ${label}: ${result.error.message} at offset ${result.error.offset}`);
|
|
80
|
+
return result.value;
|
|
81
|
+
}
|
|
82
|
+
|
|
56
83
|
function footer(agent: string, taskId: string): string {
|
|
57
84
|
return `---\n*由 ${agent} 自动生成 · 内部追踪:${taskId}*`;
|
|
58
85
|
}
|
|
59
86
|
|
|
60
87
|
function renderTaskComment(content: string, taskId: string, agent: string): string {
|
|
61
88
|
const split = splitFrontmatter(content);
|
|
89
|
+
const safeBody = sanitizeCommentBody(split.body, 'task body');
|
|
62
90
|
const taskBody = split.frontmatter === null
|
|
63
|
-
?
|
|
64
|
-
: `<details><summary>元数据 (frontmatter)</summary>\n\n
|
|
91
|
+
? safeBody
|
|
92
|
+
: `<details><summary>元数据 (frontmatter)</summary>\n\n${renderSafeCodeFence(`---\n${split.frontmatter}\n---`, 'yaml')}\n\n</details>\n\n${safeBody}`;
|
|
65
93
|
return normalizeCommentContent([
|
|
66
94
|
MARKERS.task(taskId),
|
|
67
95
|
'## 任务文件',
|
|
@@ -83,13 +111,16 @@ function artifactIdentity(artifact: string): { stem: string; title: string } {
|
|
|
83
111
|
return { stem, title: `${base}(Round ${round})` };
|
|
84
112
|
}
|
|
85
113
|
|
|
86
|
-
|
|
114
|
+
type SourceChunk = { content: string; start: number; end: number };
|
|
115
|
+
|
|
116
|
+
function chunkByUtf8(content: string, maxBytes: number): SourceChunk[] {
|
|
87
117
|
if (maxBytes <= 0) throw new Error('comment byte limit is too small');
|
|
88
|
-
const chunks:
|
|
89
|
-
let
|
|
90
|
-
while (
|
|
118
|
+
const chunks: SourceChunk[] = [];
|
|
119
|
+
let start = 0;
|
|
120
|
+
while (start < content.length) {
|
|
121
|
+
const remaining = content.slice(start);
|
|
91
122
|
if (Buffer.byteLength(remaining, 'utf8') <= maxBytes) {
|
|
92
|
-
chunks.push(remaining);
|
|
123
|
+
chunks.push({ content: remaining, start, end: content.length });
|
|
93
124
|
break;
|
|
94
125
|
}
|
|
95
126
|
let bytes = 0;
|
|
@@ -104,10 +135,102 @@ function chunkByUtf8(content: string, maxBytes: number): string[] {
|
|
|
104
135
|
}
|
|
105
136
|
const cut = newlineIndex > 0 ? newlineIndex : index;
|
|
106
137
|
if (cut === 0) throw new Error('comment byte limit cannot fit one Unicode code point');
|
|
107
|
-
chunks.push(remaining.slice(0, cut));
|
|
108
|
-
|
|
138
|
+
chunks.push({ content: remaining.slice(0, cut), start, end: start + cut });
|
|
139
|
+
start += cut;
|
|
140
|
+
}
|
|
141
|
+
return chunks.length > 0 ? chunks : [{ content: '', start: 0, end: 0 }];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function longestFenceLineRun(content: string, character: '`' | '~'): number {
|
|
145
|
+
const pattern = new RegExp(`^ {0,3}(${character}+)[ \\t]*(?:\\n|$)`, 'gm');
|
|
146
|
+
let longest = 0;
|
|
147
|
+
for (const match of content.matchAll(pattern)) longest = Math.max(longest, match[1]!.length);
|
|
148
|
+
return longest;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function boundedFence(content: string, sourceCharacter: '`' | '~', maxBytes: number): { opening: string; closing: string } | null {
|
|
152
|
+
const candidates: Array<'`' | '~'> = sourceCharacter === '`' ? ['~', '`'] : ['`', '~'];
|
|
153
|
+
for (const character of candidates) {
|
|
154
|
+
const length = Math.max(3, longestFenceLineRun(content, character) + 1);
|
|
155
|
+
const delimiter = character.repeat(length);
|
|
156
|
+
if (Buffer.byteLength(delimiter, 'utf8') * 2 + 2 <= maxBytes) return { opening: `${delimiter}\n`, closing: `${delimiter}\n` };
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function sourceSlice(piece: SourceChunk, start: number, end: number): string {
|
|
162
|
+
return piece.content.slice(Math.max(0, start - piece.start), Math.max(0, end - piece.start));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function renderOversizedFenceChunk(piece: SourceChunk, fences: readonly FenceRange[], maxBytes: number): string {
|
|
166
|
+
const insertions = new Map<number, Array<{ value: string; ensureLineStart: boolean }>>();
|
|
167
|
+
const omitted: Array<{ start: number; end: number }> = [];
|
|
168
|
+
const insert = (position: number, value: string, ensureLineStart = false) => {
|
|
169
|
+
const values = insertions.get(position) || [];
|
|
170
|
+
values.push({ value, ensureLineStart });
|
|
171
|
+
insertions.set(position, values);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
for (const fence of fences) {
|
|
175
|
+
if (piece.end <= fence.start || piece.start >= fence.end) continue;
|
|
176
|
+
const oversized = Buffer.byteLength(fence.opening, 'utf8') > maxBytes
|
|
177
|
+
|| Buffer.byteLength(fence.closing, 'utf8') > maxBytes;
|
|
178
|
+
if (!oversized) {
|
|
179
|
+
if (piece.start > fence.start && piece.start < fence.end) insert(piece.start, fence.opening);
|
|
180
|
+
if (piece.end > fence.start && piece.end < fence.end) insert(piece.end, fence.closing, true);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const code = sourceSlice(piece, Math.max(piece.start, fence.openingEnd), Math.min(piece.end, fence.closingStart));
|
|
185
|
+
const delimiter = boundedFence(code, fence.character, maxBytes);
|
|
186
|
+
if (!delimiter) return escapeHtmlText(piece.content);
|
|
187
|
+
insert(Math.max(piece.start, fence.start), delimiter.opening);
|
|
188
|
+
insert(Math.min(piece.end, fence.end), delimiter.closing, true);
|
|
189
|
+
const openingStart = Math.max(piece.start, fence.start);
|
|
190
|
+
const openingEnd = Math.min(piece.end, fence.openingEnd);
|
|
191
|
+
if (openingStart < openingEnd) omitted.push({ start: openingStart, end: openingEnd });
|
|
192
|
+
const closingStart = Math.max(piece.start, fence.closingStart);
|
|
193
|
+
const closingEnd = Math.min(piece.end, fence.end);
|
|
194
|
+
if (closingStart < closingEnd) omitted.push({ start: closingStart, end: closingEnd });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const boundaries = new Set<number>([piece.start, piece.end]);
|
|
198
|
+
for (const position of insertions.keys()) boundaries.add(position);
|
|
199
|
+
for (const range of omitted) {
|
|
200
|
+
boundaries.add(range.start);
|
|
201
|
+
boundaries.add(range.end);
|
|
109
202
|
}
|
|
110
|
-
|
|
203
|
+
const points = [...boundaries].sort((left, right) => left - right);
|
|
204
|
+
let content = '';
|
|
205
|
+
for (let index = 0; index < points.length; index += 1) {
|
|
206
|
+
const point = points[index]!;
|
|
207
|
+
for (const insertion of insertions.get(point) || []) {
|
|
208
|
+
if (insertion.ensureLineStart && content.length > 0 && !content.endsWith('\n')) content += '\n';
|
|
209
|
+
content += insertion.value;
|
|
210
|
+
}
|
|
211
|
+
const next = points[index + 1];
|
|
212
|
+
if (next === undefined) continue;
|
|
213
|
+
const skipped = omitted.some((range) => range.start <= point && next <= range.end);
|
|
214
|
+
if (!skipped) content += sourceSlice(piece, point, next);
|
|
215
|
+
}
|
|
216
|
+
return content;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function independentChunkContent(piece: SourceChunk, fences: readonly FenceRange[], maxBytes: number): string {
|
|
220
|
+
const oversizedFence = fences.some((fence) =>
|
|
221
|
+
piece.end > fence.start && piece.start < fence.end
|
|
222
|
+
&& (Buffer.byteLength(fence.opening, 'utf8') > maxBytes || Buffer.byteLength(fence.closing, 'utf8') > maxBytes)
|
|
223
|
+
);
|
|
224
|
+
if (oversizedFence) return renderOversizedFenceChunk(piece, fences, maxBytes);
|
|
225
|
+
const openingFence = fences.find((fence) => piece.start > fence.start && piece.start < fence.end);
|
|
226
|
+
const closingFence = fences.find((fence) => piece.end > fence.start && piece.end < fence.end);
|
|
227
|
+
let content = piece.content;
|
|
228
|
+
if (openingFence) content = openingFence.opening + content;
|
|
229
|
+
if (closingFence) {
|
|
230
|
+
if (!content.endsWith('\n')) content += '\n';
|
|
231
|
+
content += closingFence.closing;
|
|
232
|
+
}
|
|
233
|
+
return content;
|
|
111
234
|
}
|
|
112
235
|
|
|
113
236
|
function buildArtifactChunk(
|
|
@@ -119,7 +242,8 @@ function buildArtifactChunk(
|
|
|
119
242
|
part: number,
|
|
120
243
|
total: number,
|
|
121
244
|
chunked: boolean,
|
|
122
|
-
backfill: boolean
|
|
245
|
+
backfill: boolean,
|
|
246
|
+
renderedContent = content
|
|
123
247
|
): RenderedChunk {
|
|
124
248
|
const marker = chunked ? MARKERS.artifactChunk(taskId, stem, part, total) : MARKERS.artifact(taskId, stem);
|
|
125
249
|
const heading = chunked ? `## ${title}(${part}/${total})` : `## ${title}`;
|
|
@@ -131,7 +255,7 @@ function buildArtifactChunk(
|
|
|
131
255
|
'',
|
|
132
256
|
`> **${agent}** · ${taskId}`,
|
|
133
257
|
'',
|
|
134
|
-
|
|
258
|
+
renderedContent,
|
|
135
259
|
'',
|
|
136
260
|
footer(agent, taskId)
|
|
137
261
|
].join('\n'));
|
|
@@ -148,29 +272,44 @@ function chunkArtifactComment(input: {
|
|
|
148
272
|
}): RenderedChunk[] {
|
|
149
273
|
const byteLimit = input.byteLimit || 60_000;
|
|
150
274
|
const identity = artifactIdentity(input.artifact);
|
|
275
|
+
const safeBody = sanitizeCommentBody(input.body, 'artifact body');
|
|
151
276
|
const single = buildArtifactChunk(
|
|
152
|
-
input.taskId, identity.stem, identity.title, input.agent,
|
|
277
|
+
input.taskId, identity.stem, identity.title, input.agent, safeBody, 1, 1, false, Boolean(input.backfill)
|
|
153
278
|
);
|
|
154
279
|
if (Buffer.byteLength(single.body, 'utf8') <= byteLimit) return [single];
|
|
155
280
|
|
|
156
281
|
let total = 2;
|
|
157
|
-
let
|
|
282
|
+
let payloadLimit = Number.POSITIVE_INFINITY;
|
|
283
|
+
const ranges = fenceRanges(safeBody);
|
|
284
|
+
if (!ranges.ok) throw new Error(`${ranges.error.code}: artifact body: ${ranges.error.message} at offset ${ranges.error.offset}`);
|
|
158
285
|
for (;;) {
|
|
159
286
|
const probe = buildArtifactChunk(
|
|
160
287
|
input.taskId, identity.stem, identity.title, input.agent, '', total, total, true, Boolean(input.backfill)
|
|
161
288
|
);
|
|
162
289
|
const available = byteLimit - Buffer.byteLength(probe.body, 'utf8');
|
|
163
|
-
|
|
164
|
-
if (
|
|
165
|
-
|
|
290
|
+
const sourceLimit = Math.min(available, payloadLimit);
|
|
291
|
+
if (sourceLimit <= 0) throw new Error('comment byte limit is too small for an artifact chunk');
|
|
292
|
+
const pieces = chunkByUtf8(safeBody, sourceLimit);
|
|
293
|
+
if (pieces.length !== total) {
|
|
294
|
+
total = pieces.length;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const chunks = pieces.map((piece, index) => buildArtifactChunk(
|
|
298
|
+
input.taskId,
|
|
299
|
+
identity.stem,
|
|
300
|
+
identity.title,
|
|
301
|
+
input.agent,
|
|
302
|
+
piece.content,
|
|
303
|
+
index + 1,
|
|
304
|
+
total,
|
|
305
|
+
true,
|
|
306
|
+
Boolean(input.backfill),
|
|
307
|
+
independentChunkContent(piece, ranges.value, sourceLimit)
|
|
308
|
+
));
|
|
309
|
+
const overflow = Math.max(...chunks.map((chunk) => Buffer.byteLength(chunk.body, 'utf8') - byteLimit));
|
|
310
|
+
if (overflow <= 0) return chunks;
|
|
311
|
+
payloadLimit = sourceLimit - overflow;
|
|
166
312
|
}
|
|
167
|
-
return pieces.map((content, index) => {
|
|
168
|
-
const chunk = buildArtifactChunk(
|
|
169
|
-
input.taskId, identity.stem, identity.title, input.agent, content, index + 1, total, true, Boolean(input.backfill)
|
|
170
|
-
);
|
|
171
|
-
if (Buffer.byteLength(chunk.body, 'utf8') > byteLimit) throw new Error('rendered comment exceeds byte limit');
|
|
172
|
-
return chunk;
|
|
173
|
-
});
|
|
174
313
|
}
|
|
175
314
|
|
|
176
315
|
function findMarkerComments(comments: RemoteComment[], marker: string): RemoteComment[] {
|
|
@@ -181,7 +320,7 @@ function flattenComments(value: unknown): RemoteComment[] {
|
|
|
181
320
|
if (!Array.isArray(value)) return [];
|
|
182
321
|
const flattened = value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]);
|
|
183
322
|
return flattened.filter((entry): entry is RemoteComment =>
|
|
184
|
-
Boolean(entry && typeof entry === 'object' && typeof entry.id === 'number' && typeof entry.body === 'string')
|
|
323
|
+
Boolean(entry && typeof entry === 'object' && (typeof entry.id === 'number' || typeof entry.id === 'string') && typeof entry.body === 'string')
|
|
185
324
|
);
|
|
186
325
|
}
|
|
187
326
|
|
|
@@ -191,7 +330,7 @@ function issueNumberFromTask(content: string): number | null {
|
|
|
191
330
|
return Number.isInteger(number) && number > 0 ? number : null;
|
|
192
331
|
}
|
|
193
332
|
|
|
194
|
-
function listRemoteComments(client:
|
|
333
|
+
function listRemoteComments(client: PlatformClient, repo: string, issue: number, cwd: string) {
|
|
195
334
|
const result = client.json<unknown>([
|
|
196
335
|
'api', '--paginate', '--slurp', `repos/${repo}/issues/${issue}/comments?per_page=100`
|
|
197
336
|
], { cwd });
|
|
@@ -211,7 +350,7 @@ function hasResolvedPlatformContext(context: PlatformResult): boolean {
|
|
|
211
350
|
|
|
212
351
|
function bodyEnvelope(marker: string, title: string, taskId: string, agent: string, body: string): string {
|
|
213
352
|
return normalizeCommentContent([
|
|
214
|
-
marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', body.replace(/\n+$/, ''), '', footer(agent, taskId)
|
|
353
|
+
marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', sanitizeCommentBody(body, `${title} body`).replace(/\n+$/, ''), '', footer(agent, taskId)
|
|
215
354
|
].join('\n'));
|
|
216
355
|
}
|
|
217
356
|
|
|
@@ -291,7 +430,7 @@ function validateRelatedMarkerSet(comments: RemoteComment[], prefix: string): {
|
|
|
291
430
|
}
|
|
292
431
|
|
|
293
432
|
function writeComment(
|
|
294
|
-
client:
|
|
433
|
+
client: PlatformClient,
|
|
295
434
|
repo: string,
|
|
296
435
|
issue: number,
|
|
297
436
|
cwd: string,
|
|
@@ -305,7 +444,7 @@ function writeComment(
|
|
|
305
444
|
);
|
|
306
445
|
}
|
|
307
446
|
|
|
308
|
-
function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformResult {
|
|
447
|
+
async function syncPlatformComment(taskRef: string, options: SyncOptions): Promise<PlatformResult> {
|
|
309
448
|
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
310
449
|
if (!resolved.ok) {
|
|
311
450
|
return platformResult('failed', {
|
|
@@ -313,32 +452,45 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
|
|
|
313
452
|
});
|
|
314
453
|
}
|
|
315
454
|
const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
316
|
-
|
|
317
|
-
|
|
455
|
+
let issueIdentityFromTask: ReturnType<typeof taskIssueIdentity>;
|
|
456
|
+
try { issueIdentityFromTask = taskIssueIdentity(parseTaskFrontmatter(taskContent), undefined, options.runtimeVersion); }
|
|
457
|
+
catch (error) { return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } }); }
|
|
458
|
+
if (!issueIdentityFromTask) {
|
|
318
459
|
return platformResult('no-op', {
|
|
319
460
|
error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false }
|
|
320
461
|
});
|
|
321
462
|
}
|
|
322
|
-
const context = resolvePlatformContext({ cwd: resolved.repoRoot, client: options.client });
|
|
323
|
-
if (!hasResolvedPlatformContext(context)) return context;
|
|
324
|
-
const client = options.client || createGitHubClient();
|
|
325
|
-
const listed = listRemoteComments(client, context.platform.repository!, issue, resolved.repoRoot);
|
|
326
|
-
if (!listed.ok) {
|
|
327
|
-
return platformResult(listed.error.retryable ? 'blocked' : 'failed', {
|
|
328
|
-
...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
463
|
let desired: RenderedChunk[];
|
|
333
464
|
try {
|
|
334
465
|
desired = expectedComments(resolved.taskId, taskContent, resolved.taskDir, options);
|
|
335
466
|
} catch (error) {
|
|
336
467
|
return platformResult('failed', {
|
|
337
|
-
|
|
338
|
-
resource: { kind: 'issue', number: issue },
|
|
468
|
+
resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentityFromTask) },
|
|
339
469
|
error: { code: 'COMMENT_PAYLOAD_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false }
|
|
340
470
|
});
|
|
341
471
|
}
|
|
472
|
+
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
473
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
474
|
+
if (!hasResolvedPlatformContext(context) || !loaded.ok) return context;
|
|
475
|
+
const issue = resourceIdentityNumber(issueIdentityFromTask);
|
|
476
|
+
const listed = loaded.value.provider.comments?.list
|
|
477
|
+
? await loaded.value.provider.comments.list({
|
|
478
|
+
context: providerOperationContext(loaded.value),
|
|
479
|
+
parent: issueIdentityFromTask
|
|
480
|
+
}).then((response) => response.ok
|
|
481
|
+
? { ok: true as const, value: response.value.map((comment) => ({
|
|
482
|
+
id: providerCommentId(comment.id, loaded.value.provider),
|
|
483
|
+
body: comment.body,
|
|
484
|
+
user: comment.author?.name ? { login: comment.author.name } : undefined
|
|
485
|
+
})) }
|
|
486
|
+
: response)
|
|
487
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.list');
|
|
488
|
+
if (!listed.ok) {
|
|
489
|
+
return platformResult(listed.error.retryable ? 'blocked' : 'failed', {
|
|
490
|
+
...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
342
494
|
const existing = relatedComments(listed.value, markerPrefix(resolved.taskId, options));
|
|
343
495
|
if (!validateRelatedMarkerSet(existing, markerPrefix(resolved.taskId, options)).ok) {
|
|
344
496
|
return platformResult('failed', {
|
|
@@ -370,9 +522,8 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
|
|
|
370
522
|
});
|
|
371
523
|
}
|
|
372
524
|
|
|
373
|
-
const repo = context.platform.repository!;
|
|
374
525
|
const operations: PlatformOperation[] = [];
|
|
375
|
-
const ids: number
|
|
526
|
+
const ids: Array<number | string> = [];
|
|
376
527
|
for (const item of desired) {
|
|
377
528
|
const current = existing.find((comment) => normalizeCommentContent(comment.body).split('\n', 1)[0] === item.marker);
|
|
378
529
|
if (current && normalizeCommentContent(current.body) === normalizeCommentContent(item.body)) {
|
|
@@ -380,16 +531,18 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
|
|
|
380
531
|
operations.push({ name: `comment:${item.marker}`, status: 'no-op', reasonCode: null });
|
|
381
532
|
continue;
|
|
382
533
|
}
|
|
383
|
-
const written =
|
|
534
|
+
const written = loaded.value.provider.comments?.write
|
|
535
|
+
? await loaded.value.provider.comments.write({
|
|
536
|
+
context: providerOperationContext(loaded.value),
|
|
537
|
+
parent: issueIdentityFromTask,
|
|
538
|
+
body: item.body,
|
|
539
|
+
...(current ? { existingComment: { kind: 'id' as const, value: String(current.id) } } : {}),
|
|
540
|
+
mutation: { idempotencyKey: `comment:${resolved.taskId}:${item.marker}` }
|
|
541
|
+
})
|
|
542
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.write');
|
|
384
543
|
if (!written.ok) {
|
|
385
544
|
if (!current && written.error.retryable) {
|
|
386
|
-
|
|
387
|
-
const found = reconciled.ok ? findMarkerComments(reconciled.value, item.marker) : [];
|
|
388
|
-
if (found.length === 1) {
|
|
389
|
-
ids.push(found[0]!.id);
|
|
390
|
-
operations.push({ name: `comment:${item.marker}`, status: 'applied', reasonCode: 'CREATE_RECONCILED' });
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
545
|
+
// A provider owns reconciliation because only it knows how to address the resource.
|
|
393
546
|
}
|
|
394
547
|
return platformResult(written.error.retryable ? 'blocked' : 'failed', {
|
|
395
548
|
...contextFields(context),
|
|
@@ -398,16 +551,21 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
|
|
|
398
551
|
error: written.error
|
|
399
552
|
});
|
|
400
553
|
}
|
|
401
|
-
ids.push(
|
|
554
|
+
ids.push(providerCommentId((written.value as { remoteId: string }).remoteId, loaded.value.provider));
|
|
402
555
|
operations.push({ name: `comment:${item.marker}`, status: 'applied', reasonCode: null });
|
|
403
556
|
}
|
|
404
557
|
|
|
405
558
|
const desiredMarkers = new Set(desired.map((item) => item.marker));
|
|
406
559
|
const stale = existing.filter((comment) => !desiredMarkers.has(normalizeCommentContent(comment.body).split('\n', 1)[0]!));
|
|
407
560
|
for (const comment of stale) {
|
|
408
|
-
const deleted =
|
|
409
|
-
|
|
410
|
-
|
|
561
|
+
const deleted = loaded.value.provider.comments?.delete
|
|
562
|
+
? await loaded.value.provider.comments.delete({
|
|
563
|
+
context: providerOperationContext(loaded.value),
|
|
564
|
+
parent: issueIdentityFromTask,
|
|
565
|
+
comment: { kind: 'id', value: String(comment.id) },
|
|
566
|
+
mutation: { idempotencyKey: `comment-delete:${resolved.taskId}:${comment.id}` }
|
|
567
|
+
})
|
|
568
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.delete');
|
|
411
569
|
if (!deleted.ok) {
|
|
412
570
|
return platformResult(deleted.error.retryable ? 'blocked' : 'failed', {
|
|
413
571
|
...contextFields(context),
|
|
@@ -429,28 +587,45 @@ function syncPlatformComment(taskRef: string, options: SyncOptions): PlatformRes
|
|
|
429
587
|
});
|
|
430
588
|
}
|
|
431
589
|
|
|
432
|
-
function listPlatformComments(issue: number, cwd = process.cwd(), client
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
590
|
+
async function listPlatformComments(issue: string | number, cwd = process.cwd(), client?: PlatformClient): Promise<PlatformResult & { comments?: RemoteComment[] }> {
|
|
591
|
+
const loaded = await resolvePlatformProviderContext({ cwd, client });
|
|
592
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
593
|
+
if (!hasResolvedPlatformContext(context) || !loaded.ok) return context;
|
|
594
|
+
let identity;
|
|
595
|
+
try { identity = loaded.ok ? providerResourceToken(loaded.value.provider, 'issue', String(issue)) : null; }
|
|
596
|
+
catch (error) {
|
|
597
|
+
return platformResult('failed', { ...contextFields(context), error: { code: 'PLATFORM_IDENTITY_TOKEN_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false } });
|
|
598
|
+
}
|
|
599
|
+
if (!loaded.ok || !identity) return context;
|
|
600
|
+
const listed = loaded.value.provider.comments?.list
|
|
601
|
+
? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: identity }).then((response) => response.ok
|
|
602
|
+
? { ok: true as const, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
|
|
603
|
+
: response)
|
|
604
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.list');
|
|
605
|
+
if (!listed.ok) {
|
|
606
|
+
return platformResult(providerStatus(listed.error), {
|
|
607
|
+
...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(identity), identity }, error: providerError(listed.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
|
|
439
608
|
});
|
|
440
609
|
}
|
|
441
|
-
return { ...platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number:
|
|
610
|
+
return { ...platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(identity), identity }, error: null }), comments: listed.value };
|
|
442
611
|
}
|
|
443
612
|
|
|
444
|
-
function checkPlatformCommentOwner(taskRef: string, options: { cwd?: string; client?:
|
|
613
|
+
async function checkPlatformCommentOwner(taskRef: string, options: { cwd?: string; client?: PlatformClient; runtimeVersion?: string } = {}): Promise<PlatformResult> {
|
|
445
614
|
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
446
615
|
if (!resolved.ok) return platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } });
|
|
447
616
|
const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
617
|
+
let issueIdentity: ReturnType<typeof taskIssueIdentity>;
|
|
618
|
+
try { issueIdentity = taskIssueIdentity(parseTaskFrontmatter(content), undefined, options.runtimeVersion); }
|
|
619
|
+
catch (error) { return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } }); }
|
|
620
|
+
if (!issueIdentity) return platformResult('no-op', { error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid platform issue identity', retryable: false } });
|
|
621
|
+
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
622
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
623
|
+
if (!hasResolvedPlatformContext(context) || !loaded.ok) return context;
|
|
624
|
+
const listed = loaded.value.provider.comments?.list
|
|
625
|
+
? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: issueIdentity }).then((response) => response.ok
|
|
626
|
+
? { ok: true as const, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
|
|
627
|
+
: response)
|
|
628
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.list');
|
|
454
629
|
if (!listed.ok) return platformResult(listed.error.retryable ? 'blocked' : 'failed', { ...contextFields(context), error: listed.error });
|
|
455
630
|
const matches = findMarkerComments(listed.value, MARKERS.task(resolved.taskId));
|
|
456
631
|
if (matches.length > 1) return platformResult('failed', { ...contextFields(context), error: { code: 'COMMENT_MARKER_CONFLICT', message: 'Multiple task comments use the registered marker', retryable: false } });
|
|
@@ -458,11 +633,11 @@ function checkPlatformCommentOwner(taskRef: string, options: { cwd?: string; cli
|
|
|
458
633
|
if (owner && owner !== context.platform.currentUser && !context.capabilities.triage) {
|
|
459
634
|
return platformResult('blocked', {
|
|
460
635
|
...contextFields(context),
|
|
461
|
-
resource: { kind: 'issue', number:
|
|
636
|
+
resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity },
|
|
462
637
|
error: { code: 'COMMENT_OWNER_CONFLICT', message: `Task comment is owned by '${owner}'`, retryable: false }
|
|
463
638
|
});
|
|
464
639
|
}
|
|
465
|
-
return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number:
|
|
640
|
+
return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity }, error: null });
|
|
466
641
|
}
|
|
467
642
|
|
|
468
643
|
export {
|