@phnx-labs/agents-cli 1.20.4 → 1.20.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 (207) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +49 -18
  3. package/dist/commands/browser.js +31 -4
  4. package/dist/commands/cli.js +1 -1
  5. package/dist/commands/cloud.js +1 -1
  6. package/dist/commands/commands.js +2 -0
  7. package/dist/commands/computer.js +10 -2
  8. package/dist/commands/defaults.d.ts +7 -0
  9. package/dist/commands/defaults.js +89 -0
  10. package/dist/commands/doctor.js +1 -1
  11. package/dist/commands/exec.js +73 -19
  12. package/dist/commands/hooks.js +6 -6
  13. package/dist/commands/inspect.d.ts +26 -0
  14. package/dist/commands/inspect.js +590 -0
  15. package/dist/commands/mcp.js +17 -16
  16. package/dist/commands/models.js +1 -1
  17. package/dist/commands/packages.js +6 -4
  18. package/dist/commands/permissions.js +13 -12
  19. package/dist/commands/plugins.d.ts +13 -0
  20. package/dist/commands/plugins.js +100 -11
  21. package/dist/commands/prune.js +3 -2
  22. package/dist/commands/pull.d.ts +12 -5
  23. package/dist/commands/pull.js +26 -422
  24. package/dist/commands/push.d.ts +14 -0
  25. package/dist/commands/push.js +30 -0
  26. package/dist/commands/repo.d.ts +1 -1
  27. package/dist/commands/repo.js +155 -112
  28. package/dist/commands/resource-view.d.ts +2 -0
  29. package/dist/commands/resource-view.js +12 -3
  30. package/dist/commands/routines.js +32 -7
  31. package/dist/commands/rules.js +4 -4
  32. package/dist/commands/secrets.js +46 -9
  33. package/dist/commands/sessions.js +1 -0
  34. package/dist/commands/setup.d.ts +3 -3
  35. package/dist/commands/setup.js +17 -17
  36. package/dist/commands/skills.js +6 -5
  37. package/dist/commands/subagents.js +5 -4
  38. package/dist/commands/sync.d.ts +18 -5
  39. package/dist/commands/sync.js +251 -65
  40. package/dist/commands/teams.js +109 -11
  41. package/dist/commands/tmux.d.ts +25 -0
  42. package/dist/commands/tmux.js +415 -0
  43. package/dist/commands/trash.d.ts +2 -2
  44. package/dist/commands/trash.js +1 -1
  45. package/dist/commands/versions.js +2 -2
  46. package/dist/commands/view.d.ts +12 -1
  47. package/dist/commands/view.js +128 -40
  48. package/dist/commands/workflows.js +4 -3
  49. package/dist/commands/worktree.d.ts +4 -5
  50. package/dist/commands/worktree.js +4 -4
  51. package/dist/index.js +106 -41
  52. package/dist/lib/agents.d.ts +23 -10
  53. package/dist/lib/agents.js +88 -25
  54. package/dist/lib/auto-pull-worker.d.ts +1 -1
  55. package/dist/lib/auto-pull-worker.js +2 -2
  56. package/dist/lib/auto-pull.d.ts +1 -1
  57. package/dist/lib/auto-pull.js +1 -1
  58. package/dist/lib/beta.d.ts +1 -1
  59. package/dist/lib/beta.js +1 -1
  60. package/dist/lib/browser/chrome.d.ts +10 -0
  61. package/dist/lib/browser/chrome.js +84 -3
  62. package/dist/lib/capabilities.js +2 -0
  63. package/dist/lib/commands.d.ts +28 -1
  64. package/dist/lib/commands.js +125 -20
  65. package/dist/lib/doctor-diff.js +2 -2
  66. package/dist/lib/exec.d.ts +14 -0
  67. package/dist/lib/exec.js +59 -5
  68. package/dist/lib/fuzzy.d.ts +12 -2
  69. package/dist/lib/fuzzy.js +29 -4
  70. package/dist/lib/git.js +8 -1
  71. package/dist/lib/hooks.d.ts +2 -2
  72. package/dist/lib/hooks.js +97 -10
  73. package/dist/lib/mcp.js +32 -2
  74. package/dist/lib/migrate.d.ts +51 -0
  75. package/dist/lib/migrate.js +233 -5
  76. package/dist/lib/models.js +62 -15
  77. package/dist/lib/permissions.d.ts +59 -2
  78. package/dist/lib/permissions.js +299 -7
  79. package/dist/lib/plugin-marketplace.d.ts +98 -40
  80. package/dist/lib/plugin-marketplace.js +196 -93
  81. package/dist/lib/plugins.d.ts +21 -4
  82. package/dist/lib/plugins.js +130 -49
  83. package/dist/lib/profiles-presets.js +12 -12
  84. package/dist/lib/project-launch.d.ts +70 -0
  85. package/dist/lib/project-launch.js +404 -0
  86. package/dist/lib/pty-client.js +1 -1
  87. package/dist/lib/pty-server.d.ts +1 -1
  88. package/dist/lib/pty-server.js +8 -5
  89. package/dist/lib/refresh.d.ts +26 -0
  90. package/dist/lib/refresh.js +315 -0
  91. package/dist/lib/resource-patterns.d.ts +1 -1
  92. package/dist/lib/resource-patterns.js +1 -1
  93. package/dist/lib/resources/commands.js +2 -2
  94. package/dist/lib/resources/hooks.d.ts +1 -1
  95. package/dist/lib/resources/hooks.js +1 -1
  96. package/dist/lib/resources/mcp.d.ts +1 -1
  97. package/dist/lib/resources/mcp.js +5 -6
  98. package/dist/lib/resources/permissions.js +5 -2
  99. package/dist/lib/resources/rules.js +3 -2
  100. package/dist/lib/resources/skills.js +3 -2
  101. package/dist/lib/resources/types.d.ts +1 -1
  102. package/dist/lib/resources.d.ts +2 -0
  103. package/dist/lib/resources.js +4 -3
  104. package/dist/lib/rotate.d.ts +1 -1
  105. package/dist/lib/rotate.js +7 -19
  106. package/dist/lib/routines.d.ts +16 -4
  107. package/dist/lib/routines.js +67 -17
  108. package/dist/lib/rules/compile.js +22 -10
  109. package/dist/lib/rules/rules.js +3 -3
  110. package/dist/lib/run-config.d.ts +9 -0
  111. package/dist/lib/run-config.js +35 -0
  112. package/dist/lib/run-defaults.d.ts +42 -0
  113. package/dist/lib/run-defaults.js +180 -0
  114. package/dist/lib/runner.js +16 -3
  115. package/dist/lib/scheduler.js +15 -1
  116. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  117. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  118. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +9 -1
  119. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  120. package/dist/lib/secrets/install-helper.d.ts +11 -3
  121. package/dist/lib/secrets/install-helper.js +48 -6
  122. package/dist/lib/secrets/linux.d.ts +56 -9
  123. package/dist/lib/secrets/linux.js +327 -59
  124. package/dist/lib/session/db.js +15 -2
  125. package/dist/lib/session/discover.js +118 -3
  126. package/dist/lib/session/parse.js +3 -0
  127. package/dist/lib/session/types.d.ts +1 -1
  128. package/dist/lib/session/types.js +1 -1
  129. package/dist/lib/shims.d.ts +18 -9
  130. package/dist/lib/shims.js +133 -50
  131. package/dist/lib/skills.d.ts +1 -1
  132. package/dist/lib/skills.js +10 -9
  133. package/dist/lib/staleness/detectors/commands.d.ts +3 -0
  134. package/dist/lib/staleness/detectors/commands.js +46 -0
  135. package/dist/lib/staleness/detectors/hooks.d.ts +3 -0
  136. package/dist/lib/staleness/detectors/hooks.js +44 -0
  137. package/dist/lib/staleness/detectors/mcp.d.ts +3 -0
  138. package/dist/lib/staleness/detectors/mcp.js +31 -0
  139. package/dist/lib/staleness/detectors/permissions.d.ts +3 -0
  140. package/dist/lib/staleness/detectors/permissions.js +201 -0
  141. package/dist/lib/staleness/detectors/plugins.d.ts +8 -0
  142. package/dist/lib/staleness/detectors/plugins.js +23 -0
  143. package/dist/lib/staleness/detectors/rules.d.ts +3 -0
  144. package/dist/lib/staleness/detectors/rules.js +34 -0
  145. package/dist/lib/staleness/detectors/skills.d.ts +3 -0
  146. package/dist/lib/staleness/detectors/skills.js +71 -0
  147. package/dist/lib/staleness/detectors/subagents.d.ts +3 -0
  148. package/dist/lib/staleness/detectors/subagents.js +50 -0
  149. package/dist/lib/staleness/detectors/types.d.ts +22 -0
  150. package/dist/lib/staleness/detectors/types.js +1 -0
  151. package/dist/lib/staleness/detectors/workflows.d.ts +3 -0
  152. package/dist/lib/staleness/detectors/workflows.js +28 -0
  153. package/dist/lib/staleness/registry.d.ts +26 -0
  154. package/dist/lib/staleness/registry.js +123 -0
  155. package/dist/lib/staleness/writers/commands.d.ts +3 -0
  156. package/dist/lib/staleness/writers/commands.js +111 -0
  157. package/dist/lib/staleness/writers/hooks.d.ts +3 -0
  158. package/dist/lib/staleness/writers/hooks.js +47 -0
  159. package/dist/lib/staleness/writers/kinds.d.ts +10 -0
  160. package/dist/lib/staleness/writers/kinds.js +15 -0
  161. package/dist/lib/staleness/writers/lazy-map.d.ts +13 -0
  162. package/dist/lib/staleness/writers/lazy-map.js +19 -0
  163. package/dist/lib/staleness/writers/mcp.d.ts +10 -0
  164. package/dist/lib/staleness/writers/mcp.js +19 -0
  165. package/dist/lib/staleness/writers/permissions.d.ts +13 -0
  166. package/dist/lib/staleness/writers/permissions.js +26 -0
  167. package/dist/lib/staleness/writers/plugins.d.ts +7 -0
  168. package/dist/lib/staleness/writers/plugins.js +31 -0
  169. package/dist/lib/staleness/writers/rules.d.ts +7 -0
  170. package/dist/lib/staleness/writers/rules.js +55 -0
  171. package/dist/lib/staleness/writers/skills.d.ts +3 -0
  172. package/dist/lib/staleness/writers/skills.js +81 -0
  173. package/dist/lib/staleness/writers/sources.d.ts +16 -0
  174. package/dist/lib/staleness/writers/sources.js +72 -0
  175. package/dist/lib/staleness/writers/subagents.d.ts +3 -0
  176. package/dist/lib/staleness/writers/subagents.js +53 -0
  177. package/dist/lib/staleness/writers/types.d.ts +36 -0
  178. package/dist/lib/staleness/writers/types.js +1 -0
  179. package/dist/lib/staleness/writers/workflows.d.ts +7 -0
  180. package/dist/lib/staleness/writers/workflows.js +31 -0
  181. package/dist/lib/state.d.ts +34 -11
  182. package/dist/lib/state.js +58 -13
  183. package/dist/lib/subagents.d.ts +0 -2
  184. package/dist/lib/subagents.js +6 -6
  185. package/dist/lib/teams/agents.js +1 -1
  186. package/dist/lib/teams/api.d.ts +67 -0
  187. package/dist/lib/teams/api.js +78 -0
  188. package/dist/lib/teams/parsers.d.ts +1 -1
  189. package/dist/lib/tmux/binary.d.ts +67 -0
  190. package/dist/lib/tmux/binary.js +141 -0
  191. package/dist/lib/tmux/index.d.ts +8 -0
  192. package/dist/lib/tmux/index.js +8 -0
  193. package/dist/lib/tmux/paths.d.ts +17 -0
  194. package/dist/lib/tmux/paths.js +30 -0
  195. package/dist/lib/tmux/session.d.ts +122 -0
  196. package/dist/lib/tmux/session.js +305 -0
  197. package/dist/lib/types.d.ts +73 -13
  198. package/dist/lib/types.js +1 -1
  199. package/dist/lib/usage.js +1 -1
  200. package/dist/lib/versions.d.ts +4 -4
  201. package/dist/lib/versions.js +138 -496
  202. package/dist/lib/workflows.d.ts +2 -4
  203. package/dist/lib/workflows.js +3 -4
  204. package/package.json +6 -3
  205. package/scripts/postinstall.js +16 -63
  206. package/dist/commands/status.d.ts +0 -9
  207. package/dist/commands/status.js +0 -25
@@ -8,11 +8,76 @@ import * as fs from 'fs';
8
8
  import * as path from 'path';
9
9
  import * as os from 'os';
10
10
  import * as yaml from 'yaml';
11
+ import { AGENTS, agentConfigDirName } from './agents.js';
11
12
  const HOME = process.env.HOME ?? os.homedir();
12
- const SYSTEM_DIR = path.join(HOME, '.agents-system');
13
13
  const USER_DIR = path.join(HOME, '.agents');
14
+ /** Canonical system-repo location (post-fold). */
15
+ const SYSTEM_DIR = path.join(USER_DIR, '.system');
16
+ /** Legacy system-repo location — folded into SYSTEM_DIR by foldLegacySystemRepo(). */
17
+ const LEGACY_SYSTEM_DIR = path.join(HOME, '.agents-system');
14
18
  const HISTORY_DIR = path.join(USER_DIR, '.history');
15
19
  const CACHE_DIR = path.join(USER_DIR, '.cache');
20
+ /**
21
+ * Fold ~/.agents-system/ into ~/.agents/.system/.
22
+ *
23
+ * MUST run first in runMigration() — every other migrator reads SYSTEM_DIR
24
+ * (the new path), so the contents have to be there before they execute.
25
+ *
26
+ * Strategy:
27
+ * 1. If legacy dir doesn't exist or is already a symlink, no-op.
28
+ * 2. If new path doesn't exist yet, rename in one shot (fast path).
29
+ * 3. If both exist (mid-migration / re-run on partially-migrated state),
30
+ * merge legacy → new with new winning on collision, then drop legacy.
31
+ *
32
+ * After the contents move, the legacy path becomes a symlink → SYSTEM_DIR
33
+ * so external tooling that still references ~/.agents-system/ keeps
34
+ * resolving correctly. The symlink is harmless on its own and can be
35
+ * removed with `rm ~/.agents-system` once everything has updated.
36
+ *
37
+ * Idempotent: re-running converges to "contents at SYSTEM_DIR, symlink at
38
+ * LEGACY_SYSTEM_DIR" without duplicating data.
39
+ */
40
+ export function foldLegacySystemRepo() {
41
+ let legacyStat = null;
42
+ try {
43
+ legacyStat = fs.lstatSync(LEGACY_SYSTEM_DIR);
44
+ }
45
+ catch { /* missing */ }
46
+ if (!legacyStat)
47
+ return;
48
+ if (legacyStat.isSymbolicLink())
49
+ return;
50
+ if (!legacyStat.isDirectory())
51
+ return;
52
+ try {
53
+ fs.mkdirSync(USER_DIR, { recursive: true, mode: 0o700 });
54
+ }
55
+ catch { /* best-effort */ }
56
+ if (!fs.existsSync(SYSTEM_DIR)) {
57
+ try {
58
+ fs.renameSync(LEGACY_SYSTEM_DIR, SYSTEM_DIR);
59
+ try {
60
+ fs.symlinkSync(SYSTEM_DIR, LEGACY_SYSTEM_DIR);
61
+ }
62
+ catch { /* best-effort */ }
63
+ console.error('Folded ~/.agents-system/ into ~/.agents/.system/ (left back-compat symlink)');
64
+ return;
65
+ }
66
+ catch {
67
+ // Cross-device rename or perm issue — fall through to copy + remove.
68
+ }
69
+ }
70
+ try {
71
+ copyDirSkipExisting(LEGACY_SYSTEM_DIR, SYSTEM_DIR);
72
+ fs.rmSync(LEGACY_SYSTEM_DIR, { recursive: true, force: true });
73
+ try {
74
+ fs.symlinkSync(SYSTEM_DIR, LEGACY_SYSTEM_DIR);
75
+ }
76
+ catch { /* best-effort */ }
77
+ console.error('Merged ~/.agents-system/ into ~/.agents/.system/ (left back-compat symlink)');
78
+ }
79
+ catch { /* best-effort */ }
80
+ }
16
81
  /**
17
82
  * Move ~/.agents-system/agents.yaml -> ~/.agents/agents.yaml.
18
83
  * No-op if user file already exists or system file absent.
@@ -608,12 +673,13 @@ function repairAgentConfigSymlinks() {
608
673
  }
609
674
  let repaired = 0;
610
675
  for (const { agent, version } of defaults) {
611
- const userTarget = fs.existsSync(path.join(HISTORY_DIR, 'versions', agent, version, 'home', `.${agent}`))
612
- ? path.join(HISTORY_DIR, 'versions', agent, version, 'home', `.${agent}`)
613
- : path.join(USER_DIR, 'versions', agent, version, 'home', `.${agent}`);
676
+ const configDirName = agent in AGENTS ? agentConfigDirName(agent) : `.${agent}`;
677
+ const userTarget = fs.existsSync(path.join(HISTORY_DIR, 'versions', agent, version, 'home', configDirName))
678
+ ? path.join(HISTORY_DIR, 'versions', agent, version, 'home', configDirName)
679
+ : path.join(USER_DIR, 'versions', agent, version, 'home', configDirName);
614
680
  if (!fs.existsSync(userTarget))
615
681
  continue;
616
- const symlinkPath = path.join(HOME, `.${agent}`);
682
+ const symlinkPath = path.join(HOME, configDirName);
617
683
  let stat = null;
618
684
  try {
619
685
  stat = fs.lstatSync(symlinkPath);
@@ -1461,8 +1527,166 @@ function migrateVersionResourcesToPatterns() {
1461
1527
  console.error('Migrated agents.yaml versions: entries to pattern format');
1462
1528
  }
1463
1529
  }
1530
+ /**
1531
+ * Rename the legacy `extras-extras/` plugin-marketplace dir to `agents-extras/`
1532
+ * inside every installed agent version-home, and rewrite cross-references in
1533
+ * `known_marketplaces.json` and the agent's `settings.json`.
1534
+ *
1535
+ * A previous dev build of `agents-cli` named the extras-aliased repo's
1536
+ * synthesized marketplace dir `extras-extras` (double "extras" because the alias
1537
+ * itself was `extras`). The new naming convention is `agents-<alias>`, so the
1538
+ * directory should be `agents-extras`. Without this migration the orphan dir
1539
+ * stays on disk and Claude Code loads two parallel marketplaces (the legacy
1540
+ * `extras-extras` entry from `known_marketplaces.json` plus the freshly
1541
+ * synthesized `agents-extras` from the new code path).
1542
+ *
1543
+ * Strategy per `<historyDir>/versions/<agent>/<ver>/home/.<agent>/plugins/`:
1544
+ * 1. `marketplaces/extras-extras/` → `marketplaces/agents-extras/`
1545
+ * (drops `extras-extras/` outright when `agents-extras/` already exists —
1546
+ * previous incomplete migration ran)
1547
+ * 2. Inside the renamed dir's `.claude-plugin/marketplace.json`, set
1548
+ * `"name": "agents-extras"`.
1549
+ * 3. In `<configDir>/plugins/known_marketplaces.json`, rename the
1550
+ * `extras-extras` key to `agents-extras` and rewrite `source.path` /
1551
+ * `installLocation`.
1552
+ * 4. In `<configDir>/settings.json`'s `enabledPlugins`, rename every
1553
+ * `<plugin>@extras-extras` key to `<plugin>@agents-extras` (preserving
1554
+ * its boolean value, skipping if the new key already exists).
1555
+ *
1556
+ * Idempotent: re-running converges without further writes once everything is on
1557
+ * the new name.
1558
+ */
1559
+ export function migrateExtrasExtrasToAgentsExtras(historyDir = HISTORY_DIR) {
1560
+ const versionsRoot = path.join(historyDir, 'versions');
1561
+ if (!fs.existsSync(versionsRoot))
1562
+ return;
1563
+ const OLD = 'extras-extras';
1564
+ const NEW = 'agents-extras';
1565
+ let agentEntries;
1566
+ try {
1567
+ agentEntries = fs.readdirSync(versionsRoot, { withFileTypes: true });
1568
+ }
1569
+ catch {
1570
+ return;
1571
+ }
1572
+ let renamedDirs = 0;
1573
+ let rewroteKnown = 0;
1574
+ let rewroteSettings = 0;
1575
+ for (const agentEntry of agentEntries) {
1576
+ if (!agentEntry.isDirectory())
1577
+ continue;
1578
+ const agentId = agentEntry.name;
1579
+ const agentVersionsDir = path.join(versionsRoot, agentId);
1580
+ let verEntries;
1581
+ try {
1582
+ verEntries = fs.readdirSync(agentVersionsDir, { withFileTypes: true });
1583
+ }
1584
+ catch {
1585
+ continue;
1586
+ }
1587
+ for (const ver of verEntries) {
1588
+ if (!ver.isDirectory())
1589
+ continue;
1590
+ const configDir = path.join(agentVersionsDir, ver.name, 'home', `.${agentId}`);
1591
+ const pluginsDir = path.join(configDir, 'plugins');
1592
+ if (!fs.existsSync(pluginsDir))
1593
+ continue;
1594
+ const marketplacesDir = path.join(pluginsDir, 'marketplaces');
1595
+ const oldDir = path.join(marketplacesDir, OLD);
1596
+ const newDir = path.join(marketplacesDir, NEW);
1597
+ const oldExists = fs.existsSync(oldDir);
1598
+ const newExists = fs.existsSync(newDir);
1599
+ if (oldExists && !newExists) {
1600
+ try {
1601
+ fs.renameSync(oldDir, newDir);
1602
+ renamedDirs++;
1603
+ }
1604
+ catch { /* best-effort, leave orphan */ }
1605
+ }
1606
+ else if (oldExists && newExists) {
1607
+ // Previous incomplete migration — drop the stale old dir.
1608
+ try {
1609
+ fs.rmSync(oldDir, { recursive: true, force: true });
1610
+ }
1611
+ catch { /* best-effort */ }
1612
+ }
1613
+ // Rewrite marketplace.json name field inside the (now) agents-extras dir.
1614
+ const marketplaceJson = path.join(newDir, '.claude-plugin', 'marketplace.json');
1615
+ if (fs.existsSync(marketplaceJson)) {
1616
+ try {
1617
+ const raw = fs.readFileSync(marketplaceJson, 'utf-8');
1618
+ const parsed = JSON.parse(raw);
1619
+ if (parsed?.name === OLD) {
1620
+ parsed.name = NEW;
1621
+ fs.writeFileSync(marketplaceJson, JSON.stringify(parsed, null, 2) + '\n', 'utf-8');
1622
+ }
1623
+ }
1624
+ catch { /* best-effort */ }
1625
+ }
1626
+ // Rewrite known_marketplaces.json key + path fields.
1627
+ const knownFile = path.join(pluginsDir, 'known_marketplaces.json');
1628
+ if (fs.existsSync(knownFile)) {
1629
+ try {
1630
+ const raw = fs.readFileSync(knownFile, 'utf-8');
1631
+ const known = JSON.parse(raw);
1632
+ if (known && typeof known === 'object' && OLD in known) {
1633
+ const entry = known[OLD];
1634
+ if (!(NEW in known)) {
1635
+ if (entry?.source?.path)
1636
+ entry.source.path = entry.source.path.split(OLD).join(NEW);
1637
+ if (entry?.installLocation)
1638
+ entry.installLocation = entry.installLocation.split(OLD).join(NEW);
1639
+ known[NEW] = entry;
1640
+ }
1641
+ delete known[OLD];
1642
+ fs.writeFileSync(knownFile, JSON.stringify(known, null, 2) + '\n', 'utf-8');
1643
+ rewroteKnown++;
1644
+ }
1645
+ }
1646
+ catch { /* best-effort */ }
1647
+ }
1648
+ // Rewrite settings.json enabledPlugins keys.
1649
+ const settingsFile = path.join(configDir, 'settings.json');
1650
+ if (fs.existsSync(settingsFile)) {
1651
+ try {
1652
+ const raw = fs.readFileSync(settingsFile, 'utf-8');
1653
+ const settings = JSON.parse(raw);
1654
+ const enabled = settings?.enabledPlugins;
1655
+ if (enabled && typeof enabled === 'object') {
1656
+ const suffix = `@${OLD}`;
1657
+ const newSuffix = `@${NEW}`;
1658
+ let changed = false;
1659
+ for (const key of Object.keys(enabled)) {
1660
+ if (!key.endsWith(suffix))
1661
+ continue;
1662
+ const renamed = key.slice(0, -suffix.length) + newSuffix;
1663
+ if (renamed in enabled) {
1664
+ delete enabled[key];
1665
+ }
1666
+ else {
1667
+ enabled[renamed] = enabled[key];
1668
+ delete enabled[key];
1669
+ }
1670
+ changed = true;
1671
+ }
1672
+ if (changed) {
1673
+ fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
1674
+ rewroteSettings++;
1675
+ }
1676
+ }
1677
+ }
1678
+ catch { /* best-effort */ }
1679
+ }
1680
+ }
1681
+ }
1682
+ if (renamedDirs > 0 || rewroteKnown > 0 || rewroteSettings > 0) {
1683
+ console.error(`Renamed extras-extras → agents-extras (dirs: ${renamedDirs}, known_marketplaces: ${rewroteKnown}, settings: ${rewroteSettings})`);
1684
+ }
1685
+ }
1464
1686
  /** Run all idempotent migrations. Safe to call multiple times. */
1465
1687
  export async function runMigration() {
1688
+ // MUST run first: every other migrator reads SYSTEM_DIR (the new path).
1689
+ foldLegacySystemRepo();
1466
1690
  migrateAgentsYaml();
1467
1691
  deleteSystemPromptsJson();
1468
1692
  migrateSystemConfigJson();
@@ -1503,6 +1727,10 @@ export async function runMigration() {
1503
1727
  await migrateSystemCloudToCache();
1504
1728
  dropDeadSystemArtifacts();
1505
1729
  warnSystemOrphans();
1730
+ // Rename the legacy extras-extras marketplace dir to agents-extras across every
1731
+ // installed version-home. Runs after migrateRuntimeToHistory so the version
1732
+ // homes are at their canonical HISTORY_DIR location.
1733
+ migrateExtrasExtrasToAgentsExtras();
1506
1734
  // Symlink repair runs LAST so it can find the post-move version homes.
1507
1735
  repairAgentConfigSymlinks();
1508
1736
  }
@@ -78,27 +78,72 @@ export function locateModelSource(agent, version) {
78
78
  return null;
79
79
  }
80
80
  if (agent === 'codex') {
81
+ // Codex's vendored binary has moved across releases:
82
+ // <=0.98: @openai/codex/vendor/<triple>/codex/codex
83
+ // 0.99..0.13: @openai/codex-<plat>-<arch>/vendor/<triple>/codex/codex
84
+ // 0.134+: @openai/codex-<plat>-<arch>/vendor/<triple>/bin/codex
85
+ // We probe all known shapes; first hit wins.
86
+ const triples = ['aarch64-apple-darwin', 'x86_64-apple-darwin', 'x86_64-unknown-linux-musl', 'aarch64-unknown-linux-musl', 'x86_64-pc-windows-msvc', 'aarch64-pc-windows-msvc'];
81
87
  const triple = currentTargetTriple();
82
- if (triple) {
83
- const platformPkg = path.join(versionDir, 'node_modules', '@openai', `codex-${triple.includes('apple') ? 'darwin' : triple.includes('linux') ? 'linux' : 'win32'}-${triple.includes('aarch64') ? 'arm64' : 'x64'}`, 'vendor', triple, 'codex', 'codex');
84
- if (fs.existsSync(platformPkg))
85
- return { path: platformPkg, kind: 'binary' };
86
- }
87
- // 0.98 layout: binary inside @openai/codex itself
88
- const triples = ['aarch64-apple-darwin', 'x86_64-apple-darwin', 'x86_64-unknown-linux-musl', 'aarch64-unknown-linux-musl'];
89
- for (const t of triples) {
90
- const p = path.join(versionDir, 'node_modules', '@openai', 'codex', 'vendor', t, 'codex', 'codex');
91
- if (fs.existsSync(p))
92
- return { path: p, kind: 'binary' };
88
+ const orderedTriples = triple ? [triple, ...triples.filter((t) => t !== triple)] : triples;
89
+ const platformPkgFor = (t) => `codex-${t.includes('apple') ? 'darwin' : t.includes('linux') ? 'linux' : 'win32'}-${t.includes('aarch64') ? 'arm64' : 'x64'}`;
90
+ for (const t of orderedTriples) {
91
+ const candidates = [
92
+ path.join(versionDir, 'node_modules', '@openai', platformPkgFor(t), 'vendor', t, 'bin', 'codex'),
93
+ path.join(versionDir, 'node_modules', '@openai', platformPkgFor(t), 'vendor', t, 'codex', 'codex'),
94
+ path.join(versionDir, 'node_modules', '@openai', 'codex', 'vendor', t, 'bin', 'codex'),
95
+ path.join(versionDir, 'node_modules', '@openai', 'codex', 'vendor', t, 'codex', 'codex'),
96
+ ];
97
+ for (const p of candidates) {
98
+ if (fs.existsSync(p))
99
+ return { path: p, kind: 'binary' };
100
+ }
93
101
  }
94
102
  return null;
95
103
  }
96
104
  if (agent === 'gemini') {
97
- // Gemini ships a clean ES module with all constants and aliases -- no need
98
- // to parse the minified CLI bundle.
105
+ // <=0.41 shipped a clean ES module at @google/gemini-cli-core/dist/src/config/models.js.
106
+ // 0.42+ inlines the same constants into a minified chunk under @google/gemini-cli/bundle/.
99
107
  const modelsJs = path.join(versionDir, 'node_modules', '@google', 'gemini-cli-core', 'dist', 'src', 'config', 'models.js');
100
108
  if (fs.existsSync(modelsJs))
101
109
  return { path: modelsJs, kind: 'js' };
110
+ const bundleDir = path.join(versionDir, 'node_modules', '@google', 'gemini-cli', 'bundle');
111
+ if (fs.existsSync(bundleDir)) {
112
+ try {
113
+ const entries = fs.readdirSync(bundleDir);
114
+ // The model constants live in a single chunk; pick the first .js file
115
+ // whose contents contain VALID_GEMINI_MODELS. Skip subdirectories.
116
+ for (const name of entries) {
117
+ if (!name.endsWith('.js'))
118
+ continue;
119
+ const full = path.join(bundleDir, name);
120
+ let stat;
121
+ try {
122
+ stat = fs.statSync(full);
123
+ }
124
+ catch {
125
+ continue;
126
+ }
127
+ if (!stat.isFile())
128
+ continue;
129
+ // Most chunks just re-export the constants; only the defining chunk
130
+ // actually has `var VALID_GEMINI_MODELS = ... new Set(`. Match that
131
+ // shape so we pick the chunk that the extractor can parse.
132
+ let body;
133
+ try {
134
+ body = fs.readFileSync(full, 'utf-8');
135
+ }
136
+ catch {
137
+ continue;
138
+ }
139
+ if (/var\s+VALID_GEMINI_MODELS\s*=[^\n]*new\s+Set/.test(body) &&
140
+ /var\s+DEFAULT_GEMINI_MODEL\s*=/.test(body)) {
141
+ return { path: full, kind: 'js' };
142
+ }
143
+ }
144
+ }
145
+ catch { /* unreadable bundle dir */ }
146
+ }
102
147
  return null;
103
148
  }
104
149
  if (agent === 'opencode') {
@@ -296,7 +341,9 @@ function extractCodexCatalog(text) {
296
341
  * would pollute the runtime and ES-module interop is awkward from a CJS build).
297
342
  */
298
343
  function extractGeminiCatalog(text) {
299
- const constRe = /export\s+const\s+([A-Z0-9_]+)\s*=\s*'([^']+)'/g;
344
+ // Old (<=0.41) layout used `export const FOO = 'bar';` in models.js.
345
+ // New (0.42+) bundle inlines the same constants as `var FOO = "bar";`.
346
+ const constRe = /(?:export\s+const|var)\s+([A-Z0-9_]+)\s*=\s*['"]([^'"]+)['"]/g;
300
347
  const constants = new Map();
301
348
  let m;
302
349
  while ((m = constRe.exec(text)) !== null) {
@@ -305,7 +352,7 @@ function extractGeminiCatalog(text) {
305
352
  // The set of ids the CLI accepts as "valid model names". Names (not values)
306
353
  // are listed inside `new Set([...])`, so we expand them via the constants map.
307
354
  const validIds = new Set();
308
- const setBlock = text.match(/VALID_GEMINI_MODELS\s*=\s*new\s+Set\(\[([\s\S]*?)\]\)/);
355
+ const setBlock = text.match(/VALID_GEMINI_MODELS\s*=\s*(?:\/\*[^*]*\*\/\s*)?new\s+Set\(\[([\s\S]*?)\]\)/);
309
356
  if (setBlock) {
310
357
  const nameRe = /([A-Z0-9_]+)/g;
311
358
  let nm;
@@ -1,6 +1,4 @@
1
1
  import type { AgentId, PermissionSet, InstalledPermission, ClaudePermissions, OpenCodePermissions, CodexPermissions } from './types.js';
2
- /** Agents that support the permissions subsystem. */
3
- export declare const PERMISSIONS_CAPABLE_AGENTS: AgentId[];
4
2
  /** Filename used for Codex Starlark deny-rules generated from permission groups. */
5
3
  export declare const CODEX_RULES_FILENAME = "agents-deny.rules";
6
4
  export type ParsedRules = PermissionSet;
@@ -110,6 +108,65 @@ export declare function convertToGeminiFormat(set: PermissionSet): {
110
108
  allowed: string[];
111
109
  };
112
110
  };
111
+ /**
112
+ * Convert canonical permission set to Antigravity format.
113
+ * Antigravity reads ~/.gemini/antigravity-cli/settings.json with
114
+ * { permissions: { allow: [...], deny: [...] } }
115
+ * where each entry is action-namespaced: command(...), read_file(...),
116
+ * write_file(...), read_url(...), mcp(...).
117
+ * Bash maps to `command`, Read to `read_file`, Write to `write_file`,
118
+ * WebFetch to `read_url`. Other canonical tools are skipped.
119
+ * Note: Antigravity matches `command(npm install)` as an exact string,
120
+ * not a prefix — `Bash(npm:*)` becomes `command(npm *)` which is a glob
121
+ * but Antigravity upstream has a known exact-match bug for some forms.
122
+ */
123
+ export declare function convertToAntigravityFormat(set: PermissionSet): {
124
+ permissions: {
125
+ allow: string[];
126
+ deny?: string[];
127
+ };
128
+ };
129
+ /**
130
+ * Convert canonical permission set to Grok format.
131
+ * Grok reads ~/.grok/config.toml with
132
+ * [permission]
133
+ * rules = [ { action = "allow", tool = "bash", pattern = "git *" }, ... ]
134
+ * Tool names are lowercase: bash, read, edit, grep, mcptool, webfetch.
135
+ * Canonical Write maps to Grok's `edit` tool.
136
+ */
137
+ export declare function convertToGrokFormat(set: PermissionSet): {
138
+ permission: {
139
+ rules: GrokRule[];
140
+ };
141
+ };
142
+ export type GrokRule = {
143
+ action: 'allow' | 'deny';
144
+ tool: string;
145
+ pattern?: string;
146
+ };
147
+ export type KimiRule = {
148
+ decision: 'allow' | 'deny';
149
+ pattern: string;
150
+ };
151
+ /**
152
+ * Convert a canonical permission set to Kimi Code's `[permission].rules` format.
153
+ * Kimi (`~/.kimi-code/config.toml`) reads rules of the form
154
+ * [[permission.rules]]
155
+ * decision = "allow"
156
+ * pattern = "Bash(git status*)"
157
+ * Tool names are capitalized and the Bash arg-glob uses a trailing `*` (no
158
+ * Claude `:*` separator). Without this conversion the canonical strings match
159
+ * nothing in Kimi's engine and every tool call falls through to a prompt.
160
+ *
161
+ * Each `:*` Bash rule expands to TWO patterns (`cmd*` and `cmd*​/**`) so the
162
+ * command auto-approves whether or not its arguments contain a slash — see
163
+ * `kimiBashPatterns` for why Kimi's picomatch matcher needs both.
164
+ */
165
+ export declare function convertToKimiFormat(set: PermissionSet): {
166
+ permission: {
167
+ rules: KimiRule[];
168
+ };
169
+ };
113
170
  /**
114
171
  * Convert canonical permission set to OpenCode format.
115
172
  * OpenCode uses: { permission: { bash: { "git *": "allow", "rm *": "deny" } } }