@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
package/dist/commands/onboard.js
CHANGED
|
@@ -12,21 +12,28 @@ import { execSync } from 'child_process';
|
|
|
12
12
|
import { promptRepoPicker, promptSinglePicker } from '../lib/repo-picker.js';
|
|
13
13
|
import { DEFAULT_REVIEW_INSTRUCTIONS, DEFAULT_FIX_INSTRUCTIONS, DEFAULT_RECHECK_INSTRUCTIONS, DEFAULT_CONFLICT_RESOLVE_INSTRUCTIONS } from '../lib/workflow.js';
|
|
14
14
|
import { formatRepoWorkflowSteps, readRepoWorkflowStepTypes } from '../lib/repo-workflow.js';
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
import { initLogger, log as fileLog } from '../lib/logger.js';
|
|
16
|
+
import { LogsConfigSchema } from '../config/schema.js';
|
|
17
|
+
import { BUNDLED_SKILL_RECOMMENDATIONS, findCompetingSkill, loadSkillCatalog, RECOMMENDED_SKILL_NAMES, } from '../skills/catalog.js';
|
|
18
|
+
// Effort settings and display hints per quality tier.
|
|
19
|
+
//
|
|
20
|
+
// Cost is output-token cost at 48k output tokens, the measured median for one
|
|
21
|
+
// review. A review is an agentic session, so it takes 10-16 minutes of wall
|
|
22
|
+
// clock (median 643s over 43 logged runs) — the tier changes depth and the
|
|
23
|
+
// subprocess timeout, not seconds-scale latency.
|
|
17
24
|
const QUALITY_TIERS = {
|
|
18
25
|
fast: {
|
|
19
|
-
description: 'quick scan, top issues only (
|
|
26
|
+
description: 'quick scan, top issues only (~$0.24 per review)',
|
|
20
27
|
claude: { model: 'haiku', effort: 'low' },
|
|
21
28
|
codex: { model: 'gpt-5.6-luna', effort: 'low' },
|
|
22
29
|
},
|
|
23
30
|
balanced: {
|
|
24
|
-
description: 'full review, all issues with explanations (
|
|
31
|
+
description: 'full review, all issues with explanations (~$0.72 per review)',
|
|
25
32
|
claude: { model: 'sonnet', effort: 'medium' },
|
|
26
33
|
codex: { model: 'gpt-5.6-terra', effort: 'medium' },
|
|
27
34
|
},
|
|
28
35
|
thorough: {
|
|
29
|
-
description: 'deep multi-pass, security + architecture (
|
|
36
|
+
description: 'deep multi-pass, security + architecture (~$1.20 per review)',
|
|
30
37
|
claude: { model: 'opus', effort: 'max' },
|
|
31
38
|
codex: { model: 'gpt-5.6-sol', effort: 'high' },
|
|
32
39
|
},
|
|
@@ -168,11 +175,36 @@ async function promptAuthorVendor(login, existingAuthorRoutes, opts) {
|
|
|
168
175
|
console.log();
|
|
169
176
|
return idx === 1 ? 'codex' : idx === 2 ? 'both' : 'claude';
|
|
170
177
|
}
|
|
171
|
-
|
|
178
|
+
/**
|
|
179
|
+
* What a re-run of onboard proposes, from the `quality` block as it is WRITTEN.
|
|
180
|
+
*
|
|
181
|
+
* Both fields must come from the raw yaml, never the parsed config: the schema
|
|
182
|
+
* defaults `mode` to smart and `tier` to balanced, so a parsed value can never
|
|
183
|
+
* distinguish "the user chose this" from "this config predates the field".
|
|
184
|
+
* Reading the parsed tier made every existing config look hand-set, so any
|
|
185
|
+
* re-onboard — silently, under `--yes` — opted the user out of the new default.
|
|
186
|
+
*
|
|
187
|
+
* A config that named a tier before smart existed keeps it, on the reasoning
|
|
188
|
+
* that it was a deliberate choice; one that never mentioned quality does not.
|
|
189
|
+
*/
|
|
190
|
+
export function thoroughnessDefaults(rawQuality) {
|
|
191
|
+
return {
|
|
192
|
+
tier: (rawQuality.tier ?? 'balanced'),
|
|
193
|
+
mode: rawQuality.mode === 'fixed' ? 'fixed'
|
|
194
|
+
: rawQuality.mode === 'smart' ? 'smart'
|
|
195
|
+
: rawQuality.tier ? 'fixed' : 'smart',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* One question covering both knobs. `smart` leads because it is the default and
|
|
200
|
+
* the better answer for most repos; the three fixed tiers stay available for
|
|
201
|
+
* anyone who wants one model on everything.
|
|
202
|
+
*/
|
|
203
|
+
async function promptQualityTier(claudeEnabled, codexEnabled, rawQuality, opts) {
|
|
204
|
+
const { tier: fallbackTier, mode: fallbackMode } = thoroughnessDefaults(rawQuality);
|
|
172
205
|
if (opts.yes) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return tier;
|
|
206
|
+
console.log(` Thoroughness: ${chalk.cyan(fallbackMode === 'smart' ? 'smart' : fallbackTier)}`);
|
|
207
|
+
return { tier: fallbackTier, mode: fallbackMode };
|
|
176
208
|
}
|
|
177
209
|
function modelHint(tier) {
|
|
178
210
|
const t = QUALITY_TIERS[tier];
|
|
@@ -184,18 +216,30 @@ async function promptQualityTier(claudeEnabled, codexEnabled, currentTier, opts)
|
|
|
184
216
|
return parts.join(' · ');
|
|
185
217
|
}
|
|
186
218
|
const tiers = ['fast', 'balanced', 'thorough'];
|
|
187
|
-
const items =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
219
|
+
const items = [
|
|
220
|
+
{
|
|
221
|
+
label: 'smart',
|
|
222
|
+
description: 'adjust model + effort per PR (recommended)',
|
|
223
|
+
hint: 'skips lockfiles · docs get review only · auth and migrations get the strongest model · escalates when a round does not resolve',
|
|
224
|
+
},
|
|
225
|
+
...tiers.map(tier => ({
|
|
226
|
+
label: tier,
|
|
227
|
+
description: `${QUALITY_TIERS[tier].description} — same model on every PR`,
|
|
228
|
+
hint: modelHint(tier),
|
|
229
|
+
})),
|
|
230
|
+
];
|
|
231
|
+
// smart is index 0; the three fixed tiers follow, so this is always >= 0.
|
|
232
|
+
const defaultIdx = fallbackMode === 'smart' ? 0 : tiers.indexOf(fallbackTier) + 1;
|
|
193
233
|
const idx = await promptSinglePicker(items, {
|
|
194
|
-
title: 'Review
|
|
195
|
-
defaultIndex: defaultIdx
|
|
234
|
+
title: 'Review thoroughness — how should crosscheck spend its budget?',
|
|
235
|
+
defaultIndex: defaultIdx,
|
|
196
236
|
});
|
|
197
237
|
console.log();
|
|
198
|
-
|
|
238
|
+
// Under smart the tier still matters: it is the fallback whenever a PR's file
|
|
239
|
+
// list can't be read (one-shot commands, API failures).
|
|
240
|
+
return idx === 0
|
|
241
|
+
? { tier: fallbackTier, mode: 'smart' }
|
|
242
|
+
: { tier: tiers[idx - 1], mode: 'fixed' };
|
|
199
243
|
}
|
|
200
244
|
// Exported for tests; `workflowDir` defaults to the user's ~/.crosscheck for runtime callsites.
|
|
201
245
|
export function detectCurrentPreset(workflowDir = join(homedir(), '.crosscheck')) {
|
|
@@ -290,6 +334,29 @@ async function promptWorkflowPipeline(opts) {
|
|
|
290
334
|
return 'review-fix-recheck';
|
|
291
335
|
return 'review-fix';
|
|
292
336
|
}
|
|
337
|
+
// Linear write-back is in beta and not selectable from onboarding yet. The whole
|
|
338
|
+
// attribution ladder is still printed — each rung buys stronger attribution for
|
|
339
|
+
// more setup — because this is where people look for what crosscheck can do, and
|
|
340
|
+
// a single greyed-out "[1] off" advertises nothing. Nothing here is a prompt:
|
|
341
|
+
// whatever the config already has is carried through untouched, so a re-run can
|
|
342
|
+
// never disable an integration this step can no longer configure.
|
|
343
|
+
export async function promptLinear(current, opts) {
|
|
344
|
+
const currentMode = !current?.enabled
|
|
345
|
+
? 'off'
|
|
346
|
+
: current.mode === 'client_credentials' ? 'client_credentials' : 'api_key';
|
|
347
|
+
const decision = { mode: currentMode, teamKeys: current?.teamKeys ?? [] };
|
|
348
|
+
if (opts.yes)
|
|
349
|
+
return decision;
|
|
350
|
+
console.log(chalk.dim(' Post the review verdict onto the Linear issue a PR belongs to.'));
|
|
351
|
+
console.log(` ${chalk.yellow('(beta)')} ${chalk.dim('— preview of what is coming; not selectable here yet.')}\n`);
|
|
352
|
+
console.log(chalk.dim(' [1] off — leave Linear alone'));
|
|
353
|
+
console.log(chalk.dim(' [2] api key — works immediately. Comments post under your own Linear account'));
|
|
354
|
+
console.log(chalk.dim(' [3] workspace app — comments post as crosscheck itself, with its own icon'));
|
|
355
|
+
console.log(chalk.dim(' one app per workspace, ~5 min, needs Linear settings access'));
|
|
356
|
+
console.log(chalk.dim(`\n Current: ${currentMode} (unchanged — edit \`linear:\` in crosscheck.config.yml to set it by hand)`));
|
|
357
|
+
console.log();
|
|
358
|
+
return decision;
|
|
359
|
+
}
|
|
293
360
|
async function promptConnectionType(currentTunnel, opts) {
|
|
294
361
|
if (opts.yes) {
|
|
295
362
|
const backend = currentTunnel ?? 'localhost.run';
|
|
@@ -394,7 +461,7 @@ function buildWorkflowYaml(preset, maxRounds = 1, conflictResolve = false) {
|
|
|
394
461
|
// Writes all onboard decisions to configPath and manages the global workflow.yml.
|
|
395
462
|
// On re-runs, only the fields onboard owns are updated; everything else is preserved.
|
|
396
463
|
export function applyOnboardConfig(configPath, decisions, workflowDir = join(homedir(), '.crosscheck')) {
|
|
397
|
-
const { deployment, login, selectedRepos, selectedOrgs, vendorConfig, qualityTier, pipelinePreset, maxRounds, conflictResolve, tunnelBackend, smeeChannel, cloneProtocol } = decisions;
|
|
464
|
+
const { deployment, login, selectedRepos, selectedOrgs, vendorConfig, qualityTier, qualityMode, enabledSkills, pipelinePreset, maxRounds, conflictResolve, tunnelBackend, smeeChannel, cloneProtocol, linear } = decisions;
|
|
398
465
|
mkdirSync(dirname(configPath), { recursive: true });
|
|
399
466
|
// Load existing config (preserves all custom fields) or start fresh
|
|
400
467
|
const raw = existsSync(configPath)
|
|
@@ -405,6 +472,9 @@ export function applyOnboardConfig(configPath, decisions, workflowDir = join(hom
|
|
|
405
472
|
raw.orgs = selectedOrgs;
|
|
406
473
|
raw.mode = vendorConfig.mode;
|
|
407
474
|
raw.clone_protocol = cloneProtocol;
|
|
475
|
+
if (!raw.skills || typeof raw.skills !== 'object')
|
|
476
|
+
raw.skills = {};
|
|
477
|
+
raw.skills.enabled = enabledSkills;
|
|
408
478
|
// Repos. Per-repo workflow depth is NOT stored here — it lives in standalone
|
|
409
479
|
// files under ~/.crosscheck/workflows/ (written by `crosscheck alter`).
|
|
410
480
|
raw.repos = selectedRepos.map(r => {
|
|
@@ -469,16 +539,67 @@ export function applyOnboardConfig(configPath, decisions, workflowDir = join(hom
|
|
|
469
539
|
tunnelObj.backend = tunnelBackend;
|
|
470
540
|
if (tunnelBackend === 'smee' && smeeChannel)
|
|
471
541
|
tunnelObj.smee_channel = smeeChannel;
|
|
542
|
+
// ── Linear write-back ───────────────────────────────────────────────────────
|
|
543
|
+
// Only touched when onboard actually asked. Auth env-var names and the signature
|
|
544
|
+
// template are left alone so hand-edits survive a re-run.
|
|
545
|
+
if (linear) {
|
|
546
|
+
if (!raw.linear || typeof raw.linear !== 'object')
|
|
547
|
+
raw.linear = {};
|
|
548
|
+
const linearObj = raw.linear;
|
|
549
|
+
linearObj.enabled = linear.mode !== 'off';
|
|
550
|
+
if (linear.mode !== 'off') {
|
|
551
|
+
if (!linearObj.auth || typeof linearObj.auth !== 'object')
|
|
552
|
+
linearObj.auth = {};
|
|
553
|
+
linearObj.auth.mode = linear.mode;
|
|
554
|
+
// Write the selection even when empty — otherwise the `-` clear silently
|
|
555
|
+
// leaves the previous keys in the file and nothing appears to happen.
|
|
556
|
+
linearObj.team_keys = linear.teamKeys;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
472
559
|
// ── Quality tier + per-vendor effort ────────────────────────────────────────
|
|
473
|
-
//
|
|
474
|
-
//
|
|
560
|
+
// An explicit vendors.*.model outranks everything in resolveClaudeModel /
|
|
561
|
+
// resolveCodexModel. Under `fixed` that is exactly right — one model on every
|
|
562
|
+
// PR is what the user asked for. Under `smart` it is fatal: a pinned model
|
|
563
|
+
// makes per-PR tier selection a silent no-op, so the pin is cleared and the
|
|
564
|
+
// model resolves from the strategy instead.
|
|
565
|
+
//
|
|
566
|
+
// `tier` is written in both modes. Under smart it is the fallback for PRs
|
|
567
|
+
// whose file list can't be read (one-shot commands, API failures).
|
|
475
568
|
if (!raw.quality || typeof raw.quality !== 'object')
|
|
476
569
|
raw.quality = {};
|
|
477
|
-
raw.quality
|
|
570
|
+
const qualityRaw = raw.quality;
|
|
571
|
+
qualityRaw.tier = qualityTier;
|
|
572
|
+
qualityRaw.mode = qualityMode;
|
|
478
573
|
const tierCfg = QUALITY_TIERS[qualityTier];
|
|
479
574
|
vendors.claude.effort = tierCfg.claude.effort;
|
|
480
|
-
vendors.codex.model = tierCfg.codex.model;
|
|
481
575
|
vendors.codex.effort = tierCfg.codex.effort;
|
|
576
|
+
if (qualityMode === 'smart') {
|
|
577
|
+
// A pinned model outranks the strategy, so smart mode cannot work while one
|
|
578
|
+
// is set. Report every removal — silently discarding user-authored config
|
|
579
|
+
// (especially under `--yes`) is worse than leaving smart mode inert.
|
|
580
|
+
for (const vendor of ['codex', 'claude']) {
|
|
581
|
+
const pinned = vendors[vendor].model;
|
|
582
|
+
if (pinned) {
|
|
583
|
+
console.log(chalk.yellow(` cleared vendors.${vendor}.model (${String(pinned)}) — a pinned model overrides smart mode`));
|
|
584
|
+
delete vendors[vendor].model;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
// Codex needs an explicit per-tier map to vary by tier at all: with no
|
|
588
|
+
// `model` and no `model_tiers`, resolveCodexModel returns the CLI's own
|
|
589
|
+
// 'default' under subscription auth, so fast/balanced/thorough would be the
|
|
590
|
+
// same run. `model_tiers` is honored ahead of the auth check, so writing it
|
|
591
|
+
// is what makes smart mode mean anything for codex. Claude needs no
|
|
592
|
+
// equivalent — its tier map applies under either auth.
|
|
593
|
+
;
|
|
594
|
+
vendors.codex.model_tiers = {
|
|
595
|
+
fast: QUALITY_TIERS.fast.codex.model,
|
|
596
|
+
balanced: QUALITY_TIERS.balanced.codex.model,
|
|
597
|
+
thorough: QUALITY_TIERS.thorough.codex.model,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
vendors.codex.model = tierCfg.codex.model;
|
|
602
|
+
}
|
|
482
603
|
// ── Fix delivery mechanism (operational config, not pipeline logic) ──────────
|
|
483
604
|
// Pipeline steps and trigger conditions live in workflow.yml.
|
|
484
605
|
// config.yml only retains how fixes land on the PR (commit / pull_request / comment).
|
|
@@ -567,17 +688,52 @@ export async function runOnboard(opts = {}) {
|
|
|
567
688
|
console.error(chalk.dim('Run crosscheck init and edit crosscheck.config.yml manually.'));
|
|
568
689
|
process.exit(1);
|
|
569
690
|
}
|
|
691
|
+
// Onboard runs before a config exists on a fresh install, so it initialises
|
|
692
|
+
// logging from the schema defaults (enabled, local files only) unless an
|
|
693
|
+
// existing config already opted out or set a custom retention — otherwise
|
|
694
|
+
// re-running onboard on an existing `logs.enabled: false` install would start
|
|
695
|
+
// writing logs again, and could delete logs older than the default retention.
|
|
696
|
+
const preOnboardConfigPath = opts.config ?? resolveConfigPath() ?? join(homedir(), '.crosscheck', 'config.yml');
|
|
697
|
+
const preOnboardConfig = existsSync(preOnboardConfigPath) ? loadConfig(preOnboardConfigPath) : null;
|
|
698
|
+
initLogger(preOnboardConfig?.logs ?? LogsConfigSchema.parse({}));
|
|
699
|
+
fileLog({ level: 'info', event: 'onboard_started', command: 'onboard' });
|
|
700
|
+
// Every path out of onboard reports an outcome, so `started` minus `completed`
|
|
701
|
+
// is the abandonment count rather than a number that also absorbs crashes.
|
|
702
|
+
const onboardAbandoned = (stage) => {
|
|
703
|
+
fileLog({ level: 'info', event: 'onboard_completed', command: 'onboard', outcome: 'abandoned', stage });
|
|
704
|
+
};
|
|
570
705
|
console.log(chalk.bold('\ncrosscheck onboard\n'));
|
|
571
706
|
// ── Step 1: Auth check ─────────────────────────────────────────────────────
|
|
572
707
|
console.log(chalk.bold('Step 1 — environment check'));
|
|
708
|
+
console.log(chalk.dim(' Confirms the CLIs and auth crosscheck needs before it writes anything.'));
|
|
573
709
|
const env = await checkEnv();
|
|
574
|
-
if (!env.ok)
|
|
710
|
+
if (!env.ok) {
|
|
711
|
+
onboardAbandoned('environment_check');
|
|
575
712
|
process.exit(1);
|
|
713
|
+
}
|
|
576
714
|
console.log();
|
|
577
715
|
// ── Step 2: Deployment mode ────────────────────────────────────────────────
|
|
578
|
-
console.log(chalk.bold('Step 2 —
|
|
579
|
-
|
|
580
|
-
const
|
|
716
|
+
console.log(chalk.bold('Step 2 — who you review for'));
|
|
717
|
+
console.log(chalk.dim(' Personal reviews only PRs you author. Team reviews every author in the org.'));
|
|
718
|
+
const configPath = preOnboardConfigPath;
|
|
719
|
+
const existingConfig = preOnboardConfig;
|
|
720
|
+
// Parsed config always carries a `tier` and a `mode` (schema defaults), so
|
|
721
|
+
// read the raw file to tell an explicit choice from a pre-`mode` config.
|
|
722
|
+
const rawQuality = (() => {
|
|
723
|
+
if (!existsSync(configPath))
|
|
724
|
+
return {};
|
|
725
|
+
try {
|
|
726
|
+
const raw = yaml.load(readFileSync(configPath, 'utf8'));
|
|
727
|
+
const quality = raw?.quality;
|
|
728
|
+
return {
|
|
729
|
+
...(typeof quality?.tier === 'string' && { tier: quality.tier }),
|
|
730
|
+
...(typeof quality?.mode === 'string' && { mode: quality.mode }),
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
catch {
|
|
734
|
+
return {};
|
|
735
|
+
}
|
|
736
|
+
})();
|
|
581
737
|
const currentDeployment = existingConfig?.deployment;
|
|
582
738
|
let deployment;
|
|
583
739
|
if (opts.personal) {
|
|
@@ -606,13 +762,15 @@ export async function runOnboard(opts = {}) {
|
|
|
606
762
|
}
|
|
607
763
|
console.log();
|
|
608
764
|
// ── Step 3: Repo selection (hierarchical: namespace → repos) ───────────────
|
|
609
|
-
console.log(chalk.bold('Step 3 —
|
|
765
|
+
console.log(chalk.bold('Step 3 — repos to watch'));
|
|
766
|
+
console.log(chalk.dim(' Only these get a webhook, and only these get reviewed.'));
|
|
610
767
|
let token;
|
|
611
768
|
try {
|
|
612
769
|
token = getGithubToken();
|
|
613
770
|
}
|
|
614
771
|
catch (err) {
|
|
615
772
|
console.error(chalk.red(err instanceof Error ? err.message : String(err)));
|
|
773
|
+
onboardAbandoned('github_token');
|
|
616
774
|
process.exit(1);
|
|
617
775
|
}
|
|
618
776
|
const login = detectGitHubLogin() ?? '';
|
|
@@ -738,33 +896,70 @@ export async function runOnboard(opts = {}) {
|
|
|
738
896
|
}
|
|
739
897
|
console.log();
|
|
740
898
|
// ── Step 4: Review mode (cross-vendor vs single-vendor) ───────────────────
|
|
741
|
-
console.log(chalk.bold('Step 4 —
|
|
899
|
+
console.log(chalk.bold('Step 4 — who reviews'));
|
|
900
|
+
console.log(chalk.dim(' Cross-vendor keeps the reviewer independent of the author — self-review is where early victory hides.'));
|
|
742
901
|
const vendorConfig = await promptVendorMode(env.claudeOk, env.codexOk, existingConfig?.mode, existingConfig?.vendors?.claude?.enabled ?? true, existingConfig?.vendors?.codex?.enabled ?? true, opts);
|
|
743
902
|
console.log();
|
|
744
903
|
// ── Step 5: Primary author (cross-vendor + personal only) ───────────────────
|
|
745
|
-
|
|
904
|
+
// Header and description live inside the branch: printing them unconditionally
|
|
905
|
+
// and then skipping the prompt left a heading describing a question never asked.
|
|
746
906
|
let authorVendor = 'both';
|
|
747
907
|
if (vendorConfig.mode === 'cross-vendor' && deployment === 'personal') {
|
|
908
|
+
console.log(chalk.bold('Step 5 — your coding agent'));
|
|
909
|
+
console.log(chalk.dim(' Routes your PRs to the other vendor even when the attribution footer is missing.'));
|
|
748
910
|
const existingRoutes = existingConfig?.routing?.author_routes ?? null;
|
|
749
911
|
authorVendor = await promptAuthorVendor(login, existingRoutes, opts);
|
|
750
912
|
}
|
|
751
913
|
else {
|
|
752
914
|
const reason = vendorConfig.mode === 'single-vendor' ? 'single-vendor mode' : 'team mode';
|
|
753
|
-
console.log(chalk.dim(`
|
|
915
|
+
console.log(chalk.bold('Step 5 — your coding agent') + chalk.dim(` — skipped, not applicable in ${reason}.`));
|
|
754
916
|
console.log();
|
|
755
917
|
}
|
|
756
|
-
// ── Step 6: Review
|
|
757
|
-
console.log(chalk.bold('Step 6 — review
|
|
758
|
-
|
|
918
|
+
// ── Step 6: Review thoroughness ───────────────────────────────────────────
|
|
919
|
+
console.log(chalk.bold('Step 6 — review thoroughness'));
|
|
920
|
+
console.log(chalk.dim(' Decides how much model budget each PR earns.'));
|
|
921
|
+
const thoroughness = await promptQualityTier(vendorConfig.claudeEnabled, vendorConfig.codexEnabled, rawQuality, opts);
|
|
922
|
+
const qualityTier = thoroughness.tier;
|
|
923
|
+
const qualityMode = thoroughness.mode;
|
|
924
|
+
console.log();
|
|
925
|
+
// ── Step 6.5: Agent skills ────────────────────────────────────────────────
|
|
926
|
+
console.log(chalk.bold('Step 7 — agent skills'));
|
|
927
|
+
console.log(chalk.dim(' Extra review practices the agent can draw on. It picks what fits each step.'));
|
|
928
|
+
const bundledSkills = loadSkillCatalog();
|
|
929
|
+
const initialSkills = existingConfig
|
|
930
|
+
? existingConfig.skills.enabled
|
|
931
|
+
: [...RECOMMENDED_SKILL_NAMES];
|
|
932
|
+
console.log(chalk.dim(' Base-branch AGENTS.md and CLAUDE.md practices take precedence over Crosscheck skill advice.'));
|
|
933
|
+
const enabledSkills = opts.yes
|
|
934
|
+
? initialSkills
|
|
935
|
+
: await promptRepoPicker(bundledSkills.map(skill => skill.name), {
|
|
936
|
+
title: 'Enable skills for coding agents:',
|
|
937
|
+
initialSelected: initialSkills,
|
|
938
|
+
getDescription: name => {
|
|
939
|
+
const skill = bundledSkills.find(candidate => candidate.name === name);
|
|
940
|
+
return skill ? `(by @${skill.author}, ${skill.license})` : '';
|
|
941
|
+
},
|
|
942
|
+
getHint: (name, selected) => {
|
|
943
|
+
const skill = bundledSkills.find(candidate => candidate.name === name);
|
|
944
|
+
const recommendation = BUNDLED_SKILL_RECOMMENDATIONS[name] ?? skill?.description ?? '';
|
|
945
|
+
const competitor = findCompetingSkill(name, selected);
|
|
946
|
+
return competitor ? `${recommendation} Competes with selected ${competitor}.` : recommendation;
|
|
947
|
+
},
|
|
948
|
+
getSelectionWarning: (name, selected) => {
|
|
949
|
+
const competitor = findCompetingSkill(name, selected);
|
|
950
|
+
return competitor ? `${name} competes with selected ${competitor}; deselect it first.` : undefined;
|
|
951
|
+
},
|
|
952
|
+
});
|
|
953
|
+
console.log(` Enabled: ${enabledSkills.length > 0 ? enabledSkills.map(name => chalk.cyan(name)).join(', ') : chalk.dim('none')}`);
|
|
759
954
|
console.log();
|
|
760
955
|
// ── Step 7: Workflow pipeline ──────────────────────────────────────────────
|
|
761
|
-
console.log(chalk.bold('Step
|
|
956
|
+
console.log(chalk.bold('Step 8 — what happens after a review'));
|
|
957
|
+
console.log(chalk.dim(' Review alone posts findings. Adding fix and re-check closes the loop before merge.'));
|
|
762
958
|
const pipelinePreset = await promptWorkflowPipeline(opts);
|
|
763
959
|
console.log();
|
|
764
|
-
//
|
|
960
|
+
// Sub-prompt of step 8 — only meaningful when a recheck step exists.
|
|
765
961
|
let maxRounds;
|
|
766
962
|
if (pipelinePreset === 'review-fix-recheck') {
|
|
767
|
-
console.log(chalk.bold('Step 7.5 — fix → re-check rounds'));
|
|
768
963
|
const globalWorkflowPath = join(homedir(), '.crosscheck', 'workflow.yml');
|
|
769
964
|
let currentMaxRounds;
|
|
770
965
|
if (existsSync(globalWorkflowPath)) {
|
|
@@ -778,13 +973,14 @@ export async function runOnboard(opts = {}) {
|
|
|
778
973
|
maxRounds = await promptMaxRounds(currentMaxRounds, opts);
|
|
779
974
|
console.log();
|
|
780
975
|
}
|
|
781
|
-
//
|
|
782
|
-
|
|
976
|
+
// Sub-prompt of step 8 — orthogonal to depth, so it is asked for any preset
|
|
977
|
+
// that permits code modification.
|
|
783
978
|
const currentConflictResolve = detectConflictResolveEnabled();
|
|
784
979
|
const conflictResolve = await promptConflictResolve(currentConflictResolve, opts);
|
|
785
980
|
console.log();
|
|
786
|
-
// ── Step
|
|
787
|
-
console.log(chalk.bold('Step
|
|
981
|
+
// ── Step 9: Connection + clone protocol ───────────────────────────────────
|
|
982
|
+
console.log(chalk.bold('Step 9 — connection'));
|
|
983
|
+
console.log(chalk.dim(' How GitHub reaches this machine with PR events.'));
|
|
788
984
|
const currentTunnel = existingConfig?.tunnel?.backend;
|
|
789
985
|
let tunnelBackend = await promptConnectionType(currentTunnel, opts);
|
|
790
986
|
let smeeChannel = existingConfig?.tunnel?.smee_channel ?? '';
|
|
@@ -810,20 +1006,28 @@ export async function runOnboard(opts = {}) {
|
|
|
810
1006
|
}
|
|
811
1007
|
}
|
|
812
1008
|
console.log();
|
|
813
|
-
//
|
|
814
|
-
console.log(chalk.bold('Step 9 — clone protocol'));
|
|
1009
|
+
// Sub-prompt of step 9 — same plumbing concern as the tunnel.
|
|
815
1010
|
const cloneProtocol = await promptCloneProtocol(existingConfig?.clone_protocol, opts);
|
|
1011
|
+
console.log();
|
|
1012
|
+
// ── Step 10: Linear write-back (beta, opt-in) ─────────────────────────────
|
|
1013
|
+
console.log(chalk.bold(`Step 10 — Linear write-back ${chalk.yellow('(beta)')}`));
|
|
1014
|
+
const linear = await promptLinear({
|
|
1015
|
+
enabled: existingConfig?.linear?.enabled,
|
|
1016
|
+
mode: existingConfig?.linear?.auth?.mode,
|
|
1017
|
+
teamKeys: existingConfig?.linear?.team_keys,
|
|
1018
|
+
}, opts);
|
|
816
1019
|
// ── Step 10: Confirm and write ─────────────────────────────────────────────
|
|
817
1020
|
const selectedRepoWorkflowOverrides = selectedRepos.flatMap(repoKey => {
|
|
818
1021
|
const [owner, name] = repoKey.split('/');
|
|
819
1022
|
const steps = readRepoWorkflowStepTypes(owner, name);
|
|
820
1023
|
return steps ? [`${repoKey}: ${formatRepoWorkflowSteps(steps)}`] : [];
|
|
821
1024
|
});
|
|
822
|
-
console.log(chalk.bold('Step
|
|
1025
|
+
console.log(chalk.bold('Step 11 — review and write config'));
|
|
823
1026
|
console.log();
|
|
824
1027
|
console.log(` deployment ${chalk.cyan(deployment)}`);
|
|
825
1028
|
console.log(` connection ${chalk.cyan(tunnelBackend)}${tunnelBackend === 'smee' && smeeChannel ? chalk.dim(` (${smeeChannel})`) : ''}`);
|
|
826
1029
|
console.log(` clone ${chalk.cyan(cloneProtocol)}`);
|
|
1030
|
+
console.log(` linear ${chalk.cyan(linear.mode === 'off' ? 'off' : linear.mode)}${linear.mode !== 'off' && linear.teamKeys.length > 0 ? chalk.dim(` (${linear.teamKeys.join(', ')})`) : ''} ${chalk.yellow('(beta)')}`);
|
|
827
1031
|
console.log(` mode ${chalk.cyan(vendorConfig.mode)}`);
|
|
828
1032
|
if (vendorConfig.mode === 'single-vendor') {
|
|
829
1033
|
const activeVendor = vendorConfig.claudeEnabled ? 'claude' : 'codex';
|
|
@@ -835,7 +1039,10 @@ export async function runOnboard(opts = {}) {
|
|
|
835
1039
|
: `${authorVendor} → reviewed by ${authorVendor === 'claude' ? 'codex' : 'claude'}`;
|
|
836
1040
|
console.log(` routing ${chalk.cyan(routingLabel)}`);
|
|
837
1041
|
}
|
|
838
|
-
console.log(
|
|
1042
|
+
console.log(qualityMode === 'smart'
|
|
1043
|
+
? ` quality ${chalk.cyan('smart')}${chalk.dim(` — model + effort adjusted per PR (${qualityTier} fallback)`)}`
|
|
1044
|
+
: ` quality ${chalk.cyan(qualityTier)}${chalk.dim(` — ${QUALITY_TIERS[qualityTier].description.split(' ')[0]}`)}`);
|
|
1045
|
+
console.log(` skills ${enabledSkills.length > 0 ? enabledSkills.map(name => chalk.cyan(name)).join(', ') : chalk.dim('none')}`);
|
|
839
1046
|
console.log(` pipeline ${chalk.cyan(pipelinePreset)}`);
|
|
840
1047
|
if (pipelinePreset === 'review-fix-recheck') {
|
|
841
1048
|
console.log(` max rounds ${chalk.cyan(String(maxRounds ?? 1))}`);
|
|
@@ -860,6 +1067,7 @@ export async function runOnboard(opts = {}) {
|
|
|
860
1067
|
const confirm = await ask(` Write to config? [Y/n]: `);
|
|
861
1068
|
if (confirm.toLowerCase() === 'n') {
|
|
862
1069
|
console.log(chalk.dim(' Aborted — no changes written.'));
|
|
1070
|
+
onboardAbandoned('config_write_declined');
|
|
863
1071
|
return;
|
|
864
1072
|
}
|
|
865
1073
|
}
|
|
@@ -873,12 +1081,15 @@ export async function runOnboard(opts = {}) {
|
|
|
873
1081
|
vendorConfig,
|
|
874
1082
|
authorVendor,
|
|
875
1083
|
qualityTier,
|
|
1084
|
+
qualityMode,
|
|
1085
|
+
enabledSkills,
|
|
876
1086
|
pipelinePreset,
|
|
877
1087
|
maxRounds,
|
|
878
1088
|
conflictResolve,
|
|
879
1089
|
tunnelBackend,
|
|
880
1090
|
smeeChannel,
|
|
881
1091
|
cloneProtocol,
|
|
1092
|
+
linear,
|
|
882
1093
|
});
|
|
883
1094
|
console.log(chalk.green(` ✓ config written to ${configPath}`));
|
|
884
1095
|
if (!hadWorkflow) {
|
|
@@ -888,6 +1099,13 @@ export async function runOnboard(opts = {}) {
|
|
|
888
1099
|
console.log(chalk.dim(` keeping existing workflow at ${globalWorkflowPath}`));
|
|
889
1100
|
}
|
|
890
1101
|
console.log();
|
|
1102
|
+
fileLog({
|
|
1103
|
+
level: 'info', event: 'onboard_completed', command: 'onboard', outcome: 'completed',
|
|
1104
|
+
// Shape of the setup, never its contents: counts, not repo or org names.
|
|
1105
|
+
deployment, repos: selectedRepos.length, orgs: selectedOrgs.length,
|
|
1106
|
+
quality_tier: qualityTier, quality_mode: qualityMode, pipeline: pipelinePreset,
|
|
1107
|
+
tunnel: tunnelBackend, linear: linear !== null,
|
|
1108
|
+
});
|
|
891
1109
|
// ── Next step hint ─────────────────────────────────────────────────────────
|
|
892
1110
|
console.log(chalk.dim(' Run crosscheck watch to start monitoring.'));
|
|
893
1111
|
const alterExample = selectedRepos[0] ?? 'owner/repo';
|