@pattern-stack/codegen 0.9.2 → 0.10.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 +5 -0
- package/consumer-skills/bridge/SKILL.md +265 -0
- package/consumer-skills/codegen/SKILL.md +115 -0
- package/consumer-skills/entities/SKILL.md +111 -0
- package/consumer-skills/entities/families-and-queries.md +82 -0
- package/consumer-skills/entities/yaml-reference.md +118 -0
- package/consumer-skills/events/SKILL.md +71 -0
- package/consumer-skills/events/authoring-events.md +164 -0
- package/consumer-skills/events/typed-bus-and-outbox.md +163 -0
- package/consumer-skills/jobs/SKILL.md +66 -0
- package/consumer-skills/jobs/handler-authoring.md +236 -0
- package/consumer-skills/jobs/pools-and-ordering.md +161 -0
- package/consumer-skills/subsystems/SKILL.md +105 -0
- package/consumer-skills/subsystems/wiring-and-order.md +120 -0
- package/consumer-skills/sync/SKILL.md +134 -0
- package/consumer-skills/sync/audit-and-detection.md +302 -0
- package/consumer-skills/sync/change-sources-and-sinks.md +442 -0
- package/dist/src/cli/index.js +913 -405
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.js +26 -4
- package/dist/src/index.js.map +1 -1
- package/package.json +2 -1
package/dist/src/cli/index.js
CHANGED
|
@@ -13,8 +13,8 @@ var __decorateParam = (index2, decorator) => (target, key) => decorator(target,
|
|
|
13
13
|
|
|
14
14
|
// src/cli/index.ts
|
|
15
15
|
import { readFileSync as readFileSync6 } from "fs";
|
|
16
|
-
import { join as
|
|
17
|
-
import { Builtins, Cli, Command as
|
|
16
|
+
import { join as join10 } from "path";
|
|
17
|
+
import { Builtins, Cli, Command as Command13 } from "clipanion";
|
|
18
18
|
|
|
19
19
|
// src/cli/noun-module.ts
|
|
20
20
|
import { Command, Option } from "clipanion";
|
|
@@ -24,13 +24,36 @@ import fs from "fs";
|
|
|
24
24
|
import path from "path";
|
|
25
25
|
import yaml from "yaml";
|
|
26
26
|
|
|
27
|
+
// src/utils/find-yaml-files.ts
|
|
28
|
+
import { readdirSync } from "fs";
|
|
29
|
+
import { join, resolve } from "path";
|
|
30
|
+
function isYaml(name) {
|
|
31
|
+
return name.endsWith(".yaml") || name.endsWith(".yml");
|
|
32
|
+
}
|
|
33
|
+
function findYamlFiles(dir) {
|
|
34
|
+
const root = resolve(dir);
|
|
35
|
+
const out = [];
|
|
36
|
+
const walk = (current) => {
|
|
37
|
+
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
38
|
+
if (entry.isDirectory()) {
|
|
39
|
+
if (entry.name.startsWith(".")) continue;
|
|
40
|
+
walk(join(current, entry.name));
|
|
41
|
+
} else if (isYaml(entry.name)) {
|
|
42
|
+
out.push(join(current, entry.name));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
walk(root);
|
|
47
|
+
return out.sort();
|
|
48
|
+
}
|
|
49
|
+
|
|
27
50
|
// src/scanner/index.ts
|
|
28
51
|
import { existsSync as existsSync5, statSync as statSync2 } from "fs";
|
|
29
|
-
import { join as
|
|
52
|
+
import { join as join7 } from "path";
|
|
30
53
|
|
|
31
54
|
// src/scanner/framework-detector.ts
|
|
32
|
-
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
33
|
-
import { join } from "path";
|
|
55
|
+
import { existsSync, readdirSync as readdirSync2, readFileSync } from "fs";
|
|
56
|
+
import { join as join2 } from "path";
|
|
34
57
|
var FRAMEWORK_MARKERS = {
|
|
35
58
|
nestjs: {
|
|
36
59
|
imports: [
|
|
@@ -87,12 +110,12 @@ var FRAMEWORK_MARKERS = {
|
|
|
87
110
|
function getAllTsFiles(dir, files = []) {
|
|
88
111
|
if (!existsSync(dir)) return files;
|
|
89
112
|
try {
|
|
90
|
-
const entries =
|
|
113
|
+
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
91
114
|
for (const entry of entries) {
|
|
92
115
|
if (entry.name === "node_modules" || entry.name.startsWith(".")) {
|
|
93
116
|
continue;
|
|
94
117
|
}
|
|
95
|
-
const fullPath =
|
|
118
|
+
const fullPath = join2(dir, entry.name);
|
|
96
119
|
if (entry.isDirectory()) {
|
|
97
120
|
getAllTsFiles(fullPath, files);
|
|
98
121
|
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
@@ -177,15 +200,15 @@ async function detectFramework(projectPath) {
|
|
|
177
200
|
}
|
|
178
201
|
|
|
179
202
|
// src/scanner/orm-detector.ts
|
|
180
|
-
import { existsSync as existsSync2, readdirSync as
|
|
181
|
-
import { join as
|
|
203
|
+
import { existsSync as existsSync2, readdirSync as readdirSync3, readFileSync as readFileSync2 } from "fs";
|
|
204
|
+
import { join as join3 } from "path";
|
|
182
205
|
async function detectORM(projectPath) {
|
|
183
206
|
const markers = {
|
|
184
207
|
drizzle: [],
|
|
185
208
|
prisma: [],
|
|
186
209
|
typeorm: []
|
|
187
210
|
};
|
|
188
|
-
const prismaSchemaPath =
|
|
211
|
+
const prismaSchemaPath = join3(projectPath, "prisma", "schema.prisma");
|
|
189
212
|
if (existsSync2(prismaSchemaPath)) {
|
|
190
213
|
markers.prisma.push("prisma/schema.prisma");
|
|
191
214
|
}
|
|
@@ -265,12 +288,12 @@ function findTypeScriptFiles(dir, files = []) {
|
|
|
265
288
|
return files;
|
|
266
289
|
}
|
|
267
290
|
try {
|
|
268
|
-
const entries =
|
|
291
|
+
const entries = readdirSync3(dir, { withFileTypes: true });
|
|
269
292
|
for (const entry of entries) {
|
|
270
293
|
if (shouldSkip(entry.name)) {
|
|
271
294
|
continue;
|
|
272
295
|
}
|
|
273
|
-
const fullPath =
|
|
296
|
+
const fullPath = join3(dir, entry.name);
|
|
274
297
|
if (entry.isDirectory()) {
|
|
275
298
|
findTypeScriptFiles(fullPath, files);
|
|
276
299
|
} else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx"))) {
|
|
@@ -298,8 +321,8 @@ function shouldSkip(name) {
|
|
|
298
321
|
}
|
|
299
322
|
|
|
300
323
|
// src/scanner/architecture-detector.ts
|
|
301
|
-
import { readdirSync as
|
|
302
|
-
import { join as
|
|
324
|
+
import { readdirSync as readdirSync4, statSync } from "fs";
|
|
325
|
+
import { join as join4 } from "path";
|
|
303
326
|
async function detectArchitecture(projectPath) {
|
|
304
327
|
const scores = [
|
|
305
328
|
detectCleanArchitecture(projectPath),
|
|
@@ -356,7 +379,7 @@ function detectFeatureArchitecture(projectPath) {
|
|
|
356
379
|
const basePath = findFolder(projectPath, baseFolder);
|
|
357
380
|
if (basePath) {
|
|
358
381
|
try {
|
|
359
|
-
const subfolders =
|
|
382
|
+
const subfolders = readdirSync4(basePath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => `${baseFolder}/${dirent.name}`);
|
|
360
383
|
if (subfolders.length > 0) {
|
|
361
384
|
matchedFolders.push(baseFolder);
|
|
362
385
|
matchedFolders.push(...subfolders.slice(0, 3));
|
|
@@ -406,8 +429,8 @@ function findMatchingFolders(projectPath, folderNames) {
|
|
|
406
429
|
}
|
|
407
430
|
function findFolder(projectPath, folderName) {
|
|
408
431
|
const candidates = [
|
|
409
|
-
|
|
410
|
-
|
|
432
|
+
join4(projectPath, folderName),
|
|
433
|
+
join4(projectPath, "src", folderName)
|
|
411
434
|
];
|
|
412
435
|
for (const candidate of candidates) {
|
|
413
436
|
try {
|
|
@@ -422,8 +445,8 @@ function findFolder(projectPath, folderName) {
|
|
|
422
445
|
}
|
|
423
446
|
|
|
424
447
|
// src/scanner/naming-detector.ts
|
|
425
|
-
import { existsSync as existsSync3, readdirSync as
|
|
426
|
-
import { basename, join as
|
|
448
|
+
import { existsSync as existsSync3, readdirSync as readdirSync5, readFileSync as readFileSync3 } from "fs";
|
|
449
|
+
import { basename, join as join5 } from "path";
|
|
427
450
|
var CASE_PATTERNS = {
|
|
428
451
|
"kebab-case": /^[a-z]+(-[a-z0-9]+)*$/,
|
|
429
452
|
"camelCase": /^[a-z]+([A-Z][a-z0-9]*)*$/,
|
|
@@ -452,12 +475,12 @@ var COMMON_SUFFIXES = [
|
|
|
452
475
|
function getAllTsFiles2(dir, files = []) {
|
|
453
476
|
if (!existsSync3(dir)) return files;
|
|
454
477
|
try {
|
|
455
|
-
const entries =
|
|
478
|
+
const entries = readdirSync5(dir, { withFileTypes: true });
|
|
456
479
|
for (const entry of entries) {
|
|
457
480
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "coverage" || entry.name.startsWith(".")) {
|
|
458
481
|
continue;
|
|
459
482
|
}
|
|
460
|
-
const fullPath =
|
|
483
|
+
const fullPath = join5(dir, entry.name);
|
|
461
484
|
if (entry.isDirectory()) {
|
|
462
485
|
getAllTsFiles2(fullPath, files);
|
|
463
486
|
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
@@ -617,7 +640,7 @@ async function detectNaming(projectPath) {
|
|
|
617
640
|
|
|
618
641
|
// src/scanner/config-generator.ts
|
|
619
642
|
import { existsSync as existsSync4 } from "fs";
|
|
620
|
-
import { join as
|
|
643
|
+
import { join as join6 } from "path";
|
|
621
644
|
function generateConfig(profile) {
|
|
622
645
|
const framework = profile.framework.detected;
|
|
623
646
|
const orm = profile.orm.detected;
|
|
@@ -640,7 +663,7 @@ function generateConfig(profile) {
|
|
|
640
663
|
}
|
|
641
664
|
function buildGenerateConfig(profile, paths) {
|
|
642
665
|
const explicitFrontendSrc = typeof paths.frontend_src === "string" && paths.frontend_src.length > 0;
|
|
643
|
-
const appsFrontendExists = existsSync4(
|
|
666
|
+
const appsFrontendExists = existsSync4(join6(profile.paths.root, "apps", "frontend"));
|
|
644
667
|
return {
|
|
645
668
|
architecture: "clean",
|
|
646
669
|
frontend: explicitFrontendSrc || appsFrontendExists
|
|
@@ -806,7 +829,7 @@ function calculateConfidence(profile) {
|
|
|
806
829
|
function findSrcDirectory(projectPath) {
|
|
807
830
|
const candidates = ["src", "app", "lib"];
|
|
808
831
|
for (const candidate of candidates) {
|
|
809
|
-
const fullPath =
|
|
832
|
+
const fullPath = join7(projectPath, candidate);
|
|
810
833
|
try {
|
|
811
834
|
const stats = statSync2(fullPath);
|
|
812
835
|
if (stats.isDirectory()) {
|
|
@@ -878,7 +901,7 @@ function resolveEntitiesDir(cwd, config) {
|
|
|
878
901
|
function countEntityYamls(entitiesDir) {
|
|
879
902
|
if (!entitiesDir || !fs.existsSync(entitiesDir)) return 0;
|
|
880
903
|
try {
|
|
881
|
-
return
|
|
904
|
+
return findYamlFiles(entitiesDir).length;
|
|
882
905
|
} catch {
|
|
883
906
|
return 0;
|
|
884
907
|
}
|
|
@@ -986,13 +1009,13 @@ function renderPane(pane) {
|
|
|
986
1009
|
}
|
|
987
1010
|
|
|
988
1011
|
// src/cli/ui/hints.ts
|
|
989
|
-
function renderHints(
|
|
1012
|
+
function renderHints(hints10) {
|
|
990
1013
|
if (isJsonMode()) return;
|
|
991
|
-
if (
|
|
1014
|
+
if (hints10.length === 0) return;
|
|
992
1015
|
console.log("");
|
|
993
1016
|
console.log(theme.muted(" Next:"));
|
|
994
|
-
const maxCmd = Math.max(...
|
|
995
|
-
for (const h of
|
|
1017
|
+
const maxCmd = Math.max(...hints10.map((h) => h.command.length));
|
|
1018
|
+
for (const h of hints10) {
|
|
996
1019
|
const pad = " ".repeat(maxCmd - h.command.length + 2);
|
|
997
1020
|
console.log(` ${theme.system(h.command)}${pad}${theme.muted(h.description)}`);
|
|
998
1021
|
}
|
|
@@ -1017,13 +1040,13 @@ function buildNounSummaryCommand(noun) {
|
|
|
1017
1040
|
json: this.json,
|
|
1018
1041
|
verbose: this.verbose
|
|
1019
1042
|
});
|
|
1020
|
-
const [pane,
|
|
1043
|
+
const [pane, hints10] = await Promise.all([noun.summary(ctx), noun.hints(ctx)]);
|
|
1021
1044
|
if (isJsonMode()) {
|
|
1022
|
-
printJson({ noun: noun.name, summary: pane, hints:
|
|
1045
|
+
printJson({ noun: noun.name, summary: pane, hints: hints10 });
|
|
1023
1046
|
return 0;
|
|
1024
1047
|
}
|
|
1025
1048
|
renderPane(pane);
|
|
1026
|
-
renderHints(
|
|
1049
|
+
renderHints(hints10);
|
|
1027
1050
|
return 0;
|
|
1028
1051
|
}
|
|
1029
1052
|
}
|
|
@@ -2935,8 +2958,8 @@ function loadEntityFromYaml(filePath) {
|
|
|
2935
2958
|
}
|
|
2936
2959
|
function formatZodErrors(error) {
|
|
2937
2960
|
return error.errors.map((err) => {
|
|
2938
|
-
const
|
|
2939
|
-
const location =
|
|
2961
|
+
const path34 = err.path.join(".");
|
|
2962
|
+
const location = path34 ? `at '${path34}'` : "at root";
|
|
2940
2963
|
return `${err.message} ${location}`;
|
|
2941
2964
|
});
|
|
2942
2965
|
}
|
|
@@ -3091,8 +3114,7 @@ function detectYamlType(filePath) {
|
|
|
3091
3114
|
}
|
|
3092
3115
|
|
|
3093
3116
|
// src/parser/load-entities.ts
|
|
3094
|
-
import {
|
|
3095
|
-
import { join as join7, resolve } from "path";
|
|
3117
|
+
import { resolve as resolve2 } from "path";
|
|
3096
3118
|
function transformToEntity(result) {
|
|
3097
3119
|
const { definition, filePath } = result;
|
|
3098
3120
|
const queries = definition.queries?.filter((q) => "by" in q).map((q) => ({
|
|
@@ -3224,10 +3246,10 @@ function loadErrorToIssue(error) {
|
|
|
3224
3246
|
function loadEntities(entitiesDir) {
|
|
3225
3247
|
const entities = [];
|
|
3226
3248
|
const issues = [];
|
|
3227
|
-
const resolvedDir =
|
|
3249
|
+
const resolvedDir = resolve2(entitiesDir);
|
|
3228
3250
|
let files;
|
|
3229
3251
|
try {
|
|
3230
|
-
files =
|
|
3252
|
+
files = findYamlFiles(resolvedDir);
|
|
3231
3253
|
} catch (err) {
|
|
3232
3254
|
issues.push({
|
|
3233
3255
|
severity: "error",
|
|
@@ -3272,19 +3294,19 @@ function resolveReferences(entities) {
|
|
|
3272
3294
|
entityMap.set(entity.name, entity);
|
|
3273
3295
|
}
|
|
3274
3296
|
for (const entity of entities) {
|
|
3275
|
-
for (const [relName,
|
|
3276
|
-
const targetEntity = entityMap.get(
|
|
3297
|
+
for (const [relName, rel2] of entity.relationships) {
|
|
3298
|
+
const targetEntity = entityMap.get(rel2.target);
|
|
3277
3299
|
if (targetEntity) {
|
|
3278
|
-
|
|
3300
|
+
rel2.resolved = true;
|
|
3279
3301
|
} else {
|
|
3280
3302
|
issues.push({
|
|
3281
3303
|
severity: "error",
|
|
3282
3304
|
type: "missing_target",
|
|
3283
3305
|
entity: entity.name,
|
|
3284
3306
|
field: relName,
|
|
3285
|
-
message: `Relationship '${relName}' references unknown entity '${
|
|
3307
|
+
message: `Relationship '${relName}' references unknown entity '${rel2.target}'`,
|
|
3286
3308
|
path: entity.sourcePath,
|
|
3287
|
-
suggestion: `Define entity '${
|
|
3309
|
+
suggestion: `Define entity '${rel2.target}' or fix the target name`
|
|
3288
3310
|
});
|
|
3289
3311
|
}
|
|
3290
3312
|
}
|
|
@@ -3397,10 +3419,10 @@ function resolveTypeDirections(types) {
|
|
|
3397
3419
|
function loadRelationships(relationshipsDir) {
|
|
3398
3420
|
const relationships = [];
|
|
3399
3421
|
const issues = [];
|
|
3400
|
-
const resolvedDir =
|
|
3422
|
+
const resolvedDir = resolve2(relationshipsDir);
|
|
3401
3423
|
let files;
|
|
3402
3424
|
try {
|
|
3403
|
-
files =
|
|
3425
|
+
files = findYamlFiles(resolvedDir);
|
|
3404
3426
|
} catch {
|
|
3405
3427
|
return { relationships, issues };
|
|
3406
3428
|
}
|
|
@@ -3482,14 +3504,14 @@ function buildDomainGraph(entities, relationshipDefinitions = []) {
|
|
|
3482
3504
|
relDefMap.set(relDef.name, relDef);
|
|
3483
3505
|
}
|
|
3484
3506
|
for (const entity of entities) {
|
|
3485
|
-
for (const [relName,
|
|
3486
|
-
if (!
|
|
3487
|
-
const reverseEdge = hasReverseEdge(edges, entity.name,
|
|
3507
|
+
for (const [relName, rel2] of entity.relationships) {
|
|
3508
|
+
if (!rel2.resolved) continue;
|
|
3509
|
+
const reverseEdge = hasReverseEdge(edges, entity.name, rel2.target);
|
|
3488
3510
|
const edge = {
|
|
3489
3511
|
from: entity.name,
|
|
3490
|
-
to:
|
|
3491
|
-
relationship:
|
|
3492
|
-
cardinality: inferCardinality(
|
|
3512
|
+
to: rel2.target,
|
|
3513
|
+
relationship: rel2,
|
|
3514
|
+
cardinality: inferCardinality(rel2.type),
|
|
3493
3515
|
bidirectional: reverseEdge !== void 0
|
|
3494
3516
|
};
|
|
3495
3517
|
if (reverseEdge) {
|
|
@@ -3534,19 +3556,19 @@ function findCircularDependencies(graph) {
|
|
|
3534
3556
|
const cycles = [];
|
|
3535
3557
|
const visited = /* @__PURE__ */ new Set();
|
|
3536
3558
|
const recursionStack = /* @__PURE__ */ new Set();
|
|
3537
|
-
function dfs(node,
|
|
3559
|
+
function dfs(node, path34) {
|
|
3538
3560
|
visited.add(node);
|
|
3539
3561
|
recursionStack.add(node);
|
|
3540
3562
|
const outgoingEdges = graph.edges.filter((e) => e.from === node);
|
|
3541
3563
|
for (const edge of outgoingEdges) {
|
|
3542
3564
|
if (!visited.has(edge.to)) {
|
|
3543
|
-
dfs(edge.to, [...
|
|
3565
|
+
dfs(edge.to, [...path34, edge.to]);
|
|
3544
3566
|
} else if (recursionStack.has(edge.to)) {
|
|
3545
|
-
const cycleStart =
|
|
3567
|
+
const cycleStart = path34.indexOf(edge.to);
|
|
3546
3568
|
if (cycleStart !== -1) {
|
|
3547
|
-
cycles.push([...
|
|
3569
|
+
cycles.push([...path34.slice(cycleStart), edge.to]);
|
|
3548
3570
|
} else {
|
|
3549
|
-
cycles.push([...
|
|
3571
|
+
cycles.push([...path34, edge.to]);
|
|
3550
3572
|
}
|
|
3551
3573
|
}
|
|
3552
3574
|
}
|
|
@@ -3847,32 +3869,32 @@ function checkEntityConsistency(entity) {
|
|
|
3847
3869
|
}
|
|
3848
3870
|
function checkRelationshipConsistency(entity, graph) {
|
|
3849
3871
|
const issues = [];
|
|
3850
|
-
for (const [relName,
|
|
3851
|
-
if (
|
|
3852
|
-
const fkField = entity.fields.get(
|
|
3872
|
+
for (const [relName, rel2] of entity.relationships) {
|
|
3873
|
+
if (rel2.type === "belongs_to") {
|
|
3874
|
+
const fkField = entity.fields.get(rel2.foreignKey);
|
|
3853
3875
|
if (!fkField) {
|
|
3854
3876
|
issues.push({
|
|
3855
3877
|
severity: "warning",
|
|
3856
3878
|
type: "missing_fk_field",
|
|
3857
3879
|
entity: entity.name,
|
|
3858
3880
|
field: relName,
|
|
3859
|
-
message: `Relationship "${relName}" references foreign key "${
|
|
3860
|
-
suggestion: `Add field "${
|
|
3881
|
+
message: `Relationship "${relName}" references foreign key "${rel2.foreignKey}" but field doesn't exist`,
|
|
3882
|
+
suggestion: `Add field "${rel2.foreignKey}" with foreign_key reference`
|
|
3861
3883
|
});
|
|
3862
3884
|
}
|
|
3863
3885
|
}
|
|
3864
|
-
if (
|
|
3865
|
-
const targetEntity = graph.entities.get(
|
|
3886
|
+
if (rel2.type === "has_many" || rel2.type === "has_one") {
|
|
3887
|
+
const targetEntity = graph.entities.get(rel2.target);
|
|
3866
3888
|
if (targetEntity) {
|
|
3867
|
-
const targetFkField = targetEntity.fields.get(
|
|
3889
|
+
const targetFkField = targetEntity.fields.get(rel2.foreignKey);
|
|
3868
3890
|
if (!targetFkField) {
|
|
3869
3891
|
issues.push({
|
|
3870
3892
|
severity: "warning",
|
|
3871
3893
|
type: "missing_target_fk",
|
|
3872
3894
|
entity: entity.name,
|
|
3873
3895
|
field: relName,
|
|
3874
|
-
message: `Relationship "${relName}" expects foreign key "${
|
|
3875
|
-
suggestion: `Add field "${
|
|
3896
|
+
message: `Relationship "${relName}" expects foreign key "${rel2.foreignKey}" on "${rel2.target}" but field doesn't exist`,
|
|
3897
|
+
suggestion: `Add field "${rel2.foreignKey}" to "${rel2.target}" entity`
|
|
3876
3898
|
});
|
|
3877
3899
|
}
|
|
3878
3900
|
}
|
|
@@ -3989,7 +4011,7 @@ function checkMissingInverses(graph) {
|
|
|
3989
4011
|
const targetEntity = graph.entities.get(to);
|
|
3990
4012
|
if (!targetEntity) continue;
|
|
3991
4013
|
const hasInverse = Array.from(targetEntity.relationships.values()).some(
|
|
3992
|
-
(
|
|
4014
|
+
(rel2) => rel2.target === from
|
|
3993
4015
|
);
|
|
3994
4016
|
if (!hasInverse && relationship.type !== "belongs_to") {
|
|
3995
4017
|
issues.push({
|
|
@@ -4009,9 +4031,9 @@ function getAvailableFieldNames(entity) {
|
|
|
4009
4031
|
const behaviorFields = resolveBehaviorFields(entity.behaviors);
|
|
4010
4032
|
const behaviorFieldNames = behaviorFields.map((f) => f.name);
|
|
4011
4033
|
const belongsToFkNames = [];
|
|
4012
|
-
for (const
|
|
4013
|
-
if (
|
|
4014
|
-
belongsToFkNames.push(
|
|
4034
|
+
for (const rel2 of entity.relationships.values()) {
|
|
4035
|
+
if (rel2.type === "belongs_to" && rel2.foreignKey) {
|
|
4036
|
+
belongsToFkNames.push(rel2.foreignKey);
|
|
4015
4037
|
}
|
|
4016
4038
|
}
|
|
4017
4039
|
return [.../* @__PURE__ */ new Set([...entityFieldNames, ...behaviorFieldNames, ...belongsToFkNames])];
|
|
@@ -4116,8 +4138,8 @@ function computeStatistics(graph) {
|
|
|
4116
4138
|
for (const field of entity.fields.values()) {
|
|
4117
4139
|
fieldsByType[field.type] = (fieldsByType[field.type] ?? 0) + 1;
|
|
4118
4140
|
}
|
|
4119
|
-
for (const
|
|
4120
|
-
relationshipsByType[
|
|
4141
|
+
for (const rel2 of entity.relationships.values()) {
|
|
4142
|
+
relationshipsByType[rel2.type] = (relationshipsByType[rel2.type] ?? 0) + 1;
|
|
4121
4143
|
}
|
|
4122
4144
|
}
|
|
4123
4145
|
return {
|
|
@@ -4143,8 +4165,8 @@ function suggestTransitiveRelationships(graph, options) {
|
|
|
4143
4165
|
for (const [entityName, entity] of graph.entities) {
|
|
4144
4166
|
if (shouldExcludeEntity(entityName, opts)) continue;
|
|
4145
4167
|
const paths = findTransitivePaths(graph, entityName, opts);
|
|
4146
|
-
for (const
|
|
4147
|
-
suggestions.push(createSuggestion(
|
|
4168
|
+
for (const path34 of paths) {
|
|
4169
|
+
suggestions.push(createSuggestion(path34));
|
|
4148
4170
|
}
|
|
4149
4171
|
}
|
|
4150
4172
|
return suggestions;
|
|
@@ -4175,22 +4197,22 @@ function findTransitivePaths(graph, sourceEntity, opts) {
|
|
|
4175
4197
|
while (queue.length > 0) {
|
|
4176
4198
|
const current = queue.shift();
|
|
4177
4199
|
if (!current) continue;
|
|
4178
|
-
const { entity, depth, path:
|
|
4200
|
+
const { entity, depth, path: path34, visited } = current;
|
|
4179
4201
|
if (depth >= opts.maxDepth) continue;
|
|
4180
4202
|
const currentEntity = graph.entities.get(entity);
|
|
4181
4203
|
if (!currentEntity) continue;
|
|
4182
|
-
for (const [relName,
|
|
4183
|
-
if (
|
|
4184
|
-
if (
|
|
4185
|
-
const target =
|
|
4204
|
+
for (const [relName, rel2] of currentEntity.relationships) {
|
|
4205
|
+
if (rel2.through) continue;
|
|
4206
|
+
if (rel2.type !== "has_many" && rel2.type !== "has_one") continue;
|
|
4207
|
+
const target = rel2.target;
|
|
4186
4208
|
if (shouldExcludeEntity(target, opts)) continue;
|
|
4187
4209
|
if (visited.has(target)) continue;
|
|
4188
4210
|
const newPath = [
|
|
4189
|
-
...
|
|
4211
|
+
...path34,
|
|
4190
4212
|
{
|
|
4191
4213
|
via: entity,
|
|
4192
4214
|
relationship: relName,
|
|
4193
|
-
foreignKey:
|
|
4215
|
+
foreignKey: rel2.foreignKey
|
|
4194
4216
|
}
|
|
4195
4217
|
];
|
|
4196
4218
|
if (depth >= 1) {
|
|
@@ -4218,8 +4240,8 @@ function findTransitivePaths(graph, sourceEntity, opts) {
|
|
|
4218
4240
|
return paths;
|
|
4219
4241
|
}
|
|
4220
4242
|
function hasDirectRelationship(sourceEntity, targetName) {
|
|
4221
|
-
for (const
|
|
4222
|
-
if (
|
|
4243
|
+
for (const rel2 of sourceEntity.relationships.values()) {
|
|
4244
|
+
if (rel2.target === targetName && !rel2.through) {
|
|
4223
4245
|
return true;
|
|
4224
4246
|
}
|
|
4225
4247
|
}
|
|
@@ -4254,15 +4276,15 @@ function generateYamlSnippet(name, target, throughPath) {
|
|
|
4254
4276
|
target: ${target}
|
|
4255
4277
|
through: "${throughPath}"`;
|
|
4256
4278
|
}
|
|
4257
|
-
function createSuggestion(
|
|
4258
|
-
const pathDescription = [
|
|
4279
|
+
function createSuggestion(path34) {
|
|
4280
|
+
const pathDescription = [path34.source, ...path34.hops.map((h) => h.via), path34.target].join(" -> ");
|
|
4259
4281
|
return {
|
|
4260
4282
|
severity: "info",
|
|
4261
4283
|
type: "transitive_suggestion",
|
|
4262
|
-
entity:
|
|
4284
|
+
entity: path34.source,
|
|
4263
4285
|
message: `Potential transitive relationship: ${pathDescription}`,
|
|
4264
|
-
suggestion: `Add "${
|
|
4265
|
-
path:
|
|
4286
|
+
suggestion: `Add "${path34.suggestedName}" relationship via "${path34.throughPath}"`,
|
|
4287
|
+
path: path34
|
|
4266
4288
|
};
|
|
4267
4289
|
}
|
|
4268
4290
|
|
|
@@ -4354,13 +4376,13 @@ function toManifestEntity(entity) {
|
|
|
4354
4376
|
};
|
|
4355
4377
|
}
|
|
4356
4378
|
const relationships = {};
|
|
4357
|
-
for (const [name,
|
|
4379
|
+
for (const [name, rel2] of entity.relationships) {
|
|
4358
4380
|
relationships[name] = {
|
|
4359
|
-
type:
|
|
4360
|
-
target:
|
|
4361
|
-
foreignKey:
|
|
4362
|
-
through:
|
|
4363
|
-
inverse:
|
|
4381
|
+
type: rel2.type,
|
|
4382
|
+
target: rel2.target,
|
|
4383
|
+
foreignKey: rel2.foreignKey,
|
|
4384
|
+
through: rel2.through,
|
|
4385
|
+
inverse: rel2.inverse
|
|
4364
4386
|
};
|
|
4365
4387
|
}
|
|
4366
4388
|
return {
|
|
@@ -5332,8 +5354,8 @@ function formatEntitySection(entity) {
|
|
|
5332
5354
|
lines.push("");
|
|
5333
5355
|
lines.push("| Name | Type | Target | Foreign Key |");
|
|
5334
5356
|
lines.push("|------|------|--------|-------------|");
|
|
5335
|
-
for (const [name,
|
|
5336
|
-
lines.push(`| ${name} | ${
|
|
5357
|
+
for (const [name, rel2] of entity.relationships) {
|
|
5358
|
+
lines.push(`| ${name} | ${rel2.type} | ${rel2.target} | ${rel2.foreignKey} |`);
|
|
5337
5359
|
}
|
|
5338
5360
|
lines.push("");
|
|
5339
5361
|
}
|
|
@@ -5665,7 +5687,7 @@ function toKebabCase(input) {
|
|
|
5665
5687
|
}
|
|
5666
5688
|
function listEntityYamls(entitiesDir) {
|
|
5667
5689
|
if (!fs2.existsSync(entitiesDir)) return [];
|
|
5668
|
-
return
|
|
5690
|
+
return findYamlFiles(entitiesDir);
|
|
5669
5691
|
}
|
|
5670
5692
|
function collectEntities(entitiesDir) {
|
|
5671
5693
|
const files = listEntityYamls(entitiesDir);
|
|
@@ -5684,7 +5706,9 @@ function collectEntities(entitiesDir) {
|
|
|
5684
5706
|
}
|
|
5685
5707
|
function listRelationshipYamls(relationshipsDir) {
|
|
5686
5708
|
if (!fs2.existsSync(relationshipsDir)) return [];
|
|
5687
|
-
return
|
|
5709
|
+
return findYamlFiles(relationshipsDir).filter(
|
|
5710
|
+
(full) => detectYamlType(full) === "relationship"
|
|
5711
|
+
);
|
|
5688
5712
|
}
|
|
5689
5713
|
function collectRelationships(relationshipsDir) {
|
|
5690
5714
|
const files = listRelationshipYamls(relationshipsDir);
|
|
@@ -5692,9 +5716,9 @@ function collectRelationships(relationshipsDir) {
|
|
|
5692
5716
|
for (const file of files) {
|
|
5693
5717
|
const result = loadRelationshipFromYaml(file);
|
|
5694
5718
|
if (!result.success) continue;
|
|
5695
|
-
const
|
|
5696
|
-
const name =
|
|
5697
|
-
const plural =
|
|
5719
|
+
const rel2 = result.definition.relationship;
|
|
5720
|
+
const name = rel2.name;
|
|
5721
|
+
const plural = rel2.table ?? pluralize(name);
|
|
5698
5722
|
junctions.push({ name, plural });
|
|
5699
5723
|
}
|
|
5700
5724
|
junctions.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -5702,7 +5726,9 @@ function collectRelationships(relationshipsDir) {
|
|
|
5702
5726
|
}
|
|
5703
5727
|
function listJunctionYamls(junctionsDir) {
|
|
5704
5728
|
if (!fs2.existsSync(junctionsDir)) return [];
|
|
5705
|
-
return
|
|
5729
|
+
return findYamlFiles(junctionsDir).filter(
|
|
5730
|
+
(full) => detectYamlType(full) === "junction"
|
|
5731
|
+
);
|
|
5706
5732
|
}
|
|
5707
5733
|
function deriveJunctionName(def) {
|
|
5708
5734
|
return def.name ?? `${def.between[0]}_${def.between[1]}`;
|
|
@@ -5747,9 +5773,9 @@ var HEADER = `// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.
|
|
|
5747
5773
|
`;
|
|
5748
5774
|
function relativeImport(fromFile, toFile) {
|
|
5749
5775
|
const fromDir = path4.posix.dirname(fromFile);
|
|
5750
|
-
let
|
|
5751
|
-
if (!
|
|
5752
|
-
return
|
|
5776
|
+
let rel2 = path4.posix.relative(fromDir, toFile);
|
|
5777
|
+
if (!rel2.startsWith(".")) rel2 = `./${rel2}`;
|
|
5778
|
+
return rel2.replace(/\.ts$/, "");
|
|
5753
5779
|
}
|
|
5754
5780
|
function buildModulesBarrel(entities, barrelFile, architecture, backendSrc = "app/backend/src") {
|
|
5755
5781
|
const imports = [];
|
|
@@ -5836,8 +5862,8 @@ function resolveArchitecture(ctx) {
|
|
|
5836
5862
|
}
|
|
5837
5863
|
function resolveGeneratedDir(ctx) {
|
|
5838
5864
|
const fromConfig = ctx.config?.paths?.generated;
|
|
5839
|
-
const
|
|
5840
|
-
return path4.resolve(ctx.cwd,
|
|
5865
|
+
const rel2 = typeof fromConfig === "string" && fromConfig.length > 0 ? fromConfig : "src/generated";
|
|
5866
|
+
return path4.resolve(ctx.cwd, rel2);
|
|
5841
5867
|
}
|
|
5842
5868
|
function resolveBackendSrc(ctx) {
|
|
5843
5869
|
const fromConfig = ctx.config?.paths?.backend_src;
|
|
@@ -5852,7 +5878,7 @@ var HEADER2 = `// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.
|
|
|
5852
5878
|
`;
|
|
5853
5879
|
function collectScopeableNames(entitiesDir) {
|
|
5854
5880
|
if (!fs3.existsSync(entitiesDir)) return [];
|
|
5855
|
-
const files =
|
|
5881
|
+
const files = findYamlFiles(entitiesDir);
|
|
5856
5882
|
const names = [];
|
|
5857
5883
|
for (const file of files) {
|
|
5858
5884
|
const result = loadEntityFromYaml(file);
|
|
@@ -7042,8 +7068,7 @@ import fs8 from "fs";
|
|
|
7042
7068
|
import path11 from "path";
|
|
7043
7069
|
|
|
7044
7070
|
// src/parser/load-events.ts
|
|
7045
|
-
import {
|
|
7046
|
-
import { basename as basename2, join as join10, resolve as resolve2 } from "path";
|
|
7071
|
+
import { basename as basename2, resolve as resolve3 } from "path";
|
|
7047
7072
|
function loadErrorToIssue2(error) {
|
|
7048
7073
|
const issues = [];
|
|
7049
7074
|
issues.push({
|
|
@@ -7073,10 +7098,10 @@ function stripYamlExt(file) {
|
|
|
7073
7098
|
function loadEvents(eventsDir, entityNames) {
|
|
7074
7099
|
const events = [];
|
|
7075
7100
|
const issues = [];
|
|
7076
|
-
const resolvedDir =
|
|
7101
|
+
const resolvedDir = resolve3(eventsDir);
|
|
7077
7102
|
let files;
|
|
7078
7103
|
try {
|
|
7079
|
-
files =
|
|
7104
|
+
files = findYamlFiles(resolvedDir);
|
|
7080
7105
|
} catch {
|
|
7081
7106
|
issues.push({
|
|
7082
7107
|
severity: "warning",
|
|
@@ -7227,7 +7252,7 @@ function collectEntityEvents(entitiesDir) {
|
|
|
7227
7252
|
if (!fs8.existsSync(entitiesDir)) {
|
|
7228
7253
|
return { events, issues };
|
|
7229
7254
|
}
|
|
7230
|
-
const files =
|
|
7255
|
+
const files = findYamlFiles(entitiesDir);
|
|
7231
7256
|
for (const filePath of files) {
|
|
7232
7257
|
const result = loadEntityFromYaml(filePath);
|
|
7233
7258
|
if (!result.success) continue;
|
|
@@ -7269,7 +7294,7 @@ function collectMergedEvents(opts) {
|
|
|
7269
7294
|
const { entitiesDir, eventsDir } = opts;
|
|
7270
7295
|
const entityNames = [];
|
|
7271
7296
|
if (fs8.existsSync(entitiesDir)) {
|
|
7272
|
-
const entityFiles =
|
|
7297
|
+
const entityFiles = findYamlFiles(entitiesDir);
|
|
7273
7298
|
for (const f of entityFiles) {
|
|
7274
7299
|
const result = loadEntityFromYaml(f);
|
|
7275
7300
|
if (result.success) entityNames.push(result.definition.entity.name);
|
|
@@ -7752,7 +7777,7 @@ function printInfo(msg) {
|
|
|
7752
7777
|
// src/cli/commands/entity.ts
|
|
7753
7778
|
function listEntityYamls2(dir) {
|
|
7754
7779
|
if (!fs9.existsSync(dir)) return [];
|
|
7755
|
-
return
|
|
7780
|
+
return findYamlFiles(dir);
|
|
7756
7781
|
}
|
|
7757
7782
|
function summarizePatternLabel(entity) {
|
|
7758
7783
|
if (typeof entity.pattern === "string" && entity.pattern.length > 0) {
|
|
@@ -8788,13 +8813,6 @@ function readIfExists(p) {
|
|
|
8788
8813
|
return null;
|
|
8789
8814
|
}
|
|
8790
8815
|
}
|
|
8791
|
-
function writeFile(target, content) {
|
|
8792
|
-
const existing = readIfExists(target);
|
|
8793
|
-
if (existing === content) return "unchanged";
|
|
8794
|
-
fs10.mkdirSync(path21.dirname(target), { recursive: true });
|
|
8795
|
-
fs10.writeFileSync(target, content);
|
|
8796
|
-
return existing === null ? "written" : "updated";
|
|
8797
|
-
}
|
|
8798
8816
|
function extractRelativeImports(source) {
|
|
8799
8817
|
const out = [];
|
|
8800
8818
|
const re = /(?:import|export)\s+(?:[^'"`;]*?\s+from\s+)?['"`](\.{1,2}\/[^'"`]+)['"`]/g;
|
|
@@ -8813,7 +8831,7 @@ function resolveSourceImport(sourceFile, specifier) {
|
|
|
8813
8831
|
return null;
|
|
8814
8832
|
}
|
|
8815
8833
|
async function copyRuntime(opts) {
|
|
8816
|
-
const { sourceDir, targetDir, filter, resolveDeps, dryRun } = opts;
|
|
8834
|
+
const { sourceDir, targetDir, filter, resolveDeps, dryRun, onlyExisting } = opts;
|
|
8817
8835
|
if (!fs10.existsSync(sourceDir) || !fs10.statSync(sourceDir).isDirectory()) {
|
|
8818
8836
|
throw new Error(`runtime source directory not found: ${sourceDir}`);
|
|
8819
8837
|
}
|
|
@@ -8838,9 +8856,9 @@ async function copyRuntime(opts) {
|
|
|
8838
8856
|
}
|
|
8839
8857
|
if (!stat.isFile()) continue;
|
|
8840
8858
|
if (!entry.endsWith(".ts") && !entry.endsWith(".tsx")) continue;
|
|
8841
|
-
const
|
|
8842
|
-
if (filter && !filter(
|
|
8843
|
-
queue.push({ src, dest: path21.join(targetDir,
|
|
8859
|
+
const rel2 = path21.relative(sourceDir, src);
|
|
8860
|
+
if (filter && !filter(rel2) && !filter(entry)) continue;
|
|
8861
|
+
queue.push({ src, dest: path21.join(targetDir, rel2), isDep: false });
|
|
8844
8862
|
}
|
|
8845
8863
|
}
|
|
8846
8864
|
walk(sourceDir);
|
|
@@ -8849,14 +8867,20 @@ async function copyRuntime(opts) {
|
|
|
8849
8867
|
const next = queue.shift();
|
|
8850
8868
|
if (visited.has(next.src)) continue;
|
|
8851
8869
|
visited.add(next.src);
|
|
8870
|
+
if (onlyExisting && !fs10.existsSync(next.dest)) {
|
|
8871
|
+
continue;
|
|
8872
|
+
}
|
|
8852
8873
|
const content = fs10.readFileSync(next.src, "utf-8");
|
|
8853
8874
|
result.planned.push(next.dest);
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8875
|
+
const existing = readIfExists(next.dest);
|
|
8876
|
+
const status = existing === content ? "unchanged" : existing === null ? "written" : "updated";
|
|
8877
|
+
if (status === "written") result.written.push(next.dest);
|
|
8878
|
+
else if (status === "updated") result.updated.push(next.dest);
|
|
8879
|
+
else result.unchanged.push(next.dest);
|
|
8880
|
+
if (next.isDep) result.dependenciesCopied.push(next.dest);
|
|
8881
|
+
if (!dryRun && status !== "unchanged") {
|
|
8882
|
+
fs10.mkdirSync(path21.dirname(next.dest), { recursive: true });
|
|
8883
|
+
fs10.writeFileSync(next.dest, content);
|
|
8860
8884
|
}
|
|
8861
8885
|
if (resolveDeps) {
|
|
8862
8886
|
for (const spec of extractRelativeImports(content)) {
|
|
@@ -8909,11 +8933,11 @@ async function summary2(ctx) {
|
|
|
8909
8933
|
}
|
|
8910
8934
|
body.push(theme.muted("Installed:"));
|
|
8911
8935
|
for (const i of installed) {
|
|
8912
|
-
const
|
|
8936
|
+
const rel2 = path22.relative(ctx.cwd, i.path) || i.path;
|
|
8913
8937
|
body.push(
|
|
8914
8938
|
` ${theme.success(icons.check)} ${i.name.padEnd(10)} ${theme.muted(
|
|
8915
8939
|
`${i.backend} backend`
|
|
8916
|
-
)} ${theme.muted(
|
|
8940
|
+
)} ${theme.muted(rel2)}`
|
|
8917
8941
|
);
|
|
8918
8942
|
}
|
|
8919
8943
|
if (missing.length > 0) {
|
|
@@ -9916,9 +9940,9 @@ function buildAuthImportRewriter(subsystemsRoot) {
|
|
|
9916
9940
|
return content;
|
|
9917
9941
|
}
|
|
9918
9942
|
AUTH_BARE_IMPORT_RE.lastIndex = 0;
|
|
9919
|
-
let
|
|
9920
|
-
if (!
|
|
9921
|
-
const relPosix =
|
|
9943
|
+
let rel2 = path22.relative(path22.dirname(destPath), authRoot);
|
|
9944
|
+
if (!rel2.startsWith(".")) rel2 = `./${rel2}`;
|
|
9945
|
+
const relPosix = rel2.split(path22.sep).join("/");
|
|
9922
9946
|
return content.replace(
|
|
9923
9947
|
AUTH_BARE_IMPORT_RE,
|
|
9924
9948
|
(_match, quote) => `${quote}${relPosix}${quote}`
|
|
@@ -10100,10 +10124,10 @@ var subsystemNoun = {
|
|
|
10100
10124
|
var subsystem_default = subsystemNoun;
|
|
10101
10125
|
|
|
10102
10126
|
// src/cli/commands/project.ts
|
|
10103
|
-
import
|
|
10104
|
-
import
|
|
10127
|
+
import fs17 from "fs";
|
|
10128
|
+
import path28 from "path";
|
|
10105
10129
|
import readline from "readline";
|
|
10106
|
-
import { Command as
|
|
10130
|
+
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
10107
10131
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
10108
10132
|
|
|
10109
10133
|
// src/cli/shared/init-scaffold.ts
|
|
@@ -10746,8 +10770,8 @@ async function buildInitPlan(ctx, options) {
|
|
|
10746
10770
|
{
|
|
10747
10771
|
const entitiesDir = path23.join(cwd, "entities");
|
|
10748
10772
|
const examplePath = path23.join(entitiesDir, "example.yaml");
|
|
10749
|
-
const hasOtherYamls = fs12.existsSync(entitiesDir) &&
|
|
10750
|
-
(f) =>
|
|
10773
|
+
const hasOtherYamls = fs12.existsSync(entitiesDir) && findYamlFiles(entitiesDir).some(
|
|
10774
|
+
(f) => path23.basename(f) !== "example.yaml"
|
|
10751
10775
|
);
|
|
10752
10776
|
if (fs12.existsSync(examplePath)) {
|
|
10753
10777
|
entries.push({
|
|
@@ -11058,8 +11082,8 @@ function runtimeRoot3() {
|
|
|
11058
11082
|
if (fs13.existsSync(topLevel)) return topLevel;
|
|
11059
11083
|
return path24.join(pkgRoot, "dist", "runtime");
|
|
11060
11084
|
}
|
|
11061
|
-
function loadRuntimeFile2(
|
|
11062
|
-
return fs13.readFileSync(path24.join(runtimeRoot3(),
|
|
11085
|
+
function loadRuntimeFile2(rel2) {
|
|
11086
|
+
return fs13.readFileSync(path24.join(runtimeRoot3(), rel2), "utf-8");
|
|
11063
11087
|
}
|
|
11064
11088
|
function resolveProjectRoot(startDir) {
|
|
11065
11089
|
let dir = path24.resolve(startDir);
|
|
@@ -11342,8 +11366,457 @@ ${CONSUMER_SETUP_POINTER}
|
|
|
11342
11366
|
}
|
|
11343
11367
|
};
|
|
11344
11368
|
|
|
11345
|
-
// src/cli/commands/project.ts
|
|
11369
|
+
// src/cli/commands/project-update.ts
|
|
11370
|
+
import fs16 from "fs";
|
|
11371
|
+
import path27 from "path";
|
|
11372
|
+
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
11373
|
+
|
|
11374
|
+
// src/cli/commands/skills.ts
|
|
11375
|
+
import fs15 from "fs";
|
|
11376
|
+
import path26 from "path";
|
|
11377
|
+
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
11378
|
+
|
|
11379
|
+
// src/cli/shared/tree-copier.ts
|
|
11380
|
+
import fs14 from "fs";
|
|
11381
|
+
import path25 from "path";
|
|
11382
|
+
var TEXT_EXTENSIONS = [".ts", ".tsx", ".md", ".mdx", ".yaml", ".yml", ".json"];
|
|
11383
|
+
function isTextFile(name) {
|
|
11384
|
+
return TEXT_EXTENSIONS.some((ext) => name.endsWith(ext));
|
|
11385
|
+
}
|
|
11386
|
+
function copyTreeWithReport(opts) {
|
|
11387
|
+
const { srcDir, destDir, dryRun = false, transform, include } = opts;
|
|
11388
|
+
const report = {
|
|
11389
|
+
entries: [],
|
|
11390
|
+
created: [],
|
|
11391
|
+
updated: [],
|
|
11392
|
+
unchanged: []
|
|
11393
|
+
};
|
|
11394
|
+
if (!fs14.existsSync(srcDir) || !fs14.statSync(srcDir).isDirectory()) {
|
|
11395
|
+
throw new Error(`tree-copier source directory not found: ${srcDir}`);
|
|
11396
|
+
}
|
|
11397
|
+
const walk = (relDir) => {
|
|
11398
|
+
const absSrcDir = path25.join(srcDir, relDir);
|
|
11399
|
+
for (const entry of fs14.readdirSync(absSrcDir, { withFileTypes: true })) {
|
|
11400
|
+
const relPath2 = relDir ? path25.posix.join(relDir, entry.name) : entry.name;
|
|
11401
|
+
const absSrc = path25.join(srcDir, relPath2);
|
|
11402
|
+
if (entry.isDirectory()) {
|
|
11403
|
+
walk(relPath2);
|
|
11404
|
+
continue;
|
|
11405
|
+
}
|
|
11406
|
+
if (!entry.isFile()) continue;
|
|
11407
|
+
if (include && !include(relPath2)) continue;
|
|
11408
|
+
const dest = path25.join(destDir, relPath2);
|
|
11409
|
+
let content = fs14.readFileSync(absSrc, "utf-8");
|
|
11410
|
+
if (transform && isTextFile(entry.name)) {
|
|
11411
|
+
content = transform(content, dest);
|
|
11412
|
+
}
|
|
11413
|
+
const existing = fs14.existsSync(dest) ? fs14.readFileSync(dest, "utf-8") : null;
|
|
11414
|
+
let action;
|
|
11415
|
+
if (existing === null) {
|
|
11416
|
+
action = "created";
|
|
11417
|
+
} else if (existing === content) {
|
|
11418
|
+
action = "unchanged";
|
|
11419
|
+
} else {
|
|
11420
|
+
action = "updated";
|
|
11421
|
+
}
|
|
11422
|
+
if (!dryRun && action !== "unchanged") {
|
|
11423
|
+
fs14.mkdirSync(path25.dirname(dest), { recursive: true });
|
|
11424
|
+
fs14.writeFileSync(dest, content, "utf-8");
|
|
11425
|
+
}
|
|
11426
|
+
const record = { relPath: relPath2, dest, action };
|
|
11427
|
+
report.entries.push(record);
|
|
11428
|
+
report[action].push(record);
|
|
11429
|
+
}
|
|
11430
|
+
};
|
|
11431
|
+
walk("");
|
|
11432
|
+
return report;
|
|
11433
|
+
}
|
|
11434
|
+
|
|
11435
|
+
// src/cli/commands/skills.ts
|
|
11436
|
+
function consumerSkillsRoot() {
|
|
11437
|
+
const pkgRoot = path26.resolve(import.meta.dirname, "..", "..", "..");
|
|
11438
|
+
const topLevel = path26.join(pkgRoot, "consumer-skills");
|
|
11439
|
+
if (fs15.existsSync(topLevel)) return topLevel;
|
|
11440
|
+
return path26.join(pkgRoot, "dist", "consumer-skills");
|
|
11441
|
+
}
|
|
11442
|
+
function skillsTargetDir(cwd) {
|
|
11443
|
+
return path26.join(cwd, ".claude", "skills");
|
|
11444
|
+
}
|
|
11445
|
+
function availableSkills() {
|
|
11446
|
+
const root = consumerSkillsRoot();
|
|
11447
|
+
if (!fs15.existsSync(root)) return [];
|
|
11448
|
+
return fs15.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
11449
|
+
}
|
|
11450
|
+
function runSkillsInstall(opts) {
|
|
11451
|
+
const sourceRoot = consumerSkillsRoot();
|
|
11452
|
+
const targetDir = skillsTargetDir(opts.cwd);
|
|
11453
|
+
if (!fs15.existsSync(sourceRoot)) {
|
|
11454
|
+
return {
|
|
11455
|
+
ok: false,
|
|
11456
|
+
sourceRoot,
|
|
11457
|
+
targetDir,
|
|
11458
|
+
error: `consumer skills source missing: ${sourceRoot}`
|
|
11459
|
+
};
|
|
11460
|
+
}
|
|
11461
|
+
const report = copyTreeWithReport({
|
|
11462
|
+
srcDir: sourceRoot,
|
|
11463
|
+
destDir: targetDir,
|
|
11464
|
+
dryRun: Boolean(opts.dryRun)
|
|
11465
|
+
});
|
|
11466
|
+
return { ok: true, sourceRoot, targetDir, report };
|
|
11467
|
+
}
|
|
11346
11468
|
async function summary3(ctx) {
|
|
11469
|
+
const skills = availableSkills();
|
|
11470
|
+
const targetDir = skillsTargetDir(ctx.cwd);
|
|
11471
|
+
const installedDirs = fs15.existsSync(targetDir) ? new Set(
|
|
11472
|
+
fs15.readdirSync(targetDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name)
|
|
11473
|
+
) : /* @__PURE__ */ new Set();
|
|
11474
|
+
const body = [];
|
|
11475
|
+
if (skills.length === 0) {
|
|
11476
|
+
body.push(theme.muted("No consumer skills bundled with this package build."));
|
|
11477
|
+
return { title: "skills", body, footer: "" };
|
|
11478
|
+
}
|
|
11479
|
+
body.push(theme.muted("Consumer skills:"));
|
|
11480
|
+
for (const name of skills) {
|
|
11481
|
+
const present = installedDirs.has(name);
|
|
11482
|
+
const icon = present ? theme.success(icons.check) : theme.muted(icons.dash);
|
|
11483
|
+
const status = present ? "" : theme.muted("not installed");
|
|
11484
|
+
body.push(` ${icon} ${name.padEnd(12)} ${status}`);
|
|
11485
|
+
}
|
|
11486
|
+
const installedCount = skills.filter((s) => installedDirs.has(s)).length;
|
|
11487
|
+
return {
|
|
11488
|
+
title: "skills",
|
|
11489
|
+
body,
|
|
11490
|
+
footer: `${installedCount} of ${skills.length} skills installed \u2192 ${path26.relative(ctx.cwd, targetDir) || targetDir}`
|
|
11491
|
+
};
|
|
11492
|
+
}
|
|
11493
|
+
async function hints3(ctx) {
|
|
11494
|
+
const skills = availableSkills();
|
|
11495
|
+
const targetDir = skillsTargetDir(ctx.cwd);
|
|
11496
|
+
const allPresent = skills.length > 0 && fs15.existsSync(targetDir) && skills.every((s) => fs15.existsSync(path26.join(targetDir, s)));
|
|
11497
|
+
if (allPresent) {
|
|
11498
|
+
return [
|
|
11499
|
+
{ command: "codegen update", description: "Re-sync skills + runtime after a package bump" }
|
|
11500
|
+
];
|
|
11501
|
+
}
|
|
11502
|
+
return [
|
|
11503
|
+
{ command: "codegen skills install", description: "Vendor consumer skills into .claude/skills" }
|
|
11504
|
+
];
|
|
11505
|
+
}
|
|
11506
|
+
function renderTreeReport(report) {
|
|
11507
|
+
const show = (entry) => {
|
|
11508
|
+
const icon = theme.success(icons.check);
|
|
11509
|
+
console.log(` ${icon} ${theme.muted(entry.action.padEnd(10))} ${entry.relPath}`);
|
|
11510
|
+
};
|
|
11511
|
+
for (const e of [...report.created, ...report.updated]) show(e);
|
|
11512
|
+
if (report.unchanged.length > 0) {
|
|
11513
|
+
console.log(
|
|
11514
|
+
` ${theme.muted(icons.dash)} ${theme.muted("unchanged".padEnd(10))} ${theme.muted(
|
|
11515
|
+
`${report.unchanged.length} file${report.unchanged.length === 1 ? "" : "s"} already current`
|
|
11516
|
+
)}`
|
|
11517
|
+
);
|
|
11518
|
+
}
|
|
11519
|
+
}
|
|
11520
|
+
var SkillsInstallCommand = class extends Command5 {
|
|
11521
|
+
static paths = [["skills", "install"]];
|
|
11522
|
+
static usage = Command5.Usage({
|
|
11523
|
+
description: "Vendor consumer-facing skills into the project .claude/skills",
|
|
11524
|
+
examples: [
|
|
11525
|
+
["Install all consumer skills", "codegen skills install"],
|
|
11526
|
+
["Preview without writing", "codegen skills install --dry-run"],
|
|
11527
|
+
["Overwrite locally-edited skill files", "codegen skills install --force"]
|
|
11528
|
+
]
|
|
11529
|
+
});
|
|
11530
|
+
force = Option5.Boolean("--force", false);
|
|
11531
|
+
dryRun = Option5.Boolean("--dry-run", false);
|
|
11532
|
+
json = Option5.Boolean("--json", false);
|
|
11533
|
+
cwd = Option5.String("--cwd", { required: false });
|
|
11534
|
+
async execute() {
|
|
11535
|
+
if (this.json) setJsonMode(true);
|
|
11536
|
+
const ctx = await loadContext({ cwd: this.cwd, json: this.json, skipDetection: true });
|
|
11537
|
+
const preview = runSkillsInstall({ cwd: ctx.cwd, dryRun: true });
|
|
11538
|
+
if (!preview.ok) {
|
|
11539
|
+
if (isJsonMode()) {
|
|
11540
|
+
printJson({ command: "skills install", status: "error", error: preview.error });
|
|
11541
|
+
} else {
|
|
11542
|
+
printError(preview.error ?? "skills install failed");
|
|
11543
|
+
}
|
|
11544
|
+
return 1;
|
|
11545
|
+
}
|
|
11546
|
+
if (!this.dryRun && !this.force) {
|
|
11547
|
+
const updatedPaths = preview.report.updated.map((e) => e.dest);
|
|
11548
|
+
const gate = checkGitSafety(updatedPaths, ctx.cwd);
|
|
11549
|
+
if (gate.inRepo && !gate.clean) {
|
|
11550
|
+
printWarning(
|
|
11551
|
+
`Uncommitted changes in ${gate.dirty.length} skill file(s). Commit them or pass --force to overwrite.`
|
|
11552
|
+
);
|
|
11553
|
+
if (!isJsonMode()) return 1;
|
|
11554
|
+
}
|
|
11555
|
+
}
|
|
11556
|
+
const result = runSkillsInstall({ cwd: ctx.cwd, dryRun: this.dryRun });
|
|
11557
|
+
const report = result.report;
|
|
11558
|
+
if (isJsonMode()) {
|
|
11559
|
+
printJson({
|
|
11560
|
+
command: "skills install",
|
|
11561
|
+
dryRun: this.dryRun,
|
|
11562
|
+
target: result.targetDir,
|
|
11563
|
+
files: {
|
|
11564
|
+
created: report.created.map((e) => e.relPath),
|
|
11565
|
+
updated: report.updated.map((e) => e.relPath),
|
|
11566
|
+
unchanged: report.unchanged.map((e) => e.relPath)
|
|
11567
|
+
}
|
|
11568
|
+
});
|
|
11569
|
+
return 0;
|
|
11570
|
+
}
|
|
11571
|
+
printInfo(`target = ${path26.relative(ctx.cwd, result.targetDir) || result.targetDir}`);
|
|
11572
|
+
console.log("");
|
|
11573
|
+
renderTreeReport(report);
|
|
11574
|
+
console.log("");
|
|
11575
|
+
if (this.dryRun) {
|
|
11576
|
+
printWarning("dry-run \u2014 no files written");
|
|
11577
|
+
return 0;
|
|
11578
|
+
}
|
|
11579
|
+
printSuccess(
|
|
11580
|
+
`skills installed (${report.created.length} new, ${report.updated.length} updated, ${report.unchanged.length} unchanged)`
|
|
11581
|
+
);
|
|
11582
|
+
printInfo("Skills are auto-discovered by Claude Code from .claude/skills/.");
|
|
11583
|
+
return 0;
|
|
11584
|
+
}
|
|
11585
|
+
};
|
|
11586
|
+
var SkillsListCommand = class extends Command5 {
|
|
11587
|
+
static paths = [["skills", "list"]];
|
|
11588
|
+
static usage = Command5.Usage({
|
|
11589
|
+
description: "List available consumer skills and their installed status"
|
|
11590
|
+
});
|
|
11591
|
+
json = Option5.Boolean("--json", false);
|
|
11592
|
+
cwd = Option5.String("--cwd", { required: false });
|
|
11593
|
+
async execute() {
|
|
11594
|
+
if (this.json) setJsonMode(true);
|
|
11595
|
+
const ctx = await loadContext({ cwd: this.cwd, json: this.json, skipDetection: true });
|
|
11596
|
+
const skills = availableSkills();
|
|
11597
|
+
const targetDir = skillsTargetDir(ctx.cwd);
|
|
11598
|
+
const rows = skills.map((name) => {
|
|
11599
|
+
const dir = path26.join(targetDir, name);
|
|
11600
|
+
return { name, status: fs15.existsSync(dir) ? "installed" : "available" };
|
|
11601
|
+
});
|
|
11602
|
+
if (isJsonMode()) {
|
|
11603
|
+
printJson({ command: "skills list", target: targetDir, skills: rows });
|
|
11604
|
+
return 0;
|
|
11605
|
+
}
|
|
11606
|
+
if (rows.length === 0) {
|
|
11607
|
+
printWarning("No consumer skills bundled with this package build.");
|
|
11608
|
+
return 0;
|
|
11609
|
+
}
|
|
11610
|
+
const pad = (s, n) => s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
11611
|
+
console.log(theme.muted(`${pad("NAME", 14)}STATUS`));
|
|
11612
|
+
for (const r of rows) console.log(`${pad(r.name, 14)}${r.status}`);
|
|
11613
|
+
return 0;
|
|
11614
|
+
}
|
|
11615
|
+
};
|
|
11616
|
+
var skillsNoun = {
|
|
11617
|
+
name: "skills",
|
|
11618
|
+
commandClasses: [SkillsInstallCommand, SkillsListCommand],
|
|
11619
|
+
summary: summary3,
|
|
11620
|
+
hints: hints3
|
|
11621
|
+
};
|
|
11622
|
+
var skills_default = skillsNoun;
|
|
11623
|
+
|
|
11624
|
+
// src/cli/commands/project-update.ts
|
|
11625
|
+
var NON_RUNTIME_SUBSYSTEMS = /* @__PURE__ */ new Set(["openapi-config", "auth-integrations"]);
|
|
11626
|
+
function syncVendoredRuntime(cwd, write) {
|
|
11627
|
+
const changes = [];
|
|
11628
|
+
for (const v of VENDORED_RUNTIME_FILES) {
|
|
11629
|
+
const dest = path27.join(cwd, v.target);
|
|
11630
|
+
const content = loadRuntimeFile(v.runtime);
|
|
11631
|
+
const existing = fs16.existsSync(dest) ? fs16.readFileSync(dest, "utf-8") : null;
|
|
11632
|
+
let action;
|
|
11633
|
+
if (existing === null) action = "created";
|
|
11634
|
+
else if (existing === content) action = "unchanged";
|
|
11635
|
+
else action = "updated";
|
|
11636
|
+
if (write && action !== "unchanged") {
|
|
11637
|
+
fs16.mkdirSync(path27.dirname(dest), { recursive: true });
|
|
11638
|
+
fs16.writeFileSync(dest, content, "utf-8");
|
|
11639
|
+
}
|
|
11640
|
+
changes.push({ path: v.target, action });
|
|
11641
|
+
}
|
|
11642
|
+
return changes;
|
|
11643
|
+
}
|
|
11644
|
+
async function syncSubsystemRuntime(cwd, inst, write) {
|
|
11645
|
+
if (NON_RUNTIME_SUBSYSTEMS.has(inst.name)) {
|
|
11646
|
+
return { name: inst.name, changes: [], skippedReason: "config-only / vendored elsewhere" };
|
|
11647
|
+
}
|
|
11648
|
+
const source = subsystemSource(inst.name);
|
|
11649
|
+
if (!fs16.existsSync(source)) {
|
|
11650
|
+
return { name: inst.name, changes: [], skippedReason: "no runtime source in package" };
|
|
11651
|
+
}
|
|
11652
|
+
const subsystemsRoot = path27.dirname(inst.path);
|
|
11653
|
+
const result = await copyRuntime({
|
|
11654
|
+
sourceDir: source,
|
|
11655
|
+
targetDir: inst.path,
|
|
11656
|
+
filter: backendFileFilter(inst.backend, inst.name),
|
|
11657
|
+
resolveDeps: true,
|
|
11658
|
+
runtimeRoot: runtimeRoot2(),
|
|
11659
|
+
depsTargetRoot: path27.resolve(subsystemsRoot, ".."),
|
|
11660
|
+
dryRun: !write,
|
|
11661
|
+
// Refresh files already vendored for this subsystem; never install new
|
|
11662
|
+
// ones (that's `subsystem install`). copyRuntime classifies accurately
|
|
11663
|
+
// in dry-run too, so this report is correct either way.
|
|
11664
|
+
onlyExisting: true
|
|
11665
|
+
});
|
|
11666
|
+
const changes = [];
|
|
11667
|
+
for (const p of result.written) changes.push({ path: rel(cwd, p), action: "created" });
|
|
11668
|
+
for (const p of result.updated) changes.push({ path: rel(cwd, p), action: "updated" });
|
|
11669
|
+
for (const p of result.unchanged) changes.push({ path: rel(cwd, p), action: "unchanged" });
|
|
11670
|
+
return { name: inst.name, changes };
|
|
11671
|
+
}
|
|
11672
|
+
function rel(cwd, abs) {
|
|
11673
|
+
return path27.relative(cwd, abs) || abs;
|
|
11674
|
+
}
|
|
11675
|
+
var ProjectUpdateCommand = class extends Command6 {
|
|
11676
|
+
static paths = [["project", "update"]];
|
|
11677
|
+
static usage = Command6.Usage({
|
|
11678
|
+
description: "Re-sync vendored runtime, installed subsystems, and consumer skills to the installed package version",
|
|
11679
|
+
examples: [
|
|
11680
|
+
["Re-sync everything after a package bump", "codegen update"],
|
|
11681
|
+
["Preview without writing", "codegen update --dry-run"],
|
|
11682
|
+
["Overwrite even with uncommitted changes", "codegen update --force"],
|
|
11683
|
+
["Skip the skills re-sync", "codegen update --skip-skills"]
|
|
11684
|
+
]
|
|
11685
|
+
});
|
|
11686
|
+
dryRun = Option6.Boolean("--dry-run", false);
|
|
11687
|
+
force = Option6.Boolean("--force", false);
|
|
11688
|
+
skipSkills = Option6.Boolean("--skip-skills", false);
|
|
11689
|
+
skipSubsystems = Option6.Boolean("--skip-subsystems", false);
|
|
11690
|
+
json = Option6.Boolean("--json", false);
|
|
11691
|
+
cwd = Option6.String("--cwd", { required: false });
|
|
11692
|
+
configPath = Option6.String("--config", { required: false });
|
|
11693
|
+
async execute() {
|
|
11694
|
+
if (this.json) setJsonMode(true);
|
|
11695
|
+
const ctx = await loadContext({
|
|
11696
|
+
cwd: this.cwd,
|
|
11697
|
+
configPath: this.configPath,
|
|
11698
|
+
json: this.json,
|
|
11699
|
+
skipDetection: true
|
|
11700
|
+
});
|
|
11701
|
+
if (!ctx.isInitialized) {
|
|
11702
|
+
if (isJsonMode()) {
|
|
11703
|
+
printJson({ command: "project update", status: "not-initialized" });
|
|
11704
|
+
} else {
|
|
11705
|
+
printWarning("project is not initialized \u2014 run `codegen init` first");
|
|
11706
|
+
}
|
|
11707
|
+
return 1;
|
|
11708
|
+
}
|
|
11709
|
+
const installed = this.skipSubsystems ? [] : await detectInstalledSubsystems(ctx);
|
|
11710
|
+
if (!this.dryRun && !this.force) {
|
|
11711
|
+
const vendoredDry = syncVendoredRuntime(ctx.cwd, false);
|
|
11712
|
+
const vendoredDirtyCandidates = vendoredDry.filter((c) => c.action === "updated").map((c) => path27.join(ctx.cwd, c.path));
|
|
11713
|
+
const skillDirtyCandidates = this.skipSkills ? [] : runSkillsInstall({ cwd: ctx.cwd, dryRun: true }).report?.updated.map((e) => e.dest) ?? [];
|
|
11714
|
+
const subsystemDirs = installed.filter((i) => !NON_RUNTIME_SUBSYSTEMS.has(i.name)).map((i) => i.path);
|
|
11715
|
+
const gate = checkGitSafety(
|
|
11716
|
+
[...vendoredDirtyCandidates, ...skillDirtyCandidates, ...subsystemDirs],
|
|
11717
|
+
ctx.cwd
|
|
11718
|
+
);
|
|
11719
|
+
if (gate.inRepo && !gate.clean) {
|
|
11720
|
+
if (isJsonMode()) {
|
|
11721
|
+
printJson({
|
|
11722
|
+
command: "project update",
|
|
11723
|
+
status: "dirty-tree",
|
|
11724
|
+
dirty: gate.dirty
|
|
11725
|
+
});
|
|
11726
|
+
} else {
|
|
11727
|
+
printWarning(
|
|
11728
|
+
`Uncommitted changes in ${gate.dirty.length} file(s) that update would overwrite. Commit them or pass --force.`
|
|
11729
|
+
);
|
|
11730
|
+
for (const d of gate.dirty.slice(0, 10)) {
|
|
11731
|
+
console.log(` ${theme.muted(icons.dash)} ${d}`);
|
|
11732
|
+
}
|
|
11733
|
+
}
|
|
11734
|
+
return 1;
|
|
11735
|
+
}
|
|
11736
|
+
}
|
|
11737
|
+
const write = !this.dryRun;
|
|
11738
|
+
const vendored = syncVendoredRuntime(ctx.cwd, write);
|
|
11739
|
+
const subsystemResults = [];
|
|
11740
|
+
for (const inst of installed) {
|
|
11741
|
+
subsystemResults.push(await syncSubsystemRuntime(ctx.cwd, inst, write));
|
|
11742
|
+
}
|
|
11743
|
+
const skills = this.skipSkills ? null : runSkillsInstall({ cwd: ctx.cwd, dryRun: this.dryRun });
|
|
11744
|
+
const tally = (changes) => ({
|
|
11745
|
+
created: changes.filter((c) => c.action === "created").length,
|
|
11746
|
+
updated: changes.filter((c) => c.action === "updated").length,
|
|
11747
|
+
unchanged: changes.filter((c) => c.action === "unchanged").length
|
|
11748
|
+
});
|
|
11749
|
+
if (isJsonMode()) {
|
|
11750
|
+
printJson({
|
|
11751
|
+
command: "project update",
|
|
11752
|
+
dryRun: this.dryRun,
|
|
11753
|
+
runtime: vendored,
|
|
11754
|
+
subsystems: subsystemResults.map((s) => ({
|
|
11755
|
+
name: s.name,
|
|
11756
|
+
skipped: s.skippedReason ?? null,
|
|
11757
|
+
...tally(s.changes)
|
|
11758
|
+
})),
|
|
11759
|
+
skills: skills && skills.report ? {
|
|
11760
|
+
created: skills.report.created.length,
|
|
11761
|
+
updated: skills.report.updated.length,
|
|
11762
|
+
unchanged: skills.report.unchanged.length
|
|
11763
|
+
} : null
|
|
11764
|
+
});
|
|
11765
|
+
return 0;
|
|
11766
|
+
}
|
|
11767
|
+
printInfo(`Updating ${ctx.cwd} to the installed @pattern-stack/codegen version`);
|
|
11768
|
+
console.log("");
|
|
11769
|
+
renderSection("shared runtime", vendored);
|
|
11770
|
+
for (const s of subsystemResults) {
|
|
11771
|
+
if (s.skippedReason) {
|
|
11772
|
+
console.log(
|
|
11773
|
+
` ${theme.muted(icons.dash)} ${theme.muted(`subsystem ${s.name} skipped (${s.skippedReason})`)}`
|
|
11774
|
+
);
|
|
11775
|
+
continue;
|
|
11776
|
+
}
|
|
11777
|
+
renderSection(`subsystem ${s.name}`, s.changes);
|
|
11778
|
+
}
|
|
11779
|
+
if (skills?.report) {
|
|
11780
|
+
renderSection(
|
|
11781
|
+
"skills",
|
|
11782
|
+
[
|
|
11783
|
+
...skills.report.created.map((e) => ({ path: e.relPath, action: "created" })),
|
|
11784
|
+
...skills.report.updated.map((e) => ({ path: e.relPath, action: "updated" })),
|
|
11785
|
+
...skills.report.unchanged.map((e) => ({
|
|
11786
|
+
path: e.relPath,
|
|
11787
|
+
action: "unchanged"
|
|
11788
|
+
}))
|
|
11789
|
+
]
|
|
11790
|
+
);
|
|
11791
|
+
} else if (this.skipSkills) {
|
|
11792
|
+
console.log(` ${theme.muted(icons.dash)} ${theme.muted("skills skipped (--skip-skills)")}`);
|
|
11793
|
+
}
|
|
11794
|
+
console.log("");
|
|
11795
|
+
if (this.dryRun) {
|
|
11796
|
+
printWarning("dry-run \u2014 no files written");
|
|
11797
|
+
return 0;
|
|
11798
|
+
}
|
|
11799
|
+
printSuccess("update complete");
|
|
11800
|
+
printInfo(
|
|
11801
|
+
"Schema shape changes are NOT re-synced \u2014 if a subsystem schema changed across versions, run `codegen subsystem install <name> --force --force-config`."
|
|
11802
|
+
);
|
|
11803
|
+
return 0;
|
|
11804
|
+
}
|
|
11805
|
+
};
|
|
11806
|
+
function renderSection(label, changes) {
|
|
11807
|
+
const created = changes.filter((c) => c.action === "created");
|
|
11808
|
+
const updated = changes.filter((c) => c.action === "updated");
|
|
11809
|
+
const unchanged = changes.filter((c) => c.action === "unchanged");
|
|
11810
|
+
if (created.length === 0 && updated.length === 0 && unchanged.length === 0) return;
|
|
11811
|
+
const head = created.length + updated.length === 0 ? theme.muted(`${label} \u2014 up to date (${unchanged.length})`) : `${theme.system(label)} \u2014 ${created.length} new, ${updated.length} updated`;
|
|
11812
|
+
console.log(` ${head}`);
|
|
11813
|
+
for (const c of [...created, ...updated]) {
|
|
11814
|
+
console.log(` ${theme.success(icons.check)} ${theme.muted(c.action.padEnd(8))} ${c.path}`);
|
|
11815
|
+
}
|
|
11816
|
+
}
|
|
11817
|
+
|
|
11818
|
+
// src/cli/commands/project.ts
|
|
11819
|
+
async function summary4(ctx) {
|
|
11347
11820
|
if (!ctx.isInitialized) {
|
|
11348
11821
|
return {
|
|
11349
11822
|
title: "project",
|
|
@@ -11368,7 +11841,7 @@ async function summary3(ctx) {
|
|
|
11368
11841
|
body.push(` orm: ${orm}`);
|
|
11369
11842
|
body.push(` architecture: ${arch}`);
|
|
11370
11843
|
body.push(` entities: ${ctx.entityCount}`);
|
|
11371
|
-
body.push(` subsystems: ${ctx.installedSubsystems.length}
|
|
11844
|
+
body.push(` subsystems: ${ctx.installedSubsystems.length}/${SUBSYSTEMS.length} installed`);
|
|
11372
11845
|
body.push(` generated: ${generated}`);
|
|
11373
11846
|
return {
|
|
11374
11847
|
title: "project",
|
|
@@ -11376,7 +11849,7 @@ async function summary3(ctx) {
|
|
|
11376
11849
|
footer: `cwd: ${ctx.cwd}`
|
|
11377
11850
|
};
|
|
11378
11851
|
}
|
|
11379
|
-
async function
|
|
11852
|
+
async function hints4(ctx) {
|
|
11380
11853
|
if (!ctx.isInitialized) {
|
|
11381
11854
|
return [
|
|
11382
11855
|
{ command: "codegen init", description: "Scaffold consumer project" },
|
|
@@ -11394,17 +11867,17 @@ async function hints3(ctx) {
|
|
|
11394
11867
|
} else {
|
|
11395
11868
|
out.push({ command: "codegen entity", description: "Entity summary + hints" });
|
|
11396
11869
|
}
|
|
11397
|
-
if (ctx.installedSubsystems.length <
|
|
11870
|
+
if (ctx.installedSubsystems.length < SUBSYSTEMS.length) {
|
|
11398
11871
|
out.push({
|
|
11399
11872
|
command: "codegen subsystem",
|
|
11400
|
-
description: "Install events/jobs/cache/storage"
|
|
11873
|
+
description: "Install events/jobs/cache/storage/\u2026"
|
|
11401
11874
|
});
|
|
11402
11875
|
}
|
|
11403
11876
|
return out;
|
|
11404
11877
|
}
|
|
11405
|
-
var ProjectInitCommand = class extends
|
|
11878
|
+
var ProjectInitCommand = class extends Command7 {
|
|
11406
11879
|
static paths = [["project", "init"]];
|
|
11407
|
-
static usage =
|
|
11880
|
+
static usage = Command7.Usage({
|
|
11408
11881
|
description: "Scaffold a consumer project (config, shims, barrels, app.module)",
|
|
11409
11882
|
examples: [
|
|
11410
11883
|
["Initialize with defaults", "codegen project init --yes"],
|
|
@@ -11413,12 +11886,14 @@ var ProjectInitCommand = class extends Command5 {
|
|
|
11413
11886
|
["Overwrite existing shims", "codegen project init --force"]
|
|
11414
11887
|
]
|
|
11415
11888
|
});
|
|
11416
|
-
yes =
|
|
11417
|
-
dryRun =
|
|
11418
|
-
force =
|
|
11419
|
-
withTsconfig =
|
|
11420
|
-
|
|
11421
|
-
|
|
11889
|
+
yes = Option7.Boolean("--yes,-y", false);
|
|
11890
|
+
dryRun = Option7.Boolean("--dry-run", false);
|
|
11891
|
+
force = Option7.Boolean("--force", false);
|
|
11892
|
+
withTsconfig = Option7.Boolean("--with-tsconfig", false);
|
|
11893
|
+
// Vendor consumer skills into .claude/skills by default; opt out with --no-skills.
|
|
11894
|
+
skills = Option7.Boolean("--skills", true);
|
|
11895
|
+
json = Option7.Boolean("--json", false);
|
|
11896
|
+
cwd = Option7.String("--cwd", { required: false });
|
|
11422
11897
|
async execute() {
|
|
11423
11898
|
if (this.json) setJsonMode(true);
|
|
11424
11899
|
const ctx = await loadContext({
|
|
@@ -11449,6 +11924,7 @@ var ProjectInitCommand = class extends Command5 {
|
|
|
11449
11924
|
console.log("");
|
|
11450
11925
|
}
|
|
11451
11926
|
const result = writePlan(plan);
|
|
11927
|
+
const skillsResult = this.skills ? runSkillsInstall({ cwd: ctx.cwd, dryRun: false }) : null;
|
|
11452
11928
|
if (isJsonMode()) {
|
|
11453
11929
|
printJson({
|
|
11454
11930
|
command: "project init",
|
|
@@ -11457,7 +11933,12 @@ var ProjectInitCommand = class extends Command5 {
|
|
|
11457
11933
|
created: result.created.map((e) => e.relPath),
|
|
11458
11934
|
merged: result.merged.map((e) => e.relPath),
|
|
11459
11935
|
overwritten: result.overwritten.map((e) => e.relPath),
|
|
11460
|
-
skipped: result.skipped.map((e) => ({ path: e.relPath, reason: e.reason }))
|
|
11936
|
+
skipped: result.skipped.map((e) => ({ path: e.relPath, reason: e.reason })),
|
|
11937
|
+
skills: skillsResult && skillsResult.report ? {
|
|
11938
|
+
created: skillsResult.report.created.length,
|
|
11939
|
+
updated: skillsResult.report.updated.length,
|
|
11940
|
+
unchanged: skillsResult.report.unchanged.length
|
|
11941
|
+
} : null
|
|
11461
11942
|
});
|
|
11462
11943
|
return 0;
|
|
11463
11944
|
}
|
|
@@ -11486,6 +11967,22 @@ var ProjectInitCommand = class extends Command5 {
|
|
|
11486
11967
|
` ${theme.muted(icons.dash)} ${theme.muted("skip ")} ${e.relPath}${e.reason ? theme.muted(" (" + e.reason + ")") : ""}`
|
|
11487
11968
|
);
|
|
11488
11969
|
}
|
|
11970
|
+
if (skillsResult?.report) {
|
|
11971
|
+
const r = skillsResult.report;
|
|
11972
|
+
console.log(
|
|
11973
|
+
` ${theme.success(icons.check)} ${theme.muted("skills ")} .claude/skills/ ${theme.muted(
|
|
11974
|
+
`(${r.created.length} new, ${r.updated.length} updated, ${r.unchanged.length} unchanged)`
|
|
11975
|
+
)}`
|
|
11976
|
+
);
|
|
11977
|
+
} else if (!this.skills) {
|
|
11978
|
+
console.log(
|
|
11979
|
+
` ${theme.muted(icons.dash)} ${theme.muted("skip ")} .claude/skills/ ${theme.muted("(--no-skills)")}`
|
|
11980
|
+
);
|
|
11981
|
+
} else if (skillsResult && !skillsResult.ok) {
|
|
11982
|
+
console.log(
|
|
11983
|
+
` ${theme.warning(icons.warning)} ${theme.muted("skills ")} ${theme.muted(skillsResult.error ?? "skills install failed")}`
|
|
11984
|
+
);
|
|
11985
|
+
}
|
|
11489
11986
|
console.log("");
|
|
11490
11987
|
printInfo("Next steps:");
|
|
11491
11988
|
console.log(` 1. ${theme.system("bun add")} the peer deps (see docs/CONSUMER-SETUP.md)`);
|
|
@@ -11498,10 +11995,10 @@ var ProjectInitCommand = class extends Command5 {
|
|
|
11498
11995
|
};
|
|
11499
11996
|
function askConfirm(question) {
|
|
11500
11997
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
11501
|
-
return new Promise((
|
|
11998
|
+
return new Promise((resolve4) => {
|
|
11502
11999
|
rl.question(`${question} [Y/n] `, (answer) => {
|
|
11503
12000
|
rl.close();
|
|
11504
|
-
|
|
12001
|
+
resolve4(answer.trim().toLowerCase() !== "n");
|
|
11505
12002
|
});
|
|
11506
12003
|
});
|
|
11507
12004
|
}
|
|
@@ -11534,9 +12031,9 @@ function renderPlanOnly(plan, opts) {
|
|
|
11534
12031
|
}
|
|
11535
12032
|
return 0;
|
|
11536
12033
|
}
|
|
11537
|
-
var ProjectScanCommand = class extends
|
|
12034
|
+
var ProjectScanCommand = class extends Command7 {
|
|
11538
12035
|
static paths = [["project", "scan"]];
|
|
11539
|
-
static usage =
|
|
12036
|
+
static usage = Command7.Usage({
|
|
11540
12037
|
description: "Detect framework/ORM/architecture and propose a codegen.config.yaml",
|
|
11541
12038
|
examples: [
|
|
11542
12039
|
["Scan the current directory", "codegen project scan"],
|
|
@@ -11544,17 +12041,17 @@ var ProjectScanCommand = class extends Command5 {
|
|
|
11544
12041
|
["Preview only", "codegen project scan --dry-run"]
|
|
11545
12042
|
]
|
|
11546
12043
|
});
|
|
11547
|
-
directory =
|
|
11548
|
-
write =
|
|
11549
|
-
dryRun =
|
|
11550
|
-
verbose =
|
|
11551
|
-
json =
|
|
11552
|
-
cwd =
|
|
12044
|
+
directory = Option7.String({ required: false });
|
|
12045
|
+
write = Option7.Boolean("--write", false);
|
|
12046
|
+
dryRun = Option7.Boolean("--dry-run", false);
|
|
12047
|
+
verbose = Option7.Boolean("--verbose,-v", false);
|
|
12048
|
+
json = Option7.Boolean("--json", false);
|
|
12049
|
+
cwd = Option7.String("--cwd", { required: false });
|
|
11553
12050
|
async execute() {
|
|
11554
12051
|
if (this.json) setJsonMode(true);
|
|
11555
|
-
const baseCwd = this.cwd ?
|
|
11556
|
-
const target = this.directory ?
|
|
11557
|
-
if (!
|
|
12052
|
+
const baseCwd = this.cwd ? path28.resolve(this.cwd) : process.cwd();
|
|
12053
|
+
const target = this.directory ? path28.resolve(baseCwd, this.directory) : baseCwd;
|
|
12054
|
+
if (!fs17.existsSync(target)) {
|
|
11558
12055
|
printError(`Directory not found: ${target}`);
|
|
11559
12056
|
return 1;
|
|
11560
12057
|
}
|
|
@@ -11604,8 +12101,8 @@ var ProjectScanCommand = class extends Command5 {
|
|
|
11604
12101
|
`architecture: ${profile.architecture.evidence.join(", ") || "\u2014"}`
|
|
11605
12102
|
]);
|
|
11606
12103
|
}
|
|
11607
|
-
const outPath =
|
|
11608
|
-
const existsNow =
|
|
12104
|
+
const outPath = path28.join(target, "codegen.config.yaml");
|
|
12105
|
+
const existsNow = fs17.existsSync(outPath);
|
|
11609
12106
|
if (this.dryRun) {
|
|
11610
12107
|
console.log("");
|
|
11611
12108
|
printInfo("Dry run \u2014 proposed codegen.config.yaml:");
|
|
@@ -11618,7 +12115,7 @@ var ProjectScanCommand = class extends Command5 {
|
|
|
11618
12115
|
printWarning(`${outPath} already exists \u2014 pass --force via edit; skipping.`);
|
|
11619
12116
|
return 0;
|
|
11620
12117
|
}
|
|
11621
|
-
|
|
12118
|
+
fs17.writeFileSync(outPath, yamlText);
|
|
11622
12119
|
printSuccess(`wrote ${outPath}`);
|
|
11623
12120
|
return 0;
|
|
11624
12121
|
}
|
|
@@ -11636,14 +12133,14 @@ function printMutedBlock(title, lines) {
|
|
|
11636
12133
|
console.log(theme.muted(title + ":"));
|
|
11637
12134
|
for (const l of lines) console.log(theme.muted(" " + l));
|
|
11638
12135
|
}
|
|
11639
|
-
var ProjectConfigCommand = class extends
|
|
12136
|
+
var ProjectConfigCommand = class extends Command7 {
|
|
11640
12137
|
static paths = [["project", "config"]];
|
|
11641
|
-
static usage =
|
|
12138
|
+
static usage = Command7.Usage({
|
|
11642
12139
|
description: "Print the resolved codegen config (YAML or JSON)"
|
|
11643
12140
|
});
|
|
11644
|
-
json =
|
|
11645
|
-
cwd =
|
|
11646
|
-
configPath =
|
|
12141
|
+
json = Option7.Boolean("--json", false);
|
|
12142
|
+
cwd = Option7.String("--cwd", { required: false });
|
|
12143
|
+
configPath = Option7.String("--config", { required: false });
|
|
11647
12144
|
async execute() {
|
|
11648
12145
|
if (this.json) setJsonMode(true);
|
|
11649
12146
|
const ctx = await loadContext({
|
|
@@ -11675,9 +12172,9 @@ var ProjectConfigCommand = class extends Command5 {
|
|
|
11675
12172
|
return 0;
|
|
11676
12173
|
}
|
|
11677
12174
|
};
|
|
11678
|
-
var ProjectInspectCommand = class extends
|
|
12175
|
+
var ProjectInspectCommand = class extends Command7 {
|
|
11679
12176
|
static paths = [["project", "inspect"]];
|
|
11680
|
-
static usage =
|
|
12177
|
+
static usage = Command7.Usage({
|
|
11681
12178
|
description: "Domain analysis, statistics, documentation, and manifest operations",
|
|
11682
12179
|
examples: [
|
|
11683
12180
|
["Full analysis", "codegen project inspect --kind analyze"],
|
|
@@ -11687,20 +12184,20 @@ var ProjectInspectCommand = class extends Command5 {
|
|
|
11687
12184
|
["Review suggestions", "codegen project inspect --kind suggestions"]
|
|
11688
12185
|
]
|
|
11689
12186
|
});
|
|
11690
|
-
kind =
|
|
11691
|
-
dir =
|
|
11692
|
-
format =
|
|
11693
|
-
output =
|
|
11694
|
-
strict =
|
|
11695
|
-
entity =
|
|
11696
|
-
force =
|
|
11697
|
-
accept =
|
|
11698
|
-
skip =
|
|
11699
|
-
acceptAll =
|
|
11700
|
-
skipAll =
|
|
11701
|
-
json =
|
|
11702
|
-
cwd =
|
|
11703
|
-
configPath =
|
|
12187
|
+
kind = Option7.String("--kind", { required: true });
|
|
12188
|
+
dir = Option7.String({ required: false });
|
|
12189
|
+
format = Option7.String("--format", "console");
|
|
12190
|
+
output = Option7.String("--output,-o", { required: false });
|
|
12191
|
+
strict = Option7.Boolean("--strict", false);
|
|
12192
|
+
entity = Option7.String("--entity", { required: false });
|
|
12193
|
+
force = Option7.Boolean("--force", false);
|
|
12194
|
+
accept = Option7.String("--accept", { required: false });
|
|
12195
|
+
skip = Option7.String("--skip", { required: false });
|
|
12196
|
+
acceptAll = Option7.Boolean("--accept-all", false);
|
|
12197
|
+
skipAll = Option7.Boolean("--skip-all", false);
|
|
12198
|
+
json = Option7.Boolean("--json", false);
|
|
12199
|
+
cwd = Option7.String("--cwd", { required: false });
|
|
12200
|
+
configPath = Option7.String("--config", { required: false });
|
|
11704
12201
|
async execute() {
|
|
11705
12202
|
if (this.json || this.format === "json") setJsonMode(true);
|
|
11706
12203
|
const ctx = await loadContext({
|
|
@@ -11725,12 +12222,12 @@ var ProjectInspectCommand = class extends Command5 {
|
|
|
11725
12222
|
return 2;
|
|
11726
12223
|
}
|
|
11727
12224
|
resolveEntitiesDir(ctx) {
|
|
11728
|
-
if (this.dir) return
|
|
11729
|
-
return ctx.entitiesDir ??
|
|
12225
|
+
if (this.dir) return path28.resolve(ctx.cwd, this.dir);
|
|
12226
|
+
return ctx.entitiesDir ?? path28.resolve(ctx.cwd, "entities");
|
|
11730
12227
|
}
|
|
11731
12228
|
async runAnalysis(ctx, kind) {
|
|
11732
12229
|
const entitiesDir = this.resolveEntitiesDir(ctx);
|
|
11733
|
-
if (!entitiesDir || !
|
|
12230
|
+
if (!entitiesDir || !fs17.existsSync(entitiesDir)) {
|
|
11734
12231
|
printError(`Directory not found: ${entitiesDir ?? "(no entities/ dir)"}`);
|
|
11735
12232
|
return 1;
|
|
11736
12233
|
}
|
|
@@ -11760,7 +12257,7 @@ var ProjectInspectCommand = class extends Command5 {
|
|
|
11760
12257
|
out = formatConsole(filtered);
|
|
11761
12258
|
}
|
|
11762
12259
|
if (this.output) {
|
|
11763
|
-
|
|
12260
|
+
fs17.writeFileSync(this.output, out);
|
|
11764
12261
|
if (!isJsonMode()) printSuccess(`wrote ${this.output}`);
|
|
11765
12262
|
} else {
|
|
11766
12263
|
console.log(out);
|
|
@@ -11773,7 +12270,7 @@ var ProjectInspectCommand = class extends Command5 {
|
|
|
11773
12270
|
}
|
|
11774
12271
|
async runManifest(ctx) {
|
|
11775
12272
|
const entitiesDir = this.resolveEntitiesDir(ctx);
|
|
11776
|
-
if (!entitiesDir || !
|
|
12273
|
+
if (!entitiesDir || !fs17.existsSync(entitiesDir)) {
|
|
11777
12274
|
printError(`Directory not found: ${entitiesDir ?? "(no entities/ dir)"}`);
|
|
11778
12275
|
return 1;
|
|
11779
12276
|
}
|
|
@@ -11906,9 +12403,9 @@ function formatStatsConsole(result) {
|
|
|
11906
12403
|
lines.push("");
|
|
11907
12404
|
return lines.join("\n");
|
|
11908
12405
|
}
|
|
11909
|
-
var ProjectGraphCommand = class extends
|
|
12406
|
+
var ProjectGraphCommand = class extends Command7 {
|
|
11910
12407
|
static paths = [["project", "graph"]];
|
|
11911
|
-
static usage =
|
|
12408
|
+
static usage = Command7.Usage({
|
|
11912
12409
|
description: "Visualize the entity-relationship graph in a browser",
|
|
11913
12410
|
examples: [
|
|
11914
12411
|
["Open interactive graph viewer", "codegen project graph"],
|
|
@@ -11916,11 +12413,11 @@ var ProjectGraphCommand = class extends Command5 {
|
|
|
11916
12413
|
["Write graph JSON to file", "codegen project graph --output graph.json"]
|
|
11917
12414
|
]
|
|
11918
12415
|
});
|
|
11919
|
-
dir =
|
|
11920
|
-
output =
|
|
11921
|
-
json =
|
|
11922
|
-
cwd =
|
|
11923
|
-
configPath =
|
|
12416
|
+
dir = Option7.String({ required: false });
|
|
12417
|
+
output = Option7.String("--output,-o", { required: false });
|
|
12418
|
+
json = Option7.Boolean("--json", false);
|
|
12419
|
+
cwd = Option7.String("--cwd", { required: false });
|
|
12420
|
+
configPath = Option7.String("--config", { required: false });
|
|
11924
12421
|
async execute() {
|
|
11925
12422
|
if (this.json) setJsonMode(true);
|
|
11926
12423
|
const ctx = await loadContext({
|
|
@@ -11929,17 +12426,17 @@ var ProjectGraphCommand = class extends Command5 {
|
|
|
11929
12426
|
json: this.json,
|
|
11930
12427
|
skipDetection: true
|
|
11931
12428
|
});
|
|
11932
|
-
const entitiesDir = this.dir ?
|
|
11933
|
-
if (!
|
|
12429
|
+
const entitiesDir = this.dir ? path28.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path28.resolve(ctx.cwd, "entities");
|
|
12430
|
+
if (!fs17.existsSync(entitiesDir)) {
|
|
11934
12431
|
printError(`Entity directory not found: ${entitiesDir}`);
|
|
11935
12432
|
return 1;
|
|
11936
12433
|
}
|
|
11937
12434
|
const relCandidates = [
|
|
11938
|
-
|
|
11939
|
-
|
|
11940
|
-
|
|
12435
|
+
path28.resolve(path28.dirname(entitiesDir), "relationships"),
|
|
12436
|
+
path28.resolve(entitiesDir, "relationships"),
|
|
12437
|
+
path28.resolve(ctx.cwd, "relationships")
|
|
11941
12438
|
];
|
|
11942
|
-
const relationshipsDir = relCandidates.find((d) =>
|
|
12439
|
+
const relationshipsDir = relCandidates.find((d) => fs17.existsSync(d));
|
|
11943
12440
|
const result = await analyzeDomain(entitiesDir, relationshipsDir);
|
|
11944
12441
|
const serialized = serializeDomainGraph(result.graph);
|
|
11945
12442
|
if (isJsonMode()) {
|
|
@@ -11953,20 +12450,20 @@ var ProjectGraphCommand = class extends Command5 {
|
|
|
11953
12450
|
return 0;
|
|
11954
12451
|
}
|
|
11955
12452
|
if (this.output) {
|
|
11956
|
-
const outPath =
|
|
11957
|
-
|
|
12453
|
+
const outPath = path28.resolve(ctx.cwd, this.output);
|
|
12454
|
+
fs17.writeFileSync(outPath, JSON.stringify(serialized, null, 2));
|
|
11958
12455
|
printSuccess(`Graph written to ${outPath}`);
|
|
11959
12456
|
printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
|
|
11960
12457
|
return 0;
|
|
11961
12458
|
}
|
|
11962
12459
|
const os = await import("os");
|
|
11963
|
-
const tmpDir =
|
|
11964
|
-
const graphPath =
|
|
11965
|
-
|
|
11966
|
-
const viewerDir =
|
|
11967
|
-
const viewerDist =
|
|
11968
|
-
if (
|
|
11969
|
-
|
|
12460
|
+
const tmpDir = fs17.mkdtempSync(path28.join(os.default.tmpdir(), "codegen-graph-"));
|
|
12461
|
+
const graphPath = path28.join(tmpDir, "graph.json");
|
|
12462
|
+
fs17.writeFileSync(graphPath, JSON.stringify(serialized, null, 2));
|
|
12463
|
+
const viewerDir = path28.resolve(import.meta.dirname, "..", "..", "..", "tools", "schema-graph-viewer");
|
|
12464
|
+
const viewerDist = path28.join(viewerDir, "dist", "index.html");
|
|
12465
|
+
if (fs17.existsSync(viewerDist)) {
|
|
12466
|
+
fs17.copyFileSync(graphPath, path28.join(viewerDir, "dist", "graph.json"));
|
|
11970
12467
|
printSuccess("Graph exported");
|
|
11971
12468
|
printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
|
|
11972
12469
|
printInfo(`Graph JSON: ${graphPath}`);
|
|
@@ -11988,18 +12485,19 @@ var projectNoun = {
|
|
|
11988
12485
|
ProjectConfigCommand,
|
|
11989
12486
|
ProjectInspectCommand,
|
|
11990
12487
|
ProjectGraphCommand,
|
|
11991
|
-
ProjectUpgradeOpenapiCommand
|
|
12488
|
+
ProjectUpgradeOpenapiCommand,
|
|
12489
|
+
ProjectUpdateCommand
|
|
11992
12490
|
],
|
|
11993
|
-
summary:
|
|
11994
|
-
hints:
|
|
12491
|
+
summary: summary4,
|
|
12492
|
+
hints: hints4
|
|
11995
12493
|
};
|
|
11996
12494
|
var project_default = projectNoun;
|
|
11997
12495
|
|
|
11998
12496
|
// src/cli/commands/dev.ts
|
|
11999
|
-
import
|
|
12000
|
-
import
|
|
12497
|
+
import fs18 from "fs";
|
|
12498
|
+
import path29 from "path";
|
|
12001
12499
|
import { execSync as execSync3, spawn, spawnSync } from "child_process";
|
|
12002
|
-
import { Command as
|
|
12500
|
+
import { Command as Command8, Option as Option8 } from "clipanion";
|
|
12003
12501
|
var DEFAULT_APP_PORT = 3e3;
|
|
12004
12502
|
var DEFAULT_PG_PORT = 5433;
|
|
12005
12503
|
var DEFAULT_REDIS_PORT = 6380;
|
|
@@ -12030,33 +12528,33 @@ function getRedisPort(_ctx) {
|
|
|
12030
12528
|
return Number(process.env.DEV_REDIS_PORT ?? DEFAULT_REDIS_PORT);
|
|
12031
12529
|
}
|
|
12032
12530
|
function composeFilePath(cwd) {
|
|
12033
|
-
const devPath =
|
|
12034
|
-
if (
|
|
12035
|
-
const rootPath =
|
|
12036
|
-
if (
|
|
12531
|
+
const devPath = path29.join(cwd, COMPOSE_FILE);
|
|
12532
|
+
if (fs18.existsSync(devPath)) return devPath;
|
|
12533
|
+
const rootPath = path29.join(cwd, "docker-compose.yml");
|
|
12534
|
+
if (fs18.existsSync(rootPath)) return rootPath;
|
|
12037
12535
|
return devPath;
|
|
12038
12536
|
}
|
|
12039
12537
|
function pidFilePath(cwd) {
|
|
12040
|
-
return
|
|
12538
|
+
return path29.join(cwd, PID_FILE);
|
|
12041
12539
|
}
|
|
12042
12540
|
function readAppPid(cwd) {
|
|
12043
12541
|
const p = pidFilePath(cwd);
|
|
12044
|
-
if (!
|
|
12045
|
-
const pid = parseInt(
|
|
12542
|
+
if (!fs18.existsSync(p)) return null;
|
|
12543
|
+
const pid = parseInt(fs18.readFileSync(p, "utf-8").trim(), 10);
|
|
12046
12544
|
if (isNaN(pid)) return null;
|
|
12047
12545
|
try {
|
|
12048
12546
|
process.kill(pid, 0);
|
|
12049
12547
|
return pid;
|
|
12050
12548
|
} catch {
|
|
12051
|
-
|
|
12549
|
+
fs18.rmSync(p, { force: true });
|
|
12052
12550
|
return null;
|
|
12053
12551
|
}
|
|
12054
12552
|
}
|
|
12055
12553
|
function writeAppPid(cwd, pid) {
|
|
12056
|
-
|
|
12554
|
+
fs18.writeFileSync(pidFilePath(cwd), String(pid));
|
|
12057
12555
|
}
|
|
12058
12556
|
function clearAppPid(cwd) {
|
|
12059
|
-
|
|
12557
|
+
fs18.rmSync(pidFilePath(cwd), { force: true });
|
|
12060
12558
|
}
|
|
12061
12559
|
function checkPostgres(cwd, port) {
|
|
12062
12560
|
const r = runCmd(`docker exec codegen-dev-postgres pg_isready -U postgres`, cwd, {
|
|
@@ -12096,8 +12594,10 @@ function checkApp(cwd, port) {
|
|
|
12096
12594
|
};
|
|
12097
12595
|
}
|
|
12098
12596
|
function listEntityNames(ctx) {
|
|
12099
|
-
if (!ctx.entitiesDir || !
|
|
12100
|
-
return
|
|
12597
|
+
if (!ctx.entitiesDir || !fs18.existsSync(ctx.entitiesDir)) return [];
|
|
12598
|
+
return findYamlFiles(ctx.entitiesDir).map(
|
|
12599
|
+
(f) => path29.basename(f).replace(/\.ya?ml$/, "")
|
|
12600
|
+
);
|
|
12101
12601
|
}
|
|
12102
12602
|
function formatServiceLine(svc) {
|
|
12103
12603
|
const icon = svc.healthy ? theme.success(icons.check) : theme.error(icons.error);
|
|
@@ -12106,8 +12606,8 @@ function formatServiceLine(svc) {
|
|
|
12106
12606
|
return `${icon} ${svc.name.padEnd(12)} ${theme.muted(`${svc.host}:${svc.port}`)} ${status}${pidStr}`;
|
|
12107
12607
|
}
|
|
12108
12608
|
function ensureComposeFile(cwd, pgPort, redisPort) {
|
|
12109
|
-
const composePath =
|
|
12110
|
-
if (
|
|
12609
|
+
const composePath = path29.join(cwd, COMPOSE_FILE);
|
|
12610
|
+
if (fs18.existsSync(composePath)) return composePath;
|
|
12111
12611
|
const content = `# Auto-generated by codegen dev
|
|
12112
12612
|
# Ports offset from defaults to avoid conflicts with other local services.
|
|
12113
12613
|
services:
|
|
@@ -12142,22 +12642,22 @@ services:
|
|
|
12142
12642
|
volumes:
|
|
12143
12643
|
codegen-dev-pgdata:
|
|
12144
12644
|
`;
|
|
12145
|
-
|
|
12645
|
+
fs18.writeFileSync(composePath, content);
|
|
12146
12646
|
return composePath;
|
|
12147
12647
|
}
|
|
12148
|
-
var DevUpCommand = class extends
|
|
12648
|
+
var DevUpCommand = class extends Command8 {
|
|
12149
12649
|
static paths = [["dev", "up"]];
|
|
12150
|
-
static usage =
|
|
12650
|
+
static usage = Command8.Usage({
|
|
12151
12651
|
description: "Start Docker services (Postgres + Redis), run migrations, start the NestJS app",
|
|
12152
12652
|
examples: [
|
|
12153
12653
|
["Start everything", "codegen dev up"],
|
|
12154
12654
|
["Skip app start (services only)", "codegen dev up --no-app"]
|
|
12155
12655
|
]
|
|
12156
12656
|
});
|
|
12157
|
-
noApp =
|
|
12158
|
-
json =
|
|
12159
|
-
cwd =
|
|
12160
|
-
configPath =
|
|
12657
|
+
noApp = Option8.Boolean("--no-app", false);
|
|
12658
|
+
json = Option8.Boolean("--json", false);
|
|
12659
|
+
cwd = Option8.String("--cwd", { required: false });
|
|
12660
|
+
configPath = Option8.String("--config", { required: false });
|
|
12161
12661
|
async execute() {
|
|
12162
12662
|
if (this.json) setJsonMode(true);
|
|
12163
12663
|
const ctx = await loadContext({
|
|
@@ -12191,7 +12691,7 @@ var DevUpCommand = class extends Command6 {
|
|
|
12191
12691
|
if (!pgReady) printWarning("postgres did not become healthy in time");
|
|
12192
12692
|
if (!redisReady) printWarning("redis did not become healthy in time");
|
|
12193
12693
|
const drizzleConfig = ["drizzle.config.ts", "drizzle.config.js"].find(
|
|
12194
|
-
(f) =>
|
|
12694
|
+
(f) => fs18.existsSync(path29.join(ctx.cwd, f))
|
|
12195
12695
|
);
|
|
12196
12696
|
if (drizzleConfig) {
|
|
12197
12697
|
if (!isJsonMode()) printInfo("pushing database schema...");
|
|
@@ -12211,8 +12711,8 @@ var DevUpCommand = class extends Command6 {
|
|
|
12211
12711
|
if (!isJsonMode()) printInfo("starting NestJS app...");
|
|
12212
12712
|
const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
|
|
12213
12713
|
const redisUrl = `redis://localhost:${redisPort}`;
|
|
12214
|
-
const logFile =
|
|
12215
|
-
const logFd =
|
|
12714
|
+
const logFile = path29.join(ctx.cwd, ".dev-app.log");
|
|
12715
|
+
const logFd = fs18.openSync(logFile, "a");
|
|
12216
12716
|
const child = spawn("bun", ["src/main.ts"], {
|
|
12217
12717
|
cwd: ctx.cwd,
|
|
12218
12718
|
detached: true,
|
|
@@ -12225,7 +12725,7 @@ var DevUpCommand = class extends Command6 {
|
|
|
12225
12725
|
}
|
|
12226
12726
|
});
|
|
12227
12727
|
child.unref();
|
|
12228
|
-
|
|
12728
|
+
fs18.closeSync(logFd);
|
|
12229
12729
|
if (child.pid) {
|
|
12230
12730
|
writeAppPid(ctx.cwd, child.pid);
|
|
12231
12731
|
spawnSync("sleep", ["2"]);
|
|
@@ -12255,15 +12755,15 @@ var DevUpCommand = class extends Command6 {
|
|
|
12255
12755
|
return 0;
|
|
12256
12756
|
}
|
|
12257
12757
|
};
|
|
12258
|
-
var DevDownCommand = class extends
|
|
12758
|
+
var DevDownCommand = class extends Command8 {
|
|
12259
12759
|
static paths = [["dev", "down"]];
|
|
12260
|
-
static usage =
|
|
12760
|
+
static usage = Command8.Usage({
|
|
12261
12761
|
description: "Stop Docker services and the NestJS app"
|
|
12262
12762
|
});
|
|
12263
|
-
volumes =
|
|
12264
|
-
json =
|
|
12265
|
-
cwd =
|
|
12266
|
-
configPath =
|
|
12763
|
+
volumes = Option8.Boolean("--volumes,-v", false);
|
|
12764
|
+
json = Option8.Boolean("--json", false);
|
|
12765
|
+
cwd = Option8.String("--cwd", { required: false });
|
|
12766
|
+
configPath = Option8.String("--config", { required: false });
|
|
12267
12767
|
async execute() {
|
|
12268
12768
|
if (this.json) setJsonMode(true);
|
|
12269
12769
|
const ctx = await loadContext({
|
|
@@ -12299,14 +12799,14 @@ var DevDownCommand = class extends Command6 {
|
|
|
12299
12799
|
return 0;
|
|
12300
12800
|
}
|
|
12301
12801
|
};
|
|
12302
|
-
var DevStatusCommand = class extends
|
|
12802
|
+
var DevStatusCommand = class extends Command8 {
|
|
12303
12803
|
static paths = [["dev", "status"]];
|
|
12304
|
-
static usage =
|
|
12804
|
+
static usage = Command8.Usage({
|
|
12305
12805
|
description: "Show status of Docker services and the NestJS app"
|
|
12306
12806
|
});
|
|
12307
|
-
json =
|
|
12308
|
-
cwd =
|
|
12309
|
-
configPath =
|
|
12807
|
+
json = Option8.Boolean("--json", false);
|
|
12808
|
+
cwd = Option8.String("--cwd", { required: false });
|
|
12809
|
+
configPath = Option8.String("--config", { required: false });
|
|
12310
12810
|
async execute() {
|
|
12311
12811
|
if (this.json) setJsonMode(true);
|
|
12312
12812
|
const ctx = await loadContext({
|
|
@@ -12334,9 +12834,9 @@ var DevStatusCommand = class extends Command6 {
|
|
|
12334
12834
|
return 0;
|
|
12335
12835
|
}
|
|
12336
12836
|
};
|
|
12337
|
-
var DevLogsCommand = class extends
|
|
12837
|
+
var DevLogsCommand = class extends Command8 {
|
|
12338
12838
|
static paths = [["dev", "logs"]];
|
|
12339
|
-
static usage =
|
|
12839
|
+
static usage = Command8.Usage({
|
|
12340
12840
|
description: "Tail application and Docker service logs",
|
|
12341
12841
|
examples: [
|
|
12342
12842
|
["Tail app logs", "codegen dev logs"],
|
|
@@ -12344,11 +12844,11 @@ var DevLogsCommand = class extends Command6 {
|
|
|
12344
12844
|
["Show last N lines", "codegen dev logs --tail 50"]
|
|
12345
12845
|
]
|
|
12346
12846
|
});
|
|
12347
|
-
docker =
|
|
12348
|
-
tail =
|
|
12349
|
-
json =
|
|
12350
|
-
cwd =
|
|
12351
|
-
configPath =
|
|
12847
|
+
docker = Option8.Boolean("--docker", false);
|
|
12848
|
+
tail = Option8.String("--tail", "30");
|
|
12849
|
+
json = Option8.Boolean("--json", false);
|
|
12850
|
+
cwd = Option8.String("--cwd", { required: false });
|
|
12851
|
+
configPath = Option8.String("--config", { required: false });
|
|
12352
12852
|
async execute() {
|
|
12353
12853
|
if (this.json) setJsonMode(true);
|
|
12354
12854
|
const ctx = await loadContext({
|
|
@@ -12369,8 +12869,8 @@ var DevLogsCommand = class extends Command6 {
|
|
|
12369
12869
|
}
|
|
12370
12870
|
return 0;
|
|
12371
12871
|
}
|
|
12372
|
-
const logFile =
|
|
12373
|
-
if (!
|
|
12872
|
+
const logFile = path29.join(ctx.cwd, ".dev-app.log");
|
|
12873
|
+
if (!fs18.existsSync(logFile)) {
|
|
12374
12874
|
printInfo("no app logs found \u2014 is the app running?");
|
|
12375
12875
|
return 0;
|
|
12376
12876
|
}
|
|
@@ -12382,14 +12882,14 @@ var DevLogsCommand = class extends Command6 {
|
|
|
12382
12882
|
return 0;
|
|
12383
12883
|
}
|
|
12384
12884
|
};
|
|
12385
|
-
var DevRestartCommand = class extends
|
|
12885
|
+
var DevRestartCommand = class extends Command8 {
|
|
12386
12886
|
static paths = [["dev", "restart"]];
|
|
12387
|
-
static usage =
|
|
12887
|
+
static usage = Command8.Usage({
|
|
12388
12888
|
description: "Restart the NestJS app (keep Docker services running)"
|
|
12389
12889
|
});
|
|
12390
|
-
json =
|
|
12391
|
-
cwd =
|
|
12392
|
-
configPath =
|
|
12890
|
+
json = Option8.Boolean("--json", false);
|
|
12891
|
+
cwd = Option8.String("--cwd", { required: false });
|
|
12892
|
+
configPath = Option8.String("--config", { required: false });
|
|
12393
12893
|
async execute() {
|
|
12394
12894
|
if (this.json) setJsonMode(true);
|
|
12395
12895
|
const ctx = await loadContext({
|
|
@@ -12413,8 +12913,8 @@ var DevRestartCommand = class extends Command6 {
|
|
|
12413
12913
|
spawnSync("sleep", ["1"]);
|
|
12414
12914
|
const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
|
|
12415
12915
|
const redisUrl = `redis://localhost:${redisPort}`;
|
|
12416
|
-
const logFile =
|
|
12417
|
-
const logFd =
|
|
12916
|
+
const logFile = path29.join(ctx.cwd, ".dev-app.log");
|
|
12917
|
+
const logFd = fs18.openSync(logFile, "a");
|
|
12418
12918
|
const child = spawn("bun", ["src/main.ts"], {
|
|
12419
12919
|
cwd: ctx.cwd,
|
|
12420
12920
|
detached: true,
|
|
@@ -12427,7 +12927,7 @@ var DevRestartCommand = class extends Command6 {
|
|
|
12427
12927
|
}
|
|
12428
12928
|
});
|
|
12429
12929
|
child.unref();
|
|
12430
|
-
|
|
12930
|
+
fs18.closeSync(logFd);
|
|
12431
12931
|
if (child.pid) {
|
|
12432
12932
|
writeAppPid(ctx.cwd, child.pid);
|
|
12433
12933
|
spawnSync("sleep", ["2"]);
|
|
@@ -12489,7 +12989,7 @@ function renderDevStatus(ctx) {
|
|
|
12489
12989
|
{ command: "/dev-test", description: "Run test suite + endpoint verification" }
|
|
12490
12990
|
]);
|
|
12491
12991
|
}
|
|
12492
|
-
async function
|
|
12992
|
+
async function summary5(ctx) {
|
|
12493
12993
|
const pgPort = getPgPort(ctx);
|
|
12494
12994
|
const redisPort = getRedisPort(ctx);
|
|
12495
12995
|
const appPort = getAppPort(ctx);
|
|
@@ -12513,7 +13013,7 @@ async function summary4(ctx) {
|
|
|
12513
13013
|
footer: `${running}/${total} services healthy`
|
|
12514
13014
|
};
|
|
12515
13015
|
}
|
|
12516
|
-
async function
|
|
13016
|
+
async function hints5(ctx) {
|
|
12517
13017
|
const app = checkApp(ctx.cwd, getAppPort(ctx));
|
|
12518
13018
|
if (!app.healthy) {
|
|
12519
13019
|
return [
|
|
@@ -12536,21 +13036,20 @@ var devNoun = {
|
|
|
12536
13036
|
DevLogsCommand,
|
|
12537
13037
|
DevRestartCommand
|
|
12538
13038
|
],
|
|
12539
|
-
summary:
|
|
12540
|
-
hints:
|
|
13039
|
+
summary: summary5,
|
|
13040
|
+
hints: hints5
|
|
12541
13041
|
};
|
|
12542
13042
|
var dev_default = devNoun;
|
|
12543
13043
|
|
|
12544
13044
|
// src/cli/commands/relationship.ts
|
|
12545
|
-
import
|
|
12546
|
-
import
|
|
12547
|
-
import { Command as
|
|
13045
|
+
import fs19 from "fs";
|
|
13046
|
+
import path30 from "path";
|
|
13047
|
+
import { Command as Command9, Option as Option9 } from "clipanion";
|
|
12548
13048
|
function listRelationshipYamls2(dir) {
|
|
12549
|
-
if (!
|
|
12550
|
-
return
|
|
12551
|
-
|
|
12552
|
-
|
|
12553
|
-
}).map((f) => path27.join(dir, f));
|
|
13049
|
+
if (!fs19.existsSync(dir)) return [];
|
|
13050
|
+
return findYamlFiles(dir).filter(
|
|
13051
|
+
(full) => detectYamlType(full) === "relationship"
|
|
13052
|
+
);
|
|
12554
13053
|
}
|
|
12555
13054
|
function summarizeRelationshipFile(filePath) {
|
|
12556
13055
|
const result = loadRelationshipFromYaml(filePath);
|
|
@@ -12570,8 +13069,8 @@ function summarizeRelationshipFile(filePath) {
|
|
|
12570
13069
|
function padRight2(s, n) {
|
|
12571
13070
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
12572
13071
|
}
|
|
12573
|
-
async function
|
|
12574
|
-
const relDir =
|
|
13072
|
+
async function summary6(ctx) {
|
|
13073
|
+
const relDir = path30.resolve(ctx.cwd, "relationships");
|
|
12575
13074
|
const files = listRelationshipYamls2(relDir);
|
|
12576
13075
|
if (files.length === 0) {
|
|
12577
13076
|
return {
|
|
@@ -12603,16 +13102,16 @@ async function summary5(ctx) {
|
|
|
12603
13102
|
footer: `${rows.length} relationships`
|
|
12604
13103
|
};
|
|
12605
13104
|
}
|
|
12606
|
-
async function
|
|
13105
|
+
async function hints6(_ctx) {
|
|
12607
13106
|
return [
|
|
12608
13107
|
{ command: "codegen relationship new <file>", description: "Generate one relationship" },
|
|
12609
13108
|
{ command: "codegen relationship new --all", description: "Generate all relationships" },
|
|
12610
13109
|
{ command: "codegen relationship list", description: "List relationship definitions" }
|
|
12611
13110
|
];
|
|
12612
13111
|
}
|
|
12613
|
-
var RelationshipNewCommand = class extends
|
|
13112
|
+
var RelationshipNewCommand = class extends Command9 {
|
|
12614
13113
|
static paths = [["relationship", "new"]];
|
|
12615
|
-
static usage =
|
|
13114
|
+
static usage = Command9.Usage({
|
|
12616
13115
|
description: "Generate code for one or more relationships from YAML",
|
|
12617
13116
|
examples: [
|
|
12618
13117
|
["Generate a single relationship", "codegen relationship new relationships/person_organization.yaml"],
|
|
@@ -12620,13 +13119,13 @@ var RelationshipNewCommand = class extends Command7 {
|
|
|
12620
13119
|
["Preview without writing", "codegen relationship new relationships/person_organization.yaml --dry-run"]
|
|
12621
13120
|
]
|
|
12622
13121
|
});
|
|
12623
|
-
yaml =
|
|
12624
|
-
all =
|
|
12625
|
-
dryRun =
|
|
12626
|
-
force =
|
|
12627
|
-
json =
|
|
12628
|
-
cwd =
|
|
12629
|
-
configPath =
|
|
13122
|
+
yaml = Option9.String({ required: false });
|
|
13123
|
+
all = Option9.Boolean("--all", false);
|
|
13124
|
+
dryRun = Option9.Boolean("--dry-run", false);
|
|
13125
|
+
force = Option9.Boolean("--force", false);
|
|
13126
|
+
json = Option9.Boolean("--json", false);
|
|
13127
|
+
cwd = Option9.String("--cwd", { required: false });
|
|
13128
|
+
configPath = Option9.String("--config", { required: false });
|
|
12630
13129
|
async execute() {
|
|
12631
13130
|
if (this.json) setJsonMode(true);
|
|
12632
13131
|
const ctx = await loadContext({
|
|
@@ -12641,14 +13140,14 @@ var RelationshipNewCommand = class extends Command7 {
|
|
|
12641
13140
|
}
|
|
12642
13141
|
let targets = [];
|
|
12643
13142
|
if (this.all) {
|
|
12644
|
-
const dir =
|
|
13143
|
+
const dir = path30.resolve(ctx.cwd, "relationships");
|
|
12645
13144
|
targets = listRelationshipYamls2(dir);
|
|
12646
13145
|
if (targets.length === 0) {
|
|
12647
13146
|
printError(`No relationship YAML files found in ${dir}`);
|
|
12648
13147
|
return 1;
|
|
12649
13148
|
}
|
|
12650
13149
|
} else if (this.yaml) {
|
|
12651
|
-
targets = [
|
|
13150
|
+
targets = [path30.resolve(ctx.cwd, this.yaml)];
|
|
12652
13151
|
} else {
|
|
12653
13152
|
printError("Missing YAML path. Pass a file or --all.");
|
|
12654
13153
|
return 2;
|
|
@@ -12665,7 +13164,7 @@ var RelationshipNewCommand = class extends Command7 {
|
|
|
12665
13164
|
}
|
|
12666
13165
|
if (invalid.length > 0) {
|
|
12667
13166
|
for (const i of invalid) {
|
|
12668
|
-
printError(`${
|
|
13167
|
+
printError(`${path30.basename(i.file)} \u2014 ${i.message}`);
|
|
12669
13168
|
}
|
|
12670
13169
|
if (!isJsonMode()) return 1;
|
|
12671
13170
|
}
|
|
@@ -12696,7 +13195,7 @@ var RelationshipNewCommand = class extends Command7 {
|
|
|
12696
13195
|
}
|
|
12697
13196
|
const succeeded = [];
|
|
12698
13197
|
const failed = [
|
|
12699
|
-
...invalid.map((i) => ({ name:
|
|
13198
|
+
...invalid.map((i) => ({ name: path30.basename(i.file), file: i.file, message: i.message }))
|
|
12700
13199
|
];
|
|
12701
13200
|
for (const v of validated) {
|
|
12702
13201
|
if (!isJsonMode()) {
|
|
@@ -12715,8 +13214,8 @@ var RelationshipNewCommand = class extends Command7 {
|
|
|
12715
13214
|
if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
|
|
12716
13215
|
}
|
|
12717
13216
|
}
|
|
12718
|
-
const entitiesDir = ctx.entitiesDir ??
|
|
12719
|
-
const relationshipsDir =
|
|
13217
|
+
const entitiesDir = ctx.entitiesDir ?? path30.resolve(ctx.cwd, "entities");
|
|
13218
|
+
const relationshipsDir = path30.resolve(ctx.cwd, "relationships");
|
|
12720
13219
|
const generatedDir = resolveGeneratedDir(ctx);
|
|
12721
13220
|
const architecture = resolveArchitecture(ctx);
|
|
12722
13221
|
let barrelResult = null;
|
|
@@ -12761,21 +13260,21 @@ var RelationshipNewCommand = class extends Command7 {
|
|
|
12761
13260
|
}
|
|
12762
13261
|
if (barrelResult) {
|
|
12763
13262
|
printInfo(
|
|
12764
|
-
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${
|
|
13263
|
+
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path30.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path30.relative(ctx.cwd, barrelResult.schemaBarrel)}`
|
|
12765
13264
|
);
|
|
12766
13265
|
}
|
|
12767
13266
|
}
|
|
12768
13267
|
return failed.length === 0 ? 0 : 1;
|
|
12769
13268
|
}
|
|
12770
13269
|
};
|
|
12771
|
-
var RelationshipListCommand = class extends
|
|
13270
|
+
var RelationshipListCommand = class extends Command9 {
|
|
12772
13271
|
static paths = [["relationship", "list"]];
|
|
12773
|
-
static usage =
|
|
13272
|
+
static usage = Command9.Usage({
|
|
12774
13273
|
description: "List defined relationships as a table"
|
|
12775
13274
|
});
|
|
12776
|
-
json =
|
|
12777
|
-
cwd =
|
|
12778
|
-
configPath =
|
|
13275
|
+
json = Option9.Boolean("--json", false);
|
|
13276
|
+
cwd = Option9.String("--cwd", { required: false });
|
|
13277
|
+
configPath = Option9.String("--config", { required: false });
|
|
12779
13278
|
async execute() {
|
|
12780
13279
|
if (this.json) setJsonMode(true);
|
|
12781
13280
|
const ctx = await loadContext({
|
|
@@ -12784,7 +13283,7 @@ var RelationshipListCommand = class extends Command7 {
|
|
|
12784
13283
|
json: this.json,
|
|
12785
13284
|
skipDetection: true
|
|
12786
13285
|
});
|
|
12787
|
-
const relDir =
|
|
13286
|
+
const relDir = path30.resolve(ctx.cwd, "relationships");
|
|
12788
13287
|
const files = listRelationshipYamls2(relDir);
|
|
12789
13288
|
if (files.length === 0) {
|
|
12790
13289
|
printInfo("No relationship definitions found.");
|
|
@@ -12818,14 +13317,14 @@ var RelationshipListCommand = class extends Command7 {
|
|
|
12818
13317
|
var relationshipNoun = {
|
|
12819
13318
|
name: "relationship",
|
|
12820
13319
|
commandClasses: [RelationshipNewCommand, RelationshipListCommand],
|
|
12821
|
-
summary:
|
|
12822
|
-
hints:
|
|
13320
|
+
summary: summary6,
|
|
13321
|
+
hints: hints6
|
|
12823
13322
|
};
|
|
12824
13323
|
var relationship_default = relationshipNoun;
|
|
12825
13324
|
|
|
12826
13325
|
// src/cli/commands/junction.ts
|
|
12827
|
-
import
|
|
12828
|
-
import { Command as
|
|
13326
|
+
import path31 from "path";
|
|
13327
|
+
import { Command as Command10, Option as Option10 } from "clipanion";
|
|
12829
13328
|
function summarizeJunctionFile(filePath) {
|
|
12830
13329
|
const result = loadJunctionFromYaml(filePath);
|
|
12831
13330
|
if (!result.success) return null;
|
|
@@ -12846,8 +13345,8 @@ function summarizeJunctionFile(filePath) {
|
|
|
12846
13345
|
function padRight3(s, n) {
|
|
12847
13346
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
12848
13347
|
}
|
|
12849
|
-
async function
|
|
12850
|
-
const junctionDir =
|
|
13348
|
+
async function summary7(ctx) {
|
|
13349
|
+
const junctionDir = path31.resolve(ctx.cwd, "junctions");
|
|
12851
13350
|
const files = listJunctionYamls(junctionDir);
|
|
12852
13351
|
if (files.length === 0) {
|
|
12853
13352
|
return {
|
|
@@ -12879,16 +13378,16 @@ async function summary6(ctx) {
|
|
|
12879
13378
|
footer: `${rows.length} junctions`
|
|
12880
13379
|
};
|
|
12881
13380
|
}
|
|
12882
|
-
async function
|
|
13381
|
+
async function hints7(_ctx) {
|
|
12883
13382
|
return [
|
|
12884
13383
|
{ command: "codegen junction new <file>", description: "Generate one junction" },
|
|
12885
13384
|
{ command: "codegen junction new --all", description: "Generate all junctions" },
|
|
12886
13385
|
{ command: "codegen junction list", description: "List junction definitions" }
|
|
12887
13386
|
];
|
|
12888
13387
|
}
|
|
12889
|
-
var JunctionNewCommand = class extends
|
|
13388
|
+
var JunctionNewCommand = class extends Command10 {
|
|
12890
13389
|
static paths = [["junction", "new"]];
|
|
12891
|
-
static usage =
|
|
13390
|
+
static usage = Command10.Usage({
|
|
12892
13391
|
description: "Generate code for one or more junctions from YAML",
|
|
12893
13392
|
examples: [
|
|
12894
13393
|
["Generate a single junction", "codegen junction new junctions/opportunity_contact.yaml"],
|
|
@@ -12896,13 +13395,13 @@ var JunctionNewCommand = class extends Command8 {
|
|
|
12896
13395
|
["Preview without writing", "codegen junction new junctions/opportunity_contact.yaml --dry-run"]
|
|
12897
13396
|
]
|
|
12898
13397
|
});
|
|
12899
|
-
yaml =
|
|
12900
|
-
all =
|
|
12901
|
-
dryRun =
|
|
12902
|
-
force =
|
|
12903
|
-
json =
|
|
12904
|
-
cwd =
|
|
12905
|
-
configPath =
|
|
13398
|
+
yaml = Option10.String({ required: false });
|
|
13399
|
+
all = Option10.Boolean("--all", false);
|
|
13400
|
+
dryRun = Option10.Boolean("--dry-run", false);
|
|
13401
|
+
force = Option10.Boolean("--force", false);
|
|
13402
|
+
json = Option10.Boolean("--json", false);
|
|
13403
|
+
cwd = Option10.String("--cwd", { required: false });
|
|
13404
|
+
configPath = Option10.String("--config", { required: false });
|
|
12906
13405
|
async execute() {
|
|
12907
13406
|
if (this.json) setJsonMode(true);
|
|
12908
13407
|
const ctx = await loadContext({
|
|
@@ -12917,14 +13416,14 @@ var JunctionNewCommand = class extends Command8 {
|
|
|
12917
13416
|
}
|
|
12918
13417
|
let targets = [];
|
|
12919
13418
|
if (this.all) {
|
|
12920
|
-
const dir =
|
|
13419
|
+
const dir = path31.resolve(ctx.cwd, "junctions");
|
|
12921
13420
|
targets = listJunctionYamls(dir);
|
|
12922
13421
|
if (targets.length === 0) {
|
|
12923
13422
|
printError(`No junction YAML files found in ${dir}`);
|
|
12924
13423
|
return 1;
|
|
12925
13424
|
}
|
|
12926
13425
|
} else if (this.yaml) {
|
|
12927
|
-
targets = [
|
|
13426
|
+
targets = [path31.resolve(ctx.cwd, this.yaml)];
|
|
12928
13427
|
} else {
|
|
12929
13428
|
printError("Missing YAML path. Pass a file or --all.");
|
|
12930
13429
|
return 2;
|
|
@@ -12943,7 +13442,7 @@ var JunctionNewCommand = class extends Command8 {
|
|
|
12943
13442
|
}
|
|
12944
13443
|
if (invalid.length > 0) {
|
|
12945
13444
|
for (const i of invalid) {
|
|
12946
|
-
printError(`${
|
|
13445
|
+
printError(`${path31.basename(i.file)} \u2014 ${i.message}`);
|
|
12947
13446
|
}
|
|
12948
13447
|
if (!isJsonMode()) return 1;
|
|
12949
13448
|
}
|
|
@@ -12974,7 +13473,7 @@ var JunctionNewCommand = class extends Command8 {
|
|
|
12974
13473
|
}
|
|
12975
13474
|
const succeeded = [];
|
|
12976
13475
|
const failed = [
|
|
12977
|
-
...invalid.map((i) => ({ name:
|
|
13476
|
+
...invalid.map((i) => ({ name: path31.basename(i.file), file: i.file, message: i.message }))
|
|
12978
13477
|
];
|
|
12979
13478
|
for (const v of validated) {
|
|
12980
13479
|
if (!isJsonMode()) {
|
|
@@ -12993,9 +13492,9 @@ var JunctionNewCommand = class extends Command8 {
|
|
|
12993
13492
|
if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
|
|
12994
13493
|
}
|
|
12995
13494
|
}
|
|
12996
|
-
const entitiesDir = ctx.entitiesDir ??
|
|
12997
|
-
const relationshipsDir =
|
|
12998
|
-
const junctionsDir =
|
|
13495
|
+
const entitiesDir = ctx.entitiesDir ?? path31.resolve(ctx.cwd, "entities");
|
|
13496
|
+
const relationshipsDir = path31.resolve(ctx.cwd, "relationships");
|
|
13497
|
+
const junctionsDir = path31.resolve(ctx.cwd, "junctions");
|
|
12999
13498
|
const generatedDir = resolveGeneratedDir(ctx);
|
|
13000
13499
|
const architecture = resolveArchitecture(ctx);
|
|
13001
13500
|
let barrelResult = null;
|
|
@@ -13041,21 +13540,21 @@ var JunctionNewCommand = class extends Command8 {
|
|
|
13041
13540
|
}
|
|
13042
13541
|
if (barrelResult) {
|
|
13043
13542
|
printInfo(
|
|
13044
|
-
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${
|
|
13543
|
+
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path31.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path31.relative(ctx.cwd, barrelResult.schemaBarrel)}`
|
|
13045
13544
|
);
|
|
13046
13545
|
}
|
|
13047
13546
|
}
|
|
13048
13547
|
return failed.length === 0 ? 0 : 1;
|
|
13049
13548
|
}
|
|
13050
13549
|
};
|
|
13051
|
-
var JunctionListCommand = class extends
|
|
13550
|
+
var JunctionListCommand = class extends Command10 {
|
|
13052
13551
|
static paths = [["junction", "list"]];
|
|
13053
|
-
static usage =
|
|
13552
|
+
static usage = Command10.Usage({
|
|
13054
13553
|
description: "List defined junctions as a table"
|
|
13055
13554
|
});
|
|
13056
|
-
json =
|
|
13057
|
-
cwd =
|
|
13058
|
-
configPath =
|
|
13555
|
+
json = Option10.Boolean("--json", false);
|
|
13556
|
+
cwd = Option10.String("--cwd", { required: false });
|
|
13557
|
+
configPath = Option10.String("--config", { required: false });
|
|
13059
13558
|
async execute() {
|
|
13060
13559
|
if (this.json) setJsonMode(true);
|
|
13061
13560
|
const ctx = await loadContext({
|
|
@@ -13064,7 +13563,7 @@ var JunctionListCommand = class extends Command8 {
|
|
|
13064
13563
|
json: this.json,
|
|
13065
13564
|
skipDetection: true
|
|
13066
13565
|
});
|
|
13067
|
-
const junctionDir =
|
|
13566
|
+
const junctionDir = path31.resolve(ctx.cwd, "junctions");
|
|
13068
13567
|
const files = listJunctionYamls(junctionDir);
|
|
13069
13568
|
if (files.length === 0) {
|
|
13070
13569
|
printInfo("No junction definitions found.");
|
|
@@ -13098,16 +13597,16 @@ var JunctionListCommand = class extends Command8 {
|
|
|
13098
13597
|
var junctionNoun = {
|
|
13099
13598
|
name: "junction",
|
|
13100
13599
|
commandClasses: [JunctionNewCommand, JunctionListCommand],
|
|
13101
|
-
summary:
|
|
13102
|
-
hints:
|
|
13600
|
+
summary: summary7,
|
|
13601
|
+
hints: hints7
|
|
13103
13602
|
};
|
|
13104
13603
|
var junction_default = junctionNoun;
|
|
13105
13604
|
|
|
13106
13605
|
// src/cli/commands/events.ts
|
|
13107
|
-
import
|
|
13108
|
-
import
|
|
13606
|
+
import fs20 from "fs";
|
|
13607
|
+
import path32 from "path";
|
|
13109
13608
|
import ts2 from "typescript";
|
|
13110
|
-
import { Command as
|
|
13609
|
+
import { Command as Command11, Option as Option11 } from "clipanion";
|
|
13111
13610
|
function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
|
|
13112
13611
|
const tier2 = [];
|
|
13113
13612
|
const tier1 = [];
|
|
@@ -13204,7 +13703,7 @@ function scanDirectoryForConsumers(rootDir, eventType) {
|
|
|
13204
13703
|
const tier1 = [];
|
|
13205
13704
|
let hasEventFlowImport = false;
|
|
13206
13705
|
for (const filePath of files) {
|
|
13207
|
-
const text2 =
|
|
13706
|
+
const text2 = fs20.readFileSync(filePath, "utf8");
|
|
13208
13707
|
const sourceFile = ts2.createSourceFile(
|
|
13209
13708
|
filePath,
|
|
13210
13709
|
text2,
|
|
@@ -13241,7 +13740,7 @@ function suggestEventTypes(target, known, limit = 3) {
|
|
|
13241
13740
|
return known.map((t) => ({ t, d: levenshtein(target, t) })).sort((a, b) => a.d - b.d).slice(0, limit).map((x) => x.t);
|
|
13242
13741
|
}
|
|
13243
13742
|
function renderConsumerReport(result, cwd) {
|
|
13244
|
-
const
|
|
13743
|
+
const rel2 = (p) => path32.relative(cwd, p) || p;
|
|
13245
13744
|
const lines = [];
|
|
13246
13745
|
const total = result.tier3.length + result.tier2.length + result.tier1.length;
|
|
13247
13746
|
lines.push(`Event: ${result.eventType}`);
|
|
@@ -13255,7 +13754,7 @@ function renderConsumerReport(result, cwd) {
|
|
|
13255
13754
|
const labelWidth = Math.max(...result.tier3.map((h) => h.triggerId.length)) + 2;
|
|
13256
13755
|
for (const h of result.tier3) {
|
|
13257
13756
|
const padded = h.triggerId.padEnd(labelWidth);
|
|
13258
|
-
lines.push(` - ${padded}(${
|
|
13757
|
+
lines.push(` - ${padded}(${rel2(h.sourceFile)}:${h.sourceLine})`);
|
|
13259
13758
|
}
|
|
13260
13759
|
}
|
|
13261
13760
|
lines.push(`Tier 2 \u2014 Direct invoke via publishAndStart (${result.tier2.length}):`);
|
|
@@ -13263,7 +13762,7 @@ function renderConsumerReport(result, cwd) {
|
|
|
13263
13762
|
lines.push(" - (none)");
|
|
13264
13763
|
} else {
|
|
13265
13764
|
for (const h of result.tier2) {
|
|
13266
|
-
lines.push(` - ${
|
|
13765
|
+
lines.push(` - ${rel2(h.sourceFile)}:${h.sourceLine}`);
|
|
13267
13766
|
}
|
|
13268
13767
|
}
|
|
13269
13768
|
lines.push(`Tier 1 \u2014 Subscribers (${result.tier1.length}):`);
|
|
@@ -13271,13 +13770,13 @@ function renderConsumerReport(result, cwd) {
|
|
|
13271
13770
|
lines.push(" - (none)");
|
|
13272
13771
|
} else {
|
|
13273
13772
|
for (const h of result.tier1) {
|
|
13274
|
-
lines.push(` - ${h.siteLabel} at ${
|
|
13773
|
+
lines.push(` - ${h.siteLabel} at ${rel2(h.sourceFile)}:${h.sourceLine}`);
|
|
13275
13774
|
}
|
|
13276
13775
|
}
|
|
13277
13776
|
return lines;
|
|
13278
13777
|
}
|
|
13279
13778
|
function runConsumersScan(opts) {
|
|
13280
|
-
const scanRoot = opts.scanRoot ??
|
|
13779
|
+
const scanRoot = opts.scanRoot ?? path32.join(opts.cwd, "src");
|
|
13281
13780
|
const handlersDir = opts.handlersDir ?? scanRoot;
|
|
13282
13781
|
const allTriggers = scanHandlerFiles(handlersDir);
|
|
13283
13782
|
const tier3 = allTriggers.filter((t) => t.event === opts.eventType).map((t) => ({
|
|
@@ -13286,8 +13785,8 @@ function runConsumersScan(opts) {
|
|
|
13286
13785
|
sourceFile: t.sourceFile,
|
|
13287
13786
|
sourceLine: t.sourceLine
|
|
13288
13787
|
}));
|
|
13289
|
-
const tier21 =
|
|
13290
|
-
const eventsGeneratedDir = opts.eventsGeneratedDir ??
|
|
13788
|
+
const tier21 = fs20.existsSync(scanRoot) ? scanDirectoryForConsumers(scanRoot, opts.eventType) : { tier2: [], tier1: [], hasEventFlowImport: false };
|
|
13789
|
+
const eventsGeneratedDir = opts.eventsGeneratedDir ?? path32.join(
|
|
13291
13790
|
resolveSubsystemsRootFromContext(opts.cwd, opts.config),
|
|
13292
13791
|
"events",
|
|
13293
13792
|
"generated"
|
|
@@ -13308,15 +13807,15 @@ function runConsumersScan(opts) {
|
|
|
13308
13807
|
function resolveSubsystemsRootFromContext(cwd, config) {
|
|
13309
13808
|
const configured = config?.paths?.subsystems;
|
|
13310
13809
|
if (typeof configured === "string" && configured.length > 0) {
|
|
13311
|
-
return
|
|
13810
|
+
return path32.resolve(cwd, configured);
|
|
13312
13811
|
}
|
|
13313
13812
|
const backendSrc = config?.paths?.backend_src;
|
|
13314
13813
|
const base = typeof backendSrc === "string" && backendSrc.length > 0 ? backendSrc : "src";
|
|
13315
|
-
return
|
|
13814
|
+
return path32.resolve(cwd, base, "shared", "subsystems");
|
|
13316
13815
|
}
|
|
13317
|
-
var EventsConsumersCommand = class extends
|
|
13816
|
+
var EventsConsumersCommand = class extends Command11 {
|
|
13318
13817
|
static paths = [["events", "consumers"]];
|
|
13319
|
-
static usage =
|
|
13818
|
+
static usage = Command11.Usage({
|
|
13320
13819
|
description: "List all consumers of an event across the three tiers",
|
|
13321
13820
|
examples: [
|
|
13322
13821
|
[
|
|
@@ -13325,10 +13824,10 @@ var EventsConsumersCommand = class extends Command9 {
|
|
|
13325
13824
|
]
|
|
13326
13825
|
]
|
|
13327
13826
|
});
|
|
13328
|
-
eventType =
|
|
13329
|
-
json =
|
|
13330
|
-
cwd =
|
|
13331
|
-
configPath =
|
|
13827
|
+
eventType = Option11.String({ required: true });
|
|
13828
|
+
json = Option11.Boolean("--json", false);
|
|
13829
|
+
cwd = Option11.String("--cwd", { required: false });
|
|
13830
|
+
configPath = Option11.String("--config", { required: false });
|
|
13332
13831
|
async execute() {
|
|
13333
13832
|
if (this.json) setJsonMode(true);
|
|
13334
13833
|
const ctx = await loadContext({
|
|
@@ -13373,7 +13872,7 @@ var EventsConsumersCommand = class extends Command9 {
|
|
|
13373
13872
|
return 0;
|
|
13374
13873
|
}
|
|
13375
13874
|
};
|
|
13376
|
-
async function
|
|
13875
|
+
async function summary8(_ctx) {
|
|
13377
13876
|
return {
|
|
13378
13877
|
title: "events",
|
|
13379
13878
|
body: [
|
|
@@ -13384,7 +13883,7 @@ async function summary7(_ctx) {
|
|
|
13384
13883
|
]
|
|
13385
13884
|
};
|
|
13386
13885
|
}
|
|
13387
|
-
async function
|
|
13886
|
+
async function hints8(_ctx) {
|
|
13388
13887
|
return [
|
|
13389
13888
|
{
|
|
13390
13889
|
command: "codegen events consumers <type>",
|
|
@@ -13395,14 +13894,14 @@ async function hints7(_ctx) {
|
|
|
13395
13894
|
var eventsNoun = {
|
|
13396
13895
|
name: "events",
|
|
13397
13896
|
commandClasses: [EventsConsumersCommand],
|
|
13398
|
-
summary:
|
|
13399
|
-
hints:
|
|
13897
|
+
summary: summary8,
|
|
13898
|
+
hints: hints8
|
|
13400
13899
|
};
|
|
13401
13900
|
var events_default = eventsNoun;
|
|
13402
13901
|
|
|
13403
13902
|
// src/cli/commands/orchestration.ts
|
|
13404
|
-
import
|
|
13405
|
-
import { Command as
|
|
13903
|
+
import path33 from "path";
|
|
13904
|
+
import { Command as Command12, Option as Option12 } from "clipanion";
|
|
13406
13905
|
var DEFAULT_PATTERN_GLOBS = ["src/patterns/*.pattern.ts"];
|
|
13407
13906
|
function resolvePatternGlobs(ctx) {
|
|
13408
13907
|
const fromConfig = ctx.config?.patterns;
|
|
@@ -13415,26 +13914,26 @@ function resolveOrchestrationOutputRoot(ctx) {
|
|
|
13415
13914
|
const paths = ctx.config?.paths;
|
|
13416
13915
|
const explicit = paths?.orchestration_src;
|
|
13417
13916
|
if (typeof explicit === "string" && explicit.length > 0) {
|
|
13418
|
-
return
|
|
13917
|
+
return path33.resolve(ctx.cwd, explicit);
|
|
13419
13918
|
}
|
|
13420
13919
|
const backendSrc = typeof paths?.backend_src === "string" && paths.backend_src.length > 0 ? paths.backend_src : "app/backend/src";
|
|
13421
|
-
return
|
|
13920
|
+
return path33.resolve(ctx.cwd, backendSrc, "orchestration");
|
|
13422
13921
|
}
|
|
13423
13922
|
async function reloadRegistry(ctx) {
|
|
13424
13923
|
_resetRegistryForTests({ includeLibrary: false });
|
|
13425
13924
|
return loadAppPatterns(resolvePatternGlobs(ctx), ctx.cwd);
|
|
13426
13925
|
}
|
|
13427
|
-
var OrchestrationGenCommand = class extends
|
|
13926
|
+
var OrchestrationGenCommand = class extends Command12 {
|
|
13428
13927
|
static paths = [["orchestration", "gen"]];
|
|
13429
|
-
static usage =
|
|
13928
|
+
static usage = Command12.Usage({
|
|
13430
13929
|
description: "Emit token / providers / dispatcher / module files per orchestration pattern (ADR-032 Phase 3-2/3)."
|
|
13431
13930
|
});
|
|
13432
|
-
pattern =
|
|
13433
|
-
all =
|
|
13434
|
-
dryRun =
|
|
13435
|
-
json =
|
|
13436
|
-
cwd =
|
|
13437
|
-
configPath =
|
|
13931
|
+
pattern = Option12.String("--pattern", { required: false });
|
|
13932
|
+
all = Option12.Boolean("--all", false);
|
|
13933
|
+
dryRun = Option12.Boolean("--dry-run", false);
|
|
13934
|
+
json = Option12.Boolean("--json", false);
|
|
13935
|
+
cwd = Option12.String("--cwd", { required: false });
|
|
13936
|
+
configPath = Option12.String("--config", { required: false });
|
|
13438
13937
|
async execute() {
|
|
13439
13938
|
if (this.json) setJsonMode(true);
|
|
13440
13939
|
const ctx = await loadContext({
|
|
@@ -13507,12 +14006,12 @@ var OrchestrationGenCommand = class extends Command10 {
|
|
|
13507
14006
|
);
|
|
13508
14007
|
for (const f of result.files) {
|
|
13509
14008
|
console.log(
|
|
13510
|
-
` ${theme.muted(icons.arrow)} ${
|
|
14009
|
+
` ${theme.muted(icons.arrow)} ${path33.relative(ctx.cwd, f.outputPath)}`
|
|
13511
14010
|
);
|
|
13512
14011
|
}
|
|
13513
14012
|
} else {
|
|
13514
14013
|
printSuccess(
|
|
13515
|
-
`Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${
|
|
14014
|
+
`Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${path33.relative(ctx.cwd, outputRoot)}`
|
|
13516
14015
|
);
|
|
13517
14016
|
}
|
|
13518
14017
|
return 0;
|
|
@@ -13525,14 +14024,14 @@ var OrchestrationGenCommand = class extends Command10 {
|
|
|
13525
14024
|
}
|
|
13526
14025
|
}
|
|
13527
14026
|
};
|
|
13528
|
-
var OrchestrationListCommand = class extends
|
|
14027
|
+
var OrchestrationListCommand = class extends Command12 {
|
|
13529
14028
|
static paths = [["orchestration", "list"]];
|
|
13530
|
-
static usage =
|
|
14029
|
+
static usage = Command12.Usage({
|
|
13531
14030
|
description: "List registered orchestration patterns"
|
|
13532
14031
|
});
|
|
13533
|
-
json =
|
|
13534
|
-
cwd =
|
|
13535
|
-
configPath =
|
|
14032
|
+
json = Option12.Boolean("--json", false);
|
|
14033
|
+
cwd = Option12.String("--cwd", { required: false });
|
|
14034
|
+
configPath = Option12.String("--config", { required: false });
|
|
13536
14035
|
async execute() {
|
|
13537
14036
|
if (this.json) setJsonMode(true);
|
|
13538
14037
|
const ctx = await loadContext({
|
|
@@ -13576,14 +14075,14 @@ var OrchestrationListCommand = class extends Command10 {
|
|
|
13576
14075
|
return 0;
|
|
13577
14076
|
}
|
|
13578
14077
|
};
|
|
13579
|
-
var OrchestrationValidateCommand = class extends
|
|
14078
|
+
var OrchestrationValidateCommand = class extends Command12 {
|
|
13580
14079
|
static paths = [["orchestration", "validate"]];
|
|
13581
|
-
static usage =
|
|
14080
|
+
static usage = Command12.Usage({
|
|
13582
14081
|
description: "Run the Phase 3-1 project-level orchestration validator (ADR-032)"
|
|
13583
14082
|
});
|
|
13584
|
-
json =
|
|
13585
|
-
cwd =
|
|
13586
|
-
configPath =
|
|
14083
|
+
json = Option12.Boolean("--json", false);
|
|
14084
|
+
cwd = Option12.String("--cwd", { required: false });
|
|
14085
|
+
configPath = Option12.String("--config", { required: false });
|
|
13587
14086
|
async execute() {
|
|
13588
14087
|
if (this.json) setJsonMode(true);
|
|
13589
14088
|
const ctx = await loadContext({
|
|
@@ -13619,7 +14118,7 @@ var OrchestrationValidateCommand = class extends Command10 {
|
|
|
13619
14118
|
return errors.length === 0 && loadResult.errors.length === 0 ? 0 : 1;
|
|
13620
14119
|
}
|
|
13621
14120
|
};
|
|
13622
|
-
async function
|
|
14121
|
+
async function summary9(ctx) {
|
|
13623
14122
|
try {
|
|
13624
14123
|
await reloadRegistry(ctx);
|
|
13625
14124
|
} catch {
|
|
@@ -13633,7 +14132,7 @@ async function summary8(ctx) {
|
|
|
13633
14132
|
]
|
|
13634
14133
|
};
|
|
13635
14134
|
}
|
|
13636
|
-
async function
|
|
14135
|
+
async function hints9(_ctx) {
|
|
13637
14136
|
return [
|
|
13638
14137
|
{
|
|
13639
14138
|
command: "codegen orchestration gen",
|
|
@@ -13656,8 +14155,8 @@ var orchestrationNoun = {
|
|
|
13656
14155
|
OrchestrationListCommand,
|
|
13657
14156
|
OrchestrationValidateCommand
|
|
13658
14157
|
],
|
|
13659
|
-
summary:
|
|
13660
|
-
hints:
|
|
14158
|
+
summary: summary9,
|
|
14159
|
+
hints: hints9
|
|
13661
14160
|
};
|
|
13662
14161
|
var orchestration_default = orchestrationNoun;
|
|
13663
14162
|
|
|
@@ -13668,19 +14167,26 @@ var InitShortcut = class extends ProjectInitCommand {
|
|
|
13668
14167
|
};
|
|
13669
14168
|
var init_default = InitShortcut;
|
|
13670
14169
|
|
|
14170
|
+
// src/cli/shortcuts/update.ts
|
|
14171
|
+
var UpdateShortcut = class extends ProjectUpdateCommand {
|
|
14172
|
+
static paths = [["update"]];
|
|
14173
|
+
static usage = ProjectUpdateCommand.usage;
|
|
14174
|
+
};
|
|
14175
|
+
var update_default = UpdateShortcut;
|
|
14176
|
+
|
|
13671
14177
|
// src/cli/index.ts
|
|
13672
14178
|
function readVersion() {
|
|
13673
14179
|
try {
|
|
13674
|
-
const pkgPath =
|
|
14180
|
+
const pkgPath = join10(import.meta.dirname, "..", "..", "package.json");
|
|
13675
14181
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
13676
14182
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
13677
14183
|
} catch {
|
|
13678
14184
|
return "0.0.0";
|
|
13679
14185
|
}
|
|
13680
14186
|
}
|
|
13681
|
-
var RootSummaryCommand = class extends
|
|
13682
|
-
static paths = [
|
|
13683
|
-
static usage =
|
|
14187
|
+
var RootSummaryCommand = class extends Command13 {
|
|
14188
|
+
static paths = [Command13.Default];
|
|
14189
|
+
static usage = Command13.Usage({
|
|
13684
14190
|
description: "Show project status and available noun commands"
|
|
13685
14191
|
});
|
|
13686
14192
|
async execute() {
|
|
@@ -13722,6 +14228,7 @@ var nouns = [
|
|
|
13722
14228
|
entity_default,
|
|
13723
14229
|
subsystem_default,
|
|
13724
14230
|
project_default,
|
|
14231
|
+
skills_default,
|
|
13725
14232
|
dev_default,
|
|
13726
14233
|
relationship_default,
|
|
13727
14234
|
junction_default,
|
|
@@ -13740,6 +14247,7 @@ async function main() {
|
|
|
13740
14247
|
cli.register(Builtins.VersionCommand);
|
|
13741
14248
|
cli.register(RootSummaryCommand);
|
|
13742
14249
|
cli.register(init_default);
|
|
14250
|
+
cli.register(update_default);
|
|
13743
14251
|
for (const noun of nouns) {
|
|
13744
14252
|
for (const CommandClass of noun.commandClasses) {
|
|
13745
14253
|
cli.register(CommandClass);
|