Skip to content

Commit 42d0b1a

Browse files
feat: add /add-feature command, feature manifest system, and scaffold-default.sh
- New /add-feature command (26th command, 10th starter-kit) for adding capabilities (MongoDB, Docker, testing, etc.) to existing projects - Feature manifest system (.claude/features.json) tracks installed features per project for /update-project to sync source file changes - Enhanced /update-project with Step 3b (feature file inventory) and Step 5d (feature file updates) to sync feature source files - New scaffold-default.sh batch script for default profile (Next.js + MongoDB + Tailwind + Docker + SEO + CI) — creates 67 files in ~3s - Updated new-project.md, convert-project-to-starter-kit.md, and scaffold-clean.sh to write feature manifests - Doc sync: 25→26 commands, 9→10 starter-kit across all 5 locations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e3a564d commit 42d0b1a

13 files changed

Lines changed: 1826 additions & 26 deletions

.claude/commands/add-feature.md

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
---
2+
description: Add a capability (MongoDB, Docker, testing, etc.) to an existing project
3+
scope: starter-kit
4+
argument-hint: <feature> [--list | --force]
5+
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion
6+
---
7+
8+
# Add Feature to Existing Project
9+
10+
Add capabilities (MongoDB, Docker, testing, etc.) to an existing starter-kit project after scaffolding. Idempotent — safely updates already-installed features to the latest version.
11+
12+
**Arguments:** $ARGUMENTS
13+
14+
---
15+
16+
## Feature Definitions
17+
18+
The authoritative table of features that can be added to projects. Both `/add-feature` and `/update-project` reference these definitions.
19+
20+
| Feature | Files (from $SOURCE) | Deps | DevDeps | Env Vars | Scripts | CLAUDE.md Rule |
21+
|---------|---------------------|------|---------|----------|---------|----------------|
22+
| `mongo` | `src/core/db/index.ts`, `scripts/db-query.ts`, `scripts/queries/example-find-user.ts`, `scripts/queries/example-count-docs.ts` | `mongodb@^6.5.0` | `tsx@^4.7.0` | `MONGODB_URI`, `MONGO_DB_NAME`, `DB_SANITIZE_INPUTS` | `db:query`, `db:query:list` | Rule 3 |
23+
| `postgres` | `src/core/db/sql.ts` | `pg@^8.0.0` || `DATABASE_URL` || Rule 3b |
24+
| `docker` | Generated from templates (Dockerfile, docker-compose.yml) ||||| Rule 10 |
25+
| `vitest` | `vitest.config.ts` || `vitest@^2.0.0` || `test:unit`, `test:unit:watch`, `test:coverage` ||
26+
| `playwright` | `playwright.config.ts` || `@playwright/test@^1.42.0` || `test:e2e`, `test:e2e:ui`, `test:e2e:headed`, `test:e2e:chromium`, `test:e2e:report`, `test:kill-ports` | Rule 4 |
27+
| `content` | `scripts/build-content.ts`, `scripts/content.config.json` |||| `content:build`, `content:build:id`, `content:list` ||
28+
29+
---
30+
31+
## Step 0 — Resolve Source (Starter Kit)
32+
33+
Find the starter kit source directory:
34+
35+
1. If CWD has BOTH `claude-mastery-project.conf` AND `.claude/commands/new-project.md` → use CWD as `$SOURCE`
36+
2. Else read `~/.claude/starter-kit-source-path` → verify it still has both files
37+
3. Else ask via AskUserQuestion: "Where is the starter kit cloned?" with a text input
38+
39+
Store as `$SOURCE`.
40+
41+
---
42+
43+
## Step 1 — Parse Arguments
44+
45+
Parse `$ARGUMENTS` for:
46+
47+
- `--list` → display the Feature Definitions table above and exit immediately
48+
- `--force` → set `$FORCE=true` (skip confirmation prompts)
49+
- Everything else → feature name(s), space-separated (e.g., `mongo vitest`)
50+
51+
**Validate feature names:** Each name must match one of the features in the table above. If unknown: "Unknown feature: `<name>`. Use `/add-feature --list` to see available features."
52+
53+
If no feature names and no `--list`: ask via AskUserQuestion:
54+
"Which feature do you want to add?"
55+
- **mongo** — MongoDB wrapper, query system, connection pool management
56+
- **postgres** — SQL wrapper with parameterized queries, transaction support
57+
- **vitest** — Unit/integration test framework with coverage
58+
- **playwright** — E2E browser testing with multi-browser support
59+
60+
(Show up to 4 most common; user can type "Other" for docker, content, etc.)
61+
62+
---
63+
64+
## Step 2 — Select Target
65+
66+
1. Read `~/.claude/starter-kit-projects.json`
67+
- If file doesn't exist or empty → error: "No projects found. Use `/new-project` to create one first."
68+
69+
2. **Smart default:** If CWD is inside a registered project directory → offer it first
70+
71+
3. Filter to projects whose `path` directory still exists on disk
72+
73+
4. Display list with AskUserQuestion:
74+
- "Which project should receive the feature?"
75+
- Options: up to 4 most recent projects (by `createdAt`), each showing: `name — language/framework — path`
76+
- If more than 4: the 4th option should be "Other (type a path)"
77+
78+
5. Store selected path as `$TARGET`
79+
80+
### Validations (all must pass — stop with clear error if any fail)
81+
82+
1. `$TARGET` directory exists → if not: "Directory not found: $TARGET"
83+
2. `$TARGET` is a git repo → run: `git -C "$TARGET" rev-parse --is-inside-work-tree 2>/dev/null`
84+
- If not a git repo: "This project must be a git repo."
85+
3. `$TARGET` is NOT the starter kit itself (compare resolved paths of `$SOURCE` and `$TARGET`)
86+
- If same: "Cannot add features to the starter kit itself."
87+
4. `$TARGET` is registered in `~/.claude/starter-kit-projects.json`
88+
- If not registered: "This project isn't in the registry. Use `/convert-project-to-starter-kit` first."
89+
90+
---
91+
92+
## Step 3 — Safety Commit
93+
94+
```bash
95+
cd "$TARGET"
96+
git status --porcelain
97+
```
98+
99+
- **If uncommitted changes exist** (git status --porcelain has output):
100+
```bash
101+
cd "$TARGET" && git add -A && git commit -m "chore: pre-feature snapshot (before /add-feature)"
102+
```
103+
104+
- **If clean** (no uncommitted changes):
105+
```bash
106+
cd "$TARGET" && git commit --allow-empty -m "chore: pre-feature marker (before /add-feature)"
107+
```
108+
109+
Store the hash: `PRE_FEATURE_HASH=$(git -C "$TARGET" rev-parse HEAD)`
110+
111+
**STOP if git fails** (except "nothing to commit" which is fine — treat as clean).
112+
113+
---
114+
115+
## Step 4 — Read Manifest
116+
117+
Read `$TARGET/.claude/features.json`.
118+
119+
**If missing:** Create an empty manifest:
120+
121+
```json
122+
{
123+
"schemaVersion": 1,
124+
"installedBy": "claude-code-mastery-starter-kit",
125+
"language": "unknown",
126+
"features": {}
127+
}
128+
```
129+
130+
Write it to `$TARGET/.claude/features.json`.
131+
132+
**If exists:** Parse it. For each requested feature, check if it's already installed:
133+
134+
- **Already installed, same files:** Ask via AskUserQuestion (unless `$FORCE`):
135+
"Feature `<name>` is already installed (since <installedAt>). Update to latest version?"
136+
- Yes, update files (Recommended)
137+
- No, skip this feature
138+
139+
- **Not installed:** Proceed to install
140+
141+
---
142+
143+
## Step 5 — Preview (unless --force)
144+
145+
For each feature to install/update, display:
146+
147+
```
148+
=== Feature Preview: <name> ===
149+
150+
Files to copy:
151+
+ src/core/db/index.ts
152+
+ scripts/db-query.ts
153+
+ scripts/queries/example-find-user.ts
154+
+ scripts/queries/example-count-docs.ts
155+
156+
Dependencies to install:
157+
mongodb@^6.5.0
158+
tsx@^4.7.0 (dev)
159+
160+
Environment variables to add (.env.example):
161+
MONGODB_URI=your_mongodb_connection_string_here
162+
MONGO_DB_NAME=your_database_name
163+
DB_SANITIZE_INPUTS=true
164+
165+
Scripts to add (package.json):
166+
db:query → tsx scripts/db-query.ts
167+
db:query:list → tsx scripts/db-query.ts --list
168+
169+
CLAUDE.md sections:
170+
Rule 3: Database Access — Wrapper Only (if missing)
171+
```
172+
173+
Ask via AskUserQuestion:
174+
"Proceed with installing <N> feature(s)?"
175+
- **Yes, install** (Recommended)
176+
- **No, cancel**
177+
178+
If "No, cancel" → stop: "Feature installation cancelled. No changes made."
179+
180+
---
181+
182+
## Step 6 — Execute
183+
184+
For each feature, in order:
185+
186+
### 6a. Create directories
187+
188+
```bash
189+
mkdir -p "$TARGET/$(dirname <each-file>)"
190+
```
191+
192+
### 6b. Copy files
193+
194+
For each file in the feature definition:
195+
- Copy from `$SOURCE/<file>``$TARGET/<file>`
196+
- If file already exists and this is an update → overwrite
197+
198+
### 6c. Install dependencies (Node.js projects only)
199+
200+
Detect package manager from target project:
201+
- `pnpm-lock.yaml` exists → `pnpm`
202+
- `bun.lockb` exists → `bun`
203+
- Otherwise → `npm`
204+
205+
```bash
206+
cd "$TARGET"
207+
# Production deps
208+
<pkg-manager> add <deps>
209+
# Dev deps
210+
<pkg-manager> add -D <devDeps>
211+
```
212+
213+
Skip if no deps defined for the feature. Skip if deps already in package.json.
214+
215+
### 6d. Merge .env.example
216+
217+
For each env var in the feature definition:
218+
1. Read `$TARGET/.env.example` (create if missing)
219+
2. Check if key name (before `=`) already exists
220+
3. If missing → append the line with placeholder value
221+
4. Write back
222+
223+
**Env var placeholder values:**
224+
- `MONGODB_URI``your_mongodb_connection_string_here`
225+
- `MONGO_DB_NAME``your_database_name`
226+
- `DB_SANITIZE_INPUTS``true`
227+
- `DATABASE_URL``your_database_url_here`
228+
229+
### 6e. Merge package.json scripts
230+
231+
For each script in the feature definition:
232+
1. Read `$TARGET/package.json` as JSON
233+
2. Check if script name already exists in `scripts`
234+
3. If missing → add it
235+
4. Write back (preserve formatting)
236+
237+
**Script values:**
238+
- `db:query``tsx scripts/db-query.ts`
239+
- `db:query:list``tsx scripts/db-query.ts --list`
240+
- `test:unit``vitest run`
241+
- `test:unit:watch``vitest`
242+
- `test:coverage``vitest run --coverage`
243+
- `test:e2e``pnpm test:kill-ports && playwright test`
244+
- `test:e2e:ui``pnpm test:kill-ports && playwright test --ui`
245+
- `test:e2e:headed``pnpm test:kill-ports && playwright test --headed`
246+
- `test:e2e:chromium``pnpm test:kill-ports && playwright test --project=chromium`
247+
- `test:e2e:report``playwright show-report`
248+
- `test:kill-ports``lsof -ti:4000,4010,4020 | xargs kill -9 2>/dev/null || true`
249+
- `content:build``tsx scripts/build-content.ts`
250+
- `content:build:id``tsx scripts/build-content.ts --id`
251+
- `content:list``tsx scripts/build-content.ts --list`
252+
253+
### 6f. Add CLAUDE.md sections (if missing)
254+
255+
Check if the relevant CLAUDE.md rule section exists by searching for the header text. If missing, append it.
256+
257+
**mongo → Rule 3 header check:** Search for `Database Access` or `Wrapper Only` in `$TARGET/CLAUDE.md`
258+
If missing, append the full Rule 3 section from the starter kit's CLAUDE.md.
259+
260+
**postgres → Rule 3b header check:** Search for `SQL Database Access` in `$TARGET/CLAUDE.md`
261+
If missing, append the full Rule 3b section.
262+
263+
**playwright → Rule 4 header check:** Search for `Testing — Explicit Success Criteria` in `$TARGET/CLAUDE.md`
264+
If missing, append the full Rule 4 section.
265+
266+
### 6g. Special: Docker feature
267+
268+
Docker doesn't copy files from source — it generates them based on the target project:
269+
270+
1. Detect language from registry or `$TARGET/.claude/features.json`
271+
2. Generate `Dockerfile` using the appropriate template from the starter kit's `/new-project` command:
272+
- Node.js → multi-stage with node:20-alpine
273+
- Go → multi-stage with golang:1.23-alpine → scratch
274+
- Python → multi-stage with python:3.12-slim
275+
3. Generate `.dockerignore` if missing
276+
4. Add docker-related scripts if applicable
277+
278+
---
279+
280+
## Step 7 — Update Manifest
281+
282+
For each installed/updated feature, write to `$TARGET/.claude/features.json`:
283+
284+
```json
285+
{
286+
"schemaVersion": 1,
287+
"installedBy": "claude-code-mastery-starter-kit",
288+
"language": "<from-registry-or-detection>",
289+
"features": {
290+
"<feature-name>": {
291+
"version": "1.0.0",
292+
"installedAt": "<ISO-timestamp>",
293+
"updatedAt": "<ISO-timestamp-or-null>",
294+
"files": [
295+
"<list-of-files-copied>"
296+
]
297+
}
298+
}
299+
}
300+
```
301+
302+
- New install: set `installedAt` to now, `updatedAt` to null
303+
- Update: keep original `installedAt`, set `updatedAt` to now
304+
305+
---
306+
307+
## Step 8 — Commit + Summary
308+
309+
```bash
310+
cd "$TARGET"
311+
git add -A
312+
git commit -m "feat: add <feature-name(s)> via /add-feature"
313+
```
314+
315+
Store: `FEATURE_HASH=$(git -C "$TARGET" rev-parse HEAD)`
316+
317+
**If nothing to commit** (all files unchanged): skip the commit, note "Already up to date."
318+
319+
### Display summary
320+
321+
```
322+
=== Feature Installation Complete ===
323+
324+
Target: $TARGET
325+
Feature(s): <name(s)>
326+
327+
Files: N added, N updated
328+
Dependencies: N installed
329+
Env vars: N added to .env.example
330+
Scripts: N added to package.json
331+
CLAUDE.md: N sections added
332+
333+
Pre-feature commit: $PRE_FEATURE_HASH
334+
Feature commit: $FEATURE_HASH
335+
336+
To undo: git revert HEAD
337+
To review: git diff $PRE_FEATURE_HASH..HEAD
338+
339+
Manifest updated: .claude/features.json
340+
```
341+
342+
---
343+
344+
## Edge Cases
345+
346+
1. **Feature already installed and up to date** — If all files are identical to source, report "Feature `<name>` is already up to date — no changes needed." and skip.
347+
348+
2. **No package.json** — If the target has no package.json (e.g., Go or Python project), skip dependency installation and script merging. Still copy files and update manifest.
349+
350+
3. **Multiple features at once** — Process each feature sequentially. One commit at the end with all features listed.
351+
352+
4. **Go/Python projects requesting mongo** — Copy the Node.js wrapper files but skip npm dependency installation. Note in summary: "Files copied but dependencies not installed (not a Node.js project). Install the MongoDB driver for your language manually."
353+
354+
5. **Source file missing** — If a file listed in the feature definition doesn't exist in `$SOURCE`, warn and skip that file. Don't fail the entire operation.

0 commit comments

Comments
 (0)