@xylabs/ts-scripts-yarn3 7.4.20 → 7.4.22

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 (109) hide show
  1. package/dist/actions/claude-clean.mjs +71 -0
  2. package/dist/actions/claude-clean.mjs.map +1 -0
  3. package/dist/actions/claude-commands.mjs +11 -2
  4. package/dist/actions/claude-commands.mjs.map +1 -1
  5. package/dist/actions/claude-rules.mjs +27 -7
  6. package/dist/actions/claude-rules.mjs.map +1 -1
  7. package/dist/actions/claude-skills.mjs.map +1 -1
  8. package/dist/actions/dupdeps.mjs +3 -2
  9. package/dist/actions/dupdeps.mjs.map +1 -1
  10. package/dist/actions/gitignore.mjs +152 -0
  11. package/dist/actions/gitignore.mjs.map +1 -0
  12. package/dist/actions/index.mjs +392 -206
  13. package/dist/actions/index.mjs.map +1 -1
  14. package/dist/bin/xy.mjs +432 -226
  15. package/dist/bin/xy.mjs.map +1 -1
  16. package/dist/index.d.ts +11 -3
  17. package/dist/index.mjs +495 -282
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/lib/claudeMdTemplate.mjs +5 -1
  20. package/dist/lib/claudeMdTemplate.mjs.map +1 -1
  21. package/dist/lib/gitignoreTemplate.mjs +12 -0
  22. package/dist/lib/gitignoreTemplate.mjs.map +1 -0
  23. package/dist/lib/index.mjs +17 -3
  24. package/dist/lib/index.mjs.map +1 -1
  25. package/dist/xy/build/buildCommand.mjs +161 -0
  26. package/dist/xy/build/buildCommand.mjs.map +1 -0
  27. package/dist/xy/build/compileCommand.mjs +174 -0
  28. package/dist/xy/build/compileCommand.mjs.map +1 -0
  29. package/dist/xy/build/compileOnlyCommand.mjs +175 -0
  30. package/dist/xy/build/compileOnlyCommand.mjs.map +1 -0
  31. package/dist/xy/build/copyAssetsCommand.mjs +84 -0
  32. package/dist/xy/build/copyAssetsCommand.mjs.map +1 -0
  33. package/dist/xy/{build-commands → build}/index.mjs +45 -40
  34. package/dist/xy/build/index.mjs.map +1 -0
  35. package/dist/xy/build/rebuildCommand.mjs +114 -0
  36. package/dist/xy/build/rebuildCommand.mjs.map +1 -0
  37. package/dist/xy/build/recompileCommand.mjs +204 -0
  38. package/dist/xy/build/recompileCommand.mjs.map +1 -0
  39. package/dist/xy/common/claude/cleanCommand.mjs +79 -0
  40. package/dist/xy/common/claude/cleanCommand.mjs.map +1 -0
  41. package/dist/xy/common/claude/commandsCommand.mjs +11 -2
  42. package/dist/xy/common/claude/commandsCommand.mjs.map +1 -1
  43. package/dist/xy/common/claude/index.mjs +171 -69
  44. package/dist/xy/common/claude/index.mjs.map +1 -1
  45. package/dist/xy/common/claude/initCommand.mjs +38 -115
  46. package/dist/xy/common/claude/initCommand.mjs.map +1 -1
  47. package/dist/xy/common/claude/rulesCommand.mjs +27 -7
  48. package/dist/xy/common/claude/rulesCommand.mjs.map +1 -1
  49. package/dist/xy/common/claude/skillsCommand.mjs.map +1 -1
  50. package/dist/xy/common/gitignoreCommand.mjs +158 -0
  51. package/dist/xy/common/gitignoreCommand.mjs.map +1 -0
  52. package/dist/xy/common/index.mjs +304 -109
  53. package/dist/xy/common/index.mjs.map +1 -1
  54. package/dist/xy/index.mjs +432 -226
  55. package/dist/xy/index.mjs.map +1 -1
  56. package/dist/xy/install/dupdepsCommand.mjs +3 -2
  57. package/dist/xy/install/dupdepsCommand.mjs.map +1 -1
  58. package/dist/xy/install/index.mjs +3 -2
  59. package/dist/xy/install/index.mjs.map +1 -1
  60. package/dist/xy/xy.mjs +432 -226
  61. package/dist/xy/xy.mjs.map +1 -1
  62. package/package.json +2 -2
  63. package/templates/claude/CLAUDE-local.md +4 -0
  64. package/templates/claude/commands/xy-dead.md +5 -0
  65. package/templates/claude/{rules/xylabs-dependencies.md → commands/xy-deps.md} +7 -7
  66. package/templates/claude/commands/xy-dupdeps.md +5 -0
  67. package/templates/claude/commands/xy-gen-docs.md +5 -0
  68. package/templates/claude/commands/xy-gitignore.md +5 -0
  69. package/templates/claude/commands/xy-gitlint.md +5 -0
  70. package/templates/claude/commands/xy-license.md +5 -0
  71. package/templates/claude/commands/xy-lint-rules.md +44 -0
  72. package/templates/claude/commands/xy-recompile.md +5 -0
  73. package/templates/claude/commands/xy-reinstall.md +5 -0
  74. package/templates/claude/commands/xy-relint.md +5 -0
  75. package/templates/claude/commands/xy-retest.md +5 -0
  76. package/templates/claude/commands/xy-sonar.md +5 -0
  77. package/templates/claude/commands/xy-up.md +7 -0
  78. package/templates/claude/rules/xylabs-architecture.md +4 -7
  79. package/templates/claude/rules/xylabs-build.md +6 -11
  80. package/templates/claude/rules/xylabs-git-workflow.md +3 -5
  81. package/templates/claude/rules/xylabs-naming.md +4 -7
  82. package/templates/claude/rules/xylabs-style.md +14 -19
  83. package/templates/claude/skills/xylabs-e2e-setup/SKILL.md +16 -2
  84. package/templates/gitignore/template.gitignore +40 -0
  85. package/dist/actions/gitignore-gen.mjs +0 -88
  86. package/dist/actions/gitignore-gen.mjs.map +0 -1
  87. package/dist/xy/build-commands/build.mjs +0 -502
  88. package/dist/xy/build-commands/build.mjs.map +0 -1
  89. package/dist/xy/build-commands/index.mjs.map +0 -1
  90. package/dist/xy/common/gitignoreGenCommand.mjs +0 -98
  91. package/dist/xy/common/gitignoreGenCommand.mjs.map +0 -1
  92. package/templates/claude/commands/xylabs-deploy-major.md +0 -7
  93. package/templates/claude/commands/xylabs-deploy-minor.md +0 -7
  94. package/templates/claude/commands/xylabs-deploy.md +0 -7
  95. package/templates/claude/rules/xylabs-error-handling.md +0 -7
  96. package/templates/claude/rules/xylabs-frameworks.md +0 -8
  97. package/templates/claude/rules/xylabs-linting.md +0 -55
  98. package/templates/claude/rules/xylabs-typescript.md +0 -11
  99. /package/templates/claude/commands/{xylabs-build.md → xy-build.md} +0 -0
  100. /package/templates/claude/commands/{xylabs-clean.md → xy-clean.md} +0 -0
  101. /package/templates/claude/commands/{xylabs-compile.md → xy-compile.md} +0 -0
  102. /package/templates/claude/commands/{xylabs-cycle.md → xy-cycle.md} +0 -0
  103. /package/templates/claude/commands/{xylabs-deplint.md → xy-deplint.md} +0 -0
  104. /package/templates/claude/commands/{xylabs-fix.md → xy-fix.md} +0 -0
  105. /package/templates/claude/commands/{xylabs-knip.md → xy-knip.md} +0 -0
  106. /package/templates/claude/commands/{xylabs-lint.md → xy-lint.md} +0 -0
  107. /package/templates/claude/commands/{xylabs-publint.md → xy-publint.md} +0 -0
  108. /package/templates/claude/commands/{xylabs-rebuild.md → xy-rebuild.md} +0 -0
  109. /package/templates/claude/commands/{xylabs-test.md → xy-test.md} +0 -0
@@ -23,7 +23,8 @@ var require2 = createRequire(import.meta.url);
23
23
  var packageRoot = PATH.dirname(require2.resolve("@xylabs/ts-scripts-yarn3/package.json"));
24
24
  var templatesDir = PATH.resolve(packageRoot, "templates", "claude");
25
25
  var XYLABS_RULES_PREFIX = "xylabs-";
26
- var XYLABS_COMMANDS_PREFIX = "xylabs-";
26
+ var XYLABS_COMMANDS_PREFIX = "xy-";
27
+ var LEGACY_COMMANDS_PREFIX = "xylabs-";
27
28
  var XYLABS_SKILLS_PREFIX = "xylabs-";
28
29
  var claudeMdRuleTemplates = () => {
29
30
  const rulesDir = PATH.resolve(templatesDir, "rules");
@@ -61,7 +62,7 @@ var claudeSkillTemplates = () => {
61
62
  }
62
63
  return result;
63
64
  };
64
- var claudeMdProjectTemplate = () => readFileSync(PATH.resolve(templatesDir, "CLAUDE-project.md"), "utf8");
65
+ var claudeMdLocalTemplate = () => readFileSync(PATH.resolve(templatesDir, "CLAUDE-local.md"), "utf8");
65
66
 
66
67
  // src/lib/deleteGlob.ts
67
68
  import fs from "fs";
@@ -195,19 +196,19 @@ var INIT_CWD = () => {
195
196
  // src/lib/generateIgnoreFiles.ts
196
197
  var localeCompare = (a, b) => a.localeCompare(b);
197
198
  var mergeEntries = (a, b) => [...union(a, b)].toSorted(localeCompare);
198
- var generateIgnoreFiles = (filename3, pkg) => {
199
- console.log(chalk3.green(`Generate ${filename3} Files`));
199
+ var generateIgnoreFiles = (filename2, pkg) => {
200
+ console.log(chalk3.green(`Generate ${filename2} Files`));
200
201
  const cwd = INIT_CWD() ?? ".";
201
202
  const workspaces = pkg ? [yarnWorkspace(pkg)] : yarnWorkspaces();
202
- const readEntries = (location) => readNonEmptyLines(`${location}/${filename3}`);
203
- const writeEntries = (location, entries) => writeLines(`${location}/${filename3}`, entries);
203
+ const readEntries = (location) => readNonEmptyLines(`${location}/${filename2}`);
204
+ const writeEntries = (location, entries) => writeLines(`${location}/${filename2}`, entries);
204
205
  const results = workspaces.map(({ location, name }) => {
205
206
  try {
206
207
  writeEntries(location, mergeEntries(readEntries(cwd), readEntries(location)));
207
208
  return 0;
208
209
  } catch (ex) {
209
210
  const error = ex;
210
- console.error(`Generate ${filename3} Files [${name}] [${error.message}]`);
211
+ console.error(`Generate ${filename2} Files [${name}] [${error.message}]`);
211
212
  return 1;
212
213
  }
213
214
  });
@@ -437,6 +438,15 @@ async function generateReadmeFiles({
437
438
  return failed ? 1 : 0;
438
439
  }
439
440
 
441
+ // src/lib/gitignoreTemplate.ts
442
+ import { readFileSync as readFileSync4 } from "fs";
443
+ import { createRequire as createRequire3 } from "module";
444
+ import PATH3 from "path";
445
+ var require4 = createRequire3(import.meta.url);
446
+ var packageRoot3 = PATH3.dirname(require4.resolve("@xylabs/ts-scripts-yarn3/package.json"));
447
+ var templatesDir2 = PATH3.resolve(packageRoot3, "templates", "gitignore");
448
+ var gitignoreTemplate = () => readFileSync4(PATH3.resolve(templatesDir2, "template.gitignore"), "utf8");
449
+
440
450
  // src/lib/loadConfig.ts
441
451
  import chalk5 from "chalk";
442
452
  import { cosmiconfig } from "cosmiconfig";
@@ -489,25 +499,85 @@ var runSteps = (name, steps, exitOnFail = true, messages) => {
489
499
  }, !!exitOnFail);
490
500
  };
491
501
 
492
- // src/actions/claude-commands.ts
502
+ // src/actions/claude-clean.ts
493
503
  import {
494
504
  existsSync as existsSync3,
495
- mkdirSync,
496
505
  readdirSync as readdirSync2,
497
- readFileSync as readFileSync4,
498
- unlinkSync,
499
- writeFileSync as writeFileSync2
506
+ rmSync,
507
+ unlinkSync
500
508
  } from "fs";
501
- import PATH3 from "path";
509
+ import PATH4 from "path";
502
510
  import chalk7 from "chalk";
511
+ function removeFile(filePath, label) {
512
+ if (existsSync3(filePath)) {
513
+ unlinkSync(filePath);
514
+ console.log(chalk7.yellow(` Removed ${label}`));
515
+ return true;
516
+ }
517
+ return false;
518
+ }
519
+ function removeDir(dirPath, label) {
520
+ if (existsSync3(dirPath)) {
521
+ rmSync(dirPath, { recursive: true });
522
+ console.log(chalk7.yellow(` Removed ${label}`));
523
+ return true;
524
+ }
525
+ return false;
526
+ }
527
+ function claudeClean() {
528
+ console.log(chalk7.green("Clean Claude configuration"));
529
+ const cwd = INIT_CWD() ?? process.cwd();
530
+ let removed = 0;
531
+ const rootFiles = ["CLAUDE.md", "CLAUDE.local.md"];
532
+ for (const file of rootFiles) {
533
+ if (removeFile(PATH4.resolve(cwd, file), file)) removed++;
534
+ }
535
+ if (removeDir(PATH4.resolve(cwd, ".claude"), ".claude/")) removed++;
536
+ const packagesDir = PATH4.resolve(cwd, "packages");
537
+ if (existsSync3(packagesDir)) {
538
+ const findClaudeFiles = (dir, prefix) => {
539
+ const entries = readdirSync2(dir, { withFileTypes: true });
540
+ for (const entry of entries) {
541
+ const fullPath = PATH4.resolve(dir, entry.name);
542
+ const label = `${prefix}${entry.name}`;
543
+ if (entry.isFile() && (entry.name === "CLAUDE.md" || entry.name === "CLAUDE.local.md")) {
544
+ if (removeFile(fullPath, label)) removed++;
545
+ } else if (entry.isDirectory() && entry.name === ".claude") {
546
+ if (removeDir(fullPath, `${label}/`)) removed++;
547
+ } else if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist") {
548
+ findClaudeFiles(fullPath, `${label}/`);
549
+ }
550
+ }
551
+ };
552
+ findClaudeFiles(packagesDir, "packages/");
553
+ }
554
+ if (removed > 0) {
555
+ console.log(chalk7.green(` Removed ${removed} item(s)`));
556
+ } else {
557
+ console.log(chalk7.gray(" Nothing to clean"));
558
+ }
559
+ return 0;
560
+ }
561
+
562
+ // src/actions/claude-commands.ts
563
+ import {
564
+ existsSync as existsSync4,
565
+ mkdirSync,
566
+ readdirSync as readdirSync3,
567
+ readFileSync as readFileSync5,
568
+ unlinkSync as unlinkSync2,
569
+ writeFileSync as writeFileSync2
570
+ } from "fs";
571
+ import PATH5 from "path";
572
+ import chalk8 from "chalk";
503
573
  var syncCommandFiles = (commandsDir) => {
504
574
  const templates = claudeCommandTemplates();
505
575
  const templateNames = new Set(Object.keys(templates));
506
576
  let updated = 0;
507
577
  let created = 0;
508
- for (const [filename3, content] of Object.entries(templates)) {
509
- const targetPath = PATH3.resolve(commandsDir, filename3);
510
- const existing = existsSync3(targetPath) ? readFileSync4(targetPath, "utf8") : void 0;
578
+ for (const [filename2, content] of Object.entries(templates)) {
579
+ const targetPath = PATH5.resolve(commandsDir, filename2);
580
+ const existing = existsSync4(targetPath) ? readFileSync5(targetPath, "utf8") : void 0;
511
581
  if (existing === content) continue;
512
582
  writeFileSync2(targetPath, content, "utf8");
513
583
  if (existing) {
@@ -523,16 +593,23 @@ var syncCommandFiles = (commandsDir) => {
523
593
  };
524
594
  };
525
595
  var removeStaleCommands = (commandsDir, templateNames) => {
526
- const existingCommands = readdirSync2(commandsDir).filter((f) => f.startsWith(XYLABS_COMMANDS_PREFIX) && f.endsWith(".md"));
596
+ const existingCommands = readdirSync3(commandsDir).filter((f) => f.startsWith(XYLABS_COMMANDS_PREFIX) && f.endsWith(".md"));
527
597
  let removed = 0;
528
598
  for (const file of existingCommands) {
529
599
  if (!templateNames.has(file)) {
530
- unlinkSync(PATH3.resolve(commandsDir, file));
600
+ unlinkSync2(PATH5.resolve(commandsDir, file));
531
601
  removed++;
532
602
  }
533
603
  }
534
604
  return removed;
535
605
  };
606
+ var removeLegacyCommands = (commandsDir) => {
607
+ const legacyFiles = readdirSync3(commandsDir).filter((f) => f.startsWith(LEGACY_COMMANDS_PREFIX) && f.endsWith(".md"));
608
+ for (const file of legacyFiles) {
609
+ unlinkSync2(PATH5.resolve(commandsDir, file));
610
+ }
611
+ return legacyFiles.length;
612
+ };
536
613
  var logCommandsResult = (created, updated, removed) => {
537
614
  if (created || updated || removed) {
538
615
  const parts = [
@@ -540,44 +617,46 @@ var logCommandsResult = (created, updated, removed) => {
540
617
  updated ? `${updated} updated` : "",
541
618
  removed ? `${removed} removed` : ""
542
619
  ].filter(Boolean);
543
- console.log(chalk7.green(`.claude/commands/${XYLABS_COMMANDS_PREFIX}*.md: ${parts.join(", ")}`));
620
+ console.log(chalk8.green(`.claude/commands/${XYLABS_COMMANDS_PREFIX}*.md: ${parts.join(", ")}`));
544
621
  } else {
545
- console.log(chalk7.gray(`.claude/commands/${XYLABS_COMMANDS_PREFIX}*.md: already up to date`));
622
+ console.log(chalk8.gray(`.claude/commands/${XYLABS_COMMANDS_PREFIX}*.md: already up to date`));
546
623
  }
547
624
  };
548
625
  var claudeCommands = () => {
549
626
  const cwd = INIT_CWD() ?? process.cwd();
550
- const commandsDir = PATH3.resolve(cwd, ".claude", "commands");
627
+ const commandsDir = PATH5.resolve(cwd, ".claude", "commands");
551
628
  mkdirSync(commandsDir, { recursive: true });
629
+ const legacy = removeLegacyCommands(commandsDir);
552
630
  const {
553
631
  created,
554
632
  templateNames,
555
633
  updated
556
634
  } = syncCommandFiles(commandsDir);
557
635
  const removed = removeStaleCommands(commandsDir, templateNames);
558
- logCommandsResult(created, updated, removed);
636
+ logCommandsResult(created, updated, removed + legacy);
559
637
  return 0;
560
638
  };
561
639
 
562
640
  // src/actions/claude-rules.ts
641
+ import { spawnSync as spawnSync4 } from "child_process";
563
642
  import {
564
- existsSync as existsSync4,
643
+ existsSync as existsSync5,
565
644
  mkdirSync as mkdirSync2,
566
- readdirSync as readdirSync3,
567
- readFileSync as readFileSync5,
568
- unlinkSync as unlinkSync2,
645
+ readdirSync as readdirSync4,
646
+ readFileSync as readFileSync6,
647
+ unlinkSync as unlinkSync3,
569
648
  writeFileSync as writeFileSync3
570
649
  } from "fs";
571
- import PATH4 from "path";
572
- import chalk8 from "chalk";
650
+ import PATH6 from "path";
651
+ import chalk9 from "chalk";
573
652
  var syncRuleFiles = (rulesDir) => {
574
653
  const templates = claudeMdRuleTemplates();
575
654
  const templateNames = new Set(Object.keys(templates));
576
655
  let updated = 0;
577
656
  let created = 0;
578
- for (const [filename3, content] of Object.entries(templates)) {
579
- const targetPath = PATH4.resolve(rulesDir, filename3);
580
- const existing = existsSync4(targetPath) ? readFileSync5(targetPath, "utf8") : void 0;
657
+ for (const [filename2, content] of Object.entries(templates)) {
658
+ const targetPath = PATH6.resolve(rulesDir, filename2);
659
+ const existing = existsSync5(targetPath) ? readFileSync6(targetPath, "utf8") : void 0;
581
660
  if (existing === content) continue;
582
661
  writeFileSync3(targetPath, content, "utf8");
583
662
  if (existing) {
@@ -593,11 +672,11 @@ var syncRuleFiles = (rulesDir) => {
593
672
  };
594
673
  };
595
674
  var removeStaleRules = (rulesDir, templateNames) => {
596
- const existingRules = readdirSync3(rulesDir).filter((f) => f.startsWith(XYLABS_RULES_PREFIX) && f.endsWith(".md"));
675
+ const existingRules = readdirSync4(rulesDir).filter((f) => f.startsWith(XYLABS_RULES_PREFIX) && f.endsWith(".md"));
597
676
  let removed = 0;
598
677
  for (const file of existingRules) {
599
678
  if (!templateNames.has(file)) {
600
- unlinkSync2(PATH4.resolve(rulesDir, file));
679
+ unlinkSync3(PATH6.resolve(rulesDir, file));
601
680
  removed++;
602
681
  }
603
682
  }
@@ -610,26 +689,44 @@ var logRulesResult = (created, updated, removed) => {
610
689
  updated ? `${updated} updated` : "",
611
690
  removed ? `${removed} removed` : ""
612
691
  ].filter(Boolean);
613
- console.log(chalk8.green(`.claude/rules/${XYLABS_RULES_PREFIX}*.md: ${parts.join(", ")}`));
692
+ console.log(chalk9.green(`.claude/rules/${XYLABS_RULES_PREFIX}*.md: ${parts.join(", ")}`));
614
693
  } else {
615
- console.log(chalk8.gray(`.claude/rules/${XYLABS_RULES_PREFIX}*.md: already up to date`));
694
+ console.log(chalk9.gray(`.claude/rules/${XYLABS_RULES_PREFIX}*.md: already up to date`));
616
695
  }
617
696
  };
618
697
  var ensureProjectClaudeMd = (cwd, force) => {
619
- const projectPath = PATH4.resolve(cwd, "CLAUDE.md");
620
- if (!existsSync4(projectPath) || force) {
621
- if (force && existsSync4(projectPath)) {
622
- console.log(chalk8.yellow("Overwriting existing CLAUDE.md"));
698
+ const projectPath = PATH6.resolve(cwd, "CLAUDE.md");
699
+ if (!existsSync5(projectPath) || force) {
700
+ if (force && existsSync5(projectPath)) {
701
+ console.log(chalk9.yellow("Regenerating CLAUDE.md"));
702
+ }
703
+ console.log(chalk9.green("Generating CLAUDE.md via claude /init..."));
704
+ const result = spawnSync4("claude", ["-p", "/init", "--allowedTools", "Read", "Write", "Glob", "Grep"], {
705
+ cwd,
706
+ shell: true,
707
+ stdio: "inherit"
708
+ });
709
+ if (result.status !== 0) {
710
+ console.error(chalk9.red("claude /init failed \u2014 is Claude Code installed?"));
711
+ return 1;
623
712
  }
624
- writeFileSync3(projectPath, claudeMdProjectTemplate(), "utf8");
625
- console.log(chalk8.green("Generated CLAUDE.md"));
626
713
  } else {
627
- console.log(chalk8.gray("CLAUDE.md already exists (skipped)"));
714
+ console.log(chalk9.gray("CLAUDE.md already exists (skipped, use --force to regenerate)"));
715
+ }
716
+ return 0;
717
+ };
718
+ var ensureLocalClaudeMd = (cwd) => {
719
+ const localPath = PATH6.resolve(cwd, "CLAUDE.local.md");
720
+ if (existsSync5(localPath)) {
721
+ console.log(chalk9.gray("CLAUDE.local.md already exists (skipped)"));
722
+ } else {
723
+ writeFileSync3(localPath, claudeMdLocalTemplate(), "utf8");
724
+ console.log(chalk9.green("Generated CLAUDE.local.md"));
628
725
  }
629
726
  };
630
727
  var claudeRules = ({ force } = {}) => {
631
728
  const cwd = INIT_CWD() ?? process.cwd();
632
- const rulesDir = PATH4.resolve(cwd, ".claude", "rules");
729
+ const rulesDir = PATH6.resolve(cwd, ".claude", "rules");
633
730
  mkdirSync2(rulesDir, { recursive: true });
634
731
  const {
635
732
  created,
@@ -638,19 +735,20 @@ var claudeRules = ({ force } = {}) => {
638
735
  } = syncRuleFiles(rulesDir);
639
736
  const removed = removeStaleRules(rulesDir, templateNames);
640
737
  logRulesResult(created, updated, removed);
641
- ensureProjectClaudeMd(cwd, force);
642
- return 0;
738
+ const claudeMdResult = ensureProjectClaudeMd(cwd, force);
739
+ ensureLocalClaudeMd(cwd);
740
+ return claudeMdResult ?? 0;
643
741
  };
644
742
 
645
743
  // src/actions/claude-settings.ts
646
744
  import {
647
- existsSync as existsSync5,
745
+ existsSync as existsSync6,
648
746
  mkdirSync as mkdirSync3,
649
747
  writeFileSync as writeFileSync4
650
748
  } from "fs";
651
- import PATH5 from "path";
749
+ import PATH7 from "path";
652
750
  import { createInterface as createInterface2 } from "readline";
653
- import chalk9 from "chalk";
751
+ import chalk10 from "chalk";
654
752
  var DEFAULT_SETTINGS = {
655
753
  permissions: {
656
754
  allow: [
@@ -695,48 +793,48 @@ function askConfirmation2(question) {
695
793
  }
696
794
  async function claudeSettings() {
697
795
  const cwd = INIT_CWD() ?? process.cwd();
698
- const claudeDir = PATH5.resolve(cwd, ".claude");
699
- const settingsPath = PATH5.resolve(claudeDir, "settings.local.json");
796
+ const claudeDir = PATH7.resolve(cwd, ".claude");
797
+ const settingsPath = PATH7.resolve(claudeDir, "settings.local.json");
700
798
  mkdirSync3(claudeDir, { recursive: true });
701
- if (existsSync5(settingsPath)) {
799
+ if (existsSync6(settingsPath)) {
702
800
  const confirmed = await askConfirmation2(
703
- chalk9.yellow(`${settingsPath} already exists. Replace it? (y/N) `)
801
+ chalk10.yellow(`${settingsPath} already exists. Replace it? (y/N) `)
704
802
  );
705
803
  if (!confirmed) {
706
- console.log(chalk9.gray("Skipped \u2014 existing settings.local.json preserved"));
804
+ console.log(chalk10.gray("Skipped \u2014 existing settings.local.json preserved"));
707
805
  return 0;
708
806
  }
709
807
  }
710
808
  writeFileSync4(settingsPath, `${JSON.stringify(DEFAULT_SETTINGS, null, 2)}
711
809
  `, "utf8");
712
- console.log(chalk9.green("Generated .claude/settings.local.json"));
810
+ console.log(chalk10.green("Generated .claude/settings.local.json"));
713
811
  return 0;
714
812
  }
715
813
 
716
814
  // src/actions/claude-skills.ts
717
815
  import {
718
- existsSync as existsSync6,
816
+ existsSync as existsSync7,
719
817
  mkdirSync as mkdirSync4,
720
- readdirSync as readdirSync4,
721
- readFileSync as readFileSync6,
722
- rmSync,
818
+ readdirSync as readdirSync5,
819
+ readFileSync as readFileSync7,
820
+ rmSync as rmSync2,
723
821
  statSync as statSync2,
724
822
  writeFileSync as writeFileSync5
725
823
  } from "fs";
726
- import PATH6 from "path";
727
- import chalk10 from "chalk";
824
+ import PATH8 from "path";
825
+ import chalk11 from "chalk";
728
826
  var syncSkillFiles = (skillsDir) => {
729
827
  const templates = claudeSkillTemplates();
730
828
  const templateNames = new Set(Object.keys(templates));
731
829
  let updated = 0;
732
830
  let created = 0;
733
831
  for (const [skillName, files] of Object.entries(templates)) {
734
- const skillDir = PATH6.resolve(skillsDir, skillName);
832
+ const skillDir = PATH8.resolve(skillsDir, skillName);
735
833
  mkdirSync4(skillDir, { recursive: true });
736
- for (const [filename3, content] of Object.entries(files)) {
737
- const targetPath = PATH6.resolve(skillDir, filename3);
738
- mkdirSync4(PATH6.dirname(targetPath), { recursive: true });
739
- const existing = existsSync6(targetPath) ? readFileSync6(targetPath, "utf8") : void 0;
834
+ for (const [filename2, content] of Object.entries(files)) {
835
+ const targetPath = PATH8.resolve(skillDir, filename2);
836
+ mkdirSync4(PATH8.dirname(targetPath), { recursive: true });
837
+ const existing = existsSync7(targetPath) ? readFileSync7(targetPath, "utf8") : void 0;
740
838
  if (existing === content) continue;
741
839
  writeFileSync5(targetPath, content, "utf8");
742
840
  if (existing) {
@@ -753,13 +851,13 @@ var syncSkillFiles = (skillsDir) => {
753
851
  };
754
852
  };
755
853
  var removeStaleSkills = (skillsDir, templateNames) => {
756
- const existingSkills = readdirSync4(skillsDir).filter(
757
- (f) => f.startsWith(XYLABS_SKILLS_PREFIX) && statSync2(PATH6.resolve(skillsDir, f)).isDirectory()
854
+ const existingSkills = readdirSync5(skillsDir).filter(
855
+ (f) => f.startsWith(XYLABS_SKILLS_PREFIX) && statSync2(PATH8.resolve(skillsDir, f)).isDirectory()
758
856
  );
759
857
  let removed = 0;
760
858
  for (const dir of existingSkills) {
761
859
  if (!templateNames.has(dir)) {
762
- rmSync(PATH6.resolve(skillsDir, dir), { recursive: true });
860
+ rmSync2(PATH8.resolve(skillsDir, dir), { recursive: true });
763
861
  removed++;
764
862
  }
765
863
  }
@@ -772,14 +870,14 @@ var logSkillsResult = (created, updated, removed) => {
772
870
  updated ? `${updated} updated` : "",
773
871
  removed ? `${removed} removed` : ""
774
872
  ].filter(Boolean);
775
- console.log(chalk10.green(`.claude/skills/${XYLABS_SKILLS_PREFIX}*/: ${parts.join(", ")}`));
873
+ console.log(chalk11.green(`.claude/skills/${XYLABS_SKILLS_PREFIX}*/: ${parts.join(", ")}`));
776
874
  } else {
777
- console.log(chalk10.gray(`.claude/skills/${XYLABS_SKILLS_PREFIX}*/: already up to date`));
875
+ console.log(chalk11.gray(`.claude/skills/${XYLABS_SKILLS_PREFIX}*/: already up to date`));
778
876
  }
779
877
  };
780
878
  var claudeSkills = () => {
781
879
  const cwd = INIT_CWD() ?? process.cwd();
782
- const skillsDir = PATH6.resolve(cwd, ".claude", "skills");
880
+ const skillsDir = PATH8.resolve(cwd, ".claude", "skills");
783
881
  mkdirSync4(skillsDir, { recursive: true });
784
882
  const {
785
883
  created,
@@ -793,10 +891,10 @@ var claudeSkills = () => {
793
891
 
794
892
  // src/actions/clean-docs.ts
795
893
  import path from "path";
796
- import chalk11 from "chalk";
894
+ import chalk12 from "chalk";
797
895
  var cleanDocs = () => {
798
896
  const pkgName = process.env.npm_package_name;
799
- console.log(chalk11.green(`Cleaning Docs [${pkgName}]`));
897
+ console.log(chalk12.green(`Cleaning Docs [${pkgName}]`));
800
898
  for (const { location } of yarnWorkspaces()) deleteGlob(path.join(location, "docs"));
801
899
  return 0;
802
900
  };
@@ -820,12 +918,96 @@ var genDocsAll = ({ incremental }) => {
820
918
  return runSteps(`GenDocs [All${incremental ? "-Incremental" : ""}]`, [...steps]);
821
919
  };
822
920
 
823
- // src/actions/gitignore-gen.ts
824
- var filename = ".gitignore";
825
- var gitignoreGen = (pkg) => generateIgnoreFiles(filename, pkg);
921
+ // src/actions/gitignore.ts
922
+ import { unlinkSync as unlinkSync4 } from "fs";
923
+ import chalk13 from "chalk";
924
+ var COMMENT_PREFIX = "#";
925
+ var isComment = (line) => line.startsWith(COMMENT_PREFIX);
926
+ var isNegation = (line) => line.startsWith("!");
927
+ function parseTemplateSections(lines) {
928
+ const sections = [];
929
+ let current = [];
930
+ for (const line of lines) {
931
+ if (isComment(line)) {
932
+ if (current.length > 0) {
933
+ sections.push(current);
934
+ }
935
+ current = [line];
936
+ } else {
937
+ current.push(line);
938
+ }
939
+ }
940
+ if (current.length > 0) {
941
+ sections.push(current);
942
+ }
943
+ return sections;
944
+ }
945
+ function mergeWithTemplate(existing, templateContent) {
946
+ const templateLines = templateContent.split("\n").filter((line) => line.trim().length > 0);
947
+ const sections = parseTemplateSections(templateLines);
948
+ const existingEntries = new Set(existing.filter((line) => !isComment(line)));
949
+ const templateEntries = new Set(templateLines.filter((line) => !isComment(line)));
950
+ const customEntries = [...existingEntries].filter((entry) => !templateEntries.has(entry));
951
+ const result = [];
952
+ for (const section of sections) {
953
+ for (const line of section) {
954
+ result.push(line);
955
+ }
956
+ result.push("");
957
+ }
958
+ if (customEntries.length > 0) {
959
+ result.push("# Custom");
960
+ const sorted = [...union(customEntries, [])].toSorted((a, b) => {
961
+ if (isNegation(a) && !isNegation(b)) return 1;
962
+ if (!isNegation(a) && isNegation(b)) return -1;
963
+ return a.localeCompare(b);
964
+ });
965
+ for (const entry of sorted) {
966
+ result.push(entry);
967
+ }
968
+ result.push("");
969
+ }
970
+ return result;
971
+ }
972
+ function removePackageGitignores(cwd) {
973
+ let removed = 0;
974
+ const workspaces = yarnWorkspaces();
975
+ for (const { location } of workspaces) {
976
+ if (location === ".") continue;
977
+ const filePath = `${cwd}/${location}/.gitignore`;
978
+ try {
979
+ unlinkSync4(filePath);
980
+ console.log(chalk13.yellow(` Removed ${location}/.gitignore`));
981
+ removed++;
982
+ } catch {
983
+ }
984
+ }
985
+ return removed;
986
+ }
987
+ function gitignore() {
988
+ console.log(chalk13.green("Generate .gitignore"));
989
+ const cwd = INIT_CWD() ?? ".";
990
+ const gitignorePath = `${cwd}/.gitignore`;
991
+ try {
992
+ const templateContent = gitignoreTemplate();
993
+ const existing = readNonEmptyLines(gitignorePath);
994
+ const merged = mergeWithTemplate(existing, templateContent);
995
+ writeLines(gitignorePath, merged);
996
+ console.log(chalk13.green(" Root .gitignore updated"));
997
+ const removed = removePackageGitignores(cwd);
998
+ if (removed > 0) {
999
+ console.log(chalk13.green(` Removed ${removed} package .gitignore file(s)`));
1000
+ }
1001
+ return 0;
1002
+ } catch (ex) {
1003
+ const error = ex;
1004
+ console.error(chalk13.red(`Generate .gitignore failed: ${error.message}`));
1005
+ return 1;
1006
+ }
1007
+ }
826
1008
 
827
1009
  // src/actions/gitlint.ts
828
- import chalk12 from "chalk";
1010
+ import chalk14 from "chalk";
829
1011
  import ParseGitConfig from "parse-git-config";
830
1012
  var gitlint = () => {
831
1013
  console.log(`
@@ -836,7 +1018,7 @@ Gitlint Start [${process.cwd()}]
836
1018
  const errors = 0;
837
1019
  const gitConfig = ParseGitConfig.sync();
838
1020
  const warn = (message) => {
839
- console.warn(chalk12.yellow(`Warning: ${message}`));
1021
+ console.warn(chalk14.yellow(`Warning: ${message}`));
840
1022
  warnings++;
841
1023
  };
842
1024
  if (gitConfig.core.ignorecase) {
@@ -856,13 +1038,13 @@ Gitlint Start [${process.cwd()}]
856
1038
  }
857
1039
  const resultMessages = [];
858
1040
  if (valid > 0) {
859
- resultMessages.push(chalk12.green(`Passed: ${valid}`));
1041
+ resultMessages.push(chalk14.green(`Passed: ${valid}`));
860
1042
  }
861
1043
  if (warnings > 0) {
862
- resultMessages.push(chalk12.yellow(`Warnings: ${warnings}`));
1044
+ resultMessages.push(chalk14.yellow(`Warnings: ${warnings}`));
863
1045
  }
864
1046
  if (errors > 0) {
865
- resultMessages.push(chalk12.red(` Errors: ${errors}`));
1047
+ resultMessages.push(chalk14.red(` Errors: ${errors}`));
866
1048
  }
867
1049
  console.warn(`Gitlint Finish [ ${resultMessages.join(" | ")} ]
868
1050
  `);
@@ -871,7 +1053,7 @@ Gitlint Start [${process.cwd()}]
871
1053
 
872
1054
  // src/actions/gitlint-fix.ts
873
1055
  import { execSync as execSync2 } from "child_process";
874
- import chalk13 from "chalk";
1056
+ import chalk15 from "chalk";
875
1057
  import ParseGitConfig2 from "parse-git-config";
876
1058
  var gitlintFix = () => {
877
1059
  console.log(`
@@ -880,21 +1062,21 @@ Gitlint Fix Start [${process.cwd()}]
880
1062
  const gitConfig = ParseGitConfig2.sync();
881
1063
  if (gitConfig.core.ignorecase) {
882
1064
  execSync2("git config core.ignorecase false", { stdio: "inherit" });
883
- console.warn(chalk13.yellow("\nGitlint Fix: Updated core.ignorecase to be false\n"));
1065
+ console.warn(chalk15.yellow("\nGitlint Fix: Updated core.ignorecase to be false\n"));
884
1066
  }
885
1067
  if (gitConfig.core.autocrlf !== false) {
886
1068
  execSync2("git config core.autocrlf false", { stdio: "inherit" });
887
- console.warn(chalk13.yellow("\nGitlint Fix: Updated core.autocrlf to be false\n"));
1069
+ console.warn(chalk15.yellow("\nGitlint Fix: Updated core.autocrlf to be false\n"));
888
1070
  }
889
1071
  if (gitConfig.core.eol !== "lf") {
890
1072
  execSync2("git config core.eol lf", { stdio: "inherit" });
891
- console.warn(chalk13.yellow('\nGitlint Fix: Updated core.eol to be "lf"\n'));
1073
+ console.warn(chalk15.yellow('\nGitlint Fix: Updated core.eol to be "lf"\n'));
892
1074
  }
893
1075
  return 1;
894
1076
  };
895
1077
 
896
1078
  // src/actions/license.ts
897
- import chalk14 from "chalk";
1079
+ import chalk16 from "chalk";
898
1080
  import { init } from "license-checker";
899
1081
  var license = async (pkg) => {
900
1082
  const workspaces = yarnWorkspaces();
@@ -919,18 +1101,18 @@ var license = async (pkg) => {
919
1101
  "LGPL-3.0-or-later",
920
1102
  "Python-2.0"
921
1103
  ]);
922
- console.log(chalk14.green("License Checker"));
1104
+ console.log(chalk16.green("License Checker"));
923
1105
  return (await Promise.all(
924
1106
  workspaceList.map(({ location, name }) => {
925
1107
  return new Promise((resolve) => {
926
1108
  init({ production: true, start: location }, (error, packages) => {
927
1109
  if (error) {
928
- console.error(chalk14.red(`License Checker [${name}] Error`));
929
- console.error(chalk14.gray(error));
1110
+ console.error(chalk16.red(`License Checker [${name}] Error`));
1111
+ console.error(chalk16.gray(error));
930
1112
  console.log("\n");
931
1113
  resolve(1);
932
1114
  } else {
933
- console.log(chalk14.green(`License Checker [${name}]`));
1115
+ console.log(chalk16.green(`License Checker [${name}]`));
934
1116
  let count = 0;
935
1117
  for (const [name2, info] of Object.entries(packages)) {
936
1118
  const licenses = Array.isArray(info.licenses) ? info.licenses : [info.licenses];
@@ -946,7 +1128,7 @@ var license = async (pkg) => {
946
1128
  }
947
1129
  if (!orLicenseFound) {
948
1130
  count++;
949
- console.warn(chalk14.yellow(`${name2}: Package License not allowed [${license2}]`));
1131
+ console.warn(chalk16.yellow(`${name2}: Package License not allowed [${license2}]`));
950
1132
  }
951
1133
  }
952
1134
  }
@@ -961,8 +1143,8 @@ var license = async (pkg) => {
961
1143
  };
962
1144
 
963
1145
  // src/actions/npmignore-gen.ts
964
- var filename2 = ".npmignore";
965
- var npmignoreGen = (pkg) => generateIgnoreFiles(filename2, pkg);
1146
+ var filename = ".npmignore";
1147
+ var npmignoreGen = (pkg) => generateIgnoreFiles(filename, pkg);
966
1148
 
967
1149
  // src/actions/readme-gen.ts
968
1150
  async function readmeGen({
@@ -1028,6 +1210,16 @@ var yarn3Only = () => {
1028
1210
  return 0;
1029
1211
  };
1030
1212
 
1213
+ // src/xy/common/claude/cleanCommand.ts
1214
+ var cleanCommand = {
1215
+ command: "clean",
1216
+ describe: "Remove all Claude configuration files from the repo",
1217
+ handler: (argv) => {
1218
+ if (argv.verbose) console.log("Claude Clean");
1219
+ process.exitCode = claudeClean();
1220
+ }
1221
+ };
1222
+
1031
1223
  // src/xy/common/claude/commandsCommand.ts
1032
1224
  var commandsCommand = {
1033
1225
  command: "commands",
@@ -1058,14 +1250,6 @@ var initCommand = {
1058
1250
  process.exitCode = commandsResult || rulesResult || settingsResult;
1059
1251
  }
1060
1252
  };
1061
- var initClaudeSkillsCommand = {
1062
- command: "init:skills",
1063
- describe: "Initialize Claude skills configuration",
1064
- handler: () => {
1065
- const result = claudeSkills();
1066
- process.exitCode = result;
1067
- }
1068
- };
1069
1253
 
1070
1254
  // src/xy/common/claude/rulesCommand.ts
1071
1255
  var rulesCommand = {
@@ -1095,10 +1279,21 @@ var settingsCommand = {
1095
1279
  }
1096
1280
  };
1097
1281
 
1282
+ // src/xy/common/claude/skillsCommand.ts
1283
+ var skillsCommand = {
1284
+ builder: (yargs) => yargs,
1285
+ command: "skills",
1286
+ describe: "Sync XY Labs standard Claude skills to .claude/skills/",
1287
+ handler: (argv) => {
1288
+ if (argv.verbose) console.log("Claude Skills");
1289
+ process.exitCode = claudeSkills();
1290
+ }
1291
+ };
1292
+
1098
1293
  // src/xy/common/claude/index.ts
1099
1294
  var claudeCommand = {
1100
1295
  builder: (yargs) => {
1101
- return yargs.command(commandsCommand).command(initCommand).command(rulesCommand).command(settingsCommand).command(initClaudeSkillsCommand).demandCommand(1, "Please specify a claude subcommand");
1296
+ return yargs.command(cleanCommand).command(commandsCommand).command(initCommand).command(rulesCommand).command(settingsCommand).command(skillsCommand).demandCommand(1, "Please specify a claude subcommand");
1102
1297
  },
1103
1298
  command: "claude",
1104
1299
  describe: "Claude - Claude Code configuration utilities",
@@ -1147,13 +1342,13 @@ var genDocsCommand = {
1147
1342
  }
1148
1343
  };
1149
1344
 
1150
- // src/xy/common/gitignoreGenCommand.ts
1151
- var gitignoreGenCommand = {
1152
- command: "gitignore-gen",
1153
- describe: "GitIgnore Gen - Generate .gitignore files",
1345
+ // src/xy/common/gitignoreCommand.ts
1346
+ var gitignoreCommand = {
1347
+ command: "gitignore",
1348
+ describe: "GitIgnore - Generate root .gitignore and remove package .gitignore files",
1154
1349
  handler: (argv) => {
1155
- if (argv.verbose) console.log("GitIgnore Gen");
1156
- process.exitCode = gitignoreGen();
1350
+ if (argv.verbose) console.log("GitIgnore");
1351
+ process.exitCode = gitignore();
1157
1352
  }
1158
1353
  };
1159
1354
 
@@ -1300,7 +1495,7 @@ var yarn3OnlyCommand = {
1300
1495
 
1301
1496
  // src/xy/common/index.ts
1302
1497
  var xyCommonCommands = (args) => {
1303
- return args.command(claudeCommand).command(licenseCommand).command(deadCommand).command(genDocsCommand).command(cleanDocsCommand).command(gitlintCommand).command(gitignoreGenCommand).command(npmignoreGenCommand).command(readmeCommand).command(retestCommand).command(testCommand).command(upplugCommand).command(upyarnCommand).command(yarn3OnlyCommand);
1498
+ return args.command(claudeCommand).command(licenseCommand).command(deadCommand).command(genDocsCommand).command(cleanDocsCommand).command(gitlintCommand).command(gitignoreCommand).command(npmignoreGenCommand).command(readmeCommand).command(retestCommand).command(testCommand).command(upplugCommand).command(upyarnCommand).command(yarn3OnlyCommand);
1304
1499
  };
1305
1500
  export {
1306
1501
  xyCommonCommands