- docs/plan.md: Master roadmap with phases and priorities - docs/milestones/01-13: Detailed specs for each feature - Updated CLAUDE.md with plan references and build commands Milestones cover: - Phase 1: Temporal versioning, auto-capture, context injection, codebase indexing - Phase 2: Daily journal, content ingestion, graph visualization, import/export - Phase 3: Multi-graph, smart retrieval, TUI dashboard, browser extension, shell completions
6.4 KiB
6.4 KiB
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
# 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
# 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
# Search across all graphs
cortex query "auth" --all-graphs
# Search specific graphs
cortex query "auth" --graphs work,personal
9.4 Graph Linking
# 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
# 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
// 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
// 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
// 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
// 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<Node | null> {
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 <name> |
Create new graph |
cortex graphs delete <name> |
Delete graph |
cortex use <name> |
Switch active graph |
cortex <cmd> --graph <name> |
Run command on specific graph |
cortex query --all-graphs |
Search all graphs |
MCP Tools
memory_graphs // List available graphs
memory_use_graph // Switch active graph
memory_query // Add optional 'graph' parameter
Project Config File
// .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 for multi-project inspiration