@cannbot-ai/install-helper 1.2.1-beta.0 → 1.2.1-beta.2

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/index.js CHANGED
@@ -2913,7 +2913,7 @@ function createRepositoryManager(customPath) {
2913
2913
 
2914
2914
  // src/core/cannbot-delegate.ts
2915
2915
  import { existsSync as existsSync17, readFileSync as readFileSync12, writeFileSync as writeFileSync5, copyFileSync as copyFileSync3, unlinkSync as unlinkSync6, readdirSync as readdirSync8 } from "fs";
2916
- import { join as join16, resolve as resolve2 } from "path";
2916
+ import { join as join16, resolve as resolve3 } from "path";
2917
2917
  import { execa as execa6 } from "execa";
2918
2918
 
2919
2919
  // src/core/cannbot-installer.ts
@@ -3154,7 +3154,7 @@ async function ensureCannbotDiscovery() {
3154
3154
 
3155
3155
  // src/core/cannbot-registry.ts
3156
3156
  import { existsSync as existsSync13, readFileSync as readFileSync9, readdirSync as readdirSync6, rmSync as rmSync3, rmdirSync, unlinkSync as unlinkSync4, lstatSync as lstatSync2, writeFileSync as writeFileSync3 } from "fs";
3157
- import { join as join12, resolve, sep as sep2, dirname as dirname6 } from "path";
3157
+ import { join as join12, resolve as resolve2, sep as sep2, dirname as dirname6 } from "path";
3158
3158
 
3159
3159
  // src/core/record.ts
3160
3160
  init_paths();
@@ -3163,38 +3163,88 @@ init_fs_helpers();
3163
3163
  init_logger();
3164
3164
  init_i18n();
3165
3165
  import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync8, unlinkSync as unlinkSync3, readdirSync as readdirSync5 } from "fs";
3166
- import { join as join11 } from "path";
3166
+ import { join as join11, resolve } from "path";
3167
+ var LEGACY_LOCATION_KEY = "__legacy__";
3167
3168
  function getInstallsDir() {
3168
3169
  return join11(getCannbotConfigDir(), "installs");
3169
3170
  }
3170
3171
  function getRecordPath(pluginId) {
3171
3172
  return join11(getInstallsDir(), `${pluginId}.json`);
3172
3173
  }
3173
- function readRecord(pluginId) {
3174
+ function locationKey(loc) {
3175
+ return `${loc.tool}:${loc.level}:${resolve(loc.installPath)}`;
3176
+ }
3177
+ function readStore(pluginId) {
3174
3178
  const recordPath = getRecordPath(pluginId);
3175
3179
  if (!existsSync12(recordPath)) {
3176
3180
  return null;
3177
3181
  }
3182
+ let parsed;
3178
3183
  try {
3179
- const content = readFileSync8(recordPath, "utf-8");
3180
- return JSON.parse(content);
3184
+ parsed = JSON.parse(readFileSync8(recordPath, "utf-8"));
3181
3185
  } catch {
3182
3186
  logger.warn(t("record_corrupted").replace("{file}", recordPath));
3183
3187
  return null;
3184
3188
  }
3189
+ const store = parsed;
3190
+ if (store && typeof store === "object" && "locations" in store && store.locations) {
3191
+ return { version: 2, locations: store.locations };
3192
+ }
3193
+ const legacy = parsed;
3194
+ if (legacy && typeof legacy === "object" && typeof legacy.pluginId === "string") {
3195
+ const key = legacy.tool && legacy.level && legacy.installPath ? locationKey(legacy) : LEGACY_LOCATION_KEY;
3196
+ return { version: 2, locations: { [key]: legacy } };
3197
+ }
3198
+ logger.warn(t("record_corrupted").replace("{file}", recordPath));
3199
+ return null;
3185
3200
  }
3186
- function writeRecord(record) {
3201
+ function writeStore(pluginId, store) {
3187
3202
  const installsDir = getInstallsDir();
3188
3203
  if (!existsSync12(installsDir)) {
3189
3204
  mkdirSync5(installsDir, { recursive: true });
3190
3205
  }
3191
- const recordPath = getRecordPath(record.pluginId);
3192
- atomicWriteFileSync(recordPath, JSON.stringify(record, null, 2));
3206
+ atomicWriteFileSync(getRecordPath(pluginId), JSON.stringify(store, null, 2));
3193
3207
  }
3194
- function deleteRecord(pluginId) {
3208
+ function readRecords(pluginId) {
3209
+ const store = readStore(pluginId);
3210
+ return store ? Object.values(store.locations) : [];
3211
+ }
3212
+ var byLatest = (a, b) => (b.installTime || "").localeCompare(a.installTime || "");
3213
+ function readRecord(pluginId) {
3214
+ const records = readRecords(pluginId);
3215
+ if (records.length === 0) {
3216
+ return null;
3217
+ }
3218
+ const cwd = resolve(process.cwd());
3219
+ const localMatch = records.filter((r) => r.level === "project" && resolve(r.installPath) === cwd).sort(byLatest);
3220
+ if (localMatch.length > 0) return localMatch[0];
3221
+ const globalMatch = records.filter((r) => r.level === "global").sort(byLatest);
3222
+ if (globalMatch.length > 0) return globalMatch[0];
3223
+ return [...records].sort(byLatest)[0];
3224
+ }
3225
+ function writeRecord(record) {
3226
+ const store = readStore(record.pluginId) ?? { version: 2, locations: {} };
3227
+ store.locations[locationKey(record)] = record;
3228
+ writeStore(record.pluginId, store);
3229
+ }
3230
+ function deleteRecord(pluginId, where) {
3195
3231
  const recordPath = getRecordPath(pluginId);
3196
- if (existsSync12(recordPath)) {
3232
+ if (!existsSync12(recordPath)) {
3233
+ return;
3234
+ }
3235
+ if (!where) {
3236
+ unlinkSync3(recordPath);
3237
+ return;
3238
+ }
3239
+ const store = readStore(pluginId);
3240
+ if (!store) {
3241
+ return;
3242
+ }
3243
+ delete store.locations[locationKey(where)];
3244
+ if (Object.keys(store.locations).length === 0) {
3197
3245
  unlinkSync3(recordPath);
3246
+ } else {
3247
+ writeStore(pluginId, store);
3198
3248
  }
3199
3249
  }
3200
3250
  function scanInstalledFiles(pluginId, displayName, tool, level, installPath, configRoot, manifest, externalRepoNames, configRootConfigLink) {
@@ -3445,7 +3495,7 @@ function toInstalledEntry(target, registryPath, record) {
3445
3495
  };
3446
3496
  }
3447
3497
  function scanCannbotInstalled(target) {
3448
- const root = resolve(target || process.cwd());
3498
+ const root = resolve2(target || process.cwd());
3449
3499
  const installed = [];
3450
3500
  for (const tool of CANNBOT_SUPPORTED_TOOLS) {
3451
3501
  const result = readCannbotRegistry(root, tool);
@@ -3471,7 +3521,7 @@ function countMarkerBlocks(content, pluginName) {
3471
3521
  return content.split(start).length - 1;
3472
3522
  }
3473
3523
  function removeIfSafe(pathToRemove, allowedBase) {
3474
- const resolved = resolve(pathToRemove);
3524
+ const resolved = resolve2(pathToRemove);
3475
3525
  if (resolved !== allowedBase && !resolved.startsWith(allowedBase + sep2)) {
3476
3526
  return "skipped";
3477
3527
  }
@@ -3488,8 +3538,8 @@ function removeIfSafe(pathToRemove, allowedBase) {
3488
3538
  }
3489
3539
  }
3490
3540
  function removeEmptyDirUpward(dirPath, stopAt) {
3491
- let current = resolve(dirPath);
3492
- const stop = resolve(stopAt);
3541
+ let current = resolve2(dirPath);
3542
+ const stop = resolve2(stopAt);
3493
3543
  let removedAny = false;
3494
3544
  while (current.startsWith(stop + sep2)) {
3495
3545
  try {
@@ -3505,7 +3555,7 @@ function removeEmptyDirUpward(dirPath, stopAt) {
3505
3555
  return removedAny;
3506
3556
  }
3507
3557
  function cleanHookSettings(hookSettingsPath, target, pluginId) {
3508
- const resolved = resolve(target, hookSettingsPath);
3558
+ const resolved = resolve2(target, hookSettingsPath);
3509
3559
  if (!existsSync13(resolved)) return;
3510
3560
  let settings;
3511
3561
  try {
@@ -3618,7 +3668,7 @@ function uninstallCannbotPlugin(pluginId, target) {
3618
3668
  removedDirs: 0,
3619
3669
  errors: []
3620
3670
  };
3621
- const root = resolve(target || process.cwd());
3671
+ const root = resolve2(target || process.cwd());
3622
3672
  let read = null;
3623
3673
  let record;
3624
3674
  for (const tool of CANNBOT_SUPPORTED_TOOLS) {
@@ -3652,7 +3702,7 @@ function uninstallCannbotPlugin(pluginId, target) {
3652
3702
  }
3653
3703
  if (record.instructions) {
3654
3704
  const instructionsPath = join12(root, record.instructions);
3655
- const resolved = resolve(instructionsPath);
3705
+ const resolved = resolve2(instructionsPath);
3656
3706
  if (resolved.startsWith(allowedBase + sep2) && existsSync13(resolved)) {
3657
3707
  try {
3658
3708
  const content = readFileSync9(resolved, "utf-8");
@@ -3736,16 +3786,21 @@ function uninstallCannbotPlugin(pluginId, target) {
3736
3786
  }
3737
3787
  function findOrphanedDelegatedRecords(target) {
3738
3788
  const orphans = [];
3739
- const installed = new Set(scanCannbotInstalled(target).map((e) => e.id));
3740
3789
  const installsDir = join12(getCannbotConfigDir(), "installs");
3741
3790
  if (!existsSync13(installsDir)) return orphans;
3742
3791
  for (const file of readdirSync6(installsDir)) {
3743
3792
  if (!file.endsWith(".json") || file === "skills.json") continue;
3744
3793
  const pluginId = file.replace(/\.json$/, "");
3745
3794
  try {
3746
- const rec = JSON.parse(readFileSync9(join12(installsDir, file), "utf-8"));
3747
- if (rec?.kind !== "delegated") continue;
3748
- if (!installed.has(pluginId)) {
3795
+ const records = readRecords(pluginId);
3796
+ const delegated = records.filter((r) => r?.kind === "delegated");
3797
+ if (delegated.length === 0) continue;
3798
+ const isOrphan = delegated.some((rec) => {
3799
+ const recTarget = rec.installPath || target;
3800
+ const installedThere = new Set(scanCannbotInstalled(recTarget).map((e) => e.id));
3801
+ return !installedThere.has(pluginId);
3802
+ });
3803
+ if (isOrphan) {
3749
3804
  orphans.push(pluginId);
3750
3805
  }
3751
3806
  } catch {
@@ -3886,10 +3941,7 @@ function readAllRecords() {
3886
3941
  const records = [];
3887
3942
  const plugins = getAllPlugins();
3888
3943
  for (const plugin of plugins) {
3889
- const record = readRecord(plugin.id);
3890
- if (record) {
3891
- records.push(record);
3892
- }
3944
+ records.push(...readRecords(plugin.id));
3893
3945
  }
3894
3946
  return records;
3895
3947
  }
@@ -4598,7 +4650,7 @@ function installerSupportsBundledFlag() {
4598
4650
  }
4599
4651
  async function delegateInstall(options) {
4600
4652
  const { pluginId, tool, level } = options;
4601
- const target = resolve2(options.target || process.cwd());
4653
+ const target = resolve3(options.target || process.cwd());
4602
4654
  const displayName = options.displayName || pluginId;
4603
4655
  const result = {
4604
4656
  success: false,
@@ -5331,7 +5383,9 @@ function printEnhancedSummary(results, tool, configRoot) {
5331
5383
  ""
5332
5384
  ];
5333
5385
  if (successResults.length === 1) {
5334
- const plugin = findPlugin(successResults[0].pluginId);
5386
+ const plugin = findPlugin(successResults[0].pluginId, {
5387
+ source: successResults[0].source
5388
+ });
5335
5389
  const docsPath = plugin ? `${plugin.dir}/quickstart.md` : `${successResults[0].pluginId}/quickstart.md`;
5336
5390
  lines.push(
5337
5391
  ` ${chalk6.dim(t("install_enhanced_docs"))}: ${chalk6.dim(docsPath)}`
@@ -5499,7 +5553,8 @@ async function pluginInstallFlow() {
5499
5553
  displayName: selected[index]?.plugin?.displayName || result.pluginId,
5500
5554
  success: result.success,
5501
5555
  skillsCount: result.skillsCount,
5502
- agentsCount: result.agentsCount
5556
+ agentsCount: result.agentsCount,
5557
+ source: selected[index]?.source
5503
5558
  }));
5504
5559
  printInstallSummary(summary);
5505
5560
  const configRoot = getConfigRoot(answers.tool, answers.level);
@@ -6099,6 +6154,9 @@ async function installCommand(names, options) {
6099
6154
  logger.info(`${t("install_to")}: ${chalk10.cyan(getSkillsRoot(tool2, level2))}`);
6100
6155
  logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool2))}`);
6101
6156
  logger.blank();
6157
+ if (failCount > 0) {
6158
+ process.exitCode = 1;
6159
+ }
6102
6160
  return;
6103
6161
  }
6104
6162
  }
@@ -6135,6 +6193,7 @@ async function installCommand(names, options) {
6135
6193
  logger.step(` ${p.id}${badge} (${p.aliases.join(", ")})`);
6136
6194
  }
6137
6195
  logger.info(`${t("available_skills")}: ${chalk10.cyan("install-helper install --list")}`);
6196
+ process.exitCode = 1;
6138
6197
  return;
6139
6198
  }
6140
6199
  let tool;
@@ -6188,7 +6247,6 @@ async function installCommand(names, options) {
6188
6247
  spinner.fail(t("repo_clone_failed").replace("{error}", error instanceof Error ? error.message : t("error_unknown")).replace("{url}", "").replace("{dir}", ""));
6189
6248
  return;
6190
6249
  }
6191
- const allPlugins = getAllPlugins();
6192
6250
  const results = await installPlugins(
6193
6251
  pluginsToInstall,
6194
6252
  tool,
@@ -6199,17 +6257,24 @@ async function installCommand(names, options) {
6199
6257
  source
6200
6258
  );
6201
6259
  const summary = results.map((result) => {
6202
- const plugin = allPlugins.find((p) => p.id === result.pluginId);
6260
+ const plugin = findPlugin(result.pluginId, { source });
6203
6261
  return {
6204
6262
  pluginId: result.pluginId,
6205
6263
  displayName: plugin?.displayName || result.pluginId,
6206
6264
  success: result.success,
6207
6265
  skillsCount: result.skillsCount,
6208
- agentsCount: result.agentsCount
6266
+ agentsCount: result.agentsCount,
6267
+ // the command-line source is the user intent — legacy entries
6268
+ // carry no `source` field, so plugin?.source alone would lose the
6269
+ // "skills" scope and the docs hint would resolve cannbot-first
6270
+ source: source ?? plugin?.source
6209
6271
  };
6210
6272
  });
6211
6273
  printInstallSummary(summary);
6212
6274
  printEnhancedSummary(summary, tool, configRoot);
6275
+ if (results.some((r) => !r.success)) {
6276
+ process.exitCode = 1;
6277
+ }
6213
6278
  }
6214
6279
  }
6215
6280
  if (skills.length > 0) {
@@ -6246,6 +6311,9 @@ async function installCommand(names, options) {
6246
6311
  logger.info(`${t("install_to")}: ${chalk10.cyan(getSkillsRoot(tool, level))}`);
6247
6312
  logger.info(`${t("start_to_use").replace("{tool}", chalk10.green(tool))}`);
6248
6313
  logger.blank();
6314
+ if (failCount > 0) {
6315
+ process.exitCode = 1;
6316
+ }
6249
6317
  }
6250
6318
  }
6251
6319
  async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes, source) {
@@ -6286,7 +6354,7 @@ async function installPlugins(pluginIds, tool, level, repoPath, installPath, yes
6286
6354
  // src/commands/uninstall.ts
6287
6355
  init_registry();
6288
6356
  import { existsSync as existsSync21, unlinkSync as unlinkSync8, rmdirSync as rmdirSync2, readdirSync as readdirSync11, readFileSync as readFileSync15 } from "fs";
6289
- import { resolve as resolve3, basename as basename4, sep as sep3, join as join20 } from "path";
6357
+ import { resolve as resolve4, basename as basename4, sep as sep3, join as join20 } from "path";
6290
6358
  import chalk11 from "chalk";
6291
6359
  import { select as select5, checkbox as checkbox2, Separator as Separator4, confirm as confirm3 } from "@inquirer/prompts";
6292
6360
  init_config();
@@ -6686,13 +6754,13 @@ async function uninstallPluginById(pluginId, batchMode) {
6686
6754
  return;
6687
6755
  }
6688
6756
  logger.info(`${t("uninstall_in_progress")} ${plugin.displayName}...`);
6689
- const configRoot = getConfigRoot(record.tool, record.level);
6690
- const allowedBases = [resolve3(configRoot), resolve3(record.installPath)];
6757
+ const configRoot = record.configRoot;
6758
+ const allowedBases = [resolve4(configRoot), resolve4(record.installPath)];
6691
6759
  if (record.tool === "codex") {
6692
- allowedBases.push(resolve3(getSkillsRoot(record.tool, record.level, record.installPath)));
6760
+ allowedBases.push(resolve4(getSkillsRoot(record.tool, record.level, record.installPath)));
6693
6761
  }
6694
6762
  const isSafePath = (p) => {
6695
- const resolved = resolve3(p);
6763
+ const resolved = resolve4(p);
6696
6764
  return allowedBases.some((base) => resolved === base || resolved.startsWith(base + sep3));
6697
6765
  };
6698
6766
  let removedFiles = 0;
@@ -6720,7 +6788,7 @@ async function uninstallPluginById(pluginId, batchMode) {
6720
6788
  const sortedDirs = [...record.directories].filter(isSafePath).sort((a, b) => b.length - a.length);
6721
6789
  for (const dirPath of sortedDirs) {
6722
6790
  try {
6723
- if (resolve3(dirPath) === resolve3(configRoot)) {
6791
+ if (resolve4(dirPath) === resolve4(configRoot)) {
6724
6792
  continue;
6725
6793
  }
6726
6794
  if (existsSync21(dirPath)) {
@@ -6737,7 +6805,7 @@ async function uninstallPluginById(pluginId, batchMode) {
6737
6805
  }
6738
6806
  if (record.tool === "codex") {
6739
6807
  try {
6740
- const agentsRoot = resolve3(getSkillsRoot(record.tool, record.level, record.installPath), "..");
6808
+ const agentsRoot = resolve4(getSkillsRoot(record.tool, record.level, record.installPath), "..");
6741
6809
  if (existsSync21(agentsRoot) && readdirSync11(agentsRoot).length === 0) {
6742
6810
  rmdirSync2(agentsRoot);
6743
6811
  logger.step(` ${t("uninstall_clean_empty_dir")}: ${basename4(agentsRoot)}/`);
@@ -6746,7 +6814,11 @@ async function uninstallPluginById(pluginId, batchMode) {
6746
6814
  } catch {
6747
6815
  }
6748
6816
  }
6749
- deleteRecord(plugin.id);
6817
+ deleteRecord(plugin.id, {
6818
+ tool: record.tool,
6819
+ level: record.level,
6820
+ installPath: record.installPath
6821
+ });
6750
6822
  removeInstalledPlugin(plugin.id);
6751
6823
  const backups = findBackups(configRoot);
6752
6824
  const otherBackups = backups.filter((b) => b.pluginId !== pluginId);