@pattern-stack/codegen 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +5 -0
  2. package/consumer-skills/bridge/SKILL.md +265 -0
  3. package/consumer-skills/codegen/SKILL.md +115 -0
  4. package/consumer-skills/entities/SKILL.md +111 -0
  5. package/consumer-skills/entities/families-and-queries.md +82 -0
  6. package/consumer-skills/entities/yaml-reference.md +118 -0
  7. package/consumer-skills/events/SKILL.md +71 -0
  8. package/consumer-skills/events/authoring-events.md +164 -0
  9. package/consumer-skills/events/typed-bus-and-outbox.md +163 -0
  10. package/consumer-skills/jobs/SKILL.md +66 -0
  11. package/consumer-skills/jobs/handler-authoring.md +236 -0
  12. package/consumer-skills/jobs/pools-and-ordering.md +161 -0
  13. package/consumer-skills/subsystems/SKILL.md +105 -0
  14. package/consumer-skills/subsystems/wiring-and-order.md +120 -0
  15. package/consumer-skills/sync/SKILL.md +134 -0
  16. package/consumer-skills/sync/audit-and-detection.md +302 -0
  17. package/consumer-skills/sync/change-sources-and-sinks.md +442 -0
  18. package/dist/runtime/subsystems/bridge/bridge.module.js +3 -0
  19. package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
  20. package/dist/runtime/subsystems/bridge/index.js +3 -0
  21. package/dist/runtime/subsystems/bridge/index.js.map +1 -1
  22. package/dist/runtime/subsystems/index.js +3 -0
  23. package/dist/runtime/subsystems/index.js.map +1 -1
  24. package/dist/runtime/subsystems/jobs/index.js +3 -0
  25. package/dist/runtime/subsystems/jobs/index.js.map +1 -1
  26. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js +3 -0
  27. package/dist/runtime/subsystems/jobs/job-run-keyset-cursor.js.map +1 -1
  28. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +3 -0
  29. package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js.map +1 -1
  30. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +3 -0
  31. package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js.map +1 -1
  32. package/dist/runtime/subsystems/jobs/job-run-service.protocol.d.ts +9 -1
  33. package/dist/runtime/subsystems/jobs/job-worker.module.js +3 -0
  34. package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
  35. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +3 -0
  36. package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
  37. package/dist/src/cli/index.js +913 -405
  38. package/dist/src/cli/index.js.map +1 -1
  39. package/dist/src/index.js +26 -4
  40. package/dist/src/index.js.map +1 -1
  41. package/package.json +2 -1
  42. package/runtime/subsystems/jobs/job-run-keyset-cursor.ts +3 -0
  43. package/runtime/subsystems/jobs/job-run-service.protocol.ts +9 -1
@@ -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);
@@ -7042,8 +7068,7 @@ import fs8 from "fs";
7042
7068
  import path11 from "path";
7043
7069
 
7044
7070
  // src/parser/load-events.ts
7045
- import { readdirSync as readdirSync7 } from "fs";
7046
- import { basename as basename2, join as join10, resolve as resolve2 } from "path";
7071
+ import { basename as basename2, resolve as resolve3 } from "path";
7047
7072
  function loadErrorToIssue2(error) {
7048
7073
  const issues = [];
7049
7074
  issues.push({
@@ -7073,10 +7098,10 @@ function stripYamlExt(file) {
7073
7098
  function loadEvents(eventsDir, entityNames) {
7074
7099
  const events = [];
7075
7100
  const issues = [];
7076
- const resolvedDir = resolve2(eventsDir);
7101
+ const resolvedDir = resolve3(eventsDir);
7077
7102
  let files;
7078
7103
  try {
7079
- files = readdirSync7(resolvedDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).sort().map((f) => join10(resolvedDir, f));
7104
+ files = findYamlFiles(resolvedDir);
7080
7105
  } catch {
7081
7106
  issues.push({
7082
7107
  severity: "warning",
@@ -7227,7 +7252,7 @@ function collectEntityEvents(entitiesDir) {
7227
7252
  if (!fs8.existsSync(entitiesDir)) {
7228
7253
  return { events, issues };
7229
7254
  }
7230
- const files = fs8.readdirSync(entitiesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path11.join(entitiesDir, f)).sort();
7255
+ const files = findYamlFiles(entitiesDir);
7231
7256
  for (const filePath of files) {
7232
7257
  const result = loadEntityFromYaml(filePath);
7233
7258
  if (!result.success) continue;
@@ -7269,7 +7294,7 @@ function collectMergedEvents(opts) {
7269
7294
  const { entitiesDir, eventsDir } = opts;
7270
7295
  const entityNames = [];
7271
7296
  if (fs8.existsSync(entitiesDir)) {
7272
- const entityFiles = fs8.readdirSync(entitiesDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path11.join(entitiesDir, f)).sort();
7297
+ const entityFiles = findYamlFiles(entitiesDir);
7273
7298
  for (const f of entityFiles) {
7274
7299
  const result = loadEntityFromYaml(f);
7275
7300
  if (result.success) entityNames.push(result.definition.entity.name);
@@ -7752,7 +7777,7 @@ function printInfo(msg) {
7752
7777
  // src/cli/commands/entity.ts
7753
7778
  function listEntityYamls2(dir) {
7754
7779
  if (!fs9.existsSync(dir)) return [];
7755
- return fs9.readdirSync(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).map((f) => path13.join(dir, f));
7780
+ return findYamlFiles(dir);
7756
7781
  }
7757
7782
  function summarizePatternLabel(entity) {
7758
7783
  if (typeof entity.pattern === "string" && entity.pattern.length > 0) {
@@ -8788,13 +8813,6 @@ function readIfExists(p) {
8788
8813
  return null;
8789
8814
  }
8790
8815
  }
8791
- function writeFile(target, content) {
8792
- const existing = readIfExists(target);
8793
- if (existing === content) return "unchanged";
8794
- fs10.mkdirSync(path21.dirname(target), { recursive: true });
8795
- fs10.writeFileSync(target, content);
8796
- return existing === null ? "written" : "updated";
8797
- }
8798
8816
  function extractRelativeImports(source) {
8799
8817
  const out = [];
8800
8818
  const re = /(?:import|export)\s+(?:[^'"`;]*?\s+from\s+)?['"`](\.{1,2}\/[^'"`]+)['"`]/g;
@@ -8813,7 +8831,7 @@ function resolveSourceImport(sourceFile, specifier) {
8813
8831
  return null;
8814
8832
  }
8815
8833
  async function copyRuntime(opts) {
8816
- const { sourceDir, targetDir, filter, resolveDeps, dryRun } = opts;
8834
+ const { sourceDir, targetDir, filter, resolveDeps, dryRun, onlyExisting } = opts;
8817
8835
  if (!fs10.existsSync(sourceDir) || !fs10.statSync(sourceDir).isDirectory()) {
8818
8836
  throw new Error(`runtime source directory not found: ${sourceDir}`);
8819
8837
  }
@@ -8838,9 +8856,9 @@ async function copyRuntime(opts) {
8838
8856
  }
8839
8857
  if (!stat.isFile()) continue;
8840
8858
  if (!entry.endsWith(".ts") && !entry.endsWith(".tsx")) continue;
8841
- const rel = path21.relative(sourceDir, src);
8842
- if (filter && !filter(rel) && !filter(entry)) continue;
8843
- queue.push({ src, dest: path21.join(targetDir, rel), isDep: false });
8859
+ const rel2 = path21.relative(sourceDir, src);
8860
+ if (filter && !filter(rel2) && !filter(entry)) continue;
8861
+ queue.push({ src, dest: path21.join(targetDir, rel2), isDep: false });
8844
8862
  }
8845
8863
  }
8846
8864
  walk(sourceDir);
@@ -8849,14 +8867,20 @@ async function copyRuntime(opts) {
8849
8867
  const next = queue.shift();
8850
8868
  if (visited.has(next.src)) continue;
8851
8869
  visited.add(next.src);
8870
+ if (onlyExisting && !fs10.existsSync(next.dest)) {
8871
+ continue;
8872
+ }
8852
8873
  const content = fs10.readFileSync(next.src, "utf-8");
8853
8874
  result.planned.push(next.dest);
8854
- 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);
8875
+ const existing = readIfExists(next.dest);
8876
+ const status = existing === content ? "unchanged" : existing === null ? "written" : "updated";
8877
+ if (status === "written") result.written.push(next.dest);
8878
+ else if (status === "updated") result.updated.push(next.dest);
8879
+ else result.unchanged.push(next.dest);
8880
+ if (next.isDep) result.dependenciesCopied.push(next.dest);
8881
+ if (!dryRun && status !== "unchanged") {
8882
+ fs10.mkdirSync(path21.dirname(next.dest), { recursive: true });
8883
+ fs10.writeFileSync(next.dest, content);
8860
8884
  }
8861
8885
  if (resolveDeps) {
8862
8886
  for (const spec of extractRelativeImports(content)) {
@@ -8909,11 +8933,11 @@ async function summary2(ctx) {
8909
8933
  }
8910
8934
  body.push(theme.muted("Installed:"));
8911
8935
  for (const i of installed) {
8912
- const rel = path22.relative(ctx.cwd, i.path) || i.path;
8936
+ const rel2 = path22.relative(ctx.cwd, i.path) || i.path;
8913
8937
  body.push(
8914
8938
  ` ${theme.success(icons.check)} ${i.name.padEnd(10)} ${theme.muted(
8915
8939
  `${i.backend} backend`
8916
- )} ${theme.muted(rel)}`
8940
+ )} ${theme.muted(rel2)}`
8917
8941
  );
8918
8942
  }
8919
8943
  if (missing.length > 0) {
@@ -9916,9 +9940,9 @@ function buildAuthImportRewriter(subsystemsRoot) {
9916
9940
  return content;
9917
9941
  }
9918
9942
  AUTH_BARE_IMPORT_RE.lastIndex = 0;
9919
- let rel = path22.relative(path22.dirname(destPath), authRoot);
9920
- if (!rel.startsWith(".")) rel = `./${rel}`;
9921
- const relPosix = rel.split(path22.sep).join("/");
9943
+ let rel2 = path22.relative(path22.dirname(destPath), authRoot);
9944
+ if (!rel2.startsWith(".")) rel2 = `./${rel2}`;
9945
+ const relPosix = rel2.split(path22.sep).join("/");
9922
9946
  return content.replace(
9923
9947
  AUTH_BARE_IMPORT_RE,
9924
9948
  (_match, quote) => `${quote}${relPosix}${quote}`
@@ -10100,10 +10124,10 @@ var subsystemNoun = {
10100
10124
  var subsystem_default = subsystemNoun;
10101
10125
 
10102
10126
  // src/cli/commands/project.ts
10103
- import fs14 from "fs";
10104
- import path25 from "path";
10127
+ import fs17 from "fs";
10128
+ import path28 from "path";
10105
10129
  import readline from "readline";
10106
- import { Command as Command5, Option as Option5 } from "clipanion";
10130
+ import { Command as Command7, Option as Option7 } from "clipanion";
10107
10131
  import { stringify as stringifyYaml2 } from "yaml";
10108
10132
 
10109
10133
  // src/cli/shared/init-scaffold.ts
@@ -10746,8 +10770,8 @@ async function buildInitPlan(ctx, options) {
10746
10770
  {
10747
10771
  const entitiesDir = path23.join(cwd, "entities");
10748
10772
  const examplePath = path23.join(entitiesDir, "example.yaml");
10749
- const hasOtherYamls = fs12.existsSync(entitiesDir) && fs12.readdirSync(entitiesDir).some(
10750
- (f) => (f.endsWith(".yaml") || f.endsWith(".yml")) && f !== "example.yaml"
10773
+ const hasOtherYamls = fs12.existsSync(entitiesDir) && findYamlFiles(entitiesDir).some(
10774
+ (f) => path23.basename(f) !== "example.yaml"
10751
10775
  );
10752
10776
  if (fs12.existsSync(examplePath)) {
10753
10777
  entries.push({
@@ -11058,8 +11082,8 @@ function runtimeRoot3() {
11058
11082
  if (fs13.existsSync(topLevel)) return topLevel;
11059
11083
  return path24.join(pkgRoot, "dist", "runtime");
11060
11084
  }
11061
- function loadRuntimeFile2(rel) {
11062
- return fs13.readFileSync(path24.join(runtimeRoot3(), rel), "utf-8");
11085
+ function loadRuntimeFile2(rel2) {
11086
+ return fs13.readFileSync(path24.join(runtimeRoot3(), rel2), "utf-8");
11063
11087
  }
11064
11088
  function resolveProjectRoot(startDir) {
11065
11089
  let dir = path24.resolve(startDir);
@@ -11342,8 +11366,457 @@ ${CONSUMER_SETUP_POINTER}
11342
11366
  }
11343
11367
  };
11344
11368
 
11345
- // src/cli/commands/project.ts
11369
+ // src/cli/commands/project-update.ts
11370
+ import fs16 from "fs";
11371
+ import path27 from "path";
11372
+ import { Command as Command6, Option as Option6 } from "clipanion";
11373
+
11374
+ // src/cli/commands/skills.ts
11375
+ import fs15 from "fs";
11376
+ import path26 from "path";
11377
+ import { Command as Command5, Option as Option5 } from "clipanion";
11378
+
11379
+ // src/cli/shared/tree-copier.ts
11380
+ import fs14 from "fs";
11381
+ import path25 from "path";
11382
+ var TEXT_EXTENSIONS = [".ts", ".tsx", ".md", ".mdx", ".yaml", ".yml", ".json"];
11383
+ function isTextFile(name) {
11384
+ return TEXT_EXTENSIONS.some((ext) => name.endsWith(ext));
11385
+ }
11386
+ function copyTreeWithReport(opts) {
11387
+ const { srcDir, destDir, dryRun = false, transform, include } = opts;
11388
+ const report = {
11389
+ entries: [],
11390
+ created: [],
11391
+ updated: [],
11392
+ unchanged: []
11393
+ };
11394
+ if (!fs14.existsSync(srcDir) || !fs14.statSync(srcDir).isDirectory()) {
11395
+ throw new Error(`tree-copier source directory not found: ${srcDir}`);
11396
+ }
11397
+ const walk = (relDir) => {
11398
+ const absSrcDir = path25.join(srcDir, relDir);
11399
+ for (const entry of fs14.readdirSync(absSrcDir, { withFileTypes: true })) {
11400
+ const relPath2 = relDir ? path25.posix.join(relDir, entry.name) : entry.name;
11401
+ const absSrc = path25.join(srcDir, relPath2);
11402
+ if (entry.isDirectory()) {
11403
+ walk(relPath2);
11404
+ continue;
11405
+ }
11406
+ if (!entry.isFile()) continue;
11407
+ if (include && !include(relPath2)) continue;
11408
+ const dest = path25.join(destDir, relPath2);
11409
+ let content = fs14.readFileSync(absSrc, "utf-8");
11410
+ if (transform && isTextFile(entry.name)) {
11411
+ content = transform(content, dest);
11412
+ }
11413
+ const existing = fs14.existsSync(dest) ? fs14.readFileSync(dest, "utf-8") : null;
11414
+ let action;
11415
+ if (existing === null) {
11416
+ action = "created";
11417
+ } else if (existing === content) {
11418
+ action = "unchanged";
11419
+ } else {
11420
+ action = "updated";
11421
+ }
11422
+ if (!dryRun && action !== "unchanged") {
11423
+ fs14.mkdirSync(path25.dirname(dest), { recursive: true });
11424
+ fs14.writeFileSync(dest, content, "utf-8");
11425
+ }
11426
+ const record = { relPath: relPath2, dest, action };
11427
+ report.entries.push(record);
11428
+ report[action].push(record);
11429
+ }
11430
+ };
11431
+ walk("");
11432
+ return report;
11433
+ }
11434
+
11435
+ // src/cli/commands/skills.ts
11436
+ function consumerSkillsRoot() {
11437
+ const pkgRoot = path26.resolve(import.meta.dirname, "..", "..", "..");
11438
+ const topLevel = path26.join(pkgRoot, "consumer-skills");
11439
+ if (fs15.existsSync(topLevel)) return topLevel;
11440
+ return path26.join(pkgRoot, "dist", "consumer-skills");
11441
+ }
11442
+ function skillsTargetDir(cwd) {
11443
+ return path26.join(cwd, ".claude", "skills");
11444
+ }
11445
+ function availableSkills() {
11446
+ const root = consumerSkillsRoot();
11447
+ if (!fs15.existsSync(root)) return [];
11448
+ return fs15.readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
11449
+ }
11450
+ function runSkillsInstall(opts) {
11451
+ const sourceRoot = consumerSkillsRoot();
11452
+ const targetDir = skillsTargetDir(opts.cwd);
11453
+ if (!fs15.existsSync(sourceRoot)) {
11454
+ return {
11455
+ ok: false,
11456
+ sourceRoot,
11457
+ targetDir,
11458
+ error: `consumer skills source missing: ${sourceRoot}`
11459
+ };
11460
+ }
11461
+ const report = copyTreeWithReport({
11462
+ srcDir: sourceRoot,
11463
+ destDir: targetDir,
11464
+ dryRun: Boolean(opts.dryRun)
11465
+ });
11466
+ return { ok: true, sourceRoot, targetDir, report };
11467
+ }
11346
11468
  async function summary3(ctx) {
11469
+ const skills = availableSkills();
11470
+ const targetDir = skillsTargetDir(ctx.cwd);
11471
+ const installedDirs = fs15.existsSync(targetDir) ? new Set(
11472
+ fs15.readdirSync(targetDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name)
11473
+ ) : /* @__PURE__ */ new Set();
11474
+ const body = [];
11475
+ if (skills.length === 0) {
11476
+ body.push(theme.muted("No consumer skills bundled with this package build."));
11477
+ return { title: "skills", body, footer: "" };
11478
+ }
11479
+ body.push(theme.muted("Consumer skills:"));
11480
+ for (const name of skills) {
11481
+ const present = installedDirs.has(name);
11482
+ const icon = present ? theme.success(icons.check) : theme.muted(icons.dash);
11483
+ const status = present ? "" : theme.muted("not installed");
11484
+ body.push(` ${icon} ${name.padEnd(12)} ${status}`);
11485
+ }
11486
+ const installedCount = skills.filter((s) => installedDirs.has(s)).length;
11487
+ return {
11488
+ title: "skills",
11489
+ body,
11490
+ footer: `${installedCount} of ${skills.length} skills installed \u2192 ${path26.relative(ctx.cwd, targetDir) || targetDir}`
11491
+ };
11492
+ }
11493
+ async function hints3(ctx) {
11494
+ const skills = availableSkills();
11495
+ const targetDir = skillsTargetDir(ctx.cwd);
11496
+ const allPresent = skills.length > 0 && fs15.existsSync(targetDir) && skills.every((s) => fs15.existsSync(path26.join(targetDir, s)));
11497
+ if (allPresent) {
11498
+ return [
11499
+ { command: "codegen update", description: "Re-sync skills + runtime after a package bump" }
11500
+ ];
11501
+ }
11502
+ return [
11503
+ { command: "codegen skills install", description: "Vendor consumer skills into .claude/skills" }
11504
+ ];
11505
+ }
11506
+ function renderTreeReport(report) {
11507
+ const show = (entry) => {
11508
+ const icon = theme.success(icons.check);
11509
+ console.log(` ${icon} ${theme.muted(entry.action.padEnd(10))} ${entry.relPath}`);
11510
+ };
11511
+ for (const e of [...report.created, ...report.updated]) show(e);
11512
+ if (report.unchanged.length > 0) {
11513
+ console.log(
11514
+ ` ${theme.muted(icons.dash)} ${theme.muted("unchanged".padEnd(10))} ${theme.muted(
11515
+ `${report.unchanged.length} file${report.unchanged.length === 1 ? "" : "s"} already current`
11516
+ )}`
11517
+ );
11518
+ }
11519
+ }
11520
+ var SkillsInstallCommand = class extends Command5 {
11521
+ static paths = [["skills", "install"]];
11522
+ static usage = Command5.Usage({
11523
+ description: "Vendor consumer-facing skills into the project .claude/skills",
11524
+ examples: [
11525
+ ["Install all consumer skills", "codegen skills install"],
11526
+ ["Preview without writing", "codegen skills install --dry-run"],
11527
+ ["Overwrite locally-edited skill files", "codegen skills install --force"]
11528
+ ]
11529
+ });
11530
+ force = Option5.Boolean("--force", false);
11531
+ dryRun = Option5.Boolean("--dry-run", false);
11532
+ json = Option5.Boolean("--json", false);
11533
+ cwd = Option5.String("--cwd", { required: false });
11534
+ async execute() {
11535
+ if (this.json) setJsonMode(true);
11536
+ const ctx = await loadContext({ cwd: this.cwd, json: this.json, skipDetection: true });
11537
+ const preview = runSkillsInstall({ cwd: ctx.cwd, dryRun: true });
11538
+ if (!preview.ok) {
11539
+ if (isJsonMode()) {
11540
+ printJson({ command: "skills install", status: "error", error: preview.error });
11541
+ } else {
11542
+ printError(preview.error ?? "skills install failed");
11543
+ }
11544
+ return 1;
11545
+ }
11546
+ if (!this.dryRun && !this.force) {
11547
+ const updatedPaths = preview.report.updated.map((e) => e.dest);
11548
+ const gate = checkGitSafety(updatedPaths, ctx.cwd);
11549
+ if (gate.inRepo && !gate.clean) {
11550
+ printWarning(
11551
+ `Uncommitted changes in ${gate.dirty.length} skill file(s). Commit them or pass --force to overwrite.`
11552
+ );
11553
+ if (!isJsonMode()) return 1;
11554
+ }
11555
+ }
11556
+ const result = runSkillsInstall({ cwd: ctx.cwd, dryRun: this.dryRun });
11557
+ const report = result.report;
11558
+ if (isJsonMode()) {
11559
+ printJson({
11560
+ command: "skills install",
11561
+ dryRun: this.dryRun,
11562
+ target: result.targetDir,
11563
+ files: {
11564
+ created: report.created.map((e) => e.relPath),
11565
+ updated: report.updated.map((e) => e.relPath),
11566
+ unchanged: report.unchanged.map((e) => e.relPath)
11567
+ }
11568
+ });
11569
+ return 0;
11570
+ }
11571
+ printInfo(`target = ${path26.relative(ctx.cwd, result.targetDir) || result.targetDir}`);
11572
+ console.log("");
11573
+ renderTreeReport(report);
11574
+ console.log("");
11575
+ if (this.dryRun) {
11576
+ printWarning("dry-run \u2014 no files written");
11577
+ return 0;
11578
+ }
11579
+ printSuccess(
11580
+ `skills installed (${report.created.length} new, ${report.updated.length} updated, ${report.unchanged.length} unchanged)`
11581
+ );
11582
+ printInfo("Skills are auto-discovered by Claude Code from .claude/skills/.");
11583
+ return 0;
11584
+ }
11585
+ };
11586
+ var SkillsListCommand = class extends Command5 {
11587
+ static paths = [["skills", "list"]];
11588
+ static usage = Command5.Usage({
11589
+ description: "List available consumer skills and their installed status"
11590
+ });
11591
+ json = Option5.Boolean("--json", false);
11592
+ cwd = Option5.String("--cwd", { required: false });
11593
+ async execute() {
11594
+ if (this.json) setJsonMode(true);
11595
+ const ctx = await loadContext({ cwd: this.cwd, json: this.json, skipDetection: true });
11596
+ const skills = availableSkills();
11597
+ const targetDir = skillsTargetDir(ctx.cwd);
11598
+ const rows = skills.map((name) => {
11599
+ const dir = path26.join(targetDir, name);
11600
+ return { name, status: fs15.existsSync(dir) ? "installed" : "available" };
11601
+ });
11602
+ if (isJsonMode()) {
11603
+ printJson({ command: "skills list", target: targetDir, skills: rows });
11604
+ return 0;
11605
+ }
11606
+ if (rows.length === 0) {
11607
+ printWarning("No consumer skills bundled with this package build.");
11608
+ return 0;
11609
+ }
11610
+ const pad = (s, n) => s.length >= n ? s : s + " ".repeat(n - s.length);
11611
+ console.log(theme.muted(`${pad("NAME", 14)}STATUS`));
11612
+ for (const r of rows) console.log(`${pad(r.name, 14)}${r.status}`);
11613
+ return 0;
11614
+ }
11615
+ };
11616
+ var skillsNoun = {
11617
+ name: "skills",
11618
+ commandClasses: [SkillsInstallCommand, SkillsListCommand],
11619
+ summary: summary3,
11620
+ hints: hints3
11621
+ };
11622
+ var skills_default = skillsNoun;
11623
+
11624
+ // src/cli/commands/project-update.ts
11625
+ var NON_RUNTIME_SUBSYSTEMS = /* @__PURE__ */ new Set(["openapi-config", "auth-integrations"]);
11626
+ function syncVendoredRuntime(cwd, write) {
11627
+ const changes = [];
11628
+ for (const v of VENDORED_RUNTIME_FILES) {
11629
+ const dest = path27.join(cwd, v.target);
11630
+ const content = loadRuntimeFile(v.runtime);
11631
+ const existing = fs16.existsSync(dest) ? fs16.readFileSync(dest, "utf-8") : null;
11632
+ let action;
11633
+ if (existing === null) action = "created";
11634
+ else if (existing === content) action = "unchanged";
11635
+ else action = "updated";
11636
+ if (write && action !== "unchanged") {
11637
+ fs16.mkdirSync(path27.dirname(dest), { recursive: true });
11638
+ fs16.writeFileSync(dest, content, "utf-8");
11639
+ }
11640
+ changes.push({ path: v.target, action });
11641
+ }
11642
+ return changes;
11643
+ }
11644
+ async function syncSubsystemRuntime(cwd, inst, write) {
11645
+ if (NON_RUNTIME_SUBSYSTEMS.has(inst.name)) {
11646
+ return { name: inst.name, changes: [], skippedReason: "config-only / vendored elsewhere" };
11647
+ }
11648
+ const source = subsystemSource(inst.name);
11649
+ if (!fs16.existsSync(source)) {
11650
+ return { name: inst.name, changes: [], skippedReason: "no runtime source in package" };
11651
+ }
11652
+ const subsystemsRoot = path27.dirname(inst.path);
11653
+ const result = await copyRuntime({
11654
+ sourceDir: source,
11655
+ targetDir: inst.path,
11656
+ filter: backendFileFilter(inst.backend, inst.name),
11657
+ resolveDeps: true,
11658
+ runtimeRoot: runtimeRoot2(),
11659
+ depsTargetRoot: path27.resolve(subsystemsRoot, ".."),
11660
+ dryRun: !write,
11661
+ // Refresh files already vendored for this subsystem; never install new
11662
+ // ones (that's `subsystem install`). copyRuntime classifies accurately
11663
+ // in dry-run too, so this report is correct either way.
11664
+ onlyExisting: true
11665
+ });
11666
+ const changes = [];
11667
+ for (const p of result.written) changes.push({ path: rel(cwd, p), action: "created" });
11668
+ for (const p of result.updated) changes.push({ path: rel(cwd, p), action: "updated" });
11669
+ for (const p of result.unchanged) changes.push({ path: rel(cwd, p), action: "unchanged" });
11670
+ return { name: inst.name, changes };
11671
+ }
11672
+ function rel(cwd, abs) {
11673
+ return path27.relative(cwd, abs) || abs;
11674
+ }
11675
+ var ProjectUpdateCommand = class extends Command6 {
11676
+ static paths = [["project", "update"]];
11677
+ static usage = Command6.Usage({
11678
+ description: "Re-sync vendored runtime, installed subsystems, and consumer skills to the installed package version",
11679
+ examples: [
11680
+ ["Re-sync everything after a package bump", "codegen update"],
11681
+ ["Preview without writing", "codegen update --dry-run"],
11682
+ ["Overwrite even with uncommitted changes", "codegen update --force"],
11683
+ ["Skip the skills re-sync", "codegen update --skip-skills"]
11684
+ ]
11685
+ });
11686
+ dryRun = Option6.Boolean("--dry-run", false);
11687
+ force = Option6.Boolean("--force", false);
11688
+ skipSkills = Option6.Boolean("--skip-skills", false);
11689
+ skipSubsystems = Option6.Boolean("--skip-subsystems", false);
11690
+ json = Option6.Boolean("--json", false);
11691
+ cwd = Option6.String("--cwd", { required: false });
11692
+ configPath = Option6.String("--config", { required: false });
11693
+ async execute() {
11694
+ if (this.json) setJsonMode(true);
11695
+ const ctx = await loadContext({
11696
+ cwd: this.cwd,
11697
+ configPath: this.configPath,
11698
+ json: this.json,
11699
+ skipDetection: true
11700
+ });
11701
+ if (!ctx.isInitialized) {
11702
+ if (isJsonMode()) {
11703
+ printJson({ command: "project update", status: "not-initialized" });
11704
+ } else {
11705
+ printWarning("project is not initialized \u2014 run `codegen init` first");
11706
+ }
11707
+ return 1;
11708
+ }
11709
+ const installed = this.skipSubsystems ? [] : await detectInstalledSubsystems(ctx);
11710
+ if (!this.dryRun && !this.force) {
11711
+ const vendoredDry = syncVendoredRuntime(ctx.cwd, false);
11712
+ const vendoredDirtyCandidates = vendoredDry.filter((c) => c.action === "updated").map((c) => path27.join(ctx.cwd, c.path));
11713
+ const skillDirtyCandidates = this.skipSkills ? [] : runSkillsInstall({ cwd: ctx.cwd, dryRun: true }).report?.updated.map((e) => e.dest) ?? [];
11714
+ const subsystemDirs = installed.filter((i) => !NON_RUNTIME_SUBSYSTEMS.has(i.name)).map((i) => i.path);
11715
+ const gate = checkGitSafety(
11716
+ [...vendoredDirtyCandidates, ...skillDirtyCandidates, ...subsystemDirs],
11717
+ ctx.cwd
11718
+ );
11719
+ if (gate.inRepo && !gate.clean) {
11720
+ if (isJsonMode()) {
11721
+ printJson({
11722
+ command: "project update",
11723
+ status: "dirty-tree",
11724
+ dirty: gate.dirty
11725
+ });
11726
+ } else {
11727
+ printWarning(
11728
+ `Uncommitted changes in ${gate.dirty.length} file(s) that update would overwrite. Commit them or pass --force.`
11729
+ );
11730
+ for (const d of gate.dirty.slice(0, 10)) {
11731
+ console.log(` ${theme.muted(icons.dash)} ${d}`);
11732
+ }
11733
+ }
11734
+ return 1;
11735
+ }
11736
+ }
11737
+ const write = !this.dryRun;
11738
+ const vendored = syncVendoredRuntime(ctx.cwd, write);
11739
+ const subsystemResults = [];
11740
+ for (const inst of installed) {
11741
+ subsystemResults.push(await syncSubsystemRuntime(ctx.cwd, inst, write));
11742
+ }
11743
+ const skills = this.skipSkills ? null : runSkillsInstall({ cwd: ctx.cwd, dryRun: this.dryRun });
11744
+ const tally = (changes) => ({
11745
+ created: changes.filter((c) => c.action === "created").length,
11746
+ updated: changes.filter((c) => c.action === "updated").length,
11747
+ unchanged: changes.filter((c) => c.action === "unchanged").length
11748
+ });
11749
+ if (isJsonMode()) {
11750
+ printJson({
11751
+ command: "project update",
11752
+ dryRun: this.dryRun,
11753
+ runtime: vendored,
11754
+ subsystems: subsystemResults.map((s) => ({
11755
+ name: s.name,
11756
+ skipped: s.skippedReason ?? null,
11757
+ ...tally(s.changes)
11758
+ })),
11759
+ skills: skills && skills.report ? {
11760
+ created: skills.report.created.length,
11761
+ updated: skills.report.updated.length,
11762
+ unchanged: skills.report.unchanged.length
11763
+ } : null
11764
+ });
11765
+ return 0;
11766
+ }
11767
+ printInfo(`Updating ${ctx.cwd} to the installed @pattern-stack/codegen version`);
11768
+ console.log("");
11769
+ renderSection("shared runtime", vendored);
11770
+ for (const s of subsystemResults) {
11771
+ if (s.skippedReason) {
11772
+ console.log(
11773
+ ` ${theme.muted(icons.dash)} ${theme.muted(`subsystem ${s.name} skipped (${s.skippedReason})`)}`
11774
+ );
11775
+ continue;
11776
+ }
11777
+ renderSection(`subsystem ${s.name}`, s.changes);
11778
+ }
11779
+ if (skills?.report) {
11780
+ renderSection(
11781
+ "skills",
11782
+ [
11783
+ ...skills.report.created.map((e) => ({ path: e.relPath, action: "created" })),
11784
+ ...skills.report.updated.map((e) => ({ path: e.relPath, action: "updated" })),
11785
+ ...skills.report.unchanged.map((e) => ({
11786
+ path: e.relPath,
11787
+ action: "unchanged"
11788
+ }))
11789
+ ]
11790
+ );
11791
+ } else if (this.skipSkills) {
11792
+ console.log(` ${theme.muted(icons.dash)} ${theme.muted("skills skipped (--skip-skills)")}`);
11793
+ }
11794
+ console.log("");
11795
+ if (this.dryRun) {
11796
+ printWarning("dry-run \u2014 no files written");
11797
+ return 0;
11798
+ }
11799
+ printSuccess("update complete");
11800
+ printInfo(
11801
+ "Schema shape changes are NOT re-synced \u2014 if a subsystem schema changed across versions, run `codegen subsystem install <name> --force --force-config`."
11802
+ );
11803
+ return 0;
11804
+ }
11805
+ };
11806
+ function renderSection(label, changes) {
11807
+ const created = changes.filter((c) => c.action === "created");
11808
+ const updated = changes.filter((c) => c.action === "updated");
11809
+ const unchanged = changes.filter((c) => c.action === "unchanged");
11810
+ if (created.length === 0 && updated.length === 0 && unchanged.length === 0) return;
11811
+ const head = created.length + updated.length === 0 ? theme.muted(`${label} \u2014 up to date (${unchanged.length})`) : `${theme.system(label)} \u2014 ${created.length} new, ${updated.length} updated`;
11812
+ console.log(` ${head}`);
11813
+ for (const c of [...created, ...updated]) {
11814
+ console.log(` ${theme.success(icons.check)} ${theme.muted(c.action.padEnd(8))} ${c.path}`);
11815
+ }
11816
+ }
11817
+
11818
+ // src/cli/commands/project.ts
11819
+ async function summary4(ctx) {
11347
11820
  if (!ctx.isInitialized) {
11348
11821
  return {
11349
11822
  title: "project",
@@ -11368,7 +11841,7 @@ async function summary3(ctx) {
11368
11841
  body.push(` orm: ${orm}`);
11369
11842
  body.push(` architecture: ${arch}`);
11370
11843
  body.push(` entities: ${ctx.entityCount}`);
11371
- body.push(` subsystems: ${ctx.installedSubsystems.length}/4 installed`);
11844
+ body.push(` subsystems: ${ctx.installedSubsystems.length}/${SUBSYSTEMS.length} installed`);
11372
11845
  body.push(` generated: ${generated}`);
11373
11846
  return {
11374
11847
  title: "project",
@@ -11376,7 +11849,7 @@ async function summary3(ctx) {
11376
11849
  footer: `cwd: ${ctx.cwd}`
11377
11850
  };
11378
11851
  }
11379
- async function hints3(ctx) {
11852
+ async function hints4(ctx) {
11380
11853
  if (!ctx.isInitialized) {
11381
11854
  return [
11382
11855
  { command: "codegen init", description: "Scaffold consumer project" },
@@ -11394,17 +11867,17 @@ async function hints3(ctx) {
11394
11867
  } else {
11395
11868
  out.push({ command: "codegen entity", description: "Entity summary + hints" });
11396
11869
  }
11397
- if (ctx.installedSubsystems.length < 4) {
11870
+ if (ctx.installedSubsystems.length < SUBSYSTEMS.length) {
11398
11871
  out.push({
11399
11872
  command: "codegen subsystem",
11400
- description: "Install events/jobs/cache/storage"
11873
+ description: "Install events/jobs/cache/storage/\u2026"
11401
11874
  });
11402
11875
  }
11403
11876
  return out;
11404
11877
  }
11405
- var ProjectInitCommand = class extends Command5 {
11878
+ var ProjectInitCommand = class extends Command7 {
11406
11879
  static paths = [["project", "init"]];
11407
- static usage = Command5.Usage({
11880
+ static usage = Command7.Usage({
11408
11881
  description: "Scaffold a consumer project (config, shims, barrels, app.module)",
11409
11882
  examples: [
11410
11883
  ["Initialize with defaults", "codegen project init --yes"],
@@ -11413,12 +11886,14 @@ var ProjectInitCommand = class extends Command5 {
11413
11886
  ["Overwrite existing shims", "codegen project init --force"]
11414
11887
  ]
11415
11888
  });
11416
- yes = 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 });
11889
+ yes = Option7.Boolean("--yes,-y", false);
11890
+ dryRun = Option7.Boolean("--dry-run", false);
11891
+ force = Option7.Boolean("--force", false);
11892
+ withTsconfig = Option7.Boolean("--with-tsconfig", false);
11893
+ // Vendor consumer skills into .claude/skills by default; opt out with --no-skills.
11894
+ skills = Option7.Boolean("--skills", true);
11895
+ json = Option7.Boolean("--json", false);
11896
+ cwd = Option7.String("--cwd", { required: false });
11422
11897
  async execute() {
11423
11898
  if (this.json) setJsonMode(true);
11424
11899
  const ctx = await loadContext({
@@ -11449,6 +11924,7 @@ var ProjectInitCommand = class extends Command5 {
11449
11924
  console.log("");
11450
11925
  }
11451
11926
  const result = writePlan(plan);
11927
+ const skillsResult = this.skills ? runSkillsInstall({ cwd: ctx.cwd, dryRun: false }) : null;
11452
11928
  if (isJsonMode()) {
11453
11929
  printJson({
11454
11930
  command: "project init",
@@ -11457,7 +11933,12 @@ var ProjectInitCommand = class extends Command5 {
11457
11933
  created: result.created.map((e) => e.relPath),
11458
11934
  merged: result.merged.map((e) => e.relPath),
11459
11935
  overwritten: result.overwritten.map((e) => e.relPath),
11460
- skipped: result.skipped.map((e) => ({ path: e.relPath, reason: e.reason }))
11936
+ skipped: result.skipped.map((e) => ({ path: e.relPath, reason: e.reason })),
11937
+ skills: skillsResult && skillsResult.report ? {
11938
+ created: skillsResult.report.created.length,
11939
+ updated: skillsResult.report.updated.length,
11940
+ unchanged: skillsResult.report.unchanged.length
11941
+ } : null
11461
11942
  });
11462
11943
  return 0;
11463
11944
  }
@@ -11486,6 +11967,22 @@ var ProjectInitCommand = class extends Command5 {
11486
11967
  ` ${theme.muted(icons.dash)} ${theme.muted("skip ")} ${e.relPath}${e.reason ? theme.muted(" (" + e.reason + ")") : ""}`
11487
11968
  );
11488
11969
  }
11970
+ if (skillsResult?.report) {
11971
+ const r = skillsResult.report;
11972
+ console.log(
11973
+ ` ${theme.success(icons.check)} ${theme.muted("skills ")} .claude/skills/ ${theme.muted(
11974
+ `(${r.created.length} new, ${r.updated.length} updated, ${r.unchanged.length} unchanged)`
11975
+ )}`
11976
+ );
11977
+ } else if (!this.skills) {
11978
+ console.log(
11979
+ ` ${theme.muted(icons.dash)} ${theme.muted("skip ")} .claude/skills/ ${theme.muted("(--no-skills)")}`
11980
+ );
11981
+ } else if (skillsResult && !skillsResult.ok) {
11982
+ console.log(
11983
+ ` ${theme.warning(icons.warning)} ${theme.muted("skills ")} ${theme.muted(skillsResult.error ?? "skills install failed")}`
11984
+ );
11985
+ }
11489
11986
  console.log("");
11490
11987
  printInfo("Next steps:");
11491
11988
  console.log(` 1. ${theme.system("bun add")} the peer deps (see docs/CONSUMER-SETUP.md)`);
@@ -11498,10 +11995,10 @@ var ProjectInitCommand = class extends Command5 {
11498
11995
  };
11499
11996
  function askConfirm(question) {
11500
11997
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11501
- return new Promise((resolve3) => {
11998
+ return new Promise((resolve4) => {
11502
11999
  rl.question(`${question} [Y/n] `, (answer) => {
11503
12000
  rl.close();
11504
- resolve3(answer.trim().toLowerCase() !== "n");
12001
+ resolve4(answer.trim().toLowerCase() !== "n");
11505
12002
  });
11506
12003
  });
11507
12004
  }
@@ -11534,9 +12031,9 @@ function renderPlanOnly(plan, opts) {
11534
12031
  }
11535
12032
  return 0;
11536
12033
  }
11537
- var ProjectScanCommand = class extends Command5 {
12034
+ var ProjectScanCommand = class extends Command7 {
11538
12035
  static paths = [["project", "scan"]];
11539
- static usage = Command5.Usage({
12036
+ static usage = Command7.Usage({
11540
12037
  description: "Detect framework/ORM/architecture and propose a codegen.config.yaml",
11541
12038
  examples: [
11542
12039
  ["Scan the current directory", "codegen project scan"],
@@ -11544,17 +12041,17 @@ var ProjectScanCommand = class extends Command5 {
11544
12041
  ["Preview only", "codegen project scan --dry-run"]
11545
12042
  ]
11546
12043
  });
11547
- directory = 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 });
12044
+ directory = Option7.String({ required: false });
12045
+ write = Option7.Boolean("--write", false);
12046
+ dryRun = Option7.Boolean("--dry-run", false);
12047
+ verbose = Option7.Boolean("--verbose,-v", false);
12048
+ json = Option7.Boolean("--json", false);
12049
+ cwd = Option7.String("--cwd", { required: false });
11553
12050
  async execute() {
11554
12051
  if (this.json) setJsonMode(true);
11555
- const baseCwd = this.cwd ? path25.resolve(this.cwd) : process.cwd();
11556
- const target = this.directory ? path25.resolve(baseCwd, this.directory) : baseCwd;
11557
- if (!fs14.existsSync(target)) {
12052
+ const baseCwd = this.cwd ? path28.resolve(this.cwd) : process.cwd();
12053
+ const target = this.directory ? path28.resolve(baseCwd, this.directory) : baseCwd;
12054
+ if (!fs17.existsSync(target)) {
11558
12055
  printError(`Directory not found: ${target}`);
11559
12056
  return 1;
11560
12057
  }
@@ -11604,8 +12101,8 @@ var ProjectScanCommand = class extends Command5 {
11604
12101
  `architecture: ${profile.architecture.evidence.join(", ") || "\u2014"}`
11605
12102
  ]);
11606
12103
  }
11607
- const outPath = path25.join(target, "codegen.config.yaml");
11608
- const existsNow = fs14.existsSync(outPath);
12104
+ const outPath = path28.join(target, "codegen.config.yaml");
12105
+ const existsNow = fs17.existsSync(outPath);
11609
12106
  if (this.dryRun) {
11610
12107
  console.log("");
11611
12108
  printInfo("Dry run \u2014 proposed codegen.config.yaml:");
@@ -11618,7 +12115,7 @@ var ProjectScanCommand = class extends Command5 {
11618
12115
  printWarning(`${outPath} already exists \u2014 pass --force via edit; skipping.`);
11619
12116
  return 0;
11620
12117
  }
11621
- fs14.writeFileSync(outPath, yamlText);
12118
+ fs17.writeFileSync(outPath, yamlText);
11622
12119
  printSuccess(`wrote ${outPath}`);
11623
12120
  return 0;
11624
12121
  }
@@ -11636,14 +12133,14 @@ function printMutedBlock(title, lines) {
11636
12133
  console.log(theme.muted(title + ":"));
11637
12134
  for (const l of lines) console.log(theme.muted(" " + l));
11638
12135
  }
11639
- var ProjectConfigCommand = class extends Command5 {
12136
+ var ProjectConfigCommand = class extends Command7 {
11640
12137
  static paths = [["project", "config"]];
11641
- static usage = Command5.Usage({
12138
+ static usage = Command7.Usage({
11642
12139
  description: "Print the resolved codegen config (YAML or JSON)"
11643
12140
  });
11644
- json = Option5.Boolean("--json", false);
11645
- cwd = Option5.String("--cwd", { required: false });
11646
- configPath = Option5.String("--config", { required: false });
12141
+ json = Option7.Boolean("--json", false);
12142
+ cwd = Option7.String("--cwd", { required: false });
12143
+ configPath = Option7.String("--config", { required: false });
11647
12144
  async execute() {
11648
12145
  if (this.json) setJsonMode(true);
11649
12146
  const ctx = await loadContext({
@@ -11675,9 +12172,9 @@ var ProjectConfigCommand = class extends Command5 {
11675
12172
  return 0;
11676
12173
  }
11677
12174
  };
11678
- var ProjectInspectCommand = class extends Command5 {
12175
+ var ProjectInspectCommand = class extends Command7 {
11679
12176
  static paths = [["project", "inspect"]];
11680
- static usage = Command5.Usage({
12177
+ static usage = Command7.Usage({
11681
12178
  description: "Domain analysis, statistics, documentation, and manifest operations",
11682
12179
  examples: [
11683
12180
  ["Full analysis", "codegen project inspect --kind analyze"],
@@ -11687,20 +12184,20 @@ var ProjectInspectCommand = class extends Command5 {
11687
12184
  ["Review suggestions", "codegen project inspect --kind suggestions"]
11688
12185
  ]
11689
12186
  });
11690
- kind = 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 });
12187
+ kind = Option7.String("--kind", { required: true });
12188
+ dir = Option7.String({ required: false });
12189
+ format = Option7.String("--format", "console");
12190
+ output = Option7.String("--output,-o", { required: false });
12191
+ strict = Option7.Boolean("--strict", false);
12192
+ entity = Option7.String("--entity", { required: false });
12193
+ force = Option7.Boolean("--force", false);
12194
+ accept = Option7.String("--accept", { required: false });
12195
+ skip = Option7.String("--skip", { required: false });
12196
+ acceptAll = Option7.Boolean("--accept-all", false);
12197
+ skipAll = Option7.Boolean("--skip-all", false);
12198
+ json = Option7.Boolean("--json", false);
12199
+ cwd = Option7.String("--cwd", { required: false });
12200
+ configPath = Option7.String("--config", { required: false });
11704
12201
  async execute() {
11705
12202
  if (this.json || this.format === "json") setJsonMode(true);
11706
12203
  const ctx = await loadContext({
@@ -11725,12 +12222,12 @@ var ProjectInspectCommand = class extends Command5 {
11725
12222
  return 2;
11726
12223
  }
11727
12224
  resolveEntitiesDir(ctx) {
11728
- if (this.dir) return path25.resolve(ctx.cwd, this.dir);
11729
- return ctx.entitiesDir ?? path25.resolve(ctx.cwd, "entities");
12225
+ if (this.dir) return path28.resolve(ctx.cwd, this.dir);
12226
+ return ctx.entitiesDir ?? path28.resolve(ctx.cwd, "entities");
11730
12227
  }
11731
12228
  async runAnalysis(ctx, kind) {
11732
12229
  const entitiesDir = this.resolveEntitiesDir(ctx);
11733
- if (!entitiesDir || !fs14.existsSync(entitiesDir)) {
12230
+ if (!entitiesDir || !fs17.existsSync(entitiesDir)) {
11734
12231
  printError(`Directory not found: ${entitiesDir ?? "(no entities/ dir)"}`);
11735
12232
  return 1;
11736
12233
  }
@@ -11760,7 +12257,7 @@ var ProjectInspectCommand = class extends Command5 {
11760
12257
  out = formatConsole(filtered);
11761
12258
  }
11762
12259
  if (this.output) {
11763
- fs14.writeFileSync(this.output, out);
12260
+ fs17.writeFileSync(this.output, out);
11764
12261
  if (!isJsonMode()) printSuccess(`wrote ${this.output}`);
11765
12262
  } else {
11766
12263
  console.log(out);
@@ -11773,7 +12270,7 @@ var ProjectInspectCommand = class extends Command5 {
11773
12270
  }
11774
12271
  async runManifest(ctx) {
11775
12272
  const entitiesDir = this.resolveEntitiesDir(ctx);
11776
- if (!entitiesDir || !fs14.existsSync(entitiesDir)) {
12273
+ if (!entitiesDir || !fs17.existsSync(entitiesDir)) {
11777
12274
  printError(`Directory not found: ${entitiesDir ?? "(no entities/ dir)"}`);
11778
12275
  return 1;
11779
12276
  }
@@ -11906,9 +12403,9 @@ function formatStatsConsole(result) {
11906
12403
  lines.push("");
11907
12404
  return lines.join("\n");
11908
12405
  }
11909
- var ProjectGraphCommand = class extends Command5 {
12406
+ var ProjectGraphCommand = class extends Command7 {
11910
12407
  static paths = [["project", "graph"]];
11911
- static usage = Command5.Usage({
12408
+ static usage = Command7.Usage({
11912
12409
  description: "Visualize the entity-relationship graph in a browser",
11913
12410
  examples: [
11914
12411
  ["Open interactive graph viewer", "codegen project graph"],
@@ -11916,11 +12413,11 @@ var ProjectGraphCommand = class extends Command5 {
11916
12413
  ["Write graph JSON to file", "codegen project graph --output graph.json"]
11917
12414
  ]
11918
12415
  });
11919
- dir = 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 });
12416
+ dir = Option7.String({ required: false });
12417
+ output = Option7.String("--output,-o", { required: false });
12418
+ json = Option7.Boolean("--json", false);
12419
+ cwd = Option7.String("--cwd", { required: false });
12420
+ configPath = Option7.String("--config", { required: false });
11924
12421
  async execute() {
11925
12422
  if (this.json) setJsonMode(true);
11926
12423
  const ctx = await loadContext({
@@ -11929,17 +12426,17 @@ var ProjectGraphCommand = class extends Command5 {
11929
12426
  json: this.json,
11930
12427
  skipDetection: true
11931
12428
  });
11932
- const entitiesDir = this.dir ? path25.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path25.resolve(ctx.cwd, "entities");
11933
- if (!fs14.existsSync(entitiesDir)) {
12429
+ const entitiesDir = this.dir ? path28.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path28.resolve(ctx.cwd, "entities");
12430
+ if (!fs17.existsSync(entitiesDir)) {
11934
12431
  printError(`Entity directory not found: ${entitiesDir}`);
11935
12432
  return 1;
11936
12433
  }
11937
12434
  const relCandidates = [
11938
- path25.resolve(path25.dirname(entitiesDir), "relationships"),
11939
- path25.resolve(entitiesDir, "relationships"),
11940
- path25.resolve(ctx.cwd, "relationships")
12435
+ path28.resolve(path28.dirname(entitiesDir), "relationships"),
12436
+ path28.resolve(entitiesDir, "relationships"),
12437
+ path28.resolve(ctx.cwd, "relationships")
11941
12438
  ];
11942
- const relationshipsDir = relCandidates.find((d) => fs14.existsSync(d));
12439
+ const relationshipsDir = relCandidates.find((d) => fs17.existsSync(d));
11943
12440
  const result = await analyzeDomain(entitiesDir, relationshipsDir);
11944
12441
  const serialized = serializeDomainGraph(result.graph);
11945
12442
  if (isJsonMode()) {
@@ -11953,20 +12450,20 @@ var ProjectGraphCommand = class extends Command5 {
11953
12450
  return 0;
11954
12451
  }
11955
12452
  if (this.output) {
11956
- const outPath = path25.resolve(ctx.cwd, this.output);
11957
- fs14.writeFileSync(outPath, JSON.stringify(serialized, null, 2));
12453
+ const outPath = path28.resolve(ctx.cwd, this.output);
12454
+ fs17.writeFileSync(outPath, JSON.stringify(serialized, null, 2));
11958
12455
  printSuccess(`Graph written to ${outPath}`);
11959
12456
  printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
11960
12457
  return 0;
11961
12458
  }
11962
12459
  const os = await import("os");
11963
- const tmpDir = 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"));
12460
+ const tmpDir = fs17.mkdtempSync(path28.join(os.default.tmpdir(), "codegen-graph-"));
12461
+ const graphPath = path28.join(tmpDir, "graph.json");
12462
+ fs17.writeFileSync(graphPath, JSON.stringify(serialized, null, 2));
12463
+ const viewerDir = path28.resolve(import.meta.dirname, "..", "..", "..", "tools", "schema-graph-viewer");
12464
+ const viewerDist = path28.join(viewerDir, "dist", "index.html");
12465
+ if (fs17.existsSync(viewerDist)) {
12466
+ fs17.copyFileSync(graphPath, path28.join(viewerDir, "dist", "graph.json"));
11970
12467
  printSuccess("Graph exported");
11971
12468
  printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
11972
12469
  printInfo(`Graph JSON: ${graphPath}`);
@@ -11988,18 +12485,19 @@ var projectNoun = {
11988
12485
  ProjectConfigCommand,
11989
12486
  ProjectInspectCommand,
11990
12487
  ProjectGraphCommand,
11991
- ProjectUpgradeOpenapiCommand
12488
+ ProjectUpgradeOpenapiCommand,
12489
+ ProjectUpdateCommand
11992
12490
  ],
11993
- summary: summary3,
11994
- hints: hints3
12491
+ summary: summary4,
12492
+ hints: hints4
11995
12493
  };
11996
12494
  var project_default = projectNoun;
11997
12495
 
11998
12496
  // src/cli/commands/dev.ts
11999
- import fs15 from "fs";
12000
- import path26 from "path";
12497
+ import fs18 from "fs";
12498
+ import path29 from "path";
12001
12499
  import { execSync as execSync3, spawn, spawnSync } from "child_process";
12002
- import { Command as Command6, Option as Option6 } from "clipanion";
12500
+ import { Command as Command8, Option as Option8 } from "clipanion";
12003
12501
  var DEFAULT_APP_PORT = 3e3;
12004
12502
  var DEFAULT_PG_PORT = 5433;
12005
12503
  var DEFAULT_REDIS_PORT = 6380;
@@ -12030,33 +12528,33 @@ function getRedisPort(_ctx) {
12030
12528
  return Number(process.env.DEV_REDIS_PORT ?? DEFAULT_REDIS_PORT);
12031
12529
  }
12032
12530
  function composeFilePath(cwd) {
12033
- const devPath = 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;
12531
+ const devPath = path29.join(cwd, COMPOSE_FILE);
12532
+ if (fs18.existsSync(devPath)) return devPath;
12533
+ const rootPath = path29.join(cwd, "docker-compose.yml");
12534
+ if (fs18.existsSync(rootPath)) return rootPath;
12037
12535
  return devPath;
12038
12536
  }
12039
12537
  function pidFilePath(cwd) {
12040
- return path26.join(cwd, PID_FILE);
12538
+ return path29.join(cwd, PID_FILE);
12041
12539
  }
12042
12540
  function readAppPid(cwd) {
12043
12541
  const p = pidFilePath(cwd);
12044
- if (!fs15.existsSync(p)) return null;
12045
- const pid = parseInt(fs15.readFileSync(p, "utf-8").trim(), 10);
12542
+ if (!fs18.existsSync(p)) return null;
12543
+ const pid = parseInt(fs18.readFileSync(p, "utf-8").trim(), 10);
12046
12544
  if (isNaN(pid)) return null;
12047
12545
  try {
12048
12546
  process.kill(pid, 0);
12049
12547
  return pid;
12050
12548
  } catch {
12051
- fs15.rmSync(p, { force: true });
12549
+ fs18.rmSync(p, { force: true });
12052
12550
  return null;
12053
12551
  }
12054
12552
  }
12055
12553
  function writeAppPid(cwd, pid) {
12056
- fs15.writeFileSync(pidFilePath(cwd), String(pid));
12554
+ fs18.writeFileSync(pidFilePath(cwd), String(pid));
12057
12555
  }
12058
12556
  function clearAppPid(cwd) {
12059
- fs15.rmSync(pidFilePath(cwd), { force: true });
12557
+ fs18.rmSync(pidFilePath(cwd), { force: true });
12060
12558
  }
12061
12559
  function checkPostgres(cwd, port) {
12062
12560
  const r = runCmd(`docker exec codegen-dev-postgres pg_isready -U postgres`, cwd, {
@@ -12096,8 +12594,10 @@ function checkApp(cwd, port) {
12096
12594
  };
12097
12595
  }
12098
12596
  function listEntityNames(ctx) {
12099
- if (!ctx.entitiesDir || !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$/, ""));
12597
+ if (!ctx.entitiesDir || !fs18.existsSync(ctx.entitiesDir)) return [];
12598
+ return findYamlFiles(ctx.entitiesDir).map(
12599
+ (f) => path29.basename(f).replace(/\.ya?ml$/, "")
12600
+ );
12101
12601
  }
12102
12602
  function formatServiceLine(svc) {
12103
12603
  const icon = svc.healthy ? theme.success(icons.check) : theme.error(icons.error);
@@ -12106,8 +12606,8 @@ function formatServiceLine(svc) {
12106
12606
  return `${icon} ${svc.name.padEnd(12)} ${theme.muted(`${svc.host}:${svc.port}`)} ${status}${pidStr}`;
12107
12607
  }
12108
12608
  function ensureComposeFile(cwd, pgPort, redisPort) {
12109
- const composePath = path26.join(cwd, COMPOSE_FILE);
12110
- if (fs15.existsSync(composePath)) return composePath;
12609
+ const composePath = path29.join(cwd, COMPOSE_FILE);
12610
+ if (fs18.existsSync(composePath)) return composePath;
12111
12611
  const content = `# Auto-generated by codegen dev
12112
12612
  # Ports offset from defaults to avoid conflicts with other local services.
12113
12613
  services:
@@ -12142,22 +12642,22 @@ services:
12142
12642
  volumes:
12143
12643
  codegen-dev-pgdata:
12144
12644
  `;
12145
- fs15.writeFileSync(composePath, content);
12645
+ fs18.writeFileSync(composePath, content);
12146
12646
  return composePath;
12147
12647
  }
12148
- var DevUpCommand = class extends Command6 {
12648
+ var DevUpCommand = class extends Command8 {
12149
12649
  static paths = [["dev", "up"]];
12150
- static usage = Command6.Usage({
12650
+ static usage = Command8.Usage({
12151
12651
  description: "Start Docker services (Postgres + Redis), run migrations, start the NestJS app",
12152
12652
  examples: [
12153
12653
  ["Start everything", "codegen dev up"],
12154
12654
  ["Skip app start (services only)", "codegen dev up --no-app"]
12155
12655
  ]
12156
12656
  });
12157
- noApp = 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 });
12657
+ noApp = Option8.Boolean("--no-app", false);
12658
+ json = Option8.Boolean("--json", false);
12659
+ cwd = Option8.String("--cwd", { required: false });
12660
+ configPath = Option8.String("--config", { required: false });
12161
12661
  async execute() {
12162
12662
  if (this.json) setJsonMode(true);
12163
12663
  const ctx = await loadContext({
@@ -12191,7 +12691,7 @@ var DevUpCommand = class extends Command6 {
12191
12691
  if (!pgReady) printWarning("postgres did not become healthy in time");
12192
12692
  if (!redisReady) printWarning("redis did not become healthy in time");
12193
12693
  const drizzleConfig = ["drizzle.config.ts", "drizzle.config.js"].find(
12194
- (f) => fs15.existsSync(path26.join(ctx.cwd, f))
12694
+ (f) => fs18.existsSync(path29.join(ctx.cwd, f))
12195
12695
  );
12196
12696
  if (drizzleConfig) {
12197
12697
  if (!isJsonMode()) printInfo("pushing database schema...");
@@ -12211,8 +12711,8 @@ var DevUpCommand = class extends Command6 {
12211
12711
  if (!isJsonMode()) printInfo("starting NestJS app...");
12212
12712
  const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
12213
12713
  const redisUrl = `redis://localhost:${redisPort}`;
12214
- const logFile = path26.join(ctx.cwd, ".dev-app.log");
12215
- const logFd = fs15.openSync(logFile, "a");
12714
+ const logFile = path29.join(ctx.cwd, ".dev-app.log");
12715
+ const logFd = fs18.openSync(logFile, "a");
12216
12716
  const child = spawn("bun", ["src/main.ts"], {
12217
12717
  cwd: ctx.cwd,
12218
12718
  detached: true,
@@ -12225,7 +12725,7 @@ var DevUpCommand = class extends Command6 {
12225
12725
  }
12226
12726
  });
12227
12727
  child.unref();
12228
- fs15.closeSync(logFd);
12728
+ fs18.closeSync(logFd);
12229
12729
  if (child.pid) {
12230
12730
  writeAppPid(ctx.cwd, child.pid);
12231
12731
  spawnSync("sleep", ["2"]);
@@ -12255,15 +12755,15 @@ var DevUpCommand = class extends Command6 {
12255
12755
  return 0;
12256
12756
  }
12257
12757
  };
12258
- var DevDownCommand = class extends Command6 {
12758
+ var DevDownCommand = class extends Command8 {
12259
12759
  static paths = [["dev", "down"]];
12260
- static usage = Command6.Usage({
12760
+ static usage = Command8.Usage({
12261
12761
  description: "Stop Docker services and the NestJS app"
12262
12762
  });
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 });
12763
+ volumes = Option8.Boolean("--volumes,-v", false);
12764
+ json = Option8.Boolean("--json", false);
12765
+ cwd = Option8.String("--cwd", { required: false });
12766
+ configPath = Option8.String("--config", { required: false });
12267
12767
  async execute() {
12268
12768
  if (this.json) setJsonMode(true);
12269
12769
  const ctx = await loadContext({
@@ -12299,14 +12799,14 @@ var DevDownCommand = class extends Command6 {
12299
12799
  return 0;
12300
12800
  }
12301
12801
  };
12302
- var DevStatusCommand = class extends Command6 {
12802
+ var DevStatusCommand = class extends Command8 {
12303
12803
  static paths = [["dev", "status"]];
12304
- static usage = Command6.Usage({
12804
+ static usage = Command8.Usage({
12305
12805
  description: "Show status of Docker services and the NestJS app"
12306
12806
  });
12307
- json = Option6.Boolean("--json", false);
12308
- cwd = Option6.String("--cwd", { required: false });
12309
- configPath = Option6.String("--config", { required: false });
12807
+ json = Option8.Boolean("--json", false);
12808
+ cwd = Option8.String("--cwd", { required: false });
12809
+ configPath = Option8.String("--config", { required: false });
12310
12810
  async execute() {
12311
12811
  if (this.json) setJsonMode(true);
12312
12812
  const ctx = await loadContext({
@@ -12334,9 +12834,9 @@ var DevStatusCommand = class extends Command6 {
12334
12834
  return 0;
12335
12835
  }
12336
12836
  };
12337
- var DevLogsCommand = class extends Command6 {
12837
+ var DevLogsCommand = class extends Command8 {
12338
12838
  static paths = [["dev", "logs"]];
12339
- static usage = Command6.Usage({
12839
+ static usage = Command8.Usage({
12340
12840
  description: "Tail application and Docker service logs",
12341
12841
  examples: [
12342
12842
  ["Tail app logs", "codegen dev logs"],
@@ -12344,11 +12844,11 @@ var DevLogsCommand = class extends Command6 {
12344
12844
  ["Show last N lines", "codegen dev logs --tail 50"]
12345
12845
  ]
12346
12846
  });
12347
- docker = 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 });
12847
+ docker = Option8.Boolean("--docker", false);
12848
+ tail = Option8.String("--tail", "30");
12849
+ json = Option8.Boolean("--json", false);
12850
+ cwd = Option8.String("--cwd", { required: false });
12851
+ configPath = Option8.String("--config", { required: false });
12352
12852
  async execute() {
12353
12853
  if (this.json) setJsonMode(true);
12354
12854
  const ctx = await loadContext({
@@ -12369,8 +12869,8 @@ var DevLogsCommand = class extends Command6 {
12369
12869
  }
12370
12870
  return 0;
12371
12871
  }
12372
- const logFile = path26.join(ctx.cwd, ".dev-app.log");
12373
- if (!fs15.existsSync(logFile)) {
12872
+ const logFile = path29.join(ctx.cwd, ".dev-app.log");
12873
+ if (!fs18.existsSync(logFile)) {
12374
12874
  printInfo("no app logs found \u2014 is the app running?");
12375
12875
  return 0;
12376
12876
  }
@@ -12382,14 +12882,14 @@ var DevLogsCommand = class extends Command6 {
12382
12882
  return 0;
12383
12883
  }
12384
12884
  };
12385
- var DevRestartCommand = class extends Command6 {
12885
+ var DevRestartCommand = class extends Command8 {
12386
12886
  static paths = [["dev", "restart"]];
12387
- static usage = Command6.Usage({
12887
+ static usage = Command8.Usage({
12388
12888
  description: "Restart the NestJS app (keep Docker services running)"
12389
12889
  });
12390
- json = Option6.Boolean("--json", false);
12391
- cwd = Option6.String("--cwd", { required: false });
12392
- configPath = Option6.String("--config", { required: false });
12890
+ json = Option8.Boolean("--json", false);
12891
+ cwd = Option8.String("--cwd", { required: false });
12892
+ configPath = Option8.String("--config", { required: false });
12393
12893
  async execute() {
12394
12894
  if (this.json) setJsonMode(true);
12395
12895
  const ctx = await loadContext({
@@ -12413,8 +12913,8 @@ var DevRestartCommand = class extends Command6 {
12413
12913
  spawnSync("sleep", ["1"]);
12414
12914
  const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
12415
12915
  const redisUrl = `redis://localhost:${redisPort}`;
12416
- const logFile = path26.join(ctx.cwd, ".dev-app.log");
12417
- const logFd = fs15.openSync(logFile, "a");
12916
+ const logFile = path29.join(ctx.cwd, ".dev-app.log");
12917
+ const logFd = fs18.openSync(logFile, "a");
12418
12918
  const child = spawn("bun", ["src/main.ts"], {
12419
12919
  cwd: ctx.cwd,
12420
12920
  detached: true,
@@ -12427,7 +12927,7 @@ var DevRestartCommand = class extends Command6 {
12427
12927
  }
12428
12928
  });
12429
12929
  child.unref();
12430
- fs15.closeSync(logFd);
12930
+ fs18.closeSync(logFd);
12431
12931
  if (child.pid) {
12432
12932
  writeAppPid(ctx.cwd, child.pid);
12433
12933
  spawnSync("sleep", ["2"]);
@@ -12489,7 +12989,7 @@ function renderDevStatus(ctx) {
12489
12989
  { command: "/dev-test", description: "Run test suite + endpoint verification" }
12490
12990
  ]);
12491
12991
  }
12492
- async function summary4(ctx) {
12992
+ async function summary5(ctx) {
12493
12993
  const pgPort = getPgPort(ctx);
12494
12994
  const redisPort = getRedisPort(ctx);
12495
12995
  const appPort = getAppPort(ctx);
@@ -12513,7 +13013,7 @@ async function summary4(ctx) {
12513
13013
  footer: `${running}/${total} services healthy`
12514
13014
  };
12515
13015
  }
12516
- async function hints4(ctx) {
13016
+ async function hints5(ctx) {
12517
13017
  const app = checkApp(ctx.cwd, getAppPort(ctx));
12518
13018
  if (!app.healthy) {
12519
13019
  return [
@@ -12536,21 +13036,20 @@ var devNoun = {
12536
13036
  DevLogsCommand,
12537
13037
  DevRestartCommand
12538
13038
  ],
12539
- summary: summary4,
12540
- hints: hints4
13039
+ summary: summary5,
13040
+ hints: hints5
12541
13041
  };
12542
13042
  var dev_default = devNoun;
12543
13043
 
12544
13044
  // src/cli/commands/relationship.ts
12545
- import fs16 from "fs";
12546
- import path27 from "path";
12547
- import { Command as Command7, Option as Option7 } from "clipanion";
13045
+ import fs19 from "fs";
13046
+ import path30 from "path";
13047
+ import { Command as Command9, Option as Option9 } from "clipanion";
12548
13048
  function listRelationshipYamls2(dir) {
12549
- if (!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));
13049
+ if (!fs19.existsSync(dir)) return [];
13050
+ return findYamlFiles(dir).filter(
13051
+ (full) => detectYamlType(full) === "relationship"
13052
+ );
12554
13053
  }
12555
13054
  function summarizeRelationshipFile(filePath) {
12556
13055
  const result = loadRelationshipFromYaml(filePath);
@@ -12570,8 +13069,8 @@ function summarizeRelationshipFile(filePath) {
12570
13069
  function padRight2(s, n) {
12571
13070
  return s.length >= n ? s : s + " ".repeat(n - s.length);
12572
13071
  }
12573
- async function summary5(ctx) {
12574
- const relDir = path27.resolve(ctx.cwd, "relationships");
13072
+ async function summary6(ctx) {
13073
+ const relDir = path30.resolve(ctx.cwd, "relationships");
12575
13074
  const files = listRelationshipYamls2(relDir);
12576
13075
  if (files.length === 0) {
12577
13076
  return {
@@ -12603,16 +13102,16 @@ async function summary5(ctx) {
12603
13102
  footer: `${rows.length} relationships`
12604
13103
  };
12605
13104
  }
12606
- async function hints5(_ctx) {
13105
+ async function hints6(_ctx) {
12607
13106
  return [
12608
13107
  { command: "codegen relationship new <file>", description: "Generate one relationship" },
12609
13108
  { command: "codegen relationship new --all", description: "Generate all relationships" },
12610
13109
  { command: "codegen relationship list", description: "List relationship definitions" }
12611
13110
  ];
12612
13111
  }
12613
- var RelationshipNewCommand = class extends Command7 {
13112
+ var RelationshipNewCommand = class extends Command9 {
12614
13113
  static paths = [["relationship", "new"]];
12615
- static usage = Command7.Usage({
13114
+ static usage = Command9.Usage({
12616
13115
  description: "Generate code for one or more relationships from YAML",
12617
13116
  examples: [
12618
13117
  ["Generate a single relationship", "codegen relationship new relationships/person_organization.yaml"],
@@ -12620,13 +13119,13 @@ var RelationshipNewCommand = class extends Command7 {
12620
13119
  ["Preview without writing", "codegen relationship new relationships/person_organization.yaml --dry-run"]
12621
13120
  ]
12622
13121
  });
12623
- yaml = 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 });
13122
+ yaml = Option9.String({ required: false });
13123
+ all = Option9.Boolean("--all", false);
13124
+ dryRun = Option9.Boolean("--dry-run", false);
13125
+ force = Option9.Boolean("--force", false);
13126
+ json = Option9.Boolean("--json", false);
13127
+ cwd = Option9.String("--cwd", { required: false });
13128
+ configPath = Option9.String("--config", { required: false });
12630
13129
  async execute() {
12631
13130
  if (this.json) setJsonMode(true);
12632
13131
  const ctx = await loadContext({
@@ -12641,14 +13140,14 @@ var RelationshipNewCommand = class extends Command7 {
12641
13140
  }
12642
13141
  let targets = [];
12643
13142
  if (this.all) {
12644
- const dir = path27.resolve(ctx.cwd, "relationships");
13143
+ const dir = path30.resolve(ctx.cwd, "relationships");
12645
13144
  targets = listRelationshipYamls2(dir);
12646
13145
  if (targets.length === 0) {
12647
13146
  printError(`No relationship YAML files found in ${dir}`);
12648
13147
  return 1;
12649
13148
  }
12650
13149
  } else if (this.yaml) {
12651
- targets = [path27.resolve(ctx.cwd, this.yaml)];
13150
+ targets = [path30.resolve(ctx.cwd, this.yaml)];
12652
13151
  } else {
12653
13152
  printError("Missing YAML path. Pass a file or --all.");
12654
13153
  return 2;
@@ -12665,7 +13164,7 @@ var RelationshipNewCommand = class extends Command7 {
12665
13164
  }
12666
13165
  if (invalid.length > 0) {
12667
13166
  for (const i of invalid) {
12668
- printError(`${path27.basename(i.file)} \u2014 ${i.message}`);
13167
+ printError(`${path30.basename(i.file)} \u2014 ${i.message}`);
12669
13168
  }
12670
13169
  if (!isJsonMode()) return 1;
12671
13170
  }
@@ -12696,7 +13195,7 @@ var RelationshipNewCommand = class extends Command7 {
12696
13195
  }
12697
13196
  const succeeded = [];
12698
13197
  const failed = [
12699
- ...invalid.map((i) => ({ name: path27.basename(i.file), file: i.file, message: i.message }))
13198
+ ...invalid.map((i) => ({ name: path30.basename(i.file), file: i.file, message: i.message }))
12700
13199
  ];
12701
13200
  for (const v of validated) {
12702
13201
  if (!isJsonMode()) {
@@ -12715,8 +13214,8 @@ var RelationshipNewCommand = class extends Command7 {
12715
13214
  if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
12716
13215
  }
12717
13216
  }
12718
- const entitiesDir = ctx.entitiesDir ?? path27.resolve(ctx.cwd, "entities");
12719
- const relationshipsDir = path27.resolve(ctx.cwd, "relationships");
13217
+ const entitiesDir = ctx.entitiesDir ?? path30.resolve(ctx.cwd, "entities");
13218
+ const relationshipsDir = path30.resolve(ctx.cwd, "relationships");
12720
13219
  const generatedDir = resolveGeneratedDir(ctx);
12721
13220
  const architecture = resolveArchitecture(ctx);
12722
13221
  let barrelResult = null;
@@ -12761,21 +13260,21 @@ var RelationshipNewCommand = class extends Command7 {
12761
13260
  }
12762
13261
  if (barrelResult) {
12763
13262
  printInfo(
12764
- `barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path27.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path27.relative(ctx.cwd, barrelResult.schemaBarrel)}`
13263
+ `barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path30.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path30.relative(ctx.cwd, barrelResult.schemaBarrel)}`
12765
13264
  );
12766
13265
  }
12767
13266
  }
12768
13267
  return failed.length === 0 ? 0 : 1;
12769
13268
  }
12770
13269
  };
12771
- var RelationshipListCommand = class extends Command7 {
13270
+ var RelationshipListCommand = class extends Command9 {
12772
13271
  static paths = [["relationship", "list"]];
12773
- static usage = Command7.Usage({
13272
+ static usage = Command9.Usage({
12774
13273
  description: "List defined relationships as a table"
12775
13274
  });
12776
- json = Option7.Boolean("--json", false);
12777
- cwd = Option7.String("--cwd", { required: false });
12778
- configPath = Option7.String("--config", { required: false });
13275
+ json = Option9.Boolean("--json", false);
13276
+ cwd = Option9.String("--cwd", { required: false });
13277
+ configPath = Option9.String("--config", { required: false });
12779
13278
  async execute() {
12780
13279
  if (this.json) setJsonMode(true);
12781
13280
  const ctx = await loadContext({
@@ -12784,7 +13283,7 @@ var RelationshipListCommand = class extends Command7 {
12784
13283
  json: this.json,
12785
13284
  skipDetection: true
12786
13285
  });
12787
- const relDir = path27.resolve(ctx.cwd, "relationships");
13286
+ const relDir = path30.resolve(ctx.cwd, "relationships");
12788
13287
  const files = listRelationshipYamls2(relDir);
12789
13288
  if (files.length === 0) {
12790
13289
  printInfo("No relationship definitions found.");
@@ -12818,14 +13317,14 @@ var RelationshipListCommand = class extends Command7 {
12818
13317
  var relationshipNoun = {
12819
13318
  name: "relationship",
12820
13319
  commandClasses: [RelationshipNewCommand, RelationshipListCommand],
12821
- summary: summary5,
12822
- hints: hints5
13320
+ summary: summary6,
13321
+ hints: hints6
12823
13322
  };
12824
13323
  var relationship_default = relationshipNoun;
12825
13324
 
12826
13325
  // src/cli/commands/junction.ts
12827
- import path28 from "path";
12828
- import { Command as Command8, Option as Option8 } from "clipanion";
13326
+ import path31 from "path";
13327
+ import { Command as Command10, Option as Option10 } from "clipanion";
12829
13328
  function summarizeJunctionFile(filePath) {
12830
13329
  const result = loadJunctionFromYaml(filePath);
12831
13330
  if (!result.success) return null;
@@ -12846,8 +13345,8 @@ function summarizeJunctionFile(filePath) {
12846
13345
  function padRight3(s, n) {
12847
13346
  return s.length >= n ? s : s + " ".repeat(n - s.length);
12848
13347
  }
12849
- async function summary6(ctx) {
12850
- const junctionDir = path28.resolve(ctx.cwd, "junctions");
13348
+ async function summary7(ctx) {
13349
+ const junctionDir = path31.resolve(ctx.cwd, "junctions");
12851
13350
  const files = listJunctionYamls(junctionDir);
12852
13351
  if (files.length === 0) {
12853
13352
  return {
@@ -12879,16 +13378,16 @@ async function summary6(ctx) {
12879
13378
  footer: `${rows.length} junctions`
12880
13379
  };
12881
13380
  }
12882
- async function hints6(_ctx) {
13381
+ async function hints7(_ctx) {
12883
13382
  return [
12884
13383
  { command: "codegen junction new <file>", description: "Generate one junction" },
12885
13384
  { command: "codegen junction new --all", description: "Generate all junctions" },
12886
13385
  { command: "codegen junction list", description: "List junction definitions" }
12887
13386
  ];
12888
13387
  }
12889
- var JunctionNewCommand = class extends Command8 {
13388
+ var JunctionNewCommand = class extends Command10 {
12890
13389
  static paths = [["junction", "new"]];
12891
- static usage = Command8.Usage({
13390
+ static usage = Command10.Usage({
12892
13391
  description: "Generate code for one or more junctions from YAML",
12893
13392
  examples: [
12894
13393
  ["Generate a single junction", "codegen junction new junctions/opportunity_contact.yaml"],
@@ -12896,13 +13395,13 @@ var JunctionNewCommand = class extends Command8 {
12896
13395
  ["Preview without writing", "codegen junction new junctions/opportunity_contact.yaml --dry-run"]
12897
13396
  ]
12898
13397
  });
12899
- yaml = 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 });
13398
+ yaml = Option10.String({ required: false });
13399
+ all = Option10.Boolean("--all", false);
13400
+ dryRun = Option10.Boolean("--dry-run", false);
13401
+ force = Option10.Boolean("--force", false);
13402
+ json = Option10.Boolean("--json", false);
13403
+ cwd = Option10.String("--cwd", { required: false });
13404
+ configPath = Option10.String("--config", { required: false });
12906
13405
  async execute() {
12907
13406
  if (this.json) setJsonMode(true);
12908
13407
  const ctx = await loadContext({
@@ -12917,14 +13416,14 @@ var JunctionNewCommand = class extends Command8 {
12917
13416
  }
12918
13417
  let targets = [];
12919
13418
  if (this.all) {
12920
- const dir = path28.resolve(ctx.cwd, "junctions");
13419
+ const dir = path31.resolve(ctx.cwd, "junctions");
12921
13420
  targets = listJunctionYamls(dir);
12922
13421
  if (targets.length === 0) {
12923
13422
  printError(`No junction YAML files found in ${dir}`);
12924
13423
  return 1;
12925
13424
  }
12926
13425
  } else if (this.yaml) {
12927
- targets = [path28.resolve(ctx.cwd, this.yaml)];
13426
+ targets = [path31.resolve(ctx.cwd, this.yaml)];
12928
13427
  } else {
12929
13428
  printError("Missing YAML path. Pass a file or --all.");
12930
13429
  return 2;
@@ -12943,7 +13442,7 @@ var JunctionNewCommand = class extends Command8 {
12943
13442
  }
12944
13443
  if (invalid.length > 0) {
12945
13444
  for (const i of invalid) {
12946
- printError(`${path28.basename(i.file)} \u2014 ${i.message}`);
13445
+ printError(`${path31.basename(i.file)} \u2014 ${i.message}`);
12947
13446
  }
12948
13447
  if (!isJsonMode()) return 1;
12949
13448
  }
@@ -12974,7 +13473,7 @@ var JunctionNewCommand = class extends Command8 {
12974
13473
  }
12975
13474
  const succeeded = [];
12976
13475
  const failed = [
12977
- ...invalid.map((i) => ({ name: path28.basename(i.file), file: i.file, message: i.message }))
13476
+ ...invalid.map((i) => ({ name: path31.basename(i.file), file: i.file, message: i.message }))
12978
13477
  ];
12979
13478
  for (const v of validated) {
12980
13479
  if (!isJsonMode()) {
@@ -12993,9 +13492,9 @@ var JunctionNewCommand = class extends Command8 {
12993
13492
  if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
12994
13493
  }
12995
13494
  }
12996
- const entitiesDir = ctx.entitiesDir ?? path28.resolve(ctx.cwd, "entities");
12997
- const relationshipsDir = path28.resolve(ctx.cwd, "relationships");
12998
- const junctionsDir = path28.resolve(ctx.cwd, "junctions");
13495
+ const entitiesDir = ctx.entitiesDir ?? path31.resolve(ctx.cwd, "entities");
13496
+ const relationshipsDir = path31.resolve(ctx.cwd, "relationships");
13497
+ const junctionsDir = path31.resolve(ctx.cwd, "junctions");
12999
13498
  const generatedDir = resolveGeneratedDir(ctx);
13000
13499
  const architecture = resolveArchitecture(ctx);
13001
13500
  let barrelResult = null;
@@ -13041,21 +13540,21 @@ var JunctionNewCommand = class extends Command8 {
13041
13540
  }
13042
13541
  if (barrelResult) {
13043
13542
  printInfo(
13044
- `barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path28.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path28.relative(ctx.cwd, barrelResult.schemaBarrel)}`
13543
+ `barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path31.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path31.relative(ctx.cwd, barrelResult.schemaBarrel)}`
13045
13544
  );
13046
13545
  }
13047
13546
  }
13048
13547
  return failed.length === 0 ? 0 : 1;
13049
13548
  }
13050
13549
  };
13051
- var JunctionListCommand = class extends Command8 {
13550
+ var JunctionListCommand = class extends Command10 {
13052
13551
  static paths = [["junction", "list"]];
13053
- static usage = Command8.Usage({
13552
+ static usage = Command10.Usage({
13054
13553
  description: "List defined junctions as a table"
13055
13554
  });
13056
- json = Option8.Boolean("--json", false);
13057
- cwd = Option8.String("--cwd", { required: false });
13058
- configPath = Option8.String("--config", { required: false });
13555
+ json = Option10.Boolean("--json", false);
13556
+ cwd = Option10.String("--cwd", { required: false });
13557
+ configPath = Option10.String("--config", { required: false });
13059
13558
  async execute() {
13060
13559
  if (this.json) setJsonMode(true);
13061
13560
  const ctx = await loadContext({
@@ -13064,7 +13563,7 @@ var JunctionListCommand = class extends Command8 {
13064
13563
  json: this.json,
13065
13564
  skipDetection: true
13066
13565
  });
13067
- const junctionDir = path28.resolve(ctx.cwd, "junctions");
13566
+ const junctionDir = path31.resolve(ctx.cwd, "junctions");
13068
13567
  const files = listJunctionYamls(junctionDir);
13069
13568
  if (files.length === 0) {
13070
13569
  printInfo("No junction definitions found.");
@@ -13098,16 +13597,16 @@ var JunctionListCommand = class extends Command8 {
13098
13597
  var junctionNoun = {
13099
13598
  name: "junction",
13100
13599
  commandClasses: [JunctionNewCommand, JunctionListCommand],
13101
- summary: summary6,
13102
- hints: hints6
13600
+ summary: summary7,
13601
+ hints: hints7
13103
13602
  };
13104
13603
  var junction_default = junctionNoun;
13105
13604
 
13106
13605
  // src/cli/commands/events.ts
13107
- import fs17 from "fs";
13108
- import path29 from "path";
13606
+ import fs20 from "fs";
13607
+ import path32 from "path";
13109
13608
  import ts2 from "typescript";
13110
- import { Command as Command9, Option as Option9 } from "clipanion";
13609
+ import { Command as Command11, Option as Option11 } from "clipanion";
13111
13610
  function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
13112
13611
  const tier2 = [];
13113
13612
  const tier1 = [];
@@ -13204,7 +13703,7 @@ function scanDirectoryForConsumers(rootDir, eventType) {
13204
13703
  const tier1 = [];
13205
13704
  let hasEventFlowImport = false;
13206
13705
  for (const filePath of files) {
13207
- const text2 = fs17.readFileSync(filePath, "utf8");
13706
+ const text2 = fs20.readFileSync(filePath, "utf8");
13208
13707
  const sourceFile = ts2.createSourceFile(
13209
13708
  filePath,
13210
13709
  text2,
@@ -13241,7 +13740,7 @@ function suggestEventTypes(target, known, limit = 3) {
13241
13740
  return known.map((t) => ({ t, d: levenshtein(target, t) })).sort((a, b) => a.d - b.d).slice(0, limit).map((x) => x.t);
13242
13741
  }
13243
13742
  function renderConsumerReport(result, cwd) {
13244
- const rel = (p) => path29.relative(cwd, p) || p;
13743
+ const rel2 = (p) => path32.relative(cwd, p) || p;
13245
13744
  const lines = [];
13246
13745
  const total = result.tier3.length + result.tier2.length + result.tier1.length;
13247
13746
  lines.push(`Event: ${result.eventType}`);
@@ -13255,7 +13754,7 @@ function renderConsumerReport(result, cwd) {
13255
13754
  const labelWidth = Math.max(...result.tier3.map((h) => h.triggerId.length)) + 2;
13256
13755
  for (const h of result.tier3) {
13257
13756
  const padded = h.triggerId.padEnd(labelWidth);
13258
- lines.push(` - ${padded}(${rel(h.sourceFile)}:${h.sourceLine})`);
13757
+ lines.push(` - ${padded}(${rel2(h.sourceFile)}:${h.sourceLine})`);
13259
13758
  }
13260
13759
  }
13261
13760
  lines.push(`Tier 2 \u2014 Direct invoke via publishAndStart (${result.tier2.length}):`);
@@ -13263,7 +13762,7 @@ function renderConsumerReport(result, cwd) {
13263
13762
  lines.push(" - (none)");
13264
13763
  } else {
13265
13764
  for (const h of result.tier2) {
13266
- lines.push(` - ${rel(h.sourceFile)}:${h.sourceLine}`);
13765
+ lines.push(` - ${rel2(h.sourceFile)}:${h.sourceLine}`);
13267
13766
  }
13268
13767
  }
13269
13768
  lines.push(`Tier 1 \u2014 Subscribers (${result.tier1.length}):`);
@@ -13271,13 +13770,13 @@ function renderConsumerReport(result, cwd) {
13271
13770
  lines.push(" - (none)");
13272
13771
  } else {
13273
13772
  for (const h of result.tier1) {
13274
- lines.push(` - ${h.siteLabel} at ${rel(h.sourceFile)}:${h.sourceLine}`);
13773
+ lines.push(` - ${h.siteLabel} at ${rel2(h.sourceFile)}:${h.sourceLine}`);
13275
13774
  }
13276
13775
  }
13277
13776
  return lines;
13278
13777
  }
13279
13778
  function runConsumersScan(opts) {
13280
- const scanRoot = opts.scanRoot ?? path29.join(opts.cwd, "src");
13779
+ const scanRoot = opts.scanRoot ?? path32.join(opts.cwd, "src");
13281
13780
  const handlersDir = opts.handlersDir ?? scanRoot;
13282
13781
  const allTriggers = scanHandlerFiles(handlersDir);
13283
13782
  const tier3 = allTriggers.filter((t) => t.event === opts.eventType).map((t) => ({
@@ -13286,8 +13785,8 @@ function runConsumersScan(opts) {
13286
13785
  sourceFile: t.sourceFile,
13287
13786
  sourceLine: t.sourceLine
13288
13787
  }));
13289
- const tier21 = fs17.existsSync(scanRoot) ? scanDirectoryForConsumers(scanRoot, opts.eventType) : { tier2: [], tier1: [], hasEventFlowImport: false };
13290
- const eventsGeneratedDir = opts.eventsGeneratedDir ?? path29.join(
13788
+ const tier21 = fs20.existsSync(scanRoot) ? scanDirectoryForConsumers(scanRoot, opts.eventType) : { tier2: [], tier1: [], hasEventFlowImport: false };
13789
+ const eventsGeneratedDir = opts.eventsGeneratedDir ?? path32.join(
13291
13790
  resolveSubsystemsRootFromContext(opts.cwd, opts.config),
13292
13791
  "events",
13293
13792
  "generated"
@@ -13308,15 +13807,15 @@ function runConsumersScan(opts) {
13308
13807
  function resolveSubsystemsRootFromContext(cwd, config) {
13309
13808
  const configured = config?.paths?.subsystems;
13310
13809
  if (typeof configured === "string" && configured.length > 0) {
13311
- return path29.resolve(cwd, configured);
13810
+ return path32.resolve(cwd, configured);
13312
13811
  }
13313
13812
  const backendSrc = config?.paths?.backend_src;
13314
13813
  const base = typeof backendSrc === "string" && backendSrc.length > 0 ? backendSrc : "src";
13315
- return path29.resolve(cwd, base, "shared", "subsystems");
13814
+ return path32.resolve(cwd, base, "shared", "subsystems");
13316
13815
  }
13317
- var EventsConsumersCommand = class extends Command9 {
13816
+ var EventsConsumersCommand = class extends Command11 {
13318
13817
  static paths = [["events", "consumers"]];
13319
- static usage = Command9.Usage({
13818
+ static usage = Command11.Usage({
13320
13819
  description: "List all consumers of an event across the three tiers",
13321
13820
  examples: [
13322
13821
  [
@@ -13325,10 +13824,10 @@ var EventsConsumersCommand = class extends Command9 {
13325
13824
  ]
13326
13825
  ]
13327
13826
  });
13328
- eventType = Option9.String({ required: true });
13329
- json = Option9.Boolean("--json", false);
13330
- cwd = Option9.String("--cwd", { required: false });
13331
- configPath = Option9.String("--config", { required: false });
13827
+ eventType = Option11.String({ required: true });
13828
+ json = Option11.Boolean("--json", false);
13829
+ cwd = Option11.String("--cwd", { required: false });
13830
+ configPath = Option11.String("--config", { required: false });
13332
13831
  async execute() {
13333
13832
  if (this.json) setJsonMode(true);
13334
13833
  const ctx = await loadContext({
@@ -13373,7 +13872,7 @@ var EventsConsumersCommand = class extends Command9 {
13373
13872
  return 0;
13374
13873
  }
13375
13874
  };
13376
- async function summary7(_ctx) {
13875
+ async function summary8(_ctx) {
13377
13876
  return {
13378
13877
  title: "events",
13379
13878
  body: [
@@ -13384,7 +13883,7 @@ async function summary7(_ctx) {
13384
13883
  ]
13385
13884
  };
13386
13885
  }
13387
- async function hints7(_ctx) {
13886
+ async function hints8(_ctx) {
13388
13887
  return [
13389
13888
  {
13390
13889
  command: "codegen events consumers <type>",
@@ -13395,14 +13894,14 @@ async function hints7(_ctx) {
13395
13894
  var eventsNoun = {
13396
13895
  name: "events",
13397
13896
  commandClasses: [EventsConsumersCommand],
13398
- summary: summary7,
13399
- hints: hints7
13897
+ summary: summary8,
13898
+ hints: hints8
13400
13899
  };
13401
13900
  var events_default = eventsNoun;
13402
13901
 
13403
13902
  // src/cli/commands/orchestration.ts
13404
- import path30 from "path";
13405
- import { Command as Command10, Option as Option10 } from "clipanion";
13903
+ import path33 from "path";
13904
+ import { Command as Command12, Option as Option12 } from "clipanion";
13406
13905
  var DEFAULT_PATTERN_GLOBS = ["src/patterns/*.pattern.ts"];
13407
13906
  function resolvePatternGlobs(ctx) {
13408
13907
  const fromConfig = ctx.config?.patterns;
@@ -13415,26 +13914,26 @@ function resolveOrchestrationOutputRoot(ctx) {
13415
13914
  const paths = ctx.config?.paths;
13416
13915
  const explicit = paths?.orchestration_src;
13417
13916
  if (typeof explicit === "string" && explicit.length > 0) {
13418
- return path30.resolve(ctx.cwd, explicit);
13917
+ return path33.resolve(ctx.cwd, explicit);
13419
13918
  }
13420
13919
  const backendSrc = typeof paths?.backend_src === "string" && paths.backend_src.length > 0 ? paths.backend_src : "app/backend/src";
13421
- return path30.resolve(ctx.cwd, backendSrc, "orchestration");
13920
+ return path33.resolve(ctx.cwd, backendSrc, "orchestration");
13422
13921
  }
13423
13922
  async function reloadRegistry(ctx) {
13424
13923
  _resetRegistryForTests({ includeLibrary: false });
13425
13924
  return loadAppPatterns(resolvePatternGlobs(ctx), ctx.cwd);
13426
13925
  }
13427
- var OrchestrationGenCommand = class extends Command10 {
13926
+ var OrchestrationGenCommand = class extends Command12 {
13428
13927
  static paths = [["orchestration", "gen"]];
13429
- static usage = Command10.Usage({
13928
+ static usage = Command12.Usage({
13430
13929
  description: "Emit token / providers / dispatcher / module files per orchestration pattern (ADR-032 Phase 3-2/3)."
13431
13930
  });
13432
- pattern = 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 });
13931
+ pattern = Option12.String("--pattern", { required: false });
13932
+ all = Option12.Boolean("--all", false);
13933
+ dryRun = Option12.Boolean("--dry-run", false);
13934
+ json = Option12.Boolean("--json", false);
13935
+ cwd = Option12.String("--cwd", { required: false });
13936
+ configPath = Option12.String("--config", { required: false });
13438
13937
  async execute() {
13439
13938
  if (this.json) setJsonMode(true);
13440
13939
  const ctx = await loadContext({
@@ -13507,12 +14006,12 @@ var OrchestrationGenCommand = class extends Command10 {
13507
14006
  );
13508
14007
  for (const f of result.files) {
13509
14008
  console.log(
13510
- ` ${theme.muted(icons.arrow)} ${path30.relative(ctx.cwd, f.outputPath)}`
14009
+ ` ${theme.muted(icons.arrow)} ${path33.relative(ctx.cwd, f.outputPath)}`
13511
14010
  );
13512
14011
  }
13513
14012
  } else {
13514
14013
  printSuccess(
13515
- `Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${path30.relative(ctx.cwd, outputRoot)}`
14014
+ `Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${path33.relative(ctx.cwd, outputRoot)}`
13516
14015
  );
13517
14016
  }
13518
14017
  return 0;
@@ -13525,14 +14024,14 @@ var OrchestrationGenCommand = class extends Command10 {
13525
14024
  }
13526
14025
  }
13527
14026
  };
13528
- var OrchestrationListCommand = class extends Command10 {
14027
+ var OrchestrationListCommand = class extends Command12 {
13529
14028
  static paths = [["orchestration", "list"]];
13530
- static usage = Command10.Usage({
14029
+ static usage = Command12.Usage({
13531
14030
  description: "List registered orchestration patterns"
13532
14031
  });
13533
- json = Option10.Boolean("--json", false);
13534
- cwd = Option10.String("--cwd", { required: false });
13535
- configPath = Option10.String("--config", { required: false });
14032
+ json = Option12.Boolean("--json", false);
14033
+ cwd = Option12.String("--cwd", { required: false });
14034
+ configPath = Option12.String("--config", { required: false });
13536
14035
  async execute() {
13537
14036
  if (this.json) setJsonMode(true);
13538
14037
  const ctx = await loadContext({
@@ -13576,14 +14075,14 @@ var OrchestrationListCommand = class extends Command10 {
13576
14075
  return 0;
13577
14076
  }
13578
14077
  };
13579
- var OrchestrationValidateCommand = class extends Command10 {
14078
+ var OrchestrationValidateCommand = class extends Command12 {
13580
14079
  static paths = [["orchestration", "validate"]];
13581
- static usage = Command10.Usage({
14080
+ static usage = Command12.Usage({
13582
14081
  description: "Run the Phase 3-1 project-level orchestration validator (ADR-032)"
13583
14082
  });
13584
- json = Option10.Boolean("--json", false);
13585
- cwd = Option10.String("--cwd", { required: false });
13586
- configPath = Option10.String("--config", { required: false });
14083
+ json = Option12.Boolean("--json", false);
14084
+ cwd = Option12.String("--cwd", { required: false });
14085
+ configPath = Option12.String("--config", { required: false });
13587
14086
  async execute() {
13588
14087
  if (this.json) setJsonMode(true);
13589
14088
  const ctx = await loadContext({
@@ -13619,7 +14118,7 @@ var OrchestrationValidateCommand = class extends Command10 {
13619
14118
  return errors.length === 0 && loadResult.errors.length === 0 ? 0 : 1;
13620
14119
  }
13621
14120
  };
13622
- async function summary8(ctx) {
14121
+ async function summary9(ctx) {
13623
14122
  try {
13624
14123
  await reloadRegistry(ctx);
13625
14124
  } catch {
@@ -13633,7 +14132,7 @@ async function summary8(ctx) {
13633
14132
  ]
13634
14133
  };
13635
14134
  }
13636
- async function hints8(_ctx) {
14135
+ async function hints9(_ctx) {
13637
14136
  return [
13638
14137
  {
13639
14138
  command: "codegen orchestration gen",
@@ -13656,8 +14155,8 @@ var orchestrationNoun = {
13656
14155
  OrchestrationListCommand,
13657
14156
  OrchestrationValidateCommand
13658
14157
  ],
13659
- summary: summary8,
13660
- hints: hints8
14158
+ summary: summary9,
14159
+ hints: hints9
13661
14160
  };
13662
14161
  var orchestration_default = orchestrationNoun;
13663
14162
 
@@ -13668,19 +14167,26 @@ var InitShortcut = class extends ProjectInitCommand {
13668
14167
  };
13669
14168
  var init_default = InitShortcut;
13670
14169
 
14170
+ // src/cli/shortcuts/update.ts
14171
+ var UpdateShortcut = class extends ProjectUpdateCommand {
14172
+ static paths = [["update"]];
14173
+ static usage = ProjectUpdateCommand.usage;
14174
+ };
14175
+ var update_default = UpdateShortcut;
14176
+
13671
14177
  // src/cli/index.ts
13672
14178
  function readVersion() {
13673
14179
  try {
13674
- const pkgPath = join11(import.meta.dirname, "..", "..", "package.json");
14180
+ const pkgPath = join10(import.meta.dirname, "..", "..", "package.json");
13675
14181
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
13676
14182
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
13677
14183
  } catch {
13678
14184
  return "0.0.0";
13679
14185
  }
13680
14186
  }
13681
- var RootSummaryCommand = class extends Command11 {
13682
- static paths = [Command11.Default];
13683
- static usage = Command11.Usage({
14187
+ var RootSummaryCommand = class extends Command13 {
14188
+ static paths = [Command13.Default];
14189
+ static usage = Command13.Usage({
13684
14190
  description: "Show project status and available noun commands"
13685
14191
  });
13686
14192
  async execute() {
@@ -13722,6 +14228,7 @@ var nouns = [
13722
14228
  entity_default,
13723
14229
  subsystem_default,
13724
14230
  project_default,
14231
+ skills_default,
13725
14232
  dev_default,
13726
14233
  relationship_default,
13727
14234
  junction_default,
@@ -13740,6 +14247,7 @@ async function main() {
13740
14247
  cli.register(Builtins.VersionCommand);
13741
14248
  cli.register(RootSummaryCommand);
13742
14249
  cli.register(init_default);
14250
+ cli.register(update_default);
13743
14251
  for (const noun of nouns) {
13744
14252
  for (const CommandClass of noun.commandClasses) {
13745
14253
  cli.register(CommandClass);