@phnx-labs/agents-cli 1.20.4 → 1.20.5

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 (190) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +48 -17
  3. package/dist/commands/cli.js +1 -1
  4. package/dist/commands/cloud.js +1 -1
  5. package/dist/commands/commands.js +2 -0
  6. package/dist/commands/doctor.js +1 -1
  7. package/dist/commands/exec.js +52 -16
  8. package/dist/commands/hooks.js +6 -6
  9. package/dist/commands/inspect.d.ts +26 -0
  10. package/dist/commands/inspect.js +590 -0
  11. package/dist/commands/mcp.js +17 -16
  12. package/dist/commands/models.js +1 -1
  13. package/dist/commands/packages.js +6 -4
  14. package/dist/commands/permissions.js +13 -12
  15. package/dist/commands/plugins.d.ts +13 -0
  16. package/dist/commands/plugins.js +100 -11
  17. package/dist/commands/prune.js +3 -2
  18. package/dist/commands/pull.d.ts +12 -5
  19. package/dist/commands/pull.js +26 -422
  20. package/dist/commands/push.d.ts +14 -0
  21. package/dist/commands/push.js +30 -0
  22. package/dist/commands/repo.d.ts +1 -1
  23. package/dist/commands/repo.js +155 -112
  24. package/dist/commands/resource-view.d.ts +2 -0
  25. package/dist/commands/resource-view.js +12 -3
  26. package/dist/commands/routines.js +32 -7
  27. package/dist/commands/rules.js +1 -1
  28. package/dist/commands/sessions.js +1 -0
  29. package/dist/commands/setup.d.ts +3 -3
  30. package/dist/commands/setup.js +15 -15
  31. package/dist/commands/skills.js +6 -5
  32. package/dist/commands/subagents.js +5 -4
  33. package/dist/commands/sync.d.ts +18 -5
  34. package/dist/commands/sync.js +251 -65
  35. package/dist/commands/teams.js +1 -0
  36. package/dist/commands/tmux.d.ts +25 -0
  37. package/dist/commands/tmux.js +415 -0
  38. package/dist/commands/trash.d.ts +2 -2
  39. package/dist/commands/trash.js +1 -1
  40. package/dist/commands/versions.js +2 -2
  41. package/dist/commands/view.js +9 -4
  42. package/dist/commands/workflows.js +4 -3
  43. package/dist/commands/worktree.d.ts +4 -5
  44. package/dist/commands/worktree.js +4 -4
  45. package/dist/index.js +68 -20
  46. package/dist/lib/agents.d.ts +19 -10
  47. package/dist/lib/agents.js +79 -25
  48. package/dist/lib/auto-pull-worker.d.ts +1 -1
  49. package/dist/lib/auto-pull-worker.js +2 -2
  50. package/dist/lib/auto-pull.d.ts +1 -1
  51. package/dist/lib/auto-pull.js +1 -1
  52. package/dist/lib/beta.d.ts +1 -1
  53. package/dist/lib/beta.js +1 -1
  54. package/dist/lib/capabilities.js +2 -0
  55. package/dist/lib/commands.d.ts +28 -1
  56. package/dist/lib/commands.js +125 -20
  57. package/dist/lib/doctor-diff.js +2 -2
  58. package/dist/lib/exec.d.ts +14 -0
  59. package/dist/lib/exec.js +39 -5
  60. package/dist/lib/fuzzy.d.ts +12 -2
  61. package/dist/lib/fuzzy.js +29 -4
  62. package/dist/lib/git.js +8 -1
  63. package/dist/lib/hooks.d.ts +2 -2
  64. package/dist/lib/hooks.js +97 -10
  65. package/dist/lib/mcp.js +32 -2
  66. package/dist/lib/migrate.d.ts +51 -0
  67. package/dist/lib/migrate.js +227 -1
  68. package/dist/lib/models.js +62 -15
  69. package/dist/lib/permissions.d.ts +36 -2
  70. package/dist/lib/permissions.js +217 -7
  71. package/dist/lib/plugin-marketplace.d.ts +98 -40
  72. package/dist/lib/plugin-marketplace.js +196 -93
  73. package/dist/lib/plugins.d.ts +21 -4
  74. package/dist/lib/plugins.js +130 -49
  75. package/dist/lib/profiles-presets.js +12 -12
  76. package/dist/lib/project-launch.d.ts +65 -0
  77. package/dist/lib/project-launch.js +367 -0
  78. package/dist/lib/pty-client.js +1 -1
  79. package/dist/lib/pty-server.d.ts +1 -1
  80. package/dist/lib/pty-server.js +1 -1
  81. package/dist/lib/refresh.d.ts +26 -0
  82. package/dist/lib/refresh.js +315 -0
  83. package/dist/lib/resource-patterns.d.ts +1 -1
  84. package/dist/lib/resource-patterns.js +1 -1
  85. package/dist/lib/resources/commands.js +2 -2
  86. package/dist/lib/resources/hooks.d.ts +1 -1
  87. package/dist/lib/resources/hooks.js +1 -1
  88. package/dist/lib/resources/mcp.d.ts +1 -1
  89. package/dist/lib/resources/mcp.js +5 -6
  90. package/dist/lib/resources/permissions.js +5 -2
  91. package/dist/lib/resources/rules.js +3 -2
  92. package/dist/lib/resources/skills.js +3 -2
  93. package/dist/lib/resources/types.d.ts +1 -1
  94. package/dist/lib/resources.js +2 -2
  95. package/dist/lib/rotate.d.ts +1 -1
  96. package/dist/lib/rotate.js +1 -1
  97. package/dist/lib/routines.d.ts +16 -4
  98. package/dist/lib/routines.js +67 -17
  99. package/dist/lib/rules/compile.js +22 -10
  100. package/dist/lib/rules/rules.js +3 -3
  101. package/dist/lib/runner.js +16 -3
  102. package/dist/lib/scheduler.js +15 -1
  103. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  104. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  105. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +9 -1
  106. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  107. package/dist/lib/secrets/linux.d.ts +44 -9
  108. package/dist/lib/secrets/linux.js +302 -48
  109. package/dist/lib/session/db.js +15 -2
  110. package/dist/lib/session/discover.js +118 -3
  111. package/dist/lib/session/parse.js +3 -0
  112. package/dist/lib/session/types.d.ts +1 -1
  113. package/dist/lib/session/types.js +1 -1
  114. package/dist/lib/shims.d.ts +10 -9
  115. package/dist/lib/shims.js +101 -50
  116. package/dist/lib/skills.d.ts +1 -1
  117. package/dist/lib/skills.js +10 -9
  118. package/dist/lib/staleness/detectors/commands.d.ts +3 -0
  119. package/dist/lib/staleness/detectors/commands.js +46 -0
  120. package/dist/lib/staleness/detectors/hooks.d.ts +3 -0
  121. package/dist/lib/staleness/detectors/hooks.js +44 -0
  122. package/dist/lib/staleness/detectors/mcp.d.ts +3 -0
  123. package/dist/lib/staleness/detectors/mcp.js +31 -0
  124. package/dist/lib/staleness/detectors/permissions.d.ts +3 -0
  125. package/dist/lib/staleness/detectors/permissions.js +201 -0
  126. package/dist/lib/staleness/detectors/plugins.d.ts +8 -0
  127. package/dist/lib/staleness/detectors/plugins.js +23 -0
  128. package/dist/lib/staleness/detectors/rules.d.ts +3 -0
  129. package/dist/lib/staleness/detectors/rules.js +34 -0
  130. package/dist/lib/staleness/detectors/skills.d.ts +3 -0
  131. package/dist/lib/staleness/detectors/skills.js +71 -0
  132. package/dist/lib/staleness/detectors/subagents.d.ts +3 -0
  133. package/dist/lib/staleness/detectors/subagents.js +50 -0
  134. package/dist/lib/staleness/detectors/types.d.ts +22 -0
  135. package/dist/lib/staleness/detectors/types.js +1 -0
  136. package/dist/lib/staleness/detectors/workflows.d.ts +3 -0
  137. package/dist/lib/staleness/detectors/workflows.js +28 -0
  138. package/dist/lib/staleness/registry.d.ts +26 -0
  139. package/dist/lib/staleness/registry.js +123 -0
  140. package/dist/lib/staleness/writers/commands.d.ts +3 -0
  141. package/dist/lib/staleness/writers/commands.js +111 -0
  142. package/dist/lib/staleness/writers/hooks.d.ts +3 -0
  143. package/dist/lib/staleness/writers/hooks.js +47 -0
  144. package/dist/lib/staleness/writers/kinds.d.ts +10 -0
  145. package/dist/lib/staleness/writers/kinds.js +15 -0
  146. package/dist/lib/staleness/writers/lazy-map.d.ts +13 -0
  147. package/dist/lib/staleness/writers/lazy-map.js +19 -0
  148. package/dist/lib/staleness/writers/mcp.d.ts +10 -0
  149. package/dist/lib/staleness/writers/mcp.js +19 -0
  150. package/dist/lib/staleness/writers/permissions.d.ts +13 -0
  151. package/dist/lib/staleness/writers/permissions.js +26 -0
  152. package/dist/lib/staleness/writers/plugins.d.ts +7 -0
  153. package/dist/lib/staleness/writers/plugins.js +31 -0
  154. package/dist/lib/staleness/writers/rules.d.ts +7 -0
  155. package/dist/lib/staleness/writers/rules.js +55 -0
  156. package/dist/lib/staleness/writers/skills.d.ts +3 -0
  157. package/dist/lib/staleness/writers/skills.js +81 -0
  158. package/dist/lib/staleness/writers/sources.d.ts +16 -0
  159. package/dist/lib/staleness/writers/sources.js +72 -0
  160. package/dist/lib/staleness/writers/subagents.d.ts +3 -0
  161. package/dist/lib/staleness/writers/subagents.js +53 -0
  162. package/dist/lib/staleness/writers/types.d.ts +36 -0
  163. package/dist/lib/staleness/writers/types.js +1 -0
  164. package/dist/lib/staleness/writers/workflows.d.ts +7 -0
  165. package/dist/lib/staleness/writers/workflows.js +31 -0
  166. package/dist/lib/state.d.ts +34 -11
  167. package/dist/lib/state.js +58 -13
  168. package/dist/lib/subagents.d.ts +0 -2
  169. package/dist/lib/subagents.js +6 -6
  170. package/dist/lib/teams/agents.js +1 -1
  171. package/dist/lib/teams/parsers.d.ts +1 -1
  172. package/dist/lib/tmux/binary.d.ts +67 -0
  173. package/dist/lib/tmux/binary.js +141 -0
  174. package/dist/lib/tmux/index.d.ts +8 -0
  175. package/dist/lib/tmux/index.js +8 -0
  176. package/dist/lib/tmux/paths.d.ts +17 -0
  177. package/dist/lib/tmux/paths.js +30 -0
  178. package/dist/lib/tmux/session.d.ts +122 -0
  179. package/dist/lib/tmux/session.js +305 -0
  180. package/dist/lib/types.d.ts +58 -7
  181. package/dist/lib/types.js +1 -1
  182. package/dist/lib/usage.js +1 -1
  183. package/dist/lib/versions.d.ts +4 -4
  184. package/dist/lib/versions.js +135 -493
  185. package/dist/lib/workflows.d.ts +2 -4
  186. package/dist/lib/workflows.js +3 -4
  187. package/package.json +2 -2
  188. package/scripts/postinstall.js +16 -63
  189. package/dist/commands/status.d.ts +0 -9
  190. package/dist/commands/status.js +0 -25
@@ -62,8 +62,14 @@ export interface ConflictInfo {
62
62
  * v15 — remove foreground resource sync / rules refresh from launch shims.
63
63
  * Version homes are reconciled by agents-cli management commands; the
64
64
  * shim hot path only resolves a version and execs the agent binary.
65
+ * v16 — re-introduce project-scoped compile to the shim hot path via
66
+ * `agents sync --launch`. This stays fast (filesystem-only): compiles
67
+ * project rules, mirrors workspace resources, and synthesizes the
68
+ * scoped plugin marketplaces (agents-cli/agents-system/extras-<alias>/
69
+ * agents-project). Version-home reconciliation stays out of the hot
70
+ * path — management commands still own that.
65
71
  */
66
- export declare const SHIM_SCHEMA_VERSION = 15;
72
+ export declare const SHIM_SCHEMA_VERSION = 16;
67
73
  /**
68
74
  * Generate the full bash shim script for the given agent. The returned string
69
75
  * is written to ~/.agents/shims/{cliCommand} and made executable.
@@ -223,14 +229,9 @@ export declare function getPathShadowingExecutable(agent: AgentId): string | nul
223
229
  export declare function removeLegacyUserShim(agent: AgentId, overrides?: {
224
230
  homeDir?: string;
225
231
  }): boolean;
226
- /**
227
- * Check if the agent's CLI command is shadowed by a shell alias.
228
- *
229
- * Shell aliases live in the user's session and aren't visible from a Node.js
230
- * child process. We do a best-effort scan of common RC files for `alias
231
- * <command>=` patterns. Returns false when detection is inconclusive.
232
- */
233
- export declare function hasAliasShadowingShim(agent: AgentId): boolean;
232
+ export declare function hasAliasShadowingShim(agent: AgentId, overrides?: {
233
+ homeDir?: string;
234
+ }): boolean;
234
235
  /**
235
236
  * Check if shims directory is in PATH.
236
237
  */
package/dist/lib/shims.js CHANGED
@@ -186,8 +186,14 @@ async function promptConflictStrategy(conflictInfos) {
186
186
  * v15 — remove foreground resource sync / rules refresh from launch shims.
187
187
  * Version homes are reconciled by agents-cli management commands; the
188
188
  * shim hot path only resolves a version and execs the agent binary.
189
+ * v16 — re-introduce project-scoped compile to the shim hot path via
190
+ * `agents sync --launch`. This stays fast (filesystem-only): compiles
191
+ * project rules, mirrors workspace resources, and synthesizes the
192
+ * scoped plugin marketplaces (agents-cli/agents-system/extras-<alias>/
193
+ * agents-project). Version-home reconciliation stays out of the hot
194
+ * path — management commands still own that.
189
195
  */
190
- export const SHIM_SCHEMA_VERSION = 15;
196
+ export const SHIM_SCHEMA_VERSION = 16;
191
197
  /** Internal marker string used to embed the schema version in shim scripts. */
192
198
  const SHIM_VERSION_MARKER = 'agents-shim-version:';
193
199
  function shellQuote(value) {
@@ -244,14 +250,19 @@ export COPILOT_HOME="$VERSION_DIR/home/${configDirName}"
244
250
  # This gives agents-cli full versioned isolation + resource sync for grok.
245
251
  export GROK_HOME="$VERSION_DIR/home/.grok"
246
252
  `
247
- : '';
253
+ : agent === 'kimi'
254
+ ? `
255
+ # Kimi Code CLI honors KIMI_CODE_HOME to relocate ~/.kimi-code (config.toml,
256
+ # mcp.json, sessions, skills, hooks). Point it at the versioned home.
257
+ export KIMI_CODE_HOME="$VERSION_DIR/home/${configDirName}"
258
+ `
259
+ : '';
248
260
  const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
249
261
  return `#!/bin/bash
250
262
  # Auto-generated by agents-cli - do not edit
251
263
  # Shim for ${agentConfig.name}
252
264
  # ${SHIM_VERSION_MARKER} ${SHIM_SCHEMA_VERSION}
253
265
 
254
- AGENTS_SYSTEM_DIR="$HOME/.agents-system"
255
266
  AGENTS_USER_DIR="$HOME/.agents"
256
267
  AGENTS_BIN=${agentsBin}
257
268
  AGENT="${agent}"
@@ -262,10 +273,10 @@ if [ -z "$AGENTS_BIN" ] || [ ! -x "$AGENTS_BIN" ]; then
262
273
  exit 127
263
274
  fi
264
275
 
265
- # Find project agents.yaml walking up from cwd (skip $HOME/.agents-system/agents.yaml)
276
+ # Find project agents.yaml walking up from cwd (skip $HOME/.agents/agents.yaml)
266
277
  find_project_version() {
267
278
  local dir="$PWD"
268
- local user_agents_yaml="$AGENTS_SYSTEM_DIR/agents.yaml"
279
+ local user_agents_yaml="$AGENTS_USER_DIR/agents.yaml"
269
280
  while [ "$dir" != "/" ]; do
270
281
  local candidate="$dir/agents.yaml"
271
282
  if [ -f "$candidate" ] && [ "$candidate" != "$user_agents_yaml" ]; then
@@ -384,6 +395,16 @@ if [ "$AGENT" = "grok" ]; then
384
395
  # Last resort: whatever is on PATH (user may have installed grok globally)
385
396
  BINARY=$(command -v grok 2>/dev/null || echo "")
386
397
  fi
398
+ # Kimi special case: binary lives in ~/.kimi-code/bin/, not node_modules.
399
+ # We still use the agents-cli version dir purely for KIMI_CODE_HOME isolation.
400
+ elif [ "$AGENT" = "kimi" ]; then
401
+ KIMI_BINARY="$HOME/.kimi-code/bin/kimi"
402
+ if [ -x "$KIMI_BINARY" ]; then
403
+ BINARY="$KIMI_BINARY"
404
+ else
405
+ # Last resort: whatever is on PATH
406
+ BINARY=$(command -v kimi 2>/dev/null || echo "")
407
+ fi
387
408
  else
388
409
  BINARY="$VERSION_DIR/node_modules/.bin/$CLI_COMMAND"
389
410
  fi
@@ -451,6 +472,10 @@ fi
451
472
 
452
473
  ${managedEnv}
453
474
 
475
+ # Project-scoped compile (rules, workspace resources, scoped plugin marketplaces).
476
+ # Filesystem-only — sub-50ms steady state. Never blocks launch on failure.
477
+ "$AGENTS_BIN" sync --agent "$AGENT" --agent-version "$VERSION" --launch --cwd "$PWD" --quiet 2>/dev/null || true
478
+
454
479
  exec "$BINARY"${launchArgs} "$@"
455
480
  `;
456
481
  }
@@ -540,7 +565,13 @@ export CODEX_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${co
540
565
  # version MCP and session state are isolated.
541
566
  export COPILOT_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
542
567
  `
543
- : '';
568
+ : agent === 'kimi'
569
+ ? `
570
+ # Kimi Code CLI honors KIMI_CODE_HOME to relocate ~/.kimi-code (config.toml,
571
+ # mcp.json, sessions, skills, hooks). Point direct aliases at the versioned home.
572
+ export KIMI_CODE_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
573
+ `
574
+ : '';
544
575
  const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
545
576
  return `#!/bin/bash
546
577
  # Auto-generated by agents-cli - do not edit
@@ -754,7 +785,7 @@ export async function switchConfigSymlink(agent, version) {
754
785
  // Different target - update it
755
786
  fs.unlinkSync(configPath);
756
787
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
757
- fs.symlinkSync(versionConfigPath, configPath);
788
+ fs.symlinkSync(versionConfigPath, configPath, process.platform === 'win32' ? 'junction' : undefined);
758
789
  return { success: true };
759
790
  }
760
791
  else if (stat.isDirectory()) {
@@ -766,8 +797,25 @@ export async function switchConfigSymlink(agent, version) {
766
797
  const finalBackupPath = path.join(agentBackupDir, String(timestamp));
767
798
  fs.mkdirSync(agentBackupDir, { recursive: true });
768
799
  fs.renameSync(configPath, finalBackupPath);
800
+ // Session JSONLs that lived under the old configPath have just moved to
801
+ // finalBackupPath on disk. Rewrite any DB rows pointing at the old prefix
802
+ // so querySessions stops returning phantom rows (issue #136). The
803
+ // discoverer at src/lib/session/discover.ts already scans backup dirs, so
804
+ // future indexer runs will find the new files — this just keeps the
805
+ // existing rows valid in the meantime.
806
+ //
807
+ // Dynamic import so loading shims.ts doesn't transitively open the
808
+ // sessions DB — many tests partially mock state.js and would break.
809
+ try {
810
+ const { updateSessionFilePaths } = await import('./session/db.js');
811
+ updateSessionFilePaths(configPath, finalBackupPath);
812
+ }
813
+ catch (err) {
814
+ console.error(`Warning: failed to update session file_paths after backing up ${configPath}: ` +
815
+ `${err.message}. Stale rows may appear in session listings until the next scan.`);
816
+ }
769
817
  // Create symlink (parent already exists since the dir we just moved was here)
770
- fs.symlinkSync(versionConfigPath, configPath);
818
+ fs.symlinkSync(versionConfigPath, configPath, process.platform === 'win32' ? 'junction' : undefined);
771
819
  return { success: true, backupPath: finalBackupPath };
772
820
  }
773
821
  else {
@@ -780,7 +828,7 @@ export async function switchConfigSymlink(agent, version) {
780
828
  // For nested layouts (e.g., ~/.gemini/antigravity-cli) the parent dir
781
829
  // may also be missing if the parent agent (Gemini) is not installed.
782
830
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
783
- fs.symlinkSync(versionConfigPath, configPath);
831
+ fs.symlinkSync(versionConfigPath, configPath, process.platform === 'win32' ? 'junction' : undefined);
784
832
  return { success: true };
785
833
  }
786
834
  return { success: false, error: err.message };
@@ -1251,23 +1299,53 @@ export function removeLegacyUserShim(agent, overrides) {
1251
1299
  * Shell aliases live in the user's session and aren't visible from a Node.js
1252
1300
  * child process. We do a best-effort scan of common RC files for `alias
1253
1301
  * <command>=` patterns. Returns false when detection is inconclusive.
1302
+ *
1303
+ * Tracks the LAST `alias` / `unalias` action for this command per rc file —
1304
+ * a trailing `unalias codex` cancels an earlier `alias codex=...`, and
1305
+ * `unalias` can name multiple commands on one line. Without this, an
1306
+ * `alias` line elsewhere in the file would surface as a false positive
1307
+ * (e.g. seen in zshrc setups that conditionally clear an alias later).
1254
1308
  */
1255
- export function hasAliasShadowingShim(agent) {
1309
+ function escapeRegex(value) {
1310
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1311
+ }
1312
+ /** Walk rc lines in order; a later `unalias` clears an earlier `alias`. */
1313
+ function isAliasActiveInRcContent(content, cliCommand) {
1314
+ let active = false;
1315
+ const aliasPattern = new RegExp(`^\\s*alias\\s+${escapeRegex(cliCommand)}\\s*=`);
1316
+ for (const line of content.split('\n')) {
1317
+ const trimmed = line.trim();
1318
+ if (!trimmed || trimmed.startsWith('#'))
1319
+ continue;
1320
+ if (aliasPattern.test(line)) {
1321
+ active = true;
1322
+ continue;
1323
+ }
1324
+ const unaliasMatch = trimmed.match(/^unalias\s+(.+)$/);
1325
+ if (!unaliasMatch)
1326
+ continue;
1327
+ const tokens = unaliasMatch[1].split(/\s+/).filter((token) => !token.startsWith('-'));
1328
+ if (tokens.includes(cliCommand)) {
1329
+ active = false;
1330
+ }
1331
+ }
1332
+ return active;
1333
+ }
1334
+ export function hasAliasShadowingShim(agent, overrides) {
1256
1335
  const cliCommand = AGENTS[agent].cliCommand;
1257
- const HOME = os.homedir();
1336
+ const homeDir = overrides?.homeDir ?? os.homedir();
1258
1337
  const rcFiles = [
1259
- path.join(HOME, '.zshrc'),
1260
- path.join(HOME, '.bashrc'),
1261
- path.join(HOME, '.bash_profile'),
1262
- path.join(HOME, '.profile'),
1338
+ path.join(homeDir, '.zshrc'),
1339
+ path.join(homeDir, '.bashrc'),
1340
+ path.join(homeDir, '.bash_profile'),
1341
+ path.join(homeDir, '.profile'),
1263
1342
  ];
1264
- const pattern = new RegExp(`^\\s*alias\\s+${cliCommand}\\s*=`, 'm');
1265
1343
  for (const rcFile of rcFiles) {
1266
1344
  try {
1267
1345
  if (!fs.existsSync(rcFile))
1268
1346
  continue;
1269
1347
  const content = fs.readFileSync(rcFile, 'utf-8');
1270
- if (pattern.test(content))
1348
+ if (isAliasActiveInRcContent(content, cliCommand))
1271
1349
  return true;
1272
1350
  }
1273
1351
  catch {
@@ -1303,6 +1381,7 @@ function isShimPathCommandLine(line, shimsDir) {
1303
1381
  const markerRegexes = exactMarkers.map((marker) => new RegExp(`${escapeRegex(marker)}(?=$|[:\\s])`));
1304
1382
  const suffixRegexes = [
1305
1383
  /\/\.agents-system\/shims(?=$|[:\s])/,
1384
+ /\/\.agents\/\.cache\/shims(?=$|[:\s])/,
1306
1385
  /\/\.agents\/shims(?=$|[:\s])/,
1307
1386
  ];
1308
1387
  const touchesShimPath = [...markerRegexes, ...suffixRegexes].some((pattern) => pattern.test(normalized));
@@ -1364,10 +1443,10 @@ export function getPathSetupInstructions() {
1364
1443
  return `Add to ~/.config/fish/config.fish:
1365
1444
  fish_add_path ${shimsDir}`;
1366
1445
  }
1367
- return `Add to ~/${rcFile} (AFTER any nvm/node setup):
1446
+ return `Add to the end of ~/${rcFile} (after any nvm/node setup and agent installers):
1368
1447
  export PATH="${shimsDir}:$PATH"
1369
1448
 
1370
- IMPORTANT: Shims must come FIRST in PATH to override global installs.
1449
+ IMPORTANT: Shims must be the last PATH prepend in your shell config to override global installs.
1371
1450
 
1372
1451
  Then restart your shell or run:
1373
1452
  source ~/${rcFile}`;
@@ -1398,25 +1477,6 @@ export function addShimsToPath(overrides) {
1398
1477
  exportBlock = `# agents-cli: version-managed agent CLIs\nexport PATH="${shimsDir}:$PATH"\n`;
1399
1478
  }
1400
1479
  const contentWithoutShimLines = stripShimPathLines(content, shimsDir);
1401
- // Find insertion point - AFTER node/nvm/fnm setup if present, otherwise append.
1402
- const insertAfterPatterns = [
1403
- /^export NVM_DIR=/m,
1404
- /^source.*nvm/m,
1405
- /^\[ -s.*nvm/m,
1406
- /^eval.*fnm/m,
1407
- /^export PATH.*node/m,
1408
- /^export PATH.*npm/m,
1409
- ];
1410
- let insertIndex = -1;
1411
- const lines = contentWithoutShimLines.split('\n');
1412
- let offset = 0;
1413
- for (const line of lines) {
1414
- const lineWithNewline = `${line}\n`;
1415
- if (insertAfterPatterns.some((pattern) => pattern.test(line))) {
1416
- insertIndex = offset + lineWithNewline.length;
1417
- }
1418
- offset += lineWithNewline.length;
1419
- }
1420
1480
  // Write the updated content
1421
1481
  try {
1422
1482
  // Ensure parent directories exist (especially for fish: ~/.config/fish/)
@@ -1424,18 +1484,9 @@ export function addShimsToPath(overrides) {
1424
1484
  if (!fs.existsSync(rcDir)) {
1425
1485
  fs.mkdirSync(rcDir, { recursive: true });
1426
1486
  }
1427
- let newContent;
1428
- if (insertIndex >= 0) {
1429
- // Insert after the last node/nvm/fnm-related line so our prepend wins.
1430
- const before = contentWithoutShimLines.slice(0, insertIndex);
1431
- const separator = before.length > 0 && !before.endsWith('\n\n') ? '\n' : '';
1432
- newContent = before + separator + exportBlock + contentWithoutShimLines.slice(insertIndex);
1433
- }
1434
- else {
1435
- // Append to end
1436
- const separator = contentWithoutShimLines.length > 0 && !contentWithoutShimLines.endsWith('\n') ? '\n' : '';
1437
- newContent = contentWithoutShimLines + separator + exportBlock;
1438
- }
1487
+ // Append at EOF so later installer PATH prepends cannot shadow the shims.
1488
+ const separator = contentWithoutShimLines.length > 0 && !contentWithoutShimLines.endsWith('\n') ? '\n' : '';
1489
+ let newContent = contentWithoutShimLines + separator + exportBlock;
1439
1490
  newContent = newContent.replace(/\n{2,}$/g, '\n');
1440
1491
  if (newContent === content) {
1441
1492
  return { success: true, alreadyPresent: true, rcFile };
@@ -73,7 +73,7 @@ export declare function skillExists(agentId: AgentId, skillName: string): boolea
73
73
  */
74
74
  export declare function skillContentMatches(agentId: AgentId, skillName: string, sourcePath: string): boolean;
75
75
  /**
76
- * List skill names from user (~/.agents/skills/) and system (~/.agents-system/skills/) dirs.
76
+ * List skill names from user (~/.agents/skills/) and system (~/.agents/.system/skills/) dirs.
77
77
  * User dir takes priority; deduplication preserves first occurrence.
78
78
  */
79
79
  export declare function listCentralSkills(): string[];
@@ -10,7 +10,8 @@ import * as fs from 'fs';
10
10
  import * as path from 'path';
11
11
  import * as os from 'os';
12
12
  import * as yaml from 'yaml';
13
- import { SKILLS_CAPABLE_AGENTS, ensureSkillsDir } from './agents.js';
13
+ import { ensureSkillsDir, agentConfigDirName } from './agents.js';
14
+ import { capableAgents, isCapable } from './capabilities.js';
14
15
  import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, getTrashSkillsDir } from './state.js';
15
16
  import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from './versions.js';
16
17
  import { emit } from './events.js';
@@ -27,7 +28,7 @@ export function ensureCentralSkillsDir() {
27
28
  }
28
29
  export function getAgentSkillsDir(agentId) {
29
30
  const home = getEffectiveHome(agentId);
30
- return path.join(home, `.${agentId}`, 'skills');
31
+ return path.join(home, agentConfigDirName(agentId), 'skills');
31
32
  }
32
33
  export function getProjectSkillsDir(agentId, cwd = process.cwd()) {
33
34
  const dirs = [];
@@ -227,7 +228,7 @@ export function installSkill(sourcePath, skillName, agents, method = 'symlink')
227
228
  }
228
229
  // Symlink to each agent
229
230
  for (const agentId of agents) {
230
- if (!SKILLS_CAPABLE_AGENTS.includes(agentId)) {
231
+ if (!isCapable(agentId, 'skills')) {
231
232
  continue;
232
233
  }
233
234
  ensureSkillsDir(agentId);
@@ -328,7 +329,7 @@ export function skillContentMatches(agentId, skillName, sourcePath) {
328
329
  }
329
330
  }
330
331
  /**
331
- * List skill names from user (~/.agents/skills/) and system (~/.agents-system/skills/) dirs.
332
+ * List skill names from user (~/.agents/skills/) and system (~/.agents/.system/skills/) dirs.
332
333
  * User dir takes priority; deduplication preserves first occurrence.
333
334
  */
334
335
  export function listCentralSkills() {
@@ -388,7 +389,7 @@ export function listAllSkills() {
388
389
  */
389
390
  export function getVersionSkillsDir(agent, version) {
390
391
  const home = getVersionHomePath(agent, version);
391
- return path.join(home, `.${agent}`, 'skills');
392
+ return path.join(home, agentConfigDirName(agent), 'skills');
392
393
  }
393
394
  /**
394
395
  * List skill names installed in a specific version home.
@@ -586,9 +587,9 @@ export function removeSkillFromVersion(agent, version, skillName) {
586
587
  */
587
588
  export function iterSkillsCapableVersions(filter) {
588
589
  const pairs = [];
589
- const agents = filter?.agent ? [filter.agent] : SKILLS_CAPABLE_AGENTS;
590
+ const agents = filter?.agent ? [filter.agent] : capableAgents('skills');
590
591
  for (const agent of agents) {
591
- if (!SKILLS_CAPABLE_AGENTS.includes(agent))
592
+ if (!capableAgents('skills').includes(agent))
592
593
  continue;
593
594
  const versions = listInstalledVersions(agent);
594
595
  for (const version of versions) {
@@ -606,7 +607,7 @@ export function uninstallSkill(skillName) {
606
607
  return { success: false, error: `Skill '${skillName}' not found` };
607
608
  }
608
609
  // Remove from all agents
609
- for (const agentId of SKILLS_CAPABLE_AGENTS) {
610
+ for (const agentId of capableAgents('skills')) {
610
611
  const agentSkillPath = path.join(getAgentSkillsDir(agentId), skillName);
611
612
  if (fs.existsSync(agentSkillPath)) {
612
613
  try {
@@ -727,7 +728,7 @@ export function listInstalledSkillsWithScope(agentId, cwd = process.cwd(), optio
727
728
  }
728
729
  // User-scoped skills (version-aware when home is provided)
729
730
  const userSkillsDir = options?.home
730
- ? path.join(options.home, `.${agentId}`, 'skills')
731
+ ? path.join(options.home, agentConfigDirName(agentId), 'skills')
731
732
  : getAgentSkillsDir(agentId);
732
733
  if (fs.existsSync(userSkillsDir)) {
733
734
  try {
@@ -0,0 +1,3 @@
1
+ import type { AgentId } from '../../types.js';
2
+ import type { ResourceDetector } from './types.js';
3
+ export declare const commandsDetectors: Partial<Record<AgentId, ResourceDetector>>;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Commands detector — mirrors versions.ts:343-357. Inspects the version home,
3
+ * returns command names. Honors the commands-as-skills marker for grok and
4
+ * Codex >= 0.117.0; falls back to scanning `{agentDir}/<commandsSubdir>/` for
5
+ * the native path.
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import { AGENTS, agentConfigDirName } from '../../agents.js';
10
+ import { shouldInstallCommandAsSkill, listCommandSkillsInVersion } from '../../command-skills.js';
11
+ import { lazyAgentMap } from '../writers/lazy-map.js';
12
+ function buildCommandsDetector(agent) {
13
+ return {
14
+ kind: 'commands',
15
+ agent,
16
+ list({ version, versionHome }) {
17
+ const agentConfig = AGENTS[agent];
18
+ const agentDir = path.join(versionHome, agentConfigDirName(agent));
19
+ if (shouldInstallCommandAsSkill(agent, version)) {
20
+ return listCommandSkillsInVersion(agentDir);
21
+ }
22
+ const commandsDir = path.join(agentDir, agentConfig.commandsSubdir);
23
+ if (!fs.existsSync(commandsDir))
24
+ return [];
25
+ const ext = agentConfig.format === 'toml' ? '.toml' : '.md';
26
+ return fs.readdirSync(commandsDir)
27
+ .filter(f => f.endsWith(ext))
28
+ .map(f => f.replace(new RegExp(`\\${ext}$`), ''));
29
+ },
30
+ };
31
+ }
32
+ // Detector registration mirrors writers/commands.ts — see that file for the
33
+ // openclaw vs grok asymmetry.
34
+ export const commandsDetectors = lazyAgentMap(() => {
35
+ const m = {};
36
+ for (const id of Object.keys(AGENTS)) {
37
+ const cfg = AGENTS[id];
38
+ if (cfg.capabilities.commands === false && (!cfg.commandsSubdir || cfg.commandsSubdir === '') && id !== 'grok')
39
+ continue;
40
+ const hasCommands = cfg.capabilities.commands !== false;
41
+ const hasSkills = cfg.capabilities.skills !== false;
42
+ if (hasCommands || hasSkills)
43
+ m[id] = buildCommandsDetector(id);
44
+ }
45
+ return m;
46
+ });
@@ -0,0 +1,3 @@
1
+ import type { AgentId } from '../../types.js';
2
+ import type { ResourceDetector } from './types.js';
3
+ export declare const hooksDetectors: Partial<Record<AgentId, ResourceDetector>>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Hooks detector — names of hook scripts materialized in the version home
3
+ * whose contents match the central source. Mirrors versions.ts:391-421.
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import { agentConfigDirName } from '../../agents.js';
8
+ import { capableAgents } from '../../capabilities.js';
9
+ import { resolveHookSource } from '../writers/sources.js';
10
+ import { lazyAgentMap } from '../writers/lazy-map.js';
11
+ function buildHooksDetector(agent) {
12
+ return {
13
+ kind: 'hooks',
14
+ agent,
15
+ list({ versionHome }) {
16
+ const hooksDir = path.join(versionHome, agentConfigDirName(agent), 'hooks');
17
+ if (!fs.existsSync(hooksDir))
18
+ return [];
19
+ const installed = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
20
+ const synced = [];
21
+ for (const hook of installed) {
22
+ const src = resolveHookSource(hook);
23
+ if (!src) {
24
+ // True orphan — count as accounted for.
25
+ synced.push(hook);
26
+ continue;
27
+ }
28
+ try {
29
+ if (fs.readFileSync(src, 'utf-8') === fs.readFileSync(path.join(hooksDir, hook), 'utf-8')) {
30
+ synced.push(hook);
31
+ }
32
+ }
33
+ catch { /* read failure → not synced */ }
34
+ }
35
+ return synced;
36
+ },
37
+ };
38
+ }
39
+ export const hooksDetectors = lazyAgentMap(() => {
40
+ const m = {};
41
+ for (const agent of capableAgents('hooks'))
42
+ m[agent] = buildHooksDetector(agent);
43
+ return m;
44
+ });
@@ -0,0 +1,3 @@
1
+ import type { AgentId } from '../../types.js';
2
+ import type { ResourceDetector } from './types.js';
3
+ export declare const mcpDetectors: Partial<Record<AgentId, ResourceDetector>>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * MCP detector — parses the agent's canonical MCP config in the version home
3
+ * and returns server names. Mirrors versions.ts:432-443.
4
+ */
5
+ import * as fs from 'fs';
6
+ import { capableAgents } from '../../capabilities.js';
7
+ import { getMcpConfigPathForHome, parseMcpConfig } from '../../agents.js';
8
+ import { lazyAgentMap } from '../writers/lazy-map.js';
9
+ function buildMcpDetector(agent) {
10
+ return {
11
+ kind: 'mcp',
12
+ agent,
13
+ list({ versionHome }) {
14
+ const p = getMcpConfigPathForHome(agent, versionHome);
15
+ if (!fs.existsSync(p))
16
+ return [];
17
+ try {
18
+ return Object.keys(parseMcpConfig(agent, p));
19
+ }
20
+ catch {
21
+ return [];
22
+ }
23
+ },
24
+ };
25
+ }
26
+ export const mcpDetectors = lazyAgentMap(() => {
27
+ const m = {};
28
+ for (const agent of capableAgents('mcp'))
29
+ m[agent] = buildMcpDetector(agent);
30
+ return m;
31
+ });
@@ -0,0 +1,3 @@
1
+ import type { AgentId } from '../../types.js';
2
+ import type { ResourceDetector } from './types.js';
3
+ export declare const permissionsDetectors: Partial<Record<AgentId, ResourceDetector>>;