Skip to content

Commit 3dfa358

Browse files
x3ekclaude
andauthored
chore(dx): slim CLAUDE.md and restructure skills (#73) (#74)
* chore(dx): slim CLAUDE.md and reorganize into skills Reduce CLAUDE.md from 462 to 81 lines by moving domain-specific content into skills. Create dev-setup skill (local setup, scripts, config, deployment) and git skill (conventional commits, branch naming). Move git conventions out of github skill. Add content repo structure and frontmatter to theme-creator skill. Drop discoverable content (architecture, tech stack, repo structure, design decisions). Part of #73 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: add playwright artifacts to .gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(dx): split skills into references and add helper scripts Split github and theme-creator skills into lean SKILL.md + reference files following the progressive disclosure pattern from playwright-cli. Move agent-facing scripts (github-issue-updater.py, setup-worktree.py) from scripts/ into their respective skill directories. Add dev environment readiness check script. Part of #73 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(dx): correct parent path index in check-env.py Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(dx): correct PROJECT_ROOT path in moved setup-worktree.py Script moved from scripts/ to .claude/skills/git/scripts/ but PROJECT_ROOT still used parent.parent (2 levels). Now uses parents[4] to reach repo root from new location. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cc56043 commit 3dfa358

20 files changed

Lines changed: 683 additions & 749 deletions

.claude/skills/dev-setup/SKILL.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
---
2+
name: dev-setup
3+
description: Development environment setup, scripts, configuration, and deployment for SquishMark
4+
---
5+
6+
# Development Environment
7+
8+
## Local Setup
9+
10+
```bash
11+
# Clone the repo
12+
git clone https://github.com/xeek-dev/squishmark.git
13+
cd squishmark
14+
15+
# Create virtual environment
16+
python -m venv .venv
17+
source .venv/bin/activate # or .venv\Scripts\activate on Windows
18+
19+
# Install dependencies
20+
pip install -e ".[dev]"
21+
22+
# Run locally
23+
uvicorn squishmark.main:app --reload
24+
```
25+
26+
## Development Scripts
27+
28+
Two user-facing scripts in `scripts/` and two agent-facing scripts in skill directories:
29+
30+
### Environment Check
31+
32+
Run the readiness check before starting work:
33+
34+
```bash
35+
python .claude/skills/dev-setup/scripts/check-env.py
36+
```
37+
38+
### User-Facing Scripts (`scripts/`)
39+
40+
### `start-dev.py` — Dev server with multi-server management
41+
42+
Starts a uvicorn dev server pointing at the local `content/` directory. Supports running multiple named server instances simultaneously (tracked in `.dev-servers.json`). The default instance name is the current git branch.
43+
44+
```bash
45+
# Basic usage
46+
python scripts/start-dev.py # foreground on :8000
47+
python scripts/start-dev.py -b # background on :8000
48+
python scripts/start-dev.py -b --port 8001 # background on :8001
49+
python scripts/start-dev.py --name api -b # named background instance
50+
51+
# Server management
52+
python scripts/start-dev.py --list # show all tracked servers
53+
python scripts/start-dev.py --stop # stop current branch's server
54+
python scripts/start-dev.py --stop api # stop named server
55+
python scripts/start-dev.py --stop 12345 # stop server by PID
56+
python scripts/start-dev.py --stop-all # stop all servers
57+
python scripts/start-dev.py --restart -b # restart in background
58+
59+
# Other options
60+
python scripts/start-dev.py --host 0.0.0.0 # bind to all interfaces
61+
python scripts/start-dev.py --no-reload # disable auto-reload
62+
```
63+
64+
> **Stale port gotcha:** Background start (`-b`) can silently fail if a stale process holds the port. The old process keeps serving while the new one exits. Always verify after restart: `--stop <name>` then `lsof -ti:<port> | xargs kill` before starting fresh.
65+
66+
### `run-checks.py` — Local CI checks
67+
68+
Runs the same checks as CI: **ruff format**, **ruff check**, **pytest**, and **pyright**. By default runs all checks and reports a summary.
69+
70+
```bash
71+
python scripts/run-checks.py # run all checks
72+
python scripts/run-checks.py --fail-fast # stop on first failure
73+
python scripts/run-checks.py --docker # also run docker build (slow)
74+
```
75+
76+
### Agent-Facing Scripts (in skill directories)
77+
78+
#### `setup-worktree.py` — Git worktree management (in `git` skill)
79+
80+
Creates isolated worktrees in `.worktrees/` for parallel development.
81+
82+
```bash
83+
python .claude/skills/git/scripts/setup-worktree.py feat/42-dark-mode # create worktree + branch
84+
python .claude/skills/git/scripts/setup-worktree.py feat/42-dark-mode --install # also pip install -e
85+
python .claude/skills/git/scripts/setup-worktree.py feat/42-dark-mode --with-content # also copy content/
86+
python .claude/skills/git/scripts/setup-worktree.py feat/42-dark-mode --integration # --install + --with-content
87+
python .claude/skills/git/scripts/setup-worktree.py --list # list active worktrees
88+
python .claude/skills/git/scripts/setup-worktree.py --cleanup 42-dark-mode # remove worktree + branch
89+
```
90+
91+
#### `github-issue-updater.py` — Issue metadata updater (in `github` skill)
92+
93+
Sets issue type (task/bug/feature), adds labels, and assigns milestones in a single command.
94+
95+
```bash
96+
python .claude/skills/github/scripts/github-issue-updater.py 42 --type task
97+
python .claude/skills/github/scripts/github-issue-updater.py 42 --add-label "engine,themes"
98+
python .claude/skills/github/scripts/github-issue-updater.py 42 --milestone "SquishMark 1.0"
99+
```
100+
101+
## Configuration
102+
103+
### Environment Variables
104+
105+
```bash
106+
# Required
107+
GITHUB_CONTENT_REPO=xeek-dev/xeek-dev-content
108+
GITHUB_TOKEN=ghp_... # Only for private repos
109+
110+
# Optional
111+
CACHE_TTL_SECONDS=300
112+
DATABASE_URL=sqlite:///data/squishmark.db
113+
114+
# GitHub OAuth (for admin features)
115+
GITHUB_CLIENT_ID=...
116+
GITHUB_CLIENT_SECRET=...
117+
```
118+
119+
### config.yml (in content repo)
120+
121+
```yaml
122+
site:
123+
title: "My Blog"
124+
description: "A blog about things"
125+
author: "Your Name"
126+
url: "https://example.com"
127+
favicon: "/static/user/custom-icon.png" # Optional: override auto-detected favicon
128+
featured_max: 5 # Optional: max featured posts (default: 5)
129+
130+
theme:
131+
name: default
132+
pygments_style: monokai
133+
134+
posts:
135+
per_page: 10
136+
```
137+
138+
## Deployment (Fly.io)
139+
140+
```bash
141+
# First time
142+
fly launch
143+
144+
# Create volume for SQLite
145+
fly volumes create squishmark_data --size 1
146+
147+
# Set secrets
148+
fly secrets set GITHUB_TOKEN=ghp_...
149+
fly secrets set GITHUB_CLIENT_ID=...
150+
fly secrets set GITHUB_CLIENT_SECRET=...
151+
152+
# Deploy
153+
fly deploy
154+
```
155+
156+
## Common Tasks
157+
158+
### Adding a new route
159+
1. Create router in `src/squishmark/routers/`
160+
2. Register in `main.py`
161+
3. Add corresponding Jinja2 template if needed
162+
163+
### Testing with a local content repo
164+
Set `GITHUB_CONTENT_REPO` to a local path (prefixed with `file://`) for development without GitHub API calls.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env python3
2+
"""Check that the SquishMark development environment is ready."""
3+
4+
import os
5+
import sys
6+
from pathlib import Path
7+
8+
9+
def check(label: str, condition: bool, fix: str) -> bool:
10+
if condition:
11+
print(f" OK {label}")
12+
else:
13+
print(f" FAIL {label}")
14+
print(f" Fix: {fix}")
15+
return condition
16+
17+
18+
def main() -> None:
19+
root = Path(__file__).resolve().parents[4] # .claude/skills/dev-setup/scripts/check-env.py -> repo root
20+
os.chdir(root)
21+
22+
print("Checking SquishMark dev environment...\n")
23+
checks = []
24+
25+
# venv exists
26+
venv = root / ".venv"
27+
checks.append(
28+
check(
29+
"Virtual environment exists",
30+
venv.is_dir(),
31+
"python -m venv .venv",
32+
)
33+
)
34+
35+
# venv is active
36+
checks.append(
37+
check(
38+
"Virtual environment is active",
39+
sys.prefix != sys.base_prefix,
40+
"source .venv/bin/activate",
41+
)
42+
)
43+
44+
# package is importable
45+
try:
46+
import squishmark # noqa: F401
47+
48+
importable = True
49+
except ImportError:
50+
importable = False
51+
checks.append(
52+
check(
53+
"squishmark package installed",
54+
importable,
55+
'pip install -e ".[dev]"',
56+
)
57+
)
58+
59+
# content directory
60+
checks.append(
61+
check(
62+
"content/ directory exists",
63+
(root / "content").is_dir(),
64+
"Create a content/ directory with posts/, pages/, and config.yml",
65+
)
66+
)
67+
68+
# data directory
69+
data_dir = root / "data"
70+
checks.append(
71+
check(
72+
"data/ directory exists",
73+
data_dir.is_dir(),
74+
"mkdir data",
75+
)
76+
)
77+
78+
print()
79+
passed = sum(checks)
80+
total = len(checks)
81+
if passed == total:
82+
print(f"All {total} checks passed.")
83+
else:
84+
print(f"{passed}/{total} checks passed.")
85+
sys.exit(1)
86+
87+
88+
if __name__ == "__main__":
89+
main()

.claude/skills/git/SKILL.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
name: git
3+
description: Git conventions — conventional commits, branch naming, commit message format
4+
---
5+
6+
# Git Conventions
7+
8+
## Conventional Commits
9+
10+
Commit messages and PR titles **must** use [Conventional Commits](https://www.conventionalcommits.org/) format:
11+
12+
```
13+
type(scope): description
14+
```
15+
16+
Types: `feat`, `fix`, `refactor`, `chore`, `docs`, `test`, `ci`
17+
18+
Scope is optional but recommended. Examples:
19+
- `feat(terminal): add pixel art title renderer`
20+
- `fix(docs): correct cache-busting instructions`
21+
- `refactor(theme): extract loader into subpackage`
22+
- `chore(dx): clean up permission settings`
23+
24+
## Branch Naming
25+
26+
```
27+
type/issue-description
28+
```
29+
30+
Use the same type prefix as the anticipated merge commit. Examples:
31+
- `feat/42-dark-mode`
32+
- `fix/15-header-overflow`
33+
- `refactor/16-theme-subpackage`
34+
- `chore/23-update-dependencies`
35+
- `docs/8-api-documentation`
36+
37+
## Commit Messages
38+
39+
- Use multiple `-m` flags instead of `$()` heredoc substitution, which triggers a command-substitution security prompt
40+
- Example: `git commit -m "fix(engine): wire notes into rendering" -m "Detailed body here." -m "Co-Authored-By: ..."`
41+
42+
## Issue Titles
43+
44+
- Use plain English descriptions, **not** conventional commit format
45+
- Conventional commit format is only for commit messages and PR titles

scripts/setup-worktree.py renamed to .claude/skills/git/scripts/setup-worktree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import sys
2121
from pathlib import Path
2222

23-
PROJECT_ROOT = Path(__file__).parent.parent.resolve()
23+
PROJECT_ROOT = Path(__file__).resolve().parents[4] # .claude/skills/git/scripts/file.py -> repo root
2424
WORKTREES_DIR = PROJECT_ROOT / ".worktrees"
2525

2626

0 commit comments

Comments
 (0)