vibe-coding-mcp 2.6.0 → 2.12.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/README.md +122 -4
- package/dist/__tests__/exportSession.test.d.ts +2 -0
- package/dist/__tests__/exportSession.test.d.ts.map +1 -0
- package/dist/__tests__/exportSession.test.js +180 -0
- package/dist/__tests__/exportSession.test.js.map +1 -0
- package/dist/__tests__/git.test.d.ts +2 -0
- package/dist/__tests__/git.test.d.ts.map +1 -0
- package/dist/__tests__/git.test.js +282 -0
- package/dist/__tests__/git.test.js.map +1 -0
- package/dist/__tests__/projectProfile.test.d.ts +2 -0
- package/dist/__tests__/projectProfile.test.d.ts.map +1 -0
- package/dist/__tests__/projectProfile.test.js +216 -0
- package/dist/__tests__/projectProfile.test.js.map +1 -0
- package/dist/core/profileStorage.d.ts +117 -0
- package/dist/core/profileStorage.d.ts.map +1 -0
- package/dist/core/profileStorage.js +251 -0
- package/dist/core/profileStorage.js.map +1 -0
- package/dist/core/schemas.d.ts +576 -4
- package/dist/core/schemas.d.ts.map +1 -1
- package/dist/core/schemas.js +185 -0
- package/dist/core/schemas.js.map +1 -1
- package/dist/stdio.js +44 -2
- package/dist/stdio.js.map +1 -1
- package/dist/tools/autoTag.d.ts +145 -0
- package/dist/tools/autoTag.d.ts.map +1 -0
- package/dist/tools/autoTag.js +475 -0
- package/dist/tools/autoTag.js.map +1 -0
- package/dist/tools/batch.d.ts +119 -0
- package/dist/tools/batch.d.ts.map +1 -0
- package/dist/tools/batch.js +459 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/exportSession.d.ts +79 -0
- package/dist/tools/exportSession.d.ts.map +1 -0
- package/dist/tools/exportSession.js +434 -0
- package/dist/tools/exportSession.js.map +1 -0
- package/dist/tools/git.d.ts +226 -0
- package/dist/tools/git.d.ts.map +1 -0
- package/dist/tools/git.js +493 -0
- package/dist/tools/git.js.map +1 -0
- package/dist/tools/projectProfile.d.ts +230 -0
- package/dist/tools/projectProfile.d.ts.map +1 -0
- package/dist/tools/projectProfile.js +367 -0
- package/dist/tools/projectProfile.js.map +1 -0
- package/dist/tools/sessionStats.d.ts +129 -0
- package/dist/tools/sessionStats.d.ts.map +1 -0
- package/dist/tools/sessionStats.js +554 -0
- package/dist/tools/sessionStats.js.map +1 -0
- package/dist/tools/template.d.ts +127 -0
- package/dist/tools/template.d.ts.map +1 -0
- package/dist/tools/template.js +617 -0
- package/dist/tools/template.js.map +1 -0
- package/dist/utils/gitExecutor.d.ts +34 -0
- package/dist/utils/gitExecutor.d.ts.map +1 -0
- package/dist/utils/gitExecutor.js +95 -0
- package/dist/utils/gitExecutor.js.map +1 -0
- package/dist/utils/gitParsers.d.ts +90 -0
- package/dist/utils/gitParsers.d.ts.map +1 -0
- package/dist/utils/gitParsers.js +286 -0
- package/dist/utils/gitParsers.js.map +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ MCP server that automatically collects, summarizes, documents, and publishes cod
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
This MCP server provides
|
|
7
|
+
This MCP server provides 15 tools for managing vibe coding documentation:
|
|
8
8
|
|
|
9
9
|
| Tool | Description |
|
|
10
10
|
|------|-------------|
|
|
@@ -16,6 +16,13 @@ This MCP server provides 7 tools for managing vibe coding documentation:
|
|
|
16
16
|
| `muse_create_session_log` | Creates daily or session-based vibe coding session logs |
|
|
17
17
|
| `muse_analyze_code` | AST-based code analysis with Mermaid diagram generation |
|
|
18
18
|
| `muse_session_history` | Manages session history - save, retrieve, search past sessions |
|
|
19
|
+
| `muse_export_session` | Exports sessions to Markdown, JSON, or HTML formats |
|
|
20
|
+
| `muse_project_profile` | Manages project-specific settings and configurations |
|
|
21
|
+
| `muse_git` | Git integration - status, log, diff, branch, snapshot, design decision extraction |
|
|
22
|
+
| `muse_session_stats` | Session analytics dashboard with productivity insights and trends |
|
|
23
|
+
| `muse_auto_tag` | AI-powered auto-tagging for sessions based on content analysis |
|
|
24
|
+
| `muse_template` | Custom template management for documents and reports |
|
|
25
|
+
| `muse_batch` | Batch operations to execute multiple tools in sequence or parallel |
|
|
19
26
|
|
|
20
27
|
### Additional Features (v2.0)
|
|
21
28
|
- **AST Parsing**: TypeScript, Python, Go code analysis
|
|
@@ -41,7 +48,7 @@ This MCP server provides 7 tools for managing vibe coding documentation:
|
|
|
41
48
|
- **Configuration Validation**: Startup validation for all platform configurations
|
|
42
49
|
- **Platform Expansion**: Full support for 6 platforms (Notion, GitHub Wiki, Obsidian, Confluence, Slack, Discord)
|
|
43
50
|
- **AST Memoization**: Cached code analysis for improved performance
|
|
44
|
-
- **Test Coverage**:
|
|
51
|
+
- **Test Coverage**: 149 tests with 85%+ coverage on core modules
|
|
45
52
|
|
|
46
53
|
### AI-Powered Analysis (v2.4)
|
|
47
54
|
- **Claude AI Integration**: Use Claude AI for enhanced design decision analysis
|
|
@@ -55,6 +62,60 @@ This MCP server provides 7 tools for managing vibe coding documentation:
|
|
|
55
62
|
- **Improvement Suggestions**: AI-generated recommendations for better code
|
|
56
63
|
- **Works with AST**: Combines AI insights with AST-based analysis for comprehensive results
|
|
57
64
|
|
|
65
|
+
### Session History (v2.6)
|
|
66
|
+
- **Persistent Storage**: Save coding sessions to local JSON files
|
|
67
|
+
- **CRUD Operations**: Create, read, update, delete sessions
|
|
68
|
+
- **Search & Filter**: Find past sessions by keyword, tags, or date
|
|
69
|
+
- **Statistics**: Track total sessions, code contexts, and design decisions
|
|
70
|
+
|
|
71
|
+
### Session Export & Project Profiles (v2.7)
|
|
72
|
+
- **Session Export**: Export sessions to Markdown, JSON, or HTML formats
|
|
73
|
+
- **Multiple Templates**: Default, minimal, detailed, and report templates
|
|
74
|
+
- **Project Profiles**: Manage project-specific settings and configurations
|
|
75
|
+
- **Publishing Config**: Default platforms and settings per project
|
|
76
|
+
- **Code Analysis Config**: Language preferences and diagram types
|
|
77
|
+
- **Documentation Config**: Default document types, language, author info
|
|
78
|
+
- **Team Management**: Store team member information per project
|
|
79
|
+
|
|
80
|
+
### Git Integration (v2.8)
|
|
81
|
+
- **Repository Status**: View staged, unstaged, and untracked files
|
|
82
|
+
- **Commit History**: Browse commit log with filtering by author, date, grep
|
|
83
|
+
- **Diff Analysis**: View changes with statistics (staged, unstaged, between refs)
|
|
84
|
+
- **Branch Info**: List local and remote branches with tracking info
|
|
85
|
+
- **Git Snapshot**: Capture complete repository state for session context
|
|
86
|
+
- **Design Decision Extraction**: Auto-extract design decisions from commit messages
|
|
87
|
+
- **Session Linking**: Attach Git context to coding sessions
|
|
88
|
+
|
|
89
|
+
### Session Statistics Dashboard (v2.9)
|
|
90
|
+
- **Overview Analytics**: Total sessions, code contexts, design decisions at a glance
|
|
91
|
+
- **Language Distribution**: Breakdown of programming languages used across sessions
|
|
92
|
+
- **Timeline View**: Session activity over time (daily, weekly, monthly)
|
|
93
|
+
- **Tag Analytics**: Most used tags and tag co-occurrence analysis
|
|
94
|
+
- **Productivity Insights**: Session duration, code output, and efficiency metrics
|
|
95
|
+
- **Trend Analysis**: Compare current period with previous or average
|
|
96
|
+
|
|
97
|
+
### AI Auto-tagging (v2.10)
|
|
98
|
+
- **Smart Tag Suggestions**: Pattern-based and AI-powered tag recommendations
|
|
99
|
+
- **Confidence Scoring**: Each tag suggestion includes confidence level
|
|
100
|
+
- **Custom Tag Training**: Train the system with custom tag patterns
|
|
101
|
+
- **Configurable Rules**: Define tag rules for file extensions, keywords, patterns
|
|
102
|
+
- **Batch Tagging**: Apply tags to multiple sessions at once
|
|
103
|
+
|
|
104
|
+
### Custom Templates (v2.11)
|
|
105
|
+
- **Template Management**: Create, edit, delete custom document templates
|
|
106
|
+
- **Variable Substitution**: Support for `{{variable}}`, `${variable}`, `{variable}` formats
|
|
107
|
+
- **Built-in Templates**: README, Session Summary, Weekly Report templates included
|
|
108
|
+
- **Template Preview**: Preview rendered output before applying
|
|
109
|
+
- **Import/Export**: Share templates between projects
|
|
110
|
+
|
|
111
|
+
### Batch Operations (v2.12)
|
|
112
|
+
- **Sequential Execution**: Run multiple tools in order with dependency management
|
|
113
|
+
- **Parallel Execution**: Execute independent operations concurrently
|
|
114
|
+
- **Dependency Graph**: Topological sort for operation ordering
|
|
115
|
+
- **Job Tracking**: Monitor batch job status, cancel running jobs
|
|
116
|
+
- **Error Handling**: Stop on error or continue with remaining operations
|
|
117
|
+
- **Result Chaining**: Pass output from one operation to next using `$ref` syntax
|
|
118
|
+
|
|
58
119
|
## Installation
|
|
59
120
|
|
|
60
121
|
### Claude Code (Recommended)
|
|
@@ -140,6 +201,58 @@ User: Create a session log for today's work.
|
|
|
140
201
|
Claude: [Uses collect_code_context → create_session_log]
|
|
141
202
|
```
|
|
142
203
|
|
|
204
|
+
### 4. Git-Aware Session Documentation
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
User: Capture my current Git state and create a session with design decisions from commits.
|
|
208
|
+
|
|
209
|
+
Claude: [Uses muse_git(action='snapshot') → muse_git(action='extractDecisions') → muse_session_history(action='save')]
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 5. Complete Session Export with Git Context
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
User: Export all my sessions from this week with Git information.
|
|
216
|
+
|
|
217
|
+
Claude: [Uses muse_git(action='linkToSession') → muse_export_session(format='markdown')]
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 6. Session Analytics Dashboard
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
User: Show me my coding productivity statistics for this month.
|
|
224
|
+
|
|
225
|
+
Claude: [Uses muse_session_stats(action='overview') → muse_session_stats(action='productivity') → muse_session_stats(action='trends')]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 7. Auto-tag and Organize Sessions
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
User: Analyze my sessions and suggest relevant tags.
|
|
232
|
+
|
|
233
|
+
Claude: [Uses muse_auto_tag(action='suggest') → muse_auto_tag(action='apply')]
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 8. Generate Custom Report with Template
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
User: Create a weekly report using the team report template.
|
|
240
|
+
|
|
241
|
+
Claude: [Uses muse_template(action='apply', templateId='weekly-report') → muse_publish_document]
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### 9. Batch Documentation Workflow
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
User: Analyze this code, generate docs, and publish to Notion in one go.
|
|
248
|
+
|
|
249
|
+
Claude: [Uses muse_batch(action='execute', operations=[
|
|
250
|
+
{tool: 'muse_analyze_code', params: {...}},
|
|
251
|
+
{tool: 'muse_generate_dev_document', params: {...}, dependsOn: ['op_0']},
|
|
252
|
+
{tool: 'muse_publish_document', params: {...}, dependsOn: ['op_1']}
|
|
253
|
+
])]
|
|
254
|
+
```
|
|
255
|
+
|
|
143
256
|
## Supported Platforms
|
|
144
257
|
|
|
145
258
|
- **Notion**: Full API integration with page creation
|
|
@@ -162,10 +275,15 @@ src/
|
|
|
162
275
|
│ ├── security.ts # Path traversal, SSRF, timeout utilities
|
|
163
276
|
│ ├── logger.ts # Structured JSON logging
|
|
164
277
|
│ └── config.ts # Platform configuration validation
|
|
165
|
-
├── tools/ #
|
|
278
|
+
├── tools/ # 15 MCP tools
|
|
166
279
|
├── platforms/ # Notion, GitHub Wiki, Obsidian, Confluence, Slack, Discord
|
|
167
280
|
├── types/ # TypeScript interfaces
|
|
168
|
-
└── utils/
|
|
281
|
+
└── utils/
|
|
282
|
+
├── markdown.ts # Markdown processing
|
|
283
|
+
├── astParser.ts # AST parsing for TypeScript, Python, Go
|
|
284
|
+
├── diagramGenerator.ts # Mermaid diagram generation
|
|
285
|
+
├── gitExecutor.ts # Safe Git command execution
|
|
286
|
+
└── gitParsers.ts # Git output parsing utilities
|
|
169
287
|
```
|
|
170
288
|
|
|
171
289
|
## Development
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exportSession.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/exportSession.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { exportSessionTool } from '../tools/exportSession.js';
|
|
2
|
+
import { sessionHistoryTool } from '../tools/sessionHistory.js';
|
|
3
|
+
import * as sessionStorage from '../core/sessionStorage.js';
|
|
4
|
+
import { promises as fs } from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as os from 'os';
|
|
7
|
+
let testDir;
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
testDir = path.join(os.tmpdir(), `vibe-coding-export-test-${Date.now()}`);
|
|
10
|
+
await sessionStorage.initializeStorage(testDir);
|
|
11
|
+
// 테스트용 세션 생성
|
|
12
|
+
await sessionHistoryTool({
|
|
13
|
+
action: 'save',
|
|
14
|
+
title: 'Test Session 1',
|
|
15
|
+
summary: 'First test session for export',
|
|
16
|
+
tags: ['test', 'export'],
|
|
17
|
+
codeContexts: [
|
|
18
|
+
{
|
|
19
|
+
sessionId: 'ctx-1',
|
|
20
|
+
timestamp: new Date().toISOString(),
|
|
21
|
+
codeBlocks: [
|
|
22
|
+
{ language: 'typescript', code: 'const x = 1;' }
|
|
23
|
+
],
|
|
24
|
+
conversationSummary: 'Test code'
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
designDecisions: [
|
|
28
|
+
{
|
|
29
|
+
id: 'dd-1',
|
|
30
|
+
title: 'Test Decision',
|
|
31
|
+
description: 'A test decision',
|
|
32
|
+
rationale: 'For testing',
|
|
33
|
+
category: 'implementation',
|
|
34
|
+
timestamp: new Date().toISOString()
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
await sessionHistoryTool({
|
|
39
|
+
action: 'save',
|
|
40
|
+
title: 'Test Session 2',
|
|
41
|
+
summary: 'Second test session',
|
|
42
|
+
tags: ['test']
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
afterAll(async () => {
|
|
46
|
+
try {
|
|
47
|
+
const files = await fs.readdir(testDir);
|
|
48
|
+
for (const file of files) {
|
|
49
|
+
await fs.unlink(path.join(testDir, file));
|
|
50
|
+
}
|
|
51
|
+
await fs.rmdir(testDir);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Ignore cleanup errors
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
describe('exportSessionTool', () => {
|
|
58
|
+
describe('markdown export', () => {
|
|
59
|
+
it('should export all sessions as markdown', async () => {
|
|
60
|
+
const result = await exportSessionTool({
|
|
61
|
+
format: 'markdown'
|
|
62
|
+
});
|
|
63
|
+
expect(result.success).toBe(true);
|
|
64
|
+
expect(result.format).toBe('markdown');
|
|
65
|
+
expect(result.sessionCount).toBe(2);
|
|
66
|
+
expect(result.content).toContain('Test Session 1');
|
|
67
|
+
expect(result.content).toContain('Test Session 2');
|
|
68
|
+
});
|
|
69
|
+
it('should include code blocks in markdown', async () => {
|
|
70
|
+
const result = await exportSessionTool({
|
|
71
|
+
format: 'markdown',
|
|
72
|
+
includeCodeBlocks: true
|
|
73
|
+
});
|
|
74
|
+
expect(result.success).toBe(true);
|
|
75
|
+
expect(result.content).toContain('```typescript');
|
|
76
|
+
expect(result.content).toContain('const x = 1;');
|
|
77
|
+
});
|
|
78
|
+
it('should include design decisions', async () => {
|
|
79
|
+
const result = await exportSessionTool({
|
|
80
|
+
format: 'markdown',
|
|
81
|
+
includeDesignDecisions: true
|
|
82
|
+
});
|
|
83
|
+
expect(result.success).toBe(true);
|
|
84
|
+
expect(result.content).toContain('Test Decision');
|
|
85
|
+
expect(result.content).toContain('Design Decisions');
|
|
86
|
+
});
|
|
87
|
+
it('should use minimal template', async () => {
|
|
88
|
+
const result = await exportSessionTool({
|
|
89
|
+
format: 'markdown',
|
|
90
|
+
template: 'minimal'
|
|
91
|
+
});
|
|
92
|
+
expect(result.success).toBe(true);
|
|
93
|
+
expect(result.content).not.toContain('Table of Contents');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('json export', () => {
|
|
97
|
+
it('should export as valid JSON', async () => {
|
|
98
|
+
const result = await exportSessionTool({
|
|
99
|
+
format: 'json'
|
|
100
|
+
});
|
|
101
|
+
expect(result.success).toBe(true);
|
|
102
|
+
expect(result.format).toBe('json');
|
|
103
|
+
const parsed = JSON.parse(result.content);
|
|
104
|
+
expect(parsed.exportInfo).toBeDefined();
|
|
105
|
+
expect(parsed.sessions).toHaveLength(2);
|
|
106
|
+
});
|
|
107
|
+
it('should include metadata in JSON', async () => {
|
|
108
|
+
const result = await exportSessionTool({
|
|
109
|
+
format: 'json',
|
|
110
|
+
includeMetadata: true
|
|
111
|
+
});
|
|
112
|
+
const parsed = JSON.parse(result.content);
|
|
113
|
+
expect(parsed.sessions[0].createdAt).toBeDefined();
|
|
114
|
+
expect(parsed.sessions[0].tags).toBeDefined();
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('html export', () => {
|
|
118
|
+
it('should export as valid HTML', async () => {
|
|
119
|
+
const result = await exportSessionTool({
|
|
120
|
+
format: 'html'
|
|
121
|
+
});
|
|
122
|
+
expect(result.success).toBe(true);
|
|
123
|
+
expect(result.format).toBe('html');
|
|
124
|
+
expect(result.content).toContain('<!DOCTYPE html>');
|
|
125
|
+
expect(result.content).toContain('Test Session 1');
|
|
126
|
+
});
|
|
127
|
+
it('should include styles in HTML', async () => {
|
|
128
|
+
const result = await exportSessionTool({
|
|
129
|
+
format: 'html'
|
|
130
|
+
});
|
|
131
|
+
expect(result.content).toContain('<style>');
|
|
132
|
+
expect(result.content).toContain('font-family');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe('file output', () => {
|
|
136
|
+
it('should save to file when outputPath is provided', async () => {
|
|
137
|
+
const outputPath = path.join(testDir, 'export-test.md');
|
|
138
|
+
const result = await exportSessionTool({
|
|
139
|
+
format: 'markdown',
|
|
140
|
+
outputPath
|
|
141
|
+
});
|
|
142
|
+
expect(result.success).toBe(true);
|
|
143
|
+
expect(result.filePath).toBe(outputPath);
|
|
144
|
+
const fileContent = await fs.readFile(outputPath, 'utf-8');
|
|
145
|
+
expect(fileContent).toContain('Test Session');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe('custom options', () => {
|
|
149
|
+
it('should use custom title', async () => {
|
|
150
|
+
const result = await exportSessionTool({
|
|
151
|
+
format: 'markdown',
|
|
152
|
+
title: 'My Custom Export'
|
|
153
|
+
});
|
|
154
|
+
expect(result.content).toContain('# My Custom Export');
|
|
155
|
+
});
|
|
156
|
+
it('should exclude code blocks when disabled', async () => {
|
|
157
|
+
const result = await exportSessionTool({
|
|
158
|
+
format: 'markdown',
|
|
159
|
+
includeCodeBlocks: false
|
|
160
|
+
});
|
|
161
|
+
expect(result.content).not.toContain('```typescript');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
describe('error handling', () => {
|
|
165
|
+
it('should handle no sessions gracefully', async () => {
|
|
166
|
+
// 다른 저장소로 테스트
|
|
167
|
+
const emptyDir = path.join(os.tmpdir(), `empty-${Date.now()}`);
|
|
168
|
+
await sessionStorage.initializeStorage(emptyDir);
|
|
169
|
+
const result = await exportSessionTool({
|
|
170
|
+
format: 'markdown',
|
|
171
|
+
sessionIds: ['non-existent-id']
|
|
172
|
+
});
|
|
173
|
+
expect(result.success).toBe(false);
|
|
174
|
+
expect(result.error).toContain('No sessions found');
|
|
175
|
+
// 원래 저장소로 복원
|
|
176
|
+
await sessionStorage.initializeStorage(testDir);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=exportSession.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exportSession.test.js","sourceRoot":"","sources":["../../src/__tests__/exportSession.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/git.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { gitTool } from '../tools/git.js';
|
|
2
|
+
import { parseStatusPorcelainV2, parseLogOutput, parseDiffStat, } from '../utils/gitParsers.js';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
// Test with the actual vibe-coding-mcp repo
|
|
6
|
+
const TEST_REPO_PATH = path.resolve(process.cwd());
|
|
7
|
+
describe('gitTool', () => {
|
|
8
|
+
describe('status action', () => {
|
|
9
|
+
it('should return repository status', async () => {
|
|
10
|
+
const result = await gitTool({
|
|
11
|
+
action: 'status',
|
|
12
|
+
repoPath: TEST_REPO_PATH,
|
|
13
|
+
});
|
|
14
|
+
expect(result.success).toBe(true);
|
|
15
|
+
expect(result.action).toBe('status');
|
|
16
|
+
expect(result.status).toBeDefined();
|
|
17
|
+
expect(result.status?.branch).toBeDefined();
|
|
18
|
+
expect(typeof result.status?.isClean).toBe('boolean');
|
|
19
|
+
expect(Array.isArray(result.status?.staged)).toBe(true);
|
|
20
|
+
expect(Array.isArray(result.status?.unstaged)).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
it('should include untracked files by default', async () => {
|
|
23
|
+
const result = await gitTool({
|
|
24
|
+
action: 'status',
|
|
25
|
+
repoPath: TEST_REPO_PATH,
|
|
26
|
+
includeUntracked: true,
|
|
27
|
+
});
|
|
28
|
+
expect(result.success).toBe(true);
|
|
29
|
+
expect(Array.isArray(result.status?.untracked)).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('log action', () => {
|
|
33
|
+
it('should return commit history', async () => {
|
|
34
|
+
const result = await gitTool({
|
|
35
|
+
action: 'log',
|
|
36
|
+
repoPath: TEST_REPO_PATH,
|
|
37
|
+
limit: 5,
|
|
38
|
+
});
|
|
39
|
+
expect(result.success).toBe(true);
|
|
40
|
+
expect(result.action).toBe('log');
|
|
41
|
+
expect(result.commits).toBeDefined();
|
|
42
|
+
expect(Array.isArray(result.commits)).toBe(true);
|
|
43
|
+
expect(result.commits.length).toBeLessThanOrEqual(5);
|
|
44
|
+
if (result.commits.length > 0) {
|
|
45
|
+
const commit = result.commits[0];
|
|
46
|
+
expect(commit.hash).toBeDefined();
|
|
47
|
+
expect(commit.shortHash).toBeDefined();
|
|
48
|
+
expect(commit.author).toBeDefined();
|
|
49
|
+
expect(commit.date).toBeDefined();
|
|
50
|
+
expect(commit.message).toBeDefined();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
it('should support oneline format', async () => {
|
|
54
|
+
const result = await gitTool({
|
|
55
|
+
action: 'log',
|
|
56
|
+
repoPath: TEST_REPO_PATH,
|
|
57
|
+
limit: 3,
|
|
58
|
+
oneline: true,
|
|
59
|
+
});
|
|
60
|
+
expect(result.success).toBe(true);
|
|
61
|
+
expect(result.commits).toBeDefined();
|
|
62
|
+
});
|
|
63
|
+
it('should filter by grep', async () => {
|
|
64
|
+
const result = await gitTool({
|
|
65
|
+
action: 'log',
|
|
66
|
+
repoPath: TEST_REPO_PATH,
|
|
67
|
+
limit: 10,
|
|
68
|
+
grep: 'feat',
|
|
69
|
+
});
|
|
70
|
+
expect(result.success).toBe(true);
|
|
71
|
+
// All returned commits should contain 'feat' in message
|
|
72
|
+
if (result.commits && result.commits.length > 0) {
|
|
73
|
+
result.commits.forEach((commit) => {
|
|
74
|
+
expect(commit.message.toLowerCase()).toContain('feat');
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('diff action', () => {
|
|
80
|
+
it('should return diff information', async () => {
|
|
81
|
+
const result = await gitTool({
|
|
82
|
+
action: 'diff',
|
|
83
|
+
repoPath: TEST_REPO_PATH,
|
|
84
|
+
diffType: 'all',
|
|
85
|
+
stat: true,
|
|
86
|
+
});
|
|
87
|
+
expect(result.success).toBe(true);
|
|
88
|
+
expect(result.action).toBe('diff');
|
|
89
|
+
expect(result.diff).toBeDefined();
|
|
90
|
+
expect(typeof result.diff?.totalAdditions).toBe('number');
|
|
91
|
+
expect(typeof result.diff?.totalDeletions).toBe('number');
|
|
92
|
+
expect(Array.isArray(result.diff?.files)).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
it('should support staged diff', async () => {
|
|
95
|
+
const result = await gitTool({
|
|
96
|
+
action: 'diff',
|
|
97
|
+
repoPath: TEST_REPO_PATH,
|
|
98
|
+
diffType: 'staged',
|
|
99
|
+
});
|
|
100
|
+
expect(result.success).toBe(true);
|
|
101
|
+
expect(result.diff).toBeDefined();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('branch action', () => {
|
|
105
|
+
it('should return branch information', async () => {
|
|
106
|
+
const result = await gitTool({
|
|
107
|
+
action: 'branch',
|
|
108
|
+
repoPath: TEST_REPO_PATH,
|
|
109
|
+
includeRemote: true,
|
|
110
|
+
});
|
|
111
|
+
expect(result.success).toBe(true);
|
|
112
|
+
expect(result.action).toBe('branch');
|
|
113
|
+
expect(result.branch).toBeDefined();
|
|
114
|
+
expect(result.branch?.current).toBeDefined();
|
|
115
|
+
expect(Array.isArray(result.branch?.local)).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
it('should include remote branches', async () => {
|
|
118
|
+
const result = await gitTool({
|
|
119
|
+
action: 'branch',
|
|
120
|
+
repoPath: TEST_REPO_PATH,
|
|
121
|
+
includeRemote: true,
|
|
122
|
+
});
|
|
123
|
+
expect(result.success).toBe(true);
|
|
124
|
+
expect(Array.isArray(result.branch?.remote)).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe('snapshot action', () => {
|
|
128
|
+
it('should capture complete git snapshot', async () => {
|
|
129
|
+
const result = await gitTool({
|
|
130
|
+
action: 'snapshot',
|
|
131
|
+
repoPath: TEST_REPO_PATH,
|
|
132
|
+
includeDiff: true,
|
|
133
|
+
includeLog: true,
|
|
134
|
+
logLimit: 5,
|
|
135
|
+
});
|
|
136
|
+
expect(result.success).toBe(true);
|
|
137
|
+
expect(result.action).toBe('snapshot');
|
|
138
|
+
expect(result.snapshot).toBeDefined();
|
|
139
|
+
expect(result.snapshot?.timestamp).toBeDefined();
|
|
140
|
+
expect(result.snapshot?.repository).toBeDefined();
|
|
141
|
+
expect(result.snapshot?.status).toBeDefined();
|
|
142
|
+
expect(result.snapshot?.branch).toBeDefined();
|
|
143
|
+
expect(Array.isArray(result.snapshot?.recentCommits)).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
it('should include stash when requested', async () => {
|
|
146
|
+
const result = await gitTool({
|
|
147
|
+
action: 'snapshot',
|
|
148
|
+
repoPath: TEST_REPO_PATH,
|
|
149
|
+
includeStash: true,
|
|
150
|
+
});
|
|
151
|
+
expect(result.success).toBe(true);
|
|
152
|
+
// Stashes may or may not exist
|
|
153
|
+
if (result.snapshot?.stashes) {
|
|
154
|
+
expect(Array.isArray(result.snapshot.stashes)).toBe(true);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('extractDecisions action', () => {
|
|
159
|
+
it('should extract design decisions from commits', async () => {
|
|
160
|
+
const result = await gitTool({
|
|
161
|
+
action: 'extractDecisions',
|
|
162
|
+
repoPath: TEST_REPO_PATH,
|
|
163
|
+
limit: 50,
|
|
164
|
+
});
|
|
165
|
+
expect(result.success).toBe(true);
|
|
166
|
+
expect(result.action).toBe('extractDecisions');
|
|
167
|
+
expect(result.decisions).toBeDefined();
|
|
168
|
+
expect(Array.isArray(result.decisions)).toBe(true);
|
|
169
|
+
if (result.decisions && result.decisions.length > 0) {
|
|
170
|
+
const decision = result.decisions[0];
|
|
171
|
+
expect(decision.commitHash).toBeDefined();
|
|
172
|
+
expect(decision.title).toBeDefined();
|
|
173
|
+
expect(decision.category).toBeDefined();
|
|
174
|
+
expect(['high', 'medium', 'low']).toContain(decision.importance);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
it('should filter by since date', async () => {
|
|
178
|
+
const result = await gitTool({
|
|
179
|
+
action: 'extractDecisions',
|
|
180
|
+
repoPath: TEST_REPO_PATH,
|
|
181
|
+
since: '1 month ago',
|
|
182
|
+
limit: 20,
|
|
183
|
+
});
|
|
184
|
+
expect(result.success).toBe(true);
|
|
185
|
+
expect(result.decisions).toBeDefined();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
describe('error handling', () => {
|
|
189
|
+
it('should handle non-git directory gracefully', async () => {
|
|
190
|
+
const nonGitPath = os.tmpdir();
|
|
191
|
+
const result = await gitTool({
|
|
192
|
+
action: 'status',
|
|
193
|
+
repoPath: nonGitPath,
|
|
194
|
+
});
|
|
195
|
+
expect(result.success).toBe(false);
|
|
196
|
+
expect(result.error).toContain('Not a git repository');
|
|
197
|
+
});
|
|
198
|
+
it('should handle non-existent path', async () => {
|
|
199
|
+
const result = await gitTool({
|
|
200
|
+
action: 'status',
|
|
201
|
+
repoPath: '/non/existent/path',
|
|
202
|
+
});
|
|
203
|
+
expect(result.success).toBe(false);
|
|
204
|
+
expect(result.error).toBeDefined();
|
|
205
|
+
});
|
|
206
|
+
it('should require sessionId for linkToSession', async () => {
|
|
207
|
+
const result = await gitTool({
|
|
208
|
+
action: 'linkToSession',
|
|
209
|
+
repoPath: TEST_REPO_PATH,
|
|
210
|
+
});
|
|
211
|
+
expect(result.success).toBe(false);
|
|
212
|
+
expect(result.error).toContain('sessionId is required');
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
describe('gitParsers', () => {
|
|
217
|
+
describe('parseStatusPorcelainV2', () => {
|
|
218
|
+
it('should parse branch info', () => {
|
|
219
|
+
const output = `# branch.oid abc123
|
|
220
|
+
# branch.head main
|
|
221
|
+
# branch.upstream origin/main
|
|
222
|
+
# branch.ab +1 -0`;
|
|
223
|
+
const result = parseStatusPorcelainV2(output);
|
|
224
|
+
expect(result.branch).toBe('main');
|
|
225
|
+
expect(result.upstream).toBe('origin/main');
|
|
226
|
+
expect(result.ahead).toBe(1);
|
|
227
|
+
expect(result.behind).toBe(0);
|
|
228
|
+
});
|
|
229
|
+
it('should parse untracked files', () => {
|
|
230
|
+
const output = `# branch.head main
|
|
231
|
+
? new-file.ts
|
|
232
|
+
? another-file.js`;
|
|
233
|
+
const result = parseStatusPorcelainV2(output);
|
|
234
|
+
expect(result.untracked.length).toBe(2);
|
|
235
|
+
expect(result.untracked[0].path).toBe('new-file.ts');
|
|
236
|
+
expect(result.untracked[0].status).toBe('untracked');
|
|
237
|
+
});
|
|
238
|
+
it('should parse modified files', () => {
|
|
239
|
+
const output = `# branch.head main
|
|
240
|
+
1 .M N... 100644 100644 100644 abc123 def456 src/index.ts`;
|
|
241
|
+
const result = parseStatusPorcelainV2(output);
|
|
242
|
+
expect(result.unstaged.length).toBe(1);
|
|
243
|
+
expect(result.unstaged[0].status).toBe('modified');
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
describe('parseLogOutput', () => {
|
|
247
|
+
it('should parse log with body', () => {
|
|
248
|
+
const output = `abc123|abc1|John|john@example.com|2024-01-01T00:00:00Z|feat: Add feature
|
|
249
|
+
This is the body
|
|
250
|
+
END_COMMIT
|
|
251
|
+
def456|def4|Jane|jane@example.com|2024-01-02T00:00:00Z|fix: Fix bug
|
|
252
|
+
Another body
|
|
253
|
+
END_COMMIT`;
|
|
254
|
+
const result = parseLogOutput(output, true);
|
|
255
|
+
expect(result.length).toBe(2);
|
|
256
|
+
expect(result[0].hash).toBe('abc123');
|
|
257
|
+
expect(result[0].author).toBe('John');
|
|
258
|
+
expect(result[0].message).toBe('feat: Add feature');
|
|
259
|
+
expect(result[0].body).toBe('This is the body');
|
|
260
|
+
});
|
|
261
|
+
it('should parse oneline format', () => {
|
|
262
|
+
const output = `abc123|abc1|John|john@example.com|2024-01-01T00:00:00Z|feat: Add feature
|
|
263
|
+
def456|def4|Jane|jane@example.com|2024-01-02T00:00:00Z|fix: Fix bug`;
|
|
264
|
+
const result = parseLogOutput(output, false);
|
|
265
|
+
expect(result.length).toBe(2);
|
|
266
|
+
expect(result[0].hash).toBe('abc123');
|
|
267
|
+
expect(result[1].hash).toBe('def456');
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
describe('parseDiffStat', () => {
|
|
271
|
+
it('should parse diff stat output', () => {
|
|
272
|
+
const output = ` src/index.ts | 10 ++++------
|
|
273
|
+
src/utils.ts | 5 ++---
|
|
274
|
+
2 files changed, 6 insertions(+), 9 deletions(-)`;
|
|
275
|
+
const result = parseDiffStat(output);
|
|
276
|
+
expect(result.files.length).toBe(2);
|
|
277
|
+
expect(result.totalAdditions).toBe(6);
|
|
278
|
+
expect(result.totalDeletions).toBe(9);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
//# sourceMappingURL=git.test.js.map
|