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.
 **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:
 The `.claude/session-cache.json` file stores full task objects:
 
 
 ```json
 ```json
 {
 {
-  "version": "3.0",
+  "version": "3.1",
+  "session_id": "977c26c9-60fa-4afc-a628-a68f8043b1ab",
   "tasks": [
   "tasks": [
     {
     {
       "subject": "Task title",
       "subject": "Task title",
@@ -380,13 +381,14 @@ The `.claude/session-cache.json` file stores full task objects:
       "blockedBy": [0, 1]
       "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"
   "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
 ## Updating
 
 

+ 82 - 12
commands/save.md

@@ -19,13 +19,27 @@ $ARGUMENTS
 | Data | Source | Destination |
 | Data | Source | Destination |
 |------|--------|-------------|
 |------|--------|-------------|
 | Tasks | TaskList API | `.claude/session-cache.json` |
 | 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` |
 | Git context | `git status/log` | `.claude/session-cache.json` |
 | User notes | Command argument | `.claude/session-cache.json` |
 | User notes | Command argument | `.claude/session-cache.json` |
 | Human-readable summary | Generated | `.claude/claude-progress.md` |
 | Human-readable summary | Generated | `.claude/claude-progress.md` |
 
 
 ## Execution
 ## 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
 ### Step 1: Capture Task State
 
 
 Use TaskList and TaskGet to capture full task data:
 Use TaskList and TaskGet to capture full task data:
@@ -52,21 +66,30 @@ Extract from conversation context:
 - Open questions
 - Open questions
 - Blockers
 - Blockers
 
 
-### Step 3: Capture Git Context
+### Step 3: Capture Git & Session Context
 
 
 ```bash
 ```bash
 git branch --show-current
 git branch --show-current
 git rev-parse --short HEAD
 git rev-parse --short HEAD
 git log -1 --format="%s"
 git log -1 --format="%s"
 git status --porcelain | wc -l
 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
 ### Step 4: Write Files
 
 
 **`.claude/session-cache.json`** (machine-readable):
 **`.claude/session-cache.json`** (machine-readable):
 ```json
 ```json
 {
 {
-  "version": "3.0",
+  "version": "3.1",
+  "session_id": "<your-current-session-id>",
   "timestamp": "2025-12-13T10:30:00Z",
   "timestamp": "2025-12-13T10:30:00Z",
   "tasks": [
   "tasks": [
     {
     {
@@ -92,7 +115,7 @@ git status --porcelain | wc -l
     }
     }
   ],
   ],
   "plan": {
   "plan": {
-    "file": "docs/PLAN.md",
+    "file": "<resolved-plan-path>",
     "goal": "Add user authentication with OAuth2",
     "goal": "Add user authentication with OAuth2",
     "current_step": "Step 3: Implement OAuth flow",
     "current_step": "Step 3: Implement OAuth flow",
     "current_step_index": 3,
     "current_step_index": 3,
@@ -103,13 +126,18 @@ git status --porcelain | wc -l
     "branch": "feature/auth",
     "branch": "feature/auth",
     "last_commit": "abc123f",
     "last_commit": "abc123f",
     "last_commit_message": "feat: Add OAuth config",
     "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"
   "notes": "Stopped at callback URL issue - need to fix redirect"
 }
 }
 ```
 ```
 
 
-**`docs/PLAN.md`** (strategic plan):
+**`<plan-path>`** (strategic plan, at resolved path):
 ```markdown
 ```markdown
 # Project Plan
 # Project Plan
 
 
@@ -191,6 +219,42 @@ auth for better scalability and API compatibility.
 *Restore with: /sync*
 *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
 ## Output Format
 
 
 ```
 ```
@@ -201,12 +265,17 @@ Session saved
 | **Plan** | Step 3/5 (40%) - Implement OAuth flow |
 | **Plan** | Step 3/5 (40%) - Implement OAuth flow |
 | **Tasks** | 1 completed, 1 in progress, 1 pending |
 | **Tasks** | 1 completed, 1 in progress, 1 pending |
 | **Git** | 3 uncommitted files on feature/auth |
 | **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..." |
 | **Notes** | "Stopped at callback URL issue..." |
 
 
+Note: PR and Session rows are omitted when not available.
+
 Files written:
 Files written:
   .claude/session-cache.json
   .claude/session-cache.json
   .claude/claude-progress.md
   .claude/claude-progress.md
-  docs/PLAN.md
+  <plan-path>
+  ~/.claude/projects/.../memory/MEMORY.md (session summary)
 
 
 Restore with: /sync
 Restore with: /sync
 ```
 ```
@@ -219,19 +288,19 @@ Archives current plan before saving fresh state.
 
 
 ### What It Does
 ### 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`
 2. Clears `.claude/session-cache.json`
 3. Saves new state (if any)
 3. Saves new state (if any)
 
 
 ### Output
 ### 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)
 Session saved (fresh start)
 
 
 Files written:
 Files written:
-  docs/PLAN-2025-12-13.md (archived)
+  <plan-dir>/PLAN-2025-12-13.md (archived)
   .claude/session-cache.json (cleared)
   .claude/session-cache.json (cleared)
 ```
 ```
 
 
@@ -273,10 +342,11 @@ Files written:
 
 
 | File | Purpose | Git-tracked? |
 | 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/session-cache.json` | Session state | Optional |
 | `.claude/claude-progress.md` | Human-readable progress | 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 project context (README, AGENTS, CLAUDE)
     |      +- Read saved state (.claude/session-cache.json)
     |      +- Read saved state (.claude/session-cache.json)
     |      +- Restore tasks via TaskCreate
     |      +- Restore tasks via TaskCreate
-    |      +- Read plan (docs/PLAN.md)
+    |      +- Resolve plan path (Step 0)
+    |      +- Read plan (<plan-path>)
     |      +- Show unified status
     |      +- Show unified status
     |      +- Suggest next action
     |      +- Suggest next action
     |
     |
@@ -66,6 +67,18 @@ $ARGUMENTS
 
 
 Full bootstrap with state restoration.
 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
 ### Step 1: Parallel Reads
 
 
 Read these files simultaneously (skip any that don't exist):
 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 |
 | `README.md` | Project overview |
 | `AGENTS.md` | Agent instructions |
 | `AGENTS.md` | Agent instructions |
 | `CLAUDE.md` | Project-specific rules |
 | `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 |
 | `.claude/session-cache.json` | Saved session state |
 
 
 ### Step 2: Restore 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:
 5. For each task, call TaskUpdate to set status:
    - "completed" | "in_progress" | "pending"
    - "completed" | "in_progress" | "pending"
 6. Note time since last save
 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.
 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 |
 | **Last saved** | 2 hours ago |
 | **Branch** | feature/auth |
 | **Branch** | feature/auth |
+| **Previous session** | abc123... |
 | **Notes** | "Stopped at callback URL issue" |
 | **Notes** | "Stopped at callback URL issue" |
 
 
+Note: Previous session row only shown when session_id is present in saved state.
+
 ## Plan Status
 ## Plan Status
 
 
 **Goal**: Add user authentication with OAuth2
 **Goal**: Add user authentication with OAuth2
@@ -182,6 +200,9 @@ Progress: 40% (2/5)
 | **Branch** | feature/auth |
 | **Branch** | feature/auth |
 | **Uncommitted** | 3 files |
 | **Uncommitted** | 3 files |
 | **Last commit** | abc123f feat: Add OAuth config |
 | **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
 ## Quick Reference
 
 
@@ -195,8 +216,12 @@ Progress: 40% (2/5)
 
 
 1. **Continue**: Fix callback URL handling
 1. **Continue**: Fix callback URL handling
 2. **Check diff**: /sync --diff to see changes since save
 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
 ### Without Saved State
 
 
 ```
 ```
@@ -317,7 +342,7 @@ Auto-update plan steps from recent commits.
 
 
 1. Parse recent git commits (last 20)
 1. Parse recent git commits (last 20)
 2. Match commit messages to plan steps using keywords
 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
 ### Matching Rules
 
 
@@ -339,7 +364,7 @@ Git Sync
 | abc123 "feat: Add OAuth config" | Step 2 | Marked complete |
 | abc123 "feat: Add OAuth config" | Step 2 | Marked complete |
 | def456 "fix: Callback handling" | Step 3 | Marked in-progress |
 | def456 "fix: Callback handling" | Step 3 | Marked in-progress |
 
 
-## Updated docs/PLAN.md
+## Updated <plan-path>
 
 
 | Step | Previous | New |
 | Step | Previous | New |
 |------|----------|-----|
 |------|----------|-----|
@@ -409,30 +434,6 @@ Options:
   2. Start fresh: /save --archive
   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
 ### Branch changed since save
 ```
 ```
 Warning: Branch changed since save
 Warning: Branch changed since save