Add development plan with 13 milestone specifications

- 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
This commit is contained in:
2026-02-03 09:36:08 +01:00
parent 3d5a979a1b
commit d484f61b29
15 changed files with 3673 additions and 9 deletions

View File

@@ -0,0 +1,339 @@
# Milestone 12: Browser Extension
## Overview
Browser extension to save content from any webpage directly to Cortex. One-click capture for research and documentation.
## Motivation
- Web research is a major knowledge source
- Manual copy-paste is friction
- Supermemory's browser extension is popular
- Capture context while browsing
## Features
### 12.1 Quick Save
Right-click any page → "Save to Cortex"
### 12.2 Selection Save
Select text → Right-click → "Save selection to Cortex"
### 12.3 Popup Interface
```
┌─────────────────────────────────────────┐
│ 🧠 Save to Cortex │
├─────────────────────────────────────────┤
│ Title: [API Documentation ] │
│ Kind: [memory ▼] │
│ Tags: [docs, api, reference ] │
│ │
│ ☑ Include page content │
│ ☐ Include selection only │
│ ☐ Include screenshot │
│ │
│ [Save] [Cancel] │
└─────────────────────────────────────────┘
```
### 12.4 Auto-Extract
Automatically extract:
- Page title
- Main content (via Readability)
- Meta description
- Author/date if available
### 12.5 Tag Suggestions
Suggest tags based on:
- Page content analysis
- Existing tags in Cortex
- URL domain
### 12.6 Local Communication
Extension communicates with local Cortex server:
- Native messaging (preferred)
- Local HTTP API fallback
## Implementation
### Extension Structure
```
extension/
├── manifest.json
├── popup/
│ ├── popup.html
│ ├── popup.js
│ └── popup.css
├── content/
│ └── content.js # Injected into pages
├── background/
│ └── background.js # Service worker
├── icons/
│ ├── icon-16.png
│ ├── icon-48.png
│ └── icon-128.png
└── native/
└── cortex-native.js # Native messaging host
```
### Manifest (v3)
```json
{
"manifest_version": 3,
"name": "Cortex - Save to Memory",
"version": "1.0.0",
"description": "Save web content to your Cortex knowledge graph",
"permissions": [
"activeTab",
"contextMenus",
"storage",
"nativeMessaging"
],
"host_permissions": [
"http://localhost:3100/*"
],
"action": {
"default_popup": "popup/popup.html",
"default_icon": {
"16": "icons/icon-16.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
}
},
"background": {
"service_worker": "background/background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content/content.js"]
}
]
}
```
### Content Script
```javascript
// content/content.js
// Extract page content using Readability
function extractContent() {
const clone = document.cloneNode(true);
const reader = new Readability(clone);
const article = reader.parse();
return {
title: article?.title || document.title,
content: article?.textContent || '',
excerpt: article?.excerpt || '',
url: window.location.href,
selection: window.getSelection()?.toString() || '',
};
}
// Listen for messages from popup/background
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'extract') {
sendResponse(extractContent());
}
});
```
### Background Script
```javascript
// background/background.js
// Context menu
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: 'save-to-cortex',
title: 'Save to Cortex',
contexts: ['page', 'selection'],
});
});
chrome.contextMenus.onClicked.addListener(async (info, tab) => {
if (info.menuItemId === 'save-to-cortex') {
// Get content from page
const [{ result }] = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => window.__cortexExtract(),
});
// Save via native messaging or HTTP
await saveToCortext(result);
}
});
async function saveToCortex(content) {
try {
// Try native messaging first
const response = await chrome.runtime.sendNativeMessage('cortex', {
action: 'save',
data: content,
});
return response;
} catch {
// Fall back to HTTP API
const response = await fetch('http://localhost:3100/api/nodes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
kind: 'memory',
title: content.title,
content: content.content,
tags: ['web-clip'],
metadata: {
source: { type: 'url', url: content.url },
},
}),
});
return response.json();
}
}
```
### Popup UI
```html
<!-- popup/popup.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container">
<h1>🧠 Save to Cortex</h1>
<div class="field">
<label>Title</label>
<input type="text" id="title" />
</div>
<div class="field">
<label>Kind</label>
<select id="kind">
<option value="memory">Memory</option>
<option value="component">Component</option>
<option value="decision">Decision</option>
<option value="task">Task</option>
</select>
</div>
<div class="field">
<label>Tags</label>
<input type="text" id="tags" placeholder="comma, separated" />
</div>
<div class="field">
<label>
<input type="checkbox" id="includeContent" checked />
Include page content
</label>
</div>
<div class="actions">
<button id="save">Save</button>
<button id="cancel">Cancel</button>
</div>
</div>
<script src="popup.js"></script>
</body>
</html>
```
### Native Messaging Host
```javascript
// native/cortex-native.js
#!/usr/bin/env node
const { addNode } = require('../dist/core/store');
process.stdin.on('readable', () => {
// Read message length (4 bytes)
const lengthBuf = process.stdin.read(4);
if (!lengthBuf) return;
const length = lengthBuf.readUInt32LE(0);
const messageBuf = process.stdin.read(length);
const message = JSON.parse(messageBuf.toString());
handleMessage(message).then(response => {
const responseBuf = Buffer.from(JSON.stringify(response));
const lengthBuf = Buffer.alloc(4);
lengthBuf.writeUInt32LE(responseBuf.length, 0);
process.stdout.write(lengthBuf);
process.stdout.write(responseBuf);
});
});
async function handleMessage(message) {
if (message.action === 'save') {
const node = await addNode({
kind: message.data.kind || 'memory',
title: message.data.title,
content: message.data.content,
tags: message.data.tags || ['web-clip'],
metadata: { source: message.data.source },
});
return { success: true, nodeId: node.id };
}
return { success: false, error: 'Unknown action' };
}
```
## CLI Commands
| Command | Description |
|---------|-------------|
| `cortex extension install` | Install native messaging host |
| `cortex extension status` | Check extension connectivity |
## Testing
- [ ] Extension installs in Chrome/Edge
- [ ] Context menu appears
- [ ] Popup opens and pre-fills data
- [ ] Save creates node in Cortex
- [ ] Selection save captures selected text
- [ ] Native messaging works
- [ ] HTTP fallback works
## Acceptance Criteria
- [ ] One-click save from any webpage
- [ ] Content extracted cleanly
- [ ] Tags can be added before save
- [ ] Works without Cortex server running (queues)
- [ ] Firefox + Chrome support
## Estimated Effort
- Extension scaffold: 2 hours
- Content extraction: 3 hours
- Popup UI: 4 hours
- Background worker: 3 hours
- Native messaging: 4 hours
- Testing/polish: 4 hours
- **Total: ~20 hours**
## Dependencies
- `@anthropic/readability` for content extraction
- Chrome/Edge extension APIs
## References
- [Chrome Extension Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/)
- [Native Messaging](https://developer.chrome.com/docs/extensions/mv3/nativeMessaging/)
- [Readability](https://github.com/mozilla/readability)