Claude Code CLI Configuration
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Overview
This guide covers Claude Code CLI1 configuration: hooks, permissions, subagents, commands, and MCP. For universal AI workflows (Hero Flow, TDD, Visual Iteration), see AI Workflows.
Quick Reference
| Task | Command/File |
|---|---|
| Start session | claude |
| Plan mode | /plan |
| Headless mode | claude -p "prompt" |
| Permissions | /permissions |
| Clear context | /clear |
| Config file | .claude/settings.json |
| Hook scripts | .claude/hooks/*.sh |
| Subagents | .claude/agents/*.md |
| Commands | .claude/commands/*.md |
| MCP servers | .mcp.json |
Table of Contents
- Configuration Files
- Hooks
- Permissions
- Subagents
- Custom Commands
- MCP Integration
- Long-Running Sessions
- Syncing from crefft
Configuration Files
Claude Code uses several configuration files:
project/
├── .claude/
│ ├── settings.json # Permissions, hooks, preferences
│ ├── hooks/ # Hook scripts invoked from settings.json
│ │ ├── check-file.sh
│ │ └── verify-complete.sh
│ ├── agents/ # Reusable subagent definitions
│ │ ├── code/ # Code quality agents
│ │ │ ├── architect.md
│ │ │ ├── reviewer.md
│ │ │ └── ...
│ │ ├── system/ # Infrastructure agents
│ │ │ ├── architect.md
│ │ │ ├── docker.md
│ │ │ └── ...
│ │ └── security/ # Security agents
│ │ └── ...
│ └── commands/ # Custom slash commands
│ ├── code.md
│ ├── system.md
│ └── ...
├── .mcp.json # MCP server configuration
├── AGENTS.md # Project context (see agents-md.md)
├── CLAUDE.md # `@AGENTS.md` import
└── GEMINI.md # `@./AGENTS.md` import
settings.json Structure
{
"permissions": {
"allow": [...],
"deny": [...]
},
"hooks": {
"PostToolUse": [...],
"PreToolUse": [...],
"Stop": [...]
},
"preferences": {
"autoCompact": true,
"compactThreshold": 100000
}
}
See configs/claude/settings.json for crefft’s standard configuration.
Hooks
Hooks automate quality checks and enable long-running sessions. They MUST be configured for professional workflows.
Hook Types
| Hook | When it Runs | Use Case |
|---|---|---|
PostToolUse | After Claude uses a tool | Auto-format, auto-lint |
PreToolUse | Before Claude uses a tool | Validation, logging |
Stop | When Claude stops | Verify completion, auto-resume |
Hook Exit Codes
Exit codes decide whether a hook is a gate or a comment. Claude Code treats exit 2 as the blocking status; every other non-zero code is a non-blocking error that lets the action proceed2.
| Exit code | Effect on PostToolUse | Effect on Stop |
|---|---|---|
0 | Success. stderr goes to the debug log only; Claude never sees it | Claude stops |
1 (or any other non-zero) | Non-blocking error, stderr not shown to Claude | Claude stops anyway |
2 | Tool already ran, but Claude sees stderr | Blocks the stop; stderr becomes the reason to continue |
Hooks MUST NOT end a check with || true and MUST NOT rely on exit
code 1 to gate anything.
Why
|| true replaces the check’s status with 0, and exit 1 is non-blocking, so
both turn a failed check into a silent success:
# Don't: both of these report success to Claude Code
sh -c 'npm run lint || true' # exits 0 even when lint fails
sh -c 'npm test && npm run lint' # exits 1, which does not block Stop
# Do: report the reason on stderr and exit 2
printf 'lint failed on src/auth.ts\n' >&2
exit 2
PostToolUse: Auto-Format
MUST auto-format after file changes. This catches the “last 10%” of style issues, and MUST surface a formatter failure to Claude instead of discarding it:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-file.sh",
"args": ["npm", "run", "format", "--", "--write"]
}
]
}
]
}
}
check-file.sh runs the command it is given against the file Claude just
wrote, and reports a failure the only way PostToolUse can — stderr plus
exit 2:
#!/usr/bin/env bash
# .claude/hooks/check-file.sh - run "<argv> <edited file>" after Write or Edit.
set -uo pipefail
file=$(jq -r '.tool_input.file_path // empty')
[ -n "$file" ] || exit 0
if ! output=$("$@" "$file" 2>&1); then
printf '%s failed on %s:\n%s\n' "$*" "$file" "$(tail -n 20 <<<"$output")" >&2
exit 2
fi
exit 0
Make it executable with chmod +x .claude/hooks/check-file.sh.
Why
args selects Claude Code’s exec form, so the script path and each argument
are passed verbatim with no shell tokenization — the correct form whenever a
command references a path placeholder such as ${CLAUDE_PROJECT_DIR}2.
Reading .tool_input.file_path into a quoted variable keeps paths containing
spaces intact. Truncating to the last 20 lines keeps the feedback short enough
to be actionable.
PostToolUse: Auto-Lint
SHOULD lint after file changes, using the same script:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-file.sh",
"args": ["npm", "run", "lint:file", "--"]
}
]
}
]
}
}
A per-file lint hook is a fast signal, not proof that the change is clean. The whole-change gate below MUST still run.
Stop: Verify Completion
MUST verify before accepting completion on important tasks. A Stop hook
that runs the gate commands directly does not work: the shell reports exit 1
on failure, which Claude Code treats as non-blocking. Call a script that exits
2 instead:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/verify-complete.sh",
"args": []
}
]
}
]
}
}
#!/usr/bin/env bash
# .claude/hooks/verify-complete.sh - Stop hook completion gate.
set -uo pipefail
hook_input=$(cat)
# Claude Code overrides the hook after 8 consecutive blocks; stop asking
# once a previous block already resumed the turn.
if [ "$(jq -r '.stop_hook_active // false' <<<"$hook_input")" = "true" ]; then
exit 0
fi
failures=""
run_gate() {
local label=$1
shift
local output
if ! output=$("$@" 2>&1); then
failures+="${label} failed:"$'\n'"$(tail -n 20 <<<"$output")"$'\n\n'
fi
}
run_gate "npm test" npm test
run_gate "npm run lint" npm run lint
run_gate "npm run typecheck" npm run typecheck
if [ -n "$failures" ]; then
printf '%s' "$failures" >&2
exit 2
fi
exit 0
Why
Every gate runs, so one failing suite does not hide the next; the stderr text
becomes the reason Claude is shown for continuing. The stop_hook_active
check prevents a gate that can never pass from looping, and Claude Code caps
continuations at eight consecutive blocks regardless2. A Stop hook is
therefore a strong gate, not a guarantee that Claude never stops early.
Hooks MAY instead exit 0 and print blocking JSON on stdout; Claude
receives the reason exactly as it receives exit-2 stderr2:
{
"decision": "block",
"reason": "npm test failed: 2 failing in src/auth.test.ts"
}
Make the script executable with chmod +x .claude/hooks/verify-complete.sh.
A gate whose script is missing or not executable exits 127, which Claude Code
reports as a non-blocking error — the session stops with the gate silently
disabled2.
For a completion condition that applies to one session rather than every
session in the project, SHOULD use /goal3 instead of editing
settings.json.
Hook Input
Hook commands receive a JSON payload on stdin (there are no
$CLAUDE_* environment variables for tool data). Extract fields with jq:
| JSON field | Value | Event |
|---|---|---|
.tool_input.file_path | Path to file being written/edited | PostToolUse |
.tool_name | Name of tool being used | PreToolUse, PostToolUse |
.stop_hook_active | true when a stop hook already resumed the turn | Stop |
Example: file=$(jq -r '.tool_input.file_path // empty')
Permissions
MUST configure permissions to avoid prompt fatigue without sacrificing safety.
Permission Syntax
ToolName(pattern)
Examples:
Bash(npm run build)— Allow exactlynpm run buildBash(npm run *)— Allow every scriptpackage.jsondefinesEdit(src/**)— Allow file writes/edits in src/ and subdirectories (file-tool path rules are matched asEdit(path);Write(path)rules are not matched and are dead)Read(~/.ssh/**)— Match everything under the home directory’s.ssh
Rules are evaluated deny, then ask, then allow, and the first match wins. A deny rule therefore cannot carry allowlist exceptions, and an ask rule prompts even when a narrower allow rule also matches.3
crefft’s Standard Allowlist
Allow rules MUST name reviewed commands. They MUST NOT pre-approve a package runner or environment runner, because those execute whatever argument follows them:
{
"permissions": {
"allow": [
"Bash(npm run build)",
"Bash(npm run lint)",
"Bash(npm run typecheck)",
"Bash(npm test *)",
"Bash(git status *)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git show *)",
"Bash(git branch *)",
"Bash(rg *)",
"Edit(src/**)",
"Edit(tests/**)",
"Edit(test/**)",
"Edit(docs/**)"
]
}
}
Why
No blanket npx. Claude Code strips a fixed set of wrappers before
matching a Bash rule, and environment runners such as npx, docker exec,
and devbox run are deliberately not in that set: a rule matches whatever
follows the runner, so Bash(npx *) pre-approves any package the registry
will serve.3 Approve the inner command instead, one rule per command.
Package scripts belong to the repository. Bash(npm run *) approves every
script package.json defines now and every script a branch, a dependency
bump, or a pull request adds later. Name the scripts you reviewed. A team
MAY widen this to Bash(npm run *) for a repository whose manifest it
owns and reviews; it MUST NOT do so for code it has not reviewed.
No blanket Read, Glob, or Grep. Read-only tools already run without
approval inside the working directory and additional directories,3 so
Read(*) adds nothing there and grants the rest of the filesystem, including
~/.aws and ~/.ssh.
Narrow git mutations. Bash(git *) matches every subcommand and every
option before it, including git -c core.fsmonitor=<script> diff, which runs
a program of the caller’s choosing.3 Allow read-only subcommands; route
add, commit, checkout, and push through ask rules.
Ask Rules for Mutation and Network
Commands that change history or reach the network SHOULD prompt rather than run unattended:
{
"permissions": {
"ask": [
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git checkout *)",
"Bash(git push *)",
"Bash(npm install *)",
"Bash(npx *)",
"Bash(curl *)",
"Bash(wget *)",
"Bash(ssh *)"
]
}
}
Argument-level Bash patterns are fragile — Bash(curl https://github.com/ *)
misses curl -X GET, a redirect, or URL=… && curl $URL — so gate the tool
and use WebFetch(domain:example.com) for reviewed domains instead of trying
to constrain URLs inside a shell rule.3
crefft’s Standard Denylist
Secret material MUST be denied for reads as well as edits:
{
"permissions": {
"deny": [
"Bash(rm -rf *)",
"Bash(sudo *)",
"Bash(curl * | bash)",
"Bash(wget * | bash)",
"Bash(chmod 777 *)",
"Bash(> /dev/sd*)",
"Read(.env)",
"Read(.env.*)",
"Read(**/secrets/**)",
"Read(**/*secret*)",
"Read(**/*password*)",
"Read(**/*credential*)",
"Read(**/*token*)",
"Read(**/*.pem)",
"Read(**/*.key)",
"Read(**/id_rsa*)",
"Read(~/.aws/**)",
"Read(~/.ssh/**)",
"Edit(.env)",
"Edit(.env.*)",
"Edit(**/secrets/**)",
"Edit(**/*secret*)",
"Edit(**/*password*)",
"Edit(**/*credential*)",
"Edit(**/*token*)",
"Edit(**/*.pem)",
"Edit(**/*.key)",
"Edit(**/id_rsa*)"
]
}
}
Why
A Read deny rule blocks Claude’s file tools, the file commands Claude Code
recognizes in Bash such as cat, head, and sed, and Bash redirection
targets. It also blocks Edit from v2.1.208 and Write from v2.1.228, but it
never covers NotebookEdit, so keep the paired Edit denies.3 Bare
filenames follow gitignore semantics: Read(.env) and Read(**/.env) are
equivalent and match at any depth under the current directory.3
Permission rules are not an OS boundary. They do not constrain a subprocess
that opens a file itself — a Python or Node script Claude runs reads
~/.aws/credentials regardless of any Read deny. Pair this policy with the
sandbox, which enforces filesystem and network limits at the OS level.4
Managing Permissions
# Interactive permission management
/permissions
# View current permissions
/permissions list
# Add permission for session
/permissions allow Bash(docker compose *)
Subagents
Subagents are specialized Claude instances organized into agent families. Each family has an Opus coordinator and Sonnet specialists.
Agent Families
crefft provides three agent families:
| Family | Command | Coordinator | Purpose |
|---|---|---|---|
| Code | /code | Code Architect (Opus) | Code quality review |
| System | /system | System Architect (Opus) | Infrastructure review |
| Security | /security | Security Architect (Opus) | Security analysis |
See the family guides for full details:
Code Agent Family (/code)
| Agent | File | Purpose |
|---|---|---|
| Code Architect | code/architect.md | Opus coordinator |
| Code Reviewer | code/reviewer.md | General code review |
| Performance | code/performance.md | Deep performance analysis |
| Accessibility | code/accessibility.md | WCAG/A11y compliance |
| REST API | code/api-rest.md | REST API design |
| GraphQL API | code/api-graphql.md | GraphQL schema design |
| Test Writer | code/test-writer.md | Test generation |
| Simplifier | code/simplifier.md | Complexity reduction |
System Agent Family (/system)
| Agent | File | Purpose |
|---|---|---|
| System Architect | system/architect.md | Opus coordinator |
| Docker | system/docker.md | Container security |
| Ansible | system/ansible.md | Playbook review |
| Linux | system/linux.md | OS hardening |
| Verify | system/verify.md | CI/CD validation |
Unique Differentiators
crefft’s agents are the most comprehensive open-source option:
- Tiered architecture — Opus coordinators, Sonnet specialists, cost optimized
- Accessibility reviews — No competitor offers WCAG/A11y analysis
- API design reviews — REST and GraphQL specialists
- Infrastructure reviews — Docker, Ansible, Linux specialists
- crefft integration — References our style guides
- Multi-LLM compatible — Works with Claude, GPT, Gemini
- Auto-fix suggestions — Every issue includes fix, not just complaint
Using Agents
# Full code assessment (Opus coordinates specialists)
/code src/auth/
# Quick review (Haiku, critical only)
/code quick
# Specific specialist
/code perf src/services/
# Full infrastructure assessment
/system stacks/
# Specific system review
/system docker stacks/platform/
Custom Commands
Custom slash commands are reusable prompt templates.
Creating Commands
Store in .claude/commands/:
# .claude/commands/code.md
Analyze the code at: $ARGUMENTS
Based on subcommand, route to appropriate agent:
- (none) → Full assessment with Code Architect
- quick → Critical issues only (Haiku)
- review → Standard code review (Sonnet)
- perf → Performance Reviewer
- a11y → Accessibility Reviewer
...
Using Commands
/code src/auth/
# Full assessment with Code Architect
/code quick
# Quick review of staged changes
/code perf src/services/
# Performance-focused review
crefft’s Standard Commands
| Command | Purpose | Subcommands |
|---|---|---|
/code | Code quality review | quick, review, perf, a11y, api, tests, simplify, docs |
/system | Infrastructure review | docker, ansible, linux, verify |
/security | Security analysis | See Security Agent Family |
/refactor | Refactor code | --explain, --metrics, --incremental, --safe |
/explain | Explain code | --deep |
/plan | Create implementation plan | Feature description |
See commands/ for full definitions.
MCP Integration
Model Context Protocol (MCP)5 connects Claude to external tools.
.mcp.json Configuration
{
"mcpServers": {
"slack": {
"type": "http",
"url": "https://mcp.slack.com/mcp"
},
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@playwright/mcp@0.0.80"]
}
}
}
Every entry MUST declare a type. Claude Code reads an entry that has a
url but no type as a stdio server, skips it, and reports the
misconfiguration.6
Slack
Slack serves MCP over JSON-RPC 2.0 on Streamable HTTP at
https://mcp.slack.com/mcp. SSE connections and Dynamic Client Registration
are not supported.7 A bare URL is not an authenticated configuration:
- Registered app. Every MCP client MUST be backed by a registered Slack app with a fixed app ID, and only Marketplace-published or internal apps may use MCP.7 Claude Code is one of Slack’s listed partner clients, so a team MAY connect through it without building an app of its own.
- Admin approval. Workspace admins approve MCP clients through the standard Slack app approval process, and an app’s allowed IP ranges apply to MCP traffic too.7
- Confidential OAuth. A client of your own authenticates with its
client_idandclient_secretagainsthttps://slack.com/oauth/v2_user/authorizeandhttps://slack.com/api/oauth.v2.user.access, or discovers them fromhttps://mcp.slack.com/.well-known/oauth-authorization-server. Desktop clients SHOULD use PKCE.7 - Least-privilege scopes. Grant only the user-token scopes the tools you
actually call:
search:read.publicto search public messages,channels:historyto read a channel,chat:writeto post.7 A read-only workflow MUST NOT request write scopes.
claude mcp add --transport http slack https://mcp.slack.com/mcp
Then run /mcp inside the session: it runs the OAuth flow for remote servers
and shows each server’s connection state.6
Common MCP Servers
| Server | Purpose | Type |
|---|---|---|
| Playwright | Browser automation, screenshots | stdio |
| Slack | Search and post messages | http |
| Sentry | Error logs and debugging | http |
| PostgreSQL | Database queries | stdio |
| GitHub | Issues, PRs, actions | http |
MCP for Visual Iteration
SHOULD use Claude Code’s built-in Chrome integration4 for screenshots and browser checks, because it needs no MCP server and drives the browser you are already signed into:
claude --chrome
Prerequisites MUST be met before relying on it4:
- Chrome, Edge, or another Chromium browser, plus the Claude in Chrome extension 1.0.36 or later
- Sign-in with
/loginon a direct Anthropic plan (Pro, Max, Team, or Enterprise) — API-key andclaude setup-tokensessions keep the integration off - Not available under Windows Subsystem for Linux
Where those prerequisites do not hold, SHOULD use Microsoft’s
@playwright/mcp, pinned to an exact version6:
{
"mcpServers": {
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@playwright/mcp@0.0.80", "--isolated", "--headless"]
}
}
}
Why
@anthropic/mcp-puppeteer does not exist on the public registry, and
@modelcontextprotocol/server-puppeteer is marked deprecated (“Package no
longer supported”)7, so both fail before browser automation starts.
@playwright/mcp 0.0.80 is the current release6 and requires Node.js 18 or
newer.
Pin the version: @latest re-resolves on every launch, so an unpinned npx
line runs whatever was published since the config was reviewed. --isolated
keeps the browser profile in memory, so a session cannot inherit or persist
cookies from a previous run, and lets several clients share one workspace
without fighting over the persistent profile6.
The server drives a real browser with network access. Treat any page it visits as untrusted input: point it at your own development server, and MUST NOT give it credentials that matter more than the task.
Then in conversation:
Take a screenshot of http://localhost:3000/login
Why the browser package is pinned
Prerequisite. @playwright/mcp 0.0.80 requires Node.js 18 or newer.[^8]
Pin the version. @playwright/mcp@latest re-resolves on every start, so a
release can change agent behavior between two runs of the same task. Pin the
version and bump it deliberately. --isolated keeps the browser profile in
memory, and --headless overrides the headed default.[^8]
MCP or CLI. Microsoft notes that CLI invocations exposed as Skills are more token-efficient for coding agents, because they avoid loading large tool schemas and accessibility trees into context, and keeps MCP for work that benefits from persistent browser state and iterative reasoning over page structure, such as exploratory automation and self-healing tests.[^8] Visual iteration is that second case, so this guide configures MCP; a team whose browser work is scripted SHOULD compare the pinned Playwright CLI Skill.
Long-Running Sessions
Claude Code can run for hours or days with proper configuration.
Permission Modes
| Mode | Flag | Use Case |
|---|---|---|
| Normal | (default) | Interactive development |
| Don’t Ask | --permission-mode=dontAsk | Long tasks, respects denylist |
| Skip All | --dangerously-skip-permissions | Sandboxed environments only |
Stop Hooks for Auto-Resume
Stop hooks verify completion and auto-resume when criteria are not met. Use
the same enforcing gate as Stop: Verify Completion
— a plain npm test && npm run lint command exits 1 on failure, which Claude
Code treats as non-blocking:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/verify-complete.sh",
"args": []
}
]
}
]
}
}
Auto-resume is bounded: Claude Code ends the turn after eight consecutive blocks2. A long-running session MUST NOT be planned on the assumption that the hook resumes indefinitely.
The ralph-wiggum Plugin
Keeps prompting Claude until real completion:
claude plugins install ralph-wiggum
Long-Running Session Checklist
- Permissions allowlist configured
- PostToolUse hooks for auto-format/lint
- Stop hooks for verification
- Clear completion criteria defined
- Running in sandbox/container (if using skip-permissions)
Syncing from crefft
Manual installs and automated syncs MUST call
scripts/sync-claude-config.sh from a
checkout at a pinned release tag. The script requires Bash, Git, and Python 3.
It reads agents/ and commands/ at the repository root, plus
configs/claude/skills/, configs/claude/infrastructure/, and
configs/claude/settings.json. A missing component is an error before any
destination write.
Why
One applier gives manual installs and workflows the same source layout, ownership checks, and settings policy. Copying directories independently can delete project files or silently omit part of a release.
Manual Sync
Run from the consuming project’s root:
CREFFT_REF=v2026.10.1
git clone --branch "${CREFFT_REF}" --depth 1 \
https://github.com/agh/crefft.git "${HOME}/.crefft"
"${HOME}/.crefft/scripts/sync-claude-config.sh" --ref "${CREFFT_REF}" "${PWD}"
For an existing clone, fetch the chosen release, check it out, and call the same script:
CREFFT_REF=v2026.10.1
git -C "${HOME}/.crefft" fetch --depth 1 origin \
"refs/tags/${CREFFT_REF}:refs/tags/${CREFFT_REF}"
git -C "${HOME}/.crefft" checkout --detach "${CREFFT_REF}"
"${HOME}/.crefft/scripts/sync-claude-config.sh" --ref "${CREFFT_REF}" "${PWD}"
Do: keep the checkout at the selected release and invoke its script.
Don’t: copy or mirror configs/claude/ into the project; agents and
commands no longer live there, and whole-directory deletion has no ownership
boundary.
Ownership and Settings
The script writes .claude/crefft-receipt.json, recording the release tag,
resolved commit SHA, and SHA-256 of every installed file. Consumers MUST
commit the receipt alongside the installed files.
Later syncs replace or remove only receipt-owned files. Project-specific agents, commands, and skills beside those files survive. A conflicting unowned path, a symlink, or a locally modified owned file stops the entire sync with a conflict list before any write. The script never deletes whole component directories. Repeating the same release changes neither the files nor the receipt.
For an earlier manual installation without a receipt, consumers MAY pass
--adopt to claim existing files whose contents are identical to the release.
Different contents still conflict; file modes and timestamps do not establish
identity. Review and relocate conflicting files rather than deleting them
blindly.
Existing .claude/settings.json is kept, even on upgrades. It is installed
only when absent unless --overwrite-settings explicitly permits replacing
it. An existing unowned settings file is not claimed by default.
Why
The receipt is the deletion boundary, not the containing directory. Retaining it makes an upstream removal safe without removing a project’s own configuration. Settings can contain project-specific permissions and hooks, so replacing them requires a separate opt-in.
GitHub Action for Auto-Sync
Use the maintained
sync-crefft.yml template,
not an embedded copying workflow:
mkdir -p .github/workflows
curl -fsSL -o .github/workflows/sync-crefft.yml \
https://raw.githubusercontent.com/agh/crefft/v2026.10.1/configs/github/workflows/sync-crefft.yml
Follow the adoption notes
for repository permissions. The template checks out CREFFT_REF
(v2026.10.1 by default) into .crefft-upstream/ and calls
.crefft-upstream/scripts/sync-claude-config.sh. Set CREFFT_COMMIT to a
reviewed full commit SHA for additional verification. Every action remains
pinned by full SHA.
The workflow opens a pull request containing only .claude/ changes,
including the receipt. A manual dry run invokes the same applier in a
temporary project clone and reports changes without altering the project’s
workspace or opening a pull request.
Version Pinning
Consumers MUST select a release tag, not main. The script verifies that
HEAD resolves to that tag, that VERSION matches it, and that source inputs
are clean. It records both the tag and resolved commit in the receipt.
Tarball-only installs are not supported because they lack the Git metadata
needed to verify the checkout.
Consumers SHOULD also supply --commit with a full SHA obtained through
their release-review process; the workflow exposes the same check as
CREFFT_COMMIT. A mismatch fails before destination writes. Passing the
checkout’s own SHA without independently reviewing it adds no protection.
Why
A release pin makes updates deliberate. A separately reviewed commit pin also detects a moved tag. Checking the source tree prevents an edited checkout from claiming the identity of an unchanged release.
See Also
- Code Agent Family — Code quality review agents
- System Agent Family — Infrastructure review agents
- Security Agent Family — Security analysis agents
- AI Workflows — Hero Flow, TDD, Visual Iteration
- AGENTS.md Patterns — Project context files
- Claude Best Practices — Model selection, API usage
References
Footnotes
-
Claude Code Documentation — Official CLI documentation ↩
-
Hooks reference — Exit codes,
stop_hook_active, the eight-block continuation cap, exec form vs shell form ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Keep Claude working toward a goal — Session-scoped completion conditions with
/goal↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 -
Use Claude Code with Chrome — Built-in browser integration and its prerequisites ↩ ↩2 ↩3
-
MCP Documentation — Model Context Protocol specification ↩
-
@playwright/mcpregistry metadata — Version 0.0.80, published 2026-09-01; Node.js 18+ ↩ ↩2 ↩3 ↩4 ↩5 -
@modelcontextprotocol/server-puppeteerregistry metadata —deprecated: "Package no longer supported"↩ ↩2 ↩3 ↩4 ↩5 ↩6