dude-claude-plugin 2026.2.6 → 2026.2.7

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 (3) hide show
  1. package/package.json +1 -1
  2. package/src/db.js +55 -46
  3. package/src/server.js +62 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dude-claude-plugin",
3
- "version": "2026.2.6",
3
+ "version": "2026.2.7",
4
4
  "description": "Ultra-minimal RAG and cross-project memory for Claude CLI",
5
5
  "type": "module",
6
6
  "bin": {
package/src/db.js CHANGED
@@ -32,6 +32,8 @@ function openDb() {
32
32
  sqliteVec.load(d);
33
33
  d.pragma('journal_mode = WAL');
34
34
  d.pragma('foreign_keys = ON');
35
+ d.pragma('busy_timeout = 5000');
36
+ console.error(`[dude] Database opened: ${DB_PATH}`);
35
37
  return d;
36
38
  }
37
39
 
@@ -156,9 +158,12 @@ export function listRecords({ kind, status, project } = {}) {
156
158
 
157
159
  export function deleteRecord(id) {
158
160
  const d = getDb();
159
- d.prepare('DELETE FROM record_embedding WHERE record_id = ?').run(id);
160
- const result = d.prepare('DELETE FROM record WHERE id = ?').run(id);
161
- return result.changes > 0;
161
+ const tx = d.transaction(() => {
162
+ d.prepare('DELETE FROM record_embedding WHERE record_id = ?').run(id);
163
+ const result = d.prepare('DELETE FROM record WHERE id = ?').run(id);
164
+ return result.changes > 0;
165
+ });
166
+ return tx();
162
167
  }
163
168
 
164
169
  export function searchRecords(embedding, { kind, projectId, limit = 5 } = {}) {
@@ -213,53 +218,57 @@ export function upsertRecord({ id, projectId, kind, title, body = '', status = '
213
218
  const proj = projectId ?? getCurrentProject().id;
214
219
  const now = new Date().toISOString();
215
220
 
216
- if (id) {
217
- // Explicit update
218
- d.prepare(`
219
- UPDATE record SET kind = ?, title = ?, body = ?, status = ?, updated_at = ?
220
- WHERE id = ?
221
- `).run(kind, title, body, status, now, id);
221
+ const tx = d.transaction(() => {
222
+ if (id) {
223
+ // Explicit update
224
+ d.prepare(`
225
+ UPDATE record SET kind = ?, title = ?, body = ?, status = ?, updated_at = ?
226
+ WHERE id = ?
227
+ `).run(kind, title, body, status, now, id);
222
228
 
223
- // vec0 doesn't support UPDATE — delete then insert
224
- d.prepare('DELETE FROM record_embedding WHERE record_id = ?').run(id);
225
- d.prepare('INSERT INTO record_embedding (record_id, embedding) VALUES (?, ?)').run(BigInt(id), embeddingBuffer(embedding));
229
+ // vec0 doesn't support UPDATE — delete then insert
230
+ d.prepare('DELETE FROM record_embedding WHERE record_id = ?').run(id);
231
+ d.prepare('INSERT INTO record_embedding (record_id, embedding) VALUES (?, ?)').run(BigInt(id), embeddingBuffer(embedding));
226
232
 
227
- return getRecord(id);
228
- }
233
+ return getRecord(id);
234
+ }
229
235
 
230
- // Dedup check: look for close matches in same project+kind
231
- const candidates = d.prepare(`
232
- SELECT re.record_id, re.distance
233
- FROM record_embedding re
234
- JOIN record r ON r.id = re.record_id
235
- WHERE re.embedding MATCH ? AND k = 5
236
- AND r.project_id = ? AND r.kind = ?
237
- ORDER BY re.distance
238
- LIMIT 1
239
- `).all(embeddingBuffer(embedding), proj, kind);
240
-
241
- if (candidates.length > 0 && candidates[0].distance <= 0.15) {
242
- // Close match found — update existing record
243
- const existingId = candidates[0].record_id;
244
- d.prepare(`
245
- UPDATE record SET title = ?, body = ?, status = ?, updated_at = ?
246
- WHERE id = ?
247
- `).run(title, body, status, now, existingId);
248
-
249
- d.prepare('DELETE FROM record_embedding WHERE record_id = ?').run(existingId);
250
- d.prepare('INSERT INTO record_embedding (record_id, embedding) VALUES (?, ?)').run(BigInt(existingId), embeddingBuffer(embedding));
251
-
252
- return getRecord(existingId);
253
- }
236
+ // Dedup check: look for close matches in same project+kind
237
+ const candidates = d.prepare(`
238
+ SELECT re.record_id, re.distance
239
+ FROM record_embedding re
240
+ JOIN record r ON r.id = re.record_id
241
+ WHERE re.embedding MATCH ? AND k = 5
242
+ AND r.project_id = ? AND r.kind = ?
243
+ ORDER BY re.distance
244
+ LIMIT 1
245
+ `).all(embeddingBuffer(embedding), proj, kind);
246
+
247
+ if (candidates.length > 0 && candidates[0].distance <= 0.15) {
248
+ // Close match found — update existing record
249
+ const existingId = candidates[0].record_id;
250
+ d.prepare(`
251
+ UPDATE record SET title = ?, body = ?, status = ?, updated_at = ?
252
+ WHERE id = ?
253
+ `).run(title, body, status, now, existingId);
254
+
255
+ d.prepare('DELETE FROM record_embedding WHERE record_id = ?').run(existingId);
256
+ d.prepare('INSERT INTO record_embedding (record_id, embedding) VALUES (?, ?)').run(BigInt(existingId), embeddingBuffer(embedding));
257
+
258
+ return getRecord(existingId);
259
+ }
254
260
 
255
- // Insert new record
256
- const result = d.prepare(`
257
- INSERT INTO record (project_id, kind, title, body, status, created_at, updated_at)
258
- VALUES (?, ?, ?, ?, ?, ?, ?)
259
- `).run(proj, kind, title, body, status, now, now);
261
+ // Insert new record
262
+ const result = d.prepare(`
263
+ INSERT INTO record (project_id, kind, title, body, status, created_at, updated_at)
264
+ VALUES (?, ?, ?, ?, ?, ?, ?)
265
+ `).run(proj, kind, title, body, status, now, now);
260
266
 
261
- const newId = result.lastInsertRowid;
262
- d.prepare('INSERT INTO record_embedding (record_id, embedding) VALUES (?, ?)').run(BigInt(newId), embeddingBuffer(embedding));
267
+ const newId = result.lastInsertRowid;
268
+ d.prepare('INSERT INTO record_embedding (record_id, embedding) VALUES (?, ?)').run(BigInt(newId), embeddingBuffer(embedding));
269
+
270
+ return getRecord(Number(newId));
271
+ });
263
272
 
264
- return getRecord(Number(newId));
273
+ return tx();
265
274
  }
package/src/server.js CHANGED
@@ -32,11 +32,16 @@ export async function startServer() {
32
32
  limit: z.number().int().positive().optional().describe('Max results (default 5)'),
33
33
  },
34
34
  async ({ query, kind, project, limit }) => {
35
- const embedding = await embed(query);
36
- const results = searchRecords(embedding, { kind, limit });
37
- return {
38
- content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
39
- };
35
+ try {
36
+ const embedding = await embed(query);
37
+ const results = searchRecords(embedding, { kind, limit });
38
+ return {
39
+ content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
40
+ };
41
+ } catch (err) {
42
+ console.error('[dude] search failed:', err);
43
+ return { content: [{ type: 'text', text: `Error in search: ${err.message}` }], isError: true };
44
+ }
40
45
  },
41
46
  );
42
47
 
@@ -52,15 +57,20 @@ export async function startServer() {
52
57
  status: z.enum(['open', 'resolved', 'archived']).optional().describe('Defaults to open'),
53
58
  },
54
59
  async ({ id, kind, title, body, status }) => {
55
- const text = `${title} ${body || ''}`.trim();
56
- const embedding = await embed(text);
57
- const record = upsertRecord(
58
- { id, projectId: getCurrentProject().id, kind, title, body: body || '', status: status || 'open' },
59
- embedding,
60
- );
61
- return {
62
- content: [{ type: 'text', text: JSON.stringify(record, null, 2) }],
63
- };
60
+ try {
61
+ const text = `${title} ${body || ''}`.trim();
62
+ const embedding = await embed(text);
63
+ const record = upsertRecord(
64
+ { id, projectId: getCurrentProject().id, kind, title, body: body || '', status: status || 'open' },
65
+ embedding,
66
+ );
67
+ return {
68
+ content: [{ type: 'text', text: JSON.stringify(record, null, 2) }],
69
+ };
70
+ } catch (err) {
71
+ console.error('[dude] upsert_record failed:', err);
72
+ return { content: [{ type: 'text', text: `Error in upsert_record: ${err.message}` }], isError: true };
73
+ }
64
74
  },
65
75
  );
66
76
 
@@ -72,13 +82,18 @@ export async function startServer() {
72
82
  id: z.number().int().describe('Record ID'),
73
83
  },
74
84
  async ({ id }) => {
75
- const record = getRecord(id);
76
- if (!record) {
77
- return { content: [{ type: 'text', text: `Record ${id} not found.` }], isError: true };
85
+ try {
86
+ const record = getRecord(id);
87
+ if (!record) {
88
+ return { content: [{ type: 'text', text: `Record ${id} not found.` }], isError: true };
89
+ }
90
+ return {
91
+ content: [{ type: 'text', text: JSON.stringify(record, null, 2) }],
92
+ };
93
+ } catch (err) {
94
+ console.error('[dude] get_record failed:', err);
95
+ return { content: [{ type: 'text', text: `Error in get_record: ${err.message}` }], isError: true };
78
96
  }
79
- return {
80
- content: [{ type: 'text', text: JSON.stringify(record, null, 2) }],
81
- };
82
97
  },
83
98
  );
84
99
 
@@ -92,10 +107,15 @@ export async function startServer() {
92
107
  project: z.string().optional().describe('Project name, or "*" for all'),
93
108
  },
94
109
  async ({ kind, status, project }) => {
95
- const records = listRecords({ kind, status, project });
96
- return {
97
- content: [{ type: 'text', text: JSON.stringify(records, null, 2) }],
98
- };
110
+ try {
111
+ const records = listRecords({ kind, status, project });
112
+ return {
113
+ content: [{ type: 'text', text: JSON.stringify(records, null, 2) }],
114
+ };
115
+ } catch (err) {
116
+ console.error('[dude] list_records failed:', err);
117
+ return { content: [{ type: 'text', text: `Error in list_records: ${err.message}` }], isError: true };
118
+ }
99
119
  },
100
120
  );
101
121
 
@@ -107,10 +127,15 @@ export async function startServer() {
107
127
  id: z.number().int().describe('Record ID to delete'),
108
128
  },
109
129
  async ({ id }) => {
110
- const deleted = deleteRecord(id);
111
- return {
112
- content: [{ type: 'text', text: deleted ? `Record ${id} deleted.` : `Record ${id} not found.` }],
113
- };
130
+ try {
131
+ const deleted = deleteRecord(id);
132
+ return {
133
+ content: [{ type: 'text', text: deleted ? `Record ${id} deleted.` : `Record ${id} not found.` }],
134
+ };
135
+ } catch (err) {
136
+ console.error('[dude] delete_record failed:', err);
137
+ return { content: [{ type: 'text', text: `Error in delete_record: ${err.message}` }], isError: true };
138
+ }
114
139
  },
115
140
  );
116
141
 
@@ -120,10 +145,15 @@ export async function startServer() {
120
145
  'List all known projects.',
121
146
  {},
122
147
  async () => {
123
- const projects = listProjects();
124
- return {
125
- content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }],
126
- };
148
+ try {
149
+ const projects = listProjects();
150
+ return {
151
+ content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }],
152
+ };
153
+ } catch (err) {
154
+ console.error('[dude] list_projects failed:', err);
155
+ return { content: [{ type: 'text', text: `Error in list_projects: ${err.message}` }], isError: true };
156
+ }
127
157
  },
128
158
  );
129
159