oh-my-customcode 0.9.2 → 0.9.3
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 +29 -21
- package/dist/cli/index.js +540 -58
- package/dist/index.js +0 -16
- package/package.json +1 -1
- package/templates/.codex/codex-native-hash.txt +0 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Like oh-my-zsh transformed shell customization, oh-my-customcode makes personali
|
|
|
16
16
|
|
|
17
17
|
| Feature | Description |
|
|
18
18
|
|---------|-------------|
|
|
19
|
-
| **Batteries Included** | 42 agents,
|
|
19
|
+
| **Batteries Included** | 42 agents, 52 skills, 22 guides, 18 rules, 1 hook, 1 context - ready to use out of the box |
|
|
20
20
|
| **Sub-Agent Model** | Supports hierarchical agent orchestration with specialized roles |
|
|
21
21
|
| **Dead Simple Customization** | Create a folder + markdown file = new agent or skill |
|
|
22
22
|
| **Mix and Match** | Use built-in components, create your own, or combine both |
|
|
@@ -47,6 +47,11 @@ oh-my-customcode can operate in both Claude-native and Codex-native modes. The C
|
|
|
47
47
|
4. Project markers (`AGENTS.md`/`.codex` vs `CLAUDE.md`/`.claude`)
|
|
48
48
|
5. Default: `claude`
|
|
49
49
|
|
|
50
|
+
**Claude Mode** generates `.claude/` directory with `CLAUDE.md` entry point.
|
|
51
|
+
**Codex Mode** generates `.codex/` directory with `AGENTS.md` entry point.
|
|
52
|
+
|
|
53
|
+
Both modes install the same agents, skills, guides, and rules — only the directory layout and entry file differ.
|
|
54
|
+
|
|
50
55
|
## Customization First
|
|
51
56
|
|
|
52
57
|
This is what oh-my-customcode is all about. **Making Claude Code yours.**
|
|
@@ -129,7 +134,7 @@ Claude Code selects the appropriate model and parallelizes independent tasks (up
|
|
|
129
134
|
| **QA** | 3 | qa-planner, qa-writer, qa-engineer |
|
|
130
135
|
| **Total** | **42** | |
|
|
131
136
|
|
|
132
|
-
### Skills (
|
|
137
|
+
### Skills (52)
|
|
133
138
|
|
|
134
139
|
Includes slash commands and capabilities:
|
|
135
140
|
|
|
@@ -165,9 +170,9 @@ Comprehensive reference documentation covering:
|
|
|
165
170
|
|
|
166
171
|
Event-driven automation for Claude Code lifecycle events (PreToolUse, PostToolUse, etc.).
|
|
167
172
|
|
|
168
|
-
### Contexts (
|
|
173
|
+
### Contexts (1)
|
|
169
174
|
|
|
170
|
-
Shared context
|
|
175
|
+
Shared context file for cross-agent knowledge and mode configurations.
|
|
171
176
|
|
|
172
177
|
---
|
|
173
178
|
|
|
@@ -183,6 +188,13 @@ Shared context files for cross-agent knowledge and mode configurations.
|
|
|
183
188
|
| `omcustom doctor` | Verify installation health |
|
|
184
189
|
| `omcustom doctor --fix` | Auto-fix common issues |
|
|
185
190
|
|
|
191
|
+
**Global Options:**
|
|
192
|
+
| Option | Description |
|
|
193
|
+
|--------|-------------|
|
|
194
|
+
| `--skip-version-check` | Skip CLI tool version pre-flight check |
|
|
195
|
+
| `-v, --version` | Show version number |
|
|
196
|
+
| `-h, --help` | Show help |
|
|
197
|
+
|
|
186
198
|
---
|
|
187
199
|
|
|
188
200
|
## Project Structure
|
|
@@ -191,25 +203,21 @@ After `omcustom init`:
|
|
|
191
203
|
|
|
192
204
|
```
|
|
193
205
|
your-project/
|
|
194
|
-
├── CLAUDE.md # Entry point for Claude
|
|
195
|
-
└── .claude/
|
|
206
|
+
├── CLAUDE.md # Entry point for Claude (or AGENTS.md for Codex)
|
|
207
|
+
└── .claude/ # (or .codex/)
|
|
196
208
|
├── rules/ # Behavior rules (18 total)
|
|
197
209
|
├── hooks/ # Event hooks (1 total)
|
|
198
|
-
├── contexts/ # Context files (
|
|
199
|
-
├── agents/ #
|
|
200
|
-
│ ├── lang-golang-expert
|
|
201
|
-
│ ├── be-fastapi-expert
|
|
202
|
-
│ ├──
|
|
203
|
-
│
|
|
210
|
+
├── contexts/ # Context files (1 total)
|
|
211
|
+
├── agents/ # Agent definitions (42 flat .md files)
|
|
212
|
+
│ ├── lang-golang-expert.md
|
|
213
|
+
│ ├── be-fastapi-expert.md
|
|
214
|
+
│ ├── mgr-creator.md
|
|
215
|
+
│ └── ...
|
|
216
|
+
├── skills/ # Skill modules (52 directories, each with SKILL.md)
|
|
217
|
+
│ ├── go-best-practices/
|
|
218
|
+
│ ├── react-best-practices/
|
|
219
|
+
│ ├── secretary-routing/
|
|
204
220
|
│ └── ...
|
|
205
|
-
├── skills/ # All skills (51 total, includes slash commands)
|
|
206
|
-
│ ├── development/
|
|
207
|
-
│ ├── backend/
|
|
208
|
-
│ ├── data-engineering/
|
|
209
|
-
│ ├── database/
|
|
210
|
-
│ ├── infrastructure/
|
|
211
|
-
│ ├── system/
|
|
212
|
-
│ └── orchestration/
|
|
213
221
|
└── guides/ # Reference docs (22 total)
|
|
214
222
|
```
|
|
215
223
|
|
|
@@ -227,7 +235,7 @@ bun run build # Build for production
|
|
|
227
235
|
### Requirements
|
|
228
236
|
|
|
229
237
|
- Node.js >= 18.0.0
|
|
230
|
-
- Claude Code CLI
|
|
238
|
+
- Claude Code CLI or OpenAI Codex CLI
|
|
231
239
|
|
|
232
240
|
---
|
|
233
241
|
|
package/dist/cli/index.js
CHANGED
|
@@ -9033,6 +9033,214 @@ var {
|
|
|
9033
9033
|
Help
|
|
9034
9034
|
} = import__.default;
|
|
9035
9035
|
|
|
9036
|
+
// src/core/preflight.ts
|
|
9037
|
+
import { execSync } from "node:child_process";
|
|
9038
|
+
function isCI() {
|
|
9039
|
+
const ciEnvVars = ["CI", "GITHUB_ACTIONS", "OMCUSTOM_SKIP_PREFLIGHT"];
|
|
9040
|
+
return ciEnvVars.some((envVar) => process.env[envVar] === "true");
|
|
9041
|
+
}
|
|
9042
|
+
function hasHomebrew() {
|
|
9043
|
+
try {
|
|
9044
|
+
execSync("which brew", { encoding: "utf-8", stdio: "pipe" });
|
|
9045
|
+
return true;
|
|
9046
|
+
} catch {
|
|
9047
|
+
return false;
|
|
9048
|
+
}
|
|
9049
|
+
}
|
|
9050
|
+
function getToolInfoFromBrew(toolName) {
|
|
9051
|
+
const tool = {
|
|
9052
|
+
name: toolName,
|
|
9053
|
+
installed: false,
|
|
9054
|
+
currentVersion: null,
|
|
9055
|
+
latestVersion: null,
|
|
9056
|
+
updateAvailable: false,
|
|
9057
|
+
installMethod: "homebrew"
|
|
9058
|
+
};
|
|
9059
|
+
try {
|
|
9060
|
+
const infoOutput = execSync(`brew info --json=v2 ${toolName}`, {
|
|
9061
|
+
encoding: "utf-8",
|
|
9062
|
+
stdio: "pipe",
|
|
9063
|
+
timeout: 3000
|
|
9064
|
+
});
|
|
9065
|
+
const info = JSON.parse(infoOutput);
|
|
9066
|
+
if (info.casks && info.casks.length > 0) {
|
|
9067
|
+
const cask = info.casks[0];
|
|
9068
|
+
tool.latestVersion = cask.version;
|
|
9069
|
+
tool.currentVersion = cask.installed || null;
|
|
9070
|
+
tool.installed = cask.installed !== null;
|
|
9071
|
+
}
|
|
9072
|
+
if (!tool.installed && info.formulae && info.formulae.length > 0) {
|
|
9073
|
+
const formula = info.formulae[0];
|
|
9074
|
+
tool.latestVersion = formula.versions.stable;
|
|
9075
|
+
if (formula.installed && formula.installed.length > 0) {
|
|
9076
|
+
tool.currentVersion = formula.installed[0].version;
|
|
9077
|
+
tool.installed = true;
|
|
9078
|
+
}
|
|
9079
|
+
}
|
|
9080
|
+
if (tool.installed && tool.currentVersion && tool.latestVersion) {
|
|
9081
|
+
tool.updateAvailable = tool.currentVersion !== tool.latestVersion;
|
|
9082
|
+
}
|
|
9083
|
+
} catch {}
|
|
9084
|
+
return tool;
|
|
9085
|
+
}
|
|
9086
|
+
function getToolInfoFromNpm(toolName) {
|
|
9087
|
+
const tool = {
|
|
9088
|
+
name: toolName,
|
|
9089
|
+
installed: false,
|
|
9090
|
+
currentVersion: null,
|
|
9091
|
+
latestVersion: null,
|
|
9092
|
+
updateAvailable: false,
|
|
9093
|
+
installMethod: "npm"
|
|
9094
|
+
};
|
|
9095
|
+
try {
|
|
9096
|
+
const versionOutput = execSync(`npx ${toolName} --version`, {
|
|
9097
|
+
encoding: "utf-8",
|
|
9098
|
+
stdio: "pipe",
|
|
9099
|
+
timeout: 3000
|
|
9100
|
+
});
|
|
9101
|
+
const version = versionOutput.trim();
|
|
9102
|
+
if (version) {
|
|
9103
|
+
tool.installed = true;
|
|
9104
|
+
tool.currentVersion = version;
|
|
9105
|
+
}
|
|
9106
|
+
} catch {}
|
|
9107
|
+
return tool;
|
|
9108
|
+
}
|
|
9109
|
+
function getToolInfo(toolName) {
|
|
9110
|
+
if (hasHomebrew()) {
|
|
9111
|
+
const brewTool = getToolInfoFromBrew(toolName);
|
|
9112
|
+
if (brewTool.installed) {
|
|
9113
|
+
return brewTool;
|
|
9114
|
+
}
|
|
9115
|
+
}
|
|
9116
|
+
const npmTool = getToolInfoFromNpm(toolName);
|
|
9117
|
+
if (npmTool.installed) {
|
|
9118
|
+
return npmTool;
|
|
9119
|
+
}
|
|
9120
|
+
return {
|
|
9121
|
+
name: toolName,
|
|
9122
|
+
installed: false,
|
|
9123
|
+
currentVersion: null,
|
|
9124
|
+
latestVersion: null,
|
|
9125
|
+
updateAvailable: false,
|
|
9126
|
+
installMethod: "unknown"
|
|
9127
|
+
};
|
|
9128
|
+
}
|
|
9129
|
+
function checkOutdated(tools) {
|
|
9130
|
+
if (!hasHomebrew())
|
|
9131
|
+
return;
|
|
9132
|
+
try {
|
|
9133
|
+
const toolNames = tools.map((t) => t.name).join(" ");
|
|
9134
|
+
const outdatedOutput = execSync(`brew outdated --json=v2 ${toolNames}`, {
|
|
9135
|
+
encoding: "utf-8",
|
|
9136
|
+
stdio: "pipe",
|
|
9137
|
+
timeout: 3000
|
|
9138
|
+
});
|
|
9139
|
+
const outdated = JSON.parse(outdatedOutput);
|
|
9140
|
+
const outdatedCasks = outdated.casks || [];
|
|
9141
|
+
const outdatedFormulae = outdated.formulae || [];
|
|
9142
|
+
for (const tool of tools) {
|
|
9143
|
+
const outdatedCask = outdatedCasks.find((c) => c.name === tool.name);
|
|
9144
|
+
const outdatedFormula = outdatedFormulae.find((f) => f.name === tool.name);
|
|
9145
|
+
if (outdatedCask) {
|
|
9146
|
+
tool.latestVersion = outdatedCask.current_version;
|
|
9147
|
+
tool.updateAvailable = true;
|
|
9148
|
+
} else if (outdatedFormula) {
|
|
9149
|
+
tool.latestVersion = outdatedFormula.current_version;
|
|
9150
|
+
tool.updateAvailable = true;
|
|
9151
|
+
}
|
|
9152
|
+
}
|
|
9153
|
+
} catch {}
|
|
9154
|
+
}
|
|
9155
|
+
async function runPreflightCheck(options = {}) {
|
|
9156
|
+
const { skip = false, tools: toolNames = ["claude-code", "codex"], timeout = 5000 } = options;
|
|
9157
|
+
if (skip) {
|
|
9158
|
+
return {
|
|
9159
|
+
tools: [],
|
|
9160
|
+
hasUpdates: false,
|
|
9161
|
+
warnings: [],
|
|
9162
|
+
skipped: true,
|
|
9163
|
+
skipReason: "Skipped by --skip-version-check flag"
|
|
9164
|
+
};
|
|
9165
|
+
}
|
|
9166
|
+
if (isCI()) {
|
|
9167
|
+
return {
|
|
9168
|
+
tools: [],
|
|
9169
|
+
hasUpdates: false,
|
|
9170
|
+
warnings: [],
|
|
9171
|
+
skipped: true,
|
|
9172
|
+
skipReason: "CI environment detected"
|
|
9173
|
+
};
|
|
9174
|
+
}
|
|
9175
|
+
if (!hasHomebrew()) {
|
|
9176
|
+
return {
|
|
9177
|
+
tools: [],
|
|
9178
|
+
hasUpdates: false,
|
|
9179
|
+
warnings: ["Homebrew not found, skipping version check"],
|
|
9180
|
+
skipped: true,
|
|
9181
|
+
skipReason: "Homebrew not available"
|
|
9182
|
+
};
|
|
9183
|
+
}
|
|
9184
|
+
return new Promise((resolve) => {
|
|
9185
|
+
const timeoutId = setTimeout(() => {
|
|
9186
|
+
resolve({
|
|
9187
|
+
tools: [],
|
|
9188
|
+
hasUpdates: false,
|
|
9189
|
+
warnings: ["Version check timed out"],
|
|
9190
|
+
skipped: true,
|
|
9191
|
+
skipReason: "Timeout"
|
|
9192
|
+
});
|
|
9193
|
+
}, timeout);
|
|
9194
|
+
try {
|
|
9195
|
+
const tools = [];
|
|
9196
|
+
for (const toolName of toolNames) {
|
|
9197
|
+
const tool = getToolInfo(toolName);
|
|
9198
|
+
tools.push(tool);
|
|
9199
|
+
}
|
|
9200
|
+
checkOutdated(tools);
|
|
9201
|
+
const hasUpdates = tools.some((t) => t.updateAvailable);
|
|
9202
|
+
const warnings = [];
|
|
9203
|
+
clearTimeout(timeoutId);
|
|
9204
|
+
resolve({
|
|
9205
|
+
tools,
|
|
9206
|
+
hasUpdates,
|
|
9207
|
+
warnings,
|
|
9208
|
+
skipped: false
|
|
9209
|
+
});
|
|
9210
|
+
} catch (error) {
|
|
9211
|
+
clearTimeout(timeoutId);
|
|
9212
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
9213
|
+
resolve({
|
|
9214
|
+
tools: [],
|
|
9215
|
+
hasUpdates: false,
|
|
9216
|
+
warnings: [`Pre-flight check failed: ${errorMessage}`],
|
|
9217
|
+
skipped: true,
|
|
9218
|
+
skipReason: "Error during check"
|
|
9219
|
+
});
|
|
9220
|
+
}
|
|
9221
|
+
});
|
|
9222
|
+
}
|
|
9223
|
+
function formatPreflightWarnings(result) {
|
|
9224
|
+
if (!result.hasUpdates) {
|
|
9225
|
+
return "";
|
|
9226
|
+
}
|
|
9227
|
+
const lines = [];
|
|
9228
|
+
const updatesAvailable = result.tools.filter((t) => t.updateAvailable);
|
|
9229
|
+
if (updatesAvailable.length === 1) {
|
|
9230
|
+
const tool = updatesAvailable[0];
|
|
9231
|
+
lines.push(`⚠ ${tool.name} ${tool.latestVersion} is available (current: ${tool.currentVersion})`);
|
|
9232
|
+
lines.push(` Run: brew upgrade ${tool.name}`);
|
|
9233
|
+
} else if (updatesAvailable.length > 1) {
|
|
9234
|
+
lines.push("Run the following to upgrade:");
|
|
9235
|
+
for (const tool of updatesAvailable) {
|
|
9236
|
+
lines.push(` brew upgrade ${tool.name} # ${tool.latestVersion} available (current: ${tool.currentVersion})`);
|
|
9237
|
+
}
|
|
9238
|
+
}
|
|
9239
|
+
lines.push(" Use --skip-version-check to skip this check");
|
|
9240
|
+
return lines.join(`
|
|
9241
|
+
`);
|
|
9242
|
+
}
|
|
9243
|
+
|
|
9036
9244
|
// node_modules/i18next/dist/esm/i18next.js
|
|
9037
9245
|
var isString = (obj) => typeof obj === "string";
|
|
9038
9246
|
var defer = () => {
|
|
@@ -11461,6 +11669,17 @@ var en_default = {
|
|
|
11461
11669
|
error: {
|
|
11462
11670
|
unexpected: "An unexpected error occurred:"
|
|
11463
11671
|
},
|
|
11672
|
+
preflight: {
|
|
11673
|
+
checking: "Checking CLI tool versions...",
|
|
11674
|
+
updateAvailable: "⚠ {{name}} {{latest}} is available (current: {{current}})",
|
|
11675
|
+
upgradeCommand: " Run: brew upgrade {{name}}",
|
|
11676
|
+
multipleUpdates: "Run the following to upgrade:",
|
|
11677
|
+
skipFlag: " Use --skip-version-check to skip this check",
|
|
11678
|
+
skippedCi: "Skipping version check (CI environment detected)",
|
|
11679
|
+
skippedFlag: "Skipping version check (--skip-version-check)",
|
|
11680
|
+
homebrewNotFound: "Homebrew not found, skipping version check",
|
|
11681
|
+
timeout: "Version check timed out, continuing..."
|
|
11682
|
+
},
|
|
11464
11683
|
init: {
|
|
11465
11684
|
description: "Initialize oh-my-customcode in the current directory",
|
|
11466
11685
|
langOption: "Language for templates (en or ko)",
|
|
@@ -11503,7 +11722,25 @@ var en_default = {
|
|
|
11503
11722
|
latestVersion: "Latest version: {{version}}",
|
|
11504
11723
|
changelog: "Changelog",
|
|
11505
11724
|
promptUpdate: "Do you want to update?",
|
|
11506
|
-
skipped: "Update skipped"
|
|
11725
|
+
skipped: "Update skipped",
|
|
11726
|
+
dryRunOption: "Show what would be updated without making changes",
|
|
11727
|
+
forceOption: "Force update even if already at latest version",
|
|
11728
|
+
backupOption: "Create backup before updating",
|
|
11729
|
+
agentsOption: "Update only agents",
|
|
11730
|
+
skillsOption: "Update only skills",
|
|
11731
|
+
rulesOption: "Update only rules",
|
|
11732
|
+
guidesOption: "Update only guides",
|
|
11733
|
+
hooksOption: "Update only hooks",
|
|
11734
|
+
contextsOption: "Update only contexts",
|
|
11735
|
+
providerOption: "Provider to update (auto, claude, codex)",
|
|
11736
|
+
dryRunHeader: "Dry run - no changes will be made:",
|
|
11737
|
+
componentUpdated: "✓ {{component}} updated",
|
|
11738
|
+
componentSkipped: "- {{component}} skipped (no changes)",
|
|
11739
|
+
componentFailed: "✗ {{component}} failed: {{error}}",
|
|
11740
|
+
preservedFiles: "Preserved {{count}} user-customized files",
|
|
11741
|
+
backupCreated: "Backup created at {{path}}",
|
|
11742
|
+
summary: "Update complete: {{updated}} updated, {{skipped}} skipped",
|
|
11743
|
+
summaryFailed: "Update failed: {{error}}"
|
|
11507
11744
|
},
|
|
11508
11745
|
list: {
|
|
11509
11746
|
description: "List installed components",
|
|
@@ -11715,6 +11952,17 @@ var ko_default = {
|
|
|
11715
11952
|
error: {
|
|
11716
11953
|
unexpected: "예기치 않은 오류가 발생했습니다:"
|
|
11717
11954
|
},
|
|
11955
|
+
preflight: {
|
|
11956
|
+
checking: "CLI 도구 버전 확인 중...",
|
|
11957
|
+
updateAvailable: "⚠ {{name}} {{latest}} 사용 가능 (현재: {{current}})",
|
|
11958
|
+
upgradeCommand: " 실행: brew upgrade {{name}}",
|
|
11959
|
+
multipleUpdates: "다음을 실행하여 업그레이드하세요:",
|
|
11960
|
+
skipFlag: " --skip-version-check 옵션으로 이 검사를 건너뛸 수 있습니다",
|
|
11961
|
+
skippedCi: "버전 확인 건너뜀 (CI 환경 감지)",
|
|
11962
|
+
skippedFlag: "버전 확인 건너뜀 (--skip-version-check)",
|
|
11963
|
+
homebrewNotFound: "Homebrew를 찾을 수 없어 버전 확인을 건너뜁니다",
|
|
11964
|
+
timeout: "버전 확인 시간 초과, 계속 진행합니다..."
|
|
11965
|
+
},
|
|
11718
11966
|
init: {
|
|
11719
11967
|
description: "현재 디렉토리에 oh-my-customcode 초기화",
|
|
11720
11968
|
langOption: "템플릿 언어 (en 또는 ko)",
|
|
@@ -11757,7 +12005,25 @@ var ko_default = {
|
|
|
11757
12005
|
latestVersion: "최신 버전: {{version}}",
|
|
11758
12006
|
changelog: "변경 내역",
|
|
11759
12007
|
promptUpdate: "업데이트하시겠습니까?",
|
|
11760
|
-
skipped: "업데이트 건너뜀"
|
|
12008
|
+
skipped: "업데이트 건너뜀",
|
|
12009
|
+
dryRunOption: "변경 없이 업데이트할 내용 표시",
|
|
12010
|
+
forceOption: "이미 최신 버전이어도 강제 업데이트",
|
|
12011
|
+
backupOption: "업데이트 전 백업 생성",
|
|
12012
|
+
agentsOption: "에이전트만 업데이트",
|
|
12013
|
+
skillsOption: "스킬만 업데이트",
|
|
12014
|
+
rulesOption: "규칙만 업데이트",
|
|
12015
|
+
guidesOption: "가이드만 업데이트",
|
|
12016
|
+
hooksOption: "훅만 업데이트",
|
|
12017
|
+
contextsOption: "컨텍스트만 업데이트",
|
|
12018
|
+
providerOption: "업데이트할 제공자 (auto, claude, codex)",
|
|
12019
|
+
dryRunHeader: "드라이 런 - 변경사항 없음:",
|
|
12020
|
+
componentUpdated: "✓ {{component}} 업데이트됨",
|
|
12021
|
+
componentSkipped: "- {{component}} 건너뜀 (변경사항 없음)",
|
|
12022
|
+
componentFailed: "✗ {{component}} 실패: {{error}}",
|
|
12023
|
+
preservedFiles: "사용자 커스터마이징 파일 {{count}}개 보존됨",
|
|
12024
|
+
backupCreated: "백업 생성됨: {{path}}",
|
|
12025
|
+
summary: "업데이트 완료: {{updated}}개 업데이트, {{skipped}}개 건너뜀",
|
|
12026
|
+
summaryFailed: "업데이트 실패: {{error}}"
|
|
11761
12027
|
},
|
|
11762
12028
|
list: {
|
|
11763
12029
|
description: "설치된 컴포넌트 목록 표시",
|
|
@@ -13795,73 +14061,279 @@ async function listCommand(type = "all", options = {}) {
|
|
|
13795
14061
|
}
|
|
13796
14062
|
}
|
|
13797
14063
|
|
|
13798
|
-
// src/
|
|
13799
|
-
|
|
13800
|
-
|
|
14064
|
+
// src/core/updater.ts
|
|
14065
|
+
import { join as join7 } from "node:path";
|
|
14066
|
+
var CUSTOMIZATION_MANIFEST_FILE = ".omcustom-customizations.json";
|
|
14067
|
+
function createUpdateResult() {
|
|
14068
|
+
return {
|
|
14069
|
+
success: false,
|
|
14070
|
+
updatedComponents: [],
|
|
14071
|
+
skippedComponents: [],
|
|
14072
|
+
preservedFiles: [],
|
|
14073
|
+
backedUpPaths: [],
|
|
14074
|
+
previousVersion: "",
|
|
14075
|
+
newVersion: "",
|
|
14076
|
+
warnings: []
|
|
14077
|
+
};
|
|
14078
|
+
}
|
|
14079
|
+
async function handleBackupIfRequested(targetDir, provider, backup, result) {
|
|
14080
|
+
if (!backup)
|
|
14081
|
+
return;
|
|
14082
|
+
const backupPath = await backupInstallation(targetDir, provider);
|
|
14083
|
+
result.backedUpPaths.push(backupPath);
|
|
14084
|
+
info("update.backup_created", { path: backupPath });
|
|
14085
|
+
}
|
|
14086
|
+
async function processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result) {
|
|
14087
|
+
const componentUpdate = updateCheck.updatableComponents.find((c) => c.name === component);
|
|
14088
|
+
if (!componentUpdate && !options.force) {
|
|
14089
|
+
result.skippedComponents.push(component);
|
|
14090
|
+
return;
|
|
14091
|
+
}
|
|
14092
|
+
if (options.dryRun) {
|
|
14093
|
+
debug("update.dry_run", { component });
|
|
14094
|
+
result.updatedComponents.push(component);
|
|
14095
|
+
return;
|
|
14096
|
+
}
|
|
14097
|
+
try {
|
|
14098
|
+
const preserved = await updateComponent(targetDir, provider, component, customizations, options);
|
|
14099
|
+
result.updatedComponents.push(component);
|
|
14100
|
+
result.preservedFiles.push(...preserved);
|
|
14101
|
+
} catch (err) {
|
|
14102
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14103
|
+
result.warnings.push(`Failed to update ${component}: ${message}`);
|
|
14104
|
+
result.skippedComponents.push(component);
|
|
14105
|
+
}
|
|
14106
|
+
}
|
|
14107
|
+
async function updateAllComponents(targetDir, provider, components, updateCheck, customizations, options, result) {
|
|
14108
|
+
for (const component of components) {
|
|
14109
|
+
await processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result);
|
|
14110
|
+
}
|
|
13801
14111
|
}
|
|
13802
|
-
async function
|
|
14112
|
+
async function update(options) {
|
|
14113
|
+
const result = createUpdateResult();
|
|
14114
|
+
try {
|
|
14115
|
+
info("update.start", { targetDir: options.targetDir });
|
|
14116
|
+
const config = await loadConfig(options.targetDir);
|
|
14117
|
+
const provider = options.provider ?? (config.provider === "codex" ? "codex" : "claude");
|
|
14118
|
+
result.previousVersion = config.version;
|
|
14119
|
+
const updateCheck = await checkForUpdates(options.targetDir, provider);
|
|
14120
|
+
result.newVersion = updateCheck.latestVersion;
|
|
14121
|
+
if (!updateCheck.hasUpdates && !options.force) {
|
|
14122
|
+
info("update.no_updates");
|
|
14123
|
+
result.success = true;
|
|
14124
|
+
result.skippedComponents = options.components || getAllUpdateComponents();
|
|
14125
|
+
return result;
|
|
14126
|
+
}
|
|
14127
|
+
await handleBackupIfRequested(options.targetDir, provider, !!options.backup, result);
|
|
14128
|
+
const customizations = options.preserveCustomizations !== false ? await loadCustomizationManifest(options.targetDir) : null;
|
|
14129
|
+
const components = options.components || getAllUpdateComponents();
|
|
14130
|
+
await updateAllComponents(options.targetDir, provider, components, updateCheck, customizations, options, result);
|
|
14131
|
+
config.version = result.newVersion;
|
|
14132
|
+
config.lastUpdated = new Date().toISOString();
|
|
14133
|
+
await saveConfig(options.targetDir, config);
|
|
14134
|
+
result.success = true;
|
|
14135
|
+
success("update.success", { from: result.previousVersion, to: result.newVersion });
|
|
14136
|
+
} catch (err) {
|
|
14137
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14138
|
+
result.error = message;
|
|
14139
|
+
error("update.failed", { error: message });
|
|
14140
|
+
}
|
|
14141
|
+
return result;
|
|
14142
|
+
}
|
|
14143
|
+
async function checkForUpdates(targetDir, provider = "claude") {
|
|
14144
|
+
const config = await loadConfig(targetDir);
|
|
14145
|
+
const currentVersion = config.version;
|
|
14146
|
+
const latestVersion = await getLatestVersion(provider);
|
|
14147
|
+
const updatableComponents = [];
|
|
14148
|
+
for (const component of getAllUpdateComponents()) {
|
|
14149
|
+
const hasUpdate = await componentHasUpdate(targetDir, provider, component, config);
|
|
14150
|
+
if (hasUpdate) {
|
|
14151
|
+
updatableComponents.push({
|
|
14152
|
+
name: component,
|
|
14153
|
+
currentVersion: config.componentVersions?.[component] || "0.0.0",
|
|
14154
|
+
latestVersion
|
|
14155
|
+
});
|
|
14156
|
+
}
|
|
14157
|
+
}
|
|
13803
14158
|
return {
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
|
|
14159
|
+
hasUpdates: updatableComponents.length > 0 || currentVersion !== latestVersion,
|
|
14160
|
+
currentVersion,
|
|
14161
|
+
latestVersion,
|
|
14162
|
+
updatableComponents,
|
|
14163
|
+
checkedAt: new Date().toISOString()
|
|
13807
14164
|
};
|
|
13808
14165
|
}
|
|
13809
|
-
async function
|
|
13810
|
-
const
|
|
13811
|
-
const
|
|
13812
|
-
|
|
14166
|
+
async function preserveCustomizations(targetDir, customizations) {
|
|
14167
|
+
const preserved = new Map;
|
|
14168
|
+
const fs2 = await import("node:fs/promises");
|
|
14169
|
+
for (const filePath of customizations) {
|
|
14170
|
+
const fullPath = join7(targetDir, filePath);
|
|
14171
|
+
if (await fileExists(fullPath)) {
|
|
14172
|
+
const content = await fs2.readFile(fullPath, "utf-8");
|
|
14173
|
+
preserved.set(filePath, content);
|
|
14174
|
+
}
|
|
14175
|
+
}
|
|
14176
|
+
return preserved;
|
|
14177
|
+
}
|
|
14178
|
+
function getAllUpdateComponents() {
|
|
14179
|
+
return ["rules", "agents", "skills", "guides", "hooks", "contexts"];
|
|
14180
|
+
}
|
|
14181
|
+
async function getLatestVersion(provider) {
|
|
14182
|
+
const layout = getProviderLayout(provider);
|
|
14183
|
+
const manifestPath = resolveTemplatePath(layout.manifestFile);
|
|
14184
|
+
if (await fileExists(manifestPath)) {
|
|
14185
|
+
const manifest = await readJsonFile(manifestPath);
|
|
14186
|
+
return manifest.version;
|
|
14187
|
+
}
|
|
14188
|
+
return "0.0.0";
|
|
14189
|
+
}
|
|
14190
|
+
async function componentHasUpdate(_targetDir, provider, component, config) {
|
|
14191
|
+
const installedVersion = config.componentVersions?.[component];
|
|
14192
|
+
if (!installedVersion) {
|
|
13813
14193
|
return true;
|
|
13814
14194
|
}
|
|
13815
|
-
|
|
14195
|
+
const latestVersion = await getLatestVersion(provider);
|
|
14196
|
+
return installedVersion !== latestVersion;
|
|
14197
|
+
}
|
|
14198
|
+
async function updateComponent(targetDir, provider, component, customizations, options) {
|
|
14199
|
+
const preservedFiles = [];
|
|
14200
|
+
const componentPath = getComponentPath2(provider, component);
|
|
14201
|
+
const srcPath = resolveTemplatePath(componentPath);
|
|
14202
|
+
const destPath = join7(targetDir, componentPath);
|
|
14203
|
+
if (customizations && options.preserveCustomizations !== false) {
|
|
14204
|
+
const toPreserve = customizations.preserveFiles.filter((f) => f.startsWith(componentPath));
|
|
14205
|
+
if (toPreserve.length > 0) {
|
|
14206
|
+
const preserved = await preserveCustomizations(targetDir, toPreserve);
|
|
14207
|
+
preservedFiles.push(...preserved.keys());
|
|
14208
|
+
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
14209
|
+
const fs2 = await import("node:fs/promises");
|
|
14210
|
+
for (const [path2, content] of preserved) {
|
|
14211
|
+
await fs2.writeFile(join7(targetDir, path2), content, "utf-8");
|
|
14212
|
+
}
|
|
14213
|
+
} else {
|
|
14214
|
+
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
14215
|
+
}
|
|
14216
|
+
} else {
|
|
14217
|
+
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
14218
|
+
}
|
|
14219
|
+
debug("update.component_updated", { component });
|
|
14220
|
+
return preservedFiles;
|
|
14221
|
+
}
|
|
14222
|
+
function getComponentPath2(provider, component) {
|
|
14223
|
+
const layout = getProviderLayout(provider);
|
|
14224
|
+
if (component === "guides") {
|
|
14225
|
+
return "guides";
|
|
14226
|
+
}
|
|
14227
|
+
return `${layout.rootDir}/${component}`;
|
|
13816
14228
|
}
|
|
13817
|
-
async function
|
|
13818
|
-
const
|
|
13819
|
-
|
|
14229
|
+
async function backupInstallation(targetDir, provider) {
|
|
14230
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
14231
|
+
const backupDir = join7(targetDir, `.omcustom-backup-${timestamp}`);
|
|
14232
|
+
const fs2 = await import("node:fs/promises");
|
|
14233
|
+
await ensureDirectory(backupDir);
|
|
14234
|
+
const layout = getProviderLayout(provider);
|
|
14235
|
+
const dirsToBackup = [layout.rootDir, "guides"];
|
|
14236
|
+
for (const dir2 of dirsToBackup) {
|
|
14237
|
+
const srcPath = join7(targetDir, dir2);
|
|
14238
|
+
if (await fileExists(srcPath)) {
|
|
14239
|
+
const destPath = join7(backupDir, dir2);
|
|
14240
|
+
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
14241
|
+
}
|
|
14242
|
+
}
|
|
14243
|
+
const entryPath = join7(targetDir, layout.entryFile);
|
|
14244
|
+
if (await fileExists(entryPath)) {
|
|
14245
|
+
await fs2.copyFile(entryPath, join7(backupDir, layout.entryFile));
|
|
14246
|
+
}
|
|
14247
|
+
return backupDir;
|
|
13820
14248
|
}
|
|
14249
|
+
async function loadCustomizationManifest(targetDir) {
|
|
14250
|
+
const manifestPath = join7(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
14251
|
+
if (await fileExists(manifestPath)) {
|
|
14252
|
+
return readJsonFile(manifestPath);
|
|
14253
|
+
}
|
|
14254
|
+
return null;
|
|
14255
|
+
}
|
|
14256
|
+
|
|
14257
|
+
// src/cli/update.ts
|
|
13821
14258
|
async function updateCommand(options = {}) {
|
|
13822
|
-
console.log(i18n.t("cli.update.checking"));
|
|
13823
14259
|
try {
|
|
13824
|
-
const
|
|
13825
|
-
const
|
|
13826
|
-
|
|
13827
|
-
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
}
|
|
13834
|
-
const updateAvailable = await checkUpdateAvailable();
|
|
13835
|
-
if (!updateAvailable && !options.force) {
|
|
13836
|
-
console.log(i18n.t("cli.update.alreadyLatest"));
|
|
13837
|
-
return {
|
|
13838
|
-
success: true,
|
|
13839
|
-
message: i18n.t("cli.update.alreadyLatest"),
|
|
13840
|
-
currentVersion: currentVersion.version,
|
|
13841
|
-
newVersion: latestVersion.version
|
|
13842
|
-
};
|
|
14260
|
+
const targetDir = process.cwd();
|
|
14261
|
+
const detection = await detectProvider({
|
|
14262
|
+
targetDir,
|
|
14263
|
+
override: options.provider
|
|
14264
|
+
});
|
|
14265
|
+
const provider = detection.provider;
|
|
14266
|
+
const components = buildComponentsList(options);
|
|
14267
|
+
if (options.dryRun) {
|
|
14268
|
+
console.log(i18n.t("cli.update.dryRunHeader"));
|
|
13843
14269
|
}
|
|
13844
|
-
|
|
13845
|
-
|
|
13846
|
-
|
|
13847
|
-
|
|
13848
|
-
|
|
13849
|
-
|
|
13850
|
-
|
|
13851
|
-
|
|
13852
|
-
message: i18n.t("cli.update.success"),
|
|
13853
|
-
updatedComponents,
|
|
13854
|
-
currentVersion: currentVersion.version,
|
|
13855
|
-
newVersion: latestVersion.version
|
|
14270
|
+
const updateOptions = {
|
|
14271
|
+
targetDir,
|
|
14272
|
+
provider,
|
|
14273
|
+
components,
|
|
14274
|
+
force: options.force,
|
|
14275
|
+
preserveCustomizations: true,
|
|
14276
|
+
dryRun: options.dryRun,
|
|
14277
|
+
backup: options.backup
|
|
13856
14278
|
};
|
|
14279
|
+
const result = await update(updateOptions);
|
|
14280
|
+
printUpdateResults(result);
|
|
14281
|
+
if (!result.success) {
|
|
14282
|
+
process.exit(1);
|
|
14283
|
+
}
|
|
13857
14284
|
} catch (error2) {
|
|
13858
14285
|
const errorMessage = error2 instanceof Error ? error2.message : String(error2);
|
|
13859
|
-
console.error(i18n.t("cli.update.
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
14286
|
+
console.error(i18n.t("cli.update.summaryFailed", { error: errorMessage }));
|
|
14287
|
+
process.exit(1);
|
|
14288
|
+
}
|
|
14289
|
+
}
|
|
14290
|
+
function buildComponentsList(options) {
|
|
14291
|
+
const components = [];
|
|
14292
|
+
if (options.agents) {
|
|
14293
|
+
components.push("agents");
|
|
14294
|
+
}
|
|
14295
|
+
if (options.skills) {
|
|
14296
|
+
components.push("skills");
|
|
14297
|
+
}
|
|
14298
|
+
if (options.rules) {
|
|
14299
|
+
components.push("rules");
|
|
14300
|
+
}
|
|
14301
|
+
if (options.guides) {
|
|
14302
|
+
components.push("guides");
|
|
14303
|
+
}
|
|
14304
|
+
if (options.hooks) {
|
|
14305
|
+
components.push("hooks");
|
|
14306
|
+
}
|
|
14307
|
+
if (options.contexts) {
|
|
14308
|
+
components.push("contexts");
|
|
14309
|
+
}
|
|
14310
|
+
return components.length > 0 ? components : undefined;
|
|
14311
|
+
}
|
|
14312
|
+
function printUpdateResults(result) {
|
|
14313
|
+
for (const component of result.updatedComponents) {
|
|
14314
|
+
console.log(i18n.t("cli.update.componentUpdated", { component }));
|
|
14315
|
+
}
|
|
14316
|
+
for (const component of result.skippedComponents) {
|
|
14317
|
+
console.log(i18n.t("cli.update.componentSkipped", { component }));
|
|
14318
|
+
}
|
|
14319
|
+
if (result.preservedFiles.length > 0) {
|
|
14320
|
+
console.log(i18n.t("cli.update.preservedFiles", { count: result.preservedFiles.length }));
|
|
14321
|
+
}
|
|
14322
|
+
if (result.backedUpPaths.length > 0) {
|
|
14323
|
+
for (const path2 of result.backedUpPaths) {
|
|
14324
|
+
console.log(i18n.t("cli.update.backupCreated", { path: path2 }));
|
|
14325
|
+
}
|
|
14326
|
+
}
|
|
14327
|
+
for (const warning of result.warnings) {
|
|
14328
|
+
console.warn(warning);
|
|
14329
|
+
}
|
|
14330
|
+
if (result.success) {
|
|
14331
|
+
console.log(i18n.t("cli.update.summary", {
|
|
14332
|
+
updated: result.updatedComponents.length,
|
|
14333
|
+
skipped: result.skippedComponents.length
|
|
14334
|
+
}));
|
|
14335
|
+
} else if (result.error) {
|
|
14336
|
+
console.error(i18n.t("cli.update.summaryFailed", { error: result.error }));
|
|
13865
14337
|
}
|
|
13866
14338
|
}
|
|
13867
14339
|
|
|
@@ -13870,12 +14342,12 @@ var require2 = createRequire2(import.meta.url);
|
|
|
13870
14342
|
var packageJson = require2("../../package.json");
|
|
13871
14343
|
function createProgram() {
|
|
13872
14344
|
const program2 = new Command;
|
|
13873
|
-
program2.name("omcustom").description(i18n.t("cli.description")).version(packageJson.version, "-v, --version", i18n.t("cli.versionOption"));
|
|
14345
|
+
program2.name("omcustom").description(i18n.t("cli.description")).version(packageJson.version, "-v, --version", i18n.t("cli.versionOption")).option("--skip-version-check", "Skip CLI version pre-flight check");
|
|
13874
14346
|
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) => {
|
|
13875
14347
|
await initCommand(options);
|
|
13876
14348
|
});
|
|
13877
|
-
program2.command("update").description(i18n.t("cli.update.description")).action(async () => {
|
|
13878
|
-
await updateCommand();
|
|
14349
|
+
program2.command("update").description(i18n.t("cli.update.description")).option("--dry-run", i18n.t("cli.update.dryRunOption")).option("--force", i18n.t("cli.update.forceOption")).option("--backup", i18n.t("cli.update.backupOption")).option("--agents", i18n.t("cli.update.agentsOption")).option("--skills", i18n.t("cli.update.skillsOption")).option("--rules", i18n.t("cli.update.rulesOption")).option("--guides", i18n.t("cli.update.guidesOption")).option("--hooks", i18n.t("cli.update.hooksOption")).option("--contexts", i18n.t("cli.update.contextsOption")).option("-p, --provider <provider>", i18n.t("cli.update.providerOption"), "auto").action(async (options) => {
|
|
14350
|
+
await updateCommand(options);
|
|
13879
14351
|
});
|
|
13880
14352
|
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
14353
|
await listCommand(type, {
|
|
@@ -13887,6 +14359,16 @@ function createProgram() {
|
|
|
13887
14359
|
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) => {
|
|
13888
14360
|
await doctorCommand(options);
|
|
13889
14361
|
});
|
|
14362
|
+
program2.hook("preAction", async (thisCommand) => {
|
|
14363
|
+
const opts = thisCommand.optsWithGlobals();
|
|
14364
|
+
const skipCheck = opts.skipVersionCheck || false;
|
|
14365
|
+
const result = await runPreflightCheck({ skip: skipCheck });
|
|
14366
|
+
if (result.hasUpdates) {
|
|
14367
|
+
const warnings = formatPreflightWarnings(result);
|
|
14368
|
+
console.warn(warnings);
|
|
14369
|
+
console.warn("");
|
|
14370
|
+
}
|
|
14371
|
+
});
|
|
13890
14372
|
return program2;
|
|
13891
14373
|
}
|
|
13892
14374
|
async function main() {
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,4 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
-
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
-
for (let key of __getOwnPropNames(mod))
|
|
11
|
-
if (!__hasOwnProp.call(to, key))
|
|
12
|
-
__defProp(to, key, {
|
|
13
|
-
get: () => mod[key],
|
|
14
|
-
enumerable: true
|
|
15
|
-
});
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
3
|
|
|
20
4
|
// src/core/config.ts
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
4e8d4e056e9633819fc41f927e1c18a6bc757636abbac85552a767664df242f4
|