@nebulit/embuilder 0.1.39
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/README.md +254 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +138 -0
- package/package.json +49 -0
- package/templates/.claude/hooks/QUICKSTART.md +256 -0
- package/templates/.claude/hooks/README.md +533 -0
- package/templates/.claude/hooks/analyze-commit.sh +22 -0
- package/templates/.claude/hooks/analyze-commit.ts +518 -0
- package/templates/.claude/hooks/analyzers/README.md +198 -0
- package/templates/.claude/hooks/analyzers/code-quality-checker.ts +154 -0
- package/templates/.claude/hooks/analyzers/code-quality.md +54 -0
- package/templates/.claude/hooks/analyzers/commit-blocker-example.ts.disabled +110 -0
- package/templates/.claude/hooks/analyzers/commit-policy.md +49 -0
- package/templates/.claude/hooks/analyzers/event-model-validator.md +49 -0
- package/templates/.claude/hooks/analyzers/event-model-validator.ts +169 -0
- package/templates/.claude/hooks/analyzers/example-logger.ts +70 -0
- package/templates/.claude/hooks/analyzers/slice-scope-validator.md +81 -0
- package/templates/.claude/hooks/check-review-result.sh +47 -0
- package/templates/.claude/hooks/prepare-review.sh +34 -0
- package/templates/.claude/hooks/review-agent-prompt.md +42 -0
- package/templates/.claude/hooks/run-review-agent.sh +124 -0
- package/templates/.claude/settings.local.json +37 -0
- package/templates/.claude/skills/help/README.md +84 -0
- package/templates/.claude/skills/help/SKILL.md +393 -0
- package/templates/.claude/skills/help/templates/demo-config.json +6753 -0
- package/templates/.claude/skills/sample-slices/SKILL.md +8 -0
- package/templates/.claude/skills/sample-slices/templates/.slices/Library/addbook/code-slice.json +124 -0
- package/templates/.claude/skills/sample-slices/templates/.slices/Library/addbook/slice.json +255 -0
- package/templates/.claude/skills/sample-slices/templates/.slices/Library/availablebooks/slice.json +107 -0
- package/templates/.claude/skills/sample-slices/templates/.slices/index.json +20 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/additem/slice.json +979 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/archiveitem/slice.json +529 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/cartitems/slice.json +1072 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/cartwithproducts/slice.json +394 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/changedprices/slice.json +88 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/changeinventory/slice.json +264 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/changeprice/slice.json +308 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/clearcart/slice.json +358 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/inventories/slice.json +203 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/publishcart/slice.json +876 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/removeitem/slice.json +560 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/submitcart/slice.json +708 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/submittedcartdata/slice.json +399 -0
- package/templates/.claude/skills/sample-slices/templates/index.json +108 -0
- package/templates/.claude/skills/slice-automation/SKILL.md +49 -0
- package/templates/.claude/skills/slice-state-change/SKILL.md +369 -0
- package/templates/.claude/skills/slice-state-change/templates/AddLocation/AddLocation.test.ts.sample +76 -0
- package/templates/.claude/skills/slice-state-change/templates/AddLocation/AddLocationCommand.ts.sample +84 -0
- package/templates/.claude/skills/slice-state-change/templates/AddLocation/routes.ts.sample +73 -0
- package/templates/.claude/skills/slice-state-change/templates/README.md +46 -0
- package/templates/.claude/skills/slice-state-view/SKILL.md +336 -0
- package/templates/.claude/skills/slice-state-view/templates/Locations/Locations.test.ts.sample +84 -0
- package/templates/.claude/skills/slice-state-view/templates/Locations/LocationsProjection.ts.sample +50 -0
- package/templates/.claude/skills/slice-state-view/templates/Locations/routes.ts.sample +46 -0
- package/templates/.claude/skills/slice-state-view/templates/README.md +109 -0
- package/templates/.claude/skills/slice-state-view/templates/Tables/Tables.test.ts.sample +104 -0
- package/templates/.claude/skills/slice-state-view/templates/Tables/TablesProjection.ts.sample +59 -0
- package/templates/.claude/skills/slice-state-view/templates/Tables/routes.ts.sample +46 -0
- package/templates/.claude/skills/slice-state-view/templates/V2__tables.sql +7 -0
- package/templates/.claude/skills/slice-state-view/templates/V8__locations.sql +7 -0
- package/templates/.claude/skills/test-analyzer/SKILL.md +373 -0
- package/templates/.claude/skills/test-analyzer/examples/specification-format.md +143 -0
- package/templates/.claude/skills/test-analyzer/examples/state-change-example.md +111 -0
- package/templates/.claude/skills/test-analyzer/examples/state-view-example.md +122 -0
- package/templates/AGENTS.md +110 -0
- package/templates/Claude.md +58 -0
- package/templates/README.md +178 -0
- package/templates/backend/.env +9 -0
- package/templates/backend/BACKEND_AUTH_SETUP.md +183 -0
- package/templates/backend/SWAGGER.md +213 -0
- package/templates/backend/eslint.config.mjs +31 -0
- package/templates/backend/flyway.conf +17 -0
- package/templates/backend/package.json +44 -0
- package/templates/backend/prd.json.example +64 -0
- package/templates/backend/public/assets/images/banner.png +0 -0
- package/templates/backend/public/assets/logo.png +0 -0
- package/templates/backend/public/file.svg +4 -0
- package/templates/backend/public/globe.svg +12 -0
- package/templates/backend/public/next.svg +6 -0
- package/templates/backend/public/vercel.svg +3 -0
- package/templates/backend/public/window.svg +5 -0
- package/templates/backend/server.ts +129 -0
- package/templates/backend/setup-env.sh +50 -0
- package/templates/backend/src/common/assertions.ts +6 -0
- package/templates/backend/src/common/db.ts +1 -0
- package/templates/backend/src/common/loadPostgresEventstore.ts +16 -0
- package/templates/backend/src/common/parseEndpoint.ts +51 -0
- package/templates/backend/src/common/replay.ts +9 -0
- package/templates/backend/src/common/routes.ts +19 -0
- package/templates/backend/src/common/testHelpers.ts +53 -0
- package/templates/backend/src/core/readmodel.ts +28 -0
- package/templates/backend/src/core/types.ts +26 -0
- package/templates/backend/src/process/process.ts +53 -0
- package/templates/backend/src/supabase/LoginHandler.ts +36 -0
- package/templates/backend/src/supabase/ProtectedPageProps.ts +21 -0
- package/templates/backend/src/supabase/README.md +171 -0
- package/templates/backend/src/supabase/api.ts +63 -0
- package/templates/backend/src/supabase/authMiddleware.ts +53 -0
- package/templates/backend/src/supabase/component.ts +12 -0
- package/templates/backend/src/supabase/requireUser.ts +72 -0
- package/templates/backend/src/supabase/serverProps.ts +25 -0
- package/templates/backend/src/supabase/staticProps.ts +10 -0
- package/templates/backend/src/swagger.ts +34 -0
- package/templates/backend/src/util/assertions.ts +6 -0
- package/templates/backend/supabase/config.toml +295 -0
- package/templates/backend/supabase/migrations/20260121155918593_catalogentries.sql.sample +23 -0
- package/templates/backend/supabase/seed.sql +1 -0
- package/templates/backend/tsconfig.json +31 -0
- package/templates/frontend/.env.development +3 -0
- package/templates/frontend/AGENTS.md +7 -0
- package/templates/frontend/README.md +73 -0
- package/templates/frontend/components.json +20 -0
- package/templates/frontend/eslint.config.js +26 -0
- package/templates/frontend/index.html +18 -0
- package/templates/frontend/package-lock.json +8347 -0
- package/templates/frontend/package.json +94 -0
- package/templates/frontend/postcss.config.js +6 -0
- package/templates/frontend/public/favicon.ico +0 -0
- package/templates/frontend/public/logo.png +0 -0
- package/templates/frontend/public/placeholder.svg +1 -0
- package/templates/frontend/public/robots.txt +14 -0
- package/templates/frontend/src/App.css +42 -0
- package/templates/frontend/src/App.tsx +47 -0
- package/templates/frontend/src/components/NavLink.tsx +28 -0
- package/templates/frontend/src/components/ProtectedRoute.tsx +24 -0
- package/templates/frontend/src/components/calendar/Calendar.tsx +302 -0
- package/templates/frontend/src/components/layout/DashboardLayout.tsx +21 -0
- package/templates/frontend/src/components/layout/Header.tsx +45 -0
- package/templates/frontend/src/components/layout/Sidebar.tsx +82 -0
- package/templates/frontend/src/components/tables/ReservationTemplates.tsx +189 -0
- package/templates/frontend/src/components/ui/accordion.tsx +52 -0
- package/templates/frontend/src/components/ui/alert-dialog.tsx +104 -0
- package/templates/frontend/src/components/ui/alert.tsx +43 -0
- package/templates/frontend/src/components/ui/aspect-ratio.tsx +5 -0
- package/templates/frontend/src/components/ui/avatar.tsx +38 -0
- package/templates/frontend/src/components/ui/badge.tsx +29 -0
- package/templates/frontend/src/components/ui/breadcrumb.tsx +90 -0
- package/templates/frontend/src/components/ui/button.tsx +47 -0
- package/templates/frontend/src/components/ui/calendar.tsx +54 -0
- package/templates/frontend/src/components/ui/card.tsx +43 -0
- package/templates/frontend/src/components/ui/carousel.tsx +224 -0
- package/templates/frontend/src/components/ui/chart.tsx +303 -0
- package/templates/frontend/src/components/ui/checkbox.tsx +26 -0
- package/templates/frontend/src/components/ui/collapsible.tsx +9 -0
- package/templates/frontend/src/components/ui/command.tsx +132 -0
- package/templates/frontend/src/components/ui/context-menu.tsx +178 -0
- package/templates/frontend/src/components/ui/dialog.tsx +95 -0
- package/templates/frontend/src/components/ui/drawer.tsx +87 -0
- package/templates/frontend/src/components/ui/dropdown-menu.tsx +179 -0
- package/templates/frontend/src/components/ui/form.tsx +129 -0
- package/templates/frontend/src/components/ui/hover-card.tsx +27 -0
- package/templates/frontend/src/components/ui/input-otp.tsx +61 -0
- package/templates/frontend/src/components/ui/input.tsx +22 -0
- package/templates/frontend/src/components/ui/label.tsx +17 -0
- package/templates/frontend/src/components/ui/menubar.tsx +207 -0
- package/templates/frontend/src/components/ui/navigation-menu.tsx +120 -0
- package/templates/frontend/src/components/ui/pagination.tsx +81 -0
- package/templates/frontend/src/components/ui/popover.tsx +29 -0
- package/templates/frontend/src/components/ui/progress.tsx +23 -0
- package/templates/frontend/src/components/ui/radio-group.tsx +36 -0
- package/templates/frontend/src/components/ui/resizable.tsx +37 -0
- package/templates/frontend/src/components/ui/scroll-area.tsx +38 -0
- package/templates/frontend/src/components/ui/select.tsx +143 -0
- package/templates/frontend/src/components/ui/separator.tsx +20 -0
- package/templates/frontend/src/components/ui/sheet.tsx +107 -0
- package/templates/frontend/src/components/ui/sidebar.tsx +637 -0
- package/templates/frontend/src/components/ui/skeleton.tsx +7 -0
- package/templates/frontend/src/components/ui/slider.tsx +23 -0
- package/templates/frontend/src/components/ui/sonner.tsx +27 -0
- package/templates/frontend/src/components/ui/stat-card.tsx +44 -0
- package/templates/frontend/src/components/ui/switch.tsx +27 -0
- package/templates/frontend/src/components/ui/table.tsx +72 -0
- package/templates/frontend/src/components/ui/tabs.tsx +53 -0
- package/templates/frontend/src/components/ui/textarea.tsx +21 -0
- package/templates/frontend/src/components/ui/toast.tsx +111 -0
- package/templates/frontend/src/components/ui/toaster.tsx +24 -0
- package/templates/frontend/src/components/ui/toggle-group.tsx +49 -0
- package/templates/frontend/src/components/ui/toggle.tsx +37 -0
- package/templates/frontend/src/components/ui/tooltip.tsx +28 -0
- package/templates/frontend/src/components/ui/use-toast.ts +3 -0
- package/templates/frontend/src/contexts/AuthContext.tsx +94 -0
- package/templates/frontend/src/contexts/RefreshContext.tsx +236 -0
- package/templates/frontend/src/hooks/api/index.ts +2 -0
- package/templates/frontend/src/hooks/api/useLocations.ts +15 -0
- package/templates/frontend/src/hooks/use-mobile.tsx +19 -0
- package/templates/frontend/src/hooks/use-toast.ts +186 -0
- package/templates/frontend/src/hooks/useApiContext.ts +11 -0
- package/templates/frontend/src/index.css +118 -0
- package/templates/frontend/src/integrations/supabase/client.ts +9 -0
- package/templates/frontend/src/lib/api-client.ts +136 -0
- package/templates/frontend/src/lib/api.ts +1028 -0
- package/templates/frontend/src/lib/utils.ts +6 -0
- package/templates/frontend/src/main.tsx +5 -0
- package/templates/frontend/src/pages/Auth.tsx +408 -0
- package/templates/frontend/src/pages/Dashboard.tsx +168 -0
- package/templates/frontend/src/pages/Menus.tsx +224 -0
- package/templates/frontend/src/pages/NotFound.tsx +24 -0
- package/templates/frontend/src/pages/Register.tsx +285 -0
- package/templates/frontend/src/test/example.test.ts +0 -0
- package/templates/frontend/src/test/setup.ts +15 -0
- package/templates/frontend/src/types/index.ts +8 -0
- package/templates/frontend/src/vite-env.d.ts +1 -0
- package/templates/frontend/tailwind.config.ts +101 -0
- package/templates/frontend/tsconfig.app.json +31 -0
- package/templates/frontend/tsconfig.json +16 -0
- package/templates/frontend/tsconfig.node.json +22 -0
- package/templates/frontend/vite.config.ts +21 -0
- package/templates/frontend/vitest.config.ts +16 -0
- package/templates/init.sh +1 -0
- package/templates/prompt.md +139 -0
- package/templates/ralph.sh +120 -0
- package/templates/server.mjs +505 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
# Git Commit Pre-Tool-Call Hook
|
|
2
|
+
|
|
3
|
+
This hook system analyzes workspace changes before git commits and provides the diff information to registered analyzer scripts.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
1. **Hook Configuration**: The hook is configured in `.claude/settings.local.json` under the `hooks.PreToolUse` section
|
|
8
|
+
2. **Trigger**: Runs automatically before any `git commit` command executed by Claude
|
|
9
|
+
3. **Analysis**: Executes the main hook script (`analyze-commit.sh`) which:
|
|
10
|
+
- Captures git diff (both staged and unstaged changes)
|
|
11
|
+
- Creates a JSON payload with diff information
|
|
12
|
+
- Passes the payload to all registered analyzer scripts
|
|
13
|
+
4. **Analyzers**: Custom TypeScript scripts process the diff and provide feedback
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
The hook is configured in `.claude/settings.local.json`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"hooks": {
|
|
22
|
+
"PreToolUse": [
|
|
23
|
+
{
|
|
24
|
+
"matcher": "Bash(*git commit*)",
|
|
25
|
+
"hooks": [
|
|
26
|
+
{
|
|
27
|
+
"type": "command",
|
|
28
|
+
"command": ".claude/hooks/analyze-commit.sh",
|
|
29
|
+
"statusMessage": "Analyzing changes before commit"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Configuration Options
|
|
39
|
+
|
|
40
|
+
- **matcher**: Pattern to match tool calls (e.g., `"Bash(*git commit*)"` for git commits)
|
|
41
|
+
- **type**: Hook type (`"command"` for shell commands)
|
|
42
|
+
- **command**: Path to the hook script
|
|
43
|
+
- **statusMessage**: Message displayed while hook runs
|
|
44
|
+
|
|
45
|
+
## Hook Payload
|
|
46
|
+
|
|
47
|
+
The hook provides a JSON payload to analyzer scripts via stdin:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"event": "pre-commit",
|
|
52
|
+
"timestamp": "2026-02-23T12:00:00Z",
|
|
53
|
+
"repository": "/path/to/repo",
|
|
54
|
+
"branch": "main",
|
|
55
|
+
"staged_diff": "diff content...",
|
|
56
|
+
"unstaged_diff": "diff content...",
|
|
57
|
+
"changed_files": ["file1.ts", "file2.ts"],
|
|
58
|
+
"stats": {
|
|
59
|
+
"staged_files": 2,
|
|
60
|
+
"has_staged_changes": true,
|
|
61
|
+
"has_unstaged_changes": false
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Creating Analyzer Scripts
|
|
67
|
+
|
|
68
|
+
Analyzer scripts are placed in `.claude/hooks/analyzers/` and can be written in:
|
|
69
|
+
- **TypeScript** (`.ts` files) - Recommended
|
|
70
|
+
- **Bash** (executable scripts)
|
|
71
|
+
|
|
72
|
+
### TypeScript Analyzer Example
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
#!/usr/bin/env node
|
|
76
|
+
|
|
77
|
+
import { readFileSync } from 'fs';
|
|
78
|
+
|
|
79
|
+
interface HookData {
|
|
80
|
+
event: string;
|
|
81
|
+
timestamp: string;
|
|
82
|
+
repository: string;
|
|
83
|
+
branch: string;
|
|
84
|
+
staged_diff: string;
|
|
85
|
+
unstaged_diff: string;
|
|
86
|
+
changed_files: string[];
|
|
87
|
+
stats: {
|
|
88
|
+
staged_files: number;
|
|
89
|
+
has_staged_changes: boolean;
|
|
90
|
+
has_unstaged_changes: boolean;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface AnalyzerResult {
|
|
95
|
+
approved: boolean;
|
|
96
|
+
reason?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function main() {
|
|
100
|
+
// Read JSON data from stdin
|
|
101
|
+
const input = readFileSync(0, 'utf-8');
|
|
102
|
+
const data: HookData = JSON.parse(input);
|
|
103
|
+
|
|
104
|
+
// Your analysis logic here
|
|
105
|
+
console.log('Analyzing commit...');
|
|
106
|
+
console.log(`Files changed: ${data.changed_files.length}`);
|
|
107
|
+
|
|
108
|
+
// Perform checks, validations, etc.
|
|
109
|
+
const hasIssues = false; // Your logic here
|
|
110
|
+
|
|
111
|
+
// IMPORTANT: Always return approval status as JSON on the last line
|
|
112
|
+
const result: AnalyzerResult = {
|
|
113
|
+
approved: !hasIssues,
|
|
114
|
+
reason: hasIssues ? 'Issues found' : 'All checks passed'
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
console.log('\n' + JSON.stringify(result));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
main();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Requirements for TypeScript Analyzers
|
|
124
|
+
|
|
125
|
+
TypeScript analyzers require either `tsx` or `ts-node`:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Install tsx (recommended - faster)
|
|
129
|
+
npm install -g tsx
|
|
130
|
+
|
|
131
|
+
# Or install ts-node
|
|
132
|
+
npm install -g ts-node
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Approval Mechanism
|
|
136
|
+
|
|
137
|
+
**IMPORTANT**: Analyzers must return a JSON result indicating approval status:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
interface AnalyzerResult {
|
|
141
|
+
approved: boolean; // true = allow commit, false = block commit
|
|
142
|
+
reason?: string; // Optional explanation
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### How It Works
|
|
147
|
+
|
|
148
|
+
1. **Analyzer runs** and performs its checks
|
|
149
|
+
2. **Analyzer outputs** any informational messages (logs, warnings, etc.)
|
|
150
|
+
3. **Analyzer returns** JSON approval status as the **last line** of output:
|
|
151
|
+
```json
|
|
152
|
+
{"approved": true, "reason": "All checks passed"}
|
|
153
|
+
```
|
|
154
|
+
4. **Hook script** parses the JSON and:
|
|
155
|
+
- If `approved: false` → **BLOCKS the commit** and shows the reason
|
|
156
|
+
- If `approved: true` → **Allows the commit** to proceed
|
|
157
|
+
|
|
158
|
+
### Example: Approving Commits
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// Analysis passed - approve the commit
|
|
162
|
+
const result: AnalyzerResult = {
|
|
163
|
+
approved: true,
|
|
164
|
+
reason: 'Code quality checks passed'
|
|
165
|
+
};
|
|
166
|
+
console.log('\n' + JSON.stringify(result));
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Example: Blocking Commits
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Critical issue found - block the commit
|
|
173
|
+
if (hasSecrets) {
|
|
174
|
+
const result: AnalyzerResult = {
|
|
175
|
+
approved: false,
|
|
176
|
+
reason: 'Potential secrets detected - verify no credentials are committed'
|
|
177
|
+
};
|
|
178
|
+
console.log('\n' + JSON.stringify(result));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### What Happens When Blocked
|
|
184
|
+
|
|
185
|
+
When any analyzer returns `approved: false`, the commit is blocked:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
❌ Commit BLOCKED by analyzers:
|
|
189
|
+
|
|
190
|
+
• code-quality-checker.ts: Potential secrets detected
|
|
191
|
+
• commit-blocker.ts: Direct commits to main not allowed
|
|
192
|
+
|
|
193
|
+
💡 Fix the issues above and try again
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The git commit command will **fail** and Claude will need to fix the issues before trying again.
|
|
197
|
+
|
|
198
|
+
## Built-in Analyzers
|
|
199
|
+
|
|
200
|
+
### 1. example-logger.ts
|
|
201
|
+
Simple logger that displays commit statistics and changed files.
|
|
202
|
+
|
|
203
|
+
**What it does:**
|
|
204
|
+
- Shows branch name
|
|
205
|
+
- Lists changed files
|
|
206
|
+
- Displays staged/unstaged status
|
|
207
|
+
|
|
208
|
+
**Approval:** Always approves (informational only)
|
|
209
|
+
|
|
210
|
+
### 2. code-quality-checker.ts
|
|
211
|
+
Checks for common code quality issues.
|
|
212
|
+
|
|
213
|
+
**Checks for:**
|
|
214
|
+
- Console.log statements
|
|
215
|
+
- Debugger statements
|
|
216
|
+
- TODO/FIXME comments
|
|
217
|
+
- Potential secrets (API keys, passwords)
|
|
218
|
+
|
|
219
|
+
**Approval:**
|
|
220
|
+
- ✅ Approves: Normal warnings (console.log, TODO, etc.)
|
|
221
|
+
- ❌ Blocks: Potential secrets detected
|
|
222
|
+
|
|
223
|
+
### 3. event-model-validator.ts
|
|
224
|
+
Validates event model slice conventions.
|
|
225
|
+
|
|
226
|
+
**Validates:**
|
|
227
|
+
- Event names use past-tense convention
|
|
228
|
+
- Test files are updated with logic changes
|
|
229
|
+
- Slice structure follows patterns
|
|
230
|
+
|
|
231
|
+
**Approval:** Always approves (provides warnings but doesn't block)
|
|
232
|
+
|
|
233
|
+
### 4. commit-blocker-example.ts.disabled
|
|
234
|
+
Example showing how to block commits (disabled by default).
|
|
235
|
+
|
|
236
|
+
**Blocks commits when:**
|
|
237
|
+
- Direct commits to main branch
|
|
238
|
+
- Too many files changed (>50)
|
|
239
|
+
- Critical files changed without tests
|
|
240
|
+
- package.json changed without lockfile update
|
|
241
|
+
|
|
242
|
+
**Enable:** Rename to `commit-blocker-example.ts` to activate
|
|
243
|
+
|
|
244
|
+
## Enabling/Disabling Analyzers
|
|
245
|
+
|
|
246
|
+
### Enable an Analyzer
|
|
247
|
+
Simply ensure the `.ts` file exists in `.claude/hooks/analyzers/`:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# Already enabled by default:
|
|
251
|
+
.claude/hooks/analyzers/example-logger.ts
|
|
252
|
+
.claude/hooks/analyzers/code-quality-checker.ts
|
|
253
|
+
.claude/hooks/analyzers/event-model-validator.ts
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Disable an Analyzer
|
|
257
|
+
Remove or rename the file:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Disable by renaming
|
|
261
|
+
mv .claude/hooks/analyzers/example-logger.ts \
|
|
262
|
+
.claude/hooks/analyzers/example-logger.ts.disabled
|
|
263
|
+
|
|
264
|
+
# Or delete
|
|
265
|
+
rm .claude/hooks/analyzers/example-logger.ts
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Creating Custom Analyzers
|
|
269
|
+
|
|
270
|
+
### Step 1: Create a new TypeScript file
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
touch .claude/hooks/analyzers/my-analyzer.ts
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Step 2: Implement your analyzer
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
#!/usr/bin/env node
|
|
280
|
+
|
|
281
|
+
import { readFileSync } from 'fs';
|
|
282
|
+
|
|
283
|
+
interface HookData {
|
|
284
|
+
event: string;
|
|
285
|
+
staged_diff: string;
|
|
286
|
+
changed_files: string[];
|
|
287
|
+
// ... other fields
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function main() {
|
|
291
|
+
const input = readFileSync(0, 'utf-8');
|
|
292
|
+
const data: HookData = JSON.parse(input);
|
|
293
|
+
|
|
294
|
+
// Your custom logic
|
|
295
|
+
console.log('🔍 My Custom Analysis:');
|
|
296
|
+
|
|
297
|
+
// Example: Check if package.json changed
|
|
298
|
+
if (data.changed_files.includes('package.json')) {
|
|
299
|
+
console.log(' ⚠️ package.json changed - remember to run npm install');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
main();
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Step 3: Test your analyzer
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
# Test manually
|
|
310
|
+
echo '{"event":"pre-commit","changed_files":["package.json"],"staged_diff":"..."}' | \
|
|
311
|
+
tsx .claude/hooks/analyzers/my-analyzer.ts
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Advanced Usage
|
|
315
|
+
|
|
316
|
+
### Blocking Commits Based on Conditions
|
|
317
|
+
|
|
318
|
+
Analyzers can block commits by returning `approved: false`. Here are common patterns:
|
|
319
|
+
|
|
320
|
+
#### Block Direct Commits to Protected Branches
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
if (data.branch === 'main' || data.branch === 'production') {
|
|
324
|
+
const result: AnalyzerResult = {
|
|
325
|
+
approved: false,
|
|
326
|
+
reason: `Direct commits to ${data.branch} are not allowed - use pull requests`
|
|
327
|
+
};
|
|
328
|
+
console.log('\n' + JSON.stringify(result));
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### Block Commits Without Tests
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
const hasLogicChanges = data.changed_files.some(f =>
|
|
337
|
+
f.endsWith('Handler.ts') || f.endsWith('Command.ts')
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const hasTestChanges = data.changed_files.some(f => f.endsWith('.test.ts'));
|
|
341
|
+
|
|
342
|
+
if (hasLogicChanges && !hasTestChanges) {
|
|
343
|
+
const result: AnalyzerResult = {
|
|
344
|
+
approved: false,
|
|
345
|
+
reason: 'Logic files changed but no tests updated - add tests'
|
|
346
|
+
};
|
|
347
|
+
console.log('\n' + JSON.stringify(result));
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### Block Commits Based on Diff Content
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
const lines = data.staged_diff.split('\n');
|
|
356
|
+
const addedLines = lines.filter(l => l.startsWith('+'));
|
|
357
|
+
|
|
358
|
+
// Block if forbidden patterns found
|
|
359
|
+
const hasForbiddenPattern = addedLines.some(line =>
|
|
360
|
+
/eval\(/.test(line) || /dangerouslySetInnerHTML/.test(line)
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
if (hasForbiddenPattern) {
|
|
364
|
+
const result: AnalyzerResult = {
|
|
365
|
+
approved: false,
|
|
366
|
+
reason: 'Forbidden patterns detected (eval, dangerouslySetInnerHTML)'
|
|
367
|
+
};
|
|
368
|
+
console.log('\n' + JSON.stringify(result));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Accessing Additional Git Information
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
import { execSync } from 'child_process';
|
|
377
|
+
|
|
378
|
+
// Get commit author
|
|
379
|
+
const author = execSync('git config user.name').toString().trim();
|
|
380
|
+
|
|
381
|
+
// Get recent commits
|
|
382
|
+
const log = execSync('git log -5 --oneline').toString();
|
|
383
|
+
|
|
384
|
+
// Check if file exists in previous commit
|
|
385
|
+
const fileExists = execSync('git ls-tree -r HEAD --name-only | grep myfile.ts').toString();
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Analyzing Specific File Types
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
// Only analyze TypeScript files
|
|
392
|
+
const tsFiles = data.changed_files.filter(f => f.endsWith('.ts'));
|
|
393
|
+
|
|
394
|
+
if (tsFiles.length === 0) {
|
|
395
|
+
console.log(' ℹ️ No TypeScript files changed');
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Analyze each TypeScript file
|
|
400
|
+
for (const file of tsFiles) {
|
|
401
|
+
// Extract changes for this specific file
|
|
402
|
+
const fileDiff = extractFileDiff(data.staged_diff, file);
|
|
403
|
+
// ... analyze
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Troubleshooting
|
|
408
|
+
|
|
409
|
+
### Hook not running
|
|
410
|
+
1. Check `.claude/settings.local.json` has the correct hook configuration
|
|
411
|
+
2. Verify the hook script is executable: `chmod +x .claude/hooks/analyze-commit.sh`
|
|
412
|
+
3. Check Claude Code permissions allow the hook to run
|
|
413
|
+
|
|
414
|
+
### TypeScript analyzers not running
|
|
415
|
+
1. Install tsx or ts-node: `npm install -g tsx`
|
|
416
|
+
2. Verify TypeScript files are in `.claude/hooks/analyzers/`
|
|
417
|
+
3. Check for syntax errors in your TypeScript files
|
|
418
|
+
|
|
419
|
+
### Analyzer fails silently
|
|
420
|
+
1. Test manually: `echo '{}' | tsx .claude/hooks/analyzers/your-analyzer.ts`
|
|
421
|
+
2. Add error handling in your analyzer
|
|
422
|
+
3. Check the JSON payload format matches your interface
|
|
423
|
+
|
|
424
|
+
## Best Practices
|
|
425
|
+
|
|
426
|
+
1. **Keep analyzers fast** - They run on every commit
|
|
427
|
+
2. **Use clear output** - Emoji and formatting help readability
|
|
428
|
+
3. **Always return approval status** - Return JSON with `approved: true/false`
|
|
429
|
+
4. **Block only critical issues** - Use `approved: false` sparingly
|
|
430
|
+
5. **Provide clear reasons** - Explain why a commit was blocked
|
|
431
|
+
6. **Test independently** - Test analyzers outside the hook first
|
|
432
|
+
7. **Handle errors gracefully** - Catch exceptions and return approval
|
|
433
|
+
8. **Document your analyzers** - Add comments explaining what they check
|
|
434
|
+
|
|
435
|
+
### When to Block vs Warn
|
|
436
|
+
|
|
437
|
+
**Block (`approved: false`) when:**
|
|
438
|
+
- Secrets/credentials detected
|
|
439
|
+
- Direct commits to protected branches
|
|
440
|
+
- Critical files changed without tests
|
|
441
|
+
- Security vulnerabilities introduced
|
|
442
|
+
- Build will definitely fail
|
|
443
|
+
|
|
444
|
+
**Warn (`approved: true`) when:**
|
|
445
|
+
- Code quality issues (console.log, TODOs)
|
|
446
|
+
- Style violations
|
|
447
|
+
- Missing documentation
|
|
448
|
+
- Potential improvements
|
|
449
|
+
- Non-critical concerns
|
|
450
|
+
|
|
451
|
+
## Examples
|
|
452
|
+
|
|
453
|
+
### Check for Breaking Changes
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
async function main() {
|
|
457
|
+
const input = readFileSync(0, 'utf-8');
|
|
458
|
+
const data: HookData = JSON.parse(input);
|
|
459
|
+
|
|
460
|
+
// Check if public API files changed
|
|
461
|
+
const apiFiles = data.changed_files.filter(f =>
|
|
462
|
+
f.includes('/api/') || f.includes('/public/')
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
if (apiFiles.length > 0) {
|
|
466
|
+
console.log('⚠️ Public API files changed:');
|
|
467
|
+
apiFiles.forEach(f => console.log(` - ${f}`));
|
|
468
|
+
console.log('💡 Consider updating CHANGELOG.md');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Enforce Commit Size Limits
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
async function main() {
|
|
477
|
+
const input = readFileSync(0, 'utf-8');
|
|
478
|
+
const data: HookData = JSON.parse(input);
|
|
479
|
+
|
|
480
|
+
const MAX_FILES = 20;
|
|
481
|
+
|
|
482
|
+
if (data.changed_files.length > MAX_FILES) {
|
|
483
|
+
console.error(`❌ Too many files changed (${data.changed_files.length} > ${MAX_FILES})`);
|
|
484
|
+
console.error('💡 Consider breaking this into smaller commits');
|
|
485
|
+
process.exit(1); // Block commit
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Integration with External Tools
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
import { execSync } from 'child_process';
|
|
494
|
+
|
|
495
|
+
async function main() {
|
|
496
|
+
const input = readFileSync(0, 'utf-8');
|
|
497
|
+
const data: HookData = JSON.parse(input);
|
|
498
|
+
|
|
499
|
+
// Run external linter
|
|
500
|
+
try {
|
|
501
|
+
execSync('npm run lint', { stdio: 'inherit' });
|
|
502
|
+
console.log('✅ Linting passed');
|
|
503
|
+
} catch (error) {
|
|
504
|
+
console.error('❌ Linting failed');
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## Future Enhancements
|
|
511
|
+
|
|
512
|
+
Potential improvements to the hook system:
|
|
513
|
+
|
|
514
|
+
- Support for post-commit hooks
|
|
515
|
+
- Parallel execution of analyzers
|
|
516
|
+
- Configuration file for analyzer settings
|
|
517
|
+
- Built-in caching for faster analysis
|
|
518
|
+
- Integration with CI/CD pipelines
|
|
519
|
+
- Automatic fix suggestions
|
|
520
|
+
|
|
521
|
+
## Contributing
|
|
522
|
+
|
|
523
|
+
To add your custom analyzer to the built-in set:
|
|
524
|
+
|
|
525
|
+
1. Create your analyzer in `.claude/hooks/analyzers/`
|
|
526
|
+
2. Test thoroughly
|
|
527
|
+
3. Document what it checks
|
|
528
|
+
4. Consider making it configurable
|
|
529
|
+
5. Share with the team
|
|
530
|
+
|
|
531
|
+
## License
|
|
532
|
+
|
|
533
|
+
These hooks are part of the EMBuilder toolkit and follow the same MIT license.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Git Commit Pre-Tool-Call Hook (Bash Wrapper)
|
|
4
|
+
# This is a minimal wrapper that calls the TypeScript implementation
|
|
5
|
+
|
|
6
|
+
# Get the directory where this script is located
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
|
|
9
|
+
# Check if tsx is available
|
|
10
|
+
if command -v tsx &> /dev/null; then
|
|
11
|
+
tsx "$SCRIPT_DIR/analyze-commit.ts"
|
|
12
|
+
exit $?
|
|
13
|
+
elif command -v ts-node &> /dev/null; then
|
|
14
|
+
ts-node "$SCRIPT_DIR/analyze-commit.ts"
|
|
15
|
+
exit $?
|
|
16
|
+
else
|
|
17
|
+
echo "❌ Error: tsx or ts-node not found"
|
|
18
|
+
echo " Install with: npm install -g tsx"
|
|
19
|
+
echo ""
|
|
20
|
+
echo " Allowing commit to proceed (fail-safe mode)"
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|