oh-my-customcode 0.8.0 → 0.9.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 (132) hide show
  1. package/README.md +10 -2
  2. package/dist/cli/index.js +517 -274
  3. package/dist/index.js +303 -101
  4. package/package.json +1 -1
  5. package/templates/.codex/agents/arch-documenter.md +97 -0
  6. package/templates/.codex/agents/arch-speckit-agent.md +134 -0
  7. package/templates/.codex/agents/be-express-expert.md +80 -0
  8. package/templates/.codex/agents/be-fastapi-expert.md +43 -0
  9. package/templates/.codex/agents/be-go-backend-expert.md +43 -0
  10. package/templates/.codex/agents/be-nestjs-expert.md +60 -0
  11. package/templates/.codex/agents/be-springboot-expert.md +85 -0
  12. package/templates/.codex/agents/db-postgres-expert.md +106 -0
  13. package/templates/.codex/agents/db-redis-expert.md +101 -0
  14. package/templates/.codex/agents/db-supabase-expert.md +71 -0
  15. package/templates/.codex/agents/de-airflow-expert.md +71 -0
  16. package/templates/.codex/agents/de-dbt-expert.md +72 -0
  17. package/templates/.codex/agents/de-kafka-expert.md +81 -0
  18. package/templates/.codex/agents/de-pipeline-expert.md +92 -0
  19. package/templates/.codex/agents/de-snowflake-expert.md +89 -0
  20. package/templates/.codex/agents/de-spark-expert.md +80 -0
  21. package/templates/.codex/agents/fe-svelte-agent.md +65 -0
  22. package/templates/.codex/agents/fe-vercel-agent.md +69 -0
  23. package/templates/.codex/agents/fe-vuejs-agent.md +65 -0
  24. package/templates/.codex/agents/infra-aws-expert.md +47 -0
  25. package/templates/.codex/agents/infra-docker-expert.md +47 -0
  26. package/templates/.codex/agents/lang-golang-expert.md +43 -0
  27. package/templates/.codex/agents/lang-java21-expert.md +65 -0
  28. package/templates/.codex/agents/lang-kotlin-expert.md +43 -0
  29. package/templates/.codex/agents/lang-python-expert.md +43 -0
  30. package/templates/.codex/agents/lang-rust-expert.md +43 -0
  31. package/templates/.codex/agents/lang-typescript-expert.md +43 -0
  32. package/templates/.codex/agents/mgr-claude-code-bible.md +246 -0
  33. package/templates/.codex/agents/mgr-creator.md +120 -0
  34. package/templates/.codex/agents/mgr-gitnerd.md +113 -0
  35. package/templates/.codex/agents/mgr-sauron.md +154 -0
  36. package/templates/.codex/agents/mgr-supplier.md +120 -0
  37. package/templates/.codex/agents/mgr-sync-checker.md +99 -0
  38. package/templates/.codex/agents/mgr-updater.md +103 -0
  39. package/templates/.codex/agents/qa-engineer.md +96 -0
  40. package/templates/.codex/agents/qa-planner.md +74 -0
  41. package/templates/.codex/agents/qa-writer.md +97 -0
  42. package/templates/.codex/agents/sys-memory-keeper.md +117 -0
  43. package/templates/.codex/agents/sys-naggy.md +90 -0
  44. package/templates/.codex/agents/tool-bun-expert.md +71 -0
  45. package/templates/.codex/agents/tool-npm-expert.md +88 -0
  46. package/templates/.codex/agents/tool-optimizer.md +87 -0
  47. package/templates/.codex/codex-native-hash.txt +1 -0
  48. package/templates/.codex/contexts/dev.md +20 -0
  49. package/templates/.codex/contexts/ecomode.md +63 -0
  50. package/templates/.codex/contexts/index.yaml +41 -0
  51. package/templates/.codex/contexts/research.md +28 -0
  52. package/templates/.codex/contexts/review.md +23 -0
  53. package/templates/.codex/hooks/hooks.json +151 -0
  54. package/templates/.codex/install-hooks.sh +100 -0
  55. package/templates/.codex/rules/MAY-optimization.md +93 -0
  56. package/templates/.codex/rules/MUST-agent-design.md +162 -0
  57. package/templates/.codex/rules/MUST-agent-identification.md +108 -0
  58. package/templates/.codex/rules/MUST-continuous-improvement.md +132 -0
  59. package/templates/.codex/rules/MUST-intent-transparency.md +199 -0
  60. package/templates/.codex/rules/MUST-language-policy.md +62 -0
  61. package/templates/.codex/rules/MUST-orchestrator-coordination.md +471 -0
  62. package/templates/.codex/rules/MUST-parallel-execution.md +469 -0
  63. package/templates/.codex/rules/MUST-permissions.md +84 -0
  64. package/templates/.codex/rules/MUST-safety.md +69 -0
  65. package/templates/.codex/rules/MUST-sync-verification.md +281 -0
  66. package/templates/.codex/rules/MUST-tool-identification.md +195 -0
  67. package/templates/.codex/rules/SHOULD-agent-teams.md +183 -0
  68. package/templates/.codex/rules/SHOULD-ecomode.md +145 -0
  69. package/templates/.codex/rules/SHOULD-error-handling.md +102 -0
  70. package/templates/.codex/rules/SHOULD-hud-statusline.md +112 -0
  71. package/templates/.codex/rules/SHOULD-interaction.md +103 -0
  72. package/templates/.codex/rules/SHOULD-memory-integration.md +132 -0
  73. package/templates/.codex/rules/index.yaml +141 -0
  74. package/templates/.codex/skills/airflow-best-practices/SKILL.md +56 -0
  75. package/templates/.codex/skills/audit-agents/SKILL.md +116 -0
  76. package/templates/.codex/skills/aws-best-practices/SKILL.md +280 -0
  77. package/templates/.codex/skills/claude-code-bible/SKILL.md +180 -0
  78. package/templates/.codex/skills/claude-code-bible/scripts/fetch-docs.js +244 -0
  79. package/templates/.codex/skills/create-agent/SKILL.md +91 -0
  80. package/templates/.codex/skills/dbt-best-practices/SKILL.md +54 -0
  81. package/templates/.codex/skills/de-lead-routing/SKILL.md +230 -0
  82. package/templates/.codex/skills/dev-lead-routing/SKILL.md +253 -0
  83. package/templates/.codex/skills/dev-refactor/SKILL.md +123 -0
  84. package/templates/.codex/skills/dev-review/SKILL.md +81 -0
  85. package/templates/.codex/skills/docker-best-practices/SKILL.md +275 -0
  86. package/templates/.codex/skills/fastapi-best-practices/SKILL.md +270 -0
  87. package/templates/.codex/skills/fix-refs/SKILL.md +107 -0
  88. package/templates/.codex/skills/go-backend-best-practices/SKILL.md +338 -0
  89. package/templates/.codex/skills/go-best-practices/CLAUDE.md +9 -0
  90. package/templates/.codex/skills/go-best-practices/SKILL.md +203 -0
  91. package/templates/.codex/skills/help/SKILL.md +125 -0
  92. package/templates/.codex/skills/intent-detection/SKILL.md +215 -0
  93. package/templates/.codex/skills/intent-detection/patterns/agent-triggers.yaml +349 -0
  94. package/templates/.codex/skills/kafka-best-practices/SKILL.md +52 -0
  95. package/templates/.codex/skills/kotlin-best-practices/SKILL.md +256 -0
  96. package/templates/.codex/skills/lists/SKILL.md +78 -0
  97. package/templates/.codex/skills/memory-management/SKILL.md +195 -0
  98. package/templates/.codex/skills/memory-recall/SKILL.md +152 -0
  99. package/templates/.codex/skills/memory-save/SKILL.md +126 -0
  100. package/templates/.codex/skills/monitoring-setup/SKILL.md +115 -0
  101. package/templates/.codex/skills/npm-audit/SKILL.md +72 -0
  102. package/templates/.codex/skills/npm-publish/SKILL.md +63 -0
  103. package/templates/.codex/skills/npm-version/SKILL.md +75 -0
  104. package/templates/.codex/skills/optimize-analyze/SKILL.md +55 -0
  105. package/templates/.codex/skills/optimize-bundle/SKILL.md +67 -0
  106. package/templates/.codex/skills/optimize-report/SKILL.md +74 -0
  107. package/templates/.codex/skills/pipeline-architecture-patterns/SKILL.md +83 -0
  108. package/templates/.codex/skills/postgres-best-practices/SKILL.md +66 -0
  109. package/templates/.codex/skills/python-best-practices/SKILL.md +222 -0
  110. package/templates/.codex/skills/qa-lead-routing/SKILL.md +277 -0
  111. package/templates/.codex/skills/react-best-practices/SKILL.md +101 -0
  112. package/templates/.codex/skills/redis-best-practices/SKILL.md +83 -0
  113. package/templates/.codex/skills/result-aggregation/SKILL.md +164 -0
  114. package/templates/.codex/skills/rust-best-practices/SKILL.md +267 -0
  115. package/templates/.codex/skills/sauron-watch/SKILL.md +144 -0
  116. package/templates/.codex/skills/secretary-routing/SKILL.md +190 -0
  117. package/templates/.codex/skills/snowflake-best-practices/SKILL.md +65 -0
  118. package/templates/.codex/skills/spark-best-practices/SKILL.md +52 -0
  119. package/templates/.codex/skills/springboot-best-practices/SKILL.md +357 -0
  120. package/templates/.codex/skills/status/SKILL.md +153 -0
  121. package/templates/.codex/skills/supabase-postgres-best-practices/SKILL.md +99 -0
  122. package/templates/.codex/skills/typescript-best-practices/SKILL.md +321 -0
  123. package/templates/.codex/skills/update-docs/SKILL.md +140 -0
  124. package/templates/.codex/skills/update-external/SKILL.md +149 -0
  125. package/templates/.codex/skills/vercel-deploy/SKILL.md +73 -0
  126. package/templates/.codex/skills/web-design-guidelines/SKILL.md +118 -0
  127. package/templates/.codex/skills/writing-clearly-and-concisely/SKILL.md +64 -0
  128. package/templates/.codex/uninstall-hooks.sh +52 -0
  129. package/templates/AGENTS.md.en +39 -0
  130. package/templates/AGENTS.md.ko +39 -0
  131. package/templates/manifest.codex.json +43 -0
  132. package/templates/manifest.json +5 -5
package/dist/cli/index.js CHANGED
@@ -11464,8 +11464,9 @@ var en_default = {
11464
11464
  init: {
11465
11465
  description: "Initialize oh-my-customcode in the current directory",
11466
11466
  langOption: "Language for templates (en or ko)",
11467
+ providerOption: "Provider to initialize (auto, claude, codex)",
11467
11468
  start: "Initializing oh-my-customcode...",
11468
- exists: "Existing .claude/ directory found.",
11469
+ exists: "Existing {{rootDir}} directory found.",
11469
11470
  backing_up: "Creating backup...",
11470
11471
  backedUp: "Backed up existing installation to: {{path}}",
11471
11472
  copying: "Copying templates...",
@@ -11507,6 +11508,7 @@ var en_default = {
11507
11508
  list: {
11508
11509
  description: "List installed components",
11509
11510
  typeArgument: "Type to list: agents, skills, guides, rules, or all",
11511
+ providerOption: "Provider to list (auto, claude, codex)",
11510
11512
  scanning: "Scanning installed components...",
11511
11513
  empty: "No {{type}} found.",
11512
11514
  header: "{{type}} ({{count}} installed)",
@@ -11531,6 +11533,7 @@ var en_default = {
11531
11533
  doctor: {
11532
11534
  description: "Check and fix configuration issues",
11533
11535
  fixOption: "Automatically fix issues that can be fixed",
11536
+ providerOption: "Provider to diagnose (auto, claude, codex)",
11534
11537
  checking: "Running diagnostic checks...",
11535
11538
  applyingFixes: "Applying fixes...",
11536
11539
  fixing: "Fixing: {{name}}...",
@@ -11571,6 +11574,10 @@ var en_default = {
11571
11574
  pass: "CLAUDE.md exists",
11572
11575
  fail: "CLAUDE.md not found"
11573
11576
  },
11577
+ entryMd: {
11578
+ pass: "{{entry}} exists",
11579
+ fail: "{{entry}} not found"
11580
+ },
11574
11581
  rules: {
11575
11582
  pass: "Rules directory is valid",
11576
11583
  fail: "Rules directory is missing or incomplete"
@@ -11711,8 +11718,9 @@ var ko_default = {
11711
11718
  init: {
11712
11719
  description: "현재 디렉토리에 oh-my-customcode 초기화",
11713
11720
  langOption: "템플릿 언어 (en 또는 ko)",
11721
+ providerOption: "초기화할 제공자 (auto, claude, codex)",
11714
11722
  start: "oh-my-customcode 초기화 중...",
11715
- exists: "기존 .claude/ 디렉토리가 발견되었습니다.",
11723
+ exists: "기존 {{rootDir}} 디렉토리가 발견되었습니다.",
11716
11724
  backing_up: "백업을 생성합니다...",
11717
11725
  backedUp: "기존 설치가 백업되었습니다: {{path}}",
11718
11726
  copying: "템플릿 복사 중...",
@@ -11754,6 +11762,7 @@ var ko_default = {
11754
11762
  list: {
11755
11763
  description: "설치된 컴포넌트 목록 표시",
11756
11764
  typeArgument: "표시할 타입: agents, skills, guides, rules 또는 all",
11765
+ providerOption: "표시할 제공자 (auto, claude, codex)",
11757
11766
  scanning: "설치된 컴포넌트 검색 중...",
11758
11767
  empty: "{{type}}이(가) 없습니다.",
11759
11768
  header: "{{type}} ({{count}}개 설치됨)",
@@ -11778,6 +11787,7 @@ var ko_default = {
11778
11787
  doctor: {
11779
11788
  description: "설정 문제 확인 및 수정",
11780
11789
  fixOption: "자동으로 수정 가능한 문제 수정",
11790
+ providerOption: "진단할 제공자 (auto, claude, codex)",
11781
11791
  checking: "진단 검사 실행 중...",
11782
11792
  applyingFixes: "수정 사항 적용 중...",
11783
11793
  fixing: "수정 중: {{name}}...",
@@ -11818,6 +11828,10 @@ var ko_default = {
11818
11828
  pass: "CLAUDE.md 존재함",
11819
11829
  fail: "CLAUDE.md를 찾을 수 없음"
11820
11830
  },
11831
+ entryMd: {
11832
+ pass: "{{entry}} 존재함",
11833
+ fail: "{{entry}}를 찾을 수 없음"
11834
+ },
11821
11835
  rules: {
11822
11836
  pass: "Rules 디렉토리 정상",
11823
11837
  fail: "Rules 디렉토리가 없거나 불완전함"
@@ -12015,6 +12029,341 @@ var $stringify = publicApi.stringify;
12015
12029
  var $visit = visit.visit;
12016
12030
  var $visitAsync = visit.visitAsync;
12017
12031
 
12032
+ // src/core/layout.ts
12033
+ var PROVIDER_LAYOUTS = {
12034
+ claude: {
12035
+ provider: "claude",
12036
+ rootDir: ".claude",
12037
+ entryFile: "CLAUDE.md",
12038
+ entryTemplatePrefix: "CLAUDE.md",
12039
+ manifestFile: "manifest.json",
12040
+ backupDirPrefix: ".claude-backup-",
12041
+ directoryStructure: [
12042
+ ".claude",
12043
+ ".claude/rules",
12044
+ ".claude/hooks",
12045
+ ".claude/contexts",
12046
+ ".claude/agents",
12047
+ ".claude/skills",
12048
+ "guides"
12049
+ ]
12050
+ },
12051
+ codex: {
12052
+ provider: "codex",
12053
+ rootDir: ".codex",
12054
+ entryFile: "AGENTS.md",
12055
+ entryTemplatePrefix: "AGENTS.md",
12056
+ manifestFile: "manifest.codex.json",
12057
+ backupDirPrefix: ".codex-backup-",
12058
+ directoryStructure: [
12059
+ ".codex",
12060
+ ".codex/rules",
12061
+ ".codex/hooks",
12062
+ ".codex/contexts",
12063
+ ".codex/agents",
12064
+ ".codex/skills",
12065
+ "guides"
12066
+ ]
12067
+ }
12068
+ };
12069
+ function getProviderLayout(provider) {
12070
+ return PROVIDER_LAYOUTS[provider];
12071
+ }
12072
+ function getEntryTemplateName(provider, language) {
12073
+ const layout = getProviderLayout(provider);
12074
+ return `${layout.entryTemplatePrefix}.${language}`;
12075
+ }
12076
+ function getComponentPath(provider, component) {
12077
+ const layout = getProviderLayout(provider);
12078
+ if (component === "entry-md") {
12079
+ return layout.entryFile;
12080
+ }
12081
+ if (component === "guides") {
12082
+ return "guides";
12083
+ }
12084
+ return `${layout.rootDir}/${component}`;
12085
+ }
12086
+ function getDefaultProvider() {
12087
+ return "claude";
12088
+ }
12089
+
12090
+ // src/core/provider.ts
12091
+ import { join as join2 } from "node:path";
12092
+
12093
+ // src/utils/fs.ts
12094
+ import { dirname, join, resolve } from "node:path";
12095
+ import { fileURLToPath } from "node:url";
12096
+ async function fileExists(path) {
12097
+ const fs = await import("node:fs/promises");
12098
+ try {
12099
+ await fs.access(path);
12100
+ return true;
12101
+ } catch {
12102
+ return false;
12103
+ }
12104
+ }
12105
+ async function ensureDirectory(path) {
12106
+ const fs = await import("node:fs/promises");
12107
+ await fs.mkdir(path, { recursive: true });
12108
+ }
12109
+ function shouldSkipEntry(entryName, options) {
12110
+ if (options.exclude?.some((pattern) => matchesPattern(entryName, pattern))) {
12111
+ return true;
12112
+ }
12113
+ if (options.include && !options.include.some((pattern) => matchesPattern(entryName, pattern))) {
12114
+ return true;
12115
+ }
12116
+ return false;
12117
+ }
12118
+ async function handleSymlink(srcPath, destPath, options, fs) {
12119
+ const destExists = await fileExists(destPath);
12120
+ if (destExists && !options.overwrite) {
12121
+ return;
12122
+ }
12123
+ if (options.preserveSymlinks !== false) {
12124
+ await copyPreservedSymlink(srcPath, destPath, destExists, fs);
12125
+ } else {
12126
+ await copyFollowedSymlink(srcPath, destPath, destExists, options, fs);
12127
+ }
12128
+ }
12129
+ async function copyPreservedSymlink(srcPath, destPath, destExists, fs) {
12130
+ const linkTarget = await fs.readlink(srcPath);
12131
+ if (destExists) {
12132
+ await fs.unlink(destPath);
12133
+ }
12134
+ await fs.symlink(linkTarget, destPath);
12135
+ }
12136
+ async function copyFollowedSymlink(srcPath, destPath, destExists, options, fs) {
12137
+ const realPath = await fs.realpath(srcPath);
12138
+ const stat = await fs.stat(realPath);
12139
+ if (stat.isDirectory()) {
12140
+ await copyDirectory(realPath, destPath, options);
12141
+ return;
12142
+ }
12143
+ if (destExists) {
12144
+ await fs.unlink(destPath);
12145
+ }
12146
+ await fs.copyFile(realPath, destPath);
12147
+ }
12148
+ async function handleFile(srcPath, destPath, options, fs) {
12149
+ const destExists = await fileExists(destPath);
12150
+ if (destExists && !options.overwrite) {
12151
+ return;
12152
+ }
12153
+ await fs.copyFile(srcPath, destPath);
12154
+ if (options.preserveTimestamps) {
12155
+ const stats = await fs.stat(srcPath);
12156
+ await fs.utimes(destPath, stats.atime, stats.mtime);
12157
+ }
12158
+ }
12159
+ async function copyDirectory(src, dest, options = {}) {
12160
+ const fs = await import("node:fs/promises");
12161
+ const path = await import("node:path");
12162
+ await ensureDirectory(dest);
12163
+ const entries = await fs.readdir(src, { withFileTypes: true });
12164
+ for (const entry of entries) {
12165
+ if (shouldSkipEntry(entry.name, options)) {
12166
+ continue;
12167
+ }
12168
+ const srcPath = path.join(src, entry.name);
12169
+ const destPath = path.join(dest, entry.name);
12170
+ if (entry.isSymbolicLink()) {
12171
+ await handleSymlink(srcPath, destPath, options, fs);
12172
+ } else if (entry.isDirectory()) {
12173
+ await copyDirectory(srcPath, destPath, options);
12174
+ } else if (entry.isFile()) {
12175
+ await handleFile(srcPath, destPath, options, fs);
12176
+ }
12177
+ }
12178
+ }
12179
+ async function readJsonFile(path) {
12180
+ const fs = await import("node:fs/promises");
12181
+ const content = await fs.readFile(path, "utf-8");
12182
+ return JSON.parse(content);
12183
+ }
12184
+ async function writeJsonFile(path, data) {
12185
+ const fs = await import("node:fs/promises");
12186
+ const content = JSON.stringify(data, null, 2);
12187
+ await fs.writeFile(path, content, "utf-8");
12188
+ }
12189
+ async function readTextFile(path) {
12190
+ const fs = await import("node:fs/promises");
12191
+ return fs.readFile(path, "utf-8");
12192
+ }
12193
+ function getPackageRoot() {
12194
+ const currentFile = fileURLToPath(import.meta.url);
12195
+ const currentDir = dirname(currentFile);
12196
+ return resolve(currentDir, "..", "..");
12197
+ }
12198
+ function resolveTemplatePath(relativePath) {
12199
+ const packageRoot = getPackageRoot();
12200
+ return join(packageRoot, "templates", relativePath);
12201
+ }
12202
+ async function listFiles(dir2, options = {}) {
12203
+ const fs = await import("node:fs/promises");
12204
+ const path = await import("node:path");
12205
+ const files = [];
12206
+ const entries = await fs.readdir(dir2, { withFileTypes: true });
12207
+ for (const entry of entries) {
12208
+ const fullPath = path.join(dir2, entry.name);
12209
+ if (entry.isDirectory() && options.recursive) {
12210
+ const subFiles = await listFiles(fullPath, options);
12211
+ files.push(...subFiles);
12212
+ } else if (entry.isFile()) {
12213
+ if (!options.pattern || matchesPattern(entry.name, options.pattern)) {
12214
+ files.push(fullPath);
12215
+ }
12216
+ }
12217
+ }
12218
+ return files;
12219
+ }
12220
+ function matchesPattern(filename, pattern) {
12221
+ const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
12222
+ const regex = new RegExp(`^${regexPattern}$`);
12223
+ return regex.test(filename);
12224
+ }
12225
+
12226
+ // src/core/provider.ts
12227
+ var ENV_SIGNALS = {
12228
+ claude: [
12229
+ "ANTHROPIC_API_KEY",
12230
+ "CLAUDE_CODE",
12231
+ "CLAUDE_CODE_EFFORT_LEVEL",
12232
+ "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS",
12233
+ "CLAUDE_CODE_ENABLE_TELEMETRY"
12234
+ ],
12235
+ codex: ["OPENAI_API_KEY", "OPENAI_ORG_ID", "OPENAI_PROJECT", "CODEX_HOME", "CODEX_PROJECT"]
12236
+ };
12237
+ var PROVIDER_ENV_OVERRIDES = ["OMCUSTOM_PROVIDER", "LLM_SERVICE"];
12238
+ function normalizeProvider(value) {
12239
+ if (!value)
12240
+ return null;
12241
+ const normalized = value.toLowerCase().trim();
12242
+ if (normalized === "claude" || normalized === "codex" || normalized === "auto") {
12243
+ return normalized;
12244
+ }
12245
+ return null;
12246
+ }
12247
+ function detectFromEnv(env) {
12248
+ for (const key of PROVIDER_ENV_OVERRIDES) {
12249
+ const override = normalizeProvider(env[key]);
12250
+ if (override && override !== "auto") {
12251
+ return {
12252
+ provider: override,
12253
+ source: "override",
12254
+ confidence: "high",
12255
+ reason: `env:${key}`
12256
+ };
12257
+ }
12258
+ }
12259
+ const claudeSignals = ENV_SIGNALS.claude.filter((key) => Boolean(env[key]));
12260
+ const codexSignals = ENV_SIGNALS.codex.filter((key) => Boolean(env[key]));
12261
+ const hasClaude = claudeSignals.length > 0;
12262
+ const hasCodex = codexSignals.length > 0;
12263
+ if (hasClaude && !hasCodex) {
12264
+ return {
12265
+ provider: "claude",
12266
+ source: "env",
12267
+ confidence: "medium",
12268
+ reason: `env:${claudeSignals[0]}`
12269
+ };
12270
+ }
12271
+ if (hasCodex && !hasClaude) {
12272
+ return {
12273
+ provider: "codex",
12274
+ source: "env",
12275
+ confidence: "medium",
12276
+ reason: `env:${codexSignals[0]}`
12277
+ };
12278
+ }
12279
+ return null;
12280
+ }
12281
+ async function detectFromProject(targetDir) {
12282
+ const claudeMarkers = [join2(targetDir, "CLAUDE.md"), join2(targetDir, ".claude")];
12283
+ const codexMarkers = [join2(targetDir, "AGENTS.md"), join2(targetDir, ".codex")];
12284
+ const claudeFound = await Promise.all(claudeMarkers.map((path) => fileExists(path)));
12285
+ const codexFound = await Promise.all(codexMarkers.map((path) => fileExists(path)));
12286
+ const hasClaude = claudeFound.some(Boolean);
12287
+ const hasCodex = codexFound.some(Boolean);
12288
+ if (hasClaude && !hasCodex) {
12289
+ return {
12290
+ provider: "claude",
12291
+ source: "project",
12292
+ confidence: "medium",
12293
+ reason: "project:claude"
12294
+ };
12295
+ }
12296
+ if (hasCodex && !hasClaude) {
12297
+ return {
12298
+ provider: "codex",
12299
+ source: "project",
12300
+ confidence: "medium",
12301
+ reason: "project:codex"
12302
+ };
12303
+ }
12304
+ return null;
12305
+ }
12306
+ async function detectFromConfig(targetDir) {
12307
+ const configPath = join2(targetDir, ".omcustomrc.json");
12308
+ if (!await fileExists(configPath)) {
12309
+ return null;
12310
+ }
12311
+ try {
12312
+ const config = await readJsonFile(configPath);
12313
+ const provider = normalizeProvider(config.provider);
12314
+ if (provider && provider !== "auto") {
12315
+ return {
12316
+ provider,
12317
+ source: "config",
12318
+ confidence: "high",
12319
+ reason: "config:provider"
12320
+ };
12321
+ }
12322
+ } catch {}
12323
+ return null;
12324
+ }
12325
+ async function detectProvider(options = {}) {
12326
+ const env = options.env ?? process.env;
12327
+ const override = options.override;
12328
+ const normalizedOverride = normalizeProvider(override);
12329
+ if (normalizedOverride && normalizedOverride !== "auto") {
12330
+ return {
12331
+ provider: normalizedOverride,
12332
+ source: "override",
12333
+ confidence: "high",
12334
+ reason: "override:option"
12335
+ };
12336
+ }
12337
+ if (options.targetDir) {
12338
+ const fromConfig = await detectFromConfig(options.targetDir);
12339
+ if (fromConfig) {
12340
+ return fromConfig;
12341
+ }
12342
+ }
12343
+ if (options.targetDir && options.preferProject) {
12344
+ const fromProject = await detectFromProject(options.targetDir);
12345
+ if (fromProject) {
12346
+ return fromProject;
12347
+ }
12348
+ }
12349
+ const fromEnv = detectFromEnv(env);
12350
+ if (fromEnv) {
12351
+ return fromEnv;
12352
+ }
12353
+ if (options.targetDir && !options.preferProject) {
12354
+ const fromProject = await detectFromProject(options.targetDir);
12355
+ if (fromProject) {
12356
+ return fromProject;
12357
+ }
12358
+ }
12359
+ return {
12360
+ provider: getDefaultProvider(),
12361
+ source: "default",
12362
+ confidence: "low",
12363
+ reason: "default"
12364
+ };
12365
+ }
12366
+
12018
12367
  // src/cli/doctor.ts
12019
12368
  async function pathExists(targetPath) {
12020
12369
  try {
@@ -12107,18 +12456,18 @@ async function countAgents(agentsDir) {
12107
12456
  } catch {}
12108
12457
  return count;
12109
12458
  }
12110
- async function checkClaudeMd(targetDir) {
12111
- const claudeMdPath = path.join(targetDir, "CLAUDE.md");
12112
- const exists2 = await pathExists(claudeMdPath);
12459
+ async function checkEntryDoc(targetDir, entryFile) {
12460
+ const entryPath = path.join(targetDir, entryFile);
12461
+ const exists2 = await pathExists(entryPath);
12113
12462
  return {
12114
- name: "CLAUDE.md",
12463
+ name: entryFile,
12115
12464
  status: exists2 ? "pass" : "fail",
12116
- message: exists2 ? i18n.t("cli.doctor.checks.claudeMd.pass") : i18n.t("cli.doctor.checks.claudeMd.fail"),
12465
+ message: exists2 ? i18n.t("cli.doctor.checks.entryMd.pass", { entry: entryFile }) : i18n.t("cli.doctor.checks.entryMd.fail", { entry: entryFile }),
12117
12466
  fixable: false
12118
12467
  };
12119
12468
  }
12120
- async function checkRules(targetDir) {
12121
- const rulesDir = path.join(targetDir, ".claude", "rules");
12469
+ async function checkRules(targetDir, rootDir = ".claude") {
12470
+ const rulesDir = path.join(targetDir, rootDir, "rules");
12122
12471
  const exists2 = await isDirectory(rulesDir);
12123
12472
  if (!exists2) {
12124
12473
  return {
@@ -12144,8 +12493,8 @@ async function checkRules(targetDir) {
12144
12493
  fixable: false
12145
12494
  };
12146
12495
  }
12147
- async function checkAgents(targetDir) {
12148
- const agentsDir = path.join(targetDir, ".claude", "agents");
12496
+ async function checkAgents(targetDir, rootDir = ".claude") {
12497
+ const agentsDir = path.join(targetDir, rootDir, "agents");
12149
12498
  const exists2 = await isDirectory(agentsDir);
12150
12499
  if (!exists2) {
12151
12500
  return {
@@ -12171,8 +12520,8 @@ async function checkAgents(targetDir) {
12171
12520
  fixable: false
12172
12521
  };
12173
12522
  }
12174
- async function checkSymlinks(targetDir) {
12175
- const skillsDir = path.join(targetDir, ".claude", "skills");
12523
+ async function checkSymlinks(targetDir, rootDir = ".claude") {
12524
+ const skillsDir = path.join(targetDir, rootDir, "skills");
12176
12525
  const brokenSymlinks = [];
12177
12526
  if (await isDirectory(skillsDir)) {
12178
12527
  const skillSymlinks = await findRefsSymlinks(skillsDir);
@@ -12233,8 +12582,8 @@ async function checkIndexFiles(targetDir) {
12233
12582
  fixable: false
12234
12583
  };
12235
12584
  }
12236
- async function checkSkills(targetDir) {
12237
- const skillsDir = path.join(targetDir, ".claude", "skills");
12585
+ async function checkSkills(targetDir, rootDir = ".claude") {
12586
+ const skillsDir = path.join(targetDir, rootDir, "skills");
12238
12587
  const exists2 = await isDirectory(skillsDir);
12239
12588
  if (!exists2) {
12240
12589
  return {
@@ -12287,14 +12636,14 @@ async function checkGuides(targetDir) {
12287
12636
  fixable: false
12288
12637
  };
12289
12638
  }
12290
- async function checkHooks(targetDir) {
12291
- const hooksDir = path.join(targetDir, ".claude", "hooks");
12639
+ async function checkHooks(targetDir, rootDir = ".claude") {
12640
+ const hooksDir = path.join(targetDir, rootDir, "hooks");
12292
12641
  const exists2 = await isDirectory(hooksDir);
12293
12642
  if (!exists2) {
12294
12643
  return {
12295
12644
  name: "Hooks",
12296
12645
  status: "fail",
12297
- message: ".claude/hooks/ directory not found",
12646
+ message: `${rootDir}/hooks/ directory not found`,
12298
12647
  fixable: true
12299
12648
  };
12300
12649
  }
@@ -12303,7 +12652,7 @@ async function checkHooks(targetDir) {
12303
12652
  return {
12304
12653
  name: "Hooks",
12305
12654
  status: "warn",
12306
- message: ".claude/hooks/ directory is empty",
12655
+ message: `${rootDir}/hooks/ directory is empty`,
12307
12656
  fixable: false
12308
12657
  };
12309
12658
  }
@@ -12314,14 +12663,14 @@ async function checkHooks(targetDir) {
12314
12663
  fixable: false
12315
12664
  };
12316
12665
  }
12317
- async function checkContexts(targetDir) {
12318
- const contextsDir = path.join(targetDir, ".claude", "contexts");
12666
+ async function checkContexts(targetDir, rootDir = ".claude") {
12667
+ const contextsDir = path.join(targetDir, rootDir, "contexts");
12319
12668
  const exists2 = await isDirectory(contextsDir);
12320
12669
  if (!exists2) {
12321
12670
  return {
12322
12671
  name: "Contexts",
12323
12672
  status: "fail",
12324
- message: ".claude/contexts/ directory not found",
12673
+ message: `${rootDir}/contexts/ directory not found`,
12325
12674
  fixable: true
12326
12675
  };
12327
12676
  }
@@ -12330,7 +12679,7 @@ async function checkContexts(targetDir) {
12330
12679
  return {
12331
12680
  name: "Contexts",
12332
12681
  status: "warn",
12333
- message: ".claude/contexts/ directory is empty",
12682
+ message: `${rootDir}/contexts/ directory is empty`,
12334
12683
  fixable: false
12335
12684
  };
12336
12685
  }
@@ -12359,14 +12708,14 @@ async function createMissingDirectory(dirPath) {
12359
12708
  return false;
12360
12709
  }
12361
12710
  }
12362
- async function fixSingleIssue(check, targetDir) {
12711
+ async function fixSingleIssue(check, targetDir, rootDir = ".claude") {
12363
12712
  const fixMap = {
12364
- Rules: () => createMissingDirectory(path.join(targetDir, ".claude", "rules")),
12365
- Agents: () => createMissingDirectory(path.join(targetDir, ".claude", "agents")),
12366
- Skills: () => createMissingDirectory(path.join(targetDir, ".claude", "skills")),
12713
+ Rules: () => createMissingDirectory(path.join(targetDir, rootDir, "rules")),
12714
+ Agents: () => createMissingDirectory(path.join(targetDir, rootDir, "agents")),
12715
+ Skills: () => createMissingDirectory(path.join(targetDir, rootDir, "skills")),
12367
12716
  Guides: () => createMissingDirectory(path.join(targetDir, "guides")),
12368
- Hooks: () => createMissingDirectory(path.join(targetDir, ".claude", "hooks")),
12369
- Contexts: () => createMissingDirectory(path.join(targetDir, ".claude", "contexts")),
12717
+ Hooks: () => createMissingDirectory(path.join(targetDir, rootDir, "hooks")),
12718
+ Contexts: () => createMissingDirectory(path.join(targetDir, rootDir, "contexts")),
12370
12719
  Symlinks: async () => {
12371
12720
  if (!check.details || check.details.length === 0)
12372
12721
  return false;
@@ -12378,7 +12727,7 @@ async function fixSingleIssue(check, targetDir) {
12378
12727
  const fixer = fixMap[check.name];
12379
12728
  return fixer ? fixer() : false;
12380
12729
  }
12381
- async function fixIssues(checks, targetDir) {
12730
+ async function fixIssues(checks, targetDir, rootDir = ".claude") {
12382
12731
  const fixedChecks = [];
12383
12732
  for (const check of checks) {
12384
12733
  if (check.status !== "fail" || !check.fixable) {
@@ -12386,7 +12735,7 @@ async function fixIssues(checks, targetDir) {
12386
12735
  continue;
12387
12736
  }
12388
12737
  console.log(i18n.t("cli.doctor.fixing", { name: check.name }));
12389
- const fixed = await fixSingleIssue(check, targetDir);
12738
+ const fixed = await fixSingleIssue(check, targetDir, rootDir);
12390
12739
  fixedChecks.push(fixed ? { ...check, fixed: true, message: i18n.t("cli.doctor.fixed", { name: check.name }) } : check);
12391
12740
  }
12392
12741
  return fixedChecks;
@@ -12413,23 +12762,29 @@ async function doctorCommand(options = {}) {
12413
12762
  const targetDir = process.cwd();
12414
12763
  console.log(i18n.t("cli.doctor.checking"));
12415
12764
  console.log("");
12765
+ const detection = await detectProvider({
12766
+ targetDir,
12767
+ override: options.provider,
12768
+ preferProject: true
12769
+ });
12770
+ const layout = getProviderLayout(detection.provider);
12416
12771
  let checks = await Promise.all([
12417
- checkClaudeMd(targetDir),
12418
- checkRules(targetDir),
12419
- checkAgents(targetDir),
12420
- checkSkills(targetDir),
12421
- checkSymlinks(targetDir),
12772
+ checkEntryDoc(targetDir, layout.entryFile),
12773
+ checkRules(targetDir, layout.rootDir),
12774
+ checkAgents(targetDir, layout.rootDir),
12775
+ checkSkills(targetDir, layout.rootDir),
12776
+ checkSymlinks(targetDir, layout.rootDir),
12422
12777
  checkIndexFiles(targetDir),
12423
12778
  checkGuides(targetDir),
12424
- checkHooks(targetDir),
12425
- checkContexts(targetDir)
12779
+ checkHooks(targetDir, layout.rootDir),
12780
+ checkContexts(targetDir, layout.rootDir)
12426
12781
  ]);
12427
12782
  if (options.fix) {
12428
12783
  const hasFixableIssues = checks.some((c) => c.status === "fail" && c.fixable);
12429
12784
  if (hasFixableIssues) {
12430
12785
  console.log(i18n.t("cli.doctor.applyingFixes"));
12431
12786
  console.log("");
12432
- checks = await fixIssues(checks, targetDir);
12787
+ checks = await fixIssues(checks, targetDir, layout.rootDir);
12433
12788
  console.log("");
12434
12789
  }
12435
12790
  }
@@ -12471,144 +12826,11 @@ async function doctorCommand(options = {}) {
12471
12826
  }
12472
12827
 
12473
12828
  // src/cli/init.ts
12474
- import { join as join4 } from "node:path";
12829
+ import { join as join5 } from "node:path";
12475
12830
 
12476
12831
  // src/core/installer.ts
12477
12832
  import { copyFile as fsCopyFile, rename } from "node:fs/promises";
12478
- import { basename, join as join3 } from "node:path";
12479
-
12480
- // src/utils/fs.ts
12481
- import { dirname, join, resolve } from "node:path";
12482
- import { fileURLToPath } from "node:url";
12483
- async function fileExists(path2) {
12484
- const fs2 = await import("node:fs/promises");
12485
- try {
12486
- await fs2.access(path2);
12487
- return true;
12488
- } catch {
12489
- return false;
12490
- }
12491
- }
12492
- async function ensureDirectory(path2) {
12493
- const fs2 = await import("node:fs/promises");
12494
- await fs2.mkdir(path2, { recursive: true });
12495
- }
12496
- function shouldSkipEntry(entryName, options) {
12497
- if (options.exclude?.some((pattern) => matchesPattern(entryName, pattern))) {
12498
- return true;
12499
- }
12500
- if (options.include && !options.include.some((pattern) => matchesPattern(entryName, pattern))) {
12501
- return true;
12502
- }
12503
- return false;
12504
- }
12505
- async function handleSymlink(srcPath, destPath, options, fs2) {
12506
- const destExists = await fileExists(destPath);
12507
- if (destExists && !options.overwrite) {
12508
- return;
12509
- }
12510
- if (options.preserveSymlinks !== false) {
12511
- await copyPreservedSymlink(srcPath, destPath, destExists, fs2);
12512
- } else {
12513
- await copyFollowedSymlink(srcPath, destPath, destExists, options, fs2);
12514
- }
12515
- }
12516
- async function copyPreservedSymlink(srcPath, destPath, destExists, fs2) {
12517
- const linkTarget = await fs2.readlink(srcPath);
12518
- if (destExists) {
12519
- await fs2.unlink(destPath);
12520
- }
12521
- await fs2.symlink(linkTarget, destPath);
12522
- }
12523
- async function copyFollowedSymlink(srcPath, destPath, destExists, options, fs2) {
12524
- const realPath = await fs2.realpath(srcPath);
12525
- const stat = await fs2.stat(realPath);
12526
- if (stat.isDirectory()) {
12527
- await copyDirectory(realPath, destPath, options);
12528
- return;
12529
- }
12530
- if (destExists) {
12531
- await fs2.unlink(destPath);
12532
- }
12533
- await fs2.copyFile(realPath, destPath);
12534
- }
12535
- async function handleFile(srcPath, destPath, options, fs2) {
12536
- const destExists = await fileExists(destPath);
12537
- if (destExists && !options.overwrite) {
12538
- return;
12539
- }
12540
- await fs2.copyFile(srcPath, destPath);
12541
- if (options.preserveTimestamps) {
12542
- const stats = await fs2.stat(srcPath);
12543
- await fs2.utimes(destPath, stats.atime, stats.mtime);
12544
- }
12545
- }
12546
- async function copyDirectory(src, dest, options = {}) {
12547
- const fs2 = await import("node:fs/promises");
12548
- const path2 = await import("node:path");
12549
- await ensureDirectory(dest);
12550
- const entries = await fs2.readdir(src, { withFileTypes: true });
12551
- for (const entry of entries) {
12552
- if (shouldSkipEntry(entry.name, options)) {
12553
- continue;
12554
- }
12555
- const srcPath = path2.join(src, entry.name);
12556
- const destPath = path2.join(dest, entry.name);
12557
- if (entry.isSymbolicLink()) {
12558
- await handleSymlink(srcPath, destPath, options, fs2);
12559
- } else if (entry.isDirectory()) {
12560
- await copyDirectory(srcPath, destPath, options);
12561
- } else if (entry.isFile()) {
12562
- await handleFile(srcPath, destPath, options, fs2);
12563
- }
12564
- }
12565
- }
12566
- async function readJsonFile(path2) {
12567
- const fs2 = await import("node:fs/promises");
12568
- const content = await fs2.readFile(path2, "utf-8");
12569
- return JSON.parse(content);
12570
- }
12571
- async function writeJsonFile(path2, data) {
12572
- const fs2 = await import("node:fs/promises");
12573
- const content = JSON.stringify(data, null, 2);
12574
- await fs2.writeFile(path2, content, "utf-8");
12575
- }
12576
- async function readTextFile(path2) {
12577
- const fs2 = await import("node:fs/promises");
12578
- return fs2.readFile(path2, "utf-8");
12579
- }
12580
- function getPackageRoot() {
12581
- const currentFile = fileURLToPath(import.meta.url);
12582
- const currentDir = dirname(currentFile);
12583
- return resolve(currentDir, "..", "..");
12584
- }
12585
- function resolveTemplatePath(relativePath) {
12586
- const packageRoot = getPackageRoot();
12587
- return join(packageRoot, "templates", relativePath);
12588
- }
12589
- async function listFiles(dir2, options = {}) {
12590
- const fs2 = await import("node:fs/promises");
12591
- const path2 = await import("node:path");
12592
- const files = [];
12593
- const entries = await fs2.readdir(dir2, { withFileTypes: true });
12594
- for (const entry of entries) {
12595
- const fullPath = path2.join(dir2, entry.name);
12596
- if (entry.isDirectory() && options.recursive) {
12597
- const subFiles = await listFiles(fullPath, options);
12598
- files.push(...subFiles);
12599
- } else if (entry.isFile()) {
12600
- if (!options.pattern || matchesPattern(entry.name, options.pattern)) {
12601
- files.push(fullPath);
12602
- }
12603
- }
12604
- }
12605
- return files;
12606
- }
12607
- function matchesPattern(filename, pattern) {
12608
- const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
12609
- const regex = new RegExp(`^${regexPattern}$`);
12610
- return regex.test(filename);
12611
- }
12833
+ import { basename, join as join4 } from "node:path";
12612
12834
 
12613
12835
  // src/utils/logger.ts
12614
12836
  var currentOptions = {
@@ -12659,7 +12881,7 @@ var MESSAGES = {
12659
12881
  "install.start": "Initializing oh-my-customcode...",
12660
12882
  "install.success": "Successfully initialized!",
12661
12883
  "install.failed": "Installation failed: {{error}}",
12662
- "install.exists": "Existing .claude/ directory found",
12884
+ "install.exists": "Existing {{rootDir}} directory found",
12663
12885
  "install.backup": "Backed up existing files to: {{path}}",
12664
12886
  "install.directories_created": "Directory structure created",
12665
12887
  "install.component_skipped": "Skipped {{component}} (already exists)",
@@ -12667,6 +12889,9 @@ var MESSAGES = {
12667
12889
  "install.template_not_found": "Template not found for {{component}}: {{path}}",
12668
12890
  "install.claude_md_installed": "CLAUDE.md installed ({{language}})",
12669
12891
  "install.claude_md_not_found": "CLAUDE.md template not found for {{language}}",
12892
+ "install.entry_md_installed": "{{entry}} installed ({{language}})",
12893
+ "install.entry_md_not_found": "{{entry}} template not found for {{language}}",
12894
+ "install.entry_md_skipped": "{{entry}} skipped ({{reason}})",
12670
12895
  "update.start": "Checking for updates...",
12671
12896
  "update.success": "Updated from {{from}} to {{to}}",
12672
12897
  "update.failed": "Update failed: {{error}}",
@@ -12687,7 +12912,7 @@ var MESSAGES = {
12687
12912
  "install.start": "oh-my-customcode 초기화 중...",
12688
12913
  "install.success": "초기화 완료!",
12689
12914
  "install.failed": "설치 실패: {{error}}",
12690
- "install.exists": "기존 .claude/ 디렉토리 발견",
12915
+ "install.exists": "기존 {{rootDir}} 디렉토리 발견",
12691
12916
  "install.backup": "기존 파일 백업 완료: {{path}}",
12692
12917
  "install.directories_created": "디렉토리 구조 생성 완료",
12693
12918
  "install.component_skipped": "{{component}} 건너뜀 (이미 존재)",
@@ -12695,6 +12920,9 @@ var MESSAGES = {
12695
12920
  "install.template_not_found": "{{component}} 템플릿 없음: {{path}}",
12696
12921
  "install.claude_md_installed": "CLAUDE.md 설치 완료 ({{language}})",
12697
12922
  "install.claude_md_not_found": "{{language}}용 CLAUDE.md 템플릿 없음",
12923
+ "install.entry_md_installed": "{{entry}} 설치 완료 ({{language}})",
12924
+ "install.entry_md_not_found": "{{language}}용 {{entry}} 템플릿 없음",
12925
+ "install.entry_md_skipped": "{{entry}} 건너뜀 ({{reason}})",
12698
12926
  "update.start": "업데이트 확인 중...",
12699
12927
  "update.success": "{{from}}에서 {{to}}로 업데이트 완료",
12700
12928
  "update.failed": "업데이트 실패: {{error}}",
@@ -12794,7 +13022,7 @@ function success(messageKey, params) {
12794
13022
  }
12795
13023
 
12796
13024
  // src/core/config.ts
12797
- import { join as join2 } from "node:path";
13025
+ import { join as join3 } from "node:path";
12798
13026
  var CONFIG_FILE = ".omcustomrc.json";
12799
13027
  var CURRENT_CONFIG_VERSION = 1;
12800
13028
  function getDefaultConfig() {
@@ -12827,7 +13055,7 @@ function getDefaultPreferences() {
12827
13055
  };
12828
13056
  }
12829
13057
  function getConfigPath(targetDir) {
12830
- return join2(targetDir, CONFIG_FILE);
13058
+ return join3(targetDir, CONFIG_FILE);
12831
13059
  }
12832
13060
  async function loadConfig(targetDir) {
12833
13061
  const configPath = getConfigPath(targetDir);
@@ -12888,18 +13116,10 @@ function migrateConfig(config) {
12888
13116
  }
12889
13117
 
12890
13118
  // src/core/installer.ts
12891
- var COMPONENT_PATHS = {
12892
- "claude-md": "",
12893
- rules: ".claude/rules",
12894
- agents: ".claude/agents",
12895
- skills: ".claude/skills",
12896
- guides: "guides",
12897
- hooks: ".claude/hooks",
12898
- contexts: ".claude/contexts"
12899
- };
13119
+ var DEFAULT_LANGUAGE2 = "en";
12900
13120
  function getTemplateDir() {
12901
13121
  const packageRoot = getPackageRoot();
12902
- return join3(packageRoot, "templates");
13122
+ return join4(packageRoot, "templates");
12903
13123
  }
12904
13124
  function createInstallResult(targetDir) {
12905
13125
  return {
@@ -12917,21 +13137,22 @@ async function ensureTargetDirectory(targetDir) {
12917
13137
  await ensureDirectory(targetDir);
12918
13138
  }
12919
13139
  }
12920
- async function handleBackup(targetDir, shouldBackup, result) {
13140
+ async function handleBackup(targetDir, provider, shouldBackup, result) {
12921
13141
  if (!shouldBackup)
12922
13142
  return;
12923
- const backupPaths = await backupExistingInstallation(targetDir);
13143
+ const backupPaths = await backupExistingInstallation(targetDir, provider);
12924
13144
  result.backedUpPaths.push(...backupPaths);
12925
13145
  if (backupPaths.length > 0) {
12926
13146
  info("install.backup", { path: backupPaths[0] });
12927
13147
  }
12928
13148
  }
12929
- async function checkAndWarnExisting(targetDir, force, backup, result) {
13149
+ async function checkAndWarnExisting(targetDir, provider, force, backup, result) {
12930
13150
  if (force || backup)
12931
13151
  return;
12932
- const existingPaths = await checkExistingPaths(targetDir);
13152
+ const existingPaths = await checkExistingPaths(targetDir, provider);
12933
13153
  if (existingPaths.length > 0) {
12934
- warn("install.exists");
13154
+ const layout = getProviderLayout(provider);
13155
+ warn("install.exists", { rootDir: layout.rootDir });
12935
13156
  result.warnings.push(`Existing files found: ${existingPaths.join(", ")}. Use --force to overwrite or --backup to backup first.`);
12936
13157
  }
12937
13158
  }
@@ -12941,15 +13162,15 @@ async function verifyTemplateDirectory() {
12941
13162
  throw new Error(`Template directory not found: ${templateDir}`);
12942
13163
  }
12943
13164
  }
12944
- async function installAllComponents(targetDir, options, result) {
13165
+ async function installAllComponents(targetDir, provider, options, result) {
12945
13166
  const components = options.components || getAllComponents();
12946
13167
  for (const component of components) {
12947
- await installSingleComponent(targetDir, component, options, result);
13168
+ await installSingleComponent(targetDir, provider, component, options, result);
12948
13169
  }
12949
13170
  }
12950
- async function installSingleComponent(targetDir, component, options, result) {
13171
+ async function installSingleComponent(targetDir, provider, component, options, result) {
12951
13172
  try {
12952
- const installed = await installComponent(targetDir, component, options);
13173
+ const installed = await installComponent(targetDir, provider, component, options);
12953
13174
  if (installed) {
12954
13175
  result.installedComponents.push(component);
12955
13176
  } else {
@@ -12960,34 +13181,36 @@ async function installSingleComponent(targetDir, component, options, result) {
12960
13181
  result.warnings.push(`Failed to install ${component}: ${message}`);
12961
13182
  }
12962
13183
  }
12963
- async function installClaudeMdWithTracking(targetDir, options, result) {
12964
- const language = options.language || "en";
13184
+ async function installEntryDocWithTracking(targetDir, provider, options, result) {
13185
+ const language = options.language ?? DEFAULT_LANGUAGE2;
12965
13186
  const overwrite = !!(options.force || options.backup);
12966
- const installed = await installClaudeMd(targetDir, language, overwrite);
13187
+ const installed = await installEntryDoc(targetDir, provider, language, overwrite);
12967
13188
  if (installed) {
12968
- result.installedComponents.push("claude-md");
13189
+ result.installedComponents.push("entry-md");
12969
13190
  } else {
12970
- result.skippedComponents.push("claude-md");
13191
+ result.skippedComponents.push("entry-md");
12971
13192
  }
12972
13193
  }
12973
- async function updateInstallConfig(targetDir, options, installedComponents) {
13194
+ async function updateInstallConfig(targetDir, provider, options, installedComponents) {
12974
13195
  const config = await loadConfig(targetDir);
12975
- config.language = options.language || "en";
13196
+ config.language = options.language ?? DEFAULT_LANGUAGE2;
13197
+ config.provider = provider;
12976
13198
  config.installedAt = new Date().toISOString();
12977
13199
  config.installedComponents = installedComponents;
12978
13200
  await saveConfig(targetDir, config);
12979
13201
  }
12980
13202
  async function install(options) {
12981
13203
  const result = createInstallResult(options.targetDir);
13204
+ const provider = options.provider ?? "claude";
12982
13205
  try {
12983
13206
  info("install.start", { targetDir: options.targetDir });
12984
13207
  await ensureTargetDirectory(options.targetDir);
12985
- await handleBackup(options.targetDir, !!options.backup, result);
12986
- await checkAndWarnExisting(options.targetDir, !!options.force, !!options.backup, result);
13208
+ await handleBackup(options.targetDir, provider, !!options.backup, result);
13209
+ await checkAndWarnExisting(options.targetDir, provider, !!options.force, !!options.backup, result);
12987
13210
  await verifyTemplateDirectory();
12988
- await installAllComponents(options.targetDir, options, result);
12989
- await installClaudeMdWithTracking(options.targetDir, options, result);
12990
- await updateInstallConfig(options.targetDir, options, result.installedComponents);
13211
+ await installAllComponents(options.targetDir, provider, options, result);
13212
+ await installEntryDocWithTracking(options.targetDir, provider, options, result);
13213
+ await updateInstallConfig(options.targetDir, provider, options, result.installedComponents);
12991
13214
  result.success = true;
12992
13215
  success("install.success");
12993
13216
  } catch (err) {
@@ -13000,12 +13223,15 @@ async function install(options) {
13000
13223
  function getAllComponents() {
13001
13224
  return ["rules", "agents", "skills", "guides", "hooks", "contexts"];
13002
13225
  }
13003
- async function installComponent(targetDir, component, options) {
13004
- const templatePath = COMPONENT_PATHS[component];
13226
+ async function installComponent(targetDir, provider, component, options) {
13227
+ if (component === "entry-md") {
13228
+ return false;
13229
+ }
13230
+ const templatePath = getComponentPath(provider, component);
13005
13231
  if (!templatePath) {
13006
13232
  return false;
13007
13233
  }
13008
- const destPath = join3(targetDir, templatePath);
13234
+ const destPath = join4(targetDir, templatePath);
13009
13235
  const destExists = await fileExists(destPath);
13010
13236
  if (destExists && !options.force && !options.backup) {
13011
13237
  debug("install.component_skipped", { component });
@@ -13024,51 +13250,54 @@ async function installComponent(targetDir, component, options) {
13024
13250
  debug("install.component_installed", { component });
13025
13251
  return true;
13026
13252
  }
13027
- async function installClaudeMd(targetDir, language, overwrite = false) {
13028
- const templateFile = `CLAUDE.md.${language}`;
13253
+ async function installEntryDoc(targetDir, provider, language, overwrite = false) {
13254
+ const layout = getProviderLayout(provider);
13255
+ const templateFile = getEntryTemplateName(provider, language);
13029
13256
  const srcPath = resolveTemplatePath(templateFile);
13030
- const destPath = join3(targetDir, "CLAUDE.md");
13257
+ const destPath = join4(targetDir, layout.entryFile);
13031
13258
  if (!await fileExists(srcPath)) {
13032
- warn("install.claude_md_not_found", { language, path: srcPath });
13259
+ warn("install.entry_md_not_found", { language, path: srcPath, entry: layout.entryFile });
13033
13260
  return false;
13034
13261
  }
13035
13262
  const destExists = await fileExists(destPath);
13036
13263
  if (destExists && !overwrite) {
13037
- debug("install.claude_md_skipped", { reason: "exists", language });
13264
+ debug("install.entry_md_skipped", { reason: "exists", language, entry: layout.entryFile });
13038
13265
  return false;
13039
13266
  }
13040
13267
  await fsCopyFile(srcPath, destPath);
13041
- debug("install.claude_md_installed", { language });
13268
+ debug("install.entry_md_installed", { language, entry: layout.entryFile });
13042
13269
  return true;
13043
13270
  }
13044
13271
  async function backupExisting(sourcePath, backupDir) {
13045
13272
  const name = basename(sourcePath);
13046
- const backupPath = join3(backupDir, name);
13273
+ const backupPath = join4(backupDir, name);
13047
13274
  await rename(sourcePath, backupPath);
13048
13275
  return backupPath;
13049
13276
  }
13050
- async function checkExistingPaths(targetDir) {
13051
- const pathsToCheck = ["CLAUDE.md", ".claude", "guides"];
13277
+ async function checkExistingPaths(targetDir, provider) {
13278
+ const layout = getProviderLayout(provider);
13279
+ const pathsToCheck = [layout.entryFile, layout.rootDir, "guides"];
13052
13280
  const existingPaths = [];
13053
13281
  for (const relativePath of pathsToCheck) {
13054
- const fullPath = join3(targetDir, relativePath);
13282
+ const fullPath = join4(targetDir, relativePath);
13055
13283
  if (await fileExists(fullPath)) {
13056
13284
  existingPaths.push(relativePath);
13057
13285
  }
13058
13286
  }
13059
13287
  return existingPaths;
13060
13288
  }
13061
- async function backupExistingInstallation(targetDir) {
13062
- const existingPaths = await checkExistingPaths(targetDir);
13289
+ async function backupExistingInstallation(targetDir, provider) {
13290
+ const layout = getProviderLayout(provider);
13291
+ const existingPaths = await checkExistingPaths(targetDir, provider);
13063
13292
  if (existingPaths.length === 0) {
13064
13293
  return [];
13065
13294
  }
13066
13295
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
13067
- const backupDir = join3(targetDir, `.claude-backup-${timestamp}`);
13296
+ const backupDir = join4(targetDir, `${layout.backupDirPrefix}${timestamp}`);
13068
13297
  await ensureDirectory(backupDir);
13069
13298
  const backedUpPaths = [];
13070
13299
  for (const relativePath of existingPaths) {
13071
- const fullPath = join3(targetDir, relativePath);
13300
+ const fullPath = join4(targetDir, relativePath);
13072
13301
  try {
13073
13302
  const backupPath = await backupExisting(fullPath, backupDir);
13074
13303
  backedUpPaths.push(backupPath);
@@ -13082,22 +13311,25 @@ async function backupExistingInstallation(targetDir) {
13082
13311
  }
13083
13312
 
13084
13313
  // src/cli/init.ts
13085
- async function checkExistingInstallation(targetDir) {
13086
- const claudeDir = join4(targetDir, ".claude");
13087
- return fileExists(claudeDir);
13314
+ async function checkExistingInstallation(targetDir, provider) {
13315
+ const layout = getProviderLayout(provider);
13316
+ const rootDir = join5(targetDir, layout.rootDir);
13317
+ return fileExists(rootDir);
13088
13318
  }
13089
- var CLAUDE_SUBDIR_COMPONENTS = new Set(["rules", "hooks", "contexts", "agents", "skills"]);
13090
- function componentToPath(targetDir, component) {
13091
- if (component === "claude-md") {
13092
- return join4(targetDir, "CLAUDE.md");
13319
+ var PROVIDER_SUBDIR_COMPONENTS = new Set(["rules", "hooks", "contexts", "agents", "skills"]);
13320
+ function componentToPath(targetDir, provider, component) {
13321
+ if (component === "entry-md") {
13322
+ const layout = getProviderLayout(provider);
13323
+ return join5(targetDir, layout.entryFile);
13093
13324
  }
13094
- if (CLAUDE_SUBDIR_COMPONENTS.has(component)) {
13095
- return join4(targetDir, ".claude", component);
13325
+ if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
13326
+ const layout = getProviderLayout(provider);
13327
+ return join5(targetDir, layout.rootDir, component);
13096
13328
  }
13097
- return join4(targetDir, component);
13329
+ return join5(targetDir, component);
13098
13330
  }
13099
- function buildInstalledPaths(targetDir, components) {
13100
- return components.map((component) => componentToPath(targetDir, component));
13331
+ function buildInstalledPaths(targetDir, provider, components) {
13332
+ return components.map((component) => componentToPath(targetDir, provider, component));
13101
13333
  }
13102
13334
  function logItems(items, formatter) {
13103
13335
  for (const item of items) {
@@ -13122,10 +13354,6 @@ function createFailureResult(errorMessage) {
13122
13354
  errors: [errorMessage]
13123
13355
  };
13124
13356
  }
13125
- function notifyExistingInstallation() {
13126
- console.log(i18n.t("cli.init.exists"));
13127
- console.log(i18n.t("cli.init.backing_up"));
13128
- }
13129
13357
  function logInstallResultInfo(result) {
13130
13358
  logItems(result.backedUpPaths, (path2) => console.log(i18n.t("cli.init.backedUp", { path: path2 })));
13131
13359
  logItems(result.warnings, (warning) => console.warn(`Warning: ${warning}`));
@@ -13134,21 +13362,26 @@ async function initCommand(options) {
13134
13362
  const targetDir = process.cwd();
13135
13363
  console.log(i18n.t("cli.init.start"));
13136
13364
  try {
13137
- const exists2 = await checkExistingInstallation(targetDir);
13365
+ const detection = await detectProvider({ targetDir, override: options.provider });
13366
+ const provider = detection.provider;
13367
+ const layout = getProviderLayout(provider);
13368
+ const exists2 = await checkExistingInstallation(targetDir, provider);
13138
13369
  if (exists2) {
13139
- notifyExistingInstallation();
13370
+ console.log(i18n.t("cli.init.exists", { rootDir: layout.rootDir }));
13371
+ console.log(i18n.t("cli.init.backing_up"));
13140
13372
  }
13141
13373
  console.log(i18n.t("cli.init.copying"));
13142
13374
  const installResult = await install({
13143
13375
  targetDir,
13144
13376
  language: options.lang,
13377
+ provider,
13145
13378
  force: options.force ?? false,
13146
13379
  backup: exists2
13147
13380
  });
13148
13381
  if (!installResult.success) {
13149
13382
  return createFailureResult(installResult.error || "Unknown error");
13150
13383
  }
13151
- const installedPaths = buildInstalledPaths(targetDir, installResult.installedComponents);
13384
+ const installedPaths = buildInstalledPaths(targetDir, provider, installResult.installedComponents);
13152
13385
  logInstallResultInfo(installResult);
13153
13386
  logSuccessDetails(installedPaths, installResult.skippedComponents);
13154
13387
  return {
@@ -13164,7 +13397,7 @@ async function initCommand(options) {
13164
13397
  }
13165
13398
 
13166
13399
  // src/cli/list.ts
13167
- import { basename as basename2, dirname as dirname2, join as join5, relative } from "node:path";
13400
+ import { basename as basename2, dirname as dirname2, join as join6, relative } from "node:path";
13168
13401
  var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
13169
13402
  function parseKeyValue(line) {
13170
13403
  const colonIndex = line.indexOf(":");
@@ -13227,13 +13460,13 @@ function extractAgentTypeFromFilename(filename) {
13227
13460
  const prefix = name.split("-")[0];
13228
13461
  return prefixMap[prefix] || "unknown";
13229
13462
  }
13230
- function extractSkillCategoryFromPath(skillPath, baseDir) {
13231
- const relativePath = relative(join5(baseDir, ".claude", "skills"), skillPath);
13463
+ function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
13464
+ const relativePath = relative(join6(baseDir, rootDir, "skills"), skillPath);
13232
13465
  const parts = relativePath.split("/").filter(Boolean);
13233
13466
  return parts[0] || "unknown";
13234
13467
  }
13235
13468
  function extractGuideCategoryFromPath(guidePath, baseDir) {
13236
- const relativePath = relative(join5(baseDir, "guides"), guidePath);
13469
+ const relativePath = relative(join6(baseDir, "guides"), guidePath);
13237
13470
  const parts = relativePath.split("/").filter(Boolean);
13238
13471
  return parts[0] || "unknown";
13239
13472
  }
@@ -13326,8 +13559,8 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
13326
13559
  return;
13327
13560
  }
13328
13561
  }
13329
- async function getAgents(targetDir) {
13330
- const agentsDir = join5(targetDir, ".claude", "agents");
13562
+ async function getAgents(targetDir, rootDir = ".claude") {
13563
+ const agentsDir = join6(targetDir, rootDir, "agents");
13331
13564
  if (!await fileExists(agentsDir))
13332
13565
  return [];
13333
13566
  try {
@@ -13349,20 +13582,20 @@ async function getAgents(targetDir) {
13349
13582
  return [];
13350
13583
  }
13351
13584
  }
13352
- async function getSkills(targetDir) {
13353
- const skillsDir = join5(targetDir, ".claude", "skills");
13585
+ async function getSkills(targetDir, rootDir = ".claude") {
13586
+ const skillsDir = join6(targetDir, rootDir, "skills");
13354
13587
  if (!await fileExists(skillsDir))
13355
13588
  return [];
13356
13589
  try {
13357
13590
  const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
13358
13591
  const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
13359
13592
  const skillDir = dirname2(skillMdPath);
13360
- const indexYamlPath = join5(skillDir, "index.yaml");
13593
+ const indexYamlPath = join6(skillDir, "index.yaml");
13361
13594
  const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
13362
13595
  return {
13363
13596
  name: basename2(skillDir),
13364
13597
  type: "skill",
13365
- category: extractSkillCategoryFromPath(skillDir, targetDir),
13598
+ category: extractSkillCategoryFromPath(skillDir, targetDir, rootDir),
13366
13599
  path: relative(targetDir, skillDir),
13367
13600
  description,
13368
13601
  version
@@ -13374,7 +13607,7 @@ async function getSkills(targetDir) {
13374
13607
  }
13375
13608
  }
13376
13609
  async function getGuides(targetDir) {
13377
- const guidesDir = join5(targetDir, "guides");
13610
+ const guidesDir = join6(targetDir, "guides");
13378
13611
  if (!await fileExists(guidesDir))
13379
13612
  return [];
13380
13613
  try {
@@ -13395,8 +13628,8 @@ async function getGuides(targetDir) {
13395
13628
  }
13396
13629
  }
13397
13630
  var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
13398
- async function getRules(targetDir) {
13399
- const rulesDir = join5(targetDir, ".claude", "rules");
13631
+ async function getRules(targetDir, rootDir = ".claude") {
13632
+ const rulesDir = join6(targetDir, rootDir, "rules");
13400
13633
  if (!await fileExists(rulesDir))
13401
13634
  return [];
13402
13635
  try {
@@ -13460,8 +13693,8 @@ ${type} (${components.length}):`);
13460
13693
  function formatAsJson(components) {
13461
13694
  console.log(JSON.stringify(components, null, 2));
13462
13695
  }
13463
- async function getHooks(targetDir) {
13464
- const hooksDir = join5(targetDir, ".claude", "hooks");
13696
+ async function getHooks(targetDir, rootDir = ".claude") {
13697
+ const hooksDir = join6(targetDir, rootDir, "hooks");
13465
13698
  if (!await fileExists(hooksDir))
13466
13699
  return [];
13467
13700
  try {
@@ -13478,8 +13711,8 @@ async function getHooks(targetDir) {
13478
13711
  return [];
13479
13712
  }
13480
13713
  }
13481
- async function getContexts(targetDir) {
13482
- const contextsDir = join5(targetDir, ".claude", "contexts");
13714
+ async function getContexts(targetDir, rootDir = ".claude") {
13715
+ const contextsDir = join6(targetDir, rootDir, "contexts");
13483
13716
  if (!await fileExists(contextsDir))
13484
13717
  return [];
13485
13718
  try {
@@ -13504,7 +13737,7 @@ async function getContexts(targetDir) {
13504
13737
  var COMPONENT_GETTERS = {
13505
13738
  agents: getAgents,
13506
13739
  skills: getSkills,
13507
- guides: getGuides,
13740
+ guides: async (dir2) => getGuides(dir2),
13508
13741
  rules: getRules,
13509
13742
  hooks: getHooks,
13510
13743
  contexts: getContexts
@@ -13518,14 +13751,14 @@ function displayComponents(components, type, format) {
13518
13751
  formatAsTable(components, type);
13519
13752
  }
13520
13753
  }
13521
- async function handleListAll(targetDir, format) {
13754
+ async function handleListAll(targetDir, rootDir, format) {
13522
13755
  const [agents, skills, guides, rules, hooks, contexts] = await Promise.all([
13523
- getAgents(targetDir),
13524
- getSkills(targetDir),
13756
+ getAgents(targetDir, rootDir),
13757
+ getSkills(targetDir, rootDir),
13525
13758
  getGuides(targetDir),
13526
- getRules(targetDir),
13527
- getHooks(targetDir),
13528
- getContexts(targetDir)
13759
+ getRules(targetDir, rootDir),
13760
+ getHooks(targetDir, rootDir),
13761
+ getContexts(targetDir, rootDir)
13529
13762
  ]);
13530
13763
  if (format !== "json") {
13531
13764
  displayComponents(agents, "agents", format);
@@ -13542,7 +13775,13 @@ async function listCommand(type = "all", options = {}) {
13542
13775
  const format = options.format || "table";
13543
13776
  console.log(i18n.t("cli.list.scanning"));
13544
13777
  try {
13545
- const components = type === "all" ? await handleListAll(targetDir, format) : await COMPONENT_GETTERS[type](targetDir);
13778
+ const detection = await detectProvider({
13779
+ targetDir,
13780
+ override: options.provider,
13781
+ preferProject: true
13782
+ });
13783
+ const layout = getProviderLayout(detection.provider);
13784
+ const components = type === "all" ? await handleListAll(targetDir, layout.rootDir, format) : await COMPONENT_GETTERS[type](targetDir, layout.rootDir);
13546
13785
  if (type === "all" && format === "json") {
13547
13786
  formatAsJson(components);
13548
13787
  } else if (type !== "all") {
@@ -13632,16 +13871,20 @@ var packageJson = require2("../../package.json");
13632
13871
  function createProgram() {
13633
13872
  const program2 = new Command;
13634
13873
  program2.name("omcustom").description(i18n.t("cli.description")).version(packageJson.version, "-v, --version", i18n.t("cli.versionOption"));
13635
- program2.command("init").description(i18n.t("cli.init.description")).option("-l, --lang <language>", i18n.t("cli.init.langOption"), "en").action(async (options) => {
13874
+ program2.command("init").description(i18n.t("cli.init.description")).option("-l, --lang <language>", i18n.t("cli.init.langOption"), "en").option("-p, --provider <provider>", i18n.t("cli.init.providerOption"), "auto").action(async (options) => {
13636
13875
  await initCommand(options);
13637
13876
  });
13638
13877
  program2.command("update").description(i18n.t("cli.update.description")).action(async () => {
13639
13878
  await updateCommand();
13640
13879
  });
13641
- program2.command("list").description(i18n.t("cli.list.description")).argument("[type]", i18n.t("cli.list.typeArgument"), "all").option("-f, --format <format>", "Output format: table, json, or simple", "table").option("--verbose", "Show detailed information").action(async (type, options) => {
13642
- await listCommand(type, { format: options.format, verbose: options.verbose });
13880
+ program2.command("list").description(i18n.t("cli.list.description")).argument("[type]", i18n.t("cli.list.typeArgument"), "all").option("-f, --format <format>", "Output format: table, json, or simple", "table").option("--verbose", "Show detailed information").option("-p, --provider <provider>", i18n.t("cli.list.providerOption"), "auto").action(async (type, options) => {
13881
+ await listCommand(type, {
13882
+ format: options.format,
13883
+ verbose: options.verbose,
13884
+ provider: options.provider
13885
+ });
13643
13886
  });
13644
- program2.command("doctor").description(i18n.t("cli.doctor.description")).option("--fix", i18n.t("cli.doctor.fixOption")).action(async (options) => {
13887
+ program2.command("doctor").description(i18n.t("cli.doctor.description")).option("--fix", i18n.t("cli.doctor.fixOption")).option("-p, --provider <provider>", i18n.t("cli.doctor.providerOption"), "auto").action(async (options) => {
13645
13888
  await doctorCommand(options);
13646
13889
  });
13647
13890
  return program2;