fix: wrap migration 17 in transaction and update docs
- Add BEGIN/COMMIT/ROLLBACK to session ID column rename migration - Update claude-desktop.mdx to reflect streamlined MCP tools - Remove obsolete skill zip download instructions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,29 +28,7 @@ curl http://localhost:37777/api/health
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Download the Skill
|
||||
|
||||
Download the skill package from the repository:
|
||||
|
||||
<Card title="mem-search.zip" icon="download" href="https://github.com/thedotmack/claude-mem/raw/main/plugin/skills/mem-search.zip">
|
||||
Download the mem-search skill for Claude Desktop
|
||||
</Card>
|
||||
|
||||
Or build from source:
|
||||
|
||||
```bash
|
||||
npm run build # Generates plugin/skills/mem-search.zip
|
||||
```
|
||||
|
||||
### Step 2: Install in Claude Desktop
|
||||
|
||||
1. Open **Claude Desktop**
|
||||
2. Go to **Settings** (gear icon)
|
||||
3. Navigate to **Skills**
|
||||
4. Click **Install Skill** or drag the `mem-search.zip` file
|
||||
5. Confirm installation
|
||||
|
||||
### Step 3: Configure MCP Server
|
||||
### Step 1: Configure MCP Server
|
||||
|
||||
The skill requires the `mcp-search` MCP server. Add this to your Claude Desktop configuration:
|
||||
|
||||
@@ -93,7 +71,7 @@ The skill requires the `mcp-search` MCP server. Add this to your Claude Desktop
|
||||
Replace `YOUR_USERNAME` with your actual username. Restart Claude Desktop after editing the configuration.
|
||||
</Warning>
|
||||
|
||||
### Step 4: Restart Claude Desktop
|
||||
### Step 2: Restart Claude Desktop
|
||||
|
||||
Close and reopen Claude Desktop for the MCP server configuration to take effect.
|
||||
|
||||
@@ -111,19 +89,21 @@ Once installed, the skill auto-activates when you ask about past work:
|
||||
|
||||
## Available MCP Tools
|
||||
|
||||
The skill provides access to these MCP tools:
|
||||
The skill provides three core MCP tools following a 3-layer workflow pattern:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `search` | Unified search across observations, sessions, and prompts |
|
||||
| `search` | Search memory index. Returns compact results with IDs for filtering |
|
||||
| `timeline` | Get chronological context around a query or observation ID |
|
||||
| `get_observation` | Fetch a single observation by ID |
|
||||
| `get_observations` | Fetch multiple observations efficiently |
|
||||
| `get_session` | Fetch session summary by ID |
|
||||
| `get_prompt` | Fetch user prompt by ID |
|
||||
| `get_recent_context` | Get recent timeline items |
|
||||
| `get_context_timeline` | Get timeline around a specific observation |
|
||||
| `help` | Load detailed usage instructions |
|
||||
| `get_observations` | Fetch full observation details by ID (use after filtering with search/timeline) |
|
||||
|
||||
### Token-Efficient Workflow
|
||||
|
||||
1. **Search** → Get index with IDs (~50-100 tokens/result)
|
||||
2. **Timeline** → Get context around interesting results
|
||||
3. **Get Observations** → Fetch full details ONLY for filtered IDs
|
||||
|
||||
This 3-layer approach provides ~10x token savings compared to fetching full details upfront.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ ${i.stack}`:` ${i.message}`:this.getLevel()===0&&typeof i=="object"?l=`
|
||||
completed_at_epoch INTEGER,
|
||||
FOREIGN KEY (session_db_id) REFERENCES sdk_sessions(id) ON DELETE CASCADE
|
||||
)
|
||||
`),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_session ON pending_messages(session_db_id)"),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_status ON pending_messages(status)"),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_claude_session ON pending_messages(content_session_id)"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(16,new Date().toISOString()),p.info("DB","pending_messages table created successfully")}catch(e){throw p.error("DB","Pending messages table migration error",void 0,e),e}}renameSessionIdColumns(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(17))return;if(p.info("DB","Renaming session ID columns for semantic clarity"),this.db.query("PRAGMA table_info(sdk_sessions)").all().some(r=>r.name==="content_session_id")){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(17,new Date().toISOString());return}this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE pending_messages RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE observations RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE session_summaries RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(17,new Date().toISOString()),p.info("DB","Successfully renamed session ID columns")}catch(e){throw p.error("DB","Session ID column rename migration error",void 0,e),e}}repairSessionIdColumnRename(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(19))return;p.info("DB","Checking session ID column renames (repair migration)");let t=!1;this.db.query("PRAGMA table_info(sdk_sessions)").all().some(d=>d.name==="claude_session_id")&&(p.info("DB","Repairing sdk_sessions columns"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN sdk_session_id TO memory_session_id"),t=!0),this.db.query("PRAGMA table_info(pending_messages)").all().some(d=>d.name==="claude_session_id")&&(p.info("DB","Repairing pending_messages columns"),this.db.run("ALTER TABLE pending_messages RENAME COLUMN claude_session_id TO content_session_id"),t=!0),this.db.query("PRAGMA table_info(observations)").all().some(d=>d.name==="sdk_session_id")&&(p.info("DB","Repairing observations columns"),this.db.run("ALTER TABLE observations RENAME COLUMN sdk_session_id TO memory_session_id"),t=!0),this.db.query("PRAGMA table_info(session_summaries)").all().some(d=>d.name==="sdk_session_id")&&(p.info("DB","Repairing session_summaries columns"),this.db.run("ALTER TABLE session_summaries RENAME COLUMN sdk_session_id TO memory_session_id"),t=!0),this.db.query("PRAGMA table_info(user_prompts)").all().some(d=>d.name==="claude_session_id")&&(p.info("DB","Repairing user_prompts columns"),this.db.run("ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id"),t=!0),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(19,new Date().toISOString()),t?p.info("DB","Session ID column rename repairs completed"):p.info("DB","No session ID column repairs needed")}catch(e){throw p.error("DB","Session ID column rename repair error",void 0,e),e}}updateMemorySessionId(e,t){this.db.prepare(`
|
||||
`),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_session ON pending_messages(session_db_id)"),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_status ON pending_messages(status)"),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_claude_session ON pending_messages(content_session_id)"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(16,new Date().toISOString()),p.info("DB","pending_messages table created successfully")}catch(e){throw p.error("DB","Pending messages table migration error",void 0,e),e}}renameSessionIdColumns(){if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(17))return;if(this.db.query("PRAGMA table_info(sdk_sessions)").all().some(r=>r.name==="content_session_id")){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(17,new Date().toISOString());return}p.info("DB","Renaming session ID columns for semantic clarity"),this.db.run("BEGIN TRANSACTION");try{this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE pending_messages RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE observations RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE session_summaries RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(17,new Date().toISOString()),p.info("DB","Successfully renamed session ID columns")}catch(r){throw this.db.run("ROLLBACK"),p.error("DB","Session ID column rename migration error",void 0,r),r}}repairSessionIdColumnRename(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(19))return;p.info("DB","Checking session ID column renames (repair migration)");let t=!1;this.db.query("PRAGMA table_info(sdk_sessions)").all().some(d=>d.name==="claude_session_id")&&(p.info("DB","Repairing sdk_sessions columns"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN sdk_session_id TO memory_session_id"),t=!0),this.db.query("PRAGMA table_info(pending_messages)").all().some(d=>d.name==="claude_session_id")&&(p.info("DB","Repairing pending_messages columns"),this.db.run("ALTER TABLE pending_messages RENAME COLUMN claude_session_id TO content_session_id"),t=!0),this.db.query("PRAGMA table_info(observations)").all().some(d=>d.name==="sdk_session_id")&&(p.info("DB","Repairing observations columns"),this.db.run("ALTER TABLE observations RENAME COLUMN sdk_session_id TO memory_session_id"),t=!0),this.db.query("PRAGMA table_info(session_summaries)").all().some(d=>d.name==="sdk_session_id")&&(p.info("DB","Repairing session_summaries columns"),this.db.run("ALTER TABLE session_summaries RENAME COLUMN sdk_session_id TO memory_session_id"),t=!0),this.db.query("PRAGMA table_info(user_prompts)").all().some(d=>d.name==="claude_session_id")&&(p.info("DB","Repairing user_prompts columns"),this.db.run("ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id"),t=!0),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(19,new Date().toISOString()),t?p.info("DB","Session ID column rename repairs completed"):p.info("DB","No session ID column repairs needed")}catch(e){throw p.error("DB","Session ID column rename repair error",void 0,e),e}}updateMemorySessionId(e,t){this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET memory_session_id = ?
|
||||
WHERE id = ?
|
||||
|
||||
@@ -243,7 +243,7 @@ ${s.stack}`:` ${s.message}`:this.getLevel()===0&&typeof s=="object"?l=`
|
||||
completed_at_epoch INTEGER,
|
||||
FOREIGN KEY (session_db_id) REFERENCES sdk_sessions(id) ON DELETE CASCADE
|
||||
)
|
||||
`),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_session ON pending_messages(session_db_id)"),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_status ON pending_messages(status)"),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_claude_session ON pending_messages(content_session_id)"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(16,new Date().toISOString()),P.info("DB","pending_messages table created successfully")}catch(e){throw P.error("DB","Pending messages table migration error",void 0,e),e}}renameSessionIdColumns(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(17))return;if(P.info("DB","Renaming session ID columns for semantic clarity"),this.db.query("PRAGMA table_info(sdk_sessions)").all().some(n=>n.name==="content_session_id")){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(17,new Date().toISOString());return}this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE pending_messages RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE observations RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE session_summaries RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(17,new Date().toISOString()),P.info("DB","Successfully renamed session ID columns")}catch(e){throw P.error("DB","Session ID column rename migration error",void 0,e),e}}repairSessionIdColumnRename(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(19))return;P.info("DB","Checking session ID column renames (repair migration)");let r=!1;this.db.query("PRAGMA table_info(sdk_sessions)").all().some(c=>c.name==="claude_session_id")&&(P.info("DB","Repairing sdk_sessions columns"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN sdk_session_id TO memory_session_id"),r=!0),this.db.query("PRAGMA table_info(pending_messages)").all().some(c=>c.name==="claude_session_id")&&(P.info("DB","Repairing pending_messages columns"),this.db.run("ALTER TABLE pending_messages RENAME COLUMN claude_session_id TO content_session_id"),r=!0),this.db.query("PRAGMA table_info(observations)").all().some(c=>c.name==="sdk_session_id")&&(P.info("DB","Repairing observations columns"),this.db.run("ALTER TABLE observations RENAME COLUMN sdk_session_id TO memory_session_id"),r=!0),this.db.query("PRAGMA table_info(session_summaries)").all().some(c=>c.name==="sdk_session_id")&&(P.info("DB","Repairing session_summaries columns"),this.db.run("ALTER TABLE session_summaries RENAME COLUMN sdk_session_id TO memory_session_id"),r=!0),this.db.query("PRAGMA table_info(user_prompts)").all().some(c=>c.name==="claude_session_id")&&(P.info("DB","Repairing user_prompts columns"),this.db.run("ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id"),r=!0),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(19,new Date().toISOString()),r?P.info("DB","Session ID column rename repairs completed"):P.info("DB","No session ID column repairs needed")}catch(e){throw P.error("DB","Session ID column rename repair error",void 0,e),e}}updateMemorySessionId(e,r){this.db.prepare(`
|
||||
`),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_session ON pending_messages(session_db_id)"),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_status ON pending_messages(status)"),this.db.run("CREATE INDEX IF NOT EXISTS idx_pending_messages_claude_session ON pending_messages(content_session_id)"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(16,new Date().toISOString()),P.info("DB","pending_messages table created successfully")}catch(e){throw P.error("DB","Pending messages table migration error",void 0,e),e}}renameSessionIdColumns(){if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(17))return;if(this.db.query("PRAGMA table_info(sdk_sessions)").all().some(n=>n.name==="content_session_id")){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(17,new Date().toISOString());return}P.info("DB","Renaming session ID columns for semantic clarity"),this.db.run("BEGIN TRANSACTION");try{this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE pending_messages RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE observations RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE session_summaries RENAME COLUMN sdk_session_id TO memory_session_id"),this.db.run("ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("COMMIT"),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(17,new Date().toISOString()),P.info("DB","Successfully renamed session ID columns")}catch(n){throw this.db.run("ROLLBACK"),P.error("DB","Session ID column rename migration error",void 0,n),n}}repairSessionIdColumnRename(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(19))return;P.info("DB","Checking session ID column renames (repair migration)");let r=!1;this.db.query("PRAGMA table_info(sdk_sessions)").all().some(c=>c.name==="claude_session_id")&&(P.info("DB","Repairing sdk_sessions columns"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id"),this.db.run("ALTER TABLE sdk_sessions RENAME COLUMN sdk_session_id TO memory_session_id"),r=!0),this.db.query("PRAGMA table_info(pending_messages)").all().some(c=>c.name==="claude_session_id")&&(P.info("DB","Repairing pending_messages columns"),this.db.run("ALTER TABLE pending_messages RENAME COLUMN claude_session_id TO content_session_id"),r=!0),this.db.query("PRAGMA table_info(observations)").all().some(c=>c.name==="sdk_session_id")&&(P.info("DB","Repairing observations columns"),this.db.run("ALTER TABLE observations RENAME COLUMN sdk_session_id TO memory_session_id"),r=!0),this.db.query("PRAGMA table_info(session_summaries)").all().some(c=>c.name==="sdk_session_id")&&(P.info("DB","Repairing session_summaries columns"),this.db.run("ALTER TABLE session_summaries RENAME COLUMN sdk_session_id TO memory_session_id"),r=!0),this.db.query("PRAGMA table_info(user_prompts)").all().some(c=>c.name==="claude_session_id")&&(P.info("DB","Repairing user_prompts columns"),this.db.run("ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id"),r=!0),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(19,new Date().toISOString()),r?P.info("DB","Session ID column rename repairs completed"):P.info("DB","No session ID column repairs needed")}catch(e){throw P.error("DB","Session ID column rename repair error",void 0,e),e}}updateMemorySessionId(e,r){this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET memory_session_id = ?
|
||||
WHERE id = ?
|
||||
|
||||
@@ -584,22 +584,25 @@ export class SessionStore {
|
||||
* - sdk_session_id → memory_session_id (memory agent's session for resume)
|
||||
*/
|
||||
private renameSessionIdColumns(): void {
|
||||
const applied = this.db.prepare('SELECT version FROM schema_versions WHERE version = ?').get(17) as SchemaVersion | undefined;
|
||||
if (applied) return;
|
||||
|
||||
// Check if columns are already renamed (idempotent check)
|
||||
const sessionsInfo = this.db.query('PRAGMA table_info(sdk_sessions)').all() as TableColumnInfo[];
|
||||
const hasContentSessionId = sessionsInfo.some(col => col.name === 'content_session_id');
|
||||
|
||||
if (hasContentSessionId) {
|
||||
// Already renamed, just record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(17, new Date().toISOString());
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('DB', 'Renaming session ID columns for semantic clarity');
|
||||
|
||||
// Begin transaction for atomic rename
|
||||
this.db.run('BEGIN TRANSACTION');
|
||||
|
||||
try {
|
||||
const applied = this.db.prepare('SELECT version FROM schema_versions WHERE version = ?').get(17) as SchemaVersion | undefined;
|
||||
if (applied) return;
|
||||
|
||||
logger.info('DB', 'Renaming session ID columns for semantic clarity');
|
||||
|
||||
// Check if columns are already renamed (idempotent check)
|
||||
const sessionsInfo = this.db.query('PRAGMA table_info(sdk_sessions)').all() as TableColumnInfo[];
|
||||
const hasContentSessionId = sessionsInfo.some(col => col.name === 'content_session_id');
|
||||
|
||||
if (hasContentSessionId) {
|
||||
// Already renamed, just record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(17, new Date().toISOString());
|
||||
return;
|
||||
}
|
||||
|
||||
// SQLite 3.25+ supports ALTER TABLE RENAME COLUMN
|
||||
// Rename in sdk_sessions table
|
||||
this.db.run('ALTER TABLE sdk_sessions RENAME COLUMN claude_session_id TO content_session_id');
|
||||
@@ -617,11 +620,16 @@ export class SessionStore {
|
||||
// Rename in user_prompts table
|
||||
this.db.run('ALTER TABLE user_prompts RENAME COLUMN claude_session_id TO content_session_id');
|
||||
|
||||
// Commit transaction
|
||||
this.db.run('COMMIT');
|
||||
|
||||
// Record migration
|
||||
this.db.prepare('INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)').run(17, new Date().toISOString());
|
||||
|
||||
logger.info('DB', 'Successfully renamed session ID columns');
|
||||
} catch (error: any) {
|
||||
// Rollback on error
|
||||
this.db.run('ROLLBACK');
|
||||
logger.error('DB', 'Session ID column rename migration error', undefined, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user