@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
@@ -2,7 +2,7 @@
2
2
  * First-run setup command.
3
3
  *
4
4
  * Registers the `agents setup` command which clones the system repo into
5
- * ~/.agents-system/ and installs agent CLIs with resource syncing.
5
+ * ~/.agents/.system/ and installs agent CLIs with resource syncing.
6
6
  */
7
7
  import chalk from 'chalk';
8
8
  import ora from 'ora';
@@ -14,7 +14,7 @@ import { DEFAULT_SYSTEM_REPO, systemRepoSlug } from '../lib/types.js';
14
14
  import { getAgentsDir, getVersionsDir, ensureAgentsDir } from '../lib/state.js';
15
15
  import { isGitRepo, cloneIntoExisting, pullRepo } from '../lib/git.js';
16
16
  import { isPromptCancelled, isInteractiveTerminal } from './utils.js';
17
- import { AGENTS, getUnmanagedAgentInstalls, countSessionFiles, agentLabel } from '../lib/agents.js';
17
+ import { AGENTS, agentConfigDirName, getUnmanagedAgentInstalls, countSessionFiles, agentLabel } from '../lib/agents.js';
18
18
  import { setGlobalDefault } from '../lib/versions.js';
19
19
  import { ensureShimCurrent, switchHomeFileSymlinks, isShimsInPath, addShimsToPath, getPathSetupInstructions } from '../lib/shims.js';
20
20
  import { setHelpSections } from '../lib/help.js';
@@ -28,7 +28,7 @@ async function importAgent(agentId, version) {
28
28
  const configDir = agent.configDir;
29
29
  const versionsDir = getVersionsDir();
30
30
  const versionHome = path.join(versionsDir, agentId, version, 'home');
31
- const versionConfigDir = path.join(versionHome, `.${agentId}`);
31
+ const versionConfigDir = path.join(versionHome, agentConfigDirName(agentId));
32
32
  // Skip if version dir already exists (collision)
33
33
  if (fs.existsSync(versionConfigDir)) {
34
34
  return { success: false, skipped: true, error: `${version} already installed` };
@@ -52,12 +52,12 @@ async function importAgent(agentId, version) {
52
52
  return { success: false, error: err.message };
53
53
  }
54
54
  }
55
- /** First-run setup. Clones ~/.agents-system/ from the system repo if needed. */
55
+ /** First-run setup. Clones ~/.agents/.system/ from the system repo if needed. */
56
56
  export async function runSetup(program, options = {}) {
57
57
  const agentsDir = getAgentsDir();
58
58
  const alreadyConfigured = isGitRepo(agentsDir);
59
59
  if (alreadyConfigured && !options.force) {
60
- console.log(chalk.gray('~/.agents-system/ is already set up.'));
60
+ console.log(chalk.gray('~/.agents/.system/ is already set up.'));
61
61
  console.log(chalk.gray('\nTo sync updates: agents repo pull system'));
62
62
  console.log(chalk.gray('To re-run setup: agents setup --force'));
63
63
  return;
@@ -73,10 +73,10 @@ export async function runSetup(program, options = {}) {
73
73
  if (options.systemRepo === false) {
74
74
  ensureAgentsDir();
75
75
  console.log(chalk.gray('Skipping system repo clone (--no-system-repo).'));
76
- console.log(chalk.gray(`Populate ~/.agents-system/ yourself before running other commands that depend on it.\n`));
76
+ console.log(chalk.gray(`Populate ~/.agents/.system/ yourself before running other commands that depend on it.\n`));
77
77
  }
78
78
  else {
79
- console.log(chalk.gray(`Cloning the system repo from ${systemRepoSlug(systemRepo)} into ~/.agents-system/.\n`));
79
+ console.log(chalk.gray(`Cloning the system repo from ${systemRepoSlug(systemRepo)} into ~/.agents/.system/.\n`));
80
80
  ensureAgentsDir();
81
81
  const spinner = ora('Cloning system repo...').start();
82
82
  if (isGitRepo(agentsDir)) {
@@ -93,7 +93,7 @@ export async function runSetup(program, options = {}) {
93
93
  // Check git is available
94
94
  try {
95
95
  const { execSync } = await import('child_process');
96
- execSync('which git', { stdio: 'ignore' });
96
+ execSync('git --version', { stdio: 'ignore' });
97
97
  }
98
98
  catch {
99
99
  spinner.fail('git is not installed');
@@ -175,7 +175,7 @@ export async function runSetup(program, options = {}) {
175
175
  }
176
176
  /**
177
177
  * Ensure the system repo exists before running a command that needs it.
178
- * If ~/.agents-system/ is not a git repo AND we're in an interactive TTY,
178
+ * If ~/.agents/.system/ is not a git repo AND we're in an interactive TTY,
179
179
  * prompt the user to run setup now. In non-interactive mode, print a clear
180
180
  * error and exit.
181
181
  */
@@ -203,23 +203,23 @@ export function registerSetupCommand(program) {
203
203
  const setupCmd = program
204
204
  .command('setup')
205
205
  .description('First-time setup. Clones a config repo and installs agent CLIs.')
206
- .option('-f, --force', 'Re-run setup even if ~/.agents-system/ already exists (use with caution)')
207
- .option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents-system/ yourself)');
206
+ .option('-f, --force', 'Re-run setup even if ~/.agents/.system/ already exists (use with caution)')
207
+ .option('--no-system-repo', 'Skip cloning the system repo (you must populate ~/.agents/.system/ yourself)');
208
208
  setHelpSections(setupCmd, {
209
209
  examples: `
210
- # First-time setup (clones the system repo into ~/.agents-system/)
210
+ # First-time setup (clones the system repo into ~/.agents/.system/)
211
211
  agents setup
212
212
 
213
- # Re-run after corruption or to repair ~/.agents-system/
213
+ # Re-run after corruption or to repair ~/.agents/.system/
214
214
  agents setup --force
215
215
  `,
216
216
  notes: `
217
217
  What it does:
218
- 1. Clones the system repo into ~/.agents-system/
219
- 2. Installs agent CLIs based on agents.yaml in that repo
220
- 3. Syncs commands, skills, hooks, and MCP servers to each version
218
+ 1. Clones the system repo into ~/.agents/.system/
219
+ 2. Imports any unmanaged agent installations it finds
221
220
 
222
- Non-interactive alternative: agents pull
221
+ To install CLIs from agents.yaml and sync resources into version homes:
222
+ agents repo refresh -y
223
223
  `,
224
224
  });
225
225
  setupCmd.action(async (options) => {
@@ -4,7 +4,8 @@ import * as fs from 'fs';
4
4
  import * as os from 'os';
5
5
  import * as path from 'path';
6
6
  import { select, checkbox } from '@inquirer/prompts';
7
- import { AGENTS, SKILLS_CAPABLE_AGENTS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
7
+ import { AGENTS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
8
+ import { capableAgents } from '../lib/capabilities.js';
8
9
  import { cloneRepo } from '../lib/git.js';
9
10
  import { discoverSkillsFromRepo, installSkillCentrally, listInstalledSkills, listInstalledSkillsWithScope, getSkillInfo, getSkillRules, getSkillsDir, countSkillFiles, tryParseSkillMetadata, diffVersionSkills, iterSkillsCapableVersions, removeSkillFromVersion, } from '../lib/skills.js';
10
11
  import { getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, } from '../lib/versions.js';
@@ -54,7 +55,7 @@ When to use:
54
55
  const resolved = resolveAgentName(parts[0]);
55
56
  if (!resolved) {
56
57
  spinner.stop();
57
- console.log(chalk.red(formatAgentError(parts[0], SKILLS_CAPABLE_AGENTS)));
58
+ console.log(chalk.red(formatAgentError(parts[0], capableAgents('skills'))));
58
59
  process.exit(1);
59
60
  }
60
61
  filterAgent = resolved;
@@ -251,7 +252,7 @@ Examples:
251
252
  let selectedAgents;
252
253
  let versionSelections;
253
254
  if (options.agents) {
254
- const result = await resolveAgentTargetsAutoInstalling(options.agents, SKILLS_CAPABLE_AGENTS, { yes: options.yes });
255
+ const result = await resolveAgentTargetsAutoInstalling(options.agents, capableAgents('skills'), { yes: options.yes });
255
256
  if (!result) {
256
257
  console.log(chalk.gray('Cancelled.'));
257
258
  return;
@@ -260,7 +261,7 @@ Examples:
260
261
  versionSelections = result.versionSelections;
261
262
  }
262
263
  else {
263
- const result = await promptAgentVersionSelection(SKILLS_CAPABLE_AGENTS, {
264
+ const result = await promptAgentVersionSelection(capableAgents('skills'), {
264
265
  skipPrompts: options.yes,
265
266
  });
266
267
  selectedAgents = result.selectedAgents;
@@ -441,7 +442,7 @@ Examples:
441
442
  const cwd = process.cwd();
442
443
  const allSkills = [];
443
444
  const seenNames = new Set();
444
- for (const agentId of SKILLS_CAPABLE_AGENTS) {
445
+ for (const agentId of capableAgents('skills')) {
445
446
  const skills = listInstalledSkillsWithScope(agentId, cwd);
446
447
  for (const skill of skills) {
447
448
  if (!seenNames.has(skill.name)) {
@@ -10,8 +10,9 @@ import ora from 'ora';
10
10
  import * as fs from 'fs';
11
11
  import * as path from 'path';
12
12
  import { agentLabel } from '../lib/agents.js';
13
+ import { capableAgents } from '../lib/capabilities.js';
13
14
  import { cloneRepo } from '../lib/git.js';
14
- import { discoverSubagentsFromRepo, installSubagentCentrally, listInstalledSubagents, getInstalledSubagent, listSubagentsForAgent, SUBAGENT_CAPABLE_AGENTS, iterSubagentsCapableVersions, removeSubagentFromVersion, } from '../lib/subagents.js';
15
+ import { discoverSubagentsFromRepo, installSubagentCentrally, listInstalledSubagents, getInstalledSubagent, listSubagentsForAgent, iterSubagentsCapableVersions, removeSubagentFromVersion, } from '../lib/subagents.js';
15
16
  import { listInstalledVersions, syncResourcesToVersion, getGlobalDefault, getVersionHomePath, promptAgentVersionSelection, } from '../lib/versions.js';
16
17
  import { getSubagentsDir, recordVersionResources } from '../lib/state.js';
17
18
  import { requireDestructiveArg, promptRemovalTargets, parseCommaSeparatedList, resolveAgentTargetsAutoInstalling, } from './utils.js';
@@ -182,7 +183,7 @@ Examples:
182
183
  let selectedAgents;
183
184
  let versionSelections;
184
185
  if (agentsArg) {
185
- const result = await resolveAgentTargetsAutoInstalling(agentsArg, SUBAGENT_CAPABLE_AGENTS, { yes: options.yes });
186
+ const result = await resolveAgentTargetsAutoInstalling(agentsArg, capableAgents('subagents'), { yes: options.yes });
186
187
  if (!result) {
187
188
  console.log(chalk.gray('Cancelled.'));
188
189
  return;
@@ -191,7 +192,7 @@ Examples:
191
192
  versionSelections = result.versionSelections;
192
193
  }
193
194
  else {
194
- const result = await promptAgentVersionSelection(SUBAGENT_CAPABLE_AGENTS, {
195
+ const result = await promptAgentVersionSelection(capableAgents('subagents'), {
195
196
  skipPrompts: options.yes,
196
197
  });
197
198
  selectedAgents = result.selectedAgents;
@@ -307,7 +308,7 @@ Examples:
307
308
  /** Every (agent, version) that supports subagents and is installed. */
308
309
  function iterSubagentCapableVersions() {
309
310
  const out = [];
310
- for (const agent of SUBAGENT_CAPABLE_AGENTS) {
311
+ for (const agent of capableAgents('subagents')) {
311
312
  for (const version of listInstalledVersions(agent)) {
312
313
  out.push({ agent, version, home: getVersionHomePath(agent, version) });
313
314
  }
@@ -1,10 +1,23 @@
1
1
  /**
2
- * Internal resource sync command.
2
+ * `agents sync` — synchronize central resources into an installed agent version.
3
3
  *
4
- * Registers the hidden `agents sync` command invoked by shims to
5
- * synchronize resources (commands, skills, hooks, memory, MCP, etc.)
6
- * into a specific agent version home before launch.
4
+ * Forms:
5
+ * agents sync claude # uses default/sole installed version
6
+ * agents sync claude@2.1.142 # explicit version
7
+ * agents sync claude@latest # newest installed
8
+ * agents sync --agent claude --agent-version 2.1.142 # legacy form, still supported
9
+ *
10
+ * In a TTY the command previews available/new resources and lets the user
11
+ * select what to sync (same prompts shown after `agents add`). Pass
12
+ * --yes for non-interactive auto-sync, --force to re-sync when nothing
13
+ * has changed, --quiet for total silence.
14
+ *
15
+ * Hot path:
16
+ * --launch is the shim entry point. It skips version-home reconciliation
17
+ * and runs only the cheap project-scoped work (rules compile, workspace
18
+ * resource mirror, per-scope plugin marketplaces). Filesystem-only,
19
+ * sub-50ms steady state. Keep changes here surgical.
7
20
  */
8
21
  import { Command } from 'commander';
9
- /** Register the hidden `agents sync` command. */
22
+ /** Register the `agents sync` command. */
10
23
  export declare function registerSyncCommand(program: Command): void;
@@ -1,88 +1,274 @@
1
1
  /**
2
- * Internal resource sync command.
2
+ * `agents sync` — synchronize central resources into an installed agent version.
3
3
  *
4
- * Registers the hidden `agents sync` command invoked by shims to
5
- * synchronize resources (commands, skills, hooks, memory, MCP, etc.)
6
- * into a specific agent version home before launch.
4
+ * Forms:
5
+ * agents sync claude # uses default/sole installed version
6
+ * agents sync claude@2.1.142 # explicit version
7
+ * agents sync claude@latest # newest installed
8
+ * agents sync --agent claude --agent-version 2.1.142 # legacy form, still supported
9
+ *
10
+ * In a TTY the command previews available/new resources and lets the user
11
+ * select what to sync (same prompts shown after `agents add`). Pass
12
+ * --yes for non-interactive auto-sync, --force to re-sync when nothing
13
+ * has changed, --quiet for total silence.
14
+ *
15
+ * Hot path:
16
+ * --launch is the shim entry point. It skips version-home reconciliation
17
+ * and runs only the cheap project-scoped work (rules compile, workspace
18
+ * resource mirror, per-scope plugin marketplaces). Filesystem-only,
19
+ * sub-50ms steady state. Keep changes here surgical.
7
20
  */
8
21
  import * as path from 'path';
9
22
  import chalk from 'chalk';
10
- import { AGENTS } from '../lib/agents.js';
11
- import { isVersionInstalled, syncResourcesToVersion } from '../lib/versions.js';
23
+ import { agentLabel, resolveAgentName } from '../lib/agents.js';
24
+ import { isVersionInstalled, syncResourcesToVersion, parseAgentSpec, resolveVersion, listInstalledVersions, getAvailableResources, getActuallySyncedResources, getProjectOnlyResources, getNewResources, hasNewResources, promptResourceSelection, promptNewResourceSelection, } from '../lib/versions.js';
12
25
  import { compileRulesForProject } from '../lib/rules/compile.js';
13
- /** Register the hidden `agents sync` command. */
26
+ import { runLaunchSync } from '../lib/project-launch.js';
27
+ import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
28
+ /** Register the `agents sync` command. */
14
29
  export function registerSyncCommand(program) {
15
30
  program
16
- .command('sync', { hidden: true })
17
- .description('Internal: sync resources to a version home. Called by shims, not directly by users.')
18
- .requiredOption('--agent <agent>', 'Agent identifier (claude, codex, gemini, cursor, opencode, openclaw)')
19
- .requiredOption('--agent-version <version>', 'Installed version to sync resources into')
31
+ .command('sync [agentSpec]')
32
+ .summary('Sync resources into an installed agent version')
33
+ .description('Sync resources (commands, skills, hooks, rules, MCPs, plugins, etc.) into an installed agent version. Previews what will change and lets you pick.\n\n[agentSpec] is the agent name with an optional @version, e.g. "claude" or "claude@2.1.142". Omit the version to sync into the active (or sole installed) one.')
34
+ .option('--agent <agent>', 'Agent identifier (legacy form; prefer the positional spec)')
35
+ .option('--agent-version <version>', 'Version to sync into (legacy form; prefer "agent@version")')
20
36
  .option('--project-dir <path>', 'Path to project-level .agents/ directory containing project-scoped resources')
21
37
  .option('--cwd <path>', 'Working directory for discovering project manifest and resources')
38
+ .option('--launch', 'Hot-path mode (shim only): skip version-home reconciliation, run project-scoped compile + workspace mirror + plugin marketplaces', false)
39
+ .option('-y, --yes', 'Skip the interactive preview and auto-sync all detected resources', false)
40
+ .option('--force', 'Re-sync even if no changes are detected since the last sync', false)
22
41
  .option('--quiet', 'Suppress all output (exit code indicates success)', false)
23
- .action((opts) => {
24
- const agentId = opts.agent;
25
- const version = opts.agentVersion;
26
- const projectDir = opts.projectDir;
27
- const cwd = opts.cwd;
28
- const quiet = !!opts.quiet;
29
- if (!AGENTS[agentId]) {
30
- if (!quiet) {
31
- console.error(chalk.red(`Unknown agent '${agentId}'`));
32
- }
42
+ .action(async (agentSpec, opts) => {
43
+ await runSync(agentSpec, opts);
44
+ });
45
+ }
46
+ async function runSync(agentSpec, opts) {
47
+ const quiet = !!opts.quiet;
48
+ const errLog = (msg) => { if (!quiet)
49
+ console.error(msg); };
50
+ const outLog = (msg) => { if (!quiet)
51
+ console.log(msg); };
52
+ // ---------- 1. Resolve agent + version ----------
53
+ let agentId;
54
+ let version;
55
+ if (agentSpec) {
56
+ const parsed = parseAgentSpec(agentSpec);
57
+ if (!parsed) {
58
+ errLog(chalk.red(`Invalid agent spec '${agentSpec}'.`));
59
+ errLog(chalk.gray('Examples: claude, claude@2.1.142, codex@latest'));
33
60
  process.exitCode = 1;
34
61
  return;
35
62
  }
36
- if (!isVersionInstalled(agentId, version)) {
37
- if (!quiet) {
38
- console.error(chalk.red(`${AGENTS[agentId].name}@${version} is not installed`));
39
- }
63
+ agentId = parsed.agent;
64
+ if (parsed.version !== 'latest')
65
+ version = parsed.version;
66
+ }
67
+ if (opts.agent) {
68
+ const resolved = resolveAgentName(opts.agent);
69
+ if (!resolved) {
70
+ errLog(chalk.red(`Unknown agent '${opts.agent}'.`));
40
71
  process.exitCode = 1;
41
72
  return;
42
73
  }
43
- const result = syncResourcesToVersion(agentId, version, undefined, { projectDir, cwd });
44
- // Compile project-scope rules into the workspace itself so each agent's
45
- // native loader picks up cwd/<INSTRUCTIONS_FILE>. projectDir is the
46
- // .agents/ directory; the workspace root is its parent.
47
- let projectCompile = null;
48
- if (projectDir) {
49
- const projectRoot = path.dirname(projectDir);
50
- projectCompile = compileRulesForProject(projectRoot);
51
- }
52
- if (quiet) {
53
- return;
74
+ agentId = resolved;
75
+ }
76
+ if (opts.agentVersion) {
77
+ version = opts.agentVersion;
78
+ }
79
+ if (!agentId) {
80
+ errLog(chalk.red('Usage: agents sync <agent>[@version]'));
81
+ errLog(chalk.gray(' agents sync claude'));
82
+ errLog(chalk.gray(' agents sync claude@2.1.142'));
83
+ process.exitCode = 1;
84
+ return;
85
+ }
86
+ // ---------- 2. Resolve version (project pin → global default → sole installed) ----------
87
+ if (!version) {
88
+ version = resolveVersion(agentId, process.cwd()) || undefined;
89
+ if (!version) {
90
+ const installed = listInstalledVersions(agentId);
91
+ if (installed.length === 1) {
92
+ version = installed[0];
93
+ }
94
+ else if (installed.length === 0) {
95
+ errLog(chalk.red(`No ${agentLabel(agentId)} versions installed.`));
96
+ errLog(chalk.gray(`Install one: agents add ${agentId}@latest`));
97
+ process.exitCode = 1;
98
+ return;
99
+ }
100
+ else {
101
+ errLog(chalk.red(`No default ${agentLabel(agentId)} version pinned. Specify one:`));
102
+ for (const v of installed) {
103
+ errLog(chalk.gray(` agents sync ${agentId}@${v}`));
104
+ }
105
+ process.exitCode = 1;
106
+ return;
107
+ }
54
108
  }
55
- const synced = [];
56
- if (result.commands)
57
- synced.push('commands');
58
- if (result.skills)
59
- synced.push('skills');
60
- if (result.hooks)
61
- synced.push('hooks');
62
- if (result.memory.length > 0)
63
- synced.push('memory');
64
- if (result.permissions)
65
- synced.push('permissions');
66
- if (result.mcp.length > 0)
67
- synced.push('mcp');
68
- if (result.subagents.length > 0)
69
- synced.push('subagents');
70
- if (result.plugins.length > 0)
71
- synced.push('plugins');
72
- if (synced.length > 0) {
73
- console.log(chalk.green(`Synced ${synced.join(', ')} to ${agentId}@${version}`));
109
+ }
110
+ if (!isVersionInstalled(agentId, version)) {
111
+ errLog(chalk.red(`${agentLabel(agentId)}@${version} is not installed.`));
112
+ const installed = listInstalledVersions(agentId);
113
+ if (installed.length > 0) {
114
+ errLog(chalk.gray(`Installed: ${installed.join(', ')}`));
74
115
  }
75
- else {
76
- console.log(chalk.gray('No resources to sync'));
116
+ errLog(chalk.gray(`Install it: agents add ${agentId}@${version}`));
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+ const projectDir = opts.projectDir;
121
+ const cwd = opts.cwd || process.cwd();
122
+ // ---------- 3. --launch mode bypasses everything below ----------
123
+ if (opts.launch) {
124
+ runLaunchMode(agentId, version, cwd, quiet);
125
+ return;
126
+ }
127
+ // ---------- 4. Decide selection (interactive preview vs auto) ----------
128
+ const force = !!opts.force;
129
+ const yes = !!opts.yes;
130
+ const interactive = !quiet && !yes && isInteractiveTerminal();
131
+ let selection;
132
+ if (interactive) {
133
+ const available = getAvailableResources(cwd);
134
+ const actuallySynced = getActuallySyncedResources(agentId, version, { cwd });
135
+ const projectOnly = getProjectOnlyResources(cwd);
136
+ const newResources = getNewResources(available, actuallySynced, projectOnly);
137
+ const hasAnySynced = anyResources(actuallySynced);
138
+ try {
139
+ if (!hasAnySynced) {
140
+ outLog(chalk.cyan(`Syncing to ${agentLabel(agentId)}@${version}.`));
141
+ const userSelection = await promptResourceSelection(agentId);
142
+ if (!userSelection || Object.keys(userSelection).length === 0) {
143
+ outLog(chalk.gray('Nothing selected. No changes made.'));
144
+ return;
145
+ }
146
+ selection = userSelection;
147
+ }
148
+ else if (hasNewResources(newResources, agentId, version)) {
149
+ const userSelection = await promptNewResourceSelection(agentId, newResources, version);
150
+ if (!userSelection || Object.keys(userSelection).length === 0) {
151
+ outLog(chalk.gray('Nothing selected. No changes made.'));
152
+ return;
153
+ }
154
+ selection = userSelection;
155
+ }
156
+ else if (!force) {
157
+ outLog(chalk.gray(`${agentLabel(agentId)}@${version} is already in sync.`));
158
+ outLog(chalk.gray('Run with --force to re-sync, or --yes to bypass this check.'));
159
+ return;
160
+ }
161
+ // else: --force on a fully-synced version → selection stays undefined,
162
+ // syncResourcesToVersion falls through to its pattern-based full sync.
77
163
  }
78
- if (projectCompile?.compiled) {
79
- const linkInfo = projectCompile.symlinks.length > 0
80
- ? ` (+ ${projectCompile.symlinks.join(', ')})`
81
- : '';
82
- console.log(chalk.gray(`Compiled project rules → ${projectCompile.agentsPath}${linkInfo}`));
164
+ catch (e) {
165
+ if (isPromptCancelled(e)) {
166
+ outLog(chalk.gray('Cancelled. No changes made.'));
167
+ return;
168
+ }
169
+ throw e;
83
170
  }
84
- if (projectCompile && projectCompile.skippedClobber.length > 0) {
85
- console.log(chalk.yellow(`Skipped (user-authored, not overwritten): ${projectCompile.skippedClobber.join(', ')}`));
171
+ }
172
+ // ---------- 5. Run sync ----------
173
+ const result = syncResourcesToVersion(agentId, version, selection, { projectDir, cwd, force });
174
+ // Compile project-scope rules into the workspace itself so each agent's
175
+ // native loader picks up cwd/<INSTRUCTIONS_FILE>. projectDir is the
176
+ // .agents/ directory; the workspace root is its parent.
177
+ let projectCompile = null;
178
+ if (projectDir) {
179
+ const projectRoot = path.dirname(projectDir);
180
+ projectCompile = compileRulesForProject(projectRoot);
181
+ }
182
+ if (quiet)
183
+ return;
184
+ // ---------- 6. Detailed output ----------
185
+ printSyncDetail(result, agentId, version, cwd);
186
+ if (projectCompile?.compiled) {
187
+ const linkInfo = projectCompile.symlinks.length > 0
188
+ ? ` (+ ${projectCompile.symlinks.join(', ')})`
189
+ : '';
190
+ console.log(chalk.gray(`Compiled project rules → ${projectCompile.agentsPath}${linkInfo}`));
191
+ }
192
+ if (projectCompile && projectCompile.skippedClobber.length > 0) {
193
+ console.log(chalk.yellow(`Skipped (user-authored, not overwritten): ${projectCompile.skippedClobber.join(', ')}`));
194
+ }
195
+ }
196
+ function anyResources(r) {
197
+ return r.commands.length + r.skills.length + r.hooks.length + r.memory.length +
198
+ r.mcp.length + r.permissions.length + r.subagents.length +
199
+ r.plugins.length + r.workflows.length > 0;
200
+ }
201
+ /** Format the post-sync detail output: per-kind count + a name preview. */
202
+ function printSyncDetail(result, agent, version, cwd) {
203
+ // Booleans in SyncResult (commands, skills, hooks, permissions) carry no
204
+ // name list. Re-derive ground truth from the version home so the user
205
+ // sees what's actually present after the sync.
206
+ const synced = getActuallySyncedResources(agent, version, { cwd });
207
+ const lines = [];
208
+ if (result.commands)
209
+ lines.push({ kind: 'commands', items: synced.commands });
210
+ if (result.skills)
211
+ lines.push({ kind: 'skills', items: synced.skills });
212
+ if (result.hooks)
213
+ lines.push({ kind: 'hooks', items: synced.hooks });
214
+ if (result.memory.length > 0)
215
+ lines.push({ kind: 'memory', items: result.memory });
216
+ if (result.permissions)
217
+ lines.push({ kind: 'permissions', items: synced.permissions });
218
+ if (result.mcp.length > 0)
219
+ lines.push({ kind: 'mcp', items: result.mcp });
220
+ if (result.subagents.length > 0)
221
+ lines.push({ kind: 'subagents', items: result.subagents });
222
+ if (result.plugins.length > 0)
223
+ lines.push({ kind: 'plugins', items: result.plugins });
224
+ if (result.workflows.length > 0)
225
+ lines.push({ kind: 'workflows', items: result.workflows });
226
+ if (lines.length === 0) {
227
+ console.log(chalk.gray(`Already in sync — ${agentLabel(agent)}@${version}`));
228
+ return;
229
+ }
230
+ console.log(chalk.green(`Synced to ${agentLabel(agent)}@${version}:`));
231
+ const kindWidth = Math.max(...lines.map(l => l.kind.length));
232
+ const PREVIEW = 5;
233
+ for (const { kind, items } of lines) {
234
+ const padded = kind.padEnd(kindWidth);
235
+ const sorted = [...items].sort((a, b) => a.localeCompare(b));
236
+ const preview = sorted.slice(0, PREVIEW).join(', ');
237
+ const more = sorted.length > PREVIEW ? chalk.gray(`, +${sorted.length - PREVIEW} more`) : '';
238
+ const count = chalk.cyan(`(${sorted.length})`.padStart(5));
239
+ console.log(` ${chalk.bold(padded)} ${count} ${chalk.gray(preview)}${more}`);
240
+ }
241
+ }
242
+ function runLaunchMode(agent, version, cwd, quiet) {
243
+ let result;
244
+ try {
245
+ result = runLaunchSync({ agent, version, cwd });
246
+ }
247
+ catch (err) {
248
+ if (!quiet) {
249
+ console.error(chalk.yellow(`agents: launch sync skipped (${err.message})`));
86
250
  }
87
- });
251
+ return;
252
+ }
253
+ if (quiet)
254
+ return;
255
+ const bits = [];
256
+ if (result.rulesCompiled)
257
+ bits.push('rules');
258
+ if (result.workspaceLinks > 0)
259
+ bits.push(`${result.workspaceLinks} workspace link(s)`);
260
+ const mpCount = Object.keys(result.marketplaces).length;
261
+ if (mpCount > 0) {
262
+ const pluginCount = Object.values(result.marketplaces).reduce((acc, names) => acc + names.length, 0);
263
+ bits.push(`${pluginCount} plugin(s) across ${mpCount} marketplace(s)`);
264
+ }
265
+ if (bits.length === 0) {
266
+ console.log(chalk.gray('No project resources to compile'));
267
+ }
268
+ else {
269
+ console.log(chalk.green(`Launch sync: ${bits.join(', ')}`));
270
+ }
271
+ if (result.workspaceSkipped.length > 0) {
272
+ console.log(chalk.yellow(`Skipped (user-owned, not overwritten): ${result.workspaceSkipped.join(', ')}`));
273
+ }
88
274
  }