agentinit 1.18.0 → 1.18.2

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.2](https://github.com/agentinit/agentinit/compare/v1.18.1...v1.18.2) (2026-04-05)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **skills:** preview install status during target selection ([97d93ec](https://github.com/agentinit/agentinit/commit/97d93ecb73d6522d2e10b6133eb8b050826c3849))
7
+
8
+ ## [1.18.1](https://github.com/agentinit/agentinit/compare/v1.18.0...v1.18.1) (2026-04-05)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **skills:** compare full skill payloads before updates ([4b1e27c](https://github.com/agentinit/agentinit/commit/4b1e27cc69264d46edb38c2f6435b7f2659cfdb3))
14
+
15
+ ## [Unreleased]
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **skills:** compare installed skill payloads before prompting for updates
21
+
1
22
  # [1.18.0](https://github.com/agentinit/agentinit/compare/v1.17.2...v1.18.0) (2026-04-04)
2
23
 
3
24
 
package/README.md CHANGED
@@ -221,6 +221,8 @@ If a GitHub or local Claude bundle contains multiple plugins, `agentinit skills
221
221
 
222
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.
223
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
+
224
226
  ### `agentinit plugins`
225
227
 
226
228
  Install, inspect, search, and remove portable plugins from explicit marketplace sources, GitHub repositories, or local paths.
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 {
@@ -19006,6 +19007,22 @@ class SkillsManager {
19006
19007
  mode: "symlink"
19007
19008
  };
19008
19009
  }
19010
+ async previewInstallStatus(skill, projectPath, options2 = {}) {
19011
+ if (options2.sharedStore) {
19012
+ const plan2 = this.getCanonicalInstallPlan(skill.name, projectPath, {
19013
+ ...options2.global !== undefined ? { global: options2.global } : {}
19014
+ });
19015
+ return this.compareSkillSnapshot(skill, plan2.path);
19016
+ }
19017
+ if (!options2.agent) {
19018
+ throw new Error("Agent is required to preview install status");
19019
+ }
19020
+ const plan = await this.getInstallPlan(skill.name, options2.agent, projectPath, {
19021
+ ...options2.global !== undefined ? { global: options2.global } : {},
19022
+ ...options2.copy !== undefined ? { copy: options2.copy } : {}
19023
+ });
19024
+ return this.compareSkillSnapshot(skill, plan.canonicalPath || plan.path);
19025
+ }
19009
19026
  async installSkillForAgent(skillPath, skillName, agent, projectPath, options2 = {}) {
19010
19027
  const plan = await this.getInstallPlan(skillName, agent, projectPath, options2);
19011
19028
  const skillsDir = agent.getSkillsDir(projectPath, options2.global);
@@ -19108,6 +19125,52 @@ class SkillsManager {
19108
19125
  getInstallPath(skillName, targetDir) {
19109
19126
  return this.resolveInstallPath(targetDir, this.normalizeSkillName(skillName));
19110
19127
  }
19128
+ updateSnapshotWithFile(hash, relativePath, content) {
19129
+ hash.update(`file:${relativePath}\n`);
19130
+ hash.update(content);
19131
+ hash.update("\n");
19132
+ }
19133
+ async createDirectorySnapshot(rootPath) {
19134
+ if (!await fileExists(rootPath))
19135
+ return null;
19136
+ const hash = createHash("sha256");
19137
+ const walk = async (currentPath, relativePath) => {
19138
+ const stat = await fs24.stat(currentPath);
19139
+ if (!stat.isDirectory()) {
19140
+ const content = await fs24.readFile(currentPath);
19141
+ this.updateSnapshotWithFile(hash, relativePath, content);
19142
+ return;
19143
+ }
19144
+ hash.update(`dir:${relativePath || "."}\n`);
19145
+ const entries = (await fs24.readdir(currentPath)).sort((left, right) => left.localeCompare(right));
19146
+ for (const entry of entries) {
19147
+ await walk(join5(currentPath, entry), relativePath ? join5(relativePath, entry) : entry);
19148
+ }
19149
+ };
19150
+ await walk(rootPath, "");
19151
+ return hash.digest("hex");
19152
+ }
19153
+ async readExistingSkillSnapshot(installPath) {
19154
+ return this.createDirectorySnapshot(installPath);
19155
+ }
19156
+ async getNewSkillSnapshot(skill) {
19157
+ if (skill.generatedContent) {
19158
+ const hash = createHash("sha256");
19159
+ hash.update("dir:.\n");
19160
+ this.updateSnapshotWithFile(hash, "SKILL.md", skill.generatedContent.trim());
19161
+ return hash.digest("hex");
19162
+ }
19163
+ return this.createDirectorySnapshot(skill.path);
19164
+ }
19165
+ async compareSkillSnapshot(skill, installPath) {
19166
+ const existing = await this.readExistingSkillSnapshot(installPath);
19167
+ if (existing === null)
19168
+ return "new";
19169
+ const incoming = await this.getNewSkillSnapshot(skill);
19170
+ if (incoming === null)
19171
+ return "new";
19172
+ return existing === incoming ? "unchanged" : "changed";
19173
+ }
19111
19174
  async cleanAndCreateDirectory(path) {
19112
19175
  await fs24.rm(path, { recursive: true, force: true }).catch(() => {
19113
19176
  });
@@ -19128,7 +19191,7 @@ class SkillsManager {
19128
19191
  try {
19129
19192
  let skills2 = context.skills;
19130
19193
  if (skills2.length === 0) {
19131
- return { installed: [], skipped: [], warnings: context.warnings };
19194
+ return { installed: [], updated: [], unchanged: [], skipped: [], warnings: context.warnings };
19132
19195
  }
19133
19196
  if (options2.skills && options2.skills.length > 0) {
19134
19197
  const names = new Set(options2.skills.map((skill) => skill.toLowerCase()));
@@ -19139,18 +19202,33 @@ class SkillsManager {
19139
19202
  if (agents.length === 0 && !installToSharedStore) {
19140
19203
  return {
19141
19204
  installed: [],
19205
+ updated: [],
19206
+ unchanged: [],
19142
19207
  skipped: skills2.map((skill) => ({ skill, reason: "No target agents found" })),
19143
19208
  warnings: context.warnings
19144
19209
  };
19145
19210
  }
19146
- const result = { installed: [], skipped: [], warnings: context.warnings };
19211
+ const result = { installed: [], updated: [], unchanged: [], skipped: [], warnings: context.warnings };
19147
19212
  const installableAgents = [];
19213
+ const comparisonCache = new Map;
19214
+ const pendingUpdates = [];
19148
19215
  if (installToSharedStore) {
19149
19216
  for (const skill of skills2) {
19150
19217
  try {
19151
19218
  const installOptions = {
19152
19219
  ...options2.global !== undefined ? { global: options2.global } : {}
19153
19220
  };
19221
+ const plan = this.getCanonicalInstallPlan(skill.name, projectPath, installOptions);
19222
+ const comparison = await this.compareSkillSnapshot(skill, plan.path);
19223
+ comparisonCache.set(plan.path, comparison);
19224
+ if (comparison === "unchanged") {
19225
+ result.unchanged.push({ skill, agent: SHARED_SKILLS_TARGET_ID, path: plan.path });
19226
+ continue;
19227
+ }
19228
+ if (comparison === "changed") {
19229
+ pendingUpdates.push({ skill, agent: SHARED_SKILLS_TARGET_ID, installOptions });
19230
+ continue;
19231
+ }
19154
19232
  const installed = skill.generatedContent ? await this.installSkillFromContentToCanonicalStore(skill.name, skill.generatedContent, projectPath, installOptions) : await this.installSkillToCanonicalStore(skill.path, skill.name, projectPath, installOptions);
19155
19233
  result.installed.push({ skill, agent: SHARED_SKILLS_TARGET_ID, ...installed });
19156
19234
  } catch (error) {
@@ -19181,6 +19259,21 @@ class SkillsManager {
19181
19259
  ...options2.global !== undefined ? { global: options2.global } : {},
19182
19260
  ...options2.copy !== undefined ? { copy: options2.copy } : {}
19183
19261
  };
19262
+ const plan = await this.getInstallPlan(skill.name, agent, projectPath, installOptions);
19263
+ const comparisonPath = plan.canonicalPath || plan.path;
19264
+ let comparison = comparisonCache.get(comparisonPath);
19265
+ if (comparison === undefined) {
19266
+ comparison = await this.compareSkillSnapshot(skill, comparisonPath);
19267
+ comparisonCache.set(comparisonPath, comparison);
19268
+ }
19269
+ if (comparison === "unchanged") {
19270
+ result.unchanged.push({ skill, agent: agent.id, path: comparisonPath });
19271
+ continue;
19272
+ }
19273
+ if (comparison === "changed") {
19274
+ pendingUpdates.push({ skill, agent: agent.id, agentObj: agent, installOptions });
19275
+ continue;
19276
+ }
19184
19277
  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
19278
  result.installed.push({ skill, agent: agent.id, ...installed });
19186
19279
  } catch (error) {
@@ -19188,6 +19281,35 @@ class SkillsManager {
19188
19281
  }
19189
19282
  }
19190
19283
  }
19284
+ if (pendingUpdates.length > 0) {
19285
+ let approvedNames;
19286
+ if (options2.yes) {
19287
+ approvedNames = new Set(pendingUpdates.map((p) => p.skill.name));
19288
+ } else if (options2.confirmUpdate) {
19289
+ const uniqueSkills = [...new Map(pendingUpdates.map((p) => [p.skill.name, p.skill])).values()];
19290
+ const approved = await options2.confirmUpdate(uniqueSkills);
19291
+ approvedNames = new Set(approved.map((s) => s.name));
19292
+ } else {
19293
+ approvedNames = new Set;
19294
+ }
19295
+ for (const pending of pendingUpdates) {
19296
+ if (!approvedNames.has(pending.skill.name)) {
19297
+ result.skipped.push({ skill: pending.skill, reason: "Update available; re-run with -y to update" });
19298
+ continue;
19299
+ }
19300
+ try {
19301
+ if (pending.agent === SHARED_SKILLS_TARGET_ID) {
19302
+ 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);
19303
+ result.updated.push({ skill: pending.skill, agent: pending.agent, ...installed });
19304
+ } else if (pending.agentObj) {
19305
+ 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);
19306
+ result.updated.push({ skill: pending.skill, agent: pending.agent, ...installed });
19307
+ }
19308
+ } catch (error) {
19309
+ result.skipped.push({ skill: pending.skill, reason: error.message });
19310
+ }
19311
+ }
19312
+ }
19191
19313
  return result;
19192
19314
  } finally {
19193
19315
  await context.cleanup();
@@ -41300,7 +41422,7 @@ init_skills();
41300
41422
  function registerSkillsCommand(program2) {
41301
41423
  const marketplaceHelp = getMarketplaceIds().join(", ");
41302
41424
  const skills3 = program2.command("skills").description("Manage agent skills");
41303
- 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 and auto-detect project-configured agents only").action(async (source, options2) => {
41425
+ 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) => {
41304
41426
  logger.titleBox("AgentInit Skills");
41305
41427
  const agentManager9 = new AgentManager;
41306
41428
  const skillsManager5 = new SkillsManager(agentManager9);
@@ -41342,10 +41464,12 @@ function registerSkillsCommand(program2) {
41342
41464
  }
41343
41465
  const verifySpinner = ora("Verifying skill source...").start();
41344
41466
  let selectedPluginNames;
41467
+ let preparedSkills = [];
41345
41468
  try {
41346
- await skillsManager5.prepareSource(source, process.cwd(), {
41469
+ const prepared = await skillsManager5.prepareSource(source, process.cwd(), {
41347
41470
  from: options2.from
41348
41471
  });
41472
+ preparedSkills = prepared.skills;
41349
41473
  verifySpinner.stop();
41350
41474
  } catch (error) {
41351
41475
  if (error instanceof MultipleBundlePluginsError && (options2.all || !options2.yes)) {
@@ -41357,10 +41481,11 @@ function registerSkillsCommand(program2) {
41357
41481
  selectedPluginNames = selected;
41358
41482
  const retrySpinner = ora("Verifying skill source...").start();
41359
41483
  try {
41360
- await skillsManager5.prepareSource(source, process.cwd(), {
41484
+ const prepared = await skillsManager5.prepareSource(source, process.cwd(), {
41361
41485
  from: options2.from,
41362
41486
  ...selectedPluginNames[0] ? { pluginName: selectedPluginNames[0] } : {}
41363
41487
  });
41488
+ preparedSkills = prepared.skills;
41364
41489
  retrySpinner.stop();
41365
41490
  } catch (retryError) {
41366
41491
  retrySpinner.fail("Failed to verify skill source");
@@ -41376,9 +41501,13 @@ function registerSkillsCommand(program2) {
41376
41501
  let targetAgents = options2.agent;
41377
41502
  let targetGlobal = options2.global;
41378
41503
  if (!targetAgents && !options2.yes) {
41504
+ const selectedSkillNames = options2.skill && options2.skill.length > 0 ? new Set(options2.skill.map((name) => name.toLowerCase())) : undefined;
41505
+ const filteredPreviewSkills = selectedSkillNames ? preparedSkills.filter((skill) => selectedSkillNames.has(skill.name.toLowerCase())) : preparedSkills;
41379
41506
  const selection = await resolveInteractiveSkillTargets(skillsManager5, agentManager9, source, process.cwd(), {
41380
41507
  from: options2.from,
41381
- global: options2.global
41508
+ global: options2.global,
41509
+ copy: options2.copy,
41510
+ skills: filteredPreviewSkills
41382
41511
  });
41383
41512
  if (selection?.aborted) {
41384
41513
  await skillsManager5.discardPreparedSource(source, process.cwd(), {
@@ -41391,6 +41520,16 @@ function registerSkillsCommand(program2) {
41391
41520
  targetGlobal = selection.global;
41392
41521
  }
41393
41522
  }
41523
+ const confirmUpdate = options2.yes ? undefined : async (skills4) => {
41524
+ const names = skills4.map((s) => s.name).join(", ");
41525
+ const response = await import_prompts3.default({
41526
+ type: "confirm",
41527
+ name: "update",
41528
+ message: skills4.length === 1 ? `Skill "${skills4[0].name}" has been updated. Update it?` : `${skills4.length} skill(s) have updates (${names}). Update them?`,
41529
+ initial: true
41530
+ });
41531
+ return response.update ? skills4 : [];
41532
+ };
41394
41533
  const buildInstallOptions = (pluginName) => ({
41395
41534
  ...options2.from !== undefined ? { from: options2.from } : {},
41396
41535
  ...targetGlobal !== undefined ? { global: targetGlobal } : {},
@@ -41398,7 +41537,8 @@ function registerSkillsCommand(program2) {
41398
41537
  ...options2.skill !== undefined ? { skills: options2.skill } : {},
41399
41538
  ...options2.copy !== undefined ? { copy: options2.copy } : {},
41400
41539
  ...pluginName !== undefined ? { pluginName } : {},
41401
- ...options2.yes !== undefined ? { yes: options2.yes } : {}
41540
+ ...options2.yes !== undefined ? { yes: options2.yes } : {},
41541
+ ...confirmUpdate !== undefined ? { confirmUpdate } : {}
41402
41542
  });
41403
41543
  const pluginsToInstall = selectedPluginNames || [undefined];
41404
41544
  for (const pluginName of pluginsToInstall) {
@@ -41539,11 +41679,18 @@ async function resolveInteractiveSkillTargets(skillsManager5, agentManager9, sou
41539
41679
  name: "groups",
41540
41680
  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:",
41541
41681
  min: 1,
41542
- choices: availableGroups.map((group) => ({
41543
- title: formatSkillGroupTitle(group),
41544
- ...group.description ? { description: group.description } : {},
41545
- value: group.kind === "canonical-shared" ? [SHARED_SKILLS_TARGET_ID] : group.agents.map((agent) => agent.id),
41546
- selected: shouldPreselectSkillGroup(group, installGlobal, detectedGroups.length > 0, recommendedAgentId)
41682
+ choices: await Promise.all(availableGroups.map(async (group) => {
41683
+ const description = await buildSkillGroupPreviewDescription(skillsManager5, group, projectPath, {
41684
+ global: installGlobal,
41685
+ ...options2.copy !== undefined ? { copy: options2.copy } : {},
41686
+ ...options2.skills !== undefined ? { skills: options2.skills } : {}
41687
+ });
41688
+ return {
41689
+ title: formatSkillGroupTitle(group),
41690
+ ...description ? { description } : {},
41691
+ value: group.kind === "canonical-shared" ? [SHARED_SKILLS_TARGET_ID] : group.agents.map((agent) => agent.id),
41692
+ selected: shouldPreselectSkillGroup(group, installGlobal, detectedGroups.length > 0, recommendedAgentId)
41693
+ };
41547
41694
  }))
41548
41695
  });
41549
41696
  const selected = flattenAgentIds(response.groups);
@@ -41692,6 +41839,33 @@ var describeGlobalSkillGroup = function(group) {
41692
41839
  }
41693
41840
  return;
41694
41841
  };
41842
+ async function buildSkillGroupPreviewDescription(skillsManager5, group, projectPath, options2) {
41843
+ const skills3 = options2.skills || [];
41844
+ if (skills3.length === 0) {
41845
+ return group.description || describeGlobalSkillGroup(group);
41846
+ }
41847
+ const statuses = await Promise.all(skills3.map(async (skill) => ({
41848
+ skill,
41849
+ status: await skillsManager5.previewInstallStatus(skill, projectPath, group.kind === "canonical-shared" ? { global: options2.global, sharedStore: true } : {
41850
+ global: options2.global,
41851
+ ...options2.copy !== undefined ? { copy: options2.copy } : {},
41852
+ ...group.agents[0] ? { agent: group.agents[0] } : {}
41853
+ })
41854
+ })));
41855
+ const unchanged = statuses.filter((entry) => entry.status === "unchanged").map((entry) => entry.skill.name);
41856
+ const changed = statuses.filter((entry) => entry.status === "changed").map((entry) => entry.skill.name);
41857
+ const parts = [];
41858
+ if (unchanged.length > 0) {
41859
+ parts.push(`Already up to date: ${unchanged.join(", ")}`);
41860
+ }
41861
+ if (changed.length > 0) {
41862
+ parts.push(`${changed.length === 1 ? "Update available" : "Updates available"}: ${changed.join(", ")}`);
41863
+ }
41864
+ if (parts.length === 0) {
41865
+ return;
41866
+ }
41867
+ return parts.join(". ");
41868
+ }
41695
41869
  var formatSkillGroupTitle = function(group) {
41696
41870
  if (group.kind === "canonical-shared") {
41697
41871
  return `${group.displayDir} -> ${SHARED_SKILLS_TARGET_NAME}`;
@@ -41779,14 +41953,18 @@ var displayDiscoveredSkills = function(skills3, warnings) {
41779
41953
  }
41780
41954
  };
41781
41955
  var displayInstallResult = function(result, spinner, agentManager9, skillsManager5, source, options2) {
41782
- if (result.installed.length === 0 && result.skipped.length === 0) {
41956
+ const hasInstalled = result.installed.length > 0;
41957
+ const hasUpdated = result.updated.length > 0;
41958
+ const hasUnchanged = result.unchanged.length > 0;
41959
+ const hasSkipped = result.skipped.length > 0;
41960
+ if (!hasInstalled && !hasUpdated && !hasUnchanged && !hasSkipped) {
41783
41961
  spinner.warn("No skills found in the source.");
41784
41962
  for (const warning of result.warnings) {
41785
41963
  logger.warn(warning);
41786
41964
  }
41787
41965
  return;
41788
41966
  }
41789
- if (result.installed.length === 0 && result.skipped.length > 0 && result.skipped.every((skip) => skip.reason === "No target agents found")) {
41967
+ if (!hasInstalled && !hasUpdated && !hasUnchanged && hasSkipped && result.skipped.every((skip) => skip.reason === "No target agents found")) {
41790
41968
  spinner.warn("No target agents found.");
41791
41969
  logNoTargetAgentsGuidance(skillsManager5, agentManager9, source, {
41792
41970
  ...options2.from !== undefined ? { from: options2.from } : {}
@@ -41799,29 +41977,63 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
41799
41977
  }
41800
41978
  return;
41801
41979
  }
41802
- const uniqueInstallCount = new Set(result.installed.map((item) => `${item.path}:${item.skill.name}`)).size;
41803
- spinner.succeed(`Installed ${green(String(uniqueInstallCount))} skill(s)`);
41804
- const byPath = new Map;
41805
- for (const item of result.installed) {
41806
- const path = item.path;
41807
- const existing = byPath.get(path) || {
41808
- agents: new Set,
41809
- skills: new Set
41810
- };
41811
- existing.agents.add(formatSkillTargetName(agentManager9, item.agent));
41812
- existing.skills.add(item.skill.name);
41813
- byPath.set(path, existing);
41814
- }
41815
- for (const [path, details] of byPath) {
41816
- logger.info(` ${relative7(process.cwd(), path) || path}`);
41817
- logger.info(` Agents: ${[...details.agents].join(", ")}`);
41818
- logger.info(` Skills: ${green(String(details.skills.size))} installed (${[...details.skills].join(", ")})`);
41980
+ if (!hasInstalled && !hasUpdated && hasUnchanged) {
41981
+ const uniqueUnchanged = new Set(result.unchanged.map((item) => item.skill.name));
41982
+ spinner.info(`${uniqueUnchanged.size} skill(s) already up to date`);
41983
+ logger.info(dim(` Already installed: ${[...uniqueUnchanged].join(", ")}`));
41984
+ } else {
41985
+ const parts = [];
41986
+ if (hasInstalled) {
41987
+ const uniqueInstallCount = new Set(result.installed.map((item) => `${item.path}:${item.skill.name}`)).size;
41988
+ parts.push(`Installed ${green(String(uniqueInstallCount))}`);
41989
+ }
41990
+ if (hasUpdated) {
41991
+ const uniqueUpdateCount = new Set(result.updated.map((item) => `${item.path}:${item.skill.name}`)).size;
41992
+ parts.push(`Updated ${yellow(String(uniqueUpdateCount))}`);
41993
+ }
41994
+ if (hasUnchanged) {
41995
+ const uniqueUnchanged = new Set(result.unchanged.map((item) => item.skill.name)).size;
41996
+ parts.push(`${uniqueUnchanged} already up to date`);
41997
+ }
41998
+ spinner.succeed(`${parts.join(", ")} skill(s)`);
41999
+ const byPath = new Map;
42000
+ for (const item of result.installed) {
42001
+ const path = item.path;
42002
+ const existing = byPath.get(path) || {
42003
+ agents: new Set,
42004
+ skills: new Set
42005
+ };
42006
+ existing.agents.add(formatSkillTargetName(agentManager9, item.agent));
42007
+ existing.skills.add(item.skill.name);
42008
+ byPath.set(path, existing);
42009
+ }
42010
+ for (const [path, details] of byPath) {
42011
+ logger.info(` ${relative7(process.cwd(), path) || path}`);
42012
+ logger.info(` Agents: ${[...details.agents].join(", ")}`);
42013
+ logger.info(` Skills: ${green(String(details.skills.size))} installed (${[...details.skills].join(", ")})`);
42014
+ }
42015
+ const byPathUpdated = new Map;
42016
+ for (const item of result.updated) {
42017
+ const path = item.path;
42018
+ const existing = byPathUpdated.get(path) || {
42019
+ agents: new Set,
42020
+ skills: new Set
42021
+ };
42022
+ existing.agents.add(formatSkillTargetName(agentManager9, item.agent));
42023
+ existing.skills.add(item.skill.name);
42024
+ byPathUpdated.set(path, existing);
42025
+ }
42026
+ for (const [path, details] of byPathUpdated) {
42027
+ logger.info(` ${relative7(process.cwd(), path) || path}`);
42028
+ logger.info(` Agents: ${[...details.agents].join(", ")}`);
42029
+ logger.info(` Skills: ${yellow(String(details.skills.size))} updated (${[...details.skills].join(", ")})`);
42030
+ }
41819
42031
  }
41820
- const copiedFallbacks = result.installed.filter((item) => item.symlinkFailed);
42032
+ const copiedFallbacks = [...result.installed, ...result.updated].filter((item) => item.symlinkFailed);
41821
42033
  if (copiedFallbacks.length > 0) {
41822
42034
  logger.warn(`Symlink creation failed for ${copiedFallbacks.length} install(s); copied the skill files instead.`);
41823
42035
  }
41824
- if (result.skipped.length > 0) {
42036
+ if (hasSkipped) {
41825
42037
  logger.info("");
41826
42038
  logger.warn(`Skipped ${result.skipped.length} skill(s):`);
41827
42039
  for (const skip of result.skipped) {
@@ -41834,7 +42046,9 @@ var displayInstallResult = function(result, spinner, agentManager9, skillsManage
41834
42046
  logger.warn(warning);
41835
42047
  }
41836
42048
  }
41837
- logger.success("Skills installation complete.");
42049
+ if (hasInstalled || hasUpdated) {
42050
+ logger.success("Skills installation complete.");
42051
+ }
41838
42052
  };
41839
42053
 
41840
42054
  // dist/commands/mcp.js
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqCpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0Q5D"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqCpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqS5D"}