Skip to content

Commit 294a3de

Browse files
author
Marvin Zhang
committed
chore: Add @vitest/coverage-v8 dependency and update pnpm-lock.yaml
1 parent 10254dc commit 294a3de

File tree

4 files changed

+136
-15
lines changed

4 files changed

+136
-15
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"license": "MIT",
4141
"devDependencies": {
4242
"@types/node": "^20.0.0",
43+
"@vitest/coverage-v8": "2.1.9",
4344
"concurrently": "9.2.0",
4445
"husky": "9.1.7",
4546
"lint-staged": "16.1.2",

packages/core/src/managers/devlog/workspace-devlog-manager.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -671,18 +671,25 @@ export class WorkspaceDevlogManager {
671671
throw new Error(`Devlog ${id} not found`);
672672
}
673673

674+
// Add closure note first if reason is provided
675+
if (reason) {
676+
await this.addNote(id, `Cancelled: ${reason}`, 'progress');
677+
}
678+
679+
// Get the updated entry (with note if added) and mark as cancelled
680+
const entryWithNote = await this.getDevlog(id);
681+
if (!entryWithNote) {
682+
throw new Error(`Devlog ${id} not found after adding note`);
683+
}
684+
674685
const now = new Date().toISOString();
675686
const updated: DevlogEntry = {
676-
...existing,
687+
...entryWithNote,
677688
status: 'cancelled',
678689
updatedAt: now,
679690
closedAt: now,
680691
};
681692

682-
if (reason) {
683-
await this.addNote(id, `Cancelled: ${reason}`, 'progress');
684-
}
685-
686693
const provider = await this.getCurrentStorageProvider();
687694
await provider.save(updated);
688695

packages/mcp/src/__tests__/mcp-adapter.test.ts

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,19 +303,131 @@ describe('MCPDevlogAdapter', () => {
303303
});
304304

305305
expect(completeResult).toBeDefined();
306-
expect(completeResult.content[0].text).toContain('Completed devlog');
307-
expect(completeResult.content[0].text).toContain('Task completed successfully');
306+
expect((completeResult.content[0] as any).text).toContain('Completed devlog');
307+
expect((completeResult.content[0] as any).text).toContain('Task completed successfully');
308+
309+
// Verify the completion note was added to the entry
310+
const completedEntry = await adapter.getDevlog({ id: testEntryId });
311+
expect(completedEntry).toBeDefined();
312+
const entryData = JSON.parse((completedEntry.content[0] as any).text);
313+
expect(entryData.status).toBe('done');
314+
expect(entryData.closedAt).toBeDefined();
315+
expect(entryData.notes).toBeDefined();
316+
expect(entryData.notes.length).toBeGreaterThan(0);
317+
318+
// Find the completion note
319+
const completionNote = entryData.notes.find((note: any) =>
320+
note.content.includes('Completed: Task completed successfully'),
321+
);
322+
expect(completionNote).toBeDefined();
323+
expect(completionNote.category).toBe('progress');
324+
});
325+
326+
it('should complete devlog entry without summary', async () => {
327+
// Create a new entry for this test
328+
const createResult = await adapter.createDevlog({
329+
title: 'Test Entry for Completion Without Summary',
330+
type: 'task',
331+
description: 'Test completion without summary',
332+
});
333+
334+
const entryIdMatch = (createResult.content[0] as any).text.match(
335+
/Created devlog entry: (\d+)/,
336+
);
337+
const noSummaryEntryId = parseInt(entryIdMatch![1], 10);
338+
339+
const completeResult = await adapter.completeDevlog({
340+
id: noSummaryEntryId,
341+
});
342+
343+
expect(completeResult).toBeDefined();
344+
expect((completeResult.content[0] as any).text).toContain('Completed devlog');
345+
expect((completeResult.content[0] as any).text).not.toContain('with summary');
346+
347+
// Verify the entry is completed but no completion note was added
348+
const completedEntry = await adapter.getDevlog({ id: noSummaryEntryId });
349+
expect(completedEntry).toBeDefined();
350+
const entryData = JSON.parse((completedEntry.content[0] as any).text);
351+
expect(entryData.status).toBe('done');
352+
expect(entryData.closedAt).toBeDefined();
353+
354+
// Should not have any completion notes (no summary provided)
355+
const completionNote = entryData.notes.find((note: any) =>
356+
note.content.includes('Completed:'),
357+
);
358+
expect(completionNote).toBeUndefined();
308359
});
309360

310361
it('should close devlog entry', async () => {
362+
// Create a new entry for this test since the previous tests modified testEntryId
363+
const createResult = await adapter.createDevlog({
364+
title: 'Test Entry for Closure',
365+
type: 'task',
366+
description: 'Test closure with reason',
367+
});
368+
369+
const entryIdMatch = (createResult.content[0] as any).text.match(
370+
/Created devlog entry: (\d+)/,
371+
);
372+
const closeEntryId = parseInt(entryIdMatch![1], 10);
373+
311374
const closeResult = await adapter.closeDevlog({
312-
id: testEntryId,
375+
id: closeEntryId,
313376
reason: 'No longer needed',
314377
});
315378

316379
expect(closeResult).toBeDefined();
317-
expect(closeResult.content[0].text).toContain('Closed devlog');
318-
expect(closeResult.content[0].text).toContain('No longer needed');
380+
expect((closeResult.content[0] as any).text).toContain('Closed devlog');
381+
expect((closeResult.content[0] as any).text).toContain('No longer needed');
382+
383+
// Verify the closure note was added to the entry
384+
const closedEntry = await adapter.getDevlog({ id: closeEntryId });
385+
expect(closedEntry).toBeDefined();
386+
const entryData = JSON.parse((closedEntry.content[0] as any).text);
387+
expect(entryData.status).toBe('cancelled');
388+
expect(entryData.closedAt).toBeDefined();
389+
expect(entryData.notes).toBeDefined();
390+
expect(entryData.notes.length).toBeGreaterThan(0);
391+
392+
// Find the closure note
393+
const closureNote = entryData.notes.find((note: any) =>
394+
note.content.includes('Cancelled: No longer needed'),
395+
);
396+
expect(closureNote).toBeDefined();
397+
expect(closureNote.category).toBe('progress');
398+
});
399+
400+
it('should close devlog entry without reason', async () => {
401+
// Create a new entry for this test
402+
const createResult = await adapter.createDevlog({
403+
title: 'Test Entry for Closure Without Reason',
404+
type: 'task',
405+
description: 'Test closure without reason',
406+
});
407+
408+
const entryIdMatch = (createResult.content[0] as any).text.match(
409+
/Created devlog entry: (\d+)/,
410+
);
411+
const noReasonEntryId = parseInt(entryIdMatch![1], 10);
412+
413+
const closeResult = await adapter.closeDevlog({
414+
id: noReasonEntryId,
415+
});
416+
417+
expect(closeResult).toBeDefined();
418+
expect((closeResult.content[0] as any).text).toContain('Closed devlog');
419+
expect((closeResult.content[0] as any).text).toContain('None provided');
420+
421+
// Verify the entry is closed but no closure note was added
422+
const closedEntry = await adapter.getDevlog({ id: noReasonEntryId });
423+
expect(closedEntry).toBeDefined();
424+
const entryData = JSON.parse((closedEntry.content[0] as any).text);
425+
expect(entryData.status).toBe('cancelled');
426+
expect(entryData.closedAt).toBeDefined();
427+
428+
// Should not have any closure notes (no reason provided)
429+
const closureNote = entryData.notes.find((note: any) => note.content.includes('Cancelled:'));
430+
expect(closureNote).toBeUndefined();
319431
});
320432

321433
it('should archive and unarchive devlog entry', async () => {

pnpm-lock.yaml

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)