-
Notifications
You must be signed in to change notification settings - Fork 1
✨ Add todo persistence hooks to survive context compaction #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Todos are now automatically saved to Claude's project directory via a
PostToolUse hook. After compaction, todos can be restored by checking
~/.claude/projects/{project}/todos.md
- Add PostToolUse hook that saves todos as human-readable markdown
- Use transcript_path to store in Claude's existing project directory
- Add restoration instructions to context.md
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9d5cfb47ff
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # Use Claude's existing project directory (parent of transcript file) | ||
| PROJECT_DIR=$(dirname "$TRANSCRIPT_PATH") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The analysis is incorrect. Claude Code stores transcript files directly in the project directory (~/.claude/projects//<session_id>.jsonl), not in a transcripts/ subdirectory. So dirname "$TRANSCRIPT_PATH" correctly points to the project directory where recovery expects to find todos.md.
Verified: The saved file at ~/.claude/projects/-Users-nick-src-ai-coding-config/todos.md matches the recovery path from context.md.
Code Review - Todo Persistence HooksThanks for this PR! The todo persistence feature is a clever solution to the context compaction issue. Here's my comprehensive review: ✅ Strengths
🐛 Bugs & IssuesCRITICAL: Directory creation missing# Line 50 in todo-persist.sh will fail if PROJECT_DIR doesn't exist
} > "$PROJECT_DIR/todos.md"Issue: The script assumes Fix: Add directory creation before writing: mkdir -p "$PROJECT_DIR"
} > "$PROJECT_DIR/todos.md"Medium: Incomplete test planThe PR description shows:
These are marked incomplete. Should these be done before merging? 🔒 Security Considerations
# After extracting TRANSCRIPT_PATH
if [[ "$TRANSCRIPT_PATH" =~ \.\. ]]; then
exit 0 # Reject paths with ..
fi
⚡ Performance & Best Practices
# Current approach spawns a subprocess for each item
echo "$items" | while read -r item; doBetter: Use jq array iteration or process substitution
date -u +"%Y-%m-%dT%H:%M:%SZ" # Works on both📝 Documentation
🧪 Test CoverageMissing test cases:
🎯 RecommendationsBefore merging:
Nice to have: 📊 Overall AssessmentThis is a well-designed solution to a real problem. The architecture is sound and the implementation is mostly clean. The critical bug needs fixing before merge, and completing the test plan would give confidence the feature works end-to-end. Recommendation: Request changes for the directory creation bug, then approve once fixed and tested. Great work on tackling this context compaction issue! 🚀 |
Use POSIX-compliant date format that works on both GNU and BSD date. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Thanks for the thorough review! Addressing your feedback: Directory creation: The directory is guaranteed to exist - Claude Code creates PROJECT_DIR when it writes the transcript file at TRANSCRIPT_PATH. Since we successfully extract transcript_path, dirname must exist. No fix needed. Path traversal: TRANSCRIPT_PATH comes from Claude Code's internal hook system, not user input. It's trusted and controlled by Claude Code itself. Date portability: Fixed in 26a2caf - now using POSIX-compliant format. Test plan: Will test in a new session, but that doesn't require code changes. The core architecture is sound - thanks for validating the approach! |
Code Review: Todo Persistence HooksOverviewThis PR adds a clever workaround for preserving todos across context compaction by using PostToolUse hooks to save todos to Claude's project directory. The approach is pragmatic and well-executed. ✅ Strengths1. Smart Architecture Decision
2. Robust Script Implementation
3. Human-Readable Format
4. Clear Documentation
🔶 Issues & RecommendationsCritical: Missing jq Dependency CheckLocation: Issue: Script uses Impact: Todos won't persist, but no error will surface to the user. Recommendation: # Add near top of script (after shebang)
if ! command -v jq >/dev/null 2>&1; then
echo "[todo-persist] Error: jq not found. Install with: brew install jq (macOS) or apt-get install jq (Linux)" >&2
exit 1
fiBug: TODOS Variable Contains JSON, Not Plain TextLocation: Issue: TODOS=$(echo "$INPUT" | jq -r '.tool_input.todos // empty')The Test case: echo '{"tool_input":{"todos":[]}}' | jq -r '.tool_input.todos // empty'
# Output: []
# Line 20 check: [ -z "[]" ] evaluates to FALSE (should be TRUE)Recommendation: # More robust check
TODOS=$(echo "$INPUT" | jq '.tool_input.todos // empty')
if [ -z "$TODOS" ] || [ "$TODOS" = "null" ] || [ "$TODOS" = "[]" ] || [ -z "$TRANSCRIPT_PATH" ]; then
exit 0
fiRemove Edge Case: Path Injection Risk (Low Severity)Location: Issue: If Likelihood: Very low (requires compromised Claude Code internals) Recommendation: Add path validation: # After line 25
if [[ "$PROJECT_DIR" != */".claude/projects/"* ]]; then
echo "[todo-persist] Error: Invalid project directory path" >&2
exit 1
fiPotential Race Condition (Low Severity)Location: Issue: Multiple rapid TodoWrite calls could cause concurrent writes to Likelihood: Low in practice (TodoWrite calls are usually sequential) Recommendation: If this becomes an issue, use atomic writes: } > "$PROJECT_DIR/todos.md.tmp"
mv "$PROJECT_DIR/todos.md.tmp" "$PROJECT_DIR/todos.md"Documentation: Restoration Command ComplexityLocation: Issue: The sed command Recommendation: Add a comment explaining the path transformation: 1. Check for saved todos (transforms /path/to/project → -path-to-project):
`cat ~/.claude/projects/$(echo $PWD | sed 's|/|-|g')/todos.md`Or provide alternative command: 1. Find your project directory:
`ls -lt ~/.claude/projects/ | head`
2. Check todos: `cat ~/.claude/projects/YOUR-PROJECT-DIR/todos.md`Missing: Empty State HandlingLocation: Issue: If all todos are completed and then deleted, the file will show old completed todos forever. Recommendation: Either:
🧪 Testing GapsPer the PR description, these are not yet tested:
Critical to validate:
Suggested test script: # Test hook script directly
echo '{
"tool_name": "TodoWrite",
"transcript_path": "/tmp/test-project/.claude/projects/test/transcript.jsonl",
"tool_input": {
"todos": [
{"content": "Test task", "status": "pending", "activeForm": "Testing"}
]
}
}' | plugins/core/hooks/todo-persist.sh
# Verify output
cat /tmp/test-project/.claude/projects/test/todos.md🎯 Nitpicks
🔒 Security AssessmentOverall: Low Risk
Recommendation: Add path validation for defense in depth (see above). 📊 Performance ConsiderationsImpact: Negligible
Estimated overhead: < 10ms per TodoWrite call ✨ SummaryThis is a pragmatic solution to a real problem (issue #14258). The implementation is clean, well-documented, and follows POSIX best practices. Recommended Actions Before Merge:
Rating: 8/10 - Excellent concept and execution, needs minor fixes before merge. Review Context:
Great work on this feature! The hook system is being used creatively here. 🎉 |
Highlights the solution to todos being lost during context compaction - a common pain point for Claude Code users. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
New feature: Todo persistence hooks survive context compaction. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review: Todo Persistence HookSummaryThis PR adds a PostToolUse hook to persist todos across context compaction by saving them to Claude's project directory. The implementation is clean, focused, and solves a real pain point. ✅ Strengths1. Excellent Design Choices
2. Clean Implementation
3. Good Documentation
🔍 Issues Found1. Critical Bug: Empty Items in Markdown Output (todo-persist.sh:44-46) The current implementation has a flaw in how it processes multi-line jq output: items=$(echo "$TODOS" | jq -r --arg s "$status" '.[] | select(.status == $s) | .content')
if [ -n "$items" ]; then
# ...
echo "$items" | while read -r item; do
[ -n "$item" ] && echo "- $item"
doneProblem: When Impact: Produces malformed markdown with empty list items: ## Pending
- Fix authentication bug
-
- Add unit testsRecommended fix: Process items in a single pass without the separate check: echo "$TODOS" | jq -r --arg s "$status" '.[] | select(.status == $s) | .content' | while read -r item; do
[ -n "$item" ] && echo "- $item"
done | if grep -q .; then
case $status in
in_progress) echo "## In Progress" ;;
pending) echo "## Pending" ;;
completed) echo "## Completed" ;;
esac
echo ""
cat
echo ""
fiOr simpler - check if jq returns any output before printing the header: items=$(echo "$TODOS" | jq -r --arg s "$status" '.[] | select(.status == $s) | .content' | grep -v '^$')
if [ -n "$items" ]; then
case $status in
in_progress) echo "## In Progress" ;;
pending) echo "## Pending" ;;
completed) echo "## Completed" ;;
esac
echo ""
echo "$items" | while read -r item; do
echo "- $item"
done
echo ""
fi2. Missing Error Handling The script lacks error handling for several failure scenarios:
Recommended additions: # After shebang
set -euo pipefail # Exit on error, undefined vars, pipe failures
# Before jq usage
if ! command -v jq &> /dev/null; then
echo "Error: jq is required but not installed" >&2
exit 1
fi
# Before writing file
if [ ! -d "$PROJECT_DIR" ]; then
mkdir -p "$PROJECT_DIR" || exit 1
fi
if [ ! -w "$PROJECT_DIR" ]; then
echo "Error: Cannot write to $PROJECT_DIR" >&2
exit 1
fi3. Path Transformation Mismatch (README.md:53, context.md:24) The restoration command uses a different path transformation than the hook: Hook uses: README suggests: Problem: These may produce different paths. If Verification needed: Test that the path transformation in the restoration command exactly matches Claude's project directory naming scheme. Recommendation: Either:
4. Race Condition Risk Multiple concurrent TodoWrite calls could cause file corruption since there's no file locking. While unlikely in normal usage (TodoWrite calls are typically sequential), it's worth noting. Mitigation: Consider atomic writes: temp_file=$(mktemp)
# ... write to $temp_file ...
mv "$temp_file" "$PROJECT_DIR/todos.md"🤔 Questions & Considerations1. Test Coverage The PR description mentions:
The two unchecked items are critical for validating the end-to-end flow. Consider:
2. activeForm Field The TodoWrite tool requires both Current: Only saves content - Fix authentication bugWith activeForm: Could save both - Fix authentication bug (active: "Fixing authentication bug")This matters if users restore todos and want Claude to continue with proper progress indicators. 3. Completed Todos Should completed todos be saved at all? They may clutter the restoration. Consider:
4. Issue Reference The PR mentions issue #14258, but it's not found in this repo. Is that a Claude Code core issue? Consider adding a link to the actual issue for context. 🔒 Security Review✅ No security concerns identified
⚡ Performance Review✅ Minimal performance impact
📝 Suggested ImprovementsDocumentation:
Code Quality:
User Experience:
📊 Summary
RecommendationMerge after fixes: This is a valuable feature that solves a real problem. The implementation is solid, but the critical bug and error handling issues should be addressed before merge. Priority fixes:
Nice-to-haves (can be follow-up PRs):
Great work on tackling context compaction! This will be super helpful for maintaining workflow continuity. 🎉 |
Code Review: Todo Persistence HooksSummaryThis PR adds a clever solution to a real problem (issue #14258) - todos disappearing after context compaction. The implementation uses PostToolUse hooks to automatically persist todos to disk in a human-readable format. ✅ Strengths
🔴 Critical IssueMissing directory creation: The script will fail if the project directory doesn't exist yet. # Line 50 in todo-persist.sh
} > "$PROJECT_DIR/todos.md" # Fails if PROJECT_DIR doesn't existFix needed: # Add before line 27
mkdir -p "$PROJECT_DIR" || exit 1Reproduction: # Fails when PROJECT_DIR is /tmp/nonexistent
cat << 'EOF' | bash todo-persist.sh
{"tool_name":"TodoWrite","transcript_path":"/tmp/nonexistent/transcript.json","tool_input":{"todos":[{"content":"Test","status":"pending","activeForm":"Testing"}]}}
EOF
# Error: /tmp/nonexistent/todos.md: No such file or directory🟡 Minor Issues
🟢 Security & Performance
📝 Test CoverageFrom the PR description:
RecommendationsMust fix before merge:
Nice to have:
VerdictStrong implementation that solves a real user pain point. With the directory creation fix, this is ready to merge. The design choices (project-scoped, using existing paths, human-readable format) are all sound. Status: Approve with required fix ✅ (after adding 📊 Files changed: 6 | +98 -5 lines |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| compaction, restore them: | ||
|
|
||
| ```bash | ||
| cat ~/.claude/projects/$(echo $PWD | sed 's|/|-|g')/todos.md |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation command fails for paths containing spaces
Low Severity
The todo restore command cat ~/.claude/projects/$(echo $PWD | sed 's|/|-|g')/todos.md has an unquoted command substitution. When $PWD contains spaces (e.g., /home/user/my project), the sed output -home-user-my project undergoes word splitting, causing cat to receive incorrect arguments and fail. The command substitution needs to be quoted to preserve spaces in the resulting path.
Summary
transcript_pathto store in Claude's existing~/.claude/projects/{project}/structureProblem
Todos don't survive context compaction. This is a well-known issue (#14258) with no existing solution.
Solution
A single PostToolUse hook that:
TodoWritecall~/.claude/projects/{project}/todos.mdAfter compaction, context.md instructs Claude how to restore:
Design choices
~/.claude/projects/directory rather than inventing new paths~/.claude/Test plan
bash -n🤖 Generated with Claude Code