@kb-labs/cli-commands 2.13.0 → 2.15.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
 
@@ -1427,17 +1427,30 @@ function manifestToCommand(registered) {
1427
1427
  }
1428
1428
  };
1429
1429
  }
1430
+ function buildCanonicalId(manifest) {
1431
+ const { id, group, subgroup } = manifest;
1432
+ if (group && subgroup) {
1433
+ return `${group}:${subgroup}:${id}`;
1434
+ }
1435
+ if (group) {
1436
+ return `${group}:${id}`;
1437
+ }
1438
+ return id;
1439
+ }
1430
1440
  var InMemoryRegistry = class {
1431
- // Separate collections for security isolation
1441
+ // System commands (in-process): key = any registered name/alias
1432
1442
  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
1443
+ // Plugin commands: key = canonicalId only
1444
+ pluginByCanonical = /* @__PURE__ */ new Map();
1445
+ // Alias canonicalId: covers all user-input variants that resolve to a plugin
1446
+ pluginAliases = /* @__PURE__ */ new Map();
1447
+ // Legacy unified collection for backward compatibility (display/get)
1437
1448
  byName = /* @__PURE__ */ new Map();
1438
1449
  groups = /* @__PURE__ */ new Map();
1450
+ // manifests: any key → RegisteredCommand (for listing/lookup; still stores multi-key for compat)
1439
1451
  manifests = /* @__PURE__ */ new Map();
1440
1452
  partial = false;
1453
+ // ─── System command registration ────────────────────────────────────────
1441
1454
  register(cmd) {
1442
1455
  this.systemCommands.set(cmd.name, cmd);
1443
1456
  this.byName.set(cmd.name, cmd);
@@ -1476,11 +1489,12 @@ var InMemoryRegistry = class {
1476
1489
  }
1477
1490
  }
1478
1491
  }
1492
+ // ─── Plugin command registration ────────────────────────────────────────
1479
1493
  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.`);
1494
+ const canonicalId = buildCanonicalId(cmd.manifest);
1495
+ const collisionKey = this._findSystemCollision(cmd.manifest, canonicalId);
1496
+ if (collisionKey) {
1497
+ console.warn(`[registry] Plugin command "${canonicalId}" collides with system command "${collisionKey}". System command takes priority.`);
1484
1498
  cmd.shadowed = true;
1485
1499
  }
1486
1500
  const collisionAliases = /* @__PURE__ */ new Set();
@@ -1490,58 +1504,134 @@ var InMemoryRegistry = class {
1490
1504
  collisionAliases.add(alias);
1491
1505
  }
1492
1506
  }
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));
1507
+ this.pluginByCanonical.set(canonicalId, cmd);
1508
+ this.manifests.set(canonicalId, cmd);
1509
+ this.manifests.set(cmd.manifest.id, cmd);
1510
+ if (cmd.shadowed) {
1511
+ return;
1512
+ }
1513
+ this._registerPluginAliases(cmd, canonicalId, collisionAliases);
1514
+ const commandAdapter = manifestToCommand(cmd);
1515
+ this.byName.set(canonicalId, commandAdapter);
1516
+ const spaceCanonical = canonicalId.replace(/:/g, " ");
1517
+ this.byName.set(spaceCanonical, commandAdapter);
1518
+ this._registerSyntheticGroups(cmd, commandAdapter);
1519
+ }
1520
+ /**
1521
+ * Check whether any system command already owns the canonical id or its parts.
1522
+ * Returns the colliding key if found, null otherwise.
1523
+ */
1524
+ _findSystemCollision(manifest, canonicalId) {
1525
+ if (this.systemCommands.has(canonicalId)) return canonicalId;
1526
+ if (this.systemCommands.has(manifest.id)) return manifest.id;
1527
+ const space = canonicalId.replace(/:/g, " ");
1528
+ if (this.systemCommands.has(space)) return space;
1529
+ return null;
1530
+ }
1531
+ /**
1532
+ * Register all lookup aliases for a plugin command.
1533
+ *
1534
+ * Priority (what beats what) is handled in resolveToCanonical at query time.
1535
+ * Here we just build the mapping.
1536
+ *
1537
+ * Aliases registered:
1538
+ * - canonicalId itself ("marketplace:plugins:list")
1539
+ * - bare id ("list") ← low priority, may clash
1540
+ * - 2-part shorthand ("marketplace:list", "marketplace list")
1541
+ * - manifest.aliases[] (user-defined aliases, except collisions)
1542
+ */
1543
+ _registerPluginAliases(cmd, canonicalId, collisionAliases) {
1544
+ const { id, group, subgroup } = cmd.manifest;
1545
+ const register = (key) => {
1546
+ if (!this.systemCommands.has(key) && !this.systemCommands.has(key.replace(/:/g, " "))) {
1547
+ if (!this.pluginAliases.has(key)) {
1548
+ this.pluginAliases.set(key, canonicalId);
1549
+ this.manifests.set(key, cmd);
1524
1550
  }
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
1551
  }
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
- }
1552
+ };
1553
+ this.pluginAliases.set(canonicalId, canonicalId);
1554
+ const spaceCanonical = canonicalId.replace(/:/g, " ");
1555
+ this.pluginAliases.set(spaceCanonical, canonicalId);
1556
+ this.manifests.set(spaceCanonical, cmd);
1557
+ if (group && subgroup) {
1558
+ const twoPartColon = `${group}:${id}`;
1559
+ const twoPartSpace = `${group} ${id}`;
1560
+ register(twoPartColon);
1561
+ register(twoPartSpace);
1562
+ this.manifests.set(twoPartColon, cmd);
1563
+ this.manifests.set(twoPartSpace, cmd);
1564
+ const fullPath = `${group} ${subgroup} ${id}`;
1565
+ this.pluginAliases.set(fullPath, canonicalId);
1566
+ this.manifests.set(fullPath, cmd);
1567
+ const colonPath = `${group}:${subgroup}:${id}`;
1568
+ this.pluginAliases.set(colonPath, canonicalId);
1569
+ this.manifests.set(colonPath, cmd);
1570
+ } else if (group) {
1571
+ const colonName = `${group}:${id}`;
1572
+ const spaceName = `${group} ${id}`;
1573
+ this.pluginAliases.set(colonName, canonicalId);
1574
+ this.pluginAliases.set(spaceName, canonicalId);
1575
+ this.manifests.set(colonName, cmd);
1576
+ this.manifests.set(spaceName, cmd);
1577
+ }
1578
+ register(id);
1579
+ for (const alias of cmd.manifest.aliases || []) {
1580
+ if (!collisionAliases.has(alias)) {
1581
+ register(alias);
1542
1582
  }
1543
1583
  }
1544
1584
  }
1585
+ /**
1586
+ * Register synthetic subgroups for help display.
1587
+ */
1588
+ _registerSyntheticGroups(cmd, commandAdapter) {
1589
+ const { id, group, subgroup } = cmd.manifest;
1590
+ if (group && subgroup) {
1591
+ const subgroupKey = `${group} ${subgroup}`;
1592
+ if (!this.groups.has(subgroupKey)) {
1593
+ this.groups.set(subgroupKey, {
1594
+ name: subgroupKey,
1595
+ describe: subgroup,
1596
+ commands: []
1597
+ });
1598
+ this.byName.set(subgroupKey, this.groups.get(subgroupKey));
1599
+ }
1600
+ this.groups.get(subgroupKey).commands.push(commandAdapter);
1601
+ const twoPartSpace = `${group} ${id}`;
1602
+ if (!this.byName.has(twoPartSpace)) {
1603
+ this.byName.set(twoPartSpace, commandAdapter);
1604
+ }
1605
+ const twoPartColon = `${group}:${id}`;
1606
+ if (!this.byName.has(twoPartColon)) {
1607
+ this.byName.set(twoPartColon, commandAdapter);
1608
+ }
1609
+ const fullPath = `${group} ${subgroup} ${id}`;
1610
+ this.byName.set(fullPath, commandAdapter);
1611
+ } else if (group) {
1612
+ const fullName = `${group} ${id}`;
1613
+ const colonName = `${group}:${id}`;
1614
+ this.byName.set(fullName, commandAdapter);
1615
+ this.byName.set(colonName, commandAdapter);
1616
+ }
1617
+ }
1618
+ // ─── Resolution ──────────────────────────────────────────────────────────
1619
+ /**
1620
+ * Resolve any user input to its canonical plugin ID.
1621
+ *
1622
+ * Returns canonicalId if found in plugin aliases, undefined otherwise.
1623
+ */
1624
+ resolveToCanonical(input) {
1625
+ if (this.pluginAliases.has(input)) {
1626
+ return this.pluginAliases.get(input);
1627
+ }
1628
+ const converted = input.includes(":") ? input.replace(/:/g, " ") : input.replace(/ /g, ":");
1629
+ if (this.pluginAliases.has(converted)) {
1630
+ return this.pluginAliases.get(converted);
1631
+ }
1632
+ return void 0;
1633
+ }
1634
+ // ─── Public API ──────────────────────────────────────────────────────────
1545
1635
  markPartial(partial) {
1546
1636
  this.partial = partial;
1547
1637
  }
@@ -1553,23 +1643,43 @@ var InMemoryRegistry = class {
1553
1643
  }
1554
1644
  listManifests() {
1555
1645
  const unique = /* @__PURE__ */ new Set();
1556
- for (const cmd of this.manifests.values()) {
1646
+ for (const cmd of this.pluginByCanonical.values()) {
1557
1647
  unique.add(cmd);
1558
1648
  }
1559
1649
  return Array.from(unique);
1560
1650
  }
1561
1651
  has(name) {
1562
- return this.byName.has(name);
1652
+ return this.byName.has(name) || this.pluginAliases.has(name);
1563
1653
  }
1564
1654
  /**
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
1655
+ * Get command with type information for secure routing.
1569
1656
  *
1570
- * This separation prevents malicious plugins from escaping the sandbox.
1657
+ * System commands ALWAYS win checked first before any plugin lookup.
1658
+ * Returns type='system' for system commands (in-process execution).
1659
+ * Returns type='plugin' for plugin commands (subprocess execution).
1571
1660
  */
1572
1661
  getWithType(nameOrPath) {
1662
+ const normalized = typeof nameOrPath === "string" ? nameOrPath : nameOrPath.join(" ");
1663
+ if (this.systemCommands.has(normalized)) {
1664
+ return { cmd: this.systemCommands.get(normalized), type: "system" };
1665
+ }
1666
+ const colonVariant = normalized.replace(/ /g, ":");
1667
+ if (this.systemCommands.has(colonVariant)) {
1668
+ return { cmd: this.systemCommands.get(colonVariant), type: "system" };
1669
+ }
1670
+ if (this.groups.has(normalized)) {
1671
+ return { cmd: this.groups.get(normalized), type: "system" };
1672
+ }
1673
+ const canonicalId = this.resolveToCanonical(normalized);
1674
+ if (canonicalId) {
1675
+ const pluginCmd = this.pluginByCanonical.get(canonicalId);
1676
+ if (pluginCmd && !pluginCmd.shadowed) {
1677
+ const cmd2 = this.byName.get(canonicalId) ?? this.byName.get(normalized);
1678
+ if (cmd2) {
1679
+ return { cmd: cmd2, type: "plugin" };
1680
+ }
1681
+ }
1682
+ }
1573
1683
  const cmd = this.get(nameOrPath);
1574
1684
  if (!cmd) {
1575
1685
  return void 0;
@@ -1577,18 +1687,11 @@ var InMemoryRegistry = class {
1577
1687
  if ("commands" in cmd) {
1578
1688
  return { cmd, type: "system" };
1579
1689
  }
1580
- const normalizedName = typeof nameOrPath === "string" ? nameOrPath : nameOrPath.join(" ");
1581
- if (this.systemCommands.has(normalizedName)) {
1690
+ if (this.systemCommands.has(normalized) || this.systemCommands.has(colonVariant)) {
1582
1691
  return { cmd, type: "system" };
1583
1692
  }
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) {
1693
+ const manifestCmd = this.getManifestCommand(normalized);
1694
+ if (manifestCmd && !manifestCmd.shadowed) {
1592
1695
  return { cmd, type: "plugin" };
1593
1696
  }
1594
1697
  return { cmd, type: "system" };
@@ -1599,15 +1702,15 @@ var InMemoryRegistry = class {
1599
1702
  return this.byName.get(nameOrPath);
1600
1703
  }
1601
1704
  if (nameOrPath.includes(":")) {
1705
+ const spaceKey = nameOrPath.replace(/:/g, " ");
1706
+ if (this.byName.has(spaceKey)) {
1707
+ return this.byName.get(spaceKey);
1708
+ }
1602
1709
  const parts = nameOrPath.split(":");
1603
1710
  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);
1711
+ const spaceKey2 = parts.join(" ");
1712
+ if (this.byName.has(spaceKey2)) {
1713
+ return this.byName.get(spaceKey2);
1611
1714
  }
1612
1715
  }
1613
1716
  }
@@ -1698,7 +1801,11 @@ var InMemoryRegistry = class {
1698
1801
  if (this.manifests.has(idOrAlias)) {
1699
1802
  return this.manifests.get(idOrAlias);
1700
1803
  }
1701
- for (const cmd of this.manifests.values()) {
1804
+ const canonicalId = this.resolveToCanonical(idOrAlias);
1805
+ if (canonicalId) {
1806
+ return this.pluginByCanonical.get(canonicalId);
1807
+ }
1808
+ for (const cmd of this.pluginByCanonical.values()) {
1702
1809
  if (cmd.manifest.aliases?.includes(idOrAlias)) {
1703
1810
  return cmd;
1704
1811
  }