dude-claude-plugin 2026.2.12 → 2026.2.14

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
- "name": "dude-claude-plugin",
3
- "version": "2026.2.12",
2
+ "name": "dude",
3
+ "version": "2026.2.14",
4
4
  "description": "Ultra-minimal RAG and cross-project memory for Claude CLI",
5
5
  "author": {
6
6
  "name": "Fingerskier"
@@ -32,7 +32,7 @@ Commands:
32
32
  mcp Start the MCP stdio server (default)
33
33
  serve Start the web UI server on http://127.0.0.1:${process.env.DUDE_PORT || 3456}
34
34
  auto-retrieve Run the auto-retrieve hook (reads prompt from stdin)
35
- auto-persist Run the auto-persist hook (reads classification from stdin)
36
- auto-persist-plan Run the auto-persist-plan hook for plan events`);
35
+ auto-persist Run the auto-persist utility (reads classification JSON from stdin)
36
+ auto-persist-plan Run the auto-persist-plan utility (reads classification JSON from stdin)`);
37
37
  process.exit(1);
38
38
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { embed } from '../src/embed.js';
10
- import { initDb, upsertRecord, getCurrentProject } from '../src/db.js';
10
+ import { initDb } from '../src/db.js';
11
11
 
12
12
  try {
13
13
  const chunks = [];
@@ -34,13 +34,14 @@ try {
34
34
  const body = input.body || '';
35
35
  const status = input.status || 'open';
36
36
 
37
- await initDb();
37
+ const db = await initDb();
38
38
  const text = `${title} ${body}`.trim();
39
39
  const embedding = await embed(text);
40
40
 
41
- const record = upsertRecord(
41
+ const project = await db.getCurrentProject();
42
+ const record = await db.upsert(
42
43
  {
43
- projectId: getCurrentProject().id,
44
+ projectId: project.id,
44
45
  kind,
45
46
  title,
46
47
  body,
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { embed } from '../src/embed.js';
10
- import { initDb, upsertRecord, getCurrentProject } from '../src/db.js';
10
+ import { initDb } from '../src/db.js';
11
11
 
12
12
  try {
13
13
  const chunks = [];
@@ -34,13 +34,14 @@ try {
34
34
  const body = input.body || '';
35
35
  const status = input.status || 'open';
36
36
 
37
- await initDb();
37
+ const db = await initDb();
38
38
  const text = `${title} ${body}`.trim();
39
39
  const embedding = await embed(text);
40
40
 
41
- const record = upsertRecord(
41
+ const project = await db.getCurrentProject();
42
+ const record = await db.upsert(
42
43
  {
43
- projectId: getCurrentProject().id,
44
+ projectId: project.id,
44
45
  kind,
45
46
  title,
46
47
  body,
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { embed } from '../src/embed.js';
10
- import { initDb, searchRecords } from '../src/db.js';
10
+ import { initDb } from '../src/db.js';
11
11
 
12
12
  try {
13
13
  const chunks = [];
@@ -21,24 +21,38 @@ try {
21
21
  process.exit(0);
22
22
  }
23
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 });
24
+ const db = await initDb();
25
+ const project = await db.getCurrentProject();
28
26
 
29
- if (results.length === 0) {
30
- process.exit(0);
27
+ // 1) Project identification
28
+ process.stdout.write(`[dude] Project: ${project.name} (id=${project.id})\n`);
29
+
30
+ // 2) Recently updated records
31
+ const recencyWindow = Number(process.env.DUDE_RECENCY_HOURS) || 1;
32
+ const recentRecords = await db.getRecentRecords(project.id, recencyWindow);
33
+ if (recentRecords.length > 0) {
34
+ const recentLines = ['[dude] Recently updated records:'];
35
+ for (const r of recentRecords) {
36
+ recentLines.push(`- [${r.kind}] ${r.title} (id=${r.id}, status=${r.status}, updated: ${r.updated_at})`);
37
+ }
38
+ process.stdout.write(recentLines.join('\n') + '\n');
31
39
  }
32
40
 
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 ? '…' : ''}`);
41
+ // 3) Semantic search
42
+ const embedding = await embed(prompt);
43
+ const limit = Number(process.env.DUDE_CONTEXT_LIMIT) || 5;
44
+ const results = await db.search(embedding, { limit });
45
+
46
+ if (results.length > 0) {
47
+ const lines = ['[dude] Relevant context from memory:'];
48
+ for (const r of results) {
49
+ lines.push(`- [${r.kind}] ${r.title} (project: ${r.project}, status: ${r.status}, similarity: ${r.similarity.toFixed(2)})`);
50
+ if (r.body) {
51
+ lines.push(` ${r.body.slice(0, 200)}${r.body.length > 200 ? '…' : ''}`);
52
+ }
39
53
  }
54
+ process.stdout.write(lines.join('\n') + '\n');
40
55
  }
41
- process.stdout.write(lines.join('\n') + '\n');
42
56
  } catch (err) {
43
57
  // Non-blocking: exit cleanly on any error
44
58
  console.error(`[dude] auto-retrieve error: ${err.message}`);
package/hooks.json CHANGED
@@ -16,14 +16,9 @@
16
16
  "matcher": "Plan",
17
17
  "hooks": [
18
18
  {
19
- "type": "prompt",
20
- "prompt": "A plan has been created. Review it and output JSON to save it as a project spec: {\"action\":\"upsert\",\"kind\":\"spec\",\"title\":\"<plan title>\",\"body\":\"<full plan details including steps>\",\"status\":\"open\"}. Output ONLY the JSON, no other text.",
21
- "timeout": 30
22
- },
23
- {
24
- "type": "command",
25
- "command": "npx -y dude-claude-plugin auto-persist-plan",
26
- "timeout": 60
19
+ "type": "agent",
20
+ "prompt": "Event data: $ARGUMENTS\n\nA Plan subagent has finished. Read the plan transcript at the \"agent_transcript_path\" path from the event data. Read the last 200 lines to understand the plan.\n\nBLOCK the stop (ok: false) with reason:\n\"Please use the dude:upsert_record tool to save this plan as a specification: kind=spec, title=<plan title>, body=<plan details and steps>, status=open. After saving, briefly confirm what was persisted.\"\n\nIf you cannot read the transcript or extract a meaningful plan, ALLOW the stop (ok: true).",
21
+ "timeout": 120
27
22
  }
28
23
  ]
29
24
  }
@@ -32,14 +27,9 @@
32
27
  {
33
28
  "hooks": [
34
29
  {
35
- "type": "prompt",
36
- "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 (including completing a plan), output JSON: {\"action\":\"upsert\",\"kind\":\"spec\",\"title\":\"...\",\"body\":\"<summary of what was implemented>\",\"status\":\"resolved\"}. If a plan was created but not yet implemented, output {\"action\":\"none\"}. If neither applies, output {\"action\":\"none\"}. Output ONLY the JSON, no other text.",
37
- "timeout": 30
38
- },
39
- {
40
- "type": "command",
41
- "command": "npx -y dude-claude-plugin auto-persist",
42
- "timeout": 60
30
+ "type": "agent",
31
+ "prompt": "Event data: $ARGUMENTS\n\nCheck the \"stop_hook_active\" field. If it is true, return decision: ALLOW (ok: true). Do not read any files.\n\nOtherwise, read the transcript file at the \"transcript_path\" path from the event data. Read the last 200 lines to understand what work was done.\n\nClassify the work into exactly one category:\n- Bug fix → kind=issue, status=resolved\n- Architectural change (new patterns, restructuring, API design) → kind=arch, status=resolved\n- Feature update or improvement kind=update, status=resolved\n- New specification or plan (not yet implemented) kind=spec, status=open\n- Completing a planned spec → kind=spec, status=resolved\n- Trivial Q&A, chat, or unclassifiable no action needed\n\nIf you classified the work, BLOCK the stop (ok: false) with reason:\n\"Please use the dude:upsert_record tool to save this work record: kind=<kind>, title=<concise title>, body=<brief summary of what was done>, status=<status>. After saving, briefly confirm what was persisted.\"\n\nIf no classification is needed (trivial/unclassifiable), ALLOW the stop (ok: true).",
32
+ "timeout": 120
43
33
  }
44
34
  ]
45
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dude-claude-plugin",
3
- "version": "2026.2.12",
3
+ "version": "2026.2.14",
4
4
  "description": "Ultra-minimal RAG and cross-project memory for Claude CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,8 @@
12
12
  "scripts": {
13
13
  "start": "node bin/dude-claude.js mcp",
14
14
  "serve": "node bin/dude-claude.js serve",
15
+ "version": "node scripts/sync-plugin-version.cjs && git add .claude-plugin/plugin.json",
16
+ "test": "vitest run",
15
17
  "prepublishOnly": "node scripts/sync-plugin-version.cjs",
16
18
  "release": "npm version patch && git push && git push --tags && npm publish",
17
19
  "release:minor": "npm version minor && git push && git push --tags && npm publish",
@@ -40,18 +42,24 @@
40
42
  "bin/",
41
43
  "src/",
42
44
  "hooks/",
45
+ "scripts/",
43
46
  "web/",
44
47
  ".claude-plugin/",
45
48
  ".mcp.json",
46
49
  "hooks.json",
47
- "mcp-servers.json"
50
+ "mcp-servers.json",
51
+ "skills/"
48
52
  ],
49
53
  "dependencies": {
54
+ "@huggingface/transformers": "^3.4.1",
55
+ "@libsql/client": "^0.14.0",
50
56
  "@modelcontextprotocol/sdk": "^1.12.1",
51
- "zod": "^3.24.2",
52
57
  "better-sqlite3": "^11.8.1",
53
58
  "sqlite-vec": "^0.1.6",
54
- "@huggingface/transformers": "^3.4.1"
59
+ "zod": "^3.24.2"
55
60
  },
56
- "license": "MIT"
61
+ "license": "MIT",
62
+ "devDependencies": {
63
+ "vitest": "^4.0.18"
64
+ }
57
65
  }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Migration script: better-sqlite3 + sqlite-vec → libsql
3
+ *
4
+ * Reads all projects and records (with embeddings) from the old sqlite-vec
5
+ * database and writes them into a new libsql database with native F32_BLOB
6
+ * vector columns.
7
+ *
8
+ * Exported functions:
9
+ * migrate(oldDbPath, newDbUrl) — file-path based, opens/closes connections
10
+ * migrateFromDb(oldDb, newClient) — accepts pre-opened connections (for tests)
11
+ */
12
+
13
+ import Database from 'better-sqlite3';
14
+ import * as sqliteVec from 'sqlite-vec';
15
+ import { createClient } from '@libsql/client';
16
+
17
+ /**
18
+ * Create the libsql schema (mirrors LibsqlAdapter._runSchema).
19
+ */
20
+ async function createSchema(db) {
21
+ await db.batch([
22
+ {
23
+ sql: `CREATE TABLE IF NOT EXISTS project (
24
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
25
+ name TEXT NOT NULL UNIQUE,
26
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
27
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
28
+ )`,
29
+ },
30
+ {
31
+ sql: `CREATE TABLE IF NOT EXISTS record (
32
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
33
+ project_id INTEGER NOT NULL REFERENCES project(id) ON DELETE CASCADE,
34
+ kind TEXT NOT NULL CHECK (kind IN ('issue','spec','arch','update')),
35
+ title TEXT NOT NULL,
36
+ body TEXT NOT NULL DEFAULT '',
37
+ status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','resolved','archived')),
38
+ embedding F32_BLOB(384),
39
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
40
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
41
+ )`,
42
+ },
43
+ {
44
+ sql: `CREATE INDEX IF NOT EXISTS idx_record_project_kind
45
+ ON record(project_id, kind)`,
46
+ },
47
+ {
48
+ sql: `CREATE INDEX IF NOT EXISTS idx_record_embedding
49
+ ON record(libsql_vector_idx(embedding, 'metric=cosine'))`,
50
+ },
51
+ ], 'write');
52
+ }
53
+
54
+ /**
55
+ * Migrate data from pre-opened old DB and new libsql client.
56
+ * This is the core logic, testable with in-memory databases.
57
+ *
58
+ * @param {import('better-sqlite3').Database} oldDb — opened better-sqlite3 instance with sqlite-vec loaded
59
+ * @param {import('@libsql/client').Client} newDb — opened libsql client
60
+ * @returns {Promise<{ projects: number, records: number, embeddings: number }>}
61
+ */
62
+ export async function migrateFromDb(oldDb, newDb) {
63
+ await createSchema(newDb);
64
+
65
+ // 1. Migrate projects
66
+ const projects = oldDb.prepare('SELECT * FROM project').all();
67
+ for (const p of projects) {
68
+ await newDb.execute({
69
+ sql: 'INSERT INTO project (id, name, created_at, updated_at) VALUES (?, ?, ?, ?)',
70
+ args: [p.id, p.name, p.created_at, p.updated_at],
71
+ });
72
+ }
73
+
74
+ // 2. Migrate records with embeddings
75
+ const records = oldDb.prepare('SELECT * FROM record').all();
76
+ let embeddingCount = 0;
77
+
78
+ for (const r of records) {
79
+ // Look up the embedding from the vec0 virtual table
80
+ const vec = oldDb.prepare(
81
+ 'SELECT embedding FROM record_embedding WHERE record_id = ?',
82
+ ).get(r.id);
83
+
84
+ let embeddingJson = null;
85
+ if (vec && vec.embedding) {
86
+ const buf = vec.embedding;
87
+ const floats = new Float32Array(
88
+ buf.buffer, buf.byteOffset, buf.byteLength / 4,
89
+ );
90
+ embeddingJson = JSON.stringify(Array.from(floats));
91
+ embeddingCount++;
92
+ }
93
+
94
+ if (embeddingJson) {
95
+ await newDb.execute({
96
+ sql: `INSERT INTO record (id, project_id, kind, title, body, status, embedding, created_at, updated_at)
97
+ VALUES (?, ?, ?, ?, ?, ?, vector(?), ?, ?)`,
98
+ args: [r.id, r.project_id, r.kind, r.title, r.body, r.status, embeddingJson, r.created_at, r.updated_at],
99
+ });
100
+ } else {
101
+ await newDb.execute({
102
+ sql: `INSERT INTO record (id, project_id, kind, title, body, status, embedding, created_at, updated_at)
103
+ VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)`,
104
+ args: [r.id, r.project_id, r.kind, r.title, r.body, r.status, r.created_at, r.updated_at],
105
+ });
106
+ }
107
+ }
108
+
109
+ return { projects: projects.length, records: records.length, embeddings: embeddingCount };
110
+ }
111
+
112
+ /**
113
+ * File-path based migration — opens both databases, migrates, and closes.
114
+ *
115
+ * @param {string} oldDbPath — path to old better-sqlite3 + sqlite-vec DB
116
+ * @param {string} newDbUrl — libsql URL (e.g., 'file:/path/to/new.db')
117
+ * @returns {Promise<{ projects: number, records: number, embeddings: number }>}
118
+ */
119
+ export async function migrate(oldDbPath, newDbUrl) {
120
+ const oldDb = new Database(oldDbPath);
121
+ sqliteVec.load(oldDb);
122
+
123
+ const newDb = createClient({ url: newDbUrl });
124
+
125
+ try {
126
+ const stats = await migrateFromDb(oldDb, newDb);
127
+ return stats;
128
+ } finally {
129
+ oldDb.close();
130
+ newDb.close();
131
+ }
132
+ }
@@ -0,0 +1,5 @@
1
+ const fs = require('fs');
2
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
3
+ const plugin = JSON.parse(fs.readFileSync('.claude-plugin/plugin.json', 'utf8'));
4
+ plugin.version = pkg.version;
5
+ fs.writeFileSync('.claude-plugin/plugin.json', JSON.stringify(plugin, null, 2) + '\n');
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: issues
3
+ description: "Track and manage issues using the dude MCP server. List, create, update issues. Track bugs, tasks, blockers, and problems within projects. Search for issues. Use when tracking bugs, creating tasks, managing blockers, recording problems, or working with issue hierarchies."
4
+ ---
5
+
6
+ # Dude Issues - Issue Tracking
7
+
8
+ Track bugs, tasks, and blockers via the `dude:` MCP tools.
9
+
10
+ ## Quick Start
11
+
12
+ ```
13
+ dude:list_issues { "projectUuid": "..." } - List project issues
14
+ dude:create_issue { "project_uuid": "...", "text": "BUG: ..." }
15
+ dude:search { "entityTypes": ["issue"] } - Find issues
16
+ ```
17
+
18
+ ## Issue Operations
19
+
20
+ ### Listing Issues
21
+ | Tool | Description |
22
+ |------|-------------|
23
+ | `dude:list_issues` | List issues for a project |
24
+
25
+ **Parameters:**
26
+ - `projectUuid` (required): Project UUID
27
+ - `parentUuid` (optional): Filter to children of parent issue
28
+
29
+ ### Getting Issue Details
30
+ | Tool | Description |
31
+ |------|-------------|
32
+ | `dude:get_issue` | Get single issue details |
33
+
34
+ **Parameters:**
35
+ - `uuid` (required): Issue UUID
36
+
37
+ ### Creating Issues
38
+ | Tool | Description |
39
+ |------|-------------|
40
+ | `dude:create_issue` | Create new issue |
41
+
42
+ **Parameters:**
43
+ - `project_uuid` (required): Project UUID
44
+ - `text` (required): Issue description
45
+ - `parent_issue_uuid` (optional): Parent issue for nesting
46
+
47
+ **Examples:**
48
+ ```
49
+ dude:create_issue {
50
+ "project_uuid": "...",
51
+ "text": "BUG: Load cell readings drift after 2 hours"
52
+ }
53
+
54
+ dude:create_issue {
55
+ "project_uuid": "...",
56
+ "text": "TASK: Implement user authentication",
57
+ "parent_issue_uuid": "parent-task-uuid"
58
+ }
59
+ ```
60
+
61
+ ### Updating Issues
62
+ | Tool | Description |
63
+ |------|-------------|
64
+ | `dude:update_issue` | Update existing issue |
65
+
66
+ **Parameters:**
67
+ - `uuid` (required): Issue UUID
68
+ - `text` (optional): New description
69
+ - `parent_issue_uuid` (optional, nullable): New parent (null for top-level)
70
+ - `complete` (optional): Set completion status (1 = complete, 0 = incomplete)
71
+
72
+ ### Completing Issues
73
+ To mark an issue as complete:
74
+ ```
75
+ dude:update_issue { "uuid": "...", "complete": 1 }
76
+ ```
77
+
78
+ To reopen an issue:
79
+ ```
80
+ dude:update_issue { "uuid": "...", "complete": 0 }
81
+ ```
82
+
83
+ ## Search for Issues
84
+
85
+ ### Semantic Search
86
+ ```
87
+ dude:search {
88
+ "query": "memory leak in worker thread",
89
+ "entityTypes": ["issue"],
90
+ "projectUuid": "optional-project-uuid"
91
+ }
92
+ ```
93
+
94
+ **Parameters:**
95
+ - `query` (required): Natural language search query
96
+ - `limit` (optional): Max results (default: 10)
97
+ - `threshold` (optional): Min similarity 0-1 (default: 0.3)
98
+ - `entityTypes` (optional): Filter to `["issue"]`
99
+ - `projectUuid` (optional): Scope to specific project
100
+
101
+ ### Keyword Search
102
+ ```
103
+ dude:search_text { "query": "BUG" }
104
+ ```
105
+
106
+ **Parameters:**
107
+ - `query` (required): Text to search for
108
+
109
+ ## Issue Conventions
110
+
111
+ Use prefixes to categorize issues:
112
+ - `BUG:` - Defects and errors
113
+ - `TASK:` - Work items
114
+ - `BLOCKER:` - Critical blockers
115
+ - `QUESTION:` - Unknowns needing resolution
116
+
117
+ ## Related Skills
118
+
119
+ - **dude:projects**: Manage projects and get full project context
120
+ - **dude:specifications**: Document requirements and architecture
121
+
122
+ **Tip:** Use `dude:get_project_context` (from dude:projects) to see all issues for a project at once.
@@ -0,0 +1,115 @@
1
+ ---
2
+ name: projects
3
+ description: "Manage development projects using the dude MCP server. List, create, update projects. Get full project context with issues and specifications. Search for projects. Use when working with project organization, project hierarchies, starting work on a codebase, or needing project-level context."
4
+ ---
5
+
6
+ # Dude Projects - Project Management
7
+
8
+ Manage development projects via the `dude:` MCP tools.
9
+
10
+ ## Quick Start
11
+
12
+ ```
13
+ dude:list_projects - List all projects
14
+ dude:get_project_context - Full project with issues/specs
15
+ dude:search { "entityTypes": ["project"] } - Find projects
16
+ ```
17
+
18
+ ## Project Operations
19
+
20
+ ### Listing Projects
21
+ | Tool | Description |
22
+ |------|-------------|
23
+ | `dude:list_projects` | List all projects or filter by parent |
24
+
25
+ **Parameters:**
26
+ - `parentUuid` (optional): Filter to children of parent project
27
+
28
+ ### Getting Project Details
29
+ | Tool | Description |
30
+ |------|-------------|
31
+ | `dude:get_project` | Get single project details |
32
+ | `dude:get_project_context` | Get project with ALL issues and specs |
33
+
34
+ **get_project Parameters:**
35
+ - `uuid` (required): Project UUID
36
+
37
+ **get_project_context Parameters:**
38
+ - `uuid` (required): Project UUID
39
+ - `includeSubprojects` (optional): Include child projects (default: false)
40
+
41
+ ### Creating Projects
42
+ | Tool | Description |
43
+ |------|-------------|
44
+ | `dude:create_project` | Create new project |
45
+
46
+ **Parameters:**
47
+ - `name` (required): Project name
48
+ - `directory` (optional): Project directory path
49
+ - `parent_project_uuid` (optional): Parent project for nesting
50
+
51
+ ### Updating Projects
52
+ | Tool | Description |
53
+ |------|-------------|
54
+ | `dude:update_project` | Update existing project |
55
+
56
+ **Parameters:**
57
+ - `uuid` (required): Project UUID
58
+ - `name` (optional): New name
59
+ - `directory` (optional): New directory path
60
+ - `parent_project_uuid` (optional, nullable): New parent (null for top-level)
61
+ - `active` (optional): Set active status (1 = active, 0 = inactive)
62
+
63
+ ### Archiving Projects
64
+ To archive a project (soft delete), set the `active` flag to 0:
65
+ ```
66
+ dude:update_project { "uuid": "...", "active": 0 }
67
+ ```
68
+
69
+ To reactivate:
70
+ ```
71
+ dude:update_project { "uuid": "...", "active": 1 }
72
+ ```
73
+
74
+ ## Search for Projects
75
+
76
+ ### Semantic Search
77
+ ```
78
+ dude:search {
79
+ "query": "authentication service",
80
+ "entityTypes": ["project"],
81
+ "limit": 5
82
+ }
83
+ ```
84
+
85
+ **Parameters:**
86
+ - `query` (required): Natural language search query
87
+ - `limit` (optional): Max results (default: 10)
88
+ - `threshold` (optional): Min similarity 0-1 (default: 0.3)
89
+ - `entityTypes` (optional): Filter to `["project"]`
90
+ - `projectUuid` (optional): Scope to specific project
91
+
92
+ ### Keyword Search
93
+ ```
94
+ dude:search_text { "query": "auth" }
95
+ ```
96
+
97
+ **Parameters:**
98
+ - `query` (required): Text to search for
99
+
100
+ ## Common Workflows
101
+
102
+ ### Starting Work on a Codebase
103
+ 1. `dude:list_projects` - Find the project UUID
104
+ 2. `dude:get_project_context` - Load full context
105
+ 3. Begin coding with awareness of existing issues/specs
106
+
107
+ ### Organizing Projects
108
+ ```
109
+ dude:create_project { "name": "Frontend", "parent_project_uuid": "parent-uuid" }
110
+ ```
111
+
112
+ ## Related Skills
113
+
114
+ - **dude:issues**: Create and manage issues within projects
115
+ - **dude:specifications**: Create and manage specifications within projects
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: review-issues
3
+ description: "Interactive issue review and grooming session. Pulls all issues for the current project and walks through them with the user to triage, update, resolve, or archive. Use when grooming a backlog, reviewing open issues, or cleaning up stale tasks."
4
+ ---
5
+
6
+ # Dude Review Issues - Interactive Grooming
7
+
8
+ Walk through all issues for the current project with the user.
9
+
10
+ ## Workflow
11
+
12
+ When this skill is invoked, follow these steps **in order**:
13
+
14
+ ### Step 1: Fetch Issues
15
+
16
+ Call these in parallel:
17
+
18
+ ```
19
+ dude:list_records { "kind": "issue", "status": "open" }
20
+ dude:list_records { "kind": "issue", "status": "resolved" }
21
+ ```
22
+
23
+ ### Step 2: Present Summary
24
+
25
+ Show the user a summary of what exists:
26
+ - Total open issues
27
+ - Total resolved issues
28
+ - Group open issues by prefix: **BUG**, **TASK**, **BLOCKER**, **QUESTION**, **Other**
29
+ - List each open issue with its ID and title
30
+
31
+ ### Step 3: Walk Through Open Issues
32
+
33
+ For each open issue, present it and ask the user:
34
+
35
+ 1. **Still relevant?** — If not, mark as archived
36
+ 2. **Needs update?** — If yes, ask for the new description
37
+ 3. **Resolved?** — If yes, mark as resolved
38
+ 4. **Keep as-is?** — Move on
39
+
40
+ Apply changes immediately using:
41
+
42
+ ```
43
+ dude:upsert_record { "id": <issue_id>, "kind": "issue", "title": "<updated_title>", "status": "<new_status>" }
44
+ ```
45
+
46
+ ### Step 4: Review Resolved Issues (Optional)
47
+
48
+ Ask the user if they want to review recently resolved issues. If yes, walk through them and ask:
49
+ - Should this be archived (cleaned up)?
50
+ - Should this be reopened?
51
+
52
+ ### Step 5: New Issues
53
+
54
+ Ask the user if there are any new issues to capture. If yes, create them:
55
+
56
+ ```
57
+ dude:upsert_record { "kind": "issue", "title": "TASK: <description>", "body": "<details>" }
58
+ ```
59
+
60
+ ### Step 6: Summary
61
+
62
+ Present a final summary of all changes made during the session:
63
+ - Issues resolved
64
+ - Issues archived
65
+ - Issues updated
66
+ - New issues created
67
+
68
+ ## Tools Used
69
+
70
+ | Tool | Purpose |
71
+ |------|---------|
72
+ | `dude:list_records` | Fetch issues by kind and status |
73
+ | `dude:upsert_record` | Update or create issues |
74
+ | `dude:search` | Find related issues if needed |
75
+
76
+ ## Related Skills
77
+
78
+ - **dude:review-spec**: Review and update specifications
79
+ - **dude:issues**: CRUD reference for issue operations