skillex 0.2.3 → 0.2.4

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
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.4] - 2026-04-08
11
+
12
+ ### Changed
13
+ - `skillex ui` skill list now shows compact labels: `Name (id) · tag1, tag2, tag3` — description and full compatibility list removed from each row
14
+ - `skillex ui` shows "Fetching catalog..." while loading and limits visible rows to 12 at a time
15
+ - `skillex install` and `skillex update` now render an inline progress bar (`[████░░░░] 1/5 skill-id`) instead of printing one line per skill
16
+
10
17
  ## [0.2.3] - 2026-04-08
11
18
 
12
19
  ### Fixed
package/dist/cli.js CHANGED
@@ -362,11 +362,9 @@ async function handleInstall(positionals, flags, userConfig) {
362
362
  const result = await installSkills(positionals, {
363
363
  ...opts,
364
364
  installAll,
365
- onProgress: (current, total, skillId) => {
366
- output.info(`[${current}/${total}] Installing ${skillId}...`);
367
- },
365
+ onProgress: (current, total, skillId) => output.progress(current, total, skillId),
368
366
  });
369
- output.success(`Installed ${result.installedCount} skill(s) to ${result.statePaths.scope} state at ${result.statePaths.stateDir}`);
367
+ output.success(`Installed ${result.installedCount} skill(s)`);
370
368
  for (const skill of result.installedSkills) {
371
369
  output.info(` + ${skill.id}@${skill.version}`);
372
370
  }
@@ -374,7 +372,10 @@ async function handleInstall(positionals, flags, userConfig) {
374
372
  }
375
373
  async function handleUpdate(positionals, flags, userConfig) {
376
374
  const opts = commonOptions(flags, userConfig);
377
- const result = await updateInstalledSkills(positionals, opts);
375
+ const result = await updateInstalledSkills(positionals, {
376
+ ...opts,
377
+ onProgress: (current, total, skillId) => output.progress(current, total, skillId),
378
+ });
378
379
  if (result.updatedSkills.length === 0) {
379
380
  output.info("No skills updated.");
380
381
  }
@@ -437,7 +438,9 @@ async function handleUi(flags, userConfig) {
437
438
  const options = commonOptions(flags, userConfig);
438
439
  const state = await getInstalledSkills(options);
439
440
  const source = await resolveProjectSource(options);
441
+ output.statusLine("Fetching catalog...");
440
442
  const catalog = await loadCatalog({ ...source, ...cacheOptions(options) });
443
+ output.clearStatus();
441
444
  if (catalog.skills.length === 0) {
442
445
  output.info("No skills available in the catalog.");
443
446
  return;
@@ -450,19 +453,24 @@ async function handleUi(flags, userConfig) {
450
453
  : "No skills available in the catalog.");
451
454
  return;
452
455
  }
453
- const installResult = selection.toInstall.length > 0 ? await installSkills(selection.toInstall, options) : null;
456
+ const installResult = selection.toInstall.length > 0
457
+ ? await installSkills(selection.toInstall, {
458
+ ...options,
459
+ onProgress: (current, total, skillId) => output.progress(current, total, skillId),
460
+ })
461
+ : null;
454
462
  const removeResult = selection.toRemove.length > 0 ? await removeSkills(selection.toRemove, options) : null;
455
463
  if (!installResult && !removeResult) {
456
464
  output.info("No changes applied.");
457
465
  return;
458
466
  }
459
- output.success("UI summary:");
460
467
  if (installResult) {
461
- output.info(` Installed : ${installResult.installedSkills.map((s) => s.id).join(", ")}`);
468
+ output.success(`Installed: ${installResult.installedSkills.map((s) => s.id).join(", ")}`);
462
469
  }
463
470
  if (removeResult) {
464
- output.info(` Removed : ${removeResult.removedSkills.join(", ")}`);
471
+ output.success(`Removed: ${removeResult.removedSkills.join(", ")}`);
465
472
  }
473
+ printAutoSyncResult(installResult?.autoSync ?? removeResult?.autoSync ?? null);
466
474
  }
467
475
  async function handleStatus(flags, userConfig) {
468
476
  const options = commonOptions(flags, userConfig);
package/dist/output.d.ts CHANGED
@@ -44,3 +44,22 @@ export declare function error(message: string): void;
44
44
  * @param message - Debug message.
45
45
  */
46
46
  export declare function debug(message: string): void;
47
+ /**
48
+ * Renders an inline progress bar that overwrites the current line.
49
+ * Prints a newline when current === total.
50
+ *
51
+ * @param current - Number of completed items (1-based).
52
+ * @param total - Total number of items.
53
+ * @param label - Short label shown after the bar.
54
+ */
55
+ export declare function progress(current: number, total: number, label: string): void;
56
+ /**
57
+ * Writes a transient status message on the current line (overwritable with clearStatus).
58
+ *
59
+ * @param message - Status message to display.
60
+ */
61
+ export declare function statusLine(message: string): void;
62
+ /**
63
+ * Clears the current status line written by {@link statusLine}.
64
+ */
65
+ export declare function clearStatus(): void;
package/dist/output.js CHANGED
@@ -76,3 +76,46 @@ export function debug(message) {
76
76
  process.stderr.write(applyColor("2", `[debug] ${message}`, process.stderr) + "\n");
77
77
  }
78
78
  }
79
+ // ---------------------------------------------------------------------------
80
+ // Progress and status helpers (TTY-only; fall back to plain lines otherwise)
81
+ // ---------------------------------------------------------------------------
82
+ /**
83
+ * Renders an inline progress bar that overwrites the current line.
84
+ * Prints a newline when current === total.
85
+ *
86
+ * @param current - Number of completed items (1-based).
87
+ * @param total - Total number of items.
88
+ * @param label - Short label shown after the bar.
89
+ */
90
+ export function progress(current, total, label) {
91
+ if (!process.stdout.isTTY) {
92
+ console.log(`[${current}/${total}] ${label}`);
93
+ return;
94
+ }
95
+ const filled = total > 0 ? Math.round((current / total) * 16) : 0;
96
+ const bar = applyColor("32", "█".repeat(filled), process.stdout) + "░".repeat(16 - filled);
97
+ const counter = applyColor("2", `${current}/${total}`, process.stdout);
98
+ const line = ` [${bar}] ${counter} ${label}`;
99
+ process.stdout.write(`\r${line}\x1b[K`);
100
+ if (current === total) {
101
+ process.stdout.write("\n");
102
+ }
103
+ }
104
+ /**
105
+ * Writes a transient status message on the current line (overwritable with clearStatus).
106
+ *
107
+ * @param message - Status message to display.
108
+ */
109
+ export function statusLine(message) {
110
+ if (!process.stdout.isTTY)
111
+ return;
112
+ process.stdout.write(`\r ${applyColor("2", message, process.stdout)}\x1b[K`);
113
+ }
114
+ /**
115
+ * Clears the current status line written by {@link statusLine}.
116
+ */
117
+ export function clearStatus() {
118
+ if (!process.stdout.isTTY)
119
+ return;
120
+ process.stdout.write("\r\x1b[K");
121
+ }
package/dist/ui.d.ts CHANGED
@@ -12,6 +12,7 @@ interface UiPrompts {
12
12
  checkbox?: ((options: {
13
13
  message: string;
14
14
  instructions?: string | undefined;
15
+ pageSize?: number | undefined;
15
16
  choices: UiChoice[];
16
17
  }) => Promise<string[]>) | undefined;
17
18
  }
package/dist/ui.js CHANGED
@@ -33,13 +33,21 @@ export async function runInteractiveUi(options) {
33
33
  const selectedIds = filteredSkills.length === 0
34
34
  ? []
35
35
  : await (prompts.checkbox || fallbackCheckbox)({
36
- message: "Selecione as skills",
37
- instructions: "Type to filter first • Space to select Enter to install",
38
- choices: filteredSkills.map((skill) => ({
39
- name: `${skill.name} (${skill.id}) - ${skill.description || "Sem descricao"} [${skill.compatibility.join(",") || "sem-compat"}]`,
40
- value: skill.id,
41
- checked: installedSet.has(skill.id),
42
- })),
36
+ message: "Select skills",
37
+ instructions: "↑↓ navigate · space select · enter confirm · type to filter",
38
+ pageSize: 12,
39
+ choices: filteredSkills.map((skill) => {
40
+ const tags = (skill.tags ?? []).slice(0, 4).join(", ");
41
+ const detail = tags || (skill.description ?? "").slice(0, 55);
42
+ const label = detail
43
+ ? `${skill.name} (${skill.id}) · ${detail}`
44
+ : `${skill.name} (${skill.id})`;
45
+ return {
46
+ name: label,
47
+ value: skill.id,
48
+ checked: installedSet.has(skill.id),
49
+ };
50
+ }),
43
51
  });
44
52
  const selectedSet = new Set(selectedIds);
45
53
  const toInstall = selectedIds.filter((skillId) => !installedSet.has(skillId));
@@ -62,6 +70,7 @@ async function loadPromptAdapters() {
62
70
  checkbox: async (options) => prompts.checkbox({
63
71
  message: options.message,
64
72
  ...(options.instructions ? { instructions: options.instructions } : {}),
73
+ ...(options.pageSize !== undefined ? { pageSize: options.pageSize } : {}),
65
74
  choices: options.choices.map((choice) => ({
66
75
  name: choice.name,
67
76
  value: choice.value,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillex",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "CLI to list, install, and synchronize AI agent skills from GitHub-hosted catalogs.",
5
5
  "type": "module",
6
6
  "repository": {