oh-my-customcode 0.32.0 → 0.33.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 (85) hide show
  1. package/README.md +9 -7
  2. package/dist/cli/index.js +197 -76
  3. package/dist/index.js +52 -6
  4. package/package.json +1 -1
  5. package/templates/.claude/rules/MUST-agent-design.md +11 -0
  6. package/templates/.claude/rules/MUST-agent-identification.md +4 -4
  7. package/templates/.claude/rules/MUST-agent-teams.md +17 -18
  8. package/templates/.claude/rules/MUST-continuous-improvement.md +3 -3
  9. package/templates/.claude/rules/MUST-orchestrator-coordination.md +24 -42
  10. package/templates/.claude/rules/MUST-parallel-execution.md +18 -40
  11. package/templates/.claude/rules/MUST-sync-verification.md +6 -6
  12. package/templates/.claude/rules/MUST-tool-identification.md +39 -10
  13. package/templates/.claude/rules/SHOULD-ontology-rag-routing.md +49 -0
  14. package/templates/.claude/skills/airflow-best-practices/SKILL.md +1 -0
  15. package/templates/.claude/skills/analysis/SKILL.md +2 -1
  16. package/templates/.claude/skills/audit-agents/SKILL.md +4 -3
  17. package/templates/.claude/skills/aws-best-practices/SKILL.md +1 -0
  18. package/templates/.claude/skills/claude-code-bible/SKILL.md +1 -0
  19. package/templates/.claude/skills/codex-exec/SKILL.md +5 -4
  20. package/templates/.claude/skills/create-agent/SKILL.md +1 -0
  21. package/templates/.claude/skills/cve-triage/SKILL.md +1 -0
  22. package/templates/.claude/skills/dag-orchestration/SKILL.md +1 -0
  23. package/templates/.claude/skills/dbt-best-practices/SKILL.md +1 -0
  24. package/templates/.claude/skills/de-lead-routing/SKILL.md +1 -0
  25. package/templates/.claude/skills/dev-lead-routing/SKILL.md +1 -0
  26. package/templates/.claude/skills/dev-refactor/SKILL.md +1 -0
  27. package/templates/.claude/skills/dev-review/SKILL.md +1 -0
  28. package/templates/.claude/skills/django-best-practices/SKILL.md +1 -0
  29. package/templates/.claude/skills/docker-best-practices/SKILL.md +1 -0
  30. package/templates/.claude/skills/fastapi-best-practices/SKILL.md +1 -0
  31. package/templates/.claude/skills/fix-refs/SKILL.md +1 -0
  32. package/templates/.claude/skills/flutter-best-practices/SKILL.md +1 -0
  33. package/templates/.claude/skills/go-backend-best-practices/SKILL.md +1 -0
  34. package/templates/.claude/skills/go-best-practices/SKILL.md +1 -0
  35. package/templates/.claude/skills/help/SKILL.md +1 -0
  36. package/templates/.claude/skills/intent-detection/SKILL.md +1 -0
  37. package/templates/.claude/skills/java21-best-practices/SKILL.md +305 -0
  38. package/templates/.claude/skills/jinja2-prompts/SKILL.md +1 -0
  39. package/templates/.claude/skills/kafka-best-practices/SKILL.md +1 -0
  40. package/templates/.claude/skills/kotlin-best-practices/SKILL.md +1 -0
  41. package/templates/.claude/skills/lists/SKILL.md +1 -0
  42. package/templates/.claude/skills/memory-management/SKILL.md +1 -0
  43. package/templates/.claude/skills/memory-recall/SKILL.md +1 -0
  44. package/templates/.claude/skills/memory-save/SKILL.md +1 -0
  45. package/templates/.claude/skills/model-escalation/SKILL.md +1 -0
  46. package/templates/.claude/skills/monitoring-setup/SKILL.md +1 -0
  47. package/templates/.claude/skills/multi-model-verification/SKILL.md +1 -0
  48. package/templates/.claude/skills/npm-audit/SKILL.md +1 -0
  49. package/templates/.claude/skills/npm-publish/SKILL.md +1 -0
  50. package/templates/.claude/skills/npm-version/SKILL.md +1 -0
  51. package/templates/.claude/skills/optimize-analyze/SKILL.md +1 -0
  52. package/templates/.claude/skills/optimize-bundle/SKILL.md +1 -0
  53. package/templates/.claude/skills/optimize-report/SKILL.md +1 -0
  54. package/templates/.claude/skills/pipeline-architecture-patterns/SKILL.md +1 -0
  55. package/templates/.claude/skills/pipeline-guards/SKILL.md +2 -0
  56. package/templates/.claude/skills/postgres-best-practices/SKILL.md +1 -0
  57. package/templates/.claude/skills/pr-auto-improve/SKILL.md +1 -0
  58. package/templates/.claude/skills/python-best-practices/SKILL.md +1 -0
  59. package/templates/.claude/skills/qa-lead-routing/SKILL.md +1 -0
  60. package/templates/.claude/skills/react-best-practices/SKILL.md +1 -0
  61. package/templates/.claude/skills/redis-best-practices/SKILL.md +1 -0
  62. package/templates/.claude/skills/research/SKILL.md +1 -0
  63. package/templates/.claude/skills/result-aggregation/SKILL.md +1 -0
  64. package/templates/.claude/skills/rust-best-practices/SKILL.md +1 -0
  65. package/templates/.claude/skills/sauron-watch/SKILL.md +1 -0
  66. package/templates/.claude/skills/secretary-routing/SKILL.md +1 -0
  67. package/templates/.claude/skills/skills-sh-search/SKILL.md +1 -0
  68. package/templates/.claude/skills/snowflake-best-practices/SKILL.md +1 -0
  69. package/templates/.claude/skills/spark-best-practices/SKILL.md +1 -0
  70. package/templates/.claude/skills/springboot-best-practices/SKILL.md +1 -0
  71. package/templates/.claude/skills/status/SKILL.md +1 -0
  72. package/templates/.claude/skills/structured-dev-cycle/SKILL.md +1 -0
  73. package/templates/.claude/skills/stuck-recovery/SKILL.md +1 -0
  74. package/templates/.claude/skills/supabase-postgres-best-practices/SKILL.md +2 -1
  75. package/templates/.claude/skills/task-decomposition/SKILL.md +2 -0
  76. package/templates/.claude/skills/typescript-best-practices/SKILL.md +1 -0
  77. package/templates/.claude/skills/update-docs/SKILL.md +4 -3
  78. package/templates/.claude/skills/update-external/SKILL.md +1 -0
  79. package/templates/.claude/skills/vercel-deploy/SKILL.md +1 -0
  80. package/templates/.claude/skills/web-design-guidelines/SKILL.md +1 -0
  81. package/templates/.claude/skills/worker-reviewer-pipeline/SKILL.md +2 -0
  82. package/templates/.claude/skills/writing-clearly-and-concisely/SKILL.md +3 -2
  83. package/templates/CLAUDE.md.en +6 -5
  84. package/templates/CLAUDE.md.ko +6 -5
  85. package/templates/manifest.json +4 -4
package/dist/cli/index.js CHANGED
@@ -12164,6 +12164,10 @@ var en_default = {
12164
12164
  index: {
12165
12165
  pass: "All index.yaml files are valid",
12166
12166
  fail: "Some index.yaml files are invalid"
12167
+ },
12168
+ framework: {
12169
+ pass: "Framework is up to date (v{{version}})",
12170
+ warn: "Framework is outdated: installed v{{installed}}, latest v{{latest}} ({{behind}} version(s) behind). Run 'omcustom update' to sync."
12167
12171
  }
12168
12172
  }
12169
12173
  },
@@ -12484,6 +12488,10 @@ var ko_default = {
12484
12488
  index: {
12485
12489
  pass: "모든 index.yaml 파일 정상",
12486
12490
  fail: "일부 index.yaml 파일이 잘못됨"
12491
+ },
12492
+ framework: {
12493
+ pass: "프레임워크가 최신 상태입니다 (v{{version}})",
12494
+ warn: "프레임워크가 구버전입니다: 설치됨 v{{installed}}, 최신 v{{latest}} ({{behind}}개 버전 뒤처짐). 'omcustom update'를 실행하여 동기화하세요."
12487
12495
  }
12488
12496
  }
12489
12497
  },
@@ -13275,6 +13283,44 @@ function migrateConfig(config) {
13275
13283
  return migrated;
13276
13284
  }
13277
13285
 
13286
+ // src/core/doctor-framework.ts
13287
+ init_fs();
13288
+ import { readFile } from "node:fs/promises";
13289
+ import { join as join4 } from "node:path";
13290
+ async function getInstalledVersion(targetDir) {
13291
+ const rcPath = join4(targetDir, ".omcustomrc.json");
13292
+ if (!await fileExists(rcPath))
13293
+ return null;
13294
+ try {
13295
+ const content = JSON.parse(await readFile(rcPath, "utf-8"));
13296
+ return content.version ?? null;
13297
+ } catch {
13298
+ return null;
13299
+ }
13300
+ }
13301
+ function calculateVersionsBehind(installed, latest) {
13302
+ const [installedMajor, installedMinor] = installed.split(".").map(Number);
13303
+ const [latestMajor, latestMinor] = latest.split(".").map(Number);
13304
+ if (installedMajor > latestMajor)
13305
+ return 0;
13306
+ if (latestMajor > installedMajor) {
13307
+ return (latestMajor - installedMajor) * 100 + latestMinor;
13308
+ }
13309
+ return Math.max(0, latestMinor - installedMinor);
13310
+ }
13311
+ async function checkFrameworkVersion(targetDir, latestVersion) {
13312
+ const installed = await getInstalledVersion(targetDir);
13313
+ if (!installed)
13314
+ return null;
13315
+ const versionsBehind = calculateVersionsBehind(installed, latestVersion);
13316
+ return {
13317
+ installed,
13318
+ latest: latestVersion,
13319
+ isOutdated: installed !== latestVersion,
13320
+ versionsBehind
13321
+ };
13322
+ }
13323
+
13278
13324
  // src/core/layout.ts
13279
13325
  var CLAUDE_LAYOUT = {
13280
13326
  rootDir: ".claude",
@@ -13756,6 +13802,29 @@ function readCurrentVersion() {
13756
13802
  return "0.0.0";
13757
13803
  }
13758
13804
  }
13805
+ async function checkFrameworkDrift(targetDir, currentVersion) {
13806
+ const result = await checkFrameworkVersion(targetDir, currentVersion);
13807
+ if (!result)
13808
+ return null;
13809
+ if (result.isOutdated) {
13810
+ return {
13811
+ name: "Framework",
13812
+ status: "warn",
13813
+ message: i18n.t("cli.doctor.checks.framework.warn", {
13814
+ installed: result.installed,
13815
+ latest: result.latest,
13816
+ behind: String(result.versionsBehind)
13817
+ }),
13818
+ fixable: false
13819
+ };
13820
+ }
13821
+ return {
13822
+ name: "Framework",
13823
+ status: "pass",
13824
+ message: i18n.t("cli.doctor.checks.framework.pass", { version: result.installed }),
13825
+ fixable: false
13826
+ };
13827
+ }
13759
13828
  function checkUpdateAvailable(currentVersion) {
13760
13829
  const result = checkSelfUpdate({ currentVersion });
13761
13830
  if (!result.checked) {
@@ -13785,11 +13854,7 @@ function checkUpdateAvailable(currentVersion) {
13785
13854
  fixable: false
13786
13855
  };
13787
13856
  }
13788
- async function doctorCommand(options = {}) {
13789
- const targetDir = process.cwd();
13790
- console.log(i18n.t("cli.doctor.checking"));
13791
- console.log("");
13792
- const layout = getProviderLayout();
13857
+ async function runAllChecks(targetDir, layout, packageVersion, includeUpdates) {
13793
13858
  const baseChecks = await Promise.all([
13794
13859
  checkEntryDoc(targetDir, layout.entryFile),
13795
13860
  checkRules(targetDir, layout.rootDir),
@@ -13802,7 +13867,17 @@ async function doctorCommand(options = {}) {
13802
13867
  checkContexts(targetDir, layout.rootDir),
13803
13868
  checkCustomComponents(targetDir, layout.rootDir)
13804
13869
  ]);
13805
- const checksWithUpdate = options.updates ? [...baseChecks, checkUpdateAvailable(readCurrentVersion())] : baseChecks;
13870
+ const frameworkCheck = await checkFrameworkDrift(targetDir, packageVersion);
13871
+ const checksWithFramework = frameworkCheck ? [...baseChecks, frameworkCheck] : baseChecks;
13872
+ return includeUpdates ? [...checksWithFramework, checkUpdateAvailable(packageVersion)] : checksWithFramework;
13873
+ }
13874
+ async function doctorCommand(options = {}) {
13875
+ const targetDir = process.cwd();
13876
+ console.log(i18n.t("cli.doctor.checking"));
13877
+ console.log("");
13878
+ const layout = getProviderLayout();
13879
+ const packageVersion = readCurrentVersion();
13880
+ const checksWithUpdate = await runAllChecks(targetDir, layout, packageVersion, options.updates ?? false);
13806
13881
  let checks = checksWithUpdate;
13807
13882
  if (options.fix) {
13808
13883
  const hasFixableIssues = checksWithUpdate.some((c) => c.status === "fail" && c.fixable);
@@ -13851,16 +13926,22 @@ async function doctorCommand(options = {}) {
13851
13926
  }
13852
13927
 
13853
13928
  // src/cli/init.ts
13854
- import { join as join8 } from "node:path";
13929
+ import { join as join9 } from "node:path";
13855
13930
 
13856
13931
  // src/core/installer.ts
13857
13932
  init_fs();
13858
- import { readFile as fsReadFile, writeFile as fsWriteFile, rename } from "node:fs/promises";
13859
- import { basename as basename2, join as join6 } from "node:path";
13933
+ import {
13934
+ readFile as fsReadFile,
13935
+ writeFile as fsWriteFile,
13936
+ readdir as readdir2,
13937
+ rename,
13938
+ stat as stat2
13939
+ } from "node:fs/promises";
13940
+ import { basename as basename2, join as join7 } from "node:path";
13860
13941
 
13861
13942
  // src/core/file-preservation.ts
13862
13943
  init_fs();
13863
- import { basename, join as join4 } from "node:path";
13944
+ import { basename, join as join5 } from "node:path";
13864
13945
  var DEFAULT_CRITICAL_FILES = ["settings.json", "settings.local.json"];
13865
13946
  var DEFAULT_CRITICAL_DIRECTORIES = ["agent-memory", "agent-memory-local"];
13866
13947
  var PROTECTED_FRAMEWORK_FILES = ["CLAUDE.md", "AGENTS.md"];
@@ -13883,8 +13964,8 @@ function matchesGlobPattern(filePath, pattern) {
13883
13964
  return regex.test(filePath);
13884
13965
  }
13885
13966
  async function extractSingleFile(fileName, rootDir, tempDir, result) {
13886
- const srcPath = join4(rootDir, fileName);
13887
- const destPath = join4(tempDir, fileName);
13967
+ const srcPath = join5(rootDir, fileName);
13968
+ const destPath = join5(tempDir, fileName);
13888
13969
  try {
13889
13970
  if (await fileExists(srcPath)) {
13890
13971
  await copyFile(srcPath, destPath);
@@ -13898,8 +13979,8 @@ async function extractSingleFile(fileName, rootDir, tempDir, result) {
13898
13979
  }
13899
13980
  }
13900
13981
  async function extractSingleDir(dirName, rootDir, tempDir, result) {
13901
- const srcPath = join4(rootDir, dirName);
13902
- const destPath = join4(tempDir, dirName);
13982
+ const srcPath = join5(rootDir, dirName);
13983
+ const destPath = join5(tempDir, dirName);
13903
13984
  try {
13904
13985
  if (await fileExists(srcPath)) {
13905
13986
  await copyDirectory(srcPath, destPath, { overwrite: true, preserveTimestamps: true });
@@ -13936,8 +14017,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
13936
14017
  failures: []
13937
14018
  };
13938
14019
  for (const fileName of preservation.extractedFiles) {
13939
- const preservedPath = join4(preservation.tempDir, fileName);
13940
- const targetPath = join4(rootDir, fileName);
14020
+ const preservedPath = join5(preservation.tempDir, fileName);
14021
+ const targetPath = join5(rootDir, fileName);
13941
14022
  try {
13942
14023
  if (fileName.endsWith(".json")) {
13943
14024
  await mergeJsonFile(preservedPath, targetPath);
@@ -13953,8 +14034,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
13953
14034
  }
13954
14035
  }
13955
14036
  for (const dirName of preservation.extractedDirs) {
13956
- const preservedPath = join4(preservation.tempDir, dirName);
13957
- const targetPath = join4(rootDir, dirName);
14037
+ const preservedPath = join5(preservation.tempDir, dirName);
14038
+ const targetPath = join5(rootDir, dirName);
13958
14039
  try {
13959
14040
  await copyDirectory(preservedPath, targetPath, {
13960
14041
  overwrite: false,
@@ -14258,7 +14339,7 @@ init_fs();
14258
14339
  import { createHash } from "node:crypto";
14259
14340
  import { createReadStream } from "node:fs";
14260
14341
  import { readdir, stat } from "node:fs/promises";
14261
- import { join as join5, relative as relative2 } from "node:path";
14342
+ import { join as join6, relative as relative2 } from "node:path";
14262
14343
  var LOCKFILE_NAME = ".omcustom.lock.json";
14263
14344
  var LOCKFILE_VERSION = 1;
14264
14345
  var LOCKFILE_COMPONENTS = [
@@ -14287,7 +14368,7 @@ function computeFileHash(filePath) {
14287
14368
  });
14288
14369
  }
14289
14370
  async function writeLockfile(targetDir, lockfile) {
14290
- const lockfilePath = join5(targetDir, LOCKFILE_NAME);
14371
+ const lockfilePath = join6(targetDir, LOCKFILE_NAME);
14291
14372
  await writeJsonFile(lockfilePath, lockfile);
14292
14373
  debug("lockfile.written", { path: lockfilePath });
14293
14374
  }
@@ -14312,7 +14393,7 @@ async function collectFiles(dir2, projectRoot, isTopLevel) {
14312
14393
  if (isTopLevel && entry.startsWith(".") && entry !== ".claude") {
14313
14394
  continue;
14314
14395
  }
14315
- const fullPath = join5(dir2, entry);
14396
+ const fullPath = join6(dir2, entry);
14316
14397
  let fileStat;
14317
14398
  try {
14318
14399
  fileStat = await stat(fullPath);
@@ -14330,7 +14411,7 @@ async function collectFiles(dir2, projectRoot, isTopLevel) {
14330
14411
  }
14331
14412
  async function generateLockfile(targetDir, generatorVersion, templateVersion) {
14332
14413
  const files = {};
14333
- const componentRoots = COMPONENT_PATHS.map(([prefix]) => join5(targetDir, prefix));
14414
+ const componentRoots = COMPONENT_PATHS.map(([prefix]) => join6(targetDir, prefix));
14334
14415
  for (const componentRoot of componentRoots) {
14335
14416
  const exists2 = await fileExists(componentRoot);
14336
14417
  if (!exists2) {
@@ -14370,8 +14451,8 @@ async function generateLockfile(targetDir, generatorVersion, templateVersion) {
14370
14451
  async function generateAndWriteLockfileForDir(targetDir) {
14371
14452
  try {
14372
14453
  const packageRoot = getPackageRoot();
14373
- const manifest = await readJsonFile(join5(packageRoot, "templates", "manifest.json"));
14374
- const { version: generatorVersion } = await readJsonFile(join5(packageRoot, "package.json"));
14454
+ const manifest = await readJsonFile(join6(packageRoot, "templates", "manifest.json"));
14455
+ const { version: generatorVersion } = await readJsonFile(join6(packageRoot, "package.json"));
14375
14456
  const lockfile = await generateLockfile(targetDir, generatorVersion, manifest.version);
14376
14457
  await writeLockfile(targetDir, lockfile);
14377
14458
  return { fileCount: Object.keys(lockfile.files).length };
@@ -14381,11 +14462,24 @@ async function generateAndWriteLockfileForDir(targetDir) {
14381
14462
  }
14382
14463
  }
14383
14464
 
14465
+ // src/core/scope-filter.ts
14466
+ function getSkillScope(content) {
14467
+ const cleaned = content.replace(/^\uFEFF/, "");
14468
+ const frontmatter = cleaned.match(/^---\r?\n([\s\S]*?)\r?\n---/);
14469
+ if (!frontmatter)
14470
+ return "core";
14471
+ const match = frontmatter[1].match(/^scope:\s*(core|harness|package)\s*$/m);
14472
+ return match?.[1] ?? "core";
14473
+ }
14474
+ function shouldInstallSkill(scope) {
14475
+ return scope !== "package";
14476
+ }
14477
+
14384
14478
  // src/core/installer.ts
14385
14479
  var DEFAULT_LANGUAGE2 = "en";
14386
14480
  function getTemplateDir() {
14387
14481
  const packageRoot = getPackageRoot();
14388
- return join6(packageRoot, "templates");
14482
+ return join7(packageRoot, "templates");
14389
14483
  }
14390
14484
  function createInstallResult(targetDir) {
14391
14485
  return {
@@ -14407,7 +14501,7 @@ async function handleBackup(targetDir, shouldBackup, result) {
14407
14501
  if (!shouldBackup)
14408
14502
  return null;
14409
14503
  const layout = getProviderLayout();
14410
- const rootDir = join6(targetDir, layout.rootDir);
14504
+ const rootDir = join7(targetDir, layout.rootDir);
14411
14505
  let preservation = null;
14412
14506
  if (await fileExists(rootDir)) {
14413
14507
  const { createTempDir: createTempDir2 } = await Promise.resolve().then(() => (init_fs(), exports_fs));
@@ -14464,8 +14558,8 @@ async function installSingleComponent(targetDir, component, options, result) {
14464
14558
  }
14465
14559
  async function installStatusline(targetDir, options, _result) {
14466
14560
  const layout = getProviderLayout();
14467
- const srcPath = resolveTemplatePath(join6(layout.rootDir, "statusline.sh"));
14468
- const destPath = join6(targetDir, layout.rootDir, "statusline.sh");
14561
+ const srcPath = resolveTemplatePath(join7(layout.rootDir, "statusline.sh"));
14562
+ const destPath = join7(targetDir, layout.rootDir, "statusline.sh");
14469
14563
  if (!await fileExists(srcPath)) {
14470
14564
  debug("install.statusline_not_found", { path: srcPath });
14471
14565
  return;
@@ -14483,7 +14577,7 @@ async function installStatusline(targetDir, options, _result) {
14483
14577
  }
14484
14578
  async function installSettingsLocal(targetDir, result) {
14485
14579
  const layout = getProviderLayout();
14486
- const settingsPath = join6(targetDir, layout.rootDir, "settings.local.json");
14580
+ const settingsPath = join7(targetDir, layout.rootDir, "settings.local.json");
14487
14581
  const statusLineConfig = {
14488
14582
  statusLine: {
14489
14583
  type: "command",
@@ -14540,7 +14634,7 @@ async function install(options) {
14540
14634
  await installEntryDocWithTracking(options.targetDir, options, result);
14541
14635
  if (preservation) {
14542
14636
  const layout = getProviderLayout();
14543
- const rootDir = join6(options.targetDir, layout.rootDir);
14637
+ const rootDir = join7(options.targetDir, layout.rootDir);
14544
14638
  const restoration = await restoreCriticalFiles(rootDir, preservation);
14545
14639
  if (restoration.restoredFiles.length > 0 || restoration.restoredDirs.length > 0) {
14546
14640
  info("install.restored", {
@@ -14575,12 +14669,35 @@ async function install(options) {
14575
14669
  function getAllComponents() {
14576
14670
  return ["rules", "agents", "skills", "guides", "hooks", "contexts", "ontology"];
14577
14671
  }
14672
+ async function installSkillsWithScopeFilter(srcPath, destPath, options) {
14673
+ await ensureDirectory(destPath);
14674
+ const entries = await readdir2(srcPath);
14675
+ for (const entry of entries) {
14676
+ const entrySrcPath = join7(srcPath, entry);
14677
+ if (!(await stat2(entrySrcPath)).isDirectory())
14678
+ continue;
14679
+ const skillMdPath = join7(entrySrcPath, "SKILL.md");
14680
+ if (await fileExists(skillMdPath)) {
14681
+ const content = await fsReadFile(skillMdPath, "utf-8");
14682
+ const scope = getSkillScope(content);
14683
+ if (!shouldInstallSkill(scope)) {
14684
+ debug("install.skill_scope_excluded", { skill: entry, scope });
14685
+ continue;
14686
+ }
14687
+ }
14688
+ await copyDirectory(entrySrcPath, join7(destPath, entry), {
14689
+ overwrite: !!(options.force || options.backup),
14690
+ preserveSymlinks: true,
14691
+ preserveTimestamps: true
14692
+ });
14693
+ }
14694
+ }
14578
14695
  async function installComponent(targetDir, component, options) {
14579
14696
  if (component === "entry-md") {
14580
14697
  return false;
14581
14698
  }
14582
14699
  const templatePath = getComponentPath(component);
14583
- const destPath = join6(targetDir, templatePath);
14700
+ const destPath = join7(targetDir, templatePath);
14584
14701
  const destExists = await fileExists(destPath);
14585
14702
  if (destExists && !options.force && !options.backup) {
14586
14703
  debug("install.component_skipped", { component });
@@ -14591,11 +14708,15 @@ async function installComponent(targetDir, component, options) {
14591
14708
  warn("install.template_not_found", { component, path: srcPath });
14592
14709
  return false;
14593
14710
  }
14594
- await copyDirectory(srcPath, destPath, {
14595
- overwrite: !!(options.force || options.backup),
14596
- preserveSymlinks: true,
14597
- preserveTimestamps: true
14598
- });
14711
+ if (component === "skills") {
14712
+ await installSkillsWithScopeFilter(srcPath, destPath, options);
14713
+ } else {
14714
+ await copyDirectory(srcPath, destPath, {
14715
+ overwrite: !!(options.force || options.backup),
14716
+ preserveSymlinks: true,
14717
+ preserveTimestamps: true
14718
+ });
14719
+ }
14599
14720
  debug("install.component_installed", { component });
14600
14721
  return true;
14601
14722
  }
@@ -14608,7 +14729,7 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
14608
14729
  const layout = getProviderLayout();
14609
14730
  const templateFile = getEntryTemplateName(language);
14610
14731
  const srcPath = resolveTemplatePath(templateFile);
14611
- const destPath = join6(targetDir, layout.entryFile);
14732
+ const destPath = join7(targetDir, layout.entryFile);
14612
14733
  if (!await fileExists(srcPath)) {
14613
14734
  warn("install.entry_md_not_found", { language, path: srcPath, entry: layout.entryFile });
14614
14735
  return false;
@@ -14629,7 +14750,7 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
14629
14750
  }
14630
14751
  async function backupExisting(sourcePath, backupDir) {
14631
14752
  const name = basename2(sourcePath);
14632
- const backupPath = join6(backupDir, name);
14753
+ const backupPath = join7(backupDir, name);
14633
14754
  await rename(sourcePath, backupPath);
14634
14755
  return backupPath;
14635
14756
  }
@@ -14638,7 +14759,7 @@ async function checkExistingPaths(targetDir) {
14638
14759
  const pathsToCheck = [layout.entryFile, layout.rootDir, "guides"];
14639
14760
  const existingPaths = [];
14640
14761
  for (const relativePath of pathsToCheck) {
14641
- const fullPath = join6(targetDir, relativePath);
14762
+ const fullPath = join7(targetDir, relativePath);
14642
14763
  if (await fileExists(fullPath)) {
14643
14764
  existingPaths.push(relativePath);
14644
14765
  }
@@ -14652,11 +14773,11 @@ async function backupExistingInstallation(targetDir) {
14652
14773
  return [];
14653
14774
  }
14654
14775
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
14655
- const backupDir = join6(targetDir, `${layout.backupDirPrefix}${timestamp}`);
14776
+ const backupDir = join7(targetDir, `${layout.backupDirPrefix}${timestamp}`);
14656
14777
  await ensureDirectory(backupDir);
14657
14778
  const backedUpPaths = [];
14658
14779
  for (const relativePath of existingPaths) {
14659
- const fullPath = join6(targetDir, relativePath);
14780
+ const fullPath = join7(targetDir, relativePath);
14660
14781
  try {
14661
14782
  const backupPath = await backupExisting(fullPath, backupDir);
14662
14783
  backedUpPaths.push(backupPath);
@@ -14673,12 +14794,12 @@ async function backupExistingInstallation(targetDir) {
14673
14794
  init_fs();
14674
14795
  import { execSync as execSync3 } from "node:child_process";
14675
14796
  import { writeFile } from "node:fs/promises";
14676
- import { join as join7 } from "node:path";
14797
+ import { join as join8 } from "node:path";
14677
14798
  async function generateMCPConfig(targetDir) {
14678
14799
  const layout = getProviderLayout();
14679
- const mcpConfigPath = join7(targetDir, ".mcp.json");
14680
- const ontologyDir = join7(layout.rootDir, "ontology");
14681
- const ontologyExists = await fileExists(join7(targetDir, ontologyDir));
14800
+ const mcpConfigPath = join8(targetDir, ".mcp.json");
14801
+ const ontologyDir = join8(layout.rootDir, "ontology");
14802
+ const ontologyExists = await fileExists(join8(targetDir, ontologyDir));
14682
14803
  if (!ontologyExists) {
14683
14804
  return;
14684
14805
  }
@@ -14704,8 +14825,8 @@ async function generateMCPConfig(targetDir) {
14704
14825
  const existingConfigPath = mcpConfigPath;
14705
14826
  if (await fileExists(existingConfigPath)) {
14706
14827
  try {
14707
- const { readFile } = await import("node:fs/promises");
14708
- const existingContent = await readFile(existingConfigPath, "utf-8");
14828
+ const { readFile: readFile2 } = await import("node:fs/promises");
14829
+ const existingContent = await readFile2(existingConfigPath, "utf-8");
14709
14830
  const existing = JSON.parse(existingContent);
14710
14831
  if (!existing.mcpServers?.["ontology-rag"]) {
14711
14832
  existing.mcpServers = existing.mcpServers || {};
@@ -14735,7 +14856,7 @@ async function checkUvAvailable() {
14735
14856
  init_fs();
14736
14857
  async function checkExistingInstallation(targetDir) {
14737
14858
  const layout = getProviderLayout();
14738
- const rootDir = join8(targetDir, layout.rootDir);
14859
+ const rootDir = join9(targetDir, layout.rootDir);
14739
14860
  return fileExists(rootDir);
14740
14861
  }
14741
14862
  var PROVIDER_SUBDIR_COMPONENTS = new Set([
@@ -14749,13 +14870,13 @@ var PROVIDER_SUBDIR_COMPONENTS = new Set([
14749
14870
  function componentToPath(targetDir, component) {
14750
14871
  if (component === "entry-md") {
14751
14872
  const layout = getProviderLayout();
14752
- return join8(targetDir, layout.entryFile);
14873
+ return join9(targetDir, layout.entryFile);
14753
14874
  }
14754
14875
  if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
14755
14876
  const layout = getProviderLayout();
14756
- return join8(targetDir, layout.rootDir, component);
14877
+ return join9(targetDir, layout.rootDir, component);
14757
14878
  }
14758
- return join8(targetDir, component);
14879
+ return join9(targetDir, component);
14759
14880
  }
14760
14881
  function buildInstalledPaths(targetDir, components) {
14761
14882
  return components.map((component) => componentToPath(targetDir, component));
@@ -14835,7 +14956,7 @@ async function initCommand(options) {
14835
14956
  }
14836
14957
 
14837
14958
  // src/cli/list.ts
14838
- import { basename as basename3, dirname as dirname3, join as join9, relative as relative3 } from "node:path";
14959
+ import { basename as basename3, dirname as dirname3, join as join10, relative as relative3 } from "node:path";
14839
14960
  init_fs();
14840
14961
  var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
14841
14962
  function parseKeyValue(line) {
@@ -14900,12 +15021,12 @@ function extractAgentTypeFromFilename(filename) {
14900
15021
  return prefixMap[prefix] || "unknown";
14901
15022
  }
14902
15023
  function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
14903
- const relativePath = relative3(join9(baseDir, rootDir, "skills"), skillPath);
15024
+ const relativePath = relative3(join10(baseDir, rootDir, "skills"), skillPath);
14904
15025
  const parts = relativePath.split("/").filter(Boolean);
14905
15026
  return parts[0] || "unknown";
14906
15027
  }
14907
15028
  function extractGuideCategoryFromPath(guidePath, baseDir) {
14908
- const relativePath = relative3(join9(baseDir, "guides"), guidePath);
15029
+ const relativePath = relative3(join10(baseDir, "guides"), guidePath);
14909
15030
  const parts = relativePath.split("/").filter(Boolean);
14910
15031
  return parts[0] || "unknown";
14911
15032
  }
@@ -14999,7 +15120,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
14999
15120
  }
15000
15121
  }
15001
15122
  async function getAgents(targetDir, rootDir = ".claude", config) {
15002
- const agentsDir = join9(targetDir, rootDir, "agents");
15123
+ const agentsDir = join10(targetDir, rootDir, "agents");
15003
15124
  if (!await fileExists(agentsDir))
15004
15125
  return [];
15005
15126
  try {
@@ -15027,7 +15148,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
15027
15148
  }
15028
15149
  }
15029
15150
  async function getSkills(targetDir, rootDir = ".claude", config) {
15030
- const skillsDir = join9(targetDir, rootDir, "skills");
15151
+ const skillsDir = join10(targetDir, rootDir, "skills");
15031
15152
  if (!await fileExists(skillsDir))
15032
15153
  return [];
15033
15154
  try {
@@ -15037,7 +15158,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
15037
15158
  const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
15038
15159
  const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
15039
15160
  const skillDir = dirname3(skillMdPath);
15040
- const indexYamlPath = join9(skillDir, "index.yaml");
15161
+ const indexYamlPath = join10(skillDir, "index.yaml");
15041
15162
  const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
15042
15163
  const relativePath = relative3(targetDir, skillDir);
15043
15164
  return {
@@ -15056,7 +15177,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
15056
15177
  }
15057
15178
  }
15058
15179
  async function getGuides(targetDir, config) {
15059
- const guidesDir = join9(targetDir, "guides");
15180
+ const guidesDir = join10(targetDir, "guides");
15060
15181
  if (!await fileExists(guidesDir))
15061
15182
  return [];
15062
15183
  try {
@@ -15083,7 +15204,7 @@ async function getGuides(targetDir, config) {
15083
15204
  }
15084
15205
  var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
15085
15206
  async function getRules(targetDir, rootDir = ".claude", config) {
15086
- const rulesDir = join9(targetDir, rootDir, "rules");
15207
+ const rulesDir = join10(targetDir, rootDir, "rules");
15087
15208
  if (!await fileExists(rulesDir))
15088
15209
  return [];
15089
15210
  try {
@@ -15155,7 +15276,7 @@ function formatAsJson(components) {
15155
15276
  console.log(JSON.stringify(components, null, 2));
15156
15277
  }
15157
15278
  async function getHooks(targetDir, rootDir = ".claude") {
15158
- const hooksDir = join9(targetDir, rootDir, "hooks");
15279
+ const hooksDir = join10(targetDir, rootDir, "hooks");
15159
15280
  if (!await fileExists(hooksDir))
15160
15281
  return [];
15161
15282
  try {
@@ -15173,7 +15294,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
15173
15294
  }
15174
15295
  }
15175
15296
  async function getContexts(targetDir, rootDir = ".claude") {
15176
- const contextsDir = join9(targetDir, rootDir, "contexts");
15297
+ const contextsDir = join10(targetDir, rootDir, "contexts");
15177
15298
  if (!await fileExists(contextsDir))
15178
15299
  return [];
15179
15300
  try {
@@ -15563,7 +15684,7 @@ async function securityCommand(_options = {}) {
15563
15684
 
15564
15685
  // src/core/updater.ts
15565
15686
  init_fs();
15566
- import { join as join10 } from "node:path";
15687
+ import { join as join11 } from "node:path";
15567
15688
 
15568
15689
  // src/core/entry-merger.ts
15569
15690
  var MANAGED_START = "<!-- omcustom:start -->";
@@ -15812,7 +15933,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
15812
15933
  }
15813
15934
  async function updateEntryDoc(targetDir, config, options) {
15814
15935
  const layout = getProviderLayout();
15815
- const entryPath = join10(targetDir, layout.entryFile);
15936
+ const entryPath = join11(targetDir, layout.entryFile);
15816
15937
  const templateName = getEntryTemplateName2(config.language);
15817
15938
  const templatePath = resolveTemplatePath(templateName);
15818
15939
  if (!await fileExists(templatePath)) {
@@ -15958,7 +16079,7 @@ async function collectProtectedSkipPaths(srcPath, destPath, componentPath, force
15958
16079
  }
15959
16080
  const protectedRelative = await findProtectedFilesInDir(srcPath, componentPath);
15960
16081
  const path3 = await import("node:path");
15961
- const skipPaths = protectedRelative.map((p) => path3.relative(destPath, join10(destPath, p)));
16082
+ const skipPaths = protectedRelative.map((p) => path3.relative(destPath, join11(destPath, p)));
15962
16083
  return { skipPaths, warnedPaths: protectedRelative };
15963
16084
  }
15964
16085
  function isEntryProtected(relPath, componentRelativePrefix) {
@@ -15999,7 +16120,7 @@ async function updateComponent(targetDir, component, customizations, options, co
15999
16120
  const preservedFiles = [];
16000
16121
  const componentPath = getComponentPath2(component);
16001
16122
  const srcPath = resolveTemplatePath(componentPath);
16002
- const destPath = join10(targetDir, componentPath);
16123
+ const destPath = join11(targetDir, componentPath);
16003
16124
  const customComponents = config.customComponents || [];
16004
16125
  const skipPaths = [];
16005
16126
  if (customizations && !options.forceOverwriteAll) {
@@ -16030,7 +16151,7 @@ async function updateComponent(targetDir, component, customizations, options, co
16030
16151
  }
16031
16152
  skipPaths.push(...protectedSkipPaths);
16032
16153
  const path3 = await import("node:path");
16033
- const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join10(targetDir, p)));
16154
+ const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join11(targetDir, p)));
16034
16155
  const uniqueSkipPaths = [...new Set(normalizedSkipPaths)];
16035
16156
  await copyDirectory(srcPath, destPath, {
16036
16157
  overwrite: true,
@@ -16052,12 +16173,12 @@ async function syncRootLevelFiles(targetDir, options) {
16052
16173
  const layout = getProviderLayout();
16053
16174
  const synced = [];
16054
16175
  for (const fileName of ROOT_LEVEL_FILES) {
16055
- const srcPath = resolveTemplatePath(join10(layout.rootDir, fileName));
16176
+ const srcPath = resolveTemplatePath(join11(layout.rootDir, fileName));
16056
16177
  if (!await fileExists(srcPath)) {
16057
16178
  continue;
16058
16179
  }
16059
- const destPath = join10(targetDir, layout.rootDir, fileName);
16060
- await ensureDirectory(join10(destPath, ".."));
16180
+ const destPath = join11(targetDir, layout.rootDir, fileName);
16181
+ await ensureDirectory(join11(destPath, ".."));
16061
16182
  await fs3.copyFile(srcPath, destPath);
16062
16183
  if (fileName.endsWith(".sh")) {
16063
16184
  await fs3.chmod(destPath, 493);
@@ -16092,7 +16213,7 @@ async function removeDeprecatedFiles(targetDir, options) {
16092
16213
  });
16093
16214
  continue;
16094
16215
  }
16095
- const fullPath = join10(targetDir, entry.path);
16216
+ const fullPath = join11(targetDir, entry.path);
16096
16217
  if (await fileExists(fullPath)) {
16097
16218
  await fs3.unlink(fullPath);
16098
16219
  removed.push(entry.path);
@@ -16116,26 +16237,26 @@ function getComponentPath2(component) {
16116
16237
  }
16117
16238
  async function backupInstallation(targetDir) {
16118
16239
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
16119
- const backupDir = join10(targetDir, `.omcustom-backup-${timestamp}`);
16240
+ const backupDir = join11(targetDir, `.omcustom-backup-${timestamp}`);
16120
16241
  const fs3 = await import("node:fs/promises");
16121
16242
  await ensureDirectory(backupDir);
16122
16243
  const layout = getProviderLayout();
16123
16244
  const dirsToBackup = [layout.rootDir, "guides"];
16124
16245
  for (const dir2 of dirsToBackup) {
16125
- const srcPath = join10(targetDir, dir2);
16246
+ const srcPath = join11(targetDir, dir2);
16126
16247
  if (await fileExists(srcPath)) {
16127
- const destPath = join10(backupDir, dir2);
16248
+ const destPath = join11(backupDir, dir2);
16128
16249
  await copyDirectory(srcPath, destPath, { overwrite: true });
16129
16250
  }
16130
16251
  }
16131
- const entryPath = join10(targetDir, layout.entryFile);
16252
+ const entryPath = join11(targetDir, layout.entryFile);
16132
16253
  if (await fileExists(entryPath)) {
16133
- await fs3.copyFile(entryPath, join10(backupDir, layout.entryFile));
16254
+ await fs3.copyFile(entryPath, join11(backupDir, layout.entryFile));
16134
16255
  }
16135
16256
  return backupDir;
16136
16257
  }
16137
16258
  async function loadCustomizationManifest(targetDir) {
16138
- const manifestPath = join10(targetDir, CUSTOMIZATION_MANIFEST_FILE);
16259
+ const manifestPath = join11(targetDir, CUSTOMIZATION_MANIFEST_FILE);
16139
16260
  if (await fileExists(manifestPath)) {
16140
16261
  return readJsonFile(manifestPath);
16141
16262
  }