Add session-aware task context and auto-linking (CF-166 Phase 2)
Enhancements: 1. Session Context Table - Migration 014: session_context table - Tracks current working task per session - Auto-updated on task status changes 2. Auto-Linking to Current Working Task - When task status → in_progress: sets as current_task_id - When task status → completed: clears current_task_id - New tasks auto-link to current working task with 'relates_to' - Shows auto-link message during task creation 3. Session ID Resolution - Fixed path: ~/.cache/session-memory/current_session - Falls back to environment variable CLAUDE_SESSION_ID - Generates timestamp-based ID if unavailable Example Flow: ``` 1. task update CF-123 --status in_progress → Sets CF-123 as current working task 2. task add --title "Related work" → Created: CF-124 🔗 Auto-linked to: CF-123 (current working task) 3. task update CF-123 --status completed → Clears current working task ``` Files Changed: - migrations/014_session_context.sql (new, 33 lines) - src/tools/crud.ts (auto-linking logic, session context management) Benefits: - Zero manual linking for related work - Session context preserved automatically - Investigation workflows auto-organized - Retroactive work tracking prevented Note: Requires MCP server restart to take effect. Remaining CF-166 Features (Phase 3): - task investigate command - Investigation workflow helper - Task creation reminders Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
28
migrations/014_session_context.sql
Normal file
28
migrations/014_session_context.sql
Normal file
@@ -0,0 +1,28 @@
|
||||
-- Migration 014: Session Context for Auto-linking
|
||||
-- Tracks current working task per session for automatic task relationship creation
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session_context (
|
||||
session_id TEXT PRIMARY KEY,
|
||||
current_task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_session_context_task ON session_context(current_task_id);
|
||||
|
||||
-- Auto-update timestamp on changes
|
||||
CREATE OR REPLACE FUNCTION update_session_context_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER session_context_updated
|
||||
BEFORE UPDATE ON session_context
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_session_context_timestamp();
|
||||
|
||||
-- Record migration
|
||||
INSERT INTO schema_migrations (version) VALUES ('014_session_context')
|
||||
ON CONFLICT (version) DO NOTHING;
|
||||
@@ -5,6 +5,7 @@ import { getEmbedding, formatEmbedding } from '../embeddings.js';
|
||||
import type { Task, ChecklistItem, TaskLink } from '../types.js';
|
||||
import { getRecentDelegations } from './delegations.js';
|
||||
import { getTaskCommits } from './commits.js';
|
||||
import { taskLink } from './relations.js';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
@@ -18,8 +19,8 @@ function getSessionId(): string {
|
||||
return process.env.CLAUDE_SESSION_ID;
|
||||
}
|
||||
|
||||
// Try to read from cache file (usage-stats format)
|
||||
const cacheFile = path.join(os.homedir(), '.claude', 'cache', '.current_session_id');
|
||||
// Try to read from cache file (session-memory format)
|
||||
const cacheFile = path.join(os.homedir(), '.cache', 'session-memory', 'current_session');
|
||||
try {
|
||||
const sessionId = fs.readFileSync(cacheFile, 'utf-8').trim();
|
||||
if (sessionId) return sessionId;
|
||||
@@ -138,7 +139,29 @@ export async function taskAdd(args: TaskAddArgs): Promise<string> {
|
||||
// Record activity for session tracking
|
||||
await recordActivity(taskId, 'created', undefined, 'open');
|
||||
|
||||
return `Created: ${taskId}\n Title: ${title}\n Type: ${type}\n Priority: ${priority}\n Project: ${projectKey}${embedding ? '\n (embedded for semantic search)' : ''}${duplicateWarning}`;
|
||||
// Check for session context and auto-link to current working task
|
||||
let autoLinkMessage = '';
|
||||
const session_id = getSessionId();
|
||||
try {
|
||||
const sessionContext = await queryOne<{ current_task_id: string }>(
|
||||
`SELECT current_task_id FROM session_context WHERE session_id = $1`,
|
||||
[session_id]
|
||||
);
|
||||
|
||||
if (sessionContext?.current_task_id) {
|
||||
// Auto-link to current working task
|
||||
await taskLink({
|
||||
from_id: taskId,
|
||||
to_id: sessionContext.current_task_id,
|
||||
link_type: 'relates_to'
|
||||
});
|
||||
autoLinkMessage = `\n\n🔗 Auto-linked to: ${sessionContext.current_task_id} (current working task)`;
|
||||
}
|
||||
} catch {
|
||||
// Silently fail if session context unavailable
|
||||
}
|
||||
|
||||
return `Created: ${taskId}\n Title: ${title}\n Type: ${type}\n Priority: ${priority}\n Project: ${projectKey}${embedding ? '\n (embedded for semantic search)' : ''}${duplicateWarning}${autoLinkMessage}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,5 +427,30 @@ export async function taskUpdate(args: TaskUpdateArgs): Promise<string> {
|
||||
await recordActivity(id, 'updated');
|
||||
}
|
||||
|
||||
// Manage session context based on status changes
|
||||
if (status) {
|
||||
const session_id = getSessionId();
|
||||
try {
|
||||
if (status === 'in_progress') {
|
||||
// Set as current working task
|
||||
await execute(
|
||||
`INSERT INTO session_context (session_id, current_task_id)
|
||||
VALUES ($1, $2)
|
||||
ON CONFLICT (session_id) DO UPDATE SET current_task_id = $2, updated_at = NOW()`,
|
||||
[session_id, id]
|
||||
);
|
||||
} else if (status === 'completed') {
|
||||
// Clear if this is the current working task
|
||||
await execute(
|
||||
`DELETE FROM session_context
|
||||
WHERE session_id = $1 AND current_task_id = $2`,
|
||||
[session_id, id]
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// Silently fail if session context unavailable
|
||||
}
|
||||
}
|
||||
|
||||
return `Updated: ${id}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user