@damper/mcp 0.3.7 → 0.3.9

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/README.md CHANGED
@@ -74,14 +74,15 @@ The AI will see tools from both servers and can distinguish between them:
74
74
  | Tool | Description |
75
75
  |------|-------------|
76
76
  | `list_tasks` | Get roadmap tasks (filter by `status`, `type`, `quarter`, sort by `importance`/`newest`/`votes`) |
77
- | `get_task` | Task details + subtasks + linked feedback |
77
+ | `get_task` | Task details + subtasks + commits + linked feedback |
78
78
  | `create_task` | Create task with type (bug, feature, improvement, task) |
79
79
  | `update_task` | Update description, plan, priority, effort, quarter, labels |
80
80
  | `start_task` | Lock and start task (use `force` to take over). Returns project context index. |
81
81
  | `add_note` | Add progress note |
82
+ | `add_commit` | Log a commit (hash + message). Appears in task details. |
82
83
  | `create_subtask` | Add subtask to a task |
83
84
  | `update_subtask` | Mark subtask done/undone |
84
- | `complete_task` | Mark done, release lock. Suggests context updates if needed. |
85
+ | `complete_task` | Mark done, release lock. Optionally log commits at completion. |
85
86
  | `abandon_task` | Release lock, return to planned |
86
87
  | `list_feedback` | Browse user feedback |
87
88
  | `get_feedback` | Feedback details + votes |
@@ -259,21 +260,42 @@ When you start a task, it's locked to prevent other agents from working on it si
259
260
  - Different agent starting → fails with 409 (use `force: true` to take over)
260
261
  - Complete or abandon → releases lock
261
262
 
263
+ ### Commit Tracking
264
+
265
+ Log commits as you work to track code changes for each task:
266
+
267
+ ```
268
+ > Log commit abc1234 with message "Added validation"
269
+ > Add commit def5678: "Fixed auth bug"
270
+ ```
271
+
272
+ Commits are stored structurally and displayed in task details:
273
+
274
+ ```
275
+ ## Commits (2)
276
+ - abc1234: Added validation
277
+ - def5678: Fixed auth bug
278
+ ```
279
+
280
+ **Two ways to log commits:**
281
+ 1. `add_commit` - Log commits as you make them
282
+ 2. `complete_task` with `commits` - Log final commits at completion
283
+
262
284
  ### Recommended Workflow
263
285
 
264
286
  When working on tasks, follow this workflow for best results:
265
287
 
266
288
  1. **Start**: `start_task` → locks the task and returns project context
267
289
  2. **Log start**: `add_note` with "Session started: <your goal>"
268
- 3. **Work**: Make changes, logging commits and decisions with `add_note`
290
+ 3. **Work**: Make changes, use `add_commit` after each commit, `add_note` for decisions
269
291
  4. **Log end**: `add_note` with "Session end: <summary, next steps>"
270
292
  5. **Finish**: `complete_task` (done) or `abandon_task` (stopping)
271
293
 
272
- **Note types to log:**
273
- - Session start: `"Session started: implementing dark mode"`
274
- - Commits: `"Committed abc123: Added theme provider"`
275
- - Decisions: `"Decision: Using CSS variables because..."`
276
- - Session end: `"Session end: Done theme provider, remaining: toggle UI"`
294
+ **What to log:**
295
+ - Session start: `add_note "Session started: implementing dark mode"`
296
+ - Commits: `add_commit abc123 "Added theme provider"`
297
+ - Decisions: `add_note "Decision: Using CSS variables because..."`
298
+ - Session end: `add_note "Session end: Done theme provider, remaining: toggle UI"`
277
299
 
278
300
  **Before completing:**
279
301
  - Push all commits
@@ -319,25 +341,29 @@ Add this to your project's `CLAUDE.md` (or `.cursorrules` for Cursor) to ensure
319
341
 
320
342
  This project uses Damper MCP for task tracking. **You MUST follow this workflow.**
321
343
 
322
- ### At Session Start
323
- 1. `get_project_context` - Read available project documentation
324
- 2. `list_tasks` - Check for existing tasks to work on
325
- 3. If working on a task: `start_task` to lock it
344
+ ### At Session Start (MANDATORY)
345
+ 1. `get_project_context` - **READ THIS FIRST.** Contains architecture, conventions, and critical project info. Do NOT skip.
346
+ 2. `get_context_section` - Fetch full content for sections relevant to your task (e.g., "api", "database", "testing")
347
+ 3. `list_tasks` - Check for existing tasks to work on
348
+ 4. If working on a task: `start_task` to lock it (returns context index - read relevant sections)
326
349
 
327
350
  ### While Working
328
- - `add_note` after each commit: "Committed abc123: description"
351
+ - `add_commit` after each commit with hash and message
329
352
  - `add_note` for decisions: "Decision: chose X because Y"
330
353
  - `update_subtask` to mark subtask progress
354
+ - **Follow patterns from project context** - Don't reinvent; use existing conventions
331
355
 
332
356
  ### At Session End (MANDATORY)
333
357
  - ALWAYS call `add_note` with session summary before stopping
334
358
  - ALWAYS call `complete_task` (if done) or `abandon_task` (if stopping early)
335
359
  - NEVER leave a started task without completing or abandoning it
360
+ - If you learned something about the codebase, consider updating project context
336
361
 
337
362
  ### Why This Matters
363
+ - **Project context prevents mistakes** - Contains architecture decisions, gotchas, and patterns you need to follow
338
364
  - Locked tasks block other agents from working on them
339
- - Notes help the next agent (or you) continue the work
340
- - Project context saves future agents from re-analyzing the codebase
365
+ - Commits and notes help the next agent (or you) continue the work
366
+ - Updating context saves future agents from re-analyzing the codebase
341
367
  ```
342
368
 
343
369
  ## Environment
@@ -8,29 +8,36 @@
8
8
  * Format start_task response
9
9
  */
10
10
  export function formatStartTaskResponse(result) {
11
- const lines = [`Started ${result.id}: ${result.message}`];
11
+ const lines = [`✅ Started ${result.id}: ${result.message}`];
12
+ // Context section - emphatic about reading it first
12
13
  if (result.context) {
13
14
  if (result.context.isEmpty) {
14
15
  lines.push(`\n📚 ${result.context.hint || 'No project context available.'}`);
15
16
  }
16
17
  else {
17
- lines.push('\n**Project context available:**');
18
+ lines.push('\n⚠️ **BEFORE YOU START:** Read project context to understand patterns and conventions.');
19
+ lines.push('');
20
+ if (result.context.relevantSections && result.context.relevantSections.length > 0) {
21
+ lines.push(`**Required reading for this task:**`);
22
+ for (const section of result.context.relevantSections) {
23
+ lines.push(`→ \`get_context_section("${section}")\``);
24
+ }
25
+ lines.push('');
26
+ }
27
+ lines.push('**All available sections:**');
18
28
  for (const s of result.context.index) {
19
29
  const relevant = result.context.relevantSections?.includes(s.section) ? ' ⭐' : '';
20
30
  lines.push(`• ${s.section}${relevant}`);
21
31
  }
22
- if (result.context.relevantSections && result.context.relevantSections.length > 0) {
23
- lines.push(`\nRelevant for this task: ${result.context.relevantSections.join(', ')}`);
24
- lines.push('Use get_context_section to fetch full content.');
25
- }
26
32
  }
27
33
  }
28
34
  lines.push('\n---');
29
- lines.push('**📋 Workflow:**');
30
- lines.push('1. `add_note`: "Session started: <goal>"');
31
- lines.push('2. Work + log commits/decisions with `add_note`');
32
- lines.push('3. `add_note`: "Session end: <summary, next steps>"');
33
- lines.push('4. `complete_task` or `abandon_task`');
35
+ lines.push('**📋 Required workflow:**');
36
+ lines.push('1. **Read context sections above** (contains patterns you must follow)');
37
+ lines.push('2. `add_note`: "Session started: <goal>"');
38
+ lines.push('3. Work: use `add_commit` for commits, `add_note` for decisions');
39
+ lines.push('4. `add_note`: "Session end: <summary>"');
40
+ lines.push('5. `complete_task` or `abandon_task` (NEVER skip this)');
34
41
  return lines.join('\n');
35
42
  }
36
43
  /**
@@ -38,11 +45,13 @@ export function formatStartTaskResponse(result) {
38
45
  */
39
46
  export function formatCompleteTaskResponse(result) {
40
47
  const lines = [`✅ Completed ${result.id}`];
48
+ lines.push('\n---');
49
+ lines.push('**📚 Before you finish:**');
50
+ lines.push('Did you learn something about this codebase that would help future agents?');
51
+ lines.push('→ Use `update_context_section` to share architecture, patterns, or gotchas.');
41
52
  if (result.documentation) {
42
- lines.push('\n---');
43
- lines.push('**📚 Documentation:**');
44
53
  if (result.documentation.affectedSections?.length > 0) {
45
- lines.push(`May need updates: ${result.documentation.affectedSections.join(', ')}`);
54
+ lines.push(`\nSections that may need updates: ${result.documentation.affectedSections.join(', ')}`);
46
55
  }
47
56
  if (result.documentation.reminder) {
48
57
  lines.push(result.documentation.reminder);
package/dist/index.js CHANGED
@@ -61,6 +61,10 @@ const SubtaskSchema = z.object({
61
61
  title: z.string(),
62
62
  done: z.boolean(),
63
63
  });
64
+ const CommitSchema = z.object({
65
+ hash: z.string(),
66
+ message: z.string(),
67
+ });
64
68
  const TaskDetailSchema = z.object({
65
69
  id: z.string(),
66
70
  title: z.string(),
@@ -76,6 +80,7 @@ const TaskDetailSchema = z.object({
76
80
  voteScore: z.number(),
77
81
  subtasks: z.array(SubtaskSchema).optional(),
78
82
  agentNotes: z.string().nullable().optional(),
83
+ commits: z.array(CommitSchema).optional(),
79
84
  feedback: z.array(z.object({
80
85
  id: z.string(),
81
86
  title: z.string(),
@@ -105,7 +110,8 @@ const FeedbackDetailSchema = z.object({
105
110
  server.registerTool('list_tasks', {
106
111
  title: 'List Tasks',
107
112
  description: 'Get roadmap tasks. Returns planned/in-progress by default. ' +
108
- 'Filter by type, quarter, etc.',
113
+ 'Filter by type, quarter, etc.\n\n' +
114
+ '**Recommended:** Call `get_project_context` before starting work to understand codebase patterns.',
109
115
  inputSchema: z.object({
110
116
  status: z.enum(['planned', 'in_progress', 'done', 'all']).optional(),
111
117
  type: z.enum(['bug', 'feature', 'improvement', 'task']).optional().describe('Filter by task type'),
@@ -151,15 +157,21 @@ server.registerTool('list_tasks', {
151
157
  const quarterInfo = t.quarter ? ` 📅${t.quarter}` : '';
152
158
  return `• ${t.id}: ${typeIcon} ${t.title} [${t.status}] ${p}${quarterInfo}${t.hasImplementationPlan ? ' 📋' : ''}${subtaskInfo}`;
153
159
  });
160
+ const output = [
161
+ `Tasks in "${data.project.name}":`,
162
+ lines.join('\n'),
163
+ '',
164
+ '💡 **Tip:** Run `get_project_context` first to understand codebase patterns before starting work.',
165
+ ].join('\n');
154
166
  return {
155
- content: [{ type: 'text', text: `Tasks in "${data.project.name}":\n${lines.join('\n')}` }],
167
+ content: [{ type: 'text', text: output }],
156
168
  structuredContent: { project: data.project.name, tasks: data.tasks },
157
169
  };
158
170
  });
159
171
  // Tool: Get task
160
172
  server.registerTool('get_task', {
161
173
  title: 'Get Task',
162
- description: 'Get task details including description, plan, and linked feedback.',
174
+ description: 'Get task details including description, plan, commits, and linked feedback.',
163
175
  inputSchema: z.object({
164
176
  taskId: z.string().describe('Task ID'),
165
177
  }),
@@ -202,12 +214,24 @@ server.registerTool('get_task', {
202
214
  parts.push(`\n## Subtasks (${done}/${t.subtasks.length})`);
203
215
  t.subtasks.forEach((s) => parts.push(`- [${s.done ? 'x' : ' '}] ${s.title} (id: ${s.id})`));
204
216
  }
217
+ // Show commits
218
+ if (t.commits && t.commits.length) {
219
+ parts.push(`\n## Commits (${t.commits.length})`);
220
+ t.commits.forEach((c) => parts.push(`- ${c.hash.slice(0, 7)}: ${c.message}`));
221
+ }
205
222
  if (t.agentNotes)
206
223
  parts.push(`\n## Notes\n${t.agentNotes}`);
207
224
  if (t.feedback.length) {
208
225
  parts.push(`\n## Feedback (${t.feedback.length})`);
209
226
  t.feedback.forEach((f) => parts.push(`- ${f.title} (${f.voterCount} votes)`));
210
227
  }
228
+ // Add workflow reminder for tasks not yet started
229
+ if (t.status === 'planned') {
230
+ parts.push('\n---');
231
+ parts.push('**To work on this task:**');
232
+ parts.push('1. `get_project_context` → read relevant sections');
233
+ parts.push('2. `start_task` → locks task and shows context');
234
+ }
211
235
  return {
212
236
  content: [{ type: 'text', text: parts.join('\n') }],
213
237
  structuredContent: t,
@@ -216,7 +240,7 @@ server.registerTool('get_task', {
216
240
  // Tool: Update task
217
241
  server.registerTool('update_task', {
218
242
  title: 'Update Task',
219
- description: 'Update task fields like description, implementation plan, priority, effort, quarter, or labels. ' +
243
+ description: 'Update task fields like description, implementation plan, priority, effort, quarter, labels, or visibility. ' +
220
244
  'Useful for refining task details as you learn more about the work.',
221
245
  inputSchema: z.object({
222
246
  taskId: z.string().describe('Task ID'),
@@ -226,6 +250,7 @@ server.registerTool('update_task', {
226
250
  effort: z.enum(['xs', 's', 'm', 'l', 'xl']).nullable().optional().describe('Estimated effort'),
227
251
  quarter: z.string().nullable().optional().describe('Target quarter (e.g., "Q1 2025", "Q2 2025")'),
228
252
  labels: z.array(z.string()).optional().describe('Labels/tags'),
253
+ isPublic: z.boolean().optional().describe('Whether task is visible on public roadmap'),
229
254
  }),
230
255
  outputSchema: z.object({
231
256
  id: z.string(),
@@ -236,6 +261,7 @@ server.registerTool('update_task', {
236
261
  effort: z.string().nullable().optional(),
237
262
  quarter: z.string().nullable().optional(),
238
263
  labels: z.array(z.string()).optional(),
264
+ isPublic: z.boolean().optional(),
239
265
  }),
240
266
  annotations: {
241
267
  readOnlyHint: false,
@@ -243,7 +269,7 @@ server.registerTool('update_task', {
243
269
  idempotentHint: true,
244
270
  openWorldHint: false,
245
271
  },
246
- }, async ({ taskId, description, implementationPlan, priority, effort, quarter, labels }) => {
272
+ }, async ({ taskId, description, implementationPlan, priority, effort, quarter, labels, isPublic }) => {
247
273
  const body = {};
248
274
  if (description !== undefined)
249
275
  body.description = description;
@@ -257,6 +283,8 @@ server.registerTool('update_task', {
257
283
  body.quarter = quarter;
258
284
  if (labels !== undefined)
259
285
  body.labels = labels;
286
+ if (isPublic !== undefined)
287
+ body.isPublic = isPublic;
260
288
  const result = await api('PATCH', `/api/agent/tasks/${taskId}`, body);
261
289
  const updates = [];
262
290
  if (description !== undefined)
@@ -271,6 +299,8 @@ server.registerTool('update_task', {
271
299
  updates.push(`quarter=${quarter || 'none'}`);
272
300
  if (labels !== undefined)
273
301
  updates.push(`labels=[${labels.join(', ')}]`);
302
+ if (isPublic !== undefined)
303
+ updates.push(`isPublic=${isPublic}`);
274
304
  return {
275
305
  content: [{ type: 'text', text: `📝 Updated ${result.title}: ${updates.join(', ')}` }],
276
306
  structuredContent: {
@@ -282,6 +312,7 @@ server.registerTool('update_task', {
282
312
  effort: result.effort,
283
313
  quarter: result.quarter,
284
314
  labels: result.labels,
315
+ isPublic: result.isPublic,
285
316
  },
286
317
  };
287
318
  });
@@ -295,12 +326,14 @@ server.registerTool('create_task', {
295
326
  type: z.enum(['bug', 'feature', 'improvement', 'task']).optional().describe('Task type (default: feature)'),
296
327
  status: z.enum(['planned', 'in_progress']).optional(),
297
328
  implementationPlan: z.string().optional(),
329
+ isPublic: z.boolean().optional().describe('Whether task is visible on public roadmap (default: true)'),
298
330
  }),
299
331
  outputSchema: z.object({
300
332
  id: z.string(),
301
333
  title: z.string(),
302
334
  type: z.string(),
303
335
  status: z.string(),
336
+ isPublic: z.boolean().optional(),
304
337
  }),
305
338
  annotations: {
306
339
  readOnlyHint: false,
@@ -311,8 +344,9 @@ server.registerTool('create_task', {
311
344
  }, async (args) => {
312
345
  const result = await api('POST', '/api/agent/tasks', args);
313
346
  const typeIcon = result.type === 'bug' ? '🐛' : result.type === 'feature' ? '✨' : result.type === 'improvement' ? '💡' : '📌';
347
+ const visibilityIcon = result.isPublic ? '' : ' 🔒';
314
348
  return {
315
- content: [{ type: 'text', text: `Created: ${result.id} ${typeIcon} "${result.title}" [${result.status}]` }],
349
+ content: [{ type: 'text', text: `Created: ${result.id} ${typeIcon} "${result.title}" [${result.status}]${visibilityIcon}` }],
316
350
  structuredContent: result,
317
351
  };
318
352
  });
@@ -333,11 +367,14 @@ server.registerTool('start_task', {
333
367
  description: 'Lock and start a task. Fails if locked by another agent unless force=true. ' +
334
368
  'Use force=true to take over a task from another agent (e.g., if they abandoned it). ' +
335
369
  'Returns project context index with relevant sections for the task.\n\n' +
370
+ '**IMPORTANT:** After starting, use get_context_section to read relevant sections. ' +
371
+ 'Project context contains architecture and patterns you MUST follow.\n\n' +
336
372
  '**Workflow after starting:**\n' +
337
- '1. add_note: "Session started: <your goal>"\n' +
338
- '2. Do work, log commits/decisions with add_note\n' +
339
- '3. add_note: "Session end: <summary, next steps>"\n' +
340
- '4. complete_task (done) or abandon_task (stopping)',
373
+ '1. Read relevant context sections (architecture, conventions, etc.)\n' +
374
+ '2. add_note: "Session started: <your goal>"\n' +
375
+ '3. Do work, log commits with add_commit, decisions with add_note\n' +
376
+ '4. add_note: "Session end: <summary, next steps>"\n' +
377
+ '5. complete_task (done) or abandon_task (stopping)',
341
378
  inputSchema: z.object({
342
379
  taskId: z.string(),
343
380
  force: z.boolean().optional().describe('Take over lock from another agent'),
@@ -395,9 +432,9 @@ server.registerTool('add_note', {
395
432
  description: 'Add progress note to task. Notes help future agents continue your work.\n\n' +
396
433
  '**When to use:**\n' +
397
434
  '- Session start: "Session started: implementing X"\n' +
398
- '- Commits: "Committed abc123: Added validation"\n' +
399
435
  '- Decisions: "Decision: Using X because Y"\n' +
400
436
  '- Session end: "Session end: Done X, remaining Y, blockers Z"\n\n' +
437
+ '**For commits:** Use `add_commit` instead for structured commit tracking.\n\n' +
401
438
  '**Important:** Always log session end before complete_task or abandon_task.',
402
439
  inputSchema: z.object({
403
440
  taskId: z.string(),
@@ -420,6 +457,37 @@ server.registerTool('add_note', {
420
457
  structuredContent: { taskId, success: true },
421
458
  };
422
459
  });
460
+ // Tool: Add commit
461
+ server.registerTool('add_commit', {
462
+ title: 'Add Commit',
463
+ description: 'Log a commit for a task. Stores structured commit info that appears in task details.\n\n' +
464
+ '**When to use:**\n' +
465
+ '- After making a commit: log hash and message\n' +
466
+ '- When completing work: optionally pass commits to complete_task instead\n\n' +
467
+ '**Example:** After `git commit`, call this with the commit hash and message.',
468
+ inputSchema: z.object({
469
+ taskId: z.string(),
470
+ hash: z.string().describe('Commit hash (short or full)'),
471
+ message: z.string().describe('Commit message'),
472
+ }),
473
+ outputSchema: z.object({
474
+ taskId: z.string(),
475
+ hash: z.string(),
476
+ success: z.boolean(),
477
+ }),
478
+ annotations: {
479
+ readOnlyHint: false,
480
+ destructiveHint: false,
481
+ idempotentHint: false,
482
+ openWorldHint: false,
483
+ },
484
+ }, async ({ taskId, hash, message }) => {
485
+ const result = await api('POST', `/api/agent/tasks/${taskId}/commits`, { hash, message });
486
+ return {
487
+ content: [{ type: 'text', text: `📝 Commit logged: ${hash.slice(0, 7)} - ${message}` }],
488
+ structuredContent: { taskId, hash: result.hash, success: true },
489
+ };
490
+ });
423
491
  // Tool: Create subtask
424
492
  server.registerTool('create_subtask', {
425
493
  title: 'Create Subtask',
@@ -500,15 +568,20 @@ const DocumentationSchema = z.object({
500
568
  // Tool: Complete task
501
569
  server.registerTool('complete_task', {
502
570
  title: 'Complete Task',
503
- description: 'Mark task done with summary. Include commit hashes and any follow-up work.\n\n' +
571
+ description: 'Mark task done with summary. Optionally log final commits at completion.\n\n' +
504
572
  '**Before calling:**\n' +
505
573
  '1. Push all commits\n' +
506
574
  '2. add_note: "Session end: <what was done>"\n' +
507
575
  '3. Check if project context docs need updating\n\n' +
576
+ '**Commits:** Pass commits array to log them at completion (convenience for final commits).\n\n' +
508
577
  'Returns documentation update suggestions.',
509
578
  inputSchema: z.object({
510
579
  taskId: z.string(),
511
580
  summary: z.string().describe('What was implemented'),
581
+ commits: z.array(z.object({
582
+ hash: z.string().describe('Commit hash (short or full)'),
583
+ message: z.string().describe('Commit message'),
584
+ })).optional().describe('Optional: commits to log at completion'),
512
585
  }),
513
586
  outputSchema: z.object({
514
587
  id: z.string(),
@@ -521,8 +594,8 @@ server.registerTool('complete_task', {
521
594
  idempotentHint: true,
522
595
  openWorldHint: false,
523
596
  },
524
- }, async ({ taskId, summary }) => {
525
- const result = await api('POST', `/api/agent/tasks/${taskId}/complete`, { summary });
597
+ }, async ({ taskId, summary, commits }) => {
598
+ const result = await api('POST', `/api/agent/tasks/${taskId}/complete`, { summary, commits });
526
599
  return {
527
600
  content: [{ type: 'text', text: formatCompleteTaskResponse(result) }],
528
601
  structuredContent: result,
@@ -776,7 +849,9 @@ server.registerTool('sync_project_context', {
776
849
  server.registerTool('get_project_context', {
777
850
  title: 'Get Project Context',
778
851
  description: 'Get project context index (token-efficient). Returns section list with previews. ' +
779
- 'Use get_context_section to fetch full content for sections you need.',
852
+ 'Use get_context_section to fetch full content for sections you need.\n\n' +
853
+ '**IMPORTANT:** Call this at session start. Contains architecture, conventions, and ' +
854
+ 'patterns you MUST follow. Skipping this leads to mistakes and inconsistent code.',
780
855
  inputSchema: z.object({
781
856
  taskId: z.string().optional().describe('If provided, highlights sections relevant to this task'),
782
857
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damper/mcp",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "MCP server for Damper task management",
5
5
  "author": "Damper <hello@usedamper.com>",
6
6
  "repository": {