dude-claude-plugin 2026.2.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/settings.local.json +38 -0
- package/.mcp.json +8 -0
- package/README.md +57 -0
- package/bin/dude-claude.js +23 -0
- package/doc/SPEC.md +322 -0
- package/hooks/auto-persist.js +59 -0
- package/hooks/auto-retrieve.js +46 -0
- package/package.json +24 -0
- package/src/db.js +260 -0
- package/src/embed.js +23 -0
- package/src/migrations/001-initial.js +30 -0
- package/src/server.js +134 -0
- package/src/web.js +180 -0
- package/web/index.html +346 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(git ls-tree:*)",
|
|
5
|
+
"WebSearch",
|
|
6
|
+
"WebFetch(domain:github.com)",
|
|
7
|
+
"WebFetch(domain:modelcontextprotocol.info)",
|
|
8
|
+
"WebFetch(domain:alexgarcia.xyz)",
|
|
9
|
+
"Bash(ls:*)",
|
|
10
|
+
"Bash(git -C /Users/fingerskier/dev/fingerskier/dude-claude-plugin log --oneline --all)",
|
|
11
|
+
"Bash(chmod:*)",
|
|
12
|
+
"Bash(npm install:*)",
|
|
13
|
+
"Bash(echo:*)",
|
|
14
|
+
"Bash(source ~/.zshrc)",
|
|
15
|
+
"Bash(node:*)",
|
|
16
|
+
"Bash(export NVM_DIR=\"$HOME/.nvm\")",
|
|
17
|
+
"Bash([ -s \"$NVM_DIR/nvm.sh\" ])",
|
|
18
|
+
"Bash(. \"$NVM_DIR/nvm.sh\")",
|
|
19
|
+
"Bash([ -s \"/opt/homebrew/opt/nvm/nvm.sh\" ])",
|
|
20
|
+
"Bash(. \"/opt/homebrew/opt/nvm/nvm.sh\")",
|
|
21
|
+
"Bash(nvm list:*)",
|
|
22
|
+
"Bash(volta which:*)",
|
|
23
|
+
"Bash(git -C /Users/fingerskier/dev/fingerskier/dude-claude-plugin ls-files:*)",
|
|
24
|
+
"Bash(git -C /Users/fingerskier/dev/fingerskier/dude-claude-plugin status --short)",
|
|
25
|
+
"Bash(git -C /Users/fingerskier/dev/fingerskier/dude-claude-plugin log --oneline -10)",
|
|
26
|
+
"Bash(xargs:*)",
|
|
27
|
+
"Bash(git -C /Users/fingerskier/dev/fingerskier/dude-claude-plugin check-ignore node_modules)",
|
|
28
|
+
"Bash(git rm:*)",
|
|
29
|
+
"Bash(git add:*)",
|
|
30
|
+
"Bash(git commit -m \"$\\(cat <<''EOF''\nAdd .gitignore and remove node_modules from git tracking\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"enableAllProjectMcpServers": true,
|
|
34
|
+
"enabledMcpjsonServers": [
|
|
35
|
+
"dude"
|
|
36
|
+
],
|
|
37
|
+
"outputStyle": "default"
|
|
38
|
+
}
|
package/.mcp.json
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Dude Claude Plugin
|
|
2
|
+
A context multiplier plug-in for Claude CLI
|
|
3
|
+
|
|
4
|
+
## Install
|
|
5
|
+
|
|
6
|
+
### npx (recommended)
|
|
7
|
+
|
|
8
|
+
Add MCP server config to Claude CLI settings (`~/.claude.json` or project `.mcp.json`):
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"mcpServers": {
|
|
13
|
+
"dude": {
|
|
14
|
+
"command": "npx",
|
|
15
|
+
"args": ["dude-claude-plugin", "mcp"]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### From source
|
|
22
|
+
|
|
23
|
+
1. Clone the repo
|
|
24
|
+
2. `npm install`
|
|
25
|
+
3. Add MCP server config:
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"dude": {
|
|
30
|
+
"command": "node",
|
|
31
|
+
"args": ["/path/to/dude-claude-plugin/bin/dude-claude.js", "mcp"]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
4. (Optional) Start the web UI: `npm run serve`
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
* Local sqlite database~ auto-create
|
|
41
|
+
* Save records for each project
|
|
42
|
+
* by repo name (for Git)
|
|
43
|
+
* by path (for non-Git)
|
|
44
|
+
* Each record gets a vector embedding
|
|
45
|
+
* Prior to a think
|
|
46
|
+
* retrieve relevant records from db via semantic search
|
|
47
|
+
* After each think
|
|
48
|
+
* If it's a fix upsert associated `issue`record(s)
|
|
49
|
+
* if it's an improvement upsert associated `specification` record(s)
|
|
50
|
+
* Tools for Claude
|
|
51
|
+
* search ~ semantic vector search
|
|
52
|
+
* CRUD project
|
|
53
|
+
* CRUD issue ~ per project
|
|
54
|
+
* CRID specification ~ per project
|
|
55
|
+
*
|
|
56
|
+
* Local webserver to do manual CRUD
|
|
57
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const command = process.argv[2] || 'mcp';
|
|
4
|
+
|
|
5
|
+
switch (command) {
|
|
6
|
+
case 'mcp': {
|
|
7
|
+
const { startServer } = await import('../src/server.js');
|
|
8
|
+
await startServer();
|
|
9
|
+
break;
|
|
10
|
+
}
|
|
11
|
+
case 'serve': {
|
|
12
|
+
const { startWebServer } = await import('../src/web.js');
|
|
13
|
+
await startWebServer();
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
default:
|
|
17
|
+
console.error(`Usage: dude-claude [mcp|serve]
|
|
18
|
+
|
|
19
|
+
Commands:
|
|
20
|
+
mcp Start the MCP stdio server (default)
|
|
21
|
+
serve Start the web UI server on http://127.0.0.1:${process.env.DUDE_PORT || 3456}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
package/doc/SPEC.md
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# Dude Claude Plugin — Implementation Specification
|
|
2
|
+
|
|
3
|
+
Ultra-minimal RAG and cross-project memory for Claude CLI.
|
|
4
|
+
|
|
5
|
+
## 1. Architecture Overview
|
|
6
|
+
|
|
7
|
+
The plugin is an **MCP (Model Context Protocol) stdio server** written in Node.js.
|
|
8
|
+
Claude CLI launches it as a subprocess and communicates via JSON-RPC 2.0 over stdin/stdout.
|
|
9
|
+
|
|
10
|
+
Companion **hooks** (configured in `.claude/settings.json`) fire at session boundaries to automatically inject retrieved context and persist learnings without explicit tool calls.
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Claude CLI
|
|
14
|
+
├── MCP stdio server (tools: search, CRUD)
|
|
15
|
+
│ └── SQLite + vec0 extension
|
|
16
|
+
└── Hooks
|
|
17
|
+
├── PreToolUse → auto-retrieve relevant records
|
|
18
|
+
└── Stop → auto-upsert issue/spec records
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 2. Technology Stack
|
|
22
|
+
|
|
23
|
+
| Component | Choice | Rationale |
|
|
24
|
+
|-----------------|-----------------------|----------------------------------------|
|
|
25
|
+
| Runtime | Node.js >=18 | Claude CLI ecosystem is JS/TS-centric |
|
|
26
|
+
| Language | Plain JavaScript (ESM)| Zero build step; ultra-minimal goal |
|
|
27
|
+
| Database | better-sqlite3 | Synchronous, zero-config, single-file |
|
|
28
|
+
| Vector search | sqlite-vec (vec0) | SQLite extension; no external service |
|
|
29
|
+
| Embeddings | Local: all-MiniLM-L6-v2 via `onnxruntime-node` | Offline, fast, 384-dim |
|
|
30
|
+
| MCP SDK | `@modelcontextprotocol/sdk` | Official MCP server library |
|
|
31
|
+
| Web UI | Bare `http` module + static HTML | No framework; minimal |
|
|
32
|
+
|
|
33
|
+
### Dependency Summary
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
dependencies:
|
|
37
|
+
@modelcontextprotocol/sdk
|
|
38
|
+
better-sqlite3
|
|
39
|
+
sqlite-vec
|
|
40
|
+
onnxruntime-node
|
|
41
|
+
@xenova/transformers # wraps ONNX for embedding generation
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 3. Data Model
|
|
45
|
+
|
|
46
|
+
Single SQLite file per user: `~/.dude-claude/dude.db`
|
|
47
|
+
|
|
48
|
+
### 3.1 `project`
|
|
49
|
+
|
|
50
|
+
| Column | Type | Notes |
|
|
51
|
+
|-------------|---------|--------------------------------------------|
|
|
52
|
+
| id | INTEGER | PK, autoincrement |
|
|
53
|
+
| name | TEXT | UNIQUE — repo name (git) or absolute path |
|
|
54
|
+
| created_at | TEXT | ISO-8601 |
|
|
55
|
+
| updated_at | TEXT | ISO-8601 |
|
|
56
|
+
|
|
57
|
+
### 3.2 `record`
|
|
58
|
+
|
|
59
|
+
A record is either an **issue** or a **specification**.
|
|
60
|
+
Both share one table to keep queries and embeddings uniform.
|
|
61
|
+
|
|
62
|
+
| Column | Type | Notes |
|
|
63
|
+
|-------------|---------|-----------------------------------------------|
|
|
64
|
+
| id | INTEGER | PK, autoincrement |
|
|
65
|
+
| project_id | INTEGER | FK → project.id |
|
|
66
|
+
| kind | TEXT | `'issue'` or `'spec'` |
|
|
67
|
+
| title | TEXT | Short summary |
|
|
68
|
+
| body | TEXT | Full description / details |
|
|
69
|
+
| status | TEXT | `'open'` / `'resolved'` / `'archived'` |
|
|
70
|
+
| created_at | TEXT | ISO-8601 |
|
|
71
|
+
| updated_at | TEXT | ISO-8601 |
|
|
72
|
+
|
|
73
|
+
### 3.3 `record_embedding` (virtual — vec0)
|
|
74
|
+
|
|
75
|
+
| Column | Type | Notes |
|
|
76
|
+
|-------------|--------------|--------------------------------|
|
|
77
|
+
| record_id | INTEGER | FK → record.id |
|
|
78
|
+
| embedding | FLOAT[384] | all-MiniLM-L6-v2 output |
|
|
79
|
+
|
|
80
|
+
Created via:
|
|
81
|
+
```sql
|
|
82
|
+
CREATE VIRTUAL TABLE record_embedding USING vec0(
|
|
83
|
+
record_id INTEGER PRIMARY KEY,
|
|
84
|
+
embedding FLOAT[384]
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 3.4 Project Identification
|
|
89
|
+
|
|
90
|
+
On startup the server determines the current project:
|
|
91
|
+
1. Run `git rev-parse --show-toplevel` — if it succeeds, use the **basename** as the project name.
|
|
92
|
+
2. Otherwise, use the **working directory path** as the project name.
|
|
93
|
+
3. Upsert into `project` table.
|
|
94
|
+
|
|
95
|
+
## 4. MCP Tools
|
|
96
|
+
|
|
97
|
+
All tools are exposed under the MCP server name `dude`. Claude sees them as `mcp__dude__<tool>`.
|
|
98
|
+
|
|
99
|
+
### 4.1 `search`
|
|
100
|
+
|
|
101
|
+
Semantic search across records.
|
|
102
|
+
By default, search includes cross-project results so that learnings from one project can inform another.
|
|
103
|
+
Results from the current project are ranked higher; cross-project results appear at lower weight.
|
|
104
|
+
Each result includes the originating `project` name/ID for disambiguation.
|
|
105
|
+
|
|
106
|
+
| Parameter | Type | Required | Default | Description |
|
|
107
|
+
|--------------|---------|----------|---------|-----------------------------------|
|
|
108
|
+
| query | string | yes | — | Natural language search query |
|
|
109
|
+
| kind | string | no | both | Filter: `'issue'`, `'spec'`, or `'all'` |
|
|
110
|
+
| project | string | no | current | Project name to boost; `'*'` for equal weight across all projects |
|
|
111
|
+
| limit | integer | no | 5 | Max results returned |
|
|
112
|
+
|
|
113
|
+
Returns: array of `{ id, project, kind, title, body, status, similarity }` sorted by descending similarity.
|
|
114
|
+
Results with similarity < 0.3 are excluded.
|
|
115
|
+
The `project` field defaults to the current project but is always present in the response so callers can distinguish cross-project results.
|
|
116
|
+
|
|
117
|
+
### 4.2 `upsert_record`
|
|
118
|
+
|
|
119
|
+
Create or update a record.
|
|
120
|
+
If `id` is provided, update; otherwise insert with deduplication (see below).
|
|
121
|
+
|
|
122
|
+
| Parameter | Type | Required | Description |
|
|
123
|
+
|------------|---------|----------|--------------------------|
|
|
124
|
+
| id | integer | no | Record ID to update |
|
|
125
|
+
| kind | string | yes | `'issue'` or `'spec'` |
|
|
126
|
+
| title | string | yes | Short summary |
|
|
127
|
+
| body | string | no | Full description |
|
|
128
|
+
| status | string | no | Defaults to `'open'` |
|
|
129
|
+
|
|
130
|
+
On upsert the server:
|
|
131
|
+
1. Generates an embedding from `title + ' ' + body`.
|
|
132
|
+
2. **Deduplication**: If no `id` is provided, query `record_embedding` for existing records in the same project and `kind` whose embedding distance is below a configurable threshold (default cosine distance ≤ 0.15). If a close match exists, treat the operation as an update of that record instead of creating a duplicate.
|
|
133
|
+
3. Writes (insert or update) the record row.
|
|
134
|
+
4. Upserts into `record_embedding`.
|
|
135
|
+
|
|
136
|
+
### 4.3 `get_record`
|
|
137
|
+
|
|
138
|
+
| Parameter | Type | Required |
|
|
139
|
+
|-----------|---------|----------|
|
|
140
|
+
| id | integer | yes |
|
|
141
|
+
|
|
142
|
+
Returns full record fields.
|
|
143
|
+
|
|
144
|
+
### 4.4 `list_records`
|
|
145
|
+
|
|
146
|
+
| Parameter | Type | Required | Default |
|
|
147
|
+
|-----------|---------|----------|---------|
|
|
148
|
+
| kind | string | no | both |
|
|
149
|
+
| status | string | no | all |
|
|
150
|
+
| project | string | no | current |
|
|
151
|
+
|
|
152
|
+
Returns array of `{ id, kind, title, status, updated_at }`.
|
|
153
|
+
|
|
154
|
+
### 4.5 `delete_record`
|
|
155
|
+
|
|
156
|
+
| Parameter | Type | Required |
|
|
157
|
+
|-----------|---------|----------|
|
|
158
|
+
| id | integer | yes |
|
|
159
|
+
|
|
160
|
+
Deletes record and its embedding.
|
|
161
|
+
|
|
162
|
+
### 4.6 `list_projects`
|
|
163
|
+
|
|
164
|
+
No parameters. Returns all known projects.
|
|
165
|
+
|
|
166
|
+
## 5. Hooks
|
|
167
|
+
|
|
168
|
+
Hooks are configured in the project or user settings and call into the MCP tools automatically.
|
|
169
|
+
|
|
170
|
+
### 5.1 Auto-Retrieve (UserPromptSubmit)
|
|
171
|
+
|
|
172
|
+
When the user submits a prompt, a hook runs `mcp__dude__search` with the user's message as the query.
|
|
173
|
+
Results are injected as additional context so Claude has relevant history before it begins reasoning.
|
|
174
|
+
|
|
175
|
+
**Settings entry:**
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"hooks": {
|
|
179
|
+
"UserPromptSubmit": [
|
|
180
|
+
{
|
|
181
|
+
"hooks": [
|
|
182
|
+
{
|
|
183
|
+
"type": "command",
|
|
184
|
+
"command": "node ~/.dude-claude/hooks/auto-retrieve.js"
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The hook script:
|
|
194
|
+
1. Reads the user prompt from stdin JSON (`tool_input` or equivalent).
|
|
195
|
+
2. Queries the SQLite database directly for speed (MCP is not required for hook scripts).
|
|
196
|
+
3. If results exist, writes the top **5** results (configurable via `DUDE_CONTEXT_LIMIT` env var or the `contextLimit` key in config) to stdout as context for Claude.
|
|
197
|
+
|
|
198
|
+
### 5.2 Auto-Persist (Stop)
|
|
199
|
+
|
|
200
|
+
When Claude finishes responding, a `Stop` hook evaluates whether the conversation involved a fix or improvement and upserts records accordingly.
|
|
201
|
+
|
|
202
|
+
**Settings entry:**
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"hooks": {
|
|
206
|
+
"Stop": [
|
|
207
|
+
{
|
|
208
|
+
"hooks": [
|
|
209
|
+
{
|
|
210
|
+
"type": "prompt",
|
|
211
|
+
"prompt": "Review the conversation. If a bug was fixed, output JSON: {\"action\":\"upsert\",\"kind\":\"issue\",\"title\":\"...\",\"body\":\"...\",\"status\":\"resolved\"}. If a feature or improvement was made, output JSON: {\"action\":\"upsert\",\"kind\":\"spec\",\"title\":\"...\",\"body\":\"...\"}. If neither, output {\"action\":\"none\"}.",
|
|
212
|
+
"timeout": 30
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
A follow-up command hook parses this output and calls `mcp__dude__upsert_record`.
|
|
222
|
+
|
|
223
|
+
**Fallback behavior**: If the model returns malformed JSON or declines to classify, the hook silently skips the upsert (no data loss, no user-facing error) **and** emits a tool-result message to Claude's worklog noting that the auto-persist step was skipped and why (e.g., "Auto-persist skipped: malformed JSON from classification prompt").
|
|
224
|
+
|
|
225
|
+
### 5.3 Classification Logic
|
|
226
|
+
|
|
227
|
+
"Fix" vs "improvement" is determined by the Stop hook's LLM prompt evaluation — not by heuristics.
|
|
228
|
+
The prompt asks Claude to classify the work that was done.
|
|
229
|
+
This keeps the logic simple and leverages the model's understanding of the conversation.
|
|
230
|
+
|
|
231
|
+
The same fallback applies here: if classification fails, the hook skips silently and logs a worklog message.
|
|
232
|
+
|
|
233
|
+
## 6. Web UI
|
|
234
|
+
|
|
235
|
+
A minimal local HTTP server for manual CRUD when Claude CLI isn't running.
|
|
236
|
+
|
|
237
|
+
| Detail | Value |
|
|
238
|
+
|------------|------------------------------------------|
|
|
239
|
+
| Port | 3456 (configurable via `DUDE_PORT` env) |
|
|
240
|
+
| Start | `npx dude-claude serve` |
|
|
241
|
+
| Auth | None (localhost only, binds 127.0.0.1) |
|
|
242
|
+
|
|
243
|
+
### Endpoints
|
|
244
|
+
|
|
245
|
+
| Method | Path | Description |
|
|
246
|
+
|--------|--------------------------|---------------------------|
|
|
247
|
+
| GET | `/` | Static HTML SPA |
|
|
248
|
+
| GET | `/api/projects` | List projects |
|
|
249
|
+
| GET | `/api/records?project=&kind=&status=` | List records |
|
|
250
|
+
| GET | `/api/records/:id` | Get record |
|
|
251
|
+
| POST | `/api/records` | Create record |
|
|
252
|
+
| PUT | `/api/records/:id` | Update record |
|
|
253
|
+
| DELETE | `/api/records/:id` | Delete record |
|
|
254
|
+
| POST | `/api/search` | Semantic search |
|
|
255
|
+
|
|
256
|
+
The SPA is a single `index.html` file served from `web/index.html` using the built-in `http` module. No bundler.
|
|
257
|
+
|
|
258
|
+
## 7. File Layout
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
dude-claude-plugin/
|
|
262
|
+
package.json
|
|
263
|
+
bin/
|
|
264
|
+
dude-claude.js # CLI entry point (MCP server + serve command)
|
|
265
|
+
src/
|
|
266
|
+
server.js # MCP server setup + tool handlers
|
|
267
|
+
db.js # SQLite schema init, migration runner, query helpers
|
|
268
|
+
embed.js # Embedding generation
|
|
269
|
+
web.js # HTTP server for manual CRUD
|
|
270
|
+
migrations/
|
|
271
|
+
001-initial.js # Creates project, record, record_embedding tables
|
|
272
|
+
web/
|
|
273
|
+
index.html # Single-page CRUD UI
|
|
274
|
+
hooks/
|
|
275
|
+
auto-retrieve.js # UserPromptSubmit hook script
|
|
276
|
+
auto-persist.js # Stop hook follow-up script
|
|
277
|
+
doc/
|
|
278
|
+
SPEC.md # This file
|
|
279
|
+
.mcp.json # MCP server registration for Claude CLI
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## 8. Schema Migration
|
|
283
|
+
|
|
284
|
+
Schema changes are handled via **versioned migration scripts** stored in `src/migrations/` (e.g., `001-initial.js`, `002-add-index.js`).
|
|
285
|
+
|
|
286
|
+
On startup, `db.js`:
|
|
287
|
+
1. Ensures a `schema_version` table exists (`CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY)`).
|
|
288
|
+
2. Reads the current version (or 0 if the table is empty).
|
|
289
|
+
3. Runs any migration scripts with a version number greater than the current version, in order.
|
|
290
|
+
4. Updates `schema_version` to the latest version after all pending migrations succeed.
|
|
291
|
+
|
|
292
|
+
Migrations run inside a transaction so a failed migration leaves the database unchanged.
|
|
293
|
+
|
|
294
|
+
### File Layout Addition
|
|
295
|
+
|
|
296
|
+
```
|
|
297
|
+
src/
|
|
298
|
+
migrations/
|
|
299
|
+
001-initial.js # Creates project, record, record_embedding tables
|
|
300
|
+
002-... # Future schema changes
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## 9. Configuration
|
|
304
|
+
|
|
305
|
+
### `.mcp.json` (project-scoped, committed)
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"mcpServers": {
|
|
310
|
+
"dude": {
|
|
311
|
+
"command": "node",
|
|
312
|
+
"args": ["bin/dude-claude.js", "mcp"]
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### User-global install
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
claude mcp add --transport stdio dude -- node /path/to/dude-claude-plugin/bin/dude-claude.js mcp
|
|
322
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stop hook — auto-persist records from conversation classification.
|
|
5
|
+
* Reads classification JSON from stdin, upserts records as needed.
|
|
6
|
+
* On malformed JSON or action=none, exits silently.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { embed } from '../src/embed.js';
|
|
10
|
+
import { initDb, upsertRecord, getCurrentProject } from '../src/db.js';
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const chunks = [];
|
|
14
|
+
for await (const chunk of process.stdin) {
|
|
15
|
+
chunks.push(chunk);
|
|
16
|
+
}
|
|
17
|
+
const raw = Buffer.concat(chunks).toString().trim();
|
|
18
|
+
|
|
19
|
+
let input;
|
|
20
|
+
try {
|
|
21
|
+
input = JSON.parse(raw);
|
|
22
|
+
} catch {
|
|
23
|
+
process.stdout.write('Auto-persist skipped: malformed JSON from classification prompt\n');
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!input.action || input.action === 'none') {
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (input.action === 'upsert') {
|
|
32
|
+
const kind = input.kind || 'issue';
|
|
33
|
+
const title = input.title || 'Untitled';
|
|
34
|
+
const body = input.body || '';
|
|
35
|
+
const status = input.status || 'open';
|
|
36
|
+
|
|
37
|
+
await initDb();
|
|
38
|
+
const text = `${title} ${body}`.trim();
|
|
39
|
+
const embedding = await embed(text);
|
|
40
|
+
|
|
41
|
+
const record = upsertRecord(
|
|
42
|
+
{
|
|
43
|
+
projectId: getCurrentProject().id,
|
|
44
|
+
kind,
|
|
45
|
+
title,
|
|
46
|
+
body,
|
|
47
|
+
status,
|
|
48
|
+
},
|
|
49
|
+
embedding,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
process.stdout.write(`Auto-persisted ${kind}: "${record.title}" (id=${record.id})\n`);
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
// Non-blocking: exit cleanly on any error
|
|
56
|
+
console.error(`[dude] auto-persist error: ${err.message}`);
|
|
57
|
+
process.stdout.write(`Auto-persist skipped: ${err.message}\n`);
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* UserPromptSubmit hook — auto-retrieve relevant records.
|
|
5
|
+
* Reads the user prompt from stdin JSON, embeds it, searches the DB,
|
|
6
|
+
* and writes formatted context to stdout for Claude to see.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { embed } from '../src/embed.js';
|
|
10
|
+
import { initDb, searchRecords } from '../src/db.js';
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const chunks = [];
|
|
14
|
+
for await (const chunk of process.stdin) {
|
|
15
|
+
chunks.push(chunk);
|
|
16
|
+
}
|
|
17
|
+
const input = JSON.parse(Buffer.concat(chunks).toString());
|
|
18
|
+
const prompt = input.prompt || input.tool_input?.prompt || '';
|
|
19
|
+
|
|
20
|
+
if (!prompt.trim()) {
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await initDb();
|
|
25
|
+
const embedding = await embed(prompt);
|
|
26
|
+
const limit = Number(process.env.DUDE_CONTEXT_LIMIT) || 5;
|
|
27
|
+
const results = searchRecords(embedding, { limit });
|
|
28
|
+
|
|
29
|
+
if (results.length === 0) {
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Format context for Claude
|
|
34
|
+
const lines = ['[dude] Relevant context from memory:\n'];
|
|
35
|
+
for (const r of results) {
|
|
36
|
+
lines.push(`- [${r.kind}] ${r.title} (project: ${r.project}, status: ${r.status}, similarity: ${r.similarity.toFixed(2)})`);
|
|
37
|
+
if (r.body) {
|
|
38
|
+
lines.push(` ${r.body.slice(0, 200)}${r.body.length > 200 ? '…' : ''}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
42
|
+
} catch (err) {
|
|
43
|
+
// Non-blocking: exit cleanly on any error
|
|
44
|
+
console.error(`[dude] auto-retrieve error: ${err.message}`);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dude-claude-plugin",
|
|
3
|
+
"version": "2026.2.1",
|
|
4
|
+
"description": "Ultra-minimal RAG and cross-project memory for Claude CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dude-claude": "bin/dude-claude.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"start": "node bin/dude-claude.js mcp",
|
|
14
|
+
"serve": "node bin/dude-claude.js serve"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
18
|
+
"zod": "^3.24.2",
|
|
19
|
+
"better-sqlite3": "^11.8.1",
|
|
20
|
+
"sqlite-vec": "^0.1.6",
|
|
21
|
+
"@huggingface/transformers": "^3.4.1"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT"
|
|
24
|
+
}
|