@dvquys/qrec 0.2.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 +99 -0
- package/package.json +32 -0
- package/plugin/scripts/qrec-cli.js +71 -0
- package/plugin/scripts/qrec.cjs +144 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# qrec
|
|
2
|
+
|
|
3
|
+
Purpose-built session recall engine for Claude Code. Keeps an embedding model resident in memory for fast hybrid search (BM25 + vector) over your Claude session transcripts.
|
|
4
|
+
|
|
5
|
+
- **Warm query**: ~55ms vs ~2600ms cold model load per invocation
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
### Step 1 — Install the CLI
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g qrec
|
|
13
|
+
qrec onboard
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`qrec onboard` downloads the embedding model (~313 MB, once), indexes your Claude sessions at `~/.claude/projects/`, and starts the daemon. Your browser opens automatically to show live progress.
|
|
17
|
+
|
|
18
|
+
### Step 2 — Claude Code integration (optional)
|
|
19
|
+
|
|
20
|
+
Install the plugin so the daemon auto-starts with every Claude Code session:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
/plugin marketplace add dvquy13/qrec
|
|
24
|
+
/plugin install qrec@dvquy13-qrec
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That's it — no further configuration needed.
|
|
28
|
+
|
|
29
|
+
### Local dev
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bun install
|
|
33
|
+
bun link # register qrec globally → ~/.bun/bin/qrec
|
|
34
|
+
qrec onboard
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
The daemon runs at **http://localhost:3030**. Open it in your browser to search sessions and monitor indexing activity.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
qrec status # check if daemon is running
|
|
43
|
+
qrec stop # stop the daemon
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### API
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
curl -s -X POST http://localhost:3030/search \
|
|
50
|
+
-H "Content-Type: application/json" \
|
|
51
|
+
-d '{"query": "sqlite vec extension", "k": 5}' | jq .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"results": [
|
|
57
|
+
{
|
|
58
|
+
"session_id": "a1b2c3d4",
|
|
59
|
+
"title": "...",
|
|
60
|
+
"date": "2026-03-09",
|
|
61
|
+
"project": "qrec",
|
|
62
|
+
"score": 0.042,
|
|
63
|
+
"preview": "...best matching chunk..."
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"latencyMs": 58
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### MCP server
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
qrec mcp # stdio (Claude Code MCP config)
|
|
74
|
+
qrec mcp --http # HTTP on port 3031
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Tools: `search(query, k?)`, `get(session_id)`, `status()`
|
|
78
|
+
|
|
79
|
+
## Commands
|
|
80
|
+
|
|
81
|
+
| Command | Description |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `qrec onboard` | First-time setup: model download + index + start daemon + open browser |
|
|
84
|
+
| `qrec teardown` | Stop daemon and remove all qrec data (`~/.qrec/`) |
|
|
85
|
+
| `qrec index [path]` | Re-index sessions (default: `~/.claude/projects/`) |
|
|
86
|
+
| `qrec serve [--daemon]` | Start HTTP server on port 3030 |
|
|
87
|
+
| `qrec stop` | Stop daemon |
|
|
88
|
+
| `qrec mcp [--http]` | Start MCP server (stdio or HTTP) |
|
|
89
|
+
| `qrec status` | Status summary + log tail |
|
|
90
|
+
|
|
91
|
+
## Stack
|
|
92
|
+
|
|
93
|
+
| Layer | Choice |
|
|
94
|
+
|---|---|
|
|
95
|
+
| Runtime | Bun |
|
|
96
|
+
| Language | TypeScript |
|
|
97
|
+
| Search DB | SQLite (FTS5 + sqlite-vec) |
|
|
98
|
+
| Embeddings | node-llama-cpp 3.15.1 (`embeddinggemma-300M-Q8_0`) |
|
|
99
|
+
| MCP | `@modelcontextprotocol/sdk` |
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dvquys/qrec",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Purpose-built session recall engine — persistent daemon with BM25 + vector hybrid search",
|
|
5
|
+
"bin": {
|
|
6
|
+
"qrec": "plugin/scripts/qrec-cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"plugin/scripts/qrec.cjs",
|
|
10
|
+
"plugin/scripts/qrec-cli.js",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "bun run src/cli.ts",
|
|
15
|
+
"build": "node scripts/build.js",
|
|
16
|
+
"release": "bash scripts/release.sh",
|
|
17
|
+
"smoke-test": "bash scripts/smoke-test.sh"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
21
|
+
"node-llama-cpp": "3.15.1",
|
|
22
|
+
"sqlite-vec": "^0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/bun": "latest",
|
|
26
|
+
"esbuild": "^0.27.3",
|
|
27
|
+
"typescript": "^5.0.0"
|
|
28
|
+
},
|
|
29
|
+
"trustedDependencies": [
|
|
30
|
+
"node-llama-cpp"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// qrec-cli.js — npm bin entry. Locates bun and spawns qrec.cjs with all CLI args.
|
|
3
|
+
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
const { spawnSync, spawn } = require("child_process");
|
|
7
|
+
const { existsSync } = require("fs");
|
|
8
|
+
const { homedir } = require("os");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
const BUN_CANDIDATES = [
|
|
12
|
+
path.join(homedir(), ".bun", "bin", "bun"),
|
|
13
|
+
"/usr/local/bin/bun",
|
|
14
|
+
"/opt/homebrew/bin/bun",
|
|
15
|
+
"/home/linuxbrew/.linuxbrew/bin/bun",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function findBun() {
|
|
19
|
+
for (const candidate of BUN_CANDIDATES) {
|
|
20
|
+
if (existsSync(candidate)) return candidate;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const r = spawnSync("which", ["bun"], { encoding: "utf-8", timeout: 3000 });
|
|
24
|
+
if (r.status === 0 && r.stdout.trim()) return r.stdout.trim();
|
|
25
|
+
} catch {}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const bunPath = findBun();
|
|
30
|
+
if (!bunPath) {
|
|
31
|
+
process.stderr.write("[qrec] ERROR: bun not found. Install from https://bun.sh\n");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const qrecCjs = path.join(__dirname, "qrec.cjs");
|
|
36
|
+
const userArgs = process.argv.slice(2);
|
|
37
|
+
const bunArgs = ["run", qrecCjs, ...userArgs];
|
|
38
|
+
|
|
39
|
+
// serve --daemon: fire-and-fork so the hook returns immediately.
|
|
40
|
+
// Claude Code hooks never close stdin, so blocking on stdin would hang forever.
|
|
41
|
+
if (userArgs.includes("serve") && userArgs.includes("--daemon")) {
|
|
42
|
+
const child = spawn(bunPath, bunArgs, {
|
|
43
|
+
detached: true,
|
|
44
|
+
stdio: "ignore",
|
|
45
|
+
env: process.env,
|
|
46
|
+
});
|
|
47
|
+
child.unref();
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Buffer stdin before passing to bun (avoids libuv pipe crash on Linux).
|
|
52
|
+
const stdinChunks = [];
|
|
53
|
+
process.stdin.on("data", chunk => stdinChunks.push(chunk));
|
|
54
|
+
process.stdin.on("end", () => {
|
|
55
|
+
const buf = Buffer.concat(stdinChunks);
|
|
56
|
+
const result = spawnSync(bunPath, bunArgs, {
|
|
57
|
+
input: buf.length > 0 ? buf : undefined,
|
|
58
|
+
stdio: buf.length > 0 ? ["pipe", "inherit", "inherit"] : ["ignore", "inherit", "inherit"],
|
|
59
|
+
env: process.env,
|
|
60
|
+
});
|
|
61
|
+
process.exit(result.status ?? 1);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (process.stdin.isTTY) {
|
|
65
|
+
process.stdin.destroy();
|
|
66
|
+
const result = spawnSync(bunPath, bunArgs, {
|
|
67
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
68
|
+
env: process.env,
|
|
69
|
+
});
|
|
70
|
+
process.exit(result.status ?? 1);
|
|
71
|
+
}
|