@fitlab-ai/agent-infra 0.9.13 → 0.9.15
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 +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/cli.ts +26 -9
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/cli.js +173 -157
- package/dist/bin/internal-cli.js +126 -42
- package/dist/lib/agent-client.js +5 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +37 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/agent-client.js +5 -2
- package/dist/lib/internal/cli-route-inventory.js +154 -0
- package/dist/lib/internal/codex-lifecycle.js +15 -12
- package/dist/lib/internal/codex-sandbox-controller.js +8 -2
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/git-workflow.js +10 -6
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +322 -0
- package/dist/lib/internal/platform-checks.js +16 -4
- package/dist/lib/internal/platform-comment.js +16 -4
- package/dist/lib/internal/platform-context.js +4 -1
- package/dist/lib/internal/platform-issue.js +12 -4
- package/dist/lib/internal/platform-metadata.js +88 -0
- package/dist/lib/internal/platform-pr-review.js +16 -3
- package/dist/lib/internal/platform-pr.js +42 -14
- package/dist/lib/internal/platform-release-notes.js +6 -3
- package/dist/lib/internal/platform-security.js +93 -0
- package/dist/lib/internal/pr-review-grade.js +14 -3
- package/dist/lib/internal/release-workflow.js +82 -89
- package/dist/lib/internal/sandbox-control.js +171 -7
- package/dist/lib/internal/task-activity.js +13 -4
- package/dist/lib/internal/task-artifact.js +19 -107
- package/dist/lib/internal/task-context.js +4 -1
- package/dist/lib/internal/task-create.js +48 -14
- package/dist/lib/internal/task-delivery.js +4 -1
- package/dist/lib/internal/task-event.js +38 -35
- package/dist/lib/internal/task-finalization.js +4 -1
- package/dist/lib/internal/task-invalidation.js +4 -1
- package/dist/lib/internal/task-ledger.js +12 -1
- package/dist/lib/internal/task-lifecycle.js +4 -1
- package/dist/lib/internal/task-operation-registry.js +519 -0
- package/dist/lib/internal/task-orchestration.js +12 -0
- package/dist/lib/internal/task-override.js +10 -3
- package/dist/lib/internal/task-qualification.js +90 -0
- package/dist/lib/internal/task-review.js +10 -52
- 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 +37 -12
- package/dist/lib/internal/task-verify.js +5 -1
- package/dist/lib/internal/task-warning.js +8 -1
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +229 -0
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +248 -26
- package/dist/lib/platform/issue-comments.js +146 -34
- package/dist/lib/platform/issues.js +1 -179
- package/dist/lib/platform/pr-change-report.js +6 -12
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +83 -10
- package/dist/lib/platform/provider-contract.d.ts +67 -1
- package/dist/lib/platform/provider-contract.js +6 -2
- package/dist/lib/platform/provider-validation.js +76 -3
- package/dist/lib/platform/pull-requests.js +117 -275
- package/dist/lib/platform/repository-metadata.js +377 -0
- package/dist/lib/platform/security-alerts.js +155 -0
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/verification-sync.js +154 -162
- package/dist/lib/process-data/index.js +7 -0
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/run/index.js +5 -0
- package/dist/lib/sandbox/commands/control-status.js +22 -0
- package/dist/lib/sandbox/commands/create.js +39 -8
- package/dist/lib/sandbox/commands/ls.js +5 -0
- package/dist/lib/sandbox/commands/rm.js +3 -807
- package/dist/lib/sandbox/commands/show.js +16 -0
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +125 -12
- 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 +233 -34
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +656 -128
- package/dist/lib/sandbox/control/native-file-lock.js +85 -0
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +720 -79
- package/dist/lib/sandbox/control/state.js +63 -3
- package/dist/lib/sandbox/control/task-view.js +183 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +99 -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 +7 -2
- package/dist/lib/sandbox/recovery.js +49 -5
- package/dist/lib/sandbox/removal.js +1717 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-view.js +15 -15
- package/dist/lib/sandbox/worktree-safety.js +4 -1
- package/dist/lib/server/index.js +8 -0
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +101 -0
- package/dist/lib/task/artifact-lifecycle.js +71 -40
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +474 -0
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-repair-intent.js +130 -0
- package/dist/lib/task/artifact-schema.js +127 -0
- package/dist/lib/task/capabilities.js +83 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +2 -16
- package/dist/lib/task/control-authority.js +375 -5
- package/dist/lib/task/control-recovery.js +128 -0
- package/dist/lib/task/create-service.js +103 -4
- package/dist/lib/task/create.js +80 -2
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/events.js +306 -76
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +3 -6
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/index.js +11 -3
- package/dist/lib/task/intermediate-cleanup.js +383 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle.js +19 -1
- package/dist/lib/task/local-artifact-finalization.js +190 -290
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +101 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +206 -69
- package/dist/lib/task/qualification-audit.js +476 -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 +23 -40
- package/dist/lib/task/review-command.js +25 -0
- package/dist/lib/task/review-finalization.js +146 -97
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -54
- package/dist/lib/task/verification-config.js +11 -0
- package/dist/lib/task/verification-engine.js +170 -327
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +17 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +9 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
- package/lib/fs/durable-write.ts +29 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/agent-client.ts +4 -2
- package/lib/internal/cli-route-inventory.ts +142 -0
- package/lib/internal/codex-lifecycle.ts +14 -12
- package/lib/internal/codex-sandbox-controller.ts +7 -2
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/git-workflow.ts +9 -6
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +306 -0
- package/lib/internal/platform-checks.ts +12 -4
- package/lib/internal/platform-comment.ts +12 -4
- package/lib/internal/platform-context.ts +3 -1
- package/lib/internal/platform-issue.ts +11 -4
- package/lib/internal/platform-metadata.ts +70 -0
- package/lib/internal/platform-pr-review.ts +14 -3
- package/lib/internal/platform-pr.ts +38 -14
- package/lib/internal/platform-release-notes.ts +5 -3
- package/lib/internal/platform-security.ts +83 -0
- package/lib/internal/pr-review-grade.ts +10 -3
- package/lib/internal/release-workflow.ts +91 -87
- package/lib/internal/sandbox-control.ts +181 -6
- package/lib/internal/task-activity.ts +12 -4
- package/lib/internal/task-artifact.ts +15 -79
- package/lib/internal/task-context.ts +3 -1
- package/lib/internal/task-create.ts +76 -15
- package/lib/internal/task-delivery.ts +3 -1
- package/lib/internal/task-event.ts +21 -10
- package/lib/internal/task-finalization.ts +3 -1
- package/lib/internal/task-invalidation.ts +3 -1
- package/lib/internal/task-ledger.ts +11 -1
- package/lib/internal/task-lifecycle.ts +3 -1
- package/lib/internal/task-operation-registry.ts +590 -0
- package/lib/internal/task-orchestration.ts +12 -0
- package/lib/internal/task-override.ts +9 -3
- package/lib/internal/task-qualification.ts +61 -0
- package/lib/internal/task-review.ts +7 -50
- package/lib/internal/task-short-id.ts +10 -1
- package/lib/internal/task-snapshot.ts +3 -1
- package/lib/internal/task-validate.ts +32 -12
- package/lib/internal/task-verify.ts +4 -1
- package/lib/internal/task-warning.ts +7 -1
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +255 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +244 -27
- package/lib/platform/issue-comments.ts +159 -35
- package/lib/platform/issues.ts +4 -245
- package/lib/platform/pr-change-report.ts +6 -12
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +88 -11
- package/lib/platform/provider-contract.ts +78 -3
- package/lib/platform/provider-validation.ts +85 -3
- package/lib/platform/pull-requests.ts +129 -348
- package/lib/platform/repository-metadata.ts +453 -0
- package/lib/platform/security-alerts.ts +191 -0
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/verification-sync.ts +171 -177
- package/lib/process-data/index.ts +7 -0
- package/lib/release/workflow.ts +3 -3
- package/lib/run/index.ts +5 -0
- package/lib/sandbox/commands/control-status.ts +26 -0
- package/lib/sandbox/commands/create.ts +53 -12
- package/lib/sandbox/commands/ls.ts +7 -0
- package/lib/sandbox/commands/rm.ts +3 -996
- package/lib/sandbox/commands/show.ts +16 -0
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +150 -13
- package/lib/sandbox/control/completed-reentry.ts +98 -0
- package/lib/sandbox/control/container-identity.ts +27 -43
- package/lib/sandbox/control/executor.ts +262 -37
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +672 -18
- package/lib/sandbox/control/native-file-lock.ts +108 -0
- package/lib/sandbox/control/protocol.ts +80 -8
- package/lib/sandbox/control/server.ts +779 -82
- package/lib/sandbox/control/state.ts +84 -3
- package/lib/sandbox/control/task-view.ts +240 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +102 -0
- package/lib/sandbox/engines/authority.ts +365 -0
- package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
- package/lib/sandbox/index.ts +9 -2
- package/lib/sandbox/recovery.ts +49 -6
- package/lib/sandbox/removal.ts +2144 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-view.ts +26 -18
- package/lib/sandbox/worktree-safety.ts +4 -1
- package/lib/server/index.ts +10 -0
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +104 -0
- package/lib/task/artifact-lifecycle.ts +66 -39
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +580 -0
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-repair-intent.ts +152 -0
- package/lib/task/artifact-schema.ts +163 -0
- package/lib/task/capabilities.ts +82 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +2 -16
- package/lib/task/control-authority.ts +513 -6
- package/lib/task/control-recovery.ts +174 -0
- package/lib/task/create-service.ts +131 -6
- package/lib/task/create.ts +83 -1
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/events.ts +292 -75
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +3 -8
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/index.ts +16 -3
- package/lib/task/intermediate-cleanup.ts +455 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle.ts +17 -1
- package/lib/task/local-artifact-finalization.ts +223 -388
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +152 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +191 -68
- package/lib/task/qualification-audit.ts +588 -0
- package/lib/task/qualification-confirmation.ts +117 -0
- package/lib/task/qualification-intents.ts +150 -0
- package/lib/task/review-artifacts.ts +21 -41
- package/lib/task/review-command.ts +23 -0
- package/lib/task/review-finalization.ts +154 -153
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -52
- package/lib/task/verification-config.ts +12 -0
- package/lib/task/verification-engine.ts +165 -370
- package/lib/task/verification-types.ts +17 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +18 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/package.json +7 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- 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/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 +5 -1
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
- 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/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
- 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/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 +14 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -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/complete-manual-validation/SKILL.en.md +37 -21
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
- 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 +14 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
- 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/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
- 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 +14 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
- 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 +14 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
- 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 +25 -4
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +29 -7
- package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
- package/templates/.agents/templates/task.en.md +6 -0
- package/templates/.agents/templates/task.zh-CN.md +6 -0
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
- 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
|
@@ -16,6 +16,13 @@ import {
|
|
|
16
16
|
} from './provider-bridge.ts';
|
|
17
17
|
import { resourceIdentityNumber } from './resource-identity.ts';
|
|
18
18
|
import { taskIssueIdentity, taskIssueIdentityError } from './task-identities.ts';
|
|
19
|
+
import {
|
|
20
|
+
canonicalizeCommentBody,
|
|
21
|
+
escapeHtmlText,
|
|
22
|
+
fenceRanges,
|
|
23
|
+
renderSafeCodeFence
|
|
24
|
+
} from './comment-safety.ts';
|
|
25
|
+
import type { FenceRange } from './comment-safety.ts';
|
|
19
26
|
|
|
20
27
|
type RemoteComment = { id: number | string; body: string; user?: { login?: string } };
|
|
21
28
|
type RenderedChunk = { marker: string; body: string; content: string; part: number; total: number };
|
|
@@ -66,15 +73,22 @@ function splitFrontmatter(content: string): { frontmatter: string | null; body:
|
|
|
66
73
|
return { frontmatter: match[1]!, body: content.slice(match[0].length).replace(/^\r?\n/, '') };
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
function sanitizeCommentBody(value: string, label: string): string {
|
|
77
|
+
const result = canonicalizeCommentBody(value);
|
|
78
|
+
if (!result.ok) throw new Error(`${result.error.code}: ${label}: ${result.error.message} at offset ${result.error.offset}`);
|
|
79
|
+
return result.value;
|
|
80
|
+
}
|
|
81
|
+
|
|
69
82
|
function footer(agent: string, taskId: string): string {
|
|
70
83
|
return `---\n*由 ${agent} 自动生成 · 内部追踪:${taskId}*`;
|
|
71
84
|
}
|
|
72
85
|
|
|
73
86
|
function renderTaskComment(content: string, taskId: string, agent: string): string {
|
|
74
87
|
const split = splitFrontmatter(content);
|
|
88
|
+
const safeBody = sanitizeCommentBody(split.body, 'task body');
|
|
75
89
|
const taskBody = split.frontmatter === null
|
|
76
|
-
?
|
|
77
|
-
: `<details><summary>元数据 (frontmatter)</summary>\n\n
|
|
90
|
+
? safeBody
|
|
91
|
+
: `<details><summary>元数据 (frontmatter)</summary>\n\n${renderSafeCodeFence(`---\n${split.frontmatter}\n---`, 'yaml')}\n\n</details>\n\n${safeBody}`;
|
|
78
92
|
return normalizeCommentContent([
|
|
79
93
|
MARKERS.task(taskId),
|
|
80
94
|
'## 任务文件',
|
|
@@ -96,13 +110,16 @@ function artifactIdentity(artifact: string): { stem: string; title: string } {
|
|
|
96
110
|
return { stem, title: `${base}(Round ${round})` };
|
|
97
111
|
}
|
|
98
112
|
|
|
99
|
-
|
|
113
|
+
type SourceChunk = { content: string; start: number; end: number };
|
|
114
|
+
|
|
115
|
+
function chunkByUtf8(content: string, maxBytes: number): SourceChunk[] {
|
|
100
116
|
if (maxBytes <= 0) throw new Error('comment byte limit is too small');
|
|
101
|
-
const chunks:
|
|
102
|
-
let
|
|
103
|
-
while (
|
|
117
|
+
const chunks: SourceChunk[] = [];
|
|
118
|
+
let start = 0;
|
|
119
|
+
while (start < content.length) {
|
|
120
|
+
const remaining = content.slice(start);
|
|
104
121
|
if (Buffer.byteLength(remaining, 'utf8') <= maxBytes) {
|
|
105
|
-
chunks.push(remaining);
|
|
122
|
+
chunks.push({ content: remaining, start, end: content.length });
|
|
106
123
|
break;
|
|
107
124
|
}
|
|
108
125
|
let bytes = 0;
|
|
@@ -117,10 +134,102 @@ function chunkByUtf8(content: string, maxBytes: number): string[] {
|
|
|
117
134
|
}
|
|
118
135
|
const cut = newlineIndex > 0 ? newlineIndex : index;
|
|
119
136
|
if (cut === 0) throw new Error('comment byte limit cannot fit one Unicode code point');
|
|
120
|
-
chunks.push(remaining.slice(0, cut));
|
|
121
|
-
|
|
137
|
+
chunks.push({ content: remaining.slice(0, cut), start, end: start + cut });
|
|
138
|
+
start += cut;
|
|
122
139
|
}
|
|
123
|
-
return chunks.length > 0 ? chunks : [''];
|
|
140
|
+
return chunks.length > 0 ? chunks : [{ content: '', start: 0, end: 0 }];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function longestFenceLineRun(content: string, character: '`' | '~'): number {
|
|
144
|
+
const pattern = new RegExp(`^ {0,3}(${character}+)[ \\t]*(?:\\n|$)`, 'gm');
|
|
145
|
+
let longest = 0;
|
|
146
|
+
for (const match of content.matchAll(pattern)) longest = Math.max(longest, match[1]!.length);
|
|
147
|
+
return longest;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function boundedFence(content: string, sourceCharacter: '`' | '~', maxBytes: number): { opening: string; closing: string } | null {
|
|
151
|
+
const candidates: Array<'`' | '~'> = sourceCharacter === '`' ? ['~', '`'] : ['`', '~'];
|
|
152
|
+
for (const character of candidates) {
|
|
153
|
+
const length = Math.max(3, longestFenceLineRun(content, character) + 1);
|
|
154
|
+
const delimiter = character.repeat(length);
|
|
155
|
+
if (Buffer.byteLength(delimiter, 'utf8') * 2 + 2 <= maxBytes) return { opening: `${delimiter}\n`, closing: `${delimiter}\n` };
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function sourceSlice(piece: SourceChunk, start: number, end: number): string {
|
|
161
|
+
return piece.content.slice(Math.max(0, start - piece.start), Math.max(0, end - piece.start));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function renderOversizedFenceChunk(piece: SourceChunk, fences: readonly FenceRange[], maxBytes: number): string {
|
|
165
|
+
const insertions = new Map<number, Array<{ value: string; ensureLineStart: boolean }>>();
|
|
166
|
+
const omitted: Array<{ start: number; end: number }> = [];
|
|
167
|
+
const insert = (position: number, value: string, ensureLineStart = false) => {
|
|
168
|
+
const values = insertions.get(position) || [];
|
|
169
|
+
values.push({ value, ensureLineStart });
|
|
170
|
+
insertions.set(position, values);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
for (const fence of fences) {
|
|
174
|
+
if (piece.end <= fence.start || piece.start >= fence.end) continue;
|
|
175
|
+
const oversized = Buffer.byteLength(fence.opening, 'utf8') > maxBytes
|
|
176
|
+
|| Buffer.byteLength(fence.closing, 'utf8') > maxBytes;
|
|
177
|
+
if (!oversized) {
|
|
178
|
+
if (piece.start > fence.start && piece.start < fence.end) insert(piece.start, fence.opening);
|
|
179
|
+
if (piece.end > fence.start && piece.end < fence.end) insert(piece.end, fence.closing, true);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const code = sourceSlice(piece, Math.max(piece.start, fence.openingEnd), Math.min(piece.end, fence.closingStart));
|
|
184
|
+
const delimiter = boundedFence(code, fence.character, maxBytes);
|
|
185
|
+
if (!delimiter) return escapeHtmlText(piece.content);
|
|
186
|
+
insert(Math.max(piece.start, fence.start), delimiter.opening);
|
|
187
|
+
insert(Math.min(piece.end, fence.end), delimiter.closing, true);
|
|
188
|
+
const openingStart = Math.max(piece.start, fence.start);
|
|
189
|
+
const openingEnd = Math.min(piece.end, fence.openingEnd);
|
|
190
|
+
if (openingStart < openingEnd) omitted.push({ start: openingStart, end: openingEnd });
|
|
191
|
+
const closingStart = Math.max(piece.start, fence.closingStart);
|
|
192
|
+
const closingEnd = Math.min(piece.end, fence.end);
|
|
193
|
+
if (closingStart < closingEnd) omitted.push({ start: closingStart, end: closingEnd });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const boundaries = new Set<number>([piece.start, piece.end]);
|
|
197
|
+
for (const position of insertions.keys()) boundaries.add(position);
|
|
198
|
+
for (const range of omitted) {
|
|
199
|
+
boundaries.add(range.start);
|
|
200
|
+
boundaries.add(range.end);
|
|
201
|
+
}
|
|
202
|
+
const points = [...boundaries].sort((left, right) => left - right);
|
|
203
|
+
let content = '';
|
|
204
|
+
for (let index = 0; index < points.length; index += 1) {
|
|
205
|
+
const point = points[index]!;
|
|
206
|
+
for (const insertion of insertions.get(point) || []) {
|
|
207
|
+
if (insertion.ensureLineStart && content.length > 0 && !content.endsWith('\n')) content += '\n';
|
|
208
|
+
content += insertion.value;
|
|
209
|
+
}
|
|
210
|
+
const next = points[index + 1];
|
|
211
|
+
if (next === undefined) continue;
|
|
212
|
+
const skipped = omitted.some((range) => range.start <= point && next <= range.end);
|
|
213
|
+
if (!skipped) content += sourceSlice(piece, point, next);
|
|
214
|
+
}
|
|
215
|
+
return content;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function independentChunkContent(piece: SourceChunk, fences: readonly FenceRange[], maxBytes: number): string {
|
|
219
|
+
const oversizedFence = fences.some((fence) =>
|
|
220
|
+
piece.end > fence.start && piece.start < fence.end
|
|
221
|
+
&& (Buffer.byteLength(fence.opening, 'utf8') > maxBytes || Buffer.byteLength(fence.closing, 'utf8') > maxBytes)
|
|
222
|
+
);
|
|
223
|
+
if (oversizedFence) return renderOversizedFenceChunk(piece, fences, maxBytes);
|
|
224
|
+
const openingFence = fences.find((fence) => piece.start > fence.start && piece.start < fence.end);
|
|
225
|
+
const closingFence = fences.find((fence) => piece.end > fence.start && piece.end < fence.end);
|
|
226
|
+
let content = piece.content;
|
|
227
|
+
if (openingFence) content = openingFence.opening + content;
|
|
228
|
+
if (closingFence) {
|
|
229
|
+
if (!content.endsWith('\n')) content += '\n';
|
|
230
|
+
content += closingFence.closing;
|
|
231
|
+
}
|
|
232
|
+
return content;
|
|
124
233
|
}
|
|
125
234
|
|
|
126
235
|
function buildArtifactChunk(
|
|
@@ -132,7 +241,8 @@ function buildArtifactChunk(
|
|
|
132
241
|
part: number,
|
|
133
242
|
total: number,
|
|
134
243
|
chunked: boolean,
|
|
135
|
-
backfill: boolean
|
|
244
|
+
backfill: boolean,
|
|
245
|
+
renderedContent = content
|
|
136
246
|
): RenderedChunk {
|
|
137
247
|
const marker = chunked ? MARKERS.artifactChunk(taskId, stem, part, total) : MARKERS.artifact(taskId, stem);
|
|
138
248
|
const heading = chunked ? `## ${title}(${part}/${total})` : `## ${title}`;
|
|
@@ -144,7 +254,7 @@ function buildArtifactChunk(
|
|
|
144
254
|
'',
|
|
145
255
|
`> **${agent}** · ${taskId}`,
|
|
146
256
|
'',
|
|
147
|
-
|
|
257
|
+
renderedContent,
|
|
148
258
|
'',
|
|
149
259
|
footer(agent, taskId)
|
|
150
260
|
].join('\n'));
|
|
@@ -161,29 +271,44 @@ function chunkArtifactComment(input: {
|
|
|
161
271
|
}): RenderedChunk[] {
|
|
162
272
|
const byteLimit = input.byteLimit || 60_000;
|
|
163
273
|
const identity = artifactIdentity(input.artifact);
|
|
274
|
+
const safeBody = sanitizeCommentBody(input.body, 'artifact body');
|
|
164
275
|
const single = buildArtifactChunk(
|
|
165
|
-
input.taskId, identity.stem, identity.title, input.agent,
|
|
276
|
+
input.taskId, identity.stem, identity.title, input.agent, safeBody, 1, 1, false, Boolean(input.backfill)
|
|
166
277
|
);
|
|
167
278
|
if (Buffer.byteLength(single.body, 'utf8') <= byteLimit) return [single];
|
|
168
279
|
|
|
169
280
|
let total = 2;
|
|
170
|
-
let
|
|
281
|
+
let payloadLimit = Number.POSITIVE_INFINITY;
|
|
282
|
+
const ranges = fenceRanges(safeBody);
|
|
283
|
+
if (!ranges.ok) throw new Error(`${ranges.error.code}: artifact body: ${ranges.error.message} at offset ${ranges.error.offset}`);
|
|
171
284
|
for (;;) {
|
|
172
285
|
const probe = buildArtifactChunk(
|
|
173
286
|
input.taskId, identity.stem, identity.title, input.agent, '', total, total, true, Boolean(input.backfill)
|
|
174
287
|
);
|
|
175
288
|
const available = byteLimit - Buffer.byteLength(probe.body, 'utf8');
|
|
176
|
-
|
|
177
|
-
if (
|
|
178
|
-
|
|
289
|
+
const sourceLimit = Math.min(available, payloadLimit);
|
|
290
|
+
if (sourceLimit <= 0) throw new Error('comment byte limit is too small for an artifact chunk');
|
|
291
|
+
const pieces = chunkByUtf8(safeBody, sourceLimit);
|
|
292
|
+
if (pieces.length !== total) {
|
|
293
|
+
total = pieces.length;
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const chunks = pieces.map((piece, index) => buildArtifactChunk(
|
|
297
|
+
input.taskId,
|
|
298
|
+
identity.stem,
|
|
299
|
+
identity.title,
|
|
300
|
+
input.agent,
|
|
301
|
+
piece.content,
|
|
302
|
+
index + 1,
|
|
303
|
+
total,
|
|
304
|
+
true,
|
|
305
|
+
Boolean(input.backfill),
|
|
306
|
+
independentChunkContent(piece, ranges.value, sourceLimit)
|
|
307
|
+
));
|
|
308
|
+
const overflow = Math.max(...chunks.map((chunk) => Buffer.byteLength(chunk.body, 'utf8') - byteLimit));
|
|
309
|
+
if (overflow <= 0) return chunks;
|
|
310
|
+
payloadLimit = sourceLimit - overflow;
|
|
179
311
|
}
|
|
180
|
-
return pieces.map((content, index) => {
|
|
181
|
-
const chunk = buildArtifactChunk(
|
|
182
|
-
input.taskId, identity.stem, identity.title, input.agent, content, index + 1, total, true, Boolean(input.backfill)
|
|
183
|
-
);
|
|
184
|
-
if (Buffer.byteLength(chunk.body, 'utf8') > byteLimit) throw new Error('rendered comment exceeds byte limit');
|
|
185
|
-
return chunk;
|
|
186
|
-
});
|
|
187
312
|
}
|
|
188
313
|
|
|
189
314
|
function findMarkerComments(comments: RemoteComment[], marker: string): RemoteComment[] {
|
|
@@ -224,7 +349,7 @@ function hasResolvedPlatformContext(context: PlatformResult): boolean {
|
|
|
224
349
|
|
|
225
350
|
function bodyEnvelope(marker: string, title: string, taskId: string, agent: string, body: string): string {
|
|
226
351
|
return normalizeCommentContent([
|
|
227
|
-
marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', body.replace(/\n+$/, ''), '', footer(agent, taskId)
|
|
352
|
+
marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', sanitizeCommentBody(body, `${title} body`).replace(/\n+$/, ''), '', footer(agent, taskId)
|
|
228
353
|
].join('\n'));
|
|
229
354
|
}
|
|
230
355
|
|
|
@@ -334,6 +459,15 @@ async function syncPlatformComment(taskRef: string, options: SyncOptions): Promi
|
|
|
334
459
|
error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false }
|
|
335
460
|
});
|
|
336
461
|
}
|
|
462
|
+
let desired: RenderedChunk[];
|
|
463
|
+
try {
|
|
464
|
+
desired = expectedComments(resolved.taskId, taskContent, resolved.taskDir, options);
|
|
465
|
+
} catch (error) {
|
|
466
|
+
return platformResult('failed', {
|
|
467
|
+
resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentityFromTask) },
|
|
468
|
+
error: { code: 'COMMENT_PAYLOAD_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false }
|
|
469
|
+
});
|
|
470
|
+
}
|
|
337
471
|
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
338
472
|
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
339
473
|
if (!hasResolvedPlatformContext(context) || !loaded.ok) return context;
|
|
@@ -356,16 +490,6 @@ async function syncPlatformComment(taskRef: string, options: SyncOptions): Promi
|
|
|
356
490
|
});
|
|
357
491
|
}
|
|
358
492
|
|
|
359
|
-
let desired: RenderedChunk[];
|
|
360
|
-
try {
|
|
361
|
-
desired = expectedComments(resolved.taskId, taskContent, resolved.taskDir, options);
|
|
362
|
-
} catch (error) {
|
|
363
|
-
return platformResult('failed', {
|
|
364
|
-
...contextFields(context),
|
|
365
|
-
resource: { kind: 'issue', number: issue },
|
|
366
|
-
error: { code: 'COMMENT_PAYLOAD_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false }
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
493
|
const existing = relatedComments(listed.value, markerPrefix(resolved.taskId, options));
|
|
370
494
|
if (!validateRelatedMarkerSet(existing, markerPrefix(resolved.taskId, options)).ok) {
|
|
371
495
|
return platformResult('failed', {
|
package/lib/platform/issues.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { PlatformIssueSnapshot as IssueSnapshot } from './snapshots.ts';
|
|
2
|
+
import type { IssueFieldSchema } from './github-data.ts';
|
|
1
3
|
import fs from 'node:fs';
|
|
2
4
|
import path from 'node:path';
|
|
3
5
|
import { execFileSync } from 'node:child_process';
|
|
@@ -38,26 +40,7 @@ import {
|
|
|
38
40
|
import { resourceIdentityEquals, resourceIdentityNumber, resourceIdentityString, serializeResourceIdentity } from './resource-identity.ts';
|
|
39
41
|
import { taskIssueIdentity, taskIssueIdentityError } from './task-identities.ts';
|
|
40
42
|
import type { ResourceIdentity } from './resource-identity.ts';
|
|
41
|
-
|
|
42
|
-
type GitHubClient = PlatformClient;
|
|
43
43
|
import type { IssueSnapshot as ProviderIssueSnapshot, RepositoryMetadataSnapshot } from './provider-contract.ts';
|
|
44
|
-
|
|
45
|
-
type IssueSnapshot = {
|
|
46
|
-
repository: string;
|
|
47
|
-
number: number;
|
|
48
|
-
identity?: ResourceIdentity;
|
|
49
|
-
databaseId: number | null;
|
|
50
|
-
nodeId: string;
|
|
51
|
-
url: string;
|
|
52
|
-
state: 'open' | 'closed';
|
|
53
|
-
title: string;
|
|
54
|
-
body: string;
|
|
55
|
-
labels: string[];
|
|
56
|
-
assignees: string[];
|
|
57
|
-
milestone: string | null;
|
|
58
|
-
issueType: string | null;
|
|
59
|
-
fields: Record<string, string | number | null>;
|
|
60
|
-
};
|
|
61
44
|
type IssueResult = PlatformResult & {
|
|
62
45
|
task: { id: string | null; issueNumber: number | null };
|
|
63
46
|
issue: IssueSnapshot | null;
|
|
@@ -80,33 +63,6 @@ type SyncOptions = SharedOptions & {
|
|
|
80
63
|
dryRun?: boolean;
|
|
81
64
|
};
|
|
82
65
|
|
|
83
|
-
type RemoteIssue = {
|
|
84
|
-
number?: number;
|
|
85
|
-
id?: number;
|
|
86
|
-
node_id?: string;
|
|
87
|
-
html_url?: string;
|
|
88
|
-
state?: string;
|
|
89
|
-
title?: string;
|
|
90
|
-
body?: string | null;
|
|
91
|
-
labels?: Array<string | { name?: string }>;
|
|
92
|
-
assignees?: Array<{ login?: string }>;
|
|
93
|
-
milestone?: { title?: string } | null;
|
|
94
|
-
type?: { name?: string } | null;
|
|
95
|
-
pull_request?: unknown;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
type IssueFieldSchema = {
|
|
99
|
-
id: string;
|
|
100
|
-
name: string;
|
|
101
|
-
kind: 'single-select' | 'date' | 'text' | 'number';
|
|
102
|
-
options: Array<{ id: string; name: string }>;
|
|
103
|
-
};
|
|
104
|
-
type IssueTypeSchema = { id: string; name: string; fields: IssueFieldSchema[] };
|
|
105
|
-
type CurrentField = { id: string; name: string; kind: IssueFieldSchema['kind']; value: string | number | null };
|
|
106
|
-
|
|
107
|
-
const ISSUE_TYPES_QUERY = `query($owner:String!){organization(login:$owner){issueTypes(first:20){nodes{id name pinnedFields{__typename ... on IssueFieldSingleSelect{id name options{id name}} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}}}}}`;
|
|
108
|
-
const ISSUE_FIELDS_QUERY = `query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){id issueType{id name pinnedFields{__typename ... on IssueFieldSingleSelect{id name options{id name}} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}} issueFieldValues(first:50){nodes{__typename ... on IssueFieldSingleSelectValue{name optionId field{... on IssueFieldSingleSelect{id name}}} ... on IssueFieldDateValue{value field{... on IssueFieldDate{id name}}} ... on IssueFieldTextValue{value field{... on IssueFieldText{id name}}} ... on IssueFieldNumberValue{value field{... on IssueFieldNumber{id name}}}}}}}}`;
|
|
109
|
-
|
|
110
66
|
function result(
|
|
111
67
|
status: PlatformResult['status'],
|
|
112
68
|
taskId: string | null,
|
|
@@ -235,118 +191,6 @@ async function inspectExternalIssue(
|
|
|
235
191
|
});
|
|
236
192
|
}
|
|
237
193
|
|
|
238
|
-
function normalizeIssue(remote: RemoteIssue, repository: string, fallbackNumber?: number): IssueSnapshot | null {
|
|
239
|
-
const number = Number.isInteger(remote.number) && Number(remote.number) > 0
|
|
240
|
-
? Number(remote.number)
|
|
241
|
-
: fallbackNumber;
|
|
242
|
-
if (!number || !Number.isSafeInteger(number) || remote.pull_request) return null;
|
|
243
|
-
return {
|
|
244
|
-
repository,
|
|
245
|
-
number,
|
|
246
|
-
databaseId: Number.isInteger(remote.id) ? Number(remote.id) : null,
|
|
247
|
-
nodeId: remote.node_id || `issue-${number}`,
|
|
248
|
-
url: remote.html_url || `https://github.com/${repository}/issues/${number}`,
|
|
249
|
-
state: String(remote.state || '').toLowerCase() === 'closed' ? 'closed' : 'open',
|
|
250
|
-
title: remote.title || '',
|
|
251
|
-
body: remote.body || '',
|
|
252
|
-
labels: (remote.labels || []).map((label) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
|
|
253
|
-
assignees: (remote.assignees || []).map((assignee) => assignee.login || '').filter(Boolean).sort(),
|
|
254
|
-
milestone: remote.milestone?.title || null,
|
|
255
|
-
issueType: remote.type?.name || null,
|
|
256
|
-
fields: {}
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function fieldKind(value: { __typename?: string }): IssueFieldSchema['kind'] | null {
|
|
261
|
-
if (value.__typename === 'IssueFieldSingleSelect' || value.__typename === 'IssueFieldSingleSelectValue') return 'single-select';
|
|
262
|
-
if (value.__typename === 'IssueFieldDate' || value.__typename === 'IssueFieldDateValue') return 'date';
|
|
263
|
-
if (value.__typename === 'IssueFieldText' || value.__typename === 'IssueFieldTextValue') return 'text';
|
|
264
|
-
if (value.__typename === 'IssueFieldNumber' || value.__typename === 'IssueFieldNumberValue') return 'number';
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function normalizeFieldSchemas(value: unknown): IssueFieldSchema[] {
|
|
269
|
-
if (!Array.isArray(value)) return [];
|
|
270
|
-
return value.flatMap((raw) => {
|
|
271
|
-
const item = raw as { id?: string; name?: string; __typename?: string; options?: Array<{ id?: string; name?: string }> };
|
|
272
|
-
const kind = fieldKind(item);
|
|
273
|
-
return item.id && item.name && kind ? [{
|
|
274
|
-
id: item.id,
|
|
275
|
-
name: item.name,
|
|
276
|
-
kind,
|
|
277
|
-
options: (item.options || []).flatMap((option) => option.id && option.name ? [{ id: option.id, name: option.name }] : [])
|
|
278
|
-
}] : [];
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function normalizeIssueTypes(value: unknown): IssueTypeSchema[] {
|
|
283
|
-
const nodes = (value as { data?: { organization?: { issueTypes?: { nodes?: unknown[] } } } })?.data?.organization?.issueTypes?.nodes;
|
|
284
|
-
return (nodes || []).flatMap((raw) => {
|
|
285
|
-
const item = raw as { id?: string; name?: string; pinnedFields?: unknown[] };
|
|
286
|
-
return item.id && item.name ? [{ id: item.id, name: item.name, fields: normalizeFieldSchemas(item.pinnedFields) }] : [];
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function normalizeCurrentFields(value: unknown): { issueId: string | null; type: IssueTypeSchema | null; values: CurrentField[] } {
|
|
291
|
-
const issue = (value as { data?: { repository?: { issue?: {
|
|
292
|
-
id?: string;
|
|
293
|
-
issueType?: { id?: string; name?: string; pinnedFields?: unknown[] } | null;
|
|
294
|
-
issueFieldValues?: { nodes?: unknown[] };
|
|
295
|
-
} } } })?.data?.repository?.issue;
|
|
296
|
-
const type = issue?.issueType?.name ? {
|
|
297
|
-
id: issue.issueType.id || issue.issueType.name,
|
|
298
|
-
name: issue.issueType.name,
|
|
299
|
-
fields: normalizeFieldSchemas(issue.issueType.pinnedFields)
|
|
300
|
-
} : null;
|
|
301
|
-
const values = (issue?.issueFieldValues?.nodes || []).flatMap((raw) => {
|
|
302
|
-
const item = raw as { __typename?: string; name?: string; value?: string | number; field?: { id?: string; name?: string } };
|
|
303
|
-
const kind = fieldKind(item);
|
|
304
|
-
const value = kind === 'single-select' ? item.name : item.value;
|
|
305
|
-
return item.field?.name && kind && value !== undefined
|
|
306
|
-
? [{ id: item.field.id || item.field.name, name: item.field.name, kind, value: value ?? null }]
|
|
307
|
-
: [];
|
|
308
|
-
});
|
|
309
|
-
return { issueId: issue?.id || null, type, values };
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function graphState(client: GitHubClient, repository: string, issue: number, cwd: string) {
|
|
313
|
-
const [owner, name] = repository.split('/');
|
|
314
|
-
if (!owner || !name) return null;
|
|
315
|
-
const current = client.json<unknown>([
|
|
316
|
-
'api', 'graphql', '-f', `query=${ISSUE_FIELDS_QUERY}`, '-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `number=${issue}`
|
|
317
|
-
], { cwd });
|
|
318
|
-
if (!current.ok) return null;
|
|
319
|
-
return normalizeCurrentFields(current.value);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function fetchIssue(client: GitHubClient, repository: string, issue: number, cwd: string) {
|
|
323
|
-
return client.json<RemoteIssue>(['api', `repos/${repository}/issues/${issue}`], { cwd });
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function inspectGitHubIssue(client: GitHubClient, repository: string, issue: number, cwd: string) {
|
|
327
|
-
const fetched = fetchIssue(client, repository, issue, cwd);
|
|
328
|
-
if (!fetched.ok) return fetched;
|
|
329
|
-
const snapshot = normalizeIssue(fetched.value, repository, issue);
|
|
330
|
-
return snapshot
|
|
331
|
-
? { ok: true as const, value: snapshot }
|
|
332
|
-
: { ok: false as const, error: { code: 'ISSUE_IDENTITY_INVALID', message: 'Remote resource is not a valid Issue', retryable: false } };
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
function inspectGitHubIssueMetadata(client: GitHubClient, repository: string, issue: number, cwd: string) {
|
|
336
|
-
const fetched = fetchIssue(client, repository, issue, cwd);
|
|
337
|
-
if (!fetched.ok) return fetched;
|
|
338
|
-
const remote = fetched.value;
|
|
339
|
-
return {
|
|
340
|
-
ok: true as const,
|
|
341
|
-
value: {
|
|
342
|
-
state: String(remote.state || '').toLowerCase() === 'closed' ? 'closed' as const : 'open' as const,
|
|
343
|
-
labels: (remote.labels || []).map((label: string | { name?: string }) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
|
|
344
|
-
body: remote.body || '',
|
|
345
|
-
milestone: remote.milestone?.title || null
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
|
|
350
194
|
async function inspectPlatformIssue(taskRef: string, options: SharedOptions = {}): Promise<IssueResult> {
|
|
351
195
|
const base = await resolvedContext(taskRef, options);
|
|
352
196
|
if (!base.ok) return base.output;
|
|
@@ -543,14 +387,6 @@ function requirementsFromTask(content: string): Requirement[] {
|
|
|
543
387
|
}));
|
|
544
388
|
}
|
|
545
389
|
|
|
546
|
-
function flattenPages(value: unknown): unknown[] {
|
|
547
|
-
return Array.isArray(value) ? value.flatMap((item) => Array.isArray(item) ? item : [item]) : [];
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
function listNames(value: unknown): string[] {
|
|
551
|
-
return flattenPages(value).map((item) => typeof item === 'string' ? item : String((item as { name?: string }).name || '')).filter(Boolean);
|
|
552
|
-
}
|
|
553
|
-
|
|
554
390
|
function milestoneBasis(repoRoot: string, current: string | null): string | null {
|
|
555
391
|
if (!current || !/^\d+\.\d+\.x$/.test(current)) return current;
|
|
556
392
|
try {
|
|
@@ -642,73 +478,6 @@ function desiredFieldValues(frontmatter: Record<string, string>, fields: IssueFi
|
|
|
642
478
|
return desired;
|
|
643
479
|
}
|
|
644
480
|
|
|
645
|
-
function planGraphMetadata(
|
|
646
|
-
client: GitHubClient,
|
|
647
|
-
repository: string,
|
|
648
|
-
issue: number,
|
|
649
|
-
cwd: string,
|
|
650
|
-
frontmatter: Record<string, string>,
|
|
651
|
-
options: Pick<SyncOptions, 'issueType' | 'fields'>,
|
|
652
|
-
capabilities: PlatformResult['capabilities']
|
|
653
|
-
): { operations: PlannedOperation[]; issueId: string | null } {
|
|
654
|
-
if (!options.issueType && !options.fields) return { operations: [], issueId: null };
|
|
655
|
-
if (!capabilities.push) return {
|
|
656
|
-
operations: [
|
|
657
|
-
...(options.issueType ? [{ name: 'issue-type', status: 'skipped' as const, reasonCode: 'PUSH_REQUIRED' }] : []),
|
|
658
|
-
...(options.fields ? [{ name: 'fields', status: 'skipped' as const, reasonCode: 'PUSH_REQUIRED' }] : [])
|
|
659
|
-
], issueId: null
|
|
660
|
-
};
|
|
661
|
-
const [owner] = repository.split('/');
|
|
662
|
-
const typesResult = client.json<unknown>(['api', 'graphql', '-f', `query=${ISSUE_TYPES_QUERY}`, '-F', `owner=${owner}`], { cwd });
|
|
663
|
-
if (!typesResult.ok) return { operations: [{ name: 'issue-schema', status: 'failed', reasonCode: typesResult.error.code }], issueId: null };
|
|
664
|
-
const types = normalizeIssueTypes(typesResult.value);
|
|
665
|
-
if (types.length === 0) return {
|
|
666
|
-
operations: [
|
|
667
|
-
...(options.issueType ? [{ name: 'issue-type', status: 'skipped' as const, reasonCode: 'ISSUE_TYPES_UNSUPPORTED' }] : []),
|
|
668
|
-
...(options.fields ? [{ name: 'fields', status: 'skipped' as const, reasonCode: 'ISSUE_TYPES_UNSUPPORTED' }] : [])
|
|
669
|
-
], issueId: null
|
|
670
|
-
};
|
|
671
|
-
const current = graphState(client, repository, issue, cwd);
|
|
672
|
-
if (!current?.issueId) return { operations: [{ name: 'issue-schema', status: 'failed', reasonCode: 'ISSUE_GRAPH_IDENTITY_INVALID' }], issueId: null };
|
|
673
|
-
const target = types.find((type) => type.name === desiredIssueType(frontmatter.type || ''));
|
|
674
|
-
if (!target) return { operations: [{ name: 'issue-type', status: 'failed', reasonCode: 'ISSUE_TYPE_NOT_FOUND' }], issueId: current.issueId };
|
|
675
|
-
const operations: PlannedOperation[] = [];
|
|
676
|
-
if (options.issueType) operations.push(current.type?.id === target.id
|
|
677
|
-
? { name: 'issue-type', status: 'no-op', reasonCode: null }
|
|
678
|
-
: { name: 'issue-type', status: 'planned', reasonCode: null, value: { issueTypeId: target.id } });
|
|
679
|
-
if (options.fields) {
|
|
680
|
-
const desired = desiredFieldValues(frontmatter, target.fields);
|
|
681
|
-
const inputs = desired.filter((field) => current.values.find((value) => value.name === field.name)?.value !== field.value).map((field) => field.input);
|
|
682
|
-
if (current.type?.id !== target.id) {
|
|
683
|
-
const targetNames = new Set(target.fields.map((field) => field.name));
|
|
684
|
-
for (const old of current.values) if (!targetNames.has(old.name)) inputs.push({ fieldId: old.id, delete: true });
|
|
685
|
-
}
|
|
686
|
-
operations.push(inputs.length === 0
|
|
687
|
-
? { name: 'fields', status: 'no-op', reasonCode: null }
|
|
688
|
-
: { name: 'fields', status: 'planned', reasonCode: null, value: inputs });
|
|
689
|
-
}
|
|
690
|
-
return { operations, issueId: current.issueId };
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
function executeGraphOperation(
|
|
694
|
-
client: GitHubClient,
|
|
695
|
-
cwd: string,
|
|
696
|
-
issueId: string,
|
|
697
|
-
operation: PlannedOperation
|
|
698
|
-
) {
|
|
699
|
-
if (operation.name === 'issue-type') {
|
|
700
|
-
const query = 'mutation($issueId:ID!,$issueTypeId:ID){updateIssueIssueType(input:{issueId:$issueId,issueTypeId:$issueTypeId}){issue{id}}}';
|
|
701
|
-
const typeId = (operation.value as { issueTypeId: string }).issueTypeId;
|
|
702
|
-
return client.json(['api', 'graphql', '--input', '-'], {
|
|
703
|
-
cwd, method: 'POST', input: JSON.stringify({ query, variables: { issueId, issueTypeId: typeId } })
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
const query = 'mutation($issueId:ID!,$issueFields:[IssueFieldCreateOrUpdateInput!]!){setIssueFieldValue(input:{issueId:$issueId,issueFields:$issueFields}){issue{id}}}';
|
|
707
|
-
return client.json(['api', 'graphql', '--input', '-'], {
|
|
708
|
-
cwd, method: 'POST', input: JSON.stringify({ query, variables: { issueId, issueFields: operation.value } })
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
|
|
712
481
|
async function syncPlatformIssue(taskRef: string, options: SyncOptions): Promise<IssueResult> {
|
|
713
482
|
const base = await resolvedContext(taskRef, options);
|
|
714
483
|
if (!base.ok) return base.output;
|
|
@@ -849,15 +618,5 @@ async function syncPlatformIssue(taskRef: string, options: SyncOptions): Promise
|
|
|
849
618
|
});
|
|
850
619
|
}
|
|
851
620
|
|
|
852
|
-
export {
|
|
853
|
-
|
|
854
|
-
createPlatformIssue,
|
|
855
|
-
graphState,
|
|
856
|
-
inspectGitHubIssue,
|
|
857
|
-
inspectGitHubIssueMetadata,
|
|
858
|
-
inspectPlatformIssue,
|
|
859
|
-
normalizeIssue,
|
|
860
|
-
requirementSectionAnchors,
|
|
861
|
-
syncPlatformIssue
|
|
862
|
-
};
|
|
863
|
-
export type { BindOptions, CreateOptions, IssueResult, IssueSnapshot, SyncOptions };
|
|
621
|
+
export { bindPlatformIssue, createPlatformIssue, inspectPlatformIssue, requirementSectionAnchors, syncPlatformIssue };
|
|
622
|
+
export type { BindOptions, CreateOptions, IssueResult, SyncOptions };
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
4
|
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import { escapeHtmlText } from './comment-safety.ts';
|
|
5
6
|
|
|
6
7
|
const REPORT_FILE_NAME = 'pr-change-report.json';
|
|
7
8
|
const REPORT_VERSION = 1 as const;
|
|
@@ -483,22 +484,15 @@ function representativePath(file: ChangeFile): string {
|
|
|
483
484
|
return file.newPath || file.oldPath || '';
|
|
484
485
|
}
|
|
485
486
|
|
|
486
|
-
function
|
|
487
|
-
return value
|
|
488
|
-
.replaceAll('&', '&')
|
|
489
|
-
.replaceAll('<', '<')
|
|
490
|
-
.replaceAll('>', '>')
|
|
491
|
-
.replaceAll('"', '"')
|
|
492
|
-
.replaceAll("'", ''')
|
|
493
|
-
.replaceAll('\r', '\\r')
|
|
494
|
-
.replaceAll('\n', '\\n');
|
|
487
|
+
function escapeReportText(value: string): string {
|
|
488
|
+
return escapeHtmlText(value).replaceAll('\r', '\\r').replaceAll('\n', '\\n');
|
|
495
489
|
}
|
|
496
490
|
|
|
497
491
|
function renderRepresentativePath(file: ChangeFile): string {
|
|
498
492
|
const paths = file.oldPath && file.newPath && file.oldPath !== file.newPath
|
|
499
493
|
? [file.oldPath, file.newPath]
|
|
500
494
|
: [representativePath(file)];
|
|
501
|
-
return paths.map((filePath) => `<code>${
|
|
495
|
+
return paths.map((filePath) => `<code>${escapeReportText(filePath)}</code>`).join(' → ');
|
|
502
496
|
}
|
|
503
497
|
|
|
504
498
|
function representativeScore(file: ChangeFile, metric: 'lines' | 'bytes'): number {
|
|
@@ -551,12 +545,12 @@ function renderEvidence(evidence: Evidence): string {
|
|
|
551
545
|
? `:${evidence.startLine}`
|
|
552
546
|
: `:${evidence.startLine}-${evidence.endLine}`
|
|
553
547
|
: `:${evidence.startLine ?? evidence.endLine}`;
|
|
554
|
-
return ` - 证据:<code>${
|
|
548
|
+
return ` - 证据:<code>${escapeReportText(evidence.path)}</code>${location}:${escapeReportText(evidence.detail)}`;
|
|
555
549
|
}
|
|
556
550
|
|
|
557
551
|
function renderPrechecks(checks: Precheck[]): string[] {
|
|
558
552
|
return checks.flatMap((check) => [
|
|
559
|
-
`- **${PRECHECK_LABELS[check.id]}(${check.id})**:${check.verdict === 'pass' ? '通过' : '需复核'}。${
|
|
553
|
+
`- **${PRECHECK_LABELS[check.id]}(${check.id})**:${check.verdict === 'pass' ? '通过' : '需复核'}。${escapeReportText(check.rationale)}`,
|
|
560
554
|
...check.evidence.slice(0, 1).map(renderEvidence)
|
|
561
555
|
]);
|
|
562
556
|
}
|