agentinit 1.17.2 → 1.18.1

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## [1.18.1](https://github.com/agentinit/agentinit/compare/v1.18.0...v1.18.1) (2026-04-05)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **skills:** compare full skill payloads before updates ([4b1e27c](https://github.com/agentinit/agentinit/commit/4b1e27cc69264d46edb38c2f6435b7f2659cfdb3))
7
+
8
+ ## [Unreleased]
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **skills:** compare installed skill payloads before prompting for updates
14
+
15
+ # [1.18.0](https://github.com/agentinit/agentinit/compare/v1.17.2...v1.18.0) (2026-04-04)
16
+
17
+
18
+ ### Features
19
+
20
+ * **cli:** support selecting all bundled plugins ([2e533d7](https://github.com/agentinit/agentinit/commit/2e533d7bea4d6fd4c0c2a74982b169e80d9192bd))
21
+
1
22
  ## [1.17.2](https://github.com/agentinit/agentinit/compare/v1.17.1...v1.17.2) (2026-04-02)
2
23
 
3
24
 
@@ -184,6 +205,7 @@
184
205
  * fix plugin scope handling for global MCP installs/removals and targeted removals
185
206
  * preserve revert semantics for canonical project skills and keep `skills remove` project-scoped by default
186
207
  * prevent `skills remove` from deleting shared native skill paths that are still used by another agent
208
+ * support `--all` when listing or installing from multi-plugin Claude bundles and document the updated multiselect shortcut
187
209
  * update docs and onboarding to the `mcp add|verify`, `rules add`, and `skills add` command model
188
210
  * parse Claude Code marketplace bundle repos like `openai/codex-plugin-cc` when installing portable skills or plugins
189
211
  * keep interactive plugin target defaults on all detected agents, warn clearly when Claude-native payloads are skipped, and reuse the previewed remote source during install
package/README.md CHANGED
@@ -208,16 +208,21 @@ agentinit skills add owner/repo --global --agent agents
208
208
  # Force copied installs instead of canonical symlink installs
209
209
  agentinit skills add ./skills --copy
210
210
 
211
+ # Install every bundled plugin from a multi-plugin Claude bundle
212
+ agentinit skills add owner/repo --all
213
+
211
214
  # Review and clean up installed skills
212
215
  agentinit skills list
213
216
  agentinit skills list --agent agents
214
217
  agentinit skills remove openai-docs
215
218
  ```
216
219
 
217
- If a GitHub or local Claude bundle contains multiple plugins, `agentinit skills add` prompts you to choose one or more bundled plugins to inspect or install. Press `Space` to select and `Enter` to confirm. In non-interactive `--yes` mode, ambiguous multi-plugin bundles fail instead of prompting.
220
+ If a GitHub or local Claude bundle contains multiple plugins, `agentinit skills add` prompts you to choose one or more bundled plugins to inspect or install. Press `Space` to select, `A` to select or deselect all, and `Enter` to confirm. Use `--all` to skip the prompt and install or inspect every bundled plugin. In non-interactive `--yes` mode, ambiguous multi-plugin bundles still fail unless `--all` is provided.
218
221
 
219
222
  Skills are installed into a canonical store by default (`.agents/skills/` for project, `~/.agents/skills/` for global), with agent-specific paths symlinked automatically. Bare skill names resolve from your configured default marketplace, falling back to the public catalog at `vercel-labs/agent-skills`. Use `./name` for local paths, `owner/repo` for GitHub repos, or `--from <marketplace>` for explicit marketplace sources.
220
223
 
224
+ When you re-run `agentinit skills add`, AgentInit now compares the installed skill payload with the source before overwriting anything. Unchanged skills are reported as already up to date. If an installed skill has changed, interactive runs ask for confirmation before replacing it, while `--yes` applies the update automatically.
225
+
221
226
  ### `agentinit plugins`
222
227
 
223
228
  Install, inspect, search, and remove portable plugins from explicit marketplace sources, GitHub repositories, or local paths.
@@ -245,6 +250,9 @@ agentinit plugins install owner/repo
245
250
  agentinit plugins install ./plugins/code-review
246
251
  agentinit plugins install ./plugins/code-review --copy-skills
247
252
 
253
+ # Install every bundled plugin from a multi-plugin Claude bundle
254
+ agentinit plugins install owner/repo --all
255
+
248
256
  # Inspect and remove installed plugins
249
257
  agentinit plugins list
250
258
  agentinit plugins remove code-review
@@ -252,7 +260,7 @@ agentinit plugins remove code-review
252
260
 
253
261
  Bare plugin names resolve through your configured default marketplace. Built-in marketplaces include `claude` and `openai`; add custom ones with `agentinit config marketplaces add`. For Claude-format plugins, native bundles are installed into `~/.claude/plugins` alongside portable skill and MCP installs.
254
262
 
255
- If a GitHub or local Claude bundle contains multiple plugins, `agentinit plugins install` prompts you to choose one or more bundled plugins to inspect or install. Press `Space` to select and `Enter` to confirm. In non-interactive `--yes` mode, ambiguous multi-plugin bundles fail instead of prompting.
263
+ If a GitHub or local Claude bundle contains multiple plugins, `agentinit plugins install` prompts you to choose one or more bundled plugins to inspect or install. Press `Space` to select, `A` to select or deselect all, and `Enter` to confirm. Use `--all` to skip the prompt and install or inspect every bundled plugin. In non-interactive `--yes` mode, ambiguous multi-plugin bundles still fail unless `--all` is provided.
256
264
 
257
265
  ### `agentinit config`
258
266
 
package/dist/cli.js CHANGED
@@ -18554,6 +18554,7 @@ import {resolve as resolve8, join as join5, relative as relative3, basename as b
18554
18554
  import {promises as fs24} from "fs";
18555
18555
  import {homedir as homedir5, tmpdir} from "os";
18556
18556
  import {execFile} from "child_process";
18557
+ import {createHash} from "crypto";
18557
18558
  import {promisify} from "util";
18558
18559
 
18559
18560
  class SkillsManager {
@@ -19108,6 +19109,52 @@ class SkillsManager {
19108
19109
  getInstallPath(skillName, targetDir) {
19109
19110
  return this.resolveInstallPath(targetDir, this.normalizeSkillName(skillName));
19110
19111
  }
19112
+ updateSnapshotWithFile(hash, relativePath, content) {
19113
+ hash.update(`file:${relativePath}\n`);
19114
+ hash.update(content);
19115
+ hash.update("\n");
19116
+ }
19117
+ async createDirectorySnapshot(rootPath) {
19118
+ if (!await fileExists(rootPath))
19119
+ return null;
19120
+ const hash = createHash("sha256");
19121
+ const walk = async (currentPath, relativePath) => {
19122
+ const stat = await fs24.stat(currentPath);
19123
+ if (!stat.isDirectory()) {
19124
+ const content = await fs24.readFile(currentPath);
19125
+ this.updateSnapshotWithFile(hash, relativePath, content);
19126
+ return;
19127
+ }
19128
+ hash.update(`dir:${relativePath || "."}\n`);
19129
+ const entries = (await fs24.readdir(currentPath)).sort((left, right) => left.localeCompare(right));
19130
+ for (const entry of entries) {
19131
+ await walk(join5(currentPath, entry), relativePath ? join5(relativePath, entry) : entry);
19132
+ }
19133
+ };
19134
+ await walk(rootPath, "");
19135
+ return hash.digest("hex");
19136
+ }
19137
+ async readExistingSkillSnapshot(installPath) {
19138
+ return this.createDirectorySnapshot(installPath);
19139
+ }
19140
+ async getNewSkillSnapshot(skill) {
19141
+ if (skill.generatedContent) {
19142
+ const hash = createHash("sha256");
19143
+ hash.update("dir:.\n");
19144
+ this.updateSnapshotWithFile(hash, "SKILL.md", skill.generatedContent.trim());
19145
+ return hash.digest("hex");
19146
+ }
19147
+ return this.createDirectorySnapshot(skill.path);
19148
+ }
19149
+ async compareSkillSnapshot(skill, installPath) {
19150
+ const existing = await this.readExistingSkillSnapshot(installPath);
19151
+ if (existing === null)
19152
+ return "new";
19153
+ const incoming = await this.getNewSkillSnapshot(skill);
19154
+ if (incoming === null)
19155
+ return "new";
19156
+ return existing === incoming ? "unchanged" : "changed";
19157
+ }
19111
19158
  async cleanAndCreateDirectory(path) {
19112
19159
  await fs24.rm(path, { recursive: true, force: true }).catch(() => {
19113
19160
  });
@@ -19128,7 +19175,7 @@ class SkillsManager {
19128
19175
  try {
19129
19176
  let skills2 = context.skills;
19130
19177
  if (skills2.length === 0) {
19131
- return { installed: [], skipped: [], warnings: context.warnings };
19178
+ return { installed: [], updated: [], unchanged: [], skipped: [], warnings: context.warnings };
19132
19179
  }
19133
19180
  if (options2.skills && options2.skills.length > 0) {
19134
19181
  const names = new Set(options2.skills.map((skill) => skill.toLowerCase()));
@@ -19139,18 +19186,33 @@ class SkillsManager {
19139
19186
  if (agents.length === 0 && !installToSharedStore) {
19140
19187
  return {
19141
19188
  installed: [],
19189
+ updated: [],
19190
+ unchanged: [],
19142
19191
  skipped: skills2.map((skill) => ({ skill, reason: "No target agents found" })),
19143
19192
  warnings: context.warnings
19144
19193
  };
19145
19194
  }
19146
- const result = { installed: [], skipped: [], warnings: context.warnings };
19195
+ const result = { installed: [], updated: [], unchanged: [], skipped: [], warnings: context.warnings };
19147
19196
  const installableAgents = [];
19197
+ const comparisonCache = new Map;
19198
+ const pendingUpdates = [];
19148
19199
  if (installToSharedStore) {
19149
19200
  for (const skill of skills2) {
19150
19201
  try {
19151
19202
  const installOptions = {
19152
19203
  ...options2.global !== undefined ? { global: options2.global } : {}
19153
19204
  };
19205
+ const plan = this.getCanonicalInstallPlan(skill.name, projectPath, installOptions);
19206
+ const comparison = await this.compareSkillSnapshot(skill, plan.path);
19207
+ comparisonCache.set(plan.path, comparison);
19208
+ if (comparison === "unchanged") {
19209
+ result.unchanged.push({ skill, agent: SHARED_SKILLS_TARGET_ID, path: plan.path });
19210
+ continue;
19211
+ }
19212
+ if (comparison === "changed") {
19213
+ pendingUpdates.push({ skill, agent: SHARED_SKILLS_TARGET_ID, installOptions });
19214
+ continue;
19215
+ }
19154
19216
  const installed = skill.generatedContent ? await this.installSkillFromContentToCanonicalStore(skill.name, skill.generatedContent, projectPath, installOptions) : await this.installSkillToCanonicalStore(skill.path, skill.name, projectPath, installOptions);
19155
19217
  result.installed.push({ skill, agent: SHARED_SKILLS_TARGET_ID, ...installed });
19156
19218
  } catch (error) {
@@ -19181,6 +19243,21 @@ class SkillsManager {
19181
19243
  ...options2.global !== undefined ? { global: options2.global } : {},
19182
19244
  ...options2.copy !== undefined ? { copy: options2.copy } : {}
19183
19245
  };
19246
+ const plan = await this.getInstallPlan(skill.name, agent, projectPath, installOptions);
19247
+ const comparisonPath = plan.canonicalPath || plan.path;
19248
+ let comparison = comparisonCache.get(comparisonPath);
19249
+ if (comparison === undefined) {
19250
+ comparison = await this.compareSkillSnapshot(skill, comparisonPath);
19251
+ comparisonCache.set(comparisonPath, comparison);
19252
+ }
19253
+ if (comparison === "unchanged") {
19254
+ result.unchanged.push({ skill, agent: agent.id, path: comparisonPath });
19255
+ continue;
19256
+ }
19257
+ if (comparison === "changed") {
19258
+ pendingUpdates.push({ skill, agent: agent.id, agentObj: agent, installOptions });
19259
+ continue;
19260
+ }
19184
19261
  const installed = skill.generatedContent ? await this.installSkillFromContentForAgent(skill.name, skill.generatedContent, agent, projectPath, installOptions) : await this.installSkillForAgent(skill.path, skill.name, agent, projectPath, installOptions);
19185
19262
  result.installed.push({ skill, agent: agent.id, ...installed });
19186
19263
  } catch (error) {
@@ -19188,6 +19265,35 @@ class SkillsManager {
19188
19265
  }
19189
19266
  }
19190
19267
  }
19268
+ if (pendingUpdates.length > 0) {
19269
+ let approvedNames;
19270
+ if (options2.yes) {
19271
+ approvedNames = new Set(pendingUpdates.map((p) => p.skill.name));
19272
+ } else if (options2.confirmUpdate) {
19273
+ const uniqueSkills = [...new Map(pendingUpdates.map((p) => [p.skill.name, p.skill])).values()];
19274
+ const approved = await options2.confirmUpdate(uniqueSkills);
19275
+ approvedNames = new Set(approved.map((s) => s.name));
19276
+ } else {
19277
+ approvedNames = new Set;
19278
+ }
19279
+ for (const pending of pendingUpdates) {
19280
+ if (!approvedNames.has(pending.skill.name)) {
19281
+ result.skipped.push({ skill: pending.skill, reason: "Update available; re-run with -y to update" });
19282
+ continue;
19283
+ }
19284
+ try {
19285
+ if (pending.agent === SHARED_SKILLS_TARGET_ID) {
19286
+ const installed = pending.skill.generatedContent ? await this.installSkillFromContentToCanonicalStore(pending.skill.name, pending.skill.generatedContent, projectPath, pending.installOptions) : await this.installSkillToCanonicalStore(pending.skill.path, pending.skill.name, projectPath, pending.installOptions);
19287
+ result.updated.push({ skill: pending.skill, agent: pending.agent, ...installed });
19288
+ } else if (pending.agentObj) {
19289
+ const installed = pending.skill.generatedContent ? await this.installSkillFromContentForAgent(pending.skill.name, pending.skill.generatedContent, pending.agentObj, projectPath, pending.installOptions) : await this.installSkillForAgent(pending.skill.path, pending.skill.name, pending.agentObj, projectPath, pending.installOptions);
19290
+ result.updated.push({ skill: pending.skill, agent: pending.agent, ...installed });
19291
+ }
19292
+ } catch (error) {
19293
+ result.skipped.push({ skill: pending.skill, reason: error.message });
19294
+ }
19295
+ }
19296
+ }
19191
19297
  return result;
19192
19298
  } finally {
19193
19299
  await context.cleanup();
@@ -41225,9 +41331,73 @@ var BUILT_IN_MARKETPLACE_IDS = new Set(MARKETPLACES.map((marketplace) => marketp
41225
41331
  var BUILT_IN_VERIFIED_REPOS = new Set(getBuiltInVerifiedGithubRepos());
41226
41332
 
41227
41333
  // dist/commands/skills.js
41228
- var import_prompts2 = __toESM(require_prompts3(), 1);
41334
+ var import_prompts3 = __toESM(require_prompts3(), 1);
41229
41335
  import {homedir as homedir6} from "os";
41230
41336
  import {relative as relative7, resolve as resolve12} from "path";
41337
+
41338
+ // dist/utils/promptUtils.js
41339
+ var import_prompts2 = __toESM(require_prompts3(), 1);
41340
+ import {createRequire as createRequire2} from "module";
41341
+ function enableUppercaseToggleAllForMultiselectPrompt() {
41342
+ if (uppercaseToggleAllPatched) {
41343
+ return;
41344
+ }
41345
+ uppercaseToggleAllPatched = true;
41346
+ try {
41347
+ const MultiselectPrompt = require2("prompts/lib/elements/multiselect");
41348
+ const prototype = MultiselectPrompt?.prototype;
41349
+ if (!prototype || typeof prototype._ !== "function" || prototype.__agentinitUppercaseToggleAllPatched) {
41350
+ return;
41351
+ }
41352
+ const originalHandler = prototype._;
41353
+ prototype._ = function agentinitMultiselectHandler(input, key) {
41354
+ return originalHandler.call(this, input === "A" ? "a" : input, key);
41355
+ };
41356
+ Object.defineProperty(prototype, "__agentinitUppercaseToggleAllPatched", {
41357
+ value: true,
41358
+ configurable: false,
41359
+ enumerable: false,
41360
+ writable: false
41361
+ });
41362
+ } catch {
41363
+ }
41364
+ }
41365
+ async function promptMultiselect(options2) {
41366
+ enableUppercaseToggleAllForMultiselectPrompt();
41367
+ return import_prompts2.default({
41368
+ ...options2,
41369
+ type: "multiselect",
41370
+ instructions: options2.instructions ?? false,
41371
+ hint: options2.hint ?? MULTISELECT_TOGGLE_ALL_HINT
41372
+ });
41373
+ }
41374
+ async function selectBundlePlugins(entries, actionLabel, options2 = {}) {
41375
+ if (options2.selectAll) {
41376
+ logger.info("Selecting all bundled plugins (--all).");
41377
+ return entries.map((entry) => entry.name);
41378
+ }
41379
+ logger.info(dim(MULTISELECT_TOGGLE_ALL_HINT));
41380
+ const response = await promptMultiselect({
41381
+ name: "plugins",
41382
+ message: `This repository contains multiple plugins. Select which to ${actionLabel}:`,
41383
+ min: 1,
41384
+ choices: entries.map((entry) => ({
41385
+ title: entry.name,
41386
+ value: entry.name,
41387
+ ...entry.description ? { description: entry.description } : {}
41388
+ }))
41389
+ });
41390
+ if (!response.plugins || response.plugins.length === 0) {
41391
+ logger.info("Cancelled.");
41392
+ return null;
41393
+ }
41394
+ return response.plugins;
41395
+ }
41396
+ var require2 = createRequire2(import.meta.url);
41397
+ var MULTISELECT_TOGGLE_ALL_HINT = "Press Space to select, A to select or deselect all, then Enter to confirm.";
41398
+ var uppercaseToggleAllPatched = false;
41399
+
41400
+ // dist/commands/skills.js
41231
41401
  init_skillsManager();
41232
41402
  init_pluginManager();
41233
41403
  init_marketplaceRegistry();
@@ -41236,7 +41406,7 @@ init_skills();
41236
41406
  function registerSkillsCommand(program2) {
41237
41407
  const marketplaceHelp = getMarketplaceIds().join(", ");
41238
41408
  const skills3 = program2.command("skills").description("Manage agent skills");
41239
- skills3.command("add <source>").description("Add skills from a marketplace, GitHub repo, or local path").option("--from <marketplace>", `Marketplace source override (available: ${marketplaceHelp})`).option("-g, --global", "Install skills globally").option("-a, --agent <agents...>", "Target specific agent(s)").option("-s, --skill <names...>", "Install only specific skills by name").option("-l, --list", "List available skills from the source without installing").option("--copy", "Copy skill files instead of symlinking").option("-y, --yes", "Skip prompts and auto-detect project-configured agents only").action(async (source, options2) => {
41409
+ skills3.command("add <source>").description("Add skills from a marketplace, GitHub repo, or local path").option("--from <marketplace>", `Marketplace source override (available: ${marketplaceHelp})`).option("-g, --global", "Install skills globally").option("-a, --agent <agents...>", "Target specific agent(s)").option("-s, --skill <names...>", "Install only specific skills by name").option("-l, --list", "List available skills from the source without installing").option("--all", "Select all bundled plugins when the source contains multiple plugins").option("--copy", "Copy skill files instead of symlinking").option("-y, --yes", "Skip prompts, auto-detect project-configured agents, and apply available skill updates").action(async (source, options2) => {
41240
41410
  logger.titleBox("AgentInit Skills");
41241
41411
  const agentManager9 = new AgentManager;
41242
41412
  const skillsManager5 = new SkillsManager(agentManager9);
@@ -41251,7 +41421,7 @@ function registerSkillsCommand(program2) {
41251
41421
  } catch (error) {
41252
41422
  if (error instanceof MultipleBundlePluginsError) {
41253
41423
  spinner.stop();
41254
- const selected = await selectBundlePlugins(error, "list");
41424
+ const selected = await selectBundlePlugins(error.entries, "list", { selectAll: options2.all });
41255
41425
  if (!selected) {
41256
41426
  return;
41257
41427
  }
@@ -41284,9 +41454,9 @@ function registerSkillsCommand(program2) {
41284
41454
  });
41285
41455
  verifySpinner.stop();
41286
41456
  } catch (error) {
41287
- if (error instanceof MultipleBundlePluginsError && !options2.yes) {
41457
+ if (error instanceof MultipleBundlePluginsError && (options2.all || !options2.yes)) {
41288
41458
  verifySpinner.stop();
41289
- const selected = await selectBundlePlugins(error, "install");
41459
+ const selected = await selectBundlePlugins(error.entries, "install", { selectAll: options2.all });
41290
41460
  if (!selected) {
41291
41461
  return;
41292
41462
  }
@@ -41327,6 +41497,16 @@ function registerSkillsCommand(program2) {
41327
41497
  targetGlobal = selection.global;
41328
41498
  }
41329
41499
  }
41500
+ const confirmUpdate = options2.yes ? undefined : async (skills4) => {
41501
+ const names = skills4.map((s) => s.name).join(", ");
41502
+ const response = await import_prompts3.default({
41503
+ type: "confirm",
41504
+ name: "update",
41505
+ message: skills4.length === 1 ? `Skill "${skills4[0].name}" has been updated. Update it?` : `${skills4.length} skill(s) have updates (${names}). Update them?`,
41506
+ initial: true
41507
+ });
41508
+ return response.update ? skills4 : [];
41509
+ };
41330
41510
  const buildInstallOptions = (pluginName) => ({
41331
41511
  ...options2.from !== undefined ? { from: options2.from } : {},
41332
41512
  ...targetGlobal !== undefined ? { global: targetGlobal } : {},
@@ -41334,7 +41514,8 @@ function registerSkillsCommand(program2) {
41334
41514
  ...options2.skill !== undefined ? { skills: options2.skill } : {},
41335
41515
  ...options2.copy !== undefined ? { copy: options2.copy } : {},
41336
41516
  ...pluginName !== undefined ? { pluginName } : {},
41337
- ...options2.yes !== undefined ? { yes: options2.yes } : {}
41517
+ ...options2.yes !== undefined ? { yes: options2.yes } : {},
41518
+ ...confirmUpdate !== undefined ? { confirmUpdate } : {}
41338
41519
  });
41339
41520
  const pluginsToInstall = selectedPluginNames || [undefined];
41340
41521
  for (const pluginName of pluginsToInstall) {
@@ -41430,30 +41611,10 @@ function registerSkillsCommand(program2) {
41430
41611
  }
41431
41612
  });
41432
41613
  }
41433
- async function selectBundlePlugins(error, actionLabel) {
41434
- logger.info(dim("Press Space to select, then Enter to confirm."));
41435
- const response = await import_prompts2.default({
41436
- type: "multiselect",
41437
- name: "plugins",
41438
- message: `This repository contains multiple plugins. Select which to ${actionLabel}:`,
41439
- instructions: false,
41440
- min: 1,
41441
- choices: error.entries.map((entry) => ({
41442
- title: entry.name,
41443
- value: entry.name,
41444
- ...entry.description ? { description: entry.description } : {}
41445
- }))
41446
- });
41447
- if (!response.plugins || response.plugins.length === 0) {
41448
- logger.info("Cancelled.");
41449
- return null;
41450
- }
41451
- return response.plugins;
41452
- }
41453
41614
  async function resolveInteractiveSkillTargets(skillsManager5, agentManager9, source, projectPath, options2) {
41454
41615
  let installGlobal = !!options2.global;
41455
41616
  if (!options2.global) {
41456
- const response2 = await import_prompts2.default({
41617
+ const response2 = await import_prompts3.default({
41457
41618
  type: "select",
41458
41619
  name: "scope",
41459
41620
  message: "Where should the skill be installed?",
@@ -41491,11 +41652,9 @@ async function resolveInteractiveSkillTargets(skillsManager5, agentManager9, sou
41491
41652
  logger.warn("No project agent files with skills support were detected in this project.");
41492
41653
  logger.info("Select project agent directories manually. Run `agentinit init` if you want future installs to auto-detect.");
41493
41654
  }
41494
- const response = await import_prompts2.default({
41495
- type: "multiselect",
41655
+ const response = await promptMultiselect({
41496
41656
  name: "groups",
41497
41657
  message: installGlobal ? "Select which global agent skills directories to install into:" : detectedGroups.length > 0 ? "Select which project agent skills directories to install into:" : "Select which project agent skills directories to install into manually:",
41498
- instructions: false,
41499
41658
  min: 1,
41500
41659
  choices: availableGroups.map((group) => ({
41501
41660
  title: formatSkillGroupTitle(group),
@@ -41737,14 +41896,18 @@ var displayDiscoveredSkills = function(skills3, warnings) {
41737
41896
  }
41738
41897
  };
41739
41898
  var displayInstallResult = function(result, spinner, agentManager9, skillsManager5, source, options2) {
41740
- if (result.installed.length === 0 && result.skipped.length === 0) {
41899
+ const hasInstalled = result.installed.length > 0;
41900
+ const hasUpdated = result.updated.length > 0;
41901
+ const hasUnchanged = result.unchanged.length > 0;
41902
+ const hasSkipped = result.skipped.length > 0;
41903
+ if (!hasInstalled && !hasUpdated && !hasUnchanged && !hasSkipped) {
41741
41904
  spinner.warn("No skills found in the source.");
41742
41905
  for (const warning of result.warnings) {
41743
41906
  logger.warn(warning);
41744
41907
  }
41745
41908
  return;
41746
41909
  }
41747
- if (result.installed.length === 0 && result.skipped.length > 0 && result.skipped.every((skip) => skip.reason === "No target agents found")) {
41910
+ if (!hasInstalled && !hasUpdated && !hasUnchanged && hasSkipped && result.skipped.every((skip) => skip.reason === "No target agents found")) {
41748
41911
  spinner.warn("No target agents found.");
41749
41912
  logNoTargetAgentsGuidance(skillsManager5, agentManager9, source, {
41750
41913
  ...options2.from !== undefined ? { from: options2.from } : {}
@@ -41757,29 +41920,63 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
41757
41920
  }
41758
41921
  return;
41759
41922
  }
41760
- const uniqueInstallCount = new Set(result.installed.map((item) => `${item.path}:${item.skill.name}`)).size;
41761
- spinner.succeed(`Installed ${green(String(uniqueInstallCount))} skill(s)`);
41762
- const byPath = new Map;
41763
- for (const item of result.installed) {
41764
- const path = item.path;
41765
- const existing = byPath.get(path) || {
41766
- agents: new Set,
41767
- skills: new Set
41768
- };
41769
- existing.agents.add(formatSkillTargetName(agentManager9, item.agent));
41770
- existing.skills.add(item.skill.name);
41771
- byPath.set(path, existing);
41772
- }
41773
- for (const [path, details] of byPath) {
41774
- logger.info(` ${relative7(process.cwd(), path) || path}`);
41775
- logger.info(` Agents: ${[...details.agents].join(", ")}`);
41776
- logger.info(` Skills: ${green(String(details.skills.size))} installed (${[...details.skills].join(", ")})`);
41923
+ if (!hasInstalled && !hasUpdated && hasUnchanged) {
41924
+ const uniqueUnchanged = new Set(result.unchanged.map((item) => item.skill.name));
41925
+ spinner.info(`${uniqueUnchanged.size} skill(s) already up to date`);
41926
+ logger.info(dim(` Already installed: ${[...uniqueUnchanged].join(", ")}`));
41927
+ } else {
41928
+ const parts = [];
41929
+ if (hasInstalled) {
41930
+ const uniqueInstallCount = new Set(result.installed.map((item) => `${item.path}:${item.skill.name}`)).size;
41931
+ parts.push(`Installed ${green(String(uniqueInstallCount))}`);
41932
+ }
41933
+ if (hasUpdated) {
41934
+ const uniqueUpdateCount = new Set(result.updated.map((item) => `${item.path}:${item.skill.name}`)).size;
41935
+ parts.push(`Updated ${yellow(String(uniqueUpdateCount))}`);
41936
+ }
41937
+ if (hasUnchanged) {
41938
+ const uniqueUnchanged = new Set(result.unchanged.map((item) => item.skill.name)).size;
41939
+ parts.push(`${uniqueUnchanged} already up to date`);
41940
+ }
41941
+ spinner.succeed(`${parts.join(", ")} skill(s)`);
41942
+ const byPath = new Map;
41943
+ for (const item of result.installed) {
41944
+ const path = item.path;
41945
+ const existing = byPath.get(path) || {
41946
+ agents: new Set,
41947
+ skills: new Set
41948
+ };
41949
+ existing.agents.add(formatSkillTargetName(agentManager9, item.agent));
41950
+ existing.skills.add(item.skill.name);
41951
+ byPath.set(path, existing);
41952
+ }
41953
+ for (const [path, details] of byPath) {
41954
+ logger.info(` ${relative7(process.cwd(), path) || path}`);
41955
+ logger.info(` Agents: ${[...details.agents].join(", ")}`);
41956
+ logger.info(` Skills: ${green(String(details.skills.size))} installed (${[...details.skills].join(", ")})`);
41957
+ }
41958
+ const byPathUpdated = new Map;
41959
+ for (const item of result.updated) {
41960
+ const path = item.path;
41961
+ const existing = byPathUpdated.get(path) || {
41962
+ agents: new Set,
41963
+ skills: new Set
41964
+ };
41965
+ existing.agents.add(formatSkillTargetName(agentManager9, item.agent));
41966
+ existing.skills.add(item.skill.name);
41967
+ byPathUpdated.set(path, existing);
41968
+ }
41969
+ for (const [path, details] of byPathUpdated) {
41970
+ logger.info(` ${relative7(process.cwd(), path) || path}`);
41971
+ logger.info(` Agents: ${[...details.agents].join(", ")}`);
41972
+ logger.info(` Skills: ${yellow(String(details.skills.size))} updated (${[...details.skills].join(", ")})`);
41973
+ }
41777
41974
  }
41778
- const copiedFallbacks = result.installed.filter((item) => item.symlinkFailed);
41975
+ const copiedFallbacks = [...result.installed, ...result.updated].filter((item) => item.symlinkFailed);
41779
41976
  if (copiedFallbacks.length > 0) {
41780
41977
  logger.warn(`Symlink creation failed for ${copiedFallbacks.length} install(s); copied the skill files instead.`);
41781
41978
  }
41782
- if (result.skipped.length > 0) {
41979
+ if (hasSkipped) {
41783
41980
  logger.info("");
41784
41981
  logger.warn(`Skipped ${result.skipped.length} skill(s):`);
41785
41982
  for (const skip of result.skipped) {
@@ -41792,7 +41989,9 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
41792
41989
  logger.warn(warning);
41793
41990
  }
41794
41991
  }
41795
- logger.success("Skills installation complete.");
41992
+ if (hasInstalled || hasUpdated) {
41993
+ logger.success("Skills installation complete.");
41994
+ }
41796
41995
  };
41797
41996
 
41798
41997
  // dist/commands/mcp.js
@@ -42585,7 +42784,7 @@ function registerRulesCommand(program2) {
42585
42784
  }
42586
42785
 
42587
42786
  // dist/commands/plugins.js
42588
- var import_prompts3 = __toESM(require_prompts3(), 1);
42787
+ var import_prompts4 = __toESM(require_prompts3(), 1);
42589
42788
  import {homedir as homedir7} from "os";
42590
42789
  import {dirname as dirname9, relative as relative8, resolve as resolve13} from "path";
42591
42790
  init_pluginManager();
@@ -42595,7 +42794,7 @@ function registerPluginsCommand(program2) {
42595
42794
  const marketplaceHelp = new PluginManager().getMarketplaceIds().join(", ");
42596
42795
  const marketplaceCategoryHelp = getMarketplaceCategories().join(", ");
42597
42796
  const plugins = program2.command("plugins").description("Install agent-agnostic plugins from any marketplace or source");
42598
- plugins.command("install <source>").description("Install a plugin from <marketplace>/<name>, a GitHub repo, or a local path").option("--from <marketplace>", `Marketplace source override (available: ${marketplaceHelp})`).option("-a, --agent <agents...>", "Target specific agent(s)").option("-g, --global", "Install globally").option("--copy-skills", "Copy plugin skills instead of using canonical symlink installs").option("-l, --list", "Preview plugin contents without installing").option("-y, --yes", "Skip confirmation prompts, auto-detect project-configured agents").action(async (source, options2) => {
42797
+ plugins.command("install <source>").description("Install a plugin from <marketplace>/<name>, a GitHub repo, or a local path").option("--from <marketplace>", `Marketplace source override (available: ${marketplaceHelp})`).option("-a, --agent <agents...>", "Target specific agent(s)").option("-g, --global", "Install globally").option("--copy-skills", "Copy plugin skills instead of using canonical symlink installs").option("--all", "Select all bundled plugins when the source contains multiple plugins").option("-l, --list", "Preview plugin contents without installing").option("-y, --yes", "Skip confirmation prompts, auto-detect project-configured agents").action(async (source, options2) => {
42599
42798
  logger.titleBox("AgentInit Plugins");
42600
42799
  const agentManager12 = new AgentManager;
42601
42800
  const pluginManager3 = new PluginManager(agentManager12);
@@ -42631,7 +42830,7 @@ function registerPluginsCommand(program2) {
42631
42830
  } catch (error) {
42632
42831
  if (error instanceof MultipleBundlePluginsError) {
42633
42832
  spinner.stop();
42634
- const selected = await selectBundlePlugins2(error, "preview");
42833
+ const selected = await selectBundlePlugins(error.entries, "preview", { selectAll: options2.all });
42635
42834
  if (!selected) {
42636
42835
  return;
42637
42836
  }
@@ -42695,7 +42894,7 @@ function registerPluginsCommand(program2) {
42695
42894
  } catch (error) {
42696
42895
  if (error instanceof MultipleBundlePluginsError) {
42697
42896
  previewSpinner.stop();
42698
- const selected = await selectBundlePlugins2(error, "install");
42897
+ const selected = await selectBundlePlugins(error.entries, "install", { selectAll: options2.all });
42699
42898
  if (!selected) {
42700
42899
  return;
42701
42900
  }
@@ -42791,9 +42990,9 @@ function registerPluginsCommand(program2) {
42791
42990
  }
42792
42991
  logger.success("Plugin installation complete.");
42793
42992
  } catch (error) {
42794
- if (error instanceof MultipleBundlePluginsError && !options2.yes) {
42993
+ if (error instanceof MultipleBundlePluginsError && (options2.all || !options2.yes)) {
42795
42994
  spinner.stop();
42796
- const selected = await selectBundlePlugins2(error, "install");
42995
+ const selected = await selectBundlePlugins(error.entries, "install", { selectAll: options2.all });
42797
42996
  if (!selected) {
42798
42997
  return;
42799
42998
  }
@@ -42937,26 +43136,6 @@ function registerPluginsCommand(program2) {
42937
43136
  }
42938
43137
  });
42939
43138
  }
42940
- async function selectBundlePlugins2(error, actionLabel) {
42941
- logger.info(dim("Press Space to select, then Enter to confirm."));
42942
- const response = await import_prompts3.default({
42943
- type: "multiselect",
42944
- name: "plugins",
42945
- message: `This repository contains multiple plugins. Select which to ${actionLabel}:`,
42946
- instructions: false,
42947
- min: 1,
42948
- choices: error.entries.map((entry) => ({
42949
- title: entry.name,
42950
- value: entry.name,
42951
- ...entry.description ? { description: entry.description } : {}
42952
- }))
42953
- });
42954
- if (!response.plugins || response.plugins.length === 0) {
42955
- logger.info("Cancelled.");
42956
- return null;
42957
- }
42958
- return response.plugins;
42959
- }
42960
43139
  var formatPathForDisplay = function(pathValue, projectPath) {
42961
43140
  if (pathValue.startsWith(`${projectPath}/`)) {
42962
43141
  return relative8(projectPath, pathValue) || ".";
@@ -43290,7 +43469,7 @@ async function interactiveAgentSelect(pluginManager3, agentManager12, projectPat
43290
43469
  }));
43291
43470
  if (groups.length === 0 && !installGlobal) {
43292
43471
  logger.warn("No agents with skills support detected in this project.");
43293
- const scopeResponse = await import_prompts3.default({
43472
+ const scopeResponse = await import_prompts4.default({
43294
43473
  type: "select",
43295
43474
  name: "scope",
43296
43475
  message: "Install this plugin globally instead?",
@@ -43316,11 +43495,9 @@ async function interactiveAgentSelect(pluginManager3, agentManager12, projectPat
43316
43495
  logger.warn("No supported agents expose a skills directory.");
43317
43496
  return { aborted: true };
43318
43497
  }
43319
- const response = await import_prompts3.default({
43320
- type: "multiselect",
43498
+ const response = await promptMultiselect({
43321
43499
  name: "groups",
43322
43500
  message: installGlobal ? "Select which global agents should receive this plugin:" : "Select which agents should receive this plugin:",
43323
- instructions: false,
43324
43501
  min: 1,
43325
43502
  choices: groups.map((group) => {
43326
43503
  const compatible = group.compatibleAgentNames.length > 0 ? dim(` (also compatible: ${group.compatibleAgentNames.join(", ")})`) : "";
@@ -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;AAqCpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0b7D"}
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"}