Project Kickoff
Bootstrap a new project so Claude Code has the right permissions from the start. The goal is zero permission prompts after setup — every command you'd reasonably use should be pre-approved.
Problem: Every new project accumulates permission approvals one click at a time. Project settings.local.json shadows global settings (does not merge), so each project needs its own complete list. This skill generates that list upfront.
Output: settings.local.json, CLAUDE.md, .gitignore (and optionally git init + GitHub repo).
Operating Modes
Mode 1: New Project Setup
When: Starting a new project, or working in a directory without .claude/settings.local.json.
Steps:
Detect project type from files present in the directory:
| Indicator |
Type |
wrangler.jsonc or wrangler.toml |
cloudflare-worker |
vercel.json or next.config.* |
vercel-app |
package.json (no deploy target) |
javascript-typescript |
pyproject.toml or setup.py or requirements.txt |
python |
Cargo.toml |
rust |
go.mod |
go |
Gemfile or Rakefile |
ruby |
composer.json or wp-config.php |
php |
Dockerfile or docker-compose.yml |
docker |
.claude/agents/ or operational scripts |
ops-admin |
| Empty directory |
Ask the user |
If ambiguous, ask. Types can stack (e.g. cloudflare-worker + javascript-typescript).
Generate .claude/settings.local.json:
Read references/permission-presets.md for preset definitions, then:
a. Always include Universal Base — file ops, text processing, network, system, git. These are needed by every project.
b. Add detected language presets — JS/TS, Python, PHP, Go, Rust, Ruby, etc. When in doubt, include more rather than less. Adding Bash(cargo *) to a JS project costs nothing if Rust isn't installed.
c. Add deployment presets if detected — Cloudflare Worker, Vercel, Docker, Cloud CLIs.
d. Auto-discover MCP servers — Use ToolSearch to find available MCP tools, extract the unique server names from tool names (e.g. mcp__vault__secret_list → server is vault), and generate mcp__servername__* for each. This is necessary because:
mcp__* (single wildcard) does NOT work — wildcard doesn't cross __ boundary
mcp__*__* (double wildcard) also does NOT work
- Each server must be listed individually:
mcp__vault__*, mcp__brain__*, etc.
e. Always include: WebSearch, WebFetch
f. Always include explicit gh subcommands alongside Bash(gh *) — there's a known bug where Bash(gh *) doesn't match some subcommands:
"Bash(gh *)",
"Bash(gh repo *)",
"Bash(gh issue *)",
"Bash(gh pr *)",
"Bash(gh api *)",
"Bash(gh search *)",
"Bash(gh run *)",
"Bash(gh release *)",
g. Write with // comment groups for organisation.
h. Warn the user: "Project settings.local.json SHADOWS your global settings — it does not merge. A session restart is needed for changes to take effect."
Generate CLAUDE.md:
- Read references/claude-md-templates.md for templates
- Fill in: project name (from directory name or ask), today's date, detected stack
- Pre-fill Jez's defaults (Cloudflare account ID, pnpm, EN-AU)
Generate .gitignore:
- Use the type-appropriate template from references/claude-md-templates.md
- Always include
.claude/settings.local.json and .dev.vars
Optionally (ask first):
git init + first commit
gh repo create jezweb/[name] --private + push
Mode 2: Tidy Existing Permissions
When: User says "tidy permissions", "clean settings", or the existing settings.local.json has more than ~50 entries.
Steps:
Run the tidy script to analyse the current file:
python3 ${SKILL_DIR}/scripts/tidy_permissions.py .claude/settings.local.json
Review the report. It flags:
- Leaked secrets: API keys, tokens, hex strings embedded in approval patterns
- Shell fragments:
Bash(do), Bash(fi), Bash(then), Bash(else), Bash(done)
- Legacy colon syntax:
Bash(git:*) → should be Bash(git *)
- Duplicates: Entries covered by a broader pattern already present (e.g.
Bash(git add *) is redundant if Bash(git *) exists)
- One-time entries: Entire commit messages, hardcoded paths that will never match again
- Consolidation opportunities: e.g. 5 separate
Bash(git add:*), Bash(git commit:*) could become Bash(git *)
- Missing MCP servers: Compare connected servers vs. what's in the allow list
Present the cleaned version with a diff showing what changed.
Apply after user confirmation. Recommend the user rotate any leaked secrets.
Mode 3: Add Preset
When: User says "add python permissions", "add MCP permissions", or "add docker permissions" to an existing project.
Steps:
- Read the relevant preset section from references/permission-presets.md
- Read the existing
.claude/settings.local.json
- Merge without duplicating — add new entries, keep existing groups
- Write the updated file
- Remind user: session restart required for changes to take effect
MCP Server Auto-Discovery
To discover connected MCP servers, run these ToolSearch queries:
ToolSearch("+brain")
ToolSearch("+vault")
ToolSearch("+playwright")
ToolSearch("+gmail")
ToolSearch("+xero")
Or search broadly: ToolSearch("mcp") and extract unique server prefixes from returned tool names.
Each discovered server gets a wildcard entry: mcp__servername__*
Permission Syntax Quick Reference
| Pattern |
Meaning |
Bash(git *) |
Preferred — space before * = word boundary |
Bash(nvidia-smi) |
Exact match, no arguments |
WebFetch |
Blanket web fetch (all domains) |
WebFetch(domain:x.com) |
Domain-scoped web fetch |
WebSearch |
Blanket web search |
mcp__brain__* |
All tools on one server |
mcp__brain__brain_sites |
One specific MCP tool |
What Does NOT Work
| Pattern |
Why |
mcp__* |
Wildcard doesn't cross __ boundary |
mcp__*__* |
Still doesn't work — segments aren't glob-expanded |
Bash(git:*) |
Deprecated colon syntax (still works but prefer space) |
Important Behaviours
- Not hot-reloaded: Edits to
settings.local.json require a session restart
- "Don't ask again" injects at runtime (no restart needed) using legacy colon format — this is normal
- Shadows, not merges: Project
settings.local.json completely replaces global allow list
- Shell safety:
Bash(git *) won't match git add && rm -rf / — operators are handled safely
gh bug: Bash(gh *) sometimes doesn't match subcommands — include explicit Bash(gh issue *) etc. as workaround
Autonomy
- Just do it: Detect project type, read existing files, auto-discover MCP servers
- Brief confirmation: Write settings.local.json, CLAUDE.md, .gitignore (show what will be written)
- Ask first: git init, GitHub repo creation, overwriting existing files, applying tidy fixes
Reference Files
| When |
Read |
| Building permission presets |
references/permission-presets.md |
| Generating CLAUDE.md and .gitignore |
references/claude-md-templates.md |
1---2name: project-kickoff3description: Bootstrap new projects with curated settings.local.json permissions, CLAUDE.md, and .gitignore. Detects project type and generates comprehensive permission presets so you never get prompted. Auto-discovers connected MCP servers. Also tidies existing messy settings files. Trigger with 'kickoff', 'new project', 'bootstrap', 'setup claude', 'tidy permissions', 'clean settings', or 'init project'.4---5
6# Project Kickoff
7
8Bootstrap a new project so Claude Code has the right permissions from the start. The goal is **zero permission prompts** after setup — every command you'd reasonably use should be pre-approved.
9
10**Problem**: Every new project accumulates permission approvals one click at a time. Project `settings.local.json` shadows global settings (does not merge), so each project needs its own complete list. This skill generates that list upfront.
11
12**Output**: `settings.local.json`, `CLAUDE.md`, `.gitignore` (and optionally git init + GitHub repo).
13
14## Operating Modes
15
16### Mode 1: New Project Setup
17
18**When**: Starting a new project, or working in a directory without `.claude/settings.local.json`.
19
20**Steps**:
21
221. **Detect project type** from files present in the directory:
23
24 | Indicator | Type |
25 |-----------|------|
26 | `wrangler.jsonc` or `wrangler.toml` | cloudflare-worker |
27 | `vercel.json` or `next.config.*` | vercel-app |
28 | `package.json` (no deploy target) | javascript-typescript |
29 | `pyproject.toml` or `setup.py` or `requirements.txt` | python |
30 | `Cargo.toml` | rust |
31 | `go.mod` | go |
32 | `Gemfile` or `Rakefile` | ruby |
33 | `composer.json` or `wp-config.php` | php |
34 | `Dockerfile` or `docker-compose.yml` | docker |
35 | `.claude/agents/` or operational scripts | ops-admin |
36 | Empty directory | Ask the user |
37
38 If ambiguous, ask. Types can stack (e.g. cloudflare-worker + javascript-typescript).
39
402. **Generate `.claude/settings.local.json`**:
41
42 Read [references/permission-presets.md](references/permission-presets.md) for preset definitions, then:
43
44 a. **Always include Universal Base** — file ops, text processing, network, system, git. These are needed by every project.
45
46 b. **Add detected language presets** — JS/TS, Python, PHP, Go, Rust, Ruby, etc. When in doubt, include more rather than less. Adding `Bash(cargo *)` to a JS project costs nothing if Rust isn't installed.
47
48 c. **Add deployment presets** if detected — Cloudflare Worker, Vercel, Docker, Cloud CLIs.
49
50 d. **Auto-discover MCP servers** — Use `ToolSearch` to find available MCP tools, extract the unique server names from tool names (e.g. `mcp__vault__secret_list` → server is `vault`), and generate `mcp__servername__*` for each. This is necessary because:
51 - `mcp__*` (single wildcard) does NOT work — wildcard doesn't cross `__` boundary
52 - `mcp__*__*` (double wildcard) also does NOT work
53 - Each server must be listed individually: `mcp__vault__*`, `mcp__brain__*`, etc.
54
55 e. **Always include**: `WebSearch`, `WebFetch`
56
57 f. **Always include explicit `gh` subcommands** alongside `Bash(gh *)` — there's a known bug where `Bash(gh *)` doesn't match some subcommands:
58 ```
59 "Bash(gh *)",
60 "Bash(gh repo *)",
61 "Bash(gh issue *)",
62 "Bash(gh pr *)",
63 "Bash(gh api *)",
64 "Bash(gh search *)",
65 "Bash(gh run *)",
66 "Bash(gh release *)",
67 ```
68
69 g. Write with `//` comment groups for organisation.
70
71 h. Warn the user: **"Project settings.local.json SHADOWS your global settings — it does not merge. A session restart is needed for changes to take effect."**
72
733. **Generate `CLAUDE.md`**:
74 - Read [references/claude-md-templates.md](references/claude-md-templates.md) for templates
75 - Fill in: project name (from directory name or ask), today's date, detected stack
76 - Pre-fill Jez's defaults (Cloudflare account ID, pnpm, EN-AU)
77
784. **Generate `.gitignore`**:
79 - Use the type-appropriate template from [references/claude-md-templates.md](references/claude-md-templates.md)
80 - Always include `.claude/settings.local.json` and `.dev.vars`
81
825. **Optionally** (ask first):
83 - `git init` + first commit
84 - `gh repo create jezweb/[name] --private` + push
85
86### Mode 2: Tidy Existing Permissions
87
88**When**: User says "tidy permissions", "clean settings", or the existing `settings.local.json` has more than ~50 entries.
89
90**Steps**:
91
921. Run the tidy script to analyse the current file:
93 ```bash
94 python3 ${SKILL_DIR}/scripts/tidy_permissions.py .claude/settings.local.json
95 ```
96
972. Review the report. It flags:
98 - **Leaked secrets**: API keys, tokens, hex strings embedded in approval patterns
99 - **Shell fragments**: `Bash(do)`, `Bash(fi)`, `Bash(then)`, `Bash(else)`, `Bash(done)`
100 - **Legacy colon syntax**: `Bash(git:*)` → should be `Bash(git *)`
101 - **Duplicates**: Entries covered by a broader pattern already present (e.g. `Bash(git add *)` is redundant if `Bash(git *)` exists)
102 - **One-time entries**: Entire commit messages, hardcoded paths that will never match again
103 - **Consolidation opportunities**: e.g. 5 separate `Bash(git add:*)`, `Bash(git commit:*)` could become `Bash(git *)`
104 - **Missing MCP servers**: Compare connected servers vs. what's in the allow list
105
1063. Present the cleaned version with a diff showing what changed.
107
1084. Apply after user confirmation. Recommend the user rotate any leaked secrets.
109
110### Mode 3: Add Preset
111
112**When**: User says "add python permissions", "add MCP permissions", or "add docker permissions" to an existing project.
113
114**Steps**:
115
1161. Read the relevant preset section from [references/permission-presets.md](references/permission-presets.md)
1172. Read the existing `.claude/settings.local.json`
1183. Merge without duplicating — add new entries, keep existing groups
1194. Write the updated file
1205. Remind user: **session restart required** for changes to take effect
121
122## MCP Server Auto-Discovery
123
124To discover connected MCP servers, run these ToolSearch queries:
125
126```
127ToolSearch("+brain")
128ToolSearch("+vault")
129ToolSearch("+playwright")
130ToolSearch("+gmail")
131ToolSearch("+xero")
132```
133
134Or search broadly: `ToolSearch("mcp")` and extract unique server prefixes from returned tool names.
135
136Each discovered server gets a wildcard entry: `mcp__servername__*`
137
138## Permission Syntax Quick Reference
139
140| Pattern | Meaning |
141|---------|---------|
142| `Bash(git *)` | Preferred — space before `*` = word boundary |
143| `Bash(nvidia-smi)` | Exact match, no arguments |
144| `WebFetch` | Blanket web fetch (all domains) |
145| `WebFetch(domain:x.com)` | Domain-scoped web fetch |
146| `WebSearch` | Blanket web search |
147| `mcp__brain__*` | All tools on one server |
148| `mcp__brain__brain_sites` | One specific MCP tool |
149
150### What Does NOT Work
151
152| Pattern | Why |
153|---------|-----|
154| `mcp__*` | Wildcard doesn't cross `__` boundary |
155| `mcp__*__*` | Still doesn't work — segments aren't glob-expanded |
156| `Bash(git:*)` | Deprecated colon syntax (still works but prefer space) |
157
158### Important Behaviours
159
160- **Not hot-reloaded**: Edits to `settings.local.json` require a **session restart**
161- **"Don't ask again"** injects at runtime (no restart needed) using legacy colon format — this is normal
162- **Shadows, not merges**: Project `settings.local.json` completely replaces global allow list
163- **Shell safety**: `Bash(git *)` won't match `git add && rm -rf /` — operators are handled safely
164- **`gh` bug**: `Bash(gh *)` sometimes doesn't match subcommands — include explicit `Bash(gh issue *)` etc. as workaround
165
166## Autonomy
167
168- **Just do it**: Detect project type, read existing files, auto-discover MCP servers
169- **Brief confirmation**: Write settings.local.json, CLAUDE.md, .gitignore (show what will be written)
170- **Ask first**: git init, GitHub repo creation, overwriting existing files, applying tidy fixes
171
172## Reference Files
173
174| When | Read |
175|------|------|
176| Building permission presets | [references/permission-presets.md](references/permission-presets.md) |
177| Generating CLAUDE.md and .gitignore | [references/claude-md-templates.md](references/claude-md-templates.md) |