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.
Files changed (60) hide show
  1. package/README.md +122 -4
  2. package/dist/__tests__/exportSession.test.d.ts +2 -0
  3. package/dist/__tests__/exportSession.test.d.ts.map +1 -0
  4. package/dist/__tests__/exportSession.test.js +180 -0
  5. package/dist/__tests__/exportSession.test.js.map +1 -0
  6. package/dist/__tests__/git.test.d.ts +2 -0
  7. package/dist/__tests__/git.test.d.ts.map +1 -0
  8. package/dist/__tests__/git.test.js +282 -0
  9. package/dist/__tests__/git.test.js.map +1 -0
  10. package/dist/__tests__/projectProfile.test.d.ts +2 -0
  11. package/dist/__tests__/projectProfile.test.d.ts.map +1 -0
  12. package/dist/__tests__/projectProfile.test.js +216 -0
  13. package/dist/__tests__/projectProfile.test.js.map +1 -0
  14. package/dist/core/profileStorage.d.ts +117 -0
  15. package/dist/core/profileStorage.d.ts.map +1 -0
  16. package/dist/core/profileStorage.js +251 -0
  17. package/dist/core/profileStorage.js.map +1 -0
  18. package/dist/core/schemas.d.ts +576 -4
  19. package/dist/core/schemas.d.ts.map +1 -1
  20. package/dist/core/schemas.js +185 -0
  21. package/dist/core/schemas.js.map +1 -1
  22. package/dist/stdio.js +44 -2
  23. package/dist/stdio.js.map +1 -1
  24. package/dist/tools/autoTag.d.ts +145 -0
  25. package/dist/tools/autoTag.d.ts.map +1 -0
  26. package/dist/tools/autoTag.js +475 -0
  27. package/dist/tools/autoTag.js.map +1 -0
  28. package/dist/tools/batch.d.ts +119 -0
  29. package/dist/tools/batch.d.ts.map +1 -0
  30. package/dist/tools/batch.js +459 -0
  31. package/dist/tools/batch.js.map +1 -0
  32. package/dist/tools/exportSession.d.ts +79 -0
  33. package/dist/tools/exportSession.d.ts.map +1 -0
  34. package/dist/tools/exportSession.js +434 -0
  35. package/dist/tools/exportSession.js.map +1 -0
  36. package/dist/tools/git.d.ts +226 -0
  37. package/dist/tools/git.d.ts.map +1 -0
  38. package/dist/tools/git.js +493 -0
  39. package/dist/tools/git.js.map +1 -0
  40. package/dist/tools/projectProfile.d.ts +230 -0
  41. package/dist/tools/projectProfile.d.ts.map +1 -0
  42. package/dist/tools/projectProfile.js +367 -0
  43. package/dist/tools/projectProfile.js.map +1 -0
  44. package/dist/tools/sessionStats.d.ts +129 -0
  45. package/dist/tools/sessionStats.d.ts.map +1 -0
  46. package/dist/tools/sessionStats.js +554 -0
  47. package/dist/tools/sessionStats.js.map +1 -0
  48. package/dist/tools/template.d.ts +127 -0
  49. package/dist/tools/template.d.ts.map +1 -0
  50. package/dist/tools/template.js +617 -0
  51. package/dist/tools/template.js.map +1 -0
  52. package/dist/utils/gitExecutor.d.ts +34 -0
  53. package/dist/utils/gitExecutor.d.ts.map +1 -0
  54. package/dist/utils/gitExecutor.js +95 -0
  55. package/dist/utils/gitExecutor.js.map +1 -0
  56. package/dist/utils/gitParsers.d.ts +90 -0
  57. package/dist/utils/gitParsers.d.ts.map +1 -0
  58. package/dist/utils/gitParsers.js +286 -0
  59. package/dist/utils/gitParsers.js.map +1 -0
  60. 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 tools for managing vibe coding documentation:
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**: 81 tests with 85%+ coverage on core modules
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/ # 7 MCP tools
278
+ ├── tools/ # 15 MCP tools
166
279
  ├── platforms/ # Notion, GitHub Wiki, Obsidian, Confluence, Slack, Discord
167
280
  ├── types/ # TypeScript interfaces
168
- └── utils/ # Markdown, AST, diagram utilities
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=exportSession.test.d.ts.map
@@ -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":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,cAAc,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,IAAI,OAAe,CAAC;AAEpB,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,2BAA2B,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC1E,MAAM,cAAc,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAEhD,aAAa;IACb,MAAM,kBAAkB,CAAC;QACvB,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,gBAAgB;QACvB,OAAO,EAAE,+BAA+B;QACxC,IAAI,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;QACxB,YAAY,EAAE;YACZ;gBACE,SAAS,EAAE,OAAO;gBAClB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,UAAU,EAAE;oBACV,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE;iBACjD;gBACD,mBAAmB,EAAE,WAAW;aACjC;SACF;QACD,eAAe,EAAE;YACf;gBACE,EAAE,EAAE,MAAM;gBACV,KAAK,EAAE,eAAe;gBACtB,WAAW,EAAE,iBAAiB;gBAC9B,SAAS,EAAE,aAAa;gBACxB,QAAQ,EAAE,gBAAgB;gBAC1B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC;SACF;KACF,CAAC,CAAC;IAEH,MAAM,kBAAkB,CAAC;QACvB,MAAM,EAAE,MAAM;QACd,KAAK,EAAE,gBAAgB;QACvB,OAAO,EAAE,qBAAqB;QAC9B,IAAI,EAAE,CAAC,MAAM,CAAC;KACf,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;IAClB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,UAAU;aACnB,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACvC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACpC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;YACnD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,UAAU;gBAClB,iBAAiB,EAAE,IAAI;aACxB,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YAClD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,UAAU;gBAClB,sBAAsB,EAAE,IAAI;aAC7B,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YAClD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,UAAU;gBAClB,QAAQ,EAAE,SAAS;aACpB,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAEnC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;YACxC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,MAAM;gBACd,eAAe,EAAE,IAAI;aACtB,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;YACnD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;YACpD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;YAC7C,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;YAExD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,UAAU;gBAClB,UAAU;aACX,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAEzC,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAC3D,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;YACvC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,UAAU;gBAClB,KAAK,EAAE,kBAAkB;aAC1B,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,UAAU;gBAClB,iBAAiB,EAAE,KAAK;aACzB,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;QAC9B,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,cAAc;YACd,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC/D,MAAM,cAAc,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAEjD,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;gBACrC,MAAM,EAAE,UAAU;gBAClB,UAAU,EAAE,CAAC,iBAAiB,CAAC;aAChC,CAAC,CAAC;YAEH,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;YAEpD,aAAa;YACb,MAAM,cAAc,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=git.test.d.ts.map
@@ -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