oh-my-customcode 0.32.0 → 0.33.1
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 +13 -10
- package/dist/cli/index.js +197 -76
- package/dist/index.js +52 -6
- package/package.json +1 -1
- package/templates/.claude/rules/MUST-agent-design.md +11 -0
- package/templates/.claude/rules/MUST-agent-identification.md +4 -4
- package/templates/.claude/rules/MUST-agent-teams.md +17 -18
- package/templates/.claude/rules/MUST-continuous-improvement.md +3 -3
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +24 -42
- package/templates/.claude/rules/MUST-parallel-execution.md +18 -40
- package/templates/.claude/rules/MUST-sync-verification.md +6 -6
- package/templates/.claude/rules/MUST-tool-identification.md +39 -10
- package/templates/.claude/rules/SHOULD-ontology-rag-routing.md +49 -0
- package/templates/.claude/skills/airflow-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/analysis/SKILL.md +2 -1
- package/templates/.claude/skills/audit-agents/SKILL.md +4 -3
- package/templates/.claude/skills/aws-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/claude-code-bible/SKILL.md +1 -0
- package/templates/.claude/skills/codex-exec/SKILL.md +5 -4
- package/templates/.claude/skills/create-agent/SKILL.md +1 -0
- package/templates/.claude/skills/cve-triage/SKILL.md +1 -0
- package/templates/.claude/skills/dag-orchestration/SKILL.md +1 -0
- package/templates/.claude/skills/dbt-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/de-lead-routing/SKILL.md +1 -0
- package/templates/.claude/skills/deep-plan/SKILL.md +292 -0
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +1 -0
- package/templates/.claude/skills/dev-refactor/SKILL.md +1 -0
- package/templates/.claude/skills/dev-review/SKILL.md +1 -0
- package/templates/.claude/skills/django-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/docker-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/fastapi-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/fix-refs/SKILL.md +1 -0
- package/templates/.claude/skills/flutter-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/go-backend-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/go-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/help/SKILL.md +1 -0
- package/templates/.claude/skills/intent-detection/SKILL.md +1 -0
- package/templates/.claude/skills/java21-best-practices/SKILL.md +305 -0
- package/templates/.claude/skills/jinja2-prompts/SKILL.md +1 -0
- package/templates/.claude/skills/kafka-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/kotlin-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/lists/SKILL.md +1 -0
- package/templates/.claude/skills/memory-management/SKILL.md +1 -0
- package/templates/.claude/skills/memory-recall/SKILL.md +1 -0
- package/templates/.claude/skills/memory-save/SKILL.md +1 -0
- package/templates/.claude/skills/model-escalation/SKILL.md +1 -0
- package/templates/.claude/skills/monitoring-setup/SKILL.md +1 -0
- package/templates/.claude/skills/multi-model-verification/SKILL.md +1 -0
- package/templates/.claude/skills/npm-audit/SKILL.md +1 -0
- package/templates/.claude/skills/npm-publish/SKILL.md +1 -0
- package/templates/.claude/skills/npm-version/SKILL.md +1 -0
- package/templates/.claude/skills/optimize-analyze/SKILL.md +1 -0
- package/templates/.claude/skills/optimize-bundle/SKILL.md +1 -0
- package/templates/.claude/skills/optimize-report/SKILL.md +1 -0
- package/templates/.claude/skills/pipeline-architecture-patterns/SKILL.md +1 -0
- package/templates/.claude/skills/pipeline-guards/SKILL.md +2 -0
- package/templates/.claude/skills/postgres-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/pr-auto-improve/SKILL.md +1 -0
- package/templates/.claude/skills/python-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/qa-lead-routing/SKILL.md +1 -0
- package/templates/.claude/skills/react-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/redis-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/research/SKILL.md +1 -0
- package/templates/.claude/skills/result-aggregation/SKILL.md +1 -0
- package/templates/.claude/skills/rust-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/sauron-watch/SKILL.md +1 -0
- package/templates/.claude/skills/secretary-routing/SKILL.md +1 -0
- package/templates/.claude/skills/skills-sh-search/SKILL.md +1 -0
- package/templates/.claude/skills/snowflake-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/spark-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/springboot-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/status/SKILL.md +1 -0
- package/templates/.claude/skills/structured-dev-cycle/SKILL.md +1 -0
- package/templates/.claude/skills/stuck-recovery/SKILL.md +1 -0
- package/templates/.claude/skills/supabase-postgres-best-practices/SKILL.md +2 -1
- package/templates/.claude/skills/task-decomposition/SKILL.md +2 -0
- package/templates/.claude/skills/typescript-best-practices/SKILL.md +1 -0
- package/templates/.claude/skills/update-docs/SKILL.md +4 -3
- package/templates/.claude/skills/update-external/SKILL.md +1 -0
- package/templates/.claude/skills/vercel-deploy/SKILL.md +1 -0
- package/templates/.claude/skills/web-design-guidelines/SKILL.md +1 -0
- package/templates/.claude/skills/worker-reviewer-pipeline/SKILL.md +2 -0
- package/templates/.claude/skills/writing-clearly-and-concisely/SKILL.md +3 -2
- package/templates/CLAUDE.md.en +7 -5
- package/templates/CLAUDE.md.ko +7 -5
- package/templates/manifest.json +4 -4
package/dist/cli/index.js
CHANGED
|
@@ -12164,6 +12164,10 @@ var en_default = {
|
|
|
12164
12164
|
index: {
|
|
12165
12165
|
pass: "All index.yaml files are valid",
|
|
12166
12166
|
fail: "Some index.yaml files are invalid"
|
|
12167
|
+
},
|
|
12168
|
+
framework: {
|
|
12169
|
+
pass: "Framework is up to date (v{{version}})",
|
|
12170
|
+
warn: "Framework is outdated: installed v{{installed}}, latest v{{latest}} ({{behind}} version(s) behind). Run 'omcustom update' to sync."
|
|
12167
12171
|
}
|
|
12168
12172
|
}
|
|
12169
12173
|
},
|
|
@@ -12484,6 +12488,10 @@ var ko_default = {
|
|
|
12484
12488
|
index: {
|
|
12485
12489
|
pass: "모든 index.yaml 파일 정상",
|
|
12486
12490
|
fail: "일부 index.yaml 파일이 잘못됨"
|
|
12491
|
+
},
|
|
12492
|
+
framework: {
|
|
12493
|
+
pass: "프레임워크가 최신 상태입니다 (v{{version}})",
|
|
12494
|
+
warn: "프레임워크가 구버전입니다: 설치됨 v{{installed}}, 최신 v{{latest}} ({{behind}}개 버전 뒤처짐). 'omcustom update'를 실행하여 동기화하세요."
|
|
12487
12495
|
}
|
|
12488
12496
|
}
|
|
12489
12497
|
},
|
|
@@ -13275,6 +13283,44 @@ function migrateConfig(config) {
|
|
|
13275
13283
|
return migrated;
|
|
13276
13284
|
}
|
|
13277
13285
|
|
|
13286
|
+
// src/core/doctor-framework.ts
|
|
13287
|
+
init_fs();
|
|
13288
|
+
import { readFile } from "node:fs/promises";
|
|
13289
|
+
import { join as join4 } from "node:path";
|
|
13290
|
+
async function getInstalledVersion(targetDir) {
|
|
13291
|
+
const rcPath = join4(targetDir, ".omcustomrc.json");
|
|
13292
|
+
if (!await fileExists(rcPath))
|
|
13293
|
+
return null;
|
|
13294
|
+
try {
|
|
13295
|
+
const content = JSON.parse(await readFile(rcPath, "utf-8"));
|
|
13296
|
+
return content.version ?? null;
|
|
13297
|
+
} catch {
|
|
13298
|
+
return null;
|
|
13299
|
+
}
|
|
13300
|
+
}
|
|
13301
|
+
function calculateVersionsBehind(installed, latest) {
|
|
13302
|
+
const [installedMajor, installedMinor] = installed.split(".").map(Number);
|
|
13303
|
+
const [latestMajor, latestMinor] = latest.split(".").map(Number);
|
|
13304
|
+
if (installedMajor > latestMajor)
|
|
13305
|
+
return 0;
|
|
13306
|
+
if (latestMajor > installedMajor) {
|
|
13307
|
+
return (latestMajor - installedMajor) * 100 + latestMinor;
|
|
13308
|
+
}
|
|
13309
|
+
return Math.max(0, latestMinor - installedMinor);
|
|
13310
|
+
}
|
|
13311
|
+
async function checkFrameworkVersion(targetDir, latestVersion) {
|
|
13312
|
+
const installed = await getInstalledVersion(targetDir);
|
|
13313
|
+
if (!installed)
|
|
13314
|
+
return null;
|
|
13315
|
+
const versionsBehind = calculateVersionsBehind(installed, latestVersion);
|
|
13316
|
+
return {
|
|
13317
|
+
installed,
|
|
13318
|
+
latest: latestVersion,
|
|
13319
|
+
isOutdated: installed !== latestVersion,
|
|
13320
|
+
versionsBehind
|
|
13321
|
+
};
|
|
13322
|
+
}
|
|
13323
|
+
|
|
13278
13324
|
// src/core/layout.ts
|
|
13279
13325
|
var CLAUDE_LAYOUT = {
|
|
13280
13326
|
rootDir: ".claude",
|
|
@@ -13756,6 +13802,29 @@ function readCurrentVersion() {
|
|
|
13756
13802
|
return "0.0.0";
|
|
13757
13803
|
}
|
|
13758
13804
|
}
|
|
13805
|
+
async function checkFrameworkDrift(targetDir, currentVersion) {
|
|
13806
|
+
const result = await checkFrameworkVersion(targetDir, currentVersion);
|
|
13807
|
+
if (!result)
|
|
13808
|
+
return null;
|
|
13809
|
+
if (result.isOutdated) {
|
|
13810
|
+
return {
|
|
13811
|
+
name: "Framework",
|
|
13812
|
+
status: "warn",
|
|
13813
|
+
message: i18n.t("cli.doctor.checks.framework.warn", {
|
|
13814
|
+
installed: result.installed,
|
|
13815
|
+
latest: result.latest,
|
|
13816
|
+
behind: String(result.versionsBehind)
|
|
13817
|
+
}),
|
|
13818
|
+
fixable: false
|
|
13819
|
+
};
|
|
13820
|
+
}
|
|
13821
|
+
return {
|
|
13822
|
+
name: "Framework",
|
|
13823
|
+
status: "pass",
|
|
13824
|
+
message: i18n.t("cli.doctor.checks.framework.pass", { version: result.installed }),
|
|
13825
|
+
fixable: false
|
|
13826
|
+
};
|
|
13827
|
+
}
|
|
13759
13828
|
function checkUpdateAvailable(currentVersion) {
|
|
13760
13829
|
const result = checkSelfUpdate({ currentVersion });
|
|
13761
13830
|
if (!result.checked) {
|
|
@@ -13785,11 +13854,7 @@ function checkUpdateAvailable(currentVersion) {
|
|
|
13785
13854
|
fixable: false
|
|
13786
13855
|
};
|
|
13787
13856
|
}
|
|
13788
|
-
async function
|
|
13789
|
-
const targetDir = process.cwd();
|
|
13790
|
-
console.log(i18n.t("cli.doctor.checking"));
|
|
13791
|
-
console.log("");
|
|
13792
|
-
const layout = getProviderLayout();
|
|
13857
|
+
async function runAllChecks(targetDir, layout, packageVersion, includeUpdates) {
|
|
13793
13858
|
const baseChecks = await Promise.all([
|
|
13794
13859
|
checkEntryDoc(targetDir, layout.entryFile),
|
|
13795
13860
|
checkRules(targetDir, layout.rootDir),
|
|
@@ -13802,7 +13867,17 @@ async function doctorCommand(options = {}) {
|
|
|
13802
13867
|
checkContexts(targetDir, layout.rootDir),
|
|
13803
13868
|
checkCustomComponents(targetDir, layout.rootDir)
|
|
13804
13869
|
]);
|
|
13805
|
-
const
|
|
13870
|
+
const frameworkCheck = await checkFrameworkDrift(targetDir, packageVersion);
|
|
13871
|
+
const checksWithFramework = frameworkCheck ? [...baseChecks, frameworkCheck] : baseChecks;
|
|
13872
|
+
return includeUpdates ? [...checksWithFramework, checkUpdateAvailable(packageVersion)] : checksWithFramework;
|
|
13873
|
+
}
|
|
13874
|
+
async function doctorCommand(options = {}) {
|
|
13875
|
+
const targetDir = process.cwd();
|
|
13876
|
+
console.log(i18n.t("cli.doctor.checking"));
|
|
13877
|
+
console.log("");
|
|
13878
|
+
const layout = getProviderLayout();
|
|
13879
|
+
const packageVersion = readCurrentVersion();
|
|
13880
|
+
const checksWithUpdate = await runAllChecks(targetDir, layout, packageVersion, options.updates ?? false);
|
|
13806
13881
|
let checks = checksWithUpdate;
|
|
13807
13882
|
if (options.fix) {
|
|
13808
13883
|
const hasFixableIssues = checksWithUpdate.some((c) => c.status === "fail" && c.fixable);
|
|
@@ -13851,16 +13926,22 @@ async function doctorCommand(options = {}) {
|
|
|
13851
13926
|
}
|
|
13852
13927
|
|
|
13853
13928
|
// src/cli/init.ts
|
|
13854
|
-
import { join as
|
|
13929
|
+
import { join as join9 } from "node:path";
|
|
13855
13930
|
|
|
13856
13931
|
// src/core/installer.ts
|
|
13857
13932
|
init_fs();
|
|
13858
|
-
import {
|
|
13859
|
-
|
|
13933
|
+
import {
|
|
13934
|
+
readFile as fsReadFile,
|
|
13935
|
+
writeFile as fsWriteFile,
|
|
13936
|
+
readdir as readdir2,
|
|
13937
|
+
rename,
|
|
13938
|
+
stat as stat2
|
|
13939
|
+
} from "node:fs/promises";
|
|
13940
|
+
import { basename as basename2, join as join7 } from "node:path";
|
|
13860
13941
|
|
|
13861
13942
|
// src/core/file-preservation.ts
|
|
13862
13943
|
init_fs();
|
|
13863
|
-
import { basename, join as
|
|
13944
|
+
import { basename, join as join5 } from "node:path";
|
|
13864
13945
|
var DEFAULT_CRITICAL_FILES = ["settings.json", "settings.local.json"];
|
|
13865
13946
|
var DEFAULT_CRITICAL_DIRECTORIES = ["agent-memory", "agent-memory-local"];
|
|
13866
13947
|
var PROTECTED_FRAMEWORK_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
@@ -13883,8 +13964,8 @@ function matchesGlobPattern(filePath, pattern) {
|
|
|
13883
13964
|
return regex.test(filePath);
|
|
13884
13965
|
}
|
|
13885
13966
|
async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
13886
|
-
const srcPath =
|
|
13887
|
-
const destPath =
|
|
13967
|
+
const srcPath = join5(rootDir, fileName);
|
|
13968
|
+
const destPath = join5(tempDir, fileName);
|
|
13888
13969
|
try {
|
|
13889
13970
|
if (await fileExists(srcPath)) {
|
|
13890
13971
|
await copyFile(srcPath, destPath);
|
|
@@ -13898,8 +13979,8 @@ async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
|
13898
13979
|
}
|
|
13899
13980
|
}
|
|
13900
13981
|
async function extractSingleDir(dirName, rootDir, tempDir, result) {
|
|
13901
|
-
const srcPath =
|
|
13902
|
-
const destPath =
|
|
13982
|
+
const srcPath = join5(rootDir, dirName);
|
|
13983
|
+
const destPath = join5(tempDir, dirName);
|
|
13903
13984
|
try {
|
|
13904
13985
|
if (await fileExists(srcPath)) {
|
|
13905
13986
|
await copyDirectory(srcPath, destPath, { overwrite: true, preserveTimestamps: true });
|
|
@@ -13936,8 +14017,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
13936
14017
|
failures: []
|
|
13937
14018
|
};
|
|
13938
14019
|
for (const fileName of preservation.extractedFiles) {
|
|
13939
|
-
const preservedPath =
|
|
13940
|
-
const targetPath =
|
|
14020
|
+
const preservedPath = join5(preservation.tempDir, fileName);
|
|
14021
|
+
const targetPath = join5(rootDir, fileName);
|
|
13941
14022
|
try {
|
|
13942
14023
|
if (fileName.endsWith(".json")) {
|
|
13943
14024
|
await mergeJsonFile(preservedPath, targetPath);
|
|
@@ -13953,8 +14034,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
13953
14034
|
}
|
|
13954
14035
|
}
|
|
13955
14036
|
for (const dirName of preservation.extractedDirs) {
|
|
13956
|
-
const preservedPath =
|
|
13957
|
-
const targetPath =
|
|
14037
|
+
const preservedPath = join5(preservation.tempDir, dirName);
|
|
14038
|
+
const targetPath = join5(rootDir, dirName);
|
|
13958
14039
|
try {
|
|
13959
14040
|
await copyDirectory(preservedPath, targetPath, {
|
|
13960
14041
|
overwrite: false,
|
|
@@ -14258,7 +14339,7 @@ init_fs();
|
|
|
14258
14339
|
import { createHash } from "node:crypto";
|
|
14259
14340
|
import { createReadStream } from "node:fs";
|
|
14260
14341
|
import { readdir, stat } from "node:fs/promises";
|
|
14261
|
-
import { join as
|
|
14342
|
+
import { join as join6, relative as relative2 } from "node:path";
|
|
14262
14343
|
var LOCKFILE_NAME = ".omcustom.lock.json";
|
|
14263
14344
|
var LOCKFILE_VERSION = 1;
|
|
14264
14345
|
var LOCKFILE_COMPONENTS = [
|
|
@@ -14287,7 +14368,7 @@ function computeFileHash(filePath) {
|
|
|
14287
14368
|
});
|
|
14288
14369
|
}
|
|
14289
14370
|
async function writeLockfile(targetDir, lockfile) {
|
|
14290
|
-
const lockfilePath =
|
|
14371
|
+
const lockfilePath = join6(targetDir, LOCKFILE_NAME);
|
|
14291
14372
|
await writeJsonFile(lockfilePath, lockfile);
|
|
14292
14373
|
debug("lockfile.written", { path: lockfilePath });
|
|
14293
14374
|
}
|
|
@@ -14312,7 +14393,7 @@ async function collectFiles(dir2, projectRoot, isTopLevel) {
|
|
|
14312
14393
|
if (isTopLevel && entry.startsWith(".") && entry !== ".claude") {
|
|
14313
14394
|
continue;
|
|
14314
14395
|
}
|
|
14315
|
-
const fullPath =
|
|
14396
|
+
const fullPath = join6(dir2, entry);
|
|
14316
14397
|
let fileStat;
|
|
14317
14398
|
try {
|
|
14318
14399
|
fileStat = await stat(fullPath);
|
|
@@ -14330,7 +14411,7 @@ async function collectFiles(dir2, projectRoot, isTopLevel) {
|
|
|
14330
14411
|
}
|
|
14331
14412
|
async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
14332
14413
|
const files = {};
|
|
14333
|
-
const componentRoots = COMPONENT_PATHS.map(([prefix]) =>
|
|
14414
|
+
const componentRoots = COMPONENT_PATHS.map(([prefix]) => join6(targetDir, prefix));
|
|
14334
14415
|
for (const componentRoot of componentRoots) {
|
|
14335
14416
|
const exists2 = await fileExists(componentRoot);
|
|
14336
14417
|
if (!exists2) {
|
|
@@ -14370,8 +14451,8 @@ async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
|
14370
14451
|
async function generateAndWriteLockfileForDir(targetDir) {
|
|
14371
14452
|
try {
|
|
14372
14453
|
const packageRoot = getPackageRoot();
|
|
14373
|
-
const manifest = await readJsonFile(
|
|
14374
|
-
const { version: generatorVersion } = await readJsonFile(
|
|
14454
|
+
const manifest = await readJsonFile(join6(packageRoot, "templates", "manifest.json"));
|
|
14455
|
+
const { version: generatorVersion } = await readJsonFile(join6(packageRoot, "package.json"));
|
|
14375
14456
|
const lockfile = await generateLockfile(targetDir, generatorVersion, manifest.version);
|
|
14376
14457
|
await writeLockfile(targetDir, lockfile);
|
|
14377
14458
|
return { fileCount: Object.keys(lockfile.files).length };
|
|
@@ -14381,11 +14462,24 @@ async function generateAndWriteLockfileForDir(targetDir) {
|
|
|
14381
14462
|
}
|
|
14382
14463
|
}
|
|
14383
14464
|
|
|
14465
|
+
// src/core/scope-filter.ts
|
|
14466
|
+
function getSkillScope(content) {
|
|
14467
|
+
const cleaned = content.replace(/^\uFEFF/, "");
|
|
14468
|
+
const frontmatter = cleaned.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
14469
|
+
if (!frontmatter)
|
|
14470
|
+
return "core";
|
|
14471
|
+
const match = frontmatter[1].match(/^scope:\s*(core|harness|package)\s*$/m);
|
|
14472
|
+
return match?.[1] ?? "core";
|
|
14473
|
+
}
|
|
14474
|
+
function shouldInstallSkill(scope) {
|
|
14475
|
+
return scope !== "package";
|
|
14476
|
+
}
|
|
14477
|
+
|
|
14384
14478
|
// src/core/installer.ts
|
|
14385
14479
|
var DEFAULT_LANGUAGE2 = "en";
|
|
14386
14480
|
function getTemplateDir() {
|
|
14387
14481
|
const packageRoot = getPackageRoot();
|
|
14388
|
-
return
|
|
14482
|
+
return join7(packageRoot, "templates");
|
|
14389
14483
|
}
|
|
14390
14484
|
function createInstallResult(targetDir) {
|
|
14391
14485
|
return {
|
|
@@ -14407,7 +14501,7 @@ async function handleBackup(targetDir, shouldBackup, result) {
|
|
|
14407
14501
|
if (!shouldBackup)
|
|
14408
14502
|
return null;
|
|
14409
14503
|
const layout = getProviderLayout();
|
|
14410
|
-
const rootDir =
|
|
14504
|
+
const rootDir = join7(targetDir, layout.rootDir);
|
|
14411
14505
|
let preservation = null;
|
|
14412
14506
|
if (await fileExists(rootDir)) {
|
|
14413
14507
|
const { createTempDir: createTempDir2 } = await Promise.resolve().then(() => (init_fs(), exports_fs));
|
|
@@ -14464,8 +14558,8 @@ async function installSingleComponent(targetDir, component, options, result) {
|
|
|
14464
14558
|
}
|
|
14465
14559
|
async function installStatusline(targetDir, options, _result) {
|
|
14466
14560
|
const layout = getProviderLayout();
|
|
14467
|
-
const srcPath = resolveTemplatePath(
|
|
14468
|
-
const destPath =
|
|
14561
|
+
const srcPath = resolveTemplatePath(join7(layout.rootDir, "statusline.sh"));
|
|
14562
|
+
const destPath = join7(targetDir, layout.rootDir, "statusline.sh");
|
|
14469
14563
|
if (!await fileExists(srcPath)) {
|
|
14470
14564
|
debug("install.statusline_not_found", { path: srcPath });
|
|
14471
14565
|
return;
|
|
@@ -14483,7 +14577,7 @@ async function installStatusline(targetDir, options, _result) {
|
|
|
14483
14577
|
}
|
|
14484
14578
|
async function installSettingsLocal(targetDir, result) {
|
|
14485
14579
|
const layout = getProviderLayout();
|
|
14486
|
-
const settingsPath =
|
|
14580
|
+
const settingsPath = join7(targetDir, layout.rootDir, "settings.local.json");
|
|
14487
14581
|
const statusLineConfig = {
|
|
14488
14582
|
statusLine: {
|
|
14489
14583
|
type: "command",
|
|
@@ -14540,7 +14634,7 @@ async function install(options) {
|
|
|
14540
14634
|
await installEntryDocWithTracking(options.targetDir, options, result);
|
|
14541
14635
|
if (preservation) {
|
|
14542
14636
|
const layout = getProviderLayout();
|
|
14543
|
-
const rootDir =
|
|
14637
|
+
const rootDir = join7(options.targetDir, layout.rootDir);
|
|
14544
14638
|
const restoration = await restoreCriticalFiles(rootDir, preservation);
|
|
14545
14639
|
if (restoration.restoredFiles.length > 0 || restoration.restoredDirs.length > 0) {
|
|
14546
14640
|
info("install.restored", {
|
|
@@ -14575,12 +14669,35 @@ async function install(options) {
|
|
|
14575
14669
|
function getAllComponents() {
|
|
14576
14670
|
return ["rules", "agents", "skills", "guides", "hooks", "contexts", "ontology"];
|
|
14577
14671
|
}
|
|
14672
|
+
async function installSkillsWithScopeFilter(srcPath, destPath, options) {
|
|
14673
|
+
await ensureDirectory(destPath);
|
|
14674
|
+
const entries = await readdir2(srcPath);
|
|
14675
|
+
for (const entry of entries) {
|
|
14676
|
+
const entrySrcPath = join7(srcPath, entry);
|
|
14677
|
+
if (!(await stat2(entrySrcPath)).isDirectory())
|
|
14678
|
+
continue;
|
|
14679
|
+
const skillMdPath = join7(entrySrcPath, "SKILL.md");
|
|
14680
|
+
if (await fileExists(skillMdPath)) {
|
|
14681
|
+
const content = await fsReadFile(skillMdPath, "utf-8");
|
|
14682
|
+
const scope = getSkillScope(content);
|
|
14683
|
+
if (!shouldInstallSkill(scope)) {
|
|
14684
|
+
debug("install.skill_scope_excluded", { skill: entry, scope });
|
|
14685
|
+
continue;
|
|
14686
|
+
}
|
|
14687
|
+
}
|
|
14688
|
+
await copyDirectory(entrySrcPath, join7(destPath, entry), {
|
|
14689
|
+
overwrite: !!(options.force || options.backup),
|
|
14690
|
+
preserveSymlinks: true,
|
|
14691
|
+
preserveTimestamps: true
|
|
14692
|
+
});
|
|
14693
|
+
}
|
|
14694
|
+
}
|
|
14578
14695
|
async function installComponent(targetDir, component, options) {
|
|
14579
14696
|
if (component === "entry-md") {
|
|
14580
14697
|
return false;
|
|
14581
14698
|
}
|
|
14582
14699
|
const templatePath = getComponentPath(component);
|
|
14583
|
-
const destPath =
|
|
14700
|
+
const destPath = join7(targetDir, templatePath);
|
|
14584
14701
|
const destExists = await fileExists(destPath);
|
|
14585
14702
|
if (destExists && !options.force && !options.backup) {
|
|
14586
14703
|
debug("install.component_skipped", { component });
|
|
@@ -14591,11 +14708,15 @@ async function installComponent(targetDir, component, options) {
|
|
|
14591
14708
|
warn("install.template_not_found", { component, path: srcPath });
|
|
14592
14709
|
return false;
|
|
14593
14710
|
}
|
|
14594
|
-
|
|
14595
|
-
|
|
14596
|
-
|
|
14597
|
-
|
|
14598
|
-
|
|
14711
|
+
if (component === "skills") {
|
|
14712
|
+
await installSkillsWithScopeFilter(srcPath, destPath, options);
|
|
14713
|
+
} else {
|
|
14714
|
+
await copyDirectory(srcPath, destPath, {
|
|
14715
|
+
overwrite: !!(options.force || options.backup),
|
|
14716
|
+
preserveSymlinks: true,
|
|
14717
|
+
preserveTimestamps: true
|
|
14718
|
+
});
|
|
14719
|
+
}
|
|
14599
14720
|
debug("install.component_installed", { component });
|
|
14600
14721
|
return true;
|
|
14601
14722
|
}
|
|
@@ -14608,7 +14729,7 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
14608
14729
|
const layout = getProviderLayout();
|
|
14609
14730
|
const templateFile = getEntryTemplateName(language);
|
|
14610
14731
|
const srcPath = resolveTemplatePath(templateFile);
|
|
14611
|
-
const destPath =
|
|
14732
|
+
const destPath = join7(targetDir, layout.entryFile);
|
|
14612
14733
|
if (!await fileExists(srcPath)) {
|
|
14613
14734
|
warn("install.entry_md_not_found", { language, path: srcPath, entry: layout.entryFile });
|
|
14614
14735
|
return false;
|
|
@@ -14629,7 +14750,7 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
14629
14750
|
}
|
|
14630
14751
|
async function backupExisting(sourcePath, backupDir) {
|
|
14631
14752
|
const name = basename2(sourcePath);
|
|
14632
|
-
const backupPath =
|
|
14753
|
+
const backupPath = join7(backupDir, name);
|
|
14633
14754
|
await rename(sourcePath, backupPath);
|
|
14634
14755
|
return backupPath;
|
|
14635
14756
|
}
|
|
@@ -14638,7 +14759,7 @@ async function checkExistingPaths(targetDir) {
|
|
|
14638
14759
|
const pathsToCheck = [layout.entryFile, layout.rootDir, "guides"];
|
|
14639
14760
|
const existingPaths = [];
|
|
14640
14761
|
for (const relativePath of pathsToCheck) {
|
|
14641
|
-
const fullPath =
|
|
14762
|
+
const fullPath = join7(targetDir, relativePath);
|
|
14642
14763
|
if (await fileExists(fullPath)) {
|
|
14643
14764
|
existingPaths.push(relativePath);
|
|
14644
14765
|
}
|
|
@@ -14652,11 +14773,11 @@ async function backupExistingInstallation(targetDir) {
|
|
|
14652
14773
|
return [];
|
|
14653
14774
|
}
|
|
14654
14775
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
14655
|
-
const backupDir =
|
|
14776
|
+
const backupDir = join7(targetDir, `${layout.backupDirPrefix}${timestamp}`);
|
|
14656
14777
|
await ensureDirectory(backupDir);
|
|
14657
14778
|
const backedUpPaths = [];
|
|
14658
14779
|
for (const relativePath of existingPaths) {
|
|
14659
|
-
const fullPath =
|
|
14780
|
+
const fullPath = join7(targetDir, relativePath);
|
|
14660
14781
|
try {
|
|
14661
14782
|
const backupPath = await backupExisting(fullPath, backupDir);
|
|
14662
14783
|
backedUpPaths.push(backupPath);
|
|
@@ -14673,12 +14794,12 @@ async function backupExistingInstallation(targetDir) {
|
|
|
14673
14794
|
init_fs();
|
|
14674
14795
|
import { execSync as execSync3 } from "node:child_process";
|
|
14675
14796
|
import { writeFile } from "node:fs/promises";
|
|
14676
|
-
import { join as
|
|
14797
|
+
import { join as join8 } from "node:path";
|
|
14677
14798
|
async function generateMCPConfig(targetDir) {
|
|
14678
14799
|
const layout = getProviderLayout();
|
|
14679
|
-
const mcpConfigPath =
|
|
14680
|
-
const ontologyDir =
|
|
14681
|
-
const ontologyExists = await fileExists(
|
|
14800
|
+
const mcpConfigPath = join8(targetDir, ".mcp.json");
|
|
14801
|
+
const ontologyDir = join8(layout.rootDir, "ontology");
|
|
14802
|
+
const ontologyExists = await fileExists(join8(targetDir, ontologyDir));
|
|
14682
14803
|
if (!ontologyExists) {
|
|
14683
14804
|
return;
|
|
14684
14805
|
}
|
|
@@ -14704,8 +14825,8 @@ async function generateMCPConfig(targetDir) {
|
|
|
14704
14825
|
const existingConfigPath = mcpConfigPath;
|
|
14705
14826
|
if (await fileExists(existingConfigPath)) {
|
|
14706
14827
|
try {
|
|
14707
|
-
const { readFile } = await import("node:fs/promises");
|
|
14708
|
-
const existingContent = await
|
|
14828
|
+
const { readFile: readFile2 } = await import("node:fs/promises");
|
|
14829
|
+
const existingContent = await readFile2(existingConfigPath, "utf-8");
|
|
14709
14830
|
const existing = JSON.parse(existingContent);
|
|
14710
14831
|
if (!existing.mcpServers?.["ontology-rag"]) {
|
|
14711
14832
|
existing.mcpServers = existing.mcpServers || {};
|
|
@@ -14735,7 +14856,7 @@ async function checkUvAvailable() {
|
|
|
14735
14856
|
init_fs();
|
|
14736
14857
|
async function checkExistingInstallation(targetDir) {
|
|
14737
14858
|
const layout = getProviderLayout();
|
|
14738
|
-
const rootDir =
|
|
14859
|
+
const rootDir = join9(targetDir, layout.rootDir);
|
|
14739
14860
|
return fileExists(rootDir);
|
|
14740
14861
|
}
|
|
14741
14862
|
var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
@@ -14749,13 +14870,13 @@ var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
|
14749
14870
|
function componentToPath(targetDir, component) {
|
|
14750
14871
|
if (component === "entry-md") {
|
|
14751
14872
|
const layout = getProviderLayout();
|
|
14752
|
-
return
|
|
14873
|
+
return join9(targetDir, layout.entryFile);
|
|
14753
14874
|
}
|
|
14754
14875
|
if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
|
|
14755
14876
|
const layout = getProviderLayout();
|
|
14756
|
-
return
|
|
14877
|
+
return join9(targetDir, layout.rootDir, component);
|
|
14757
14878
|
}
|
|
14758
|
-
return
|
|
14879
|
+
return join9(targetDir, component);
|
|
14759
14880
|
}
|
|
14760
14881
|
function buildInstalledPaths(targetDir, components) {
|
|
14761
14882
|
return components.map((component) => componentToPath(targetDir, component));
|
|
@@ -14835,7 +14956,7 @@ async function initCommand(options) {
|
|
|
14835
14956
|
}
|
|
14836
14957
|
|
|
14837
14958
|
// src/cli/list.ts
|
|
14838
|
-
import { basename as basename3, dirname as dirname3, join as
|
|
14959
|
+
import { basename as basename3, dirname as dirname3, join as join10, relative as relative3 } from "node:path";
|
|
14839
14960
|
init_fs();
|
|
14840
14961
|
var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
|
|
14841
14962
|
function parseKeyValue(line) {
|
|
@@ -14900,12 +15021,12 @@ function extractAgentTypeFromFilename(filename) {
|
|
|
14900
15021
|
return prefixMap[prefix] || "unknown";
|
|
14901
15022
|
}
|
|
14902
15023
|
function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
|
|
14903
|
-
const relativePath = relative3(
|
|
15024
|
+
const relativePath = relative3(join10(baseDir, rootDir, "skills"), skillPath);
|
|
14904
15025
|
const parts = relativePath.split("/").filter(Boolean);
|
|
14905
15026
|
return parts[0] || "unknown";
|
|
14906
15027
|
}
|
|
14907
15028
|
function extractGuideCategoryFromPath(guidePath, baseDir) {
|
|
14908
|
-
const relativePath = relative3(
|
|
15029
|
+
const relativePath = relative3(join10(baseDir, "guides"), guidePath);
|
|
14909
15030
|
const parts = relativePath.split("/").filter(Boolean);
|
|
14910
15031
|
return parts[0] || "unknown";
|
|
14911
15032
|
}
|
|
@@ -14999,7 +15120,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
|
|
|
14999
15120
|
}
|
|
15000
15121
|
}
|
|
15001
15122
|
async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
15002
|
-
const agentsDir =
|
|
15123
|
+
const agentsDir = join10(targetDir, rootDir, "agents");
|
|
15003
15124
|
if (!await fileExists(agentsDir))
|
|
15004
15125
|
return [];
|
|
15005
15126
|
try {
|
|
@@ -15027,7 +15148,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
15027
15148
|
}
|
|
15028
15149
|
}
|
|
15029
15150
|
async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
15030
|
-
const skillsDir =
|
|
15151
|
+
const skillsDir = join10(targetDir, rootDir, "skills");
|
|
15031
15152
|
if (!await fileExists(skillsDir))
|
|
15032
15153
|
return [];
|
|
15033
15154
|
try {
|
|
@@ -15037,7 +15158,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
15037
15158
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
15038
15159
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
15039
15160
|
const skillDir = dirname3(skillMdPath);
|
|
15040
|
-
const indexYamlPath =
|
|
15161
|
+
const indexYamlPath = join10(skillDir, "index.yaml");
|
|
15041
15162
|
const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
|
|
15042
15163
|
const relativePath = relative3(targetDir, skillDir);
|
|
15043
15164
|
return {
|
|
@@ -15056,7 +15177,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
15056
15177
|
}
|
|
15057
15178
|
}
|
|
15058
15179
|
async function getGuides(targetDir, config) {
|
|
15059
|
-
const guidesDir =
|
|
15180
|
+
const guidesDir = join10(targetDir, "guides");
|
|
15060
15181
|
if (!await fileExists(guidesDir))
|
|
15061
15182
|
return [];
|
|
15062
15183
|
try {
|
|
@@ -15083,7 +15204,7 @@ async function getGuides(targetDir, config) {
|
|
|
15083
15204
|
}
|
|
15084
15205
|
var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
|
|
15085
15206
|
async function getRules(targetDir, rootDir = ".claude", config) {
|
|
15086
|
-
const rulesDir =
|
|
15207
|
+
const rulesDir = join10(targetDir, rootDir, "rules");
|
|
15087
15208
|
if (!await fileExists(rulesDir))
|
|
15088
15209
|
return [];
|
|
15089
15210
|
try {
|
|
@@ -15155,7 +15276,7 @@ function formatAsJson(components) {
|
|
|
15155
15276
|
console.log(JSON.stringify(components, null, 2));
|
|
15156
15277
|
}
|
|
15157
15278
|
async function getHooks(targetDir, rootDir = ".claude") {
|
|
15158
|
-
const hooksDir =
|
|
15279
|
+
const hooksDir = join10(targetDir, rootDir, "hooks");
|
|
15159
15280
|
if (!await fileExists(hooksDir))
|
|
15160
15281
|
return [];
|
|
15161
15282
|
try {
|
|
@@ -15173,7 +15294,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
15173
15294
|
}
|
|
15174
15295
|
}
|
|
15175
15296
|
async function getContexts(targetDir, rootDir = ".claude") {
|
|
15176
|
-
const contextsDir =
|
|
15297
|
+
const contextsDir = join10(targetDir, rootDir, "contexts");
|
|
15177
15298
|
if (!await fileExists(contextsDir))
|
|
15178
15299
|
return [];
|
|
15179
15300
|
try {
|
|
@@ -15563,7 +15684,7 @@ async function securityCommand(_options = {}) {
|
|
|
15563
15684
|
|
|
15564
15685
|
// src/core/updater.ts
|
|
15565
15686
|
init_fs();
|
|
15566
|
-
import { join as
|
|
15687
|
+
import { join as join11 } from "node:path";
|
|
15567
15688
|
|
|
15568
15689
|
// src/core/entry-merger.ts
|
|
15569
15690
|
var MANAGED_START = "<!-- omcustom:start -->";
|
|
@@ -15812,7 +15933,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
|
|
|
15812
15933
|
}
|
|
15813
15934
|
async function updateEntryDoc(targetDir, config, options) {
|
|
15814
15935
|
const layout = getProviderLayout();
|
|
15815
|
-
const entryPath =
|
|
15936
|
+
const entryPath = join11(targetDir, layout.entryFile);
|
|
15816
15937
|
const templateName = getEntryTemplateName2(config.language);
|
|
15817
15938
|
const templatePath = resolveTemplatePath(templateName);
|
|
15818
15939
|
if (!await fileExists(templatePath)) {
|
|
@@ -15958,7 +16079,7 @@ async function collectProtectedSkipPaths(srcPath, destPath, componentPath, force
|
|
|
15958
16079
|
}
|
|
15959
16080
|
const protectedRelative = await findProtectedFilesInDir(srcPath, componentPath);
|
|
15960
16081
|
const path3 = await import("node:path");
|
|
15961
|
-
const skipPaths = protectedRelative.map((p) => path3.relative(destPath,
|
|
16082
|
+
const skipPaths = protectedRelative.map((p) => path3.relative(destPath, join11(destPath, p)));
|
|
15962
16083
|
return { skipPaths, warnedPaths: protectedRelative };
|
|
15963
16084
|
}
|
|
15964
16085
|
function isEntryProtected(relPath, componentRelativePrefix) {
|
|
@@ -15999,7 +16120,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
15999
16120
|
const preservedFiles = [];
|
|
16000
16121
|
const componentPath = getComponentPath2(component);
|
|
16001
16122
|
const srcPath = resolveTemplatePath(componentPath);
|
|
16002
|
-
const destPath =
|
|
16123
|
+
const destPath = join11(targetDir, componentPath);
|
|
16003
16124
|
const customComponents = config.customComponents || [];
|
|
16004
16125
|
const skipPaths = [];
|
|
16005
16126
|
if (customizations && !options.forceOverwriteAll) {
|
|
@@ -16030,7 +16151,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
16030
16151
|
}
|
|
16031
16152
|
skipPaths.push(...protectedSkipPaths);
|
|
16032
16153
|
const path3 = await import("node:path");
|
|
16033
|
-
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath,
|
|
16154
|
+
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join11(targetDir, p)));
|
|
16034
16155
|
const uniqueSkipPaths = [...new Set(normalizedSkipPaths)];
|
|
16035
16156
|
await copyDirectory(srcPath, destPath, {
|
|
16036
16157
|
overwrite: true,
|
|
@@ -16052,12 +16173,12 @@ async function syncRootLevelFiles(targetDir, options) {
|
|
|
16052
16173
|
const layout = getProviderLayout();
|
|
16053
16174
|
const synced = [];
|
|
16054
16175
|
for (const fileName of ROOT_LEVEL_FILES) {
|
|
16055
|
-
const srcPath = resolveTemplatePath(
|
|
16176
|
+
const srcPath = resolveTemplatePath(join11(layout.rootDir, fileName));
|
|
16056
16177
|
if (!await fileExists(srcPath)) {
|
|
16057
16178
|
continue;
|
|
16058
16179
|
}
|
|
16059
|
-
const destPath =
|
|
16060
|
-
await ensureDirectory(
|
|
16180
|
+
const destPath = join11(targetDir, layout.rootDir, fileName);
|
|
16181
|
+
await ensureDirectory(join11(destPath, ".."));
|
|
16061
16182
|
await fs3.copyFile(srcPath, destPath);
|
|
16062
16183
|
if (fileName.endsWith(".sh")) {
|
|
16063
16184
|
await fs3.chmod(destPath, 493);
|
|
@@ -16092,7 +16213,7 @@ async function removeDeprecatedFiles(targetDir, options) {
|
|
|
16092
16213
|
});
|
|
16093
16214
|
continue;
|
|
16094
16215
|
}
|
|
16095
|
-
const fullPath =
|
|
16216
|
+
const fullPath = join11(targetDir, entry.path);
|
|
16096
16217
|
if (await fileExists(fullPath)) {
|
|
16097
16218
|
await fs3.unlink(fullPath);
|
|
16098
16219
|
removed.push(entry.path);
|
|
@@ -16116,26 +16237,26 @@ function getComponentPath2(component) {
|
|
|
16116
16237
|
}
|
|
16117
16238
|
async function backupInstallation(targetDir) {
|
|
16118
16239
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
16119
|
-
const backupDir =
|
|
16240
|
+
const backupDir = join11(targetDir, `.omcustom-backup-${timestamp}`);
|
|
16120
16241
|
const fs3 = await import("node:fs/promises");
|
|
16121
16242
|
await ensureDirectory(backupDir);
|
|
16122
16243
|
const layout = getProviderLayout();
|
|
16123
16244
|
const dirsToBackup = [layout.rootDir, "guides"];
|
|
16124
16245
|
for (const dir2 of dirsToBackup) {
|
|
16125
|
-
const srcPath =
|
|
16246
|
+
const srcPath = join11(targetDir, dir2);
|
|
16126
16247
|
if (await fileExists(srcPath)) {
|
|
16127
|
-
const destPath =
|
|
16248
|
+
const destPath = join11(backupDir, dir2);
|
|
16128
16249
|
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
16129
16250
|
}
|
|
16130
16251
|
}
|
|
16131
|
-
const entryPath =
|
|
16252
|
+
const entryPath = join11(targetDir, layout.entryFile);
|
|
16132
16253
|
if (await fileExists(entryPath)) {
|
|
16133
|
-
await fs3.copyFile(entryPath,
|
|
16254
|
+
await fs3.copyFile(entryPath, join11(backupDir, layout.entryFile));
|
|
16134
16255
|
}
|
|
16135
16256
|
return backupDir;
|
|
16136
16257
|
}
|
|
16137
16258
|
async function loadCustomizationManifest(targetDir) {
|
|
16138
|
-
const manifestPath =
|
|
16259
|
+
const manifestPath = join11(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
16139
16260
|
if (await fileExists(manifestPath)) {
|
|
16140
16261
|
return readJsonFile(manifestPath);
|
|
16141
16262
|
}
|