oh-my-customcode 0.9.2 → 0.9.4
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 +25 -30
- package/dist/cli/index.js +1315 -568
- package/dist/index.js +234 -17
- package/package.json +1 -1
- package/templates/.claude/agents/arch-documenter.md +7 -71
- package/templates/.claude/agents/arch-speckit-agent.md +21 -108
- package/templates/.claude/agents/be-express-expert.md +8 -58
- package/templates/.claude/agents/be-nestjs-expert.md +6 -38
- package/templates/.claude/agents/be-springboot-expert.md +11 -56
- package/templates/.claude/agents/db-postgres-expert.md +10 -80
- package/templates/.claude/agents/db-redis-expert.md +10 -75
- package/templates/.claude/agents/db-supabase-expert.md +12 -48
- package/templates/.claude/agents/de-airflow-expert.md +8 -45
- package/templates/.claude/agents/de-dbt-expert.md +8 -46
- package/templates/.claude/agents/de-kafka-expert.md +10 -10
- package/templates/.claude/agents/de-pipeline-expert.md +9 -69
- package/templates/.claude/agents/de-snowflake-expert.md +9 -62
- package/templates/.claude/agents/de-spark-expert.md +10 -54
- package/templates/.claude/agents/fe-svelte-agent.md +5 -41
- package/templates/.claude/agents/fe-vercel-agent.md +9 -41
- package/templates/.claude/agents/fe-vuejs-agent.md +7 -42
- package/templates/.claude/agents/lang-java21-expert.md +11 -37
- package/templates/.claude/agents/mgr-claude-code-bible.md +22 -207
- package/templates/.claude/agents/mgr-creator.md +7 -88
- package/templates/.claude/agents/mgr-gitnerd.md +8 -76
- package/templates/.claude/agents/mgr-sauron.md +27 -20
- package/templates/.claude/agents/mgr-supplier.md +11 -96
- package/templates/.claude/agents/mgr-sync-checker.md +9 -70
- package/templates/.claude/agents/mgr-updater.md +9 -79
- package/templates/.claude/agents/qa-engineer.md +8 -72
- package/templates/.claude/agents/qa-planner.md +2 -3
- package/templates/.claude/agents/qa-writer.md +6 -76
- package/templates/.claude/agents/sys-memory-keeper.md +13 -87
- package/templates/.claude/agents/sys-naggy.md +9 -62
- package/templates/.claude/agents/tool-bun-expert.md +7 -52
- package/templates/.claude/agents/tool-npm-expert.md +6 -64
- package/templates/.claude/agents/tool-optimizer.md +7 -60
- package/templates/.claude/rules/MAY-optimization.md +16 -80
- package/templates/.claude/rules/MUST-agent-design.md +29 -134
- package/templates/.claude/rules/MUST-agent-identification.md +9 -88
- package/templates/.claude/rules/MUST-continuous-improvement.md +10 -117
- package/templates/.claude/rules/MUST-intent-transparency.md +14 -171
- package/templates/.claude/rules/MUST-language-policy.md +11 -46
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +82 -425
- package/templates/.claude/rules/MUST-parallel-execution.md +33 -405
- package/templates/.claude/rules/MUST-permissions.md +14 -68
- package/templates/.claude/rules/MUST-safety.md +11 -57
- package/templates/.claude/rules/MUST-sync-verification.md +49 -205
- package/templates/.claude/rules/MUST-tool-identification.md +21 -134
- package/templates/.claude/rules/SHOULD-agent-teams.md +22 -166
- package/templates/.claude/rules/SHOULD-ecomode.md +15 -123
- package/templates/.claude/rules/SHOULD-error-handling.md +19 -88
- package/templates/.claude/rules/SHOULD-hud-statusline.md +9 -89
- package/templates/.claude/rules/SHOULD-interaction.md +18 -87
- package/templates/.claude/rules/SHOULD-memory-integration.md +25 -118
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +70 -243
- package/templates/.claude/skills/springboot-best-practices/SKILL.md +180 -319
- package/templates/.codex/agents/arch-documenter.md +7 -71
- package/templates/.codex/agents/arch-speckit-agent.md +21 -108
- package/templates/.codex/agents/be-express-expert.md +8 -58
- package/templates/.codex/agents/be-nestjs-expert.md +6 -38
- package/templates/.codex/agents/be-springboot-expert.md +11 -56
- package/templates/.codex/agents/db-postgres-expert.md +10 -80
- package/templates/.codex/agents/db-redis-expert.md +10 -75
- package/templates/.codex/agents/db-supabase-expert.md +12 -48
- package/templates/.codex/agents/de-airflow-expert.md +8 -45
- package/templates/.codex/agents/de-dbt-expert.md +8 -46
- package/templates/.codex/agents/de-kafka-expert.md +10 -10
- package/templates/.codex/agents/de-pipeline-expert.md +9 -69
- package/templates/.codex/agents/de-snowflake-expert.md +9 -62
- package/templates/.codex/agents/de-spark-expert.md +10 -54
- package/templates/.codex/agents/fe-svelte-agent.md +5 -41
- package/templates/.codex/agents/fe-vercel-agent.md +9 -41
- package/templates/.codex/agents/fe-vuejs-agent.md +7 -42
- package/templates/.codex/agents/infra-aws-expert.md +1 -1
- package/templates/.codex/agents/infra-docker-expert.md +1 -1
- package/templates/.codex/agents/lang-java21-expert.md +11 -37
- package/templates/.codex/agents/mgr-claude-code-bible.md +25 -210
- package/templates/.codex/agents/mgr-creator.md +7 -88
- package/templates/.codex/agents/mgr-gitnerd.md +8 -76
- package/templates/.codex/agents/mgr-sauron.md +30 -23
- package/templates/.codex/agents/mgr-supplier.md +11 -96
- package/templates/.codex/agents/mgr-sync-checker.md +12 -73
- package/templates/.codex/agents/mgr-updater.md +9 -79
- package/templates/.codex/agents/qa-engineer.md +8 -72
- package/templates/.codex/agents/qa-planner.md +2 -3
- package/templates/.codex/agents/qa-writer.md +6 -76
- package/templates/.codex/agents/sys-memory-keeper.md +13 -87
- package/templates/.codex/agents/sys-naggy.md +9 -62
- package/templates/.codex/agents/tool-bun-expert.md +7 -52
- package/templates/.codex/agents/tool-npm-expert.md +6 -64
- package/templates/.codex/agents/tool-optimizer.md +7 -60
- package/templates/.codex/rules/MAY-optimization.md +16 -80
- package/templates/.codex/rules/MUST-agent-design.md +29 -134
- package/templates/.codex/rules/MUST-agent-identification.md +9 -88
- package/templates/.codex/rules/MUST-continuous-improvement.md +10 -117
- package/templates/.codex/rules/MUST-intent-transparency.md +14 -171
- package/templates/.codex/rules/MUST-language-policy.md +11 -46
- package/templates/.codex/rules/MUST-orchestrator-coordination.md +82 -425
- package/templates/.codex/rules/MUST-parallel-execution.md +33 -405
- package/templates/.codex/rules/MUST-permissions.md +14 -68
- package/templates/.codex/rules/MUST-safety.md +11 -57
- package/templates/.codex/rules/MUST-sync-verification.md +58 -214
- package/templates/.codex/rules/MUST-tool-identification.md +22 -135
- package/templates/.codex/rules/SHOULD-agent-teams.md +22 -166
- package/templates/.codex/rules/SHOULD-ecomode.md +15 -123
- package/templates/.codex/rules/SHOULD-error-handling.md +19 -88
- package/templates/.codex/rules/SHOULD-hud-statusline.md +9 -89
- package/templates/.codex/rules/SHOULD-interaction.md +18 -87
- package/templates/.codex/rules/SHOULD-memory-integration.md +25 -118
- package/templates/.codex/skills/dev-lead-routing/SKILL.md +70 -243
- package/templates/.codex/skills/springboot-best-practices/SKILL.md +180 -319
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: "설치된 컴포넌트 목록 표시",
|
|
@@ -12029,69 +12295,11 @@ var $stringify = publicApi.stringify;
|
|
|
12029
12295
|
var $visit = visit.visit;
|
|
12030
12296
|
var $visitAsync = visit.visitAsync;
|
|
12031
12297
|
|
|
12032
|
-
// src/core/
|
|
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
|
|
12298
|
+
// src/core/config.ts
|
|
12091
12299
|
import { join as join2 } from "node:path";
|
|
12092
12300
|
|
|
12093
12301
|
// src/utils/fs.ts
|
|
12094
|
-
import { dirname, join, resolve } from "node:path";
|
|
12302
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
12095
12303
|
import { fileURLToPath } from "node:url";
|
|
12096
12304
|
async function fileExists(path) {
|
|
12097
12305
|
const fs = await import("node:fs/promises");
|
|
@@ -12156,6 +12364,25 @@ async function handleFile(srcPath, destPath, options, fs) {
|
|
|
12156
12364
|
await fs.utimes(destPath, stats.atime, stats.mtime);
|
|
12157
12365
|
}
|
|
12158
12366
|
}
|
|
12367
|
+
function shouldSkipPath(destPath, destRoot, skipPaths) {
|
|
12368
|
+
if (!skipPaths || skipPaths.length === 0) {
|
|
12369
|
+
return false;
|
|
12370
|
+
}
|
|
12371
|
+
const relativePath = relative(destRoot, destPath);
|
|
12372
|
+
for (const skipPath of skipPaths) {
|
|
12373
|
+
if (skipPath.endsWith("/")) {
|
|
12374
|
+
const dirPath = skipPath.slice(0, -1);
|
|
12375
|
+
if (relativePath === dirPath || relativePath.startsWith(dirPath + sep)) {
|
|
12376
|
+
return true;
|
|
12377
|
+
}
|
|
12378
|
+
} else {
|
|
12379
|
+
if (relativePath === skipPath) {
|
|
12380
|
+
return true;
|
|
12381
|
+
}
|
|
12382
|
+
}
|
|
12383
|
+
}
|
|
12384
|
+
return false;
|
|
12385
|
+
}
|
|
12159
12386
|
async function copyDirectory(src, dest, options = {}) {
|
|
12160
12387
|
const fs = await import("node:fs/promises");
|
|
12161
12388
|
const path = await import("node:path");
|
|
@@ -12167,6 +12394,9 @@ async function copyDirectory(src, dest, options = {}) {
|
|
|
12167
12394
|
}
|
|
12168
12395
|
const srcPath = path.join(src, entry.name);
|
|
12169
12396
|
const destPath = path.join(dest, entry.name);
|
|
12397
|
+
if (shouldSkipPath(destPath, dest, options.skipPaths)) {
|
|
12398
|
+
continue;
|
|
12399
|
+
}
|
|
12170
12400
|
if (entry.isSymbolicLink()) {
|
|
12171
12401
|
await handleSymlink(srcPath, destPath, options, fs);
|
|
12172
12402
|
} else if (entry.isDirectory()) {
|
|
@@ -12190,6 +12420,11 @@ async function readTextFile(path) {
|
|
|
12190
12420
|
const fs = await import("node:fs/promises");
|
|
12191
12421
|
return fs.readFile(path, "utf-8");
|
|
12192
12422
|
}
|
|
12423
|
+
async function writeTextFile(path, content) {
|
|
12424
|
+
const fs = await import("node:fs/promises");
|
|
12425
|
+
await ensureDirectory(dirname(path));
|
|
12426
|
+
await fs.writeFile(path, content, "utf-8");
|
|
12427
|
+
}
|
|
12193
12428
|
function getPackageRoot() {
|
|
12194
12429
|
const currentFile = fileURLToPath(import.meta.url);
|
|
12195
12430
|
const currentDir = dirname(currentFile);
|
|
@@ -12223,124 +12458,479 @@ function matchesPattern(filename, pattern) {
|
|
|
12223
12458
|
return regex.test(filename);
|
|
12224
12459
|
}
|
|
12225
12460
|
|
|
12226
|
-
// src/
|
|
12227
|
-
var
|
|
12228
|
-
|
|
12229
|
-
|
|
12230
|
-
|
|
12231
|
-
|
|
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"]
|
|
12461
|
+
// src/utils/logger.ts
|
|
12462
|
+
var currentOptions = {
|
|
12463
|
+
level: "info",
|
|
12464
|
+
colors: true,
|
|
12465
|
+
locale: "en",
|
|
12466
|
+
timestamps: false
|
|
12236
12467
|
};
|
|
12237
|
-
var
|
|
12238
|
-
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
|
|
12257
|
-
|
|
12258
|
-
|
|
12259
|
-
|
|
12260
|
-
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12275
|
-
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
|
|
12279
|
-
|
|
12280
|
-
}
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
}
|
|
12295
|
-
|
|
12296
|
-
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
12305
|
-
}
|
|
12306
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12327
|
-
|
|
12328
|
-
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
}
|
|
12337
|
-
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12468
|
+
var LOG_LEVELS = {
|
|
12469
|
+
debug: 0,
|
|
12470
|
+
info: 1,
|
|
12471
|
+
warn: 2,
|
|
12472
|
+
error: 3
|
|
12473
|
+
};
|
|
12474
|
+
var COLORS = {
|
|
12475
|
+
reset: "\x1B[0m",
|
|
12476
|
+
bold: "\x1B[1m",
|
|
12477
|
+
dim: "\x1B[2m",
|
|
12478
|
+
black: "\x1B[30m",
|
|
12479
|
+
red: "\x1B[31m",
|
|
12480
|
+
green: "\x1B[32m",
|
|
12481
|
+
yellow: "\x1B[33m",
|
|
12482
|
+
blue: "\x1B[34m",
|
|
12483
|
+
magenta: "\x1B[35m",
|
|
12484
|
+
cyan: "\x1B[36m",
|
|
12485
|
+
white: "\x1B[37m",
|
|
12486
|
+
brightRed: "\x1B[91m",
|
|
12487
|
+
brightGreen: "\x1B[92m",
|
|
12488
|
+
brightYellow: "\x1B[93m",
|
|
12489
|
+
brightBlue: "\x1B[94m",
|
|
12490
|
+
brightMagenta: "\x1B[95m",
|
|
12491
|
+
brightCyan: "\x1B[96m"
|
|
12492
|
+
};
|
|
12493
|
+
var LEVEL_COLORS = {
|
|
12494
|
+
debug: COLORS.dim,
|
|
12495
|
+
info: COLORS.blue,
|
|
12496
|
+
warn: COLORS.yellow,
|
|
12497
|
+
error: COLORS.red
|
|
12498
|
+
};
|
|
12499
|
+
var LEVEL_ICONS = {
|
|
12500
|
+
debug: "\uD83D\uDD0D",
|
|
12501
|
+
info: "ℹ️",
|
|
12502
|
+
warn: "⚠️",
|
|
12503
|
+
error: "❌"
|
|
12504
|
+
};
|
|
12505
|
+
var MESSAGES = {
|
|
12506
|
+
en: {
|
|
12507
|
+
"install.start": "Initializing oh-my-customcode...",
|
|
12508
|
+
"install.success": "Successfully initialized!",
|
|
12509
|
+
"install.failed": "Installation failed: {{error}}",
|
|
12510
|
+
"install.exists": "Existing {{rootDir}} directory found",
|
|
12511
|
+
"install.backup": "Backed up existing files to: {{path}}",
|
|
12512
|
+
"install.directories_created": "Directory structure created",
|
|
12513
|
+
"install.component_skipped": "Skipped {{component}} (already exists)",
|
|
12514
|
+
"install.component_installed": "Installed {{component}}",
|
|
12515
|
+
"install.template_not_found": "Template not found for {{component}}: {{path}}",
|
|
12516
|
+
"install.claude_md_installed": "CLAUDE.md installed ({{language}})",
|
|
12517
|
+
"install.claude_md_not_found": "CLAUDE.md template not found for {{language}}",
|
|
12518
|
+
"install.entry_md_installed": "{{entry}} installed ({{language}})",
|
|
12519
|
+
"install.entry_md_not_found": "{{entry}} template not found for {{language}}",
|
|
12520
|
+
"install.entry_md_skipped": "{{entry}} skipped ({{reason}})",
|
|
12521
|
+
"update.start": "Checking for updates...",
|
|
12522
|
+
"update.success": "Updated from {{from}} to {{to}}",
|
|
12523
|
+
"update.failed": "Update failed: {{error}}",
|
|
12524
|
+
"update.no_updates": "Already up to date",
|
|
12525
|
+
"update.backup_created": "Backup created at: {{path}}",
|
|
12526
|
+
"update.dry_run": "Would update {{component}}",
|
|
12527
|
+
"update.component_updated": "Updated {{component}}",
|
|
12528
|
+
"update.file_applied": "Applied update to {{path}}",
|
|
12529
|
+
"config.load_failed": "Failed to load config: {{error}}",
|
|
12530
|
+
"config.not_found": "Config not found at {{path}}, using defaults",
|
|
12531
|
+
"config.saved": "Config saved to {{path}}",
|
|
12532
|
+
"config.deleted": "Config deleted from {{path}}",
|
|
12533
|
+
"general.done": "Done!",
|
|
12534
|
+
"general.failed": "Failed",
|
|
12535
|
+
"general.skipped": "Skipped"
|
|
12536
|
+
},
|
|
12537
|
+
ko: {
|
|
12538
|
+
"install.start": "oh-my-customcode 초기화 중...",
|
|
12539
|
+
"install.success": "초기화 완료!",
|
|
12540
|
+
"install.failed": "설치 실패: {{error}}",
|
|
12541
|
+
"install.exists": "기존 {{rootDir}} 디렉토리 발견",
|
|
12542
|
+
"install.backup": "기존 파일 백업 완료: {{path}}",
|
|
12543
|
+
"install.directories_created": "디렉토리 구조 생성 완료",
|
|
12544
|
+
"install.component_skipped": "{{component}} 건너뜀 (이미 존재)",
|
|
12545
|
+
"install.component_installed": "{{component}} 설치 완료",
|
|
12546
|
+
"install.template_not_found": "{{component}} 템플릿 없음: {{path}}",
|
|
12547
|
+
"install.claude_md_installed": "CLAUDE.md 설치 완료 ({{language}})",
|
|
12548
|
+
"install.claude_md_not_found": "{{language}}용 CLAUDE.md 템플릿 없음",
|
|
12549
|
+
"install.entry_md_installed": "{{entry}} 설치 완료 ({{language}})",
|
|
12550
|
+
"install.entry_md_not_found": "{{language}}용 {{entry}} 템플릿 없음",
|
|
12551
|
+
"install.entry_md_skipped": "{{entry}} 건너뜀 ({{reason}})",
|
|
12552
|
+
"update.start": "업데이트 확인 중...",
|
|
12553
|
+
"update.success": "{{from}}에서 {{to}}로 업데이트 완료",
|
|
12554
|
+
"update.failed": "업데이트 실패: {{error}}",
|
|
12555
|
+
"update.no_updates": "이미 최신 버전입니다",
|
|
12556
|
+
"update.backup_created": "백업 생성됨: {{path}}",
|
|
12557
|
+
"update.dry_run": "{{component}} 업데이트 예정",
|
|
12558
|
+
"update.component_updated": "{{component}} 업데이트 완료",
|
|
12559
|
+
"update.file_applied": "{{path}} 업데이트 적용",
|
|
12560
|
+
"config.load_failed": "설정 로드 실패: {{error}}",
|
|
12561
|
+
"config.not_found": "{{path}}에 설정 없음, 기본값 사용",
|
|
12562
|
+
"config.saved": "설정 저장: {{path}}",
|
|
12563
|
+
"config.deleted": "설정 삭제: {{path}}",
|
|
12564
|
+
"general.done": "완료!",
|
|
12565
|
+
"general.failed": "실패",
|
|
12566
|
+
"general.skipped": "건너뜀"
|
|
12567
|
+
}
|
|
12568
|
+
};
|
|
12569
|
+
function getMessage(key, params) {
|
|
12570
|
+
const messages = MESSAGES[currentOptions.locale] || MESSAGES.en;
|
|
12571
|
+
let message = messages[key] || key;
|
|
12572
|
+
if (params) {
|
|
12573
|
+
for (const [k, v] of Object.entries(params)) {
|
|
12574
|
+
message = message.replace(new RegExp(`{{${k}}}`, "g"), v);
|
|
12575
|
+
}
|
|
12576
|
+
}
|
|
12577
|
+
return message;
|
|
12578
|
+
}
|
|
12579
|
+
function formatMessage(level, message) {
|
|
12580
|
+
const parts = [];
|
|
12581
|
+
if (currentOptions.timestamps) {
|
|
12582
|
+
const timestamp = new Date().toISOString().slice(11, 19);
|
|
12583
|
+
if (currentOptions.colors) {
|
|
12584
|
+
parts.push(`${COLORS.dim}[${timestamp}]${COLORS.reset}`);
|
|
12585
|
+
} else {
|
|
12586
|
+
parts.push(`[${timestamp}]`);
|
|
12587
|
+
}
|
|
12588
|
+
}
|
|
12589
|
+
if (currentOptions.prefix) {
|
|
12590
|
+
if (currentOptions.colors) {
|
|
12591
|
+
parts.push(`${COLORS.cyan}[${currentOptions.prefix}]${COLORS.reset}`);
|
|
12592
|
+
} else {
|
|
12593
|
+
parts.push(`[${currentOptions.prefix}]`);
|
|
12594
|
+
}
|
|
12595
|
+
}
|
|
12596
|
+
if (currentOptions.colors) {
|
|
12597
|
+
const color = LEVEL_COLORS[level];
|
|
12598
|
+
const icon = LEVEL_ICONS[level];
|
|
12599
|
+
parts.push(`${color}${icon}${COLORS.reset}`);
|
|
12600
|
+
} else {
|
|
12601
|
+
parts.push(`[${level.toUpperCase()}]`);
|
|
12602
|
+
}
|
|
12603
|
+
if (currentOptions.colors && level === "error") {
|
|
12604
|
+
parts.push(`${COLORS.red}${message}${COLORS.reset}`);
|
|
12605
|
+
} else if (currentOptions.colors && level === "warn") {
|
|
12606
|
+
parts.push(`${COLORS.yellow}${message}${COLORS.reset}`);
|
|
12607
|
+
} else {
|
|
12608
|
+
parts.push(message);
|
|
12609
|
+
}
|
|
12610
|
+
return parts.join(" ");
|
|
12611
|
+
}
|
|
12612
|
+
function shouldLog(level) {
|
|
12613
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[currentOptions.level];
|
|
12614
|
+
}
|
|
12615
|
+
function debug(messageKey, params) {
|
|
12616
|
+
if (shouldLog("debug")) {
|
|
12617
|
+
const message = getMessage(messageKey, params);
|
|
12618
|
+
console.debug(formatMessage("debug", message));
|
|
12619
|
+
}
|
|
12620
|
+
}
|
|
12621
|
+
function info(messageKey, params) {
|
|
12622
|
+
if (shouldLog("info")) {
|
|
12623
|
+
const message = getMessage(messageKey, params);
|
|
12624
|
+
console.info(formatMessage("info", message));
|
|
12625
|
+
}
|
|
12626
|
+
}
|
|
12627
|
+
function warn(messageKey, params) {
|
|
12628
|
+
if (shouldLog("warn")) {
|
|
12629
|
+
const message = getMessage(messageKey, params);
|
|
12630
|
+
console.warn(formatMessage("warn", message));
|
|
12631
|
+
}
|
|
12632
|
+
}
|
|
12633
|
+
function error(messageKey, params) {
|
|
12634
|
+
if (shouldLog("error")) {
|
|
12635
|
+
const message = getMessage(messageKey, params);
|
|
12636
|
+
console.error(formatMessage("error", message));
|
|
12637
|
+
}
|
|
12638
|
+
}
|
|
12639
|
+
function success(messageKey, params) {
|
|
12640
|
+
if (shouldLog("info")) {
|
|
12641
|
+
const message = getMessage(messageKey, params);
|
|
12642
|
+
if (currentOptions.colors) {
|
|
12643
|
+
console.info(`${COLORS.green}✓${COLORS.reset} ${message}`);
|
|
12644
|
+
} else {
|
|
12645
|
+
console.info(`[SUCCESS] ${message}`);
|
|
12646
|
+
}
|
|
12647
|
+
}
|
|
12648
|
+
}
|
|
12649
|
+
|
|
12650
|
+
// src/core/config.ts
|
|
12651
|
+
var CONFIG_FILE = ".omcustomrc.json";
|
|
12652
|
+
var CURRENT_CONFIG_VERSION = 1;
|
|
12653
|
+
function getDefaultConfig() {
|
|
12654
|
+
return {
|
|
12655
|
+
configVersion: CURRENT_CONFIG_VERSION,
|
|
12656
|
+
version: "0.0.0",
|
|
12657
|
+
language: "en",
|
|
12658
|
+
provider: "auto",
|
|
12659
|
+
installedAt: "",
|
|
12660
|
+
lastUpdated: "",
|
|
12661
|
+
installedComponents: [],
|
|
12662
|
+
componentVersions: {},
|
|
12663
|
+
agents: {},
|
|
12664
|
+
preferences: getDefaultPreferences(),
|
|
12665
|
+
sourceRepo: "https://github.com/baekenough/oh-my-customcode",
|
|
12666
|
+
autoUpdate: {
|
|
12667
|
+
enabled: false,
|
|
12668
|
+
checkIntervalHours: 24,
|
|
12669
|
+
autoApplyMinor: false
|
|
12670
|
+
},
|
|
12671
|
+
preserveFiles: [],
|
|
12672
|
+
customComponents: []
|
|
12673
|
+
};
|
|
12674
|
+
}
|
|
12675
|
+
function getDefaultPreferences() {
|
|
12676
|
+
return {
|
|
12677
|
+
logLevel: "info",
|
|
12678
|
+
colors: true,
|
|
12679
|
+
showProgress: true,
|
|
12680
|
+
confirmPrompts: true,
|
|
12681
|
+
autoBackup: true
|
|
12682
|
+
};
|
|
12683
|
+
}
|
|
12684
|
+
function getConfigPath(targetDir) {
|
|
12685
|
+
return join2(targetDir, CONFIG_FILE);
|
|
12686
|
+
}
|
|
12687
|
+
async function loadConfig(targetDir) {
|
|
12688
|
+
const configPath = getConfigPath(targetDir);
|
|
12689
|
+
if (await fileExists(configPath)) {
|
|
12690
|
+
try {
|
|
12691
|
+
const config = await readJsonFile(configPath);
|
|
12692
|
+
const merged = mergeConfig(getDefaultConfig(), config);
|
|
12693
|
+
if (merged.configVersion < CURRENT_CONFIG_VERSION) {
|
|
12694
|
+
const migrated = migrateConfig(merged);
|
|
12695
|
+
await saveConfig(targetDir, migrated);
|
|
12696
|
+
return migrated;
|
|
12697
|
+
}
|
|
12698
|
+
return merged;
|
|
12699
|
+
} catch (err) {
|
|
12700
|
+
warn("config.load_failed", { error: String(err) });
|
|
12701
|
+
return getDefaultConfig();
|
|
12702
|
+
}
|
|
12703
|
+
}
|
|
12704
|
+
debug("config.not_found", { path: configPath });
|
|
12705
|
+
return getDefaultConfig();
|
|
12706
|
+
}
|
|
12707
|
+
async function saveConfig(targetDir, config) {
|
|
12708
|
+
const configPath = getConfigPath(targetDir);
|
|
12709
|
+
await ensureDirectory(targetDir);
|
|
12710
|
+
config.lastUpdated = new Date().toISOString();
|
|
12711
|
+
await writeJsonFile(configPath, config);
|
|
12712
|
+
debug("config.saved", { path: configPath });
|
|
12713
|
+
}
|
|
12714
|
+
function deduplicateCustomComponents(components) {
|
|
12715
|
+
const seen = new Map;
|
|
12716
|
+
for (const c of components) {
|
|
12717
|
+
seen.set(c.path, c);
|
|
12718
|
+
}
|
|
12719
|
+
return [...seen.values()];
|
|
12720
|
+
}
|
|
12721
|
+
function mergeConfig(defaults, overrides) {
|
|
12722
|
+
return {
|
|
12723
|
+
...defaults,
|
|
12724
|
+
...overrides,
|
|
12725
|
+
preferences: overrides.preferences ? { ...defaults.preferences, ...overrides.preferences } : defaults.preferences,
|
|
12726
|
+
autoUpdate: overrides.autoUpdate ? { ...defaults.autoUpdate, ...overrides.autoUpdate } : defaults.autoUpdate,
|
|
12727
|
+
componentVersions: {
|
|
12728
|
+
...defaults.componentVersions,
|
|
12729
|
+
...overrides.componentVersions
|
|
12730
|
+
},
|
|
12731
|
+
agents: {
|
|
12732
|
+
...defaults.agents,
|
|
12733
|
+
...overrides.agents
|
|
12734
|
+
},
|
|
12735
|
+
preserveFiles: overrides.preserveFiles ? [...new Set([...defaults.preserveFiles || [], ...overrides.preserveFiles])] : defaults.preserveFiles,
|
|
12736
|
+
customComponents: overrides.customComponents ? deduplicateCustomComponents([
|
|
12737
|
+
...defaults.customComponents || [],
|
|
12738
|
+
...overrides.customComponents
|
|
12739
|
+
]) : defaults.customComponents
|
|
12740
|
+
};
|
|
12741
|
+
}
|
|
12742
|
+
function migrateConfig(config) {
|
|
12743
|
+
const migrated = { ...config };
|
|
12744
|
+
if (config.configVersion < 1) {
|
|
12745
|
+
migrated.configVersion = 1;
|
|
12746
|
+
migrated.preferences = getDefaultPreferences();
|
|
12747
|
+
migrated.autoUpdate = {
|
|
12748
|
+
enabled: false,
|
|
12749
|
+
checkIntervalHours: 24,
|
|
12750
|
+
autoApplyMinor: false
|
|
12751
|
+
};
|
|
12752
|
+
}
|
|
12753
|
+
migrated.configVersion = CURRENT_CONFIG_VERSION;
|
|
12754
|
+
return migrated;
|
|
12755
|
+
}
|
|
12756
|
+
|
|
12757
|
+
// src/core/layout.ts
|
|
12758
|
+
var PROVIDER_LAYOUTS = {
|
|
12759
|
+
claude: {
|
|
12760
|
+
provider: "claude",
|
|
12761
|
+
rootDir: ".claude",
|
|
12762
|
+
entryFile: "CLAUDE.md",
|
|
12763
|
+
entryTemplatePrefix: "CLAUDE.md",
|
|
12764
|
+
manifestFile: "manifest.json",
|
|
12765
|
+
backupDirPrefix: ".claude-backup-",
|
|
12766
|
+
directoryStructure: [
|
|
12767
|
+
".claude",
|
|
12768
|
+
".claude/rules",
|
|
12769
|
+
".claude/hooks",
|
|
12770
|
+
".claude/contexts",
|
|
12771
|
+
".claude/agents",
|
|
12772
|
+
".claude/skills",
|
|
12773
|
+
"guides"
|
|
12774
|
+
]
|
|
12775
|
+
},
|
|
12776
|
+
codex: {
|
|
12777
|
+
provider: "codex",
|
|
12778
|
+
rootDir: ".codex",
|
|
12779
|
+
entryFile: "AGENTS.md",
|
|
12780
|
+
entryTemplatePrefix: "AGENTS.md",
|
|
12781
|
+
manifestFile: "manifest.codex.json",
|
|
12782
|
+
backupDirPrefix: ".codex-backup-",
|
|
12783
|
+
directoryStructure: [
|
|
12784
|
+
".codex",
|
|
12785
|
+
".codex/rules",
|
|
12786
|
+
".codex/hooks",
|
|
12787
|
+
".codex/contexts",
|
|
12788
|
+
".codex/agents",
|
|
12789
|
+
".codex/skills",
|
|
12790
|
+
"guides"
|
|
12791
|
+
]
|
|
12792
|
+
}
|
|
12793
|
+
};
|
|
12794
|
+
function getProviderLayout(provider) {
|
|
12795
|
+
return PROVIDER_LAYOUTS[provider];
|
|
12796
|
+
}
|
|
12797
|
+
function getEntryTemplateName(provider, language) {
|
|
12798
|
+
const layout = getProviderLayout(provider);
|
|
12799
|
+
return `${layout.entryTemplatePrefix}.${language}`;
|
|
12800
|
+
}
|
|
12801
|
+
function getComponentPath(provider, component) {
|
|
12802
|
+
const layout = getProviderLayout(provider);
|
|
12803
|
+
if (component === "entry-md") {
|
|
12804
|
+
return layout.entryFile;
|
|
12805
|
+
}
|
|
12806
|
+
if (component === "guides") {
|
|
12807
|
+
return "guides";
|
|
12808
|
+
}
|
|
12809
|
+
return `${layout.rootDir}/${component}`;
|
|
12810
|
+
}
|
|
12811
|
+
function getDefaultProvider() {
|
|
12812
|
+
return "claude";
|
|
12813
|
+
}
|
|
12814
|
+
|
|
12815
|
+
// src/core/provider.ts
|
|
12816
|
+
import { join as join3 } from "node:path";
|
|
12817
|
+
var ENV_SIGNALS = {
|
|
12818
|
+
claude: [
|
|
12819
|
+
"ANTHROPIC_API_KEY",
|
|
12820
|
+
"CLAUDE_CODE",
|
|
12821
|
+
"CLAUDE_CODE_EFFORT_LEVEL",
|
|
12822
|
+
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS",
|
|
12823
|
+
"CLAUDE_CODE_ENABLE_TELEMETRY"
|
|
12824
|
+
],
|
|
12825
|
+
codex: ["OPENAI_API_KEY", "OPENAI_ORG_ID", "OPENAI_PROJECT", "CODEX_HOME", "CODEX_PROJECT"]
|
|
12826
|
+
};
|
|
12827
|
+
var PROVIDER_ENV_OVERRIDES = ["OMCUSTOM_PROVIDER", "LLM_SERVICE"];
|
|
12828
|
+
function normalizeProvider(value) {
|
|
12829
|
+
if (!value)
|
|
12830
|
+
return null;
|
|
12831
|
+
const normalized = value.toLowerCase().trim();
|
|
12832
|
+
if (normalized === "claude" || normalized === "codex" || normalized === "auto") {
|
|
12833
|
+
return normalized;
|
|
12834
|
+
}
|
|
12835
|
+
return null;
|
|
12836
|
+
}
|
|
12837
|
+
function detectFromEnv(env) {
|
|
12838
|
+
for (const key of PROVIDER_ENV_OVERRIDES) {
|
|
12839
|
+
const override = normalizeProvider(env[key]);
|
|
12840
|
+
if (override && override !== "auto") {
|
|
12841
|
+
return {
|
|
12842
|
+
provider: override,
|
|
12843
|
+
source: "override",
|
|
12844
|
+
confidence: "high",
|
|
12845
|
+
reason: `env:${key}`
|
|
12846
|
+
};
|
|
12847
|
+
}
|
|
12848
|
+
}
|
|
12849
|
+
const claudeSignals = ENV_SIGNALS.claude.filter((key) => Boolean(env[key]));
|
|
12850
|
+
const codexSignals = ENV_SIGNALS.codex.filter((key) => Boolean(env[key]));
|
|
12851
|
+
const hasClaude = claudeSignals.length > 0;
|
|
12852
|
+
const hasCodex = codexSignals.length > 0;
|
|
12853
|
+
if (hasClaude && !hasCodex) {
|
|
12854
|
+
return {
|
|
12855
|
+
provider: "claude",
|
|
12856
|
+
source: "env",
|
|
12857
|
+
confidence: "medium",
|
|
12858
|
+
reason: `env:${claudeSignals[0]}`
|
|
12859
|
+
};
|
|
12860
|
+
}
|
|
12861
|
+
if (hasCodex && !hasClaude) {
|
|
12862
|
+
return {
|
|
12863
|
+
provider: "codex",
|
|
12864
|
+
source: "env",
|
|
12865
|
+
confidence: "medium",
|
|
12866
|
+
reason: `env:${codexSignals[0]}`
|
|
12867
|
+
};
|
|
12868
|
+
}
|
|
12869
|
+
return null;
|
|
12870
|
+
}
|
|
12871
|
+
async function detectFromProject(targetDir) {
|
|
12872
|
+
const claudeMarkers = [join3(targetDir, "CLAUDE.md"), join3(targetDir, ".claude")];
|
|
12873
|
+
const codexMarkers = [join3(targetDir, "AGENTS.md"), join3(targetDir, ".codex")];
|
|
12874
|
+
const claudeFound = await Promise.all(claudeMarkers.map((path) => fileExists(path)));
|
|
12875
|
+
const codexFound = await Promise.all(codexMarkers.map((path) => fileExists(path)));
|
|
12876
|
+
const hasClaude = claudeFound.some(Boolean);
|
|
12877
|
+
const hasCodex = codexFound.some(Boolean);
|
|
12878
|
+
if (hasClaude && !hasCodex) {
|
|
12879
|
+
return {
|
|
12880
|
+
provider: "claude",
|
|
12881
|
+
source: "project",
|
|
12882
|
+
confidence: "medium",
|
|
12883
|
+
reason: "project:claude"
|
|
12884
|
+
};
|
|
12885
|
+
}
|
|
12886
|
+
if (hasCodex && !hasClaude) {
|
|
12887
|
+
return {
|
|
12888
|
+
provider: "codex",
|
|
12889
|
+
source: "project",
|
|
12890
|
+
confidence: "medium",
|
|
12891
|
+
reason: "project:codex"
|
|
12892
|
+
};
|
|
12893
|
+
}
|
|
12894
|
+
return null;
|
|
12895
|
+
}
|
|
12896
|
+
async function detectFromConfig(targetDir) {
|
|
12897
|
+
const configPath = join3(targetDir, ".omcustomrc.json");
|
|
12898
|
+
if (!await fileExists(configPath)) {
|
|
12899
|
+
return null;
|
|
12900
|
+
}
|
|
12901
|
+
try {
|
|
12902
|
+
const config = await readJsonFile(configPath);
|
|
12903
|
+
const provider = normalizeProvider(config.provider);
|
|
12904
|
+
if (provider && provider !== "auto") {
|
|
12905
|
+
return {
|
|
12906
|
+
provider,
|
|
12907
|
+
source: "config",
|
|
12908
|
+
confidence: "high",
|
|
12909
|
+
reason: "config:provider"
|
|
12910
|
+
};
|
|
12911
|
+
}
|
|
12912
|
+
} catch {}
|
|
12913
|
+
return null;
|
|
12914
|
+
}
|
|
12915
|
+
async function detectProvider(options = {}) {
|
|
12916
|
+
const env = options.env ?? process.env;
|
|
12917
|
+
const override = options.override;
|
|
12918
|
+
const normalizedOverride = normalizeProvider(override);
|
|
12919
|
+
if (normalizedOverride && normalizedOverride !== "auto") {
|
|
12920
|
+
return {
|
|
12921
|
+
provider: normalizedOverride,
|
|
12922
|
+
source: "override",
|
|
12923
|
+
confidence: "high",
|
|
12924
|
+
reason: "override:option"
|
|
12925
|
+
};
|
|
12926
|
+
}
|
|
12927
|
+
if (options.targetDir) {
|
|
12928
|
+
const fromConfig = await detectFromConfig(options.targetDir);
|
|
12929
|
+
if (fromConfig) {
|
|
12930
|
+
return fromConfig;
|
|
12931
|
+
}
|
|
12932
|
+
}
|
|
12933
|
+
if (options.targetDir && options.preferProject) {
|
|
12344
12934
|
const fromProject = await detectFromProject(options.targetDir);
|
|
12345
12935
|
if (fromProject) {
|
|
12346
12936
|
return fromProject;
|
|
@@ -12690,6 +13280,49 @@ async function checkContexts(targetDir, rootDir = ".claude") {
|
|
|
12690
13280
|
fixable: false
|
|
12691
13281
|
};
|
|
12692
13282
|
}
|
|
13283
|
+
async function checkCustomComponents(targetDir, _rootDir = ".claude") {
|
|
13284
|
+
try {
|
|
13285
|
+
const config = await loadConfig(targetDir);
|
|
13286
|
+
const customComponents = config.customComponents || [];
|
|
13287
|
+
if (customComponents.length === 0) {
|
|
13288
|
+
return {
|
|
13289
|
+
name: "Custom components",
|
|
13290
|
+
status: "pass",
|
|
13291
|
+
message: "No custom components configured",
|
|
13292
|
+
fixable: false
|
|
13293
|
+
};
|
|
13294
|
+
}
|
|
13295
|
+
const missing = [];
|
|
13296
|
+
for (const component of customComponents) {
|
|
13297
|
+
const fullPath = path.join(targetDir, component.path);
|
|
13298
|
+
if (!await pathExists(fullPath)) {
|
|
13299
|
+
missing.push(component.path);
|
|
13300
|
+
}
|
|
13301
|
+
}
|
|
13302
|
+
if (missing.length > 0) {
|
|
13303
|
+
return {
|
|
13304
|
+
name: "Custom components",
|
|
13305
|
+
status: "warn",
|
|
13306
|
+
message: `Custom components: ${customComponents.length} items (${missing.length} missing)`,
|
|
13307
|
+
fixable: false,
|
|
13308
|
+
details: missing
|
|
13309
|
+
};
|
|
13310
|
+
}
|
|
13311
|
+
return {
|
|
13312
|
+
name: "Custom components",
|
|
13313
|
+
status: "pass",
|
|
13314
|
+
message: `Custom components: ${customComponents.length} items (managed: false)`,
|
|
13315
|
+
fixable: false
|
|
13316
|
+
};
|
|
13317
|
+
} catch {
|
|
13318
|
+
return {
|
|
13319
|
+
name: "Custom components",
|
|
13320
|
+
status: "pass",
|
|
13321
|
+
message: "No config file found",
|
|
13322
|
+
fixable: false
|
|
13323
|
+
};
|
|
13324
|
+
}
|
|
13325
|
+
}
|
|
12693
13326
|
async function fixBrokenSymlinks(_targetDir, brokenSymlinks) {
|
|
12694
13327
|
let fixed = 0;
|
|
12695
13328
|
for (const symlink of brokenSymlinks) {
|
|
@@ -12777,7 +13410,8 @@ async function doctorCommand(options = {}) {
|
|
|
12777
13410
|
checkIndexFiles(targetDir),
|
|
12778
13411
|
checkGuides(targetDir),
|
|
12779
13412
|
checkHooks(targetDir, layout.rootDir),
|
|
12780
|
-
checkContexts(targetDir, layout.rootDir)
|
|
13413
|
+
checkContexts(targetDir, layout.rootDir),
|
|
13414
|
+
checkCustomComponents(targetDir, layout.rootDir)
|
|
12781
13415
|
]);
|
|
12782
13416
|
if (options.fix) {
|
|
12783
13417
|
const hasFixableIssues = checks.some((c) => c.status === "fail" && c.fixable);
|
|
@@ -12790,332 +13424,47 @@ async function doctorCommand(options = {}) {
|
|
|
12790
13424
|
}
|
|
12791
13425
|
for (const check of checks) {
|
|
12792
13426
|
if (!options.quiet || check.status !== "pass") {
|
|
12793
|
-
printCheck(check);
|
|
12794
|
-
}
|
|
12795
|
-
}
|
|
12796
|
-
const passCount = checks.filter((c) => c.status === "pass" || c.fixed).length;
|
|
12797
|
-
const warnCount = checks.filter((c) => c.status === "warn").length;
|
|
12798
|
-
const failCount = checks.filter((c) => c.status === "fail" && !c.fixed).length;
|
|
12799
|
-
const fixedCount = checks.filter((c) => c.fixed).length;
|
|
12800
|
-
console.log("");
|
|
12801
|
-
if (failCount === 0) {
|
|
12802
|
-
console.log(i18n.t("cli.doctor.passed"));
|
|
12803
|
-
} else {
|
|
12804
|
-
console.log(i18n.t("cli.doctor.failed"));
|
|
12805
|
-
if (!options.fix) {
|
|
12806
|
-
const fixableCount = checks.filter((c) => c.status === "fail" && c.fixable).length;
|
|
12807
|
-
if (fixableCount > 0) {
|
|
12808
|
-
console.log(i18n.t("cli.doctor.runWithFix", { count: fixableCount }));
|
|
12809
|
-
}
|
|
12810
|
-
}
|
|
12811
|
-
}
|
|
12812
|
-
console.log(i18n.t("cli.doctor.summary", {
|
|
12813
|
-
pass: passCount,
|
|
12814
|
-
warn: warnCount,
|
|
12815
|
-
fail: failCount,
|
|
12816
|
-
fixed: fixedCount
|
|
12817
|
-
}));
|
|
12818
|
-
return {
|
|
12819
|
-
success: failCount === 0,
|
|
12820
|
-
checks,
|
|
12821
|
-
passCount,
|
|
12822
|
-
warnCount,
|
|
12823
|
-
failCount,
|
|
12824
|
-
fixedCount
|
|
12825
|
-
};
|
|
12826
|
-
}
|
|
12827
|
-
|
|
12828
|
-
// src/cli/init.ts
|
|
12829
|
-
import { join as join5 } from "node:path";
|
|
12830
|
-
|
|
12831
|
-
// src/core/installer.ts
|
|
12832
|
-
import { copyFile as fsCopyFile, rename } from "node:fs/promises";
|
|
12833
|
-
import { basename, join as join4 } from "node:path";
|
|
12834
|
-
|
|
12835
|
-
// src/utils/logger.ts
|
|
12836
|
-
var currentOptions = {
|
|
12837
|
-
level: "info",
|
|
12838
|
-
colors: true,
|
|
12839
|
-
locale: "en",
|
|
12840
|
-
timestamps: false
|
|
12841
|
-
};
|
|
12842
|
-
var LOG_LEVELS = {
|
|
12843
|
-
debug: 0,
|
|
12844
|
-
info: 1,
|
|
12845
|
-
warn: 2,
|
|
12846
|
-
error: 3
|
|
12847
|
-
};
|
|
12848
|
-
var COLORS = {
|
|
12849
|
-
reset: "\x1B[0m",
|
|
12850
|
-
bold: "\x1B[1m",
|
|
12851
|
-
dim: "\x1B[2m",
|
|
12852
|
-
black: "\x1B[30m",
|
|
12853
|
-
red: "\x1B[31m",
|
|
12854
|
-
green: "\x1B[32m",
|
|
12855
|
-
yellow: "\x1B[33m",
|
|
12856
|
-
blue: "\x1B[34m",
|
|
12857
|
-
magenta: "\x1B[35m",
|
|
12858
|
-
cyan: "\x1B[36m",
|
|
12859
|
-
white: "\x1B[37m",
|
|
12860
|
-
brightRed: "\x1B[91m",
|
|
12861
|
-
brightGreen: "\x1B[92m",
|
|
12862
|
-
brightYellow: "\x1B[93m",
|
|
12863
|
-
brightBlue: "\x1B[94m",
|
|
12864
|
-
brightMagenta: "\x1B[95m",
|
|
12865
|
-
brightCyan: "\x1B[96m"
|
|
12866
|
-
};
|
|
12867
|
-
var LEVEL_COLORS = {
|
|
12868
|
-
debug: COLORS.dim,
|
|
12869
|
-
info: COLORS.blue,
|
|
12870
|
-
warn: COLORS.yellow,
|
|
12871
|
-
error: COLORS.red
|
|
12872
|
-
};
|
|
12873
|
-
var LEVEL_ICONS = {
|
|
12874
|
-
debug: "\uD83D\uDD0D",
|
|
12875
|
-
info: "ℹ️",
|
|
12876
|
-
warn: "⚠️",
|
|
12877
|
-
error: "❌"
|
|
12878
|
-
};
|
|
12879
|
-
var MESSAGES = {
|
|
12880
|
-
en: {
|
|
12881
|
-
"install.start": "Initializing oh-my-customcode...",
|
|
12882
|
-
"install.success": "Successfully initialized!",
|
|
12883
|
-
"install.failed": "Installation failed: {{error}}",
|
|
12884
|
-
"install.exists": "Existing {{rootDir}} directory found",
|
|
12885
|
-
"install.backup": "Backed up existing files to: {{path}}",
|
|
12886
|
-
"install.directories_created": "Directory structure created",
|
|
12887
|
-
"install.component_skipped": "Skipped {{component}} (already exists)",
|
|
12888
|
-
"install.component_installed": "Installed {{component}}",
|
|
12889
|
-
"install.template_not_found": "Template not found for {{component}}: {{path}}",
|
|
12890
|
-
"install.claude_md_installed": "CLAUDE.md installed ({{language}})",
|
|
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}})",
|
|
12895
|
-
"update.start": "Checking for updates...",
|
|
12896
|
-
"update.success": "Updated from {{from}} to {{to}}",
|
|
12897
|
-
"update.failed": "Update failed: {{error}}",
|
|
12898
|
-
"update.no_updates": "Already up to date",
|
|
12899
|
-
"update.backup_created": "Backup created at: {{path}}",
|
|
12900
|
-
"update.dry_run": "Would update {{component}}",
|
|
12901
|
-
"update.component_updated": "Updated {{component}}",
|
|
12902
|
-
"update.file_applied": "Applied update to {{path}}",
|
|
12903
|
-
"config.load_failed": "Failed to load config: {{error}}",
|
|
12904
|
-
"config.not_found": "Config not found at {{path}}, using defaults",
|
|
12905
|
-
"config.saved": "Config saved to {{path}}",
|
|
12906
|
-
"config.deleted": "Config deleted from {{path}}",
|
|
12907
|
-
"general.done": "Done!",
|
|
12908
|
-
"general.failed": "Failed",
|
|
12909
|
-
"general.skipped": "Skipped"
|
|
12910
|
-
},
|
|
12911
|
-
ko: {
|
|
12912
|
-
"install.start": "oh-my-customcode 초기화 중...",
|
|
12913
|
-
"install.success": "초기화 완료!",
|
|
12914
|
-
"install.failed": "설치 실패: {{error}}",
|
|
12915
|
-
"install.exists": "기존 {{rootDir}} 디렉토리 발견",
|
|
12916
|
-
"install.backup": "기존 파일 백업 완료: {{path}}",
|
|
12917
|
-
"install.directories_created": "디렉토리 구조 생성 완료",
|
|
12918
|
-
"install.component_skipped": "{{component}} 건너뜀 (이미 존재)",
|
|
12919
|
-
"install.component_installed": "{{component}} 설치 완료",
|
|
12920
|
-
"install.template_not_found": "{{component}} 템플릿 없음: {{path}}",
|
|
12921
|
-
"install.claude_md_installed": "CLAUDE.md 설치 완료 ({{language}})",
|
|
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}})",
|
|
12926
|
-
"update.start": "업데이트 확인 중...",
|
|
12927
|
-
"update.success": "{{from}}에서 {{to}}로 업데이트 완료",
|
|
12928
|
-
"update.failed": "업데이트 실패: {{error}}",
|
|
12929
|
-
"update.no_updates": "이미 최신 버전입니다",
|
|
12930
|
-
"update.backup_created": "백업 생성됨: {{path}}",
|
|
12931
|
-
"update.dry_run": "{{component}} 업데이트 예정",
|
|
12932
|
-
"update.component_updated": "{{component}} 업데이트 완료",
|
|
12933
|
-
"update.file_applied": "{{path}} 업데이트 적용",
|
|
12934
|
-
"config.load_failed": "설정 로드 실패: {{error}}",
|
|
12935
|
-
"config.not_found": "{{path}}에 설정 없음, 기본값 사용",
|
|
12936
|
-
"config.saved": "설정 저장: {{path}}",
|
|
12937
|
-
"config.deleted": "설정 삭제: {{path}}",
|
|
12938
|
-
"general.done": "완료!",
|
|
12939
|
-
"general.failed": "실패",
|
|
12940
|
-
"general.skipped": "건너뜀"
|
|
12941
|
-
}
|
|
12942
|
-
};
|
|
12943
|
-
function getMessage(key, params) {
|
|
12944
|
-
const messages = MESSAGES[currentOptions.locale] || MESSAGES.en;
|
|
12945
|
-
let message = messages[key] || key;
|
|
12946
|
-
if (params) {
|
|
12947
|
-
for (const [k, v] of Object.entries(params)) {
|
|
12948
|
-
message = message.replace(new RegExp(`{{${k}}}`, "g"), v);
|
|
12949
|
-
}
|
|
12950
|
-
}
|
|
12951
|
-
return message;
|
|
12952
|
-
}
|
|
12953
|
-
function formatMessage(level, message) {
|
|
12954
|
-
const parts = [];
|
|
12955
|
-
if (currentOptions.timestamps) {
|
|
12956
|
-
const timestamp = new Date().toISOString().slice(11, 19);
|
|
12957
|
-
if (currentOptions.colors) {
|
|
12958
|
-
parts.push(`${COLORS.dim}[${timestamp}]${COLORS.reset}`);
|
|
12959
|
-
} else {
|
|
12960
|
-
parts.push(`[${timestamp}]`);
|
|
12961
|
-
}
|
|
12962
|
-
}
|
|
12963
|
-
if (currentOptions.prefix) {
|
|
12964
|
-
if (currentOptions.colors) {
|
|
12965
|
-
parts.push(`${COLORS.cyan}[${currentOptions.prefix}]${COLORS.reset}`);
|
|
12966
|
-
} else {
|
|
12967
|
-
parts.push(`[${currentOptions.prefix}]`);
|
|
12968
|
-
}
|
|
12969
|
-
}
|
|
12970
|
-
if (currentOptions.colors) {
|
|
12971
|
-
const color = LEVEL_COLORS[level];
|
|
12972
|
-
const icon = LEVEL_ICONS[level];
|
|
12973
|
-
parts.push(`${color}${icon}${COLORS.reset}`);
|
|
12974
|
-
} else {
|
|
12975
|
-
parts.push(`[${level.toUpperCase()}]`);
|
|
12976
|
-
}
|
|
12977
|
-
if (currentOptions.colors && level === "error") {
|
|
12978
|
-
parts.push(`${COLORS.red}${message}${COLORS.reset}`);
|
|
12979
|
-
} else if (currentOptions.colors && level === "warn") {
|
|
12980
|
-
parts.push(`${COLORS.yellow}${message}${COLORS.reset}`);
|
|
12981
|
-
} else {
|
|
12982
|
-
parts.push(message);
|
|
12983
|
-
}
|
|
12984
|
-
return parts.join(" ");
|
|
12985
|
-
}
|
|
12986
|
-
function shouldLog(level) {
|
|
12987
|
-
return LOG_LEVELS[level] >= LOG_LEVELS[currentOptions.level];
|
|
12988
|
-
}
|
|
12989
|
-
function debug(messageKey, params) {
|
|
12990
|
-
if (shouldLog("debug")) {
|
|
12991
|
-
const message = getMessage(messageKey, params);
|
|
12992
|
-
console.debug(formatMessage("debug", message));
|
|
12993
|
-
}
|
|
12994
|
-
}
|
|
12995
|
-
function info(messageKey, params) {
|
|
12996
|
-
if (shouldLog("info")) {
|
|
12997
|
-
const message = getMessage(messageKey, params);
|
|
12998
|
-
console.info(formatMessage("info", message));
|
|
12999
|
-
}
|
|
13000
|
-
}
|
|
13001
|
-
function warn(messageKey, params) {
|
|
13002
|
-
if (shouldLog("warn")) {
|
|
13003
|
-
const message = getMessage(messageKey, params);
|
|
13004
|
-
console.warn(formatMessage("warn", message));
|
|
13005
|
-
}
|
|
13006
|
-
}
|
|
13007
|
-
function error(messageKey, params) {
|
|
13008
|
-
if (shouldLog("error")) {
|
|
13009
|
-
const message = getMessage(messageKey, params);
|
|
13010
|
-
console.error(formatMessage("error", message));
|
|
13011
|
-
}
|
|
13012
|
-
}
|
|
13013
|
-
function success(messageKey, params) {
|
|
13014
|
-
if (shouldLog("info")) {
|
|
13015
|
-
const message = getMessage(messageKey, params);
|
|
13016
|
-
if (currentOptions.colors) {
|
|
13017
|
-
console.info(`${COLORS.green}✓${COLORS.reset} ${message}`);
|
|
13018
|
-
} else {
|
|
13019
|
-
console.info(`[SUCCESS] ${message}`);
|
|
13020
|
-
}
|
|
13021
|
-
}
|
|
13022
|
-
}
|
|
13023
|
-
|
|
13024
|
-
// src/core/config.ts
|
|
13025
|
-
import { join as join3 } from "node:path";
|
|
13026
|
-
var CONFIG_FILE = ".omcustomrc.json";
|
|
13027
|
-
var CURRENT_CONFIG_VERSION = 1;
|
|
13028
|
-
function getDefaultConfig() {
|
|
13029
|
-
return {
|
|
13030
|
-
configVersion: CURRENT_CONFIG_VERSION,
|
|
13031
|
-
version: "0.0.0",
|
|
13032
|
-
language: "en",
|
|
13033
|
-
provider: "auto",
|
|
13034
|
-
installedAt: "",
|
|
13035
|
-
lastUpdated: "",
|
|
13036
|
-
installedComponents: [],
|
|
13037
|
-
componentVersions: {},
|
|
13038
|
-
agents: {},
|
|
13039
|
-
preferences: getDefaultPreferences(),
|
|
13040
|
-
sourceRepo: "https://github.com/baekenough/oh-my-customcode",
|
|
13041
|
-
autoUpdate: {
|
|
13042
|
-
enabled: false,
|
|
13043
|
-
checkIntervalHours: 24,
|
|
13044
|
-
autoApplyMinor: false
|
|
13427
|
+
printCheck(check);
|
|
13045
13428
|
}
|
|
13046
|
-
}
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
}
|
|
13060
|
-
async function loadConfig(targetDir) {
|
|
13061
|
-
const configPath = getConfigPath(targetDir);
|
|
13062
|
-
if (await fileExists(configPath)) {
|
|
13063
|
-
try {
|
|
13064
|
-
const config = await readJsonFile(configPath);
|
|
13065
|
-
const merged = mergeConfig(getDefaultConfig(), config);
|
|
13066
|
-
if (merged.configVersion < CURRENT_CONFIG_VERSION) {
|
|
13067
|
-
const migrated = migrateConfig(merged);
|
|
13068
|
-
await saveConfig(targetDir, migrated);
|
|
13069
|
-
return migrated;
|
|
13429
|
+
}
|
|
13430
|
+
const passCount = checks.filter((c) => c.status === "pass" || c.fixed).length;
|
|
13431
|
+
const warnCount = checks.filter((c) => c.status === "warn").length;
|
|
13432
|
+
const failCount = checks.filter((c) => c.status === "fail" && !c.fixed).length;
|
|
13433
|
+
const fixedCount = checks.filter((c) => c.fixed).length;
|
|
13434
|
+
console.log("");
|
|
13435
|
+
if (failCount === 0) {
|
|
13436
|
+
console.log(i18n.t("cli.doctor.passed"));
|
|
13437
|
+
} else {
|
|
13438
|
+
console.log(i18n.t("cli.doctor.failed"));
|
|
13439
|
+
if (!options.fix) {
|
|
13440
|
+
const fixableCount = checks.filter((c) => c.status === "fail" && c.fixable).length;
|
|
13441
|
+
if (fixableCount > 0) {
|
|
13442
|
+
console.log(i18n.t("cli.doctor.runWithFix", { count: fixableCount }));
|
|
13070
13443
|
}
|
|
13071
|
-
return merged;
|
|
13072
|
-
} catch (err) {
|
|
13073
|
-
warn("config.load_failed", { error: String(err) });
|
|
13074
|
-
return getDefaultConfig();
|
|
13075
13444
|
}
|
|
13076
13445
|
}
|
|
13077
|
-
|
|
13078
|
-
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
config.lastUpdated = new Date().toISOString();
|
|
13084
|
-
await writeJsonFile(configPath, config);
|
|
13085
|
-
debug("config.saved", { path: configPath });
|
|
13086
|
-
}
|
|
13087
|
-
function mergeConfig(defaults, overrides) {
|
|
13446
|
+
console.log(i18n.t("cli.doctor.summary", {
|
|
13447
|
+
pass: passCount,
|
|
13448
|
+
warn: warnCount,
|
|
13449
|
+
fail: failCount,
|
|
13450
|
+
fixed: fixedCount
|
|
13451
|
+
}));
|
|
13088
13452
|
return {
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
...overrides.componentVersions
|
|
13096
|
-
},
|
|
13097
|
-
agents: {
|
|
13098
|
-
...defaults.agents,
|
|
13099
|
-
...overrides.agents
|
|
13100
|
-
}
|
|
13453
|
+
success: failCount === 0,
|
|
13454
|
+
checks,
|
|
13455
|
+
passCount,
|
|
13456
|
+
warnCount,
|
|
13457
|
+
failCount,
|
|
13458
|
+
fixedCount
|
|
13101
13459
|
};
|
|
13102
13460
|
}
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13106
|
-
migrated.configVersion = 1;
|
|
13107
|
-
migrated.preferences = getDefaultPreferences();
|
|
13108
|
-
migrated.autoUpdate = {
|
|
13109
|
-
enabled: false,
|
|
13110
|
-
checkIntervalHours: 24,
|
|
13111
|
-
autoApplyMinor: false
|
|
13112
|
-
};
|
|
13113
|
-
}
|
|
13114
|
-
migrated.configVersion = CURRENT_CONFIG_VERSION;
|
|
13115
|
-
return migrated;
|
|
13116
|
-
}
|
|
13461
|
+
|
|
13462
|
+
// src/cli/init.ts
|
|
13463
|
+
import { join as join5 } from "node:path";
|
|
13117
13464
|
|
|
13118
13465
|
// src/core/installer.ts
|
|
13466
|
+
import { copyFile as fsCopyFile, rename } from "node:fs/promises";
|
|
13467
|
+
import { basename, join as join4 } from "node:path";
|
|
13119
13468
|
var DEFAULT_LANGUAGE2 = "en";
|
|
13120
13469
|
function getTemplateDir() {
|
|
13121
13470
|
const packageRoot = getPackageRoot();
|
|
@@ -13397,7 +13746,7 @@ async function initCommand(options) {
|
|
|
13397
13746
|
}
|
|
13398
13747
|
|
|
13399
13748
|
// src/cli/list.ts
|
|
13400
|
-
import { basename as basename2, dirname as dirname2, join as join6, relative } from "node:path";
|
|
13749
|
+
import { basename as basename2, dirname as dirname2, join as join6, relative as relative2 } from "node:path";
|
|
13401
13750
|
var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
|
|
13402
13751
|
function parseKeyValue(line) {
|
|
13403
13752
|
const colonIndex = line.indexOf(":");
|
|
@@ -13461,12 +13810,12 @@ function extractAgentTypeFromFilename(filename) {
|
|
|
13461
13810
|
return prefixMap[prefix] || "unknown";
|
|
13462
13811
|
}
|
|
13463
13812
|
function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
|
|
13464
|
-
const relativePath =
|
|
13813
|
+
const relativePath = relative2(join6(baseDir, rootDir, "skills"), skillPath);
|
|
13465
13814
|
const parts = relativePath.split("/").filter(Boolean);
|
|
13466
13815
|
return parts[0] || "unknown";
|
|
13467
13816
|
}
|
|
13468
13817
|
function extractGuideCategoryFromPath(guidePath, baseDir) {
|
|
13469
|
-
const relativePath =
|
|
13818
|
+
const relativePath = relative2(join6(baseDir, "guides"), guidePath);
|
|
13470
13819
|
const parts = relativePath.split("/").filter(Boolean);
|
|
13471
13820
|
return parts[0] || "unknown";
|
|
13472
13821
|
}
|
|
@@ -13564,17 +13913,22 @@ async function getAgents(targetDir, rootDir = ".claude") {
|
|
|
13564
13913
|
if (!await fileExists(agentsDir))
|
|
13565
13914
|
return [];
|
|
13566
13915
|
try {
|
|
13916
|
+
const config = await loadConfig(targetDir);
|
|
13917
|
+
const customComponents = config.customComponents || [];
|
|
13918
|
+
const customAgentPaths = new Set(customComponents.filter((c) => c.type === "agent").map((c) => c.path));
|
|
13567
13919
|
const agentMdFiles = await listFiles(agentsDir, { recursive: false, pattern: "*.md" });
|
|
13568
13920
|
const agents = await Promise.all(agentMdFiles.map(async (agentMdPath) => {
|
|
13569
13921
|
const filename = basename2(agentMdPath);
|
|
13570
13922
|
const name = basename2(filename, ".md");
|
|
13571
13923
|
const description = await tryExtractMarkdownDescription(agentMdPath);
|
|
13924
|
+
const relativePath = relative2(targetDir, agentMdPath);
|
|
13572
13925
|
return {
|
|
13573
13926
|
name,
|
|
13574
13927
|
type: extractAgentTypeFromFilename(filename),
|
|
13575
|
-
path:
|
|
13928
|
+
path: relativePath,
|
|
13576
13929
|
description,
|
|
13577
|
-
version: undefined
|
|
13930
|
+
version: undefined,
|
|
13931
|
+
managed: !customAgentPaths.has(relativePath)
|
|
13578
13932
|
};
|
|
13579
13933
|
}));
|
|
13580
13934
|
return agents.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -13587,18 +13941,23 @@ async function getSkills(targetDir, rootDir = ".claude") {
|
|
|
13587
13941
|
if (!await fileExists(skillsDir))
|
|
13588
13942
|
return [];
|
|
13589
13943
|
try {
|
|
13944
|
+
const config = await loadConfig(targetDir);
|
|
13945
|
+
const customComponents = config.customComponents || [];
|
|
13946
|
+
const customSkillPaths = new Set(customComponents.filter((c) => c.type === "skill").map((c) => c.path));
|
|
13590
13947
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
13591
13948
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
13592
13949
|
const skillDir = dirname2(skillMdPath);
|
|
13593
13950
|
const indexYamlPath = join6(skillDir, "index.yaml");
|
|
13594
13951
|
const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
|
|
13952
|
+
const relativePath = relative2(targetDir, skillDir);
|
|
13595
13953
|
return {
|
|
13596
13954
|
name: basename2(skillDir),
|
|
13597
13955
|
type: "skill",
|
|
13598
13956
|
category: extractSkillCategoryFromPath(skillDir, targetDir, rootDir),
|
|
13599
|
-
path:
|
|
13957
|
+
path: relativePath,
|
|
13600
13958
|
description,
|
|
13601
|
-
version
|
|
13959
|
+
version,
|
|
13960
|
+
managed: !customSkillPaths.has(relativePath)
|
|
13602
13961
|
};
|
|
13603
13962
|
}));
|
|
13604
13963
|
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -13611,15 +13970,20 @@ async function getGuides(targetDir) {
|
|
|
13611
13970
|
if (!await fileExists(guidesDir))
|
|
13612
13971
|
return [];
|
|
13613
13972
|
try {
|
|
13973
|
+
const config = await loadConfig(targetDir);
|
|
13974
|
+
const customComponents = config.customComponents || [];
|
|
13975
|
+
const customGuidePaths = new Set(customComponents.filter((c) => c.type === "guide").map((c) => c.path));
|
|
13614
13976
|
const guideMdFiles = await listFiles(guidesDir, { recursive: true, pattern: "*.md" });
|
|
13615
13977
|
const guides = await Promise.all(guideMdFiles.map(async (guideMdPath) => {
|
|
13616
13978
|
const description = await tryExtractMarkdownDescription(guideMdPath, { maxLength: 100 });
|
|
13979
|
+
const relativePath = relative2(targetDir, guideMdPath);
|
|
13617
13980
|
return {
|
|
13618
13981
|
name: basename2(guideMdPath, ".md"),
|
|
13619
13982
|
type: "guide",
|
|
13620
13983
|
category: extractGuideCategoryFromPath(guideMdPath, targetDir),
|
|
13621
|
-
path:
|
|
13622
|
-
description
|
|
13984
|
+
path: relativePath,
|
|
13985
|
+
description,
|
|
13986
|
+
managed: !customGuidePaths.has(relativePath)
|
|
13623
13987
|
};
|
|
13624
13988
|
}));
|
|
13625
13989
|
return guides.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -13633,17 +13997,22 @@ async function getRules(targetDir, rootDir = ".claude") {
|
|
|
13633
13997
|
if (!await fileExists(rulesDir))
|
|
13634
13998
|
return [];
|
|
13635
13999
|
try {
|
|
14000
|
+
const config = await loadConfig(targetDir);
|
|
14001
|
+
const customComponents = config.customComponents || [];
|
|
14002
|
+
const customRulePaths = new Set(customComponents.filter((c) => c.type === "rule").map((c) => c.path));
|
|
13636
14003
|
const ruleMdFiles = await listFiles(rulesDir, { recursive: false, pattern: "*.md" });
|
|
13637
14004
|
const rules = await Promise.all(ruleMdFiles.map(async (ruleMdPath) => {
|
|
13638
14005
|
const filename = basename2(ruleMdPath);
|
|
13639
14006
|
const description = await tryExtractMarkdownDescription(ruleMdPath, {
|
|
13640
14007
|
cleanFormatting: true
|
|
13641
14008
|
});
|
|
14009
|
+
const relativePath = relative2(targetDir, ruleMdPath);
|
|
13642
14010
|
return {
|
|
13643
14011
|
name: basename2(ruleMdPath, ".md"),
|
|
13644
14012
|
type: extractRulePriorityFromFilename(filename),
|
|
13645
|
-
path:
|
|
13646
|
-
description
|
|
14013
|
+
path: relativePath,
|
|
14014
|
+
description,
|
|
14015
|
+
managed: !customRulePaths.has(relativePath)
|
|
13647
14016
|
};
|
|
13648
14017
|
}));
|
|
13649
14018
|
return rules.sort((a, b) => {
|
|
@@ -13669,7 +14038,8 @@ function formatAsTable(components, type) {
|
|
|
13669
14038
|
console.log(` ${nameHeader} ${typeHeader} Description`);
|
|
13670
14039
|
console.log(` ${"─".repeat(nameWidth)} ${"─".repeat(typeWidth)} ${"─".repeat(40)}`);
|
|
13671
14040
|
for (const component of components) {
|
|
13672
|
-
const
|
|
14041
|
+
const managedTag = component.managed === false ? " [custom]" : "";
|
|
14042
|
+
const name = `${component.name}${managedTag}`.padEnd(nameWidth);
|
|
13673
14043
|
const typeOrCategory = (component.category || component.type).padEnd(typeWidth);
|
|
13674
14044
|
const description = component.description ? component.description.substring(0, 40) : "";
|
|
13675
14045
|
console.log(` ${name} ${typeOrCategory} ${description}`);
|
|
@@ -13687,7 +14057,8 @@ function formatAsSimple(components, type) {
|
|
|
13687
14057
|
${type} (${components.length}):`);
|
|
13688
14058
|
for (const component of components) {
|
|
13689
14059
|
const typeInfo = component.category || component.type;
|
|
13690
|
-
|
|
14060
|
+
const managedTag = component.managed === false ? " [custom]" : "";
|
|
14061
|
+
console.log(` ${component.name}${managedTag} [${typeInfo}]`);
|
|
13691
14062
|
}
|
|
13692
14063
|
}
|
|
13693
14064
|
function formatAsJson(components) {
|
|
@@ -13705,7 +14076,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
13705
14076
|
return allFiles.map((hookPath) => ({
|
|
13706
14077
|
name: basename2(hookPath),
|
|
13707
14078
|
type: "hook",
|
|
13708
|
-
path:
|
|
14079
|
+
path: relative2(targetDir, hookPath)
|
|
13709
14080
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
13710
14081
|
} catch {
|
|
13711
14082
|
return [];
|
|
@@ -13725,7 +14096,7 @@ async function getContexts(targetDir, rootDir = ".claude") {
|
|
|
13725
14096
|
return {
|
|
13726
14097
|
name: basename2(ctxPath, ext),
|
|
13727
14098
|
type: "context",
|
|
13728
|
-
path:
|
|
14099
|
+
path: relative2(targetDir, ctxPath),
|
|
13729
14100
|
description
|
|
13730
14101
|
};
|
|
13731
14102
|
}));
|
|
@@ -13795,73 +14166,439 @@ async function listCommand(type = "all", options = {}) {
|
|
|
13795
14166
|
}
|
|
13796
14167
|
}
|
|
13797
14168
|
|
|
13798
|
-
// src/
|
|
13799
|
-
|
|
13800
|
-
|
|
14169
|
+
// src/core/updater.ts
|
|
14170
|
+
import { join as join7 } from "node:path";
|
|
14171
|
+
|
|
14172
|
+
// src/core/entry-merger.ts
|
|
14173
|
+
var MANAGED_START = "<!-- omcustom:start -->";
|
|
14174
|
+
var MANAGED_END = "<!-- omcustom:end -->";
|
|
14175
|
+
function parseEntryDoc(content) {
|
|
14176
|
+
const sections = [];
|
|
14177
|
+
const lines = content.split(`
|
|
14178
|
+
`);
|
|
14179
|
+
let currentSection = null;
|
|
14180
|
+
let currentLines = [];
|
|
14181
|
+
for (const line of lines) {
|
|
14182
|
+
if (line.trim() === MANAGED_START) {
|
|
14183
|
+
if (currentLines.length > 0) {
|
|
14184
|
+
sections.push({
|
|
14185
|
+
type: "custom",
|
|
14186
|
+
content: currentLines.join(`
|
|
14187
|
+
`)
|
|
14188
|
+
});
|
|
14189
|
+
currentLines = [];
|
|
14190
|
+
}
|
|
14191
|
+
currentSection = { type: "managed", content: "" };
|
|
14192
|
+
continue;
|
|
14193
|
+
}
|
|
14194
|
+
if (line.trim() === MANAGED_END) {
|
|
14195
|
+
if (currentSection && currentSection.type === "managed") {
|
|
14196
|
+
currentSection.content = currentLines.join(`
|
|
14197
|
+
`);
|
|
14198
|
+
sections.push(currentSection);
|
|
14199
|
+
currentSection = null;
|
|
14200
|
+
currentLines = [];
|
|
14201
|
+
}
|
|
14202
|
+
continue;
|
|
14203
|
+
}
|
|
14204
|
+
currentLines.push(line);
|
|
14205
|
+
}
|
|
14206
|
+
if (currentLines.length > 0) {
|
|
14207
|
+
sections.push({
|
|
14208
|
+
type: "custom",
|
|
14209
|
+
content: currentLines.join(`
|
|
14210
|
+
`)
|
|
14211
|
+
});
|
|
14212
|
+
}
|
|
14213
|
+
return { sections };
|
|
13801
14214
|
}
|
|
13802
|
-
|
|
14215
|
+
function mergeEntryDoc(existingContent, templateContent) {
|
|
14216
|
+
const warnings = [];
|
|
14217
|
+
const { sections } = parseEntryDoc(existingContent);
|
|
14218
|
+
const hasManagedSections = sections.some((s) => s.type === "managed");
|
|
14219
|
+
if (!hasManagedSections) {
|
|
14220
|
+
const wrapped = wrapInManagedMarkers(templateContent);
|
|
14221
|
+
return {
|
|
14222
|
+
content: wrapped,
|
|
14223
|
+
managedSections: 1,
|
|
14224
|
+
customSections: 0,
|
|
14225
|
+
warnings: ["No managed sections found in existing content, wrapping template entirely"]
|
|
14226
|
+
};
|
|
14227
|
+
}
|
|
14228
|
+
const mergedSections = [];
|
|
14229
|
+
let managedCount = 0;
|
|
14230
|
+
let customCount = 0;
|
|
14231
|
+
let templateInserted = false;
|
|
14232
|
+
for (const section of sections) {
|
|
14233
|
+
if (section.type === "managed") {
|
|
14234
|
+
if (!templateInserted) {
|
|
14235
|
+
mergedSections.push(MANAGED_START);
|
|
14236
|
+
mergedSections.push(templateContent);
|
|
14237
|
+
mergedSections.push(MANAGED_END);
|
|
14238
|
+
templateInserted = true;
|
|
14239
|
+
managedCount++;
|
|
14240
|
+
} else {
|
|
14241
|
+
warnings.push("Multiple managed sections found, keeping only the first one");
|
|
14242
|
+
}
|
|
14243
|
+
} else {
|
|
14244
|
+
mergedSections.push(section.content);
|
|
14245
|
+
customCount++;
|
|
14246
|
+
}
|
|
14247
|
+
}
|
|
13803
14248
|
return {
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
|
|
14249
|
+
content: mergedSections.join(`
|
|
14250
|
+
`),
|
|
14251
|
+
managedSections: managedCount,
|
|
14252
|
+
customSections: customCount,
|
|
14253
|
+
warnings
|
|
13807
14254
|
};
|
|
13808
14255
|
}
|
|
13809
|
-
|
|
13810
|
-
|
|
13811
|
-
|
|
13812
|
-
|
|
14256
|
+
function wrapInManagedMarkers(content) {
|
|
14257
|
+
return `${MANAGED_START}
|
|
14258
|
+
${content}
|
|
14259
|
+
${MANAGED_END}`;
|
|
14260
|
+
}
|
|
14261
|
+
|
|
14262
|
+
// src/core/updater.ts
|
|
14263
|
+
var CUSTOMIZATION_MANIFEST_FILE = ".omcustom-customizations.json";
|
|
14264
|
+
function createUpdateResult() {
|
|
14265
|
+
return {
|
|
14266
|
+
success: false,
|
|
14267
|
+
updatedComponents: [],
|
|
14268
|
+
skippedComponents: [],
|
|
14269
|
+
preservedFiles: [],
|
|
14270
|
+
backedUpPaths: [],
|
|
14271
|
+
previousVersion: "",
|
|
14272
|
+
newVersion: "",
|
|
14273
|
+
warnings: []
|
|
14274
|
+
};
|
|
14275
|
+
}
|
|
14276
|
+
async function handleBackupIfRequested(targetDir, provider, backup, result) {
|
|
14277
|
+
if (!backup)
|
|
14278
|
+
return;
|
|
14279
|
+
const backupPath = await backupInstallation(targetDir, provider);
|
|
14280
|
+
result.backedUpPaths.push(backupPath);
|
|
14281
|
+
info("update.backup_created", { path: backupPath });
|
|
14282
|
+
}
|
|
14283
|
+
async function processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result) {
|
|
14284
|
+
const componentUpdate = updateCheck.updatableComponents.find((c) => c.name === component);
|
|
14285
|
+
if (!componentUpdate && !options.force) {
|
|
14286
|
+
result.skippedComponents.push(component);
|
|
14287
|
+
return;
|
|
14288
|
+
}
|
|
14289
|
+
if (options.dryRun) {
|
|
14290
|
+
debug("update.dry_run", { component });
|
|
14291
|
+
result.updatedComponents.push(component);
|
|
14292
|
+
return;
|
|
14293
|
+
}
|
|
14294
|
+
try {
|
|
14295
|
+
const preserved = await updateComponent(targetDir, provider, component, customizations, options);
|
|
14296
|
+
result.updatedComponents.push(component);
|
|
14297
|
+
result.preservedFiles.push(...preserved);
|
|
14298
|
+
} catch (err) {
|
|
14299
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14300
|
+
result.warnings.push(`Failed to update ${component}: ${message}`);
|
|
14301
|
+
result.skippedComponents.push(component);
|
|
14302
|
+
}
|
|
14303
|
+
}
|
|
14304
|
+
async function updateAllComponents(targetDir, provider, components, updateCheck, customizations, options, result) {
|
|
14305
|
+
for (const component of components) {
|
|
14306
|
+
await processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result);
|
|
14307
|
+
}
|
|
14308
|
+
}
|
|
14309
|
+
function getEntryTemplateName2(provider, language) {
|
|
14310
|
+
const layout = getProviderLayout(provider);
|
|
14311
|
+
const baseName = layout.entryFile.replace(".md", "");
|
|
14312
|
+
return language === "ko" ? `${baseName}.md.ko` : `${baseName}.md.en`;
|
|
14313
|
+
}
|
|
14314
|
+
async function backupFile(filePath) {
|
|
14315
|
+
const fs2 = await import("node:fs/promises");
|
|
14316
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
14317
|
+
const backupPath = `${filePath}.backup-${timestamp}`;
|
|
14318
|
+
if (await fileExists(filePath)) {
|
|
14319
|
+
await fs2.copyFile(filePath, backupPath);
|
|
14320
|
+
debug("update.file_backed_up", { path: filePath, backup: backupPath });
|
|
14321
|
+
}
|
|
14322
|
+
}
|
|
14323
|
+
function resolveCustomizations(customizations, configPreserveFiles) {
|
|
14324
|
+
if (!customizations && configPreserveFiles.length === 0) {
|
|
14325
|
+
return null;
|
|
14326
|
+
}
|
|
14327
|
+
if (customizations && configPreserveFiles.length > 0) {
|
|
14328
|
+
customizations.preserveFiles = [
|
|
14329
|
+
...new Set([...customizations.preserveFiles, ...configPreserveFiles])
|
|
14330
|
+
];
|
|
14331
|
+
return customizations;
|
|
14332
|
+
}
|
|
14333
|
+
if (configPreserveFiles.length > 0) {
|
|
14334
|
+
return {
|
|
14335
|
+
modifiedFiles: [],
|
|
14336
|
+
preserveFiles: configPreserveFiles,
|
|
14337
|
+
customComponents: [],
|
|
14338
|
+
lastUpdated: new Date().toISOString()
|
|
14339
|
+
};
|
|
14340
|
+
}
|
|
14341
|
+
return customizations;
|
|
14342
|
+
}
|
|
14343
|
+
async function updateEntryDoc(targetDir, provider, config, options) {
|
|
14344
|
+
const layout = getProviderLayout(provider);
|
|
14345
|
+
const entryPath = join7(targetDir, layout.entryFile);
|
|
14346
|
+
const templateName = getEntryTemplateName2(provider, config.language);
|
|
14347
|
+
const templatePath = resolveTemplatePath(templateName);
|
|
14348
|
+
if (!await fileExists(templatePath)) {
|
|
14349
|
+
warn("update.entry_template_not_found", { template: templateName });
|
|
14350
|
+
return;
|
|
14351
|
+
}
|
|
14352
|
+
const templateContent = await readTextFile(templatePath);
|
|
14353
|
+
if (await fileExists(entryPath)) {
|
|
14354
|
+
if (options.force) {
|
|
14355
|
+
await backupFile(entryPath);
|
|
14356
|
+
await writeTextFile(entryPath, templateContent);
|
|
14357
|
+
info("update.entry_doc_force_updated", { path: layout.entryFile });
|
|
14358
|
+
} else {
|
|
14359
|
+
const existingContent = await readTextFile(entryPath);
|
|
14360
|
+
const mergeResult = mergeEntryDoc(existingContent, templateContent);
|
|
14361
|
+
await writeTextFile(entryPath, mergeResult.content);
|
|
14362
|
+
debug("update.entry_doc_merged", {
|
|
14363
|
+
path: layout.entryFile,
|
|
14364
|
+
managed: String(mergeResult.managedSections),
|
|
14365
|
+
custom: String(mergeResult.customSections)
|
|
14366
|
+
});
|
|
14367
|
+
if (mergeResult.warnings.length > 0) {
|
|
14368
|
+
for (const warning of mergeResult.warnings) {
|
|
14369
|
+
warn("update.entry_merge_warning", { warning });
|
|
14370
|
+
}
|
|
14371
|
+
}
|
|
14372
|
+
}
|
|
14373
|
+
} else {
|
|
14374
|
+
await writeTextFile(entryPath, wrapInManagedMarkers(templateContent));
|
|
14375
|
+
info("update.entry_doc_created", { path: layout.entryFile });
|
|
14376
|
+
}
|
|
14377
|
+
}
|
|
14378
|
+
async function update(options) {
|
|
14379
|
+
const result = createUpdateResult();
|
|
14380
|
+
try {
|
|
14381
|
+
info("update.start", { targetDir: options.targetDir });
|
|
14382
|
+
const config = await loadConfig(options.targetDir);
|
|
14383
|
+
const provider = options.provider ?? (config.provider === "codex" ? "codex" : "claude");
|
|
14384
|
+
result.previousVersion = config.version;
|
|
14385
|
+
const updateCheck = await checkForUpdates(options.targetDir, provider);
|
|
14386
|
+
result.newVersion = updateCheck.latestVersion;
|
|
14387
|
+
if (!updateCheck.hasUpdates && !options.force) {
|
|
14388
|
+
info("update.no_updates");
|
|
14389
|
+
result.success = true;
|
|
14390
|
+
result.skippedComponents = options.components || getAllUpdateComponents();
|
|
14391
|
+
return result;
|
|
14392
|
+
}
|
|
14393
|
+
await handleBackupIfRequested(options.targetDir, provider, !!options.backup, result);
|
|
14394
|
+
const manifestCustomizations = options.preserveCustomizations !== false ? await loadCustomizationManifest(options.targetDir) : null;
|
|
14395
|
+
const configPreserveFiles = config.preserveFiles || [];
|
|
14396
|
+
const customizations = resolveCustomizations(manifestCustomizations, configPreserveFiles);
|
|
14397
|
+
const components = options.components || getAllUpdateComponents();
|
|
14398
|
+
await updateAllComponents(options.targetDir, provider, components, updateCheck, customizations, options, result);
|
|
14399
|
+
if (!options.components || options.components.length === 0) {
|
|
14400
|
+
await updateEntryDoc(options.targetDir, provider, config, options);
|
|
14401
|
+
}
|
|
14402
|
+
config.version = result.newVersion;
|
|
14403
|
+
config.lastUpdated = new Date().toISOString();
|
|
14404
|
+
await saveConfig(options.targetDir, config);
|
|
14405
|
+
result.success = true;
|
|
14406
|
+
success("update.success", { from: result.previousVersion, to: result.newVersion });
|
|
14407
|
+
} catch (err) {
|
|
14408
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14409
|
+
result.error = message;
|
|
14410
|
+
error("update.failed", { error: message });
|
|
14411
|
+
}
|
|
14412
|
+
return result;
|
|
14413
|
+
}
|
|
14414
|
+
async function checkForUpdates(targetDir, provider = "claude") {
|
|
14415
|
+
const config = await loadConfig(targetDir);
|
|
14416
|
+
const currentVersion = config.version;
|
|
14417
|
+
const latestVersion = await getLatestVersion(provider);
|
|
14418
|
+
const updatableComponents = [];
|
|
14419
|
+
for (const component of getAllUpdateComponents()) {
|
|
14420
|
+
const hasUpdate = await componentHasUpdate(targetDir, provider, component, config);
|
|
14421
|
+
if (hasUpdate) {
|
|
14422
|
+
updatableComponents.push({
|
|
14423
|
+
name: component,
|
|
14424
|
+
currentVersion: config.componentVersions?.[component] || "0.0.0",
|
|
14425
|
+
latestVersion
|
|
14426
|
+
});
|
|
14427
|
+
}
|
|
14428
|
+
}
|
|
14429
|
+
return {
|
|
14430
|
+
hasUpdates: updatableComponents.length > 0 || currentVersion !== latestVersion,
|
|
14431
|
+
currentVersion,
|
|
14432
|
+
latestVersion,
|
|
14433
|
+
updatableComponents,
|
|
14434
|
+
checkedAt: new Date().toISOString()
|
|
14435
|
+
};
|
|
14436
|
+
}
|
|
14437
|
+
function getAllUpdateComponents() {
|
|
14438
|
+
return ["rules", "agents", "skills", "guides", "hooks", "contexts"];
|
|
14439
|
+
}
|
|
14440
|
+
async function getLatestVersion(provider) {
|
|
14441
|
+
const layout = getProviderLayout(provider);
|
|
14442
|
+
const manifestPath = resolveTemplatePath(layout.manifestFile);
|
|
14443
|
+
if (await fileExists(manifestPath)) {
|
|
14444
|
+
const manifest = await readJsonFile(manifestPath);
|
|
14445
|
+
return manifest.version;
|
|
14446
|
+
}
|
|
14447
|
+
return "0.0.0";
|
|
14448
|
+
}
|
|
14449
|
+
async function componentHasUpdate(_targetDir, provider, component, config) {
|
|
14450
|
+
const installedVersion = config.componentVersions?.[component];
|
|
14451
|
+
if (!installedVersion) {
|
|
13813
14452
|
return true;
|
|
13814
14453
|
}
|
|
13815
|
-
|
|
14454
|
+
const latestVersion = await getLatestVersion(provider);
|
|
14455
|
+
return installedVersion !== latestVersion;
|
|
14456
|
+
}
|
|
14457
|
+
async function updateComponent(targetDir, provider, component, customizations, options) {
|
|
14458
|
+
const preservedFiles = [];
|
|
14459
|
+
const componentPath = getComponentPath2(provider, component);
|
|
14460
|
+
const srcPath = resolveTemplatePath(componentPath);
|
|
14461
|
+
const destPath = join7(targetDir, componentPath);
|
|
14462
|
+
const config = await loadConfig(targetDir);
|
|
14463
|
+
const customComponents = config.customComponents || [];
|
|
14464
|
+
const skipPaths = [];
|
|
14465
|
+
if (customizations && options.preserveCustomizations !== false) {
|
|
14466
|
+
const toPreserve = customizations.preserveFiles.filter((f) => f.startsWith(componentPath));
|
|
14467
|
+
preservedFiles.push(...toPreserve);
|
|
14468
|
+
skipPaths.push(...toPreserve);
|
|
14469
|
+
}
|
|
14470
|
+
for (const cc of customComponents) {
|
|
14471
|
+
if (cc.path.startsWith(componentPath)) {
|
|
14472
|
+
skipPaths.push(cc.path);
|
|
14473
|
+
}
|
|
14474
|
+
}
|
|
14475
|
+
const path2 = await import("node:path");
|
|
14476
|
+
const normalizedSkipPaths = skipPaths.map((p) => path2.relative(destPath, join7(targetDir, p)));
|
|
14477
|
+
await copyDirectory(srcPath, destPath, {
|
|
14478
|
+
overwrite: true,
|
|
14479
|
+
skipPaths: normalizedSkipPaths.length > 0 ? normalizedSkipPaths : undefined
|
|
14480
|
+
});
|
|
14481
|
+
debug("update.component_updated", {
|
|
14482
|
+
component,
|
|
14483
|
+
skippedPaths: String(normalizedSkipPaths.length)
|
|
14484
|
+
});
|
|
14485
|
+
return preservedFiles;
|
|
14486
|
+
}
|
|
14487
|
+
function getComponentPath2(provider, component) {
|
|
14488
|
+
const layout = getProviderLayout(provider);
|
|
14489
|
+
if (component === "guides") {
|
|
14490
|
+
return "guides";
|
|
14491
|
+
}
|
|
14492
|
+
return `${layout.rootDir}/${component}`;
|
|
14493
|
+
}
|
|
14494
|
+
async function backupInstallation(targetDir, provider) {
|
|
14495
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
14496
|
+
const backupDir = join7(targetDir, `.omcustom-backup-${timestamp}`);
|
|
14497
|
+
const fs2 = await import("node:fs/promises");
|
|
14498
|
+
await ensureDirectory(backupDir);
|
|
14499
|
+
const layout = getProviderLayout(provider);
|
|
14500
|
+
const dirsToBackup = [layout.rootDir, "guides"];
|
|
14501
|
+
for (const dir2 of dirsToBackup) {
|
|
14502
|
+
const srcPath = join7(targetDir, dir2);
|
|
14503
|
+
if (await fileExists(srcPath)) {
|
|
14504
|
+
const destPath = join7(backupDir, dir2);
|
|
14505
|
+
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
14506
|
+
}
|
|
14507
|
+
}
|
|
14508
|
+
const entryPath = join7(targetDir, layout.entryFile);
|
|
14509
|
+
if (await fileExists(entryPath)) {
|
|
14510
|
+
await fs2.copyFile(entryPath, join7(backupDir, layout.entryFile));
|
|
14511
|
+
}
|
|
14512
|
+
return backupDir;
|
|
13816
14513
|
}
|
|
13817
|
-
async function
|
|
13818
|
-
const
|
|
13819
|
-
|
|
14514
|
+
async function loadCustomizationManifest(targetDir) {
|
|
14515
|
+
const manifestPath = join7(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
14516
|
+
if (await fileExists(manifestPath)) {
|
|
14517
|
+
return readJsonFile(manifestPath);
|
|
14518
|
+
}
|
|
14519
|
+
return null;
|
|
13820
14520
|
}
|
|
14521
|
+
|
|
14522
|
+
// src/cli/update.ts
|
|
13821
14523
|
async function updateCommand(options = {}) {
|
|
13822
|
-
console.log(i18n.t("cli.update.checking"));
|
|
13823
14524
|
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
|
-
};
|
|
14525
|
+
const targetDir = process.cwd();
|
|
14526
|
+
const detection = await detectProvider({
|
|
14527
|
+
targetDir,
|
|
14528
|
+
override: options.provider
|
|
14529
|
+
});
|
|
14530
|
+
const provider = detection.provider;
|
|
14531
|
+
const components = buildComponentsList(options);
|
|
14532
|
+
if (options.dryRun) {
|
|
14533
|
+
console.log(i18n.t("cli.update.dryRunHeader"));
|
|
13843
14534
|
}
|
|
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
|
|
14535
|
+
const updateOptions = {
|
|
14536
|
+
targetDir,
|
|
14537
|
+
provider,
|
|
14538
|
+
components,
|
|
14539
|
+
force: options.force,
|
|
14540
|
+
preserveCustomizations: true,
|
|
14541
|
+
dryRun: options.dryRun,
|
|
14542
|
+
backup: options.backup
|
|
13856
14543
|
};
|
|
14544
|
+
const result = await update(updateOptions);
|
|
14545
|
+
printUpdateResults(result);
|
|
14546
|
+
if (!result.success) {
|
|
14547
|
+
process.exit(1);
|
|
14548
|
+
}
|
|
13857
14549
|
} catch (error2) {
|
|
13858
14550
|
const errorMessage = error2 instanceof Error ? error2.message : String(error2);
|
|
13859
|
-
console.error(i18n.t("cli.update.
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
14551
|
+
console.error(i18n.t("cli.update.summaryFailed", { error: errorMessage }));
|
|
14552
|
+
process.exit(1);
|
|
14553
|
+
}
|
|
14554
|
+
}
|
|
14555
|
+
function buildComponentsList(options) {
|
|
14556
|
+
const components = [];
|
|
14557
|
+
if (options.agents) {
|
|
14558
|
+
components.push("agents");
|
|
14559
|
+
}
|
|
14560
|
+
if (options.skills) {
|
|
14561
|
+
components.push("skills");
|
|
14562
|
+
}
|
|
14563
|
+
if (options.rules) {
|
|
14564
|
+
components.push("rules");
|
|
14565
|
+
}
|
|
14566
|
+
if (options.guides) {
|
|
14567
|
+
components.push("guides");
|
|
14568
|
+
}
|
|
14569
|
+
if (options.hooks) {
|
|
14570
|
+
components.push("hooks");
|
|
14571
|
+
}
|
|
14572
|
+
if (options.contexts) {
|
|
14573
|
+
components.push("contexts");
|
|
14574
|
+
}
|
|
14575
|
+
return components.length > 0 ? components : undefined;
|
|
14576
|
+
}
|
|
14577
|
+
function printUpdateResults(result) {
|
|
14578
|
+
for (const component of result.updatedComponents) {
|
|
14579
|
+
console.log(i18n.t("cli.update.componentUpdated", { component }));
|
|
14580
|
+
}
|
|
14581
|
+
for (const component of result.skippedComponents) {
|
|
14582
|
+
console.log(i18n.t("cli.update.componentSkipped", { component }));
|
|
14583
|
+
}
|
|
14584
|
+
if (result.preservedFiles.length > 0) {
|
|
14585
|
+
console.log(i18n.t("cli.update.preservedFiles", { count: result.preservedFiles.length }));
|
|
14586
|
+
}
|
|
14587
|
+
if (result.backedUpPaths.length > 0) {
|
|
14588
|
+
for (const path2 of result.backedUpPaths) {
|
|
14589
|
+
console.log(i18n.t("cli.update.backupCreated", { path: path2 }));
|
|
14590
|
+
}
|
|
14591
|
+
}
|
|
14592
|
+
for (const warning of result.warnings) {
|
|
14593
|
+
console.warn(warning);
|
|
14594
|
+
}
|
|
14595
|
+
if (result.success) {
|
|
14596
|
+
console.log(i18n.t("cli.update.summary", {
|
|
14597
|
+
updated: result.updatedComponents.length,
|
|
14598
|
+
skipped: result.skippedComponents.length
|
|
14599
|
+
}));
|
|
14600
|
+
} else if (result.error) {
|
|
14601
|
+
console.error(i18n.t("cli.update.summaryFailed", { error: result.error }));
|
|
13865
14602
|
}
|
|
13866
14603
|
}
|
|
13867
14604
|
|
|
@@ -13870,12 +14607,12 @@ var require2 = createRequire2(import.meta.url);
|
|
|
13870
14607
|
var packageJson = require2("../../package.json");
|
|
13871
14608
|
function createProgram() {
|
|
13872
14609
|
const program2 = new Command;
|
|
13873
|
-
program2.name("omcustom").description(i18n.t("cli.description")).version(packageJson.version, "-v, --version", i18n.t("cli.versionOption"));
|
|
14610
|
+
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
14611
|
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
14612
|
await initCommand(options);
|
|
13876
14613
|
});
|
|
13877
|
-
program2.command("update").description(i18n.t("cli.update.description")).action(async () => {
|
|
13878
|
-
await updateCommand();
|
|
14614
|
+
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) => {
|
|
14615
|
+
await updateCommand(options);
|
|
13879
14616
|
});
|
|
13880
14617
|
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
14618
|
await listCommand(type, {
|
|
@@ -13887,6 +14624,16 @@ function createProgram() {
|
|
|
13887
14624
|
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
14625
|
await doctorCommand(options);
|
|
13889
14626
|
});
|
|
14627
|
+
program2.hook("preAction", async (thisCommand) => {
|
|
14628
|
+
const opts = thisCommand.optsWithGlobals();
|
|
14629
|
+
const skipCheck = opts.skipVersionCheck || false;
|
|
14630
|
+
const result = await runPreflightCheck({ skip: skipCheck });
|
|
14631
|
+
if (result.hasUpdates) {
|
|
14632
|
+
const warnings = formatPreflightWarnings(result);
|
|
14633
|
+
console.warn(warnings);
|
|
14634
|
+
console.warn("");
|
|
14635
|
+
}
|
|
14636
|
+
});
|
|
13890
14637
|
return program2;
|
|
13891
14638
|
}
|
|
13892
14639
|
async function main() {
|