@phnx-labs/agents-cli 1.15.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +143 -39
  2. package/README.md +6 -6
  3. package/dist/commands/alias.js +2 -2
  4. package/dist/commands/browser-picker.d.ts +21 -0
  5. package/dist/commands/browser-picker.js +114 -0
  6. package/dist/commands/browser.js +793 -83
  7. package/dist/commands/cloud.js +8 -0
  8. package/dist/commands/commands.js +72 -22
  9. package/dist/commands/daemon.js +2 -2
  10. package/dist/commands/exec.js +70 -1
  11. package/dist/commands/hooks.js +71 -26
  12. package/dist/commands/mcp.js +81 -39
  13. package/dist/commands/plugins.js +224 -17
  14. package/dist/commands/prune.js +29 -1
  15. package/dist/commands/pull.js +3 -3
  16. package/dist/commands/repo.js +1 -1
  17. package/dist/commands/routines.js +2 -2
  18. package/dist/commands/secrets.js +154 -20
  19. package/dist/commands/sessions.js +62 -19
  20. package/dist/commands/{init.d.ts → setup.d.ts} +7 -6
  21. package/dist/commands/{init.js → setup.js} +22 -21
  22. package/dist/commands/skills.js +60 -19
  23. package/dist/commands/subagents.js +41 -13
  24. package/dist/commands/utils.d.ts +16 -0
  25. package/dist/commands/utils.js +32 -0
  26. package/dist/commands/view.js +78 -20
  27. package/dist/commands/workflows.d.ts +10 -0
  28. package/dist/commands/workflows.js +457 -0
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +48 -36
  31. package/dist/lib/agents.js +2 -2
  32. package/dist/lib/auto-pull-worker.js +2 -3
  33. package/dist/lib/auto-pull.js +2 -2
  34. package/dist/lib/browser/cdp.d.ts +7 -1
  35. package/dist/lib/browser/cdp.js +32 -1
  36. package/dist/lib/browser/chrome.d.ts +10 -0
  37. package/dist/lib/browser/chrome.js +41 -3
  38. package/dist/lib/browser/devices.d.ts +4 -0
  39. package/dist/lib/browser/devices.js +27 -0
  40. package/dist/lib/browser/drivers/local.js +22 -6
  41. package/dist/lib/browser/drivers/ssh.js +9 -2
  42. package/dist/lib/browser/input.d.ts +1 -0
  43. package/dist/lib/browser/input.js +3 -0
  44. package/dist/lib/browser/ipc.js +158 -23
  45. package/dist/lib/browser/profiles.d.ts +10 -2
  46. package/dist/lib/browser/profiles.js +122 -37
  47. package/dist/lib/browser/service.d.ts +91 -13
  48. package/dist/lib/browser/service.js +767 -132
  49. package/dist/lib/browser/types.d.ts +91 -3
  50. package/dist/lib/browser/types.js +16 -0
  51. package/dist/lib/cloud/rush.d.ts +28 -1
  52. package/dist/lib/cloud/rush.js +69 -14
  53. package/dist/lib/cloud/store.js +2 -2
  54. package/dist/lib/commands.d.ts +1 -15
  55. package/dist/lib/commands.js +11 -7
  56. package/dist/lib/daemon.js +2 -3
  57. package/dist/lib/doctor-diff.js +4 -4
  58. package/dist/lib/events.js +2 -2
  59. package/dist/lib/hooks.d.ts +11 -7
  60. package/dist/lib/hooks.js +138 -49
  61. package/dist/lib/migrate.d.ts +1 -1
  62. package/dist/lib/migrate.js +1237 -22
  63. package/dist/lib/models.js +2 -2
  64. package/dist/lib/permissions.d.ts +8 -66
  65. package/dist/lib/permissions.js +18 -18
  66. package/dist/lib/plugins.d.ts +94 -24
  67. package/dist/lib/plugins.js +702 -123
  68. package/dist/lib/pty-server.js +9 -10
  69. package/dist/lib/resource-patterns.d.ts +41 -0
  70. package/dist/lib/resource-patterns.js +82 -0
  71. package/dist/lib/resources/hooks.d.ts +5 -1
  72. package/dist/lib/resources/hooks.js +21 -4
  73. package/dist/lib/resources/index.d.ts +17 -0
  74. package/dist/lib/resources/index.js +7 -0
  75. package/dist/lib/resources/types.d.ts +1 -1
  76. package/dist/lib/resources/workflows.d.ts +24 -0
  77. package/dist/lib/resources/workflows.js +110 -0
  78. package/dist/lib/resources.d.ts +6 -1
  79. package/dist/lib/resources.js +12 -2
  80. package/dist/lib/rotate.js +3 -4
  81. package/dist/lib/session/active.d.ts +3 -0
  82. package/dist/lib/session/active.js +92 -6
  83. package/dist/lib/session/cloud.js +2 -2
  84. package/dist/lib/session/db.d.ts +18 -0
  85. package/dist/lib/session/db.js +109 -5
  86. package/dist/lib/session/discover.d.ts +6 -0
  87. package/dist/lib/session/discover.js +55 -29
  88. package/dist/lib/session/team-filter.js +2 -2
  89. package/dist/lib/shims.d.ts +4 -52
  90. package/dist/lib/shims.js +23 -15
  91. package/dist/lib/skills.js +6 -2
  92. package/dist/lib/sqlite.js +10 -4
  93. package/dist/lib/state.d.ts +101 -16
  94. package/dist/lib/state.js +179 -31
  95. package/dist/lib/subagents.d.ts +28 -0
  96. package/dist/lib/subagents.js +98 -1
  97. package/dist/lib/sync-manifest.d.ts +1 -1
  98. package/dist/lib/sync-manifest.js +3 -3
  99. package/dist/lib/teams/persistence.js +15 -5
  100. package/dist/lib/teams/registry.js +2 -2
  101. package/dist/lib/types.d.ts +75 -17
  102. package/dist/lib/types.js +3 -3
  103. package/dist/lib/usage.js +2 -2
  104. package/dist/lib/versions.d.ts +3 -0
  105. package/dist/lib/versions.js +158 -47
  106. package/dist/lib/workflows.d.ts +79 -0
  107. package/dist/lib/workflows.js +233 -0
  108. package/package.json +1 -5
  109. package/scripts/postinstall.js +60 -59
  110. package/dist/commands/fork.d.ts +0 -10
  111. package/dist/commands/fork.js +0 -146
@@ -2,19 +2,24 @@ import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
5
- import * as yaml from 'yaml';
6
5
  import { AGENTS, ALL_AGENT_IDS, getAllCliStates, getAccountInfo, resolveAgentName, formatAgentError, agentLabel, colorAgent, } from '../lib/agents.js';
7
6
  import { formatUsageSection, formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
8
7
  import { readManifest } from '../lib/manifest.js';
9
8
  import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, resolveVersionAlias, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, } from '../lib/versions.js';
10
9
  import { getShimsDir, isShimsInPath, ensureVersionedAliasCurrent, removeShim, } from '../lib/shims.js';
11
10
  import { getAgentResources } from '../lib/resources.js';
12
- import { getAgentsDir, getUserAgentsDir, getPromptcutsPath } from '../lib/state.js';
11
+ import { WORKFLOW_CAPABLE_AGENTS } from '../lib/workflows.js';
12
+ import { getAgentsDir, getUserAgentsDir, getEffectivePromptcutsPath, readMergedPromptcuts } from '../lib/state.js';
13
13
  import { isGitRepo, getGitSyncStatus } from '../lib/git.js';
14
14
  import { getCentralRulesFileName } from '../lib/rules/rules.js';
15
+ import { composeRulesFromState } from '../lib/rules/compose.js';
15
16
  import { getConfiguredRunStrategy } from '../lib/rotate.js';
16
17
  import { confirm } from '@inquirer/prompts';
17
18
  import { formatPath, isInteractiveTerminal, isPromptCancelled } from './utils.js';
19
+ function termLink(text, filePath) {
20
+ const url = `file://${filePath}`;
21
+ return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
22
+ }
18
23
  function formatLastActive(date) {
19
24
  if (!date)
20
25
  return '';
@@ -356,6 +361,8 @@ async function showInstalledVersions(filterAgentId) {
356
361
  synced.push('mcp');
357
362
  if (result.plugins.length > 0)
358
363
  synced.push('plugins');
364
+ if (result.workflows.length > 0)
365
+ synced.push('workflows');
359
366
  if (synced.length > 0) {
360
367
  console.log(chalk.green(`\nSynced to ${agentLabel(filterAgentId)}@${defaultVersion}: ${synced.join(', ')}`));
361
368
  }
@@ -478,6 +485,7 @@ async function showAgentResources(agentId, requestedVersion) {
478
485
  ...r,
479
486
  syncState: r.scope === 'project' ? undefined : getSyncState(r.name, 'hooks', hooksSync),
480
487
  })),
488
+ workflows: resources.workflows.map(r => ({ name: r.name, path: r.path, scope: r.scope })),
481
489
  };
482
490
  spinner.stop();
483
491
  // Render helper for resources
@@ -488,7 +496,8 @@ async function showAgentResources(agentId, requestedVersion) {
488
496
  return;
489
497
  }
490
498
  const versionStr = agentData.version ? ` (${agentData.version})` : '';
491
- console.log(` ${chalk.bold(agentData.agentName)}${chalk.gray(versionStr)}:`);
499
+ const agentHeader = home ? termLink(agentData.agentName, home) : agentData.agentName;
500
+ console.log(` ${chalk.bold(agentHeader)}${chalk.gray(versionStr)}:`);
492
501
  for (const r of items) {
493
502
  let nameColor = chalk.cyan;
494
503
  if (r.syncState === 'synced')
@@ -499,7 +508,8 @@ async function showAgentResources(agentId, requestedVersion) {
499
508
  nameColor = chalk.yellow;
500
509
  else if (r.syncState === 'deleted')
501
510
  nameColor = chalk.red;
502
- let display = nameColor(r.name);
511
+ const linkedName = r.path ? termLink(r.name, r.path) : r.name;
512
+ let display = nameColor(linkedName);
503
513
  if (r.ruleCount !== undefined)
504
514
  display += chalk.gray(` (${r.ruleCount} rules)`);
505
515
  // Source annotation: project overrides user, user overrides system
@@ -507,30 +517,23 @@ async function showAgentResources(agentId, requestedVersion) {
507
517
  : r.scope === 'user' ? chalk.cyan('[user]')
508
518
  : chalk.gray('[system]');
509
519
  display += ` ${sourceTag}`;
510
- const pathStr = r.path ? chalk.gray(formatPath(r.path, cwd)) : '';
511
520
  const syncStr = r.syncState ? chalk.gray(` [${r.syncState}]`) : '';
512
- console.log(` ${display.padEnd(38)} ${pathStr}${syncStr}`);
521
+ console.log(` ${display}${syncStr}`);
513
522
  }
514
523
  }
515
- // Render the single ~/.agents/promptcuts.yaml (cross-agent, not per-version).
516
- // Reads the file to surface the shortcut count cheap (<1KB typical).
524
+ // Render promptcuts (cross-agent, not per-version). Shortcuts are layered
525
+ // across system + user files with user precedence; the displayed file path
526
+ // is whichever is "live" — user if it exists, else system.
517
527
  function renderPromptcuts() {
518
528
  console.log(chalk.bold(`\nPromptcuts\n`));
519
- const promptcutsPath = getPromptcutsPath();
520
- if (!fs.existsSync(promptcutsPath)) {
529
+ const merged = readMergedPromptcuts();
530
+ const count = Object.keys(merged).length;
531
+ if (count === 0) {
521
532
  console.log(` ${chalk.gray('none')}`);
522
533
  return;
523
534
  }
524
- let count = 0;
525
- try {
526
- const parsed = yaml.parse(fs.readFileSync(promptcutsPath, 'utf-8'));
527
- count = parsed?.shortcuts ? Object.keys(parsed.shortcuts).length : 0;
528
- }
529
- catch {
530
- count = 0;
531
- }
532
535
  const label = `${count} shortcut${count === 1 ? '' : 's'}`;
533
- console.log(` ${chalk.green(label).padEnd(24)} ${chalk.gray(formatPath(promptcutsPath, cwd))}`);
536
+ console.log(` ${chalk.green(label).padEnd(24)} ${chalk.gray(formatPath(getEffectivePromptcutsPath(), cwd))}`);
534
537
  }
535
538
  // 1. Agent CLI info
536
539
  console.log(chalk.bold('Agent CLIs\n'));
@@ -565,7 +568,62 @@ async function showAgentResources(agentId, requestedVersion) {
565
568
  }
566
569
  }
567
570
  renderSection('MCP Servers', agentData.mcp);
568
- renderSection('Rules', agentData.memory);
571
+ if (WORKFLOW_CAPABLE_AGENTS.includes(agentId)) {
572
+ renderSection('Workflows', agentData.workflows);
573
+ }
574
+ // Rules section with subrules breakdown
575
+ function renderRulesSection() {
576
+ console.log(chalk.bold('\nRules\n'));
577
+ const items = agentData.memory;
578
+ if (items.length === 0) {
579
+ console.log(` ${chalk.gray('none')}`);
580
+ return;
581
+ }
582
+ const versionStr = agentData.version ? ` (${agentData.version})` : '';
583
+ console.log(` ${chalk.bold(agentData.agentName)}${chalk.gray(versionStr)}:`);
584
+ // Get composed subrules for the user scope
585
+ let composedSubrules = [];
586
+ try {
587
+ const composed = composeRulesFromState({ cwd });
588
+ composedSubrules = composed.subrules;
589
+ }
590
+ catch {
591
+ // No preset configured or rules.yaml missing — show rules without subrule breakdown
592
+ }
593
+ for (const r of items) {
594
+ let nameColor = chalk.cyan;
595
+ if (r.syncState === 'synced')
596
+ nameColor = chalk.green;
597
+ else if (r.syncState === 'new')
598
+ nameColor = chalk.blue;
599
+ else if (r.syncState === 'modified')
600
+ nameColor = chalk.yellow;
601
+ else if (r.syncState === 'deleted')
602
+ nameColor = chalk.red;
603
+ const linkedName = r.path ? termLink(r.name, r.path) : r.name;
604
+ let display = nameColor(linkedName);
605
+ if (r.ruleCount !== undefined)
606
+ display += chalk.gray(` (${r.ruleCount} rules)`);
607
+ const sourceTag = r.scope === 'project' ? chalk.blue('[project]')
608
+ : r.scope === 'user' ? chalk.cyan('[user]')
609
+ : chalk.gray('[system]');
610
+ display += ` ${sourceTag}`;
611
+ const syncStr = r.syncState ? chalk.gray(` [${r.syncState}]`) : '';
612
+ console.log(` ${display}${syncStr}`);
613
+ // Show subrules for user-scope rules (the compiled CLAUDE.md)
614
+ if (r.scope === 'user' && composedSubrules.length > 0) {
615
+ for (const sub of composedSubrules) {
616
+ const scopeLabel = sub.layerScope === 'project' ? chalk.blue('[project]')
617
+ : sub.layerScope === 'user' ? chalk.cyan('[user]')
618
+ : sub.layerScope === 'extra' ? chalk.magenta(`[${sub.layerAlias || 'extra'}]`)
619
+ : chalk.gray('[system]');
620
+ const linkedSubName = termLink(sub.name, sub.sourcePath);
621
+ console.log(` ${chalk.gray('-')} ${linkedSubName} ${scopeLabel}`);
622
+ }
623
+ }
624
+ }
625
+ }
626
+ renderRulesSection();
569
627
  renderSection('Hooks', agentData.hooks);
570
628
  renderPromptcuts();
571
629
  // Show legend at the end if git repo exists
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Workflow management commands.
3
+ *
4
+ * Implements `agents workflows` — list, view, add, remove pipeline workflows
5
+ * (WORKFLOW.md bundles with optional subagents/, skills/, plugins/ subdirs).
6
+ * Run a workflow with: agents run <workflow-name>
7
+ */
8
+ import type { Command } from 'commander';
9
+ /** Register the `agents workflows` command tree (list, view, add, remove). */
10
+ export declare function registerWorkflowsCommands(program: Command): void;
@@ -0,0 +1,457 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import * as fs from 'fs';
4
+ import * as os from 'os';
5
+ import * as path from 'path';
6
+ import { select, checkbox } from '@inquirer/prompts';
7
+ import { resolveAgentName, agentLabel } from '../lib/agents.js';
8
+ import { cloneRepo } from '../lib/git.js';
9
+ import { WORKFLOW_CAPABLE_AGENTS, discoverWorkflowsFromRepo, installWorkflowCentrally, removeWorkflow, listInstalledWorkflows, listWorkflowsForAgent, removeWorkflowFromVersion, iterWorkflowsCapableVersions, } from '../lib/workflows.js';
10
+ import { getVersionHomePath, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, resolveAgentVersionTargets, } from '../lib/versions.js';
11
+ import { recordVersionResources, getUserWorkflowsDir } from '../lib/state.js';
12
+ import { isPromptCancelled, isInteractiveTerminal, requireInteractiveSelection, printWithPager, promptRemovalTargets, } from './utils.js';
13
+ import { showResourceList, buildTargetsSection, } from './resource-view.js';
14
+ /** Register the `agents workflows` command tree (list, view, add, remove). */
15
+ export function registerWorkflowsCommands(program) {
16
+ const workflowsCmd = program
17
+ .command('workflows')
18
+ .description('Manage multi-agent pipeline workflows (WORKFLOW.md bundles)')
19
+ .addHelpText('after', `
20
+ Workflows are directory bundles (WORKFLOW.md + subagents/) that define multi-agent pipelines run via:
21
+ agents run <workflow-name>
22
+
23
+ Examples:
24
+ # See what workflows are available
25
+ agents workflows list
26
+
27
+ # Install from GitHub
28
+ agents workflows add gh:user/workflows
29
+
30
+ # Install a local workflow directory
31
+ agents workflows add ./rdev
32
+
33
+ # Remove a workflow
34
+ agents workflows remove rdev
35
+ `);
36
+ workflowsCmd
37
+ .command('list [agent]')
38
+ .description('Show installed workflows and which agent versions they are synced to')
39
+ .option('-a, --agent <agent>', 'Filter to a specific agent')
40
+ .action(async (agentArg, options) => {
41
+ const spinner = ora({ text: 'Loading...', isSilent: !process.stdout.isTTY }).start();
42
+ const agentInput = agentArg || options.agent;
43
+ let filterAgent;
44
+ let filterVersion;
45
+ if (agentInput) {
46
+ const parts = agentInput.split('@');
47
+ const resolved = resolveAgentName(parts[0]);
48
+ if (!resolved) {
49
+ spinner.stop();
50
+ console.log(chalk.red(`Unknown agent: ${parts[0]}`));
51
+ process.exit(1);
52
+ }
53
+ filterAgent = resolved;
54
+ filterVersion = parts[1] ? resolveVersionAlias(resolved, parts[1]) : undefined;
55
+ }
56
+ const rows = buildWorkflowRows({ filterAgent, filterVersion });
57
+ spinner.stop();
58
+ await showResourceList({
59
+ resourcePlural: 'workflows',
60
+ resourceSingular: 'workflow',
61
+ extraLabel: 'Agents',
62
+ rows,
63
+ emptyMessage: filterAgent
64
+ ? `No workflows in central storage for ${agentLabel(filterAgent)}.`
65
+ : 'No workflows in ~/.agents/workflows/. Add one with: agents workflows add gh:user/repo',
66
+ centralPath: getUserWorkflowsDir(),
67
+ filterAgent,
68
+ filterVersion,
69
+ });
70
+ });
71
+ workflowsCmd
72
+ .command('add [source]')
73
+ .description('Install workflows from a source (GitHub, local) or pick from central storage')
74
+ .option('-a, --agents <list>', 'Targets: claude, claude@2.1.138')
75
+ .option('-y, --yes', 'Skip confirmation prompts')
76
+ .addHelpText('after', `
77
+ Examples:
78
+ # Install from GitHub
79
+ agents workflows add gh:user/workflows
80
+
81
+ # Install a local workflow directory (must contain WORKFLOW.md)
82
+ agents workflows add ./rdev
83
+
84
+ # Install and sync to a specific version
85
+ agents workflows add gh:user/workflows --agents claude@2.1.138
86
+ `)
87
+ .action(async (source, options) => {
88
+ try {
89
+ let workflows;
90
+ if (!source) {
91
+ // Interactive: pick from central storage
92
+ const installed = listInstalledWorkflows();
93
+ if (installed.size === 0) {
94
+ console.log(chalk.yellow('No workflows in ~/.agents/workflows/'));
95
+ console.log(chalk.gray('\nTo add workflows from a repo:'));
96
+ console.log(chalk.cyan(' agents workflows add gh:user/repo'));
97
+ return;
98
+ }
99
+ if (!isInteractiveTerminal()) {
100
+ requireInteractiveSelection('Selecting workflows', [
101
+ 'agents workflows add gh:user/repo',
102
+ ]);
103
+ }
104
+ const choices = Array.from(installed.values()).map(w => ({
105
+ value: w.name,
106
+ name: w.frontmatter.description
107
+ ? `${w.name} ${chalk.gray(w.frontmatter.description.slice(0, 50))}`
108
+ : w.name,
109
+ }));
110
+ const selected = await checkbox({ message: 'Select workflows to sync', choices });
111
+ if (selected.length === 0) {
112
+ console.log(chalk.gray('No workflows selected.'));
113
+ return;
114
+ }
115
+ workflows = selected.map(name => ({ name, path: installed.get(name).path }));
116
+ }
117
+ else {
118
+ // Fetch from repo or local path
119
+ const spinner = ora('Fetching workflows...').start();
120
+ const isGitRepo = source.startsWith('gh:') || source.startsWith('git:') ||
121
+ source.startsWith('https://') || source.startsWith('http://');
122
+ let localPath;
123
+ if (isGitRepo) {
124
+ const result = await cloneRepo(source);
125
+ localPath = result.localPath;
126
+ spinner.succeed('Repository cloned');
127
+ }
128
+ else {
129
+ localPath = source.startsWith('~')
130
+ ? path.join(os.homedir(), source.slice(1))
131
+ : path.resolve(source);
132
+ if (!fs.existsSync(localPath)) {
133
+ spinner.fail(`Path not found: ${localPath}`);
134
+ return;
135
+ }
136
+ spinner.succeed('Using local path');
137
+ }
138
+ const discovered = discoverWorkflowsFromRepo(localPath);
139
+ if (discovered.length === 0) {
140
+ console.log(chalk.yellow('No workflows found (looking for WORKFLOW.md files)'));
141
+ return;
142
+ }
143
+ console.log(chalk.bold(`\nFound ${discovered.length} workflow(s):`));
144
+ for (const w of discovered) {
145
+ console.log(`\n ${chalk.cyan(w.name)}: ${w.frontmatter.description || 'no description'}`);
146
+ if (w.subagentCount > 0) {
147
+ console.log(` ${chalk.gray(`${w.subagentCount} subagent${w.subagentCount === 1 ? '' : 's'}`)}`);
148
+ }
149
+ }
150
+ const installSpinner = ora('Installing to central storage...').start();
151
+ let installed = 0;
152
+ for (const w of discovered) {
153
+ const result = installWorkflowCentrally(w.path, w.name);
154
+ if (result.success) {
155
+ installed++;
156
+ }
157
+ else {
158
+ installSpinner.stop();
159
+ console.log(chalk.red(`\n Failed to install ${w.name}: ${result.error}`));
160
+ installSpinner.start();
161
+ }
162
+ }
163
+ installSpinner.succeed(`Installed ${installed} workflow(s) to ~/.agents/workflows/`);
164
+ workflows = discovered.map(w => ({
165
+ name: w.name,
166
+ path: path.join(getUserWorkflowsDir(), w.name),
167
+ }));
168
+ }
169
+ // Agent/version selection
170
+ let selectedAgents;
171
+ let versionSelections;
172
+ if (options.agents) {
173
+ const result = resolveAgentVersionTargets(options.agents, WORKFLOW_CAPABLE_AGENTS);
174
+ selectedAgents = result.selectedAgents;
175
+ versionSelections = result.versionSelections;
176
+ }
177
+ else {
178
+ const result = await promptAgentVersionSelection(WORKFLOW_CAPABLE_AGENTS, {
179
+ skipPrompts: options.yes || !isInteractiveTerminal(),
180
+ });
181
+ selectedAgents = result.selectedAgents;
182
+ versionSelections = result.versionSelections;
183
+ }
184
+ if (selectedAgents.length === 0) {
185
+ console.log(chalk.yellow('\nNo agents selected.'));
186
+ return;
187
+ }
188
+ const syncSpinner = ora('Syncing to agent versions...').start();
189
+ let synced = 0;
190
+ const workflowNames = workflows.map(w => w.name);
191
+ for (const [agentId, versions] of versionSelections) {
192
+ for (const version of versions) {
193
+ syncResourcesToVersion(agentId, version);
194
+ recordVersionResources(agentId, version, 'workflows', workflowNames);
195
+ synced++;
196
+ }
197
+ }
198
+ if (synced > 0) {
199
+ syncSpinner.succeed(`Synced to ${synced} agent version(s)`);
200
+ }
201
+ else {
202
+ syncSpinner.info('No version-managed agents to sync');
203
+ }
204
+ console.log(chalk.green('\nWorkflows installed.'));
205
+ console.log(chalk.gray('Run with: agents run <workflow-name>'));
206
+ }
207
+ catch (err) {
208
+ if (isPromptCancelled(err)) {
209
+ console.log(chalk.gray('\nCancelled'));
210
+ return;
211
+ }
212
+ console.error(chalk.red('Failed to add workflows'));
213
+ console.error(chalk.red(err.message));
214
+ process.exit(1);
215
+ }
216
+ });
217
+ workflowsCmd
218
+ .command('remove [name]')
219
+ .description('Remove a workflow from version homes (interactive picker if no name given)')
220
+ .addHelpText('after', `
221
+ Examples:
222
+ # Remove a workflow by name
223
+ agents workflows remove rdev
224
+
225
+ # Interactive picker
226
+ agents workflows remove
227
+ `)
228
+ .action(async (name) => {
229
+ const workflowTargetMap = new Map();
230
+ for (const { agent, version } of iterWorkflowsCapableVersions()) {
231
+ const home = getVersionHomePath(agent, version);
232
+ for (const n of listWorkflowsForAgent(agent, home)) {
233
+ const existing = workflowTargetMap.get(n);
234
+ if (existing) {
235
+ existing.targets.push({ agent, version });
236
+ }
237
+ else {
238
+ workflowTargetMap.set(n, { name: n, targets: [{ agent, version }] });
239
+ }
240
+ }
241
+ }
242
+ let toRemove;
243
+ if (name) {
244
+ toRemove = [name];
245
+ }
246
+ else {
247
+ if (workflowTargetMap.size === 0) {
248
+ console.log(chalk.yellow('No workflows synced to any version.'));
249
+ return;
250
+ }
251
+ if (!isInteractiveTerminal()) {
252
+ requireInteractiveSelection('Selecting workflows to remove', [
253
+ 'agents workflows remove rdev',
254
+ ]);
255
+ }
256
+ try {
257
+ const selected = await checkbox({
258
+ message: 'Select workflows to remove',
259
+ choices: Array.from(workflowTargetMap.values()).map(w => ({
260
+ value: w.name,
261
+ name: w.name,
262
+ })),
263
+ });
264
+ if (selected.length === 0) {
265
+ console.log(chalk.gray('No workflows selected.'));
266
+ return;
267
+ }
268
+ toRemove = selected;
269
+ }
270
+ catch (err) {
271
+ if (isPromptCancelled(err)) {
272
+ console.log(chalk.gray('Cancelled'));
273
+ return;
274
+ }
275
+ throw err;
276
+ }
277
+ }
278
+ let removed = 0;
279
+ for (const workflowName of toRemove) {
280
+ const info = workflowTargetMap.get(workflowName);
281
+ if (!info || info.targets.length === 0) {
282
+ // Not synced to any version — try removing from central storage directly
283
+ const result = removeWorkflow(workflowName);
284
+ if (result.success) {
285
+ console.log(` ${chalk.red('-')} ${workflowName}: removed from central storage`);
286
+ removed++;
287
+ }
288
+ else {
289
+ console.log(chalk.yellow(` Workflow '${workflowName}' not found.`));
290
+ }
291
+ continue;
292
+ }
293
+ const removalTargets = info.targets.map(t => ({
294
+ agent: t.agent,
295
+ version: t.version,
296
+ label: `${agentLabel(t.agent)}@${t.version}`,
297
+ }));
298
+ const selectedTargets = await promptRemovalTargets(workflowName, removalTargets);
299
+ if (selectedTargets.length === 0) {
300
+ console.log(chalk.gray(` Skipped '${workflowName}'.`));
301
+ continue;
302
+ }
303
+ for (const target of selectedTargets) {
304
+ const result = removeWorkflowFromVersion(target.agent, target.version, workflowName);
305
+ if (result.success) {
306
+ console.log(` ${chalk.red('-')} ${target.label}: ${workflowName}`);
307
+ removed++;
308
+ }
309
+ else if (result.error) {
310
+ console.log(` ${chalk.yellow('!')} ${target.label}: ${result.error}`);
311
+ }
312
+ }
313
+ }
314
+ if (removed === 0) {
315
+ console.log(chalk.yellow('No workflows removed.'));
316
+ }
317
+ else {
318
+ console.log(chalk.green(`\nRemoved ${removed} workflow(s) from version homes.`));
319
+ console.log(chalk.gray('Central source unchanged. Use "agents workflows remove <name>" again to remove from ~/.agents/workflows/.'));
320
+ }
321
+ });
322
+ workflowsCmd
323
+ .command('view [name]')
324
+ .description('Read workflow details (description, subagents, model, MCP)')
325
+ .addHelpText('after', `
326
+ Examples:
327
+ # View a specific workflow
328
+ agents workflows view rdev
329
+
330
+ # Interactive picker
331
+ agents workflows view
332
+ `)
333
+ .action(async (name) => {
334
+ const installed = listInstalledWorkflows();
335
+ if (!name) {
336
+ if (installed.size === 0) {
337
+ console.log(chalk.yellow('No workflows installed'));
338
+ return;
339
+ }
340
+ if (!isInteractiveTerminal()) {
341
+ requireInteractiveSelection('Selecting a workflow to view', [
342
+ 'agents workflows view rdev',
343
+ ]);
344
+ }
345
+ try {
346
+ name = await select({
347
+ message: 'Select a workflow to view',
348
+ choices: Array.from(installed.values()).map(w => ({
349
+ value: w.name,
350
+ name: w.frontmatter.description
351
+ ? `${w.name} - ${w.frontmatter.description}`
352
+ : w.name,
353
+ })),
354
+ });
355
+ }
356
+ catch (err) {
357
+ if (isPromptCancelled(err)) {
358
+ console.log(chalk.gray('Cancelled'));
359
+ return;
360
+ }
361
+ throw err;
362
+ }
363
+ }
364
+ const workflow = installed.get(name);
365
+ if (!workflow) {
366
+ console.log(chalk.yellow(`Workflow '${name}' not found`));
367
+ return;
368
+ }
369
+ const lines = [];
370
+ const fm = workflow.frontmatter;
371
+ lines.push(chalk.bold(`\n${fm.name || workflow.name}\n`));
372
+ if (fm.description)
373
+ lines.push(` ${fm.description}`);
374
+ lines.push('');
375
+ if (fm.model)
376
+ lines.push(` Model: ${fm.model}`);
377
+ if (fm.tools?.length)
378
+ lines.push(` Tools: ${fm.tools.join(', ')}`);
379
+ if (fm.mcpServers?.length)
380
+ lines.push(` MCP: ${fm.mcpServers.join(', ')}`);
381
+ if (fm.skills?.length)
382
+ lines.push(` Skills: ${fm.skills.join(', ')}`);
383
+ lines.push(` Path: ${workflow.path}`);
384
+ if (fm.allowedAgents?.length) {
385
+ lines.push(chalk.bold(`\n Subagents (${workflow.subagentCount}):`));
386
+ for (const a of fm.allowedAgents) {
387
+ lines.push(` ${chalk.cyan(a)}`);
388
+ }
389
+ }
390
+ lines.push('');
391
+ printWithPager(lines.join('\n'), lines.length);
392
+ });
393
+ }
394
+ function buildWorkflowRows(opts) {
395
+ const central = listInstalledWorkflows();
396
+ if (central.size === 0)
397
+ return [];
398
+ const targetPairs = iterWorkflowsCapableVersions({
399
+ agent: opts.filterAgent,
400
+ version: opts.filterVersion,
401
+ });
402
+ const syncedByTarget = new Map();
403
+ const defaultByAgent = new Map();
404
+ for (const { agent, version } of targetPairs) {
405
+ if (!defaultByAgent.has(agent))
406
+ defaultByAgent.set(agent, getGlobalDefault(agent));
407
+ const home = getVersionHomePath(agent, version);
408
+ syncedByTarget.set(`${agent}@${version}`, new Set(listWorkflowsForAgent(agent, home)));
409
+ }
410
+ const rows = [];
411
+ for (const [name, workflow] of central) {
412
+ const targets = targetPairs.map(({ agent, version }) => ({
413
+ agent,
414
+ version,
415
+ isDefault: defaultByAgent.get(agent) === version,
416
+ status: syncedByTarget.get(`${agent}@${version}`)?.has(name) ? 'synced' : 'missing',
417
+ }));
418
+ rows.push({
419
+ name,
420
+ description: workflow.frontmatter.description,
421
+ extra: workflow.subagentCount > 0 ? `${workflow.subagentCount}` : '-',
422
+ targets,
423
+ buildDetail: () => formatWorkflowDetail(workflow, targets),
424
+ });
425
+ }
426
+ rows.sort((a, b) => {
427
+ const aSynced = a.targets.filter(t => t.status === 'synced').length;
428
+ const bSynced = b.targets.filter(t => t.status === 'synced').length;
429
+ if (aSynced !== bSynced)
430
+ return bSynced - aSynced;
431
+ return a.name.localeCompare(b.name);
432
+ });
433
+ return rows;
434
+ }
435
+ function formatWorkflowDetail(workflow, targets) {
436
+ const lines = [];
437
+ const fm = workflow.frontmatter;
438
+ lines.push(chalk.bold.cyan(workflow.name));
439
+ if (fm.description)
440
+ lines.push(chalk.gray(fm.description));
441
+ lines.push('');
442
+ const meta = [];
443
+ if (fm.model)
444
+ meta.push(`model ${chalk.white(fm.model)}`);
445
+ if (fm.mcpServers?.length)
446
+ meta.push(`${chalk.white(fm.mcpServers.length)} MCP`);
447
+ if (fm.skills?.length)
448
+ meta.push(`${chalk.white(fm.skills.length)} skill${fm.skills.length === 1 ? '' : 's'}`);
449
+ meta.push(`${chalk.white(workflow.subagentCount)} subagent${workflow.subagentCount === 1 ? '' : 's'}`);
450
+ if (meta.length)
451
+ lines.push(' ' + meta.join(chalk.gray(' · ')));
452
+ lines.push(' ' + chalk.gray(workflow.path));
453
+ lines.push('');
454
+ lines.push(chalk.bold(' Synced to'));
455
+ lines.push(buildTargetsSection(targets));
456
+ return lines.join('\n');
457
+ }
package/dist/index.d.ts CHANGED
@@ -3,6 +3,6 @@
3
3
  * CLI entry point for agents-cli.
4
4
  *
5
5
  * Registers all commands, handles update checks, auto-corrects typos,
6
- * and launches the first-run interactive init when appropriate.
6
+ * and launches the first-run interactive setup when appropriate.
7
7
  */
8
8
  export {};