@claude-code-hooks/cli 0.1.2 → 0.1.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.
Files changed (2) hide show
  1. package/package.json +4 -4
  2. package/src/cli.js +72 -40
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-code-hooks/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Wizard CLI to set up and manage @claude-code-hooks packages for Claude Code.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/beefiker/claude-code-hooks/tree/main/packages/cli",
@@ -28,9 +28,9 @@
28
28
  "dependencies": {
29
29
  "@clack/prompts": "^1.0.0",
30
30
  "@claude-code-hooks/core": "0.1.2",
31
- "@claude-code-hooks/security": "0.1.3",
32
- "@claude-code-hooks/secrets": "0.1.3",
33
- "@claude-code-hooks/sound": "0.2.7"
31
+ "@claude-code-hooks/security": "0.1.4",
32
+ "@claude-code-hooks/secrets": "0.1.4",
33
+ "@claude-code-hooks/sound": "0.2.8"
34
34
  },
35
35
  "keywords": [
36
36
  "claude",
package/src/cli.js CHANGED
@@ -83,48 +83,65 @@ async function main() {
83
83
 
84
84
  intro('claude-code-hooks');
85
85
 
86
- const action = await select({
87
- message: 'What do you want to do?',
88
- options: [
89
- { value: 'setup', label: 'Setup / enable packages' },
90
- { value: 'uninstall', label: 'Uninstall / remove managed hooks' },
91
- { value: 'exit', label: 'Exit' }
92
- ]
93
- });
94
- if (isCancel(action) || action === 'exit') dieCancelled('Bye');
95
-
96
- const target = await select({
97
- message: 'Where do you want to apply changes?',
98
- options: [
99
- { value: 'global', label: `Global (default): ${pc.dim('~/.claude/settings.json')}` },
100
- { value: 'projectOnly', label: `Project-only: write ${pc.bold(CONFIG_FILENAME)} + print snippet` }
101
- ]
102
- });
103
- if (isCancel(target)) dieCancelled();
104
-
105
- const selected = await multiselect({
106
- message: action === 'setup' ? 'Which packages do you want to enable?' : 'Which packages do you want to uninstall?',
107
- options: [
108
- { value: 'security', label: '@claude-code-hooks/security' },
109
- { value: 'secrets', label: '@claude-code-hooks/secrets' },
110
- { value: 'sound', label: '@claude-code-hooks/sound' }
111
- ],
112
- required: true
113
- });
114
- if (isCancel(selected)) dieCancelled();
86
+ // ── Step 1–3: action, target, packages (with simple back navigation) ──
87
+ let action;
88
+ let target;
89
+ let selected;
90
+
91
+ while (true) {
92
+ action = await select({
93
+ message: `${pc.dim('Step 1/5')} Action`,
94
+ options: [
95
+ { value: 'setup', label: 'Setup / enable packages' },
96
+ { value: 'uninstall', label: 'Uninstall / remove managed hooks' },
97
+ { value: 'exit', label: 'Exit' }
98
+ ]
99
+ });
100
+ if (isCancel(action) || action === 'exit') dieCancelled('Bye');
101
+
102
+ target = await select({
103
+ message: `${pc.dim('Step 2/5')} Target`,
104
+ options: [
105
+ { value: 'global', label: `Global (default): ${pc.dim('~/.claude/settings.json')}` },
106
+ { value: 'projectOnly', label: `Project-only: write ${pc.bold(CONFIG_FILENAME)} + print snippet` },
107
+ { value: '__back__', label: 'Back' }
108
+ ]
109
+ });
110
+ if (isCancel(target)) dieCancelled();
111
+ if (target === '__back__') continue;
112
+
113
+ selected = await multiselect({
114
+ message: `${pc.dim('Step 3/5')} Packages`,
115
+ options: [
116
+ { value: 'security', label: '@claude-code-hooks/security', hint: 'Warn/block risky commands' },
117
+ { value: 'secrets', label: '@claude-code-hooks/secrets', hint: 'Detect secret-like tokens' },
118
+ { value: 'sound', label: '@claude-code-hooks/sound', hint: 'Play sounds on events' }
119
+ ],
120
+ required: true
121
+ });
122
+ if (isCancel(selected)) dieCancelled();
123
+
124
+ const proceed = await confirm({ message: 'Continue to configure selected packages?', initialValue: true });
125
+ if (isCancel(proceed)) dieCancelled();
126
+ if (!proceed) continue;
127
+
128
+ break;
129
+ }
115
130
 
116
131
  // Build per-package plan/config
117
- const perPackage = {
118
- security: null,
119
- secrets: null,
120
- sound: null
121
- };
132
+ const perPackage = { security: null, secrets: null, sound: null };
133
+
134
+ // ── Step 4/5: configure ──
135
+ note(
136
+ selected.map((k) => `${pc.bold(k)}: ${pc.dim({ security: 'Warn/block risky commands', secrets: 'Detect secret-like tokens', sound: 'Play sounds on events' }[k] || '')}`).join('\n'),
137
+ `${pc.dim('Step 4/5')} Configure`
138
+ );
122
139
 
123
- if (selected.includes('security')) perPackage.security = await planSecuritySetup({ action, projectDir });
124
- if (selected.includes('secrets')) perPackage.secrets = await planSecretsSetup({ action, projectDir });
125
- if (selected.includes('sound')) perPackage.sound = await planSoundSetup({ action, projectDir });
140
+ if (selected.includes('security')) perPackage.security = await planSecuritySetup({ action, projectDir, ui: 'umbrella' });
141
+ if (selected.includes('secrets')) perPackage.secrets = await planSecretsSetup({ action, projectDir, ui: 'umbrella' });
142
+ if (selected.includes('sound')) perPackage.sound = await planSoundSetup({ action, projectDir, ui: 'umbrella' });
126
143
 
127
- // Review summary
144
+ // ── Step 5/5: review ──
128
145
  const files = [];
129
146
  if (target === 'global') files.push(configPathForScope('global', projectDir));
130
147
  if (target === 'projectOnly') {
@@ -132,12 +149,27 @@ async function main() {
132
149
  files.push(path.join(projectDir, 'claude-code-hooks.snippet.json (optional)'));
133
150
  }
134
151
 
152
+ function summarizePlan(key, plan) {
153
+ if (!plan) return `${key}: (skipped)`;
154
+ if (action === 'uninstall') return `${key}: remove managed hooks`;
155
+
156
+ const events = plan.snippetHooks ? Object.keys(plan.snippetHooks) : [];
157
+ const list = events.slice(0, 5);
158
+ const tail = events.length > 5 ? ` +${events.length - 5} more` : '';
159
+ return `${key}: ${events.length} event(s)${events.length ? ` (${list.join(', ')}${tail})` : ''}`;
160
+ }
161
+
135
162
  note(
136
163
  [
164
+ `${pc.dim('Step 5/5')} Review`,
165
+ '',
137
166
  `Action: ${pc.bold(action)}`,
138
167
  `Target: ${pc.bold(target === 'global' ? 'global settings' : 'project-only')}`,
139
- `Packages: ${pc.bold(selected.join(', '))}`,
140
- `Files:`,
168
+ '',
169
+ `${pc.bold('Packages')}`,
170
+ ...selected.map((k) => ` - ${summarizePlan(k, perPackage[k])}`),
171
+ '',
172
+ `${pc.bold('Files')}`,
141
173
  ...files.map((f) => ` - ${f}`)
142
174
  ].join('\n'),
143
175
  'Review'