@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.
Files changed (387) hide show
  1. package/.pi/agents/describe/pr-describer.md +14 -0
  2. package/.pi/agents/review/unified-reviewer.md +31 -1
  3. package/.pi/agents/task/agents-md-updater.md +26 -0
  4. package/.pi/agents/task/changelog-updater.md +29 -0
  5. package/.pi/agents/task/review-issue-fixer.md +42 -0
  6. package/.pi/agents/visual/pr-explainer.md +205 -0
  7. package/.pi/workflows/github-pr-describe.yaml +26 -0
  8. package/.pi/workflows/github-pr-fix-review-issues-stacked.yaml +148 -0
  9. package/.pi/workflows/github-pr-post-comment.yaml +19 -0
  10. package/.pi/workflows/github-pr-review-post.yaml +43 -0
  11. package/.pi/workflows/github-pr-review.yaml +364 -0
  12. package/.pi/workflows/github-pr-show-changes.yaml +25 -0
  13. package/.pi/workflows/github-pr-update-agents-md-stacked.yaml +103 -0
  14. package/.pi/workflows/github-pr-visual-explain.yaml +35 -0
  15. package/.pi/workflows/gitlab-mr-describe.yaml +24 -0
  16. package/.pi/workflows/gitlab-mr-fix-review-issues-stacked.yaml +144 -0
  17. package/.pi/workflows/gitlab-mr-post-comment.yaml +17 -0
  18. package/.pi/workflows/gitlab-mr-review.yaml +364 -0
  19. package/.pi/workflows/gitlab-mr-show-changes.yaml +23 -0
  20. package/.pi/workflows/gitlab-mr-update-agents-md-stacked.yaml +100 -0
  21. package/.pi/workflows/gitlab-mr-visual-explain.yaml +33 -0
  22. package/.pi/workflows/local-changelog-update.yaml +23 -0
  23. package/.pi/workflows/local-fix-review-issues.yaml +111 -0
  24. package/.pi/workflows/local-review.yaml +24 -0
  25. package/.pi/workflows/local-update-agents-md.yaml +24 -0
  26. package/.pi/workflows/local-visual-explain.yaml +31 -0
  27. package/.pi/workflows/release-changelog-finalize.yaml +47 -0
  28. package/.pi/workflows/tag-changelog-update.yaml +26 -0
  29. package/README.md +281 -104
  30. package/dist/ci/runner.d.ts.map +1 -1
  31. package/dist/ci/runner.js +9 -8
  32. package/dist/ci/runner.js.map +1 -1
  33. package/dist/cli/index.js +95 -325
  34. package/dist/cli/index.js.map +1 -1
  35. package/dist/cli/init.d.ts.map +1 -1
  36. package/dist/cli/init.js +25 -23
  37. package/dist/cli/init.js.map +1 -1
  38. package/dist/cli/run-agent.d.ts +26 -0
  39. package/dist/cli/run-agent.d.ts.map +1 -0
  40. package/dist/cli/run-agent.js +143 -0
  41. package/dist/cli/run-agent.js.map +1 -0
  42. package/dist/cli/workflow.d.ts +105 -0
  43. package/dist/cli/workflow.d.ts.map +1 -0
  44. package/dist/cli/workflow.js +3309 -0
  45. package/dist/cli/workflow.js.map +1 -0
  46. package/dist/github/client.d.ts +12 -0
  47. package/dist/github/client.d.ts.map +1 -1
  48. package/dist/github/client.js +27 -0
  49. package/dist/github/client.js.map +1 -1
  50. package/dist/github/platform-adapter.d.ts +6 -1
  51. package/dist/github/platform-adapter.d.ts.map +1 -1
  52. package/dist/github/platform-adapter.js +84 -8
  53. package/dist/github/platform-adapter.js.map +1 -1
  54. package/dist/gitlab/client.d.ts +11 -0
  55. package/dist/gitlab/client.d.ts.map +1 -1
  56. package/dist/gitlab/client.js +11 -0
  57. package/dist/gitlab/client.js.map +1 -1
  58. package/dist/gitlab/platform-adapter.d.ts +3 -1
  59. package/dist/gitlab/platform-adapter.d.ts.map +1 -1
  60. package/dist/gitlab/platform-adapter.js +32 -1
  61. package/dist/gitlab/platform-adapter.js.map +1 -1
  62. package/dist/lib/agent-id.d.ts +9 -0
  63. package/dist/lib/agent-id.d.ts.map +1 -0
  64. package/dist/lib/agent-id.js +32 -0
  65. package/dist/lib/agent-id.js.map +1 -0
  66. package/dist/lib/comment-formatter.d.ts +15 -1
  67. package/dist/lib/comment-formatter.d.ts.map +1 -1
  68. package/dist/lib/comment-formatter.js +53 -4
  69. package/dist/lib/comment-formatter.js.map +1 -1
  70. package/dist/lib/comment-manager.d.ts +4 -0
  71. package/dist/lib/comment-manager.d.ts.map +1 -1
  72. package/dist/lib/comment-manager.js +7 -1
  73. package/dist/lib/comment-manager.js.map +1 -1
  74. package/dist/lib/comment-poster.d.ts +2 -2
  75. package/dist/lib/comment-poster.d.ts.map +1 -1
  76. package/dist/lib/comment-poster.js +31 -4
  77. package/dist/lib/comment-poster.js.map +1 -1
  78. package/dist/lib/config.d.ts +160 -44
  79. package/dist/lib/config.d.ts.map +1 -1
  80. package/dist/lib/config.js +475 -101
  81. package/dist/lib/config.js.map +1 -1
  82. package/dist/lib/context-compression.d.ts +10 -0
  83. package/dist/lib/context-compression.d.ts.map +1 -1
  84. package/dist/lib/context-compression.js +101 -13
  85. package/dist/lib/context-compression.js.map +1 -1
  86. package/dist/lib/context-loader.d.ts +5 -4
  87. package/dist/lib/context-loader.d.ts.map +1 -1
  88. package/dist/lib/context-loader.js +79 -7
  89. package/dist/lib/context-loader.js.map +1 -1
  90. package/dist/lib/describe-core.d.ts.map +1 -1
  91. package/dist/lib/describe-core.js +3 -2
  92. package/dist/lib/describe-core.js.map +1 -1
  93. package/dist/lib/description-executor.js +1 -1
  94. package/dist/lib/description-executor.js.map +1 -1
  95. package/dist/lib/diff-lines.d.ts +18 -0
  96. package/dist/lib/diff-lines.d.ts.map +1 -0
  97. package/dist/lib/diff-lines.js +40 -0
  98. package/dist/lib/diff-lines.js.map +1 -0
  99. package/dist/lib/exit.js +4 -4
  100. package/dist/lib/exit.js.map +1 -1
  101. package/dist/lib/html-artifact.d.ts +14 -0
  102. package/dist/lib/html-artifact.d.ts.map +1 -0
  103. package/dist/lib/html-artifact.js +59 -0
  104. package/dist/lib/html-artifact.js.map +1 -0
  105. package/dist/lib/issue-parser.js +3 -3
  106. package/dist/lib/issue-parser.js.map +1 -1
  107. package/dist/lib/json-output-schema.d.ts +70 -0
  108. package/dist/lib/json-output-schema.d.ts.map +1 -1
  109. package/dist/lib/json-output-schema.js +40 -0
  110. package/dist/lib/json-output-schema.js.map +1 -1
  111. package/dist/lib/logger.d.ts +1 -1
  112. package/dist/lib/logger.d.ts.map +1 -1
  113. package/dist/lib/platform-client.d.ts +26 -0
  114. package/dist/lib/platform-client.d.ts.map +1 -1
  115. package/dist/lib/review-artifact.d.ts +69 -0
  116. package/dist/lib/review-artifact.d.ts.map +1 -0
  117. package/dist/lib/review-artifact.js +171 -0
  118. package/dist/lib/review-artifact.js.map +1 -0
  119. package/dist/lib/review-core.d.ts +6 -4
  120. package/dist/lib/review-core.d.ts.map +1 -1
  121. package/dist/lib/review-core.js +88 -173
  122. package/dist/lib/review-core.js.map +1 -1
  123. package/dist/lib/review-orchestrator.d.ts +23 -0
  124. package/dist/lib/review-orchestrator.d.ts.map +1 -1
  125. package/dist/lib/review-orchestrator.js +31 -21
  126. package/dist/lib/review-orchestrator.js.map +1 -1
  127. package/dist/lib/review-usage.d.ts +4 -0
  128. package/dist/lib/review-usage.d.ts.map +1 -1
  129. package/dist/lib/review-usage.js +25 -0
  130. package/dist/lib/review-usage.js.map +1 -1
  131. package/dist/lib/trace-collector.d.ts +105 -0
  132. package/dist/lib/trace-collector.d.ts.map +1 -0
  133. package/dist/lib/trace-collector.js +255 -0
  134. package/dist/lib/trace-collector.js.map +1 -0
  135. package/dist/lib/trace-html.d.ts +3 -0
  136. package/dist/lib/trace-html.d.ts.map +1 -0
  137. package/dist/lib/trace-html.js +349 -0
  138. package/dist/lib/trace-html.js.map +1 -0
  139. package/dist/lib/workflow-artifacts.d.ts +54 -0
  140. package/dist/lib/workflow-artifacts.d.ts.map +1 -0
  141. package/dist/lib/workflow-artifacts.js +150 -0
  142. package/dist/lib/workflow-artifacts.js.map +1 -0
  143. package/dist/pi/sdk.d.ts.map +1 -1
  144. package/dist/pi/sdk.js +605 -16
  145. package/dist/pi/sdk.js.map +1 -1
  146. package/dist/runtime/agent-loader.d.ts +10 -6
  147. package/dist/runtime/agent-loader.d.ts.map +1 -1
  148. package/dist/runtime/agent-loader.js +55 -29
  149. package/dist/runtime/agent-loader.js.map +1 -1
  150. package/dist/runtime/built-in-paths.d.ts +1 -0
  151. package/dist/runtime/built-in-paths.d.ts.map +1 -1
  152. package/dist/runtime/built-in-paths.js +7 -0
  153. package/dist/runtime/built-in-paths.js.map +1 -1
  154. package/dist/runtime/client.d.ts +14 -0
  155. package/dist/runtime/client.d.ts.map +1 -1
  156. package/dist/runtime/client.js +87 -56
  157. package/dist/runtime/client.js.map +1 -1
  158. package/dist/runtime/path-config.d.ts +2 -2
  159. package/dist/runtime/path-config.d.ts.map +1 -1
  160. package/dist/runtime/path-config.js +8 -8
  161. package/dist/runtime/path-config.js.map +1 -1
  162. package/package.json +22 -16
  163. package/.pi/agents/review/documentation.md +0 -56
  164. package/.pi/agents/review/performance.md +0 -53
  165. package/.pi/agents/review/quality.md +0 -59
  166. package/.pi/agents/review/security.md +0 -53
  167. package/.pi/agents/review/style.md +0 -132
  168. package/dist/cli/describe-mr.d.ts +0 -11
  169. package/dist/cli/describe-mr.d.ts.map +0 -1
  170. package/dist/cli/describe-mr.js +0 -134
  171. package/dist/cli/describe-mr.js.map +0 -1
  172. package/dist/cli/describe-pr.d.ts +0 -12
  173. package/dist/cli/describe-pr.d.ts.map +0 -1
  174. package/dist/cli/describe-pr.js +0 -135
  175. package/dist/cli/describe-pr.js.map +0 -1
  176. package/dist/cli/post-comments.d.ts +0 -20
  177. package/dist/cli/post-comments.d.ts.map +0 -1
  178. package/dist/cli/post-comments.js +0 -225
  179. package/dist/cli/post-comments.js.map +0 -1
  180. package/dist/cli/review-local.d.ts +0 -13
  181. package/dist/cli/review-local.d.ts.map +0 -1
  182. package/dist/cli/review-local.integration.test.d.ts +0 -2
  183. package/dist/cli/review-local.integration.test.d.ts.map +0 -1
  184. package/dist/cli/review-local.integration.test.js +0 -343
  185. package/dist/cli/review-local.integration.test.js.map +0 -1
  186. package/dist/cli/review-local.js +0 -90
  187. package/dist/cli/review-local.js.map +0 -1
  188. package/dist/cli/review-local.live.e2e.test.d.ts +0 -2
  189. package/dist/cli/review-local.live.e2e.test.d.ts.map +0 -1
  190. package/dist/cli/review-local.live.e2e.test.js +0 -153
  191. package/dist/cli/review-local.live.e2e.test.js.map +0 -1
  192. package/dist/cli/review-local.test.d.ts +0 -2
  193. package/dist/cli/review-local.test.d.ts.map +0 -1
  194. package/dist/cli/review-local.test.js +0 -164
  195. package/dist/cli/review-local.test.js.map +0 -1
  196. package/dist/cli/review-mr.d.ts +0 -22
  197. package/dist/cli/review-mr.d.ts.map +0 -1
  198. package/dist/cli/review-mr.js +0 -181
  199. package/dist/cli/review-mr.js.map +0 -1
  200. package/dist/cli/review-mr.test.d.ts +0 -2
  201. package/dist/cli/review-mr.test.d.ts.map +0 -1
  202. package/dist/cli/review-mr.test.js +0 -142
  203. package/dist/cli/review-mr.test.js.map +0 -1
  204. package/dist/cli/review-pr.d.ts +0 -22
  205. package/dist/cli/review-pr.d.ts.map +0 -1
  206. package/dist/cli/review-pr.js +0 -181
  207. package/dist/cli/review-pr.js.map +0 -1
  208. package/dist/cli/review-pr.test.d.ts +0 -2
  209. package/dist/cli/review-pr.test.d.ts.map +0 -1
  210. package/dist/cli/review-pr.test.js +0 -137
  211. package/dist/cli/review-pr.test.js.map +0 -1
  212. package/dist/cli/review-url.d.ts +0 -35
  213. package/dist/cli/review-url.d.ts.map +0 -1
  214. package/dist/cli/review-url.js +0 -110
  215. package/dist/cli/review-url.js.map +0 -1
  216. package/dist/cli/review-url.test.d.ts +0 -2
  217. package/dist/cli/review-url.test.d.ts.map +0 -1
  218. package/dist/cli/review-url.test.js +0 -132
  219. package/dist/cli/review-url.test.js.map +0 -1
  220. package/dist/cli/show-changes.d.ts +0 -15
  221. package/dist/cli/show-changes.d.ts.map +0 -1
  222. package/dist/cli/show-changes.js +0 -184
  223. package/dist/cli/show-changes.js.map +0 -1
  224. package/dist/github/client.test.d.ts +0 -2
  225. package/dist/github/client.test.d.ts.map +0 -1
  226. package/dist/github/client.test.js +0 -206
  227. package/dist/github/client.test.js.map +0 -1
  228. package/dist/github/platform-adapter.test.d.ts +0 -2
  229. package/dist/github/platform-adapter.test.d.ts.map +0 -1
  230. package/dist/github/platform-adapter.test.js +0 -40
  231. package/dist/github/platform-adapter.test.js.map +0 -1
  232. package/dist/gitlab/diff-parser.test.d.ts +0 -2
  233. package/dist/gitlab/diff-parser.test.d.ts.map +0 -1
  234. package/dist/gitlab/diff-parser.test.js +0 -315
  235. package/dist/gitlab/diff-parser.test.js.map +0 -1
  236. package/dist/gitlab/platform-adapter.test.d.ts +0 -2
  237. package/dist/gitlab/platform-adapter.test.d.ts.map +0 -1
  238. package/dist/gitlab/platform-adapter.test.js +0 -21
  239. package/dist/gitlab/platform-adapter.test.js.map +0 -1
  240. package/dist/index.test.d.ts +0 -2
  241. package/dist/index.test.d.ts.map +0 -1
  242. package/dist/index.test.js +0 -7
  243. package/dist/index.test.js.map +0 -1
  244. package/dist/lib/code-quality-report.test.d.ts +0 -2
  245. package/dist/lib/code-quality-report.test.d.ts.map +0 -1
  246. package/dist/lib/code-quality-report.test.js +0 -327
  247. package/dist/lib/code-quality-report.test.js.map +0 -1
  248. package/dist/lib/comment-formatter.test.d.ts +0 -2
  249. package/dist/lib/comment-formatter.test.d.ts.map +0 -1
  250. package/dist/lib/comment-formatter.test.js +0 -694
  251. package/dist/lib/comment-formatter.test.js.map +0 -1
  252. package/dist/lib/comment-manager.test.d.ts +0 -2
  253. package/dist/lib/comment-manager.test.d.ts.map +0 -1
  254. package/dist/lib/comment-manager.test.js +0 -680
  255. package/dist/lib/comment-manager.test.js.map +0 -1
  256. package/dist/lib/comment-poster.test.d.ts +0 -5
  257. package/dist/lib/comment-poster.test.d.ts.map +0 -1
  258. package/dist/lib/comment-poster.test.js +0 -245
  259. package/dist/lib/comment-poster.test.js.map +0 -1
  260. package/dist/lib/config-model-overrides.test.d.ts +0 -12
  261. package/dist/lib/config-model-overrides.test.d.ts.map +0 -1
  262. package/dist/lib/config-model-overrides.test.js +0 -254
  263. package/dist/lib/config-model-overrides.test.js.map +0 -1
  264. package/dist/lib/config.test.d.ts +0 -2
  265. package/dist/lib/config.test.d.ts.map +0 -1
  266. package/dist/lib/config.test.js +0 -73
  267. package/dist/lib/config.test.js.map +0 -1
  268. package/dist/lib/context-compression.test.d.ts +0 -2
  269. package/dist/lib/context-compression.test.d.ts.map +0 -1
  270. package/dist/lib/context-compression.test.js +0 -337
  271. package/dist/lib/context-compression.test.js.map +0 -1
  272. package/dist/lib/context-loader.test.d.ts +0 -2
  273. package/dist/lib/context-loader.test.d.ts.map +0 -1
  274. package/dist/lib/context-loader.test.js +0 -207
  275. package/dist/lib/context-loader.test.js.map +0 -1
  276. package/dist/lib/cursor-fix-link.test.d.ts +0 -2
  277. package/dist/lib/cursor-fix-link.test.d.ts.map +0 -1
  278. package/dist/lib/cursor-fix-link.test.js +0 -70
  279. package/dist/lib/cursor-fix-link.test.js.map +0 -1
  280. package/dist/lib/describe-core.test.d.ts +0 -2
  281. package/dist/lib/describe-core.test.d.ts.map +0 -1
  282. package/dist/lib/describe-core.test.js +0 -208
  283. package/dist/lib/describe-core.test.js.map +0 -1
  284. package/dist/lib/describe-output-path.test.d.ts +0 -2
  285. package/dist/lib/describe-output-path.test.d.ts.map +0 -1
  286. package/dist/lib/describe-output-path.test.js +0 -51
  287. package/dist/lib/describe-output-path.test.js.map +0 -1
  288. package/dist/lib/describe-parser.test.d.ts +0 -2
  289. package/dist/lib/describe-parser.test.d.ts.map +0 -1
  290. package/dist/lib/describe-parser.test.js +0 -282
  291. package/dist/lib/describe-parser.test.js.map +0 -1
  292. package/dist/lib/description-executor.test.d.ts +0 -2
  293. package/dist/lib/description-executor.test.d.ts.map +0 -1
  294. package/dist/lib/description-executor.test.js +0 -128
  295. package/dist/lib/description-executor.test.js.map +0 -1
  296. package/dist/lib/description-formatter.test.d.ts +0 -2
  297. package/dist/lib/description-formatter.test.d.ts.map +0 -1
  298. package/dist/lib/description-formatter.test.js +0 -57
  299. package/dist/lib/description-formatter.test.js.map +0 -1
  300. package/dist/lib/diff-parser.test.d.ts +0 -2
  301. package/dist/lib/diff-parser.test.d.ts.map +0 -1
  302. package/dist/lib/diff-parser.test.js +0 -335
  303. package/dist/lib/diff-parser.test.js.map +0 -1
  304. package/dist/lib/error-comment-poster.test.d.ts +0 -2
  305. package/dist/lib/error-comment-poster.test.d.ts.map +0 -1
  306. package/dist/lib/error-comment-poster.test.js +0 -128
  307. package/dist/lib/error-comment-poster.test.js.map +0 -1
  308. package/dist/lib/exit.test.d.ts +0 -2
  309. package/dist/lib/exit.test.d.ts.map +0 -1
  310. package/dist/lib/exit.test.js +0 -120
  311. package/dist/lib/exit.test.js.map +0 -1
  312. package/dist/lib/issue-parser.test.d.ts +0 -2
  313. package/dist/lib/issue-parser.test.d.ts.map +0 -1
  314. package/dist/lib/issue-parser.test.js +0 -281
  315. package/dist/lib/issue-parser.test.js.map +0 -1
  316. package/dist/lib/json-output-schema.test.d.ts +0 -2
  317. package/dist/lib/json-output-schema.test.d.ts.map +0 -1
  318. package/dist/lib/json-output-schema.test.js +0 -92
  319. package/dist/lib/json-output-schema.test.js.map +0 -1
  320. package/dist/lib/json-output.test.d.ts +0 -2
  321. package/dist/lib/json-output.test.d.ts.map +0 -1
  322. package/dist/lib/json-output.test.js +0 -141
  323. package/dist/lib/json-output.test.js.map +0 -1
  324. package/dist/lib/logger.test.d.ts +0 -2
  325. package/dist/lib/logger.test.d.ts.map +0 -1
  326. package/dist/lib/logger.test.js +0 -324
  327. package/dist/lib/logger.test.js.map +0 -1
  328. package/dist/lib/position-validator.test.d.ts +0 -2
  329. package/dist/lib/position-validator.test.d.ts.map +0 -1
  330. package/dist/lib/position-validator.test.js +0 -128
  331. package/dist/lib/position-validator.test.js.map +0 -1
  332. package/dist/lib/prompt-budget.test.d.ts +0 -2
  333. package/dist/lib/prompt-budget.test.d.ts.map +0 -1
  334. package/dist/lib/prompt-budget.test.js +0 -55
  335. package/dist/lib/prompt-budget.test.js.map +0 -1
  336. package/dist/lib/repository-validator.test.d.ts +0 -5
  337. package/dist/lib/repository-validator.test.d.ts.map +0 -1
  338. package/dist/lib/repository-validator.test.js +0 -341
  339. package/dist/lib/repository-validator.test.js.map +0 -1
  340. package/dist/lib/review-core.test.d.ts +0 -2
  341. package/dist/lib/review-core.test.d.ts.map +0 -1
  342. package/dist/lib/review-core.test.js +0 -600
  343. package/dist/lib/review-core.test.js.map +0 -1
  344. package/dist/lib/review-orchestrator.test.d.ts +0 -2
  345. package/dist/lib/review-orchestrator.test.d.ts.map +0 -1
  346. package/dist/lib/review-orchestrator.test.js +0 -531
  347. package/dist/lib/review-orchestrator.test.js.map +0 -1
  348. package/dist/lib/review-output-path.test.d.ts +0 -2
  349. package/dist/lib/review-output-path.test.d.ts.map +0 -1
  350. package/dist/lib/review-output-path.test.js +0 -83
  351. package/dist/lib/review-output-path.test.js.map +0 -1
  352. package/dist/lib/review-parser.test.d.ts +0 -2
  353. package/dist/lib/review-parser.test.d.ts.map +0 -1
  354. package/dist/lib/review-parser.test.js +0 -130
  355. package/dist/lib/review-parser.test.js.map +0 -1
  356. package/dist/lib/review-usage.test.d.ts +0 -2
  357. package/dist/lib/review-usage.test.d.ts.map +0 -1
  358. package/dist/lib/review-usage.test.js +0 -83
  359. package/dist/lib/review-usage.test.js.map +0 -1
  360. package/dist/lib/unified-review-executor.d.ts +0 -60
  361. package/dist/lib/unified-review-executor.d.ts.map +0 -1
  362. package/dist/lib/unified-review-executor.js +0 -207
  363. package/dist/lib/unified-review-executor.js.map +0 -1
  364. package/dist/lib/unified-review-executor.test.d.ts +0 -5
  365. package/dist/lib/unified-review-executor.test.d.ts.map +0 -1
  366. package/dist/lib/unified-review-executor.test.js +0 -472
  367. package/dist/lib/unified-review-executor.test.js.map +0 -1
  368. package/dist/lib/write-json-output.test.d.ts +0 -2
  369. package/dist/lib/write-json-output.test.d.ts.map +0 -1
  370. package/dist/lib/write-json-output.test.js +0 -259
  371. package/dist/lib/write-json-output.test.js.map +0 -1
  372. package/dist/pi/sdk.test.d.ts +0 -2
  373. package/dist/pi/sdk.test.d.ts.map +0 -1
  374. package/dist/pi/sdk.test.js +0 -449
  375. package/dist/pi/sdk.test.js.map +0 -1
  376. package/dist/runtime/agent-loader.test.d.ts +0 -2
  377. package/dist/runtime/agent-loader.test.d.ts.map +0 -1
  378. package/dist/runtime/agent-loader.test.js +0 -280
  379. package/dist/runtime/agent-loader.test.js.map +0 -1
  380. package/dist/runtime/client.test.d.ts +0 -2
  381. package/dist/runtime/client.test.d.ts.map +0 -1
  382. package/dist/runtime/client.test.js +0 -523
  383. package/dist/runtime/client.test.js.map +0 -1
  384. package/dist/runtime/path-config.test.d.ts +0 -2
  385. package/dist/runtime/path-config.test.d.ts.map +0 -1
  386. package/dist/runtime/path-config.test.js +0 -112
  387. package/dist/runtime/path-config.test.js.map +0 -1
@@ -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: ['security', 'quality', 'style', 'performance', 'documentation'],
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. CLI arguments (passed as overrides)
52
- * 2. Environment variables
53
- * 3. .drs/drs.config.yaml or .drs/drs.config.json
54
- * 4. .gitlab-review.yml
55
- * 5. Default values
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
- let config = { ...DEFAULT_CONFIG };
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
- if (process.env.REVIEW_DEFAULT_MODEL) {
87
- config.review.defaultModel = process.env.REVIEW_DEFAULT_MODEL;
88
- config.review.default = mergeSection(config.review.default, {
89
- model: process.env.REVIEW_DEFAULT_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.review.default = mergeSection(config.review.default, {
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 review.default.model or defaultModel in .drs/drs.config.yaml or REVIEW_DEFAULT_MODEL environment variable.\n' +
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
- if (config.review.mode) {
133
- console.warn('⚠ review.mode is deprecated. Configure review.agents explicitly (include unified-reviewer when needed).');
134
- const validModes = ['multi-agent', 'unified', 'hybrid'];
135
- if (!validModes.includes(config.review.mode)) {
136
- throw new Error(`Invalid review mode "${config.review.mode}". Valid options: ${validModes.join(', ')}`);
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
- if (config.review.unified?.severityThreshold) {
140
- console.warn('⚠ review.unified.severityThreshold is deprecated and ignored by the agents-first pipeline.');
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 (getAgentNames(config).length === 0) {
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 REVIEW_DEFAULT_MODEL environment variable.');
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 (config.review.default?.model ??
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.review.default?.thinkingLevel;
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
- if (!Array.isArray(config.review.default?.skills)) {
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 applyLegacyModeAgentFallback(config, agentNames) {
274
- const deduped = Array.from(new Set(agentNames));
275
- const mode = config.review.mode;
276
- if (mode === 'unified' && !deduped.includes('unified-reviewer')) {
277
- return ['unified-reviewer'];
278
- }
279
- if (mode === 'hybrid' && !deduped.includes('unified-reviewer')) {
280
- return ['unified-reviewer', ...deduped];
281
- }
282
- return deduped;
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 names from configuration.
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 getAgentNames(config) {
293
- const configuredAgentNames = normalizeAgentConfig(config.review.agents).map((agent) => agent.name);
294
- return applyLegacyModeAgentFallback(config, configuredAgentNames);
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 REVIEW_AGENT_<NAME>_MODEL (e.g., REVIEW_AGENT_SECURITY_MODEL)
301
- * 3. defaultModel in config
302
- * 4. Environment variable REVIEW_DEFAULT_MODEL
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
- // Get default model from config or environment
310
- const defaultModel = getDefaultModel(config);
311
- for (const agentName of getAgentNames(config)) {
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
- // Use review/<agent> format which matches how agents are invoked
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 unifiedModel = config.review.unified?.model ?? process.env.REVIEW_UNIFIED_MODEL ?? getDefaultModel(config);
702
+ const agentId = 'review/unified-reviewer';
703
+ const unifiedModel = config.review.unified?.model ?? process.env.REVIEW_UNIFIED_MODEL;
336
704
  if (unifiedModel) {
337
- overrides['review/unified-reviewer'] = unifiedModel;
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. review.defaultModel in config (fallback)
347
- * 4. Environment variable REVIEW_DEFAULT_MODEL (fallback)
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
- // Check for describer-specific model
352
- const describerModel = config.describe?.model ?? process.env.DESCRIBE_MODEL ?? getDefaultModel(config);
720
+ const agentId = 'describe/pr-describer';
721
+ const describerModel = config.describe?.model ?? process.env.DESCRIBE_MODEL;
353
722
  if (describerModel) {
354
- overrides['describe/pr-describer'] = describerModel;
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
  }