diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..247facf --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,62 @@ +# Claude Code Efficiency Guide + +This file is read automatically by Claude Code at the start of every session. +It teaches Claude to work efficiently on large codebases and avoid running out +of usage mid-task. + +## Finding Errors in Large Codebases + +When asked to find or fix errors, bugs, or issues across the codebase: + +1. **Run the scanner first.** Execute: `python scan.py` + This runs the project's linters, tests, and type checkers in seconds. + It uses zero AI tokens — it's just running tools and collecting output. +2. **Read the scanner output.** It tells you exactly which files have errors + and what the errors are, with line numbers. +3. **Go straight to fixing.** Only read the files the scanner identified. + Do NOT read the entire codebase to "understand it" — that wastes tokens. + +## Working on Large Tasks + +When a task will touch more than 5 files or require significant changes: + +### Plan before you code +Before writing any code, briefly list: +- Which files need to change +- What order to change them in +- How to verify each change + +### Commit after each logical unit +Make a git commit after each meaningful piece of work. This creates save points. +If the session ends unexpectedly, the work so far is preserved. + +### Stay focused +- Use grep/search to find what you need — don't read whole files for one function +- Read specific line ranges, not entire large files +- Don't re-read files you already read in this session +- Don't explore "for context" — go straight to the files that need changing + +### If you're running low on usage +If you sense the task is too large to finish in this session: + +1. **Commit everything done so far** +2. **Create `.claude-handoff.md`** in the repo root containing: + - What was completed (with commit hashes if possible) + - What still needs to be done, in order + - Any gotchas, edge cases, or context the next session needs + - Which files still need changes +3. **Tell the user** what was completed and that the handoff file is ready + +The user can then start a new session and say: +"Continue the work described in .claude-handoff.md" + +## Project-Specific Commands + +Customize these for your project. Uncomment and edit as needed: + +``` +# Run tests: npm test / pytest / cargo test / go test ./... +# Lint: npm run lint / ruff check . / cargo clippy +# Build: npm run build / cargo build / go build ./... +# Type check: npx tsc --noEmit / mypy . +``` diff --git a/README.md b/README.md index 6aca23a..4604d53 100644 --- a/README.md +++ b/README.md @@ -1,159 +1,113 @@ -# Claude Task Chunker (`ctc`) +# Claude Task Chunker -Break large coding tasks into self-contained chunks for efficient Claude Code sessions. Stop running out of usage mid-task. +Save Claude Code usage on large codebases. Two files you drop into any repo. ## The Problem -You ask Claude Code to make a big change — add auth, refactor a module, fix errors across a 10,000-line codebase — and it runs out of usage halfway through. You're left with half-finished work and no easy way to pick up where you left off. +You ask Claude Code to fix errors or add a feature to a big codebase. It spends +half your usage just *reading files to find the problem*, then runs out before +it finishes the fix. ## The Solution -Two things working together: +Two files you copy into your project repo: -1. **`ctc scan`** — Runs linters, tests, and pattern matching on your codebase *without using any AI tokens*. Finds the errors so Claude doesn't have to read 10,000 lines to find them itself. -2. **`ctc plan`** — Breaks big tasks into self-contained chunks with ready-to-paste prompts for Claude Code. -3. **`ctc init`** — Drops a `CLAUDE.md` into your repo that teaches Claude Code to be usage-efficient automatically. +| File | What it does | +|------|-------------| +| `CLAUDE.md` | Claude reads this automatically every session. It teaches Claude to scan first, stay focused, commit often, and leave a handoff note if usage runs low. | +| `scan.py` | A script Claude runs itself to find errors. It calls your project's linters, tests, and type checkers — zero AI tokens used. | -## Install +## Setup (one time per project) + +1. Copy `CLAUDE.md` and `scan.py` into the root of your project repo +2. Edit the "Project-Specific Commands" section in `CLAUDE.md` if needed +3. Commit them ```bash -# Clone this repo, then: -pip install -e . +cp /path/to/claude-task-chunker/CLAUDE.md /path/to/your-project/ +cp /path/to/claude-task-chunker/scan.py /path/to/your-project/ +cd /path/to/your-project +git add CLAUDE.md scan.py +git commit -m "Add Claude Code efficiency tools" ``` -## Quick Start: The Workflow +That's it. You never touch these files again. -### Step 1: Set up your project (one time) +## How You Use It -```bash -cd /path/to/your/big-project -ctc init -``` +Your workflow doesn't change. You type in Claude Code exactly like before. -This creates a `CLAUDE.md` in your project that teaches Claude Code to work efficiently — chunk its own work, commit often, and not waste tokens exploring. - -### Step 2: Find errors fast (no AI needed) - -```bash -ctc scan -# or with a specific focus: -ctc scan "authentication is broken" -``` - -This runs your project's linters, tests, and type checkers automatically. It detects what kind of project you have (Python, Node, Rust, Go) and runs the right tools. The output goes to `.claude-chunks/scan_results.md`. - -### Step 3: Open Claude Code and paste the results - -Open Claude Code connected to your repo as normal. Then: +### Finding and fixing errors ``` -Fix the errors in .claude-chunks/scan_results.md +You: "Find and fix the errors in this codebase" ``` -Claude reads the pre-scanned results and goes straight to fixing — no wasted tokens reading 10,000 lines to find the bugs. +Claude (because it read CLAUDE.md) will automatically: +1. Run `python scan.py` — gets linter/test/type errors in seconds +2. Read only the files with errors +3. Fix them +4. Commit -### Step 4: For big tasks, plan chunks first +Without CLAUDE.md, Claude would read file after file trying to find the problems, +burning through your usage. -```bash -ctc plan "Add OAuth2 authentication with login, logout, and RBAC" -``` - -This creates numbered prompt files in `.claude-chunks/prompts/`. Each one is a self-contained session: - -```bash -ctc next # see the next chunk's prompt -ctc next -c # copy it to clipboard -``` - -Open Claude Code, paste the prompt, let it work, then: - -```bash -ctc done 1 # mark chunk 1 complete -ctc next # get chunk 2 -``` - -## Your Actual Workflow - -Here's what this looks like day-to-day: +### Big features ``` -You: cd my-big-project -You: ctc scan # 10 seconds, finds 3 errors -You: [open Claude Code] -You: "Fix the errors in .claude-chunks/scan_results.md" -Claude: [reads the file, fixes the 3 errors, done in one session] +You: "Add OAuth2 authentication with login, logout, and role-based access" ``` -For big features: +Claude (because it read CLAUDE.md) will automatically: +1. Plan which files to change before coding +2. Work through the plan, committing after each piece +3. If usage runs low, commit what's done and create `.claude-handoff.md` + +If it does run out, your next session you just say: ``` -You: ctc plan "migrate database from MySQL to Postgres" -s phase -You: ctc next -c # copies chunk 1 prompt -You: [open Claude Code, paste] -Claude: [does chunk 1: research & plan, commits] -You: ctc done 1 -You: ctc next -c # copies chunk 2 prompt -You: [paste into Claude Code — same session or new one] -Claude: [does chunk 2: core implementation, commits] -You: ctc done 2 - ... and so on +You: "Continue the work described in .claude-handoff.md" ``` -## Commands +And Claude picks up right where it left off — no wasted tokens re-discovering +what was already done. -| Command | What it does | -|---------|-------------| -| `ctc init` | Add CLAUDE.md to your project (teaches Claude to be efficient) | -| `ctc scan` | Find errors with linters/tests (no AI tokens used) | -| `ctc scan "description"` | Same, but adds your description to the output prompt | -| `ctc plan ` | Break a big task into chunks | -| `ctc plan -s component` | Chunk by directory/module instead of phase | -| `ctc plan -s manual --chunks-file chunks.txt` | Use your own chunk list | -| `ctc status` | See progress on current plan | -| `ctc next` | Print the next chunk's prompt | -| `ctc next -c` | Copy it to clipboard | -| `ctc done ` | Mark a chunk as complete | -| `ctc show ` | View a specific chunk's prompt | - -## What `ctc scan` Detects +## What `scan.py` Detects It auto-detects your project type and runs the right tools: | Project | What it runs | |---------|-------------| -| **Python** | pytest, ruff, mypy, syntax check | -| **Node/TypeScript** | npm/yarn test, eslint, tsc --noEmit | -| **Rust** | cargo check, cargo test --no-run | -| **Go** | go build, go vet, go test | -| **Any** | grep for TODO/FIXME/HACK/BUG markers | +| Python | pytest, ruff, mypy, syntax check | +| Node/TypeScript | npm/yarn test, eslint, tsc --noEmit | +| Rust | cargo check, cargo test --no-run | +| Go | go build, go vet, go test | -## Decomposition Strategies +## Customizing CLAUDE.md -### `phase` (default) -Breaks work into: Research & Plan -> Core Implementation -> Tests -> Integration & Cleanup. +The `CLAUDE.md` file has a section at the bottom for project-specific commands. +Edit it to match your project: -### `component` -One chunk per top-level directory. Good when changes are spread across independent modules. +```markdown +## Project-Specific Commands -### `manual` -You write a text file with one chunk per line: - -``` -# chunks.txt -Set up database models for users and roles -Build registration and login API endpoints -Add JWT middleware and session management -Build role-based access control decorators +# Run tests: pytest -x +# Lint: ruff check . --fix +# Build: docker compose build +# Type check: mypy src/ ``` -```bash -ctc plan "Add user auth" -s manual --chunks-file chunks.txt +You can also add any other instructions you want Claude to follow in every +session — coding style, architecture decisions, files to avoid, etc. + +## What's in this repo + +``` +CLAUDE.md — Template to copy into your projects +scan.py — Scanner script to copy into your projects +README.md — You're reading it ``` -## Tips - -- **Always `ctc scan` before asking Claude to fix errors** — saves huge amounts of usage -- **`ctc init` once per project** — the CLAUDE.md stays in the repo and works every session -- **Keep chunks independent** — the less each chunk depends on others, the cleaner the handoff -- **Add `.claude-chunks/` to `.gitignore`** — it's working state, not source code -- **Commit CLAUDE.md to your repo** — it benefits every Claude Code session in that project +The `claude_task_chunker/` directory contains an optional CLI tool (`ctc`) for +manually planning and tracking chunks from the terminal. Most users won't need +it — the CLAUDE.md + scan.py approach handles everything automatically. diff --git a/scan.py b/scan.py new file mode 100644 index 0000000..ca7473b --- /dev/null +++ b/scan.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +"""Fast codebase scanner — finds errors using linters, tests, and type checkers. + +Claude Code runs this script itself via bash. It uses zero AI tokens because +it's just running your project's existing tools and collecting the output. + +Usage (run by Claude Code, not by you): + python scan.py [project_root] + +Outputs a structured report to stdout that Claude can read and act on. +Auto-detects project type: Python, Node/TypeScript, Rust, Go. +""" + +import subprocess +import shutil +import sys +from pathlib import Path + + +def run(cmd, cwd, timeout=120): + """Run a command, return (exit_code, output).""" + try: + r = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, timeout=timeout) + out = r.stdout + if r.stderr: + out += "\n" + r.stderr + return r.returncode, out.strip() + except subprocess.TimeoutExpired: + return -1, f"TIMEOUT after {timeout}s: {' '.join(cmd)}" + except FileNotFoundError: + return -1, "" + + +def extract_files(output, root): + """Pull file paths from linter output like 'path/file.py:10: error'.""" + files = set() + for line in output.splitlines(): + if ":" in line: + candidate = line.split(":")[0].strip().lstrip("./") + if candidate and (root / candidate).exists(): + files.add(candidate) + return sorted(files) + + +def scan_python(root): + """Run Python checks: pytest, ruff, mypy, syntax.""" + sections = [] + + if not any((root / f).exists() for f in ["pyproject.toml", "setup.py", "requirements.txt"]): + return sections + + # pytest + for cmd in [["python", "-m", "pytest", "--tb=short", "-q"], ["pytest", "--tb=short", "-q"]]: + code, out = run(cmd, root) + if code == -1 and not out: + continue + if code != 0 and out: + sections.append(("Test Failures", out)) + break + + # ruff + if shutil.which("ruff"): + code, out = run(["ruff", "check", "."], root) + if code != 0 and out: + sections.append(("Lint Errors (ruff)", out)) + + # mypy + if shutil.which("mypy"): + code, out = run(["mypy", ".", "--no-error-summary"], root, timeout=180) + if code != 0 and out: + sections.append(("Type Errors (mypy)", out)) + + # Syntax errors + syntax_errors = [] + for py_file in list(root.rglob("*.py"))[:500]: + rel = py_file.relative_to(root) + if any(p in {".venv", "venv", "node_modules", "__pycache__", ".git", ".tox"} for p in rel.parts): + continue + code, out = run(["python", "-c", f"import ast; ast.parse(open('{py_file}').read())"], root) + if code != 0: + syntax_errors.append(f"{rel}: SyntaxError - {out.splitlines()[-1] if out else 'unknown'}") + if syntax_errors: + sections.append(("Syntax Errors", "\n".join(syntax_errors))) + + return sections + + +def scan_node(root): + """Run Node/TypeScript checks: test, eslint, tsc.""" + sections = [] + + if not (root / "package.json").exists(): + return sections + + pkg = "yarn" if (root / "yarn.lock").exists() else "npm" + + code, out = run([pkg, "test", "--", "--reporter=min"], root, timeout=180) + if code != 0 and out: + sections.append(("Test Failures", out)) + + if shutil.which("npx"): + code, out = run(["npx", "eslint", ".", "--format=compact", "--no-warn-ignored"], root, timeout=120) + if code != 0 and out: + sections.append(("Lint Errors (eslint)", out)) + + if (root / "tsconfig.json").exists(): + code, out = run(["npx", "tsc", "--noEmit"], root, timeout=180) + if code != 0 and out: + sections.append(("TypeScript Errors", out)) + + return sections + + +def scan_rust(root): + """Run Rust checks: cargo check, cargo test --no-run.""" + sections = [] + + if not (root / "Cargo.toml").exists(): + return sections + + code, out = run(["cargo", "check", "--message-format=short"], root, timeout=300) + if code != 0 and out: + sections.append(("Build Errors (cargo)", out)) + + code, out = run(["cargo", "test", "--no-run"], root, timeout=300) + if code != 0 and out: + sections.append(("Test Compilation Errors", out)) + + return sections + + +def scan_go(root): + """Run Go checks: build, vet, test.""" + sections = [] + + if not (root / "go.mod").exists(): + return sections + + code, out = run(["go", "build", "./..."], root, timeout=180) + if code != 0 and out: + sections.append(("Build Errors (go)", out)) + + code, out = run(["go", "vet", "./..."], root, timeout=120) + if code != 0 and out: + sections.append(("Vet Warnings (go)", out)) + + code, out = run(["go", "test", "-short", "-count=1", "./..."], root, timeout=180) + if code != 0 and out: + sections.append(("Test Failures (go)", out)) + + return sections + + +def main(): + root = Path(sys.argv[1] if len(sys.argv) > 1 else ".").resolve() + + if not root.is_dir(): + print(f"Error: {root} is not a directory", file=sys.stderr) + sys.exit(1) + + all_sections = [] + all_sections.extend(scan_python(root)) + all_sections.extend(scan_node(root)) + all_sections.extend(scan_rust(root)) + all_sections.extend(scan_go(root)) + + if not all_sections: + print("SCAN COMPLETE: No errors found.") + sys.exit(0) + + # Collect all files with issues + all_files = set() + for title, output in all_sections: + all_files.update(extract_files(output, root)) + + # Print structured report + print(f"SCAN COMPLETE: Found issues in {len(all_sections)} categories across {len(all_files)} files.\n") + + for title, output in all_sections: + print(f"=== {title} ===") + lines = output.splitlines() + if len(lines) > 80: + print("(truncated to last 80 lines)") + print("\n".join(lines[-80:])) + else: + print(output) + print() + + if all_files: + print("=== Files with Issues ===") + for f in sorted(all_files): + print(f" {f}") + + +if __name__ == "__main__": + main()