@jskit-ai/jskit-cli 0.2.27 → 0.2.29

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 (59) hide show
  1. package/package.json +3 -2
  2. package/src/server/cliRuntime/appState.js +226 -0
  3. package/src/server/cliRuntime/capabilitySupport.js +194 -0
  4. package/src/server/cliRuntime/descriptorValidation.js +150 -0
  5. package/src/server/cliRuntime/ioAndMigrations.js +381 -0
  6. package/src/server/cliRuntime/localPackageSupport.js +390 -0
  7. package/src/server/cliRuntime/mutationApplication.js +9 -0
  8. package/src/server/cliRuntime/mutationWhen.js +285 -0
  9. package/src/server/cliRuntime/mutations/fileMutations.js +247 -0
  10. package/src/server/cliRuntime/mutations/installMigrationMutation.js +213 -0
  11. package/src/server/cliRuntime/mutations/mutationPathUtils.js +12 -0
  12. package/src/server/cliRuntime/mutations/surfaceTargets.js +155 -0
  13. package/src/server/cliRuntime/mutations/templateContext.js +171 -0
  14. package/src/server/cliRuntime/mutations/textMutations.js +250 -0
  15. package/src/server/cliRuntime/packageInstallFlow.js +489 -0
  16. package/src/server/cliRuntime/packageIntrospection/exportEntries.js +259 -0
  17. package/src/server/cliRuntime/packageIntrospection/exportedSymbols.js +216 -0
  18. package/src/server/cliRuntime/packageIntrospection/placementNormalization.js +98 -0
  19. package/src/server/cliRuntime/packageIntrospection/providerBindingIntrospection.js +377 -0
  20. package/src/server/cliRuntime/packageIntrospection.js +137 -0
  21. package/src/server/cliRuntime/packageOptions.js +299 -0
  22. package/src/server/cliRuntime/packageRegistries.js +343 -0
  23. package/src/server/cliRuntime/packageTemplateResolution.js +131 -0
  24. package/src/server/cliRuntime/viteProxy.js +356 -0
  25. package/src/server/commandHandlers/health.js +292 -0
  26. package/src/server/commandHandlers/list.js +292 -0
  27. package/src/server/commandHandlers/package.js +23 -0
  28. package/src/server/commandHandlers/packageCommands/add.js +282 -0
  29. package/src/server/commandHandlers/packageCommands/create.js +155 -0
  30. package/src/server/commandHandlers/packageCommands/generate.js +116 -0
  31. package/src/server/commandHandlers/packageCommands/migrations.js +155 -0
  32. package/src/server/commandHandlers/packageCommands/position.js +103 -0
  33. package/src/server/commandHandlers/packageCommands/remove.js +181 -0
  34. package/src/server/commandHandlers/packageCommands/update.js +40 -0
  35. package/src/server/commandHandlers/shared.js +314 -0
  36. package/src/server/commandHandlers/show/payloads.js +92 -0
  37. package/src/server/commandHandlers/show/renderBundleText.js +16 -0
  38. package/src/server/commandHandlers/show/renderHelpers.js +82 -0
  39. package/src/server/commandHandlers/show/renderPackageCapabilities.js +124 -0
  40. package/src/server/commandHandlers/show/renderPackageExports.js +203 -0
  41. package/src/server/commandHandlers/show/renderPackageText.js +332 -0
  42. package/src/server/commandHandlers/show.js +114 -0
  43. package/src/server/core/argParser.js +144 -0
  44. package/src/server/{runtimeDeps.js → core/buildCommandDeps.js} +2 -1
  45. package/src/server/core/commandCatalog.js +47 -0
  46. package/src/server/core/createCliRunner.js +150 -0
  47. package/src/server/core/createCommandHandlers.js +43 -0
  48. package/src/server/{runCli.js → core/dispatchCli.js} +14 -1
  49. package/src/server/core/usageHelp.js +344 -0
  50. package/src/server/index.js +1 -1
  51. package/src/server/{optionInterpolation.js → shared/optionInterpolation.js} +12 -1
  52. package/src/server/{pathResolution.js → shared/pathResolution.js} +1 -1
  53. package/src/server/argParser.js +0 -206
  54. package/src/server/cliRuntime.js +0 -4956
  55. package/src/server/commandHandlers.js +0 -2109
  56. /package/src/server/{cliError.js → shared/cliError.js} +0 -0
  57. /package/src/server/{collectionUtils.js → shared/collectionUtils.js} +0 -0
  58. /package/src/server/{outputFormatting.js → shared/outputFormatting.js} +0 -0
  59. /package/src/server/{packageIdHelpers.js → shared/packageIdHelpers.js} +0 -0
@@ -0,0 +1,292 @@
1
+ import {
2
+ ensureArray,
3
+ ensureObject,
4
+ sortStrings
5
+ } from "../shared/collectionUtils.js";
6
+
7
+ function createHealthCommands(ctx = {}) {
8
+ const {
9
+ resolveAppRootFromCwd,
10
+ loadLockFile,
11
+ loadPackageRegistry,
12
+ loadBundleRegistry,
13
+ loadAppLocalPackageRegistry,
14
+ mergePackageRegistries,
15
+ hydratePackageRegistryFromInstalledNodeModules,
16
+ inspectPackageOfferings,
17
+ fileExists,
18
+ path
19
+ } = ctx;
20
+
21
+ function collectDescriptorContainerTokens({ packageId, side, values, issues }) {
22
+ const declaredTokens = new Set();
23
+ const duplicateTokens = new Set();
24
+ let invalidCount = 0;
25
+
26
+ for (const rawValue of ensureArray(values)) {
27
+ if (typeof rawValue !== "string") {
28
+ invalidCount += 1;
29
+ continue;
30
+ }
31
+ const token = rawValue.trim();
32
+ if (!token) {
33
+ invalidCount += 1;
34
+ continue;
35
+ }
36
+ if (declaredTokens.has(token)) {
37
+ duplicateTokens.add(token);
38
+ continue;
39
+ }
40
+ declaredTokens.add(token);
41
+ }
42
+
43
+ if (invalidCount > 0) {
44
+ issues.push({
45
+ packageId,
46
+ side,
47
+ code: "descriptor-token-invalid",
48
+ message: `${packageId} (${side}): metadata.apiSummary.containerTokens includes ${invalidCount} non-string or empty token value(s).`
49
+ });
50
+ }
51
+ for (const token of sortStrings([...duplicateTokens])) {
52
+ issues.push({
53
+ packageId,
54
+ side,
55
+ code: "descriptor-token-duplicate",
56
+ token,
57
+ message: `${packageId} (${side}): descriptor token is declared more than once: ${token}.`
58
+ });
59
+ }
60
+
61
+ return declaredTokens;
62
+ }
63
+
64
+ function collectUsedContainerTokens({ packageId, side, bindings, issues }) {
65
+ const usedTokens = new Set();
66
+ for (const rawBinding of ensureArray(bindings)) {
67
+ const binding = ensureObject(rawBinding);
68
+ const tokenExpression = String(binding.tokenExpression || "").trim();
69
+ const token = String(binding.token || "").trim();
70
+ const location = String(binding.location || "").trim();
71
+ if (binding.tokenResolved !== true || !token) {
72
+ const expressionLabel = tokenExpression || "<empty>";
73
+ const locationSuffix = location ? ` at ${location}` : "";
74
+ issues.push({
75
+ packageId,
76
+ side,
77
+ code: "binding-token-unresolved",
78
+ tokenExpression: expressionLabel,
79
+ location,
80
+ message: `${packageId} (${side}): unresolved DI token expression "${expressionLabel}"${locationSuffix}.`
81
+ });
82
+ continue;
83
+ }
84
+ usedTokens.add(token);
85
+ }
86
+ return usedTokens;
87
+ }
88
+
89
+ function collectProviderIntrospectionIssues({ packageId, packageInsights, issues }) {
90
+ const introspection = ensureObject(packageInsights);
91
+ if (!introspection.available) {
92
+ issues.push({
93
+ packageId,
94
+ side: "",
95
+ code: "provider-introspection-unavailable",
96
+ message: `${packageId}: provider source introspection is unavailable, so DI token parity cannot be verified.`
97
+ });
98
+ return;
99
+ }
100
+
101
+ const notes = ensureArray(introspection.notes).map((value) => String(value || "").trim()).filter(Boolean);
102
+ for (const note of notes) {
103
+ if (
104
+ note.startsWith("Skipped wildcard provider entrypoint during introspection:") ||
105
+ note.startsWith("Provider file missing during introspection:") ||
106
+ note.startsWith("Failed reading provider ")
107
+ ) {
108
+ issues.push({
109
+ packageId,
110
+ side: "",
111
+ code: "provider-introspection-incomplete",
112
+ message: `${packageId}: ${note}`
113
+ });
114
+ }
115
+ }
116
+ }
117
+
118
+ function collectDiLabelParityIssuesForPackage({ packageEntry, packageInsights }) {
119
+ const packageId = String(packageEntry?.packageId || "").trim();
120
+ const descriptor = ensureObject(packageEntry?.descriptor);
121
+ const metadataApiSummary = ensureObject(ensureObject(descriptor.metadata).apiSummary);
122
+ const descriptorTokenSummary = ensureObject(metadataApiSummary.containerTokens);
123
+ const bindingSections = ensureObject(ensureObject(packageInsights).containerBindings);
124
+ const issues = [];
125
+ const sides = ["server", "client"];
126
+
127
+ collectProviderIntrospectionIssues({ packageId, packageInsights, issues });
128
+
129
+ for (const side of sides) {
130
+ const declaredTokens = collectDescriptorContainerTokens({
131
+ packageId,
132
+ side,
133
+ values: descriptorTokenSummary[side],
134
+ issues
135
+ });
136
+ const usedTokens = collectUsedContainerTokens({
137
+ packageId,
138
+ side,
139
+ bindings: bindingSections[side],
140
+ issues
141
+ });
142
+
143
+ for (const token of sortStrings([...usedTokens])) {
144
+ if (!declaredTokens.has(token)) {
145
+ issues.push({
146
+ packageId,
147
+ side,
148
+ code: "binding-token-undeclared",
149
+ token,
150
+ message: `${packageId} (${side}): token is used by providers but missing from metadata.apiSummary.containerTokens.${side}: ${token}.`
151
+ });
152
+ }
153
+ }
154
+ for (const token of sortStrings([...declaredTokens])) {
155
+ if (!usedTokens.has(token)) {
156
+ issues.push({
157
+ packageId,
158
+ side,
159
+ code: "descriptor-token-unused",
160
+ token,
161
+ message: `${packageId} (${side}): token is declared in metadata.apiSummary.containerTokens.${side} but never bound by providers: ${token}.`
162
+ });
163
+ }
164
+ }
165
+ }
166
+
167
+ return issues;
168
+ }
169
+
170
+ async function commandDoctor({ cwd, options, stdout }) {
171
+ const appRoot = await resolveAppRootFromCwd(cwd);
172
+ const { lock } = await loadLockFile(appRoot);
173
+ const packageRegistry = await loadPackageRegistry();
174
+ const appLocalRegistry = await loadAppLocalPackageRegistry(appRoot);
175
+ const combinedPackageRegistry = mergePackageRegistries(packageRegistry, appLocalRegistry);
176
+ const issues = [];
177
+ const installed = ensureObject(lock.installedPackages);
178
+ await hydratePackageRegistryFromInstalledNodeModules({
179
+ appRoot,
180
+ packageRegistry: combinedPackageRegistry,
181
+ seedPackageIds: Object.keys(installed)
182
+ });
183
+
184
+ for (const [packageId, lockEntryValue] of Object.entries(installed)) {
185
+ const lockEntry = ensureObject(lockEntryValue);
186
+ if (!combinedPackageRegistry.has(packageId)) {
187
+ issues.push(`Installed package not found in package registry: ${packageId}`);
188
+ continue;
189
+ }
190
+
191
+ const managed = ensureObject(lockEntry.managed);
192
+ for (const fileChange of ensureArray(managed.files)) {
193
+ const changeRecord = ensureObject(fileChange);
194
+ const relativePath = String(changeRecord.path || "").trim();
195
+ const absolutePath = path.join(appRoot, relativePath);
196
+ if (!(await fileExists(absolutePath))) {
197
+ issues.push(`${packageId}: managed file missing: ${relativePath}`);
198
+ }
199
+ }
200
+ }
201
+
202
+ const payload = {
203
+ appRoot,
204
+ lockVersion: lock.lockVersion,
205
+ installedPackages: sortStrings(Object.keys(installed)),
206
+ issues
207
+ };
208
+
209
+ if (options.json) {
210
+ stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
211
+ } else {
212
+ stdout.write(`App root: ${appRoot}\n`);
213
+ stdout.write(`Installed packages: ${payload.installedPackages.length}\n`);
214
+ if (issues.length === 0) {
215
+ stdout.write("Doctor status: healthy\n");
216
+ } else {
217
+ stdout.write(`Doctor status: unhealthy (${issues.length} issue(s))\n`);
218
+ for (const issue of issues) {
219
+ stdout.write(`- ${issue}\n`);
220
+ }
221
+ }
222
+ }
223
+
224
+ return issues.length === 0 ? 0 : 1;
225
+ }
226
+
227
+ async function commandLintDescriptors({ options, stdout }) {
228
+ const packageRegistry = await loadPackageRegistry();
229
+ const bundleRegistry = await loadBundleRegistry();
230
+ const shouldCheckDiLabels = options.checkDiLabels === true;
231
+ let diLabelIssues = [];
232
+ if (shouldCheckDiLabels) {
233
+ const issues = [];
234
+ for (const packageId of sortStrings([...packageRegistry.keys()])) {
235
+ const packageEntry = packageRegistry.get(packageId);
236
+ if (!packageEntry) {
237
+ continue;
238
+ }
239
+ const packageInsights = await inspectPackageOfferings({ packageEntry });
240
+ issues.push(...collectDiLabelParityIssuesForPackage({ packageEntry, packageInsights }));
241
+ }
242
+ diLabelIssues = issues;
243
+ }
244
+ const payload = {
245
+ packageCount: packageRegistry.size,
246
+ bundleCount: bundleRegistry.size,
247
+ packages: sortStrings([...packageRegistry.keys()]),
248
+ bundles: sortStrings([...bundleRegistry.keys()]),
249
+ diLabelCheck: shouldCheckDiLabels
250
+ ? {
251
+ enabled: true,
252
+ issueCount: diLabelIssues.length,
253
+ issues: diLabelIssues
254
+ }
255
+ : {
256
+ enabled: false
257
+ }
258
+ };
259
+
260
+ if (options.json) {
261
+ stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
262
+ } else {
263
+ const descriptorStatus = shouldCheckDiLabels && diLabelIssues.length > 0 ? "failed" : "passed";
264
+ stdout.write(`Descriptor lint ${descriptorStatus}.\n`);
265
+ stdout.write(`Packages: ${payload.packageCount}\n`);
266
+ stdout.write(`Bundles: ${payload.bundleCount}\n`);
267
+ if (shouldCheckDiLabels) {
268
+ if (diLabelIssues.length === 0) {
269
+ stdout.write("DI label parity check passed.\n");
270
+ } else {
271
+ stdout.write(`DI label parity check failed (${diLabelIssues.length} issue(s)).\n`);
272
+ for (const issue of diLabelIssues) {
273
+ const code = String(issue?.code || "").trim();
274
+ const codeLabel = code ? `[${code}] ` : "";
275
+ stdout.write(`- ${codeLabel}${String(issue?.message || "").trim()}\n`);
276
+ }
277
+ }
278
+ }
279
+ }
280
+ if (shouldCheckDiLabels && diLabelIssues.length > 0) {
281
+ return 1;
282
+ }
283
+ return 0;
284
+ }
285
+
286
+ return {
287
+ commandDoctor,
288
+ commandLintDescriptors
289
+ };
290
+ }
291
+
292
+ export { createHealthCommands };
@@ -0,0 +1,292 @@
1
+ import {
2
+ ensureArray,
3
+ ensureObject,
4
+ sortStrings
5
+ } from "../shared/collectionUtils.js";
6
+
7
+ function createListCommands(ctx = {}) {
8
+ const {
9
+ createCliError,
10
+ createColorFormatter,
11
+ normalizeRelativePosixPath,
12
+ resolveAppRootFromCwd,
13
+ loadLockFile,
14
+ loadPackageRegistry,
15
+ loadBundleRegistry,
16
+ loadAppLocalPackageRegistry,
17
+ discoverShellOutletTargetsFromApp,
18
+ resolvePackageKind
19
+ } = ctx;
20
+
21
+ async function commandList({ positional, options, cwd, stdout }) {
22
+ const packageRegistry = await loadPackageRegistry();
23
+ const bundleRegistry = await loadBundleRegistry();
24
+
25
+ const appRoot = await resolveAppRootFromCwd(cwd);
26
+ const appLocalRegistry = await loadAppLocalPackageRegistry(appRoot);
27
+ const { lock } = await loadLockFile(appRoot);
28
+ const installedPackageEntries = ensureObject(lock.installedPackages);
29
+ const installedPackages = new Set(Object.keys(installedPackageEntries));
30
+ const installedUnknownPackageIds = sortStrings(
31
+ [...installedPackages].filter((packageId) => !packageRegistry.has(packageId))
32
+ );
33
+ const installedLocalPackageIds = sortStrings(
34
+ installedUnknownPackageIds.filter((packageId) => {
35
+ const lockEntry = ensureObject(installedPackageEntries[packageId]);
36
+ const sourceType = String(ensureObject(lockEntry.source).type || "").trim();
37
+ return sourceType === "local-package" || sourceType === "app-local-package" || appLocalRegistry.has(packageId);
38
+ })
39
+ );
40
+ const installedExternalPackageIds = sortStrings(
41
+ installedUnknownPackageIds.filter((packageId) => !installedLocalPackageIds.includes(packageId))
42
+ );
43
+ const availableLocalPackageIds = sortStrings(
44
+ [...appLocalRegistry.keys()].filter((packageId) => !installedPackages.has(packageId))
45
+ );
46
+
47
+ const mode = String(positional[0] || "").trim();
48
+ const shouldListBundles = !mode || mode === "bundles";
49
+ const shouldListPackages = !mode || mode === "packages";
50
+ const shouldListGenerators = !mode || mode === "generators";
51
+ if (mode === "placements") {
52
+ throw createCliError('list mode "placements" moved to a dedicated command: jskit list-placements.');
53
+ }
54
+
55
+ if (!shouldListBundles && !shouldListPackages && !shouldListGenerators) {
56
+ throw createCliError(`Unknown list mode: ${mode}`, { showUsage: true });
57
+ }
58
+
59
+ const color = createColorFormatter(stdout);
60
+ const lines = [];
61
+ if (shouldListBundles) {
62
+ lines.push(color.heading("Available bundles:"));
63
+ const bundleIds = sortStrings([...bundleRegistry.keys()]);
64
+ for (const bundleId of bundleIds) {
65
+ const bundle = bundleRegistry.get(bundleId);
66
+ const packageIds = ensureArray(bundle.packages).map((value) => String(value));
67
+ const isInstalled = packageIds.length > 0 && packageIds.every((packageId) => installedPackages.has(packageId));
68
+ const providerLabel = Number(bundle.provider) === 1 ? " [provider]" : "";
69
+ const installedLabel = isInstalled ? " (installed)" : "";
70
+ lines.push(
71
+ `- ${color.item(bundle.bundleId)} ${color.version(`(${bundle.version})`)}${isInstalled ? color.installed(installedLabel) : installedLabel}${providerLabel ? color.provider(providerLabel) : providerLabel}: ${String(bundle.description || "")}`
72
+ );
73
+ if (options.full || options.expanded) {
74
+ for (const packageId of packageIds) {
75
+ lines.push(` - ${color.dim(packageId)}`);
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ if (shouldListPackages) {
82
+ if (lines.length > 0) {
83
+ lines.push("");
84
+ }
85
+ lines.push(color.heading("Available runtime packages:"));
86
+ const packageIds = sortStrings([...packageRegistry.keys()].filter((packageId) => {
87
+ const packageEntry = packageRegistry.get(packageId);
88
+ return resolvePackageKind(packageEntry) === "runtime";
89
+ }));
90
+ for (const packageId of packageIds) {
91
+ const packageEntry = packageRegistry.get(packageId);
92
+ const installedLabel = installedPackages.has(packageId) ? " (installed)" : "";
93
+ lines.push(
94
+ `- ${color.item(packageId)} ${color.version(`(${packageEntry.version})`)}${installedLabel ? color.installed(installedLabel) : ""}`
95
+ );
96
+ }
97
+
98
+ if (installedLocalPackageIds.length > 0) {
99
+ lines.push("");
100
+ lines.push(color.heading("Installed local packages:"));
101
+ for (const packageId of installedLocalPackageIds) {
102
+ const lockEntry = ensureObject(installedPackageEntries[packageId]);
103
+ const version = String(lockEntry.version || "").trim();
104
+ const versionLabel = version ? ` ${color.version(`(${version})`)}` : "";
105
+ lines.push(`- ${color.item(packageId)}${versionLabel}${color.installed(" (installed)")}`);
106
+ }
107
+ }
108
+
109
+ if (installedExternalPackageIds.length > 0) {
110
+ lines.push("");
111
+ lines.push(color.heading("Installed external packages:"));
112
+ for (const packageId of installedExternalPackageIds) {
113
+ const lockEntry = ensureObject(installedPackageEntries[packageId]);
114
+ const version = String(lockEntry.version || "").trim();
115
+ const versionLabel = version ? ` ${color.version(`(${version})`)}` : "";
116
+ lines.push(`- ${color.item(packageId)}${versionLabel}${color.installed(" (installed)")}`);
117
+ }
118
+ }
119
+
120
+ if (availableLocalPackageIds.length > 0) {
121
+ lines.push("");
122
+ lines.push(color.heading("Available local packages (not installed):"));
123
+ for (const packageId of availableLocalPackageIds) {
124
+ const packageEntry = appLocalRegistry.get(packageId);
125
+ const version = String(packageEntry?.version || "").trim();
126
+ const versionLabel = version ? ` ${color.version(`(${version})`)}` : "";
127
+ lines.push(`- ${color.item(packageId)}${versionLabel}`);
128
+ }
129
+ }
130
+ }
131
+
132
+ if (shouldListGenerators) {
133
+ if (lines.length > 0) {
134
+ lines.push("");
135
+ }
136
+ lines.push(color.heading("Available generators:"));
137
+ const packageIds = sortStrings([...packageRegistry.keys()].filter((packageId) => {
138
+ const packageEntry = packageRegistry.get(packageId);
139
+ return resolvePackageKind(packageEntry) === "generator";
140
+ }));
141
+ for (const packageId of packageIds) {
142
+ const packageEntry = packageRegistry.get(packageId);
143
+ const installedLabel = installedPackages.has(packageId) ? " (installed)" : "";
144
+ lines.push(
145
+ `- ${color.item(packageId)} ${color.version(`(${packageEntry.version})`)}${installedLabel ? color.installed(installedLabel) : ""}`
146
+ );
147
+ }
148
+ }
149
+
150
+ if (options.json) {
151
+ const payload = {
152
+ bundles: shouldListBundles
153
+ ? sortStrings([...bundleRegistry.keys()]).map((bundleId) => {
154
+ const bundle = bundleRegistry.get(bundleId);
155
+ const packageIds = ensureArray(bundle.packages).map((value) => String(value));
156
+ return {
157
+ bundleId: bundle.bundleId,
158
+ version: bundle.version,
159
+ description: bundle.description || "",
160
+ provider: Number(bundle.provider) === 1,
161
+ installed: packageIds.length > 0 && packageIds.every((packageId) => installedPackages.has(packageId)),
162
+ packages: packageIds
163
+ };
164
+ })
165
+ : [],
166
+ packages: shouldListPackages
167
+ ? sortStrings([...packageRegistry.keys()])
168
+ .filter((packageId) => resolvePackageKind(packageRegistry.get(packageId)) === "runtime")
169
+ .map((packageId) => {
170
+ const packageEntry = packageRegistry.get(packageId);
171
+ return {
172
+ packageId,
173
+ version: packageEntry.version,
174
+ installed: installedPackages.has(packageId)
175
+ };
176
+ })
177
+ : [],
178
+ runtimePackages: shouldListPackages
179
+ ? sortStrings([...packageRegistry.keys()]).map((packageId) => {
180
+ const packageEntry = packageRegistry.get(packageId);
181
+ if (resolvePackageKind(packageEntry) !== "runtime") {
182
+ return null;
183
+ }
184
+ return {
185
+ packageId,
186
+ version: packageEntry.version,
187
+ installed: installedPackages.has(packageId)
188
+ };
189
+ }).filter(Boolean)
190
+ : [],
191
+ generators: shouldListGenerators
192
+ ? sortStrings([...packageRegistry.keys()]).map((packageId) => {
193
+ const packageEntry = packageRegistry.get(packageId);
194
+ if (resolvePackageKind(packageEntry) !== "generator") {
195
+ return null;
196
+ }
197
+ return {
198
+ packageId,
199
+ version: packageEntry.version,
200
+ installed: installedPackages.has(packageId)
201
+ };
202
+ }).filter(Boolean)
203
+ : [],
204
+ installedLocalPackages: shouldListPackages
205
+ ? installedLocalPackageIds.map((packageId) => {
206
+ const lockEntry = ensureObject(installedPackageEntries[packageId]);
207
+ return {
208
+ packageId,
209
+ version: String(lockEntry.version || "").trim()
210
+ };
211
+ })
212
+ : [],
213
+ installedExternalPackages: shouldListPackages
214
+ ? installedExternalPackageIds.map((packageId) => {
215
+ const lockEntry = ensureObject(installedPackageEntries[packageId]);
216
+ return {
217
+ packageId,
218
+ version: String(lockEntry.version || "").trim(),
219
+ source: ensureObject(lockEntry.source)
220
+ };
221
+ })
222
+ : [],
223
+ availableLocalPackages: shouldListPackages
224
+ ? availableLocalPackageIds.map((packageId) => {
225
+ const packageEntry = appLocalRegistry.get(packageId);
226
+ return {
227
+ packageId,
228
+ version: String(packageEntry?.version || "").trim(),
229
+ packagePath: normalizeRelativePosixPath(String(packageEntry?.relativeDir || ""))
230
+ };
231
+ })
232
+ : []
233
+ };
234
+ stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
235
+ } else {
236
+ stdout.write(`${lines.join("\n")}\n`);
237
+ }
238
+
239
+ return 0;
240
+ }
241
+
242
+ async function commandListPlacements({ options, cwd, stdout }) {
243
+ const appRoot = await resolveAppRootFromCwd(cwd);
244
+ const discoveredPlacements = await discoverShellOutletTargetsFromApp({
245
+ appRoot,
246
+ sourceRoot: "src"
247
+ });
248
+ const placementTargets = ensureArray(discoveredPlacements.targets)
249
+ .map((entry) => ensureObject(entry))
250
+ .filter((entry) => String(entry.id || "").trim())
251
+ .sort((left, right) => String(left.id || "").localeCompare(String(right.id || "")));
252
+
253
+ if (options.json) {
254
+ const payload = {
255
+ placements: placementTargets.map((placementTarget) => ({
256
+ id: String(placementTarget.id || "").trim(),
257
+ host: String(placementTarget.host || "").trim(),
258
+ position: String(placementTarget.position || "").trim(),
259
+ default: placementTarget.default === true,
260
+ sourcePath: String(placementTarget.sourcePath || "").trim()
261
+ }))
262
+ };
263
+ stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
264
+ return 0;
265
+ }
266
+
267
+ const color = createColorFormatter(stdout);
268
+ const lines = [color.heading("Available placements:")];
269
+ if (placementTargets.length < 1) {
270
+ lines.push("- none");
271
+ } else {
272
+ for (const placementTarget of placementTargets) {
273
+ const placementId = String(placementTarget.id || "").trim();
274
+ const sourcePath = String(placementTarget.sourcePath || "").trim();
275
+ const isDefault = placementTarget.default === true;
276
+ const defaultLabel = isDefault ? color.installed(" (default)") : "";
277
+ const sourceLabel = sourcePath ? ` ${color.dim(`[${sourcePath}]`)}` : "";
278
+ lines.push(`- ${color.item(placementId)}${defaultLabel}${sourceLabel}`);
279
+ }
280
+ }
281
+
282
+ stdout.write(`${lines.join("\n")}\n`);
283
+ return 0;
284
+ }
285
+
286
+ return {
287
+ commandList,
288
+ commandListPlacements
289
+ };
290
+ }
291
+
292
+ export { createListCommands };
@@ -0,0 +1,23 @@
1
+ import { runPackageCreateCommand } from "./packageCommands/create.js";
2
+ import { runPackageAddCommand } from "./packageCommands/add.js";
3
+ import { runPackageGenerateCommand } from "./packageCommands/generate.js";
4
+ import { runPackageUpdateCommand } from "./packageCommands/update.js";
5
+ import { runPackageMigrationsCommand } from "./packageCommands/migrations.js";
6
+ import { runPackagePositionCommand } from "./packageCommands/position.js";
7
+ import { runPackageRemoveCommand } from "./packageCommands/remove.js";
8
+
9
+ function createPackageCommands(ctx = {}) {
10
+ const commandAdd = async (args) => runPackageAddCommand(ctx, args);
11
+
12
+ return {
13
+ commandCreate: async (args) => runPackageCreateCommand(ctx, args),
14
+ commandAdd,
15
+ commandGenerate: async (args) => runPackageGenerateCommand(ctx, args, { runCommandAdd: commandAdd }),
16
+ commandUpdate: async (args) => runPackageUpdateCommand(ctx, args, { runCommandAdd: commandAdd }),
17
+ commandMigrations: async (args) => runPackageMigrationsCommand(ctx, args),
18
+ commandPosition: async (args) => runPackagePositionCommand(ctx, args),
19
+ commandRemove: async (args) => runPackageRemoveCommand(ctx, args)
20
+ };
21
+ }
22
+
23
+ export { createPackageCommands };