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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +181 -0
  3. package/README.md +184 -0
  4. package/dist/db/schema.d.ts +21 -0
  5. package/dist/db/schema.js +53 -0
  6. package/dist/db/schema.js.map +1 -0
  7. package/dist/db/store.d.ts +136 -0
  8. package/dist/db/store.js +0 -0
  9. package/dist/db/store.js.map +1 -0
  10. package/dist/embedding/provider.d.ts +37 -0
  11. package/dist/embedding/provider.js +53 -0
  12. package/dist/embedding/provider.js.map +1 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +49 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/kernel/config.d.ts +17 -0
  17. package/dist/kernel/config.js +26 -0
  18. package/dist/kernel/config.js.map +1 -0
  19. package/dist/kernel/context.d.ts +26 -0
  20. package/dist/kernel/context.js +7 -0
  21. package/dist/kernel/context.js.map +1 -0
  22. package/dist/kernel/module.d.ts +13 -0
  23. package/dist/kernel/module.js +7 -0
  24. package/dist/kernel/module.js.map +1 -0
  25. package/dist/kernel/plugin.d.ts +68 -0
  26. package/dist/kernel/plugin.js +144 -0
  27. package/dist/kernel/plugin.js.map +1 -0
  28. package/dist/kernel/result.d.ts +14 -0
  29. package/dist/kernel/result.js +11 -0
  30. package/dist/kernel/result.js.map +1 -0
  31. package/dist/management/module.d.ts +2 -0
  32. package/dist/management/module.js +39 -0
  33. package/dist/management/module.js.map +1 -0
  34. package/dist/management/server.d.ts +18 -0
  35. package/dist/management/server.js +216 -0
  36. package/dist/management/server.js.map +1 -0
  37. package/dist/management/ui.d.ts +10 -0
  38. package/dist/management/ui.js +159 -0
  39. package/dist/management/ui.js.map +1 -0
  40. package/dist/modules/core.d.ts +2 -0
  41. package/dist/modules/core.js +386 -0
  42. package/dist/modules/core.js.map +1 -0
  43. package/dist/modules/plugins.d.ts +2 -0
  44. package/dist/modules/plugins.js +177 -0
  45. package/dist/modules/plugins.js.map +1 -0
  46. package/dist/originals/store.d.ts +50 -0
  47. package/dist/originals/store.js +185 -0
  48. package/dist/originals/store.js.map +1 -0
  49. package/dist/review/charts.d.ts +16 -0
  50. package/dist/review/charts.js +69 -0
  51. package/dist/review/charts.js.map +1 -0
  52. package/dist/review/patterns.d.ts +33 -0
  53. package/dist/review/patterns.js +73 -0
  54. package/dist/review/patterns.js.map +1 -0
  55. package/dist/review/review.d.ts +30 -0
  56. package/dist/review/review.js +82 -0
  57. package/dist/review/review.js.map +1 -0
  58. package/dist/review/window.d.ts +18 -0
  59. package/dist/review/window.js +57 -0
  60. package/dist/review/window.js.map +1 -0
  61. 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
+ }
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/db/store.ts"],"names":[],"mappings":"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"}