oh-my-customcode 0.42.3 → 0.43.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js
CHANGED
|
@@ -9376,7 +9376,9 @@ var require_src = __commonJS((exports, module) => {
|
|
|
9376
9376
|
});
|
|
9377
9377
|
|
|
9378
9378
|
// src/cli/index.ts
|
|
9379
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
9379
9380
|
import { createRequire as createRequire2 } from "node:module";
|
|
9381
|
+
import { join as join14 } from "node:path";
|
|
9380
9382
|
|
|
9381
9383
|
// node_modules/.bun/commander@14.0.2/node_modules/commander/esm.mjs
|
|
9382
9384
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -14207,7 +14209,7 @@ async function doctorCommand(options = {}) {
|
|
|
14207
14209
|
}
|
|
14208
14210
|
|
|
14209
14211
|
// src/cli/init.ts
|
|
14210
|
-
import { join as
|
|
14212
|
+
import { join as join10 } from "node:path";
|
|
14211
14213
|
|
|
14212
14214
|
// src/core/installer.ts
|
|
14213
14215
|
init_fs();
|
|
@@ -15063,6 +15065,83 @@ async function checkUvAvailable() {
|
|
|
15063
15065
|
// src/cli/init.ts
|
|
15064
15066
|
init_fs();
|
|
15065
15067
|
|
|
15068
|
+
// src/cli/serve.ts
|
|
15069
|
+
import { spawn } from "node:child_process";
|
|
15070
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
15071
|
+
import { readFile as readFile2, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
15072
|
+
import { join as join9 } from "node:path";
|
|
15073
|
+
var DEFAULT_PORT = 4321;
|
|
15074
|
+
var PID_FILE = join9(process.env.HOME ?? "~", ".omcustom-serve.pid");
|
|
15075
|
+
function findServeBuildDir(projectRoot) {
|
|
15076
|
+
const localBuild = join9(projectRoot, "packages", "serve", "build");
|
|
15077
|
+
if (existsSync2(join9(localBuild, "index.js")))
|
|
15078
|
+
return localBuild;
|
|
15079
|
+
const npmBuild = join9(import.meta.dirname, "..", "..", "packages", "serve", "build");
|
|
15080
|
+
if (existsSync2(join9(npmBuild, "index.js")))
|
|
15081
|
+
return npmBuild;
|
|
15082
|
+
return null;
|
|
15083
|
+
}
|
|
15084
|
+
async function isServeRunning() {
|
|
15085
|
+
try {
|
|
15086
|
+
const raw = await readFile2(PID_FILE, "utf-8");
|
|
15087
|
+
const pid = Number(raw.trim());
|
|
15088
|
+
if (!Number.isFinite(pid) || pid <= 0) {
|
|
15089
|
+
await cleanupPidFile();
|
|
15090
|
+
return false;
|
|
15091
|
+
}
|
|
15092
|
+
process.kill(pid, 0);
|
|
15093
|
+
return true;
|
|
15094
|
+
} catch {
|
|
15095
|
+
await cleanupPidFile();
|
|
15096
|
+
return false;
|
|
15097
|
+
}
|
|
15098
|
+
}
|
|
15099
|
+
async function startServeBackground(projectRoot, port = DEFAULT_PORT) {
|
|
15100
|
+
if (await isServeRunning()) {
|
|
15101
|
+
return;
|
|
15102
|
+
}
|
|
15103
|
+
const buildDir = findServeBuildDir(projectRoot);
|
|
15104
|
+
if (buildDir === null) {
|
|
15105
|
+
return;
|
|
15106
|
+
}
|
|
15107
|
+
const child = spawn("node", [join9(buildDir, "index.js")], {
|
|
15108
|
+
env: {
|
|
15109
|
+
...process.env,
|
|
15110
|
+
OMCUSTOM_PORT: String(port),
|
|
15111
|
+
OMCUSTOM_HOST: "localhost",
|
|
15112
|
+
OMCUSTOM_ORIGIN: `http://localhost:${port}`,
|
|
15113
|
+
OMCUSTOM_PROJECT_ROOT: projectRoot
|
|
15114
|
+
},
|
|
15115
|
+
stdio: "ignore",
|
|
15116
|
+
detached: true
|
|
15117
|
+
});
|
|
15118
|
+
child.unref();
|
|
15119
|
+
if (child.pid !== undefined) {
|
|
15120
|
+
await writeFile2(PID_FILE, String(child.pid), "utf-8");
|
|
15121
|
+
}
|
|
15122
|
+
}
|
|
15123
|
+
async function stopServe() {
|
|
15124
|
+
try {
|
|
15125
|
+
const raw = await readFile2(PID_FILE, "utf-8");
|
|
15126
|
+
const pid = Number(raw.trim());
|
|
15127
|
+
if (!Number.isFinite(pid) || pid <= 0) {
|
|
15128
|
+
await cleanupPidFile();
|
|
15129
|
+
return false;
|
|
15130
|
+
}
|
|
15131
|
+
process.kill(pid, "SIGTERM");
|
|
15132
|
+
await cleanupPidFile();
|
|
15133
|
+
return true;
|
|
15134
|
+
} catch {
|
|
15135
|
+
await cleanupPidFile();
|
|
15136
|
+
return false;
|
|
15137
|
+
}
|
|
15138
|
+
}
|
|
15139
|
+
async function cleanupPidFile() {
|
|
15140
|
+
try {
|
|
15141
|
+
await unlink(PID_FILE);
|
|
15142
|
+
} catch {}
|
|
15143
|
+
}
|
|
15144
|
+
|
|
15066
15145
|
// node_modules/.bun/@clack+core@1.1.0/node_modules/@clack/core/dist/index.mjs
|
|
15067
15146
|
import { styleText as D } from "node:util";
|
|
15068
15147
|
import { stdout as R, stdin as q } from "node:process";
|
|
@@ -16040,7 +16119,7 @@ async function runInitWizard(options) {
|
|
|
16040
16119
|
// src/cli/init.ts
|
|
16041
16120
|
async function checkExistingInstallation(targetDir) {
|
|
16042
16121
|
const layout = getProviderLayout();
|
|
16043
|
-
const rootDir =
|
|
16122
|
+
const rootDir = join10(targetDir, layout.rootDir);
|
|
16044
16123
|
return fileExists(rootDir);
|
|
16045
16124
|
}
|
|
16046
16125
|
var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
@@ -16054,13 +16133,13 @@ var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
|
16054
16133
|
function componentToPath(targetDir, component) {
|
|
16055
16134
|
if (component === "entry-md") {
|
|
16056
16135
|
const layout = getProviderLayout();
|
|
16057
|
-
return
|
|
16136
|
+
return join10(targetDir, layout.entryFile);
|
|
16058
16137
|
}
|
|
16059
16138
|
if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
|
|
16060
16139
|
const layout = getProviderLayout();
|
|
16061
|
-
return
|
|
16140
|
+
return join10(targetDir, layout.rootDir, component);
|
|
16062
16141
|
}
|
|
16063
|
-
return
|
|
16142
|
+
return join10(targetDir, component);
|
|
16064
16143
|
}
|
|
16065
16144
|
function buildInstalledPaths(targetDir, components) {
|
|
16066
16145
|
return components.map((component) => componentToPath(targetDir, component));
|
|
@@ -16162,6 +16241,8 @@ async function initCommand(options) {
|
|
|
16162
16241
|
console.log(" /plugin install context7");
|
|
16163
16242
|
console.log("");
|
|
16164
16243
|
console.log('See CLAUDE.md "외부 의존성" section for details.');
|
|
16244
|
+
await startServeBackground(targetDir).catch(() => {});
|
|
16245
|
+
console.log(`Web UI: http://127.0.0.1:${DEFAULT_PORT}`);
|
|
16165
16246
|
return {
|
|
16166
16247
|
success: true,
|
|
16167
16248
|
message: i18n.t("cli.init.success"),
|
|
@@ -16175,7 +16256,7 @@ async function initCommand(options) {
|
|
|
16175
16256
|
}
|
|
16176
16257
|
|
|
16177
16258
|
// src/cli/list.ts
|
|
16178
|
-
import { basename as basename3, dirname as dirname3, join as
|
|
16259
|
+
import { basename as basename3, dirname as dirname3, join as join11, relative as relative3 } from "node:path";
|
|
16179
16260
|
init_fs();
|
|
16180
16261
|
var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
|
|
16181
16262
|
function parseKeyValue(line) {
|
|
@@ -16240,12 +16321,12 @@ function extractAgentTypeFromFilename(filename) {
|
|
|
16240
16321
|
return prefixMap[prefix] || "unknown";
|
|
16241
16322
|
}
|
|
16242
16323
|
function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
|
|
16243
|
-
const relativePath = relative3(
|
|
16324
|
+
const relativePath = relative3(join11(baseDir, rootDir, "skills"), skillPath);
|
|
16244
16325
|
const parts = relativePath.split("/").filter(Boolean);
|
|
16245
16326
|
return parts[0] || "unknown";
|
|
16246
16327
|
}
|
|
16247
16328
|
function extractGuideCategoryFromPath(guidePath, baseDir) {
|
|
16248
|
-
const relativePath = relative3(
|
|
16329
|
+
const relativePath = relative3(join11(baseDir, "guides"), guidePath);
|
|
16249
16330
|
const parts = relativePath.split("/").filter(Boolean);
|
|
16250
16331
|
return parts[0] || "unknown";
|
|
16251
16332
|
}
|
|
@@ -16339,7 +16420,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
|
|
|
16339
16420
|
}
|
|
16340
16421
|
}
|
|
16341
16422
|
async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
16342
|
-
const agentsDir =
|
|
16423
|
+
const agentsDir = join11(targetDir, rootDir, "agents");
|
|
16343
16424
|
if (!await fileExists(agentsDir))
|
|
16344
16425
|
return [];
|
|
16345
16426
|
try {
|
|
@@ -16367,7 +16448,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
16367
16448
|
}
|
|
16368
16449
|
}
|
|
16369
16450
|
async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
16370
|
-
const skillsDir =
|
|
16451
|
+
const skillsDir = join11(targetDir, rootDir, "skills");
|
|
16371
16452
|
if (!await fileExists(skillsDir))
|
|
16372
16453
|
return [];
|
|
16373
16454
|
try {
|
|
@@ -16377,7 +16458,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
16377
16458
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
16378
16459
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
16379
16460
|
const skillDir = dirname3(skillMdPath);
|
|
16380
|
-
const indexYamlPath =
|
|
16461
|
+
const indexYamlPath = join11(skillDir, "index.yaml");
|
|
16381
16462
|
const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
|
|
16382
16463
|
const relativePath = relative3(targetDir, skillDir);
|
|
16383
16464
|
return {
|
|
@@ -16396,7 +16477,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
16396
16477
|
}
|
|
16397
16478
|
}
|
|
16398
16479
|
async function getGuides(targetDir, config) {
|
|
16399
|
-
const guidesDir =
|
|
16480
|
+
const guidesDir = join11(targetDir, "guides");
|
|
16400
16481
|
if (!await fileExists(guidesDir))
|
|
16401
16482
|
return [];
|
|
16402
16483
|
try {
|
|
@@ -16423,7 +16504,7 @@ async function getGuides(targetDir, config) {
|
|
|
16423
16504
|
}
|
|
16424
16505
|
var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
|
|
16425
16506
|
async function getRules(targetDir, rootDir = ".claude", config) {
|
|
16426
|
-
const rulesDir =
|
|
16507
|
+
const rulesDir = join11(targetDir, rootDir, "rules");
|
|
16427
16508
|
if (!await fileExists(rulesDir))
|
|
16428
16509
|
return [];
|
|
16429
16510
|
try {
|
|
@@ -16495,7 +16576,7 @@ function formatAsJson(components) {
|
|
|
16495
16576
|
console.log(JSON.stringify(components, null, 2));
|
|
16496
16577
|
}
|
|
16497
16578
|
async function getHooks(targetDir, rootDir = ".claude") {
|
|
16498
|
-
const hooksDir =
|
|
16579
|
+
const hooksDir = join11(targetDir, rootDir, "hooks");
|
|
16499
16580
|
if (!await fileExists(hooksDir))
|
|
16500
16581
|
return [];
|
|
16501
16582
|
try {
|
|
@@ -16513,7 +16594,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
16513
16594
|
}
|
|
16514
16595
|
}
|
|
16515
16596
|
async function getContexts(targetDir, rootDir = ".claude") {
|
|
16516
|
-
const contextsDir =
|
|
16597
|
+
const contextsDir = join11(targetDir, rootDir, "contexts");
|
|
16517
16598
|
if (!await fileExists(contextsDir))
|
|
16518
16599
|
return [];
|
|
16519
16600
|
try {
|
|
@@ -16901,9 +16982,73 @@ async function securityCommand(_options = {}) {
|
|
|
16901
16982
|
};
|
|
16902
16983
|
}
|
|
16903
16984
|
|
|
16985
|
+
// src/cli/serve-commands.ts
|
|
16986
|
+
import { execFile, spawnSync as spawnSync2 } from "node:child_process";
|
|
16987
|
+
import { join as join12 } from "node:path";
|
|
16988
|
+
async function serveCommand(options) {
|
|
16989
|
+
const port = options.port !== undefined ? Number(options.port) : DEFAULT_PORT;
|
|
16990
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
|
16991
|
+
console.error(`Invalid port: ${options.port}`);
|
|
16992
|
+
process.exit(1);
|
|
16993
|
+
}
|
|
16994
|
+
const cwd = process.cwd();
|
|
16995
|
+
if (options.foreground === true) {
|
|
16996
|
+
runForeground(cwd, port);
|
|
16997
|
+
return;
|
|
16998
|
+
}
|
|
16999
|
+
await startServeBackground(cwd, port);
|
|
17000
|
+
const running = await isServeRunning();
|
|
17001
|
+
if (running) {
|
|
17002
|
+
console.log(`Web UI started: http://127.0.0.1:${port}`);
|
|
17003
|
+
if (options.open === true) {
|
|
17004
|
+
openBrowser(port);
|
|
17005
|
+
}
|
|
17006
|
+
} else {
|
|
17007
|
+
console.error("Failed to start Web UI server");
|
|
17008
|
+
process.exit(1);
|
|
17009
|
+
}
|
|
17010
|
+
}
|
|
17011
|
+
async function serveStopCommand() {
|
|
17012
|
+
const stopped = await stopServe();
|
|
17013
|
+
if (stopped) {
|
|
17014
|
+
console.log("Web UI server stopped");
|
|
17015
|
+
} else {
|
|
17016
|
+
console.log("Web UI server is not running");
|
|
17017
|
+
}
|
|
17018
|
+
}
|
|
17019
|
+
function runForeground(projectRoot, port) {
|
|
17020
|
+
const buildDir = findServeBuildDir(projectRoot);
|
|
17021
|
+
if (buildDir === null) {
|
|
17022
|
+
console.error("Web UI build not found. Run: cd packages/serve && bun run build");
|
|
17023
|
+
process.exit(1);
|
|
17024
|
+
}
|
|
17025
|
+
console.log(`Web UI: http://127.0.0.1:${port}`);
|
|
17026
|
+
spawnSync2("node", [join12(buildDir, "index.js")], {
|
|
17027
|
+
env: {
|
|
17028
|
+
...process.env,
|
|
17029
|
+
OMCUSTOM_PORT: String(port),
|
|
17030
|
+
OMCUSTOM_HOST: "localhost",
|
|
17031
|
+
OMCUSTOM_ORIGIN: `http://localhost:${port}`,
|
|
17032
|
+
OMCUSTOM_PROJECT_ROOT: projectRoot
|
|
17033
|
+
},
|
|
17034
|
+
stdio: "inherit"
|
|
17035
|
+
});
|
|
17036
|
+
}
|
|
17037
|
+
function openBrowser(port) {
|
|
17038
|
+
const url = `http://127.0.0.1:${port}`;
|
|
17039
|
+
const platform = process.platform;
|
|
17040
|
+
if (platform === "darwin") {
|
|
17041
|
+
execFile("open", [url], () => {});
|
|
17042
|
+
} else if (platform === "win32") {
|
|
17043
|
+
execFile("cmd", ["/c", "start", url], () => {});
|
|
17044
|
+
} else {
|
|
17045
|
+
execFile("xdg-open", [url], () => {});
|
|
17046
|
+
}
|
|
17047
|
+
}
|
|
17048
|
+
|
|
16904
17049
|
// src/core/updater.ts
|
|
16905
17050
|
init_fs();
|
|
16906
|
-
import { join as
|
|
17051
|
+
import { join as join13 } from "node:path";
|
|
16907
17052
|
|
|
16908
17053
|
// src/core/entry-merger.ts
|
|
16909
17054
|
var MANAGED_START = "<!-- omcustom:start -->";
|
|
@@ -17152,7 +17297,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
|
|
|
17152
17297
|
}
|
|
17153
17298
|
async function updateEntryDoc(targetDir, config, options) {
|
|
17154
17299
|
const layout = getProviderLayout();
|
|
17155
|
-
const entryPath =
|
|
17300
|
+
const entryPath = join13(targetDir, layout.entryFile);
|
|
17156
17301
|
const templateName = getEntryTemplateName2(config.language);
|
|
17157
17302
|
const templatePath = resolveTemplatePath(templateName);
|
|
17158
17303
|
if (!await fileExists(templatePath)) {
|
|
@@ -17298,7 +17443,7 @@ async function collectProtectedSkipPaths(srcPath, destPath, componentPath, force
|
|
|
17298
17443
|
}
|
|
17299
17444
|
const protectedRelative = await findProtectedFilesInDir(srcPath, componentPath);
|
|
17300
17445
|
const path3 = await import("node:path");
|
|
17301
|
-
const skipPaths = protectedRelative.map((p) => path3.relative(destPath,
|
|
17446
|
+
const skipPaths = protectedRelative.map((p) => path3.relative(destPath, join13(destPath, p)));
|
|
17302
17447
|
return { skipPaths, warnedPaths: protectedRelative };
|
|
17303
17448
|
}
|
|
17304
17449
|
function isEntryProtected(relPath, componentRelativePrefix) {
|
|
@@ -17339,7 +17484,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
17339
17484
|
const preservedFiles = [];
|
|
17340
17485
|
const componentPath = getComponentPath2(component);
|
|
17341
17486
|
const srcPath = resolveTemplatePath(componentPath);
|
|
17342
|
-
const destPath =
|
|
17487
|
+
const destPath = join13(targetDir, componentPath);
|
|
17343
17488
|
const customComponents = config.customComponents || [];
|
|
17344
17489
|
const skipPaths = [];
|
|
17345
17490
|
if (customizations && !options.forceOverwriteAll) {
|
|
@@ -17370,7 +17515,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
17370
17515
|
}
|
|
17371
17516
|
skipPaths.push(...protectedSkipPaths);
|
|
17372
17517
|
const path3 = await import("node:path");
|
|
17373
|
-
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath,
|
|
17518
|
+
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join13(targetDir, p)));
|
|
17374
17519
|
const uniqueSkipPaths = [...new Set(normalizedSkipPaths)];
|
|
17375
17520
|
await copyDirectory(srcPath, destPath, {
|
|
17376
17521
|
overwrite: true,
|
|
@@ -17392,12 +17537,12 @@ async function syncRootLevelFiles(targetDir, options) {
|
|
|
17392
17537
|
const layout = getProviderLayout();
|
|
17393
17538
|
const synced = [];
|
|
17394
17539
|
for (const fileName of ROOT_LEVEL_FILES) {
|
|
17395
|
-
const srcPath = resolveTemplatePath(
|
|
17540
|
+
const srcPath = resolveTemplatePath(join13(layout.rootDir, fileName));
|
|
17396
17541
|
if (!await fileExists(srcPath)) {
|
|
17397
17542
|
continue;
|
|
17398
17543
|
}
|
|
17399
|
-
const destPath =
|
|
17400
|
-
await ensureDirectory(
|
|
17544
|
+
const destPath = join13(targetDir, layout.rootDir, fileName);
|
|
17545
|
+
await ensureDirectory(join13(destPath, ".."));
|
|
17401
17546
|
await fs3.copyFile(srcPath, destPath);
|
|
17402
17547
|
if (fileName.endsWith(".sh")) {
|
|
17403
17548
|
await fs3.chmod(destPath, 493);
|
|
@@ -17432,7 +17577,7 @@ async function removeDeprecatedFiles(targetDir, options) {
|
|
|
17432
17577
|
});
|
|
17433
17578
|
continue;
|
|
17434
17579
|
}
|
|
17435
|
-
const fullPath =
|
|
17580
|
+
const fullPath = join13(targetDir, entry.path);
|
|
17436
17581
|
if (await fileExists(fullPath)) {
|
|
17437
17582
|
await fs3.unlink(fullPath);
|
|
17438
17583
|
removed.push(entry.path);
|
|
@@ -17456,26 +17601,26 @@ function getComponentPath2(component) {
|
|
|
17456
17601
|
}
|
|
17457
17602
|
async function backupInstallation(targetDir) {
|
|
17458
17603
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
17459
|
-
const backupDir =
|
|
17604
|
+
const backupDir = join13(targetDir, `.omcustom-backup-${timestamp}`);
|
|
17460
17605
|
const fs3 = await import("node:fs/promises");
|
|
17461
17606
|
await ensureDirectory(backupDir);
|
|
17462
17607
|
const layout = getProviderLayout();
|
|
17463
17608
|
const dirsToBackup = [layout.rootDir, "guides"];
|
|
17464
17609
|
for (const dir2 of dirsToBackup) {
|
|
17465
|
-
const srcPath =
|
|
17610
|
+
const srcPath = join13(targetDir, dir2);
|
|
17466
17611
|
if (await fileExists(srcPath)) {
|
|
17467
|
-
const destPath =
|
|
17612
|
+
const destPath = join13(backupDir, dir2);
|
|
17468
17613
|
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
17469
17614
|
}
|
|
17470
17615
|
}
|
|
17471
|
-
const entryPath =
|
|
17616
|
+
const entryPath = join13(targetDir, layout.entryFile);
|
|
17472
17617
|
if (await fileExists(entryPath)) {
|
|
17473
|
-
await fs3.copyFile(entryPath,
|
|
17618
|
+
await fs3.copyFile(entryPath, join13(backupDir, layout.entryFile));
|
|
17474
17619
|
}
|
|
17475
17620
|
return backupDir;
|
|
17476
17621
|
}
|
|
17477
17622
|
async function loadCustomizationManifest(targetDir) {
|
|
17478
|
-
const manifestPath =
|
|
17623
|
+
const manifestPath = join13(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
17479
17624
|
if (await fileExists(manifestPath)) {
|
|
17480
17625
|
return readJsonFile(manifestPath);
|
|
17481
17626
|
}
|
|
@@ -17585,6 +17730,12 @@ function createProgram() {
|
|
|
17585
17730
|
const result = await securityCommand(options);
|
|
17586
17731
|
process.exitCode = result.success ? 0 : 1;
|
|
17587
17732
|
});
|
|
17733
|
+
program2.command("serve").description("Start the web UI server").option("-p, --port <port>", "Port number", "4321").option("--open", "Open browser automatically").option("--foreground", "Run in foreground (not detached)").action(async (options) => {
|
|
17734
|
+
await serveCommand(options);
|
|
17735
|
+
});
|
|
17736
|
+
program2.command("serve-stop").description("Stop the web UI server").action(async () => {
|
|
17737
|
+
await serveStopCommand();
|
|
17738
|
+
});
|
|
17588
17739
|
program2.hook("preAction", async (thisCommand, actionCommand) => {
|
|
17589
17740
|
const opts = thisCommand.optsWithGlobals();
|
|
17590
17741
|
const skipCheck = opts.skipVersionCheck || false;
|
|
@@ -17600,6 +17751,13 @@ function createProgram() {
|
|
|
17600
17751
|
console.warn(warnings);
|
|
17601
17752
|
console.warn("");
|
|
17602
17753
|
}
|
|
17754
|
+
const commandName = actionCommand.name();
|
|
17755
|
+
if (commandName !== "serve-stop" && commandName !== "serve") {
|
|
17756
|
+
const cwd = process.cwd();
|
|
17757
|
+
if (existsSync3(join14(cwd, ".claude"))) {
|
|
17758
|
+
startServeBackground(cwd).catch(() => {});
|
|
17759
|
+
}
|
|
17760
|
+
}
|
|
17603
17761
|
});
|
|
17604
17762
|
return program2;
|
|
17605
17763
|
}
|
package/package.json
CHANGED
|
@@ -115,6 +115,16 @@
|
|
|
115
115
|
}
|
|
116
116
|
],
|
|
117
117
|
"description": "Check codex CLI and Agent Teams availability at session start"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"matcher": "*",
|
|
121
|
+
"hooks": [
|
|
122
|
+
{
|
|
123
|
+
"type": "command",
|
|
124
|
+
"command": "bash .claude/hooks/scripts/serve-autostart.sh"
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"description": "Auto-start Web UI (packages/serve) in background if not already running"
|
|
118
128
|
}
|
|
119
129
|
],
|
|
120
130
|
"SubagentStart": [
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Web UI Auto-start Hook
|
|
3
|
+
# Trigger: SessionStart
|
|
4
|
+
# Purpose: Start packages/serve in the background if not already running
|
|
5
|
+
# Protocol: stdin JSON -> stdout pass-through, exit 0 always (never blocks session start)
|
|
6
|
+
|
|
7
|
+
input=$(cat)
|
|
8
|
+
|
|
9
|
+
PID_FILE="$HOME/.omcustom-serve.pid"
|
|
10
|
+
|
|
11
|
+
# Check if already running
|
|
12
|
+
if [ -f "$PID_FILE" ]; then
|
|
13
|
+
EXISTING_PID=$(cat "$PID_FILE" 2>/dev/null || echo "")
|
|
14
|
+
if [ -n "$EXISTING_PID" ] && kill -0 "$EXISTING_PID" 2>/dev/null; then
|
|
15
|
+
# Server already running — silently pass through
|
|
16
|
+
echo "$input"
|
|
17
|
+
exit 0
|
|
18
|
+
else
|
|
19
|
+
# Stale PID file — clean it up
|
|
20
|
+
rm -f "$PID_FILE"
|
|
21
|
+
fi
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Resolve project root: script lives at .claude/hooks/scripts/ -> 3 levels up
|
|
25
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
26
|
+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
|
27
|
+
|
|
28
|
+
# Prefer git-based root detection if available
|
|
29
|
+
if command -v git >/dev/null 2>&1; then
|
|
30
|
+
GIT_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "")
|
|
31
|
+
if [ -n "$GIT_ROOT" ]; then
|
|
32
|
+
PROJECT_ROOT="$GIT_ROOT"
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Locate the build artifact
|
|
37
|
+
BUILD_FILE="${PROJECT_ROOT}/packages/serve/build/index.js"
|
|
38
|
+
|
|
39
|
+
if [ ! -f "$BUILD_FILE" ]; then
|
|
40
|
+
# Build not present — skip silently (advisory only)
|
|
41
|
+
echo "[Hook] Web UI build not found, skipping auto-start (${BUILD_FILE})" >&2
|
|
42
|
+
echo "$input"
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Ensure node is available
|
|
47
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
48
|
+
echo "[Hook] node not found, skipping Web UI auto-start" >&2
|
|
49
|
+
echo "$input"
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Start server fully detached from current process group
|
|
54
|
+
OMCUSTOM_PORT="${OMCUSTOM_PORT:-4321}"
|
|
55
|
+
OMCUSTOM_HOST="${OMCUSTOM_HOST:-localhost}"
|
|
56
|
+
PORT="$OMCUSTOM_PORT"
|
|
57
|
+
HOST="$OMCUSTOM_HOST"
|
|
58
|
+
|
|
59
|
+
nohup env OMCUSTOM_PORT="$OMCUSTOM_PORT" OMCUSTOM_HOST="$OMCUSTOM_HOST" OMCUSTOM_ORIGIN="http://localhost:${PORT}" node "$BUILD_FILE" \
|
|
60
|
+
>"$HOME/.omcustom-serve.log" 2>&1 \
|
|
61
|
+
</dev/null &
|
|
62
|
+
SERVER_PID=$!
|
|
63
|
+
disown "$SERVER_PID"
|
|
64
|
+
|
|
65
|
+
echo "$SERVER_PID" > "$PID_FILE"
|
|
66
|
+
|
|
67
|
+
echo "[Hook] Web UI started (PID ${SERVER_PID}): http://${HOST}:${PORT}" >&2
|
|
68
|
+
|
|
69
|
+
# Pass through stdin JSON
|
|
70
|
+
echo "$input"
|
|
71
|
+
exit 0
|
package/templates/manifest.json
CHANGED