docs: add comprehensive documentation for codeflash-cc-plugin

Six documents covering getting started, usage, configuration, architecture,
hook lifecycle, and troubleshooting for the Claude Code plugin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
aseembits93 2026-03-17 15:54:05 -07:00
parent 1e92f3d2ed
commit 9cc2b949cc
6 changed files with 689 additions and 0 deletions

133
docs/architecture.md Normal file
View file

@ -0,0 +1,133 @@
# Architecture
This document describes the plugin's components, data flow, and internal protocols.
## Data flow
```
User types /optimize User commits code
| |
v v
SKILL.md hooks.json
(fork context) (Stop event, * matcher)
| |
v v
optimizer agent suggest-optimize.sh
(15 turns, inherited model) (bash, 30s timeout)
| |
v v
Verify env & config Detect new commits
| with .py/.js/.ts files
v |
codeflash CLI v
(background, 10min timeout) Block Claude's stop
| with suggestion message
v |
Results reported Claude acts on suggestion
to user (install / configure / run)
```
## Component inventory
| File | Type | Purpose |
|------|------|---------|
| `.claude-plugin/plugin.json` | Manifest | Plugin identity, version, metadata |
| `.claude-plugin/marketplace.json` | Manifest | Marketplace listing, owner info |
| `skills/optimize/SKILL.md` | Skill | `/optimize` slash command definition |
| `commands/setup.md` | Command | `/setup` slash command for auto-permissions |
| `agents/optimizer.md` | Agent | Background optimization agent with full workflow |
| `hooks/hooks.json` | Hook config | Registers the Stop hook |
| `scripts/suggest-optimize.sh` | Hook script | Commit detection, dedup, project discovery |
| `scripts/find-venv.sh` | Helper script | Python venv auto-discovery |
## Skill format
Skills use YAML frontmatter in a Markdown file:
```yaml
---
name: optimize
description: Optimize Python, JavaScript, or TypeScript code for performance using Codeflash
user-invocable: true
argument-hint: "[--file] [--function] [--subagent]"
context: fork # Forks context so optimization doesn't pollute main conversation
agent: codeflash:optimizer # Delegates to the optimizer agent
allowed-tools: Task
---
```
The `context: fork` setting means the skill runs in a forked context -- the optimizer agent gets its own conversation branch, keeping the main session clean.
## Agent format
Agents use YAML frontmatter followed by a system prompt:
```yaml
---
name: optimizer
description: |
Optimizes Python and JavaScript/TypeScript code for performance...
model: inherit # Uses the same model as the parent conversation
maxTurns: 15 # Maximum number of agent turns
color: cyan # Status line color
tools: Read, Glob, Grep, Bash, Write, Edit
---
```
The agent body contains the full workflow: project detection, environment verification, configuration setup, running codeflash, and error handling.
## Hook system
### `hooks.json` structure
```json
{
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/suggest-optimize.sh",
"timeout": 30
}
]
}
]
}
}
```
- **Event**: `Stop` -- fires every time Claude finishes a response
- **Matcher**: `*` -- matches all stops (no filtering by tool or content)
- **Timeout**: 30 seconds for the hook script to complete
- **`${CLAUDE_PLUGIN_ROOT}`**: Resolved by Claude Code to the plugin's install directory
### Hook stdin/stdout protocol
**Input** (JSON on stdin):
```json
{
"stop_hook_active": false,
"transcript_path": "/path/to/transcript.jsonl"
}
```
**Output** (JSON on stdout):
```json
{"decision": "block", "reason": "message for Claude to act on"}
```
Or no output / exit 0 to allow the stop (no blocking).
The `decision` field can be:
- `"block"` -- prevents Claude from stopping, injects `reason` as a new prompt for Claude to act on
- Absent / script exits 0 without output -- allows the stop
## State files
| File | Purpose | Lifetime |
|------|---------|----------|
| `/tmp/codeflash-hook-debug.log` | Debug output from the hook script (`set -x` stderr) | Persists across sessions until manually cleared |
| `$TRANSCRIPT_DIR/codeflash-seen` | SHA-256 hashes of already-processed commit sets | Per-session (lives alongside the transcript file) |

117
docs/configuration.md Normal file
View file

@ -0,0 +1,117 @@
# Configuration
This document covers all configuration surfaces: plugin manifests, project-level codeflash config, and Claude Code permissions.
## Plugin manifests
These files live in `.claude-plugin/` and define the plugin for Claude Code's plugin system. You generally don't need to modify them.
### `plugin.json`
```json
{
"name": "codeflash",
"description": "Run codeflash as a background agent to optimize code for performance",
"version": "0.1.10",
"author": { "name": "Codeflash", "url": "https://codeflash.ai" },
"repository": "https://github.com/codeflash-ai/codeflash-cc-plugin",
"license": "MIT",
"keywords": ["python", "javascript", "typescript", "optimization", "performance"]
}
```
### `marketplace.json`
Defines the plugin for the Claude Code marketplace. Contains owner info, metadata, and a `plugins` array with the same fields as `plugin.json` plus `source` (relative path) and `category`.
## Python project configuration
Codeflash reads its configuration from `[tool.codeflash]` in `pyproject.toml`.
### Full reference
```toml
[tool.codeflash]
# All paths are relative to this pyproject.toml's directory.
module-root = "src" # Root of your Python module (where tests import from)
tests-root = "tests" # Directory containing your test files
ignore-paths = [] # Paths to exclude from optimization
formatter-cmds = ["disabled"] # Formatter commands, or ["disabled"] to skip formatting
```
### Fields
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `module-root` | Yes | -- | Relative path to the module root. If your tests do `from mypackage import ...`, then `mypackage/` is the module root |
| `tests-root` | Yes | -- | Relative path to the tests directory |
| `ignore-paths` | No | `[]` | List of paths to exclude from optimization |
| `formatter-cmds` | No | `["disabled"]` | List of formatter commands. Each can include flags, e.g. `"black --line-length 88 {file}"`. Use `["disabled"]` to skip formatting |
### Auto-discovery
When configuration is missing, the optimizer agent discovers values automatically:
- **module-root**: Uses Glob and Read to find the Python package directory (the one tests import from)
- **tests-root**: Looks for directories named `tests` or `test`, or folders containing `test_*.py` files. Falls back to `tests` (creates it if needed)
## JS/TS project configuration
Codeflash reads its configuration from a `"codeflash"` key at the root level of `package.json`.
### Full reference
```json
{
"codeflash": {
"moduleRoot": "src",
"testsRoot": "tests",
"formatterCmds": ["disabled"],
"ignorePaths": ["dist", "**/node_modules", "**/__tests__"]
}
}
```
### Fields
| Field | Required | Default | Description |
|-------|----------|---------|-------------|
| `moduleRoot` | Yes | -- | Relative path to the JS/TS module root (e.g. `.` or `src`) |
| `testsRoot` | Yes | -- | Relative path to the tests directory |
| `formatterCmds` | No | `["disabled"]` | Formatter commands. Use `npx` prefix for project-local tools, e.g. `"npx prettier --write {file}"` |
| `ignorePaths` | No | `[]` | Glob patterns to exclude from optimization |
### Auto-discovery
When configuration is missing, the optimizer agent:
- **moduleRoot**: Inspects the project structure; typically `.` or `src`
- **testsRoot**: Looks for `tests`, `test`, `__tests__`, or directories with `*.test.js`/`*.spec.ts` files. Falls back to `tests`
## Claude Code permissions
To allow codeflash to run without permission prompts, add the following to `.claude/settings.json` in your project root:
```json
{
"permissions": {
"allow": [
"Bash(*codeflash*)"
]
}
}
```
This can be set up automatically by running `/setup`.
### Scope options
The permission can be placed at different levels:
| File | Scope |
|------|-------|
| `.claude/settings.json` | Project-wide, shared with team (committed to git) |
| `.claude/settings.local.json` | Project-wide, personal (gitignored) |
| `~/.claude/settings.json` | User-wide, all projects |
The stop hook checks `.claude/settings.json` in the repo root to determine if auto-allow is already configured. If the `permissions.allow` array contains any entry matching `codeflash`, the hook skips adding auto-allow instructions to its suggestions.

79
docs/getting-started.md Normal file
View file

@ -0,0 +1,79 @@
# Getting Started
This guide walks you through installing the Codeflash Claude Code plugin and running your first optimization.
## Prerequisites
- **Claude Code** v2.1.38 or later
- **Python projects**: [codeflash](https://pypi.org/project/codeflash/) installed in a virtual environment
- **JS/TS projects**: [codeflash](https://www.npmjs.com/package/codeflash) installed as a dev dependency
## Installation
### 1. Add the marketplace and install
```bash
/plugin marketplace add codeflash-ai/codeflash-cc-plugin
/plugin install codeflash
```
Or from a local clone:
```bash
git clone https://github.com/codeflash-ai/codeflash-cc-plugin.git
/plugin marketplace add ./codeflash-cc-plugin
/plugin install codeflash
```
### 2. Choose installation scope
By default, plugins install at the **user** level (available across all projects). You can change this:
| Scope | Flag | Effect |
|-------|------|--------|
| User (default) | _(none)_ | Available in all your projects |
| Project | `--scope project` | Shared with team via `.claude/settings.json` |
| Local | `--scope local` | This project only, gitignored |
```bash
/plugin install codeflash --scope project
```
### 3. Verify installation
Run `/plugin` to open the plugin manager. Confirm **codeflash** appears under the **Installed** tab.
## First optimization
Run the `/optimize` skill with a target file:
```
/optimize src/utils.py
```
What happens behind the scenes:
1. The skill forks a background **optimizer agent**
2. The agent walks upward from CWD to the git root, looking for `pyproject.toml` (Python) or `package.json` (JS/TS)
3. It verifies codeflash is installed and configured
4. If configuration is missing, it auto-discovers your module root and tests directory and writes the config for you
5. It runs `codeflash --subagent` in the background with a 10-minute timeout
6. Results are reported when optimization completes
You can continue working while codeflash optimizes in the background.
## Set up auto-permissions
Run `/setup` to allow codeflash to execute automatically without permission prompts:
```
/setup
```
This adds `Bash(*codeflash*)` to the `permissions.allow` array in `.claude/settings.json`. After this, the post-commit hook can trigger optimizations without asking each time.
## Next steps
- [Usage Guide](usage-guide.md) -- all commands, flags, and workflows
- [Configuration](configuration.md) -- config reference for Python and JS/TS projects
- [Troubleshooting](troubleshooting.md) -- common problems and fixes

145
docs/hook-lifecycle.md Normal file
View file

@ -0,0 +1,145 @@
# Hook Lifecycle
Deep dive into `scripts/suggest-optimize.sh` -- the Stop hook that detects commits and suggests optimizations.
## When the hook fires
The hook fires on **every Claude stop** (every time Claude finishes a response). This is configured in `hooks/hooks.json` with a `*` matcher, meaning it runs unconditionally regardless of what Claude just did.
## Decision tree
The hook evaluates a series of conditions and takes the first matching exit path:
```
1. stop_hook_active == true?
YES -> exit 0 (allow stop, prevent infinite loop)
2. Not inside a git repo?
YES -> exit 0
3. No transcript_path or file doesn't exist?
YES -> exit 0
4. Session start time unavailable?
YES -> exit 0
5. No commits with .py/.js/.ts/.jsx/.tsx files since session start?
YES -> exit 0
6. Commit hash set already seen (in codeflash-seen)?
YES -> exit 0
7. JS/TS project with JS changes?
7a. Not configured -> block: set up config (+ install if needed)
7b. Configured, not installed -> block: install codeflash
7c. Configured + installed -> block: run codeflash
8. No Python changes?
YES -> exit 0
9. Python project, no venv found?
YES -> block: create venv, install, configure, run
10. Python project with venv:
10a. Not configured -> block: set up config (+ install if needed)
10b. Configured, not installed -> block: install codeflash
10c. Configured + installed -> block: run codeflash
```
Every `block` decision also appends auto-allow instructions if `Bash(*codeflash*)` is not yet in `.claude/settings.json`.
## Infinite loop prevention
When the hook blocks Claude's stop with a suggestion, Claude acts on it (e.g., runs codeflash). When Claude finishes that response, the hook fires again. To prevent an infinite loop:
1. Claude sets `stop_hook_active: true` in the hook input when it's responding to a previous hook block
2. The hook checks this flag first and immediately exits if true
This means the hook only triggers once per "natural" Claude stop, not on stops caused by responding to hook suggestions.
## Session boundary detection
The hook needs to know when the current session started to find only commits made during this session.
1. It reads `transcript_path` from the hook input JSON
2. It gets the transcript file's **birth time** (creation timestamp) using `stat`:
- **macOS**: `stat -f %B <file>` (birth time)
- **Linux**: `stat -c %W <file>` (birth time), falls back to `stat -c %Y` (modification time) if birth time is unavailable
3. This timestamp becomes `SESSION_START`, used in `git log --after=@$SESSION_START`
## Commit detection
```bash
git log --after="@$SESSION_START" --name-only --diff-filter=ACMR \
--pretty=format: -- '*.py' '*.js' '*.ts' '*.jsx' '*.tsx'
```
- `--after=@$SESSION_START` -- only commits after session start (Unix timestamp)
- `--diff-filter=ACMR` -- Added, Copied, Modified, Renamed files only
- `--pretty=format:` -- suppress commit metadata, show only file names
- File patterns filter to Python and JS/TS extensions
The results are sorted and deduplicated. The hook also determines which language families have changes (`HAS_PYTHON_CHANGES`, `HAS_JS_CHANGES`) by grepping the file list for extension patterns.
## Deduplication
The hook prevents suggesting optimization for the same set of commits twice:
1. It computes the commit hashes of all matching commits:
```bash
git log --after="@$SESSION_START" --pretty=format:%H -- '*.py' '*.js' '*.ts' '*.jsx' '*.tsx'
```
2. It hashes the full list with SHA-256:
```bash
... | shasum -a 256 | cut -d' ' -f1
```
3. It checks this hash against `$TRANSCRIPT_DIR/codeflash-seen`
4. If found, the hook exits (already processed)
5. If not found, appends the hash and continues
The seen-marker file lives in the transcript directory, so it's scoped to the current session/project.
## Project detection (`detect_project`)
The `detect_project` function walks from `$PWD` upward to `$REPO_ROOT`:
1. At each directory level, check for `pyproject.toml` first, then `package.json`
2. The **first** config file found determines the project type
3. It records:
- `PROJECT_TYPE`: `"python"` or `"js"`
- `PROJECT_DIR`: directory containing the config file
- `PROJECT_CONFIG_PATH`: full path to the config file
- `PROJECT_CONFIGURED`: `"true"` if codeflash config section exists
For Python, it checks for `[tool.codeflash]` in `pyproject.toml`. For JS/TS, it checks for a `"codeflash"` key in `package.json` using `jq`.
The walk stops at `$REPO_ROOT` -- it never searches above the git repository root.
## Auto-allow suggestion
Every `block` decision checks whether codeflash is already auto-allowed:
```bash
SETTINGS_JSON="$REPO_ROOT/.claude/settings.json"
jq -e '.permissions.allow // [] | any(test("codeflash"))' "$SETTINGS_JSON"
```
If no matching entry exists, the block message appends instructions to add `Bash(*codeflash*)` to `permissions.allow`. This means after the first optimization, future runs won't need permission prompts.
## Debug logging
The hook writes all debug output to `/tmp/codeflash-hook-debug.log`:
```bash
LOGFILE="/tmp/codeflash-hook-debug.log"
exec 2>>"$LOGFILE"
set -x
```
All stderr (including bash trace output from `set -x`) is appended to this file. To debug hook issues:
```bash
tail -f /tmp/codeflash-hook-debug.log
```
The log persists across sessions and is not automatically cleaned up.

123
docs/troubleshooting.md Normal file
View file

@ -0,0 +1,123 @@
# Troubleshooting
## Plugin not appearing after install
**Symptom**: `/plugin` doesn't show codeflash in the Installed tab.
**Fix**:
1. Verify the marketplace was added: `/plugin marketplace add codeflash-ai/codeflash-cc-plugin`
2. Install again: `/plugin install codeflash`
3. Check you're running Claude Code v2.1.38 or later
## `/optimize` does nothing
**Symptom**: Running `/optimize` produces no output or immediately returns.
**Possible causes**:
- No project config found. The agent walks from CWD to the git root looking for `pyproject.toml` or `package.json`. Make sure you're inside a git repository.
- Codeflash CLI not installed. For Python: `pip install codeflash` in your venv. For JS/TS: `npm install --save-dev codeflash`.
- The agent is running in the background. Check if you see "Codeflash is optimizing in the background" -- results appear when the background task completes.
## Permission prompts every time
**Symptom**: Claude asks for permission to run codeflash on every invocation.
**Fix**: Run `/setup` to add `Bash(*codeflash*)` to `.claude/settings.json`. Or add it manually:
```json
{
"permissions": {
"allow": [
"Bash(*codeflash*)"
]
}
}
```
## No venv found (Python)
**Symptom**: Hook or agent reports "No Python virtual environment was found."
**Fix**:
1. Create a venv: `python3 -m venv .venv`
2. Activate it: `source .venv/bin/activate`
3. Install codeflash: `pip install codeflash`
4. Restart Claude Code from within the activated venv
The plugin searches for venvs in this order:
1. `<project_dir>/.venv`
2. `<project_dir>/venv`
3. `<repo_root>/.venv`
4. `<repo_root>/venv`
## Codeflash not installed (JS/TS)
**Symptom**: `npx codeflash --version` fails or package not found.
**Fix**:
```bash
npm install --save-dev codeflash
```
Run this in the directory containing your `package.json`.
## Hook not triggering after commits
**Symptom**: You commit Python/JS/TS files but don't see optimization suggestions.
**Check**:
1. Verify the hook is registered: look for `hooks/hooks.json` in the plugin directory
2. Check the debug log: `cat /tmp/codeflash-hook-debug.log`
3. Ensure commits touch `.py`, `.js`, `.ts`, `.jsx`, or `.tsx` files
4. The hook only detects commits made **during the current session** (after the transcript file was created). Commits from before starting Claude Code won't trigger it.
## Hook triggering repeatedly
**Symptom**: The hook keeps suggesting optimization for the same commits.
This shouldn't happen due to deduplication (the hook tracks seen commit hashes in `$TRANSCRIPT_DIR/codeflash-seen`). If it does:
1. Check the seen-marker file exists in the transcript directory
2. Look at `/tmp/codeflash-hook-debug.log` for the dedup logic trace
3. If commit hashes changed (e.g., amended commits), the hook treats them as new
## "Attempting to repair broken tests..."
This is **normal codeflash behavior**, not an error. Codeflash generates tests and sometimes needs to fix them. Let it continue.
## 10-minute timeout exceeded
**Symptom**: Codeflash background task times out.
This can happen on large projects or with `--all`. Options:
- Optimize specific files instead of the entire project: `/optimize src/specific_file.py`
- Target individual functions: `/optimize src/utils.py my_function`
- The `--all` flag scans every function, which naturally takes longer
## Formatter errors
**Symptom**: Codeflash fails with formatter-related errors.
**Check**:
1. Read the `formatter-cmds` (Python) or `formatterCmds` (JS/TS) in your config
2. Verify each formatter is installed:
- Python: `which black` (or whichever formatter)
- JS/TS: `npx prettier --version` (or whichever formatter)
3. Set `formatter-cmds = ["disabled"]` or `"formatterCmds": ["disabled"]` to skip formatting entirely
## Debugging the hook script manually
Run the hook script directly to test it:
```bash
echo '{"stop_hook_active": false, "transcript_path": "/path/to/transcript.jsonl"}' | \
bash /path/to/codeflash-cc-plugin/scripts/suggest-optimize.sh
```
Check the debug log for detailed trace output:
```bash
tail -100 /tmp/codeflash-hook-debug.log
```
The log includes every variable and branch taken (via `set -x`), making it straightforward to trace why the hook did or didn't trigger.

92
docs/usage-guide.md Normal file
View file

@ -0,0 +1,92 @@
# Usage Guide
## The `/optimize` skill
`/optimize` is the primary command. It spawns a background optimizer agent that runs the codeflash CLI on your code.
### Syntax
```
/optimize [file] [function] [flags]
```
### Examples
| Command | Effect |
|---------|--------|
| `/optimize` | Let codeflash detect changed files automatically |
| `/optimize src/utils.py` | Optimize all functions in `src/utils.py` |
| `/optimize src/utils.py my_function` | Optimize only `my_function` in that file |
| `/optimize --all` | Optimize the entire project |
| `/optimize src/utils.py --no-pr` | Optimize without creating a PR |
| `/optimize src/utils.py --effort high` | Set optimization effort level to high |
Flags can be combined: `/optimize src/utils.py my_function --no-pr --effort high`
### What happens behind the scenes
1. The skill (defined in `skills/optimize/SKILL.md`) forks context and spawns the **optimizer agent**
2. The agent locates your project config (`pyproject.toml` or `package.json`)
3. It verifies the codeflash CLI is installed and the project is configured
4. It runs `codeflash --subagent` as a **background task** with a 10-minute timeout
5. You're notified when optimization completes with results
The agent has up to **15 turns** to complete its work (install codeflash, configure the project, run optimization).
## The `/setup` command
`/setup` configures auto-permissions so codeflash runs without prompting.
### What it does
1. Finds `.claude/settings.json` in your project root
2. Checks if `Bash(*codeflash*)` is already in `permissions.allow`
3. If not, adds it (creating the file and directory if needed)
4. Preserves any existing settings
Running `/setup` multiple times is safe -- it's idempotent. If permissions are already configured, it reports "No changes needed."
## Automatic post-commit suggestions
After every Claude response (the **Stop** hook), the plugin checks whether you committed Python, JS, or TS files during the current session. If so, it suggests running `/optimize`.
### How commit detection works
1. The hook determines the session start time from the transcript file's creation timestamp
2. It queries `git log --after=@<session_start>` for commits touching `*.py`, `*.js`, `*.ts`, `*.jsx`, `*.tsx` files
3. It deduplicates so the same commits don't trigger suggestions twice
4. If new commits are found, it blocks Claude's stop and injects a suggestion
The suggestion varies depending on the project state:
| State | Suggestion |
|-------|------------|
| Configured + installed | Run `codeflash --subagent` in the background |
| Configured, not installed | Install codeflash first, then run |
| Not configured | Auto-discover config, write it, then run |
| No venv (Python) | Create venv, install codeflash, configure, then run |
If `Bash(*codeflash*)` is not yet in `.claude/settings.json`, the suggestion also includes adding it for auto-permissions.
## Python-specific workflow
For Python projects, the optimizer agent:
1. Checks for an active virtual environment (`$VIRTUAL_ENV`)
2. If none, searches for `.venv` or `venv` directories in the project dir and repo root
3. Verifies `codeflash` is installed in the venv (`$VIRTUAL_ENV/bin/codeflash --version`)
4. Reads `[tool.codeflash]` from `pyproject.toml` for configuration
5. Runs: `source $VIRTUAL_ENV/bin/activate && codeflash --subagent [flags]`
The agent also checks `formatter-cmds` in the config and verifies formatters are installed.
## JS/TS-specific workflow
For JavaScript/TypeScript projects, the optimizer agent:
1. Checks codeflash is available via `npx codeflash --version`
2. Reads the `"codeflash"` key from `package.json` for configuration
3. Always runs from the project root (the directory containing `package.json`)
4. Runs: `npx codeflash --subagent [flags]`
No virtual environment is needed -- JS/TS projects use `npx`/`npm` directly.