@clearlydev/nx-archweave 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/review-verdict.js +5 -248
- package/dist/eslint/index.js +28 -321
- package/dist/generators/_chunks/chunk-2WSX5YNB.js +274 -0
- package/dist/generators/_chunks/chunk-34V6HAJT.js +55 -0
- package/dist/generators/_chunks/chunk-3A6RZUCZ.js +114 -0
- package/dist/generators/_chunks/chunk-47T7NOPP.js +30 -0
- package/dist/generators/_chunks/chunk-5MI3O4VZ.js +99 -0
- package/dist/generators/_chunks/chunk-67INEVWL.js +1068 -0
- package/dist/generators/_chunks/chunk-6ZTIUBB2.js +139 -0
- package/dist/generators/_chunks/chunk-AIJCJ7JK.js +203 -0
- package/dist/generators/_chunks/chunk-AQHV7DJS.js +656 -0
- package/dist/generators/_chunks/chunk-C2VVUWBA.js +386 -0
- package/dist/generators/_chunks/chunk-EXYZLL7A.js +234 -0
- package/dist/generators/_chunks/chunk-FQHFT5IH.js +50 -0
- package/dist/generators/_chunks/chunk-H222FPLK.js +80 -0
- package/dist/generators/_chunks/chunk-IIDOBI42.js +616 -0
- package/dist/generators/_chunks/chunk-JHRTVURZ.js +189 -0
- package/dist/generators/_chunks/chunk-L2RZ2LBO.js +132 -0
- package/dist/generators/_chunks/chunk-MQNQJOWA.js +217 -0
- package/dist/generators/_chunks/chunk-QQNDYJTR.js +243 -0
- package/dist/generators/_chunks/chunk-SFXU6IUS.js +0 -0
- package/dist/generators/_chunks/chunk-SNBIVIMI.js +89 -0
- package/dist/generators/_chunks/chunk-TUN34LAQ.js +8 -0
- package/dist/generators/_chunks/chunk-TXAQ2GJC.js +20 -0
- package/dist/generators/_chunks/chunk-U6LAFHSF.js +49 -0
- package/dist/generators/_chunks/chunk-URV6FLUH.js +73 -0
- package/dist/generators/_chunks/chunk-YNMLB4DL.js +25 -0
- package/dist/generators/_chunks/chunk-YY2UESLM.js +282 -0
- package/dist/generators/add-agent-rules/generator.js +11 -842
- package/dist/generators/add-backend/generator.js +11 -906
- package/dist/generators/add-fsd-lite/generator.js +12 -801
- package/dist/generators/add-github-templates/generator.js +13 -116
- package/dist/generators/add-integration/generator.js +15 -743
- package/dist/generators/add-review/generator.js +12 -1231
- package/dist/generators/allow-builds/generator.js +7 -71
- package/dist/generators/doctor/generator.js +74 -2042
- package/dist/generators/entity/generator.js +15 -604
- package/dist/generators/feature/generator.js +15 -604
- package/dist/generators/init/generator.js +68 -3376
- package/dist/generators/install-integration/generator.js +35 -871
- package/dist/generators/page/generator.js +15 -604
- package/dist/generators/record-integration/generator.js +12 -778
- package/dist/generators/shared/generator.js +15 -604
- package/dist/generators/widget/generator.js +15 -604
- package/dist/index.js +5 -663
- package/package.json +2 -2
|
@@ -1,1234 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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-comment.js
|
|
137
|
-
var VERDICT_MARKER = "<!-- archweave-verdict -->";
|
|
138
|
-
function renderCommitLine(locale, shortSha) {
|
|
139
|
-
return locale === "en" ? `<sub>Commit \`${shortSha}\` \xB7 this comment is updated on every review round.</sub>` : `<sub>\uCEE4\uBC0B \`${shortSha}\` \xB7 \uC774 \uCF54\uBA58\uD2B8\uB294 \uB9AC\uBDF0 \uB77C\uC6B4\uB4DC\uB9C8\uB2E4 \uAC31\uC2E0\uB429\uB2C8\uB2E4.</sub>`;
|
|
140
|
-
}
|
|
141
|
-
var NOT_RUN_REASON = {
|
|
142
|
-
en: {
|
|
143
|
-
secret: (secretName) => `the ${secretName} secret is not registered`,
|
|
144
|
-
fork: () => "this pull request comes from a fork, and GitHub passes no secrets to runs from forks",
|
|
145
|
-
dependabot: (secretName) => `Dependabot runs receive only Dependabot secrets, and ${secretName} is not one of them`,
|
|
146
|
-
rounds: (_secretName, maxRounds) => `the automated review round limit (${maxRounds}) is reached`
|
|
147
|
-
},
|
|
148
|
-
ko: {
|
|
149
|
-
secret: (secretName) => `${secretName} \uC2DC\uD06C\uB9BF\uC774 \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC74C`,
|
|
150
|
-
fork: () => "\uD3EC\uD06C\uC5D0\uC11C \uC628 pull request\uC774\uACE0, GitHub\uB294 \uD3EC\uD06C \uC2E4\uD589\uC5D0 \uC2DC\uD06C\uB9BF\uC744 \uB118\uAE30\uC9C0 \uC54A\uC74C",
|
|
151
|
-
dependabot: (secretName) => `Dependabot \uC2E4\uD589\uC740 Dependabot \uC2DC\uD06C\uB9BF\uB9CC \uBC1B\uB294\uB370, ${secretName}\uB294 \uADF8\uC911\uC5D0 \uC5C6\uC74C`,
|
|
152
|
-
rounds: (_secretName, maxRounds) => `\uC790\uB3D9 \uB9AC\uBDF0 \uB77C\uC6B4\uB4DC \uD55C\uB3C4(${maxRounds})\uC5D0 \uB3C4\uB2EC\uD568`
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
function renderNotRunNotice(locale, reason, secretName, maxRounds) {
|
|
156
|
-
const why = NOT_RUN_REASON[locale][reason](secretName, maxRounds);
|
|
157
|
-
return locale === "en" ? `AI review did not run for this commit (${why}). A green check here does not mean the change was reviewed.` : `\uC774 \uCEE4\uBC0B\uC5D0\uB294 AI \uB9AC\uBDF0\uAC00 \uC2E4\uD589\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4(${why}). \uC774 green \uCCB4\uD06C\uB294 \uBCC0\uACBD \uC0AC\uD56D\uC774 \uB9AC\uBDF0\uB418\uC5C8\uB2E4\uB294 \uB73B\uC774 \uC544\uB2D9\uB2C8\uB2E4.`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ../review-policy/dist/verdict-gate/verdict-gate.js
|
|
161
|
-
var ROUND_MARKER = "<!-- archweave-review-round -->";
|
|
162
|
-
|
|
163
|
-
// ../template-engine/dist/review/config-snippet.js
|
|
164
|
-
function buildReviewConfigSnippet(input) {
|
|
165
|
-
return {
|
|
166
|
-
enabled: true,
|
|
167
|
-
provider: input.provider,
|
|
168
|
-
mode: input.mode,
|
|
169
|
-
locale: input.locale,
|
|
170
|
-
maxRounds: input.maxRounds,
|
|
171
|
-
blockingPriorities: ["P0", "P1"]
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// ../template-engine/dist/review/action-pins.js
|
|
176
|
-
var ACTION_PINS = {
|
|
177
|
-
"actions/checkout": "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1",
|
|
178
|
-
"actions/setup-node": "actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0",
|
|
179
|
-
"pnpm/action-setup": "pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0",
|
|
180
|
-
"oven-sh/setup-bun": "oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0",
|
|
181
|
-
"anthropics/claude-code-action": "anthropics/claude-code-action@6b6870e649b5ff408753befeaee5fc7989d4e6a9 # v1.0.258",
|
|
182
|
-
"google-github-actions/run-gemini-cli": "google-github-actions/run-gemini-cli@f77273f4c914e4bf38440cf36a0369cb64a37489 # v0.1.22",
|
|
183
|
-
"openai/codex-action": "openai/codex-action@bdf19a4a223ec2549a3e2274a0cf61556bc07675 # v1.13"
|
|
184
|
-
};
|
|
185
|
-
function pinnedUses(action) {
|
|
186
|
-
return ACTION_PINS[action];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ../template-engine/dist/review/workflow-skeleton.js
|
|
190
|
-
function indentBlock(text, spaces) {
|
|
191
|
-
const pad = " ".repeat(spaces);
|
|
192
|
-
return text.split("\n").map((line) => line.length === 0 ? line : pad + line).join("\n");
|
|
193
|
-
}
|
|
194
|
-
function renderTriggerBlock() {
|
|
195
|
-
return ["on:", " pull_request:", " types: [opened, synchronize, ready_for_review, reopened]"].join("\n");
|
|
196
|
-
}
|
|
197
|
-
function renderConcurrencyBlock() {
|
|
198
|
-
return [
|
|
199
|
-
"concurrency:",
|
|
200
|
-
" group: archweave-review-${{ github.event.pull_request.number }}",
|
|
201
|
-
" cancel-in-progress: false"
|
|
202
|
-
].join("\n");
|
|
203
|
-
}
|
|
204
|
-
var REVIEW_GATE_CONDITION = "steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'";
|
|
205
|
-
function renderReviewPermissions(idToken) {
|
|
206
|
-
return ["permissions:", " contents: read", " pull-requests: write", ...idToken ? [" id-token: write"] : []].join("\n");
|
|
207
|
-
}
|
|
208
|
-
function renderCheckoutStep(gateCondition, extraWith = []) {
|
|
209
|
-
return [
|
|
210
|
-
"- name: Checkout repository",
|
|
211
|
-
...gateCondition === void 0 ? [] : [` if: ${gateCondition}`],
|
|
212
|
-
` uses: ${pinnedUses("actions/checkout")}`,
|
|
213
|
-
" with:",
|
|
214
|
-
...extraWith.map((line) => ` ${line}`),
|
|
215
|
-
" fetch-depth: 1",
|
|
216
|
-
" persist-credentials: false"
|
|
217
|
-
].join("\n");
|
|
218
|
-
}
|
|
219
|
-
function renderSecretGateStep(secretName) {
|
|
220
|
-
return [
|
|
221
|
-
"- name: Check review secret",
|
|
222
|
-
" id: secret-gate",
|
|
223
|
-
" env:",
|
|
224
|
-
` REVIEW_SECRET: \${{ secrets.${secretName} }}`,
|
|
225
|
-
" run: |",
|
|
226
|
-
' if [ -z "$REVIEW_SECRET" ]; then',
|
|
227
|
-
` echo "AI review is not active: register the ${secretName} secret to enable it."`,
|
|
228
|
-
' echo "enabled=false" >> "$GITHUB_OUTPUT"',
|
|
229
|
-
" else",
|
|
230
|
-
' echo "enabled=true" >> "$GITHUB_OUTPUT"',
|
|
231
|
-
" fi"
|
|
232
|
-
].join("\n");
|
|
233
|
-
}
|
|
234
|
-
function renderRoundGateStep(maxRounds) {
|
|
235
|
-
return [
|
|
236
|
-
"- name: Check review round limit",
|
|
237
|
-
" id: round-gate",
|
|
238
|
-
" if: steps.secret-gate.outputs.enabled == 'true'",
|
|
239
|
-
" env:",
|
|
240
|
-
" GH_TOKEN: ${{ github.token }}",
|
|
241
|
-
" run: |",
|
|
242
|
-
` counts=$(gh api "repos/\${{ github.repository }}/issues/\${{ github.event.pull_request.number }}/comments" --paginate --jq '[([.[] | select(.user.type == "Bot") | select(.body | contains("${ROUND_MARKER}"))] | length), ([.[].id] | max // 0)]' | jq -s -r '[(map(.[0]) | add // 0), (map(.[1]) | max // 0)] | @tsv')`,
|
|
243
|
-
` rounds=$(printf '%s' "$counts" | cut -f1)`,
|
|
244
|
-
` echo "last-comment-id=$(printf '%s' "$counts" | cut -f2)" >> "$GITHUB_OUTPUT"`,
|
|
245
|
-
` if [ "$rounds" -ge ${maxRounds} ]; then`,
|
|
246
|
-
` echo "Automated review round limit (${maxRounds}) reached for this PR; skipping."`,
|
|
247
|
-
' echo "allowed=false" >> "$GITHUB_OUTPUT"',
|
|
248
|
-
" else",
|
|
249
|
-
' echo "allowed=true" >> "$GITHUB_OUTPUT"',
|
|
250
|
-
" fi"
|
|
251
|
-
].join("\n");
|
|
252
|
-
}
|
|
253
|
-
function shellQuote(text) {
|
|
254
|
-
return `'${text.replace(/'/g, `'\\''`)}'`;
|
|
255
|
-
}
|
|
256
|
-
function renderNotRunFunction(secretName, maxRounds, locale) {
|
|
257
|
-
const reasons = ["secret", "fork", "dependabot", "rounds"];
|
|
258
|
-
const [commitBefore, commitAfter] = renderCommitLine(locale, "\0").split("\0");
|
|
259
|
-
return [
|
|
260
|
-
"not_run() {",
|
|
261
|
-
' case "$1" in',
|
|
262
|
-
...reasons.map((reason) => ` ${reason}) annotation=${shellQuote(renderNotRunNotice("en", reason, secretName, maxRounds))}; notice=${shellQuote(renderNotRunNotice(locale, reason, secretName, maxRounds))} ;;`),
|
|
263
|
-
" esac",
|
|
264
|
-
' echo "::notice title=AI review did not run::$annotation"',
|
|
265
|
-
` printf '%s\\n' "$notice" >> "$GITHUB_STEP_SUMMARY"`,
|
|
266
|
-
` short_sha=$(printf '%s' "$HEAD_SHA" | cut -c1-7)`,
|
|
267
|
-
' case "$short_sha" in',
|
|
268
|
-
" '' | *[!0-9a-f]*) short_sha= ;;",
|
|
269
|
-
" esac",
|
|
270
|
-
" {",
|
|
271
|
-
' echo "notice-body<<ARCHWEAVE_NOT_RUN_EOF"',
|
|
272
|
-
` printf '%s\\n' ${shellQuote(VERDICT_MARKER)} "\u2139\uFE0F $notice"`,
|
|
273
|
-
` if [ -n "$short_sha" ]; then printf '\\n%s\\n' ${shellQuote(commitBefore)}"$short_sha"${shellQuote(commitAfter)}; fi`,
|
|
274
|
-
' echo "ARCHWEAVE_NOT_RUN_EOF"',
|
|
275
|
-
' echo "run=false"',
|
|
276
|
-
' } >> "$GITHUB_OUTPUT"',
|
|
277
|
-
"}"
|
|
278
|
-
];
|
|
279
|
-
}
|
|
280
|
-
var JUDGE_CONDITION = "steps.ai-review-result.outputs.run == 'true'";
|
|
281
|
-
function renderAiReviewResultStep(secretName, maxRounds, locale) {
|
|
282
|
-
return [
|
|
283
|
-
"- name: Check the AI review result",
|
|
284
|
-
" id: ai-review-result",
|
|
285
|
-
" env:",
|
|
286
|
-
" AI_REVIEW_RESULT: ${{ needs.ai-review.result }}",
|
|
287
|
-
" REVIEW_ENABLED: ${{ needs.ai-review.outputs.enabled }}",
|
|
288
|
-
" REVIEW_ALLOWED: ${{ needs.ai-review.outputs.allowed }}",
|
|
289
|
-
" LAST_COMMENT_ID: ${{ needs.ai-review.outputs.last-comment-id }}",
|
|
290
|
-
" HEAD_SHA: ${{ github.event.pull_request.head.sha }}",
|
|
291
|
-
" HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}",
|
|
292
|
-
" REPOSITORY: ${{ github.repository }}",
|
|
293
|
-
" PR_AUTHOR: ${{ github.event.pull_request.user.login }}",
|
|
294
|
-
" run: |",
|
|
295
|
-
...renderNotRunFunction(secretName, maxRounds, locale).map((line) => ` ${line}`),
|
|
296
|
-
' if [ "$AI_REVIEW_RESULT" != success ]; then',
|
|
297
|
-
` echo "::error title=AI review did not succeed::The AI review job ended with '$AI_REVIEW_RESULT'."`,
|
|
298
|
-
" exit 1",
|
|
299
|
-
" fi",
|
|
300
|
-
' case "$REVIEW_ENABLED/$REVIEW_ALLOWED" in',
|
|
301
|
-
" false/*)",
|
|
302
|
-
' if [ "$HEAD_REPOSITORY" != "$REPOSITORY" ]; then not_run fork',
|
|
303
|
-
` elif [ "$PR_AUTHOR" = 'dependabot[bot]' ]; then not_run dependabot`,
|
|
304
|
-
" else not_run secret",
|
|
305
|
-
" fi",
|
|
306
|
-
" exit 0",
|
|
307
|
-
" ;;",
|
|
308
|
-
" true/false)",
|
|
309
|
-
" not_run rounds",
|
|
310
|
-
" exit 0",
|
|
311
|
-
" ;;",
|
|
312
|
-
" true/true)",
|
|
313
|
-
' case "$LAST_COMMENT_ID" in',
|
|
314
|
-
" '' | *[!0-9]*) ;;",
|
|
315
|
-
" *)",
|
|
316
|
-
' echo "run=true" >> "$GITHUB_OUTPUT"',
|
|
317
|
-
" exit 0",
|
|
318
|
-
" ;;",
|
|
319
|
-
" esac",
|
|
320
|
-
" ;;",
|
|
321
|
-
" esac",
|
|
322
|
-
` echo "::error title=Review verdict not computed::The AI review job's results did not reach this job, so no verdict can be computed. Re-run all jobs."`,
|
|
323
|
-
" exit 1"
|
|
324
|
-
].join("\n");
|
|
325
|
-
}
|
|
326
|
-
function renderVerdictGateStep(maxRounds, locale) {
|
|
327
|
-
return [
|
|
328
|
-
"- name: Compute review verdict",
|
|
329
|
-
" id: verdict",
|
|
330
|
-
` if: ${JUDGE_CONDITION}`,
|
|
331
|
-
" env:",
|
|
332
|
-
" GH_TOKEN: ${{ github.token }}",
|
|
333
|
-
" GITHUB_REPOSITORY: ${{ github.repository }}",
|
|
334
|
-
" ARCHWEAVE_PR_NUMBER: ${{ github.event.pull_request.number }}",
|
|
335
|
-
` ARCHWEAVE_MAX_ROUNDS: '${maxRounds}'`,
|
|
336
|
-
` ARCHWEAVE_LOCALE: '${locale}'`,
|
|
337
|
-
" ARCHWEAVE_HEAD_SHA: ${{ github.event.pull_request.head.sha }}",
|
|
338
|
-
" ARCHWEAVE_ROUND_AFTER_ID: ${{ needs.ai-review.outputs.last-comment-id }}",
|
|
339
|
-
" ARCHWEAVE_VERDICT_OUTPUT: 'github-output'",
|
|
340
|
-
" run: |",
|
|
341
|
-
" if [ -x node_modules/.bin/archweave-review-verdict ]; then",
|
|
342
|
-
" node_modules/.bin/archweave-review-verdict",
|
|
343
|
-
" else",
|
|
344
|
-
' echo "nx-archweave is not installed in this workspace - skipping the verdict gate."',
|
|
345
|
-
" fi"
|
|
346
|
-
].join("\n");
|
|
347
|
-
}
|
|
348
|
-
function renderBlockStep() {
|
|
349
|
-
return [
|
|
350
|
-
"- name: Fail the review check",
|
|
351
|
-
" if: steps.verdict.outputs.blocked == 'true'",
|
|
352
|
-
" env:",
|
|
353
|
-
" BLOCK_REASON: ${{ steps.verdict.outputs.block-reason }}",
|
|
354
|
-
" run: |",
|
|
355
|
-
' case "$BLOCK_REASON" in',
|
|
356
|
-
' block) echo "The AI verdict is BLOCK: fix the P0/P1 findings in the verdict comment and push." ;;',
|
|
357
|
-
' no-summary) echo "The AI review posted no summary for this commit, so there is no verdict. Re-run all jobs to review it again, or review it yourself." ;;',
|
|
358
|
-
' unreadable) echo "The AI review summary has no readable findings block, so there is no verdict. Re-run all jobs to review it again, or review it yourself." ;;',
|
|
359
|
-
' *) echo "The review check failed. See the verdict comment on this pull request." ;;',
|
|
360
|
-
" esac",
|
|
361
|
-
" exit 1"
|
|
362
|
-
].join("\n");
|
|
363
|
-
}
|
|
364
|
-
function renderVerdictCommentStep() {
|
|
365
|
-
return [
|
|
366
|
-
"- name: Post review verdict",
|
|
367
|
-
" env:",
|
|
368
|
-
" GH_TOKEN: ${{ github.token }}",
|
|
369
|
-
" REPOSITORY: ${{ github.repository }}",
|
|
370
|
-
" PR_NUMBER: ${{ github.event.pull_request.number }}",
|
|
371
|
-
" VERDICT_BODY: ${{ needs.review.outputs.comment-body }}",
|
|
372
|
-
" PREVIOUS_ID: ${{ needs.review.outputs.previous-comment-id }}",
|
|
373
|
-
" run: |",
|
|
374
|
-
` payload=$(jq -n --arg body "$VERDICT_BODY" '{body: $body}')`,
|
|
375
|
-
' if [ -z "$PREVIOUS_ID" ]; then',
|
|
376
|
-
` PREVIOUS_ID=$(gh api "repos/$REPOSITORY/issues/$PR_NUMBER/comments" --paginate --jq '.[] | select(.user.login == "github-actions[bot]") | select(.body | contains("${VERDICT_MARKER}")) | .id' | tail -n 1) || PREVIOUS_ID=`,
|
|
377
|
-
" fi",
|
|
378
|
-
' case "$PREVIOUS_ID" in',
|
|
379
|
-
" ''|*[!0-9]*) PREVIOUS_ID= ;;",
|
|
380
|
-
" esac",
|
|
381
|
-
` if [ -n "$PREVIOUS_ID" ] && printf '%s' "$payload" | gh api -X PATCH "repos/$REPOSITORY/issues/comments/$PREVIOUS_ID" --input - > /dev/null; then`,
|
|
382
|
-
" exit 0",
|
|
383
|
-
" fi",
|
|
384
|
-
` printf '%s' "$payload" | gh api -X POST "repos/$REPOSITORY/issues/$PR_NUMBER/comments" --input - > /dev/null \\`,
|
|
385
|
-
' || echo "Could not post the verdict comment (continuing)."'
|
|
386
|
-
].join("\n");
|
|
387
|
-
}
|
|
388
|
-
var SETUP_NODE_VERSION = "22";
|
|
389
|
-
var FALLBACK_PNPM_VERSION = "10";
|
|
390
|
-
var INSTALL_LINES = {
|
|
391
|
-
pnpm: [" pnpm install --frozen-lockfile --ignore-scripts"],
|
|
392
|
-
npm: [" npm ci --ignore-scripts"],
|
|
393
|
-
yarn: [
|
|
394
|
-
" corepack enable yarn",
|
|
395
|
-
' if [ "$(yarn --version | cut -d. -f1)" -ge 2 ]; then YARN_ENABLE_SCRIPTS=false yarn install --immutable --mode=skip-build; else yarn install --frozen-lockfile --ignore-scripts; fi'
|
|
396
|
-
],
|
|
397
|
-
bun: [" bun install --frozen-lockfile --ignore-scripts"]
|
|
398
|
-
};
|
|
399
|
-
function renderPnpmVersionStep(gate) {
|
|
400
|
-
return [
|
|
401
|
-
"- name: Detect pnpm version",
|
|
402
|
-
" id: pnpm-version",
|
|
403
|
-
gate,
|
|
404
|
-
" run: |",
|
|
405
|
-
` if [ -n "$(jq -r '.packageManager // empty' package.json)" ]; then`,
|
|
406
|
-
' echo "version=" >> "$GITHUB_OUTPUT"',
|
|
407
|
-
" else",
|
|
408
|
-
` echo "version=${FALLBACK_PNPM_VERSION}" >> "$GITHUB_OUTPUT"`,
|
|
409
|
-
" fi"
|
|
410
|
-
].join("\n");
|
|
411
|
-
}
|
|
412
|
-
function renderWorkspaceSetupSteps(packageManager) {
|
|
413
|
-
const gate = ` if: ${JUDGE_CONDITION}`;
|
|
414
|
-
const steps = [];
|
|
415
|
-
if (packageManager === "pnpm") {
|
|
416
|
-
steps.push(renderPnpmVersionStep(gate));
|
|
417
|
-
steps.push([
|
|
418
|
-
"- name: Set up pnpm",
|
|
419
|
-
gate,
|
|
420
|
-
` uses: ${pinnedUses("pnpm/action-setup")}`,
|
|
421
|
-
" with:",
|
|
422
|
-
" version: ${{ steps.pnpm-version.outputs.version }}"
|
|
423
|
-
].join("\n"));
|
|
424
|
-
}
|
|
425
|
-
if (packageManager === "bun") {
|
|
426
|
-
steps.push(["- name: Set up bun", gate, ` uses: ${pinnedUses("oven-sh/setup-bun")}`].join("\n"));
|
|
427
|
-
}
|
|
428
|
-
steps.push([
|
|
429
|
-
"- name: Set up Node",
|
|
430
|
-
gate,
|
|
431
|
-
` uses: ${pinnedUses("actions/setup-node")}`,
|
|
432
|
-
" with:",
|
|
433
|
-
` node-version: ${SETUP_NODE_VERSION}`,
|
|
434
|
-
// yarn: no setup-node cache — it would query the runner's global yarn 1 even in a
|
|
435
|
-
// berry project. Corepack (shipped with Node) provides the project's own yarn below.
|
|
436
|
-
// bun: setup-node has no bun cache. Node is still set up — nx and the verdict bin
|
|
437
|
-
// run on it.
|
|
438
|
-
...packageManager === "yarn" || packageManager === "bun" ? [] : [` cache: ${packageManager}`]
|
|
439
|
-
].join("\n"));
|
|
440
|
-
steps.push([
|
|
441
|
-
"- name: Install workspace dependencies",
|
|
442
|
-
gate,
|
|
443
|
-
" run: |",
|
|
444
|
-
...INSTALL_LINES[packageManager]
|
|
445
|
-
].join("\n"));
|
|
446
|
-
return steps.join("\n\n");
|
|
447
|
-
}
|
|
448
|
-
var REVIEW_JOB_TIMEOUT_MINUTES = 15;
|
|
449
|
-
function renderReviewWorkflow(input, parts) {
|
|
450
|
-
const aiReviewSteps = [renderSecretGateStep(parts.secretName), renderRoundGateStep(input.maxRounds), ...parts.reviewSteps];
|
|
451
|
-
const reviewSteps = [
|
|
452
|
-
renderAiReviewResultStep(parts.secretName, input.maxRounds, input.locale),
|
|
453
|
-
renderCheckoutStep(JUDGE_CONDITION),
|
|
454
|
-
renderWorkspaceSetupSteps(input.packageManager),
|
|
455
|
-
renderVerdictGateStep(input.maxRounds, input.locale),
|
|
456
|
-
renderBlockStep()
|
|
457
|
-
];
|
|
458
|
-
return [
|
|
459
|
-
`name: ${parts.name}`,
|
|
460
|
-
"",
|
|
461
|
-
renderTriggerBlock(),
|
|
462
|
-
"",
|
|
463
|
-
renderConcurrencyBlock(),
|
|
464
|
-
"",
|
|
465
|
-
"permissions: {}",
|
|
466
|
-
"",
|
|
467
|
-
"jobs:",
|
|
468
|
-
" ai-review:",
|
|
469
|
-
" runs-on: ubuntu-latest",
|
|
470
|
-
` timeout-minutes: ${REVIEW_JOB_TIMEOUT_MINUTES}`,
|
|
471
|
-
indentBlock(renderReviewPermissions(parts.idToken), 4),
|
|
472
|
-
" outputs:",
|
|
473
|
-
" enabled: ${{ steps.secret-gate.outputs.enabled }}",
|
|
474
|
-
" allowed: ${{ steps.round-gate.outputs.allowed }}",
|
|
475
|
-
" last-comment-id: ${{ steps.round-gate.outputs.last-comment-id }}",
|
|
476
|
-
" steps:",
|
|
477
|
-
indentBlock(aiReviewSteps.join("\n\n"), 6),
|
|
478
|
-
"",
|
|
479
|
-
" review:",
|
|
480
|
-
" needs: ai-review",
|
|
481
|
-
" if: always()",
|
|
482
|
-
" runs-on: ubuntu-latest",
|
|
483
|
-
` timeout-minutes: ${REVIEW_JOB_TIMEOUT_MINUTES}`,
|
|
484
|
-
" permissions:",
|
|
485
|
-
" contents: read",
|
|
486
|
-
" pull-requests: read",
|
|
487
|
-
" outputs:",
|
|
488
|
-
" comment-body: ${{ steps.verdict.outputs.comment-body || steps.ai-review-result.outputs.notice-body }}",
|
|
489
|
-
" previous-comment-id: ${{ steps.verdict.outputs.previous-comment-id }}",
|
|
490
|
-
" steps:",
|
|
491
|
-
indentBlock(reviewSteps.join("\n\n"), 6),
|
|
492
|
-
"",
|
|
493
|
-
" review-comment:",
|
|
494
|
-
" needs: review",
|
|
495
|
-
// Runs after a BLOCK failed the review job, too: the comment explains the red check.
|
|
496
|
-
" if: ${{ !cancelled() && needs.review.outputs.comment-body != '' }}",
|
|
497
|
-
" runs-on: ubuntu-latest",
|
|
498
|
-
` timeout-minutes: ${REVIEW_JOB_TIMEOUT_MINUTES}`,
|
|
499
|
-
" permissions:",
|
|
500
|
-
" pull-requests: write",
|
|
501
|
-
" steps:",
|
|
502
|
-
indentBlock(renderVerdictCommentStep(), 6),
|
|
503
|
-
""
|
|
504
|
-
].join("\n");
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// ../template-engine/dist/review/claude/claude-provider.js
|
|
508
|
-
var SECRET_NAME = "CLAUDE_CODE_OAUTH_TOKEN";
|
|
509
|
-
var ALLOWED_TOOLS = "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)";
|
|
510
|
-
var CLAUDE_STEP_ID = "claude-review";
|
|
511
|
-
var PR_CONTEXT = ["REPO: ${{ github.repository }}", "PR NUMBER: ${{ github.event.pull_request.number }}"];
|
|
512
|
-
function renderWorkflowInstructions(locale) {
|
|
513
|
-
const lines = locale === "en" ? [
|
|
514
|
-
...PR_CONTEXT,
|
|
515
|
-
"",
|
|
516
|
-
"Review the diff of this pull request using `gh pr diff <PR NUMBER>` and `gh pr view <PR NUMBER>`.",
|
|
517
|
-
"Post specific findings as inline comments with `mcp__github_inline_comment__create_inline_comment` (pass `confirmed: true`), and exactly one top-level summary comment.",
|
|
518
|
-
"Post the summary with `gh pr comment <PR NUMBER> --body-file - <<'ARCHWEAVE_EOF'`, the body on the following lines, then a line `ARCHWEAVE_EOF` (the quoted delimiter keeps backticks and `$` literal). Never pass the body with `--body \"...\"` or `$(...)`.",
|
|
519
|
-
"Run each `gh` command on its own: no pipes to other programs, no `&&` chains.",
|
|
520
|
-
`Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
|
|
521
|
-
"If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
|
|
522
|
-
"Do not modify any code, and never approve or merge the pull request.",
|
|
523
|
-
"Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
|
|
524
|
-
] : [
|
|
525
|
-
...PR_CONTEXT,
|
|
526
|
-
"",
|
|
527
|
-
"\uC774 pull request\uC758 diff\uB97C `gh pr diff <PR NUMBER>`\uC640 `gh pr view <PR NUMBER>`\uB85C \uD655\uC778\uD558\uACE0 \uB9AC\uBDF0\uD558\uC138\uC694.",
|
|
528
|
-
"\uAD6C\uCCB4\uC801 \uC9C0\uC801\uC740 `mcp__github_inline_comment__create_inline_comment`(`confirmed: true` \uC804\uB2EC)\uB85C inline comment\uB97C \uB2EC\uACE0, \uC804\uCCB4 \uC694\uC57D\uC740 \uC815\uD655\uD788 \uD55C \uAC1C\uC758 top-level comment\uB85C \uAC8C\uC2DC\uD558\uC138\uC694.",
|
|
529
|
-
"\uC694\uC57D\uC740 `gh pr comment <PR NUMBER> --body-file - <<'ARCHWEAVE_EOF'`\uB85C \uAC8C\uC2DC\uD558\uC138\uC694. \uB2E4\uC74C \uC904\uBD80\uD130 \uBCF8\uBB38\uC744 \uC4F0\uACE0, \uB9C8\uC9C0\uB9C9 \uC904\uC5D0 `ARCHWEAVE_EOF`\uB9CC \uC4F0\uC138\uC694(\uB530\uC634\uD45C \uCE5C \uAD6C\uBD84\uC790\uB77C \uBC31\uD2F1\uACFC `$`\uAC00 \uADF8\uB300\uB85C \uB0A8\uC544\uC694). \uBCF8\uBB38\uC744 `--body \"...\"`\uB098 `$(...)`\uB85C \uB118\uAE30\uC9C0 \uB9C8\uC138\uC694.",
|
|
530
|
-
"`gh` \uBA85\uB839\uC740 \uD558\uB098\uC529 \uB530\uB85C \uC2E4\uD589\uD558\uC138\uC694. \uB2E4\uB978 \uD504\uB85C\uADF8\uB7A8\uC73C\uB85C \uD30C\uC774\uD504\uD558\uAC70\uB098 `&&`\uB85C \uC787\uC9C0 \uB9C8\uC138\uC694.",
|
|
531
|
-
`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.`,
|
|
532
|
-
'\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).',
|
|
533
|
-
"\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).",
|
|
534
|
-
"\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."
|
|
535
|
-
];
|
|
536
|
-
return lines.join("\n");
|
|
537
|
-
}
|
|
538
|
-
function composePrompt(input) {
|
|
539
|
-
const mode = REVIEW_MODES[input.mode];
|
|
540
|
-
const sections = [renderWorkflowInstructions(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
|
|
541
|
-
if (mode.includesOverEngineering) {
|
|
542
|
-
sections.push(buildOverEngineeringPrompt(input.locale));
|
|
543
|
-
}
|
|
544
|
-
sections.push(renderFindingsBlockInstruction(input.locale));
|
|
545
|
-
return sections.join("\n\n---\n\n");
|
|
546
|
-
}
|
|
547
|
-
function renderDeniedToolsStep() {
|
|
548
|
-
return [
|
|
549
|
-
"- name: Show denied tool calls",
|
|
550
|
-
` if: always() && steps.${CLAUDE_STEP_ID}.outputs.execution_file != ''`,
|
|
551
|
-
" env:",
|
|
552
|
-
` EXECUTION_FILE: \${{ steps.${CLAUDE_STEP_ID}.outputs.execution_file }}`,
|
|
553
|
-
" run: |",
|
|
554
|
-
' [ -f "$EXECUTION_FILE" ] || exit 0',
|
|
555
|
-
` stop_token="archweave-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \\n')"`,
|
|
556
|
-
' echo "::stop-commands::$stop_token"',
|
|
557
|
-
` jq -c '.[] | select(.type == "result") | ((.permission_denials[]? | {tool: .tool_name, command: ((.tool_input.command // "") | tostring | .[0:200])}), (select(.is_error == true) | {error: ((.result // "") | tostring | .[0:300])}))' "$EXECUTION_FILE" || echo "Could not read the execution file."`,
|
|
558
|
-
' echo "::$stop_token::"'
|
|
559
|
-
].join("\n");
|
|
560
|
-
}
|
|
561
|
-
function buildWorkflowYaml(input) {
|
|
562
|
-
return renderReviewWorkflow(input, {
|
|
563
|
-
name: "Claude Code Review",
|
|
564
|
-
secretName: SECRET_NAME,
|
|
565
|
-
// The Claude GitHub App signs in with the workflow's OIDC token (claude-code-action's FAQ).
|
|
566
|
-
idToken: true,
|
|
567
|
-
reviewSteps: [
|
|
568
|
-
renderCheckoutStep(REVIEW_GATE_CONDITION),
|
|
569
|
-
[
|
|
570
|
-
"- name: Run Claude review",
|
|
571
|
-
` id: ${CLAUDE_STEP_ID}`,
|
|
572
|
-
` if: ${REVIEW_GATE_CONDITION}`,
|
|
573
|
-
` uses: ${pinnedUses("anthropics/claude-code-action")}`,
|
|
574
|
-
" with:",
|
|
575
|
-
` claude_code_oauth_token: \${{ secrets.${SECRET_NAME} }}`,
|
|
576
|
-
" prompt: |",
|
|
577
|
-
indentBlock(composePrompt(input), 6),
|
|
578
|
-
" claude_args: |",
|
|
579
|
-
` --allowedTools "${ALLOWED_TOOLS}"`
|
|
580
|
-
].join("\n"),
|
|
581
|
-
renderDeniedToolsStep()
|
|
582
|
-
]
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
var claudeProvider = {
|
|
586
|
-
id: "claude",
|
|
587
|
-
workflowFileName: ".github/workflows/claude-code-review.yml",
|
|
588
|
-
requiredSecretName: SECRET_NAME,
|
|
589
|
-
buildWorkflowYaml
|
|
590
|
-
};
|
|
591
|
-
|
|
592
|
-
// ../template-engine/dist/review/cursor/cursor-provider.js
|
|
593
|
-
var SECRET_NAME2 = "CURSOR_API_KEY";
|
|
594
|
-
var PROMPT_HEREDOC_DELIMITER = "ARCHWEAVE_CURSOR_REVIEW_PROMPT";
|
|
595
|
-
function renderWorkflowInstructions2(locale) {
|
|
596
|
-
const lines = locale === "en" ? [
|
|
597
|
-
"Review the diff of this pull request using `gh pr diff` and `gh pr view`.",
|
|
598
|
-
"Post specific findings as inline comments and exactly one top-level summary comment.",
|
|
599
|
-
`Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
|
|
600
|
-
"If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
|
|
601
|
-
"Do not modify any code, and never approve or merge the pull request.",
|
|
602
|
-
"Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
|
|
603
|
-
] : [
|
|
604
|
-
"\uC774 pull request\uC758 diff\uB97C `gh pr diff`\uC640 `gh pr view`\uB85C \uD655\uC778\uD558\uACE0 \uB9AC\uBDF0\uD558\uC138\uC694.",
|
|
605
|
-
"\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.",
|
|
606
|
-
`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.`,
|
|
607
|
-
'\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).',
|
|
608
|
-
"\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).",
|
|
609
|
-
"\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."
|
|
610
|
-
];
|
|
611
|
-
return lines.join("\n");
|
|
612
|
-
}
|
|
613
|
-
function composePrompt2(input) {
|
|
614
|
-
const mode = REVIEW_MODES[input.mode];
|
|
615
|
-
const sections = [renderWorkflowInstructions2(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
|
|
616
|
-
if (mode.includesOverEngineering) {
|
|
617
|
-
sections.push(buildOverEngineeringPrompt(input.locale));
|
|
618
|
-
}
|
|
619
|
-
sections.push(renderFindingsBlockInstruction(input.locale));
|
|
620
|
-
return sections.join("\n\n---\n\n");
|
|
621
|
-
}
|
|
622
|
-
function buildWorkflowYaml2(input) {
|
|
623
|
-
return renderReviewWorkflow(input, {
|
|
624
|
-
name: "Cursor Code Review",
|
|
625
|
-
secretName: SECRET_NAME2,
|
|
626
|
-
// An API key: the Cursor CLI needs no OIDC token.
|
|
627
|
-
idToken: false,
|
|
628
|
-
reviewSteps: [
|
|
629
|
-
renderCheckoutStep(REVIEW_GATE_CONDITION),
|
|
630
|
-
[
|
|
631
|
-
"- name: Install cursor-agent CLI",
|
|
632
|
-
` if: ${REVIEW_GATE_CONDITION}`,
|
|
633
|
-
" run: |",
|
|
634
|
-
// HTTPS only, TLS 1.2 or newer. Cursor publishes no pinned version or checksum for
|
|
635
|
-
// this installer; docs/pr-review.md states what that leaves open.
|
|
636
|
-
" curl --proto '=https' --tlsv1.2 -fsS https://cursor.com/install | bash",
|
|
637
|
-
// The docs name ~/.cursor/bin; the installer currently links `agent` into ~/.local/bin.
|
|
638
|
-
// Both go on the PATH, so the next step finds it wherever the installer puts it.
|
|
639
|
-
' echo "$HOME/.local/bin" >> "$GITHUB_PATH"',
|
|
640
|
-
' echo "$HOME/.cursor/bin" >> "$GITHUB_PATH"'
|
|
641
|
-
].join("\n"),
|
|
642
|
-
[
|
|
643
|
-
"- name: Run Cursor review",
|
|
644
|
-
` if: ${REVIEW_GATE_CONDITION}`,
|
|
645
|
-
" env:",
|
|
646
|
-
` CURSOR_API_KEY: \${{ secrets.${SECRET_NAME2} }}`,
|
|
647
|
-
" GH_TOKEN: ${{ github.token }}",
|
|
648
|
-
" run: |",
|
|
649
|
-
// cursor-agent stops at "Workspace Trust Required" without --trust (2026.10.22 on
|
|
650
|
-
// Actions); the folder is this run's own checkout, and forks never get the secret.
|
|
651
|
-
` agent --trust -p "$(cat <<'${PROMPT_HEREDOC_DELIMITER}'`,
|
|
652
|
-
indentBlock(composePrompt2(input), 4),
|
|
653
|
-
` ${PROMPT_HEREDOC_DELIMITER}`,
|
|
654
|
-
' )"'
|
|
655
|
-
].join("\n")
|
|
656
|
-
]
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
var cursorProvider = {
|
|
660
|
-
id: "cursor",
|
|
661
|
-
workflowFileName: ".github/workflows/cursor-code-review.yml",
|
|
662
|
-
requiredSecretName: SECRET_NAME2,
|
|
663
|
-
buildWorkflowYaml: buildWorkflowYaml2
|
|
664
|
-
};
|
|
665
|
-
|
|
666
|
-
// ../template-engine/dist/review/gemini/gemini-provider.js
|
|
667
|
-
var SECRET_NAME3 = "GEMINI_API_KEY";
|
|
668
|
-
function renderWorkflowInstructions3(locale) {
|
|
669
|
-
const lines = locale === "en" ? [
|
|
670
|
-
"Review the diff of this pull request using `gh pr diff` and `gh pr view`.",
|
|
671
|
-
"Post specific findings as inline comments and exactly one top-level summary comment.",
|
|
672
|
-
`Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
|
|
673
|
-
"If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
|
|
674
|
-
"Do not modify any code, and never approve or merge the pull request.",
|
|
675
|
-
"Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
|
|
676
|
-
] : [
|
|
677
|
-
"\uC774 pull request\uC758 diff\uB97C `gh pr diff`\uC640 `gh pr view`\uB85C \uD655\uC778\uD558\uACE0 \uB9AC\uBDF0\uD558\uC138\uC694.",
|
|
678
|
-
"\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.",
|
|
679
|
-
`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.`,
|
|
680
|
-
'\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).',
|
|
681
|
-
"\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).",
|
|
682
|
-
"\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."
|
|
683
|
-
];
|
|
684
|
-
return lines.join("\n");
|
|
685
|
-
}
|
|
686
|
-
function composePrompt3(input) {
|
|
687
|
-
const mode = REVIEW_MODES[input.mode];
|
|
688
|
-
const sections = [renderWorkflowInstructions3(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
|
|
689
|
-
if (mode.includesOverEngineering) {
|
|
690
|
-
sections.push(buildOverEngineeringPrompt(input.locale));
|
|
691
|
-
}
|
|
692
|
-
sections.push(renderFindingsBlockInstruction(input.locale));
|
|
693
|
-
return sections.join("\n\n---\n\n");
|
|
694
|
-
}
|
|
695
|
-
var GEMINI_CLI_VERSION = "0.65.0";
|
|
696
|
-
function buildWorkflowYaml3(input) {
|
|
697
|
-
return renderReviewWorkflow(input, {
|
|
698
|
-
name: "Gemini Code Review",
|
|
699
|
-
secretName: SECRET_NAME3,
|
|
700
|
-
// An API key: run-gemini-cli uses OIDC only for Google Cloud workload identity federation.
|
|
701
|
-
idToken: false,
|
|
702
|
-
reviewSteps: [
|
|
703
|
-
renderCheckoutStep(REVIEW_GATE_CONDITION),
|
|
704
|
-
[
|
|
705
|
-
"- name: Run Gemini review",
|
|
706
|
-
` if: ${REVIEW_GATE_CONDITION}`,
|
|
707
|
-
` uses: ${pinnedUses("google-github-actions/run-gemini-cli")}`,
|
|
708
|
-
" env:",
|
|
709
|
-
" GITHUB_TOKEN: ${{ github.token }}",
|
|
710
|
-
// Gemini CLI 0.65 refuses to run in a folder it hasn't been told to trust (measured
|
|
711
|
-
// 2026-10-25 on Actions). The folder is this run's own checkout; pull requests from forks
|
|
712
|
-
// never get here, because they get no secret.
|
|
713
|
-
" GEMINI_CLI_TRUST_WORKSPACE: 'true'",
|
|
714
|
-
" with:",
|
|
715
|
-
` gemini_api_key: \${{ secrets.${SECRET_NAME3} }}`,
|
|
716
|
-
` gemini_cli_version: '${GEMINI_CLI_VERSION}'`,
|
|
717
|
-
" prompt: |",
|
|
718
|
-
indentBlock(composePrompt3(input), 6)
|
|
719
|
-
].join("\n")
|
|
720
|
-
]
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
var geminiProvider = {
|
|
724
|
-
id: "gemini",
|
|
725
|
-
workflowFileName: ".github/workflows/gemini-code-review.yml",
|
|
726
|
-
requiredSecretName: SECRET_NAME3,
|
|
727
|
-
buildWorkflowYaml: buildWorkflowYaml3
|
|
728
|
-
};
|
|
729
|
-
|
|
730
|
-
// ../template-engine/dist/review/gpt/gpt-provider.js
|
|
731
|
-
var SECRET_NAME4 = "OPENAI_API_KEY";
|
|
732
|
-
function renderWorkflowInstructions4(locale) {
|
|
733
|
-
const lines = locale === "en" ? [
|
|
734
|
-
"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.",
|
|
735
|
-
"Write your entire response as a single message: report specific findings, then close with one overall summary.",
|
|
736
|
-
`Always include the literal marker ${ROUND_MARKER} in your message.`,
|
|
737
|
-
"If there are no findings that require action, write exactly one message saying so (and still include the marker).",
|
|
738
|
-
"Do not modify any code, and never approve or merge the pull request.",
|
|
739
|
-
"Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
|
|
740
|
-
] : [
|
|
741
|
-
"\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).",
|
|
742
|
-
"\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.",
|
|
743
|
-
`\uBA54\uC2DC\uC9C0\uC5D0\uB294 \uBC18\uB4DC\uC2DC \uB9AC\uD130\uB7F4 \uB9C8\uCEE4 ${ROUND_MARKER}\uB97C \uD3EC\uD568\uD558\uC138\uC694.`,
|
|
744
|
-
'\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).',
|
|
745
|
-
"\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).",
|
|
746
|
-
"\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."
|
|
747
|
-
];
|
|
748
|
-
return lines.join("\n");
|
|
749
|
-
}
|
|
750
|
-
function composePrompt4(input) {
|
|
751
|
-
const mode = REVIEW_MODES[input.mode];
|
|
752
|
-
const sections = [renderWorkflowInstructions4(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
|
|
753
|
-
if (mode.includesOverEngineering) {
|
|
754
|
-
sections.push(buildOverEngineeringPrompt(input.locale));
|
|
755
|
-
}
|
|
756
|
-
sections.push(renderFindingsBlockInstruction(input.locale));
|
|
757
|
-
return sections.join("\n\n---\n\n");
|
|
758
|
-
}
|
|
759
|
-
function buildWorkflowYaml4(input) {
|
|
760
|
-
return renderReviewWorkflow(input, {
|
|
761
|
-
name: "GPT Code Review",
|
|
762
|
-
secretName: SECRET_NAME4,
|
|
763
|
-
// An API key: codex-action needs no OIDC token.
|
|
764
|
-
idToken: false,
|
|
765
|
-
reviewSteps: [
|
|
766
|
-
renderCheckoutStep(REVIEW_GATE_CONDITION, ["ref: refs/pull/${{ github.event.pull_request.number }}/merge"]),
|
|
767
|
-
[
|
|
768
|
-
"- name: Fetch base and head refs for the PR",
|
|
769
|
-
` if: ${REVIEW_GATE_CONDITION}`,
|
|
770
|
-
" env:",
|
|
771
|
-
" PR_BASE_REF: ${{ github.event.pull_request.base.ref }}",
|
|
772
|
-
" PR_NUMBER: ${{ github.event.pull_request.number }}",
|
|
773
|
-
" run: |",
|
|
774
|
-
" # Explicit destinations: the sandboxed review step has no network, so both sides",
|
|
775
|
-
" # of the diff must survive as stable local refs (FETCH_HEAD alone is ambiguous).",
|
|
776
|
-
" git fetch --no-tags origin \\",
|
|
777
|
-
' "+refs/heads/$PR_BASE_REF:refs/remotes/origin/pr-base" \\',
|
|
778
|
-
' "+refs/pull/$PR_NUMBER/head:refs/remotes/origin/pr-head"'
|
|
779
|
-
].join("\n"),
|
|
780
|
-
[
|
|
781
|
-
"- name: Run Codex review",
|
|
782
|
-
" id: codex-review",
|
|
783
|
-
` if: ${REVIEW_GATE_CONDITION}`,
|
|
784
|
-
` uses: ${pinnedUses("openai/codex-action")}`,
|
|
785
|
-
" with:",
|
|
786
|
-
` openai-api-key: \${{ secrets.${SECRET_NAME4} }}`,
|
|
787
|
-
' permission-profile: ":read-only"',
|
|
788
|
-
" prompt: |",
|
|
789
|
-
indentBlock(composePrompt4(input), 6)
|
|
790
|
-
].join("\n"),
|
|
791
|
-
[
|
|
792
|
-
"- name: Post review comment",
|
|
793
|
-
` if: ${REVIEW_GATE_CONDITION}`,
|
|
794
|
-
" env:",
|
|
795
|
-
" GH_TOKEN: ${{ github.token }}",
|
|
796
|
-
" PR_NUMBER: ${{ github.event.pull_request.number }}",
|
|
797
|
-
" REVIEW_BODY: ${{ steps.codex-review.outputs.final-message }}",
|
|
798
|
-
" run: |",
|
|
799
|
-
' printf %s "$REVIEW_BODY" | gh pr comment "$PR_NUMBER" --body-file -'
|
|
800
|
-
].join("\n")
|
|
801
|
-
]
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
var gptProvider = {
|
|
805
|
-
id: "gpt",
|
|
806
|
-
workflowFileName: ".github/workflows/gpt-code-review.yml",
|
|
807
|
-
requiredSecretName: SECRET_NAME4,
|
|
808
|
-
buildWorkflowYaml: buildWorkflowYaml4
|
|
809
|
-
};
|
|
810
|
-
|
|
811
|
-
// ../template-engine/dist/review/provider.js
|
|
812
|
-
var REVIEW_PROVIDERS = {
|
|
813
|
-
claude: claudeProvider,
|
|
814
|
-
gemini: geminiProvider,
|
|
815
|
-
gpt: gptProvider,
|
|
816
|
-
cursor: cursorProvider
|
|
817
|
-
};
|
|
818
|
-
|
|
819
|
-
// ../template-engine/dist/review/compose-review-setup.js
|
|
820
|
-
function composeReviewSetup(input) {
|
|
821
|
-
if (!input.enabled)
|
|
822
|
-
return null;
|
|
823
|
-
const provider = REVIEW_PROVIDERS[input.provider];
|
|
824
|
-
return {
|
|
825
|
-
workflowFilePath: provider.workflowFileName,
|
|
826
|
-
workflowYaml: provider.buildWorkflowYaml(input),
|
|
827
|
-
requiredSecretName: provider.requiredSecretName,
|
|
828
|
-
configSnippet: buildReviewConfigSnippet(input)
|
|
829
|
-
};
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// ../template-engine/dist/review/conflict.js
|
|
833
|
-
function judgeReviewFileConflict(existing, generated) {
|
|
834
|
-
if (existing === null)
|
|
835
|
-
return { kind: "create" };
|
|
836
|
-
if (existing === generated)
|
|
837
|
-
return { kind: "unchanged" };
|
|
838
|
-
return { kind: "merge-proposal", existing, generated };
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
// src/generators/shared/dry-run.ts
|
|
842
|
-
function resolveDryRun(options) {
|
|
843
|
-
return process.env["NX_DRY_RUN"] === "true" || (options.dryRun ?? false);
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// src/generators/shared/template-files.ts
|
|
847
|
-
import { existsSync } from "node:fs";
|
|
848
|
-
import { fileURLToPath } from "node:url";
|
|
849
|
-
import { generateFiles } from "@nx/devkit";
|
|
850
|
-
import { createTree } from "@nx/devkit/testing";
|
|
851
|
-
function generatorFilesDir(generator, fromUrl) {
|
|
852
|
-
for (const relative of [`../${generator}/files`, `./generators/${generator}/files`]) {
|
|
853
|
-
const dir = fileURLToPath(new URL(relative, fromUrl));
|
|
854
|
-
if (existsSync(dir)) return dir;
|
|
855
|
-
}
|
|
856
|
-
throw new Error(`The "${generator}" generator's template files are missing next to ${fileURLToPath(fromUrl)}`);
|
|
857
|
-
}
|
|
858
|
-
function renderTemplateFiles(srcFolder, target, substitutions) {
|
|
859
|
-
const scratch = createTree();
|
|
860
|
-
const seeded = new Set(scratch.listChanges().map((change) => change.path));
|
|
861
|
-
generateFiles(scratch, srcFolder, target, substitutions);
|
|
862
|
-
return scratch.listChanges().flatMap(
|
|
863
|
-
(change) => change.type === "CREATE" && change.content !== null && !seeded.has(change.path) ? [{ filePath: change.path, content: change.content.toString("utf-8") }] : []
|
|
864
|
-
).sort((a, b) => a.filePath.localeCompare(b.filePath));
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
// src/generators/shared/json-edit.ts
|
|
868
|
-
import { findNodeAtLocation, parseTree } from "jsonc-parser";
|
|
869
|
-
function detectIndent(raw) {
|
|
870
|
-
for (const line of raw.split("\n")) {
|
|
871
|
-
const match = /^(\t+| +)/.exec(line);
|
|
872
|
-
if (match) return match[1];
|
|
873
|
-
}
|
|
874
|
-
return " ";
|
|
875
|
-
}
|
|
876
|
-
function styleOf(raw) {
|
|
877
|
-
return { eol: raw.includes("\r\n") ? "\r\n" : "\n", unit: detectIndent(raw) };
|
|
878
|
-
}
|
|
879
|
-
function lineIndentAt(raw, offset) {
|
|
880
|
-
const lineStart = raw.lastIndexOf("\n", offset - 1) + 1;
|
|
881
|
-
return /^[ \t]*/.exec(raw.slice(lineStart))?.[0] ?? "";
|
|
882
|
-
}
|
|
883
|
-
function isMultiline(raw, node) {
|
|
884
|
-
return raw.slice(node.offset, node.offset + node.length).includes("\n");
|
|
885
|
-
}
|
|
886
|
-
function isPrimitive(value) {
|
|
887
|
-
return value === null || ["string", "number", "boolean"].includes(typeof value);
|
|
888
|
-
}
|
|
889
|
-
function renderValue(value, indent, style) {
|
|
890
|
-
if (isPrimitive(value)) return JSON.stringify(value);
|
|
891
|
-
if (Array.isArray(value)) {
|
|
892
|
-
if (value.every(isPrimitive)) return `[${value.map((item) => JSON.stringify(item)).join(", ")}]`;
|
|
893
|
-
const inner2 = `${indent}${style.unit}`;
|
|
894
|
-
return `[${style.eol}${value.map((item) => `${inner2}${renderValue(item, inner2, style)}`).join(`,${style.eol}`)}${style.eol}${indent}]`;
|
|
895
|
-
}
|
|
896
|
-
const entries = Object.entries(value);
|
|
897
|
-
if (entries.length === 0) return "{}";
|
|
898
|
-
const inner = `${indent}${style.unit}`;
|
|
899
|
-
const body = entries.map(([key, item]) => `${inner}${JSON.stringify(key)}: ${renderValue(item, inner, style)}`).join(`,${style.eol}`);
|
|
900
|
-
return `{${style.eol}${body}${style.eol}${indent}}`;
|
|
901
|
-
}
|
|
902
|
-
function splice(raw, start, end, text) {
|
|
903
|
-
return raw.slice(0, start) + text + raw.slice(end);
|
|
904
|
-
}
|
|
905
|
-
function nest(keys, value) {
|
|
906
|
-
return keys.reduceRight((inner, key) => ({ [key]: inner }), value);
|
|
907
|
-
}
|
|
908
|
-
function insertProperty(raw, object, key, value, style) {
|
|
909
|
-
const properties = object.children ?? [];
|
|
910
|
-
const objectIndent = lineIndentAt(raw, object.offset);
|
|
911
|
-
if (properties.length === 0) {
|
|
912
|
-
const inner = `${objectIndent}${style.unit}`;
|
|
913
|
-
const text = `{${style.eol}${inner}${JSON.stringify(String(key))}: ${renderValue(value, inner, style)}${style.eol}${objectIndent}}`;
|
|
914
|
-
return splice(raw, object.offset, object.offset + object.length, text);
|
|
915
|
-
}
|
|
916
|
-
const last = properties[properties.length - 1];
|
|
917
|
-
const end = last.offset + last.length;
|
|
918
|
-
if (!isMultiline(raw, object)) {
|
|
919
|
-
return splice(raw, end, end, `, ${JSON.stringify(String(key))}: ${renderValue(value, "", style)}`);
|
|
920
|
-
}
|
|
921
|
-
const indent = lineIndentAt(raw, last.offset);
|
|
922
|
-
return splice(raw, end, end, `,${style.eol}${indent}${JSON.stringify(String(key))}: ${renderValue(value, indent, style)}`);
|
|
923
|
-
}
|
|
924
|
-
function setJsonValue(raw, path, value) {
|
|
925
|
-
const style = styleOf(raw);
|
|
926
|
-
const root = parseTree(raw);
|
|
927
|
-
if (root === void 0) throw new Error("setJsonValue: not a JSON document");
|
|
928
|
-
const existing = findNodeAtLocation(root, path);
|
|
929
|
-
if (existing !== void 0) {
|
|
930
|
-
return splice(raw, existing.offset, existing.offset + existing.length, renderValue(value, lineIndentAt(raw, existing.offset), style));
|
|
931
|
-
}
|
|
932
|
-
for (let depth = path.length - 1; depth >= 0; depth--) {
|
|
933
|
-
const parent = depth === 0 ? root : findNodeAtLocation(root, path.slice(0, depth));
|
|
934
|
-
if (parent === void 0) continue;
|
|
935
|
-
if (parent.type !== "object") throw new Error(`setJsonValue: ${path.slice(0, depth).join(".") || "the root"} is not an object`);
|
|
936
|
-
return insertProperty(raw, parent, path[depth], nest(path.slice(depth + 1), value), style);
|
|
937
|
-
}
|
|
938
|
-
throw new Error("setJsonValue: unreachable");
|
|
939
|
-
}
|
|
940
|
-
function applyJsonEdits(raw, edits) {
|
|
941
|
-
if (raw === null) {
|
|
942
|
-
let text = "{}\n";
|
|
943
|
-
for (const [path, value] of edits) text = setJsonValue(text, path, value);
|
|
944
|
-
return text;
|
|
945
|
-
}
|
|
946
|
-
return edits.reduce((text, [path, value]) => setJsonValue(text, path, value), raw);
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
// src/generators/shared/toolkit-config-file.ts
|
|
950
|
-
function recordedChoice(config, key, allowed) {
|
|
951
|
-
const value = config?.[key];
|
|
952
|
-
return typeof value === "string" && allowed.includes(value) ? value : void 0;
|
|
953
|
-
}
|
|
954
|
-
var RECORDED_PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
|
|
955
|
-
function workspacePackageManager(exists, read, config) {
|
|
956
|
-
return detectWorkspacePackageManager(exists, read) ?? recordedChoice(config, "packageManager", RECORDED_PACKAGE_MANAGERS) ?? null;
|
|
957
|
-
}
|
|
958
|
-
function treePackageManager(tree, config) {
|
|
959
|
-
return workspacePackageManager((p) => tree.exists(p), (p) => tree.read(p, "utf-8"), config);
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
// src/generators/add-review/render.ts
|
|
963
|
-
var DEPENDABOT_PATH = ".github/dependabot.yml";
|
|
964
|
-
var PROVIDER_STATUS_URL = "https://github.com/clearlydev/nx-archweave/blob/main/docs/pr-review.md#provider-status";
|
|
965
|
-
function renderLineDiff(existing, generated) {
|
|
966
|
-
const a = existing.split("\n");
|
|
967
|
-
const b = generated.split("\n");
|
|
968
|
-
const m = a.length;
|
|
969
|
-
const n = b.length;
|
|
970
|
-
const lcs = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
|
|
971
|
-
for (let i2 = m - 1; i2 >= 0; i2--) {
|
|
972
|
-
for (let j2 = n - 1; j2 >= 0; j2--) {
|
|
973
|
-
lcs[i2][j2] = a[i2] === b[j2] ? lcs[i2 + 1][j2 + 1] + 1 : Math.max(lcs[i2 + 1][j2], lcs[i2][j2 + 1]);
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
const out = ["--- existing", "+++ generated"];
|
|
977
|
-
let i = 0;
|
|
978
|
-
let j = 0;
|
|
979
|
-
while (i < m && j < n) {
|
|
980
|
-
if (a[i] === b[j]) {
|
|
981
|
-
out.push(` ${a[i]}`);
|
|
982
|
-
i++;
|
|
983
|
-
j++;
|
|
984
|
-
} else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
|
|
985
|
-
out.push(`- ${a[i]}`);
|
|
986
|
-
i++;
|
|
987
|
-
} else {
|
|
988
|
-
out.push(`+ ${b[j]}`);
|
|
989
|
-
j++;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
while (i < m) out.push(`- ${a[i++]}`);
|
|
993
|
-
while (j < n) out.push(`+ ${b[j++]}`);
|
|
994
|
-
return out.join("\n");
|
|
995
|
-
}
|
|
996
|
-
var WORKFLOW_ACTION_LINES = {
|
|
997
|
-
created: "created",
|
|
998
|
-
overwritten: "overwritten with the generated version (per --onConflict=overwrite)",
|
|
999
|
-
unchanged: "unchanged (already matches the generated version)",
|
|
1000
|
-
proposed: "NOT written \u2014 differs from the generated version (merge proposal below)",
|
|
1001
|
-
skipped: "left as-is (per --onConflict=skip)",
|
|
1002
|
-
alternate: "generated alongside the existing file",
|
|
1003
|
-
aborted: "NOT written"
|
|
1004
|
-
};
|
|
1005
|
-
var CONFIG_ACTION_LINES = {
|
|
1006
|
-
created: "created with the review field",
|
|
1007
|
-
updated: "review field updated",
|
|
1008
|
-
unchanged: "review field unchanged",
|
|
1009
|
-
"not-updated": "NOT updated \u2014 kept consistent with the workflow file that is actually in effect",
|
|
1010
|
-
aborted: "NOT updated"
|
|
1011
|
-
};
|
|
1012
|
-
function renderAddReviewReport(report) {
|
|
1013
|
-
const lines = [];
|
|
1014
|
-
if (report.packageManagerNote !== void 0) {
|
|
1015
|
-
lines.push(`Note: ${report.packageManagerNote}`);
|
|
1016
|
-
}
|
|
1017
|
-
if (report.workflowAction === "aborted") {
|
|
1018
|
-
lines.push("add-review aborted \u2014 nothing was written.");
|
|
1019
|
-
lines.push(` toolkit.config.json: ${report.configError ?? "unusable"}`);
|
|
1020
|
-
lines.push(" Fix the config file manually, then rerun the generator.");
|
|
1021
|
-
return lines.join("\n");
|
|
1022
|
-
}
|
|
1023
|
-
if (report.dryRun) {
|
|
1024
|
-
lines.push("add-review dry-run \u2014 no files were written.");
|
|
1025
|
-
lines.push("");
|
|
1026
|
-
lines.push("Generated workflow YAML:");
|
|
1027
|
-
lines.push(report.workflowYaml ?? "");
|
|
1028
|
-
lines.push("");
|
|
1029
|
-
lines.push("toolkit.config.json review field:");
|
|
1030
|
-
lines.push(report.configSnippetJson ?? "");
|
|
1031
|
-
lines.push("");
|
|
1032
|
-
lines.push("Effective decision:");
|
|
1033
|
-
}
|
|
1034
|
-
lines.push(` ${report.workflowPath}: ${WORKFLOW_ACTION_LINES[report.workflowAction]}`);
|
|
1035
|
-
lines.push(` toolkit.config.json: ${CONFIG_ACTION_LINES[report.configAction]}`);
|
|
1036
|
-
if (report.workflowAction === "proposed" && report.diff) {
|
|
1037
|
-
lines.push("");
|
|
1038
|
-
lines.push(report.diff);
|
|
1039
|
-
lines.push("");
|
|
1040
|
-
lines.push("To resolve, rerun with one of:");
|
|
1041
|
-
lines.push(" --onConflict=overwrite replace the existing file with the generated version");
|
|
1042
|
-
lines.push(" --onConflict=skip keep the existing file");
|
|
1043
|
-
lines.push(" --onConflict=alternate-filename write the generated version alongside it");
|
|
1044
|
-
}
|
|
1045
|
-
if (report.dependabot === "created") {
|
|
1046
|
-
lines.push(` ${DEPENDABOT_PATH}: created \u2014 Dependabot proposes newer pins for the workflow's actions`);
|
|
1047
|
-
} else if (report.dependabot === "present") {
|
|
1048
|
-
lines.push(` ${DEPENDABOT_PATH}: already updates github-actions`);
|
|
1049
|
-
} else if (report.dependabot === "proposed") {
|
|
1050
|
-
lines.push(` ${DEPENDABOT_PATH}: NOT changed \u2014 it has no github-actions entry. The workflow pins each action`);
|
|
1051
|
-
lines.push(" to a commit SHA; add this entry under `updates:` so those pins get update pull requests:");
|
|
1052
|
-
lines.push("");
|
|
1053
|
-
lines.push(report.dependabotProposal ?? "");
|
|
1054
|
-
}
|
|
1055
|
-
if (report.workflowAction === "alternate") {
|
|
1056
|
-
lines.push("");
|
|
1057
|
-
lines.push(" Review the generated file, then replace the main workflow file with it yourself;");
|
|
1058
|
-
lines.push(" the config stays pointing at the workflow that is actually in effect until you do.");
|
|
1059
|
-
}
|
|
1060
|
-
lines.push("");
|
|
1061
|
-
lines.push(`Note: the review workflow stays green-skipped until you register the ${report.requiredSecretName} secret.`);
|
|
1062
|
-
if (report.betaProvider !== void 0) {
|
|
1063
|
-
lines.push(`Note: the ${report.betaProvider} provider is in beta \u2014 its workflow hasn't finished a review end to end on`);
|
|
1064
|
-
lines.push(`GitHub Actions yet. Status, costs and known issues: ${PROVIDER_STATUS_URL}`);
|
|
1065
|
-
}
|
|
1066
|
-
lines.push("Note: this workflow runs with elevated permissions only after it is merged to the default branch \u2014");
|
|
1067
|
-
lines.push("merging is the first real validation.");
|
|
1068
|
-
return lines.join("\n");
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
// src/generators/add-review/generator.ts
|
|
1072
|
-
var CONFIG_PATH = "toolkit.config.json";
|
|
1073
|
-
function deriveAltWorkflowPath(mainWorkflowPath) {
|
|
1074
|
-
return mainWorkflowPath.replace(/\.yml$/, ".archweave.yml");
|
|
1075
|
-
}
|
|
1076
|
-
function readConfig(tree) {
|
|
1077
|
-
const raw = tree.read(CONFIG_PATH, "utf-8");
|
|
1078
|
-
if (raw === null) return { raw: null, parsed: null, error: null };
|
|
1079
|
-
try {
|
|
1080
|
-
const parsed = JSON.parse(raw);
|
|
1081
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1082
|
-
return { raw, parsed: null, error: `${CONFIG_PATH} must contain a top-level JSON object` };
|
|
1083
|
-
}
|
|
1084
|
-
return { raw, parsed, error: null };
|
|
1085
|
-
} catch {
|
|
1086
|
-
return { raw, parsed: null, error: `${CONFIG_PATH} is not valid JSON` };
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
function resolveLocale(option, config) {
|
|
1090
|
-
if (option) return option;
|
|
1091
|
-
const fromConfig = config.parsed?.["locale"];
|
|
1092
|
-
return fromConfig === "ko" || fromConfig === "en" ? fromConfig : "en";
|
|
1093
|
-
}
|
|
1094
|
-
function deepEqual(a, b) {
|
|
1095
|
-
if (a === b) return true;
|
|
1096
|
-
if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
|
|
1097
|
-
if (Array.isArray(a) || Array.isArray(b)) {
|
|
1098
|
-
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((value, index) => deepEqual(value, b[index]));
|
|
1099
|
-
}
|
|
1100
|
-
const aKeys = Object.keys(a).sort();
|
|
1101
|
-
const bKeys = Object.keys(b).sort();
|
|
1102
|
-
if (aKeys.length !== bKeys.length || aKeys.some((key, index) => key !== bKeys[index])) return false;
|
|
1103
|
-
return aKeys.every((key) => deepEqual(a[key], b[key]));
|
|
1104
|
-
}
|
|
1105
|
-
var PROVIDERS = ["claude", "gemini", "gpt", "cursor"];
|
|
1106
|
-
var BETA_PROVIDERS = ["gemini", "gpt", "cursor"];
|
|
1107
|
-
var MODES = ["fast", "standard", "thorough"];
|
|
1108
|
-
function recordedReview(config) {
|
|
1109
|
-
const review = config?.["review"];
|
|
1110
|
-
if (typeof review !== "object" || review === null || Array.isArray(review)) return {};
|
|
1111
|
-
const record = review;
|
|
1112
|
-
const pick = (value, allowed) => typeof value === "string" && allowed.includes(value) ? value : void 0;
|
|
1113
|
-
const rounds = record["maxRounds"];
|
|
1114
|
-
return {
|
|
1115
|
-
provider: pick(record["provider"], PROVIDERS),
|
|
1116
|
-
mode: pick(record["mode"], MODES),
|
|
1117
|
-
maxRounds: rounds === 1 || rounds === 2 || rounds === 3 || rounds === 4 || rounds === 5 ? rounds : void 0
|
|
1118
|
-
};
|
|
1119
|
-
}
|
|
1120
|
-
function runAddReview(tree, options) {
|
|
1121
|
-
const config = readConfig(tree);
|
|
1122
|
-
const detectedPm = treePackageManager(tree, config.parsed);
|
|
1123
|
-
const packageManager = options.packageManager ?? detectedPm ?? "pnpm";
|
|
1124
|
-
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;
|
|
1125
|
-
const recorded = recordedReview(config.parsed);
|
|
1126
|
-
const provider = options.provider ?? recorded.provider ?? "claude";
|
|
1127
|
-
const plan = composeReviewSetup({
|
|
1128
|
-
enabled: true,
|
|
1129
|
-
provider,
|
|
1130
|
-
mode: options.mode ?? recorded.mode ?? "standard",
|
|
1131
|
-
locale: resolveLocale(options.locale, config),
|
|
1132
|
-
maxRounds: options.maxRounds ?? recorded.maxRounds ?? 3,
|
|
1133
|
-
packageManager
|
|
1134
|
-
});
|
|
1135
|
-
if (plan === null) throw new Error("unreachable: add-review always composes with enabled: true");
|
|
1136
|
-
if (config.error !== null) {
|
|
1137
|
-
return {
|
|
1138
|
-
dryRun: options.dryRun ?? false,
|
|
1139
|
-
workflowPath: plan.workflowFilePath,
|
|
1140
|
-
workflowAction: "aborted",
|
|
1141
|
-
configAction: "aborted",
|
|
1142
|
-
requiredSecretName: plan.requiredSecretName,
|
|
1143
|
-
diff: null,
|
|
1144
|
-
workflowYaml: null,
|
|
1145
|
-
configSnippetJson: null,
|
|
1146
|
-
configError: config.error,
|
|
1147
|
-
dependabot: "not-applied",
|
|
1148
|
-
dependabotProposal: null
|
|
1149
|
-
};
|
|
1150
|
-
}
|
|
1151
|
-
const existing = tree.read(plan.workflowFilePath, "utf-8");
|
|
1152
|
-
const verdict = judgeReviewFileConflict(existing, plan.workflowYaml);
|
|
1153
|
-
let workflowAction;
|
|
1154
|
-
let workflowPath = plan.workflowFilePath;
|
|
1155
|
-
let writeWorkflowTo = null;
|
|
1156
|
-
let diff = null;
|
|
1157
|
-
let mainFileMatchesOptions;
|
|
1158
|
-
if (verdict.kind === "create") {
|
|
1159
|
-
workflowAction = "created";
|
|
1160
|
-
writeWorkflowTo = plan.workflowFilePath;
|
|
1161
|
-
mainFileMatchesOptions = true;
|
|
1162
|
-
} else if (verdict.kind === "unchanged") {
|
|
1163
|
-
workflowAction = "unchanged";
|
|
1164
|
-
mainFileMatchesOptions = true;
|
|
1165
|
-
} else {
|
|
1166
|
-
switch (options.onConflict) {
|
|
1167
|
-
case "overwrite":
|
|
1168
|
-
workflowAction = "overwritten";
|
|
1169
|
-
writeWorkflowTo = plan.workflowFilePath;
|
|
1170
|
-
mainFileMatchesOptions = true;
|
|
1171
|
-
break;
|
|
1172
|
-
case "skip":
|
|
1173
|
-
workflowAction = "skipped";
|
|
1174
|
-
mainFileMatchesOptions = false;
|
|
1175
|
-
break;
|
|
1176
|
-
case "alternate-filename": {
|
|
1177
|
-
const altWorkflowPath = deriveAltWorkflowPath(plan.workflowFilePath);
|
|
1178
|
-
workflowAction = "alternate";
|
|
1179
|
-
workflowPath = altWorkflowPath;
|
|
1180
|
-
writeWorkflowTo = altWorkflowPath;
|
|
1181
|
-
mainFileMatchesOptions = false;
|
|
1182
|
-
break;
|
|
1183
|
-
}
|
|
1184
|
-
default:
|
|
1185
|
-
workflowAction = "proposed";
|
|
1186
|
-
diff = renderLineDiff(verdict.existing, verdict.generated);
|
|
1187
|
-
mainFileMatchesOptions = false;
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
let configAction;
|
|
1191
|
-
let newConfigContent = null;
|
|
1192
|
-
if (!mainFileMatchesOptions) {
|
|
1193
|
-
configAction = "not-updated";
|
|
1194
|
-
} else {
|
|
1195
|
-
const existingReview = config.parsed?.["review"];
|
|
1196
|
-
if (deepEqual(existingReview, plan.configSnippet)) {
|
|
1197
|
-
configAction = "unchanged";
|
|
1198
|
-
} else {
|
|
1199
|
-
configAction = config.raw === null ? "created" : "updated";
|
|
1200
|
-
newConfigContent = applyJsonEdits(config.raw, [[["review"], plan.configSnippet]]);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
const [dependabotFile] = renderTemplateFiles(generatorFilesDir("add-review", import.meta.url), ".github", {});
|
|
1204
|
-
const existingDependabot = tree.read(DEPENDABOT_PATH, "utf-8");
|
|
1205
|
-
const dependabot = existingDependabot === null ? "created" : /github-actions/.test(existingDependabot) ? "present" : "proposed";
|
|
1206
|
-
const dryRun = options.dryRun ?? false;
|
|
1207
|
-
if (!dryRun) {
|
|
1208
|
-
if (writeWorkflowTo !== null) tree.write(writeWorkflowTo, plan.workflowYaml);
|
|
1209
|
-
if (newConfigContent !== null) tree.write(CONFIG_PATH, newConfigContent);
|
|
1210
|
-
if (dependabot === "created") tree.write(DEPENDABOT_PATH, dependabotFile.content);
|
|
1211
|
-
}
|
|
1212
|
-
return {
|
|
1213
|
-
dryRun,
|
|
1214
|
-
workflowPath,
|
|
1215
|
-
workflowAction,
|
|
1216
|
-
configAction,
|
|
1217
|
-
requiredSecretName: plan.requiredSecretName,
|
|
1218
|
-
diff,
|
|
1219
|
-
workflowYaml: dryRun ? plan.workflowYaml : null,
|
|
1220
|
-
configSnippetJson: dryRun ? JSON.stringify(plan.configSnippet, null, 2) : null,
|
|
1221
|
-
configError: null,
|
|
1222
|
-
dependabot,
|
|
1223
|
-
dependabotProposal: dependabot === "proposed" ? dependabotFile.content : null,
|
|
1224
|
-
...packageManagerNote !== void 0 ? { packageManagerNote } : {},
|
|
1225
|
-
...BETA_PROVIDERS.includes(provider) ? { betaProvider: provider } : {}
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
async function addReviewGenerator(tree, options) {
|
|
1229
|
-
console.log(renderAddReviewReport(runAddReview(tree, { ...options, dryRun: resolveDryRun(options) })));
|
|
1230
|
-
}
|
|
1231
|
-
var generator_default = addReviewGenerator;
|
|
1
|
+
import {
|
|
2
|
+
addReviewGenerator,
|
|
3
|
+
generator_default,
|
|
4
|
+
recordedReview,
|
|
5
|
+
runAddReview
|
|
6
|
+
} from "../_chunks/chunk-JHRTVURZ.js";
|
|
7
|
+
import "../_chunks/chunk-67INEVWL.js";
|
|
8
|
+
import "../_chunks/chunk-3A6RZUCZ.js";
|
|
9
|
+
import "../_chunks/chunk-YNMLB4DL.js";
|
|
10
|
+
import "../_chunks/chunk-TUN34LAQ.js";
|
|
11
|
+
import "../_chunks/chunk-EXYZLL7A.js";
|
|
12
|
+
import "../_chunks/chunk-FQHFT5IH.js";
|
|
1232
13
|
export {
|
|
1233
14
|
addReviewGenerator,
|
|
1234
15
|
generator_default as default,
|