@damper/mcp 0.8.7 β 0.10.0
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/dist/formatters.d.ts +12 -2
- package/dist/formatters.js +28 -35
- package/dist/index.js +116 -78
- package/package.json +1 -1
package/dist/formatters.d.ts
CHANGED
|
@@ -35,6 +35,11 @@ export interface CompleteTaskResult {
|
|
|
35
35
|
reminder: string;
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
+
export interface ReviewTaskResult {
|
|
39
|
+
id: string;
|
|
40
|
+
status: string;
|
|
41
|
+
completionChecklist?: string[];
|
|
42
|
+
}
|
|
38
43
|
export interface AbandonTaskResult {
|
|
39
44
|
id: string;
|
|
40
45
|
status: string;
|
|
@@ -49,11 +54,16 @@ export declare function formatStartTaskResponse(result: StartTaskResult): string
|
|
|
49
54
|
* Guides the agent to propose improvements to the user with rationale.
|
|
50
55
|
*/
|
|
51
56
|
export declare function formatSelfAssessmentActions(assessment: SelfAssessment): string;
|
|
57
|
+
/**
|
|
58
|
+
* Format review_task response.
|
|
59
|
+
* Shows self-assessment follow-up actions and reminds about next steps.
|
|
60
|
+
*/
|
|
61
|
+
export declare function formatReviewTaskResponse(result: ReviewTaskResult, assessment: SelfAssessment): string;
|
|
52
62
|
/**
|
|
53
63
|
* Format complete_task response
|
|
54
64
|
*/
|
|
55
|
-
export declare function formatCompleteTaskResponse(result: CompleteTaskResult
|
|
65
|
+
export declare function formatCompleteTaskResponse(result: CompleteTaskResult): string;
|
|
56
66
|
/**
|
|
57
67
|
* Format abandon_task response
|
|
58
68
|
*/
|
|
59
|
-
export declare function formatAbandonTaskResponse(result: AbandonTaskResult, hasSummary: boolean
|
|
69
|
+
export declare function formatAbandonTaskResponse(result: AbandonTaskResult, hasSummary: boolean): string;
|
package/dist/formatters.js
CHANGED
|
@@ -52,7 +52,8 @@ export function formatStartTaskResponse(result) {
|
|
|
52
52
|
lines.push('1. `add_note`: "Session started: <goal>"');
|
|
53
53
|
lines.push('2. Work: use `add_commit` for commits, `add_note` for decisions');
|
|
54
54
|
lines.push('3. `add_note`: "Session end: <summary>"');
|
|
55
|
-
lines.push('4. `
|
|
55
|
+
lines.push('4. `review_task` (with `selfAssessment`) β required self-review before completion');
|
|
56
|
+
lines.push('5. `complete_task` (with `confirmations`) or `abandon_task`');
|
|
56
57
|
return lines.join('\n');
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
@@ -91,31 +92,38 @@ export function formatSelfAssessmentActions(assessment) {
|
|
|
91
92
|
}
|
|
92
93
|
return lines.join('\n');
|
|
93
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Format review_task response.
|
|
97
|
+
* Shows self-assessment follow-up actions and reminds about next steps.
|
|
98
|
+
*/
|
|
99
|
+
export function formatReviewTaskResponse(result, assessment) {
|
|
100
|
+
const lines = [`π Review recorded for ${result.id}.`];
|
|
101
|
+
// Self-assessment follow-up actions
|
|
102
|
+
const actions = formatSelfAssessmentActions(assessment);
|
|
103
|
+
if (actions) {
|
|
104
|
+
lines.push(actions);
|
|
105
|
+
}
|
|
106
|
+
if (!actions) {
|
|
107
|
+
lines.push('\nNo friction points reported β nice!');
|
|
108
|
+
}
|
|
109
|
+
// Remind about completion checklist
|
|
110
|
+
if (result.completionChecklist && result.completionChecklist.length > 0) {
|
|
111
|
+
lines.push('\nπ **Completion checklist (pass as `confirmations` in `complete_task`):**');
|
|
112
|
+
for (const item of result.completionChecklist) {
|
|
113
|
+
lines.push(` β‘ ${item}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
lines.push('\n**Next:** Push commits, then call `complete_task` with summary and `reviewInstructions` (what to test/verify).');
|
|
117
|
+
return lines.join('\n');
|
|
118
|
+
}
|
|
94
119
|
/**
|
|
95
120
|
* Format complete_task response
|
|
96
121
|
*/
|
|
97
|
-
export function formatCompleteTaskResponse(result
|
|
122
|
+
export function formatCompleteTaskResponse(result) {
|
|
98
123
|
const statusMessage = result.status === 'done'
|
|
99
124
|
? `β
Task ${result.id} marked as done`
|
|
100
125
|
: `β
Task ${result.id} moved to review`;
|
|
101
126
|
const lines = [statusMessage];
|
|
102
|
-
// Self-assessment follow-up actions (shown first if provided)
|
|
103
|
-
if (assessment) {
|
|
104
|
-
const actions = formatSelfAssessmentActions(assessment);
|
|
105
|
-
if (actions) {
|
|
106
|
-
lines.push(actions);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (!assessment) {
|
|
110
|
-
// No assessment provided β nudge the agent to reflect
|
|
111
|
-
lines.push('\n---');
|
|
112
|
-
lines.push('**π Before you finish β quick self-assessment:**');
|
|
113
|
-
lines.push('Reflect on your session. If you encountered any of these, propose improvements to the user:');
|
|
114
|
-
lines.push('β’ Missing project docs β offer to call `update_context_section`');
|
|
115
|
-
lines.push('β’ Confusing tools β offer to call `report_issue`');
|
|
116
|
-
lines.push('β’ Spec/process gaps β offer to update docs or report');
|
|
117
|
-
lines.push('Explain briefly why each improvement would help future tasks.');
|
|
118
|
-
}
|
|
119
127
|
if (result.documentation) {
|
|
120
128
|
if (result.documentation.affectedSections?.length > 0) {
|
|
121
129
|
lines.push(`\nSections that may need updates: ${result.documentation.affectedSections.join(', ')}`);
|
|
@@ -129,26 +137,11 @@ export function formatCompleteTaskResponse(result, assessment) {
|
|
|
129
137
|
/**
|
|
130
138
|
* Format abandon_task response
|
|
131
139
|
*/
|
|
132
|
-
export function formatAbandonTaskResponse(result, hasSummary
|
|
140
|
+
export function formatAbandonTaskResponse(result, hasSummary) {
|
|
133
141
|
const lines = [`βΈοΈ Abandoned ${result.id}: ${result.message}`];
|
|
134
142
|
if (!hasSummary) {
|
|
135
143
|
lines.push('\nβ οΈ Tip: Include a summary next time for better handoff.');
|
|
136
144
|
}
|
|
137
145
|
lines.push('\nπ‘ Task returned to "planned" (from in_progress or in_review). Notes preserved for next agent.');
|
|
138
|
-
// Self-assessment follow-up actions
|
|
139
|
-
if (assessment) {
|
|
140
|
-
const actions = formatSelfAssessmentActions(assessment);
|
|
141
|
-
if (actions) {
|
|
142
|
-
lines.push(actions);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (!assessment) {
|
|
146
|
-
lines.push('\n---');
|
|
147
|
-
lines.push('**π Before you finish β quick self-assessment:**');
|
|
148
|
-
lines.push('Something caused this task to be abandoned. Propose improvements to the user:');
|
|
149
|
-
lines.push('β’ Missing project docs that would have helped β offer to call `update_context_section`');
|
|
150
|
-
lines.push('β’ Tool issues that blocked progress β offer to call `report_issue`');
|
|
151
|
-
lines.push('Explain briefly why each fix would help the next agent pick this up smoothly.');
|
|
152
|
-
}
|
|
153
146
|
return lines.join('\n');
|
|
154
147
|
}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { formatStartTaskResponse, formatCompleteTaskResponse, formatAbandonTaskResponse, } from './formatters.js';
|
|
5
|
+
import { formatStartTaskResponse, formatReviewTaskResponse, formatCompleteTaskResponse, formatAbandonTaskResponse, } from './formatters.js';
|
|
6
6
|
// Config
|
|
7
7
|
const API_KEY = process.env.DAMPER_API_KEY;
|
|
8
8
|
const API_URL = process.env.DAMPER_API_URL || 'https://api.usedamper.com';
|
|
@@ -28,6 +28,12 @@ async function api(method, path, body) {
|
|
|
28
28
|
lockErr.lockInfo = err;
|
|
29
29
|
throw lockErr;
|
|
30
30
|
}
|
|
31
|
+
// Preserve review-required info for 400 review gate
|
|
32
|
+
if (res.status === 400 && err.reviewRequired) {
|
|
33
|
+
const reviewErr = new Error(err.error || `HTTP ${res.status}`);
|
|
34
|
+
reviewErr.reviewRequired = true;
|
|
35
|
+
throw reviewErr;
|
|
36
|
+
}
|
|
31
37
|
// Preserve checklist info for 400 checklist failures
|
|
32
38
|
if (res.status === 400 && (err.missingItems || err.invalidEvidence)) {
|
|
33
39
|
const checklistErr = new Error(err.error || `HTTP ${res.status}`);
|
|
@@ -101,6 +107,7 @@ const FeedbackSummarySchema = z.object({
|
|
|
101
107
|
title: z.string(),
|
|
102
108
|
type: z.string(),
|
|
103
109
|
voterCount: z.number(),
|
|
110
|
+
isPublic: z.boolean(),
|
|
104
111
|
linkedTaskId: z.string().nullish(),
|
|
105
112
|
});
|
|
106
113
|
const FeedbackDetailSchema = z.object({
|
|
@@ -110,6 +117,7 @@ const FeedbackDetailSchema = z.object({
|
|
|
110
117
|
type: z.string(),
|
|
111
118
|
status: z.string(),
|
|
112
119
|
voteScore: z.number(),
|
|
120
|
+
isPublic: z.boolean(),
|
|
113
121
|
metadata: z.unknown().optional(),
|
|
114
122
|
linkedTaskId: z.string().nullish(),
|
|
115
123
|
voters: z.array(z.object({ email: z.string(), plan: z.string() })),
|
|
@@ -275,6 +283,8 @@ server.registerTool('update_task', {
|
|
|
275
283
|
description: z.string().optional().describe('Task description (shown on public roadmap). Write for end users: 1-3 sentences on what changes and why it matters. ' +
|
|
276
284
|
'No code references, file paths, or jargon.'),
|
|
277
285
|
implementationPlan: z.string().optional().describe('Implementation plan (markdown, internal only - never shown publicly)'),
|
|
286
|
+
reviewInstructions: z.string().optional().describe('Review instructions (markdown, internal only). What the reviewer should test, verify, or check. ' +
|
|
287
|
+
'Write specific, actionable steps β not a restatement of what was done.'),
|
|
278
288
|
priority: z.enum(['high', 'medium', 'low']).nullable().optional().describe('Task priority'),
|
|
279
289
|
effort: z.enum(['xs', 's', 'm', 'l', 'xl']).nullable().optional().describe('Estimated effort'),
|
|
280
290
|
quarter: z.string().nullable().optional().describe('Target quarter (e.g., "Q1 2025", "Q2 2025")'),
|
|
@@ -286,6 +296,7 @@ server.registerTool('update_task', {
|
|
|
286
296
|
title: z.string(),
|
|
287
297
|
description: z.string().nullable().optional(),
|
|
288
298
|
implementationPlan: z.string().nullable().optional(),
|
|
299
|
+
reviewInstructions: z.string().nullable().optional(),
|
|
289
300
|
priority: z.string().nullable().optional(),
|
|
290
301
|
effort: z.string().nullable().optional(),
|
|
291
302
|
quarter: z.string().nullable().optional(),
|
|
@@ -298,7 +309,7 @@ server.registerTool('update_task', {
|
|
|
298
309
|
idempotentHint: true,
|
|
299
310
|
openWorldHint: false,
|
|
300
311
|
},
|
|
301
|
-
}, async ({ taskId, title, description, implementationPlan, priority, effort, quarter, labels, isPublic }) => {
|
|
312
|
+
}, async ({ taskId, title, description, implementationPlan, reviewInstructions, priority, effort, quarter, labels, isPublic }) => {
|
|
302
313
|
const body = {};
|
|
303
314
|
if (title !== undefined)
|
|
304
315
|
body.title = title;
|
|
@@ -306,6 +317,8 @@ server.registerTool('update_task', {
|
|
|
306
317
|
body.description = description;
|
|
307
318
|
if (implementationPlan !== undefined)
|
|
308
319
|
body.implementationPlan = implementationPlan;
|
|
320
|
+
if (reviewInstructions !== undefined)
|
|
321
|
+
body.reviewInstructions = reviewInstructions;
|
|
309
322
|
if (priority !== undefined)
|
|
310
323
|
body.priority = priority;
|
|
311
324
|
if (effort !== undefined)
|
|
@@ -324,6 +337,8 @@ server.registerTool('update_task', {
|
|
|
324
337
|
updates.push('description');
|
|
325
338
|
if (implementationPlan !== undefined)
|
|
326
339
|
updates.push('plan');
|
|
340
|
+
if (reviewInstructions !== undefined)
|
|
341
|
+
updates.push('reviewInstructions');
|
|
327
342
|
if (priority !== undefined)
|
|
328
343
|
updates.push(`priority=${priority || 'none'}`);
|
|
329
344
|
if (effort !== undefined)
|
|
@@ -341,6 +356,7 @@ server.registerTool('update_task', {
|
|
|
341
356
|
title: result.title,
|
|
342
357
|
description: result.description,
|
|
343
358
|
implementationPlan: result.implementationPlan,
|
|
359
|
+
reviewInstructions: result.reviewInstructions,
|
|
344
360
|
priority: result.priority,
|
|
345
361
|
effort: result.effort,
|
|
346
362
|
quarter: result.quarter,
|
|
@@ -600,6 +616,38 @@ const SelfAssessmentSchema = z.object({
|
|
|
600
616
|
toolIssues: z.array(z.string()).optional().describe('MCP tools or workflow steps that were confusing, broken, or could be improved'),
|
|
601
617
|
processGaps: z.array(z.string()).optional().describe('Gaps in specs, checklists, or conventions that caused friction or mistakes'),
|
|
602
618
|
});
|
|
619
|
+
// Tool: Review task (self-assessment gate before completion)
|
|
620
|
+
server.registerTool('review_task', {
|
|
621
|
+
title: 'Review Task',
|
|
622
|
+
description: 'Self-review gate β MUST be called before `complete_task`.\n\n' +
|
|
623
|
+
'Reflect on your session: what project knowledge was missing, what tools were confusing, ' +
|
|
624
|
+
'what process gaps caused friction. This drives project improvements.\n\n' +
|
|
625
|
+
'**Flow:** `start_task` β work β `review_task` β `complete_task`\n\n' +
|
|
626
|
+
'If you have findings, propose improvements to the user after this call ' +
|
|
627
|
+
'(e.g., `update_context_section` for missing docs, `report_issue` for tool problems).',
|
|
628
|
+
inputSchema: z.object({
|
|
629
|
+
taskId: z.string(),
|
|
630
|
+
selfAssessment: SelfAssessmentSchema.describe('Reflect on your session: what went well, what knowledge was missing, what caused friction. ' +
|
|
631
|
+
'Be honest β this drives project improvements. Pass empty object if nothing to report.'),
|
|
632
|
+
}),
|
|
633
|
+
outputSchema: z.object({
|
|
634
|
+
id: z.string(),
|
|
635
|
+
status: z.string(),
|
|
636
|
+
completionChecklist: z.array(z.string()).optional(),
|
|
637
|
+
}),
|
|
638
|
+
annotations: {
|
|
639
|
+
readOnlyHint: false,
|
|
640
|
+
destructiveHint: false,
|
|
641
|
+
idempotentHint: true,
|
|
642
|
+
openWorldHint: false,
|
|
643
|
+
},
|
|
644
|
+
}, async ({ taskId, selfAssessment }) => {
|
|
645
|
+
const result = await api('POST', `/api/agent/tasks/${taskId}/review`, { selfAssessment });
|
|
646
|
+
return {
|
|
647
|
+
content: [{ type: 'text', text: formatReviewTaskResponse(result, selfAssessment) }],
|
|
648
|
+
structuredContent: result,
|
|
649
|
+
};
|
|
650
|
+
});
|
|
603
651
|
// Documentation reminder schema for complete_task response
|
|
604
652
|
const DocumentationSchema = z.object({
|
|
605
653
|
hasContext: z.boolean(),
|
|
@@ -611,22 +659,22 @@ server.registerTool('complete_task', {
|
|
|
611
659
|
title: 'Complete Task',
|
|
612
660
|
description: 'Mark task as ready for review (in_review). A human will verify and move it to done. ' +
|
|
613
661
|
'Pass `skipReview: true` to mark as done immediately (use when the human explicitly asks).\n\n' +
|
|
662
|
+
'**Requires `review_task` first** β completion will be rejected if you haven\'t called `review_task`.\n\n' +
|
|
614
663
|
'**Before calling:**\n' +
|
|
615
|
-
'1.
|
|
616
|
-
'2.
|
|
617
|
-
'
|
|
618
|
-
'
|
|
619
|
-
'**Completion checklist:** If the project has a completion checklist (shown in `start_task` response), ' +
|
|
664
|
+
'1. Call `review_task` (self-assessment)\n' +
|
|
665
|
+
'2. Push all commits\n' +
|
|
666
|
+
'3. Check if project context docs need updating\n\n' +
|
|
667
|
+
'**Completion checklist:** If the project has a completion checklist (shown in `start_task` and `review_task` responses), ' +
|
|
620
668
|
'you MUST pass `confirmations` β an array of `{item, evidence}` objects. Each confirmation needs the checklist item text ' +
|
|
621
669
|
'and concrete evidence proving you verified it (e.g., test output, build log snippet). ' +
|
|
622
670
|
'Empty evidence or evidence identical to the item text will be rejected.\n\n' +
|
|
623
671
|
'**Commits:** Pass commits array to log them at completion (convenience for final commits).\n\n' +
|
|
672
|
+
'**Review instructions:** When sending to review (default), provide `reviewInstructions` β specific steps ' +
|
|
673
|
+
'for the reviewer: what to test in UI, edge cases, areas of risk. Not a restatement of what was done.\n\n' +
|
|
624
674
|
'Returns documentation update suggestions.',
|
|
625
675
|
inputSchema: z.object({
|
|
626
676
|
taskId: z.string(),
|
|
627
677
|
summary: z.string().describe('Brief one-line summary of what was done, written for end users. Focus on the outcome, not the implementation.'),
|
|
628
|
-
selfAssessment: SelfAssessmentSchema.optional().describe('Reflect on your session: what went well, what knowledge was missing, what caused friction. ' +
|
|
629
|
-
'Be honest β this drives project improvements.'),
|
|
630
678
|
commits: z.array(z.object({
|
|
631
679
|
hash: z.string().describe('Commit hash (short or full)'),
|
|
632
680
|
message: z.string().describe('Commit message'),
|
|
@@ -636,6 +684,9 @@ server.registerTool('complete_task', {
|
|
|
636
684
|
evidence: z.string().describe('Concrete proof of verification (e.g., "bun test: 47 passed, 0 failed")'),
|
|
637
685
|
})).optional().describe('Completion checklist confirmations β echo back each item from the checklist shown in start_task'),
|
|
638
686
|
skipReview: z.boolean().optional().describe('Skip human review and mark task as done immediately (default: false, task goes to in_review)'),
|
|
687
|
+
reviewInstructions: z.string().optional().describe('What the reviewer should test or verify (markdown). Recommended when sending to review (default behavior). ' +
|
|
688
|
+
'Write specific, actionable steps: what to test in the UI, what edge cases to check, ' +
|
|
689
|
+
'what areas might break. Not a restatement of the summary.'),
|
|
639
690
|
}),
|
|
640
691
|
outputSchema: z.object({
|
|
641
692
|
id: z.string(),
|
|
@@ -648,43 +699,22 @@ server.registerTool('complete_task', {
|
|
|
648
699
|
idempotentHint: true,
|
|
649
700
|
openWorldHint: false,
|
|
650
701
|
},
|
|
651
|
-
}, async ({ taskId, summary,
|
|
702
|
+
}, async ({ taskId, summary, commits, confirmations, skipReview, reviewInstructions }) => {
|
|
652
703
|
try {
|
|
653
|
-
const result = await api('POST', `/api/agent/tasks/${taskId}/complete`, { summary, commits, confirmations, skipReview });
|
|
654
|
-
// Log self-assessment as a structured note if provided
|
|
655
|
-
if (selfAssessment) {
|
|
656
|
-
const assessmentLines = ['Self-Assessment:'];
|
|
657
|
-
if (selfAssessment.missingContext?.length) {
|
|
658
|
-
assessmentLines.push('Missing context:');
|
|
659
|
-
for (const item of selfAssessment.missingContext)
|
|
660
|
-
assessmentLines.push(` - ${item}`);
|
|
661
|
-
}
|
|
662
|
-
if (selfAssessment.toolIssues?.length) {
|
|
663
|
-
assessmentLines.push('Tool issues:');
|
|
664
|
-
for (const item of selfAssessment.toolIssues)
|
|
665
|
-
assessmentLines.push(` - ${item}`);
|
|
666
|
-
}
|
|
667
|
-
if (selfAssessment.processGaps?.length) {
|
|
668
|
-
assessmentLines.push('Process gaps:');
|
|
669
|
-
for (const item of selfAssessment.processGaps)
|
|
670
|
-
assessmentLines.push(` - ${item}`);
|
|
671
|
-
}
|
|
672
|
-
if (assessmentLines.length > 1) {
|
|
673
|
-
try {
|
|
674
|
-
await api('POST', `/api/agent/tasks/${taskId}/notes`, { note: assessmentLines.join('\n') });
|
|
675
|
-
}
|
|
676
|
-
catch {
|
|
677
|
-
// Don't fail completion if note logging fails
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
704
|
+
const result = await api('POST', `/api/agent/tasks/${taskId}/complete`, { summary, commits, confirmations, skipReview, reviewInstructions });
|
|
681
705
|
return {
|
|
682
|
-
content: [{ type: 'text', text: formatCompleteTaskResponse(result
|
|
706
|
+
content: [{ type: 'text', text: formatCompleteTaskResponse(result) }],
|
|
683
707
|
structuredContent: result,
|
|
684
708
|
};
|
|
685
709
|
}
|
|
686
710
|
catch (err) {
|
|
687
711
|
const error = err;
|
|
712
|
+
if (error.reviewRequired) {
|
|
713
|
+
return {
|
|
714
|
+
content: [{ type: 'text', text: 'β Completion blocked β self-review required.\n\nCall `review_task` before `complete_task`. This is a required step for reflecting on your session.' }],
|
|
715
|
+
isError: true,
|
|
716
|
+
};
|
|
717
|
+
}
|
|
688
718
|
if (error.checklistInfo) {
|
|
689
719
|
const { missingItems, invalidEvidence, checklist } = error.checklistInfo;
|
|
690
720
|
const lines = [
|
|
@@ -719,14 +749,10 @@ server.registerTool('abandon_task', {
|
|
|
719
749
|
description: 'Release lock and return task to planned status. Works for in_progress and in_review tasks.\n\n' +
|
|
720
750
|
'**Before calling:**\n' +
|
|
721
751
|
'1. Push any WIP commits\n\n' +
|
|
722
|
-
'**Summary parameter:** What was done, what remains, blockers. Helps the next agent
|
|
723
|
-
'**Self-assessment:** Especially important when abandoning β reflect on what blocked you ' +
|
|
724
|
-
'or what knowledge was missing so the next agent has a smoother experience.',
|
|
752
|
+
'**Summary parameter:** What was done, what remains, blockers. Helps the next agent.',
|
|
725
753
|
inputSchema: z.object({
|
|
726
754
|
taskId: z.string(),
|
|
727
755
|
summary: z.string().optional().describe('Handoff summary: what was done, what remains, any blockers'),
|
|
728
|
-
selfAssessment: SelfAssessmentSchema.optional().describe('Reflect on your session: what went well, what knowledge was missing, what caused friction. ' +
|
|
729
|
-
'Be honest β this drives project improvements.'),
|
|
730
756
|
}),
|
|
731
757
|
outputSchema: z.object({
|
|
732
758
|
id: z.string(),
|
|
@@ -739,37 +765,10 @@ server.registerTool('abandon_task', {
|
|
|
739
765
|
idempotentHint: true,
|
|
740
766
|
openWorldHint: false,
|
|
741
767
|
},
|
|
742
|
-
}, async ({ taskId, summary
|
|
768
|
+
}, async ({ taskId, summary }) => {
|
|
743
769
|
const result = await api('POST', `/api/agent/tasks/${taskId}/abandon`, summary ? { summary } : undefined);
|
|
744
|
-
// Log self-assessment as a structured note if provided
|
|
745
|
-
if (selfAssessment) {
|
|
746
|
-
const assessmentLines = ['Self-Assessment (abandoned):'];
|
|
747
|
-
if (selfAssessment.missingContext?.length) {
|
|
748
|
-
assessmentLines.push('Missing context:');
|
|
749
|
-
for (const item of selfAssessment.missingContext)
|
|
750
|
-
assessmentLines.push(` - ${item}`);
|
|
751
|
-
}
|
|
752
|
-
if (selfAssessment.toolIssues?.length) {
|
|
753
|
-
assessmentLines.push('Tool issues:');
|
|
754
|
-
for (const item of selfAssessment.toolIssues)
|
|
755
|
-
assessmentLines.push(` - ${item}`);
|
|
756
|
-
}
|
|
757
|
-
if (selfAssessment.processGaps?.length) {
|
|
758
|
-
assessmentLines.push('Process gaps:');
|
|
759
|
-
for (const item of selfAssessment.processGaps)
|
|
760
|
-
assessmentLines.push(` - ${item}`);
|
|
761
|
-
}
|
|
762
|
-
if (assessmentLines.length > 1) {
|
|
763
|
-
try {
|
|
764
|
-
await api('POST', `/api/agent/tasks/${taskId}/notes`, { note: assessmentLines.join('\n') });
|
|
765
|
-
}
|
|
766
|
-
catch {
|
|
767
|
-
// Don't fail abandonment if note logging fails
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
770
|
return {
|
|
772
|
-
content: [{ type: 'text', text: formatAbandonTaskResponse(result, !!summary
|
|
771
|
+
content: [{ type: 'text', text: formatAbandonTaskResponse(result, !!summary) }],
|
|
773
772
|
structuredContent: result,
|
|
774
773
|
};
|
|
775
774
|
});
|
|
@@ -862,7 +861,7 @@ This project uses Damper MCP for task tracking. **You MUST follow this workflow.
|
|
|
862
861
|
2. **Abandon and hand off** - Call \`abandon_task\` with a detailed handoff summary for the next agent.
|
|
863
862
|
- NEVER leave a started task without completing or abandoning it
|
|
864
863
|
- If the project has a **completion checklist** (shown in \`start_task\` response), you MUST pass all items as \`confirmations\` when calling \`complete_task\`
|
|
865
|
-
- **Self-
|
|
864
|
+
- **Self-review (required):** Call \`review_task\` before \`complete_task\`. Pass \`selfAssessment\` reflecting on:
|
|
866
865
|
- \`missingContext\` β What project knowledge was missing or wrong?
|
|
867
866
|
- \`toolIssues\` β Which tools or workflow steps were confusing or broken?
|
|
868
867
|
- \`processGaps\` β What specs or conventions were missing?
|
|
@@ -902,6 +901,12 @@ All content you generate β titles, descriptions, changelogs, notes β should
|
|
|
902
901
|
- Types: feat, fix, refactor, test, docs, chore, style, perf
|
|
903
902
|
- Imperative mood: "add feature" not "added feature"
|
|
904
903
|
|
|
904
|
+
**Review instructions** (provided at task completion via \`reviewInstructions\`):
|
|
905
|
+
- Write for a human reviewer who hasn't seen the code changes
|
|
906
|
+
- List specific things to test: UI flows, edge cases, regressions
|
|
907
|
+
- Mention files/areas that were changed and what to verify
|
|
908
|
+
- Include any setup steps needed to test (e.g., "run migrations first")
|
|
909
|
+
|
|
905
910
|
**Notes:**
|
|
906
911
|
- Prefix with category: "Decision: β¦", "Blocked: β¦", "Context: β¦"
|
|
907
912
|
- Only log non-obvious decisions and blockers β skip routine progress
|
|
@@ -1335,7 +1340,8 @@ server.registerTool('list_feedback', {
|
|
|
1335
1340
|
}
|
|
1336
1341
|
const lines = data.feedback.map((f) => {
|
|
1337
1342
|
const link = f.linkedTaskId ? ` β ${f.linkedTaskId}` : '';
|
|
1338
|
-
|
|
1343
|
+
const hidden = !f.isPublic ? ' [hidden]' : '';
|
|
1344
|
+
return `β’ ${f.id}: ${f.title} [${f.type}] (${f.voterCount} votes)${hidden}${link}`;
|
|
1339
1345
|
});
|
|
1340
1346
|
return {
|
|
1341
1347
|
content: [{ type: 'text', text: `Feedback:\n${lines.join('\n')}` }],
|
|
@@ -1412,12 +1418,14 @@ server.registerTool('get_feedback', {
|
|
|
1412
1418
|
// Tool: Update feedback
|
|
1413
1419
|
server.registerTool('update_feedback', {
|
|
1414
1420
|
title: 'Update Feedback',
|
|
1415
|
-
description: 'Update feedback status. Use to triage, close, or
|
|
1421
|
+
description: 'Update feedback status or visibility. Use to triage, close, mark as done, or hide from public page.',
|
|
1416
1422
|
inputSchema: z.object({
|
|
1417
1423
|
feedbackId: z.string().describe('Feedback ID'),
|
|
1418
1424
|
status: z
|
|
1419
1425
|
.enum(['new', 'under_review', 'planned', 'in_progress', 'done', 'closed'])
|
|
1426
|
+
.optional()
|
|
1420
1427
|
.describe('New status'),
|
|
1428
|
+
isPublic: z.boolean().optional().describe('Whether feedback is visible on public page'),
|
|
1421
1429
|
}),
|
|
1422
1430
|
outputSchema: z.object({
|
|
1423
1431
|
id: z.string(),
|
|
@@ -1431,8 +1439,13 @@ server.registerTool('update_feedback', {
|
|
|
1431
1439
|
idempotentHint: true,
|
|
1432
1440
|
openWorldHint: false,
|
|
1433
1441
|
},
|
|
1434
|
-
}, async ({ feedbackId, status }) => {
|
|
1435
|
-
const
|
|
1442
|
+
}, async ({ feedbackId, status, isPublic }) => {
|
|
1443
|
+
const body = {};
|
|
1444
|
+
if (status)
|
|
1445
|
+
body.status = status;
|
|
1446
|
+
if (isPublic !== undefined)
|
|
1447
|
+
body.isPublic = isPublic;
|
|
1448
|
+
const result = await api('PATCH', `/api/agent/feedback/${feedbackId}`, body);
|
|
1436
1449
|
return {
|
|
1437
1450
|
content: [
|
|
1438
1451
|
{
|
|
@@ -2064,6 +2077,31 @@ server.registerTool('publish_changelog', {
|
|
|
2064
2077
|
},
|
|
2065
2078
|
};
|
|
2066
2079
|
});
|
|
2080
|
+
// Tool: Delete changelog
|
|
2081
|
+
server.registerTool('delete_changelog', {
|
|
2082
|
+
title: 'Delete Changelog',
|
|
2083
|
+
description: 'Permanently delete a changelog entry.',
|
|
2084
|
+
inputSchema: z.object({
|
|
2085
|
+
changelogId: z.string().describe('Changelog ID'),
|
|
2086
|
+
}),
|
|
2087
|
+
outputSchema: z.object({
|
|
2088
|
+
success: z.boolean(),
|
|
2089
|
+
id: z.string(),
|
|
2090
|
+
title: z.string(),
|
|
2091
|
+
}),
|
|
2092
|
+
annotations: {
|
|
2093
|
+
readOnlyHint: false,
|
|
2094
|
+
destructiveHint: true,
|
|
2095
|
+
idempotentHint: true,
|
|
2096
|
+
openWorldHint: false,
|
|
2097
|
+
},
|
|
2098
|
+
}, async ({ changelogId }) => {
|
|
2099
|
+
const result = await api('DELETE', `/api/agent/changelogs/${changelogId}`);
|
|
2100
|
+
return {
|
|
2101
|
+
content: [{ type: 'text', text: `Deleted changelog: "${result.title}"` }],
|
|
2102
|
+
structuredContent: result,
|
|
2103
|
+
};
|
|
2104
|
+
});
|
|
2067
2105
|
// ==================== Project Settings Tools ====================
|
|
2068
2106
|
// Tool: Get project settings
|
|
2069
2107
|
server.registerTool('get_project_settings', {
|