oh-my-customcode 0.10.3 → 0.12.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # oh-my-customcode
6
6
 
7
- > **Your Claude Code, Your Way**
7
+ > **Your Coding Agent Stack, Your Way**
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/oh-my-customcode.svg)](https://www.npmjs.com/package/oh-my-customcode)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -13,9 +13,9 @@
13
13
 
14
14
  **[한국어 문서 (Korean)](./README_ko.md)**
15
15
 
16
- **The easiest way to customize Claude Code with agents, skills, and rules.**
16
+ **The easiest way to customize Claude Code and OpenAI Codex with agents, skills, and rules.**
17
17
 
18
- Like oh-my-zsh transformed shell customization, oh-my-customcode makes personalizing your Claude Code experience simple, powerful, and fun.
18
+ Like oh-my-zsh transformed shell customization, oh-my-customcode makes personalizing your coding agent workflow simple, powerful, and fun.
19
19
 
20
20
  ## What Makes It Special
21
21
 
@@ -38,19 +38,19 @@ cd your-project
38
38
  omcustom init
39
39
  ```
40
40
 
41
- That's it. You now have a fully configured Claude Code environment.
41
+ That's it. You now have a fully configured agent environment.
42
42
 
43
43
  ---
44
44
 
45
- > **Codex CLI Support**: Experimental Codex CLI mode is available. See `AGENTS.md` and `.codex/` for Codex-native configuration.
45
+ > **Dual Provider Support**: Claude and Codex are both supported. Use `--provider claude|codex` (or auto-detect) during `omcustom init`/`update`/`list`/`doctor`.
46
46
 
47
47
  ## Customization First
48
48
 
49
- This is what oh-my-customcode is all about. **Making Claude Code yours.**
49
+ This is what oh-my-customcode is all about. **Making your coding workflow yours.**
50
50
 
51
- ### Just Tell Claude What You Need
51
+ ### Just Describe What You Need
52
52
 
53
- No manual file editing. Describe what you want in natural language, and the orchestrator delegates to the right agent:
53
+ No manual file editing. Describe what you want in natural language, and routing skills plus manager agents delegate to the right sub-agent:
54
54
 
55
55
  ```
56
56
  "Create a migration review expert agent"
@@ -63,13 +63,13 @@ No manual file editing. Describe what you want in natural language, and the orch
63
63
 
64
64
  ```
65
65
  User (natural language)
66
- /create-agent (routing skill)
66
+ secretary-routing (routing skill)
67
67
  → mgr-creator:sonnet — scaffolds agent, registers, verifies
68
68
  → mgr-updater:sonnet — syncs documentation
69
69
  → mgr-supplier:haiku — checks dependencies
70
70
  ```
71
71
 
72
- Claude Code's routing system analyzes your request, routes it to the appropriate skill and agent, and the sub-agent handles everything automatically.
72
+ The routing chain analyzes your request, maps it to the appropriate skill and manager agent, and the selected sub-agent handles execution automatically.
73
73
 
74
74
  ### Sub-Agent Model
75
75
 
@@ -81,6 +81,8 @@ Each sub-agent runs on an optimized model for its task type:
81
81
  | `sonnet` | General tasks (default) | Agent creation, code generation |
82
82
  | `haiku` | Fast, simple operations | File search, validation |
83
83
 
84
+ > Note: Claude examples keep `opus/sonnet/haiku`. Codex-native templates use profile terms: `reasoning/balanced/fast`.
85
+
84
86
  Claude Code selects the appropriate model and parallelizes independent tasks (up to 4 concurrent sub-agents):
85
87
 
86
88
  ```
@@ -126,18 +128,110 @@ Claude Code selects the appropriate model and parallelizes independent tasks (up
126
128
  | **QA** | 3 | qa-planner, qa-writer, qa-engineer |
127
129
  | **Total** | **42** | |
128
130
 
129
- ### Skills (51)
131
+ Canonical agent IDs (`templates/.claude/agents/*.md`):
132
+
133
+ ```text
134
+ arch-documenter
135
+ arch-speckit-agent
136
+ be-express-expert
137
+ be-fastapi-expert
138
+ be-go-backend-expert
139
+ be-nestjs-expert
140
+ be-springboot-expert
141
+ db-postgres-expert
142
+ db-redis-expert
143
+ db-supabase-expert
144
+ de-airflow-expert
145
+ de-dbt-expert
146
+ de-kafka-expert
147
+ de-pipeline-expert
148
+ de-snowflake-expert
149
+ de-spark-expert
150
+ fe-svelte-agent
151
+ fe-vercel-agent
152
+ fe-vuejs-agent
153
+ infra-aws-expert
154
+ infra-docker-expert
155
+ lang-golang-expert
156
+ lang-java21-expert
157
+ lang-kotlin-expert
158
+ lang-python-expert
159
+ lang-rust-expert
160
+ lang-typescript-expert
161
+ mgr-claude-code-bible
162
+ mgr-creator
163
+ mgr-gitnerd
164
+ mgr-sauron
165
+ mgr-supplier
166
+ mgr-sync-checker
167
+ mgr-updater
168
+ qa-engineer
169
+ qa-planner
170
+ qa-writer
171
+ sys-memory-keeper
172
+ sys-naggy
173
+ tool-bun-expert
174
+ tool-npm-expert
175
+ tool-optimizer
176
+ ```
130
177
 
131
- Includes slash commands and capabilities:
178
+ ### Skills (51)
132
179
 
133
- - **Development** (8): Go, Python, TypeScript, Kotlin, Rust, Java, React, Vercel
134
- - **Backend** (5): FastAPI, Spring Boot, Express, NestJS, Go Backend
135
- - **Data Engineering** (6): Airflow, dbt, Spark, Kafka, Snowflake, Pipeline
136
- - **Database** (3): Supabase, PostgreSQL, Redis
137
- - **Infrastructure** (2): Docker, AWS
138
- - **System** (2): Memory management, result aggregation
139
- - **Orchestration** (4): secretary-routing, dev-lead-routing, de-lead-routing, qa-lead-routing
140
- - **Slash Commands** (20+): /create-agent, /code-review, /audit-dependencies, /sync-check, /commit, /pr, and more
180
+ Canonical skill IDs (`templates/.claude/skills/*/SKILL.md`):
181
+
182
+ ```text
183
+ airflow-best-practices
184
+ audit-agents
185
+ aws-best-practices
186
+ claude-code-bible
187
+ create-agent
188
+ dbt-best-practices
189
+ de-lead-routing
190
+ dev-lead-routing
191
+ dev-refactor
192
+ dev-review
193
+ docker-best-practices
194
+ fastapi-best-practices
195
+ fix-refs
196
+ go-backend-best-practices
197
+ go-best-practices
198
+ help
199
+ intent-detection
200
+ kafka-best-practices
201
+ kotlin-best-practices
202
+ lists
203
+ memory-management
204
+ memory-recall
205
+ memory-save
206
+ monitoring-setup
207
+ npm-audit
208
+ npm-publish
209
+ npm-version
210
+ optimize-analyze
211
+ optimize-bundle
212
+ optimize-report
213
+ pipeline-architecture-patterns
214
+ postgres-best-practices
215
+ python-best-practices
216
+ qa-lead-routing
217
+ react-best-practices
218
+ redis-best-practices
219
+ result-aggregation
220
+ rust-best-practices
221
+ sauron-watch
222
+ secretary-routing
223
+ snowflake-best-practices
224
+ spark-best-practices
225
+ springboot-best-practices
226
+ status
227
+ supabase-postgres-best-practices
228
+ typescript-best-practices
229
+ update-docs
230
+ update-external
231
+ vercel-deploy
232
+ web-design-guidelines
233
+ writing-clearly-and-concisely
234
+ ```
141
235
 
142
236
  ### Guides (22)
143
237
 
@@ -160,12 +254,18 @@ Comprehensive reference documentation covering:
160
254
 
161
255
  ### Hooks (1)
162
256
 
163
- Event-driven automation for Claude Code lifecycle events (PreToolUse, PostToolUse, etc.).
257
+ Event-driven automation for agent lifecycle events (PreToolUse, PostToolUse, etc.).
164
258
 
165
259
  ### Contexts (4)
166
260
 
167
261
  Shared context files for cross-agent knowledge and mode configurations.
168
262
 
263
+ ### Packages
264
+
265
+ | Package | Version | Description |
266
+ |---------|---------|-------------|
267
+ | [ontology-rag](packages/ontology-rag/) | 0.3.0 | Ontology+RAG context engine for intelligent context loading. Reduces token usage by 75-95% through hierarchical loading, graph-based routing, and adaptive compression. |
268
+
169
269
  ---
170
270
 
171
271
  ## CLI Commands
@@ -255,7 +355,7 @@ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
255
355
  ---
256
356
 
257
357
  <p align="center">
258
- <strong>Your Claude Code. Your rules. Your way.</strong>
358
+ <strong>Your coding workflow. Your rules. Your way.</strong>
259
359
  </p>
260
360
 
261
361
  <p align="center">
package/dist/cli/index.js CHANGED
@@ -12552,6 +12552,7 @@ var MESSAGES = {
12552
12552
  "install.entry_md_skipped": "{{entry}} skipped ({{reason}})",
12553
12553
  "update.start": "Checking for updates...",
12554
12554
  "update.success": "Updated from {{from}} to {{to}}",
12555
+ "update.components_synced": "Components synced (version {{version}}): {{components}}",
12555
12556
  "update.failed": "Update failed: {{error}}",
12556
12557
  "update.no_updates": "Already up to date",
12557
12558
  "update.backup_created": "Backup created at: {{path}}",
@@ -12583,6 +12584,7 @@ var MESSAGES = {
12583
12584
  "install.entry_md_skipped": "{{entry}} 건너뜀 ({{reason}})",
12584
12585
  "update.start": "업데이트 확인 중...",
12585
12586
  "update.success": "{{from}}에서 {{to}}로 업데이트 완료",
12587
+ "update.components_synced": "컴포넌트 동기화 완료 (버전 {{version}}): {{components}}",
12586
12588
  "update.failed": "업데이트 실패: {{error}}",
12587
12589
  "update.no_updates": "이미 최신 버전입니다",
12588
12590
  "update.backup_created": "백업 생성됨: {{path}}",
@@ -13515,7 +13517,7 @@ async function doctorCommand(options = {}) {
13515
13517
  }
13516
13518
 
13517
13519
  // src/cli/init.ts
13518
- import { join as join5 } from "node:path";
13520
+ import { join as join6 } from "node:path";
13519
13521
 
13520
13522
  // src/core/installer.ts
13521
13523
  import { readFile as fsReadFile, writeFile as fsWriteFile, rename } from "node:fs/promises";
@@ -13976,23 +13978,77 @@ async function backupExistingInstallation(targetDir, provider) {
13976
13978
  return backedUpPaths.length > 0 ? [backupDir] : [];
13977
13979
  }
13978
13980
 
13981
+ // src/core/mcp-config.ts
13982
+ import { execSync as execSync2 } from "node:child_process";
13983
+ import { writeFile } from "node:fs/promises";
13984
+ import { join as join5 } from "node:path";
13985
+ async function generateMCPConfig(targetDir, provider) {
13986
+ const layout = getProviderLayout(provider);
13987
+ const mcpConfigPath = join5(targetDir, ".mcp.json");
13988
+ const ontologyDir = join5(layout.rootDir, "ontology");
13989
+ const ontologyExists = await fileExists(join5(targetDir, ontologyDir));
13990
+ if (!ontologyExists) {
13991
+ return;
13992
+ }
13993
+ const config = {
13994
+ mcpServers: {
13995
+ "ontology-rag": {
13996
+ type: "stdio",
13997
+ command: "python",
13998
+ args: ["-m", "ontology_rag.mcp_server"],
13999
+ env: {
14000
+ ONTOLOGY_DIR: ontologyDir
14001
+ }
14002
+ }
14003
+ }
14004
+ };
14005
+ const existingConfigPath = mcpConfigPath;
14006
+ if (await fileExists(existingConfigPath)) {
14007
+ try {
14008
+ const { readFile } = await import("node:fs/promises");
14009
+ const existingContent = await readFile(existingConfigPath, "utf-8");
14010
+ const existing = JSON.parse(existingContent);
14011
+ if (!existing.mcpServers?.["ontology-rag"]) {
14012
+ existing.mcpServers = existing.mcpServers || {};
14013
+ existing.mcpServers["ontology-rag"] = config.mcpServers["ontology-rag"];
14014
+ await writeFile(mcpConfigPath, `${JSON.stringify(existing, null, 2)}
14015
+ `);
14016
+ }
14017
+ } catch {
14018
+ await writeFile(mcpConfigPath, `${JSON.stringify(config, null, 2)}
14019
+ `);
14020
+ }
14021
+ } else {
14022
+ await writeFile(mcpConfigPath, `${JSON.stringify(config, null, 2)}
14023
+ `);
14024
+ }
14025
+ }
14026
+ async function checkPythonAvailable() {
14027
+ try {
14028
+ execSync2("python --version", { stdio: "pipe" });
14029
+ return true;
14030
+ } catch {
14031
+ return false;
14032
+ }
14033
+ }
14034
+
13979
14035
  // src/cli/init.ts
13980
14036
  async function checkExistingInstallation(targetDir, provider) {
13981
14037
  const layout = getProviderLayout(provider);
13982
- const rootDir = join5(targetDir, layout.rootDir);
14038
+ const rootDir = join6(targetDir, layout.rootDir);
13983
14039
  return fileExists(rootDir);
13984
14040
  }
13985
14041
  var PROVIDER_SUBDIR_COMPONENTS = new Set(["rules", "hooks", "contexts", "agents", "skills"]);
13986
14042
  function componentToPath(targetDir, provider, component) {
13987
14043
  if (component === "entry-md") {
13988
14044
  const layout = getProviderLayout(provider);
13989
- return join5(targetDir, layout.entryFile);
14045
+ return join6(targetDir, layout.entryFile);
13990
14046
  }
13991
14047
  if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
13992
14048
  const layout = getProviderLayout(provider);
13993
- return join5(targetDir, layout.rootDir, component);
14049
+ return join6(targetDir, layout.rootDir, component);
13994
14050
  }
13995
- return join5(targetDir, component);
14051
+ return join6(targetDir, component);
13996
14052
  }
13997
14053
  function buildInstalledPaths(targetDir, provider, components) {
13998
14054
  return components.map((component) => componentToPath(targetDir, provider, component));
@@ -14050,6 +14106,17 @@ async function initCommand(options) {
14050
14106
  const installedPaths = buildInstalledPaths(targetDir, provider, installResult.installedComponents);
14051
14107
  logInstallResultInfo(installResult);
14052
14108
  logSuccessDetails(installedPaths, installResult.skippedComponents);
14109
+ const pythonAvailable = await checkPythonAvailable();
14110
+ if (pythonAvailable) {
14111
+ try {
14112
+ await generateMCPConfig(targetDir, provider);
14113
+ } catch {
14114
+ console.warn("Warning: Failed to generate MCP config. You can configure it manually.");
14115
+ }
14116
+ } else {
14117
+ console.warn("Warning: Python not found. Skipping MCP server configuration.");
14118
+ console.warn("Install Python 3.10+ and ontology-rag to enable MCP integration.");
14119
+ }
14053
14120
  return {
14054
14121
  success: true,
14055
14122
  message: i18n.t("cli.init.success"),
@@ -14063,7 +14130,7 @@ async function initCommand(options) {
14063
14130
  }
14064
14131
 
14065
14132
  // src/cli/list.ts
14066
- import { basename as basename2, dirname as dirname2, join as join6, relative as relative2 } from "node:path";
14133
+ import { basename as basename2, dirname as dirname2, join as join7, relative as relative2 } from "node:path";
14067
14134
  var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
14068
14135
  function parseKeyValue(line) {
14069
14136
  const colonIndex = line.indexOf(":");
@@ -14127,12 +14194,12 @@ function extractAgentTypeFromFilename(filename) {
14127
14194
  return prefixMap[prefix] || "unknown";
14128
14195
  }
14129
14196
  function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
14130
- const relativePath = relative2(join6(baseDir, rootDir, "skills"), skillPath);
14197
+ const relativePath = relative2(join7(baseDir, rootDir, "skills"), skillPath);
14131
14198
  const parts = relativePath.split("/").filter(Boolean);
14132
14199
  return parts[0] || "unknown";
14133
14200
  }
14134
14201
  function extractGuideCategoryFromPath(guidePath, baseDir) {
14135
- const relativePath = relative2(join6(baseDir, "guides"), guidePath);
14202
+ const relativePath = relative2(join7(baseDir, "guides"), guidePath);
14136
14203
  const parts = relativePath.split("/").filter(Boolean);
14137
14204
  return parts[0] || "unknown";
14138
14205
  }
@@ -14226,7 +14293,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
14226
14293
  }
14227
14294
  }
14228
14295
  async function getAgents(targetDir, rootDir = ".claude", config) {
14229
- const agentsDir = join6(targetDir, rootDir, "agents");
14296
+ const agentsDir = join7(targetDir, rootDir, "agents");
14230
14297
  if (!await fileExists(agentsDir))
14231
14298
  return [];
14232
14299
  try {
@@ -14254,7 +14321,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
14254
14321
  }
14255
14322
  }
14256
14323
  async function getSkills(targetDir, rootDir = ".claude", config) {
14257
- const skillsDir = join6(targetDir, rootDir, "skills");
14324
+ const skillsDir = join7(targetDir, rootDir, "skills");
14258
14325
  if (!await fileExists(skillsDir))
14259
14326
  return [];
14260
14327
  try {
@@ -14264,7 +14331,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
14264
14331
  const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
14265
14332
  const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
14266
14333
  const skillDir = dirname2(skillMdPath);
14267
- const indexYamlPath = join6(skillDir, "index.yaml");
14334
+ const indexYamlPath = join7(skillDir, "index.yaml");
14268
14335
  const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
14269
14336
  const relativePath = relative2(targetDir, skillDir);
14270
14337
  return {
@@ -14283,7 +14350,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
14283
14350
  }
14284
14351
  }
14285
14352
  async function getGuides(targetDir, config) {
14286
- const guidesDir = join6(targetDir, "guides");
14353
+ const guidesDir = join7(targetDir, "guides");
14287
14354
  if (!await fileExists(guidesDir))
14288
14355
  return [];
14289
14356
  try {
@@ -14310,7 +14377,7 @@ async function getGuides(targetDir, config) {
14310
14377
  }
14311
14378
  var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
14312
14379
  async function getRules(targetDir, rootDir = ".claude", config) {
14313
- const rulesDir = join6(targetDir, rootDir, "rules");
14380
+ const rulesDir = join7(targetDir, rootDir, "rules");
14314
14381
  if (!await fileExists(rulesDir))
14315
14382
  return [];
14316
14383
  try {
@@ -14382,7 +14449,7 @@ function formatAsJson(components) {
14382
14449
  console.log(JSON.stringify(components, null, 2));
14383
14450
  }
14384
14451
  async function getHooks(targetDir, rootDir = ".claude") {
14385
- const hooksDir = join6(targetDir, rootDir, "hooks");
14452
+ const hooksDir = join7(targetDir, rootDir, "hooks");
14386
14453
  if (!await fileExists(hooksDir))
14387
14454
  return [];
14388
14455
  try {
@@ -14400,7 +14467,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
14400
14467
  }
14401
14468
  }
14402
14469
  async function getContexts(targetDir, rootDir = ".claude") {
14403
- const contextsDir = join6(targetDir, rootDir, "contexts");
14470
+ const contextsDir = join7(targetDir, rootDir, "contexts");
14404
14471
  if (!await fileExists(contextsDir))
14405
14472
  return [];
14406
14473
  try {
@@ -14485,7 +14552,7 @@ async function listCommand(type = "all", options = {}) {
14485
14552
  }
14486
14553
 
14487
14554
  // src/core/updater.ts
14488
- import { join as join7 } from "node:path";
14555
+ import { join as join8 } from "node:path";
14489
14556
 
14490
14557
  // src/core/entry-merger.ts
14491
14558
  var MANAGED_START = "<!-- omcustom:start -->";
@@ -14737,7 +14804,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
14737
14804
  }
14738
14805
  async function updateEntryDoc(targetDir, provider, config, options) {
14739
14806
  const layout = getProviderLayout(provider);
14740
- const entryPath = join7(targetDir, layout.entryFile);
14807
+ const entryPath = join8(targetDir, layout.entryFile);
14741
14808
  const templateName = getEntryTemplateName2(provider, config.language);
14742
14809
  const templatePath = resolveTemplatePath(templateName);
14743
14810
  if (!await fileExists(templatePath)) {
@@ -14798,7 +14865,14 @@ async function update(options) {
14798
14865
  config.lastUpdated = new Date().toISOString();
14799
14866
  await saveConfig(options.targetDir, config);
14800
14867
  result.success = true;
14801
- success("update.success", { from: result.previousVersion, to: result.newVersion });
14868
+ if (result.previousVersion !== result.newVersion) {
14869
+ success("update.success", { from: result.previousVersion, to: result.newVersion });
14870
+ } else if (result.updatedComponents.length > 0) {
14871
+ success("update.components_synced", {
14872
+ version: result.newVersion,
14873
+ components: result.updatedComponents.join(", ")
14874
+ });
14875
+ }
14802
14876
  } catch (err) {
14803
14877
  const message = err instanceof Error ? err.message : String(err);
14804
14878
  result.error = message;
@@ -14853,7 +14927,7 @@ async function updateComponent(targetDir, provider, component, customizations, o
14853
14927
  const preservedFiles = [];
14854
14928
  const componentPath = getComponentPath2(provider, component);
14855
14929
  const srcPath = resolveTemplatePath(componentPath);
14856
- const destPath = join7(targetDir, componentPath);
14930
+ const destPath = join8(targetDir, componentPath);
14857
14931
  const customComponents = config.customComponents || [];
14858
14932
  const skipPaths = [];
14859
14933
  if (customizations && !options.forceOverwriteAll) {
@@ -14867,7 +14941,7 @@ async function updateComponent(targetDir, provider, component, customizations, o
14867
14941
  }
14868
14942
  }
14869
14943
  const path2 = await import("node:path");
14870
- const normalizedSkipPaths = skipPaths.map((p) => path2.relative(destPath, join7(targetDir, p)));
14944
+ const normalizedSkipPaths = skipPaths.map((p) => path2.relative(destPath, join8(targetDir, p)));
14871
14945
  await copyDirectory(srcPath, destPath, {
14872
14946
  overwrite: true,
14873
14947
  skipPaths: normalizedSkipPaths.length > 0 ? normalizedSkipPaths : undefined
@@ -14887,26 +14961,26 @@ function getComponentPath2(provider, component) {
14887
14961
  }
14888
14962
  async function backupInstallation(targetDir, provider) {
14889
14963
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
14890
- const backupDir = join7(targetDir, `.omcustom-backup-${timestamp}`);
14964
+ const backupDir = join8(targetDir, `.omcustom-backup-${timestamp}`);
14891
14965
  const fs2 = await import("node:fs/promises");
14892
14966
  await ensureDirectory(backupDir);
14893
14967
  const layout = getProviderLayout(provider);
14894
14968
  const dirsToBackup = [layout.rootDir, "guides"];
14895
14969
  for (const dir2 of dirsToBackup) {
14896
- const srcPath = join7(targetDir, dir2);
14970
+ const srcPath = join8(targetDir, dir2);
14897
14971
  if (await fileExists(srcPath)) {
14898
- const destPath = join7(backupDir, dir2);
14972
+ const destPath = join8(backupDir, dir2);
14899
14973
  await copyDirectory(srcPath, destPath, { overwrite: true });
14900
14974
  }
14901
14975
  }
14902
- const entryPath = join7(targetDir, layout.entryFile);
14976
+ const entryPath = join8(targetDir, layout.entryFile);
14903
14977
  if (await fileExists(entryPath)) {
14904
- await fs2.copyFile(entryPath, join7(backupDir, layout.entryFile));
14978
+ await fs2.copyFile(entryPath, join8(backupDir, layout.entryFile));
14905
14979
  }
14906
14980
  return backupDir;
14907
14981
  }
14908
14982
  async function loadCustomizationManifest(targetDir) {
14909
- const manifestPath = join7(targetDir, CUSTOMIZATION_MANIFEST_FILE);
14983
+ const manifestPath = join8(targetDir, CUSTOMIZATION_MANIFEST_FILE);
14910
14984
  if (await fileExists(manifestPath)) {
14911
14985
  return readJsonFile(manifestPath);
14912
14986
  }
package/dist/index.js CHANGED
@@ -238,6 +238,7 @@ var MESSAGES = {
238
238
  "install.entry_md_skipped": "{{entry}} skipped ({{reason}})",
239
239
  "update.start": "Checking for updates...",
240
240
  "update.success": "Updated from {{from}} to {{to}}",
241
+ "update.components_synced": "Components synced (version {{version}}): {{components}}",
241
242
  "update.failed": "Update failed: {{error}}",
242
243
  "update.no_updates": "Already up to date",
243
244
  "update.backup_created": "Backup created at: {{path}}",
@@ -269,6 +270,7 @@ var MESSAGES = {
269
270
  "install.entry_md_skipped": "{{entry}} 건너뜀 ({{reason}})",
270
271
  "update.start": "업데이트 확인 중...",
271
272
  "update.success": "{{from}}에서 {{to}}로 업데이트 완료",
273
+ "update.components_synced": "컴포넌트 동기화 완료 (버전 {{version}}): {{components}}",
272
274
  "update.failed": "업데이트 실패: {{error}}",
273
275
  "update.no_updates": "이미 최신 버전입니다",
274
276
  "update.backup_created": "백업 생성됨: {{path}}",
@@ -1508,7 +1510,14 @@ async function update(options) {
1508
1510
  config.lastUpdated = new Date().toISOString();
1509
1511
  await saveConfig(options.targetDir, config);
1510
1512
  result.success = true;
1511
- success("update.success", { from: result.previousVersion, to: result.newVersion });
1513
+ if (result.previousVersion !== result.newVersion) {
1514
+ success("update.success", { from: result.previousVersion, to: result.newVersion });
1515
+ } else if (result.updatedComponents.length > 0) {
1516
+ success("update.components_synced", {
1517
+ version: result.newVersion,
1518
+ components: result.updatedComponents.join(", ")
1519
+ });
1520
+ }
1512
1521
  } catch (err) {
1513
1522
  const message = err instanceof Error ? err.message : String(err);
1514
1523
  result.error = message;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oh-my-customcode",
3
- "version": "0.10.3",
4
- "description": "Batteries-included agent harness for Claude Code",
3
+ "version": "0.12.0",
4
+ "description": "Batteries-included agent harness for Claude Code and OpenAI Codex",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "omcustom": "./dist/cli/index.js"
@@ -59,6 +59,9 @@
59
59
  "keywords": [
60
60
  "claude",
61
61
  "claude-code",
62
+ "codex",
63
+ "codex-cli",
64
+ "openai",
62
65
  "ai",
63
66
  "agent",
64
67
  "cli"