@humanbased/crosscheck 1.2.0 → 1.3.0-beta.100
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/LICENSE +1 -1
- package/README.md +183 -375
- package/README.zh.md +1 -1
- package/assets/icon-256.png +0 -0
- package/assets/linear-comment.svg +18 -0
- package/assets/linear-onboard.svg +30 -0
- package/assets/linear-status.svg +23 -0
- package/assets/linear-test.svg +34 -0
- package/assets/skills/code-review/.crosscheck-skill.json +9 -0
- package/assets/skills/code-review/LICENSE +21 -0
- package/assets/skills/code-review/SKILL.md +89 -0
- package/assets/skills/code-review/agents/openai.yaml +3 -0
- package/assets/skills/code-review-skill/.crosscheck-skill.json +9 -0
- package/assets/skills/code-review-skill/LICENSE +21 -0
- package/assets/skills/code-review-skill/SKILL.md +231 -0
- package/assets/skills/code-review-skill/assets/pr-review-template.md +137 -0
- package/assets/skills/code-review-skill/assets/review-checklist.md +123 -0
- package/assets/skills/code-review-skill/reference/angular.md +768 -0
- package/assets/skills/code-review-skill/reference/architecture-review-guide.md +472 -0
- package/assets/skills/code-review-skill/reference/c.md +890 -0
- package/assets/skills/code-review-skill/reference/code-quality-universal.md +488 -0
- package/assets/skills/code-review-skill/reference/code-review-best-practices.md +136 -0
- package/assets/skills/code-review-skill/reference/common-bugs-checklist.md +286 -0
- package/assets/skills/code-review-skill/reference/cpp.md +893 -0
- package/assets/skills/code-review-skill/reference/cross-cutting/async-concurrency-patterns.md +515 -0
- package/assets/skills/code-review-skill/reference/cross-cutting/error-handling-principles.md +492 -0
- package/assets/skills/code-review-skill/reference/cross-cutting/n-plus-one-queries.md +309 -0
- package/assets/skills/code-review-skill/reference/cross-cutting/sql-injection-prevention.md +308 -0
- package/assets/skills/code-review-skill/reference/cross-cutting/xss-prevention.md +264 -0
- package/assets/skills/code-review-skill/reference/csharp.md +525 -0
- package/assets/skills/code-review-skill/reference/css-less-sass.md +661 -0
- package/assets/skills/code-review-skill/reference/django.md +985 -0
- package/assets/skills/code-review-skill/reference/fastapi.md +580 -0
- package/assets/skills/code-review-skill/reference/go.md +993 -0
- package/assets/skills/code-review-skill/reference/java.md +409 -0
- package/assets/skills/code-review-skill/reference/java8.md +586 -0
- package/assets/skills/code-review-skill/reference/kotlin.md +1018 -0
- package/assets/skills/code-review-skill/reference/nestjs.md +593 -0
- package/assets/skills/code-review-skill/reference/performance-review-guide.md +816 -0
- package/assets/skills/code-review-skill/reference/php.md +684 -0
- package/assets/skills/code-review-skill/reference/python.md +1073 -0
- package/assets/skills/code-review-skill/reference/qt.md +757 -0
- package/assets/skills/code-review-skill/reference/react.md +871 -0
- package/assets/skills/code-review-skill/reference/ruby.md +964 -0
- package/assets/skills/code-review-skill/reference/rust.md +846 -0
- package/assets/skills/code-review-skill/reference/security-review-guide.md +494 -0
- package/assets/skills/code-review-skill/reference/svelte.md +1064 -0
- package/assets/skills/code-review-skill/reference/swift.md +936 -0
- package/assets/skills/code-review-skill/reference/typescript.md +1016 -0
- package/assets/skills/code-review-skill/reference/vue.md +924 -0
- package/assets/skills/code-review-skill/reference/zig.md +440 -0
- package/assets/skills/code-review-skill/scripts/pr-analyzer.py +435 -0
- package/assets/skills/code-review-skill/scripts/test_pr_analyzer.py +380 -0
- package/assets/skills/codebase-design/.crosscheck-skill.json +9 -0
- package/assets/skills/codebase-design/DEEPENING.md +37 -0
- package/assets/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/assets/skills/codebase-design/LICENSE +21 -0
- package/assets/skills/codebase-design/SKILL.md +114 -0
- package/assets/skills/codebase-design/agents/openai.yaml +3 -0
- package/assets/skills/diagnosing-bugs/.crosscheck-skill.json +9 -0
- package/assets/skills/diagnosing-bugs/LICENSE +21 -0
- package/assets/skills/diagnosing-bugs/SKILL.md +134 -0
- package/assets/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/assets/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
- package/crosscheck.config.example.yml +124 -11
- package/dist/__tests__/adoption.test.d.ts +2 -0
- package/dist/__tests__/adoption.test.d.ts.map +1 -0
- package/dist/__tests__/adoption.test.js +175 -0
- package/dist/__tests__/adoption.test.js.map +1 -0
- package/dist/__tests__/auto-fix-branch.test.d.ts +2 -0
- package/dist/__tests__/auto-fix-branch.test.d.ts.map +1 -0
- package/dist/__tests__/auto-fix-branch.test.js +143 -0
- package/dist/__tests__/auto-fix-branch.test.js.map +1 -0
- package/dist/__tests__/board.test.js +13 -0
- package/dist/__tests__/board.test.js.map +1 -1
- package/dist/__tests__/can-write-verdict.test.d.ts +2 -0
- package/dist/__tests__/can-write-verdict.test.d.ts.map +1 -0
- package/dist/__tests__/can-write-verdict.test.js +31 -0
- package/dist/__tests__/can-write-verdict.test.js.map +1 -0
- package/dist/__tests__/codex-env.test.d.ts +2 -0
- package/dist/__tests__/codex-env.test.d.ts.map +1 -0
- package/dist/__tests__/codex-env.test.js +70 -0
- package/dist/__tests__/codex-env.test.js.map +1 -0
- package/dist/__tests__/codex.test.js +100 -25
- package/dist/__tests__/codex.test.js.map +1 -1
- package/dist/__tests__/comment-bodies.test.js +49 -1
- package/dist/__tests__/comment-bodies.test.js.map +1 -1
- package/dist/__tests__/commit-subject.test.d.ts +2 -0
- package/dist/__tests__/commit-subject.test.d.ts.map +1 -0
- package/dist/__tests__/commit-subject.test.js +72 -0
- package/dist/__tests__/commit-subject.test.js.map +1 -0
- package/dist/__tests__/conflict-resolve.test.js +44 -1
- package/dist/__tests__/conflict-resolve.test.js.map +1 -1
- package/dist/__tests__/credential-free-origin.test.d.ts +2 -0
- package/dist/__tests__/credential-free-origin.test.d.ts.map +1 -0
- package/dist/__tests__/credential-free-origin.test.js +59 -0
- package/dist/__tests__/credential-free-origin.test.js.map +1 -0
- package/dist/__tests__/detector.test.js +87 -1
- package/dist/__tests__/detector.test.js.map +1 -1
- package/dist/__tests__/error-classification.test.js +34 -0
- package/dist/__tests__/error-classification.test.js.map +1 -1
- package/dist/__tests__/fix.test.js +69 -0
- package/dist/__tests__/fix.test.js.map +1 -1
- package/dist/__tests__/harness-section.test.d.ts +2 -0
- package/dist/__tests__/harness-section.test.d.ts.map +1 -0
- package/dist/__tests__/harness-section.test.js +46 -0
- package/dist/__tests__/harness-section.test.js.map +1 -0
- package/dist/__tests__/kickass.test.js +31 -0
- package/dist/__tests__/kickass.test.js.map +1 -1
- package/dist/__tests__/linear-branding.test.d.ts +2 -0
- package/dist/__tests__/linear-branding.test.d.ts.map +1 -0
- package/dist/__tests__/linear-branding.test.js +156 -0
- package/dist/__tests__/linear-branding.test.js.map +1 -0
- package/dist/__tests__/linear-client.test.d.ts +2 -0
- package/dist/__tests__/linear-client.test.d.ts.map +1 -0
- package/dist/__tests__/linear-client.test.js +120 -0
- package/dist/__tests__/linear-client.test.js.map +1 -0
- package/dist/__tests__/linear-comment.test.d.ts +2 -0
- package/dist/__tests__/linear-comment.test.d.ts.map +1 -0
- package/dist/__tests__/linear-comment.test.js +151 -0
- package/dist/__tests__/linear-comment.test.js.map +1 -0
- package/dist/__tests__/linear-identity.test.d.ts +2 -0
- package/dist/__tests__/linear-identity.test.d.ts.map +1 -0
- package/dist/__tests__/linear-identity.test.js +253 -0
- package/dist/__tests__/linear-identity.test.js.map +1 -0
- package/dist/__tests__/linear-notify.test.d.ts +2 -0
- package/dist/__tests__/linear-notify.test.d.ts.map +1 -0
- package/dist/__tests__/linear-notify.test.js +144 -0
- package/dist/__tests__/linear-notify.test.js.map +1 -0
- package/dist/__tests__/linear-ref.test.d.ts +2 -0
- package/dist/__tests__/linear-ref.test.d.ts.map +1 -0
- package/dist/__tests__/linear-ref.test.js +261 -0
- package/dist/__tests__/linear-ref.test.js.map +1 -0
- package/dist/__tests__/linear-test-ref.test.d.ts +2 -0
- package/dist/__tests__/linear-test-ref.test.d.ts.map +1 -0
- package/dist/__tests__/linear-test-ref.test.js +81 -0
- package/dist/__tests__/linear-test-ref.test.js.map +1 -0
- package/dist/__tests__/linear-verify.test.d.ts +2 -0
- package/dist/__tests__/linear-verify.test.d.ts.map +1 -0
- package/dist/__tests__/linear-verify.test.js +132 -0
- package/dist/__tests__/linear-verify.test.js.map +1 -0
- package/dist/__tests__/linear-worker.test.d.ts +2 -0
- package/dist/__tests__/linear-worker.test.d.ts.map +1 -0
- package/dist/__tests__/linear-worker.test.js +83 -0
- package/dist/__tests__/linear-worker.test.js.map +1 -0
- package/dist/__tests__/linear-write-possible.test.d.ts +2 -0
- package/dist/__tests__/linear-write-possible.test.d.ts.map +1 -0
- package/dist/__tests__/linear-write-possible.test.js +30 -0
- package/dist/__tests__/linear-write-possible.test.js.map +1 -0
- package/dist/__tests__/loader.test.js +2 -2
- package/dist/__tests__/loader.test.js.map +1 -1
- package/dist/__tests__/onboard-preservation.test.js +59 -4
- package/dist/__tests__/onboard-preservation.test.js.map +1 -1
- package/dist/__tests__/optimize.test.js +2 -0
- package/dist/__tests__/optimize.test.js.map +1 -1
- package/dist/__tests__/pr-status.test.js +213 -3
- package/dist/__tests__/pr-status.test.js.map +1 -1
- package/dist/__tests__/pr-workflow-state.test.js +247 -7
- package/dist/__tests__/pr-workflow-state.test.js.map +1 -1
- package/dist/__tests__/repo-picker.test.js +7 -1
- package/dist/__tests__/repo-picker.test.js.map +1 -1
- package/dist/__tests__/repository-guidance.test.d.ts +2 -0
- package/dist/__tests__/repository-guidance.test.d.ts.map +1 -0
- package/dist/__tests__/repository-guidance.test.js +107 -0
- package/dist/__tests__/repository-guidance.test.js.map +1 -0
- package/dist/__tests__/review-comment-body.test.js +35 -0
- package/dist/__tests__/review-comment-body.test.js.map +1 -1
- package/dist/__tests__/review-models.test.js +19 -3
- package/dist/__tests__/review-models.test.js.map +1 -1
- package/dist/__tests__/review-strategy.test.d.ts +2 -0
- package/dist/__tests__/review-strategy.test.d.ts.map +1 -0
- package/dist/__tests__/review-strategy.test.js +397 -0
- package/dist/__tests__/review-strategy.test.js.map +1 -0
- package/dist/__tests__/reviewed-sha.test.d.ts +2 -0
- package/dist/__tests__/reviewed-sha.test.d.ts.map +1 -0
- package/dist/__tests__/reviewed-sha.test.js +116 -0
- package/dist/__tests__/reviewed-sha.test.js.map +1 -0
- package/dist/__tests__/runner.test.js +206 -2
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/__tests__/skill-attribution.test.d.ts +2 -0
- package/dist/__tests__/skill-attribution.test.d.ts.map +1 -0
- package/dist/__tests__/skill-attribution.test.js +53 -0
- package/dist/__tests__/skill-attribution.test.js.map +1 -0
- package/dist/__tests__/skill-broker.test.d.ts +2 -0
- package/dist/__tests__/skill-broker.test.d.ts.map +1 -0
- package/dist/__tests__/skill-broker.test.js +186 -0
- package/dist/__tests__/skill-broker.test.js.map +1 -0
- package/dist/__tests__/skill-catalog.test.d.ts +2 -0
- package/dist/__tests__/skill-catalog.test.d.ts.map +1 -0
- package/dist/__tests__/skill-catalog.test.js +40 -0
- package/dist/__tests__/skill-catalog.test.js.map +1 -0
- package/dist/__tests__/skill-installer.test.d.ts +2 -0
- package/dist/__tests__/skill-installer.test.d.ts.map +1 -0
- package/dist/__tests__/skill-installer.test.js +96 -0
- package/dist/__tests__/skill-installer.test.js.map +1 -0
- package/dist/__tests__/skills-config.test.d.ts +2 -0
- package/dist/__tests__/skills-config.test.d.ts.map +1 -0
- package/dist/__tests__/skills-config.test.js +12 -0
- package/dist/__tests__/skills-config.test.js.map +1 -0
- package/dist/__tests__/superseded-fix-pr.test.d.ts +2 -0
- package/dist/__tests__/superseded-fix-pr.test.d.ts.map +1 -0
- package/dist/__tests__/superseded-fix-pr.test.js +225 -0
- package/dist/__tests__/superseded-fix-pr.test.js.map +1 -0
- package/dist/__tests__/webhook.test.js +60 -0
- package/dist/__tests__/webhook.test.js.map +1 -1
- package/dist/cli.js +37 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/adoption.d.ts +6 -0
- package/dist/commands/adoption.d.ts.map +1 -0
- package/dist/commands/adoption.js +132 -0
- package/dist/commands/adoption.js.map +1 -0
- package/dist/commands/detect-step.d.ts.map +1 -1
- package/dist/commands/detect-step.js +5 -1
- package/dist/commands/detect-step.js.map +1 -1
- package/dist/commands/diagnose.d.ts.map +1 -1
- package/dist/commands/diagnose.js +2 -2
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/issue.js +1 -1
- package/dist/commands/issue.js.map +1 -1
- package/dist/commands/kickass.d.ts +1 -1
- package/dist/commands/kickass.d.ts.map +1 -1
- package/dist/commands/kickass.js +36 -22
- package/dist/commands/kickass.js.map +1 -1
- package/dist/commands/linear-test.d.ts +18 -0
- package/dist/commands/linear-test.d.ts.map +1 -0
- package/dist/commands/linear-test.js +130 -0
- package/dist/commands/linear-test.js.map +1 -0
- package/dist/commands/onboard.d.ts +36 -3
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +263 -45
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/optimize.d.ts.map +1 -1
- package/dist/commands/optimize.js +1 -0
- package/dist/commands/optimize.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +65 -6
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +85 -11
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +6 -0
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/skill.d.ts +2 -0
- package/dist/commands/skill.d.ts.map +1 -0
- package/dist/commands/skill.js +16 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +53 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +215 -66
- package/dist/commands/watch.js.map +1 -1
- package/dist/config/loader.d.ts +3 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +19 -6
- package/dist/config/loader.js.map +1 -1
- package/dist/config/review-model-tiers.json +3 -3
- package/dist/config/review-strategy.json +204 -0
- package/dist/config/schema.d.ts +269 -15
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +109 -10
- package/dist/config/schema.js.map +1 -1
- package/dist/github/client.d.ts +21 -1
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +46 -7
- package/dist/github/client.js.map +1 -1
- package/dist/github/detector.d.ts.map +1 -1
- package/dist/github/detector.js +28 -1
- package/dist/github/detector.js.map +1 -1
- package/dist/github/merge.d.ts +0 -5
- package/dist/github/merge.d.ts.map +1 -1
- package/dist/github/merge.js +0 -9
- package/dist/github/merge.js.map +1 -1
- package/dist/github/reviewed-sha.d.ts +13 -0
- package/dist/github/reviewed-sha.d.ts.map +1 -0
- package/dist/github/reviewed-sha.js +62 -0
- package/dist/github/reviewed-sha.js.map +1 -0
- package/dist/github/superseded-fix-pr.d.ts +19 -0
- package/dist/github/superseded-fix-pr.d.ts.map +1 -0
- package/dist/github/superseded-fix-pr.js +97 -0
- package/dist/github/superseded-fix-pr.js.map +1 -0
- package/dist/github/webhook.d.ts +8 -1
- package/dist/github/webhook.d.ts.map +1 -1
- package/dist/github/webhook.js +8 -1
- package/dist/github/webhook.js.map +1 -1
- package/dist/issues/ticket-ref.d.ts.map +1 -1
- package/dist/issues/ticket-ref.js +6 -5
- package/dist/issues/ticket-ref.js.map +1 -1
- package/dist/lib/adoption.d.ts +64 -0
- package/dist/lib/adoption.d.ts.map +1 -0
- package/dist/lib/adoption.js +165 -0
- package/dist/lib/adoption.js.map +1 -0
- package/dist/lib/annotation.d.ts +7 -0
- package/dist/lib/annotation.d.ts.map +1 -1
- package/dist/lib/annotation.js +11 -8
- package/dist/lib/annotation.js.map +1 -1
- package/dist/lib/auto-fix-branch.d.ts +35 -0
- package/dist/lib/auto-fix-branch.d.ts.map +1 -0
- package/dist/lib/auto-fix-branch.js +88 -0
- package/dist/lib/auto-fix-branch.js.map +1 -0
- package/dist/lib/board.d.ts +3 -0
- package/dist/lib/board.d.ts.map +1 -1
- package/dist/lib/board.js +4 -2
- package/dist/lib/board.js.map +1 -1
- package/dist/lib/clone.d.ts +2 -0
- package/dist/lib/clone.d.ts.map +1 -1
- package/dist/lib/clone.js +69 -10
- package/dist/lib/clone.js.map +1 -1
- package/dist/lib/comment-bodies.d.ts +37 -0
- package/dist/lib/comment-bodies.d.ts.map +1 -1
- package/dist/lib/comment-bodies.js +50 -9
- package/dist/lib/comment-bodies.js.map +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +17 -1
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/pr-picker.d.ts +1 -1
- package/dist/lib/pr-picker.d.ts.map +1 -1
- package/dist/lib/pr-picker.js +10 -5
- package/dist/lib/pr-picker.js.map +1 -1
- package/dist/lib/pr-status.d.ts +2 -1
- package/dist/lib/pr-status.d.ts.map +1 -1
- package/dist/lib/pr-status.js +70 -6
- package/dist/lib/pr-status.js.map +1 -1
- package/dist/lib/pr-workflow-state.d.ts +17 -1
- package/dist/lib/pr-workflow-state.d.ts.map +1 -1
- package/dist/lib/pr-workflow-state.js +110 -5
- package/dist/lib/pr-workflow-state.js.map +1 -1
- package/dist/lib/repo-picker.d.ts +3 -0
- package/dist/lib/repo-picker.d.ts.map +1 -1
- package/dist/lib/repo-picker.js +45 -10
- package/dist/lib/repo-picker.js.map +1 -1
- package/dist/lib/repository-guidance.d.ts +2 -0
- package/dist/lib/repository-guidance.d.ts.map +1 -0
- package/dist/lib/repository-guidance.js +55 -0
- package/dist/lib/repository-guidance.js.map +1 -0
- package/dist/lib/review-models.d.ts +15 -2
- package/dist/lib/review-models.d.ts.map +1 -1
- package/dist/lib/review-models.js +26 -6
- package/dist/lib/review-models.js.map +1 -1
- package/dist/lib/review-strategy.d.ts +92 -0
- package/dist/lib/review-strategy.d.ts.map +1 -0
- package/dist/lib/review-strategy.js +282 -0
- package/dist/lib/review-strategy.js.map +1 -0
- package/dist/lib/runner.d.ts +123 -1
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +919 -101
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/vendor.d.ts +1 -0
- package/dist/lib/vendor.d.ts.map +1 -1
- package/dist/lib/vendor.js +6 -0
- package/dist/lib/vendor.js.map +1 -1
- package/dist/lib/workflow.d.ts +13 -0
- package/dist/lib/workflow.d.ts.map +1 -1
- package/dist/lib/workflow.js +24 -1
- package/dist/lib/workflow.js.map +1 -1
- package/dist/linear/client.d.ts +18 -0
- package/dist/linear/client.d.ts.map +1 -0
- package/dist/linear/client.js +67 -0
- package/dist/linear/client.js.map +1 -0
- package/dist/linear/comment.d.ts +20 -0
- package/dist/linear/comment.d.ts.map +1 -0
- package/dist/linear/comment.js +57 -0
- package/dist/linear/comment.js.map +1 -0
- package/dist/linear/identity.d.ts +59 -0
- package/dist/linear/identity.d.ts.map +1 -0
- package/dist/linear/identity.js +187 -0
- package/dist/linear/identity.js.map +1 -0
- package/dist/linear/notify.d.ts +35 -0
- package/dist/linear/notify.d.ts.map +1 -0
- package/dist/linear/notify.js +76 -0
- package/dist/linear/notify.js.map +1 -0
- package/dist/linear/ref.d.ts +13 -0
- package/dist/linear/ref.d.ts.map +1 -0
- package/dist/linear/ref.js +90 -0
- package/dist/linear/ref.js.map +1 -0
- package/dist/linear/verify.d.ts +26 -0
- package/dist/linear/verify.d.ts.map +1 -0
- package/dist/linear/verify.js +67 -0
- package/dist/linear/verify.js.map +1 -0
- package/dist/reviewers/address.d.ts +1 -1
- package/dist/reviewers/address.d.ts.map +1 -1
- package/dist/reviewers/address.js +1 -1
- package/dist/reviewers/address.js.map +1 -1
- package/dist/reviewers/claude.d.ts +4 -1
- package/dist/reviewers/claude.d.ts.map +1 -1
- package/dist/reviewers/claude.js +39 -7
- package/dist/reviewers/claude.js.map +1 -1
- package/dist/reviewers/codex-env.d.ts +12 -0
- package/dist/reviewers/codex-env.d.ts.map +1 -0
- package/dist/reviewers/codex-env.js +54 -0
- package/dist/reviewers/codex-env.js.map +1 -0
- package/dist/reviewers/codex.d.ts +18 -1
- package/dist/reviewers/codex.d.ts.map +1 -1
- package/dist/reviewers/codex.js +164 -81
- package/dist/reviewers/codex.js.map +1 -1
- package/dist/reviewers/conflict-resolve.d.ts +3 -1
- package/dist/reviewers/conflict-resolve.d.ts.map +1 -1
- package/dist/reviewers/conflict-resolve.js +21 -6
- package/dist/reviewers/conflict-resolve.js.map +1 -1
- package/dist/reviewers/fix.d.ts +5 -2
- package/dist/reviewers/fix.d.ts.map +1 -1
- package/dist/reviewers/fix.js +38 -12
- package/dist/reviewers/fix.js.map +1 -1
- package/dist/skills/attribution.d.ts +4 -0
- package/dist/skills/attribution.d.ts.map +1 -0
- package/dist/skills/attribution.js +14 -0
- package/dist/skills/attribution.js.map +1 -0
- package/dist/skills/broker-server.d.ts +2 -0
- package/dist/skills/broker-server.d.ts.map +1 -0
- package/dist/skills/broker-server.js +17 -0
- package/dist/skills/broker-server.js.map +1 -0
- package/dist/skills/broker.d.ts +43 -0
- package/dist/skills/broker.d.ts.map +1 -0
- package/dist/skills/broker.js +311 -0
- package/dist/skills/broker.js.map +1 -0
- package/dist/skills/catalog.d.ts +28 -0
- package/dist/skills/catalog.d.ts.map +1 -0
- package/dist/skills/catalog.js +104 -0
- package/dist/skills/catalog.js.map +1 -0
- package/dist/skills/installer.d.ts +10 -0
- package/dist/skills/installer.d.ts.map +1 -0
- package/dist/skills/installer.js +138 -0
- package/dist/skills/installer.js.map +1 -0
- package/dist/skills/integrity.d.ts +4 -0
- package/dist/skills/integrity.d.ts.map +1 -0
- package/dist/skills/integrity.js +36 -0
- package/dist/skills/integrity.js.map +1 -0
- package/docs/dynamic-thoroughness.md +738 -0
- package/docs/linear-identity-contract.md +139 -0
- package/docs/linear-identity.md +293 -0
- package/docs/metrics.md +115 -0
- package/docs/trust.md +159 -0
- package/get-started.md +322 -16
- package/package.json +6 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AA4KA,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAuBD,wBAAsB,QAAQ,CAAC,IAAI,GAAE,SAAc,iBAo4ClD"}
|
package/dist/commands/watch.js
CHANGED
|
@@ -2,19 +2,21 @@ import { execSync, spawn } from 'child_process';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { createWebhookServer } from '../github/webhook.js';
|
|
4
4
|
import { createGithubClient, getCommitMessage, registerOrgWebhook, deleteOrgWebhook, registerRepoWebhook, deleteRepoWebhook, patchOrgWebhookEvents, patchRepoWebhookEvents, findOrgWebhook, findRepoWebhook, listUserRepos, checkRepoAccessible, } from '../github/client.js';
|
|
5
|
+
import { closeSupersededAutoFixPRs } from '../github/superseded-fix-pr.js';
|
|
5
6
|
import { detectOriginFull, assignReviewer } from '../github/detector.js';
|
|
6
|
-
import { loadConfig, getGithubToken, getWebhookSecret, resolveConfigPath, promptDeploymentMode, detectScopesForDeployment, patchDeploymentConfig, detectGitHubLogin, } from '../config/loader.js';
|
|
7
|
+
import { loadConfig, getGithubToken, getWebhookSecret, resolveConfigPath, promptDeploymentMode, detectScopesForDeployment, patchDeploymentConfig, detectGitHubLogin, getLinearCredentials, } from '../config/loader.js';
|
|
7
8
|
import { randomFortune } from '../lib/fortune.js';
|
|
8
9
|
import { scanUnreviewedPRs } from '../lib/backtrace.js';
|
|
9
10
|
import { initLogger, log as fileLog, logError, logUncaught } from '../lib/logger.js';
|
|
10
11
|
import { isAuthorAllowed } from '../lib/filter.js';
|
|
11
12
|
import { runWorkflow } from '../lib/runner.js';
|
|
12
|
-
import { loadWorkflow, DEFAULT_RECHECK_INSTRUCTIONS } from '../lib/workflow.js';
|
|
13
|
-
import { filterStepsByTypes, formatRepoWorkflowSteps,
|
|
13
|
+
import { loadWorkflow, linearWritePossible, DEFAULT_RECHECK_INSTRUCTIONS } from '../lib/workflow.js';
|
|
14
|
+
import { filterStepsByTypes, formatRepoWorkflowSteps, isReviewOnlyWorkflow, readRepoWorkflowStepTypes, resolveRepoWorkflowSteps, workflowHasStep } from '../lib/repo-workflow.js';
|
|
14
15
|
import { fetchStepHistory, identifyNextWorkflowStep, decideReviewOnly } from '../lib/pr-workflow-state.js';
|
|
15
16
|
import { parseAnnotation } from '../lib/annotation.js';
|
|
16
17
|
import { PRBoard, fmtTime, FMT_TIME_WIDTH } from '../lib/board.js';
|
|
17
18
|
import { clonePRForReview } from '../lib/clone.js';
|
|
19
|
+
import { resolveLinearAuth, isLinearConfigError } from '../linear/identity.js';
|
|
18
20
|
import { getSmartSwitch, isSubscriptionLimitError, detectFailedVendor, triggerSwitch, notifyReviewSuccess, stopSmartSwitch, } from '../lib/smart-switch.js';
|
|
19
21
|
import { buildReviewFailedCommentBody } from '../lib/comment-bodies.js';
|
|
20
22
|
import { classifyReviewerError } from '../lib/reviewer-error.js';
|
|
@@ -128,6 +130,21 @@ function openTunnel(localPort) {
|
|
|
128
130
|
});
|
|
129
131
|
});
|
|
130
132
|
}
|
|
133
|
+
// PR history is what decides whether a step runs at all — the approval stop, the
|
|
134
|
+
// per-SHA dedup, the resume point. Acting without it means posting a duplicate review
|
|
135
|
+
// or modifying an already-approved commit, so callers fail closed rather than assume an
|
|
136
|
+
// empty history. One short retry first: the usual cause is a transient GitHub blip or a
|
|
137
|
+
// rate-limit tick, and deferring the event strands the PR until something else happens.
|
|
138
|
+
const HISTORY_RETRY_DELAY_MS = 3_000;
|
|
139
|
+
async function fetchStepHistoryWithRetry(owner, repo, prNumber, token) {
|
|
140
|
+
try {
|
|
141
|
+
return await fetchStepHistory(owner, repo, prNumber, token);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
await new Promise(resolve => setTimeout(resolve, HISTORY_RETRY_DELAY_MS));
|
|
145
|
+
return await fetchStepHistory(owner, repo, prNumber, token);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
131
148
|
export async function runWatch(opts = {}) {
|
|
132
149
|
const configPath = opts.config;
|
|
133
150
|
let config = loadConfig(configPath);
|
|
@@ -156,6 +173,25 @@ export async function runWatch(opts = {}) {
|
|
|
156
173
|
console.error(chalk.red(`✗ ${err instanceof Error ? err.message : String(err)}`));
|
|
157
174
|
process.exit(1);
|
|
158
175
|
}
|
|
176
|
+
// Preflight Linear identity at startup. watch is a long-running daemon: a bad
|
|
177
|
+
// credential discovered per-event would either drop events silently or spam the
|
|
178
|
+
// board. Fail loudly at boot instead, the way the run path fails before its
|
|
179
|
+
// clone. The token is discarded — per-event resolution mints a fresh one, since
|
|
180
|
+
// app tokens outlive neither a long daemon run nor a rotation.
|
|
181
|
+
// Same gate as the run and per-event paths: a daemon whose configured workflow
|
|
182
|
+
// is fix/conflict-resolve only can never call notifyLinear, so a missing
|
|
183
|
+
// credential must not stop it booting.
|
|
184
|
+
if (linearWritePossible(config.linear, loadWorkflow(process.cwd()))) {
|
|
185
|
+
try {
|
|
186
|
+
const preflight = await resolveLinearAuth(config.linear, getLinearCredentials(config.linear.auth));
|
|
187
|
+
console.log(chalk.dim(` linear: ${preflight.mode} — writes as ${preflight.actor}`));
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
logError({ command: 'watch', phase: 'linear-auth' }, err);
|
|
191
|
+
console.error(chalk.red(`✗ ${err instanceof Error ? err.message : String(err)}`));
|
|
192
|
+
process.exit(isLinearConfigError(err) ? 1 : 2);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
159
195
|
fileLog({ level: 'info', event: 'session_start', command: 'watch' });
|
|
160
196
|
// Fetch the authenticated user so the onComment handler can reject annotation
|
|
161
197
|
// injection from non-token accounts. If this fails, the handler is disabled
|
|
@@ -191,12 +227,16 @@ export async function runWatch(opts = {}) {
|
|
|
191
227
|
// Last-reviewed diff hash per PR — skip reviews when a new SHA has identical diff vs base
|
|
192
228
|
// (force-push, amend, no-op rebase). Persisted so restarts don't re-review unchanged content.
|
|
193
229
|
const diffHashes = new PersistentDiffHashMap();
|
|
194
|
-
//
|
|
195
|
-
|
|
230
|
+
// Completed round count per PR — the auto-loop's round counter, which history cannot
|
|
231
|
+
// supply (it returns the last recheck's round unchanged across iterations).
|
|
196
232
|
const prRoundCounts = new Map();
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
|
|
233
|
+
// Comment IDs the issue_comment bridge has already handled, so a webhook redelivery
|
|
234
|
+
// of the same comment does not dispatch twice. There is no need to guess at which
|
|
235
|
+
// comments are "ours": webhook.ts forwards only `trigger=kickass` annotations, and
|
|
236
|
+
// this watcher's own reviews carry trigger=watch/comment/backtrace, so they never
|
|
237
|
+
// reach the bridge. Every comment that does reach it is a kickass dispatch whose
|
|
238
|
+
// verdict this watcher is meant to act on — history detection decides how.
|
|
239
|
+
const handledCommentIds = new Set();
|
|
200
240
|
// Idle tracking — reset on any PR activity; used by the idle-issue timer.
|
|
201
241
|
let lastActivityAt = Date.now();
|
|
202
242
|
let idleAnalysisRunning = false;
|
|
@@ -215,6 +255,7 @@ export async function runWatch(opts = {}) {
|
|
|
215
255
|
fileLog({ level: 'info', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'author_not_allowed', author: params.author });
|
|
216
256
|
return;
|
|
217
257
|
}
|
|
258
|
+
const prKey = `${owner}/${repoName}#${prNumber}`;
|
|
218
259
|
const { origin, method: originMethod } = await detectOriginFull(params.body ?? '', params.headRef, owner, repoName, prNumber, config, token, params.author);
|
|
219
260
|
// Smart-switch: when cross-vendor is degraded, override to single-vendor with the healthy vendor
|
|
220
261
|
const ss = getSmartSwitch();
|
|
@@ -235,9 +276,17 @@ export async function runWatch(opts = {}) {
|
|
|
235
276
|
title: params.title,
|
|
236
277
|
body: params.body ?? '',
|
|
237
278
|
head: { ref: params.headRef, sha: params.headSha, repo: params.headRepo ? { full_name: params.headRepo } : null },
|
|
238
|
-
base: {
|
|
279
|
+
base: {
|
|
280
|
+
ref: params.baseRef,
|
|
281
|
+
repo: {
|
|
282
|
+
full_name: `${owner}/${repoName}`,
|
|
283
|
+
...(params.defaultBranch !== undefined && { default_branch: params.defaultBranch }),
|
|
284
|
+
},
|
|
285
|
+
},
|
|
239
286
|
html_url: `https://github.com/${owner}/${repoName}/pull/${prNumber}`,
|
|
240
287
|
user: { login: params.author },
|
|
288
|
+
...(params.labels !== undefined && { labels: params.labels.map(name => ({ name })) }),
|
|
289
|
+
...(params.createdAt !== undefined && { created_at: params.createdAt }),
|
|
241
290
|
};
|
|
242
291
|
if (!acquirePRLock(owner, repoName, prNumber, params.headSha)) {
|
|
243
292
|
fileLog({ level: 'info', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'in_progress_local' });
|
|
@@ -257,21 +306,26 @@ export async function runWatch(opts = {}) {
|
|
|
257
306
|
logError({ repo: `${owner}/${repoName}`, pr: prNumber, phase: 'lock' }, err);
|
|
258
307
|
return;
|
|
259
308
|
}
|
|
260
|
-
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
309
|
+
// Determine the correct starting step from PR comment history. Webhook, backtrace,
|
|
310
|
+
// and auto_loop events always ask — there is no PR-level session fast-path.
|
|
311
|
+
//
|
|
312
|
+
// There used to be one (`reviewedPRKeys`): reviewed-in-this-session meant "run the
|
|
313
|
+
// next event as a recheck". It was wrong by construction on two axes. It is
|
|
314
|
+
// SHA-agnostic, so a push it had never seen inherited the previous SHA's routing;
|
|
315
|
+
// and it only knows what THIS process did, so an approval, a forced re-review, or a
|
|
316
|
+
// fix pushed by `kickass`, `crosscheck run`, or a second watcher was invisible to
|
|
317
|
+
// it. Each case where it guessed wrong grew its own guard — recheck-no-fix depth,
|
|
318
|
+
// repo overrides, approvals — and every one of those guards did the same thing:
|
|
319
|
+
// undo the guess and ask history. So ask history. It costs one call per event, and
|
|
320
|
+
// it is the only thing that sees the PR as it actually is.
|
|
321
|
+
//
|
|
322
|
+
// Comment triggers ask too. The SHA-specific fast path they used to take ran an
|
|
323
|
+
// externally-posted NEEDS_WORK as a recheck instead of routing to fix, because it
|
|
324
|
+
// skipped detection whenever this session had already touched the SHA.
|
|
325
|
+
let isRecheckRun = false;
|
|
326
|
+
// auto_loop's round is pre-computed from the completed round count rather than
|
|
327
|
+
// from history, which returns the last recheck's round unchanged across iterations
|
|
328
|
+
// and would defeat the max_rounds cap.
|
|
275
329
|
let round = params.action === 'auto_loop'
|
|
276
330
|
? (prRoundCounts.get(prKey) ?? 1) + 1
|
|
277
331
|
: isRecheckRun ? (prRoundCounts.get(prKey) ?? 1) + 1 : 1;
|
|
@@ -285,17 +339,6 @@ export async function runWatch(opts = {}) {
|
|
|
285
339
|
isRecheckRun = false;
|
|
286
340
|
round = 1;
|
|
287
341
|
}
|
|
288
|
-
// A recheck-no-fix depth (`review,recheck`) decides review-vs-recheck per SHA from
|
|
289
|
-
// the last verdict: an unresolved review gets a recheck, an APPROVE gets a fresh
|
|
290
|
-
// review (identifyNextWorkflowStep). The session fast-path above is PR-level and
|
|
291
|
-
// SHA-agnostic, so it would coerce every later push in this process into a recheck
|
|
292
|
-
// and never consult that decision. Force history detection instead. Comment-
|
|
293
|
-
// triggered runs keep their SHA-specific fast path — a kickass re-review targets a
|
|
294
|
-
// SHA history detection would report as already complete.
|
|
295
|
-
if (isRecheckWithoutFix(repoStepOverride) && isRecheckRun && params.action !== 'comment') {
|
|
296
|
-
isRecheckRun = false;
|
|
297
|
-
round = 1;
|
|
298
|
-
}
|
|
299
342
|
if (reviewOnlyForRepo) {
|
|
300
343
|
// Review-only comes from a per-repo override (crosscheck alter --review-only).
|
|
301
344
|
// Restrict the workflow to review step(s) and dedup on the exact head sha so
|
|
@@ -312,7 +355,7 @@ export async function runWatch(opts = {}) {
|
|
|
312
355
|
round = 1;
|
|
313
356
|
resolvedSteps = reviewSteps;
|
|
314
357
|
try {
|
|
315
|
-
const history = await
|
|
358
|
+
const history = await fetchStepHistoryWithRetry(owner, repoName, prNumber, token);
|
|
316
359
|
const decision = decideReviewOnly(history, params.headSha);
|
|
317
360
|
if (decision.alreadyReviewed) {
|
|
318
361
|
await releaseRemoteLock(lockOctokit, owner, repoName, params.headSha, 'success');
|
|
@@ -322,7 +365,18 @@ export async function runWatch(opts = {}) {
|
|
|
322
365
|
}
|
|
323
366
|
round = decision.round;
|
|
324
367
|
}
|
|
325
|
-
catch {
|
|
368
|
+
catch (err) {
|
|
369
|
+
// The dedup this mode relies on lives in that history. Reviewing without it
|
|
370
|
+
// would post a second review on a SHA already covered.
|
|
371
|
+
// Released as `failure`, not `success`: this SHA was not reviewed, and
|
|
372
|
+
// `crosscheck/review` is a commit status a repo can require for merge — a
|
|
373
|
+
// green one here would let an unreviewed HEAD satisfy branch protection
|
|
374
|
+
// during a GitHub outage.
|
|
375
|
+
await releaseRemoteLock(lockOctokit, owner, repoName, params.headSha, 'failure');
|
|
376
|
+
releasePRLock(owner, repoName, prNumber, params.headSha);
|
|
377
|
+
fileLog({ level: 'warn', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'history_unavailable', sha: params.headSha, error: err instanceof Error ? err.message : String(err) });
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
326
380
|
}
|
|
327
381
|
else if (!isRecheckRun) {
|
|
328
382
|
try {
|
|
@@ -331,18 +385,34 @@ export async function runWatch(opts = {}) {
|
|
|
331
385
|
// fresh so live edits to workflow.yml apply across events.
|
|
332
386
|
const globalWorkflow = loadWorkflow(process.cwd());
|
|
333
387
|
const allSteps = repoStepOverride ? filterStepsByTypes(globalWorkflow, repoStepOverride) : globalWorkflow;
|
|
334
|
-
const history = await
|
|
335
|
-
|
|
388
|
+
const history = await fetchStepHistoryWithRetry(owner, repoName, prNumber, token);
|
|
389
|
+
// The webhook payload carries no usable `mergeable`, so ask — but only for
|
|
390
|
+
// workflows that can act on the answer. GitHub computes it lazily, and a null
|
|
391
|
+
// simply means the conflict route is not offered on this event. Unlike the
|
|
392
|
+
// history read above this one stays best-effort: not knowing whether the PR
|
|
393
|
+
// conflicts only costs a deferred resolve, not a wrong review.
|
|
394
|
+
let mergeable = null;
|
|
395
|
+
if (allSteps.some(s => s.type === 'conflict-resolve')) {
|
|
396
|
+
try {
|
|
397
|
+
const { data: freshPR } = await lockOctokit.rest.pulls.get({ owner, repo: repoName, pull_number: prNumber });
|
|
398
|
+
mergeable = freshPR.mergeable;
|
|
399
|
+
}
|
|
400
|
+
catch { /* best-effort — unknown mergeability just skips the conflict route */ }
|
|
401
|
+
}
|
|
402
|
+
const nextResult = identifyNextWorkflowStep(history, allSteps, params.headSha, { mergeable });
|
|
336
403
|
if (nextResult.step === null) {
|
|
337
404
|
// Workflow already complete for this HEAD sha — release lock and skip.
|
|
338
405
|
// Happens when a synchronize event fires after all steps are done.
|
|
339
406
|
await releaseRemoteLock(lockOctokit, owner, repoName, params.headSha, 'success');
|
|
340
407
|
releasePRLock(owner, repoName, prNumber, params.headSha);
|
|
341
|
-
fileLog({ level: 'info', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'workflow_complete', sha: params.headSha });
|
|
408
|
+
fileLog({ level: 'info', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: nextResult.stopReason ?? 'workflow_complete', sha: params.headSha });
|
|
342
409
|
return;
|
|
343
410
|
}
|
|
344
411
|
if (nextResult.hasExistingReview) {
|
|
345
|
-
|
|
412
|
+
// conflict-resolve is excluded: the run continues into the review step, and
|
|
413
|
+
// the merge commit it produces is new content owed a real review, not a
|
|
414
|
+
// recheck of findings the merge had nothing to do with.
|
|
415
|
+
isRecheckRun = nextResult.step.type !== 'review' && nextResult.step.type !== 'conflict-resolve';
|
|
346
416
|
// auto_loop pre-computed the correct incremented round from prRoundCounts;
|
|
347
417
|
// history detection returns the last recheck's round (unchanged across
|
|
348
418
|
// iterations), so overwriting here would defeat the max_rounds cap.
|
|
@@ -391,15 +461,26 @@ export async function runWatch(opts = {}) {
|
|
|
391
461
|
}
|
|
392
462
|
}
|
|
393
463
|
}
|
|
394
|
-
catch {
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
|
|
402
|
-
|
|
464
|
+
catch (err) {
|
|
465
|
+
// History still unreadable after the retry. Everything this function decides —
|
|
466
|
+
// whether HEAD is already approved, already reviewed, or mid-loop — lives in
|
|
467
|
+
// that history, so running anyway risks a second review on an approved commit
|
|
468
|
+
// or a fix re-applied to code that already has one. Defer instead: release the
|
|
469
|
+
// locks and let the next event, `kickass`, or `crosscheck run` pick the PR up.
|
|
470
|
+
//
|
|
471
|
+
// auto_loop is the exception. It is a continuation of a run this process just
|
|
472
|
+
// completed, so the state is known from the session rather than from history,
|
|
473
|
+
// and deferring would strand the loop — nothing else re-triggers it.
|
|
474
|
+
if (params.action !== 'auto_loop') {
|
|
475
|
+
// `failure`, not `success` — see the review-only branch above: a green
|
|
476
|
+
// `crosscheck/review` on a HEAD nobody reviewed can satisfy branch
|
|
477
|
+
// protection.
|
|
478
|
+
await releaseRemoteLock(lockOctokit, owner, repoName, params.headSha, 'failure');
|
|
479
|
+
releasePRLock(owner, repoName, prNumber, params.headSha);
|
|
480
|
+
fileLog({ level: 'warn', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'history_unavailable', sha: params.headSha, error: err instanceof Error ? err.message : String(err) });
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
isRecheckRun = true;
|
|
403
484
|
}
|
|
404
485
|
}
|
|
405
486
|
const reviewStart = Date.now();
|
|
@@ -407,6 +488,32 @@ export async function runWatch(opts = {}) {
|
|
|
407
488
|
let stopHeartbeat = () => { };
|
|
408
489
|
let boardAdded = false;
|
|
409
490
|
try {
|
|
491
|
+
// Board first, so any failure below is visible rather than swallowed by the
|
|
492
|
+
// enclosing catch (which only calls logError — a no-op when file logging is
|
|
493
|
+
// off). Then auth, then the clone: during an outage a long-running watcher
|
|
494
|
+
// would otherwise pay for a full clone on every event before rejecting it.
|
|
495
|
+
// Computed here rather than at the runWorkflow call so the auth gate below
|
|
496
|
+
// sees the workflow that will actually run. resolvedSteps alone is
|
|
497
|
+
// undefined for a plain event, and canWriteVerdict fails open on undefined
|
|
498
|
+
// — which let a fix-only daemon resolve credentials on every event.
|
|
499
|
+
const workflowStepsForRun = resolvedSteps
|
|
500
|
+
?? (repoStepOverride ? filterStepsByTypes(loadWorkflow(process.cwd()), repoStepOverride) : loadWorkflow(process.cwd()));
|
|
501
|
+
board.addPR(key, prNumber, `${owner}/${repoName}`, params.headRef, round);
|
|
502
|
+
boardAdded = true;
|
|
503
|
+
// Only workflows that can write a verdict need an identity — the fix and
|
|
504
|
+
// resolve paths never call notifyLinear.
|
|
505
|
+
let linearAuth = null;
|
|
506
|
+
if (linearWritePossible(effectiveConfig.linear, workflowStepsForRun)) {
|
|
507
|
+
try {
|
|
508
|
+
linearAuth = await resolveLinearAuth(effectiveConfig.linear, getLinearCredentials(effectiveConfig.linear.auth));
|
|
509
|
+
}
|
|
510
|
+
catch (err) {
|
|
511
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
512
|
+
board.failPR(key, `linear identity unavailable: ${message}`);
|
|
513
|
+
fileLog({ level: 'error', event: 'linear_auth_failed', repo: `${owner}/${repoName}`, pr: prNumber, reason: message });
|
|
514
|
+
throw err;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
410
517
|
await clonePRForReview({
|
|
411
518
|
owner, repo: repoName, prNumber, baseRef: params.baseRef,
|
|
412
519
|
tmpDir, token, protocol: config.clone_protocol,
|
|
@@ -442,18 +549,18 @@ export async function runWatch(opts = {}) {
|
|
|
442
549
|
logError({ repo: `${owner}/${repoName}`, pr: prNumber, phase: 'no_diff_comment' }, err);
|
|
443
550
|
}
|
|
444
551
|
await releaseRemoteLock(lockOctokit, owner, repoName, params.headSha, 'success');
|
|
552
|
+
// The PR is on the board from before the clone, so close its slot rather
|
|
553
|
+
// than leaving a row that never resolves.
|
|
554
|
+
board.completePR(key, { elapsedMs: Date.now() - reviewStart, url: `https://github.com/${owner}/${repoName}/pull/${prNumber}` });
|
|
445
555
|
return;
|
|
446
556
|
}
|
|
447
|
-
board.addPR(key, prNumber, `${owner}/${repoName}`, params.headRef, round);
|
|
448
|
-
boardAdded = true;
|
|
449
557
|
const prLoc = computePRLoc(tmpDir, params.baseRef);
|
|
450
558
|
board.updatePR(key, { prLoc });
|
|
451
559
|
stopHeartbeat = startRemoteLockHeartbeat(lockOctokit, owner, repoName, params.headSha);
|
|
452
|
-
const
|
|
453
|
-
?? (repoStepOverride ? filterStepsByTypes(loadWorkflow(process.cwd()), repoStepOverride) : undefined);
|
|
454
|
-
const { verdict, fixAppliedCount } = await runWorkflow({
|
|
560
|
+
const { verdict, fixAppliedCount, strategySkipped } = await runWorkflow({
|
|
455
561
|
owner, repoName, prNumber, pr,
|
|
456
562
|
tmpDir, token, config: effectiveConfig, origin,
|
|
563
|
+
linearAuth,
|
|
457
564
|
reviewStart,
|
|
458
565
|
log: (msg) => bLog(`${chalk.dim(fmtTime())} ${msg}`),
|
|
459
566
|
onPhaseChange: (label, data) => board.updatePR(key, { label, ...data }),
|
|
@@ -470,10 +577,11 @@ export async function runWatch(opts = {}) {
|
|
|
470
577
|
round,
|
|
471
578
|
trigger: params.action === 'backtrace' ? 'backtrace' : params.action === 'comment' ? 'comment' : 'watch',
|
|
472
579
|
});
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
580
|
+
// A strategy-skipped PR never ran a review, so it must not advance the
|
|
581
|
+
// auto-loop's round counter — that run posted nothing to count.
|
|
582
|
+
if (strategySkipped === undefined) {
|
|
583
|
+
prRoundCounts.set(prKey, round);
|
|
584
|
+
}
|
|
477
585
|
// Recompute the diff hash AFTER runWorkflow — workflow steps such as
|
|
478
586
|
// `conflict-resolve` or `fix` followed by `recheck` can mutate the checkout,
|
|
479
587
|
// so the pre-workflow hash may not represent the content that was actually
|
|
@@ -493,7 +601,10 @@ export async function runWatch(opts = {}) {
|
|
|
493
601
|
}
|
|
494
602
|
catch { /* fall back — skip auto-loop this cycle */ }
|
|
495
603
|
}
|
|
496
|
-
|
|
604
|
+
// Same gate as the session caches above: caching the diff of a PR that was
|
|
605
|
+
// never reviewed would make the first event that *should* review it get
|
|
606
|
+
// skipped as `no_diff_change`.
|
|
607
|
+
if (newDiffHash && strategySkipped === undefined) {
|
|
497
608
|
// For non-commit delivery with an applied fix the checkout may be on a fix
|
|
498
609
|
// branch; computeDiffHash would return the post-fix diff, not the original PR
|
|
499
610
|
// diff. Caching that under prKey corrupts future no_diff_change checks for the
|
|
@@ -514,9 +625,15 @@ export async function runWatch(opts = {}) {
|
|
|
514
625
|
}
|
|
515
626
|
}
|
|
516
627
|
}
|
|
628
|
+
// A class-skipped PR (e.g. lockfile-only) never ran a review, so say so
|
|
629
|
+
// rather than reporting it as a completed one.
|
|
630
|
+
if (strategySkipped) {
|
|
631
|
+
bLog(`${chalk.dim(fmtTime())} ${chalk.dim(`PR #${prNumber} skipped — ${strategySkipped} class, nothing to review`)}`);
|
|
632
|
+
}
|
|
517
633
|
board.completePR(key, {
|
|
518
634
|
elapsedMs: Date.now() - reviewStart,
|
|
519
635
|
url: `github.com/${owner}/${repoName}/pull/${prNumber}`,
|
|
636
|
+
...(strategySkipped !== undefined && { label: `skipped · ${strategySkipped}` }),
|
|
520
637
|
});
|
|
521
638
|
// Smart-switch recovery confirmation: if a restore attempt is pending and
|
|
522
639
|
// this reviewer matches the previously-degraded vendor, announce full restoration.
|
|
@@ -649,6 +766,9 @@ export async function runWatch(opts = {}) {
|
|
|
649
766
|
title: pr.title, body: pr.body, author: pr.user.login,
|
|
650
767
|
headSha: pr.head.sha, headRef: pr.head.ref, headRepo: pr.head.repo?.full_name ?? null,
|
|
651
768
|
baseRef: pr.base.ref, action: event.action,
|
|
769
|
+
...(pr.labels !== undefined && { labels: pr.labels.map(l => l.name) }),
|
|
770
|
+
...(pr.base.repo.default_branch !== undefined && { defaultBranch: pr.base.repo.default_branch }),
|
|
771
|
+
...(pr.created_at !== undefined && { createdAt: pr.created_at }),
|
|
652
772
|
});
|
|
653
773
|
}, (msg) => bLog(chalk.dim(fmtTime()) + ' ' + msg), fileLog, async (event) => {
|
|
654
774
|
const owner = event.repository.owner.login;
|
|
@@ -698,10 +818,11 @@ export async function runWatch(opts = {}) {
|
|
|
698
818
|
fileLog({ level: 'info', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'comment_stale_sha', sha: prData.head.sha, annotation_sha: annotationSha });
|
|
699
819
|
return;
|
|
700
820
|
}
|
|
701
|
-
if (
|
|
702
|
-
fileLog({ level: 'info', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: '
|
|
821
|
+
if (handledCommentIds.has(event.comment.id)) {
|
|
822
|
+
fileLog({ level: 'info', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'comment_already_handled', comment_id: event.comment.id });
|
|
703
823
|
return;
|
|
704
824
|
}
|
|
825
|
+
handledCommentIds.add(event.comment.id);
|
|
705
826
|
// GitHub can deliver issue_comment before ck run's finally block releases
|
|
706
827
|
// the remote lock. Retry with backoff; re-fetch the PR head on each attempt
|
|
707
828
|
// so we don't dispatch against a sha that advanced while we were waiting.
|
|
@@ -725,10 +846,6 @@ export async function runWatch(opts = {}) {
|
|
|
725
846
|
break;
|
|
726
847
|
}
|
|
727
848
|
}
|
|
728
|
-
if (reviewedPRShaKeys.has(`${owner}/${repoName}#${prNumber}@${prData.head.sha}`)) {
|
|
729
|
-
fileLog({ level: 'info', event: 'pr_skipped', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'comment_self_trigger', sha: prData.head.sha });
|
|
730
|
-
break;
|
|
731
|
-
}
|
|
732
849
|
if (await checkRemoteLock(octokit, owner, repoName, prData.head.sha).catch(() => false)) {
|
|
733
850
|
fileLog({ level: 'info', event: 'comment_trigger_deferred', repo: `${owner}/${repoName}`, pr: prNumber, reason: 'in_progress_remote', attempt });
|
|
734
851
|
continue;
|
|
@@ -743,6 +860,12 @@ export async function runWatch(opts = {}) {
|
|
|
743
860
|
headRepo: prData.head.repo?.full_name ?? null,
|
|
744
861
|
baseRef: prData.base.ref,
|
|
745
862
|
action: 'comment',
|
|
863
|
+
// Same fields the webhook path forwards: without them the risky
|
|
864
|
+
// class's label and hotfix rules classify this PR differently
|
|
865
|
+
// depending on which trigger ran it.
|
|
866
|
+
...(prData.labels !== undefined && { labels: prData.labels.map((l) => l.name) }),
|
|
867
|
+
...(prData.base.repo?.default_branch !== undefined && { defaultBranch: prData.base.repo.default_branch }),
|
|
868
|
+
...(prData.created_at !== undefined && { createdAt: prData.created_at }),
|
|
746
869
|
});
|
|
747
870
|
break;
|
|
748
871
|
}
|
|
@@ -750,6 +873,29 @@ export async function runWatch(opts = {}) {
|
|
|
750
873
|
catch (err) {
|
|
751
874
|
logError({ repo: `${owner}/${repoName}`, pr: prNumber, phase: 'comment_trigger' }, err);
|
|
752
875
|
}
|
|
876
|
+
}, async (event) => {
|
|
877
|
+
const owner = event.repository.owner.login;
|
|
878
|
+
const repoName = event.repository.name;
|
|
879
|
+
const prNumber = event.number;
|
|
880
|
+
try {
|
|
881
|
+
const octokit = createGithubClient(token);
|
|
882
|
+
const outcomes = await closeSupersededAutoFixPRs(octokit, owner, repoName, prNumber, event.pull_request.merge_commit_sha ?? null);
|
|
883
|
+
for (const outcome of outcomes) {
|
|
884
|
+
fileLog({
|
|
885
|
+
level: outcome.status === 'failed' ? 'warn' : 'info',
|
|
886
|
+
event: 'auto_fix_pr_superseded',
|
|
887
|
+
repo: `${owner}/${repoName}`, pr: prNumber,
|
|
888
|
+
fix_pr: outcome.prNumber, status: outcome.status,
|
|
889
|
+
...(outcome.reason && { reason: outcome.reason }),
|
|
890
|
+
});
|
|
891
|
+
if (outcome.status === 'closed') {
|
|
892
|
+
bLog(chalk.dim(fmtTime()) + ` closed superseded auto-fix PR ${owner}/${repoName}#${outcome.prNumber} (${prNumber} merged)`);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
catch (err) {
|
|
897
|
+
logError({ repo: `${owner}/${repoName}`, pr: prNumber, phase: 'auto_fix_supersede' }, err);
|
|
898
|
+
}
|
|
753
899
|
});
|
|
754
900
|
await new Promise((resolve, reject) => {
|
|
755
901
|
server.on('error', (err) => {
|
|
@@ -1029,6 +1175,9 @@ export async function runWatch(opts = {}) {
|
|
|
1029
1175
|
title: pr.title, body: pr.body, author: pr.author,
|
|
1030
1176
|
headSha: pr.headSha, headRef: pr.headRef, headRepo: pr.headRepo,
|
|
1031
1177
|
baseRef: pr.baseRef, action: 'backtrace',
|
|
1178
|
+
...(pr.labels !== undefined && { labels: pr.labels }),
|
|
1179
|
+
...(pr.defaultBranch !== undefined && { defaultBranch: pr.defaultBranch }),
|
|
1180
|
+
createdAt: pr.createdAt,
|
|
1032
1181
|
})));
|
|
1033
1182
|
}
|
|
1034
1183
|
catch (err) {
|