Skip to content

Commit 7148acf

Browse files
author
Marvin Zhang
committed
feat: Introduce DevlogManagerV2 with enhanced operations and enterprise integrations
- Added DevlogManagerV2 class to manage devlog operations with a modular architecture. - Implemented FileSystemStorage for devlog data persistence. - Created DevlogOperations to handle CRUD operations and business logic. - Developed EnterpriseSync for integration with Jira, Azure DevOps, and GitHub. - Added utility functions in DevlogUtils for ID generation, filtering, and searching devlogs. - Updated index.ts to export new modules and types. - Enhanced types in @devlog/types for GitHub integration.
1 parent e4ada0d commit 7148acf

File tree

11 files changed

+1582
-11
lines changed

11 files changed

+1582
-11
lines changed

.github/copilot-instructions.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
This project uses **itself** for development tracking. When working on devlog features, ALWAYS:
66

77
### 1. Use Devlog for All Development Work
8-
- Create devlog entries for new features, bugs, or improvements
8+
- Create devlog entries for new features, bugs, or improvements using `@devlog/mcp-server`
99
- Use `find_or_create_devlog` to prevent duplicates
10-
- Track progress with notes and status updates
10+
- Track progress with notes and status updates through the MCP server tools
1111

1212
### 2. Standard Entry Format
1313
```json
@@ -21,11 +21,11 @@ This project uses **itself** for development tracking. When working on devlog fe
2121
```
2222

2323
### 3. Key Practices
24-
- **Always check existing entries** before creating new ones
25-
- **Update progress** as work continues
26-
- **Document decisions** and technical details in notes
27-
- **Use enterprise integrations** when configured
28-
- **Demonstrate new features** by using them
24+
- **Always check existing entries** before creating new ones using MCP server tools
25+
- **Update progress** as work continues through devlog MCP functions
26+
- **Document decisions** and technical details in notes using `add_devlog_note`
27+
- **Use enterprise integrations** when configured via MCP sync functions
28+
- **Demonstrate new features** by using them through the MCP interface
2929

3030
### 4. Duplicate Prevention
3131
- Use `find_or_create_devlog` instead of `create_devlog`
@@ -39,10 +39,10 @@ This project uses **itself** for development tracking. When working on devlog fe
3939
- ✅ MCP server for AI assistant integration
4040

4141
### 6. When Adding New Features
42-
1. Create devlog entry for the feature
42+
1. Create devlog entry for the feature using MCP server
4343
2. Use the feature to track its own development
44-
3. Update the entry as you implement
45-
4. Document the feature in the entry notes
46-
5. Demo the feature by using it
44+
3. Update the entry as you implement via MCP functions
45+
4. Document the feature in the entry notes using MCP tools
46+
5. Demo the feature by using it through the MCP interface
4747

4848
This ensures the devlog system is continuously tested and improved through real-world usage.

.vscode/mcp.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22
"servers": {
33
"github": {
44
"url": "https://api.githubcopilot.com/mcp/"
5+
},
6+
"devlog": {
7+
"type": "stdio",
8+
"command": "pnpm",
9+
"args": [
10+
"--filter",
11+
"@devlog/mcp-server",
12+
"dev"
13+
]
514
}
615
}
716
}

REFACTORING.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# DevlogManager Refactoring - Migration Guide
2+
3+
## Overview
4+
5+
The DevlogManager has been refactored from a monolithic class into a modular architecture to improve maintainability, testability, and code organization. This document explains the changes and how to migrate.
6+
7+
## New Architecture
8+
9+
The DevlogManager is now composed of several focused modules:
10+
11+
### Core Modules
12+
13+
1. **DevlogManager** (`devlog-manager-v2.ts`) - Main orchestrator class
14+
2. **FileSystemStorage** (`storage/file-system-storage.ts`) - File I/O operations
15+
3. **DevlogOperations** (`operations/devlog-operations.ts`) - Core business logic
16+
4. **EnterpriseSync** (`integrations/enterprise-sync.ts`) - External system integrations
17+
5. **DevlogUtils** (`utils/devlog-utils.ts`) - Utility functions
18+
19+
### Benefits
20+
21+
- **Single Responsibility**: Each module has a focused purpose
22+
- **Better Testing**: Modules can be tested in isolation
23+
- **Easier Maintenance**: Changes to one concern don't affect others
24+
- **Extensibility**: New storage backends or integrations can be added easily
25+
26+
## Migration Guide
27+
28+
### For Most Users
29+
30+
**No changes required!** The public API of DevlogManager remains the same. Your existing code will continue to work without modifications.
31+
32+
```typescript
33+
// This still works exactly the same
34+
import { DevlogManager } from '@devlog/core';
35+
36+
const manager = new DevlogManager({
37+
workspaceRoot: '/path/to/workspace'
38+
});
39+
```
40+
41+
### For Advanced Users
42+
43+
If you want to use the new modular architecture directly:
44+
45+
```typescript
46+
// New modular approach
47+
import {
48+
DevlogManager, // This is the new modular version
49+
FileSystemStorage,
50+
DevlogOperations,
51+
EnterpriseSync
52+
} from '@devlog/core';
53+
54+
// Option 1: Use the orchestrator (recommended)
55+
const manager = new DevlogManager({
56+
workspaceRoot: '/path/to/workspace',
57+
integrations: { /* your integrations */ }
58+
});
59+
60+
// Option 2: Compose modules manually (advanced)
61+
const storage = new FileSystemStorage('/path/to/.devlog');
62+
const enterpriseSync = new EnterpriseSync(integrations);
63+
const operations = new DevlogOperations(storage, enterpriseSync);
64+
```
65+
66+
### For Package Developers
67+
68+
If you're building on top of DevlogManager, you can now depend on individual modules:
69+
70+
```typescript
71+
import { DevlogStorage } from '@devlog/core';
72+
73+
// Implement your own storage backend
74+
class DatabaseStorage implements DevlogStorage {
75+
// Implement interface methods
76+
}
77+
78+
// Use with operations
79+
const operations = new DevlogOperations(new DatabaseStorage());
80+
```
81+
82+
## File Structure Changes
83+
84+
### Before
85+
```
86+
src/
87+
devlog-manager.ts (1000+ lines)
88+
index.ts
89+
```
90+
91+
### After
92+
```
93+
src/
94+
devlog-manager.ts (original - still works)
95+
devlog-manager-v2.ts (new modular orchestrator)
96+
storage/
97+
file-system-storage.ts (file I/O operations)
98+
operations/
99+
devlog-operations.ts (core business logic)
100+
integrations/
101+
enterprise-sync.ts (Jira, ADO, GitHub sync)
102+
utils/
103+
devlog-utils.ts (utility functions)
104+
index.ts (exports all modules)
105+
```
106+
107+
## Testing the New Architecture
108+
109+
A comprehensive test suite has been added to verify the modular architecture:
110+
111+
```bash
112+
cd packages/core
113+
pnpm test src/__tests__/devlog-manager-v2.test.ts
114+
```
115+
116+
## Backwards Compatibility
117+
118+
-**Public API**: No breaking changes
119+
-**File Format**: No changes to stored devlog files
120+
-**Integrations**: All enterprise integrations work the same
121+
-**MCP Server**: No changes required
122+
123+
## Future Plans
124+
125+
- The original monolithic `devlog-manager.ts` will be deprecated in v2.0
126+
- New features will be added to the modular architecture
127+
- Documentation will be updated to reflect the new structure
128+
129+
## Questions?
130+
131+
If you encounter any issues with the refactoring or need help migrating advanced use cases, please open an issue in the repository.
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2+
import { DevlogManager } from '../devlog-manager-v2';
3+
import { CreateDevlogRequest } from '@devlog/types';
4+
import * as path from 'path';
5+
import * as fs from 'fs/promises';
6+
import * as os from 'os';
7+
8+
describe('DevlogManager V2 (Modular Architecture)', () => {
9+
let manager: DevlogManager;
10+
let testDir: string;
11+
12+
beforeEach(async () => {
13+
// Create a temporary directory for testing
14+
testDir = await fs.mkdtemp(path.join(os.tmpdir(), 'devlog-test-'));
15+
manager = new DevlogManager({
16+
workspaceRoot: testDir
17+
});
18+
});
19+
20+
afterEach(async () => {
21+
// Clean up test directory
22+
await fs.rm(testDir, { recursive: true, force: true });
23+
});
24+
25+
it('should create and retrieve devlog entries', async () => {
26+
const request: CreateDevlogRequest = {
27+
title: 'Test Feature',
28+
type: 'feature',
29+
description: 'Testing the new modular architecture',
30+
priority: 'medium'
31+
};
32+
33+
// Create devlog
34+
const entry = await manager.createDevlog(request);
35+
expect(entry.title).toBe(request.title);
36+
expect(entry.type).toBe(request.type);
37+
expect(entry.description).toBe(request.description);
38+
expect(entry.priority).toBe(request.priority);
39+
expect(entry.status).toBe('todo');
40+
41+
// Retrieve devlog
42+
const retrieved = await manager.getDevlog(entry.id);
43+
expect(retrieved).toEqual(entry);
44+
});
45+
46+
it('should list and filter devlog entries', async () => {
47+
// Create multiple entries
48+
const requests: CreateDevlogRequest[] = [
49+
{ title: 'Feature 1', type: 'feature', description: 'First feature', priority: 'high' },
50+
{ title: 'Bug 1', type: 'bugfix', description: 'First bug', priority: 'critical' },
51+
{ title: 'Task 1', type: 'task', description: 'First task', priority: 'low' }
52+
];
53+
54+
for (const request of requests) {
55+
await manager.createDevlog(request);
56+
}
57+
58+
// List all entries
59+
const allEntries = await manager.listDevlogs();
60+
expect(allEntries).toHaveLength(3);
61+
62+
// Filter by type
63+
const features = await manager.listDevlogs({ type: ['feature'] });
64+
expect(features).toHaveLength(1);
65+
expect(features[0].type).toBe('feature');
66+
67+
// Filter by priority
68+
const highPriority = await manager.listDevlogs({ priority: ['high', 'critical'] });
69+
expect(highPriority).toHaveLength(2);
70+
});
71+
72+
it('should search devlog entries', async () => {
73+
const request: CreateDevlogRequest = {
74+
title: 'Searchable Feature',
75+
type: 'feature',
76+
description: 'This is a unique searchable description',
77+
priority: 'medium'
78+
};
79+
80+
await manager.createDevlog(request);
81+
82+
// Search by title
83+
const titleResults = await manager.searchDevlogs('Searchable');
84+
expect(titleResults).toHaveLength(1);
85+
86+
// Search by description
87+
const descResults = await manager.searchDevlogs('unique searchable');
88+
expect(descResults).toHaveLength(1);
89+
90+
// Search for non-existent term
91+
const noResults = await manager.searchDevlogs('nonexistent');
92+
expect(noResults).toHaveLength(0);
93+
});
94+
95+
it('should add notes to devlog entries', async () => {
96+
const request: CreateDevlogRequest = {
97+
title: 'Note Test Feature',
98+
type: 'feature',
99+
description: 'Testing note functionality',
100+
priority: 'medium'
101+
};
102+
103+
const entry = await manager.createDevlog(request);
104+
105+
// Add a note
106+
const updatedEntry = await manager.addNote(entry.id, {
107+
category: 'progress',
108+
content: 'This is a test note'
109+
});
110+
111+
expect(updatedEntry.notes).toHaveLength(1);
112+
expect(updatedEntry.notes[0].content).toBe('This is a test note');
113+
expect(updatedEntry.notes[0].category).toBe('progress');
114+
});
115+
116+
it('should update AI context', async () => {
117+
const request: CreateDevlogRequest = {
118+
title: 'AI Context Test',
119+
type: 'feature',
120+
description: 'Testing AI context updates',
121+
priority: 'medium'
122+
};
123+
124+
const entry = await manager.createDevlog(request);
125+
126+
// Update AI context
127+
const updatedEntry = await manager.updateAIContext({
128+
id: entry.id,
129+
summary: 'Updated AI summary',
130+
insights: ['New insight 1', 'New insight 2'],
131+
questions: ['Question 1?', 'Question 2?'],
132+
nextSteps: ['Step 1', 'Step 2']
133+
});
134+
135+
expect(updatedEntry.aiContext.currentSummary).toBe('Updated AI summary');
136+
expect(updatedEntry.aiContext.keyInsights).toContain('New insight 1');
137+
expect(updatedEntry.aiContext.keyInsights).toContain('New insight 2');
138+
expect(updatedEntry.aiContext.openQuestions).toEqual(['Question 1?', 'Question 2?']);
139+
expect(updatedEntry.aiContext.suggestedNextSteps).toEqual(['Step 1', 'Step 2']);
140+
});
141+
142+
it('should complete devlog entries', async () => {
143+
const request: CreateDevlogRequest = {
144+
title: 'Completion Test',
145+
type: 'task',
146+
description: 'Testing completion functionality',
147+
priority: 'medium'
148+
};
149+
150+
const entry = await manager.createDevlog(request);
151+
152+
// Complete the devlog
153+
const completedEntry = await manager.completeDevlog(entry.id, 'Task completed successfully');
154+
155+
expect(completedEntry.status).toBe('done');
156+
expect(completedEntry.notes).toHaveLength(1);
157+
expect(completedEntry.notes[0].content).toBe('Completed: Task completed successfully');
158+
});
159+
160+
it('should prevent duplicate entries with findOrCreateDevlog', async () => {
161+
const request: CreateDevlogRequest = {
162+
title: 'Duplicate Test',
163+
type: 'feature',
164+
description: 'Testing duplicate prevention',
165+
priority: 'medium'
166+
};
167+
168+
// Create first entry
169+
const result1 = await manager.findOrCreateDevlog(request);
170+
expect(result1.created).toBe(true);
171+
172+
// Try to create the same entry again
173+
const result2 = await manager.findOrCreateDevlog(request);
174+
expect(result2.created).toBe(false);
175+
expect(result2.entry.id).toBe(result1.entry.id);
176+
});
177+
178+
it('should get statistics', async () => {
179+
const requests: CreateDevlogRequest[] = [
180+
{ title: 'Feature 1', type: 'feature', description: 'Feature', priority: 'high' },
181+
{ title: 'Feature 2', type: 'feature', description: 'Feature', priority: 'medium' },
182+
{ title: 'Bug 1', type: 'bugfix', description: 'Bug', priority: 'critical' }
183+
];
184+
185+
for (const request of requests) {
186+
await manager.createDevlog(request);
187+
}
188+
189+
const stats = await manager.getStats();
190+
expect(stats.totalEntries).toBe(3);
191+
expect(stats.byType.feature).toBe(2);
192+
expect(stats.byType.bugfix).toBe(1);
193+
expect(stats.byPriority.high).toBe(1);
194+
expect(stats.byPriority.medium).toBe(1);
195+
expect(stats.byPriority.critical).toBe(1);
196+
});
197+
});

0 commit comments

Comments
 (0)