@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,435 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PR Analyzer - Analyze PR complexity and suggest review approach.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python pr-analyzer.py [--diff-file FILE] [--stats]
|
|
7
|
+
|
|
8
|
+
Or pipe diff directly:
|
|
9
|
+
git diff main...HEAD | python pr-analyzer.py
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import re
|
|
15
|
+
import argparse
|
|
16
|
+
from collections import defaultdict
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import List, Dict, Optional
|
|
19
|
+
|
|
20
|
+
RISK_NO_TESTS = "NO_TEST_CHANGES"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class FileStats:
|
|
25
|
+
"""Statistics for a single file."""
|
|
26
|
+
filename: str
|
|
27
|
+
additions: int = 0
|
|
28
|
+
deletions: int = 0
|
|
29
|
+
is_test: bool = False
|
|
30
|
+
is_config: bool = False
|
|
31
|
+
language: str = "unknown"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class PRAnalysis:
|
|
36
|
+
"""Complete PR analysis results."""
|
|
37
|
+
total_files: int
|
|
38
|
+
total_additions: int
|
|
39
|
+
total_deletions: int
|
|
40
|
+
files: List[FileStats]
|
|
41
|
+
complexity_score: float
|
|
42
|
+
size_category: str
|
|
43
|
+
estimated_review_time: int
|
|
44
|
+
risk_factors: List[str]
|
|
45
|
+
suggestions: List[str]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def detect_language(filename: str) -> str:
|
|
49
|
+
"""Detect programming language from filename."""
|
|
50
|
+
_, ext = os.path.splitext(filename)
|
|
51
|
+
extensions = {
|
|
52
|
+
'.py': 'Python',
|
|
53
|
+
'.js': 'JavaScript',
|
|
54
|
+
'.ts': 'TypeScript',
|
|
55
|
+
'.tsx': 'TypeScript/React',
|
|
56
|
+
'.jsx': 'JavaScript/React',
|
|
57
|
+
'.rs': 'Rust',
|
|
58
|
+
'.go': 'Go',
|
|
59
|
+
'.c': 'C',
|
|
60
|
+
'.h': 'C/C++',
|
|
61
|
+
'.cpp': 'C++',
|
|
62
|
+
'.hpp': 'C++',
|
|
63
|
+
'.cc': 'C++',
|
|
64
|
+
'.cxx': 'C++',
|
|
65
|
+
'.hh': 'C++',
|
|
66
|
+
'.hxx': 'C++',
|
|
67
|
+
'.java': 'Java',
|
|
68
|
+
'.kt': 'Kotlin',
|
|
69
|
+
'.swift': 'Swift',
|
|
70
|
+
'.rb': 'Ruby',
|
|
71
|
+
'.php': 'PHP',
|
|
72
|
+
'.cs': 'C#',
|
|
73
|
+
'.vue': 'Vue',
|
|
74
|
+
'.svelte': 'Svelte',
|
|
75
|
+
'.sql': 'SQL',
|
|
76
|
+
'.md': 'Markdown',
|
|
77
|
+
'.json': 'JSON',
|
|
78
|
+
'.yaml': 'YAML',
|
|
79
|
+
'.yml': 'YAML',
|
|
80
|
+
'.toml': 'TOML',
|
|
81
|
+
'.css': 'CSS',
|
|
82
|
+
'.scss': 'SCSS',
|
|
83
|
+
'.less': 'Less',
|
|
84
|
+
'.html': 'HTML',
|
|
85
|
+
'.zig': 'Zig',
|
|
86
|
+
'.ex': 'Elixir',
|
|
87
|
+
'.exs': 'Elixir',
|
|
88
|
+
'.erl': 'Erlang',
|
|
89
|
+
'.scala': 'Scala',
|
|
90
|
+
'.lua': 'Lua',
|
|
91
|
+
}
|
|
92
|
+
return extensions.get(ext.lower(), 'unknown')
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def is_test_file(filename: str) -> bool:
|
|
96
|
+
"""Check if file is a test file."""
|
|
97
|
+
test_patterns = [
|
|
98
|
+
r'(?:^|/)test_[^/]+\.py$', # Python: test_handler.py (anchored to path segment)
|
|
99
|
+
r'[^/]+_test\.', # *_test.<ext> โ Rust, Go, etc.
|
|
100
|
+
r'[^/]+\.test\.(js|ts|tsx|jsx)$', # JS/TS: handler.test.ts
|
|
101
|
+
r'[^/]+\.spec\.(js|ts|tsx|jsx)$', # JS/TS: handler.spec.ts
|
|
102
|
+
r'(?:^|/)tests?/', # tests/ or test/ directory
|
|
103
|
+
r'(?:^|/)__tests__/', # __tests__/ directory
|
|
104
|
+
r'(?:^|/)spec/', # spec/ directory (Ruby, etc.)
|
|
105
|
+
]
|
|
106
|
+
return any(re.search(p, filename) for p in test_patterns)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def is_config_file(filename: str) -> bool:
|
|
110
|
+
"""Check if file is a configuration file.
|
|
111
|
+
|
|
112
|
+
Uses explicit known-name matching to avoid flagging data files
|
|
113
|
+
like data.json, openapi.yaml, or swagger.json as config.
|
|
114
|
+
"""
|
|
115
|
+
basename = os.path.basename(filename)
|
|
116
|
+
|
|
117
|
+
# Env files (.env, .env.local, .env.production, โฆ)
|
|
118
|
+
if basename.startswith('.env'):
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
# Known config filenames (exact match)
|
|
122
|
+
known_config_names = {
|
|
123
|
+
'package.json', 'package-lock.json',
|
|
124
|
+
'tsconfig.json', 'jsconfig.json',
|
|
125
|
+
'babel.config.json', 'babel.config.js',
|
|
126
|
+
'webpack.config.js', 'webpack.config.ts',
|
|
127
|
+
'rollup.config.js', 'vite.config.ts', 'vite.config.js',
|
|
128
|
+
'.eslintrc.json', '.eslintrc.js', '.eslintrc.yml',
|
|
129
|
+
'.prettierrc.json', '.prettierrc.yml', '.prettierrc.js',
|
|
130
|
+
'jest.config.js', 'jest.config.ts',
|
|
131
|
+
'vitest.config.ts', 'vitest.config.js',
|
|
132
|
+
'tailwind.config.js', 'tailwind.config.ts',
|
|
133
|
+
'postcss.config.js',
|
|
134
|
+
'docker-compose.yml', 'docker-compose.yaml',
|
|
135
|
+
'Dockerfile',
|
|
136
|
+
'Makefile', 'CMakeLists.txt',
|
|
137
|
+
'pyproject.toml', 'poetry.toml', 'Pipfile',
|
|
138
|
+
'setup.cfg', 'setup.py', 'tox.ini',
|
|
139
|
+
'Cargo.toml', 'Cargo.lock',
|
|
140
|
+
'go.mod', 'go.sum',
|
|
141
|
+
'Gemfile', 'Gemfile.lock',
|
|
142
|
+
'composer.json', 'composer.lock',
|
|
143
|
+
'Podfile', 'Package.swift',
|
|
144
|
+
'.gitignore', '.gitattributes',
|
|
145
|
+
'gradle.properties', 'build.gradle', 'build.gradle.kts',
|
|
146
|
+
'settings.gradle', 'settings.gradle.kts',
|
|
147
|
+
}
|
|
148
|
+
if basename in known_config_names:
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
# Known config path patterns
|
|
152
|
+
config_path_patterns = [
|
|
153
|
+
r'\.github/workflows/[^/]+\.ya?ml$',
|
|
154
|
+
r'\.vscode/',
|
|
155
|
+
r'\.idea/',
|
|
156
|
+
]
|
|
157
|
+
if any(re.search(p, filename) for p in config_path_patterns):
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
# Files with "config" in the name (e.g. app.config.ts, database_config.yml)
|
|
161
|
+
if re.search(r'config\.', basename):
|
|
162
|
+
return True
|
|
163
|
+
|
|
164
|
+
# Files under a config/ directory
|
|
165
|
+
if re.search(r'(?:^|/)config/', filename):
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def parse_diff(diff_content: str) -> List[FileStats]:
|
|
172
|
+
"""Parse git diff output and extract file statistics."""
|
|
173
|
+
files = []
|
|
174
|
+
current_file = None
|
|
175
|
+
|
|
176
|
+
for line in diff_content.split('\n'):
|
|
177
|
+
# New file header
|
|
178
|
+
if line.startswith('diff --git'):
|
|
179
|
+
if current_file:
|
|
180
|
+
files.append(current_file)
|
|
181
|
+
# "diff --git a/<path> b/<path>" โ match the b/ side via a
|
|
182
|
+
# backreference so a literal "b/" inside paths like lib/, web/ or
|
|
183
|
+
# db/ can't be mistaken for the prefix. Renames have differing
|
|
184
|
+
# paths, so fall back to the b/ side after the separating space.
|
|
185
|
+
match = re.match(r'diff --git a/(.+?) b/\1', line)
|
|
186
|
+
if not match:
|
|
187
|
+
match = re.search(r' b/(.+)$', line)
|
|
188
|
+
if match:
|
|
189
|
+
filename = match.group(1)
|
|
190
|
+
current_file = FileStats(
|
|
191
|
+
filename=filename,
|
|
192
|
+
language=detect_language(filename),
|
|
193
|
+
is_test=is_test_file(filename),
|
|
194
|
+
is_config=is_config_file(filename),
|
|
195
|
+
)
|
|
196
|
+
else:
|
|
197
|
+
current_file = None
|
|
198
|
+
elif current_file:
|
|
199
|
+
if line.startswith('+') and not line.startswith('+++'):
|
|
200
|
+
current_file.additions += 1
|
|
201
|
+
elif line.startswith('-') and not line.startswith('---'):
|
|
202
|
+
current_file.deletions += 1
|
|
203
|
+
|
|
204
|
+
if current_file:
|
|
205
|
+
files.append(current_file)
|
|
206
|
+
|
|
207
|
+
return files
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def calculate_complexity(files: List[FileStats]) -> float:
|
|
211
|
+
"""Calculate complexity score (0-1 scale)."""
|
|
212
|
+
if not files:
|
|
213
|
+
return 0.0
|
|
214
|
+
|
|
215
|
+
total_changes = sum(f.additions + f.deletions for f in files)
|
|
216
|
+
|
|
217
|
+
# Base complexity from size
|
|
218
|
+
size_factor = min(total_changes / 1000, 1.0)
|
|
219
|
+
|
|
220
|
+
# Factor for number of files
|
|
221
|
+
file_factor = min(len(files) / 20, 1.0)
|
|
222
|
+
|
|
223
|
+
# Factor for non-test code ratio
|
|
224
|
+
test_lines = sum(f.additions + f.deletions for f in files if f.is_test)
|
|
225
|
+
non_test_ratio = 1 - (test_lines / max(total_changes, 1))
|
|
226
|
+
|
|
227
|
+
# Factor for language diversity
|
|
228
|
+
languages = set(f.language for f in files if f.language != 'unknown')
|
|
229
|
+
lang_factor = min(len(languages) / 5, 1.0)
|
|
230
|
+
|
|
231
|
+
complexity = (
|
|
232
|
+
size_factor * 0.4 +
|
|
233
|
+
file_factor * 0.2 +
|
|
234
|
+
non_test_ratio * 0.2 +
|
|
235
|
+
lang_factor * 0.2
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
return round(complexity, 2)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def categorize_size(total_changes: int) -> str:
|
|
242
|
+
"""Categorize PR size."""
|
|
243
|
+
if total_changes < 50:
|
|
244
|
+
return "XS (Extra Small)"
|
|
245
|
+
elif total_changes < 200:
|
|
246
|
+
return "S (Small)"
|
|
247
|
+
elif total_changes < 400:
|
|
248
|
+
return "M (Medium)"
|
|
249
|
+
elif total_changes < 800:
|
|
250
|
+
return "L (Large)"
|
|
251
|
+
else:
|
|
252
|
+
return "XL (Extra Large) - Consider splitting"
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def estimate_review_time(files: List[FileStats], complexity: float) -> int:
|
|
256
|
+
"""Estimate review time in minutes."""
|
|
257
|
+
total_changes = sum(f.additions + f.deletions for f in files)
|
|
258
|
+
|
|
259
|
+
# Base time: ~1 minute per 20 lines
|
|
260
|
+
base_time = total_changes / 20
|
|
261
|
+
|
|
262
|
+
# Adjust for complexity
|
|
263
|
+
adjusted_time = base_time * (1 + complexity)
|
|
264
|
+
|
|
265
|
+
# Minimum 5 minutes, maximum 120 minutes
|
|
266
|
+
return max(5, min(120, int(adjusted_time)))
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def identify_risk_factors(files: List[FileStats]) -> List[str]:
|
|
270
|
+
"""Identify potential risk factors in the PR."""
|
|
271
|
+
risks = []
|
|
272
|
+
|
|
273
|
+
total_changes = sum(f.additions + f.deletions for f in files)
|
|
274
|
+
test_changes = sum(f.additions + f.deletions for f in files if f.is_test)
|
|
275
|
+
|
|
276
|
+
if total_changes > 400:
|
|
277
|
+
risks.append("Large PR (>400 lines) - harder to review thoroughly")
|
|
278
|
+
|
|
279
|
+
if test_changes == 0 and total_changes > 50:
|
|
280
|
+
risks.append(f"{RISK_NO_TESTS}: No test changes - verify test coverage")
|
|
281
|
+
|
|
282
|
+
if total_changes > 100 and test_changes / max(total_changes, 1) < 0.2:
|
|
283
|
+
risks.append("Low test ratio (<20%) - consider adding more tests")
|
|
284
|
+
|
|
285
|
+
# Security-sensitive files
|
|
286
|
+
security_patterns = ['.env', 'auth', 'security', 'password', 'token', 'secret']
|
|
287
|
+
for f in files:
|
|
288
|
+
if any(p in f.filename.lower() for p in security_patterns):
|
|
289
|
+
risks.append(f"Security-sensitive file: {f.filename}")
|
|
290
|
+
break
|
|
291
|
+
|
|
292
|
+
# Database changes
|
|
293
|
+
for f in files:
|
|
294
|
+
if 'migration' in f.filename.lower() or f.language == 'SQL':
|
|
295
|
+
risks.append("Database changes detected - review carefully")
|
|
296
|
+
break
|
|
297
|
+
|
|
298
|
+
# Config changes
|
|
299
|
+
config_files = [f for f in files if f.is_config]
|
|
300
|
+
if config_files:
|
|
301
|
+
risks.append(f"Configuration changes in {len(config_files)} file(s)")
|
|
302
|
+
|
|
303
|
+
return risks
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def generate_suggestions(files: List[FileStats], complexity: float, risks: List[str]) -> List[str]:
|
|
307
|
+
"""Generate review suggestions."""
|
|
308
|
+
suggestions = []
|
|
309
|
+
|
|
310
|
+
total_changes = sum(f.additions + f.deletions for f in files)
|
|
311
|
+
|
|
312
|
+
if total_changes > 800:
|
|
313
|
+
suggestions.append("Consider splitting this PR into smaller, focused changes")
|
|
314
|
+
|
|
315
|
+
if complexity > 0.7:
|
|
316
|
+
suggestions.append("High complexity - allocate extra review time")
|
|
317
|
+
suggestions.append("Consider pair reviewing for critical sections")
|
|
318
|
+
|
|
319
|
+
if any(RISK_NO_TESTS in r for r in risks):
|
|
320
|
+
suggestions.append("Request test additions before approval")
|
|
321
|
+
|
|
322
|
+
# Language-specific suggestions
|
|
323
|
+
languages = set(f.language for f in files)
|
|
324
|
+
if 'TypeScript' in languages or 'TypeScript/React' in languages:
|
|
325
|
+
suggestions.append("Check for proper type usage (avoid 'any')")
|
|
326
|
+
if 'Rust' in languages:
|
|
327
|
+
suggestions.append("Check for unwrap() usage and error handling")
|
|
328
|
+
if 'C' in languages or 'C++' in languages or 'C/C++' in languages:
|
|
329
|
+
suggestions.append("Check for memory safety, bounds checks, and UB risks")
|
|
330
|
+
if 'SQL' in languages:
|
|
331
|
+
suggestions.append("Review for SQL injection and query performance")
|
|
332
|
+
|
|
333
|
+
if not suggestions:
|
|
334
|
+
suggestions.append("Standard review process should suffice")
|
|
335
|
+
|
|
336
|
+
return suggestions
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def analyze_pr(diff_content: str) -> PRAnalysis:
|
|
340
|
+
"""Perform complete PR analysis."""
|
|
341
|
+
files = parse_diff(diff_content)
|
|
342
|
+
|
|
343
|
+
total_additions = sum(f.additions for f in files)
|
|
344
|
+
total_deletions = sum(f.deletions for f in files)
|
|
345
|
+
total_changes = total_additions + total_deletions
|
|
346
|
+
|
|
347
|
+
complexity = calculate_complexity(files)
|
|
348
|
+
risks = identify_risk_factors(files)
|
|
349
|
+
suggestions = generate_suggestions(files, complexity, risks)
|
|
350
|
+
|
|
351
|
+
return PRAnalysis(
|
|
352
|
+
total_files=len(files),
|
|
353
|
+
total_additions=total_additions,
|
|
354
|
+
total_deletions=total_deletions,
|
|
355
|
+
files=files,
|
|
356
|
+
complexity_score=complexity,
|
|
357
|
+
size_category=categorize_size(total_changes),
|
|
358
|
+
estimated_review_time=estimate_review_time(files, complexity),
|
|
359
|
+
risk_factors=risks,
|
|
360
|
+
suggestions=suggestions,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def print_analysis(analysis: PRAnalysis, show_files: bool = False):
|
|
365
|
+
"""Print analysis results."""
|
|
366
|
+
print("\n" + "=" * 60)
|
|
367
|
+
print("PR ANALYSIS REPORT")
|
|
368
|
+
print("=" * 60)
|
|
369
|
+
|
|
370
|
+
print(f"\n๐ SUMMARY")
|
|
371
|
+
print(f" Files changed: {analysis.total_files}")
|
|
372
|
+
print(f" Additions: +{analysis.total_additions}")
|
|
373
|
+
print(f" Deletions: -{analysis.total_deletions}")
|
|
374
|
+
print(f" Total changes: {analysis.total_additions + analysis.total_deletions}")
|
|
375
|
+
|
|
376
|
+
print(f"\n๐ SIZE: {analysis.size_category}")
|
|
377
|
+
print(f" Complexity score: {analysis.complexity_score}/1.0")
|
|
378
|
+
print(f" Estimated review time: ~{analysis.estimated_review_time} minutes")
|
|
379
|
+
|
|
380
|
+
if analysis.risk_factors:
|
|
381
|
+
print(f"\nโ ๏ธ RISK FACTORS:")
|
|
382
|
+
for risk in analysis.risk_factors:
|
|
383
|
+
print(f" โข {risk}")
|
|
384
|
+
|
|
385
|
+
print(f"\n๐ก SUGGESTIONS:")
|
|
386
|
+
for suggestion in analysis.suggestions:
|
|
387
|
+
print(f" โข {suggestion}")
|
|
388
|
+
|
|
389
|
+
if show_files:
|
|
390
|
+
print(f"\n๐ FILES:")
|
|
391
|
+
# Group by language
|
|
392
|
+
by_lang: Dict[str, List[FileStats]] = defaultdict(list)
|
|
393
|
+
for f in analysis.files:
|
|
394
|
+
by_lang[f.language].append(f)
|
|
395
|
+
|
|
396
|
+
for lang, lang_files in sorted(by_lang.items()):
|
|
397
|
+
print(f"\n [{lang}]")
|
|
398
|
+
for f in lang_files:
|
|
399
|
+
prefix = "๐งช" if f.is_test else "โ๏ธ" if f.is_config else "๐"
|
|
400
|
+
print(f" {prefix} {f.filename} (+{f.additions}/-{f.deletions})")
|
|
401
|
+
|
|
402
|
+
print("\n" + "=" * 60)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def main():
|
|
406
|
+
parser = argparse.ArgumentParser(description='Analyze PR complexity')
|
|
407
|
+
parser.add_argument('--diff-file', '-f', help='Path to diff file')
|
|
408
|
+
parser.add_argument('--stats', '-s', action='store_true', help='Show file details')
|
|
409
|
+
args = parser.parse_args()
|
|
410
|
+
|
|
411
|
+
# Read diff from file or stdin
|
|
412
|
+
try:
|
|
413
|
+
if args.diff_file:
|
|
414
|
+
with open(args.diff_file, 'r', encoding='utf-8', errors='replace') as f:
|
|
415
|
+
diff_content = f.read()
|
|
416
|
+
elif not sys.stdin.isatty():
|
|
417
|
+
diff_content = sys.stdin.buffer.read().decode('utf-8', errors='replace')
|
|
418
|
+
else:
|
|
419
|
+
print("Usage: git diff main...HEAD | python pr-analyzer.py")
|
|
420
|
+
print(" python pr-analyzer.py -f diff.txt")
|
|
421
|
+
sys.exit(1)
|
|
422
|
+
except OSError as e:
|
|
423
|
+
print(f"Error reading diff input: {e}", file=sys.stderr)
|
|
424
|
+
sys.exit(1)
|
|
425
|
+
|
|
426
|
+
if not diff_content.strip():
|
|
427
|
+
print("No diff content provided")
|
|
428
|
+
sys.exit(1)
|
|
429
|
+
|
|
430
|
+
analysis = analyze_pr(diff_content)
|
|
431
|
+
print_analysis(analysis, show_files=args.stats)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
if __name__ == '__main__':
|
|
435
|
+
main()
|