aiblueprint-cli 1.4.73 → 1.4.75
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 +7 -0
- package/agents-config/skills/use-style/SKILL.md +54 -0
- package/agents-config/skills/use-style/examples/anthropic.html +400 -0
- package/agents-config/skills/use-style/examples/dusk.html +368 -0
- package/agents-config/skills/use-style/examples/gumroad.html +396 -0
- package/agents-config/skills/use-style/examples/linear.html +389 -0
- package/agents-config/skills/use-style/examples/new-york-times.html +417 -0
- package/agents-config/skills/use-style/examples/raycast.html +402 -0
- package/agents-config/skills/use-style/examples/split-auth.html +366 -0
- package/agents-config/skills/use-style/examples/stripe.html +386 -0
- package/agents-config/skills/use-style/examples/vercel.html +379 -0
- package/agents-config/skills/use-style/styles/anthropic.md +266 -0
- package/agents-config/skills/use-style/styles/dusk.md +269 -0
- package/agents-config/skills/use-style/styles/grid.md +284 -0
- package/agents-config/skills/use-style/styles/gumroad.md +273 -0
- package/agents-config/skills/use-style/styles/linear.md +355 -0
- package/agents-config/skills/use-style/styles/new-york-times.md +277 -0
- package/agents-config/skills/use-style/styles/raycast.md +285 -0
- package/agents-config/skills/use-style/styles/split-auth.md +281 -0
- package/agents-config/skills/use-style/styles/stripe.md +372 -0
- package/agents-config/skills/use-style/styles/vercel-simple.md +322 -0
- package/dist/cli.js +513 -42
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33628,6 +33628,8 @@ async function mergeCodexConfigFile(sourceConfigPath, codexDir) {
|
|
|
33628
33628
|
// src/lib/configs-store.ts
|
|
33629
33629
|
var import_fs_extra7 = __toESM(require_lib4(), 1);
|
|
33630
33630
|
import path11 from "path";
|
|
33631
|
+
var RETENTION_MANAGED_BACKUP_TRIGGERS = new Set(["setup", "sync", "load", "backup-load"]);
|
|
33632
|
+
var DEFAULT_BACKUP_RETENTION_DAYS = 30;
|
|
33631
33633
|
function getConfigStorePaths(rootDir) {
|
|
33632
33634
|
const baseDir = path11.join(rootDir, ".aiblueprint");
|
|
33633
33635
|
return {
|
|
@@ -33866,6 +33868,117 @@ async function listConfigBackups(options = {}) {
|
|
|
33866
33868
|
const paths = getConfigStorePaths(folders.rootDir);
|
|
33867
33869
|
return listSnapshots(paths.backupsDir, "backup");
|
|
33868
33870
|
}
|
|
33871
|
+
function normalizeBackupRetentionDays(value = DEFAULT_BACKUP_RETENTION_DAYS) {
|
|
33872
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
33873
|
+
throw new Error("Backup retention days must be a positive integer.");
|
|
33874
|
+
}
|
|
33875
|
+
return value;
|
|
33876
|
+
}
|
|
33877
|
+
function isRetentionManagedBackup(metadata, includeManual = false) {
|
|
33878
|
+
return RETENTION_MANAGED_BACKUP_TRIGGERS.has(metadata.trigger) || includeManual && metadata.trigger === "manual-backup";
|
|
33879
|
+
}
|
|
33880
|
+
function isPathInside(childPath, parentPath) {
|
|
33881
|
+
const relativePath = path11.relative(parentPath, childPath);
|
|
33882
|
+
return relativePath !== "" && !relativePath.startsWith("..") && !path11.isAbsolute(relativePath);
|
|
33883
|
+
}
|
|
33884
|
+
async function realBackupStorePath(backupsDir) {
|
|
33885
|
+
if (!await import_fs_extra7.default.pathExists(backupsDir))
|
|
33886
|
+
return null;
|
|
33887
|
+
const stats = await import_fs_extra7.default.lstat(backupsDir);
|
|
33888
|
+
if (stats.isSymbolicLink()) {
|
|
33889
|
+
throw new Error("Backup directory cannot be a symlink.");
|
|
33890
|
+
}
|
|
33891
|
+
if (!stats.isDirectory()) {
|
|
33892
|
+
throw new Error("Backup path is not a directory.");
|
|
33893
|
+
}
|
|
33894
|
+
return import_fs_extra7.default.realpath(backupsDir);
|
|
33895
|
+
}
|
|
33896
|
+
function cleanupDateForSnapshot(snapshot) {
|
|
33897
|
+
if (snapshot.metadata.type !== "backup")
|
|
33898
|
+
return null;
|
|
33899
|
+
const createdAt = new Date(snapshot.metadata.createdAt);
|
|
33900
|
+
if (Number.isNaN(createdAt.getTime()))
|
|
33901
|
+
return null;
|
|
33902
|
+
return createdAt;
|
|
33903
|
+
}
|
|
33904
|
+
async function removeBackupSnapshot(snapshot, backupsRealPath) {
|
|
33905
|
+
const snapshotRealPath = await import_fs_extra7.default.realpath(snapshot.path);
|
|
33906
|
+
if (!isPathInside(snapshotRealPath, backupsRealPath)) {
|
|
33907
|
+
throw new Error(`Refusing to delete backup outside store: ${snapshot.name}`);
|
|
33908
|
+
}
|
|
33909
|
+
await import_fs_extra7.default.remove(snapshot.path);
|
|
33910
|
+
}
|
|
33911
|
+
async function cleanConfigBackups(options = {}) {
|
|
33912
|
+
const days = normalizeBackupRetentionDays(options.days);
|
|
33913
|
+
const folders = resolveConfigStoreFolders(options);
|
|
33914
|
+
const paths = getConfigStorePaths(folders.rootDir);
|
|
33915
|
+
const backupsRealPath = await realBackupStorePath(paths.backupsDir);
|
|
33916
|
+
const now = options.now ?? new Date;
|
|
33917
|
+
const cutoffDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
|
|
33918
|
+
if (!backupsRealPath) {
|
|
33919
|
+
return {
|
|
33920
|
+
cutoff: cutoffDate.toISOString(),
|
|
33921
|
+
deleted: [],
|
|
33922
|
+
failed: [],
|
|
33923
|
+
kept: [],
|
|
33924
|
+
skipped: []
|
|
33925
|
+
};
|
|
33926
|
+
}
|
|
33927
|
+
const backups = await listSnapshots(paths.backupsDir, "backup");
|
|
33928
|
+
const deleted = [];
|
|
33929
|
+
const failed = [];
|
|
33930
|
+
const kept = [];
|
|
33931
|
+
const skipped = [];
|
|
33932
|
+
const deletionCandidates = [];
|
|
33933
|
+
for (const backup of backups) {
|
|
33934
|
+
if (!isRetentionManagedBackup(backup.metadata, options.includeManual)) {
|
|
33935
|
+
skipped.push({ snapshot: backup, reason: "not retention-managed" });
|
|
33936
|
+
continue;
|
|
33937
|
+
}
|
|
33938
|
+
const cleanupDate = cleanupDateForSnapshot(backup);
|
|
33939
|
+
if (!cleanupDate) {
|
|
33940
|
+
skipped.push({ snapshot: backup, reason: "missing valid backup metadata date" });
|
|
33941
|
+
continue;
|
|
33942
|
+
}
|
|
33943
|
+
if (cleanupDate < cutoffDate) {
|
|
33944
|
+
deletionCandidates.push(backup);
|
|
33945
|
+
} else {
|
|
33946
|
+
kept.push(backup);
|
|
33947
|
+
}
|
|
33948
|
+
}
|
|
33949
|
+
if (options.dryRun) {
|
|
33950
|
+
deleted.push(...deletionCandidates);
|
|
33951
|
+
} else {
|
|
33952
|
+
for (const backup of deletionCandidates) {
|
|
33953
|
+
try {
|
|
33954
|
+
await removeBackupSnapshot(backup, backupsRealPath);
|
|
33955
|
+
deleted.push(backup);
|
|
33956
|
+
} catch (error) {
|
|
33957
|
+
failed.push({
|
|
33958
|
+
snapshot: backup,
|
|
33959
|
+
error: error instanceof Error ? error.message : String(error)
|
|
33960
|
+
});
|
|
33961
|
+
}
|
|
33962
|
+
}
|
|
33963
|
+
if (deleted.length > 0 || failed.length > 0) {
|
|
33964
|
+
await appendHistory(paths, {
|
|
33965
|
+
action: "backup-clean",
|
|
33966
|
+
days,
|
|
33967
|
+
cutoff: cutoffDate.toISOString(),
|
|
33968
|
+
deleted: deleted.map((backup) => backup.name),
|
|
33969
|
+
failed: failed.map((entry) => ({ name: entry.snapshot.name, error: entry.error })),
|
|
33970
|
+
skipped: skipped.map((entry) => ({ name: entry.snapshot.name, reason: entry.reason }))
|
|
33971
|
+
});
|
|
33972
|
+
}
|
|
33973
|
+
}
|
|
33974
|
+
return {
|
|
33975
|
+
cutoff: cutoffDate.toISOString(),
|
|
33976
|
+
deleted,
|
|
33977
|
+
failed,
|
|
33978
|
+
kept,
|
|
33979
|
+
skipped
|
|
33980
|
+
};
|
|
33981
|
+
}
|
|
33869
33982
|
|
|
33870
33983
|
// src/lib/codex-agents-renderer.ts
|
|
33871
33984
|
var import_fs_extra8 = __toESM(require_lib4(), 1);
|
|
@@ -34083,6 +34196,17 @@ async function resolveClaudeAssetPath(sourceDir, name) {
|
|
|
34083
34196
|
}
|
|
34084
34197
|
return null;
|
|
34085
34198
|
}
|
|
34199
|
+
async function resolveDirectoryCopyDestination(destination) {
|
|
34200
|
+
const stat = await import_fs_extra9.default.lstat(destination).catch(() => null);
|
|
34201
|
+
if (!stat?.isSymbolicLink())
|
|
34202
|
+
return destination;
|
|
34203
|
+
const targetStat = await import_fs_extra9.default.stat(destination).catch(() => null);
|
|
34204
|
+
if (targetStat?.isDirectory()) {
|
|
34205
|
+
return import_fs_extra9.default.realpath(destination);
|
|
34206
|
+
}
|
|
34207
|
+
await import_fs_extra9.default.remove(destination);
|
|
34208
|
+
return destination;
|
|
34209
|
+
}
|
|
34086
34210
|
async function setupCommand(params = {}) {
|
|
34087
34211
|
const {
|
|
34088
34212
|
folder,
|
|
@@ -34195,11 +34319,10 @@ async function setupCommand(params = {}) {
|
|
|
34195
34319
|
s.start("Setting up scripts");
|
|
34196
34320
|
const scriptsSource = await resolveClaudeAssetPath(sourceDir, "scripts");
|
|
34197
34321
|
if (scriptsSource) {
|
|
34198
|
-
|
|
34199
|
-
|
|
34200
|
-
|
|
34201
|
-
await
|
|
34202
|
-
await import_fs_extra9.default.ensureDir(path13.join(claudeDir, "scripts/statusline/data"));
|
|
34322
|
+
const scriptsDestination = path13.join(claudeDir, "scripts");
|
|
34323
|
+
await import_fs_extra9.default.copy(scriptsSource, await resolveDirectoryCopyDestination(scriptsDestination), { overwrite: true });
|
|
34324
|
+
await replacePathPlaceholdersInDir(scriptsDestination, claudeDir);
|
|
34325
|
+
await import_fs_extra9.default.ensureDir(path13.join(scriptsDestination, "statusline/data"));
|
|
34203
34326
|
s.stop("Scripts installed");
|
|
34204
34327
|
} else {
|
|
34205
34328
|
s.stop("Scripts not available in repository");
|
|
@@ -35738,6 +35861,7 @@ var IGNORED_ENTRY_NAMES2 = new Set([
|
|
|
35738
35861
|
".git",
|
|
35739
35862
|
"node_modules"
|
|
35740
35863
|
]);
|
|
35864
|
+
var RULE_EXTENSION_RENAME_REASON = "Rule file extension normalized from .mdc to .md";
|
|
35741
35865
|
function uniqueByPath(candidates) {
|
|
35742
35866
|
const seen = new Set;
|
|
35743
35867
|
const unique = [];
|
|
@@ -35847,8 +35971,10 @@ function getContainerCandidates(options, includeCodex = true) {
|
|
|
35847
35971
|
}
|
|
35848
35972
|
]);
|
|
35849
35973
|
}
|
|
35850
|
-
function getInstructionFileCandidates(options,
|
|
35974
|
+
function getInstructionFileCandidates(options, scope) {
|
|
35851
35975
|
const folders = resolveFolders(options);
|
|
35976
|
+
const cursorDir = path17.join(folders.rootDir, ".cursor");
|
|
35977
|
+
const linkToolInstructionsWhenMissing = scope === "global";
|
|
35852
35978
|
return uniqueByPath([
|
|
35853
35979
|
{
|
|
35854
35980
|
label: "agents-instructions",
|
|
@@ -35860,11 +35986,17 @@ function getInstructionFileCandidates(options, includeCodex = true) {
|
|
|
35860
35986
|
path: path17.join(folders.claudeDir, "CLAUDE.md"),
|
|
35861
35987
|
linkWhenMissing: true
|
|
35862
35988
|
},
|
|
35863
|
-
|
|
35989
|
+
{
|
|
35864
35990
|
label: "codex-instructions",
|
|
35865
35991
|
path: path17.join(folders.codexDir, "AGENTS.md"),
|
|
35866
|
-
linkWhenMissing:
|
|
35867
|
-
|
|
35992
|
+
linkWhenMissing: linkToolInstructionsWhenMissing,
|
|
35993
|
+
linkWhenParentExists: true
|
|
35994
|
+
},
|
|
35995
|
+
{
|
|
35996
|
+
label: "cursor-instructions",
|
|
35997
|
+
path: path17.join(cursorDir, "AGENTS.md"),
|
|
35998
|
+
linkWhenParentExists: true
|
|
35999
|
+
}
|
|
35868
36000
|
]);
|
|
35869
36001
|
}
|
|
35870
36002
|
function getRepositoryContainerCandidates(options) {
|
|
@@ -35971,6 +36103,60 @@ async function hashPath(targetPath) {
|
|
|
35971
36103
|
}
|
|
35972
36104
|
return hashString(`other:${stat.mode}:${stat.size}`);
|
|
35973
36105
|
}
|
|
36106
|
+
async function hashRulePath(targetPath) {
|
|
36107
|
+
const stat = await import_fs_extra13.default.lstat(targetPath);
|
|
36108
|
+
if (stat.isSymbolicLink()) {
|
|
36109
|
+
const linkTarget = await import_fs_extra13.default.readlink(targetPath);
|
|
36110
|
+
return hashString(`symlink:${linkTarget}`);
|
|
36111
|
+
}
|
|
36112
|
+
if (stat.isFile()) {
|
|
36113
|
+
const fileHash = crypto.createHash("sha256");
|
|
36114
|
+
fileHash.update("file:");
|
|
36115
|
+
fileHash.update(await import_fs_extra13.default.readFile(targetPath));
|
|
36116
|
+
return fileHash.digest("hex");
|
|
36117
|
+
}
|
|
36118
|
+
if (stat.isDirectory()) {
|
|
36119
|
+
const entries = (await import_fs_extra13.default.readdir(targetPath, { withFileTypes: true })).filter((entry) => !IGNORED_ENTRY_NAMES2.has(entry.name)).sort(compareRuleEntryNames);
|
|
36120
|
+
const canonicalEntries = [];
|
|
36121
|
+
const usedNames = new Set;
|
|
36122
|
+
const hashesByName = new Map;
|
|
36123
|
+
const dirHash = crypto.createHash("sha256");
|
|
36124
|
+
dirHash.update("dir:");
|
|
36125
|
+
for (const entry of entries) {
|
|
36126
|
+
const entryHash = await hashRulePath(path17.join(targetPath, entry.name));
|
|
36127
|
+
let entryName = entry.isDirectory() ? entry.name : normalizeRuleFileName(entry.name);
|
|
36128
|
+
const existingHash = hashesByName.get(entryName);
|
|
36129
|
+
if (existingHash) {
|
|
36130
|
+
if (existingHash === entryHash)
|
|
36131
|
+
continue;
|
|
36132
|
+
entryName = findAvailableRuleName(entryName, usedNames);
|
|
36133
|
+
}
|
|
36134
|
+
usedNames.add(entryName);
|
|
36135
|
+
hashesByName.set(entryName, entryHash);
|
|
36136
|
+
canonicalEntries.push({ name: entryName, hash: entryHash });
|
|
36137
|
+
}
|
|
36138
|
+
for (const entry of canonicalEntries.sort((a, b3) => a.name.localeCompare(b3.name))) {
|
|
36139
|
+
dirHash.update(entry.name);
|
|
36140
|
+
dirHash.update("\x00");
|
|
36141
|
+
dirHash.update(entry.hash);
|
|
36142
|
+
dirHash.update("\x00");
|
|
36143
|
+
}
|
|
36144
|
+
return dirHash.digest("hex");
|
|
36145
|
+
}
|
|
36146
|
+
return hashString(`other:${stat.mode}:${stat.size}`);
|
|
36147
|
+
}
|
|
36148
|
+
async function hashPathForCategory(category, targetPath) {
|
|
36149
|
+
return category === "rules" ? hashRulePath(targetPath) : hashPath(targetPath);
|
|
36150
|
+
}
|
|
36151
|
+
function findAvailableRuleName(originalName, usedNames) {
|
|
36152
|
+
let index = 1;
|
|
36153
|
+
while (true) {
|
|
36154
|
+
const candidate = nameWithSuffix(originalName, "agents-rules", index);
|
|
36155
|
+
if (!usedNames.has(candidate))
|
|
36156
|
+
return candidate;
|
|
36157
|
+
index++;
|
|
36158
|
+
}
|
|
36159
|
+
}
|
|
35974
36160
|
var TEXT_EXTENSIONS = new Set([
|
|
35975
36161
|
".cjs",
|
|
35976
36162
|
".js",
|
|
@@ -36049,15 +36235,40 @@ function nameWithSuffix(name, suffix, index) {
|
|
|
36049
36235
|
}
|
|
36050
36236
|
return `${name}--${numberedSuffix}`;
|
|
36051
36237
|
}
|
|
36052
|
-
|
|
36238
|
+
function normalizeRuleFileName(name) {
|
|
36239
|
+
return path17.extname(name).toLowerCase() === ".mdc" ? `${path17.basename(name, path17.extname(name))}.md` : name;
|
|
36240
|
+
}
|
|
36241
|
+
function ruleExtensionPriority(name) {
|
|
36242
|
+
const ext = path17.extname(name).toLowerCase();
|
|
36243
|
+
if (ext === ".md")
|
|
36244
|
+
return 0;
|
|
36245
|
+
if (ext === ".mdc")
|
|
36246
|
+
return 1;
|
|
36247
|
+
return 2;
|
|
36248
|
+
}
|
|
36249
|
+
function compareRuleEntryNames(a, b3) {
|
|
36250
|
+
const normalizedCompare = normalizeRuleFileName(a.name).localeCompare(normalizeRuleFileName(b3.name));
|
|
36251
|
+
if (normalizedCompare !== 0)
|
|
36252
|
+
return normalizedCompare;
|
|
36253
|
+
const priorityCompare = ruleExtensionPriority(a.name) - ruleExtensionPriority(b3.name);
|
|
36254
|
+
if (priorityCompare !== 0)
|
|
36255
|
+
return priorityCompare;
|
|
36256
|
+
return a.name.localeCompare(b3.name);
|
|
36257
|
+
}
|
|
36258
|
+
async function addExistingDestinationHashes(category, destinationDir, knownHashes, result) {
|
|
36053
36259
|
if (!await import_fs_extra13.default.pathExists(destinationDir))
|
|
36054
36260
|
return;
|
|
36261
|
+
const plannedRuleNames = new Map(result.renamed.filter((entry) => entry.category === "rules").map((entry) => [path17.resolve(entry.from), path17.basename(entry.to)]));
|
|
36262
|
+
const removedRulePaths = new Set(result.duplicates.filter((entry) => entry.category === "rules").map((entry) => path17.resolve(entry.from)));
|
|
36055
36263
|
const entries = await import_fs_extra13.default.readdir(destinationDir, { withFileTypes: true });
|
|
36056
36264
|
for (const entry of entries) {
|
|
36057
36265
|
if (IGNORED_ENTRY_NAMES2.has(entry.name))
|
|
36058
36266
|
continue;
|
|
36059
36267
|
const entryPath = path17.join(destinationDir, entry.name);
|
|
36060
|
-
|
|
36268
|
+
if (category === "rules" && removedRulePaths.has(path17.resolve(entryPath)))
|
|
36269
|
+
continue;
|
|
36270
|
+
const entryName = category === "rules" ? plannedRuleNames.get(path17.resolve(entryPath)) ?? normalizeRuleFileName(entry.name) : entry.name;
|
|
36271
|
+
knownHashes.set(await hashPathForCategory(category, entryPath), entryName);
|
|
36061
36272
|
}
|
|
36062
36273
|
}
|
|
36063
36274
|
async function importCategoryEntries(category, candidates, destinationDir, result, dryRun = false) {
|
|
@@ -36074,7 +36285,7 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
|
|
|
36074
36285
|
if (destinationRealPath && candidateRealPath && samePath(destinationRealPath, candidateRealPath)) {
|
|
36075
36286
|
continue;
|
|
36076
36287
|
}
|
|
36077
|
-
const entries = await collectCandidateEntries(candidate).catch(() => null);
|
|
36288
|
+
const entries = await collectCandidateEntries(candidate, category).catch(() => null);
|
|
36078
36289
|
if (!entries) {
|
|
36079
36290
|
result.skipped.push({
|
|
36080
36291
|
category,
|
|
@@ -36100,12 +36311,12 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
|
|
|
36100
36311
|
await import_fs_extra13.default.ensureDir(destinationDir);
|
|
36101
36312
|
}
|
|
36102
36313
|
const knownHashes = new Map;
|
|
36103
|
-
await addExistingDestinationHashes(destinationDir, knownHashes);
|
|
36314
|
+
await addExistingDestinationHashes(category, destinationDir, knownHashes, result);
|
|
36104
36315
|
const knownNames = new Set(knownHashes.values());
|
|
36105
36316
|
for (const { candidate, entries } of sourceEntries) {
|
|
36106
36317
|
for (const entry of entries) {
|
|
36107
36318
|
const sourcePath = entry.path;
|
|
36108
|
-
const sourceHash = await
|
|
36319
|
+
const sourceHash = await hashPathForCategory(category, sourcePath);
|
|
36109
36320
|
const existingName = knownHashes.get(sourceHash);
|
|
36110
36321
|
if (existingName) {
|
|
36111
36322
|
result.duplicates.push({
|
|
@@ -36116,10 +36327,10 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
|
|
|
36116
36327
|
});
|
|
36117
36328
|
continue;
|
|
36118
36329
|
}
|
|
36119
|
-
let targetName = entry.name;
|
|
36330
|
+
let targetName = category === "rules" ? normalizeRuleFileName(entry.name) : entry.name;
|
|
36120
36331
|
let targetPath = path17.join(destinationDir, targetName);
|
|
36121
36332
|
if (await pathExistsOrSymlink(targetPath) || knownNames.has(targetName)) {
|
|
36122
|
-
targetName = await findAvailableTargetName(destinationDir,
|
|
36333
|
+
targetName = await findAvailableTargetName(destinationDir, targetName, candidate.label, knownNames);
|
|
36123
36334
|
targetPath = path17.join(destinationDir, targetName);
|
|
36124
36335
|
result.renamed.push({
|
|
36125
36336
|
category,
|
|
@@ -36137,7 +36348,7 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
|
|
|
36137
36348
|
await normalizePortableContent(targetPath);
|
|
36138
36349
|
}
|
|
36139
36350
|
knownNames.add(targetName);
|
|
36140
|
-
knownHashes.set(dryRun ? sourceHash : await
|
|
36351
|
+
knownHashes.set(dryRun ? sourceHash : await hashPathForCategory(category, targetPath), targetName);
|
|
36141
36352
|
result.imported.push({
|
|
36142
36353
|
category,
|
|
36143
36354
|
name: entry.name,
|
|
@@ -36148,6 +36359,84 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
|
|
|
36148
36359
|
}
|
|
36149
36360
|
return true;
|
|
36150
36361
|
}
|
|
36362
|
+
function recordRuleExtensionRename(result, from3, to) {
|
|
36363
|
+
result.renamed.push({
|
|
36364
|
+
category: "rules",
|
|
36365
|
+
name: path17.basename(from3),
|
|
36366
|
+
from: from3,
|
|
36367
|
+
to,
|
|
36368
|
+
reason: RULE_EXTENSION_RENAME_REASON
|
|
36369
|
+
});
|
|
36370
|
+
}
|
|
36371
|
+
async function migrateExistingRuleExtensions(rulesDir, result, dryRun = false) {
|
|
36372
|
+
const usedNamesByDir = new Map;
|
|
36373
|
+
async function usedNamesForDir(currentDir) {
|
|
36374
|
+
const existing = usedNamesByDir.get(currentDir);
|
|
36375
|
+
if (existing)
|
|
36376
|
+
return existing;
|
|
36377
|
+
const names = new Set(await import_fs_extra13.default.readdir(currentDir).catch(() => []));
|
|
36378
|
+
usedNamesByDir.set(currentDir, names);
|
|
36379
|
+
return names;
|
|
36380
|
+
}
|
|
36381
|
+
async function walk(currentDir) {
|
|
36382
|
+
const entries = sortSourceEntries(await import_fs_extra13.default.readdir(currentDir, { withFileTypes: true }).catch(() => []), "rules");
|
|
36383
|
+
for (const entry of entries) {
|
|
36384
|
+
if (IGNORED_ENTRY_NAMES2.has(entry.name))
|
|
36385
|
+
continue;
|
|
36386
|
+
const sourcePath = path17.join(currentDir, entry.name);
|
|
36387
|
+
if (entry.isDirectory()) {
|
|
36388
|
+
await walk(sourcePath);
|
|
36389
|
+
continue;
|
|
36390
|
+
}
|
|
36391
|
+
if (!entry.isFile() && !entry.isSymbolicLink())
|
|
36392
|
+
continue;
|
|
36393
|
+
if (path17.extname(entry.name).toLowerCase() !== ".mdc")
|
|
36394
|
+
continue;
|
|
36395
|
+
const targetPath = path17.join(currentDir, normalizeRuleFileName(entry.name));
|
|
36396
|
+
if (samePath(sourcePath, targetPath))
|
|
36397
|
+
continue;
|
|
36398
|
+
if (await pathExistsOrSymlink(targetPath)) {
|
|
36399
|
+
const sourceHash = await hashPath(sourcePath);
|
|
36400
|
+
const targetHash = await hashPath(targetPath);
|
|
36401
|
+
if (sourceHash === targetHash) {
|
|
36402
|
+
const backupRoot = await ensureBackupPath(result, dryRun);
|
|
36403
|
+
const backupTarget = path17.join(backupRoot, safeRelativePath(result.rootDir, sourcePath));
|
|
36404
|
+
if (!dryRun) {
|
|
36405
|
+
await import_fs_extra13.default.ensureDir(path17.dirname(backupTarget));
|
|
36406
|
+
await import_fs_extra13.default.move(sourcePath, backupTarget, { overwrite: false });
|
|
36407
|
+
}
|
|
36408
|
+
result.duplicates.push({
|
|
36409
|
+
category: "rules",
|
|
36410
|
+
name: entry.name,
|
|
36411
|
+
from: sourcePath,
|
|
36412
|
+
keptAs: targetPath
|
|
36413
|
+
});
|
|
36414
|
+
const usedNames3 = await usedNamesForDir(currentDir);
|
|
36415
|
+
usedNames3.delete(entry.name);
|
|
36416
|
+
continue;
|
|
36417
|
+
}
|
|
36418
|
+
const usedNames2 = await usedNamesForDir(currentDir);
|
|
36419
|
+
const targetName = await findAvailableTargetName(currentDir, path17.basename(targetPath), "agents-rules", usedNames2);
|
|
36420
|
+
const renamedTargetPath = path17.join(currentDir, targetName);
|
|
36421
|
+
if (!dryRun) {
|
|
36422
|
+
await import_fs_extra13.default.move(sourcePath, renamedTargetPath, { overwrite: false });
|
|
36423
|
+
}
|
|
36424
|
+
usedNames2.delete(entry.name);
|
|
36425
|
+
usedNames2.add(targetName);
|
|
36426
|
+
recordRuleExtensionRename(result, sourcePath, renamedTargetPath);
|
|
36427
|
+
continue;
|
|
36428
|
+
}
|
|
36429
|
+
const usedNames = await usedNamesForDir(currentDir);
|
|
36430
|
+
if (!dryRun) {
|
|
36431
|
+
await import_fs_extra13.default.move(sourcePath, targetPath, { overwrite: false });
|
|
36432
|
+
}
|
|
36433
|
+
usedNames.delete(entry.name);
|
|
36434
|
+
usedNames.add(path17.basename(targetPath));
|
|
36435
|
+
recordRuleExtensionRename(result, sourcePath, targetPath);
|
|
36436
|
+
}
|
|
36437
|
+
}
|
|
36438
|
+
await walk(rulesDir);
|
|
36439
|
+
}
|
|
36151
36440
|
async function findAvailableTargetName(destinationDir, originalName, label, knownNames) {
|
|
36152
36441
|
const suffix = suffixFromLabel(label);
|
|
36153
36442
|
let index = 1;
|
|
@@ -36159,11 +36448,18 @@ async function findAvailableTargetName(destinationDir, originalName, label, know
|
|
|
36159
36448
|
index++;
|
|
36160
36449
|
}
|
|
36161
36450
|
}
|
|
36162
|
-
|
|
36451
|
+
function sortSourceEntries(entries, category) {
|
|
36452
|
+
return [...entries].sort((a, b3) => {
|
|
36453
|
+
if (category === "rules")
|
|
36454
|
+
return compareRuleEntryNames(a, b3);
|
|
36455
|
+
return a.name.localeCompare(b3.name);
|
|
36456
|
+
});
|
|
36457
|
+
}
|
|
36458
|
+
async function collectCandidateEntries(candidate, category) {
|
|
36163
36459
|
const stat = await import_fs_extra13.default.lstat(candidate.path);
|
|
36164
36460
|
if (stat.isDirectory()) {
|
|
36165
36461
|
const entries = await import_fs_extra13.default.readdir(candidate.path, { withFileTypes: true });
|
|
36166
|
-
return entries.map((entry) => ({
|
|
36462
|
+
return sortSourceEntries(entries, category).map((entry) => ({
|
|
36167
36463
|
name: entry.name,
|
|
36168
36464
|
path: path17.join(candidate.path, entry.name)
|
|
36169
36465
|
}));
|
|
@@ -36172,7 +36468,7 @@ async function collectCandidateEntries(candidate) {
|
|
|
36172
36468
|
const targetStat = await import_fs_extra13.default.stat(candidate.path).catch(() => null);
|
|
36173
36469
|
if (targetStat?.isDirectory()) {
|
|
36174
36470
|
const entries = await import_fs_extra13.default.readdir(candidate.path, { withFileTypes: true });
|
|
36175
|
-
return entries.map((entry) => ({
|
|
36471
|
+
return sortSourceEntries(entries, category).map((entry) => ({
|
|
36176
36472
|
name: entry.name,
|
|
36177
36473
|
path: path17.join(candidate.path, entry.name)
|
|
36178
36474
|
}));
|
|
@@ -36368,6 +36664,8 @@ async function importInstructionFiles(candidates, destinationPath, result, dryRu
|
|
|
36368
36664
|
async function shouldLinkMissingInstruction(candidate) {
|
|
36369
36665
|
if (candidate.linkWhenMissing)
|
|
36370
36666
|
return true;
|
|
36667
|
+
if (candidate.linkWhenParentExists)
|
|
36668
|
+
return import_fs_extra13.default.pathExists(path17.dirname(candidate.path));
|
|
36371
36669
|
return false;
|
|
36372
36670
|
}
|
|
36373
36671
|
async function linkInstructionFile(candidate, destinationPath, result, dryRun = false) {
|
|
@@ -36450,7 +36748,7 @@ async function collectRuleIndexEntries(rulesDir, rootDir, currentDir = rulesDir)
|
|
|
36450
36748
|
}
|
|
36451
36749
|
if (!entry.isFile() && !entry.isSymbolicLink())
|
|
36452
36750
|
continue;
|
|
36453
|
-
if (
|
|
36751
|
+
if (path17.extname(entry.name).toLowerCase() !== ".md")
|
|
36454
36752
|
continue;
|
|
36455
36753
|
rules.push({
|
|
36456
36754
|
title: await readRuleTitle(entryPath),
|
|
@@ -36561,12 +36859,12 @@ async function ensureClaudeInstructionSymlink(result, agentsPath, claudePath, dr
|
|
|
36561
36859
|
to: agentsPath
|
|
36562
36860
|
});
|
|
36563
36861
|
}
|
|
36564
|
-
async function writeRepositoryInstructionIndex(result, folders, dryRun = false) {
|
|
36862
|
+
async function writeRepositoryInstructionIndex(result, folders, dryRun = false, includeRules = true) {
|
|
36565
36863
|
const rulesDir = path17.join(folders.agentsDir, "rules");
|
|
36566
36864
|
const agentsPath = path17.join(folders.rootDir, "AGENTS.md");
|
|
36567
36865
|
const claudePath = path17.join(folders.rootDir, "CLAUDE.md");
|
|
36568
36866
|
const rulesDirExists = await pathExistsOrSymlink(rulesDir);
|
|
36569
|
-
const rules = dryRun || rulesDirExists ? dryRun ? await collectPlannedRuleIndexEntries(result, folders.rootDir) : await collectRuleIndexEntries(rulesDir, folders.rootDir) : [];
|
|
36867
|
+
const rules = includeRules && (dryRun || rulesDirExists) ? dryRun ? await collectPlannedRuleIndexEntries(result, folders.rootDir) : await collectRuleIndexEntries(rulesDir, folders.rootDir) : [];
|
|
36570
36868
|
const agentsExists = await pathExistsOrSymlink(agentsPath);
|
|
36571
36869
|
const claudeExists = await pathExistsOrSymlink(claudePath);
|
|
36572
36870
|
if (rules.length === 0 && !agentsExists && !claudeExists) {
|
|
@@ -36601,21 +36899,20 @@ ${renderRulesIndexBlock(rules)}
|
|
|
36601
36899
|
}
|
|
36602
36900
|
async function collectPlannedRuleIndexEntries(result, rootDir) {
|
|
36603
36901
|
const rules = [];
|
|
36902
|
+
const removedRulePaths = new Set([
|
|
36903
|
+
...result.renamed.filter((entry) => entry.category === "rules").map((entry) => path17.relative(rootDir, entry.from)),
|
|
36904
|
+
...result.duplicates.filter((entry) => entry.category === "rules").map((entry) => path17.relative(rootDir, entry.from))
|
|
36905
|
+
]);
|
|
36604
36906
|
const plannedRuleEntries = [
|
|
36605
36907
|
...result.imported.filter((entry) => entry.category === "rules"),
|
|
36606
36908
|
...result.renamed.filter((entry) => entry.category === "rules")
|
|
36607
36909
|
];
|
|
36608
36910
|
for (const entry of plannedRuleEntries) {
|
|
36609
|
-
|
|
36610
|
-
if (![".md", ".mdc"].includes(ext))
|
|
36611
|
-
continue;
|
|
36612
|
-
rules.push({
|
|
36613
|
-
title: await readRuleTitle(entry.from),
|
|
36614
|
-
relativePath: path17.relative(rootDir, entry.to)
|
|
36615
|
-
});
|
|
36911
|
+
rules.push(...await collectPlannedRuleEntryFiles(entry, rootDir));
|
|
36616
36912
|
}
|
|
36617
36913
|
if (await pathExistsOrSymlink(path17.join(rootDir, ".agents", "rules"))) {
|
|
36618
|
-
|
|
36914
|
+
const existingRules = await collectRuleIndexEntries(path17.join(rootDir, ".agents", "rules"), rootDir);
|
|
36915
|
+
rules.push(...existingRules.filter((rule) => !removedRulePaths.has(rule.relativePath)));
|
|
36619
36916
|
}
|
|
36620
36917
|
const byPath = new Map;
|
|
36621
36918
|
for (const rule of rules) {
|
|
@@ -36623,12 +36920,66 @@ async function collectPlannedRuleIndexEntries(result, rootDir) {
|
|
|
36623
36920
|
}
|
|
36624
36921
|
return [...byPath.values()].sort((a, b3) => a.relativePath.localeCompare(b3.relativePath));
|
|
36625
36922
|
}
|
|
36923
|
+
async function collectPlannedRuleEntryFiles(entry, rootDir) {
|
|
36924
|
+
const sourceStat = await import_fs_extra13.default.lstat(entry.from).catch(() => null);
|
|
36925
|
+
if (!sourceStat?.isDirectory()) {
|
|
36926
|
+
const ext = path17.extname(entry.to).toLowerCase();
|
|
36927
|
+
if (ext !== ".md")
|
|
36928
|
+
return [];
|
|
36929
|
+
return [{
|
|
36930
|
+
title: await readRuleTitle(entry.from),
|
|
36931
|
+
relativePath: path17.relative(rootDir, entry.to)
|
|
36932
|
+
}];
|
|
36933
|
+
}
|
|
36934
|
+
return collectPlannedRuleDirectoryFiles(entry.from, entry.to, rootDir);
|
|
36935
|
+
}
|
|
36936
|
+
async function collectPlannedRuleDirectoryFiles(sourceDir, targetDir, rootDir) {
|
|
36937
|
+
const entries = sortSourceEntries(await import_fs_extra13.default.readdir(sourceDir, { withFileTypes: true }).catch(() => []), "rules");
|
|
36938
|
+
const rules = [];
|
|
36939
|
+
const usedNames = new Set;
|
|
36940
|
+
const plannedSourcesByName = new Map;
|
|
36941
|
+
for (const entry of entries) {
|
|
36942
|
+
if (IGNORED_ENTRY_NAMES2.has(entry.name))
|
|
36943
|
+
continue;
|
|
36944
|
+
const sourcePath = path17.join(sourceDir, entry.name);
|
|
36945
|
+
if (entry.isDirectory()) {
|
|
36946
|
+
const targetPath2 = path17.join(targetDir, entry.name);
|
|
36947
|
+
usedNames.add(entry.name);
|
|
36948
|
+
rules.push(...await collectPlannedRuleDirectoryFiles(sourcePath, targetPath2, rootDir));
|
|
36949
|
+
continue;
|
|
36950
|
+
}
|
|
36951
|
+
if (!entry.isFile() && !entry.isSymbolicLink())
|
|
36952
|
+
continue;
|
|
36953
|
+
const ext = path17.extname(entry.name).toLowerCase();
|
|
36954
|
+
if (![".md", ".mdc"].includes(ext))
|
|
36955
|
+
continue;
|
|
36956
|
+
let targetName = normalizeRuleFileName(entry.name);
|
|
36957
|
+
const existingSourcePath = plannedSourcesByName.get(targetName);
|
|
36958
|
+
if (existingSourcePath) {
|
|
36959
|
+
const sourceHash = await hashRulePath(sourcePath);
|
|
36960
|
+
const existingHash = await hashRulePath(existingSourcePath);
|
|
36961
|
+
if (sourceHash === existingHash)
|
|
36962
|
+
continue;
|
|
36963
|
+
targetName = await findAvailableTargetName(targetDir, targetName, "agents-rules", usedNames);
|
|
36964
|
+
}
|
|
36965
|
+
usedNames.add(targetName);
|
|
36966
|
+
plannedSourcesByName.set(targetName, sourcePath);
|
|
36967
|
+
const targetPath = path17.join(targetDir, targetName);
|
|
36968
|
+
rules.push({
|
|
36969
|
+
title: await readRuleTitle(sourcePath),
|
|
36970
|
+
relativePath: path17.relative(rootDir, targetPath)
|
|
36971
|
+
});
|
|
36972
|
+
}
|
|
36973
|
+
return rules;
|
|
36974
|
+
}
|
|
36626
36975
|
async function unifyAgentsConfiguration(options = {}) {
|
|
36627
36976
|
const scope = options.scope ?? "global";
|
|
36628
36977
|
const dryRun = Boolean(options.dryRun);
|
|
36629
36978
|
const folders = resolveFolders(options);
|
|
36630
36979
|
const includeCodex = scope !== "repository";
|
|
36631
|
-
const
|
|
36980
|
+
const defaultCategories = scope === "repository" ? ["instructions", "skills", "agents", "rules"] : ["instructions", "skills", "agents"];
|
|
36981
|
+
const selectedCategories = new Set((options.categories ?? defaultCategories).filter((category) => defaultCategories.includes(category)));
|
|
36982
|
+
const instructionCandidates = getInstructionFileCandidates(options, scope);
|
|
36632
36983
|
const candidates = scope === "repository" ? getRepositoryContainerCandidates(options) : getContainerCandidates(options);
|
|
36633
36984
|
const result = {
|
|
36634
36985
|
rootDir: folders.rootDir,
|
|
@@ -36649,10 +37000,17 @@ async function unifyAgentsConfiguration(options = {}) {
|
|
|
36649
37000
|
instructions: path17.join(folders.agentsDir, "AGENTS.md"),
|
|
36650
37001
|
rules: path17.join(folders.agentsDir, "rules")
|
|
36651
37002
|
};
|
|
36652
|
-
|
|
37003
|
+
if (selectedCategories.has("instructions")) {
|
|
37004
|
+
await importInstructionFiles(instructionCandidates, destinationByCategory.instructions, result, dryRun);
|
|
37005
|
+
}
|
|
36653
37006
|
const categories = scope === "repository" ? ["skills", "agents", "rules"] : ["skills", "agents"];
|
|
37007
|
+
if (scope === "repository" && selectedCategories.has("rules") && await pathExistsOrSymlink(destinationByCategory.rules)) {
|
|
37008
|
+
await migrateExistingRuleExtensions(destinationByCategory.rules, result, dryRun);
|
|
37009
|
+
}
|
|
36654
37010
|
const activeCategories = new Set;
|
|
36655
37011
|
for (const category of categories) {
|
|
37012
|
+
if (!selectedCategories.has(category))
|
|
37013
|
+
continue;
|
|
36656
37014
|
const isActive = await importCategoryEntries(category, candidates, destinationByCategory[category], result, dryRun);
|
|
36657
37015
|
if (isActive) {
|
|
36658
37016
|
activeCategories.add(category);
|
|
@@ -36663,11 +37021,16 @@ async function unifyAgentsConfiguration(options = {}) {
|
|
|
36663
37021
|
continue;
|
|
36664
37022
|
await linkContainer(candidate, destinationByCategory[candidate.category], result, dryRun);
|
|
36665
37023
|
}
|
|
36666
|
-
|
|
36667
|
-
|
|
37024
|
+
if (selectedCategories.has("instructions")) {
|
|
37025
|
+
for (const candidate of instructionCandidates) {
|
|
37026
|
+
await linkInstructionFile(candidate, destinationByCategory.instructions, result, dryRun);
|
|
37027
|
+
}
|
|
36668
37028
|
}
|
|
36669
|
-
if (scope === "repository") {
|
|
36670
|
-
|
|
37029
|
+
if (scope === "repository" && (selectedCategories.has("instructions") || selectedCategories.has("rules"))) {
|
|
37030
|
+
if (!dryRun && selectedCategories.has("rules") && await pathExistsOrSymlink(destinationByCategory.rules)) {
|
|
37031
|
+
await migrateExistingRuleExtensions(destinationByCategory.rules, result, dryRun);
|
|
37032
|
+
}
|
|
37033
|
+
await writeRepositoryInstructionIndex(result, folders, dryRun, selectedCategories.has("rules"));
|
|
36671
37034
|
}
|
|
36672
37035
|
return result;
|
|
36673
37036
|
}
|
|
@@ -36676,6 +37039,46 @@ async function previewAgentsConfiguration(options = {}) {
|
|
|
36676
37039
|
}
|
|
36677
37040
|
|
|
36678
37041
|
// src/commands/agents-unify.ts
|
|
37042
|
+
var CATEGORY_LABELS = {
|
|
37043
|
+
instructions: "AGENTS.md",
|
|
37044
|
+
skills: "Skills",
|
|
37045
|
+
agents: "Agents",
|
|
37046
|
+
rules: "Rules and memories"
|
|
37047
|
+
};
|
|
37048
|
+
function defaultCategoriesForScope(scope) {
|
|
37049
|
+
return scope === "repository" ? ["instructions", "skills", "agents", "rules"] : ["instructions", "skills", "agents"];
|
|
37050
|
+
}
|
|
37051
|
+
function selectableCategoriesForScope(scope) {
|
|
37052
|
+
return defaultCategoriesForScope(scope);
|
|
37053
|
+
}
|
|
37054
|
+
async function resolveSelectedCategories(params) {
|
|
37055
|
+
const scope = params.scope ?? "global";
|
|
37056
|
+
const selectableCategories = selectableCategoriesForScope(scope);
|
|
37057
|
+
const requestedCategories = params.categories?.filter((category) => selectableCategories.includes(category));
|
|
37058
|
+
const initialValues = requestedCategories ?? selectableCategories;
|
|
37059
|
+
if (!params.interactive) {
|
|
37060
|
+
if (params.categories && initialValues.length === 0) {
|
|
37061
|
+
throw new Error(`Selected categories do not apply to ${scope} scope`);
|
|
37062
|
+
}
|
|
37063
|
+
return requestedCategories;
|
|
37064
|
+
}
|
|
37065
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
37066
|
+
throw new Error("Interactive category selection requires a TTY");
|
|
37067
|
+
}
|
|
37068
|
+
const selected = await fe({
|
|
37069
|
+
message: "What do you want to unify?",
|
|
37070
|
+
options: selectableCategories.map((category) => ({
|
|
37071
|
+
value: category,
|
|
37072
|
+
label: CATEGORY_LABELS[category]
|
|
37073
|
+
})),
|
|
37074
|
+
initialValues,
|
|
37075
|
+
required: true
|
|
37076
|
+
});
|
|
37077
|
+
if (pD(selected)) {
|
|
37078
|
+
return null;
|
|
37079
|
+
}
|
|
37080
|
+
return selected;
|
|
37081
|
+
}
|
|
36679
37082
|
function countByCategory(result, key, category) {
|
|
36680
37083
|
return result[key].filter((entry) => entry.category === category).length;
|
|
36681
37084
|
}
|
|
@@ -36741,15 +37144,24 @@ AIBlueprint agents unify ${source_default.gray(`v${getVersion()}`)}
|
|
|
36741
37144
|
`));
|
|
36742
37145
|
console.log(source_default.gray(`Scope: ${params.scope ?? "global"}`));
|
|
36743
37146
|
console.log(source_default.gray(params.scope === "repository" ? "Centralizing project agent configuration into .agents" : "Centralizing reusable agent configuration into .agents, then rendering Codex agents"));
|
|
36744
|
-
const
|
|
37147
|
+
const selectedCategories = await resolveSelectedCategories(params);
|
|
37148
|
+
if (selectedCategories === null) {
|
|
37149
|
+
console.log(source_default.gray(`
|
|
37150
|
+
Unify cancelled`));
|
|
37151
|
+
return;
|
|
37152
|
+
}
|
|
37153
|
+
const commandParams = { ...params, categories: selectedCategories };
|
|
37154
|
+
const effectiveCategories = selectedCategories ?? defaultCategoriesForScope(params.scope ?? "global");
|
|
37155
|
+
console.log(source_default.gray(`Categories: ${effectiveCategories.map((category) => CATEGORY_LABELS[category]).join(", ")}`));
|
|
37156
|
+
const preview = await previewAgentsConfiguration(commandParams);
|
|
36745
37157
|
printAgentsUnifyPreview(preview);
|
|
36746
37158
|
if (!await confirmUnify()) {
|
|
36747
37159
|
console.log(source_default.gray(`
|
|
36748
37160
|
Unify cancelled`));
|
|
36749
37161
|
return;
|
|
36750
37162
|
}
|
|
36751
|
-
const result = await unifyAgentsConfiguration(
|
|
36752
|
-
const codexResult = params.scope === "repository" ? null : await renderCodexAgentsFromMarkdown(
|
|
37163
|
+
const result = await unifyAgentsConfiguration(commandParams);
|
|
37164
|
+
const codexResult = params.scope === "repository" || !effectiveCategories.includes("agents") ? null : await renderCodexAgentsFromMarkdown(commandParams);
|
|
36753
37165
|
console.log(source_default.green(`
|
|
36754
37166
|
Unify complete`));
|
|
36755
37167
|
console.log(source_default.gray(` Shared folder: ${result.agentsDir}`));
|
|
@@ -38454,6 +38866,35 @@ async function configsBackupsCreateCommand(reason, options = {}) {
|
|
|
38454
38866
|
process.exit(1);
|
|
38455
38867
|
}
|
|
38456
38868
|
}
|
|
38869
|
+
async function configsBackupsCleanCommand(options = {}) {
|
|
38870
|
+
try {
|
|
38871
|
+
const days = options.days ?? DEFAULT_BACKUP_RETENTION_DAYS;
|
|
38872
|
+
console.log(source_default.gray(`Cleaning config backups older than ${days} day${days !== 1 ? "s" : ""}...`));
|
|
38873
|
+
const result = await cleanConfigBackups(options);
|
|
38874
|
+
if (options.dryRun) {
|
|
38875
|
+
console.log(source_default.yellow(`Dry run: ${result.deleted.length} backup${result.deleted.length !== 1 ? "s" : ""} would be deleted.`));
|
|
38876
|
+
} else {
|
|
38877
|
+
console.log(source_default.green(`Deleted ${result.deleted.length} old backup${result.deleted.length !== 1 ? "s" : ""}.`));
|
|
38878
|
+
}
|
|
38879
|
+
console.log(source_default.gray(`Cutoff: ${formatDate2(result.cutoff)}`));
|
|
38880
|
+
for (const backup of result.deleted) {
|
|
38881
|
+
console.log(source_default.gray(` ${backup.name}`));
|
|
38882
|
+
}
|
|
38883
|
+
if (result.skipped.length > 0) {
|
|
38884
|
+
console.log(source_default.gray(`Skipped ${result.skipped.length} backup${result.skipped.length !== 1 ? "s" : ""}.`));
|
|
38885
|
+
}
|
|
38886
|
+
if (result.failed.length > 0) {
|
|
38887
|
+
console.error(source_default.red(`Failed to delete ${result.failed.length} backup${result.failed.length !== 1 ? "s" : ""}.`));
|
|
38888
|
+
for (const failure of result.failed) {
|
|
38889
|
+
console.error(source_default.red(` ${failure.snapshot.name}: ${failure.error}`));
|
|
38890
|
+
}
|
|
38891
|
+
process.exit(1);
|
|
38892
|
+
}
|
|
38893
|
+
} catch (error) {
|
|
38894
|
+
console.error(source_default.red(error instanceof Error ? error.message : String(error)));
|
|
38895
|
+
process.exit(1);
|
|
38896
|
+
}
|
|
38897
|
+
}
|
|
38457
38898
|
|
|
38458
38899
|
// src/commands/openclaw-pro.ts
|
|
38459
38900
|
import os20 from "os";
|
|
@@ -38941,6 +39382,18 @@ var __dirname3 = dirname3(fileURLToPath3(import.meta.url));
|
|
|
38941
39382
|
var packageJson = JSON.parse(readFileSync3(join2(__dirname3, "../package.json"), "utf8"));
|
|
38942
39383
|
var program2 = new Command;
|
|
38943
39384
|
program2.name("aiblueprint").description("AIBlueprint CLI for setting up AI coding configurations").version(packageJson.version);
|
|
39385
|
+
function readUnifyCategories(options) {
|
|
39386
|
+
const categories = [];
|
|
39387
|
+
if (options.agentsMd)
|
|
39388
|
+
categories.push("instructions");
|
|
39389
|
+
if (options.skills)
|
|
39390
|
+
categories.push("skills");
|
|
39391
|
+
if (options.agents)
|
|
39392
|
+
categories.push("agents");
|
|
39393
|
+
if (options.rules)
|
|
39394
|
+
categories.push("rules");
|
|
39395
|
+
return categories.length > 0 ? categories : undefined;
|
|
39396
|
+
}
|
|
38944
39397
|
function registerAgentsCommands(cmd) {
|
|
38945
39398
|
cmd.option("-f, --folder <path>", "Root folder that contains .claude/, .codex/, .agents/ (default: $HOME)").option("--claudeCodeFolder <path>", "Override Claude Code folder (default: {folder}/.claude)").option("--codexFolder <path>", "Override Codex folder (default: {folder}/.codex)").option("--agentsFolder <path>", "Override shared agents folder (default: {folder}/.agents)").option("-s, --skip", "Skip interactive prompts and install all features");
|
|
38946
39399
|
cmd.command("setup").description("Setup AI coding configuration with AIBlueprint defaults").action((options, command) => {
|
|
@@ -38968,7 +39421,7 @@ function registerAgentsCommands(cmd) {
|
|
|
38968
39421
|
codexFolder: parentOptions.codexFolder
|
|
38969
39422
|
});
|
|
38970
39423
|
});
|
|
38971
|
-
cmd.command("unify [scope]").description("Unify agent configuration into .agents (scope: global or projects/repository; default: global)").action((scope, options, command) => {
|
|
39424
|
+
cmd.command("unify [scope]").description("Unify agent configuration into .agents (scope: global or projects/repository; default: global)").option("-i, --interactive", "Choose which categories to unify").option("--agents-md", "Unify instruction files into .agents/AGENTS.md").option("--skills", "Unify skills").option("--agents", "Unify Markdown agents").option("--rules", "Unify repository rules and memories").action((scope, options, command) => {
|
|
38972
39425
|
const parentOptions = command.parent.opts();
|
|
38973
39426
|
const requestedScope = scope ?? "global";
|
|
38974
39427
|
const selectedScope = requestedScope === "projects" || requestedScope === "project" ? "repository" : requestedScope;
|
|
@@ -38983,7 +39436,9 @@ function registerAgentsCommands(cmd) {
|
|
|
38983
39436
|
claudeCodeFolder: parentOptions.claudeCodeFolder,
|
|
38984
39437
|
codexFolder: parentOptions.codexFolder,
|
|
38985
39438
|
agentsFolder: parentOptions.agentsFolder,
|
|
38986
|
-
scope: selectedScope
|
|
39439
|
+
scope: selectedScope,
|
|
39440
|
+
categories: readUnifyCategories(options),
|
|
39441
|
+
interactive: options.interactive
|
|
38987
39442
|
});
|
|
38988
39443
|
});
|
|
38989
39444
|
cmd.command("codex-agents").description("Render shared Markdown agents from .agents/agents into Codex TOML custom agents").option("--overwrite", "Overwrite existing non-generated Codex agent files").action((options, command) => {
|
|
@@ -39068,6 +39523,13 @@ function readConfigOptions(command, options = {}) {
|
|
|
39068
39523
|
agentsFolder: findOption("agentsFolder")
|
|
39069
39524
|
};
|
|
39070
39525
|
}
|
|
39526
|
+
function parseRetentionDays(value) {
|
|
39527
|
+
try {
|
|
39528
|
+
return normalizeBackupRetentionDays(Number(value));
|
|
39529
|
+
} catch (error) {
|
|
39530
|
+
throw new InvalidArgumentError(error instanceof Error ? error.message : String(error));
|
|
39531
|
+
}
|
|
39532
|
+
}
|
|
39071
39533
|
var agentsCmd = program2.command("agents").description("AI coding configuration commands");
|
|
39072
39534
|
registerAgentsCommands(agentsCmd);
|
|
39073
39535
|
var aiCodingCmd = program2.command("ai-coding").description("Legacy alias for agents configuration commands");
|
|
@@ -39119,6 +39581,15 @@ addConfigFolderOptions(configsBackupsCmd.command("create [reason]").description(
|
|
|
39119
39581
|
...folderOptions
|
|
39120
39582
|
});
|
|
39121
39583
|
});
|
|
39584
|
+
addConfigFolderOptions(configsBackupsCmd.command("clean").description("Delete old config backups managed by retention").option("-d, --days <days>", "Delete backups older than this many days", parseRetentionDays, DEFAULT_BACKUP_RETENTION_DAYS).option("--dry-run", "Show backups that would be deleted without removing them").option("--include-manual", "Also delete manual backups created with configs backups create")).action((options, command) => {
|
|
39585
|
+
const folderOptions = readConfigOptions(command, options);
|
|
39586
|
+
configsBackupsCleanCommand({
|
|
39587
|
+
...folderOptions,
|
|
39588
|
+
days: options.days,
|
|
39589
|
+
dryRun: options.dryRun,
|
|
39590
|
+
includeManual: options.includeManual
|
|
39591
|
+
});
|
|
39592
|
+
});
|
|
39122
39593
|
var openclawCmd = program2.command("openclaw").description("OpenClaw configuration commands").option("-f, --folder <path>", "Specify custom OpenClaw folder path (default: ~/.openclaw)");
|
|
39123
39594
|
var openclawProCmd = openclawCmd.command("pro").description("Manage OpenClaw Pro features");
|
|
39124
39595
|
openclawProCmd.command("activate [token]").description("Activate OpenClaw Pro with your access token").action((token) => {
|