@pattern-stack/codegen 0.9.2 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +5 -0
  3. package/consumer-skills/bridge/SKILL.md +265 -0
  4. package/consumer-skills/codegen/SKILL.md +115 -0
  5. package/consumer-skills/entities/SKILL.md +111 -0
  6. package/consumer-skills/entities/families-and-queries.md +82 -0
  7. package/consumer-skills/entities/yaml-reference.md +118 -0
  8. package/consumer-skills/events/SKILL.md +71 -0
  9. package/consumer-skills/events/authoring-events.md +164 -0
  10. package/consumer-skills/events/typed-bus-and-outbox.md +163 -0
  11. package/consumer-skills/jobs/SKILL.md +66 -0
  12. package/consumer-skills/jobs/handler-authoring.md +236 -0
  13. package/consumer-skills/jobs/pools-and-ordering.md +161 -0
  14. package/consumer-skills/subsystems/SKILL.md +161 -0
  15. package/consumer-skills/subsystems/wiring-and-order.md +120 -0
  16. package/consumer-skills/sync/SKILL.md +134 -0
  17. package/consumer-skills/sync/audit-and-detection.md +302 -0
  18. package/consumer-skills/sync/change-sources-and-sinks.md +442 -0
  19. package/dist/runtime/subsystems/bridge/bridge.module.d.ts +0 -1
  20. package/dist/runtime/subsystems/bridge/bridge.module.js +294 -710
  21. package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
  22. package/dist/runtime/subsystems/bridge/index.d.ts +0 -1
  23. package/dist/runtime/subsystems/bridge/index.js +248 -664
  24. package/dist/runtime/subsystems/bridge/index.js.map +1 -1
  25. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +18 -10
  26. package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -1
  27. package/dist/runtime/subsystems/events/events.module.js +43 -244
  28. package/dist/runtime/subsystems/events/events.module.js.map +1 -1
  29. package/dist/runtime/subsystems/events/index.d.ts +0 -1
  30. package/dist/runtime/subsystems/events/index.js +39 -241
  31. package/dist/runtime/subsystems/events/index.js.map +1 -1
  32. package/dist/runtime/subsystems/index.js +174 -791
  33. package/dist/runtime/subsystems/index.js.map +1 -1
  34. package/dist/runtime/subsystems/jobs/bullmq.config.d.ts +22 -3
  35. package/dist/runtime/subsystems/jobs/bullmq.config.js.map +1 -1
  36. package/dist/runtime/subsystems/jobs/index.d.ts +1 -4
  37. package/dist/runtime/subsystems/jobs/index.js +87 -506
  38. package/dist/runtime/subsystems/jobs/index.js.map +1 -1
  39. package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js.map +1 -1
  40. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js +3 -0
  41. package/dist/runtime/subsystems/jobs/job-worker.bullmq-backend.js.map +1 -1
  42. package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +11 -4
  43. package/dist/runtime/subsystems/jobs/job-worker.module.js +248 -664
  44. package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
  45. package/dist/runtime/subsystems/jobs/jobs-domain.module.d.ts +0 -1
  46. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +89 -391
  47. package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
  48. package/dist/src/cli/index.js +1065 -440
  49. package/dist/src/cli/index.js.map +1 -1
  50. package/dist/src/index.js +26 -4
  51. package/dist/src/index.js.map +1 -1
  52. package/package.json +2 -1
  53. package/runtime/subsystems/events/event-bus.drizzle-backend.ts +32 -10
  54. package/runtime/subsystems/events/events.module.ts +38 -6
  55. package/runtime/subsystems/events/index.ts +7 -1
  56. package/runtime/subsystems/jobs/bullmq.config.ts +23 -3
  57. package/runtime/subsystems/jobs/index.ts +13 -8
  58. package/runtime/subsystems/jobs/job-worker.bullmq-backend.ts +5 -2
  59. package/runtime/subsystems/jobs/job-worker.module.ts +27 -7
  60. package/runtime/subsystems/jobs/jobs-domain.module.ts +27 -2
  61. package/templates/subsystem/events/domain-events.schema.ejs.t +43 -2
@@ -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 join11 } from "path";
17
- import { Builtins, Cli, Command as Command11 } from "clipanion";
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 join6 } from "path";
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 = readdirSync(dir, { withFileTypes: true });
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 = join(dir, entry.name);
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 readdirSync2, readFileSync as readFileSync2 } from "fs";
181
- import { join as join2 } from "path";
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 = join2(projectPath, "prisma", "schema.prisma");
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 = readdirSync2(dir, { withFileTypes: true });
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 = join2(dir, entry.name);
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 readdirSync3, statSync } from "fs";
302
- import { join as join3 } from "path";
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 = readdirSync3(basePath, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => `${baseFolder}/${dirent.name}`);
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
- join3(projectPath, folderName),
410
- join3(projectPath, "src", folderName)
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 readdirSync4, readFileSync as readFileSync3 } from "fs";
426
- import { basename, join as join4 } from "path";
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 = readdirSync4(dir, { withFileTypes: true });
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 = join4(dir, entry.name);
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 join5 } from "path";
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(join5(profile.paths.root, "apps", "frontend"));
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 = join6(projectPath, candidate);
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 fs.readdirSync(entitiesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).length;
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(hints9) {
1012
+ function renderHints(hints10) {
990
1013
  if (isJsonMode()) return;
991
- if (hints9.length === 0) return;
1014
+ if (hints10.length === 0) return;
992
1015
  console.log("");
993
1016
  console.log(theme.muted(" Next:"));
994
- const maxCmd = Math.max(...hints9.map((h) => h.command.length));
995
- for (const h of hints9) {
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, hints9] = await Promise.all([noun.summary(ctx), noun.hints(ctx)]);
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: hints9 });
1045
+ printJson({ noun: noun.name, summary: pane, hints: hints10 });
1023
1046
  return 0;
1024
1047
  }
1025
1048
  renderPane(pane);
1026
- renderHints(hints9);
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 path31 = err.path.join(".");
2939
- const location = path31 ? `at '${path31}'` : "at root";
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 { readdirSync as readdirSync5 } from "fs";
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 = resolve(entitiesDir);
3249
+ const resolvedDir = resolve2(entitiesDir);
3228
3250
  let files;
3229
3251
  try {
3230
- files = readdirSync5(resolvedDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => join7(resolvedDir, f));
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, rel] of entity.relationships) {
3276
- const targetEntity = entityMap.get(rel.target);
3297
+ for (const [relName, rel2] of entity.relationships) {
3298
+ const targetEntity = entityMap.get(rel2.target);
3277
3299
  if (targetEntity) {
3278
- rel.resolved = true;
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 '${rel.target}'`,
3307
+ message: `Relationship '${relName}' references unknown entity '${rel2.target}'`,
3286
3308
  path: entity.sourcePath,
3287
- suggestion: `Define entity '${rel.target}' or fix the target name`
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 = resolve(relationshipsDir);
3422
+ const resolvedDir = resolve2(relationshipsDir);
3401
3423
  let files;
3402
3424
  try {
3403
- files = readdirSync5(resolvedDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => join7(resolvedDir, f));
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, rel] of entity.relationships) {
3486
- if (!rel.resolved) continue;
3487
- const reverseEdge = hasReverseEdge(edges, entity.name, rel.target);
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: rel.target,
3491
- relationship: rel,
3492
- cardinality: inferCardinality(rel.type),
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, path31) {
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, [...path31, edge.to]);
3565
+ dfs(edge.to, [...path34, edge.to]);
3544
3566
  } else if (recursionStack.has(edge.to)) {
3545
- const cycleStart = path31.indexOf(edge.to);
3567
+ const cycleStart = path34.indexOf(edge.to);
3546
3568
  if (cycleStart !== -1) {
3547
- cycles.push([...path31.slice(cycleStart), edge.to]);
3569
+ cycles.push([...path34.slice(cycleStart), edge.to]);
3548
3570
  } else {
3549
- cycles.push([...path31, edge.to]);
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, rel] of entity.relationships) {
3851
- if (rel.type === "belongs_to") {
3852
- const fkField = entity.fields.get(rel.foreignKey);
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 "${rel.foreignKey}" but field doesn't exist`,
3860
- suggestion: `Add field "${rel.foreignKey}" with foreign_key reference`
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 (rel.type === "has_many" || rel.type === "has_one") {
3865
- const targetEntity = graph.entities.get(rel.target);
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(rel.foreignKey);
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 "${rel.foreignKey}" on "${rel.target}" but field doesn't exist`,
3875
- suggestion: `Add field "${rel.foreignKey}" to "${rel.target}" entity`
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
- (rel) => rel.target === from
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 rel of entity.relationships.values()) {
4013
- if (rel.type === "belongs_to" && rel.foreignKey) {
4014
- belongsToFkNames.push(rel.foreignKey);
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 rel of entity.relationships.values()) {
4120
- relationshipsByType[rel.type] = (relationshipsByType[rel.type] ?? 0) + 1;
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 path31 of paths) {
4147
- suggestions.push(createSuggestion(path31));
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: path31, visited } = current;
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, rel] of currentEntity.relationships) {
4183
- if (rel.through) continue;
4184
- if (rel.type !== "has_many" && rel.type !== "has_one") continue;
4185
- const target = rel.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
- ...path31,
4211
+ ...path34,
4190
4212
  {
4191
4213
  via: entity,
4192
4214
  relationship: relName,
4193
- foreignKey: rel.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 rel of sourceEntity.relationships.values()) {
4222
- if (rel.target === targetName && !rel.through) {
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(path31) {
4258
- const pathDescription = [path31.source, ...path31.hops.map((h) => h.via), path31.target].join(" -> ");
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: path31.source,
4284
+ entity: path34.source,
4263
4285
  message: `Potential transitive relationship: ${pathDescription}`,
4264
- suggestion: `Add "${path31.suggestedName}" relationship via "${path31.throughPath}"`,
4265
- path: path31
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, rel] of entity.relationships) {
4379
+ for (const [name, rel2] of entity.relationships) {
4358
4380
  relationships[name] = {
4359
- type: rel.type,
4360
- target: rel.target,
4361
- foreignKey: rel.foreignKey,
4362
- through: rel.through,
4363
- inverse: rel.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, rel] of entity.relationships) {
5336
- lines.push(`| ${name} | ${rel.type} | ${rel.target} | ${rel.foreignKey} |`);
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 fs2.readdirSync(entitiesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path4.join(entitiesDir, f)).sort();
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 fs2.readdirSync(relationshipsDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path4.join(relationshipsDir, f)).filter((full) => detectYamlType(full) === "relationship").sort();
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 rel = result.definition.relationship;
5696
- const name = rel.name;
5697
- const plural = rel.table ?? pluralize(name);
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 fs2.readdirSync(junctionsDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path4.join(junctionsDir, f)).filter((full) => detectYamlType(full) === "junction").sort();
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 rel = path4.posix.relative(fromDir, toFile);
5751
- if (!rel.startsWith(".")) rel = `./${rel}`;
5752
- return rel.replace(/\.ts$/, "");
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 rel = typeof fromConfig === "string" && fromConfig.length > 0 ? fromConfig : "src/generated";
5840
- return path4.resolve(ctx.cwd, rel);
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 = fs3.readdirSync(entitiesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path5.join(entitiesDir, f)).sort();
5881
+ const files = findYamlFiles(entitiesDir);
5856
5882
  const names = [];
5857
5883
  for (const file of files) {
5858
5884
  const result = loadEntityFromYaml(file);
@@ -5986,6 +6012,19 @@ var SUBSYSTEMS = [
5986
6012
  }
5987
6013
  ];
5988
6014
  var KNOWN_NAMES = SUBSYSTEMS.map((s) => s.name);
6015
+ var SUBSYSTEM_MODULE_FILE = {
6016
+ events: "events.module.ts",
6017
+ jobs: "jobs-domain.module.ts",
6018
+ cache: "cache.module.ts",
6019
+ storage: "storage.module.ts",
6020
+ sync: "sync.module.ts",
6021
+ bridge: "bridge.module.ts",
6022
+ observability: "observability.module.ts",
6023
+ auth: "auth.module.ts"
6024
+ };
6025
+ function subsystemModuleFile(name) {
6026
+ return SUBSYSTEM_MODULE_FILE[name] ?? null;
6027
+ }
5989
6028
  function candidateRoots(cwd, configured) {
5990
6029
  const roots = [
5991
6030
  ...configured ? [path6.resolve(cwd, configured)] : [],
@@ -6008,7 +6047,7 @@ function inferBackend(dir, name) {
6008
6047
  if (hasMemory) return "memory";
6009
6048
  return "unknown";
6010
6049
  }
6011
- async function detectInstalledSubsystems(ctx) {
6050
+ async function detectSubsystemStatesImpl(ctx) {
6012
6051
  const configured = ctx.config?.paths?.subsystems;
6013
6052
  const roots = candidateRoots(ctx.cwd, configured);
6014
6053
  const found = [];
@@ -6022,16 +6061,16 @@ async function detectInstalledSubsystems(ctx) {
6022
6061
  const dir = path6.join(root, name);
6023
6062
  if (!fs4.existsSync(dir) || !fs4.statSync(dir).isDirectory()) continue;
6024
6063
  const files = fs4.readdirSync(dir);
6025
- let hasProtocol = files.some((f) => f.endsWith(".protocol.ts"));
6026
- if (name === "auth") {
6027
- hasProtocol = files.includes("auth.module.ts");
6028
- }
6029
- if (!hasProtocol) continue;
6064
+ const moduleFile = subsystemModuleFile(name);
6065
+ const hasModule = moduleFile ? files.includes(moduleFile) : false;
6066
+ const present = name === "auth" ? hasModule : files.some((f) => f.endsWith(".protocol.ts")) || hasModule;
6067
+ if (!present) continue;
6030
6068
  seen.add(name);
6031
6069
  found.push({
6032
6070
  name,
6033
6071
  path: dir,
6034
- backend: inferBackend(dir, name)
6072
+ backend: inferBackend(dir, name),
6073
+ status: hasModule ? "installed" : "incomplete"
6035
6074
  });
6036
6075
  }
6037
6076
  }
@@ -6047,7 +6086,8 @@ async function detectInstalledSubsystems(ctx) {
6047
6086
  found.push({
6048
6087
  name: "openapi-config",
6049
6088
  path: configPath,
6050
- backend: "config-only"
6089
+ backend: "config-only",
6090
+ status: "installed"
6051
6091
  });
6052
6092
  }
6053
6093
  } catch {
@@ -6070,7 +6110,8 @@ async function detectInstalledSubsystems(ctx) {
6070
6110
  found.push({
6071
6111
  name: "auth-integrations",
6072
6112
  path: path6.dirname(moduleFile),
6073
- backend: "drizzle"
6113
+ backend: "drizzle",
6114
+ status: "installed"
6074
6115
  });
6075
6116
  break;
6076
6117
  }
@@ -6078,6 +6119,13 @@ async function detectInstalledSubsystems(ctx) {
6078
6119
  }
6079
6120
  return found;
6080
6121
  }
6122
+ async function detectSubsystemStates(ctx) {
6123
+ return detectSubsystemStatesImpl(ctx);
6124
+ }
6125
+ async function detectInstalledSubsystems(ctx) {
6126
+ const states = await detectSubsystemStatesImpl(ctx);
6127
+ return states.filter((s) => s.status === "installed");
6128
+ }
6081
6129
 
6082
6130
  // src/cli/shared/subsystems-path.ts
6083
6131
  import path7 from "path";
@@ -6197,7 +6245,8 @@ var HEADER3 = `// AUTO-GENERATED by @pattern-stack/codegen. DO NOT EDIT.
6197
6245
 
6198
6246
  `;
6199
6247
  function buildSubsystemBarrel(installed, config, subsystemsRel) {
6200
- const installedNames = new Set(installed.map((i) => i.name));
6248
+ const actable = installed.filter((i) => i.status !== "incomplete");
6249
+ const installedNames = new Set(actable.map((i) => i.name));
6201
6250
  const emitted = [];
6202
6251
  const skipped = [];
6203
6252
  const allImports = [`import type { DynamicModule } from '@nestjs/common';`];
@@ -6215,25 +6264,17 @@ function buildSubsystemBarrel(installed, config, subsystemsRel) {
6215
6264
  allCalls.push(...out.calls);
6216
6265
  emitted.push(name);
6217
6266
  }
6218
- for (const inst of installed) {
6267
+ for (const inst of actable) {
6219
6268
  if (!COMPOSABLE_ORDER.includes(inst.name) && !COMPOSERS[inst.name]) {
6220
6269
  skipped.push(inst.name);
6221
6270
  }
6222
6271
  }
6223
- if (allCalls.length === 0) {
6224
- return {
6225
- content: HEADER3 + `export const SUBSYSTEM_MODULES: DynamicModule[] = [];
6226
- `,
6227
- emitted,
6228
- skipped
6229
- };
6230
- }
6231
- const body = allImports.join("\n") + `
6232
-
6233
- export const SUBSYSTEM_MODULES: DynamicModule[] = [
6272
+ const exportLine = allCalls.length === 0 ? `export const SUBSYSTEM_MODULES: DynamicModule[] = [];
6273
+ ` : `export const SUBSYSTEM_MODULES: DynamicModule[] = [
6234
6274
  ${allCalls.join("\n")}
6235
6275
  ];
6236
6276
  `;
6277
+ const body = allImports.join("\n") + "\n\n" + exportLine;
6237
6278
  return { content: HEADER3 + body, emitted, skipped };
6238
6279
  }
6239
6280
  async function regenerateSubsystemBarrel(opts) {
@@ -7042,8 +7083,7 @@ import fs8 from "fs";
7042
7083
  import path11 from "path";
7043
7084
 
7044
7085
  // src/parser/load-events.ts
7045
- import { readdirSync as readdirSync7 } from "fs";
7046
- import { basename as basename2, join as join10, resolve as resolve2 } from "path";
7086
+ import { basename as basename2, resolve as resolve3 } from "path";
7047
7087
  function loadErrorToIssue2(error) {
7048
7088
  const issues = [];
7049
7089
  issues.push({
@@ -7073,10 +7113,10 @@ function stripYamlExt(file) {
7073
7113
  function loadEvents(eventsDir, entityNames) {
7074
7114
  const events = [];
7075
7115
  const issues = [];
7076
- const resolvedDir = resolve2(eventsDir);
7116
+ const resolvedDir = resolve3(eventsDir);
7077
7117
  let files;
7078
7118
  try {
7079
- files = readdirSync7(resolvedDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).sort().map((f) => join10(resolvedDir, f));
7119
+ files = findYamlFiles(resolvedDir);
7080
7120
  } catch {
7081
7121
  issues.push({
7082
7122
  severity: "warning",
@@ -7227,7 +7267,7 @@ function collectEntityEvents(entitiesDir) {
7227
7267
  if (!fs8.existsSync(entitiesDir)) {
7228
7268
  return { events, issues };
7229
7269
  }
7230
- const files = fs8.readdirSync(entitiesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path11.join(entitiesDir, f)).sort();
7270
+ const files = findYamlFiles(entitiesDir);
7231
7271
  for (const filePath of files) {
7232
7272
  const result = loadEntityFromYaml(filePath);
7233
7273
  if (!result.success) continue;
@@ -7269,7 +7309,7 @@ function collectMergedEvents(opts) {
7269
7309
  const { entitiesDir, eventsDir } = opts;
7270
7310
  const entityNames = [];
7271
7311
  if (fs8.existsSync(entitiesDir)) {
7272
- const entityFiles = fs8.readdirSync(entitiesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path11.join(entitiesDir, f)).sort();
7312
+ const entityFiles = findYamlFiles(entitiesDir);
7273
7313
  for (const f of entityFiles) {
7274
7314
  const result = loadEntityFromYaml(f);
7275
7315
  if (result.success) entityNames.push(result.definition.entity.name);
@@ -7752,7 +7792,7 @@ function printInfo(msg) {
7752
7792
  // src/cli/commands/entity.ts
7753
7793
  function listEntityYamls2(dir) {
7754
7794
  if (!fs9.existsSync(dir)) return [];
7755
- return fs9.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path13.join(dir, f));
7795
+ return findYamlFiles(dir);
7756
7796
  }
7757
7797
  function summarizePatternLabel(entity) {
7758
7798
  if (typeof entity.pattern === "string" && entity.pattern.length > 0) {
@@ -8788,13 +8828,6 @@ function readIfExists(p) {
8788
8828
  return null;
8789
8829
  }
8790
8830
  }
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
8831
  function extractRelativeImports(source) {
8799
8832
  const out = [];
8800
8833
  const re = /(?:import|export)\s+(?:[^'"`;]*?\s+from\s+)?['"`](\.{1,2}\/[^'"`]+)['"`]/g;
@@ -8813,7 +8846,7 @@ function resolveSourceImport(sourceFile, specifier) {
8813
8846
  return null;
8814
8847
  }
8815
8848
  async function copyRuntime(opts) {
8816
- const { sourceDir, targetDir, filter, resolveDeps, dryRun } = opts;
8849
+ const { sourceDir, targetDir, filter, resolveDeps, dryRun, onlyExisting } = opts;
8817
8850
  if (!fs10.existsSync(sourceDir) || !fs10.statSync(sourceDir).isDirectory()) {
8818
8851
  throw new Error(`runtime source directory not found: ${sourceDir}`);
8819
8852
  }
@@ -8838,9 +8871,9 @@ async function copyRuntime(opts) {
8838
8871
  }
8839
8872
  if (!stat.isFile()) continue;
8840
8873
  if (!entry.endsWith(".ts") && !entry.endsWith(".tsx")) continue;
8841
- const rel = path21.relative(sourceDir, src);
8842
- if (filter && !filter(rel) && !filter(entry)) continue;
8843
- queue.push({ src, dest: path21.join(targetDir, rel), isDep: false });
8874
+ const rel2 = path21.relative(sourceDir, src);
8875
+ if (filter && !filter(rel2) && !filter(entry)) continue;
8876
+ queue.push({ src, dest: path21.join(targetDir, rel2), isDep: false });
8844
8877
  }
8845
8878
  }
8846
8879
  walk(sourceDir);
@@ -8849,14 +8882,20 @@ async function copyRuntime(opts) {
8849
8882
  const next = queue.shift();
8850
8883
  if (visited.has(next.src)) continue;
8851
8884
  visited.add(next.src);
8885
+ if (onlyExisting && !fs10.existsSync(next.dest)) {
8886
+ continue;
8887
+ }
8852
8888
  const content = fs10.readFileSync(next.src, "utf-8");
8853
8889
  result.planned.push(next.dest);
8854
- if (!dryRun) {
8855
- const status = writeFile(next.dest, content);
8856
- if (status === "written") result.written.push(next.dest);
8857
- else if (status === "updated") result.updated.push(next.dest);
8858
- else result.unchanged.push(next.dest);
8859
- if (next.isDep) result.dependenciesCopied.push(next.dest);
8890
+ const existing = readIfExists(next.dest);
8891
+ const status = existing === content ? "unchanged" : existing === null ? "written" : "updated";
8892
+ if (status === "written") result.written.push(next.dest);
8893
+ else if (status === "updated") result.updated.push(next.dest);
8894
+ else result.unchanged.push(next.dest);
8895
+ if (next.isDep) result.dependenciesCopied.push(next.dest);
8896
+ if (!dryRun && status !== "unchanged") {
8897
+ fs10.mkdirSync(path21.dirname(next.dest), { recursive: true });
8898
+ fs10.writeFileSync(next.dest, content);
8860
8899
  }
8861
8900
  if (resolveDeps) {
8862
8901
  for (const spec of extractRelativeImports(content)) {
@@ -8909,11 +8948,11 @@ async function summary2(ctx) {
8909
8948
  }
8910
8949
  body.push(theme.muted("Installed:"));
8911
8950
  for (const i of installed) {
8912
- const rel = path22.relative(ctx.cwd, i.path) || i.path;
8951
+ const rel2 = path22.relative(ctx.cwd, i.path) || i.path;
8913
8952
  body.push(
8914
8953
  ` ${theme.success(icons.check)} ${i.name.padEnd(10)} ${theme.muted(
8915
8954
  `${i.backend} backend`
8916
- )} ${theme.muted(rel)}`
8955
+ )} ${theme.muted(rel2)}`
8917
8956
  );
8918
8957
  }
8919
8958
  if (missing.length > 0) {
@@ -8969,6 +9008,8 @@ function backendFileFilter(backend, subsystemName) {
8969
9008
  if (subsystemName === "auth" && file === "auth-oauth-state.schema.ts") {
8970
9009
  return false;
8971
9010
  }
9011
+ if (file.endsWith(".redis-backend.ts") && backend !== "redis") return false;
9012
+ if (file.endsWith(".bullmq-backend.ts") && backend !== "bullmq") return false;
8972
9013
  if (backend === "memory") {
8973
9014
  if (file.endsWith(".drizzle-backend.ts")) return false;
8974
9015
  if (file.endsWith(".schema.ts")) return false;
@@ -9916,9 +9957,9 @@ function buildAuthImportRewriter(subsystemsRoot) {
9916
9957
  return content;
9917
9958
  }
9918
9959
  AUTH_BARE_IMPORT_RE.lastIndex = 0;
9919
- let rel = path22.relative(path22.dirname(destPath), authRoot);
9920
- if (!rel.startsWith(".")) rel = `./${rel}`;
9921
- const relPosix = rel.split(path22.sep).join("/");
9960
+ let rel2 = path22.relative(path22.dirname(destPath), authRoot);
9961
+ if (!rel2.startsWith(".")) rel2 = `./${rel2}`;
9962
+ const relPosix = rel2.split(path22.sep).join("/");
9922
9963
  return content.replace(
9923
9964
  AUTH_BARE_IMPORT_RE,
9924
9965
  (_match, quote) => `${quote}${relPosix}${quote}`
@@ -10031,14 +10072,14 @@ var SubsystemListCommand = class extends Command3 {
10031
10072
  json: this.json,
10032
10073
  skipDetection: true
10033
10074
  });
10034
- const installed = await detectInstalledSubsystems(ctx);
10075
+ const states = await detectSubsystemStates(ctx);
10035
10076
  const byName = /* @__PURE__ */ new Map();
10036
- for (const i of installed) byName.set(i.name, i);
10077
+ for (const i of states) byName.set(i.name, i);
10037
10078
  const rows = SUBSYSTEMS.map((s) => {
10038
10079
  const inst = byName.get(s.name);
10039
10080
  return {
10040
10081
  name: s.name,
10041
- status: inst ? "installed" : "available",
10082
+ status: inst ? inst.status : "available",
10042
10083
  backend: inst ? inst.backend : null,
10043
10084
  path: inst ? path22.relative(ctx.cwd, inst.path) || inst.path : null
10044
10085
  };
@@ -10062,29 +10103,129 @@ var SubsystemListCommand = class extends Command3 {
10062
10103
  var SubsystemRemoveCommand = class extends Command3 {
10063
10104
  static paths = [["subsystem", "remove"]];
10064
10105
  static usage = Command3.Usage({
10065
- description: "Remove a subsystem (not yet implemented)"
10106
+ description: "Remove a vendored subsystem",
10107
+ examples: [
10108
+ ["Remove the jobs subsystem", "codegen subsystem remove jobs"],
10109
+ [
10110
+ "Skip the git-safety check (uncommitted edits will be lost)",
10111
+ "codegen subsystem remove jobs --force"
10112
+ ],
10113
+ ["Non-interactive parity with install", "codegen subsystem remove jobs --yes"]
10114
+ ]
10066
10115
  });
10067
10116
  name = Option3.String({ required: true });
10117
+ // #7: parity with `subsystem install` so a non-interactive caller can pass
10118
+ // the same flag set across install/remove. Accepted but currently a no-op
10119
+ // (remove has no interactive prompt yet); kept for forward-compatibility.
10120
+ yes = Option3.Boolean("--yes,-y", false);
10121
+ force = Option3.Boolean("--force", false);
10068
10122
  json = Option3.Boolean("--json", false);
10069
10123
  cwd = Option3.String("--cwd", { required: false });
10070
10124
  configPath = Option3.String("--config", { required: false });
10071
10125
  async execute() {
10072
10126
  if (this.json) setJsonMode(true);
10127
+ const ctx = await loadContext({
10128
+ cwd: this.cwd,
10129
+ configPath: this.configPath,
10130
+ json: this.json,
10131
+ skipDetection: true
10132
+ });
10133
+ const desc3 = describeSubsystem(this.name);
10134
+ if (!desc3) {
10135
+ printError(
10136
+ `Unknown subsystem '${this.name}'. Known: ${SUBSYSTEMS.map((s) => s.name).join(", ")}`
10137
+ );
10138
+ return 2;
10139
+ }
10140
+ if (desc3.name === "openapi-config") {
10141
+ printError(
10142
+ "openapi-config has no vendored runtime to remove \u2014 it's a config-only pseudo-subsystem."
10143
+ );
10144
+ printInfo(
10145
+ "To uninstall: delete the `openapi:` block from codegen.config.yaml and uninstall the @nestjs/swagger / @anatine/zod-openapi peer deps."
10146
+ );
10147
+ return 1;
10148
+ }
10149
+ if (desc3.name === "auth-integrations") {
10150
+ printError(
10151
+ "auth-integrations is vendored under <modules>/integrations/ alongside the codegen-emitted entity layer \u2014 not auto-removable here."
10152
+ );
10153
+ printInfo(
10154
+ "To uninstall: remove the integrations/ directory and the IntegrationsAuthModule registration from app.module.ts by hand."
10155
+ );
10156
+ return 1;
10157
+ }
10158
+ const installed = await detectInstalledSubsystems(ctx);
10159
+ const target = installed.find((i) => i.name === desc3.name);
10160
+ if (!target) {
10161
+ if (isJsonMode()) {
10162
+ printJson({
10163
+ command: "subsystem remove",
10164
+ subsystem: desc3.name,
10165
+ status: "not-installed"
10166
+ });
10167
+ } else {
10168
+ printError(`${desc3.name} is not installed \u2014 nothing to remove.`);
10169
+ }
10170
+ return 1;
10171
+ }
10172
+ const subsystemDir = target.path;
10173
+ if (!fs11.existsSync(subsystemDir)) {
10174
+ printError(
10175
+ `Detected install at ${subsystemDir} but the directory is gone \u2014 refusing to act.`
10176
+ );
10177
+ return 1;
10178
+ }
10179
+ if (!this.force) {
10180
+ const rel2 = path22.relative(ctx.cwd, subsystemDir) || subsystemDir;
10181
+ const gitCheck = checkGitSafety([rel2], ctx.cwd);
10182
+ if (gitCheck.inRepo && !gitCheck.clean) {
10183
+ printWarning(
10184
+ `Uncommitted changes under ${subsystemDir}. Pass --force to delete anyway.`
10185
+ );
10186
+ if (!isJsonMode()) return 1;
10187
+ }
10188
+ }
10189
+ try {
10190
+ fs11.rmSync(subsystemDir, { recursive: true, force: true });
10191
+ } catch (err) {
10192
+ const message = err instanceof Error ? err.message : String(err);
10193
+ printError(`Failed to remove ${subsystemDir}: ${message}`);
10194
+ return 1;
10195
+ }
10196
+ let barrelRegenerated = false;
10197
+ try {
10198
+ const generatedDir = resolveGeneratedDir(ctx);
10199
+ await regenerateSubsystemBarrel({ ctx, generatedDir });
10200
+ barrelRegenerated = true;
10201
+ } catch (err) {
10202
+ const msg = err instanceof Error ? err.message : String(err);
10203
+ printWarning(`subsystem barrel regeneration failed \u2014 ${msg}`);
10204
+ }
10073
10205
  if (isJsonMode()) {
10074
10206
  printJson({
10075
10207
  command: "subsystem remove",
10076
- status: "not-implemented",
10077
- message: "Manually delete the subsystem directory and remove the module registration from your app.module.ts."
10208
+ subsystem: desc3.name,
10209
+ status: "removed",
10210
+ path: subsystemDir,
10211
+ barrelRegenerated
10078
10212
  });
10079
- return 1;
10213
+ return 0;
10080
10214
  }
10081
- printError("subsystem remove is not yet implemented.");
10082
- console.log(
10083
- theme.muted(
10084
- " Manually delete the subsystem directory and remove the module\n registration from your app.module.ts."
10085
- )
10215
+ printSuccess(
10216
+ `${desc3.name} subsystem removed (${path22.relative(ctx.cwd, subsystemDir) || subsystemDir}).`
10217
+ );
10218
+ if (barrelRegenerated) {
10219
+ printInfo("Regenerated <generated>/subsystems.ts barrel.");
10220
+ }
10221
+ printInfo("Next steps (manual):");
10222
+ printInfo(
10223
+ ` 1. Remove the \`${capitalize(desc3.name)}Module.forRoot(...)\` registration from app.module.ts.`
10086
10224
  );
10087
- return 1;
10225
+ printInfo(
10226
+ ` 2. Remove the \`${desc3.name}:\` block from codegen.config.yaml (if you no longer want it).`
10227
+ );
10228
+ return 0;
10088
10229
  }
10089
10230
  };
10090
10231
  var subsystemNoun = {
@@ -10100,10 +10241,10 @@ var subsystemNoun = {
10100
10241
  var subsystem_default = subsystemNoun;
10101
10242
 
10102
10243
  // src/cli/commands/project.ts
10103
- import fs14 from "fs";
10104
- import path25 from "path";
10244
+ import fs17 from "fs";
10245
+ import path28 from "path";
10105
10246
  import readline from "readline";
10106
- import { Command as Command5, Option as Option5 } from "clipanion";
10247
+ import { Command as Command7, Option as Option7 } from "clipanion";
10107
10248
  import { stringify as stringifyYaml2 } from "yaml";
10108
10249
 
10109
10250
  // src/cli/shared/init-scaffold.ts
@@ -10746,8 +10887,8 @@ async function buildInitPlan(ctx, options) {
10746
10887
  {
10747
10888
  const entitiesDir = path23.join(cwd, "entities");
10748
10889
  const examplePath = path23.join(entitiesDir, "example.yaml");
10749
- const hasOtherYamls = fs12.existsSync(entitiesDir) && fs12.readdirSync(entitiesDir).some(
10750
- (f) => (f.endsWith(".yaml") || f.endsWith(".yml")) && f !== "example.yaml"
10890
+ const hasOtherYamls = fs12.existsSync(entitiesDir) && findYamlFiles(entitiesDir).some(
10891
+ (f) => path23.basename(f) !== "example.yaml"
10751
10892
  );
10752
10893
  if (fs12.existsSync(examplePath)) {
10753
10894
  entries.push({
@@ -11058,8 +11199,8 @@ function runtimeRoot3() {
11058
11199
  if (fs13.existsSync(topLevel)) return topLevel;
11059
11200
  return path24.join(pkgRoot, "dist", "runtime");
11060
11201
  }
11061
- function loadRuntimeFile2(rel) {
11062
- return fs13.readFileSync(path24.join(runtimeRoot3(), rel), "utf-8");
11202
+ function loadRuntimeFile2(rel2) {
11203
+ return fs13.readFileSync(path24.join(runtimeRoot3(), rel2), "utf-8");
11063
11204
  }
11064
11205
  function resolveProjectRoot(startDir) {
11065
11206
  let dir = path24.resolve(startDir);
@@ -11342,8 +11483,457 @@ ${CONSUMER_SETUP_POINTER}
11342
11483
  }
11343
11484
  };
11344
11485
 
11345
- // src/cli/commands/project.ts
11486
+ // src/cli/commands/project-update.ts
11487
+ import fs16 from "fs";
11488
+ import path27 from "path";
11489
+ import { Command as Command6, Option as Option6 } from "clipanion";
11490
+
11491
+ // src/cli/commands/skills.ts
11492
+ import fs15 from "fs";
11493
+ import path26 from "path";
11494
+ import { Command as Command5, Option as Option5 } from "clipanion";
11495
+
11496
+ // src/cli/shared/tree-copier.ts
11497
+ import fs14 from "fs";
11498
+ import path25 from "path";
11499
+ var TEXT_EXTENSIONS = [".ts", ".tsx", ".md", ".mdx", ".yaml", ".yml", ".json"];
11500
+ function isTextFile(name) {
11501
+ return TEXT_EXTENSIONS.some((ext) => name.endsWith(ext));
11502
+ }
11503
+ function copyTreeWithReport(opts) {
11504
+ const { srcDir, destDir, dryRun = false, transform, include } = opts;
11505
+ const report = {
11506
+ entries: [],
11507
+ created: [],
11508
+ updated: [],
11509
+ unchanged: []
11510
+ };
11511
+ if (!fs14.existsSync(srcDir) || !fs14.statSync(srcDir).isDirectory()) {
11512
+ throw new Error(`tree-copier source directory not found: ${srcDir}`);
11513
+ }
11514
+ const walk = (relDir) => {
11515
+ const absSrcDir = path25.join(srcDir, relDir);
11516
+ for (const entry of fs14.readdirSync(absSrcDir, { withFileTypes: true })) {
11517
+ const relPath2 = relDir ? path25.posix.join(relDir, entry.name) : entry.name;
11518
+ const absSrc = path25.join(srcDir, relPath2);
11519
+ if (entry.isDirectory()) {
11520
+ walk(relPath2);
11521
+ continue;
11522
+ }
11523
+ if (!entry.isFile()) continue;
11524
+ if (include && !include(relPath2)) continue;
11525
+ const dest = path25.join(destDir, relPath2);
11526
+ let content = fs14.readFileSync(absSrc, "utf-8");
11527
+ if (transform && isTextFile(entry.name)) {
11528
+ content = transform(content, dest);
11529
+ }
11530
+ const existing = fs14.existsSync(dest) ? fs14.readFileSync(dest, "utf-8") : null;
11531
+ let action;
11532
+ if (existing === null) {
11533
+ action = "created";
11534
+ } else if (existing === content) {
11535
+ action = "unchanged";
11536
+ } else {
11537
+ action = "updated";
11538
+ }
11539
+ if (!dryRun && action !== "unchanged") {
11540
+ fs14.mkdirSync(path25.dirname(dest), { recursive: true });
11541
+ fs14.writeFileSync(dest, content, "utf-8");
11542
+ }
11543
+ const record = { relPath: relPath2, dest, action };
11544
+ report.entries.push(record);
11545
+ report[action].push(record);
11546
+ }
11547
+ };
11548
+ walk("");
11549
+ return report;
11550
+ }
11551
+
11552
+ // src/cli/commands/skills.ts
11553
+ function consumerSkillsRoot() {
11554
+ const pkgRoot = path26.resolve(import.meta.dirname, "..", "..", "..");
11555
+ const topLevel = path26.join(pkgRoot, "consumer-skills");
11556
+ if (fs15.existsSync(topLevel)) return topLevel;
11557
+ return path26.join(pkgRoot, "dist", "consumer-skills");
11558
+ }
11559
+ function skillsTargetDir(cwd) {
11560
+ return path26.join(cwd, ".claude", "skills");
11561
+ }
11562
+ function availableSkills() {
11563
+ const root = consumerSkillsRoot();
11564
+ if (!fs15.existsSync(root)) return [];
11565
+ return fs15.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
11566
+ }
11567
+ function runSkillsInstall(opts) {
11568
+ const sourceRoot = consumerSkillsRoot();
11569
+ const targetDir = skillsTargetDir(opts.cwd);
11570
+ if (!fs15.existsSync(sourceRoot)) {
11571
+ return {
11572
+ ok: false,
11573
+ sourceRoot,
11574
+ targetDir,
11575
+ error: `consumer skills source missing: ${sourceRoot}`
11576
+ };
11577
+ }
11578
+ const report = copyTreeWithReport({
11579
+ srcDir: sourceRoot,
11580
+ destDir: targetDir,
11581
+ dryRun: Boolean(opts.dryRun)
11582
+ });
11583
+ return { ok: true, sourceRoot, targetDir, report };
11584
+ }
11346
11585
  async function summary3(ctx) {
11586
+ const skills = availableSkills();
11587
+ const targetDir = skillsTargetDir(ctx.cwd);
11588
+ const installedDirs = fs15.existsSync(targetDir) ? new Set(
11589
+ fs15.readdirSync(targetDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name)
11590
+ ) : /* @__PURE__ */ new Set();
11591
+ const body = [];
11592
+ if (skills.length === 0) {
11593
+ body.push(theme.muted("No consumer skills bundled with this package build."));
11594
+ return { title: "skills", body, footer: "" };
11595
+ }
11596
+ body.push(theme.muted("Consumer skills:"));
11597
+ for (const name of skills) {
11598
+ const present = installedDirs.has(name);
11599
+ const icon = present ? theme.success(icons.check) : theme.muted(icons.dash);
11600
+ const status = present ? "" : theme.muted("not installed");
11601
+ body.push(` ${icon} ${name.padEnd(12)} ${status}`);
11602
+ }
11603
+ const installedCount = skills.filter((s) => installedDirs.has(s)).length;
11604
+ return {
11605
+ title: "skills",
11606
+ body,
11607
+ footer: `${installedCount} of ${skills.length} skills installed \u2192 ${path26.relative(ctx.cwd, targetDir) || targetDir}`
11608
+ };
11609
+ }
11610
+ async function hints3(ctx) {
11611
+ const skills = availableSkills();
11612
+ const targetDir = skillsTargetDir(ctx.cwd);
11613
+ const allPresent = skills.length > 0 && fs15.existsSync(targetDir) && skills.every((s) => fs15.existsSync(path26.join(targetDir, s)));
11614
+ if (allPresent) {
11615
+ return [
11616
+ { command: "codegen update", description: "Re-sync skills + runtime after a package bump" }
11617
+ ];
11618
+ }
11619
+ return [
11620
+ { command: "codegen skills install", description: "Vendor consumer skills into .claude/skills" }
11621
+ ];
11622
+ }
11623
+ function renderTreeReport(report) {
11624
+ const show = (entry) => {
11625
+ const icon = theme.success(icons.check);
11626
+ console.log(` ${icon} ${theme.muted(entry.action.padEnd(10))} ${entry.relPath}`);
11627
+ };
11628
+ for (const e of [...report.created, ...report.updated]) show(e);
11629
+ if (report.unchanged.length > 0) {
11630
+ console.log(
11631
+ ` ${theme.muted(icons.dash)} ${theme.muted("unchanged".padEnd(10))} ${theme.muted(
11632
+ `${report.unchanged.length} file${report.unchanged.length === 1 ? "" : "s"} already current`
11633
+ )}`
11634
+ );
11635
+ }
11636
+ }
11637
+ var SkillsInstallCommand = class extends Command5 {
11638
+ static paths = [["skills", "install"]];
11639
+ static usage = Command5.Usage({
11640
+ description: "Vendor consumer-facing skills into the project .claude/skills",
11641
+ examples: [
11642
+ ["Install all consumer skills", "codegen skills install"],
11643
+ ["Preview without writing", "codegen skills install --dry-run"],
11644
+ ["Overwrite locally-edited skill files", "codegen skills install --force"]
11645
+ ]
11646
+ });
11647
+ force = Option5.Boolean("--force", false);
11648
+ dryRun = Option5.Boolean("--dry-run", false);
11649
+ json = Option5.Boolean("--json", false);
11650
+ cwd = Option5.String("--cwd", { required: false });
11651
+ async execute() {
11652
+ if (this.json) setJsonMode(true);
11653
+ const ctx = await loadContext({ cwd: this.cwd, json: this.json, skipDetection: true });
11654
+ const preview = runSkillsInstall({ cwd: ctx.cwd, dryRun: true });
11655
+ if (!preview.ok) {
11656
+ if (isJsonMode()) {
11657
+ printJson({ command: "skills install", status: "error", error: preview.error });
11658
+ } else {
11659
+ printError(preview.error ?? "skills install failed");
11660
+ }
11661
+ return 1;
11662
+ }
11663
+ if (!this.dryRun && !this.force) {
11664
+ const updatedPaths = preview.report.updated.map((e) => e.dest);
11665
+ const gate = checkGitSafety(updatedPaths, ctx.cwd);
11666
+ if (gate.inRepo && !gate.clean) {
11667
+ printWarning(
11668
+ `Uncommitted changes in ${gate.dirty.length} skill file(s). Commit them or pass --force to overwrite.`
11669
+ );
11670
+ if (!isJsonMode()) return 1;
11671
+ }
11672
+ }
11673
+ const result = runSkillsInstall({ cwd: ctx.cwd, dryRun: this.dryRun });
11674
+ const report = result.report;
11675
+ if (isJsonMode()) {
11676
+ printJson({
11677
+ command: "skills install",
11678
+ dryRun: this.dryRun,
11679
+ target: result.targetDir,
11680
+ files: {
11681
+ created: report.created.map((e) => e.relPath),
11682
+ updated: report.updated.map((e) => e.relPath),
11683
+ unchanged: report.unchanged.map((e) => e.relPath)
11684
+ }
11685
+ });
11686
+ return 0;
11687
+ }
11688
+ printInfo(`target = ${path26.relative(ctx.cwd, result.targetDir) || result.targetDir}`);
11689
+ console.log("");
11690
+ renderTreeReport(report);
11691
+ console.log("");
11692
+ if (this.dryRun) {
11693
+ printWarning("dry-run \u2014 no files written");
11694
+ return 0;
11695
+ }
11696
+ printSuccess(
11697
+ `skills installed (${report.created.length} new, ${report.updated.length} updated, ${report.unchanged.length} unchanged)`
11698
+ );
11699
+ printInfo("Skills are auto-discovered by Claude Code from .claude/skills/.");
11700
+ return 0;
11701
+ }
11702
+ };
11703
+ var SkillsListCommand = class extends Command5 {
11704
+ static paths = [["skills", "list"]];
11705
+ static usage = Command5.Usage({
11706
+ description: "List available consumer skills and their installed status"
11707
+ });
11708
+ json = Option5.Boolean("--json", false);
11709
+ cwd = Option5.String("--cwd", { required: false });
11710
+ async execute() {
11711
+ if (this.json) setJsonMode(true);
11712
+ const ctx = await loadContext({ cwd: this.cwd, json: this.json, skipDetection: true });
11713
+ const skills = availableSkills();
11714
+ const targetDir = skillsTargetDir(ctx.cwd);
11715
+ const rows = skills.map((name) => {
11716
+ const dir = path26.join(targetDir, name);
11717
+ return { name, status: fs15.existsSync(dir) ? "installed" : "available" };
11718
+ });
11719
+ if (isJsonMode()) {
11720
+ printJson({ command: "skills list", target: targetDir, skills: rows });
11721
+ return 0;
11722
+ }
11723
+ if (rows.length === 0) {
11724
+ printWarning("No consumer skills bundled with this package build.");
11725
+ return 0;
11726
+ }
11727
+ const pad = (s, n) => s.length >= n ? s : s + " ".repeat(n - s.length);
11728
+ console.log(theme.muted(`${pad("NAME", 14)}STATUS`));
11729
+ for (const r of rows) console.log(`${pad(r.name, 14)}${r.status}`);
11730
+ return 0;
11731
+ }
11732
+ };
11733
+ var skillsNoun = {
11734
+ name: "skills",
11735
+ commandClasses: [SkillsInstallCommand, SkillsListCommand],
11736
+ summary: summary3,
11737
+ hints: hints3
11738
+ };
11739
+ var skills_default = skillsNoun;
11740
+
11741
+ // src/cli/commands/project-update.ts
11742
+ var NON_RUNTIME_SUBSYSTEMS = /* @__PURE__ */ new Set(["openapi-config", "auth-integrations"]);
11743
+ function syncVendoredRuntime(cwd, write) {
11744
+ const changes = [];
11745
+ for (const v of VENDORED_RUNTIME_FILES) {
11746
+ const dest = path27.join(cwd, v.target);
11747
+ const content = loadRuntimeFile(v.runtime);
11748
+ const existing = fs16.existsSync(dest) ? fs16.readFileSync(dest, "utf-8") : null;
11749
+ let action;
11750
+ if (existing === null) action = "created";
11751
+ else if (existing === content) action = "unchanged";
11752
+ else action = "updated";
11753
+ if (write && action !== "unchanged") {
11754
+ fs16.mkdirSync(path27.dirname(dest), { recursive: true });
11755
+ fs16.writeFileSync(dest, content, "utf-8");
11756
+ }
11757
+ changes.push({ path: v.target, action });
11758
+ }
11759
+ return changes;
11760
+ }
11761
+ async function syncSubsystemRuntime(cwd, inst, write) {
11762
+ if (NON_RUNTIME_SUBSYSTEMS.has(inst.name)) {
11763
+ return { name: inst.name, changes: [], skippedReason: "config-only / vendored elsewhere" };
11764
+ }
11765
+ const source = subsystemSource(inst.name);
11766
+ if (!fs16.existsSync(source)) {
11767
+ return { name: inst.name, changes: [], skippedReason: "no runtime source in package" };
11768
+ }
11769
+ const subsystemsRoot = path27.dirname(inst.path);
11770
+ const result = await copyRuntime({
11771
+ sourceDir: source,
11772
+ targetDir: inst.path,
11773
+ filter: backendFileFilter(inst.backend, inst.name),
11774
+ resolveDeps: true,
11775
+ runtimeRoot: runtimeRoot2(),
11776
+ depsTargetRoot: path27.resolve(subsystemsRoot, ".."),
11777
+ dryRun: !write,
11778
+ // Refresh files already vendored for this subsystem; never install new
11779
+ // ones (that's `subsystem install`). copyRuntime classifies accurately
11780
+ // in dry-run too, so this report is correct either way.
11781
+ onlyExisting: true
11782
+ });
11783
+ const changes = [];
11784
+ for (const p of result.written) changes.push({ path: rel(cwd, p), action: "created" });
11785
+ for (const p of result.updated) changes.push({ path: rel(cwd, p), action: "updated" });
11786
+ for (const p of result.unchanged) changes.push({ path: rel(cwd, p), action: "unchanged" });
11787
+ return { name: inst.name, changes };
11788
+ }
11789
+ function rel(cwd, abs) {
11790
+ return path27.relative(cwd, abs) || abs;
11791
+ }
11792
+ var ProjectUpdateCommand = class extends Command6 {
11793
+ static paths = [["project", "update"]];
11794
+ static usage = Command6.Usage({
11795
+ description: "Re-sync vendored runtime, installed subsystems, and consumer skills to the installed package version",
11796
+ examples: [
11797
+ ["Re-sync everything after a package bump", "codegen update"],
11798
+ ["Preview without writing", "codegen update --dry-run"],
11799
+ ["Overwrite even with uncommitted changes", "codegen update --force"],
11800
+ ["Skip the skills re-sync", "codegen update --skip-skills"]
11801
+ ]
11802
+ });
11803
+ dryRun = Option6.Boolean("--dry-run", false);
11804
+ force = Option6.Boolean("--force", false);
11805
+ skipSkills = Option6.Boolean("--skip-skills", false);
11806
+ skipSubsystems = Option6.Boolean("--skip-subsystems", false);
11807
+ json = Option6.Boolean("--json", false);
11808
+ cwd = Option6.String("--cwd", { required: false });
11809
+ configPath = Option6.String("--config", { required: false });
11810
+ async execute() {
11811
+ if (this.json) setJsonMode(true);
11812
+ const ctx = await loadContext({
11813
+ cwd: this.cwd,
11814
+ configPath: this.configPath,
11815
+ json: this.json,
11816
+ skipDetection: true
11817
+ });
11818
+ if (!ctx.isInitialized) {
11819
+ if (isJsonMode()) {
11820
+ printJson({ command: "project update", status: "not-initialized" });
11821
+ } else {
11822
+ printWarning("project is not initialized \u2014 run `codegen init` first");
11823
+ }
11824
+ return 1;
11825
+ }
11826
+ const installed = this.skipSubsystems ? [] : await detectInstalledSubsystems(ctx);
11827
+ if (!this.dryRun && !this.force) {
11828
+ const vendoredDry = syncVendoredRuntime(ctx.cwd, false);
11829
+ const vendoredDirtyCandidates = vendoredDry.filter((c) => c.action === "updated").map((c) => path27.join(ctx.cwd, c.path));
11830
+ const skillDirtyCandidates = this.skipSkills ? [] : runSkillsInstall({ cwd: ctx.cwd, dryRun: true }).report?.updated.map((e) => e.dest) ?? [];
11831
+ const subsystemDirs = installed.filter((i) => !NON_RUNTIME_SUBSYSTEMS.has(i.name)).map((i) => i.path);
11832
+ const gate = checkGitSafety(
11833
+ [...vendoredDirtyCandidates, ...skillDirtyCandidates, ...subsystemDirs],
11834
+ ctx.cwd
11835
+ );
11836
+ if (gate.inRepo && !gate.clean) {
11837
+ if (isJsonMode()) {
11838
+ printJson({
11839
+ command: "project update",
11840
+ status: "dirty-tree",
11841
+ dirty: gate.dirty
11842
+ });
11843
+ } else {
11844
+ printWarning(
11845
+ `Uncommitted changes in ${gate.dirty.length} file(s) that update would overwrite. Commit them or pass --force.`
11846
+ );
11847
+ for (const d of gate.dirty.slice(0, 10)) {
11848
+ console.log(` ${theme.muted(icons.dash)} ${d}`);
11849
+ }
11850
+ }
11851
+ return 1;
11852
+ }
11853
+ }
11854
+ const write = !this.dryRun;
11855
+ const vendored = syncVendoredRuntime(ctx.cwd, write);
11856
+ const subsystemResults = [];
11857
+ for (const inst of installed) {
11858
+ subsystemResults.push(await syncSubsystemRuntime(ctx.cwd, inst, write));
11859
+ }
11860
+ const skills = this.skipSkills ? null : runSkillsInstall({ cwd: ctx.cwd, dryRun: this.dryRun });
11861
+ const tally = (changes) => ({
11862
+ created: changes.filter((c) => c.action === "created").length,
11863
+ updated: changes.filter((c) => c.action === "updated").length,
11864
+ unchanged: changes.filter((c) => c.action === "unchanged").length
11865
+ });
11866
+ if (isJsonMode()) {
11867
+ printJson({
11868
+ command: "project update",
11869
+ dryRun: this.dryRun,
11870
+ runtime: vendored,
11871
+ subsystems: subsystemResults.map((s) => ({
11872
+ name: s.name,
11873
+ skipped: s.skippedReason ?? null,
11874
+ ...tally(s.changes)
11875
+ })),
11876
+ skills: skills && skills.report ? {
11877
+ created: skills.report.created.length,
11878
+ updated: skills.report.updated.length,
11879
+ unchanged: skills.report.unchanged.length
11880
+ } : null
11881
+ });
11882
+ return 0;
11883
+ }
11884
+ printInfo(`Updating ${ctx.cwd} to the installed @pattern-stack/codegen version`);
11885
+ console.log("");
11886
+ renderSection("shared runtime", vendored);
11887
+ for (const s of subsystemResults) {
11888
+ if (s.skippedReason) {
11889
+ console.log(
11890
+ ` ${theme.muted(icons.dash)} ${theme.muted(`subsystem ${s.name} skipped (${s.skippedReason})`)}`
11891
+ );
11892
+ continue;
11893
+ }
11894
+ renderSection(`subsystem ${s.name}`, s.changes);
11895
+ }
11896
+ if (skills?.report) {
11897
+ renderSection(
11898
+ "skills",
11899
+ [
11900
+ ...skills.report.created.map((e) => ({ path: e.relPath, action: "created" })),
11901
+ ...skills.report.updated.map((e) => ({ path: e.relPath, action: "updated" })),
11902
+ ...skills.report.unchanged.map((e) => ({
11903
+ path: e.relPath,
11904
+ action: "unchanged"
11905
+ }))
11906
+ ]
11907
+ );
11908
+ } else if (this.skipSkills) {
11909
+ console.log(` ${theme.muted(icons.dash)} ${theme.muted("skills skipped (--skip-skills)")}`);
11910
+ }
11911
+ console.log("");
11912
+ if (this.dryRun) {
11913
+ printWarning("dry-run \u2014 no files written");
11914
+ return 0;
11915
+ }
11916
+ printSuccess("update complete");
11917
+ printInfo(
11918
+ "Schema shape changes are NOT re-synced \u2014 if a subsystem schema changed across versions, run `codegen subsystem install <name> --force --force-config`."
11919
+ );
11920
+ return 0;
11921
+ }
11922
+ };
11923
+ function renderSection(label, changes) {
11924
+ const created = changes.filter((c) => c.action === "created");
11925
+ const updated = changes.filter((c) => c.action === "updated");
11926
+ const unchanged = changes.filter((c) => c.action === "unchanged");
11927
+ if (created.length === 0 && updated.length === 0 && unchanged.length === 0) return;
11928
+ 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`;
11929
+ console.log(` ${head}`);
11930
+ for (const c of [...created, ...updated]) {
11931
+ console.log(` ${theme.success(icons.check)} ${theme.muted(c.action.padEnd(8))} ${c.path}`);
11932
+ }
11933
+ }
11934
+
11935
+ // src/cli/commands/project.ts
11936
+ async function summary4(ctx) {
11347
11937
  if (!ctx.isInitialized) {
11348
11938
  return {
11349
11939
  title: "project",
@@ -11368,7 +11958,7 @@ async function summary3(ctx) {
11368
11958
  body.push(` orm: ${orm}`);
11369
11959
  body.push(` architecture: ${arch}`);
11370
11960
  body.push(` entities: ${ctx.entityCount}`);
11371
- body.push(` subsystems: ${ctx.installedSubsystems.length}/4 installed`);
11961
+ body.push(` subsystems: ${ctx.installedSubsystems.length}/${SUBSYSTEMS.length} installed`);
11372
11962
  body.push(` generated: ${generated}`);
11373
11963
  return {
11374
11964
  title: "project",
@@ -11376,7 +11966,7 @@ async function summary3(ctx) {
11376
11966
  footer: `cwd: ${ctx.cwd}`
11377
11967
  };
11378
11968
  }
11379
- async function hints3(ctx) {
11969
+ async function hints4(ctx) {
11380
11970
  if (!ctx.isInitialized) {
11381
11971
  return [
11382
11972
  { command: "codegen init", description: "Scaffold consumer project" },
@@ -11394,17 +11984,17 @@ async function hints3(ctx) {
11394
11984
  } else {
11395
11985
  out.push({ command: "codegen entity", description: "Entity summary + hints" });
11396
11986
  }
11397
- if (ctx.installedSubsystems.length < 4) {
11987
+ if (ctx.installedSubsystems.length < SUBSYSTEMS.length) {
11398
11988
  out.push({
11399
11989
  command: "codegen subsystem",
11400
- description: "Install events/jobs/cache/storage"
11990
+ description: "Install events/jobs/cache/storage/\u2026"
11401
11991
  });
11402
11992
  }
11403
11993
  return out;
11404
11994
  }
11405
- var ProjectInitCommand = class extends Command5 {
11995
+ var ProjectInitCommand = class extends Command7 {
11406
11996
  static paths = [["project", "init"]];
11407
- static usage = Command5.Usage({
11997
+ static usage = Command7.Usage({
11408
11998
  description: "Scaffold a consumer project (config, shims, barrels, app.module)",
11409
11999
  examples: [
11410
12000
  ["Initialize with defaults", "codegen project init --yes"],
@@ -11413,12 +12003,14 @@ var ProjectInitCommand = class extends Command5 {
11413
12003
  ["Overwrite existing shims", "codegen project init --force"]
11414
12004
  ]
11415
12005
  });
11416
- yes = Option5.Boolean("--yes,-y", false);
11417
- dryRun = Option5.Boolean("--dry-run", false);
11418
- force = Option5.Boolean("--force", false);
11419
- withTsconfig = Option5.Boolean("--with-tsconfig", false);
11420
- json = Option5.Boolean("--json", false);
11421
- cwd = Option5.String("--cwd", { required: false });
12006
+ yes = Option7.Boolean("--yes,-y", false);
12007
+ dryRun = Option7.Boolean("--dry-run", false);
12008
+ force = Option7.Boolean("--force", false);
12009
+ withTsconfig = Option7.Boolean("--with-tsconfig", false);
12010
+ // Vendor consumer skills into .claude/skills by default; opt out with --no-skills.
12011
+ skills = Option7.Boolean("--skills", true);
12012
+ json = Option7.Boolean("--json", false);
12013
+ cwd = Option7.String("--cwd", { required: false });
11422
12014
  async execute() {
11423
12015
  if (this.json) setJsonMode(true);
11424
12016
  const ctx = await loadContext({
@@ -11449,6 +12041,7 @@ var ProjectInitCommand = class extends Command5 {
11449
12041
  console.log("");
11450
12042
  }
11451
12043
  const result = writePlan(plan);
12044
+ const skillsResult = this.skills ? runSkillsInstall({ cwd: ctx.cwd, dryRun: false }) : null;
11452
12045
  if (isJsonMode()) {
11453
12046
  printJson({
11454
12047
  command: "project init",
@@ -11457,7 +12050,12 @@ var ProjectInitCommand = class extends Command5 {
11457
12050
  created: result.created.map((e) => e.relPath),
11458
12051
  merged: result.merged.map((e) => e.relPath),
11459
12052
  overwritten: result.overwritten.map((e) => e.relPath),
11460
- skipped: result.skipped.map((e) => ({ path: e.relPath, reason: e.reason }))
12053
+ skipped: result.skipped.map((e) => ({ path: e.relPath, reason: e.reason })),
12054
+ skills: skillsResult && skillsResult.report ? {
12055
+ created: skillsResult.report.created.length,
12056
+ updated: skillsResult.report.updated.length,
12057
+ unchanged: skillsResult.report.unchanged.length
12058
+ } : null
11461
12059
  });
11462
12060
  return 0;
11463
12061
  }
@@ -11486,6 +12084,22 @@ var ProjectInitCommand = class extends Command5 {
11486
12084
  ` ${theme.muted(icons.dash)} ${theme.muted("skip ")} ${e.relPath}${e.reason ? theme.muted(" (" + e.reason + ")") : ""}`
11487
12085
  );
11488
12086
  }
12087
+ if (skillsResult?.report) {
12088
+ const r = skillsResult.report;
12089
+ console.log(
12090
+ ` ${theme.success(icons.check)} ${theme.muted("skills ")} .claude/skills/ ${theme.muted(
12091
+ `(${r.created.length} new, ${r.updated.length} updated, ${r.unchanged.length} unchanged)`
12092
+ )}`
12093
+ );
12094
+ } else if (!this.skills) {
12095
+ console.log(
12096
+ ` ${theme.muted(icons.dash)} ${theme.muted("skip ")} .claude/skills/ ${theme.muted("(--no-skills)")}`
12097
+ );
12098
+ } else if (skillsResult && !skillsResult.ok) {
12099
+ console.log(
12100
+ ` ${theme.warning(icons.warning)} ${theme.muted("skills ")} ${theme.muted(skillsResult.error ?? "skills install failed")}`
12101
+ );
12102
+ }
11489
12103
  console.log("");
11490
12104
  printInfo("Next steps:");
11491
12105
  console.log(` 1. ${theme.system("bun add")} the peer deps (see docs/CONSUMER-SETUP.md)`);
@@ -11498,10 +12112,10 @@ var ProjectInitCommand = class extends Command5 {
11498
12112
  };
11499
12113
  function askConfirm(question) {
11500
12114
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11501
- return new Promise((resolve3) => {
12115
+ return new Promise((resolve4) => {
11502
12116
  rl.question(`${question} [Y/n] `, (answer) => {
11503
12117
  rl.close();
11504
- resolve3(answer.trim().toLowerCase() !== "n");
12118
+ resolve4(answer.trim().toLowerCase() !== "n");
11505
12119
  });
11506
12120
  });
11507
12121
  }
@@ -11534,9 +12148,9 @@ function renderPlanOnly(plan, opts) {
11534
12148
  }
11535
12149
  return 0;
11536
12150
  }
11537
- var ProjectScanCommand = class extends Command5 {
12151
+ var ProjectScanCommand = class extends Command7 {
11538
12152
  static paths = [["project", "scan"]];
11539
- static usage = Command5.Usage({
12153
+ static usage = Command7.Usage({
11540
12154
  description: "Detect framework/ORM/architecture and propose a codegen.config.yaml",
11541
12155
  examples: [
11542
12156
  ["Scan the current directory", "codegen project scan"],
@@ -11544,17 +12158,17 @@ var ProjectScanCommand = class extends Command5 {
11544
12158
  ["Preview only", "codegen project scan --dry-run"]
11545
12159
  ]
11546
12160
  });
11547
- directory = Option5.String({ required: false });
11548
- write = Option5.Boolean("--write", false);
11549
- dryRun = Option5.Boolean("--dry-run", false);
11550
- verbose = Option5.Boolean("--verbose,-v", false);
11551
- json = Option5.Boolean("--json", false);
11552
- cwd = Option5.String("--cwd", { required: false });
12161
+ directory = Option7.String({ required: false });
12162
+ write = Option7.Boolean("--write", false);
12163
+ dryRun = Option7.Boolean("--dry-run", false);
12164
+ verbose = Option7.Boolean("--verbose,-v", false);
12165
+ json = Option7.Boolean("--json", false);
12166
+ cwd = Option7.String("--cwd", { required: false });
11553
12167
  async execute() {
11554
12168
  if (this.json) setJsonMode(true);
11555
- const baseCwd = this.cwd ? path25.resolve(this.cwd) : process.cwd();
11556
- const target = this.directory ? path25.resolve(baseCwd, this.directory) : baseCwd;
11557
- if (!fs14.existsSync(target)) {
12169
+ const baseCwd = this.cwd ? path28.resolve(this.cwd) : process.cwd();
12170
+ const target = this.directory ? path28.resolve(baseCwd, this.directory) : baseCwd;
12171
+ if (!fs17.existsSync(target)) {
11558
12172
  printError(`Directory not found: ${target}`);
11559
12173
  return 1;
11560
12174
  }
@@ -11604,8 +12218,8 @@ var ProjectScanCommand = class extends Command5 {
11604
12218
  `architecture: ${profile.architecture.evidence.join(", ") || "\u2014"}`
11605
12219
  ]);
11606
12220
  }
11607
- const outPath = path25.join(target, "codegen.config.yaml");
11608
- const existsNow = fs14.existsSync(outPath);
12221
+ const outPath = path28.join(target, "codegen.config.yaml");
12222
+ const existsNow = fs17.existsSync(outPath);
11609
12223
  if (this.dryRun) {
11610
12224
  console.log("");
11611
12225
  printInfo("Dry run \u2014 proposed codegen.config.yaml:");
@@ -11618,7 +12232,7 @@ var ProjectScanCommand = class extends Command5 {
11618
12232
  printWarning(`${outPath} already exists \u2014 pass --force via edit; skipping.`);
11619
12233
  return 0;
11620
12234
  }
11621
- fs14.writeFileSync(outPath, yamlText);
12235
+ fs17.writeFileSync(outPath, yamlText);
11622
12236
  printSuccess(`wrote ${outPath}`);
11623
12237
  return 0;
11624
12238
  }
@@ -11636,14 +12250,14 @@ function printMutedBlock(title, lines) {
11636
12250
  console.log(theme.muted(title + ":"));
11637
12251
  for (const l of lines) console.log(theme.muted(" " + l));
11638
12252
  }
11639
- var ProjectConfigCommand = class extends Command5 {
12253
+ var ProjectConfigCommand = class extends Command7 {
11640
12254
  static paths = [["project", "config"]];
11641
- static usage = Command5.Usage({
12255
+ static usage = Command7.Usage({
11642
12256
  description: "Print the resolved codegen config (YAML or JSON)"
11643
12257
  });
11644
- json = Option5.Boolean("--json", false);
11645
- cwd = Option5.String("--cwd", { required: false });
11646
- configPath = Option5.String("--config", { required: false });
12258
+ json = Option7.Boolean("--json", false);
12259
+ cwd = Option7.String("--cwd", { required: false });
12260
+ configPath = Option7.String("--config", { required: false });
11647
12261
  async execute() {
11648
12262
  if (this.json) setJsonMode(true);
11649
12263
  const ctx = await loadContext({
@@ -11675,9 +12289,9 @@ var ProjectConfigCommand = class extends Command5 {
11675
12289
  return 0;
11676
12290
  }
11677
12291
  };
11678
- var ProjectInspectCommand = class extends Command5 {
12292
+ var ProjectInspectCommand = class extends Command7 {
11679
12293
  static paths = [["project", "inspect"]];
11680
- static usage = Command5.Usage({
12294
+ static usage = Command7.Usage({
11681
12295
  description: "Domain analysis, statistics, documentation, and manifest operations",
11682
12296
  examples: [
11683
12297
  ["Full analysis", "codegen project inspect --kind analyze"],
@@ -11687,20 +12301,20 @@ var ProjectInspectCommand = class extends Command5 {
11687
12301
  ["Review suggestions", "codegen project inspect --kind suggestions"]
11688
12302
  ]
11689
12303
  });
11690
- kind = Option5.String("--kind", { required: true });
11691
- dir = Option5.String({ required: false });
11692
- format = Option5.String("--format", "console");
11693
- output = Option5.String("--output,-o", { required: false });
11694
- strict = Option5.Boolean("--strict", false);
11695
- entity = Option5.String("--entity", { required: false });
11696
- force = Option5.Boolean("--force", false);
11697
- accept = Option5.String("--accept", { required: false });
11698
- skip = Option5.String("--skip", { required: false });
11699
- acceptAll = Option5.Boolean("--accept-all", false);
11700
- skipAll = Option5.Boolean("--skip-all", false);
11701
- json = Option5.Boolean("--json", false);
11702
- cwd = Option5.String("--cwd", { required: false });
11703
- configPath = Option5.String("--config", { required: false });
12304
+ kind = Option7.String("--kind", { required: true });
12305
+ dir = Option7.String({ required: false });
12306
+ format = Option7.String("--format", "console");
12307
+ output = Option7.String("--output,-o", { required: false });
12308
+ strict = Option7.Boolean("--strict", false);
12309
+ entity = Option7.String("--entity", { required: false });
12310
+ force = Option7.Boolean("--force", false);
12311
+ accept = Option7.String("--accept", { required: false });
12312
+ skip = Option7.String("--skip", { required: false });
12313
+ acceptAll = Option7.Boolean("--accept-all", false);
12314
+ skipAll = Option7.Boolean("--skip-all", false);
12315
+ json = Option7.Boolean("--json", false);
12316
+ cwd = Option7.String("--cwd", { required: false });
12317
+ configPath = Option7.String("--config", { required: false });
11704
12318
  async execute() {
11705
12319
  if (this.json || this.format === "json") setJsonMode(true);
11706
12320
  const ctx = await loadContext({
@@ -11725,12 +12339,12 @@ var ProjectInspectCommand = class extends Command5 {
11725
12339
  return 2;
11726
12340
  }
11727
12341
  resolveEntitiesDir(ctx) {
11728
- if (this.dir) return path25.resolve(ctx.cwd, this.dir);
11729
- return ctx.entitiesDir ?? path25.resolve(ctx.cwd, "entities");
12342
+ if (this.dir) return path28.resolve(ctx.cwd, this.dir);
12343
+ return ctx.entitiesDir ?? path28.resolve(ctx.cwd, "entities");
11730
12344
  }
11731
12345
  async runAnalysis(ctx, kind) {
11732
12346
  const entitiesDir = this.resolveEntitiesDir(ctx);
11733
- if (!entitiesDir || !fs14.existsSync(entitiesDir)) {
12347
+ if (!entitiesDir || !fs17.existsSync(entitiesDir)) {
11734
12348
  printError(`Directory not found: ${entitiesDir ?? "(no entities/ dir)"}`);
11735
12349
  return 1;
11736
12350
  }
@@ -11760,7 +12374,7 @@ var ProjectInspectCommand = class extends Command5 {
11760
12374
  out = formatConsole(filtered);
11761
12375
  }
11762
12376
  if (this.output) {
11763
- fs14.writeFileSync(this.output, out);
12377
+ fs17.writeFileSync(this.output, out);
11764
12378
  if (!isJsonMode()) printSuccess(`wrote ${this.output}`);
11765
12379
  } else {
11766
12380
  console.log(out);
@@ -11773,7 +12387,7 @@ var ProjectInspectCommand = class extends Command5 {
11773
12387
  }
11774
12388
  async runManifest(ctx) {
11775
12389
  const entitiesDir = this.resolveEntitiesDir(ctx);
11776
- if (!entitiesDir || !fs14.existsSync(entitiesDir)) {
12390
+ if (!entitiesDir || !fs17.existsSync(entitiesDir)) {
11777
12391
  printError(`Directory not found: ${entitiesDir ?? "(no entities/ dir)"}`);
11778
12392
  return 1;
11779
12393
  }
@@ -11906,9 +12520,9 @@ function formatStatsConsole(result) {
11906
12520
  lines.push("");
11907
12521
  return lines.join("\n");
11908
12522
  }
11909
- var ProjectGraphCommand = class extends Command5 {
12523
+ var ProjectGraphCommand = class extends Command7 {
11910
12524
  static paths = [["project", "graph"]];
11911
- static usage = Command5.Usage({
12525
+ static usage = Command7.Usage({
11912
12526
  description: "Visualize the entity-relationship graph in a browser",
11913
12527
  examples: [
11914
12528
  ["Open interactive graph viewer", "codegen project graph"],
@@ -11916,11 +12530,11 @@ var ProjectGraphCommand = class extends Command5 {
11916
12530
  ["Write graph JSON to file", "codegen project graph --output graph.json"]
11917
12531
  ]
11918
12532
  });
11919
- dir = Option5.String({ required: false });
11920
- output = Option5.String("--output,-o", { required: false });
11921
- json = Option5.Boolean("--json", false);
11922
- cwd = Option5.String("--cwd", { required: false });
11923
- configPath = Option5.String("--config", { required: false });
12533
+ dir = Option7.String({ required: false });
12534
+ output = Option7.String("--output,-o", { required: false });
12535
+ json = Option7.Boolean("--json", false);
12536
+ cwd = Option7.String("--cwd", { required: false });
12537
+ configPath = Option7.String("--config", { required: false });
11924
12538
  async execute() {
11925
12539
  if (this.json) setJsonMode(true);
11926
12540
  const ctx = await loadContext({
@@ -11929,17 +12543,17 @@ var ProjectGraphCommand = class extends Command5 {
11929
12543
  json: this.json,
11930
12544
  skipDetection: true
11931
12545
  });
11932
- const entitiesDir = this.dir ? path25.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path25.resolve(ctx.cwd, "entities");
11933
- if (!fs14.existsSync(entitiesDir)) {
12546
+ const entitiesDir = this.dir ? path28.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path28.resolve(ctx.cwd, "entities");
12547
+ if (!fs17.existsSync(entitiesDir)) {
11934
12548
  printError(`Entity directory not found: ${entitiesDir}`);
11935
12549
  return 1;
11936
12550
  }
11937
12551
  const relCandidates = [
11938
- path25.resolve(path25.dirname(entitiesDir), "relationships"),
11939
- path25.resolve(entitiesDir, "relationships"),
11940
- path25.resolve(ctx.cwd, "relationships")
12552
+ path28.resolve(path28.dirname(entitiesDir), "relationships"),
12553
+ path28.resolve(entitiesDir, "relationships"),
12554
+ path28.resolve(ctx.cwd, "relationships")
11941
12555
  ];
11942
- const relationshipsDir = relCandidates.find((d) => fs14.existsSync(d));
12556
+ const relationshipsDir = relCandidates.find((d) => fs17.existsSync(d));
11943
12557
  const result = await analyzeDomain(entitiesDir, relationshipsDir);
11944
12558
  const serialized = serializeDomainGraph(result.graph);
11945
12559
  if (isJsonMode()) {
@@ -11953,20 +12567,20 @@ var ProjectGraphCommand = class extends Command5 {
11953
12567
  return 0;
11954
12568
  }
11955
12569
  if (this.output) {
11956
- const outPath = path25.resolve(ctx.cwd, this.output);
11957
- fs14.writeFileSync(outPath, JSON.stringify(serialized, null, 2));
12570
+ const outPath = path28.resolve(ctx.cwd, this.output);
12571
+ fs17.writeFileSync(outPath, JSON.stringify(serialized, null, 2));
11958
12572
  printSuccess(`Graph written to ${outPath}`);
11959
12573
  printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
11960
12574
  return 0;
11961
12575
  }
11962
12576
  const os = await import("os");
11963
- const tmpDir = fs14.mkdtempSync(path25.join(os.default.tmpdir(), "codegen-graph-"));
11964
- const graphPath = path25.join(tmpDir, "graph.json");
11965
- fs14.writeFileSync(graphPath, JSON.stringify(serialized, null, 2));
11966
- const viewerDir = path25.resolve(import.meta.dirname, "..", "..", "..", "tools", "schema-graph-viewer");
11967
- const viewerDist = path25.join(viewerDir, "dist", "index.html");
11968
- if (fs14.existsSync(viewerDist)) {
11969
- fs14.copyFileSync(graphPath, path25.join(viewerDir, "dist", "graph.json"));
12577
+ const tmpDir = fs17.mkdtempSync(path28.join(os.default.tmpdir(), "codegen-graph-"));
12578
+ const graphPath = path28.join(tmpDir, "graph.json");
12579
+ fs17.writeFileSync(graphPath, JSON.stringify(serialized, null, 2));
12580
+ const viewerDir = path28.resolve(import.meta.dirname, "..", "..", "..", "tools", "schema-graph-viewer");
12581
+ const viewerDist = path28.join(viewerDir, "dist", "index.html");
12582
+ if (fs17.existsSync(viewerDist)) {
12583
+ fs17.copyFileSync(graphPath, path28.join(viewerDir, "dist", "graph.json"));
11970
12584
  printSuccess("Graph exported");
11971
12585
  printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
11972
12586
  printInfo(`Graph JSON: ${graphPath}`);
@@ -11988,18 +12602,19 @@ var projectNoun = {
11988
12602
  ProjectConfigCommand,
11989
12603
  ProjectInspectCommand,
11990
12604
  ProjectGraphCommand,
11991
- ProjectUpgradeOpenapiCommand
12605
+ ProjectUpgradeOpenapiCommand,
12606
+ ProjectUpdateCommand
11992
12607
  ],
11993
- summary: summary3,
11994
- hints: hints3
12608
+ summary: summary4,
12609
+ hints: hints4
11995
12610
  };
11996
12611
  var project_default = projectNoun;
11997
12612
 
11998
12613
  // src/cli/commands/dev.ts
11999
- import fs15 from "fs";
12000
- import path26 from "path";
12614
+ import fs18 from "fs";
12615
+ import path29 from "path";
12001
12616
  import { execSync as execSync3, spawn, spawnSync } from "child_process";
12002
- import { Command as Command6, Option as Option6 } from "clipanion";
12617
+ import { Command as Command8, Option as Option8 } from "clipanion";
12003
12618
  var DEFAULT_APP_PORT = 3e3;
12004
12619
  var DEFAULT_PG_PORT = 5433;
12005
12620
  var DEFAULT_REDIS_PORT = 6380;
@@ -12030,33 +12645,33 @@ function getRedisPort(_ctx) {
12030
12645
  return Number(process.env.DEV_REDIS_PORT ?? DEFAULT_REDIS_PORT);
12031
12646
  }
12032
12647
  function composeFilePath(cwd) {
12033
- const devPath = path26.join(cwd, COMPOSE_FILE);
12034
- if (fs15.existsSync(devPath)) return devPath;
12035
- const rootPath = path26.join(cwd, "docker-compose.yml");
12036
- if (fs15.existsSync(rootPath)) return rootPath;
12648
+ const devPath = path29.join(cwd, COMPOSE_FILE);
12649
+ if (fs18.existsSync(devPath)) return devPath;
12650
+ const rootPath = path29.join(cwd, "docker-compose.yml");
12651
+ if (fs18.existsSync(rootPath)) return rootPath;
12037
12652
  return devPath;
12038
12653
  }
12039
12654
  function pidFilePath(cwd) {
12040
- return path26.join(cwd, PID_FILE);
12655
+ return path29.join(cwd, PID_FILE);
12041
12656
  }
12042
12657
  function readAppPid(cwd) {
12043
12658
  const p = pidFilePath(cwd);
12044
- if (!fs15.existsSync(p)) return null;
12045
- const pid = parseInt(fs15.readFileSync(p, "utf-8").trim(), 10);
12659
+ if (!fs18.existsSync(p)) return null;
12660
+ const pid = parseInt(fs18.readFileSync(p, "utf-8").trim(), 10);
12046
12661
  if (isNaN(pid)) return null;
12047
12662
  try {
12048
12663
  process.kill(pid, 0);
12049
12664
  return pid;
12050
12665
  } catch {
12051
- fs15.rmSync(p, { force: true });
12666
+ fs18.rmSync(p, { force: true });
12052
12667
  return null;
12053
12668
  }
12054
12669
  }
12055
12670
  function writeAppPid(cwd, pid) {
12056
- fs15.writeFileSync(pidFilePath(cwd), String(pid));
12671
+ fs18.writeFileSync(pidFilePath(cwd), String(pid));
12057
12672
  }
12058
12673
  function clearAppPid(cwd) {
12059
- fs15.rmSync(pidFilePath(cwd), { force: true });
12674
+ fs18.rmSync(pidFilePath(cwd), { force: true });
12060
12675
  }
12061
12676
  function checkPostgres(cwd, port) {
12062
12677
  const r = runCmd(`docker exec codegen-dev-postgres pg_isready -U postgres`, cwd, {
@@ -12096,8 +12711,10 @@ function checkApp(cwd, port) {
12096
12711
  };
12097
12712
  }
12098
12713
  function listEntityNames(ctx) {
12099
- if (!ctx.entitiesDir || !fs15.existsSync(ctx.entitiesDir)) return [];
12100
- return fs15.readdirSync(ctx.entitiesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => f.replace(/\.ya?ml$/, ""));
12714
+ if (!ctx.entitiesDir || !fs18.existsSync(ctx.entitiesDir)) return [];
12715
+ return findYamlFiles(ctx.entitiesDir).map(
12716
+ (f) => path29.basename(f).replace(/\.ya?ml$/, "")
12717
+ );
12101
12718
  }
12102
12719
  function formatServiceLine(svc) {
12103
12720
  const icon = svc.healthy ? theme.success(icons.check) : theme.error(icons.error);
@@ -12106,8 +12723,8 @@ function formatServiceLine(svc) {
12106
12723
  return `${icon} ${svc.name.padEnd(12)} ${theme.muted(`${svc.host}:${svc.port}`)} ${status}${pidStr}`;
12107
12724
  }
12108
12725
  function ensureComposeFile(cwd, pgPort, redisPort) {
12109
- const composePath = path26.join(cwd, COMPOSE_FILE);
12110
- if (fs15.existsSync(composePath)) return composePath;
12726
+ const composePath = path29.join(cwd, COMPOSE_FILE);
12727
+ if (fs18.existsSync(composePath)) return composePath;
12111
12728
  const content = `# Auto-generated by codegen dev
12112
12729
  # Ports offset from defaults to avoid conflicts with other local services.
12113
12730
  services:
@@ -12142,22 +12759,22 @@ services:
12142
12759
  volumes:
12143
12760
  codegen-dev-pgdata:
12144
12761
  `;
12145
- fs15.writeFileSync(composePath, content);
12762
+ fs18.writeFileSync(composePath, content);
12146
12763
  return composePath;
12147
12764
  }
12148
- var DevUpCommand = class extends Command6 {
12765
+ var DevUpCommand = class extends Command8 {
12149
12766
  static paths = [["dev", "up"]];
12150
- static usage = Command6.Usage({
12767
+ static usage = Command8.Usage({
12151
12768
  description: "Start Docker services (Postgres + Redis), run migrations, start the NestJS app",
12152
12769
  examples: [
12153
12770
  ["Start everything", "codegen dev up"],
12154
12771
  ["Skip app start (services only)", "codegen dev up --no-app"]
12155
12772
  ]
12156
12773
  });
12157
- noApp = Option6.Boolean("--no-app", false);
12158
- json = Option6.Boolean("--json", false);
12159
- cwd = Option6.String("--cwd", { required: false });
12160
- configPath = Option6.String("--config", { required: false });
12774
+ noApp = Option8.Boolean("--no-app", false);
12775
+ json = Option8.Boolean("--json", false);
12776
+ cwd = Option8.String("--cwd", { required: false });
12777
+ configPath = Option8.String("--config", { required: false });
12161
12778
  async execute() {
12162
12779
  if (this.json) setJsonMode(true);
12163
12780
  const ctx = await loadContext({
@@ -12191,7 +12808,7 @@ var DevUpCommand = class extends Command6 {
12191
12808
  if (!pgReady) printWarning("postgres did not become healthy in time");
12192
12809
  if (!redisReady) printWarning("redis did not become healthy in time");
12193
12810
  const drizzleConfig = ["drizzle.config.ts", "drizzle.config.js"].find(
12194
- (f) => fs15.existsSync(path26.join(ctx.cwd, f))
12811
+ (f) => fs18.existsSync(path29.join(ctx.cwd, f))
12195
12812
  );
12196
12813
  if (drizzleConfig) {
12197
12814
  if (!isJsonMode()) printInfo("pushing database schema...");
@@ -12211,8 +12828,8 @@ var DevUpCommand = class extends Command6 {
12211
12828
  if (!isJsonMode()) printInfo("starting NestJS app...");
12212
12829
  const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
12213
12830
  const redisUrl = `redis://localhost:${redisPort}`;
12214
- const logFile = path26.join(ctx.cwd, ".dev-app.log");
12215
- const logFd = fs15.openSync(logFile, "a");
12831
+ const logFile = path29.join(ctx.cwd, ".dev-app.log");
12832
+ const logFd = fs18.openSync(logFile, "a");
12216
12833
  const child = spawn("bun", ["src/main.ts"], {
12217
12834
  cwd: ctx.cwd,
12218
12835
  detached: true,
@@ -12225,7 +12842,7 @@ var DevUpCommand = class extends Command6 {
12225
12842
  }
12226
12843
  });
12227
12844
  child.unref();
12228
- fs15.closeSync(logFd);
12845
+ fs18.closeSync(logFd);
12229
12846
  if (child.pid) {
12230
12847
  writeAppPid(ctx.cwd, child.pid);
12231
12848
  spawnSync("sleep", ["2"]);
@@ -12255,15 +12872,15 @@ var DevUpCommand = class extends Command6 {
12255
12872
  return 0;
12256
12873
  }
12257
12874
  };
12258
- var DevDownCommand = class extends Command6 {
12875
+ var DevDownCommand = class extends Command8 {
12259
12876
  static paths = [["dev", "down"]];
12260
- static usage = Command6.Usage({
12877
+ static usage = Command8.Usage({
12261
12878
  description: "Stop Docker services and the NestJS app"
12262
12879
  });
12263
- volumes = Option6.Boolean("--volumes,-v", false);
12264
- json = Option6.Boolean("--json", false);
12265
- cwd = Option6.String("--cwd", { required: false });
12266
- configPath = Option6.String("--config", { required: false });
12880
+ volumes = Option8.Boolean("--volumes,-v", false);
12881
+ json = Option8.Boolean("--json", false);
12882
+ cwd = Option8.String("--cwd", { required: false });
12883
+ configPath = Option8.String("--config", { required: false });
12267
12884
  async execute() {
12268
12885
  if (this.json) setJsonMode(true);
12269
12886
  const ctx = await loadContext({
@@ -12299,14 +12916,14 @@ var DevDownCommand = class extends Command6 {
12299
12916
  return 0;
12300
12917
  }
12301
12918
  };
12302
- var DevStatusCommand = class extends Command6 {
12919
+ var DevStatusCommand = class extends Command8 {
12303
12920
  static paths = [["dev", "status"]];
12304
- static usage = Command6.Usage({
12921
+ static usage = Command8.Usage({
12305
12922
  description: "Show status of Docker services and the NestJS app"
12306
12923
  });
12307
- json = Option6.Boolean("--json", false);
12308
- cwd = Option6.String("--cwd", { required: false });
12309
- configPath = Option6.String("--config", { required: false });
12924
+ json = Option8.Boolean("--json", false);
12925
+ cwd = Option8.String("--cwd", { required: false });
12926
+ configPath = Option8.String("--config", { required: false });
12310
12927
  async execute() {
12311
12928
  if (this.json) setJsonMode(true);
12312
12929
  const ctx = await loadContext({
@@ -12334,9 +12951,9 @@ var DevStatusCommand = class extends Command6 {
12334
12951
  return 0;
12335
12952
  }
12336
12953
  };
12337
- var DevLogsCommand = class extends Command6 {
12954
+ var DevLogsCommand = class extends Command8 {
12338
12955
  static paths = [["dev", "logs"]];
12339
- static usage = Command6.Usage({
12956
+ static usage = Command8.Usage({
12340
12957
  description: "Tail application and Docker service logs",
12341
12958
  examples: [
12342
12959
  ["Tail app logs", "codegen dev logs"],
@@ -12344,11 +12961,11 @@ var DevLogsCommand = class extends Command6 {
12344
12961
  ["Show last N lines", "codegen dev logs --tail 50"]
12345
12962
  ]
12346
12963
  });
12347
- docker = Option6.Boolean("--docker", false);
12348
- tail = Option6.String("--tail", "30");
12349
- json = Option6.Boolean("--json", false);
12350
- cwd = Option6.String("--cwd", { required: false });
12351
- configPath = Option6.String("--config", { required: false });
12964
+ docker = Option8.Boolean("--docker", false);
12965
+ tail = Option8.String("--tail", "30");
12966
+ json = Option8.Boolean("--json", false);
12967
+ cwd = Option8.String("--cwd", { required: false });
12968
+ configPath = Option8.String("--config", { required: false });
12352
12969
  async execute() {
12353
12970
  if (this.json) setJsonMode(true);
12354
12971
  const ctx = await loadContext({
@@ -12369,8 +12986,8 @@ var DevLogsCommand = class extends Command6 {
12369
12986
  }
12370
12987
  return 0;
12371
12988
  }
12372
- const logFile = path26.join(ctx.cwd, ".dev-app.log");
12373
- if (!fs15.existsSync(logFile)) {
12989
+ const logFile = path29.join(ctx.cwd, ".dev-app.log");
12990
+ if (!fs18.existsSync(logFile)) {
12374
12991
  printInfo("no app logs found \u2014 is the app running?");
12375
12992
  return 0;
12376
12993
  }
@@ -12382,14 +12999,14 @@ var DevLogsCommand = class extends Command6 {
12382
12999
  return 0;
12383
13000
  }
12384
13001
  };
12385
- var DevRestartCommand = class extends Command6 {
13002
+ var DevRestartCommand = class extends Command8 {
12386
13003
  static paths = [["dev", "restart"]];
12387
- static usage = Command6.Usage({
13004
+ static usage = Command8.Usage({
12388
13005
  description: "Restart the NestJS app (keep Docker services running)"
12389
13006
  });
12390
- json = Option6.Boolean("--json", false);
12391
- cwd = Option6.String("--cwd", { required: false });
12392
- configPath = Option6.String("--config", { required: false });
13007
+ json = Option8.Boolean("--json", false);
13008
+ cwd = Option8.String("--cwd", { required: false });
13009
+ configPath = Option8.String("--config", { required: false });
12393
13010
  async execute() {
12394
13011
  if (this.json) setJsonMode(true);
12395
13012
  const ctx = await loadContext({
@@ -12413,8 +13030,8 @@ var DevRestartCommand = class extends Command6 {
12413
13030
  spawnSync("sleep", ["1"]);
12414
13031
  const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
12415
13032
  const redisUrl = `redis://localhost:${redisPort}`;
12416
- const logFile = path26.join(ctx.cwd, ".dev-app.log");
12417
- const logFd = fs15.openSync(logFile, "a");
13033
+ const logFile = path29.join(ctx.cwd, ".dev-app.log");
13034
+ const logFd = fs18.openSync(logFile, "a");
12418
13035
  const child = spawn("bun", ["src/main.ts"], {
12419
13036
  cwd: ctx.cwd,
12420
13037
  detached: true,
@@ -12427,7 +13044,7 @@ var DevRestartCommand = class extends Command6 {
12427
13044
  }
12428
13045
  });
12429
13046
  child.unref();
12430
- fs15.closeSync(logFd);
13047
+ fs18.closeSync(logFd);
12431
13048
  if (child.pid) {
12432
13049
  writeAppPid(ctx.cwd, child.pid);
12433
13050
  spawnSync("sleep", ["2"]);
@@ -12489,7 +13106,7 @@ function renderDevStatus(ctx) {
12489
13106
  { command: "/dev-test", description: "Run test suite + endpoint verification" }
12490
13107
  ]);
12491
13108
  }
12492
- async function summary4(ctx) {
13109
+ async function summary5(ctx) {
12493
13110
  const pgPort = getPgPort(ctx);
12494
13111
  const redisPort = getRedisPort(ctx);
12495
13112
  const appPort = getAppPort(ctx);
@@ -12513,7 +13130,7 @@ async function summary4(ctx) {
12513
13130
  footer: `${running}/${total} services healthy`
12514
13131
  };
12515
13132
  }
12516
- async function hints4(ctx) {
13133
+ async function hints5(ctx) {
12517
13134
  const app = checkApp(ctx.cwd, getAppPort(ctx));
12518
13135
  if (!app.healthy) {
12519
13136
  return [
@@ -12536,21 +13153,20 @@ var devNoun = {
12536
13153
  DevLogsCommand,
12537
13154
  DevRestartCommand
12538
13155
  ],
12539
- summary: summary4,
12540
- hints: hints4
13156
+ summary: summary5,
13157
+ hints: hints5
12541
13158
  };
12542
13159
  var dev_default = devNoun;
12543
13160
 
12544
13161
  // src/cli/commands/relationship.ts
12545
- import fs16 from "fs";
12546
- import path27 from "path";
12547
- import { Command as Command7, Option as Option7 } from "clipanion";
13162
+ import fs19 from "fs";
13163
+ import path30 from "path";
13164
+ import { Command as Command9, Option as Option9 } from "clipanion";
12548
13165
  function listRelationshipYamls2(dir) {
12549
- if (!fs16.existsSync(dir)) return [];
12550
- return fs16.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).filter((f) => {
12551
- const fullPath = path27.join(dir, f);
12552
- return detectYamlType(fullPath) === "relationship";
12553
- }).map((f) => path27.join(dir, f));
13166
+ if (!fs19.existsSync(dir)) return [];
13167
+ return findYamlFiles(dir).filter(
13168
+ (full) => detectYamlType(full) === "relationship"
13169
+ );
12554
13170
  }
12555
13171
  function summarizeRelationshipFile(filePath) {
12556
13172
  const result = loadRelationshipFromYaml(filePath);
@@ -12570,8 +13186,8 @@ function summarizeRelationshipFile(filePath) {
12570
13186
  function padRight2(s, n) {
12571
13187
  return s.length >= n ? s : s + " ".repeat(n - s.length);
12572
13188
  }
12573
- async function summary5(ctx) {
12574
- const relDir = path27.resolve(ctx.cwd, "relationships");
13189
+ async function summary6(ctx) {
13190
+ const relDir = path30.resolve(ctx.cwd, "relationships");
12575
13191
  const files = listRelationshipYamls2(relDir);
12576
13192
  if (files.length === 0) {
12577
13193
  return {
@@ -12603,16 +13219,16 @@ async function summary5(ctx) {
12603
13219
  footer: `${rows.length} relationships`
12604
13220
  };
12605
13221
  }
12606
- async function hints5(_ctx) {
13222
+ async function hints6(_ctx) {
12607
13223
  return [
12608
13224
  { command: "codegen relationship new <file>", description: "Generate one relationship" },
12609
13225
  { command: "codegen relationship new --all", description: "Generate all relationships" },
12610
13226
  { command: "codegen relationship list", description: "List relationship definitions" }
12611
13227
  ];
12612
13228
  }
12613
- var RelationshipNewCommand = class extends Command7 {
13229
+ var RelationshipNewCommand = class extends Command9 {
12614
13230
  static paths = [["relationship", "new"]];
12615
- static usage = Command7.Usage({
13231
+ static usage = Command9.Usage({
12616
13232
  description: "Generate code for one or more relationships from YAML",
12617
13233
  examples: [
12618
13234
  ["Generate a single relationship", "codegen relationship new relationships/person_organization.yaml"],
@@ -12620,13 +13236,13 @@ var RelationshipNewCommand = class extends Command7 {
12620
13236
  ["Preview without writing", "codegen relationship new relationships/person_organization.yaml --dry-run"]
12621
13237
  ]
12622
13238
  });
12623
- yaml = Option7.String({ required: false });
12624
- all = Option7.Boolean("--all", false);
12625
- dryRun = Option7.Boolean("--dry-run", false);
12626
- force = Option7.Boolean("--force", false);
12627
- json = Option7.Boolean("--json", false);
12628
- cwd = Option7.String("--cwd", { required: false });
12629
- configPath = Option7.String("--config", { required: false });
13239
+ yaml = Option9.String({ required: false });
13240
+ all = Option9.Boolean("--all", false);
13241
+ dryRun = Option9.Boolean("--dry-run", false);
13242
+ force = Option9.Boolean("--force", false);
13243
+ json = Option9.Boolean("--json", false);
13244
+ cwd = Option9.String("--cwd", { required: false });
13245
+ configPath = Option9.String("--config", { required: false });
12630
13246
  async execute() {
12631
13247
  if (this.json) setJsonMode(true);
12632
13248
  const ctx = await loadContext({
@@ -12641,14 +13257,14 @@ var RelationshipNewCommand = class extends Command7 {
12641
13257
  }
12642
13258
  let targets = [];
12643
13259
  if (this.all) {
12644
- const dir = path27.resolve(ctx.cwd, "relationships");
13260
+ const dir = path30.resolve(ctx.cwd, "relationships");
12645
13261
  targets = listRelationshipYamls2(dir);
12646
13262
  if (targets.length === 0) {
12647
13263
  printError(`No relationship YAML files found in ${dir}`);
12648
13264
  return 1;
12649
13265
  }
12650
13266
  } else if (this.yaml) {
12651
- targets = [path27.resolve(ctx.cwd, this.yaml)];
13267
+ targets = [path30.resolve(ctx.cwd, this.yaml)];
12652
13268
  } else {
12653
13269
  printError("Missing YAML path. Pass a file or --all.");
12654
13270
  return 2;
@@ -12665,7 +13281,7 @@ var RelationshipNewCommand = class extends Command7 {
12665
13281
  }
12666
13282
  if (invalid.length > 0) {
12667
13283
  for (const i of invalid) {
12668
- printError(`${path27.basename(i.file)} \u2014 ${i.message}`);
13284
+ printError(`${path30.basename(i.file)} \u2014 ${i.message}`);
12669
13285
  }
12670
13286
  if (!isJsonMode()) return 1;
12671
13287
  }
@@ -12696,7 +13312,7 @@ var RelationshipNewCommand = class extends Command7 {
12696
13312
  }
12697
13313
  const succeeded = [];
12698
13314
  const failed = [
12699
- ...invalid.map((i) => ({ name: path27.basename(i.file), file: i.file, message: i.message }))
13315
+ ...invalid.map((i) => ({ name: path30.basename(i.file), file: i.file, message: i.message }))
12700
13316
  ];
12701
13317
  for (const v of validated) {
12702
13318
  if (!isJsonMode()) {
@@ -12715,8 +13331,8 @@ var RelationshipNewCommand = class extends Command7 {
12715
13331
  if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
12716
13332
  }
12717
13333
  }
12718
- const entitiesDir = ctx.entitiesDir ?? path27.resolve(ctx.cwd, "entities");
12719
- const relationshipsDir = path27.resolve(ctx.cwd, "relationships");
13334
+ const entitiesDir = ctx.entitiesDir ?? path30.resolve(ctx.cwd, "entities");
13335
+ const relationshipsDir = path30.resolve(ctx.cwd, "relationships");
12720
13336
  const generatedDir = resolveGeneratedDir(ctx);
12721
13337
  const architecture = resolveArchitecture(ctx);
12722
13338
  let barrelResult = null;
@@ -12761,21 +13377,21 @@ var RelationshipNewCommand = class extends Command7 {
12761
13377
  }
12762
13378
  if (barrelResult) {
12763
13379
  printInfo(
12764
- `barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path27.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path27.relative(ctx.cwd, barrelResult.schemaBarrel)}`
13380
+ `barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path30.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path30.relative(ctx.cwd, barrelResult.schemaBarrel)}`
12765
13381
  );
12766
13382
  }
12767
13383
  }
12768
13384
  return failed.length === 0 ? 0 : 1;
12769
13385
  }
12770
13386
  };
12771
- var RelationshipListCommand = class extends Command7 {
13387
+ var RelationshipListCommand = class extends Command9 {
12772
13388
  static paths = [["relationship", "list"]];
12773
- static usage = Command7.Usage({
13389
+ static usage = Command9.Usage({
12774
13390
  description: "List defined relationships as a table"
12775
13391
  });
12776
- json = Option7.Boolean("--json", false);
12777
- cwd = Option7.String("--cwd", { required: false });
12778
- configPath = Option7.String("--config", { required: false });
13392
+ json = Option9.Boolean("--json", false);
13393
+ cwd = Option9.String("--cwd", { required: false });
13394
+ configPath = Option9.String("--config", { required: false });
12779
13395
  async execute() {
12780
13396
  if (this.json) setJsonMode(true);
12781
13397
  const ctx = await loadContext({
@@ -12784,7 +13400,7 @@ var RelationshipListCommand = class extends Command7 {
12784
13400
  json: this.json,
12785
13401
  skipDetection: true
12786
13402
  });
12787
- const relDir = path27.resolve(ctx.cwd, "relationships");
13403
+ const relDir = path30.resolve(ctx.cwd, "relationships");
12788
13404
  const files = listRelationshipYamls2(relDir);
12789
13405
  if (files.length === 0) {
12790
13406
  printInfo("No relationship definitions found.");
@@ -12818,14 +13434,14 @@ var RelationshipListCommand = class extends Command7 {
12818
13434
  var relationshipNoun = {
12819
13435
  name: "relationship",
12820
13436
  commandClasses: [RelationshipNewCommand, RelationshipListCommand],
12821
- summary: summary5,
12822
- hints: hints5
13437
+ summary: summary6,
13438
+ hints: hints6
12823
13439
  };
12824
13440
  var relationship_default = relationshipNoun;
12825
13441
 
12826
13442
  // src/cli/commands/junction.ts
12827
- import path28 from "path";
12828
- import { Command as Command8, Option as Option8 } from "clipanion";
13443
+ import path31 from "path";
13444
+ import { Command as Command10, Option as Option10 } from "clipanion";
12829
13445
  function summarizeJunctionFile(filePath) {
12830
13446
  const result = loadJunctionFromYaml(filePath);
12831
13447
  if (!result.success) return null;
@@ -12846,8 +13462,8 @@ function summarizeJunctionFile(filePath) {
12846
13462
  function padRight3(s, n) {
12847
13463
  return s.length >= n ? s : s + " ".repeat(n - s.length);
12848
13464
  }
12849
- async function summary6(ctx) {
12850
- const junctionDir = path28.resolve(ctx.cwd, "junctions");
13465
+ async function summary7(ctx) {
13466
+ const junctionDir = path31.resolve(ctx.cwd, "junctions");
12851
13467
  const files = listJunctionYamls(junctionDir);
12852
13468
  if (files.length === 0) {
12853
13469
  return {
@@ -12879,16 +13495,16 @@ async function summary6(ctx) {
12879
13495
  footer: `${rows.length} junctions`
12880
13496
  };
12881
13497
  }
12882
- async function hints6(_ctx) {
13498
+ async function hints7(_ctx) {
12883
13499
  return [
12884
13500
  { command: "codegen junction new <file>", description: "Generate one junction" },
12885
13501
  { command: "codegen junction new --all", description: "Generate all junctions" },
12886
13502
  { command: "codegen junction list", description: "List junction definitions" }
12887
13503
  ];
12888
13504
  }
12889
- var JunctionNewCommand = class extends Command8 {
13505
+ var JunctionNewCommand = class extends Command10 {
12890
13506
  static paths = [["junction", "new"]];
12891
- static usage = Command8.Usage({
13507
+ static usage = Command10.Usage({
12892
13508
  description: "Generate code for one or more junctions from YAML",
12893
13509
  examples: [
12894
13510
  ["Generate a single junction", "codegen junction new junctions/opportunity_contact.yaml"],
@@ -12896,13 +13512,13 @@ var JunctionNewCommand = class extends Command8 {
12896
13512
  ["Preview without writing", "codegen junction new junctions/opportunity_contact.yaml --dry-run"]
12897
13513
  ]
12898
13514
  });
12899
- yaml = Option8.String({ required: false });
12900
- all = Option8.Boolean("--all", false);
12901
- dryRun = Option8.Boolean("--dry-run", false);
12902
- force = Option8.Boolean("--force", false);
12903
- json = Option8.Boolean("--json", false);
12904
- cwd = Option8.String("--cwd", { required: false });
12905
- configPath = Option8.String("--config", { required: false });
13515
+ yaml = Option10.String({ required: false });
13516
+ all = Option10.Boolean("--all", false);
13517
+ dryRun = Option10.Boolean("--dry-run", false);
13518
+ force = Option10.Boolean("--force", false);
13519
+ json = Option10.Boolean("--json", false);
13520
+ cwd = Option10.String("--cwd", { required: false });
13521
+ configPath = Option10.String("--config", { required: false });
12906
13522
  async execute() {
12907
13523
  if (this.json) setJsonMode(true);
12908
13524
  const ctx = await loadContext({
@@ -12917,14 +13533,14 @@ var JunctionNewCommand = class extends Command8 {
12917
13533
  }
12918
13534
  let targets = [];
12919
13535
  if (this.all) {
12920
- const dir = path28.resolve(ctx.cwd, "junctions");
13536
+ const dir = path31.resolve(ctx.cwd, "junctions");
12921
13537
  targets = listJunctionYamls(dir);
12922
13538
  if (targets.length === 0) {
12923
13539
  printError(`No junction YAML files found in ${dir}`);
12924
13540
  return 1;
12925
13541
  }
12926
13542
  } else if (this.yaml) {
12927
- targets = [path28.resolve(ctx.cwd, this.yaml)];
13543
+ targets = [path31.resolve(ctx.cwd, this.yaml)];
12928
13544
  } else {
12929
13545
  printError("Missing YAML path. Pass a file or --all.");
12930
13546
  return 2;
@@ -12943,7 +13559,7 @@ var JunctionNewCommand = class extends Command8 {
12943
13559
  }
12944
13560
  if (invalid.length > 0) {
12945
13561
  for (const i of invalid) {
12946
- printError(`${path28.basename(i.file)} \u2014 ${i.message}`);
13562
+ printError(`${path31.basename(i.file)} \u2014 ${i.message}`);
12947
13563
  }
12948
13564
  if (!isJsonMode()) return 1;
12949
13565
  }
@@ -12974,7 +13590,7 @@ var JunctionNewCommand = class extends Command8 {
12974
13590
  }
12975
13591
  const succeeded = [];
12976
13592
  const failed = [
12977
- ...invalid.map((i) => ({ name: path28.basename(i.file), file: i.file, message: i.message }))
13593
+ ...invalid.map((i) => ({ name: path31.basename(i.file), file: i.file, message: i.message }))
12978
13594
  ];
12979
13595
  for (const v of validated) {
12980
13596
  if (!isJsonMode()) {
@@ -12993,9 +13609,9 @@ var JunctionNewCommand = class extends Command8 {
12993
13609
  if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
12994
13610
  }
12995
13611
  }
12996
- const entitiesDir = ctx.entitiesDir ?? path28.resolve(ctx.cwd, "entities");
12997
- const relationshipsDir = path28.resolve(ctx.cwd, "relationships");
12998
- const junctionsDir = path28.resolve(ctx.cwd, "junctions");
13612
+ const entitiesDir = ctx.entitiesDir ?? path31.resolve(ctx.cwd, "entities");
13613
+ const relationshipsDir = path31.resolve(ctx.cwd, "relationships");
13614
+ const junctionsDir = path31.resolve(ctx.cwd, "junctions");
12999
13615
  const generatedDir = resolveGeneratedDir(ctx);
13000
13616
  const architecture = resolveArchitecture(ctx);
13001
13617
  let barrelResult = null;
@@ -13041,21 +13657,21 @@ var JunctionNewCommand = class extends Command8 {
13041
13657
  }
13042
13658
  if (barrelResult) {
13043
13659
  printInfo(
13044
- `barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path28.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path28.relative(ctx.cwd, barrelResult.schemaBarrel)}`
13660
+ `barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path31.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path31.relative(ctx.cwd, barrelResult.schemaBarrel)}`
13045
13661
  );
13046
13662
  }
13047
13663
  }
13048
13664
  return failed.length === 0 ? 0 : 1;
13049
13665
  }
13050
13666
  };
13051
- var JunctionListCommand = class extends Command8 {
13667
+ var JunctionListCommand = class extends Command10 {
13052
13668
  static paths = [["junction", "list"]];
13053
- static usage = Command8.Usage({
13669
+ static usage = Command10.Usage({
13054
13670
  description: "List defined junctions as a table"
13055
13671
  });
13056
- json = Option8.Boolean("--json", false);
13057
- cwd = Option8.String("--cwd", { required: false });
13058
- configPath = Option8.String("--config", { required: false });
13672
+ json = Option10.Boolean("--json", false);
13673
+ cwd = Option10.String("--cwd", { required: false });
13674
+ configPath = Option10.String("--config", { required: false });
13059
13675
  async execute() {
13060
13676
  if (this.json) setJsonMode(true);
13061
13677
  const ctx = await loadContext({
@@ -13064,7 +13680,7 @@ var JunctionListCommand = class extends Command8 {
13064
13680
  json: this.json,
13065
13681
  skipDetection: true
13066
13682
  });
13067
- const junctionDir = path28.resolve(ctx.cwd, "junctions");
13683
+ const junctionDir = path31.resolve(ctx.cwd, "junctions");
13068
13684
  const files = listJunctionYamls(junctionDir);
13069
13685
  if (files.length === 0) {
13070
13686
  printInfo("No junction definitions found.");
@@ -13098,16 +13714,16 @@ var JunctionListCommand = class extends Command8 {
13098
13714
  var junctionNoun = {
13099
13715
  name: "junction",
13100
13716
  commandClasses: [JunctionNewCommand, JunctionListCommand],
13101
- summary: summary6,
13102
- hints: hints6
13717
+ summary: summary7,
13718
+ hints: hints7
13103
13719
  };
13104
13720
  var junction_default = junctionNoun;
13105
13721
 
13106
13722
  // src/cli/commands/events.ts
13107
- import fs17 from "fs";
13108
- import path29 from "path";
13723
+ import fs20 from "fs";
13724
+ import path32 from "path";
13109
13725
  import ts2 from "typescript";
13110
- import { Command as Command9, Option as Option9 } from "clipanion";
13726
+ import { Command as Command11, Option as Option11 } from "clipanion";
13111
13727
  function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
13112
13728
  const tier2 = [];
13113
13729
  const tier1 = [];
@@ -13204,7 +13820,7 @@ function scanDirectoryForConsumers(rootDir, eventType) {
13204
13820
  const tier1 = [];
13205
13821
  let hasEventFlowImport = false;
13206
13822
  for (const filePath of files) {
13207
- const text2 = fs17.readFileSync(filePath, "utf8");
13823
+ const text2 = fs20.readFileSync(filePath, "utf8");
13208
13824
  const sourceFile = ts2.createSourceFile(
13209
13825
  filePath,
13210
13826
  text2,
@@ -13241,7 +13857,7 @@ function suggestEventTypes(target, known, limit = 3) {
13241
13857
  return known.map((t) => ({ t, d: levenshtein(target, t) })).sort((a, b) => a.d - b.d).slice(0, limit).map((x) => x.t);
13242
13858
  }
13243
13859
  function renderConsumerReport(result, cwd) {
13244
- const rel = (p) => path29.relative(cwd, p) || p;
13860
+ const rel2 = (p) => path32.relative(cwd, p) || p;
13245
13861
  const lines = [];
13246
13862
  const total = result.tier3.length + result.tier2.length + result.tier1.length;
13247
13863
  lines.push(`Event: ${result.eventType}`);
@@ -13255,7 +13871,7 @@ function renderConsumerReport(result, cwd) {
13255
13871
  const labelWidth = Math.max(...result.tier3.map((h) => h.triggerId.length)) + 2;
13256
13872
  for (const h of result.tier3) {
13257
13873
  const padded = h.triggerId.padEnd(labelWidth);
13258
- lines.push(` - ${padded}(${rel(h.sourceFile)}:${h.sourceLine})`);
13874
+ lines.push(` - ${padded}(${rel2(h.sourceFile)}:${h.sourceLine})`);
13259
13875
  }
13260
13876
  }
13261
13877
  lines.push(`Tier 2 \u2014 Direct invoke via publishAndStart (${result.tier2.length}):`);
@@ -13263,7 +13879,7 @@ function renderConsumerReport(result, cwd) {
13263
13879
  lines.push(" - (none)");
13264
13880
  } else {
13265
13881
  for (const h of result.tier2) {
13266
- lines.push(` - ${rel(h.sourceFile)}:${h.sourceLine}`);
13882
+ lines.push(` - ${rel2(h.sourceFile)}:${h.sourceLine}`);
13267
13883
  }
13268
13884
  }
13269
13885
  lines.push(`Tier 1 \u2014 Subscribers (${result.tier1.length}):`);
@@ -13271,13 +13887,13 @@ function renderConsumerReport(result, cwd) {
13271
13887
  lines.push(" - (none)");
13272
13888
  } else {
13273
13889
  for (const h of result.tier1) {
13274
- lines.push(` - ${h.siteLabel} at ${rel(h.sourceFile)}:${h.sourceLine}`);
13890
+ lines.push(` - ${h.siteLabel} at ${rel2(h.sourceFile)}:${h.sourceLine}`);
13275
13891
  }
13276
13892
  }
13277
13893
  return lines;
13278
13894
  }
13279
13895
  function runConsumersScan(opts) {
13280
- const scanRoot = opts.scanRoot ?? path29.join(opts.cwd, "src");
13896
+ const scanRoot = opts.scanRoot ?? path32.join(opts.cwd, "src");
13281
13897
  const handlersDir = opts.handlersDir ?? scanRoot;
13282
13898
  const allTriggers = scanHandlerFiles(handlersDir);
13283
13899
  const tier3 = allTriggers.filter((t) => t.event === opts.eventType).map((t) => ({
@@ -13286,8 +13902,8 @@ function runConsumersScan(opts) {
13286
13902
  sourceFile: t.sourceFile,
13287
13903
  sourceLine: t.sourceLine
13288
13904
  }));
13289
- const tier21 = fs17.existsSync(scanRoot) ? scanDirectoryForConsumers(scanRoot, opts.eventType) : { tier2: [], tier1: [], hasEventFlowImport: false };
13290
- const eventsGeneratedDir = opts.eventsGeneratedDir ?? path29.join(
13905
+ const tier21 = fs20.existsSync(scanRoot) ? scanDirectoryForConsumers(scanRoot, opts.eventType) : { tier2: [], tier1: [], hasEventFlowImport: false };
13906
+ const eventsGeneratedDir = opts.eventsGeneratedDir ?? path32.join(
13291
13907
  resolveSubsystemsRootFromContext(opts.cwd, opts.config),
13292
13908
  "events",
13293
13909
  "generated"
@@ -13308,15 +13924,15 @@ function runConsumersScan(opts) {
13308
13924
  function resolveSubsystemsRootFromContext(cwd, config) {
13309
13925
  const configured = config?.paths?.subsystems;
13310
13926
  if (typeof configured === "string" && configured.length > 0) {
13311
- return path29.resolve(cwd, configured);
13927
+ return path32.resolve(cwd, configured);
13312
13928
  }
13313
13929
  const backendSrc = config?.paths?.backend_src;
13314
13930
  const base = typeof backendSrc === "string" && backendSrc.length > 0 ? backendSrc : "src";
13315
- return path29.resolve(cwd, base, "shared", "subsystems");
13931
+ return path32.resolve(cwd, base, "shared", "subsystems");
13316
13932
  }
13317
- var EventsConsumersCommand = class extends Command9 {
13933
+ var EventsConsumersCommand = class extends Command11 {
13318
13934
  static paths = [["events", "consumers"]];
13319
- static usage = Command9.Usage({
13935
+ static usage = Command11.Usage({
13320
13936
  description: "List all consumers of an event across the three tiers",
13321
13937
  examples: [
13322
13938
  [
@@ -13325,10 +13941,10 @@ var EventsConsumersCommand = class extends Command9 {
13325
13941
  ]
13326
13942
  ]
13327
13943
  });
13328
- eventType = Option9.String({ required: true });
13329
- json = Option9.Boolean("--json", false);
13330
- cwd = Option9.String("--cwd", { required: false });
13331
- configPath = Option9.String("--config", { required: false });
13944
+ eventType = Option11.String({ required: true });
13945
+ json = Option11.Boolean("--json", false);
13946
+ cwd = Option11.String("--cwd", { required: false });
13947
+ configPath = Option11.String("--config", { required: false });
13332
13948
  async execute() {
13333
13949
  if (this.json) setJsonMode(true);
13334
13950
  const ctx = await loadContext({
@@ -13373,7 +13989,7 @@ var EventsConsumersCommand = class extends Command9 {
13373
13989
  return 0;
13374
13990
  }
13375
13991
  };
13376
- async function summary7(_ctx) {
13992
+ async function summary8(_ctx) {
13377
13993
  return {
13378
13994
  title: "events",
13379
13995
  body: [
@@ -13384,7 +14000,7 @@ async function summary7(_ctx) {
13384
14000
  ]
13385
14001
  };
13386
14002
  }
13387
- async function hints7(_ctx) {
14003
+ async function hints8(_ctx) {
13388
14004
  return [
13389
14005
  {
13390
14006
  command: "codegen events consumers <type>",
@@ -13395,14 +14011,14 @@ async function hints7(_ctx) {
13395
14011
  var eventsNoun = {
13396
14012
  name: "events",
13397
14013
  commandClasses: [EventsConsumersCommand],
13398
- summary: summary7,
13399
- hints: hints7
14014
+ summary: summary8,
14015
+ hints: hints8
13400
14016
  };
13401
14017
  var events_default = eventsNoun;
13402
14018
 
13403
14019
  // src/cli/commands/orchestration.ts
13404
- import path30 from "path";
13405
- import { Command as Command10, Option as Option10 } from "clipanion";
14020
+ import path33 from "path";
14021
+ import { Command as Command12, Option as Option12 } from "clipanion";
13406
14022
  var DEFAULT_PATTERN_GLOBS = ["src/patterns/*.pattern.ts"];
13407
14023
  function resolvePatternGlobs(ctx) {
13408
14024
  const fromConfig = ctx.config?.patterns;
@@ -13415,26 +14031,26 @@ function resolveOrchestrationOutputRoot(ctx) {
13415
14031
  const paths = ctx.config?.paths;
13416
14032
  const explicit = paths?.orchestration_src;
13417
14033
  if (typeof explicit === "string" && explicit.length > 0) {
13418
- return path30.resolve(ctx.cwd, explicit);
14034
+ return path33.resolve(ctx.cwd, explicit);
13419
14035
  }
13420
14036
  const backendSrc = typeof paths?.backend_src === "string" && paths.backend_src.length > 0 ? paths.backend_src : "app/backend/src";
13421
- return path30.resolve(ctx.cwd, backendSrc, "orchestration");
14037
+ return path33.resolve(ctx.cwd, backendSrc, "orchestration");
13422
14038
  }
13423
14039
  async function reloadRegistry(ctx) {
13424
14040
  _resetRegistryForTests({ includeLibrary: false });
13425
14041
  return loadAppPatterns(resolvePatternGlobs(ctx), ctx.cwd);
13426
14042
  }
13427
- var OrchestrationGenCommand = class extends Command10 {
14043
+ var OrchestrationGenCommand = class extends Command12 {
13428
14044
  static paths = [["orchestration", "gen"]];
13429
- static usage = Command10.Usage({
14045
+ static usage = Command12.Usage({
13430
14046
  description: "Emit token / providers / dispatcher / module files per orchestration pattern (ADR-032 Phase 3-2/3)."
13431
14047
  });
13432
- pattern = Option10.String("--pattern", { required: false });
13433
- all = Option10.Boolean("--all", false);
13434
- dryRun = Option10.Boolean("--dry-run", false);
13435
- json = Option10.Boolean("--json", false);
13436
- cwd = Option10.String("--cwd", { required: false });
13437
- configPath = Option10.String("--config", { required: false });
14048
+ pattern = Option12.String("--pattern", { required: false });
14049
+ all = Option12.Boolean("--all", false);
14050
+ dryRun = Option12.Boolean("--dry-run", false);
14051
+ json = Option12.Boolean("--json", false);
14052
+ cwd = Option12.String("--cwd", { required: false });
14053
+ configPath = Option12.String("--config", { required: false });
13438
14054
  async execute() {
13439
14055
  if (this.json) setJsonMode(true);
13440
14056
  const ctx = await loadContext({
@@ -13507,12 +14123,12 @@ var OrchestrationGenCommand = class extends Command10 {
13507
14123
  );
13508
14124
  for (const f of result.files) {
13509
14125
  console.log(
13510
- ` ${theme.muted(icons.arrow)} ${path30.relative(ctx.cwd, f.outputPath)}`
14126
+ ` ${theme.muted(icons.arrow)} ${path33.relative(ctx.cwd, f.outputPath)}`
13511
14127
  );
13512
14128
  }
13513
14129
  } else {
13514
14130
  printSuccess(
13515
- `Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${path30.relative(ctx.cwd, outputRoot)}`
14131
+ `Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${path33.relative(ctx.cwd, outputRoot)}`
13516
14132
  );
13517
14133
  }
13518
14134
  return 0;
@@ -13525,14 +14141,14 @@ var OrchestrationGenCommand = class extends Command10 {
13525
14141
  }
13526
14142
  }
13527
14143
  };
13528
- var OrchestrationListCommand = class extends Command10 {
14144
+ var OrchestrationListCommand = class extends Command12 {
13529
14145
  static paths = [["orchestration", "list"]];
13530
- static usage = Command10.Usage({
14146
+ static usage = Command12.Usage({
13531
14147
  description: "List registered orchestration patterns"
13532
14148
  });
13533
- json = Option10.Boolean("--json", false);
13534
- cwd = Option10.String("--cwd", { required: false });
13535
- configPath = Option10.String("--config", { required: false });
14149
+ json = Option12.Boolean("--json", false);
14150
+ cwd = Option12.String("--cwd", { required: false });
14151
+ configPath = Option12.String("--config", { required: false });
13536
14152
  async execute() {
13537
14153
  if (this.json) setJsonMode(true);
13538
14154
  const ctx = await loadContext({
@@ -13576,14 +14192,14 @@ var OrchestrationListCommand = class extends Command10 {
13576
14192
  return 0;
13577
14193
  }
13578
14194
  };
13579
- var OrchestrationValidateCommand = class extends Command10 {
14195
+ var OrchestrationValidateCommand = class extends Command12 {
13580
14196
  static paths = [["orchestration", "validate"]];
13581
- static usage = Command10.Usage({
14197
+ static usage = Command12.Usage({
13582
14198
  description: "Run the Phase 3-1 project-level orchestration validator (ADR-032)"
13583
14199
  });
13584
- json = Option10.Boolean("--json", false);
13585
- cwd = Option10.String("--cwd", { required: false });
13586
- configPath = Option10.String("--config", { required: false });
14200
+ json = Option12.Boolean("--json", false);
14201
+ cwd = Option12.String("--cwd", { required: false });
14202
+ configPath = Option12.String("--config", { required: false });
13587
14203
  async execute() {
13588
14204
  if (this.json) setJsonMode(true);
13589
14205
  const ctx = await loadContext({
@@ -13619,7 +14235,7 @@ var OrchestrationValidateCommand = class extends Command10 {
13619
14235
  return errors.length === 0 && loadResult.errors.length === 0 ? 0 : 1;
13620
14236
  }
13621
14237
  };
13622
- async function summary8(ctx) {
14238
+ async function summary9(ctx) {
13623
14239
  try {
13624
14240
  await reloadRegistry(ctx);
13625
14241
  } catch {
@@ -13633,7 +14249,7 @@ async function summary8(ctx) {
13633
14249
  ]
13634
14250
  };
13635
14251
  }
13636
- async function hints8(_ctx) {
14252
+ async function hints9(_ctx) {
13637
14253
  return [
13638
14254
  {
13639
14255
  command: "codegen orchestration gen",
@@ -13656,8 +14272,8 @@ var orchestrationNoun = {
13656
14272
  OrchestrationListCommand,
13657
14273
  OrchestrationValidateCommand
13658
14274
  ],
13659
- summary: summary8,
13660
- hints: hints8
14275
+ summary: summary9,
14276
+ hints: hints9
13661
14277
  };
13662
14278
  var orchestration_default = orchestrationNoun;
13663
14279
 
@@ -13668,19 +14284,26 @@ var InitShortcut = class extends ProjectInitCommand {
13668
14284
  };
13669
14285
  var init_default = InitShortcut;
13670
14286
 
14287
+ // src/cli/shortcuts/update.ts
14288
+ var UpdateShortcut = class extends ProjectUpdateCommand {
14289
+ static paths = [["update"]];
14290
+ static usage = ProjectUpdateCommand.usage;
14291
+ };
14292
+ var update_default = UpdateShortcut;
14293
+
13671
14294
  // src/cli/index.ts
13672
14295
  function readVersion() {
13673
14296
  try {
13674
- const pkgPath = join11(import.meta.dirname, "..", "..", "package.json");
14297
+ const pkgPath = join10(import.meta.dirname, "..", "..", "package.json");
13675
14298
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
13676
14299
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
13677
14300
  } catch {
13678
14301
  return "0.0.0";
13679
14302
  }
13680
14303
  }
13681
- var RootSummaryCommand = class extends Command11 {
13682
- static paths = [Command11.Default];
13683
- static usage = Command11.Usage({
14304
+ var RootSummaryCommand = class extends Command13 {
14305
+ static paths = [Command13.Default];
14306
+ static usage = Command13.Usage({
13684
14307
  description: "Show project status and available noun commands"
13685
14308
  });
13686
14309
  async execute() {
@@ -13722,6 +14345,7 @@ var nouns = [
13722
14345
  entity_default,
13723
14346
  subsystem_default,
13724
14347
  project_default,
14348
+ skills_default,
13725
14349
  dev_default,
13726
14350
  relationship_default,
13727
14351
  junction_default,
@@ -13740,6 +14364,7 @@ async function main() {
13740
14364
  cli.register(Builtins.VersionCommand);
13741
14365
  cli.register(RootSummaryCommand);
13742
14366
  cli.register(init_default);
14367
+ cli.register(update_default);
13743
14368
  for (const noun of nouns) {
13744
14369
  for (const CommandClass of noun.commandClasses) {
13745
14370
  cli.register(CommandClass);