oh-my-customcode 0.79.1 → 0.79.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +664 -346
- package/dist/index.js +212 -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,253 @@ 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
|
+
cleanRegistry: () => cleanRegistry,
|
|
2152
|
+
_setRegistryDirForTesting: () => _setRegistryDirForTesting
|
|
2153
|
+
});
|
|
2154
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2155
|
+
import { homedir } from "node:os";
|
|
2156
|
+
import { basename, join, resolve } from "node:path";
|
|
2157
|
+
function _setRegistryDirForTesting(dir) {
|
|
2158
|
+
_registryDirOverride = dir;
|
|
2159
|
+
}
|
|
2160
|
+
function registryDir() {
|
|
2161
|
+
if (_registryDirOverride !== undefined)
|
|
2162
|
+
return _registryDirOverride;
|
|
2163
|
+
const home = process.env.HOME ?? homedir();
|
|
2164
|
+
return join(home, ".oh-my-customcode");
|
|
2165
|
+
}
|
|
2166
|
+
function registryPath() {
|
|
2167
|
+
return join(registryDir(), "projects.json");
|
|
2168
|
+
}
|
|
2169
|
+
async function readRegistryRaw() {
|
|
2170
|
+
try {
|
|
2171
|
+
const content = await readFile(registryPath(), "utf-8");
|
|
2172
|
+
const parsed = JSON.parse(content);
|
|
2173
|
+
if (typeof parsed === "object" && parsed !== null && "projects" in parsed && typeof parsed.projects === "object" && parsed.projects !== null) {
|
|
2174
|
+
return parsed;
|
|
2175
|
+
}
|
|
2176
|
+
return { ...EMPTY_REGISTRY };
|
|
2177
|
+
} catch {
|
|
2178
|
+
return { ...EMPTY_REGISTRY };
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
async function writeRegistry(registry) {
|
|
2182
|
+
const dir = registryDir();
|
|
2183
|
+
await mkdir(dir, { recursive: true });
|
|
2184
|
+
await writeFile(registryPath(), JSON.stringify(registry, null, 2), "utf-8");
|
|
2185
|
+
}
|
|
2186
|
+
async function readRegistry() {
|
|
2187
|
+
return readRegistryRaw();
|
|
2188
|
+
}
|
|
2189
|
+
async function registerProject(projectPath, version) {
|
|
2190
|
+
const normalizedPath = resolve(projectPath);
|
|
2191
|
+
const registry = await readRegistryRaw();
|
|
2192
|
+
const existing = registry.projects[normalizedPath];
|
|
2193
|
+
const now = new Date().toISOString();
|
|
2194
|
+
registry.projects[normalizedPath] = {
|
|
2195
|
+
version,
|
|
2196
|
+
installedAt: existing?.installedAt ?? now,
|
|
2197
|
+
updatedAt: now
|
|
2198
|
+
};
|
|
2199
|
+
await writeRegistry(registry);
|
|
2200
|
+
}
|
|
2201
|
+
async function unregisterProject(projectPath) {
|
|
2202
|
+
const normalizedPath = resolve(projectPath);
|
|
2203
|
+
const registry = await readRegistryRaw();
|
|
2204
|
+
if (!(normalizedPath in registry.projects))
|
|
2205
|
+
return;
|
|
2206
|
+
delete registry.projects[normalizedPath];
|
|
2207
|
+
await writeRegistry(registry);
|
|
2208
|
+
}
|
|
2209
|
+
async function cleanRegistry() {
|
|
2210
|
+
const { access: fsAccess } = await import("node:fs/promises");
|
|
2211
|
+
const registry = await readRegistryRaw();
|
|
2212
|
+
const paths = Object.keys(registry.projects);
|
|
2213
|
+
let removed = 0;
|
|
2214
|
+
for (const projectPath of paths) {
|
|
2215
|
+
try {
|
|
2216
|
+
await fsAccess(projectPath);
|
|
2217
|
+
} catch {
|
|
2218
|
+
delete registry.projects[projectPath];
|
|
2219
|
+
removed++;
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
if (removed > 0) {
|
|
2223
|
+
await writeRegistry(registry);
|
|
2224
|
+
}
|
|
2225
|
+
return removed;
|
|
2226
|
+
}
|
|
2227
|
+
async function parseLockFile(lockPath) {
|
|
2228
|
+
try {
|
|
2229
|
+
const content = await readFile(lockPath, "utf-8");
|
|
2230
|
+
const lock = JSON.parse(content);
|
|
2231
|
+
const version = typeof lock.version === "string" ? lock.version : typeof lock.templateVersion === "string" ? lock.templateVersion : "0.0.0";
|
|
2232
|
+
const now = new Date().toISOString();
|
|
2233
|
+
return {
|
|
2234
|
+
version,
|
|
2235
|
+
installedAt: typeof lock.installedAt === "string" ? lock.installedAt : now,
|
|
2236
|
+
updatedAt: typeof lock.updatedAt === "string" ? lock.updatedAt : now
|
|
2237
|
+
};
|
|
2238
|
+
} catch {
|
|
2239
|
+
return null;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
async function migrateFromLockfiles(searchDirs) {
|
|
2243
|
+
const { readdir } = await import("node:fs/promises");
|
|
2244
|
+
const MAX_DEPTH = 3;
|
|
2245
|
+
const discovered = new Map;
|
|
2246
|
+
async function scan(dir, depth) {
|
|
2247
|
+
if (depth > MAX_DEPTH || discovered.has(dir))
|
|
2248
|
+
return;
|
|
2249
|
+
let entries;
|
|
2250
|
+
try {
|
|
2251
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
2252
|
+
} catch {
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
const entry = await parseLockFile(join(dir, ".omcustom.lock.json"));
|
|
2256
|
+
if (entry !== null) {
|
|
2257
|
+
discovered.set(resolve(dir), entry);
|
|
2258
|
+
return;
|
|
2259
|
+
}
|
|
2260
|
+
if (depth < MAX_DEPTH) {
|
|
2261
|
+
const subdirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SCAN_SKIP_DIRS.has(e.name));
|
|
2262
|
+
await Promise.all(subdirs.map((sub) => scan(join(dir, sub.name), depth + 1)));
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
await Promise.all(searchDirs.map((dir) => scan(dir, 0).catch(() => {})));
|
|
2266
|
+
if (discovered.size === 0)
|
|
2267
|
+
return 0;
|
|
2268
|
+
const registry = await readRegistryRaw();
|
|
2269
|
+
let imported = 0;
|
|
2270
|
+
for (const [projectPath, entry] of discovered) {
|
|
2271
|
+
if (!(projectPath in registry.projects)) {
|
|
2272
|
+
registry.projects[projectPath] = entry;
|
|
2273
|
+
imported++;
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
if (imported > 0) {
|
|
2277
|
+
await writeRegistry(registry);
|
|
2278
|
+
}
|
|
2279
|
+
return imported;
|
|
2280
|
+
}
|
|
2281
|
+
function registryToList(registry) {
|
|
2282
|
+
return Object.entries(registry.projects).map(([path, entry]) => ({
|
|
2283
|
+
name: basename(path),
|
|
2284
|
+
path,
|
|
2285
|
+
version: entry.version,
|
|
2286
|
+
installedAt: entry.installedAt,
|
|
2287
|
+
updatedAt: entry.updatedAt
|
|
2288
|
+
}));
|
|
2289
|
+
}
|
|
2290
|
+
var _registryDirOverride, EMPTY_REGISTRY, SCAN_SKIP_DIRS;
|
|
2291
|
+
var init_registry = __esm(() => {
|
|
2292
|
+
EMPTY_REGISTRY = { projects: {} };
|
|
2293
|
+
SCAN_SKIP_DIRS = new Set(["node_modules", "dist", "build", ".git"]);
|
|
2294
|
+
});
|
|
2295
|
+
|
|
2296
|
+
// package.json
|
|
2297
|
+
var package_default;
|
|
2298
|
+
var init_package = __esm(() => {
|
|
2299
|
+
package_default = {
|
|
2300
|
+
name: "oh-my-customcode",
|
|
2301
|
+
workspaces: [
|
|
2302
|
+
"packages/*"
|
|
2303
|
+
],
|
|
2304
|
+
version: "0.79.3",
|
|
2305
|
+
description: "Batteries-included agent harness for Claude Code",
|
|
2306
|
+
type: "module",
|
|
2307
|
+
bin: {
|
|
2308
|
+
omcustom: "./dist/cli/index.js"
|
|
2309
|
+
},
|
|
2310
|
+
main: "./dist/index.js",
|
|
2311
|
+
types: "./dist/index.d.ts",
|
|
2312
|
+
exports: {
|
|
2313
|
+
".": {
|
|
2314
|
+
import: "./dist/index.js",
|
|
2315
|
+
types: "./dist/index.d.ts"
|
|
2316
|
+
}
|
|
2317
|
+
},
|
|
2318
|
+
files: [
|
|
2319
|
+
"dist",
|
|
2320
|
+
"templates"
|
|
2321
|
+
],
|
|
2322
|
+
publishConfig: {
|
|
2323
|
+
access: "public",
|
|
2324
|
+
registry: "https://registry.npmjs.org/"
|
|
2325
|
+
},
|
|
2326
|
+
scripts: {
|
|
2327
|
+
dev: "bun run src/cli/index.ts",
|
|
2328
|
+
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",
|
|
2329
|
+
test: "bun test tests/ packages/eval-core/",
|
|
2330
|
+
"test:unit": "bun test tests/unit",
|
|
2331
|
+
"test:integration": "bun test tests/integration",
|
|
2332
|
+
"test:e2e": "bun test tests/e2e",
|
|
2333
|
+
"test:coverage": "bun test --coverage",
|
|
2334
|
+
lint: "biome check src/ tests/ scripts/",
|
|
2335
|
+
"lint:fix": "biome check --write src/ tests/ scripts/",
|
|
2336
|
+
format: "biome format --write src/ tests/ scripts/",
|
|
2337
|
+
typecheck: "tsc --noEmit",
|
|
2338
|
+
"docs:dev": "vitepress dev docs",
|
|
2339
|
+
"docs:build": "vitepress build docs",
|
|
2340
|
+
prepare: "sh scripts/setup-hooks.sh || true",
|
|
2341
|
+
"setup:hooks": "sh scripts/setup-hooks.sh",
|
|
2342
|
+
prepublishOnly: "bun run build && bun run test"
|
|
2343
|
+
},
|
|
2344
|
+
dependencies: {
|
|
2345
|
+
"@clack/prompts": "^1.1.0",
|
|
2346
|
+
"@inquirer/prompts": "^8.3.2",
|
|
2347
|
+
commander: "^14.0.2",
|
|
2348
|
+
i18next: "^26.0.2",
|
|
2349
|
+
yaml: "^2.8.2"
|
|
2350
|
+
},
|
|
2351
|
+
devDependencies: {
|
|
2352
|
+
"@anthropic-ai/sdk": "^0.82.0",
|
|
2353
|
+
"@biomejs/biome": "^2.3.12",
|
|
2354
|
+
"@types/bun": "^1.3.6",
|
|
2355
|
+
"@types/js-yaml": "^4.0.9",
|
|
2356
|
+
"@types/nodemailer": "^8.0.0",
|
|
2357
|
+
"js-yaml": "^4.1.0",
|
|
2358
|
+
nodemailer: "^8.0.1",
|
|
2359
|
+
typescript: "^6.0.2",
|
|
2360
|
+
vitepress: "^1.6.4"
|
|
2361
|
+
},
|
|
2362
|
+
keywords: [
|
|
2363
|
+
"claude",
|
|
2364
|
+
"claude-code",
|
|
2365
|
+
"openai",
|
|
2366
|
+
"ai",
|
|
2367
|
+
"agent",
|
|
2368
|
+
"cli"
|
|
2369
|
+
],
|
|
2370
|
+
author: "baekenough",
|
|
2371
|
+
license: "MIT",
|
|
2372
|
+
repository: {
|
|
2373
|
+
type: "git",
|
|
2374
|
+
url: "git+https://github.com/baekenough/oh-my-customcode.git"
|
|
2375
|
+
},
|
|
2376
|
+
bugs: {
|
|
2377
|
+
url: "https://github.com/baekenough/oh-my-customcode/issues"
|
|
2378
|
+
},
|
|
2379
|
+
homepage: "https://github.com/baekenough/oh-my-customcode#readme",
|
|
2380
|
+
engines: {
|
|
2381
|
+
node: ">=18.0.0"
|
|
2382
|
+
},
|
|
2383
|
+
overrides: {
|
|
2384
|
+
rollup: "^4.59.0",
|
|
2385
|
+
esbuild: "^0.25.0"
|
|
2386
|
+
}
|
|
2387
|
+
};
|
|
2388
|
+
});
|
|
2389
|
+
|
|
2143
2390
|
// node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/nodes/identity.js
|
|
2144
2391
|
var require_identity = __commonJS((exports) => {
|
|
2145
2392
|
var ALIAS = Symbol.for("yaml.alias");
|
|
@@ -9068,7 +9315,7 @@ __export(exports_fs, {
|
|
|
9068
9315
|
copyDirectory: () => copyDirectory,
|
|
9069
9316
|
calculateChecksum: () => calculateChecksum
|
|
9070
9317
|
});
|
|
9071
|
-
import { dirname as dirname2, isAbsolute, join as
|
|
9318
|
+
import { dirname as dirname2, isAbsolute, join as join3, relative, resolve as resolve2, sep } from "node:path";
|
|
9072
9319
|
import { fileURLToPath } from "node:url";
|
|
9073
9320
|
function validatePreserveFilePath(filePath, projectRoot) {
|
|
9074
9321
|
if (!filePath || filePath.trim() === "") {
|
|
@@ -9083,7 +9330,7 @@ function validatePreserveFilePath(filePath, projectRoot) {
|
|
|
9083
9330
|
reason: "Absolute paths are not allowed"
|
|
9084
9331
|
};
|
|
9085
9332
|
}
|
|
9086
|
-
const resolvedPath =
|
|
9333
|
+
const resolvedPath = resolve2(projectRoot, filePath);
|
|
9087
9334
|
const relativePath = relative(projectRoot, resolvedPath);
|
|
9088
9335
|
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
9089
9336
|
return {
|
|
@@ -9229,11 +9476,11 @@ async function remove(path) {
|
|
|
9229
9476
|
function getPackageRoot() {
|
|
9230
9477
|
const currentFile = fileURLToPath(import.meta.url);
|
|
9231
9478
|
const currentDir = dirname2(currentFile);
|
|
9232
|
-
return
|
|
9479
|
+
return resolve2(currentDir, "..", "..");
|
|
9233
9480
|
}
|
|
9234
9481
|
function resolveTemplatePath(relativePath) {
|
|
9235
9482
|
const packageRoot = getPackageRoot();
|
|
9236
|
-
return
|
|
9483
|
+
return join3(packageRoot, "templates", relativePath);
|
|
9237
9484
|
}
|
|
9238
9485
|
async function listFiles(dir2, options = {}) {
|
|
9239
9486
|
const fs = await import("node:fs/promises");
|
|
@@ -9313,104 +9560,10 @@ function isAbsolutePath(inputPath) {
|
|
|
9313
9560
|
return isAbsolute(inputPath);
|
|
9314
9561
|
}
|
|
9315
9562
|
function resolvePath(...paths) {
|
|
9316
|
-
return
|
|
9563
|
+
return resolve2(...paths);
|
|
9317
9564
|
}
|
|
9318
9565
|
var init_fs = () => {};
|
|
9319
9566
|
|
|
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.1",
|
|
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
9567
|
// src/cli/projects.ts
|
|
9415
9568
|
var exports_projects = {};
|
|
9416
9569
|
__export(exports_projects, {
|
|
@@ -9420,10 +9573,10 @@ __export(exports_projects, {
|
|
|
9420
9573
|
findProjects: () => findProjects,
|
|
9421
9574
|
default: () => projects_default
|
|
9422
9575
|
});
|
|
9423
|
-
import { homedir as
|
|
9424
|
-
import { basename as
|
|
9576
|
+
import { homedir as homedir3 } from "node:os";
|
|
9577
|
+
import { basename as basename4, join as join10, sep as sep2 } from "node:path";
|
|
9425
9578
|
async function readLockFile(projectDir) {
|
|
9426
|
-
const lockFilePath =
|
|
9579
|
+
const lockFilePath = join10(projectDir, ".omcustom.lock.json");
|
|
9427
9580
|
try {
|
|
9428
9581
|
const fs2 = await import("node:fs/promises");
|
|
9429
9582
|
const content = await fs2.readFile(lockFilePath, "utf-8");
|
|
@@ -9432,20 +9585,6 @@ async function readLockFile(projectDir) {
|
|
|
9432
9585
|
return null;
|
|
9433
9586
|
}
|
|
9434
9587
|
}
|
|
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
9588
|
function computeStatus(version, currentVersion) {
|
|
9450
9589
|
if (!version)
|
|
9451
9590
|
return "unknown";
|
|
@@ -9461,48 +9600,6 @@ function computeStatus(version, currentVersion) {
|
|
|
9461
9600
|
}
|
|
9462
9601
|
return "latest";
|
|
9463
9602
|
}
|
|
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
9603
|
async function getTemplateVersion() {
|
|
9507
9604
|
const manifestPath = resolveTemplatePath("manifest.json");
|
|
9508
9605
|
if (await fileExists(manifestPath)) {
|
|
@@ -9513,13 +9610,74 @@ async function getTemplateVersion() {
|
|
|
9513
9610
|
}
|
|
9514
9611
|
async function findProjects(options = {}) {
|
|
9515
9612
|
const currentVersion = await getTemplateVersion();
|
|
9516
|
-
const
|
|
9613
|
+
const registry = await readRegistry();
|
|
9614
|
+
if (Object.keys(registry.projects).length === 0) {
|
|
9615
|
+
const fallbackResults = await _findProjectsFromLockfiles(options, currentVersion);
|
|
9616
|
+
if (fallbackResults.length === 0 && !options.paths) {
|
|
9617
|
+
process.stderr.write(" No projects in registry. Run `omcustom projects --migrate` to import existing projects.\n");
|
|
9618
|
+
}
|
|
9619
|
+
return fallbackResults;
|
|
9620
|
+
}
|
|
9621
|
+
const results = [];
|
|
9622
|
+
for (const [projectPath, entry] of Object.entries(registry.projects)) {
|
|
9623
|
+
if (options.paths && options.paths.length > 0) {
|
|
9624
|
+
const matchesPath = options.paths.some((searchPath) => projectPath === searchPath || projectPath.startsWith(searchPath + sep2));
|
|
9625
|
+
if (!matchesPath)
|
|
9626
|
+
continue;
|
|
9627
|
+
}
|
|
9628
|
+
results.push({
|
|
9629
|
+
name: basename4(projectPath),
|
|
9630
|
+
path: projectPath,
|
|
9631
|
+
version: entry.version || null,
|
|
9632
|
+
installedAt: entry.installedAt || null,
|
|
9633
|
+
updatedAt: entry.updatedAt || null,
|
|
9634
|
+
status: computeStatus(entry.version || null, currentVersion),
|
|
9635
|
+
detectionMethod: "registry"
|
|
9636
|
+
});
|
|
9637
|
+
}
|
|
9638
|
+
return results.sort((a, b) => {
|
|
9639
|
+
if (a.status === "latest" && b.status !== "latest")
|
|
9640
|
+
return -1;
|
|
9641
|
+
if (a.status !== "latest" && b.status === "latest")
|
|
9642
|
+
return 1;
|
|
9643
|
+
return a.name.localeCompare(b.name);
|
|
9644
|
+
});
|
|
9645
|
+
}
|
|
9646
|
+
async function _findProjectsFromLockfiles(options, currentVersion) {
|
|
9647
|
+
const { dirname: dirname3 } = await import("node:path");
|
|
9648
|
+
const fs2 = await import("node:fs/promises");
|
|
9517
9649
|
const seen = new Set;
|
|
9518
9650
|
const results = [];
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9651
|
+
async function scanDir(dir2, depth) {
|
|
9652
|
+
if (depth > 3 || seen.has(dir2))
|
|
9653
|
+
return;
|
|
9654
|
+
seen.add(dir2);
|
|
9655
|
+
let entries;
|
|
9656
|
+
try {
|
|
9657
|
+
entries = await fs2.readdir(dir2, { withFileTypes: true });
|
|
9658
|
+
} catch {
|
|
9659
|
+
return;
|
|
9660
|
+
}
|
|
9661
|
+
const lockFile = await readLockFile(dir2);
|
|
9662
|
+
if (lockFile) {
|
|
9663
|
+
const version = lockFile.version || lockFile.templateVersion || null;
|
|
9664
|
+
results.push({
|
|
9665
|
+
name: basename4(dir2),
|
|
9666
|
+
path: dir2,
|
|
9667
|
+
version,
|
|
9668
|
+
installedAt: lockFile.installedAt || null,
|
|
9669
|
+
updatedAt: lockFile.updatedAt || null,
|
|
9670
|
+
status: computeStatus(version, currentVersion),
|
|
9671
|
+
detectionMethod: "lockfile"
|
|
9672
|
+
});
|
|
9673
|
+
return;
|
|
9674
|
+
}
|
|
9675
|
+
if (depth < 3) {
|
|
9676
|
+
const subdirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist" && e.name !== "build" && e.name !== ".git");
|
|
9677
|
+
await Promise.all(subdirs.map((sub) => scanDir(join10(dir2, sub.name), depth + 1).catch(() => {})));
|
|
9678
|
+
}
|
|
9522
9679
|
}
|
|
9680
|
+
const searchPaths = options.paths ? [...options.paths] : [];
|
|
9523
9681
|
if (!options.paths) {
|
|
9524
9682
|
const cwd = process.cwd();
|
|
9525
9683
|
if (!searchPaths.includes(cwd))
|
|
@@ -9528,10 +9686,7 @@ async function findProjects(options = {}) {
|
|
|
9528
9686
|
if (parent !== cwd && !searchPaths.includes(parent))
|
|
9529
9687
|
searchPaths.push(parent);
|
|
9530
9688
|
}
|
|
9531
|
-
|
|
9532
|
-
searchPaths.push(...options.paths);
|
|
9533
|
-
}
|
|
9534
|
-
await Promise.all(searchPaths.map((searchPath) => searchDirectory(searchPath, 0, results, currentVersion, seen).catch(() => {})));
|
|
9689
|
+
await Promise.all(searchPaths.map((p) => scanDir(p, 0).catch(() => {})));
|
|
9535
9690
|
return results.sort((a, b) => {
|
|
9536
9691
|
if (a.status === "latest" && b.status !== "latest")
|
|
9537
9692
|
return -1;
|
|
@@ -9542,7 +9697,7 @@ async function findProjects(options = {}) {
|
|
|
9542
9697
|
}
|
|
9543
9698
|
async function writeLockFile(projectDir, version, existing) {
|
|
9544
9699
|
const fs2 = await import("node:fs/promises");
|
|
9545
|
-
const lockFilePath =
|
|
9700
|
+
const lockFilePath = join10(projectDir, ".omcustom.lock.json");
|
|
9546
9701
|
const now = new Date().toISOString();
|
|
9547
9702
|
const merged = {
|
|
9548
9703
|
...existing || {},
|
|
@@ -9556,8 +9711,7 @@ function formatProjectsTable(projects, currentVersion) {
|
|
|
9556
9711
|
if (projects.length === 0) {
|
|
9557
9712
|
console.log(`
|
|
9558
9713
|
oh-my-customcode가 적용된 프로젝트를 찾을 수 없습니다.`);
|
|
9559
|
-
console.log(
|
|
9560
|
-
`);
|
|
9714
|
+
console.log(" 레지스트리가 비어 있습니다. `omcustom projects --migrate`를 실행하여 기존 프로젝트를 가져오세요.\n");
|
|
9561
9715
|
return;
|
|
9562
9716
|
}
|
|
9563
9717
|
const nameWidth = Math.max(20, ...projects.map((p) => p.name.length));
|
|
@@ -9589,7 +9743,7 @@ function formatProjectsTable(projects, currentVersion) {
|
|
|
9589
9743
|
`);
|
|
9590
9744
|
}
|
|
9591
9745
|
function shortenPath(path2) {
|
|
9592
|
-
const home =
|
|
9746
|
+
const home = homedir3();
|
|
9593
9747
|
if (path2.startsWith(home)) {
|
|
9594
9748
|
return `~${path2.slice(home.length)}`;
|
|
9595
9749
|
}
|
|
@@ -9606,9 +9760,43 @@ oh-my-customcode 적용 프로젝트 (${projects.length}개):`);
|
|
|
9606
9760
|
console.log(`
|
|
9607
9761
|
현재 설치 버전: v${currentVersion}`);
|
|
9608
9762
|
}
|
|
9763
|
+
async function runMigration(options) {
|
|
9764
|
+
const { migrateFromLockfiles: migrateFromLockfiles2 } = await Promise.resolve().then(() => (init_registry(), exports_registry));
|
|
9765
|
+
const { homedir: _homedir } = await import("node:os");
|
|
9766
|
+
const DEFAULT_SEARCH_DIRS = ["workspace", "projects", "dev", "src", "code", "repos", "work"];
|
|
9767
|
+
const home = _homedir();
|
|
9768
|
+
const searchDirs = [...DEFAULT_SEARCH_DIRS.map((d) => join10(home, d)), ...options.paths ?? []];
|
|
9769
|
+
const cwd = process.cwd();
|
|
9770
|
+
if (!searchDirs.includes(cwd))
|
|
9771
|
+
searchDirs.push(cwd);
|
|
9772
|
+
console.log(" 레지스트리 마이그레이션 시작...");
|
|
9773
|
+
try {
|
|
9774
|
+
const imported = await migrateFromLockfiles2(searchDirs);
|
|
9775
|
+
console.log(` 마이그레이션 완료: ${imported}개 프로젝트가 레지스트리에 추가되었습니다.`);
|
|
9776
|
+
return null;
|
|
9777
|
+
} catch (error2) {
|
|
9778
|
+
return error2 instanceof Error ? error2.message : String(error2);
|
|
9779
|
+
}
|
|
9780
|
+
}
|
|
9609
9781
|
async function projectsCommand(options = {}) {
|
|
9610
9782
|
const currentVersion = await getTemplateVersion();
|
|
9611
9783
|
const format = options.format || "table";
|
|
9784
|
+
if (options.migrate) {
|
|
9785
|
+
const migrationError = await runMigration(options);
|
|
9786
|
+
if (migrationError) {
|
|
9787
|
+
console.error(" 마이그레이션 실패:", migrationError);
|
|
9788
|
+
return { success: false, projects: [], currentVersion, errors: [migrationError] };
|
|
9789
|
+
}
|
|
9790
|
+
}
|
|
9791
|
+
if (options.clean) {
|
|
9792
|
+
const { cleanRegistry: cleanRegistry2 } = await Promise.resolve().then(() => (init_registry(), exports_registry));
|
|
9793
|
+
const removed = await cleanRegistry2();
|
|
9794
|
+
if (removed > 0) {
|
|
9795
|
+
console.log(` 정리 완료: ${removed}개 존재하지 않는 프로젝트가 레지스트리에서 제거되었습니다.`);
|
|
9796
|
+
} else {
|
|
9797
|
+
console.log(" 정리할 항목이 없습니다.");
|
|
9798
|
+
}
|
|
9799
|
+
}
|
|
9612
9800
|
console.log(" oh-my-customcode 적용 프로젝트를 검색 중...");
|
|
9613
9801
|
try {
|
|
9614
9802
|
const projects = await findProjects(options);
|
|
@@ -9626,11 +9814,11 @@ async function projectsCommand(options = {}) {
|
|
|
9626
9814
|
return { success: false, projects: [], currentVersion, errors: [errorMessage] };
|
|
9627
9815
|
}
|
|
9628
9816
|
}
|
|
9629
|
-
var
|
|
9817
|
+
var projects_default;
|
|
9630
9818
|
var init_projects = __esm(() => {
|
|
9631
9819
|
init_package();
|
|
9820
|
+
init_registry();
|
|
9632
9821
|
init_fs();
|
|
9633
|
-
DEFAULT_SEARCH_DIRS = ["workspace", "projects", "dev", "src", "code", "repos", "work"];
|
|
9634
9822
|
projects_default = projectsCommand;
|
|
9635
9823
|
});
|
|
9636
9824
|
|
|
@@ -11185,13 +11373,13 @@ var PromisePolyfill;
|
|
|
11185
11373
|
var init_promise_polyfill = __esm(() => {
|
|
11186
11374
|
PromisePolyfill = class PromisePolyfill extends Promise {
|
|
11187
11375
|
static withResolver() {
|
|
11188
|
-
let
|
|
11376
|
+
let resolve4;
|
|
11189
11377
|
let reject;
|
|
11190
11378
|
const promise = new Promise((res, rej) => {
|
|
11191
|
-
|
|
11379
|
+
resolve4 = res;
|
|
11192
11380
|
reject = rej;
|
|
11193
11381
|
});
|
|
11194
|
-
return { promise, resolve:
|
|
11382
|
+
return { promise, resolve: resolve4, reject };
|
|
11195
11383
|
}
|
|
11196
11384
|
};
|
|
11197
11385
|
});
|
|
@@ -11229,7 +11417,7 @@ function createPrompt(view) {
|
|
|
11229
11417
|
output
|
|
11230
11418
|
});
|
|
11231
11419
|
const screen = new ScreenManager(rl);
|
|
11232
|
-
const { promise, resolve:
|
|
11420
|
+
const { promise, resolve: resolve4, reject } = PromisePolyfill.withResolver();
|
|
11233
11421
|
const cancel = () => reject(new CancelPromptError);
|
|
11234
11422
|
if (signal) {
|
|
11235
11423
|
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
|
|
@@ -11257,7 +11445,7 @@ function createPrompt(view) {
|
|
|
11257
11445
|
cycle(() => {
|
|
11258
11446
|
try {
|
|
11259
11447
|
const nextView = view(config, (value) => {
|
|
11260
|
-
setImmediate(() =>
|
|
11448
|
+
setImmediate(() => resolve4(value));
|
|
11261
11449
|
});
|
|
11262
11450
|
if (nextView === undefined) {
|
|
11263
11451
|
const callerFilename = callSites[1]?.getFileName();
|
|
@@ -17122,7 +17310,7 @@ var require_lib2 = __commonJS((exports) => {
|
|
|
17122
17310
|
return matches;
|
|
17123
17311
|
};
|
|
17124
17312
|
exports.analyse = analyse;
|
|
17125
|
-
var detectFile = (filepath, opts = {}) => new Promise((
|
|
17313
|
+
var detectFile = (filepath, opts = {}) => new Promise((resolve4, reject) => {
|
|
17126
17314
|
let fd;
|
|
17127
17315
|
const fs3 = (0, node_1.default)();
|
|
17128
17316
|
const handler = (err, buffer) => {
|
|
@@ -17132,7 +17320,7 @@ var require_lib2 = __commonJS((exports) => {
|
|
|
17132
17320
|
if (err) {
|
|
17133
17321
|
reject(err);
|
|
17134
17322
|
} else if (buffer) {
|
|
17135
|
-
|
|
17323
|
+
resolve4((0, exports.detect)(buffer));
|
|
17136
17324
|
} else {
|
|
17137
17325
|
reject(new Error("No error and no buffer received"));
|
|
17138
17326
|
}
|
|
@@ -22259,11 +22447,15 @@ function formatPreflightWarnings(result) {
|
|
|
22259
22447
|
`);
|
|
22260
22448
|
}
|
|
22261
22449
|
|
|
22450
|
+
// src/cli/index.ts
|
|
22451
|
+
init_registry();
|
|
22452
|
+
|
|
22262
22453
|
// src/core/self-update.ts
|
|
22454
|
+
init_package();
|
|
22263
22455
|
import { execSync as execSync2, spawnSync } from "node:child_process";
|
|
22264
22456
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22265
|
-
import { homedir } from "node:os";
|
|
22266
|
-
import { dirname, join } from "node:path";
|
|
22457
|
+
import { homedir as homedir2 } from "node:os";
|
|
22458
|
+
import { dirname, join as join2 } from "node:path";
|
|
22267
22459
|
import { createInterface } from "node:readline/promises";
|
|
22268
22460
|
|
|
22269
22461
|
// node_modules/.bun/i18next@26.0.2+8e24a2f921b8d7be/node_modules/i18next/dist/esm/i18next.js
|
|
@@ -22271,8 +22463,8 @@ var isString = (obj) => typeof obj === "string";
|
|
|
22271
22463
|
var defer = () => {
|
|
22272
22464
|
let res;
|
|
22273
22465
|
let rej;
|
|
22274
|
-
const promise = new Promise((
|
|
22275
|
-
res =
|
|
22466
|
+
const promise = new Promise((resolve2, reject) => {
|
|
22467
|
+
res = resolve2;
|
|
22276
22468
|
rej = reject;
|
|
22277
22469
|
});
|
|
22278
22470
|
promise.resolve = res;
|
|
@@ -24828,6 +25020,8 @@ var en_default = {
|
|
|
24828
25020
|
interactiveUpdating: "Updating selected projects...",
|
|
24829
25021
|
projectLatestSuffix: "(latest)",
|
|
24830
25022
|
newVersionAvailable: "[Info] oh-my-customcode v{{latest}} available (current: v{{current}}). Run 'npm i -g oh-my-customcode' to upgrade.",
|
|
25023
|
+
selfUpdateDone: "✓ oh-my-customcode updated ({{from}} → {{to}})",
|
|
25024
|
+
selfUpdateFailed: "⚠ Self-update check failed — continuing with external updates",
|
|
24831
25025
|
rtkMissing: "[RTK] RTK is not installed. Attempting installation...",
|
|
24832
25026
|
rtkInstalled: "[RTK] ✓ RTK installed — 60-90% token savings activated",
|
|
24833
25027
|
codexMissing: "[Codex] Codex CLI is not installed. Attempting installation...",
|
|
@@ -25241,6 +25435,8 @@ var ko_default = {
|
|
|
25241
25435
|
interactiveUpdating: "선택된 프로젝트 업데이트 중...",
|
|
25242
25436
|
projectLatestSuffix: "(최신)",
|
|
25243
25437
|
newVersionAvailable: "[정보] oh-my-customcode v{{latest}} 사용 가능 (현재: v{{current}}). 'npm i -g oh-my-customcode'를 실행하여 업그레이드하세요.",
|
|
25438
|
+
selfUpdateDone: "✓ oh-my-customcode 업데이트 완료 ({{from}} → {{to}})",
|
|
25439
|
+
selfUpdateFailed: "⚠ 자체 업데이트 확인 실패 — 외부 업데이트를 계속 진행합니다",
|
|
25244
25440
|
rtkMissing: "[RTK] RTK가 설치되지 않았습니다. 설치를 시도합니다...",
|
|
25245
25441
|
rtkInstalled: "[RTK] ✓ RTK 설치 완료 — 토큰 60-90% 절감 활성화",
|
|
25246
25442
|
codexMissing: "[Codex] Codex CLI가 설치되지 않았습니다. 설치를 시도합니다...",
|
|
@@ -25551,7 +25747,7 @@ var i18n = {
|
|
|
25551
25747
|
// src/core/self-update.ts
|
|
25552
25748
|
var DEFAULT_PACKAGE_NAME = "oh-my-customcode";
|
|
25553
25749
|
var DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
25554
|
-
var DEFAULT_CACHE_PATH =
|
|
25750
|
+
var DEFAULT_CACHE_PATH = join2(homedir2(), ".oh-my-customcode", "self-update-cache.json");
|
|
25555
25751
|
function normalizeVersion(version) {
|
|
25556
25752
|
return version.trim().replace(/^v/i, "").split("-")[0] || "";
|
|
25557
25753
|
}
|
|
@@ -25688,6 +25884,70 @@ function runGlobalUpdate(packageName, latestVersion) {
|
|
|
25688
25884
|
printContinueCurrentVersion();
|
|
25689
25885
|
}
|
|
25690
25886
|
}
|
|
25887
|
+
function shouldSkipEnvironmentUpdate(argv, env) {
|
|
25888
|
+
if (isNpxInvocation(argv, env))
|
|
25889
|
+
return true;
|
|
25890
|
+
if (env.CI === "true" || env.GITHUB_ACTIONS === "true")
|
|
25891
|
+
return true;
|
|
25892
|
+
if (env.OMCUSTOM_SKIP_SELF_UPDATE === "true")
|
|
25893
|
+
return true;
|
|
25894
|
+
return false;
|
|
25895
|
+
}
|
|
25896
|
+
function installGlobalPackage(packageName, version, silent) {
|
|
25897
|
+
try {
|
|
25898
|
+
execSync2(`npm install -g ${packageName}@${version}`, {
|
|
25899
|
+
stdio: silent ? "pipe" : "inherit",
|
|
25900
|
+
timeout: 60000
|
|
25901
|
+
});
|
|
25902
|
+
return true;
|
|
25903
|
+
} catch {
|
|
25904
|
+
return false;
|
|
25905
|
+
}
|
|
25906
|
+
}
|
|
25907
|
+
function executeSelfUpdate(options = {}) {
|
|
25908
|
+
const packageName = options.packageName || DEFAULT_PACKAGE_NAME;
|
|
25909
|
+
const argv = options.argv || process.argv;
|
|
25910
|
+
const env = options.env || process.env;
|
|
25911
|
+
const currentVersion = normalizeVersion(options.currentVersion || package_default.version || "");
|
|
25912
|
+
const noUpdate = {
|
|
25913
|
+
updated: false,
|
|
25914
|
+
fromVersion: currentVersion,
|
|
25915
|
+
toVersion: currentVersion
|
|
25916
|
+
};
|
|
25917
|
+
if (shouldSkipEnvironmentUpdate(argv, env)) {
|
|
25918
|
+
return noUpdate;
|
|
25919
|
+
}
|
|
25920
|
+
const checkOptions = {
|
|
25921
|
+
currentVersion,
|
|
25922
|
+
packageName,
|
|
25923
|
+
cachePath: options.cachePath,
|
|
25924
|
+
cacheTtlMs: options.cacheTtlMs,
|
|
25925
|
+
fetchLatestVersion: options.fetchLatestVersion,
|
|
25926
|
+
now: options.now,
|
|
25927
|
+
argv,
|
|
25928
|
+
env
|
|
25929
|
+
};
|
|
25930
|
+
const result = checkSelfUpdate(checkOptions);
|
|
25931
|
+
if (!result.checked || !result.updateAvailable || !result.latestVersion) {
|
|
25932
|
+
return noUpdate;
|
|
25933
|
+
}
|
|
25934
|
+
const latestVersion = result.latestVersion;
|
|
25935
|
+
if (!options.silent) {
|
|
25936
|
+
console.log(i18n.t("cli.selfUpdate.updatingGlobal", { version: latestVersion }));
|
|
25937
|
+
}
|
|
25938
|
+
const installed = installGlobalPackage(packageName, latestVersion, options.silent ?? false);
|
|
25939
|
+
if (installed) {
|
|
25940
|
+
if (!options.silent) {
|
|
25941
|
+
console.log(i18n.t("cli.selfUpdate.updated", { version: latestVersion }));
|
|
25942
|
+
printContinuationSpacing();
|
|
25943
|
+
}
|
|
25944
|
+
return { updated: true, fromVersion: currentVersion, toVersion: latestVersion };
|
|
25945
|
+
}
|
|
25946
|
+
if (!options.silent) {
|
|
25947
|
+
console.warn(i18n.t("cli.selfUpdate.failed", { error: "npm install failed" }));
|
|
25948
|
+
}
|
|
25949
|
+
return noUpdate;
|
|
25950
|
+
}
|
|
25691
25951
|
function checkSelfUpdate(options) {
|
|
25692
25952
|
const packageName = options.packageName || DEFAULT_PACKAGE_NAME;
|
|
25693
25953
|
const cachePath = options.cachePath || DEFAULT_CACHE_PATH;
|
|
@@ -26145,7 +26405,7 @@ function installCodex(deps = defaultDeps) {
|
|
|
26145
26405
|
|
|
26146
26406
|
// src/core/config.ts
|
|
26147
26407
|
init_fs();
|
|
26148
|
-
import { join as
|
|
26408
|
+
import { join as join4 } from "node:path";
|
|
26149
26409
|
var CONFIG_FILE = ".omcustomrc.json";
|
|
26150
26410
|
var CURRENT_CONFIG_VERSION = 1;
|
|
26151
26411
|
function getDefaultConfig() {
|
|
@@ -26185,7 +26445,7 @@ function getDefaultPreferences() {
|
|
|
26185
26445
|
};
|
|
26186
26446
|
}
|
|
26187
26447
|
function getConfigPath(targetDir) {
|
|
26188
|
-
return
|
|
26448
|
+
return join4(targetDir, CONFIG_FILE);
|
|
26189
26449
|
}
|
|
26190
26450
|
async function loadConfig(targetDir) {
|
|
26191
26451
|
const configPath = getConfigPath(targetDir);
|
|
@@ -26282,14 +26542,14 @@ function migrateConfig(config) {
|
|
|
26282
26542
|
|
|
26283
26543
|
// src/core/doctor-framework.ts
|
|
26284
26544
|
init_fs();
|
|
26285
|
-
import { readFile } from "node:fs/promises";
|
|
26286
|
-
import { join as
|
|
26545
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
26546
|
+
import { join as join5 } from "node:path";
|
|
26287
26547
|
async function getInstalledVersion(targetDir) {
|
|
26288
|
-
const rcPath =
|
|
26548
|
+
const rcPath = join5(targetDir, ".omcustomrc.json");
|
|
26289
26549
|
if (!await fileExists(rcPath))
|
|
26290
26550
|
return null;
|
|
26291
26551
|
try {
|
|
26292
|
-
const content = JSON.parse(await
|
|
26552
|
+
const content = JSON.parse(await readFile2(rcPath, "utf-8"));
|
|
26293
26553
|
return content.version ?? null;
|
|
26294
26554
|
} catch {
|
|
26295
26555
|
return null;
|
|
@@ -26357,7 +26617,7 @@ init_fs();
|
|
|
26357
26617
|
import { createHash } from "node:crypto";
|
|
26358
26618
|
import { createReadStream } from "node:fs";
|
|
26359
26619
|
import { readdir, stat } from "node:fs/promises";
|
|
26360
|
-
import { join as
|
|
26620
|
+
import { join as join6, relative as relative2 } from "node:path";
|
|
26361
26621
|
var LOCKFILE_NAME = ".omcustom.lock.json";
|
|
26362
26622
|
var LOCKFILE_VERSION = 1;
|
|
26363
26623
|
var LOCKFILE_COMPONENTS = [
|
|
@@ -26371,7 +26631,7 @@ var LOCKFILE_COMPONENTS = [
|
|
|
26371
26631
|
];
|
|
26372
26632
|
var COMPONENT_PATHS = LOCKFILE_COMPONENTS.map((component) => [getComponentPath(component), component]);
|
|
26373
26633
|
function computeFileHash(filePath) {
|
|
26374
|
-
return new Promise((
|
|
26634
|
+
return new Promise((resolve3, reject) => {
|
|
26375
26635
|
const hash = createHash("sha256");
|
|
26376
26636
|
const stream = createReadStream(filePath);
|
|
26377
26637
|
stream.on("error", (err) => {
|
|
@@ -26381,12 +26641,12 @@ function computeFileHash(filePath) {
|
|
|
26381
26641
|
hash.update(chunk);
|
|
26382
26642
|
});
|
|
26383
26643
|
stream.on("end", () => {
|
|
26384
|
-
|
|
26644
|
+
resolve3(hash.digest("hex"));
|
|
26385
26645
|
});
|
|
26386
26646
|
});
|
|
26387
26647
|
}
|
|
26388
26648
|
async function readLockfile(targetDir) {
|
|
26389
|
-
const lockfilePath =
|
|
26649
|
+
const lockfilePath = join6(targetDir, LOCKFILE_NAME);
|
|
26390
26650
|
const exists2 = await fileExists(lockfilePath);
|
|
26391
26651
|
if (!exists2) {
|
|
26392
26652
|
debug("lockfile.not_found", { path: lockfilePath });
|
|
@@ -26410,7 +26670,7 @@ async function readLockfile(targetDir) {
|
|
|
26410
26670
|
}
|
|
26411
26671
|
}
|
|
26412
26672
|
async function writeLockfile(targetDir, lockfile) {
|
|
26413
|
-
const lockfilePath =
|
|
26673
|
+
const lockfilePath = join6(targetDir, LOCKFILE_NAME);
|
|
26414
26674
|
await writeJsonFile(lockfilePath, lockfile);
|
|
26415
26675
|
debug("lockfile.written", { path: lockfilePath });
|
|
26416
26676
|
}
|
|
@@ -26435,7 +26695,7 @@ async function collectFiles(dir2, projectRoot, isTopLevel) {
|
|
|
26435
26695
|
if (isTopLevel && entry.startsWith(".") && entry !== ".claude") {
|
|
26436
26696
|
continue;
|
|
26437
26697
|
}
|
|
26438
|
-
const fullPath =
|
|
26698
|
+
const fullPath = join6(dir2, entry);
|
|
26439
26699
|
let fileStat;
|
|
26440
26700
|
try {
|
|
26441
26701
|
fileStat = await stat(fullPath);
|
|
@@ -26453,7 +26713,7 @@ async function collectFiles(dir2, projectRoot, isTopLevel) {
|
|
|
26453
26713
|
}
|
|
26454
26714
|
async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
26455
26715
|
const files = {};
|
|
26456
|
-
const componentRoots = COMPONENT_PATHS.map(([prefix]) =>
|
|
26716
|
+
const componentRoots = COMPONENT_PATHS.map(([prefix]) => join6(targetDir, prefix));
|
|
26457
26717
|
for (const componentRoot of componentRoots) {
|
|
26458
26718
|
const exists2 = await fileExists(componentRoot);
|
|
26459
26719
|
if (!exists2) {
|
|
@@ -26493,8 +26753,8 @@ async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
|
26493
26753
|
async function generateAndWriteLockfileForDir(targetDir) {
|
|
26494
26754
|
try {
|
|
26495
26755
|
const packageRoot = getPackageRoot();
|
|
26496
|
-
const manifest = await readJsonFile(
|
|
26497
|
-
const { version: generatorVersion } = await readJsonFile(
|
|
26756
|
+
const manifest = await readJsonFile(join6(packageRoot, "templates", "manifest.json"));
|
|
26757
|
+
const { version: generatorVersion } = await readJsonFile(join6(packageRoot, "package.json"));
|
|
26498
26758
|
const lockfile = await generateLockfile(targetDir, generatorVersion, manifest.version);
|
|
26499
26759
|
await writeLockfile(targetDir, lockfile);
|
|
26500
26760
|
return { fileCount: Object.keys(lockfile.files).length };
|
|
@@ -27244,7 +27504,7 @@ async function doctorCommand(options = {}) {
|
|
|
27244
27504
|
|
|
27245
27505
|
// src/cli/init.ts
|
|
27246
27506
|
init_package();
|
|
27247
|
-
import { join as
|
|
27507
|
+
import { join as join12 } from "node:path";
|
|
27248
27508
|
|
|
27249
27509
|
// src/core/installer.ts
|
|
27250
27510
|
init_fs();
|
|
@@ -27255,18 +27515,18 @@ import {
|
|
|
27255
27515
|
rename,
|
|
27256
27516
|
stat as stat2
|
|
27257
27517
|
} from "node:fs/promises";
|
|
27258
|
-
import { basename as
|
|
27518
|
+
import { basename as basename3, join as join8 } from "node:path";
|
|
27259
27519
|
|
|
27260
27520
|
// src/core/file-preservation.ts
|
|
27261
27521
|
init_fs();
|
|
27262
|
-
import { basename, join as
|
|
27522
|
+
import { basename as basename2, join as join7 } from "node:path";
|
|
27263
27523
|
var DEFAULT_CRITICAL_FILES = ["settings.json", "settings.local.json"];
|
|
27264
27524
|
var DEFAULT_CRITICAL_DIRECTORIES = ["agent-memory", "agent-memory-local"];
|
|
27265
27525
|
var PROTECTED_FRAMEWORK_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
27266
27526
|
var PROTECTED_RULE_PATTERNS = ["rules/MUST-*.md"];
|
|
27267
27527
|
function isProtectedFile(relativePath) {
|
|
27268
|
-
const
|
|
27269
|
-
if (PROTECTED_FRAMEWORK_FILES.includes(
|
|
27528
|
+
const basename3 = relativePath.split("/").pop() ?? "";
|
|
27529
|
+
if (PROTECTED_FRAMEWORK_FILES.includes(basename3)) {
|
|
27270
27530
|
return true;
|
|
27271
27531
|
}
|
|
27272
27532
|
for (const pattern of PROTECTED_RULE_PATTERNS) {
|
|
@@ -27282,8 +27542,8 @@ function matchesGlobPattern(filePath, pattern) {
|
|
|
27282
27542
|
return regex.test(filePath);
|
|
27283
27543
|
}
|
|
27284
27544
|
async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
27285
|
-
const srcPath =
|
|
27286
|
-
const destPath =
|
|
27545
|
+
const srcPath = join7(rootDir, fileName);
|
|
27546
|
+
const destPath = join7(tempDir, fileName);
|
|
27287
27547
|
try {
|
|
27288
27548
|
if (await fileExists(srcPath)) {
|
|
27289
27549
|
await copyFile(srcPath, destPath);
|
|
@@ -27297,8 +27557,8 @@ async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
|
27297
27557
|
}
|
|
27298
27558
|
}
|
|
27299
27559
|
async function extractSingleDir(dirName, rootDir, tempDir, result) {
|
|
27300
|
-
const srcPath =
|
|
27301
|
-
const destPath =
|
|
27560
|
+
const srcPath = join7(rootDir, dirName);
|
|
27561
|
+
const destPath = join7(tempDir, dirName);
|
|
27302
27562
|
try {
|
|
27303
27563
|
if (await fileExists(srcPath)) {
|
|
27304
27564
|
await copyDirectory(srcPath, destPath, { overwrite: true, preserveTimestamps: true });
|
|
@@ -27335,8 +27595,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
27335
27595
|
failures: []
|
|
27336
27596
|
};
|
|
27337
27597
|
for (const fileName of preservation.extractedFiles) {
|
|
27338
|
-
const preservedPath =
|
|
27339
|
-
const targetPath =
|
|
27598
|
+
const preservedPath = join7(preservation.tempDir, fileName);
|
|
27599
|
+
const targetPath = join7(rootDir, fileName);
|
|
27340
27600
|
try {
|
|
27341
27601
|
if (fileName.endsWith(".json")) {
|
|
27342
27602
|
await mergeJsonFile(preservedPath, targetPath);
|
|
@@ -27352,8 +27612,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
27352
27612
|
}
|
|
27353
27613
|
}
|
|
27354
27614
|
for (const dirName of preservation.extractedDirs) {
|
|
27355
|
-
const preservedPath =
|
|
27356
|
-
const targetPath =
|
|
27615
|
+
const preservedPath = join7(preservation.tempDir, dirName);
|
|
27616
|
+
const targetPath = join7(rootDir, dirName);
|
|
27357
27617
|
try {
|
|
27358
27618
|
await copyDirectory(preservedPath, targetPath, {
|
|
27359
27619
|
overwrite: false,
|
|
@@ -27375,10 +27635,10 @@ async function mergeJsonFile(preservedPath, targetPath) {
|
|
|
27375
27635
|
const targetData = await readJsonFile(targetPath);
|
|
27376
27636
|
const merged = deepMerge(targetData, preservedData);
|
|
27377
27637
|
await writeJsonFile(targetPath, merged);
|
|
27378
|
-
debug("preserve.merged_json", { file:
|
|
27638
|
+
debug("preserve.merged_json", { file: basename2(targetPath) });
|
|
27379
27639
|
} else {
|
|
27380
27640
|
await copyFile(preservedPath, targetPath);
|
|
27381
|
-
debug("preserve.copied_json", { file:
|
|
27641
|
+
debug("preserve.copied_json", { file: basename2(targetPath) });
|
|
27382
27642
|
}
|
|
27383
27643
|
}
|
|
27384
27644
|
function deepMerge(target, source) {
|
|
@@ -27684,7 +27944,7 @@ function shouldInstallAgent(agentDomain, filterDomain) {
|
|
|
27684
27944
|
var DEFAULT_LANGUAGE2 = "en";
|
|
27685
27945
|
function getTemplateDir() {
|
|
27686
27946
|
const packageRoot = getPackageRoot();
|
|
27687
|
-
return
|
|
27947
|
+
return join8(packageRoot, "templates");
|
|
27688
27948
|
}
|
|
27689
27949
|
function createInstallResult(targetDir) {
|
|
27690
27950
|
return {
|
|
@@ -27706,7 +27966,7 @@ async function handleBackup(targetDir, shouldBackup, result) {
|
|
|
27706
27966
|
if (!shouldBackup)
|
|
27707
27967
|
return null;
|
|
27708
27968
|
const layout = getProviderLayout();
|
|
27709
|
-
const rootDir =
|
|
27969
|
+
const rootDir = join8(targetDir, layout.rootDir);
|
|
27710
27970
|
let preservation = null;
|
|
27711
27971
|
if (await fileExists(rootDir)) {
|
|
27712
27972
|
const { createTempDir: createTempDir2 } = await Promise.resolve().then(() => (init_fs(), exports_fs));
|
|
@@ -27763,8 +28023,8 @@ async function installSingleComponent(targetDir, component, options, result) {
|
|
|
27763
28023
|
}
|
|
27764
28024
|
async function installStatusline(targetDir, options, _result) {
|
|
27765
28025
|
const layout = getProviderLayout();
|
|
27766
|
-
const srcPath = resolveTemplatePath(
|
|
27767
|
-
const destPath =
|
|
28026
|
+
const srcPath = resolveTemplatePath(join8(layout.rootDir, "statusline.sh"));
|
|
28027
|
+
const destPath = join8(targetDir, layout.rootDir, "statusline.sh");
|
|
27768
28028
|
if (!await fileExists(srcPath)) {
|
|
27769
28029
|
debug("install.statusline_not_found", { path: srcPath });
|
|
27770
28030
|
return;
|
|
@@ -27782,7 +28042,7 @@ async function installStatusline(targetDir, options, _result) {
|
|
|
27782
28042
|
}
|
|
27783
28043
|
async function installSettingsLocal(targetDir, result) {
|
|
27784
28044
|
const layout = getProviderLayout();
|
|
27785
|
-
const settingsPath =
|
|
28045
|
+
const settingsPath = join8(targetDir, layout.rootDir, "settings.local.json");
|
|
27786
28046
|
const statusLineConfig = {
|
|
27787
28047
|
statusLine: {
|
|
27788
28048
|
type: "command",
|
|
@@ -27868,7 +28128,7 @@ async function install(options) {
|
|
|
27868
28128
|
await installEntryDocWithTracking(options.targetDir, options, result);
|
|
27869
28129
|
if (preservation) {
|
|
27870
28130
|
const layout = getProviderLayout();
|
|
27871
|
-
const rootDir =
|
|
28131
|
+
const rootDir = join8(options.targetDir, layout.rootDir);
|
|
27872
28132
|
const restoration = await restoreCriticalFiles(rootDir, preservation);
|
|
27873
28133
|
if (restoration.restoredFiles.length > 0 || restoration.restoredDirs.length > 0) {
|
|
27874
28134
|
info("install.restored", {
|
|
@@ -27905,7 +28165,7 @@ async function install(options) {
|
|
|
27905
28165
|
async function getTemplateManifest() {
|
|
27906
28166
|
const packageRoot = getPackageRoot();
|
|
27907
28167
|
const layout = getProviderLayout();
|
|
27908
|
-
const manifestPath =
|
|
28168
|
+
const manifestPath = join8(packageRoot, "templates", layout.manifestFile);
|
|
27909
28169
|
if (await fileExists(manifestPath)) {
|
|
27910
28170
|
return readJsonFile(manifestPath);
|
|
27911
28171
|
}
|
|
@@ -27928,10 +28188,10 @@ async function installSkillsWithScopeFilter(srcPath, destPath, options) {
|
|
|
27928
28188
|
await ensureDirectory(destPath);
|
|
27929
28189
|
const entries = await readdir2(srcPath);
|
|
27930
28190
|
for (const entry of entries) {
|
|
27931
|
-
const entrySrcPath =
|
|
28191
|
+
const entrySrcPath = join8(srcPath, entry);
|
|
27932
28192
|
if (!(await stat2(entrySrcPath)).isDirectory())
|
|
27933
28193
|
continue;
|
|
27934
|
-
const skillMdPath =
|
|
28194
|
+
const skillMdPath = join8(entrySrcPath, "SKILL.md");
|
|
27935
28195
|
if (await fileExists(skillMdPath)) {
|
|
27936
28196
|
const content = await fsReadFile(skillMdPath, "utf-8");
|
|
27937
28197
|
const scope = getSkillScope(content);
|
|
@@ -27940,7 +28200,7 @@ async function installSkillsWithScopeFilter(srcPath, destPath, options) {
|
|
|
27940
28200
|
continue;
|
|
27941
28201
|
}
|
|
27942
28202
|
}
|
|
27943
|
-
await copyDirectory(entrySrcPath,
|
|
28203
|
+
await copyDirectory(entrySrcPath, join8(destPath, entry), {
|
|
27944
28204
|
overwrite: !!(options.force || options.backup),
|
|
27945
28205
|
preserveSymlinks: true,
|
|
27946
28206
|
preserveTimestamps: true
|
|
@@ -27951,10 +28211,10 @@ async function installAgentsWithDomainFilter(srcPath, destPath, options) {
|
|
|
27951
28211
|
await ensureDirectory(destPath);
|
|
27952
28212
|
const entries = await readdir2(srcPath);
|
|
27953
28213
|
for (const entry of entries) {
|
|
27954
|
-
const entrySrcPath =
|
|
28214
|
+
const entrySrcPath = join8(srcPath, entry);
|
|
27955
28215
|
const entryStat = await stat2(entrySrcPath);
|
|
27956
28216
|
if (entryStat.isDirectory()) {
|
|
27957
|
-
await copyDirectory(entrySrcPath,
|
|
28217
|
+
await copyDirectory(entrySrcPath, join8(destPath, entry), {
|
|
27958
28218
|
overwrite: !!(options.force || options.backup),
|
|
27959
28219
|
preserveSymlinks: true,
|
|
27960
28220
|
preserveTimestamps: true
|
|
@@ -27971,7 +28231,7 @@ async function installAgentsWithDomainFilter(srcPath, destPath, options) {
|
|
|
27971
28231
|
continue;
|
|
27972
28232
|
}
|
|
27973
28233
|
}
|
|
27974
|
-
await copyFile(entrySrcPath,
|
|
28234
|
+
await copyFile(entrySrcPath, join8(destPath, entry));
|
|
27975
28235
|
}
|
|
27976
28236
|
}
|
|
27977
28237
|
async function installComponent(targetDir, component, options) {
|
|
@@ -27979,7 +28239,7 @@ async function installComponent(targetDir, component, options) {
|
|
|
27979
28239
|
return false;
|
|
27980
28240
|
}
|
|
27981
28241
|
const templatePath = getComponentPath(component);
|
|
27982
|
-
const destPath =
|
|
28242
|
+
const destPath = join8(targetDir, templatePath);
|
|
27983
28243
|
const destExists = await fileExists(destPath);
|
|
27984
28244
|
if (destExists && !options.force && !options.backup) {
|
|
27985
28245
|
debug("install.component_skipped", { component });
|
|
@@ -28013,7 +28273,7 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
28013
28273
|
const layout = getProviderLayout();
|
|
28014
28274
|
const templateFile = getEntryTemplateName(language);
|
|
28015
28275
|
const srcPath = resolveTemplatePath(templateFile);
|
|
28016
|
-
const destPath =
|
|
28276
|
+
const destPath = join8(targetDir, layout.entryFile);
|
|
28017
28277
|
if (!await fileExists(srcPath)) {
|
|
28018
28278
|
warn("install.entry_md_not_found", { language, path: srcPath, entry: layout.entryFile });
|
|
28019
28279
|
return false;
|
|
@@ -28033,8 +28293,8 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
28033
28293
|
return true;
|
|
28034
28294
|
}
|
|
28035
28295
|
async function backupExisting(sourcePath, backupDir) {
|
|
28036
|
-
const name =
|
|
28037
|
-
const backupPath =
|
|
28296
|
+
const name = basename3(sourcePath);
|
|
28297
|
+
const backupPath = join8(backupDir, name);
|
|
28038
28298
|
await rename(sourcePath, backupPath);
|
|
28039
28299
|
return backupPath;
|
|
28040
28300
|
}
|
|
@@ -28043,7 +28303,7 @@ async function checkExistingPaths(targetDir) {
|
|
|
28043
28303
|
const pathsToCheck = [layout.entryFile, layout.rootDir, "guides"];
|
|
28044
28304
|
const existingPaths = [];
|
|
28045
28305
|
for (const relativePath of pathsToCheck) {
|
|
28046
|
-
const fullPath =
|
|
28306
|
+
const fullPath = join8(targetDir, relativePath);
|
|
28047
28307
|
if (await fileExists(fullPath)) {
|
|
28048
28308
|
existingPaths.push(relativePath);
|
|
28049
28309
|
}
|
|
@@ -28057,11 +28317,11 @@ async function backupExistingInstallation(targetDir) {
|
|
|
28057
28317
|
return [];
|
|
28058
28318
|
}
|
|
28059
28319
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
28060
|
-
const backupDir =
|
|
28320
|
+
const backupDir = join8(targetDir, `${layout.backupDirPrefix}${timestamp}`);
|
|
28061
28321
|
await ensureDirectory(backupDir);
|
|
28062
28322
|
const backedUpPaths = [];
|
|
28063
28323
|
for (const relativePath of existingPaths) {
|
|
28064
|
-
const fullPath =
|
|
28324
|
+
const fullPath = join8(targetDir, relativePath);
|
|
28065
28325
|
try {
|
|
28066
28326
|
const backupPath = await backupExisting(fullPath, backupDir);
|
|
28067
28327
|
backedUpPaths.push(backupPath);
|
|
@@ -28077,13 +28337,13 @@ async function backupExistingInstallation(targetDir) {
|
|
|
28077
28337
|
// src/core/mcp-config.ts
|
|
28078
28338
|
init_fs();
|
|
28079
28339
|
import { execSync as execSync5 } from "node:child_process";
|
|
28080
|
-
import { writeFile } from "node:fs/promises";
|
|
28081
|
-
import { join as
|
|
28340
|
+
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
28341
|
+
import { join as join9 } from "node:path";
|
|
28082
28342
|
async function generateMCPConfig(targetDir) {
|
|
28083
28343
|
const layout = getProviderLayout();
|
|
28084
|
-
const mcpConfigPath =
|
|
28085
|
-
const ontologyDir =
|
|
28086
|
-
const ontologyExists = await fileExists(
|
|
28344
|
+
const mcpConfigPath = join9(targetDir, ".mcp.json");
|
|
28345
|
+
const ontologyDir = join9(layout.rootDir, "ontology");
|
|
28346
|
+
const ontologyExists = await fileExists(join9(targetDir, ontologyDir));
|
|
28087
28347
|
if (!ontologyExists) {
|
|
28088
28348
|
return;
|
|
28089
28349
|
}
|
|
@@ -28118,21 +28378,21 @@ async function generateMCPConfig(targetDir) {
|
|
|
28118
28378
|
const existingConfigPath = mcpConfigPath;
|
|
28119
28379
|
if (await fileExists(existingConfigPath)) {
|
|
28120
28380
|
try {
|
|
28121
|
-
const { readFile:
|
|
28122
|
-
const existingContent = await
|
|
28381
|
+
const { readFile: readFile3 } = await import("node:fs/promises");
|
|
28382
|
+
const existingContent = await readFile3(existingConfigPath, "utf-8");
|
|
28123
28383
|
const existing = JSON.parse(existingContent);
|
|
28124
28384
|
if (!existing.mcpServers?.["ontology-rag"]) {
|
|
28125
28385
|
existing.mcpServers = existing.mcpServers || {};
|
|
28126
28386
|
existing.mcpServers["ontology-rag"] = config.mcpServers["ontology-rag"];
|
|
28127
|
-
await
|
|
28387
|
+
await writeFile2(mcpConfigPath, `${JSON.stringify(existing, null, 2)}
|
|
28128
28388
|
`);
|
|
28129
28389
|
}
|
|
28130
28390
|
} catch {
|
|
28131
|
-
await
|
|
28391
|
+
await writeFile2(mcpConfigPath, `${JSON.stringify(config, null, 2)}
|
|
28132
28392
|
`);
|
|
28133
28393
|
}
|
|
28134
28394
|
} else {
|
|
28135
|
-
await
|
|
28395
|
+
await writeFile2(mcpConfigPath, `${JSON.stringify(config, null, 2)}
|
|
28136
28396
|
`);
|
|
28137
28397
|
}
|
|
28138
28398
|
info("ontology-rag MCP server configured successfully");
|
|
@@ -28146,16 +28406,20 @@ async function checkUvAvailable() {
|
|
|
28146
28406
|
}
|
|
28147
28407
|
}
|
|
28148
28408
|
|
|
28409
|
+
// src/cli/init.ts
|
|
28410
|
+
init_registry();
|
|
28411
|
+
|
|
28149
28412
|
// src/core/snapshot.ts
|
|
28150
28413
|
init_package();
|
|
28151
28414
|
init_projects();
|
|
28152
28415
|
import { existsSync as existsSync2 } from "node:fs";
|
|
28153
28416
|
import { copyFile as copyFile2, cp } from "node:fs/promises";
|
|
28154
|
-
import { join as
|
|
28417
|
+
import { join as join11 } from "node:path";
|
|
28155
28418
|
init_fs();
|
|
28419
|
+
init_registry();
|
|
28156
28420
|
async function checkExistingInstallation(targetDir) {
|
|
28157
28421
|
const layout = getProviderLayout();
|
|
28158
|
-
const rootDir =
|
|
28422
|
+
const rootDir = join11(targetDir, layout.rootDir);
|
|
28159
28423
|
return fileExists(rootDir);
|
|
28160
28424
|
}
|
|
28161
28425
|
async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
@@ -28167,7 +28431,7 @@ async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
|
28167
28431
|
};
|
|
28168
28432
|
}
|
|
28169
28433
|
const layout = getProviderLayout();
|
|
28170
|
-
const snapshotClaude =
|
|
28434
|
+
const snapshotClaude = join11(snapshotPath, layout.rootDir);
|
|
28171
28435
|
if (!existsSync2(snapshotClaude)) {
|
|
28172
28436
|
return {
|
|
28173
28437
|
success: false,
|
|
@@ -28181,29 +28445,32 @@ async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
|
28181
28445
|
if (exists2 && !options.force) {
|
|
28182
28446
|
console.log(i18n.t("cli.init.exists", { rootDir: layout.rootDir }));
|
|
28183
28447
|
console.log(i18n.t("cli.init.backing_up"));
|
|
28184
|
-
const backupDir =
|
|
28185
|
-
await cp(
|
|
28448
|
+
const backupDir = join11(targetDir, `.claude-backup-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, -1)}`);
|
|
28449
|
+
await cp(join11(targetDir, layout.rootDir), backupDir, { recursive: true });
|
|
28186
28450
|
console.log(` Backed up to: ${backupDir}`);
|
|
28187
28451
|
}
|
|
28188
|
-
await cp(snapshotClaude,
|
|
28452
|
+
await cp(snapshotClaude, join11(targetDir, layout.rootDir), {
|
|
28189
28453
|
recursive: true,
|
|
28190
28454
|
force: true
|
|
28191
28455
|
});
|
|
28192
|
-
const snapshotGuides =
|
|
28456
|
+
const snapshotGuides = join11(snapshotPath, "guides");
|
|
28193
28457
|
if (existsSync2(snapshotGuides)) {
|
|
28194
|
-
await cp(snapshotGuides,
|
|
28458
|
+
await cp(snapshotGuides, join11(targetDir, "guides"), {
|
|
28195
28459
|
recursive: true,
|
|
28196
28460
|
force: true
|
|
28197
28461
|
});
|
|
28198
28462
|
}
|
|
28199
|
-
const snapshotEntry =
|
|
28463
|
+
const snapshotEntry = join11(snapshotPath, layout.entryFile);
|
|
28200
28464
|
if (existsSync2(snapshotEntry)) {
|
|
28201
|
-
await copyFile2(snapshotEntry,
|
|
28465
|
+
await copyFile2(snapshotEntry, join11(targetDir, layout.entryFile));
|
|
28202
28466
|
}
|
|
28203
28467
|
try {
|
|
28204
28468
|
const existing = await readLockFile(targetDir);
|
|
28205
28469
|
await writeLockFile(targetDir, package_default.version, existing);
|
|
28206
28470
|
} catch {}
|
|
28471
|
+
try {
|
|
28472
|
+
await registerProject(targetDir, package_default.version);
|
|
28473
|
+
} catch {}
|
|
28207
28474
|
console.log(i18n.t("cli.init.success"));
|
|
28208
28475
|
console.log(`
|
|
28209
28476
|
Installed from snapshot: ${snapshotPath}`);
|
|
@@ -29203,7 +29470,7 @@ async function runInitWizard(options) {
|
|
|
29203
29470
|
// src/cli/init.ts
|
|
29204
29471
|
async function checkExistingInstallation2(targetDir) {
|
|
29205
29472
|
const layout = getProviderLayout();
|
|
29206
|
-
const rootDir =
|
|
29473
|
+
const rootDir = join12(targetDir, layout.rootDir);
|
|
29207
29474
|
return fileExists(rootDir);
|
|
29208
29475
|
}
|
|
29209
29476
|
var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
@@ -29217,13 +29484,13 @@ var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
|
29217
29484
|
function componentToPath(targetDir, component) {
|
|
29218
29485
|
if (component === "entry-md") {
|
|
29219
29486
|
const layout = getProviderLayout();
|
|
29220
|
-
return
|
|
29487
|
+
return join12(targetDir, layout.entryFile);
|
|
29221
29488
|
}
|
|
29222
29489
|
if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
|
|
29223
29490
|
const layout = getProviderLayout();
|
|
29224
|
-
return
|
|
29491
|
+
return join12(targetDir, layout.rootDir, component);
|
|
29225
29492
|
}
|
|
29226
|
-
return
|
|
29493
|
+
return join12(targetDir, component);
|
|
29227
29494
|
}
|
|
29228
29495
|
function buildInstalledPaths(targetDir, components) {
|
|
29229
29496
|
return components.map((component) => componentToPath(targetDir, component));
|
|
@@ -29323,6 +29590,9 @@ async function initCommand(options) {
|
|
|
29323
29590
|
const existing = await readLockFile(targetDir);
|
|
29324
29591
|
await writeLockFile(targetDir, package_default.version, existing);
|
|
29325
29592
|
} catch {}
|
|
29593
|
+
try {
|
|
29594
|
+
await registerProject(targetDir, package_default.version);
|
|
29595
|
+
} catch {}
|
|
29326
29596
|
console.log("");
|
|
29327
29597
|
console.log("Required plugins (install manually):");
|
|
29328
29598
|
console.log(" /plugin marketplace add obra/superpowers-marketplace");
|
|
@@ -29345,7 +29615,7 @@ async function initCommand(options) {
|
|
|
29345
29615
|
}
|
|
29346
29616
|
|
|
29347
29617
|
// src/cli/list.ts
|
|
29348
|
-
import { basename as
|
|
29618
|
+
import { basename as basename5, dirname as dirname3, join as join13, relative as relative3 } from "node:path";
|
|
29349
29619
|
init_fs();
|
|
29350
29620
|
var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
|
|
29351
29621
|
function parseKeyValue(line) {
|
|
@@ -29392,7 +29662,7 @@ function parseYamlMetadata(content) {
|
|
|
29392
29662
|
return result;
|
|
29393
29663
|
}
|
|
29394
29664
|
function extractAgentTypeFromFilename(filename) {
|
|
29395
|
-
const name =
|
|
29665
|
+
const name = basename5(filename, ".md");
|
|
29396
29666
|
const prefixMap = {
|
|
29397
29667
|
lang: "language",
|
|
29398
29668
|
be: "backend",
|
|
@@ -29410,17 +29680,17 @@ function extractAgentTypeFromFilename(filename) {
|
|
|
29410
29680
|
return prefixMap[prefix] || "unknown";
|
|
29411
29681
|
}
|
|
29412
29682
|
function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
|
|
29413
|
-
const relativePath = relative3(
|
|
29683
|
+
const relativePath = relative3(join13(baseDir, rootDir, "skills"), skillPath);
|
|
29414
29684
|
const parts = relativePath.split("/").filter(Boolean);
|
|
29415
29685
|
return parts[0] || "unknown";
|
|
29416
29686
|
}
|
|
29417
29687
|
function extractGuideCategoryFromPath(guidePath, baseDir) {
|
|
29418
|
-
const relativePath = relative3(
|
|
29688
|
+
const relativePath = relative3(join13(baseDir, "guides"), guidePath);
|
|
29419
29689
|
const parts = relativePath.split("/").filter(Boolean);
|
|
29420
29690
|
return parts[0] || "unknown";
|
|
29421
29691
|
}
|
|
29422
29692
|
function extractRulePriorityFromFilename(filename) {
|
|
29423
|
-
const name =
|
|
29693
|
+
const name = basename5(filename, ".md");
|
|
29424
29694
|
const parts = name.split("-");
|
|
29425
29695
|
return parts[0] || "unknown";
|
|
29426
29696
|
}
|
|
@@ -29509,7 +29779,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
|
|
|
29509
29779
|
}
|
|
29510
29780
|
}
|
|
29511
29781
|
async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
29512
|
-
const agentsDir =
|
|
29782
|
+
const agentsDir = join13(targetDir, rootDir, "agents");
|
|
29513
29783
|
if (!await fileExists(agentsDir))
|
|
29514
29784
|
return [];
|
|
29515
29785
|
try {
|
|
@@ -29518,8 +29788,8 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
29518
29788
|
const customAgentPaths = new Set(customComponents.filter((c) => c.type === "agent").map((c) => c.path));
|
|
29519
29789
|
const agentMdFiles = await listFiles(agentsDir, { recursive: false, pattern: "*.md" });
|
|
29520
29790
|
const agents = await Promise.all(agentMdFiles.map(async (agentMdPath) => {
|
|
29521
|
-
const filename =
|
|
29522
|
-
const name =
|
|
29791
|
+
const filename = basename5(agentMdPath);
|
|
29792
|
+
const name = basename5(filename, ".md");
|
|
29523
29793
|
const description = await tryExtractMarkdownDescription(agentMdPath);
|
|
29524
29794
|
const relativePath = relative3(targetDir, agentMdPath);
|
|
29525
29795
|
return {
|
|
@@ -29537,7 +29807,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
29537
29807
|
}
|
|
29538
29808
|
}
|
|
29539
29809
|
async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
29540
|
-
const skillsDir =
|
|
29810
|
+
const skillsDir = join13(targetDir, rootDir, "skills");
|
|
29541
29811
|
if (!await fileExists(skillsDir))
|
|
29542
29812
|
return [];
|
|
29543
29813
|
try {
|
|
@@ -29546,12 +29816,12 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
29546
29816
|
const customSkillPaths = new Set(customComponents.filter((c) => c.type === "skill").map((c) => c.path));
|
|
29547
29817
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
29548
29818
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
29549
|
-
const skillDir =
|
|
29550
|
-
const indexYamlPath =
|
|
29819
|
+
const skillDir = dirname3(skillMdPath);
|
|
29820
|
+
const indexYamlPath = join13(skillDir, "index.yaml");
|
|
29551
29821
|
const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
|
|
29552
29822
|
const relativePath = relative3(targetDir, skillDir);
|
|
29553
29823
|
return {
|
|
29554
|
-
name:
|
|
29824
|
+
name: basename5(skillDir),
|
|
29555
29825
|
type: "skill",
|
|
29556
29826
|
category: extractSkillCategoryFromPath(skillDir, targetDir, rootDir),
|
|
29557
29827
|
path: relativePath,
|
|
@@ -29566,7 +29836,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
29566
29836
|
}
|
|
29567
29837
|
}
|
|
29568
29838
|
async function getGuides(targetDir, config) {
|
|
29569
|
-
const guidesDir =
|
|
29839
|
+
const guidesDir = join13(targetDir, "guides");
|
|
29570
29840
|
if (!await fileExists(guidesDir))
|
|
29571
29841
|
return [];
|
|
29572
29842
|
try {
|
|
@@ -29578,7 +29848,7 @@ async function getGuides(targetDir, config) {
|
|
|
29578
29848
|
const description = await tryExtractMarkdownDescription(guideMdPath, { maxLength: 100 });
|
|
29579
29849
|
const relativePath = relative3(targetDir, guideMdPath);
|
|
29580
29850
|
return {
|
|
29581
|
-
name:
|
|
29851
|
+
name: basename5(guideMdPath, ".md"),
|
|
29582
29852
|
type: "guide",
|
|
29583
29853
|
category: extractGuideCategoryFromPath(guideMdPath, targetDir),
|
|
29584
29854
|
path: relativePath,
|
|
@@ -29593,7 +29863,7 @@ async function getGuides(targetDir, config) {
|
|
|
29593
29863
|
}
|
|
29594
29864
|
var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
|
|
29595
29865
|
async function getRules(targetDir, rootDir = ".claude", config) {
|
|
29596
|
-
const rulesDir =
|
|
29866
|
+
const rulesDir = join13(targetDir, rootDir, "rules");
|
|
29597
29867
|
if (!await fileExists(rulesDir))
|
|
29598
29868
|
return [];
|
|
29599
29869
|
try {
|
|
@@ -29602,13 +29872,13 @@ async function getRules(targetDir, rootDir = ".claude", config) {
|
|
|
29602
29872
|
const customRulePaths = new Set(customComponents.filter((c) => c.type === "rule").map((c) => c.path));
|
|
29603
29873
|
const ruleMdFiles = await listFiles(rulesDir, { recursive: false, pattern: "*.md" });
|
|
29604
29874
|
const rules = await Promise.all(ruleMdFiles.map(async (ruleMdPath) => {
|
|
29605
|
-
const filename =
|
|
29875
|
+
const filename = basename5(ruleMdPath);
|
|
29606
29876
|
const description = await tryExtractMarkdownDescription(ruleMdPath, {
|
|
29607
29877
|
cleanFormatting: true
|
|
29608
29878
|
});
|
|
29609
29879
|
const relativePath = relative3(targetDir, ruleMdPath);
|
|
29610
29880
|
return {
|
|
29611
|
-
name:
|
|
29881
|
+
name: basename5(ruleMdPath, ".md"),
|
|
29612
29882
|
type: extractRulePriorityFromFilename(filename),
|
|
29613
29883
|
path: relativePath,
|
|
29614
29884
|
description,
|
|
@@ -29665,7 +29935,7 @@ function formatAsJson(components) {
|
|
|
29665
29935
|
console.log(JSON.stringify(components, null, 2));
|
|
29666
29936
|
}
|
|
29667
29937
|
async function getHooks(targetDir, rootDir = ".claude") {
|
|
29668
|
-
const hooksDir =
|
|
29938
|
+
const hooksDir = join13(targetDir, rootDir, "hooks");
|
|
29669
29939
|
if (!await fileExists(hooksDir))
|
|
29670
29940
|
return [];
|
|
29671
29941
|
try {
|
|
@@ -29674,7 +29944,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
29674
29944
|
const hookYamls = await listFiles(hooksDir, { recursive: true, pattern: "*.yaml" });
|
|
29675
29945
|
const allFiles = [...hookFiles, ...hookConfigs, ...hookYamls];
|
|
29676
29946
|
return allFiles.map((hookPath) => ({
|
|
29677
|
-
name:
|
|
29947
|
+
name: basename5(hookPath),
|
|
29678
29948
|
type: "hook",
|
|
29679
29949
|
path: relative3(targetDir, hookPath)
|
|
29680
29950
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -29683,7 +29953,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
29683
29953
|
}
|
|
29684
29954
|
}
|
|
29685
29955
|
async function getContexts(targetDir, rootDir = ".claude") {
|
|
29686
|
-
const contextsDir =
|
|
29956
|
+
const contextsDir = join13(targetDir, rootDir, "contexts");
|
|
29687
29957
|
if (!await fileExists(contextsDir))
|
|
29688
29958
|
return [];
|
|
29689
29959
|
try {
|
|
@@ -29694,7 +29964,7 @@ async function getContexts(targetDir, rootDir = ".claude") {
|
|
|
29694
29964
|
const ext = ctxPath.endsWith(".md") ? ".md" : ".yaml";
|
|
29695
29965
|
const description = ext === ".md" ? await tryExtractMarkdownDescription(ctxPath, { maxLength: 100 }) : undefined;
|
|
29696
29966
|
return {
|
|
29697
|
-
name:
|
|
29967
|
+
name: basename5(ctxPath, ext),
|
|
29698
29968
|
type: "context",
|
|
29699
29969
|
path: relative3(targetDir, ctxPath),
|
|
29700
29970
|
description
|
|
@@ -30076,29 +30346,29 @@ async function securityCommand(_options = {}) {
|
|
|
30076
30346
|
|
|
30077
30347
|
// src/cli/serve-commands.ts
|
|
30078
30348
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
30079
|
-
import { join as
|
|
30349
|
+
import { join as join15 } from "node:path";
|
|
30080
30350
|
|
|
30081
30351
|
// src/cli/serve.ts
|
|
30082
30352
|
import { spawn } from "node:child_process";
|
|
30083
30353
|
import { existsSync as existsSync3 } from "node:fs";
|
|
30084
|
-
import { readFile as
|
|
30085
|
-
import { join as
|
|
30354
|
+
import { readFile as readFile3, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
30355
|
+
import { join as join14 } from "node:path";
|
|
30086
30356
|
var DEFAULT_PORT = 4321;
|
|
30087
|
-
var PID_FILE =
|
|
30357
|
+
var PID_FILE = join14(process.env.HOME ?? "~", ".omcustom-serve.pid");
|
|
30088
30358
|
function findServeBuildDir(projectRoot, options) {
|
|
30089
|
-
const localBuild =
|
|
30090
|
-
if (existsSync3(
|
|
30359
|
+
const localBuild = join14(projectRoot, "packages", "serve", "build");
|
|
30360
|
+
if (existsSync3(join14(localBuild, "index.js")))
|
|
30091
30361
|
return localBuild;
|
|
30092
30362
|
if (options?.skipNpmFallback !== true) {
|
|
30093
|
-
const npmBuild =
|
|
30094
|
-
if (existsSync3(
|
|
30363
|
+
const npmBuild = join14(import.meta.dirname, "..", "..", "packages", "serve", "build");
|
|
30364
|
+
if (existsSync3(join14(npmBuild, "index.js")))
|
|
30095
30365
|
return npmBuild;
|
|
30096
30366
|
}
|
|
30097
30367
|
return null;
|
|
30098
30368
|
}
|
|
30099
30369
|
async function isServeRunning() {
|
|
30100
30370
|
try {
|
|
30101
|
-
const raw = await
|
|
30371
|
+
const raw = await readFile3(PID_FILE, "utf-8");
|
|
30102
30372
|
const pid = Number(raw.trim());
|
|
30103
30373
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
30104
30374
|
await cleanupPidFile();
|
|
@@ -30119,7 +30389,7 @@ async function startServeBackground(projectRoot, port = DEFAULT_PORT, buildDirOp
|
|
|
30119
30389
|
if (buildDir === null) {
|
|
30120
30390
|
return;
|
|
30121
30391
|
}
|
|
30122
|
-
const child = spawn("node", [
|
|
30392
|
+
const child = spawn("node", [join14(buildDir, "index.js")], {
|
|
30123
30393
|
env: {
|
|
30124
30394
|
...process.env,
|
|
30125
30395
|
OMCUSTOM_PORT: String(port),
|
|
@@ -30132,12 +30402,12 @@ async function startServeBackground(projectRoot, port = DEFAULT_PORT, buildDirOp
|
|
|
30132
30402
|
});
|
|
30133
30403
|
child.unref();
|
|
30134
30404
|
if (child.pid !== undefined) {
|
|
30135
|
-
await
|
|
30405
|
+
await writeFile3(PID_FILE, String(child.pid), "utf-8");
|
|
30136
30406
|
}
|
|
30137
30407
|
}
|
|
30138
30408
|
async function stopServe() {
|
|
30139
30409
|
try {
|
|
30140
|
-
const raw = await
|
|
30410
|
+
const raw = await readFile3(PID_FILE, "utf-8");
|
|
30141
30411
|
const pid = Number(raw.trim());
|
|
30142
30412
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
30143
30413
|
await cleanupPidFile();
|
|
@@ -30196,7 +30466,7 @@ function runForeground(projectRoot, port, buildDirOpts) {
|
|
|
30196
30466
|
process.exit(1);
|
|
30197
30467
|
}
|
|
30198
30468
|
console.log(`Web UI: http://localhost:${port}`);
|
|
30199
|
-
spawnSync2("node", [
|
|
30469
|
+
spawnSync2("node", [join15(buildDir, "index.js")], {
|
|
30200
30470
|
env: {
|
|
30201
30471
|
...process.env,
|
|
30202
30472
|
OMCUSTOM_PORT: String(port),
|
|
@@ -30209,18 +30479,18 @@ function runForeground(projectRoot, port, buildDirOpts) {
|
|
|
30209
30479
|
}
|
|
30210
30480
|
|
|
30211
30481
|
// src/cli/sync.ts
|
|
30212
|
-
import { resolve as
|
|
30482
|
+
import { resolve as resolve3 } from "node:path";
|
|
30213
30483
|
|
|
30214
30484
|
// src/core/sync.ts
|
|
30215
30485
|
init_fs();
|
|
30216
30486
|
import { existsSync as existsSync4 } from "node:fs";
|
|
30217
|
-
import { cp as cp2, mkdir } from "node:fs/promises";
|
|
30218
|
-
import { join as
|
|
30487
|
+
import { cp as cp2, mkdir as mkdir2 } from "node:fs/promises";
|
|
30488
|
+
import { join as join16 } from "node:path";
|
|
30219
30489
|
async function loadVersions() {
|
|
30220
30490
|
try {
|
|
30221
30491
|
const packageRoot = getPackageRoot();
|
|
30222
|
-
const manifest = await readJsonFile(
|
|
30223
|
-
const pkg = await readJsonFile(
|
|
30492
|
+
const manifest = await readJsonFile(join16(packageRoot, "templates", "manifest.json"));
|
|
30493
|
+
const pkg = await readJsonFile(join16(packageRoot, "package.json"));
|
|
30224
30494
|
return { generatorVersion: pkg.version, templateVersion: manifest.version };
|
|
30225
30495
|
} catch {
|
|
30226
30496
|
return { generatorVersion: "0.0.0", templateVersion: "0.0.0" };
|
|
@@ -30290,7 +30560,7 @@ async function countFiles(dir2) {
|
|
|
30290
30560
|
return 0;
|
|
30291
30561
|
}
|
|
30292
30562
|
for (const entry of entries) {
|
|
30293
|
-
const full =
|
|
30563
|
+
const full = join16(current, entry);
|
|
30294
30564
|
try {
|
|
30295
30565
|
const s = await stat3(full);
|
|
30296
30566
|
if (s.isDirectory()) {
|
|
@@ -30305,19 +30575,19 @@ async function countFiles(dir2) {
|
|
|
30305
30575
|
return walk(dir2);
|
|
30306
30576
|
}
|
|
30307
30577
|
async function exportSnapshot(targetDir, outputPath) {
|
|
30308
|
-
const claudeDir =
|
|
30309
|
-
const guidesDir =
|
|
30578
|
+
const claudeDir = join16(targetDir, ".claude");
|
|
30579
|
+
const guidesDir = join16(targetDir, "guides");
|
|
30310
30580
|
if (!existsSync4(claudeDir)) {
|
|
30311
30581
|
return { success: false, exportPath: outputPath, fileCount: 0 };
|
|
30312
30582
|
}
|
|
30313
|
-
await
|
|
30314
|
-
const destClaude =
|
|
30583
|
+
await mkdir2(outputPath, { recursive: true });
|
|
30584
|
+
const destClaude = join16(outputPath, ".claude");
|
|
30315
30585
|
await cp2(claudeDir, destClaude, {
|
|
30316
30586
|
recursive: true,
|
|
30317
30587
|
filter: isExportable
|
|
30318
30588
|
});
|
|
30319
30589
|
if (existsSync4(guidesDir)) {
|
|
30320
|
-
await cp2(guidesDir,
|
|
30590
|
+
await cp2(guidesDir, join16(outputPath, "guides"), { recursive: true });
|
|
30321
30591
|
}
|
|
30322
30592
|
const lockfile = await generateCurrentLockfile(targetDir);
|
|
30323
30593
|
if (lockfile) {
|
|
@@ -30329,7 +30599,7 @@ async function exportSnapshot(targetDir, outputPath) {
|
|
|
30329
30599
|
|
|
30330
30600
|
// src/cli/sync.ts
|
|
30331
30601
|
async function runExport(targetDir, outputPath) {
|
|
30332
|
-
const result = await exportSnapshot(targetDir,
|
|
30602
|
+
const result = await exportSnapshot(targetDir, resolve3(outputPath));
|
|
30333
30603
|
if (!result.success) {
|
|
30334
30604
|
console.error(`
|
|
30335
30605
|
Export failed — no .claude/ directory found in current project.`);
|
|
@@ -30386,7 +30656,7 @@ Summary: ${result.unchanged} unchanged, ${result.modified.length} modified, ${re
|
|
|
30386
30656
|
}
|
|
30387
30657
|
function syncCommand(program2) {
|
|
30388
30658
|
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 =
|
|
30659
|
+
const targetDir = resolve3(".");
|
|
30390
30660
|
if (options.export) {
|
|
30391
30661
|
await runExport(targetDir, options.export);
|
|
30392
30662
|
return;
|
|
@@ -30400,7 +30670,7 @@ init_package();
|
|
|
30400
30670
|
|
|
30401
30671
|
// src/core/updater.ts
|
|
30402
30672
|
init_package();
|
|
30403
|
-
import { join as
|
|
30673
|
+
import { join as join17 } from "node:path";
|
|
30404
30674
|
init_fs();
|
|
30405
30675
|
|
|
30406
30676
|
// src/core/entry-merger.ts
|
|
@@ -30655,7 +30925,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
|
|
|
30655
30925
|
}
|
|
30656
30926
|
async function updateEntryDoc(targetDir, config, options) {
|
|
30657
30927
|
const layout = getProviderLayout();
|
|
30658
|
-
const entryPath =
|
|
30928
|
+
const entryPath = join17(targetDir, layout.entryFile);
|
|
30659
30929
|
const templateName = getEntryTemplateName2(config.language);
|
|
30660
30930
|
const templatePath = resolveTemplatePath(templateName);
|
|
30661
30931
|
if (!await fileExists(templatePath)) {
|
|
@@ -30734,6 +31004,36 @@ function checkAndInstallRtkAfterUpdate() {
|
|
|
30734
31004
|
}
|
|
30735
31005
|
}
|
|
30736
31006
|
}
|
|
31007
|
+
async function updateProjectRegistry(targetDir, newVersion) {
|
|
31008
|
+
try {
|
|
31009
|
+
const { registerProject: registerProject2 } = await Promise.resolve().then(() => (init_registry(), exports_registry));
|
|
31010
|
+
await registerProject2(targetDir, newVersion);
|
|
31011
|
+
} catch {}
|
|
31012
|
+
}
|
|
31013
|
+
async function regenerateLockfile(targetDir, result) {
|
|
31014
|
+
const lockfileResult = await generateAndWriteLockfileForDir(targetDir);
|
|
31015
|
+
if (lockfileResult.warning) {
|
|
31016
|
+
result.warnings.push(lockfileResult.warning);
|
|
31017
|
+
warn("update.lockfile_failed", { error: lockfileResult.warning });
|
|
31018
|
+
} else {
|
|
31019
|
+
debug("update.lockfile_regenerated", {
|
|
31020
|
+
files: String(lockfileResult.fileCount)
|
|
31021
|
+
});
|
|
31022
|
+
}
|
|
31023
|
+
}
|
|
31024
|
+
async function shouldSkipSelfUpdate2(targetDir, result) {
|
|
31025
|
+
const targetPkgPath = join17(targetDir, "package.json");
|
|
31026
|
+
if (await fileExists(targetPkgPath)) {
|
|
31027
|
+
const targetPkg = await readJsonFile(targetPkgPath);
|
|
31028
|
+
if (targetPkg.name === "oh-my-customcode") {
|
|
31029
|
+
warn("update.self_update_skipped");
|
|
31030
|
+
result.success = true;
|
|
31031
|
+
result.warnings.push("Skipped: source project cannot update itself");
|
|
31032
|
+
return true;
|
|
31033
|
+
}
|
|
31034
|
+
}
|
|
31035
|
+
return false;
|
|
31036
|
+
}
|
|
30737
31037
|
function checkAndInstallCodexAfterUpdate() {
|
|
30738
31038
|
if (!isCodexInstalled()) {
|
|
30739
31039
|
warn("update.codex_missing");
|
|
@@ -30756,15 +31056,8 @@ async function update(options) {
|
|
|
30756
31056
|
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
31057
|
return result;
|
|
30758
31058
|
}
|
|
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
|
-
}
|
|
31059
|
+
if (await shouldSkipSelfUpdate2(options.targetDir, result)) {
|
|
31060
|
+
return result;
|
|
30768
31061
|
}
|
|
30769
31062
|
const updateCheck = await checkForUpdates(options.targetDir);
|
|
30770
31063
|
result.newVersion = updateCheck.latestVersion;
|
|
@@ -30782,17 +31075,12 @@ async function update(options) {
|
|
|
30782
31075
|
const components = options.components || getAllUpdateComponents();
|
|
30783
31076
|
await updateAllComponents(options.targetDir, components, updateCheck, customizations, options, result, config, lockfile);
|
|
30784
31077
|
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
|
-
}
|
|
31078
|
+
await regenerateLockfile(options.targetDir, result);
|
|
30794
31079
|
checkAndInstallRtkAfterUpdate();
|
|
30795
31080
|
checkAndInstallCodexAfterUpdate();
|
|
31081
|
+
if (result.success && !options.dryRun) {
|
|
31082
|
+
await updateProjectRegistry(options.targetDir, result.newVersion);
|
|
31083
|
+
}
|
|
30796
31084
|
} catch (err) {
|
|
30797
31085
|
const message = err instanceof Error ? err.message : String(err);
|
|
30798
31086
|
result.error = message;
|
|
@@ -30872,11 +31160,11 @@ async function collectProtectedSkipPaths(srcPath, destPath, componentPath, force
|
|
|
30872
31160
|
const warnedPaths = [];
|
|
30873
31161
|
const updatedPaths = [];
|
|
30874
31162
|
for (const p of protectedRelative) {
|
|
30875
|
-
const targetFilePath =
|
|
31163
|
+
const targetFilePath = join17(targetDir, componentPath, p);
|
|
30876
31164
|
const lockfileKey = `${componentPath}/${p}`.replace(/\\/g, "/");
|
|
30877
31165
|
const shouldSkip = await shouldSkipProtectedFile(targetFilePath, lockfileKey, lockfile);
|
|
30878
31166
|
if (shouldSkip) {
|
|
30879
|
-
skipPaths.push(path3.relative(destPath,
|
|
31167
|
+
skipPaths.push(path3.relative(destPath, join17(destPath, p)));
|
|
30880
31168
|
warnedPaths.push(p);
|
|
30881
31169
|
} else {
|
|
30882
31170
|
updatedPaths.push(p);
|
|
@@ -30922,7 +31210,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
30922
31210
|
const preservedFiles = [];
|
|
30923
31211
|
const componentPath = getComponentPath2(component);
|
|
30924
31212
|
const srcPath = resolveTemplatePath(componentPath);
|
|
30925
|
-
const destPath =
|
|
31213
|
+
const destPath = join17(targetDir, componentPath);
|
|
30926
31214
|
const customComponents = config.customComponents || [];
|
|
30927
31215
|
const skipPaths = [];
|
|
30928
31216
|
if (customizations && !options.forceOverwriteAll) {
|
|
@@ -30964,7 +31252,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
30964
31252
|
}
|
|
30965
31253
|
skipPaths.push(...protectedSkipPaths);
|
|
30966
31254
|
const path3 = await import("node:path");
|
|
30967
|
-
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath,
|
|
31255
|
+
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join17(targetDir, p)));
|
|
30968
31256
|
const uniqueSkipPaths = [...new Set(normalizedSkipPaths)];
|
|
30969
31257
|
await copyDirectory(srcPath, destPath, {
|
|
30970
31258
|
overwrite: true,
|
|
@@ -30986,12 +31274,12 @@ async function syncRootLevelFiles(targetDir, options) {
|
|
|
30986
31274
|
const layout = getProviderLayout();
|
|
30987
31275
|
const synced = [];
|
|
30988
31276
|
for (const fileName of ROOT_LEVEL_FILES) {
|
|
30989
|
-
const srcPath = resolveTemplatePath(
|
|
31277
|
+
const srcPath = resolveTemplatePath(join17(layout.rootDir, fileName));
|
|
30990
31278
|
if (!await fileExists(srcPath)) {
|
|
30991
31279
|
continue;
|
|
30992
31280
|
}
|
|
30993
|
-
const destPath =
|
|
30994
|
-
await ensureDirectory(
|
|
31281
|
+
const destPath = join17(targetDir, layout.rootDir, fileName);
|
|
31282
|
+
await ensureDirectory(join17(destPath, ".."));
|
|
30995
31283
|
await fs3.copyFile(srcPath, destPath);
|
|
30996
31284
|
if (fileName.endsWith(".sh")) {
|
|
30997
31285
|
await fs3.chmod(destPath, 493);
|
|
@@ -31026,7 +31314,7 @@ async function removeDeprecatedFiles(targetDir, options) {
|
|
|
31026
31314
|
});
|
|
31027
31315
|
continue;
|
|
31028
31316
|
}
|
|
31029
|
-
const fullPath =
|
|
31317
|
+
const fullPath = join17(targetDir, entry.path);
|
|
31030
31318
|
if (await fileExists(fullPath)) {
|
|
31031
31319
|
await fs3.unlink(fullPath);
|
|
31032
31320
|
removed.push(entry.path);
|
|
@@ -31067,7 +31355,7 @@ async function syncNamespaceInFile(targetFilePath, upstreamFilePath) {
|
|
|
31067
31355
|
async function processNamespaceSyncEntry(entry, relPath, fullSrcPath, destPath, componentPath, lockfile) {
|
|
31068
31356
|
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
31069
31357
|
return null;
|
|
31070
|
-
const targetFilePath =
|
|
31358
|
+
const targetFilePath = join17(destPath, relPath);
|
|
31071
31359
|
const lockfileKey = `${componentPath}/${relPath}`.replace(/\\/g, "/");
|
|
31072
31360
|
const shouldSkip = await shouldSkipProtectedFile(targetFilePath, lockfileKey, lockfile);
|
|
31073
31361
|
if (shouldSkip)
|
|
@@ -31082,7 +31370,7 @@ async function applyNamespaceSync(targetDir, component, lockfile) {
|
|
|
31082
31370
|
return [];
|
|
31083
31371
|
const componentPath = getComponentPath2(component);
|
|
31084
31372
|
const srcPath = resolveTemplatePath(componentPath);
|
|
31085
|
-
const destPath =
|
|
31373
|
+
const destPath = join17(targetDir, componentPath);
|
|
31086
31374
|
const fs3 = await import("node:fs/promises");
|
|
31087
31375
|
const synced = [];
|
|
31088
31376
|
const queue = [{ dir: srcPath, relDir: "" }];
|
|
@@ -31096,7 +31384,7 @@ async function applyNamespaceSync(targetDir, component, lockfile) {
|
|
|
31096
31384
|
}
|
|
31097
31385
|
for (const entry of entries) {
|
|
31098
31386
|
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
31099
|
-
const fullSrcPath =
|
|
31387
|
+
const fullSrcPath = join17(dir2, entry.name);
|
|
31100
31388
|
if (entry.isDirectory()) {
|
|
31101
31389
|
queue.push({ dir: fullSrcPath, relDir: relPath });
|
|
31102
31390
|
continue;
|
|
@@ -31119,26 +31407,26 @@ function getComponentPath2(component) {
|
|
|
31119
31407
|
}
|
|
31120
31408
|
async function backupInstallation(targetDir) {
|
|
31121
31409
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
31122
|
-
const backupDir =
|
|
31410
|
+
const backupDir = join17(targetDir, `.omcustom-backup-${timestamp}`);
|
|
31123
31411
|
const fs3 = await import("node:fs/promises");
|
|
31124
31412
|
await ensureDirectory(backupDir);
|
|
31125
31413
|
const layout = getProviderLayout();
|
|
31126
31414
|
const dirsToBackup = [layout.rootDir, "guides"];
|
|
31127
31415
|
for (const dir2 of dirsToBackup) {
|
|
31128
|
-
const srcPath =
|
|
31416
|
+
const srcPath = join17(targetDir, dir2);
|
|
31129
31417
|
if (await fileExists(srcPath)) {
|
|
31130
|
-
const destPath =
|
|
31418
|
+
const destPath = join17(backupDir, dir2);
|
|
31131
31419
|
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
31132
31420
|
}
|
|
31133
31421
|
}
|
|
31134
|
-
const entryPath =
|
|
31422
|
+
const entryPath = join17(targetDir, layout.entryFile);
|
|
31135
31423
|
if (await fileExists(entryPath)) {
|
|
31136
|
-
await fs3.copyFile(entryPath,
|
|
31424
|
+
await fs3.copyFile(entryPath, join17(backupDir, layout.entryFile));
|
|
31137
31425
|
}
|
|
31138
31426
|
return backupDir;
|
|
31139
31427
|
}
|
|
31140
31428
|
async function loadCustomizationManifest(targetDir) {
|
|
31141
|
-
const manifestPath =
|
|
31429
|
+
const manifestPath = join17(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
31142
31430
|
if (await fileExists(manifestPath)) {
|
|
31143
31431
|
return readJsonFile(manifestPath);
|
|
31144
31432
|
}
|
|
@@ -31158,7 +31446,21 @@ async function checkCliVersion(checkFn) {
|
|
|
31158
31446
|
} catch {}
|
|
31159
31447
|
}
|
|
31160
31448
|
async function updateCommand(options = {}, cliVersionCheck = checkSelfUpdate) {
|
|
31161
|
-
|
|
31449
|
+
if (!options.skipSelf) {
|
|
31450
|
+
try {
|
|
31451
|
+
const selfUpdateResult = executeSelfUpdate();
|
|
31452
|
+
if (selfUpdateResult.updated) {
|
|
31453
|
+
console.log(i18n.t("cli.update.selfUpdateDone", {
|
|
31454
|
+
from: selfUpdateResult.fromVersion,
|
|
31455
|
+
to: selfUpdateResult.toVersion
|
|
31456
|
+
}));
|
|
31457
|
+
}
|
|
31458
|
+
} catch {
|
|
31459
|
+
console.warn(i18n.t("cli.update.selfUpdateFailed"));
|
|
31460
|
+
}
|
|
31461
|
+
} else {
|
|
31462
|
+
await checkCliVersion(cliVersionCheck);
|
|
31463
|
+
}
|
|
31162
31464
|
try {
|
|
31163
31465
|
if (options.all) {
|
|
31164
31466
|
await updateAllProjects(options);
|
|
@@ -31382,7 +31684,7 @@ function createProgram() {
|
|
|
31382
31684
|
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
31685
|
await initCommand(options);
|
|
31384
31686
|
});
|
|
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) => {
|
|
31687
|
+
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
31688
|
await updateCommand(options);
|
|
31387
31689
|
});
|
|
31388
31690
|
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 +31725,24 @@ function createProgram() {
|
|
|
31423
31725
|
console.warn(i18n.t("cli.web.deprecated.serveStop"));
|
|
31424
31726
|
await serveStopCommand();
|
|
31425
31727
|
});
|
|
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({
|
|
31728
|
+
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").option("--clean", "Remove registry entries whose project paths no longer exist on disk").action(async (options) => {
|
|
31729
|
+
await projectsCommand({
|
|
31730
|
+
format: options.format,
|
|
31731
|
+
paths: options.path,
|
|
31732
|
+
migrate: options.migrate,
|
|
31733
|
+
clean: options.clean
|
|
31734
|
+
});
|
|
31735
|
+
});
|
|
31736
|
+
program2.command("unregister [path]").description("Remove a project from the local registry").action(async (projectPath) => {
|
|
31737
|
+
const targetPath = projectPath ?? process.cwd();
|
|
31738
|
+
try {
|
|
31739
|
+
await unregisterProject(targetPath);
|
|
31740
|
+
console.log(` 프로젝트가 레지스트리에서 제거되었습니다: ${targetPath}`);
|
|
31741
|
+
} catch (error2) {
|
|
31742
|
+
const msg = error2 instanceof Error ? error2.message : String(error2);
|
|
31743
|
+
console.error(` 레지스트리 제거 실패: ${msg}`);
|
|
31744
|
+
process.exitCode = 1;
|
|
31745
|
+
}
|
|
31428
31746
|
});
|
|
31429
31747
|
program2.hook("preAction", async (thisCommand, actionCommand) => {
|
|
31430
31748
|
const opts = thisCommand.optsWithGlobals();
|