donguri-journal 0.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/LICENSE +21 -0
- package/README.ja.md +181 -0
- package/README.md +184 -0
- package/dist/db/schema.d.ts +21 -0
- package/dist/db/schema.js +53 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/store.d.ts +136 -0
- package/dist/db/store.js +0 -0
- package/dist/db/store.js.map +1 -0
- package/dist/embedding/provider.d.ts +37 -0
- package/dist/embedding/provider.js +53 -0
- package/dist/embedding/provider.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel/config.d.ts +17 -0
- package/dist/kernel/config.js +26 -0
- package/dist/kernel/config.js.map +1 -0
- package/dist/kernel/context.d.ts +26 -0
- package/dist/kernel/context.js +7 -0
- package/dist/kernel/context.js.map +1 -0
- package/dist/kernel/module.d.ts +13 -0
- package/dist/kernel/module.js +7 -0
- package/dist/kernel/module.js.map +1 -0
- package/dist/kernel/plugin.d.ts +68 -0
- package/dist/kernel/plugin.js +144 -0
- package/dist/kernel/plugin.js.map +1 -0
- package/dist/kernel/result.d.ts +14 -0
- package/dist/kernel/result.js +11 -0
- package/dist/kernel/result.js.map +1 -0
- package/dist/management/module.d.ts +2 -0
- package/dist/management/module.js +39 -0
- package/dist/management/module.js.map +1 -0
- package/dist/management/server.d.ts +18 -0
- package/dist/management/server.js +216 -0
- package/dist/management/server.js.map +1 -0
- package/dist/management/ui.d.ts +10 -0
- package/dist/management/ui.js +159 -0
- package/dist/management/ui.js.map +1 -0
- package/dist/modules/core.d.ts +2 -0
- package/dist/modules/core.js +386 -0
- package/dist/modules/core.js.map +1 -0
- package/dist/modules/plugins.d.ts +2 -0
- package/dist/modules/plugins.js +177 -0
- package/dist/modules/plugins.js.map +1 -0
- package/dist/originals/store.d.ts +50 -0
- package/dist/originals/store.js +185 -0
- package/dist/originals/store.js.map +1 -0
- package/dist/review/charts.d.ts +16 -0
- package/dist/review/charts.js +69 -0
- package/dist/review/charts.js.map +1 -0
- package/dist/review/patterns.d.ts +33 -0
- package/dist/review/patterns.js +73 -0
- package/dist/review/patterns.js.map +1 -0
- package/dist/review/review.d.ts +30 -0
- package/dist/review/review.js +82 -0
- package/dist/review/review.js.map +1 -0
- package/dist/review/window.d.ts +18 -0
- package/dist/review/window.js +57 -0
- package/dist/review/window.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nemutame
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# 🐿️ donguri-journal
|
|
2
|
+
|
|
3
|
+
[English](README.md) | **日本語**
|
|
4
|
+
|
|
5
|
+
> ローカルファースト・時間軸対応の「**記憶**」MCP サーバー(AI エージェント向け)。
|
|
6
|
+
|
|
7
|
+
リスは掘り返すよりずっと多くのドングリ(donguri)を埋めます——ためらわず、ひたすら
|
|
8
|
+
貯め込む。donguri-journal も同じ姿勢です。マルチモーダル LLM(Claude など)が
|
|
9
|
+
コンパニオン兼 UI となり、このサーバーはその背後にある永続的な「記憶器官」。核となる
|
|
10
|
+
仕事は、会話の流れの中で気軽に **capture(捕捉)** し何ひとつ失わないこと、そして時間を
|
|
11
|
+
越えて **recall(想起)** できるようにすることです。貯め込んだ山を*うまく*掘り返すこと
|
|
12
|
+
——より豊かな振り返り・再浮上・新しい切り口——はもっと難しく開かれた部分で、そこを
|
|
13
|
+
**プラグイン** が拡張します。
|
|
14
|
+
|
|
15
|
+
> 設計思想と全体ロードマップは **[docs/DESIGN.ja.md](docs/DESIGN.ja.md)** にあります。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## これは何か
|
|
20
|
+
|
|
21
|
+
- **ローカルファースト。** すべてが手元の SQLite ファイル1つ+ローカルの originals
|
|
22
|
+
ディレクトリに収まります。クラウド不要・アカウント不要。
|
|
23
|
+
- **時間が第一級。** すべてのエントリが `created_at`(捕捉した時刻)と `occurred_at`
|
|
24
|
+
(出来事が実際に起きた時刻)の両方を持ちます。「3か月前は何を考えていた?」週次/月次
|
|
25
|
+
レビュー、BuJo 的なマイグレーションなど、人間の振り返りのための設計です。
|
|
26
|
+
- **マルチモーダルは委譲。** サーバーは vision/音声モデルを動かしません。あなたの
|
|
27
|
+
マルチモーダル LLM が画像/音声/URL から忠実なテキストを抽出して渡し、**原本のバイト
|
|
28
|
+
はそのまま保存**されます(破壊しません)。
|
|
29
|
+
- **ゼロセットアップの埋め込み。** 意味検索はインプロセスの
|
|
30
|
+
[transformers.js](https://github.com/xenova/transformers.js)
|
|
31
|
+
(`Xenova/all-MiniLM-L6-v2`, 384 次元)で標準動作。Ollama も手動のモデル取得も不要。
|
|
32
|
+
バックエンドは上級者向けに差し替え可能です。
|
|
33
|
+
|
|
34
|
+
> **現状:** Phase 1(capture / recall)+ Phase 1.5(レビュー/インサイト)、ローカル
|
|
35
|
+
> 原本保存、エントリ管理、プラグイン読み込み、そして**読み取り専用の管理コンソール**が
|
|
36
|
+
> 実装済み。UI からの削除/エクスポート、アルバム表示、キュレーション済みプラグイン
|
|
37
|
+
> レジストリ、ローカルファースト同期は計画中です——[docs/DESIGN.md](docs/DESIGN.md) を参照。
|
|
38
|
+
|
|
39
|
+
## 必要環境
|
|
40
|
+
|
|
41
|
+
- **Node.js 22 以上**
|
|
42
|
+
- **MCP を話せるマルチモーダル LLM クライアント**(例: Claude Desktop)。これは必須
|
|
43
|
+
要件です——サーバー自体は UI を持たず、メディアの処理もしません。
|
|
44
|
+
|
|
45
|
+
## セットアップ
|
|
46
|
+
|
|
47
|
+
### AI エージェントで導入(CLI 不要)
|
|
48
|
+
|
|
49
|
+
donguri-journal は AI エージェント向けの MCP サーバーです——だからセットアップも
|
|
50
|
+
エージェントに任せましょう。シェルを実行しファイルを編集できるコーディングエージェント
|
|
51
|
+
(例: **Claude Code**、またはファイル/ターミナル権限を与えた **Claude Desktop**)に、
|
|
52
|
+
下のプロンプトを貼り付けてください。クローン→ビルド→MCP クライアントへの登録→動作確認
|
|
53
|
+
までを代行します。あなたはコマンドを一切打ちません。
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
私のマシンに「donguri-journal」(ローカルファーストのジャーナル記憶 MCP サーバー)を
|
|
57
|
+
最初から最後までセットアップして。ターミナルコマンドは私に打たせず、あなたが実行して。
|
|
58
|
+
|
|
59
|
+
1. Node.js 22 以上が使えることを確認(無ければ nvm で入れる)。
|
|
60
|
+
2. https://github.com/nemutame/donguri-journal を ~/tools/donguri-journal のような
|
|
61
|
+
安定した場所にクローン(既にあれば git pull)。
|
|
62
|
+
3. そのディレクトリで実行: npm ci && npm run build (dist/index.js が生成される)。
|
|
63
|
+
4. 既存のサーバー設定を保ったまま、私の MCP クライアントに登録:
|
|
64
|
+
- Claude Desktop — 設定 JSON を編集し、"mcpServers" の下に "donguri-journal" を追加。
|
|
65
|
+
"command": "node"、"args": ["<絶対パス>/dist/index.js"]:
|
|
66
|
+
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
67
|
+
Windows: %APPDATA%\Claude\claude_desktop_config.json
|
|
68
|
+
クライアントがシェルの PATH を引き継がない場合(nvm で頻発)は、"node" の代わりに
|
|
69
|
+
node バイナリの絶対パスを使う。
|
|
70
|
+
- Claude Code — 実行: claude mcp add donguri-journal -- node <絶対パス>/dist/index.js
|
|
71
|
+
5. MCP クライアントを完全に再起動するよう私に伝える。そのあと、短いテストメモを capture
|
|
72
|
+
して recall し動作確認して。初回 capture 時に埋め込みモデル(約 90 MB)が一度だけ
|
|
73
|
+
ダウンロードされる(ネットワーク必要)。以降はすべてローカルで動く。
|
|
74
|
+
|
|
75
|
+
すべて私のマシン内で完結させて。デプロイやデータの外部送信はしないで。
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 手動セットアップ
|
|
79
|
+
|
|
80
|
+
自分で行いたい場合は、ローカルチェックアウトからビルドします。
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm ci
|
|
84
|
+
npm run build
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
そのうえで MCP クライアントに登録します。例(Claude Desktop の
|
|
88
|
+
`claude_desktop_config.json`):
|
|
89
|
+
|
|
90
|
+
```jsonc
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"donguri-journal": {
|
|
94
|
+
"command": "node",
|
|
95
|
+
"args": ["/absolute/path/to/donguri-journal/dist/index.js"]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
初回利用時に埋め込みモデル(約 90 MB)が自動でダウンロード・キャッシュされます
|
|
102
|
+
(一度だけネットワークが必要)。MCP クライアントがシェルの `PATH` を引き継がない場合
|
|
103
|
+
(nvm でよく起きます)は、`node` を絶対パスで指定してください。例:
|
|
104
|
+
`/home/you/.nvm/versions/node/v22.x.y/bin/node`
|
|
105
|
+
|
|
106
|
+
### 設定
|
|
107
|
+
|
|
108
|
+
| 環境変数 | 既定値 | 意味 |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `JOURNAL_DB_PATH` | `~/.journal-mcp/journal.db` | SQLite データベースファイルのパス。 |
|
|
111
|
+
| `JOURNAL_ORIGINALS_DIR` | `~/.journal-mcp/originals` | 原本(画像/音声/ファイル)を content-addressed で保存するディレクトリ。 |
|
|
112
|
+
| `JOURNAL_MAX_ORIGINAL_BYTES` | `26214400`(25 MiB) | 1 つの原本の最大サイズ。これを超える `original_data` は拒否されます。 |
|
|
113
|
+
| `JOURNAL_PLUGINS_DIR` | `~/.journal-mcp/plugins` | 導入済みプラグインを置くディレクトリ(プラグインごとにサブディレクトリ)。 |
|
|
114
|
+
| `JOURNAL_PLUGINS_CONFIG` | `~/.journal-mcp/plugins.json` | どのプラグインが導入/有効かを記録する JSON ファイル。 |
|
|
115
|
+
|
|
116
|
+
`stdout` は MCP プロトコル専用です。ログはすべて `stderr` に出力されます。
|
|
117
|
+
|
|
118
|
+
## ツール
|
|
119
|
+
|
|
120
|
+
ツールの説明文は、フロントエンド LLM への指示書として書かれています(いつ各ツールを
|
|
121
|
+
呼ぶか)。
|
|
122
|
+
|
|
123
|
+
| ツール | 役割 |
|
|
124
|
+
| --- | --- |
|
|
125
|
+
| `capture` | いま記憶を貯める。低摩擦。メディアの場合、LLM は抽出テキストに加えて原本のバイト(`original_data`)を渡し、サーバーがそのまま保存。自動で重複排除。 |
|
|
126
|
+
| `query_entries` | 日付範囲 / タグ / 種別による**構造化**検索。正確で絞り込み可能な問いやレビュー向け。 |
|
|
127
|
+
| `recall_related` | **意味**ベクトル検索。言い回しが違っても、意味的に関連する過去のエントリを見つける。 |
|
|
128
|
+
| `generate_review` | 日 / 週 / 月(または任意範囲)の振り返り。**PNG のアクティビティチャート**+構造化集計(合計・最多日・種別・上位タグ)+提示ヒントを返す。 |
|
|
129
|
+
| `surface_patterns` | 再発テーマ——最近のエントリが**過去のどれと「こだま」するか**。距離付きのクラスタ+ PNG チャート+提示ヒントを返す。 |
|
|
130
|
+
| `get_original` | `original_ref` で保存済みの原本を取得。画像はインラインで返し(LLM が再閲覧・再抽出可能)、それ以外はメタデータのみ返す。 |
|
|
131
|
+
| `reindex` | 保守——現在の埋め込みバックエンドで原本からベクトルインデックスを再構築。バックエンド変更後に実行(不一致時は起動時に警告)。原本は一切触らない。 |
|
|
132
|
+
| `storage_stats` | 容量: エントリ数(有効/ソフト削除)・ベクトル数・種別/月別・原本の件数とバイト・DB サイズ。 |
|
|
133
|
+
| `delete_entry` | エントリ削除——`mode: soft`(復元可能な tombstone)/ `hard`(entry+ベクトル+孤児原本を完全消去し VACUUM)。 |
|
|
134
|
+
| `open_management_ui` | 所有者が LLM 会話の外で直接、閲覧・フィルタ・意味検索・容量統計を見るための **localhost 限定** Web コンソールを起動。ブラウザで開くトークン付き URL を返す。 |
|
|
135
|
+
| `list_installed_plugins` | 導入済みプラグインを一覧(有効状態・バージョン・宣言ケイパビリティ)。 |
|
|
136
|
+
| `install_plugin` | ローカルのプラグインを導入。2段階: 提案(マニフェスト+権限を確認)→ `confirm: true`。再起動なしで即有効。 |
|
|
137
|
+
| `uninstall_plugin` | 導入済みプラグインをディスクとレジストリから削除。既に登録済みのツールはサーバー再起動まで残ります。 |
|
|
138
|
+
|
|
139
|
+
`query_entries` と `recall_related` は意図的に別経路です(LLM が問いに応じて、正確な
|
|
140
|
+
絞り込みか意味かを選ぶ)。`generate_review` と `surface_patterns` は、描画済みの PNG
|
|
141
|
+
チャートに加えて構造化データと提示ヒントを返すので、LLM は素の一覧ではなく豊かな
|
|
142
|
+
振り返りとして提示できます。
|
|
143
|
+
|
|
144
|
+
## 保存のしくみ
|
|
145
|
+
|
|
146
|
+
2 層構造により、インデックスは常に再構築可能で、原本は失われません。
|
|
147
|
+
|
|
148
|
+
- **`entries`** — インデックス対象テキスト(`body`)、原本への参照(`original_ref`)、
|
|
149
|
+
各種タイムスタンプ、タグ、メタデータ。`extraction_state` は `body` の生成方法を記録し、
|
|
150
|
+
ロスのある抽出を後でやり直せるようにします。
|
|
151
|
+
- **`vec_entries`** — 使い捨ての
|
|
152
|
+
[sqlite-vec](https://github.com/asg017/sqlite-vec) ベクトルインデックス。有効な埋め込み
|
|
153
|
+
モデル/次元を記録し、バックエンド切替時に再インデックスを促せます。
|
|
154
|
+
- **originals(原本)** — LLM が原本のバイトを送ると、ローカルの content-addressed
|
|
155
|
+
ストア(`OriginalStore`、既定はローカルディレクトリ)にそのまま保存され、
|
|
156
|
+
`original_ref` がそれを指します。バックエンドは差し替え可能で、サーバーは中身を
|
|
157
|
+
解釈しません。埋め込みは常に抽出テキストから作られ、メディア自体からは作りません。
|
|
158
|
+
|
|
159
|
+
## コントリビュート
|
|
160
|
+
|
|
161
|
+
歓迎します——**日本語での Issue / PR でも構いません**。大きめの変更を提案する前に、
|
|
162
|
+
設計意図を [docs/DESIGN.ja.md](docs/DESIGN.ja.md)(英語版: [docs/DESIGN.md](docs/DESIGN.md))で確認してください。
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npm run lint # Biome(lint + フォーマットチェック)
|
|
166
|
+
npm run lint:fix # 自動修正
|
|
167
|
+
npm run typecheck # tsc(src + テスト)
|
|
168
|
+
npm test # tsx 経由の node:test
|
|
169
|
+
npm run build # tsc -> dist/
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
ワークフロー:
|
|
173
|
+
|
|
174
|
+
- Node 22 は `.nvmrc` で固定(`nvm use`)。
|
|
175
|
+
- `main` は保護されています——ブランチを切って Pull Request を作成してください。
|
|
176
|
+
- すべての PR は **CI**(lint + typecheck + build + テスト)と **CodeRabbit** レビューでゲート
|
|
177
|
+
され、両方の通過後にマージできます。
|
|
178
|
+
|
|
179
|
+
## ライセンス
|
|
180
|
+
|
|
181
|
+
[MIT](./LICENSE) © Nemutame
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# 🐿️ donguri-journal
|
|
2
|
+
|
|
3
|
+
**English** | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
> A local-first, time-aware journaling **memory** server for AI agents, over MCP.
|
|
6
|
+
|
|
7
|
+
A squirrel buries far more acorns (*donguri*) than it ever digs back up — it hoards
|
|
8
|
+
without hesitation. donguri-journal takes the same stance: a multimodal LLM (Claude,
|
|
9
|
+
etc.) is the companion and UI, and this server is the persistent memory organ behind
|
|
10
|
+
it. Its core job is to **capture** everything casually in the flow of conversation and
|
|
11
|
+
never lose it, then let you **recall** it across time. Digging the pile back up
|
|
12
|
+
*well* — richer review, resurfacing, new lenses on the hoard — is the harder,
|
|
13
|
+
open-ended part, and that's what **plugins** extend.
|
|
14
|
+
|
|
15
|
+
> Design rationale and the full roadmap live in **[docs/DESIGN.md](docs/DESIGN.md)**.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What it is
|
|
20
|
+
|
|
21
|
+
- **Local-first.** Everything lives in a single SQLite file plus a local originals
|
|
22
|
+
directory on your machine. No cloud, no account.
|
|
23
|
+
- **Time-aware.** Every entry has both `created_at` (when captured) and `occurred_at`
|
|
24
|
+
(when the event actually happened) — built for human reflection: "what was I
|
|
25
|
+
thinking 3 months ago?", weekly/monthly review, BuJo-style migration.
|
|
26
|
+
- **Multimodal by delegation.** The server never runs vision/audio models. Your
|
|
27
|
+
multimodal LLM extracts faithful text from images/audio/URLs and passes it in; the
|
|
28
|
+
original bytes are stored verbatim, never destroyed.
|
|
29
|
+
- **Zero-setup embeddings.** Semantic search works out of the box via in-process
|
|
30
|
+
[transformers.js](https://github.com/xenova/transformers.js)
|
|
31
|
+
(`Xenova/all-MiniLM-L6-v2`, 384-dim). No Ollama, no manual model pull. The backend
|
|
32
|
+
is swappable for power users.
|
|
33
|
+
|
|
34
|
+
> **Status:** Phase 1 (capture / recall) + Phase 1.5 (review / insight), local
|
|
35
|
+
> originals storage, entry management, plugin loading, and a **read-only management
|
|
36
|
+
> console** are implemented. UI-driven delete / export, an album view, a curated
|
|
37
|
+
> plugin registry, and local-first sync are planned — see
|
|
38
|
+
> [docs/DESIGN.md](docs/DESIGN.md).
|
|
39
|
+
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- **Node.js 22+**
|
|
43
|
+
- A **multimodal LLM client that speaks MCP** (e.g. Claude Desktop). This is a hard
|
|
44
|
+
requirement — the server has no UI of its own and does not process media itself.
|
|
45
|
+
|
|
46
|
+
## Setup
|
|
47
|
+
|
|
48
|
+
### Install with an AI agent (no CLI needed)
|
|
49
|
+
|
|
50
|
+
donguri-journal is an MCP server for AI agents — so let an agent set it up. Paste the
|
|
51
|
+
prompt below to any coding agent that can run a shell and edit files on your machine
|
|
52
|
+
(e.g. **Claude Code**, or **Claude Desktop** with filesystem/terminal access). It will
|
|
53
|
+
clone, build, register the server with your MCP client, and verify it — you don't type
|
|
54
|
+
any commands yourself.
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
Set up "donguri-journal", a local-first journaling memory MCP server, on my machine,
|
|
58
|
+
end to end. Don't ask me to run terminal commands — you run them.
|
|
59
|
+
|
|
60
|
+
1. Make sure Node.js 22+ is available (install it via nvm if it's missing).
|
|
61
|
+
2. Clone https://github.com/nemutame/donguri-journal into a stable location such as
|
|
62
|
+
~/tools/donguri-journal (if it already exists, git pull instead).
|
|
63
|
+
3. In that directory run: npm ci && npm run build (this produces dist/index.js).
|
|
64
|
+
4. Register it with my MCP client, preserving any servers already configured:
|
|
65
|
+
- Claude Desktop — edit the config JSON and add a "donguri-journal" entry under
|
|
66
|
+
"mcpServers" with "command": "node" and "args": ["<ABS_PATH>/dist/index.js"]:
|
|
67
|
+
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
68
|
+
Windows: %APPDATA%\Claude\claude_desktop_config.json
|
|
69
|
+
If the client doesn't inherit my shell PATH (common with nvm), use the absolute
|
|
70
|
+
path to the node binary instead of "node".
|
|
71
|
+
- Claude Code — run: claude mcp add donguri-journal -- node <ABS_PATH>/dist/index.js
|
|
72
|
+
5. Tell me to fully restart the MCP client. Then verify by capturing a short test note
|
|
73
|
+
and recalling it — the first capture downloads a ~90 MB embedding model once
|
|
74
|
+
(needs network), after which everything runs locally.
|
|
75
|
+
|
|
76
|
+
Keep everything on my machine: don't deploy anything or send my data anywhere.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Manual setup
|
|
80
|
+
|
|
81
|
+
Prefer to do it yourself? Build from a local checkout:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npm ci
|
|
85
|
+
npm run build
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Then register it with your MCP client. Example (Claude Desktop,
|
|
89
|
+
`claude_desktop_config.json`):
|
|
90
|
+
|
|
91
|
+
```jsonc
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"donguri-journal": {
|
|
95
|
+
"command": "node",
|
|
96
|
+
"args": ["/absolute/path/to/donguri-journal/dist/index.js"]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
On first use the embedding model (~90 MB) is downloaded and cached automatically
|
|
103
|
+
(needs network once). If your MCP client does not inherit your shell `PATH` (common
|
|
104
|
+
with nvm), use an absolute path to `node`, e.g.
|
|
105
|
+
`/home/you/.nvm/versions/node/v22.x.y/bin/node`.
|
|
106
|
+
|
|
107
|
+
### Configuration
|
|
108
|
+
|
|
109
|
+
| Env var | Default | Meaning |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| `JOURNAL_DB_PATH` | `~/.journal-mcp/journal.db` | Path to the SQLite database file. |
|
|
112
|
+
| `JOURNAL_ORIGINALS_DIR` | `~/.journal-mcp/originals` | Directory where original artifacts (images/audio/files) are stored, content-addressed. |
|
|
113
|
+
| `JOURNAL_MAX_ORIGINAL_BYTES` | `26214400` (25 MiB) | Max accepted size of a single original artifact; larger `original_data` is rejected. |
|
|
114
|
+
| `JOURNAL_PLUGINS_DIR` | `~/.journal-mcp/plugins` | Directory where installed plugins live (one subdirectory per plugin). |
|
|
115
|
+
| `JOURNAL_PLUGINS_CONFIG` | `~/.journal-mcp/plugins.json` | JSON file recording which plugins are installed / enabled. |
|
|
116
|
+
|
|
117
|
+
`stdout` is reserved for the MCP protocol; all logs go to `stderr`.
|
|
118
|
+
|
|
119
|
+
## Tools
|
|
120
|
+
|
|
121
|
+
The tool descriptions are written as instructions for the front-end LLM (when to call
|
|
122
|
+
each).
|
|
123
|
+
|
|
124
|
+
| Tool | Purpose |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `capture` | Stash a memory now. Low-friction; for media, the LLM passes extracted text plus the raw original bytes (`original_data`), which the server stores verbatim. Auto-deduplicated. |
|
|
127
|
+
| `query_entries` | **Structured** lookup by date range / tag / source kind. For precise, filterable questions and reviews. |
|
|
128
|
+
| `recall_related` | **Semantic** vector search — find past entries related in meaning, even with different wording. |
|
|
129
|
+
| `generate_review` | Reflective review of a day / week / month (or custom range). Returns a **PNG activity chart** + structured aggregates (totals, busiest day, source kinds, top tags) + presentation hints. |
|
|
130
|
+
| `surface_patterns` | Recurring themes — recent entries that **echo earlier ones**. Returns echo clusters with distances + a PNG chart + presentation hints. |
|
|
131
|
+
| `get_original` | Fetch a stored original artifact by its `original_ref`. Images are returned inline so the LLM can re-view / re-extract; other types return metadata only. |
|
|
132
|
+
| `reindex` | Maintenance — rebuild the vector index from the originals using the current embedding backend. Run after switching the embedding backend (the server warns on startup when the index no longer matches). Originals are never touched. |
|
|
133
|
+
| `storage_stats` | Capacity: entry counts (active vs soft-deleted), vectors, breakdown by source kind / month, originals count + bytes, and DB size. |
|
|
134
|
+
| `delete_entry` | Delete an entry — `mode: soft` (recoverable tombstone) or `hard` (permanent purge of entry + vector + orphaned original, with VACUUM). |
|
|
135
|
+
| `open_management_ui` | Start a **localhost-only** web console for the owner to browse, filter, semantically recall, and see storage stats directly — outside the LLM conversation. Returns a token-bearing URL to open in a browser. |
|
|
136
|
+
| `list_installed_plugins` | List installed plugins with their enabled state, version, and declared capabilities. |
|
|
137
|
+
| `install_plugin` | Install a local plugin. Two-step: propose (see manifest + capabilities), then `confirm: true`. Loads immediately — no restart. |
|
|
138
|
+
| `uninstall_plugin` | Remove an installed plugin from disk and the registry. Tools it already registered stay available until the server restarts. |
|
|
139
|
+
|
|
140
|
+
`query_entries` and `recall_related` are intentionally separate retrieval paths; the
|
|
141
|
+
LLM picks based on the question (precise filter vs. meaning). `generate_review` and
|
|
142
|
+
`surface_patterns` return rendered PNG charts alongside structured data and
|
|
143
|
+
presentation hints, so the LLM can present a rich, reflective summary rather than a
|
|
144
|
+
bare list.
|
|
145
|
+
|
|
146
|
+
## How it stores things
|
|
147
|
+
|
|
148
|
+
Two layers, so the index is always rebuildable and originals are never lost:
|
|
149
|
+
|
|
150
|
+
- **`entries`** — the indexed text (`body`), a pointer to the verbatim original
|
|
151
|
+
(`original_ref`), timestamps, tags, and metadata. `extraction_state` records how
|
|
152
|
+
`body` was produced, so lossy extraction can be redone later.
|
|
153
|
+
- **`vec_entries`** — a disposable [sqlite-vec](https://github.com/asg017/sqlite-vec)
|
|
154
|
+
vector index. The active embedding model/dim is recorded so switching backends can
|
|
155
|
+
trigger a reindex.
|
|
156
|
+
- **originals** — when the LLM sends an artifact's bytes, they're saved verbatim in a
|
|
157
|
+
local content-addressed store (`OriginalStore`, default: a local directory), and
|
|
158
|
+
`original_ref` points at them. The backend is pluggable; the server never interprets
|
|
159
|
+
the bytes. Embeddings are always made from the extracted text, never the media
|
|
160
|
+
itself.
|
|
161
|
+
|
|
162
|
+
## Contributing
|
|
163
|
+
|
|
164
|
+
Contributions are welcome — **issues and PRs in Japanese are fine too**. See
|
|
165
|
+
[docs/DESIGN.md](docs/DESIGN.md) for the design intent before proposing larger changes.
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npm run lint # Biome (lint + format check)
|
|
169
|
+
npm run lint:fix # auto-fix
|
|
170
|
+
npm run typecheck # tsc (src + tests)
|
|
171
|
+
npm test # node:test via tsx
|
|
172
|
+
npm run build # tsc -> dist/
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Workflow:
|
|
176
|
+
|
|
177
|
+
- Node 22 is pinned via `.nvmrc` (`nvm use`).
|
|
178
|
+
- `main` is protected — work on a branch and open a pull request.
|
|
179
|
+
- Every PR is gated by **CI** (lint + typecheck + build + tests) and a **CodeRabbit** review;
|
|
180
|
+
both must pass before merge.
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
[MIT](./LICENSE) © Nemutame
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database schema.
|
|
3
|
+
*
|
|
4
|
+
* Two layers:
|
|
5
|
+
* - `entries` keeps the indexed text (`body`) plus a pointer to the verbatim
|
|
6
|
+
* original (`original_ref`). Extraction is lossy and non-final, so
|
|
7
|
+
* `extraction_state` records how `body` was produced for later re-extraction.
|
|
8
|
+
* - `vec_entries` is a disposable, rebuildable vector index (sqlite-vec vec0),
|
|
9
|
+
* keyed by `rowid = entries.id`.
|
|
10
|
+
*
|
|
11
|
+
* `embedding_meta` records the active model/dim so a backend switch can be
|
|
12
|
+
* detected. `schema_meta` holds the schema version.
|
|
13
|
+
*/
|
|
14
|
+
import type Database from "better-sqlite3";
|
|
15
|
+
export declare const SCHEMA_VERSION = 2;
|
|
16
|
+
export declare function createSchema(db: Database.Database, dim: number): void;
|
|
17
|
+
/**
|
|
18
|
+
* Apply idempotent migrations for databases created by an older schema.
|
|
19
|
+
* Safe to run on every startup.
|
|
20
|
+
*/
|
|
21
|
+
export declare function migrate(db: Database.Database): void;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export const SCHEMA_VERSION = 2;
|
|
2
|
+
export function createSchema(db, dim) {
|
|
3
|
+
if (!Number.isInteger(dim) || dim <= 0) {
|
|
4
|
+
throw new Error(`Embedding dimension must be a positive integer, got: ${dim}`);
|
|
5
|
+
}
|
|
6
|
+
db.exec(`
|
|
7
|
+
CREATE TABLE IF NOT EXISTS entries (
|
|
8
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9
|
+
body TEXT NOT NULL,
|
|
10
|
+
source_kind TEXT NOT NULL DEFAULT 'text',
|
|
11
|
+
original_ref TEXT,
|
|
12
|
+
extraction_state TEXT NOT NULL DEFAULT 'verbatim',
|
|
13
|
+
tags TEXT NOT NULL DEFAULT '[]',
|
|
14
|
+
meta TEXT NOT NULL DEFAULT '{}',
|
|
15
|
+
occurred_at TEXT NOT NULL,
|
|
16
|
+
created_at TEXT NOT NULL,
|
|
17
|
+
content_hash TEXT NOT NULL,
|
|
18
|
+
deleted_at TEXT
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_entries_content_hash ON entries(content_hash);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_entries_created_at ON entries(created_at);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_entries_occurred_at ON entries(occurred_at);
|
|
24
|
+
CREATE INDEX IF NOT EXISTS idx_entries_source_kind ON entries(source_kind);
|
|
25
|
+
|
|
26
|
+
CREATE TABLE IF NOT EXISTS schema_meta (
|
|
27
|
+
key TEXT PRIMARY KEY,
|
|
28
|
+
value TEXT NOT NULL
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
CREATE TABLE IF NOT EXISTS embedding_meta (
|
|
32
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
33
|
+
model_id TEXT NOT NULL,
|
|
34
|
+
dim INTEGER NOT NULL
|
|
35
|
+
);
|
|
36
|
+
`);
|
|
37
|
+
// `dim` is validated as a positive integer above, so it is safe to inline.
|
|
38
|
+
// The dimension of a vec0 column is fixed at creation time.
|
|
39
|
+
db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS vec_entries USING vec0(embedding float[${dim}]);`);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Apply idempotent migrations for databases created by an older schema.
|
|
43
|
+
* Safe to run on every startup.
|
|
44
|
+
*/
|
|
45
|
+
export function migrate(db) {
|
|
46
|
+
const columns = db.prepare("PRAGMA table_info(entries)").all();
|
|
47
|
+
if (!columns.some((column) => column.name === "deleted_at")) {
|
|
48
|
+
db.exec("ALTER TABLE entries ADD COLUMN deleted_at TEXT");
|
|
49
|
+
}
|
|
50
|
+
// Created here (not in createSchema) so it runs after the column is guaranteed.
|
|
51
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_entries_deleted_at ON entries(deleted_at)");
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,MAAM,UAAU,YAAY,CAAC,EAAqB,EAAE,GAAW;IAC7D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,wDAAwD,GAAG,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BP,CAAC,CAAC;IAEH,2EAA2E;IAC3E,4DAA4D;IAC5D,EAAE,CAAC,IAAI,CAAC,6EAA6E,GAAG,KAAK,CAAC,CAAC;AACjG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,EAAqB;IAC3C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC,GAAG,EAA6B,CAAC;IAC1F,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,CAAC,EAAE,CAAC;QAC5D,EAAE,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;IAC5D,CAAC;IACD,gFAAgF;IAChF,EAAE,CAAC,IAAI,CAAC,0EAA0E,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { EmbeddingProvider } from "../embedding/provider.js";
|
|
2
|
+
export interface CaptureInput {
|
|
3
|
+
body: string;
|
|
4
|
+
source_kind?: string;
|
|
5
|
+
original_ref?: string | null;
|
|
6
|
+
extraction_state?: string;
|
|
7
|
+
tags?: string[];
|
|
8
|
+
meta?: Record<string, unknown>;
|
|
9
|
+
occurred_at?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface Entry {
|
|
12
|
+
id: number;
|
|
13
|
+
body: string;
|
|
14
|
+
source_kind: string;
|
|
15
|
+
original_ref: string | null;
|
|
16
|
+
extraction_state: string;
|
|
17
|
+
tags: string[];
|
|
18
|
+
meta: Record<string, unknown>;
|
|
19
|
+
occurred_at: string;
|
|
20
|
+
created_at: string;
|
|
21
|
+
content_hash: string;
|
|
22
|
+
}
|
|
23
|
+
export interface CaptureResult {
|
|
24
|
+
id: number;
|
|
25
|
+
/** True when an identical entry already existed and nothing new was stored. */
|
|
26
|
+
deduped: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface QueryFilters {
|
|
29
|
+
since?: string;
|
|
30
|
+
until?: string;
|
|
31
|
+
source_kind?: string;
|
|
32
|
+
tag?: string;
|
|
33
|
+
time_field?: "created_at" | "occurred_at";
|
|
34
|
+
limit?: number;
|
|
35
|
+
/** Include soft-deleted entries (default false). */
|
|
36
|
+
include_deleted?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface RecallHit extends Entry {
|
|
39
|
+
/** Vector distance; smaller means more similar. */
|
|
40
|
+
distance: number;
|
|
41
|
+
}
|
|
42
|
+
/** A time window used by the aggregation queries that back reviews. */
|
|
43
|
+
export interface ReviewTimeWindow {
|
|
44
|
+
since?: string;
|
|
45
|
+
until?: string;
|
|
46
|
+
time_field?: "created_at" | "occurred_at";
|
|
47
|
+
}
|
|
48
|
+
export interface DayCount {
|
|
49
|
+
day: string;
|
|
50
|
+
count: number;
|
|
51
|
+
}
|
|
52
|
+
export interface SourceKindCount {
|
|
53
|
+
source_kind: string;
|
|
54
|
+
count: number;
|
|
55
|
+
}
|
|
56
|
+
export interface TagCount {
|
|
57
|
+
tag: string;
|
|
58
|
+
count: number;
|
|
59
|
+
}
|
|
60
|
+
export interface MonthCount {
|
|
61
|
+
month: string;
|
|
62
|
+
count: number;
|
|
63
|
+
}
|
|
64
|
+
export interface EntryStats {
|
|
65
|
+
active: number;
|
|
66
|
+
soft_deleted: number;
|
|
67
|
+
vectors: number;
|
|
68
|
+
by_source_kind: SourceKindCount[];
|
|
69
|
+
by_month: MonthCount[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Canonicalize any valid ISO-8601 input to a UTC `...Z` string. Timestamps are
|
|
73
|
+
* stored as TEXT and compared/sorted lexicographically, which is only correct
|
|
74
|
+
* when every value is in the same (UTC) representation — an offset like
|
|
75
|
+
* `+09:00` would otherwise sort wrong relative to a `Z` value.
|
|
76
|
+
*/
|
|
77
|
+
export declare function normalizeTimestamp(value: string): string;
|
|
78
|
+
export declare class JournalStore {
|
|
79
|
+
#private;
|
|
80
|
+
constructor(dbPath: string, embedder: EmbeddingProvider);
|
|
81
|
+
/** Create tables and reconcile embedding metadata. Safe to call repeatedly. */
|
|
82
|
+
init(): void;
|
|
83
|
+
/** Capture one entry. Deduplicates on sha256(body + occurred_at). */
|
|
84
|
+
insert(input: CaptureInput): Promise<CaptureResult>;
|
|
85
|
+
/** Structured lookup. `time_field` selects which timestamp to filter/sort by. */
|
|
86
|
+
query(filters: QueryFilters): Entry[];
|
|
87
|
+
/** Semantic nearest-neighbour recall over the vector index. */
|
|
88
|
+
recall(queryText: string, k?: number): Promise<RecallHit[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Rebuild the vector index from the original entries with the current
|
|
91
|
+
* embedding backend. Use after switching models (vectors from different
|
|
92
|
+
* models are not comparable). Originals are untouched; only the disposable
|
|
93
|
+
* `vec_entries` index is dropped and recreated, then `embedding_meta` is
|
|
94
|
+
* updated to the active backend.
|
|
95
|
+
*/
|
|
96
|
+
reindex(batchSize?: number): Promise<{
|
|
97
|
+
reindexed: number;
|
|
98
|
+
model_id: string;
|
|
99
|
+
dim: number;
|
|
100
|
+
}>;
|
|
101
|
+
/** Total number of entries in the window. */
|
|
102
|
+
countInWindow(window: ReviewTimeWindow): number;
|
|
103
|
+
/** Entry counts grouped by calendar day (YYYY-MM-DD), ascending. */
|
|
104
|
+
aggregateByDay(window: ReviewTimeWindow): DayCount[];
|
|
105
|
+
/** Entry counts grouped by source kind, most frequent first. */
|
|
106
|
+
aggregateBySourceKind(window: ReviewTimeWindow): SourceKindCount[];
|
|
107
|
+
/** Tag frequencies within the window, most frequent first. */
|
|
108
|
+
aggregateTags(window: ReviewTimeWindow, limit?: number): TagCount[];
|
|
109
|
+
/**
|
|
110
|
+
* Delete an entry. `soft` sets a tombstone (recoverable, sync-friendly);
|
|
111
|
+
* `hard` physically purges the row + vector, reports an original_ref that is
|
|
112
|
+
* now unreferenced, and VACUUMs so the bytes do not linger in the WAL/freelist.
|
|
113
|
+
*/
|
|
114
|
+
/** Soft delete: set a recoverable tombstone. Returns true if a row changed. */
|
|
115
|
+
softDelete(id: number): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Inspect a hard delete without performing it: whether the entry exists, its
|
|
118
|
+
* original_ref, and whether deleting it would leave that original
|
|
119
|
+
* unreferenced. Lets the caller erase the original BEFORE the row, so a
|
|
120
|
+
* failure can't leave the bytes orphaned.
|
|
121
|
+
*/
|
|
122
|
+
peekHardDelete(id: number): {
|
|
123
|
+
exists: boolean;
|
|
124
|
+
original_ref: string | null;
|
|
125
|
+
orphan: boolean;
|
|
126
|
+
};
|
|
127
|
+
/** Hard delete: physically purge the entry + its vector, then VACUUM. */
|
|
128
|
+
purgeEntry(id: number): boolean;
|
|
129
|
+
/** Current original_ref for an entry, or null if none / not found. */
|
|
130
|
+
getOriginalRef(id: number): string | null;
|
|
131
|
+
/** Attach an original_ref only if the entry doesn't already have one. */
|
|
132
|
+
attachOriginalIfAbsent(id: number, ref: string): boolean;
|
|
133
|
+
/** Entry-level statistics for storage_stats. */
|
|
134
|
+
entryStats(): EntryStats;
|
|
135
|
+
close(): void;
|
|
136
|
+
}
|
package/dist/db/store.js
ADDED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/db/store.ts"],"names":[],"mappings":"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"}
|