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.
- package/.claude-plugin/plugin.json +2 -2
- package/bin/dude-claude.js +2 -2
- package/hooks/auto-persist-plan.js +5 -4
- package/hooks/auto-persist.js +5 -4
- package/hooks/auto-retrieve.js +28 -14
- package/hooks.json +6 -16
- package/package.json +13 -5
- package/scripts/migrate-to-libsql.js +132 -0
- package/scripts/sync-plugin-version.cjs +5 -0
- package/skills/issues/SKILL.md +122 -0
- package/skills/projects/SKILL.md +115 -0
- package/skills/review-issues/SKILL.md +79 -0
- package/skills/review-spec/SKILL.md +82 -0
- package/skills/specifications/SKILL.md +122 -0
- package/src/db-adapter.js +86 -0
- package/src/db-libsql.js +378 -0
- package/src/db-sqlite-vec.js +326 -0
- package/src/db.js +77 -284
- package/src/migrations/002-expand-kinds.js +26 -0
- package/src/server.js +17 -23
- package/src/web.js +14 -22
- package/web/index.html +8 -0
package/bin/dude-claude.js
CHANGED
|
@@ -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
|
|
36
|
-
auto-persist-plan Run the auto-persist-plan
|
|
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
|
|
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
|
|
41
|
+
const project = await db.getCurrentProject();
|
|
42
|
+
const record = await db.upsert(
|
|
42
43
|
{
|
|
43
|
-
projectId:
|
|
44
|
+
projectId: project.id,
|
|
44
45
|
kind,
|
|
45
46
|
title,
|
|
46
47
|
body,
|
package/hooks/auto-persist.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { embed } from '../src/embed.js';
|
|
10
|
-
import { initDb
|
|
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
|
|
41
|
+
const project = await db.getCurrentProject();
|
|
42
|
+
const record = await db.upsert(
|
|
42
43
|
{
|
|
43
|
-
projectId:
|
|
44
|
+
projectId: project.id,
|
|
44
45
|
kind,
|
|
45
46
|
title,
|
|
46
47
|
body,
|
package/hooks/auto-retrieve.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { embed } from '../src/embed.js';
|
|
10
|
-
import { initDb
|
|
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
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
//
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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": "
|
|
20
|
-
"prompt": "
|
|
21
|
-
"timeout":
|
|
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": "
|
|
36
|
-
"prompt": "
|
|
37
|
-
"timeout":
|
|
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.
|
|
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
|
-
"
|
|
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
|