From 624648dab66d3fb8011dee5d3a42c3a084807e9a Mon Sep 17 00:00:00 2001 From: Cody Hart Date: Tue, 27 Jan 2026 19:28:33 -0500 Subject: [PATCH] Harden scripts, pin CI actions, and clean up plugin structure - Pin actions/checkout to SHA (v4.2.2) in CI and release workflows - Fix package.sh cd leak by wrapping zip step in subshell - Fix test.sh: check_json_field uses sys.argv instead of string interpolation, check_frontmatter reports all missing fields, add python3 availability guard - Align again.md to use Write tool matching the settings permission - Remove redundant dev-install.sh (duplicated by make dev) - Rename command from lookagain to look:again (plugin name: look) - Add Makefile, CONTRIBUTING.md, and comprehensive test suite Co-Authored-By: Claude Opus 4.5 --- .github/workflows/ci.yml | 98 +---------- .github/workflows/release.yml | 6 +- CONTRIBUTING.md | 78 +++++++++ Makefile | 20 +++ README.md | 12 +- scripts/package.sh | 5 +- scripts/test.sh | 263 +++++++++++++++++++++++++++++ src/agents/lookagain-reviewer.md | 8 +- src/commands/again.md | 129 ++++++++++++++ src/commands/lookagain.md | 121 ------------- src/dot-claude-plugin/plugin.json | 10 +- src/dot-claude/settings.local.json | 2 +- 12 files changed, 515 insertions(+), 237 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 Makefile create mode 100755 scripts/test.sh create mode 100644 src/commands/again.md delete mode 100644 src/commands/lookagain.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 154c01c..81c6694 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,102 +8,12 @@ on: jobs: validate: - name: Validate Plugin Structure + name: Validate Plugin runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Validate plugin.json - run: | - if ! python3 -c "import json; json.load(open('src/dot-claude-plugin/plugin.json'))"; then - echo "Error: plugin.json is not valid JSON" - exit 1 - fi - echo "plugin.json is valid" - - - name: Check required files exist - run: | - required_files=( - "src/dot-claude-plugin/plugin.json" - "src/commands/lookagain.md" - "src/agents/lookagain-reviewer.md" - "src/skills/lookagain-output-format/SKILL.md" - "README.md" - "LICENSE" - "CHANGELOG.md" - ) - - missing=0 - for file in "${required_files[@]}"; do - if [[ ! -f "$file" ]]; then - echo "Missing: $file" - missing=1 - else - echo "Found: $file" - fi - done - - if [[ $missing -eq 1 ]]; then - exit 1 - fi - - - name: Validate plugin.json references - run: | - # Check that referenced commands, agents, skills exist - commands=$(python3 -c "import json; print('\n'.join(json.load(open('src/dot-claude-plugin/plugin.json')).get('commands', [])))") - agents=$(python3 -c "import json; print('\n'.join(json.load(open('src/dot-claude-plugin/plugin.json')).get('agents', [])))") - skills=$(python3 -c "import json; print('\n'.join(json.load(open('src/dot-claude-plugin/plugin.json')).get('skills', [])))") - - errors=0 - - for cmd in $commands; do - if [[ ! -f "src/commands/$cmd.md" ]]; then - echo "Error: Command '$cmd' referenced but src/commands/$cmd.md not found" - errors=1 - fi - done - - for agent in $agents; do - if [[ ! -f "src/agents/$agent.md" ]]; then - echo "Error: Agent '$agent' referenced but src/agents/$agent.md not found" - errors=1 - fi - done - - for skill in $skills; do - if [[ ! -f "src/skills/$skill/SKILL.md" ]]; then - echo "Error: Skill '$skill' referenced but src/skills/$skill/SKILL.md not found" - errors=1 - fi - done - - if [[ $errors -eq 1 ]]; then - exit 1 - fi - - echo "All plugin.json references are valid" - - - name: Test package script - run: | - chmod +x scripts/package.sh - ./scripts/package.sh - - # Verify output structure - if [[ ! -d "dist/lookagain/.claude" ]]; then - echo "Error: dist/lookagain/.claude not created" - exit 1 - fi - - if [[ ! -d "dist/lookagain/.claude-plugin" ]]; then - echo "Error: dist/lookagain/.claude-plugin not created" - exit 1 - fi - - if ! ls dist/lookagain-v*.zip 1>/dev/null 2>&1; then - echo "Error: zip archive not created" - exit 1 - fi - - echo "Package script completed successfully" + - name: Run tests + run: make test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3d1d474..43f6c97 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Validate tag matches plugin version run: | @@ -48,10 +48,10 @@ jobs: fi # Write to file to preserve newlines - echo "$changelog" > release_notes.md + printf '%s\n' "$changelog" > release_notes.md - name: Create GitHub Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1 with: body_path: release_notes.md files: dist/lookagain-v*.zip diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0c5f3d7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,78 @@ +# Contributing to lookagain + +## Development Setup + +1. Clone the repository: + ```bash + git clone https://github.com/HartBrook/lookagain.git + cd lookagain + ``` + +2. Ensure you have [Claude Code](https://claude.ai/code) installed. + +## Project Structure + +``` +lookagain/ +├── src/ +│ ├── commands/ # Plugin commands +│ │ └── again.md # Main orchestrator +│ ├── agents/ # Subagent definitions +│ │ └── lookagain-reviewer.md +│ ├── skills/ # Output format specs +│ │ └── lookagain-output-format/ +│ ├── dot-claude-plugin/ # Plugin manifest (becomes .claude-plugin/) +│ └── dot-claude/ # Claude settings (becomes .claude/) +├── scripts/ +│ ├── package.sh # Build script +│ └── test.sh # Plugin validation tests +├── dist/ # Build output (git-ignored) +└── Makefile +``` + +## Development Workflow + +### Build and Test Locally + +```bash +# Build and start Claude Code with the plugin loaded +make dev + +# Or just build without starting +make build + +# Clean build artifacts +make clean + +# Show all available commands +make help +``` + +`make dev` builds the plugin and starts a new Claude Code session with it loaded. Test with `/look:again`. + +### Making Changes + +1. Edit files in `src/` +2. Run `make dev` to rebuild and start Claude Code with the plugin +3. Test with `/look:again` +4. Exit and repeat + +### Key Files + +- **[src/commands/again.md](src/commands/again.md)**: Main orchestrator logic. Controls pass execution, auto-fixing, and aggregation. +- **[src/agents/lookagain-reviewer.md](src/agents/lookagain-reviewer.md)**: Reviewer subagent. Defines how individual review passes work. +- **[src/skills/lookagain-output-format/SKILL.md](src/skills/lookagain-output-format/SKILL.md)**: JSON output format specification. +- **[src/dot-claude-plugin/plugin.json](src/dot-claude-plugin/plugin.json)**: Plugin metadata and version. + +## Pull Requests + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/my-feature`) +3. Make your changes +4. Test locally with `make dev` +5. Commit with a clear message +6. Open a pull request + +## Versioning + +Update the version in `src/dot-claude-plugin/plugin.json` when making releases. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9bec581 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +.PHONY: help build test dev clean + +help: ## Show this help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}' + +build: ## Build the plugin (creates dist/) + @./scripts/package.sh + +test: ## Run plugin validation tests + @./scripts/test.sh + +dev: build ## Build and start Claude Code with plugin loaded + @echo "" + @echo "Starting Claude Code with plugin loaded..." + @echo "" + @claude --plugin-dir ./dist/lookagain + +clean: ## Remove build artifacts + @rm -rf dist/ + @echo "Cleaned dist/" diff --git a/README.md b/README.md index 418e182..f2e53e5 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A single code review pass catches ~60-70% of issues. Running multiple independen ``` ┌─────────────────────────────────────────────────────┐ -│ /lookagain │ +│ /look:again │ │ │ │ ┌───────────────────────────────────────────┐ │ │ │ Orchestrator (main agent) │ │ @@ -45,19 +45,19 @@ A single code review pass catches ~60-70% of issues. Running multiple independen ```bash # Basic: 3 review passes with auto-fix for must_fix issues -/lookagain +/look:again # More passes for critical code -/lookagain passes=5 +/look:again passes=5 # Review specific directory -/lookagain target=src/auth +/look:again target=src/auth # Disable auto-fix (review only) -/lookagain auto-fix=false +/look:again auto-fix=false # Increase max passes for stubborn issues -/lookagain passes=3 max-passes=10 +/look:again passes=3 max-passes=10 ``` ## How It Works diff --git a/scripts/package.sh b/scripts/package.sh index d985eda..82d7d06 100755 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -9,7 +9,7 @@ PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" DIST_DIR="$PROJECT_ROOT/dist" # Get version from plugin.json -VERSION=$(grep -o '"version": *"[^"]*"' "$PROJECT_ROOT/src/dot-claude-plugin/plugin.json" | cut -d'"' -f4) +VERSION=$(python3 -c "import json; print(json.load(open('$PROJECT_ROOT/src/dot-claude-plugin/plugin.json'))['version'])") if [[ -z "$VERSION" ]]; then echo "Error: Could not extract version from plugin.json" @@ -35,8 +35,7 @@ cp "$PROJECT_ROOT/LICENSE" "$DIST_DIR/lookagain/" cp "$PROJECT_ROOT/CHANGELOG.md" "$DIST_DIR/lookagain/" # Create zip archive -cd "$DIST_DIR" -zip -r "lookagain-v$VERSION.zip" lookagain +(cd "$DIST_DIR" && zip -r "lookagain-v$VERSION.zip" lookagain) echo "" echo "Build complete:" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..3d2ce12 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,263 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +if ! command -v python3 >/dev/null 2>&1; then + echo "Error: python3 is required but not found" + exit 1 +fi + +PASS=0 +FAIL=0 + +pass() { + PASS=$((PASS + 1)) + echo " ✓ $1" +} + +fail() { + FAIL=$((FAIL + 1)) + echo " ✗ $1" +} + +check_file() { + if [[ -f "$PROJECT_ROOT/$1" ]]; then + pass "$1 exists" + else + fail "$1 missing" + fi +} + +check_json_field() { + local file="$1" field="$2" + if python3 -c "import json,sys; d=json.load(open(sys.argv[1])); sys.exit(0 if sys.argv[2] in d else 1)" "$file" "$field" 2>/dev/null; then + pass "$field present" + else + fail "$field missing from $(basename "$file")" + fi +} + +check_frontmatter() { + local file="$1" + shift + local required_fields=("$@") + local relpath="${file#"$PROJECT_ROOT"/}" + + # Check file starts with --- + if ! head -1 "$file" | grep -q "^---$"; then + fail "$relpath: no frontmatter delimiter" + return + fi + + # Check required fields in frontmatter via grep (robust against lenient YAML) + local frontmatter + frontmatter=$(awk 'NR==1{next} /^---$/{exit} {print}' "$file") + local missing=0 + for field in "${required_fields[@]}"; do + if ! echo "$frontmatter" | grep -q "^${field}:"; then + fail "$relpath: field '$field' not found" + missing=1 + fi + done + if [[ $missing -eq 0 ]]; then + pass "$relpath frontmatter valid" + fi +} + +# ============================================================ +# Test Groups +# ============================================================ + +test_plugin_json() { + local pjson="$PROJECT_ROOT/src/dot-claude-plugin/plugin.json" + + # Valid JSON + if python3 -c "import json; json.load(open('$pjson'))" 2>/dev/null; then + pass "plugin.json is valid JSON" + else + fail "plugin.json is not valid JSON" + return + fi + + # Required fields + for field in name version description author commands agents; do + check_json_field "$pjson" "$field" + done + + # Version matches semver + if python3 -c " +import json, re, sys +v = json.load(open('$pjson'))['version'] +sys.exit(0 if re.match(r'^\d+\.\d+\.\d+$', v) else 1) +" 2>/dev/null; then + pass "version is valid semver" + else + fail "version is not valid semver" + fi + + # Author has name + if python3 -c " +import json, sys +d = json.load(open('$pjson')) +sys.exit(0 if isinstance(d.get('author'), dict) and 'name' in d['author'] else 1) +" 2>/dev/null; then + pass "author.name present" + else + fail "author must be an object with name field" + fi +} + +test_required_files() { + check_file "src/dot-claude-plugin/plugin.json" + check_file "src/commands/again.md" + check_file "src/agents/lookagain-reviewer.md" + check_file "src/skills/lookagain-output-format/SKILL.md" + check_file "src/dot-claude/settings.local.json" + check_file "README.md" + check_file "LICENSE" + check_file "CHANGELOG.md" +} + +test_frontmatter() { + check_frontmatter "$PROJECT_ROOT/src/commands/again.md" name description + check_frontmatter "$PROJECT_ROOT/src/agents/lookagain-reviewer.md" name description tools + check_frontmatter "$PROJECT_ROOT/src/skills/lookagain-output-format/SKILL.md" name description +} + +test_cross_references() { + local pjson="$PROJECT_ROOT/src/dot-claude-plugin/plugin.json" + + # Commands resolve + while IFS= read -r cmd; do + local resolved="$PROJECT_ROOT/src/${cmd#./}" + if [[ -f "$resolved" ]]; then + pass "command $cmd resolves" + else + fail "command $cmd not found at src/${cmd#./}" + fi + done < <(python3 -c "import json; [print(c) for c in json.load(open('$pjson')).get('commands', [])]") + + # Agents resolve + while IFS= read -r agent; do + local resolved="$PROJECT_ROOT/src/${agent#./}" + if [[ -f "$resolved" ]]; then + pass "agent $agent resolves" + else + fail "agent $agent not found at src/${agent#./}" + fi + done < <(python3 -c "import json; [print(a) for a in json.load(open('$pjson')).get('agents', [])]") + + # Skills resolve with SKILL.md + while IFS= read -r skill; do + local resolved="$PROJECT_ROOT/src/${skill#./}" + if [[ -d "$resolved" ]] && [[ -f "$resolved/SKILL.md" ]]; then + pass "skill $skill resolves with SKILL.md" + else + fail "skill $skill not found or missing SKILL.md" + fi + done < <(python3 -c "import json; [print(s) for s in json.load(open('$pjson')).get('skills', [])]") +} + +test_build() { + # Clean and build + rm -rf "$PROJECT_ROOT/dist" + "$PROJECT_ROOT/scripts/package.sh" > /dev/null + + local dist="$PROJECT_ROOT/dist/lookagain" + + # Check structure + if [[ -d "$dist/.claude" ]]; then + pass "dist/.claude/ created" + else + fail "dist/.claude/ missing" + fi + + if [[ -d "$dist/.claude-plugin" ]]; then + pass "dist/.claude-plugin/ created" + else + fail "dist/.claude-plugin/ missing" + fi + + if python3 -c "import json; json.load(open('$dist/.claude-plugin/plugin.json'))" 2>/dev/null; then + pass "dist plugin.json is valid JSON" + else + fail "dist plugin.json is invalid" + fi + + for f in commands/again.md agents/lookagain-reviewer.md skills/lookagain-output-format/SKILL.md README.md; do + if [[ -f "$dist/$f" ]]; then + pass "dist/$f exists" + else + fail "dist/$f missing" + fi + done + + # Zip exists with correct version + local version + version=$(python3 -c "import json; print(json.load(open('$PROJECT_ROOT/src/dot-claude-plugin/plugin.json'))['version'])") + if [[ -f "$PROJECT_ROOT/dist/lookagain-v${version}.zip" ]]; then + pass "zip archive lookagain-v${version}.zip exists" + else + fail "zip archive not found" + fi +} + +test_settings() { + local settings="$PROJECT_ROOT/src/dot-claude/settings.local.json" + + if python3 -c "import json; json.load(open('$settings'))" 2>/dev/null; then + pass "settings.local.json is valid JSON" + else + fail "settings.local.json is not valid JSON" + return + fi + + if python3 -c " +import json, sys +d = json.load(open('$settings')) +sys.exit(0 if isinstance(d.get('permissions', {}).get('allow'), list) else 1) +" 2>/dev/null; then + pass "permissions.allow is an array" + else + fail "permissions.allow missing or not an array" + fi +} + +# ============================================================ +# Run +# ============================================================ + +echo "=== lookagain test suite ===" +echo "" + +echo "--- plugin.json ---" +test_plugin_json +echo "" + +echo "--- required files ---" +test_required_files +echo "" + +echo "--- frontmatter ---" +test_frontmatter +echo "" + +echo "--- cross-references ---" +test_cross_references +echo "" + +echo "--- settings ---" +test_settings +echo "" + +echo "--- build ---" +test_build +echo "" + +echo "=== Results: $PASS passed, $FAIL failed ===" + +if [[ $FAIL -gt 0 ]]; then + exit 1 +fi diff --git a/src/agents/lookagain-reviewer.md b/src/agents/lookagain-reviewer.md index 36116a5..e60d398 100644 --- a/src/agents/lookagain-reviewer.md +++ b/src/agents/lookagain-reviewer.md @@ -11,10 +11,10 @@ You are an expert code reviewer performing an independent review pass. Your goal ## Review Process -1. **Understand the scope** - - What files/directories are you reviewing? - - Use Glob and Read to explore the codebase - - Check git diff if reviewing recent changes +1. **Read the target code directly** + - Use Glob to find source files in the target path (skip config, lockfiles, and generated files) + - Read each source file. Do not summarize or explore broadly — read the actual code. + - Use `git diff` to identify recent changes if the target is the full project 2. **Analyze for issues** - Security vulnerabilities (injection, auth bypass, data exposure) diff --git a/src/commands/again.md b/src/commands/again.md new file mode 100644 index 0000000..ec2ad9d --- /dev/null +++ b/src/commands/again.md @@ -0,0 +1,129 @@ +--- +name: again +description: Run sequential code review passes with fresh contexts to catch more issues +arguments: + - name: passes + description: Number of review passes to run + default: "3" + - name: target + description: Target directory or files to review (default: current directory) + default: "." + - name: auto-fix + description: Automatically fix must_fix issues between passes (true/false) + default: "true" + - name: max-passes + description: Maximum passes if must_fix issues persist + default: "7" +--- + +# Iterative Code Review + +You are orchestrating a sequential, multi-pass code review. Passes run ONE AT A TIME, with fixes applied between each pass so the next reviewer sees the improved code. + +## Configuration from arguments + +- **Passes**: $ARGUMENTS.passes +- **Target**: $ARGUMENTS.target +- **Auto-fix**: $ARGUMENTS.auto-fix +- **Max passes**: $ARGUMENTS.max-passes + +## Process + +### Phase 0: Setup + +1. Clean previous results: `rm -rf .lookagain && mkdir -p .lookagain` +2. Do NOT explore or read the codebase yourself. You are an orchestrator — your only job is to spawn reviewers, collect results, apply fixes, and aggregate. The reviewers will read the code. + +### Phase 1: Execute Review Passes Sequentially + +CRITICAL: Passes MUST run in sequence, NOT in parallel. Each pass reviews the code AS IT EXISTS AFTER previous fixes. + +Repeat the following loop for each pass (1 through configured number of passes): + +**Step 1 — Review**: Spawn a fresh subagent using the Task tool with the `lookagain-reviewer` agent. +- Include in the prompt: pass number, target path, and instruction to output JSON. +- Do NOT include findings from previous passes. The subagent must review independently. +- WAIT for the subagent to complete before proceeding. + +**Step 2 — Collect**: Parse the JSON findings from the subagent response. +- Expected structure: `{ "issues": [{ "severity": "must_fix|should_fix|suggestion", "title": "...", "description": "...", "file": "...", "line": N, "suggested_fix": "..." }] }` +- Store findings and track which pass found each issue. + +**Step 3 — Fix**: If auto-fix is enabled, apply fixes for `must_fix` issues NOW, before the next pass. +- Make minimal code changes. Do not refactor. +- Do NOT fix `should_fix` or `suggestion` items. +- The next pass will review the code WITH these fixes applied. + +**Step 4 — Log and continue**: Log "Pass N complete. Found X must_fix, Y should_fix, Z suggestions." then proceed to the next pass. + +After the configured passes, if `must_fix` issues remain and we haven't hit max-passes, run additional passes. + +### Phase 2: Aggregate Results + +After all passes complete: + +1. **Deduplicate findings** + - Same issue found in multiple passes = higher confidence + - Key on (file, title) - issues with same file and title are duplicates + - Track which passes found each unique issue + +2. **Calculate confidence scores** + - Confidence = (passes that found this issue) / (total passes) * 100% + +3. **Generate summary report** + - Group by severity + - Sort by confidence within each group + - Include file locations and suggested fixes + +### Phase 3: Save Results + +Save results to `.lookagain/` using the Write tool: + +1. `pass-N.json` - Raw findings from each pass (save after each pass completes) +2. `aggregate.json` - Machine-readable findings +3. `aggregate.md` - Human-readable report + +### Output Format + +Present the final summary to the user: + +``` +## Iterative Review Complete + +**Passes completed**: N +**Unique issues found**: X + +### Must Fix (N issues) + +| Issue | File | Confidence | Fixed | +| ----- | ---- | ---------- | ----- | +| ... | ... | ...% | ✓/✗ | + +### Should Fix (N issues) + +| Issue | File | Confidence | +| ----- | ---- | ---------- | +| ... | ... | ...% | + +### Suggestions (N issues) + +| Issue | File | Confidence | +| ----- | ---- | ---------- | +| ... | ... | ...% | + +Full report saved to `.lookagain/aggregate.md` +``` + +## Important Rules + +1. **Sequential, not parallel**: NEVER launch multiple review passes at the same time. Each pass must complete and its fixes must be applied before starting the next pass. + +2. **Fresh context per pass**: Always use Task tool for subagents. Never try to "reset" context manually. + +3. **Subagent independence**: Do NOT tell subagents what previous passes found. The value is independent analysis on the current state of the code. + +4. **Minimal fixes**: When auto-fixing, change only what's necessary. Don't refactor. + +5. **Structured output**: Ensure subagent returns valid JSON. If parsing fails, log error and continue. + +6. **Respect max-passes**: Never exceed max-passes, even if must_fix issues remain. diff --git a/src/commands/lookagain.md b/src/commands/lookagain.md deleted file mode 100644 index 6146b61..0000000 --- a/src/commands/lookagain.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -name: lookagain -description: Run sequential code review passes with fresh contexts to catch more issues -arguments: - - name: passes - description: Number of review passes to run - default: "3" - - name: target - description: Target directory or files to review (default: current directory) - default: "." - - name: auto-fix - description: Automatically fix must_fix issues between passes (true/false) - default: "true" - - name: max-passes - description: Maximum passes if must_fix issues persist - default: "7" ---- - -# Iterative Code Review - -You are orchestrating a multi-pass code review. Each pass MUST use a fresh subagent via the Task tool to ensure independent analysis. - -## Configuration from arguments - -- **Passes**: $ARGUMENTS.passes -- **Target**: $ARGUMENTS.target -- **Auto-fix**: $ARGUMENTS.auto-fix -- **Max passes**: $ARGUMENTS.max-passes - -## Process - -### Phase 1: Execute Review Passes - -For each pass from 1 to the configured number of passes: - -1. **Spawn a fresh subagent** using the Task tool with the `lookagain-reviewer` agent - - The subagent prompt should include: pass number, target path, and instruction to output JSON - - CRITICAL: Each Task invocation creates a fresh context. Do not pass previous findings to the subagent. - -2. **Collect the subagent's response** - - Parse the JSON findings from the subagent - - Findings should have structure: `{ "issues": [{ "severity": "must_fix|should_fix|suggestion", "title": "...", "description": "...", "file": "...", "line": N, "suggested_fix": "..." }] }` - -3. **Store findings for this pass** - - Keep track of which pass found each issue - -4. **Apply fixes if auto-fix is enabled** - - For each `must_fix` issue, make the minimal code change to fix it - - Do NOT fix `should_fix` or `suggestion` items automatically - -5. **Check continuation condition** - - If this was the last configured pass AND there are still `must_fix` issues AND we haven't hit max-passes, continue with another pass - - Log: "Pass N complete. Found X must_fix, Y should_fix, Z suggestions." - -### Phase 2: Aggregate Results - -After all passes complete: - -1. **Deduplicate findings** - - Same issue found in multiple passes = higher confidence - - Key on (file, title) - issues with same file and title are duplicates - - Track which passes found each unique issue - -2. **Calculate confidence scores** - - Confidence = (passes that found this issue) / (total passes) * 100% - -3. **Generate summary report** - - Group by severity - - Sort by confidence within each group - - Include file locations and suggested fixes - -### Phase 3: Save Results - -Create `.lookagain/` directory with: - -1. `aggregate.md` - Human-readable report -2. `aggregate.json` - Machine-readable findings -3. `pass-N.json` - Raw findings from each pass (for debugging) - -### Output Format - -Present the final summary to the user: - -``` -## Iterative Review Complete - -**Passes completed**: N -**Unique issues found**: X - -### Must Fix (N issues) - -| Issue | File | Confidence | Fixed | -| ----- | ---- | ---------- | ----- | -| ... | ... | ...% | ✓/✗ | - -### Should Fix (N issues) - -| Issue | File | Confidence | -| ----- | ---- | ---------- | -| ... | ... | ...% | - -### Suggestions (N issues) - -| Issue | File | Confidence | -| ----- | ---- | ---------- | -| ... | ... | ...% | - -Full report saved to `.lookagain/aggregate.md` -``` - -## Important Rules - -1. **Fresh context per pass**: Always use Task tool for subagents. Never try to "reset" context manually. - -2. **Subagent independence**: Do NOT tell subagents what previous passes found. The value is independent analysis. - -3. **Minimal fixes**: When auto-fixing, change only what's necessary. Don't refactor. - -4. **Structured output**: Ensure subagent returns valid JSON. If parsing fails, log error and continue. - -5. **Respect max-passes**: Never exceed max-passes, even if must_fix issues remain. diff --git a/src/dot-claude-plugin/plugin.json b/src/dot-claude-plugin/plugin.json index d179c53..be3d462 100644 --- a/src/dot-claude-plugin/plugin.json +++ b/src/dot-claude-plugin/plugin.json @@ -1,11 +1,11 @@ { - "name": "lookagain", + "name": "look", "version": "0.1.0", "description": "Sequential code review with fresh agent contexts. Runs multiple independent review passes to catch more issues.", - "author": "HartBrook", + "author": { "name": "HartBrook" }, "repository": "https://github.com/HartBrook/lookagain", "keywords": ["code-review", "quality", "iterative", "multi-pass"], - "commands": ["lookagain"], - "agents": ["lookagain-reviewer"], - "skills": ["lookagain-output-format"] + "commands": ["./commands/again.md"], + "agents": ["./agents/lookagain-reviewer.md"], + "skills": ["./skills/lookagain-output-format"] } diff --git a/src/dot-claude/settings.local.json b/src/dot-claude/settings.local.json index d0df7d3..5d1f3c1 100644 --- a/src/dot-claude/settings.local.json +++ b/src/dot-claude/settings.local.json @@ -1,7 +1,7 @@ { "permissions": { "allow": [ - "Bash(tree:*)" + "Write(.lookagain/**)" ] } }