oh-my-customcode 0.79.0 → 0.79.2
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/dist/cli/index.js +635 -346
- package/dist/index.js +193 -41
- package/package.json +1 -1
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +3 -1
- package/templates/manifest.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2140,6 +2140,234 @@ var require_commander = __commonJS((exports) => {
|
|
|
2140
2140
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
2141
2141
|
});
|
|
2142
2142
|
|
|
2143
|
+
// src/core/registry.ts
|
|
2144
|
+
var exports_registry = {};
|
|
2145
|
+
__export(exports_registry, {
|
|
2146
|
+
unregisterProject: () => unregisterProject,
|
|
2147
|
+
registryToList: () => registryToList,
|
|
2148
|
+
registerProject: () => registerProject,
|
|
2149
|
+
readRegistry: () => readRegistry,
|
|
2150
|
+
migrateFromLockfiles: () => migrateFromLockfiles,
|
|
2151
|
+
_setRegistryDirForTesting: () => _setRegistryDirForTesting
|
|
2152
|
+
});
|
|
2153
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2154
|
+
import { homedir } from "node:os";
|
|
2155
|
+
import { basename, join, resolve } from "node:path";
|
|
2156
|
+
function _setRegistryDirForTesting(dir) {
|
|
2157
|
+
_registryDirOverride = dir;
|
|
2158
|
+
}
|
|
2159
|
+
function registryDir() {
|
|
2160
|
+
if (_registryDirOverride !== undefined)
|
|
2161
|
+
return _registryDirOverride;
|
|
2162
|
+
const home = process.env.HOME ?? homedir();
|
|
2163
|
+
return join(home, ".oh-my-customcode");
|
|
2164
|
+
}
|
|
2165
|
+
function registryPath() {
|
|
2166
|
+
return join(registryDir(), "projects.json");
|
|
2167
|
+
}
|
|
2168
|
+
async function readRegistryRaw() {
|
|
2169
|
+
try {
|
|
2170
|
+
const content = await readFile(registryPath(), "utf-8");
|
|
2171
|
+
const parsed = JSON.parse(content);
|
|
2172
|
+
if (typeof parsed === "object" && parsed !== null && "projects" in parsed && typeof parsed.projects === "object" && parsed.projects !== null) {
|
|
2173
|
+
return parsed;
|
|
2174
|
+
}
|
|
2175
|
+
return { ...EMPTY_REGISTRY };
|
|
2176
|
+
} catch {
|
|
2177
|
+
return { ...EMPTY_REGISTRY };
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
async function writeRegistry(registry) {
|
|
2181
|
+
const dir = registryDir();
|
|
2182
|
+
await mkdir(dir, { recursive: true });
|
|
2183
|
+
await writeFile(registryPath(), JSON.stringify(registry, null, 2), "utf-8");
|
|
2184
|
+
}
|
|
2185
|
+
async function readRegistry() {
|
|
2186
|
+
return readRegistryRaw();
|
|
2187
|
+
}
|
|
2188
|
+
async function registerProject(projectPath, version) {
|
|
2189
|
+
const normalizedPath = resolve(projectPath);
|
|
2190
|
+
const registry = await readRegistryRaw();
|
|
2191
|
+
const existing = registry.projects[normalizedPath];
|
|
2192
|
+
const now = new Date().toISOString();
|
|
2193
|
+
registry.projects[normalizedPath] = {
|
|
2194
|
+
version,
|
|
2195
|
+
installedAt: existing?.installedAt ?? now,
|
|
2196
|
+
updatedAt: now
|
|
2197
|
+
};
|
|
2198
|
+
await writeRegistry(registry);
|
|
2199
|
+
}
|
|
2200
|
+
async function unregisterProject(projectPath) {
|
|
2201
|
+
const normalizedPath = resolve(projectPath);
|
|
2202
|
+
const registry = await readRegistryRaw();
|
|
2203
|
+
if (!(normalizedPath in registry.projects))
|
|
2204
|
+
return;
|
|
2205
|
+
delete registry.projects[normalizedPath];
|
|
2206
|
+
await writeRegistry(registry);
|
|
2207
|
+
}
|
|
2208
|
+
async function parseLockFile(lockPath) {
|
|
2209
|
+
try {
|
|
2210
|
+
const content = await readFile(lockPath, "utf-8");
|
|
2211
|
+
const lock = JSON.parse(content);
|
|
2212
|
+
const version = typeof lock.version === "string" ? lock.version : typeof lock.templateVersion === "string" ? lock.templateVersion : "0.0.0";
|
|
2213
|
+
const now = new Date().toISOString();
|
|
2214
|
+
return {
|
|
2215
|
+
version,
|
|
2216
|
+
installedAt: typeof lock.installedAt === "string" ? lock.installedAt : now,
|
|
2217
|
+
updatedAt: typeof lock.updatedAt === "string" ? lock.updatedAt : now
|
|
2218
|
+
};
|
|
2219
|
+
} catch {
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
async function migrateFromLockfiles(searchDirs) {
|
|
2224
|
+
const { readdir } = await import("node:fs/promises");
|
|
2225
|
+
const MAX_DEPTH = 3;
|
|
2226
|
+
const discovered = new Map;
|
|
2227
|
+
async function scan(dir, depth) {
|
|
2228
|
+
if (depth > MAX_DEPTH || discovered.has(dir))
|
|
2229
|
+
return;
|
|
2230
|
+
let entries;
|
|
2231
|
+
try {
|
|
2232
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
2233
|
+
} catch {
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
const entry = await parseLockFile(join(dir, ".omcustom.lock.json"));
|
|
2237
|
+
if (entry !== null) {
|
|
2238
|
+
discovered.set(resolve(dir), entry);
|
|
2239
|
+
return;
|
|
2240
|
+
}
|
|
2241
|
+
if (depth < MAX_DEPTH) {
|
|
2242
|
+
const subdirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SCAN_SKIP_DIRS.has(e.name));
|
|
2243
|
+
await Promise.all(subdirs.map((sub) => scan(join(dir, sub.name), depth + 1)));
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
await Promise.all(searchDirs.map((dir) => scan(dir, 0).catch(() => {})));
|
|
2247
|
+
if (discovered.size === 0)
|
|
2248
|
+
return 0;
|
|
2249
|
+
const registry = await readRegistryRaw();
|
|
2250
|
+
let imported = 0;
|
|
2251
|
+
for (const [projectPath, entry] of discovered) {
|
|
2252
|
+
if (!(projectPath in registry.projects)) {
|
|
2253
|
+
registry.projects[projectPath] = entry;
|
|
2254
|
+
imported++;
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
if (imported > 0) {
|
|
2258
|
+
await writeRegistry(registry);
|
|
2259
|
+
}
|
|
2260
|
+
return imported;
|
|
2261
|
+
}
|
|
2262
|
+
function registryToList(registry) {
|
|
2263
|
+
return Object.entries(registry.projects).map(([path, entry]) => ({
|
|
2264
|
+
name: basename(path),
|
|
2265
|
+
path,
|
|
2266
|
+
version: entry.version,
|
|
2267
|
+
installedAt: entry.installedAt,
|
|
2268
|
+
updatedAt: entry.updatedAt
|
|
2269
|
+
}));
|
|
2270
|
+
}
|
|
2271
|
+
var _registryDirOverride, EMPTY_REGISTRY, SCAN_SKIP_DIRS;
|
|
2272
|
+
var init_registry = __esm(() => {
|
|
2273
|
+
EMPTY_REGISTRY = { projects: {} };
|
|
2274
|
+
SCAN_SKIP_DIRS = new Set(["node_modules", "dist", "build", ".git"]);
|
|
2275
|
+
});
|
|
2276
|
+
|
|
2277
|
+
// package.json
|
|
2278
|
+
var package_default;
|
|
2279
|
+
var init_package = __esm(() => {
|
|
2280
|
+
package_default = {
|
|
2281
|
+
name: "oh-my-customcode",
|
|
2282
|
+
workspaces: [
|
|
2283
|
+
"packages/*"
|
|
2284
|
+
],
|
|
2285
|
+
version: "0.79.2",
|
|
2286
|
+
description: "Batteries-included agent harness for Claude Code",
|
|
2287
|
+
type: "module",
|
|
2288
|
+
bin: {
|
|
2289
|
+
omcustom: "./dist/cli/index.js"
|
|
2290
|
+
},
|
|
2291
|
+
main: "./dist/index.js",
|
|
2292
|
+
types: "./dist/index.d.ts",
|
|
2293
|
+
exports: {
|
|
2294
|
+
".": {
|
|
2295
|
+
import: "./dist/index.js",
|
|
2296
|
+
types: "./dist/index.d.ts"
|
|
2297
|
+
}
|
|
2298
|
+
},
|
|
2299
|
+
files: [
|
|
2300
|
+
"dist",
|
|
2301
|
+
"templates"
|
|
2302
|
+
],
|
|
2303
|
+
publishConfig: {
|
|
2304
|
+
access: "public",
|
|
2305
|
+
registry: "https://registry.npmjs.org/"
|
|
2306
|
+
},
|
|
2307
|
+
scripts: {
|
|
2308
|
+
dev: "bun run src/cli/index.ts",
|
|
2309
|
+
build: "bun build src/cli/index.ts --outdir dist/cli --target node && bun build src/index.ts --outdir dist --target node && bun run scripts/sync-source-lockfile.ts",
|
|
2310
|
+
test: "bun test tests/ packages/eval-core/",
|
|
2311
|
+
"test:unit": "bun test tests/unit",
|
|
2312
|
+
"test:integration": "bun test tests/integration",
|
|
2313
|
+
"test:e2e": "bun test tests/e2e",
|
|
2314
|
+
"test:coverage": "bun test --coverage",
|
|
2315
|
+
lint: "biome check src/ tests/ scripts/",
|
|
2316
|
+
"lint:fix": "biome check --write src/ tests/ scripts/",
|
|
2317
|
+
format: "biome format --write src/ tests/ scripts/",
|
|
2318
|
+
typecheck: "tsc --noEmit",
|
|
2319
|
+
"docs:dev": "vitepress dev docs",
|
|
2320
|
+
"docs:build": "vitepress build docs",
|
|
2321
|
+
prepare: "sh scripts/setup-hooks.sh || true",
|
|
2322
|
+
"setup:hooks": "sh scripts/setup-hooks.sh",
|
|
2323
|
+
prepublishOnly: "bun run build && bun run test"
|
|
2324
|
+
},
|
|
2325
|
+
dependencies: {
|
|
2326
|
+
"@clack/prompts": "^1.1.0",
|
|
2327
|
+
"@inquirer/prompts": "^8.3.2",
|
|
2328
|
+
commander: "^14.0.2",
|
|
2329
|
+
i18next: "^26.0.2",
|
|
2330
|
+
yaml: "^2.8.2"
|
|
2331
|
+
},
|
|
2332
|
+
devDependencies: {
|
|
2333
|
+
"@anthropic-ai/sdk": "^0.82.0",
|
|
2334
|
+
"@biomejs/biome": "^2.3.12",
|
|
2335
|
+
"@types/bun": "^1.3.6",
|
|
2336
|
+
"@types/js-yaml": "^4.0.9",
|
|
2337
|
+
"@types/nodemailer": "^8.0.0",
|
|
2338
|
+
"js-yaml": "^4.1.0",
|
|
2339
|
+
nodemailer: "^8.0.1",
|
|
2340
|
+
typescript: "^6.0.2",
|
|
2341
|
+
vitepress: "^1.6.4"
|
|
2342
|
+
},
|
|
2343
|
+
keywords: [
|
|
2344
|
+
"claude",
|
|
2345
|
+
"claude-code",
|
|
2346
|
+
"openai",
|
|
2347
|
+
"ai",
|
|
2348
|
+
"agent",
|
|
2349
|
+
"cli"
|
|
2350
|
+
],
|
|
2351
|
+
author: "baekenough",
|
|
2352
|
+
license: "MIT",
|
|
2353
|
+
repository: {
|
|
2354
|
+
type: "git",
|
|
2355
|
+
url: "git+https://github.com/baekenough/oh-my-customcode.git"
|
|
2356
|
+
},
|
|
2357
|
+
bugs: {
|
|
2358
|
+
url: "https://github.com/baekenough/oh-my-customcode/issues"
|
|
2359
|
+
},
|
|
2360
|
+
homepage: "https://github.com/baekenough/oh-my-customcode#readme",
|
|
2361
|
+
engines: {
|
|
2362
|
+
node: ">=18.0.0"
|
|
2363
|
+
},
|
|
2364
|
+
overrides: {
|
|
2365
|
+
rollup: "^4.59.0",
|
|
2366
|
+
esbuild: "^0.25.0"
|
|
2367
|
+
}
|
|
2368
|
+
};
|
|
2369
|
+
});
|
|
2370
|
+
|
|
2143
2371
|
// node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
|
|
2144
2372
|
var require_identity = __commonJS((exports) => {
|
|
2145
2373
|
var ALIAS = Symbol.for("yaml.alias");
|
|
@@ -9068,7 +9296,7 @@ __export(exports_fs, {
|
|
|
9068
9296
|
copyDirectory: () => copyDirectory,
|
|
9069
9297
|
calculateChecksum: () => calculateChecksum
|
|
9070
9298
|
});
|
|
9071
|
-
import { dirname as dirname2, isAbsolute, join as
|
|
9299
|
+
import { dirname as dirname2, isAbsolute, join as join3, relative, resolve as resolve2, sep } from "node:path";
|
|
9072
9300
|
import { fileURLToPath } from "node:url";
|
|
9073
9301
|
function validatePreserveFilePath(filePath, projectRoot) {
|
|
9074
9302
|
if (!filePath || filePath.trim() === "") {
|
|
@@ -9083,7 +9311,7 @@ function validatePreserveFilePath(filePath, projectRoot) {
|
|
|
9083
9311
|
reason: "Absolute paths are not allowed"
|
|
9084
9312
|
};
|
|
9085
9313
|
}
|
|
9086
|
-
const resolvedPath =
|
|
9314
|
+
const resolvedPath = resolve2(projectRoot, filePath);
|
|
9087
9315
|
const relativePath = relative(projectRoot, resolvedPath);
|
|
9088
9316
|
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
9089
9317
|
return {
|
|
@@ -9229,11 +9457,11 @@ async function remove(path) {
|
|
|
9229
9457
|
function getPackageRoot() {
|
|
9230
9458
|
const currentFile = fileURLToPath(import.meta.url);
|
|
9231
9459
|
const currentDir = dirname2(currentFile);
|
|
9232
|
-
return
|
|
9460
|
+
return resolve2(currentDir, "..", "..");
|
|
9233
9461
|
}
|
|
9234
9462
|
function resolveTemplatePath(relativePath) {
|
|
9235
9463
|
const packageRoot = getPackageRoot();
|
|
9236
|
-
return
|
|
9464
|
+
return join3(packageRoot, "templates", relativePath);
|
|
9237
9465
|
}
|
|
9238
9466
|
async function listFiles(dir2, options = {}) {
|
|
9239
9467
|
const fs = await import("node:fs/promises");
|
|
@@ -9313,104 +9541,10 @@ function isAbsolutePath(inputPath) {
|
|
|
9313
9541
|
return isAbsolute(inputPath);
|
|
9314
9542
|
}
|
|
9315
9543
|
function resolvePath(...paths) {
|
|
9316
|
-
return
|
|
9544
|
+
return resolve2(...paths);
|
|
9317
9545
|
}
|
|
9318
9546
|
var init_fs = () => {};
|
|
9319
9547
|
|
|
9320
|
-
// package.json
|
|
9321
|
-
var package_default;
|
|
9322
|
-
var init_package = __esm(() => {
|
|
9323
|
-
package_default = {
|
|
9324
|
-
name: "oh-my-customcode",
|
|
9325
|
-
workspaces: [
|
|
9326
|
-
"packages/*"
|
|
9327
|
-
],
|
|
9328
|
-
version: "0.79.0",
|
|
9329
|
-
description: "Batteries-included agent harness for Claude Code",
|
|
9330
|
-
type: "module",
|
|
9331
|
-
bin: {
|
|
9332
|
-
omcustom: "./dist/cli/index.js"
|
|
9333
|
-
},
|
|
9334
|
-
main: "./dist/index.js",
|
|
9335
|
-
types: "./dist/index.d.ts",
|
|
9336
|
-
exports: {
|
|
9337
|
-
".": {
|
|
9338
|
-
import: "./dist/index.js",
|
|
9339
|
-
types: "./dist/index.d.ts"
|
|
9340
|
-
}
|
|
9341
|
-
},
|
|
9342
|
-
files: [
|
|
9343
|
-
"dist",
|
|
9344
|
-
"templates"
|
|
9345
|
-
],
|
|
9346
|
-
publishConfig: {
|
|
9347
|
-
access: "public",
|
|
9348
|
-
registry: "https://registry.npmjs.org/"
|
|
9349
|
-
},
|
|
9350
|
-
scripts: {
|
|
9351
|
-
dev: "bun run src/cli/index.ts",
|
|
9352
|
-
build: "bun build src/cli/index.ts --outdir dist/cli --target node && bun build src/index.ts --outdir dist --target node && bun run scripts/sync-source-lockfile.ts",
|
|
9353
|
-
test: "bun test tests/ packages/eval-core/",
|
|
9354
|
-
"test:unit": "bun test tests/unit",
|
|
9355
|
-
"test:integration": "bun test tests/integration",
|
|
9356
|
-
"test:e2e": "bun test tests/e2e",
|
|
9357
|
-
"test:coverage": "bun test --coverage",
|
|
9358
|
-
lint: "biome check src/ tests/ scripts/",
|
|
9359
|
-
"lint:fix": "biome check --write src/ tests/ scripts/",
|
|
9360
|
-
format: "biome format --write src/ tests/ scripts/",
|
|
9361
|
-
typecheck: "tsc --noEmit",
|
|
9362
|
-
"docs:dev": "vitepress dev docs",
|
|
9363
|
-
"docs:build": "vitepress build docs",
|
|
9364
|
-
prepare: "sh scripts/setup-hooks.sh || true",
|
|
9365
|
-
"setup:hooks": "sh scripts/setup-hooks.sh",
|
|
9366
|
-
prepublishOnly: "bun run build && bun run test"
|
|
9367
|
-
},
|
|
9368
|
-
dependencies: {
|
|
9369
|
-
"@clack/prompts": "^1.1.0",
|
|
9370
|
-
"@inquirer/prompts": "^8.3.2",
|
|
9371
|
-
commander: "^14.0.2",
|
|
9372
|
-
i18next: "^26.0.2",
|
|
9373
|
-
yaml: "^2.8.2"
|
|
9374
|
-
},
|
|
9375
|
-
devDependencies: {
|
|
9376
|
-
"@anthropic-ai/sdk": "^0.82.0",
|
|
9377
|
-
"@biomejs/biome": "^2.3.12",
|
|
9378
|
-
"@types/bun": "^1.3.6",
|
|
9379
|
-
"@types/js-yaml": "^4.0.9",
|
|
9380
|
-
"@types/nodemailer": "^8.0.0",
|
|
9381
|
-
"js-yaml": "^4.1.0",
|
|
9382
|
-
nodemailer: "^8.0.1",
|
|
9383
|
-
typescript: "^6.0.2",
|
|
9384
|
-
vitepress: "^1.6.4"
|
|
9385
|
-
},
|
|
9386
|
-
keywords: [
|
|
9387
|
-
"claude",
|
|
9388
|
-
"claude-code",
|
|
9389
|
-
"openai",
|
|
9390
|
-
"ai",
|
|
9391
|
-
"agent",
|
|
9392
|
-
"cli"
|
|
9393
|
-
],
|
|
9394
|
-
author: "baekenough",
|
|
9395
|
-
license: "MIT",
|
|
9396
|
-
repository: {
|
|
9397
|
-
type: "git",
|
|
9398
|
-
url: "git+https://github.com/baekenough/oh-my-customcode.git"
|
|
9399
|
-
},
|
|
9400
|
-
bugs: {
|
|
9401
|
-
url: "https://github.com/baekenough/oh-my-customcode/issues"
|
|
9402
|
-
},
|
|
9403
|
-
homepage: "https://github.com/baekenough/oh-my-customcode#readme",
|
|
9404
|
-
engines: {
|
|
9405
|
-
node: ">=18.0.0"
|
|
9406
|
-
},
|
|
9407
|
-
overrides: {
|
|
9408
|
-
rollup: "^4.59.0",
|
|
9409
|
-
esbuild: "^0.25.0"
|
|
9410
|
-
}
|
|
9411
|
-
};
|
|
9412
|
-
});
|
|
9413
|
-
|
|
9414
9548
|
// src/cli/projects.ts
|
|
9415
9549
|
var exports_projects = {};
|
|
9416
9550
|
__export(exports_projects, {
|
|
@@ -9420,10 +9554,10 @@ __export(exports_projects, {
|
|
|
9420
9554
|
findProjects: () => findProjects,
|
|
9421
9555
|
default: () => projects_default
|
|
9422
9556
|
});
|
|
9423
|
-
import { homedir as
|
|
9424
|
-
import { basename as
|
|
9557
|
+
import { homedir as homedir3 } from "node:os";
|
|
9558
|
+
import { basename as basename4, join as join10, sep as sep2 } from "node:path";
|
|
9425
9559
|
async function readLockFile(projectDir) {
|
|
9426
|
-
const lockFilePath =
|
|
9560
|
+
const lockFilePath = join10(projectDir, ".omcustom.lock.json");
|
|
9427
9561
|
try {
|
|
9428
9562
|
const fs2 = await import("node:fs/promises");
|
|
9429
9563
|
const content = await fs2.readFile(lockFilePath, "utf-8");
|
|
@@ -9432,20 +9566,6 @@ async function readLockFile(projectDir) {
|
|
|
9432
9566
|
return null;
|
|
9433
9567
|
}
|
|
9434
9568
|
}
|
|
9435
|
-
async function hasOmcustomMarkers(dir2) {
|
|
9436
|
-
const fs2 = await import("node:fs/promises");
|
|
9437
|
-
const agentsDir = join9(dir2, ".claude", "agents");
|
|
9438
|
-
const skillsDir = join9(dir2, ".claude", "skills");
|
|
9439
|
-
try {
|
|
9440
|
-
const [agentsStat, skillsStat] = await Promise.allSettled([
|
|
9441
|
-
fs2.stat(agentsDir),
|
|
9442
|
-
fs2.stat(skillsDir)
|
|
9443
|
-
]);
|
|
9444
|
-
return agentsStat.status === "fulfilled" && agentsStat.value.isDirectory() && skillsStat.status === "fulfilled" && skillsStat.value.isDirectory();
|
|
9445
|
-
} catch {
|
|
9446
|
-
return false;
|
|
9447
|
-
}
|
|
9448
|
-
}
|
|
9449
9569
|
function computeStatus(version, currentVersion) {
|
|
9450
9570
|
if (!version)
|
|
9451
9571
|
return "unknown";
|
|
@@ -9461,48 +9581,6 @@ function computeStatus(version, currentVersion) {
|
|
|
9461
9581
|
}
|
|
9462
9582
|
return "latest";
|
|
9463
9583
|
}
|
|
9464
|
-
async function searchDirectory(dir2, depth, results, currentVersion, seen) {
|
|
9465
|
-
if (depth > MAX_SEARCH_DEPTH || seen.has(dir2))
|
|
9466
|
-
return;
|
|
9467
|
-
seen.add(dir2);
|
|
9468
|
-
const fs2 = await import("node:fs/promises");
|
|
9469
|
-
let entries;
|
|
9470
|
-
try {
|
|
9471
|
-
entries = await fs2.readdir(dir2, { withFileTypes: true });
|
|
9472
|
-
} catch {
|
|
9473
|
-
return;
|
|
9474
|
-
}
|
|
9475
|
-
const lockFile = await readLockFile(dir2);
|
|
9476
|
-
if (lockFile) {
|
|
9477
|
-
const version = lockFile.version || lockFile.templateVersion || null;
|
|
9478
|
-
results.push({
|
|
9479
|
-
name: basename3(dir2),
|
|
9480
|
-
path: dir2,
|
|
9481
|
-
version,
|
|
9482
|
-
installedAt: lockFile.installedAt || null,
|
|
9483
|
-
updatedAt: lockFile.updatedAt || null,
|
|
9484
|
-
status: computeStatus(version, currentVersion),
|
|
9485
|
-
detectionMethod: "lockfile"
|
|
9486
|
-
});
|
|
9487
|
-
return;
|
|
9488
|
-
}
|
|
9489
|
-
if (await hasOmcustomMarkers(dir2)) {
|
|
9490
|
-
results.push({
|
|
9491
|
-
name: basename3(dir2),
|
|
9492
|
-
path: dir2,
|
|
9493
|
-
version: null,
|
|
9494
|
-
installedAt: null,
|
|
9495
|
-
updatedAt: null,
|
|
9496
|
-
status: "unknown",
|
|
9497
|
-
detectionMethod: "directory"
|
|
9498
|
-
});
|
|
9499
|
-
return;
|
|
9500
|
-
}
|
|
9501
|
-
if (depth < MAX_SEARCH_DEPTH) {
|
|
9502
|
-
const subdirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist" && e.name !== "build" && e.name !== ".git");
|
|
9503
|
-
await Promise.all(subdirs.map((subdir) => searchDirectory(join9(dir2, subdir.name), depth + 1, results, currentVersion, seen)));
|
|
9504
|
-
}
|
|
9505
|
-
}
|
|
9506
9584
|
async function getTemplateVersion() {
|
|
9507
9585
|
const manifestPath = resolveTemplatePath("manifest.json");
|
|
9508
9586
|
if (await fileExists(manifestPath)) {
|
|
@@ -9513,13 +9591,74 @@ async function getTemplateVersion() {
|
|
|
9513
9591
|
}
|
|
9514
9592
|
async function findProjects(options = {}) {
|
|
9515
9593
|
const currentVersion = await getTemplateVersion();
|
|
9516
|
-
const
|
|
9594
|
+
const registry = await readRegistry();
|
|
9595
|
+
if (Object.keys(registry.projects).length === 0) {
|
|
9596
|
+
const fallbackResults = await _findProjectsFromLockfiles(options, currentVersion);
|
|
9597
|
+
if (fallbackResults.length === 0 && !options.paths) {
|
|
9598
|
+
process.stderr.write(" No projects in registry. Run `omcustom projects --migrate` to import existing projects.\n");
|
|
9599
|
+
}
|
|
9600
|
+
return fallbackResults;
|
|
9601
|
+
}
|
|
9602
|
+
const results = [];
|
|
9603
|
+
for (const [projectPath, entry] of Object.entries(registry.projects)) {
|
|
9604
|
+
if (options.paths && options.paths.length > 0) {
|
|
9605
|
+
const matchesPath = options.paths.some((searchPath) => projectPath === searchPath || projectPath.startsWith(searchPath + sep2));
|
|
9606
|
+
if (!matchesPath)
|
|
9607
|
+
continue;
|
|
9608
|
+
}
|
|
9609
|
+
results.push({
|
|
9610
|
+
name: basename4(projectPath),
|
|
9611
|
+
path: projectPath,
|
|
9612
|
+
version: entry.version || null,
|
|
9613
|
+
installedAt: entry.installedAt || null,
|
|
9614
|
+
updatedAt: entry.updatedAt || null,
|
|
9615
|
+
status: computeStatus(entry.version || null, currentVersion),
|
|
9616
|
+
detectionMethod: "registry"
|
|
9617
|
+
});
|
|
9618
|
+
}
|
|
9619
|
+
return results.sort((a, b) => {
|
|
9620
|
+
if (a.status === "latest" && b.status !== "latest")
|
|
9621
|
+
return -1;
|
|
9622
|
+
if (a.status !== "latest" && b.status === "latest")
|
|
9623
|
+
return 1;
|
|
9624
|
+
return a.name.localeCompare(b.name);
|
|
9625
|
+
});
|
|
9626
|
+
}
|
|
9627
|
+
async function _findProjectsFromLockfiles(options, currentVersion) {
|
|
9628
|
+
const { dirname: dirname3 } = await import("node:path");
|
|
9629
|
+
const fs2 = await import("node:fs/promises");
|
|
9517
9630
|
const seen = new Set;
|
|
9518
9631
|
const results = [];
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9632
|
+
async function scanDir(dir2, depth) {
|
|
9633
|
+
if (depth > 3 || seen.has(dir2))
|
|
9634
|
+
return;
|
|
9635
|
+
seen.add(dir2);
|
|
9636
|
+
let entries;
|
|
9637
|
+
try {
|
|
9638
|
+
entries = await fs2.readdir(dir2, { withFileTypes: true });
|
|
9639
|
+
} catch {
|
|
9640
|
+
return;
|
|
9641
|
+
}
|
|
9642
|
+
const lockFile = await readLockFile(dir2);
|
|
9643
|
+
if (lockFile) {
|
|
9644
|
+
const version = lockFile.version || lockFile.templateVersion || null;
|
|
9645
|
+
results.push({
|
|
9646
|
+
name: basename4(dir2),
|
|
9647
|
+
path: dir2,
|
|
9648
|
+
version,
|
|
9649
|
+
installedAt: lockFile.installedAt || null,
|
|
9650
|
+
updatedAt: lockFile.updatedAt || null,
|
|
9651
|
+
status: computeStatus(version, currentVersion),
|
|
9652
|
+
detectionMethod: "lockfile"
|
|
9653
|
+
});
|
|
9654
|
+
return;
|
|
9655
|
+
}
|
|
9656
|
+
if (depth < 3) {
|
|
9657
|
+
const subdirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist" && e.name !== "build" && e.name !== ".git");
|
|
9658
|
+
await Promise.all(subdirs.map((sub) => scanDir(join10(dir2, sub.name), depth + 1).catch(() => {})));
|
|
9659
|
+
}
|
|
9522
9660
|
}
|
|
9661
|
+
const searchPaths = options.paths ? [...options.paths] : [];
|
|
9523
9662
|
if (!options.paths) {
|
|
9524
9663
|
const cwd = process.cwd();
|
|
9525
9664
|
if (!searchPaths.includes(cwd))
|
|
@@ -9528,10 +9667,7 @@ async function findProjects(options = {}) {
|
|
|
9528
9667
|
if (parent !== cwd && !searchPaths.includes(parent))
|
|
9529
9668
|
searchPaths.push(parent);
|
|
9530
9669
|
}
|
|
9531
|
-
|
|
9532
|
-
searchPaths.push(...options.paths);
|
|
9533
|
-
}
|
|
9534
|
-
await Promise.all(searchPaths.map((searchPath) => searchDirectory(searchPath, 0, results, currentVersion, seen).catch(() => {})));
|
|
9670
|
+
await Promise.all(searchPaths.map((p) => scanDir(p, 0).catch(() => {})));
|
|
9535
9671
|
return results.sort((a, b) => {
|
|
9536
9672
|
if (a.status === "latest" && b.status !== "latest")
|
|
9537
9673
|
return -1;
|
|
@@ -9542,7 +9678,7 @@ async function findProjects(options = {}) {
|
|
|
9542
9678
|
}
|
|
9543
9679
|
async function writeLockFile(projectDir, version, existing) {
|
|
9544
9680
|
const fs2 = await import("node:fs/promises");
|
|
9545
|
-
const lockFilePath =
|
|
9681
|
+
const lockFilePath = join10(projectDir, ".omcustom.lock.json");
|
|
9546
9682
|
const now = new Date().toISOString();
|
|
9547
9683
|
const merged = {
|
|
9548
9684
|
...existing || {},
|
|
@@ -9556,8 +9692,7 @@ function formatProjectsTable(projects, currentVersion) {
|
|
|
9556
9692
|
if (projects.length === 0) {
|
|
9557
9693
|
console.log(`
|
|
9558
9694
|
oh-my-customcode가 적용된 프로젝트를 찾을 수 없습니다.`);
|
|
9559
|
-
console.log(
|
|
9560
|
-
`);
|
|
9695
|
+
console.log(" 레지스트리가 비어 있습니다. `omcustom projects --migrate`를 실행하여 기존 프로젝트를 가져오세요.\n");
|
|
9561
9696
|
return;
|
|
9562
9697
|
}
|
|
9563
9698
|
const nameWidth = Math.max(20, ...projects.map((p) => p.name.length));
|
|
@@ -9589,7 +9724,7 @@ function formatProjectsTable(projects, currentVersion) {
|
|
|
9589
9724
|
`);
|
|
9590
9725
|
}
|
|
9591
9726
|
function shortenPath(path2) {
|
|
9592
|
-
const home =
|
|
9727
|
+
const home = homedir3();
|
|
9593
9728
|
if (path2.startsWith(home)) {
|
|
9594
9729
|
return `~${path2.slice(home.length)}`;
|
|
9595
9730
|
}
|
|
@@ -9606,9 +9741,34 @@ oh-my-customcode 적용 프로젝트 (${projects.length}개):`);
|
|
|
9606
9741
|
console.log(`
|
|
9607
9742
|
현재 설치 버전: v${currentVersion}`);
|
|
9608
9743
|
}
|
|
9744
|
+
async function runMigration(options) {
|
|
9745
|
+
const { migrateFromLockfiles: migrateFromLockfiles2 } = await Promise.resolve().then(() => (init_registry(), exports_registry));
|
|
9746
|
+
const { homedir: _homedir } = await import("node:os");
|
|
9747
|
+
const DEFAULT_SEARCH_DIRS = ["workspace", "projects", "dev", "src", "code", "repos", "work"];
|
|
9748
|
+
const home = _homedir();
|
|
9749
|
+
const searchDirs = [...DEFAULT_SEARCH_DIRS.map((d) => join10(home, d)), ...options.paths ?? []];
|
|
9750
|
+
const cwd = process.cwd();
|
|
9751
|
+
if (!searchDirs.includes(cwd))
|
|
9752
|
+
searchDirs.push(cwd);
|
|
9753
|
+
console.log(" 레지스트리 마이그레이션 시작...");
|
|
9754
|
+
try {
|
|
9755
|
+
const imported = await migrateFromLockfiles2(searchDirs);
|
|
9756
|
+
console.log(` 마이그레이션 완료: ${imported}개 프로젝트가 레지스트리에 추가되었습니다.`);
|
|
9757
|
+
return null;
|
|
9758
|
+
} catch (error2) {
|
|
9759
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
9760
|
+
}
|
|
9761
|
+
}
|
|
9609
9762
|
async function projectsCommand(options = {}) {
|
|
9610
9763
|
const currentVersion = await getTemplateVersion();
|
|
9611
9764
|
const format = options.format || "table";
|
|
9765
|
+
if (options.migrate) {
|
|
9766
|
+
const migrationError = await runMigration(options);
|
|
9767
|
+
if (migrationError) {
|
|
9768
|
+
console.error(" 마이그레이션 실패:", migrationError);
|
|
9769
|
+
return { success: false, projects: [], currentVersion, errors: [migrationError] };
|
|
9770
|
+
}
|
|
9771
|
+
}
|
|
9612
9772
|
console.log(" oh-my-customcode 적용 프로젝트를 검색 중...");
|
|
9613
9773
|
try {
|
|
9614
9774
|
const projects = await findProjects(options);
|
|
@@ -9626,11 +9786,11 @@ async function projectsCommand(options = {}) {
|
|
|
9626
9786
|
return { success: false, projects: [], currentVersion, errors: [errorMessage] };
|
|
9627
9787
|
}
|
|
9628
9788
|
}
|
|
9629
|
-
var
|
|
9789
|
+
var projects_default;
|
|
9630
9790
|
var init_projects = __esm(() => {
|
|
9631
9791
|
init_package();
|
|
9792
|
+
init_registry();
|
|
9632
9793
|
init_fs();
|
|
9633
|
-
DEFAULT_SEARCH_DIRS = ["workspace", "projects", "dev", "src", "code", "repos", "work"];
|
|
9634
9794
|
projects_default = projectsCommand;
|
|
9635
9795
|
});
|
|
9636
9796
|
|
|
@@ -11185,13 +11345,13 @@ var PromisePolyfill;
|
|
|
11185
11345
|
var init_promise_polyfill = __esm(() => {
|
|
11186
11346
|
PromisePolyfill = class PromisePolyfill extends Promise {
|
|
11187
11347
|
static withResolver() {
|
|
11188
|
-
let
|
|
11348
|
+
let resolve4;
|
|
11189
11349
|
let reject;
|
|
11190
11350
|
const promise = new Promise((res, rej) => {
|
|
11191
|
-
|
|
11351
|
+
resolve4 = res;
|
|
11192
11352
|
reject = rej;
|
|
11193
11353
|
});
|
|
11194
|
-
return { promise, resolve:
|
|
11354
|
+
return { promise, resolve: resolve4, reject };
|
|
11195
11355
|
}
|
|
11196
11356
|
};
|
|
11197
11357
|
});
|
|
@@ -11229,7 +11389,7 @@ function createPrompt(view) {
|
|
|
11229
11389
|
output
|
|
11230
11390
|
});
|
|
11231
11391
|
const screen = new ScreenManager(rl);
|
|
11232
|
-
const { promise, resolve:
|
|
11392
|
+
const { promise, resolve: resolve4, reject } = PromisePolyfill.withResolver();
|
|
11233
11393
|
const cancel = () => reject(new CancelPromptError);
|
|
11234
11394
|
if (signal) {
|
|
11235
11395
|
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
|
|
@@ -11257,7 +11417,7 @@ function createPrompt(view) {
|
|
|
11257
11417
|
cycle(() => {
|
|
11258
11418
|
try {
|
|
11259
11419
|
const nextView = view(config, (value) => {
|
|
11260
|
-
setImmediate(() =>
|
|
11420
|
+
setImmediate(() => resolve4(value));
|
|
11261
11421
|
});
|
|
11262
11422
|
if (nextView === undefined) {
|
|
11263
11423
|
const callerFilename = callSites[1]?.getFileName();
|
|
@@ -17122,7 +17282,7 @@ var require_lib2 = __commonJS((exports) => {
|
|
|
17122
17282
|
return matches;
|
|
17123
17283
|
};
|
|
17124
17284
|
exports.analyse = analyse;
|
|
17125
|
-
var detectFile = (filepath, opts = {}) => new Promise((
|
|
17285
|
+
var detectFile = (filepath, opts = {}) => new Promise((resolve4, reject) => {
|
|
17126
17286
|
let fd;
|
|
17127
17287
|
const fs3 = (0, node_1.default)();
|
|
17128
17288
|
const handler = (err, buffer) => {
|
|
@@ -17132,7 +17292,7 @@ var require_lib2 = __commonJS((exports) => {
|
|
|
17132
17292
|
if (err) {
|
|
17133
17293
|
reject(err);
|
|
17134
17294
|
} else if (buffer) {
|
|
17135
|
-
|
|
17295
|
+
resolve4((0, exports.detect)(buffer));
|
|
17136
17296
|
} else {
|
|
17137
17297
|
reject(new Error("No error and no buffer received"));
|
|
17138
17298
|
}
|
|
@@ -22259,11 +22419,15 @@ function formatPreflightWarnings(result) {
|
|
|
22259
22419
|
`);
|
|
22260
22420
|
}
|
|
22261
22421
|
|
|
22422
|
+
// src/cli/index.ts
|
|
22423
|
+
init_registry();
|
|
22424
|
+
|
|
22262
22425
|
// src/core/self-update.ts
|
|
22426
|
+
init_package();
|
|
22263
22427
|
import { execSync as execSync2, spawnSync } from "node:child_process";
|
|
22264
22428
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22265
|
-
import { homedir } from "node:os";
|
|
22266
|
-
import { dirname, join } from "node:path";
|
|
22429
|
+
import { homedir as homedir2 } from "node:os";
|
|
22430
|
+
import { dirname, join as join2 } from "node:path";
|
|
22267
22431
|
import { createInterface } from "node:readline/promises";
|
|
22268
22432
|
|
|
22269
22433
|
// node_modules/.bun/i18next@26.0.2+8e24a2f921b8d7be/node_modules/i18next/dist/esm/i18next.js
|
|
@@ -22271,8 +22435,8 @@ var isString = (obj) => typeof obj === "string";
|
|
|
22271
22435
|
var defer = () => {
|
|
22272
22436
|
let res;
|
|
22273
22437
|
let rej;
|
|
22274
|
-
const promise = new Promise((
|
|
22275
|
-
res =
|
|
22438
|
+
const promise = new Promise((resolve2, reject) => {
|
|
22439
|
+
res = resolve2;
|
|
22276
22440
|
rej = reject;
|
|
22277
22441
|
});
|
|
22278
22442
|
promise.resolve = res;
|
|
@@ -24828,6 +24992,8 @@ var en_default = {
|
|
|
24828
24992
|
interactiveUpdating: "Updating selected projects...",
|
|
24829
24993
|
projectLatestSuffix: "(latest)",
|
|
24830
24994
|
newVersionAvailable: "[Info] oh-my-customcode v{{latest}} available (current: v{{current}}). Run 'npm i -g oh-my-customcode' to upgrade.",
|
|
24995
|
+
selfUpdateDone: "✓ oh-my-customcode updated ({{from}} → {{to}})",
|
|
24996
|
+
selfUpdateFailed: "⚠ Self-update check failed — continuing with external updates",
|
|
24831
24997
|
rtkMissing: "[RTK] RTK is not installed. Attempting installation...",
|
|
24832
24998
|
rtkInstalled: "[RTK] ✓ RTK installed — 60-90% token savings activated",
|
|
24833
24999
|
codexMissing: "[Codex] Codex CLI is not installed. Attempting installation...",
|
|
@@ -25241,6 +25407,8 @@ var ko_default = {
|
|
|
25241
25407
|
interactiveUpdating: "선택된 프로젝트 업데이트 중...",
|
|
25242
25408
|
projectLatestSuffix: "(최신)",
|
|
25243
25409
|
newVersionAvailable: "[정보] oh-my-customcode v{{latest}} 사용 가능 (현재: v{{current}}). 'npm i -g oh-my-customcode'를 실행하여 업그레이드하세요.",
|
|
25410
|
+
selfUpdateDone: "✓ oh-my-customcode 업데이트 완료 ({{from}} → {{to}})",
|
|
25411
|
+
selfUpdateFailed: "⚠ 자체 업데이트 확인 실패 — 외부 업데이트를 계속 진행합니다",
|
|
25244
25412
|
rtkMissing: "[RTK] RTK가 설치되지 않았습니다. 설치를 시도합니다...",
|
|
25245
25413
|
rtkInstalled: "[RTK] ✓ RTK 설치 완료 — 토큰 60-90% 절감 활성화",
|
|
25246
25414
|
codexMissing: "[Codex] Codex CLI가 설치되지 않았습니다. 설치를 시도합니다...",
|
|
@@ -25551,7 +25719,7 @@ var i18n = {
|
|
|
25551
25719
|
// src/core/self-update.ts
|
|
25552
25720
|
var DEFAULT_PACKAGE_NAME = "oh-my-customcode";
|
|
25553
25721
|
var DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
25554
|
-
var DEFAULT_CACHE_PATH =
|
|
25722
|
+
var DEFAULT_CACHE_PATH = join2(homedir2(), ".oh-my-customcode", "self-update-cache.json");
|
|
25555
25723
|
function normalizeVersion(version) {
|
|
25556
25724
|
return version.trim().replace(/^v/i, "").split("-")[0] || "";
|
|
25557
25725
|
}
|
|
@@ -25688,6 +25856,70 @@ function runGlobalUpdate(packageName, latestVersion) {
|
|
|
25688
25856
|
printContinueCurrentVersion();
|
|
25689
25857
|
}
|
|
25690
25858
|
}
|
|
25859
|
+
function shouldSkipEnvironmentUpdate(argv, env) {
|
|
25860
|
+
if (isNpxInvocation(argv, env))
|
|
25861
|
+
return true;
|
|
25862
|
+
if (env.CI === "true" || env.GITHUB_ACTIONS === "true")
|
|
25863
|
+
return true;
|
|
25864
|
+
if (env.OMCUSTOM_SKIP_SELF_UPDATE === "true")
|
|
25865
|
+
return true;
|
|
25866
|
+
return false;
|
|
25867
|
+
}
|
|
25868
|
+
function installGlobalPackage(packageName, version, silent) {
|
|
25869
|
+
try {
|
|
25870
|
+
execSync2(`npm install -g ${packageName}@${version}`, {
|
|
25871
|
+
stdio: silent ? "pipe" : "inherit",
|
|
25872
|
+
timeout: 60000
|
|
25873
|
+
});
|
|
25874
|
+
return true;
|
|
25875
|
+
} catch {
|
|
25876
|
+
return false;
|
|
25877
|
+
}
|
|
25878
|
+
}
|
|
25879
|
+
function executeSelfUpdate(options = {}) {
|
|
25880
|
+
const packageName = options.packageName || DEFAULT_PACKAGE_NAME;
|
|
25881
|
+
const argv = options.argv || process.argv;
|
|
25882
|
+
const env = options.env || process.env;
|
|
25883
|
+
const currentVersion = normalizeVersion(options.currentVersion || package_default.version || "");
|
|
25884
|
+
const noUpdate = {
|
|
25885
|
+
updated: false,
|
|
25886
|
+
fromVersion: currentVersion,
|
|
25887
|
+
toVersion: currentVersion
|
|
25888
|
+
};
|
|
25889
|
+
if (shouldSkipEnvironmentUpdate(argv, env)) {
|
|
25890
|
+
return noUpdate;
|
|
25891
|
+
}
|
|
25892
|
+
const checkOptions = {
|
|
25893
|
+
currentVersion,
|
|
25894
|
+
packageName,
|
|
25895
|
+
cachePath: options.cachePath,
|
|
25896
|
+
cacheTtlMs: options.cacheTtlMs,
|
|
25897
|
+
fetchLatestVersion: options.fetchLatestVersion,
|
|
25898
|
+
now: options.now,
|
|
25899
|
+
argv,
|
|
25900
|
+
env
|
|
25901
|
+
};
|
|
25902
|
+
const result = checkSelfUpdate(checkOptions);
|
|
25903
|
+
if (!result.checked || !result.updateAvailable || !result.latestVersion) {
|
|
25904
|
+
return noUpdate;
|
|
25905
|
+
}
|
|
25906
|
+
const latestVersion = result.latestVersion;
|
|
25907
|
+
if (!options.silent) {
|
|
25908
|
+
console.log(i18n.t("cli.selfUpdate.updatingGlobal", { version: latestVersion }));
|
|
25909
|
+
}
|
|
25910
|
+
const installed = installGlobalPackage(packageName, latestVersion, options.silent ?? false);
|
|
25911
|
+
if (installed) {
|
|
25912
|
+
if (!options.silent) {
|
|
25913
|
+
console.log(i18n.t("cli.selfUpdate.updated", { version: latestVersion }));
|
|
25914
|
+
printContinuationSpacing();
|
|
25915
|
+
}
|
|
25916
|
+
return { updated: true, fromVersion: currentVersion, toVersion: latestVersion };
|
|
25917
|
+
}
|
|
25918
|
+
if (!options.silent) {
|
|
25919
|
+
console.warn(i18n.t("cli.selfUpdate.failed", { error: "npm install failed" }));
|
|
25920
|
+
}
|
|
25921
|
+
return noUpdate;
|
|
25922
|
+
}
|
|
25691
25923
|
function checkSelfUpdate(options) {
|
|
25692
25924
|
const packageName = options.packageName || DEFAULT_PACKAGE_NAME;
|
|
25693
25925
|
const cachePath = options.cachePath || DEFAULT_CACHE_PATH;
|
|
@@ -26145,7 +26377,7 @@ function installCodex(deps = defaultDeps) {
|
|
|
26145
26377
|
|
|
26146
26378
|
// src/core/config.ts
|
|
26147
26379
|
init_fs();
|
|
26148
|
-
import { join as
|
|
26380
|
+
import { join as join4 } from "node:path";
|
|
26149
26381
|
var CONFIG_FILE = ".omcustomrc.json";
|
|
26150
26382
|
var CURRENT_CONFIG_VERSION = 1;
|
|
26151
26383
|
function getDefaultConfig() {
|
|
@@ -26185,7 +26417,7 @@ function getDefaultPreferences() {
|
|
|
26185
26417
|
};
|
|
26186
26418
|
}
|
|
26187
26419
|
function getConfigPath(targetDir) {
|
|
26188
|
-
return
|
|
26420
|
+
return join4(targetDir, CONFIG_FILE);
|
|
26189
26421
|
}
|
|
26190
26422
|
async function loadConfig(targetDir) {
|
|
26191
26423
|
const configPath = getConfigPath(targetDir);
|
|
@@ -26282,14 +26514,14 @@ function migrateConfig(config) {
|
|
|
26282
26514
|
|
|
26283
26515
|
// src/core/doctor-framework.ts
|
|
26284
26516
|
init_fs();
|
|
26285
|
-
import { readFile } from "node:fs/promises";
|
|
26286
|
-
import { join as
|
|
26517
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
26518
|
+
import { join as join5 } from "node:path";
|
|
26287
26519
|
async function getInstalledVersion(targetDir) {
|
|
26288
|
-
const rcPath =
|
|
26520
|
+
const rcPath = join5(targetDir, ".omcustomrc.json");
|
|
26289
26521
|
if (!await fileExists(rcPath))
|
|
26290
26522
|
return null;
|
|
26291
26523
|
try {
|
|
26292
|
-
const content = JSON.parse(await
|
|
26524
|
+
const content = JSON.parse(await readFile2(rcPath, "utf-8"));
|
|
26293
26525
|
return content.version ?? null;
|
|
26294
26526
|
} catch {
|
|
26295
26527
|
return null;
|
|
@@ -26357,7 +26589,7 @@ init_fs();
|
|
|
26357
26589
|
import { createHash } from "node:crypto";
|
|
26358
26590
|
import { createReadStream } from "node:fs";
|
|
26359
26591
|
import { readdir, stat } from "node:fs/promises";
|
|
26360
|
-
import { join as
|
|
26592
|
+
import { join as join6, relative as relative2 } from "node:path";
|
|
26361
26593
|
var LOCKFILE_NAME = ".omcustom.lock.json";
|
|
26362
26594
|
var LOCKFILE_VERSION = 1;
|
|
26363
26595
|
var LOCKFILE_COMPONENTS = [
|
|
@@ -26371,7 +26603,7 @@ var LOCKFILE_COMPONENTS = [
|
|
|
26371
26603
|
];
|
|
26372
26604
|
var COMPONENT_PATHS = LOCKFILE_COMPONENTS.map((component) => [getComponentPath(component), component]);
|
|
26373
26605
|
function computeFileHash(filePath) {
|
|
26374
|
-
return new Promise((
|
|
26606
|
+
return new Promise((resolve3, reject) => {
|
|
26375
26607
|
const hash = createHash("sha256");
|
|
26376
26608
|
const stream = createReadStream(filePath);
|
|
26377
26609
|
stream.on("error", (err) => {
|
|
@@ -26381,12 +26613,12 @@ function computeFileHash(filePath) {
|
|
|
26381
26613
|
hash.update(chunk);
|
|
26382
26614
|
});
|
|
26383
26615
|
stream.on("end", () => {
|
|
26384
|
-
|
|
26616
|
+
resolve3(hash.digest("hex"));
|
|
26385
26617
|
});
|
|
26386
26618
|
});
|
|
26387
26619
|
}
|
|
26388
26620
|
async function readLockfile(targetDir) {
|
|
26389
|
-
const lockfilePath =
|
|
26621
|
+
const lockfilePath = join6(targetDir, LOCKFILE_NAME);
|
|
26390
26622
|
const exists2 = await fileExists(lockfilePath);
|
|
26391
26623
|
if (!exists2) {
|
|
26392
26624
|
debug("lockfile.not_found", { path: lockfilePath });
|
|
@@ -26410,7 +26642,7 @@ async function readLockfile(targetDir) {
|
|
|
26410
26642
|
}
|
|
26411
26643
|
}
|
|
26412
26644
|
async function writeLockfile(targetDir, lockfile) {
|
|
26413
|
-
const lockfilePath =
|
|
26645
|
+
const lockfilePath = join6(targetDir, LOCKFILE_NAME);
|
|
26414
26646
|
await writeJsonFile(lockfilePath, lockfile);
|
|
26415
26647
|
debug("lockfile.written", { path: lockfilePath });
|
|
26416
26648
|
}
|
|
@@ -26435,7 +26667,7 @@ async function collectFiles(dir2, projectRoot, isTopLevel) {
|
|
|
26435
26667
|
if (isTopLevel && entry.startsWith(".") && entry !== ".claude") {
|
|
26436
26668
|
continue;
|
|
26437
26669
|
}
|
|
26438
|
-
const fullPath =
|
|
26670
|
+
const fullPath = join6(dir2, entry);
|
|
26439
26671
|
let fileStat;
|
|
26440
26672
|
try {
|
|
26441
26673
|
fileStat = await stat(fullPath);
|
|
@@ -26453,7 +26685,7 @@ async function collectFiles(dir2, projectRoot, isTopLevel) {
|
|
|
26453
26685
|
}
|
|
26454
26686
|
async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
26455
26687
|
const files = {};
|
|
26456
|
-
const componentRoots = COMPONENT_PATHS.map(([prefix]) =>
|
|
26688
|
+
const componentRoots = COMPONENT_PATHS.map(([prefix]) => join6(targetDir, prefix));
|
|
26457
26689
|
for (const componentRoot of componentRoots) {
|
|
26458
26690
|
const exists2 = await fileExists(componentRoot);
|
|
26459
26691
|
if (!exists2) {
|
|
@@ -26493,8 +26725,8 @@ async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
|
26493
26725
|
async function generateAndWriteLockfileForDir(targetDir) {
|
|
26494
26726
|
try {
|
|
26495
26727
|
const packageRoot = getPackageRoot();
|
|
26496
|
-
const manifest = await readJsonFile(
|
|
26497
|
-
const { version: generatorVersion } = await readJsonFile(
|
|
26728
|
+
const manifest = await readJsonFile(join6(packageRoot, "templates", "manifest.json"));
|
|
26729
|
+
const { version: generatorVersion } = await readJsonFile(join6(packageRoot, "package.json"));
|
|
26498
26730
|
const lockfile = await generateLockfile(targetDir, generatorVersion, manifest.version);
|
|
26499
26731
|
await writeLockfile(targetDir, lockfile);
|
|
26500
26732
|
return { fileCount: Object.keys(lockfile.files).length };
|
|
@@ -27244,7 +27476,7 @@ async function doctorCommand(options = {}) {
|
|
|
27244
27476
|
|
|
27245
27477
|
// src/cli/init.ts
|
|
27246
27478
|
init_package();
|
|
27247
|
-
import { join as
|
|
27479
|
+
import { join as join12 } from "node:path";
|
|
27248
27480
|
|
|
27249
27481
|
// src/core/installer.ts
|
|
27250
27482
|
init_fs();
|
|
@@ -27255,18 +27487,18 @@ import {
|
|
|
27255
27487
|
rename,
|
|
27256
27488
|
stat as stat2
|
|
27257
27489
|
} from "node:fs/promises";
|
|
27258
|
-
import { basename as
|
|
27490
|
+
import { basename as basename3, join as join8 } from "node:path";
|
|
27259
27491
|
|
|
27260
27492
|
// src/core/file-preservation.ts
|
|
27261
27493
|
init_fs();
|
|
27262
|
-
import { basename, join as
|
|
27494
|
+
import { basename as basename2, join as join7 } from "node:path";
|
|
27263
27495
|
var DEFAULT_CRITICAL_FILES = ["settings.json", "settings.local.json"];
|
|
27264
27496
|
var DEFAULT_CRITICAL_DIRECTORIES = ["agent-memory", "agent-memory-local"];
|
|
27265
27497
|
var PROTECTED_FRAMEWORK_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
27266
27498
|
var PROTECTED_RULE_PATTERNS = ["rules/MUST-*.md"];
|
|
27267
27499
|
function isProtectedFile(relativePath) {
|
|
27268
|
-
const
|
|
27269
|
-
if (PROTECTED_FRAMEWORK_FILES.includes(
|
|
27500
|
+
const basename3 = relativePath.split("/").pop() ?? "";
|
|
27501
|
+
if (PROTECTED_FRAMEWORK_FILES.includes(basename3)) {
|
|
27270
27502
|
return true;
|
|
27271
27503
|
}
|
|
27272
27504
|
for (const pattern of PROTECTED_RULE_PATTERNS) {
|
|
@@ -27282,8 +27514,8 @@ function matchesGlobPattern(filePath, pattern) {
|
|
|
27282
27514
|
return regex.test(filePath);
|
|
27283
27515
|
}
|
|
27284
27516
|
async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
27285
|
-
const srcPath =
|
|
27286
|
-
const destPath =
|
|
27517
|
+
const srcPath = join7(rootDir, fileName);
|
|
27518
|
+
const destPath = join7(tempDir, fileName);
|
|
27287
27519
|
try {
|
|
27288
27520
|
if (await fileExists(srcPath)) {
|
|
27289
27521
|
await copyFile(srcPath, destPath);
|
|
@@ -27297,8 +27529,8 @@ async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
|
27297
27529
|
}
|
|
27298
27530
|
}
|
|
27299
27531
|
async function extractSingleDir(dirName, rootDir, tempDir, result) {
|
|
27300
|
-
const srcPath =
|
|
27301
|
-
const destPath =
|
|
27532
|
+
const srcPath = join7(rootDir, dirName);
|
|
27533
|
+
const destPath = join7(tempDir, dirName);
|
|
27302
27534
|
try {
|
|
27303
27535
|
if (await fileExists(srcPath)) {
|
|
27304
27536
|
await copyDirectory(srcPath, destPath, { overwrite: true, preserveTimestamps: true });
|
|
@@ -27335,8 +27567,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
27335
27567
|
failures: []
|
|
27336
27568
|
};
|
|
27337
27569
|
for (const fileName of preservation.extractedFiles) {
|
|
27338
|
-
const preservedPath =
|
|
27339
|
-
const targetPath =
|
|
27570
|
+
const preservedPath = join7(preservation.tempDir, fileName);
|
|
27571
|
+
const targetPath = join7(rootDir, fileName);
|
|
27340
27572
|
try {
|
|
27341
27573
|
if (fileName.endsWith(".json")) {
|
|
27342
27574
|
await mergeJsonFile(preservedPath, targetPath);
|
|
@@ -27352,8 +27584,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
27352
27584
|
}
|
|
27353
27585
|
}
|
|
27354
27586
|
for (const dirName of preservation.extractedDirs) {
|
|
27355
|
-
const preservedPath =
|
|
27356
|
-
const targetPath =
|
|
27587
|
+
const preservedPath = join7(preservation.tempDir, dirName);
|
|
27588
|
+
const targetPath = join7(rootDir, dirName);
|
|
27357
27589
|
try {
|
|
27358
27590
|
await copyDirectory(preservedPath, targetPath, {
|
|
27359
27591
|
overwrite: false,
|
|
@@ -27375,10 +27607,10 @@ async function mergeJsonFile(preservedPath, targetPath) {
|
|
|
27375
27607
|
const targetData = await readJsonFile(targetPath);
|
|
27376
27608
|
const merged = deepMerge(targetData, preservedData);
|
|
27377
27609
|
await writeJsonFile(targetPath, merged);
|
|
27378
|
-
debug("preserve.merged_json", { file:
|
|
27610
|
+
debug("preserve.merged_json", { file: basename2(targetPath) });
|
|
27379
27611
|
} else {
|
|
27380
27612
|
await copyFile(preservedPath, targetPath);
|
|
27381
|
-
debug("preserve.copied_json", { file:
|
|
27613
|
+
debug("preserve.copied_json", { file: basename2(targetPath) });
|
|
27382
27614
|
}
|
|
27383
27615
|
}
|
|
27384
27616
|
function deepMerge(target, source) {
|
|
@@ -27684,7 +27916,7 @@ function shouldInstallAgent(agentDomain, filterDomain) {
|
|
|
27684
27916
|
var DEFAULT_LANGUAGE2 = "en";
|
|
27685
27917
|
function getTemplateDir() {
|
|
27686
27918
|
const packageRoot = getPackageRoot();
|
|
27687
|
-
return
|
|
27919
|
+
return join8(packageRoot, "templates");
|
|
27688
27920
|
}
|
|
27689
27921
|
function createInstallResult(targetDir) {
|
|
27690
27922
|
return {
|
|
@@ -27706,7 +27938,7 @@ async function handleBackup(targetDir, shouldBackup, result) {
|
|
|
27706
27938
|
if (!shouldBackup)
|
|
27707
27939
|
return null;
|
|
27708
27940
|
const layout = getProviderLayout();
|
|
27709
|
-
const rootDir =
|
|
27941
|
+
const rootDir = join8(targetDir, layout.rootDir);
|
|
27710
27942
|
let preservation = null;
|
|
27711
27943
|
if (await fileExists(rootDir)) {
|
|
27712
27944
|
const { createTempDir: createTempDir2 } = await Promise.resolve().then(() => (init_fs(), exports_fs));
|
|
@@ -27763,8 +27995,8 @@ async function installSingleComponent(targetDir, component, options, result) {
|
|
|
27763
27995
|
}
|
|
27764
27996
|
async function installStatusline(targetDir, options, _result) {
|
|
27765
27997
|
const layout = getProviderLayout();
|
|
27766
|
-
const srcPath = resolveTemplatePath(
|
|
27767
|
-
const destPath =
|
|
27998
|
+
const srcPath = resolveTemplatePath(join8(layout.rootDir, "statusline.sh"));
|
|
27999
|
+
const destPath = join8(targetDir, layout.rootDir, "statusline.sh");
|
|
27768
28000
|
if (!await fileExists(srcPath)) {
|
|
27769
28001
|
debug("install.statusline_not_found", { path: srcPath });
|
|
27770
28002
|
return;
|
|
@@ -27782,7 +28014,7 @@ async function installStatusline(targetDir, options, _result) {
|
|
|
27782
28014
|
}
|
|
27783
28015
|
async function installSettingsLocal(targetDir, result) {
|
|
27784
28016
|
const layout = getProviderLayout();
|
|
27785
|
-
const settingsPath =
|
|
28017
|
+
const settingsPath = join8(targetDir, layout.rootDir, "settings.local.json");
|
|
27786
28018
|
const statusLineConfig = {
|
|
27787
28019
|
statusLine: {
|
|
27788
28020
|
type: "command",
|
|
@@ -27868,7 +28100,7 @@ async function install(options) {
|
|
|
27868
28100
|
await installEntryDocWithTracking(options.targetDir, options, result);
|
|
27869
28101
|
if (preservation) {
|
|
27870
28102
|
const layout = getProviderLayout();
|
|
27871
|
-
const rootDir =
|
|
28103
|
+
const rootDir = join8(options.targetDir, layout.rootDir);
|
|
27872
28104
|
const restoration = await restoreCriticalFiles(rootDir, preservation);
|
|
27873
28105
|
if (restoration.restoredFiles.length > 0 || restoration.restoredDirs.length > 0) {
|
|
27874
28106
|
info("install.restored", {
|
|
@@ -27905,7 +28137,7 @@ async function install(options) {
|
|
|
27905
28137
|
async function getTemplateManifest() {
|
|
27906
28138
|
const packageRoot = getPackageRoot();
|
|
27907
28139
|
const layout = getProviderLayout();
|
|
27908
|
-
const manifestPath =
|
|
28140
|
+
const manifestPath = join8(packageRoot, "templates", layout.manifestFile);
|
|
27909
28141
|
if (await fileExists(manifestPath)) {
|
|
27910
28142
|
return readJsonFile(manifestPath);
|
|
27911
28143
|
}
|
|
@@ -27928,10 +28160,10 @@ async function installSkillsWithScopeFilter(srcPath, destPath, options) {
|
|
|
27928
28160
|
await ensureDirectory(destPath);
|
|
27929
28161
|
const entries = await readdir2(srcPath);
|
|
27930
28162
|
for (const entry of entries) {
|
|
27931
|
-
const entrySrcPath =
|
|
28163
|
+
const entrySrcPath = join8(srcPath, entry);
|
|
27932
28164
|
if (!(await stat2(entrySrcPath)).isDirectory())
|
|
27933
28165
|
continue;
|
|
27934
|
-
const skillMdPath =
|
|
28166
|
+
const skillMdPath = join8(entrySrcPath, "SKILL.md");
|
|
27935
28167
|
if (await fileExists(skillMdPath)) {
|
|
27936
28168
|
const content = await fsReadFile(skillMdPath, "utf-8");
|
|
27937
28169
|
const scope = getSkillScope(content);
|
|
@@ -27940,7 +28172,7 @@ async function installSkillsWithScopeFilter(srcPath, destPath, options) {
|
|
|
27940
28172
|
continue;
|
|
27941
28173
|
}
|
|
27942
28174
|
}
|
|
27943
|
-
await copyDirectory(entrySrcPath,
|
|
28175
|
+
await copyDirectory(entrySrcPath, join8(destPath, entry), {
|
|
27944
28176
|
overwrite: !!(options.force || options.backup),
|
|
27945
28177
|
preserveSymlinks: true,
|
|
27946
28178
|
preserveTimestamps: true
|
|
@@ -27951,10 +28183,10 @@ async function installAgentsWithDomainFilter(srcPath, destPath, options) {
|
|
|
27951
28183
|
await ensureDirectory(destPath);
|
|
27952
28184
|
const entries = await readdir2(srcPath);
|
|
27953
28185
|
for (const entry of entries) {
|
|
27954
|
-
const entrySrcPath =
|
|
28186
|
+
const entrySrcPath = join8(srcPath, entry);
|
|
27955
28187
|
const entryStat = await stat2(entrySrcPath);
|
|
27956
28188
|
if (entryStat.isDirectory()) {
|
|
27957
|
-
await copyDirectory(entrySrcPath,
|
|
28189
|
+
await copyDirectory(entrySrcPath, join8(destPath, entry), {
|
|
27958
28190
|
overwrite: !!(options.force || options.backup),
|
|
27959
28191
|
preserveSymlinks: true,
|
|
27960
28192
|
preserveTimestamps: true
|
|
@@ -27971,7 +28203,7 @@ async function installAgentsWithDomainFilter(srcPath, destPath, options) {
|
|
|
27971
28203
|
continue;
|
|
27972
28204
|
}
|
|
27973
28205
|
}
|
|
27974
|
-
await copyFile(entrySrcPath,
|
|
28206
|
+
await copyFile(entrySrcPath, join8(destPath, entry));
|
|
27975
28207
|
}
|
|
27976
28208
|
}
|
|
27977
28209
|
async function installComponent(targetDir, component, options) {
|
|
@@ -27979,7 +28211,7 @@ async function installComponent(targetDir, component, options) {
|
|
|
27979
28211
|
return false;
|
|
27980
28212
|
}
|
|
27981
28213
|
const templatePath = getComponentPath(component);
|
|
27982
|
-
const destPath =
|
|
28214
|
+
const destPath = join8(targetDir, templatePath);
|
|
27983
28215
|
const destExists = await fileExists(destPath);
|
|
27984
28216
|
if (destExists && !options.force && !options.backup) {
|
|
27985
28217
|
debug("install.component_skipped", { component });
|
|
@@ -28013,7 +28245,7 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
28013
28245
|
const layout = getProviderLayout();
|
|
28014
28246
|
const templateFile = getEntryTemplateName(language);
|
|
28015
28247
|
const srcPath = resolveTemplatePath(templateFile);
|
|
28016
|
-
const destPath =
|
|
28248
|
+
const destPath = join8(targetDir, layout.entryFile);
|
|
28017
28249
|
if (!await fileExists(srcPath)) {
|
|
28018
28250
|
warn("install.entry_md_not_found", { language, path: srcPath, entry: layout.entryFile });
|
|
28019
28251
|
return false;
|
|
@@ -28033,8 +28265,8 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
28033
28265
|
return true;
|
|
28034
28266
|
}
|
|
28035
28267
|
async function backupExisting(sourcePath, backupDir) {
|
|
28036
|
-
const name =
|
|
28037
|
-
const backupPath =
|
|
28268
|
+
const name = basename3(sourcePath);
|
|
28269
|
+
const backupPath = join8(backupDir, name);
|
|
28038
28270
|
await rename(sourcePath, backupPath);
|
|
28039
28271
|
return backupPath;
|
|
28040
28272
|
}
|
|
@@ -28043,7 +28275,7 @@ async function checkExistingPaths(targetDir) {
|
|
|
28043
28275
|
const pathsToCheck = [layout.entryFile, layout.rootDir, "guides"];
|
|
28044
28276
|
const existingPaths = [];
|
|
28045
28277
|
for (const relativePath of pathsToCheck) {
|
|
28046
|
-
const fullPath =
|
|
28278
|
+
const fullPath = join8(targetDir, relativePath);
|
|
28047
28279
|
if (await fileExists(fullPath)) {
|
|
28048
28280
|
existingPaths.push(relativePath);
|
|
28049
28281
|
}
|
|
@@ -28057,11 +28289,11 @@ async function backupExistingInstallation(targetDir) {
|
|
|
28057
28289
|
return [];
|
|
28058
28290
|
}
|
|
28059
28291
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
28060
|
-
const backupDir =
|
|
28292
|
+
const backupDir = join8(targetDir, `${layout.backupDirPrefix}${timestamp}`);
|
|
28061
28293
|
await ensureDirectory(backupDir);
|
|
28062
28294
|
const backedUpPaths = [];
|
|
28063
28295
|
for (const relativePath of existingPaths) {
|
|
28064
|
-
const fullPath =
|
|
28296
|
+
const fullPath = join8(targetDir, relativePath);
|
|
28065
28297
|
try {
|
|
28066
28298
|
const backupPath = await backupExisting(fullPath, backupDir);
|
|
28067
28299
|
backedUpPaths.push(backupPath);
|
|
@@ -28077,13 +28309,13 @@ async function backupExistingInstallation(targetDir) {
|
|
|
28077
28309
|
// src/core/mcp-config.ts
|
|
28078
28310
|
init_fs();
|
|
28079
28311
|
import { execSync as execSync5 } from "node:child_process";
|
|
28080
|
-
import { writeFile } from "node:fs/promises";
|
|
28081
|
-
import { join as
|
|
28312
|
+
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
28313
|
+
import { join as join9 } from "node:path";
|
|
28082
28314
|
async function generateMCPConfig(targetDir) {
|
|
28083
28315
|
const layout = getProviderLayout();
|
|
28084
|
-
const mcpConfigPath =
|
|
28085
|
-
const ontologyDir =
|
|
28086
|
-
const ontologyExists = await fileExists(
|
|
28316
|
+
const mcpConfigPath = join9(targetDir, ".mcp.json");
|
|
28317
|
+
const ontologyDir = join9(layout.rootDir, "ontology");
|
|
28318
|
+
const ontologyExists = await fileExists(join9(targetDir, ontologyDir));
|
|
28087
28319
|
if (!ontologyExists) {
|
|
28088
28320
|
return;
|
|
28089
28321
|
}
|
|
@@ -28118,21 +28350,21 @@ async function generateMCPConfig(targetDir) {
|
|
|
28118
28350
|
const existingConfigPath = mcpConfigPath;
|
|
28119
28351
|
if (await fileExists(existingConfigPath)) {
|
|
28120
28352
|
try {
|
|
28121
|
-
const { readFile:
|
|
28122
|
-
const existingContent = await
|
|
28353
|
+
const { readFile: readFile3 } = await import("node:fs/promises");
|
|
28354
|
+
const existingContent = await readFile3(existingConfigPath, "utf-8");
|
|
28123
28355
|
const existing = JSON.parse(existingContent);
|
|
28124
28356
|
if (!existing.mcpServers?.["ontology-rag"]) {
|
|
28125
28357
|
existing.mcpServers = existing.mcpServers || {};
|
|
28126
28358
|
existing.mcpServers["ontology-rag"] = config.mcpServers["ontology-rag"];
|
|
28127
|
-
await
|
|
28359
|
+
await writeFile2(mcpConfigPath, `${JSON.stringify(existing, null, 2)}
|
|
28128
28360
|
`);
|
|
28129
28361
|
}
|
|
28130
28362
|
} catch {
|
|
28131
|
-
await
|
|
28363
|
+
await writeFile2(mcpConfigPath, `${JSON.stringify(config, null, 2)}
|
|
28132
28364
|
`);
|
|
28133
28365
|
}
|
|
28134
28366
|
} else {
|
|
28135
|
-
await
|
|
28367
|
+
await writeFile2(mcpConfigPath, `${JSON.stringify(config, null, 2)}
|
|
28136
28368
|
`);
|
|
28137
28369
|
}
|
|
28138
28370
|
info("ontology-rag MCP server configured successfully");
|
|
@@ -28146,16 +28378,20 @@ async function checkUvAvailable() {
|
|
|
28146
28378
|
}
|
|
28147
28379
|
}
|
|
28148
28380
|
|
|
28381
|
+
// src/cli/init.ts
|
|
28382
|
+
init_registry();
|
|
28383
|
+
|
|
28149
28384
|
// src/core/snapshot.ts
|
|
28150
28385
|
init_package();
|
|
28151
28386
|
init_projects();
|
|
28152
28387
|
import { existsSync as existsSync2 } from "node:fs";
|
|
28153
28388
|
import { copyFile as copyFile2, cp } from "node:fs/promises";
|
|
28154
|
-
import { join as
|
|
28389
|
+
import { join as join11 } from "node:path";
|
|
28155
28390
|
init_fs();
|
|
28391
|
+
init_registry();
|
|
28156
28392
|
async function checkExistingInstallation(targetDir) {
|
|
28157
28393
|
const layout = getProviderLayout();
|
|
28158
|
-
const rootDir =
|
|
28394
|
+
const rootDir = join11(targetDir, layout.rootDir);
|
|
28159
28395
|
return fileExists(rootDir);
|
|
28160
28396
|
}
|
|
28161
28397
|
async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
@@ -28167,7 +28403,7 @@ async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
|
28167
28403
|
};
|
|
28168
28404
|
}
|
|
28169
28405
|
const layout = getProviderLayout();
|
|
28170
|
-
const snapshotClaude =
|
|
28406
|
+
const snapshotClaude = join11(snapshotPath, layout.rootDir);
|
|
28171
28407
|
if (!existsSync2(snapshotClaude)) {
|
|
28172
28408
|
return {
|
|
28173
28409
|
success: false,
|
|
@@ -28181,29 +28417,32 @@ async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
|
28181
28417
|
if (exists2 && !options.force) {
|
|
28182
28418
|
console.log(i18n.t("cli.init.exists", { rootDir: layout.rootDir }));
|
|
28183
28419
|
console.log(i18n.t("cli.init.backing_up"));
|
|
28184
|
-
const backupDir =
|
|
28185
|
-
await cp(
|
|
28420
|
+
const backupDir = join11(targetDir, `.claude-backup-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, -1)}`);
|
|
28421
|
+
await cp(join11(targetDir, layout.rootDir), backupDir, { recursive: true });
|
|
28186
28422
|
console.log(` Backed up to: ${backupDir}`);
|
|
28187
28423
|
}
|
|
28188
|
-
await cp(snapshotClaude,
|
|
28424
|
+
await cp(snapshotClaude, join11(targetDir, layout.rootDir), {
|
|
28189
28425
|
recursive: true,
|
|
28190
28426
|
force: true
|
|
28191
28427
|
});
|
|
28192
|
-
const snapshotGuides =
|
|
28428
|
+
const snapshotGuides = join11(snapshotPath, "guides");
|
|
28193
28429
|
if (existsSync2(snapshotGuides)) {
|
|
28194
|
-
await cp(snapshotGuides,
|
|
28430
|
+
await cp(snapshotGuides, join11(targetDir, "guides"), {
|
|
28195
28431
|
recursive: true,
|
|
28196
28432
|
force: true
|
|
28197
28433
|
});
|
|
28198
28434
|
}
|
|
28199
|
-
const snapshotEntry =
|
|
28435
|
+
const snapshotEntry = join11(snapshotPath, layout.entryFile);
|
|
28200
28436
|
if (existsSync2(snapshotEntry)) {
|
|
28201
|
-
await copyFile2(snapshotEntry,
|
|
28437
|
+
await copyFile2(snapshotEntry, join11(targetDir, layout.entryFile));
|
|
28202
28438
|
}
|
|
28203
28439
|
try {
|
|
28204
28440
|
const existing = await readLockFile(targetDir);
|
|
28205
28441
|
await writeLockFile(targetDir, package_default.version, existing);
|
|
28206
28442
|
} catch {}
|
|
28443
|
+
try {
|
|
28444
|
+
await registerProject(targetDir, package_default.version);
|
|
28445
|
+
} catch {}
|
|
28207
28446
|
console.log(i18n.t("cli.init.success"));
|
|
28208
28447
|
console.log(`
|
|
28209
28448
|
Installed from snapshot: ${snapshotPath}`);
|
|
@@ -29203,7 +29442,7 @@ async function runInitWizard(options) {
|
|
|
29203
29442
|
// src/cli/init.ts
|
|
29204
29443
|
async function checkExistingInstallation2(targetDir) {
|
|
29205
29444
|
const layout = getProviderLayout();
|
|
29206
|
-
const rootDir =
|
|
29445
|
+
const rootDir = join12(targetDir, layout.rootDir);
|
|
29207
29446
|
return fileExists(rootDir);
|
|
29208
29447
|
}
|
|
29209
29448
|
var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
@@ -29217,13 +29456,13 @@ var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
|
29217
29456
|
function componentToPath(targetDir, component) {
|
|
29218
29457
|
if (component === "entry-md") {
|
|
29219
29458
|
const layout = getProviderLayout();
|
|
29220
|
-
return
|
|
29459
|
+
return join12(targetDir, layout.entryFile);
|
|
29221
29460
|
}
|
|
29222
29461
|
if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
|
|
29223
29462
|
const layout = getProviderLayout();
|
|
29224
|
-
return
|
|
29463
|
+
return join12(targetDir, layout.rootDir, component);
|
|
29225
29464
|
}
|
|
29226
|
-
return
|
|
29465
|
+
return join12(targetDir, component);
|
|
29227
29466
|
}
|
|
29228
29467
|
function buildInstalledPaths(targetDir, components) {
|
|
29229
29468
|
return components.map((component) => componentToPath(targetDir, component));
|
|
@@ -29323,6 +29562,9 @@ async function initCommand(options) {
|
|
|
29323
29562
|
const existing = await readLockFile(targetDir);
|
|
29324
29563
|
await writeLockFile(targetDir, package_default.version, existing);
|
|
29325
29564
|
} catch {}
|
|
29565
|
+
try {
|
|
29566
|
+
await registerProject(targetDir, package_default.version);
|
|
29567
|
+
} catch {}
|
|
29326
29568
|
console.log("");
|
|
29327
29569
|
console.log("Required plugins (install manually):");
|
|
29328
29570
|
console.log(" /plugin marketplace add obra/superpowers-marketplace");
|
|
@@ -29345,7 +29587,7 @@ async function initCommand(options) {
|
|
|
29345
29587
|
}
|
|
29346
29588
|
|
|
29347
29589
|
// src/cli/list.ts
|
|
29348
|
-
import { basename as
|
|
29590
|
+
import { basename as basename5, dirname as dirname3, join as join13, relative as relative3 } from "node:path";
|
|
29349
29591
|
init_fs();
|
|
29350
29592
|
var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
|
|
29351
29593
|
function parseKeyValue(line) {
|
|
@@ -29392,7 +29634,7 @@ function parseYamlMetadata(content) {
|
|
|
29392
29634
|
return result;
|
|
29393
29635
|
}
|
|
29394
29636
|
function extractAgentTypeFromFilename(filename) {
|
|
29395
|
-
const name =
|
|
29637
|
+
const name = basename5(filename, ".md");
|
|
29396
29638
|
const prefixMap = {
|
|
29397
29639
|
lang: "language",
|
|
29398
29640
|
be: "backend",
|
|
@@ -29410,17 +29652,17 @@ function extractAgentTypeFromFilename(filename) {
|
|
|
29410
29652
|
return prefixMap[prefix] || "unknown";
|
|
29411
29653
|
}
|
|
29412
29654
|
function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
|
|
29413
|
-
const relativePath = relative3(
|
|
29655
|
+
const relativePath = relative3(join13(baseDir, rootDir, "skills"), skillPath);
|
|
29414
29656
|
const parts = relativePath.split("/").filter(Boolean);
|
|
29415
29657
|
return parts[0] || "unknown";
|
|
29416
29658
|
}
|
|
29417
29659
|
function extractGuideCategoryFromPath(guidePath, baseDir) {
|
|
29418
|
-
const relativePath = relative3(
|
|
29660
|
+
const relativePath = relative3(join13(baseDir, "guides"), guidePath);
|
|
29419
29661
|
const parts = relativePath.split("/").filter(Boolean);
|
|
29420
29662
|
return parts[0] || "unknown";
|
|
29421
29663
|
}
|
|
29422
29664
|
function extractRulePriorityFromFilename(filename) {
|
|
29423
|
-
const name =
|
|
29665
|
+
const name = basename5(filename, ".md");
|
|
29424
29666
|
const parts = name.split("-");
|
|
29425
29667
|
return parts[0] || "unknown";
|
|
29426
29668
|
}
|
|
@@ -29509,7 +29751,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
|
|
|
29509
29751
|
}
|
|
29510
29752
|
}
|
|
29511
29753
|
async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
29512
|
-
const agentsDir =
|
|
29754
|
+
const agentsDir = join13(targetDir, rootDir, "agents");
|
|
29513
29755
|
if (!await fileExists(agentsDir))
|
|
29514
29756
|
return [];
|
|
29515
29757
|
try {
|
|
@@ -29518,8 +29760,8 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
29518
29760
|
const customAgentPaths = new Set(customComponents.filter((c) => c.type === "agent").map((c) => c.path));
|
|
29519
29761
|
const agentMdFiles = await listFiles(agentsDir, { recursive: false, pattern: "*.md" });
|
|
29520
29762
|
const agents = await Promise.all(agentMdFiles.map(async (agentMdPath) => {
|
|
29521
|
-
const filename =
|
|
29522
|
-
const name =
|
|
29763
|
+
const filename = basename5(agentMdPath);
|
|
29764
|
+
const name = basename5(filename, ".md");
|
|
29523
29765
|
const description = await tryExtractMarkdownDescription(agentMdPath);
|
|
29524
29766
|
const relativePath = relative3(targetDir, agentMdPath);
|
|
29525
29767
|
return {
|
|
@@ -29537,7 +29779,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
29537
29779
|
}
|
|
29538
29780
|
}
|
|
29539
29781
|
async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
29540
|
-
const skillsDir =
|
|
29782
|
+
const skillsDir = join13(targetDir, rootDir, "skills");
|
|
29541
29783
|
if (!await fileExists(skillsDir))
|
|
29542
29784
|
return [];
|
|
29543
29785
|
try {
|
|
@@ -29546,12 +29788,12 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
29546
29788
|
const customSkillPaths = new Set(customComponents.filter((c) => c.type === "skill").map((c) => c.path));
|
|
29547
29789
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
29548
29790
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
29549
|
-
const skillDir =
|
|
29550
|
-
const indexYamlPath =
|
|
29791
|
+
const skillDir = dirname3(skillMdPath);
|
|
29792
|
+
const indexYamlPath = join13(skillDir, "index.yaml");
|
|
29551
29793
|
const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
|
|
29552
29794
|
const relativePath = relative3(targetDir, skillDir);
|
|
29553
29795
|
return {
|
|
29554
|
-
name:
|
|
29796
|
+
name: basename5(skillDir),
|
|
29555
29797
|
type: "skill",
|
|
29556
29798
|
category: extractSkillCategoryFromPath(skillDir, targetDir, rootDir),
|
|
29557
29799
|
path: relativePath,
|
|
@@ -29566,7 +29808,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
29566
29808
|
}
|
|
29567
29809
|
}
|
|
29568
29810
|
async function getGuides(targetDir, config) {
|
|
29569
|
-
const guidesDir =
|
|
29811
|
+
const guidesDir = join13(targetDir, "guides");
|
|
29570
29812
|
if (!await fileExists(guidesDir))
|
|
29571
29813
|
return [];
|
|
29572
29814
|
try {
|
|
@@ -29578,7 +29820,7 @@ async function getGuides(targetDir, config) {
|
|
|
29578
29820
|
const description = await tryExtractMarkdownDescription(guideMdPath, { maxLength: 100 });
|
|
29579
29821
|
const relativePath = relative3(targetDir, guideMdPath);
|
|
29580
29822
|
return {
|
|
29581
|
-
name:
|
|
29823
|
+
name: basename5(guideMdPath, ".md"),
|
|
29582
29824
|
type: "guide",
|
|
29583
29825
|
category: extractGuideCategoryFromPath(guideMdPath, targetDir),
|
|
29584
29826
|
path: relativePath,
|
|
@@ -29593,7 +29835,7 @@ async function getGuides(targetDir, config) {
|
|
|
29593
29835
|
}
|
|
29594
29836
|
var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
|
|
29595
29837
|
async function getRules(targetDir, rootDir = ".claude", config) {
|
|
29596
|
-
const rulesDir =
|
|
29838
|
+
const rulesDir = join13(targetDir, rootDir, "rules");
|
|
29597
29839
|
if (!await fileExists(rulesDir))
|
|
29598
29840
|
return [];
|
|
29599
29841
|
try {
|
|
@@ -29602,13 +29844,13 @@ async function getRules(targetDir, rootDir = ".claude", config) {
|
|
|
29602
29844
|
const customRulePaths = new Set(customComponents.filter((c) => c.type === "rule").map((c) => c.path));
|
|
29603
29845
|
const ruleMdFiles = await listFiles(rulesDir, { recursive: false, pattern: "*.md" });
|
|
29604
29846
|
const rules = await Promise.all(ruleMdFiles.map(async (ruleMdPath) => {
|
|
29605
|
-
const filename =
|
|
29847
|
+
const filename = basename5(ruleMdPath);
|
|
29606
29848
|
const description = await tryExtractMarkdownDescription(ruleMdPath, {
|
|
29607
29849
|
cleanFormatting: true
|
|
29608
29850
|
});
|
|
29609
29851
|
const relativePath = relative3(targetDir, ruleMdPath);
|
|
29610
29852
|
return {
|
|
29611
|
-
name:
|
|
29853
|
+
name: basename5(ruleMdPath, ".md"),
|
|
29612
29854
|
type: extractRulePriorityFromFilename(filename),
|
|
29613
29855
|
path: relativePath,
|
|
29614
29856
|
description,
|
|
@@ -29665,7 +29907,7 @@ function formatAsJson(components) {
|
|
|
29665
29907
|
console.log(JSON.stringify(components, null, 2));
|
|
29666
29908
|
}
|
|
29667
29909
|
async function getHooks(targetDir, rootDir = ".claude") {
|
|
29668
|
-
const hooksDir =
|
|
29910
|
+
const hooksDir = join13(targetDir, rootDir, "hooks");
|
|
29669
29911
|
if (!await fileExists(hooksDir))
|
|
29670
29912
|
return [];
|
|
29671
29913
|
try {
|
|
@@ -29674,7 +29916,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
29674
29916
|
const hookYamls = await listFiles(hooksDir, { recursive: true, pattern: "*.yaml" });
|
|
29675
29917
|
const allFiles = [...hookFiles, ...hookConfigs, ...hookYamls];
|
|
29676
29918
|
return allFiles.map((hookPath) => ({
|
|
29677
|
-
name:
|
|
29919
|
+
name: basename5(hookPath),
|
|
29678
29920
|
type: "hook",
|
|
29679
29921
|
path: relative3(targetDir, hookPath)
|
|
29680
29922
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -29683,7 +29925,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
29683
29925
|
}
|
|
29684
29926
|
}
|
|
29685
29927
|
async function getContexts(targetDir, rootDir = ".claude") {
|
|
29686
|
-
const contextsDir =
|
|
29928
|
+
const contextsDir = join13(targetDir, rootDir, "contexts");
|
|
29687
29929
|
if (!await fileExists(contextsDir))
|
|
29688
29930
|
return [];
|
|
29689
29931
|
try {
|
|
@@ -29694,7 +29936,7 @@ async function getContexts(targetDir, rootDir = ".claude") {
|
|
|
29694
29936
|
const ext = ctxPath.endsWith(".md") ? ".md" : ".yaml";
|
|
29695
29937
|
const description = ext === ".md" ? await tryExtractMarkdownDescription(ctxPath, { maxLength: 100 }) : undefined;
|
|
29696
29938
|
return {
|
|
29697
|
-
name:
|
|
29939
|
+
name: basename5(ctxPath, ext),
|
|
29698
29940
|
type: "context",
|
|
29699
29941
|
path: relative3(targetDir, ctxPath),
|
|
29700
29942
|
description
|
|
@@ -30076,29 +30318,29 @@ async function securityCommand(_options = {}) {
|
|
|
30076
30318
|
|
|
30077
30319
|
// src/cli/serve-commands.ts
|
|
30078
30320
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
30079
|
-
import { join as
|
|
30321
|
+
import { join as join15 } from "node:path";
|
|
30080
30322
|
|
|
30081
30323
|
// src/cli/serve.ts
|
|
30082
30324
|
import { spawn } from "node:child_process";
|
|
30083
30325
|
import { existsSync as existsSync3 } from "node:fs";
|
|
30084
|
-
import { readFile as
|
|
30085
|
-
import { join as
|
|
30326
|
+
import { readFile as readFile3, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
30327
|
+
import { join as join14 } from "node:path";
|
|
30086
30328
|
var DEFAULT_PORT = 4321;
|
|
30087
|
-
var PID_FILE =
|
|
30329
|
+
var PID_FILE = join14(process.env.HOME ?? "~", ".omcustom-serve.pid");
|
|
30088
30330
|
function findServeBuildDir(projectRoot, options) {
|
|
30089
|
-
const localBuild =
|
|
30090
|
-
if (existsSync3(
|
|
30331
|
+
const localBuild = join14(projectRoot, "packages", "serve", "build");
|
|
30332
|
+
if (existsSync3(join14(localBuild, "index.js")))
|
|
30091
30333
|
return localBuild;
|
|
30092
30334
|
if (options?.skipNpmFallback !== true) {
|
|
30093
|
-
const npmBuild =
|
|
30094
|
-
if (existsSync3(
|
|
30335
|
+
const npmBuild = join14(import.meta.dirname, "..", "..", "packages", "serve", "build");
|
|
30336
|
+
if (existsSync3(join14(npmBuild, "index.js")))
|
|
30095
30337
|
return npmBuild;
|
|
30096
30338
|
}
|
|
30097
30339
|
return null;
|
|
30098
30340
|
}
|
|
30099
30341
|
async function isServeRunning() {
|
|
30100
30342
|
try {
|
|
30101
|
-
const raw = await
|
|
30343
|
+
const raw = await readFile3(PID_FILE, "utf-8");
|
|
30102
30344
|
const pid = Number(raw.trim());
|
|
30103
30345
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
30104
30346
|
await cleanupPidFile();
|
|
@@ -30119,7 +30361,7 @@ async function startServeBackground(projectRoot, port = DEFAULT_PORT, buildDirOp
|
|
|
30119
30361
|
if (buildDir === null) {
|
|
30120
30362
|
return;
|
|
30121
30363
|
}
|
|
30122
|
-
const child = spawn("node", [
|
|
30364
|
+
const child = spawn("node", [join14(buildDir, "index.js")], {
|
|
30123
30365
|
env: {
|
|
30124
30366
|
...process.env,
|
|
30125
30367
|
OMCUSTOM_PORT: String(port),
|
|
@@ -30132,12 +30374,12 @@ async function startServeBackground(projectRoot, port = DEFAULT_PORT, buildDirOp
|
|
|
30132
30374
|
});
|
|
30133
30375
|
child.unref();
|
|
30134
30376
|
if (child.pid !== undefined) {
|
|
30135
|
-
await
|
|
30377
|
+
await writeFile3(PID_FILE, String(child.pid), "utf-8");
|
|
30136
30378
|
}
|
|
30137
30379
|
}
|
|
30138
30380
|
async function stopServe() {
|
|
30139
30381
|
try {
|
|
30140
|
-
const raw = await
|
|
30382
|
+
const raw = await readFile3(PID_FILE, "utf-8");
|
|
30141
30383
|
const pid = Number(raw.trim());
|
|
30142
30384
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
30143
30385
|
await cleanupPidFile();
|
|
@@ -30196,7 +30438,7 @@ function runForeground(projectRoot, port, buildDirOpts) {
|
|
|
30196
30438
|
process.exit(1);
|
|
30197
30439
|
}
|
|
30198
30440
|
console.log(`Web UI: http://localhost:${port}`);
|
|
30199
|
-
spawnSync2("node", [
|
|
30441
|
+
spawnSync2("node", [join15(buildDir, "index.js")], {
|
|
30200
30442
|
env: {
|
|
30201
30443
|
...process.env,
|
|
30202
30444
|
OMCUSTOM_PORT: String(port),
|
|
@@ -30209,18 +30451,18 @@ function runForeground(projectRoot, port, buildDirOpts) {
|
|
|
30209
30451
|
}
|
|
30210
30452
|
|
|
30211
30453
|
// src/cli/sync.ts
|
|
30212
|
-
import { resolve as
|
|
30454
|
+
import { resolve as resolve3 } from "node:path";
|
|
30213
30455
|
|
|
30214
30456
|
// src/core/sync.ts
|
|
30215
30457
|
init_fs();
|
|
30216
30458
|
import { existsSync as existsSync4 } from "node:fs";
|
|
30217
|
-
import { cp as cp2, mkdir } from "node:fs/promises";
|
|
30218
|
-
import { join as
|
|
30459
|
+
import { cp as cp2, mkdir as mkdir2 } from "node:fs/promises";
|
|
30460
|
+
import { join as join16 } from "node:path";
|
|
30219
30461
|
async function loadVersions() {
|
|
30220
30462
|
try {
|
|
30221
30463
|
const packageRoot = getPackageRoot();
|
|
30222
|
-
const manifest = await readJsonFile(
|
|
30223
|
-
const pkg = await readJsonFile(
|
|
30464
|
+
const manifest = await readJsonFile(join16(packageRoot, "templates", "manifest.json"));
|
|
30465
|
+
const pkg = await readJsonFile(join16(packageRoot, "package.json"));
|
|
30224
30466
|
return { generatorVersion: pkg.version, templateVersion: manifest.version };
|
|
30225
30467
|
} catch {
|
|
30226
30468
|
return { generatorVersion: "0.0.0", templateVersion: "0.0.0" };
|
|
@@ -30290,7 +30532,7 @@ async function countFiles(dir2) {
|
|
|
30290
30532
|
return 0;
|
|
30291
30533
|
}
|
|
30292
30534
|
for (const entry of entries) {
|
|
30293
|
-
const full =
|
|
30535
|
+
const full = join16(current, entry);
|
|
30294
30536
|
try {
|
|
30295
30537
|
const s = await stat3(full);
|
|
30296
30538
|
if (s.isDirectory()) {
|
|
@@ -30305,19 +30547,19 @@ async function countFiles(dir2) {
|
|
|
30305
30547
|
return walk(dir2);
|
|
30306
30548
|
}
|
|
30307
30549
|
async function exportSnapshot(targetDir, outputPath) {
|
|
30308
|
-
const claudeDir =
|
|
30309
|
-
const guidesDir =
|
|
30550
|
+
const claudeDir = join16(targetDir, ".claude");
|
|
30551
|
+
const guidesDir = join16(targetDir, "guides");
|
|
30310
30552
|
if (!existsSync4(claudeDir)) {
|
|
30311
30553
|
return { success: false, exportPath: outputPath, fileCount: 0 };
|
|
30312
30554
|
}
|
|
30313
|
-
await
|
|
30314
|
-
const destClaude =
|
|
30555
|
+
await mkdir2(outputPath, { recursive: true });
|
|
30556
|
+
const destClaude = join16(outputPath, ".claude");
|
|
30315
30557
|
await cp2(claudeDir, destClaude, {
|
|
30316
30558
|
recursive: true,
|
|
30317
30559
|
filter: isExportable
|
|
30318
30560
|
});
|
|
30319
30561
|
if (existsSync4(guidesDir)) {
|
|
30320
|
-
await cp2(guidesDir,
|
|
30562
|
+
await cp2(guidesDir, join16(outputPath, "guides"), { recursive: true });
|
|
30321
30563
|
}
|
|
30322
30564
|
const lockfile = await generateCurrentLockfile(targetDir);
|
|
30323
30565
|
if (lockfile) {
|
|
@@ -30329,7 +30571,7 @@ async function exportSnapshot(targetDir, outputPath) {
|
|
|
30329
30571
|
|
|
30330
30572
|
// src/cli/sync.ts
|
|
30331
30573
|
async function runExport(targetDir, outputPath) {
|
|
30332
|
-
const result = await exportSnapshot(targetDir,
|
|
30574
|
+
const result = await exportSnapshot(targetDir, resolve3(outputPath));
|
|
30333
30575
|
if (!result.success) {
|
|
30334
30576
|
console.error(`
|
|
30335
30577
|
Export failed — no .claude/ directory found in current project.`);
|
|
@@ -30386,7 +30628,7 @@ Summary: ${result.unchanged} unchanged, ${result.modified.length} modified, ${re
|
|
|
30386
30628
|
}
|
|
30387
30629
|
function syncCommand(program2) {
|
|
30388
30630
|
program2.command("sync").description(i18n.t("cli.sync.description")).option("--check", "Compare current state against lockfile (default behavior)").option("--reference <path>", "Compare against an external snapshot instead of the lockfile").option("--export <path>", "Export current .claude/ state as a reusable snapshot").action(async (options) => {
|
|
30389
|
-
const targetDir =
|
|
30631
|
+
const targetDir = resolve3(".");
|
|
30390
30632
|
if (options.export) {
|
|
30391
30633
|
await runExport(targetDir, options.export);
|
|
30392
30634
|
return;
|
|
@@ -30400,7 +30642,7 @@ init_package();
|
|
|
30400
30642
|
|
|
30401
30643
|
// src/core/updater.ts
|
|
30402
30644
|
init_package();
|
|
30403
|
-
import { join as
|
|
30645
|
+
import { join as join17 } from "node:path";
|
|
30404
30646
|
init_fs();
|
|
30405
30647
|
|
|
30406
30648
|
// src/core/entry-merger.ts
|
|
@@ -30655,7 +30897,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
|
|
|
30655
30897
|
}
|
|
30656
30898
|
async function updateEntryDoc(targetDir, config, options) {
|
|
30657
30899
|
const layout = getProviderLayout();
|
|
30658
|
-
const entryPath =
|
|
30900
|
+
const entryPath = join17(targetDir, layout.entryFile);
|
|
30659
30901
|
const templateName = getEntryTemplateName2(config.language);
|
|
30660
30902
|
const templatePath = resolveTemplatePath(templateName);
|
|
30661
30903
|
if (!await fileExists(templatePath)) {
|
|
@@ -30734,6 +30976,36 @@ function checkAndInstallRtkAfterUpdate() {
|
|
|
30734
30976
|
}
|
|
30735
30977
|
}
|
|
30736
30978
|
}
|
|
30979
|
+
async function updateProjectRegistry(targetDir, newVersion) {
|
|
30980
|
+
try {
|
|
30981
|
+
const { registerProject: registerProject2 } = await Promise.resolve().then(() => (init_registry(), exports_registry));
|
|
30982
|
+
await registerProject2(targetDir, newVersion);
|
|
30983
|
+
} catch {}
|
|
30984
|
+
}
|
|
30985
|
+
async function regenerateLockfile(targetDir, result) {
|
|
30986
|
+
const lockfileResult = await generateAndWriteLockfileForDir(targetDir);
|
|
30987
|
+
if (lockfileResult.warning) {
|
|
30988
|
+
result.warnings.push(lockfileResult.warning);
|
|
30989
|
+
warn("update.lockfile_failed", { error: lockfileResult.warning });
|
|
30990
|
+
} else {
|
|
30991
|
+
debug("update.lockfile_regenerated", {
|
|
30992
|
+
files: String(lockfileResult.fileCount)
|
|
30993
|
+
});
|
|
30994
|
+
}
|
|
30995
|
+
}
|
|
30996
|
+
async function shouldSkipSelfUpdate2(targetDir, result) {
|
|
30997
|
+
const targetPkgPath = join17(targetDir, "package.json");
|
|
30998
|
+
if (await fileExists(targetPkgPath)) {
|
|
30999
|
+
const targetPkg = await readJsonFile(targetPkgPath);
|
|
31000
|
+
if (targetPkg.name === "oh-my-customcode") {
|
|
31001
|
+
warn("update.self_update_skipped");
|
|
31002
|
+
result.success = true;
|
|
31003
|
+
result.warnings.push("Skipped: source project cannot update itself");
|
|
31004
|
+
return true;
|
|
31005
|
+
}
|
|
31006
|
+
}
|
|
31007
|
+
return false;
|
|
31008
|
+
}
|
|
30737
31009
|
function checkAndInstallCodexAfterUpdate() {
|
|
30738
31010
|
if (!isCodexInstalled()) {
|
|
30739
31011
|
warn("update.codex_missing");
|
|
@@ -30756,15 +31028,8 @@ async function update(options) {
|
|
|
30756
31028
|
result.error = `Downgrade prevented: project has v${result.previousVersion} but CLI is v${cliVersion}. Update the CLI first: npm install -g oh-my-customcode@latest`;
|
|
30757
31029
|
return result;
|
|
30758
31030
|
}
|
|
30759
|
-
|
|
30760
|
-
|
|
30761
|
-
const targetPkg = await readJsonFile(targetPkgPath);
|
|
30762
|
-
if (targetPkg.name === "oh-my-customcode") {
|
|
30763
|
-
warn("update.self_update_skipped");
|
|
30764
|
-
result.success = true;
|
|
30765
|
-
result.warnings.push("Skipped: source project cannot update itself");
|
|
30766
|
-
return result;
|
|
30767
|
-
}
|
|
31031
|
+
if (await shouldSkipSelfUpdate2(options.targetDir, result)) {
|
|
31032
|
+
return result;
|
|
30768
31033
|
}
|
|
30769
31034
|
const updateCheck = await checkForUpdates(options.targetDir);
|
|
30770
31035
|
result.newVersion = updateCheck.latestVersion;
|
|
@@ -30782,17 +31047,12 @@ async function update(options) {
|
|
|
30782
31047
|
const components = options.components || getAllUpdateComponents();
|
|
30783
31048
|
await updateAllComponents(options.targetDir, components, updateCheck, customizations, options, result, config, lockfile);
|
|
30784
31049
|
await runFullUpdatePostProcessing(options, result, config);
|
|
30785
|
-
|
|
30786
|
-
if (lockfileResult.warning) {
|
|
30787
|
-
result.warnings.push(lockfileResult.warning);
|
|
30788
|
-
warn("update.lockfile_failed", { error: lockfileResult.warning });
|
|
30789
|
-
} else {
|
|
30790
|
-
debug("update.lockfile_regenerated", {
|
|
30791
|
-
files: String(lockfileResult.fileCount)
|
|
30792
|
-
});
|
|
30793
|
-
}
|
|
31050
|
+
await regenerateLockfile(options.targetDir, result);
|
|
30794
31051
|
checkAndInstallRtkAfterUpdate();
|
|
30795
31052
|
checkAndInstallCodexAfterUpdate();
|
|
31053
|
+
if (result.success && !options.dryRun) {
|
|
31054
|
+
await updateProjectRegistry(options.targetDir, result.newVersion);
|
|
31055
|
+
}
|
|
30796
31056
|
} catch (err) {
|
|
30797
31057
|
const message = err instanceof Error ? err.message : String(err);
|
|
30798
31058
|
result.error = message;
|
|
@@ -30872,11 +31132,11 @@ async function collectProtectedSkipPaths(srcPath, destPath, componentPath, force
|
|
|
30872
31132
|
const warnedPaths = [];
|
|
30873
31133
|
const updatedPaths = [];
|
|
30874
31134
|
for (const p of protectedRelative) {
|
|
30875
|
-
const targetFilePath =
|
|
31135
|
+
const targetFilePath = join17(targetDir, componentPath, p);
|
|
30876
31136
|
const lockfileKey = `${componentPath}/${p}`.replace(/\\/g, "/");
|
|
30877
31137
|
const shouldSkip = await shouldSkipProtectedFile(targetFilePath, lockfileKey, lockfile);
|
|
30878
31138
|
if (shouldSkip) {
|
|
30879
|
-
skipPaths.push(path3.relative(destPath,
|
|
31139
|
+
skipPaths.push(path3.relative(destPath, join17(destPath, p)));
|
|
30880
31140
|
warnedPaths.push(p);
|
|
30881
31141
|
} else {
|
|
30882
31142
|
updatedPaths.push(p);
|
|
@@ -30922,7 +31182,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
30922
31182
|
const preservedFiles = [];
|
|
30923
31183
|
const componentPath = getComponentPath2(component);
|
|
30924
31184
|
const srcPath = resolveTemplatePath(componentPath);
|
|
30925
|
-
const destPath =
|
|
31185
|
+
const destPath = join17(targetDir, componentPath);
|
|
30926
31186
|
const customComponents = config.customComponents || [];
|
|
30927
31187
|
const skipPaths = [];
|
|
30928
31188
|
if (customizations && !options.forceOverwriteAll) {
|
|
@@ -30964,7 +31224,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
30964
31224
|
}
|
|
30965
31225
|
skipPaths.push(...protectedSkipPaths);
|
|
30966
31226
|
const path3 = await import("node:path");
|
|
30967
|
-
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath,
|
|
31227
|
+
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join17(targetDir, p)));
|
|
30968
31228
|
const uniqueSkipPaths = [...new Set(normalizedSkipPaths)];
|
|
30969
31229
|
await copyDirectory(srcPath, destPath, {
|
|
30970
31230
|
overwrite: true,
|
|
@@ -30986,12 +31246,12 @@ async function syncRootLevelFiles(targetDir, options) {
|
|
|
30986
31246
|
const layout = getProviderLayout();
|
|
30987
31247
|
const synced = [];
|
|
30988
31248
|
for (const fileName of ROOT_LEVEL_FILES) {
|
|
30989
|
-
const srcPath = resolveTemplatePath(
|
|
31249
|
+
const srcPath = resolveTemplatePath(join17(layout.rootDir, fileName));
|
|
30990
31250
|
if (!await fileExists(srcPath)) {
|
|
30991
31251
|
continue;
|
|
30992
31252
|
}
|
|
30993
|
-
const destPath =
|
|
30994
|
-
await ensureDirectory(
|
|
31253
|
+
const destPath = join17(targetDir, layout.rootDir, fileName);
|
|
31254
|
+
await ensureDirectory(join17(destPath, ".."));
|
|
30995
31255
|
await fs3.copyFile(srcPath, destPath);
|
|
30996
31256
|
if (fileName.endsWith(".sh")) {
|
|
30997
31257
|
await fs3.chmod(destPath, 493);
|
|
@@ -31026,7 +31286,7 @@ async function removeDeprecatedFiles(targetDir, options) {
|
|
|
31026
31286
|
});
|
|
31027
31287
|
continue;
|
|
31028
31288
|
}
|
|
31029
|
-
const fullPath =
|
|
31289
|
+
const fullPath = join17(targetDir, entry.path);
|
|
31030
31290
|
if (await fileExists(fullPath)) {
|
|
31031
31291
|
await fs3.unlink(fullPath);
|
|
31032
31292
|
removed.push(entry.path);
|
|
@@ -31067,7 +31327,7 @@ async function syncNamespaceInFile(targetFilePath, upstreamFilePath) {
|
|
|
31067
31327
|
async function processNamespaceSyncEntry(entry, relPath, fullSrcPath, destPath, componentPath, lockfile) {
|
|
31068
31328
|
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
31069
31329
|
return null;
|
|
31070
|
-
const targetFilePath =
|
|
31330
|
+
const targetFilePath = join17(destPath, relPath);
|
|
31071
31331
|
const lockfileKey = `${componentPath}/${relPath}`.replace(/\\/g, "/");
|
|
31072
31332
|
const shouldSkip = await shouldSkipProtectedFile(targetFilePath, lockfileKey, lockfile);
|
|
31073
31333
|
if (shouldSkip)
|
|
@@ -31082,7 +31342,7 @@ async function applyNamespaceSync(targetDir, component, lockfile) {
|
|
|
31082
31342
|
return [];
|
|
31083
31343
|
const componentPath = getComponentPath2(component);
|
|
31084
31344
|
const srcPath = resolveTemplatePath(componentPath);
|
|
31085
|
-
const destPath =
|
|
31345
|
+
const destPath = join17(targetDir, componentPath);
|
|
31086
31346
|
const fs3 = await import("node:fs/promises");
|
|
31087
31347
|
const synced = [];
|
|
31088
31348
|
const queue = [{ dir: srcPath, relDir: "" }];
|
|
@@ -31096,7 +31356,7 @@ async function applyNamespaceSync(targetDir, component, lockfile) {
|
|
|
31096
31356
|
}
|
|
31097
31357
|
for (const entry of entries) {
|
|
31098
31358
|
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
31099
|
-
const fullSrcPath =
|
|
31359
|
+
const fullSrcPath = join17(dir2, entry.name);
|
|
31100
31360
|
if (entry.isDirectory()) {
|
|
31101
31361
|
queue.push({ dir: fullSrcPath, relDir: relPath });
|
|
31102
31362
|
continue;
|
|
@@ -31119,26 +31379,26 @@ function getComponentPath2(component) {
|
|
|
31119
31379
|
}
|
|
31120
31380
|
async function backupInstallation(targetDir) {
|
|
31121
31381
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
31122
|
-
const backupDir =
|
|
31382
|
+
const backupDir = join17(targetDir, `.omcustom-backup-${timestamp}`);
|
|
31123
31383
|
const fs3 = await import("node:fs/promises");
|
|
31124
31384
|
await ensureDirectory(backupDir);
|
|
31125
31385
|
const layout = getProviderLayout();
|
|
31126
31386
|
const dirsToBackup = [layout.rootDir, "guides"];
|
|
31127
31387
|
for (const dir2 of dirsToBackup) {
|
|
31128
|
-
const srcPath =
|
|
31388
|
+
const srcPath = join17(targetDir, dir2);
|
|
31129
31389
|
if (await fileExists(srcPath)) {
|
|
31130
|
-
const destPath =
|
|
31390
|
+
const destPath = join17(backupDir, dir2);
|
|
31131
31391
|
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
31132
31392
|
}
|
|
31133
31393
|
}
|
|
31134
|
-
const entryPath =
|
|
31394
|
+
const entryPath = join17(targetDir, layout.entryFile);
|
|
31135
31395
|
if (await fileExists(entryPath)) {
|
|
31136
|
-
await fs3.copyFile(entryPath,
|
|
31396
|
+
await fs3.copyFile(entryPath, join17(backupDir, layout.entryFile));
|
|
31137
31397
|
}
|
|
31138
31398
|
return backupDir;
|
|
31139
31399
|
}
|
|
31140
31400
|
async function loadCustomizationManifest(targetDir) {
|
|
31141
|
-
const manifestPath =
|
|
31401
|
+
const manifestPath = join17(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
31142
31402
|
if (await fileExists(manifestPath)) {
|
|
31143
31403
|
return readJsonFile(manifestPath);
|
|
31144
31404
|
}
|
|
@@ -31158,7 +31418,21 @@ async function checkCliVersion(checkFn) {
|
|
|
31158
31418
|
} catch {}
|
|
31159
31419
|
}
|
|
31160
31420
|
async function updateCommand(options = {}, cliVersionCheck = checkSelfUpdate) {
|
|
31161
|
-
|
|
31421
|
+
if (!options.skipSelf) {
|
|
31422
|
+
try {
|
|
31423
|
+
const selfUpdateResult = executeSelfUpdate();
|
|
31424
|
+
if (selfUpdateResult.updated) {
|
|
31425
|
+
console.log(i18n.t("cli.update.selfUpdateDone", {
|
|
31426
|
+
from: selfUpdateResult.fromVersion,
|
|
31427
|
+
to: selfUpdateResult.toVersion
|
|
31428
|
+
}));
|
|
31429
|
+
}
|
|
31430
|
+
} catch {
|
|
31431
|
+
console.warn(i18n.t("cli.update.selfUpdateFailed"));
|
|
31432
|
+
}
|
|
31433
|
+
} else {
|
|
31434
|
+
await checkCliVersion(cliVersionCheck);
|
|
31435
|
+
}
|
|
31162
31436
|
try {
|
|
31163
31437
|
if (options.all) {
|
|
31164
31438
|
await updateAllProjects(options);
|
|
@@ -31382,7 +31656,7 @@ function createProgram() {
|
|
|
31382
31656
|
program2.command("init").description(i18n.t("cli.init.description")).option("-l, --lang <language>", i18n.t("cli.init.langOption")).option("--domain <domain>", "Install only agents/skills for specific domain (backend, frontend, data-engineering, devops)").option("--yes", "Skip interactive wizard, use defaults").option("--from-snapshot <path>", "Install from a pre-configured team snapshot directory").action(async (options) => {
|
|
31383
31657
|
await initCommand(options);
|
|
31384
31658
|
});
|
|
31385
|
-
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("--hard", i18n.t("cli.update.hardOption")).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("--all", i18n.t("cli.update.allOption")).action(async (options) => {
|
|
31659
|
+
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("--hard", i18n.t("cli.update.hardOption")).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("--all", i18n.t("cli.update.allOption")).option("--skip-self", "Skip self-update of oh-my-customcode package").action(async (options) => {
|
|
31386
31660
|
await updateCommand(options);
|
|
31387
31661
|
});
|
|
31388
31662
|
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").action(async (type, options) => {
|
|
@@ -31423,8 +31697,23 @@ function createProgram() {
|
|
|
31423
31697
|
console.warn(i18n.t("cli.web.deprecated.serveStop"));
|
|
31424
31698
|
await serveStopCommand();
|
|
31425
31699
|
});
|
|
31426
|
-
program2.command("projects").description("List all projects on this machine where oh-my-customcode is installed").option("-f, --format <format>", "Output format: table, json, or simple", "table").option("--path <dir>", "Additional search directory (can be specified multiple times)", (val, prev) => [...prev, val], []).action(async (options) => {
|
|
31427
|
-
await projectsCommand({
|
|
31700
|
+
program2.command("projects").description("List all projects on this machine where oh-my-customcode is installed").option("-f, --format <format>", "Output format: table, json, or simple", "table").option("--path <dir>", "Additional search directory (can be specified multiple times)", (val, prev) => [...prev, val], []).option("--migrate", "Scan for existing projects with lock files and import them into the registry").action(async (options) => {
|
|
31701
|
+
await projectsCommand({
|
|
31702
|
+
format: options.format,
|
|
31703
|
+
paths: options.path,
|
|
31704
|
+
migrate: options.migrate
|
|
31705
|
+
});
|
|
31706
|
+
});
|
|
31707
|
+
program2.command("unregister [path]").description("Remove a project from the local registry").action(async (projectPath) => {
|
|
31708
|
+
const targetPath = projectPath ?? process.cwd();
|
|
31709
|
+
try {
|
|
31710
|
+
await unregisterProject(targetPath);
|
|
31711
|
+
console.log(` 프로젝트가 레지스트리에서 제거되었습니다: ${targetPath}`);
|
|
31712
|
+
} catch (error2) {
|
|
31713
|
+
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
31714
|
+
console.error(` 레지스트리 제거 실패: ${msg}`);
|
|
31715
|
+
process.exitCode = 1;
|
|
31716
|
+
}
|
|
31428
31717
|
});
|
|
31429
31718
|
program2.hook("preAction", async (thisCommand, actionCommand) => {
|
|
31430
31719
|
const opts = thisCommand.optsWithGlobals();
|