@diff-review-system/drs 1.1.2 → 2.1.0

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 (229) hide show
  1. package/.opencode/agent/describe/pr-describer.md +221 -0
  2. package/.opencode/agent/review/documentation.md +56 -0
  3. package/.opencode/agent/review/performance.md +32 -139
  4. package/.opencode/agent/review/quality.md +36 -113
  5. package/.opencode/agent/review/security.md +32 -97
  6. package/.opencode/agent/review/style.md +4 -0
  7. package/.opencode/agent/review/unified-reviewer.md +74 -0
  8. package/.opencode/opencode.jsonc +4 -29
  9. package/.opencode/tool/write_json_output.ts +24 -0
  10. package/README.md +48 -21
  11. package/dist/ci/runner.d.ts.map +1 -1
  12. package/dist/ci/runner.js +2 -0
  13. package/dist/ci/runner.js.map +1 -1
  14. package/dist/cli/describe-mr.d.ts +11 -0
  15. package/dist/cli/describe-mr.d.ts.map +1 -0
  16. package/dist/cli/describe-mr.js +104 -0
  17. package/dist/cli/describe-mr.js.map +1 -0
  18. package/dist/cli/describe-pr.d.ts +12 -0
  19. package/dist/cli/describe-pr.d.ts.map +1 -0
  20. package/dist/cli/describe-pr.js +105 -0
  21. package/dist/cli/describe-pr.js.map +1 -0
  22. package/dist/cli/index.js +222 -16
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/cli/init.d.ts +1 -1
  25. package/dist/cli/init.d.ts.map +1 -1
  26. package/dist/cli/init.js +273 -145
  27. package/dist/cli/init.js.map +1 -1
  28. package/dist/cli/post-comments.d.ts +16 -0
  29. package/dist/cli/post-comments.d.ts.map +1 -0
  30. package/dist/cli/post-comments.js +222 -0
  31. package/dist/cli/post-comments.js.map +1 -0
  32. package/dist/cli/review-local.d.ts +3 -0
  33. package/dist/cli/review-local.d.ts.map +1 -1
  34. package/dist/cli/review-local.js +44 -18
  35. package/dist/cli/review-local.js.map +1 -1
  36. package/dist/cli/review-mr.d.ts +6 -0
  37. package/dist/cli/review-mr.d.ts.map +1 -1
  38. package/dist/cli/review-mr.js +63 -7
  39. package/dist/cli/review-mr.js.map +1 -1
  40. package/dist/cli/review-pr.d.ts +6 -0
  41. package/dist/cli/review-pr.d.ts.map +1 -1
  42. package/dist/cli/review-pr.js +8 -1
  43. package/dist/cli/review-pr.js.map +1 -1
  44. package/dist/cli/show-changes.d.ts +15 -0
  45. package/dist/cli/show-changes.d.ts.map +1 -0
  46. package/dist/cli/show-changes.js +184 -0
  47. package/dist/cli/show-changes.js.map +1 -0
  48. package/dist/github/platform-adapter.d.ts.map +1 -1
  49. package/dist/github/platform-adapter.js +4 -2
  50. package/dist/github/platform-adapter.js.map +1 -1
  51. package/dist/gitlab/client.d.ts.map +1 -1
  52. package/dist/gitlab/client.js +1 -1
  53. package/dist/gitlab/client.js.map +1 -1
  54. package/dist/gitlab/platform-adapter.d.ts.map +1 -1
  55. package/dist/gitlab/platform-adapter.js +6 -5
  56. package/dist/gitlab/platform-adapter.js.map +1 -1
  57. package/dist/lib/change-summary.d.ts +8 -0
  58. package/dist/lib/change-summary.d.ts.map +1 -0
  59. package/dist/lib/change-summary.js +2 -0
  60. package/dist/lib/change-summary.js.map +1 -0
  61. package/dist/lib/comment-formatter.d.ts +3 -2
  62. package/dist/lib/comment-formatter.d.ts.map +1 -1
  63. package/dist/lib/comment-formatter.js +16 -2
  64. package/dist/lib/comment-formatter.js.map +1 -1
  65. package/dist/lib/comment-formatter.test.d.ts +2 -0
  66. package/dist/lib/comment-formatter.test.d.ts.map +1 -0
  67. package/dist/lib/comment-formatter.test.js +607 -0
  68. package/dist/lib/comment-formatter.test.js.map +1 -0
  69. package/dist/lib/comment-manager.test.d.ts +2 -0
  70. package/dist/lib/comment-manager.test.d.ts.map +1 -0
  71. package/dist/lib/comment-manager.test.js +618 -0
  72. package/dist/lib/comment-manager.test.js.map +1 -0
  73. package/dist/lib/comment-poster.d.ts +21 -0
  74. package/dist/lib/comment-poster.d.ts.map +1 -0
  75. package/dist/lib/comment-poster.js +96 -0
  76. package/dist/lib/comment-poster.js.map +1 -0
  77. package/dist/lib/comment-poster.test.d.ts +5 -0
  78. package/dist/lib/comment-poster.test.d.ts.map +1 -0
  79. package/dist/lib/comment-poster.test.js +215 -0
  80. package/dist/lib/comment-poster.test.js.map +1 -0
  81. package/dist/lib/config-model-overrides.test.js +71 -41
  82. package/dist/lib/config-model-overrides.test.js.map +1 -1
  83. package/dist/lib/config.d.ts +65 -7
  84. package/dist/lib/config.d.ts.map +1 -1
  85. package/dist/lib/config.js +122 -22
  86. package/dist/lib/config.js.map +1 -1
  87. package/dist/lib/config.test.js +31 -2
  88. package/dist/lib/config.test.js.map +1 -1
  89. package/dist/lib/context-compression.d.ts +19 -0
  90. package/dist/lib/context-compression.d.ts.map +1 -0
  91. package/dist/lib/context-compression.js +170 -0
  92. package/dist/lib/context-compression.js.map +1 -0
  93. package/dist/lib/context-compression.test.d.ts +2 -0
  94. package/dist/lib/context-compression.test.d.ts.map +1 -0
  95. package/dist/lib/context-compression.test.js +33 -0
  96. package/dist/lib/context-compression.test.js.map +1 -0
  97. package/dist/lib/context-loader.d.ts.map +1 -1
  98. package/dist/lib/context-loader.js +8 -1
  99. package/dist/lib/context-loader.js.map +1 -1
  100. package/dist/lib/context-loader.test.d.ts +2 -0
  101. package/dist/lib/context-loader.test.d.ts.map +1 -0
  102. package/dist/lib/context-loader.test.js +207 -0
  103. package/dist/lib/context-loader.test.js.map +1 -0
  104. package/dist/lib/describe-core.d.ts +9 -0
  105. package/dist/lib/describe-core.d.ts.map +1 -0
  106. package/dist/lib/describe-core.js +71 -0
  107. package/dist/lib/describe-core.js.map +1 -0
  108. package/dist/lib/describe-core.test.d.ts +2 -0
  109. package/dist/lib/describe-core.test.d.ts.map +1 -0
  110. package/dist/lib/describe-core.test.js +208 -0
  111. package/dist/lib/describe-core.test.js.map +1 -0
  112. package/dist/lib/describe-output-path.test.d.ts +2 -0
  113. package/dist/lib/describe-output-path.test.d.ts.map +1 -0
  114. package/dist/lib/describe-output-path.test.js +51 -0
  115. package/dist/lib/describe-output-path.test.js.map +1 -0
  116. package/dist/lib/describe-parser.d.ts +3 -0
  117. package/dist/lib/describe-parser.d.ts.map +1 -0
  118. package/dist/lib/describe-parser.js +163 -0
  119. package/dist/lib/describe-parser.js.map +1 -0
  120. package/dist/lib/describe-parser.test.d.ts +2 -0
  121. package/dist/lib/describe-parser.test.d.ts.map +1 -0
  122. package/dist/lib/describe-parser.test.js +282 -0
  123. package/dist/lib/describe-parser.test.js.map +1 -0
  124. package/dist/lib/description-executor.d.ts +22 -0
  125. package/dist/lib/description-executor.d.ts.map +1 -0
  126. package/dist/lib/description-executor.js +72 -0
  127. package/dist/lib/description-executor.js.map +1 -0
  128. package/dist/lib/description-formatter.d.ts +37 -0
  129. package/dist/lib/description-formatter.d.ts.map +1 -0
  130. package/dist/lib/description-formatter.js +219 -0
  131. package/dist/lib/description-formatter.js.map +1 -0
  132. package/dist/lib/diff-parser.d.ts +11 -0
  133. package/dist/lib/diff-parser.d.ts.map +1 -1
  134. package/dist/lib/diff-parser.js +37 -0
  135. package/dist/lib/diff-parser.js.map +1 -1
  136. package/dist/lib/issue-parser.d.ts +1 -1
  137. package/dist/lib/issue-parser.d.ts.map +1 -1
  138. package/dist/lib/issue-parser.js +16 -14
  139. package/dist/lib/issue-parser.js.map +1 -1
  140. package/dist/lib/json-output-schema.d.ts +207 -0
  141. package/dist/lib/json-output-schema.d.ts.map +1 -0
  142. package/dist/lib/json-output-schema.js +124 -0
  143. package/dist/lib/json-output-schema.js.map +1 -0
  144. package/dist/lib/json-output-schema.test.d.ts +2 -0
  145. package/dist/lib/json-output-schema.test.d.ts.map +1 -0
  146. package/dist/lib/json-output-schema.test.js +92 -0
  147. package/dist/lib/json-output-schema.test.js.map +1 -0
  148. package/dist/lib/json-output.d.ts +43 -0
  149. package/dist/lib/json-output.d.ts.map +1 -0
  150. package/dist/lib/json-output.js +34 -0
  151. package/dist/lib/json-output.js.map +1 -0
  152. package/dist/lib/output-paths.d.ts +6 -0
  153. package/dist/lib/output-paths.d.ts.map +1 -0
  154. package/dist/lib/output-paths.js +5 -0
  155. package/dist/lib/output-paths.js.map +1 -0
  156. package/dist/lib/platform-client.d.ts +1 -1
  157. package/dist/lib/platform-client.d.ts.map +1 -1
  158. package/dist/lib/repository-validator.d.ts +57 -0
  159. package/dist/lib/repository-validator.d.ts.map +1 -0
  160. package/dist/lib/repository-validator.js +225 -0
  161. package/dist/lib/repository-validator.js.map +1 -0
  162. package/dist/lib/repository-validator.test.d.ts +5 -0
  163. package/dist/lib/repository-validator.test.d.ts.map +1 -0
  164. package/dist/lib/repository-validator.test.js +341 -0
  165. package/dist/lib/repository-validator.test.js.map +1 -0
  166. package/dist/lib/review-core.d.ts +66 -0
  167. package/dist/lib/review-core.d.ts.map +1 -0
  168. package/dist/lib/review-core.js +449 -0
  169. package/dist/lib/review-core.js.map +1 -0
  170. package/dist/lib/review-core.test.d.ts +2 -0
  171. package/dist/lib/review-core.test.d.ts.map +1 -0
  172. package/dist/lib/review-core.test.js +552 -0
  173. package/dist/lib/review-core.test.js.map +1 -0
  174. package/dist/lib/review-orchestrator.d.ts +27 -10
  175. package/dist/lib/review-orchestrator.d.ts.map +1 -1
  176. package/dist/lib/review-orchestrator.js +51 -110
  177. package/dist/lib/review-orchestrator.js.map +1 -1
  178. package/dist/lib/review-orchestrator.test.d.ts +2 -0
  179. package/dist/lib/review-orchestrator.test.d.ts.map +1 -0
  180. package/dist/lib/review-orchestrator.test.js +413 -0
  181. package/dist/lib/review-orchestrator.test.js.map +1 -0
  182. package/dist/lib/review-output-path.test.d.ts +2 -0
  183. package/dist/lib/review-output-path.test.d.ts.map +1 -0
  184. package/dist/lib/review-output-path.test.js +83 -0
  185. package/dist/lib/review-output-path.test.js.map +1 -0
  186. package/dist/lib/review-parser.d.ts +2 -0
  187. package/dist/lib/review-parser.d.ts.map +1 -0
  188. package/dist/lib/review-parser.js +100 -0
  189. package/dist/lib/review-parser.js.map +1 -0
  190. package/dist/lib/unified-review-executor.d.ts +21 -4
  191. package/dist/lib/unified-review-executor.d.ts.map +1 -1
  192. package/dist/lib/unified-review-executor.js +84 -151
  193. package/dist/lib/unified-review-executor.js.map +1 -1
  194. package/dist/lib/unified-review-executor.test.d.ts +5 -0
  195. package/dist/lib/unified-review-executor.test.d.ts.map +1 -0
  196. package/dist/lib/unified-review-executor.test.js +350 -0
  197. package/dist/lib/unified-review-executor.test.js.map +1 -0
  198. package/dist/lib/write-json-output.d.ts +13 -0
  199. package/dist/lib/write-json-output.d.ts.map +1 -0
  200. package/dist/lib/write-json-output.js +37 -0
  201. package/dist/lib/write-json-output.js.map +1 -0
  202. package/dist/opencode/agent-loader.d.ts +3 -4
  203. package/dist/opencode/agent-loader.d.ts.map +1 -1
  204. package/dist/opencode/agent-loader.js +48 -34
  205. package/dist/opencode/agent-loader.js.map +1 -1
  206. package/dist/opencode/agent-skill-overlay.d.ts +11 -0
  207. package/dist/opencode/agent-skill-overlay.d.ts.map +1 -0
  208. package/dist/opencode/agent-skill-overlay.js +164 -0
  209. package/dist/opencode/agent-skill-overlay.js.map +1 -0
  210. package/dist/opencode/client.d.ts +11 -3
  211. package/dist/opencode/client.d.ts.map +1 -1
  212. package/dist/opencode/client.js +237 -71
  213. package/dist/opencode/client.js.map +1 -1
  214. package/dist/opencode/client.test.d.ts +2 -0
  215. package/dist/opencode/client.test.d.ts.map +1 -0
  216. package/dist/opencode/client.test.js +317 -0
  217. package/dist/opencode/client.test.js.map +1 -0
  218. package/dist/opencode/opencode-paths.d.ts +2 -0
  219. package/dist/opencode/opencode-paths.d.ts.map +1 -0
  220. package/dist/opencode/opencode-paths.js +7 -0
  221. package/dist/opencode/opencode-paths.js.map +1 -0
  222. package/dist/opencode/skill-loader.d.ts +6 -0
  223. package/dist/opencode/skill-loader.d.ts.map +1 -0
  224. package/dist/opencode/skill-loader.js +36 -0
  225. package/dist/opencode/skill-loader.js.map +1 -0
  226. package/package.json +7 -7
  227. package/.opencode/agent/github-reviewer.md +0 -77
  228. package/.opencode/agent/gitlab-reviewer.md +0 -77
  229. package/.opencode/agent/local-reviewer.md +0 -63
@@ -1 +1 @@
1
- {"version":3,"file":"review-orchestrator.d.ts","sourceRoot":"","sources":["../../src/lib/review-orchestrator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAgC,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE1F,OAAO,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG5E;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,oCAAoC;IACpC,OAAO,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;IAC7C,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,CAE/E;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC,CAoBzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,YAAY,CAAC,CA8IvB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAgB/D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAE/D"}
1
+ {"version":3,"file":"review-orchestrator.d.ts","sourceRoot":"","sources":["../../src/lib/review-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAgC,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAGL,oBAAoB,IAAI,cAAc,EACtC,iBAAiB,IAAI,mBAAmB,EAEzC,MAAM,kBAAkB,CAAC;AAG1B;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wFAAwF;IACxF,cAAc,CAAC,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+DAA+D;IAC/D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,oCAAoC;IACpC,OAAO,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;IAC7C,+CAA+C;IAC/C,aAAa,CAAC,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC;IAC5D,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,CAE/E;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CA0BzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,YAAY,CAAC,CAoFvB;AAGD,eAAO,MAAM,oBAAoB,uBAAiB,CAAC;AACnD,eAAO,MAAM,iBAAiB,4BAAsB,CAAC"}
@@ -1,9 +1,15 @@
1
+ /**
2
+ * Review orchestrator for local diff reviews
3
+ *
4
+ * This module handles local git diff reviews (pre-push analysis).
5
+ * It uses the shared core logic from review-core.ts.
6
+ */
1
7
  import chalk from 'chalk';
2
- import { shouldIgnoreFile, getModelOverrides, getAgentNames } from './config.js';
8
+ import { shouldIgnoreFile, getModelOverrides, getUnifiedModelOverride, } from './config.js';
3
9
  import { createOpencodeClientInstance } from '../opencode/client.js';
4
- import { parseReviewIssues } from './issue-parser.js';
5
10
  import { calculateSummary } from './comment-formatter.js';
6
- import { buildReviewPrompt } from './context-loader.js';
11
+ import { buildBaseInstructions, runReviewPipeline, displayReviewSummary as displaySummary, hasBlockingIssues as checkBlockingIssues, } from './review-core.js';
12
+ import { compressFilesWithDiffs, formatCompressionSummary } from './context-compression.js';
7
13
  /**
8
14
  * Filter files based on ignore patterns in config
9
15
  */
@@ -13,15 +19,21 @@ export function filterIgnoredFiles(files, config) {
13
19
  /**
14
20
  * Connect to OpenCode server (or start in-process)
15
21
  */
16
- export async function connectToOpenCode(config, workingDir) {
22
+ export async function connectToOpenCode(config, workingDir, options) {
17
23
  console.log(chalk.gray('Connecting to OpenCode server...\n'));
18
24
  try {
19
25
  // Get model overrides from DRS config
20
- const modelOverrides = getModelOverrides(config);
26
+ const modelOverrides = options?.modelOverrides ?? {
27
+ ...getModelOverrides(config),
28
+ ...getUnifiedModelOverride(config),
29
+ };
21
30
  return await createOpencodeClientInstance({
22
31
  baseUrl: config.opencode.serverUrl || undefined,
23
32
  directory: workingDir || process.cwd(),
24
33
  modelOverrides,
34
+ provider: config.opencode.provider,
35
+ config,
36
+ debug: options?.debug,
25
37
  });
26
38
  }
27
39
  catch (error) {
@@ -64,120 +76,49 @@ export async function executeReview(config, source) {
64
76
  }
65
77
  console.log(chalk.gray(`Reviewing ${filteredFiles.length} file(s)\n`));
66
78
  // Connect to OpenCode
67
- const opencode = await connectToOpenCode(config, source.workingDir);
79
+ const opencode = await connectToOpenCode(config, source.workingDir, { debug: source.debug });
68
80
  try {
69
- // Execute review
70
- console.log(chalk.gray('Starting code analysis...\n'));
71
- const issues = [];
72
- const baseInstructions = `Review the following files from ${source.name}:
73
-
74
- ${filteredFiles.map((f) => `- ${f}`).join('\n')}
75
-
76
- **Instructions:**
77
- 1. Use the Read tool to examine each changed file
78
- 2. Analyze the code for issues in your specialty area
79
- 3. Output your findings in this JSON format:
80
-
81
- \`\`\`json
82
- {
83
- "issues": [
84
- {
85
- "category": "SECURITY" | "QUALITY" | "STYLE" | "PERFORMANCE",
86
- "severity": "CRITICAL" | "HIGH" | "MEDIUM" | "LOW",
87
- "title": "Brief title",
88
- "file": "path/to/file.ts",
89
- "line": 42,
90
- "problem": "Description of the problem",
91
- "solution": "How to fix it",
92
- "agent": "security" | "quality" | "style" | "performance"
93
- }
94
- ]
95
- }
96
- \`\`\`
97
-
98
- Be thorough and identify all issues. Include line numbers when possible.`;
99
- const agentNames = getAgentNames(config);
100
- const agentPromises = agentNames.map(async (agentType) => {
101
- const agentName = `review/${agentType}`;
102
- console.log(chalk.gray(`Running ${agentType} review...\n`));
103
- try {
104
- const reviewPrompt = buildReviewPrompt(agentType, baseInstructions, source.name, filteredFiles);
105
- const session = await opencode.createSession({
106
- agent: agentName,
107
- message: reviewPrompt,
108
- context: {
109
- ...source.context,
110
- files: filteredFiles,
111
- },
112
- });
113
- const agentIssues = [];
114
- for await (const message of opencode.streamMessages(session.id)) {
115
- if (message.role === 'assistant') {
116
- const parsedIssues = parseReviewIssues(message.content);
117
- if (parsedIssues.length > 0) {
118
- agentIssues.push(...parsedIssues);
119
- console.log(chalk.green(`✓ [${agentType}] Found ${parsedIssues.length} issue(s)`));
120
- }
121
- }
122
- }
123
- await opencode.closeSession(session.id);
124
- return { agentType, success: true, issues: agentIssues };
125
- }
126
- catch (error) {
127
- console.error(chalk.red(`✗ ${agentType} agent failed: ${error}`));
128
- return { agentType, success: false, issues: [] };
129
- }
130
- });
131
- const agentResults = await Promise.all(agentPromises);
132
- const successfulAgents = agentResults.filter((r) => r.success);
133
- const failedAgents = agentResults.filter((r) => !r.success);
134
- if (successfulAgents.length === 0) {
135
- console.error(chalk.red('\n✗ All review agents failed!\n'));
136
- console.error(chalk.yellow('This usually means:\n' +
137
- ' 1. Model configuration is incorrect or missing\n' +
138
- ' 2. API credentials are invalid or missing\n' +
139
- ' 3. Models are not accessible or timed out\n' +
140
- ' 4. Agents cannot find files to review\n'));
141
- await opencode.shutdown();
142
- process.exit(1);
81
+ // Build instructions - use provided diffs if available, otherwise fall back to git command
82
+ const diffCommand = source.staged ? 'git diff --cached -- <file>' : 'git diff -- <file>';
83
+ // Use provided diffs if available (filtered to match filteredFiles)
84
+ let filesForInstructions;
85
+ if (source.filesWithDiffs && source.filesWithDiffs.length > 0) {
86
+ // Filter to only include files that passed ignore patterns
87
+ filesForInstructions = source.filesWithDiffs.filter((f) => filteredFiles.includes(f.filename));
143
88
  }
144
- if (failedAgents.length > 0) {
145
- console.log(chalk.yellow(`\n⚠️ ${failedAgents.length} of ${agentResults.length} agents failed: ${failedAgents.map((r) => r.agentType).join(', ')}\n`));
89
+ else {
90
+ // No diffs provided - agents will need to run git diff
91
+ filesForInstructions = filteredFiles.map((f) => ({ filename: f }));
146
92
  }
147
- agentResults.forEach((result) => issues.push(...result.issues));
148
- const summary = calculateSummary(filteredFiles.length, issues);
93
+ const compression = compressFilesWithDiffs(filesForInstructions, config.contextCompression);
94
+ const compressionSummary = formatCompressionSummary(compression);
95
+ if (compressionSummary) {
96
+ console.log(chalk.yellow('⚠ Diff content trimmed to fit token budget.\n'));
97
+ }
98
+ const baseInstructions = buildBaseInstructions(source.name, compression.files, diffCommand, compressionSummary);
99
+ // Run agents using shared core logic
100
+ const result = await runReviewPipeline(opencode, config, baseInstructions, source.name, filteredFiles, source.context, source.workingDir || process.cwd(), source.debug || false);
149
101
  return {
150
- issues,
151
- summary,
152
- filesReviewed: filteredFiles.length,
102
+ issues: result.issues,
103
+ summary: result.summary,
104
+ changeSummary: result.changeSummary,
105
+ filesReviewed: result.filesReviewed,
153
106
  };
154
107
  }
108
+ catch (error) {
109
+ // Handle "all agents failed" error
110
+ if (error instanceof Error && error.message === 'All review agents failed') {
111
+ await opencode.shutdown();
112
+ process.exit(1);
113
+ }
114
+ throw error;
115
+ }
155
116
  finally {
156
117
  // Always shut down OpenCode client
157
118
  await opencode.shutdown();
158
119
  }
159
120
  }
160
- /**
161
- * Display review summary to terminal (common formatting)
162
- */
163
- export function displayReviewSummary(result) {
164
- console.log(chalk.bold('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
165
- console.log(chalk.bold('📊 Review Summary'));
166
- console.log(chalk.bold('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
167
- console.log(` Files reviewed: ${chalk.cyan(result.summary.filesReviewed)}`);
168
- console.log(` Issues found: ${chalk.yellow(result.summary.issuesFound)}`);
169
- if (result.summary.issuesFound > 0) {
170
- console.log(` 🔴 Critical: ${chalk.red(result.summary.bySeverity.CRITICAL)}`);
171
- console.log(` 🟡 High: ${chalk.yellow(result.summary.bySeverity.HIGH)}`);
172
- console.log(` 🟠 Medium: ${chalk.hex('#FFA500')(result.summary.bySeverity.MEDIUM)}`);
173
- console.log(` ⚪ Low: ${chalk.gray(result.summary.bySeverity.LOW)}`);
174
- }
175
- console.log('');
176
- }
177
- /**
178
- * Check if review has blocking issues (CRITICAL or HIGH)
179
- */
180
- export function hasBlockingIssues(result) {
181
- return result.summary.bySeverity.CRITICAL > 0 || result.summary.bySeverity.HIGH > 0;
182
- }
121
+ // Re-export display functions from core for backward compatibility
122
+ export const displayReviewSummary = displaySummary;
123
+ export const hasBlockingIssues = checkBlockingIssues;
183
124
  //# sourceMappingURL=review-orchestrator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"review-orchestrator.js","sourceRoot":"","sources":["../../src/lib/review-orchestrator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review-orchestrator.js","sourceRoot":"","sources":["../../src/lib/review-orchestrator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=review-orchestrator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-orchestrator.test.d.ts","sourceRoot":"","sources":["../../src/lib/review-orchestrator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,413 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { filterIgnoredFiles, connectToOpenCode, executeReview, } from './review-orchestrator.js';
3
+ // Mock dependencies
4
+ vi.mock('./config.js', async () => {
5
+ const actual = await vi.importActual('./config.js');
6
+ return {
7
+ ...actual,
8
+ shouldIgnoreFile: vi.fn((file, config) => {
9
+ const patterns = config.review.ignorePatterns || [];
10
+ return patterns.some((pattern) => {
11
+ if (pattern.endsWith('/*')) {
12
+ const dir = pattern.slice(0, -2);
13
+ return file.startsWith(dir + '/');
14
+ }
15
+ if (pattern.includes('*')) {
16
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
17
+ return regex.test(file);
18
+ }
19
+ return file === pattern;
20
+ });
21
+ }),
22
+ getModelOverrides: vi.fn(() => ({})),
23
+ getUnifiedModelOverride: vi.fn(() => ({})),
24
+ };
25
+ });
26
+ // Store mock client instance for verification
27
+ let mockOpencodeClient;
28
+ vi.mock('../opencode/client.js', () => ({
29
+ createOpencodeClientInstance: vi.fn(async () => {
30
+ mockOpencodeClient = {
31
+ createSession: vi.fn(async () => ({ id: 'session-1' })),
32
+ streamMessages: vi.fn(async function* () {
33
+ yield {
34
+ role: 'assistant',
35
+ content: JSON.stringify({
36
+ issues: [
37
+ {
38
+ category: 'QUALITY',
39
+ severity: 'MEDIUM',
40
+ title: 'Test issue',
41
+ file: 'src/app.ts',
42
+ line: 10,
43
+ problem: 'Test problem',
44
+ solution: 'Test solution',
45
+ },
46
+ ],
47
+ }),
48
+ };
49
+ }),
50
+ closeSession: vi.fn(async () => { }),
51
+ shutdown: vi.fn(async () => { }),
52
+ };
53
+ return mockOpencodeClient;
54
+ }),
55
+ }));
56
+ vi.mock('./review-core.js', () => ({
57
+ buildBaseInstructions: vi.fn((label) => `Instructions for ${label}`),
58
+ runReviewPipeline: vi.fn(async (_opencode, _config, _instructions, _label, files) => ({
59
+ issues: [
60
+ {
61
+ category: 'QUALITY',
62
+ severity: 'MEDIUM',
63
+ title: 'Test issue',
64
+ file: files[0] || 'src/app.ts',
65
+ line: 10,
66
+ problem: 'Test problem',
67
+ solution: 'Test solution',
68
+ agent: 'quality',
69
+ },
70
+ ],
71
+ summary: {
72
+ filesReviewed: files.length,
73
+ issuesFound: 1,
74
+ bySeverity: { CRITICAL: 0, HIGH: 0, MEDIUM: 1, LOW: 0 },
75
+ byCategory: { SECURITY: 0, QUALITY: 1, STYLE: 0, PERFORMANCE: 0, DOCUMENTATION: 0 },
76
+ },
77
+ filesReviewed: files.length,
78
+ agentResults: [],
79
+ })),
80
+ displayReviewSummary: vi.fn(),
81
+ hasBlockingIssues: vi.fn(() => false),
82
+ }));
83
+ vi.mock('./comment-formatter.js', () => ({
84
+ calculateSummary: vi.fn((filesReviewed, issues) => ({
85
+ filesReviewed,
86
+ issuesFound: issues.length,
87
+ bySeverity: {
88
+ CRITICAL: issues.filter((i) => i.severity === 'CRITICAL').length,
89
+ HIGH: issues.filter((i) => i.severity === 'HIGH').length,
90
+ MEDIUM: issues.filter((i) => i.severity === 'MEDIUM').length,
91
+ LOW: issues.filter((i) => i.severity === 'LOW').length,
92
+ },
93
+ byCategory: {
94
+ SECURITY: issues.filter((i) => i.category === 'SECURITY').length,
95
+ QUALITY: issues.filter((i) => i.category === 'QUALITY').length,
96
+ STYLE: issues.filter((i) => i.category === 'STYLE').length,
97
+ PERFORMANCE: issues.filter((i) => i.category === 'PERFORMANCE').length,
98
+ DOCUMENTATION: issues.filter((i) => i.category === 'DOCUMENTATION').length,
99
+ },
100
+ })),
101
+ }));
102
+ vi.mock('./context-compression.js', () => ({
103
+ compressFilesWithDiffs: vi.fn((files) => ({
104
+ files,
105
+ removedFiles: [],
106
+ removedHunks: 0,
107
+ originalTokens: 1000,
108
+ compressedTokens: 1000,
109
+ })),
110
+ formatCompressionSummary: vi.fn(() => null),
111
+ }));
112
+ describe('review-orchestrator', () => {
113
+ beforeEach(() => {
114
+ vi.spyOn(console, 'log').mockImplementation(() => { });
115
+ vi.spyOn(console, 'error').mockImplementation(() => { });
116
+ });
117
+ describe('filterIgnoredFiles', () => {
118
+ it('should filter files based on ignore patterns', () => {
119
+ const config = {
120
+ review: {
121
+ ignorePatterns: ['*.test.ts', 'dist/*', 'node_modules/*'],
122
+ },
123
+ };
124
+ const files = [
125
+ 'src/app.ts',
126
+ 'src/app.test.ts',
127
+ 'dist/bundle.js',
128
+ 'node_modules/package/index.js',
129
+ 'src/utils.ts',
130
+ ];
131
+ const result = filterIgnoredFiles(files, config);
132
+ expect(result).toEqual(['src/app.ts', 'src/utils.ts']);
133
+ });
134
+ it('should return all files when no ignore patterns', () => {
135
+ const config = {
136
+ opencode: {},
137
+ gitlab: { url: '', token: '' },
138
+ github: { token: '' },
139
+ review: {
140
+ ignorePatterns: [],
141
+ },
142
+ };
143
+ const files = ['src/app.ts', 'src/utils.ts', 'README.md'];
144
+ const result = filterIgnoredFiles(files, config);
145
+ expect(result).toEqual(files);
146
+ });
147
+ it('should handle glob patterns correctly', () => {
148
+ const config = {
149
+ review: {
150
+ ignorePatterns: ['**/*.spec.ts', 'test/**'],
151
+ },
152
+ };
153
+ const files = ['src/app.ts', 'src/app.spec.ts', 'test/integration.ts', 'lib/utils.ts'];
154
+ const result = filterIgnoredFiles(files, config);
155
+ expect(result).toContain('src/app.ts');
156
+ expect(result).toContain('lib/utils.ts');
157
+ });
158
+ it('should handle empty file list', () => {
159
+ const config = {
160
+ review: {
161
+ ignorePatterns: ['*.test.ts'],
162
+ },
163
+ };
164
+ const result = filterIgnoredFiles([], config);
165
+ expect(result).toEqual([]);
166
+ });
167
+ });
168
+ describe('connectToOpenCode', () => {
169
+ it('should connect to OpenCode server successfully', async () => {
170
+ const config = {
171
+ opencode: {
172
+ serverUrl: 'http://localhost:3000',
173
+ },
174
+ review: {},
175
+ };
176
+ const client = await connectToOpenCode(config, '/test/dir');
177
+ expect(client).toBeDefined();
178
+ // eslint-disable-next-line @typescript-eslint/unbound-method
179
+ expect(client.createSession).toBeDefined();
180
+ // eslint-disable-next-line @typescript-eslint/unbound-method
181
+ expect(client.shutdown).toBeDefined();
182
+ });
183
+ it('should handle connection failure', async () => {
184
+ const { createOpencodeClientInstance } = await import('../opencode/client.js');
185
+ vi.mocked(createOpencodeClientInstance).mockRejectedValueOnce(new Error('Connection failed'));
186
+ const config = {
187
+ opencode: {},
188
+ review: {},
189
+ };
190
+ await expect(connectToOpenCode(config)).rejects.toThrow('Connection failed');
191
+ expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Failed to connect to OpenCode server'));
192
+ });
193
+ it('should pass model overrides to OpenCode client', async () => {
194
+ const { createOpencodeClientInstance } = await import('../opencode/client.js');
195
+ const { getModelOverrides, getUnifiedModelOverride } = await import('./config.js');
196
+ vi.mocked(getModelOverrides).mockReturnValue({
197
+ 'review/security': 'claude-opus-4',
198
+ });
199
+ vi.mocked(getUnifiedModelOverride).mockReturnValue({
200
+ 'review/unified-reviewer': 'claude-sonnet-4',
201
+ });
202
+ const config = {
203
+ opencode: {},
204
+ review: {},
205
+ };
206
+ await connectToOpenCode(config, '/test/dir', { debug: true });
207
+ expect(createOpencodeClientInstance).toHaveBeenCalledWith({
208
+ baseUrl: undefined,
209
+ config,
210
+ directory: '/test/dir',
211
+ modelOverrides: {
212
+ 'review/security': 'claude-opus-4',
213
+ 'review/unified-reviewer': 'claude-sonnet-4',
214
+ },
215
+ provider: undefined,
216
+ debug: true,
217
+ });
218
+ });
219
+ it('should use process.cwd() when no working directory provided', async () => {
220
+ const { createOpencodeClientInstance } = await import('../opencode/client.js');
221
+ const config = {
222
+ opencode: {},
223
+ review: {},
224
+ };
225
+ await connectToOpenCode(config);
226
+ expect(createOpencodeClientInstance).toHaveBeenCalledWith(expect.objectContaining({
227
+ config,
228
+ directory: process.cwd(),
229
+ }));
230
+ });
231
+ });
232
+ describe('executeReview', () => {
233
+ let mockConfig;
234
+ beforeEach(() => {
235
+ mockConfig = {
236
+ opencode: {},
237
+ review: {
238
+ agents: ['security', 'quality'],
239
+ ignorePatterns: ['*.test.ts'],
240
+ },
241
+ contextCompression: {
242
+ enabled: false,
243
+ },
244
+ };
245
+ });
246
+ it('should execute review successfully with files', async () => {
247
+ const source = {
248
+ name: 'Local diff',
249
+ files: ['src/app.ts', 'src/utils.ts'],
250
+ context: {},
251
+ workingDir: '/test/dir',
252
+ };
253
+ const result = await executeReview(mockConfig, source);
254
+ expect(result.issues).toBeDefined();
255
+ expect(result.summary).toBeDefined();
256
+ expect(result.filesReviewed).toBe(2);
257
+ expect(result.issues.length).toBeGreaterThan(0);
258
+ });
259
+ it('should filter ignored files', async () => {
260
+ const source = {
261
+ name: 'Local diff',
262
+ files: ['src/app.ts', 'src/app.test.ts', 'src/utils.ts'],
263
+ context: {},
264
+ };
265
+ const result = await executeReview(mockConfig, source);
266
+ // Should have filtered out .test.ts file
267
+ expect(result.filesReviewed).toBe(2);
268
+ });
269
+ it('should return empty result when all files are ignored', async () => {
270
+ const source = {
271
+ name: 'Local diff',
272
+ files: ['test1.test.ts', 'test2.test.ts'],
273
+ context: {},
274
+ };
275
+ const result = await executeReview(mockConfig, source);
276
+ expect(result.issues).toEqual([]);
277
+ expect(result.filesReviewed).toBe(0);
278
+ expect(result.summary.issuesFound).toBe(0);
279
+ });
280
+ it('should handle staged diff command', async () => {
281
+ const { buildBaseInstructions } = await import('./review-core.js');
282
+ const source = {
283
+ name: 'Staged changes',
284
+ files: ['src/app.ts'],
285
+ context: {},
286
+ staged: true,
287
+ };
288
+ await executeReview(mockConfig, source);
289
+ expect(buildBaseInstructions).toHaveBeenCalledWith('Staged changes', expect.anything(), 'git diff --cached -- <file>', null);
290
+ });
291
+ it('should handle unstaged diff command', async () => {
292
+ const { buildBaseInstructions } = await import('./review-core.js');
293
+ const source = {
294
+ name: 'Unstaged changes',
295
+ files: ['src/app.ts'],
296
+ context: {},
297
+ staged: false,
298
+ };
299
+ await executeReview(mockConfig, source);
300
+ expect(buildBaseInstructions).toHaveBeenCalledWith('Unstaged changes', expect.anything(), 'git diff -- <file>', null);
301
+ });
302
+ it('should use provided diffs when available', async () => {
303
+ const source = {
304
+ name: 'PR #123',
305
+ files: ['src/app.ts', 'src/utils.ts'],
306
+ filesWithDiffs: [
307
+ { filename: 'src/app.ts', patch: '+ new code' },
308
+ { filename: 'src/utils.ts', patch: '- old code\n+ new code' },
309
+ ],
310
+ context: {},
311
+ };
312
+ const result = await executeReview(mockConfig, source);
313
+ expect(result.filesReviewed).toBe(2);
314
+ });
315
+ it('should filter filesWithDiffs to match filtered files', async () => {
316
+ const source = {
317
+ name: 'PR #123',
318
+ files: ['src/app.ts', 'src/app.test.ts', 'src/utils.ts'],
319
+ filesWithDiffs: [
320
+ { filename: 'src/app.ts', patch: '+ new code' },
321
+ { filename: 'src/app.test.ts', patch: '+ test code' },
322
+ { filename: 'src/utils.ts', patch: '+ util code' },
323
+ ],
324
+ context: {},
325
+ };
326
+ await executeReview(mockConfig, source);
327
+ // Should filter out .test.ts file from diffs as well
328
+ const { buildBaseInstructions } = await import('./review-core.js');
329
+ expect(buildBaseInstructions).toHaveBeenCalled();
330
+ });
331
+ it('should handle empty file list', async () => {
332
+ const source = {
333
+ name: 'Empty review',
334
+ files: [],
335
+ context: {},
336
+ };
337
+ const result = await executeReview(mockConfig, source);
338
+ expect(result.issues).toEqual([]);
339
+ expect(result.filesReviewed).toBe(0);
340
+ });
341
+ it('should pass debug flag to agents', async () => {
342
+ const source = {
343
+ name: 'Debug review',
344
+ files: ['src/app.ts'],
345
+ context: {},
346
+ debug: true,
347
+ };
348
+ await executeReview(mockConfig, source);
349
+ const { runReviewPipeline } = await import('./review-core.js');
350
+ expect(runReviewPipeline).toHaveBeenCalledWith(expect.anything(), mockConfig, expect.anything(), 'Debug review', ['src/app.ts'], {}, process.cwd(), true);
351
+ });
352
+ it('should pass additional context to agents', async () => {
353
+ const source = {
354
+ name: 'PR #123',
355
+ files: ['src/app.ts'],
356
+ context: {
357
+ prNumber: 123,
358
+ author: 'test-user',
359
+ },
360
+ };
361
+ await executeReview(mockConfig, source);
362
+ const { runReviewPipeline } = await import('./review-core.js');
363
+ expect(runReviewPipeline).toHaveBeenCalledWith(expect.anything(), mockConfig, expect.anything(), 'PR #123', ['src/app.ts'], { prNumber: 123, author: 'test-user' }, process.cwd(), false);
364
+ });
365
+ it('should shutdown OpenCode client after review', async () => {
366
+ const source = {
367
+ name: 'Test review',
368
+ files: ['src/app.ts'],
369
+ context: {},
370
+ };
371
+ await executeReview(mockConfig, source);
372
+ // Verify shutdown was called on the mock client
373
+ expect(mockOpencodeClient.shutdown).toHaveBeenCalled();
374
+ });
375
+ it('should shutdown OpenCode client even on error', async () => {
376
+ const { runReviewPipeline } = await import('./review-core.js');
377
+ vi.mocked(runReviewPipeline).mockRejectedValueOnce(new Error('Review failed'));
378
+ const source = {
379
+ name: 'Test review',
380
+ files: ['src/app.ts'],
381
+ context: {},
382
+ };
383
+ await expect(executeReview(mockConfig, source)).rejects.toThrow('Review failed');
384
+ // Verify shutdown was called even on error
385
+ expect(mockOpencodeClient.shutdown).toHaveBeenCalled();
386
+ });
387
+ it('should handle "All review agents failed" error specially', async () => {
388
+ const { runReviewPipeline } = await import('./review-core.js');
389
+ vi.mocked(runReviewPipeline).mockRejectedValueOnce(new Error('All review agents failed'));
390
+ const mockExit = vi.spyOn(process, 'exit').mockImplementation((() => { }));
391
+ const source = {
392
+ name: 'Test review',
393
+ files: ['src/app.ts'],
394
+ context: {},
395
+ };
396
+ await expect(executeReview(mockConfig, source)).rejects.toThrow('All review agents failed');
397
+ expect(mockExit).toHaveBeenCalledWith(1);
398
+ mockExit.mockRestore();
399
+ });
400
+ it('should log compression warning when context is compressed', async () => {
401
+ const { formatCompressionSummary } = await import('./context-compression.js');
402
+ vi.mocked(formatCompressionSummary).mockReturnValueOnce('⚠️ Removed 5 files to fit token budget');
403
+ const source = {
404
+ name: 'Large PR',
405
+ files: ['src/app.ts'],
406
+ context: {},
407
+ };
408
+ await executeReview(mockConfig, source);
409
+ expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Diff content trimmed to fit token budget'));
410
+ });
411
+ });
412
+ });
413
+ //# sourceMappingURL=review-orchestrator.test.js.map