@clearlydev/nx-archweave 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +2 -2
@@ -0,0 +1,616 @@
1
+ import {
2
+ renderLineDiff
3
+ } from "./chunk-3A6RZUCZ.js";
4
+ import {
5
+ resolveFsdPolicy
6
+ } from "./chunk-5MI3O4VZ.js";
7
+ import {
8
+ readBoundaryTags
9
+ } from "./chunk-6ZTIUBB2.js";
10
+ import {
11
+ resolveDryRun
12
+ } from "./chunk-TUN34LAQ.js";
13
+ import {
14
+ readToolkitConfig,
15
+ recordAiHosts,
16
+ resolveAiHostsOption,
17
+ resolveLocaleOption,
18
+ rulesFilesFor
19
+ } from "./chunk-EXYZLL7A.js";
20
+
21
+ // ../templates/dist/agents/agent-sections.js
22
+ var UNIVERSAL_AGENT_SECTION_IDS = [
23
+ "think-before-coding",
24
+ "simplicity-first",
25
+ "surgical-changes",
26
+ "goal-driven-execution",
27
+ "code-structure"
28
+ ];
29
+ var PLACEHOLDER_AGENT_SECTION_IDS = ["project-goal", "architecture-boundary"];
30
+ var TEAM_AGENT_SECTION_ID = "team-rules";
31
+ var FSD_AGENT_SECTION_ID = "fsd-structure";
32
+ var DEFAULT_AGENT_SECTION_IDS = [
33
+ ...UNIVERSAL_AGENT_SECTION_IDS,
34
+ "safety-rules",
35
+ "language-policy",
36
+ ...PLACEHOLDER_AGENT_SECTION_IDS
37
+ ];
38
+ var EN_SECTIONS = {
39
+ "think-before-coding": {
40
+ id: "think-before-coding",
41
+ heading: "Think Before Coding",
42
+ body: [
43
+ "Before writing any code, restate the problem in your own words and identify what could go wrong.",
44
+ "Read the surrounding code and existing patterns before introducing new ones \u2014 consistency beats",
45
+ "personal preference. When a request is ambiguous, make the most reasonable interpretation and say",
46
+ "what you assumed, rather than guessing silently or blocking on a question you could answer yourself.",
47
+ "Prefer understanding the root cause of a problem over patching its symptoms."
48
+ ].join("\n")
49
+ },
50
+ "simplicity-first": {
51
+ id: "simplicity-first",
52
+ heading: "Simplicity First",
53
+ body: [
54
+ "Choose the simplest design that solves the actual problem in front of you, not the one you might",
55
+ "have tomorrow. Avoid speculative abstraction, unnecessary configuration, and generic frameworks",
56
+ "built for requirements nobody has asked for yet. A straightforward, slightly duplicated solution",
57
+ "that is easy to read beats a clever, general one that is hard to follow. Add complexity only when",
58
+ "a concrete, current need demands it."
59
+ ].join("\n")
60
+ },
61
+ "surgical-changes": {
62
+ id: "surgical-changes",
63
+ heading: "Surgical Changes",
64
+ body: [
65
+ "Change only what the task requires. Do not reformat, rename, or refactor unrelated code while",
66
+ "fixing something else \u2014 unrelated changes make review harder and hide the actual fix in noise.",
67
+ "Keep diffs small and focused so a reviewer can see exactly what changed and why. If you notice an",
68
+ "unrelated problem while working, note it separately instead of folding it into the current change."
69
+ ].join("\n")
70
+ },
71
+ "goal-driven-execution": {
72
+ id: "goal-driven-execution",
73
+ heading: "Goal-Driven Execution",
74
+ body: [
75
+ "Keep the actual goal in view, not just the literal instruction. If a requested step turns out to",
76
+ "be unnecessary, harmful, or based on a wrong assumption once you're in the code, say so and propose",
77
+ "the better path instead of executing it mechanically. Finish what you start: a task is done when it",
78
+ "actually works, not when the obvious-looking code has been written. Verify before declaring success."
79
+ ].join("\n")
80
+ },
81
+ "code-structure": {
82
+ id: "code-structure",
83
+ heading: "Code Structure and Abstraction",
84
+ body: [
85
+ "Keep functions and modules focused on one responsibility; split a file or function when it starts",
86
+ "accumulating unrelated concerns rather than letting it grow into a god object. Name things after",
87
+ "what they do or represent, not after their implementation detail. Extract an abstraction only after",
88
+ "a pattern has actually repeated \u2014 not in anticipation of a repetition that may never come."
89
+ ].join("\n")
90
+ },
91
+ "safety-rules": {
92
+ id: "safety-rules",
93
+ heading: "Safety Rules",
94
+ body: [
95
+ "Never force-push to shared branches, never bypass required status checks, and never disable or",
96
+ "delete CI/CD configuration just to make a failing check pass.",
97
+ "Do not read, print, or commit the contents of secret files (`.env`, credential files, private",
98
+ "keys) \u2014 treat anything matching a common secret pattern as off-limits unless the user has already",
99
+ "shared its contents with you directly.",
100
+ "Do not run destructive commands (`rm -rf`, `git reset --hard`, `git clean -f`, force pushes)",
101
+ "without explicit confirmation, and check for uncommitted work first.",
102
+ "Do not send repository contents, credentials, or user data to any external service.",
103
+ "When you are unsure whether an action is safe, stop and ask instead of proceeding.",
104
+ "",
105
+ "<!-- TODO: list project-specific protected resources (e.g. production config files, protected",
106
+ "branches, restricted directories, external services this project must never call in tests). -->"
107
+ ].join("\n")
108
+ },
109
+ "language-policy": {
110
+ id: "language-policy",
111
+ heading: "Language Policy",
112
+ body: [
113
+ "Communicate with the user in English. Write code, identifiers, comments, commit messages, and",
114
+ "documentation in English as well, unless the user explicitly asks for another language for a",
115
+ "specific piece of content."
116
+ ].join("\n")
117
+ },
118
+ "team-rules": {
119
+ id: "team-rules",
120
+ heading: "Team Rules",
121
+ body: [
122
+ "This repository is shared by a team: optimize for the reviewer, not for yourself.",
123
+ "Keep each pull request small and about one thing, fill in the pull request template, and link",
124
+ "the issue it closes.",
125
+ "Treat review findings as work, not opinions: address every P0/P1 finding before asking for",
126
+ "another look, answer each comment, and never merge your own pull request or dismiss a review",
127
+ "to get it through.",
128
+ "Respect module ownership: a change that crosses a module or layer boundary needs that",
129
+ "module's owner in the review. Never weaken a module-boundary lint rule, add an eslint-disable",
130
+ "for one, or retag a project to get an import past the boundary \u2014 propose the boundary change",
131
+ "in the pull request description instead.",
132
+ "Changes to shared code (the `shared` layer, `shared-types`, root configuration) affect every",
133
+ "team member: keep them backward compatible, call them out in the pull request, and keep them",
134
+ "separate from feature work.",
135
+ "Never commit directly to a protected branch; work on a branch and bring it up to date with",
136
+ "its base before requesting review.",
137
+ "",
138
+ "<!-- TODO: list team-specific conventions (module owners / CODEOWNERS, required reviewers,",
139
+ "branch naming, release process). -->"
140
+ ].join("\n")
141
+ },
142
+ "fsd-structure": {
143
+ id: "fsd-structure",
144
+ heading: "Frontend Structure (FSD Lite)",
145
+ body: [
146
+ 'The frontend app follows canonical Feature-Sliced Design. "Lite" means only that the default scope',
147
+ "is `app`, `pages`, `features`, `entities` and `shared` (`widgets` only on request) and that Solo",
148
+ "workspaces get warnings instead of errors \u2014 the rules themselves are the canonical ones:",
149
+ "- A layer imports only layers below it: app > pages > widgets > features > entities > shared.",
150
+ "- Slices on one layer (pages, widgets, features, entities) never import each other; move the",
151
+ " shared part one layer down, or expose it through the other slice's `@x/<consumer>` public API.",
152
+ "- Import a slice only through its `index.ts`; never reach into its `ui/`, `model/` or `api/` files.",
153
+ "- `app` and `shared` hold segments, not slices; shared segments are named by purpose (`ui`, `api`,",
154
+ " `model`, `lib`, `config`), never by essence (`components`, `hooks`, `utils`, `types`).",
155
+ "Create slices with the generators (`nx g @clearlydev/nx-archweave:page|widget|feature|entity|shared --name=\u2026`,",
156
+ "`--segments=ui,api` for extra segments).",
157
+ "The `archweave/fsd-boundaries` ESLint rule reports violations: fix the import, never disable the",
158
+ "rule or add an eslint-disable for it. On Next.js the pages layer is the `_pages/` directory."
159
+ ].join("\n")
160
+ },
161
+ "project-goal": {
162
+ id: "project-goal",
163
+ heading: "Project Goal",
164
+ body: "<!-- TODO: describe this project's goal, target users, and what success looks like. -->"
165
+ },
166
+ "architecture-boundary": {
167
+ id: "architecture-boundary",
168
+ heading: "Architecture Boundary",
169
+ body: "<!-- TODO: describe this project's module boundaries, ownership rules, and which modules may or may not depend on which. -->"
170
+ }
171
+ };
172
+ var KO_SECTIONS = {
173
+ "think-before-coding": {
174
+ id: "think-before-coding",
175
+ heading: "\uCF54\uB529 \uC804\uC5D0 \uBA3C\uC800 \uC0DD\uAC01\uD558\uAE30",
176
+ body: [
177
+ "\uCF54\uB4DC\uB97C \uC791\uC131\uD558\uAE30 \uC804\uC5D0 \uBB38\uC81C\uB97C \uC2A4\uC2A4\uB85C\uC758 \uB9D0\uB85C \uB2E4\uC2DC \uC815\uB9AC\uD558\uACE0 \uBB34\uC5C7\uC774 \uC798\uBABB\uB420 \uC218 \uC788\uB294\uC9C0 \uD30C\uC545\uD55C\uB2E4.",
178
+ "\uC0C8 \uD328\uD134\uC744 \uB3C4\uC785\uD558\uAE30 \uC804\uC5D0 \uC8FC\uBCC0 \uCF54\uB4DC\uC640 \uAE30\uC874 \uAD00\uB840\uB97C \uBA3C\uC800 \uC77D\uB294\uB2E4 \u2014 \uAC1C\uC778 \uCDE8\uD5A5\uBCF4\uB2E4 \uC77C\uAD00\uC131\uC774 \uC6B0\uC120\uC774\uB2E4.",
179
+ "\uC694\uCCAD\uC774 \uBAA8\uD638\uD560 \uB54C\uB294 \uAC00\uC7A5 \uD569\uB9AC\uC801\uC778 \uD574\uC11D\uC744 \uD0DD\uD558\uACE0 \uBB34\uC5C7\uC744 \uAC00\uC815\uD588\uB294\uC9C0 \uBC1D\uD78C\uB2E4. \uC2A4\uC2A4\uB85C \uB2F5\uD560 \uC218 \uC788\uB294",
180
+ "\uC9C8\uBB38\uC73C\uB85C \uC791\uC5C5\uC744 \uB9C9\uC9C0 \uB9D0\uACE0, \uC870\uC6A9\uD788 \uCD94\uCE21\uB9CC \uD558\uC9C0\uB3C4 \uC54A\uB294\uB2E4. \uC99D\uC0C1\uC744 \uB55C\uC9C8\uD558\uAE30\uBCF4\uB2E4 \uADFC\uBCF8 \uC6D0\uC778\uC744 \uC774\uD574\uD558\uB294",
181
+ "\uCABD\uC744 \uC6B0\uC120\uD55C\uB2E4."
182
+ ].join("\n")
183
+ },
184
+ "simplicity-first": {
185
+ id: "simplicity-first",
186
+ heading: "\uB2E8\uC21C\uD568 \uC6B0\uC120",
187
+ body: [
188
+ "\uC9C0\uAE08 \uB208\uC55E\uC758 \uBB38\uC81C\uB97C \uD574\uACB0\uD558\uB294 \uAC00\uC7A5 \uB2E8\uC21C\uD55C \uC124\uACC4\uB97C \uC120\uD0DD\uD55C\uB2E4 \u2014 \uB0B4\uC77C \uD544\uC694\uD560\uC9C0\uB3C4 \uBAA8\uB974\uB294 \uAC83\uAE4C\uC9C0 \uB300\uBE44\uD558\uC9C0",
189
+ "\uC54A\uB294\uB2E4. \uC544\uBB34\uB3C4 \uC694\uCCAD\uD558\uC9C0 \uC54A\uC740 \uC694\uAD6C\uC0AC\uD56D\uC744 \uC704\uD55C \uCD94\uCE21\uC131 \uCD94\uC0C1\uD654, \uBD88\uD544\uC694\uD55C \uC124\uC815, \uBC94\uC6A9 \uD504\uB808\uC784\uC6CC\uD06C\uB97C",
190
+ "\uD53C\uD55C\uB2E4. \uC77D\uAE30 \uC26C\uC6B4 \uC57D\uAC04\uC758 \uC911\uBCF5\uC774 \uC788\uB294 \uD574\uBC95\uC774, \uC774\uD574\uD558\uAE30 \uC5B4\uB824\uC6B4 \uC601\uB9AC\uD558\uACE0 \uBC94\uC6A9\uC801\uC778 \uD574\uBC95\uBCF4\uB2E4 \uB0AB\uB2E4.",
191
+ "\uAD6C\uCCB4\uC801\uC774\uACE0 \uD604\uC7AC \uC874\uC7AC\uD558\uB294 \uD544\uC694\uAC00 \uC788\uC744 \uB54C\uB9CC \uBCF5\uC7A1\uB3C4\uB97C \uCD94\uAC00\uD55C\uB2E4."
192
+ ].join("\n")
193
+ },
194
+ "surgical-changes": {
195
+ id: "surgical-changes",
196
+ heading: "\uC678\uACFC\uC801 \uBCC0\uACBD",
197
+ body: [
198
+ "\uC791\uC5C5\uC5D0 \uD544\uC694\uD55C \uBD80\uBD84\uB9CC \uBCC0\uACBD\uD55C\uB2E4. \uB2E4\uB978 \uBB38\uC81C\uB97C \uACE0\uCE58\uB294 \uAE40\uC5D0 \uAD00\uB828 \uC5C6\uB294 \uCF54\uB4DC\uB97C \uC7AC\uD3EC\uB9F7\uD558\uAC70\uB098, \uC774\uB984\uC744",
199
+ "\uBC14\uAFB8\uAC70\uB098, \uB9AC\uD329\uD130\uB9C1\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uAD00\uB828 \uC5C6\uB294 \uBCC0\uACBD\uC740 \uB9AC\uBDF0\uB97C \uC5B4\uB835\uAC8C \uB9CC\uB4E4\uACE0 \uC2E4\uC81C \uC218\uC815 \uB0B4\uC6A9\uC744 \uB178\uC774\uC988",
200
+ "\uC18D\uC5D0 \uC228\uAE34\uB2E4. diff\uB97C \uC791\uACE0 \uC9D1\uC911\uB418\uAC8C \uC720\uC9C0\uD574 \uB9AC\uBDF0\uC5B4\uAC00 \uBB34\uC5C7\uC774, \uC65C \uBC14\uB00C\uC5C8\uB294\uC9C0 \uC815\uD655\uD788 \uBCFC \uC218 \uC788\uAC8C \uD55C\uB2E4.",
201
+ "\uC791\uC5C5 \uC911 \uAD00\uB828 \uC5C6\uB294 \uBB38\uC81C\uB97C \uBC1C\uACAC\uD558\uBA74 \uD604\uC7AC \uBCC0\uACBD\uC5D0 \uC11E\uC9C0 \uB9D0\uACE0 \uBCC4\uB3C4\uB85C \uC54C\uB9B0\uB2E4."
202
+ ].join("\n")
203
+ },
204
+ "goal-driven-execution": {
205
+ id: "goal-driven-execution",
206
+ heading: "\uBAA9\uD45C \uC9C0\uD5A5 \uC2E4\uD589",
207
+ body: [
208
+ "\uBB38\uC790 \uADF8\uB300\uB85C\uC758 \uC9C0\uC2DC\uAC00 \uC544\uB2C8\uB77C \uC2E4\uC81C \uBAA9\uD45C\uB97C \uACC4\uC18D \uC5FC\uB450\uC5D0 \uB454\uB2E4. \uCF54\uB4DC\uB97C \uC0B4\uD3B4\uBCF4\uB2C8 \uC694\uCCAD\uBC1B\uC740 \uB2E8\uACC4\uAC00",
209
+ "\uBD88\uD544\uC694\uD558\uAC70\uB098, \uD574\uB86D\uAC70\uB098, \uC798\uBABB\uB41C \uAC00\uC815\uC5D0 \uAE30\uBC18\uD55C \uAC83\uC73C\uB85C \uB4DC\uB7EC\uB098\uBA74 \uAE30\uACC4\uC801\uC73C\uB85C \uC2E4\uD589\uD558\uC9C0 \uB9D0\uACE0 \uADF8\uB807\uB2E4\uACE0",
210
+ "\uB9D0\uD55C \uB4A4 \uB354 \uB098\uC740 \uACBD\uB85C\uB97C \uC81C\uC548\uD55C\uB2E4. \uC2DC\uC791\uD55C \uC77C\uC740 \uB05D\uB0B8\uB2E4 \u2014 \uADF8\uB7F4\uB4EF\uD574 \uBCF4\uC774\uB294 \uCF54\uB4DC\uB97C \uC791\uC131\uD588\uC744 \uB54C\uAC00 \uC544\uB2C8\uB77C",
211
+ "\uC2E4\uC81C\uB85C \uB3D9\uC791\uD560 \uB54C\uAC00 \uC644\uB8CC\uB2E4. \uC131\uACF5\uC744 \uC120\uC5B8\uD558\uAE30 \uC804\uC5D0 \uBC18\uB4DC\uC2DC \uAC80\uC99D\uD55C\uB2E4."
212
+ ].join("\n")
213
+ },
214
+ "code-structure": {
215
+ id: "code-structure",
216
+ heading: "\uCF54\uB4DC \uAD6C\uC870\uC640 \uCD94\uC0C1\uD654",
217
+ body: [
218
+ "\uD568\uC218\uC640 \uBAA8\uB4C8\uC744 \uD558\uB098\uC758 \uCC45\uC784\uC5D0 \uC9D1\uC911\uC2DC\uD0A8\uB2E4. God object\uB85C \uCEE4\uC9C0\uB3C4\uB85D \uB450\uC9C0 \uB9D0\uACE0, \uAD00\uB828 \uC5C6\uB294 \uAD00\uC2EC\uC0AC\uAC00",
219
+ "\uC313\uC774\uAE30 \uC2DC\uC791\uD558\uBA74 \uD30C\uC77C\uC774\uB098 \uD568\uC218\uB97C \uBD84\uB9AC\uD55C\uB2E4. \uAD6C\uD604 \uC138\uBD80\uC0AC\uD56D\uC774 \uC544\uB2C8\uB77C \uD558\uB294 \uC77C\uC774\uB098 \uB098\uD0C0\uB0B4\uB294 \uB300\uC0C1\uC744",
220
+ "\uAE30\uC900\uC73C\uB85C \uC774\uB984\uC744 \uC9D3\uB294\uB2E4. \uD328\uD134\uC774 \uC2E4\uC81C\uB85C \uBC18\uBCF5\uB41C \uB4A4\uC5D0\uB9CC \uCD94\uC0C1\uD654\uB97C \uCD94\uCD9C\uD55C\uB2E4 \u2014 \uC5B8\uC820\uAC00 \uBC18\uBCF5\uB420\uC9C0\uB3C4 \uBAA8\uB978\uB2E4\uB294",
221
+ "\uC608\uCE21\uB9CC\uC73C\uB85C \uBBF8\uB9AC \uCD94\uCD9C\uD558\uC9C0 \uC54A\uB294\uB2E4."
222
+ ].join("\n")
223
+ },
224
+ "safety-rules": {
225
+ id: "safety-rules",
226
+ heading: "\uC548\uC804 \uADDC\uCE59",
227
+ body: [
228
+ "\uACF5\uC720 \uBE0C\uB79C\uCE58\uC5D0 force-push\uD558\uC9C0 \uC54A\uACE0, \uD544\uC218 \uC0C1\uD0DC \uAC80\uC0AC\uB97C \uC6B0\uD68C\uD558\uC9C0 \uC54A\uC73C\uBA70, \uC2E4\uD328\uD558\uB294 \uAC80\uC0AC\uB97C \uD1B5\uACFC\uC2DC\uD0A4\uAE30",
229
+ "\uC704\uD574 CI/CD \uC124\uC815\uC744 \uBE44\uD65C\uC131\uD654\uD558\uAC70\uB098 \uC0AD\uC81C\uD558\uC9C0 \uC54A\uB294\uB2E4.",
230
+ "\uBE44\uBC00 \uD30C\uC77C(`.env`, \uC790\uACA9 \uC99D\uBA85 \uD30C\uC77C, \uAC1C\uC778 \uD0A4)\uC758 \uB0B4\uC6A9\uC744 \uC77D\uAC70\uB098 \uCD9C\uB825\uD558\uAC70\uB098 \uCEE4\uBC0B\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uC0AC\uC6A9\uC790\uAC00",
231
+ "\uBA3C\uC800 \uC9C1\uC811 \uB0B4\uC6A9\uC744 \uACF5\uC720\uD558\uC9C0 \uC54A\uB294 \uD55C, \uC77C\uBC18\uC801\uC778 \uBE44\uBC00 \uD30C\uC77C \uD328\uD134\uC5D0 \uD574\uB2F9\uD558\uB294 \uAC83\uC740 \uC811\uADFC \uAE08\uC9C0 \uB300\uC0C1\uC73C\uB85C",
232
+ "\uCDE8\uAE09\uD55C\uB2E4.",
233
+ "\uBA85\uC2DC\uC801 \uD655\uC778 \uC5C6\uC774 \uD30C\uAD34\uC801 \uBA85\uB839(`rm -rf`, `git reset --hard`, `git clean -f`, force-push)\uC744 \uC2E4\uD589\uD558\uC9C0",
234
+ "\uC54A\uC73C\uBA70, \uC2E4\uD589 \uC804 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 \uC791\uC5C5\uC774 \uC788\uB294\uC9C0 \uBA3C\uC800 \uD655\uC778\uD55C\uB2E4.",
235
+ "\uC800\uC7A5\uC18C \uB0B4\uC6A9, \uC790\uACA9 \uC99D\uBA85, \uC0AC\uC6A9\uC790 \uB370\uC774\uD130\uB97C \uC678\uBD80 \uC11C\uBE44\uC2A4\uB85C \uC804\uC1A1\uD558\uC9C0 \uC54A\uB294\uB2E4.",
236
+ "\uC5B4\uB5A4 \uB3D9\uC791\uC774 \uC548\uC804\uD55C\uC9C0 \uD655\uC2E0\uC774 \uC11C\uC9C0 \uC54A\uC73C\uBA74 \uC9C4\uD589\uD558\uC9C0 \uB9D0\uACE0 \uBA3C\uC800 \uC9C8\uBB38\uD55C\uB2E4.",
237
+ "",
238
+ // The marker itself stays in English, matching the Language Policy's "code/comments in
239
+ // English" rule and giving tooling a single locale-invariant string to check for.
240
+ "<!-- TODO: list project-specific protected resources (e.g. production config files, protected",
241
+ "branches, restricted directories, external services this project must never call in tests). -->"
242
+ ].join("\n")
243
+ },
244
+ "language-policy": {
245
+ id: "language-policy",
246
+ heading: "\uC5B8\uC5B4 \uC815\uCC45",
247
+ body: [
248
+ "\uC0AC\uC6A9\uC790\uC640\uC758 \uB300\uD654\uB294 \uD55C\uAD6D\uC5B4\uB85C \uC9C4\uD589\uD55C\uB2E4. \uCF54\uB4DC, \uC2DD\uBCC4\uC790, \uC8FC\uC11D, \uCEE4\uBC0B \uBA54\uC2DC\uC9C0, \uBB38\uC11C\uB294 \uC0AC\uC6A9\uC790\uAC00 \uD2B9\uC815",
249
+ "\uD56D\uBAA9\uC5D0 \uB300\uD574 \uB2E4\uB978 \uC5B8\uC5B4\uB97C \uBA85\uC2DC\uC801\uC73C\uB85C \uC694\uCCAD\uD558\uC9C0 \uC54A\uB294 \uD55C \uC601\uC5B4\uB85C \uC791\uC131\uD55C\uB2E4."
250
+ ].join("\n")
251
+ },
252
+ "team-rules": {
253
+ id: "team-rules",
254
+ heading: "\uD300 \uADDC\uCE59",
255
+ body: [
256
+ "\uC774 \uC800\uC7A5\uC18C\uB294 \uD300\uC774 \uD568\uAED8 \uC4F4\uB2E4 \u2014 \uC790\uC2E0\uC774 \uC544\uB2C8\uB77C \uB9AC\uBDF0\uC5B4\uB97C \uC704\uD574 \uCD5C\uC801\uD654\uD55C\uB2E4.",
257
+ "PR\uC740 \uC791\uAC8C, \uD55C \uAC00\uC9C0 \uC8FC\uC81C\uB9CC \uB2E4\uB8E8\uBA70, PR \uD15C\uD50C\uB9BF\uC744 \uCC44\uC6B0\uACE0 \uB2EB\uB294 \uC774\uC288\uB97C \uB9C1\uD06C\uD55C\uB2E4.",
258
+ "\uB9AC\uBDF0 finding\uC740 \uC758\uACAC\uC774 \uC544\uB2C8\uB77C \uC791\uC5C5\uC774\uB2E4 \u2014 \uC7AC\uB9AC\uBDF0\uB97C \uC694\uCCAD\uD558\uAE30 \uC804\uC5D0 P0/P1 finding\uC744 \uBAA8\uB450 \uD574\uACB0\uD558\uACE0,",
259
+ "\uCF54\uBA58\uD2B8\uB9C8\uB2E4 \uB2F5\uD558\uBA70, \uC790\uAE30 PR\uC744 \uC2A4\uC2A4\uB85C \uBA38\uC9C0\uD558\uAC70\uB098 \uB9AC\uBDF0\uB97C dismiss\uD574\uC11C \uD1B5\uACFC\uC2DC\uD0A4\uC9C0 \uC54A\uB294\uB2E4.",
260
+ "\uBAA8\uB4C8 \uC18C\uC720\uAD8C\uC744 \uC874\uC911\uD55C\uB2E4 \u2014 \uBAA8\uB4C8\uC774\uB098 \uB808\uC774\uC5B4 \uACBD\uACC4\uB97C \uB118\uB294 \uBCC0\uACBD\uC740 \uD574\uB2F9 \uBAA8\uB4C8 \uC624\uB108\uC758 \uB9AC\uBDF0\uAC00 \uD544\uC694\uD558\uB2E4.",
261
+ "import\uB97C \uACBD\uACC4 \uB108\uBA38\uB85C \uD1B5\uACFC\uC2DC\uD0A4\uAE30 \uC704\uD574 module-boundary \uB9B0\uD2B8 \uADDC\uCE59\uC744 \uC57D\uD654\uD558\uAC70\uB098, eslint-disable\uC744",
262
+ "\uCD94\uAC00\uD558\uAC70\uB098, \uD504\uB85C\uC81D\uD2B8 \uD0DC\uADF8\uB97C \uBC14\uAFB8\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uACBD\uACC4 \uBCC0\uACBD\uC740 PR \uBCF8\uBB38\uC5D0\uC11C \uC81C\uC548\uD55C\uB2E4.",
263
+ "\uACF5\uC720 \uCF54\uB4DC(`shared` \uB808\uC774\uC5B4, `shared-types`, \uB8E8\uD2B8 \uC124\uC815) \uBCC0\uACBD\uC740 \uD300 \uC804\uC6D0\uC5D0\uAC8C \uC601\uD5A5\uC744 \uC900\uB2E4 \u2014 \uD558\uC704",
264
+ "\uD638\uD658\uC744 \uC720\uC9C0\uD558\uACE0, PR\uC5D0 \uBA85\uC2DC\uD558\uBA70, \uAE30\uB2A5 \uC791\uC5C5\uACFC \uBD84\uB9AC\uD55C\uB2E4.",
265
+ "\uBCF4\uD638 \uBE0C\uB79C\uCE58\uC5D0 \uC9C1\uC811 \uCEE4\uBC0B\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uBE0C\uB79C\uCE58\uC5D0\uC11C \uC791\uC5C5\uD558\uACE0 \uB9AC\uBDF0 \uC694\uCCAD \uC804\uC5D0 \uBCA0\uC774\uC2A4\uC640 \uB3D9\uAE30\uD654\uD55C\uB2E4.",
266
+ "",
267
+ // The marker stays in English, matching the Safety Rules precedent (one locale-invariant
268
+ // string for tooling to check).
269
+ "<!-- TODO: list team-specific conventions (module owners / CODEOWNERS, required reviewers,",
270
+ "branch naming, release process). -->"
271
+ ].join("\n")
272
+ },
273
+ "fsd-structure": {
274
+ id: "fsd-structure",
275
+ heading: "\uD504\uB860\uD2B8\uC5D4\uB4DC \uAD6C\uC870 (FSD Lite)",
276
+ body: [
277
+ '\uD504\uB860\uD2B8\uC5D4\uB4DC \uC571\uC740 \uC815\uC2DD Feature-Sliced Design\uC744 \uB530\uB978\uB2E4. "Lite"\uB294 \uAE30\uBCF8 \uBC94\uC704\uAC00 `app`, `pages`, `features`,',
278
+ "`entities`, `shared`\uC774\uACE0(`widgets`\uB294 \uC694\uCCAD\uD560 \uB54C\uB9CC), Solo \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC5D0\uC11C\uB294 \uC704\uBC18\uC744 \uC5D0\uB7EC\uAC00 \uC544\uB2CC",
279
+ "\uACBD\uACE0\uB85C \uC54C\uB9B0\uB2E4\uB294 \uB73B\uC77C \uBFD0\uC774\uB2E4 \u2014 \uADDC\uCE59 \uC790\uCCB4\uB294 \uC815\uC2DD FSD \uADF8\uB300\uB85C\uB2E4:",
280
+ "- \uB808\uC774\uC5B4\uB294 \uC790\uAE30\uBCF4\uB2E4 \uC544\uB798 \uB808\uC774\uC5B4\uB9CC import\uD55C\uB2E4: app > pages > widgets > features > entities > shared.",
281
+ "- \uAC19\uC740 \uB808\uC774\uC5B4\uC758 \uC2AC\uB77C\uC774\uC2A4\uB07C\uB9AC(pages, widgets, features, entities)\uB294 import\uD558\uC9C0 \uC54A\uB294\uB2E4. \uACF5\uC720\uD560 \uCF54\uB4DC\uB294",
282
+ " \uD55C \uB808\uC774\uC5B4 \uC544\uB798\uB85C \uB0B4\uB9AC\uAC70\uB098, \uC0C1\uB300 \uC2AC\uB77C\uC774\uC2A4\uC758 `@x/<consumer>` \uACF5\uAC1C API\uB85C\uB9CC \uB178\uCD9C\uD55C\uB2E4.",
283
+ "- \uC2AC\uB77C\uC774\uC2A4\uB294 `index.ts`\uB97C \uD1B5\uD574\uC11C\uB9CC import\uD55C\uB2E4. `ui/`, `model/`, `api/` \uB0B4\uBD80 \uD30C\uC77C\uC5D0 \uC9C1\uC811 \uB2FF\uC9C0 \uC54A\uB294\uB2E4.",
284
+ "- `app`\uACFC `shared`\uB294 \uC2AC\uB77C\uC774\uC2A4\uAC00 \uC544\uB2C8\uB77C \uC138\uADF8\uBA3C\uD2B8\uB97C \uAC00\uC9C4\uB2E4. shared \uC138\uADF8\uBA3C\uD2B8 \uC774\uB984\uC740 \uBAA9\uC801\uD615(`ui`, `api`,",
285
+ " `model`, `lib`, `config`)\uC73C\uB85C \uC9D3\uACE0, \uBCF8\uC9C8\uD615(`components`, `hooks`, `utils`, `types`)\uC740 \uC4F0\uC9C0 \uC54A\uB294\uB2E4.",
286
+ "\uC2AC\uB77C\uC774\uC2A4\uB294 generator\uB85C \uB9CC\uB4E0\uB2E4(`nx g @clearlydev/nx-archweave:page|widget|feature|entity|shared --name=\u2026`,",
287
+ "\uCD94\uAC00 \uC138\uADF8\uBA3C\uD2B8\uB294 `--segments=ui,api`).",
288
+ "`archweave/fsd-boundaries` ESLint \uADDC\uCE59\uC774 \uC704\uBC18\uC744 \uC54C\uB824 \uC900\uB2E4: import\uB97C \uACE0\uCE58\uB418, \uADDC\uCE59\uC744 \uB044\uAC70\uB098 eslint-disable\uB85C",
289
+ "\uB118\uAE30\uC9C0 \uC54A\uB294\uB2E4. Next.js\uC5D0\uC11C\uB294 pages \uB808\uC774\uC5B4\uAC00 `_pages/` \uD3F4\uB354\uB2E4."
290
+ ].join("\n")
291
+ },
292
+ "project-goal": {
293
+ id: "project-goal",
294
+ heading: "\uD504\uB85C\uC81D\uD2B8 \uBAA9\uD45C",
295
+ body: "<!-- TODO: \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uBAA9\uD45C, \uB300\uC0C1 \uC0AC\uC6A9\uC790, \uC131\uACF5 \uAE30\uC900\uC744 \uC791\uC131\uD558\uC138\uC694. -->"
296
+ },
297
+ "architecture-boundary": {
298
+ id: "architecture-boundary",
299
+ heading: "\uC544\uD0A4\uD14D\uCC98 \uACBD\uACC4",
300
+ body: "<!-- TODO: \uC774 \uD504\uB85C\uC81D\uD2B8\uC758 \uBAA8\uB4C8 \uACBD\uACC4, \uC18C\uC720 \uADDC\uCE59, \uC5B4\uB5A4 \uBAA8\uB4C8\uC774 \uC5B4\uB5A4 \uBAA8\uB4C8\uC5D0 \uC758\uC874\uD560 \uC218 \uC788\uB294\uC9C0\uB97C \uC791\uC131\uD558\uC138\uC694. -->"
301
+ }
302
+ };
303
+ var AGENT_RULE_SECTIONS = {
304
+ en: EN_SECTIONS,
305
+ ko: KO_SECTIONS
306
+ };
307
+
308
+ // ../templates/dist/agents/fsd-policy-notes.js
309
+ var NOTES = {
310
+ en: {
311
+ heading: "This workspace also configures:",
312
+ groups: "- A folder that only groups slices (`features/mypage/{order,claims}`) holds no code and no `index.ts`; each slice in it is separate, and slices in one group never import each other either. Create one with `--group=<name>`.",
313
+ component: "- `shared` exposes each component through its own `index.ts` (`shared/ui/button`); import that, not the files inside it.",
314
+ file: "- Inside `shared`, files may be imported directly (`shared/lib/format/currency`); `shared` still never imports a layer above it.",
315
+ tests: "- Test files (`*.spec.ts`, `*.test.ts`, `__tests__/`) may import another slice's internals; upward imports stay forbidden there too."
316
+ },
317
+ ko: {
318
+ heading: "\uC774 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB294 \uB2E4\uC74C\uB3C4 \uC124\uC815\uD588\uB2E4:",
319
+ groups: "- \uC2AC\uB77C\uC774\uC2A4\uB97C \uBB36\uAE30\uB9CC \uD558\uB294 \uD3F4\uB354(`features/mypage/{order,claims}`)\uC5D0\uB294 \uCF54\uB4DC\uB3C4 `index.ts`\uB3C4 \uB450\uC9C0 \uC54A\uB294\uB2E4. \uADF8 \uC548\uC758 \uC2AC\uB77C\uC774\uC2A4\uB294 \uAC01\uAC01 \uB3C5\uB9BD\uC774\uB77C, \uAC19\uC740 \uADF8\uB8F9 \uC548\uC5D0\uC11C\uB3C4 \uC11C\uB85C import\uD558\uC9C0 \uC54A\uB294\uB2E4. \uADF8\uB8F9 \uC548\uC5D0 \uB9CC\uB4E4 \uB54C\uB294 `--group=<\uC774\uB984>`\uC744 \uC4F4\uB2E4.",
320
+ component: "- `shared`\uB294 \uCEF4\uD3EC\uB10C\uD2B8\uB9C8\uB2E4 \uC790\uAE30 `index.ts`\uB85C \uACF5\uAC1C\uD55C\uB2E4(`shared/ui/button`). \uADF8 index\uB97C import\uD558\uACE0, \uC548\uCABD \uD30C\uC77C\uC5D0\uB294 \uB2FF\uC9C0 \uC54A\uB294\uB2E4.",
321
+ file: "- `shared` \uC548\uC758 \uD30C\uC77C\uC740 \uC9C1\uC811 import\uD574\uB3C4 \uB41C\uB2E4(`shared/lib/format/currency`). \uB2E4\uB9CC `shared`\uB294 \uC5EC\uC804\uD788 \uC704 \uB808\uC774\uC5B4\uB97C import\uD558\uC9C0 \uC54A\uB294\uB2E4.",
322
+ tests: "- \uD14C\uC2A4\uD2B8 \uD30C\uC77C(`*.spec.ts`, `*.test.ts`, `__tests__/`)\uC740 \uB2E4\uB978 \uC2AC\uB77C\uC774\uC2A4\uC758 \uB0B4\uBD80 \uD30C\uC77C\uC744 import\uD574\uB3C4 \uB41C\uB2E4. \uC704 \uB808\uC774\uC5B4 import\uB294 \uD14C\uC2A4\uD2B8\uC5D0\uC11C\uB3C4 \uAE08\uC9C0\uB2E4."
323
+ }
324
+ };
325
+ function buildFsdPolicyNotes(locale, notes) {
326
+ const text = NOTES[locale];
327
+ const lines = [
328
+ ...notes.sliceGroups ? [text.groups] : [],
329
+ ...notes.sharedPublicApi === "component" ? [text.component] : notes.sharedPublicApi === "file" ? [text.file] : [],
330
+ ...notes.testFiles ? [text.tests] : []
331
+ ];
332
+ return lines.length === 0 ? "" : [text.heading, ...lines].join("\n");
333
+ }
334
+
335
+ // ../templates/dist/agents/module-boundaries.js
336
+ function buildModuleBoundariesSection(locale, tags) {
337
+ const { frontend, backend, shared } = tags;
338
+ if (locale === "ko") {
339
+ return {
340
+ id: "module-boundaries",
341
+ heading: "\uBAA8\uB4C8 \uACBD\uACC4",
342
+ body: [
343
+ "\uB8E8\uD2B8 ESLint \uC124\uC815\uC758 `@nx/enforce-module-boundaries` \uADDC\uCE59\uC774 \uD504\uB85C\uC81D\uD2B8 \uD0DC\uADF8\uB85C \uD504\uB860\uD2B8\uC5D4\uB4DC\uC640 \uBC31\uC5D4\uB4DC\uB97C \uB098\uB208\uB2E4:",
344
+ `- \`${frontend}\` \uD504\uB85C\uC81D\uD2B8\uB294 \`${backend}\` \uD504\uB85C\uC81D\uD2B8\uB97C import\uD558\uC9C0 \uC54A\uACE0, \uADF8 \uBC18\uB300\uB3C4 \uB9C8\uCC2C\uAC00\uC9C0\uB2E4.`,
345
+ `- \`${shared}\` \uD504\uB85C\uC81D\uD2B8(shared-types \uB77C\uC774\uBE0C\uB7EC\uB9AC \uB4F1)\uB294 \uC5B4\uB290 \uCABD\uB3C4 import\uD558\uC9C0 \uC54A\uB294\uB2E4.`,
346
+ "- \uC774 \uD0DC\uADF8\uAC00 \uD558\uB098\uB3C4 \uC5C6\uB294 \uD504\uB85C\uC81D\uD2B8\uB294 \uADDC\uCE59 \uBC16\uC5D0 \uC788\uB2E4. \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB97C \uB9CC\uB4E4 \uB54C\uB294 \uBC14\uB85C \uC790\uAE30 \uCABD \uD0DC\uADF8\uB97C \uBD99\uC778\uB2E4:",
347
+ ` \`nx g @nx/js:lib libs/<name> --tags=${shared}\` (\uD504\uB860\uD2B8\uC5D4\uB4DC \uC804\uC6A9\uC774\uBA74 \`${frontend}\`, \uBC31\uC5D4\uB4DC \uC804\uC6A9\uC774\uBA74 \`${backend}\`).`,
348
+ `import\uB97C \uD1B5\uACFC\uC2DC\uD0A4\uB824\uACE0 \uD0DC\uADF8\uB97C \uC9C0\uC6B0\uAC70\uB098 \`depConstraints\`\uB97C \uB290\uC2A8\uD558\uAC8C \uD558\uC9C0 \uC54A\uB294\uB2E4. \uD568\uAED8 \uC4F0\uB294 \uCF54\uB4DC\uB294 \`${shared}\``,
349
+ "\uB77C\uC774\uBE0C\uB7EC\uB9AC\uB85C \uC62E\uAE34\uB2E4."
350
+ ].join("\n")
351
+ };
352
+ }
353
+ return {
354
+ id: "module-boundaries",
355
+ heading: "Module Boundaries",
356
+ body: [
357
+ "The root ESLint config's `@nx/enforce-module-boundaries` rule keeps the frontend and the backend apart by project tag:",
358
+ `- \`${frontend}\` projects never import \`${backend}\` projects, and the other way round.`,
359
+ `- \`${shared}\` projects (such as the shared-types library) import neither side.`,
360
+ "- A project with none of these tags is outside the rule. When you create a library, give it its side right away:",
361
+ ` \`nx g @nx/js:lib libs/<name> --tags=${shared}\` (\`${frontend}\` or \`${backend}\` for code only one side uses).`,
362
+ `Never remove a tag or loosen the \`depConstraints\` to make an import pass; move the shared code into a \`${shared}\``,
363
+ "library instead."
364
+ ].join("\n")
365
+ };
366
+ }
367
+
368
+ // ../template-engine/dist/agent-rules/compose-agent-rules.js
369
+ function resolveDefaultAgentSectionIds(teamRules, fsdStructure = false) {
370
+ let ids = [...DEFAULT_AGENT_SECTION_IDS];
371
+ const insertAfter = (anchorId, id) => {
372
+ const anchor = ids.indexOf(anchorId);
373
+ if (anchor === -1) {
374
+ throw new Error(`composeAgentRules: the default composition no longer contains ${anchorId}`);
375
+ }
376
+ ids = [...ids.slice(0, anchor + 1), id, ...ids.slice(anchor + 1)];
377
+ };
378
+ if (fsdStructure)
379
+ insertAfter("code-structure", FSD_AGENT_SECTION_ID);
380
+ if (teamRules)
381
+ insertAfter("language-policy", TEAM_AGENT_SECTION_ID);
382
+ return ids;
383
+ }
384
+ function renderSections(sections) {
385
+ return `${sections.map((section) => `## ${section.heading}
386
+
387
+ ${section.body}`).join("\n\n")}
388
+ `;
389
+ }
390
+ function composeAgentRules(input) {
391
+ const sectionIds = input.sections ?? resolveDefaultAgentSectionIds(input.teamRules ?? false, input.fsdStructure ?? false);
392
+ const notes = input.fsdPolicy === void 0 ? "" : buildFsdPolicyNotes(input.locale, input.fsdPolicy);
393
+ const sections = sectionIds.map((id) => {
394
+ const section = AGENT_RULE_SECTIONS[input.locale][id];
395
+ return id === FSD_AGENT_SECTION_ID && notes !== "" ? { ...section, body: `${section.body}
396
+
397
+ ${notes}` } : section;
398
+ });
399
+ if (input.moduleBoundaries !== void 0 && input.sections === void 0) {
400
+ const anchor = sections.findIndex((section) => section.id === FSD_AGENT_SECTION_ID);
401
+ const at = (anchor === -1 ? sections.findIndex((section) => section.id === "code-structure") : anchor) + 1;
402
+ sections.splice(at, 0, buildModuleBoundariesSection(input.locale, input.moduleBoundaries));
403
+ }
404
+ const content = renderSections(sections);
405
+ return input.files.map((filePath) => ({ filePath, content }));
406
+ }
407
+
408
+ // ../template-engine/dist/agent-rules/validate-agent-rules.js
409
+ var LOCALES = ["en", "ko"];
410
+ function headingsFor(id) {
411
+ return LOCALES.map((locale) => `## ${AGENT_RULE_SECTIONS[locale][id].heading}`);
412
+ }
413
+ var REQUIRED_SECTION_IDS = ["safety-rules", "language-policy"];
414
+ function validateAgentRules(content, extraRequired = []) {
415
+ return [...REQUIRED_SECTION_IDS, ...extraRequired].every((id) => headingsFor(id).some((heading) => content.includes(heading)));
416
+ }
417
+
418
+ // ../template-engine/dist/agent-rules/marker-block.js
419
+ import { createHash } from "node:crypto";
420
+ var AGENT_RULES_BLOCK_START = "<!-- nx-archweave:agent-rules start -->";
421
+ var AGENT_RULES_BLOCK_END = "<!-- nx-archweave:agent-rules end -->";
422
+ var START_MARKER = /<!-- nx-archweave:agent-rules start(?: hash=([0-9a-f]{12}))? -->/g;
423
+ function normalizeForHash(text) {
424
+ return text.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "");
425
+ }
426
+ function hashOf(body) {
427
+ return createHash("sha256").update(normalizeForHash(body)).digest("hex").slice(0, 12);
428
+ }
429
+ function withFinalNewline(body) {
430
+ return body.endsWith("\n") ? body : `${body}
431
+ `;
432
+ }
433
+ function renderAgentRulesBlock(body, eol = "\n") {
434
+ const content = withFinalNewline(normalizeForHash(body));
435
+ const lines = `<!-- nx-archweave:agent-rules start hash=${hashOf(content)} -->
436
+ ${content}${AGENT_RULES_BLOCK_END}`;
437
+ return eol === "\n" ? lines : lines.replace(/\r?\n/g, eol);
438
+ }
439
+ function countOf(text, needle) {
440
+ return text.split(needle).length - 1;
441
+ }
442
+ function applyAgentRulesBlock(existing, body, options = {}) {
443
+ if (existing === null || existing.length === 0)
444
+ return { kind: "create", text: `${renderAgentRulesBlock(body)}
445
+ ` };
446
+ const eol = existing.includes("\r\n") ? "\r\n" : "\n";
447
+ const block = renderAgentRulesBlock(body, eol);
448
+ const starts = [...existing.matchAll(START_MARKER)];
449
+ const ends = countOf(existing, AGENT_RULES_BLOCK_END);
450
+ if (starts.length === 0 && ends === 0) {
451
+ const head = existing.endsWith("\n") ? existing : `${existing}${eol}`;
452
+ return { kind: "append", text: `${head}${eol}${block}${eol}` };
453
+ }
454
+ if (starts.length !== 1 || ends !== 1) {
455
+ return {
456
+ kind: "unrecognized",
457
+ reason: `expected one ${AGENT_RULES_BLOCK_START} \u2026 ${AGENT_RULES_BLOCK_END} pair, found ${starts.length} start and ${ends} end marker(s)`
458
+ };
459
+ }
460
+ const startMatch = starts[0];
461
+ const start = startMatch.index ?? 0;
462
+ const end = existing.indexOf(AGENT_RULES_BLOCK_END);
463
+ if (end < start)
464
+ return { kind: "unrecognized", reason: "the end marker comes before the start marker" };
465
+ const text = existing.slice(0, start) + block + existing.slice(end + AGENT_RULES_BLOCK_END.length);
466
+ if (text === existing)
467
+ return { kind: "unchanged" };
468
+ const bodyStart = start + startMatch[0].length + (existing.startsWith("\r\n", start + startMatch[0].length) ? 2 : 1);
469
+ const existingBody = normalizeForHash(existing.slice(bodyStart, end));
470
+ const generatedBody = withFinalNewline(normalizeForHash(body));
471
+ const recordedHash = startMatch[1];
472
+ const untouched = recordedHash !== void 0 ? hashOf(existingBody) === recordedHash : existingBody === generatedBody;
473
+ if (!untouched && options.overwrite !== true)
474
+ return { kind: "user-modified", existingBody, generatedBody };
475
+ return { kind: "replace", text };
476
+ }
477
+
478
+ // src/generators/add-agent-rules/render.ts
479
+ var ACTION_LINE = {
480
+ created: (result) => `${result.filePath}: created`,
481
+ appended: (result) => `${result.filePath}: nx-archweave block added after the existing content (nothing else changed)`,
482
+ updated: (result) => `${result.filePath}: nx-archweave block updated (nothing outside it changed)`,
483
+ unchanged: (result) => `${result.filePath}: unchanged`,
484
+ unrecognized: (result) => `${result.filePath}: not written \u2014 ${result.reason ?? "the nx-archweave markers could not be placed"}`,
485
+ "user-modified": (result) => `${result.filePath}: not written \u2014 the nx-archweave block was edited by hand, so it is kept`
486
+ };
487
+ function renderAddAgentRulesReport(report) {
488
+ const lines = [];
489
+ lines.push(report.dryRun ? "add-agent-rules dry-run \u2014 no files were written." : "add-agent-rules:");
490
+ lines.push(` hosts: ${report.hosts.join(", ")}`);
491
+ lines.push(` locale: ${report.locale}`);
492
+ lines.push(` teamRules: ${report.teamRules}`);
493
+ lines.push("");
494
+ for (const file of report.files) {
495
+ lines.push(` ${ACTION_LINE[file.action](file)}`);
496
+ if (file.action === "user-modified") {
497
+ lines.push(" What this run would write instead (your block \u2192 generated):");
498
+ for (const line of (file.diff ?? "").split("\n")) lines.push(` ${line}`);
499
+ lines.push(" Rerun with --overwriteBlock to replace the block with the generated one.");
500
+ }
501
+ }
502
+ const RECORD_LINE = {
503
+ created: "toolkit.config.json: aiHosts recorded (file created)",
504
+ updated: "toolkit.config.json: aiHosts recorded",
505
+ unchanged: "toolkit.config.json: aiHosts already recorded",
506
+ unrecognized: "toolkit.config.json: not a JSON object \u2014 aiHosts not recorded (fix the file, then rerun)",
507
+ "not-applied": "toolkit.config.json: aiHosts not recorded \u2014 a file above was not written",
508
+ "not-requested": null
509
+ };
510
+ const recordLine = RECORD_LINE[report.hostsRecord];
511
+ if (recordLine !== null) lines.push(` ${recordLine}`);
512
+ if (report.files.some((file) => file.action === "unrecognized")) {
513
+ lines.push("");
514
+ lines.push('Manual fix needed: keep exactly one "<!-- nx-archweave:agent-rules start \u2026 -->" \u2026 "<!-- nx-archweave:agent-rules end -->"');
515
+ lines.push("pair in the file (or remove both markers), then rerun add-agent-rules.");
516
+ }
517
+ if (report.dryRun) {
518
+ lines.push("");
519
+ lines.push("Generated content preview:");
520
+ for (const file of report.files) {
521
+ if (file.content === null) continue;
522
+ lines.push("");
523
+ lines.push(`--- ${file.filePath} ---`);
524
+ lines.push(file.content);
525
+ }
526
+ }
527
+ return lines.join("\n");
528
+ }
529
+
530
+ // src/generators/add-agent-rules/generator.ts
531
+ function resolveTeamRules(option, config) {
532
+ if (option !== void 0) return option;
533
+ const agentRules = config?.["agentRules"];
534
+ const teamRules = typeof agentRules === "object" && agentRules !== null ? agentRules["teamRules"] : void 0;
535
+ return typeof teamRules === "boolean" ? teamRules : false;
536
+ }
537
+ function resolveFsdStructure(config) {
538
+ const fsd = config?.["fsd"];
539
+ const enabled = typeof fsd === "object" && fsd !== null ? fsd["enabled"] : void 0;
540
+ if (typeof enabled === "boolean") return enabled;
541
+ const frontend = config?.["frontend"];
542
+ return typeof frontend === "string" && frontend !== "none";
543
+ }
544
+ function resolveModuleBoundaries(config) {
545
+ const backend = config?.["backend"];
546
+ return backend === "express" || backend === "nest" ? readBoundaryTags(config) : void 0;
547
+ }
548
+ var POLICY_KEYS = ["preset", "sliceGroups", "sharedPublicApi", "testFiles"];
549
+ function resolveFsdPolicyNotes(config) {
550
+ const fsd = config?.["fsd"];
551
+ if (typeof fsd !== "object" || fsd === null || !POLICY_KEYS.some((key) => key in fsd)) return void 0;
552
+ const policy = resolveFsdPolicy(fsd);
553
+ return { sliceGroups: policy.sliceGroups !== false, sharedPublicApi: policy.sharedPublicApi, testFiles: policy.testFiles !== false };
554
+ }
555
+ function runAddAgentRules(tree, options, { recordHosts = true, fsdStructure, moduleBoundaries } = {}) {
556
+ const config = readToolkitConfig(tree);
557
+ const hosts = resolveAiHostsOption(options.hosts, config);
558
+ const files = rulesFilesFor(hosts);
559
+ const locale = resolveLocaleOption(options.locale, config);
560
+ const teamRules = resolveTeamRules(options.teamRules, config);
561
+ const dryRun = options.dryRun ?? false;
562
+ const boundaries = moduleBoundaries === void 0 ? resolveModuleBoundaries(config) : moduleBoundaries ?? void 0;
563
+ const composed = composeAgentRules({
564
+ files,
565
+ locale,
566
+ teamRules,
567
+ fsdStructure: fsdStructure ?? resolveFsdStructure(config),
568
+ fsdPolicy: resolveFsdPolicyNotes(config),
569
+ ...boundaries !== void 0 ? { moduleBoundaries: boundaries } : {}
570
+ });
571
+ const results = composed.map(({ filePath, content }) => {
572
+ if (!validateAgentRules(content, teamRules ? ["team-rules"] : [])) {
573
+ throw new Error(
574
+ `Generated ${filePath} is missing a required section (Safety Rules, Language Policy${teamRules ? ", Team Rules" : ""})`
575
+ );
576
+ }
577
+ const existing = tree.read(filePath, "utf-8");
578
+ const outcome = applyAgentRulesBlock(existing, content, { overwrite: options.overwriteBlock === true });
579
+ switch (outcome.kind) {
580
+ case "unchanged":
581
+ return { filePath, action: "unchanged", reason: null, content: null };
582
+ case "unrecognized":
583
+ return { filePath, action: "unrecognized", reason: outcome.reason, content: null };
584
+ case "user-modified":
585
+ return {
586
+ filePath,
587
+ action: "user-modified",
588
+ reason: null,
589
+ content: null,
590
+ diff: renderLineDiff(outcome.existingBody, outcome.generatedBody)
591
+ };
592
+ case "create":
593
+ case "append":
594
+ case "replace": {
595
+ if (!dryRun) tree.write(filePath, outcome.text);
596
+ const action = outcome.kind === "create" ? "created" : outcome.kind === "append" ? "appended" : "updated";
597
+ return { filePath, action, reason: null, content: dryRun ? outcome.text : null };
598
+ }
599
+ }
600
+ });
601
+ const explicit = options.hosts !== void 0 && options.hosts.length > 0;
602
+ const allApplied = results.every((file) => file.action !== "unrecognized");
603
+ const hostsRecord = explicit && recordHosts ? allApplied ? recordAiHosts(tree, hosts, !dryRun) : "not-applied" : "not-requested";
604
+ return { dryRun, locale, hosts, teamRules, files: results, hostsRecord };
605
+ }
606
+ async function addAgentRulesGenerator(tree, options) {
607
+ console.log(renderAddAgentRulesReport(runAddAgentRules(tree, { ...options, dryRun: resolveDryRun(options) })));
608
+ }
609
+ var generator_default = addAgentRulesGenerator;
610
+
611
+ export {
612
+ renderAddAgentRulesReport,
613
+ runAddAgentRules,
614
+ addAgentRulesGenerator,
615
+ generator_default
616
+ };