oh-my-customcode 1.1.100 → 1.1.102
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 +331 -196
- package/dist/index.js +71 -32
- package/package.json +1 -1
- package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +24 -1
- package/templates/guides/claude-code/15-version-compatibility.md +148 -1
- package/templates/manifest.json +1 -1
- package/templates/workflows/auto-dev.yaml +24 -1
package/dist/cli/index.js
CHANGED
|
@@ -49,7 +49,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
49
49
|
// src/utils/home.ts
|
|
50
50
|
import { realpathSync } from "node:fs";
|
|
51
51
|
import { homedir, userInfo } from "node:os";
|
|
52
|
-
import { isAbsolute, parse, sep } from "node:path";
|
|
52
|
+
import { basename, dirname, isAbsolute, join, parse, sep } from "node:path";
|
|
53
53
|
function readEnvHome() {
|
|
54
54
|
return process.env.HOME;
|
|
55
55
|
}
|
|
@@ -99,11 +99,18 @@ function realpathOrSelf(p) {
|
|
|
99
99
|
if (p === "" || !isAbsolute(p)) {
|
|
100
100
|
return p;
|
|
101
101
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
const tail = [];
|
|
103
|
+
let current = p;
|
|
104
|
+
let parent = dirname(current);
|
|
105
|
+
while (parent !== current) {
|
|
106
|
+
try {
|
|
107
|
+
return join(realpathSync(current), ...tail);
|
|
108
|
+
} catch {}
|
|
109
|
+
tail.unshift(basename(current));
|
|
110
|
+
current = parent;
|
|
111
|
+
parent = dirname(current);
|
|
106
112
|
}
|
|
113
|
+
return p;
|
|
107
114
|
}
|
|
108
115
|
function isUnderHome(path, home) {
|
|
109
116
|
const base = home ?? resolveHomeDir();
|
|
@@ -149,7 +156,7 @@ var init_home = () => {};
|
|
|
149
156
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
150
157
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
151
158
|
import { tmpdir } from "node:os";
|
|
152
|
-
import { basename, join, resolve, sep as sep2 } from "node:path";
|
|
159
|
+
import { basename as basename2, join as join2, resolve, sep as sep2 } from "node:path";
|
|
153
160
|
function isTempPath(projectPath) {
|
|
154
161
|
const normalized = resolve(projectPath);
|
|
155
162
|
const candidates = new Set;
|
|
@@ -184,10 +191,10 @@ function registryDir(home) {
|
|
|
184
191
|
if (resolved === "") {
|
|
185
192
|
throw new Error("Could not determine the home directory for the project registry");
|
|
186
193
|
}
|
|
187
|
-
return
|
|
194
|
+
return join2(resolved, ".oh-my-customcode");
|
|
188
195
|
}
|
|
189
196
|
function registryPath() {
|
|
190
|
-
return
|
|
197
|
+
return join2(registryDir(), "projects.json");
|
|
191
198
|
}
|
|
192
199
|
function emptyRegistry() {
|
|
193
200
|
return { projects: {} };
|
|
@@ -288,14 +295,14 @@ async function migrateFromLockfiles(searchDirs) {
|
|
|
288
295
|
} catch {
|
|
289
296
|
return;
|
|
290
297
|
}
|
|
291
|
-
const entry = await parseLockFile(
|
|
298
|
+
const entry = await parseLockFile(join2(dir, ".omcustom.lock.json"));
|
|
292
299
|
if (entry !== null) {
|
|
293
300
|
discovered.set(resolve(dir), entry);
|
|
294
301
|
return;
|
|
295
302
|
}
|
|
296
303
|
if (depth < MAX_DEPTH) {
|
|
297
304
|
const subdirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SCAN_SKIP_DIRS.has(e.name));
|
|
298
|
-
await Promise.all(subdirs.map((sub) => scan(
|
|
305
|
+
await Promise.all(subdirs.map((sub) => scan(join2(dir, sub.name), depth + 1)));
|
|
299
306
|
}
|
|
300
307
|
}
|
|
301
308
|
await Promise.all(searchDirs.map((dir) => scan(dir, 0).catch(() => {})));
|
|
@@ -328,7 +335,7 @@ var init_package = __esm(() => {
|
|
|
328
335
|
workspaces: [
|
|
329
336
|
"packages/*"
|
|
330
337
|
],
|
|
331
|
-
version: "1.1.
|
|
338
|
+
version: "1.1.102",
|
|
332
339
|
description: "Batteries-included agent harness for Claude Code",
|
|
333
340
|
type: "module",
|
|
334
341
|
bin: {
|
|
@@ -7324,7 +7331,7 @@ var require_public_api = __commonJS(function(exports) {
|
|
|
7324
7331
|
});
|
|
7325
7332
|
|
|
7326
7333
|
// src/utils/fs.ts
|
|
7327
|
-
import { dirname as
|
|
7334
|
+
import { dirname as dirname3, isAbsolute as isAbsolute3, join as join4, relative, resolve as resolve2, sep as sep3 } from "node:path";
|
|
7328
7335
|
import { fileURLToPath } from "node:url";
|
|
7329
7336
|
function validatePreserveFilePath(filePath, projectRoot) {
|
|
7330
7337
|
if (!filePath || filePath.trim() === "") {
|
|
@@ -7333,7 +7340,7 @@ function validatePreserveFilePath(filePath, projectRoot) {
|
|
|
7333
7340
|
reason: "Path cannot be empty"
|
|
7334
7341
|
};
|
|
7335
7342
|
}
|
|
7336
|
-
if (
|
|
7343
|
+
if (isAbsolute3(filePath)) {
|
|
7337
7344
|
return {
|
|
7338
7345
|
valid: false,
|
|
7339
7346
|
reason: "Absolute paths are not allowed"
|
|
@@ -7341,7 +7348,7 @@ function validatePreserveFilePath(filePath, projectRoot) {
|
|
|
7341
7348
|
}
|
|
7342
7349
|
const resolvedPath = resolve2(projectRoot, filePath);
|
|
7343
7350
|
const relativePath = relative(projectRoot, resolvedPath);
|
|
7344
|
-
if (relativePath.startsWith("..") ||
|
|
7351
|
+
if (relativePath.startsWith("..") || isAbsolute3(relativePath)) {
|
|
7345
7352
|
return {
|
|
7346
7353
|
valid: false,
|
|
7347
7354
|
reason: "Path cannot traverse outside project root"
|
|
@@ -7483,17 +7490,17 @@ async function readTextFile(path) {
|
|
|
7483
7490
|
}
|
|
7484
7491
|
async function writeTextFile(path, content) {
|
|
7485
7492
|
const fs = await import("node:fs/promises");
|
|
7486
|
-
await ensureDirectory(
|
|
7493
|
+
await ensureDirectory(dirname3(path));
|
|
7487
7494
|
await fs.writeFile(path, content, "utf-8");
|
|
7488
7495
|
}
|
|
7489
7496
|
function getPackageRoot() {
|
|
7490
7497
|
const currentFile = fileURLToPath(import.meta.url);
|
|
7491
|
-
const currentDir =
|
|
7498
|
+
const currentDir = dirname3(currentFile);
|
|
7492
7499
|
return resolve2(currentDir, "..", "..");
|
|
7493
7500
|
}
|
|
7494
7501
|
function resolveTemplatePath(relativePath) {
|
|
7495
7502
|
const packageRoot = getPackageRoot();
|
|
7496
|
-
return
|
|
7503
|
+
return join4(packageRoot, "templates", relativePath);
|
|
7497
7504
|
}
|
|
7498
7505
|
async function listFiles(dir, options = {}) {
|
|
7499
7506
|
const fs = await import("node:fs/promises");
|
|
@@ -7515,7 +7522,7 @@ async function listFiles(dir, options = {}) {
|
|
|
7515
7522
|
}
|
|
7516
7523
|
async function copyFile(src, dest) {
|
|
7517
7524
|
const fs = await import("node:fs/promises");
|
|
7518
|
-
await ensureDirectory(
|
|
7525
|
+
await ensureDirectory(dirname3(dest));
|
|
7519
7526
|
await fs.copyFile(src, dest);
|
|
7520
7527
|
}
|
|
7521
7528
|
async function createTempDir(prefix = "omcustom-") {
|
|
@@ -7536,9 +7543,9 @@ var UTF8_BOM = "\uFEFF";
|
|
|
7536
7543
|
var init_fs = () => {};
|
|
7537
7544
|
|
|
7538
7545
|
// src/cli/projects.ts
|
|
7539
|
-
import { basename as
|
|
7546
|
+
import { basename as basename5, join as join12, resolve as resolve3 } from "node:path";
|
|
7540
7547
|
async function readLockFile(projectDir) {
|
|
7541
|
-
const lockFilePath =
|
|
7548
|
+
const lockFilePath = join12(projectDir, ".omcustom.lock.json");
|
|
7542
7549
|
try {
|
|
7543
7550
|
const fs = await import("node:fs/promises");
|
|
7544
7551
|
const content = await fs.readFile(lockFilePath, "utf-8");
|
|
@@ -7608,7 +7615,7 @@ async function findProjects(options = {}) {
|
|
|
7608
7615
|
if (!isUnderHome(projectPath, home))
|
|
7609
7616
|
continue;
|
|
7610
7617
|
results.push({
|
|
7611
|
-
name:
|
|
7618
|
+
name: basename5(projectPath),
|
|
7612
7619
|
path: projectPath,
|
|
7613
7620
|
version: entry.version || null,
|
|
7614
7621
|
installedAt: entry.installedAt || null,
|
|
@@ -7634,7 +7641,7 @@ async function _scanDirForLockfiles(dir, depth, seen, results, home, currentVers
|
|
|
7634
7641
|
if (isUnderHome(dir, home)) {
|
|
7635
7642
|
const version = lockFile.version || lockFile.templateVersion || null;
|
|
7636
7643
|
results.push({
|
|
7637
|
-
name:
|
|
7644
|
+
name: basename5(dir),
|
|
7638
7645
|
path: dir,
|
|
7639
7646
|
version,
|
|
7640
7647
|
installedAt: lockFile.installedAt || null,
|
|
@@ -7647,7 +7654,7 @@ async function _scanDirForLockfiles(dir, depth, seen, results, home, currentVers
|
|
|
7647
7654
|
}
|
|
7648
7655
|
if (depth < 3) {
|
|
7649
7656
|
const subdirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !SCAN_SKIP_DIRS2.has(e.name));
|
|
7650
|
-
await Promise.all(subdirs.map((sub) => _scanDirForLockfiles(
|
|
7657
|
+
await Promise.all(subdirs.map((sub) => _scanDirForLockfiles(join12(dir, sub.name), depth + 1, seen, results, home, currentVersion, fs).catch(() => {})));
|
|
7651
7658
|
}
|
|
7652
7659
|
}
|
|
7653
7660
|
async function _findProjectsFromLockfiles(options, currentVersion) {
|
|
@@ -7671,7 +7678,7 @@ async function _findProjectsFromLockfiles(options, currentVersion) {
|
|
|
7671
7678
|
}
|
|
7672
7679
|
async function writeLockFile(projectDir, version, existing) {
|
|
7673
7680
|
const fs = await import("node:fs/promises");
|
|
7674
|
-
const lockFilePath =
|
|
7681
|
+
const lockFilePath = join12(projectDir, ".omcustom.lock.json");
|
|
7675
7682
|
const now = new Date().toISOString();
|
|
7676
7683
|
const merged = {
|
|
7677
7684
|
...existing || {},
|
|
@@ -7730,7 +7737,7 @@ oh-my-customcode 적용 프로젝트 (${projects.length}개):`);
|
|
|
7730
7737
|
현재 설치 버전: v${currentVersion}`);
|
|
7731
7738
|
}
|
|
7732
7739
|
function buildMigrationSearchDirs(home, extraPaths = []) {
|
|
7733
|
-
return [...home ? DEFAULT_SEARCH_DIRS.map((d) =>
|
|
7740
|
+
return [...home ? DEFAULT_SEARCH_DIRS.map((d) => join12(home, d)) : [], ...extraPaths];
|
|
7734
7741
|
}
|
|
7735
7742
|
async function runMigration(options) {
|
|
7736
7743
|
await Promise.resolve().then(() => init_registry());
|
|
@@ -11965,8 +11972,7 @@ init_registry();
|
|
|
11965
11972
|
init_package();
|
|
11966
11973
|
import { execSync as execSync2, spawnSync } from "node:child_process";
|
|
11967
11974
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11968
|
-
import {
|
|
11969
|
-
import { dirname, join as join2 } from "node:path";
|
|
11975
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join3 } from "node:path";
|
|
11970
11976
|
import { createInterface } from "node:readline/promises";
|
|
11971
11977
|
|
|
11972
11978
|
// node_modules/.bun/i18next@26.0.2+ff0372d108591aba/node_modules/i18next/dist/esm/i18next.js
|
|
@@ -14658,7 +14664,9 @@ var en_default = {
|
|
|
14658
14664
|
openOption: "Open browser automatically after start",
|
|
14659
14665
|
foregroundOption: "Run in foreground (not detached)",
|
|
14660
14666
|
started: "Web UI started: http://localhost:{{port}}",
|
|
14661
|
-
failed: "Failed to start Web UI server"
|
|
14667
|
+
failed: "Failed to start Web UI server",
|
|
14668
|
+
homeUnresolved: "Cannot start the Web UI server: the home directory cannot be resolved, either because HOME is a relative path or because HOME, the OS-reported home and the passwd entry are all empty, so the PID file location cannot be determined. Set HOME to an absolute path.",
|
|
14669
|
+
pidNotWritable: "Cannot start the Web UI server: the PID file {{path}} cannot be written ({{error}}). Check the PID file path and its parent directory."
|
|
14662
14670
|
},
|
|
14663
14671
|
stop: {
|
|
14664
14672
|
description: "Stop the Web UI server",
|
|
@@ -15076,7 +15084,9 @@ var ko_default = {
|
|
|
15076
15084
|
openOption: "시작 후 브라우저 자동 열기",
|
|
15077
15085
|
foregroundOption: "포그라운드 실행 (백그라운드 분리 안 함)",
|
|
15078
15086
|
started: "Web UI 시작됨: http://localhost:{{port}}",
|
|
15079
|
-
failed: "Web UI 서버 시작 실패"
|
|
15087
|
+
failed: "Web UI 서버 시작 실패",
|
|
15088
|
+
homeUnresolved: "Web UI 서버를 시작할 수 없습니다: 홈 디렉토리를 확인할 수 없어 PID 파일 위치를 정할 수 없습니다. HOME이 상대 경로이거나, HOME·OS 보고 홈·passwd 항목이 모두 비어 있는 경우입니다. HOME을 절대 경로로 설정하세요.",
|
|
15089
|
+
pidNotWritable: "Web UI 서버를 시작할 수 없습니다: PID 파일 {{path}}에 쓸 수 없습니다 ({{error}}). PID 파일 경로와 그 상위 디렉토리를 확인하세요."
|
|
15080
15090
|
},
|
|
15081
15091
|
stop: {
|
|
15082
15092
|
description: "Web UI 서버 중지",
|
|
@@ -15262,9 +15272,18 @@ var i18n = {
|
|
|
15262
15272
|
};
|
|
15263
15273
|
|
|
15264
15274
|
// src/core/self-update.ts
|
|
15275
|
+
init_home();
|
|
15265
15276
|
var DEFAULT_PACKAGE_NAME = "oh-my-customcode";
|
|
15266
15277
|
var DEFAULT_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
15267
|
-
var
|
|
15278
|
+
var CACHE_DIR_NAME = ".oh-my-customcode";
|
|
15279
|
+
var CACHE_FILE_NAME = "self-update-cache.json";
|
|
15280
|
+
function resolveSelfUpdateCachePath(sources) {
|
|
15281
|
+
const home = resolveHomeDir(sources);
|
|
15282
|
+
if (!isAbsolute2(home)) {
|
|
15283
|
+
return null;
|
|
15284
|
+
}
|
|
15285
|
+
return join3(home, CACHE_DIR_NAME, CACHE_FILE_NAME);
|
|
15286
|
+
}
|
|
15268
15287
|
var MS_EPOCH_THRESHOLD = 1000000000000;
|
|
15269
15288
|
function normalizeVersion(version) {
|
|
15270
15289
|
return version.trim().replace(/^v/i, "").split("-")[0] || "";
|
|
@@ -15360,20 +15379,36 @@ function readSelfUpdateCache(cachePath) {
|
|
|
15360
15379
|
}
|
|
15361
15380
|
return null;
|
|
15362
15381
|
}
|
|
15382
|
+
function readCacheAt(cachePath) {
|
|
15383
|
+
return cachePath === null ? null : readSelfUpdateCache(cachePath);
|
|
15384
|
+
}
|
|
15385
|
+
function isFileSystemError(error) {
|
|
15386
|
+
const { code, errno } = error ?? {};
|
|
15387
|
+
return typeof code === "string" && typeof errno === "number";
|
|
15388
|
+
}
|
|
15363
15389
|
function writeCache(cachePath, latestVersion, now) {
|
|
15364
|
-
|
|
15365
|
-
|
|
15366
|
-
mkdirSync(dir, { recursive: true });
|
|
15390
|
+
if (cachePath === null) {
|
|
15391
|
+
return;
|
|
15367
15392
|
}
|
|
15368
|
-
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
|
|
15373
|
-
|
|
15374
|
-
|
|
15375
|
-
|
|
15393
|
+
try {
|
|
15394
|
+
const dir = dirname2(cachePath);
|
|
15395
|
+
if (!existsSync(dir)) {
|
|
15396
|
+
mkdirSync(dir, { recursive: true });
|
|
15397
|
+
}
|
|
15398
|
+
const payload = {
|
|
15399
|
+
checkedAt: new Date(now).toISOString(),
|
|
15400
|
+
latestVersion,
|
|
15401
|
+
version: latestVersion,
|
|
15402
|
+
timestamp: Math.floor(now / 1000),
|
|
15403
|
+
source: "omcustom-cli"
|
|
15404
|
+
};
|
|
15405
|
+
writeFileSync(cachePath, `${JSON.stringify(payload, null, 2)}
|
|
15376
15406
|
`, "utf-8");
|
|
15407
|
+
} catch (error) {
|
|
15408
|
+
if (!isFileSystemError(error)) {
|
|
15409
|
+
throw error;
|
|
15410
|
+
}
|
|
15411
|
+
}
|
|
15377
15412
|
}
|
|
15378
15413
|
function isCacheFresh(cache, now, cacheTtlMs) {
|
|
15379
15414
|
const checkedAt = new Date(cache.checkedAt).getTime();
|
|
@@ -15513,7 +15548,7 @@ function executeSelfUpdate(options = {}) {
|
|
|
15513
15548
|
}
|
|
15514
15549
|
function checkSelfUpdate(options) {
|
|
15515
15550
|
const packageName = options.packageName || DEFAULT_PACKAGE_NAME;
|
|
15516
|
-
const cachePath = options.cachePath ||
|
|
15551
|
+
const cachePath = options.cachePath || resolveSelfUpdateCachePath();
|
|
15517
15552
|
const cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
15518
15553
|
const fetchLatestVersion = options.fetchLatestVersion || fetchLatestVersionFromNpm;
|
|
15519
15554
|
const now = options.now ?? Date.now();
|
|
@@ -15529,7 +15564,7 @@ function checkSelfUpdate(options) {
|
|
|
15529
15564
|
}
|
|
15530
15565
|
let latestVersion = null;
|
|
15531
15566
|
let usedCache = false;
|
|
15532
|
-
const cache =
|
|
15567
|
+
const cache = readCacheAt(cachePath);
|
|
15533
15568
|
if (cache && isCacheFresh(cache, now, cacheTtlMs)) {
|
|
15534
15569
|
const cachedVersion = normalizeVersion(cache.latestVersion);
|
|
15535
15570
|
if (isVersionPlausible(currentVersion, cachedVersion)) {
|
|
@@ -16118,7 +16153,7 @@ function installCodex(deps = defaultDeps) {
|
|
|
16118
16153
|
|
|
16119
16154
|
// src/core/config.ts
|
|
16120
16155
|
init_fs();
|
|
16121
|
-
import { join as
|
|
16156
|
+
import { join as join5 } from "node:path";
|
|
16122
16157
|
var CONFIG_FILE = ".omcustomrc.json";
|
|
16123
16158
|
var CURRENT_CONFIG_VERSION = 1;
|
|
16124
16159
|
function getDefaultConfig() {
|
|
@@ -16158,7 +16193,7 @@ function getDefaultPreferences() {
|
|
|
16158
16193
|
};
|
|
16159
16194
|
}
|
|
16160
16195
|
function getConfigPath(targetDir) {
|
|
16161
|
-
return
|
|
16196
|
+
return join5(targetDir, CONFIG_FILE);
|
|
16162
16197
|
}
|
|
16163
16198
|
async function loadConfig(targetDir) {
|
|
16164
16199
|
const configPath = getConfigPath(targetDir);
|
|
@@ -16256,9 +16291,9 @@ function migrateConfig(config) {
|
|
|
16256
16291
|
// src/core/doctor-framework.ts
|
|
16257
16292
|
init_fs();
|
|
16258
16293
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
16259
|
-
import { join as
|
|
16294
|
+
import { join as join6 } from "node:path";
|
|
16260
16295
|
async function getInstalledVersion(targetDir) {
|
|
16261
|
-
const rcPath =
|
|
16296
|
+
const rcPath = join6(targetDir, ".omcustomrc.json");
|
|
16262
16297
|
if (!await fileExists(rcPath))
|
|
16263
16298
|
return null;
|
|
16264
16299
|
try {
|
|
@@ -16330,7 +16365,7 @@ init_fs();
|
|
|
16330
16365
|
import { createHash } from "node:crypto";
|
|
16331
16366
|
import { createReadStream } from "node:fs";
|
|
16332
16367
|
import { readdir, stat } from "node:fs/promises";
|
|
16333
|
-
import { join as
|
|
16368
|
+
import { join as join7, relative as relative2 } from "node:path";
|
|
16334
16369
|
var LOCKFILE_NAME = ".omcustom.lock.json";
|
|
16335
16370
|
var LOCKFILE_VERSION = 1;
|
|
16336
16371
|
var LOCKFILE_COMPONENTS = [
|
|
@@ -16359,7 +16394,7 @@ function computeFileHash(filePath) {
|
|
|
16359
16394
|
});
|
|
16360
16395
|
}
|
|
16361
16396
|
async function readLockfile(targetDir) {
|
|
16362
|
-
const lockfilePath =
|
|
16397
|
+
const lockfilePath = join7(targetDir, LOCKFILE_NAME);
|
|
16363
16398
|
const exists = await fileExists(lockfilePath);
|
|
16364
16399
|
if (!exists) {
|
|
16365
16400
|
debug("lockfile.not_found", { path: lockfilePath });
|
|
@@ -16383,7 +16418,7 @@ async function readLockfile(targetDir) {
|
|
|
16383
16418
|
}
|
|
16384
16419
|
}
|
|
16385
16420
|
async function writeLockfile(targetDir, lockfile) {
|
|
16386
|
-
const lockfilePath =
|
|
16421
|
+
const lockfilePath = join7(targetDir, LOCKFILE_NAME);
|
|
16387
16422
|
await writeJsonFile(lockfilePath, lockfile);
|
|
16388
16423
|
debug("lockfile.written", { path: lockfilePath });
|
|
16389
16424
|
}
|
|
@@ -16408,7 +16443,7 @@ async function collectFiles(dir, projectRoot, isTopLevel) {
|
|
|
16408
16443
|
if (isTopLevel && entry.startsWith(".") && entry !== ".claude") {
|
|
16409
16444
|
continue;
|
|
16410
16445
|
}
|
|
16411
|
-
const fullPath =
|
|
16446
|
+
const fullPath = join7(dir, entry);
|
|
16412
16447
|
let fileStat;
|
|
16413
16448
|
try {
|
|
16414
16449
|
fileStat = await stat(fullPath);
|
|
@@ -16424,9 +16459,13 @@ async function collectFiles(dir, projectRoot, isTopLevel) {
|
|
|
16424
16459
|
}
|
|
16425
16460
|
return results;
|
|
16426
16461
|
}
|
|
16462
|
+
function sortFilesByPath(files) {
|
|
16463
|
+
const sorted = Object.keys(files).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
16464
|
+
return Object.fromEntries(sorted.map((key) => [key, files[key]]));
|
|
16465
|
+
}
|
|
16427
16466
|
async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
16428
16467
|
const files = {};
|
|
16429
|
-
const componentRoots = COMPONENT_PATHS.map(([prefix]) =>
|
|
16468
|
+
const componentRoots = COMPONENT_PATHS.map(([prefix]) => join7(targetDir, prefix));
|
|
16430
16469
|
for (const componentRoot of componentRoots) {
|
|
16431
16470
|
const exists = await fileExists(componentRoot);
|
|
16432
16471
|
if (!exists) {
|
|
@@ -16460,15 +16499,50 @@ async function generateLockfile(targetDir, generatorVersion, templateVersion) {
|
|
|
16460
16499
|
generatorVersion,
|
|
16461
16500
|
generatedAt: new Date().toISOString(),
|
|
16462
16501
|
templateVersion,
|
|
16463
|
-
files
|
|
16502
|
+
files: sortFilesByPath(files)
|
|
16464
16503
|
};
|
|
16465
16504
|
}
|
|
16505
|
+
function hasSameLockfileContent(next, previous) {
|
|
16506
|
+
if (next.lockfileVersion !== previous.lockfileVersion || next.generatorVersion !== previous.generatorVersion || next.templateVersion !== previous.templateVersion) {
|
|
16507
|
+
return false;
|
|
16508
|
+
}
|
|
16509
|
+
const nextKeys = Object.keys(next.files);
|
|
16510
|
+
if (nextKeys.length !== Object.keys(previous.files).length) {
|
|
16511
|
+
return false;
|
|
16512
|
+
}
|
|
16513
|
+
return nextKeys.every((key) => {
|
|
16514
|
+
if (!Object.hasOwn(previous.files, key)) {
|
|
16515
|
+
return false;
|
|
16516
|
+
}
|
|
16517
|
+
const before = previous.files[key];
|
|
16518
|
+
const after = next.files[key];
|
|
16519
|
+
return typeof before === "object" && before !== null && before.templateHash === after.templateHash && before.size === after.size && before.component === after.component;
|
|
16520
|
+
});
|
|
16521
|
+
}
|
|
16522
|
+
function isIsoTimestamp(value) {
|
|
16523
|
+
if (typeof value !== "string") {
|
|
16524
|
+
return false;
|
|
16525
|
+
}
|
|
16526
|
+
const time = new Date(value).getTime();
|
|
16527
|
+
return !Number.isNaN(time) && new Date(time).toISOString() === value;
|
|
16528
|
+
}
|
|
16529
|
+
function preserveGeneratedAt(next, previous) {
|
|
16530
|
+
if (previous === null || !isIsoTimestamp(previous.generatedAt)) {
|
|
16531
|
+
return next;
|
|
16532
|
+
}
|
|
16533
|
+
if (!hasSameLockfileContent(next, previous)) {
|
|
16534
|
+
return next;
|
|
16535
|
+
}
|
|
16536
|
+
return { ...next, generatedAt: previous.generatedAt };
|
|
16537
|
+
}
|
|
16466
16538
|
async function generateAndWriteLockfileForDir(targetDir) {
|
|
16467
16539
|
try {
|
|
16468
16540
|
const packageRoot = getPackageRoot();
|
|
16469
|
-
const manifest = await readJsonFile(
|
|
16470
|
-
const { version: generatorVersion } = await readJsonFile(
|
|
16471
|
-
const
|
|
16541
|
+
const manifest = await readJsonFile(join7(packageRoot, "templates", "manifest.json"));
|
|
16542
|
+
const { version: generatorVersion } = await readJsonFile(join7(packageRoot, "package.json"));
|
|
16543
|
+
const generated = await generateLockfile(targetDir, generatorVersion, manifest.version);
|
|
16544
|
+
const previous = await readLockfile(targetDir);
|
|
16545
|
+
const lockfile = preserveGeneratedAt(generated, previous);
|
|
16472
16546
|
await writeLockfile(targetDir, lockfile);
|
|
16473
16547
|
return { fileCount: Object.keys(lockfile.files).length };
|
|
16474
16548
|
} catch (err) {
|
|
@@ -17217,7 +17291,7 @@ async function doctorCommand(options = {}) {
|
|
|
17217
17291
|
|
|
17218
17292
|
// src/cli/init.ts
|
|
17219
17293
|
init_package();
|
|
17220
|
-
import { join as
|
|
17294
|
+
import { join as join14 } from "node:path";
|
|
17221
17295
|
|
|
17222
17296
|
// src/core/installer.ts
|
|
17223
17297
|
init_fs();
|
|
@@ -17228,11 +17302,11 @@ import {
|
|
|
17228
17302
|
rename,
|
|
17229
17303
|
stat as stat2
|
|
17230
17304
|
} from "node:fs/promises";
|
|
17231
|
-
import { basename as
|
|
17305
|
+
import { basename as basename4, dirname as dirname5, join as join9 } from "node:path";
|
|
17232
17306
|
|
|
17233
17307
|
// src/core/file-preservation.ts
|
|
17234
17308
|
init_fs();
|
|
17235
|
-
import { basename as
|
|
17309
|
+
import { basename as basename3, join as join8 } from "node:path";
|
|
17236
17310
|
|
|
17237
17311
|
// src/core/hook-group-merge.ts
|
|
17238
17312
|
var LEGACY_OMCUSTOM_DESCRIPTIONS = [
|
|
@@ -17366,8 +17440,8 @@ function matchesGlobPattern(filePath, pattern) {
|
|
|
17366
17440
|
return regex.test(filePath);
|
|
17367
17441
|
}
|
|
17368
17442
|
async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
17369
|
-
const srcPath =
|
|
17370
|
-
const destPath =
|
|
17443
|
+
const srcPath = join8(rootDir, fileName);
|
|
17444
|
+
const destPath = join8(tempDir, fileName);
|
|
17371
17445
|
try {
|
|
17372
17446
|
if (await fileExists(srcPath)) {
|
|
17373
17447
|
await copyFile(srcPath, destPath);
|
|
@@ -17381,8 +17455,8 @@ async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
|
17381
17455
|
}
|
|
17382
17456
|
}
|
|
17383
17457
|
async function extractSingleDir(dirName, rootDir, tempDir, result) {
|
|
17384
|
-
const srcPath =
|
|
17385
|
-
const destPath =
|
|
17458
|
+
const srcPath = join8(rootDir, dirName);
|
|
17459
|
+
const destPath = join8(tempDir, dirName);
|
|
17386
17460
|
try {
|
|
17387
17461
|
if (await fileExists(srcPath)) {
|
|
17388
17462
|
await copyDirectory(srcPath, destPath, { overwrite: true, preserveTimestamps: true });
|
|
@@ -17419,8 +17493,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
17419
17493
|
failures: []
|
|
17420
17494
|
};
|
|
17421
17495
|
for (const fileName of preservation.extractedFiles) {
|
|
17422
|
-
const preservedPath =
|
|
17423
|
-
const targetPath =
|
|
17496
|
+
const preservedPath = join8(preservation.tempDir, fileName);
|
|
17497
|
+
const targetPath = join8(rootDir, fileName);
|
|
17424
17498
|
try {
|
|
17425
17499
|
if (fileName.endsWith(".json")) {
|
|
17426
17500
|
await mergeJsonFile(preservedPath, targetPath);
|
|
@@ -17436,8 +17510,8 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
17436
17510
|
}
|
|
17437
17511
|
}
|
|
17438
17512
|
for (const dirName of preservation.extractedDirs) {
|
|
17439
|
-
const preservedPath =
|
|
17440
|
-
const targetPath =
|
|
17513
|
+
const preservedPath = join8(preservation.tempDir, dirName);
|
|
17514
|
+
const targetPath = join8(rootDir, dirName);
|
|
17441
17515
|
try {
|
|
17442
17516
|
await copyDirectory(preservedPath, targetPath, {
|
|
17443
17517
|
overwrite: false,
|
|
@@ -17462,14 +17536,14 @@ async function mergeJsonFile(preservedPath, targetPath) {
|
|
|
17462
17536
|
if (await fileExists(targetPath)) {
|
|
17463
17537
|
const { data: targetData } = parseJsonText(await readTextFile(targetPath));
|
|
17464
17538
|
const merged = deepMerge(targetData, preservedData);
|
|
17465
|
-
if (HOOKS_MERGE_FILES.has(
|
|
17539
|
+
if (HOOKS_MERGE_FILES.has(basename3(targetPath)) && isGeneratedHookBlock(targetData.hooks)) {
|
|
17466
17540
|
merged.hooks = mergeHookBlocks(preservedData.hooks, targetData.hooks);
|
|
17467
17541
|
}
|
|
17468
17542
|
await writeTextFile(targetPath, stringifyJson(merged, format));
|
|
17469
|
-
debug("preserve.merged_json", { file:
|
|
17543
|
+
debug("preserve.merged_json", { file: basename3(targetPath) });
|
|
17470
17544
|
} else {
|
|
17471
17545
|
await copyFile(preservedPath, targetPath);
|
|
17472
|
-
debug("preserve.copied_json", { file:
|
|
17546
|
+
debug("preserve.copied_json", { file: basename3(targetPath) });
|
|
17473
17547
|
}
|
|
17474
17548
|
}
|
|
17475
17549
|
function deepMerge(target, source) {
|
|
@@ -17807,7 +17881,7 @@ function migrateHookCommands(settings) {
|
|
|
17807
17881
|
|
|
17808
17882
|
// src/core/hooks-settings.ts
|
|
17809
17883
|
init_fs();
|
|
17810
|
-
import { dirname as
|
|
17884
|
+
import { dirname as dirname4 } from "node:path";
|
|
17811
17885
|
var IDENT_CHAR = /[A-Za-z0-9_.]/;
|
|
17812
17886
|
var IDENT_START = /[A-Za-z_]/;
|
|
17813
17887
|
function consumeSingleCharOp(input, i) {
|
|
@@ -18311,7 +18385,7 @@ async function mergeHooksIntoSettings(settingsPath, hooksJsonPath, options = {})
|
|
|
18311
18385
|
}
|
|
18312
18386
|
const settings = existing.ok ? existing.settings : {};
|
|
18313
18387
|
settings.hooks = preserve ? mergeHookBlocks(settings.hooks, hooks) : hooks;
|
|
18314
|
-
await ensureDirectory(
|
|
18388
|
+
await ensureDirectory(dirname4(settingsPath));
|
|
18315
18389
|
await writeTextFile(settingsPath, stringifyJson(settings, { bom: existing.bom, trailingNewline: true }));
|
|
18316
18390
|
return { warnings };
|
|
18317
18391
|
}
|
|
@@ -18348,7 +18422,7 @@ function shouldInstallAgent(agentDomain, filterDomain) {
|
|
|
18348
18422
|
var DEFAULT_LANGUAGE2 = "en";
|
|
18349
18423
|
function getTemplateDir() {
|
|
18350
18424
|
const packageRoot = getPackageRoot();
|
|
18351
|
-
return
|
|
18425
|
+
return join9(packageRoot, "templates");
|
|
18352
18426
|
}
|
|
18353
18427
|
function createInstallResult(targetDir) {
|
|
18354
18428
|
return {
|
|
@@ -18370,7 +18444,7 @@ async function handleBackup(targetDir, shouldBackup, result) {
|
|
|
18370
18444
|
if (!shouldBackup)
|
|
18371
18445
|
return null;
|
|
18372
18446
|
const layout = getProviderLayout();
|
|
18373
|
-
const rootDir =
|
|
18447
|
+
const rootDir = join9(targetDir, layout.rootDir);
|
|
18374
18448
|
let preservation = null;
|
|
18375
18449
|
if (await fileExists(rootDir)) {
|
|
18376
18450
|
await Promise.resolve().then(() => init_fs());
|
|
@@ -18427,8 +18501,8 @@ async function installSingleComponent(targetDir, component, options, result) {
|
|
|
18427
18501
|
}
|
|
18428
18502
|
async function installStatusline(targetDir, options, _result) {
|
|
18429
18503
|
const layout = getProviderLayout();
|
|
18430
|
-
const srcPath = resolveTemplatePath(
|
|
18431
|
-
const destPath =
|
|
18504
|
+
const srcPath = resolveTemplatePath(join9(layout.rootDir, "statusline.sh"));
|
|
18505
|
+
const destPath = join9(targetDir, layout.rootDir, "statusline.sh");
|
|
18432
18506
|
if (!await fileExists(srcPath)) {
|
|
18433
18507
|
debug("install.statusline_not_found", { path: srcPath });
|
|
18434
18508
|
return;
|
|
@@ -18446,9 +18520,9 @@ async function installStatusline(targetDir, options, _result) {
|
|
|
18446
18520
|
}
|
|
18447
18521
|
async function installSchemas(targetDir, options, _result) {
|
|
18448
18522
|
const layout = getProviderLayout();
|
|
18449
|
-
const relPath =
|
|
18523
|
+
const relPath = join9(layout.rootDir, "schemas", "tool-inputs.json");
|
|
18450
18524
|
const srcPath = resolveTemplatePath(relPath);
|
|
18451
|
-
const destPath =
|
|
18525
|
+
const destPath = join9(targetDir, relPath);
|
|
18452
18526
|
if (!await fileExists(srcPath)) {
|
|
18453
18527
|
debug("install.schemas_not_found", { path: srcPath });
|
|
18454
18528
|
return;
|
|
@@ -18459,13 +18533,13 @@ async function installSchemas(targetDir, options, _result) {
|
|
|
18459
18533
|
return;
|
|
18460
18534
|
}
|
|
18461
18535
|
}
|
|
18462
|
-
await ensureDirectory(
|
|
18536
|
+
await ensureDirectory(dirname5(destPath));
|
|
18463
18537
|
await copyFile(srcPath, destPath);
|
|
18464
18538
|
debug("install.schemas_installed", {});
|
|
18465
18539
|
}
|
|
18466
18540
|
async function installTestsConfig(targetDir, options, _result) {
|
|
18467
|
-
const srcPath = resolveTemplatePath(
|
|
18468
|
-
const destPath =
|
|
18541
|
+
const srcPath = resolveTemplatePath(join9("tests", "tsconfig.json"));
|
|
18542
|
+
const destPath = join9(targetDir, "tests", "tsconfig.json");
|
|
18469
18543
|
if (!await fileExists(srcPath)) {
|
|
18470
18544
|
debug("install.tests_config_not_found", { path: srcPath });
|
|
18471
18545
|
return;
|
|
@@ -18515,7 +18589,7 @@ async function mergeStatusLineIntoExisting(settingsPath, existing, format, defau
|
|
|
18515
18589
|
}
|
|
18516
18590
|
async function installSettingsLocal(targetDir, result) {
|
|
18517
18591
|
const layout = getProviderLayout();
|
|
18518
|
-
const settingsPath =
|
|
18592
|
+
const settingsPath = join9(targetDir, layout.rootDir, "settings.local.json");
|
|
18519
18593
|
const statusLineConfig = {
|
|
18520
18594
|
statusLine: {
|
|
18521
18595
|
type: "command",
|
|
@@ -18538,8 +18612,8 @@ async function installSettingsLocal(targetDir, result) {
|
|
|
18538
18612
|
}
|
|
18539
18613
|
async function installHooksSettings(targetDir, _options, result) {
|
|
18540
18614
|
const layout = getProviderLayout();
|
|
18541
|
-
const hooksJsonPath =
|
|
18542
|
-
const settingsPath =
|
|
18615
|
+
const hooksJsonPath = join9(targetDir, getComponentPath("hooks"), "hooks.json");
|
|
18616
|
+
const settingsPath = join9(targetDir, layout.rootDir, "settings.local.json");
|
|
18543
18617
|
if (!await fileExists(hooksJsonPath)) {
|
|
18544
18618
|
debug("install.hooks_settings_skipped", { reason: "hooks.json not installed" });
|
|
18545
18619
|
return;
|
|
@@ -18561,7 +18635,7 @@ async function installHooksSettings(targetDir, _options, result) {
|
|
|
18561
18635
|
var BOM = "\uFEFF";
|
|
18562
18636
|
async function migrateRestoredHookCommands(targetDir, result) {
|
|
18563
18637
|
const layout = getProviderLayout();
|
|
18564
|
-
const settingsPath =
|
|
18638
|
+
const settingsPath = join9(targetDir, layout.rootDir, "settings.local.json");
|
|
18565
18639
|
if (!await fileExists(settingsPath)) {
|
|
18566
18640
|
return;
|
|
18567
18641
|
}
|
|
@@ -18647,7 +18721,7 @@ async function install(options) {
|
|
|
18647
18721
|
await installEntryDocWithTracking(options.targetDir, options, result);
|
|
18648
18722
|
if (preservation) {
|
|
18649
18723
|
const layout = getProviderLayout();
|
|
18650
|
-
const rootDir =
|
|
18724
|
+
const rootDir = join9(options.targetDir, layout.rootDir);
|
|
18651
18725
|
const restoration = await restoreCriticalFiles(rootDir, preservation);
|
|
18652
18726
|
if (restoration.restoredFiles.length > 0 || restoration.restoredDirs.length > 0) {
|
|
18653
18727
|
info("install.restored", {
|
|
@@ -18685,7 +18759,7 @@ async function install(options) {
|
|
|
18685
18759
|
async function getTemplateManifest() {
|
|
18686
18760
|
const packageRoot = getPackageRoot();
|
|
18687
18761
|
const layout = getProviderLayout();
|
|
18688
|
-
const manifestPath =
|
|
18762
|
+
const manifestPath = join9(packageRoot, "templates", layout.manifestFile);
|
|
18689
18763
|
if (await fileExists(manifestPath)) {
|
|
18690
18764
|
return readJsonFile(manifestPath);
|
|
18691
18765
|
}
|
|
@@ -18708,10 +18782,10 @@ async function installSkillsWithScopeFilter(srcPath, destPath, options) {
|
|
|
18708
18782
|
await ensureDirectory(destPath);
|
|
18709
18783
|
const entries = await readdir2(srcPath);
|
|
18710
18784
|
for (const entry of entries) {
|
|
18711
|
-
const entrySrcPath =
|
|
18785
|
+
const entrySrcPath = join9(srcPath, entry);
|
|
18712
18786
|
if (!(await stat2(entrySrcPath)).isDirectory())
|
|
18713
18787
|
continue;
|
|
18714
|
-
const skillMdPath =
|
|
18788
|
+
const skillMdPath = join9(entrySrcPath, "SKILL.md");
|
|
18715
18789
|
if (await fileExists(skillMdPath)) {
|
|
18716
18790
|
const content = await fsReadFile(skillMdPath, "utf-8");
|
|
18717
18791
|
const scope = getSkillScope(content);
|
|
@@ -18720,7 +18794,7 @@ async function installSkillsWithScopeFilter(srcPath, destPath, options) {
|
|
|
18720
18794
|
continue;
|
|
18721
18795
|
}
|
|
18722
18796
|
}
|
|
18723
|
-
await copyDirectory(entrySrcPath,
|
|
18797
|
+
await copyDirectory(entrySrcPath, join9(destPath, entry), {
|
|
18724
18798
|
overwrite: !!(options.force || options.backup),
|
|
18725
18799
|
preserveSymlinks: true,
|
|
18726
18800
|
preserveTimestamps: true
|
|
@@ -18731,10 +18805,10 @@ async function installAgentsWithDomainFilter(srcPath, destPath, options) {
|
|
|
18731
18805
|
await ensureDirectory(destPath);
|
|
18732
18806
|
const entries = await readdir2(srcPath);
|
|
18733
18807
|
for (const entry of entries) {
|
|
18734
|
-
const entrySrcPath =
|
|
18808
|
+
const entrySrcPath = join9(srcPath, entry);
|
|
18735
18809
|
const entryStat = await stat2(entrySrcPath);
|
|
18736
18810
|
if (entryStat.isDirectory()) {
|
|
18737
|
-
await copyDirectory(entrySrcPath,
|
|
18811
|
+
await copyDirectory(entrySrcPath, join9(destPath, entry), {
|
|
18738
18812
|
overwrite: !!(options.force || options.backup),
|
|
18739
18813
|
preserveSymlinks: true,
|
|
18740
18814
|
preserveTimestamps: true
|
|
@@ -18751,7 +18825,7 @@ async function installAgentsWithDomainFilter(srcPath, destPath, options) {
|
|
|
18751
18825
|
continue;
|
|
18752
18826
|
}
|
|
18753
18827
|
}
|
|
18754
|
-
await copyFile(entrySrcPath,
|
|
18828
|
+
await copyFile(entrySrcPath, join9(destPath, entry));
|
|
18755
18829
|
}
|
|
18756
18830
|
}
|
|
18757
18831
|
async function installComponent(targetDir, component, options) {
|
|
@@ -18759,7 +18833,7 @@ async function installComponent(targetDir, component, options) {
|
|
|
18759
18833
|
return false;
|
|
18760
18834
|
}
|
|
18761
18835
|
const templatePath = getComponentPath(component);
|
|
18762
|
-
const destPath =
|
|
18836
|
+
const destPath = join9(targetDir, templatePath);
|
|
18763
18837
|
const destExists = await fileExists(destPath);
|
|
18764
18838
|
if (destExists && !options.force && !options.backup) {
|
|
18765
18839
|
debug("install.component_skipped", { component });
|
|
@@ -18793,7 +18867,7 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
18793
18867
|
const layout = getProviderLayout();
|
|
18794
18868
|
const templateFile = getEntryTemplateName(language);
|
|
18795
18869
|
const srcPath = resolveTemplatePath(templateFile);
|
|
18796
|
-
const destPath =
|
|
18870
|
+
const destPath = join9(targetDir, layout.entryFile);
|
|
18797
18871
|
if (!await fileExists(srcPath)) {
|
|
18798
18872
|
warn("install.entry_md_not_found", { language, path: srcPath, entry: layout.entryFile });
|
|
18799
18873
|
return false;
|
|
@@ -18813,8 +18887,8 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
18813
18887
|
return true;
|
|
18814
18888
|
}
|
|
18815
18889
|
async function backupExisting(sourcePath, backupDir) {
|
|
18816
|
-
const name =
|
|
18817
|
-
const backupPath =
|
|
18890
|
+
const name = basename4(sourcePath);
|
|
18891
|
+
const backupPath = join9(backupDir, name);
|
|
18818
18892
|
await rename(sourcePath, backupPath);
|
|
18819
18893
|
return backupPath;
|
|
18820
18894
|
}
|
|
@@ -18823,7 +18897,7 @@ async function checkExistingPaths(targetDir) {
|
|
|
18823
18897
|
const pathsToCheck = [layout.entryFile, layout.rootDir, "guides"];
|
|
18824
18898
|
const existingPaths = [];
|
|
18825
18899
|
for (const relativePath of pathsToCheck) {
|
|
18826
|
-
const fullPath =
|
|
18900
|
+
const fullPath = join9(targetDir, relativePath);
|
|
18827
18901
|
if (await fileExists(fullPath)) {
|
|
18828
18902
|
existingPaths.push(relativePath);
|
|
18829
18903
|
}
|
|
@@ -18837,11 +18911,11 @@ async function backupExistingInstallation(targetDir) {
|
|
|
18837
18911
|
return [];
|
|
18838
18912
|
}
|
|
18839
18913
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
18840
|
-
const backupDir =
|
|
18914
|
+
const backupDir = join9(targetDir, `${layout.backupDirPrefix}${timestamp}`);
|
|
18841
18915
|
await ensureDirectory(backupDir);
|
|
18842
18916
|
const backedUpPaths = [];
|
|
18843
18917
|
for (const relativePath of existingPaths) {
|
|
18844
|
-
const fullPath =
|
|
18918
|
+
const fullPath = join9(targetDir, relativePath);
|
|
18845
18919
|
try {
|
|
18846
18920
|
const backupPath = await backupExisting(fullPath, backupDir);
|
|
18847
18921
|
backedUpPaths.push(backupPath);
|
|
@@ -18857,12 +18931,12 @@ async function backupExistingInstallation(targetDir) {
|
|
|
18857
18931
|
// src/core/mcp-config.ts
|
|
18858
18932
|
init_fs();
|
|
18859
18933
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
18860
|
-
import { join as
|
|
18934
|
+
import { join as join10 } from "node:path";
|
|
18861
18935
|
async function generateMCPConfig(targetDir) {
|
|
18862
18936
|
const layout = getProviderLayout();
|
|
18863
|
-
const mcpConfigPath =
|
|
18864
|
-
const ontologyDir =
|
|
18865
|
-
const ontologyExists = await fileExists(
|
|
18937
|
+
const mcpConfigPath = join10(targetDir, ".mcp.json");
|
|
18938
|
+
const ontologyDir = join10(layout.rootDir, "ontology");
|
|
18939
|
+
const ontologyExists = await fileExists(join10(targetDir, ontologyDir));
|
|
18866
18940
|
if (!ontologyExists) {
|
|
18867
18941
|
return;
|
|
18868
18942
|
}
|
|
@@ -18904,7 +18978,7 @@ async function generateMCPConfig(targetDir) {
|
|
|
18904
18978
|
// src/core/ontology-rag-setup.ts
|
|
18905
18979
|
init_fs();
|
|
18906
18980
|
import { execSync as execSync5 } from "node:child_process";
|
|
18907
|
-
import { join as
|
|
18981
|
+
import { join as join11 } from "node:path";
|
|
18908
18982
|
var MIN_PYTHON_MINOR = 10;
|
|
18909
18983
|
var DETECTION_TIMEOUT_MS = 3000;
|
|
18910
18984
|
var INSTALL_TIMEOUT_MS = 90000;
|
|
@@ -18955,7 +19029,7 @@ function createVenvWithPython3(targetDir) {
|
|
|
18955
19029
|
}
|
|
18956
19030
|
function installOntologyRagEditable(targetDir, useUv) {
|
|
18957
19031
|
const packageRoot = getPackageRoot();
|
|
18958
|
-
const ontologyPkgPath =
|
|
19032
|
+
const ontologyPkgPath = join11(packageRoot, "packages", "ontology-rag");
|
|
18959
19033
|
if (useUv) {
|
|
18960
19034
|
execSync5(`uv pip install --python .venv/bin/python -e "${ontologyPkgPath}"`, {
|
|
18961
19035
|
cwd: targetDir,
|
|
@@ -19028,7 +19102,7 @@ async function setupOntologyRag(targetDir) {
|
|
|
19028
19102
|
reason
|
|
19029
19103
|
};
|
|
19030
19104
|
}
|
|
19031
|
-
const venvPython =
|
|
19105
|
+
const venvPython = join11(targetDir, ".venv", "bin", "python");
|
|
19032
19106
|
const venvReady = await fileExists(venvPython);
|
|
19033
19107
|
if (!venvReady) {
|
|
19034
19108
|
const reason = ".venv/bin/python missing after install — environment may be incomplete. " + "Run `uv pip install -e packages/ontology-rag` manually to complete setup.";
|
|
@@ -19054,12 +19128,12 @@ init_package();
|
|
|
19054
19128
|
init_projects();
|
|
19055
19129
|
import { existsSync as existsSync2 } from "node:fs";
|
|
19056
19130
|
import { copyFile as copyFile2, cp } from "node:fs/promises";
|
|
19057
|
-
import { join as
|
|
19131
|
+
import { join as join13 } from "node:path";
|
|
19058
19132
|
init_fs();
|
|
19059
19133
|
init_registry();
|
|
19060
19134
|
async function checkExistingInstallation(targetDir) {
|
|
19061
19135
|
const layout = getProviderLayout();
|
|
19062
|
-
const rootDir =
|
|
19136
|
+
const rootDir = join13(targetDir, layout.rootDir);
|
|
19063
19137
|
return fileExists(rootDir);
|
|
19064
19138
|
}
|
|
19065
19139
|
async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
@@ -19071,7 +19145,7 @@ async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
|
19071
19145
|
};
|
|
19072
19146
|
}
|
|
19073
19147
|
const layout = getProviderLayout();
|
|
19074
|
-
const snapshotClaude =
|
|
19148
|
+
const snapshotClaude = join13(snapshotPath, layout.rootDir);
|
|
19075
19149
|
if (!existsSync2(snapshotClaude)) {
|
|
19076
19150
|
return {
|
|
19077
19151
|
success: false,
|
|
@@ -19085,24 +19159,24 @@ async function installFromSnapshot(targetDir, snapshotPath, options) {
|
|
|
19085
19159
|
if (exists && !options.force) {
|
|
19086
19160
|
console.log(i18n.t("cli.init.exists", { rootDir: layout.rootDir }));
|
|
19087
19161
|
console.log(i18n.t("cli.init.backing_up"));
|
|
19088
|
-
const backupDir =
|
|
19089
|
-
await cp(
|
|
19162
|
+
const backupDir = join13(targetDir, `.claude-backup-${new Date().toISOString().replace(/[:.]/g, "-").slice(0, -1)}`);
|
|
19163
|
+
await cp(join13(targetDir, layout.rootDir), backupDir, { recursive: true });
|
|
19090
19164
|
console.log(` Backed up to: ${backupDir}`);
|
|
19091
19165
|
}
|
|
19092
|
-
await cp(snapshotClaude,
|
|
19166
|
+
await cp(snapshotClaude, join13(targetDir, layout.rootDir), {
|
|
19093
19167
|
recursive: true,
|
|
19094
19168
|
force: true
|
|
19095
19169
|
});
|
|
19096
|
-
const snapshotGuides =
|
|
19170
|
+
const snapshotGuides = join13(snapshotPath, "guides");
|
|
19097
19171
|
if (existsSync2(snapshotGuides)) {
|
|
19098
|
-
await cp(snapshotGuides,
|
|
19172
|
+
await cp(snapshotGuides, join13(targetDir, "guides"), {
|
|
19099
19173
|
recursive: true,
|
|
19100
19174
|
force: true
|
|
19101
19175
|
});
|
|
19102
19176
|
}
|
|
19103
|
-
const snapshotEntry =
|
|
19177
|
+
const snapshotEntry = join13(snapshotPath, layout.entryFile);
|
|
19104
19178
|
if (existsSync2(snapshotEntry)) {
|
|
19105
|
-
await copyFile2(snapshotEntry,
|
|
19179
|
+
await copyFile2(snapshotEntry, join13(targetDir, layout.entryFile));
|
|
19106
19180
|
}
|
|
19107
19181
|
try {
|
|
19108
19182
|
const existing = await readLockFile(targetDir);
|
|
@@ -19985,7 +20059,7 @@ async function runInitWizard(options) {
|
|
|
19985
20059
|
// src/cli/init.ts
|
|
19986
20060
|
async function checkExistingInstallation2(targetDir) {
|
|
19987
20061
|
const layout = getProviderLayout();
|
|
19988
|
-
const rootDir =
|
|
20062
|
+
const rootDir = join14(targetDir, layout.rootDir);
|
|
19989
20063
|
return fileExists(rootDir);
|
|
19990
20064
|
}
|
|
19991
20065
|
var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
@@ -19999,13 +20073,13 @@ var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
|
19999
20073
|
function componentToPath(targetDir, component) {
|
|
20000
20074
|
if (component === "entry-md") {
|
|
20001
20075
|
const layout = getProviderLayout();
|
|
20002
|
-
return
|
|
20076
|
+
return join14(targetDir, layout.entryFile);
|
|
20003
20077
|
}
|
|
20004
20078
|
if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
|
|
20005
20079
|
const layout = getProviderLayout();
|
|
20006
|
-
return
|
|
20080
|
+
return join14(targetDir, layout.rootDir, component);
|
|
20007
20081
|
}
|
|
20008
|
-
return
|
|
20082
|
+
return join14(targetDir, component);
|
|
20009
20083
|
}
|
|
20010
20084
|
function buildInstalledPaths(targetDir, components) {
|
|
20011
20085
|
return components.map((component) => componentToPath(targetDir, component));
|
|
@@ -20128,7 +20202,7 @@ async function initCommand(options) {
|
|
|
20128
20202
|
}
|
|
20129
20203
|
|
|
20130
20204
|
// src/cli/list.ts
|
|
20131
|
-
import { basename as
|
|
20205
|
+
import { basename as basename6, dirname as dirname6, join as join15, relative as relative3 } from "node:path";
|
|
20132
20206
|
init_fs();
|
|
20133
20207
|
var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
|
|
20134
20208
|
function parseKeyValue(line) {
|
|
@@ -20175,7 +20249,7 @@ function parseYamlMetadata(content) {
|
|
|
20175
20249
|
return result;
|
|
20176
20250
|
}
|
|
20177
20251
|
function extractAgentTypeFromFilename(filename) {
|
|
20178
|
-
const name =
|
|
20252
|
+
const name = basename6(filename, ".md");
|
|
20179
20253
|
const prefixMap = {
|
|
20180
20254
|
lang: "language",
|
|
20181
20255
|
be: "backend",
|
|
@@ -20193,17 +20267,17 @@ function extractAgentTypeFromFilename(filename) {
|
|
|
20193
20267
|
return prefixMap[prefix] || "unknown";
|
|
20194
20268
|
}
|
|
20195
20269
|
function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
|
|
20196
|
-
const relativePath = relative3(
|
|
20270
|
+
const relativePath = relative3(join15(baseDir, rootDir, "skills"), skillPath);
|
|
20197
20271
|
const parts = relativePath.split("/").filter(Boolean);
|
|
20198
20272
|
return parts[0] || "unknown";
|
|
20199
20273
|
}
|
|
20200
20274
|
function extractGuideCategoryFromPath(guidePath, baseDir) {
|
|
20201
|
-
const relativePath = relative3(
|
|
20275
|
+
const relativePath = relative3(join15(baseDir, "guides"), guidePath);
|
|
20202
20276
|
const parts = relativePath.split("/").filter(Boolean);
|
|
20203
20277
|
return parts[0] || "unknown";
|
|
20204
20278
|
}
|
|
20205
20279
|
function extractRulePriorityFromFilename(filename) {
|
|
20206
|
-
const name =
|
|
20280
|
+
const name = basename6(filename, ".md");
|
|
20207
20281
|
const parts = name.split("-");
|
|
20208
20282
|
return parts[0] || "unknown";
|
|
20209
20283
|
}
|
|
@@ -20292,7 +20366,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
|
|
|
20292
20366
|
}
|
|
20293
20367
|
}
|
|
20294
20368
|
async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
20295
|
-
const agentsDir =
|
|
20369
|
+
const agentsDir = join15(targetDir, rootDir, "agents");
|
|
20296
20370
|
if (!await fileExists(agentsDir))
|
|
20297
20371
|
return [];
|
|
20298
20372
|
try {
|
|
@@ -20301,8 +20375,8 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
20301
20375
|
const customAgentPaths = new Set(customComponents.filter((c) => c.type === "agent").map((c) => c.path));
|
|
20302
20376
|
const agentMdFiles = await listFiles(agentsDir, { recursive: false, pattern: "*.md" });
|
|
20303
20377
|
const agents = await Promise.all(agentMdFiles.map(async (agentMdPath) => {
|
|
20304
|
-
const filename =
|
|
20305
|
-
const name =
|
|
20378
|
+
const filename = basename6(agentMdPath);
|
|
20379
|
+
const name = basename6(filename, ".md");
|
|
20306
20380
|
const description = await tryExtractMarkdownDescription(agentMdPath);
|
|
20307
20381
|
const relativePath = relative3(targetDir, agentMdPath);
|
|
20308
20382
|
return {
|
|
@@ -20320,7 +20394,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
20320
20394
|
}
|
|
20321
20395
|
}
|
|
20322
20396
|
async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
20323
|
-
const skillsDir =
|
|
20397
|
+
const skillsDir = join15(targetDir, rootDir, "skills");
|
|
20324
20398
|
if (!await fileExists(skillsDir))
|
|
20325
20399
|
return [];
|
|
20326
20400
|
try {
|
|
@@ -20329,12 +20403,12 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
20329
20403
|
const customSkillPaths = new Set(customComponents.filter((c) => c.type === "skill").map((c) => c.path));
|
|
20330
20404
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
20331
20405
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
20332
|
-
const skillDir =
|
|
20333
|
-
const indexYamlPath =
|
|
20406
|
+
const skillDir = dirname6(skillMdPath);
|
|
20407
|
+
const indexYamlPath = join15(skillDir, "index.yaml");
|
|
20334
20408
|
const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
|
|
20335
20409
|
const relativePath = relative3(targetDir, skillDir);
|
|
20336
20410
|
return {
|
|
20337
|
-
name:
|
|
20411
|
+
name: basename6(skillDir),
|
|
20338
20412
|
type: "skill",
|
|
20339
20413
|
category: extractSkillCategoryFromPath(skillDir, targetDir, rootDir),
|
|
20340
20414
|
path: relativePath,
|
|
@@ -20349,7 +20423,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
20349
20423
|
}
|
|
20350
20424
|
}
|
|
20351
20425
|
async function getGuides(targetDir, config) {
|
|
20352
|
-
const guidesDir =
|
|
20426
|
+
const guidesDir = join15(targetDir, "guides");
|
|
20353
20427
|
if (!await fileExists(guidesDir))
|
|
20354
20428
|
return [];
|
|
20355
20429
|
try {
|
|
@@ -20361,7 +20435,7 @@ async function getGuides(targetDir, config) {
|
|
|
20361
20435
|
const description = await tryExtractMarkdownDescription(guideMdPath, { maxLength: 100 });
|
|
20362
20436
|
const relativePath = relative3(targetDir, guideMdPath);
|
|
20363
20437
|
return {
|
|
20364
|
-
name:
|
|
20438
|
+
name: basename6(guideMdPath, ".md"),
|
|
20365
20439
|
type: "guide",
|
|
20366
20440
|
category: extractGuideCategoryFromPath(guideMdPath, targetDir),
|
|
20367
20441
|
path: relativePath,
|
|
@@ -20376,7 +20450,7 @@ async function getGuides(targetDir, config) {
|
|
|
20376
20450
|
}
|
|
20377
20451
|
var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
|
|
20378
20452
|
async function getRules(targetDir, rootDir = ".claude", config) {
|
|
20379
|
-
const rulesDir =
|
|
20453
|
+
const rulesDir = join15(targetDir, rootDir, "rules");
|
|
20380
20454
|
if (!await fileExists(rulesDir))
|
|
20381
20455
|
return [];
|
|
20382
20456
|
try {
|
|
@@ -20385,13 +20459,13 @@ async function getRules(targetDir, rootDir = ".claude", config) {
|
|
|
20385
20459
|
const customRulePaths = new Set(customComponents.filter((c) => c.type === "rule").map((c) => c.path));
|
|
20386
20460
|
const ruleMdFiles = await listFiles(rulesDir, { recursive: false, pattern: "*.md" });
|
|
20387
20461
|
const rules = await Promise.all(ruleMdFiles.map(async (ruleMdPath) => {
|
|
20388
|
-
const filename =
|
|
20462
|
+
const filename = basename6(ruleMdPath);
|
|
20389
20463
|
const description = await tryExtractMarkdownDescription(ruleMdPath, {
|
|
20390
20464
|
cleanFormatting: true
|
|
20391
20465
|
});
|
|
20392
20466
|
const relativePath = relative3(targetDir, ruleMdPath);
|
|
20393
20467
|
return {
|
|
20394
|
-
name:
|
|
20468
|
+
name: basename6(ruleMdPath, ".md"),
|
|
20395
20469
|
type: extractRulePriorityFromFilename(filename),
|
|
20396
20470
|
path: relativePath,
|
|
20397
20471
|
description,
|
|
@@ -20448,7 +20522,7 @@ function formatAsJson(components) {
|
|
|
20448
20522
|
console.log(JSON.stringify(components, null, 2));
|
|
20449
20523
|
}
|
|
20450
20524
|
async function getHooks(targetDir, rootDir = ".claude") {
|
|
20451
|
-
const hooksDir =
|
|
20525
|
+
const hooksDir = join15(targetDir, rootDir, "hooks");
|
|
20452
20526
|
if (!await fileExists(hooksDir))
|
|
20453
20527
|
return [];
|
|
20454
20528
|
try {
|
|
@@ -20457,7 +20531,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
20457
20531
|
const hookYamls = await listFiles(hooksDir, { recursive: true, pattern: "*.yaml" });
|
|
20458
20532
|
const allFiles = [...hookFiles, ...hookConfigs, ...hookYamls];
|
|
20459
20533
|
return allFiles.map((hookPath) => ({
|
|
20460
|
-
name:
|
|
20534
|
+
name: basename6(hookPath),
|
|
20461
20535
|
type: "hook",
|
|
20462
20536
|
path: relative3(targetDir, hookPath)
|
|
20463
20537
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -20466,7 +20540,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
20466
20540
|
}
|
|
20467
20541
|
}
|
|
20468
20542
|
async function getContexts(targetDir, rootDir = ".claude") {
|
|
20469
|
-
const contextsDir =
|
|
20543
|
+
const contextsDir = join15(targetDir, rootDir, "contexts");
|
|
20470
20544
|
if (!await fileExists(contextsDir))
|
|
20471
20545
|
return [];
|
|
20472
20546
|
try {
|
|
@@ -20477,7 +20551,7 @@ async function getContexts(targetDir, rootDir = ".claude") {
|
|
|
20477
20551
|
const ext = ctxPath.endsWith(".md") ? ".md" : ".yaml";
|
|
20478
20552
|
const description = ext === ".md" ? await tryExtractMarkdownDescription(ctxPath, { maxLength: 100 }) : undefined;
|
|
20479
20553
|
return {
|
|
20480
|
-
name:
|
|
20554
|
+
name: basename6(ctxPath, ext),
|
|
20481
20555
|
type: "context",
|
|
20482
20556
|
path: relative3(targetDir, ctxPath),
|
|
20483
20557
|
description
|
|
@@ -20859,50 +20933,85 @@ async function securityCommand(_options = {}) {
|
|
|
20859
20933
|
|
|
20860
20934
|
// src/cli/serve-commands.ts
|
|
20861
20935
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
20862
|
-
import { join as
|
|
20936
|
+
import { join as join17 } from "node:path";
|
|
20863
20937
|
|
|
20864
20938
|
// src/cli/serve.ts
|
|
20939
|
+
init_home();
|
|
20865
20940
|
import { spawn } from "node:child_process";
|
|
20866
|
-
import { existsSync as existsSync3 } from "node:fs";
|
|
20867
|
-
import { readFile as readFile3, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
20868
|
-
import { join as
|
|
20941
|
+
import { constants as constants3, existsSync as existsSync3 } from "node:fs";
|
|
20942
|
+
import { access, readFile as readFile3, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
20943
|
+
import { dirname as dirname7, isAbsolute as isAbsolute4, join as join16 } from "node:path";
|
|
20869
20944
|
var DEFAULT_PORT = 4321;
|
|
20870
|
-
var
|
|
20945
|
+
var PID_FILE_NAME = ".omcustom-serve.pid";
|
|
20946
|
+
|
|
20947
|
+
class ServePidFileError extends Error {
|
|
20948
|
+
reason;
|
|
20949
|
+
pidFile;
|
|
20950
|
+
constructor(reason, pidFile, options) {
|
|
20951
|
+
super(reason === "home-unresolved" ? "Cannot resolve the serve PID file: the home directory is empty or relative" : `Cannot write the serve PID file: ${pidFile}`, options);
|
|
20952
|
+
this.name = "ServePidFileError";
|
|
20953
|
+
this.reason = reason;
|
|
20954
|
+
this.pidFile = pidFile;
|
|
20955
|
+
}
|
|
20956
|
+
}
|
|
20957
|
+
function resolveServePidFile(sources) {
|
|
20958
|
+
const home = resolveHomeDir(sources);
|
|
20959
|
+
if (!isAbsolute4(home)) {
|
|
20960
|
+
return null;
|
|
20961
|
+
}
|
|
20962
|
+
return join16(home, PID_FILE_NAME);
|
|
20963
|
+
}
|
|
20871
20964
|
function findServeBuildDir(projectRoot, options) {
|
|
20872
|
-
const localBuild =
|
|
20873
|
-
if (existsSync3(
|
|
20965
|
+
const localBuild = join16(projectRoot, "packages", "serve", "build");
|
|
20966
|
+
if (existsSync3(join16(localBuild, "index.js")))
|
|
20874
20967
|
return localBuild;
|
|
20875
20968
|
if (options?.skipNpmFallback !== true) {
|
|
20876
|
-
const npmBuild =
|
|
20877
|
-
if (existsSync3(
|
|
20969
|
+
const npmBuild = join16(import.meta.dirname, "..", "..", "packages", "serve", "build");
|
|
20970
|
+
if (existsSync3(join16(npmBuild, "index.js")))
|
|
20878
20971
|
return npmBuild;
|
|
20879
20972
|
}
|
|
20880
20973
|
return null;
|
|
20881
20974
|
}
|
|
20882
20975
|
async function isServeRunning() {
|
|
20976
|
+
const pidFile = resolveServePidFile();
|
|
20977
|
+
if (pidFile === null) {
|
|
20978
|
+
return false;
|
|
20979
|
+
}
|
|
20980
|
+
return isServeRunningAt(pidFile);
|
|
20981
|
+
}
|
|
20982
|
+
async function isServeRunningAt(pidFile) {
|
|
20883
20983
|
try {
|
|
20884
|
-
const raw = await readFile3(
|
|
20984
|
+
const raw = await readFile3(pidFile, "utf-8");
|
|
20885
20985
|
const pid = Number(raw.trim());
|
|
20886
20986
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
20887
|
-
await cleanupPidFile();
|
|
20987
|
+
await cleanupPidFile(pidFile);
|
|
20888
20988
|
return false;
|
|
20889
20989
|
}
|
|
20890
20990
|
process.kill(pid, 0);
|
|
20891
20991
|
return true;
|
|
20892
20992
|
} catch {
|
|
20893
|
-
await cleanupPidFile();
|
|
20993
|
+
await cleanupPidFile(pidFile);
|
|
20894
20994
|
return false;
|
|
20895
20995
|
}
|
|
20896
20996
|
}
|
|
20897
20997
|
async function startServeBackground(projectRoot, port = DEFAULT_PORT, buildDirOpts) {
|
|
20898
|
-
|
|
20998
|
+
const pidFile = resolveServePidFile();
|
|
20999
|
+
if (pidFile === null) {
|
|
21000
|
+
throw new ServePidFileError("home-unresolved", null);
|
|
21001
|
+
}
|
|
21002
|
+
if (await isServeRunningAt(pidFile)) {
|
|
20899
21003
|
return;
|
|
20900
21004
|
}
|
|
20901
21005
|
const buildDir = findServeBuildDir(projectRoot, buildDirOpts);
|
|
20902
21006
|
if (buildDir === null) {
|
|
20903
21007
|
return;
|
|
20904
21008
|
}
|
|
20905
|
-
|
|
21009
|
+
try {
|
|
21010
|
+
await access(dirname7(pidFile), constants3.W_OK);
|
|
21011
|
+
} catch (error) {
|
|
21012
|
+
throw new ServePidFileError("pid-not-writable", pidFile, { cause: error });
|
|
21013
|
+
}
|
|
21014
|
+
const child = spawn("node", [join16(buildDir, "index.js")], {
|
|
20906
21015
|
env: {
|
|
20907
21016
|
...process.env,
|
|
20908
21017
|
OMCUSTOM_PORT: String(port),
|
|
@@ -20913,30 +21022,41 @@ async function startServeBackground(projectRoot, port = DEFAULT_PORT, buildDirOp
|
|
|
20913
21022
|
stdio: "ignore",
|
|
20914
21023
|
detached: true
|
|
20915
21024
|
});
|
|
21025
|
+
child.on("error", () => {});
|
|
20916
21026
|
child.unref();
|
|
20917
|
-
if (child.pid
|
|
20918
|
-
|
|
21027
|
+
if (child.pid === undefined) {
|
|
21028
|
+
return;
|
|
21029
|
+
}
|
|
21030
|
+
try {
|
|
21031
|
+
await writeFile3(pidFile, String(child.pid), "utf-8");
|
|
21032
|
+
} catch (error) {
|
|
21033
|
+
child.kill("SIGTERM");
|
|
21034
|
+
throw new ServePidFileError("pid-not-writable", pidFile, { cause: error });
|
|
20919
21035
|
}
|
|
20920
21036
|
}
|
|
20921
21037
|
async function stopServe() {
|
|
21038
|
+
const pidFile = resolveServePidFile();
|
|
21039
|
+
if (pidFile === null) {
|
|
21040
|
+
return false;
|
|
21041
|
+
}
|
|
20922
21042
|
try {
|
|
20923
|
-
const raw = await readFile3(
|
|
21043
|
+
const raw = await readFile3(pidFile, "utf-8");
|
|
20924
21044
|
const pid = Number(raw.trim());
|
|
20925
21045
|
if (!Number.isFinite(pid) || pid <= 0) {
|
|
20926
|
-
await cleanupPidFile();
|
|
21046
|
+
await cleanupPidFile(pidFile);
|
|
20927
21047
|
return false;
|
|
20928
21048
|
}
|
|
20929
21049
|
process.kill(pid, "SIGTERM");
|
|
20930
|
-
await cleanupPidFile();
|
|
21050
|
+
await cleanupPidFile(pidFile);
|
|
20931
21051
|
return true;
|
|
20932
21052
|
} catch {
|
|
20933
|
-
await cleanupPidFile();
|
|
21053
|
+
await cleanupPidFile(pidFile);
|
|
20934
21054
|
return false;
|
|
20935
21055
|
}
|
|
20936
21056
|
}
|
|
20937
|
-
async function cleanupPidFile() {
|
|
21057
|
+
async function cleanupPidFile(pidFile) {
|
|
20938
21058
|
try {
|
|
20939
|
-
await unlink(
|
|
21059
|
+
await unlink(pidFile);
|
|
20940
21060
|
} catch {}
|
|
20941
21061
|
}
|
|
20942
21062
|
|
|
@@ -20955,7 +21075,15 @@ async function serveCommand(options) {
|
|
|
20955
21075
|
runForeground(cwd, port, buildDirOpts);
|
|
20956
21076
|
return;
|
|
20957
21077
|
}
|
|
20958
|
-
|
|
21078
|
+
try {
|
|
21079
|
+
await startServeBackground(cwd, port, buildDirOpts);
|
|
21080
|
+
} catch (error) {
|
|
21081
|
+
if (!(error instanceof ServePidFileError)) {
|
|
21082
|
+
throw error;
|
|
21083
|
+
}
|
|
21084
|
+
console.error(describePidFileError(error));
|
|
21085
|
+
process.exit(1);
|
|
21086
|
+
}
|
|
20959
21087
|
const running = await isServeRunning();
|
|
20960
21088
|
if (running) {
|
|
20961
21089
|
console.log(i18n.t("cli.web.start.started", { port }));
|
|
@@ -20964,6 +21092,13 @@ async function serveCommand(options) {
|
|
|
20964
21092
|
process.exit(1);
|
|
20965
21093
|
}
|
|
20966
21094
|
}
|
|
21095
|
+
function describePidFileError(error) {
|
|
21096
|
+
if (error.reason === "home-unresolved") {
|
|
21097
|
+
return i18n.t("cli.web.start.homeUnresolved");
|
|
21098
|
+
}
|
|
21099
|
+
const cause = error.cause instanceof Error ? error.cause.message : String(error.cause);
|
|
21100
|
+
return i18n.t("cli.web.start.pidNotWritable", { path: String(error.pidFile), error: cause });
|
|
21101
|
+
}
|
|
20967
21102
|
async function serveStopCommand() {
|
|
20968
21103
|
const stopped = await stopServe();
|
|
20969
21104
|
if (stopped) {
|
|
@@ -20979,7 +21114,7 @@ function runForeground(projectRoot, port, buildDirOpts) {
|
|
|
20979
21114
|
process.exit(1);
|
|
20980
21115
|
}
|
|
20981
21116
|
console.log(`Web UI: http://localhost:${port}`);
|
|
20982
|
-
spawnSync2("node", [
|
|
21117
|
+
spawnSync2("node", [join17(buildDir, "index.js")], {
|
|
20983
21118
|
env: {
|
|
20984
21119
|
...process.env,
|
|
20985
21120
|
OMCUSTOM_PORT: String(port),
|
|
@@ -20998,12 +21133,12 @@ import { resolve as resolve4 } from "node:path";
|
|
|
20998
21133
|
init_fs();
|
|
20999
21134
|
import { existsSync as existsSync4 } from "node:fs";
|
|
21000
21135
|
import { cp as cp2, mkdir as mkdir2 } from "node:fs/promises";
|
|
21001
|
-
import { join as
|
|
21136
|
+
import { join as join18 } from "node:path";
|
|
21002
21137
|
async function loadVersions() {
|
|
21003
21138
|
try {
|
|
21004
21139
|
const packageRoot = getPackageRoot();
|
|
21005
|
-
const manifest = await readJsonFile(
|
|
21006
|
-
const pkg = await readJsonFile(
|
|
21140
|
+
const manifest = await readJsonFile(join18(packageRoot, "templates", "manifest.json"));
|
|
21141
|
+
const pkg = await readJsonFile(join18(packageRoot, "package.json"));
|
|
21007
21142
|
return { generatorVersion: pkg.version, templateVersion: manifest.version };
|
|
21008
21143
|
} catch {
|
|
21009
21144
|
return { generatorVersion: "0.0.0", templateVersion: "0.0.0" };
|
|
@@ -21073,7 +21208,7 @@ async function countFiles(dir) {
|
|
|
21073
21208
|
return 0;
|
|
21074
21209
|
}
|
|
21075
21210
|
for (const entry of entries) {
|
|
21076
|
-
const full =
|
|
21211
|
+
const full = join18(current, entry);
|
|
21077
21212
|
try {
|
|
21078
21213
|
const s = await stat(full);
|
|
21079
21214
|
if (s.isDirectory()) {
|
|
@@ -21088,19 +21223,19 @@ async function countFiles(dir) {
|
|
|
21088
21223
|
return walk(dir);
|
|
21089
21224
|
}
|
|
21090
21225
|
async function exportSnapshot(targetDir, outputPath) {
|
|
21091
|
-
const claudeDir =
|
|
21092
|
-
const guidesDir =
|
|
21226
|
+
const claudeDir = join18(targetDir, ".claude");
|
|
21227
|
+
const guidesDir = join18(targetDir, "guides");
|
|
21093
21228
|
if (!existsSync4(claudeDir)) {
|
|
21094
21229
|
return { success: false, exportPath: outputPath, fileCount: 0 };
|
|
21095
21230
|
}
|
|
21096
21231
|
await mkdir2(outputPath, { recursive: true });
|
|
21097
|
-
const destClaude =
|
|
21232
|
+
const destClaude = join18(outputPath, ".claude");
|
|
21098
21233
|
await cp2(claudeDir, destClaude, {
|
|
21099
21234
|
recursive: true,
|
|
21100
21235
|
filter: isExportable
|
|
21101
21236
|
});
|
|
21102
21237
|
if (existsSync4(guidesDir)) {
|
|
21103
|
-
await cp2(guidesDir,
|
|
21238
|
+
await cp2(guidesDir, join18(outputPath, "guides"), { recursive: true });
|
|
21104
21239
|
}
|
|
21105
21240
|
const lockfile = await generateCurrentLockfile(targetDir);
|
|
21106
21241
|
if (lockfile) {
|
|
@@ -21184,7 +21319,7 @@ import { constants as osConstants } from "node:os";
|
|
|
21184
21319
|
|
|
21185
21320
|
// src/core/updater.ts
|
|
21186
21321
|
init_package();
|
|
21187
|
-
import { join as
|
|
21322
|
+
import { join as join19 } from "node:path";
|
|
21188
21323
|
init_fs();
|
|
21189
21324
|
|
|
21190
21325
|
// src/core/entry-merger.ts
|
|
@@ -21439,7 +21574,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
|
|
|
21439
21574
|
}
|
|
21440
21575
|
async function updateEntryDoc(targetDir, config, options) {
|
|
21441
21576
|
const layout = getProviderLayout();
|
|
21442
|
-
const entryPath =
|
|
21577
|
+
const entryPath = join19(targetDir, layout.entryFile);
|
|
21443
21578
|
const templateName = getEntryTemplateName2(config.language);
|
|
21444
21579
|
const templatePath = resolveTemplatePath(templateName);
|
|
21445
21580
|
if (!await fileExists(templatePath)) {
|
|
@@ -21477,7 +21612,7 @@ async function backfillStatusLineRefreshInterval(targetDir, options) {
|
|
|
21477
21612
|
return;
|
|
21478
21613
|
}
|
|
21479
21614
|
const layout = getProviderLayout();
|
|
21480
|
-
const settingsPath =
|
|
21615
|
+
const settingsPath = join19(targetDir, layout.rootDir, "settings.local.json");
|
|
21481
21616
|
if (!await fileExists(settingsPath)) {
|
|
21482
21617
|
return;
|
|
21483
21618
|
}
|
|
@@ -21503,7 +21638,7 @@ async function migrateHookCommandsInSettingsLocal(targetDir, options) {
|
|
|
21503
21638
|
return;
|
|
21504
21639
|
}
|
|
21505
21640
|
const layout = getProviderLayout();
|
|
21506
|
-
const settingsPath =
|
|
21641
|
+
const settingsPath = join19(targetDir, layout.rootDir, "settings.local.json");
|
|
21507
21642
|
if (!await fileExists(settingsPath)) {
|
|
21508
21643
|
return;
|
|
21509
21644
|
}
|
|
@@ -21598,7 +21733,7 @@ async function regenerateLockfile(targetDir, result) {
|
|
|
21598
21733
|
}
|
|
21599
21734
|
}
|
|
21600
21735
|
async function shouldSkipSelfUpdate2(targetDir, result) {
|
|
21601
|
-
const targetPkgPath =
|
|
21736
|
+
const targetPkgPath = join19(targetDir, "package.json");
|
|
21602
21737
|
if (await fileExists(targetPkgPath)) {
|
|
21603
21738
|
const targetPkg = await readJsonFile(targetPkgPath);
|
|
21604
21739
|
if (targetPkg.name === "oh-my-customcode") {
|
|
@@ -21737,11 +21872,11 @@ async function collectProtectedSkipPaths(srcPath, destPath, componentPath, force
|
|
|
21737
21872
|
const warnedPaths = [];
|
|
21738
21873
|
const updatedPaths = [];
|
|
21739
21874
|
for (const p of protectedRelative) {
|
|
21740
|
-
const targetFilePath =
|
|
21875
|
+
const targetFilePath = join19(targetDir, componentPath, p);
|
|
21741
21876
|
const lockfileKey = `${componentPath}/${p}`.replace(/\\/g, "/");
|
|
21742
21877
|
const shouldSkip = await shouldSkipProtectedFile(targetFilePath, lockfileKey, lockfile);
|
|
21743
21878
|
if (shouldSkip) {
|
|
21744
|
-
skipPaths.push(path.relative(destPath,
|
|
21879
|
+
skipPaths.push(path.relative(destPath, join19(destPath, p)));
|
|
21745
21880
|
warnedPaths.push(p);
|
|
21746
21881
|
} else {
|
|
21747
21882
|
updatedPaths.push(p);
|
|
@@ -21787,7 +21922,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
21787
21922
|
const preservedFiles = [];
|
|
21788
21923
|
const componentPath = getComponentPath2(component);
|
|
21789
21924
|
const srcPath = resolveTemplatePath(componentPath);
|
|
21790
|
-
const destPath =
|
|
21925
|
+
const destPath = join19(targetDir, componentPath);
|
|
21791
21926
|
const customComponents = config.customComponents || [];
|
|
21792
21927
|
const skipPaths = [];
|
|
21793
21928
|
if (customizations && !options.forceOverwriteAll) {
|
|
@@ -21829,7 +21964,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
21829
21964
|
}
|
|
21830
21965
|
skipPaths.push(...protectedSkipPaths);
|
|
21831
21966
|
const path = await import("node:path");
|
|
21832
|
-
const normalizedSkipPaths = skipPaths.map((p) => path.relative(destPath,
|
|
21967
|
+
const normalizedSkipPaths = skipPaths.map((p) => path.relative(destPath, join19(targetDir, p)));
|
|
21833
21968
|
const uniqueSkipPaths = [...new Set(normalizedSkipPaths)];
|
|
21834
21969
|
await copyDirectory(srcPath, destPath, {
|
|
21835
21970
|
overwrite: true,
|
|
@@ -21858,7 +21993,7 @@ async function syncRootLevelFiles(targetDir, options, customizations) {
|
|
|
21858
21993
|
const preserved = [];
|
|
21859
21994
|
const preserveSet = new Set(customizations && !options.forceOverwriteAll ? customizations.preserveFiles : []);
|
|
21860
21995
|
for (const fileName of ROOT_LEVEL_FILES) {
|
|
21861
|
-
const srcPath = resolveTemplatePath(
|
|
21996
|
+
const srcPath = resolveTemplatePath(join19(layout.rootDir, fileName));
|
|
21862
21997
|
if (!await fileExists(srcPath)) {
|
|
21863
21998
|
continue;
|
|
21864
21999
|
}
|
|
@@ -21867,8 +22002,8 @@ async function syncRootLevelFiles(targetDir, options, customizations) {
|
|
|
21867
22002
|
preserved.push(relPath);
|
|
21868
22003
|
continue;
|
|
21869
22004
|
}
|
|
21870
|
-
const destPath =
|
|
21871
|
-
await ensureDirectory(
|
|
22005
|
+
const destPath = join19(targetDir, layout.rootDir, fileName);
|
|
22006
|
+
await ensureDirectory(join19(destPath, ".."));
|
|
21872
22007
|
await fs.copyFile(srcPath, destPath);
|
|
21873
22008
|
if (fileName.endsWith(".sh")) {
|
|
21874
22009
|
await fs.chmod(destPath, 493);
|
|
@@ -21903,7 +22038,7 @@ async function removeDeprecatedFiles(targetDir, options) {
|
|
|
21903
22038
|
});
|
|
21904
22039
|
continue;
|
|
21905
22040
|
}
|
|
21906
|
-
const fullPath =
|
|
22041
|
+
const fullPath = join19(targetDir, entry.path);
|
|
21907
22042
|
if (await fileExists(fullPath)) {
|
|
21908
22043
|
await fs.unlink(fullPath);
|
|
21909
22044
|
removed.push(entry.path);
|
|
@@ -21944,7 +22079,7 @@ async function syncNamespaceInFile(targetFilePath, upstreamFilePath) {
|
|
|
21944
22079
|
async function processNamespaceSyncEntry(entry, relPath, fullSrcPath, destPath, componentPath, lockfile) {
|
|
21945
22080
|
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
21946
22081
|
return null;
|
|
21947
|
-
const targetFilePath =
|
|
22082
|
+
const targetFilePath = join19(destPath, relPath);
|
|
21948
22083
|
const lockfileKey = `${componentPath}/${relPath}`.replace(/\\/g, "/");
|
|
21949
22084
|
const shouldSkip = await shouldSkipProtectedFile(targetFilePath, lockfileKey, lockfile);
|
|
21950
22085
|
if (shouldSkip)
|
|
@@ -21959,7 +22094,7 @@ async function applyNamespaceSync(targetDir, component, lockfile) {
|
|
|
21959
22094
|
return [];
|
|
21960
22095
|
const componentPath = getComponentPath2(component);
|
|
21961
22096
|
const srcPath = resolveTemplatePath(componentPath);
|
|
21962
|
-
const destPath =
|
|
22097
|
+
const destPath = join19(targetDir, componentPath);
|
|
21963
22098
|
const fs = await import("node:fs/promises");
|
|
21964
22099
|
const synced = [];
|
|
21965
22100
|
const queue = [{ dir: srcPath, relDir: "" }];
|
|
@@ -21973,7 +22108,7 @@ async function applyNamespaceSync(targetDir, component, lockfile) {
|
|
|
21973
22108
|
}
|
|
21974
22109
|
for (const entry of entries) {
|
|
21975
22110
|
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
21976
|
-
const fullSrcPath =
|
|
22111
|
+
const fullSrcPath = join19(dir, entry.name);
|
|
21977
22112
|
if (entry.isDirectory()) {
|
|
21978
22113
|
queue.push({ dir: fullSrcPath, relDir: relPath });
|
|
21979
22114
|
continue;
|
|
@@ -21996,26 +22131,26 @@ function getComponentPath2(component) {
|
|
|
21996
22131
|
}
|
|
21997
22132
|
async function backupInstallation(targetDir) {
|
|
21998
22133
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
21999
|
-
const backupDir =
|
|
22134
|
+
const backupDir = join19(targetDir, `.omcustom-backup-${timestamp}`);
|
|
22000
22135
|
const fs = await import("node:fs/promises");
|
|
22001
22136
|
await ensureDirectory(backupDir);
|
|
22002
22137
|
const layout = getProviderLayout();
|
|
22003
22138
|
const dirsToBackup = [layout.rootDir, "guides"];
|
|
22004
22139
|
for (const dir of dirsToBackup) {
|
|
22005
|
-
const srcPath =
|
|
22140
|
+
const srcPath = join19(targetDir, dir);
|
|
22006
22141
|
if (await fileExists(srcPath)) {
|
|
22007
|
-
const destPath =
|
|
22142
|
+
const destPath = join19(backupDir, dir);
|
|
22008
22143
|
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
22009
22144
|
}
|
|
22010
22145
|
}
|
|
22011
|
-
const entryPath =
|
|
22146
|
+
const entryPath = join19(targetDir, layout.entryFile);
|
|
22012
22147
|
if (await fileExists(entryPath)) {
|
|
22013
|
-
await fs.copyFile(entryPath,
|
|
22148
|
+
await fs.copyFile(entryPath, join19(backupDir, layout.entryFile));
|
|
22014
22149
|
}
|
|
22015
22150
|
return backupDir;
|
|
22016
22151
|
}
|
|
22017
22152
|
async function loadCustomizationManifest(targetDir) {
|
|
22018
|
-
const manifestPath =
|
|
22153
|
+
const manifestPath = join19(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
22019
22154
|
if (await fileExists(manifestPath)) {
|
|
22020
22155
|
return readJsonFile(manifestPath);
|
|
22021
22156
|
}
|