hookstack-cli 0.1.7 → 0.1.9

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 (3) hide show
  1. package/README.md +1 -1
  2. package/bin/cli.mjs +26 -15
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Install Claude Code hooks in one command.**
4
4
 
5
- [hookstack.vercel.app](https://hookstack.vercel.app) — browse 66+ community-vetted lifecycle hooks for Claude Code & GitHub Copilot, then run this CLI to wire them into your project.
5
+ [hookstack.vercel.app](https://hookstack.vercel.app) — browse a growing catalogue of Claude Code hooks, then run this CLI to wire them into your project.
6
6
 
7
7
  ---
8
8
 
package/bin/cli.mjs CHANGED
@@ -21,7 +21,9 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
21
21
  const VERSION = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8')).version
22
22
 
23
23
  async function fetchHooks(slugs) {
24
- const url = `${API_BASE}/api/hooks?slugs=${slugs.map(encodeURIComponent).join(',')}`
24
+ const url = slugs.length === 0
25
+ ? `${API_BASE}/api/hooks`
26
+ : `${API_BASE}/api/hooks?slugs=${slugs.map(encodeURIComponent).join(',')}`
25
27
  const res = await fetch(url)
26
28
  if (!res.ok) {
27
29
  const body = await res.text().catch(() => '')
@@ -125,7 +127,8 @@ async function interactiveInstall(slugs, args) {
125
127
  p.intro(pc.bgCyan(pc.black(' hookstack-cli ')))
126
128
 
127
129
  const s = p.spinner()
128
- s.start(`Fetching ${plural(slugs.length, 'hook')}`)
130
+ const isDefault = slugs.length === 0
131
+ s.start(isDefault ? 'Fetching default HookStack…' : `Fetching ${plural(slugs.length, 'hook')}`)
129
132
  let data
130
133
  try {
131
134
  data = await fetchHooks(slugs)
@@ -136,33 +139,39 @@ async function interactiveInstall(slugs, args) {
136
139
  }
137
140
  const { hooks } = data
138
141
  const notFound = slugs.filter(slug => !hooks.find(h => h.slug === slug))
139
- s.stop(`Fetched ${plural(hooks.length, 'hook')}`)
142
+ s.stop(isDefault
143
+ ? `Default HookStack — ${plural(hooks.length, 'hook')}`
144
+ : `Fetched ${plural(hooks.length, 'hook')}`)
140
145
  if (notFound.length) p.log.warn(`Unknown slugs skipped: ${notFound.join(', ')}`)
141
146
  if (hooks.length === 0) {
142
147
  p.cancel('No hooks to install.')
143
148
  process.exit(1)
144
149
  }
145
150
 
151
+ if (isDefault) {
152
+ p.log.info(`The default HookStack gives your Claude Code setup ${plural(hooks.length, 'battle-tested hook')} covering security, context, validation and workflow.`)
153
+ }
154
+
146
155
  const scope = await p.select({
147
- message: 'Installation scope',
156
+ message: 'Where do you want to install?',
148
157
  initialValue: args.scope,
149
158
  options: [
150
- { value: 'project', label: 'Project', hint: './.claude — committed with your project' },
151
- { value: 'global', label: 'Global', hint: '~/.claude — every project on this machine' },
159
+ { value: 'project', label: 'This project', hint: './.claude — committed with your repo' },
160
+ { value: 'global', label: 'All my projects', hint: '~/.claude — every project on this machine' },
152
161
  ],
153
162
  })
154
163
  if (p.isCancel(scope)) { p.cancel('Cancelled.'); process.exit(0) }
155
164
 
156
165
  const dirs = resolveScopeRoot(scope, { cwd: process.cwd(), home: homedir() })
157
166
 
158
- p.note(summaryPanel(buildSummaryRows(hooks, { root: dirs.root })), 'Installation Summary')
167
+ p.note(summaryPanel(buildSummaryRows(hooks, { root: dirs.root })), `${plural(hooks.length, 'Hook')} to install`)
159
168
  p.note(securityPanel(buildSecurityRows(hooks)), 'Security')
160
169
 
161
170
  const ok = await p.confirm({ message: `Install ${plural(hooks.length, 'hook')} into ${scope === 'global' ? '~/.claude' : './.claude'}?` })
162
171
  if (p.isCancel(ok) || !ok) { p.cancel('Cancelled.'); process.exit(0) }
163
172
 
164
173
  const s2 = p.spinner()
165
- s2.start('Installing')
174
+ s2.start('Installing')
166
175
  let result
167
176
  try {
168
177
  result = doInstall(hooks, dirs, scope, p.log)
@@ -173,12 +182,14 @@ async function interactiveInstall(slugs, args) {
173
182
  }
174
183
  s2.stop(`Wrote ${plural(result.scriptCount, 'script')} + patched settings.json`)
175
184
 
185
+ p.log.info(`Browse more hooks → ${pc.cyan(`${API_BASE}/#catalogue`)}`)
176
186
  p.log.info(`⭐ star us → ${pc.cyan(REPO_URL)}`)
177
- p.outro(pc.green(`✓ Installed ${plural(result.hookCount, 'hook')} — restart Claude Code to activate.`))
187
+ p.outro(pc.green(`✓ ${plural(result.hookCount, 'hook')} installed — restart Claude Code to activate.`))
178
188
  }
179
189
 
180
190
  async function directInstall(slugs, args) {
181
- console.log(`\nFetching ${plural(slugs.length, 'hook')}…`)
191
+ const isDefault = slugs.length === 0
192
+ console.log(isDefault ? '\nInstalling default HookStack…' : `\nFetching ${plural(slugs.length, 'hook')}…`)
182
193
  let data
183
194
  try {
184
195
  data = await fetchHooks(slugs)
@@ -205,25 +216,25 @@ const HELP = `
205
216
  hookstack — Claude Code hook installer
206
217
 
207
218
  Usage:
208
- npx hookstack-cli@latest install --hooks=<slug1>,<slug2>,...
219
+ npx hookstack-cli@latest install # install the default HookStack
220
+ npx hookstack-cli@latest install --hooks=<slug1>,<slug2>,... # custom selection
209
221
 
210
222
  Options:
211
- --hooks <slugs> Comma-separated list of hook slugs
223
+ --hooks <slugs> Comma-separated list of hook slugs (omit for default set)
212
224
  --global, -g Install into ~/.claude instead of ./.claude
213
225
  --scope <s> "project" (default) or "global"
214
226
  --yes, -y Skip prompts (non-interactive install)
215
227
  --version, -v Show version
216
228
  --help, -h Show this help
217
229
 
218
- Runs interactively in a terminal; falls back to a direct install when piped
219
- or when --yes is passed. Browse hooks at https://hookstack.vercel.app
230
+ Browse hooks at https://hookstack.vercel.app
220
231
  `
221
232
 
222
233
  async function main() {
223
234
  const args = parseArgs(process.argv)
224
235
 
225
236
  if (args.version) { console.log(VERSION); return }
226
- if (args.help || args.hooks.length === 0) { console.log(HELP); return }
237
+ if (args.help) { console.log(HELP); return }
227
238
 
228
239
  const command = args.command ?? 'install'
229
240
  if (command !== 'install') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hookstack-cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "CLI installer for the Hookstack catalogue of Claude Code hooks",
5
5
  "type": "module",
6
6
  "bin": {