claude-agent-skills 1.5.1 → 1.5.3

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/commands/hub.js CHANGED
@@ -2,10 +2,9 @@ import updateNotifier from 'update-notifier';
2
2
  import { createRequire } from 'node:module';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { dirname, join } from 'node:path';
5
- import ansis from 'ansis';
6
- import { showIntro } from '../lib/banner.js';
5
+ import { showIntro, showIntroStatic } from '../lib/banner.js';
7
6
  import { CliCancel } from '../lib/prompts.js';
8
- import { brand, muted, white } from '../lib/theme.js';
7
+ import { brand, muted } from '../lib/theme.js';
9
8
  import { inlineSelect } from '../lib/inlineSelect.js';
10
9
 
11
10
  const req = createRequire(import.meta.url);
@@ -31,30 +30,61 @@ const MENU = [
31
30
  { value: 'quit', label: 'Quit' },
32
31
  ];
33
32
 
34
- function printCompactHeader() {
35
- const silver = s => ansis.rgb(190, 190, 190)(s);
36
- process.stdout.write('\n');
37
- process.stdout.write(brand('◈ CLAUDE SKILLS') + ' ' + silver('Agent Skills for Claude Code') + '\n');
38
- process.stdout.write('\n');
39
- }
40
-
41
33
  function restoreScreen() {
42
34
  process.stdout.write('\x1b[?1049l');
43
35
  }
44
36
 
37
+ /** Show a live countdown then return — lets the user read command output before menu clears. */
38
+ async function pauseBeforeReturn(seconds = 5) {
39
+ let remaining = seconds;
40
+ const render = () => process.stdout.write(
41
+ `\r\x1b[2K ${brand('◂')} ${muted(`returning to menu in ${remaining}s · press any key`)}`
42
+ );
43
+
44
+ process.stdout.write('\n');
45
+ render();
46
+
47
+ return new Promise(resolve => {
48
+ let done = false;
49
+ function cleanup() {
50
+ if (done) return;
51
+ done = true;
52
+ clearInterval(tick);
53
+ process.stdin.setRawMode(false);
54
+ process.stdin.pause();
55
+ process.stdin.removeAllListeners('data');
56
+ process.stdout.write('\n');
57
+ }
58
+
59
+ const tick = setInterval(() => {
60
+ remaining--;
61
+ if (remaining <= 0) { cleanup(); resolve(); }
62
+ else render();
63
+ }, 1000);
64
+
65
+ process.stdin.setRawMode(true);
66
+ process.stdin.resume();
67
+ process.stdin.setEncoding('utf8');
68
+ process.stdin.on('data', key => {
69
+ if (key === '\x03') { cleanup(); process.exit(0); }
70
+ cleanup();
71
+ resolve();
72
+ });
73
+ });
74
+ }
75
+
45
76
  export async function runHub() {
46
- // Enter alternate screen buffer — isolated viewport with no scrollback accumulation.
47
- // Original terminal content is restored when we exit (same as vim/less/claude-code).
77
+ // Alternate screen buffer — isolated viewport, no scrollback. Restored on exit like vim/less.
48
78
  process.stdout.write('\x1b[?1049h\x1b[2J\x1b[H');
49
- process.on('exit', restoreScreen); // covers Ctrl+C, process.exit(), uncaught errors
79
+ process.on('exit', restoreScreen);
50
80
 
51
- await showIntro();
81
+ await showIntro(); // animated banner on first load
52
82
 
53
83
  let first = true;
54
84
  for (;;) {
55
85
  if (!first) {
56
86
  process.stdout.write('\x1b[2J\x1b[H');
57
- printCompactHeader();
87
+ await showIntroStatic(); // static banner — always visible on return to menu
58
88
  }
59
89
  first = false;
60
90
 
@@ -65,10 +95,7 @@ export async function runHub() {
65
95
  options: MENU,
66
96
  });
67
97
 
68
- if (choice === 'quit') {
69
- restoreScreen();
70
- return;
71
- }
98
+ if (choice === 'quit') { restoreScreen(); return; }
72
99
 
73
100
  if (choice === 'add') await runAdd(SKIP);
74
101
  if (choice === 'update') await runUpdate(SKIP);
@@ -76,12 +103,12 @@ export async function runHub() {
76
103
  if (choice === 'list') await runList(SKIP);
77
104
  if (choice === 'sync') await runSync(SKIP);
78
105
  if (choice === 'check') await runCheck(SKIP);
106
+
107
+ // Pause so user can read command output before the screen clears
108
+ await pauseBeforeReturn();
79
109
  } catch (e) {
80
- if (e instanceof CliCancel) continue; // sub-command ESC → back to menu
81
- if (e?.isCancel) { // hub menu ESC → quit
82
- restoreScreen();
83
- return;
84
- }
110
+ if (e instanceof CliCancel) continue; // ESC from sub-step → back to menu immediately
111
+ if (e?.isCancel) { restoreScreen(); return; }
85
112
  restoreScreen();
86
113
  throw e;
87
114
  }
package/lib/banner.js CHANGED
@@ -89,6 +89,13 @@ async function renderStatic() {
89
89
  renderFrame(staticColor);
90
90
  }
91
91
 
92
+ function printSubtitle() {
93
+ const silver = s => ansis.rgb(190, 190, 190)(s);
94
+ process.stdout.write('\n');
95
+ process.stdout.write(silver('Agent Skills for Claude Code by Pavithran Francis') + '\n\n');
96
+ process.stdout.write(muted('Repository: ') + silver(REPO) + '\n\n');
97
+ }
98
+
92
99
  /**
93
100
  * showIntro() — full two-sweep entrance + subtitle (call ONCE at startup)
94
101
  * showIntro({ skip:true }) — no-op (used inside sub-commands called from hub)
@@ -100,10 +107,14 @@ export async function showIntro({ skip = false } = {}) {
100
107
 
101
108
  await runSweep({ step: 3, fps: 55 }); // first sweep (~0.8 s)
102
109
  await runSweep({ step: 3, fps: 55 }); // second sweep (~0.8 s)
103
- await renderStatic(); // settle to metallic pride
110
+ await renderStatic(); // settle to metallic state
104
111
 
105
- const silver = s => ansis.rgb(190, 190, 190)(s);
106
- process.stdout.write('\n');
107
- process.stdout.write(silver('Agent Skills for Claude Code by Pavithran Francis') + '\n\n');
108
- process.stdout.write(muted('Repository: ') + silver(REPO) + '\n\n');
112
+ printSubtitle();
113
+ }
114
+
115
+ /** Static banner with no animation — used on hub loop iterations after the first. */
116
+ export async function showIntroStatic() {
117
+ process.stdout.write('\n'.repeat(ART.length));
118
+ await renderStatic();
119
+ printSubtitle();
109
120
  }
@@ -13,13 +13,21 @@ const KEY = {
13
13
  ESC: '\x1b',
14
14
  };
15
15
 
16
+ function formatHints(str) {
17
+ return ' ' + brand('►► ') + str.split(' · ').map(part => {
18
+ const [key, ...rest] = part.trim().split(' ');
19
+ return ansis.bold(ansis.white(key)) + (rest.length ? muted(' ' + rest.join(' ')) : '');
20
+ }).join(muted(' · '));
21
+ }
22
+
16
23
  export async function inlineSelect({ message, hint = '↑↓ navigate · enter select · esc back', options }) {
17
24
  let cursor = 0;
18
25
  let lastLines = 0;
19
26
 
20
27
  function renderLines() {
21
28
  const out = [];
22
- out.push(brand('◆') + ' ' + ansis.bold(white(message)) + ' ' + muted(hint));
29
+ // Title line no hints here (moved to footer bar)
30
+ out.push(brand('◆') + ' ' + ansis.bold(white(message)));
23
31
  out.push(muted('│'));
24
32
  for (let i = 0; i < options.length; i++) {
25
33
  const focused = i === cursor;
@@ -31,6 +39,8 @@ export async function inlineSelect({ message, hint = '↑↓ navigate · enter s
31
39
  out.push(muted('│') + ' ' + arrow + ' ' + label + desc);
32
40
  }
33
41
  out.push(muted('│'));
42
+ // Dedicated hint footer bar
43
+ out.push(formatHints(hint));
34
44
  return out;
35
45
  }
36
46
 
package/lib/picker.js CHANGED
@@ -3,7 +3,14 @@
3
3
  * Navigation: ↑↓←→ space=toggle a=all enter=confirm esc=back
4
4
  */
5
5
  import ansis from 'ansis';
6
- import { skillColor, white, muted, success, brand, warn } from './theme.js';
6
+ import { skillColor, white, muted, success, brand } from './theme.js';
7
+
8
+ function formatHints(str) {
9
+ return ' ' + brand('►► ') + str.split(' · ').map(part => {
10
+ const [key, ...rest] = part.trim().split(' ');
11
+ return ansis.bold(ansis.white(key)) + (rest.length ? muted(' ' + rest.join(' ')) : '');
12
+ }).join(muted(' · '));
13
+ }
7
14
 
8
15
  const KEY = {
9
16
  UP: '\x1b[A',
@@ -52,12 +59,13 @@ export async function skillPicker({ message, options }) {
52
59
  return Math.max(0, Math.min(options.length - 1, c));
53
60
  }
54
61
 
62
+ const HINT = '↑↓←→ navigate · space toggle · a=all · enter confirm · esc back';
63
+
55
64
  function renderGrid() {
56
65
  const lines = [];
57
66
 
58
- // Header
59
- lines.push(brand('◆') + ' ' + ansis.bold(white(message)) +
60
- muted(' ↑↓←→ navigate · space toggle · a=all · enter confirm · esc back'));
67
+ // Header — title only, hints moved to footer bar
68
+ lines.push(brand('◆') + ' ' + ansis.bold(white(message)));
61
69
  lines.push(muted('│'));
62
70
 
63
71
  // Grid rows
@@ -94,12 +102,13 @@ export async function skillPicker({ message, options }) {
94
102
  lines.push(muted('│ ') + white(line));
95
103
  }
96
104
 
97
- // Footer
105
+ // Footer — count + hint bar
98
106
  const selCount = sel.size;
99
107
  lines.push(
100
108
  ' ' + (selCount > 0 ? success(`${selCount} selected`) : muted('0 selected')) +
101
109
  muted(` · ${cursor + 1} of ${options.length}`)
102
110
  );
111
+ lines.push(formatHints(HINT));
103
112
 
104
113
  return lines;
105
114
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-agent-skills",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "Install and manage Pavi's Claude Code agent skills",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/skills.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema_version": 1,
3
3
  "name": "claude-agent-skills",
4
- "version": "1.5.1",
4
+ "version": "1.5.3",
5
5
  "skills": [
6
6
  "ask-matt",
7
7
  "brainstorming",