@clearlydev/nx-archweave 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +265 -2
- package/dist/bin/review-verdict.js +330 -0
- package/dist/eslint/fsd-boundaries.d.ts +119 -0
- package/dist/eslint/fsd-boundaries.js +144 -0
- package/dist/eslint/index.d.ts +78 -0
- package/dist/eslint/index.js +256 -0
- package/dist/eslint/tsconfig-aliases.d.ts +18 -0
- package/dist/eslint/tsconfig-aliases.js +114 -0
- package/dist/generators/add-agent-rules/generator.js +550 -0
- package/dist/generators/add-agent-rules/render.js +46 -0
- package/dist/generators/add-backend/generator.js +784 -0
- package/dist/generators/add-backend/render.js +102 -0
- package/dist/generators/add-fsd-lite/generator.js +553 -0
- package/dist/generators/add-fsd-lite/render.js +72 -0
- package/dist/generators/add-github-templates/files/en/codeowners/.github/CODEOWNERS.template +4 -0
- package/dist/generators/add-github-templates/files/en/issue/.github/ISSUE_TEMPLATE/bug-report.yml.template +28 -0
- package/dist/generators/add-github-templates/files/en/issue/.github/ISSUE_TEMPLATE/feature-request.yml.template +17 -0
- package/dist/generators/add-github-templates/files/en/pr/.github/PULL_REQUEST_TEMPLATE.md.template +12 -0
- package/dist/generators/add-github-templates/files/ko/codeowners/.github/CODEOWNERS.template +4 -0
- package/dist/generators/add-github-templates/files/ko/issue/.github/ISSUE_TEMPLATE/bug-report.yml.template +28 -0
- package/dist/generators/add-github-templates/files/ko/issue/.github/ISSUE_TEMPLATE/feature-request.yml.template +17 -0
- package/dist/generators/add-github-templates/files/ko/pr/.github/PULL_REQUEST_TEMPLATE.md.template +12 -0
- package/dist/generators/add-github-templates/generator.js +125 -0
- package/dist/generators/add-github-templates/render.js +26 -0
- package/dist/generators/add-integration/generator.js +849 -0
- package/dist/generators/add-integration/render.js +34 -0
- package/dist/generators/add-review/generator.js +921 -0
- package/dist/generators/add-review/render.js +96 -0
- package/dist/generators/allow-builds/generator.js +436 -0
- package/dist/generators/allow-builds/render.js +82 -0
- package/dist/generators/doctor/generator.js +2462 -0
- package/dist/generators/doctor/integration-report.js +70 -0
- package/dist/generators/doctor/render.js +10 -0
- package/dist/generators/entity/generator.js +395 -0
- package/dist/generators/feature/generator.js +395 -0
- package/dist/generators/init/generator.js +3211 -0
- package/dist/generators/init/render.js +125 -0
- package/dist/generators/init/validation.js +74 -0
- package/dist/generators/install-integration/generator.js +1265 -0
- package/dist/generators/install-integration/render.js +128 -0
- package/dist/generators/page/generator.js +395 -0
- package/dist/generators/record-integration/generator.js +883 -0
- package/dist/generators/record-integration/render.js +31 -0
- package/dist/generators/shared/dry-run.js +12 -0
- package/dist/generators/shared/frontend-app.js +57 -0
- package/dist/generators/shared/generator.js +395 -0
- package/dist/generators/shared/project-type.js +39 -0
- package/dist/generators/shared/record-pipeline.js +34 -0
- package/dist/generators/shared/run-nx.js +24 -0
- package/dist/generators/shared/template-files.js +38 -0
- package/dist/generators/shared/toolkit-config-file.js +110 -0
- package/dist/generators/slice/core.js +185 -0
- package/dist/generators/slice/files/index/index.ts.template +14 -0
- package/dist/generators/slice/files/module/__name__.spec.ts.template +11 -0
- package/dist/generators/slice/files/module/__name__.ts.template +11 -0
- package/dist/generators/slice/files/segment/index.ts.template +20 -0
- package/dist/generators/slice/render.js +70 -0
- package/dist/generators/widget/generator.js +395 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +711 -0
- package/dist/lib/nx-plugin.d.ts +2 -0
- package/dist/lib/nx-plugin.js +7 -0
- package/dist/runtime/integrations-composition-root.js +20 -0
- package/dist/runtime/node-command-runner.js +81 -0
- package/generators.json +85 -0
- package/package.json +96 -4
- package/src/generators/add-agent-rules/schema.json +43 -0
- package/src/generators/add-backend/schema.json +39 -0
- package/src/generators/add-fsd-lite/schema.json +43 -0
- package/src/generators/add-github-templates/schema.json +35 -0
- package/src/generators/add-integration/schema.json +35 -0
- package/src/generators/add-review/schema.json +72 -0
- package/src/generators/allow-builds/schema.json +20 -0
- package/src/generators/doctor/schema.json +20 -0
- package/src/generators/entity/schema.json +37 -0
- package/src/generators/feature/schema.json +37 -0
- package/src/generators/init/schema.json +98 -0
- package/src/generators/install-integration/schema.json +35 -0
- package/src/generators/page/schema.json +53 -0
- package/src/generators/record-integration/schema.json +26 -0
- package/src/generators/shared/schema.json +32 -0
- package/src/generators/widget/schema.json +45 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
// ../templates/dist/agents/agent-sections.js
|
|
2
|
+
var UNIVERSAL_AGENT_SECTION_IDS = [
|
|
3
|
+
"think-before-coding",
|
|
4
|
+
"simplicity-first",
|
|
5
|
+
"surgical-changes",
|
|
6
|
+
"goal-driven-execution",
|
|
7
|
+
"code-structure"
|
|
8
|
+
];
|
|
9
|
+
var PLACEHOLDER_AGENT_SECTION_IDS = ["project-goal", "architecture-boundary"];
|
|
10
|
+
var TEAM_AGENT_SECTION_ID = "team-rules";
|
|
11
|
+
var FSD_AGENT_SECTION_ID = "fsd-structure";
|
|
12
|
+
var DEFAULT_AGENT_SECTION_IDS = [
|
|
13
|
+
...UNIVERSAL_AGENT_SECTION_IDS,
|
|
14
|
+
"safety-rules",
|
|
15
|
+
"language-policy",
|
|
16
|
+
...PLACEHOLDER_AGENT_SECTION_IDS
|
|
17
|
+
];
|
|
18
|
+
var EN_SECTIONS = {
|
|
19
|
+
"think-before-coding": {
|
|
20
|
+
id: "think-before-coding",
|
|
21
|
+
heading: "Think Before Coding",
|
|
22
|
+
body: [
|
|
23
|
+
"Before writing any code, restate the problem in your own words and identify what could go wrong.",
|
|
24
|
+
"Read the surrounding code and existing patterns before introducing new ones \u2014 consistency beats",
|
|
25
|
+
"personal preference. When a request is ambiguous, make the most reasonable interpretation and say",
|
|
26
|
+
"what you assumed, rather than guessing silently or blocking on a question you could answer yourself.",
|
|
27
|
+
"Prefer understanding the root cause of a problem over patching its symptoms."
|
|
28
|
+
].join("\n")
|
|
29
|
+
},
|
|
30
|
+
"simplicity-first": {
|
|
31
|
+
id: "simplicity-first",
|
|
32
|
+
heading: "Simplicity First",
|
|
33
|
+
body: [
|
|
34
|
+
"Choose the simplest design that solves the actual problem in front of you, not the one you might",
|
|
35
|
+
"have tomorrow. Avoid speculative abstraction, unnecessary configuration, and generic frameworks",
|
|
36
|
+
"built for requirements nobody has asked for yet. A straightforward, slightly duplicated solution",
|
|
37
|
+
"that is easy to read beats a clever, general one that is hard to follow. Add complexity only when",
|
|
38
|
+
"a concrete, current need demands it."
|
|
39
|
+
].join("\n")
|
|
40
|
+
},
|
|
41
|
+
"surgical-changes": {
|
|
42
|
+
id: "surgical-changes",
|
|
43
|
+
heading: "Surgical Changes",
|
|
44
|
+
body: [
|
|
45
|
+
"Change only what the task requires. Do not reformat, rename, or refactor unrelated code while",
|
|
46
|
+
"fixing something else \u2014 unrelated changes make review harder and hide the actual fix in noise.",
|
|
47
|
+
"Keep diffs small and focused so a reviewer can see exactly what changed and why. If you notice an",
|
|
48
|
+
"unrelated problem while working, note it separately instead of folding it into the current change."
|
|
49
|
+
].join("\n")
|
|
50
|
+
},
|
|
51
|
+
"goal-driven-execution": {
|
|
52
|
+
id: "goal-driven-execution",
|
|
53
|
+
heading: "Goal-Driven Execution",
|
|
54
|
+
body: [
|
|
55
|
+
"Keep the actual goal in view, not just the literal instruction. If a requested step turns out to",
|
|
56
|
+
"be unnecessary, harmful, or based on a wrong assumption once you're in the code, say so and propose",
|
|
57
|
+
"the better path instead of executing it mechanically. Finish what you start: a task is done when it",
|
|
58
|
+
"actually works, not when the obvious-looking code has been written. Verify before declaring success."
|
|
59
|
+
].join("\n")
|
|
60
|
+
},
|
|
61
|
+
"code-structure": {
|
|
62
|
+
id: "code-structure",
|
|
63
|
+
heading: "Code Structure and Abstraction",
|
|
64
|
+
body: [
|
|
65
|
+
"Keep functions and modules focused on one responsibility; split a file or function when it starts",
|
|
66
|
+
"accumulating unrelated concerns rather than letting it grow into a god object. Name things after",
|
|
67
|
+
"what they do or represent, not after their implementation detail. Extract an abstraction only after",
|
|
68
|
+
"a pattern has actually repeated \u2014 not in anticipation of a repetition that may never come."
|
|
69
|
+
].join("\n")
|
|
70
|
+
},
|
|
71
|
+
"safety-rules": {
|
|
72
|
+
id: "safety-rules",
|
|
73
|
+
heading: "Safety Rules",
|
|
74
|
+
body: [
|
|
75
|
+
"Never force-push to shared branches, never bypass required status checks, and never disable or",
|
|
76
|
+
"delete CI/CD configuration just to make a failing check pass.",
|
|
77
|
+
"Do not read, print, or commit the contents of secret files (`.env`, credential files, private",
|
|
78
|
+
"keys) \u2014 treat anything matching a common secret pattern as off-limits unless the user has already",
|
|
79
|
+
"shared its contents with you directly.",
|
|
80
|
+
"Do not run destructive commands (`rm -rf`, `git reset --hard`, `git clean -f`, force pushes)",
|
|
81
|
+
"without explicit confirmation, and check for uncommitted work first.",
|
|
82
|
+
"Do not send repository contents, credentials, or user data to any external service.",
|
|
83
|
+
"When you are unsure whether an action is safe, stop and ask instead of proceeding.",
|
|
84
|
+
"",
|
|
85
|
+
"<!-- TODO: list project-specific protected resources (e.g. production config files, protected",
|
|
86
|
+
"branches, restricted directories, external services this project must never call in tests). -->"
|
|
87
|
+
].join("\n")
|
|
88
|
+
},
|
|
89
|
+
"language-policy": {
|
|
90
|
+
id: "language-policy",
|
|
91
|
+
heading: "Language Policy",
|
|
92
|
+
body: [
|
|
93
|
+
"Communicate with the user in English. Write code, identifiers, comments, commit messages, and",
|
|
94
|
+
"documentation in English as well, unless the user explicitly asks for another language for a",
|
|
95
|
+
"specific piece of content."
|
|
96
|
+
].join("\n")
|
|
97
|
+
},
|
|
98
|
+
"team-rules": {
|
|
99
|
+
id: "team-rules",
|
|
100
|
+
heading: "Team Rules",
|
|
101
|
+
body: [
|
|
102
|
+
"This repository is shared by a team: optimize for the reviewer, not for yourself.",
|
|
103
|
+
"Keep each pull request small and about one thing, fill in the pull request template, and link",
|
|
104
|
+
"the issue it closes.",
|
|
105
|
+
"Treat review findings as work, not opinions: address every P0/P1 finding before asking for",
|
|
106
|
+
"another look, answer each comment, and never merge your own pull request or dismiss a review",
|
|
107
|
+
"to get it through.",
|
|
108
|
+
"Respect module ownership: a change that crosses a module or layer boundary needs that",
|
|
109
|
+
"module's owner in the review. Never weaken a module-boundary lint rule, add an eslint-disable",
|
|
110
|
+
"for one, or retag a project to get an import past the boundary \u2014 propose the boundary change",
|
|
111
|
+
"in the pull request description instead.",
|
|
112
|
+
"Changes to shared code (the `shared` layer, `shared-types`, root configuration) affect every",
|
|
113
|
+
"team member: keep them backward compatible, call them out in the pull request, and keep them",
|
|
114
|
+
"separate from feature work.",
|
|
115
|
+
"Never commit directly to a protected branch; work on a branch and bring it up to date with",
|
|
116
|
+
"its base before requesting review.",
|
|
117
|
+
"",
|
|
118
|
+
"<!-- TODO: list team-specific conventions (module owners / CODEOWNERS, required reviewers,",
|
|
119
|
+
"branch naming, release process). -->"
|
|
120
|
+
].join("\n")
|
|
121
|
+
},
|
|
122
|
+
"fsd-structure": {
|
|
123
|
+
id: "fsd-structure",
|
|
124
|
+
heading: "Frontend Structure (FSD Lite)",
|
|
125
|
+
body: [
|
|
126
|
+
'The frontend app follows canonical Feature-Sliced Design. "Lite" means only that the default scope',
|
|
127
|
+
"is `app`, `pages`, `features`, `entities` and `shared` (`widgets` only on request) and that Solo",
|
|
128
|
+
"workspaces get warnings instead of errors \u2014 the rules themselves are the canonical ones:",
|
|
129
|
+
"- A layer imports only layers below it: app > pages > widgets > features > entities > shared.",
|
|
130
|
+
"- Slices on one layer (pages, widgets, features, entities) never import each other; move the",
|
|
131
|
+
" shared part one layer down, or expose it through the other slice's `@x/<consumer>` public API.",
|
|
132
|
+
"- Import a slice only through its `index.ts`; never reach into its `ui/`, `model/` or `api/` files.",
|
|
133
|
+
"- `app` and `shared` hold segments, not slices; shared segments are named by purpose (`ui`, `api`,",
|
|
134
|
+
" `model`, `lib`, `config`), never by essence (`components`, `hooks`, `utils`, `types`).",
|
|
135
|
+
"Create slices with the generators (`nx g @clearlydev/nx-archweave:page|widget|feature|entity|shared --name=\u2026`,",
|
|
136
|
+
"`--segments=ui,api` for extra segments).",
|
|
137
|
+
"The `archweave/fsd-boundaries` ESLint rule reports violations: fix the import, never disable the",
|
|
138
|
+
"rule or add an eslint-disable for it. On Next.js the pages layer is the `_pages/` directory."
|
|
139
|
+
].join("\n")
|
|
140
|
+
},
|
|
141
|
+
"project-goal": {
|
|
142
|
+
id: "project-goal",
|
|
143
|
+
heading: "Project Goal",
|
|
144
|
+
body: "<!-- TODO: describe this project's goal, target users, and what success looks like. -->"
|
|
145
|
+
},
|
|
146
|
+
"architecture-boundary": {
|
|
147
|
+
id: "architecture-boundary",
|
|
148
|
+
heading: "Architecture Boundary",
|
|
149
|
+
body: "<!-- TODO: describe this project's module boundaries, ownership rules, and which modules may or may not depend on which. -->"
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var KO_SECTIONS = {
|
|
153
|
+
"think-before-coding": {
|
|
154
|
+
id: "think-before-coding",
|
|
155
|
+
heading: "\uCF54\uB529 \uC804\uC5D0 \uBA3C\uC800 \uC0DD\uAC01\uD558\uAE30",
|
|
156
|
+
body: [
|
|
157
|
+
"\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.",
|
|
158
|
+
"\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.",
|
|
159
|
+
"\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",
|
|
160
|
+
"\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",
|
|
161
|
+
"\uCABD\uC744 \uC6B0\uC120\uD55C\uB2E4."
|
|
162
|
+
].join("\n")
|
|
163
|
+
},
|
|
164
|
+
"simplicity-first": {
|
|
165
|
+
id: "simplicity-first",
|
|
166
|
+
heading: "\uB2E8\uC21C\uD568 \uC6B0\uC120",
|
|
167
|
+
body: [
|
|
168
|
+
"\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",
|
|
169
|
+
"\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",
|
|
170
|
+
"\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.",
|
|
171
|
+
"\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."
|
|
172
|
+
].join("\n")
|
|
173
|
+
},
|
|
174
|
+
"surgical-changes": {
|
|
175
|
+
id: "surgical-changes",
|
|
176
|
+
heading: "\uC678\uACFC\uC801 \uBCC0\uACBD",
|
|
177
|
+
body: [
|
|
178
|
+
"\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",
|
|
179
|
+
"\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",
|
|
180
|
+
"\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.",
|
|
181
|
+
"\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."
|
|
182
|
+
].join("\n")
|
|
183
|
+
},
|
|
184
|
+
"goal-driven-execution": {
|
|
185
|
+
id: "goal-driven-execution",
|
|
186
|
+
heading: "\uBAA9\uD45C \uC9C0\uD5A5 \uC2E4\uD589",
|
|
187
|
+
body: [
|
|
188
|
+
"\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",
|
|
189
|
+
"\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",
|
|
190
|
+
"\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",
|
|
191
|
+
"\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."
|
|
192
|
+
].join("\n")
|
|
193
|
+
},
|
|
194
|
+
"code-structure": {
|
|
195
|
+
id: "code-structure",
|
|
196
|
+
heading: "\uCF54\uB4DC \uAD6C\uC870\uC640 \uCD94\uC0C1\uD654",
|
|
197
|
+
body: [
|
|
198
|
+
"\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",
|
|
199
|
+
"\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",
|
|
200
|
+
"\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",
|
|
201
|
+
"\uC608\uCE21\uB9CC\uC73C\uB85C \uBBF8\uB9AC \uCD94\uCD9C\uD558\uC9C0 \uC54A\uB294\uB2E4."
|
|
202
|
+
].join("\n")
|
|
203
|
+
},
|
|
204
|
+
"safety-rules": {
|
|
205
|
+
id: "safety-rules",
|
|
206
|
+
heading: "\uC548\uC804 \uADDC\uCE59",
|
|
207
|
+
body: [
|
|
208
|
+
"\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",
|
|
209
|
+
"\uC704\uD574 CI/CD \uC124\uC815\uC744 \uBE44\uD65C\uC131\uD654\uD558\uAC70\uB098 \uC0AD\uC81C\uD558\uC9C0 \uC54A\uB294\uB2E4.",
|
|
210
|
+
"\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",
|
|
211
|
+
"\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",
|
|
212
|
+
"\uCDE8\uAE09\uD55C\uB2E4.",
|
|
213
|
+
"\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",
|
|
214
|
+
"\uC54A\uC73C\uBA70, \uC2E4\uD589 \uC804 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 \uC791\uC5C5\uC774 \uC788\uB294\uC9C0 \uBA3C\uC800 \uD655\uC778\uD55C\uB2E4.",
|
|
215
|
+
"\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.",
|
|
216
|
+
"\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.",
|
|
217
|
+
"",
|
|
218
|
+
// The marker itself stays in English, matching the Language Policy's "code/comments in
|
|
219
|
+
// English" rule and giving tooling a single locale-invariant string to check for.
|
|
220
|
+
"<!-- TODO: list project-specific protected resources (e.g. production config files, protected",
|
|
221
|
+
"branches, restricted directories, external services this project must never call in tests). -->"
|
|
222
|
+
].join("\n")
|
|
223
|
+
},
|
|
224
|
+
"language-policy": {
|
|
225
|
+
id: "language-policy",
|
|
226
|
+
heading: "\uC5B8\uC5B4 \uC815\uCC45",
|
|
227
|
+
body: [
|
|
228
|
+
"\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",
|
|
229
|
+
"\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."
|
|
230
|
+
].join("\n")
|
|
231
|
+
},
|
|
232
|
+
"team-rules": {
|
|
233
|
+
id: "team-rules",
|
|
234
|
+
heading: "\uD300 \uADDC\uCE59",
|
|
235
|
+
body: [
|
|
236
|
+
"\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.",
|
|
237
|
+
"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.",
|
|
238
|
+
"\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,",
|
|
239
|
+
"\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.",
|
|
240
|
+
"\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.",
|
|
241
|
+
"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",
|
|
242
|
+
"\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.",
|
|
243
|
+
"\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",
|
|
244
|
+
"\uD638\uD658\uC744 \uC720\uC9C0\uD558\uACE0, PR\uC5D0 \uBA85\uC2DC\uD558\uBA70, \uAE30\uB2A5 \uC791\uC5C5\uACFC \uBD84\uB9AC\uD55C\uB2E4.",
|
|
245
|
+
"\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.",
|
|
246
|
+
"",
|
|
247
|
+
// The marker stays in English, matching the Safety Rules precedent (one locale-invariant
|
|
248
|
+
// string for tooling to check).
|
|
249
|
+
"<!-- TODO: list team-specific conventions (module owners / CODEOWNERS, required reviewers,",
|
|
250
|
+
"branch naming, release process). -->"
|
|
251
|
+
].join("\n")
|
|
252
|
+
},
|
|
253
|
+
"fsd-structure": {
|
|
254
|
+
id: "fsd-structure",
|
|
255
|
+
heading: "\uD504\uB860\uD2B8\uC5D4\uB4DC \uAD6C\uC870 (FSD Lite)",
|
|
256
|
+
body: [
|
|
257
|
+
'\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`,',
|
|
258
|
+
"`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",
|
|
259
|
+
"\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:",
|
|
260
|
+
"- \uB808\uC774\uC5B4\uB294 \uC790\uAE30\uBCF4\uB2E4 \uC544\uB798 \uB808\uC774\uC5B4\uB9CC import\uD55C\uB2E4: app > pages > widgets > features > entities > shared.",
|
|
261
|
+
"- \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",
|
|
262
|
+
" \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.",
|
|
263
|
+
"- \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.",
|
|
264
|
+
"- `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`,",
|
|
265
|
+
" `model`, `lib`, `config`)\uC73C\uB85C \uC9D3\uACE0, \uBCF8\uC9C8\uD615(`components`, `hooks`, `utils`, `types`)\uC740 \uC4F0\uC9C0 \uC54A\uB294\uB2E4.",
|
|
266
|
+
"\uC2AC\uB77C\uC774\uC2A4\uB294 generator\uB85C \uB9CC\uB4E0\uB2E4(`nx g @clearlydev/nx-archweave:page|widget|feature|entity|shared --name=\u2026`,",
|
|
267
|
+
"\uCD94\uAC00 \uC138\uADF8\uBA3C\uD2B8\uB294 `--segments=ui,api`).",
|
|
268
|
+
"`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",
|
|
269
|
+
"\uB118\uAE30\uC9C0 \uC54A\uB294\uB2E4. Next.js\uC5D0\uC11C\uB294 pages \uB808\uC774\uC5B4\uAC00 `_pages/` \uD3F4\uB354\uB2E4."
|
|
270
|
+
].join("\n")
|
|
271
|
+
},
|
|
272
|
+
"project-goal": {
|
|
273
|
+
id: "project-goal",
|
|
274
|
+
heading: "\uD504\uB85C\uC81D\uD2B8 \uBAA9\uD45C",
|
|
275
|
+
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. -->"
|
|
276
|
+
},
|
|
277
|
+
"architecture-boundary": {
|
|
278
|
+
id: "architecture-boundary",
|
|
279
|
+
heading: "\uC544\uD0A4\uD14D\uCC98 \uACBD\uACC4",
|
|
280
|
+
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. -->"
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
var AGENT_RULE_SECTIONS = {
|
|
284
|
+
en: EN_SECTIONS,
|
|
285
|
+
ko: KO_SECTIONS
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// ../template-engine/dist/ai-hosts/ai-hosts.js
|
|
289
|
+
var AI_HOSTS = [
|
|
290
|
+
{ id: "claude-code", label: "Claude Code", nxAgent: "claude", rulesFile: "CLAUDE.md", reviewProvider: "claude" },
|
|
291
|
+
{ id: "codex", label: "Codex", nxAgent: "codex", rulesFile: "AGENTS.md", reviewProvider: "gpt" },
|
|
292
|
+
{ id: "cursor", label: "Cursor", nxAgent: "cursor", rulesFile: "AGENTS.md", reviewProvider: "cursor" },
|
|
293
|
+
{ id: "gemini-cli", label: "Gemini CLI", nxAgent: "gemini", rulesFile: "AGENTS.md", reviewProvider: "gemini" }
|
|
294
|
+
];
|
|
295
|
+
var AI_HOST_IDS = AI_HOSTS.map((host) => host.id);
|
|
296
|
+
var DEFAULT_AI_HOSTS = ["claude-code"];
|
|
297
|
+
function isAiHost(value) {
|
|
298
|
+
return typeof value === "string" && AI_HOST_IDS.includes(value);
|
|
299
|
+
}
|
|
300
|
+
function definitionOf(id) {
|
|
301
|
+
const host = AI_HOSTS.find((candidate) => candidate.id === id);
|
|
302
|
+
if (!host)
|
|
303
|
+
throw new Error(`Unknown AI host "${id}"`);
|
|
304
|
+
return host;
|
|
305
|
+
}
|
|
306
|
+
function rulesFilesFor(hosts) {
|
|
307
|
+
const picked = hosts.length > 0 ? hosts : DEFAULT_AI_HOSTS;
|
|
308
|
+
const files = new Set(picked.map((id) => definitionOf(id).rulesFile));
|
|
309
|
+
return ["CLAUDE.md", "AGENTS.md"].filter((file) => files.has(file));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ../template-engine/dist/agent-rules/compose-agent-rules.js
|
|
313
|
+
function resolveDefaultAgentSectionIds(teamRules, fsdStructure = false) {
|
|
314
|
+
let ids = [...DEFAULT_AGENT_SECTION_IDS];
|
|
315
|
+
const insertAfter = (anchorId, id) => {
|
|
316
|
+
const anchor = ids.indexOf(anchorId);
|
|
317
|
+
if (anchor === -1) {
|
|
318
|
+
throw new Error(`composeAgentRules: the default composition no longer contains ${anchorId}`);
|
|
319
|
+
}
|
|
320
|
+
ids = [...ids.slice(0, anchor + 1), id, ...ids.slice(anchor + 1)];
|
|
321
|
+
};
|
|
322
|
+
if (fsdStructure)
|
|
323
|
+
insertAfter("code-structure", FSD_AGENT_SECTION_ID);
|
|
324
|
+
if (teamRules)
|
|
325
|
+
insertAfter("language-policy", TEAM_AGENT_SECTION_ID);
|
|
326
|
+
return ids;
|
|
327
|
+
}
|
|
328
|
+
function renderSections(sections) {
|
|
329
|
+
return `${sections.map((section) => `## ${section.heading}
|
|
330
|
+
|
|
331
|
+
${section.body}`).join("\n\n")}
|
|
332
|
+
`;
|
|
333
|
+
}
|
|
334
|
+
function composeAgentRules(input) {
|
|
335
|
+
const sectionIds = input.sections ?? resolveDefaultAgentSectionIds(input.teamRules ?? false, input.fsdStructure ?? false);
|
|
336
|
+
const sections = sectionIds.map((id) => AGENT_RULE_SECTIONS[input.locale][id]);
|
|
337
|
+
const content = renderSections(sections);
|
|
338
|
+
return input.files.map((filePath) => ({ filePath, content }));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ../template-engine/dist/agent-rules/validate-agent-rules.js
|
|
342
|
+
var LOCALES = ["en", "ko"];
|
|
343
|
+
function headingsFor(id) {
|
|
344
|
+
return LOCALES.map((locale) => `## ${AGENT_RULE_SECTIONS[locale][id].heading}`);
|
|
345
|
+
}
|
|
346
|
+
var REQUIRED_SECTION_IDS = ["safety-rules", "language-policy"];
|
|
347
|
+
function validateAgentRules(content, extraRequired = []) {
|
|
348
|
+
return [...REQUIRED_SECTION_IDS, ...extraRequired].every((id) => headingsFor(id).some((heading) => content.includes(heading)));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ../template-engine/dist/agent-rules/marker-block.js
|
|
352
|
+
var AGENT_RULES_BLOCK_START = "<!-- nx-archweave:agent-rules start -->";
|
|
353
|
+
var AGENT_RULES_BLOCK_END = "<!-- nx-archweave:agent-rules end -->";
|
|
354
|
+
function renderAgentRulesBlock(body, eol = "\n") {
|
|
355
|
+
const lines = `${AGENT_RULES_BLOCK_START}
|
|
356
|
+
${body.endsWith("\n") ? body : `${body}
|
|
357
|
+
`}${AGENT_RULES_BLOCK_END}`;
|
|
358
|
+
return eol === "\n" ? lines : lines.replace(/\r?\n/g, eol);
|
|
359
|
+
}
|
|
360
|
+
function countOf(text, needle) {
|
|
361
|
+
return text.split(needle).length - 1;
|
|
362
|
+
}
|
|
363
|
+
function applyAgentRulesBlock(existing, body) {
|
|
364
|
+
if (existing === null || existing.length === 0)
|
|
365
|
+
return { kind: "create", text: `${renderAgentRulesBlock(body)}
|
|
366
|
+
` };
|
|
367
|
+
const eol = existing.includes("\r\n") ? "\r\n" : "\n";
|
|
368
|
+
const block = renderAgentRulesBlock(body, eol);
|
|
369
|
+
const starts = countOf(existing, AGENT_RULES_BLOCK_START);
|
|
370
|
+
const ends = countOf(existing, AGENT_RULES_BLOCK_END);
|
|
371
|
+
if (starts === 0 && ends === 0) {
|
|
372
|
+
const head = existing.endsWith("\n") ? existing : `${existing}${eol}`;
|
|
373
|
+
return { kind: "append", text: `${head}${eol}${block}${eol}` };
|
|
374
|
+
}
|
|
375
|
+
if (starts !== 1 || ends !== 1) {
|
|
376
|
+
return {
|
|
377
|
+
kind: "unrecognized",
|
|
378
|
+
reason: `expected one ${AGENT_RULES_BLOCK_START} \u2026 ${AGENT_RULES_BLOCK_END} pair, found ${starts} start and ${ends} end marker(s)`
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
const start = existing.indexOf(AGENT_RULES_BLOCK_START);
|
|
382
|
+
const end = existing.indexOf(AGENT_RULES_BLOCK_END);
|
|
383
|
+
if (end < start)
|
|
384
|
+
return { kind: "unrecognized", reason: "the end marker comes before the start marker" };
|
|
385
|
+
const text = existing.slice(0, start) + block + existing.slice(end + AGENT_RULES_BLOCK_END.length);
|
|
386
|
+
return text === existing ? { kind: "unchanged" } : { kind: "replace", text };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/generators/shared/dry-run.ts
|
|
390
|
+
function resolveDryRun(options) {
|
|
391
|
+
return process.env["NX_DRY_RUN"] === "true" || (options.dryRun ?? false);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/generators/shared/toolkit-config-file.ts
|
|
395
|
+
var TOOLKIT_CONFIG_PATH = "toolkit.config.json";
|
|
396
|
+
function detectIndent(raw) {
|
|
397
|
+
for (const line of raw.split("\n")) {
|
|
398
|
+
const match = /^(\t+| +)/.exec(line);
|
|
399
|
+
if (match) return match[1];
|
|
400
|
+
}
|
|
401
|
+
return " ";
|
|
402
|
+
}
|
|
403
|
+
function readToolkitConfigFile(tree) {
|
|
404
|
+
const raw = tree.read(TOOLKIT_CONFIG_PATH, "utf-8");
|
|
405
|
+
if (raw === null) return { raw: null, parsed: null };
|
|
406
|
+
try {
|
|
407
|
+
const parsed = JSON.parse(raw);
|
|
408
|
+
return {
|
|
409
|
+
raw,
|
|
410
|
+
parsed: typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null
|
|
411
|
+
};
|
|
412
|
+
} catch {
|
|
413
|
+
return { raw, parsed: null };
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
function readToolkitConfig(tree) {
|
|
417
|
+
return readToolkitConfigFile(tree).parsed;
|
|
418
|
+
}
|
|
419
|
+
function resolveLocaleOption(option, config) {
|
|
420
|
+
if (option) return option;
|
|
421
|
+
const locale = config?.["locale"];
|
|
422
|
+
return locale === "ko" || locale === "en" ? locale : "en";
|
|
423
|
+
}
|
|
424
|
+
function resolveAiHostsOption(option, config) {
|
|
425
|
+
if (option !== void 0 && option.length > 0) return [...new Set(option)];
|
|
426
|
+
const recorded = config?.["aiHosts"];
|
|
427
|
+
const fromConfig = Array.isArray(recorded) ? [...new Set(recorded.filter(isAiHost))] : [];
|
|
428
|
+
return fromConfig.length > 0 ? fromConfig : [...DEFAULT_AI_HOSTS];
|
|
429
|
+
}
|
|
430
|
+
function recordAiHosts(tree, hosts, write) {
|
|
431
|
+
const { raw, parsed } = readToolkitConfigFile(tree);
|
|
432
|
+
if (raw !== null && parsed === null) return "unrecognized";
|
|
433
|
+
const existing = parsed ?? {};
|
|
434
|
+
const recorded = existing["aiHosts"];
|
|
435
|
+
if (Array.isArray(recorded) && recorded.length === hosts.length && recorded.every((id, index) => id === hosts[index])) {
|
|
436
|
+
return "unchanged";
|
|
437
|
+
}
|
|
438
|
+
if (write) {
|
|
439
|
+
const indent = raw === null ? " " : detectIndent(raw);
|
|
440
|
+
const body = JSON.stringify({ ...existing, aiHosts: [...hosts] }, null, indent);
|
|
441
|
+
tree.write(TOOLKIT_CONFIG_PATH, raw === null || raw.endsWith("\n") ? `${body}
|
|
442
|
+
` : body);
|
|
443
|
+
}
|
|
444
|
+
return raw === null ? "created" : "updated";
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/generators/add-agent-rules/render.ts
|
|
448
|
+
var ACTION_LINE = {
|
|
449
|
+
created: (result) => `${result.filePath}: created`,
|
|
450
|
+
appended: (result) => `${result.filePath}: nx-archweave block added after the existing content (nothing else changed)`,
|
|
451
|
+
updated: (result) => `${result.filePath}: nx-archweave block updated (nothing outside it changed)`,
|
|
452
|
+
unchanged: (result) => `${result.filePath}: unchanged`,
|
|
453
|
+
unrecognized: (result) => `${result.filePath}: not written \u2014 ${result.reason ?? "the nx-archweave markers could not be placed"}`
|
|
454
|
+
};
|
|
455
|
+
function renderAddAgentRulesReport(report) {
|
|
456
|
+
const lines = [];
|
|
457
|
+
lines.push(report.dryRun ? "add-agent-rules dry-run \u2014 no files were written." : "add-agent-rules:");
|
|
458
|
+
lines.push(` hosts: ${report.hosts.join(", ")}`);
|
|
459
|
+
lines.push(` locale: ${report.locale}`);
|
|
460
|
+
lines.push(` teamRules: ${report.teamRules}`);
|
|
461
|
+
lines.push("");
|
|
462
|
+
for (const file of report.files) {
|
|
463
|
+
lines.push(` ${ACTION_LINE[file.action](file)}`);
|
|
464
|
+
}
|
|
465
|
+
const RECORD_LINE = {
|
|
466
|
+
created: "toolkit.config.json: aiHosts recorded (file created)",
|
|
467
|
+
updated: "toolkit.config.json: aiHosts recorded",
|
|
468
|
+
unchanged: "toolkit.config.json: aiHosts already recorded",
|
|
469
|
+
unrecognized: "toolkit.config.json: not a JSON object \u2014 aiHosts not recorded (fix the file, then rerun)",
|
|
470
|
+
"not-applied": "toolkit.config.json: aiHosts not recorded \u2014 a file above was not written",
|
|
471
|
+
"not-requested": null
|
|
472
|
+
};
|
|
473
|
+
const recordLine = RECORD_LINE[report.hostsRecord];
|
|
474
|
+
if (recordLine !== null) lines.push(` ${recordLine}`);
|
|
475
|
+
if (report.files.some((file) => file.action === "unrecognized")) {
|
|
476
|
+
lines.push("");
|
|
477
|
+
lines.push('Manual fix needed: keep exactly one "<!-- nx-archweave:agent-rules start -->" \u2026 "<!-- nx-archweave:agent-rules end -->"');
|
|
478
|
+
lines.push("pair in the file (or remove both markers), then rerun add-agent-rules.");
|
|
479
|
+
}
|
|
480
|
+
if (report.dryRun) {
|
|
481
|
+
lines.push("");
|
|
482
|
+
lines.push("Generated content preview:");
|
|
483
|
+
for (const file of report.files) {
|
|
484
|
+
if (file.content === null) continue;
|
|
485
|
+
lines.push("");
|
|
486
|
+
lines.push(`--- ${file.filePath} ---`);
|
|
487
|
+
lines.push(file.content);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return lines.join("\n");
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// src/generators/add-agent-rules/generator.ts
|
|
494
|
+
function resolveTeamRules(option, config) {
|
|
495
|
+
if (option !== void 0) return option;
|
|
496
|
+
const agentRules = config?.["agentRules"];
|
|
497
|
+
const teamRules = typeof agentRules === "object" && agentRules !== null ? agentRules["teamRules"] : void 0;
|
|
498
|
+
return typeof teamRules === "boolean" ? teamRules : false;
|
|
499
|
+
}
|
|
500
|
+
function resolveFsdStructure(config) {
|
|
501
|
+
const fsd = config?.["fsd"];
|
|
502
|
+
const enabled = typeof fsd === "object" && fsd !== null ? fsd["enabled"] : void 0;
|
|
503
|
+
if (typeof enabled === "boolean") return enabled;
|
|
504
|
+
const frontend = config?.["frontend"];
|
|
505
|
+
return typeof frontend === "string" && frontend !== "none";
|
|
506
|
+
}
|
|
507
|
+
function runAddAgentRules(tree, options, { recordHosts = true, fsdStructure } = {}) {
|
|
508
|
+
const config = readToolkitConfig(tree);
|
|
509
|
+
const hosts = resolveAiHostsOption(options.hosts, config);
|
|
510
|
+
const files = rulesFilesFor(hosts);
|
|
511
|
+
const locale = resolveLocaleOption(options.locale, config);
|
|
512
|
+
const teamRules = resolveTeamRules(options.teamRules, config);
|
|
513
|
+
const dryRun = options.dryRun ?? false;
|
|
514
|
+
const composed = composeAgentRules({ files, locale, teamRules, fsdStructure: fsdStructure ?? resolveFsdStructure(config) });
|
|
515
|
+
const results = composed.map(({ filePath, content }) => {
|
|
516
|
+
if (!validateAgentRules(content, teamRules ? ["team-rules"] : [])) {
|
|
517
|
+
throw new Error(
|
|
518
|
+
`Generated ${filePath} is missing a required section (Safety Rules, Language Policy${teamRules ? ", Team Rules" : ""})`
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
const existing = tree.read(filePath, "utf-8");
|
|
522
|
+
const outcome = applyAgentRulesBlock(existing, content);
|
|
523
|
+
switch (outcome.kind) {
|
|
524
|
+
case "unchanged":
|
|
525
|
+
return { filePath, action: "unchanged", reason: null, content: null };
|
|
526
|
+
case "unrecognized":
|
|
527
|
+
return { filePath, action: "unrecognized", reason: outcome.reason, content: null };
|
|
528
|
+
case "create":
|
|
529
|
+
case "append":
|
|
530
|
+
case "replace": {
|
|
531
|
+
if (!dryRun) tree.write(filePath, outcome.text);
|
|
532
|
+
const action = outcome.kind === "create" ? "created" : outcome.kind === "append" ? "appended" : "updated";
|
|
533
|
+
return { filePath, action, reason: null, content: dryRun ? outcome.text : null };
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
const explicit = options.hosts !== void 0 && options.hosts.length > 0;
|
|
538
|
+
const allApplied = results.every((file) => file.action !== "unrecognized");
|
|
539
|
+
const hostsRecord = explicit && recordHosts ? allApplied ? recordAiHosts(tree, hosts, !dryRun) : "not-applied" : "not-requested";
|
|
540
|
+
return { dryRun, locale, hosts, teamRules, files: results, hostsRecord };
|
|
541
|
+
}
|
|
542
|
+
async function addAgentRulesGenerator(tree, options) {
|
|
543
|
+
console.log(renderAddAgentRulesReport(runAddAgentRules(tree, { ...options, dryRun: resolveDryRun(options) })));
|
|
544
|
+
}
|
|
545
|
+
var generator_default = addAgentRulesGenerator;
|
|
546
|
+
export {
|
|
547
|
+
addAgentRulesGenerator,
|
|
548
|
+
generator_default as default,
|
|
549
|
+
runAddAgentRules
|
|
550
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const ACTION_LINE = {
|
|
2
|
+
created: (result) => `${result.filePath}: created`,
|
|
3
|
+
appended: (result) => `${result.filePath}: nx-archweave block added after the existing content (nothing else changed)`,
|
|
4
|
+
updated: (result) => `${result.filePath}: nx-archweave block updated (nothing outside it changed)`,
|
|
5
|
+
unchanged: (result) => `${result.filePath}: unchanged`,
|
|
6
|
+
unrecognized: (result) => `${result.filePath}: not written — ${result.reason ?? 'the nx-archweave markers could not be placed'}`,
|
|
7
|
+
};
|
|
8
|
+
export function renderAddAgentRulesReport(report) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
lines.push(report.dryRun ? 'add-agent-rules dry-run — no files were written.' : 'add-agent-rules:');
|
|
11
|
+
lines.push(` hosts: ${report.hosts.join(', ')}`);
|
|
12
|
+
lines.push(` locale: ${report.locale}`);
|
|
13
|
+
lines.push(` teamRules: ${report.teamRules}`);
|
|
14
|
+
lines.push('');
|
|
15
|
+
for (const file of report.files) {
|
|
16
|
+
lines.push(` ${ACTION_LINE[file.action](file)}`);
|
|
17
|
+
}
|
|
18
|
+
const RECORD_LINE = {
|
|
19
|
+
created: 'toolkit.config.json: aiHosts recorded (file created)',
|
|
20
|
+
updated: 'toolkit.config.json: aiHosts recorded',
|
|
21
|
+
unchanged: 'toolkit.config.json: aiHosts already recorded',
|
|
22
|
+
unrecognized: 'toolkit.config.json: not a JSON object — aiHosts not recorded (fix the file, then rerun)',
|
|
23
|
+
'not-applied': 'toolkit.config.json: aiHosts not recorded — a file above was not written',
|
|
24
|
+
'not-requested': null,
|
|
25
|
+
};
|
|
26
|
+
const recordLine = RECORD_LINE[report.hostsRecord];
|
|
27
|
+
if (recordLine !== null)
|
|
28
|
+
lines.push(` ${recordLine}`);
|
|
29
|
+
if (report.files.some((file) => file.action === 'unrecognized')) {
|
|
30
|
+
lines.push('');
|
|
31
|
+
lines.push('Manual fix needed: keep exactly one "<!-- nx-archweave:agent-rules start -->" … "<!-- nx-archweave:agent-rules end -->"');
|
|
32
|
+
lines.push('pair in the file (or remove both markers), then rerun add-agent-rules.');
|
|
33
|
+
}
|
|
34
|
+
if (report.dryRun) {
|
|
35
|
+
lines.push('');
|
|
36
|
+
lines.push('Generated content preview:');
|
|
37
|
+
for (const file of report.files) {
|
|
38
|
+
if (file.content === null)
|
|
39
|
+
continue;
|
|
40
|
+
lines.push('');
|
|
41
|
+
lines.push(`--- ${file.filePath} ---`);
|
|
42
|
+
lines.push(file.content);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return lines.join('\n');
|
|
46
|
+
}
|