From 90bb7e94f005f329b0425b28b15de8f7d5063695 Mon Sep 17 00:00:00 2001 From: Christian Gick Date: Sat, 17 Jan 2026 08:44:19 +0200 Subject: [PATCH] Add session-aware task context and auto-linking (CF-166 Phase 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- migrations/014_session_context.sql | 28 ++++++++++++++++ src/tools/crud.ts | 54 ++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 migrations/014_session_context.sql diff --git a/migrations/014_session_context.sql b/migrations/014_session_context.sql new file mode 100644 index 0000000..6a1f614 --- /dev/null +++ b/migrations/014_session_context.sql @@ -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; diff --git a/src/tools/crud.ts b/src/tools/crud.ts index 5a424ef..3b24a20 100644 --- a/src/tools/crud.ts +++ b/src/tools/crud.ts @@ -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 { // 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 { 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}`; }