@su-record/vibe 0.1.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/LICENSE +21 -0
- package/README.md +448 -0
- package/agents/backend-python-expert.md +453 -0
- package/agents/database-postgres-expert.md +538 -0
- package/agents/frontend-flutter-expert.md +487 -0
- package/agents/frontend-react-expert.md +424 -0
- package/agents/quality-reviewer.md +542 -0
- package/agents/specification-agent.md +505 -0
- package/bin/sutory +332 -0
- package/bin/vibe +338 -0
- package/mcp/dist/__tests__/complexity.test.js +126 -0
- package/mcp/dist/__tests__/memory.test.js +120 -0
- package/mcp/dist/__tests__/python-dart-complexity.test.js +146 -0
- package/mcp/dist/index.js +230 -0
- package/mcp/dist/lib/ContextCompressor.js +305 -0
- package/mcp/dist/lib/MemoryManager.js +334 -0
- package/mcp/dist/lib/ProjectCache.js +126 -0
- package/mcp/dist/lib/PythonParser.js +241 -0
- package/mcp/dist/tools/browser/browserPool.js +76 -0
- package/mcp/dist/tools/browser/browserUtils.js +135 -0
- package/mcp/dist/tools/browser/inspectNetworkRequests.js +140 -0
- package/mcp/dist/tools/browser/monitorConsoleLogs.js +97 -0
- package/mcp/dist/tools/convention/analyzeComplexity.js +248 -0
- package/mcp/dist/tools/convention/applyQualityRules.js +102 -0
- package/mcp/dist/tools/convention/checkCouplingCohesion.js +233 -0
- package/mcp/dist/tools/convention/complexityMetrics.js +133 -0
- package/mcp/dist/tools/convention/dartComplexity.js +117 -0
- package/mcp/dist/tools/convention/getCodingGuide.js +64 -0
- package/mcp/dist/tools/convention/languageDetector.js +50 -0
- package/mcp/dist/tools/convention/pythonComplexity.js +109 -0
- package/mcp/dist/tools/convention/suggestImprovements.js +257 -0
- package/mcp/dist/tools/convention/validateCodeQuality.js +177 -0
- package/mcp/dist/tools/memory/autoSaveContext.js +79 -0
- package/mcp/dist/tools/memory/database.js +123 -0
- package/mcp/dist/tools/memory/deleteMemory.js +39 -0
- package/mcp/dist/tools/memory/listMemories.js +38 -0
- package/mcp/dist/tools/memory/memoryConfig.js +27 -0
- package/mcp/dist/tools/memory/memorySQLite.js +138 -0
- package/mcp/dist/tools/memory/memoryUtils.js +34 -0
- package/mcp/dist/tools/memory/migrate.js +113 -0
- package/mcp/dist/tools/memory/prioritizeMemory.js +109 -0
- package/mcp/dist/tools/memory/recallMemory.js +40 -0
- package/mcp/dist/tools/memory/restoreSessionContext.js +69 -0
- package/mcp/dist/tools/memory/saveMemory.js +34 -0
- package/mcp/dist/tools/memory/searchMemories.js +37 -0
- package/mcp/dist/tools/memory/startSession.js +100 -0
- package/mcp/dist/tools/memory/updateMemory.js +46 -0
- package/mcp/dist/tools/planning/analyzeRequirements.js +166 -0
- package/mcp/dist/tools/planning/createUserStories.js +119 -0
- package/mcp/dist/tools/planning/featureRoadmap.js +202 -0
- package/mcp/dist/tools/planning/generatePrd.js +156 -0
- package/mcp/dist/tools/prompt/analyzePrompt.js +145 -0
- package/mcp/dist/tools/prompt/enhancePrompt.js +105 -0
- package/mcp/dist/tools/semantic/findReferences.js +195 -0
- package/mcp/dist/tools/semantic/findSymbol.js +200 -0
- package/mcp/dist/tools/thinking/analyzeProblem.js +50 -0
- package/mcp/dist/tools/thinking/breakDownProblem.js +140 -0
- package/mcp/dist/tools/thinking/createThinkingChain.js +39 -0
- package/mcp/dist/tools/thinking/formatAsPlan.js +73 -0
- package/mcp/dist/tools/thinking/stepByStepAnalysis.js +58 -0
- package/mcp/dist/tools/thinking/thinkAloudProcess.js +75 -0
- package/mcp/dist/tools/time/getCurrentTime.js +61 -0
- package/mcp/dist/tools/ui/previewUiAscii.js +232 -0
- package/mcp/dist/types/tool.js +2 -0
- package/mcp/package.json +53 -0
- package/package.json +49 -0
- package/scripts/install-mcp.js +48 -0
- package/scripts/install.sh +70 -0
- package/skills/core/communication-guide.md +104 -0
- package/skills/core/development-philosophy.md +53 -0
- package/skills/core/quick-start.md +121 -0
- package/skills/languages/dart-flutter.md +509 -0
- package/skills/languages/python-fastapi.md +386 -0
- package/skills/languages/typescript-nextjs.md +441 -0
- package/skills/languages/typescript-react-native.md +446 -0
- package/skills/languages/typescript-react.md +525 -0
- package/skills/quality/checklist.md +276 -0
- package/skills/quality/testing-strategy.md +437 -0
- package/skills/standards/anti-patterns.md +369 -0
- package/skills/standards/code-structure.md +291 -0
- package/skills/standards/complexity-metrics.md +312 -0
- package/skills/standards/naming-conventions.md +198 -0
- package/skills/tools/mcp-hi-ai-guide.md +665 -0
- package/skills/tools/mcp-workflow.md +51 -0
- package/templates/constitution-template.md +193 -0
- package/templates/plan-template.md +237 -0
- package/templates/spec-template.md +142 -0
- package/templates/tasks-template.md +132 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Sequential thinking tool - completely independent
|
|
2
|
+
export const formatAsPlanDefinition = {
|
|
3
|
+
name: 'format_as_plan',
|
|
4
|
+
description: '๊ณํ์ผ๋ก|์ ๋ฆฌํด์ค|์ฒดํฌ๋ฆฌ์คํธ|format as plan|make a plan|organize this|checklist - Format content into clear plans',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
content: { type: 'string', description: 'Content to format as a plan' },
|
|
9
|
+
priority: { type: 'string', description: 'Default priority level', enum: ['high', 'medium', 'low'] },
|
|
10
|
+
includeTimeEstimates: { type: 'boolean', description: 'Include time estimates for each step' },
|
|
11
|
+
includeCheckboxes: { type: 'boolean', description: 'Include checkboxes for tracking progress' }
|
|
12
|
+
},
|
|
13
|
+
required: ['content']
|
|
14
|
+
},
|
|
15
|
+
annotations: {
|
|
16
|
+
title: 'Format as Plan',
|
|
17
|
+
audience: ['user', 'assistant']
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export async function formatAsPlan(args) {
|
|
21
|
+
const { content: planContent, priority = 'medium', includeTimeEstimates = true, includeCheckboxes = true } = args;
|
|
22
|
+
// Parse content into actionable steps
|
|
23
|
+
const sentences = planContent.split(/[.!?]+/).filter(s => s.trim().length > 10);
|
|
24
|
+
const planSteps = sentences.map((sentence, index) => {
|
|
25
|
+
const stepNumber = index + 1;
|
|
26
|
+
const cleanSentence = sentence.trim();
|
|
27
|
+
// Estimate time based on content complexity
|
|
28
|
+
let timeEstimate = '5min';
|
|
29
|
+
if (cleanSentence.length > 100)
|
|
30
|
+
timeEstimate = '15min';
|
|
31
|
+
else if (cleanSentence.length > 50)
|
|
32
|
+
timeEstimate = '10min';
|
|
33
|
+
// Detect priority keywords
|
|
34
|
+
let stepPriority = priority;
|
|
35
|
+
if (cleanSentence.match(/urgent|critical|important|first|must/i))
|
|
36
|
+
stepPriority = 'high';
|
|
37
|
+
else if (cleanSentence.match(/later|eventually|nice|optional/i))
|
|
38
|
+
stepPriority = 'low';
|
|
39
|
+
// Format step
|
|
40
|
+
let formattedStep = includeCheckboxes ? `${stepNumber}. โก ` : `${stepNumber}. `;
|
|
41
|
+
formattedStep += cleanSentence;
|
|
42
|
+
if (includeTimeEstimates)
|
|
43
|
+
formattedStep += ` (${stepPriority.toUpperCase()}, ${timeEstimate})`;
|
|
44
|
+
return {
|
|
45
|
+
number: stepNumber,
|
|
46
|
+
content: cleanSentence,
|
|
47
|
+
priority: stepPriority,
|
|
48
|
+
timeEstimate,
|
|
49
|
+
formatted: formattedStep
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
// Calculate total time
|
|
53
|
+
const totalMinutes = planSteps.reduce((total, step) => {
|
|
54
|
+
const minutes = parseInt(step.timeEstimate.replace('min', ''));
|
|
55
|
+
return total + minutes;
|
|
56
|
+
}, 0);
|
|
57
|
+
const planResult = {
|
|
58
|
+
action: 'format_as_plan',
|
|
59
|
+
originalContent: planContent,
|
|
60
|
+
formattedPlan: planSteps.map((s) => s.formatted).join('\n'),
|
|
61
|
+
steps: planSteps.length,
|
|
62
|
+
totalEstimatedTime: `${totalMinutes} minutes`,
|
|
63
|
+
breakdown: {
|
|
64
|
+
high: planSteps.filter((s) => s.priority === 'high').length,
|
|
65
|
+
medium: planSteps.filter((s) => s.priority === 'medium').length,
|
|
66
|
+
low: planSteps.filter((s) => s.priority === 'low').length
|
|
67
|
+
},
|
|
68
|
+
status: 'success'
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
content: [{ type: 'text', text: `${planResult.formattedPlan}\n\nTotal: ${planResult.totalEstimatedTime} | Priority: ${planResult.breakdown.high}H ${planResult.breakdown.medium}M ${planResult.breakdown.low}L` }]
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Sequential thinking tool - completely independent
|
|
2
|
+
export const stepByStepAnalysisDefinition = {
|
|
3
|
+
name: 'step_by_step_analysis',
|
|
4
|
+
description: '๋จ๊ณ๋ณ|์ฐจ๊ทผ์ฐจ๊ทผ|ํ๋์ฉ|step by step|one by one|gradually - Perform detailed step-by-step analysis',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
task: { type: 'string', description: 'Task to analyze step by step' },
|
|
9
|
+
context: { type: 'string', description: 'Additional context for the task' },
|
|
10
|
+
detailLevel: { type: 'string', description: 'Level of detail', enum: ['basic', 'detailed', 'comprehensive'] }
|
|
11
|
+
},
|
|
12
|
+
required: ['task']
|
|
13
|
+
},
|
|
14
|
+
annotations: {
|
|
15
|
+
title: 'Step-by-Step Analysis',
|
|
16
|
+
audience: ['user', 'assistant']
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export async function stepByStepAnalysis(args) {
|
|
20
|
+
const { task, context = '', detailLevel = 'detailed' } = args;
|
|
21
|
+
const stepCount = detailLevel === 'basic' ? 3 : detailLevel === 'detailed' ? 5 : 7;
|
|
22
|
+
const stepAnalysis = {
|
|
23
|
+
action: 'step_by_step_analysis',
|
|
24
|
+
task,
|
|
25
|
+
context,
|
|
26
|
+
detailLevel,
|
|
27
|
+
steps: Array.from({ length: stepCount }, (_, i) => {
|
|
28
|
+
const stepNum = i + 1;
|
|
29
|
+
return {
|
|
30
|
+
stepNumber: stepNum,
|
|
31
|
+
title: `Step ${stepNum}: ${task} - Phase ${stepNum}`,
|
|
32
|
+
description: `Detailed analysis of ${task} in step ${stepNum}`,
|
|
33
|
+
actions: [
|
|
34
|
+
`Analyze requirements for step ${stepNum}`,
|
|
35
|
+
`Identify dependencies and prerequisites`,
|
|
36
|
+
`Execute the planned actions`,
|
|
37
|
+
`Validate results and check for issues`,
|
|
38
|
+
`Prepare for next step`
|
|
39
|
+
],
|
|
40
|
+
checkpoints: [
|
|
41
|
+
`Verify step ${stepNum} requirements are met`,
|
|
42
|
+
`Confirm outputs are as expected`,
|
|
43
|
+
`Check for any blocking issues`
|
|
44
|
+
],
|
|
45
|
+
estimatedTime: detailLevel === 'comprehensive' ? `${stepNum * 10} minutes` : `${stepNum * 5} minutes`
|
|
46
|
+
};
|
|
47
|
+
}),
|
|
48
|
+
summary: {
|
|
49
|
+
totalSteps: stepCount,
|
|
50
|
+
estimatedTotalTime: detailLevel === 'comprehensive' ? `${stepCount * 35} minutes` : `${stepCount * 20} minutes`,
|
|
51
|
+
complexity: detailLevel === 'basic' ? 'low' : detailLevel === 'detailed' ? 'medium' : 'high'
|
|
52
|
+
},
|
|
53
|
+
status: 'success'
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: 'text', text: `Task: ${task}\nDetail: ${detailLevel}\nSteps: ${stepCount}\n\n${stepAnalysis.steps.map(s => `Step ${s.stepNumber}: ${s.title}\n Time: ${s.estimatedTime}\n Actions: ${s.actions.join(', ')}\n Checkpoints: ${s.checkpoints.join(', ')}`).join('\n\n')}\n\nTotal Time: ${stepAnalysis.summary.estimatedTotalTime} | Complexity: ${stepAnalysis.summary.complexity}` }]
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Sequential thinking tool - completely independent
|
|
2
|
+
export const thinkAloudProcessDefinition = {
|
|
3
|
+
name: 'think_aloud_process',
|
|
4
|
+
description: '์๊ฐํด๋ด|๊ณ ๋ฏผํด๋ด|์ด๋ป๊ฒ ์๊ฐํด|think about it|let me think|reasoning - Generate think-aloud reasoning process',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
scenario: { type: 'string', description: 'Scenario or problem to think through' },
|
|
9
|
+
perspective: { type: 'string', description: 'Thinking perspective', enum: ['analytical', 'creative', 'systematic', 'critical'] },
|
|
10
|
+
verbosity: { type: 'string', description: 'Verbosity level', enum: ['concise', 'moderate', 'verbose'] }
|
|
11
|
+
},
|
|
12
|
+
required: ['scenario']
|
|
13
|
+
},
|
|
14
|
+
annotations: {
|
|
15
|
+
title: 'Think Aloud',
|
|
16
|
+
audience: ['user', 'assistant']
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export async function thinkAloudProcess(args) {
|
|
20
|
+
const { scenario, perspective = 'analytical', verbosity = 'moderate' } = args;
|
|
21
|
+
const thoughtCount = verbosity === 'concise' ? 3 : verbosity === 'moderate' ? 5 : 8;
|
|
22
|
+
const thinkAloudProcess = {
|
|
23
|
+
action: 'think_aloud_process',
|
|
24
|
+
scenario,
|
|
25
|
+
perspective,
|
|
26
|
+
verbosity,
|
|
27
|
+
thoughtProcess: Array.from({ length: thoughtCount }, (_, i) => {
|
|
28
|
+
const thoughtNum = i + 1;
|
|
29
|
+
let thoughtStyle = '';
|
|
30
|
+
switch (perspective) {
|
|
31
|
+
case 'analytical':
|
|
32
|
+
thoughtStyle = `Analyzing: Let me examine ${scenario} systematically...`;
|
|
33
|
+
break;
|
|
34
|
+
case 'creative':
|
|
35
|
+
thoughtStyle = `Brainstorming: What if I approach ${scenario} differently...`;
|
|
36
|
+
break;
|
|
37
|
+
case 'systematic':
|
|
38
|
+
thoughtStyle = `Step ${thoughtNum}: Following systematic approach to ${scenario}...`;
|
|
39
|
+
break;
|
|
40
|
+
case 'critical':
|
|
41
|
+
thoughtStyle = `Questioning: What assumptions am I making about ${scenario}...`;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
stepNumber: thoughtNum,
|
|
46
|
+
thought: thoughtStyle,
|
|
47
|
+
reasoning: `In step ${thoughtNum}, I need to consider the implications of ${scenario} from a ${perspective} perspective`,
|
|
48
|
+
questions: [
|
|
49
|
+
`What do I know about this aspect of ${scenario}?`,
|
|
50
|
+
`What don't I know yet?`,
|
|
51
|
+
`What should I explore next?`
|
|
52
|
+
],
|
|
53
|
+
conclusions: [
|
|
54
|
+
`Based on current analysis...`,
|
|
55
|
+
`This leads me to think that...`,
|
|
56
|
+
`Next I should focus on...`
|
|
57
|
+
],
|
|
58
|
+
confidence: Math.min(95, 60 + (thoughtNum * 5))
|
|
59
|
+
};
|
|
60
|
+
}),
|
|
61
|
+
metacognition: {
|
|
62
|
+
thinkingStyle: perspective,
|
|
63
|
+
processEffectiveness: verbosity === 'verbose' ? 'highly detailed' : verbosity === 'moderate' ? 'balanced' : 'efficient',
|
|
64
|
+
nextSteps: [
|
|
65
|
+
'Review thinking process for gaps',
|
|
66
|
+
'Validate conclusions against evidence',
|
|
67
|
+
'Plan concrete actions based on analysis'
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
status: 'success'
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: 'text', text: `Scenario: ${scenario}\nPerspective: ${perspective}\nThoughts: ${thoughtCount}\n${thinkAloudProcess.thoughtProcess.map((t, i) => `${i + 1}. ${t.thought} (confidence: ${t.confidence}%)`).join('\n')}\n\nNext: ${thinkAloudProcess.metacognition.nextSteps.join(', ')}` }]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Time utility tool - completely independent
|
|
2
|
+
export const getCurrentTimeDefinition = {
|
|
3
|
+
name: 'get_current_time',
|
|
4
|
+
description: '์ง๊ธ ๋ช์|ํ์ฌ ์๊ฐ|๋ช์์ผ|what time|current time|time now - Get current time',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
format: { type: 'string', description: 'Time format', enum: ['iso', 'local', 'utc', 'timestamp', 'human'] },
|
|
9
|
+
timezone: { type: 'string', description: 'Timezone (e.g., America/New_York, Asia/Seoul)' }
|
|
10
|
+
},
|
|
11
|
+
required: []
|
|
12
|
+
},
|
|
13
|
+
annotations: {
|
|
14
|
+
title: 'Get Current Time',
|
|
15
|
+
audience: ['user', 'assistant']
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export async function getCurrentTime(args) {
|
|
19
|
+
const { format = 'iso', timezone } = args;
|
|
20
|
+
const now = new Date();
|
|
21
|
+
let timeResult;
|
|
22
|
+
switch (format) {
|
|
23
|
+
case 'iso':
|
|
24
|
+
timeResult = now.toISOString();
|
|
25
|
+
break;
|
|
26
|
+
case 'local':
|
|
27
|
+
timeResult = now.toLocaleString();
|
|
28
|
+
break;
|
|
29
|
+
case 'utc':
|
|
30
|
+
timeResult = now.toUTCString();
|
|
31
|
+
break;
|
|
32
|
+
case 'timestamp':
|
|
33
|
+
timeResult = Math.floor(now.getTime() / 1000).toString();
|
|
34
|
+
break;
|
|
35
|
+
case 'human':
|
|
36
|
+
const options = {
|
|
37
|
+
year: 'numeric',
|
|
38
|
+
month: 'long',
|
|
39
|
+
day: 'numeric',
|
|
40
|
+
hour: '2-digit',
|
|
41
|
+
minute: '2-digit',
|
|
42
|
+
second: '2-digit',
|
|
43
|
+
timeZone: timezone
|
|
44
|
+
};
|
|
45
|
+
timeResult = now.toLocaleString('en-US', options);
|
|
46
|
+
break;
|
|
47
|
+
default:
|
|
48
|
+
timeResult = now.toISOString();
|
|
49
|
+
}
|
|
50
|
+
const currentTimeResult = {
|
|
51
|
+
action: 'get_current_time',
|
|
52
|
+
format,
|
|
53
|
+
timezone: timezone || 'local',
|
|
54
|
+
result: timeResult,
|
|
55
|
+
timestamp: now.getTime(),
|
|
56
|
+
status: 'success'
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: 'text', text: `Time: ${timeResult}\nFormat: ${format}\nTimezone: ${timezone || 'local'}\nTimestamp: ${now.getTime()}` }]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// UI Preview tool - ASCII art visualization before development
|
|
2
|
+
export const previewUiAsciiDefinition = {
|
|
3
|
+
name: 'preview_ui_ascii',
|
|
4
|
+
description: 'UI ๋ง๋ค์ด|ํ์ด์ง ๊ฐ๋ฐ|ํ์ด์ง ๋ง๋ค์ด|์ปดํฌ๋ํธ ์์ฑ|๋ ์ด์์|ํ๋ฉด ๊ตฌ์ฑ|create page|build UI|design component|make page|develop page - Preview UI before coding',
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
page_name: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
description: 'Name of the page or component (e.g., "Login Page", "Dashboard")'
|
|
11
|
+
},
|
|
12
|
+
layout_type: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
enum: ['sidebar', 'header-footer', 'grid', 'centered', 'split', 'custom'],
|
|
15
|
+
description: 'Layout structure type (default: header-footer)'
|
|
16
|
+
},
|
|
17
|
+
components: {
|
|
18
|
+
type: 'array',
|
|
19
|
+
description: 'List of UI components to include',
|
|
20
|
+
items: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
type: { type: 'string', description: 'Component type (header, sidebar, button, input, card, etc.)' },
|
|
24
|
+
label: { type: 'string', description: 'Component label or text' },
|
|
25
|
+
position: { type: 'string', description: 'Position in layout (top, left, center, right, bottom)' }
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
width: {
|
|
30
|
+
type: 'number',
|
|
31
|
+
description: 'Preview width in characters (default: 60)'
|
|
32
|
+
},
|
|
33
|
+
responsive: {
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
description: 'Show mobile view preview (default: false)'
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
required: ['page_name', 'components']
|
|
39
|
+
},
|
|
40
|
+
annotations: {
|
|
41
|
+
title: 'Preview UI (ASCII)',
|
|
42
|
+
audience: ['user', 'assistant']
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
export async function previewUiAscii(args) {
|
|
46
|
+
const { page_name, layout_type = 'header-footer', components, width = 60, responsive = false } = args;
|
|
47
|
+
// ASCII art generation
|
|
48
|
+
const topBorder = 'โ' + 'โ'.repeat(width - 2) + 'โ';
|
|
49
|
+
const bottomBorder = 'โ' + 'โ'.repeat(width - 2) + 'โ';
|
|
50
|
+
const separator = 'โ' + 'โ'.repeat(width - 2) + 'โค';
|
|
51
|
+
const emptyLine = 'โ' + ' '.repeat(width - 2) + 'โ';
|
|
52
|
+
const createLine = (text, align = 'left') => {
|
|
53
|
+
const contentWidth = width - 4;
|
|
54
|
+
let content = text.slice(0, contentWidth);
|
|
55
|
+
if (align === 'center') {
|
|
56
|
+
const padding = Math.floor((contentWidth - content.length) / 2);
|
|
57
|
+
content = ' '.repeat(padding) + content + ' '.repeat(contentWidth - padding - content.length);
|
|
58
|
+
}
|
|
59
|
+
else if (align === 'right') {
|
|
60
|
+
content = ' '.repeat(contentWidth - content.length) + content;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
content = content + ' '.repeat(contentWidth - content.length);
|
|
64
|
+
}
|
|
65
|
+
return 'โ ' + content + ' โ';
|
|
66
|
+
};
|
|
67
|
+
const createBox = (label, w, h) => {
|
|
68
|
+
const lines = [];
|
|
69
|
+
lines.push('โ' + 'โ'.repeat(w - 2) + 'โ');
|
|
70
|
+
for (let i = 0; i < h - 2; i++) {
|
|
71
|
+
if (i === Math.floor((h - 2) / 2)) {
|
|
72
|
+
const padding = Math.floor((w - 4 - label.length) / 2);
|
|
73
|
+
const text = ' '.repeat(padding) + label + ' '.repeat(w - 4 - padding - label.length);
|
|
74
|
+
lines.push('โ ' + text + ' โ');
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
lines.push('โ ' + ' '.repeat(w - 4) + ' โ');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
lines.push('โ' + 'โ'.repeat(w - 2) + 'โ');
|
|
81
|
+
return lines;
|
|
82
|
+
};
|
|
83
|
+
let preview = [];
|
|
84
|
+
// Generate preview based on layout type
|
|
85
|
+
preview.push(topBorder);
|
|
86
|
+
switch (layout_type) {
|
|
87
|
+
case 'header-footer': {
|
|
88
|
+
// Header
|
|
89
|
+
const header = components.find(c => c.type === 'header' || c.position === 'top');
|
|
90
|
+
if (header) {
|
|
91
|
+
preview.push(createLine(header.label || 'Header', 'left'));
|
|
92
|
+
preview.push(separator);
|
|
93
|
+
}
|
|
94
|
+
// Main content
|
|
95
|
+
const mainComponents = components.filter(c => c.type !== 'header' && c.type !== 'footer' && c.position !== 'top' && c.position !== 'bottom');
|
|
96
|
+
preview.push(emptyLine);
|
|
97
|
+
mainComponents.forEach(comp => {
|
|
98
|
+
const label = comp.label || comp.type.toUpperCase();
|
|
99
|
+
if (comp.type === 'button') {
|
|
100
|
+
preview.push(createLine(` [${label}]`, 'center'));
|
|
101
|
+
}
|
|
102
|
+
else if (comp.type === 'input') {
|
|
103
|
+
preview.push(createLine(` ${label}: [____________]`, 'left'));
|
|
104
|
+
}
|
|
105
|
+
else if (comp.type === 'card') {
|
|
106
|
+
preview.push(createLine(` โโ ${label} โโโโโโโโโโโโโโ`, 'left'));
|
|
107
|
+
preview.push(createLine(` โ Content here... โ`, 'left'));
|
|
108
|
+
preview.push(createLine(` โโโโโโโโโโโโโโโโโโโโโโโโ`, 'left'));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
preview.push(createLine(` ${label}`, 'left'));
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
preview.push(emptyLine);
|
|
115
|
+
// Footer
|
|
116
|
+
const footer = components.find(c => c.type === 'footer' || c.position === 'bottom');
|
|
117
|
+
if (footer) {
|
|
118
|
+
preview.push(separator);
|
|
119
|
+
preview.push(createLine(footer.label || 'Footer', 'center'));
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case 'sidebar': {
|
|
124
|
+
// Header
|
|
125
|
+
const header = components.find(c => c.type === 'header' || c.position === 'top');
|
|
126
|
+
if (header) {
|
|
127
|
+
preview.push(createLine(header.label || 'Header', 'left'));
|
|
128
|
+
preview.push(separator);
|
|
129
|
+
}
|
|
130
|
+
// Sidebar + Content
|
|
131
|
+
const sidebarWidth = Math.floor(width * 0.25);
|
|
132
|
+
const contentWidth = width - sidebarWidth - 5;
|
|
133
|
+
const sidebar = components.find(c => c.type === 'sidebar' || c.position === 'left');
|
|
134
|
+
const mainComponents = components.filter(c => c.type !== 'header' && c.type !== 'footer' && c.type !== 'sidebar' &&
|
|
135
|
+
c.position !== 'top' && c.position !== 'bottom' && c.position !== 'left');
|
|
136
|
+
preview.push('โ โ' + 'โ'.repeat(sidebarWidth - 2) + 'โ' + ' '.repeat(contentWidth - sidebarWidth + 3) + 'โ');
|
|
137
|
+
preview.push('โ โ' + (sidebar?.label || 'Nav').padEnd(sidebarWidth - 2) + 'โ Content Area' + ' '.repeat(contentWidth - 15) + 'โ');
|
|
138
|
+
const navItems = mainComponents.slice(0, 3);
|
|
139
|
+
navItems.forEach((item, idx) => {
|
|
140
|
+
const navLabel = `${item.label || item.type}`.slice(0, sidebarWidth - 4);
|
|
141
|
+
const contentLabel = idx === 0 ? `โโ ${mainComponents[0]?.label || 'Main'} โโ` : '';
|
|
142
|
+
preview.push('โ โ ' + navLabel.padEnd(sidebarWidth - 3) + 'โ ' + contentLabel.padEnd(contentWidth - 3) + 'โ');
|
|
143
|
+
});
|
|
144
|
+
preview.push('โ โ' + 'โ'.repeat(sidebarWidth - 2) + 'โ' + ' '.repeat(contentWidth - sidebarWidth + 3) + 'โ');
|
|
145
|
+
// Footer
|
|
146
|
+
const footer = components.find(c => c.type === 'footer' || c.position === 'bottom');
|
|
147
|
+
if (footer) {
|
|
148
|
+
preview.push(separator);
|
|
149
|
+
preview.push(createLine(footer.label || 'Footer', 'center'));
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case 'grid': {
|
|
154
|
+
preview.push(createLine('Grid Layout', 'center'));
|
|
155
|
+
preview.push(separator);
|
|
156
|
+
const gridComponents = components.filter(c => c.type !== 'header' && c.type !== 'footer');
|
|
157
|
+
const cols = Math.ceil(Math.sqrt(gridComponents.length));
|
|
158
|
+
const cellWidth = Math.floor((width - 4) / cols) - 2;
|
|
159
|
+
for (let i = 0; i < gridComponents.length; i += cols) {
|
|
160
|
+
const row = gridComponents.slice(i, i + cols);
|
|
161
|
+
preview.push('โ ' + row.map(c => {
|
|
162
|
+
const label = (c.label || c.type).slice(0, cellWidth - 2);
|
|
163
|
+
return 'โ' + label.padEnd(cellWidth - 2, 'โ') + 'โ';
|
|
164
|
+
}).join(' ') + ' '.repeat(width - 4 - row.length * (cellWidth + 1)) + ' โ');
|
|
165
|
+
preview.push('โ ' + row.map(c => 'โ' + ' '.repeat(cellWidth - 2) + 'โ').join(' ') + ' '.repeat(width - 4 - row.length * (cellWidth + 1)) + ' โ');
|
|
166
|
+
preview.push('โ ' + row.map(c => 'โ' + 'โ'.repeat(cellWidth - 2) + 'โ').join(' ') + ' '.repeat(width - 4 - row.length * (cellWidth + 1)) + ' โ');
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 'centered': {
|
|
171
|
+
const main = components[0];
|
|
172
|
+
preview.push(emptyLine);
|
|
173
|
+
preview.push(emptyLine);
|
|
174
|
+
preview.push(createLine(main?.label || 'Main Content', 'center'));
|
|
175
|
+
components.slice(1).forEach(comp => {
|
|
176
|
+
if (comp.type === 'button') {
|
|
177
|
+
preview.push(createLine(`[${comp.label || 'Button'}]`, 'center'));
|
|
178
|
+
}
|
|
179
|
+
else if (comp.type === 'input') {
|
|
180
|
+
preview.push(createLine(`${comp.label || 'Input'}: [____________]`, 'center'));
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
preview.push(emptyLine);
|
|
184
|
+
preview.push(emptyLine);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case 'split': {
|
|
188
|
+
const leftWidth = Math.floor((width - 5) / 2);
|
|
189
|
+
const rightWidth = width - leftWidth - 5;
|
|
190
|
+
preview.push('โ โ' + 'โ'.repeat(leftWidth) + 'โ โ' + 'โ'.repeat(rightWidth) + 'โ โ');
|
|
191
|
+
const left = components.find(c => c.position === 'left') || components[0];
|
|
192
|
+
const right = components.find(c => c.position === 'right') || components[1];
|
|
193
|
+
preview.push('โ โ' + (left?.label || 'Left').padEnd(leftWidth) + 'โ โ' + (right?.label || 'Right').padEnd(rightWidth) + 'โ โ');
|
|
194
|
+
for (let i = 0; i < 5; i++) {
|
|
195
|
+
preview.push('โ โ' + ' '.repeat(leftWidth) + 'โ โ' + ' '.repeat(rightWidth) + 'โ โ');
|
|
196
|
+
}
|
|
197
|
+
preview.push('โ โ' + 'โ'.repeat(leftWidth) + 'โ โ' + 'โ'.repeat(rightWidth) + 'โ โ');
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
preview.push(bottomBorder);
|
|
202
|
+
// Mobile view if responsive
|
|
203
|
+
let mobilePreview = '';
|
|
204
|
+
if (responsive) {
|
|
205
|
+
const mobileWidth = 30;
|
|
206
|
+
const mobileTop = 'โ' + 'โ'.repeat(mobileWidth - 2) + 'โ';
|
|
207
|
+
const mobileBottom = 'โ' + 'โ'.repeat(mobileWidth - 2) + 'โ';
|
|
208
|
+
mobilePreview = '\n\n๐ฑ Mobile View:\n';
|
|
209
|
+
mobilePreview += mobileTop + '\n';
|
|
210
|
+
components.forEach(comp => {
|
|
211
|
+
const label = (comp.label || comp.type).slice(0, mobileWidth - 4);
|
|
212
|
+
mobilePreview += 'โ ' + label.padEnd(mobileWidth - 4) + ' โ\n';
|
|
213
|
+
});
|
|
214
|
+
mobilePreview += mobileBottom;
|
|
215
|
+
}
|
|
216
|
+
const result = {
|
|
217
|
+
page_name,
|
|
218
|
+
layout_type,
|
|
219
|
+
ascii_preview: preview.join('\n'),
|
|
220
|
+
mobile_preview: responsive ? mobilePreview : null,
|
|
221
|
+
components_count: components.length,
|
|
222
|
+
message: 'โ
์ด ๋ ์ด์์์ผ๋ก ์งํํ์๊ฒ ์ต๋๊น? ์น์ธํ์๋ฉด ์ฝ๋ ์์ฑ์ ์์ํฉ๋๋ค.',
|
|
223
|
+
action: 'preview_ui_ascii',
|
|
224
|
+
status: 'awaiting_confirmation'
|
|
225
|
+
};
|
|
226
|
+
return {
|
|
227
|
+
content: [{
|
|
228
|
+
type: 'text',
|
|
229
|
+
text: `๐จ UI Preview: ${page_name}\n\n${preview.join('\n')}${mobilePreview}\n\n${result.message}`
|
|
230
|
+
}]
|
|
231
|
+
};
|
|
232
|
+
}
|
package/mcp/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vide-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for vide framework (based on hi-ai)",
|
|
5
|
+
"private": true,
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "./src/index.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "npm run build && node dist/index.js",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest",
|
|
14
|
+
"test:ui": "vitest --ui",
|
|
15
|
+
"test:coverage": "vitest run --coverage"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"ai",
|
|
21
|
+
"assistant",
|
|
22
|
+
"development-tools",
|
|
23
|
+
"code-analysis",
|
|
24
|
+
"typescript",
|
|
25
|
+
"javascript",
|
|
26
|
+
"python",
|
|
27
|
+
"semantic-analysis",
|
|
28
|
+
"code-quality",
|
|
29
|
+
"memory-management",
|
|
30
|
+
"project-planning",
|
|
31
|
+
"claude",
|
|
32
|
+
"anthropic"
|
|
33
|
+
],
|
|
34
|
+
"author": "Su",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
38
|
+
"@smithery/sdk": "^1.6.8",
|
|
39
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
40
|
+
"@types/glob": "^8.1.0",
|
|
41
|
+
"better-sqlite3": "^12.4.1",
|
|
42
|
+
"chalk": "^5.3.0",
|
|
43
|
+
"glob": "^11.0.3",
|
|
44
|
+
"puppeteer-core": "^22.15.0",
|
|
45
|
+
"ts-morph": "^26.0.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.0.0",
|
|
49
|
+
"@vitest/ui": "^4.0.9",
|
|
50
|
+
"typescript": "^5.5.4",
|
|
51
|
+
"vitest": "^4.0.9"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@su-record/vibe",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vibe - SPEC-driven AI coding framework with MCP integration",
|
|
5
|
+
"main": "cli/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vibe": "./bin/vibe"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "cd mcp && npm install && npm run build",
|
|
11
|
+
"postinstall": "node scripts/install-mcp.js",
|
|
12
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"ai",
|
|
16
|
+
"coding",
|
|
17
|
+
"spec-driven",
|
|
18
|
+
"claude",
|
|
19
|
+
"mcp",
|
|
20
|
+
"framework",
|
|
21
|
+
"spec-kit",
|
|
22
|
+
"requirements",
|
|
23
|
+
"user-story"
|
|
24
|
+
],
|
|
25
|
+
"author": "grove",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/su-record/vibe.git"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/su-record/vibe#readme",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {},
|
|
36
|
+
"devDependencies": {},
|
|
37
|
+
"files": [
|
|
38
|
+
"bin/",
|
|
39
|
+
"cli/",
|
|
40
|
+
"agents/",
|
|
41
|
+
"skills/",
|
|
42
|
+
"templates/",
|
|
43
|
+
"scripts/",
|
|
44
|
+
"mcp/dist/",
|
|
45
|
+
"mcp/package.json",
|
|
46
|
+
"README.md",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
console.log('๐ง vibe MCP ์๋ฒ ์ค์น ์ค...\n');
|
|
8
|
+
|
|
9
|
+
// MCP ์๋ฒ ๊ฒฝ๋ก (npm ๊ธ๋ก๋ฒ ์ค์น ์์น)
|
|
10
|
+
const mcpIndexPath = path.join(__dirname, '../mcp/dist/index.js');
|
|
11
|
+
|
|
12
|
+
// ์๋ ๊ฒฝ๋ก๋ก ํ์ธ (๋ก์ปฌ ๊ฐ๋ฐ ์)
|
|
13
|
+
if (!fs.existsSync(mcpIndexPath)) {
|
|
14
|
+
console.log('โ ๏ธ MCP ์๋ฒ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.');
|
|
15
|
+
console.log(' ๊ฒฝ๋ก:', mcpIndexPath);
|
|
16
|
+
console.log(' npm install์ ๋ค์ ์คํํด์ฃผ์ธ์.\n');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log('๐ MCP ์๋ฒ ๊ฒฝ๋ก:', mcpIndexPath);
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Claude Code์ MCP ์๋ฒ ๋ฑ๋ก
|
|
24
|
+
const command = `claude mcp add vibe node "${mcpIndexPath}"`;
|
|
25
|
+
console.log('๐ ์คํ:', command);
|
|
26
|
+
console.log('');
|
|
27
|
+
|
|
28
|
+
execSync(command, { stdio: 'inherit' });
|
|
29
|
+
|
|
30
|
+
console.log('\nโ
vibe MCP ์๋ฒ ๋ฑ๋ก ์๋ฃ!');
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log('์ฌ์ฉ ๊ฐ๋ฅํ ๋๊ตฌ:');
|
|
33
|
+
console.log(' - 38๊ฐ MCP ๋๊ตฌ (@su-record/hi-ai ๊ธฐ๋ฐ)');
|
|
34
|
+
console.log(' - ์ฝ๋ ๋ถ์, ํ์ง ๊ฒ์ฆ, UI ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ฑ');
|
|
35
|
+
console.log('');
|
|
36
|
+
console.log('๋ค์ ๋ช
๋ น์ด๋ก ํ์ธ:');
|
|
37
|
+
console.log(' claude mcp list');
|
|
38
|
+
console.log('');
|
|
39
|
+
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error('โ MCP ์๋ฒ ๋ฑ๋ก ์คํจ');
|
|
42
|
+
console.error('');
|
|
43
|
+
console.error('์๋ ๋ฑ๋ก ๋ฐฉ๋ฒ:');
|
|
44
|
+
console.error(` claude mcp add vibe node "${mcpIndexPath}"`);
|
|
45
|
+
console.error('');
|
|
46
|
+
console.error('์๋ฌ:', error.message);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|