oh-my-customcode 0.9.4 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -6
- package/dist/cli/index.js +93 -49
- package/dist/index.js +69 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/oh-my-customcode)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://github.com/baekenough/oh-my-customcode/actions/workflows/ci.yml)
|
|
8
|
+
[](https://github.com/baekenough/oh-my-customcode/actions/workflows/security-audit.yml)
|
|
8
9
|
|
|
9
10
|
**[한국어 문서 (Korean)](./README_ko.md)**
|
|
10
11
|
|
|
@@ -16,7 +17,7 @@ Like oh-my-zsh transformed shell customization, oh-my-customcode makes personali
|
|
|
16
17
|
|
|
17
18
|
| Feature | Description |
|
|
18
19
|
|---------|-------------|
|
|
19
|
-
| **Batteries Included** | 42 agents,
|
|
20
|
+
| **Batteries Included** | 42 agents, 51 skills, 22 guides, 18 rules, 1 hook, 4 contexts - ready to use out of the box |
|
|
20
21
|
| **Sub-Agent Model** | Supports hierarchical agent orchestration with specialized roles |
|
|
21
22
|
| **Dead Simple Customization** | Create a folder + markdown file = new agent or skill |
|
|
22
23
|
| **Mix and Match** | Use built-in components, create your own, or combine both |
|
|
@@ -121,7 +122,7 @@ Claude Code selects the appropriate model and parallelizes independent tasks (up
|
|
|
121
122
|
| **QA** | 3 | qa-planner, qa-writer, qa-engineer |
|
|
122
123
|
| **Total** | **42** | |
|
|
123
124
|
|
|
124
|
-
### Skills (
|
|
125
|
+
### Skills (51)
|
|
125
126
|
|
|
126
127
|
Includes slash commands and capabilities:
|
|
127
128
|
|
|
@@ -157,9 +158,9 @@ Comprehensive reference documentation covering:
|
|
|
157
158
|
|
|
158
159
|
Event-driven automation for Claude Code lifecycle events (PreToolUse, PostToolUse, etc.).
|
|
159
160
|
|
|
160
|
-
### Contexts (
|
|
161
|
+
### Contexts (4)
|
|
161
162
|
|
|
162
|
-
Shared context
|
|
163
|
+
Shared context files for cross-agent knowledge and mode configurations.
|
|
163
164
|
|
|
164
165
|
---
|
|
165
166
|
|
|
@@ -194,13 +195,13 @@ your-project/
|
|
|
194
195
|
└── .claude/ # (or .codex/)
|
|
195
196
|
├── rules/ # Behavior rules (18 total)
|
|
196
197
|
├── hooks/ # Event hooks (1 total)
|
|
197
|
-
├── contexts/ # Context files (
|
|
198
|
+
├── contexts/ # Context files (4 total)
|
|
198
199
|
├── agents/ # Agent definitions (42 flat .md files)
|
|
199
200
|
│ ├── lang-golang-expert.md
|
|
200
201
|
│ ├── be-fastapi-expert.md
|
|
201
202
|
│ ├── mgr-creator.md
|
|
202
203
|
│ └── ...
|
|
203
|
-
├── skills/ # Skill modules (
|
|
204
|
+
├── skills/ # Skill modules (51 directories, each with SKILL.md)
|
|
204
205
|
│ ├── go-best-practices/
|
|
205
206
|
│ ├── react-best-practices/
|
|
206
207
|
│ ├── secretary-routing/
|
|
@@ -219,6 +220,17 @@ bun test # Run tests
|
|
|
219
220
|
bun run build # Build for production
|
|
220
221
|
```
|
|
221
222
|
|
|
223
|
+
### Quality Gates
|
|
224
|
+
|
|
225
|
+
| Gate | Tool | Threshold |
|
|
226
|
+
|------|------|-----------|
|
|
227
|
+
| Lint | Biome | Zero errors (complexity enforced) |
|
|
228
|
+
| Test Coverage | Bun test | 95% (pre-commit), 97% (CI) |
|
|
229
|
+
| Security Audit | bun pm audit | No high/critical vulnerabilities |
|
|
230
|
+
| Dependabot | GitHub | Weekly scans, auto-PR for updates |
|
|
231
|
+
|
|
232
|
+
Pre-commit hooks automatically enforce lint, test, and coverage gates before each commit.
|
|
233
|
+
|
|
222
234
|
### Requirements
|
|
223
235
|
|
|
224
236
|
- Node.js >= 18.0.0
|
package/dist/cli/index.js
CHANGED
|
@@ -11725,6 +11725,7 @@ var en_default = {
|
|
|
11725
11725
|
skipped: "Update skipped",
|
|
11726
11726
|
dryRunOption: "Show what would be updated without making changes",
|
|
11727
11727
|
forceOption: "Force update even if already at latest version",
|
|
11728
|
+
forceOverwriteAllOption: "Bypass all file preservation (manifest and config)",
|
|
11728
11729
|
backupOption: "Create backup before updating",
|
|
11729
11730
|
agentsOption: "Update only agents",
|
|
11730
11731
|
skillsOption: "Update only skills",
|
|
@@ -12008,6 +12009,7 @@ var ko_default = {
|
|
|
12008
12009
|
skipped: "업데이트 건너뜀",
|
|
12009
12010
|
dryRunOption: "변경 없이 업데이트할 내용 표시",
|
|
12010
12011
|
forceOption: "이미 최신 버전이어도 강제 업데이트",
|
|
12012
|
+
forceOverwriteAllOption: "모든 파일 보존 무시 (manifest 및 config)",
|
|
12011
12013
|
backupOption: "업데이트 전 백업 생성",
|
|
12012
12014
|
agentsOption: "에이전트만 업데이트",
|
|
12013
12015
|
skillsOption: "스킬만 업데이트",
|
|
@@ -13908,13 +13910,13 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
|
|
|
13908
13910
|
return;
|
|
13909
13911
|
}
|
|
13910
13912
|
}
|
|
13911
|
-
async function getAgents(targetDir, rootDir = ".claude") {
|
|
13913
|
+
async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
13912
13914
|
const agentsDir = join6(targetDir, rootDir, "agents");
|
|
13913
13915
|
if (!await fileExists(agentsDir))
|
|
13914
13916
|
return [];
|
|
13915
13917
|
try {
|
|
13916
|
-
const
|
|
13917
|
-
const customComponents =
|
|
13918
|
+
const resolvedConfig = config ?? await loadConfig(targetDir);
|
|
13919
|
+
const customComponents = resolvedConfig.customComponents || [];
|
|
13918
13920
|
const customAgentPaths = new Set(customComponents.filter((c) => c.type === "agent").map((c) => c.path));
|
|
13919
13921
|
const agentMdFiles = await listFiles(agentsDir, { recursive: false, pattern: "*.md" });
|
|
13920
13922
|
const agents = await Promise.all(agentMdFiles.map(async (agentMdPath) => {
|
|
@@ -13936,13 +13938,13 @@ async function getAgents(targetDir, rootDir = ".claude") {
|
|
|
13936
13938
|
return [];
|
|
13937
13939
|
}
|
|
13938
13940
|
}
|
|
13939
|
-
async function getSkills(targetDir, rootDir = ".claude") {
|
|
13941
|
+
async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
13940
13942
|
const skillsDir = join6(targetDir, rootDir, "skills");
|
|
13941
13943
|
if (!await fileExists(skillsDir))
|
|
13942
13944
|
return [];
|
|
13943
13945
|
try {
|
|
13944
|
-
const
|
|
13945
|
-
const customComponents =
|
|
13946
|
+
const resolvedConfig = config ?? await loadConfig(targetDir);
|
|
13947
|
+
const customComponents = resolvedConfig.customComponents || [];
|
|
13946
13948
|
const customSkillPaths = new Set(customComponents.filter((c) => c.type === "skill").map((c) => c.path));
|
|
13947
13949
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
13948
13950
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
@@ -13965,13 +13967,13 @@ async function getSkills(targetDir, rootDir = ".claude") {
|
|
|
13965
13967
|
return [];
|
|
13966
13968
|
}
|
|
13967
13969
|
}
|
|
13968
|
-
async function getGuides(targetDir) {
|
|
13970
|
+
async function getGuides(targetDir, config) {
|
|
13969
13971
|
const guidesDir = join6(targetDir, "guides");
|
|
13970
13972
|
if (!await fileExists(guidesDir))
|
|
13971
13973
|
return [];
|
|
13972
13974
|
try {
|
|
13973
|
-
const
|
|
13974
|
-
const customComponents =
|
|
13975
|
+
const resolvedConfig = config ?? await loadConfig(targetDir);
|
|
13976
|
+
const customComponents = resolvedConfig.customComponents || [];
|
|
13975
13977
|
const customGuidePaths = new Set(customComponents.filter((c) => c.type === "guide").map((c) => c.path));
|
|
13976
13978
|
const guideMdFiles = await listFiles(guidesDir, { recursive: true, pattern: "*.md" });
|
|
13977
13979
|
const guides = await Promise.all(guideMdFiles.map(async (guideMdPath) => {
|
|
@@ -13992,13 +13994,13 @@ async function getGuides(targetDir) {
|
|
|
13992
13994
|
}
|
|
13993
13995
|
}
|
|
13994
13996
|
var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
|
|
13995
|
-
async function getRules(targetDir, rootDir = ".claude") {
|
|
13997
|
+
async function getRules(targetDir, rootDir = ".claude", config) {
|
|
13996
13998
|
const rulesDir = join6(targetDir, rootDir, "rules");
|
|
13997
13999
|
if (!await fileExists(rulesDir))
|
|
13998
14000
|
return [];
|
|
13999
14001
|
try {
|
|
14000
|
-
const
|
|
14001
|
-
const customComponents =
|
|
14002
|
+
const resolvedConfig = config ?? await loadConfig(targetDir);
|
|
14003
|
+
const customComponents = resolvedConfig.customComponents || [];
|
|
14002
14004
|
const customRulePaths = new Set(customComponents.filter((c) => c.type === "rule").map((c) => c.path));
|
|
14003
14005
|
const ruleMdFiles = await listFiles(rulesDir, { recursive: false, pattern: "*.md" });
|
|
14004
14006
|
const rules = await Promise.all(ruleMdFiles.map(async (ruleMdPath) => {
|
|
@@ -14108,7 +14110,7 @@ async function getContexts(targetDir, rootDir = ".claude") {
|
|
|
14108
14110
|
var COMPONENT_GETTERS = {
|
|
14109
14111
|
agents: getAgents,
|
|
14110
14112
|
skills: getSkills,
|
|
14111
|
-
guides: async (dir2) => getGuides(dir2),
|
|
14113
|
+
guides: async (dir2, _rootDir, config) => getGuides(dir2, config),
|
|
14112
14114
|
rules: getRules,
|
|
14113
14115
|
hooks: getHooks,
|
|
14114
14116
|
contexts: getContexts
|
|
@@ -14122,12 +14124,12 @@ function displayComponents(components, type, format) {
|
|
|
14122
14124
|
formatAsTable(components, type);
|
|
14123
14125
|
}
|
|
14124
14126
|
}
|
|
14125
|
-
async function handleListAll(targetDir, rootDir, format) {
|
|
14127
|
+
async function handleListAll(targetDir, rootDir, format, config) {
|
|
14126
14128
|
const [agents, skills, guides, rules, hooks, contexts] = await Promise.all([
|
|
14127
|
-
getAgents(targetDir, rootDir),
|
|
14128
|
-
getSkills(targetDir, rootDir),
|
|
14129
|
-
getGuides(targetDir),
|
|
14130
|
-
getRules(targetDir, rootDir),
|
|
14129
|
+
getAgents(targetDir, rootDir, config),
|
|
14130
|
+
getSkills(targetDir, rootDir, config),
|
|
14131
|
+
getGuides(targetDir, config),
|
|
14132
|
+
getRules(targetDir, rootDir, config),
|
|
14131
14133
|
getHooks(targetDir, rootDir),
|
|
14132
14134
|
getContexts(targetDir, rootDir)
|
|
14133
14135
|
]);
|
|
@@ -14152,7 +14154,8 @@ async function listCommand(type = "all", options = {}) {
|
|
|
14152
14154
|
preferProject: true
|
|
14153
14155
|
});
|
|
14154
14156
|
const layout = getProviderLayout(detection.provider);
|
|
14155
|
-
const
|
|
14157
|
+
const config = await loadConfig(targetDir);
|
|
14158
|
+
const components = type === "all" ? await handleListAll(targetDir, layout.rootDir, format, config) : await COMPONENT_GETTERS[type](targetDir, layout.rootDir, config);
|
|
14156
14159
|
if (type === "all" && format === "json") {
|
|
14157
14160
|
formatAsJson(components);
|
|
14158
14161
|
} else if (type !== "all") {
|
|
@@ -14172,34 +14175,60 @@ import { join as join7 } from "node:path";
|
|
|
14172
14175
|
// src/core/entry-merger.ts
|
|
14173
14176
|
var MANAGED_START = "<!-- omcustom:start -->";
|
|
14174
14177
|
var MANAGED_END = "<!-- omcustom:end -->";
|
|
14178
|
+
function isCodeBlockDelimiter(line) {
|
|
14179
|
+
const trimmed = line.trim();
|
|
14180
|
+
return trimmed.startsWith("```") || trimmed.startsWith("~~~");
|
|
14181
|
+
}
|
|
14182
|
+
function handleManagedStart(currentLines, sections) {
|
|
14183
|
+
if (currentLines.length > 0) {
|
|
14184
|
+
sections.push({
|
|
14185
|
+
type: "custom",
|
|
14186
|
+
content: currentLines.join(`
|
|
14187
|
+
`)
|
|
14188
|
+
});
|
|
14189
|
+
}
|
|
14190
|
+
return {
|
|
14191
|
+
currentSection: { type: "managed", content: "" },
|
|
14192
|
+
currentLines: []
|
|
14193
|
+
};
|
|
14194
|
+
}
|
|
14195
|
+
function handleManagedEnd(currentSection, currentLines, sections) {
|
|
14196
|
+
if (currentSection && currentSection.type === "managed") {
|
|
14197
|
+
currentSection.content = currentLines.join(`
|
|
14198
|
+
`);
|
|
14199
|
+
sections.push(currentSection);
|
|
14200
|
+
return {
|
|
14201
|
+
currentSection: null,
|
|
14202
|
+
currentLines: []
|
|
14203
|
+
};
|
|
14204
|
+
}
|
|
14205
|
+
return { currentSection, currentLines };
|
|
14206
|
+
}
|
|
14175
14207
|
function parseEntryDoc(content) {
|
|
14176
14208
|
const sections = [];
|
|
14177
14209
|
const lines = content.split(`
|
|
14178
14210
|
`);
|
|
14179
14211
|
let currentSection = null;
|
|
14180
14212
|
let currentLines = [];
|
|
14213
|
+
let insideCodeBlock = false;
|
|
14181
14214
|
for (const line of lines) {
|
|
14182
|
-
if (line
|
|
14183
|
-
|
|
14184
|
-
|
|
14185
|
-
|
|
14186
|
-
|
|
14187
|
-
|
|
14188
|
-
|
|
14189
|
-
|
|
14215
|
+
if (isCodeBlockDelimiter(line)) {
|
|
14216
|
+
insideCodeBlock = !insideCodeBlock;
|
|
14217
|
+
}
|
|
14218
|
+
if (!insideCodeBlock) {
|
|
14219
|
+
const trimmed = line.trim();
|
|
14220
|
+
if (trimmed === MANAGED_START) {
|
|
14221
|
+
const result = handleManagedStart(currentLines, sections);
|
|
14222
|
+
currentSection = result.currentSection;
|
|
14223
|
+
currentLines = result.currentLines;
|
|
14224
|
+
continue;
|
|
14190
14225
|
}
|
|
14191
|
-
|
|
14192
|
-
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
currentSection.content = currentLines.join(`
|
|
14197
|
-
`);
|
|
14198
|
-
sections.push(currentSection);
|
|
14199
|
-
currentSection = null;
|
|
14200
|
-
currentLines = [];
|
|
14226
|
+
if (trimmed === MANAGED_END) {
|
|
14227
|
+
const result = handleManagedEnd(currentSection, currentLines, sections);
|
|
14228
|
+
currentSection = result.currentSection;
|
|
14229
|
+
currentLines = result.currentLines;
|
|
14230
|
+
continue;
|
|
14201
14231
|
}
|
|
14202
|
-
continue;
|
|
14203
14232
|
}
|
|
14204
14233
|
currentLines.push(line);
|
|
14205
14234
|
}
|
|
@@ -14280,7 +14309,7 @@ async function handleBackupIfRequested(targetDir, provider, backup, result) {
|
|
|
14280
14309
|
result.backedUpPaths.push(backupPath);
|
|
14281
14310
|
info("update.backup_created", { path: backupPath });
|
|
14282
14311
|
}
|
|
14283
|
-
async function processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result) {
|
|
14312
|
+
async function processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result, config) {
|
|
14284
14313
|
const componentUpdate = updateCheck.updatableComponents.find((c) => c.name === component);
|
|
14285
14314
|
if (!componentUpdate && !options.force) {
|
|
14286
14315
|
result.skippedComponents.push(component);
|
|
@@ -14292,7 +14321,7 @@ async function processComponentUpdate(targetDir, provider, component, updateChec
|
|
|
14292
14321
|
return;
|
|
14293
14322
|
}
|
|
14294
14323
|
try {
|
|
14295
|
-
const preserved = await updateComponent(targetDir, provider, component, customizations, options);
|
|
14324
|
+
const preserved = await updateComponent(targetDir, provider, component, customizations, options, config);
|
|
14296
14325
|
result.updatedComponents.push(component);
|
|
14297
14326
|
result.preservedFiles.push(...preserved);
|
|
14298
14327
|
} catch (err) {
|
|
@@ -14301,9 +14330,9 @@ async function processComponentUpdate(targetDir, provider, component, updateChec
|
|
|
14301
14330
|
result.skippedComponents.push(component);
|
|
14302
14331
|
}
|
|
14303
14332
|
}
|
|
14304
|
-
async function updateAllComponents(targetDir, provider, components, updateCheck, customizations, options, result) {
|
|
14333
|
+
async function updateAllComponents(targetDir, provider, components, updateCheck, customizations, options, result, config) {
|
|
14305
14334
|
for (const component of components) {
|
|
14306
|
-
await processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result);
|
|
14335
|
+
await processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result, config);
|
|
14307
14336
|
}
|
|
14308
14337
|
}
|
|
14309
14338
|
function getEntryTemplateName2(provider, language) {
|
|
@@ -14320,6 +14349,21 @@ async function backupFile(filePath) {
|
|
|
14320
14349
|
debug("update.file_backed_up", { path: filePath, backup: backupPath });
|
|
14321
14350
|
}
|
|
14322
14351
|
}
|
|
14352
|
+
async function resolveManifestCustomizations(options, targetDir) {
|
|
14353
|
+
if (options.forceOverwriteAll) {
|
|
14354
|
+
return null;
|
|
14355
|
+
}
|
|
14356
|
+
if (options.preserveCustomizations === false) {
|
|
14357
|
+
return null;
|
|
14358
|
+
}
|
|
14359
|
+
return loadCustomizationManifest(targetDir);
|
|
14360
|
+
}
|
|
14361
|
+
function resolveConfigPreserveFiles(options, config) {
|
|
14362
|
+
if (options.forceOverwriteAll) {
|
|
14363
|
+
return [];
|
|
14364
|
+
}
|
|
14365
|
+
return config.preserveFiles || [];
|
|
14366
|
+
}
|
|
14323
14367
|
function resolveCustomizations(customizations, configPreserveFiles) {
|
|
14324
14368
|
if (!customizations && configPreserveFiles.length === 0) {
|
|
14325
14369
|
return null;
|
|
@@ -14391,11 +14435,11 @@ async function update(options) {
|
|
|
14391
14435
|
return result;
|
|
14392
14436
|
}
|
|
14393
14437
|
await handleBackupIfRequested(options.targetDir, provider, !!options.backup, result);
|
|
14394
|
-
const manifestCustomizations =
|
|
14395
|
-
const configPreserveFiles = config
|
|
14438
|
+
const manifestCustomizations = await resolveManifestCustomizations(options, options.targetDir);
|
|
14439
|
+
const configPreserveFiles = resolveConfigPreserveFiles(options, config);
|
|
14396
14440
|
const customizations = resolveCustomizations(manifestCustomizations, configPreserveFiles);
|
|
14397
14441
|
const components = options.components || getAllUpdateComponents();
|
|
14398
|
-
await updateAllComponents(options.targetDir, provider, components, updateCheck, customizations, options, result);
|
|
14442
|
+
await updateAllComponents(options.targetDir, provider, components, updateCheck, customizations, options, result, config);
|
|
14399
14443
|
if (!options.components || options.components.length === 0) {
|
|
14400
14444
|
await updateEntryDoc(options.targetDir, provider, config, options);
|
|
14401
14445
|
}
|
|
@@ -14454,15 +14498,14 @@ async function componentHasUpdate(_targetDir, provider, component, config) {
|
|
|
14454
14498
|
const latestVersion = await getLatestVersion(provider);
|
|
14455
14499
|
return installedVersion !== latestVersion;
|
|
14456
14500
|
}
|
|
14457
|
-
async function updateComponent(targetDir, provider, component, customizations, options) {
|
|
14501
|
+
async function updateComponent(targetDir, provider, component, customizations, options, config) {
|
|
14458
14502
|
const preservedFiles = [];
|
|
14459
14503
|
const componentPath = getComponentPath2(provider, component);
|
|
14460
14504
|
const srcPath = resolveTemplatePath(componentPath);
|
|
14461
14505
|
const destPath = join7(targetDir, componentPath);
|
|
14462
|
-
const config = await loadConfig(targetDir);
|
|
14463
14506
|
const customComponents = config.customComponents || [];
|
|
14464
14507
|
const skipPaths = [];
|
|
14465
|
-
if (customizations && options.
|
|
14508
|
+
if (customizations && !options.forceOverwriteAll) {
|
|
14466
14509
|
const toPreserve = customizations.preserveFiles.filter((f) => f.startsWith(componentPath));
|
|
14467
14510
|
preservedFiles.push(...toPreserve);
|
|
14468
14511
|
skipPaths.push(...toPreserve);
|
|
@@ -14538,6 +14581,7 @@ async function updateCommand(options = {}) {
|
|
|
14538
14581
|
components,
|
|
14539
14582
|
force: options.force,
|
|
14540
14583
|
preserveCustomizations: true,
|
|
14584
|
+
forceOverwriteAll: options.forceOverwriteAll,
|
|
14541
14585
|
dryRun: options.dryRun,
|
|
14542
14586
|
backup: options.backup
|
|
14543
14587
|
};
|
|
@@ -14611,7 +14655,7 @@ function createProgram() {
|
|
|
14611
14655
|
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) => {
|
|
14612
14656
|
await initCommand(options);
|
|
14613
14657
|
});
|
|
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) => {
|
|
14658
|
+
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("--force-overwrite-all", i18n.t("cli.update.forceOverwriteAllOption")).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
14659
|
await updateCommand(options);
|
|
14616
14660
|
});
|
|
14617
14661
|
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) => {
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,4 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
-
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
-
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
-
for (let key of __getOwnPropNames(mod))
|
|
11
|
-
if (!__hasOwnProp.call(to, key))
|
|
12
|
-
__defProp(to, key, {
|
|
13
|
-
get: () => mod[key],
|
|
14
|
-
enumerable: true
|
|
15
|
-
});
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
3
|
|
|
20
4
|
// src/core/config.ts
|
|
@@ -904,34 +888,60 @@ import { join as join5 } from "node:path";
|
|
|
904
888
|
// src/core/entry-merger.ts
|
|
905
889
|
var MANAGED_START = "<!-- omcustom:start -->";
|
|
906
890
|
var MANAGED_END = "<!-- omcustom:end -->";
|
|
891
|
+
function isCodeBlockDelimiter(line) {
|
|
892
|
+
const trimmed = line.trim();
|
|
893
|
+
return trimmed.startsWith("```") || trimmed.startsWith("~~~");
|
|
894
|
+
}
|
|
895
|
+
function handleManagedStart(currentLines, sections) {
|
|
896
|
+
if (currentLines.length > 0) {
|
|
897
|
+
sections.push({
|
|
898
|
+
type: "custom",
|
|
899
|
+
content: currentLines.join(`
|
|
900
|
+
`)
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
currentSection: { type: "managed", content: "" },
|
|
905
|
+
currentLines: []
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
function handleManagedEnd(currentSection, currentLines, sections) {
|
|
909
|
+
if (currentSection && currentSection.type === "managed") {
|
|
910
|
+
currentSection.content = currentLines.join(`
|
|
911
|
+
`);
|
|
912
|
+
sections.push(currentSection);
|
|
913
|
+
return {
|
|
914
|
+
currentSection: null,
|
|
915
|
+
currentLines: []
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
return { currentSection, currentLines };
|
|
919
|
+
}
|
|
907
920
|
function parseEntryDoc(content) {
|
|
908
921
|
const sections = [];
|
|
909
922
|
const lines = content.split(`
|
|
910
923
|
`);
|
|
911
924
|
let currentSection = null;
|
|
912
925
|
let currentLines = [];
|
|
926
|
+
let insideCodeBlock = false;
|
|
913
927
|
for (const line of lines) {
|
|
914
|
-
if (line
|
|
915
|
-
|
|
916
|
-
sections.push({
|
|
917
|
-
type: "custom",
|
|
918
|
-
content: currentLines.join(`
|
|
919
|
-
`)
|
|
920
|
-
});
|
|
921
|
-
currentLines = [];
|
|
922
|
-
}
|
|
923
|
-
currentSection = { type: "managed", content: "" };
|
|
924
|
-
continue;
|
|
928
|
+
if (isCodeBlockDelimiter(line)) {
|
|
929
|
+
insideCodeBlock = !insideCodeBlock;
|
|
925
930
|
}
|
|
926
|
-
if (
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
931
|
+
if (!insideCodeBlock) {
|
|
932
|
+
const trimmed = line.trim();
|
|
933
|
+
if (trimmed === MANAGED_START) {
|
|
934
|
+
const result = handleManagedStart(currentLines, sections);
|
|
935
|
+
currentSection = result.currentSection;
|
|
936
|
+
currentLines = result.currentLines;
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
if (trimmed === MANAGED_END) {
|
|
940
|
+
const result = handleManagedEnd(currentSection, currentLines, sections);
|
|
941
|
+
currentSection = result.currentSection;
|
|
942
|
+
currentLines = result.currentLines;
|
|
943
|
+
continue;
|
|
933
944
|
}
|
|
934
|
-
continue;
|
|
935
945
|
}
|
|
936
946
|
currentLines.push(line);
|
|
937
947
|
}
|
|
@@ -1012,7 +1022,7 @@ async function handleBackupIfRequested(targetDir, provider, backup, result) {
|
|
|
1012
1022
|
result.backedUpPaths.push(backupPath);
|
|
1013
1023
|
info("update.backup_created", { path: backupPath });
|
|
1014
1024
|
}
|
|
1015
|
-
async function processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result) {
|
|
1025
|
+
async function processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result, config) {
|
|
1016
1026
|
const componentUpdate = updateCheck.updatableComponents.find((c) => c.name === component);
|
|
1017
1027
|
if (!componentUpdate && !options.force) {
|
|
1018
1028
|
result.skippedComponents.push(component);
|
|
@@ -1024,7 +1034,7 @@ async function processComponentUpdate(targetDir, provider, component, updateChec
|
|
|
1024
1034
|
return;
|
|
1025
1035
|
}
|
|
1026
1036
|
try {
|
|
1027
|
-
const preserved = await updateComponent(targetDir, provider, component, customizations, options);
|
|
1037
|
+
const preserved = await updateComponent(targetDir, provider, component, customizations, options, config);
|
|
1028
1038
|
result.updatedComponents.push(component);
|
|
1029
1039
|
result.preservedFiles.push(...preserved);
|
|
1030
1040
|
} catch (err) {
|
|
@@ -1033,9 +1043,9 @@ async function processComponentUpdate(targetDir, provider, component, updateChec
|
|
|
1033
1043
|
result.skippedComponents.push(component);
|
|
1034
1044
|
}
|
|
1035
1045
|
}
|
|
1036
|
-
async function updateAllComponents(targetDir, provider, components, updateCheck, customizations, options, result) {
|
|
1046
|
+
async function updateAllComponents(targetDir, provider, components, updateCheck, customizations, options, result, config) {
|
|
1037
1047
|
for (const component of components) {
|
|
1038
|
-
await processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result);
|
|
1048
|
+
await processComponentUpdate(targetDir, provider, component, updateCheck, customizations, options, result, config);
|
|
1039
1049
|
}
|
|
1040
1050
|
}
|
|
1041
1051
|
function getEntryTemplateName2(provider, language) {
|
|
@@ -1052,6 +1062,21 @@ async function backupFile(filePath) {
|
|
|
1052
1062
|
debug("update.file_backed_up", { path: filePath, backup: backupPath });
|
|
1053
1063
|
}
|
|
1054
1064
|
}
|
|
1065
|
+
async function resolveManifestCustomizations(options, targetDir) {
|
|
1066
|
+
if (options.forceOverwriteAll) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
if (options.preserveCustomizations === false) {
|
|
1070
|
+
return null;
|
|
1071
|
+
}
|
|
1072
|
+
return loadCustomizationManifest(targetDir);
|
|
1073
|
+
}
|
|
1074
|
+
function resolveConfigPreserveFiles(options, config) {
|
|
1075
|
+
if (options.forceOverwriteAll) {
|
|
1076
|
+
return [];
|
|
1077
|
+
}
|
|
1078
|
+
return config.preserveFiles || [];
|
|
1079
|
+
}
|
|
1055
1080
|
function resolveCustomizations(customizations, configPreserveFiles) {
|
|
1056
1081
|
if (!customizations && configPreserveFiles.length === 0) {
|
|
1057
1082
|
return null;
|
|
@@ -1123,11 +1148,11 @@ async function update(options) {
|
|
|
1123
1148
|
return result;
|
|
1124
1149
|
}
|
|
1125
1150
|
await handleBackupIfRequested(options.targetDir, provider, !!options.backup, result);
|
|
1126
|
-
const manifestCustomizations =
|
|
1127
|
-
const configPreserveFiles = config
|
|
1151
|
+
const manifestCustomizations = await resolveManifestCustomizations(options, options.targetDir);
|
|
1152
|
+
const configPreserveFiles = resolveConfigPreserveFiles(options, config);
|
|
1128
1153
|
const customizations = resolveCustomizations(manifestCustomizations, configPreserveFiles);
|
|
1129
1154
|
const components = options.components || getAllUpdateComponents();
|
|
1130
|
-
await updateAllComponents(options.targetDir, provider, components, updateCheck, customizations, options, result);
|
|
1155
|
+
await updateAllComponents(options.targetDir, provider, components, updateCheck, customizations, options, result, config);
|
|
1131
1156
|
if (!options.components || options.components.length === 0) {
|
|
1132
1157
|
await updateEntryDoc(options.targetDir, provider, config, options);
|
|
1133
1158
|
}
|
|
@@ -1207,15 +1232,14 @@ async function componentHasUpdate(_targetDir, provider, component, config) {
|
|
|
1207
1232
|
const latestVersion = await getLatestVersion(provider);
|
|
1208
1233
|
return installedVersion !== latestVersion;
|
|
1209
1234
|
}
|
|
1210
|
-
async function updateComponent(targetDir, provider, component, customizations, options) {
|
|
1235
|
+
async function updateComponent(targetDir, provider, component, customizations, options, config) {
|
|
1211
1236
|
const preservedFiles = [];
|
|
1212
1237
|
const componentPath = getComponentPath2(provider, component);
|
|
1213
1238
|
const srcPath = resolveTemplatePath(componentPath);
|
|
1214
1239
|
const destPath = join5(targetDir, componentPath);
|
|
1215
|
-
const config = await loadConfig(targetDir);
|
|
1216
1240
|
const customComponents = config.customComponents || [];
|
|
1217
1241
|
const skipPaths = [];
|
|
1218
|
-
if (customizations && options.
|
|
1242
|
+
if (customizations && !options.forceOverwriteAll) {
|
|
1219
1243
|
const toPreserve = customizations.preserveFiles.filter((f) => f.startsWith(componentPath));
|
|
1220
1244
|
preservedFiles.push(...toPreserve);
|
|
1221
1245
|
skipPaths.push(...toPreserve);
|