aiblueprint-cli 1.3.4 → 1.3.6

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 (2) hide show
  1. package/dist/cli.js +249 -23
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -35324,6 +35324,8 @@ async function listRemoteFilesRecursive(dirPath, githubToken, basePath = "") {
35324
35324
  const results = [];
35325
35325
  const files = await listRemoteDirectory(dirPath, githubToken);
35326
35326
  for (const file of files) {
35327
+ if (file.name === "node_modules" || file.name === ".DS_Store")
35328
+ continue;
35327
35329
  const relativePath = basePath ? `${basePath}/${file.name}` : file.name;
35328
35330
  if (file.type === "file") {
35329
35331
  results.push({ path: relativePath, sha: file.sha, isFolder: false });
@@ -35350,6 +35352,8 @@ async function listLocalFiles(dir) {
35350
35352
  }
35351
35353
  const items = await import_fs_extra15.default.readdir(dir);
35352
35354
  for (const item of items) {
35355
+ if (item === "node_modules" || item === ".DS_Store")
35356
+ continue;
35353
35357
  const fullPath = path17.join(dir, item);
35354
35358
  const stat = await import_fs_extra15.default.stat(fullPath);
35355
35359
  if (stat.isDirectory()) {
@@ -35366,6 +35370,8 @@ async function listLocalFilesRecursive(dir, basePath) {
35366
35370
  const files = [];
35367
35371
  const items = await import_fs_extra15.default.readdir(dir);
35368
35372
  for (const item of items) {
35373
+ if (item === "node_modules" || item === ".DS_Store")
35374
+ continue;
35369
35375
  const fullPath = path17.join(dir, item);
35370
35376
  const relativePath = `${basePath}/${item}`;
35371
35377
  const stat = await import_fs_extra15.default.stat(fullPath);
@@ -35436,6 +35442,68 @@ async function analyzeCategory(category, claudeDir, githubToken) {
35436
35442
  }
35437
35443
  return items;
35438
35444
  }
35445
+ async function fetchRemoteSettings(githubToken) {
35446
+ try {
35447
+ const url = `https://raw.githubusercontent.com/${PREMIUM_REPO2}/${PREMIUM_BRANCH2}/claude-code-config/settings.json`;
35448
+ const response = await fetch(url, {
35449
+ headers: {
35450
+ Authorization: `token ${githubToken}`,
35451
+ Accept: "application/vnd.github.v3.raw"
35452
+ }
35453
+ });
35454
+ if (!response.ok) {
35455
+ return null;
35456
+ }
35457
+ return await response.json();
35458
+ } catch {
35459
+ return null;
35460
+ }
35461
+ }
35462
+ async function getLocalSettings(claudeDir) {
35463
+ const settingsPath = path17.join(claudeDir, "settings.json");
35464
+ try {
35465
+ const content = await import_fs_extra15.default.readFile(settingsPath, "utf-8");
35466
+ return JSON.parse(content);
35467
+ } catch {
35468
+ return {};
35469
+ }
35470
+ }
35471
+ function analyzeHooksChanges(remoteSettings, localSettings) {
35472
+ const hookItems = [];
35473
+ if (!remoteSettings?.hooks) {
35474
+ return hookItems;
35475
+ }
35476
+ const localHooks = localSettings?.hooks || {};
35477
+ for (const [hookType, remoteHookArray] of Object.entries(remoteSettings.hooks)) {
35478
+ if (!Array.isArray(remoteHookArray))
35479
+ continue;
35480
+ const localHookArray = localHooks[hookType] || [];
35481
+ for (const remoteHook of remoteHookArray) {
35482
+ const matcher = remoteHook.matcher || "";
35483
+ const existingLocal = localHookArray.find((h2) => h2.matcher === matcher);
35484
+ if (!existingLocal) {
35485
+ hookItems.push({
35486
+ hookType,
35487
+ matcher,
35488
+ status: "new",
35489
+ remoteHook
35490
+ });
35491
+ } else {
35492
+ const remoteStr = JSON.stringify(remoteHook);
35493
+ const localStr = JSON.stringify(existingLocal);
35494
+ if (remoteStr !== localStr) {
35495
+ hookItems.push({
35496
+ hookType,
35497
+ matcher,
35498
+ status: "modified",
35499
+ remoteHook
35500
+ });
35501
+ }
35502
+ }
35503
+ }
35504
+ }
35505
+ return hookItems;
35506
+ }
35439
35507
  async function analyzeSyncChanges(claudeDir, githubToken) {
35440
35508
  const allItems = [];
35441
35509
  const categories = [
@@ -35448,10 +35516,16 @@ async function analyzeSyncChanges(claudeDir, githubToken) {
35448
35516
  const items = await analyzeCategory(category, claudeDir, githubToken);
35449
35517
  allItems.push(...items);
35450
35518
  }
35519
+ const remoteSettings = await fetchRemoteSettings(githubToken);
35520
+ const localSettings = await getLocalSettings(claudeDir);
35521
+ const hooks = remoteSettings ? analyzeHooksChanges(remoteSettings, localSettings) : [];
35522
+ const hooksNewCount = hooks.filter((h2) => h2.status === "new").length;
35523
+ const hooksModifiedCount = hooks.filter((h2) => h2.status === "modified").length;
35451
35524
  return {
35452
35525
  items: allItems,
35453
- newCount: allItems.filter((i) => i.status === "new").length,
35454
- modifiedCount: allItems.filter((i) => i.status === "modified").length,
35526
+ hooks,
35527
+ newCount: allItems.filter((i) => i.status === "new").length + hooksNewCount,
35528
+ modifiedCount: allItems.filter((i) => i.status === "modified").length + hooksModifiedCount,
35455
35529
  deletedCount: allItems.filter((i) => i.status === "deleted").length,
35456
35530
  unchangedCount: allItems.filter((i) => i.status === "unchanged").length
35457
35531
  };
@@ -35476,6 +35550,43 @@ async function downloadFromPrivateGitHub2(relativePath, targetPath, githubToken)
35476
35550
  return false;
35477
35551
  }
35478
35552
  }
35553
+ async function syncSelectedHooks(claudeDir, hooks, onProgress) {
35554
+ if (hooks.length === 0) {
35555
+ return { success: 0, failed: 0 };
35556
+ }
35557
+ const settingsPath = path17.join(claudeDir, "settings.json");
35558
+ let settings = {};
35559
+ try {
35560
+ const content = await import_fs_extra15.default.readFile(settingsPath, "utf-8");
35561
+ settings = JSON.parse(content);
35562
+ } catch {
35563
+ settings = {};
35564
+ }
35565
+ if (!settings.hooks) {
35566
+ settings.hooks = {};
35567
+ }
35568
+ let success = 0;
35569
+ let failed = 0;
35570
+ for (const hook of hooks) {
35571
+ onProgress?.(`${hook.hookType}[${hook.matcher || "*"}]`, hook.status === "new" ? "adding" : "updating");
35572
+ try {
35573
+ if (!settings.hooks[hook.hookType]) {
35574
+ settings.hooks[hook.hookType] = [];
35575
+ }
35576
+ const existingIndex = settings.hooks[hook.hookType].findIndex((h2) => h2.matcher === hook.matcher);
35577
+ if (existingIndex >= 0) {
35578
+ settings.hooks[hook.hookType][existingIndex] = hook.remoteHook;
35579
+ } else {
35580
+ settings.hooks[hook.hookType].push(hook.remoteHook);
35581
+ }
35582
+ success++;
35583
+ } catch {
35584
+ failed++;
35585
+ }
35586
+ }
35587
+ await import_fs_extra15.default.writeFile(settingsPath, JSON.stringify(settings, null, 2));
35588
+ return { success, failed };
35589
+ }
35479
35590
  async function syncSelectedItems(claudeDir, items, githubToken, onProgress) {
35480
35591
  let success = 0;
35481
35592
  let failed = 0;
@@ -35528,6 +35639,104 @@ function groupByCategory(items) {
35528
35639
  }
35529
35640
  return grouped;
35530
35641
  }
35642
+ function aggregateByTopLevelFolder(items) {
35643
+ const folderMap = new Map;
35644
+ for (const item of items) {
35645
+ const parts = item.name.split("/");
35646
+ const topLevel = parts[0];
35647
+ if (!folderMap.has(topLevel)) {
35648
+ folderMap.set(topLevel, { name: topLevel, newCount: 0, modifiedCount: 0, deletedCount: 0 });
35649
+ }
35650
+ const summary = folderMap.get(topLevel);
35651
+ if (item.status === "new")
35652
+ summary.newCount++;
35653
+ else if (item.status === "modified")
35654
+ summary.modifiedCount++;
35655
+ else if (item.status === "deleted")
35656
+ summary.deletedCount++;
35657
+ }
35658
+ return Array.from(folderMap.values());
35659
+ }
35660
+ function formatFolderSummary(summary) {
35661
+ const parts = [];
35662
+ if (summary.newCount > 0)
35663
+ parts.push(source_default.green(`+${summary.newCount}`));
35664
+ if (summary.modifiedCount > 0)
35665
+ parts.push(source_default.yellow(`~${summary.modifiedCount}`));
35666
+ if (summary.deletedCount > 0)
35667
+ parts.push(source_default.red(`-${summary.deletedCount}`));
35668
+ const countStr = parts.length > 0 ? ` (${parts.join(", ")})` : "";
35669
+ return `\uD83D\uDCC1 ${summary.name}${countStr}`;
35670
+ }
35671
+ function createSelectionChoices(changedItems, hooks = []) {
35672
+ const choices = [];
35673
+ const folderedCategories = ["scripts", "skills"];
35674
+ const grouped = groupByCategory(changedItems);
35675
+ for (const [category, items] of grouped) {
35676
+ if (folderedCategories.includes(category)) {
35677
+ const folderMap = new Map;
35678
+ for (const item of items) {
35679
+ const topLevel = item.name.split("/")[0];
35680
+ if (!folderMap.has(topLevel))
35681
+ folderMap.set(topLevel, []);
35682
+ folderMap.get(topLevel).push(item);
35683
+ }
35684
+ for (const [folder, folderItems] of folderMap) {
35685
+ const summary = aggregateByTopLevelFolder(folderItems)[0];
35686
+ choices.push({
35687
+ value: { type: "folder", folder, category, items: folderItems },
35688
+ label: `\uD83D\uDCC1 ${category}/${folder}`,
35689
+ hint: formatFolderHint(summary)
35690
+ });
35691
+ }
35692
+ } else {
35693
+ for (const item of items) {
35694
+ const icons = { new: "\uD83C\uDD95", modified: "\uD83D\uDCDD", deleted: "\uD83D\uDDD1️", unchanged: "" };
35695
+ const actions = { new: "add", modified: "update", deleted: "remove", unchanged: "" };
35696
+ choices.push({
35697
+ value: { type: "file", item },
35698
+ label: `${icons[item.status]} ${item.relativePath}`,
35699
+ hint: actions[item.status]
35700
+ });
35701
+ }
35702
+ }
35703
+ }
35704
+ for (const hook of hooks) {
35705
+ const icon = hook.status === "new" ? "\uD83C\uDD95" : "\uD83D\uDCDD";
35706
+ const action = hook.status === "new" ? "add" : "update";
35707
+ const matcherDisplay = hook.matcher || "*";
35708
+ choices.push({
35709
+ value: { type: "hook", hook },
35710
+ label: `${icon} settings.json → ${hook.hookType}[${matcherDisplay}]`,
35711
+ hint: action
35712
+ });
35713
+ }
35714
+ return choices;
35715
+ }
35716
+ function formatFolderHint(summary) {
35717
+ const parts = [];
35718
+ if (summary.newCount > 0)
35719
+ parts.push(`+${summary.newCount}`);
35720
+ if (summary.modifiedCount > 0)
35721
+ parts.push(`~${summary.modifiedCount}`);
35722
+ if (summary.deletedCount > 0)
35723
+ parts.push(`-${summary.deletedCount}`);
35724
+ return parts.join(", ");
35725
+ }
35726
+ function expandSelections(selections) {
35727
+ const items = [];
35728
+ const hooks = [];
35729
+ for (const sel of selections) {
35730
+ if (sel.type === "file") {
35731
+ items.push(sel.item);
35732
+ } else if (sel.type === "folder") {
35733
+ items.push(...sel.items);
35734
+ } else if (sel.type === "hook") {
35735
+ hooks.push(sel.hook);
35736
+ }
35737
+ }
35738
+ return { items, hooks };
35739
+ }
35531
35740
  async function proSyncCommand(options = {}) {
35532
35741
  oe(source_default.blue(`\uD83D\uDD04 Sync Premium Configurations ${source_default.gray(`v${getVersion()}`)}`));
35533
35742
  try {
@@ -35544,7 +35753,8 @@ async function proSyncCommand(options = {}) {
35544
35753
  const result = await analyzeSyncChanges(claudeDir, githubToken);
35545
35754
  spinner.stop("Analysis complete");
35546
35755
  const changedItems = result.items.filter((i) => i.status !== "unchanged");
35547
- if (changedItems.length === 0) {
35756
+ const changedHooks = result.hooks;
35757
+ if (changedItems.length === 0 && changedHooks.length === 0) {
35548
35758
  f2.success("Everything is up to date!");
35549
35759
  $e(source_default.green("✅ No changes needed"));
35550
35760
  return;
@@ -35553,24 +35763,33 @@ async function proSyncCommand(options = {}) {
35553
35763
  f2.message("");
35554
35764
  f2.message(source_default.bold("Changes by category:"));
35555
35765
  const grouped = groupByCategory(changedItems);
35766
+ const folderedCategories = ["scripts", "skills"];
35556
35767
  for (const [category, items] of grouped) {
35557
35768
  f2.message("");
35558
35769
  f2.message(source_default.cyan.bold(` ${category.toUpperCase()}`));
35559
- for (const item of items) {
35560
- f2.message(` ${formatItem(item)}`);
35770
+ if (folderedCategories.includes(category)) {
35771
+ const folderSummaries = aggregateByTopLevelFolder(items);
35772
+ for (const summary2 of folderSummaries) {
35773
+ f2.message(` ${formatFolderSummary(summary2)}`);
35774
+ }
35775
+ } else {
35776
+ for (const item of items) {
35777
+ f2.message(` ${formatItem(item)}`);
35778
+ }
35561
35779
  }
35562
35780
  }
35563
- f2.message("");
35564
- const choices = [];
35565
- for (const item of changedItems) {
35566
- const icons = { new: "\uD83C\uDD95", modified: "\uD83D\uDCDD", deleted: "\uD83D\uDDD1️", unchanged: "" };
35567
- const actions = { new: "add", modified: "update", deleted: "remove", unchanged: "" };
35568
- choices.push({
35569
- value: item,
35570
- label: `${icons[item.status]} ${item.relativePath}`,
35571
- hint: actions[item.status]
35572
- });
35781
+ if (changedHooks.length > 0) {
35782
+ f2.message("");
35783
+ f2.message(source_default.cyan.bold(` SETTINGS (hooks)`));
35784
+ for (const hook of changedHooks) {
35785
+ const icon = hook.status === "new" ? "\uD83C\uDD95" : "\uD83D\uDCDD";
35786
+ const color = hook.status === "new" ? source_default.green : source_default.yellow;
35787
+ const matcherDisplay = hook.matcher || "*";
35788
+ f2.message(` ${icon} ${color(`${hook.hookType}[${matcherDisplay}]`)}`);
35789
+ }
35573
35790
  }
35791
+ f2.message("");
35792
+ const choices = createSelectionChoices(changedItems, changedHooks);
35574
35793
  const selected = await ae({
35575
35794
  message: "Select items to sync:",
35576
35795
  options: choices,
@@ -35581,14 +35800,16 @@ async function proSyncCommand(options = {}) {
35581
35800
  ue("Sync cancelled");
35582
35801
  process.exit(0);
35583
35802
  }
35584
- const selectedItems = selected;
35585
- if (selectedItems.length === 0) {
35803
+ const expanded = expandSelections(selected);
35804
+ const selectedItems = expanded.items;
35805
+ const selectedHooks = expanded.hooks;
35806
+ if (selectedItems.length === 0 && selectedHooks.length === 0) {
35586
35807
  f2.warn("No items selected");
35587
35808
  $e(source_default.yellow("⚠️ Nothing to sync"));
35588
35809
  return;
35589
35810
  }
35590
- const toAdd = selectedItems.filter((i) => i.status === "new").length;
35591
- const toUpdate = selectedItems.filter((i) => i.status === "modified").length;
35811
+ const toAdd = selectedItems.filter((i) => i.status === "new").length + selectedHooks.filter((h2) => h2.status === "new").length;
35812
+ const toUpdate = selectedItems.filter((i) => i.status === "modified").length + selectedHooks.filter((h2) => h2.status === "modified").length;
35592
35813
  const toRemove = selectedItems.filter((i) => i.status === "deleted").length;
35593
35814
  const summary = [
35594
35815
  toAdd > 0 ? `add ${toAdd}` : "",
@@ -35607,14 +35828,19 @@ async function proSyncCommand(options = {}) {
35607
35828
  const syncResult = await syncSelectedItems(claudeDir, selectedItems, githubToken, (file, action) => {
35608
35829
  spinner.message(`${action}: ${source_default.cyan(file)}`);
35609
35830
  });
35831
+ const hooksResult = await syncSelectedHooks(claudeDir, selectedHooks, (hook, action) => {
35832
+ spinner.message(`${action}: ${source_default.cyan(hook)}`);
35833
+ });
35610
35834
  spinner.stop("Sync complete");
35835
+ const totalSuccess = syncResult.success + hooksResult.success;
35836
+ const totalFailed = syncResult.failed + hooksResult.failed;
35611
35837
  const results = [];
35612
- if (syncResult.success > 0)
35613
- results.push(source_default.green(`${syncResult.success} added/updated`));
35838
+ if (totalSuccess > 0)
35839
+ results.push(source_default.green(`${totalSuccess} added/updated`));
35614
35840
  if (syncResult.deleted > 0)
35615
35841
  results.push(source_default.red(`${syncResult.deleted} removed`));
35616
- if (syncResult.failed > 0)
35617
- results.push(source_default.yellow(`${syncResult.failed} failed`));
35842
+ if (totalFailed > 0)
35843
+ results.push(source_default.yellow(`${totalFailed} failed`));
35618
35844
  f2.success(results.join(", "));
35619
35845
  const scriptsWereSynced = selectedItems.some((i) => i.category === "scripts");
35620
35846
  if (scriptsWereSynced) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiblueprint-cli",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "AIBlueprint CLI for setting up Claude Code configurations",
5
5
  "author": "AIBlueprint",
6
6
  "license": "MIT",