aiblueprint-cli 1.4.66 → 1.4.68

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +311 -292
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -77,7 +77,7 @@ npx aiblueprint-cli@latest agents symlink
77
77
  # Centralize global skills and agents in ~/.agents
78
78
  npx aiblueprint-cli@latest agents unify
79
79
 
80
- # Unify project-local .claude/.codex/.cursor config into .agents
80
+ # Unify project-local .claude/.cursor config into .agents
81
81
  npx aiblueprint-cli@latest agents unify projects
82
82
 
83
83
  # Recover sessions from saved configs and backups
package/dist/cli.js CHANGED
@@ -34892,10 +34892,132 @@ async function symlinkCommand(params = {}) {
34892
34892
  }
34893
34893
 
34894
34894
  // src/lib/agents-unifier.ts
34895
- var import_fs_extra12 = __toESM(require_lib4(), 1);
34895
+ var import_fs_extra13 = __toESM(require_lib4(), 1);
34896
34896
  import crypto from "crypto";
34897
- import os13 from "os";
34897
+ import os14 from "os";
34898
+ import path17 from "path";
34899
+
34900
+ // src/lib/backup-utils.ts
34901
+ var import_fs_extra12 = __toESM(require_lib4(), 1);
34898
34902
  import path16 from "path";
34903
+ import os13 from "os";
34904
+ var BACKUP_BASE_DIR = path16.join(os13.homedir(), ".config", "aiblueprint", "backup");
34905
+ function getBackupDir() {
34906
+ return process.env.AIBLUEPRINT_BACKUP_DIR || BACKUP_BASE_DIR;
34907
+ }
34908
+ function formatDate(date) {
34909
+ const pad = (n) => n.toString().padStart(2, "0");
34910
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
34911
+ }
34912
+ function createBackupNameSuffix(value) {
34913
+ return value.trim().replace(/^[a-zA-Z]:/, (drive) => drive.replace(":", "")).replace(/[\\/]+/g, "--").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "root";
34914
+ }
34915
+ function createTimestampedBackupName(suffix, date = new Date) {
34916
+ const base = formatDate(date);
34917
+ if (!suffix)
34918
+ return base;
34919
+ return `${base}--${createBackupNameSuffix(suffix)}`;
34920
+ }
34921
+ async function listBackups() {
34922
+ const backupBaseDir = getBackupDir();
34923
+ const exists = await import_fs_extra12.default.pathExists(backupBaseDir);
34924
+ if (!exists) {
34925
+ return [];
34926
+ }
34927
+ const entries = await import_fs_extra12.default.readdir(backupBaseDir, { withFileTypes: true });
34928
+ const backups = [];
34929
+ for (const entry of entries) {
34930
+ if (!entry.isDirectory())
34931
+ continue;
34932
+ const match = entry.name.match(/^(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})(?:--.+)?$/);
34933
+ if (!match)
34934
+ continue;
34935
+ const [, year, month, day, hour, minute, second] = match;
34936
+ const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
34937
+ backups.push({
34938
+ name: entry.name,
34939
+ path: path16.join(backupBaseDir, entry.name),
34940
+ date
34941
+ });
34942
+ }
34943
+ return backups.sort((a, b) => b.date.getTime() - a.date.getTime());
34944
+ }
34945
+ var AGENTS_BACKUP_SUBDIR = ".agents";
34946
+ var CLAUDE_ITEMS = ["commands", "agents", "skills", "scripts", "settings.json"];
34947
+ var MANAGED_FOLDERS = [".claude", ".codex", ".agents"];
34948
+ async function copyForBackup(sourcePath, destPath) {
34949
+ await import_fs_extra12.default.copy(sourcePath, destPath, {
34950
+ overwrite: true,
34951
+ dereference: false
34952
+ });
34953
+ }
34954
+ async function hasMeaningfulContent(dir) {
34955
+ if (!await import_fs_extra12.default.pathExists(dir))
34956
+ return false;
34957
+ const files = await import_fs_extra12.default.readdir(dir);
34958
+ return files.some((f) => f !== ".DS_Store");
34959
+ }
34960
+ async function loadBackup(backupPath, claudeDir, codexDir, agentsDir) {
34961
+ const exists = await import_fs_extra12.default.pathExists(backupPath);
34962
+ if (!exists) {
34963
+ throw new Error(`Backup not found: ${backupPath}`);
34964
+ }
34965
+ const managedDestinations = {
34966
+ ".claude": claudeDir,
34967
+ ".codex": codexDir,
34968
+ ".agents": agentsDir
34969
+ };
34970
+ let restoredManagedFolder = false;
34971
+ for (const folderName of MANAGED_FOLDERS) {
34972
+ const sourcePath = path16.join(backupPath, folderName);
34973
+ const destPath = managedDestinations[folderName];
34974
+ if (!destPath || !await import_fs_extra12.default.pathExists(sourcePath))
34975
+ continue;
34976
+ await import_fs_extra12.default.ensureDir(destPath);
34977
+ await copyForBackup(sourcePath, destPath);
34978
+ restoredManagedFolder = true;
34979
+ }
34980
+ if (!restoredManagedFolder) {
34981
+ await import_fs_extra12.default.ensureDir(claudeDir);
34982
+ for (const item of CLAUDE_ITEMS) {
34983
+ const sourcePath = path16.join(backupPath, item);
34984
+ const destPath = path16.join(claudeDir, item);
34985
+ if (await import_fs_extra12.default.pathExists(sourcePath)) {
34986
+ await copyForBackup(sourcePath, destPath);
34987
+ }
34988
+ }
34989
+ if (agentsDir) {
34990
+ const agentsBackupPath = path16.join(backupPath, AGENTS_BACKUP_SUBDIR);
34991
+ if (await import_fs_extra12.default.pathExists(agentsBackupPath)) {
34992
+ await import_fs_extra12.default.ensureDir(agentsDir);
34993
+ await copyForBackup(agentsBackupPath, agentsDir);
34994
+ }
34995
+ }
34996
+ }
34997
+ }
34998
+ async function createBackup(claudeDir, codexDir, agentsDir) {
34999
+ const claudeHasContent = await hasMeaningfulContent(claudeDir);
35000
+ const codexHasContent = codexDir ? await hasMeaningfulContent(codexDir) : false;
35001
+ const agentsHasContent = agentsDir ? await hasMeaningfulContent(agentsDir) : false;
35002
+ if (!claudeHasContent && !codexHasContent && !agentsHasContent) {
35003
+ return null;
35004
+ }
35005
+ const backupPath = path16.join(getBackupDir(), createTimestampedBackupName());
35006
+ await import_fs_extra12.default.ensureDir(backupPath);
35007
+ if (claudeHasContent) {
35008
+ await copyForBackup(claudeDir, path16.join(backupPath, ".claude"));
35009
+ }
35010
+ if (codexHasContent && codexDir) {
35011
+ await copyForBackup(codexDir, path16.join(backupPath, ".codex"));
35012
+ }
35013
+ if (agentsHasContent && agentsDir) {
35014
+ const destPath = path16.join(backupPath, AGENTS_BACKUP_SUBDIR);
35015
+ await copyForBackup(agentsDir, destPath);
35016
+ }
35017
+ return backupPath;
35018
+ }
35019
+
35020
+ // src/lib/agents-unifier.ts
34899
35021
  var IGNORED_ENTRY_NAMES2 = new Set([
34900
35022
  ".DS_Store",
34901
35023
  ".git",
@@ -34905,7 +35027,7 @@ function uniqueByPath(candidates) {
34905
35027
  const seen = new Set;
34906
35028
  const unique = [];
34907
35029
  for (const candidate of candidates) {
34908
- const resolved = path16.resolve(candidate.path);
35030
+ const resolved = path17.resolve(candidate.path);
34909
35031
  if (seen.has(resolved))
34910
35032
  continue;
34911
35033
  seen.add(resolved);
@@ -34913,245 +35035,221 @@ function uniqueByPath(candidates) {
34913
35035
  }
34914
35036
  return unique;
34915
35037
  }
34916
- function getContainerCandidates(options) {
35038
+ function getContainerCandidates(options, includeCodex = true) {
34917
35039
  const folders = resolveFolders(options);
34918
- const cursorDir = path16.join(folders.rootDir, ".cursor");
34919
- const factoryDir = path16.join(folders.rootDir, ".factory");
34920
- const opencodeDir = path16.join(folders.rootDir, ".config", "opencode");
35040
+ const cursorDir = path17.join(folders.rootDir, ".cursor");
35041
+ const factoryDir = path17.join(folders.rootDir, ".factory");
35042
+ const opencodeDir = path17.join(folders.rootDir, ".config", "opencode");
34921
35043
  return uniqueByPath([
34922
35044
  {
34923
35045
  category: "skills",
34924
35046
  label: "agents-skills",
34925
- path: path16.join(folders.agentsDir, "skills"),
35047
+ path: path17.join(folders.agentsDir, "skills"),
34926
35048
  isDestination: true
34927
35049
  },
34928
35050
  {
34929
35051
  category: "skills",
34930
35052
  label: "claude-skills",
34931
- path: path16.join(folders.claudeDir, "skills"),
35053
+ path: path17.join(folders.claudeDir, "skills"),
34932
35054
  linkWhenMissing: true
34933
35055
  },
34934
- {
35056
+ ...includeCodex ? [{
34935
35057
  category: "skills",
34936
35058
  label: "codex-skills",
34937
- path: path16.join(folders.codexDir, "skills"),
35059
+ path: path17.join(folders.codexDir, "skills"),
34938
35060
  linkWhenMissing: true
34939
- },
35061
+ }] : [],
34940
35062
  {
34941
35063
  category: "skills",
34942
35064
  label: "cursor-skills",
34943
- path: path16.join(cursorDir, "skills"),
35065
+ path: path17.join(cursorDir, "skills"),
34944
35066
  linkWhenParentExists: true
34945
35067
  },
34946
35068
  {
34947
35069
  category: "skills",
34948
35070
  label: "cursor-skills-cursor",
34949
- path: path16.join(cursorDir, "skills-cursor"),
35071
+ path: path17.join(cursorDir, "skills-cursor"),
34950
35072
  linkWhenParentExists: true
34951
35073
  },
34952
35074
  {
34953
35075
  category: "skills",
34954
35076
  label: "factory-skills",
34955
- path: path16.join(factoryDir, "skills"),
35077
+ path: path17.join(factoryDir, "skills"),
34956
35078
  linkWhenParentExists: true
34957
35079
  },
34958
35080
  {
34959
35081
  category: "skills",
34960
35082
  label: "opencode-skill",
34961
- path: path16.join(opencodeDir, "skill"),
35083
+ path: path17.join(opencodeDir, "skill"),
34962
35084
  linkWhenParentExists: true
34963
35085
  },
34964
35086
  {
34965
35087
  category: "skills",
34966
35088
  label: "opencode-skills",
34967
- path: path16.join(opencodeDir, "skills"),
35089
+ path: path17.join(opencodeDir, "skills"),
34968
35090
  linkWhenParentExists: true
34969
35091
  },
34970
35092
  {
34971
35093
  category: "agents",
34972
35094
  label: "agents-agents",
34973
- path: path16.join(folders.agentsDir, "agents"),
35095
+ path: path17.join(folders.agentsDir, "agents"),
34974
35096
  isDestination: true
34975
35097
  },
34976
35098
  {
34977
35099
  category: "agents",
34978
35100
  label: "claude-agents",
34979
- path: path16.join(folders.claudeDir, "agents"),
35101
+ path: path17.join(folders.claudeDir, "agents"),
34980
35102
  linkWhenMissing: true
34981
35103
  },
34982
35104
  {
34983
35105
  category: "agents",
34984
35106
  label: "claude-agnets",
34985
- path: path16.join(folders.claudeDir, "agnets")
35107
+ path: path17.join(folders.claudeDir, "agnets")
34986
35108
  },
34987
35109
  {
34988
35110
  category: "agents",
34989
35111
  label: "cursor-agents",
34990
- path: path16.join(cursorDir, "agents"),
35112
+ path: path17.join(cursorDir, "agents"),
34991
35113
  linkWhenParentExists: true
34992
35114
  },
34993
35115
  {
34994
35116
  category: "agents",
34995
35117
  label: "factory-droids",
34996
- path: path16.join(factoryDir, "droids"),
35118
+ path: path17.join(factoryDir, "droids"),
34997
35119
  linkWhenParentExists: true
34998
35120
  },
34999
35121
  {
35000
35122
  category: "agents",
35001
35123
  label: "opencode-agent",
35002
- path: path16.join(opencodeDir, "agent"),
35124
+ path: path17.join(opencodeDir, "agent"),
35003
35125
  linkWhenParentExists: true
35004
35126
  },
35005
35127
  {
35006
35128
  category: "agents",
35007
35129
  label: "opencode-agents",
35008
- path: path16.join(opencodeDir, "agents"),
35130
+ path: path17.join(opencodeDir, "agents"),
35009
35131
  linkWhenParentExists: true
35010
35132
  }
35011
35133
  ]);
35012
35134
  }
35013
- function getInstructionFileCandidates(options) {
35135
+ function getInstructionFileCandidates(options, includeCodex = true) {
35014
35136
  const folders = resolveFolders(options);
35015
35137
  return uniqueByPath([
35016
35138
  {
35017
35139
  label: "agents-instructions",
35018
- path: path16.join(folders.agentsDir, "AGENTS.md"),
35140
+ path: path17.join(folders.agentsDir, "AGENTS.md"),
35019
35141
  isDestination: true
35020
35142
  },
35021
35143
  {
35022
35144
  label: "claude-instructions",
35023
- path: path16.join(folders.claudeDir, "CLAUDE.md"),
35145
+ path: path17.join(folders.claudeDir, "CLAUDE.md"),
35024
35146
  linkWhenMissing: true
35025
35147
  },
35026
- {
35148
+ ...includeCodex ? [{
35027
35149
  label: "codex-instructions",
35028
- path: path16.join(folders.codexDir, "AGENTS.md"),
35150
+ path: path17.join(folders.codexDir, "AGENTS.md"),
35029
35151
  linkWhenMissing: true
35030
- }
35152
+ }] : []
35031
35153
  ]);
35032
35154
  }
35033
35155
  function getRepositoryContainerCandidates(options) {
35034
35156
  const folders = resolveFolders(options);
35035
- const cursorDir = path16.join(folders.rootDir, ".cursor");
35157
+ const cursorDir = path17.join(folders.rootDir, ".cursor");
35036
35158
  return uniqueByPath([
35037
- ...getContainerCandidates(options),
35159
+ ...getContainerCandidates(options, false),
35038
35160
  {
35039
35161
  category: "rules",
35040
35162
  label: "agents-rules",
35041
- path: path16.join(folders.agentsDir, "rules"),
35163
+ path: path17.join(folders.agentsDir, "rules"),
35042
35164
  isDestination: true
35043
35165
  },
35044
35166
  {
35045
35167
  category: "rules",
35046
35168
  label: "claude-rules",
35047
- path: path16.join(folders.claudeDir, "rules"),
35169
+ path: path17.join(folders.claudeDir, "rules"),
35048
35170
  linkWhenMissing: true
35049
35171
  },
35050
- {
35051
- category: "rules",
35052
- label: "codex-rules",
35053
- path: path16.join(folders.codexDir, "rules"),
35054
- linkWhenParentExists: true
35055
- },
35056
35172
  {
35057
35173
  category: "rules",
35058
35174
  label: "cursor-rules",
35059
- path: path16.join(cursorDir, "rules"),
35175
+ path: path17.join(cursorDir, "rules"),
35060
35176
  linkWhenParentExists: true
35061
35177
  },
35062
35178
  {
35063
35179
  category: "rules",
35064
35180
  label: "claude-memories",
35065
- path: path16.join(folders.claudeDir, "memories"),
35066
- linkSource: false
35067
- },
35068
- {
35069
- category: "rules",
35070
- label: "codex-memories",
35071
- path: path16.join(folders.codexDir, "memories"),
35181
+ path: path17.join(folders.claudeDir, "memories"),
35072
35182
  linkSource: false
35073
35183
  },
35074
35184
  {
35075
35185
  category: "rules",
35076
35186
  label: "cursor-memories",
35077
- path: path16.join(cursorDir, "memories"),
35187
+ path: path17.join(cursorDir, "memories"),
35078
35188
  linkSource: false
35079
35189
  },
35080
35190
  {
35081
35191
  category: "rules",
35082
35192
  label: "claude-memory",
35083
- path: path16.join(folders.claudeDir, "memory.md"),
35084
- linkSource: false
35085
- },
35086
- {
35087
- category: "rules",
35088
- label: "codex-memory",
35089
- path: path16.join(folders.codexDir, "memory.md"),
35193
+ path: path17.join(folders.claudeDir, "memory.md"),
35090
35194
  linkSource: false
35091
35195
  },
35092
35196
  {
35093
35197
  category: "rules",
35094
35198
  label: "cursor-memory",
35095
- path: path16.join(cursorDir, "memory.md"),
35199
+ path: path17.join(cursorDir, "memory.md"),
35096
35200
  linkSource: false
35097
35201
  },
35098
35202
  {
35099
35203
  category: "rules",
35100
35204
  label: "claude-memory-uppercase",
35101
- path: path16.join(folders.claudeDir, "MEMORY.md"),
35102
- linkSource: false
35103
- },
35104
- {
35105
- category: "rules",
35106
- label: "codex-memory-uppercase",
35107
- path: path16.join(folders.codexDir, "MEMORY.md"),
35205
+ path: path17.join(folders.claudeDir, "MEMORY.md"),
35108
35206
  linkSource: false
35109
35207
  },
35110
35208
  {
35111
35209
  category: "rules",
35112
35210
  label: "cursor-memory-uppercase",
35113
- path: path16.join(cursorDir, "MEMORY.md"),
35211
+ path: path17.join(cursorDir, "MEMORY.md"),
35114
35212
  linkSource: false
35115
35213
  }
35116
35214
  ]);
35117
35215
  }
35118
35216
  async function pathExistsOrSymlink(targetPath) {
35119
- const stat = await import_fs_extra12.default.lstat(targetPath).catch(() => null);
35217
+ const stat = await import_fs_extra13.default.lstat(targetPath).catch(() => null);
35120
35218
  return Boolean(stat);
35121
35219
  }
35122
35220
  async function realPathIfPossible(targetPath) {
35123
35221
  try {
35124
- return await import_fs_extra12.default.realpath(targetPath);
35222
+ return await import_fs_extra13.default.realpath(targetPath);
35125
35223
  } catch {
35126
35224
  return null;
35127
35225
  }
35128
35226
  }
35129
35227
  function samePath(a, b) {
35130
- return path16.resolve(a) === path16.resolve(b);
35228
+ return path17.resolve(a) === path17.resolve(b);
35131
35229
  }
35132
35230
  function hashString(value) {
35133
35231
  return crypto.createHash("sha256").update(value).digest("hex");
35134
35232
  }
35135
35233
  async function hashPath(targetPath) {
35136
- const stat = await import_fs_extra12.default.lstat(targetPath);
35234
+ const stat = await import_fs_extra13.default.lstat(targetPath);
35137
35235
  if (stat.isSymbolicLink()) {
35138
- const linkTarget = await import_fs_extra12.default.readlink(targetPath);
35236
+ const linkTarget = await import_fs_extra13.default.readlink(targetPath);
35139
35237
  return hashString(`symlink:${linkTarget}`);
35140
35238
  }
35141
35239
  if (stat.isFile()) {
35142
35240
  const fileHash = crypto.createHash("sha256");
35143
35241
  fileHash.update("file:");
35144
- fileHash.update(await import_fs_extra12.default.readFile(targetPath));
35242
+ fileHash.update(await import_fs_extra13.default.readFile(targetPath));
35145
35243
  return fileHash.digest("hex");
35146
35244
  }
35147
35245
  if (stat.isDirectory()) {
35148
- const entries = (await import_fs_extra12.default.readdir(targetPath, { withFileTypes: true })).filter((entry) => !IGNORED_ENTRY_NAMES2.has(entry.name)).sort((a, b) => a.name.localeCompare(b.name));
35246
+ const entries = (await import_fs_extra13.default.readdir(targetPath, { withFileTypes: true })).filter((entry) => !IGNORED_ENTRY_NAMES2.has(entry.name)).sort((a, b) => a.name.localeCompare(b.name));
35149
35247
  const dirHash = crypto.createHash("sha256");
35150
35248
  dirHash.update("dir:");
35151
35249
  for (const entry of entries) {
35152
35250
  dirHash.update(entry.name);
35153
35251
  dirHash.update("\x00");
35154
- dirHash.update(await hashPath(path16.join(targetPath, entry.name)));
35252
+ dirHash.update(await hashPath(path17.join(targetPath, entry.name)));
35155
35253
  dirHash.update("\x00");
35156
35254
  }
35157
35255
  return dirHash.digest("hex");
@@ -35197,39 +35295,39 @@ function normalizePortableText(content) {
35197
35295
  return normalized;
35198
35296
  }
35199
35297
  function isLikelyTextFile(filePath) {
35200
- const ext = path16.extname(filePath).toLowerCase();
35298
+ const ext = path17.extname(filePath).toLowerCase();
35201
35299
  if (TEXT_EXTENSIONS.has(ext))
35202
35300
  return true;
35203
- return path16.basename(filePath) === "SKILL.md";
35301
+ return path17.basename(filePath) === "SKILL.md";
35204
35302
  }
35205
35303
  async function normalizePortableContent(targetPath) {
35206
- const stat = await import_fs_extra12.default.lstat(targetPath).catch(() => null);
35304
+ const stat = await import_fs_extra13.default.lstat(targetPath).catch(() => null);
35207
35305
  if (!stat || stat.isSymbolicLink())
35208
35306
  return;
35209
35307
  if (stat.isDirectory()) {
35210
- const entries = await import_fs_extra12.default.readdir(targetPath);
35308
+ const entries = await import_fs_extra13.default.readdir(targetPath);
35211
35309
  for (const entry of entries) {
35212
35310
  if (IGNORED_ENTRY_NAMES2.has(entry))
35213
35311
  continue;
35214
- await normalizePortableContent(path16.join(targetPath, entry));
35312
+ await normalizePortableContent(path17.join(targetPath, entry));
35215
35313
  }
35216
35314
  return;
35217
35315
  }
35218
35316
  if (!stat.isFile() || !isLikelyTextFile(targetPath))
35219
35317
  return;
35220
- const content = await import_fs_extra12.default.readFile(targetPath, "utf-8").catch(() => null);
35318
+ const content = await import_fs_extra13.default.readFile(targetPath, "utf-8").catch(() => null);
35221
35319
  if (content === null || content.includes("\x00"))
35222
35320
  return;
35223
35321
  const normalized = normalizePortableText(content);
35224
35322
  if (normalized !== content) {
35225
- await import_fs_extra12.default.writeFile(targetPath, normalized, "utf-8");
35323
+ await import_fs_extra13.default.writeFile(targetPath, normalized, "utf-8");
35226
35324
  }
35227
35325
  }
35228
35326
  function suffixFromLabel(label) {
35229
35327
  return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "source";
35230
35328
  }
35231
35329
  function nameWithSuffix(name, suffix, index) {
35232
- const parsed = path16.parse(name);
35330
+ const parsed = path17.parse(name);
35233
35331
  const numberedSuffix = index === 1 ? suffix : `${suffix}-${index}`;
35234
35332
  if (parsed.ext && parsed.name) {
35235
35333
  return `${parsed.name}--${numberedSuffix}${parsed.ext}`;
@@ -35241,32 +35339,31 @@ async function findTargetName(destinationDir, originalName, label) {
35241
35339
  let index = 1;
35242
35340
  while (true) {
35243
35341
  const candidate = nameWithSuffix(originalName, suffix, index);
35244
- if (!await pathExistsOrSymlink(path16.join(destinationDir, candidate))) {
35342
+ if (!await pathExistsOrSymlink(path17.join(destinationDir, candidate))) {
35245
35343
  return candidate;
35246
35344
  }
35247
35345
  index++;
35248
35346
  }
35249
35347
  }
35250
35348
  async function addExistingDestinationHashes(destinationDir, knownHashes) {
35251
- if (!await import_fs_extra12.default.pathExists(destinationDir))
35349
+ if (!await import_fs_extra13.default.pathExists(destinationDir))
35252
35350
  return;
35253
- const entries = await import_fs_extra12.default.readdir(destinationDir, { withFileTypes: true });
35351
+ const entries = await import_fs_extra13.default.readdir(destinationDir, { withFileTypes: true });
35254
35352
  for (const entry of entries) {
35255
35353
  if (IGNORED_ENTRY_NAMES2.has(entry.name))
35256
35354
  continue;
35257
- const entryPath = path16.join(destinationDir, entry.name);
35355
+ const entryPath = path17.join(destinationDir, entry.name);
35258
35356
  knownHashes.set(await hashPath(entryPath), entry.name);
35259
35357
  }
35260
35358
  }
35261
35359
  async function importCategoryEntries(category, candidates, destinationDir, result) {
35262
- await import_fs_extra12.default.ensureDir(destinationDir);
35263
- const destinationRealPath = await realPathIfPossible(destinationDir);
35264
- const knownHashes = new Map;
35265
- await addExistingDestinationHashes(destinationDir, knownHashes);
35360
+ const sourceEntries = [];
35361
+ const destinationExists = await pathExistsOrSymlink(destinationDir);
35362
+ const destinationRealPath = destinationExists ? await realPathIfPossible(destinationDir) : null;
35266
35363
  for (const candidate of candidates) {
35267
35364
  if (candidate.category !== category || candidate.isDestination)
35268
35365
  continue;
35269
- const candidateStat = await import_fs_extra12.default.lstat(candidate.path).catch(() => null);
35366
+ const candidateStat = await import_fs_extra13.default.lstat(candidate.path).catch(() => null);
35270
35367
  if (!candidateStat)
35271
35368
  continue;
35272
35369
  const candidateRealPath = await realPathIfPossible(candidate.path);
@@ -35282,9 +35379,24 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
35282
35379
  });
35283
35380
  continue;
35284
35381
  }
35382
+ const collectableEntries = [];
35285
35383
  for (const entry of entries) {
35286
- if (!shouldCollectPath(category, entry.name, entry.path))
35384
+ if (!await shouldCollectPath(category, entry.name, entry.path))
35287
35385
  continue;
35386
+ collectableEntries.push(entry);
35387
+ }
35388
+ if (collectableEntries.length > 0) {
35389
+ sourceEntries.push({ candidate, entries: collectableEntries });
35390
+ }
35391
+ }
35392
+ if (!destinationExists && sourceEntries.length === 0) {
35393
+ return false;
35394
+ }
35395
+ await import_fs_extra13.default.ensureDir(destinationDir);
35396
+ const knownHashes = new Map;
35397
+ await addExistingDestinationHashes(destinationDir, knownHashes);
35398
+ for (const { candidate, entries } of sourceEntries) {
35399
+ for (const entry of entries) {
35288
35400
  const sourcePath = entry.path;
35289
35401
  const sourceHash = await hashPath(sourcePath);
35290
35402
  const existingName = knownHashes.get(sourceHash);
@@ -35293,15 +35405,15 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
35293
35405
  category,
35294
35406
  name: entry.name,
35295
35407
  from: sourcePath,
35296
- keptAs: path16.join(destinationDir, existingName)
35408
+ keptAs: path17.join(destinationDir, existingName)
35297
35409
  });
35298
35410
  continue;
35299
35411
  }
35300
35412
  let targetName = entry.name;
35301
- let targetPath = path16.join(destinationDir, targetName);
35413
+ let targetPath = path17.join(destinationDir, targetName);
35302
35414
  if (await pathExistsOrSymlink(targetPath)) {
35303
35415
  targetName = await findTargetName(destinationDir, entry.name, candidate.label);
35304
- targetPath = path16.join(destinationDir, targetName);
35416
+ targetPath = path17.join(destinationDir, targetName);
35305
35417
  result.renamed.push({
35306
35418
  category,
35307
35419
  name: entry.name,
@@ -35310,12 +35422,12 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
35310
35422
  reason: "Same name with different content"
35311
35423
  });
35312
35424
  }
35313
- await import_fs_extra12.default.copy(sourcePath, targetPath, {
35425
+ await import_fs_extra13.default.copy(sourcePath, targetPath, {
35314
35426
  dereference: false,
35315
35427
  overwrite: false
35316
35428
  });
35317
35429
  await normalizePortableContent(targetPath);
35318
- knownHashes.set(sourceHash, targetName);
35430
+ knownHashes.set(await hashPath(targetPath), targetName);
35319
35431
  result.imported.push({
35320
35432
  category,
35321
35433
  name: entry.name,
@@ -35324,28 +35436,29 @@ async function importCategoryEntries(category, candidates, destinationDir, resul
35324
35436
  });
35325
35437
  }
35326
35438
  }
35439
+ return true;
35327
35440
  }
35328
35441
  async function collectCandidateEntries(candidate) {
35329
- const stat = await import_fs_extra12.default.lstat(candidate.path);
35442
+ const stat = await import_fs_extra13.default.lstat(candidate.path);
35330
35443
  if (stat.isDirectory()) {
35331
- const entries = await import_fs_extra12.default.readdir(candidate.path, { withFileTypes: true });
35444
+ const entries = await import_fs_extra13.default.readdir(candidate.path, { withFileTypes: true });
35332
35445
  return entries.map((entry) => ({
35333
35446
  name: entry.name,
35334
- path: path16.join(candidate.path, entry.name)
35447
+ path: path17.join(candidate.path, entry.name)
35335
35448
  }));
35336
35449
  }
35337
35450
  if (stat.isSymbolicLink()) {
35338
- const targetStat = await import_fs_extra12.default.stat(candidate.path).catch(() => null);
35451
+ const targetStat = await import_fs_extra13.default.stat(candidate.path).catch(() => null);
35339
35452
  if (targetStat?.isDirectory()) {
35340
- const entries = await import_fs_extra12.default.readdir(candidate.path, { withFileTypes: true });
35453
+ const entries = await import_fs_extra13.default.readdir(candidate.path, { withFileTypes: true });
35341
35454
  return entries.map((entry) => ({
35342
35455
  name: entry.name,
35343
- path: path16.join(candidate.path, entry.name)
35456
+ path: path17.join(candidate.path, entry.name)
35344
35457
  }));
35345
35458
  }
35346
35459
  }
35347
35460
  return [{
35348
- name: path16.basename(candidate.path),
35461
+ name: path17.basename(candidate.path),
35349
35462
  path: candidate.path
35350
35463
  }];
35351
35464
  }
@@ -35355,53 +35468,51 @@ async function shouldCollectPath(category, name, sourcePath) {
35355
35468
  if (category === "skills" && name === ".cursor-managed-skills-manifest.json") {
35356
35469
  return true;
35357
35470
  }
35358
- const stat = await import_fs_extra12.default.lstat(sourcePath).catch(() => null);
35471
+ const stat = await import_fs_extra13.default.lstat(sourcePath).catch(() => null);
35359
35472
  if (!stat)
35360
35473
  return false;
35361
35474
  return stat.isFile() || stat.isDirectory() || stat.isSymbolicLink();
35362
35475
  }
35363
- function timestamp2(date = new Date) {
35364
- return date.toISOString().replace(/\.\d{3}Z$/, "").replace(/[:T]/g, "-");
35365
- }
35366
35476
  function safeRelativePath(rootDir, targetPath) {
35367
- const relativePath = path16.relative(rootDir, targetPath);
35368
- if (!relativePath || relativePath.startsWith("..") || path16.isAbsolute(relativePath)) {
35369
- return path16.join("external", targetPath.replace(/[^a-zA-Z0-9._-]+/g, "-"));
35477
+ const relativePath = path17.relative(rootDir, targetPath);
35478
+ if (!relativePath || relativePath.startsWith("..") || path17.isAbsolute(relativePath)) {
35479
+ return path17.join("external", targetPath.replace(/[^a-zA-Z0-9._-]+/g, "-"));
35370
35480
  }
35371
35481
  return relativePath;
35372
35482
  }
35373
35483
  function createBackupPath(rootDir) {
35374
- return path16.join(rootDir, ".aiblueprint", "backups", "agents-unify-sources", timestamp2());
35484
+ const projectKey = createBackupNameSuffix(path17.resolve(rootDir));
35485
+ return path17.join(getBackupDir(), createTimestampedBackupName(`project-${projectKey}-agents-unify-sources`));
35375
35486
  }
35376
35487
  async function ensureBackupPath(result) {
35377
35488
  if (!result.backupPath) {
35378
35489
  result.backupPath = createBackupPath(result.rootDir);
35379
- await import_fs_extra12.default.ensureDir(result.backupPath);
35490
+ await import_fs_extra13.default.ensureDir(result.backupPath);
35380
35491
  }
35381
35492
  return result.backupPath;
35382
35493
  }
35383
35494
  async function createDirectorySymlink(source, target) {
35384
- await import_fs_extra12.default.ensureDir(path16.dirname(target));
35385
- if (os13.platform() === "win32") {
35386
- await import_fs_extra12.default.symlink(source, target, "junction");
35495
+ await import_fs_extra13.default.ensureDir(path17.dirname(target));
35496
+ if (os14.platform() === "win32") {
35497
+ await import_fs_extra13.default.symlink(source, target, "junction");
35387
35498
  return;
35388
35499
  }
35389
- await import_fs_extra12.default.symlink(source, target, "dir");
35500
+ await import_fs_extra13.default.symlink(source, target, "dir");
35390
35501
  }
35391
35502
  async function createFileSymlink(source, target) {
35392
- await import_fs_extra12.default.ensureDir(path16.dirname(target));
35393
- if (os13.platform() === "win32") {
35394
- await import_fs_extra12.default.symlink(source, target, "file");
35503
+ await import_fs_extra13.default.ensureDir(path17.dirname(target));
35504
+ if (os14.platform() === "win32") {
35505
+ await import_fs_extra13.default.symlink(source, target, "file");
35395
35506
  return;
35396
35507
  }
35397
- await import_fs_extra12.default.symlink(source, target);
35508
+ await import_fs_extra13.default.symlink(source, target);
35398
35509
  }
35399
35510
  async function shouldLinkMissingContainer(candidate) {
35400
35511
  if (candidate.linkWhenMissing)
35401
35512
  return true;
35402
35513
  if (!candidate.linkWhenParentExists)
35403
35514
  return false;
35404
- return import_fs_extra12.default.pathExists(path16.dirname(candidate.path));
35515
+ return import_fs_extra13.default.pathExists(path17.dirname(candidate.path));
35405
35516
  }
35406
35517
  async function linkContainer(candidate, destinationDir, result) {
35407
35518
  if (candidate.linkSource === false) {
@@ -35411,7 +35522,7 @@ async function linkContainer(candidate, destinationDir, result) {
35411
35522
  return;
35412
35523
  }
35413
35524
  const destinationRealPath = await realPathIfPossible(destinationDir);
35414
- const stat = await import_fs_extra12.default.lstat(candidate.path).catch(() => null);
35525
+ const stat = await import_fs_extra13.default.lstat(candidate.path).catch(() => null);
35415
35526
  if (!stat) {
35416
35527
  if (!await shouldLinkMissingContainer(candidate))
35417
35528
  return;
@@ -35433,7 +35544,7 @@ async function linkContainer(candidate, destinationDir, result) {
35433
35544
  });
35434
35545
  return;
35435
35546
  }
35436
- await import_fs_extra12.default.remove(candidate.path);
35547
+ await import_fs_extra13.default.remove(candidate.path);
35437
35548
  await createDirectorySymlink(destinationDir, candidate.path);
35438
35549
  result.linked.push({
35439
35550
  category: candidate.category,
@@ -35443,9 +35554,9 @@ async function linkContainer(candidate, destinationDir, result) {
35443
35554
  return;
35444
35555
  }
35445
35556
  const backupRoot = await ensureBackupPath(result);
35446
- const backupTarget = path16.join(backupRoot, safeRelativePath(result.rootDir, candidate.path));
35447
- await import_fs_extra12.default.ensureDir(path16.dirname(backupTarget));
35448
- await import_fs_extra12.default.move(candidate.path, backupTarget, { overwrite: false });
35557
+ const backupTarget = path17.join(backupRoot, safeRelativePath(result.rootDir, candidate.path));
35558
+ await import_fs_extra13.default.ensureDir(path17.dirname(backupTarget));
35559
+ await import_fs_extra13.default.move(candidate.path, backupTarget, { overwrite: false });
35449
35560
  await createDirectorySymlink(destinationDir, candidate.path);
35450
35561
  result.linked.push({
35451
35562
  category: candidate.category,
@@ -35455,29 +35566,37 @@ async function linkContainer(candidate, destinationDir, result) {
35455
35566
  });
35456
35567
  }
35457
35568
  async function importInstructionFiles(candidates, destinationPath, result) {
35458
- await import_fs_extra12.default.ensureDir(path16.dirname(destinationPath));
35459
- const destinationRealPath = await realPathIfPossible(destinationPath);
35460
- let destinationHash = await pathExistsOrSymlink(destinationPath) ? await hashPath(destinationPath) : null;
35569
+ const destinationExists = await pathExistsOrSymlink(destinationPath);
35570
+ const destinationRealPath = destinationExists ? await realPathIfPossible(destinationPath) : null;
35571
+ const sourceCandidates = [];
35461
35572
  for (const candidate of candidates) {
35462
35573
  if (candidate.isDestination)
35463
35574
  continue;
35464
- const sourceStat = await import_fs_extra12.default.lstat(candidate.path).catch(() => null);
35575
+ const sourceStat = await import_fs_extra13.default.lstat(candidate.path).catch(() => null);
35465
35576
  if (!sourceStat)
35466
35577
  continue;
35467
35578
  const sourceRealPath = await realPathIfPossible(candidate.path);
35468
35579
  if (destinationRealPath && sourceRealPath && samePath(destinationRealPath, sourceRealPath)) {
35469
35580
  continue;
35470
35581
  }
35582
+ sourceCandidates.push(candidate);
35583
+ }
35584
+ if (!destinationExists && sourceCandidates.length === 0) {
35585
+ return false;
35586
+ }
35587
+ await import_fs_extra13.default.ensureDir(path17.dirname(destinationPath));
35588
+ let destinationHash = destinationExists ? await hashPath(destinationPath) : null;
35589
+ for (const candidate of sourceCandidates) {
35471
35590
  const sourceHash = await hashPath(candidate.path);
35472
35591
  if (!destinationHash) {
35473
- await import_fs_extra12.default.copy(candidate.path, destinationPath, {
35592
+ await import_fs_extra13.default.copy(candidate.path, destinationPath, {
35474
35593
  dereference: false,
35475
35594
  overwrite: false
35476
35595
  });
35477
35596
  destinationHash = sourceHash;
35478
35597
  result.imported.push({
35479
35598
  category: "instructions",
35480
- name: path16.basename(candidate.path),
35599
+ name: path17.basename(candidate.path),
35481
35600
  from: candidate.path,
35482
35601
  to: destinationPath
35483
35602
  });
@@ -35486,26 +35605,27 @@ async function importInstructionFiles(candidates, destinationPath, result) {
35486
35605
  if (sourceHash === destinationHash) {
35487
35606
  result.duplicates.push({
35488
35607
  category: "instructions",
35489
- name: path16.basename(candidate.path),
35608
+ name: path17.basename(candidate.path),
35490
35609
  from: candidate.path,
35491
35610
  keptAs: destinationPath
35492
35611
  });
35493
35612
  continue;
35494
35613
  }
35495
- const targetName = await findTargetName(path16.dirname(destinationPath), path16.basename(destinationPath), candidate.label);
35496
- const targetPath = path16.join(path16.dirname(destinationPath), targetName);
35497
- await import_fs_extra12.default.copy(candidate.path, targetPath, {
35614
+ const targetName = await findTargetName(path17.dirname(destinationPath), path17.basename(destinationPath), candidate.label);
35615
+ const targetPath = path17.join(path17.dirname(destinationPath), targetName);
35616
+ await import_fs_extra13.default.copy(candidate.path, targetPath, {
35498
35617
  dereference: false,
35499
35618
  overwrite: false
35500
35619
  });
35501
35620
  result.renamed.push({
35502
35621
  category: "instructions",
35503
- name: path16.basename(candidate.path),
35622
+ name: path17.basename(candidate.path),
35504
35623
  from: candidate.path,
35505
35624
  to: targetPath,
35506
35625
  reason: "Shared instruction file already exists with different content"
35507
35626
  });
35508
35627
  }
35628
+ return true;
35509
35629
  }
35510
35630
  async function shouldLinkMissingInstruction(candidate) {
35511
35631
  if (candidate.linkWhenMissing)
@@ -35520,7 +35640,7 @@ async function linkInstructionFile(candidate, destinationPath, result) {
35520
35640
  return;
35521
35641
  }
35522
35642
  const destinationRealPath = await realPathIfPossible(destinationPath);
35523
- const stat = await import_fs_extra12.default.lstat(candidate.path).catch(() => null);
35643
+ const stat = await import_fs_extra13.default.lstat(candidate.path).catch(() => null);
35524
35644
  if (!stat) {
35525
35645
  if (!await shouldLinkMissingInstruction(candidate))
35526
35646
  return;
@@ -35542,7 +35662,7 @@ async function linkInstructionFile(candidate, destinationPath, result) {
35542
35662
  });
35543
35663
  return;
35544
35664
  }
35545
- await import_fs_extra12.default.remove(candidate.path);
35665
+ await import_fs_extra13.default.remove(candidate.path);
35546
35666
  await createFileSymlink(destinationPath, candidate.path);
35547
35667
  result.linked.push({
35548
35668
  category: "instructions",
@@ -35552,9 +35672,9 @@ async function linkInstructionFile(candidate, destinationPath, result) {
35552
35672
  return;
35553
35673
  }
35554
35674
  const backupRoot = await ensureBackupPath(result);
35555
- const backupTarget = path16.join(backupRoot, safeRelativePath(result.rootDir, candidate.path));
35556
- await import_fs_extra12.default.ensureDir(path16.dirname(backupTarget));
35557
- await import_fs_extra12.default.move(candidate.path, backupTarget, { overwrite: false });
35675
+ const backupTarget = path17.join(backupRoot, safeRelativePath(result.rootDir, candidate.path));
35676
+ await import_fs_extra13.default.ensureDir(path17.dirname(backupTarget));
35677
+ await import_fs_extra13.default.move(candidate.path, backupTarget, { overwrite: false });
35558
35678
  await createFileSymlink(destinationPath, candidate.path);
35559
35679
  result.linked.push({
35560
35680
  category: "instructions",
@@ -35566,31 +35686,31 @@ async function linkInstructionFile(candidate, destinationPath, result) {
35566
35686
  var RULES_INDEX_START = "<!-- AIBLUEPRINT:RULES:START -->";
35567
35687
  var RULES_INDEX_END = "<!-- AIBLUEPRINT:RULES:END -->";
35568
35688
  function titleFromRulePath(rulePath) {
35569
- return path16.basename(rulePath, path16.extname(rulePath)).replace(/[-_]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
35689
+ return path17.basename(rulePath, path17.extname(rulePath)).replace(/[-_]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
35570
35690
  }
35571
35691
  async function readRuleTitle(rulePath) {
35572
- const content = await import_fs_extra12.default.readFile(rulePath, "utf-8").catch(() => "");
35692
+ const content = await import_fs_extra13.default.readFile(rulePath, "utf-8").catch(() => "");
35573
35693
  const heading = content.match(/^#\s+(.+)$/m)?.[1]?.trim();
35574
35694
  return heading || titleFromRulePath(rulePath);
35575
35695
  }
35576
35696
  async function collectRuleIndexEntries(rulesDir, rootDir, currentDir = rulesDir) {
35577
- const entries = await import_fs_extra12.default.readdir(currentDir, { withFileTypes: true }).catch(() => []);
35697
+ const entries = await import_fs_extra13.default.readdir(currentDir, { withFileTypes: true }).catch(() => []);
35578
35698
  const rules = [];
35579
35699
  for (const entry of entries) {
35580
35700
  if (IGNORED_ENTRY_NAMES2.has(entry.name))
35581
35701
  continue;
35582
- const entryPath = path16.join(currentDir, entry.name);
35702
+ const entryPath = path17.join(currentDir, entry.name);
35583
35703
  if (entry.isDirectory()) {
35584
35704
  rules.push(...await collectRuleIndexEntries(rulesDir, rootDir, entryPath));
35585
35705
  continue;
35586
35706
  }
35587
35707
  if (!entry.isFile() && !entry.isSymbolicLink())
35588
35708
  continue;
35589
- if (![".md", ".mdc"].includes(path16.extname(entry.name).toLowerCase()))
35709
+ if (![".md", ".mdc"].includes(path17.extname(entry.name).toLowerCase()))
35590
35710
  continue;
35591
35711
  rules.push({
35592
35712
  title: await readRuleTitle(entryPath),
35593
- relativePath: path16.relative(rootDir, entryPath)
35713
+ relativePath: path17.relative(rootDir, entryPath)
35594
35714
  });
35595
35715
  }
35596
35716
  return rules.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
@@ -35628,10 +35748,10 @@ function renderRulesIndexBlock(rules) {
35628
35748
  `);
35629
35749
  }
35630
35750
  async function readExistingInstructions(rootDir) {
35631
- const agentsPath = path16.join(rootDir, "AGENTS.md");
35632
- const claudePath = path16.join(rootDir, "CLAUDE.md");
35633
- const agentsContent = await import_fs_extra12.default.readFile(agentsPath, "utf-8").catch(() => null);
35634
- const claudeContent = await import_fs_extra12.default.readFile(claudePath, "utf-8").catch(() => null);
35751
+ const agentsPath = path17.join(rootDir, "AGENTS.md");
35752
+ const claudePath = path17.join(rootDir, "CLAUDE.md");
35753
+ const agentsContent = await import_fs_extra13.default.readFile(agentsPath, "utf-8").catch(() => null);
35754
+ const claudeContent = await import_fs_extra13.default.readFile(claudePath, "utf-8").catch(() => null);
35635
35755
  if (agentsContent !== null && claudeContent !== null && agentsContent.trim() !== claudeContent.trim()) {
35636
35756
  return `${agentsContent.trimEnd()}
35637
35757
 
@@ -35647,26 +35767,26 @@ ${claudeContent.trimStart()}`;
35647
35767
  `;
35648
35768
  }
35649
35769
  async function copyPathToBackup(result, targetPath) {
35650
- const stat = await import_fs_extra12.default.lstat(targetPath).catch(() => null);
35770
+ const stat = await import_fs_extra13.default.lstat(targetPath).catch(() => null);
35651
35771
  if (!stat)
35652
35772
  return null;
35653
35773
  const backupRoot = await ensureBackupPath(result);
35654
- const backupTarget = path16.join(backupRoot, safeRelativePath(result.rootDir, targetPath));
35655
- await import_fs_extra12.default.ensureDir(path16.dirname(backupTarget));
35656
- await import_fs_extra12.default.copy(targetPath, backupTarget, {
35774
+ const backupTarget = path17.join(backupRoot, safeRelativePath(result.rootDir, targetPath));
35775
+ await import_fs_extra13.default.ensureDir(path17.dirname(backupTarget));
35776
+ await import_fs_extra13.default.copy(targetPath, backupTarget, {
35657
35777
  dereference: false,
35658
35778
  overwrite: false
35659
35779
  });
35660
35780
  return backupTarget;
35661
35781
  }
35662
35782
  async function replaceWithFileSymlink(sourcePath, targetPath) {
35663
- await import_fs_extra12.default.ensureDir(path16.dirname(targetPath));
35664
- const relativeSource = path16.relative(path16.dirname(targetPath), sourcePath) || path16.basename(sourcePath);
35665
- await import_fs_extra12.default.symlink(relativeSource, targetPath, "file");
35783
+ await import_fs_extra13.default.ensureDir(path17.dirname(targetPath));
35784
+ const relativeSource = path17.relative(path17.dirname(targetPath), sourcePath) || path17.basename(sourcePath);
35785
+ await import_fs_extra13.default.symlink(relativeSource, targetPath, "file");
35666
35786
  }
35667
35787
  async function ensureClaudeInstructionSymlink(result, agentsPath, claudePath) {
35668
35788
  const agentsRealPath = await realPathIfPossible(agentsPath);
35669
- const claudeStat = await import_fs_extra12.default.lstat(claudePath).catch(() => null);
35789
+ const claudeStat = await import_fs_extra13.default.lstat(claudePath).catch(() => null);
35670
35790
  if (claudeStat?.isSymbolicLink()) {
35671
35791
  const claudeRealPath = await realPathIfPossible(claudePath);
35672
35792
  if (agentsRealPath && claudeRealPath && samePath(agentsRealPath, claudeRealPath)) {
@@ -35675,29 +35795,34 @@ async function ensureClaudeInstructionSymlink(result, agentsPath, claudePath) {
35675
35795
  }
35676
35796
  if (claudeStat) {
35677
35797
  await copyPathToBackup(result, claudePath);
35678
- await import_fs_extra12.default.remove(claudePath);
35798
+ await import_fs_extra13.default.remove(claudePath);
35679
35799
  }
35680
35800
  await replaceWithFileSymlink(agentsPath, claudePath);
35681
35801
  }
35682
35802
  async function writeRepositoryInstructionIndex(result, folders) {
35683
- const rulesDir = path16.join(folders.agentsDir, "rules");
35684
- await import_fs_extra12.default.ensureDir(rulesDir);
35803
+ const rulesDir = path17.join(folders.agentsDir, "rules");
35804
+ if (!await pathExistsOrSymlink(rulesDir)) {
35805
+ return;
35806
+ }
35685
35807
  const rules = await collectRuleIndexEntries(rulesDir, folders.rootDir);
35808
+ if (rules.length === 0) {
35809
+ return;
35810
+ }
35686
35811
  const existing = stripGeneratedRulesIndex(await readExistingInstructions(folders.rootDir));
35687
35812
  const content = `${existing}
35688
35813
 
35689
35814
  ${renderRulesIndexBlock(rules)}
35690
35815
  `;
35691
- const agentsPath = path16.join(folders.rootDir, "AGENTS.md");
35692
- const claudePath = path16.join(folders.rootDir, "CLAUDE.md");
35693
- const agentsStat = await import_fs_extra12.default.lstat(agentsPath).catch(() => null);
35816
+ const agentsPath = path17.join(folders.rootDir, "AGENTS.md");
35817
+ const claudePath = path17.join(folders.rootDir, "CLAUDE.md");
35818
+ const agentsStat = await import_fs_extra13.default.lstat(agentsPath).catch(() => null);
35694
35819
  if (agentsStat) {
35695
35820
  await copyPathToBackup(result, agentsPath);
35696
35821
  if (agentsStat.isSymbolicLink()) {
35697
- await import_fs_extra12.default.remove(agentsPath);
35822
+ await import_fs_extra13.default.remove(agentsPath);
35698
35823
  }
35699
35824
  }
35700
- await import_fs_extra12.default.writeFile(agentsPath, content, "utf-8");
35825
+ await import_fs_extra13.default.writeFile(agentsPath, content, "utf-8");
35701
35826
  await ensureClaudeInstructionSymlink(result, agentsPath, claudePath);
35702
35827
  result.instructionIndex = {
35703
35828
  agentsPath,
@@ -35708,7 +35833,8 @@ ${renderRulesIndexBlock(rules)}
35708
35833
  async function unifyAgentsConfiguration(options = {}) {
35709
35834
  const scope = options.scope ?? "global";
35710
35835
  const folders = resolveFolders(options);
35711
- const instructionCandidates = getInstructionFileCandidates(options);
35836
+ const includeCodex = scope !== "repository";
35837
+ const instructionCandidates = getInstructionFileCandidates(options, includeCodex);
35712
35838
  const candidates = scope === "repository" ? getRepositoryContainerCandidates(options) : getContainerCandidates(options);
35713
35839
  const result = {
35714
35840
  rootDir: folders.rootDir,
@@ -35724,18 +35850,23 @@ async function unifyAgentsConfiguration(options = {}) {
35724
35850
  instructionIndex: null
35725
35851
  };
35726
35852
  const destinationByCategory = {
35727
- skills: path16.join(folders.agentsDir, "skills"),
35728
- agents: path16.join(folders.agentsDir, "agents"),
35729
- instructions: path16.join(folders.agentsDir, "AGENTS.md"),
35730
- rules: path16.join(folders.agentsDir, "rules")
35853
+ skills: path17.join(folders.agentsDir, "skills"),
35854
+ agents: path17.join(folders.agentsDir, "agents"),
35855
+ instructions: path17.join(folders.agentsDir, "AGENTS.md"),
35856
+ rules: path17.join(folders.agentsDir, "rules")
35731
35857
  };
35732
- await import_fs_extra12.default.ensureDir(folders.agentsDir);
35733
35858
  await importInstructionFiles(instructionCandidates, destinationByCategory.instructions, result);
35734
35859
  const categories = scope === "repository" ? ["skills", "agents", "rules"] : ["skills", "agents"];
35860
+ const activeCategories = new Set;
35735
35861
  for (const category of categories) {
35736
- await importCategoryEntries(category, candidates, destinationByCategory[category], result);
35862
+ const isActive = await importCategoryEntries(category, candidates, destinationByCategory[category], result);
35863
+ if (isActive) {
35864
+ activeCategories.add(category);
35865
+ }
35737
35866
  }
35738
35867
  for (const candidate of candidates) {
35868
+ if (!activeCategories.has(candidate.category))
35869
+ continue;
35739
35870
  await linkContainer(candidate, destinationByCategory[candidate.category], result);
35740
35871
  }
35741
35872
  for (const candidate of instructionCandidates) {
@@ -35765,9 +35896,9 @@ async function agentsUnifyCommand(params = {}) {
35765
35896
  AIBlueprint agents unify ${source_default.gray(`v${getVersion()}`)}
35766
35897
  `));
35767
35898
  console.log(source_default.gray(`Scope: ${params.scope ?? "global"}`));
35768
- console.log(source_default.gray("Centralizing reusable agent configuration into .agents, then rendering Codex agents"));
35899
+ console.log(source_default.gray(params.scope === "repository" ? "Centralizing project agent configuration into .agents" : "Centralizing reusable agent configuration into .agents, then rendering Codex agents"));
35769
35900
  const result = await unifyAgentsConfiguration(params);
35770
- const codexResult = await renderCodexAgentsFromMarkdown(params);
35901
+ const codexResult = params.scope === "repository" ? null : await renderCodexAgentsFromMarkdown(params);
35771
35902
  console.log(source_default.green(`
35772
35903
  Unify complete`));
35773
35904
  console.log(source_default.gray(` Shared folder: ${result.agentsDir}`));
@@ -35777,7 +35908,9 @@ Unify complete`));
35777
35908
  if (result.scope === "repository") {
35778
35909
  printCategorySummary(result, "rules");
35779
35910
  }
35780
- console.log(source_default.gray(` codex agents: ${codexResult.rendered.length} rendered, ${codexResult.skipped.length} skipped`));
35911
+ if (codexResult) {
35912
+ console.log(source_default.gray(` codex agents: ${codexResult.rendered.length} rendered, ${codexResult.skipped.length} skipped`));
35913
+ }
35781
35914
  if (result.instructionIndex) {
35782
35915
  console.log(source_default.gray(` rules index: ${result.instructionIndex.indexedRules.length} rules indexed in ${result.instructionIndex.agentsPath}`));
35783
35916
  console.log(source_default.gray(` Claude instructions: ${result.instructionIndex.claudePath}`));
@@ -35832,120 +35965,6 @@ var import_fs_extra14 = __toESM(require_lib4(), 1);
35832
35965
  import crypto2 from "crypto";
35833
35966
  import os15 from "os";
35834
35967
  import path18 from "path";
35835
-
35836
- // src/lib/backup-utils.ts
35837
- var import_fs_extra13 = __toESM(require_lib4(), 1);
35838
- import path17 from "path";
35839
- import os14 from "os";
35840
- var BACKUP_BASE_DIR = path17.join(os14.homedir(), ".config", "aiblueprint", "backup");
35841
- function getBackupDir() {
35842
- return process.env.AIBLUEPRINT_BACKUP_DIR || BACKUP_BASE_DIR;
35843
- }
35844
- function formatDate(date) {
35845
- const pad = (n) => n.toString().padStart(2, "0");
35846
- return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
35847
- }
35848
- async function listBackups() {
35849
- const backupBaseDir = getBackupDir();
35850
- const exists = await import_fs_extra13.default.pathExists(backupBaseDir);
35851
- if (!exists) {
35852
- return [];
35853
- }
35854
- const entries = await import_fs_extra13.default.readdir(backupBaseDir, { withFileTypes: true });
35855
- const backups = [];
35856
- for (const entry of entries) {
35857
- if (!entry.isDirectory())
35858
- continue;
35859
- const match = entry.name.match(/^(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})$/);
35860
- if (!match)
35861
- continue;
35862
- const [, year, month, day, hour, minute, second] = match;
35863
- const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day), parseInt(hour), parseInt(minute), parseInt(second));
35864
- backups.push({
35865
- name: entry.name,
35866
- path: path17.join(backupBaseDir, entry.name),
35867
- date
35868
- });
35869
- }
35870
- return backups.sort((a, b) => b.date.getTime() - a.date.getTime());
35871
- }
35872
- var AGENTS_BACKUP_SUBDIR = ".agents";
35873
- var CLAUDE_ITEMS = ["commands", "agents", "skills", "scripts", "settings.json"];
35874
- var MANAGED_FOLDERS = [".claude", ".codex", ".agents"];
35875
- async function copyForBackup(sourcePath, destPath) {
35876
- await import_fs_extra13.default.copy(sourcePath, destPath, {
35877
- overwrite: true,
35878
- dereference: false
35879
- });
35880
- }
35881
- async function hasMeaningfulContent(dir) {
35882
- if (!await import_fs_extra13.default.pathExists(dir))
35883
- return false;
35884
- const files = await import_fs_extra13.default.readdir(dir);
35885
- return files.some((f) => f !== ".DS_Store");
35886
- }
35887
- async function loadBackup(backupPath, claudeDir, codexDir, agentsDir) {
35888
- const exists = await import_fs_extra13.default.pathExists(backupPath);
35889
- if (!exists) {
35890
- throw new Error(`Backup not found: ${backupPath}`);
35891
- }
35892
- const managedDestinations = {
35893
- ".claude": claudeDir,
35894
- ".codex": codexDir,
35895
- ".agents": agentsDir
35896
- };
35897
- let restoredManagedFolder = false;
35898
- for (const folderName of MANAGED_FOLDERS) {
35899
- const sourcePath = path17.join(backupPath, folderName);
35900
- const destPath = managedDestinations[folderName];
35901
- if (!destPath || !await import_fs_extra13.default.pathExists(sourcePath))
35902
- continue;
35903
- await import_fs_extra13.default.ensureDir(destPath);
35904
- await copyForBackup(sourcePath, destPath);
35905
- restoredManagedFolder = true;
35906
- }
35907
- if (!restoredManagedFolder) {
35908
- await import_fs_extra13.default.ensureDir(claudeDir);
35909
- for (const item of CLAUDE_ITEMS) {
35910
- const sourcePath = path17.join(backupPath, item);
35911
- const destPath = path17.join(claudeDir, item);
35912
- if (await import_fs_extra13.default.pathExists(sourcePath)) {
35913
- await copyForBackup(sourcePath, destPath);
35914
- }
35915
- }
35916
- if (agentsDir) {
35917
- const agentsBackupPath = path17.join(backupPath, AGENTS_BACKUP_SUBDIR);
35918
- if (await import_fs_extra13.default.pathExists(agentsBackupPath)) {
35919
- await import_fs_extra13.default.ensureDir(agentsDir);
35920
- await copyForBackup(agentsBackupPath, agentsDir);
35921
- }
35922
- }
35923
- }
35924
- }
35925
- async function createBackup(claudeDir, codexDir, agentsDir) {
35926
- const claudeHasContent = await hasMeaningfulContent(claudeDir);
35927
- const codexHasContent = codexDir ? await hasMeaningfulContent(codexDir) : false;
35928
- const agentsHasContent = agentsDir ? await hasMeaningfulContent(agentsDir) : false;
35929
- if (!claudeHasContent && !codexHasContent && !agentsHasContent) {
35930
- return null;
35931
- }
35932
- const timestamp3 = formatDate(new Date);
35933
- const backupPath = path17.join(getBackupDir(), timestamp3);
35934
- await import_fs_extra13.default.ensureDir(backupPath);
35935
- if (claudeHasContent) {
35936
- await copyForBackup(claudeDir, path17.join(backupPath, ".claude"));
35937
- }
35938
- if (codexHasContent && codexDir) {
35939
- await copyForBackup(codexDir, path17.join(backupPath, ".codex"));
35940
- }
35941
- if (agentsHasContent && agentsDir) {
35942
- const destPath = path17.join(backupPath, AGENTS_BACKUP_SUBDIR);
35943
- await copyForBackup(agentsDir, destPath);
35944
- }
35945
- return backupPath;
35946
- }
35947
-
35948
- // src/lib/session-unifier.ts
35949
35968
  var MANAGED_FOLDERS2 = [".claude", ".codex", ".agents"];
35950
35969
  var SESSION_PATHS = {
35951
35970
  ".claude": ["projects", "sessions"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiblueprint-cli",
3
- "version": "1.4.66",
3
+ "version": "1.4.68",
4
4
  "description": "AIBlueprint CLI for setting up AI coding configurations",
5
5
  "author": "AIBlueprint",
6
6
  "license": "MIT",