@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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { notifyLinear, issueBelongsToWorkspace } from '../linear/notify.js';
|
|
3
|
+
import { LinearConfigSchema } from '../config/schema.js';
|
|
4
|
+
const AUTH = {
|
|
5
|
+
mode: 'client_credentials',
|
|
6
|
+
token: 'minted',
|
|
7
|
+
bearer: true,
|
|
8
|
+
actor: 'crosscheck',
|
|
9
|
+
signature: '🤖 crosscheck · crosscheck',
|
|
10
|
+
signatureTemplate: '🤖 {actor} · {product}',
|
|
11
|
+
product: 'crosscheck',
|
|
12
|
+
createAsUser: 'crosscheck',
|
|
13
|
+
};
|
|
14
|
+
const PR = {
|
|
15
|
+
branch: 'feat/in-2269-identity',
|
|
16
|
+
title: 'feat: tiered Linear identity',
|
|
17
|
+
body: '',
|
|
18
|
+
url: 'https://github.com/acme/app/pull/12',
|
|
19
|
+
sha: 'abc1234',
|
|
20
|
+
};
|
|
21
|
+
const BASE = { auth: AUTH, pr: PR, verdict: 'NEEDS_WORK', reviewer: 'codex', origin: 'claude', model: 'gpt-5' };
|
|
22
|
+
function cfg(overrides = {}) {
|
|
23
|
+
return LinearConfigSchema.parse({ enabled: true, team_keys: ['IN'], ...overrides });
|
|
24
|
+
}
|
|
25
|
+
/** Replies to the issue lookup, then the comment mutation. */
|
|
26
|
+
function scriptedFetch(issueNodes, mutation) {
|
|
27
|
+
let call = 0;
|
|
28
|
+
return vi.fn(async () => {
|
|
29
|
+
call++;
|
|
30
|
+
if (call === 1)
|
|
31
|
+
return new Response(JSON.stringify({ data: { issues: { nodes: issueNodes } } }), { status: 200 });
|
|
32
|
+
return new Response(JSON.stringify({
|
|
33
|
+
data: mutation ?? { commentCreate: { success: true, comment: { id: 'c1', url: 'https://linear.app/x/c1' } } },
|
|
34
|
+
}), { status: 200 });
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const FOUND = [{ id: 'uuid-1', identifier: 'IN-2269', url: 'https://linear.app/x/issue/IN-2269' }];
|
|
38
|
+
describe('notifyLinear', () => {
|
|
39
|
+
it('posts to the issue referenced by the branch', async () => {
|
|
40
|
+
const fetchImpl = scriptedFetch(FOUND);
|
|
41
|
+
const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
|
|
42
|
+
expect(result).toEqual({ status: 'posted', identifier: 'IN-2269', url: 'https://linear.app/x/c1' });
|
|
43
|
+
});
|
|
44
|
+
it('sends a body that leads with the signature and carries createAsUser', async () => {
|
|
45
|
+
const fetchImpl = scriptedFetch(FOUND);
|
|
46
|
+
await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
|
|
47
|
+
const calls = fetchImpl.mock.calls;
|
|
48
|
+
const mutation = JSON.parse(calls[1][1].body);
|
|
49
|
+
expect(mutation.variables.createAsUser).toBe('crosscheck');
|
|
50
|
+
expect(mutation.variables.body.split('\n')[0]).toBe('🤖 crosscheck · crosscheck');
|
|
51
|
+
});
|
|
52
|
+
it('skips when the verdict is not in comment_on', async () => {
|
|
53
|
+
const fetchImpl = scriptedFetch(FOUND);
|
|
54
|
+
const result = await notifyLinear({ ...BASE, config: cfg({ comment_on: ['BLOCK'] }) }, { fetchImpl });
|
|
55
|
+
expect(result).toEqual({ status: 'skipped', reason: 'verdict-not-configured' });
|
|
56
|
+
expect(fetchImpl).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
it('skips when the PR references no Linear issue', async () => {
|
|
59
|
+
const fetchImpl = scriptedFetch(FOUND);
|
|
60
|
+
const pr = { ...PR, branch: 'feat/no-ref', title: 'chore: bump', body: '' };
|
|
61
|
+
const result = await notifyLinear({ ...BASE, pr, config: cfg() }, { fetchImpl });
|
|
62
|
+
expect(result).toEqual({ status: 'skipped', reason: 'no-issue-ref' });
|
|
63
|
+
expect(fetchImpl).not.toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
it('skips when the referenced issue does not exist', async () => {
|
|
66
|
+
const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl: scriptedFetch([]) });
|
|
67
|
+
expect(result).toEqual({ status: 'skipped', reason: 'issue-not-found', identifier: 'IN-2269' });
|
|
68
|
+
});
|
|
69
|
+
it('reports failure instead of throwing when the API errors', async () => {
|
|
70
|
+
const fetchImpl = vi.fn(async () => new Response('{}', { status: 500 }));
|
|
71
|
+
const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
|
|
72
|
+
expect(result.status).toBe('failed');
|
|
73
|
+
expect(result.reason).toContain('500');
|
|
74
|
+
});
|
|
75
|
+
it('reports failure when the mutation is rejected', async () => {
|
|
76
|
+
const fetchImpl = scriptedFetch(FOUND, { commentCreate: { success: false, comment: null } });
|
|
77
|
+
const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
|
|
78
|
+
expect(result.status).toBe('failed');
|
|
79
|
+
expect(result.reason).toMatch(/rejected/i);
|
|
80
|
+
});
|
|
81
|
+
it('never leaks the token in a failure reason', async () => {
|
|
82
|
+
const fetchImpl = vi.fn(async () => new Response('{}', { status: 401 }));
|
|
83
|
+
const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
|
|
84
|
+
expect(result.reason).not.toContain('minted');
|
|
85
|
+
});
|
|
86
|
+
it('follows a linear.app URL in the body when no team_keys are configured', async () => {
|
|
87
|
+
const pr = { ...PR, branch: 'feat/nope', title: 'chore', body: 'https://linear.app/x/issue/IN-2269/z' };
|
|
88
|
+
const result = await notifyLinear({ ...BASE, pr, config: cfg({ team_keys: [] }) }, { fetchImpl: scriptedFetch(FOUND) });
|
|
89
|
+
expect(result.status).toBe('posted');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe('workspace mismatch', () => {
|
|
93
|
+
const scoped = (issueUrl) => {
|
|
94
|
+
let call = 0;
|
|
95
|
+
return vi.fn(async () => {
|
|
96
|
+
call++;
|
|
97
|
+
if (call === 1) {
|
|
98
|
+
return new Response(JSON.stringify({
|
|
99
|
+
data: { issues: { nodes: [{ id: 'u1', identifier: 'IN-2269', url: issueUrl }] } },
|
|
100
|
+
}), { status: 200 });
|
|
101
|
+
}
|
|
102
|
+
return new Response(JSON.stringify({
|
|
103
|
+
data: { commentCreate: { success: true, comment: { id: 'c1', url: 'https://linear.app/x/c1' } } },
|
|
104
|
+
}), { status: 200 });
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
const prWithUrl = (url) => ({ ...PR, branch: 'feat/none', title: 'chore', body: url });
|
|
108
|
+
it('posts when the resolved issue is in the URL-named workspace', async () => {
|
|
109
|
+
const fetchImpl = scoped('https://linear.app/acme/issue/IN-2269/slug');
|
|
110
|
+
const result = await notifyLinear({ ...BASE, pr: prWithUrl('https://linear.app/acme/issue/IN-2269'), config: cfg({ team_keys: [] }) }, { fetchImpl });
|
|
111
|
+
expect(result.status).toBe('posted');
|
|
112
|
+
});
|
|
113
|
+
it('skips when the credentials resolve a different workspace', async () => {
|
|
114
|
+
// Same identifier, different workspace — a real issue, but not the one meant.
|
|
115
|
+
const fetchImpl = scoped('https://linear.app/other-corp/issue/IN-2269/slug');
|
|
116
|
+
const result = await notifyLinear({ ...BASE, pr: prWithUrl('https://linear.app/acme/issue/IN-2269'), config: cfg({ team_keys: [] }) }, { fetchImpl });
|
|
117
|
+
expect(result).toEqual({ status: 'skipped', reason: 'workspace-mismatch', identifier: 'IN-2269' });
|
|
118
|
+
});
|
|
119
|
+
it('does not post the comment when the workspace mismatches', async () => {
|
|
120
|
+
const fetchImpl = scoped('https://linear.app/other-corp/issue/IN-2269/slug');
|
|
121
|
+
await notifyLinear({ ...BASE, pr: prWithUrl('https://linear.app/acme/issue/IN-2269'), config: cfg({ team_keys: [] }) }, { fetchImpl });
|
|
122
|
+
expect(fetchImpl.mock.calls).toHaveLength(1);
|
|
123
|
+
});
|
|
124
|
+
it('does not constrain a bare ref, which names no workspace', async () => {
|
|
125
|
+
const fetchImpl = scoped('https://linear.app/whatever/issue/IN-2269/slug');
|
|
126
|
+
const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
|
|
127
|
+
expect(result.status).toBe('posted');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe('issueBelongsToWorkspace', () => {
|
|
131
|
+
it('matches the first path segment', () => {
|
|
132
|
+
expect(issueBelongsToWorkspace('https://linear.app/acme/issue/IN-1', 'acme')).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
it('is case insensitive', () => {
|
|
135
|
+
expect(issueBelongsToWorkspace('https://linear.app/ACME/issue/IN-1', 'acme')).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
it('rejects a different workspace', () => {
|
|
138
|
+
expect(issueBelongsToWorkspace('https://linear.app/other/issue/IN-1', 'acme')).toBe(false);
|
|
139
|
+
});
|
|
140
|
+
it('fails closed on an unparseable URL', () => {
|
|
141
|
+
expect(issueBelongsToWorkspace('not a url', 'acme')).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
//# sourceMappingURL=linear-notify.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-notify.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-notify.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-ref.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-ref.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { extractLinearRef } from '../linear/ref.js';
|
|
3
|
+
describe('extractLinearRef', () => {
|
|
4
|
+
describe('Linear URLs (no team_keys needed)', () => {
|
|
5
|
+
it('finds an issue URL in the PR body', () => {
|
|
6
|
+
const ref = extractLinearRef({ body: 'Closes https://linear.app/inductive-network/issue/IN-2269/some-slug' }, []);
|
|
7
|
+
expect(ref).toEqual({ id: 'IN-2269', key: 'IN', number: 2269, source: 'body', workspace: 'inductive-network' });
|
|
8
|
+
});
|
|
9
|
+
it('finds an issue URL in the PR title', () => {
|
|
10
|
+
const ref = extractLinearRef({ title: 'fix: see linear.app/acme/issue/ENG-7/thing' }, []);
|
|
11
|
+
expect(ref?.id).toBe('ENG-7');
|
|
12
|
+
expect(ref?.source).toBe('title');
|
|
13
|
+
});
|
|
14
|
+
it('accepts a URL with no trailing slug', () => {
|
|
15
|
+
const ref = extractLinearRef({ body: 'https://linear.app/acme/issue/AB-12' }, []);
|
|
16
|
+
expect(ref?.id).toBe('AB-12');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('bare identifiers require configured team_keys', () => {
|
|
20
|
+
it('ignores bare identifiers when team_keys is empty', () => {
|
|
21
|
+
expect(extractLinearRef({ branch: 'feat/in-2269-thing', title: 'IN-2269 fix' }, [])).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
it('matches a configured key in a branch name, case-insensitively', () => {
|
|
24
|
+
const ref = extractLinearRef({ branch: 'feat/in-2269-tiered-identity' }, ['IN']);
|
|
25
|
+
expect(ref).toEqual({ id: 'IN-2269', key: 'IN', number: 2269, source: 'branch' });
|
|
26
|
+
});
|
|
27
|
+
it('matches an uppercase key in a title', () => {
|
|
28
|
+
const ref = extractLinearRef({ title: 'IN-543: unified resolver' }, ['IN']);
|
|
29
|
+
expect(ref?.id).toBe('IN-543');
|
|
30
|
+
expect(ref?.source).toBe('title');
|
|
31
|
+
});
|
|
32
|
+
it('does not match a key that is not configured', () => {
|
|
33
|
+
expect(extractLinearRef({ branch: 'feat/eng-1-thing' }, ['IN'])).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('false-positive resistance', () => {
|
|
37
|
+
// The reason bare-identifier matching is gated behind team_keys: these all
|
|
38
|
+
// look exactly like Linear identifiers to a naive regex.
|
|
39
|
+
const traps = ['UTF-8', 'SHA-256', 'ISO-8601', 'GPT-5', 'RFC-2119'];
|
|
40
|
+
for (const trap of traps) {
|
|
41
|
+
it(`does not treat ${trap} as an issue ref`, () => {
|
|
42
|
+
expect(extractLinearRef({ title: `chore: switch to ${trap}` }, [])).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
it('still ignores traps when unrelated team_keys are configured', () => {
|
|
46
|
+
expect(extractLinearRef({ title: 'chore: SHA-256 hashing' }, ['IN'])).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
it('requires a word boundary so SUBIN-1 does not match key IN', () => {
|
|
49
|
+
expect(extractLinearRef({ title: 'SUBIN-1 nope' }, ['IN'])).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('precedence', () => {
|
|
53
|
+
it('prefers branch over title and body', () => {
|
|
54
|
+
const ref = extractLinearRef({ branch: 'feat/in-1-a', title: 'IN-2 b', body: 'IN-3 c' }, ['IN']);
|
|
55
|
+
expect(ref?.id).toBe('IN-1');
|
|
56
|
+
expect(ref?.source).toBe('branch');
|
|
57
|
+
});
|
|
58
|
+
it('prefers title over body', () => {
|
|
59
|
+
const ref = extractLinearRef({ title: 'IN-2 b', body: 'IN-3 c' }, ['IN']);
|
|
60
|
+
expect(ref?.id).toBe('IN-2');
|
|
61
|
+
});
|
|
62
|
+
it('prefers an unambiguous URL over a bare key in the same field', () => {
|
|
63
|
+
const ref = extractLinearRef({ body: 'IN-99 mentioned, real one: https://linear.app/x/issue/IN-2269/z' }, ['IN']);
|
|
64
|
+
expect(ref?.id).toBe('IN-2269');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
it('returns null when nothing matches', () => {
|
|
68
|
+
expect(extractLinearRef({ branch: 'main', title: 'chore: bump', body: '' }, ['IN'])).toBeNull();
|
|
69
|
+
});
|
|
70
|
+
it('handles absent fields', () => {
|
|
71
|
+
expect(extractLinearRef({}, ['IN'])).toBeNull();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('hostname anchoring', () => {
|
|
75
|
+
// Regression: an unanchored `linear.app/` fragment let any domain ending in
|
|
76
|
+
// "linear.app" bypass the team_keys gate and target an arbitrary issue.
|
|
77
|
+
const impostors = [
|
|
78
|
+
'https://notlinear.app/acme/issue/IN-2269',
|
|
79
|
+
'https://evil-linear.app/acme/issue/IN-2269',
|
|
80
|
+
'https://mylinear.app/x/issue/IN-1/slug',
|
|
81
|
+
];
|
|
82
|
+
for (const url of impostors) {
|
|
83
|
+
it(`does not trust ${url.replace('https://', '').split('/')[0]}`, () => {
|
|
84
|
+
expect(extractLinearRef({ body: url }, [])).toBeNull();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
it('still trusts the real host', () => {
|
|
88
|
+
expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-2269' }, [])?.id).toBe('IN-2269');
|
|
89
|
+
});
|
|
90
|
+
it('trusts it bare, without a scheme', () => {
|
|
91
|
+
expect(extractLinearRef({ body: 'see linear.app/acme/issue/IN-7' }, [])?.id).toBe('IN-7');
|
|
92
|
+
});
|
|
93
|
+
it('trusts it at the very start of a field', () => {
|
|
94
|
+
expect(extractLinearRef({ body: 'linear.app/acme/issue/IN-8' }, [])?.id).toBe('IN-8');
|
|
95
|
+
});
|
|
96
|
+
it('falls through to team_keys when an impostor URL is present', () => {
|
|
97
|
+
// The impostor must not shadow a legitimate bare ref elsewhere in the text.
|
|
98
|
+
const ref = extractLinearRef({ body: 'https://notlinear.app/x/issue/IN-999 but really IN-42' }, ['IN']);
|
|
99
|
+
expect(ref?.id).toBe('IN-999');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('hostname is parsed, not pattern-matched', () => {
|
|
103
|
+
// A path segment named linear.app is not the linear.app host. A boundary check
|
|
104
|
+
// on the surrounding character passes here; only real parsing rejects it.
|
|
105
|
+
const impostors = [
|
|
106
|
+
'https://evil.example/linear.app/acme/issue/IN-2269',
|
|
107
|
+
'https://evil.example/?u=https://linear.app/acme/issue/IN-2269',
|
|
108
|
+
'https://linear.app.evil.example/acme/issue/IN-2269',
|
|
109
|
+
'https://user@evil.example/linear.app/acme/issue/IN-1',
|
|
110
|
+
];
|
|
111
|
+
for (const url of impostors) {
|
|
112
|
+
it(`rejects ${url.slice(8, 40)}...`, () => {
|
|
113
|
+
expect(extractLinearRef({ body: url }, [])).toBeNull();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
it('accepts the real host over https', () => {
|
|
117
|
+
expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-2269/slug' }, [])?.id).toBe('IN-2269');
|
|
118
|
+
});
|
|
119
|
+
it('accepts the real host case-insensitively', () => {
|
|
120
|
+
expect(extractLinearRef({ body: 'https://LINEAR.APP/acme/issue/IN-3' }, [])?.id).toBe('IN-3');
|
|
121
|
+
});
|
|
122
|
+
it('ignores a malformed URL without throwing', () => {
|
|
123
|
+
expect(extractLinearRef({ body: 'https://[not-a-url/issue/IN-9' }, [])).toBeNull();
|
|
124
|
+
});
|
|
125
|
+
it('finds a real URL after an impostor one', () => {
|
|
126
|
+
const body = 'https://evil.example/linear.app/x/issue/IN-1 and https://linear.app/acme/issue/IN-42';
|
|
127
|
+
expect(extractLinearRef({ body }, [])?.id).toBe('IN-42');
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe('workspace and issue-number boundaries', () => {
|
|
131
|
+
// Identifiers are unique only within a workspace, so an explicit URL must carry
|
|
132
|
+
// its slug — otherwise a URL for workspace A resolves against credentials for
|
|
133
|
+
// workspace B and comments on B's same-numbered issue.
|
|
134
|
+
it('carries the workspace slug from an absolute URL', () => {
|
|
135
|
+
const ref = extractLinearRef({ body: 'https://linear.app/acme/issue/IN-42' }, []);
|
|
136
|
+
expect(ref?.workspace).toBe('acme');
|
|
137
|
+
});
|
|
138
|
+
it('carries it from a scheme-less URL', () => {
|
|
139
|
+
expect(extractLinearRef({ body: 'see linear.app/beta-corp/issue/IN-42' }, [])?.workspace).toBe('beta-corp');
|
|
140
|
+
});
|
|
141
|
+
it('lower-cases the slug for comparison', () => {
|
|
142
|
+
expect(extractLinearRef({ body: 'https://linear.app/ACME/issue/IN-42' }, [])?.workspace).toBe('acme');
|
|
143
|
+
});
|
|
144
|
+
it('leaves a bare ref with no workspace, since it names none', () => {
|
|
145
|
+
const ref = extractLinearRef({ branch: 'feat/in-42-x' }, ['IN']);
|
|
146
|
+
expect(ref?.id).toBe('IN-42');
|
|
147
|
+
expect(ref?.workspace).toBeUndefined();
|
|
148
|
+
});
|
|
149
|
+
// Without a boundary after the digits, /issue/IN-123abc matched the IN-123
|
|
150
|
+
// prefix and could post to an unrelated issue while bypassing team_keys.
|
|
151
|
+
it('rejects a malformed identifier with a trailing suffix', () => {
|
|
152
|
+
expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-123abc' }, [])).toBeNull();
|
|
153
|
+
});
|
|
154
|
+
it('accepts an identifier followed by a slug', () => {
|
|
155
|
+
expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-123/some-slug' }, [])?.id).toBe('IN-123');
|
|
156
|
+
});
|
|
157
|
+
it('accepts an identifier at the end of the path', () => {
|
|
158
|
+
expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-123' }, [])?.id).toBe('IN-123');
|
|
159
|
+
});
|
|
160
|
+
it('rejects a suffixed identifier in the scheme-less form too', () => {
|
|
161
|
+
expect(extractLinearRef({ body: 'see linear.app/acme/issue/IN-123abc' }, [])).toBeNull();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('punctuation after a URL', () => {
|
|
165
|
+
// A ref at the end of a sentence is still a ref. Requiring a slash or the exact
|
|
166
|
+
// end of the path silently missed these.
|
|
167
|
+
const cases = [
|
|
168
|
+
['Fixes https://linear.app/acme/issue/IN-42.', 'IN-42'],
|
|
169
|
+
['See https://linear.app/acme/issue/IN-42, then merge', 'IN-42'],
|
|
170
|
+
['(https://linear.app/acme/issue/IN-42)', 'IN-42'],
|
|
171
|
+
['see linear.app/acme/issue/IN-42.', 'IN-42'],
|
|
172
|
+
['see linear.app/acme/issue/IN-42, ok', 'IN-42'],
|
|
173
|
+
];
|
|
174
|
+
for (const [body, expected] of cases) {
|
|
175
|
+
it(`resolves ${expected} from ${JSON.stringify(body.slice(0, 46))}`, () => {
|
|
176
|
+
expect(extractLinearRef({ body }, [])?.id).toBe(expected);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
it('still rejects an alphanumeric suffix', () => {
|
|
180
|
+
expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-123abc' }, [])).toBeNull();
|
|
181
|
+
expect(extractLinearRef({ body: 'see linear.app/acme/issue/IN-123abc' }, [])).toBeNull();
|
|
182
|
+
});
|
|
183
|
+
it('still rejects extra digits', () => {
|
|
184
|
+
expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-12/x' }, [])?.id).toBe('IN-12');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
describe('the identifier must be a whole path segment', () => {
|
|
188
|
+
// Two lookahead attempts got this wrong in opposite directions: requiring a
|
|
189
|
+
// slash missed "IN-42." at the end of a sentence; allowing any non-alphanumeric
|
|
190
|
+
// accepted "IN-123-typo" as IN-123. The segment is now parsed whole.
|
|
191
|
+
const rejected = [
|
|
192
|
+
'https://linear.app/acme/issue/IN-123-typo',
|
|
193
|
+
'https://linear.app/acme/issue/IN-123abc',
|
|
194
|
+
'https://linear.app/acme/issue/IN-123_x',
|
|
195
|
+
'see linear.app/acme/issue/IN-123-typo',
|
|
196
|
+
];
|
|
197
|
+
for (const url of rejected) {
|
|
198
|
+
it(`rejects ${url.split('/issue/')[1]}`, () => {
|
|
199
|
+
expect(extractLinearRef({ body: url }, [])).toBeNull();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
const accepted = [
|
|
203
|
+
['https://linear.app/acme/issue/IN-42', 'IN-42'],
|
|
204
|
+
['https://linear.app/acme/issue/IN-42/some-slug', 'IN-42'],
|
|
205
|
+
['Fixes https://linear.app/acme/issue/IN-42.', 'IN-42'],
|
|
206
|
+
['See https://linear.app/acme/issue/IN-42, then merge', 'IN-42'],
|
|
207
|
+
['(https://linear.app/acme/issue/IN-42)', 'IN-42'],
|
|
208
|
+
['see linear.app/acme/issue/IN-42.', 'IN-42'],
|
|
209
|
+
];
|
|
210
|
+
for (const [body, expected] of accepted) {
|
|
211
|
+
it(`accepts ${JSON.stringify(body.slice(0, 44))}`, () => {
|
|
212
|
+
expect(extractLinearRef({ body }, [])?.id).toBe(expected);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
describe('a malformed candidate does not hide a valid one', () => {
|
|
217
|
+
it('skips a malformed scheme-less URL and finds the later valid one', () => {
|
|
218
|
+
const body = 'linear.app/acme/issue/IN-bad and then linear.app/acme/issue/IN-42';
|
|
219
|
+
expect(extractLinearRef({ body }, [])?.id).toBe('IN-42');
|
|
220
|
+
});
|
|
221
|
+
it('skips a malformed absolute URL and finds the later valid one', () => {
|
|
222
|
+
const body = 'https://linear.app/acme/issue/IN-bad https://linear.app/acme/issue/IN-42';
|
|
223
|
+
expect(extractLinearRef({ body }, [])?.id).toBe('IN-42');
|
|
224
|
+
});
|
|
225
|
+
it('returns null when every candidate is malformed', () => {
|
|
226
|
+
expect(extractLinearRef({ body: 'linear.app/a/issue/IN-bad linear.app/b/issue/X' }, [])).toBeNull();
|
|
227
|
+
});
|
|
228
|
+
it('keeps the workspace of the candidate that actually resolved', () => {
|
|
229
|
+
const body = 'linear.app/wrong/issue/IN-bad linear.app/right/issue/IN-42';
|
|
230
|
+
expect(extractLinearRef({ body }, [])?.workspace).toBe('right');
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe('punctuated scheme-less URLs keep their workspace', () => {
|
|
234
|
+
// Regression: `(linear.app/...)` missed the URL matcher, fell through to the
|
|
235
|
+
// bare-ID extractor, and came back without a workspace — silently losing the
|
|
236
|
+
// workspace-mismatch protection that an explicit URL is supposed to carry.
|
|
237
|
+
const wrapped = ['(linear.app/acme/issue/IN-42)', '[linear.app/acme/issue/IN-42]', '"linear.app/acme/issue/IN-42"', 'see: linear.app/acme/issue/IN-42'];
|
|
238
|
+
for (const body of wrapped) {
|
|
239
|
+
it(`keeps the workspace for ${JSON.stringify(body.slice(0, 32))}`, () => {
|
|
240
|
+
const ref = extractLinearRef({ body }, ['IN']);
|
|
241
|
+
expect(ref?.id).toBe('IN-42');
|
|
242
|
+
expect(ref?.workspace).toBe('acme');
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
it('still refuses a lookalike host after punctuation', () => {
|
|
246
|
+
expect(extractLinearRef({ body: '(notlinear.app/acme/issue/IN-42)' }, [])).toBeNull();
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
describe('one-character team keys', () => {
|
|
250
|
+
// Linear permits them; requiring two silently skipped those workspaces.
|
|
251
|
+
it('resolves a one-character key from a URL', () => {
|
|
252
|
+
expect(extractLinearRef({ body: 'https://linear.app/acme/issue/X-42' }, [])?.id).toBe('X-42');
|
|
253
|
+
});
|
|
254
|
+
it('resolves a one-character key as a configured bare ref', () => {
|
|
255
|
+
expect(extractLinearRef({ branch: 'feat/x-42-thing' }, ['X'])?.id).toBe('X-42');
|
|
256
|
+
});
|
|
257
|
+
it('still rejects a bare ref whose key is not configured', () => {
|
|
258
|
+
expect(extractLinearRef({ branch: 'feat/y-42-thing' }, ['X'])).toBeNull();
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
//# sourceMappingURL=linear-ref.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-ref.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-ref.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-test-ref.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-test-ref.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { resolveTestRef, softWrap } from '../commands/linear-test.js';
|
|
3
|
+
describe('resolveTestRef', () => {
|
|
4
|
+
it('takes a named issue verbatim, upper-cased', () => {
|
|
5
|
+
expect(resolveTestRef('eng-42', {}, [])).toEqual({ id: 'ENG-42', source: 'argument' });
|
|
6
|
+
});
|
|
7
|
+
it('lets a named issue bypass the team_keys gate', () => {
|
|
8
|
+
// Typing the ref is unambiguous in a way that finding it in a branch is not,
|
|
9
|
+
// so `crosscheck linear-test ENG-42` works before team_keys is configured.
|
|
10
|
+
expect(resolveTestRef('ENG-42', {}, [])).toEqual({ id: 'ENG-42', source: 'argument' });
|
|
11
|
+
});
|
|
12
|
+
it('prefers the named issue over anything inferable', () => {
|
|
13
|
+
expect(resolveTestRef('ENG-1', { branch: 'feat/in-2269-x' }, ['IN']))
|
|
14
|
+
.toEqual({ id: 'ENG-1', source: 'argument' });
|
|
15
|
+
});
|
|
16
|
+
it('falls back to the branch when no issue is named', () => {
|
|
17
|
+
expect(resolveTestRef(undefined, { branch: 'feat/in-2269-x' }, ['IN']))
|
|
18
|
+
.toEqual({ id: 'IN-2269', source: 'branch' });
|
|
19
|
+
});
|
|
20
|
+
it('falls back to the title', () => {
|
|
21
|
+
expect(resolveTestRef(undefined, { title: 'IN-543: resolver' }, ['IN']))
|
|
22
|
+
.toEqual({ id: 'IN-543', source: 'title' });
|
|
23
|
+
});
|
|
24
|
+
it('still honours the team_keys gate for inferred refs', () => {
|
|
25
|
+
expect(resolveTestRef(undefined, { branch: 'feat/in-2269-x' }, [])).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
it('returns null with nothing to go on', () => {
|
|
28
|
+
expect(resolveTestRef(undefined, {}, ['IN'])).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
it('treats a blank argument as absent', () => {
|
|
31
|
+
expect(resolveTestRef(' ', { branch: 'feat/in-7-x' }, ['IN']))
|
|
32
|
+
.toEqual({ id: 'IN-7', source: 'branch' });
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('softWrap', () => {
|
|
36
|
+
it('leaves a short line alone', () => {
|
|
37
|
+
expect(softWrap('short', 20)).toEqual(['short']);
|
|
38
|
+
});
|
|
39
|
+
it('wraps on whitespace at the boundary', () => {
|
|
40
|
+
expect(softWrap('aaa bbb ccc ddd', 7)).toEqual(['aaa bbb', 'ccc ddd']);
|
|
41
|
+
});
|
|
42
|
+
it('never breaks a token, even one longer than the width', () => {
|
|
43
|
+
const url = 'https://github.com/acme/app/pull/42';
|
|
44
|
+
expect(softWrap(`see ${url} ok`, 10)).toEqual(['see', url, 'ok']);
|
|
45
|
+
});
|
|
46
|
+
it('wraps the annotation without splitting a field', () => {
|
|
47
|
+
const line = '<!-- crosscheck: origin=claude reviewer=codex model=gpt-5.6-terra type=review round=1 verdict=NEEDS_WORK -->';
|
|
48
|
+
for (const chunk of softWrap(line, 80))
|
|
49
|
+
expect(chunk.length).toBeLessThanOrEqual(80);
|
|
50
|
+
expect(softWrap(line, 80).join(' ')).toBe(line);
|
|
51
|
+
});
|
|
52
|
+
it('preserves content exactly when rejoined', () => {
|
|
53
|
+
const line = 'the quick brown fox jumps over the lazy dog';
|
|
54
|
+
expect(softWrap(line, 12).join(' ')).toBe(line);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('onboard Linear decision — regression guards', () => {
|
|
58
|
+
// These encode the two onboarding defects the recheck found. The prompt itself
|
|
59
|
+
// needs a TTY, so these pin the decision rules the prompt implements.
|
|
60
|
+
it('Enter must map to the current mode, not to off', () => {
|
|
61
|
+
const decide = (answer, currentMode) => answer === '1' ? 'off'
|
|
62
|
+
: answer === '2' ? 'api_key'
|
|
63
|
+
: answer === '3' ? 'client_credentials'
|
|
64
|
+
: currentMode;
|
|
65
|
+
expect(decide('', 'client_credentials')).toBe('client_credentials');
|
|
66
|
+
expect(decide('', 'api_key')).toBe('api_key');
|
|
67
|
+
expect(decide('', 'off')).toBe('off');
|
|
68
|
+
expect(decide('1', 'client_credentials')).toBe('off');
|
|
69
|
+
expect(decide('2', 'off')).toBe('api_key');
|
|
70
|
+
expect(decide('3', 'off')).toBe('client_credentials');
|
|
71
|
+
});
|
|
72
|
+
it('an explicit clear must be distinguishable from Enter', () => {
|
|
73
|
+
const resolveKeys = (answer, current) => answer === '-' ? []
|
|
74
|
+
: answer ? answer.split(',').map(k => k.trim().toUpperCase()).filter(Boolean)
|
|
75
|
+
: current;
|
|
76
|
+
expect(resolveKeys('', ['IN'])).toEqual(['IN']);
|
|
77
|
+
expect(resolveKeys('-', ['IN'])).toEqual([]);
|
|
78
|
+
expect(resolveKeys('eng,ops', ['IN'])).toEqual(['ENG', 'OPS']);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=linear-test-ref.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-test-ref.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-test-ref.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-verify.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-verify.test.ts"],"names":[],"mappings":""}
|