@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
package/dist/index.js CHANGED
@@ -56,11 +56,12 @@ if (IS_DEV_BUILD) {
56
56
  }
57
57
  // Import command registrations
58
58
  import { registerPullCommand } from './commands/pull.js';
59
+ import { registerPushCommand } from './commands/push.js';
59
60
  import { registerRepoCommands } from './commands/repo.js';
60
61
  import { registerSetupCommand, runSetup } from './commands/setup.js';
61
- import { registerStatusCommand } from './commands/status.js';
62
62
  import { registerFeedbackCommand } from './commands/feedback.js';
63
63
  import { registerViewCommand } from './commands/view.js';
64
+ import { registerInspectCommand } from './commands/inspect.js';
64
65
  import { registerCommandsCommands } from './commands/commands.js';
65
66
  import { registerHooksCommands } from './commands/hooks.js';
66
67
  import { registerSkillsCommands } from './commands/skills.js';
@@ -75,6 +76,7 @@ import { registerDaemonCommands } from './commands/daemon.js';
75
76
  import { registerRoutinesCommands } from './commands/routines.js';
76
77
  import { registerRunCommand } from './commands/exec.js';
77
78
  import { registerModelsCommand } from './commands/models.js';
79
+ import { registerDefaultsCommands } from './commands/defaults.js';
78
80
  import { registerPruneCommand } from './commands/prune.js';
79
81
  import { registerTrashCommands } from './commands/trash.js';
80
82
  import { registerDoctorCommand } from './commands/doctor.js';
@@ -86,6 +88,7 @@ import { registerSyncCommand } from './commands/sync.js';
86
88
  import { registerRefreshRulesCommand } from './commands/refresh-rules.js';
87
89
  import { registerDriveCommands } from './commands/drive.js';
88
90
  import { registerPtyCommands } from './commands/pty.js';
91
+ import { registerTmuxCommands } from './commands/tmux.js';
89
92
  import { registerBrowserCommand } from './commands/browser.js';
90
93
  import { registerComputerCommand } from './commands/computer.js';
91
94
  import { registerProfilesCommands } from './commands/profiles.js';
@@ -99,7 +102,7 @@ import { applyGlobalHelpConventions } from './lib/help.js';
99
102
  import { isInteractiveTerminal, isPromptCancelled } from './commands/utils.js';
100
103
  import { AGENTS } from './lib/agents.js';
101
104
  import { getGlobalDefault, listInstalledVersions } from './lib/versions.js';
102
- import { addShimsToPath, ensureShimCurrent, ensureVersionedAliasCurrent, getPathShadowingExecutable, getPathSetupInstructions, hasAliasShadowingShim, isShimsInPath, listAgentsWithInstalledVersions, removeLegacyUserShim, } from './lib/shims.js';
105
+ import { addShimsToPath, ensureShimCurrent, ensureVersionedAliasCurrent, getPathShadowingExecutable, getPathSetupInstructions, getShimsDir, isShimsInPath, listAgentsWithInstalledVersions, removeLegacyUserShim, } from './lib/shims.js';
103
106
  const program = new Command();
104
107
  program
105
108
  .name('agents')
@@ -131,6 +134,7 @@ Agent versions:
131
134
  prune cleanup [target] Remove orphan resources and older duplicate version installs
132
135
  trash Inspect and restore soft-deleted version directories
133
136
  view [agent[@version]] List versions, or inspect one in detail
137
+ inspect <agent>[@version] Deep details for one agent+version — paths, capabilities, resources, drill into any kind
134
138
 
135
139
  Agent configuration (synced across versions):
136
140
  rules Instructions given to agents (CLAUDE.md, etc.)
@@ -148,6 +152,7 @@ Packages:
148
152
 
149
153
  Run and dispatch:
150
154
  run <agent|profile> [prompt] Run an agent. Omit prompt for interactive mode.
155
+ defaults Configure run defaults by agent/version selector
151
156
  teams Coordinate multiple agents on shared work
152
157
  routines Run agents on a cron schedule (scheduler auto-starts)
153
158
  sessions Browse, search, and replay past runs (live-search in TTY; grouped by workspace)
@@ -164,7 +169,7 @@ Diagnostics:
164
169
 
165
170
  Config sync:
166
171
  drive Sync session history across machines via rsync
167
- pull Clone or pull the system repo at ~/.agents-system/
172
+ pull Clone or pull the system repo at ~/.agents/.system/
168
173
  repo init --path <dir> Scaffold your own editable repo from a template
169
174
  repo add <path|gh:user/repo> Merge an extra repo after the system repo
170
175
 
@@ -182,7 +187,7 @@ Options:
182
187
  -V, --version Show version number
183
188
  -h, --help Show help
184
189
 
185
- System config lives in ~/.agents-system/. Run 'agents <command> --help' for details.
190
+ System config lives in ~/.agents/.system/. Run 'agents <command> --help' for details.
186
191
  `;
187
192
  }
188
193
  return originalHelpInformation();
@@ -214,9 +219,12 @@ async function showWhatsNew(fromVersion, toVersion) {
214
219
  const versionMatch = line.match(/^## (\d+\.\d+\.\d+)/);
215
220
  if (versionMatch) {
216
221
  currentVersion = versionMatch[1];
217
- const isNewer = currentVersion !== fromVersion &&
218
- compareVersions(currentVersion, fromVersion) > 0;
219
- inRelevantSection = isNewer;
222
+ // Only the range the user actually moved through: (fromVersion, toVersion].
223
+ // Bounding the top end matters when upgrading to a specific older
224
+ // version, and guards against a changelog that lists unreleased entries.
225
+ const inRange = compareVersions(currentVersion, fromVersion) > 0 &&
226
+ compareVersions(currentVersion, toVersion) <= 0;
227
+ inRelevantSection = inRange;
220
228
  if (inRelevantSection) {
221
229
  relevantChanges.push('');
222
230
  relevantChanges.push(chalk.bold(`v${currentVersion}`));
@@ -276,11 +284,14 @@ function saveUpdateCheck(latestVersion) {
276
284
  }
277
285
  }
278
286
  /** Fetch the exact latest npm version plus its registry integrity hash. */
279
- async function fetchLatestNpmPackageMetadata(timeoutMs = 5000) {
280
- const response = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE_NAME}/latest`, {
287
+ async function fetchNpmPackageMetadata(versionOrTag = 'latest', timeoutMs = 5000) {
288
+ const response = await fetch(`https://registry.npmjs.org/${NPM_PACKAGE_NAME}/${versionOrTag}`, {
281
289
  signal: AbortSignal.timeout(timeoutMs),
282
290
  });
283
291
  if (!response.ok) {
292
+ if (response.status === 404) {
293
+ throw new Error(`${NPM_PACKAGE_NAME}@${versionOrTag} not found on npm`);
294
+ }
284
295
  throw new Error('Could not reach npm registry');
285
296
  }
286
297
  const data = await response.json();
@@ -335,7 +346,7 @@ async function promptUpgrade(latestVersion) {
335
346
  const { spawnSync } = await import('child_process');
336
347
  let spinner = ora('Resolving package metadata...').start();
337
348
  try {
338
- const metadata = await fetchLatestNpmPackageMetadata();
349
+ const metadata = await fetchNpmPackageMetadata();
339
350
  spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${metadata.version}`);
340
351
  printResolvedPackage(metadata);
341
352
  const approved = await confirm({
@@ -409,10 +420,17 @@ async function checkForUpdates() {
409
420
  }
410
421
  }
411
422
  }
412
- async function maybeBootstrapShimIntegration(requestedCommand) {
423
+ async function maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequested) {
413
424
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
414
425
  return;
415
426
  }
427
+ // Pure documentation paths must never trigger interactive repair — mirrors
428
+ // the helpOrVersionRequested gate around ensureInitialized below. Covers
429
+ // both bare `agents --version` (requestedCommand === undefined) and
430
+ // `agents <subcommand> --help` (requestedCommand === subcommand name).
431
+ if (helpOrVersionRequested) {
432
+ return;
433
+ }
416
434
  if (requestedCommand === 'sync' || requestedCommand === 'refresh-rules') {
417
435
  return;
418
436
  }
@@ -447,11 +465,13 @@ async function maybeBootstrapShimIntegration(requestedCommand) {
447
465
  const shadowed = defaultAgents
448
466
  .map((agent) => ({ agent, shadowedBy: getPathShadowingExecutable(agent) }))
449
467
  .filter((item) => Boolean(item.shadowedBy));
450
- // Also check for shell aliases that shadow the shim
451
- const aliased = defaultAgents.filter((agent) => hasAliasShadowingShim(agent));
452
- // If shims are in PATH and nothing is binary-shadowing, we're done.
453
468
  // Shell aliases that call the same command with extra flags are intentional
454
- // customization and don't break shim integration.
469
+ // customization and don't break shim integration — `addShimsToPath` cannot
470
+ // touch them, so they don't belong in the repair prompt. We previously
471
+ // computed an `aliased` list here and inserted it into `affected`, which
472
+ // contradicted the comment below and surfaced false positives (e.g. an
473
+ // earlier `alias codex=...` cancelled by a later `unalias codex` was
474
+ // reported because the detector did a static rc-file regex).
455
475
  if (shadowed.length === 0 && isShimsInPath()) {
456
476
  return;
457
477
  }
@@ -468,14 +488,11 @@ async function maybeBootstrapShimIntegration(requestedCommand) {
468
488
  for (const { agent, shadowedBy } of shadowed) {
469
489
  affected.push(`${AGENTS[agent].cliCommand} -> ${shadowedBy}`);
470
490
  }
471
- for (const agent of aliased) {
472
- if (!shadowed.some((s) => s.agent === agent)) {
473
- affected.push(`${AGENTS[agent].cliCommand} (alias)`);
474
- }
475
- }
476
491
  if (affected.length === 0) {
477
- // PATH issue - show all installed agents
478
- affected.push(...installedAgents.map((agent) => AGENTS[agent].cliCommand));
492
+ // Pure PATH-not-loaded case: rc may already have the shim block, but the
493
+ // running shell hasn't sourced it. Don't list agents here — they aren't
494
+ // broken; only the PATH is stale. The prompt + post-message handle it.
495
+ affected.push('PATH entry missing');
479
496
  }
480
497
  const shouldRepair = await confirm({
481
498
  message: `Repair shim integration now? ${affected.join(', ')}`,
@@ -494,15 +511,38 @@ async function maybeBootstrapShimIntegration(requestedCommand) {
494
511
  if (!pathResult.success) {
495
512
  console.log(chalk.yellow('Could not repair shim PATH setup automatically.'));
496
513
  console.log(chalk.gray(pathResult.error || getPathSetupInstructions()));
514
+ // Write the sentinel even on failure — otherwise an unwritable rc file
515
+ // re-prompts every invocation in the same shell. The user opens a new
516
+ // terminal (new PPID) to retry.
517
+ try {
518
+ fs.writeFileSync(sentinelPath, '1');
519
+ }
520
+ catch { /* best-effort */ }
497
521
  return;
498
522
  }
523
+ // When the rc file already has the canonical shim block, `addShimsToPath`
524
+ // is a no-op — re-emitting produced byte-identical content. In this branch
525
+ // the user clicked "Yes" but nothing changed on disk, AND the underlying
526
+ // cause (a real binary shadow, or a stale shell PATH) is unaffected by
527
+ // this command. Be honest about it and point at the actual action.
499
528
  if (pathResult.alreadyPresent) {
500
- console.log(chalk.yellow('Shim PATH entry is already in your shell config, but this shell has not reloaded it yet.'));
529
+ if (shadowed.length > 0) {
530
+ const targets = shadowed
531
+ .map(({ agent, shadowedBy }) => ` ${AGENTS[agent].cliCommand}: ${shadowedBy}`)
532
+ .join('\n');
533
+ console.log(chalk.yellow('Repair could not change anything — the shim is shadowed by another binary on PATH:'));
534
+ console.log(chalk.gray(targets));
535
+ console.log(chalk.gray(`Fix it by removing or reordering that binary, or making sure ${getShimsDir()} appears earlier in PATH than its parent dir.`));
536
+ }
537
+ else {
538
+ console.log(chalk.yellow(`Shim PATH entry is already in ~/${pathResult.rcFile} — this shell just needs to reload it.`));
539
+ console.log(chalk.gray(`Run: source ~/${pathResult.rcFile} (or open a new terminal)`));
540
+ }
501
541
  }
502
542
  else {
503
543
  console.log(chalk.green(`Repaired shim PATH setup in ~/${pathResult.rcFile}`));
544
+ console.log(chalk.gray(getPathSetupInstructions()));
504
545
  }
505
- console.log(chalk.gray(getPathSetupInstructions()));
506
546
  try {
507
547
  fs.writeFileSync(sentinelPath, '1');
508
548
  }
@@ -510,7 +550,7 @@ async function maybeBootstrapShimIntegration(requestedCommand) {
510
550
  }
511
551
  // Register all commands
512
552
  registerViewCommand(program);
513
- registerStatusCommand(program);
553
+ registerInspectCommand(program);
514
554
  registerFeedbackCommand(program);
515
555
  registerCommandsCommands(program);
516
556
  registerHooksCommands(program);
@@ -551,6 +591,7 @@ registerPackagesCommands(program);
551
591
  registerDaemonCommands(program);
552
592
  registerRoutinesCommands(program);
553
593
  registerRunCommand(program);
594
+ registerDefaultsCommands(program);
554
595
  registerModelsCommand(program);
555
596
  registerPruneCommand(program);
556
597
  registerTrashCommands(program);
@@ -577,6 +618,7 @@ registerFactoryCommands(program);
577
618
  registerUsageCommand(program);
578
619
  registerAliasCommand(program);
579
620
  registerPtyCommands(program);
621
+ registerTmuxCommands(program);
580
622
  registerBrowserCommand(program);
581
623
  registerComputerCommand(program);
582
624
  // Deprecated 'jobs' and 'cron' aliases for 'routines'
@@ -594,26 +636,31 @@ for (const alias of ['jobs', 'cron']) {
594
636
  }
595
637
  program
596
638
  .command('upgrade')
597
- .description('Upgrade agents-cli to the latest version')
639
+ .description('Upgrade agents-cli to the latest version (or a specific [version])')
640
+ .argument('[version]', 'Target version or dist-tag to install (default: latest)')
598
641
  .option('-y, --yes', 'Install without an interactive confirmation prompt')
599
- .action(async (options) => {
600
- let spinner = ora('Checking for updates...').start();
642
+ .action(async (version, options) => {
643
+ const target = version ?? 'latest';
644
+ let spinner = ora(version ? `Resolving ${NPM_PACKAGE_NAME}@${target}...` : 'Checking for updates...').start();
601
645
  try {
602
- const metadata = await fetchLatestNpmPackageMetadata();
603
- const latestVersion = metadata.version;
604
- if (latestVersion === VERSION) {
605
- spinner.succeed(`Already on latest version (${VERSION})`);
646
+ const metadata = await fetchNpmPackageMetadata(target);
647
+ const resolvedVersion = metadata.version;
648
+ if (resolvedVersion === VERSION) {
649
+ spinner.succeed(`Already on ${VERSION}`);
606
650
  return;
607
651
  }
608
- if (compareVersions(latestVersion, VERSION) <= 0) {
609
- spinner.succeed(`Already ahead of latest (${VERSION} >= ${latestVersion})`);
652
+ // For `latest` (no explicit version) skip when already ahead. When a
653
+ // version is named explicitly, honor it even if it's a downgrade.
654
+ if (!version && compareVersions(resolvedVersion, VERSION) <= 0) {
655
+ spinner.succeed(`Already ahead of latest (${VERSION} >= ${resolvedVersion})`);
610
656
  return;
611
657
  }
612
- spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${latestVersion}`);
658
+ const direction = compareVersions(resolvedVersion, VERSION) < 0 ? 'Downgrade' : 'Upgrade';
659
+ spinner.succeed(`Resolved ${NPM_PACKAGE_NAME}@${resolvedVersion}`);
613
660
  printResolvedPackage(metadata);
614
661
  if (isInteractiveTerminal() && !options.yes) {
615
662
  const approved = await confirm({
616
- message: `Install ${NPM_PACKAGE_NAME}@${latestVersion}?`,
663
+ message: `Install ${NPM_PACKAGE_NAME}@${resolvedVersion}?`,
617
664
  default: false,
618
665
  });
619
666
  if (!approved) {
@@ -621,17 +668,21 @@ program
621
668
  return;
622
669
  }
623
670
  }
624
- spinner = ora(`Upgrading ${VERSION} -> ${latestVersion}...`).start();
671
+ spinner = ora(`${direction === 'Downgrade' ? 'Downgrading' : 'Upgrading'} ${VERSION} -> ${resolvedVersion}...`).start();
625
672
  await installResolvedPackage(metadata);
626
- spinner.succeed(`Upgraded to ${latestVersion}`);
627
- await showWhatsNew(VERSION, latestVersion);
673
+ spinner.succeed(`${direction}d to ${resolvedVersion}`);
674
+ // Only show the changelog for a genuine upgrade range.
675
+ if (compareVersions(resolvedVersion, VERSION) > 0) {
676
+ await showWhatsNew(VERSION, resolvedVersion);
677
+ }
628
678
  }
629
679
  catch (err) {
630
680
  spinner.fail('Upgrade failed');
631
- console.log(chalk.gray('Run manually: agents upgrade --yes'));
681
+ console.log(chalk.gray(`Run manually: agents upgrade ${version ? version + ' ' : ''}--yes`));
632
682
  }
633
683
  });
634
684
  registerPullCommand(program);
685
+ registerPushCommand(program);
635
686
  registerRepoCommands(program);
636
687
  registerSetupCommand(program);
637
688
  applyGlobalHelpConventions(program);
@@ -742,6 +793,20 @@ const SETUP_EXEMPT_COMMANDS = new Set(['setup', 'help']);
742
793
  // Help and version output are pure documentation — they must never gate on
743
794
  // setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
744
795
  const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
796
+ // Fold legacy ~/.agents-system/ into ~/.agents/.system/ BEFORE ensureInitialized
797
+ // runs. ensureInitialized checks for .git inside the new path; if the user is
798
+ // upgrading from a layout where .git lives under the legacy path, the check
799
+ // would fail and exit before the migrator ever runs. Also runs outside the
800
+ // sentinel guard below because the sentinel was set by pre-fold releases and
801
+ // would otherwise skip this step on every existing install. Idempotent —
802
+ // no-ops when legacy is missing or already a symlink.
803
+ if (process.env.AGENTS_SKIP_MIGRATION !== '1') {
804
+ try {
805
+ const { foldLegacySystemRepo } = await import('./lib/migrate.js');
806
+ foldLegacySystemRepo();
807
+ }
808
+ catch { /* must never block CLI startup */ }
809
+ }
745
810
  if (!firstRun &&
746
811
  requestedCommand &&
747
812
  !SETUP_EXEMPT_COMMANDS.has(requestedCommand) &&
@@ -783,7 +848,7 @@ if (process.env.AGENTS_SKIP_MIGRATION !== '1') {
783
848
  catch { /* migration must never block CLI startup */ }
784
849
  }
785
850
  try {
786
- await maybeBootstrapShimIntegration(requestedCommand);
851
+ await maybeBootstrapShimIntegration(requestedCommand, helpOrVersionRequested);
787
852
  await program.parseAsync();
788
853
  }
789
854
  catch (err) {
@@ -23,16 +23,6 @@ export declare const GEMINI_HOOKS_MIN_VERSION = "0.26.0";
23
23
  export declare const AGENTS: Record<AgentId, AgentConfig>;
24
24
  /** All registered agent IDs derived from the AGENTS registry. */
25
25
  export declare const ALL_AGENT_IDS: AgentId[];
26
- /** Agents that support MCP (Model Context Protocol) server integration. */
27
- export declare const MCP_CAPABLE_AGENTS: AgentId[];
28
- /** Agents that support skills (SKILL.md + rules/ bundles). */
29
- export declare const SKILLS_CAPABLE_AGENTS: AgentId[];
30
- /** Agents that support file-based slash commands. */
31
- export declare const COMMANDS_CAPABLE_AGENTS: AgentId[];
32
- /** Agents that support event hooks (pre/post lifecycle callbacks). */
33
- export declare const HOOKS_CAPABLE_AGENTS: readonly AgentId[];
34
- /** Agents that support the plugin system. */
35
- export declare const PLUGINS_CAPABLE_AGENTS: AgentId[];
36
26
  /** Get the chalk color function for an agent. Works for any AgentId or SessionAgentId. */
37
27
  export declare function colorAgent(agentId: string): (s: string) => string;
38
28
  /** Return the agent's display name, colored. */
@@ -59,6 +49,13 @@ export interface UnmanagedInstall {
59
49
  configDir: string;
60
50
  version: string | null;
61
51
  }
52
+ /**
53
+ * Agents that `agents setup` probes for pre-existing native installations
54
+ * (i.e., a config dir present before agents-cli took over). Add an agent here
55
+ * once its `cliCommand` reports a usable `--version` and its session dir is
56
+ * wired into `getSessionDir`.
57
+ */
58
+ export declare const UNMANAGED_DETECTION_CANDIDATES: AgentId[];
62
59
  /**
63
60
  * Detect existing agent installations that are NOT yet managed by agents-cli.
64
61
  * Returns agents whose config dir exists as a real directory (not a symlink).
@@ -68,6 +65,22 @@ export declare function getUnmanagedAgentInstalls(): Promise<UnmanagedInstall[]>
68
65
  export declare function ensureCommandsDir(agentId: AgentId): void;
69
66
  /** Create the agent's skills directory if it does not exist. */
70
67
  export declare function ensureSkillsDir(agentId: AgentId): void;
68
+ /**
69
+ * The agent's config-dir name relative to $HOME — e.g. '.claude',
70
+ * '.gemini/antigravity-cli', '.config/amp', '.kimi-code'.
71
+ *
72
+ * Path segment to join onto a (version) home root when locating an agent's
73
+ * commands/skills/plugins. Do NOT hardcode `.${agentId}`: it is wrong for
74
+ * every agent whose config dir is nested or under ~/.config — antigravity
75
+ * (~/.gemini/antigravity-cli), amp (~/.config/amp), goose (~/.config/goose),
76
+ * kimi (~/.kimi-code). Mirrors the shim configDirName derivation in shims.ts.
77
+ *
78
+ * Relativized against the module-level HOME constant (the same value used to
79
+ * build every `configDir`), NOT a fresh `os.homedir()` — so the result stays a
80
+ * clean relative name even when HOME is overridden after module load (tests,
81
+ * sandboxes). Using `os.homedir()` here would yield `../../real/home/.claude`.
82
+ */
83
+ export declare function agentConfigDirName(agentId: AgentId): string;
71
84
  /** Account identity and billing information extracted from an agent's auth config. */
72
85
  export interface AccountInfo {
73
86
  accountKey: string | null;
@@ -203,7 +203,7 @@ export const AGENTS = {
203
203
  format: 'markdown',
204
204
  variableSyntax: '$ARGUMENTS',
205
205
  supportsHooks: true,
206
- capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
206
+ capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
207
207
  },
208
208
  // codex hooks: gated to >= 0.116.0 (introduced [features] codex_hooks flag).
209
209
  codex: {
@@ -222,7 +222,7 @@ export const AGENTS = {
222
222
  format: 'markdown',
223
223
  variableSyntax: '$ARGUMENTS',
224
224
  supportsHooks: true,
225
- capabilities: { hooks: { since: '0.116.0' }, mcp: true, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, modes: ['plan', 'edit', 'skip'] },
225
+ capabilities: { hooks: { since: '0.116.0' }, mcp: true, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
226
226
  },
227
227
  gemini: {
228
228
  id: 'gemini',
@@ -241,7 +241,7 @@ export const AGENTS = {
241
241
  supportsHooks: true,
242
242
  nativeAgentsSkillsDir: true,
243
243
  // gemini hooks: shipped in v0.26.0 (Jan 2026); older binaries silently ignore the `hooks` key.
244
- capabilities: { hooks: { since: '0.26.0' }, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
244
+ capabilities: { hooks: { since: '0.26.0' }, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'GEMINI.md' }, workflows: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
245
245
  },
246
246
  cursor: {
247
247
  id: 'cursor',
@@ -259,7 +259,7 @@ export const AGENTS = {
259
259
  format: 'markdown',
260
260
  variableSyntax: '$ARGUMENTS',
261
261
  supportsHooks: false,
262
- capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['edit', 'skip'] },
262
+ capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: '.cursorrules' }, workflows: false, modes: ['edit', 'skip'] },
263
263
  },
264
264
  opencode: {
265
265
  id: 'opencode',
@@ -276,7 +276,7 @@ export const AGENTS = {
276
276
  format: 'markdown',
277
277
  variableSyntax: '$ARGUMENTS',
278
278
  supportsHooks: false,
279
- capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit'] },
279
+ capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
280
280
  },
281
281
  openclaw: {
282
282
  id: 'openclaw',
@@ -293,7 +293,7 @@ export const AGENTS = {
293
293
  format: 'markdown',
294
294
  variableSyntax: '{{ARGUMENTS}}',
295
295
  supportsHooks: true,
296
- capabilities: { hooks: true, mcp: true, allowlist: false, skills: true, commands: false, plugins: true, modes: ['plan', 'edit', 'skip'] },
296
+ capabilities: { hooks: true, mcp: true, allowlist: false, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
297
297
  },
298
298
  copilot: {
299
299
  id: 'copilot',
@@ -310,7 +310,7 @@ export const AGENTS = {
310
310
  format: 'markdown',
311
311
  variableSyntax: '$ARGUMENTS',
312
312
  supportsHooks: false,
313
- capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit', 'auto', 'skip'] },
313
+ capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'auto', 'skip'] },
314
314
  },
315
315
  amp: {
316
316
  id: 'amp',
@@ -327,7 +327,7 @@ export const AGENTS = {
327
327
  format: 'markdown',
328
328
  variableSyntax: '$ARGUMENTS',
329
329
  supportsHooks: false,
330
- capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit'] },
330
+ capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
331
331
  },
332
332
  kiro: {
333
333
  id: 'kiro',
@@ -345,7 +345,7 @@ export const AGENTS = {
345
345
  format: 'markdown',
346
346
  variableSyntax: '$ARGUMENTS',
347
347
  supportsHooks: false,
348
- capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['edit'] },
348
+ capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit'] },
349
349
  },
350
350
  goose: {
351
351
  id: 'goose',
@@ -363,7 +363,7 @@ export const AGENTS = {
363
363
  format: 'markdown',
364
364
  variableSyntax: '$ARGUMENTS',
365
365
  supportsHooks: false,
366
- capabilities: { hooks: false, mcp: true, allowlist: false, skills: false, commands: false, plugins: false, modes: ['edit'] },
366
+ capabilities: { hooks: false, mcp: true, allowlist: false, skills: false, commands: false, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit'] },
367
367
  },
368
368
  roo: {
369
369
  id: 'roo',
@@ -381,7 +381,7 @@ export const AGENTS = {
381
381
  format: 'markdown',
382
382
  variableSyntax: '$ARGUMENTS',
383
383
  supportsHooks: false,
384
- capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, modes: ['plan', 'edit'] },
384
+ capabilities: { hooks: false, mcp: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
385
385
  },
386
386
  // Google Antigravity CLI (`agy`) — official replacement for Gemini CLI as of IO 2026.
387
387
  // configDir nests inside `~/.gemini/` since agy shares the parent dir with the Gemini
@@ -408,7 +408,7 @@ export const AGENTS = {
408
408
  format: 'markdown',
409
409
  variableSyntax: '{{args}}',
410
410
  supportsHooks: true,
411
- capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, modes: ['edit', 'skip'], rulesImports: false },
411
+ capabilities: { hooks: true, mcp: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit', 'skip'], rulesImports: false },
412
412
  },
413
413
  // xAI Grok Build CLI (`grok`) — early beta, SuperGrok Heavy. Auth via OAuth on
414
414
  // first launch, or XAI_API_KEY env var for headless. MCP servers configured inline
@@ -439,23 +439,56 @@ export const AGENTS = {
439
439
  skills: true,
440
440
  commands: false, // covered by skills
441
441
  plugins: true,
442
+ subagents: false,
443
+ rules: { file: 'AGENTS.md' },
444
+ workflows: false,
442
445
  modes: ['plan', 'edit', 'skip'],
443
446
  rulesImports: true,
444
447
  },
445
448
  },
449
+ // Kimi Code CLI (`kimi`) — Moonshot AI coding agent.
450
+ // Install: `curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash`
451
+ // or: `npm install -g @moonshot-ai/kimi-code`
452
+ // Config: `~/.kimi-code/config.toml`, `~/.kimi-code/mcp.json`,
453
+ // `~/.kimi-code/skills/`, `~/.kimi-code/hooks/`
454
+ kimi: {
455
+ id: 'kimi',
456
+ name: 'Kimi',
457
+ color: 'magentaBright',
458
+ cliCommand: 'kimi',
459
+ npmPackage: '@moonshot-ai/kimi-code',
460
+ installScript: 'curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash',
461
+ configDir: path.join(HOME, '.kimi-code'),
462
+ commandsDir: '',
463
+ commandsSubdir: '',
464
+ skillsDir: path.join(HOME, '.kimi-code', 'skills'),
465
+ hooksDir: path.join(HOME, '.kimi-code', 'hooks'),
466
+ instructionsFile: 'AGENTS.md',
467
+ format: 'markdown',
468
+ variableSyntax: '$ARGUMENTS',
469
+ supportsHooks: true,
470
+ capabilities: {
471
+ hooks: true,
472
+ mcp: true,
473
+ allowlist: true,
474
+ skills: true,
475
+ commands: false,
476
+ plugins: true,
477
+ subagents: false,
478
+ rules: { file: 'AGENTS.md' },
479
+ workflows: false,
480
+ modes: ['plan', 'edit', 'auto', 'skip'],
481
+ rulesImports: false,
482
+ },
483
+ },
446
484
  };
447
485
  /** All registered agent IDs derived from the AGENTS registry. */
448
486
  export const ALL_AGENT_IDS = Object.keys(AGENTS);
449
- /** Agents that support MCP (Model Context Protocol) server integration. */
450
- export const MCP_CAPABLE_AGENTS = ALL_AGENT_IDS.filter((id) => AGENTS[id].capabilities.mcp);
451
- /** Agents that support skills (SKILL.md + rules/ bundles). */
452
- export const SKILLS_CAPABLE_AGENTS = ALL_AGENT_IDS.filter((id) => AGENTS[id].capabilities.skills);
453
- /** Agents that support file-based slash commands. */
454
- export const COMMANDS_CAPABLE_AGENTS = ALL_AGENT_IDS.filter((id) => AGENTS[id].capabilities.commands);
455
- /** Agents that support event hooks (pre/post lifecycle callbacks). */
456
- export const HOOKS_CAPABLE_AGENTS = ALL_AGENT_IDS.filter((id) => AGENTS[id].capabilities.hooks !== false);
457
- /** Agents that support the plugin system. */
458
- export const PLUGINS_CAPABLE_AGENTS = ALL_AGENT_IDS.filter((id) => AGENTS[id].capabilities.plugins !== false);
487
+ // Capability-filtered agent lists used to live here as `*_CAPABLE_AGENTS`
488
+ // constants. They were a frequent source of silent-skip bugs (e.g. grok
489
+ // rules sync gated on `COMMANDS_CAPABLE_AGENTS`). Use `capableAgents(cap)`
490
+ // from `./capabilities.js` instead it consults the AgentConfig matrix
491
+ // directly, so a single source of truth drives every gate.
459
492
  /** Get the chalk color function for an agent. Works for any AgentId or SessionAgentId. */
460
493
  export function colorAgent(agentId) {
461
494
  const agent = AGENTS[agentId];
@@ -610,14 +643,26 @@ export function isConfigured(agentId) {
610
643
  const agent = AGENTS[agentId];
611
644
  return fs.existsSync(agent.configDir);
612
645
  }
646
+ /**
647
+ * Agents that `agents setup` probes for pre-existing native installations
648
+ * (i.e., a config dir present before agents-cli took over). Add an agent here
649
+ * once its `cliCommand` reports a usable `--version` and its session dir is
650
+ * wired into `getSessionDir`.
651
+ */
652
+ export const UNMANAGED_DETECTION_CANDIDATES = [
653
+ 'claude',
654
+ 'codex',
655
+ 'gemini',
656
+ 'grok',
657
+ 'copilot',
658
+ ];
613
659
  /**
614
660
  * Detect existing agent installations that are NOT yet managed by agents-cli.
615
661
  * Returns agents whose config dir exists as a real directory (not a symlink).
616
662
  */
617
663
  export async function getUnmanagedAgentInstalls() {
618
664
  const unmanaged = [];
619
- const candidates = ['claude', 'codex', 'gemini', 'grok'];
620
- for (const agentId of candidates) {
665
+ for (const agentId of UNMANAGED_DETECTION_CANDIDATES) {
621
666
  const agent = AGENTS[agentId];
622
667
  try {
623
668
  const stat = fs.lstatSync(agent.configDir);
@@ -646,6 +691,24 @@ export function ensureSkillsDir(agentId) {
646
691
  fs.mkdirSync(agent.skillsDir, { recursive: true });
647
692
  }
648
693
  }
694
+ /**
695
+ * The agent's config-dir name relative to $HOME — e.g. '.claude',
696
+ * '.gemini/antigravity-cli', '.config/amp', '.kimi-code'.
697
+ *
698
+ * Path segment to join onto a (version) home root when locating an agent's
699
+ * commands/skills/plugins. Do NOT hardcode `.${agentId}`: it is wrong for
700
+ * every agent whose config dir is nested or under ~/.config — antigravity
701
+ * (~/.gemini/antigravity-cli), amp (~/.config/amp), goose (~/.config/goose),
702
+ * kimi (~/.kimi-code). Mirrors the shim configDirName derivation in shims.ts.
703
+ *
704
+ * Relativized against the module-level HOME constant (the same value used to
705
+ * build every `configDir`), NOT a fresh `os.homedir()` — so the result stays a
706
+ * clean relative name even when HOME is overridden after module load (tests,
707
+ * sandboxes). Using `os.homedir()` here would yield `../../real/home/.claude`.
708
+ */
709
+ export function agentConfigDirName(agentId) {
710
+ return path.relative(HOME, AGENTS[agentId].configDir);
711
+ }
649
712
  /** Return the email address associated with the agent's auth config, or null. */
650
713
  export async function getAccountEmail(agentId, home) {
651
714
  const info = await getAccountInfo(agentId, home);
@@ -1280,7 +1343,7 @@ export function getMcpConfigPathForHome(agentId, home) {
1280
1343
  case 'grok':
1281
1344
  return path.join(home, '.grok', 'config.toml');
1282
1345
  default:
1283
- return path.join(home, `.${agentId}`, 'settings.json');
1346
+ return path.join(home, agentConfigDirName(agentId), 'settings.json');
1284
1347
  }
1285
1348
  }
1286
1349
  /**
@@ -5,7 +5,7 @@
5
5
  * For the user repo + enabled extras: `git fetch` + write a status marker the foreground
6
6
  * CLI surfaces on its next invocation.
7
7
  *
8
- * Per-repo lock files at ~/.agents-system/.fetch/<alias>.lock prevent concurrent fetches.
8
+ * Per-repo lock files at ~/.agents/.system/.fetch/<alias>.lock prevent concurrent fetches.
9
9
  * Lock mtime under 5 min => skip (another invocation already in flight).
10
10
  */
11
11
  export {};
@@ -5,7 +5,7 @@
5
5
  * For the user repo + enabled extras: `git fetch` + write a status marker the foreground
6
6
  * CLI surfaces on its next invocation.
7
7
  *
8
- * Per-repo lock files at ~/.agents-system/.fetch/<alias>.lock prevent concurrent fetches.
8
+ * Per-repo lock files at ~/.agents/.system/.fetch/<alias>.lock prevent concurrent fetches.
9
9
  * Lock mtime under 5 min => skip (another invocation already in flight).
10
10
  */
11
11
  import * as fs from 'fs';
@@ -15,7 +15,7 @@ import { getSystemAgentsDir, getUserAgentsDir, getEnabledExtraRepos, getFetchCac
15
15
  import { lockFilePath, statusFilePath } from './auto-pull.js';
16
16
  const LOCK_TTL_MS = 5 * 60 * 1000;
17
17
  /**
18
- * Background auto-pull of ~/.agents-system/ is off by default. When enabled it
18
+ * Background auto-pull of ~/.agents/.system/ is off by default. When enabled it
19
19
  * silently fast-forwards a tracked source tree that the CLI then reads as a
20
20
  * source of skills, hooks, install manifests, and commands — anyone with push
21
21
  * access to that upstream gets remote code execution on every user the next