@synity/bitrix-skills 1.3.7 → 1.3.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.8
4
+
5
+ ### Patch Changes
6
+
7
+ - fix(list): global features now show correct [installed] status by checking ~/.claude/skills/<name>/ on disk instead of project manifest
8
+
9
+ fix(install): add --all flag + generic install handler for bx, bx-crm, bx-calendar global skills
10
+
3
11
  ## 1.3.7
4
12
 
5
13
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -944,11 +944,15 @@ import { createRequire } from "module";
944
944
 
945
945
  // src/commands/install.ts
946
946
  init_esm_shims();
947
+ init_fs_safety();
947
948
  import { Command, Option } from "clipanion";
948
949
  import chalk from "chalk";
949
950
  import { existsSync as existsSync4 } from "fs";
951
+ import { access as access4, copyFile as copyFile3, mkdir as mkdir4, readdir as readdir2 } from "fs/promises";
950
952
  import { createInterface } from "readline";
951
- import { join as join4, relative as relative2 } from "path";
953
+ import { dirname as dirname3, join as join4, relative as relative2, resolve as resolve2 } from "path";
954
+ import { homedir as homedir2 } from "os";
955
+ import { fileURLToPath as fileURLToPath5 } from "url";
952
956
 
953
957
  // src/lib/feature-registry.ts
954
958
  init_esm_shims();
@@ -1046,11 +1050,11 @@ function computeChecksum(filepath) {
1046
1050
 
1047
1051
  // src/commands/install.ts
1048
1052
  function promptKey() {
1049
- return new Promise((resolve2) => {
1053
+ return new Promise((resolve4) => {
1050
1054
  const rl = createInterface({ input: process.stdin, output: process.stdout });
1051
1055
  rl.question(chalk.cyan("License key (Enter to skip \u2014 free tier only): "), (answer) => {
1052
1056
  rl.close();
1053
- resolve2(answer.trim());
1057
+ resolve4(answer.trim());
1054
1058
  });
1055
1059
  });
1056
1060
  }
@@ -1096,6 +1100,63 @@ async function installBxTask(cwd) {
1096
1100
  return { ok: false, message: `bx-task: ${err.message}` };
1097
1101
  }
1098
1102
  }
1103
+ async function getFeatureAssetsDir(featureName) {
1104
+ const here = dirname3(fileURLToPath5(import.meta.url));
1105
+ const candidates = [
1106
+ // dist/cli.js context (inlined): ../src/features/<name>/assets
1107
+ resolve2(here, `../src/features/${featureName}/assets`),
1108
+ // dist/features/<name>/ context: ../../../src/features/<name>/assets
1109
+ resolve2(here, `../../../src/features/${featureName}/assets`),
1110
+ // dev (running from src/)
1111
+ resolve2(here, `../features/${featureName}/assets`)
1112
+ ];
1113
+ for (const c of candidates) {
1114
+ try {
1115
+ await access4(c);
1116
+ return c;
1117
+ } catch {
1118
+ }
1119
+ }
1120
+ return candidates[0];
1121
+ }
1122
+ async function copyDirRecursive(srcDir, destDir, skillBase, installedFiles, onConflict) {
1123
+ await mkdir4(destDir, { recursive: true });
1124
+ const entries = await readdir2(srcDir, { withFileTypes: true });
1125
+ for (const entry of entries) {
1126
+ const src = join4(srcDir, entry.name);
1127
+ const dest = join4(destDir, entry.name);
1128
+ if (entry.isDirectory()) {
1129
+ await copyDirRecursive(src, dest, skillBase, installedFiles, onConflict);
1130
+ continue;
1131
+ }
1132
+ assertContainedIn(dest, skillBase, dest);
1133
+ await assertNotSymlink(dest);
1134
+ try {
1135
+ await access4(dest);
1136
+ if (onConflict === "skip") continue;
1137
+ } catch {
1138
+ }
1139
+ await copyFile3(src, dest);
1140
+ installedFiles.push(dest);
1141
+ }
1142
+ }
1143
+ async function installGlobalSkill(featureName) {
1144
+ try {
1145
+ const skillBase = resolve2(homedir2(), ".claude", "skills");
1146
+ const dest = resolve2(skillBase, featureName);
1147
+ const assetsDir = await getFeatureAssetsDir(featureName);
1148
+ const installedFiles = [];
1149
+ await copyDirRecursive(assetsDir, dest, skillBase, installedFiles, "overwrite");
1150
+ return {
1151
+ ok: true,
1152
+ message: `${featureName}: ${installedFiles.length} files installed`,
1153
+ installPath: dest,
1154
+ installedAbsPaths: installedFiles
1155
+ };
1156
+ } catch (err) {
1157
+ return { ok: false, message: `${featureName}: ${err.message}` };
1158
+ }
1159
+ }
1099
1160
  function upsertFeature(manifest, entry) {
1100
1161
  const idx = manifest.features.findIndex((f) => f.name === entry.name);
1101
1162
  const features = [...manifest.features];
@@ -1122,6 +1183,7 @@ var InstallCommand = class extends Command {
1122
1183
  ]
1123
1184
  });
1124
1185
  key = Option.String("--key", { description: "License key to unlock paid tier features" });
1186
+ all = Option.Boolean("--all", false, { description: "Install all available features" });
1125
1187
  featuresFlag = Option.String("--features", { description: "Comma-separated feature names" });
1126
1188
  featureArgs = Option.Rest({ required: 0 });
1127
1189
  async execute() {
@@ -1207,10 +1269,15 @@ Installing ${chalk.bold(name)}...
1207
1269
  ok = r.ok;
1208
1270
  message = r.message;
1209
1271
  if (r.installPath) {
1210
- const { homedir: homedir2 } = await import("os");
1211
1272
  installPath = join4(homedir2(), ".claude", "skills", "bx-task");
1212
1273
  }
1213
1274
  installedAbsPaths = r.installedAbsPaths ?? [];
1275
+ } else if (featureInfo.target === "global") {
1276
+ const r = await installGlobalSkill(name);
1277
+ ok = r.ok;
1278
+ message = r.message;
1279
+ if (r.installPath) installPath = r.installPath;
1280
+ installedAbsPaths = r.installedAbsPaths ?? [];
1214
1281
  } else {
1215
1282
  this.context.stderr.write(chalk.yellow(` ! No install handler for feature: ${name}
1216
1283
  `));
@@ -1321,6 +1388,15 @@ Uninstalling ${chalk2.bold(name)}...
1321
1388
  init_esm_shims();
1322
1389
  import { Command as Command3 } from "clipanion";
1323
1390
  import chalk3 from "chalk";
1391
+ import { existsSync as existsSync5 } from "fs";
1392
+ import { resolve as resolve3 } from "path";
1393
+ import { homedir as homedir3 } from "os";
1394
+ function isInstalled(f, manifest) {
1395
+ if (f.target === "global") {
1396
+ return existsSync5(resolve3(homedir3(), ".claude", "skills", f.name));
1397
+ }
1398
+ return !!manifest?.features.find((e) => e.name === f.name);
1399
+ }
1324
1400
  var ListCommand = class extends Command3 {
1325
1401
  static paths = [["list"]];
1326
1402
  static usage = Command3.Usage({
@@ -1329,20 +1405,19 @@ var ListCommand = class extends Command3 {
1329
1405
  async execute() {
1330
1406
  const features = listFeatures();
1331
1407
  const manifest = readManifest(process.cwd());
1332
- const installed = new Map(manifest?.features.map((f) => [f.name, f]) ?? []);
1333
1408
  if (features.length === 0) {
1334
1409
  this.context.stdout.write(chalk3.yellow("No features found.\n"));
1335
1410
  return 0;
1336
1411
  }
1337
1412
  this.context.stdout.write("\nAvailable features:\n");
1338
1413
  for (const f of features) {
1339
- const entry = installed.get(f.name);
1340
- const check = entry ? chalk3.green("\u2713") : chalk3.gray("\u25CB");
1414
+ const installed = isInstalled(f, manifest);
1415
+ const check = installed ? chalk3.green("\u2713") : chalk3.gray("\u25CB");
1341
1416
  const name = chalk3.bold(f.name.padEnd(12));
1342
1417
  const ver = chalk3.cyan(f.version.padEnd(14));
1343
1418
  const target = chalk3.gray(f.target.padEnd(8));
1344
1419
  const desc = f.description;
1345
- const status = entry ? chalk3.green("[installed]") : chalk3.gray("[not installed]");
1420
+ const status = installed ? chalk3.green("[installed]") : chalk3.gray("[not installed]");
1346
1421
  this.context.stdout.write(` ${check} ${name} ${ver} ${target} ${desc} ${status}
1347
1422
  `);
1348
1423
  }
@@ -1354,7 +1429,7 @@ var ListCommand = class extends Command3 {
1354
1429
  init_esm_shims();
1355
1430
  import { Command as Command4 } from "clipanion";
1356
1431
  import chalk4 from "chalk";
1357
- import { existsSync as existsSync5 } from "fs";
1432
+ import { existsSync as existsSync6 } from "fs";
1358
1433
  import { join as join5 } from "path";
1359
1434
  var VerifyCommand = class extends Command4 {
1360
1435
  static paths = [["verify"]];
@@ -1378,7 +1453,7 @@ var VerifyCommand = class extends Command4 {
1378
1453
  `);
1379
1454
  this.context.stdout.write(chalk4.gray(` install path: ${feature.installPath}
1380
1455
  `));
1381
- if (!existsSync5(feature.installPath)) {
1456
+ if (!existsSync6(feature.installPath)) {
1382
1457
  this.context.stderr.write(chalk4.red(` \u2717 install path missing: ${feature.installPath}
1383
1458
  `));
1384
1459
  anyMismatch = true;
@@ -1391,7 +1466,7 @@ var VerifyCommand = class extends Command4 {
1391
1466
  }
1392
1467
  for (const [rel, expectedHash] of Object.entries(stored)) {
1393
1468
  const absPath = join5(feature.installPath, rel);
1394
- if (!existsSync5(absPath)) {
1469
+ if (!existsSync6(absPath)) {
1395
1470
  this.context.stderr.write(chalk4.red(` \u2717 missing: ${rel}
1396
1471
  `));
1397
1472
  anyMismatch = true;
@@ -1430,7 +1505,7 @@ var VerifyCommand = class extends Command4 {
1430
1505
  init_esm_shims();
1431
1506
  import { Command as Command5 } from "clipanion";
1432
1507
  import chalk5 from "chalk";
1433
- import { existsSync as existsSync6 } from "fs";
1508
+ import { existsSync as existsSync7 } from "fs";
1434
1509
  import { relative as relative3 } from "path";
1435
1510
  var UpdateCommand = class extends Command5 {
1436
1511
  static paths = [["update"]];
@@ -1478,7 +1553,7 @@ var UpdateCommand = class extends Command5 {
1478
1553
  const { buildDestMap: buildDestMap2 } = await Promise.resolve().then(() => (init_dest_map(), dest_map_exports));
1479
1554
  const assetManifest = await loadManifest2();
1480
1555
  const dests = buildDestMap2(assetManifest, cwd);
1481
- installedAbsPaths = dests.map((d) => d.destAbs).filter((p) => existsSync6(p));
1556
+ installedAbsPaths = dests.map((d) => d.destAbs).filter((p) => existsSync7(p));
1482
1557
  } catch {
1483
1558
  }
1484
1559
  } else if (feature.name === "bx-task") {
@@ -1507,7 +1582,7 @@ var UpdateCommand = class extends Command5 {
1507
1582
  const checksums = {};
1508
1583
  for (const absPath of installedAbsPaths) {
1509
1584
  try {
1510
- if (existsSync6(absPath)) {
1585
+ if (existsSync7(absPath)) {
1511
1586
  const rel = relative3(feature.installPath, absPath);
1512
1587
  checksums[rel] = computeChecksum(absPath);
1513
1588
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synity/bitrix-skills",
3
- "version": "1.3.7",
3
+ "version": "1.3.8",
4
4
  "description": "Multi-feature Bitrix24 tooling CLI for Synity projects",
5
5
  "type": "module",
6
6
  "bin": {