@kb-labs/cli-commands 2.14.0 → 2.16.0

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.
@@ -1,5 +1,5 @@
1
- import { d as CommandManifest, A as AvailabilityCheck, R as RegisteredCommand, D as DiscoveryResult } from '../discover-BY7VE2Hf.js';
2
- export { b as CacheFile, c as CommandLookupResult, e as CommandModule, f as CommandType, F as FlagDefinition, G as GlobalFlags, g as PackageCacheEntry, P as ProductGroup, _ as __test, h as discoverManifests, i as discoverManifestsByNamespace, j as findCommand, k as findCommandWithType, r as registry, l as resetInProcCache } from '../discover-BY7VE2Hf.js';
1
+ import { d as CommandManifest, A as AvailabilityCheck, R as RegisteredCommand, D as DiscoveryResult } from '../discover-Be0ez3AR.js';
2
+ export { b as CacheFile, c as CommandLookupResult, e as CommandModule, f as CommandType, F as FlagDefinition, G as GlobalFlags, g as PackageCacheEntry, P as ProductGroup, _ as __test, h as discoverManifests, i as discoverManifestsByNamespace, j as findCommand, k as findCommandWithType, r as registry, l as resetInProcCache } from '../discover-Be0ez3AR.js';
3
3
  import { ILogger } from '@kb-labs/core-platform';
4
4
  import '@kb-labs/plugin-contracts';
5
5
 
@@ -1279,23 +1279,24 @@ async function registerManifests(discoveryResults, registry2, options = {}) {
1279
1279
  } catch (hookError) {
1280
1280
  log2.debug(`Lifecycle hooks unavailable for ${manifest.id}: ${hookError.message}`);
1281
1281
  }
1282
- const existing = globalIds.get(manifest.id);
1282
+ const canonicalKey = manifest.group ? `${manifest.group}:${manifest.id}` : manifest.id;
1283
+ const existing = globalIds.get(canonicalKey);
1283
1284
  if (existing) {
1284
1285
  const collision = checkCollision(manifest, existing, result.source, namespace);
1285
1286
  if (collision.shouldShadow) {
1286
1287
  existing.shadowed = true;
1287
- globalIds.set(manifest.id, cmd);
1288
+ globalIds.set(canonicalKey, cmd);
1288
1289
  if (logLevel === "info" || logLevel === "debug") {
1289
- log2.info(`${manifest.id} from ${result.source} shadows ${existing.source} version`);
1290
+ log2.info(`${canonicalKey} from ${result.source} shadows ${existing.source} version`);
1290
1291
  }
1291
1292
  } else {
1292
1293
  cmd.shadowed = true;
1293
1294
  if (logLevel === "info" || logLevel === "debug") {
1294
- log2.info(`${manifest.id} from ${result.source} shadowed by ${existing.source} version`);
1295
+ log2.info(`${canonicalKey} from ${result.source} shadowed by ${existing.source} version`);
1295
1296
  }
1296
1297
  }
1297
1298
  } else {
1298
- globalIds.set(manifest.id, cmd);
1299
+ globalIds.set(canonicalKey, cmd);
1299
1300
  }
1300
1301
  const aliasesToCheck = manifest.aliases || [];
1301
1302
  for (const alias of aliasesToCheck) {
@@ -1427,17 +1428,30 @@ function manifestToCommand(registered) {
1427
1428
  }
1428
1429
  };
1429
1430
  }
1431
+ function buildCanonicalId(manifest) {
1432
+ const { id, group, subgroup } = manifest;
1433
+ if (group && subgroup) {
1434
+ return `${group}:${subgroup}:${id}`;
1435
+ }
1436
+ if (group) {
1437
+ return `${group}:${id}`;
1438
+ }
1439
+ return id;
1440
+ }
1430
1441
  var InMemoryRegistry = class {
1431
- // Separate collections for security isolation
1442
+ // System commands (in-process): key = any registered name/alias
1432
1443
  systemCommands = /* @__PURE__ */ new Map();
1433
- // System commands (in-process)
1434
- pluginCommands = /* @__PURE__ */ new Map();
1435
- // Plugin commands (subprocess)
1436
- // Legacy unified collection for backward compatibility
1444
+ // Plugin commands: key = canonicalId only
1445
+ pluginByCanonical = /* @__PURE__ */ new Map();
1446
+ // Alias canonicalId: covers all user-input variants that resolve to a plugin
1447
+ pluginAliases = /* @__PURE__ */ new Map();
1448
+ // Legacy unified collection for backward compatibility (display/get)
1437
1449
  byName = /* @__PURE__ */ new Map();
1438
1450
  groups = /* @__PURE__ */ new Map();
1451
+ // manifests: any key → RegisteredCommand (for listing/lookup; still stores multi-key for compat)
1439
1452
  manifests = /* @__PURE__ */ new Map();
1440
1453
  partial = false;
1454
+ // ─── System command registration ────────────────────────────────────────
1441
1455
  register(cmd) {
1442
1456
  this.systemCommands.set(cmd.name, cmd);
1443
1457
  this.byName.set(cmd.name, cmd);
@@ -1476,11 +1490,12 @@ var InMemoryRegistry = class {
1476
1490
  }
1477
1491
  }
1478
1492
  }
1493
+ // ─── Plugin command registration ────────────────────────────────────────
1479
1494
  registerManifest(cmd) {
1480
- const cmdId = cmd.manifest.id;
1481
- const hasCollision = this.systemCommands.has(cmdId);
1482
- if (hasCollision) {
1483
- console.warn(`[registry] Plugin command "${cmdId}" collides with system command. System command takes priority.`);
1495
+ const canonicalId = buildCanonicalId(cmd.manifest);
1496
+ const collisionKey = this._findSystemCollision(cmd.manifest, canonicalId);
1497
+ if (collisionKey) {
1498
+ console.warn(`[registry] Plugin command "${canonicalId}" collides with system command "${collisionKey}". System command takes priority.`);
1484
1499
  cmd.shadowed = true;
1485
1500
  }
1486
1501
  const collisionAliases = /* @__PURE__ */ new Set();
@@ -1490,58 +1505,137 @@ var InMemoryRegistry = class {
1490
1505
  collisionAliases.add(alias);
1491
1506
  }
1492
1507
  }
1493
- this.pluginCommands.set(cmdId, cmd);
1494
- this.manifests.set(cmdId, cmd);
1495
- if (!hasCollision) {
1496
- const commandAdapter = manifestToCommand(cmd);
1497
- this.byName.set(cmdId, commandAdapter);
1498
- this.byName.set(commandAdapter.name, commandAdapter);
1499
- if (cmdId.includes(":")) {
1500
- const spaceSeparated = cmdId.replace(/:/g, " ");
1501
- this.byName.set(spaceSeparated, commandAdapter);
1502
- }
1503
- if (cmd.manifest.group && cmd.manifest.subgroup) {
1504
- const fullPath = `${cmd.manifest.group} ${cmd.manifest.subgroup} ${cmd.manifest.id}`;
1505
- const colonPath = `${cmd.manifest.group}:${cmd.manifest.subgroup}:${cmd.manifest.id}`;
1506
- this.byName.set(fullPath, commandAdapter);
1507
- this.byName.set(colonPath, commandAdapter);
1508
- this.manifests.set(fullPath, cmd);
1509
- this.manifests.set(colonPath, cmd);
1510
- this.pluginCommands.set(fullPath, cmd);
1511
- this.pluginCommands.set(colonPath, cmd);
1512
- const twoPartName = `${cmd.manifest.group} ${cmd.manifest.id}`;
1513
- if (!this.byName.has(twoPartName)) {
1514
- this.byName.set(twoPartName, commandAdapter);
1515
- }
1516
- const subgroupKey = `${cmd.manifest.group} ${cmd.manifest.subgroup}`;
1517
- if (!this.groups.has(subgroupKey)) {
1518
- this.groups.set(subgroupKey, {
1519
- name: subgroupKey,
1520
- describe: cmd.manifest.subgroup,
1521
- commands: []
1522
- });
1523
- this.byName.set(subgroupKey, this.groups.get(subgroupKey));
1508
+ this.pluginByCanonical.set(canonicalId, cmd);
1509
+ this.manifests.set(canonicalId, cmd);
1510
+ this.manifests.set(cmd.manifest.id, cmd);
1511
+ if (cmd.shadowed) {
1512
+ return;
1513
+ }
1514
+ this._registerPluginAliases(cmd, canonicalId, collisionAliases);
1515
+ const commandAdapter = manifestToCommand(cmd);
1516
+ this.byName.set(canonicalId, commandAdapter);
1517
+ const spaceCanonical = canonicalId.replace(/:/g, " ");
1518
+ this.byName.set(spaceCanonical, commandAdapter);
1519
+ this._registerSyntheticGroups(cmd, commandAdapter);
1520
+ }
1521
+ /**
1522
+ * Check whether any system command already owns the canonical id or its parts.
1523
+ * Returns the colliding key if found, null otherwise.
1524
+ */
1525
+ _findSystemCollision(manifest, canonicalId) {
1526
+ if (this.systemCommands.has(canonicalId)) {
1527
+ return canonicalId;
1528
+ }
1529
+ const space = canonicalId.replace(/:/g, " ");
1530
+ if (this.systemCommands.has(space)) {
1531
+ return space;
1532
+ }
1533
+ return null;
1534
+ }
1535
+ /**
1536
+ * Register all lookup aliases for a plugin command.
1537
+ *
1538
+ * Priority (what beats what) is handled in resolveToCanonical at query time.
1539
+ * Here we just build the mapping.
1540
+ *
1541
+ * Aliases registered:
1542
+ * - canonicalId itself ("marketplace:plugins:list")
1543
+ * - bare id ("list") ← low priority, may clash
1544
+ * - 2-part shorthand ("marketplace:list", "marketplace list")
1545
+ * - manifest.aliases[] (user-defined aliases, except collisions)
1546
+ */
1547
+ _registerPluginAliases(cmd, canonicalId, collisionAliases) {
1548
+ const { id, group, subgroup } = cmd.manifest;
1549
+ const register = (key) => {
1550
+ if (!this.systemCommands.has(key) && !this.systemCommands.has(key.replace(/:/g, " "))) {
1551
+ if (!this.pluginAliases.has(key)) {
1552
+ this.pluginAliases.set(key, canonicalId);
1553
+ this.manifests.set(key, cmd);
1524
1554
  }
1525
- this.groups.get(subgroupKey).commands.push(commandAdapter);
1526
- } else if (cmd.manifest.group) {
1527
- const fullName = `${cmd.manifest.group} ${cmd.manifest.id}`;
1528
- const colonName = `${cmd.manifest.group}:${cmd.manifest.id}`;
1529
- this.byName.set(fullName, commandAdapter);
1530
- this.byName.set(colonName, commandAdapter);
1531
- this.manifests.set(fullName, cmd);
1532
- this.manifests.set(colonName, cmd);
1533
- this.pluginCommands.set(fullName, cmd);
1534
- this.pluginCommands.set(colonName, cmd);
1535
1555
  }
1536
- if (cmd.manifest.aliases) {
1537
- for (const alias of cmd.manifest.aliases) {
1538
- if (!collisionAliases.has(alias)) {
1539
- this.byName.set(alias, commandAdapter);
1540
- }
1541
- }
1556
+ };
1557
+ this.pluginAliases.set(canonicalId, canonicalId);
1558
+ const spaceCanonical = canonicalId.replace(/:/g, " ");
1559
+ this.pluginAliases.set(spaceCanonical, canonicalId);
1560
+ this.manifests.set(spaceCanonical, cmd);
1561
+ if (group && subgroup) {
1562
+ const twoPartColon = `${group}:${id}`;
1563
+ const twoPartSpace = `${group} ${id}`;
1564
+ register(twoPartColon);
1565
+ register(twoPartSpace);
1566
+ this.manifests.set(twoPartColon, cmd);
1567
+ this.manifests.set(twoPartSpace, cmd);
1568
+ const fullPath = `${group} ${subgroup} ${id}`;
1569
+ this.pluginAliases.set(fullPath, canonicalId);
1570
+ this.manifests.set(fullPath, cmd);
1571
+ const colonPath = `${group}:${subgroup}:${id}`;
1572
+ this.pluginAliases.set(colonPath, canonicalId);
1573
+ this.manifests.set(colonPath, cmd);
1574
+ } else if (group) {
1575
+ const colonName = `${group}:${id}`;
1576
+ const spaceName = `${group} ${id}`;
1577
+ this.pluginAliases.set(colonName, canonicalId);
1578
+ this.pluginAliases.set(spaceName, canonicalId);
1579
+ this.manifests.set(colonName, cmd);
1580
+ this.manifests.set(spaceName, cmd);
1581
+ }
1582
+ register(id);
1583
+ for (const alias of cmd.manifest.aliases || []) {
1584
+ if (!collisionAliases.has(alias)) {
1585
+ register(alias);
1542
1586
  }
1543
1587
  }
1544
1588
  }
1589
+ /**
1590
+ * Register synthetic subgroups for help display.
1591
+ */
1592
+ _registerSyntheticGroups(cmd, commandAdapter) {
1593
+ const { id, group, subgroup } = cmd.manifest;
1594
+ if (group && subgroup) {
1595
+ const subgroupKey = `${group} ${subgroup}`;
1596
+ if (!this.groups.has(subgroupKey)) {
1597
+ this.groups.set(subgroupKey, {
1598
+ name: subgroupKey,
1599
+ describe: subgroup,
1600
+ commands: []
1601
+ });
1602
+ this.byName.set(subgroupKey, this.groups.get(subgroupKey));
1603
+ }
1604
+ this.groups.get(subgroupKey).commands.push(commandAdapter);
1605
+ const twoPartSpace = `${group} ${id}`;
1606
+ if (!this.byName.has(twoPartSpace)) {
1607
+ this.byName.set(twoPartSpace, commandAdapter);
1608
+ }
1609
+ const twoPartColon = `${group}:${id}`;
1610
+ if (!this.byName.has(twoPartColon)) {
1611
+ this.byName.set(twoPartColon, commandAdapter);
1612
+ }
1613
+ const fullPath = `${group} ${subgroup} ${id}`;
1614
+ this.byName.set(fullPath, commandAdapter);
1615
+ } else if (group) {
1616
+ const fullName = `${group} ${id}`;
1617
+ const colonName = `${group}:${id}`;
1618
+ this.byName.set(fullName, commandAdapter);
1619
+ this.byName.set(colonName, commandAdapter);
1620
+ }
1621
+ }
1622
+ // ─── Resolution ──────────────────────────────────────────────────────────
1623
+ /**
1624
+ * Resolve any user input to its canonical plugin ID.
1625
+ *
1626
+ * Returns canonicalId if found in plugin aliases, undefined otherwise.
1627
+ */
1628
+ resolveToCanonical(input) {
1629
+ if (this.pluginAliases.has(input)) {
1630
+ return this.pluginAliases.get(input);
1631
+ }
1632
+ const converted = input.includes(":") ? input.replace(/:/g, " ") : input.replace(/ /g, ":");
1633
+ if (this.pluginAliases.has(converted)) {
1634
+ return this.pluginAliases.get(converted);
1635
+ }
1636
+ return void 0;
1637
+ }
1638
+ // ─── Public API ──────────────────────────────────────────────────────────
1545
1639
  markPartial(partial) {
1546
1640
  this.partial = partial;
1547
1641
  }
@@ -1553,23 +1647,43 @@ var InMemoryRegistry = class {
1553
1647
  }
1554
1648
  listManifests() {
1555
1649
  const unique = /* @__PURE__ */ new Set();
1556
- for (const cmd of this.manifests.values()) {
1650
+ for (const cmd of this.pluginByCanonical.values()) {
1557
1651
  unique.add(cmd);
1558
1652
  }
1559
1653
  return Array.from(unique);
1560
1654
  }
1561
1655
  has(name) {
1562
- return this.byName.has(name);
1656
+ return this.byName.has(name) || this.pluginAliases.has(name);
1563
1657
  }
1564
1658
  /**
1565
- * Get command with type information for secure routing
1566
- *
1567
- * Returns type='system' for commands from registerGroup() - execute in-process
1568
- * Returns type='plugin' for commands from registerManifest() - execute in subprocess
1659
+ * Get command with type information for secure routing.
1569
1660
  *
1570
- * This separation prevents malicious plugins from escaping the sandbox.
1661
+ * System commands ALWAYS win checked first before any plugin lookup.
1662
+ * Returns type='system' for system commands (in-process execution).
1663
+ * Returns type='plugin' for plugin commands (subprocess execution).
1571
1664
  */
1572
1665
  getWithType(nameOrPath) {
1666
+ const normalized = typeof nameOrPath === "string" ? nameOrPath : nameOrPath.join(" ");
1667
+ if (this.systemCommands.has(normalized)) {
1668
+ return { cmd: this.systemCommands.get(normalized), type: "system" };
1669
+ }
1670
+ const colonVariant = normalized.replace(/ /g, ":");
1671
+ if (this.systemCommands.has(colonVariant)) {
1672
+ return { cmd: this.systemCommands.get(colonVariant), type: "system" };
1673
+ }
1674
+ if (this.groups.has(normalized)) {
1675
+ return { cmd: this.groups.get(normalized), type: "system" };
1676
+ }
1677
+ const canonicalId = this.resolveToCanonical(normalized);
1678
+ if (canonicalId) {
1679
+ const pluginCmd = this.pluginByCanonical.get(canonicalId);
1680
+ if (pluginCmd && !pluginCmd.shadowed) {
1681
+ const cmd2 = this.byName.get(canonicalId) ?? this.byName.get(normalized);
1682
+ if (cmd2) {
1683
+ return { cmd: cmd2, type: "plugin" };
1684
+ }
1685
+ }
1686
+ }
1573
1687
  const cmd = this.get(nameOrPath);
1574
1688
  if (!cmd) {
1575
1689
  return void 0;
@@ -1577,18 +1691,11 @@ var InMemoryRegistry = class {
1577
1691
  if ("commands" in cmd) {
1578
1692
  return { cmd, type: "system" };
1579
1693
  }
1580
- const normalizedName = typeof nameOrPath === "string" ? nameOrPath : nameOrPath.join(" ");
1581
- if (this.systemCommands.has(normalizedName)) {
1694
+ if (this.systemCommands.has(normalized) || this.systemCommands.has(colonVariant)) {
1582
1695
  return { cmd, type: "system" };
1583
1696
  }
1584
- if (normalizedName.includes(":")) {
1585
- const spaceVersion = normalizedName.replace(":", " ");
1586
- if (this.systemCommands.has(spaceVersion)) {
1587
- return { cmd, type: "system" };
1588
- }
1589
- }
1590
- const manifestCmd = this.getManifestCommand(normalizedName);
1591
- if (manifestCmd) {
1697
+ const manifestCmd = this.getManifestCommand(normalized);
1698
+ if (manifestCmd && !manifestCmd.shadowed) {
1592
1699
  return { cmd, type: "plugin" };
1593
1700
  }
1594
1701
  return { cmd, type: "system" };
@@ -1599,15 +1706,15 @@ var InMemoryRegistry = class {
1599
1706
  return this.byName.get(nameOrPath);
1600
1707
  }
1601
1708
  if (nameOrPath.includes(":")) {
1709
+ const spaceKey = nameOrPath.replace(/:/g, " ");
1710
+ if (this.byName.has(spaceKey)) {
1711
+ return this.byName.get(spaceKey);
1712
+ }
1602
1713
  const parts = nameOrPath.split(":");
1603
1714
  if (parts.length === 2) {
1604
- const exactMatch = this.byName.get(nameOrPath);
1605
- if (exactMatch) {
1606
- return exactMatch;
1607
- }
1608
- const spaceKey = parts.join(" ");
1609
- if (this.byName.has(spaceKey)) {
1610
- return this.byName.get(spaceKey);
1715
+ const spaceKey2 = parts.join(" ");
1716
+ if (this.byName.has(spaceKey2)) {
1717
+ return this.byName.get(spaceKey2);
1611
1718
  }
1612
1719
  }
1613
1720
  }
@@ -1698,7 +1805,11 @@ var InMemoryRegistry = class {
1698
1805
  if (this.manifests.has(idOrAlias)) {
1699
1806
  return this.manifests.get(idOrAlias);
1700
1807
  }
1701
- for (const cmd of this.manifests.values()) {
1808
+ const canonicalId = this.resolveToCanonical(idOrAlias);
1809
+ if (canonicalId) {
1810
+ return this.pluginByCanonical.get(canonicalId);
1811
+ }
1812
+ for (const cmd of this.pluginByCanonical.values()) {
1702
1813
  if (cmd.manifest.aliases?.includes(idOrAlias)) {
1703
1814
  return cmd;
1704
1815
  }