oh-my-customcode 0.24.2 → 0.30.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/README.md +3 -3
- package/dist/cli/index.js +518 -245
- package/dist/index.js +327 -37
- package/package.json +1 -1
- package/templates/.claude/agents/be-django-expert.md +45 -0
- package/templates/.claude/hooks/hooks.json +10 -0
- package/templates/.claude/hooks/scripts/context-budget-advisor.sh +86 -0
- package/templates/.claude/hooks/scripts/session-env-check.sh +58 -0
- package/templates/.claude/rules/SHOULD-ecomode.md +39 -0
- package/templates/.claude/rules/SHOULD-memory-integration.md +99 -9
- package/templates/.claude/skills/dev-lead-routing/SKILL.md +2 -1
- package/templates/.claude/skills/django-best-practices/SKILL.md +440 -0
- package/templates/CLAUDE.md.en +5 -5
- package/templates/CLAUDE.md.ko +5 -5
- package/templates/guides/django-best-practices/README.md +476 -0
- package/templates/manifest.json +4 -4
package/dist/cli/index.js
CHANGED
|
@@ -31,6 +31,20 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
31
31
|
return to;
|
|
32
32
|
};
|
|
33
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
38
|
+
var __export = (target, all) => {
|
|
39
|
+
for (var name in all)
|
|
40
|
+
__defProp(target, name, {
|
|
41
|
+
get: all[name],
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
34
48
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
35
49
|
|
|
36
50
|
// node_modules/commander/lib/error.js
|
|
@@ -9028,6 +9042,281 @@ var require_public_api = __commonJS((exports) => {
|
|
|
9028
9042
|
exports.stringify = stringify;
|
|
9029
9043
|
});
|
|
9030
9044
|
|
|
9045
|
+
// src/utils/fs.ts
|
|
9046
|
+
var exports_fs = {};
|
|
9047
|
+
__export(exports_fs, {
|
|
9048
|
+
writeTextFile: () => writeTextFile,
|
|
9049
|
+
writeJsonFile: () => writeJsonFile,
|
|
9050
|
+
validatePreserveFilePath: () => validatePreserveFilePath,
|
|
9051
|
+
resolveTemplatePath: () => resolveTemplatePath,
|
|
9052
|
+
resolvePath: () => resolvePath,
|
|
9053
|
+
remove: () => remove,
|
|
9054
|
+
readTextFile: () => readTextFile,
|
|
9055
|
+
readJsonFile: () => readJsonFile,
|
|
9056
|
+
normalizePath: () => normalizePath,
|
|
9057
|
+
move: () => move,
|
|
9058
|
+
listFiles: () => listFiles,
|
|
9059
|
+
isAbsolutePath: () => isAbsolutePath,
|
|
9060
|
+
getRelativePath: () => getRelativePath,
|
|
9061
|
+
getPackageRoot: () => getPackageRoot,
|
|
9062
|
+
getFileStats: () => getFileStats,
|
|
9063
|
+
filesAreIdentical: () => filesAreIdentical,
|
|
9064
|
+
fileExists: () => fileExists,
|
|
9065
|
+
ensureDirectory: () => ensureDirectory,
|
|
9066
|
+
createTempDir: () => createTempDir,
|
|
9067
|
+
copyFile: () => copyFile,
|
|
9068
|
+
copyDirectory: () => copyDirectory,
|
|
9069
|
+
calculateChecksum: () => calculateChecksum
|
|
9070
|
+
});
|
|
9071
|
+
import { dirname as dirname2, isAbsolute, join as join2, relative, resolve, sep } from "node:path";
|
|
9072
|
+
import { fileURLToPath } from "node:url";
|
|
9073
|
+
function validatePreserveFilePath(filePath, projectRoot) {
|
|
9074
|
+
if (!filePath || filePath.trim() === "") {
|
|
9075
|
+
return {
|
|
9076
|
+
valid: false,
|
|
9077
|
+
reason: "Path cannot be empty"
|
|
9078
|
+
};
|
|
9079
|
+
}
|
|
9080
|
+
if (isAbsolute(filePath)) {
|
|
9081
|
+
return {
|
|
9082
|
+
valid: false,
|
|
9083
|
+
reason: "Absolute paths are not allowed"
|
|
9084
|
+
};
|
|
9085
|
+
}
|
|
9086
|
+
const resolvedPath = resolve(projectRoot, filePath);
|
|
9087
|
+
const relativePath = relative(projectRoot, resolvedPath);
|
|
9088
|
+
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
9089
|
+
return {
|
|
9090
|
+
valid: false,
|
|
9091
|
+
reason: "Path cannot traverse outside project root"
|
|
9092
|
+
};
|
|
9093
|
+
}
|
|
9094
|
+
return { valid: true };
|
|
9095
|
+
}
|
|
9096
|
+
async function fileExists(path) {
|
|
9097
|
+
const fs = await import("node:fs/promises");
|
|
9098
|
+
try {
|
|
9099
|
+
await fs.access(path);
|
|
9100
|
+
return true;
|
|
9101
|
+
} catch {
|
|
9102
|
+
return false;
|
|
9103
|
+
}
|
|
9104
|
+
}
|
|
9105
|
+
async function ensureDirectory(path) {
|
|
9106
|
+
const fs = await import("node:fs/promises");
|
|
9107
|
+
await fs.mkdir(path, { recursive: true });
|
|
9108
|
+
}
|
|
9109
|
+
function shouldSkipEntry(entryName, options) {
|
|
9110
|
+
if (options.exclude?.some((pattern) => matchesPattern(entryName, pattern))) {
|
|
9111
|
+
return true;
|
|
9112
|
+
}
|
|
9113
|
+
if (options.include && !options.include.some((pattern) => matchesPattern(entryName, pattern))) {
|
|
9114
|
+
return true;
|
|
9115
|
+
}
|
|
9116
|
+
return false;
|
|
9117
|
+
}
|
|
9118
|
+
async function handleSymlink(srcPath, destPath, options, fs) {
|
|
9119
|
+
const destExists = await fileExists(destPath);
|
|
9120
|
+
if (destExists && !options.overwrite) {
|
|
9121
|
+
return;
|
|
9122
|
+
}
|
|
9123
|
+
if (options.preserveSymlinks !== false) {
|
|
9124
|
+
await copyPreservedSymlink(srcPath, destPath, destExists, fs);
|
|
9125
|
+
} else {
|
|
9126
|
+
await copyFollowedSymlink(srcPath, destPath, destExists, options, fs);
|
|
9127
|
+
}
|
|
9128
|
+
}
|
|
9129
|
+
async function copyPreservedSymlink(srcPath, destPath, destExists, fs) {
|
|
9130
|
+
const linkTarget = await fs.readlink(srcPath);
|
|
9131
|
+
if (destExists) {
|
|
9132
|
+
await fs.unlink(destPath);
|
|
9133
|
+
}
|
|
9134
|
+
await fs.symlink(linkTarget, destPath);
|
|
9135
|
+
}
|
|
9136
|
+
async function copyFollowedSymlink(srcPath, destPath, destExists, options, fs) {
|
|
9137
|
+
const realPath = await fs.realpath(srcPath);
|
|
9138
|
+
const stat = await fs.stat(realPath);
|
|
9139
|
+
if (stat.isDirectory()) {
|
|
9140
|
+
await copyDirectory(realPath, destPath, options);
|
|
9141
|
+
return;
|
|
9142
|
+
}
|
|
9143
|
+
if (destExists) {
|
|
9144
|
+
await fs.unlink(destPath);
|
|
9145
|
+
}
|
|
9146
|
+
await fs.copyFile(realPath, destPath);
|
|
9147
|
+
}
|
|
9148
|
+
async function handleFile(srcPath, destPath, options, fs) {
|
|
9149
|
+
const destExists = await fileExists(destPath);
|
|
9150
|
+
if (destExists && !options.overwrite) {
|
|
9151
|
+
return;
|
|
9152
|
+
}
|
|
9153
|
+
await fs.copyFile(srcPath, destPath);
|
|
9154
|
+
if (options.preserveTimestamps) {
|
|
9155
|
+
const stats = await fs.stat(srcPath);
|
|
9156
|
+
await fs.utimes(destPath, stats.atime, stats.mtime);
|
|
9157
|
+
}
|
|
9158
|
+
}
|
|
9159
|
+
function shouldSkipPath(destPath, destRoot, skipPaths) {
|
|
9160
|
+
if (!skipPaths || skipPaths.length === 0) {
|
|
9161
|
+
return false;
|
|
9162
|
+
}
|
|
9163
|
+
const relativePath = relative(destRoot, destPath);
|
|
9164
|
+
for (const skipPath of skipPaths) {
|
|
9165
|
+
if (skipPath.endsWith("/")) {
|
|
9166
|
+
const dirPath = skipPath.slice(0, -1);
|
|
9167
|
+
if (relativePath === dirPath || relativePath.startsWith(dirPath + sep)) {
|
|
9168
|
+
return true;
|
|
9169
|
+
}
|
|
9170
|
+
} else {
|
|
9171
|
+
if (relativePath === skipPath) {
|
|
9172
|
+
return true;
|
|
9173
|
+
}
|
|
9174
|
+
}
|
|
9175
|
+
}
|
|
9176
|
+
return false;
|
|
9177
|
+
}
|
|
9178
|
+
async function copyDirectory(src, dest, options = {}) {
|
|
9179
|
+
const fs = await import("node:fs/promises");
|
|
9180
|
+
const path = await import("node:path");
|
|
9181
|
+
await ensureDirectory(dest);
|
|
9182
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
9183
|
+
for (const entry of entries) {
|
|
9184
|
+
if (shouldSkipEntry(entry.name, options)) {
|
|
9185
|
+
continue;
|
|
9186
|
+
}
|
|
9187
|
+
const srcPath = path.join(src, entry.name);
|
|
9188
|
+
const destPath = path.join(dest, entry.name);
|
|
9189
|
+
if (shouldSkipPath(destPath, dest, options.skipPaths)) {
|
|
9190
|
+
continue;
|
|
9191
|
+
}
|
|
9192
|
+
if (entry.isSymbolicLink()) {
|
|
9193
|
+
await handleSymlink(srcPath, destPath, options, fs);
|
|
9194
|
+
} else if (entry.isDirectory()) {
|
|
9195
|
+
await copyDirectory(srcPath, destPath, options);
|
|
9196
|
+
} else if (entry.isFile()) {
|
|
9197
|
+
await handleFile(srcPath, destPath, options, fs);
|
|
9198
|
+
}
|
|
9199
|
+
}
|
|
9200
|
+
}
|
|
9201
|
+
async function readJsonFile(path) {
|
|
9202
|
+
const fs = await import("node:fs/promises");
|
|
9203
|
+
const content = await fs.readFile(path, "utf-8");
|
|
9204
|
+
return JSON.parse(content);
|
|
9205
|
+
}
|
|
9206
|
+
async function writeJsonFile(path, data) {
|
|
9207
|
+
const fs = await import("node:fs/promises");
|
|
9208
|
+
const content = JSON.stringify(data, null, 2);
|
|
9209
|
+
await fs.writeFile(path, content, "utf-8");
|
|
9210
|
+
}
|
|
9211
|
+
async function readTextFile(path) {
|
|
9212
|
+
const fs = await import("node:fs/promises");
|
|
9213
|
+
return fs.readFile(path, "utf-8");
|
|
9214
|
+
}
|
|
9215
|
+
async function writeTextFile(path, content) {
|
|
9216
|
+
const fs = await import("node:fs/promises");
|
|
9217
|
+
await ensureDirectory(dirname2(path));
|
|
9218
|
+
await fs.writeFile(path, content, "utf-8");
|
|
9219
|
+
}
|
|
9220
|
+
async function remove(path) {
|
|
9221
|
+
const fs = await import("node:fs/promises");
|
|
9222
|
+
const stat = await fs.stat(path);
|
|
9223
|
+
if (stat.isDirectory()) {
|
|
9224
|
+
await fs.rm(path, { recursive: true, force: true });
|
|
9225
|
+
} else {
|
|
9226
|
+
await fs.unlink(path);
|
|
9227
|
+
}
|
|
9228
|
+
}
|
|
9229
|
+
function getPackageRoot() {
|
|
9230
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
9231
|
+
const currentDir = dirname2(currentFile);
|
|
9232
|
+
return resolve(currentDir, "..", "..");
|
|
9233
|
+
}
|
|
9234
|
+
function resolveTemplatePath(relativePath) {
|
|
9235
|
+
const packageRoot = getPackageRoot();
|
|
9236
|
+
return join2(packageRoot, "templates", relativePath);
|
|
9237
|
+
}
|
|
9238
|
+
async function listFiles(dir2, options = {}) {
|
|
9239
|
+
const fs = await import("node:fs/promises");
|
|
9240
|
+
const path = await import("node:path");
|
|
9241
|
+
const files = [];
|
|
9242
|
+
const entries = await fs.readdir(dir2, { withFileTypes: true });
|
|
9243
|
+
for (const entry of entries) {
|
|
9244
|
+
const fullPath = path.join(dir2, entry.name);
|
|
9245
|
+
if (entry.isDirectory() && options.recursive) {
|
|
9246
|
+
const subFiles = await listFiles(fullPath, options);
|
|
9247
|
+
files.push(...subFiles);
|
|
9248
|
+
} else if (entry.isFile()) {
|
|
9249
|
+
if (!options.pattern || matchesPattern(entry.name, options.pattern)) {
|
|
9250
|
+
files.push(fullPath);
|
|
9251
|
+
}
|
|
9252
|
+
}
|
|
9253
|
+
}
|
|
9254
|
+
return files;
|
|
9255
|
+
}
|
|
9256
|
+
async function getFileStats(path) {
|
|
9257
|
+
const fs = await import("node:fs/promises");
|
|
9258
|
+
const stats = await fs.stat(path);
|
|
9259
|
+
return {
|
|
9260
|
+
size: stats.size,
|
|
9261
|
+
created: stats.birthtime,
|
|
9262
|
+
modified: stats.mtime,
|
|
9263
|
+
isDirectory: stats.isDirectory(),
|
|
9264
|
+
isFile: stats.isFile()
|
|
9265
|
+
};
|
|
9266
|
+
}
|
|
9267
|
+
async function copyFile(src, dest) {
|
|
9268
|
+
const fs = await import("node:fs/promises");
|
|
9269
|
+
await ensureDirectory(dirname2(dest));
|
|
9270
|
+
await fs.copyFile(src, dest);
|
|
9271
|
+
}
|
|
9272
|
+
async function move(src, dest) {
|
|
9273
|
+
const fs = await import("node:fs/promises");
|
|
9274
|
+
await ensureDirectory(dirname2(dest));
|
|
9275
|
+
await fs.rename(src, dest);
|
|
9276
|
+
}
|
|
9277
|
+
async function createTempDir(prefix = "omcustom-") {
|
|
9278
|
+
const fs = await import("node:fs/promises");
|
|
9279
|
+
const os = await import("node:os");
|
|
9280
|
+
const path = await import("node:path");
|
|
9281
|
+
const tempBase = os.tmpdir();
|
|
9282
|
+
const tempDir = path.join(tempBase, `${prefix}${Date.now()}`);
|
|
9283
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
9284
|
+
return tempDir;
|
|
9285
|
+
}
|
|
9286
|
+
async function calculateChecksum(path) {
|
|
9287
|
+
const fs = await import("node:fs/promises");
|
|
9288
|
+
const crypto = await import("node:crypto");
|
|
9289
|
+
const content = await fs.readFile(path);
|
|
9290
|
+
const hash = crypto.createHash("md5");
|
|
9291
|
+
hash.update(content);
|
|
9292
|
+
return hash.digest("hex");
|
|
9293
|
+
}
|
|
9294
|
+
async function filesAreIdentical(path1, path2) {
|
|
9295
|
+
const [checksum1, checksum2] = await Promise.all([
|
|
9296
|
+
calculateChecksum(path1),
|
|
9297
|
+
calculateChecksum(path2)
|
|
9298
|
+
]);
|
|
9299
|
+
return checksum1 === checksum2;
|
|
9300
|
+
}
|
|
9301
|
+
function matchesPattern(filename, pattern) {
|
|
9302
|
+
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
9303
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
9304
|
+
return regex.test(filename);
|
|
9305
|
+
}
|
|
9306
|
+
function getRelativePath(basePath, fullPath) {
|
|
9307
|
+
return relative(basePath, fullPath);
|
|
9308
|
+
}
|
|
9309
|
+
function normalizePath(inputPath) {
|
|
9310
|
+
return inputPath.replace(/\\/g, "/");
|
|
9311
|
+
}
|
|
9312
|
+
function isAbsolutePath(inputPath) {
|
|
9313
|
+
return isAbsolute(inputPath);
|
|
9314
|
+
}
|
|
9315
|
+
function resolvePath(...paths) {
|
|
9316
|
+
return resolve(...paths);
|
|
9317
|
+
}
|
|
9318
|
+
var init_fs = () => {};
|
|
9319
|
+
|
|
9031
9320
|
// src/cli/index.ts
|
|
9032
9321
|
import { createRequire as createRequire2 } from "node:module";
|
|
9033
9322
|
|
|
@@ -12626,196 +12915,9 @@ var $visit = visit.visit;
|
|
|
12626
12915
|
var $visitAsync = visit.visitAsync;
|
|
12627
12916
|
|
|
12628
12917
|
// src/core/config.ts
|
|
12918
|
+
init_fs();
|
|
12629
12919
|
import { join as join3 } from "node:path";
|
|
12630
12920
|
|
|
12631
|
-
// src/utils/fs.ts
|
|
12632
|
-
import { dirname as dirname2, isAbsolute, join as join2, relative, resolve, sep } from "node:path";
|
|
12633
|
-
import { fileURLToPath } from "node:url";
|
|
12634
|
-
function validatePreserveFilePath(filePath, projectRoot) {
|
|
12635
|
-
if (!filePath || filePath.trim() === "") {
|
|
12636
|
-
return {
|
|
12637
|
-
valid: false,
|
|
12638
|
-
reason: "Path cannot be empty"
|
|
12639
|
-
};
|
|
12640
|
-
}
|
|
12641
|
-
if (isAbsolute(filePath)) {
|
|
12642
|
-
return {
|
|
12643
|
-
valid: false,
|
|
12644
|
-
reason: "Absolute paths are not allowed"
|
|
12645
|
-
};
|
|
12646
|
-
}
|
|
12647
|
-
const resolvedPath = resolve(projectRoot, filePath);
|
|
12648
|
-
const relativePath = relative(projectRoot, resolvedPath);
|
|
12649
|
-
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
12650
|
-
return {
|
|
12651
|
-
valid: false,
|
|
12652
|
-
reason: "Path cannot traverse outside project root"
|
|
12653
|
-
};
|
|
12654
|
-
}
|
|
12655
|
-
return { valid: true };
|
|
12656
|
-
}
|
|
12657
|
-
async function fileExists(path) {
|
|
12658
|
-
const fs = await import("node:fs/promises");
|
|
12659
|
-
try {
|
|
12660
|
-
await fs.access(path);
|
|
12661
|
-
return true;
|
|
12662
|
-
} catch {
|
|
12663
|
-
return false;
|
|
12664
|
-
}
|
|
12665
|
-
}
|
|
12666
|
-
async function ensureDirectory(path) {
|
|
12667
|
-
const fs = await import("node:fs/promises");
|
|
12668
|
-
await fs.mkdir(path, { recursive: true });
|
|
12669
|
-
}
|
|
12670
|
-
function shouldSkipEntry(entryName, options) {
|
|
12671
|
-
if (options.exclude?.some((pattern) => matchesPattern(entryName, pattern))) {
|
|
12672
|
-
return true;
|
|
12673
|
-
}
|
|
12674
|
-
if (options.include && !options.include.some((pattern) => matchesPattern(entryName, pattern))) {
|
|
12675
|
-
return true;
|
|
12676
|
-
}
|
|
12677
|
-
return false;
|
|
12678
|
-
}
|
|
12679
|
-
async function handleSymlink(srcPath, destPath, options, fs) {
|
|
12680
|
-
const destExists = await fileExists(destPath);
|
|
12681
|
-
if (destExists && !options.overwrite) {
|
|
12682
|
-
return;
|
|
12683
|
-
}
|
|
12684
|
-
if (options.preserveSymlinks !== false) {
|
|
12685
|
-
await copyPreservedSymlink(srcPath, destPath, destExists, fs);
|
|
12686
|
-
} else {
|
|
12687
|
-
await copyFollowedSymlink(srcPath, destPath, destExists, options, fs);
|
|
12688
|
-
}
|
|
12689
|
-
}
|
|
12690
|
-
async function copyPreservedSymlink(srcPath, destPath, destExists, fs) {
|
|
12691
|
-
const linkTarget = await fs.readlink(srcPath);
|
|
12692
|
-
if (destExists) {
|
|
12693
|
-
await fs.unlink(destPath);
|
|
12694
|
-
}
|
|
12695
|
-
await fs.symlink(linkTarget, destPath);
|
|
12696
|
-
}
|
|
12697
|
-
async function copyFollowedSymlink(srcPath, destPath, destExists, options, fs) {
|
|
12698
|
-
const realPath = await fs.realpath(srcPath);
|
|
12699
|
-
const stat = await fs.stat(realPath);
|
|
12700
|
-
if (stat.isDirectory()) {
|
|
12701
|
-
await copyDirectory(realPath, destPath, options);
|
|
12702
|
-
return;
|
|
12703
|
-
}
|
|
12704
|
-
if (destExists) {
|
|
12705
|
-
await fs.unlink(destPath);
|
|
12706
|
-
}
|
|
12707
|
-
await fs.copyFile(realPath, destPath);
|
|
12708
|
-
}
|
|
12709
|
-
async function handleFile(srcPath, destPath, options, fs) {
|
|
12710
|
-
const destExists = await fileExists(destPath);
|
|
12711
|
-
if (destExists && !options.overwrite) {
|
|
12712
|
-
return;
|
|
12713
|
-
}
|
|
12714
|
-
await fs.copyFile(srcPath, destPath);
|
|
12715
|
-
if (options.preserveTimestamps) {
|
|
12716
|
-
const stats = await fs.stat(srcPath);
|
|
12717
|
-
await fs.utimes(destPath, stats.atime, stats.mtime);
|
|
12718
|
-
}
|
|
12719
|
-
}
|
|
12720
|
-
function shouldSkipPath(destPath, destRoot, skipPaths) {
|
|
12721
|
-
if (!skipPaths || skipPaths.length === 0) {
|
|
12722
|
-
return false;
|
|
12723
|
-
}
|
|
12724
|
-
const relativePath = relative(destRoot, destPath);
|
|
12725
|
-
for (const skipPath of skipPaths) {
|
|
12726
|
-
if (skipPath.endsWith("/")) {
|
|
12727
|
-
const dirPath = skipPath.slice(0, -1);
|
|
12728
|
-
if (relativePath === dirPath || relativePath.startsWith(dirPath + sep)) {
|
|
12729
|
-
return true;
|
|
12730
|
-
}
|
|
12731
|
-
} else {
|
|
12732
|
-
if (relativePath === skipPath) {
|
|
12733
|
-
return true;
|
|
12734
|
-
}
|
|
12735
|
-
}
|
|
12736
|
-
}
|
|
12737
|
-
return false;
|
|
12738
|
-
}
|
|
12739
|
-
async function copyDirectory(src, dest, options = {}) {
|
|
12740
|
-
const fs = await import("node:fs/promises");
|
|
12741
|
-
const path = await import("node:path");
|
|
12742
|
-
await ensureDirectory(dest);
|
|
12743
|
-
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
12744
|
-
for (const entry of entries) {
|
|
12745
|
-
if (shouldSkipEntry(entry.name, options)) {
|
|
12746
|
-
continue;
|
|
12747
|
-
}
|
|
12748
|
-
const srcPath = path.join(src, entry.name);
|
|
12749
|
-
const destPath = path.join(dest, entry.name);
|
|
12750
|
-
if (shouldSkipPath(destPath, dest, options.skipPaths)) {
|
|
12751
|
-
continue;
|
|
12752
|
-
}
|
|
12753
|
-
if (entry.isSymbolicLink()) {
|
|
12754
|
-
await handleSymlink(srcPath, destPath, options, fs);
|
|
12755
|
-
} else if (entry.isDirectory()) {
|
|
12756
|
-
await copyDirectory(srcPath, destPath, options);
|
|
12757
|
-
} else if (entry.isFile()) {
|
|
12758
|
-
await handleFile(srcPath, destPath, options, fs);
|
|
12759
|
-
}
|
|
12760
|
-
}
|
|
12761
|
-
}
|
|
12762
|
-
async function readJsonFile(path) {
|
|
12763
|
-
const fs = await import("node:fs/promises");
|
|
12764
|
-
const content = await fs.readFile(path, "utf-8");
|
|
12765
|
-
return JSON.parse(content);
|
|
12766
|
-
}
|
|
12767
|
-
async function writeJsonFile(path, data) {
|
|
12768
|
-
const fs = await import("node:fs/promises");
|
|
12769
|
-
const content = JSON.stringify(data, null, 2);
|
|
12770
|
-
await fs.writeFile(path, content, "utf-8");
|
|
12771
|
-
}
|
|
12772
|
-
async function readTextFile(path) {
|
|
12773
|
-
const fs = await import("node:fs/promises");
|
|
12774
|
-
return fs.readFile(path, "utf-8");
|
|
12775
|
-
}
|
|
12776
|
-
async function writeTextFile(path, content) {
|
|
12777
|
-
const fs = await import("node:fs/promises");
|
|
12778
|
-
await ensureDirectory(dirname2(path));
|
|
12779
|
-
await fs.writeFile(path, content, "utf-8");
|
|
12780
|
-
}
|
|
12781
|
-
function getPackageRoot() {
|
|
12782
|
-
const currentFile = fileURLToPath(import.meta.url);
|
|
12783
|
-
const currentDir = dirname2(currentFile);
|
|
12784
|
-
return resolve(currentDir, "..", "..");
|
|
12785
|
-
}
|
|
12786
|
-
function resolveTemplatePath(relativePath) {
|
|
12787
|
-
const packageRoot = getPackageRoot();
|
|
12788
|
-
return join2(packageRoot, "templates", relativePath);
|
|
12789
|
-
}
|
|
12790
|
-
async function listFiles(dir2, options = {}) {
|
|
12791
|
-
const fs = await import("node:fs/promises");
|
|
12792
|
-
const path = await import("node:path");
|
|
12793
|
-
const files = [];
|
|
12794
|
-
const entries = await fs.readdir(dir2, { withFileTypes: true });
|
|
12795
|
-
for (const entry of entries) {
|
|
12796
|
-
const fullPath = path.join(dir2, entry.name);
|
|
12797
|
-
if (entry.isDirectory() && options.recursive) {
|
|
12798
|
-
const subFiles = await listFiles(fullPath, options);
|
|
12799
|
-
files.push(...subFiles);
|
|
12800
|
-
} else if (entry.isFile()) {
|
|
12801
|
-
if (!options.pattern || matchesPattern(entry.name, options.pattern)) {
|
|
12802
|
-
files.push(fullPath);
|
|
12803
|
-
}
|
|
12804
|
-
}
|
|
12805
|
-
}
|
|
12806
|
-
return files;
|
|
12807
|
-
}
|
|
12808
|
-
async function copyFile(src, dest) {
|
|
12809
|
-
const fs = await import("node:fs/promises");
|
|
12810
|
-
await ensureDirectory(dirname2(dest));
|
|
12811
|
-
await fs.copyFile(src, dest);
|
|
12812
|
-
}
|
|
12813
|
-
function matchesPattern(filename, pattern) {
|
|
12814
|
-
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
12815
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
12816
|
-
return regex.test(filename);
|
|
12817
|
-
}
|
|
12818
|
-
|
|
12819
12921
|
// src/utils/logger.ts
|
|
12820
12922
|
var currentOptions = {
|
|
12821
12923
|
level: "info",
|
|
@@ -13026,7 +13128,12 @@ function getDefaultConfig() {
|
|
|
13026
13128
|
checkIntervalHours: 24,
|
|
13027
13129
|
autoApplyMinor: false
|
|
13028
13130
|
},
|
|
13029
|
-
preserveFiles: [
|
|
13131
|
+
preserveFiles: [
|
|
13132
|
+
".claude/settings.json",
|
|
13133
|
+
".claude/settings.local.json",
|
|
13134
|
+
".claude/agent-memory/",
|
|
13135
|
+
".claude/agent-memory-local/"
|
|
13136
|
+
],
|
|
13030
13137
|
customComponents: []
|
|
13031
13138
|
};
|
|
13032
13139
|
}
|
|
@@ -13670,11 +13777,141 @@ async function doctorCommand(options = {}) {
|
|
|
13670
13777
|
}
|
|
13671
13778
|
|
|
13672
13779
|
// src/cli/init.ts
|
|
13673
|
-
import { join as
|
|
13780
|
+
import { join as join7 } from "node:path";
|
|
13674
13781
|
|
|
13675
13782
|
// src/core/installer.ts
|
|
13783
|
+
init_fs();
|
|
13676
13784
|
import { readFile as fsReadFile, writeFile as fsWriteFile, rename } from "node:fs/promises";
|
|
13785
|
+
import { basename as basename2, join as join5 } from "node:path";
|
|
13786
|
+
|
|
13787
|
+
// src/core/file-preservation.ts
|
|
13788
|
+
init_fs();
|
|
13677
13789
|
import { basename, join as join4 } from "node:path";
|
|
13790
|
+
var DEFAULT_CRITICAL_FILES = ["settings.json", "settings.local.json"];
|
|
13791
|
+
var DEFAULT_CRITICAL_DIRECTORIES = ["agent-memory", "agent-memory-local"];
|
|
13792
|
+
async function extractSingleFile(fileName, rootDir, tempDir, result) {
|
|
13793
|
+
const srcPath = join4(rootDir, fileName);
|
|
13794
|
+
const destPath = join4(tempDir, fileName);
|
|
13795
|
+
try {
|
|
13796
|
+
if (await fileExists(srcPath)) {
|
|
13797
|
+
await copyFile(srcPath, destPath);
|
|
13798
|
+
result.extractedFiles.push(fileName);
|
|
13799
|
+
debug("preserve.extracted_file", { file: fileName });
|
|
13800
|
+
}
|
|
13801
|
+
} catch (err) {
|
|
13802
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
13803
|
+
result.failures.push({ path: fileName, reason });
|
|
13804
|
+
warn("preserve.extract_failed", { file: fileName, error: reason });
|
|
13805
|
+
}
|
|
13806
|
+
}
|
|
13807
|
+
async function extractSingleDir(dirName, rootDir, tempDir, result) {
|
|
13808
|
+
const srcPath = join4(rootDir, dirName);
|
|
13809
|
+
const destPath = join4(tempDir, dirName);
|
|
13810
|
+
try {
|
|
13811
|
+
if (await fileExists(srcPath)) {
|
|
13812
|
+
await copyDirectory(srcPath, destPath, { overwrite: true, preserveTimestamps: true });
|
|
13813
|
+
result.extractedDirs.push(dirName);
|
|
13814
|
+
debug("preserve.extracted_dir", { dir: dirName });
|
|
13815
|
+
}
|
|
13816
|
+
} catch (err) {
|
|
13817
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
13818
|
+
result.failures.push({ path: dirName, reason });
|
|
13819
|
+
warn("preserve.extract_dir_failed", { dir: dirName, error: reason });
|
|
13820
|
+
}
|
|
13821
|
+
}
|
|
13822
|
+
async function extractCriticalFiles(rootDir, tempDir, additionalFiles = []) {
|
|
13823
|
+
const result = {
|
|
13824
|
+
tempDir,
|
|
13825
|
+
extractedFiles: [],
|
|
13826
|
+
extractedDirs: [],
|
|
13827
|
+
failures: []
|
|
13828
|
+
};
|
|
13829
|
+
await ensureDirectory(tempDir);
|
|
13830
|
+
const filesToExtract = [...DEFAULT_CRITICAL_FILES, ...additionalFiles];
|
|
13831
|
+
for (const fileName of filesToExtract) {
|
|
13832
|
+
await extractSingleFile(fileName, rootDir, tempDir, result);
|
|
13833
|
+
}
|
|
13834
|
+
for (const dirName of DEFAULT_CRITICAL_DIRECTORIES) {
|
|
13835
|
+
await extractSingleDir(dirName, rootDir, tempDir, result);
|
|
13836
|
+
}
|
|
13837
|
+
return result;
|
|
13838
|
+
}
|
|
13839
|
+
async function restoreCriticalFiles(rootDir, preservation) {
|
|
13840
|
+
const result = {
|
|
13841
|
+
restoredFiles: [],
|
|
13842
|
+
restoredDirs: [],
|
|
13843
|
+
failures: []
|
|
13844
|
+
};
|
|
13845
|
+
for (const fileName of preservation.extractedFiles) {
|
|
13846
|
+
const preservedPath = join4(preservation.tempDir, fileName);
|
|
13847
|
+
const targetPath = join4(rootDir, fileName);
|
|
13848
|
+
try {
|
|
13849
|
+
if (fileName.endsWith(".json")) {
|
|
13850
|
+
await mergeJsonFile(preservedPath, targetPath);
|
|
13851
|
+
} else {
|
|
13852
|
+
await copyFile(preservedPath, targetPath);
|
|
13853
|
+
}
|
|
13854
|
+
result.restoredFiles.push(fileName);
|
|
13855
|
+
debug("preserve.restored_file", { file: fileName });
|
|
13856
|
+
} catch (err) {
|
|
13857
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
13858
|
+
result.failures.push({ path: fileName, reason });
|
|
13859
|
+
warn("preserve.restore_failed", { file: fileName, error: reason });
|
|
13860
|
+
}
|
|
13861
|
+
}
|
|
13862
|
+
for (const dirName of preservation.extractedDirs) {
|
|
13863
|
+
const preservedPath = join4(preservation.tempDir, dirName);
|
|
13864
|
+
const targetPath = join4(rootDir, dirName);
|
|
13865
|
+
try {
|
|
13866
|
+
await copyDirectory(preservedPath, targetPath, {
|
|
13867
|
+
overwrite: false,
|
|
13868
|
+
preserveTimestamps: true
|
|
13869
|
+
});
|
|
13870
|
+
result.restoredDirs.push(dirName);
|
|
13871
|
+
debug("preserve.restored_dir", { dir: dirName });
|
|
13872
|
+
} catch (err) {
|
|
13873
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
13874
|
+
result.failures.push({ path: dirName, reason });
|
|
13875
|
+
warn("preserve.restore_dir_failed", { dir: dirName, error: reason });
|
|
13876
|
+
}
|
|
13877
|
+
}
|
|
13878
|
+
return result;
|
|
13879
|
+
}
|
|
13880
|
+
async function mergeJsonFile(preservedPath, targetPath) {
|
|
13881
|
+
const preservedData = await readJsonFile(preservedPath);
|
|
13882
|
+
if (await fileExists(targetPath)) {
|
|
13883
|
+
const targetData = await readJsonFile(targetPath);
|
|
13884
|
+
const merged = deepMerge(targetData, preservedData);
|
|
13885
|
+
await writeJsonFile(targetPath, merged);
|
|
13886
|
+
debug("preserve.merged_json", { file: basename(targetPath) });
|
|
13887
|
+
} else {
|
|
13888
|
+
await copyFile(preservedPath, targetPath);
|
|
13889
|
+
debug("preserve.copied_json", { file: basename(targetPath) });
|
|
13890
|
+
}
|
|
13891
|
+
}
|
|
13892
|
+
function deepMerge(target, source) {
|
|
13893
|
+
const result = { ...target };
|
|
13894
|
+
for (const key of Object.keys(source)) {
|
|
13895
|
+
const sourceVal = source[key];
|
|
13896
|
+
const targetVal = result[key];
|
|
13897
|
+
if (sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal !== null && typeof targetVal === "object" && !Array.isArray(targetVal)) {
|
|
13898
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
13899
|
+
} else {
|
|
13900
|
+
result[key] = sourceVal;
|
|
13901
|
+
}
|
|
13902
|
+
}
|
|
13903
|
+
return result;
|
|
13904
|
+
}
|
|
13905
|
+
async function cleanupPreservation(tempDir) {
|
|
13906
|
+
try {
|
|
13907
|
+
const { rm } = await import("node:fs/promises");
|
|
13908
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
13909
|
+
debug("preserve.cleanup", { dir: tempDir });
|
|
13910
|
+
} catch (err) {
|
|
13911
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
13912
|
+
warn("preserve.cleanup_failed", { dir: tempDir, error: reason });
|
|
13913
|
+
}
|
|
13914
|
+
}
|
|
13678
13915
|
|
|
13679
13916
|
// src/core/git-workflow.ts
|
|
13680
13917
|
import { execFileSync } from "node:child_process";
|
|
@@ -13927,7 +14164,7 @@ function getDefaultWorkflow() {
|
|
|
13927
14164
|
var DEFAULT_LANGUAGE2 = "en";
|
|
13928
14165
|
function getTemplateDir() {
|
|
13929
14166
|
const packageRoot = getPackageRoot();
|
|
13930
|
-
return
|
|
14167
|
+
return join5(packageRoot, "templates");
|
|
13931
14168
|
}
|
|
13932
14169
|
function createInstallResult(targetDir) {
|
|
13933
14170
|
return {
|
|
@@ -13947,12 +14184,27 @@ async function ensureTargetDirectory(targetDir) {
|
|
|
13947
14184
|
}
|
|
13948
14185
|
async function handleBackup(targetDir, shouldBackup, result) {
|
|
13949
14186
|
if (!shouldBackup)
|
|
13950
|
-
return;
|
|
14187
|
+
return null;
|
|
14188
|
+
const layout = getProviderLayout();
|
|
14189
|
+
const rootDir = join5(targetDir, layout.rootDir);
|
|
14190
|
+
let preservation = null;
|
|
14191
|
+
if (await fileExists(rootDir)) {
|
|
14192
|
+
const { createTempDir: createTempDir2 } = await Promise.resolve().then(() => (init_fs(), exports_fs));
|
|
14193
|
+
const tempDir = await createTempDir2("omcustom-preserve-");
|
|
14194
|
+
preservation = await extractCriticalFiles(rootDir, tempDir);
|
|
14195
|
+
if (preservation.extractedFiles.length > 0 || preservation.extractedDirs.length > 0) {
|
|
14196
|
+
info("install.preserved", {
|
|
14197
|
+
files: String(preservation.extractedFiles.length),
|
|
14198
|
+
dirs: String(preservation.extractedDirs.length)
|
|
14199
|
+
});
|
|
14200
|
+
}
|
|
14201
|
+
}
|
|
13951
14202
|
const backupPaths = await backupExistingInstallation(targetDir);
|
|
13952
14203
|
result.backedUpPaths.push(...backupPaths);
|
|
13953
14204
|
if (backupPaths.length > 0) {
|
|
13954
14205
|
info("install.backup", { path: backupPaths[0] });
|
|
13955
14206
|
}
|
|
14207
|
+
return preservation;
|
|
13956
14208
|
}
|
|
13957
14209
|
async function checkAndWarnExisting(targetDir, force, backup, result) {
|
|
13958
14210
|
if (force || backup)
|
|
@@ -13991,8 +14243,8 @@ async function installSingleComponent(targetDir, component, options, result) {
|
|
|
13991
14243
|
}
|
|
13992
14244
|
async function installStatusline(targetDir, options, _result) {
|
|
13993
14245
|
const layout = getProviderLayout();
|
|
13994
|
-
const srcPath = resolveTemplatePath(
|
|
13995
|
-
const destPath =
|
|
14246
|
+
const srcPath = resolveTemplatePath(join5(layout.rootDir, "statusline.sh"));
|
|
14247
|
+
const destPath = join5(targetDir, layout.rootDir, "statusline.sh");
|
|
13996
14248
|
if (!await fileExists(srcPath)) {
|
|
13997
14249
|
debug("install.statusline_not_found", { path: srcPath });
|
|
13998
14250
|
return;
|
|
@@ -14010,7 +14262,7 @@ async function installStatusline(targetDir, options, _result) {
|
|
|
14010
14262
|
}
|
|
14011
14263
|
async function installSettingsLocal(targetDir, result) {
|
|
14012
14264
|
const layout = getProviderLayout();
|
|
14013
|
-
const settingsPath =
|
|
14265
|
+
const settingsPath = join5(targetDir, layout.rootDir, "settings.local.json");
|
|
14014
14266
|
const statusLineConfig = {
|
|
14015
14267
|
statusLine: {
|
|
14016
14268
|
type: "command",
|
|
@@ -14058,13 +14310,30 @@ async function install(options) {
|
|
|
14058
14310
|
try {
|
|
14059
14311
|
info("install.start", { targetDir: options.targetDir });
|
|
14060
14312
|
await ensureTargetDirectory(options.targetDir);
|
|
14061
|
-
await handleBackup(options.targetDir, !!options.backup, result);
|
|
14313
|
+
const preservation = await handleBackup(options.targetDir, !!options.backup, result);
|
|
14062
14314
|
await checkAndWarnExisting(options.targetDir, !!options.force, !!options.backup, result);
|
|
14063
14315
|
await verifyTemplateDirectory();
|
|
14064
14316
|
await installAllComponents(options.targetDir, options, result);
|
|
14065
14317
|
await installStatusline(options.targetDir, options, result);
|
|
14066
14318
|
await installSettingsLocal(options.targetDir, result);
|
|
14067
14319
|
await installEntryDocWithTracking(options.targetDir, options, result);
|
|
14320
|
+
if (preservation) {
|
|
14321
|
+
const layout = getProviderLayout();
|
|
14322
|
+
const rootDir = join5(options.targetDir, layout.rootDir);
|
|
14323
|
+
const restoration = await restoreCriticalFiles(rootDir, preservation);
|
|
14324
|
+
if (restoration.restoredFiles.length > 0 || restoration.restoredDirs.length > 0) {
|
|
14325
|
+
info("install.restored", {
|
|
14326
|
+
files: String(restoration.restoredFiles.length),
|
|
14327
|
+
dirs: String(restoration.restoredDirs.length)
|
|
14328
|
+
});
|
|
14329
|
+
}
|
|
14330
|
+
if (restoration.failures.length > 0) {
|
|
14331
|
+
for (const failure of restoration.failures) {
|
|
14332
|
+
result.warnings.push(`Failed to restore ${failure.path}: ${failure.reason}`);
|
|
14333
|
+
}
|
|
14334
|
+
}
|
|
14335
|
+
await cleanupPreservation(preservation.tempDir);
|
|
14336
|
+
}
|
|
14068
14337
|
await updateInstallConfig(options.targetDir, options, result.installedComponents);
|
|
14069
14338
|
result.success = true;
|
|
14070
14339
|
success("install.success");
|
|
@@ -14083,7 +14352,7 @@ async function installComponent(targetDir, component, options) {
|
|
|
14083
14352
|
return false;
|
|
14084
14353
|
}
|
|
14085
14354
|
const templatePath = getComponentPath(component);
|
|
14086
|
-
const destPath =
|
|
14355
|
+
const destPath = join5(targetDir, templatePath);
|
|
14087
14356
|
const destExists = await fileExists(destPath);
|
|
14088
14357
|
if (destExists && !options.force && !options.backup) {
|
|
14089
14358
|
debug("install.component_skipped", { component });
|
|
@@ -14111,7 +14380,7 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
14111
14380
|
const layout = getProviderLayout();
|
|
14112
14381
|
const templateFile = getEntryTemplateName(language);
|
|
14113
14382
|
const srcPath = resolveTemplatePath(templateFile);
|
|
14114
|
-
const destPath =
|
|
14383
|
+
const destPath = join5(targetDir, layout.entryFile);
|
|
14115
14384
|
if (!await fileExists(srcPath)) {
|
|
14116
14385
|
warn("install.entry_md_not_found", { language, path: srcPath, entry: layout.entryFile });
|
|
14117
14386
|
return false;
|
|
@@ -14131,8 +14400,8 @@ async function installEntryDoc(targetDir, language, overwrite = false) {
|
|
|
14131
14400
|
return true;
|
|
14132
14401
|
}
|
|
14133
14402
|
async function backupExisting(sourcePath, backupDir) {
|
|
14134
|
-
const name =
|
|
14135
|
-
const backupPath =
|
|
14403
|
+
const name = basename2(sourcePath);
|
|
14404
|
+
const backupPath = join5(backupDir, name);
|
|
14136
14405
|
await rename(sourcePath, backupPath);
|
|
14137
14406
|
return backupPath;
|
|
14138
14407
|
}
|
|
@@ -14141,7 +14410,7 @@ async function checkExistingPaths(targetDir) {
|
|
|
14141
14410
|
const pathsToCheck = [layout.entryFile, layout.rootDir, "guides"];
|
|
14142
14411
|
const existingPaths = [];
|
|
14143
14412
|
for (const relativePath of pathsToCheck) {
|
|
14144
|
-
const fullPath =
|
|
14413
|
+
const fullPath = join5(targetDir, relativePath);
|
|
14145
14414
|
if (await fileExists(fullPath)) {
|
|
14146
14415
|
existingPaths.push(relativePath);
|
|
14147
14416
|
}
|
|
@@ -14155,11 +14424,11 @@ async function backupExistingInstallation(targetDir) {
|
|
|
14155
14424
|
return [];
|
|
14156
14425
|
}
|
|
14157
14426
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
14158
|
-
const backupDir =
|
|
14427
|
+
const backupDir = join5(targetDir, `${layout.backupDirPrefix}${timestamp}`);
|
|
14159
14428
|
await ensureDirectory(backupDir);
|
|
14160
14429
|
const backedUpPaths = [];
|
|
14161
14430
|
for (const relativePath of existingPaths) {
|
|
14162
|
-
const fullPath =
|
|
14431
|
+
const fullPath = join5(targetDir, relativePath);
|
|
14163
14432
|
try {
|
|
14164
14433
|
const backupPath = await backupExisting(fullPath, backupDir);
|
|
14165
14434
|
backedUpPaths.push(backupPath);
|
|
@@ -14173,14 +14442,15 @@ async function backupExistingInstallation(targetDir) {
|
|
|
14173
14442
|
}
|
|
14174
14443
|
|
|
14175
14444
|
// src/core/mcp-config.ts
|
|
14445
|
+
init_fs();
|
|
14176
14446
|
import { execSync as execSync3 } from "node:child_process";
|
|
14177
14447
|
import { writeFile } from "node:fs/promises";
|
|
14178
|
-
import { join as
|
|
14448
|
+
import { join as join6 } from "node:path";
|
|
14179
14449
|
async function generateMCPConfig(targetDir) {
|
|
14180
14450
|
const layout = getProviderLayout();
|
|
14181
|
-
const mcpConfigPath =
|
|
14182
|
-
const ontologyDir =
|
|
14183
|
-
const ontologyExists = await fileExists(
|
|
14451
|
+
const mcpConfigPath = join6(targetDir, ".mcp.json");
|
|
14452
|
+
const ontologyDir = join6(layout.rootDir, "ontology");
|
|
14453
|
+
const ontologyExists = await fileExists(join6(targetDir, ontologyDir));
|
|
14184
14454
|
if (!ontologyExists) {
|
|
14185
14455
|
return;
|
|
14186
14456
|
}
|
|
@@ -14234,9 +14504,10 @@ async function checkUvAvailable() {
|
|
|
14234
14504
|
}
|
|
14235
14505
|
|
|
14236
14506
|
// src/cli/init.ts
|
|
14507
|
+
init_fs();
|
|
14237
14508
|
async function checkExistingInstallation(targetDir) {
|
|
14238
14509
|
const layout = getProviderLayout();
|
|
14239
|
-
const rootDir =
|
|
14510
|
+
const rootDir = join7(targetDir, layout.rootDir);
|
|
14240
14511
|
return fileExists(rootDir);
|
|
14241
14512
|
}
|
|
14242
14513
|
var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
@@ -14250,13 +14521,13 @@ var PROVIDER_SUBDIR_COMPONENTS = new Set([
|
|
|
14250
14521
|
function componentToPath(targetDir, component) {
|
|
14251
14522
|
if (component === "entry-md") {
|
|
14252
14523
|
const layout = getProviderLayout();
|
|
14253
|
-
return
|
|
14524
|
+
return join7(targetDir, layout.entryFile);
|
|
14254
14525
|
}
|
|
14255
14526
|
if (PROVIDER_SUBDIR_COMPONENTS.has(component)) {
|
|
14256
14527
|
const layout = getProviderLayout();
|
|
14257
|
-
return
|
|
14528
|
+
return join7(targetDir, layout.rootDir, component);
|
|
14258
14529
|
}
|
|
14259
|
-
return
|
|
14530
|
+
return join7(targetDir, component);
|
|
14260
14531
|
}
|
|
14261
14532
|
function buildInstalledPaths(targetDir, components) {
|
|
14262
14533
|
return components.map((component) => componentToPath(targetDir, component));
|
|
@@ -14336,7 +14607,8 @@ async function initCommand(options) {
|
|
|
14336
14607
|
}
|
|
14337
14608
|
|
|
14338
14609
|
// src/cli/list.ts
|
|
14339
|
-
import { basename as
|
|
14610
|
+
import { basename as basename3, dirname as dirname3, join as join8, relative as relative2 } from "node:path";
|
|
14611
|
+
init_fs();
|
|
14340
14612
|
var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
|
|
14341
14613
|
function parseKeyValue(line) {
|
|
14342
14614
|
const colonIndex = line.indexOf(":");
|
|
@@ -14382,7 +14654,7 @@ function parseYamlMetadata(content) {
|
|
|
14382
14654
|
return result;
|
|
14383
14655
|
}
|
|
14384
14656
|
function extractAgentTypeFromFilename(filename) {
|
|
14385
|
-
const name =
|
|
14657
|
+
const name = basename3(filename, ".md");
|
|
14386
14658
|
const prefixMap = {
|
|
14387
14659
|
lang: "language",
|
|
14388
14660
|
be: "backend",
|
|
@@ -14400,17 +14672,17 @@ function extractAgentTypeFromFilename(filename) {
|
|
|
14400
14672
|
return prefixMap[prefix] || "unknown";
|
|
14401
14673
|
}
|
|
14402
14674
|
function extractSkillCategoryFromPath(skillPath, baseDir, rootDir) {
|
|
14403
|
-
const relativePath = relative2(
|
|
14675
|
+
const relativePath = relative2(join8(baseDir, rootDir, "skills"), skillPath);
|
|
14404
14676
|
const parts = relativePath.split("/").filter(Boolean);
|
|
14405
14677
|
return parts[0] || "unknown";
|
|
14406
14678
|
}
|
|
14407
14679
|
function extractGuideCategoryFromPath(guidePath, baseDir) {
|
|
14408
|
-
const relativePath = relative2(
|
|
14680
|
+
const relativePath = relative2(join8(baseDir, "guides"), guidePath);
|
|
14409
14681
|
const parts = relativePath.split("/").filter(Boolean);
|
|
14410
14682
|
return parts[0] || "unknown";
|
|
14411
14683
|
}
|
|
14412
14684
|
function extractRulePriorityFromFilename(filename) {
|
|
14413
|
-
const name =
|
|
14685
|
+
const name = basename3(filename, ".md");
|
|
14414
14686
|
const parts = name.split("-");
|
|
14415
14687
|
return parts[0] || "unknown";
|
|
14416
14688
|
}
|
|
@@ -14499,7 +14771,7 @@ async function tryExtractMarkdownDescription(mdPath, options = {}) {
|
|
|
14499
14771
|
}
|
|
14500
14772
|
}
|
|
14501
14773
|
async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
14502
|
-
const agentsDir =
|
|
14774
|
+
const agentsDir = join8(targetDir, rootDir, "agents");
|
|
14503
14775
|
if (!await fileExists(agentsDir))
|
|
14504
14776
|
return [];
|
|
14505
14777
|
try {
|
|
@@ -14508,8 +14780,8 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
14508
14780
|
const customAgentPaths = new Set(customComponents.filter((c) => c.type === "agent").map((c) => c.path));
|
|
14509
14781
|
const agentMdFiles = await listFiles(agentsDir, { recursive: false, pattern: "*.md" });
|
|
14510
14782
|
const agents = await Promise.all(agentMdFiles.map(async (agentMdPath) => {
|
|
14511
|
-
const filename =
|
|
14512
|
-
const name =
|
|
14783
|
+
const filename = basename3(agentMdPath);
|
|
14784
|
+
const name = basename3(filename, ".md");
|
|
14513
14785
|
const description = await tryExtractMarkdownDescription(agentMdPath);
|
|
14514
14786
|
const relativePath = relative2(targetDir, agentMdPath);
|
|
14515
14787
|
return {
|
|
@@ -14527,7 +14799,7 @@ async function getAgents(targetDir, rootDir = ".claude", config) {
|
|
|
14527
14799
|
}
|
|
14528
14800
|
}
|
|
14529
14801
|
async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
14530
|
-
const skillsDir =
|
|
14802
|
+
const skillsDir = join8(targetDir, rootDir, "skills");
|
|
14531
14803
|
if (!await fileExists(skillsDir))
|
|
14532
14804
|
return [];
|
|
14533
14805
|
try {
|
|
@@ -14537,11 +14809,11 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
14537
14809
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
14538
14810
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
14539
14811
|
const skillDir = dirname3(skillMdPath);
|
|
14540
|
-
const indexYamlPath =
|
|
14812
|
+
const indexYamlPath = join8(skillDir, "index.yaml");
|
|
14541
14813
|
const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
|
|
14542
14814
|
const relativePath = relative2(targetDir, skillDir);
|
|
14543
14815
|
return {
|
|
14544
|
-
name:
|
|
14816
|
+
name: basename3(skillDir),
|
|
14545
14817
|
type: "skill",
|
|
14546
14818
|
category: extractSkillCategoryFromPath(skillDir, targetDir, rootDir),
|
|
14547
14819
|
path: relativePath,
|
|
@@ -14556,7 +14828,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
14556
14828
|
}
|
|
14557
14829
|
}
|
|
14558
14830
|
async function getGuides(targetDir, config) {
|
|
14559
|
-
const guidesDir =
|
|
14831
|
+
const guidesDir = join8(targetDir, "guides");
|
|
14560
14832
|
if (!await fileExists(guidesDir))
|
|
14561
14833
|
return [];
|
|
14562
14834
|
try {
|
|
@@ -14568,7 +14840,7 @@ async function getGuides(targetDir, config) {
|
|
|
14568
14840
|
const description = await tryExtractMarkdownDescription(guideMdPath, { maxLength: 100 });
|
|
14569
14841
|
const relativePath = relative2(targetDir, guideMdPath);
|
|
14570
14842
|
return {
|
|
14571
|
-
name:
|
|
14843
|
+
name: basename3(guideMdPath, ".md"),
|
|
14572
14844
|
type: "guide",
|
|
14573
14845
|
category: extractGuideCategoryFromPath(guideMdPath, targetDir),
|
|
14574
14846
|
path: relativePath,
|
|
@@ -14583,7 +14855,7 @@ async function getGuides(targetDir, config) {
|
|
|
14583
14855
|
}
|
|
14584
14856
|
var RULE_PRIORITY_ORDER = { MUST: 0, SHOULD: 1, MAY: 2 };
|
|
14585
14857
|
async function getRules(targetDir, rootDir = ".claude", config) {
|
|
14586
|
-
const rulesDir =
|
|
14858
|
+
const rulesDir = join8(targetDir, rootDir, "rules");
|
|
14587
14859
|
if (!await fileExists(rulesDir))
|
|
14588
14860
|
return [];
|
|
14589
14861
|
try {
|
|
@@ -14592,13 +14864,13 @@ async function getRules(targetDir, rootDir = ".claude", config) {
|
|
|
14592
14864
|
const customRulePaths = new Set(customComponents.filter((c) => c.type === "rule").map((c) => c.path));
|
|
14593
14865
|
const ruleMdFiles = await listFiles(rulesDir, { recursive: false, pattern: "*.md" });
|
|
14594
14866
|
const rules = await Promise.all(ruleMdFiles.map(async (ruleMdPath) => {
|
|
14595
|
-
const filename =
|
|
14867
|
+
const filename = basename3(ruleMdPath);
|
|
14596
14868
|
const description = await tryExtractMarkdownDescription(ruleMdPath, {
|
|
14597
14869
|
cleanFormatting: true
|
|
14598
14870
|
});
|
|
14599
14871
|
const relativePath = relative2(targetDir, ruleMdPath);
|
|
14600
14872
|
return {
|
|
14601
|
-
name:
|
|
14873
|
+
name: basename3(ruleMdPath, ".md"),
|
|
14602
14874
|
type: extractRulePriorityFromFilename(filename),
|
|
14603
14875
|
path: relativePath,
|
|
14604
14876
|
description,
|
|
@@ -14655,7 +14927,7 @@ function formatAsJson(components) {
|
|
|
14655
14927
|
console.log(JSON.stringify(components, null, 2));
|
|
14656
14928
|
}
|
|
14657
14929
|
async function getHooks(targetDir, rootDir = ".claude") {
|
|
14658
|
-
const hooksDir =
|
|
14930
|
+
const hooksDir = join8(targetDir, rootDir, "hooks");
|
|
14659
14931
|
if (!await fileExists(hooksDir))
|
|
14660
14932
|
return [];
|
|
14661
14933
|
try {
|
|
@@ -14664,7 +14936,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
14664
14936
|
const hookYamls = await listFiles(hooksDir, { recursive: true, pattern: "*.yaml" });
|
|
14665
14937
|
const allFiles = [...hookFiles, ...hookConfigs, ...hookYamls];
|
|
14666
14938
|
return allFiles.map((hookPath) => ({
|
|
14667
|
-
name:
|
|
14939
|
+
name: basename3(hookPath),
|
|
14668
14940
|
type: "hook",
|
|
14669
14941
|
path: relative2(targetDir, hookPath)
|
|
14670
14942
|
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -14673,7 +14945,7 @@ async function getHooks(targetDir, rootDir = ".claude") {
|
|
|
14673
14945
|
}
|
|
14674
14946
|
}
|
|
14675
14947
|
async function getContexts(targetDir, rootDir = ".claude") {
|
|
14676
|
-
const contextsDir =
|
|
14948
|
+
const contextsDir = join8(targetDir, rootDir, "contexts");
|
|
14677
14949
|
if (!await fileExists(contextsDir))
|
|
14678
14950
|
return [];
|
|
14679
14951
|
try {
|
|
@@ -14684,7 +14956,7 @@ async function getContexts(targetDir, rootDir = ".claude") {
|
|
|
14684
14956
|
const ext = ctxPath.endsWith(".md") ? ".md" : ".yaml";
|
|
14685
14957
|
const description = ext === ".md" ? await tryExtractMarkdownDescription(ctxPath, { maxLength: 100 }) : undefined;
|
|
14686
14958
|
return {
|
|
14687
|
-
name:
|
|
14959
|
+
name: basename3(ctxPath, ext),
|
|
14688
14960
|
type: "context",
|
|
14689
14961
|
path: relative2(targetDir, ctxPath),
|
|
14690
14962
|
description
|
|
@@ -15062,7 +15334,8 @@ async function securityCommand(_options = {}) {
|
|
|
15062
15334
|
}
|
|
15063
15335
|
|
|
15064
15336
|
// src/core/updater.ts
|
|
15065
|
-
|
|
15337
|
+
init_fs();
|
|
15338
|
+
import { join as join9 } from "node:path";
|
|
15066
15339
|
|
|
15067
15340
|
// src/core/entry-merger.ts
|
|
15068
15341
|
var MANAGED_START = "<!-- omcustom:start -->";
|
|
@@ -15311,7 +15584,7 @@ function resolveCustomizations(customizations, configPreserveFiles, targetDir) {
|
|
|
15311
15584
|
}
|
|
15312
15585
|
async function updateEntryDoc(targetDir, config, options) {
|
|
15313
15586
|
const layout = getProviderLayout();
|
|
15314
|
-
const entryPath =
|
|
15587
|
+
const entryPath = join9(targetDir, layout.entryFile);
|
|
15315
15588
|
const templateName = getEntryTemplateName2(config.language);
|
|
15316
15589
|
const templatePath = resolveTemplatePath(templateName);
|
|
15317
15590
|
if (!await fileExists(templatePath)) {
|
|
@@ -15445,7 +15718,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
15445
15718
|
const preservedFiles = [];
|
|
15446
15719
|
const componentPath = getComponentPath2(component);
|
|
15447
15720
|
const srcPath = resolveTemplatePath(componentPath);
|
|
15448
|
-
const destPath =
|
|
15721
|
+
const destPath = join9(targetDir, componentPath);
|
|
15449
15722
|
const customComponents = config.customComponents || [];
|
|
15450
15723
|
const skipPaths = [];
|
|
15451
15724
|
if (customizations && !options.forceOverwriteAll) {
|
|
@@ -15459,7 +15732,7 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
15459
15732
|
}
|
|
15460
15733
|
}
|
|
15461
15734
|
const path3 = await import("node:path");
|
|
15462
|
-
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath,
|
|
15735
|
+
const normalizedSkipPaths = skipPaths.map((p) => path3.relative(destPath, join9(targetDir, p)));
|
|
15463
15736
|
await copyDirectory(srcPath, destPath, {
|
|
15464
15737
|
overwrite: true,
|
|
15465
15738
|
skipPaths: normalizedSkipPaths.length > 0 ? normalizedSkipPaths : undefined
|
|
@@ -15479,12 +15752,12 @@ async function syncRootLevelFiles(targetDir, options) {
|
|
|
15479
15752
|
const layout = getProviderLayout();
|
|
15480
15753
|
const synced = [];
|
|
15481
15754
|
for (const fileName of ROOT_LEVEL_FILES) {
|
|
15482
|
-
const srcPath = resolveTemplatePath(
|
|
15755
|
+
const srcPath = resolveTemplatePath(join9(layout.rootDir, fileName));
|
|
15483
15756
|
if (!await fileExists(srcPath)) {
|
|
15484
15757
|
continue;
|
|
15485
15758
|
}
|
|
15486
|
-
const destPath =
|
|
15487
|
-
await ensureDirectory(
|
|
15759
|
+
const destPath = join9(targetDir, layout.rootDir, fileName);
|
|
15760
|
+
await ensureDirectory(join9(destPath, ".."));
|
|
15488
15761
|
await fs3.copyFile(srcPath, destPath);
|
|
15489
15762
|
if (fileName.endsWith(".sh")) {
|
|
15490
15763
|
await fs3.chmod(destPath, 493);
|
|
@@ -15519,7 +15792,7 @@ async function removeDeprecatedFiles(targetDir, options) {
|
|
|
15519
15792
|
});
|
|
15520
15793
|
continue;
|
|
15521
15794
|
}
|
|
15522
|
-
const fullPath =
|
|
15795
|
+
const fullPath = join9(targetDir, entry.path);
|
|
15523
15796
|
if (await fileExists(fullPath)) {
|
|
15524
15797
|
await fs3.unlink(fullPath);
|
|
15525
15798
|
removed.push(entry.path);
|
|
@@ -15543,26 +15816,26 @@ function getComponentPath2(component) {
|
|
|
15543
15816
|
}
|
|
15544
15817
|
async function backupInstallation(targetDir) {
|
|
15545
15818
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
15546
|
-
const backupDir =
|
|
15819
|
+
const backupDir = join9(targetDir, `.omcustom-backup-${timestamp}`);
|
|
15547
15820
|
const fs3 = await import("node:fs/promises");
|
|
15548
15821
|
await ensureDirectory(backupDir);
|
|
15549
15822
|
const layout = getProviderLayout();
|
|
15550
15823
|
const dirsToBackup = [layout.rootDir, "guides"];
|
|
15551
15824
|
for (const dir2 of dirsToBackup) {
|
|
15552
|
-
const srcPath =
|
|
15825
|
+
const srcPath = join9(targetDir, dir2);
|
|
15553
15826
|
if (await fileExists(srcPath)) {
|
|
15554
|
-
const destPath =
|
|
15827
|
+
const destPath = join9(backupDir, dir2);
|
|
15555
15828
|
await copyDirectory(srcPath, destPath, { overwrite: true });
|
|
15556
15829
|
}
|
|
15557
15830
|
}
|
|
15558
|
-
const entryPath =
|
|
15831
|
+
const entryPath = join9(targetDir, layout.entryFile);
|
|
15559
15832
|
if (await fileExists(entryPath)) {
|
|
15560
|
-
await fs3.copyFile(entryPath,
|
|
15833
|
+
await fs3.copyFile(entryPath, join9(backupDir, layout.entryFile));
|
|
15561
15834
|
}
|
|
15562
15835
|
return backupDir;
|
|
15563
15836
|
}
|
|
15564
15837
|
async function loadCustomizationManifest(targetDir) {
|
|
15565
|
-
const manifestPath =
|
|
15838
|
+
const manifestPath = join9(targetDir, CUSTOMIZATION_MANIFEST_FILE);
|
|
15566
15839
|
if (await fileExists(manifestPath)) {
|
|
15567
15840
|
return readJsonFile(manifestPath);
|
|
15568
15841
|
}
|