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 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 join2, relative, resolve, sep } from "node:path";
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 = resolve(projectRoot, filePath);
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 resolve(currentDir, "..", "..");
9479
+ return resolve2(currentDir, "..", "..");
9233
9480
  }
9234
9481
  function resolveTemplatePath(relativePath) {
9235
9482
  const packageRoot = getPackageRoot();
9236
- return join2(packageRoot, "templates", relativePath);
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 resolve(...paths);
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 homedir2 } from "node:os";
9424
- import { basename as basename3, dirname as dirname3, join as join9 } from "node:path";
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 = join9(projectDir, ".omcustom.lock.json");
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 home = homedir2();
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
- const searchPaths = [];
9520
- for (const dir2 of DEFAULT_SEARCH_DIRS) {
9521
- searchPaths.push(join9(home, dir2));
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
- if (options.paths) {
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 = join9(projectDir, ".omcustom.lock.json");
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(` 검색 경로: ~/workspace, ~/projects, ~/dev, ~/src, ~/code, ~/repos, ~/work, (현재 디렉토리 및 부모)
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 = homedir2();
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 DEFAULT_SEARCH_DIRS, MAX_SEARCH_DEPTH = 3, projects_default;
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 resolve3;
11376
+ let resolve4;
11189
11377
  let reject;
11190
11378
  const promise = new Promise((res, rej) => {
11191
- resolve3 = res;
11379
+ resolve4 = res;
11192
11380
  reject = rej;
11193
11381
  });
11194
- return { promise, resolve: resolve3, reject };
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: resolve3, reject } = PromisePolyfill.withResolver();
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(() => resolve3(value));
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((resolve3, reject) => {
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
- resolve3((0, exports.detect)(buffer));
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((resolve, reject) => {
22275
- res = resolve;
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 = join(homedir(), ".oh-my-customcode", "self-update-cache.json");
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 join3 } from "node:path";
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 join3(targetDir, CONFIG_FILE);
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 join4 } from "node:path";
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 = join4(targetDir, ".omcustomrc.json");
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 readFile(rcPath, "utf-8"));
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 join5, relative as relative2 } from "node:path";
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((resolve2, reject) => {
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
- resolve2(hash.digest("hex"));
26644
+ resolve3(hash.digest("hex"));
26385
26645
  });
26386
26646
  });
26387
26647
  }
26388
26648
  async function readLockfile(targetDir) {
26389
- const lockfilePath = join5(targetDir, LOCKFILE_NAME);
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 = join5(targetDir, LOCKFILE_NAME);
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 = join5(dir2, entry);
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]) => join5(targetDir, 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(join5(packageRoot, "templates", "manifest.json"));
26497
- const { version: generatorVersion } = await readJsonFile(join5(packageRoot, "package.json"));
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 join11 } from "node:path";
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 basename2, join as join7 } from "node:path";
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 join6 } from "node:path";
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 basename2 = relativePath.split("/").pop() ?? "";
27269
- if (PROTECTED_FRAMEWORK_FILES.includes(basename2)) {
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 = join6(rootDir, fileName);
27286
- const destPath = join6(tempDir, fileName);
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 = join6(rootDir, dirName);
27301
- const destPath = join6(tempDir, dirName);
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 = join6(preservation.tempDir, fileName);
27339
- const targetPath = join6(rootDir, fileName);
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 = join6(preservation.tempDir, dirName);
27356
- const targetPath = join6(rootDir, dirName);
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: basename(targetPath) });
27638
+ debug("preserve.merged_json", { file: basename2(targetPath) });
27379
27639
  } else {
27380
27640
  await copyFile(preservedPath, targetPath);
27381
- debug("preserve.copied_json", { file: basename(targetPath) });
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 join7(packageRoot, "templates");
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 = join7(targetDir, layout.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(join7(layout.rootDir, "statusline.sh"));
27767
- const destPath = join7(targetDir, layout.rootDir, "statusline.sh");
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 = join7(targetDir, layout.rootDir, "settings.local.json");
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 = join7(options.targetDir, layout.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 = join7(packageRoot, "templates", layout.manifestFile);
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 = join7(srcPath, entry);
28191
+ const entrySrcPath = join8(srcPath, entry);
27932
28192
  if (!(await stat2(entrySrcPath)).isDirectory())
27933
28193
  continue;
27934
- const skillMdPath = join7(entrySrcPath, "SKILL.md");
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, join7(destPath, entry), {
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 = join7(srcPath, entry);
28214
+ const entrySrcPath = join8(srcPath, entry);
27955
28215
  const entryStat = await stat2(entrySrcPath);
27956
28216
  if (entryStat.isDirectory()) {
27957
- await copyDirectory(entrySrcPath, join7(destPath, entry), {
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, join7(destPath, entry));
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 = join7(targetDir, templatePath);
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 = join7(targetDir, layout.entryFile);
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 = basename2(sourcePath);
28037
- const backupPath = join7(backupDir, name);
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 = join7(targetDir, relativePath);
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 = join7(targetDir, `${layout.backupDirPrefix}${timestamp}`);
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 = join7(targetDir, relativePath);
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 join8 } from "node:path";
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 = join8(targetDir, ".mcp.json");
28085
- const ontologyDir = join8(layout.rootDir, "ontology");
28086
- const ontologyExists = await fileExists(join8(targetDir, ontologyDir));
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: readFile2 } = await import("node:fs/promises");
28122
- const existingContent = await readFile2(existingConfigPath, "utf-8");
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 writeFile(mcpConfigPath, `${JSON.stringify(existing, null, 2)}
28387
+ await writeFile2(mcpConfigPath, `${JSON.stringify(existing, null, 2)}
28128
28388
  `);
28129
28389
  }
28130
28390
  } catch {
28131
- await writeFile(mcpConfigPath, `${JSON.stringify(config, null, 2)}
28391
+ await writeFile2(mcpConfigPath, `${JSON.stringify(config, null, 2)}
28132
28392
  `);
28133
28393
  }
28134
28394
  } else {
28135
- await writeFile(mcpConfigPath, `${JSON.stringify(config, null, 2)}
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 join10 } from "node:path";
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 = join10(targetDir, layout.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 = join10(snapshotPath, layout.rootDir);
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 = join10(targetDir, `.claude-backup-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, -1)}`);
28185
- await cp(join10(targetDir, layout.rootDir), backupDir, { recursive: true });
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, join10(targetDir, layout.rootDir), {
28452
+ await cp(snapshotClaude, join11(targetDir, layout.rootDir), {
28189
28453
  recursive: true,
28190
28454
  force: true
28191
28455
  });
28192
- const snapshotGuides = join10(snapshotPath, "guides");
28456
+ const snapshotGuides = join11(snapshotPath, "guides");
28193
28457
  if (existsSync2(snapshotGuides)) {
28194
- await cp(snapshotGuides, join10(targetDir, "guides"), {
28458
+ await cp(snapshotGuides, join11(targetDir, "guides"), {
28195
28459
  recursive: true,
28196
28460
  force: true
28197
28461
  });
28198
28462
  }
28199
- const snapshotEntry = join10(snapshotPath, layout.entryFile);
28463
+ const snapshotEntry = join11(snapshotPath, layout.entryFile);
28200
28464
  if (existsSync2(snapshotEntry)) {
28201
- await copyFile2(snapshotEntry, join10(targetDir, layout.entryFile));
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 = join11(targetDir, layout.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 join11(targetDir, layout.entryFile);
29487
+ return join12(targetDir, layout.entryFile);
29221
29488
  }
29222
29489
  if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
29223
29490
  const layout = getProviderLayout();
29224
- return join11(targetDir, layout.rootDir, component);
29491
+ return join12(targetDir, layout.rootDir, component);
29225
29492
  }
29226
- return join11(targetDir, component);
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 basename4, dirname as dirname4, join as join12, relative as relative3 } from "node:path";
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 = basename4(filename, ".md");
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(join12(baseDir, rootDir, "skills"), skillPath);
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(join12(baseDir, "guides"), guidePath);
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 = basename4(filename, ".md");
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 = join12(targetDir, rootDir, "agents");
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 = basename4(agentMdPath);
29522
- const name = basename4(filename, ".md");
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 = join12(targetDir, rootDir, "skills");
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 = dirname4(skillMdPath);
29550
- const indexYamlPath = join12(skillDir, "index.yaml");
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: basename4(skillDir),
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 = join12(targetDir, "guides");
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: basename4(guideMdPath, ".md"),
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 = join12(targetDir, rootDir, "rules");
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 = basename4(ruleMdPath);
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: basename4(ruleMdPath, ".md"),
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 = join12(targetDir, rootDir, "hooks");
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: basename4(hookPath),
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 = join12(targetDir, rootDir, "contexts");
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: basename4(ctxPath, ext),
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 join14 } from "node:path";
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 readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
30085
- import { join as join13 } from "node:path";
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 = join13(process.env.HOME ?? "~", ".omcustom-serve.pid");
30357
+ var PID_FILE = join14(process.env.HOME ?? "~", ".omcustom-serve.pid");
30088
30358
  function findServeBuildDir(projectRoot, options) {
30089
- const localBuild = join13(projectRoot, "packages", "serve", "build");
30090
- if (existsSync3(join13(localBuild, "index.js")))
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 = join13(import.meta.dirname, "..", "..", "packages", "serve", "build");
30094
- if (existsSync3(join13(npmBuild, "index.js")))
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 readFile2(PID_FILE, "utf-8");
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", [join13(buildDir, "index.js")], {
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 writeFile2(PID_FILE, String(child.pid), "utf-8");
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 readFile2(PID_FILE, "utf-8");
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", [join14(buildDir, "index.js")], {
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 resolve2 } from "node:path";
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 join15 } from "node:path";
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(join15(packageRoot, "templates", "manifest.json"));
30223
- const pkg = await readJsonFile(join15(packageRoot, "package.json"));
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 = join15(current, entry);
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 = join15(targetDir, ".claude");
30309
- const guidesDir = join15(targetDir, "guides");
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 mkdir(outputPath, { recursive: true });
30314
- const destClaude = join15(outputPath, ".claude");
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, join15(outputPath, "guides"), { recursive: true });
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, resolve2(outputPath));
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 = resolve2(".");
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 join16 } from "node:path";
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 = join16(targetDir, layout.entryFile);
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
- const targetPkgPath = join16(options.targetDir, "package.json");
30760
- if (await fileExists(targetPkgPath)) {
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
- const lockfileResult = await generateAndWriteLockfileForDir(options.targetDir);
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 = join16(targetDir, componentPath, p);
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, join16(destPath, p)));
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 = join16(targetDir, componentPath);
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, join16(targetDir, p)));
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(join16(layout.rootDir, fileName));
31277
+ const srcPath = resolveTemplatePath(join17(layout.rootDir, fileName));
30990
31278
  if (!await fileExists(srcPath)) {
30991
31279
  continue;
30992
31280
  }
30993
- const destPath = join16(targetDir, layout.rootDir, fileName);
30994
- await ensureDirectory(join16(destPath, ".."));
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 = join16(targetDir, entry.path);
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 = join16(destPath, relPath);
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 = join16(targetDir, componentPath);
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 = join16(dir2, entry.name);
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 = join16(targetDir, `.omcustom-backup-${timestamp}`);
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 = join16(targetDir, dir2);
31416
+ const srcPath = join17(targetDir, dir2);
31129
31417
  if (await fileExists(srcPath)) {
31130
- const destPath = join16(backupDir, dir2);
31418
+ const destPath = join17(backupDir, dir2);
31131
31419
  await copyDirectory(srcPath, destPath, { overwrite: true });
31132
31420
  }
31133
31421
  }
31134
- const entryPath = join16(targetDir, layout.entryFile);
31422
+ const entryPath = join17(targetDir, layout.entryFile);
31135
31423
  if (await fileExists(entryPath)) {
31136
- await fs3.copyFile(entryPath, join16(backupDir, layout.entryFile));
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 = join16(targetDir, CUSTOMIZATION_MANIFEST_FILE);
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
- await checkCliVersion(cliVersionCheck);
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({ format: options.format, paths: options.path });
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();