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.
@@ -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
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "dude": {
4
+ "command": "node",
5
+ "args": ["bin/dude-claude.js", "mcp"]
6
+ }
7
+ }
8
+ }
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
+ }