@hir4ta/memoria 0.13.2 → 0.14.1
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/.claude-plugin/plugin.json +1 -1
- package/README.ja.md +29 -15
- package/README.md +29 -15
- package/bin/memoria.js +91 -5
- package/dist/lib/db.js +167 -0
- package/dist/public/assets/index-CpMhKAZt.js +288 -0
- package/dist/public/assets/index-J1A9Ovuz.css +1 -0
- package/dist/public/assets/{react-force-graph-2d-CiBxrA3j.js → react-force-graph-2d-D56jdA7v.js} +1 -1
- package/dist/public/index.html +2 -2
- package/dist/server.js +118 -2
- package/hooks/pre-compact.sh +63 -18
- package/hooks/session-end.sh +119 -56
- package/hooks/session-start.sh +5 -6
- package/package.json +1 -2
- package/skills/init/skill.md +68 -0
- package/skills/resume/skill.md +20 -9
- package/skills/save/skill.md +45 -52
- package/skills/using-memoria/skill.md +15 -0
- package/dist/public/assets/index-ByVvcr4B.js +0 -288
- package/dist/public/assets/index-wXKQkB3X.css +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memoria",
|
|
3
3
|
"description": "A plugin that provides long-term memory for Claude Code. It automatically saves context lost during auto-compact, offering features for session restoration, recording technical decisions, and learning developer patterns.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.14.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "hir4ta"
|
|
7
7
|
},
|
package/README.ja.md
CHANGED
|
@@ -74,6 +74,16 @@ Claude Code内で以下を実行
|
|
|
74
74
|
/plugin install memoria@memoria-marketplace
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
プロジェクトでmemoriaを初期化:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Claude Code内で
|
|
81
|
+
/memoria:init
|
|
82
|
+
|
|
83
|
+
# またはターミナルから
|
|
84
|
+
npx @hir4ta/memoria --init
|
|
85
|
+
```
|
|
86
|
+
|
|
77
87
|
Claude Codeを再起動して完了
|
|
78
88
|
|
|
79
89
|
## アップデート
|
|
@@ -129,6 +139,7 @@ Continue from a previous session? Use `/memoria:resume <id>`
|
|
|
129
139
|
|
|
130
140
|
| コマンド | 説明 |
|
|
131
141
|
| --------- | ------ |
|
|
142
|
+
| `/memoria:init` | プロジェクトでmemoriaを初期化 |
|
|
132
143
|
| `/memoria:save` | 全データ抽出: 要約・判断・パターン・ルール |
|
|
133
144
|
| `/memoria:plan [トピック]` | 記憶参照 + ソクラティック質問 + タスク分割 |
|
|
134
145
|
| `/memoria:resume [id]` | セッションを再開(ID省略で一覧表示) |
|
|
@@ -227,14 +238,27 @@ flowchart TB
|
|
|
227
238
|
|
|
228
239
|
## データ保存
|
|
229
240
|
|
|
230
|
-
|
|
241
|
+
memoriaは**ハイブリッドストレージ**方式でプライバシーと共有を両立:
|
|
242
|
+
|
|
243
|
+
| ストレージ | 用途 | 共有 |
|
|
244
|
+
|-----------|------|------|
|
|
245
|
+
| **JSON** | 要約、決定、パターン、ルール | Git管理(チーム共有) |
|
|
246
|
+
| **SQLite** | 会話履歴、バックアップ | ローカル専用(.gitignore) |
|
|
247
|
+
|
|
248
|
+
**なぜハイブリッド?**
|
|
249
|
+
- **プライバシー**: 会話履歴(interactions)は各開発者のローカルのみ
|
|
250
|
+
- **軽量化**: JSONファイルが100KB+から約5KBに軽量化(interactions除外)
|
|
251
|
+
- **将来対応**: セマンティック検索用のembeddingsテーブル準備済み
|
|
252
|
+
|
|
253
|
+
### ディレクトリ構成
|
|
231
254
|
|
|
232
255
|
```text
|
|
233
256
|
.memoria/
|
|
257
|
+
├── local.db # SQLite(ローカル専用、.gitignore)
|
|
234
258
|
├── tags.json # タグマスターファイル(93タグ、表記揺れ防止)
|
|
235
|
-
├── sessions/ #
|
|
259
|
+
├── sessions/ # セッションメタデータ
|
|
236
260
|
│ └── YYYY/MM/
|
|
237
|
-
│ └── {id}.json
|
|
261
|
+
│ └── {id}.json # メタデータのみ(interactionsはSQLite)
|
|
238
262
|
├── decisions/ # 技術的な判断(/saveから)
|
|
239
263
|
│ └── YYYY/MM/
|
|
240
264
|
│ └── {id}.json
|
|
@@ -245,11 +269,11 @@ flowchart TB
|
|
|
245
269
|
└── reports/ # 週次レポート (YYYY-MM)
|
|
246
270
|
```
|
|
247
271
|
|
|
248
|
-
Git
|
|
272
|
+
Gitでバージョン管理可能です。`local.db`は自動で`.gitignore`に追加されます。
|
|
249
273
|
|
|
250
274
|
### セッションJSONスキーマ
|
|
251
275
|
|
|
252
|
-
|
|
276
|
+
セッションメタデータはJSONに保存(interactionsはプライバシー保護のためSQLiteに保存):
|
|
253
277
|
|
|
254
278
|
```json
|
|
255
279
|
{
|
|
@@ -264,15 +288,6 @@ Gitでバージョン管理可能です。`.gitignore` に追加するかはプ
|
|
|
264
288
|
"projectDir": "/path/to/project",
|
|
265
289
|
"user": { "name": "tanaka", "email": "tanaka@example.com" }
|
|
266
290
|
},
|
|
267
|
-
"interactions": [
|
|
268
|
-
{
|
|
269
|
-
"id": "int-001",
|
|
270
|
-
"timestamp": "2026-01-27T10:15:00Z",
|
|
271
|
-
"user": "認証機能を実装して",
|
|
272
|
-
"thinking": "思考プロセスの重要なポイント",
|
|
273
|
-
"assistant": "RS256署名でJWT認証を実装しました"
|
|
274
|
-
}
|
|
275
|
-
],
|
|
276
291
|
"metrics": {
|
|
277
292
|
"userMessages": 5,
|
|
278
293
|
"assistantResponses": 5,
|
|
@@ -282,7 +297,6 @@ Gitでバージョン管理可能です。`.gitignore` に追加するかはプ
|
|
|
282
297
|
"files": [
|
|
283
298
|
{ "path": "src/auth/jwt.ts", "action": "create" }
|
|
284
299
|
],
|
|
285
|
-
"preCompactBackups": [],
|
|
286
300
|
"resumedFrom": "def45678",
|
|
287
301
|
"status": "complete",
|
|
288
302
|
|
package/README.md
CHANGED
|
@@ -74,6 +74,16 @@ Run the following in Claude Code:
|
|
|
74
74
|
/plugin install memoria@memoria-marketplace
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
+
Then initialize memoria in your project:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# In Claude Code
|
|
81
|
+
/memoria:init
|
|
82
|
+
|
|
83
|
+
# Or from terminal
|
|
84
|
+
npx @hir4ta/memoria --init
|
|
85
|
+
```
|
|
86
|
+
|
|
77
87
|
Restart Claude Code to complete installation.
|
|
78
88
|
|
|
79
89
|
## Update
|
|
@@ -129,6 +139,7 @@ Continue from a previous session? Use `/memoria:resume <id>`
|
|
|
129
139
|
|
|
130
140
|
| Command | Description |
|
|
131
141
|
|---------|-------------|
|
|
142
|
+
| `/memoria:init` | Initialize memoria in current project |
|
|
132
143
|
| `/memoria:save` | Extract all data: summary, decisions, patterns, rules |
|
|
133
144
|
| `/memoria:plan [topic]` | Memory-informed design + Socratic questions + task breakdown |
|
|
134
145
|
| `/memoria:resume [id]` | Resume session (show list if ID omitted) |
|
|
@@ -227,14 +238,27 @@ flowchart TB
|
|
|
227
238
|
|
|
228
239
|
## Data Storage
|
|
229
240
|
|
|
230
|
-
|
|
241
|
+
memoria uses a **hybrid storage** approach for privacy and collaboration:
|
|
242
|
+
|
|
243
|
+
| Storage | Purpose | Sharing |
|
|
244
|
+
|---------|---------|---------|
|
|
245
|
+
| **JSON** | Summaries, decisions, patterns, rules | Git-managed (team shared) |
|
|
246
|
+
| **SQLite** | Interactions, backups | Local only (.gitignore) |
|
|
247
|
+
|
|
248
|
+
**Why hybrid?**
|
|
249
|
+
- **Privacy**: Conversation history (interactions) stays local to each developer
|
|
250
|
+
- **Lightweight**: JSON files reduced from 100KB+ to ~5KB (interactions excluded)
|
|
251
|
+
- **Future-ready**: Embeddings table prepared for semantic search
|
|
252
|
+
|
|
253
|
+
### Directory Structure
|
|
231
254
|
|
|
232
255
|
```text
|
|
233
256
|
.memoria/
|
|
257
|
+
├── local.db # SQLite (local only, .gitignore)
|
|
234
258
|
├── tags.json # Tag master file (93 tags, prevents notation variations)
|
|
235
|
-
├── sessions/ # Session
|
|
259
|
+
├── sessions/ # Session metadata (YYYY/MM)
|
|
236
260
|
│ └── YYYY/MM/
|
|
237
|
-
│ └── {id}.json #
|
|
261
|
+
│ └── {id}.json # Metadata only (interactions in SQLite)
|
|
238
262
|
├── decisions/ # Technical decisions (from /save)
|
|
239
263
|
│ └── YYYY/MM/
|
|
240
264
|
│ └── {id}.json
|
|
@@ -245,11 +269,11 @@ All data is stored in `.memoria/` directory:
|
|
|
245
269
|
└── reports/ # Weekly reports (YYYY-MM)
|
|
246
270
|
```
|
|
247
271
|
|
|
248
|
-
Git-manageable.
|
|
272
|
+
Git-manageable. The `local.db` file is automatically added to `.gitignore`.
|
|
249
273
|
|
|
250
274
|
### Session JSON Schema
|
|
251
275
|
|
|
252
|
-
|
|
276
|
+
Session metadata is stored in JSON (interactions are stored in SQLite for privacy):
|
|
253
277
|
|
|
254
278
|
```json
|
|
255
279
|
{
|
|
@@ -264,15 +288,6 @@ All session data is stored in a single JSON file:
|
|
|
264
288
|
"projectDir": "/path/to/project",
|
|
265
289
|
"user": { "name": "tanaka", "email": "tanaka@example.com" }
|
|
266
290
|
},
|
|
267
|
-
"interactions": [
|
|
268
|
-
{
|
|
269
|
-
"id": "int-001",
|
|
270
|
-
"timestamp": "2026-01-27T10:15:00Z",
|
|
271
|
-
"user": "Implement authentication",
|
|
272
|
-
"thinking": "Key insights from thinking process",
|
|
273
|
-
"assistant": "Implemented JWT auth with RS256 signing"
|
|
274
|
-
}
|
|
275
|
-
],
|
|
276
291
|
"metrics": {
|
|
277
292
|
"userMessages": 5,
|
|
278
293
|
"assistantResponses": 5,
|
|
@@ -282,7 +297,6 @@ All session data is stored in a single JSON file:
|
|
|
282
297
|
"files": [
|
|
283
298
|
{ "path": "src/auth/jwt.ts", "action": "create" }
|
|
284
299
|
],
|
|
285
|
-
"preCompactBackups": [],
|
|
286
300
|
"resumedFrom": "def45678",
|
|
287
301
|
"status": "complete",
|
|
288
302
|
|
package/bin/memoria.js
CHANGED
|
@@ -5,6 +5,18 @@ import fs from "node:fs";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
|
+
// Suppress Node.js SQLite experimental warning (must be before dynamic import)
|
|
9
|
+
const originalEmit = process.emit;
|
|
10
|
+
process.emit = function (name, data, ...args) {
|
|
11
|
+
if (name === "warning" && data?.name === "ExperimentalWarning" && data?.message?.includes("SQLite")) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return originalEmit.call(process, name, data, ...args);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Dynamic import to ensure warning suppression is active
|
|
18
|
+
const { DatabaseSync } = await import("node:sqlite");
|
|
19
|
+
|
|
8
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
21
|
const __dirname = path.dirname(__filename);
|
|
10
22
|
|
|
@@ -17,6 +29,7 @@ function showHelp() {
|
|
|
17
29
|
memoria - Claude Code Long-term Memory Plugin
|
|
18
30
|
|
|
19
31
|
Usage:
|
|
32
|
+
memoria --init Initialize .memoria directory in current project
|
|
20
33
|
memoria --dashboard Start the web dashboard
|
|
21
34
|
memoria -d Same as above (short form)
|
|
22
35
|
memoria --port <port> Specify port (default: 7777)
|
|
@@ -24,6 +37,7 @@ Usage:
|
|
|
24
37
|
|
|
25
38
|
Examples:
|
|
26
39
|
cd /path/to/your/project
|
|
40
|
+
npx @hir4ta/memoria --init
|
|
27
41
|
npx @hir4ta/memoria --dashboard
|
|
28
42
|
npx @hir4ta/memoria -d --port 8080
|
|
29
43
|
`);
|
|
@@ -33,11 +47,81 @@ function checkMemoriaDir() {
|
|
|
33
47
|
const memoriaDir = path.join(projectRoot, ".memoria");
|
|
34
48
|
if (!fs.existsSync(memoriaDir)) {
|
|
35
49
|
console.log(`\nWARNING: .memoria directory not found: ${projectRoot}`);
|
|
36
|
-
console.log(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
console.log(" Run: npx @hir4ta/memoria --init");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function initMemoria() {
|
|
55
|
+
const memoriaDir = path.join(projectRoot, ".memoria");
|
|
56
|
+
const sessionsDir = path.join(memoriaDir, "sessions");
|
|
57
|
+
const rulesDir = path.join(memoriaDir, "rules");
|
|
58
|
+
const patternsDir = path.join(memoriaDir, "patterns");
|
|
59
|
+
const tagsPath = path.join(memoriaDir, "tags.json");
|
|
60
|
+
const dbPath = path.join(memoriaDir, "local.db");
|
|
61
|
+
|
|
62
|
+
// Check if already initialized
|
|
63
|
+
if (fs.existsSync(memoriaDir)) {
|
|
64
|
+
console.log(`memoria is already initialized: ${memoriaDir}`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Create directories
|
|
69
|
+
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
70
|
+
fs.mkdirSync(rulesDir, { recursive: true });
|
|
71
|
+
fs.mkdirSync(patternsDir, { recursive: true });
|
|
72
|
+
|
|
73
|
+
// Copy default tags.json
|
|
74
|
+
const defaultTagsPath = path.join(packageDir, "hooks", "default-tags.json");
|
|
75
|
+
if (fs.existsSync(defaultTagsPath)) {
|
|
76
|
+
fs.copyFileSync(defaultTagsPath, tagsPath);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Initialize rules files
|
|
80
|
+
const now = new Date().toISOString();
|
|
81
|
+
const rulesTemplate = JSON.stringify(
|
|
82
|
+
{
|
|
83
|
+
schemaVersion: 1,
|
|
84
|
+
createdAt: now,
|
|
85
|
+
updatedAt: now,
|
|
86
|
+
items: [],
|
|
87
|
+
},
|
|
88
|
+
null,
|
|
89
|
+
2,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
fs.writeFileSync(
|
|
93
|
+
path.join(rulesDir, "review-guidelines.json"),
|
|
94
|
+
rulesTemplate,
|
|
95
|
+
);
|
|
96
|
+
fs.writeFileSync(path.join(rulesDir, "dev-rules.json"), rulesTemplate);
|
|
97
|
+
|
|
98
|
+
// Initialize SQLite database
|
|
99
|
+
const schemaPath = path.join(packageDir, "lib", "schema.sql");
|
|
100
|
+
try {
|
|
101
|
+
const db = new DatabaseSync(dbPath);
|
|
102
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
103
|
+
if (fs.existsSync(schemaPath)) {
|
|
104
|
+
const schema = fs.readFileSync(schemaPath, "utf-8");
|
|
105
|
+
db.exec(schema);
|
|
106
|
+
}
|
|
107
|
+
db.close();
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(`Warning: Failed to initialize SQLite database: ${error.message}`);
|
|
40
110
|
}
|
|
111
|
+
|
|
112
|
+
console.log(`memoria initialized: ${memoriaDir}`);
|
|
113
|
+
console.log(`
|
|
114
|
+
Created:
|
|
115
|
+
${sessionsDir}/
|
|
116
|
+
${rulesDir}/
|
|
117
|
+
${patternsDir}/
|
|
118
|
+
${tagsPath}
|
|
119
|
+
${rulesDir}/review-guidelines.json
|
|
120
|
+
${rulesDir}/dev-rules.json
|
|
121
|
+
${dbPath}
|
|
122
|
+
|
|
123
|
+
You can now use memoria with Claude Code in this project.
|
|
124
|
+
`);
|
|
41
125
|
}
|
|
42
126
|
|
|
43
127
|
function getPort() {
|
|
@@ -90,7 +174,9 @@ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
|
90
174
|
process.exit(0);
|
|
91
175
|
}
|
|
92
176
|
|
|
93
|
-
if (args.includes("--
|
|
177
|
+
if (args.includes("--init")) {
|
|
178
|
+
initMemoria();
|
|
179
|
+
} else if (args.includes("--dashboard") || args.includes("-d")) {
|
|
94
180
|
startDashboard();
|
|
95
181
|
} else {
|
|
96
182
|
console.error(`ERROR: Unknown option: ${args.join(" ")}`);
|
package/dist/lib/db.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// lib/db.ts
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
var originalEmit = process.emit;
|
|
7
|
+
process.emit = function(name, data, ...args) {
|
|
8
|
+
if (name === "warning" && typeof data === "object" && data?.name === "ExperimentalWarning" && data?.message?.includes("SQLite")) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return originalEmit.call(process, name, data, ...args);
|
|
12
|
+
};
|
|
13
|
+
var { DatabaseSync } = await import("node:sqlite");
|
|
14
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
var __dirname = dirname(__filename);
|
|
16
|
+
function getCurrentUser() {
|
|
17
|
+
try {
|
|
18
|
+
return execSync("git config user.name", { encoding: "utf-8" }).trim();
|
|
19
|
+
} catch {
|
|
20
|
+
try {
|
|
21
|
+
return execSync("whoami", { encoding: "utf-8" }).trim();
|
|
22
|
+
} catch {
|
|
23
|
+
return "unknown";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function getDbPath(memoriaDir) {
|
|
28
|
+
return join(memoriaDir, "local.db");
|
|
29
|
+
}
|
|
30
|
+
function initDatabase(memoriaDir) {
|
|
31
|
+
const dbPath = getDbPath(memoriaDir);
|
|
32
|
+
const db = new DatabaseSync(dbPath);
|
|
33
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
34
|
+
const schemaPath = join(__dirname, "schema.sql");
|
|
35
|
+
if (existsSync(schemaPath)) {
|
|
36
|
+
const schema = readFileSync(schemaPath, "utf-8");
|
|
37
|
+
db.exec(schema);
|
|
38
|
+
}
|
|
39
|
+
return db;
|
|
40
|
+
}
|
|
41
|
+
function openDatabase(memoriaDir) {
|
|
42
|
+
const dbPath = getDbPath(memoriaDir);
|
|
43
|
+
if (!existsSync(dbPath)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const db = new DatabaseSync(dbPath);
|
|
47
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
48
|
+
return db;
|
|
49
|
+
}
|
|
50
|
+
function insertInteractions(db, interactions) {
|
|
51
|
+
const insert = db.prepare(`
|
|
52
|
+
INSERT INTO interactions (session_id, owner, role, content, thinking, tool_calls, timestamp, is_compact_summary)
|
|
53
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
54
|
+
`);
|
|
55
|
+
db.exec("BEGIN TRANSACTION");
|
|
56
|
+
try {
|
|
57
|
+
for (const item of interactions) {
|
|
58
|
+
insert.run(
|
|
59
|
+
item.session_id,
|
|
60
|
+
item.owner,
|
|
61
|
+
item.role,
|
|
62
|
+
item.content,
|
|
63
|
+
item.thinking || null,
|
|
64
|
+
item.tool_calls || null,
|
|
65
|
+
item.timestamp,
|
|
66
|
+
item.is_compact_summary || 0
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
db.exec("COMMIT");
|
|
70
|
+
} catch (error) {
|
|
71
|
+
db.exec("ROLLBACK");
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function getInteractions(db, sessionId) {
|
|
76
|
+
const stmt = db.prepare(`
|
|
77
|
+
SELECT * FROM interactions
|
|
78
|
+
WHERE session_id = ?
|
|
79
|
+
ORDER BY timestamp ASC
|
|
80
|
+
`);
|
|
81
|
+
return stmt.all(sessionId);
|
|
82
|
+
}
|
|
83
|
+
function getInteractionsByOwner(db, sessionId, owner) {
|
|
84
|
+
const stmt = db.prepare(`
|
|
85
|
+
SELECT * FROM interactions
|
|
86
|
+
WHERE session_id = ? AND owner = ?
|
|
87
|
+
ORDER BY timestamp ASC
|
|
88
|
+
`);
|
|
89
|
+
return stmt.all(sessionId, owner);
|
|
90
|
+
}
|
|
91
|
+
function hasInteractions(db, sessionId, owner) {
|
|
92
|
+
const stmt = db.prepare(`
|
|
93
|
+
SELECT COUNT(*) as count FROM interactions
|
|
94
|
+
WHERE session_id = ? AND owner = ?
|
|
95
|
+
`);
|
|
96
|
+
const result = stmt.get(sessionId, owner);
|
|
97
|
+
return result.count > 0;
|
|
98
|
+
}
|
|
99
|
+
function insertPreCompactBackup(db, backup) {
|
|
100
|
+
const stmt = db.prepare(`
|
|
101
|
+
INSERT INTO pre_compact_backups (session_id, owner, interactions)
|
|
102
|
+
VALUES (?, ?, ?)
|
|
103
|
+
`);
|
|
104
|
+
stmt.run(backup.session_id, backup.owner, backup.interactions);
|
|
105
|
+
}
|
|
106
|
+
function getLatestBackup(db, sessionId) {
|
|
107
|
+
const stmt = db.prepare(`
|
|
108
|
+
SELECT * FROM pre_compact_backups
|
|
109
|
+
WHERE session_id = ?
|
|
110
|
+
ORDER BY created_at DESC
|
|
111
|
+
LIMIT 1
|
|
112
|
+
`);
|
|
113
|
+
return stmt.get(sessionId) || null;
|
|
114
|
+
}
|
|
115
|
+
function getAllBackups(db, sessionId) {
|
|
116
|
+
const stmt = db.prepare(`
|
|
117
|
+
SELECT * FROM pre_compact_backups
|
|
118
|
+
WHERE session_id = ?
|
|
119
|
+
ORDER BY created_at ASC
|
|
120
|
+
`);
|
|
121
|
+
return stmt.all(sessionId);
|
|
122
|
+
}
|
|
123
|
+
function searchInteractions(db, query, limit = 10) {
|
|
124
|
+
const stmt = db.prepare(`
|
|
125
|
+
SELECT i.session_id, i.content, i.thinking
|
|
126
|
+
FROM interactions_fts fts
|
|
127
|
+
JOIN interactions i ON fts.rowid = i.id
|
|
128
|
+
WHERE interactions_fts MATCH ?
|
|
129
|
+
LIMIT ?
|
|
130
|
+
`);
|
|
131
|
+
return stmt.all(query, limit);
|
|
132
|
+
}
|
|
133
|
+
function deleteInteractions(db, sessionId) {
|
|
134
|
+
const stmt = db.prepare("DELETE FROM interactions WHERE session_id = ?");
|
|
135
|
+
stmt.run(sessionId);
|
|
136
|
+
}
|
|
137
|
+
function deleteBackups(db, sessionId) {
|
|
138
|
+
const stmt = db.prepare(
|
|
139
|
+
"DELETE FROM pre_compact_backups WHERE session_id = ?"
|
|
140
|
+
);
|
|
141
|
+
stmt.run(sessionId);
|
|
142
|
+
}
|
|
143
|
+
function getDbStats(db) {
|
|
144
|
+
const interactionsCount = db.prepare("SELECT COUNT(*) as count FROM interactions").get();
|
|
145
|
+
const backupsCount = db.prepare("SELECT COUNT(*) as count FROM pre_compact_backups").get();
|
|
146
|
+
return {
|
|
147
|
+
interactions: interactionsCount.count,
|
|
148
|
+
backups: backupsCount.count
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
deleteBackups,
|
|
153
|
+
deleteInteractions,
|
|
154
|
+
getAllBackups,
|
|
155
|
+
getCurrentUser,
|
|
156
|
+
getDbPath,
|
|
157
|
+
getDbStats,
|
|
158
|
+
getInteractions,
|
|
159
|
+
getInteractionsByOwner,
|
|
160
|
+
getLatestBackup,
|
|
161
|
+
hasInteractions,
|
|
162
|
+
initDatabase,
|
|
163
|
+
insertInteractions,
|
|
164
|
+
insertPreCompactBackup,
|
|
165
|
+
openDatabase,
|
|
166
|
+
searchInteractions
|
|
167
|
+
};
|