Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
description: Use Bun instead of Node.js, npm, pnpm, or vite.
globs: "*.ts, *.tsx, *.html, *.css, *.js, *.jsx, package.json"
alwaysApply: false
---

Default to using Bun instead of Node.js.

- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
- Bun automatically loads .env, so don't use dotenv.

## APIs

- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
- `Bun.redis` for Redis. Don't use `ioredis`.
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
- `WebSocket` is built-in. Don't use `ws`.
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
- Bun.$`ls` instead of execa.

## Testing

Use `bun test` to run tests.

```ts#index.test.ts
import { test, expect } from "bun:test";

test("hello world", () => {
expect(1).toBe(1);
});
```

## Frontend

Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.

Server:

```ts#index.ts
import index from "./index.html"

Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
```

HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.

```html#index.html
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
```

With the following `frontend.tsx`:

```tsx#frontend.tsx
import React from "react";

// import .css files directly and it works
import './index.css';

import { createRoot } from "react-dom/client";

const root = createRoot(document.body);

export default function Frontend() {
return <h1>Hello, world!</h1>;
}

root.render(<Frontend />);
```

Then, run index.ts

```sh
bun --hot ./index.ts
```

For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.
91 changes: 91 additions & 0 deletions .github/workflows/alpha-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Alpha Release

on:
push:
tags:
- 'v*.*.*-alpha.*'
workflow_dispatch:
inputs:
version_type:
description: 'Version bump type'
required: true
default: 'alpha'
type: choice
options:
- alpha
- alpha:patch
- alpha:minor
- alpha:major

permissions:
contents: write
id-token: write

jobs:
alpha-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'

- name: Configure Git
if: github.event_name == 'workflow_dispatch'
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"

- name: Install dependencies
run: bun install

- name: Build
run: bun run build

- name: Check TypeScript
run: bun run typecheck

- name: Create alpha version (manual trigger)
if: github.event_name == 'workflow_dispatch'
run: |
npm version prerelease --preid=alpha
git push
git push --tags
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish alpha to npm
run: npm publish --tag alpha --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Get version
id: version
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT

- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ steps.version.outputs.version }}
release_name: v${{ steps.version.outputs.version }}
body: |
πŸ§ͺ **Alpha Release** - This is a pre-release version for testing purposes.

Install with: `npm install @timoaus/define-claude-code-hooks@alpha`

**⚠️ Warning**: Alpha versions may contain breaking changes and are not recommended for production use.
draft: false
prerelease: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ hook-log.stop.json
hook-log.tool-use.json
.claude/settings.local.json
.hooks/
tmp/
34 changes: 29 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,17 @@ bun install
- For PreToolUse/PostToolUse: One entry per matcher
- For other hooks (Stop, Notification, SubagentStop): One entry only if handlers exist

5. **Multiple Hook Files**: The system supports three different hook files, all located in `.claude/hooks/`:
5. **Multiple Hook Files**: The system supports two different hook files, all located in `.claude/hooks/`:
- `hooks.ts` - Project hooks (compiles to `.hooks/hooks.js`, updates `.claude/settings.json`)
- `hooks.local.ts` - Local hooks (compiles to `.hooks/hooks.local.js`, updates `.claude/settings.local.json`)
- `hooks.user.ts` - User hooks (compiles to `.hooks/hooks.user.js`, updates `~/.claude/settings.json`)

The CLI automatically detects which files exist, compiles them, and updates the corresponding settings files.

### Core Components

- **src/index.ts**: Exports `defineHooks` and `defineHook` functions. Contains the self-execution logic that makes hooks files act as their own runners.

- **src/cli.ts**: The CLI that compiles TypeScript hooks to JavaScript and updates settings.json files. It automatically detects which hook files exist (hooks.ts, hooks.local.ts, hooks.user.ts), compiles them to `.hooks/`, and updates the corresponding settings files.
- **src/cli.ts**: The CLI that compiles TypeScript hooks to JavaScript and updates settings.json files. It automatically detects which hook files exist (hooks.ts, hooks.local.ts), compiles them to `.hooks/`, and updates the corresponding settings files.

- **src/types.ts**: TypeScript type definitions for all hook types, inputs, and outputs. Key distinction between tool hooks (PreToolUse/PostToolUse) that have matchers and non-tool hooks.

Expand Down Expand Up @@ -94,20 +93,24 @@ Note that commands reference the compiled JavaScript files in `.hooks/`, not the
- Hook source files are all located in `.claude/hooks/`:
- `hooks.ts` (project-wide hooks)
- `hooks.local.ts` (local-only hooks, not committed to git)
- `hooks.user.ts` (user-specific hooks that update ~/.claude/settings.json)
- Compiled JavaScript files are generated in `.hooks/`:
- `.hooks/hooks.js`
- `.hooks/hooks.local.js`
- `.hooks/hooks.user.js`
- The `.hooks/` directory is gitignored
- Compatible with npm, yarn, pnpm, and bun package managers
- The CLI no longer requires flags - it automatically detects which hook files exist, compiles them, and updates the appropriate settings files
- No runtime dependency on ts-node - hooks execute as pure JavaScript

### Testing

When testing the package, create test repositories in the `tmp/` folder in the project root. This folder is already in `.gitignore` so test projects won't be committed.

## Release Process

The project uses automated releases via GitHub Actions. To release a new version:

### Regular Releases

1. **Ensure all changes are committed** and tests pass: `bun run test:run`

2. **Run the release script** based on the type of change:
Expand All @@ -126,6 +129,27 @@ The project uses automated releases via GitHub Actions. To release a new version
- Run type checking
- Publish to npm registry with provenance

### Alpha Releases

For testing new features before a stable release:

1. **Run the alpha release script**:
- Alpha release (incremental): `npm run release:alpha`
- Alpha patch: `npm run release:alpha:patch`
- Alpha minor: `npm run release:alpha:minor`
- Alpha major: `npm run release:alpha:major`

2. **Or trigger manually via GitHub Actions**:
- Go to Actions β†’ Alpha Release β†’ Run workflow
- Select the version bump type
- This will create and publish an alpha version

3. **Alpha versions**:
- Are published with the `alpha` tag on npm
- Can be installed with: `npm install @timoaus/define-claude-code-hooks@alpha`
- Are marked as pre-releases on GitHub
- Follow the pattern: `1.2.3-alpha.0`, `1.2.3-alpha.1`, etc.

The package is published under the `@timoaus` scope as `@timoaus/define-claude-code-hooks`.

Note: The repository must have the `NPM_TOKEN` secret configured for publishing.
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,22 @@ Type-safe hook definitions for Claude Code with automatic settings management.

## Quick Start

### 1. Install the package
### Option 1: Use the Interactive Init Command (Recommended)

```bash
npx @timoaus/define-claude-code-hooks --init
```

This interactive command will:
- Let you choose between project or local hooks
- Install predefined hooks (logging, security, announcements)
- Install the package as a dev dependency
- Add the `claude:hooks` script to your package.json
- Set up your hooks automatically

### Option 2: Manual Setup

#### 1. Install the package

```bash
npm install --save-dev @timoaus/define-claude-code-hooks
Expand All @@ -16,7 +31,7 @@ pnpm add --save-dev @timoaus/define-claude-code-hooks
bun add --dev @timoaus/define-claude-code-hooks
```

### 2. Add a package.json script
#### 2. Add a package.json script

Add this script to your `package.json` to easily update your hooks:

Expand All @@ -28,13 +43,12 @@ Add this script to your `package.json` to easily update your hooks:
}
```

### 3. Create a simple hook
#### 3. Create a simple hook

You can create hooks in three different files within `.claude/hooks/`:
You can create hooks in two different files within `.claude/hooks/`:

- `hooks.ts` - Project hooks (updates `.claude/settings.json`)
- `hooks.local.ts` - Local hooks (updates `.claude/settings.local.json`)
- `hooks.user.ts` - User hooks (updates `~/.claude/settings.json`)

For example, create `.claude/hooks/hooks.ts`:

Expand Down Expand Up @@ -138,7 +152,6 @@ Choose where to create your hooks based on your needs (all in `.claude/hooks/`):

- `hooks.ts` - Project-wide hooks (committed to git)
- `hooks.local.ts` - Local-only hooks (not committed)
- `hooks.user.ts` - User-specific hooks (updates ~/.claude/settings.json)

Example:

Expand Down Expand Up @@ -208,9 +221,7 @@ The CLI automatically detects which hook files exist and updates the correspondi

- `hooks.ts` β†’ `.claude/settings.json` (project settings, relative paths)
- `hooks.local.ts` β†’ `.claude/settings.local.json` (local settings, relative paths)
- `hooks.user.ts` β†’ `~/.claude/settings.json` (user settings, absolute paths)

**Note:** If your global Claude settings.json is not in the default location (`~/.claude/settings.json`), use the `--global-settings-path` option to specify the correct path.

## API

Expand Down Expand Up @@ -272,7 +283,7 @@ interface HookOutput {

## How It Works

1. The CLI scans for hook files (hooks.ts, hooks.local.ts, hooks.user.ts)
1. The CLI scans for hook files (hooks.ts, hooks.local.ts)
2. For each file found, it updates the corresponding settings.json with commands that use ts-node to execute TypeScript directly
3. Marks managed hooks so they can be safely removed later

Expand Down
Loading