Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ jobs:
- uses: actions/checkout@v4

- name: Check script syntax
run: bash -n scripts/ai-config-sync.sh && bash -n scripts/ai-memory-link.sh
run: bash -n scripts/ai-config-sync.sh && bash -n scripts/ai-memory-link.sh && bash -n scripts/review-memory-write.sh
33 changes: 5 additions & 28 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,11 @@ This project is in early, working state. Contributions that improve clarity, fix

## Open problems worth solving

### 1. Memory review UX before commit

**The gap:** Claude Code's auto-memory writes MEMORY.md silently during a session. There's no moment where you review what was written before it becomes permanent. You might end up persisting wrong conclusions, misattributed bugs, or stale state.

**The idea:** A `PostToolUse` hook that fires whenever MEMORY.md is written. The hook would show a diff of changes and prompt: approve, edit, or discard.

```json
// .claude/settings.json
{
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "scripts/review-memory-write.sh"
}]
}]
}
}
```

The hook script would:
1. Check if the written file is `MEMORY.md`
2. Show `git diff` of the change
3. Prompt: approve / edit / discard
4. On discard: `git checkout -- MEMORY.md`

This is genuinely unaddressed by any existing tool (as of early 2026). A clean implementation here would be a meaningful contribution.
### 1. Memory review UX before commit ✅ _solved_

**Implemented in:** `scripts/review-memory-write.sh` + `templates/settings.json`

A `PreToolUse` + `PostToolUse` hook pair intercepts every write to MEMORY.md. Before the change becomes permanent the user sees a unified diff and chooses: approve, edit in `$EDITOR`, or discard (restores from backup made by the pre-hook). See the [README](README.md#review-memory-writesh--manual-review-before-changes-stick) for setup instructions.

---

Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,46 @@ Generated files are added to `.gitignore` automatically. See [`docs/CROSS_TOOL_S

---

## review-memory-write.sh — manual review before changes stick

Claude Code can write to MEMORY.md silently during a session. If it saves wrong conclusions, misattributed bugs, or stale state you may not notice until the next session.

`review-memory-write.sh` is a Claude Code hook that intercepts every write to MEMORY.md and shows you a diff before the change becomes permanent.

```
Claude writes MEMORY.md
PostToolUse hook fires
┌─────────────────────────────────┐
│ diff of what changed │
│ │
│ a Approve — keep as-is │
│ e Edit — open in $EDITOR │
│ d Discard — restore previous │
└─────────────────────────────────┘
```

### Setup

```bash
# 1. Put the script on your PATH
cp scripts/review-memory-write.sh ~/.local/bin/
chmod +x ~/.local/bin/review-memory-write.sh

# 2. Install the hook config into your project
mkdir -p .claude
cp /path/to/ai-dev-context/templates/settings.json .claude/settings.json
```

The `templates/settings.json` file registers the hook for both `PreToolUse` (backup) and `PostToolUse` (review). If your project already has a `.claude/settings.json`, merge the `hooks` block in manually.

**Dependencies:** `python3` (for JSON parsing), `diff` (from diffutils — standard on any Linux/macOS install).

---

## Complementary tools

These tools solve adjacent problems and work well alongside this system:
Expand Down
112 changes: 112 additions & 0 deletions scripts/review-memory-write.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# review-memory-write.sh — Prompt for manual review whenever MEMORY.md is written.
#
# Register as both a PreToolUse and PostToolUse hook in .claude/settings.json.
# See templates/settings.json in this repo for the configuration block.
#
# PreToolUse: backs up MEMORY.md before any write so a diff is possible.
# PostToolUse: shows the diff and prompts: (a)pprove / (e)dit / (d)iscard.
#
# For any file that is not a MEMORY.md the script exits immediately.
set -euo pipefail

BACKUP_DIR="${TMPDIR:-/tmp}/ai-memory-review"

# --- Read and parse JSON from stdin ---

INPUT="$(cat)"

# Parse a value from the JSON input by dot-separated key path.
# Requires python3 (standard on macOS and Linux).
parse() {
export PARSE_KEY="$1"
printf '%s\n' "$INPUT" | python3 -c "
import sys, json, os
d = json.load(sys.stdin)
for k in os.environ['PARSE_KEY'].split('.'):
d = d.get(k, '') if isinstance(d, dict) else ''
print(d if isinstance(d, str) else '')
" 2>/dev/null || true
}

hook_event="$(parse 'hook_event_name')"
file_path="$(parse 'tool_input.file_path')"

# --- Only act on MEMORY.md files ---

if [[ "$(basename "$file_path")" != "MEMORY.md" ]]; then
exit 0
fi

mkdir -p "$BACKUP_DIR"
backup_path="${BACKUP_DIR}/$(printf '%s' "$file_path" | tr '/' '_').bak"

# --- PreToolUse: save a backup before the write ---

if [[ "$hook_event" == "PreToolUse" ]]; then
if [[ -f "$file_path" ]]; then
cp "$file_path" "$backup_path"
else
# New file — create empty backup so diff shows everything as added
: > "$backup_path"
fi
exit 0 # must exit 0 to allow the tool to proceed
fi

# --- PostToolUse: show diff and prompt ---

if [[ "$hook_event" != "PostToolUse" ]]; then
exit 0
fi

# Require a TTY for interactive prompting; auto-approve if none is available
if [[ ! -e /dev/tty ]]; then
exit 0
fi

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " MEMORY.md was modified — review before it becomes permanent"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""

if [[ -f "$backup_path" ]]; then
diff -u "$backup_path" "$file_path" && echo "(no changes detected)" || true
else
echo "(no backup found — showing full file)"
cat "$file_path"
fi

echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " a Approve — keep changes as-is"
echo " e Edit — open in \$EDITOR before approving"
echo " d Discard — restore the previous version"
echo ""

exec < /dev/tty
read -r -p "Choice [a/e/d, default a]: " choice

case "${choice,,}" in
e|edit)
"${EDITOR:-vi}" "$file_path" < /dev/tty > /dev/tty
echo " ✓ Memory saved after editing."
rm -f "$backup_path"
;;
d|discard)
if [[ -f "$backup_path" ]]; then
cp "$backup_path" "$file_path"
rm -f "$backup_path"
echo " ✗ Changes discarded — MEMORY.md restored to previous version."
else
echo " ! No backup available — cannot restore. File left as-is."
fi
;;
*)
# Default: approve
echo " ✓ Changes approved."
rm -f "$backup_path"
;;
esac

echo ""
26 changes: 26 additions & 0 deletions templates/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "review-memory-write.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "review-memory-write.sh"
}
]
}
]
}
}