Browse Source

feat(commands): Upgrade /save and /sync to schema v3.1

- Add session ID tracking for --resume suggestions
- Add PR-linked session awareness via gh pr view
- Respect plansDirectory setting (Claude Code v2.1.9+)
- Write session summary to native MEMORY.md (auto-loaded safety net)
- Drop legacy v2.0 migration code from /sync
- Bump schema from v3.0 to v3.1 (backwards compatible)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
0xDarkMatter 2 months ago
parent
commit
db252468f0
3 changed files with 118 additions and 45 deletions
  1. 7 5
      README.md
  2. 82 12
      commands/save.md
  3. 29 28
      commands/sync.md

+ 7 - 5
README.md

@@ -364,13 +364,14 @@ Session 2:
 
 **Use both together:** `claude --resume` for conversation context, `/sync` for task state.
 
-### Session Cache Schema (v3.0)
+### Session Cache Schema (v3.1)
 
 The `.claude/session-cache.json` file stores full task objects:
 
 ```json
 {
-  "version": "3.0",
+  "version": "3.1",
+  "session_id": "977c26c9-60fa-4afc-a628-a68f8043b1ab",
   "tasks": [
     {
       "subject": "Task title",
@@ -380,13 +381,14 @@ The `.claude/session-cache.json` file stores full task objects:
       "blockedBy": [0, 1]
     }
   ],
-  "plan": { "goal": "...", "current_step": "...", "progress_percent": 40 },
-  "git": { "branch": "main", "last_commit": "abc123" },
+  "plan": { "file": "docs/PLAN.md", "goal": "...", "current_step": "...", "progress_percent": 40 },
+  "git": { "branch": "main", "last_commit": "abc123", "pr_number": 42, "pr_url": "https://..." },
+  "memory": { "synced": true },
   "notes": "Session notes"
 }
 ```
 
-**Migration:** `/sync` auto-detects v2.0 files (legacy `todos` format) and migrates them. Run `/save` after migration to upgrade the file.
+**Compatibility:** `/sync` handles both v3.0 and v3.1 files gracefully. Missing v3.1 fields are treated as absent.
 
 ## Updating
 

+ 82 - 12
commands/save.md

@@ -19,13 +19,27 @@ $ARGUMENTS
 | Data | Source | Destination |
 |------|--------|-------------|
 | Tasks | TaskList API | `.claude/session-cache.json` |
-| Plan content | Conversation context | `docs/PLAN.md` |
+| Plan content | Conversation context | `<plan-path>` (see Step 0) |
 | Git context | `git status/log` | `.claude/session-cache.json` |
 | User notes | Command argument | `.claude/session-cache.json` |
 | Human-readable summary | Generated | `.claude/claude-progress.md` |
 
 ## Execution
 
+### Step 0: Resolve Plan Path
+
+Determine where the strategic plan file lives:
+
+1. Check `.claude/settings.local.json` for a `plansDirectory` key
+2. If not found, check `.claude/settings.json` for `plansDirectory`
+3. If found, plan path = `<plansDirectory>/PLAN.md`
+4. If not found, default to `docs/PLAN.md`
+
+Store the resolved path for use in all subsequent steps.
+
+Note: `plansDirectory` is a Claude Code setting (added in v2.1.9) for plan file storage.
+Our strategic `PLAN.md` co-locates with native plans when this is set.
+
 ### Step 1: Capture Task State
 
 Use TaskList and TaskGet to capture full task data:
@@ -52,21 +66,30 @@ Extract from conversation context:
 - Open questions
 - Blockers
 
-### Step 3: Capture Git Context
+### Step 3: Capture Git & Session Context
 
 ```bash
 git branch --show-current
 git rev-parse --short HEAD
 git log -1 --format="%s"
 git status --porcelain | wc -l
+
+# Detect linked PR (requires gh CLI, fails gracefully)
+gh pr view --json number,url --jq '{number,url}' 2>/dev/null
 ```
 
+Additionally, capture your current session ID. You have access to this from your
+runtime context - it is the unique identifier for this conversation session.
+
+If `gh` is not installed or no PR exists for the current branch, skip the PR fields.
+
 ### Step 4: Write Files
 
 **`.claude/session-cache.json`** (machine-readable):
 ```json
 {
-  "version": "3.0",
+  "version": "3.1",
+  "session_id": "<your-current-session-id>",
   "timestamp": "2025-12-13T10:30:00Z",
   "tasks": [
     {
@@ -92,7 +115,7 @@ git status --porcelain | wc -l
     }
   ],
   "plan": {
-    "file": "docs/PLAN.md",
+    "file": "<resolved-plan-path>",
     "goal": "Add user authentication with OAuth2",
     "current_step": "Step 3: Implement OAuth flow",
     "current_step_index": 3,
@@ -103,13 +126,18 @@ git status --porcelain | wc -l
     "branch": "feature/auth",
     "last_commit": "abc123f",
     "last_commit_message": "feat: Add OAuth config",
-    "uncommitted_count": 3
+    "uncommitted_count": 3,
+    "pr_number": 42,
+    "pr_url": "https://github.com/user/repo/pull/42"
+  },
+  "memory": {
+    "synced": true
   },
   "notes": "Stopped at callback URL issue - need to fix redirect"
 }
 ```
 
-**`docs/PLAN.md`** (strategic plan):
+**`<plan-path>`** (strategic plan, at resolved path):
 ```markdown
 # Project Plan
 
@@ -191,6 +219,42 @@ auth for better scalability and API compatibility.
 *Restore with: /sync*
 ```
 
+### Step 5: Update Native Memory
+
+Write a brief session summary to your auto memory directory as a safety net.
+This ensures basic session context appears in the system prompt of future sessions,
+even without running `/sync`.
+
+**Target file:** Your auto memory `MEMORY.md` (path is in your system prompt).
+
+**Procedure:**
+
+1. Read `MEMORY.md` from your auto memory directory if it exists
+2. If it contains a `## Last Session` section, replace that entire section
+   (from the heading to the next `##` heading or end of file) with the updated content
+3. If no such section exists, append it to the end of the file
+4. If the file does not exist, create it with only this section
+
+**`## Last Session` content** (keep under 10 lines):
+
+```markdown
+## Last Session
+
+- **Goal**: [plan goal or "No active plan"]
+- **Branch**: [current git branch]
+- **Step**: [current plan step or "N/A"]
+- **Session**: [session_id] (resume with `claude --resume <id>`)
+- **PR**: [#number if linked, omit line if none]
+- **Notes**: [user notes if provided, omit line if none]
+- **Restore**: Run `/sync` to restore full task state
+```
+
+**Important:**
+- Do NOT overwrite existing MEMORY.md content outside `## Last Session`
+- Keep the section under 10 lines to preserve the 200-line MEMORY.md budget
+- If the memory directory does not exist, create it with `mkdir -p`
+- This is best-effort - warn but do not fail `/save` if memory write fails
+
 ## Output Format
 
 ```
@@ -201,12 +265,17 @@ Session saved
 | **Plan** | Step 3/5 (40%) - Implement OAuth flow |
 | **Tasks** | 1 completed, 1 in progress, 1 pending |
 | **Git** | 3 uncommitted files on feature/auth |
+| **PR** | #42 (https://github.com/user/repo/pull/42) |
+| **Session** | abc123... (resumable via --resume) |
 | **Notes** | "Stopped at callback URL issue..." |
 
+Note: PR and Session rows are omitted when not available.
+
 Files written:
   .claude/session-cache.json
   .claude/claude-progress.md
-  docs/PLAN.md
+  <plan-path>
+  ~/.claude/projects/.../memory/MEMORY.md (session summary)
 
 Restore with: /sync
 ```
@@ -219,19 +288,19 @@ Archives current plan before saving fresh state.
 
 ### What It Does
 
-1. Moves `docs/PLAN.md` to `docs/PLAN-<date>.md`
+1. Moves `<plan-path>` to `<plan-dir>/PLAN-<date>.md`
 2. Clears `.claude/session-cache.json`
 3. Saves new state (if any)
 
 ### Output
 
 ```
-Archived: docs/PLAN.md -> docs/PLAN-2025-12-13.md
+Archived: <plan-path> -> <plan-dir>/PLAN-2025-12-13.md
 
 Session saved (fresh start)
 
 Files written:
-  docs/PLAN-2025-12-13.md (archived)
+  <plan-dir>/PLAN-2025-12-13.md (archived)
   .claude/session-cache.json (cleared)
 ```
 
@@ -273,10 +342,11 @@ Files written:
 
 | File | Purpose | Git-tracked? |
 |------|---------|--------------|
-| `docs/PLAN.md` | Strategic plan | Yes |
+| `<plan-path>` | Strategic plan (default: `docs/PLAN.md`) | Yes |
 | `.claude/session-cache.json` | Session state | Optional |
 | `.claude/claude-progress.md` | Human-readable progress | Optional |
-| `docs/PLAN-<date>.md` | Archived plans | Yes |
+| `<plan-dir>/PLAN-<date>.md` | Archived plans | Yes |
+| `~/.claude/.../memory/MEMORY.md` | Session summary (auto-loaded) | No (user home) |
 
 ---
 

+ 29 - 28
commands/sync.md

@@ -31,7 +31,8 @@ $ARGUMENTS
     |      +- Read project context (README, AGENTS, CLAUDE)
     |      +- Read saved state (.claude/session-cache.json)
     |      +- Restore tasks via TaskCreate
-    |      +- Read plan (docs/PLAN.md)
+    |      +- Resolve plan path (Step 0)
+    |      +- Read plan (<plan-path>)
     |      +- Show unified status
     |      +- Suggest next action
     |
@@ -66,6 +67,18 @@ $ARGUMENTS
 
 Full bootstrap with state restoration.
 
+### Step 0: Resolve Plan Path
+
+Determine the plan file location before reading:
+
+1. If saved state exists (`.claude/session-cache.json`) and has a `plan.file` value, prefer that path
+2. Else check `.claude/settings.local.json` for a `plansDirectory` key
+3. Else check `.claude/settings.json` for `plansDirectory`
+4. If found, plan path = `<plansDirectory>/PLAN.md`
+5. Otherwise, default to `docs/PLAN.md`
+
+Use the resolved path in all subsequent file reads and output.
+
 ### Step 1: Parallel Reads
 
 Read these files simultaneously (skip any that don't exist):
@@ -75,7 +88,7 @@ Read these files simultaneously (skip any that don't exist):
 | `README.md` | Project overview |
 | `AGENTS.md` | Agent instructions |
 | `CLAUDE.md` | Project-specific rules |
-| `docs/PLAN.md` | Current plan |
+| `<plan-path>` | Current plan (resolved in Step 0) |
 | `.claude/session-cache.json` | Saved session state |
 
 ### Step 2: Restore Session State
@@ -95,6 +108,8 @@ If `.claude/session-cache.json` exists:
 5. For each task, call TaskUpdate to set status:
    - "completed" | "in_progress" | "pending"
 6. Note time since last save
+7. If session_id present, note it for --resume suggestion in output
+8. If git.pr_number/pr_url present, note for --from-pr suggestion in output
 ```
 
 Note: Tasks do not persist across sessions automatically, which is why this restore step is needed.
@@ -151,8 +166,11 @@ Project Synced: [project-name]
 |-------|-------|
 | **Last saved** | 2 hours ago |
 | **Branch** | feature/auth |
+| **Previous session** | abc123... |
 | **Notes** | "Stopped at callback URL issue" |
 
+Note: Previous session row only shown when session_id is present in saved state.
+
 ## Plan Status
 
 **Goal**: Add user authentication with OAuth2
@@ -182,6 +200,9 @@ Progress: 40% (2/5)
 | **Branch** | feature/auth |
 | **Uncommitted** | 3 files |
 | **Last commit** | abc123f feat: Add OAuth config |
+| **PR** | #42 - https://github.com/user/repo/pull/42 |
+
+Note: PR row only shown when pr_number/pr_url are present in saved state.
 
 ## Quick Reference
 
@@ -195,8 +216,12 @@ Progress: 40% (2/5)
 
 1. **Continue**: Fix callback URL handling
 2. **Check diff**: /sync --diff to see changes since save
+3. **Resume conversation**: `claude --resume abc123...` (when session_id present)
+4. **PR context**: `claude --from-pr 42` (when PR is linked)
 ```
 
+Note: Next Steps items 3-4 are only shown when the corresponding data exists in saved state.
+
 ### Without Saved State
 
 ```
@@ -317,7 +342,7 @@ Auto-update plan steps from recent commits.
 
 1. Parse recent git commits (last 20)
 2. Match commit messages to plan steps using keywords
-3. Update step status in `docs/PLAN.md`
+3. Update step status in the resolved plan path
 
 ### Matching Rules
 
@@ -339,7 +364,7 @@ Git Sync
 | abc123 "feat: Add OAuth config" | Step 2 | Marked complete |
 | def456 "fix: Callback handling" | Step 3 | Marked in-progress |
 
-## Updated docs/PLAN.md
+## Updated <plan-path>
 
 | Step | Previous | New |
 |------|----------|-----|
@@ -409,30 +434,6 @@ Options:
   2. Start fresh: /save --archive
 ```
 
-### Legacy v2.0 schema (todos object)
-```
-Info: Found v2.0 session-cache.json (legacy format)
-
-Converting todos to tasks:
-  - 3 completed → tasks with status: "completed"
-  - 1 in_progress → tasks with status: "in_progress"
-  - 2 pending → tasks with status: "pending"
-
-Note: Legacy format lacks descriptions and dependencies.
-Consider running /save to upgrade to v3.0 schema.
-```
-
-**Migration logic:**
-```
-if version == "2.0" and "todos" in json:
-    for task in todos.completed:
-        TaskCreate(subject=task, description="(migrated from v2.0)", status="completed")
-    for task in todos.in_progress:
-        TaskCreate(subject=task, description="(migrated from v2.0)", status="in_progress")
-    for task in todos.pending:
-        TaskCreate(subject=task, description="(migrated from v2.0)", status="pending")
-```
-
 ### Branch changed since save
 ```
 Warning: Branch changed since save