@clearlydev/nx-archweave 0.3.2 → 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.
Files changed (46) hide show
  1. package/dist/bin/review-verdict.js +5 -248
  2. package/dist/eslint/index.js +28 -321
  3. package/dist/generators/_chunks/chunk-2WSX5YNB.js +274 -0
  4. package/dist/generators/_chunks/chunk-34V6HAJT.js +55 -0
  5. package/dist/generators/_chunks/chunk-3A6RZUCZ.js +114 -0
  6. package/dist/generators/_chunks/chunk-47T7NOPP.js +30 -0
  7. package/dist/generators/_chunks/chunk-5MI3O4VZ.js +99 -0
  8. package/dist/generators/_chunks/chunk-67INEVWL.js +1068 -0
  9. package/dist/generators/_chunks/chunk-6ZTIUBB2.js +139 -0
  10. package/dist/generators/_chunks/chunk-AIJCJ7JK.js +203 -0
  11. package/dist/generators/_chunks/chunk-AQHV7DJS.js +656 -0
  12. package/dist/generators/_chunks/chunk-C2VVUWBA.js +386 -0
  13. package/dist/generators/_chunks/chunk-EXYZLL7A.js +234 -0
  14. package/dist/generators/_chunks/chunk-FQHFT5IH.js +50 -0
  15. package/dist/generators/_chunks/chunk-H222FPLK.js +80 -0
  16. package/dist/generators/_chunks/chunk-IIDOBI42.js +616 -0
  17. package/dist/generators/_chunks/chunk-JHRTVURZ.js +189 -0
  18. package/dist/generators/_chunks/chunk-L2RZ2LBO.js +132 -0
  19. package/dist/generators/_chunks/chunk-MQNQJOWA.js +217 -0
  20. package/dist/generators/_chunks/chunk-QQNDYJTR.js +243 -0
  21. package/dist/generators/_chunks/chunk-SFXU6IUS.js +0 -0
  22. package/dist/generators/_chunks/chunk-SNBIVIMI.js +89 -0
  23. package/dist/generators/_chunks/chunk-TUN34LAQ.js +8 -0
  24. package/dist/generators/_chunks/chunk-TXAQ2GJC.js +20 -0
  25. package/dist/generators/_chunks/chunk-U6LAFHSF.js +49 -0
  26. package/dist/generators/_chunks/chunk-URV6FLUH.js +73 -0
  27. package/dist/generators/_chunks/chunk-YNMLB4DL.js +25 -0
  28. package/dist/generators/_chunks/chunk-YY2UESLM.js +282 -0
  29. package/dist/generators/add-agent-rules/generator.js +11 -842
  30. package/dist/generators/add-backend/generator.js +11 -906
  31. package/dist/generators/add-fsd-lite/generator.js +12 -801
  32. package/dist/generators/add-github-templates/generator.js +13 -116
  33. package/dist/generators/add-integration/generator.js +15 -743
  34. package/dist/generators/add-review/generator.js +12 -1231
  35. package/dist/generators/allow-builds/generator.js +7 -71
  36. package/dist/generators/doctor/generator.js +74 -2042
  37. package/dist/generators/entity/generator.js +15 -604
  38. package/dist/generators/feature/generator.js +15 -604
  39. package/dist/generators/init/generator.js +68 -3376
  40. package/dist/generators/install-integration/generator.js +35 -871
  41. package/dist/generators/page/generator.js +15 -604
  42. package/dist/generators/record-integration/generator.js +12 -778
  43. package/dist/generators/shared/generator.js +15 -604
  44. package/dist/generators/widget/generator.js +15 -604
  45. package/dist/index.js +5 -663
  46. package/package.json +1 -1
@@ -1,3 +1,59 @@
1
+ import {
2
+ loadManifest
3
+ } from "../_chunks/chunk-U6LAFHSF.js";
4
+ import {
5
+ detectUnitTestRunner
6
+ } from "../_chunks/chunk-AIJCJ7JK.js";
7
+ import "../_chunks/chunk-SFXU6IUS.js";
8
+ import {
9
+ REVIEW_MODES,
10
+ REVIEW_PROVIDERS,
11
+ ROUND_MARKER,
12
+ judgeReviewFileConflict
13
+ } from "../_chunks/chunk-67INEVWL.js";
14
+ import {
15
+ createGroupDetector,
16
+ indexFilePattern
17
+ } from "../_chunks/chunk-MQNQJOWA.js";
18
+ import {
19
+ resolveFsdPolicy
20
+ } from "../_chunks/chunk-5MI3O4VZ.js";
21
+ import {
22
+ readSharedTypesConfig
23
+ } from "../_chunks/chunk-QQNDYJTR.js";
24
+ import {
25
+ classifyFsdEslintConfig
26
+ } from "../_chunks/chunk-L2RZ2LBO.js";
27
+ import {
28
+ resolveFrontendApp,
29
+ sourceRootOf
30
+ } from "../_chunks/chunk-2WSX5YNB.js";
31
+ import "../_chunks/chunk-TXAQ2GJC.js";
32
+ import {
33
+ ESLINT_CONFIG_CANDIDATES,
34
+ classifyBoundaryConstraints,
35
+ maskComments,
36
+ readBoundaryTags
37
+ } from "../_chunks/chunk-6ZTIUBB2.js";
38
+ import "../_chunks/chunk-YNMLB4DL.js";
39
+ import {
40
+ diskRunNx
41
+ } from "../_chunks/chunk-47T7NOPP.js";
42
+ import {
43
+ PLUGIN_CATALOG,
44
+ createIntegrationsCompositionRoot,
45
+ detectWorkspacePackage,
46
+ verifyClaudeMarketplacePlugin
47
+ } from "../_chunks/chunk-AQHV7DJS.js";
48
+ import "../_chunks/chunk-34V6HAJT.js";
49
+ import {
50
+ readToolkitConfig,
51
+ recordedFrontends
52
+ } from "../_chunks/chunk-EXYZLL7A.js";
53
+ import {
54
+ detectWorkspacePackageManager
55
+ } from "../_chunks/chunk-FQHFT5IH.js";
56
+
1
57
  // ../shared/dist/lib/doctor-report.js
2
58
  function createDoctorCheck(input) {
3
59
  return { suggestedAction: void 0, ...input };
@@ -34,58 +90,6 @@ function serializeDoctorReport(report) {
34
90
  `;
35
91
  }
36
92
 
37
- // ../shared/dist/lib/spawnable-command.js
38
- import { statSync } from "node:fs";
39
- import { win32 } from "node:path";
40
- function isFile(path) {
41
- try {
42
- return statSync(path).isFile();
43
- } catch {
44
- return false;
45
- }
46
- }
47
- var DEFAULT_ENV = { platform: process.platform, env: process.env, isFile };
48
- function envValue(env, name) {
49
- const key = Object.keys(env).find((candidate) => candidate.toUpperCase() === name);
50
- return key === void 0 ? void 0 : env[key];
51
- }
52
- function resolveWindowsCommand(command, environment) {
53
- const exts = (envValue(environment.env, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
54
- const candidates = (base) => win32.extname(base) !== "" ? [base] : exts.map((ext) => base + ext);
55
- if (win32.isAbsolute(command) || /[\\/]/.test(command)) {
56
- return candidates(command).find(environment.isFile);
57
- }
58
- const dirs = (envValue(environment.env, "PATH") ?? "").split(";").filter(Boolean);
59
- for (const dir of dirs) {
60
- const found = candidates(win32.join(dir, command)).find(environment.isFile);
61
- if (found !== void 0)
62
- return found;
63
- }
64
- return void 0;
65
- }
66
- var META_CHARS = /([()\][%!^"`<>&|;, *?])/g;
67
- var BIN_SHIM = /node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/i;
68
- function escapeCmdArgument(arg, doubleEscape = false) {
69
- let quoted = arg.replace(/(\\*)"/g, '$1$1\\"');
70
- quoted = quoted.replace(/(\\*)$/, "$1$1");
71
- const escaped = `"${quoted}"`.replace(META_CHARS, "^$1");
72
- return doubleEscape ? escaped.replace(META_CHARS, "^$1") : escaped;
73
- }
74
- function spawnableCommand(command, args, environment = DEFAULT_ENV) {
75
- if (environment.platform !== "win32")
76
- return { command, args: [...args] };
77
- const resolved = resolveWindowsCommand(command, environment);
78
- if (resolved === void 0 || !/\.(cmd|bat)$/i.test(resolved))
79
- return { command, args: [...args] };
80
- const doubleEscape = BIN_SHIM.test(resolved);
81
- const line = [resolved.replace(META_CHARS, "^$1"), ...args.map((arg) => escapeCmdArgument(arg, doubleEscape))].join(" ");
82
- return {
83
- command: envValue(environment.env, "COMSPEC") ?? "cmd.exe",
84
- args: ["/d", "/s", "/c", `"${line}"`],
85
- windowsVerbatimArguments: true
86
- };
87
- }
88
-
89
93
  // src/generators/doctor/render.ts
90
94
  function renderDoctorReport(report) {
91
95
  const lines = [`Overall status: ${report.overallStatus}`, ""];
@@ -101,45 +105,6 @@ function renderDoctorReport(report) {
101
105
  // ../template-engine/dist/doctor/detect-environment.js
102
106
  import { existsSync, readFileSync } from "node:fs";
103
107
  import { join } from "node:path";
104
-
105
- // ../template-engine/dist/environment/package-manager.js
106
- var LOCKFILES = [
107
- ["yarn", ["yarn.lock"]],
108
- ["bun", ["bun.lock", "bun.lockb"]],
109
- ["npm", ["package-lock.json"]]
110
- ];
111
- function detectWorkspacePackageManager(exists, read = () => void 0) {
112
- if (exists("pnpm-lock.yaml") || exists("pnpm-workspace.yaml"))
113
- return "pnpm";
114
- const found = LOCKFILES.filter(([, files]) => files.some((file) => exists(file))).map(([pm]) => pm);
115
- if (found.length <= 1)
116
- return found[0] ?? null;
117
- const declared = /"packageManager"\s*:\s*"(npm|yarn|bun)@/.exec(read("package.json") ?? "")?.[1];
118
- const named = found.find((pm) => pm === declared);
119
- if (named)
120
- return named;
121
- return found.includes("npm") ? "npm" : found[0];
122
- }
123
- function packageManagerCommands(pm, flavor = "classic") {
124
- switch (pm) {
125
- case "pnpm":
126
- return { addDevAtRoot: (spec) => `pnpm add -D -w ${spec}`, addAtRoot: (spec) => `pnpm add -w ${spec}`, runNx: "pnpm nx", install: "pnpm install", workspaceRegistration: "pnpm-workspace.yaml" };
127
- case "yarn":
128
- return {
129
- addDevAtRoot: (spec) => flavor === "berry" ? `yarn add -D ${spec}` : `yarn add -D -W ${spec}`,
130
- addAtRoot: (spec) => flavor === "berry" ? `yarn add ${spec}` : `yarn add -W ${spec}`,
131
- runNx: "yarn nx",
132
- install: "yarn install",
133
- workspaceRegistration: "package.json workspaces"
134
- };
135
- case "bun":
136
- return { addDevAtRoot: (spec) => `bun add -d ${spec}`, addAtRoot: (spec) => `bun add ${spec}`, runNx: "bun nx", install: "bun install", workspaceRegistration: "package.json workspaces" };
137
- case "npm":
138
- return { addDevAtRoot: (spec) => `npm install -D ${spec}`, addAtRoot: (spec) => `npm install ${spec}`, runNx: "npm exec nx --", install: "npm install", workspaceRegistration: "package.json workspaces" };
139
- }
140
- }
141
-
142
- // ../template-engine/dist/doctor/detect-environment.js
143
108
  function detectEnvironment(root) {
144
109
  const hasNxWorkspace = existsSync(join(root, "nx.json"));
145
110
  if (!hasNxWorkspace) {
@@ -171,804 +136,6 @@ import { join as join6 } from "node:path";
171
136
  // ../template-engine/dist/doctor/review-checks.js
172
137
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
173
138
  import { join as join2 } from "node:path";
174
-
175
- // ../review-policy/dist/modes/review-modes.js
176
- var REVIEW_MODES = {
177
- fast: {
178
- mode: "fast",
179
- promptStyle: "fast-condensed",
180
- includesCorrectnessSecurity: true,
181
- includesOverEngineering: false
182
- },
183
- standard: {
184
- mode: "standard",
185
- promptStyle: "sectioned",
186
- includesCorrectnessSecurity: true,
187
- includesOverEngineering: false
188
- },
189
- thorough: {
190
- mode: "thorough",
191
- promptStyle: "sectioned",
192
- includesCorrectnessSecurity: true,
193
- includesOverEngineering: true
194
- }
195
- };
196
-
197
- // ../review-policy/dist/verdict-gate/findings-block.js
198
- var FINDINGS_MARKER = "<!-- archweave-findings-v1 -->";
199
- var FINDINGS_SCHEMA_ID = "archweave/findings@1";
200
-
201
- // ../review-policy/dist/prompts/prompt-shared.js
202
- function renderFindingSchemaInstruction(locale, priorities) {
203
- 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:";
204
- return [
205
- intro,
206
- `- priority: ${priorities.join(" | ")}`,
207
- "- category: kebab-case category name",
208
- "- title: one-line summary",
209
- "- file: path of the affected file",
210
- "- lineRange: { start, end? }",
211
- "- evidence: what in the code proves the problem",
212
- "- impact: what goes wrong and for whom",
213
- "- reproductionCondition: the exact input/state that triggers it",
214
- "- recommendation: the concrete fix",
215
- "- confidence: confirmed | plausible"
216
- ].join("\n");
217
- }
218
- function renderFindingsBlockInstruction(locale) {
219
- const lines = locale === "en" ? [
220
- "At the END of the top-level summary comment, append the machine-readable findings block:",
221
- `the literal marker line ${FINDINGS_MARKER}, then a fenced \`\`\`json block containing`,
222
- `{ "schema": "${FINDINGS_SCHEMA_ID}", "findings": [ ... ] } with one entry per finding,`,
223
- "using exactly the finding schema above. If there are no findings, still append the block with an empty findings array."
224
- ] : [
225
- "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:",
226
- `\uB9AC\uD130\uB7F4 \uB9C8\uCEE4 \uC904 ${FINDINGS_MARKER} \uB2E4\uC74C\uC5D0 fenced \`\`\`json \uBE14\uB85D\uC73C\uB85C`,
227
- `{ "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.`,
228
- "finding\uC774 \uC5C6\uC5B4\uB3C4 \uBE48 \uBC30\uC5F4\uB85C \uBE14\uB85D\uC744 \uBC18\uB4DC\uC2DC \uD3EC\uD568\uD558\uC138\uC694."
229
- ];
230
- return lines.join("\n");
231
- }
232
- function renderDedupInstruction(locale) {
233
- 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.";
234
- }
235
-
236
- // ../review-policy/dist/prompts/correctness-security-prompt.js
237
- var CORRECTNESS_SECURITY_PRIORITIES = ["P0", "P1", "P2", "Nit"];
238
- var FAST_PRIORITIES = ["P0", "P1", "P2"];
239
- function buildCorrectnessSecurityPrompt(locale, style = "sectioned") {
240
- if (style === "fast-condensed") {
241
- const fastBody = locale === "en" ? [
242
- "You are a code reviewer focused on correctness and security.",
243
- "",
244
- "Review the provided diff and report only the most critical problems. Skip minor style and taste remarks entirely \u2014 report nothing below P2.",
245
- "",
246
- `Only report findings with priority ${FAST_PRIORITIES.join(", ")}. Do NOT report P3 (over-engineering) findings \u2014 they are out of scope here.`
247
- ] : [
248
- "\uB2F9\uC2E0\uC740 \uC815\uD655\uC131(correctness)\uACFC \uBCF4\uC548(security)\uC5D0 \uC9D1\uC911\uD558\uB294 \uCF54\uB4DC \uB9AC\uBDF0\uC5B4\uC785\uB2C8\uB2E4.",
249
- "",
250
- "\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.",
251
- "",
252
- `\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.`
253
- ];
254
- return [...fastBody, "", renderFindingSchemaInstruction(locale, FAST_PRIORITIES), "", renderDedupInstruction(locale)].join("\n");
255
- }
256
- const body = locale === "en" ? [
257
- "You are a code reviewer focused on correctness and security.",
258
- "",
259
- 'Review the provided diff and report findings in two explicitly separated top-level sections: "Correctness" and "Security". Never mix the two.',
260
- "",
261
- `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.`
262
- ] : [
263
- "\uB2F9\uC2E0\uC740 \uC815\uD655\uC131(correctness)\uACFC \uBCF4\uC548(security)\uC5D0 \uC9D1\uC911\uD558\uB294 \uCF54\uB4DC \uB9AC\uBDF0\uC5B4\uC785\uB2C8\uB2E4.",
264
- "",
265
- '\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.',
266
- "",
267
- `\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.`
268
- ];
269
- return [...body, "", renderFindingSchemaInstruction(locale, CORRECTNESS_SECURITY_PRIORITIES), "", renderDedupInstruction(locale)].join("\n");
270
- }
271
-
272
- // ../review-policy/dist/prompts/over-engineering-prompt.js
273
- var OVER_ENGINEERING_PRIORITIES = ["P3"];
274
- function buildOverEngineeringPrompt(locale) {
275
- const body = locale === "en" ? [
276
- "You are a code reviewer focused exclusively on over-engineering.",
277
- "",
278
- "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.",
279
- "",
280
- 'Report your findings in their own top-level section titled "Over-engineering"; never mix them into correctness or security sections.'
281
- ] : [
282
- "\uB2F9\uC2E0\uC740 \uACFC\uC789 \uC124\uACC4(over-engineering)\uB9CC\uC744 \uC804\uB2F4\uD558\uB294 \uCF54\uB4DC \uB9AC\uBDF0\uC5B4\uC785\uB2C8\uB2E4.",
283
- "",
284
- "\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.",
285
- "",
286
- '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.'
287
- ];
288
- return [...body, "", renderFindingSchemaInstruction(locale, OVER_ENGINEERING_PRIORITIES), "", renderDedupInstruction(locale)].join("\n");
289
- }
290
-
291
- // ../review-policy/dist/verdict-gate/verdict-comment.js
292
- var VERDICT_MARKER = "<!-- archweave-verdict -->";
293
- function renderCommitLine(locale, shortSha) {
294
- 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>`;
295
- }
296
- var NOT_RUN_REASON = {
297
- en: {
298
- secret: (secretName) => `the ${secretName} secret is not registered`,
299
- fork: () => "this pull request comes from a fork, and GitHub passes no secrets to runs from forks",
300
- dependabot: (secretName) => `Dependabot runs receive only Dependabot secrets, and ${secretName} is not one of them`,
301
- rounds: (_secretName, maxRounds) => `the automated review round limit (${maxRounds}) is reached`
302
- },
303
- ko: {
304
- secret: (secretName) => `${secretName} \uC2DC\uD06C\uB9BF\uC774 \uB4F1\uB85D\uB418\uC9C0 \uC54A\uC74C`,
305
- 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",
306
- dependabot: (secretName) => `Dependabot \uC2E4\uD589\uC740 Dependabot \uC2DC\uD06C\uB9BF\uB9CC \uBC1B\uB294\uB370, ${secretName}\uB294 \uADF8\uC911\uC5D0 \uC5C6\uC74C`,
307
- rounds: (_secretName, maxRounds) => `\uC790\uB3D9 \uB9AC\uBDF0 \uB77C\uC6B4\uB4DC \uD55C\uB3C4(${maxRounds})\uC5D0 \uB3C4\uB2EC\uD568`
308
- }
309
- };
310
- function renderNotRunNotice(locale, reason, secretName, maxRounds) {
311
- const why = NOT_RUN_REASON[locale][reason](secretName, maxRounds);
312
- 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.`;
313
- }
314
-
315
- // ../review-policy/dist/verdict-gate/verdict-gate.js
316
- var ROUND_MARKER = "<!-- archweave-review-round -->";
317
-
318
- // ../template-engine/dist/review/action-pins.js
319
- var ACTION_PINS = {
320
- "actions/checkout": "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1",
321
- "actions/setup-node": "actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0",
322
- "pnpm/action-setup": "pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0",
323
- "oven-sh/setup-bun": "oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0",
324
- "anthropics/claude-code-action": "anthropics/claude-code-action@6b6870e649b5ff408753befeaee5fc7989d4e6a9 # v1.0.258",
325
- "google-github-actions/run-gemini-cli": "google-github-actions/run-gemini-cli@f77273f4c914e4bf38440cf36a0369cb64a37489 # v0.1.22",
326
- "openai/codex-action": "openai/codex-action@bdf19a4a223ec2549a3e2274a0cf61556bc07675 # v1.13"
327
- };
328
- function pinnedUses(action) {
329
- return ACTION_PINS[action];
330
- }
331
-
332
- // ../template-engine/dist/review/workflow-skeleton.js
333
- function indentBlock(text, spaces) {
334
- const pad = " ".repeat(spaces);
335
- return text.split("\n").map((line) => line.length === 0 ? line : pad + line).join("\n");
336
- }
337
- function renderTriggerBlock() {
338
- return ["on:", " pull_request:", " types: [opened, synchronize, ready_for_review, reopened]"].join("\n");
339
- }
340
- function renderConcurrencyBlock() {
341
- return [
342
- "concurrency:",
343
- " group: archweave-review-${{ github.event.pull_request.number }}",
344
- " cancel-in-progress: false"
345
- ].join("\n");
346
- }
347
- var REVIEW_GATE_CONDITION = "steps.secret-gate.outputs.enabled == 'true' && steps.round-gate.outputs.allowed == 'true'";
348
- function renderReviewPermissions(idToken) {
349
- return ["permissions:", " contents: read", " pull-requests: write", ...idToken ? [" id-token: write"] : []].join("\n");
350
- }
351
- function renderCheckoutStep(gateCondition, extraWith = []) {
352
- return [
353
- "- name: Checkout repository",
354
- ...gateCondition === void 0 ? [] : [` if: ${gateCondition}`],
355
- ` uses: ${pinnedUses("actions/checkout")}`,
356
- " with:",
357
- ...extraWith.map((line) => ` ${line}`),
358
- " fetch-depth: 1",
359
- " persist-credentials: false"
360
- ].join("\n");
361
- }
362
- function renderSecretGateStep(secretName) {
363
- return [
364
- "- name: Check review secret",
365
- " id: secret-gate",
366
- " env:",
367
- ` REVIEW_SECRET: \${{ secrets.${secretName} }}`,
368
- " run: |",
369
- ' if [ -z "$REVIEW_SECRET" ]; then',
370
- ` echo "AI review is not active: register the ${secretName} secret to enable it."`,
371
- ' echo "enabled=false" >> "$GITHUB_OUTPUT"',
372
- " else",
373
- ' echo "enabled=true" >> "$GITHUB_OUTPUT"',
374
- " fi"
375
- ].join("\n");
376
- }
377
- function renderRoundGateStep(maxRounds) {
378
- return [
379
- "- name: Check review round limit",
380
- " id: round-gate",
381
- " if: steps.secret-gate.outputs.enabled == 'true'",
382
- " env:",
383
- " GH_TOKEN: ${{ github.token }}",
384
- " run: |",
385
- ` 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')`,
386
- ` rounds=$(printf '%s' "$counts" | cut -f1)`,
387
- ` echo "last-comment-id=$(printf '%s' "$counts" | cut -f2)" >> "$GITHUB_OUTPUT"`,
388
- ` if [ "$rounds" -ge ${maxRounds} ]; then`,
389
- ` echo "Automated review round limit (${maxRounds}) reached for this PR; skipping."`,
390
- ' echo "allowed=false" >> "$GITHUB_OUTPUT"',
391
- " else",
392
- ' echo "allowed=true" >> "$GITHUB_OUTPUT"',
393
- " fi"
394
- ].join("\n");
395
- }
396
- function shellQuote(text) {
397
- return `'${text.replace(/'/g, `'\\''`)}'`;
398
- }
399
- function renderNotRunFunction(secretName, maxRounds, locale) {
400
- const reasons = ["secret", "fork", "dependabot", "rounds"];
401
- const [commitBefore, commitAfter] = renderCommitLine(locale, "\0").split("\0");
402
- return [
403
- "not_run() {",
404
- ' case "$1" in',
405
- ...reasons.map((reason) => ` ${reason}) annotation=${shellQuote(renderNotRunNotice("en", reason, secretName, maxRounds))}; notice=${shellQuote(renderNotRunNotice(locale, reason, secretName, maxRounds))} ;;`),
406
- " esac",
407
- ' echo "::notice title=AI review did not run::$annotation"',
408
- ` printf '%s\\n' "$notice" >> "$GITHUB_STEP_SUMMARY"`,
409
- ` short_sha=$(printf '%s' "$HEAD_SHA" | cut -c1-7)`,
410
- ' case "$short_sha" in',
411
- " '' | *[!0-9a-f]*) short_sha= ;;",
412
- " esac",
413
- " {",
414
- ' echo "notice-body<<ARCHWEAVE_NOT_RUN_EOF"',
415
- ` printf '%s\\n' ${shellQuote(VERDICT_MARKER)} "\u2139\uFE0F $notice"`,
416
- ` if [ -n "$short_sha" ]; then printf '\\n%s\\n' ${shellQuote(commitBefore)}"$short_sha"${shellQuote(commitAfter)}; fi`,
417
- ' echo "ARCHWEAVE_NOT_RUN_EOF"',
418
- ' echo "run=false"',
419
- ' } >> "$GITHUB_OUTPUT"',
420
- "}"
421
- ];
422
- }
423
- var JUDGE_CONDITION = "steps.ai-review-result.outputs.run == 'true'";
424
- function renderAiReviewResultStep(secretName, maxRounds, locale) {
425
- return [
426
- "- name: Check the AI review result",
427
- " id: ai-review-result",
428
- " env:",
429
- " AI_REVIEW_RESULT: ${{ needs.ai-review.result }}",
430
- " REVIEW_ENABLED: ${{ needs.ai-review.outputs.enabled }}",
431
- " REVIEW_ALLOWED: ${{ needs.ai-review.outputs.allowed }}",
432
- " LAST_COMMENT_ID: ${{ needs.ai-review.outputs.last-comment-id }}",
433
- " HEAD_SHA: ${{ github.event.pull_request.head.sha }}",
434
- " HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}",
435
- " REPOSITORY: ${{ github.repository }}",
436
- " PR_AUTHOR: ${{ github.event.pull_request.user.login }}",
437
- " run: |",
438
- ...renderNotRunFunction(secretName, maxRounds, locale).map((line) => ` ${line}`),
439
- ' if [ "$AI_REVIEW_RESULT" != success ]; then',
440
- ` echo "::error title=AI review did not succeed::The AI review job ended with '$AI_REVIEW_RESULT'."`,
441
- " exit 1",
442
- " fi",
443
- ' case "$REVIEW_ENABLED/$REVIEW_ALLOWED" in',
444
- " false/*)",
445
- ' if [ "$HEAD_REPOSITORY" != "$REPOSITORY" ]; then not_run fork',
446
- ` elif [ "$PR_AUTHOR" = 'dependabot[bot]' ]; then not_run dependabot`,
447
- " else not_run secret",
448
- " fi",
449
- " exit 0",
450
- " ;;",
451
- " true/false)",
452
- " not_run rounds",
453
- " exit 0",
454
- " ;;",
455
- " true/true)",
456
- ' case "$LAST_COMMENT_ID" in',
457
- " '' | *[!0-9]*) ;;",
458
- " *)",
459
- ' echo "run=true" >> "$GITHUB_OUTPUT"',
460
- " exit 0",
461
- " ;;",
462
- " esac",
463
- " ;;",
464
- " esac",
465
- ` 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."`,
466
- " exit 1"
467
- ].join("\n");
468
- }
469
- function renderVerdictGateStep(maxRounds, locale) {
470
- return [
471
- "- name: Compute review verdict",
472
- " id: verdict",
473
- ` if: ${JUDGE_CONDITION}`,
474
- " env:",
475
- " GH_TOKEN: ${{ github.token }}",
476
- " GITHUB_REPOSITORY: ${{ github.repository }}",
477
- " ARCHWEAVE_PR_NUMBER: ${{ github.event.pull_request.number }}",
478
- ` ARCHWEAVE_MAX_ROUNDS: '${maxRounds}'`,
479
- ` ARCHWEAVE_LOCALE: '${locale}'`,
480
- " ARCHWEAVE_HEAD_SHA: ${{ github.event.pull_request.head.sha }}",
481
- " ARCHWEAVE_ROUND_AFTER_ID: ${{ needs.ai-review.outputs.last-comment-id }}",
482
- " ARCHWEAVE_VERDICT_OUTPUT: 'github-output'",
483
- " run: |",
484
- " if [ -x node_modules/.bin/archweave-review-verdict ]; then",
485
- " node_modules/.bin/archweave-review-verdict",
486
- " else",
487
- ' echo "nx-archweave is not installed in this workspace - skipping the verdict gate."',
488
- " fi"
489
- ].join("\n");
490
- }
491
- function renderBlockStep() {
492
- return [
493
- "- name: Fail the review check",
494
- " if: steps.verdict.outputs.blocked == 'true'",
495
- " env:",
496
- " BLOCK_REASON: ${{ steps.verdict.outputs.block-reason }}",
497
- " run: |",
498
- ' case "$BLOCK_REASON" in',
499
- ' block) echo "The AI verdict is BLOCK: fix the P0/P1 findings in the verdict comment and push." ;;',
500
- ' 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." ;;',
501
- ' 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." ;;',
502
- ' *) echo "The review check failed. See the verdict comment on this pull request." ;;',
503
- " esac",
504
- " exit 1"
505
- ].join("\n");
506
- }
507
- function renderVerdictCommentStep() {
508
- return [
509
- "- name: Post review verdict",
510
- " env:",
511
- " GH_TOKEN: ${{ github.token }}",
512
- " REPOSITORY: ${{ github.repository }}",
513
- " PR_NUMBER: ${{ github.event.pull_request.number }}",
514
- " VERDICT_BODY: ${{ needs.review.outputs.comment-body }}",
515
- " PREVIOUS_ID: ${{ needs.review.outputs.previous-comment-id }}",
516
- " run: |",
517
- ` payload=$(jq -n --arg body "$VERDICT_BODY" '{body: $body}')`,
518
- ' if [ -z "$PREVIOUS_ID" ]; then',
519
- ` 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=`,
520
- " fi",
521
- ' case "$PREVIOUS_ID" in',
522
- " ''|*[!0-9]*) PREVIOUS_ID= ;;",
523
- " esac",
524
- ` if [ -n "$PREVIOUS_ID" ] && printf '%s' "$payload" | gh api -X PATCH "repos/$REPOSITORY/issues/comments/$PREVIOUS_ID" --input - > /dev/null; then`,
525
- " exit 0",
526
- " fi",
527
- ` printf '%s' "$payload" | gh api -X POST "repos/$REPOSITORY/issues/$PR_NUMBER/comments" --input - > /dev/null \\`,
528
- ' || echo "Could not post the verdict comment (continuing)."'
529
- ].join("\n");
530
- }
531
- var SETUP_NODE_VERSION = "22";
532
- var FALLBACK_PNPM_VERSION = "10";
533
- var INSTALL_LINES = {
534
- pnpm: [" pnpm install --frozen-lockfile --ignore-scripts"],
535
- npm: [" npm ci --ignore-scripts"],
536
- yarn: [
537
- " corepack enable yarn",
538
- ' 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'
539
- ],
540
- bun: [" bun install --frozen-lockfile --ignore-scripts"]
541
- };
542
- function renderPnpmVersionStep(gate) {
543
- return [
544
- "- name: Detect pnpm version",
545
- " id: pnpm-version",
546
- gate,
547
- " run: |",
548
- ` if [ -n "$(jq -r '.packageManager // empty' package.json)" ]; then`,
549
- ' echo "version=" >> "$GITHUB_OUTPUT"',
550
- " else",
551
- ` echo "version=${FALLBACK_PNPM_VERSION}" >> "$GITHUB_OUTPUT"`,
552
- " fi"
553
- ].join("\n");
554
- }
555
- function renderWorkspaceSetupSteps(packageManager) {
556
- const gate = ` if: ${JUDGE_CONDITION}`;
557
- const steps = [];
558
- if (packageManager === "pnpm") {
559
- steps.push(renderPnpmVersionStep(gate));
560
- steps.push([
561
- "- name: Set up pnpm",
562
- gate,
563
- ` uses: ${pinnedUses("pnpm/action-setup")}`,
564
- " with:",
565
- " version: ${{ steps.pnpm-version.outputs.version }}"
566
- ].join("\n"));
567
- }
568
- if (packageManager === "bun") {
569
- steps.push(["- name: Set up bun", gate, ` uses: ${pinnedUses("oven-sh/setup-bun")}`].join("\n"));
570
- }
571
- steps.push([
572
- "- name: Set up Node",
573
- gate,
574
- ` uses: ${pinnedUses("actions/setup-node")}`,
575
- " with:",
576
- ` node-version: ${SETUP_NODE_VERSION}`,
577
- // yarn: no setup-node cache — it would query the runner's global yarn 1 even in a
578
- // berry project. Corepack (shipped with Node) provides the project's own yarn below.
579
- // bun: setup-node has no bun cache. Node is still set up — nx and the verdict bin
580
- // run on it.
581
- ...packageManager === "yarn" || packageManager === "bun" ? [] : [` cache: ${packageManager}`]
582
- ].join("\n"));
583
- steps.push([
584
- "- name: Install workspace dependencies",
585
- gate,
586
- " run: |",
587
- ...INSTALL_LINES[packageManager]
588
- ].join("\n"));
589
- return steps.join("\n\n");
590
- }
591
- var REVIEW_JOB_TIMEOUT_MINUTES = 15;
592
- function renderReviewWorkflow(input, parts) {
593
- const aiReviewSteps = [renderSecretGateStep(parts.secretName), renderRoundGateStep(input.maxRounds), ...parts.reviewSteps];
594
- const reviewSteps = [
595
- renderAiReviewResultStep(parts.secretName, input.maxRounds, input.locale),
596
- renderCheckoutStep(JUDGE_CONDITION),
597
- renderWorkspaceSetupSteps(input.packageManager),
598
- renderVerdictGateStep(input.maxRounds, input.locale),
599
- renderBlockStep()
600
- ];
601
- return [
602
- `name: ${parts.name}`,
603
- "",
604
- renderTriggerBlock(),
605
- "",
606
- renderConcurrencyBlock(),
607
- "",
608
- "permissions: {}",
609
- "",
610
- "jobs:",
611
- " ai-review:",
612
- " runs-on: ubuntu-latest",
613
- ` timeout-minutes: ${REVIEW_JOB_TIMEOUT_MINUTES}`,
614
- indentBlock(renderReviewPermissions(parts.idToken), 4),
615
- " outputs:",
616
- " enabled: ${{ steps.secret-gate.outputs.enabled }}",
617
- " allowed: ${{ steps.round-gate.outputs.allowed }}",
618
- " last-comment-id: ${{ steps.round-gate.outputs.last-comment-id }}",
619
- " steps:",
620
- indentBlock(aiReviewSteps.join("\n\n"), 6),
621
- "",
622
- " review:",
623
- " needs: ai-review",
624
- " if: always()",
625
- " runs-on: ubuntu-latest",
626
- ` timeout-minutes: ${REVIEW_JOB_TIMEOUT_MINUTES}`,
627
- " permissions:",
628
- " contents: read",
629
- " pull-requests: read",
630
- " outputs:",
631
- " comment-body: ${{ steps.verdict.outputs.comment-body || steps.ai-review-result.outputs.notice-body }}",
632
- " previous-comment-id: ${{ steps.verdict.outputs.previous-comment-id }}",
633
- " steps:",
634
- indentBlock(reviewSteps.join("\n\n"), 6),
635
- "",
636
- " review-comment:",
637
- " needs: review",
638
- // Runs after a BLOCK failed the review job, too: the comment explains the red check.
639
- " if: ${{ !cancelled() && needs.review.outputs.comment-body != '' }}",
640
- " runs-on: ubuntu-latest",
641
- ` timeout-minutes: ${REVIEW_JOB_TIMEOUT_MINUTES}`,
642
- " permissions:",
643
- " pull-requests: write",
644
- " steps:",
645
- indentBlock(renderVerdictCommentStep(), 6),
646
- ""
647
- ].join("\n");
648
- }
649
-
650
- // ../template-engine/dist/review/claude/claude-provider.js
651
- var SECRET_NAME = "CLAUDE_CODE_OAUTH_TOKEN";
652
- var ALLOWED_TOOLS = "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)";
653
- var CLAUDE_STEP_ID = "claude-review";
654
- var PR_CONTEXT = ["REPO: ${{ github.repository }}", "PR NUMBER: ${{ github.event.pull_request.number }}"];
655
- function renderWorkflowInstructions(locale) {
656
- const lines = locale === "en" ? [
657
- ...PR_CONTEXT,
658
- "",
659
- "Review the diff of this pull request using `gh pr diff <PR NUMBER>` and `gh pr view <PR NUMBER>`.",
660
- "Post specific findings as inline comments with `mcp__github_inline_comment__create_inline_comment` (pass `confirmed: true`), and exactly one top-level summary comment.",
661
- "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 `$(...)`.",
662
- "Run each `gh` command on its own: no pipes to other programs, no `&&` chains.",
663
- `Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
664
- "If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
665
- "Do not modify any code, and never approve or merge the pull request.",
666
- "Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
667
- ] : [
668
- ...PR_CONTEXT,
669
- "",
670
- "\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.",
671
- "\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.",
672
- "\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.",
673
- "`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.",
674
- `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.`,
675
- '\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).',
676
- "\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).",
677
- "\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."
678
- ];
679
- return lines.join("\n");
680
- }
681
- function composePrompt(input) {
682
- const mode = REVIEW_MODES[input.mode];
683
- const sections = [renderWorkflowInstructions(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
684
- if (mode.includesOverEngineering) {
685
- sections.push(buildOverEngineeringPrompt(input.locale));
686
- }
687
- sections.push(renderFindingsBlockInstruction(input.locale));
688
- return sections.join("\n\n---\n\n");
689
- }
690
- function renderDeniedToolsStep() {
691
- return [
692
- "- name: Show denied tool calls",
693
- ` if: always() && steps.${CLAUDE_STEP_ID}.outputs.execution_file != ''`,
694
- " env:",
695
- ` EXECUTION_FILE: \${{ steps.${CLAUDE_STEP_ID}.outputs.execution_file }}`,
696
- " run: |",
697
- ' [ -f "$EXECUTION_FILE" ] || exit 0',
698
- ` stop_token="archweave-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \\n')"`,
699
- ' echo "::stop-commands::$stop_token"',
700
- ` 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."`,
701
- ' echo "::$stop_token::"'
702
- ].join("\n");
703
- }
704
- function buildWorkflowYaml(input) {
705
- return renderReviewWorkflow(input, {
706
- name: "Claude Code Review",
707
- secretName: SECRET_NAME,
708
- // The Claude GitHub App signs in with the workflow's OIDC token (claude-code-action's FAQ).
709
- idToken: true,
710
- reviewSteps: [
711
- renderCheckoutStep(REVIEW_GATE_CONDITION),
712
- [
713
- "- name: Run Claude review",
714
- ` id: ${CLAUDE_STEP_ID}`,
715
- ` if: ${REVIEW_GATE_CONDITION}`,
716
- ` uses: ${pinnedUses("anthropics/claude-code-action")}`,
717
- " with:",
718
- ` claude_code_oauth_token: \${{ secrets.${SECRET_NAME} }}`,
719
- " prompt: |",
720
- indentBlock(composePrompt(input), 6),
721
- " claude_args: |",
722
- ` --allowedTools "${ALLOWED_TOOLS}"`
723
- ].join("\n"),
724
- renderDeniedToolsStep()
725
- ]
726
- });
727
- }
728
- var claudeProvider = {
729
- id: "claude",
730
- workflowFileName: ".github/workflows/claude-code-review.yml",
731
- requiredSecretName: SECRET_NAME,
732
- buildWorkflowYaml
733
- };
734
-
735
- // ../template-engine/dist/review/cursor/cursor-provider.js
736
- var SECRET_NAME2 = "CURSOR_API_KEY";
737
- var PROMPT_HEREDOC_DELIMITER = "ARCHWEAVE_CURSOR_REVIEW_PROMPT";
738
- function renderWorkflowInstructions2(locale) {
739
- const lines = locale === "en" ? [
740
- "Review the diff of this pull request using `gh pr diff` and `gh pr view`.",
741
- "Post specific findings as inline comments and exactly one top-level summary comment.",
742
- `Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
743
- "If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
744
- "Do not modify any code, and never approve or merge the pull request.",
745
- "Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
746
- ] : [
747
- "\uC774 pull request\uC758 diff\uB97C `gh pr diff`\uC640 `gh pr view`\uB85C \uD655\uC778\uD558\uACE0 \uB9AC\uBDF0\uD558\uC138\uC694.",
748
- "\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.",
749
- `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.`,
750
- '\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).',
751
- "\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).",
752
- "\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."
753
- ];
754
- return lines.join("\n");
755
- }
756
- function composePrompt2(input) {
757
- const mode = REVIEW_MODES[input.mode];
758
- const sections = [renderWorkflowInstructions2(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
759
- if (mode.includesOverEngineering) {
760
- sections.push(buildOverEngineeringPrompt(input.locale));
761
- }
762
- sections.push(renderFindingsBlockInstruction(input.locale));
763
- return sections.join("\n\n---\n\n");
764
- }
765
- function buildWorkflowYaml2(input) {
766
- return renderReviewWorkflow(input, {
767
- name: "Cursor Code Review",
768
- secretName: SECRET_NAME2,
769
- // An API key: the Cursor CLI needs no OIDC token.
770
- idToken: false,
771
- reviewSteps: [
772
- renderCheckoutStep(REVIEW_GATE_CONDITION),
773
- [
774
- "- name: Install cursor-agent CLI",
775
- ` if: ${REVIEW_GATE_CONDITION}`,
776
- " run: |",
777
- // HTTPS only, TLS 1.2 or newer. Cursor publishes no pinned version or checksum for
778
- // this installer; docs/pr-review.md states what that leaves open.
779
- " curl --proto '=https' --tlsv1.2 -fsS https://cursor.com/install | bash",
780
- // The docs name ~/.cursor/bin; the installer currently links `agent` into ~/.local/bin.
781
- // Both go on the PATH, so the next step finds it wherever the installer puts it.
782
- ' echo "$HOME/.local/bin" >> "$GITHUB_PATH"',
783
- ' echo "$HOME/.cursor/bin" >> "$GITHUB_PATH"'
784
- ].join("\n"),
785
- [
786
- "- name: Run Cursor review",
787
- ` if: ${REVIEW_GATE_CONDITION}`,
788
- " env:",
789
- ` CURSOR_API_KEY: \${{ secrets.${SECRET_NAME2} }}`,
790
- " GH_TOKEN: ${{ github.token }}",
791
- " run: |",
792
- // cursor-agent stops at "Workspace Trust Required" without --trust (2026.10.22 on
793
- // Actions); the folder is this run's own checkout, and forks never get the secret.
794
- ` agent --trust -p "$(cat <<'${PROMPT_HEREDOC_DELIMITER}'`,
795
- indentBlock(composePrompt2(input), 4),
796
- ` ${PROMPT_HEREDOC_DELIMITER}`,
797
- ' )"'
798
- ].join("\n")
799
- ]
800
- });
801
- }
802
- var cursorProvider = {
803
- id: "cursor",
804
- workflowFileName: ".github/workflows/cursor-code-review.yml",
805
- requiredSecretName: SECRET_NAME2,
806
- buildWorkflowYaml: buildWorkflowYaml2
807
- };
808
-
809
- // ../template-engine/dist/review/gemini/gemini-provider.js
810
- var SECRET_NAME3 = "GEMINI_API_KEY";
811
- function renderWorkflowInstructions3(locale) {
812
- const lines = locale === "en" ? [
813
- "Review the diff of this pull request using `gh pr diff` and `gh pr view`.",
814
- "Post specific findings as inline comments and exactly one top-level summary comment.",
815
- `Always include the literal marker ${ROUND_MARKER} in the top-level summary comment.`,
816
- "If there are no findings that require action, post exactly one top-level comment saying so (and still include the marker).",
817
- "Do not modify any code, and never approve or merge the pull request.",
818
- "Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
819
- ] : [
820
- "\uC774 pull request\uC758 diff\uB97C `gh pr diff`\uC640 `gh pr view`\uB85C \uD655\uC778\uD558\uACE0 \uB9AC\uBDF0\uD558\uC138\uC694.",
821
- "\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.",
822
- `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.`,
823
- '\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).',
824
- "\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).",
825
- "\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."
826
- ];
827
- return lines.join("\n");
828
- }
829
- function composePrompt3(input) {
830
- const mode = REVIEW_MODES[input.mode];
831
- const sections = [renderWorkflowInstructions3(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
832
- if (mode.includesOverEngineering) {
833
- sections.push(buildOverEngineeringPrompt(input.locale));
834
- }
835
- sections.push(renderFindingsBlockInstruction(input.locale));
836
- return sections.join("\n\n---\n\n");
837
- }
838
- var GEMINI_CLI_VERSION = "0.65.0";
839
- function buildWorkflowYaml3(input) {
840
- return renderReviewWorkflow(input, {
841
- name: "Gemini Code Review",
842
- secretName: SECRET_NAME3,
843
- // An API key: run-gemini-cli uses OIDC only for Google Cloud workload identity federation.
844
- idToken: false,
845
- reviewSteps: [
846
- renderCheckoutStep(REVIEW_GATE_CONDITION),
847
- [
848
- "- name: Run Gemini review",
849
- ` if: ${REVIEW_GATE_CONDITION}`,
850
- ` uses: ${pinnedUses("google-github-actions/run-gemini-cli")}`,
851
- " env:",
852
- " GITHUB_TOKEN: ${{ github.token }}",
853
- // Gemini CLI 0.65 refuses to run in a folder it hasn't been told to trust (measured
854
- // 2026-10-25 on Actions). The folder is this run's own checkout; pull requests from forks
855
- // never get here, because they get no secret.
856
- " GEMINI_CLI_TRUST_WORKSPACE: 'true'",
857
- " with:",
858
- ` gemini_api_key: \${{ secrets.${SECRET_NAME3} }}`,
859
- ` gemini_cli_version: '${GEMINI_CLI_VERSION}'`,
860
- " prompt: |",
861
- indentBlock(composePrompt3(input), 6)
862
- ].join("\n")
863
- ]
864
- });
865
- }
866
- var geminiProvider = {
867
- id: "gemini",
868
- workflowFileName: ".github/workflows/gemini-code-review.yml",
869
- requiredSecretName: SECRET_NAME3,
870
- buildWorkflowYaml: buildWorkflowYaml3
871
- };
872
-
873
- // ../template-engine/dist/review/gpt/gpt-provider.js
874
- var SECRET_NAME4 = "OPENAI_API_KEY";
875
- function renderWorkflowInstructions4(locale) {
876
- const lines = locale === "en" ? [
877
- "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.",
878
- "Write your entire response as a single message: report specific findings, then close with one overall summary.",
879
- `Always include the literal marker ${ROUND_MARKER} in your message.`,
880
- "If there are no findings that require action, write exactly one message saying so (and still include the marker).",
881
- "Do not modify any code, and never approve or merge the pull request.",
882
- "Write summaries and explanations in English; keep file names, function names, and identifiers verbatim."
883
- ] : [
884
- "\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).",
885
- "\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.",
886
- `\uBA54\uC2DC\uC9C0\uC5D0\uB294 \uBC18\uB4DC\uC2DC \uB9AC\uD130\uB7F4 \uB9C8\uCEE4 ${ROUND_MARKER}\uB97C \uD3EC\uD568\uD558\uC138\uC694.`,
887
- '\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).',
888
- "\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).",
889
- "\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."
890
- ];
891
- return lines.join("\n");
892
- }
893
- function composePrompt4(input) {
894
- const mode = REVIEW_MODES[input.mode];
895
- const sections = [renderWorkflowInstructions4(input.locale), buildCorrectnessSecurityPrompt(input.locale, mode.promptStyle)];
896
- if (mode.includesOverEngineering) {
897
- sections.push(buildOverEngineeringPrompt(input.locale));
898
- }
899
- sections.push(renderFindingsBlockInstruction(input.locale));
900
- return sections.join("\n\n---\n\n");
901
- }
902
- function buildWorkflowYaml4(input) {
903
- return renderReviewWorkflow(input, {
904
- name: "GPT Code Review",
905
- secretName: SECRET_NAME4,
906
- // An API key: codex-action needs no OIDC token.
907
- idToken: false,
908
- reviewSteps: [
909
- renderCheckoutStep(REVIEW_GATE_CONDITION, ["ref: refs/pull/${{ github.event.pull_request.number }}/merge"]),
910
- [
911
- "- name: Fetch base and head refs for the PR",
912
- ` if: ${REVIEW_GATE_CONDITION}`,
913
- " env:",
914
- " PR_BASE_REF: ${{ github.event.pull_request.base.ref }}",
915
- " PR_NUMBER: ${{ github.event.pull_request.number }}",
916
- " run: |",
917
- " # Explicit destinations: the sandboxed review step has no network, so both sides",
918
- " # of the diff must survive as stable local refs (FETCH_HEAD alone is ambiguous).",
919
- " git fetch --no-tags origin \\",
920
- ' "+refs/heads/$PR_BASE_REF:refs/remotes/origin/pr-base" \\',
921
- ' "+refs/pull/$PR_NUMBER/head:refs/remotes/origin/pr-head"'
922
- ].join("\n"),
923
- [
924
- "- name: Run Codex review",
925
- " id: codex-review",
926
- ` if: ${REVIEW_GATE_CONDITION}`,
927
- ` uses: ${pinnedUses("openai/codex-action")}`,
928
- " with:",
929
- ` openai-api-key: \${{ secrets.${SECRET_NAME4} }}`,
930
- ' permission-profile: ":read-only"',
931
- " prompt: |",
932
- indentBlock(composePrompt4(input), 6)
933
- ].join("\n"),
934
- [
935
- "- name: Post review comment",
936
- ` if: ${REVIEW_GATE_CONDITION}`,
937
- " env:",
938
- " GH_TOKEN: ${{ github.token }}",
939
- " PR_NUMBER: ${{ github.event.pull_request.number }}",
940
- " REVIEW_BODY: ${{ steps.codex-review.outputs.final-message }}",
941
- " run: |",
942
- ' printf %s "$REVIEW_BODY" | gh pr comment "$PR_NUMBER" --body-file -'
943
- ].join("\n")
944
- ]
945
- });
946
- }
947
- var gptProvider = {
948
- id: "gpt",
949
- workflowFileName: ".github/workflows/gpt-code-review.yml",
950
- requiredSecretName: SECRET_NAME4,
951
- buildWorkflowYaml: buildWorkflowYaml4
952
- };
953
-
954
- // ../template-engine/dist/review/provider.js
955
- var REVIEW_PROVIDERS = {
956
- claude: claudeProvider,
957
- gemini: geminiProvider,
958
- gpt: gptProvider,
959
- cursor: cursorProvider
960
- };
961
-
962
- // ../template-engine/dist/review/conflict.js
963
- function judgeReviewFileConflict(existing, generated) {
964
- if (existing === null)
965
- return { kind: "create" };
966
- if (existing === generated)
967
- return { kind: "unchanged" };
968
- return { kind: "merge-proposal", existing, generated };
969
- }
970
-
971
- // ../template-engine/dist/doctor/review-checks.js
972
139
  var CONFIG_PATH = "toolkit.config.json";
973
140
  var DEFAULT_PROVIDER_ID = "claude";
974
141
  function deriveAltWorkflowPath(mainWorkflowPath) {
@@ -1252,107 +419,6 @@ function buildReviewConfigConsistencyCheck(root, mainWorkflowPath, mainExists, c
1252
419
  // ../template-engine/dist/doctor/backend-checks.js
1253
420
  import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "node:fs";
1254
421
  import { join as join3 } from "node:path";
1255
-
1256
- // ../template-engine/dist/backend/eslint-boundaries.js
1257
- var DEFAULT_BOUNDARY_TAGS = { frontend: "scope:frontend", backend: "scope:backend", shared: "scope:shared" };
1258
- function readBoundaryTags(config) {
1259
- const boundaries = config?.["boundaries"];
1260
- const tags = typeof boundaries === "object" && boundaries !== null ? boundaries["tags"] : void 0;
1261
- const pick = (role) => {
1262
- const value = typeof tags === "object" && tags !== null ? tags[role] : void 0;
1263
- return typeof value === "string" && value.trim() !== "" ? value : DEFAULT_BOUNDARY_TAGS[role];
1264
- };
1265
- return { frontend: pick("frontend"), backend: pick("backend"), shared: pick("shared") };
1266
- }
1267
- function scopeConstraints(tags = DEFAULT_BOUNDARY_TAGS) {
1268
- return [
1269
- { sourceTag: tags.frontend, notDependOnLibsWithTags: [tags.backend] },
1270
- { sourceTag: tags.backend, notDependOnLibsWithTags: [tags.frontend] },
1271
- { sourceTag: tags.shared, notDependOnLibsWithTags: [tags.frontend, tags.backend] }
1272
- ];
1273
- }
1274
- var SCOPE_CONSTRAINTS = scopeConstraints();
1275
- var ESLINT_CONFIG_CANDIDATES = ["eslint.config.mjs", "eslint.config.js", "eslint.config.cjs"];
1276
- var appliedMarker = (tags) => `sourceTag: '${tags.frontend}'`;
1277
- var ALLOW_ALL_BLOCK = /depConstraints:\s*\[\s*\{\s*sourceTag:\s*'\*',\s*onlyDependOnLibsWithTags:\s*\[\s*'\*'\s*\],?\s*\},?\s*\]/g;
1278
- function maskComments(text) {
1279
- const out = text.split("");
1280
- let mode = "code";
1281
- let quote = "";
1282
- for (let i = 0; i < text.length; i++) {
1283
- const c = text[i];
1284
- const d = text[i + 1];
1285
- if (mode === "code") {
1286
- if (c === "'" || c === '"' || c === "`") {
1287
- mode = "string";
1288
- quote = c;
1289
- } else if (c === "/" && d === "/") {
1290
- mode = "line";
1291
- out[i] = " ";
1292
- } else if (c === "/" && d === "*") {
1293
- mode = "block";
1294
- out[i] = " ";
1295
- }
1296
- } else if (mode === "string") {
1297
- if (c === "\\") {
1298
- i++;
1299
- } else if (c === quote) {
1300
- mode = "code";
1301
- }
1302
- } else if (mode === "line") {
1303
- if (c === "\n") {
1304
- mode = "code";
1305
- } else {
1306
- out[i] = " ";
1307
- }
1308
- } else {
1309
- if (c === "*" && d === "/") {
1310
- out[i] = " ";
1311
- out[i + 1] = " ";
1312
- i++;
1313
- mode = "code";
1314
- } else if (c !== "\n") {
1315
- out[i] = " ";
1316
- }
1317
- }
1318
- }
1319
- return out.join("");
1320
- }
1321
- function findAllowAllMatches(masked) {
1322
- const matches = [];
1323
- ALLOW_ALL_BLOCK.lastIndex = 0;
1324
- for (let m = ALLOW_ALL_BLOCK.exec(masked); m !== null; m = ALLOW_ALL_BLOCK.exec(masked)) {
1325
- matches.push({ index: m.index, length: m[0].length });
1326
- }
1327
- return matches;
1328
- }
1329
- function classifyBoundaryConstraints(configText, tags = DEFAULT_BOUNDARY_TAGS) {
1330
- const masked = maskComments(configText);
1331
- if (masked.includes(appliedMarker(tags)))
1332
- return "enforced";
1333
- if (findAllowAllMatches(masked).length > 0)
1334
- return "allow-all";
1335
- return "unknown";
1336
- }
1337
-
1338
- // ../templates/dist/fsd/fsd-eslint.js
1339
- var FSD_ESLINT_MODULE = "@clearlydev/nx-archweave/eslint";
1340
-
1341
- // ../template-engine/dist/backend/compose-backend-wiring.js
1342
- var DEFAULT_SHARED_TYPES_ROOT = "libs/shared-types";
1343
- function readSharedTypesConfig(config) {
1344
- const value = config?.["sharedTypes"];
1345
- if (value === false)
1346
- return false;
1347
- if (typeof value !== "object" || value === null)
1348
- return { path: DEFAULT_SHARED_TYPES_ROOT };
1349
- const record = value;
1350
- const path = typeof record["path"] === "string" && record["path"].trim() !== "" ? record["path"].replace(/\/+$/, "") : DEFAULT_SHARED_TYPES_ROOT;
1351
- const importPath = typeof record["importPath"] === "string" && record["importPath"].trim() !== "" ? record["importPath"] : void 0;
1352
- return importPath === void 0 ? { path } : { path, importPath };
1353
- }
1354
-
1355
- // ../template-engine/dist/doctor/backend-checks.js
1356
422
  var CONFIG_PATH2 = "toolkit.config.json";
1357
423
  var BACKEND_CHECK_IDS = [
1358
424
  "backend-framework",
@@ -1473,21 +539,21 @@ function buildBackendChecks(root, projects) {
1473
539
  suggestedAction: "Fix the backend field, then re-run doctor."
1474
540
  }));
1475
541
  } else {
1476
- let hasDependency2 = false;
542
+ let hasDependency = false;
1477
543
  try {
1478
544
  const pkg = JSON.parse(readFileSync3(join3(root, "package.json"), "utf-8"));
1479
545
  const packageName = backend === "nest" ? "@nestjs/core" : "express";
1480
- hasDependency2 = Boolean(pkg.dependencies?.[packageName] ?? pkg.devDependencies?.[packageName]);
546
+ hasDependency = Boolean(pkg.dependencies?.[packageName] ?? pkg.devDependencies?.[packageName]);
1481
547
  } catch {
1482
- hasDependency2 = false;
548
+ hasDependency = false;
1483
549
  }
1484
550
  checks.push(createDoctorCheck({
1485
551
  id: "backend-framework",
1486
552
  label: CHECK_LABELS["backend-framework"],
1487
- status: hasDependency2 ? "pass" : "warning",
1488
- message: hasDependency2 ? `${backend} dependency detected in the root package.json` : `backend is configured as ${backend} but its dependency was not found in the root package.json`,
1489
- evidence: hasDependency2 ? ["package.json"] : [],
1490
- suggestedAction: hasDependency2 ? void 0 : "Run the official generator commands shown by add-backend."
553
+ status: hasDependency ? "pass" : "warning",
554
+ message: hasDependency ? `${backend} dependency detected in the root package.json` : `backend is configured as ${backend} but its dependency was not found in the root package.json`,
555
+ evidence: hasDependency ? ["package.json"] : [],
556
+ suggestedAction: hasDependency ? void 0 : "Run the official generator commands shown by add-backend."
1491
557
  }), createDoctorCheck({
1492
558
  id: "backend-config-consistency",
1493
559
  label: CHECK_LABELS["backend-config-consistency"],
@@ -1640,94 +706,6 @@ function buildFsdLikeCheck(root, projects) {
1640
706
  import { existsSync as existsSync5, readdirSync as readdirSync4, readFileSync as readFileSync4 } from "node:fs";
1641
707
  import { join as join5 } from "node:path";
1642
708
 
1643
- // ../template-engine/dist/frameworks/profiles.js
1644
- var ALL_LAYERS = ["app", "pages", "features", "entities", "shared"];
1645
- var NEXT = {
1646
- id: "next",
1647
- recognised: true,
1648
- // Next.js's own app/ (App Router) directory is the composition root; FSD's Next.js guide
1649
- // names the FSD app and pages layers _app and _pages, since Next.js reads src/app and
1650
- // src/pages as its routers.
1651
- layers: ["pages", "features", "entities", "shared"],
1652
- layerDirs: { app: "_app", pages: "_pages" },
1653
- buildOutputDirs: [".next", "out"],
1654
- platformSuffixes: [],
1655
- routerDirs: ["pages"]
1656
- };
1657
- var REACT_VITE = {
1658
- id: "react-vite",
1659
- recognised: true,
1660
- layers: ALL_LAYERS,
1661
- layerDirs: {},
1662
- buildOutputDirs: ["dist"],
1663
- platformSuffixes: [],
1664
- routerDirs: []
1665
- };
1666
- var EXPO_ROUTER = {
1667
- id: "expo",
1668
- recognised: true,
1669
- layers: ALL_LAYERS,
1670
- layerDirs: { app: "_app" },
1671
- buildOutputDirs: [".expo", "dist"],
1672
- platformSuffixes: ["ios", "android", "native", "web"],
1673
- routerDirs: []
1674
- };
1675
- var EXPO = { ...EXPO_ROUTER, layerDirs: {} };
1676
- var GENERIC = {
1677
- id: "generic",
1678
- recognised: false,
1679
- layers: ALL_LAYERS,
1680
- layerDirs: {},
1681
- buildOutputDirs: ["dist", "build"],
1682
- platformSuffixes: [],
1683
- routerDirs: []
1684
- };
1685
- var NEXT_CONFIGS = ["next.config.js", "next.config.mjs", "next.config.ts", "next.config.cjs"];
1686
- var VITE_CONFIGS = ["vite.config.ts", "vite.config.mts", "vite.config.js", "vite.config.mjs"];
1687
- var EXPO_CONFIGS = ["app.config.ts", "app.config.js", "app.config.mjs"];
1688
- function hasExpoKey(input, file) {
1689
- const text = input.read(file);
1690
- if (typeof text !== "string")
1691
- return false;
1692
- try {
1693
- return typeof JSON.parse(text)["expo"] === "object";
1694
- } catch {
1695
- return false;
1696
- }
1697
- }
1698
- function dependsOn(input, manifest, name) {
1699
- const text = input.read(manifest);
1700
- if (typeof text !== "string")
1701
- return false;
1702
- try {
1703
- const parsed = JSON.parse(text);
1704
- return ["dependencies", "devDependencies", "peerDependencies"].some((field) => parsed[field]?.[name] !== void 0);
1705
- } catch {
1706
- return false;
1707
- }
1708
- }
1709
- function detectFramework(input) {
1710
- const prefix = input.projectRoot === "" || input.projectRoot === "." ? "" : `${input.projectRoot}/`;
1711
- const executors = input.executors ?? [];
1712
- if (NEXT_CONFIGS.some((file) => input.exists(`${prefix}${file}`)) || executors.some((executor) => executor.startsWith("@nx/next:"))) {
1713
- return NEXT;
1714
- }
1715
- const dependsAnywhere = (name) => dependsOn(input, `${prefix}package.json`, name) || dependsOn(input, "package.json", name);
1716
- const usesExpo = executors.some((executor) => executor.startsWith("@nx/expo:")) || hasExpoKey(input, `${prefix}app.json`) || EXPO_CONFIGS.some((file) => input.exists(`${prefix}${file}`)) || dependsOn(input, `${prefix}package.json`, "expo");
1717
- if (usesExpo)
1718
- return dependsAnywhere("expo-router") ? EXPO_ROUTER : EXPO;
1719
- const usesReact = dependsAnywhere("react");
1720
- const usesVite = VITE_CONFIGS.some((file) => input.exists(`${prefix}${file}`)) || executors.some((executor) => executor.startsWith("@nx/vite:"));
1721
- if (usesVite && usesReact)
1722
- return REACT_VITE;
1723
- return GENERIC;
1724
- }
1725
-
1726
- // ../template-engine/dist/fsd/fsd-eslint-config.js
1727
- function classifyFsdEslintConfig(configText) {
1728
- return maskComments(configText).includes(FSD_ESLINT_MODULE) ? "applied" : "absent";
1729
- }
1730
-
1731
709
  // ../template-engine/dist/doctor/doctor-projects.js
1732
710
  function projectPrefix(root) {
1733
711
  return root === "" || root === "." ? "" : `${root}/`;
@@ -2059,47 +1037,6 @@ function buildGithubTemplatesCheck(root) {
2059
1037
  });
2060
1038
  }
2061
1039
 
2062
- // ../integrations/dist/registry/integration-registry.js
2063
- function createIntegrationRegistry(integrations) {
2064
- const seenIds = /* @__PURE__ */ new Set();
2065
- for (const integration of integrations) {
2066
- if (seenIds.has(integration.id)) {
2067
- throw new Error(`Duplicate integration id: "${integration.id}"`);
2068
- }
2069
- seenIds.add(integration.id);
2070
- }
2071
- return integrations;
2072
- }
2073
- function listIntegrations(registry) {
2074
- return registry;
2075
- }
2076
- function getIntegration(registry, id) {
2077
- return registry.find((integration) => integration.id === id);
2078
- }
2079
-
2080
- // ../integrations/dist/manifest/manifest.js
2081
- import { existsSync as existsSync7, lstatSync, readFileSync as readFileSync5, renameSync, unlinkSync, writeFileSync } from "node:fs";
2082
- import { join as join7 } from "node:path";
2083
- var MANIFEST_FILENAME = "archweave-integrations.json";
2084
- function createEmptyManifest() {
2085
- return { integrations: {} };
2086
- }
2087
- function loadManifest(root) {
2088
- const manifestPath = join7(root, MANIFEST_FILENAME);
2089
- if (!existsSync7(manifestPath)) {
2090
- return createEmptyManifest();
2091
- }
2092
- try {
2093
- const parsed = JSON.parse(readFileSync5(manifestPath, "utf8"));
2094
- if (parsed && typeof parsed === "object" && typeof parsed.integrations === "object") {
2095
- return { integrations: parsed.integrations ?? {} };
2096
- }
2097
- return createEmptyManifest();
2098
- } catch {
2099
- return createEmptyManifest();
2100
- }
2101
- }
2102
-
2103
1040
  // ../integrations/dist/doctor/detect-drift.js
2104
1041
  function classifyIntegrationDrift(input) {
2105
1042
  const { id, declared, manifestEntry, detected } = input;
@@ -2225,16 +1162,16 @@ function buildIntegrationDoctorChecks(results) {
2225
1162
  }
2226
1163
 
2227
1164
  // ../integrations/dist/config/read-enabled-integrations.js
2228
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
2229
- import { join as join8 } from "node:path";
1165
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
1166
+ import { join as join7 } from "node:path";
2230
1167
  var CONFIG_FILENAME = "toolkit.config.json";
2231
1168
  function readEnabledIntegrationIds(root) {
2232
- const configPath = join8(root, CONFIG_FILENAME);
2233
- if (!existsSync8(configPath)) {
1169
+ const configPath = join7(root, CONFIG_FILENAME);
1170
+ if (!existsSync7(configPath)) {
2234
1171
  return [];
2235
1172
  }
2236
1173
  try {
2237
- const parsed = JSON.parse(readFileSync6(configPath, "utf8"));
1174
+ const parsed = JSON.parse(readFileSync5(configPath, "utf8"));
2238
1175
  const enabled = parsed?.integrations?.enabled;
2239
1176
  return Array.isArray(enabled) && enabled.every((id) => typeof id === "string") ? enabled : [];
2240
1177
  } catch {
@@ -2242,654 +1179,6 @@ function readEnabledIntegrationIds(root) {
2242
1179
  }
2243
1180
  }
2244
1181
 
2245
- // ../integrations/dist/process/read-only-command-runner.js
2246
- var CLAUDE_CLI_TIMEOUT_MS = 5e3;
2247
- var CLAUDE_CLI_STDOUT_LIMIT_BYTES = 1048576;
2248
- var CLAUDE_CLI_STDERR_LIMIT_BYTES = 65536;
2249
-
2250
- // ../integrations/dist/catalog/plugin-catalog.js
2251
- var PLUGIN_HOSTS = ["claude-code"];
2252
- var PLUGIN_CATALOG = [
2253
- {
2254
- // Researched 2026-07-15. The claude-code values are the ones the manifest and verifier
2255
- // were measured with. ponytail documents official installs for ~20 hosts; only claude-code
2256
- // has an adapter.
2257
- id: "ponytail",
2258
- displayName: "ponytail",
2259
- // The project's own tagline.
2260
- description: 'over-engineering review rules and skills \u2014 "makes your AI agent think like the laziest senior dev in the room"',
2261
- homepage: "https://ponytail.dev",
2262
- repository: "https://github.com/DietrichGebert/ponytail",
2263
- license: "MIT",
2264
- npmPackage: "@dietrichgebert/ponytail",
2265
- installDocs: "https://github.com/DietrichGebert/ponytail#install",
2266
- uninstallSteps: [
2267
- "Run this from a clone of https://github.com/DietrichGebert/ponytail (or before removing the plugin below \u2014 the script is itself a plugin file, so removing the plugin first deletes it): node scripts/uninstall.js",
2268
- "Then remove the plugin via your host's own command, e.g. in Claude Code: /plugin uninstall ponytail@ponytail (or use the Claude Code Desktop app: Customize > manage marketplaces > remove plugin).",
2269
- "See https://github.com/DietrichGebert/ponytail#uninstall for the full, current official instructions."
2270
- ],
2271
- hosts: {
2272
- "claude-code": { name: "ponytail", marketplace: "DietrichGebert/ponytail", pluginId: "ponytail@ponytail" }
2273
- }
2274
- }
2275
- ];
2276
- var PLUGIN_IDS = PLUGIN_CATALOG.map((entry) => entry.id);
2277
-
2278
- // ../integrations/dist/hosts/claude-code/detect.js
2279
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
2280
- import { join as join9 } from "node:path";
2281
- async function detectWorkspacePackage(entry, context) {
2282
- const npmPackage = entry.npmPackage;
2283
- if (npmPackage === void 0) {
2284
- return { installed: false, managedBy: "unknown", evidence: [] };
2285
- }
2286
- const packageJsonPath = join9(context.workspaceRoot, "package.json");
2287
- if (!existsSync9(packageJsonPath)) {
2288
- return { installed: false, managedBy: "unknown", evidence: [] };
2289
- }
2290
- let pkg;
2291
- try {
2292
- pkg = JSON.parse(readFileSync7(packageJsonPath, "utf8"));
2293
- } catch {
2294
- return { installed: false, managedBy: "unknown", evidence: [] };
2295
- }
2296
- const deps = pkg["dependencies"] ?? {};
2297
- const devDeps = pkg["devDependencies"] ?? {};
2298
- const version = deps[npmPackage] ?? devDeps[npmPackage];
2299
- if (!version) {
2300
- return { installed: false, managedBy: "unknown", evidence: [] };
2301
- }
2302
- return {
2303
- installed: true,
2304
- installedVersion: version,
2305
- managedBy: "external-tool",
2306
- evidence: [`package.json: ${npmPackage}@${version}`]
2307
- };
2308
- }
2309
-
2310
- // ../integrations/dist/hosts/claude-code/plan.js
2311
- var VERSION_DECIDED_BY_MARKETPLACE = "latest (decided by the marketplace)";
2312
- var WORKSPACE_FILES_BY_SCOPE = {
2313
- user: [],
2314
- project: [".claude/settings.json"],
2315
- local: [".claude/settings.local.json"]
2316
- };
2317
- var DEFAULT_PLUGIN_SCOPE = "project";
2318
- function createClaudeMarketplacePlan(entry) {
2319
- return async (options) => {
2320
- const distribution = entry.hosts["claude-code"];
2321
- if (options.targetEnvironment !== "claude-code" || distribution === void 0) {
2322
- const supported = Object.entries(entry.hosts).filter(([, value]) => value !== void 0).map(([host]) => host).join(", ");
2323
- throw new Error(`Target environment "${options.targetEnvironment}" is not supported by this integration. Supported: ${supported}.`);
2324
- }
2325
- const resolvedVersion = options.versionRange ?? VERSION_DECIDED_BY_MARKETPLACE;
2326
- const scope = options.scope ?? DEFAULT_PLUGIN_SCOPE;
2327
- return {
2328
- officialCommand: {
2329
- executable: "claude",
2330
- args: [
2331
- "plugin",
2332
- "install",
2333
- distribution.name,
2334
- "--marketplace",
2335
- distribution.marketplace,
2336
- "--scope",
2337
- scope,
2338
- "--json"
2339
- ]
2340
- },
2341
- resolvedVersion,
2342
- filesCreated: [],
2343
- // project/local scope: Claude Code records the plugin in the workspace's own
2344
- // .claude settings — shown before approval as the files that will change.
2345
- filesModified: [...WORKSPACE_FILES_BY_SCOPE[scope]],
2346
- conflicts: [],
2347
- sourceRepository: entry.repository,
2348
- officialHomepage: entry.homepage,
2349
- license: entry.license,
2350
- requiresManualExecution: false
2351
- };
2352
- };
2353
- }
2354
-
2355
- // ../integrations/dist/hosts/claude-code/install.js
2356
- import { homedir } from "node:os";
2357
- function manualInstallSteps(entry) {
2358
- const distribution = entry.hosts["claude-code"];
2359
- if (distribution === void 0)
2360
- return [`See ${entry.installDocs} for the official instructions.`];
2361
- return [
2362
- `In Claude Code, send this as its own prompt: /plugin marketplace add ${distribution.marketplace}`,
2363
- `Then, as a second separate prompt: /plugin install ${distribution.pluginId}`,
2364
- `See ${entry.installDocs} for the official instructions.`
2365
- ];
2366
- }
2367
- var CLAUDE_CLI_INSTALL_TIMEOUT_MS = 12e4;
2368
- var CLAUDE_CLI_INSTALL_STDOUT_LIMIT_BYTES = 1048576;
2369
- var CLAUDE_CLI_INSTALL_STDERR_LIMIT_BYTES = 262144;
2370
- function defaultRerunCommand(entry, runNx = "pnpm nx") {
2371
- return `${runNx} g @clearlydev/nx-archweave:install-integration --tool=${entry.id} --apply`;
2372
- }
2373
- function chatAlternative(entry) {
2374
- return ["Alternatively, inside a Claude Code session (the chat path):", ...manualInstallSteps(entry)];
2375
- }
2376
- var MARKETPLACE_NOTE = "If the marketplace was registered before the failure, it stays in your user settings \u2014 see `claude plugin marketplace list`.";
2377
- function claudeCliNotFoundSteps(entry, rerunCommand) {
2378
- return [
2379
- "The claude CLI was not found on PATH. Install the Claude Code CLI from your terminal: npm install -g @anthropic-ai/claude-code (setup guide: https://code.claude.com/docs/en/setup)",
2380
- `Then rerun: ${rerunCommand}`,
2381
- ...chatAlternative(entry)
2382
- ];
2383
- }
2384
- function claudeCliUnsupportedSteps(entry, rerunCommand, cliSaid) {
2385
- return [
2386
- `Your claude CLI does not know this command (it said: ${cliSaid}). Scriptable plugin install needs Claude Code 2.1.285 or newer: run \`claude update\`, or reinstall it with npm install -g @anthropic-ai/claude-code@latest`,
2387
- `Then rerun: ${rerunCommand}`,
2388
- ...chatAlternative(entry)
2389
- ];
2390
- }
2391
- var TAIL_CHARS = 500;
2392
- var UNSUPPORTED_CLI_PATTERN = /error: unknown (command|option) '(plugin|install|marketplace|--json|--marketplace|--scope)'/;
2393
- function tail(text) {
2394
- const trimmed = text.trim();
2395
- return trimmed.length > TAIL_CHARS ? trimmed.slice(-TAIL_CHARS) : trimmed;
2396
- }
2397
- var ESCAPED_HOME = homedir().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2398
- var HOME_PATTERN = new RegExp(`${ESCAPED_HOME}(?=[/\\\\\\s'"\`]|$)`, "g");
2399
- function redactHomePaths(text) {
2400
- let out = ESCAPED_HOME.length > 1 ? text.replace(HOME_PATTERN, "~") : text;
2401
- out = out.replace(/\/(?:Users|home)\/[^/\s'"`]+/g, "~");
2402
- out = out.replace(/[A-Za-z]:\\Users\\[^\\\s'"`]+/g, "~");
2403
- out = out.replace(/\/root(?=[/\s'"`]|$)/g, "~");
2404
- return out;
2405
- }
2406
- function parseLastJsonObject(stdout) {
2407
- const lines = stdout.split("\n");
2408
- for (let i = lines.length - 1; i >= 0; i--) {
2409
- const line = lines[i].trim();
2410
- if (!line.startsWith("{"))
2411
- continue;
2412
- try {
2413
- const parsed = JSON.parse(line);
2414
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed))
2415
- return parsed;
2416
- } catch {
2417
- }
2418
- }
2419
- return null;
2420
- }
2421
- function renderUserCommand(command) {
2422
- return [command.executable, ...command.args.filter((arg) => arg !== "--json")].join(" ");
2423
- }
2424
- async function classifyRun(runner, command, cwd) {
2425
- let raw;
2426
- try {
2427
- raw = await runner.run({
2428
- executable: command.executable,
2429
- argv: command.args,
2430
- ...cwd === void 0 ? {} : { cwd },
2431
- timeoutMs: CLAUDE_CLI_INSTALL_TIMEOUT_MS,
2432
- stdoutLimitBytes: CLAUDE_CLI_INSTALL_STDOUT_LIMIT_BYTES,
2433
- stderrLimitBytes: CLAUDE_CLI_INSTALL_STDERR_LIMIT_BYTES
2434
- });
2435
- } catch (error) {
2436
- const code = typeof error?.code === "string" ? error.code : "unknown";
2437
- return code === "ENOENT" ? { kind: "cli-not-found" } : { kind: "failed-to-start", code };
2438
- }
2439
- if (raw.timedOut)
2440
- return { kind: "timeout" };
2441
- if (raw.outputExceeded)
2442
- return { kind: "output-too-large" };
2443
- const json = parseLastJsonObject(raw.stdout);
2444
- const jsonSaysFailed = json !== null && json["outcome"] === "failed";
2445
- if (raw.exitCode === 0 && !jsonSaysFailed)
2446
- return { kind: "executed" };
2447
- if (json !== null) {
2448
- const message = redactHomePaths(typeof json["message"] === "string" ? json["message"] : `the claude CLI exited with code ${raw.exitCode}`);
2449
- const failureCode = typeof json["failureCode"] === "string" ? json["failureCode"] : void 0;
2450
- const shown = json["shownCommand"];
2451
- if (typeof shown === "object" && shown !== null)
2452
- return { kind: "confirmation-required", message };
2453
- return failureCode === void 0 ? { kind: "failed", message } : { kind: "failed", failureCode, message };
2454
- }
2455
- if (UNSUPPORTED_CLI_PATTERN.test(raw.stderr)) {
2456
- const line = raw.stderr.split("\n").find((l) => UNSUPPORTED_CLI_PATTERN.test(l)) ?? "";
2457
- return { kind: "cli-unsupported", stderrTail: redactHomePaths(line.trim()) };
2458
- }
2459
- const said = redactHomePaths(tail(raw.stderr.length > 0 ? raw.stderr : raw.stdout));
2460
- return { kind: "failed", message: said.length > 0 ? said : `the claude CLI exited with code ${raw.exitCode}` };
2461
- }
2462
- function manualStepsFor(entry, detail, command, rerunCommand) {
2463
- const chat = chatAlternative(entry);
2464
- const userCommand = command === null ? "" : renderUserCommand(command);
2465
- const rerun = `Once resolved, rerun to install, verify and record it: ${rerunCommand}`;
2466
- switch (detail.kind) {
2467
- case "executed":
2468
- return [];
2469
- case "manual-only":
2470
- return manualInstallSteps(entry);
2471
- case "cli-not-found":
2472
- return claudeCliNotFoundSteps(entry, rerunCommand);
2473
- case "failed-to-start":
2474
- return [
2475
- `The claude CLI was found but could not be started (${detail.code}) \u2014 check its permissions and your system limits.`,
2476
- rerun,
2477
- ...chat
2478
- ];
2479
- case "cli-unsupported":
2480
- return claudeCliUnsupportedSteps(entry, rerunCommand, detail.stderrTail);
2481
- case "confirmation-required":
2482
- return [
2483
- `The marketplace declares a command that must be confirmed by a person; nx-archweave never accepts it for you (${detail.message})`,
2484
- `Review it and run the install yourself: ${userCommand}`,
2485
- rerun,
2486
- ...chat
2487
- ];
2488
- case "failed":
2489
- return [`The install command did not succeed: ${detail.message}`, MARKETPLACE_NOTE, rerun, ...chat];
2490
- case "timeout":
2491
- return [
2492
- `The install command did not finish within ${CLAUDE_CLI_INSTALL_TIMEOUT_MS / 1e3} s and was stopped.`,
2493
- MARKETPLACE_NOTE,
2494
- rerun,
2495
- ...chat
2496
- ];
2497
- case "output-too-large":
2498
- return [
2499
- "The install command produced more output than nx-archweave is willing to buffer and was stopped.",
2500
- `Run it yourself to see the full output: ${userCommand}`,
2501
- rerun,
2502
- ...chat
2503
- ];
2504
- }
2505
- }
2506
- async function installClaudeMarketplacePlugin(entry, deps, plan, options = {}) {
2507
- const rerunCommand = options.rerunCommand ?? defaultRerunCommand(entry);
2508
- const detail = plan.officialCommand === null ? { kind: "manual-only" } : await classifyRun(deps.runner, plan.officialCommand, options.cwd);
2509
- const executed = detail.kind === "executed";
2510
- const result = {
2511
- executed,
2512
- // One command: it either reached exit 0 or it did not. What a failed run may
2513
- // have left behind is re-checked live by install-integration, not guessed here.
2514
- partialFailure: false,
2515
- filesCreated: [],
2516
- // The workspace files the plan said the host would change (project/local scope);
2517
- // Claude's own plugin store is outside the workspace and never listed.
2518
- filesModified: executed ? [...plan.filesModified] : [],
2519
- manualStepsRemaining: manualStepsFor(entry, detail, plan.officialCommand, rerunCommand)
2520
- };
2521
- return { result, detail };
2522
- }
2523
- function createClaudeMarketplaceInstall(entry, deps) {
2524
- return async (plan) => (await installClaudeMarketplacePlugin(entry, deps, plan)).result;
2525
- }
2526
-
2527
- // ../integrations/dist/hosts/claude-code/verify-via-claude-cli.js
2528
- import { realpathSync } from "node:fs";
2529
- import { resolve } from "node:path";
2530
-
2531
- // ../integrations/dist/hosts/claude-code/parse-claude-plugin-list.js
2532
- var KNOWN_SCOPES = /* @__PURE__ */ new Set(["user", "project", "local", "managed"]);
2533
- function isKnownScope(scope) {
2534
- return KNOWN_SCOPES.has(scope);
2535
- }
2536
- function isValidEntry(value) {
2537
- if (typeof value !== "object" || value === null) {
2538
- return false;
2539
- }
2540
- const record = value;
2541
- if (typeof record["id"] !== "string") {
2542
- return false;
2543
- }
2544
- if (typeof record["enabled"] !== "boolean") {
2545
- return false;
2546
- }
2547
- if (typeof record["scope"] !== "string" || record["scope"].length === 0) {
2548
- return false;
2549
- }
2550
- if (record["version"] !== void 0 && typeof record["version"] !== "string") {
2551
- return false;
2552
- }
2553
- return true;
2554
- }
2555
- function parseClaudePluginList(rawStdout, options = {}) {
2556
- let parsed;
2557
- try {
2558
- parsed = JSON.parse(rawStdout);
2559
- } catch {
2560
- return { ok: false, reason: "invalid-json" };
2561
- }
2562
- if (!Array.isArray(parsed)) {
2563
- return { ok: false, reason: "non-array" };
2564
- }
2565
- const entries = [];
2566
- const otherScopeIds = [];
2567
- for (const item of parsed) {
2568
- if (!isValidEntry(item)) {
2569
- return { ok: false, reason: "missing-fields" };
2570
- }
2571
- if (typeof item.projectPath === "string" && options.isThisWorkspace && !options.isThisWorkspace(item.projectPath)) {
2572
- continue;
2573
- }
2574
- const scope = item.scope;
2575
- if (!isKnownScope(scope)) {
2576
- otherScopeIds.push(item.id);
2577
- continue;
2578
- }
2579
- entries.push({
2580
- id: item.id,
2581
- version: item.version,
2582
- scope,
2583
- enabled: item.enabled
2584
- });
2585
- }
2586
- return { ok: true, entries, otherScopeIds };
2587
- }
2588
-
2589
- // ../integrations/dist/hosts/claude-code/match-claude-plugin.js
2590
- function matchClaudePlugin(entries, pluginId) {
2591
- const matches = entries.filter((candidate) => candidate.id === pluginId);
2592
- if (matches.length === 0) {
2593
- return { kind: "absent" };
2594
- }
2595
- if (matches.length > 1) {
2596
- return { kind: "ambiguous", entries: matches };
2597
- }
2598
- return { kind: "matched", entry: matches[0] };
2599
- }
2600
-
2601
- // ../integrations/dist/hosts/claude-code/claude-cli-verification-result.js
2602
- function evaluateClaudeCliMatch(match) {
2603
- switch (match.kind) {
2604
- case "matched":
2605
- return match.entry.enabled ? { status: "verified", version: match.entry.version, scope: match.entry.scope } : { status: "installed-but-disabled", scope: match.entry.scope };
2606
- case "absent":
2607
- return { status: "absent" };
2608
- case "ambiguous":
2609
- return { status: "ambiguous" };
2610
- }
2611
- }
2612
-
2613
- // ../integrations/dist/hosts/claude-code/verify-via-claude-cli.js
2614
- async function verifyViaClaudeCli(runner, pluginId, cwd) {
2615
- let raw;
2616
- try {
2617
- raw = await runner.run({
2618
- executable: "claude",
2619
- argv: ["plugin", "list", "--json"],
2620
- ...cwd === void 0 ? {} : { cwd },
2621
- timeoutMs: CLAUDE_CLI_TIMEOUT_MS,
2622
- stdoutLimitBytes: CLAUDE_CLI_STDOUT_LIMIT_BYTES,
2623
- stderrLimitBytes: CLAUDE_CLI_STDERR_LIMIT_BYTES
2624
- });
2625
- } catch {
2626
- return { source: "claude-cli-unavailable", reason: "not-found" };
2627
- }
2628
- if (raw.timedOut) {
2629
- return { source: "claude-cli-unavailable", reason: "timeout" };
2630
- }
2631
- if (raw.outputExceeded) {
2632
- return { source: "claude-cli-unavailable", reason: "output-too-large" };
2633
- }
2634
- if (raw.exitCode !== 0) {
2635
- return { source: "claude-cli-unavailable", reason: "non-zero-exit" };
2636
- }
2637
- const parsed = parseClaudePluginList(raw.stdout, cwd === void 0 ? {} : { isThisWorkspace: samePathAs(cwd) });
2638
- if (!parsed.ok) {
2639
- return {
2640
- source: "claude-cli-unavailable",
2641
- reason: parsed.reason === "invalid-json" ? "invalid-json" : "unexpected-schema"
2642
- };
2643
- }
2644
- const match = matchClaudePlugin(parsed.entries, pluginId);
2645
- if (parsed.otherScopeIds.includes(pluginId)) {
2646
- return match.kind === "absent" ? { source: "claude-cli-unavailable", reason: "unexpected-schema" } : { source: "claude-cli", outcome: { status: "ambiguous" } };
2647
- }
2648
- return { source: "claude-cli", outcome: evaluateClaudeCliMatch(match) };
2649
- }
2650
- function canonical(path) {
2651
- try {
2652
- return realpathSync(path);
2653
- } catch {
2654
- return resolve(path);
2655
- }
2656
- }
2657
- function samePathAs(workspaceRoot) {
2658
- const root = canonical(workspaceRoot);
2659
- return (projectPath) => canonical(projectPath) === root;
2660
- }
2661
-
2662
- // ../integrations/dist/hosts/claude-code/verify.js
2663
- async function verifyClaudeMarketplacePlugin(entry, deps, context) {
2664
- const detect = deps.detect ?? detectWorkspacePackage;
2665
- const pluginId = entry.hosts["claude-code"]?.pluginId;
2666
- if (pluginId === void 0) {
2667
- throw new Error(`${entry.displayName} has no Claude Code distribution \u2014 it cannot be verified through the claude CLI.`);
2668
- }
2669
- const cliResult = await verifyViaClaudeCli(deps.runner, pluginId, context.workspaceRoot);
2670
- if (cliResult.source === "claude-cli") {
2671
- switch (cliResult.outcome.status) {
2672
- case "verified":
2673
- return {
2674
- source: "claude-cli",
2675
- result: { verified: true, resolvedVersion: cliResult.outcome.version, problems: [] }
2676
- };
2677
- case "installed-but-disabled":
2678
- return {
2679
- source: "claude-cli",
2680
- result: {
2681
- verified: false,
2682
- problems: [
2683
- `${entry.displayName} is installed (scope: ${cliResult.outcome.scope}) but currently disabled in Claude Code.`,
2684
- "Enable it before recording this integration \u2014 a disabled plugin is never recorded as verified."
2685
- ]
2686
- }
2687
- };
2688
- case "absent":
2689
- return {
2690
- source: "claude-cli",
2691
- result: {
2692
- verified: false,
2693
- problems: [
2694
- `${pluginId} was not found in the installed Claude Code plugin list.`,
2695
- `Install it (${defaultRerunCommand(entry)}) or complete the manual Claude Code steps, then try again.`
2696
- ]
2697
- }
2698
- };
2699
- case "ambiguous":
2700
- return {
2701
- source: "claude-cli",
2702
- result: {
2703
- verified: false,
2704
- problems: [
2705
- `Verification error: more than one installed plugin matches ${pluginId} exactly. This is ambiguous and cannot be safely resolved automatically.`
2706
- ]
2707
- }
2708
- };
2709
- }
2710
- }
2711
- if (cliResult.reason === "not-found") {
2712
- const detected = await detect(entry, context);
2713
- if (detected.installed) {
2714
- return {
2715
- source: "package-json",
2716
- result: { verified: true, resolvedVersion: detected.installedVersion, problems: [] }
2717
- };
2718
- }
2719
- return {
2720
- source: "package-json",
2721
- result: {
2722
- verified: false,
2723
- problems: [
2724
- `The claude CLI was not found, and ${entry.displayName} was not found as a workspace dependency either.`,
2725
- `Install the Claude Code CLI to use the authoritative verification signal, or add ${entry.displayName} as a workspace dependency for the weaker fallback signal.`
2726
- ]
2727
- }
2728
- };
2729
- }
2730
- return {
2731
- source: "claude-cli",
2732
- result: {
2733
- verified: false,
2734
- problems: [`Verification error: the claude CLI returned an unexpected result (${cliResult.reason}).`]
2735
- }
2736
- };
2737
- }
2738
- function createClaudeMarketplaceVerifier(entry, deps) {
2739
- return async (context) => (await verifyClaudeMarketplacePlugin(entry, deps, context)).result;
2740
- }
2741
-
2742
- // ../integrations/dist/catalog/create-catalog-integration.js
2743
- function createCatalogIntegration(entry, deps) {
2744
- return {
2745
- id: entry.id,
2746
- displayName: entry.displayName,
2747
- homepage: entry.homepage,
2748
- repository: entry.repository,
2749
- license: entry.license,
2750
- supportedEnvironments: PLUGIN_HOSTS.filter((host) => entry.hosts[host] !== void 0),
2751
- detect: (context) => detectWorkspacePackage(entry, context),
2752
- plan: createClaudeMarketplacePlan(entry),
2753
- install: createClaudeMarketplaceInstall(entry, { runner: deps.runner }),
2754
- verify: createClaudeMarketplaceVerifier(entry, { runner: deps.runner }),
2755
- uninstallInstructions: async () => [...entry.uninstallSteps]
2756
- };
2757
- }
2758
- function createCatalogIntegrations(deps) {
2759
- return PLUGIN_CATALOG.map((entry) => createCatalogIntegration(entry, deps));
2760
- }
2761
-
2762
- // src/runtime/node-command-runner.ts
2763
- import { spawn } from "node:child_process";
2764
- var KILL_GRACE_MS = 500;
2765
- var NodeCommandRunner = class {
2766
- run(input) {
2767
- return new Promise((resolve2, reject) => {
2768
- let stdout = "";
2769
- let stderr = "";
2770
- let timedOut = false;
2771
- let outputExceeded = false;
2772
- let settled = false;
2773
- let killTimer;
2774
- const spawnable = spawnableCommand(input.executable, input.argv);
2775
- const child = spawn(spawnable.command, spawnable.args, {
2776
- cwd: input.cwd,
2777
- shell: false,
2778
- windowsVerbatimArguments: spawnable.windowsVerbatimArguments,
2779
- windowsHide: true,
2780
- // No stdin: nothing here ever answers a prompt (a command that wants
2781
- // confirmation must fail fast instead of waiting on an open pipe).
2782
- stdio: ["ignore", "pipe", "pipe"]
2783
- });
2784
- const terminate = () => {
2785
- child.kill("SIGTERM");
2786
- killTimer = setTimeout(() => {
2787
- child.kill("SIGKILL");
2788
- }, KILL_GRACE_MS);
2789
- };
2790
- const timer = setTimeout(() => {
2791
- timedOut = true;
2792
- terminate();
2793
- }, input.timeoutMs);
2794
- child.stdout.on("data", (chunk) => {
2795
- stdout += chunk.toString("utf8");
2796
- if (Buffer.byteLength(stdout, "utf8") > input.stdoutLimitBytes) {
2797
- outputExceeded = true;
2798
- terminate();
2799
- }
2800
- });
2801
- child.stderr.on("data", (chunk) => {
2802
- stderr += chunk.toString("utf8");
2803
- if (Buffer.byteLength(stderr, "utf8") > input.stderrLimitBytes) {
2804
- outputExceeded = true;
2805
- terminate();
2806
- }
2807
- });
2808
- child.on("error", (error) => {
2809
- if (settled) return;
2810
- settled = true;
2811
- clearTimeout(timer);
2812
- clearTimeout(killTimer);
2813
- reject(error);
2814
- });
2815
- child.on("close", (code) => {
2816
- if (settled) return;
2817
- settled = true;
2818
- clearTimeout(timer);
2819
- clearTimeout(killTimer);
2820
- resolve2({ stdout, stderr, exitCode: code, timedOut, outputExceeded });
2821
- });
2822
- });
2823
- }
2824
- };
2825
-
2826
- // src/runtime/integrations-composition-root.ts
2827
- function createIntegrationsCompositionRoot(deps = {}) {
2828
- const runner = deps.runner ?? new NodeCommandRunner();
2829
- const registry = createIntegrationRegistry(createCatalogIntegrations({ runner }));
2830
- return {
2831
- registry,
2832
- runner,
2833
- getIntegration: (id) => getIntegration(registry, id),
2834
- listIntegrations: () => listIntegrations(registry)
2835
- };
2836
- }
2837
-
2838
- // src/generators/shared/run-nx.ts
2839
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
2840
- import { join as join10 } from "node:path";
2841
-
2842
- // src/generators/shared/toolkit-config-file.ts
2843
- var TOOLKIT_CONFIG_PATH = "toolkit.config.json";
2844
- function readToolkitConfigFile(tree) {
2845
- const raw = tree.read(TOOLKIT_CONFIG_PATH, "utf-8");
2846
- if (raw === null) return { raw: null, parsed: null };
2847
- try {
2848
- const parsed = JSON.parse(raw);
2849
- return {
2850
- raw,
2851
- parsed: typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null
2852
- };
2853
- } catch {
2854
- return { raw, parsed: null };
2855
- }
2856
- }
2857
- function readToolkitConfig(tree) {
2858
- return readToolkitConfigFile(tree).parsed;
2859
- }
2860
- function recordedChoice(config, key, allowed) {
2861
- const value = config?.[key];
2862
- return typeof value === "string" && allowed.includes(value) ? value : void 0;
2863
- }
2864
- var RECORDED_PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
2865
- function workspacePackageManager(exists, read, config) {
2866
- return detectWorkspacePackageManager(exists, read) ?? recordedChoice(config, "packageManager", RECORDED_PACKAGE_MANAGERS) ?? null;
2867
- }
2868
- function recordedFrontends(config) {
2869
- const frontends = config?.["frontends"];
2870
- if (!Array.isArray(frontends)) return [];
2871
- return frontends.flatMap(
2872
- (entry) => typeof entry === "object" && entry !== null && typeof entry["project"] === "string" ? [entry.project] : []
2873
- );
2874
- }
2875
-
2876
- // src/generators/shared/run-nx.ts
2877
- function runNxFor(exists, read) {
2878
- let config = null;
2879
- try {
2880
- const parsed = JSON.parse(read(TOOLKIT_CONFIG_PATH) ?? "null");
2881
- if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) config = parsed;
2882
- } catch {
2883
- }
2884
- return packageManagerCommands(workspacePackageManager(exists, read, config) ?? "pnpm").runNx;
2885
- }
2886
- function diskRunNx(root) {
2887
- return runNxFor(
2888
- (path) => existsSync10(join10(root, path)),
2889
- (path) => existsSync10(join10(root, path)) ? readFileSync8(join10(root, path), "utf-8") : void 0
2890
- );
2891
- }
2892
-
2893
1182
  // src/generators/doctor/integration-report.ts
2894
1183
  var DOCTOR_TARGET_ENVIRONMENT = "claude-code";
2895
1184
  async function buildDoctorReportWithIntegrations(root, options = {}, deps = {}) {
@@ -2941,267 +1230,10 @@ async function applyLiveVerification(driftResults, ctx) {
2941
1230
  }
2942
1231
 
2943
1232
  // src/generators/doctor/workspace-checks.ts
2944
- import { getProjects as getProjects2, joinPathFragments } from "@nx/devkit";
2945
-
2946
- // src/eslint/fsd-policy.ts
2947
- var PRESETS = {
2948
- basic: {
2949
- sliceGroups: { auto: true, include: [], exclude: [] },
2950
- sharedPublicApi: "segment",
2951
- testFiles: false,
2952
- segments: [],
2953
- platformSuffixes: [],
2954
- routerDirs: []
2955
- },
2956
- practical: {
2957
- sliceGroups: { auto: true, include: [], exclude: [] },
2958
- sharedPublicApi: "file",
2959
- testFiles: ["**/*.{spec,test}.*", "**/__tests__/**"],
2960
- segments: [],
2961
- platformSuffixes: [],
2962
- routerDirs: []
2963
- }
2964
- };
2965
- var isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
2966
- function resolveFsdPolicy(recorded, overrides = {}) {
2967
- const fsd = typeof recorded === "object" && recorded !== null ? recorded : {};
2968
- const preset = fsd["preset"] === "practical" ? "practical" : "basic";
2969
- const policy = { ...PRESETS[preset] };
2970
- const groups = fsd["sliceGroups"];
2971
- if (groups === false) policy.sliceGroups = false;
2972
- else if (groups === "auto") policy.sliceGroups = { auto: true, include: [], exclude: [] };
2973
- else if (typeof groups === "object" && groups !== null) {
2974
- const value = groups;
2975
- policy.sliceGroups = {
2976
- auto: value["auto"] !== false,
2977
- include: isStringArray(value["include"]) ? value["include"] : [],
2978
- exclude: isStringArray(value["exclude"]) ? value["exclude"] : []
2979
- };
2980
- }
2981
- const shared = fsd["sharedPublicApi"];
2982
- if (shared === "segment" || shared === "component" || shared === "file") policy.sharedPublicApi = shared;
2983
- const tests = fsd["testFiles"];
2984
- if (tests === false || isStringArray(tests)) policy.testFiles = tests;
2985
- if (isStringArray(fsd["segments"])) policy.segments = fsd["segments"];
2986
- if (isStringArray(fsd["platformSuffixes"])) policy.platformSuffixes = fsd["platformSuffixes"];
2987
- if (isStringArray(fsd["routerDirs"])) policy.routerDirs = fsd["routerDirs"];
2988
- for (const [key, value] of Object.entries(overrides)) {
2989
- if (value !== void 0) policy[key] = value;
2990
- }
2991
- return policy;
2992
- }
2993
-
2994
- // src/eslint/slice-groups.ts
2995
- import { readdirSync as readdirSync5, statSync as statSync2 } from "node:fs";
2996
- import { join as join11 } from "node:path";
2997
- var SEGMENTS = [
2998
- "ui",
2999
- "model",
3000
- "api",
3001
- "lib",
3002
- "config",
3003
- "components",
3004
- "hooks",
3005
- "types",
3006
- "utils",
3007
- "helpers",
3008
- "constants",
3009
- "consts",
3010
- "store",
3011
- "stores",
3012
- "styles",
3013
- "assets",
3014
- "services",
3015
- "context"
3016
- ];
3017
- var IGNORED = /^(\.|readme(\.|$))/i;
3018
- var listings = /* @__PURE__ */ new Map();
3019
- function list(dir) {
3020
- let mtimeMs;
3021
- try {
3022
- mtimeMs = statSync2(dir).mtimeMs;
3023
- } catch {
3024
- listings.delete(dir);
3025
- return null;
3026
- }
3027
- const cached = listings.get(dir);
3028
- if (cached !== void 0 && cached.mtimeMs === mtimeMs) return cached.entries;
3029
- try {
3030
- const entries = readdirSync5(dir, { withFileTypes: true }).map((entry) => ({ name: entry.name, directory: entry.isDirectory() }));
3031
- listings.set(dir, { mtimeMs, entries });
3032
- return entries;
3033
- } catch {
3034
- return null;
3035
- }
3036
- }
3037
- function createGroupDetector(rootAbsolute, policy, isIndexFile, listDirectory = list) {
3038
- const groups = policy.sliceGroups;
3039
- if (groups === false) return () => false;
3040
- const segments = /* @__PURE__ */ new Set([...SEGMENTS, ...policy.segments]);
3041
- return (layerDir, name) => {
3042
- const key = `${layerDir}/${name}`;
3043
- if (groups.include.includes(key)) return true;
3044
- if (groups.exclude.includes(key) || !groups.auto) return false;
3045
- const entries = listDirectory(join11(rootAbsolute, layerDir, name))?.filter((entry) => !IGNORED.test(entry.name)) ?? null;
3046
- if (entries === null || entries.length === 0) return false;
3047
- if (entries.some((entry) => !entry.directory || segments.has(entry.name))) return false;
3048
- return entries.some((entry) => listDirectory(join11(rootAbsolute, layerDir, name, entry.name))?.some((child) => !child.directory && isIndexFile(child.name)) ?? false);
3049
- };
3050
- }
3051
-
3052
- // src/eslint/fsd-boundaries.ts
3053
- function indexFilePattern(platformSuffixes) {
3054
- const platforms = platformSuffixes.length === 0 ? "" : `(\\.(${platformSuffixes.map((suffix) => suffix.replace(/[^a-z0-9-]/gi, "")).join("|")}))?`;
3055
- return new RegExp(`^index${platforms}(\\.[cm]?[jt]sx?)?$`);
3056
- }
3057
- var DEFAULT_POLICY = resolveFsdPolicy(void 0);
3058
-
3059
- // src/generators/shared/frontend-app.ts
3060
- import { getProjects } from "@nx/devkit";
3061
-
3062
- // src/generators/shared/project-type.ts
3063
- import { parseJson } from "@nx/devkit";
3064
- function resolveProjectType(tree, root, declared) {
3065
- if (declared) return declared === "library" ? "library" : "application";
3066
- const prefix = root === "" || root === "." ? "" : `${root}/`;
3067
- if (tree.exists(`${prefix}tsconfig.lib.json`)) return "library";
3068
- if (tree.exists(`${prefix}tsconfig.app.json`)) return "application";
3069
- const raw = tree.read(`${prefix}package.json`, "utf-8");
3070
- if (raw === null) return "library";
3071
- try {
3072
- const manifest = parseJson(raw);
3073
- if (manifest && !["exports", "main", "module", "bin"].some((key) => Boolean(manifest[key]))) return "application";
3074
- } catch {
3075
- }
3076
- return "library";
3077
- }
3078
-
3079
- // src/generators/shared/frontend-app.ts
3080
- var baseNameOf = (root) => root.split("/").pop() ?? root;
3081
- function isEndToEnd(root) {
3082
- const dir = baseNameOf(root);
3083
- return dir === "e2e" || dir.endsWith("-e2e");
3084
- }
3085
- function isBackend(root, config, backendTag) {
3086
- return baseNameOf(root) === "api" || (config.tags ?? []).includes(backendTag);
3087
- }
3088
- function prefixOf(root) {
3089
- return root === "" || root === "." ? "" : `${root}/`;
3090
- }
3091
- function sourceRootOf(tree, config) {
3092
- const prefix = prefixOf(config.root);
3093
- const declared = config.sourceRoot?.replace(/\/$/, "");
3094
- const atRoot = declared !== void 0 && (declared === config.root || prefix === "" && (declared === "." || declared === ""));
3095
- if (declared && !atRoot) return declared;
3096
- if (atRoot && !tree.exists(`${prefix}src`)) return prefix === "" ? "." : config.root;
3097
- return `${prefix}src`;
3098
- }
3099
- function frameworkOf(tree, config) {
3100
- return detectFramework({
3101
- exists: (path) => tree.exists(path),
3102
- read: (path) => tree.read(path, "utf-8"),
3103
- projectRoot: config.root,
3104
- executors: Object.values(config.targets ?? {}).flatMap((target) => target.executor ? [target.executor] : [])
3105
- });
3106
- }
3107
- function resolveFrontendApp(tree, explicitProject) {
3108
- let projects;
3109
- try {
3110
- projects = getProjects(tree);
3111
- } catch (error) {
3112
- const message = error instanceof Error ? error.message : String(error);
3113
- const pathMatch = /Cannot parse ([^:]+):/.exec(message);
3114
- return { kind: "invalid-project-json", detail: pathMatch ? pathMatch[1] : message };
3115
- }
3116
- const found = (project2, config2) => {
3117
- const profile = frameworkOf(tree, config2);
3118
- return { kind: "found", project: project2, root: config2.root, sourceRoot: sourceRootOf(tree, config2), framework: profile.id, profile };
3119
- };
3120
- if (explicitProject !== void 0) {
3121
- const config2 = projects.get(explicitProject);
3122
- if (config2) return found(explicitProject, config2);
3123
- const rootApp = inferredRootApp(tree, projects);
3124
- return rootApp?.kind === "found" && rootApp.project === explicitProject ? rootApp : { kind: "not-found", project: explicitProject };
3125
- }
3126
- const backendTag = readBoundaryTags(readToolkitConfig(tree)).backend;
3127
- const eligible = [...projects.entries()].filter(([, config2]) => !isEndToEnd(config2.root) && !isBackend(config2.root, config2, backendTag));
3128
- const declared = eligible.filter(([, config2]) => config2.projectType === "application");
3129
- const inferred = eligible.filter(([, config2]) => {
3130
- if (config2.projectType !== void 0) return false;
3131
- if (config2.root === "" || config2.root === ".") return frameworkOf(tree, config2).recognised;
3132
- return resolveProjectType(tree, config2.root, void 0) === "application";
3133
- });
3134
- const candidates = (declared.length > 0 ? declared : inferred).sort(([a], [b]) => a.localeCompare(b));
3135
- if (candidates.length === 0) {
3136
- const rootApp = inferredRootApp(tree, projects);
3137
- if (rootApp !== null) return rootApp;
3138
- const otherProjects = [...projects.keys()].filter((name) => !isEndToEnd(projects.get(name)?.root ?? "")).sort();
3139
- return { kind: "none", otherProjects };
3140
- }
3141
- if (candidates.length > 1) return { kind: "ambiguous", candidates: candidates.map(([name]) => name) };
3142
- const [project, config] = candidates[0];
3143
- return found(project, config);
3144
- }
3145
- function inferredRootApp(tree, projects) {
3146
- if ([...projects.values()].some((config2) => config2.root === "" || config2.root === ".")) return null;
3147
- const raw = tree.read("package.json", "utf-8");
3148
- if (raw === null) return null;
3149
- let name;
3150
- try {
3151
- name = JSON.parse(raw).name;
3152
- } catch {
3153
- return null;
3154
- }
3155
- if (typeof name !== "string" || name === "") return null;
3156
- const config = { name, root: "." };
3157
- const framework = frameworkOf(tree, config);
3158
- if (!framework.recognised) return null;
3159
- return { kind: "found", project: name, root: ".", sourceRoot: sourceRootOf(tree, config), framework: framework.id, profile: framework, inferredRoot: true };
3160
- }
3161
-
3162
- // src/generators/slice/core.ts
3163
- function hasDependency(tree, manifest, name) {
3164
- const raw = tree.read(manifest, "utf-8");
3165
- if (raw === null) return false;
3166
- try {
3167
- const parsed = JSON.parse(raw);
3168
- return parsed["dependencies"]?.[name] !== void 0 || parsed["devDependencies"]?.[name] !== void 0;
3169
- } catch {
3170
- return false;
3171
- }
3172
- }
3173
- function detectUnitTestRunner(tree, projectRoot) {
3174
- const prefix = projectRoot === "" || projectRoot === "." ? "" : `${projectRoot}/`;
3175
- const project = tree.read(`${prefix}project.json`, "utf-8");
3176
- if (project !== null) {
3177
- try {
3178
- const targets = JSON.parse(project).targets ?? {};
3179
- for (const target of Object.values(targets)) {
3180
- if (target?.executor?.startsWith("@nx/jest:")) return "jest";
3181
- if (target?.executor?.startsWith("@nx/vite:test") || target?.executor?.startsWith("@nx/vitest:")) return "vitest";
3182
- }
3183
- } catch {
3184
- }
3185
- }
3186
- for (const dir of prefix === "" ? [""] : [prefix, ""]) {
3187
- if (["ts", "mts", "cts", "js", "mjs", "cjs"].some((ext) => tree.exists(`${dir}jest.config.${ext}`))) return "jest";
3188
- if (["ts", "mts", "js", "mjs"].some((ext) => tree.exists(`${dir}vitest.config.${ext}`) || tree.exists(`${dir}vitest.workspace.${ext}`))) return "vitest";
3189
- for (const ext of ["ts", "mts", "js", "mjs"]) {
3190
- const vite = tree.read(`${dir}vite.config.${ext}`, "utf-8");
3191
- if (vite !== null && /\btest\s*:/.test(vite)) return "vitest";
3192
- }
3193
- }
3194
- for (const manifest of prefix === "" ? ["package.json"] : [`${prefix}package.json`, "package.json"]) {
3195
- if (hasDependency(tree, manifest, "vitest")) return "vitest";
3196
- if (hasDependency(tree, manifest, "jest")) return "jest";
3197
- }
3198
- return "none";
3199
- }
3200
-
3201
- // src/generators/doctor/workspace-checks.ts
1233
+ import { getProjects, joinPathFragments } from "@nx/devkit";
3202
1234
  function doctorProjects(tree) {
3203
1235
  try {
3204
- return [...getProjects2(tree).entries()].map(([name, config]) => ({ name, root: config.root, sourceRoot: sourceRootOf(tree, config), tags: config.tags ?? [] })).sort((a, b) => a.name.localeCompare(b.name));
1236
+ return [...getProjects(tree).entries()].map(([name, config]) => ({ name, root: config.root, sourceRoot: sourceRootOf(tree, config), tags: config.tags ?? [] })).sort((a, b) => a.name.localeCompare(b.name));
3205
1237
  } catch {
3206
1238
  return void 0;
3207
1239
  }
@@ -3344,9 +1376,9 @@ function buildPolicySuggestion(tree, sourceRoot, current) {
3344
1376
  }
3345
1377
 
3346
1378
  // src/generators/doctor/nx-support.ts
3347
- import { readFileSync as readFileSync9 } from "node:fs";
1379
+ import { readFileSync as readFileSync6 } from "node:fs";
3348
1380
  import { createRequire } from "node:module";
3349
- import { join as join12 } from "node:path";
1381
+ import { join as join8 } from "node:path";
3350
1382
  var MAJORS_EITHER_SIDE = 1;
3351
1383
  function majorOf(version) {
3352
1384
  const match = /^(\d+)\./.exec(version.trim());
@@ -3355,7 +1387,7 @@ function majorOf(version) {
3355
1387
  function readOwnDevkitVersion() {
3356
1388
  try {
3357
1389
  const path = createRequire(import.meta.url).resolve("@nx/devkit/package.json");
3358
- const version = JSON.parse(readFileSync9(path, "utf-8")).version;
1390
+ const version = JSON.parse(readFileSync6(path, "utf-8")).version;
3359
1391
  return typeof version === "string" ? version : void 0;
3360
1392
  } catch {
3361
1393
  return void 0;
@@ -3363,7 +1395,7 @@ function readOwnDevkitVersion() {
3363
1395
  }
3364
1396
  function readInstalledNx(root) {
3365
1397
  try {
3366
- const version = JSON.parse(readFileSync9(join12(root, "node_modules", "nx", "package.json"), "utf-8")).version;
1398
+ const version = JSON.parse(readFileSync6(join8(root, "node_modules", "nx", "package.json"), "utf-8")).version;
3367
1399
  return typeof version === "string" ? version : void 0;
3368
1400
  } catch {
3369
1401
  return void 0;