start-vibing-stacks 1.0.0
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 +100 -0
- package/dist/detector.d.ts +16 -0
- package/dist/detector.js +103 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +239 -0
- package/dist/installer.d.ts +30 -0
- package/dist/installer.js +224 -0
- package/dist/setup.d.ts +17 -0
- package/dist/setup.js +161 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.js +4 -0
- package/dist/ui.d.ts +11 -0
- package/dist/ui.js +47 -0
- package/package.json +57 -0
- package/stacks/_shared/agents/claude-md-compactor.md +44 -0
- package/stacks/_shared/agents/commit-manager.md +65 -0
- package/stacks/_shared/agents/documenter.md +82 -0
- package/stacks/_shared/agents/domain-updater.md +51 -0
- package/stacks/_shared/agents/research-web.md +60 -0
- package/stacks/_shared/agents/tester.md +61 -0
- package/stacks/_shared/commands/feature.md +13 -0
- package/stacks/_shared/commands/fix.md +9 -0
- package/stacks/_shared/commands/validate.md +10 -0
- package/stacks/_shared/config/domain-mapping.json +41 -0
- package/stacks/_shared/config/security-rules.json +31 -0
- package/stacks/_shared/hooks/stop-validator.ts +171 -0
- package/stacks/_shared/hooks/user-prompt-submit.ts +77 -0
- package/stacks/_shared/skills/debugging-patterns/SKILL.md +39 -0
- package/stacks/_shared/skills/docker-patterns/SKILL.md +47 -0
- package/stacks/_shared/skills/git-workflow/SKILL.md +35 -0
- package/stacks/nodejs/stack.json +87 -0
- package/stacks/php/config/quality-gates.json +23 -0
- package/stacks/php/skills/composer-workflow/SKILL.md +78 -0
- package/stacks/php/skills/php-patterns/SKILL.md +119 -0
- package/stacks/php/skills/phpstan-analysis/SKILL.md +68 -0
- package/stacks/php/skills/phpunit-testing/SKILL.md +122 -0
- package/stacks/php/skills/security-scan-php/SKILL.md +80 -0
- package/stacks/php/stack.json +95 -0
- package/templates/CLAUDE-default.md +54 -0
- package/templates/CLAUDE-php.md +88 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: commit-manager
|
|
3
|
+
description: "AUTOMATICALLY invoke as FINAL AGENT when implementation is complete. Creates conventional commits, merges to main."
|
|
4
|
+
model: haiku
|
|
5
|
+
tools: Read, Write, Edit, Bash, Grep, Glob
|
|
6
|
+
skills: git-workflow
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Commit Manager Agent
|
|
10
|
+
|
|
11
|
+
You manage commits, merges, and are the FINAL agent in the workflow.
|
|
12
|
+
|
|
13
|
+
## Workflow Order
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
domain-updater → commit-manager (YOU)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Complete Git Flow
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 1. Check status
|
|
23
|
+
git status && git diff --name-status
|
|
24
|
+
|
|
25
|
+
# 2. Stage files
|
|
26
|
+
git add -A
|
|
27
|
+
|
|
28
|
+
# 3. Create commit
|
|
29
|
+
git commit -m "type(scope): description
|
|
30
|
+
|
|
31
|
+
Generated with Claude Code
|
|
32
|
+
Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
33
|
+
|
|
34
|
+
# 4. Switch to main
|
|
35
|
+
git checkout main
|
|
36
|
+
|
|
37
|
+
# 5. Merge branch
|
|
38
|
+
git merge [branch-name]
|
|
39
|
+
|
|
40
|
+
# 6. Sync with remote
|
|
41
|
+
git pull origin main --rebase || true
|
|
42
|
+
git push origin main
|
|
43
|
+
|
|
44
|
+
# 7. Delete feature branch
|
|
45
|
+
git branch -d [branch-name]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Conventional Commits
|
|
49
|
+
|
|
50
|
+
| Type | Use |
|
|
51
|
+
|----------|---------------|
|
|
52
|
+
| feat | New feature |
|
|
53
|
+
| fix | Bug fix |
|
|
54
|
+
| docs | Documentation |
|
|
55
|
+
| test | Tests |
|
|
56
|
+
| refactor | Code change |
|
|
57
|
+
| chore | Maintenance |
|
|
58
|
+
|
|
59
|
+
## Critical Rules
|
|
60
|
+
|
|
61
|
+
1. **NEVER commit without validators passing**
|
|
62
|
+
2. **ALWAYS conventional commits**
|
|
63
|
+
3. **NEVER force push main**
|
|
64
|
+
4. **ALWAYS merge to main** — direct merge, no PRs
|
|
65
|
+
5. **ALWAYS end on main branch**
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: documenter
|
|
3
|
+
description: "AUTOMATICALLY invoke AFTER any code implementation. Creates/updates domain documentation. PROACTIVELY runs after implementation."
|
|
4
|
+
model: sonnet
|
|
5
|
+
tools: Read, Write, Edit, Grep, Glob, Bash
|
|
6
|
+
skills: docs-tracker, codebase-knowledge
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Documenter Agent
|
|
10
|
+
|
|
11
|
+
You create and maintain domain documentation so Claude doesn't need to re-explore the codebase every session.
|
|
12
|
+
|
|
13
|
+
## Step-by-Step
|
|
14
|
+
|
|
15
|
+
### 1. Read Stack Config
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cat .claude/config/active-project.json # Know the stack, extensions
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Detect Changed Files
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
git diff --name-only HEAD
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 3. Map Files to Domains
|
|
28
|
+
|
|
29
|
+
Read `.claude/config/domain-mapping.json` for patterns.
|
|
30
|
+
|
|
31
|
+
### 4. For Each Affected Domain
|
|
32
|
+
|
|
33
|
+
**If exists** → Update "Last Update", add files, add commit
|
|
34
|
+
**If not** → CREATE from template
|
|
35
|
+
|
|
36
|
+
### 5. Domain File Template
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
# {Domain Name}
|
|
40
|
+
|
|
41
|
+
## Last Update
|
|
42
|
+
- **Date:** {YYYY-MM-DD}
|
|
43
|
+
- **Commit:** {hash}
|
|
44
|
+
- **Summary:** {what changed}
|
|
45
|
+
|
|
46
|
+
## Files
|
|
47
|
+
|
|
48
|
+
| File | Purpose |
|
|
49
|
+
|------|---------|
|
|
50
|
+
| `path/file.ext` | Description |
|
|
51
|
+
|
|
52
|
+
## Connections
|
|
53
|
+
|
|
54
|
+
| Domain | How They Connect |
|
|
55
|
+
|--------|-----------------|
|
|
56
|
+
| {domain} | {description} |
|
|
57
|
+
|
|
58
|
+
## Recent Commits
|
|
59
|
+
|
|
60
|
+
| Hash | Date | Description |
|
|
61
|
+
|------|------|-------------|
|
|
62
|
+
| abc123 | YYYY-MM-DD | feat: description |
|
|
63
|
+
|
|
64
|
+
## Attention Points
|
|
65
|
+
|
|
66
|
+
- {Important consideration or gotcha}
|
|
67
|
+
|
|
68
|
+
## Problems & Solutions
|
|
69
|
+
|
|
70
|
+
### {Problem Title}
|
|
71
|
+
**Problem:** {What went wrong}
|
|
72
|
+
**Solution:** {How it was fixed}
|
|
73
|
+
**Prevention:** {How to avoid in future}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Critical Rules
|
|
77
|
+
|
|
78
|
+
1. **RUN AFTER EVERY IMPLEMENTATION**
|
|
79
|
+
2. **UPDATE DOMAINS, NOT JUST CLAUDE.MD**
|
|
80
|
+
3. **INCLUDE COMMIT HASH**
|
|
81
|
+
4. **DOCUMENT CONNECTIONS** bidirectionally
|
|
82
|
+
5. **RECORD PROBLEMS** for future sessions
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: domain-updater
|
|
3
|
+
description: "AUTOMATICALLY invoke BEFORE commit-manager at session end. Records problems, solutions, and learnings in domain docs."
|
|
4
|
+
model: haiku
|
|
5
|
+
tools: Read, Write, Edit, Bash, Grep, Glob
|
|
6
|
+
skills: codebase-knowledge, docs-tracker
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Domain Updater Agent
|
|
10
|
+
|
|
11
|
+
You record session LEARNINGS in domain docs. Different from documenter: documenter maps files, you record wisdom.
|
|
12
|
+
|
|
13
|
+
## What You Add
|
|
14
|
+
|
|
15
|
+
### 1. Problems & Solutions
|
|
16
|
+
|
|
17
|
+
```markdown
|
|
18
|
+
### {Date} - {Problem Title}
|
|
19
|
+
**Problem:** {What went wrong}
|
|
20
|
+
**Root Cause:** {Why it happened}
|
|
21
|
+
**Solution:** {How it was fixed}
|
|
22
|
+
**Prevention:** {How to avoid in future}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Attention Points
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
- [YYYY-MM-DD] **Rule name** - Description of gotcha
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 3. Recent Commits
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
| Hash | Date | Description |
|
|
35
|
+
|------|------|-------------|
|
|
36
|
+
| abc123 | YYYY-MM-DD | feat: what was done |
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Workflow Order
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
implementation → quality gates → domain-updater (YOU) → commit-manager
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Critical Rules
|
|
46
|
+
|
|
47
|
+
1. **RUN BEFORE COMMIT** — changes included in same commit
|
|
48
|
+
2. **DOCUMENT PROBLEMS** — future sessions benefit
|
|
49
|
+
3. **INCLUDE SOLUTIONS** — not just what broke
|
|
50
|
+
4. **PREVENTION TIPS** — how to avoid next time
|
|
51
|
+
5. **DATE EVERYTHING**
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: research-web
|
|
3
|
+
description: "AUTOMATICALLY invoke BEFORE implementing any new feature or technology. Triggers: new feature, new technology, 'search', 'find info'. Web research specialist."
|
|
4
|
+
model: sonnet
|
|
5
|
+
tools: WebSearch, WebFetch, Read, Write
|
|
6
|
+
skills: research-cache
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Research Web Agent
|
|
10
|
+
|
|
11
|
+
You perform targeted web research for development questions.
|
|
12
|
+
|
|
13
|
+
## Before Searching
|
|
14
|
+
|
|
15
|
+
1. Read `.claude/config/active-project.json` → know the stack
|
|
16
|
+
2. Check research-cache for existing results
|
|
17
|
+
3. If cached & fresh → Return cached
|
|
18
|
+
|
|
19
|
+
## Search Strategy
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
[topic] + [year] + [stack context]
|
|
23
|
+
|
|
24
|
+
Examples (PHP):
|
|
25
|
+
- "PHP 8.3 readonly classes best practices 2025"
|
|
26
|
+
- "Laravel Octane RoadRunner performance tuning 2025"
|
|
27
|
+
|
|
28
|
+
Examples (Node):
|
|
29
|
+
- "Next.js 15 server actions best practices 2025"
|
|
30
|
+
- "Bun vs Node.js performance 2025"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Source Priority
|
|
34
|
+
|
|
35
|
+
1. Official documentation
|
|
36
|
+
2. GitHub issues/discussions
|
|
37
|
+
3. Stack Overflow (recent answers)
|
|
38
|
+
4. Technical blogs (verified authors)
|
|
39
|
+
|
|
40
|
+
## Output Format
|
|
41
|
+
|
|
42
|
+
```markdown
|
|
43
|
+
## Research: [Topic]
|
|
44
|
+
|
|
45
|
+
### Key Findings
|
|
46
|
+
1. [Finding] - Source: [URL]
|
|
47
|
+
|
|
48
|
+
### Recommendations
|
|
49
|
+
- [Actionable recommendation]
|
|
50
|
+
|
|
51
|
+
### Sources
|
|
52
|
+
- [URL] - [Date accessed]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Critical Rules
|
|
56
|
+
|
|
57
|
+
1. **ALWAYS CHECK CACHE** - Avoid duplicate searches
|
|
58
|
+
2. **CITE SOURCES** - Every finding needs URL
|
|
59
|
+
3. **RECENT FIRST** - Prefer current year content
|
|
60
|
+
4. **STACK-AWARE** - Use stack context in queries
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tester
|
|
3
|
+
description: "AUTOMATICALLY invoke AFTER implementing any function or utility. Creates tests using the stack-appropriate framework."
|
|
4
|
+
model: sonnet
|
|
5
|
+
tools: Read, Write, Edit, Bash, Grep, Glob
|
|
6
|
+
skills: test-coverage
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Tester Agent (Universal)
|
|
10
|
+
|
|
11
|
+
You create tests using the stack-appropriate test framework.
|
|
12
|
+
|
|
13
|
+
## Step 1: Read Stack Config
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cat .claude/config/active-project.json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This tells you which framework to use:
|
|
20
|
+
- **PHP** → PHPUnit
|
|
21
|
+
- **Node.js/TS** → Vitest
|
|
22
|
+
- **Python** → pytest
|
|
23
|
+
|
|
24
|
+
## Step 2: Read Stack Test Skill
|
|
25
|
+
|
|
26
|
+
Load the appropriate skill from `.claude/skills/`:
|
|
27
|
+
- PHP → `phpunit-testing/SKILL.md`
|
|
28
|
+
- Node → `vitest-testing/SKILL.md`
|
|
29
|
+
|
|
30
|
+
## Step 3: Create Tests
|
|
31
|
+
|
|
32
|
+
Follow the stack skill's patterns for:
|
|
33
|
+
- File location and naming
|
|
34
|
+
- Test structure (Arrange-Act-Assert)
|
|
35
|
+
- Mocking patterns
|
|
36
|
+
- Assertion style
|
|
37
|
+
|
|
38
|
+
## Universal Rules (ALL stacks)
|
|
39
|
+
|
|
40
|
+
1. **ISOLATED TESTS** — No dependencies between tests
|
|
41
|
+
2. **CLEAN STATE** — Reset between tests
|
|
42
|
+
3. **DESCRIPTIVE NAMES** — "should [behavior] when [condition]"
|
|
43
|
+
4. **ARRANGE-ACT-ASSERT** — Clear structure
|
|
44
|
+
5. **EDGE CASES** — Test null, empty, boundaries
|
|
45
|
+
6. **NO .SKIP** — Never commit skipped tests
|
|
46
|
+
7. **RUN AFTER WRITING** — Execute and fix failures
|
|
47
|
+
|
|
48
|
+
## Running Tests
|
|
49
|
+
|
|
50
|
+
Read `commands.test` from `.claude/config/active-project.json`:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# PHP
|
|
54
|
+
vendor/bin/phpunit
|
|
55
|
+
|
|
56
|
+
# Node.js
|
|
57
|
+
bun run test
|
|
58
|
+
|
|
59
|
+
# Python
|
|
60
|
+
pytest
|
|
61
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# /feature — Start New Feature
|
|
2
|
+
|
|
3
|
+
Execute in order:
|
|
4
|
+
|
|
5
|
+
1. **Research** → research-web agent (if new tech)
|
|
6
|
+
2. **Plan** → Break into tasks, create TODO list
|
|
7
|
+
3. **Implement** → Write code following stack patterns
|
|
8
|
+
4. **Test** → tester agent (PHPUnit / Vitest / pytest)
|
|
9
|
+
5. **Document** → documenter agent
|
|
10
|
+
6. **Update domains** → domain-updater agent
|
|
11
|
+
7. **Commit** → commit-manager agent
|
|
12
|
+
|
|
13
|
+
Always read `.claude/config/active-project.json` first.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# /fix — Fix Bug
|
|
2
|
+
|
|
3
|
+
Execute in order:
|
|
4
|
+
|
|
5
|
+
1. **Analyze** → Read error, reproduce, isolate
|
|
6
|
+
2. **Fix** → Apply minimal fix
|
|
7
|
+
3. **Test** → Add regression test (tester agent)
|
|
8
|
+
4. **Document** → Record in domain Problems & Solutions
|
|
9
|
+
5. **Commit** → commit-manager agent
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# /validate — Run Full Validation
|
|
2
|
+
|
|
3
|
+
Read quality gates from `.claude/config/active-project.json` and run all:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Read stack commands
|
|
7
|
+
cat .claude/config/active-project.json | jq '.qualityGates'
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Then execute each gate in order. Report pass/fail for each.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "Domain mapping - maps file patterns to documentation domains",
|
|
3
|
+
"domains": {
|
|
4
|
+
"authentication": {
|
|
5
|
+
"patterns": ["**/auth/**", "**/login/**", "**/session/**", "**/*auth*", "**/*login*"],
|
|
6
|
+
"description": "User authentication, sessions, tokens"
|
|
7
|
+
},
|
|
8
|
+
"api": {
|
|
9
|
+
"patterns": ["**/api/**", "**/routes/**", "**/routers/**", "**/controllers/**"],
|
|
10
|
+
"description": "API endpoints and routes"
|
|
11
|
+
},
|
|
12
|
+
"database": {
|
|
13
|
+
"patterns": ["**/models/**", "**/migrations/**", "**/db/**", "**/schemas/**", "**/*.model.*", "**/*.schema.*"],
|
|
14
|
+
"description": "Database models, schemas, migrations"
|
|
15
|
+
},
|
|
16
|
+
"ui-components": {
|
|
17
|
+
"patterns": ["**/components/**", "**/ui/**", "**/views/**", "**/templates/**"],
|
|
18
|
+
"description": "Reusable UI components"
|
|
19
|
+
},
|
|
20
|
+
"pages": {
|
|
21
|
+
"patterns": ["**/pages/**", "**/app/**/page.*", "**/app/**/layout.*"],
|
|
22
|
+
"description": "Page routes and layouts"
|
|
23
|
+
},
|
|
24
|
+
"utilities": {
|
|
25
|
+
"patterns": ["**/lib/**", "**/utils/**", "**/helpers/**", "**/common/**"],
|
|
26
|
+
"description": "Utility functions and helpers"
|
|
27
|
+
},
|
|
28
|
+
"testing": {
|
|
29
|
+
"patterns": ["**/tests/**", "**/*.test.*", "**/*.spec.*", "**/*Test.php"],
|
|
30
|
+
"description": "All test files"
|
|
31
|
+
},
|
|
32
|
+
"infrastructure": {
|
|
33
|
+
"patterns": ["**/docker*", "**/Dockerfile*", "**/.github/**", "**/scripts/**", "**/deploy/**"],
|
|
34
|
+
"description": "Docker, CI/CD, deployment"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"rules": {
|
|
38
|
+
"domainFileLocation": ".claude/skills/codebase-knowledge/domains/",
|
|
39
|
+
"requiredSections": ["Last Update", "Files", "Connections", "Recent Commits", "Attention Points"]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Universal security rules. Agents read active-project.json for stack-specific additions.",
|
|
3
|
+
"validation": {
|
|
4
|
+
"requireOnAllRoutes": true
|
|
5
|
+
},
|
|
6
|
+
"sensitivePatterns": {
|
|
7
|
+
"forbidden": [
|
|
8
|
+
"eval(",
|
|
9
|
+
"exec(",
|
|
10
|
+
"system(",
|
|
11
|
+
"passthru(",
|
|
12
|
+
"shell_exec(",
|
|
13
|
+
"password:",
|
|
14
|
+
"passwordHash",
|
|
15
|
+
"apiKey:",
|
|
16
|
+
"secret:"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"cookies": {
|
|
20
|
+
"httpOnly": true,
|
|
21
|
+
"secure": true,
|
|
22
|
+
"sameSite": "strict"
|
|
23
|
+
},
|
|
24
|
+
"owaspChecks": {
|
|
25
|
+
"a01_brokenAccessControl": true,
|
|
26
|
+
"a02_cryptographicFailures": true,
|
|
27
|
+
"a03_injection": true,
|
|
28
|
+
"a07_authenticationFailures": true,
|
|
29
|
+
"a09_securityLogging": true
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stop Validator Hook — Start Vibing Stacks (Universal)
|
|
4
|
+
*
|
|
5
|
+
* Reads active-project.json to determine stack-specific validations.
|
|
6
|
+
* Blocks task completion if:
|
|
7
|
+
* 1. Branch != main (work must be merged)
|
|
8
|
+
* 2. Git tree not clean
|
|
9
|
+
* 3. CLAUDE.md not updated
|
|
10
|
+
* 4. CLAUDE.md missing required sections
|
|
11
|
+
* 5. CLAUDE.md exceeds 40k chars
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { execSync } from 'child_process';
|
|
15
|
+
import { existsSync, readFileSync } from 'fs';
|
|
16
|
+
import { join } from 'path';
|
|
17
|
+
|
|
18
|
+
const PROJECT_DIR = process.env['CLAUDE_PROJECT_DIR'] || process.cwd();
|
|
19
|
+
const CLAUDE_MD = join(PROJECT_DIR, 'CLAUDE.md');
|
|
20
|
+
const ACTIVE_PROJECT = join(PROJECT_DIR, '.claude', 'config', 'active-project.json');
|
|
21
|
+
const MAX_CHARS = 40000;
|
|
22
|
+
|
|
23
|
+
// Load stack info
|
|
24
|
+
let stackId = 'unknown';
|
|
25
|
+
try {
|
|
26
|
+
if (existsSync(ACTIVE_PROJECT)) {
|
|
27
|
+
const config = JSON.parse(readFileSync(ACTIVE_PROJECT, 'utf8'));
|
|
28
|
+
stackId = config.stack || 'unknown';
|
|
29
|
+
}
|
|
30
|
+
} catch {}
|
|
31
|
+
|
|
32
|
+
// Source extensions per stack
|
|
33
|
+
const STACK_EXTENSIONS: Record<string, Set<string>> = {
|
|
34
|
+
php: new Set(['.php', '.blade.php', '.twig']),
|
|
35
|
+
nodejs: new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs']),
|
|
36
|
+
python: new Set(['.py']),
|
|
37
|
+
go: new Set(['.go']),
|
|
38
|
+
rust: new Set(['.rs']),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const sourceExtensions = STACK_EXTENSIONS[stackId] || new Set(['.ts', '.js', '.php', '.py']);
|
|
42
|
+
|
|
43
|
+
interface HookResult {
|
|
44
|
+
continue: boolean;
|
|
45
|
+
decision: 'approve' | 'block';
|
|
46
|
+
reason: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function cmd(command: string): string {
|
|
50
|
+
try {
|
|
51
|
+
return execSync(command, { cwd: PROJECT_DIR, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
52
|
+
} catch {
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getBranch(): string {
|
|
58
|
+
return cmd('git rev-parse --abbrev-ref HEAD') || 'unknown';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getModifiedFiles(): string[] {
|
|
62
|
+
const staged = cmd('git diff --name-only --cached').split('\n').filter(Boolean);
|
|
63
|
+
const unstaged = cmd('git diff --name-only').split('\n').filter(Boolean);
|
|
64
|
+
const untracked = cmd('git ls-files --others --exclude-standard').split('\n').filter(Boolean);
|
|
65
|
+
return [...new Set([...staged, ...unstaged, ...untracked])];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function validate(): HookResult {
|
|
69
|
+
const branch = getBranch();
|
|
70
|
+
const isMain = branch === 'main' || branch === 'master';
|
|
71
|
+
const modified = getModifiedFiles();
|
|
72
|
+
const isClean = modified.length === 0;
|
|
73
|
+
|
|
74
|
+
// 1. Must be on main with clean tree
|
|
75
|
+
if (!isMain && modified.length > 0) {
|
|
76
|
+
return {
|
|
77
|
+
continue: true,
|
|
78
|
+
decision: 'block',
|
|
79
|
+
reason: `BLOCKED: On branch '${branch}' with ${modified.length} modified files.\n\nComplete git workflow:\n1. git add -A\n2. git commit -m "type: description"\n3. git checkout main\n4. git merge ${branch}\n5. git push origin main\n6. git branch -d ${branch}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!isMain) {
|
|
84
|
+
return {
|
|
85
|
+
continue: true,
|
|
86
|
+
decision: 'block',
|
|
87
|
+
reason: `BLOCKED: On branch '${branch}'. Switch to main:\n1. git checkout main\n2. git merge ${branch}\n3. git push origin main`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!isClean) {
|
|
92
|
+
return {
|
|
93
|
+
continue: true,
|
|
94
|
+
decision: 'block',
|
|
95
|
+
reason: `BLOCKED: ${modified.length} uncommitted files:\n${modified.slice(0, 10).map(f => ` - ${f}`).join('\n')}\n\nCommit or stash before completing.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 2. CLAUDE.md must exist
|
|
100
|
+
if (!existsSync(CLAUDE_MD)) {
|
|
101
|
+
return {
|
|
102
|
+
continue: true,
|
|
103
|
+
decision: 'block',
|
|
104
|
+
reason: 'BLOCKED: CLAUDE.md not found. Create it with required sections.',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const content = readFileSync(CLAUDE_MD, 'utf8');
|
|
109
|
+
|
|
110
|
+
// 3. Size check
|
|
111
|
+
if (content.length > MAX_CHARS) {
|
|
112
|
+
return {
|
|
113
|
+
continue: true,
|
|
114
|
+
decision: 'block',
|
|
115
|
+
reason: `BLOCKED: CLAUDE.md is ${content.length} chars (max ${MAX_CHARS}). Run claude-md-compactor agent.`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 4. Required sections
|
|
120
|
+
const required = [
|
|
121
|
+
{ pattern: /^# .+/m, name: 'Project Title (H1)' },
|
|
122
|
+
{ pattern: /^## Last Change/m, name: 'Last Change' },
|
|
123
|
+
{ pattern: /^## Stack/m, name: 'Stack' },
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const missing = required.filter(r => !r.pattern.test(content)).map(r => r.name);
|
|
127
|
+
if (missing.length > 0) {
|
|
128
|
+
return {
|
|
129
|
+
continue: true,
|
|
130
|
+
decision: 'block',
|
|
131
|
+
reason: `BLOCKED: CLAUDE.md missing sections: ${missing.join(', ')}`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// All good
|
|
136
|
+
return {
|
|
137
|
+
continue: false,
|
|
138
|
+
decision: 'approve',
|
|
139
|
+
reason: `ALL CHECKS PASSED ✅\nStack: ${stackId}\nBranch: ${branch}\nTree: Clean`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function main(): Promise<void> {
|
|
144
|
+
// Read stdin (hook input)
|
|
145
|
+
let hookInput: any = {};
|
|
146
|
+
try {
|
|
147
|
+
const chunks: string[] = [];
|
|
148
|
+
process.stdin.setEncoding('utf8');
|
|
149
|
+
const timeout = setTimeout(() => process.stdin.destroy(), 1000);
|
|
150
|
+
for await (const chunk of process.stdin) {
|
|
151
|
+
chunks.push(chunk);
|
|
152
|
+
}
|
|
153
|
+
clearTimeout(timeout);
|
|
154
|
+
hookInput = JSON.parse(chunks.join('') || '{}');
|
|
155
|
+
} catch {}
|
|
156
|
+
|
|
157
|
+
// Prevent loop
|
|
158
|
+
if (hookInput.stop_hook_active) {
|
|
159
|
+
console.log(JSON.stringify({ continue: false, decision: 'approve', reason: 'Cycle detected' }));
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const result = validate();
|
|
164
|
+
console.log(JSON.stringify(result));
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
main().catch(() => {
|
|
169
|
+
console.log(JSON.stringify({ continue: false, decision: 'approve', reason: 'Hook error' }));
|
|
170
|
+
process.exit(0);
|
|
171
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* UserPromptSubmit Hook — Start Vibing Stacks
|
|
4
|
+
*
|
|
5
|
+
* Injects workflow instructions before each user prompt.
|
|
6
|
+
* Reads active-project.json for stack-specific context.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
|
|
12
|
+
const PROJECT_DIR = process.env['CLAUDE_PROJECT_DIR'] || process.cwd();
|
|
13
|
+
const ACTIVE_PROJECT = join(PROJECT_DIR, '.claude', 'config', 'active-project.json');
|
|
14
|
+
|
|
15
|
+
let stackName = 'Unknown';
|
|
16
|
+
let qualityCmd = 'Run quality gates';
|
|
17
|
+
try {
|
|
18
|
+
if (existsSync(ACTIVE_PROJECT)) {
|
|
19
|
+
const config = JSON.parse(readFileSync(ACTIVE_PROJECT, 'utf8'));
|
|
20
|
+
stackName = config.stack || 'Unknown';
|
|
21
|
+
// Read quality gates command from stack config
|
|
22
|
+
const stackConfig = join(PROJECT_DIR, '.claude', 'config', 'quality-gates.json');
|
|
23
|
+
if (existsSync(stackConfig)) {
|
|
24
|
+
const gates = JSON.parse(readFileSync(stackConfig, 'utf8'));
|
|
25
|
+
qualityCmd = gates.runAll || qualityCmd;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
} catch {}
|
|
29
|
+
|
|
30
|
+
async function main(): Promise<void> {
|
|
31
|
+
let hookInput: any = {};
|
|
32
|
+
try {
|
|
33
|
+
const chunks: string[] = [];
|
|
34
|
+
process.stdin.setEncoding('utf8');
|
|
35
|
+
const timeout = setTimeout(() => process.stdin.destroy(), 1000);
|
|
36
|
+
for await (const chunk of process.stdin) {
|
|
37
|
+
chunks.push(chunk);
|
|
38
|
+
}
|
|
39
|
+
clearTimeout(timeout);
|
|
40
|
+
hookInput = JSON.parse(chunks.join('') || '{}');
|
|
41
|
+
} catch {}
|
|
42
|
+
|
|
43
|
+
const prompt = hookInput.user_prompt || hookInput.prompt || '';
|
|
44
|
+
if (!prompt.trim()) {
|
|
45
|
+
console.log(JSON.stringify({ continue: true }));
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const today = new Date().toISOString().split('T')[0];
|
|
50
|
+
|
|
51
|
+
const systemMessage = `TASK WORKFLOW (Stack: ${stackName}):
|
|
52
|
+
|
|
53
|
+
0. READ both CLAUDE.md and .claude/config/active-project.json before changes.
|
|
54
|
+
|
|
55
|
+
1. CREATE a detailed todo-list breaking down the request.
|
|
56
|
+
Include "Update CLAUDE.md" as final task.
|
|
57
|
+
|
|
58
|
+
2. WORK through each item sequentially.
|
|
59
|
+
|
|
60
|
+
3. Run quality gates: ${qualityCmd}
|
|
61
|
+
|
|
62
|
+
4. COMMIT using conventional commits via commit-manager agent.
|
|
63
|
+
|
|
64
|
+
5. UPDATE CLAUDE.md:
|
|
65
|
+
a. "## Last Change" (date: ${today}, branch, summary)
|
|
66
|
+
b. Update ALL affected rule/flow sections
|
|
67
|
+
|
|
68
|
+
6. Run stop-validator before finishing.`;
|
|
69
|
+
|
|
70
|
+
console.log(JSON.stringify({ continue: true, systemMessage }));
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
main().catch(() => {
|
|
75
|
+
console.log(JSON.stringify({ continue: true }));
|
|
76
|
+
process.exit(0);
|
|
77
|
+
});
|