@clearlydev/nx-archweave 0.0.0-stage → 0.1.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +265 -2
  3. package/dist/bin/review-verdict.js +330 -0
  4. package/dist/eslint/fsd-boundaries.d.ts +119 -0
  5. package/dist/eslint/fsd-boundaries.js +144 -0
  6. package/dist/eslint/index.d.ts +78 -0
  7. package/dist/eslint/index.js +256 -0
  8. package/dist/eslint/tsconfig-aliases.d.ts +18 -0
  9. package/dist/eslint/tsconfig-aliases.js +114 -0
  10. package/dist/generators/add-agent-rules/generator.js +550 -0
  11. package/dist/generators/add-agent-rules/render.js +46 -0
  12. package/dist/generators/add-backend/generator.js +784 -0
  13. package/dist/generators/add-backend/render.js +102 -0
  14. package/dist/generators/add-fsd-lite/generator.js +553 -0
  15. package/dist/generators/add-fsd-lite/render.js +72 -0
  16. package/dist/generators/add-github-templates/files/en/codeowners/.github/CODEOWNERS.template +4 -0
  17. package/dist/generators/add-github-templates/files/en/issue/.github/ISSUE_TEMPLATE/bug-report.yml.template +28 -0
  18. package/dist/generators/add-github-templates/files/en/issue/.github/ISSUE_TEMPLATE/feature-request.yml.template +17 -0
  19. package/dist/generators/add-github-templates/files/en/pr/.github/PULL_REQUEST_TEMPLATE.md.template +12 -0
  20. package/dist/generators/add-github-templates/files/ko/codeowners/.github/CODEOWNERS.template +4 -0
  21. package/dist/generators/add-github-templates/files/ko/issue/.github/ISSUE_TEMPLATE/bug-report.yml.template +28 -0
  22. package/dist/generators/add-github-templates/files/ko/issue/.github/ISSUE_TEMPLATE/feature-request.yml.template +17 -0
  23. package/dist/generators/add-github-templates/files/ko/pr/.github/PULL_REQUEST_TEMPLATE.md.template +12 -0
  24. package/dist/generators/add-github-templates/generator.js +125 -0
  25. package/dist/generators/add-github-templates/render.js +26 -0
  26. package/dist/generators/add-integration/generator.js +818 -0
  27. package/dist/generators/add-integration/render.js +34 -0
  28. package/dist/generators/add-review/generator.js +921 -0
  29. package/dist/generators/add-review/render.js +96 -0
  30. package/dist/generators/allow-builds/generator.js +436 -0
  31. package/dist/generators/allow-builds/render.js +82 -0
  32. package/dist/generators/doctor/generator.js +2431 -0
  33. package/dist/generators/doctor/integration-report.js +70 -0
  34. package/dist/generators/doctor/render.js +10 -0
  35. package/dist/generators/entity/generator.js +395 -0
  36. package/dist/generators/feature/generator.js +395 -0
  37. package/dist/generators/init/generator.js +3211 -0
  38. package/dist/generators/init/render.js +125 -0
  39. package/dist/generators/init/validation.js +74 -0
  40. package/dist/generators/install-integration/generator.js +1233 -0
  41. package/dist/generators/install-integration/render.js +128 -0
  42. package/dist/generators/page/generator.js +395 -0
  43. package/dist/generators/record-integration/generator.js +852 -0
  44. package/dist/generators/record-integration/render.js +31 -0
  45. package/dist/generators/shared/dry-run.js +12 -0
  46. package/dist/generators/shared/frontend-app.js +57 -0
  47. package/dist/generators/shared/generator.js +395 -0
  48. package/dist/generators/shared/project-type.js +39 -0
  49. package/dist/generators/shared/record-pipeline.js +34 -0
  50. package/dist/generators/shared/run-nx.js +24 -0
  51. package/dist/generators/shared/template-files.js +38 -0
  52. package/dist/generators/shared/toolkit-config-file.js +110 -0
  53. package/dist/generators/slice/core.js +185 -0
  54. package/dist/generators/slice/files/index/index.ts.template +14 -0
  55. package/dist/generators/slice/files/module/__name__.spec.ts.template +11 -0
  56. package/dist/generators/slice/files/module/__name__.ts.template +11 -0
  57. package/dist/generators/slice/files/segment/index.ts.template +20 -0
  58. package/dist/generators/slice/render.js +70 -0
  59. package/dist/generators/widget/generator.js +395 -0
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.js +680 -0
  62. package/dist/lib/nx-plugin.d.ts +2 -0
  63. package/dist/lib/nx-plugin.js +7 -0
  64. package/dist/runtime/integrations-composition-root.js +20 -0
  65. package/dist/runtime/node-command-runner.js +81 -0
  66. package/generators.json +85 -0
  67. package/package.json +95 -4
  68. package/src/generators/add-agent-rules/schema.json +43 -0
  69. package/src/generators/add-backend/schema.json +39 -0
  70. package/src/generators/add-fsd-lite/schema.json +43 -0
  71. package/src/generators/add-github-templates/schema.json +35 -0
  72. package/src/generators/add-integration/schema.json +35 -0
  73. package/src/generators/add-review/schema.json +72 -0
  74. package/src/generators/allow-builds/schema.json +20 -0
  75. package/src/generators/doctor/schema.json +20 -0
  76. package/src/generators/entity/schema.json +37 -0
  77. package/src/generators/feature/schema.json +37 -0
  78. package/src/generators/init/schema.json +98 -0
  79. package/src/generators/install-integration/schema.json +35 -0
  80. package/src/generators/page/schema.json +53 -0
  81. package/src/generators/record-integration/schema.json +26 -0
  82. package/src/generators/shared/schema.json +32 -0
  83. package/src/generators/widget/schema.json +45 -0
@@ -0,0 +1,921 @@
1
+ // ../template-engine/dist/environment/package-manager.js
2
+ var LOCKFILES = [
3
+ ["yarn", ["yarn.lock"]],
4
+ ["bun", ["bun.lock", "bun.lockb"]],
5
+ ["npm", ["package-lock.json"]]
6
+ ];
7
+ function detectWorkspacePackageManager(exists, read = () => void 0) {
8
+ if (exists("pnpm-lock.yaml") || exists("pnpm-workspace.yaml"))
9
+ return "pnpm";
10
+ const found = LOCKFILES.filter(([, files]) => files.some((file) => exists(file))).map(([pm]) => pm);
11
+ if (found.length <= 1)
12
+ return found[0] ?? null;
13
+ const declared = /"packageManager"\s*:\s*"(npm|yarn|bun)@/.exec(read("package.json") ?? "")?.[1];
14
+ const named = found.find((pm) => pm === declared);
15
+ if (named)
16
+ return named;
17
+ return found.includes("npm") ? "npm" : found[0];
18
+ }
19
+
20
+ // ../review-policy/dist/modes/review-modes.js
21
+ var REVIEW_MODES = {
22
+ fast: {
23
+ mode: "fast",
24
+ promptStyle: "fast-condensed",
25
+ includesCorrectnessSecurity: true,
26
+ includesOverEngineering: false
27
+ },
28
+ standard: {
29
+ mode: "standard",
30
+ promptStyle: "sectioned",
31
+ includesCorrectnessSecurity: true,
32
+ includesOverEngineering: false
33
+ },
34
+ thorough: {
35
+ mode: "thorough",
36
+ promptStyle: "sectioned",
37
+ includesCorrectnessSecurity: true,
38
+ includesOverEngineering: true
39
+ }
40
+ };
41
+
42
+ // ../review-policy/dist/verdict-gate/findings-block.js
43
+ var FINDINGS_MARKER = "<!-- archweave-findings-v1 -->";
44
+ var FINDINGS_SCHEMA_ID = "archweave/findings@1";
45
+
46
+ // ../review-policy/dist/prompts/prompt-shared.js
47
+ function renderFindingSchemaInstruction(locale, priorities) {
48
+ const intro = locale === "en" ? "For every finding, fill in every field of this schema:" : "\uBAA8\uB4E0 finding\uC5D0 \uB300\uD574 \uC544\uB798 \uC2A4\uD0A4\uB9C8\uC758 \uBAA8\uB4E0 \uD544\uB4DC\uB97C \uCC44\uC6B0\uC138\uC694:";
49
+ return [
50
+ intro,
51
+ `- priority: ${priorities.join(" | ")}`,
52
+ "- category: kebab-case category name",
53
+ "- title: one-line summary",
54
+ "- file: path of the affected file",
55
+ "- lineRange: { start, end? }",
56
+ "- evidence: what in the code proves the problem",
57
+ "- impact: what goes wrong and for whom",
58
+ "- reproductionCondition: the exact input/state that triggers it",
59
+ "- recommendation: the concrete fix",
60
+ "- confidence: confirmed | plausible"
61
+ ].join("\n");
62
+ }
63
+ function renderFindingsBlockInstruction(locale) {
64
+ const lines = locale === "en" ? [
65
+ "At the END of the top-level summary comment, append the machine-readable findings block:",
66
+ `the literal marker line ${FINDINGS_MARKER}, then a fenced \`\`\`json block containing`,
67
+ `{ "schema": "${FINDINGS_SCHEMA_ID}", "findings": [ ... ] } with one entry per finding,`,
68
+ "using exactly the finding schema above. If there are no findings, still append the block with an empty findings array."
69
+ ] : [
70
+ "top-level \uC694\uC57D \uCF54\uBA58\uD2B8\uC758 \uB9E8 \uB05D\uC5D0 \uAE30\uACC4 \uD310\uB3C5\uC6A9 findings \uBE14\uB85D\uC744 \uB367\uBD99\uC774\uC138\uC694:",
71
+ `\uB9AC\uD130\uB7F4 \uB9C8\uCEE4 \uC904 ${FINDINGS_MARKER} \uB2E4\uC74C\uC5D0 fenced \`\`\`json \uBE14\uB85D\uC73C\uB85C`,
72
+ `{ "schema": "${FINDINGS_SCHEMA_ID}", "findings": [ ... ] }\uB97C, finding\uB9C8\uB2E4 \uD55C \uC6D0\uC18C\uC529, \uC704 finding \uC2A4\uD0A4\uB9C8 \uADF8\uB300\uB85C \uC791\uC131\uD558\uC138\uC694.`,
73
+ "finding\uC774 \uC5C6\uC5B4\uB3C4 \uBE48 \uBC30\uC5F4\uB85C \uBE14\uB85D\uC744 \uBC18\uB4DC\uC2DC \uD3EC\uD568\uD558\uC138\uC694."
74
+ ];
75
+ return lines.join("\n");
76
+ }
77
+ function renderDedupInstruction(locale) {
78
+ return locale === "en" ? "If multiple findings share the same root cause, merge them into a single finding instead of reporting duplicates." : "\uAC19\uC740 \uC6D0\uC778\uC5D0\uC11C \uB098\uC628 finding\uC774 \uC5EC\uB7EC \uAC1C\uB77C\uBA74 \uC911\uBCF5 \uBCF4\uACE0\uD558\uC9C0 \uB9D0\uACE0 \uD558\uB098\uC758 finding\uC73C\uB85C \uD569\uCCD0\uC11C \uBCF4\uACE0\uD558\uC138\uC694.";
79
+ }
80
+
81
+ // ../review-policy/dist/prompts/correctness-security-prompt.js
82
+ var CORRECTNESS_SECURITY_PRIORITIES = ["P0", "P1", "P2", "Nit"];
83
+ var FAST_PRIORITIES = ["P0", "P1", "P2"];
84
+ function buildCorrectnessSecurityPrompt(locale, style = "sectioned") {
85
+ if (style === "fast-condensed") {
86
+ const fastBody = locale === "en" ? [
87
+ "You are a code reviewer focused on correctness and security.",
88
+ "",
89
+ "Review the provided diff and report only the most critical problems. Skip minor style and taste remarks entirely \u2014 report nothing below P2.",
90
+ "",
91
+ `Only report findings with priority ${FAST_PRIORITIES.join(", ")}. Do NOT report P3 (over-engineering) findings \u2014 they are out of scope here.`
92
+ ] : [
93
+ "\uB2F9\uC2E0\uC740 \uC815\uD655\uC131(correctness)\uACFC \uBCF4\uC548(security)\uC5D0 \uC9D1\uC911\uD558\uB294 \uCF54\uB4DC \uB9AC\uBDF0\uC5B4\uC785\uB2C8\uB2E4.",
94
+ "",
95
+ "\uC8FC\uC5B4\uC9C4 diff\uC5D0\uC11C \uAC00\uC7A5 \uCE58\uBA85\uC801\uC778 \uBB38\uC81C\uB9CC \uBCF4\uACE0\uD558\uC138\uC694. \uC0AC\uC18C\uD55C \uC2A4\uD0C0\uC77C\xB7\uCDE8\uD5A5 \uC9C0\uC801\uC740 \uC644\uC804\uD788 \uC0DD\uB7B5\uD558\uACE0, P2 \uBBF8\uB9CC\uC740 \uBCF4\uACE0\uD558\uC9C0 \uB9C8\uC138\uC694.",
96
+ "",
97
+ `\uC6B0\uC120\uC21C\uC704 ${FAST_PRIORITIES.join(", ")}\uC5D0 \uD574\uB2F9\uD558\uB294 finding\uB9CC \uBCF4\uACE0\uD558\uC138\uC694. P3(\uACFC\uC789 \uC124\uACC4)\uB294 \uBCF4\uACE0\uD558\uC9C0 \uB9C8\uC138\uC694 \u2014 \uC774 \uB9AC\uBDF0\uC758 \uBC94\uC704 \uBC16\uC785\uB2C8\uB2E4.`
98
+ ];
99
+ return [...fastBody, "", renderFindingSchemaInstruction(locale, FAST_PRIORITIES), "", renderDedupInstruction(locale)].join("\n");
100
+ }
101
+ const body = locale === "en" ? [
102
+ "You are a code reviewer focused on correctness and security.",
103
+ "",
104
+ 'Review the provided diff and report findings in two explicitly separated top-level sections: "Correctness" and "Security". Never mix the two.',
105
+ "",
106
+ `Only report findings with priority ${CORRECTNESS_SECURITY_PRIORITIES.join(", ")}. Do NOT report P3 (over-engineering) findings \u2014 they are covered by a separate reviewer and are out of scope here.`
107
+ ] : [
108
+ "\uB2F9\uC2E0\uC740 \uC815\uD655\uC131(correctness)\uACFC \uBCF4\uC548(security)\uC5D0 \uC9D1\uC911\uD558\uB294 \uCF54\uB4DC \uB9AC\uBDF0\uC5B4\uC785\uB2C8\uB2E4.",
109
+ "",
110
+ '\uC8FC\uC5B4\uC9C4 diff\uB97C \uB9AC\uBDF0\uD558\uACE0, finding\uC744 "Correctness"\uC640 "Security" \uB450 \uAC1C\uC758 \uCD5C\uC0C1\uC704 \uC139\uC158\uC73C\uB85C \uBD84\uB9AC\uD574 \uBCF4\uACE0\uD558\uC138\uC694. \uB450 \uC139\uC158\uC744 \uC808\uB300 \uC11E\uC9C0 \uB9C8\uC138\uC694.',
111
+ "",
112
+ `\uC6B0\uC120\uC21C\uC704 ${CORRECTNESS_SECURITY_PRIORITIES.join(", ")}\uC5D0 \uD574\uB2F9\uD558\uB294 finding\uB9CC \uBCF4\uACE0\uD558\uC138\uC694. P3\uB294 \uBCF4\uACE0\uD558\uC9C0 \uB9C8\uC138\uC694 \u2014 P3(\uACFC\uC789 \uC124\uACC4)\uB294 \uBCC4\uB3C4\uC758 \uB9AC\uBDF0\uC5B4\uAC00 \uB2F4\uB2F9\uD558\uBBC0\uB85C \uC774 \uB9AC\uBDF0\uC758 \uBC94\uC704 \uBC16\uC785\uB2C8\uB2E4.`
113
+ ];
114
+ return [...body, "", renderFindingSchemaInstruction(locale, CORRECTNESS_SECURITY_PRIORITIES), "", renderDedupInstruction(locale)].join("\n");
115
+ }
116
+
117
+ // ../review-policy/dist/prompts/over-engineering-prompt.js
118
+ var OVER_ENGINEERING_PRIORITIES = ["P3"];
119
+ function buildOverEngineeringPrompt(locale) {
120
+ const body = locale === "en" ? [
121
+ "You are a code reviewer focused exclusively on over-engineering.",
122
+ "",
123
+ "Review the provided diff and report findings with priority P3 only. Do NOT report P0, P1, P2, or Nit findings \u2014 correctness and security are covered by a separate reviewer.",
124
+ "",
125
+ 'Report your findings in their own top-level section titled "Over-engineering"; never mix them into correctness or security sections.'
126
+ ] : [
127
+ "\uB2F9\uC2E0\uC740 \uACFC\uC789 \uC124\uACC4(over-engineering)\uB9CC\uC744 \uC804\uB2F4\uD558\uB294 \uCF54\uB4DC \uB9AC\uBDF0\uC5B4\uC785\uB2C8\uB2E4.",
128
+ "",
129
+ "\uC8FC\uC5B4\uC9C4 diff\uB97C \uB9AC\uBDF0\uD558\uACE0 \uC6B0\uC120\uC21C\uC704 P3\uB9CC \uBCF4\uACE0\uD558\uC138\uC694. P0, P1, P2, Nit\uC740 \uBCF4\uACE0\uD558\uC9C0 \uB9C8\uC138\uC694 \u2014 \uC815\uD655\uC131\uACFC \uBCF4\uC548\uC740 \uBCC4\uB3C4\uC758 \uB9AC\uBDF0\uC5B4\uAC00 \uB2F4\uB2F9\uD569\uB2C8\uB2E4.",
130
+ "",
131
+ 'finding\uC740 "Over-engineering"\uC774\uB77C\uB294 \uBCC4\uB3C4\uC758 \uCD5C\uC0C1\uC704 \uC139\uC158\uC73C\uB85C \uBCF4\uACE0\uD558\uACE0, correctness/security \uC139\uC158\uACFC \uC808\uB300 \uC11E\uC9C0 \uB9C8\uC138\uC694.'
132
+ ];
133
+ return [...body, "", renderFindingSchemaInstruction(locale, OVER_ENGINEERING_PRIORITIES), "", renderDedupInstruction(locale)].join("\n");
134
+ }
135
+
136
+ // ../review-policy/dist/verdict-gate/verdict-gate.js
137
+ var ROUND_MARKER = "<!-- archweave-review-round -->";
138
+
139
+ // ../template-engine/dist/review/config-snippet.js
140
+ function buildReviewConfigSnippet(input) {
141
+ return {
142
+ enabled: true,
143
+ provider: input.provider,
144
+ mode: input.mode,
145
+ locale: input.locale,
146
+ maxRounds: input.maxRounds,
147
+ blockingPriorities: ["P0", "P1"]
148
+ };
149
+ }
150
+
151
+ // ../template-engine/dist/review/workflow-skeleton.js
152
+ function indentBlock(text, spaces) {
153
+ const pad = " ".repeat(spaces);
154
+ return text.split("\n").map((line) => line.length === 0 ? line : pad + line).join("\n");
155
+ }
156
+ function renderTriggerBlock() {
157
+ return ["on:", " pull_request:", " types: [opened, synchronize, ready_for_review, reopened]"].join("\n");
158
+ }
159
+ function renderConcurrencyBlock() {
160
+ return [
161
+ "concurrency:",
162
+ " group: archweave-review-${{ github.event.pull_request.number }}",
163
+ " cancel-in-progress: false"
164
+ ].join("\n");
165
+ }
166
+ function renderPermissionsBlock() {
167
+ return ["permissions:", " contents: read", " pull-requests: write", " id-token: write"].join("\n");
168
+ }
169
+ function renderSecretGateStep(secretName) {
170
+ return [
171
+ "- name: Check review secret",
172
+ " id: secret-gate",
173
+ " env:",
174
+ ` REVIEW_SECRET: \${{ secrets.${secretName} }}`,
175
+ " run: |",
176
+ ' if [ -z "$REVIEW_SECRET" ]; then',
177
+ ` echo "AI review is not active: register the ${secretName} secret to enable it."`,
178
+ ' echo "enabled=false" >> "$GITHUB_OUTPUT"',
179
+ " else",
180
+ ' echo "enabled=true" >> "$GITHUB_OUTPUT"',
181
+ " fi"
182
+ ].join("\n");
183
+ }
184
+ function renderRoundGateStep(maxRounds) {
185
+ return [
186
+ "- name: Check review round limit",
187
+ " id: round-gate",
188
+ " if: steps.secret-gate.outputs.enabled == 'true'",
189
+ " env:",
190
+ " GH_TOKEN: ${{ github.token }}",
191
+ " run: |",
192
+ ` counts=$(gh api "repos/\${{ github.repository }}/issues/\${{ github.event.pull_request.number }}/comments" --paginate --jq '[([.[] | select(.body | contains("${ROUND_MARKER}"))] | length), ([.[].id] | max // 0)]' | jq -s -r '[(map(.[0]) | add // 0), (map(.[1]) | max // 0)] | @tsv')`,
193
+ ` rounds=$(printf '%s' "$counts" | cut -f1)`,
194
+ ` echo "last-comment-id=$(printf '%s' "$counts" | cut -f2)" >> "$GITHUB_OUTPUT"`,
195
+ ` if [ "$rounds" -ge ${maxRounds} ]; then`,
196
+ ` echo "Automated review round limit (${maxRounds}) reached for this PR; skipping."`,
197
+ ' echo "allowed=false" >> "$GITHUB_OUTPUT"',
198
+ " else",
199
+ ' echo "allowed=true" >> "$GITHUB_OUTPUT"',
200
+ " fi"
201
+ ].join("\n");
202
+ }
203
+ function renderVerdictGateStep(maxRounds, locale) {
204
+ return [
205
+ "- name: Compute review verdict",
206
+ " if: steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'",
207
+ " env:",
208
+ " GH_TOKEN: ${{ github.token }}",
209
+ " GITHUB_REPOSITORY: ${{ github.repository }}",
210
+ " ARCHWEAVE_PR_NUMBER: ${{ github.event.pull_request.number }}",
211
+ ` ARCHWEAVE_MAX_ROUNDS: '${maxRounds}'`,
212
+ ` ARCHWEAVE_LOCALE: '${locale}'`,
213
+ " ARCHWEAVE_HEAD_SHA: ${{ github.event.pull_request.head.sha }}",
214
+ " ARCHWEAVE_ROUND_AFTER_ID: ${{ steps.round-gate.outputs.last-comment-id }}",
215
+ " run: |",
216
+ " if [ -x node_modules/.bin/archweave-review-verdict ]; then",
217
+ " node_modules/.bin/archweave-review-verdict",
218
+ " else",
219
+ ' echo "nx-archweave is not installed in this workspace - skipping the verdict gate."',
220
+ " fi"
221
+ ].join("\n");
222
+ }
223
+ var SETUP_NODE_VERSION = "22";
224
+ var SETUP_PNPM_VERSION = "10";
225
+ var SETUP_BUN_ACTION = "oven-sh/setup-bun@v2";
226
+ var INSTALL_LINES = {
227
+ pnpm: [" pnpm install --frozen-lockfile"],
228
+ npm: [" npm ci"],
229
+ yarn: [
230
+ " corepack enable yarn",
231
+ ' if [ "$(yarn --version | cut -d. -f1)" -ge 2 ]; then yarn install --immutable; else yarn install --frozen-lockfile; fi'
232
+ ],
233
+ bun: [" bun install --frozen-lockfile"]
234
+ };
235
+ function renderWorkspaceSetupSteps(packageManager) {
236
+ const gate = " if: steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'";
237
+ const steps = [];
238
+ if (packageManager === "pnpm") {
239
+ steps.push(["- name: Set up pnpm", gate, " uses: pnpm/action-setup@v4", " with:", ` version: ${SETUP_PNPM_VERSION}`].join("\n"));
240
+ }
241
+ if (packageManager === "bun") {
242
+ steps.push(["- name: Set up bun", gate, ` uses: ${SETUP_BUN_ACTION}`].join("\n"));
243
+ }
244
+ steps.push([
245
+ "- name: Set up Node",
246
+ gate,
247
+ " uses: actions/setup-node@v4",
248
+ " with:",
249
+ ` node-version: ${SETUP_NODE_VERSION}`,
250
+ // yarn: no setup-node cache — it would query the runner's global yarn 1 even in a
251
+ // berry project. Corepack (shipped with Node) provides the project's own yarn below.
252
+ // bun: setup-node has no bun cache. Node is still set up — nx and the verdict bin
253
+ // run on it.
254
+ ...packageManager === "yarn" || packageManager === "bun" ? [] : [` cache: ${packageManager}`]
255
+ ].join("\n"));
256
+ steps.push([
257
+ "- name: Install workspace dependencies",
258
+ gate,
259
+ " run: |",
260
+ ...INSTALL_LINES[packageManager]
261
+ ].join("\n"));
262
+ return steps.join("\n\n");
263
+ }
264
+
265
+ // ../template-engine/dist/review/claude/claude-provider.js
266
+ var SECRET_NAME = "CLAUDE_CODE_OAUTH_TOKEN";
267
+ var ALLOWED_TOOLS = "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)";
268
+ function renderWorkflowInstructions(locale) {
269
+ const lines = locale === "en" ? [
270
+ "Review the diff of this pull request using `gh pr diff` and `gh pr view`.",
271
+ "Post specific findings as inline comments and exactly one top-level summary comment.",
272
+ `Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
273
+ "If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
274
+ "Do not modify any code, and never approve or merge the pull request.",
275
+ "Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
276
+ ] : [
277
+ "\uC774 pull request\uC758 diff\uB97C `gh pr diff`\uC640 `gh pr view`\uB85C \uD655\uC778\uD558\uACE0 \uB9AC\uBDF0\uD558\uC138\uC694.",
278
+ "\uAD6C\uCCB4\uC801 \uC9C0\uC801\uC740 inline comment\uB85C, \uC804\uCCB4 \uC694\uC57D\uC740 \uC815\uD655\uD788 \uD55C \uAC1C\uC758 top-level comment\uB85C \uAC8C\uC2DC\uD558\uC138\uC694.",
279
+ `top-level \uC694\uC57D \uCF54\uBA58\uD2B8\uC5D0\uB294 \uBC18\uB4DC\uC2DC \uB9AC\uD130\uB7F4 \uB9C8\uCEE4 ${ROUND_MARKER}\uB97C \uD3EC\uD568\uD558\uC138\uC694.`,
280
+ '\uC870\uCE58\uAC00 \uD544\uC694\uD55C \uBB38\uC81C\uAC00 \uC5C6\uC73C\uBA74 "\uC870\uCE58\uAC00 \uD544\uC694\uD55C \uBB38\uC81C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."\uB77C\uB294 top-level comment\uB97C \uC815\uD655\uD788 \uD55C \uAC1C \uAC8C\uC2DC\uD558\uC138\uC694(\uC774\uB54C\uB3C4 \uB9C8\uCEE4\uB294 \uD3EC\uD568).',
281
+ "\uCF54\uB4DC\uB97C \uC218\uC815\uD558\uC9C0 \uB9C8\uC138\uC694(Do not modify any code). PR\uC744 \uC2B9\uC778\uD558\uAC70\uB098 \uBCD1\uD569\uD558\uC9C0 \uB9C8\uC138\uC694(never approve or merge).",
282
+ "\uC694\uC57D\uACFC \uC124\uBA85\uC740 \uD55C\uAD6D\uC5B4\uB85C \uC4F0\uB418, \uD30C\uC77C\uBA85\xB7\uD568\uC218\uBA85\xB7\uC2DD\uBCC4\uC790\uB294 \uC6D0\uBB38 \uADF8\uB300\uB85C \uC720\uC9C0\uD558\uC138\uC694."
283
+ ];
284
+ return lines.join("\n");
285
+ }
286
+ function composePrompt(input) {
287
+ const mode = REVIEW_MODES[input.mode];
288
+ const sections = [renderWorkflowInstructions(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
289
+ if (mode.includesOverEngineering) {
290
+ sections.push(buildOverEngineeringPrompt(input.locale));
291
+ }
292
+ sections.push(renderFindingsBlockInstruction(input.locale));
293
+ return sections.join("\n\n---\n\n");
294
+ }
295
+ function buildWorkflowYaml(input) {
296
+ const steps = [
297
+ renderSecretGateStep(SECRET_NAME),
298
+ renderRoundGateStep(input.maxRounds),
299
+ [
300
+ "- name: Checkout repository",
301
+ " if: steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'",
302
+ " uses: actions/checkout@v4",
303
+ " with:",
304
+ " fetch-depth: 1"
305
+ ].join("\n"),
306
+ [
307
+ "- name: Run Claude review",
308
+ " if: steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'",
309
+ " uses: anthropics/claude-code-action@v1",
310
+ " with:",
311
+ ` claude_code_oauth_token: \${{ secrets.${SECRET_NAME} }}`,
312
+ " prompt: |",
313
+ indentBlock(composePrompt(input), 6),
314
+ " claude_args: |",
315
+ ` --allowedTools "${ALLOWED_TOOLS}"`
316
+ ].join("\n"),
317
+ renderWorkspaceSetupSteps(input.packageManager),
318
+ renderVerdictGateStep(input.maxRounds, input.locale)
319
+ ];
320
+ return [
321
+ "name: Claude Code Review",
322
+ "",
323
+ renderTriggerBlock(),
324
+ "",
325
+ renderConcurrencyBlock(),
326
+ "",
327
+ renderPermissionsBlock(),
328
+ "",
329
+ "jobs:",
330
+ " review:",
331
+ " runs-on: ubuntu-latest",
332
+ " steps:",
333
+ indentBlock(steps.join("\n\n"), 6),
334
+ ""
335
+ ].join("\n");
336
+ }
337
+ var claudeProvider = {
338
+ id: "claude",
339
+ workflowFileName: ".github/workflows/claude-code-review.yml",
340
+ requiredSecretName: SECRET_NAME,
341
+ buildWorkflowYaml
342
+ };
343
+
344
+ // ../template-engine/dist/review/cursor/cursor-provider.js
345
+ var SECRET_NAME2 = "CURSOR_API_KEY";
346
+ var PROMPT_HEREDOC_DELIMITER = "ARCHWEAVE_CURSOR_REVIEW_PROMPT";
347
+ function renderWorkflowInstructions2(locale) {
348
+ const lines = locale === "en" ? [
349
+ "Review the diff of this pull request using `gh pr diff` and `gh pr view`.",
350
+ "Post specific findings as inline comments and exactly one top-level summary comment.",
351
+ `Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
352
+ "If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
353
+ "Do not modify any code, and never approve or merge the pull request.",
354
+ "Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
355
+ ] : [
356
+ "\uC774 pull request\uC758 diff\uB97C `gh pr diff`\uC640 `gh pr view`\uB85C \uD655\uC778\uD558\uACE0 \uB9AC\uBDF0\uD558\uC138\uC694.",
357
+ "\uAD6C\uCCB4\uC801 \uC9C0\uC801\uC740 inline comment\uB85C, \uC804\uCCB4 \uC694\uC57D\uC740 \uC815\uD655\uD788 \uD55C \uAC1C\uC758 top-level comment\uB85C \uAC8C\uC2DC\uD558\uC138\uC694.",
358
+ `top-level \uC694\uC57D \uCF54\uBA58\uD2B8\uC5D0\uB294 \uBC18\uB4DC\uC2DC \uB9AC\uD130\uB7F4 \uB9C8\uCEE4 ${ROUND_MARKER}\uB97C \uD3EC\uD568\uD558\uC138\uC694.`,
359
+ '\uC870\uCE58\uAC00 \uD544\uC694\uD55C \uBB38\uC81C\uAC00 \uC5C6\uC73C\uBA74 "\uC870\uCE58\uAC00 \uD544\uC694\uD55C \uBB38\uC81C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."\uB77C\uB294 top-level comment\uB97C \uC815\uD655\uD788 \uD55C \uAC1C \uAC8C\uC2DC\uD558\uC138\uC694(\uC774\uB54C\uB3C4 \uB9C8\uCEE4\uB294 \uD3EC\uD568).',
360
+ "\uCF54\uB4DC\uB97C \uC218\uC815\uD558\uC9C0 \uB9C8\uC138\uC694(Do not modify any code). PR\uC744 \uC2B9\uC778\uD558\uAC70\uB098 \uBCD1\uD569\uD558\uC9C0 \uB9C8\uC138\uC694(never approve or merge).",
361
+ "\uC694\uC57D\uACFC \uC124\uBA85\uC740 \uD55C\uAD6D\uC5B4\uB85C \uC4F0\uB418, \uD30C\uC77C\uBA85\xB7\uD568\uC218\uBA85\xB7\uC2DD\uBCC4\uC790\uB294 \uC6D0\uBB38 \uADF8\uB300\uB85C \uC720\uC9C0\uD558\uC138\uC694."
362
+ ];
363
+ return lines.join("\n");
364
+ }
365
+ function composePrompt2(input) {
366
+ const mode = REVIEW_MODES[input.mode];
367
+ const sections = [renderWorkflowInstructions2(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
368
+ if (mode.includesOverEngineering) {
369
+ sections.push(buildOverEngineeringPrompt(input.locale));
370
+ }
371
+ sections.push(renderFindingsBlockInstruction(input.locale));
372
+ return sections.join("\n\n---\n\n");
373
+ }
374
+ function buildWorkflowYaml2(input) {
375
+ const gateCondition = "steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'";
376
+ const steps = [
377
+ renderSecretGateStep(SECRET_NAME2),
378
+ renderRoundGateStep(input.maxRounds),
379
+ [
380
+ "- name: Checkout repository",
381
+ ` if: ${gateCondition}`,
382
+ " uses: actions/checkout@v4",
383
+ " with:",
384
+ " fetch-depth: 1"
385
+ ].join("\n"),
386
+ [
387
+ "- name: Install cursor-agent CLI",
388
+ ` if: ${gateCondition}`,
389
+ " run: |",
390
+ " curl https://cursor.com/install -fsS | bash",
391
+ ' echo "$HOME/.cursor/bin" >> "$GITHUB_PATH"'
392
+ ].join("\n"),
393
+ [
394
+ "- name: Run Cursor review",
395
+ ` if: ${gateCondition}`,
396
+ " env:",
397
+ ` CURSOR_API_KEY: \${{ secrets.${SECRET_NAME2} }}`,
398
+ " GH_TOKEN: ${{ github.token }}",
399
+ " run: |",
400
+ ` agent -p "$(cat <<'${PROMPT_HEREDOC_DELIMITER}'`,
401
+ indentBlock(composePrompt2(input), 4),
402
+ ` ${PROMPT_HEREDOC_DELIMITER}`,
403
+ ' )"'
404
+ ].join("\n"),
405
+ renderWorkspaceSetupSteps(input.packageManager),
406
+ renderVerdictGateStep(input.maxRounds, input.locale)
407
+ ];
408
+ return [
409
+ "name: Cursor Code Review",
410
+ "",
411
+ renderTriggerBlock(),
412
+ "",
413
+ renderConcurrencyBlock(),
414
+ "",
415
+ renderPermissionsBlock(),
416
+ "",
417
+ "jobs:",
418
+ " review:",
419
+ " runs-on: ubuntu-latest",
420
+ " steps:",
421
+ indentBlock(steps.join("\n\n"), 6),
422
+ ""
423
+ ].join("\n");
424
+ }
425
+ var cursorProvider = {
426
+ id: "cursor",
427
+ workflowFileName: ".github/workflows/cursor-code-review.yml",
428
+ requiredSecretName: SECRET_NAME2,
429
+ buildWorkflowYaml: buildWorkflowYaml2
430
+ };
431
+
432
+ // ../template-engine/dist/review/gemini/gemini-provider.js
433
+ var SECRET_NAME3 = "GEMINI_API_KEY";
434
+ function renderWorkflowInstructions3(locale) {
435
+ const lines = locale === "en" ? [
436
+ "Review the diff of this pull request using `gh pr diff` and `gh pr view`.",
437
+ "Post specific findings as inline comments and exactly one top-level summary comment.",
438
+ `Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
439
+ "If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
440
+ "Do not modify any code, and never approve or merge the pull request.",
441
+ "Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
442
+ ] : [
443
+ "\uC774 pull request\uC758 diff\uB97C `gh pr diff`\uC640 `gh pr view`\uB85C \uD655\uC778\uD558\uACE0 \uB9AC\uBDF0\uD558\uC138\uC694.",
444
+ "\uAD6C\uCCB4\uC801 \uC9C0\uC801\uC740 inline comment\uB85C, \uC804\uCCB4 \uC694\uC57D\uC740 \uC815\uD655\uD788 \uD55C \uAC1C\uC758 top-level comment\uB85C \uAC8C\uC2DC\uD558\uC138\uC694.",
445
+ `top-level \uC694\uC57D \uCF54\uBA58\uD2B8\uC5D0\uB294 \uBC18\uB4DC\uC2DC \uB9AC\uD130\uB7F4 \uB9C8\uCEE4 ${ROUND_MARKER}\uB97C \uD3EC\uD568\uD558\uC138\uC694.`,
446
+ '\uC870\uCE58\uAC00 \uD544\uC694\uD55C \uBB38\uC81C\uAC00 \uC5C6\uC73C\uBA74 "\uC870\uCE58\uAC00 \uD544\uC694\uD55C \uBB38\uC81C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."\uB77C\uB294 top-level comment\uB97C \uC815\uD655\uD788 \uD55C \uAC1C \uAC8C\uC2DC\uD558\uC138\uC694(\uC774\uB54C\uB3C4 \uB9C8\uCEE4\uB294 \uD3EC\uD568).',
447
+ "\uCF54\uB4DC\uB97C \uC218\uC815\uD558\uC9C0 \uB9C8\uC138\uC694(Do not modify any code). PR\uC744 \uC2B9\uC778\uD558\uAC70\uB098 \uBCD1\uD569\uD558\uC9C0 \uB9C8\uC138\uC694(never approve or merge).",
448
+ "\uC694\uC57D\uACFC \uC124\uBA85\uC740 \uD55C\uAD6D\uC5B4\uB85C \uC4F0\uB418, \uD30C\uC77C\uBA85\xB7\uD568\uC218\uBA85\xB7\uC2DD\uBCC4\uC790\uB294 \uC6D0\uBB38 \uADF8\uB300\uB85C \uC720\uC9C0\uD558\uC138\uC694."
449
+ ];
450
+ return lines.join("\n");
451
+ }
452
+ function composePrompt3(input) {
453
+ const mode = REVIEW_MODES[input.mode];
454
+ const sections = [renderWorkflowInstructions3(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
455
+ if (mode.includesOverEngineering) {
456
+ sections.push(buildOverEngineeringPrompt(input.locale));
457
+ }
458
+ sections.push(renderFindingsBlockInstruction(input.locale));
459
+ return sections.join("\n\n---\n\n");
460
+ }
461
+ function buildWorkflowYaml3(input) {
462
+ const steps = [
463
+ renderSecretGateStep(SECRET_NAME3),
464
+ renderRoundGateStep(input.maxRounds),
465
+ [
466
+ "- name: Checkout repository",
467
+ " if: steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'",
468
+ " uses: actions/checkout@v4",
469
+ " with:",
470
+ " fetch-depth: 1"
471
+ ].join("\n"),
472
+ [
473
+ "- name: Run Gemini review",
474
+ " if: steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'",
475
+ " uses: google-github-actions/run-gemini-cli@v0",
476
+ " env:",
477
+ " GITHUB_TOKEN: ${{ github.token }}",
478
+ " with:",
479
+ ` gemini_api_key: \${{ secrets.${SECRET_NAME3} }}`,
480
+ " prompt: |",
481
+ indentBlock(composePrompt3(input), 6)
482
+ ].join("\n"),
483
+ renderWorkspaceSetupSteps(input.packageManager),
484
+ renderVerdictGateStep(input.maxRounds, input.locale)
485
+ ];
486
+ return [
487
+ "name: Gemini Code Review",
488
+ "",
489
+ renderTriggerBlock(),
490
+ "",
491
+ renderConcurrencyBlock(),
492
+ "",
493
+ renderPermissionsBlock(),
494
+ "",
495
+ "jobs:",
496
+ " review:",
497
+ " runs-on: ubuntu-latest",
498
+ " steps:",
499
+ indentBlock(steps.join("\n\n"), 6),
500
+ ""
501
+ ].join("\n");
502
+ }
503
+ var geminiProvider = {
504
+ id: "gemini",
505
+ workflowFileName: ".github/workflows/gemini-code-review.yml",
506
+ requiredSecretName: SECRET_NAME3,
507
+ buildWorkflowYaml: buildWorkflowYaml3
508
+ };
509
+
510
+ // ../template-engine/dist/review/gpt/gpt-provider.js
511
+ var SECRET_NAME4 = "OPENAI_API_KEY";
512
+ function renderWorkflowInstructions4(locale) {
513
+ const lines = locale === "en" ? [
514
+ "Review ONLY the changes introduced by this pull request. The base and head commits are already fetched under fixed ref names: run `git diff origin/pr-base...origin/pr-head` (and `git log origin/pr-base..origin/pr-head` for commit context) \u2014 this sandboxed step has no network access, so do not attempt to run `gh` or reach the GitHub API.",
515
+ "Write your entire response as a single message: report specific findings, then close with one overall summary.",
516
+ `Always include the literal marker ${ROUND_MARKER} in your message.`,
517
+ "If there are no findings that require action, write exactly one message saying so (and still include the marker).",
518
+ "Do not modify any code, and never approve or merge the pull request.",
519
+ "Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
520
+ ] : [
521
+ "\uC774 pull request\uAC00 \uB3C4\uC785\uD55C \uBCC0\uACBD \uC0AC\uD56D\uB9CC \uB9AC\uBDF0\uD558\uC138\uC694. base/head \uCEE4\uBC0B\uC740 \uACE0\uC815 ref \uC774\uB984\uC73C\uB85C \uC774\uBBF8 fetch\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: `git diff origin/pr-base...origin/pr-head`\uB85C \uBE44\uAD50\uD558\uACE0, \uCEE4\uBC0B \uB9E5\uB77D\uC740 `git log origin/pr-base..origin/pr-head`\uB85C \uD655\uC778\uD558\uC138\uC694(\uC774 \uC0CC\uB4DC\uBC15\uC2A4 \uC2A4\uD15D\uC740 \uB124\uD2B8\uC6CC\uD06C\uAC00 \uC5C6\uC73C\uBBC0\uB85C `gh`\uB098 GitHub API\uB97C \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694).",
522
+ "\uC804\uCCB4 \uC751\uB2F5\uC744 \uD558\uB098\uC758 \uBA54\uC2DC\uC9C0\uB85C \uC791\uC131\uD558\uC138\uC694: \uAD6C\uCCB4\uC801 \uC9C0\uC801\uC744 \uC801\uC740 \uB4A4 \uB9C8\uC9C0\uB9C9\uC5D0 \uC804\uCCB4 \uC694\uC57D\uC744 \uD55C \uBC88 \uB354 \uB0A8\uAE30\uC138\uC694.",
523
+ `\uBA54\uC2DC\uC9C0\uC5D0\uB294 \uBC18\uB4DC\uC2DC \uB9AC\uD130\uB7F4 \uB9C8\uCEE4 ${ROUND_MARKER}\uB97C \uD3EC\uD568\uD558\uC138\uC694.`,
524
+ '\uC870\uCE58\uAC00 \uD544\uC694\uD55C \uBB38\uC81C\uAC00 \uC5C6\uC73C\uBA74 "\uC870\uCE58\uAC00 \uD544\uC694\uD55C \uBB38\uC81C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4."\uB77C\uB294 \uBA54\uC2DC\uC9C0\uB97C \uC815\uD655\uD788 \uD55C \uAC1C \uC791\uC131\uD558\uC138\uC694(\uC774\uB54C\uB3C4 \uB9C8\uCEE4\uB294 \uD3EC\uD568).',
525
+ "\uCF54\uB4DC\uB97C \uC218\uC815\uD558\uC9C0 \uB9C8\uC138\uC694(Do not modify any code). PR\uC744 \uC2B9\uC778\uD558\uAC70\uB098 \uBCD1\uD569\uD558\uC9C0 \uB9C8\uC138\uC694(never approve or merge).",
526
+ "\uC694\uC57D\uACFC \uC124\uBA85\uC740 \uD55C\uAD6D\uC5B4\uB85C \uC4F0\uB418, \uD30C\uC77C\uBA85\xB7\uD568\uC218\uBA85\xB7\uC2DD\uBCC4\uC790\uB294 \uC6D0\uBB38 \uADF8\uB300\uB85C \uC720\uC9C0\uD558\uC138\uC694."
527
+ ];
528
+ return lines.join("\n");
529
+ }
530
+ function composePrompt4(input) {
531
+ const mode = REVIEW_MODES[input.mode];
532
+ const sections = [renderWorkflowInstructions4(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
533
+ if (mode.includesOverEngineering) {
534
+ sections.push(buildOverEngineeringPrompt(input.locale));
535
+ }
536
+ sections.push(renderFindingsBlockInstruction(input.locale));
537
+ return sections.join("\n\n---\n\n");
538
+ }
539
+ function buildWorkflowYaml4(input) {
540
+ const gateCondition = "steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'";
541
+ const steps = [
542
+ renderSecretGateStep(SECRET_NAME4),
543
+ renderRoundGateStep(input.maxRounds),
544
+ [
545
+ "- name: Checkout repository",
546
+ ` if: ${gateCondition}`,
547
+ " uses: actions/checkout@v4",
548
+ " with:",
549
+ " ref: refs/pull/${{ github.event.pull_request.number }}/merge",
550
+ " fetch-depth: 1",
551
+ " persist-credentials: false"
552
+ ].join("\n"),
553
+ [
554
+ "- name: Fetch base and head refs for the PR",
555
+ ` if: ${gateCondition}`,
556
+ " env:",
557
+ " PR_BASE_REF: ${{ github.event.pull_request.base.ref }}",
558
+ " PR_NUMBER: ${{ github.event.pull_request.number }}",
559
+ " run: |",
560
+ " # Explicit destinations: the sandboxed review step has no network, so both sides",
561
+ " # of the diff must survive as stable local refs (FETCH_HEAD alone is ambiguous).",
562
+ " git fetch --no-tags origin \\",
563
+ ' "+refs/heads/$PR_BASE_REF:refs/remotes/origin/pr-base" \\',
564
+ ' "+refs/pull/$PR_NUMBER/head:refs/remotes/origin/pr-head"'
565
+ ].join("\n"),
566
+ [
567
+ "- name: Run Codex review",
568
+ " id: codex-review",
569
+ ` if: ${gateCondition}`,
570
+ " uses: openai/codex-action@v1",
571
+ " with:",
572
+ ` openai-api-key: \${{ secrets.${SECRET_NAME4} }}`,
573
+ ' permission-profile: ":read-only"',
574
+ " prompt: |",
575
+ indentBlock(composePrompt4(input), 6)
576
+ ].join("\n"),
577
+ [
578
+ "- name: Post review comment",
579
+ ` if: ${gateCondition}`,
580
+ " env:",
581
+ " GH_TOKEN: ${{ github.token }}",
582
+ " PR_NUMBER: ${{ github.event.pull_request.number }}",
583
+ " REVIEW_BODY: ${{ steps.codex-review.outputs.final-message }}",
584
+ " run: |",
585
+ ' printf %s "$REVIEW_BODY" | gh pr comment "$PR_NUMBER" --body-file -'
586
+ ].join("\n"),
587
+ renderWorkspaceSetupSteps(input.packageManager),
588
+ renderVerdictGateStep(input.maxRounds, input.locale)
589
+ ];
590
+ return [
591
+ "name: GPT Code Review",
592
+ "",
593
+ renderTriggerBlock(),
594
+ "",
595
+ renderConcurrencyBlock(),
596
+ "",
597
+ renderPermissionsBlock(),
598
+ "",
599
+ "jobs:",
600
+ " review:",
601
+ " runs-on: ubuntu-latest",
602
+ " steps:",
603
+ indentBlock(steps.join("\n\n"), 6),
604
+ ""
605
+ ].join("\n");
606
+ }
607
+ var gptProvider = {
608
+ id: "gpt",
609
+ workflowFileName: ".github/workflows/gpt-code-review.yml",
610
+ requiredSecretName: SECRET_NAME4,
611
+ buildWorkflowYaml: buildWorkflowYaml4
612
+ };
613
+
614
+ // ../template-engine/dist/review/provider.js
615
+ var REVIEW_PROVIDERS = {
616
+ claude: claudeProvider,
617
+ gemini: geminiProvider,
618
+ gpt: gptProvider,
619
+ cursor: cursorProvider
620
+ };
621
+
622
+ // ../template-engine/dist/review/compose-review-setup.js
623
+ function composeReviewSetup(input) {
624
+ if (!input.enabled)
625
+ return null;
626
+ const provider = REVIEW_PROVIDERS[input.provider];
627
+ return {
628
+ workflowFilePath: provider.workflowFileName,
629
+ workflowYaml: provider.buildWorkflowYaml(input),
630
+ requiredSecretName: provider.requiredSecretName,
631
+ configSnippet: buildReviewConfigSnippet(input)
632
+ };
633
+ }
634
+
635
+ // ../template-engine/dist/review/conflict.js
636
+ function judgeReviewFileConflict(existing, generated) {
637
+ if (existing === null)
638
+ return { kind: "create" };
639
+ if (existing === generated)
640
+ return { kind: "unchanged" };
641
+ return { kind: "merge-proposal", existing, generated };
642
+ }
643
+
644
+ // src/generators/shared/dry-run.ts
645
+ function resolveDryRun(options) {
646
+ return process.env["NX_DRY_RUN"] === "true" || (options.dryRun ?? false);
647
+ }
648
+
649
+ // src/generators/shared/toolkit-config-file.ts
650
+ function detectIndent(raw) {
651
+ for (const line of raw.split("\n")) {
652
+ const match = /^(\t+| +)/.exec(line);
653
+ if (match) return match[1];
654
+ }
655
+ return " ";
656
+ }
657
+ function recordedChoice(config, key, allowed) {
658
+ const value = config?.[key];
659
+ return typeof value === "string" && allowed.includes(value) ? value : void 0;
660
+ }
661
+ var RECORDED_PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
662
+ function workspacePackageManager(exists, read, config) {
663
+ return detectWorkspacePackageManager(exists, read) ?? recordedChoice(config, "packageManager", RECORDED_PACKAGE_MANAGERS) ?? null;
664
+ }
665
+ function treePackageManager(tree, config) {
666
+ return workspacePackageManager((p) => tree.exists(p), (p) => tree.read(p, "utf-8"), config);
667
+ }
668
+
669
+ // src/generators/add-review/render.ts
670
+ function renderLineDiff(existing, generated) {
671
+ const a = existing.split("\n");
672
+ const b = generated.split("\n");
673
+ const m = a.length;
674
+ const n = b.length;
675
+ const lcs = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
676
+ for (let i2 = m - 1; i2 >= 0; i2--) {
677
+ for (let j2 = n - 1; j2 >= 0; j2--) {
678
+ lcs[i2][j2] = a[i2] === b[j2] ? lcs[i2 + 1][j2 + 1] + 1 : Math.max(lcs[i2 + 1][j2], lcs[i2][j2 + 1]);
679
+ }
680
+ }
681
+ const out = ["--- existing", "+++ generated"];
682
+ let i = 0;
683
+ let j = 0;
684
+ while (i < m && j < n) {
685
+ if (a[i] === b[j]) {
686
+ out.push(` ${a[i]}`);
687
+ i++;
688
+ j++;
689
+ } else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
690
+ out.push(`- ${a[i]}`);
691
+ i++;
692
+ } else {
693
+ out.push(`+ ${b[j]}`);
694
+ j++;
695
+ }
696
+ }
697
+ while (i < m) out.push(`- ${a[i++]}`);
698
+ while (j < n) out.push(`+ ${b[j++]}`);
699
+ return out.join("\n");
700
+ }
701
+ var WORKFLOW_ACTION_LINES = {
702
+ created: "created",
703
+ overwritten: "overwritten with the generated version (per --onConflict=overwrite)",
704
+ unchanged: "unchanged (already matches the generated version)",
705
+ proposed: "NOT written \u2014 differs from the generated version (merge proposal below)",
706
+ skipped: "left as-is (per --onConflict=skip)",
707
+ alternate: "generated alongside the existing file",
708
+ aborted: "NOT written"
709
+ };
710
+ var CONFIG_ACTION_LINES = {
711
+ created: "created with the review field",
712
+ updated: "review field updated",
713
+ unchanged: "review field unchanged",
714
+ "not-updated": "NOT updated \u2014 kept consistent with the workflow file that is actually in effect",
715
+ aborted: "NOT updated"
716
+ };
717
+ function renderAddReviewReport(report) {
718
+ const lines = [];
719
+ if (report.packageManagerNote !== void 0) {
720
+ lines.push(`Note: ${report.packageManagerNote}`);
721
+ }
722
+ if (report.workflowAction === "aborted") {
723
+ lines.push("add-review aborted \u2014 nothing was written.");
724
+ lines.push(` toolkit.config.json: ${report.configError ?? "unusable"}`);
725
+ lines.push(" Fix the config file manually, then rerun the generator.");
726
+ return lines.join("\n");
727
+ }
728
+ if (report.dryRun) {
729
+ lines.push("add-review dry-run \u2014 no files were written.");
730
+ lines.push("");
731
+ lines.push("Generated workflow YAML:");
732
+ lines.push(report.workflowYaml ?? "");
733
+ lines.push("");
734
+ lines.push("toolkit.config.json review field:");
735
+ lines.push(report.configSnippetJson ?? "");
736
+ lines.push("");
737
+ lines.push("Effective decision:");
738
+ }
739
+ lines.push(` ${report.workflowPath}: ${WORKFLOW_ACTION_LINES[report.workflowAction]}`);
740
+ lines.push(` toolkit.config.json: ${CONFIG_ACTION_LINES[report.configAction]}`);
741
+ if (report.workflowAction === "proposed" && report.diff) {
742
+ lines.push("");
743
+ lines.push(report.diff);
744
+ lines.push("");
745
+ lines.push("To resolve, rerun with one of:");
746
+ lines.push(" --onConflict=overwrite replace the existing file with the generated version");
747
+ lines.push(" --onConflict=skip keep the existing file");
748
+ lines.push(" --onConflict=alternate-filename write the generated version alongside it");
749
+ }
750
+ if (report.workflowAction === "alternate") {
751
+ lines.push("");
752
+ lines.push(" Review the generated file, then replace the main workflow file with it yourself;");
753
+ lines.push(" the config stays pointing at the workflow that is actually in effect until you do.");
754
+ }
755
+ lines.push("");
756
+ lines.push(`Note: the review workflow stays green-skipped until you register the ${report.requiredSecretName} secret.`);
757
+ lines.push("Note: this workflow runs with elevated permissions only after it is merged to the default branch \u2014");
758
+ lines.push("merging is the first real validation.");
759
+ return lines.join("\n");
760
+ }
761
+
762
+ // src/generators/add-review/generator.ts
763
+ var CONFIG_PATH = "toolkit.config.json";
764
+ function deriveAltWorkflowPath(mainWorkflowPath) {
765
+ return mainWorkflowPath.replace(/\.yml$/, ".archweave.yml");
766
+ }
767
+ function readConfig(tree) {
768
+ const raw = tree.read(CONFIG_PATH, "utf-8");
769
+ if (raw === null) return { raw: null, parsed: null, error: null };
770
+ try {
771
+ const parsed = JSON.parse(raw);
772
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
773
+ return { raw, parsed: null, error: `${CONFIG_PATH} must contain a top-level JSON object` };
774
+ }
775
+ return { raw, parsed, error: null };
776
+ } catch {
777
+ return { raw, parsed: null, error: `${CONFIG_PATH} is not valid JSON` };
778
+ }
779
+ }
780
+ function resolveLocale(option, config) {
781
+ if (option) return option;
782
+ const fromConfig = config.parsed?.["locale"];
783
+ return fromConfig === "ko" || fromConfig === "en" ? fromConfig : "en";
784
+ }
785
+ function deepEqual(a, b) {
786
+ if (a === b) return true;
787
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
788
+ if (Array.isArray(a) || Array.isArray(b)) {
789
+ return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((value, index) => deepEqual(value, b[index]));
790
+ }
791
+ const aKeys = Object.keys(a).sort();
792
+ const bKeys = Object.keys(b).sort();
793
+ if (aKeys.length !== bKeys.length || aKeys.some((key, index) => key !== bKeys[index])) return false;
794
+ return aKeys.every((key) => deepEqual(a[key], b[key]));
795
+ }
796
+ var PROVIDERS = ["claude", "gemini", "gpt", "cursor"];
797
+ var MODES = ["fast", "standard", "thorough"];
798
+ function recordedReview(config) {
799
+ const review = config?.["review"];
800
+ if (typeof review !== "object" || review === null || Array.isArray(review)) return {};
801
+ const record = review;
802
+ const pick = (value, allowed) => typeof value === "string" && allowed.includes(value) ? value : void 0;
803
+ const rounds = record["maxRounds"];
804
+ return {
805
+ provider: pick(record["provider"], PROVIDERS),
806
+ mode: pick(record["mode"], MODES),
807
+ maxRounds: rounds === 1 || rounds === 2 || rounds === 3 || rounds === 4 || rounds === 5 ? rounds : void 0
808
+ };
809
+ }
810
+ function runAddReview(tree, options) {
811
+ const config = readConfig(tree);
812
+ const detectedPm = treePackageManager(tree, config.parsed);
813
+ const packageManager = options.packageManager ?? detectedPm ?? "pnpm";
814
+ const packageManagerNote = options.packageManager === void 0 && detectedPm === null ? "the package manager could not be detected (no pnpm, yarn, bun or npm lockfile) \u2014 the workflow was rendered for pnpm; re-run with --packageManager=npm|yarn|bun if that is wrong." : void 0;
815
+ const recorded = recordedReview(config.parsed);
816
+ const plan = composeReviewSetup({
817
+ enabled: true,
818
+ provider: options.provider ?? recorded.provider ?? "claude",
819
+ mode: options.mode ?? recorded.mode ?? "standard",
820
+ locale: resolveLocale(options.locale, config),
821
+ maxRounds: options.maxRounds ?? recorded.maxRounds ?? 3,
822
+ packageManager
823
+ });
824
+ if (plan === null) throw new Error("unreachable: add-review always composes with enabled: true");
825
+ if (config.error !== null) {
826
+ return {
827
+ dryRun: options.dryRun ?? false,
828
+ workflowPath: plan.workflowFilePath,
829
+ workflowAction: "aborted",
830
+ configAction: "aborted",
831
+ requiredSecretName: plan.requiredSecretName,
832
+ diff: null,
833
+ workflowYaml: null,
834
+ configSnippetJson: null,
835
+ configError: config.error
836
+ };
837
+ }
838
+ const existing = tree.read(plan.workflowFilePath, "utf-8");
839
+ const verdict = judgeReviewFileConflict(existing, plan.workflowYaml);
840
+ let workflowAction;
841
+ let workflowPath = plan.workflowFilePath;
842
+ let writeWorkflowTo = null;
843
+ let diff = null;
844
+ let mainFileMatchesOptions;
845
+ if (verdict.kind === "create") {
846
+ workflowAction = "created";
847
+ writeWorkflowTo = plan.workflowFilePath;
848
+ mainFileMatchesOptions = true;
849
+ } else if (verdict.kind === "unchanged") {
850
+ workflowAction = "unchanged";
851
+ mainFileMatchesOptions = true;
852
+ } else {
853
+ switch (options.onConflict) {
854
+ case "overwrite":
855
+ workflowAction = "overwritten";
856
+ writeWorkflowTo = plan.workflowFilePath;
857
+ mainFileMatchesOptions = true;
858
+ break;
859
+ case "skip":
860
+ workflowAction = "skipped";
861
+ mainFileMatchesOptions = false;
862
+ break;
863
+ case "alternate-filename": {
864
+ const altWorkflowPath = deriveAltWorkflowPath(plan.workflowFilePath);
865
+ workflowAction = "alternate";
866
+ workflowPath = altWorkflowPath;
867
+ writeWorkflowTo = altWorkflowPath;
868
+ mainFileMatchesOptions = false;
869
+ break;
870
+ }
871
+ default:
872
+ workflowAction = "proposed";
873
+ diff = renderLineDiff(verdict.existing, verdict.generated);
874
+ mainFileMatchesOptions = false;
875
+ }
876
+ }
877
+ let configAction;
878
+ let newConfigContent = null;
879
+ if (!mainFileMatchesOptions) {
880
+ configAction = "not-updated";
881
+ } else {
882
+ const existingReview = config.parsed?.["review"];
883
+ if (deepEqual(existingReview, plan.configSnippet)) {
884
+ configAction = "unchanged";
885
+ } else {
886
+ configAction = config.raw === null ? "created" : "updated";
887
+ const indent = config.raw === null ? " " : detectIndent(config.raw);
888
+ const trailingNewline = config.raw === null || config.raw.endsWith("\n");
889
+ const body = JSON.stringify({ ...config.parsed ?? {}, review: plan.configSnippet }, null, indent);
890
+ newConfigContent = trailingNewline ? `${body}
891
+ ` : body;
892
+ }
893
+ }
894
+ const dryRun = options.dryRun ?? false;
895
+ if (!dryRun) {
896
+ if (writeWorkflowTo !== null) tree.write(writeWorkflowTo, plan.workflowYaml);
897
+ if (newConfigContent !== null) tree.write(CONFIG_PATH, newConfigContent);
898
+ }
899
+ return {
900
+ dryRun,
901
+ workflowPath,
902
+ workflowAction,
903
+ configAction,
904
+ requiredSecretName: plan.requiredSecretName,
905
+ diff,
906
+ workflowYaml: dryRun ? plan.workflowYaml : null,
907
+ configSnippetJson: dryRun ? JSON.stringify(plan.configSnippet, null, 2) : null,
908
+ configError: null,
909
+ ...packageManagerNote !== void 0 ? { packageManagerNote } : {}
910
+ };
911
+ }
912
+ async function addReviewGenerator(tree, options) {
913
+ console.log(renderAddReviewReport(runAddReview(tree, { ...options, dryRun: resolveDryRun(options) })));
914
+ }
915
+ var generator_default = addReviewGenerator;
916
+ export {
917
+ addReviewGenerator,
918
+ generator_default as default,
919
+ recordedReview,
920
+ runAddReview
921
+ };