Add codebase scanner, CLAUDE.md init, and usage-focused workflow
- ctc scan: runs linters/tests/type-checkers to find errors without burning AI tokens. Auto-detects Python, Node, Rust, Go projects. - ctc init: drops a CLAUDE.md into any repo teaching Claude Code to work efficiently (chunk work, commit often, read scan results first). - Updated README with the actual workflow: scan -> paste -> fix. https://claude.ai/code/session_01Fzv8baXnEVVhnrffAb3Ucc
This commit is contained in:
+134
-3
@@ -5,7 +5,8 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
from .analyzer import analyze_codebase
|
||||
from .decomposer import TaskChunk, decompose_by_phase, decompose_by_component, STRATEGIES
|
||||
from .decomposer import TaskChunk, decompose_by_phase, decompose_by_component
|
||||
from .scanner import scan_codebase
|
||||
from .tracker import save_plan, load_plan, mark_done, get_next_chunk, get_status_summary, TRACKER_DIR
|
||||
|
||||
|
||||
@@ -54,8 +55,8 @@ def cmd_plan(args: argparse.Namespace) -> None:
|
||||
|
||||
print(f"\nPlan saved to: {plan_file}")
|
||||
print(f"Prompts saved to: {root / TRACKER_DIR / 'prompts'}/")
|
||||
print(f"\nTo start, copy the prompt from chunk_01.md into Claude Code.")
|
||||
print(f"After each chunk, run: ctc done <chunk_id>")
|
||||
print("\nTo start, copy the prompt from chunk_01.md into Claude Code.")
|
||||
print("After each chunk, run: ctc done <chunk_id>")
|
||||
|
||||
|
||||
def cmd_status(args: argparse.Namespace) -> None:
|
||||
@@ -128,6 +129,74 @@ def cmd_show(args: argparse.Namespace) -> None:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def cmd_scan(args: argparse.Namespace) -> None:
|
||||
"""Scan the codebase for errors, then output a ready-to-paste prompt."""
|
||||
root = Path(args.project).resolve()
|
||||
if not root.is_dir():
|
||||
print(f"Error: {root} is not a directory.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Scanning {root}...", file=sys.stderr)
|
||||
result = scan_codebase(root)
|
||||
|
||||
task = args.task or ""
|
||||
prompt = result.to_prompt(task)
|
||||
|
||||
output_file = root / ".claude-chunks" / "scan_results.md"
|
||||
output_file.parent.mkdir(exist_ok=True)
|
||||
output_file.write_text(prompt)
|
||||
|
||||
if args.copy:
|
||||
try:
|
||||
import subprocess
|
||||
process = subprocess.Popen(
|
||||
_get_clipboard_cmd(),
|
||||
stdin=subprocess.PIPE,
|
||||
)
|
||||
process.communicate(prompt.encode())
|
||||
print("Scan complete! Prompt copied to clipboard.", file=sys.stderr)
|
||||
print(f"Also saved to: {output_file}", file=sys.stderr)
|
||||
except (FileNotFoundError, OSError):
|
||||
print(f"Scan complete! Saved to: {output_file}", file=sys.stderr)
|
||||
print(prompt)
|
||||
else:
|
||||
print(f"Scan complete! Saved to: {output_file}", file=sys.stderr)
|
||||
print(file=sys.stderr)
|
||||
print(prompt)
|
||||
|
||||
# Summary stats to stderr
|
||||
n_err = len(result.errors)
|
||||
n_warn = len(result.warnings)
|
||||
n_files = len(set(result.files_with_issues))
|
||||
print(f"\nFound: {n_err} errors, {n_warn} warnings across {n_files} files", file=sys.stderr)
|
||||
|
||||
|
||||
def cmd_init(args: argparse.Namespace) -> None:
|
||||
"""Initialize a CLAUDE.md in the target project to teach Claude Code chunking."""
|
||||
root = Path(args.project).resolve()
|
||||
claude_md = root / "CLAUDE.md"
|
||||
|
||||
if claude_md.exists() and not args.force:
|
||||
print(f"CLAUDE.md already exists at {claude_md}. Use --force to overwrite.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
template = _get_claude_md_template()
|
||||
|
||||
# If CLAUDE.md exists and --force, append rather than overwrite
|
||||
if claude_md.exists() and args.force:
|
||||
existing = claude_md.read_text()
|
||||
if "# Usage Efficiency" not in existing:
|
||||
claude_md.write_text(existing.rstrip() + "\n\n" + template)
|
||||
print(f"Appended chunking instructions to {claude_md}")
|
||||
else:
|
||||
print("CLAUDE.md already has chunking instructions.")
|
||||
else:
|
||||
claude_md.write_text(template)
|
||||
print(f"Created {claude_md}")
|
||||
|
||||
print("Claude Code will now follow usage-efficient chunking practices in this repo.")
|
||||
|
||||
|
||||
def _get_clipboard_cmd() -> list[str]:
|
||||
"""Get the platform-appropriate clipboard command."""
|
||||
import platform
|
||||
@@ -160,6 +229,57 @@ def _parse_manual_chunks(filepath: str, task: str) -> list[dict]:
|
||||
return chunks
|
||||
|
||||
|
||||
def _get_claude_md_template() -> str:
|
||||
return '''# Usage Efficiency
|
||||
|
||||
When working on large tasks in this codebase, follow these practices to minimize
|
||||
token usage and avoid running out of session capacity.
|
||||
|
||||
## Before Starting a Large Task
|
||||
|
||||
1. Check if a pre-scan exists: look for `.claude-chunks/scan_results.md`
|
||||
2. If it exists, read it FIRST — it contains linter/test/build errors already identified
|
||||
3. Use those results to target your work. Do NOT re-read the entire codebase.
|
||||
|
||||
## Chunking Strategy
|
||||
|
||||
For any task that will touch more than 5 files or require significant analysis:
|
||||
|
||||
1. **Plan first, implement second.** Before writing code, list:
|
||||
- Which files need to change
|
||||
- What order to change them in
|
||||
- How to verify each change works
|
||||
2. **Work in commits.** Make a commit after each logical unit of work. This creates
|
||||
save points if the session ends unexpectedly.
|
||||
3. **Stay focused.** Only read files directly relevant to the current task. Don't
|
||||
explore the codebase "to understand it better" — that burns tokens fast.
|
||||
4. **Use tools efficiently:**
|
||||
- Use `grep`/`rg` to find what you need instead of reading whole files
|
||||
- Read specific line ranges instead of entire files when possible
|
||||
- Don't re-read files you've already read in this session
|
||||
|
||||
## If the Task Is Too Large
|
||||
|
||||
If you realize mid-task that you won't finish in this session:
|
||||
|
||||
1. Commit everything you've done so far
|
||||
2. Create a file called `.claude-chunks/handoff.md` with:
|
||||
- What was completed
|
||||
- What remains to be done
|
||||
- Any gotchas or context the next session needs
|
||||
3. Tell the user to run `ctc status` to see progress
|
||||
|
||||
## Error Fixing Workflow
|
||||
|
||||
When asked to find and fix errors in the codebase:
|
||||
|
||||
1. First check for `.claude-chunks/scan_results.md` — it may already have the errors
|
||||
2. If not, ask the user to run `ctc scan` first (it's faster than AI-powered search)
|
||||
3. Focus on the specific files and line numbers from the scan results
|
||||
4. Don't read unrelated files
|
||||
'''
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="ctc",
|
||||
@@ -184,6 +304,15 @@ def main() -> None:
|
||||
help="File with one chunk description per line (for manual strategy)",
|
||||
)
|
||||
|
||||
# scan
|
||||
p_scan = sub.add_parser("scan", help="Scan codebase for errors and generate a prompt")
|
||||
p_scan.add_argument("task", nargs="?", default="", help="Optional: what you're trying to fix")
|
||||
p_scan.add_argument("-c", "--copy", action="store_true", help="Copy prompt to clipboard")
|
||||
|
||||
# init
|
||||
p_init = sub.add_parser("init", help="Add CLAUDE.md with chunking instructions to a project")
|
||||
p_init.add_argument("--force", action="store_true", help="Overwrite/append to existing CLAUDE.md")
|
||||
|
||||
# status
|
||||
sub.add_parser("status", help="Show progress on the current plan")
|
||||
|
||||
@@ -203,6 +332,8 @@ def main() -> None:
|
||||
|
||||
commands = {
|
||||
"plan": cmd_plan,
|
||||
"scan": cmd_scan,
|
||||
"init": cmd_init,
|
||||
"status": cmd_status,
|
||||
"done": cmd_done,
|
||||
"next": cmd_next,
|
||||
|
||||
Reference in New Issue
Block a user