mnemos-capture 1.0.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +216 -74
- package/dist/cli/hooks.js +164 -0
- package/dist/cli/index.js +35 -6
- package/dist/cli/mcp-server.js +21 -1
- package/dist/cli/version-check.js +79 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,133 +4,275 @@
|
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Mnemos</h1>
|
|
6
6
|
|
|
7
|
-
<p align="center"
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="https://img.shields.io/npm/v/mnemos-capture" alt="npm" />
|
|
9
|
+
<img src="https://img.shields.io/badge/license-MIT-blue" alt="License" />
|
|
10
|
+
<img src="https://img.shields.io/badge/MCP-compatible-green" alt="MCP" />
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center"><strong>A knowledge pipeline for AI agents.<br />Capture anything — your agents retrieve and apply it.</strong></p>
|
|
14
|
+
|
|
15
|
+
Your agents only know what they were trained on. They don't know what you read this morning, what framework you found last week, or what idea you had at midnight.
|
|
16
|
+
|
|
17
|
+
Mnemos bridges that gap. It's a knowledge base that grows in real time and plugs directly into any agent via MCP — so every insight you capture is immediately available to every agent you use.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Get started
|
|
21
|
+
|
|
22
|
+
### 1. Sign up (30 seconds)
|
|
23
|
+
|
|
24
|
+
Go to **[mnemos-capture.vercel.app](https://mnemos-capture.vercel.app)** → **Sign in with GitHub**.
|
|
25
|
+
|
|
26
|
+
During setup, Mnemos will:
|
|
8
27
|
|
|
9
|
-
|
|
28
|
+
- Create a knowledge repo in your GitHub account — plain Markdown files, no proprietary format
|
|
29
|
+
- Ask for your Anthropic API key — your key, stored per-user, Mnemos never pays for your API calls
|
|
30
|
+
- Set a PIN so you can unlock the app quickly on mobile
|
|
31
|
+
|
|
32
|
+
No config files. No repos to clone. No CLI setup required.
|
|
33
|
+
|
|
34
|
+
### 2. Capture something
|
|
10
35
|
|
|
11
|
-
|
|
36
|
+
Open the app on any device — phone, tablet, or desktop. Paste any content and hit **Capture**.
|
|
37
|
+
|
|
38
|
+
The result is a structured Markdown file, auto-committed to your GitHub knowledge repo and immediately available to your agents.
|
|
39
|
+
|
|
40
|
+
### 3. Connect to Claude Code
|
|
41
|
+
|
|
42
|
+
After signing up, you get an MCP API key. Run this once on your machine:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
claude mcp add mnemos -- npx -y mnemos-capture@latest serve-mcp --key <your-api-key>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Your agent confirms the connection and can immediately access your knowledge base.
|
|
49
|
+
|
|
50
|
+
> `npx -y mnemos-capture@latest serve-mcp` runs a lightweight local process that bridges Claude Code to the Mnemos API. The `@latest` tag guarantees you always run the most recently published version. As a safety net, the MCP server also self-upgrades on startup: if a newer version is on npm, it respawns under `npx @latest` automatically — so existing users who registered without `@latest` pick up updates on their next session. No data is stored locally — everything lives in your GitHub repo.
|
|
51
|
+
|
|
52
|
+
**Proactive inbox notifications (automatic).** The first time the MCP server starts, Mnemos silently installs a `SessionStart` hook in `~/.claude/settings.json`. From then on, every new Claude Code session opens with a short reminder:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Mnemos: 3 captures in inbox — run list_inbox to review
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Your agent sees this before the conversation begins and can proactively surface pending captures instead of waiting for you to ask. The hook is skipped if one already exists, so rotating keys or upgrading modes is always safe — your configuration is never overwritten silently.
|
|
59
|
+
|
|
60
|
+
**Upgrade to a full session briefing (optional).** If you want more than a count — an LLM-composed briefing grounded in your current git branch, recent commits, and `CLAUDE.md` — run:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx -y mnemos-capture@latest setup-hooks --key <your-api-key> --briefing
|
|
64
|
+
```
|
|
12
65
|
|
|
13
|
-
|
|
66
|
+
Each session now starts with a focused summary: which captures are relevant to what you're working on right now, which synthesized rules apply, and what's waiting in your inbox. Adds ~5s to session start (calls `briefing` on the hosted API, uses your Anthropic key).
|
|
14
67
|
|
|
15
|
-
|
|
68
|
+
**Where the hook runs.** `SessionStart` hooks fire anywhere Claude Code reads your local settings — CLI, VS Code / JetBrains extensions, and the desktop app. For Claude Code on the web or other MCP clients (Cursor, etc.), the MCP tools (`list_inbox`, `briefing`, `recall`, …) still work — you just won't get the proactive nudge automatically. Add a line to that project's `CLAUDE.md` like `"At session start, call list_inbox to surface pending captures"` for a portable alternative.
|
|
16
69
|
|
|
17
|
-
|
|
70
|
+
### 4. Connect to any MCP-compatible agent
|
|
18
71
|
|
|
19
|
-
|
|
20
|
-
2. **AI extracts** — an LLM pulls the actual insight (not a summary), tags it by topic, and connects it to what you're building right now
|
|
21
|
-
3. **Your agents apply it** — Claude Code, Codex, or any agentic tool reads your knowledge repo via MCP or Git and uses it in real workflows
|
|
72
|
+
Your knowledge lives in a standard GitHub repo. Any agent that can read Git or speak MCP can access it — no lock-in, no custom integration required.
|
|
22
73
|
|
|
23
|
-
Every capture includes an **"Applied to"** field — a concrete sentence linking what you learned to what you're building. This is the difference between a saved-for-later graveyard and a knowledge system that compounds.
|
|
24
74
|
|
|
25
75
|
## How it works
|
|
26
76
|
|
|
27
77
|
```mermaid
|
|
28
|
-
flowchart
|
|
29
|
-
A["
|
|
30
|
-
B -->|
|
|
31
|
-
C -->|commit| D["Your GitHub repo\n(Markdown)"]
|
|
32
|
-
D -->|MCP| E["
|
|
33
|
-
|
|
34
|
-
|
|
78
|
+
flowchart TD
|
|
79
|
+
A["Article / research /\ntranscript / idea"] -->|paste| B["Mnemos\n(web / mobile / CLI)"]
|
|
80
|
+
B -->|extracts insights| C["Structured knowledge\n+ where to apply it"]
|
|
81
|
+
C -->|auto-commit| D["Your GitHub repo\n(Markdown)"]
|
|
82
|
+
D -->|MCP| E["Agent retrieves\n& surfaces to you"]
|
|
83
|
+
E -->|you decide| F{"Apply?"}
|
|
84
|
+
F -->|yes| G["Applied to\nyour workflow"]
|
|
85
|
+
F -->|no| H["Stays in\nknowledge base"]
|
|
35
86
|
|
|
36
87
|
style A fill:#1a1a2e,stroke:#444,color:#fff
|
|
37
88
|
style B fill:#2A62C6,stroke:#1a4a9e,color:#fff
|
|
38
89
|
style C fill:#d4a843,stroke:#b8912e,color:#fff
|
|
39
90
|
style D fill:#2d8a4e,stroke:#1e6b3a,color:#fff
|
|
40
91
|
style E fill:#1a1a2e,stroke:#444,color:#fff
|
|
41
|
-
style F fill:#
|
|
42
|
-
style G fill:#
|
|
92
|
+
style F fill:#2A62C6,stroke:#1a4a9e,color:#fff
|
|
93
|
+
style G fill:#2d8a4e,stroke:#1e6b3a,color:#fff
|
|
94
|
+
style H fill:#1a1a2e,stroke:#444,color:#fff
|
|
43
95
|
```
|
|
44
96
|
|
|
45
|
-
|
|
97
|
+
1. **You paste content** — an article, a research paper, a thread, a transcript, your own ideas. Anything text-based.
|
|
98
|
+
2. **Mnemos extracts the insight** — Claude Haiku 4.5 pulls the core idea (not a summary), identifies key takeaways, and tags where it could apply in your work. All automatic.
|
|
99
|
+
3. **It's committed to your repo** — a structured Markdown file lands in your GitHub knowledge repo, searchable and version-controlled.
|
|
100
|
+
4. **Your agents retrieve it** — Claude Code or any MCP-compatible agent can search your knowledge base and surface the right insight when it's relevant. You review, you decide what gets applied.
|
|
101
|
+
|
|
102
|
+
### Example output
|
|
103
|
+
|
|
104
|
+
Paste a research post about multi-agent systems. This is what gets committed to your repo:
|
|
105
|
+
|
|
106
|
+
```markdown
|
|
107
|
+
---
|
|
108
|
+
date: 2026-04-01
|
|
109
|
+
source: Why Multi-Agent Systems Outperform Single-Agent Loops
|
|
110
|
+
url: https://example.com/multi-agent-systems
|
|
111
|
+
type: post
|
|
112
|
+
tags: multi-agent, orchestration, agent-design, ai-architecture
|
|
113
|
+
status: inbox
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
# Why Multi-Agent Systems Outperform Single-Agent Loops
|
|
117
|
+
|
|
118
|
+
## Core idea
|
|
119
|
+
Decomposing complex tasks across specialized agents reduces error propagation
|
|
120
|
+
and improves output quality — a single orchestrator routing to focused subagents
|
|
121
|
+
consistently outperforms one generalist agent handling everything sequentially.
|
|
122
|
+
|
|
123
|
+
## Key takeaways
|
|
124
|
+
- Specialized agents outperform generalist agents on tasks requiring depth over breadth
|
|
125
|
+
- Parallel execution across subagents reduces total latency by 40-60% on multi-step tasks
|
|
126
|
+
- Orchestrator-subagent patterns improve error isolation — one agent failing doesn't
|
|
127
|
+
collapse the entire workflow
|
|
128
|
+
|
|
129
|
+
## Quotes
|
|
130
|
+
> "The bottleneck in complex agentic tasks is not model capability — it's
|
|
131
|
+
> error propagation across sequential steps."
|
|
132
|
+
|
|
133
|
+
## Applied to
|
|
134
|
+
Evaluate your current single-agent workflows for tasks that could be decomposed
|
|
135
|
+
into parallel subagent calls — prioritize anything with 3+ sequential steps.
|
|
136
|
+
```
|
|
46
137
|
|
|
47
|
-
|
|
138
|
+
Every field is designed to help your agents find and apply the right knowledge at the right time. The **"Applied to"** field is the most important — it connects the insight to a concrete action in your work.
|
|
48
139
|
|
|
49
|
-
- **Articles and blog posts** — the insight, not a summary
|
|
50
|
-
- **Twitter/X threads** — the argument stitched together
|
|
51
|
-
- **Podcast and meeting transcripts** — the signal, not the filler
|
|
52
|
-
- **Your own notes and ideas** — raw thoughts turned into structured knowledge
|
|
53
|
-
- **Book highlights and excerpts** — the quotes and frameworks worth keeping
|
|
54
|
-
- **Newsletter and Substack posts** — the actionable takeaways
|
|
55
140
|
|
|
56
|
-
|
|
141
|
+
## What you can feed it
|
|
57
142
|
|
|
58
|
-
|
|
143
|
+
If it's text, Mnemos can extract insight from it. You don't need to categorize or tag anything yourself — that happens automatically.
|
|
59
144
|
|
|
60
|
-
|
|
145
|
+
- **Research papers and preprints** — new models, architectures, evaluation methods
|
|
146
|
+
- **Framework and library docs** — patterns, APIs, integration approaches worth keeping
|
|
147
|
+
- **Optimization techniques** — prompt engineering, caching strategies, latency improvements
|
|
148
|
+
- **Technical threads and writeups** — the argument or finding, not the noise
|
|
149
|
+
- **Your own ideas** — workflow improvements, architecture decisions, things you want your agents to act on later
|
|
150
|
+
- **Transcripts and talks** — the signal extracted, ready to apply
|
|
61
151
|
|
|
62
|
-
Extraction runs on **Claude Haiku 4.5** with prompt caching and input truncation, optimized for minimal token usage:
|
|
63
152
|
|
|
64
|
-
|
|
65
|
-
|-------|----------------------|
|
|
66
|
-
| 50 captures/month (casual) | ~$0.15 |
|
|
67
|
-
| 100 captures/month (regular) | ~$0.30 |
|
|
68
|
-
| 200 captures/month (heavy) | ~$0.60 |
|
|
153
|
+
## Knowledge lifecycle
|
|
69
154
|
|
|
70
|
-
|
|
155
|
+
Knowledge that sits unreviewed rots. Mnemos forces a decision: apply it, save it for later, or discard it.
|
|
71
156
|
|
|
72
|
-
|
|
157
|
+
```
|
|
158
|
+
┌─────────┐
|
|
159
|
+
│ Capture │
|
|
160
|
+
└────┬─────┘
|
|
161
|
+
▼
|
|
162
|
+
┌─────────┐ ┌──────────┐
|
|
163
|
+
│ Inbox │────▶│ Applied │ You used the insight in your workflow
|
|
164
|
+
└────┬─────┘ └──────────┘
|
|
165
|
+
│
|
|
166
|
+
├──────────▶┌──────────┐
|
|
167
|
+
│ │ Archived │ Reviewed, not actionable right now
|
|
168
|
+
│ └──────────┘
|
|
169
|
+
│
|
|
170
|
+
└──────────▶┌──────────┐
|
|
171
|
+
│ Deleted │ Not useful, permanently removed
|
|
172
|
+
└──────────┘
|
|
173
|
+
```
|
|
73
174
|
|
|
74
|
-
|
|
175
|
+
**How it works in practice:**
|
|
75
176
|
|
|
76
|
-
|
|
177
|
+
1. **Capture** — every new capture lands in `inbox/`. After each capture, Mnemos tells you how many items are waiting for review.
|
|
178
|
+
2. **Review** — use `list_inbox` to see what's in your inbox (with summaries), and `read_capture` to read the full content of any capture.
|
|
179
|
+
3. **Apply** — when you've used an insight (added it to a project file, changed a workflow, created a rule), use `apply_capture` to move it to `applied/`. This closes the loop — the knowledge is now part of your system.
|
|
180
|
+
4. **Archive** — reviewed it but it's not actionable right now? Use `archive_capture` to move it to `archived/`. It stays searchable but out of your inbox.
|
|
181
|
+
5. **Delete** — not useful? Use `delete_capture` to permanently remove it.
|
|
77
182
|
|
|
78
|
-
|
|
183
|
+
All captures are tracked in `INDEX.md` — a master table your agents use to search across your entire knowledge base.
|
|
79
184
|
|
|
80
|
-
- Create a knowledge repo in your GitHub account (you own it — just Markdown files in a repo under your name)
|
|
81
|
-
- Ask for your Anthropic API key (your key, stored per-user — Mnemos never pays for your API calls)
|
|
82
|
-
- Set a PIN so you can unlock the app quickly on mobile
|
|
83
185
|
|
|
84
|
-
|
|
186
|
+
## MCP tools
|
|
85
187
|
|
|
86
|
-
|
|
188
|
+
Mnemos exposes 7 tools via MCP, organized by what you're doing:
|
|
87
189
|
|
|
88
|
-
|
|
190
|
+
### Capture
|
|
89
191
|
|
|
90
|
-
|
|
192
|
+
| Tool | What it does |
|
|
193
|
+
|------|-------------|
|
|
194
|
+
| `capture` | Extracts insight from any pasted content and commits a structured Markdown file to your repo. This is the entry point — everything starts here. |
|
|
91
195
|
|
|
92
|
-
###
|
|
196
|
+
### Discover
|
|
93
197
|
|
|
94
|
-
|
|
198
|
+
| Tool | What it does |
|
|
199
|
+
|------|-------------|
|
|
200
|
+
| `list_inbox` | Shows your unprocessed captures with summaries — title, type, tags, and core idea. Up to 10 at a time. Use this to see what's waiting for your review. |
|
|
201
|
+
| `search_captures` | Searches your knowledge base by keyword or tag. Finds relevant captures across inbox, applied, and archived — so your agent can pull the right knowledge when it's needed. |
|
|
202
|
+
| `read_capture` | Reads the full Markdown of any capture. Use this to see the complete insight, takeaways, and context before deciding what to do with it. |
|
|
95
203
|
|
|
96
|
-
|
|
204
|
+
### Manage
|
|
205
|
+
|
|
206
|
+
| Tool | What it does |
|
|
207
|
+
|------|-------------|
|
|
208
|
+
| `apply_capture` | Moves a capture from inbox to applied. Marks it as used — you can add a note about where you applied it (e.g., "Added as a rule in CLAUDE.md"). |
|
|
209
|
+
| `archive_capture` | Moves a capture from inbox to archived. It's been reviewed but isn't actionable right now. Stays searchable, out of your inbox. |
|
|
210
|
+
| `delete_capture` | Permanently removes a capture and its index entry. For mistakes or captures that aren't useful. |
|
|
211
|
+
|
|
212
|
+
The web UI and MCP use the same pipeline. The web UI is for capturing on the go. MCP is for your agents — they retrieve and manage captures without you touching the browser.
|
|
97
213
|
|
|
98
|
-
```bash
|
|
99
|
-
claude mcp add mnemos -- npx mnemos-capture serve-mcp --key <your-api-key>
|
|
100
|
-
```
|
|
101
214
|
|
|
102
|
-
|
|
215
|
+
## Mobile
|
|
103
216
|
|
|
104
|
-
|
|
105
|
-
- *"What's in my inbox?"*
|
|
106
|
-
- *"Search my captures for evaluation frameworks"*
|
|
217
|
+
Open Mnemos in your phone's browser, tap **Share → Add to Home Screen**. It runs full-screen like a native app.
|
|
107
218
|
|
|
108
|
-
|
|
219
|
+
Capture on the go — reading an article on your phone at lunch? Capture it. Your agents have it by the time you sit down to work.
|
|
109
220
|
|
|
110
|
-
##
|
|
221
|
+
## Your data, your storage
|
|
111
222
|
|
|
112
|
-
|
|
223
|
+
Your knowledge lives in a GitHub repo you own. Plain Markdown files, version-controlled, portable.
|
|
113
224
|
|
|
114
|
-
|
|
225
|
+
- **No lock-in** — clone it, search it, move it, delete Mnemos tomorrow and your repo stays exactly where it is
|
|
226
|
+
- **No proprietary format** — every capture is a readable `.md` file
|
|
227
|
+
- **No training on your data** — Mnemos never reads your captures for any purpose other than serving them back to you and your agents
|
|
228
|
+
- **Any tool can access it** — anything that reads Git or speaks MCP works with your knowledge base
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
## Cost
|
|
232
|
+
|
|
233
|
+
Mnemos uses your own Anthropic API key (BYOK). You bring your key, Mnemos never charges you for API calls.
|
|
234
|
+
|
|
235
|
+
Extraction runs on **Claude Haiku 4.5** with prompt caching and input truncation, optimized for minimal token usage:
|
|
236
|
+
|
|
237
|
+
| Usage | Estimated monthly cost |
|
|
238
|
+
|-------|----------------------|
|
|
239
|
+
| 50 captures/month | ~$0.15 |
|
|
240
|
+
| 100 captures/month | ~$0.30 |
|
|
241
|
+
| 200 captures/month | ~$0.60 |
|
|
242
|
+
|
|
243
|
+
Less than $1/month for heavy use.
|
|
115
244
|
|
|
116
|
-
Your knowledge lives in a repo you own. No proprietary database, no vendor lock-in. It's version-controlled, portable, and readable as plain Markdown. Clone it, search it, back it up — it's just files. And because it's a standard Git repo, any MCP-compatible agent or tool can read from it directly.
|
|
117
245
|
|
|
118
246
|
## Tech stack
|
|
119
247
|
|
|
120
|
-
Next.js · TypeScript
|
|
248
|
+
Next.js · TypeScript · Claude Haiku 4.5 · Anthropic SDK · GitHub OAuth · Vercel Postgres · GitHub Content API · MCP protocol · Tailwind CSS
|
|
121
249
|
|
|
122
250
|
## Roadmap
|
|
123
251
|
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
252
|
+
- Chrome extension for one-click capture from anywhere in the browser
|
|
253
|
+
- URL auto-fetch — paste a link, Mnemos fetches and extracts
|
|
254
|
+
- Multi-provider support (OpenAI, Google — schema is ready)
|
|
255
|
+
- Voice memo capture
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
## Built by
|
|
259
|
+
|
|
260
|
+
[Sofía Padrón Valdez](https://github.com/Soph20) — builder, AI systems architect.
|
|
261
|
+
|
|
262
|
+
## Citation
|
|
263
|
+
|
|
264
|
+
If you use this repository in your agentic workflows, please consider citing as follows:
|
|
265
|
+
```
|
|
266
|
+
@misc{2026mnemos,
|
|
267
|
+
title = {Mnemos: A Knowledge Pipeline for AI Agents},
|
|
268
|
+
author = {Sofia Padron Valdez},
|
|
269
|
+
year = 2026,
|
|
270
|
+
journal = {GitHub repository},
|
|
271
|
+
publisher = {GitHub},
|
|
272
|
+
howpublished = {\url{https://github.com/Soph20/mnemos-capture}}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
133
275
|
|
|
134
276
|
## License
|
|
135
277
|
|
|
136
|
-
[MIT](LICENSE)
|
|
278
|
+
[MIT License - see LICENSE file for details](./LICENSE)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { homedir } from "os";
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
const HOSTED_URL = "https://mnemos-capture.vercel.app/api/mcp";
|
|
6
|
+
const CLAUDE_SETTINGS_PATH = join(homedir(), ".claude", "settings.json");
|
|
7
|
+
// Matches both legacy ("mnemos-capture inbox-check") and modern
|
|
8
|
+
// ("mnemos-capture@latest inbox-check") hook command forms.
|
|
9
|
+
function isMnemosHook(cmd) {
|
|
10
|
+
return cmd.includes("mnemos-capture") && cmd.includes("inbox-check");
|
|
11
|
+
}
|
|
12
|
+
function readSettings() {
|
|
13
|
+
if (!existsSync(CLAUDE_SETTINGS_PATH))
|
|
14
|
+
return {};
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(readFileSync(CLAUDE_SETTINGS_PATH, "utf-8"));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function writeSettings(settings) {
|
|
23
|
+
const dir = join(homedir(), ".claude");
|
|
24
|
+
if (!existsSync(dir))
|
|
25
|
+
mkdirSync(dir, { recursive: true });
|
|
26
|
+
writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2) + "\n");
|
|
27
|
+
}
|
|
28
|
+
export function setupHooks(apiKey, opts = {}) {
|
|
29
|
+
const settings = readSettings();
|
|
30
|
+
const hooks = settings.hooks ?? {};
|
|
31
|
+
const sessionStart = hooks.SessionStart ?? [];
|
|
32
|
+
// Inspect any existing mnemos hook so we can either skip, migrate, or replace
|
|
33
|
+
let existingCmd = null;
|
|
34
|
+
for (const group of sessionStart) {
|
|
35
|
+
for (const h of group.hooks ?? []) {
|
|
36
|
+
if (typeof h.command === "string" && isMnemosHook(h.command)) {
|
|
37
|
+
existingCmd = h.command;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (existingCmd)
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
// skipIfExists (serve-mcp auto-install): preserve user-configured hooks but
|
|
45
|
+
// silently migrate legacy hooks that don't use @latest so they always stay fresh.
|
|
46
|
+
if (existingCmd && opts.skipIfExists) {
|
|
47
|
+
const alreadyLatest = existingCmd.includes("mnemos-capture@latest");
|
|
48
|
+
if (alreadyLatest)
|
|
49
|
+
return;
|
|
50
|
+
// Migration: preserve the existing --briefing flag when upgrading to @latest
|
|
51
|
+
opts = { ...opts, briefing: existingCmd.includes("--briefing") };
|
|
52
|
+
}
|
|
53
|
+
const command = opts.briefing
|
|
54
|
+
? `npx -y mnemos-capture@latest inbox-check --key ${apiKey} --briefing`
|
|
55
|
+
: `npx -y mnemos-capture@latest inbox-check --key ${apiKey}`;
|
|
56
|
+
// Remove any existing mnemos inbox-check hook (handles key rotation too)
|
|
57
|
+
const filtered = sessionStart
|
|
58
|
+
.map((group) => ({
|
|
59
|
+
...group,
|
|
60
|
+
hooks: (group.hooks ?? []).filter((h) => !(typeof h.command === "string" && isMnemosHook(h.command))),
|
|
61
|
+
}))
|
|
62
|
+
.filter((group) => (group.hooks ?? []).length > 0);
|
|
63
|
+
filtered.push({ hooks: [{ type: "command", command }] });
|
|
64
|
+
settings.hooks = { ...hooks, SessionStart: filtered };
|
|
65
|
+
writeSettings(settings);
|
|
66
|
+
if (!opts.silent) {
|
|
67
|
+
console.log("Mnemos inbox hook installed.");
|
|
68
|
+
console.log(`Location: ${CLAUDE_SETTINGS_PATH}`);
|
|
69
|
+
if (opts.briefing) {
|
|
70
|
+
console.log("At the start of each Claude Code session you'll see a full project briefing.");
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.log("At the start of each Claude Code session you'll see your inbox count.");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function run(cmd) {
|
|
78
|
+
return execSync(cmd, { timeout: 2000, stdio: ["pipe", "pipe", "pipe"] }).toString().trim();
|
|
79
|
+
}
|
|
80
|
+
function detectProjectContext() {
|
|
81
|
+
const parts = [];
|
|
82
|
+
// Repo name
|
|
83
|
+
try {
|
|
84
|
+
const remote = run("git remote get-url origin");
|
|
85
|
+
const match = remote.match(/\/([^/]+?)(?:\.git)?$/);
|
|
86
|
+
const repo = match?.[1] ?? run("git rev-parse --show-toplevel").split("/").pop();
|
|
87
|
+
if (repo)
|
|
88
|
+
parts.push(`Project: ${repo}`);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
const dir = process.cwd().split("/").pop();
|
|
92
|
+
if (dir)
|
|
93
|
+
parts.push(`Project: ${dir}`);
|
|
94
|
+
}
|
|
95
|
+
// Current branch
|
|
96
|
+
try {
|
|
97
|
+
const branch = run("git branch --show-current");
|
|
98
|
+
if (branch)
|
|
99
|
+
parts.push(`Branch: ${branch}`);
|
|
100
|
+
}
|
|
101
|
+
catch { }
|
|
102
|
+
// Recent commits (last 5)
|
|
103
|
+
try {
|
|
104
|
+
const log = run("git log --oneline -5");
|
|
105
|
+
if (log)
|
|
106
|
+
parts.push(`Recent commits:\n${log}`);
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
// CLAUDE.md — first 800 chars gives the model real project intent
|
|
110
|
+
try {
|
|
111
|
+
const root = run("git rev-parse --show-toplevel");
|
|
112
|
+
const claudeMd = readFileSync(join(root, "CLAUDE.md"), "utf-8").slice(0, 800).trim();
|
|
113
|
+
if (claudeMd)
|
|
114
|
+
parts.push(`Project instructions (CLAUDE.md excerpt):\n${claudeMd}`);
|
|
115
|
+
}
|
|
116
|
+
catch { }
|
|
117
|
+
return parts.length > 0 ? parts.join("\n\n") : "current project";
|
|
118
|
+
}
|
|
119
|
+
async function callMcp(apiKey, toolName, toolArgs) {
|
|
120
|
+
const res = await fetch(HOSTED_URL, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: {
|
|
123
|
+
"Content-Type": "application/json",
|
|
124
|
+
Authorization: `Bearer ${apiKey}`,
|
|
125
|
+
},
|
|
126
|
+
body: JSON.stringify({
|
|
127
|
+
jsonrpc: "2.0",
|
|
128
|
+
method: "tools/call",
|
|
129
|
+
params: { name: toolName, arguments: toolArgs },
|
|
130
|
+
id: 1,
|
|
131
|
+
}),
|
|
132
|
+
signal: AbortSignal.timeout(15000),
|
|
133
|
+
});
|
|
134
|
+
if (!res.ok)
|
|
135
|
+
return null;
|
|
136
|
+
const data = (await res.json());
|
|
137
|
+
return data.result?.content?.[0]?.text ?? null;
|
|
138
|
+
}
|
|
139
|
+
export async function inboxCheck(apiKey, opts = {}) {
|
|
140
|
+
try {
|
|
141
|
+
if (opts.briefing) {
|
|
142
|
+
const projectContext = detectProjectContext();
|
|
143
|
+
const text = await callMcp(apiKey, "briefing", { project_context: projectContext });
|
|
144
|
+
if (text?.trim())
|
|
145
|
+
process.stdout.write(text.trim() + "\n");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// Default: fast count via list_inbox (no LLM call)
|
|
149
|
+
const text = await callMcp(apiKey, "list_inbox", {});
|
|
150
|
+
if (!text)
|
|
151
|
+
return;
|
|
152
|
+
// list_inbox returns "N capture(s) in inbox:" or "Inbox is empty."
|
|
153
|
+
const match = text.match(/^(\d+)\s+capture/);
|
|
154
|
+
if (!match)
|
|
155
|
+
return;
|
|
156
|
+
const count = parseInt(match[1], 10);
|
|
157
|
+
if (count > 0) {
|
|
158
|
+
process.stdout.write(`Mnemos: ${count} capture${count === 1 ? "" : "s"} in inbox — run list_inbox to review\n`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Silent fail — never interrupt a session startup
|
|
163
|
+
}
|
|
164
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -8,11 +8,30 @@ async function main() {
|
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
10
10
|
if (command === "serve-mcp") {
|
|
11
|
-
// MCP server mode — proxies to hosted instance
|
|
12
11
|
const { serveMcp } = await import("./mcp-server.js");
|
|
13
12
|
await serveMcp();
|
|
14
13
|
return;
|
|
15
14
|
}
|
|
15
|
+
if (command === "setup-hooks") {
|
|
16
|
+
const { setupHooks } = await import("./hooks.js");
|
|
17
|
+
const keyIdx = args.indexOf("--key");
|
|
18
|
+
const apiKey = keyIdx !== -1 ? args[keyIdx + 1] : undefined;
|
|
19
|
+
if (!apiKey) {
|
|
20
|
+
console.error("Usage: npx mnemos-capture setup-hooks --key YOUR_API_KEY [--briefing]");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
setupHooks(apiKey, { briefing: args.includes("--briefing") });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (command === "inbox-check") {
|
|
27
|
+
const { inboxCheck } = await import("./hooks.js");
|
|
28
|
+
const keyIdx = args.indexOf("--key");
|
|
29
|
+
const apiKey = keyIdx !== -1 ? args[keyIdx + 1] : undefined;
|
|
30
|
+
if (!apiKey)
|
|
31
|
+
process.exit(0); // silent fail in hook context
|
|
32
|
+
await inboxCheck(apiKey, { briefing: args.includes("--briefing") });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
16
35
|
// Default: open the hosted app
|
|
17
36
|
console.log("");
|
|
18
37
|
console.log(" Mnemos — Knowledge capture for agentic workflows\n");
|
|
@@ -21,7 +40,7 @@ async function main() {
|
|
|
21
40
|
console.log(" First time? Sign in with GitHub — setup takes 30 seconds.");
|
|
22
41
|
console.log("");
|
|
23
42
|
console.log(" Connect to Claude Code:");
|
|
24
|
-
console.log(" claude mcp add mnemos -- npx mnemos-capture serve-mcp --key YOUR_API_KEY");
|
|
43
|
+
console.log(" claude mcp add mnemos -- npx -y mnemos-capture@latest serve-mcp --key YOUR_API_KEY");
|
|
25
44
|
console.log("");
|
|
26
45
|
// Try to open the URL in the default browser
|
|
27
46
|
const { exec } = await import("child_process");
|
|
@@ -33,9 +52,11 @@ function printHelp() {
|
|
|
33
52
|
Mnemos — Knowledge capture for agentic workflows
|
|
34
53
|
|
|
35
54
|
Usage:
|
|
36
|
-
npx mnemos-capture
|
|
37
|
-
npx mnemos-capture serve-mcp
|
|
38
|
-
npx mnemos-capture
|
|
55
|
+
npx mnemos-capture Open Mnemos in your browser
|
|
56
|
+
npx mnemos-capture serve-mcp --key KEY Start the MCP server for Claude Code
|
|
57
|
+
npx mnemos-capture setup-hooks --key KEY Install inbox count hook (fast)
|
|
58
|
+
npx mnemos-capture setup-hooks --key KEY --briefing Install full briefing hook (uses LLM)
|
|
59
|
+
npx mnemos-capture help Show this help
|
|
39
60
|
|
|
40
61
|
Get started:
|
|
41
62
|
1. Run: npx mnemos-capture
|
|
@@ -44,7 +65,15 @@ function printHelp() {
|
|
|
44
65
|
4. Start capturing!
|
|
45
66
|
|
|
46
67
|
Connect to Claude Code:
|
|
47
|
-
claude mcp add mnemos -- npx mnemos-capture serve-mcp --key YOUR_API_KEY
|
|
68
|
+
claude mcp add mnemos -- npx -y mnemos-capture@latest serve-mcp --key YOUR_API_KEY
|
|
69
|
+
|
|
70
|
+
The @latest tag ensures every Claude Code session uses the most recent
|
|
71
|
+
published version. serve-mcp also self-upgrades on startup as a safety net
|
|
72
|
+
for users who registered without @latest.
|
|
73
|
+
|
|
74
|
+
The hook is installed automatically when you start the MCP server.
|
|
75
|
+
To upgrade to a full project briefing at session start:
|
|
76
|
+
npx mnemos-capture setup-hooks --key YOUR_API_KEY --briefing
|
|
48
77
|
`);
|
|
49
78
|
}
|
|
50
79
|
main().catch((err) => {
|
package/dist/cli/mcp-server.js
CHANGED
|
@@ -33,10 +33,30 @@ export async function serveMcp() {
|
|
|
33
33
|
const apiKey = keyIdx !== -1 ? args[keyIdx + 1] : undefined;
|
|
34
34
|
if (!apiKey) {
|
|
35
35
|
process.stderr.write("\nMnemos MCP server requires an API key.\n");
|
|
36
|
-
process.stderr.write("Usage: npx mnemos-capture serve-mcp --key <your-api-key>\n\n");
|
|
36
|
+
process.stderr.write("Usage: npx -y mnemos-capture@latest serve-mcp --key <your-api-key>\n\n");
|
|
37
37
|
process.stderr.write("Get your API key at: https://mnemos-capture.vercel.app/onboard\n\n");
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
|
+
// Self-upgrade: if a newer version exists on npm, respawn via npx @latest.
|
|
41
|
+
// Handled before anything else so stdio isn't yet owned by the MCP loop.
|
|
42
|
+
try {
|
|
43
|
+
const { maybeSelfUpgrade } = await import("./version-check.js");
|
|
44
|
+
const upgrading = await maybeSelfUpgrade();
|
|
45
|
+
if (upgrading)
|
|
46
|
+
return; // child has taken over; parent will exit when child does
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Version check must never block startup
|
|
50
|
+
}
|
|
51
|
+
// Auto-install the inbox hook the first time the MCP server is registered.
|
|
52
|
+
// Uses silent mode so it never prints to stdout (which is reserved for MCP protocol).
|
|
53
|
+
try {
|
|
54
|
+
const { setupHooks } = await import("./hooks.js");
|
|
55
|
+
setupHooks(apiKey, { silent: true, skipIfExists: true });
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Non-fatal — hook setup should never block the MCP server from starting
|
|
59
|
+
}
|
|
40
60
|
process.stderr.write("Mnemos MCP server starting (proxying to hosted instance)...\n");
|
|
41
61
|
let buffer = "";
|
|
42
62
|
process.stdin.setEncoding("utf-8");
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Self-update enforcement: ensures users always run the latest mnemos-capture.
|
|
2
|
+
//
|
|
3
|
+
// Strategy: on serve-mcp startup, query npm for the latest published version.
|
|
4
|
+
// If behind, respawn the process via `npx -y mnemos-capture@latest` and proxy
|
|
5
|
+
// stdio. A MNEMOS_UPGRADED=1 env var prevents infinite recursion.
|
|
6
|
+
import { readFileSync } from "fs";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { dirname, join } from "path";
|
|
9
|
+
import { spawn } from "child_process";
|
|
10
|
+
const NPM_REGISTRY = "https://registry.npmjs.org/mnemos-capture/latest";
|
|
11
|
+
const CHECK_TIMEOUT_MS = 2000;
|
|
12
|
+
export function getCurrentVersion() {
|
|
13
|
+
try {
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
// dist/cli/version-check.js → ../../package.json
|
|
16
|
+
const pkg = JSON.parse(readFileSync(join(here, "../../package.json"), "utf-8"));
|
|
17
|
+
return pkg.version ?? "0.0.0";
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return "0.0.0";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function isNewer(latest, current) {
|
|
24
|
+
const l = latest.split(".").map((n) => parseInt(n, 10) || 0);
|
|
25
|
+
const c = current.split(".").map((n) => parseInt(n, 10) || 0);
|
|
26
|
+
for (let i = 0; i < 3; i++) {
|
|
27
|
+
const lv = l[i] ?? 0;
|
|
28
|
+
const cv = c[i] ?? 0;
|
|
29
|
+
if (lv > cv)
|
|
30
|
+
return true;
|
|
31
|
+
if (lv < cv)
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
async function getLatestVersion() {
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch(NPM_REGISTRY, { signal: AbortSignal.timeout(CHECK_TIMEOUT_MS) });
|
|
39
|
+
if (!res.ok)
|
|
40
|
+
return null;
|
|
41
|
+
const data = (await res.json());
|
|
42
|
+
return data.version ?? null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* If a newer version exists on npm, respawn via `npx -y mnemos-capture@latest`
|
|
50
|
+
* with the original argv, piping stdio. Returns true if the caller should stop
|
|
51
|
+
* (because the child is taking over). Returns false otherwise — caller should
|
|
52
|
+
* continue with normal startup.
|
|
53
|
+
*/
|
|
54
|
+
export async function maybeSelfUpgrade() {
|
|
55
|
+
// Recursion guard: the child spawned below will inherit this env var.
|
|
56
|
+
if (process.env.MNEMOS_UPGRADED === "1")
|
|
57
|
+
return false;
|
|
58
|
+
const current = getCurrentVersion();
|
|
59
|
+
const latest = await getLatestVersion();
|
|
60
|
+
if (!latest || !isNewer(latest, current))
|
|
61
|
+
return false;
|
|
62
|
+
process.stderr.write(`Mnemos: upgrading to v${latest} (was v${current})...\n`);
|
|
63
|
+
const child = spawn("npx", ["-y", `mnemos-capture@${latest}`, ...process.argv.slice(2)], {
|
|
64
|
+
stdio: "inherit",
|
|
65
|
+
env: { ...process.env, MNEMOS_UPGRADED: "1" },
|
|
66
|
+
});
|
|
67
|
+
// Keep parent alive until the child exits, then exit with its code.
|
|
68
|
+
await new Promise((resolve) => {
|
|
69
|
+
child.on("exit", (code) => {
|
|
70
|
+
process.exit(code ?? 0);
|
|
71
|
+
resolve();
|
|
72
|
+
});
|
|
73
|
+
child.on("error", () => {
|
|
74
|
+
// If spawn itself fails, fall through and let the parent continue
|
|
75
|
+
resolve();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
return true;
|
|
79
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mnemos-capture",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Zero-friction knowledge capture for agentic workflows. Paste anything → Claude extracts insights → auto-commits to your knowledge repo → your Claude Code workflow picks it up.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|