@su-record/vibe 0.1.2 → 0.1.4
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 +13 -6
- package/bin/vibe +20 -2
- package/package.json +5 -6
- package/scripts/install-mcp.js +31 -5
- package/mcp/dist/__tests__/complexity.test.js +0 -126
- package/mcp/dist/__tests__/memory.test.js +0 -120
- package/mcp/dist/__tests__/python-dart-complexity.test.js +0 -146
- package/mcp/dist/index.js +0 -230
- package/mcp/dist/lib/ContextCompressor.js +0 -305
- package/mcp/dist/lib/MemoryManager.js +0 -334
- package/mcp/dist/lib/ProjectCache.js +0 -126
- package/mcp/dist/lib/PythonParser.js +0 -241
- package/mcp/dist/tools/browser/browserPool.js +0 -76
- package/mcp/dist/tools/browser/browserUtils.js +0 -135
- package/mcp/dist/tools/browser/inspectNetworkRequests.js +0 -140
- package/mcp/dist/tools/browser/monitorConsoleLogs.js +0 -97
- package/mcp/dist/tools/convention/analyzeComplexity.js +0 -248
- package/mcp/dist/tools/convention/applyQualityRules.js +0 -102
- package/mcp/dist/tools/convention/checkCouplingCohesion.js +0 -233
- package/mcp/dist/tools/convention/complexityMetrics.js +0 -133
- package/mcp/dist/tools/convention/dartComplexity.js +0 -117
- package/mcp/dist/tools/convention/getCodingGuide.js +0 -64
- package/mcp/dist/tools/convention/languageDetector.js +0 -50
- package/mcp/dist/tools/convention/pythonComplexity.js +0 -109
- package/mcp/dist/tools/convention/suggestImprovements.js +0 -257
- package/mcp/dist/tools/convention/validateCodeQuality.js +0 -177
- package/mcp/dist/tools/memory/autoSaveContext.js +0 -79
- package/mcp/dist/tools/memory/database.js +0 -123
- package/mcp/dist/tools/memory/deleteMemory.js +0 -39
- package/mcp/dist/tools/memory/listMemories.js +0 -38
- package/mcp/dist/tools/memory/memoryConfig.js +0 -27
- package/mcp/dist/tools/memory/memorySQLite.js +0 -138
- package/mcp/dist/tools/memory/memoryUtils.js +0 -34
- package/mcp/dist/tools/memory/migrate.js +0 -113
- package/mcp/dist/tools/memory/prioritizeMemory.js +0 -109
- package/mcp/dist/tools/memory/recallMemory.js +0 -40
- package/mcp/dist/tools/memory/restoreSessionContext.js +0 -69
- package/mcp/dist/tools/memory/saveMemory.js +0 -34
- package/mcp/dist/tools/memory/searchMemories.js +0 -37
- package/mcp/dist/tools/memory/startSession.js +0 -100
- package/mcp/dist/tools/memory/updateMemory.js +0 -46
- package/mcp/dist/tools/planning/analyzeRequirements.js +0 -166
- package/mcp/dist/tools/planning/createUserStories.js +0 -119
- package/mcp/dist/tools/planning/featureRoadmap.js +0 -202
- package/mcp/dist/tools/planning/generatePrd.js +0 -156
- package/mcp/dist/tools/prompt/analyzePrompt.js +0 -145
- package/mcp/dist/tools/prompt/enhancePrompt.js +0 -105
- package/mcp/dist/tools/semantic/findReferences.js +0 -195
- package/mcp/dist/tools/semantic/findSymbol.js +0 -200
- package/mcp/dist/tools/thinking/analyzeProblem.js +0 -50
- package/mcp/dist/tools/thinking/breakDownProblem.js +0 -140
- package/mcp/dist/tools/thinking/createThinkingChain.js +0 -39
- package/mcp/dist/tools/thinking/formatAsPlan.js +0 -73
- package/mcp/dist/tools/thinking/stepByStepAnalysis.js +0 -58
- package/mcp/dist/tools/thinking/thinkAloudProcess.js +0 -75
- package/mcp/dist/tools/time/getCurrentTime.js +0 -61
- package/mcp/dist/tools/ui/previewUiAscii.js +0 -232
- package/mcp/dist/types/tool.js +0 -2
- package/mcp/package.json +0 -53
package/README.md
CHANGED
|
@@ -29,17 +29,16 @@ Transform natural language requirements into production-ready code through struc
|
|
|
29
29
|
npm install -g @su-record/vibe
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
This
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
- Enables slash commands for Claude Code
|
|
32
|
+
This installs:
|
|
33
|
+
- Vibe CLI commands
|
|
34
|
+
- @su-record/hi-ai MCP server (38 development tools)
|
|
36
35
|
|
|
37
36
|
---
|
|
38
37
|
|
|
39
38
|
## Quick Start
|
|
40
39
|
|
|
41
40
|
```bash
|
|
42
|
-
# Initialize project
|
|
41
|
+
# Initialize project (registers MCP server for this project)
|
|
43
42
|
vibe init
|
|
44
43
|
|
|
45
44
|
# Create specification through guided Q&A
|
|
@@ -145,12 +144,20 @@ Vibe includes 38 MCP tools across multiple categories:
|
|
|
145
144
|
- `recall_memory` - Retrieve stored information
|
|
146
145
|
- `auto_save_context` - Automatic context checkpointing
|
|
147
146
|
|
|
147
|
+
**MCP Server Registration:**
|
|
148
|
+
|
|
149
|
+
The MCP server is automatically registered when you run `vibe init` in your project. This registers the server **locally for your project only**, not globally.
|
|
150
|
+
|
|
148
151
|
**Verify MCP server:**
|
|
149
152
|
```bash
|
|
153
|
+
# Run this in your project directory
|
|
150
154
|
claude mcp list
|
|
151
|
-
#
|
|
155
|
+
# Expected output:
|
|
156
|
+
# vibe: node /path/to/@su-record/vibe/node_modules/@su-record/hi-ai/dist/index.js - ✓ Connected
|
|
152
157
|
```
|
|
153
158
|
|
|
159
|
+
**Important:** Each project that uses Vibe must run `vibe init` to register the MCP server for that specific project.
|
|
160
|
+
|
|
154
161
|
---
|
|
155
162
|
|
|
156
163
|
## Configuration
|
package/bin/vibe
CHANGED
|
@@ -92,6 +92,20 @@ function init() {
|
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
// MCP 서버 등록 먼저 체크 및 실행
|
|
96
|
+
console.log('🔧 MCP 서버 확인 중...\n');
|
|
97
|
+
try {
|
|
98
|
+
const { execSync } = require('child_process');
|
|
99
|
+
|
|
100
|
+
// MCP 서버 등록 스크립트 실행
|
|
101
|
+
const installScriptPath = path.join(__dirname, '../scripts/install-mcp.js');
|
|
102
|
+
execSync(`node "${installScriptPath}"`, { stdio: 'inherit' });
|
|
103
|
+
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.log('\n⚠️ MCP 서버 등록 중 오류가 발생했습니다.');
|
|
106
|
+
console.log(' 계속 진행합니다...\n');
|
|
107
|
+
}
|
|
108
|
+
|
|
95
109
|
// 폴더 생성
|
|
96
110
|
fs.mkdirSync(vibeDir);
|
|
97
111
|
fs.mkdirSync(path.join(vibeDir, 'specs'));
|
|
@@ -115,7 +129,7 @@ function init() {
|
|
|
115
129
|
},
|
|
116
130
|
mcp: {
|
|
117
131
|
enabled: true,
|
|
118
|
-
servers: ['
|
|
132
|
+
servers: ['vibe']
|
|
119
133
|
},
|
|
120
134
|
quality: {
|
|
121
135
|
strict: true,
|
|
@@ -135,11 +149,15 @@ function init() {
|
|
|
135
149
|
├── plans/ # 기술 계획들
|
|
136
150
|
└── tasks/ # 작업 목록들
|
|
137
151
|
|
|
152
|
+
MCP 서버:
|
|
153
|
+
✓ vibe MCP 서버 등록 완료 (38개 도구)
|
|
154
|
+
확인: claude mcp list
|
|
155
|
+
|
|
138
156
|
언어 변경:
|
|
139
157
|
.vibe/config.json에서 "language"를 "en" 또는 "ko"로 변경
|
|
140
158
|
|
|
141
159
|
다음 단계:
|
|
142
|
-
vibe
|
|
160
|
+
vibe spec "기능명" - 새 기능 SPEC 작성
|
|
143
161
|
`);
|
|
144
162
|
}
|
|
145
163
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@su-record/vibe",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Vibe - SPEC-driven AI coding framework with MCP integration",
|
|
5
5
|
"main": "cli/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"vibe": "./bin/vibe"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "
|
|
11
|
-
"postinstall": "node scripts/install-mcp.js",
|
|
10
|
+
"build": "echo \"No build needed - using @su-record/hi-ai\"",
|
|
12
11
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
12
|
},
|
|
14
13
|
"keywords": [
|
|
@@ -32,7 +31,9 @@
|
|
|
32
31
|
"engines": {
|
|
33
32
|
"node": ">=18.0.0"
|
|
34
33
|
},
|
|
35
|
-
"dependencies": {
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@su-record/hi-ai": "latest"
|
|
36
|
+
},
|
|
36
37
|
"devDependencies": {},
|
|
37
38
|
"files": [
|
|
38
39
|
"bin/",
|
|
@@ -41,8 +42,6 @@
|
|
|
41
42
|
"skills/",
|
|
42
43
|
"templates/",
|
|
43
44
|
"scripts/",
|
|
44
|
-
"mcp/dist/",
|
|
45
|
-
"mcp/package.json",
|
|
46
45
|
"README.md",
|
|
47
46
|
"LICENSE"
|
|
48
47
|
]
|
package/scripts/install-mcp.js
CHANGED
|
@@ -6,14 +6,23 @@ const fs = require('fs');
|
|
|
6
6
|
|
|
7
7
|
console.log('🔧 vibe MCP 서버 설치 중...\n');
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
9
|
+
// vibe 패키지의 실제 설치 경로 찾기
|
|
10
|
+
// npm link인 경우: 로컬 개발 경로
|
|
11
|
+
// npm install -g인 경우: 글로벌 설치 경로
|
|
12
|
+
// path.dirname()을 사용해 크로스 플랫폼 호환 (Windows/Unix)
|
|
13
|
+
const vibePackagePath = path.dirname(__dirname);
|
|
11
14
|
|
|
12
|
-
//
|
|
15
|
+
// MCP 서버 경로 (hi-ai 패키지)
|
|
16
|
+
// path.join()이 자동으로 OS별 경로 구분자 처리
|
|
17
|
+
const mcpIndexPath = path.join(vibePackagePath, 'node_modules', '@su-record', 'hi-ai', 'dist', 'index.js');
|
|
18
|
+
|
|
19
|
+
// hi-ai 설치 확인
|
|
13
20
|
if (!fs.existsSync(mcpIndexPath)) {
|
|
14
|
-
console.log('⚠️ MCP 서버를 찾을 수 없습니다.');
|
|
21
|
+
console.log('⚠️ hi-ai MCP 서버를 찾을 수 없습니다.');
|
|
15
22
|
console.log(' 경로:', mcpIndexPath);
|
|
16
|
-
console.log(' npm install을
|
|
23
|
+
console.log(' npm install을 실행해주세요.\n');
|
|
24
|
+
console.log(' npm install');
|
|
25
|
+
console.log('');
|
|
17
26
|
process.exit(1);
|
|
18
27
|
}
|
|
19
28
|
|
|
@@ -38,11 +47,28 @@ try {
|
|
|
38
47
|
console.log('');
|
|
39
48
|
|
|
40
49
|
} catch (error) {
|
|
50
|
+
// stderr 출력 확인
|
|
51
|
+
const stderrOutput = error.stderr ? error.stderr.toString() : '';
|
|
52
|
+
const stdoutOutput = error.stdout ? error.stdout.toString() : '';
|
|
53
|
+
const fullOutput = error.message + stderrOutput + stdoutOutput;
|
|
54
|
+
|
|
55
|
+
// "already exists" 에러는 성공으로 간주
|
|
56
|
+
if (fullOutput.includes('already exists')) {
|
|
57
|
+
console.log('ℹ️ vibe MCP 서버가 이미 등록되어 있습니다.');
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log('다음 명령어로 확인:');
|
|
60
|
+
console.log(' claude mcp list');
|
|
61
|
+
console.log('');
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
41
65
|
console.error('❌ MCP 서버 등록 실패');
|
|
42
66
|
console.error('');
|
|
43
67
|
console.error('수동 등록 방법:');
|
|
44
68
|
console.error(` claude mcp add vibe node "${mcpIndexPath}"`);
|
|
45
69
|
console.error('');
|
|
46
70
|
console.error('에러:', error.message);
|
|
71
|
+
if (stderrOutput) console.error('stderr:', stderrOutput);
|
|
72
|
+
if (stdoutOutput) console.error('stdout:', stdoutOutput);
|
|
47
73
|
process.exit(1);
|
|
48
74
|
}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect } from 'vitest';
|
|
2
|
-
import { analyzeComplexity, analyzeComplexityDefinition } from '../tools/convention/analyzeComplexity.js';
|
|
3
|
-
describe('Code Complexity Analysis', () => {
|
|
4
|
-
describe('analyzeComplexity', () => {
|
|
5
|
-
test('should analyze simple function with low complexity', async () => {
|
|
6
|
-
const simpleCode = `
|
|
7
|
-
function add(a, b) {
|
|
8
|
-
return a + b;
|
|
9
|
-
}
|
|
10
|
-
`;
|
|
11
|
-
const result = await analyzeComplexity({ code: simpleCode, metrics: 'all' });
|
|
12
|
-
const parsed = JSON.parse(result.content[0].text.replace('Complexity Analysis:\n', ''));
|
|
13
|
-
expect(parsed.status).toBe('success');
|
|
14
|
-
expect(parsed.results.cyclomaticComplexity).toBeDefined();
|
|
15
|
-
expect(parsed.results.cognitiveComplexity).toBeDefined();
|
|
16
|
-
expect(parsed.results.halsteadMetrics).toBeDefined();
|
|
17
|
-
expect(parsed.results.cyclomaticComplexity.status).toBe('pass');
|
|
18
|
-
});
|
|
19
|
-
test('should detect high cyclomatic complexity', async () => {
|
|
20
|
-
const complexCode = `
|
|
21
|
-
function complexFunction(x) {
|
|
22
|
-
if (x > 0) {
|
|
23
|
-
if (x < 10) {
|
|
24
|
-
for (let i = 0; i < x; i++) {
|
|
25
|
-
if (i % 2 === 0) {
|
|
26
|
-
while (i < 5) {
|
|
27
|
-
if (i === 3) {
|
|
28
|
-
return i;
|
|
29
|
-
}
|
|
30
|
-
i++;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return x;
|
|
37
|
-
}
|
|
38
|
-
`;
|
|
39
|
-
const result = await analyzeComplexity({ code: complexCode, metrics: 'all' });
|
|
40
|
-
const parsed = JSON.parse(result.content[0].text.replace('Complexity Analysis:\n', ''));
|
|
41
|
-
expect(parsed.results.cyclomaticComplexity.value).toBeGreaterThan(5);
|
|
42
|
-
expect(parsed.issues.length).toBeGreaterThan(0);
|
|
43
|
-
});
|
|
44
|
-
test('should calculate AST-based complexity', async () => {
|
|
45
|
-
const code = `
|
|
46
|
-
function test() {
|
|
47
|
-
if (true) return 1;
|
|
48
|
-
for (let i = 0; i < 10; i++) {
|
|
49
|
-
console.log(i);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
`;
|
|
53
|
-
const result = await analyzeComplexity({ code, metrics: 'cyclomatic' });
|
|
54
|
-
const parsed = JSON.parse(result.content[0].text.replace('Complexity Analysis:\n', ''));
|
|
55
|
-
expect(parsed.results.astCyclomaticComplexity).toBeDefined();
|
|
56
|
-
expect(parsed.results.astCyclomaticComplexity.value).toBeGreaterThan(1);
|
|
57
|
-
});
|
|
58
|
-
test('should calculate cognitive complexity with nesting', async () => {
|
|
59
|
-
const code = `
|
|
60
|
-
function nested() {
|
|
61
|
-
if (a) {
|
|
62
|
-
if (b) {
|
|
63
|
-
if (c) {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
`;
|
|
70
|
-
const result = await analyzeComplexity({ code, metrics: 'cognitive' });
|
|
71
|
-
const parsed = JSON.parse(result.content[0].text.replace('Complexity Analysis:\n', ''));
|
|
72
|
-
expect(parsed.results.cognitiveComplexity.value).toBeGreaterThan(0);
|
|
73
|
-
});
|
|
74
|
-
test('should calculate Halstead metrics', async () => {
|
|
75
|
-
const code = `
|
|
76
|
-
function calculate(x, y) {
|
|
77
|
-
const sum = x + y;
|
|
78
|
-
const product = x * y;
|
|
79
|
-
return sum + product;
|
|
80
|
-
}
|
|
81
|
-
`;
|
|
82
|
-
const result = await analyzeComplexity({ code, metrics: 'halstead' });
|
|
83
|
-
const parsed = JSON.parse(result.content[0].text.replace('Complexity Analysis:\n', ''));
|
|
84
|
-
expect(parsed.results.halsteadMetrics).toBeDefined();
|
|
85
|
-
expect(parsed.results.halsteadMetrics.vocabulary).toBeGreaterThan(0);
|
|
86
|
-
expect(parsed.results.halsteadMetrics.difficulty).toBeGreaterThan(0);
|
|
87
|
-
});
|
|
88
|
-
test('should provide recommendations for complex code', async () => {
|
|
89
|
-
const veryComplexCode = `
|
|
90
|
-
function veryComplex(a, b, c, d, e) {
|
|
91
|
-
if (a) {
|
|
92
|
-
if (b) {
|
|
93
|
-
for (let i = 0; i < 10; i++) {
|
|
94
|
-
if (c) {
|
|
95
|
-
while (d) {
|
|
96
|
-
if (e) {
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
`;
|
|
107
|
-
const result = await analyzeComplexity({ code: veryComplexCode, metrics: 'all' });
|
|
108
|
-
const parsed = JSON.parse(result.content[0].text.replace('Complexity Analysis:\n', ''));
|
|
109
|
-
expect(parsed.recommendations).toBeDefined();
|
|
110
|
-
expect(parsed.recommendations.length).toBeGreaterThan(0);
|
|
111
|
-
expect(parsed.overallScore).toBeLessThan(100);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
describe('Tool Definition', () => {
|
|
115
|
-
test('should have correct definition', () => {
|
|
116
|
-
expect(analyzeComplexityDefinition.name).toBe('analyze_complexity');
|
|
117
|
-
expect(analyzeComplexityDefinition.description).toContain('IMPORTANT');
|
|
118
|
-
expect(analyzeComplexityDefinition.inputSchema.required).toContain('code');
|
|
119
|
-
});
|
|
120
|
-
test('should include keyword triggers', () => {
|
|
121
|
-
const desc = analyzeComplexityDefinition.description;
|
|
122
|
-
expect(desc).toContain('복잡도');
|
|
123
|
-
expect(desc).toContain('complexity');
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { saveMemory, saveMemoryDefinition } from '../tools/memory/saveMemory.js';
|
|
3
|
-
import { recallMemory } from '../tools/memory/recallMemory.js';
|
|
4
|
-
import { listMemories } from '../tools/memory/listMemories.js';
|
|
5
|
-
import { deleteMemory } from '../tools/memory/deleteMemory.js';
|
|
6
|
-
import { memoryDB } from '../tools/memory/database.js';
|
|
7
|
-
import { clearAllMemories } from '../tools/memory/memorySQLite.js';
|
|
8
|
-
import { promises as fs } from 'fs';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
const TEST_MEMORY_DIR = path.join(process.cwd(), 'memories');
|
|
11
|
-
describe('Memory Management Tools', () => {
|
|
12
|
-
beforeEach(async () => {
|
|
13
|
-
// Ensure fresh database for each test
|
|
14
|
-
try {
|
|
15
|
-
// Close existing connection if any
|
|
16
|
-
memoryDB.close();
|
|
17
|
-
}
|
|
18
|
-
catch (error) {
|
|
19
|
-
// Ignore
|
|
20
|
-
}
|
|
21
|
-
// Clean up test files
|
|
22
|
-
try {
|
|
23
|
-
await fs.rm(TEST_MEMORY_DIR, { recursive: true, force: true });
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
// Ignore
|
|
27
|
-
}
|
|
28
|
-
// Clear all memories (this will recreate DB if needed)
|
|
29
|
-
try {
|
|
30
|
-
clearAllMemories();
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
// Database might not exist yet, will be created on first use
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
afterEach(async () => {
|
|
37
|
-
// Close database connection
|
|
38
|
-
try {
|
|
39
|
-
memoryDB.close();
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
// Ignore
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
describe('saveMemory', () => {
|
|
46
|
-
test('should save memory successfully', async () => {
|
|
47
|
-
const result = await saveMemory({
|
|
48
|
-
key: 'test-key',
|
|
49
|
-
value: 'test-value',
|
|
50
|
-
category: 'project'
|
|
51
|
-
});
|
|
52
|
-
expect(result.content[0].text).toContain('success');
|
|
53
|
-
expect(result.content[0].text).toContain('test-key');
|
|
54
|
-
});
|
|
55
|
-
test('should create memory database if not exists', async () => {
|
|
56
|
-
await saveMemory({ key: 'test-key', value: 'test-value' });
|
|
57
|
-
// Check if database file exists
|
|
58
|
-
const dbPath = path.join(TEST_MEMORY_DIR, 'memories.db');
|
|
59
|
-
const dbExists = await fs.access(dbPath).then(() => true).catch(() => false);
|
|
60
|
-
expect(dbExists).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
describe('recallMemory', () => {
|
|
64
|
-
test('should recall saved memory', async () => {
|
|
65
|
-
await saveMemory({ key: 'recall-test', value: 'recall-value' });
|
|
66
|
-
const result = await recallMemory({ key: 'recall-test' });
|
|
67
|
-
const parsedResult = JSON.parse(result.content[0].text.replace('Memory recalled:\n', ''));
|
|
68
|
-
expect(parsedResult.value).toBe('recall-value');
|
|
69
|
-
expect(parsedResult.key).toBe('recall-test');
|
|
70
|
-
});
|
|
71
|
-
test('should return not found for non-existent key', async () => {
|
|
72
|
-
const result = await recallMemory({ key: 'non-existent' });
|
|
73
|
-
expect(result.content[0].text).toContain('not found');
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
describe('listMemories', () => {
|
|
77
|
-
test('should list all memories', async () => {
|
|
78
|
-
await saveMemory({ key: 'key1', value: 'value1', category: 'project' });
|
|
79
|
-
await saveMemory({ key: 'key2', value: 'value2', category: 'code' });
|
|
80
|
-
const result = await listMemories({ summary: false });
|
|
81
|
-
const parsed = JSON.parse(result.content[0].text.replace('Memory list:\n', ''));
|
|
82
|
-
expect(parsed.memories.length).toBe(2);
|
|
83
|
-
expect(parsed.total).toBe(2);
|
|
84
|
-
});
|
|
85
|
-
test('should filter memories by category', async () => {
|
|
86
|
-
await saveMemory({ key: 'project1', value: 'val1', category: 'project' });
|
|
87
|
-
await saveMemory({ key: 'code1', value: 'val2', category: 'code' });
|
|
88
|
-
const result = await listMemories({ category: 'project', summary: false });
|
|
89
|
-
const parsed = JSON.parse(result.content[0].text.replace('Memory list:\n', ''));
|
|
90
|
-
expect(parsed.total).toBe(1);
|
|
91
|
-
expect(parsed.memories[0].key).toBe('project1');
|
|
92
|
-
});
|
|
93
|
-
test('should return empty array when no memories exist', async () => {
|
|
94
|
-
const result = await listMemories({ summary: false });
|
|
95
|
-
const parsed = JSON.parse(result.content[0].text.replace('Memory list:\n', ''));
|
|
96
|
-
expect(parsed.memories).toEqual([]);
|
|
97
|
-
expect(parsed.total).toBe(0);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
describe('deleteMemory', () => {
|
|
101
|
-
test('should delete existing memory', async () => {
|
|
102
|
-
await saveMemory({ key: 'delete-me', value: 'temporary' });
|
|
103
|
-
const deleteResult = await deleteMemory({ key: 'delete-me' });
|
|
104
|
-
expect(deleteResult.content[0].text).toContain('success');
|
|
105
|
-
const recallResult = await recallMemory({ key: 'delete-me' });
|
|
106
|
-
expect(recallResult.content[0].text).toContain('not found');
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
describe('Tool Definition', () => {
|
|
110
|
-
test('should have correct structure', () => {
|
|
111
|
-
expect(saveMemoryDefinition.name).toBe('save_memory');
|
|
112
|
-
expect(saveMemoryDefinition.description).toContain('IMPORTANT');
|
|
113
|
-
expect(saveMemoryDefinition.inputSchema.required).toContain('key');
|
|
114
|
-
});
|
|
115
|
-
test('should include keyword triggers', () => {
|
|
116
|
-
expect(saveMemoryDefinition.description).toContain('기억해');
|
|
117
|
-
expect(saveMemoryDefinition.description).toContain('remember');
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Python and Dart/Flutter code complexity analysis
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect } from 'vitest';
|
|
5
|
-
import { detectLanguage } from '../tools/convention/languageDetector.js';
|
|
6
|
-
import { calculatePythonComplexity, calculatePythonCognitiveComplexity } from '../tools/convention/pythonComplexity.js';
|
|
7
|
-
import { calculateDartComplexity, calculateDartCognitiveComplexity } from '../tools/convention/dartComplexity.js';
|
|
8
|
-
import { analyzeComplexity } from '../tools/convention/analyzeComplexity.js';
|
|
9
|
-
describe('Language Detection', () => {
|
|
10
|
-
it('should detect Python code', () => {
|
|
11
|
-
const pythonCode = `
|
|
12
|
-
def calculate_sum(numbers):
|
|
13
|
-
total = 0
|
|
14
|
-
for num in numbers:
|
|
15
|
-
total += num
|
|
16
|
-
return total
|
|
17
|
-
`;
|
|
18
|
-
expect(detectLanguage(pythonCode)).toBe('python');
|
|
19
|
-
});
|
|
20
|
-
it('should detect Dart/Flutter code', () => {
|
|
21
|
-
const dartCode = `
|
|
22
|
-
class MyWidget extends StatelessWidget {
|
|
23
|
-
@override
|
|
24
|
-
Widget build(BuildContext context) {
|
|
25
|
-
return Container(
|
|
26
|
-
child: Text('Hello'),
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
`;
|
|
31
|
-
expect(detectLanguage(dartCode)).toBe('dart');
|
|
32
|
-
});
|
|
33
|
-
it('should detect TypeScript code', () => {
|
|
34
|
-
const tsCode = `
|
|
35
|
-
interface User {
|
|
36
|
-
name: string;
|
|
37
|
-
age: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function greet(user: User): void {
|
|
41
|
-
console.log(\`Hello, \${user.name}\`);
|
|
42
|
-
}
|
|
43
|
-
`;
|
|
44
|
-
expect(detectLanguage(tsCode)).toBe('typescript');
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
describe('Python Complexity Analysis', () => {
|
|
48
|
-
it('should calculate Python cyclomatic complexity', () => {
|
|
49
|
-
const pythonCode = `
|
|
50
|
-
def complex_function(x, y, z):
|
|
51
|
-
if x > 0:
|
|
52
|
-
if y > 0:
|
|
53
|
-
return x + y
|
|
54
|
-
elif y < 0:
|
|
55
|
-
return x - y
|
|
56
|
-
elif z > 0:
|
|
57
|
-
for i in range(10):
|
|
58
|
-
if i % 2 == 0:
|
|
59
|
-
print(i)
|
|
60
|
-
return 0
|
|
61
|
-
`;
|
|
62
|
-
const result = calculatePythonComplexity(pythonCode);
|
|
63
|
-
expect(result.value).toBeGreaterThan(5);
|
|
64
|
-
expect(result.status).toBeDefined();
|
|
65
|
-
});
|
|
66
|
-
it('should calculate Python cognitive complexity', () => {
|
|
67
|
-
const pythonCode = `
|
|
68
|
-
def nested_function(items):
|
|
69
|
-
for item in items:
|
|
70
|
-
if item > 0:
|
|
71
|
-
if item % 2 == 0:
|
|
72
|
-
print("Even positive")
|
|
73
|
-
return items
|
|
74
|
-
`;
|
|
75
|
-
const result = calculatePythonCognitiveComplexity(pythonCode);
|
|
76
|
-
expect(result.value).toBeGreaterThan(0);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
describe('Dart Complexity Analysis', () => {
|
|
80
|
-
it('should calculate Dart cyclomatic complexity', () => {
|
|
81
|
-
const dartCode = `
|
|
82
|
-
Widget buildWidget(bool condition) {
|
|
83
|
-
if (condition) {
|
|
84
|
-
return Text('Yes');
|
|
85
|
-
} else {
|
|
86
|
-
return Text('No');
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
`;
|
|
90
|
-
const result = calculateDartComplexity(dartCode);
|
|
91
|
-
expect(result.value).toBeGreaterThan(1);
|
|
92
|
-
});
|
|
93
|
-
it('should calculate Dart cognitive complexity', () => {
|
|
94
|
-
const dartCode = `
|
|
95
|
-
class MyWidget extends StatelessWidget {
|
|
96
|
-
@override
|
|
97
|
-
Widget build(BuildContext context) {
|
|
98
|
-
return condition1
|
|
99
|
-
? Text('A')
|
|
100
|
-
: condition2
|
|
101
|
-
? Text('B')
|
|
102
|
-
: Text('C');
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
`;
|
|
106
|
-
const result = calculateDartCognitiveComplexity(dartCode);
|
|
107
|
-
expect(result.value).toBeGreaterThanOrEqual(0); // Ternary operators may not be counted
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
describe('Integrated Complexity Analysis', () => {
|
|
111
|
-
it('should analyze Python code with auto-detection', async () => {
|
|
112
|
-
const pythonCode = `
|
|
113
|
-
def fibonacci(n):
|
|
114
|
-
if n <= 1:
|
|
115
|
-
return n
|
|
116
|
-
return fibonacci(n-1) + fibonacci(n-2)
|
|
117
|
-
`;
|
|
118
|
-
const result = await analyzeComplexity({ code: pythonCode });
|
|
119
|
-
expect(result.content[0].text).toContain('Python');
|
|
120
|
-
expect(result.content[0].text).toContain('cyclomaticComplexity');
|
|
121
|
-
});
|
|
122
|
-
it('should analyze Dart code with auto-detection', async () => {
|
|
123
|
-
const dartCode = `
|
|
124
|
-
class Counter extends StatefulWidget {
|
|
125
|
-
@override
|
|
126
|
-
_CounterState createState() => _CounterState();
|
|
127
|
-
}
|
|
128
|
-
`;
|
|
129
|
-
const result = await analyzeComplexity({ code: dartCode });
|
|
130
|
-
expect(result.content[0].text).toContain('Dart');
|
|
131
|
-
});
|
|
132
|
-
it('should analyze TypeScript code', async () => {
|
|
133
|
-
const tsCode = `
|
|
134
|
-
function complexFunc(a: number, b: number): number {
|
|
135
|
-
if (a > b) {
|
|
136
|
-
return a;
|
|
137
|
-
} else if (a < b) {
|
|
138
|
-
return b;
|
|
139
|
-
}
|
|
140
|
-
return 0;
|
|
141
|
-
}
|
|
142
|
-
`;
|
|
143
|
-
const result = await analyzeComplexity({ code: tsCode });
|
|
144
|
-
expect(result.content[0].text).toContain('TypeScript');
|
|
145
|
-
});
|
|
146
|
-
});
|