@diff-review-system/drs 3.3.1 → 4.0.1
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/.pi/agents/describe/pr-describer.md +14 -0
- package/.pi/agents/review/unified-reviewer.md +31 -1
- package/.pi/agents/task/agents-md-updater.md +26 -0
- package/.pi/agents/task/changelog-updater.md +29 -0
- package/.pi/agents/task/review-issue-fixer.md +42 -0
- package/.pi/agents/visual/pr-explainer.md +205 -0
- package/.pi/workflows/github-pr-describe.yaml +26 -0
- package/.pi/workflows/github-pr-fix-review-issues-stacked.yaml +148 -0
- package/.pi/workflows/github-pr-post-comment.yaml +19 -0
- package/.pi/workflows/github-pr-review-post.yaml +43 -0
- package/.pi/workflows/github-pr-review.yaml +364 -0
- package/.pi/workflows/github-pr-show-changes.yaml +25 -0
- package/.pi/workflows/github-pr-update-agents-md-stacked.yaml +103 -0
- package/.pi/workflows/github-pr-visual-explain.yaml +35 -0
- package/.pi/workflows/gitlab-mr-describe.yaml +24 -0
- package/.pi/workflows/gitlab-mr-fix-review-issues-stacked.yaml +144 -0
- package/.pi/workflows/gitlab-mr-post-comment.yaml +17 -0
- package/.pi/workflows/gitlab-mr-review.yaml +364 -0
- package/.pi/workflows/gitlab-mr-show-changes.yaml +23 -0
- package/.pi/workflows/gitlab-mr-update-agents-md-stacked.yaml +100 -0
- package/.pi/workflows/gitlab-mr-visual-explain.yaml +33 -0
- package/.pi/workflows/local-changelog-update.yaml +23 -0
- package/.pi/workflows/local-fix-review-issues.yaml +111 -0
- package/.pi/workflows/local-review.yaml +24 -0
- package/.pi/workflows/local-update-agents-md.yaml +24 -0
- package/.pi/workflows/local-visual-explain.yaml +31 -0
- package/.pi/workflows/release-changelog-finalize.yaml +47 -0
- package/.pi/workflows/tag-changelog-update.yaml +26 -0
- package/README.md +281 -104
- package/dist/ci/runner.d.ts.map +1 -1
- package/dist/ci/runner.js +9 -8
- package/dist/ci/runner.js.map +1 -1
- package/dist/cli/index.js +95 -325
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +25 -23
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/run-agent.d.ts +26 -0
- package/dist/cli/run-agent.d.ts.map +1 -0
- package/dist/cli/run-agent.js +143 -0
- package/dist/cli/run-agent.js.map +1 -0
- package/dist/cli/workflow.d.ts +105 -0
- package/dist/cli/workflow.d.ts.map +1 -0
- package/dist/cli/workflow.js +3309 -0
- package/dist/cli/workflow.js.map +1 -0
- package/dist/github/client.d.ts +12 -0
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +27 -0
- package/dist/github/client.js.map +1 -1
- package/dist/github/platform-adapter.d.ts +6 -1
- package/dist/github/platform-adapter.d.ts.map +1 -1
- package/dist/github/platform-adapter.js +84 -8
- package/dist/github/platform-adapter.js.map +1 -1
- package/dist/gitlab/client.d.ts +11 -0
- package/dist/gitlab/client.d.ts.map +1 -1
- package/dist/gitlab/client.js +11 -0
- package/dist/gitlab/client.js.map +1 -1
- package/dist/gitlab/platform-adapter.d.ts +3 -1
- package/dist/gitlab/platform-adapter.d.ts.map +1 -1
- package/dist/gitlab/platform-adapter.js +32 -1
- package/dist/gitlab/platform-adapter.js.map +1 -1
- package/dist/lib/agent-id.d.ts +9 -0
- package/dist/lib/agent-id.d.ts.map +1 -0
- package/dist/lib/agent-id.js +32 -0
- package/dist/lib/agent-id.js.map +1 -0
- package/dist/lib/comment-formatter.d.ts +15 -1
- package/dist/lib/comment-formatter.d.ts.map +1 -1
- package/dist/lib/comment-formatter.js +53 -4
- package/dist/lib/comment-formatter.js.map +1 -1
- package/dist/lib/comment-manager.d.ts +4 -0
- package/dist/lib/comment-manager.d.ts.map +1 -1
- package/dist/lib/comment-manager.js +7 -1
- package/dist/lib/comment-manager.js.map +1 -1
- package/dist/lib/comment-poster.d.ts +2 -2
- package/dist/lib/comment-poster.d.ts.map +1 -1
- package/dist/lib/comment-poster.js +31 -4
- package/dist/lib/comment-poster.js.map +1 -1
- package/dist/lib/config.d.ts +160 -44
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +475 -101
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/context-compression.d.ts +10 -0
- package/dist/lib/context-compression.d.ts.map +1 -1
- package/dist/lib/context-compression.js +101 -13
- package/dist/lib/context-compression.js.map +1 -1
- package/dist/lib/context-loader.d.ts +5 -4
- package/dist/lib/context-loader.d.ts.map +1 -1
- package/dist/lib/context-loader.js +79 -7
- package/dist/lib/context-loader.js.map +1 -1
- package/dist/lib/describe-core.d.ts.map +1 -1
- package/dist/lib/describe-core.js +3 -2
- package/dist/lib/describe-core.js.map +1 -1
- package/dist/lib/description-executor.js +1 -1
- package/dist/lib/description-executor.js.map +1 -1
- package/dist/lib/diff-lines.d.ts +18 -0
- package/dist/lib/diff-lines.d.ts.map +1 -0
- package/dist/lib/diff-lines.js +40 -0
- package/dist/lib/diff-lines.js.map +1 -0
- package/dist/lib/exit.js +4 -4
- package/dist/lib/exit.js.map +1 -1
- package/dist/lib/html-artifact.d.ts +14 -0
- package/dist/lib/html-artifact.d.ts.map +1 -0
- package/dist/lib/html-artifact.js +59 -0
- package/dist/lib/html-artifact.js.map +1 -0
- package/dist/lib/issue-parser.js +3 -3
- package/dist/lib/issue-parser.js.map +1 -1
- package/dist/lib/json-output-schema.d.ts +70 -0
- package/dist/lib/json-output-schema.d.ts.map +1 -1
- package/dist/lib/json-output-schema.js +40 -0
- package/dist/lib/json-output-schema.js.map +1 -1
- package/dist/lib/logger.d.ts +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/platform-client.d.ts +26 -0
- package/dist/lib/platform-client.d.ts.map +1 -1
- package/dist/lib/review-artifact.d.ts +69 -0
- package/dist/lib/review-artifact.d.ts.map +1 -0
- package/dist/lib/review-artifact.js +171 -0
- package/dist/lib/review-artifact.js.map +1 -0
- package/dist/lib/review-core.d.ts +6 -4
- package/dist/lib/review-core.d.ts.map +1 -1
- package/dist/lib/review-core.js +88 -173
- package/dist/lib/review-core.js.map +1 -1
- package/dist/lib/review-orchestrator.d.ts +23 -0
- package/dist/lib/review-orchestrator.d.ts.map +1 -1
- package/dist/lib/review-orchestrator.js +31 -21
- package/dist/lib/review-orchestrator.js.map +1 -1
- package/dist/lib/review-usage.d.ts +4 -0
- package/dist/lib/review-usage.d.ts.map +1 -1
- package/dist/lib/review-usage.js +25 -0
- package/dist/lib/review-usage.js.map +1 -1
- package/dist/lib/trace-collector.d.ts +105 -0
- package/dist/lib/trace-collector.d.ts.map +1 -0
- package/dist/lib/trace-collector.js +255 -0
- package/dist/lib/trace-collector.js.map +1 -0
- package/dist/lib/trace-html.d.ts +3 -0
- package/dist/lib/trace-html.d.ts.map +1 -0
- package/dist/lib/trace-html.js +349 -0
- package/dist/lib/trace-html.js.map +1 -0
- package/dist/lib/workflow-artifacts.d.ts +54 -0
- package/dist/lib/workflow-artifacts.d.ts.map +1 -0
- package/dist/lib/workflow-artifacts.js +150 -0
- package/dist/lib/workflow-artifacts.js.map +1 -0
- package/dist/pi/sdk.d.ts.map +1 -1
- package/dist/pi/sdk.js +605 -16
- package/dist/pi/sdk.js.map +1 -1
- package/dist/runtime/agent-loader.d.ts +10 -6
- package/dist/runtime/agent-loader.d.ts.map +1 -1
- package/dist/runtime/agent-loader.js +55 -29
- package/dist/runtime/agent-loader.js.map +1 -1
- package/dist/runtime/built-in-paths.d.ts +1 -0
- package/dist/runtime/built-in-paths.d.ts.map +1 -1
- package/dist/runtime/built-in-paths.js +7 -0
- package/dist/runtime/built-in-paths.js.map +1 -1
- package/dist/runtime/client.d.ts +14 -0
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +87 -56
- package/dist/runtime/client.js.map +1 -1
- package/dist/runtime/path-config.d.ts +2 -2
- package/dist/runtime/path-config.d.ts.map +1 -1
- package/dist/runtime/path-config.js +8 -8
- package/dist/runtime/path-config.js.map +1 -1
- package/package.json +22 -16
- package/.pi/agents/review/documentation.md +0 -56
- package/.pi/agents/review/performance.md +0 -53
- package/.pi/agents/review/quality.md +0 -59
- package/.pi/agents/review/security.md +0 -53
- package/.pi/agents/review/style.md +0 -132
- package/dist/cli/describe-mr.d.ts +0 -11
- package/dist/cli/describe-mr.d.ts.map +0 -1
- package/dist/cli/describe-mr.js +0 -134
- package/dist/cli/describe-mr.js.map +0 -1
- package/dist/cli/describe-pr.d.ts +0 -12
- package/dist/cli/describe-pr.d.ts.map +0 -1
- package/dist/cli/describe-pr.js +0 -135
- package/dist/cli/describe-pr.js.map +0 -1
- package/dist/cli/post-comments.d.ts +0 -20
- package/dist/cli/post-comments.d.ts.map +0 -1
- package/dist/cli/post-comments.js +0 -225
- package/dist/cli/post-comments.js.map +0 -1
- package/dist/cli/review-local.d.ts +0 -13
- package/dist/cli/review-local.d.ts.map +0 -1
- package/dist/cli/review-local.integration.test.d.ts +0 -2
- package/dist/cli/review-local.integration.test.d.ts.map +0 -1
- package/dist/cli/review-local.integration.test.js +0 -343
- package/dist/cli/review-local.integration.test.js.map +0 -1
- package/dist/cli/review-local.js +0 -90
- package/dist/cli/review-local.js.map +0 -1
- package/dist/cli/review-local.live.e2e.test.d.ts +0 -2
- package/dist/cli/review-local.live.e2e.test.d.ts.map +0 -1
- package/dist/cli/review-local.live.e2e.test.js +0 -153
- package/dist/cli/review-local.live.e2e.test.js.map +0 -1
- package/dist/cli/review-local.test.d.ts +0 -2
- package/dist/cli/review-local.test.d.ts.map +0 -1
- package/dist/cli/review-local.test.js +0 -164
- package/dist/cli/review-local.test.js.map +0 -1
- package/dist/cli/review-mr.d.ts +0 -22
- package/dist/cli/review-mr.d.ts.map +0 -1
- package/dist/cli/review-mr.js +0 -181
- package/dist/cli/review-mr.js.map +0 -1
- package/dist/cli/review-mr.test.d.ts +0 -2
- package/dist/cli/review-mr.test.d.ts.map +0 -1
- package/dist/cli/review-mr.test.js +0 -142
- package/dist/cli/review-mr.test.js.map +0 -1
- package/dist/cli/review-pr.d.ts +0 -22
- package/dist/cli/review-pr.d.ts.map +0 -1
- package/dist/cli/review-pr.js +0 -181
- package/dist/cli/review-pr.js.map +0 -1
- package/dist/cli/review-pr.test.d.ts +0 -2
- package/dist/cli/review-pr.test.d.ts.map +0 -1
- package/dist/cli/review-pr.test.js +0 -137
- package/dist/cli/review-pr.test.js.map +0 -1
- package/dist/cli/review-url.d.ts +0 -35
- package/dist/cli/review-url.d.ts.map +0 -1
- package/dist/cli/review-url.js +0 -110
- package/dist/cli/review-url.js.map +0 -1
- package/dist/cli/review-url.test.d.ts +0 -2
- package/dist/cli/review-url.test.d.ts.map +0 -1
- package/dist/cli/review-url.test.js +0 -132
- package/dist/cli/review-url.test.js.map +0 -1
- package/dist/cli/show-changes.d.ts +0 -15
- package/dist/cli/show-changes.d.ts.map +0 -1
- package/dist/cli/show-changes.js +0 -184
- package/dist/cli/show-changes.js.map +0 -1
- package/dist/github/client.test.d.ts +0 -2
- package/dist/github/client.test.d.ts.map +0 -1
- package/dist/github/client.test.js +0 -206
- package/dist/github/client.test.js.map +0 -1
- package/dist/github/platform-adapter.test.d.ts +0 -2
- package/dist/github/platform-adapter.test.d.ts.map +0 -1
- package/dist/github/platform-adapter.test.js +0 -40
- package/dist/github/platform-adapter.test.js.map +0 -1
- package/dist/gitlab/diff-parser.test.d.ts +0 -2
- package/dist/gitlab/diff-parser.test.d.ts.map +0 -1
- package/dist/gitlab/diff-parser.test.js +0 -315
- package/dist/gitlab/diff-parser.test.js.map +0 -1
- package/dist/gitlab/platform-adapter.test.d.ts +0 -2
- package/dist/gitlab/platform-adapter.test.d.ts.map +0 -1
- package/dist/gitlab/platform-adapter.test.js +0 -21
- package/dist/gitlab/platform-adapter.test.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -7
- package/dist/index.test.js.map +0 -1
- package/dist/lib/code-quality-report.test.d.ts +0 -2
- package/dist/lib/code-quality-report.test.d.ts.map +0 -1
- package/dist/lib/code-quality-report.test.js +0 -327
- package/dist/lib/code-quality-report.test.js.map +0 -1
- package/dist/lib/comment-formatter.test.d.ts +0 -2
- package/dist/lib/comment-formatter.test.d.ts.map +0 -1
- package/dist/lib/comment-formatter.test.js +0 -694
- package/dist/lib/comment-formatter.test.js.map +0 -1
- package/dist/lib/comment-manager.test.d.ts +0 -2
- package/dist/lib/comment-manager.test.d.ts.map +0 -1
- package/dist/lib/comment-manager.test.js +0 -680
- package/dist/lib/comment-manager.test.js.map +0 -1
- package/dist/lib/comment-poster.test.d.ts +0 -5
- package/dist/lib/comment-poster.test.d.ts.map +0 -1
- package/dist/lib/comment-poster.test.js +0 -245
- package/dist/lib/comment-poster.test.js.map +0 -1
- package/dist/lib/config-model-overrides.test.d.ts +0 -12
- package/dist/lib/config-model-overrides.test.d.ts.map +0 -1
- package/dist/lib/config-model-overrides.test.js +0 -254
- package/dist/lib/config-model-overrides.test.js.map +0 -1
- package/dist/lib/config.test.d.ts +0 -2
- package/dist/lib/config.test.d.ts.map +0 -1
- package/dist/lib/config.test.js +0 -73
- package/dist/lib/config.test.js.map +0 -1
- package/dist/lib/context-compression.test.d.ts +0 -2
- package/dist/lib/context-compression.test.d.ts.map +0 -1
- package/dist/lib/context-compression.test.js +0 -337
- package/dist/lib/context-compression.test.js.map +0 -1
- package/dist/lib/context-loader.test.d.ts +0 -2
- package/dist/lib/context-loader.test.d.ts.map +0 -1
- package/dist/lib/context-loader.test.js +0 -207
- package/dist/lib/context-loader.test.js.map +0 -1
- package/dist/lib/cursor-fix-link.test.d.ts +0 -2
- package/dist/lib/cursor-fix-link.test.d.ts.map +0 -1
- package/dist/lib/cursor-fix-link.test.js +0 -70
- package/dist/lib/cursor-fix-link.test.js.map +0 -1
- package/dist/lib/describe-core.test.d.ts +0 -2
- package/dist/lib/describe-core.test.d.ts.map +0 -1
- package/dist/lib/describe-core.test.js +0 -208
- package/dist/lib/describe-core.test.js.map +0 -1
- package/dist/lib/describe-output-path.test.d.ts +0 -2
- package/dist/lib/describe-output-path.test.d.ts.map +0 -1
- package/dist/lib/describe-output-path.test.js +0 -51
- package/dist/lib/describe-output-path.test.js.map +0 -1
- package/dist/lib/describe-parser.test.d.ts +0 -2
- package/dist/lib/describe-parser.test.d.ts.map +0 -1
- package/dist/lib/describe-parser.test.js +0 -282
- package/dist/lib/describe-parser.test.js.map +0 -1
- package/dist/lib/description-executor.test.d.ts +0 -2
- package/dist/lib/description-executor.test.d.ts.map +0 -1
- package/dist/lib/description-executor.test.js +0 -128
- package/dist/lib/description-executor.test.js.map +0 -1
- package/dist/lib/description-formatter.test.d.ts +0 -2
- package/dist/lib/description-formatter.test.d.ts.map +0 -1
- package/dist/lib/description-formatter.test.js +0 -57
- package/dist/lib/description-formatter.test.js.map +0 -1
- package/dist/lib/diff-parser.test.d.ts +0 -2
- package/dist/lib/diff-parser.test.d.ts.map +0 -1
- package/dist/lib/diff-parser.test.js +0 -335
- package/dist/lib/diff-parser.test.js.map +0 -1
- package/dist/lib/error-comment-poster.test.d.ts +0 -2
- package/dist/lib/error-comment-poster.test.d.ts.map +0 -1
- package/dist/lib/error-comment-poster.test.js +0 -128
- package/dist/lib/error-comment-poster.test.js.map +0 -1
- package/dist/lib/exit.test.d.ts +0 -2
- package/dist/lib/exit.test.d.ts.map +0 -1
- package/dist/lib/exit.test.js +0 -120
- package/dist/lib/exit.test.js.map +0 -1
- package/dist/lib/issue-parser.test.d.ts +0 -2
- package/dist/lib/issue-parser.test.d.ts.map +0 -1
- package/dist/lib/issue-parser.test.js +0 -281
- package/dist/lib/issue-parser.test.js.map +0 -1
- package/dist/lib/json-output-schema.test.d.ts +0 -2
- package/dist/lib/json-output-schema.test.d.ts.map +0 -1
- package/dist/lib/json-output-schema.test.js +0 -92
- package/dist/lib/json-output-schema.test.js.map +0 -1
- package/dist/lib/json-output.test.d.ts +0 -2
- package/dist/lib/json-output.test.d.ts.map +0 -1
- package/dist/lib/json-output.test.js +0 -141
- package/dist/lib/json-output.test.js.map +0 -1
- package/dist/lib/logger.test.d.ts +0 -2
- package/dist/lib/logger.test.d.ts.map +0 -1
- package/dist/lib/logger.test.js +0 -324
- package/dist/lib/logger.test.js.map +0 -1
- package/dist/lib/position-validator.test.d.ts +0 -2
- package/dist/lib/position-validator.test.d.ts.map +0 -1
- package/dist/lib/position-validator.test.js +0 -128
- package/dist/lib/position-validator.test.js.map +0 -1
- package/dist/lib/prompt-budget.test.d.ts +0 -2
- package/dist/lib/prompt-budget.test.d.ts.map +0 -1
- package/dist/lib/prompt-budget.test.js +0 -55
- package/dist/lib/prompt-budget.test.js.map +0 -1
- package/dist/lib/repository-validator.test.d.ts +0 -5
- package/dist/lib/repository-validator.test.d.ts.map +0 -1
- package/dist/lib/repository-validator.test.js +0 -341
- package/dist/lib/repository-validator.test.js.map +0 -1
- package/dist/lib/review-core.test.d.ts +0 -2
- package/dist/lib/review-core.test.d.ts.map +0 -1
- package/dist/lib/review-core.test.js +0 -600
- package/dist/lib/review-core.test.js.map +0 -1
- package/dist/lib/review-orchestrator.test.d.ts +0 -2
- package/dist/lib/review-orchestrator.test.d.ts.map +0 -1
- package/dist/lib/review-orchestrator.test.js +0 -531
- package/dist/lib/review-orchestrator.test.js.map +0 -1
- package/dist/lib/review-output-path.test.d.ts +0 -2
- package/dist/lib/review-output-path.test.d.ts.map +0 -1
- package/dist/lib/review-output-path.test.js +0 -83
- package/dist/lib/review-output-path.test.js.map +0 -1
- package/dist/lib/review-parser.test.d.ts +0 -2
- package/dist/lib/review-parser.test.d.ts.map +0 -1
- package/dist/lib/review-parser.test.js +0 -130
- package/dist/lib/review-parser.test.js.map +0 -1
- package/dist/lib/review-usage.test.d.ts +0 -2
- package/dist/lib/review-usage.test.d.ts.map +0 -1
- package/dist/lib/review-usage.test.js +0 -83
- package/dist/lib/review-usage.test.js.map +0 -1
- package/dist/lib/unified-review-executor.d.ts +0 -60
- package/dist/lib/unified-review-executor.d.ts.map +0 -1
- package/dist/lib/unified-review-executor.js +0 -207
- package/dist/lib/unified-review-executor.js.map +0 -1
- package/dist/lib/unified-review-executor.test.d.ts +0 -5
- package/dist/lib/unified-review-executor.test.d.ts.map +0 -1
- package/dist/lib/unified-review-executor.test.js +0 -472
- package/dist/lib/unified-review-executor.test.js.map +0 -1
- package/dist/lib/write-json-output.test.d.ts +0 -2
- package/dist/lib/write-json-output.test.d.ts.map +0 -1
- package/dist/lib/write-json-output.test.js +0 -259
- package/dist/lib/write-json-output.test.js.map +0 -1
- package/dist/pi/sdk.test.d.ts +0 -2
- package/dist/pi/sdk.test.d.ts.map +0 -1
- package/dist/pi/sdk.test.js +0 -449
- package/dist/pi/sdk.test.js.map +0 -1
- package/dist/runtime/agent-loader.test.d.ts +0 -2
- package/dist/runtime/agent-loader.test.d.ts.map +0 -1
- package/dist/runtime/agent-loader.test.js +0 -280
- package/dist/runtime/agent-loader.test.js.map +0 -1
- package/dist/runtime/client.test.d.ts +0 -2
- package/dist/runtime/client.test.d.ts.map +0 -1
- package/dist/runtime/client.test.js +0 -523
- package/dist/runtime/client.test.js.map +0 -1
- package/dist/runtime/path-config.test.d.ts +0 -2
- package/dist/runtime/path-config.test.d.ts.map +0 -1
- package/dist/runtime/path-config.test.js +0 -112
- package/dist/runtime/path-config.test.js.map +0 -1
package/dist/lib/config.js
CHANGED
|
@@ -1,8 +1,51 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from 'fs';
|
|
2
|
-
import { resolve } from 'path';
|
|
1
|
+
import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
2
|
+
import { basename, extname, join, resolve } from 'path';
|
|
3
3
|
import * as yaml from 'yaml';
|
|
4
|
+
import { requireAgentId } from './agent-id.js';
|
|
5
|
+
import { getBuiltInWorkflowPaths } from '../runtime/built-in-paths.js';
|
|
6
|
+
// Canonical list of supported workflow actions. WorkflowNodeConfig['action']
|
|
7
|
+
// derives its union from this tuple so the schema and the load-time validator
|
|
8
|
+
// can never drift. Keep both this tuple and the validator in lockstep.
|
|
9
|
+
export const SUPPORTED_WORKFLOW_ACTIONS = [
|
|
10
|
+
'write',
|
|
11
|
+
'git-diff',
|
|
12
|
+
'git-add',
|
|
13
|
+
'git-branch',
|
|
14
|
+
'git-commit',
|
|
15
|
+
'git-push',
|
|
16
|
+
'has-diff',
|
|
17
|
+
'stack-guard',
|
|
18
|
+
'review-threshold',
|
|
19
|
+
'save-artifact',
|
|
20
|
+
'load-artifact',
|
|
21
|
+
'artifact-exists',
|
|
22
|
+
'create-review-artifact',
|
|
23
|
+
'review-artifact-status',
|
|
24
|
+
'review-artifact-add-finding',
|
|
25
|
+
'review-artifact-update-findings',
|
|
26
|
+
'verify-fix',
|
|
27
|
+
'review-artifact-promote-finding',
|
|
28
|
+
'review-artifact-resolve-finding',
|
|
29
|
+
'create-change-request',
|
|
30
|
+
'create-pr',
|
|
31
|
+
'create-mr',
|
|
32
|
+
'change-source',
|
|
33
|
+
'review',
|
|
34
|
+
'review-context',
|
|
35
|
+
'describe',
|
|
36
|
+
'code-quality-report',
|
|
37
|
+
'post-comment',
|
|
38
|
+
'post-review-comments',
|
|
39
|
+
'post-fix-status',
|
|
40
|
+
];
|
|
4
41
|
const DEFAULT_CONFIG = {
|
|
5
42
|
pi: {},
|
|
43
|
+
agents: {
|
|
44
|
+
default: {
|
|
45
|
+
model: getDefaultModelEnv() ?? 'anthropic/claude-sonnet-4-5-20250929',
|
|
46
|
+
skills: [],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
6
49
|
gitlab: {
|
|
7
50
|
url: process.env.GITLAB_URL ?? 'https://gitlab.com',
|
|
8
51
|
token: process.env.GITLAB_TOKEN ?? '',
|
|
@@ -11,11 +54,7 @@ const DEFAULT_CONFIG = {
|
|
|
11
54
|
token: process.env.GITHUB_TOKEN ?? '',
|
|
12
55
|
},
|
|
13
56
|
review: {
|
|
14
|
-
agents: ['
|
|
15
|
-
default: {
|
|
16
|
-
model: process.env.REVIEW_DEFAULT_MODEL ?? 'anthropic/claude-sonnet-4-5-20250929',
|
|
17
|
-
skills: [],
|
|
18
|
-
},
|
|
57
|
+
agents: ['review/unified-reviewer'],
|
|
19
58
|
ignorePatterns: [
|
|
20
59
|
'*.test.ts',
|
|
21
60
|
'*.spec.ts',
|
|
@@ -27,12 +66,10 @@ const DEFAULT_CONFIG = {
|
|
|
27
66
|
],
|
|
28
67
|
describe: {
|
|
29
68
|
enabled: false,
|
|
30
|
-
postDescription: false,
|
|
31
69
|
},
|
|
32
70
|
cursorFixLinks: {
|
|
33
71
|
enabled: false,
|
|
34
72
|
},
|
|
35
|
-
postErrorComment: false,
|
|
36
73
|
},
|
|
37
74
|
contextCompression: {
|
|
38
75
|
enabled: true,
|
|
@@ -41,32 +78,254 @@ const DEFAULT_CONFIG = {
|
|
|
41
78
|
softBufferTokens: 1500,
|
|
42
79
|
hardBufferTokens: 1000,
|
|
43
80
|
tokenEstimateDivisor: 4,
|
|
81
|
+
summaryThresholdMultiplier: 3,
|
|
44
82
|
},
|
|
45
83
|
describe: {
|
|
46
84
|
includeProjectContext: true,
|
|
47
85
|
},
|
|
48
86
|
};
|
|
87
|
+
function isRecord(value) {
|
|
88
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
89
|
+
}
|
|
90
|
+
function isWorkflowFileName(fileName) {
|
|
91
|
+
return fileName.endsWith('.yaml') || fileName.endsWith('.yml');
|
|
92
|
+
}
|
|
93
|
+
const SUPPORTED_ACTION_SET = new Set(SUPPORTED_WORKFLOW_ACTIONS);
|
|
94
|
+
function levenshteinDistance(a, b) {
|
|
95
|
+
if (a === b)
|
|
96
|
+
return 0;
|
|
97
|
+
if (a.length === 0)
|
|
98
|
+
return b.length;
|
|
99
|
+
if (b.length === 0)
|
|
100
|
+
return a.length;
|
|
101
|
+
let previous = new Array(b.length + 1);
|
|
102
|
+
let current = new Array(b.length + 1);
|
|
103
|
+
for (let j = 0; j <= b.length; j++)
|
|
104
|
+
previous[j] = j;
|
|
105
|
+
for (let i = 1; i <= a.length; i++) {
|
|
106
|
+
current[0] = i;
|
|
107
|
+
for (let j = 1; j <= b.length; j++) {
|
|
108
|
+
const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
|
|
109
|
+
current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + cost);
|
|
110
|
+
}
|
|
111
|
+
const tmp = previous;
|
|
112
|
+
previous = current;
|
|
113
|
+
current = tmp;
|
|
114
|
+
}
|
|
115
|
+
return previous[b.length];
|
|
116
|
+
}
|
|
117
|
+
function findClosestSupportedAction(input) {
|
|
118
|
+
let best;
|
|
119
|
+
let bestDistance = Infinity;
|
|
120
|
+
// Cap at two edits so we never suggest a long action for a shorter typo.
|
|
121
|
+
const threshold = 2;
|
|
122
|
+
for (const supported of SUPPORTED_WORKFLOW_ACTIONS) {
|
|
123
|
+
const distance = levenshteinDistance(input, supported);
|
|
124
|
+
if (distance < bestDistance && distance <= threshold) {
|
|
125
|
+
best = supported;
|
|
126
|
+
bestDistance = distance;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return best;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validate every node's `action` value in a workflow against
|
|
133
|
+
* SUPPORTED_WORKFLOW_ACTIONS. Throws with a did-you-mean hint on failure:
|
|
134
|
+
* - Near-miss typos get the closest supported action via Levenshtein.
|
|
135
|
+
* - Wholly unknown actions get a list of every supported action.
|
|
136
|
+
*
|
|
137
|
+
* Called from validateWorkflowDefinition so misconfigured YAML fails fast at
|
|
138
|
+
* config-load time instead of partway through wave execution.
|
|
139
|
+
*/
|
|
140
|
+
export function validateWorkflowActions(workflowName, nodes) {
|
|
141
|
+
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
142
|
+
const action = node.action;
|
|
143
|
+
if (typeof action !== 'string')
|
|
144
|
+
continue;
|
|
145
|
+
if (SUPPORTED_ACTION_SET.has(action))
|
|
146
|
+
continue;
|
|
147
|
+
let hint;
|
|
148
|
+
const closest = findClosestSupportedAction(action);
|
|
149
|
+
if (closest) {
|
|
150
|
+
hint = "Did you mean '" + closest + "'?";
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
hint = 'Supported actions: ' + SUPPORTED_WORKFLOW_ACTIONS.join(', ') + '.';
|
|
154
|
+
}
|
|
155
|
+
throw new Error('Workflow "' +
|
|
156
|
+
workflowName +
|
|
157
|
+
'" node "' +
|
|
158
|
+
nodeId +
|
|
159
|
+
'" has unsupported action "' +
|
|
160
|
+
action +
|
|
161
|
+
'". ' +
|
|
162
|
+
hint);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function validateWorkflowInputs(workflowName, inputs) {
|
|
166
|
+
if (inputs === undefined)
|
|
167
|
+
return;
|
|
168
|
+
if (!isRecord(inputs)) {
|
|
169
|
+
throw new Error(`Workflow "${workflowName}" inputs must be an object.`);
|
|
170
|
+
}
|
|
171
|
+
for (const [inputName, input] of Object.entries(inputs)) {
|
|
172
|
+
if (typeof input === 'string')
|
|
173
|
+
continue;
|
|
174
|
+
if (!isRecord(input)) {
|
|
175
|
+
throw new Error(`Workflow "${workflowName}" input "${inputName}" must be a string or object.`);
|
|
176
|
+
}
|
|
177
|
+
const allowed = new Set([
|
|
178
|
+
'type',
|
|
179
|
+
'value',
|
|
180
|
+
'file',
|
|
181
|
+
'default',
|
|
182
|
+
'required',
|
|
183
|
+
'values',
|
|
184
|
+
'description',
|
|
185
|
+
]);
|
|
186
|
+
const unknown = Object.keys(input).filter((key) => !allowed.has(key));
|
|
187
|
+
if (unknown.length > 0) {
|
|
188
|
+
throw new Error(`Workflow "${workflowName}" input "${inputName}" has unsupported field(s): ${unknown.join(', ')}.`);
|
|
189
|
+
}
|
|
190
|
+
const rawType = input.type;
|
|
191
|
+
if (rawType !== undefined && typeof rawType !== 'string') {
|
|
192
|
+
throw new Error(`Workflow "${workflowName}" input "${inputName}" type must be a string.`);
|
|
193
|
+
}
|
|
194
|
+
const type = rawType ?? 'string';
|
|
195
|
+
if (!['string', 'boolean', 'number', 'enum'].includes(type)) {
|
|
196
|
+
throw new Error(`Workflow "${workflowName}" input "${inputName}" has unsupported type "${type}".`);
|
|
197
|
+
}
|
|
198
|
+
if (input.file !== undefined && (input.value !== undefined || input.default !== undefined)) {
|
|
199
|
+
throw new Error(`Workflow "${workflowName}" input "${inputName}" cannot define file with value/default.`);
|
|
200
|
+
}
|
|
201
|
+
if (input.required !== undefined && typeof input.required !== 'boolean') {
|
|
202
|
+
throw new Error(`Workflow "${workflowName}" input "${inputName}" required must be boolean.`);
|
|
203
|
+
}
|
|
204
|
+
if (input.description !== undefined && typeof input.description !== 'string') {
|
|
205
|
+
throw new Error(`Workflow "${workflowName}" input "${inputName}" description must be string.`);
|
|
206
|
+
}
|
|
207
|
+
if (type === 'enum' && (!Array.isArray(input.values) || input.values.length === 0)) {
|
|
208
|
+
throw new Error(`Workflow "${workflowName}" input "${inputName}" type enum must define values.`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function validateWorkflowDefinition(workflowName, workflow, sourcePath) {
|
|
213
|
+
if (!isRecord(workflow) || !isRecord(workflow.nodes)) {
|
|
214
|
+
throw new Error(`Workflow "${workflowName}" in ${sourcePath} must define a nodes object.`);
|
|
215
|
+
}
|
|
216
|
+
const typed = workflow;
|
|
217
|
+
validateWorkflowInputs(workflowName, typed.inputs);
|
|
218
|
+
validateWorkflowActions(workflowName, typed.nodes);
|
|
219
|
+
return typed;
|
|
220
|
+
}
|
|
221
|
+
function loadWorkflowFile(filePath) {
|
|
222
|
+
const parsed = yaml.parse(readFileSync(filePath, 'utf-8')) ?? {};
|
|
223
|
+
if (!isRecord(parsed)) {
|
|
224
|
+
throw new Error(`Workflow file ${filePath} must contain a YAML object.`);
|
|
225
|
+
}
|
|
226
|
+
if (parsed.workflows !== undefined) {
|
|
227
|
+
throw new Error(`Workflow file ${filePath} must define one workflow directly.`);
|
|
228
|
+
}
|
|
229
|
+
const workflowName = parsed.name === undefined ? basename(filePath, extname(filePath)) : parsed.name;
|
|
230
|
+
if (typeof workflowName !== 'string' || !workflowName.trim()) {
|
|
231
|
+
throw new Error(`Workflow file ${filePath} must use a non-empty string name.`);
|
|
232
|
+
}
|
|
233
|
+
const workflow = { ...parsed };
|
|
234
|
+
delete workflow.name;
|
|
235
|
+
return {
|
|
236
|
+
[workflowName.trim()]: validateWorkflowDefinition(workflowName.trim(), workflow, filePath),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function loadWorkflowFilesFromDirectory(directoryPath, source) {
|
|
240
|
+
if (!existsSync(directoryPath)) {
|
|
241
|
+
return { workflows: {}, sources: {} };
|
|
242
|
+
}
|
|
243
|
+
if (!statSync(directoryPath).isDirectory()) {
|
|
244
|
+
throw new Error(`Workflow path ${directoryPath} exists but is not a directory.`);
|
|
245
|
+
}
|
|
246
|
+
const workflows = {};
|
|
247
|
+
const sources = {};
|
|
248
|
+
for (const fileName of readdirSync(directoryPath).filter(isWorkflowFileName).sort()) {
|
|
249
|
+
const fileWorkflows = loadWorkflowFile(join(directoryPath, fileName));
|
|
250
|
+
for (const workflowName of Object.keys(fileWorkflows)) {
|
|
251
|
+
sources[workflowName] = source;
|
|
252
|
+
}
|
|
253
|
+
Object.assign(workflows, fileWorkflows);
|
|
254
|
+
}
|
|
255
|
+
return { workflows, sources };
|
|
256
|
+
}
|
|
257
|
+
function loadBuiltInWorkflowFiles() {
|
|
258
|
+
const result = {
|
|
259
|
+
workflows: {},
|
|
260
|
+
sources: {},
|
|
261
|
+
};
|
|
262
|
+
for (const directory of getBuiltInWorkflowPaths()) {
|
|
263
|
+
const loaded = loadWorkflowFilesFromDirectory(directory, 'packaged');
|
|
264
|
+
Object.assign(result.workflows, loaded.workflows);
|
|
265
|
+
Object.assign(result.sources, loaded.sources);
|
|
266
|
+
}
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Load map of workflow names to their source origin (packaged or project).
|
|
271
|
+
*/
|
|
272
|
+
export function loadWorkflowSources(projectPath) {
|
|
273
|
+
const basePath = projectPath ?? process.cwd();
|
|
274
|
+
const builtIn = loadBuiltInWorkflowFiles();
|
|
275
|
+
const project = loadWorkflowFilesFromDirectory(resolve(basePath, '.drs/workflows'), 'project');
|
|
276
|
+
// Project workflows take precedence, so they override packaged sources.
|
|
277
|
+
return { ...builtIn.sources, ...project.sources };
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Load map of workflow names to their source origin, including whether
|
|
281
|
+
* a project workflow is overriding an existing packaged workflow.
|
|
282
|
+
*/
|
|
283
|
+
export function loadWorkflowSourceInfo(projectPath) {
|
|
284
|
+
const basePath = projectPath ?? process.cwd();
|
|
285
|
+
const builtIn = loadBuiltInWorkflowFiles();
|
|
286
|
+
const project = loadWorkflowFilesFromDirectory(resolve(basePath, '.drs/workflows'), 'project');
|
|
287
|
+
const info = {};
|
|
288
|
+
for (const name of Object.keys(builtIn.sources)) {
|
|
289
|
+
info[name] = { source: 'packaged', overridesPackaged: false };
|
|
290
|
+
}
|
|
291
|
+
for (const name of Object.keys(project.sources)) {
|
|
292
|
+
info[name] = { source: 'project', overridesPackaged: name in builtIn.sources };
|
|
293
|
+
}
|
|
294
|
+
return info;
|
|
295
|
+
}
|
|
49
296
|
/**
|
|
50
297
|
* Load configuration from various sources with precedence:
|
|
51
|
-
* 1.
|
|
52
|
-
* 2.
|
|
53
|
-
* 3. .drs/
|
|
54
|
-
* 4. .
|
|
55
|
-
* 5.
|
|
298
|
+
* 1. Default values
|
|
299
|
+
* 2. Built-in workflow files
|
|
300
|
+
* 3. .drs/workflows/*.yaml
|
|
301
|
+
* 4. .drs/drs.config.yaml
|
|
302
|
+
* 5. .gitlab-review.yml
|
|
303
|
+
* 6. Environment variables
|
|
304
|
+
* 7. CLI arguments (passed as overrides)
|
|
56
305
|
*/
|
|
57
306
|
export function loadConfig(projectPath, overrides) {
|
|
58
307
|
const basePath = projectPath ?? process.cwd();
|
|
59
|
-
|
|
308
|
+
const builtIn = loadBuiltInWorkflowFiles();
|
|
309
|
+
let config = mergeConfig({ ...DEFAULT_CONFIG }, { workflows: builtIn.workflows });
|
|
310
|
+
const projectWorkflowPath = resolve(basePath, '.drs/workflows');
|
|
311
|
+
const project = loadWorkflowFilesFromDirectory(projectWorkflowPath, 'project');
|
|
312
|
+
config = mergeConfig(config, { workflows: project.workflows });
|
|
60
313
|
// Try loading from .drs/drs.config.yaml
|
|
61
314
|
const drsConfigPath = resolve(basePath, '.drs/drs.config.yaml');
|
|
62
315
|
if (existsSync(drsConfigPath)) {
|
|
63
|
-
const fileConfig = yaml.parse(readFileSync(drsConfigPath, 'utf-8'));
|
|
316
|
+
const fileConfig = yaml.parse(readFileSync(drsConfigPath, 'utf-8')) ?? {};
|
|
317
|
+
rejectLegacyAgentConfigKeys(fileConfig, drsConfigPath);
|
|
318
|
+
rejectRemovedReviewPostingConfigKeys(fileConfig, drsConfigPath);
|
|
319
|
+
rejectInlineWorkflowConfig(fileConfig, drsConfigPath);
|
|
64
320
|
config = mergeConfig(config, fileConfig);
|
|
65
321
|
}
|
|
66
322
|
// Try loading from .gitlab-review.yml
|
|
67
323
|
const gitlabReviewPath = resolve(basePath, '.gitlab-review.yml');
|
|
68
324
|
if (existsSync(gitlabReviewPath)) {
|
|
69
|
-
const fileConfig = yaml.parse(readFileSync(gitlabReviewPath, 'utf-8'));
|
|
325
|
+
const fileConfig = yaml.parse(readFileSync(gitlabReviewPath, 'utf-8')) ?? {};
|
|
326
|
+
rejectLegacyAgentConfigKeys(fileConfig, gitlabReviewPath);
|
|
327
|
+
rejectRemovedReviewPostingConfigKeys(fileConfig, gitlabReviewPath);
|
|
328
|
+
rejectInlineWorkflowConfig(fileConfig, gitlabReviewPath);
|
|
70
329
|
config = mergeConfig(config, fileConfig);
|
|
71
330
|
}
|
|
72
331
|
// Apply environment variable overrides
|
|
@@ -83,31 +342,20 @@ export function loadConfig(projectPath, overrides) {
|
|
|
83
342
|
// Environment variable is always simple string format (comma-separated)
|
|
84
343
|
config.review.agents = process.env.REVIEW_AGENTS.split(',').map((a) => a.trim());
|
|
85
344
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
config.
|
|
89
|
-
model:
|
|
345
|
+
const defaultModelEnv = getDefaultModelEnv();
|
|
346
|
+
if (defaultModelEnv) {
|
|
347
|
+
config.agents.default = mergeSection(config.agents.default, {
|
|
348
|
+
model: defaultModelEnv,
|
|
90
349
|
});
|
|
91
350
|
}
|
|
92
|
-
if (process.env.REVIEW_MODE) {
|
|
93
|
-
console.warn('⚠ REVIEW_MODE is deprecated. Configure review.agents explicitly (include unified-reviewer when needed).');
|
|
94
|
-
config.review.mode = process.env.REVIEW_MODE;
|
|
95
|
-
}
|
|
96
351
|
if (process.env.REVIEW_UNIFIED_MODEL) {
|
|
97
352
|
config.review.unified = {
|
|
98
353
|
...config.review.unified,
|
|
99
354
|
model: process.env.REVIEW_UNIFIED_MODEL,
|
|
100
355
|
};
|
|
101
356
|
}
|
|
102
|
-
if (process.env.REVIEW_UNIFIED_THRESHOLD) {
|
|
103
|
-
console.warn('⚠ REVIEW_UNIFIED_THRESHOLD is deprecated and ignored by the agents-first pipeline.');
|
|
104
|
-
config.review.unified = {
|
|
105
|
-
...config.review.unified,
|
|
106
|
-
severityThreshold: process.env.REVIEW_UNIFIED_THRESHOLD,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
357
|
if (process.env.REVIEW_THINKING_LEVEL) {
|
|
110
|
-
config.
|
|
358
|
+
config.agents.default = mergeSection(config.agents.default, {
|
|
111
359
|
thinkingLevel: process.env.REVIEW_THINKING_LEVEL,
|
|
112
360
|
});
|
|
113
361
|
}
|
|
@@ -117,29 +365,56 @@ export function loadConfig(projectPath, overrides) {
|
|
|
117
365
|
if (process.env.REVIEW_SKIP_BRANCH_CHECK === 'true') {
|
|
118
366
|
config.review.skipBranchCheck = true;
|
|
119
367
|
}
|
|
120
|
-
if (process.env.REVIEW_POST_ERROR_COMMENT === 'true') {
|
|
121
|
-
config.review.postErrorComment = true;
|
|
122
|
-
}
|
|
123
368
|
// Validate required fields
|
|
124
369
|
if (!getDefaultModel(config)) {
|
|
125
|
-
throw new Error('Default model is required. Set
|
|
370
|
+
throw new Error('Default model is required. Set agents.default.model in .drs/drs.config.yaml or DRS_DEFAULT_MODEL environment variable.\n' +
|
|
126
371
|
'Run "drs init" to configure your project.');
|
|
127
372
|
}
|
|
128
373
|
// Apply CLI overrides
|
|
129
374
|
if (overrides) {
|
|
130
375
|
config = mergeConfig(config, overrides);
|
|
131
376
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
377
|
+
return normalizeRuntimeConfig(config);
|
|
378
|
+
}
|
|
379
|
+
function rejectLegacyAgentConfigKeys(fileConfig, sourcePath) {
|
|
380
|
+
const reviewConfig = fileConfig.review;
|
|
381
|
+
if (!reviewConfig || typeof reviewConfig !== 'object') {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const migrations = [];
|
|
385
|
+
if ('default' in reviewConfig)
|
|
386
|
+
migrations.push('review.default -> agents.default');
|
|
387
|
+
if ('defaultModel' in reviewConfig)
|
|
388
|
+
migrations.push('review.defaultModel -> agents.default.model');
|
|
389
|
+
if ('paths' in reviewConfig)
|
|
390
|
+
migrations.push('review.paths -> agents.paths');
|
|
391
|
+
if (migrations.length > 0) {
|
|
392
|
+
throw new Error(`Config file ${sourcePath} uses legacy DRS 3.x agent config keys: ${migrations.join(', ')}. ` +
|
|
393
|
+
'DRS 4.0 requires top-level agent configuration. Move model/skill defaults to agents.default and custom paths to agents.paths.');
|
|
138
394
|
}
|
|
139
|
-
|
|
140
|
-
|
|
395
|
+
}
|
|
396
|
+
function rejectInlineWorkflowConfig(fileConfig, sourcePath) {
|
|
397
|
+
if (!isRecord(fileConfig) || fileConfig.workflows === undefined) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
throw new Error(`Config file ${sourcePath} cannot define top-level workflows.`);
|
|
401
|
+
}
|
|
402
|
+
function rejectRemovedReviewPostingConfigKeys(fileConfig, sourcePath) {
|
|
403
|
+
if (!isRecord(fileConfig) || !isRecord(fileConfig.review)) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const removedKeys = [];
|
|
407
|
+
if (Object.prototype.hasOwnProperty.call(fileConfig.review, 'postErrorComment')) {
|
|
408
|
+
removedKeys.push('review.postErrorComment');
|
|
409
|
+
}
|
|
410
|
+
if (isRecord(fileConfig.review.describe) &&
|
|
411
|
+
Object.prototype.hasOwnProperty.call(fileConfig.review.describe, 'postDescription')) {
|
|
412
|
+
removedKeys.push('review.describe.postDescription');
|
|
413
|
+
}
|
|
414
|
+
if (removedKeys.length > 0) {
|
|
415
|
+
throw new Error(`Config file ${sourcePath} uses removed DRS 4.0 review posting keys: ${removedKeys.join(', ')}. ` +
|
|
416
|
+
'Run posting explicitly with workflows, or use github-pr-describe/gitlab-mr-describe with post=true when updating PR/MR descriptions.');
|
|
141
417
|
}
|
|
142
|
-
return normalizeRuntimeConfig(config);
|
|
143
418
|
}
|
|
144
419
|
/**
|
|
145
420
|
* Deep merge two config objects, skipping undefined values
|
|
@@ -148,12 +423,16 @@ function mergeConfig(base, override) {
|
|
|
148
423
|
return {
|
|
149
424
|
pi: mergeSection(base.pi, override.pi),
|
|
150
425
|
opencode: mergeSection(base.opencode, override.opencode),
|
|
426
|
+
agents: mergeSection(base.agents, override.agents),
|
|
427
|
+
workflows: mergeSection(base.workflows, override.workflows),
|
|
428
|
+
workflow: mergeSection(base.workflow, override.workflow),
|
|
151
429
|
gitlab: mergeSection(base.gitlab, override.gitlab),
|
|
152
430
|
github: mergeSection(base.github, override.github),
|
|
153
431
|
review: mergeSection(base.review, override.review),
|
|
154
432
|
describe: mergeSection(base.describe, override.describe),
|
|
155
433
|
contextCompression: mergeSection(base.contextCompression, override.contextCompression),
|
|
156
434
|
pricing: mergeSection(base.pricing, override.pricing),
|
|
435
|
+
fix: mergeSection(base.fix, override.fix),
|
|
157
436
|
};
|
|
158
437
|
}
|
|
159
438
|
function normalizeRuntimeConfig(config) {
|
|
@@ -168,8 +447,24 @@ function normalizeRuntimeConfig(config) {
|
|
|
168
447
|
...(legacyRuntime.provider ?? {}),
|
|
169
448
|
...(piRuntime.provider ?? {}),
|
|
170
449
|
};
|
|
450
|
+
const mergedRuntimeTimeouts = {
|
|
451
|
+
...(legacyRuntime.runtime ?? {}),
|
|
452
|
+
...(piRuntime.runtime ?? {}),
|
|
453
|
+
};
|
|
454
|
+
const legacyProviderRetry = legacyRuntime.retry?.provider;
|
|
455
|
+
const piProviderRetry = piRuntime.retry?.provider;
|
|
456
|
+
const mergedProviderRetry = {
|
|
457
|
+
...(legacyProviderRetry ?? {}),
|
|
458
|
+
...(piProviderRetry ?? {}),
|
|
459
|
+
};
|
|
171
460
|
const normalizedRuntime = {
|
|
172
461
|
provider: Object.keys(mergedProvider).length > 0 ? mergedProvider : undefined,
|
|
462
|
+
runtime: Object.keys(mergedRuntimeTimeouts).length > 0 ? mergedRuntimeTimeouts : undefined,
|
|
463
|
+
retry: Object.keys(mergedProviderRetry).length > 0
|
|
464
|
+
? {
|
|
465
|
+
provider: mergedProviderRetry,
|
|
466
|
+
}
|
|
467
|
+
: undefined,
|
|
173
468
|
};
|
|
174
469
|
return {
|
|
175
470
|
...config,
|
|
@@ -191,7 +486,6 @@ function mergeSection(base, override) {
|
|
|
191
486
|
for (const key in override) {
|
|
192
487
|
const value = override[key];
|
|
193
488
|
if (value !== undefined) {
|
|
194
|
-
// TypeScript knows that value is T[Extract<keyof T, string>] which is assignable to T[keyof T]
|
|
195
489
|
result[key] = value;
|
|
196
490
|
}
|
|
197
491
|
}
|
|
@@ -208,11 +502,11 @@ export function validateConfig(config, platform) {
|
|
|
208
502
|
if (platform === 'github' && !config.github.token) {
|
|
209
503
|
throw new Error('GitHub token is required. Set GITHUB_TOKEN environment variable or configure in config file');
|
|
210
504
|
}
|
|
211
|
-
if (
|
|
505
|
+
if (getReviewAgentIds(config).length === 0) {
|
|
212
506
|
throw new Error('At least one review agent must be configured');
|
|
213
507
|
}
|
|
214
508
|
if (!getDefaultModel(config)) {
|
|
215
|
-
throw new Error('Default model is required. Run "drs init" to configure or set
|
|
509
|
+
throw new Error('Default model is required. Run "drs init" to configure agents.default.model or set DRS_DEFAULT_MODEL environment variable.');
|
|
216
510
|
}
|
|
217
511
|
}
|
|
218
512
|
/**
|
|
@@ -255,69 +549,144 @@ export function normalizeAgentConfig(agents) {
|
|
|
255
549
|
return agent;
|
|
256
550
|
});
|
|
257
551
|
}
|
|
552
|
+
function dedupeStrings(values) {
|
|
553
|
+
return Array.from(new Set(values.map((value) => value.trim()).filter((value) => value.length > 0)));
|
|
554
|
+
}
|
|
555
|
+
function getNamespaceDefaults(config, namespace) {
|
|
556
|
+
return config.agents.namespaces?.[namespace] ?? {};
|
|
557
|
+
}
|
|
558
|
+
function getAgentOverride(config, agentId) {
|
|
559
|
+
return config.agents.overrides?.[agentId] ?? {};
|
|
560
|
+
}
|
|
561
|
+
function getDefaultModelEnv() {
|
|
562
|
+
return process.env.DRS_DEFAULT_MODEL ?? process.env.REVIEW_DEFAULT_MODEL ?? undefined;
|
|
563
|
+
}
|
|
564
|
+
function getAgentModelEnv(agentId) {
|
|
565
|
+
const suffix = agentId.toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
566
|
+
return process.env[`DRS_AGENT_${suffix}_MODEL`] ?? process.env[`REVIEW_AGENT_${suffix}_MODEL`];
|
|
567
|
+
}
|
|
568
|
+
function mergeToolSettings(...settings) {
|
|
569
|
+
const merged = {};
|
|
570
|
+
for (const setting of settings) {
|
|
571
|
+
if (!setting) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
for (const [toolName, enabled] of Object.entries(setting)) {
|
|
575
|
+
if (typeof enabled === 'boolean') {
|
|
576
|
+
merged[toolName] = enabled;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
581
|
+
}
|
|
582
|
+
function mergeRunSettings(...settings) {
|
|
583
|
+
const merged = {};
|
|
584
|
+
for (const setting of settings) {
|
|
585
|
+
if (!setting) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
if (setting.prompt !== undefined)
|
|
589
|
+
merged.prompt = setting.prompt;
|
|
590
|
+
if (setting.promptFile !== undefined)
|
|
591
|
+
merged.promptFile = setting.promptFile;
|
|
592
|
+
if (setting.output !== undefined)
|
|
593
|
+
merged.output = setting.output;
|
|
594
|
+
if (setting.json !== undefined)
|
|
595
|
+
merged.json = setting.json;
|
|
596
|
+
}
|
|
597
|
+
return merged;
|
|
598
|
+
}
|
|
258
599
|
export function getDefaultModel(config) {
|
|
259
|
-
return
|
|
260
|
-
config.review.defaultModel ??
|
|
261
|
-
process.env.REVIEW_DEFAULT_MODEL ??
|
|
262
|
-
undefined);
|
|
600
|
+
return config.agents.default?.model ?? getDefaultModelEnv();
|
|
263
601
|
}
|
|
264
602
|
export function getDefaultThinkingLevel(config) {
|
|
265
|
-
return config.
|
|
603
|
+
return config.agents.default?.thinkingLevel;
|
|
604
|
+
}
|
|
605
|
+
export function resolveAgentThinkingLevel(config, agentId) {
|
|
606
|
+
const { namespace } = requireAgentId(agentId);
|
|
607
|
+
return (getAgentOverride(config, agentId).thinkingLevel ??
|
|
608
|
+
getNamespaceDefaults(config, namespace).thinkingLevel ??
|
|
609
|
+
getDefaultThinkingLevel(config));
|
|
266
610
|
}
|
|
267
611
|
export function getDefaultSkills(config) {
|
|
268
|
-
|
|
269
|
-
return [];
|
|
270
|
-
}
|
|
271
|
-
return config.review.default.skills.map(String).filter((skill) => skill.length > 0);
|
|
612
|
+
return dedupeStrings((config.agents.default?.skills ?? []).map(String));
|
|
272
613
|
}
|
|
273
|
-
function
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
614
|
+
export function resolveAgentSkills(config, agentId, definitionSkills = [], additionalSkills = [], precomputedReviewAgentConfig) {
|
|
615
|
+
const { namespace } = requireAgentId(agentId);
|
|
616
|
+
const namespaceDefaults = getNamespaceDefaults(config, namespace);
|
|
617
|
+
const agentOverride = getAgentOverride(config, agentId);
|
|
618
|
+
const reviewAgentConfig = precomputedReviewAgentConfig === undefined
|
|
619
|
+
? normalizeAgentConfig(config.review.agents).find((agent) => agent.name === agentId)
|
|
620
|
+
: precomputedReviewAgentConfig;
|
|
621
|
+
return dedupeStrings([
|
|
622
|
+
...(config.agents.default?.skills ?? []),
|
|
623
|
+
...(namespaceDefaults.skills ?? []),
|
|
624
|
+
...definitionSkills,
|
|
625
|
+
...(agentOverride.skills ?? []),
|
|
626
|
+
...(reviewAgentConfig?.skills ?? []),
|
|
627
|
+
...additionalSkills,
|
|
628
|
+
]);
|
|
629
|
+
}
|
|
630
|
+
export function resolveAgentModel(config, agentId, explicitModel) {
|
|
631
|
+
const { namespace } = requireAgentId(agentId);
|
|
632
|
+
const envModel = getAgentModelEnv(agentId);
|
|
633
|
+
return (explicitModel ??
|
|
634
|
+
envModel ??
|
|
635
|
+
getAgentOverride(config, agentId).model ??
|
|
636
|
+
getNamespaceDefaults(config, namespace).model ??
|
|
637
|
+
getDefaultModel(config));
|
|
638
|
+
}
|
|
639
|
+
export function resolveRuntimeAgentModel(config, agentId, definitionModel) {
|
|
640
|
+
const { namespace } = requireAgentId(agentId);
|
|
641
|
+
return (getAgentModelEnv(agentId) ??
|
|
642
|
+
getAgentOverride(config, agentId).model ??
|
|
643
|
+
definitionModel ??
|
|
644
|
+
getNamespaceDefaults(config, namespace).model ??
|
|
645
|
+
getDefaultModel(config));
|
|
646
|
+
}
|
|
647
|
+
export function resolveAgentTools(config, agentId, definitionTools) {
|
|
648
|
+
const { namespace } = requireAgentId(agentId);
|
|
649
|
+
return mergeToolSettings(config.agents.default?.tools, getNamespaceDefaults(config, namespace).tools, definitionTools, getAgentOverride(config, agentId).tools);
|
|
650
|
+
}
|
|
651
|
+
export function resolveAgentRunConfig(config, agentId) {
|
|
652
|
+
const { namespace } = requireAgentId(agentId);
|
|
653
|
+
return mergeRunSettings(config.agents.default?.run, getNamespaceDefaults(config, namespace).run, getAgentOverride(config, agentId).run);
|
|
283
654
|
}
|
|
284
655
|
/**
|
|
285
|
-
* Extract effective agent
|
|
286
|
-
*
|
|
287
|
-
* Primary model: review.agents controls execution order and composition.
|
|
288
|
-
* Legacy compatibility:
|
|
289
|
-
* - mode=unified forces unified-reviewer when not explicitly configured.
|
|
290
|
-
* - mode=hybrid prepends unified-reviewer when not explicitly configured.
|
|
656
|
+
* Extract effective review agent ids from configuration.
|
|
291
657
|
*/
|
|
292
|
-
export function
|
|
293
|
-
|
|
294
|
-
|
|
658
|
+
export function getReviewAgentIds(config) {
|
|
659
|
+
return getReviewAgentIdsFromNormalized(normalizeAgentConfig(config.review.agents));
|
|
660
|
+
}
|
|
661
|
+
function getReviewAgentIdsFromNormalized(normalizedAgents) {
|
|
662
|
+
const configuredAgentIds = normalizedAgents.map((agent) => agent.name);
|
|
663
|
+
const deduped = dedupeStrings(configuredAgentIds);
|
|
664
|
+
for (const agentId of deduped) {
|
|
665
|
+
const { namespace } = requireAgentId(agentId);
|
|
666
|
+
if (namespace !== 'review') {
|
|
667
|
+
throw new Error(`Invalid review agent "${agentId}". Review agents must be in the "review" namespace.`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return deduped;
|
|
295
671
|
}
|
|
296
672
|
/**
|
|
297
673
|
* Build model overrides from config and environment variables
|
|
298
674
|
* Precedence:
|
|
299
675
|
* 1. Per-agent model in config
|
|
300
|
-
* 2. Environment variable
|
|
301
|
-
* 3.
|
|
302
|
-
* 4.
|
|
303
|
-
*
|
|
676
|
+
* 2. Environment variable DRS_AGENT_<NAMESPACE>_<NAME>_MODEL
|
|
677
|
+
* 3. agents.overrides.<agent>.model
|
|
678
|
+
* 4. agents.namespaces.<namespace>.model
|
|
679
|
+
* 5. agents.default.model (falls back to DRS_DEFAULT_MODEL)
|
|
304
680
|
*/
|
|
305
681
|
export function getModelOverrides(config) {
|
|
306
682
|
const overrides = {};
|
|
307
683
|
const normalizedAgents = normalizeAgentConfig(config.review.agents);
|
|
308
684
|
const agentConfigByName = new Map(normalizedAgents.map((agent) => [agent.name, agent]));
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const configuredAgent = agentConfigByName.get(agentName);
|
|
313
|
-
// Check per-agent environment variable (e.g., REVIEW_AGENT_SECURITY_MODEL)
|
|
314
|
-
const envVarName = `REVIEW_AGENT_${agentName.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_MODEL`;
|
|
315
|
-
const envModel = process.env[envVarName];
|
|
316
|
-
// Precedence: agent.model > env var > defaultModel
|
|
317
|
-
const model = configuredAgent?.model ?? envModel ?? defaultModel;
|
|
685
|
+
for (const agentId of getReviewAgentIdsFromNormalized(normalizedAgents)) {
|
|
686
|
+
const configuredAgent = agentConfigByName.get(agentId);
|
|
687
|
+
const model = resolveAgentModel(config, agentId, configuredAgent?.model);
|
|
318
688
|
if (model) {
|
|
319
|
-
|
|
320
|
-
overrides[`review/${agentName}`] = model;
|
|
689
|
+
overrides[agentId] = model;
|
|
321
690
|
}
|
|
322
691
|
}
|
|
323
692
|
return overrides;
|
|
@@ -327,14 +696,13 @@ export function getModelOverrides(config) {
|
|
|
327
696
|
* Precedence:
|
|
328
697
|
* 1. review.unified.model in config
|
|
329
698
|
* 2. Environment variable REVIEW_UNIFIED_MODEL
|
|
330
|
-
* 3. review.defaultModel in config (fallback)
|
|
331
|
-
* 4. Environment variable REVIEW_DEFAULT_MODEL (fallback)
|
|
332
699
|
*/
|
|
333
700
|
export function getUnifiedModelOverride(config) {
|
|
334
701
|
const overrides = {};
|
|
335
|
-
const
|
|
702
|
+
const agentId = 'review/unified-reviewer';
|
|
703
|
+
const unifiedModel = config.review.unified?.model ?? process.env.REVIEW_UNIFIED_MODEL;
|
|
336
704
|
if (unifiedModel) {
|
|
337
|
-
overrides[
|
|
705
|
+
overrides[agentId] = unifiedModel;
|
|
338
706
|
}
|
|
339
707
|
return overrides;
|
|
340
708
|
}
|
|
@@ -343,15 +711,21 @@ export function getUnifiedModelOverride(config) {
|
|
|
343
711
|
* Precedence:
|
|
344
712
|
* 1. describe.model in config
|
|
345
713
|
* 2. Environment variable DESCRIBE_MODEL
|
|
346
|
-
* 3.
|
|
347
|
-
* 4.
|
|
714
|
+
* 3. agents.overrides["describe/pr-describer"].model
|
|
715
|
+
* 4. agents.namespaces.describe.model
|
|
716
|
+
* 5. agents.default.model (falls back to DRS_DEFAULT_MODEL)
|
|
348
717
|
*/
|
|
349
718
|
export function getDescriberModelOverride(config) {
|
|
350
719
|
const overrides = {};
|
|
351
|
-
|
|
352
|
-
const describerModel = config.describe?.model ?? process.env.DESCRIBE_MODEL
|
|
720
|
+
const agentId = 'describe/pr-describer';
|
|
721
|
+
const describerModel = config.describe?.model ?? process.env.DESCRIBE_MODEL;
|
|
353
722
|
if (describerModel) {
|
|
354
|
-
overrides[
|
|
723
|
+
overrides[agentId] = describerModel;
|
|
724
|
+
return overrides;
|
|
725
|
+
}
|
|
726
|
+
const genericModel = resolveAgentModel(config, agentId);
|
|
727
|
+
if (genericModel) {
|
|
728
|
+
overrides[agentId] = genericModel;
|
|
355
729
|
}
|
|
356
730
|
return overrides;
|
|
357
731
|
}
|