# Milestone 9: Multi-Graph Support ## Overview Support multiple separate knowledge graphs, one per project or context. Switch between them seamlessly. ## Motivation - Different projects have different knowledge domains - Prevents cross-contamination of contexts - Enables project-specific memory without noise - Clean separation of concerns ## Features ### 9.1 Graph Management ```bash # List all graphs cortex graphs # Create new graph cortex graphs create work cortex graphs create personal # Switch active graph cortex use work cortex use personal # Delete graph cortex graphs delete old-project ``` ### 9.2 Automatic Project Detection ```bash # Auto-detect based on .cortex file or git remote cd ~/projects/myapp cortex query "auth" # Automatically uses 'myapp' graph # Explicit override cortex query "auth" --graph personal ``` ### 9.3 Cross-Graph Search ```bash # Search across all graphs cortex query "auth" --all-graphs # Search specific graphs cortex query "auth" --graphs work,personal ``` ### 9.4 Graph Linking ```bash # Link nodes across graphs cortex link abc123 --to def456 --graph work # Reference external graph nodes cortex show work:abc123 # graph:nodeId syntax ``` ### 9.5 Graph Configuration ```bash # Set default graph cortex config set default-graph work # Project-specific graph mapping # In .cortex.json or .cortex file: { "graph": "myapp" } ``` ## Implementation ### Storage Structure ``` ~/.cortex/ ├── graphs/ │ ├── default/ │ │ └── cortex.db │ ├── work/ │ │ └── cortex.db │ └── personal/ │ └── cortex.db ├── config.json └── graph-links.db # Cross-graph references ``` ### Graph Manager ```typescript // src/core/graphs.ts export interface GraphInfo { name: string; path: string; nodeCount: number; lastAccessed: number; createdAt: number; } export function listGraphs(): GraphInfo[] { const graphsDir = path.join(getConfigDir(), 'graphs'); const dirs = fs.readdirSync(graphsDir); return dirs.map(name => ({ name, path: path.join(graphsDir, name), ...getGraphStats(path.join(graphsDir, name, 'cortex.db')), })); } export function createGraph(name: string): void { const graphPath = path.join(getConfigDir(), 'graphs', name); fs.mkdirSync(graphPath, { recursive: true }); initializeDatabase(path.join(graphPath, 'cortex.db')); } export function useGraph(name: string): void { const graphPath = path.join(getConfigDir(), 'graphs', name); if (!fs.existsSync(graphPath)) { throw new Error(`Graph '${name}' does not exist`); } setActiveGraph(name); } export function getActiveGraph(): string { // Check project-local .cortex file const localConfig = findLocalConfig(); if (localConfig?.graph) return localConfig.graph; // Check git remote for auto-detection const gitRemote = getGitRemote(); if (gitRemote) { const projectName = extractProjectName(gitRemote); if (graphExists(projectName)) return projectName; } // Fall back to default return getConfig().defaultGraph || 'default'; } ``` ### Database Connection Manager ```typescript // src/core/db.ts let activeDb: Database | null = null; let activeGraph: string = 'default'; export function getDb(): Database { const currentGraph = getActiveGraph(); if (activeDb && activeGraph === currentGraph) { return activeDb; } // Close previous connection if (activeDb) { activeDb.close(); } // Open new connection const dbPath = path.join(getConfigDir(), 'graphs', currentGraph, 'cortex.db'); activeDb = new Database(dbPath); activeGraph = currentGraph; return activeDb; } ``` ### Project Detection ```typescript // src/core/graphs.ts function findLocalConfig(): LocalConfig | null { // Walk up directory tree looking for .cortex or .cortex.json let dir = process.cwd(); while (dir !== path.dirname(dir)) { const configPath = path.join(dir, '.cortex'); if (fs.existsSync(configPath)) { return JSON.parse(fs.readFileSync(configPath, 'utf-8')); } const jsonPath = path.join(dir, '.cortex.json'); if (fs.existsSync(jsonPath)) { return JSON.parse(fs.readFileSync(jsonPath, 'utf-8')); } dir = path.dirname(dir); } return null; } ``` ### Cross-Graph References ```typescript // Parse graph:nodeId syntax function parseNodeRef(ref: string): { graph?: string; nodeId: string } { if (ref.includes(':')) { const [graph, nodeId] = ref.split(':'); return { graph, nodeId }; } return { nodeId: ref }; } // Get node from any graph async function getNodeFromAnyGraph(ref: string): Promise { const { graph, nodeId } = parseNodeRef(ref); if (graph) { const originalGraph = getActiveGraph(); useGraph(graph); const node = await getNode(nodeId); useGraph(originalGraph); return node; } return getNode(nodeId); } ``` ## CLI Commands | Command | Description | |---------|-------------| | `cortex graphs` | List all graphs | | `cortex graphs create ` | Create new graph | | `cortex graphs delete ` | Delete graph | | `cortex use ` | Switch active graph | | `cortex --graph ` | Run command on specific graph | | `cortex query --all-graphs` | Search all graphs | ## MCP Tools ```typescript memory_graphs // List available graphs memory_use_graph // Switch active graph memory_query // Add optional 'graph' parameter ``` ## Project Config File ```json // .cortex or .cortex.json in project root { "graph": "myproject", "autoCapture": true, "contextInjection": { "maxTokens": 4000, "includeTasks": true } } ``` ## Testing - [ ] Create/list/delete graphs - [ ] Switch between graphs - [ ] Auto-detect from .cortex file - [ ] Auto-detect from git remote - [ ] Cross-graph search works - [ ] Cross-graph references resolve - [ ] Concurrent access handles graph switching ## Acceptance Criteria - [ ] Each project can have isolated graph - [ ] Auto-detection based on directory - [ ] Cross-graph search available - [ ] Graph switching is fast (<100ms) - [ ] No data leakage between graphs - [ ] MCP tools respect active graph ## Estimated Effort - Storage structure: 2 hours - Graph manager: 4 hours - DB connection manager: 2 hours - Project detection: 3 hours - Cross-graph search: 3 hours - CLI commands: 2 hours - Testing: 3 hours - **Total: ~19 hours** ## Dependencies - None (can implement independently) ## References - [Git worktrees](https://git-scm.com/docs/git-worktree) for multi-project inspiration