@claude-code-hooks/cli 0.1.1 → 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.
- package/package.json +5 -5
- package/src/cli.js +76 -44
- package/src/snippet.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-code-hooks/cli",
|
|
3
|
-
"version": "0.1.
|
|
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",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@clack/prompts": "^1.0.0",
|
|
30
|
-
"@claude-code-hooks/core": "0.1.
|
|
31
|
-
"@claude-code-hooks/security": "0.1.
|
|
32
|
-
"@claude-code-hooks/secrets": "0.1.
|
|
33
|
-
"@claude-code-hooks/sound": "0.2.
|
|
30
|
+
"@claude-code-hooks/core": "0.1.2",
|
|
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
|
@@ -64,13 +64,13 @@ async function ensureProjectOnlyConfig(projectDir, selected, perPackageConfig) {
|
|
|
64
64
|
|
|
65
65
|
async function maybeWriteSnippet(projectDir, snippetObj) {
|
|
66
66
|
const ok = await confirm({
|
|
67
|
-
message: `Write snippet file to ${pc.bold(path.join(projectDir, 'claude-hooks.snippet.json'))}?`,
|
|
67
|
+
message: `Write snippet file to ${pc.bold(path.join(projectDir, 'claude-code-hooks.snippet.json'))}?`,
|
|
68
68
|
initialValue: false
|
|
69
69
|
});
|
|
70
70
|
if (isCancel(ok)) return;
|
|
71
71
|
if (!ok) return;
|
|
72
72
|
|
|
73
|
-
const filePath = path.join(projectDir, 'claude-hooks.snippet.json');
|
|
73
|
+
const filePath = path.join(projectDir, 'claude-code-hooks.snippet.json');
|
|
74
74
|
await fs.writeFile(filePath, JSON.stringify(snippetObj, null, 2) + '\n');
|
|
75
75
|
note(filePath, 'Wrote snippet');
|
|
76
76
|
}
|
|
@@ -83,61 +83,93 @@ async function main() {
|
|
|
83
83
|
|
|
84
84
|
intro('claude-code-hooks');
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
//
|
|
144
|
+
// ── Step 5/5: review ──
|
|
128
145
|
const files = [];
|
|
129
146
|
if (target === 'global') files.push(configPathForScope('global', projectDir));
|
|
130
147
|
if (target === 'projectOnly') {
|
|
131
148
|
files.push(path.join(projectDir, CONFIG_FILENAME));
|
|
132
|
-
files.push(path.join(projectDir, 'claude-hooks.snippet.json (optional)'));
|
|
149
|
+
files.push(path.join(projectDir, 'claude-code-hooks.snippet.json (optional)'));
|
|
150
|
+
}
|
|
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})` : ''}`;
|
|
133
160
|
}
|
|
134
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
|
-
|
|
140
|
-
|
|
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'
|
|
@@ -151,7 +183,7 @@ async function main() {
|
|
|
151
183
|
const s = spinner();
|
|
152
184
|
s.start('Writing project config...');
|
|
153
185
|
|
|
154
|
-
// perPackage.*.projectConfigSection is shaped for claude-hooks.config.json sections.
|
|
186
|
+
// perPackage.*.projectConfigSection is shaped for claude-code-hooks.config.json sections.
|
|
155
187
|
const projectCfg = await ensureProjectOnlyConfig(projectDir, selected, {
|
|
156
188
|
security: perPackage.security?.projectConfigSection,
|
|
157
189
|
secrets: perPackage.secrets?.projectConfigSection,
|
package/src/snippet.js
CHANGED
|
@@ -19,7 +19,7 @@ export function buildSettingsSnippet({ projectDir, selected, packagePlans }) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// Include a comment-like pointer to project config path (JSON doesn't support comments, so we use a metadata key).
|
|
22
|
-
const cfgPath = path.join(projectDir, 'claude-hooks.config.json');
|
|
22
|
+
const cfgPath = path.join(projectDir, 'claude-code-hooks.config.json');
|
|
23
23
|
|
|
24
24
|
return {
|
|
25
25
|
"__generated_by": "@claude-code-hooks/cli",
|