@sha3/code 1.0.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.
Files changed (165) hide show
  1. package/AGENTS.md +75 -0
  2. package/README.md +554 -0
  3. package/ai/adapters/codex.md +7 -0
  4. package/ai/adapters/copilot.md +7 -0
  5. package/ai/adapters/cursor.md +7 -0
  6. package/ai/adapters/windsurf.md +8 -0
  7. package/ai/constitution.md +12 -0
  8. package/bin/code-standards.mjs +47 -0
  9. package/biome.json +37 -0
  10. package/index.mjs +11 -0
  11. package/lib/cli/parse-args.mjs +416 -0
  12. package/lib/cli/post-run-guidance.mjs +43 -0
  13. package/lib/cli/run-init.mjs +123 -0
  14. package/lib/cli/run-profile.mjs +46 -0
  15. package/lib/cli/run-refactor.mjs +152 -0
  16. package/lib/cli/run-verify.mjs +67 -0
  17. package/lib/constants.mjs +167 -0
  18. package/lib/contract/load-rule-catalog.mjs +12 -0
  19. package/lib/contract/render-agents.mjs +79 -0
  20. package/lib/contract/render-contract-json.mjs +7 -0
  21. package/lib/contract/resolve-contract.mjs +52 -0
  22. package/lib/paths.mjs +50 -0
  23. package/lib/profile.mjs +108 -0
  24. package/lib/project/ai-instructions.mjs +28 -0
  25. package/lib/project/biome-ignore.mjs +14 -0
  26. package/lib/project/managed-files.mjs +105 -0
  27. package/lib/project/package-metadata.mjs +132 -0
  28. package/lib/project/prompt-files.mjs +111 -0
  29. package/lib/project/template-resolution.mjs +70 -0
  30. package/lib/refactor/materialize-refactor-context.mjs +106 -0
  31. package/lib/refactor/preservation-questions.mjs +33 -0
  32. package/lib/refactor/public-contract-extractor.mjs +22 -0
  33. package/lib/refactor/render-analysis-summary.mjs +50 -0
  34. package/lib/refactor/source-analysis.mjs +74 -0
  35. package/lib/utils/fs.mjs +220 -0
  36. package/lib/utils/prompts.mjs +63 -0
  37. package/lib/utils/text.mjs +43 -0
  38. package/lib/verify/change-audit-verifier.mjs +140 -0
  39. package/lib/verify/change-context.mjs +36 -0
  40. package/lib/verify/error-handling-verifier.mjs +164 -0
  41. package/lib/verify/explain-rule.mjs +54 -0
  42. package/lib/verify/issue-helpers.mjs +132 -0
  43. package/lib/verify/project-layout-verifier.mjs +259 -0
  44. package/lib/verify/project-verifier.mjs +267 -0
  45. package/lib/verify/readme-public-api.mjs +237 -0
  46. package/lib/verify/readme-verifier.mjs +216 -0
  47. package/lib/verify/render-json-report.mjs +3 -0
  48. package/lib/verify/render-text-report.mjs +34 -0
  49. package/lib/verify/source-analysis.mjs +126 -0
  50. package/lib/verify/source-rule-verifier.mjs +453 -0
  51. package/lib/verify/testing-verifier.mjs +113 -0
  52. package/lib/verify/tooling-verifier.mjs +82 -0
  53. package/lib/verify/typescript-style-verifier.mjs +407 -0
  54. package/package.json +55 -0
  55. package/profiles/default.profile.json +40 -0
  56. package/profiles/schema.json +96 -0
  57. package/prompts/init-contract.md +25 -0
  58. package/prompts/init-phase-2-implement.md +25 -0
  59. package/prompts/init-phase-3-verify.md +23 -0
  60. package/prompts/init.prompt.md +24 -0
  61. package/prompts/refactor-contract.md +26 -0
  62. package/prompts/refactor-phase-2-rebuild.md +25 -0
  63. package/prompts/refactor-phase-3-verify.md +24 -0
  64. package/prompts/refactor.prompt.md +26 -0
  65. package/resources/ai/AGENTS.md +18 -0
  66. package/resources/ai/adapters/codex.md +5 -0
  67. package/resources/ai/adapters/copilot.md +5 -0
  68. package/resources/ai/adapters/cursor.md +5 -0
  69. package/resources/ai/adapters/windsurf.md +5 -0
  70. package/resources/ai/contract.schema.json +68 -0
  71. package/resources/ai/rule-catalog.json +878 -0
  72. package/resources/ai/rule-catalog.schema.json +66 -0
  73. package/resources/ai/templates/adapters/codex.template.md +7 -0
  74. package/resources/ai/templates/adapters/copilot.template.md +7 -0
  75. package/resources/ai/templates/adapters/cursor.template.md +7 -0
  76. package/resources/ai/templates/adapters/windsurf.template.md +7 -0
  77. package/resources/ai/templates/agents.project.template.md +141 -0
  78. package/resources/ai/templates/examples/demo/src/billing/billing.service.ts +73 -0
  79. package/resources/ai/templates/examples/demo/src/config.ts +3 -0
  80. package/resources/ai/templates/examples/demo/src/invoice/invoice.errors.ts +51 -0
  81. package/resources/ai/templates/examples/demo/src/invoice/invoice.service.ts +96 -0
  82. package/resources/ai/templates/examples/demo/src/invoice/invoice.types.ts +9 -0
  83. package/resources/ai/templates/examples/rules/async-bad.ts +52 -0
  84. package/resources/ai/templates/examples/rules/async-good.ts +56 -0
  85. package/resources/ai/templates/examples/rules/class-first-bad.ts +36 -0
  86. package/resources/ai/templates/examples/rules/class-first-good.ts +74 -0
  87. package/resources/ai/templates/examples/rules/constructor-bad.ts +68 -0
  88. package/resources/ai/templates/examples/rules/constructor-good.ts +71 -0
  89. package/resources/ai/templates/examples/rules/control-flow-bad.ts +31 -0
  90. package/resources/ai/templates/examples/rules/control-flow-good.ts +54 -0
  91. package/resources/ai/templates/examples/rules/errors-bad.ts +42 -0
  92. package/resources/ai/templates/examples/rules/errors-good.ts +23 -0
  93. package/resources/ai/templates/examples/rules/functions-bad.ts +48 -0
  94. package/resources/ai/templates/examples/rules/functions-good.ts +58 -0
  95. package/resources/ai/templates/examples/rules/returns-bad.ts +38 -0
  96. package/resources/ai/templates/examples/rules/returns-good.ts +44 -0
  97. package/resources/ai/templates/examples/rules/testing-bad.ts +34 -0
  98. package/resources/ai/templates/examples/rules/testing-good.ts +54 -0
  99. package/resources/ai/templates/rules/architecture.md +41 -0
  100. package/resources/ai/templates/rules/async.md +13 -0
  101. package/resources/ai/templates/rules/class-first.md +45 -0
  102. package/resources/ai/templates/rules/control-flow.md +13 -0
  103. package/resources/ai/templates/rules/errors.md +18 -0
  104. package/resources/ai/templates/rules/functions.md +29 -0
  105. package/resources/ai/templates/rules/naming.md +13 -0
  106. package/resources/ai/templates/rules/readme.md +36 -0
  107. package/resources/ai/templates/rules/returns.md +13 -0
  108. package/resources/ai/templates/rules/testing.md +18 -0
  109. package/resources/ai/templates/rules.project.template.md +66 -0
  110. package/resources/ai/templates/skills/change-synchronization/SKILL.md +42 -0
  111. package/resources/ai/templates/skills/feature-shaping/SKILL.md +45 -0
  112. package/resources/ai/templates/skills/http-api-conventions/SKILL.md +171 -0
  113. package/resources/ai/templates/skills/init-workflow/SKILL.md +52 -0
  114. package/resources/ai/templates/skills/readme-authoring/SKILL.md +51 -0
  115. package/resources/ai/templates/skills/refactor-workflow/SKILL.md +50 -0
  116. package/resources/ai/templates/skills/simplicity-audit/SKILL.md +41 -0
  117. package/resources/ai/templates/skills/test-scope-selection/SKILL.md +50 -0
  118. package/resources/ai/templates/skills.index.template.md +25 -0
  119. package/standards/architecture.md +72 -0
  120. package/standards/changelog-policy.md +12 -0
  121. package/standards/manifest.json +36 -0
  122. package/standards/readme.md +56 -0
  123. package/standards/schema.json +124 -0
  124. package/standards/style.md +106 -0
  125. package/standards/testing.md +20 -0
  126. package/standards/tooling.md +38 -0
  127. package/templates/node-lib/.biomeignore +10 -0
  128. package/templates/node-lib/.vscode/extensions.json +1 -0
  129. package/templates/node-lib/.vscode/settings.json +9 -0
  130. package/templates/node-lib/README.md +172 -0
  131. package/templates/node-lib/biome.json +37 -0
  132. package/templates/node-lib/gitignore +6 -0
  133. package/templates/node-lib/package.json +32 -0
  134. package/templates/node-lib/scripts/release-publish.mjs +106 -0
  135. package/templates/node-lib/scripts/run-tests.mjs +65 -0
  136. package/templates/node-lib/src/config.ts +3 -0
  137. package/templates/node-lib/src/index.ts +2 -0
  138. package/templates/node-lib/src/logger.ts +7 -0
  139. package/templates/node-lib/src/package-info/package-info.service.ts +47 -0
  140. package/templates/node-lib/test/package-info.test.ts +10 -0
  141. package/templates/node-lib/tsconfig.build.json +1 -0
  142. package/templates/node-lib/tsconfig.json +5 -0
  143. package/templates/node-service/.biomeignore +10 -0
  144. package/templates/node-service/.vscode/extensions.json +1 -0
  145. package/templates/node-service/.vscode/settings.json +9 -0
  146. package/templates/node-service/README.md +244 -0
  147. package/templates/node-service/biome.json +37 -0
  148. package/templates/node-service/ecosystem.config.cjs +3 -0
  149. package/templates/node-service/gitignore +6 -0
  150. package/templates/node-service/package.json +42 -0
  151. package/templates/node-service/scripts/release-publish.mjs +106 -0
  152. package/templates/node-service/scripts/run-tests.mjs +65 -0
  153. package/templates/node-service/src/app/service-runtime.service.ts +57 -0
  154. package/templates/node-service/src/app-info/app-info.service.ts +47 -0
  155. package/templates/node-service/src/config.ts +11 -0
  156. package/templates/node-service/src/http/http-server.service.ts +66 -0
  157. package/templates/node-service/src/index.ts +2 -0
  158. package/templates/node-service/src/logger.ts +7 -0
  159. package/templates/node-service/src/main.ts +5 -0
  160. package/templates/node-service/test/service-runtime.test.ts +13 -0
  161. package/templates/node-service/tsconfig.build.json +1 -0
  162. package/templates/node-service/tsconfig.json +5 -0
  163. package/tsconfig/base.json +16 -0
  164. package/tsconfig/node-lib.json +5 -0
  165. package/tsconfig/node-service.json +1 -0
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { parseInitArgs, parseProfileArgs, parseRefactorArgs, parseVerifyArgs, printUsage } from "../lib/cli/parse-args.mjs";
4
+ import { runInit } from "../lib/cli/run-init.mjs";
5
+ import { runProfile } from "../lib/cli/run-profile.mjs";
6
+ import { runRefactor } from "../lib/cli/run-refactor.mjs";
7
+ import { runVerify } from "../lib/cli/run-verify.mjs";
8
+
9
+ async function main() {
10
+ const argv = process.argv.slice(2);
11
+
12
+ if (argv.length === 0 || argv.includes("-h") || argv.includes("--help")) {
13
+ printUsage();
14
+ process.exit(0);
15
+ }
16
+
17
+ const [command, ...rest] = argv;
18
+
19
+ try {
20
+ if (command === "init") {
21
+ await runInit(parseInitArgs(rest));
22
+ return;
23
+ }
24
+
25
+ if (command === "refactor") {
26
+ await runRefactor(parseRefactorArgs(rest));
27
+ return;
28
+ }
29
+
30
+ if (command === "profile") {
31
+ await runProfile(parseProfileArgs(rest));
32
+ return;
33
+ }
34
+
35
+ if (command === "verify") {
36
+ await runVerify(parseVerifyArgs(rest));
37
+ return;
38
+ }
39
+
40
+ throw new Error(`Unknown command: ${command}`);
41
+ } catch (error) {
42
+ console.error(error instanceof Error ? error.message : String(error));
43
+ process.exit(1);
44
+ }
45
+ }
46
+
47
+ main();
package/biome.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "files": {
3
+ "ignoreUnknown": true,
4
+ "ignore": [".code-standards", "dist"]
5
+ },
6
+ "formatter": {
7
+ "enabled": true,
8
+ "indentStyle": "space",
9
+ "indentWidth": 2,
10
+ "lineWidth": 160
11
+ },
12
+ "linter": {
13
+ "enabled": true,
14
+ "rules": {
15
+ "recommended": true,
16
+ "correctness": {
17
+ "noUnusedFunctionParameters": "error",
18
+ "noUnusedImports": "error",
19
+ "noUnusedPrivateClassMembers": "error",
20
+ "noUnusedVariables": "error"
21
+ }
22
+ }
23
+ },
24
+ "javascript": {
25
+ "formatter": {
26
+ "quoteStyle": "double",
27
+ "semicolons": "always",
28
+ "arrowParentheses": "always",
29
+ "bracketSpacing": true
30
+ }
31
+ },
32
+ "json": {
33
+ "formatter": {
34
+ "enabled": true
35
+ }
36
+ }
37
+ }
package/index.mjs ADDED
@@ -0,0 +1,11 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ const rootDir = path.dirname(fileURLToPath(import.meta.url));
4
+
5
+ export const tsconfigPaths = {
6
+ base: path.join(rootDir, "tsconfig", "base.json"),
7
+ nodeLib: path.join(rootDir, "tsconfig", "node-lib.json"),
8
+ nodeService: path.join(rootDir, "tsconfig", "node-service.json"),
9
+ };
10
+
11
+ export const biomeConfigPath = path.join(rootDir, "biome.json");
@@ -0,0 +1,416 @@
1
+ import { TEMPLATE_NAMES } from "../constants.mjs";
2
+
3
+ export function printUsage() {
4
+ console.log(`Usage:
5
+ code-standards <command> [options]
6
+
7
+ Commands:
8
+ init Initialize a project in the current directory
9
+ refactor Snapshot the current repo and rebuild it from the active scaffold
10
+ profile Create or update the AI style profile
11
+ verify Validate deterministic standards in a project
12
+
13
+ Init options:
14
+ --template <node-lib|node-service>
15
+ --package-name <name>
16
+ --repository-url <url>
17
+ --yes
18
+ --no-install
19
+ --force
20
+ --with-ai-adapters
21
+ --no-ai-adapters
22
+ --profile <path>
23
+
24
+ Refactor options:
25
+ --template <node-lib|node-service>
26
+ --package-name <name>
27
+ --repository-url <url>
28
+ --profile <path>
29
+ --with-ai-adapters
30
+ --no-ai-adapters
31
+ --install
32
+ --yes
33
+ --force
34
+
35
+ Verify options:
36
+ --report <text|json>
37
+ --only <rule-id[,rule-id...]>
38
+ --files <path[,path...]>
39
+ --explain <rule-id>
40
+ --strict
41
+ --changed-against <ref>
42
+ --staged
43
+ --all-files
44
+
45
+ Profile options:
46
+ --profile <path>
47
+ --non-interactive
48
+ --force-profile
49
+
50
+ Global:
51
+ -h, --help`);
52
+ }
53
+
54
+ export function parseInitArgs(argv) {
55
+ const options = {
56
+ template: undefined,
57
+ packageName: undefined,
58
+ repositoryUrl: undefined,
59
+ yes: false,
60
+ install: true,
61
+ force: false,
62
+ withAiAdapters: true,
63
+ profilePath: undefined,
64
+ help: false,
65
+ };
66
+
67
+ for (let i = 0; i < argv.length; i += 1) {
68
+ const token = argv[i];
69
+
70
+ if (!token.startsWith("-")) {
71
+ throw new Error(`Positional project names are not supported: ${token}. Run init from your target directory.`);
72
+ }
73
+
74
+ if (token === "--template") {
75
+ const value = argv[i + 1];
76
+ if (!value || value.startsWith("-")) {
77
+ throw new Error("Missing value for --template");
78
+ }
79
+ if (!TEMPLATE_NAMES.includes(value)) {
80
+ throw new Error(`Invalid template: ${value}`);
81
+ }
82
+ options.template = value;
83
+ i += 1;
84
+ continue;
85
+ }
86
+
87
+ if (token === "--target") {
88
+ throw new Error("--target is not supported. Run init from your target directory.");
89
+ }
90
+
91
+ if (token === "--profile") {
92
+ const value = argv[i + 1];
93
+ if (!value || value.startsWith("-")) {
94
+ throw new Error("Missing value for --profile");
95
+ }
96
+ options.profilePath = value;
97
+ i += 1;
98
+ continue;
99
+ }
100
+
101
+ if (token === "--package-name") {
102
+ const value = argv[i + 1];
103
+ if (!value || value.startsWith("-")) {
104
+ throw new Error("Missing value for --package-name");
105
+ }
106
+ options.packageName = value;
107
+ i += 1;
108
+ continue;
109
+ }
110
+
111
+ if (token === "--repository-url") {
112
+ const value = argv[i + 1];
113
+ if (!value || value.startsWith("-")) {
114
+ throw new Error("Missing value for --repository-url");
115
+ }
116
+ options.repositoryUrl = value;
117
+ i += 1;
118
+ continue;
119
+ }
120
+
121
+ if (token === "--yes") {
122
+ options.yes = true;
123
+ continue;
124
+ }
125
+
126
+ if (token === "--no-install") {
127
+ options.install = false;
128
+ continue;
129
+ }
130
+
131
+ if (token === "--force") {
132
+ options.force = true;
133
+ continue;
134
+ }
135
+
136
+ if (token === "--with-ai-adapters") {
137
+ options.withAiAdapters = true;
138
+ continue;
139
+ }
140
+
141
+ if (token === "--no-ai-adapters") {
142
+ options.withAiAdapters = false;
143
+ continue;
144
+ }
145
+
146
+ if (token === "-h" || token === "--help") {
147
+ options.help = true;
148
+ continue;
149
+ }
150
+
151
+ throw new Error(`Unknown option: ${token}`);
152
+ }
153
+
154
+ return options;
155
+ }
156
+
157
+ export function parseRefactorArgs(argv) {
158
+ const options = {
159
+ template: undefined,
160
+ packageName: undefined,
161
+ repositoryUrl: undefined,
162
+ profilePath: undefined,
163
+ withAiAdapters: true,
164
+ install: false,
165
+ yes: false,
166
+ force: false,
167
+ help: false,
168
+ };
169
+
170
+ for (let i = 0; i < argv.length; i += 1) {
171
+ const token = argv[i];
172
+
173
+ if (!token.startsWith("-")) {
174
+ throw new Error(`Positional arguments are not supported for refactor: ${token}.`);
175
+ }
176
+
177
+ if (token === "--template") {
178
+ const value = argv[i + 1];
179
+ if (!value || value.startsWith("-")) {
180
+ throw new Error("Missing value for --template");
181
+ }
182
+ if (!TEMPLATE_NAMES.includes(value)) {
183
+ throw new Error(`Invalid template: ${value}`);
184
+ }
185
+ options.template = value;
186
+ i += 1;
187
+ continue;
188
+ }
189
+
190
+ if (token === "--package-name") {
191
+ const value = argv[i + 1];
192
+ if (!value || value.startsWith("-")) {
193
+ throw new Error("Missing value for --package-name");
194
+ }
195
+ options.packageName = value;
196
+ i += 1;
197
+ continue;
198
+ }
199
+
200
+ if (token === "--repository-url") {
201
+ const value = argv[i + 1];
202
+ if (!value || value.startsWith("-")) {
203
+ throw new Error("Missing value for --repository-url");
204
+ }
205
+ options.repositoryUrl = value;
206
+ i += 1;
207
+ continue;
208
+ }
209
+
210
+ if (token === "--profile") {
211
+ const value = argv[i + 1];
212
+ if (!value || value.startsWith("-")) {
213
+ throw new Error("Missing value for --profile");
214
+ }
215
+ options.profilePath = value;
216
+ i += 1;
217
+ continue;
218
+ }
219
+
220
+ if (token === "--with-ai-adapters") {
221
+ options.withAiAdapters = true;
222
+ continue;
223
+ }
224
+
225
+ if (token === "--no-ai-adapters") {
226
+ options.withAiAdapters = false;
227
+ continue;
228
+ }
229
+
230
+ if (token === "--install") {
231
+ options.install = true;
232
+ continue;
233
+ }
234
+
235
+ if (token === "--yes") {
236
+ options.yes = true;
237
+ continue;
238
+ }
239
+
240
+ if (token === "--force") {
241
+ options.force = true;
242
+ continue;
243
+ }
244
+
245
+ if (token === "-h" || token === "--help") {
246
+ options.help = true;
247
+ continue;
248
+ }
249
+
250
+ throw new Error(`Unknown option: ${token}`);
251
+ }
252
+
253
+ return options;
254
+ }
255
+
256
+ export function parseProfileArgs(argv) {
257
+ const options = { profilePath: undefined, nonInteractive: false, forceProfile: false, help: false };
258
+
259
+ for (let i = 0; i < argv.length; i += 1) {
260
+ const token = argv[i];
261
+
262
+ if (token === "--profile") {
263
+ const value = argv[i + 1];
264
+ if (!value || value.startsWith("-")) {
265
+ throw new Error("Missing value for --profile");
266
+ }
267
+ options.profilePath = value;
268
+ i += 1;
269
+ continue;
270
+ }
271
+
272
+ if (token === "--non-interactive") {
273
+ options.nonInteractive = true;
274
+ continue;
275
+ }
276
+
277
+ if (token === "--force-profile") {
278
+ options.forceProfile = true;
279
+ continue;
280
+ }
281
+
282
+ if (token === "-h" || token === "--help") {
283
+ options.help = true;
284
+ continue;
285
+ }
286
+
287
+ throw new Error(`Unknown option: ${token}`);
288
+ }
289
+
290
+ return options;
291
+ }
292
+
293
+ export function parseVerifyArgs(argv) {
294
+ const options = {
295
+ report: "text",
296
+ onlyRuleIds: undefined,
297
+ files: undefined,
298
+ explainRuleId: undefined,
299
+ strict: false,
300
+ changedAgainst: undefined,
301
+ staged: false,
302
+ allFiles: false,
303
+ help: false,
304
+ };
305
+
306
+ for (let i = 0; i < argv.length; i += 1) {
307
+ const token = argv[i];
308
+
309
+ if (token === "--report") {
310
+ const value = argv[i + 1];
311
+ if (!value || value.startsWith("-")) {
312
+ throw new Error("Missing value for --report");
313
+ }
314
+ if (value !== "text" && value !== "json") {
315
+ throw new Error(`Invalid value for --report: ${value}`);
316
+ }
317
+ options.report = value;
318
+ i += 1;
319
+ continue;
320
+ }
321
+
322
+ if (token === "--only") {
323
+ const value = argv[i + 1];
324
+ if (!value || value.startsWith("-")) {
325
+ throw new Error("Missing value for --only");
326
+ }
327
+ const onlyRuleIds = [
328
+ ...new Set(
329
+ value
330
+ .split(",")
331
+ .map((ruleId) => ruleId.trim())
332
+ .filter((ruleId) => ruleId.length > 0),
333
+ ),
334
+ ];
335
+ if (onlyRuleIds.length === 0) {
336
+ throw new Error("--only requires at least one rule id");
337
+ }
338
+ options.onlyRuleIds = onlyRuleIds;
339
+ i += 1;
340
+ continue;
341
+ }
342
+
343
+ if (token === "--files") {
344
+ const value = argv[i + 1];
345
+ if (!value || value.startsWith("-")) {
346
+ throw new Error("Missing value for --files");
347
+ }
348
+ const files = [
349
+ ...new Set(
350
+ value
351
+ .split(",")
352
+ .map((filePath) => filePath.trim())
353
+ .filter((filePath) => filePath.length > 0),
354
+ ),
355
+ ];
356
+ if (files.length === 0) {
357
+ throw new Error("--files requires at least one path");
358
+ }
359
+ options.files = files;
360
+ i += 1;
361
+ continue;
362
+ }
363
+
364
+ if (token === "--explain") {
365
+ const value = argv[i + 1];
366
+ if (!value || value.startsWith("-")) {
367
+ throw new Error("Missing value for --explain");
368
+ }
369
+ options.explainRuleId = value;
370
+ i += 1;
371
+ continue;
372
+ }
373
+
374
+ if (token === "--changed-against") {
375
+ const value = argv[i + 1];
376
+ if (!value || value.startsWith("-")) {
377
+ throw new Error("Missing value for --changed-against");
378
+ }
379
+ options.changedAgainst = value;
380
+ i += 1;
381
+ continue;
382
+ }
383
+
384
+ if (token === "--strict") {
385
+ options.strict = true;
386
+ continue;
387
+ }
388
+
389
+ if (token === "--staged") {
390
+ options.staged = true;
391
+ continue;
392
+ }
393
+
394
+ if (token === "--all-files") {
395
+ options.allFiles = true;
396
+ continue;
397
+ }
398
+
399
+ if (token === "-h" || token === "--help") {
400
+ options.help = true;
401
+ continue;
402
+ }
403
+
404
+ throw new Error(`Unknown option: ${token}`);
405
+ }
406
+
407
+ if (options.explainRuleId && (options.onlyRuleIds || options.files || options.report !== "text")) {
408
+ throw new Error("--explain cannot be combined with --only, --files, or --report");
409
+ }
410
+
411
+ if (options.staged && options.allFiles) {
412
+ throw new Error("--staged cannot be combined with --all-files");
413
+ }
414
+
415
+ return options;
416
+ }
@@ -0,0 +1,43 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ function printIndentedLines(lines) {
5
+ for (const line of lines) {
6
+ console.log(` ${line}`);
7
+ }
8
+ }
9
+
10
+ export function printInitGuidance(targetPath) {
11
+ console.log(`Project created at ${targetPath}`);
12
+ console.log("Guided next steps:");
13
+ printIndentedLines([
14
+ "1. Open PROMPT.md and complete the User Request section.",
15
+ "2. Paste only PROMPT.md into the LLM.",
16
+ "3. The LLM must read the phase prompts itself, execute the workflow end to end, run npm run check, and update SCAFFOLD-FEEDBACK.md.",
17
+ "4. Remember that Biome ignores managed contract files by default, and single-return stays strict outside src/http/.",
18
+ ]);
19
+ }
20
+
21
+ export async function printRefactorGuidance(targetPath) {
22
+ const promptPath = path.join(targetPath, "PROMPT.md");
23
+
24
+ console.log(`Project refactored at ${targetPath}`);
25
+ console.log("Legacy code was moved into .code-standards/refactor-source/latest/ for reference.");
26
+ console.log("The snapshot is legacy reference only. Do not restore managed files or toolchain files from it.");
27
+ console.log("The LLM MUST analyze the legacy code and then rebuild on the fresh scaffold. It MUST NOT copy the legacy structure as-is.");
28
+ console.log("No lint, format, or check pass was run against the intermediate refactor state.");
29
+ console.log("Final npm run check is deferred until after the LLM rewrites src/ and test/.");
30
+ console.log("The user should only fill PROMPT.md; the LLM must load and follow the phase prompts by itself.");
31
+
32
+ try {
33
+ const prompt = await readFile(promptPath, "utf8");
34
+
35
+ console.log("Start from this single LLM entry file:");
36
+ console.log("");
37
+ console.log("----- BEGIN PROMPT CHECKLIST -----");
38
+ console.log(prompt.trimEnd());
39
+ console.log("----- END PROMPT CHECKLIST -----");
40
+ } catch {
41
+ console.log(`Refactor prompt written to ${promptPath}`);
42
+ }
43
+ }
@@ -0,0 +1,123 @@
1
+ import path from "node:path";
2
+
3
+ import { TEMPLATE_NAMES } from "../constants.mjs";
4
+ import { loadProfileSchema, readPackageVersion, resolvePackageRoot } from "../paths.mjs";
5
+ import { resolveProfileForInit } from "../profile.mjs";
6
+ import { generateAiInstructions } from "../project/ai-instructions.mjs";
7
+ import { ensureBiomeIgnore } from "../project/biome-ignore.mjs";
8
+ import { applyPackageCoordinates, readProjectPackageJson, updateCodeStandardsMetadata, writeProjectPackageJson } from "../project/package-metadata.mjs";
9
+ import { validateInitResources } from "../project/template-resolution.mjs";
10
+ import { copyTemplateDirectory, ensureTargetReady, formatFilesWithBiome, runCommand } from "../utils/fs.mjs";
11
+ import { promptYesNo, withReadline } from "../utils/prompts.mjs";
12
+ import { defaultPackageNameForProject, defaultRepositoryUrlForPackage, normalizePackageName, normalizeRepositoryUrl } from "../utils/text.mjs";
13
+ import { printInitGuidance } from "./post-run-guidance.mjs";
14
+
15
+ async function promptForMissing(options, targetPath) {
16
+ return withReadline(async (rl) => {
17
+ const resolved = { ...options };
18
+ const inferredProjectName = path.basename(targetPath);
19
+ const projectName = inferredProjectName && inferredProjectName !== path.sep ? inferredProjectName : "my-project";
20
+ const defaultPackageName = defaultPackageNameForProject(projectName);
21
+
22
+ if (!resolved.template) {
23
+ const templateAnswer = await rl.question("Choose template (node-lib/node-service) [node-lib]: ");
24
+ const normalized = templateAnswer.trim() || "node-lib";
25
+
26
+ if (!TEMPLATE_NAMES.includes(normalized)) {
27
+ throw new Error(`Invalid template: ${normalized}`);
28
+ }
29
+
30
+ resolved.template = normalized;
31
+ }
32
+
33
+ if (!resolved.packageName) {
34
+ const packageAnswer = await rl.question(`npm package name [${defaultPackageName}]: `);
35
+ resolved.packageName = packageAnswer.trim() || defaultPackageName;
36
+ }
37
+
38
+ if (!resolved.repositoryUrl) {
39
+ const defaultRepositoryUrl = defaultRepositoryUrlForPackage(resolved.packageName);
40
+ const repositoryAnswer = await rl.question(`GitHub repository URL [${defaultRepositoryUrl}]: `);
41
+ resolved.repositoryUrl = repositoryAnswer.trim() || defaultRepositoryUrl;
42
+ }
43
+
44
+ if (options.install) {
45
+ const shouldInstall = await promptYesNo(rl, "Install dependencies now?", true);
46
+ resolved.install = shouldInstall;
47
+ }
48
+
49
+ return resolved;
50
+ });
51
+ }
52
+
53
+ export async function runInit(rawOptions) {
54
+ if (rawOptions.help) {
55
+ const { printUsage } = await import("./parse-args.mjs");
56
+ printUsage();
57
+ return;
58
+ }
59
+
60
+ let options = { ...rawOptions };
61
+ const targetPath = path.resolve(process.cwd());
62
+ const inferredProjectName = path.basename(targetPath);
63
+ const projectName = inferredProjectName && inferredProjectName !== path.sep ? inferredProjectName : "my-project";
64
+ const defaultPackageName = defaultPackageNameForProject(projectName);
65
+
66
+ if (options.yes || !process.stdin.isTTY) {
67
+ options.template ??= "node-lib";
68
+ options.packageName ??= defaultPackageName;
69
+ options.repositoryUrl ??= defaultRepositoryUrlForPackage(options.packageName);
70
+ } else {
71
+ options = await promptForMissing(options, targetPath);
72
+ }
73
+
74
+ const template = options.template ?? "node-lib";
75
+
76
+ if (!TEMPLATE_NAMES.includes(template)) {
77
+ throw new Error(`Invalid template: ${template}`);
78
+ }
79
+
80
+ const packageRoot = resolvePackageRoot();
81
+ const packageVersion = await readPackageVersion(packageRoot);
82
+ const schema = await loadProfileSchema(packageRoot);
83
+ const profileResolution = await resolveProfileForInit(packageRoot, targetPath, options, schema);
84
+ const packageName = normalizePackageName(options.packageName, projectName);
85
+ const repositoryUrl = normalizeRepositoryUrl(options.repositoryUrl, packageName);
86
+
87
+ await ensureTargetReady(targetPath, options.force);
88
+
89
+ const { templateDir } = await validateInitResources(packageRoot, template);
90
+ const tokens = { projectName, packageName, packageVersion, year: String(new Date().getFullYear()) };
91
+
92
+ await copyTemplateDirectory(templateDir, targetPath, tokens);
93
+
94
+ const contract = await generateAiInstructions({
95
+ packageRoot,
96
+ packageVersion,
97
+ targetDir: targetPath,
98
+ tokens,
99
+ profile: profileResolution.profile,
100
+ template,
101
+ withAiAdapters: options.withAiAdapters,
102
+ workflow: "init",
103
+ });
104
+ await ensureBiomeIgnore(targetPath, contract.managedFiles);
105
+
106
+ if (options.install) {
107
+ console.log("Installing dependencies...");
108
+ await runCommand("npm", ["install"], targetPath);
109
+ }
110
+
111
+ const { packageJsonPath, packageJson } = await readProjectPackageJson(targetPath);
112
+ const packageWithCoordinates = applyPackageCoordinates(packageJson, packageName, repositoryUrl);
113
+ const packageWithMetadata = updateCodeStandardsMetadata(packageWithCoordinates, {
114
+ template,
115
+ profilePath: profileResolution.profilePathForMetadata,
116
+ withAiAdapters: options.withAiAdapters,
117
+ lastRefactorWith: packageVersion,
118
+ });
119
+ await writeProjectPackageJson(packageJsonPath, packageWithMetadata);
120
+ await formatFilesWithBiome(packageRoot, targetPath, ["package.json", "ai/contract.json"]);
121
+
122
+ printInitGuidance(targetPath);
123
+ }