@sculptor/cli 0.2.2 → 0.3.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/README.md +142 -369
- package/bin/sc.js +0 -0
- package/dist/agents.d.ts +2 -0
- package/dist/agents.js +100 -0
- package/dist/agents.js.map +1 -0
- package/dist/cli.js +317 -15
- package/dist/cli.js.map +1 -1
- package/dist/diagnostics.d.ts +16 -0
- package/dist/diagnostics.js +275 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/package-commands.d.ts +24 -0
- package/dist/package-commands.js +299 -0
- package/dist/package-commands.js.map +1 -0
- package/dist/package-manager.d.ts +3 -0
- package/dist/package-manager.js +20 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/package-registry.d.ts +37 -0
- package/dist/package-registry.js +477 -0
- package/dist/package-registry.js.map +1 -0
- package/dist/plugins.d.ts +3 -0
- package/dist/plugins.js +29 -1
- package/dist/plugins.js.map +1 -1
- package/dist/runtime-dependencies.d.ts +8 -0
- package/dist/runtime-dependencies.js +2 -0
- package/dist/runtime-dependencies.js.map +1 -0
- package/dist/scaffold.d.ts +19 -1
- package/dist/scaffold.js +244 -1
- package/dist/scaffold.js.map +1 -1
- package/package.json +5 -4
package/dist/agents.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { loadConfig } from "@sculptor/config";
|
|
4
|
+
import { loadPackageRegistry } from "./package-registry.js";
|
|
5
|
+
const cliVersion = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
6
|
+
const safeLoadConfig = (rootDir) => {
|
|
7
|
+
try {
|
|
8
|
+
const config = loadConfig(rootDir);
|
|
9
|
+
return {
|
|
10
|
+
srcRoot: String(config.framework.project?.srcRoot ?? "src"),
|
|
11
|
+
routingStyle: String(config.framework.routing?.style ?? "decorator")
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return {
|
|
16
|
+
srcRoot: "src",
|
|
17
|
+
routingStyle: "decorator"
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const safeLoadPackages = (rootDir) => {
|
|
22
|
+
try {
|
|
23
|
+
return Object.values(loadPackageRegistry(rootDir).packages)
|
|
24
|
+
.map((record) => record.name)
|
|
25
|
+
.sort((left, right) => left.localeCompare(right));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const buildPackageList = (packages) => packages.length > 0 ? packages.map((name) => `- \`${name}\``).join("\n") : "- _none detected_";
|
|
32
|
+
export const buildAgentsMarkdown = (rootDir) => {
|
|
33
|
+
const { srcRoot, routingStyle } = safeLoadConfig(rootDir);
|
|
34
|
+
const packages = safeLoadPackages(rootDir);
|
|
35
|
+
const version = cliVersion.version ?? "0.0.0";
|
|
36
|
+
return `# AGENTS.md
|
|
37
|
+
|
|
38
|
+
Sculptor CLI \`v${version}\`
|
|
39
|
+
|
|
40
|
+
## Working Rules
|
|
41
|
+
|
|
42
|
+
- Exact package names only. Do not singularize or pluralize package identities.
|
|
43
|
+
- Treat package index files as the package contract.
|
|
44
|
+
- Update generated sections only when markers exist.
|
|
45
|
+
- Keep runtime composition explicit and scanner-friendly.
|
|
46
|
+
- Prefer warnings and diagnostics over silent mutation.
|
|
47
|
+
|
|
48
|
+
## Package Architecture
|
|
49
|
+
|
|
50
|
+
- Project src root: \`${srcRoot}\`
|
|
51
|
+
- Routing style: \`${routingStyle}\`
|
|
52
|
+
- Package registry artifact: \`sculptor.packages.json\`
|
|
53
|
+
- Package index source of truth: \`index.ts\` inside each package
|
|
54
|
+
- Generated markers: \`[sculptor:imports:start]\`, \`[sculptor:exports:start]\`, \`[sculptor:package:start]\`
|
|
55
|
+
|
|
56
|
+
## DI Conventions
|
|
57
|
+
|
|
58
|
+
- Use \`@Service()\`, \`@Repository()\`, \`@Middleware()\`, and \`@AutoInject()\`.
|
|
59
|
+
- Injection is explicit only.
|
|
60
|
+
- Constructor injection and property injection are both supported.
|
|
61
|
+
- Avoid hidden autowiring or token guessing.
|
|
62
|
+
|
|
63
|
+
## Registry Conventions
|
|
64
|
+
|
|
65
|
+
- \`sc sync\` validates registry state and keeps \`sculptor.packages.json\` current.
|
|
66
|
+
- \`sc ls\` and \`sc pkg\` should reflect exact stored package names.
|
|
67
|
+
- \`sc doctor\` is the calm diagnostics entrypoint.
|
|
68
|
+
- \`sc update\` only updates \`@sculptor/cli\`.
|
|
69
|
+
|
|
70
|
+
## Generator Conventions
|
|
71
|
+
|
|
72
|
+
- Generators must preserve manual code outside generated markers.
|
|
73
|
+
- Package generation should use exact names and deterministic paths.
|
|
74
|
+
- Use the configured src root unless an explicit \`in\` path is supplied.
|
|
75
|
+
|
|
76
|
+
## Runtime Conventions
|
|
77
|
+
|
|
78
|
+
- Keep global registry composition thin.
|
|
79
|
+
- Flatten package composition internally.
|
|
80
|
+
- Preserve compatibility with legacy flat registries during migration.
|
|
81
|
+
|
|
82
|
+
## CLI Conventions
|
|
83
|
+
|
|
84
|
+
- \`sc agents\` writes this file.
|
|
85
|
+
- \`sc agents refresh\` regenerates this file.
|
|
86
|
+
- \`sc doctor\` reports diagnostics without mutating the project.
|
|
87
|
+
- \`sc update\` only manages the global CLI binary.
|
|
88
|
+
|
|
89
|
+
## Detected Packages
|
|
90
|
+
|
|
91
|
+
${buildPackageList(packages)}
|
|
92
|
+
`;
|
|
93
|
+
};
|
|
94
|
+
export const writeAgentsMarkdown = (rootDir) => {
|
|
95
|
+
const filePath = path.join(rootDir, "AGENTS.md");
|
|
96
|
+
const content = buildAgentsMarkdown(rootDir);
|
|
97
|
+
fs.writeFileSync(filePath, `${content}\n`, "utf8");
|
|
98
|
+
return filePath;
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAC3B,EAAE,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAC7C,CAAC;AAE1B,MAAM,cAAc,GAAG,CAAC,OAAe,EAA6C,EAAE;IACpF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACnC,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,IAAI,KAAK,CAAC;YAC3D,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,IAAI,WAAW,CAAC;SACrE,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,OAAO,EAAE,KAAK;YACd,YAAY,EAAE,WAAW;SAC1B,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAY,EAAE;IACrD,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;aACxD,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;aAC5B,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,QAAkB,EAAU,EAAE,CACtD,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC;AAEjG,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAU,EAAE;IAC7D,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,IAAI,OAAO,CAAC;IAE9C,OAAO;;kBAES,OAAO;;;;;;;;;;;;wBAYD,OAAO;qBACV,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwC/B,gBAAgB,CAAC,QAAQ,CAAC;CAC3B,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAU,EAAE;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7C,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,GAAG,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC;IACnD,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -7,11 +7,47 @@ import { stdin, stdout } from "node:process";
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { loadConfig } from "@sculptor/config";
|
|
9
9
|
import { getConfigValue, listConfigEntries, setConfigValue } from "./config-commands.js";
|
|
10
|
-
import {
|
|
10
|
+
import { getPackageFlagValue, handleLsCommand, ensureRootRegistryForPackages, handlePackageCommand, handleRegisterCommand, handleSyncCommand, stripPackageFlag, validatePackageRegistryState } from "./package-commands.js";
|
|
11
|
+
import { getOwningPackage, loadPackageRegistry, savePackageRegistry, syncPackageRegistry, updatePackageIndexForRecord, upsertFileIntoRegistry } from "./package-registry.js";
|
|
12
|
+
import { writeAgentsMarkdown } from "./agents.js";
|
|
13
|
+
import { detectPackageManager, globalInstallArgsFor } from "./package-manager.js";
|
|
14
|
+
import { createDoctorReport, hasDoctorErrors, printDoctorReport } from "./diagnostics.js";
|
|
15
|
+
import { controllerHelp, dtoHelp, generateHelp, generateResourceFiles, middlewareHelp, moduleHelp, parseGenerateMode, readModeFromFlags, routeHelp, scaffoldProject, syncTestHarness, repositoryHelp, typeHelp, writeGeneratedFiles } from "./scaffold.js";
|
|
11
16
|
import { loadPluginModule, resolvePluginManifest } from "./plugins.js";
|
|
12
17
|
const cliPackageVersion = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
13
18
|
const versionLabel = cliPackageVersion.version ?? "0.0.0";
|
|
14
|
-
const isCommand = (value) => [
|
|
19
|
+
const isCommand = (value) => [
|
|
20
|
+
"new",
|
|
21
|
+
"start",
|
|
22
|
+
"dev",
|
|
23
|
+
"build",
|
|
24
|
+
"lint",
|
|
25
|
+
"test",
|
|
26
|
+
"sync",
|
|
27
|
+
"install",
|
|
28
|
+
"i",
|
|
29
|
+
"update",
|
|
30
|
+
"generate",
|
|
31
|
+
"g",
|
|
32
|
+
"pkg",
|
|
33
|
+
"package",
|
|
34
|
+
"ls",
|
|
35
|
+
"list",
|
|
36
|
+
"reg",
|
|
37
|
+
"register",
|
|
38
|
+
"r",
|
|
39
|
+
"ureg",
|
|
40
|
+
"unreg",
|
|
41
|
+
"unregister",
|
|
42
|
+
"ur",
|
|
43
|
+
"rm",
|
|
44
|
+
"remove",
|
|
45
|
+
"help",
|
|
46
|
+
"config",
|
|
47
|
+
"add",
|
|
48
|
+
"agents",
|
|
49
|
+
"doctor"
|
|
50
|
+
].includes(value);
|
|
15
51
|
const isFlag = (value) => value.startsWith("-");
|
|
16
52
|
const isVersionFlag = (value) => ["-v", "--v", "--version", "version", "v"].includes(value);
|
|
17
53
|
const sculptorCliBanner = String.raw `
|
|
@@ -76,6 +112,26 @@ const requireAppRoot = (cwd, command) => {
|
|
|
76
112
|
}
|
|
77
113
|
return cwd;
|
|
78
114
|
};
|
|
115
|
+
const findAppRoot = (cwd) => {
|
|
116
|
+
let current = path.resolve(cwd);
|
|
117
|
+
for (;;) {
|
|
118
|
+
if (fs.existsSync(path.join(current, "sculptor.json"))) {
|
|
119
|
+
return current;
|
|
120
|
+
}
|
|
121
|
+
const parent = path.dirname(current);
|
|
122
|
+
if (parent === current) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
current = parent;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const requireOutsideAppRoot = (cwd, command) => {
|
|
129
|
+
const appRoot = findAppRoot(cwd);
|
|
130
|
+
if (appRoot) {
|
|
131
|
+
throw new Error(`${command} can only be run outside a Sculptor app root.`);
|
|
132
|
+
}
|
|
133
|
+
return cwd;
|
|
134
|
+
};
|
|
79
135
|
const extractOutputDir = (args) => {
|
|
80
136
|
const index = args.findIndex((arg) => arg === "in");
|
|
81
137
|
if (index < 0 || index === args.length - 1) {
|
|
@@ -97,6 +153,15 @@ const getFlagValue = (args, names) => {
|
|
|
97
153
|
return undefined;
|
|
98
154
|
};
|
|
99
155
|
const getFlagPresence = (args, names) => names.some((name) => args.includes(name) || args.some((arg) => arg.startsWith(`${name}=`)));
|
|
156
|
+
const resolveRegisteredPackagePath = (cwd, packageName) => {
|
|
157
|
+
const registry = loadPackageRegistry(cwd);
|
|
158
|
+
for (const record of Object.values(registry.packages)) {
|
|
159
|
+
if (record.name === packageName) {
|
|
160
|
+
return record.path;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
};
|
|
100
165
|
const getPrompt = (prompt) => {
|
|
101
166
|
if (prompt) {
|
|
102
167
|
return prompt;
|
|
@@ -182,12 +247,31 @@ sc <command> [options]
|
|
|
182
247
|
- \`sc build\`
|
|
183
248
|
- \`sc lint\`
|
|
184
249
|
- \`sc test\`
|
|
250
|
+
- \`sc sync\`
|
|
251
|
+
- \`sc ls\`
|
|
252
|
+
- \`sc list\`
|
|
253
|
+
- \`sc pkg\`
|
|
254
|
+
- \`sc package\`
|
|
255
|
+
- \`sc reg <file>\` / \`sc register <file>\` / \`sc r <file>\`
|
|
256
|
+
- \`sc ureg <file>\` / \`sc unreg <file>\` / \`sc unregister <file>\` / \`sc ur <file>\`
|
|
257
|
+
- \`sc rm <file>\` / \`sc remove <file>\`
|
|
258
|
+
- \`sc install deps\`
|
|
259
|
+
- \`sc update\`
|
|
260
|
+
- \`sc doctor\`
|
|
185
261
|
- \`sc generate\` or \`sc g\`
|
|
186
262
|
- \`sc config <get|set|list>\`
|
|
187
263
|
- \`sc add <plugin>\`
|
|
264
|
+
- \`sc agents\`
|
|
265
|
+
- \`sc agents refresh\`
|
|
188
266
|
- \`sc help\`
|
|
189
267
|
- \`sc help generate\`
|
|
190
268
|
- \`sc help controller\`
|
|
269
|
+
- \`sc help repository\`
|
|
270
|
+
- \`sc help dto\`
|
|
271
|
+
- \`sc help pkg\`
|
|
272
|
+
- \`sc help package\`
|
|
273
|
+
- \`sc help ls\`
|
|
274
|
+
- \`sc help list\`
|
|
191
275
|
- \`sc --version\` / \`sc -v\`
|
|
192
276
|
- \`sc version\`
|
|
193
277
|
|
|
@@ -197,8 +281,12 @@ sc <command> [options]
|
|
|
197
281
|
- \`service\` / \`s\`
|
|
198
282
|
- \`module\` / \`m\` or \`mo\`
|
|
199
283
|
- \`middleware\` / \`mw\`
|
|
284
|
+
- \`repository\` / \`repo\`
|
|
285
|
+
- \`dto\` / \`dto\`
|
|
200
286
|
- \`type\` / \`t\`
|
|
201
287
|
- \`route\` / \`r\`
|
|
288
|
+
- \`pkg\`
|
|
289
|
+
- \`package\`
|
|
202
290
|
|
|
203
291
|
## Binary Alias
|
|
204
292
|
|
|
@@ -226,6 +314,14 @@ const printHelp = (topic, log) => {
|
|
|
226
314
|
log(middlewareHelp);
|
|
227
315
|
return;
|
|
228
316
|
}
|
|
317
|
+
if (topic === "repository") {
|
|
318
|
+
log(repositoryHelp);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (topic === "dto") {
|
|
322
|
+
log(dtoHelp);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
229
325
|
if (topic === "type") {
|
|
230
326
|
log(typeHelp);
|
|
231
327
|
return;
|
|
@@ -238,10 +334,58 @@ const printHelp = (topic, log) => {
|
|
|
238
334
|
log(`# Config\n\nUse \`sc config get\`, \`sc config set\`, or \`sc config list\`.`);
|
|
239
335
|
return;
|
|
240
336
|
}
|
|
337
|
+
if (topic === "sync") {
|
|
338
|
+
log(`# Sync\n\nUse \`sc sync\` to refresh \`sculptor.packages.json\`.`);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (topic === "pkg") {
|
|
342
|
+
log(`# Package\n\nUse \`sc pkg <name>\`, \`sc package <name>\`, \`sc pkg ls\`, or \`sc pkg rm <name>\`.\nPackage names are exact and are not normalized.`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (topic === "package") {
|
|
346
|
+
log(`# Package\n\nUse \`sc pkg <name>\`, \`sc package <name>\`, \`sc pkg ls\`, or \`sc pkg rm <name>\`.\nPackage names are exact and are not normalized.`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (topic === "ls") {
|
|
350
|
+
log(`# List\n\nUse \`sc ls\` or \`sc list\`, and \`sc ls -t\` for tree view.`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (topic === "list") {
|
|
354
|
+
log(`# List\n\nUse \`sc ls\` or \`sc list\`, and \`sc ls -t\` for tree view.`);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (topic === "reg" || topic === "register" || topic === "r") {
|
|
358
|
+
log(`# Register\n\nUse \`sc reg <file>\`, \`sc register <file>\`, or \`sc r <file>\``);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (topic === "ureg" || topic === "unreg" || topic === "unregister" || topic === "ur") {
|
|
362
|
+
log(`# Unregister\n\nUse \`sc ureg <file>\`, \`sc unreg <file>\`, \`sc unregister <file>\`, or \`sc ur <file>\``);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (topic === "rm" || topic === "remove") {
|
|
366
|
+
log(`# Remove\n\nUse \`sc rm <file>\` or \`sc remove <file>\``);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (topic === "install") {
|
|
370
|
+
log(`# Install\n\nUsage: \`sc install deps\` or \`sc i deps\``);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (topic === "update") {
|
|
374
|
+
log(`# Update\n\nUsage: \`sc update\`\n\nUpdates the globally installed Sculptor CLI package.`);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (topic === "doctor") {
|
|
378
|
+
log(`# Doctor\n\nUsage: \`sc doctor\`\n\nRuns diagnostics for the current Sculptor project and package registry.`);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
241
381
|
if (topic === "add") {
|
|
242
382
|
log(`# Add\n\nUsage: \`sc add <plugin>\``);
|
|
243
383
|
return;
|
|
244
384
|
}
|
|
385
|
+
if (topic === "agents") {
|
|
386
|
+
log(`# Agents\n\nUsage: \`sc agents\` or \`sc agents refresh\`\n\nWrites \`AGENTS.md\` in the current directory.`);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
245
389
|
printMainHelp(log);
|
|
246
390
|
};
|
|
247
391
|
const printVersion = (log) => {
|
|
@@ -311,16 +455,82 @@ const runSpawn = (command, args, cwd, spawn, log, env) => {
|
|
|
311
455
|
process.exit(result.status);
|
|
312
456
|
}
|
|
313
457
|
};
|
|
458
|
+
const installScaffoldDependencies = (cwd, spawn, log) => {
|
|
459
|
+
runSpawn("npm", ["i"], cwd, spawn, log);
|
|
460
|
+
runSpawn("npm", ["i", "@sculptor/core@latest", "@sculptor/paws@latest"], cwd, spawn, log);
|
|
461
|
+
runSpawn("npm", ["i", "-D", "@sculptor/cli@latest", "@sculptor/config@latest", "@sculptor/router@latest"], cwd, spawn, log);
|
|
462
|
+
};
|
|
463
|
+
const updateGlobalCliPackage = (cwd, spawn, log) => {
|
|
464
|
+
const packageManager = detectPackageManager();
|
|
465
|
+
runSpawn(packageManager, globalInstallArgsFor(packageManager, ["@sculptor/cli@latest"]), cwd, spawn, log);
|
|
466
|
+
};
|
|
314
467
|
const handleNew = async (args, cwd, prompt, spawn, log) => {
|
|
315
468
|
printBanner(log, "SculptorTS CLI", `v${versionLabel}`);
|
|
316
469
|
const metadata = await resolveProjectMetadata(args, cwd, prompt);
|
|
317
470
|
const targetDir = path.join(cwd, metadata.appName);
|
|
318
|
-
scaffoldProject(metadata, targetDir
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
471
|
+
const scaffolded = await scaffoldProject(metadata, targetDir, {
|
|
472
|
+
cwd,
|
|
473
|
+
prompt,
|
|
474
|
+
spawn,
|
|
475
|
+
log,
|
|
476
|
+
error: () => undefined
|
|
477
|
+
});
|
|
478
|
+
if (!scaffolded) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
installScaffoldDependencies(targetDir, spawn, log);
|
|
322
482
|
log(`Created SculptorTS project at ${targetDir}`);
|
|
323
483
|
};
|
|
484
|
+
const handleInstall = (args, cwd, spawn, log, error) => {
|
|
485
|
+
const [subcommand] = args;
|
|
486
|
+
if (!subcommand || subcommand === "help") {
|
|
487
|
+
log(`Usage: sc install deps`);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (subcommand !== "deps") {
|
|
491
|
+
error(`Unknown install subcommand "${subcommand}".`);
|
|
492
|
+
process.exit(1);
|
|
493
|
+
}
|
|
494
|
+
const appRoot = findAppRoot(cwd);
|
|
495
|
+
if (!appRoot) {
|
|
496
|
+
throw new Error("sc install deps can only be run from a Sculptor app root.");
|
|
497
|
+
}
|
|
498
|
+
installScaffoldDependencies(appRoot, spawn, log);
|
|
499
|
+
log(`Installed Sculptor dependencies at ${appRoot}`);
|
|
500
|
+
};
|
|
501
|
+
const handleUpdate = (cwd, spawn, log, error) => {
|
|
502
|
+
requireOutsideAppRoot(cwd, "sc update");
|
|
503
|
+
try {
|
|
504
|
+
updateGlobalCliPackage(cwd, spawn, log);
|
|
505
|
+
log("Updated the globally installed Sculptor CLI package.");
|
|
506
|
+
}
|
|
507
|
+
catch (caught) {
|
|
508
|
+
const message = caught instanceof Error ? caught.message : String(caught);
|
|
509
|
+
error(message);
|
|
510
|
+
process.exit(1);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
const handleDoctor = (cwd, log, error) => {
|
|
514
|
+
const report = createDoctorReport(cwd);
|
|
515
|
+
printDoctorReport(report, log);
|
|
516
|
+
if (hasDoctorErrors(report)) {
|
|
517
|
+
error("Doctor found blocking issues.");
|
|
518
|
+
process.exit(1);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
const handleAgents = (args, cwd, log, error) => {
|
|
522
|
+
const [subcommand] = args;
|
|
523
|
+
if (subcommand && subcommand !== "refresh" && subcommand !== "help") {
|
|
524
|
+
error(`Unknown agents subcommand "${subcommand}".`);
|
|
525
|
+
process.exit(1);
|
|
526
|
+
}
|
|
527
|
+
if (subcommand === "help") {
|
|
528
|
+
log(`# Agents\n\nUsage: \`sc agents\` or \`sc agents refresh\`\n\nWrites \`AGENTS.md\` in the current directory.`);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
const filePath = writeAgentsMarkdown(cwd);
|
|
532
|
+
log(`Wrote ${path.relative(cwd, filePath) || "AGENTS.md"}`);
|
|
533
|
+
};
|
|
324
534
|
const handleDev = (args, cwd, spawn, log) => {
|
|
325
535
|
const appRoot = requireAppRoot(cwd, "sc dev");
|
|
326
536
|
const devServer = resolveDefaultDevServer(cwd);
|
|
@@ -365,6 +575,19 @@ const handleStart = (args, cwd, spawn, log) => {
|
|
|
365
575
|
const handleBuild = (cwd, spawn, log) => {
|
|
366
576
|
const appRoot = requireAppRoot(cwd, "sc build");
|
|
367
577
|
const appTsconfig = path.join(appRoot, "tsconfig.json");
|
|
578
|
+
try {
|
|
579
|
+
const report = validatePackageRegistryState(appRoot);
|
|
580
|
+
if (report.packageCountDetected > 0 || report.packageCountRegistered > 0 || report.messages.length > 0) {
|
|
581
|
+
log(`Packages detected: ${report.packageCountDetected}`);
|
|
582
|
+
log(`Packages registered: ${report.packageCountRegistered}`);
|
|
583
|
+
for (const message of report.messages) {
|
|
584
|
+
log(message);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
log(error instanceof Error ? error.message : String(error));
|
|
590
|
+
}
|
|
368
591
|
runSpawn("npx", ["tsc", "-p", appTsconfig], appRoot, spawn, log);
|
|
369
592
|
};
|
|
370
593
|
const handleLint = (cwd, spawn, log) => {
|
|
@@ -427,10 +650,10 @@ const handleAdd = async (args, cwd, log, error) => {
|
|
|
427
650
|
const manifest = resolvePluginManifest(module, pluginName);
|
|
428
651
|
log(`Loaded plugin ${manifest.name} for ${appRoot}`);
|
|
429
652
|
};
|
|
430
|
-
const handleGenerate = (args, cwd, prompt, log, error) => {
|
|
653
|
+
const handleGenerate = async (args, cwd, prompt, spawn, log, error) => {
|
|
431
654
|
const [kindInput, ...restInput] = args;
|
|
432
655
|
if (!kindInput) {
|
|
433
|
-
error("Usage: sc generate <controller|service|module|middleware|type|route> <name>");
|
|
656
|
+
error("Usage: sc generate <controller|service|repository|dto|module|middleware|type|route|pkg> <name>");
|
|
434
657
|
process.exit(1);
|
|
435
658
|
}
|
|
436
659
|
const kindMap = {
|
|
@@ -438,25 +661,32 @@ const handleGenerate = (args, cwd, prompt, log, error) => {
|
|
|
438
661
|
controller: "controller",
|
|
439
662
|
s: "service",
|
|
440
663
|
service: "service",
|
|
664
|
+
repo: "repository",
|
|
665
|
+
repository: "repository",
|
|
441
666
|
m: "module",
|
|
442
667
|
mo: "module",
|
|
443
668
|
module: "module",
|
|
444
669
|
mw: "middleware",
|
|
445
670
|
middleware: "middleware",
|
|
671
|
+
dto: "dto",
|
|
446
672
|
t: "type",
|
|
447
673
|
type: "type",
|
|
448
674
|
r: "route",
|
|
449
675
|
route: "route",
|
|
450
|
-
resource: "route"
|
|
676
|
+
resource: "route",
|
|
677
|
+
pkg: "pkg",
|
|
678
|
+
package: "pkg"
|
|
451
679
|
};
|
|
452
680
|
const kind = kindMap[kindInput];
|
|
453
681
|
if (!kind) {
|
|
454
682
|
error(`Unknown generator "${kindInput}".`);
|
|
455
683
|
process.exit(1);
|
|
456
684
|
}
|
|
457
|
-
const { args:
|
|
685
|
+
const { args: restAfterOutputDir, outputDir } = extractOutputDir(restInput);
|
|
686
|
+
const rest = stripPackageFlag(restAfterOutputDir);
|
|
458
687
|
const positional = rest.filter((arg) => !isFlag(arg));
|
|
459
688
|
const explicitName = positional[0];
|
|
689
|
+
const packageTarget = getPackageFlagValue(restAfterOutputDir);
|
|
460
690
|
const fallbackMode = resolveDefaultMode(cwd);
|
|
461
691
|
const devServer = resolveDefaultDevServer(cwd);
|
|
462
692
|
const mode = parseGenerateMode(readModeFromFlags(rest, fallbackMode), fallbackMode);
|
|
@@ -475,6 +705,21 @@ const handleGenerate = (args, cwd, prompt, log, error) => {
|
|
|
475
705
|
process.exit(1);
|
|
476
706
|
}
|
|
477
707
|
const appRoot = requireAppRoot(cwd, "sc generate");
|
|
708
|
+
const packagePath = packageTarget ? resolveRegisteredPackagePath(appRoot, packageTarget) : undefined;
|
|
709
|
+
if (packageTarget && !packagePath) {
|
|
710
|
+
error(`Package "${packageTarget}" is not registered.`);
|
|
711
|
+
process.exit(1);
|
|
712
|
+
}
|
|
713
|
+
const packageScopedOutput = packagePath !== undefined
|
|
714
|
+
? (outputDir
|
|
715
|
+
? path.posix.join(packagePath, outputDir)
|
|
716
|
+
: kind === "route"
|
|
717
|
+
? path.posix.join(packagePath, "routes")
|
|
718
|
+
: packagePath)
|
|
719
|
+
: undefined;
|
|
720
|
+
const resolvedOutputDir = kind === "pkg"
|
|
721
|
+
? outputDir ?? String(loadConfig(appRoot).framework.project?.srcRoot ?? "src")
|
|
722
|
+
: packageScopedOutput ?? outputDir;
|
|
478
723
|
const resolvedName = explicitName ??
|
|
479
724
|
(() => {
|
|
480
725
|
if (!outputDir) {
|
|
@@ -483,14 +728,32 @@ const handleGenerate = (args, cwd, prompt, log, error) => {
|
|
|
483
728
|
const parts = outputDir.split(/[\\/]/).filter(Boolean);
|
|
484
729
|
return parts[parts.length - 1] ?? "index";
|
|
485
730
|
})();
|
|
486
|
-
const files = generateResourceFiles(kind, resolvedName, mode, devServer,
|
|
731
|
+
const files = await generateResourceFiles(kind, resolvedName, mode, devServer, resolvedOutputDir, typeVariant, functionalRoutes, resolveTestingGenerate(appRoot), { cwd, prompt, spawn, log, error });
|
|
487
732
|
const targetDir = appRoot;
|
|
488
|
-
|
|
733
|
+
if (!files) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
await writeGeneratedFiles(targetDir, files, { cwd, prompt, spawn, log, error });
|
|
737
|
+
if (packagePath || kind === "pkg") {
|
|
738
|
+
const registry = syncPackageRegistry(appRoot);
|
|
739
|
+
for (const filePath of Object.keys(files)) {
|
|
740
|
+
upsertFileIntoRegistry(registry, filePath);
|
|
741
|
+
const owningPackage = getOwningPackage(registry, filePath);
|
|
742
|
+
if (owningPackage) {
|
|
743
|
+
updatePackageIndexForRecord(path.join(appRoot, owningPackage.index), owningPackage);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
savePackageRegistry(appRoot, registry);
|
|
747
|
+
}
|
|
489
748
|
if (resolveTestingGenerate(appRoot)) {
|
|
490
|
-
syncTestHarness(targetDir);
|
|
749
|
+
await syncTestHarness(targetDir, { cwd, prompt, spawn, log, error });
|
|
750
|
+
}
|
|
751
|
+
syncPackageRegistry(appRoot);
|
|
752
|
+
if (kind === "pkg") {
|
|
753
|
+
ensureRootRegistryForPackages(appRoot);
|
|
491
754
|
}
|
|
492
755
|
log(`Generated ${kind} "${resolvedName}" using ${mode} mode.`);
|
|
493
|
-
return
|
|
756
|
+
return;
|
|
494
757
|
};
|
|
495
758
|
export const runCli = async (argv = process.argv, options = {}) => {
|
|
496
759
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -531,21 +794,60 @@ export const runCli = async (argv = process.argv, options = {}) => {
|
|
|
531
794
|
case "build":
|
|
532
795
|
handleBuild(cwd, spawn, log);
|
|
533
796
|
return;
|
|
797
|
+
case "sync":
|
|
798
|
+
handleSyncCommand(args, { cwd, prompt, log, error });
|
|
799
|
+
return;
|
|
534
800
|
case "lint":
|
|
535
801
|
handleLint(cwd, spawn, log);
|
|
536
802
|
return;
|
|
537
803
|
case "test":
|
|
538
804
|
handleTest(cwd, spawn, log);
|
|
539
805
|
return;
|
|
806
|
+
case "install":
|
|
807
|
+
case "i":
|
|
808
|
+
handleInstall(args, cwd, spawn, log, error);
|
|
809
|
+
return;
|
|
810
|
+
case "update":
|
|
811
|
+
handleUpdate(cwd, spawn, log, error);
|
|
812
|
+
return;
|
|
813
|
+
case "doctor":
|
|
814
|
+
handleDoctor(cwd, log, error);
|
|
815
|
+
return;
|
|
540
816
|
case "config":
|
|
541
817
|
handleConfig(args, cwd, log, error);
|
|
542
818
|
return;
|
|
543
819
|
case "add":
|
|
544
820
|
await handleAdd(args, cwd, log, error);
|
|
545
821
|
return;
|
|
822
|
+
case "agents":
|
|
823
|
+
handleAgents(args, cwd, log, error);
|
|
824
|
+
return;
|
|
825
|
+
case "pkg":
|
|
826
|
+
case "package":
|
|
827
|
+
await handlePackageCommand(args, { cwd, prompt, log, error });
|
|
828
|
+
return;
|
|
829
|
+
case "ls":
|
|
830
|
+
case "list":
|
|
831
|
+
handleLsCommand(args, { cwd, prompt, log, error });
|
|
832
|
+
return;
|
|
833
|
+
case "reg":
|
|
834
|
+
case "register":
|
|
835
|
+
case "r":
|
|
836
|
+
await handleRegisterCommand("reg", args, { cwd, prompt, log, error });
|
|
837
|
+
return;
|
|
838
|
+
case "ureg":
|
|
839
|
+
case "unreg":
|
|
840
|
+
case "unregister":
|
|
841
|
+
case "ur":
|
|
842
|
+
await handleRegisterCommand("ureg", args, { cwd, prompt, log, error });
|
|
843
|
+
return;
|
|
844
|
+
case "rm":
|
|
845
|
+
case "remove":
|
|
846
|
+
await handleRegisterCommand("rm", args, { cwd, prompt, log, error });
|
|
847
|
+
return;
|
|
546
848
|
case "generate":
|
|
547
849
|
case "g":
|
|
548
|
-
await handleGenerate(args, cwd, prompt, log, error);
|
|
850
|
+
await handleGenerate(args, cwd, prompt, spawn, log, error);
|
|
549
851
|
return;
|
|
550
852
|
}
|
|
551
853
|
};
|