From 27232eebfb87ac495cf899b5798706f29dd4ba1d Mon Sep 17 00:00:00 2001 From: ismeth Date: Thu, 16 Apr 2026 15:39:08 +0300 Subject: [PATCH 1/6] release: v0.13.0 --- crates/aft/Cargo.toml | 2 +- packages/npm/darwin-arm64/package.json | 2 +- packages/npm/darwin-x64/package.json | 2 +- packages/npm/linux-arm64/package.json | 2 +- packages/npm/linux-x64/package.json | 2 +- packages/npm/win32-x64/package.json | 2 +- packages/opencode-plugin/package.json | 12 ++++++------ 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/aft/Cargo.toml b/crates/aft/Cargo.toml index d8f76e2..b7587bc 100644 --- a/crates/aft/Cargo.toml +++ b/crates/aft/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agent-file-tools" -version = "0.12.2" +version = "0.13.0" edition = "2021" description = "Agent File Tools — tree-sitter powered code analysis for AI agents" license = "MIT" diff --git a/packages/npm/darwin-arm64/package.json b/packages/npm/darwin-arm64/package.json index ba995e3..2b310de 100644 --- a/packages/npm/darwin-arm64/package.json +++ b/packages/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@cortexkit/aft-darwin-arm64", - "version": "0.12.2", + "version": "0.13.0", "description": "AFT binary for macOS ARM64 (Apple Silicon)", "os": [ "darwin" diff --git a/packages/npm/darwin-x64/package.json b/packages/npm/darwin-x64/package.json index 3ad9069..ce04754 100644 --- a/packages/npm/darwin-x64/package.json +++ b/packages/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@cortexkit/aft-darwin-x64", - "version": "0.12.2", + "version": "0.13.0", "description": "AFT binary for macOS x64 (Intel)", "os": [ "darwin" diff --git a/packages/npm/linux-arm64/package.json b/packages/npm/linux-arm64/package.json index 9d690f7..0cfe026 100644 --- a/packages/npm/linux-arm64/package.json +++ b/packages/npm/linux-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@cortexkit/aft-linux-arm64", - "version": "0.12.2", + "version": "0.13.0", "description": "AFT binary for Linux ARM64", "os": [ "linux" diff --git a/packages/npm/linux-x64/package.json b/packages/npm/linux-x64/package.json index 71e1138..cd080d9 100644 --- a/packages/npm/linux-x64/package.json +++ b/packages/npm/linux-x64/package.json @@ -1,6 +1,6 @@ { "name": "@cortexkit/aft-linux-x64", - "version": "0.12.2", + "version": "0.13.0", "description": "AFT binary for Linux x64", "os": [ "linux" diff --git a/packages/npm/win32-x64/package.json b/packages/npm/win32-x64/package.json index 1029e24..beb67be 100644 --- a/packages/npm/win32-x64/package.json +++ b/packages/npm/win32-x64/package.json @@ -1,6 +1,6 @@ { "name": "@cortexkit/aft-win32-x64", - "version": "0.12.2", + "version": "0.13.0", "description": "AFT binary for Windows x64", "os": [ "win32" diff --git a/packages/opencode-plugin/package.json b/packages/opencode-plugin/package.json index 1f7ee73..28f86a1 100644 --- a/packages/opencode-plugin/package.json +++ b/packages/opencode-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@cortexkit/aft-opencode", - "version": "0.12.2", + "version": "0.13.0", "type": "module", "description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis", "main": "dist/index.js", @@ -35,11 +35,11 @@ "zod": "^4.1.8" }, "optionalDependencies": { - "@cortexkit/aft-darwin-arm64": "0.12.2", - "@cortexkit/aft-darwin-x64": "0.12.2", - "@cortexkit/aft-linux-arm64": "0.12.2", - "@cortexkit/aft-linux-x64": "0.12.2", - "@cortexkit/aft-win32-x64": "0.12.2" + "@cortexkit/aft-darwin-arm64": "0.13.0", + "@cortexkit/aft-darwin-x64": "0.13.0", + "@cortexkit/aft-linux-arm64": "0.13.0", + "@cortexkit/aft-linux-x64": "0.13.0", + "@cortexkit/aft-win32-x64": "0.13.0" }, "devDependencies": { "@types/node": "^22.0.0", From c036282923a9069629b9c4daee08d24677cb7d9c Mon Sep 17 00:00:00 2001 From: nandanugg Date: Thu, 16 Apr 2026 20:49:01 +0700 Subject: [PATCH 2/6] feat: add Claude Code hooks integration Add install/uninstall scripts that set up AFT hooks for Claude Code: - Tool interception for Read, Grep, Glob via PreToolUse hooks - CLI wrapper for semantic commands (outline, zoom, call_tree, callers, etc.) - Global AFT.md instructions so Claude learns to use AFT for context savings Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 63 ++++ README.md | 35 +- scripts/install-claude-hooks.sh | 512 ++++++++++++++++++++++++++++++ scripts/uninstall-claude-hooks.sh | 50 +++ 4 files changed, 659 insertions(+), 1 deletion(-) create mode 100644 CLAUDE.md create mode 100755 scripts/install-claude-hooks.sh create mode 100755 scripts/uninstall-claude-hooks.sh diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5a97cc3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,63 @@ +# AFT - Agent File Tools + +Tree-sitter powered code analysis for massive context savings (60-90% token reduction). + +## AFT CLI Commands + +Use `aft` commands via Bash for code navigation. These provide structured output optimized for LLM consumption. + +### Semantic Commands (prefer these over raw file reads) + +```bash +# Get structure without content (~10% of full read tokens) +aft outline + +# Inspect symbol with call-graph annotations +aft zoom + +# Forward call graph - what does this function call? +aft call_tree + +# Reverse call graph - who calls this function? +aft callers + +# Impact analysis - what breaks if this changes? +aft impact + +# Trace analysis - how does execution reach this? +aft trace_to +``` + +### Basic Commands + +```bash +aft read [start_line] [limit] # Read with line numbers +aft grep [path] # Trigram-indexed search +aft glob [path] # File pattern matching +``` + +## When to Use What + +| Task | Command | Token Savings | +|------|---------|---------------| +| Understanding file structure | `aft outline` | ~90% vs full read | +| Finding function definition | `aft zoom file symbol` | Exact code only | +| Understanding dependencies | `aft call_tree` | Structured graph | +| Finding usage sites | `aft callers` | All call sites | +| Planning refactors | `aft impact` | Change propagation | +| Debugging call paths | `aft trace_to` | Execution paths | + +## Best Practices + +1. **Start with outline** - Before reading a file, use `aft outline` to understand structure +2. **Zoom to symbols** - Instead of reading full files, use `aft zoom` for specific functions +3. **Use call graphs** - For understanding code flow, `call_tree` and `callers` are more efficient than grep +4. **Impact before refactor** - Run `aft impact` before making changes to understand blast radius + +## Supported Languages + +TypeScript, JavaScript, Python, Rust, Go, C/C++, Java, Ruby, Markdown + +## Hook Integration + +Read, Grep, and Glob tools are automatically routed through AFT via hooks for indexed performance. diff --git a/README.md b/README.md index 6b09800..8905f23 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,39 @@ ## Get Started +### Claude Code + +Run the install script to set up AFT hooks for Claude Code: + +```bash +./scripts/install-claude-hooks.sh +``` + +This installs: +- **Tool interception** — Read, Grep, Glob tools route through AFT for indexed performance +- **CLI wrapper** — `aft` command for semantic commands (outline, zoom, call_tree, callers, etc.) +- **Instructions** — Claude learns when to use AFT commands for context savings + +After installation, restart Claude Code. Then use semantic commands via Bash: + +```bash +aft outline src/ # File structure (~10% of full read tokens) +aft zoom main.go main # Inspect function with call graph +aft callers api.ts handler # Find all callers +aft call_tree service.ts run # What does run() call? +``` + +
+Uninstall + +```bash +./scripts/uninstall-claude-hooks.sh +``` +
+ +--- + + ### OpenCode Run the setup wizard — it registers AFT in your OpenCode and TUI config and asks which experimental features to enable: @@ -1013,7 +1046,7 @@ opencode-aft/ ## Roadmap -- MCP server for Claude Code, Cursor, and other MCP-compatible hosts +- Cursor support via hooks - LSP integration for type-aware symbol resolution (partially implemented) - Streaming responses for large call trees - Watch mode for live outline updates diff --git a/scripts/install-claude-hooks.sh b/scripts/install-claude-hooks.sh new file mode 100755 index 0000000..056c6c9 --- /dev/null +++ b/scripts/install-claude-hooks.sh @@ -0,0 +1,512 @@ +#!/usr/bin/env bash +# AFT Claude Code Hooks Installer +# Installs AFT hooks for Claude Code integration + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AFT_ROOT="$(dirname "$SCRIPT_DIR")" +CLAUDE_DIR="$HOME/.claude" +HOOKS_DIR="$CLAUDE_DIR/hooks" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +info() { echo -e "${GREEN}[INFO]${NC} $1"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } + +# Check for required tools +command -v jq &>/dev/null || error "jq is required but not installed. Install with: brew install jq" + +# Build AFT binary if needed +AFT_BINARY="$AFT_ROOT/target/release/aft" +if [ ! -x "$AFT_BINARY" ]; then + info "Building AFT binary..." + cd "$AFT_ROOT" + cargo build --release || error "Failed to build AFT binary" +fi + +info "AFT binary: $AFT_BINARY" + +# Create directories +mkdir -p "$HOOKS_DIR" +info "Created hooks directory: $HOOKS_DIR" + +# Write aft CLI wrapper +cat > "$HOOKS_DIR/aft" << 'WRAPPER_EOF' +#!/usr/bin/env bash +# AFT CLI wrapper for Claude Code +# Usage: aft [args...] +# +# Commands: +# outline - Get file/directory structure (symbols, functions, classes) +# zoom [symbol] - Inspect symbol with call-graph annotations +# call_tree - What does this function call? (forward graph) +# callers - Who calls this function? (reverse graph) +# impact - What breaks if this changes? +# trace_to - How does execution reach this function? +# read [start] [limit] - Read file with line numbers +# grep [path] - Search with trigram index +# glob [path] - Find files by pattern + +set -euo pipefail + +AFT_BINARY="__AFT_BINARY_PATH__" +WORK_DIR="${PWD}" + +# Check binary exists +if [ ! -x "$AFT_BINARY" ]; then + echo "Error: AFT binary not found at $AFT_BINARY" >&2 + exit 1 +fi + +# Send command to AFT binary +call_aft() { + local cmd="$1" + shift + local params="$1" + + local config_req=$(jq -cn --arg root "$WORK_DIR" '{id:"cfg",command:"configure",project_root:$root}') + local cmd_req=$(echo "$params" | jq -c --arg cmd "$cmd" '{id:"cmd",command:$cmd} + .') + + local result=$( (echo "$config_req"; echo "$cmd_req") | "$AFT_BINARY" 2>/dev/null | grep '"id":"cmd"' | head -1) + + # Check success + local success=$(echo "$result" | jq -r '.success // false') + if [ "$success" != "true" ]; then + local msg=$(echo "$result" | jq -r '.message // "Command failed"') + echo "Error: $msg" >&2 + exit 1 + fi + + # Output text or content + local text=$(echo "$result" | jq -r '.text // .content // empty') + if [ -n "$text" ]; then + echo "$text" + else + echo "$result" | jq . + fi +} + +CMD="${1:-help}" +shift || true + +case "$CMD" in + outline) + FILE="${1:-}" + [ -z "$FILE" ] && { echo "Usage: aft outline "; exit 1; } + + # Check if directory - discover source files + if [ -d "$FILE" ]; then + FILES=$(find "$FILE" -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \ + -o -name "*.py" -o -name "*.rs" -o -name "*.go" -o -name "*.c" -o -name "*.cpp" -o -name "*.h" \ + -o -name "*.java" -o -name "*.rb" -o -name "*.md" \) \ + ! -path "*/node_modules/*" ! -path "*/.git/*" ! -path "*/target/*" ! -path "*/dist/*" \ + 2>/dev/null | head -100 | jq -R . | jq -s .) + PARAMS=$(jq -cn --argjson files "$FILES" '{files:$files}') + else + PARAMS=$(jq -cn --arg f "$FILE" '{file:$f}') + fi + call_aft "outline" "$PARAMS" + ;; + + zoom) + FILE="${1:-}" + SYMBOL="${2:-}" + [ -z "$FILE" ] && { echo "Usage: aft zoom [symbol]"; exit 1; } + + if [ -n "$SYMBOL" ]; then + PARAMS=$(jq -cn --arg f "$FILE" --arg s "$SYMBOL" '{file:$f,symbol:$s}') + else + PARAMS=$(jq -cn --arg f "$FILE" '{file:$f}') + fi + call_aft "zoom" "$PARAMS" + ;; + + call_tree) + FILE="${1:-}" + SYMBOL="${2:-}" + [ -z "$FILE" ] || [ -z "$SYMBOL" ] && { echo "Usage: aft call_tree "; exit 1; } + + PARAMS=$(jq -cn --arg f "$FILE" --arg s "$SYMBOL" '{file:$f,symbol:$s}') + call_aft "call_tree" "$PARAMS" + ;; + + callers) + FILE="${1:-}" + SYMBOL="${2:-}" + [ -z "$FILE" ] || [ -z "$SYMBOL" ] && { echo "Usage: aft callers "; exit 1; } + + PARAMS=$(jq -cn --arg f "$FILE" --arg s "$SYMBOL" '{file:$f,symbol:$s}') + call_aft "callers" "$PARAMS" + ;; + + impact) + FILE="${1:-}" + SYMBOL="${2:-}" + [ -z "$FILE" ] || [ -z "$SYMBOL" ] && { echo "Usage: aft impact "; exit 1; } + + PARAMS=$(jq -cn --arg f "$FILE" --arg s "$SYMBOL" '{file:$f,symbol:$s}') + call_aft "impact" "$PARAMS" + ;; + + trace_to) + FILE="${1:-}" + SYMBOL="${2:-}" + [ -z "$FILE" ] || [ -z "$SYMBOL" ] && { echo "Usage: aft trace_to "; exit 1; } + + PARAMS=$(jq -cn --arg f "$FILE" --arg s "$SYMBOL" '{file:$f,symbol:$s}') + call_aft "trace_to" "$PARAMS" + ;; + + read) + FILE="${1:-}" + START="${2:-1}" + LIMIT="${3:-2000}" + [ -z "$FILE" ] && { echo "Usage: aft read [start_line] [limit]"; exit 1; } + + PARAMS=$(jq -cn --arg f "$FILE" --argjson s "$START" --argjson l "$LIMIT" \ + '{file:$f,start_line:$s,limit:$l}') + call_aft "read" "$PARAMS" + ;; + + grep) + PATTERN="${1:-}" + PATH_ARG="${2:-.}" + [ -z "$PATTERN" ] && { echo "Usage: aft grep [path]"; exit 1; } + + PARAMS=$(jq -cn --arg p "$PATTERN" --arg d "$PATH_ARG" '{pattern:$p,path:$d}') + call_aft "grep" "$PARAMS" + ;; + + glob) + PATTERN="${1:-}" + PATH_ARG="${2:-.}" + [ -z "$PATTERN" ] && { echo "Usage: aft glob [path]"; exit 1; } + + PARAMS=$(jq -cn --arg p "$PATTERN" --arg d "$PATH_ARG" '{pattern:$p,path:$d}') + call_aft "glob" "$PARAMS" + ;; + + help|--help|-h) + cat << 'EOF' +AFT - Agent File Tools (Tree-sitter powered code analysis) + +SEMANTIC COMMANDS (massive context savings): + aft outline Structure without content (~10% tokens) + aft zoom Symbol + call graph annotations + aft call_tree Forward call graph (what does it call?) + aft callers Reverse call graph (who calls it?) + aft impact What breaks if this changes? + aft trace_to How does execution reach this? + +BASIC COMMANDS: + aft read [start] [limit] Read with line numbers + aft grep [path] Trigram-indexed search + aft glob [path] File pattern matching + +EXAMPLES: + aft outline src/ # Get structure of all files in src/ + aft zoom main.go main # Inspect main() with call graph + aft callers api.go HandleRequest # Find all callers + aft call_tree service.go Process # See what Process() calls +EOF + ;; + + *) + echo "Unknown command: $CMD" + echo "Run 'aft help' for usage" + exit 1 + ;; +esac +WRAPPER_EOF + +# Replace placeholder with actual binary path +sed -i '' "s|__AFT_BINARY_PATH__|$AFT_BINARY|g" "$HOOKS_DIR/aft" +chmod +x "$HOOKS_DIR/aft" +info "Installed CLI wrapper: $HOOKS_DIR/aft" + +# Write aft-hook.sh +cat > "$HOOKS_DIR/aft-hook.sh" << 'HOOK_EOF' +#!/usr/bin/env bash +# AFT Hook for Claude Code +# Intercepts Read, Grep, Glob tools and routes them through AFT binary + +TOOL_NAME="${1:-}" +AFT_BINARY="__AFT_BINARY_PATH__" + +# Check dependencies +command -v jq &>/dev/null || exit 0 +[ -x "$AFT_BINARY" ] || exit 0 + +# Read input JSON +INPUT=$(cat) +TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}') +WORK_DIR=$(echo "$INPUT" | jq -r '.session.workingDirectory // "."') + +# Call AFT binary with configure + command +call_aft() { + local cmd="$1" + local params="$2" + + local config_req=$(jq -cn --arg root "$WORK_DIR" '{id:"cfg",command:"configure",project_root:$root}') + local cmd_req=$(echo "$params" | jq -c --arg cmd "$cmd" '{id:"cmd",command:$cmd} + .') + + (echo "$config_req"; echo "$cmd_req") | "$AFT_BINARY" 2>/dev/null | grep '"id":"cmd"' | head -1 +} + +case "$TOOL_NAME" in + Read) + FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty') + [ -z "$FILE_PATH" ] && exit 0 + + OFFSET=$(echo "$TOOL_INPUT" | jq -r '.offset // 0') + LIMIT=$(echo "$TOOL_INPUT" | jq -r '.limit // 2000') + START_LINE=$((OFFSET + 1)) + + PARAMS=$(jq -cn --arg f "$FILE_PATH" --argjson s "$START_LINE" --argjson l "$LIMIT" \ + '{file:$f,start_line:$s,limit:$l}') + + RESULT=$(call_aft "read" "$PARAMS") + [ -z "$RESULT" ] && exit 0 + + SUCCESS=$(echo "$RESULT" | jq -r '.success') + [ "$SUCCESS" != "true" ] && exit 0 + + CONTENT=$(echo "$RESULT" | jq -r '.content // empty') + [ -z "$CONTENT" ] && exit 0 + + # Output to stderr for exit 2 blocking message + echo "[AFT Read] $FILE_PATH" >&2 + echo "$CONTENT" >&2 + exit 2 + ;; + + Grep) + PATTERN=$(echo "$TOOL_INPUT" | jq -r '.pattern // empty') + [ -z "$PATTERN" ] && exit 0 + + PATH_ARG=$(echo "$TOOL_INPUT" | jq -r '.path // "."') + INCLUDE=$(echo "$TOOL_INPUT" | jq -r '.include // empty') + + if [ -n "$INCLUDE" ]; then + PARAMS=$(jq -cn --arg p "$PATTERN" --arg d "$PATH_ARG" --arg i "$INCLUDE" \ + '{pattern:$p,path:$d,include:$i}') + else + PARAMS=$(jq -cn --arg p "$PATTERN" --arg d "$PATH_ARG" '{pattern:$p,path:$d}') + fi + + RESULT=$(call_aft "grep" "$PARAMS") + [ -z "$RESULT" ] && exit 0 + + SUCCESS=$(echo "$RESULT" | jq -r '.success') + [ "$SUCCESS" != "true" ] && exit 0 + + CONTENT=$(echo "$RESULT" | jq -r '.text // empty') + [ -z "$CONTENT" ] && exit 0 + + echo "[AFT Grep] $PATTERN" >&2 + echo "$CONTENT" >&2 + exit 2 + ;; + + Glob) + PATTERN=$(echo "$TOOL_INPUT" | jq -r '.pattern // empty') + [ -z "$PATTERN" ] && exit 0 + + PATH_ARG=$(echo "$TOOL_INPUT" | jq -r '.path // "."') + PARAMS=$(jq -cn --arg p "$PATTERN" --arg d "$PATH_ARG" '{pattern:$p,path:$d}') + + RESULT=$(call_aft "glob" "$PARAMS") + [ -z "$RESULT" ] && exit 0 + + SUCCESS=$(echo "$RESULT" | jq -r '.success') + [ "$SUCCESS" != "true" ] && exit 0 + + CONTENT=$(echo "$RESULT" | jq -r '.text // empty') + [ -z "$CONTENT" ] && exit 0 + + echo "[AFT Glob] $PATTERN" >&2 + echo "$CONTENT" >&2 + exit 2 + ;; + + *) + exit 0 + ;; +esac +HOOK_EOF + +sed -i '' "s|__AFT_BINARY_PATH__|$AFT_BINARY|g" "$HOOKS_DIR/aft-hook.sh" +chmod +x "$HOOKS_DIR/aft-hook.sh" +info "Installed hook script: $HOOKS_DIR/aft-hook.sh" + +# Write AFT.md instructions +cat > "$CLAUDE_DIR/AFT.md" << 'INSTRUCTIONS_EOF' +# AFT - Agent File Tools + +Tree-sitter powered code analysis for massive context savings (60-90% token reduction). + +## AFT CLI Commands + +Use `aft` commands via Bash for code navigation. These provide structured output optimized for LLM consumption. + +### Semantic Commands (prefer these over raw file reads) + +```bash +# Get structure without content (~10% of full read tokens) +aft outline + +# Inspect symbol with call-graph annotations +aft zoom + +# Forward call graph - what does this function call? +aft call_tree + +# Reverse call graph - who calls this function? +aft callers + +# Impact analysis - what breaks if this changes? +aft impact + +# Trace analysis - how does execution reach this? +aft trace_to +``` + +### Basic Commands + +```bash +aft read [start_line] [limit] # Read with line numbers +aft grep [path] # Trigram-indexed search +aft glob [path] # File pattern matching +``` + +## When to Use What + +| Task | Command | Token Savings | +|------|---------|---------------| +| Understanding file structure | `aft outline` | ~90% vs full read | +| Finding function definition | `aft zoom file symbol` | Exact code only | +| Understanding dependencies | `aft call_tree` | Structured graph | +| Finding usage sites | `aft callers` | All call sites | +| Planning refactors | `aft impact` | Change propagation | +| Debugging call paths | `aft trace_to` | Execution paths | + +## Best Practices + +1. **Start with outline** - Before reading a file, use `aft outline` to understand structure +2. **Zoom to symbols** - Instead of reading full files, use `aft zoom` for specific functions +3. **Use call graphs** - For understanding code flow, `call_tree` and `callers` are more efficient than grep +4. **Impact before refactor** - Run `aft impact` before making changes to understand blast radius + +## Supported Languages + +TypeScript, JavaScript, Python, Rust, Go, C/C++, Java, Ruby, Markdown + +## Hook Integration + +Read, Grep, and Glob tools are automatically routed through AFT via hooks for indexed performance. +INSTRUCTIONS_EOF + +info "Installed instructions: $CLAUDE_DIR/AFT.md" + +# Update CLAUDE.md to include @AFT.md +if [ -f "$CLAUDE_DIR/CLAUDE.md" ]; then + if ! grep -q "@AFT.md" "$CLAUDE_DIR/CLAUDE.md"; then + echo "@AFT.md" >> "$CLAUDE_DIR/CLAUDE.md" + info "Added @AFT.md to existing CLAUDE.md" + else + info "CLAUDE.md already includes @AFT.md" + fi +else + echo "@AFT.md" > "$CLAUDE_DIR/CLAUDE.md" + info "Created CLAUDE.md with @AFT.md" +fi + +# Update settings.json with hooks +SETTINGS_FILE="$CLAUDE_DIR/settings.json" + +if [ -f "$SETTINGS_FILE" ]; then + # Check if hooks already exist + if jq -e '.hooks.PreToolUse[] | select(.matcher == "Read") | .hooks[] | select(.command | contains("aft-hook.sh"))' "$SETTINGS_FILE" &>/dev/null; then + info "AFT hooks already configured in settings.json" + else + # Add AFT hooks to existing PreToolUse array + TEMP_FILE=$(mktemp) + + jq --arg hooks_dir "$HOOKS_DIR" ' + .hooks.PreToolUse = ( + (.hooks.PreToolUse // []) + [ + { + "matcher": "Read", + "hooks": [{"type": "command", "command": ($hooks_dir + "/aft-hook.sh Read")}] + }, + { + "matcher": "Grep", + "hooks": [{"type": "command", "command": ($hooks_dir + "/aft-hook.sh Grep")}] + }, + { + "matcher": "Glob", + "hooks": [{"type": "command", "command": ($hooks_dir + "/aft-hook.sh Glob")}] + } + ] + ) + ' "$SETTINGS_FILE" > "$TEMP_FILE" + + mv "$TEMP_FILE" "$SETTINGS_FILE" + info "Added AFT hooks to settings.json" + fi +else + # Create new settings.json + cat > "$SETTINGS_FILE" << SETTINGS_EOF +{ + "\$schema": "https://json.schemastore.org/claude-code-settings.json", + "hooks": { + "PreToolUse": [ + { + "matcher": "Read", + "hooks": [{"type": "command", "command": "$HOOKS_DIR/aft-hook.sh Read"}] + }, + { + "matcher": "Grep", + "hooks": [{"type": "command", "command": "$HOOKS_DIR/aft-hook.sh Grep"}] + }, + { + "matcher": "Glob", + "hooks": [{"type": "command", "command": "$HOOKS_DIR/aft-hook.sh Glob"}] + } + ] + } +} +SETTINGS_EOF + info "Created settings.json with AFT hooks" +fi + +# Add aft to PATH via symlink +if [ -d "/usr/local/bin" ] && [ -w "/usr/local/bin" ]; then + ln -sf "$HOOKS_DIR/aft" /usr/local/bin/aft 2>/dev/null && \ + info "Symlinked aft to /usr/local/bin/aft" || \ + warn "Could not symlink to /usr/local/bin (run with sudo if needed)" +else + warn "Cannot write to /usr/local/bin - add $HOOKS_DIR to PATH manually" +fi + +echo "" +echo -e "${GREEN}AFT Claude Code integration installed successfully!${NC}" +echo "" +echo "Installed files:" +echo " $HOOKS_DIR/aft - CLI wrapper" +echo " $HOOKS_DIR/aft-hook.sh - Tool interceptor" +echo " $CLAUDE_DIR/AFT.md - Claude instructions" +echo " $CLAUDE_DIR/settings.json - Hook configuration" +echo "" +echo "Usage:" +echo " aft outline src/ # Get file structure" +echo " aft zoom file.ts func # Inspect function" +echo " aft callers file.ts func # Find all callers" +echo "" +echo "Restart Claude Code to activate hooks." diff --git a/scripts/uninstall-claude-hooks.sh b/scripts/uninstall-claude-hooks.sh new file mode 100755 index 0000000..a65a261 --- /dev/null +++ b/scripts/uninstall-claude-hooks.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# AFT Claude Code Hooks Uninstaller +# Removes AFT hooks from Claude Code + +set -euo pipefail + +CLAUDE_DIR="$HOME/.claude" +HOOKS_DIR="$CLAUDE_DIR/hooks" + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +info() { echo -e "${GREEN}[INFO]${NC} $1"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } + +# Remove hook files +[ -f "$HOOKS_DIR/aft" ] && rm "$HOOKS_DIR/aft" && info "Removed $HOOKS_DIR/aft" +[ -f "$HOOKS_DIR/aft-hook.sh" ] && rm "$HOOKS_DIR/aft-hook.sh" && info "Removed $HOOKS_DIR/aft-hook.sh" + +# Remove AFT.md +[ -f "$CLAUDE_DIR/AFT.md" ] && rm "$CLAUDE_DIR/AFT.md" && info "Removed $CLAUDE_DIR/AFT.md" + +# Remove @AFT.md from CLAUDE.md +if [ -f "$CLAUDE_DIR/CLAUDE.md" ]; then + if grep -q "@AFT.md" "$CLAUDE_DIR/CLAUDE.md"; then + sed -i '' '/@AFT.md/d' "$CLAUDE_DIR/CLAUDE.md" + info "Removed @AFT.md from CLAUDE.md" + fi +fi + +# Remove hooks from settings.json +SETTINGS_FILE="$CLAUDE_DIR/settings.json" +if [ -f "$SETTINGS_FILE" ] && command -v jq &>/dev/null; then + TEMP_FILE=$(mktemp) + jq ' + .hooks.PreToolUse = [ + .hooks.PreToolUse[] | select(.hooks[].command | contains("aft-hook.sh") | not) + ] + ' "$SETTINGS_FILE" > "$TEMP_FILE" 2>/dev/null && mv "$TEMP_FILE" "$SETTINGS_FILE" && \ + info "Removed AFT hooks from settings.json" +fi + +# Remove symlink +[ -L "/usr/local/bin/aft" ] && rm "/usr/local/bin/aft" 2>/dev/null && info "Removed /usr/local/bin/aft symlink" + +echo "" +echo -e "${GREEN}AFT Claude Code hooks uninstalled.${NC}" +echo "Restart Claude Code to complete removal." From e254d5f731ebc9272dab6180e095ea0f51d40b4d Mon Sep 17 00:00:00 2001 From: nandanugg Date: Thu, 16 Apr 2026 21:05:28 +0700 Subject: [PATCH 3/6] docs: strengthen CLAUDE.md with mandatory rules and context protection - Add MANDATORY section emphasizing AFT-first approach - Add Decision Tree for quick command selection - Rename "Best Practices" to "Rules (NOT suggestions)" - Add Context Protection section to prevent context exhaustion - Clarify that AFT applies to all file types, not just code Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5a97cc3..1edb8dd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,11 +2,20 @@ Tree-sitter powered code analysis for massive context savings (60-90% token reduction). +## MANDATORY: Always Use AFT First + +**CRITICAL**: AFT semantic commands are the DEFAULT, not optional. Grep/Read with limited context (e.g., 3 lines) misses the bigger picture. We want to SEE the full picture, not shoot in the dark. + +**Before ANY code exploration:** +1. `aft outline` FIRST - understand structure before diving in +2. `aft zoom` for symbols - never read full files when you need one function +3. `aft callers`/`aft call_tree` for flow - grep misses cross-file relationships + ## AFT CLI Commands Use `aft` commands via Bash for code navigation. These provide structured output optimized for LLM consumption. -### Semantic Commands (prefer these over raw file reads) +### Semantic Commands (USE THESE BY DEFAULT) ```bash # Get structure without content (~10% of full read tokens) @@ -28,7 +37,7 @@ aft impact aft trace_to ``` -### Basic Commands +### Basic Commands (fallback only) ```bash aft read [start_line] [limit] # Read with line numbers @@ -36,6 +45,30 @@ aft grep [path] # Trigram-indexed search aft glob [path] # File pattern matching ``` +## Decision Tree + +``` +Need to understand code? + | + +-- Don't know the file structure? + | -> aft outline + | + +-- Know the file, need specific symbol? + | -> aft zoom + | + +-- Need to understand what calls what? + | -> aft call_tree + | + +-- Need to find all usages? + | -> aft callers + | + +-- Planning a change? + | -> aft impact + | + +-- Debugging how execution reaches a point? + -> aft trace_to +``` + ## When to Use What | Task | Command | Token Savings | @@ -47,12 +80,22 @@ aft glob [path] # File pattern matching | Planning refactors | `aft impact` | Change propagation | | Debugging call paths | `aft trace_to` | Execution paths | -## Best Practices +## Rules (NOT suggestions) + +1. **ALWAYS start with outline** - Before reading ANY file, use `aft outline` to understand structure +2. **ALWAYS zoom to symbols** - Never read full files when you need specific functions +3. **ALWAYS use call graphs** - For understanding code flow, `call_tree` and `callers` reveal what grep cannot +4. **ALWAYS impact before refactor** - Run `aft impact` before making changes to understand blast radius +5. **NEVER grep with limited context** - If you need more than the symbol name, use AFT semantic commands + +## Context Protection + +**Context is finite.** Even when a user explicitly requests "contents" or "read all files": -1. **Start with outline** - Before reading a file, use `aft outline` to understand structure -2. **Zoom to symbols** - Instead of reading full files, use `aft zoom` for specific functions -3. **Use call graphs** - For understanding code flow, `call_tree` and `callers` are more efficient than grep -4. **Impact before refactor** - Run `aft impact` before making changes to understand blast radius +1. **Directory reads: outline first** - For directories with 5+ files, ALWAYS run `aft outline` and confirm which specific files are needed before reading +2. **All file types benefit** - AFT applies to markdown, config, docs, and data files — not just code. Documentation directories especially benefit from outline-first +3. **Batch limit** - Never read more than 3-5 files in a single action without confirming user intent. Context exhaustion breaks the conversation. +4. **User requests don't override physics** - "Read all files" is a request, not a command to fill context. Propose `aft outline` + selective reads instead. ## Supported Languages From 305251558c46b2c3f127baab0f2fa6bac96df7c5 Mon Sep 17 00:00:00 2001 From: nandanugg Date: Thu, 16 Apr 2026 21:10:34 +0700 Subject: [PATCH 4/6] docs: emphasize AFT applies to all file types, not just code - Add prominent "AFT applies to ALL file types" note in MANDATORY section - Change "Before ANY code exploration" to "Before reading ANY files" - Add docs/config example to decision tree - Add new row for docs/configs in "When to Use What" table - Add rule #6: "ALWAYS outline before sampling" Addresses feedback that code-centric language caused AFT to be skipped for markdown/documentation files. Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 1edb8dd..92e97c5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,9 @@ Tree-sitter powered code analysis for massive context savings (60-90% token redu **CRITICAL**: AFT semantic commands are the DEFAULT, not optional. Grep/Read with limited context (e.g., 3 lines) misses the bigger picture. We want to SEE the full picture, not shoot in the dark. -**Before ANY code exploration:** +**AFT applies to ALL file types** — not just code. Markdown, config, docs, JSON, YAML all benefit. Even for "just checking what files are" — outline first. + +**Before reading ANY files:** 1. `aft outline` FIRST - understand structure before diving in 2. `aft zoom` for symbols - never read full files when you need one function 3. `aft callers`/`aft call_tree` for flow - grep misses cross-file relationships @@ -48,11 +50,14 @@ aft glob [path] # File pattern matching ## Decision Tree ``` -Need to understand code? +Need to understand files? | +-- Don't know the file structure? | -> aft outline | + +-- Checking what files contain (docs, config, etc.)? + | -> aft outline , then selective reads + | +-- Know the file, need specific symbol? | -> aft zoom | @@ -74,6 +79,7 @@ Need to understand code? | Task | Command | Token Savings | |------|---------|---------------| | Understanding file structure | `aft outline` | ~90% vs full read | +| Checking what docs/configs contain | `aft outline` + selective read | ~80% vs read all | | Finding function definition | `aft zoom file symbol` | Exact code only | | Understanding dependencies | `aft call_tree` | Structured graph | | Finding usage sites | `aft callers` | All call sites | @@ -87,6 +93,7 @@ Need to understand code? 3. **ALWAYS use call graphs** - For understanding code flow, `call_tree` and `callers` reveal what grep cannot 4. **ALWAYS impact before refactor** - Run `aft impact` before making changes to understand blast radius 5. **NEVER grep with limited context** - If you need more than the symbol name, use AFT semantic commands +6. **ALWAYS outline before sampling** - Even for "just checking what files are" tasks, outline first ## Context Protection From 0b9821b17361e21cef7e6e13e313062f8aae699a Mon Sep 17 00:00:00 2001 From: nandanugg Date: Thu, 16 Apr 2026 21:38:45 +0700 Subject: [PATCH 5/6] docs: clarify Read hook limitation and Edit workflow - Remove Read from hooked tools list (conflicts with Edit validation) - Add guidance to use `aft read` via Bash for indexed reads - Add warning: use native Read tool when editing is needed Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 92e97c5..7ebe916 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -110,4 +110,8 @@ TypeScript, JavaScript, Python, Rust, Go, C/C++, Java, Ruby, Markdown ## Hook Integration -Read, Grep, and Glob tools are automatically routed through AFT via hooks for indexed performance. +Grep and Glob tools are automatically routed through AFT via hooks for indexed performance. + +**Reading files**: Use `aft read` via Bash for indexed reads with token savings. + +**Warning**: When you need to Edit a file, use the native Read tool (not `aft read`) because Edit requires a prior Read tool call for validation. From 0847f9f1f54dea10f99f91b454ba75a8a148160f Mon Sep 17 00:00:00 2001 From: nandanugg Date: Thu, 16 Apr 2026 21:50:42 +0700 Subject: [PATCH 6/6] docs: add rule for outlining before delegating to subagents Subagents don't follow ordering guarantees, so leaving "outline first" as a mid-step instruction doesn't work. Run outline yourself and include the output in the subagent prompt. Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CLAUDE.md b/CLAUDE.md index 7ebe916..3e274c1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -94,6 +94,7 @@ Need to understand files? 4. **ALWAYS impact before refactor** - Run `aft impact` before making changes to understand blast radius 5. **NEVER grep with limited context** - If you need more than the symbol name, use AFT semantic commands 6. **ALWAYS outline before sampling** - Even for "just checking what files are" tasks, outline first +7. **ALWAYS outline before delegating** - When briefing a subagent to explore a repo or directory, run `aft outline ` yourself first and include the output in the subagent prompt. Never leave outline as a mid-step instruction — subagents don't follow ordering guarantees. ## Context Protection