agentinit 1.17.1 → 1.18.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.
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../src/commands/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4BpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8Z7D"}
1
+ {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../src/commands/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2b7D"}
@@ -4,6 +4,7 @@ import { homedir } from 'os';
4
4
  import { dirname, relative, resolve } from 'path';
5
5
  import { green, dim, bold, cyan, yellow, orange } from '../utils/colors.js';
6
6
  import { logger } from '../utils/logger.js';
7
+ import { promptMultiselect, selectBundlePlugins } from '../utils/promptUtils.js';
7
8
  import { MultipleBundlePluginsError, PluginManager } from '../core/pluginManager.js';
8
9
  import { AgentManager } from '../core/agentManager.js';
9
10
  import { getConfiguredDefaultMarketplaceId, getMarketplaceCategories } from '../core/marketplaceRegistry.js';
@@ -21,6 +22,7 @@ export function registerPluginsCommand(program) {
21
22
  .option('-a, --agent <agents...>', 'Target specific agent(s)')
22
23
  .option('-g, --global', 'Install globally')
23
24
  .option('--copy-skills', 'Copy plugin skills instead of using canonical symlink installs')
25
+ .option('--all', 'Select all bundled plugins when the source contains multiple plugins')
24
26
  .option('-l, --list', 'Preview plugin contents without installing')
25
27
  .option('-y, --yes', 'Skip confirmation prompts, auto-detect project-configured agents')
26
28
  .action(async (source, options) => {
@@ -61,43 +63,45 @@ export function registerPluginsCommand(program) {
61
63
  catch (error) {
62
64
  if (error instanceof MultipleBundlePluginsError) {
63
65
  spinner.stop();
64
- const selected = await selectBundlePlugin(error, 'preview');
66
+ const selected = await selectBundlePlugins(error.entries, 'preview', { selectAll: options.all });
65
67
  if (!selected) {
66
68
  return;
67
69
  }
68
- const retrySpinner = ora('Fetching plugin...').start();
69
- try {
70
- const preview = await pluginManager.inspectPlugin(source, {
71
- from: options.from,
72
- pluginName: selected,
73
- });
74
- retrySpinner.stop();
75
- const p = preview.plugin;
76
- console.log('');
77
- logger.info(`${bold(p.name)} ${dim(`v${p.version}`)} ${dim(`[${p.format} format]`)}`);
78
- if (p.description)
79
- logger.info(` ${p.description}`);
80
- console.log('');
81
- if (p.skills.length > 0) {
82
- logger.info(` ${green('Skills')} (${p.skills.length}):`);
83
- for (const skill of p.skills) {
84
- logger.info(` ${green(skill.name)} - ${skill.description}`);
70
+ for (const pluginName of selected) {
71
+ const retrySpinner = ora(`Fetching plugin ${pluginName}...`).start();
72
+ try {
73
+ const preview = await pluginManager.inspectPlugin(source, {
74
+ from: options.from,
75
+ pluginName,
76
+ });
77
+ retrySpinner.stop();
78
+ const p = preview.plugin;
79
+ console.log('');
80
+ logger.info(`${bold(p.name)} ${dim(`v${p.version}`)} ${dim(`[${p.format} format]`)}`);
81
+ if (p.description)
82
+ logger.info(` ${p.description}`);
83
+ console.log('');
84
+ if (p.skills.length > 0) {
85
+ logger.info(` ${green('Skills')} (${p.skills.length}):`);
86
+ for (const skill of p.skills) {
87
+ logger.info(` ${green(skill.name)} - ${skill.description}`);
88
+ }
85
89
  }
86
- }
87
- if (p.mcpServers.length > 0) {
88
- logger.info(` ${cyan('MCP Servers')} (${p.mcpServers.length}):`);
89
- for (const mcp of p.mcpServers) {
90
- logger.info(` ${cyan(mcp.name)} [${mcp.type}]`);
90
+ if (p.mcpServers.length > 0) {
91
+ logger.info(` ${cyan('MCP Servers')} (${p.mcpServers.length}):`);
92
+ for (const mcp of p.mcpServers) {
93
+ logger.info(` ${cyan(mcp.name)} [${mcp.type}]`);
94
+ }
95
+ }
96
+ if (p.skills.length === 0 && p.mcpServers.length === 0) {
97
+ logger.info(' No portable components found (no skills or MCP servers).');
91
98
  }
99
+ renderPluginWarnings(preview, process.cwd());
92
100
  }
93
- if (p.skills.length === 0 && p.mcpServers.length === 0) {
94
- logger.info(' No portable components found (no skills or MCP servers).');
101
+ catch (retryError) {
102
+ retrySpinner.fail(`Failed to fetch plugin ${pluginName}`);
103
+ logger.error(`Error: ${retryError instanceof Error ? retryError.message : 'Unknown error'}`);
95
104
  }
96
- renderPluginWarnings(preview, process.cwd());
97
- }
98
- catch (retryError) {
99
- retrySpinner.fail('Failed to fetch plugin');
100
- logger.error(`Error: ${retryError instanceof Error ? retryError.message : 'Unknown error'}`);
101
105
  }
102
106
  }
103
107
  else {
@@ -110,35 +114,47 @@ export function registerPluginsCommand(program) {
110
114
  // Interactive agent selection if not --yes and not --agent
111
115
  let agentIds = options.agent;
112
116
  let targetGlobal = options.global;
113
- let preview = null;
117
+ let selectionPreview = null;
114
118
  let previewRendered = false;
115
- let selectedPluginName;
119
+ let selectedPluginNames;
116
120
  if (!agentIds && !options.yes) {
117
121
  const previewSpinner = ora('Inspecting plugin...').start();
118
122
  try {
119
- preview = await pluginManager.preparePluginInstall(source, {
123
+ const preview = await pluginManager.preparePluginInstall(source, {
120
124
  from: options.from,
121
125
  });
122
126
  previewSpinner.stop();
123
- renderPluginWarnings(preview, process.cwd());
127
+ selectionPreview = buildPluginSelectionPreview([preview]);
128
+ renderPreparedPluginWarnings([preview], process.cwd());
124
129
  previewRendered = true;
125
130
  }
126
131
  catch (error) {
127
132
  if (error instanceof MultipleBundlePluginsError) {
128
133
  previewSpinner.stop();
129
- const selected = await selectBundlePlugin(error, 'install');
134
+ const selected = await selectBundlePlugins(error.entries, 'install', { selectAll: options.all });
130
135
  if (!selected) {
131
136
  return;
132
137
  }
133
- selectedPluginName = selected;
138
+ selectedPluginNames = selected;
134
139
  const retrySpinner = ora('Inspecting plugin...').start();
135
140
  try {
136
- preview = await pluginManager.preparePluginInstall(source, {
137
- from: options.from,
138
- pluginName: selectedPluginName,
139
- });
141
+ const previewsByPlugin = new Map();
142
+ const previewOrder = selectedPluginNames.length > 1
143
+ ? [...selectedPluginNames.slice(1), selectedPluginNames[0]]
144
+ : selectedPluginNames;
145
+ for (const pluginName of previewOrder) {
146
+ const preparedPreview = await pluginManager.preparePluginInstall(source, {
147
+ from: options.from,
148
+ pluginName,
149
+ });
150
+ previewsByPlugin.set(pluginName, preparedPreview);
151
+ }
152
+ const previews = selectedPluginNames
153
+ .map(pluginName => previewsByPlugin.get(pluginName))
154
+ .filter((value) => !!value);
140
155
  retrySpinner.stop();
141
- renderPluginWarnings(preview, process.cwd());
156
+ selectionPreview = buildPluginSelectionPreview(previews);
157
+ renderPreparedPluginWarnings(previews, process.cwd());
142
158
  previewRendered = true;
143
159
  }
144
160
  catch (retryError) {
@@ -154,7 +170,7 @@ export function registerPluginsCommand(program) {
154
170
  }
155
171
  }
156
172
  try {
157
- const selection = await interactiveAgentSelect(pluginManager, agentManager, process.cwd(), targetGlobal, preview);
173
+ const selection = await interactiveAgentSelect(pluginManager, agentManager, process.cwd(), targetGlobal, selectionPreview);
158
174
  if (!selection || selection.aborted || !selection.agents || selection.agents.length === 0) {
159
175
  await pluginManager.discardPreparedPlugin(source, { from: options.from });
160
176
  logger.info('No agents selected. Aborting.');
@@ -172,87 +188,98 @@ export function registerPluginsCommand(program) {
172
188
  }
173
189
  }
174
190
  // Install
175
- const spinner = ora('Installing plugin...').start();
176
- try {
177
- const result = await pluginManager.installPlugin(source, process.cwd(), {
178
- from: options.from,
179
- agents: agentIds,
180
- global: targetGlobal,
181
- copySkills: options.copySkills,
182
- yes: options.yes,
183
- ...(selectedPluginName ? { pluginName: selectedPluginName } : {}),
184
- });
185
- const p = result.plugin;
186
- const totalSkills = result.skills.installed.length;
187
- const totalMcp = result.mcpServers.applied.length;
188
- const totalNative = result.nativePlugins.installed.length;
189
- if (totalSkills === 0 && totalMcp === 0 && totalNative === 0) {
190
- spinner.warn(`Plugin "${p.name}" has no portable components to install.`);
191
- if (!previewRendered) {
192
- renderPluginWarnings(result, process.cwd());
191
+ const pluginsToInstall = selectedPluginNames || [undefined];
192
+ for (const pluginName of pluginsToInstall) {
193
+ const spinner = ora(pluginName ? `Installing plugin ${pluginName}...` : 'Installing plugin...').start();
194
+ try {
195
+ if (pluginName && pluginName !== selectedPluginNames?.[0]) {
196
+ await pluginManager.preparePluginInstall(source, {
197
+ from: options.from,
198
+ pluginName,
199
+ });
193
200
  }
194
- return;
195
- }
196
- spinner.succeed(`Installed plugin ${green(bold(p.name))} ${dim(`v${p.version}`)}`);
197
- renderInstalledComponents(result, agentManager, process.cwd());
198
- // Skipped
199
- if (result.skills.skipped.length > 0 || result.mcpServers.skipped.length > 0 || result.nativePlugins.skipped.length > 0) {
200
- console.log('');
201
- for (const s of result.skills.skipped) {
202
- logger.debug(`Skipped skill ${s.name}: ${s.reason}`);
201
+ const result = await pluginManager.installPlugin(source, process.cwd(), {
202
+ from: options.from,
203
+ agents: agentIds,
204
+ global: targetGlobal,
205
+ copySkills: options.copySkills,
206
+ yes: options.yes,
207
+ ...(pluginName ? { pluginName } : {}),
208
+ });
209
+ const p = result.plugin;
210
+ const totalSkills = result.skills.installed.length;
211
+ const totalMcp = result.mcpServers.applied.length;
212
+ const totalNative = result.nativePlugins.installed.length;
213
+ if (totalSkills === 0 && totalMcp === 0 && totalNative === 0) {
214
+ spinner.warn(`Plugin "${p.name}" has no portable components to install.`);
215
+ if (!previewRendered) {
216
+ renderPluginWarnings(result, process.cwd());
217
+ }
218
+ continue;
203
219
  }
204
- for (const s of result.mcpServers.skipped) {
205
- logger.debug(`Skipped MCP ${s.name}: ${s.reason}`);
220
+ spinner.succeed(`Installed plugin ${green(bold(p.name))} ${dim(`v${p.version}`)}`);
221
+ renderInstalledComponents(result, agentManager, process.cwd());
222
+ // Skipped
223
+ if (result.skills.skipped.length > 0 || result.mcpServers.skipped.length > 0 || result.nativePlugins.skipped.length > 0) {
224
+ console.log('');
225
+ for (const s of result.skills.skipped) {
226
+ logger.debug(`Skipped skill ${s.name}: ${s.reason}`);
227
+ }
228
+ for (const s of result.mcpServers.skipped) {
229
+ logger.debug(`Skipped MCP ${s.name}: ${s.reason}`);
230
+ }
231
+ for (const s of result.nativePlugins.skipped) {
232
+ logger.warn(`Skipped native plugin payload for ${s.agent}: ${s.reason}`);
233
+ }
206
234
  }
207
- for (const s of result.nativePlugins.skipped) {
208
- logger.warn(`Skipped native plugin payload for ${s.agent}: ${s.reason}`);
235
+ if (!previewRendered) {
236
+ renderPluginWarnings(result, process.cwd());
209
237
  }
238
+ logger.success('Plugin installation complete.');
210
239
  }
211
- if (!previewRendered) {
212
- renderPluginWarnings(result, process.cwd());
213
- }
214
- logger.success('Plugin installation complete.');
215
- }
216
- catch (error) {
217
- if (error instanceof MultipleBundlePluginsError && !options.yes) {
218
- spinner.stop();
219
- const selected = await selectBundlePlugin(error, 'install');
220
- if (!selected) {
221
- return;
222
- }
223
- const retrySpinner = ora('Installing plugin...').start();
224
- try {
225
- const result = await pluginManager.installPlugin(source, process.cwd(), {
226
- from: options.from,
227
- agents: agentIds,
228
- global: targetGlobal,
229
- copySkills: options.copySkills,
230
- yes: options.yes,
231
- pluginName: selected,
232
- });
233
- const p = result.plugin;
234
- const totalSkills = result.skills.installed.length;
235
- const totalMcp = result.mcpServers.applied.length;
236
- const totalNative = result.nativePlugins.installed.length;
237
- if (totalSkills === 0 && totalMcp === 0 && totalNative === 0) {
238
- retrySpinner.warn(`Plugin "${p.name}" has no portable components to install.`);
239
- renderPluginWarnings(result, process.cwd());
240
+ catch (error) {
241
+ if (error instanceof MultipleBundlePluginsError && (options.all || !options.yes)) {
242
+ spinner.stop();
243
+ const selected = await selectBundlePlugins(error.entries, 'install', { selectAll: options.all });
244
+ if (!selected) {
240
245
  return;
241
246
  }
242
- retrySpinner.succeed(`Installed plugin ${green(bold(p.name))} ${dim(`v${p.version}`)}`);
243
- renderInstalledComponents(result, agentManager, process.cwd());
244
- renderPluginWarnings(result, process.cwd());
245
- logger.success('Plugin installation complete.');
247
+ for (const selectedName of selected) {
248
+ const retrySpinner = ora(`Installing plugin ${selectedName}...`).start();
249
+ try {
250
+ const result = await pluginManager.installPlugin(source, process.cwd(), {
251
+ from: options.from,
252
+ agents: agentIds,
253
+ global: targetGlobal,
254
+ copySkills: options.copySkills,
255
+ yes: options.yes,
256
+ pluginName: selectedName,
257
+ });
258
+ const p = result.plugin;
259
+ const totalSkills = result.skills.installed.length;
260
+ const totalMcp = result.mcpServers.applied.length;
261
+ const totalNative = result.nativePlugins.installed.length;
262
+ if (totalSkills === 0 && totalMcp === 0 && totalNative === 0) {
263
+ retrySpinner.warn(`Plugin "${p.name}" has no portable components to install.`);
264
+ renderPluginWarnings(result, process.cwd());
265
+ continue;
266
+ }
267
+ retrySpinner.succeed(`Installed plugin ${green(bold(p.name))} ${dim(`v${p.version}`)}`);
268
+ renderInstalledComponents(result, agentManager, process.cwd());
269
+ renderPluginWarnings(result, process.cwd());
270
+ logger.success('Plugin installation complete.');
271
+ }
272
+ catch (retryError) {
273
+ retrySpinner.fail(`Failed to install plugin ${selectedName}`);
274
+ logger.error(`Error: ${retryError instanceof Error ? retryError.message : 'Unknown error'}`);
275
+ }
276
+ }
246
277
  }
247
- catch (retryError) {
248
- retrySpinner.fail('Failed to install plugin');
249
- logger.error(`Error: ${retryError instanceof Error ? retryError.message : 'Unknown error'}`);
278
+ else {
279
+ spinner.fail('Failed to install plugin');
280
+ logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
250
281
  }
251
282
  }
252
- else {
253
- spinner.fail('Failed to install plugin');
254
- logger.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
255
- }
256
283
  }
257
284
  });
258
285
  // --- plugins search [query] ---
@@ -383,22 +410,6 @@ export function registerPluginsCommand(program) {
383
410
  }
384
411
  });
385
412
  }
386
- async function selectBundlePlugin(error, actionLabel) {
387
- const response = await prompts({
388
- type: 'select',
389
- name: 'plugin',
390
- message: `This repository contains multiple plugins. Select one to ${actionLabel}:`,
391
- choices: error.entries.map(entry => ({
392
- title: entry.name,
393
- value: entry.name,
394
- })),
395
- });
396
- if (!response.plugin) {
397
- logger.info('Cancelled.');
398
- return null;
399
- }
400
- return response.plugin;
401
- }
402
413
  function formatPathForDisplay(pathValue, projectPath) {
403
414
  if (pathValue.startsWith(`${projectPath}/`)) {
404
415
  return relative(projectPath, pathValue) || '.';
@@ -414,16 +425,48 @@ function getAgentLabel(agentIds, agentManager) {
414
425
  .map(agentId => agentManager.getAgentById(agentId)?.name || agentId)
415
426
  .join(', ');
416
427
  }
428
+ function buildPluginSelectionPreview(previews) {
429
+ const nativeAgentIds = new Set();
430
+ const nativeFeatures = new Set();
431
+ const nativeInstallPaths = new Set();
432
+ for (const preview of previews) {
433
+ if (!preview.nativePreview) {
434
+ continue;
435
+ }
436
+ nativeAgentIds.add(preview.nativePreview.agent);
437
+ nativeInstallPaths.add(preview.nativePreview.installPath);
438
+ for (const feature of preview.nativePreview.features) {
439
+ nativeFeatures.add(feature);
440
+ }
441
+ }
442
+ return {
443
+ selectedCount: previews.length,
444
+ skillCount: previews.reduce((total, preview) => total + preview.plugin.skills.length, 0),
445
+ mcpServerCount: previews.reduce((total, preview) => total + preview.plugin.mcpServers.length, 0),
446
+ nativeAgentIds: [...nativeAgentIds],
447
+ nativeFeatures: [...nativeFeatures],
448
+ nativeInstallPaths: [...nativeInstallPaths],
449
+ };
450
+ }
417
451
  function getPortableComponentSummary(preview) {
418
452
  const parts = [];
419
- if (preview.plugin.skills.length > 0) {
420
- parts.push(`${preview.plugin.skills.length} skill(s)`);
453
+ if (preview.skillCount > 0) {
454
+ parts.push(`${preview.skillCount} skill(s)`);
421
455
  }
422
- if (preview.plugin.mcpServers.length > 0) {
423
- parts.push(`${preview.plugin.mcpServers.length} MCP server(s)`);
456
+ if (preview.mcpServerCount > 0) {
457
+ parts.push(`${preview.mcpServerCount} MCP server(s)`);
424
458
  }
425
459
  return parts.length > 0 ? parts.join(', ') : 'No portable components';
426
460
  }
461
+ function renderPreparedPluginWarnings(previews, projectPath) {
462
+ for (const preview of previews) {
463
+ if (previews.length > 1) {
464
+ console.log('');
465
+ logger.info(`${bold(preview.plugin.name)} ${dim(`v${preview.plugin.version}`)}`);
466
+ }
467
+ renderPluginWarnings(preview, projectPath);
468
+ }
469
+ }
427
470
  function getSourceWarnings(warnings) {
428
471
  const items = [];
429
472
  for (const warning of warnings) {
@@ -691,36 +734,44 @@ function shouldPreselectPluginGroup(group, installGlobal, preview) {
691
734
  if (group.kind === 'canonical-shared') {
692
735
  return true;
693
736
  }
694
- if (preview.nativePreview) {
695
- return group.agents.some(agent => agent.id === preview.nativePreview?.agent);
737
+ if (preview.nativeAgentIds.length > 0) {
738
+ return group.agents.some(agent => preview.nativeAgentIds.includes(agent.id));
696
739
  }
697
740
  return false;
698
741
  }
699
742
  function getPluginGroupDescription(group, preview, projectPath) {
700
743
  const portableSummary = getPortableComponentSummary(preview);
701
- if (!preview.nativePreview) {
744
+ if (preview.nativeAgentIds.length === 0) {
702
745
  return portableSummary;
703
746
  }
704
- const containsClaudeCode = group.agents.some(agent => agent.id === 'claude');
747
+ const containsClaudeCode = group.agents.some(agent => preview.nativeAgentIds.includes(agent.id));
705
748
  if (!containsClaudeCode) {
706
- return `${portableSummary}. Skills will be installed here, but Claude-specific components will not be fully available for these agents.`;
749
+ return `${portableSummary}. Some selected plugins also include Claude-specific components that will not be fully available for these agents.`;
707
750
  }
708
751
  const otherAgents = group.agents
709
- .filter(agent => agent.id !== 'claude')
752
+ .filter(agent => !preview.nativeAgentIds.includes(agent.id))
710
753
  .map(agent => agent.name);
711
- const installPath = formatPathForDisplay(preview.nativePreview.installPath, projectPath);
754
+ const installPath = preview.nativeInstallPaths.length === 1
755
+ ? formatPathForDisplay(preview.nativeInstallPaths[0], projectPath)
756
+ : `~/.claude/plugins (${preview.nativeInstallPaths.length} plugin-specific install paths)`;
757
+ const nativeSummary = preview.nativeFeatures.length > 0
758
+ ? ` Native components: ${preview.nativeFeatures.join(', ')}.`
759
+ : '';
712
760
  if (otherAgents.length === 0) {
713
- return `${portableSummary}. Full plugin support is available in Claude Code; the native plugin installs at ${installPath}.`;
761
+ return `${portableSummary}. Full plugin support is available in Claude Code; native components install at ${installPath}.${nativeSummary}`;
714
762
  }
715
763
  const otherAgentsLabel = otherAgents.join(', ');
716
764
  const shareVerb = otherAgents.length === 1 ? 'shares' : 'share';
717
765
  const receiveVerb = otherAgents.length === 1 ? 'receives' : 'receive';
718
- return `${portableSummary}. Full plugin support is available in Claude Code; the native plugin installs at ${installPath}. ${otherAgentsLabel} ${shareVerb} this skills directory but only ${receiveVerb} the installed skills.`;
766
+ return `${portableSummary}. Full plugin support is available in Claude Code; native components install at ${installPath}.${nativeSummary} ${otherAgentsLabel} ${shareVerb} this skills directory but only ${receiveVerb} the installed skills.`;
719
767
  }
720
768
  /**
721
769
  * Interactive agent selection grouped by shared skills directory
722
770
  */
723
771
  async function interactiveAgentSelect(pluginManager, agentManager, projectPath, global, preview) {
772
+ if (!preview) {
773
+ return { aborted: true };
774
+ }
724
775
  let installGlobal = !!global;
725
776
  let groups = installGlobal
726
777
  ? buildGlobalPluginGroups(agentManager, projectPath)
@@ -756,13 +807,11 @@ async function interactiveAgentSelect(pluginManager, agentManager, projectPath,
756
807
  logger.warn('No supported agents expose a skills directory.');
757
808
  return { aborted: true };
758
809
  }
759
- const response = await prompts({
760
- type: 'multiselect',
810
+ const response = await promptMultiselect({
761
811
  name: 'groups',
762
812
  message: installGlobal
763
813
  ? 'Select which global agents should receive this plugin:'
764
814
  : 'Select which agents should receive this plugin:',
765
- instructions: false,
766
815
  min: 1,
767
816
  choices: groups.map(group => {
768
817
  const compatible = group.compatibleAgentNames.length > 0