From 2e5e90abf93a37e4719af7465af1c431332af7a8 Mon Sep 17 00:00:00 2001 From: Bayram Annakov Date: Sat, 3 Jan 2026 22:16:50 -0800 Subject: [PATCH 1/3] Add claude-reflect to Workflow Orchestration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Self-learning system for Claude Code that: - Captures corrections during sessions via hooks - Queues learnings with confidence scoring (0.60-0.90) - Processes with human review via /reflect - Syncs to CLAUDE.md and AGENTS.md Commands: /reflect, /skip-reflect, /view-queue Repository: https://github.com/bayramannakov/claude-reflect πŸ€– Generated with [Claude Code](https://claude.ai/claude-code) Co-Authored-By: Claude Opus 4.5 --- README.md | 1 + .../claude-reflect/.claude-plugin/plugin.json | 20 + plugins/claude-reflect/SKILL.md | 62 ++ plugins/claude-reflect/commands/reflect.md | 810 ++++++++++++++++++ .../claude-reflect/commands/skip-reflect.md | 32 + plugins/claude-reflect/commands/view-queue.md | 55 ++ plugins/claude-reflect/hooks/hooks.json | 26 + .../scripts/capture-learning.sh | 163 ++++ .../claude-reflect/scripts/check-learnings.sh | 26 + .../scripts/extract-session-learnings.sh | 46 + .../scripts/extract-tool-rejections.sh | 34 + .../scripts/post-commit-reminder.sh | 43 + 12 files changed, 1318 insertions(+) create mode 100644 plugins/claude-reflect/.claude-plugin/plugin.json create mode 100644 plugins/claude-reflect/SKILL.md create mode 100644 plugins/claude-reflect/commands/reflect.md create mode 100644 plugins/claude-reflect/commands/skip-reflect.md create mode 100644 plugins/claude-reflect/commands/view-queue.md create mode 100644 plugins/claude-reflect/hooks/hooks.json create mode 100755 plugins/claude-reflect/scripts/capture-learning.sh create mode 100755 plugins/claude-reflect/scripts/check-learnings.sh create mode 100755 plugins/claude-reflect/scripts/extract-session-learnings.sh create mode 100755 plugins/claude-reflect/scripts/extract-tool-rejections.sh create mode 100755 plugins/claude-reflect/scripts/post-commit-reminder.sh diff --git a/README.md b/README.md index e4de615..7e240b7 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Install or disable them dynamically with the `/plugin` command β€” enabling you ### Workflow Orchestration - [angelos-symbo](./plugins/angelos-symbo) +- [claude-reflect](./plugins/claude-reflect) - Self-learning system that captures corrections during sessions and syncs to CLAUDE.md with human review - [ceo-quality-controller-agent](./plugins/ceo-quality-controller-agent) - [claude-desktop-extension](./plugins/claude-desktop-extension) - [lyra](./plugins/lyra) diff --git a/plugins/claude-reflect/.claude-plugin/plugin.json b/plugins/claude-reflect/.claude-plugin/plugin.json new file mode 100644 index 0000000..d119f37 --- /dev/null +++ b/plugins/claude-reflect/.claude-plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "name": "claude-reflect", + "version": "1.4.1", + "description": "Self-learning system for Claude Code that captures corrections and updates CLAUDE.md automatically", + "author": { + "name": "Bayram Annakov", + "url": "https://github.com/bayramannakov" + }, + "repository": "https://github.com/bayramannakov/claude-reflect", + "license": "MIT", + "keywords": [ + "claude-code", + "self-learning", + "corrections", + "CLAUDE.md", + "memory", + "learnings" + ], + "hooks": "./hooks/hooks.json" +} diff --git a/plugins/claude-reflect/SKILL.md b/plugins/claude-reflect/SKILL.md new file mode 100644 index 0000000..57da463 --- /dev/null +++ b/plugins/claude-reflect/SKILL.md @@ -0,0 +1,62 @@ +--- +name: claude-reflect +description: Self-learning system that captures corrections during sessions and reminds users to run /reflect to update CLAUDE.md. Use when discussing learnings, corrections, or when the user mentions remembering something for future sessions. +--- + +# Claude Reflect - Self-Learning System + +A two-stage system that helps Claude Code learn from user corrections. + +## How It Works + +**Stage 1: Capture (Automatic)** +Hooks detect correction patterns ("no, use X", "actually...", "use X not Y") and queue them to `~/.claude/learnings-queue.json`. + +**Stage 2: Process (Manual)** +User runs `/reflect` to review and apply queued learnings to CLAUDE.md files. + +## Available Commands + +| Command | Purpose | +|---------|---------| +| `/reflect` | Process queued learnings with human review | +| `/reflect --scan-history` | Scan past sessions for missed learnings | +| `/reflect --dry-run` | Preview changes without applying | +| `/skip-reflect` | Discard all queued learnings | +| `/view-queue` | View pending learnings without processing | + +## When to Remind Users + +Remind users about `/reflect` when: +- They complete a feature or meaningful work unit +- They make corrections you should remember for future sessions +- They explicitly say "remember this" or similar +- Context is about to compact and queue has items + +## Correction Detection Patterns + +High-confidence corrections: +- Tool rejections (user stops an action with guidance) +- "no, use X" / "don't use Y" +- "actually..." / "I meant..." +- "use X not Y" / "X instead of Y" +- "remember:" (explicit marker) + +## CLAUDE.md Destinations + +- `~/.claude/CLAUDE.md` - Global learnings (model names, general patterns) +- `./CLAUDE.md` - Project-specific learnings (conventions, tools, structure) + +## Example Interaction + +``` +User: no, use gpt-5.1 not gpt-5 for reasoning tasks +Claude: Got it, I'll use gpt-5.1 for reasoning tasks. + +[Hook captures this correction to queue] + +User: /reflect +Claude: Found 1 learning queued. "Use gpt-5.1 for reasoning tasks" + Scope: global + Apply to ~/.claude/CLAUDE.md? [y/n] +``` diff --git a/plugins/claude-reflect/commands/reflect.md b/plugins/claude-reflect/commands/reflect.md new file mode 100644 index 0000000..089d845 --- /dev/null +++ b/plugins/claude-reflect/commands/reflect.md @@ -0,0 +1,810 @@ +--- +description: Reflect on session corrections and update CLAUDE.md (with human review) +allowed-tools: Read, Edit, Write, Glob, Bash, Grep, AskUserQuestion +--- + +## Arguments +- `--dry-run`: Preview all changes without prompting or writing. +- `--scan-history`: Scan ALL past sessions for corrections (useful for first-time setup or cold start). +- `--days N`: Limit history scan to last N days (default: 30). Only used with `--scan-history`. +- `--targets`: Show detected AI assistant config files and exit. +- `--review`: Show learnings with stale/decayed entries for review. +- `--dedupe`: Scan CLAUDE.md for similar entries and propose consolidations. + +## Context +- Project CLAUDE.md: @CLAUDE.md +- Global CLAUDE.md: @~/.claude/CLAUDE.md +- Learnings queue: !`cat ~/.claude/learnings-queue.json 2>/dev/null || echo "[]"` +- Current project: !`pwd` + +## Multi-Target Export + +Claude-reflect syncs learnings to CLAUDE.md and AGENTS.md (the emerging cross-tool standard). + +**Supported Targets:** + +| Target | File Path | Format | Notes | +|--------|-----------|--------|-------| +| **Claude Code** | `~/.claude/CLAUDE.md`, `./CLAUDE.md` | Markdown | Always enabled | +| **AGENTS.md** | `./AGENTS.md` | Markdown | Industry standard (Codex, Cursor, Aider, Jules, Zed, Factory) | + +**Detection Logic:** +```bash +# Always enabled +~/.claude/CLAUDE.md +./CLAUDE.md (if exists) + +# Only if file exists +test -f AGENTS.md && echo "AGENTS.md" +``` + +**Note on Confidence & Decay:** +- Confidence scores help prioritize learnings during `/reflect` review +- Decay applies to **queue items only** β€” if a learning sits unprocessed for too long, it's flagged as stale +- Once applied to CLAUDE.md, entries are permanent (edit manually to remove) + +## Your Task + +### Handle --targets Argument + +**If user passed `--targets`:** + +Detect and display all AI assistant config files in the current project: + +```bash +echo "=== Detected AI Assistant Configs ===" +echo "" +echo "βœ“ ~/.claude/CLAUDE.md (Claude Code - always enabled)" +test -f CLAUDE.md && echo "βœ“ ./CLAUDE.md (Project)" || echo "βœ— ./CLAUDE.md (not found)" +test -f AGENTS.md && echo "βœ“ AGENTS.md (Codex, Cursor, Aider, Jules, Zed)" || echo "βœ— AGENTS.md (not found)" +``` + +Then display summary: +``` +═══════════════════════════════════════════════════════════ +DETECTED TARGETS +═══════════════════════════════════════════════════════════ + + βœ“ ~/.claude/CLAUDE.md (Claude Code - always enabled) + βœ“ ./CLAUDE.md (Project) + βœ— AGENTS.md (not found) + +To enable AGENTS.md (syncs to Codex, Cursor, Aider, Jules, Zed, Factory): + touch AGENTS.md + +═══════════════════════════════════════════════════════════ +``` + +Exit after showing targets (don't process learnings). + +### Handle --review Argument + +**If user passed `--review`:** + +Show learnings with their confidence and decay status: + +```bash +cat ~/.claude/learnings-queue.json | jq -r '.[] | "\(.timestamp) | conf:\(.confidence // 0.5) | decay:\(.decay_days // 90)d | \(.message | .[0:60])"' +``` + +Display table of learnings with decay status: +``` +═══════════════════════════════════════════════════════════ +LEARNINGS REVIEW β€” Confidence & Decay Status +═══════════════════════════════════════════════════════════ + +β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ # β”‚ Conf. β”‚ Decay β”‚ Learning β”‚ +β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ 1 β”‚ 0.90 βœ“ β”‚ 120d β”‚ Use gpt-5.1 for reasoning β”‚ +β”‚ 2 β”‚ 0.60 β”‚ 60d ⚠ β”‚ Enable flag X for API calls β”‚ +β”‚ 3 β”‚ 0.40 ⚠ β”‚ 30d ⚠ β”‚ Consider using batch mode β”‚ +β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + +Legend: βœ“ High confidence ⚠ Low confidence/Near decay +═══════════════════════════════════════════════════════════ +``` + +Exit after showing review (don't process learnings). + +### Handle --dedupe Argument + +**If user passed `--dedupe`:** + +Scan existing CLAUDE.md files for similar entries that could be consolidated. + +**1. Read both CLAUDE.md files:** +```bash +cat ~/.claude/CLAUDE.md +cat CLAUDE.md 2>/dev/null +``` + +**2. Extract all bullet points:** +Look for lines starting with `- ` under section headers. + +**3. Analyze for semantic similarity:** +Group entries that: +- Reference the same tool/model/concept +- Give overlapping or redundant advice +- Could be merged without losing information + +**4. Present consolidation proposals:** +``` +═══════════════════════════════════════════════════════════ +CLAUDE.MD DEDUPLICATION SCAN +═══════════════════════════════════════════════════════════ + +Found 2 groups of similar entries: + +Group 1 (Global CLAUDE.md): + Line 45: "- Use gpt-5.1 for complex tasks" + Line 52: "- Prefer gpt-5.1 for reasoning" + β†’ Proposed: "- Use gpt-5.1 for complex reasoning tasks" + +Group 2 (Project CLAUDE.md): + Line 12: "- Always use venv" + Line 28: "- Create virtual environment for Python" + β†’ Proposed: "- Use venv for Python projects" + +No duplicates: 23 entries are unique + +═══════════════════════════════════════════════════════════ +``` + +**5. Use AskUserQuestion:** +```json +{ + "questions": [{ + "question": "Apply deduplication to CLAUDE.md files?", + "header": "Dedupe", + "multiSelect": false, + "options": [ + {"label": "Apply all consolidations", "description": "Merge 2 groups, remove 4 redundant lines"}, + {"label": "Review each group", "description": "Decide per group"}, + {"label": "Cancel", "description": "Keep files unchanged"} + ] + }] +} +``` + +**6. Apply changes:** +- Use Edit tool to replace redundant entries with consolidated versions +- Remove duplicate lines +- Preserve section structure + +Exit after deduplication (don't process queue). + +### First-Run Detection (Per-Project) + +Check if /reflect has been run in THIS project before. Run these commands separately: + +**WARNING**: Do NOT combine these into a single compound command with `$(...)`. Claude Code's bash executor mangles subshell syntax. Run each command individually and manually substitute the result. + +1. Find the project folder name: +```bash +ls ~/.claude/projects/ | grep -i "$(basename "$(pwd)")" +``` + +2. Check if initialized (replace PROJECT_FOLDER with result from step 1): +```bash +test -f ~/.claude/projects/PROJECT_FOLDER/.reflect-initialized && echo "initialized" || echo "first-run" +``` + +**If "first-run" for this project AND user did NOT pass `--scan-history`:** + +Use AskUserQuestion to recommend historical scan: +```json +{ + "questions": [{ + "question": "First time running /reflect in this project. Scan past sessions for learnings?", + "header": "First run", + "multiSelect": false, + "options": [ + {"label": "Yes, scan history (Recommended)", "description": "Find corrections from past sessions in this project"}, + {"label": "No, just process queue", "description": "Only process learnings captured by hooks"} + ] + }] +} +``` + +If user chooses "Yes, scan history", proceed as if `--scan-history` was passed. + +### Step 0: Check Arguments + +**If user passed `--dry-run`:** +- Process all learnings with project filtering +- Show proposed changes with line numbers +- Do NOT prompt for actions, do NOT write +- End with: "Dry run complete. Run /reflect without --dry-run to apply." + +**If user passed `--scan-history`:** +- FIRST: Load the queue (Step 1) - queued items are NEVER skipped +- THEN: Scan ALL historical sessions for this project (Step 0.5) +- Combine queue items + history scan results into working list +- Proceed to Step 3 (Project-Aware Filtering) + +### Step 0.5: Historical Scan (only with --scan-history) + +Scan past sessions for corrections missed by hooks. Useful for: +- First-time /reflect installation (cold start) +- Periodic deep review of past learnings + +**0.5a. Find ALL session files for this project:** + +1. First, list project folders to find the correct path pattern: + ```bash + ls ~/.claude/projects/ | grep -i "$(basename $(pwd))" + ``` + +2. **Handle underscores vs hyphens:** Directory names may use underscores (`darwin_new`) but encoded paths use hyphens (`darwin-new`). If first grep fails, try replacing underscores: + ```bash + # If no match, try with hyphens instead of underscores + ls ~/.claude/projects/ | grep -i "$(basename $(pwd) | tr '_' '-')" + ``` + +3. Then list ALL session files in that folder: + ```bash + ls ~/.claude/projects/[PROJECT_FOLDER]/*.jsonl + ``` + +Note: Project paths have `/` replaced with `-`. For `/Users/bob/code/myapp`, look for `-Users-bob-code-myapp`. + +**IMPORTANT**: With `--scan-history`, process ALL session files (not just recent ones). This includes: +- Main session files (UUID format like `fa5ae539-d170-4fa8-a8d2-bf50b3ec2861.jsonl`) +- Agent files (`agent-*.jsonl`) - these may contain corrections too +- Apply `--days N` filter by checking file modification times if specified + +**0.5b. Extract corrections from session files:** + +Session files are JSONL. Use jq to extract user messages, then grep for patterns. + +**CRITICAL**: Filter out command expansion messages using `isMeta != true`. Command expansions (like /reflect itself) are stored with `isMeta: true` and contain documentation text that would cause false positives. + +**DYNAMIC PATTERN SELECTION**: Before running grep, sample a few user messages to detect the conversation language. If non-English, adapt the patterns accordingly: + +| Language | Example patterns to add | +|----------|------------------------| +| Russian | `Π½Π΅Ρ‚,? ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉ\|Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉ\|Π½Π° самом Π΄Π΅Π»Π΅\|Π·Π°ΠΏΠΎΠΌΠ½ΠΈ:\|Π»ΡƒΡ‡ΡˆΠ΅\|ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡ΠΈΡ‚Π°ΡŽ` | +| Spanish | `no,? usa\|no uses\|en realidad\|recuerda:\|prefiero\|siempre usa` | +| German | `nein,? verwende\|nicht verwenden\|eigentlich\|merke:\|bevorzuge\|immer` | + +Generate appropriate patterns for the detected language and combine with English patterns. + +**Default English patterns:** `remember:`, `no, use`, `don't use`, `actually`, `stop using`, `never use`, `that's wrong`, `I meant`, `use X not Y` + +**0.5b. Extract corrections from session files:** + +For each `.jsonl` file in the project folder, extract user messages that match correction patterns. Use your judgment on the best extraction method - you can use Read, Grep, Bash with jq, or any combination that works. + +**What to extract:** +1. **User messages** with correction patterns (from `type: "user"` entries with `isMeta != true`) +2. **Tool rejections** - look for `toolUseResult` fields containing "user said:" followed by feedback text + - "user said:" followed by empty content means rejection without feedback - skip these + +**Key file structure:** +- Session files: `~/.claude/projects/[PROJECT_FOLDER]/*.jsonl` +- User messages: `{"type": "user", "message": {"content": [{"type": "text", "text": "..."}]}}` +- Tool rejections: `{"toolUseResult": "The user doesn't want to proceed\nuser said:\n[feedback]"}` + +**0.5b-extra. Tool rejections are HIGH confidence:** + +When a user stops a tool and provides feedback, this is a strong correction signal. The feedback appears after "user said:" (may be on the next line in the JSON). + +**CRITICAL: Tool rejections MUST be shown to user:** +- Even if you think they're "task-specific", present them +- The user will decide if they're reusable +- Count how many you found and report: "Found N tool rejections" +- Never say "analyzed N rejections, none reusable" without showing them + +**0.5c. Apply date filter if `--days N` specified:** +- Check file modification time +- Skip files older than N days + +**0.5d. LLM Filter (Inline):** + +For each extracted correction, evaluate whether it's a REUSABLE learning. + +**CRITICAL RULES:** +1. **NEVER filter out `remember:` items** - these are explicit user requests, always present them +2. **NEVER filter out queue items** - the user explicitly captured these via hooks +3. **When in doubt, INCLUDE the learning and let user decide** - don't auto-reject borderline cases +4. **If extraction found matches, SHOW THEM** - never conclude "0 learnings" without presenting raw matches to user +5. **Tool rejections = ALWAYS SHOW** - even "task-specific" ones might have reusable elements + +**REJECT ONLY if clearly:** +- A question (ends with "?") +- Pure task confirmation ("yes", "ok", "done", "looks good") +- Too vague to extract meaning ("fix it", "wrong") + +**ACCEPT if it mentions:** +- Tool/technology/API names or parameters +- Flags, settings, or configuration options ("enable X", "use flag Y") +- Best practices or patterns ("always do X", "don't do Y") +- Model names or versions +- Rate limits, delays, or timing +- File paths or environment setup + +**TRUST USER CORRECTIONS**: For model names, API versions, tool availability, and flag/parameter values - the user has more current knowledge than Claude's training data. Do NOT try to validate whether something "exists" or is "correct". Accept user corrections as authoritative. + +**BORDERLINE β†’ Get context first:** +If a correction seems context-specific (like "please enable that flag"), search for surrounding messages to understand WHAT flag/parameter. Often these ARE reusable learnings about API parameters. + +```bash +# Get context around a correction (find line number, then show surrounding) +grep -n "enable that flag" "$SESSION_FILE" | head -1 +``` + +For each ACCEPTED correction, create: +1. An actionable learning in imperative form (e.g., "Use gpt-5.1 for reasoning tasks" or "Enable flag X for better results") +2. Suggested scope: "global" or "project" +3. Include the actual parameter/value when possible + +**0.5e. Deduplicate:** +- Collect all accepted corrections +- Remove exact duplicates +- For similar corrections, keep the most recent + +**0.5f. Build working list:** +- ADD history scan results to working list (alongside any queue items from Step 1) +- Use the actionable learning you created as the proposed entry +- Use the scope suggestion (global/project) as default +- Mark source as "history-scan" or "tool-rejection" + +**SANITY CHECK before proceeding:** +- Verify queue items from Step 1 are still in working list +- If queue had N items, working list must have at least N items +- If working list is empty but queue was NOT empty β†’ BUG, re-add queue items + +**MANDATORY PRESENTATION RULE:** +If your extraction (grep, search, jq) found ANY matches: +1. You MUST present them to the user - do NOT auto-conclude "0 learnings" +2. Show at least the top 10-15 raw matches for user review +3. For each match, propose: keep as learning OR skip +4. Let the USER decide what's reusable, not the LLM + +**Format for presenting raw matches:** +``` +═══════════════════════════════════════════════════════════ +RAW MATCHES FOUND β€” [N] items need review +═══════════════════════════════════════════════════════════ + +#1 [source: session-scan | tool-rejection] + "[raw text from extraction]" + β†’ Proposed: [actionable learning] | Scope: [global/project] + +#2 ... +═══════════════════════════════════════════════════════════ +``` + +Then use AskUserQuestion to let user select which to keep. + +**NEVER conclude "0 learnings found" if:** +- Grep/search returned >0 matches +- Tool rejections were found but not shown +- You filtered items without user review + +- Continue to Step 3 (Project-Aware Filtering) with COMBINED list (queue + history) + +### Step 1: Load and Validate +- Read the queue from `~/.claude/learnings-queue.json` +- Add all queue items to the working list (mark source as "queued") +- **IMPORTANT**: Even if queue is empty, continue if `--scan-history` will add items +- Only exit early if: queue is empty AND not doing history scan AND user declines manual capture + +### Step 2: Session Reflection (Enhanced with History Analysis) + +**Note**: This step is for analyzing the CURRENT session only (when NOT using `--scan-history`). +If `--scan-history` was passed, skip to Step 3 with results from Step 0.5. + +Analyze the current session for corrections missed by real-time hooks: + +**2a. Find current session file:** + +List session files for this project (most recent first): +```bash +ls -lt ~/.claude/projects/ | grep -i "$(basename $(pwd))" +``` + +Then list files in that folder and pick the most recent non-agent file: +```bash +ls -lt ~/.claude/projects/[PROJECT_FOLDER]/*.jsonl | head -5 +``` + +Agent files (`agent-*.jsonl`) are sub-conversations; focus on main session files for current session analysis. + +**2b. Extract tool rejections (HIGH confidence corrections):** + +Search the current session file for `toolUseResult` fields containing "user said:" followed by feedback. These are high-confidence corrections. + +- "user said:" followed by empty content = rejection without feedback, skip these +- Extract the feedback text after "user said:" for processing + +**2c. Extract user messages with correction patterns:** + +Search the current session file for user messages matching correction patterns. Use the same patterns from Step 0.5b. Remember: +- Filter out `isMeta: true` entries (command expansions like /reflect itself) +- Apply language-specific patterns if conversation is non-English + +**2d. Also reflect on conversation context:** +- Were there any corrections or patterns not explicitly queued? +- Model names, API patterns, tool usage mistakes, project conventions? +- Implicit corrections (e.g., "Actually, the API returns...") + +**2e. LLM Filter (Inline):** +If there are extracted corrections from 2b or 2c, evaluate each using the same criteria as Step 0.5d: +- REJECT questions, one-time tasks, context-specific items, vague feedback +- ACCEPT tool recommendations, patterns, conventions, model corrections +- Create actionable learnings in imperative form with scope suggestions + +**2f. Add findings to working list:** +For each ACCEPTED learning: +- Use the actionable learning you created as the proposed entry +- Use the scope suggestion (global/project) as default +- Add to working list alongside queued items +- Mark source type: + - "queued" β€” from hooks/explicit remember: + - "session-scan" β€” from message pattern matching + - "tool-rejection" β€” from tool rejections (HIGH confidence) + +### Step 3: Project-Aware Filtering + +Get current project path. For each queue item, compare `item.project` with current project: + +**CASE A: Same project** +- Show normally +- Offer: [a]pprove | [e]dit | [s]kip +- If approve, ask scope: [p]roject | [g]lobal | [b]oth + +**CASE B: Different project, looks GLOBAL** +(message contains: gpt-*, claude-*, model names, general patterns like "always/never") +- Show with warning: "⚠️ FROM DIFFERENT PROJECT" +- Show: "Captured in: [original-project]" +- Offer: [g]lobal | [s]kip (NOT project - wrong context) + +**CASE C: Different project, looks PROJECT-SPECIFIC** +(message contains: specific DB names, file paths, project-specific tools) +- Auto-skip with note: "Skipping project-specific learning from [other-project]" +- Offer: [f]orce to add to global anyway + +**Heuristics:** +- `gpt-[0-9]` or `claude-` β†’ GLOBAL (model name) +- `always|never|don't` + generic verb β†’ GLOBAL (general rule) +- Specific tool/DB/service names β†’ PROJECT-SPECIFIC +- File paths β†’ PROJECT-SPECIFIC + +### Step 3.5: Semantic Deduplication (Within Queue) + +Before checking against CLAUDE.md, consolidate similar learnings within the current batch. + +**3.5a. Group by semantic similarity:** + +Analyze all learnings in the working list. Look for entries that: +- Reference the same tool, model, or concept +- Give similar advice (even with different wording) +- Could be consolidated into a single, clearer entry + +**Example - Before consolidation:** +``` +1. "Use gpt-5.1 for complex tasks" +2. "Prefer gpt-5.1 over gpt-5 for reasoning" +3. "gpt-5.1 is better for hard problems" +``` + +**Example - After consolidation:** +``` +1. "Use gpt-5.1 for complex reasoning (replaces gpt-5)" +``` + +**3.5b. Present consolidation proposals:** + +If similar learnings are detected, show: +``` +═══════════════════════════════════════════════════════════ +SIMILAR LEARNINGS DETECTED +═══════════════════════════════════════════════════════════ + +These 3 learnings appear related: + #2: "Use gpt-5.1 for complex tasks" + #5: "Prefer gpt-5.1 over gpt-5 for reasoning" + #7: "gpt-5.1 is better for hard problems" + +Proposed consolidation: + β†’ "Use gpt-5.1 for complex reasoning tasks (replaces gpt-5)" + +═══════════════════════════════════════════════════════════ +``` + +**3.5c. Use AskUserQuestion for consolidation:** + +```json +{ + "questions": [{ + "question": "Consolidate these 3 similar learnings into one?", + "header": "Dedupe", + "multiSelect": false, + "options": [ + {"label": "Yes, consolidate", "description": "Merge into: 'Use gpt-5.1 for complex reasoning tasks'"}, + {"label": "Keep separate", "description": "Add all 3 as individual entries"}, + {"label": "Edit consolidation", "description": "Let me modify the merged text"} + ] + }] +} +``` + +**3.5d. Consolidation rules:** +- Keep highest confidence score from the group +- Combine decay_days (use longest) +- Mark source as "consolidated" +- If user chooses "Edit", allow them to provide custom text + +**3.5e. Skip if no duplicates:** +- If all learnings are semantically distinct, proceed to Step 4 +- Only show consolidation UI when similar entries are detected + +### Step 4: Duplicate Detection with Line Numbers + +For each learning kept after filtering, search BOTH CLAUDE.md files: + +```bash +grep -n -i "keyword" ~/.claude/CLAUDE.md +grep -n -i "keyword" CLAUDE.md +``` + +If duplicate found: +- Show: "⚠️ SIMILAR in [global/project] CLAUDE.md: Line [N]: [content]" +- Offer: [m]erge | [r]eplace | [a]dd anyway | [s]kip + +### Step 5: Present Summary and Get User Decision + +**5a. Display condensed summary table:** + +Show all learnings in a compact table format: + +``` +════════════════════════════════════════════════════════════ +LEARNINGS SUMMARY β€” [N] items found +════════════════════════════════════════════════════════════ + +β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ # β”‚ Learning β”‚ Scope β”‚ Status β”‚ +β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ 1 β”‚ Use DB for persistent storage β”‚ project β”‚ βœ“ new β”‚ +β”‚ 2 β”‚ Backoff on actual errors only β”‚ global β”‚ βœ“ new β”‚ +β”‚ ...β”‚ ... β”‚ ... β”‚ ... β”‚ +β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + +Destinations: [N] β†’ Global, [M] β†’ Project +Duplicates: [K] items will be merged with existing entries +``` + +**5b. Use AskUserQuestion for strategy:** + +Use the AskUserQuestion tool: +```json +{ + "questions": [{ + "question": "How would you like to process these [N] learnings?", + "header": "Action", + "multiSelect": false, + "options": [ + {"label": "Apply all (Recommended)", "description": "Add [X] new entries, merge [K] duplicates with recommended scopes"}, + {"label": "Select which to apply", "description": "Choose specific learnings from grouped lists"}, + {"label": "Review details first", "description": "Show full details for each learning before deciding"}, + {"label": "Skip all", "description": "Don't apply any learnings, clear the queue"} + ] + }] +} +``` + +**5c. Handle user selection:** + +- **"Apply all"** β†’ Proceed to Step 6 (Final Confirmation) +- **"Select which to apply"** β†’ Go to Step 5.1 (Selection Mode) +- **"Review details first"** β†’ Show full learning cards (format below), then return to 5b +- **"Skip all"** β†’ Go to Step 8 (Clear Queue) + +**Full learning card format (for "Review details first"):** +``` +════════════════════════════════════════════════════════════ +LEARNING [N] of [TOTAL] β€” [source: queued/session-scan/tool-rejection] +════════════════════════════════════════════════════════════ +Original message: + "[the user's original text]" + +Proposed addition: +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ ## [Section Name] β”‚ +β”‚ - [Exact bullet point that will be added] β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + +Duplicate check: + βœ“ None found + OR + ⚠️ SIMILAR in [global/project] CLAUDE.md: + Line [N]: "[existing content]" +════════════════════════════════════════════════════════════ +``` + +### Step 5.1: Selection Mode (if user chose "Select which to apply") + +Group learnings by destination and use AskUserQuestion with multiSelect. + +**Rules:** +- Split into multiple questions if >4 items per destination +- Use short labels: "#{N} {short_title}" (max 20 chars) +- Use descriptions for full learning text (max 80 chars) + +**Example for GLOBAL learnings:** +```json +{ + "questions": [ + { + "question": "Select GLOBAL learnings to apply:", + "header": "Global", + "multiSelect": true, + "options": [ + {"label": "#2 Backoff errors", "description": "Implement backoff only on actual errors, not artificial delays"}, + {"label": "#3 DB cache", "description": "Use local database cache to minimize data fetching"}, + {"label": "#4 Batch+delays", "description": "Use batching with stochastic delays for API rate limits"}, + {"label": "#5 Use venv", "description": "Always use virtual environments for Python projects"} + ] + } + ] +} +``` + +**If >4 global items:** Add second question with header "Global+" + +**Example for PROJECT learnings:** +```json +{ + "questions": [ + { + "question": "Select PROJECT learnings to apply:", + "header": "Project", + "multiSelect": true, + "options": [ + {"label": "#1 DB storage", "description": "Use database for persistent tracking data"}, + {"label": "#6 DB ports", "description": "Assign unique ports per database instance"} + ] + } + ] +} +``` + +**Selection rules:** +- Items NOT selected will be skipped +- Continue to Step 6 with selected items only + +### Step 6: Final Confirmation + +**6a. Show summary of changes:** +``` +════════════════════════════════════════════════════════════ +SUMMARY: [N] changes ready to apply +════════════════════════════════════════════════════════════ + +Project CLAUDE.md ([path]): + Line [N]: UPDATE "[old]" β†’ "[new]" + After line [N]: ADD "[new entry]" + +Global CLAUDE.md (~/.claude/CLAUDE.md): + Line [N]: REPLACE "[old]" β†’ "[new]" + After line [N]: ADD "[new entry]" + +Skipped: [N] learnings (including [M] from other projects) +════════════════════════════════════════════════════════════ +``` + +**6b. Use AskUserQuestion for confirmation:** +```json +{ + "questions": [{ + "question": "Apply [N] learnings to CLAUDE.md files?", + "header": "Confirm", + "multiSelect": false, + "options": [ + {"label": "Yes, apply all", "description": "[X] to Global, [Y] to Project CLAUDE.md"}, + {"label": "Go back", "description": "Return to selection to adjust"}, + {"label": "Cancel", "description": "Don't apply anything, keep queue"} + ] + }] +} +``` + +**6c. Handle response:** +- **"Yes, apply all"** β†’ Proceed to Step 7 +- **"Go back"** β†’ Return to Step 5b +- **"Cancel"** β†’ Exit without changes (keep queue intact) + +### Step 7: Apply Changes + +Only after final confirmation: + +**7a. Apply to CLAUDE.md (Primary Targets):** +1. Read current CLAUDE.md files +2. Use Edit tool with precise old_string from detected line numbers +3. For new entries, add after the relevant section header + +**7b. Apply to AGENTS.md (if exists):** + +Check if AGENTS.md exists: +```bash +test -f AGENTS.md && echo "AGENTS.md found" +``` + +If AGENTS.md exists, apply the SAME learnings using this format: + +```markdown +## Claude-Reflect Learnings + + + +### Model Preferences +- Use gpt-5.1 for reasoning tasks + +### Tool Usage +- Use local database cache to minimize API calls + + +``` + +**Update Strategy:** +- Look for existing ``) +- If not found: APPEND section at the end of the file +- Always preserve user's existing content outside the marked section + +### Step 8: Clear Queue + +```bash +echo "[]" > ~/.claude/learnings-queue.json +``` + +### Step 9: Confirm + +``` +════════════════════════════════════════════════════════════ +DONE: Applied [N] learnings +════════════════════════════════════════════════════════════ + βœ“ ~/.claude/CLAUDE.md [N] entries + βœ“ ./CLAUDE.md [N] entries + βœ“ AGENTS.md [N] entries (if exists) + + Skipped: [N] +════════════════════════════════════════════════════════════ +``` + +### Step 10: Mark Initialized (Per-Project) + +Create marker file for THIS project so first-run detection won't trigger again. +Use the PROJECT_FOLDER you found in First-Run Detection: + +```bash +touch ~/.claude/projects/PROJECT_FOLDER/.reflect-initialized +``` + +Replace PROJECT_FOLDER with the actual folder name (e.g., `-Users-bob-myproject`). + +## Formatting Rules + +- **Bullets, not prose**: Keep entries as single bullet points +- **Actionable**: "Use X for Y" not "X is better than Y" +- **Concise**: Max 2 lines per entry +- **Examples when helpful**: `(e.g., gpt-5.2 not gpt-5.1)` + +## Section Headers + +Use these standard headers: +- `## LLM Model Recommendations` β€” model names, versions +- `## Tool Usage` β€” MCP, APIs, which tool for what +- `## Project Conventions` β€” coding style, patterns +- `## Common Errors to Avoid` β€” gotchas, mistakes +- `## Environment Setup` β€” venv, configs, paths + +## Size Check + +If CLAUDE.md exceeds 150 lines, warn: +``` +Note: CLAUDE.md is [N] lines. Consider consolidating entries. +``` diff --git a/plugins/claude-reflect/commands/skip-reflect.md b/plugins/claude-reflect/commands/skip-reflect.md new file mode 100644 index 0000000..1c068f2 --- /dev/null +++ b/plugins/claude-reflect/commands/skip-reflect.md @@ -0,0 +1,32 @@ +--- +description: Discard queued learnings without processing +allowed-tools: Bash +--- + +## Context +- Queue count: !`jq 'length' ~/.claude/learnings-queue.json 2>/dev/null || echo 0` + +## Your Task + +1. If queue is empty: + - Output: "Queue is already empty. Nothing to skip." + - Exit + +2. If queue has items: + - Show: "You are about to discard [count] learning(s). These will be lost:" + - List each queued item briefly (type + first 50 chars of message) + - Ask: "Are you sure? [y/n]" + +3. If user confirms (y/yes): + - Clear the queue: + ```bash + echo "[]" > ~/.claude/learnings-queue.json + ``` + - Output: "Discarded [count] learnings. Queue cleared." + +4. If user declines (n/no): + - Output: "Aborted. Run /reflect to process learnings instead." + +## Note +This is an escape hatch for when auto-detection captures false positives +or learnings aren't worth saving. Use sparingly. diff --git a/plugins/claude-reflect/commands/view-queue.md b/plugins/claude-reflect/commands/view-queue.md new file mode 100644 index 0000000..78e47a2 --- /dev/null +++ b/plugins/claude-reflect/commands/view-queue.md @@ -0,0 +1,55 @@ +--- +description: View the learnings queue without processing +allowed-tools: Bash +--- + +## Context +- Queue file: `~/.claude/learnings-queue.json` + +## Your Task + +Display the current learnings queue in a readable format: + +``` +════════════════════════════════════════════════════════════ +LEARNINGS QUEUE: [N] items +════════════════════════════════════════════════════════════ + +1. [type] "first 80 chars of message..." + Patterns: [patterns matched] + Project: [project path] + Time: [timestamp] + +2. [type] "first 80 chars of message..." + ... + +════════════════════════════════════════════════════════════ +Commands: + /reflect - Process and save learnings + /skip-reflect - Discard all learnings +════════════════════════════════════════════════════════════ +``` + +If queue is empty: +``` +════════════════════════════════════════════════════════════ +LEARNINGS QUEUE: Empty +════════════════════════════════════════════════════════════ +No learnings queued. Use "remember: " to add items, +or corrections will be auto-detected. Run /reflect to process. +════════════════════════════════════════════════════════════ +``` + +## Implementation + +Read and format the queue: +```bash +cat ~/.claude/learnings-queue.json 2>/dev/null || echo "[]" +``` + +Parse each item and display: +- `type`: "explicit" or "auto" +- `message`: truncated to 80 chars with "..." if longer +- `patterns`: what triggered detection +- `project`: where it was captured +- `timestamp`: when it was captured diff --git a/plugins/claude-reflect/hooks/hooks.json b/plugins/claude-reflect/hooks/hooks.json new file mode 100644 index 0000000..3989ddc --- /dev/null +++ b/plugins/claude-reflect/hooks/hooks.json @@ -0,0 +1,26 @@ +{ + "hooks": { + "PreCompact": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-learnings.sh" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/scripts/post-commit-reminder.sh" + } + ] + } + ] + } +} diff --git a/plugins/claude-reflect/scripts/capture-learning.sh b/plugins/claude-reflect/scripts/capture-learning.sh new file mode 100755 index 0000000..7485780 --- /dev/null +++ b/plugins/claude-reflect/scripts/capture-learning.sh @@ -0,0 +1,163 @@ +#!/bin/bash +# V3: Detects correction patterns, positive patterns, OR explicit markers +# Features: confidence scoring, positive pattern capture, decay metadata +# Used by UserPromptSubmit hook + +QUEUE_FILE="$HOME/.claude/learnings-queue.json" + +# Read JSON from stdin +INPUT="$(cat -)" +[ -z "$INPUT" ] && exit 0 + +# Extract prompt from JSON - handle different possible field names +PROMPT="$(echo "$INPUT" | jq -r '.prompt // .message // .text // empty' 2>/dev/null)" +[ -z "$PROMPT" ] && exit 0 + +# Get current project path +PROJECT="$(pwd)" + +# Initialize queue if doesn't exist +[ ! -f "$QUEUE_FILE" ] && echo "[]" > "$QUEUE_FILE" + +TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +MATCHED_PATTERNS="" +TYPE="" +CONFIDENCE=0.0 +SENTIMENT="correction" # "correction" or "positive" +DECAY_DAYS=90 # Default decay period + +# Check for explicit "remember:" +if echo "$PROMPT" | grep -qi "remember:"; then + TYPE="explicit" + MATCHED_PATTERNS="remember:" + CONFIDENCE=0.90 + DECAY_DAYS=120 + +# Check for POSITIVE patterns (new in v3) +elif echo "$PROMPT" | grep -qiE "perfect!|exactly right|that's exactly|that's what I wanted|great approach|keep doing this|love it|excellent|nailed it"; then + TYPE="positive" + SENTIMENT="positive" + CONFIDENCE=0.70 + DECAY_DAYS=90 + + if echo "$PROMPT" | grep -qiE "perfect!|exactly right|that's exactly"; then + MATCHED_PATTERNS="$MATCHED_PATTERNS perfect" + fi + if echo "$PROMPT" | grep -qiE "that's what I wanted|great approach"; then + MATCHED_PATTERNS="$MATCHED_PATTERNS great-approach" + fi + if echo "$PROMPT" | grep -qiE "keep doing this|love it|excellent|nailed it"; then + MATCHED_PATTERNS="$MATCHED_PATTERNS keep-doing" + fi + +else + # Check for correction patterns (conservative set to minimize false positives) + # These patterns strongly indicate a user correction + # Confidence: 0.80 for strong patterns, 0.60 for medium patterns + + PATTERN_COUNT=0 + + # Pattern: "no, use X" / "no use X" (strong) + if echo "$PROMPT" | grep -qiE "no[,. ]+use"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS no,use" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Pattern: "don't use" (strong) + if echo "$PROMPT" | grep -qiE "don't use|do not use"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS don't-use" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Pattern: "stop using" / "never use" (strong) + if echo "$PROMPT" | grep -qiE "stop using|never use"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS stop/never-use" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Pattern: "that's wrong" / "that's incorrect" (strong) + if echo "$PROMPT" | grep -qiE "that's (wrong|incorrect)|that is (wrong|incorrect)"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS that's-wrong" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Pattern: "not right" / "not correct" (medium) + if echo "$PROMPT" | grep -qiE "not right|not correct"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS not-right" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Pattern: "actually," (medium - context dependent) + if echo "$PROMPT" | grep -qiE "^actually[,. ]|[.!?] actually[,. ]"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS actually" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Pattern: "I meant" / "I said" (strong) + if echo "$PROMPT" | grep -qiE "I meant|I said"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS I-meant/said" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Pattern: "I told you" / "I already told" (strong - repeated correction) + if echo "$PROMPT" | grep -qiE "I told you|I already told"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS I-told-you" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + CONFIDENCE=0.85 # Higher confidence for repeated corrections + fi + + # Pattern: "you should use" / "you need to use" (medium) + if echo "$PROMPT" | grep -qiE "you (should|need to|must) use"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS you-should-use" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Pattern: "use X not Y" / "not X, use Y" (strong) + if echo "$PROMPT" | grep -qiE "use .+ not|not .+, use"; then + TYPE="auto" + MATCHED_PATTERNS="$MATCHED_PATTERNS use-X-not-Y" + PATTERN_COUNT=$((PATTERN_COUNT + 1)) + fi + + # Set confidence based on pattern count (if not already set) + if [ "$TYPE" = "auto" ] && [ "$CONFIDENCE" = "0.0" ]; then + if [ "$PATTERN_COUNT" -ge 3 ]; then + CONFIDENCE=0.85 + DECAY_DAYS=120 + elif [ "$PATTERN_COUNT" -ge 2 ]; then + CONFIDENCE=0.75 + DECAY_DAYS=90 + else + CONFIDENCE=0.60 + DECAY_DAYS=60 + fi + fi +fi + +# If we found something, queue it +if [ -n "$TYPE" ]; then + # Trim leading space from matched patterns + MATCHED_PATTERNS=$(echo "$MATCHED_PATTERNS" | sed 's/^ *//') + + jq --arg type "$TYPE" \ + --arg msg "$PROMPT" \ + --arg ts "$TIMESTAMP" \ + --arg proj "$PROJECT" \ + --arg patterns "$MATCHED_PATTERNS" \ + --arg confidence "$CONFIDENCE" \ + --arg sentiment "$SENTIMENT" \ + --arg decay "$DECAY_DAYS" \ + '. += [{"type": $type, "message": $msg, "timestamp": $ts, "project": $proj, "patterns": $patterns, "confidence": ($confidence | tonumber), "sentiment": $sentiment, "decay_days": ($decay | tonumber)}]' \ + "$QUEUE_FILE" > "$QUEUE_FILE.tmp" 2>/dev/null && mv "$QUEUE_FILE.tmp" "$QUEUE_FILE" +fi + +exit 0 diff --git a/plugins/claude-reflect/scripts/check-learnings.sh b/plugins/claude-reflect/scripts/check-learnings.sh new file mode 100755 index 0000000..00d49e8 --- /dev/null +++ b/plugins/claude-reflect/scripts/check-learnings.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# V3: INFORMS about learnings and saves backup (does NOT block) +# Used by PreCompact hook + +QUEUE_FILE="$HOME/.claude/learnings-queue.json" +BACKUP_DIR="$HOME/.claude/learnings-backups" + +if [ -f "$QUEUE_FILE" ]; then + COUNT=$(jq 'length' "$QUEUE_FILE" 2>/dev/null || echo 0) + if [ "$COUNT" -gt 0 ]; then + # Create backup directory if needed + mkdir -p "$BACKUP_DIR" + + # Save learnings to timestamped backup file + BACKUP_FILE="$BACKUP_DIR/pre-compact-$(date +%Y%m%d-%H%M%S).json" + cp "$QUEUE_FILE" "$BACKUP_FILE" + + # Output informational message (no blocking) + echo "" + echo "Note: $COUNT learning(s) backed up to $BACKUP_FILE" + echo "Run /reflect in new session to process." + echo "" + fi +fi + +exit 0 diff --git a/plugins/claude-reflect/scripts/extract-session-learnings.sh b/plugins/claude-reflect/scripts/extract-session-learnings.sh new file mode 100755 index 0000000..3f4faa6 --- /dev/null +++ b/plugins/claude-reflect/scripts/extract-session-learnings.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Extract user messages from a Claude Code session file +# Usage: extract-session-learnings.sh [--corrections-only] + +SESSION_FILE="$1" +CORRECTIONS_ONLY="$2" + +if [ -z "$SESSION_FILE" ]; then + echo "Usage: extract-session-learnings.sh [--corrections-only]" + exit 1 +fi + +if [ ! -f "$SESSION_FILE" ]; then + echo "Error: Session file not found: $SESSION_FILE" + exit 1 +fi + +# Extract user messages, excluding meta/system messages and tool results +# Filter: type=user, not isMeta, message.content is a simple string (not array/object) +extract_messages() { + jq -r ' + select(.type=="user") | + select(.isMeta != true) | + select(.message.content | type == "string") | + .message.content + ' "$SESSION_FILE" 2>/dev/null \ + | grep -v '^$' \ + | grep -v '^<' \ + | grep -v '^\[' \ + | grep -v '^{' \ + | grep -v 'tool_result' \ + | grep -v 'tool_use_id' \ + | grep -v ' +# +# Tool rejections contain high-quality corrections because the user +# explicitly stopped a tool and provided guidance. + +SESSION_FILE="$1" + +if [ -z "$SESSION_FILE" ]; then + echo "Usage: extract-tool-rejections.sh " + exit 1 +fi + +if [ ! -f "$SESSION_FILE" ]; then + echo "Error: Session file not found: $SESSION_FILE" + exit 1 +fi + +# Extract the user's correction from tool rejections +# Pattern: "The user doesn't want to proceed... the user said:\n[CORRECTION]" +# The correction is on the line AFTER "the user said:" +jq -r ' + select(.type=="user") | + select(.message.content | type == "array") | + .message.content[] | + select(.type=="tool_result") | + select(.is_error==true) | + select(.content | type == "string") | + select(.content | contains("The user doesn'\''t want to proceed")) | + .content +' "$SESSION_FILE" 2>/dev/null \ + | awk '/the user said:/{getline; print}' \ + | grep -v '^$' diff --git a/plugins/claude-reflect/scripts/post-commit-reminder.sh b/plugins/claude-reflect/scripts/post-commit-reminder.sh new file mode 100755 index 0000000..1298708 --- /dev/null +++ b/plugins/claude-reflect/scripts/post-commit-reminder.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Checks if a git commit just happened and reminds about /reflect +# Used by PostToolUse hook for Bash tool + +QUEUE_FILE="$HOME/.claude/learnings-queue.json" + +# Read JSON from stdin into variable +INPUT="$(cat -)" + +# Exit if no input +[ -z "$INPUT" ] && exit 0 + +# Extract the command that was executed +COMMAND="$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)" + +# Exit if no command +[ -z "$COMMAND" ] && exit 0 + +# Check if it was a git commit command (not amend, not git commit without actual commit) +if [[ "$COMMAND" == *"git commit"* ]]; then + # Build reminder message + MSG="Git commit detected!" + + # Check queue + if [ -f "$QUEUE_FILE" ]; then + COUNT=$(jq 'length' "$QUEUE_FILE" 2>/dev/null || echo 0) + if [ "$COUNT" -gt 0 ]; then + MSG="$MSG You have $COUNT queued learning(s)." + fi + fi + + MSG="$MSG Feature complete? Run /reflect to process learnings." + + # Output proper JSON for hook response + jq -n --arg msg "$MSG" '{ + "hookSpecificOutput": { + "hookEventName": "PostToolUse", + "additionalContext": $msg + } + }' +fi + +exit 0 From 50513676a11d4aadf9c4866ecb0e2f884d79a460 Mon Sep 17 00:00:00 2001 From: Bayram Annakov Date: Sat, 3 Jan 2026 22:24:29 -0800 Subject: [PATCH 2/3] Add MIT LICENSE file --- plugins/claude-reflect/LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 plugins/claude-reflect/LICENSE diff --git a/plugins/claude-reflect/LICENSE b/plugins/claude-reflect/LICENSE new file mode 100644 index 0000000..737ac7e --- /dev/null +++ b/plugins/claude-reflect/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Bayram Annakov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From aa1755a24339ef2aae178da75cb285567a4a5bbf Mon Sep 17 00:00:00 2001 From: Bayram Annakov Date: Sat, 3 Jan 2026 22:29:26 -0800 Subject: [PATCH 3/3] Fix: jq filter for session content array, exclude --amend from reminder, remove duplicate heading --- plugins/claude-reflect/commands/reflect.md | 2 -- .../scripts/extract-session-learnings.sh | 10 +++++----- plugins/claude-reflect/scripts/post-commit-reminder.sh | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/plugins/claude-reflect/commands/reflect.md b/plugins/claude-reflect/commands/reflect.md index 089d845..7779ef7 100644 --- a/plugins/claude-reflect/commands/reflect.md +++ b/plugins/claude-reflect/commands/reflect.md @@ -272,8 +272,6 @@ Generate appropriate patterns for the detected language and combine with English **Default English patterns:** `remember:`, `no, use`, `don't use`, `actually`, `stop using`, `never use`, `that's wrong`, `I meant`, `use X not Y` -**0.5b. Extract corrections from session files:** - For each `.jsonl` file in the project folder, extract user messages that match correction patterns. Use your judgment on the best extraction method - you can use Read, Grep, Bash with jq, or any combination that works. **What to extract:** diff --git a/plugins/claude-reflect/scripts/extract-session-learnings.sh b/plugins/claude-reflect/scripts/extract-session-learnings.sh index 3f4faa6..a1f663c 100755 --- a/plugins/claude-reflect/scripts/extract-session-learnings.sh +++ b/plugins/claude-reflect/scripts/extract-session-learnings.sh @@ -16,13 +16,13 @@ if [ ! -f "$SESSION_FILE" ]; then fi # Extract user messages, excluding meta/system messages and tool results -# Filter: type=user, not isMeta, message.content is a simple string (not array/object) +# Filter: type=user, not isMeta, extract text from content array extract_messages() { jq -r ' - select(.type=="user") | - select(.isMeta != true) | - select(.message.content | type == "string") | - .message.content + select(.type=="user" and .isMeta != true) | + .message.content[]? | + select(.type=="text") | + .text ' "$SESSION_FILE" 2>/dev/null \ | grep -v '^$' \ | grep -v '^<' \ diff --git a/plugins/claude-reflect/scripts/post-commit-reminder.sh b/plugins/claude-reflect/scripts/post-commit-reminder.sh index 1298708..c265728 100755 --- a/plugins/claude-reflect/scripts/post-commit-reminder.sh +++ b/plugins/claude-reflect/scripts/post-commit-reminder.sh @@ -16,8 +16,8 @@ COMMAND="$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)" # Exit if no command [ -z "$COMMAND" ] && exit 0 -# Check if it was a git commit command (not amend, not git commit without actual commit) -if [[ "$COMMAND" == *"git commit"* ]]; then +# Check if it was a git commit command (not amend) +if [[ "$COMMAND" == *"git commit"* && "$COMMAND" != *"--amend"* ]]; then # Build reminder message MSG="Git commit detected!"