dude-claude-plugin 2026.2.18 → 2026.2.19

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dude",
3
- "version": "2026.2.18",
3
+ "version": "2026.2.19",
4
4
  "description": "Ultra-minimal RAG and cross-project memory for Claude CLI",
5
5
  "author": {
6
6
  "name": "Fingerskier"
package/README.md CHANGED
@@ -63,7 +63,17 @@ npx dude-claude-plugin serve
63
63
  dude-claude serve
64
64
  ```
65
65
 
66
- Opens a local dashboard on port 3456 for browsing and editing projects, issues, and specifications.
66
+ Opens a local dashboard at `http://127.0.0.1:3456` (auto-opens in your browser). The UI has two panels: a sidebar listing records with filters, and a main panel for viewing and editing.
67
+
68
+ **What you can do:**
69
+
70
+ - **Project selector** — filter records by project or view all projects at once
71
+ - **Semantic search** — type in the search bar to find records by meaning, not just keywords (results ranked by similarity)
72
+ - **Filter by kind** — narrow to issues, specs, arch decisions, updates, or tests
73
+ - **Filter by status** — open, resolved, archived, active, or inactive
74
+ - **Create / edit / delete** — click "+ New" to add a record, click any record to edit it, or delete from the detail view
75
+
76
+ Set `DUDE_PORT` to change the default port (see [Configuration](#configuration)).
67
77
 
68
78
  ## Configuration
69
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dude-claude-plugin",
3
- "version": "2026.2.18",
3
+ "version": "2026.2.19",
4
4
  "description": "Ultra-minimal RAG and cross-project memory for Claude CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Export the local libsql database as SQL INSERT statements.
4
+ *
5
+ * Usage:
6
+ * node scripts/dump-local.js > dump.sql
7
+ * turso db shell <dbname> < dump.sql
8
+ *
9
+ * Reads from ~/.dude-claude/dude-libsql.db (or DUDE_DB_PATH env override).
10
+ * Outputs projects first, then records (with vector embeddings), to stdout.
11
+ */
12
+
13
+ import { createClient } from '@libsql/client';
14
+ import { join } from 'node:path';
15
+ import { homedir } from 'node:os';
16
+
17
+ const DATA_DIR = join(homedir(), '.dude-claude');
18
+ const DB_PATH = process.env.DUDE_DB_PATH || join(DATA_DIR, 'dude-libsql.db');
19
+
20
+ function esc(str) {
21
+ if (str == null) return 'NULL';
22
+ return "'" + String(str).replace(/'/g, "''") + "'";
23
+ }
24
+
25
+ function parseEmbedding(blob) {
26
+ if (!blob) return null;
27
+ if (blob instanceof Float32Array) return blob;
28
+ if (blob instanceof ArrayBuffer) return new Float32Array(blob);
29
+ if (ArrayBuffer.isView(blob)) {
30
+ return new Float32Array(blob.buffer, blob.byteOffset, blob.byteLength / 4);
31
+ }
32
+ if (typeof blob === 'string') return new Float32Array(JSON.parse(blob));
33
+ return null;
34
+ }
35
+
36
+ async function main() {
37
+ const db = createClient({ url: `file:${DB_PATH}` });
38
+
39
+ // -- Projects --
40
+ const projects = await db.execute('SELECT * FROM project ORDER BY id');
41
+ for (const p of projects.rows) {
42
+ console.log(
43
+ `INSERT INTO project (id, name, created_at, updated_at) VALUES (${p.id}, ${esc(p.name)}, ${esc(p.created_at)}, ${esc(p.updated_at)});`
44
+ );
45
+ }
46
+
47
+ // -- Records (with embeddings) --
48
+ const records = await db.execute('SELECT * FROM record ORDER BY id');
49
+ for (const r of records.rows) {
50
+ const emb = parseEmbedding(r.embedding);
51
+ const embExpr = emb
52
+ ? `vector('${JSON.stringify(Array.from(emb))}')`
53
+ : 'NULL';
54
+
55
+ console.log(
56
+ `INSERT INTO record (id, project_id, kind, title, body, status, embedding, created_at, updated_at) VALUES (${r.id}, ${r.project_id}, ${esc(r.kind)}, ${esc(r.title)}, ${esc(r.body)}, ${esc(r.status)}, ${embExpr}, ${esc(r.created_at)}, ${esc(r.updated_at)});`
57
+ );
58
+ }
59
+
60
+ db.close();
61
+ console.error(`[dump] Exported ${projects.rows.length} projects, ${records.rows.length} records`);
62
+ }
63
+
64
+ main().catch(err => {
65
+ console.error('dump-local failed:', err);
66
+ process.exit(1);
67
+ });
package/src/db-libsql.js CHANGED
@@ -21,6 +21,7 @@ export class LibsqlAdapter extends DbAdapter {
21
21
  this.config = config;
22
22
  this.db = null;
23
23
  this.currentProject = null;
24
+ this._syncError = null;
24
25
  }
25
26
 
26
27
  // ---------------------------------------------------------------------------
@@ -30,13 +31,32 @@ export class LibsqlAdapter extends DbAdapter {
30
31
  async init() {
31
32
  if (this.db) return;
32
33
  this._ensureDataDir();
33
- this.db = this._createClient();
34
+ this._syncError = null;
35
+
36
+ if (this._hasSyncConfig()) {
37
+ try {
38
+ this.db = this._createClient(); // tries sync-enabled client
39
+ } catch (err) {
40
+ console.error(`[dude] Cloud sync connection failed: ${err.message}`);
41
+ console.error('[dude] Falling back to local-only mode.');
42
+ this._syncError = err.message;
43
+ this.db = this._createLocalOnlyClient();
44
+ }
45
+ } else {
46
+ this.db = this._createLocalOnlyClient();
47
+ }
48
+
34
49
  await this._runSchema();
35
50
  const projectName = this._detectProject();
36
51
  this.currentProject = await this._upsertProject(projectName);
37
52
  await this._migrateProjectNames(projectName);
53
+
38
54
  const syncInfo = await this.syncStatus();
39
- const syncMsg = syncInfo.enabled ? ` | cloud sync → ${syncInfo.syncUrl}` : '';
55
+ const syncMsg = syncInfo.enabled
56
+ ? ` | cloud sync → ${syncInfo.syncUrl}`
57
+ : syncInfo.error
58
+ ? ' | cloud sync FAILED — local-only mode'
59
+ : '';
40
60
  console.error(`[dude] LibSQL DB ready — project "${this.currentProject.name}" (id=${this.currentProject.id})${syncMsg}`);
41
61
  }
42
62
 
@@ -46,6 +66,15 @@ export class LibsqlAdapter extends DbAdapter {
46
66
  }
47
67
  }
48
68
 
69
+ _hasSyncConfig() {
70
+ return !!(this.config.syncUrl || process.env.DUDE_TURSO_URL);
71
+ }
72
+
73
+ _createLocalOnlyClient() {
74
+ const url = this.config.url || `file:${this.config.dbPath || DB_PATH}`;
75
+ return createClient({ url });
76
+ }
77
+
49
78
  _createClient() {
50
79
  const url = this.config.url
51
80
  || `file:${this.config.dbPath || DB_PATH}`;
@@ -55,8 +84,8 @@ export class LibsqlAdapter extends DbAdapter {
55
84
  if (this.config.syncUrl || process.env.DUDE_TURSO_URL) {
56
85
  opts.syncUrl = this.config.syncUrl || process.env.DUDE_TURSO_URL;
57
86
  opts.authToken = this.config.authToken || process.env.DUDE_TURSO_TOKEN;
58
- const interval = this.config.syncInterval || process.env.DUDE_SYNC_INTERVAL;
59
- if (interval) opts.syncInterval = parseInt(interval);
87
+ const interval = this.config.syncInterval || process.env.DUDE_SYNC_INTERVAL || 60000;
88
+ opts.syncInterval = parseInt(interval);
60
89
  }
61
90
 
62
91
  return createClient(opts);
@@ -419,22 +448,39 @@ export class LibsqlAdapter extends DbAdapter {
419
448
  const syncUrl = this.config.syncUrl || process.env.DUDE_TURSO_URL || null;
420
449
  const syncInterval = this.config.syncInterval
421
450
  || process.env.DUDE_SYNC_INTERVAL
422
- || null;
451
+ || (syncUrl ? 60000 : null);
423
452
  return {
424
- enabled: !!syncUrl,
453
+ enabled: !!syncUrl && !this._syncError,
425
454
  ...(syncUrl ? { syncUrl } : {}),
426
455
  ...(syncInterval ? { syncInterval: parseInt(syncInterval) } : {}),
456
+ ...(this._syncError ? { error: this._syncError, degraded: true } : {}),
427
457
  };
428
458
  }
429
459
 
430
460
  async sync() {
431
- const status = await this.syncStatus();
432
- if (!status.enabled) {
461
+ if (!this._hasSyncConfig()) {
433
462
  return { synced: false, message: 'Cloud sync not configured. Set DUDE_TURSO_URL and DUDE_TURSO_TOKEN to enable.' };
434
463
  }
464
+
465
+ // If degraded, attempt to reconnect with sync-enabled client
466
+ if (this._syncError) {
467
+ try {
468
+ const newClient = this._createClient();
469
+ // Run a test query to verify the connection works
470
+ await newClient.execute('SELECT 1');
471
+ this.db.close();
472
+ this.db = newClient;
473
+ this._syncError = null;
474
+ console.error('[dude] Cloud sync reconnected successfully.');
475
+ return { synced: true, message: `Reconnected and synced with ${this.config.syncUrl || process.env.DUDE_TURSO_URL}` };
476
+ } catch (err) {
477
+ return { synced: false, message: `Reconnection failed: ${err.message}` };
478
+ }
479
+ }
480
+
435
481
  try {
436
482
  await this.db.sync();
437
- return { synced: true, message: `Synced with ${status.syncUrl}` };
483
+ return { synced: true, message: `Synced with ${this.config.syncUrl || process.env.DUDE_TURSO_URL}` };
438
484
  } catch (err) {
439
485
  return { synced: false, message: `Sync failed: ${err.message}` };
440
486
  }