aiblueprint-cli 1.1.4 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -32360,10 +32360,9 @@ var inquirer = {
32360
32360
  var lib_default = inquirer;
32361
32361
 
32362
32362
  // src/commands/setup.ts
32363
- var import_fs_extra = __toESM(require_lib4(), 1);
32364
- import path2 from "path";
32365
- import os3 from "os";
32366
- import { execSync } from "child_process";
32363
+ var import_fs_extra5 = __toESM(require_lib4(), 1);
32364
+ import path6 from "path";
32365
+ import os5 from "os";
32367
32366
 
32368
32367
  // node_modules/chalk/source/vendor/ansi-styles/index.js
32369
32368
  var ANSI_BACKGROUND_OFFSET = 10;
@@ -32858,8 +32857,278 @@ var source_default = chalk;
32858
32857
  // src/commands/setup.ts
32859
32858
  import { fileURLToPath } from "url";
32860
32859
  import { dirname } from "path";
32861
- var __filename2 = fileURLToPath(import.meta.url);
32862
- var __dirname2 = dirname(__filename2);
32860
+
32861
+ // src/commands/setup/shell-shortcuts.ts
32862
+ var import_fs_extra = __toESM(require_lib4(), 1);
32863
+ import path2 from "path";
32864
+ import os3 from "os";
32865
+ async function setupShellShortcuts() {
32866
+ try {
32867
+ const platform = os3.platform();
32868
+ let shellConfigFile;
32869
+ if (platform === "darwin") {
32870
+ shellConfigFile = path2.join(os3.homedir(), ".zshenv");
32871
+ } else if (platform === "linux") {
32872
+ const shell = process.env.SHELL || "";
32873
+ if (shell.includes("zsh")) {
32874
+ shellConfigFile = path2.join(os3.homedir(), ".zshrc");
32875
+ } else {
32876
+ shellConfigFile = path2.join(os3.homedir(), ".bashrc");
32877
+ }
32878
+ } else {
32879
+ console.log(source_default.yellow("Shell shortcuts are only supported on macOS and Linux"));
32880
+ return;
32881
+ }
32882
+ const aliases = `
32883
+ # AIBlueprint Claude Code aliases
32884
+ alias cc="claude --dangerously-skip-permissions"
32885
+ alias ccc="claude --dangerously-skip-permissions -c"
32886
+ `;
32887
+ const existingContent = await import_fs_extra.default.readFile(shellConfigFile, "utf-8").catch(() => "");
32888
+ if (!existingContent.includes("AIBlueprint Claude Code aliases")) {
32889
+ await import_fs_extra.default.appendFile(shellConfigFile, aliases);
32890
+ }
32891
+ } catch (error) {
32892
+ console.error(source_default.red("Error setting up shell shortcuts:"), error);
32893
+ throw error;
32894
+ }
32895
+ }
32896
+
32897
+ // src/commands/setup/symlinks.ts
32898
+ var import_fs_extra2 = __toESM(require_lib4(), 1);
32899
+ import path3 from "path";
32900
+ import os4 from "os";
32901
+ async function getToolPaths(tool, customFolder) {
32902
+ let baseDir;
32903
+ switch (tool) {
32904
+ case "claude-code":
32905
+ baseDir = customFolder ? path3.resolve(customFolder) : path3.join(os4.homedir(), ".claude");
32906
+ return {
32907
+ baseDir,
32908
+ commandsPath: path3.join(baseDir, "commands"),
32909
+ agentsPath: path3.join(baseDir, "agents")
32910
+ };
32911
+ case "codex":
32912
+ baseDir = customFolder ? path3.resolve(customFolder) : path3.join(os4.homedir(), ".codex");
32913
+ return {
32914
+ baseDir,
32915
+ commandsPath: path3.join(baseDir, "prompts")
32916
+ };
32917
+ case "opencode":
32918
+ baseDir = customFolder ? path3.resolve(customFolder) : path3.join(os4.homedir(), ".config", "opencode");
32919
+ return {
32920
+ baseDir,
32921
+ commandsPath: path3.join(baseDir, "command")
32922
+ };
32923
+ case "factoryai":
32924
+ baseDir = customFolder ? path3.resolve(customFolder) : path3.join(os4.homedir(), ".factory");
32925
+ return {
32926
+ baseDir,
32927
+ commandsPath: path3.join(baseDir, "commands"),
32928
+ agentsPath: path3.join(baseDir, "droids")
32929
+ };
32930
+ default:
32931
+ throw new Error(`Unknown tool type: ${tool}`);
32932
+ }
32933
+ }
32934
+ async function createSymlink(sourcePath, targetPath, options = {}) {
32935
+ try {
32936
+ const sourceExists = await import_fs_extra2.default.pathExists(sourcePath);
32937
+ if (!sourceExists) {
32938
+ console.log(source_default.yellow(` Source path ${sourcePath} does not exist. Skipping symlink creation...`));
32939
+ return false;
32940
+ }
32941
+ const targetDir = path3.dirname(targetPath);
32942
+ await import_fs_extra2.default.ensureDir(targetDir);
32943
+ const targetExists = await import_fs_extra2.default.pathExists(targetPath);
32944
+ if (targetExists) {
32945
+ const stat = await import_fs_extra2.default.lstat(targetPath);
32946
+ if (stat.isSymbolicLink()) {
32947
+ await import_fs_extra2.default.remove(targetPath);
32948
+ } else {
32949
+ console.log(source_default.yellow(options.skipMessage || ` ${targetPath} already exists and is not a symlink. Skipping...`));
32950
+ return false;
32951
+ }
32952
+ }
32953
+ await import_fs_extra2.default.symlink(sourcePath, targetPath);
32954
+ return true;
32955
+ } catch (error) {
32956
+ console.error(source_default.red(options.errorPrefix || "Error creating symlink:"), error);
32957
+ throw error;
32958
+ }
32959
+ }
32960
+ async function setupCodexSymlink(claudeDir, customCodexFolder, customClaudeCodeFolder) {
32961
+ try {
32962
+ let codexDir;
32963
+ if (customCodexFolder) {
32964
+ codexDir = path3.resolve(customCodexFolder);
32965
+ } else if (customClaudeCodeFolder) {
32966
+ const parentDir = path3.dirname(claudeDir);
32967
+ codexDir = path3.join(parentDir, "codex");
32968
+ } else {
32969
+ codexDir = path3.join(os4.homedir(), ".codex");
32970
+ }
32971
+ const promptsPath = path3.join(codexDir, "prompts");
32972
+ const commandsPath = path3.join(claudeDir, "commands");
32973
+ await createSymlink(commandsPath, promptsPath, {
32974
+ skipMessage: " ~/.codex/prompts already exists and is not a symlink. Skipping...",
32975
+ errorPrefix: "Error setting up Codex symlink:"
32976
+ });
32977
+ } catch (error) {
32978
+ console.error(source_default.red("Error setting up Codex symlink:"), error);
32979
+ }
32980
+ }
32981
+ async function setupOpenCodeSymlink(claudeDir, customOpenCodeFolder, customClaudeCodeFolder) {
32982
+ try {
32983
+ let openCodeDir;
32984
+ if (customOpenCodeFolder) {
32985
+ openCodeDir = path3.resolve(customOpenCodeFolder);
32986
+ } else if (customClaudeCodeFolder) {
32987
+ const parentDir = path3.dirname(claudeDir);
32988
+ openCodeDir = path3.join(parentDir, ".opencode");
32989
+ } else {
32990
+ openCodeDir = path3.join(os4.homedir(), ".config", "opencode");
32991
+ }
32992
+ const commandPath = path3.join(openCodeDir, "command");
32993
+ const commandsPath = path3.join(claudeDir, "commands");
32994
+ await createSymlink(commandsPath, commandPath, {
32995
+ skipMessage: " ~/.config/opencode/command already exists and is not a symlink. Skipping...",
32996
+ errorPrefix: "Error setting up OpenCode symlink:"
32997
+ });
32998
+ } catch (error) {
32999
+ console.error(source_default.red("Error setting up OpenCode symlink:"), error);
33000
+ }
33001
+ }
33002
+
33003
+ // src/commands/setup/dependencies.ts
33004
+ import { execSync } from "child_process";
33005
+ async function checkAndInstallDependencies() {
33006
+ const checkCommand = (cmd) => {
33007
+ try {
33008
+ execSync(`which ${cmd}`, { stdio: "ignore" });
33009
+ return true;
33010
+ } catch {
33011
+ return false;
33012
+ }
33013
+ };
33014
+ if (!checkCommand("bun")) {
33015
+ console.log(source_default.yellow(`
33016
+ Installing bun...`));
33017
+ try {
33018
+ execSync("npm install -g bun", { stdio: "inherit" });
33019
+ } catch (error) {
33020
+ console.log(source_default.red(" Failed to install bun. Please install it manually: npm install -g bun"));
33021
+ }
33022
+ }
33023
+ if (!checkCommand("ccusage")) {
33024
+ console.log(source_default.yellow(`
33025
+ Installing ccusage...`));
33026
+ try {
33027
+ execSync("npm install -g ccusage", { stdio: "inherit" });
33028
+ } catch (error) {
33029
+ console.log(source_default.red(" Failed to install ccusage. Please install it manually: npm install -g ccusage"));
33030
+ }
33031
+ }
33032
+ }
33033
+
33034
+ // src/commands/setup/settings.ts
33035
+ var import_fs_extra3 = __toESM(require_lib4(), 1);
33036
+ import path4 from "path";
33037
+ async function updateSettings(options, claudeDir) {
33038
+ const settingsPath = path4.join(claudeDir, "settings.json");
33039
+ let settings = {};
33040
+ try {
33041
+ const existingSettings = await import_fs_extra3.default.readFile(settingsPath, "utf-8");
33042
+ settings = JSON.parse(existingSettings);
33043
+ } catch {
33044
+ }
33045
+ if (options.customStatusline) {
33046
+ if (settings.statusLine) {
33047
+ const confirmAnswer = await lib_default.prompt([
33048
+ {
33049
+ type: "confirm",
33050
+ name: "replace",
33051
+ message: "You already have a statusLine configuration. Replace it?"
33052
+ }
33053
+ ]);
33054
+ if (!confirmAnswer.replace) {
33055
+ console.log(source_default.yellow(" Keeping existing statusLine configuration"));
33056
+ } else {
33057
+ settings.statusLine = {
33058
+ type: "command",
33059
+ command: `bash ${path4.join(claudeDir, "scripts/statusline-ccusage.sh")}`,
33060
+ padding: 0
33061
+ };
33062
+ }
33063
+ } else {
33064
+ settings.statusLine = {
33065
+ type: "command",
33066
+ command: `bash ${path4.join(claudeDir, "scripts/statusline-ccusage.sh")}`,
33067
+ padding: 0
33068
+ };
33069
+ }
33070
+ }
33071
+ if (!settings.hooks) {
33072
+ settings.hooks = {};
33073
+ }
33074
+ if (options.commandValidation) {
33075
+ if (!settings.hooks.PreToolUse) {
33076
+ settings.hooks.PreToolUse = [];
33077
+ }
33078
+ const bashHook = {
33079
+ matcher: "Bash",
33080
+ hooks: [
33081
+ {
33082
+ type: "command",
33083
+ command: `bun ${path4.join(claudeDir, "scripts/validate-command.js")}`
33084
+ }
33085
+ ]
33086
+ };
33087
+ const existingBashHook = settings.hooks.PreToolUse.find((h) => h.matcher === "Bash");
33088
+ if (!existingBashHook) {
33089
+ settings.hooks.PreToolUse.push(bashHook);
33090
+ }
33091
+ }
33092
+ if (options.notificationSounds) {
33093
+ if (!settings.hooks.Stop) {
33094
+ settings.hooks.Stop = [];
33095
+ }
33096
+ const stopHook = {
33097
+ matcher: "",
33098
+ hooks: [
33099
+ {
33100
+ type: "command",
33101
+ command: `afplay -v 0.1 ${path4.join(claudeDir, "song/finish.mp3")}`
33102
+ }
33103
+ ]
33104
+ };
33105
+ const existingStopHook = settings.hooks.Stop.find((h) => h.hooks?.some((hook) => hook.command?.includes("finish.mp3")));
33106
+ if (!existingStopHook) {
33107
+ settings.hooks.Stop.push(stopHook);
33108
+ }
33109
+ if (!settings.hooks.Notification) {
33110
+ settings.hooks.Notification = [];
33111
+ }
33112
+ const notificationHook = {
33113
+ matcher: "",
33114
+ hooks: [
33115
+ {
33116
+ type: "command",
33117
+ command: `afplay -v 0.1 ${path4.join(claudeDir, "song/need-human.mp3")}`
33118
+ }
33119
+ ]
33120
+ };
33121
+ const existingNotificationHook = settings.hooks.Notification.find((h) => h.hooks?.some((hook) => hook.command?.includes("need-human.mp3")));
33122
+ if (!existingNotificationHook) {
33123
+ settings.hooks.Notification.push(notificationHook);
33124
+ }
33125
+ }
33126
+ await import_fs_extra3.default.writeJson(settingsPath, settings, { spaces: 2 });
33127
+ }
33128
+
33129
+ // src/commands/setup/utils.ts
33130
+ var import_fs_extra4 = __toESM(require_lib4(), 1);
33131
+ import path5 from "path";
32863
33132
 
32864
33133
  class SimpleSpinner {
32865
33134
  message = "";
@@ -32880,8 +33149,8 @@ async function downloadFromGitHub(relativePath, targetPath) {
32880
33149
  return false;
32881
33150
  }
32882
33151
  const content = await response.arrayBuffer();
32883
- await import_fs_extra.default.ensureDir(path2.dirname(targetPath));
32884
- await import_fs_extra.default.writeFile(targetPath, Buffer.from(content));
33152
+ await import_fs_extra4.default.ensureDir(path5.dirname(targetPath));
33153
+ await import_fs_extra4.default.writeFile(targetPath, Buffer.from(content));
32885
33154
  return true;
32886
33155
  } catch (error) {
32887
33156
  return false;
@@ -32898,10 +33167,10 @@ async function downloadDirectoryFromGitHub(dirPath, targetDir) {
32898
33167
  if (!Array.isArray(files)) {
32899
33168
  return false;
32900
33169
  }
32901
- await import_fs_extra.default.ensureDir(targetDir);
33170
+ await import_fs_extra4.default.ensureDir(targetDir);
32902
33171
  for (const file of files) {
32903
33172
  const relativePath = `${dirPath}/${file.name}`;
32904
- const targetPath = path2.join(targetDir, file.name);
33173
+ const targetPath = path5.join(targetDir, file.name);
32905
33174
  if (file.type === "file") {
32906
33175
  await downloadFromGitHub(relativePath, targetPath);
32907
33176
  } else if (file.type === "dir") {
@@ -32913,7 +33182,18 @@ async function downloadDirectoryFromGitHub(dirPath, targetDir) {
32913
33182
  return false;
32914
33183
  }
32915
33184
  }
32916
- async function setupCommand(customFolder, skipInteractive) {
33185
+
33186
+ // src/commands/setup.ts
33187
+ var __filename2 = fileURLToPath(import.meta.url);
33188
+ var __dirname2 = dirname(__filename2);
33189
+ var GITHUB_RAW_BASE2 = "https://raw.githubusercontent.com/Melvynx/aiblueprint-cli/main/claude-code-config";
33190
+ async function setupCommand(params = {}) {
33191
+ const {
33192
+ claudeCodeFolder: customClaudeCodeFolder,
33193
+ codexFolder: customCodexFolder,
33194
+ openCodeFolder: customOpenCodeFolder,
33195
+ skipInteractive
33196
+ } = params;
32917
33197
  try {
32918
33198
  console.log(source_default.blue.bold(`
32919
33199
  \uD83D\uDE80 AIBlueprint Claude Code Setup
@@ -32921,23 +33201,73 @@ async function setupCommand(customFolder, skipInteractive) {
32921
33201
  console.log(source_default.bgBlue(" Setting up your Claude Code environment "));
32922
33202
  let features;
32923
33203
  if (skipInteractive) {
32924
- features = ["shellShortcuts", "commandValidation", "customStatusline", "aiblueprintCommands", "aiblueprintAgents", "outputStyles", "notificationSounds"];
33204
+ features = [
33205
+ "shellShortcuts",
33206
+ "commandValidation",
33207
+ "customStatusline",
33208
+ "aiblueprintCommands",
33209
+ "aiblueprintAgents",
33210
+ "outputStyles",
33211
+ "notificationSounds",
33212
+ "codexSymlink",
33213
+ "openCodeSymlink"
33214
+ ];
32925
33215
  console.log(source_default.green("✓ Installing all features (--skip mode)"));
32926
33216
  } else {
32927
- const answers = await lib_default.prompt([{
32928
- type: "checkbox",
32929
- name: "features",
32930
- message: "Which features would you like to install?",
32931
- choices: [
32932
- { value: "shellShortcuts", name: "Shell shortcuts (cc, ccc aliases) - Quick access to Claude Code", checked: true },
32933
- { value: "commandValidation", name: "Command validation - Security hook for bash commands", checked: true },
32934
- { value: "customStatusline", name: "Custom statusline - Shows git, costs, tokens info", checked: true },
32935
- { value: "aiblueprintCommands", name: "AIBlueprint commands - Pre-configured command templates", checked: true },
32936
- { value: "aiblueprintAgents", name: "AIBlueprint agents - Specialized AI agents", checked: true },
32937
- { value: "outputStyles", name: "Output styles - Custom output formatting", checked: true },
32938
- { value: "notificationSounds", name: "Notification sounds - Audio alerts for events", checked: true }
32939
- ]
32940
- }]);
33217
+ const answers = await lib_default.prompt([
33218
+ {
33219
+ type: "checkbox",
33220
+ name: "features",
33221
+ message: "Which features would you like to install?",
33222
+ choices: [
33223
+ {
33224
+ value: "shellShortcuts",
33225
+ name: "Shell shortcuts (cc, ccc aliases) - Quick access to Claude Code",
33226
+ checked: true
33227
+ },
33228
+ {
33229
+ value: "commandValidation",
33230
+ name: "Command validation - Security hook for bash commands",
33231
+ checked: true
33232
+ },
33233
+ {
33234
+ value: "customStatusline",
33235
+ name: "Custom statusline - Shows git, costs, tokens info",
33236
+ checked: true
33237
+ },
33238
+ {
33239
+ value: "aiblueprintCommands",
33240
+ name: "AIBlueprint commands - Pre-configured command templates",
33241
+ checked: true
33242
+ },
33243
+ {
33244
+ value: "aiblueprintAgents",
33245
+ name: "AIBlueprint agents - Specialized AI agents",
33246
+ checked: true
33247
+ },
33248
+ {
33249
+ value: "outputStyles",
33250
+ name: "Output styles - Custom output formatting",
33251
+ checked: true
33252
+ },
33253
+ {
33254
+ value: "notificationSounds",
33255
+ name: "Notification sounds - Audio alerts for events",
33256
+ checked: true
33257
+ },
33258
+ {
33259
+ value: "codexSymlink",
33260
+ name: "Codex symlink - Link commands to ~/.codex/prompts",
33261
+ checked: false
33262
+ },
33263
+ {
33264
+ value: "openCodeSymlink",
33265
+ name: "OpenCode symlink - Link commands to ~/.config/opencode/command",
33266
+ checked: false
33267
+ }
33268
+ ]
33269
+ }
33270
+ ]);
32941
33271
  features = answers.features;
32942
33272
  if (!features || features.length === 0) {
32943
33273
  console.log(source_default.yellow("Setup cancelled - no features selected"));
@@ -32951,15 +33281,17 @@ async function setupCommand(customFolder, skipInteractive) {
32951
33281
  aiblueprintCommands: features.includes("aiblueprintCommands"),
32952
33282
  aiblueprintAgents: features.includes("aiblueprintAgents"),
32953
33283
  outputStyles: features.includes("outputStyles"),
32954
- notificationSounds: features.includes("notificationSounds")
33284
+ notificationSounds: features.includes("notificationSounds"),
33285
+ codexSymlink: features.includes("codexSymlink"),
33286
+ openCodeSymlink: features.includes("openCodeSymlink")
32955
33287
  };
32956
33288
  const s = new SimpleSpinner;
32957
- const claudeDir = customFolder ? path2.resolve(customFolder) : path2.join(os3.homedir(), ".claude");
33289
+ const claudeDir = customClaudeCodeFolder ? path6.resolve(customClaudeCodeFolder) : path6.join(os5.homedir(), ".claude");
32958
33290
  console.log(source_default.gray(`Installing to: ${claudeDir}`));
32959
- await import_fs_extra.default.ensureDir(claudeDir);
33291
+ await import_fs_extra5.default.ensureDir(claudeDir);
32960
33292
  let useGitHub = true;
32961
33293
  let sourceDir;
32962
- const testUrl = `${GITHUB_RAW_BASE}/scripts/validate-command.js`;
33294
+ const testUrl = `${GITHUB_RAW_BASE2}/scripts/validate-command.js`;
32963
33295
  try {
32964
33296
  const testResponse = await fetch(testUrl);
32965
33297
  useGitHub = testResponse.ok;
@@ -32969,14 +33301,14 @@ async function setupCommand(customFolder, skipInteractive) {
32969
33301
  if (!useGitHub) {
32970
33302
  const currentDir = process.cwd();
32971
33303
  const possiblePaths = [
32972
- path2.join(currentDir, "claude-code-config"),
32973
- path2.join(__dirname2, "../../claude-code-config"),
32974
- path2.join(__dirname2, "../claude-code-config"),
32975
- path2.join(path2.dirname(process.argv[1]), "../claude-code-config")
33304
+ path6.join(currentDir, "claude-code-config"),
33305
+ path6.join(__dirname2, "../../claude-code-config"),
33306
+ path6.join(__dirname2, "../claude-code-config"),
33307
+ path6.join(path6.dirname(process.argv[1]), "../claude-code-config")
32976
33308
  ];
32977
33309
  sourceDir = possiblePaths.find((p) => {
32978
33310
  try {
32979
- return import_fs_extra.default.existsSync(p);
33311
+ return import_fs_extra5.default.existsSync(p);
32980
33312
  } catch {
32981
33313
  return false;
32982
33314
  }
@@ -32996,53 +33328,68 @@ async function setupCommand(customFolder, skipInteractive) {
32996
33328
  if (options.commandValidation || options.customStatusline || options.notificationSounds) {
32997
33329
  s.start("Setting up scripts");
32998
33330
  if (useGitHub) {
32999
- const scriptsDir = path2.join(claudeDir, "scripts");
33000
- await import_fs_extra.default.ensureDir(scriptsDir);
33001
- const scriptFiles = ["validate-command.js", "statusline-ccusage.sh", "validate-command.readme.md", "statusline.readme.md"];
33331
+ const scriptsDir = path6.join(claudeDir, "scripts");
33332
+ await import_fs_extra5.default.ensureDir(scriptsDir);
33333
+ const scriptFiles = [
33334
+ "validate-command.js",
33335
+ "statusline-ccusage.sh",
33336
+ "validate-command.readme.md",
33337
+ "statusline.readme.md"
33338
+ ];
33002
33339
  for (const file of scriptFiles) {
33003
- await downloadFromGitHub(`scripts/${file}`, path2.join(scriptsDir, file));
33340
+ await downloadFromGitHub(`scripts/${file}`, path6.join(scriptsDir, file));
33004
33341
  }
33005
33342
  } else {
33006
- await import_fs_extra.default.copy(path2.join(sourceDir, "scripts"), path2.join(claudeDir, "scripts"), { overwrite: true });
33343
+ await import_fs_extra5.default.copy(path6.join(sourceDir, "scripts"), path6.join(claudeDir, "scripts"), { overwrite: true });
33007
33344
  }
33008
33345
  s.stop("Scripts installed");
33009
33346
  }
33010
33347
  if (options.aiblueprintCommands) {
33011
33348
  s.start("Setting up AIBlueprint commands");
33012
33349
  if (useGitHub) {
33013
- await downloadDirectoryFromGitHub("commands", path2.join(claudeDir, "commands"));
33350
+ await downloadDirectoryFromGitHub("commands", path6.join(claudeDir, "commands"));
33014
33351
  } else {
33015
- await import_fs_extra.default.copy(path2.join(sourceDir, "commands"), path2.join(claudeDir, "commands"), { overwrite: true });
33352
+ await import_fs_extra5.default.copy(path6.join(sourceDir, "commands"), path6.join(claudeDir, "commands"), { overwrite: true });
33016
33353
  }
33017
33354
  s.stop("Commands installed");
33018
33355
  }
33356
+ if (options.codexSymlink && options.aiblueprintCommands) {
33357
+ s.start("Setting up Codex symlink");
33358
+ await setupCodexSymlink(claudeDir, customCodexFolder, customClaudeCodeFolder);
33359
+ s.stop("Codex symlink configured");
33360
+ }
33361
+ if (options.openCodeSymlink && options.aiblueprintCommands) {
33362
+ s.start("Setting up OpenCode symlink");
33363
+ await setupOpenCodeSymlink(claudeDir, customOpenCodeFolder, customClaudeCodeFolder);
33364
+ s.stop("OpenCode symlink configured");
33365
+ }
33019
33366
  if (options.aiblueprintAgents) {
33020
33367
  s.start("Setting up AIBlueprint agents");
33021
33368
  if (useGitHub) {
33022
- await downloadDirectoryFromGitHub("agents", path2.join(claudeDir, "agents"));
33369
+ await downloadDirectoryFromGitHub("agents", path6.join(claudeDir, "agents"));
33023
33370
  } else {
33024
- await import_fs_extra.default.copy(path2.join(sourceDir, "agents"), path2.join(claudeDir, "agents"), { overwrite: true });
33371
+ await import_fs_extra5.default.copy(path6.join(sourceDir, "agents"), path6.join(claudeDir, "agents"), { overwrite: true });
33025
33372
  }
33026
33373
  s.stop("Agents installed");
33027
33374
  }
33028
33375
  if (options.outputStyles) {
33029
33376
  s.start("Setting up output styles");
33030
33377
  if (useGitHub) {
33031
- await downloadDirectoryFromGitHub("output-styles", path2.join(claudeDir, "output-styles"));
33378
+ await downloadDirectoryFromGitHub("output-styles", path6.join(claudeDir, "output-styles"));
33032
33379
  } else {
33033
- await import_fs_extra.default.copy(path2.join(sourceDir, "output-styles"), path2.join(claudeDir, "output-styles"), { overwrite: true });
33380
+ await import_fs_extra5.default.copy(path6.join(sourceDir, "output-styles"), path6.join(claudeDir, "output-styles"), { overwrite: true });
33034
33381
  }
33035
33382
  s.stop("Output styles installed");
33036
33383
  }
33037
33384
  if (options.notificationSounds) {
33038
33385
  s.start("Setting up notification sounds");
33039
33386
  if (useGitHub) {
33040
- const songDir = path2.join(claudeDir, "song");
33041
- await import_fs_extra.default.ensureDir(songDir);
33042
- await downloadFromGitHub("song/finish.mp3", path2.join(songDir, "finish.mp3"));
33043
- await downloadFromGitHub("song/need-human.mp3", path2.join(songDir, "need-human.mp3"));
33387
+ const songDir = path6.join(claudeDir, "song");
33388
+ await import_fs_extra5.default.ensureDir(songDir);
33389
+ await downloadFromGitHub("song/finish.mp3", path6.join(songDir, "finish.mp3"));
33390
+ await downloadFromGitHub("song/need-human.mp3", path6.join(songDir, "need-human.mp3"));
33044
33391
  } else {
33045
- await import_fs_extra.default.copy(path2.join(sourceDir, "song"), path2.join(claudeDir, "song"), { overwrite: true });
33392
+ await import_fs_extra5.default.copy(path6.join(sourceDir, "song"), path6.join(claudeDir, "song"), { overwrite: true });
33046
33393
  }
33047
33394
  s.stop("Notification sounds installed");
33048
33395
  }
@@ -33070,164 +33417,16 @@ Next steps:`));
33070
33417
  process.exit(1);
33071
33418
  }
33072
33419
  }
33073
- async function setupShellShortcuts() {
33074
- try {
33075
- const platform = os3.platform();
33076
- let shellConfigFile;
33077
- if (platform === "darwin") {
33078
- shellConfigFile = path2.join(os3.homedir(), ".zshenv");
33079
- } else if (platform === "linux") {
33080
- const shell = process.env.SHELL || "";
33081
- if (shell.includes("zsh")) {
33082
- shellConfigFile = path2.join(os3.homedir(), ".zshrc");
33083
- } else {
33084
- shellConfigFile = path2.join(os3.homedir(), ".bashrc");
33085
- }
33086
- } else {
33087
- console.log(source_default.yellow("Shell shortcuts are only supported on macOS and Linux"));
33088
- return;
33089
- }
33090
- const aliases = `
33091
- # AIBlueprint Claude Code aliases
33092
- alias cc="claude --dangerously-skip-permissions"
33093
- alias ccc="claude --dangerously-skip-permissions -c"
33094
- `;
33095
- const existingContent = await import_fs_extra.default.readFile(shellConfigFile, "utf-8").catch(() => "");
33096
- if (!existingContent.includes("AIBlueprint Claude Code aliases")) {
33097
- await import_fs_extra.default.appendFile(shellConfigFile, aliases);
33098
- }
33099
- } catch (error) {
33100
- console.error(source_default.red("Error setting up shell shortcuts:"), error);
33101
- throw error;
33102
- }
33103
- }
33104
- async function checkAndInstallDependencies() {
33105
- const checkCommand = (cmd) => {
33106
- try {
33107
- execSync(`which ${cmd}`, { stdio: "ignore" });
33108
- return true;
33109
- } catch {
33110
- return false;
33111
- }
33112
- };
33113
- if (!checkCommand("bun")) {
33114
- console.log(source_default.yellow(`
33115
- Installing bun...`));
33116
- try {
33117
- execSync("npm install -g bun", { stdio: "inherit" });
33118
- } catch (error) {
33119
- console.log(source_default.red(" Failed to install bun. Please install it manually: npm install -g bun"));
33120
- }
33121
- }
33122
- if (!checkCommand("ccusage")) {
33123
- console.log(source_default.yellow(`
33124
- Installing ccusage...`));
33125
- try {
33126
- execSync("npm install -g ccusage", { stdio: "inherit" });
33127
- } catch (error) {
33128
- console.log(source_default.red(" Failed to install ccusage. Please install it manually: npm install -g ccusage"));
33129
- }
33130
- }
33131
- }
33132
- async function updateSettings(options, claudeDir) {
33133
- const settingsPath = path2.join(claudeDir, "settings.json");
33134
- let settings = {};
33135
- try {
33136
- const existingSettings = await import_fs_extra.default.readFile(settingsPath, "utf-8");
33137
- settings = JSON.parse(existingSettings);
33138
- } catch {
33139
- }
33140
- if (options.customStatusline) {
33141
- if (settings.statusLine) {
33142
- const confirmAnswer = await lib_default.prompt([{
33143
- type: "confirm",
33144
- name: "replace",
33145
- message: "You already have a statusLine configuration. Replace it?"
33146
- }]);
33147
- if (!confirmAnswer.replace) {
33148
- console.log(source_default.yellow(" Keeping existing statusLine configuration"));
33149
- } else {
33150
- settings.statusLine = {
33151
- type: "command",
33152
- command: `bash ${path2.join(claudeDir, "scripts/statusline-ccusage.sh")}`,
33153
- padding: 0
33154
- };
33155
- }
33156
- } else {
33157
- settings.statusLine = {
33158
- type: "command",
33159
- command: `bash ${path2.join(claudeDir, "scripts/statusline-ccusage.sh")}`,
33160
- padding: 0
33161
- };
33162
- }
33163
- }
33164
- if (!settings.hooks) {
33165
- settings.hooks = {};
33166
- }
33167
- if (options.commandValidation) {
33168
- if (!settings.hooks.PreToolUse) {
33169
- settings.hooks.PreToolUse = [];
33170
- }
33171
- const bashHook = {
33172
- matcher: "Bash",
33173
- hooks: [
33174
- {
33175
- type: "command",
33176
- command: `bun ${path2.join(claudeDir, "scripts/validate-command.js")}`
33177
- }
33178
- ]
33179
- };
33180
- const existingBashHook = settings.hooks.PreToolUse.find((h) => h.matcher === "Bash");
33181
- if (!existingBashHook) {
33182
- settings.hooks.PreToolUse.push(bashHook);
33183
- }
33184
- }
33185
- if (options.notificationSounds) {
33186
- if (!settings.hooks.Stop) {
33187
- settings.hooks.Stop = [];
33188
- }
33189
- const stopHook = {
33190
- matcher: "",
33191
- hooks: [
33192
- {
33193
- type: "command",
33194
- command: `afplay -v 0.1 ${path2.join(claudeDir, "song/finish.mp3")}`
33195
- }
33196
- ]
33197
- };
33198
- const existingStopHook = settings.hooks.Stop.find((h) => h.hooks?.some((hook) => hook.command?.includes("finish.mp3")));
33199
- if (!existingStopHook) {
33200
- settings.hooks.Stop.push(stopHook);
33201
- }
33202
- if (!settings.hooks.Notification) {
33203
- settings.hooks.Notification = [];
33204
- }
33205
- const notificationHook = {
33206
- matcher: "",
33207
- hooks: [
33208
- {
33209
- type: "command",
33210
- command: `afplay -v 0.1 ${path2.join(claudeDir, "song/need-human.mp3")}`
33211
- }
33212
- ]
33213
- };
33214
- const existingNotificationHook = settings.hooks.Notification.find((h) => h.hooks?.some((hook) => hook.command?.includes("need-human.mp3")));
33215
- if (!existingNotificationHook) {
33216
- settings.hooks.Notification.push(notificationHook);
33217
- }
33218
- }
33219
- await import_fs_extra.default.writeJson(settingsPath, settings, { spaces: 2 });
33220
- }
33221
33420
 
33222
33421
  // src/commands/addHook.ts
33223
- var import_fs_extra5 = __toESM(require_lib4(), 1);
33224
- import path6 from "path";
33422
+ var import_fs_extra9 = __toESM(require_lib4(), 1);
33423
+ import path10 from "path";
33225
33424
  import { fileURLToPath as fileURLToPath3 } from "url";
33226
33425
  import { dirname as dirname3 } from "path";
33227
33426
 
33228
33427
  // src/utils/claude-config.ts
33229
- var import_fs_extra2 = __toESM(require_lib4(), 1);
33230
- import path3 from "path";
33428
+ var import_fs_extra6 = __toESM(require_lib4(), 1);
33429
+ import path7 from "path";
33231
33430
  import { fileURLToPath as fileURLToPath2 } from "url";
33232
33431
  import { dirname as dirname2 } from "path";
33233
33432
  var __filename3 = fileURLToPath2(import.meta.url);
@@ -33258,14 +33457,14 @@ function parseYamlFrontmatter(content) {
33258
33457
  }
33259
33458
  function getLocalConfigPaths(subDir) {
33260
33459
  return [
33261
- path3.join(__dirname3, `../claude-code-config/${subDir}`),
33262
- path3.join(__dirname3, `../../claude-code-config/${subDir}`)
33460
+ path7.join(__dirname3, `../claude-code-config/${subDir}`),
33461
+ path7.join(__dirname3, `../../claude-code-config/${subDir}`)
33263
33462
  ];
33264
33463
  }
33265
33464
  async function findLocalConfigDir(subDir) {
33266
33465
  const possiblePaths = getLocalConfigPaths(subDir);
33267
33466
  for (const testPath of possiblePaths) {
33268
- if (await import_fs_extra2.default.pathExists(testPath)) {
33467
+ if (await import_fs_extra6.default.pathExists(testPath)) {
33269
33468
  return testPath;
33270
33469
  }
33271
33470
  }
@@ -33276,26 +33475,26 @@ async function getTargetDirectory(options) {
33276
33475
  return options.folder;
33277
33476
  }
33278
33477
  const cwd = process.cwd();
33279
- const localClaudeDir = path3.join(cwd, ".claude");
33280
- const isGitRepo = await import_fs_extra2.default.pathExists(path3.join(cwd, ".git"));
33281
- const hasClaudeConfig = await import_fs_extra2.default.pathExists(localClaudeDir);
33478
+ const localClaudeDir = path7.join(cwd, ".claude");
33479
+ const isGitRepo = await import_fs_extra6.default.pathExists(path7.join(cwd, ".git"));
33480
+ const hasClaudeConfig = await import_fs_extra6.default.pathExists(localClaudeDir);
33282
33481
  if (isGitRepo || hasClaudeConfig) {
33283
33482
  return localClaudeDir;
33284
33483
  }
33285
- return path3.join(process.env.HOME || process.env.USERPROFILE || "~", ".claude");
33484
+ return path7.join(process.env.HOME || process.env.USERPROFILE || "~", ".claude");
33286
33485
  }
33287
33486
 
33288
33487
  // src/utils/file-installer.ts
33289
- var import_fs_extra4 = __toESM(require_lib4(), 1);
33290
- import path5 from "path";
33488
+ var import_fs_extra8 = __toESM(require_lib4(), 1);
33489
+ import path9 from "path";
33291
33490
 
33292
33491
  // src/utils/github.ts
33293
- var import_fs_extra3 = __toESM(require_lib4(), 1);
33294
- import path4 from "path";
33295
- var GITHUB_RAW_BASE2 = "https://raw.githubusercontent.com/Melvynx/aiblueprint-cli/main/claude-code-config";
33492
+ var import_fs_extra7 = __toESM(require_lib4(), 1);
33493
+ import path8 from "path";
33494
+ var GITHUB_RAW_BASE3 = "https://raw.githubusercontent.com/Melvynx/aiblueprint-cli/main/claude-code-config";
33296
33495
  async function downloadFromGitHub2(relativePath) {
33297
33496
  try {
33298
- const url = `${GITHUB_RAW_BASE2}/${relativePath}`;
33497
+ const url = `${GITHUB_RAW_BASE3}/${relativePath}`;
33299
33498
  const response = await fetch(url);
33300
33499
  if (!response.ok) {
33301
33500
  return null;
@@ -33320,7 +33519,7 @@ async function listFilesFromGitHub(dirPath) {
33320
33519
  }
33321
33520
  async function isGitHubAvailable() {
33322
33521
  try {
33323
- const testUrl = `${GITHUB_RAW_BASE2}/commands/commit.md`;
33522
+ const testUrl = `${GITHUB_RAW_BASE3}/commands/commit.md`;
33324
33523
  const testResponse = await fetch(testUrl);
33325
33524
  return testResponse.ok;
33326
33525
  } catch {
@@ -33330,8 +33529,8 @@ async function isGitHubAvailable() {
33330
33529
  async function downloadAndWriteFile(relativePath, targetPath) {
33331
33530
  const content = await downloadFromGitHub2(relativePath);
33332
33531
  if (content) {
33333
- await import_fs_extra3.default.ensureDir(path4.dirname(targetPath));
33334
- await import_fs_extra3.default.writeFile(targetPath, content);
33532
+ await import_fs_extra7.default.ensureDir(path8.dirname(targetPath));
33533
+ await import_fs_extra7.default.writeFile(targetPath, content);
33335
33534
  return true;
33336
33535
  }
33337
33536
  return false;
@@ -33340,7 +33539,7 @@ async function downloadAndWriteFile(relativePath, targetPath) {
33340
33539
  // src/utils/file-installer.ts
33341
33540
  async function installFileWithGitHubFallback(options) {
33342
33541
  const { sourceDir, targetPath, fileName } = options;
33343
- await import_fs_extra4.default.ensureDir(path5.dirname(targetPath));
33542
+ await import_fs_extra8.default.ensureDir(path9.dirname(targetPath));
33344
33543
  const useGitHub = options.useGitHub ?? await isGitHubAvailable();
33345
33544
  if (useGitHub) {
33346
33545
  const relativePath = `${sourceDir}/${fileName}`;
@@ -33354,11 +33553,11 @@ async function installFileWithGitHubFallback(options) {
33354
33553
  if (!localConfigDir) {
33355
33554
  throw new Error(`Neither GitHub nor local ${sourceDir} directory found`);
33356
33555
  }
33357
- const localFilePath = path5.join(localConfigDir, fileName);
33358
- if (!await import_fs_extra4.default.pathExists(localFilePath)) {
33556
+ const localFilePath = path9.join(localConfigDir, fileName);
33557
+ if (!await import_fs_extra8.default.pathExists(localFilePath)) {
33359
33558
  throw new Error(`File not found: ${fileName}`);
33360
33559
  }
33361
- await import_fs_extra4.default.copy(localFilePath, targetPath);
33560
+ await import_fs_extra8.default.copy(localFilePath, targetPath);
33362
33561
  }
33363
33562
  async function getFileContentWithGitHubFallback(sourceDir, fileName) {
33364
33563
  const useGitHub = await isGitHubAvailable();
@@ -33373,11 +33572,11 @@ async function getFileContentWithGitHubFallback(sourceDir, fileName) {
33373
33572
  if (!localConfigDir) {
33374
33573
  throw new Error(`Neither GitHub nor local ${sourceDir} directory found`);
33375
33574
  }
33376
- const localFilePath = path5.join(localConfigDir, fileName);
33377
- if (!await import_fs_extra4.default.pathExists(localFilePath)) {
33575
+ const localFilePath = path9.join(localConfigDir, fileName);
33576
+ if (!await import_fs_extra8.default.pathExists(localFilePath)) {
33378
33577
  throw new Error(`File not found: ${fileName}`);
33379
33578
  }
33380
- return await import_fs_extra4.default.readFile(localFilePath, "utf-8");
33579
+ return await import_fs_extra8.default.readFile(localFilePath, "utf-8");
33381
33580
  }
33382
33581
 
33383
33582
  // src/commands/addHook.ts
@@ -33417,10 +33616,10 @@ async function addHookCommand(hookType, options) {
33417
33616
  const s = new SimpleSpinner2;
33418
33617
  const targetDir = await getTargetDirectory(options);
33419
33618
  const claudeDir = targetDir;
33420
- const hooksDir = path6.join(claudeDir, "hooks");
33421
- const hookFilePath = path6.join(hooksDir, hook.hookFile);
33422
- const settingsPath = path6.join(claudeDir, "settings.json");
33423
- if (await import_fs_extra5.default.pathExists(hookFilePath)) {
33619
+ const hooksDir = path10.join(claudeDir, "hooks");
33620
+ const hookFilePath = path10.join(hooksDir, hook.hookFile);
33621
+ const settingsPath = path10.join(claudeDir, "settings.json");
33622
+ if (await import_fs_extra9.default.pathExists(hookFilePath)) {
33424
33623
  const overwriteAnswer = await lib_default.prompt([{
33425
33624
  type: "confirm",
33426
33625
  name: "overwrite",
@@ -33433,18 +33632,18 @@ async function addHookCommand(hookType, options) {
33433
33632
  }
33434
33633
  try {
33435
33634
  s.start("Installing hook...");
33436
- await import_fs_extra5.default.ensureDir(hooksDir);
33635
+ await import_fs_extra9.default.ensureDir(hooksDir);
33437
33636
  await installFileWithGitHubFallback({
33438
33637
  sourceDir: "hooks",
33439
33638
  targetPath: hookFilePath,
33440
33639
  fileName: hook.hookFile
33441
33640
  });
33442
- await import_fs_extra5.default.chmod(hookFilePath, 493);
33641
+ await import_fs_extra9.default.chmod(hookFilePath, 493);
33443
33642
  s.stop("Hook file installed");
33444
33643
  s.start("Updating settings.json...");
33445
33644
  let settings = {};
33446
33645
  try {
33447
- const existingSettings = await import_fs_extra5.default.readFile(settingsPath, "utf-8");
33646
+ const existingSettings = await import_fs_extra9.default.readFile(settingsPath, "utf-8");
33448
33647
  settings = JSON.parse(existingSettings);
33449
33648
  } catch {
33450
33649
  settings = {};
@@ -33481,7 +33680,7 @@ async function addHookCommand(hookType, options) {
33481
33680
  } else {
33482
33681
  settings.hooks[hook.event].push(newHook);
33483
33682
  }
33484
- await import_fs_extra5.default.writeFile(settingsPath, JSON.stringify(settings, null, 2));
33683
+ await import_fs_extra9.default.writeFile(settingsPath, JSON.stringify(settings, null, 2));
33485
33684
  s.stop("Settings updated");
33486
33685
  console.log(source_default.green("✨ Hook installed successfully!"));
33487
33686
  console.log(source_default.gray(`
@@ -33500,8 +33699,8 @@ The hook will run automatically when you edit TypeScript files with Claude Code.
33500
33699
  }
33501
33700
 
33502
33701
  // src/commands/addCommand.ts
33503
- var import_fs_extra6 = __toESM(require_lib4(), 1);
33504
- import path7 from "path";
33702
+ var import_fs_extra10 = __toESM(require_lib4(), 1);
33703
+ import path11 from "path";
33505
33704
  class SimpleSpinner3 {
33506
33705
  message = "";
33507
33706
  start(message) {
@@ -33518,12 +33717,13 @@ async function discoverAvailableCommands() {
33518
33717
  let mdFiles = [];
33519
33718
  if (useGitHub) {
33520
33719
  mdFiles = (await listFilesFromGitHub("commands")).filter((file) => file.endsWith(".md"));
33521
- } else {
33720
+ }
33721
+ if (mdFiles.length === 0) {
33522
33722
  const commandsDir = await findLocalConfigDir("commands");
33523
33723
  if (!commandsDir) {
33524
33724
  throw new Error("Commands directory not found");
33525
33725
  }
33526
- const files = await import_fs_extra6.default.readdir(commandsDir);
33726
+ const files = await import_fs_extra10.default.readdir(commandsDir);
33527
33727
  mdFiles = files.filter((file) => file.endsWith(".md"));
33528
33728
  }
33529
33729
  for (const file of mdFiles) {
@@ -33584,9 +33784,9 @@ async function addCommandCommand(commandName, options = {}) {
33584
33784
  if (options.folder) {
33585
33785
  console.log(source_default.gray(`Using custom folder: ${targetDir}`));
33586
33786
  }
33587
- const commandsDir = path7.join(targetDir, "commands");
33588
- const commandFilePath = path7.join(commandsDir, command.commandFile);
33589
- if (await import_fs_extra6.default.pathExists(commandFilePath)) {
33787
+ const commandsDir = path11.join(targetDir, "commands");
33788
+ const commandFilePath = path11.join(commandsDir, command.commandFile);
33789
+ if (await import_fs_extra10.default.pathExists(commandFilePath)) {
33590
33790
  const overwriteAnswer = await lib_default.prompt([{
33591
33791
  type: "confirm",
33592
33792
  name: "overwrite",
@@ -33626,6 +33826,172 @@ The command will be available immediately in Claude Code.`));
33626
33826
  }
33627
33827
  }
33628
33828
 
33829
+ // src/commands/symlink.ts
33830
+ var TOOLS = [
33831
+ {
33832
+ name: "Claude Code",
33833
+ value: "claude-code",
33834
+ supportsCommands: true,
33835
+ supportsAgents: true
33836
+ },
33837
+ {
33838
+ name: "Codex",
33839
+ value: "codex",
33840
+ supportsCommands: true,
33841
+ supportsAgents: false
33842
+ },
33843
+ {
33844
+ name: "OpenCode",
33845
+ value: "opencode",
33846
+ supportsCommands: true,
33847
+ supportsAgents: false
33848
+ },
33849
+ {
33850
+ name: "FactoryAI",
33851
+ value: "factoryai",
33852
+ supportsCommands: true,
33853
+ supportsAgents: true
33854
+ }
33855
+ ];
33856
+ async function symlinkCommand(params = {}) {
33857
+ try {
33858
+ console.log(source_default.blue.bold(`
33859
+ \uD83D\uDD17 Symlink Manager
33860
+ `));
33861
+ console.log(source_default.gray("Create symlinks between different CLI tool configurations"));
33862
+ const sourceAnswer = await lib_default.prompt([
33863
+ {
33864
+ type: "list",
33865
+ name: "source",
33866
+ message: "Select source tool:",
33867
+ choices: TOOLS.map((tool) => ({
33868
+ name: tool.name,
33869
+ value: tool.value
33870
+ }))
33871
+ }
33872
+ ]);
33873
+ const sourceTool = sourceAnswer.source;
33874
+ const sourceConfig = TOOLS.find((t) => t.value === sourceTool);
33875
+ const contentTypeChoices = [];
33876
+ if (sourceConfig.supportsCommands) {
33877
+ contentTypeChoices.push({ name: "Commands only", value: "commands" });
33878
+ }
33879
+ if (sourceConfig.supportsAgents) {
33880
+ contentTypeChoices.push({ name: "Agents only", value: "agents" });
33881
+ }
33882
+ if (sourceConfig.supportsCommands && sourceConfig.supportsAgents) {
33883
+ contentTypeChoices.push({ name: "Both", value: "both" });
33884
+ }
33885
+ if (contentTypeChoices.length === 0) {
33886
+ console.log(source_default.red(`
33887
+ ❌ Error: ${sourceConfig.name} doesn't support any syncable content`));
33888
+ process.exit(1);
33889
+ }
33890
+ const contentAnswer = await lib_default.prompt([
33891
+ {
33892
+ type: "list",
33893
+ name: "contentType",
33894
+ message: "What would you like to sync?",
33895
+ choices: contentTypeChoices
33896
+ }
33897
+ ]);
33898
+ const syncType = contentAnswer.contentType;
33899
+ const syncCommands = syncType === "commands" || syncType === "both";
33900
+ const syncAgents = syncType === "agents" || syncType === "both";
33901
+ const destinationChoices = [];
33902
+ for (const tool of TOOLS) {
33903
+ if (tool.value === sourceTool)
33904
+ continue;
33905
+ if (syncCommands && tool.supportsCommands) {
33906
+ destinationChoices.push({
33907
+ name: syncAgents ? `${tool.name} (commands)` : tool.name,
33908
+ value: `${tool.value}-commands`,
33909
+ tool: tool.value,
33910
+ contentType: "commands"
33911
+ });
33912
+ }
33913
+ if (syncAgents && tool.supportsAgents) {
33914
+ destinationChoices.push({
33915
+ name: syncCommands ? `${tool.name} (agents)` : tool.name,
33916
+ value: `${tool.value}-agents`,
33917
+ tool: tool.value,
33918
+ contentType: "agents"
33919
+ });
33920
+ }
33921
+ }
33922
+ if (destinationChoices.length === 0) {
33923
+ console.log(source_default.yellow(`
33924
+ ⚠️ No compatible destination tools found for the selected sync type`));
33925
+ process.exit(0);
33926
+ }
33927
+ const destinationAnswer = await lib_default.prompt([
33928
+ {
33929
+ type: "checkbox",
33930
+ name: "destinations",
33931
+ message: "Select destination tools (multi-select):",
33932
+ choices: destinationChoices.map((choice) => ({
33933
+ name: choice.name,
33934
+ value: choice.value,
33935
+ checked: false
33936
+ })),
33937
+ validate: (answer) => {
33938
+ if (answer.length === 0) {
33939
+ return "Please select at least one destination";
33940
+ }
33941
+ return true;
33942
+ }
33943
+ }
33944
+ ]);
33945
+ const selectedDestinations = destinationAnswer.destinations;
33946
+ const customFolders = {
33947
+ "claude-code": params.claudeCodeFolder,
33948
+ codex: params.codexFolder,
33949
+ opencode: params.openCodeFolder,
33950
+ factoryai: params.factoryAiFolder
33951
+ };
33952
+ const sourcePaths = await getToolPaths(sourceTool, customFolders[sourceTool]);
33953
+ console.log(source_default.blue(`
33954
+ \uD83D\uDCE6 Creating symlinks...
33955
+ `));
33956
+ let successCount = 0;
33957
+ let skipCount = 0;
33958
+ for (const destValue of selectedDestinations) {
33959
+ const destChoice = destinationChoices.find((c) => c.value === destValue);
33960
+ const destPaths = await getToolPaths(destChoice.tool, customFolders[destChoice.tool]);
33961
+ let sourcePath;
33962
+ let targetPath;
33963
+ if (destChoice.contentType === "commands") {
33964
+ sourcePath = sourcePaths.commandsPath;
33965
+ targetPath = destPaths.commandsPath;
33966
+ } else {
33967
+ sourcePath = sourcePaths.agentsPath;
33968
+ targetPath = destPaths.agentsPath;
33969
+ }
33970
+ const toolName = TOOLS.find((t) => t.value === destChoice.tool)?.name || destChoice.tool;
33971
+ const contentLabel = destChoice.contentType === "commands" ? "commands" : "agents";
33972
+ try {
33973
+ const success = await createSymlink(sourcePath, targetPath, {
33974
+ skipMessage: source_default.yellow(` ⚠️ ${toolName} ${contentLabel} path already exists and is not a symlink. Skipping...`)
33975
+ });
33976
+ if (success) {
33977
+ console.log(source_default.green(` ✓ ${toolName} (${contentLabel}) symlink created`));
33978
+ successCount++;
33979
+ } else {
33980
+ skipCount++;
33981
+ }
33982
+ } catch (error) {
33983
+ console.error(source_default.red(` ✗ Failed to create ${toolName} (${contentLabel}) symlink:`), error);
33984
+ }
33985
+ }
33986
+ console.log(source_default.green(`
33987
+ ✨ Symlink setup complete! ${successCount} created, ${skipCount} skipped`));
33988
+ } catch (error) {
33989
+ console.error(source_default.red(`
33990
+ ❌ Symlink setup failed:`), error);
33991
+ process.exit(1);
33992
+ }
33993
+ }
33994
+
33629
33995
  // src/cli.ts
33630
33996
  import { readFileSync as readFileSync2 } from "fs";
33631
33997
  import { dirname as dirname4, join } from "path";
@@ -33634,10 +34000,15 @@ var __dirname5 = dirname4(fileURLToPath4(import.meta.url));
33634
34000
  var packageJson = JSON.parse(readFileSync2(join(__dirname5, "../package.json"), "utf8"));
33635
34001
  var program2 = new Command;
33636
34002
  program2.name("aiblueprint").description("AIBlueprint CLI for setting up Claude Code configurations").version(packageJson.version);
33637
- var claudeCodeCmd = program2.command("claude-code").description("Claude Code configuration commands").option("-f, --folder <path>", "Specify custom folder path (default: ~/.claude)").option("-s, --skip", "Skip interactive prompts and install all features");
34003
+ var claudeCodeCmd = program2.command("claude-code").description("Claude Code configuration commands").option("-f, --folder <path>", "Specify custom Claude Code folder path (default: ~/.claude) - alias for --claudeCodeFolder").option("--claudeCodeFolder <path>", "Specify custom Claude Code folder path (default: ~/.claude)").option("--codexFolder <path>", "Specify custom Codex folder path (default: ~/.codex)").option("--openCodeFolder <path>", "Specify custom OpenCode folder path (default: ~/.config/opencode)").option("--factoryAiFolder <path>", "Specify custom FactoryAI folder path (default: ~/.factory)").option("-s, --skip", "Skip interactive prompts and install all features");
33638
34004
  claudeCodeCmd.command("setup").description("Setup Claude Code configuration with AIBlueprint defaults").action((options, command) => {
33639
34005
  const parentOptions = command.parent.opts();
33640
- setupCommand(parentOptions.folder, parentOptions.skip);
34006
+ setupCommand({
34007
+ claudeCodeFolder: parentOptions.claudeCodeFolder || parentOptions.folder,
34008
+ codexFolder: parentOptions.codexFolder,
34009
+ openCodeFolder: parentOptions.openCodeFolder,
34010
+ skipInteractive: parentOptions.skip
34011
+ });
33641
34012
  });
33642
34013
  var addCmd = claudeCodeCmd.command("add").description(`Add components to your Claude Code configuration
33643
34014
  ` + `Examples:
@@ -33646,11 +34017,22 @@ var addCmd = claudeCodeCmd.command("add").description(`Add components to your Cl
33646
34017
  ` + " aiblueprint claude-code add commands commit");
33647
34018
  addCmd.command("hook <type>").description("Add a hook to your Claude Code configuration. Available types: post-edit-typescript").action((type, options, command) => {
33648
34019
  const parentOptions = command.parent.parent.opts();
33649
- addHookCommand(type, { folder: parentOptions.folder });
34020
+ const claudeCodeFolder = parentOptions.claudeCodeFolder || parentOptions.folder;
34021
+ addHookCommand(type, { folder: claudeCodeFolder });
33650
34022
  });
33651
34023
  addCmd.command("commands [command-name]").description("Install a Claude Code command or list all available commands (use without argument to list)").action((commandName, options, command) => {
33652
34024
  const parentOptions = command.parent.parent.opts();
33653
- addCommandCommand(commandName, { folder: parentOptions.folder });
34025
+ const claudeCodeFolder = parentOptions.claudeCodeFolder || parentOptions.folder;
34026
+ addCommandCommand(commandName, { folder: claudeCodeFolder });
34027
+ });
34028
+ claudeCodeCmd.command("symlink").description("Create symlinks between different CLI tools (Claude Code, Codex, OpenCode, FactoryAI)").action((options, command) => {
34029
+ const parentOptions = command.parent.opts();
34030
+ symlinkCommand({
34031
+ claudeCodeFolder: parentOptions.claudeCodeFolder || parentOptions.folder,
34032
+ codexFolder: parentOptions.codexFolder,
34033
+ openCodeFolder: parentOptions.openCodeFolder,
34034
+ factoryAiFolder: parentOptions.factoryAiFolder
34035
+ });
33654
34036
  });
33655
34037
  program2.parse(process.argv);
33656
34038
  if (!process.argv.slice(2).length) {