Skip to content

Commit 4d6f1b3

Browse files
hubyrodclaude
andcommitted
Update CLAUDE.md and README.md to reflect current architecture
Replace generic Bun template in CLAUDE.md with project-specific guidance covering architecture, commands, and config system. Update README config example to show group-based and direct stream routes, add issue_comment to supported events, and fix env var references. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 28f3cb4 commit 4d6f1b3

2 files changed

Lines changed: 74 additions & 93 deletions

File tree

CLAUDE.md

Lines changed: 55 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,75 @@
1+
# CLAUDE.md
12

2-
Default to using Bun instead of Node.js.
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
34

4-
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
5-
- Use `bun test` instead of `jest` or `vitest`
6-
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
7-
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
8-
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
9-
- Use `bunx <package> <command>` instead of `npx <package> <command>`
10-
- Bun automatically loads .env, so don't use dotenv.
5+
## What This Is
116

12-
## APIs
7+
Skiphooks is a GitHub webhook server that forwards repository events (PRs, issues, pushes, releases, comments) to Slashwork streams via GraphQL. Built with Bun — no Node.js, no Express, no runtime dependencies.
138

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

22-
## Testing
11+
```sh
12+
bun install # Install dependencies
13+
bun run dev # Dev server with --watch
14+
bun run start # Production server
15+
bun test # Run all tests
16+
bun test --filter "handler" # Run specific tests
17+
bunx tsc --noEmit # Type-check without emitting
18+
./test-webhook.sh skipper # Send test webhook to a route
19+
./test-webhook.sh skjs http://localhost:3000 # Custom base URL
20+
```
2321

24-
Use `bun test` to run tests.
22+
## Bun Rules
2523

26-
```ts#index.test.ts
27-
import { test, expect } from "bun:test";
24+
Default to Bun for everything. Bun auto-loads `.env` — don't use dotenv.
2825

29-
test("hello world", () => {
30-
expect(1).toBe(1);
31-
});
32-
```
26+
- `Bun.serve()` for HTTP — don't use Express
27+
- `bun:test` for testing — don't use Jest/Vitest
28+
- `Bun.file` over `node:fs` readFile/writeFile
29+
- `bun install` / `bun run` / `bunx` — not npm/yarn/npx
3330

34-
## Frontend
35-
36-
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
37-
38-
Server:
39-
40-
```ts#index.ts
41-
import index from "./index.html"
42-
43-
Bun.serve({
44-
routes: {
45-
"/": index,
46-
"/api/users/:id": {
47-
GET: (req) => {
48-
return new Response(JSON.stringify({ id: req.params.id }));
49-
},
50-
},
51-
},
52-
// optional websocket support
53-
websocket: {
54-
open: (ws) => {
55-
ws.send("Hello, world!");
56-
},
57-
message: (ws, message) => {
58-
ws.send(message);
59-
},
60-
close: (ws) => {
61-
// handle close
62-
}
63-
},
64-
development: {
65-
hmr: true,
66-
console: true,
67-
}
68-
})
69-
```
31+
## Architecture
7032

71-
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.
33+
**Request flow:** `Bun.serve() → route match → signature verify → handler dispatch → format markdown → GraphQL post`
7234

73-
```html#index.html
74-
<html>
75-
<body>
76-
<h1>Hello, world!</h1>
77-
<script type="module" src="./frontend.tsx"></script>
78-
</body>
79-
</html>
80-
```
35+
### Entry point: `src/index.ts`
8136

82-
With the following `frontend.tsx`:
37+
`Bun.serve()` matches URLs via regex `/github/<route-name>`. Routes are defined in root `config.ts`. On each request: verify HMAC-SHA256 signature, look up handler by `x-github-event` header, check action relevance, format to markdown, POST to Slashwork GraphQL.
8338

84-
```tsx#frontend.tsx
85-
import React from "react";
86-
import { createRoot } from "react-dom/client";
39+
### Config: root `config.ts` + `src/config.ts`
8740

88-
// import .css files directly and it works
89-
import './index.css';
41+
Two-level config system:
42+
- **`src/config.ts`** — types (`SkiphooksConfig`, `GroupConfig`, `RouteConfig`) and `loadConfig()` with validation
43+
- **Root `config.ts`** — runtime config instance with actual values
9044

91-
const root = createRoot(document.body);
45+
Routes support two modes:
46+
- **Group-based:** `{ group: "skipper" }` — references a named group in `config.groups` that provides `id` and `authToken`
47+
- **Direct stream:** `{ streamId: "...", authToken: "..." }` — standalone stream config
9248

93-
export default function Frontend() {
94-
return <h1>Hello, world!</h1>;
95-
}
49+
### Handlers: `src/handlers/`
9650

97-
root.render(<Frontend />);
98-
```
51+
Each handler implements `EventHandler` interface (`types.ts`):
52+
- `isRelevantAction(action?)` — filters which GitHub actions to process
53+
- `format(payload)` — returns `{ markdown: string }` for Slashwork
9954

100-
Then, run index.ts
55+
Handlers: `pull-request.ts`, `issues.ts`, `issue-comment.ts`, `push.ts`, `release.ts`
10156

102-
```sh
103-
bun --hot ./index.ts
104-
```
57+
### Slashwork client: `src/slashwork.ts`
58+
59+
GraphQL client with Bearer token auth. `postToSlashwork()` sends formatted markdown to a target stream/group. `validateConnection()` checks auth on startup.
60+
61+
### Webhook verification: `src/webhook.ts`
62+
63+
HMAC-SHA256 signature verification using `node:crypto`. Compares `x-hub-signature-256` header against computed hash.
64+
65+
## Environment Variables
66+
67+
See `.env.example`. Key vars:
68+
- `GITHUB_WEBHOOK_SECRET` — shared secret for webhook signature verification
69+
- `SLASHWORK_GRAPHQL_URL` — GraphQL endpoint
70+
- `SLASHWORK_AUTH_TOKEN_SKIPPER` / `SLASHWORK_AUTH_TOKEN_SKJS` — per-group auth tokens
71+
- `PORT` — server port (default 3000)
72+
73+
## Deployment
10574

106-
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
75+
Clever Cloud via GitHub Actions. Push to `main` → typecheck + tests → auto-deploy.

README.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ A GitHub webhook server that posts repository events to Slashwork via their Grap
88
|---|---|
99
| `pull_request` | opened, closed/merged, review_requested, ready_for_review, synchronize |
1010
| `issues` | opened, closed, reopened, labeled, assigned |
11+
| `issue_comment` | created |
1112
| `push` | all pushes (no action filter) |
1213
| `release` | published, created, edited |
1314

@@ -35,7 +36,7 @@ cp .env.example .env
3536

3637
## Configuration
3738

38-
Event routing is configured in `config.ts` at the project root. Each event type maps to a Slashwork group ID:
39+
Event routing is configured in `config.ts` at the project root:
3940

4041
```ts
4142
import type { SkiphooksConfig } from "./src/config.ts";
@@ -46,11 +47,21 @@ const config: SkiphooksConfig = {
4647
},
4748
slashwork: {
4849
graphqlUrl: process.env.SLASHWORK_GRAPHQL_URL!,
49-
authToken: process.env.SLASHWORK_AUTH_TOKEN!,
50+
},
51+
groups: {
52+
myproject: {
53+
id: "g_abc123",
54+
authToken: process.env.SLASHWORK_AUTH_TOKEN_MYPROJECT!,
55+
},
5056
},
5157
routes: {
52-
"my-project": { groupId: "group-abc-123" },
53-
"releases": { groupId: "group-def-456" },
58+
// Group-based route — references a named group for its ID and auth token
59+
myproject: { group: "myproject" },
60+
// Direct stream route — specifies stream ID and auth token inline
61+
releases: {
62+
streamId: "g_def456",
63+
authToken: process.env.SLASHWORK_AUTH_TOKEN_RELEASES!,
64+
},
5465
},
5566
};
5667

@@ -59,14 +70,15 @@ export default config;
5970

6071
- **Secrets** stay in `.env` and are referenced via `process.env`.
6172
- **Routes** are path-based — each route name becomes a `/github/<name>` endpoint. Only configured routes are active — requests to unknown routes return 404.
73+
- **Groups** let multiple routes share the same auth token and target ID. Routes can reference a group by name or specify `streamId`/`authToken` directly.
6274

6375
## Slashwork Configuration
6476

6577
See the [Slashwork Developer docs](https://slashwork.com/developer) for full details.
6678

6779
1. Create a **stream** (or use an existing one) where notifications will be posted.
68-
2. Create an **application** with a dedicated auth token — this goes in `SLASHWORK_AUTH_TOKEN`.
69-
3. Find the **group ID** of your target stream(s) from their URLs — these go in `config.ts` routes.
80+
2. Create an **application** with a dedicated auth token — this goes in a `SLASHWORK_AUTH_TOKEN_*` env var.
81+
3. Find the **group ID** of your target stream(s) from their URLs — these go in `config.ts` groups or routes.
7082
4. Set `SLASHWORK_GRAPHQL_URL` to `https://<your-instance>.slashwork.com/api/graphql`.
7183

7284
## GitHub Webhook Setup
@@ -109,6 +121,6 @@ Deployed to [Clever Cloud](https://clever-cloud.com) via GitHub Actions. Every p
109121

110122
- **Signature mismatch (401):** Ensure `GITHUB_WEBHOOK_SECRET` matches exactly between GitHub and your `.env`. Check for trailing whitespace.
111123
- **Posts not appearing:** Verify group IDs in `config.ts` point to valid streams. Check server logs for GraphQL errors.
112-
- **Token issues:** Ensure `SLASHWORK_AUTH_TOKEN` has write permissions to the target groups.
124+
- **Token issues:** Ensure your `SLASHWORK_AUTH_TOKEN_*` env vars have write permissions to the target groups.
113125
- **No events received:** Confirm the webhook is active in GitHub (Settings → Webhooks) and the desired events are selected.
114126
- **Event ignored:** Check that the route exists in `config.ts` and the action is one of the supported actions listed above.

0 commit comments

Comments
 (0)