create-byan-agent 2.7.0 → 2.7.2
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 +151 -207
- package/bin/create-byan-agent-v2.js +1 -1
- package/lib/domain-questions.js +235 -0
- package/lib/errors.js +61 -0
- package/lib/exit-codes.js +54 -0
- package/lib/phase2-chat.js +534 -0
- package/lib/platforms/claude-code.js +196 -0
- package/lib/platforms/codex.js +92 -0
- package/lib/platforms/copilot-cli.js +123 -0
- package/lib/platforms/index.js +14 -0
- package/lib/platforms/vscode.js +51 -0
- package/lib/project-agents-generator.js +238 -0
- package/lib/utils/config-loader.js +79 -0
- package/lib/utils/file-utils.js +104 -0
- package/lib/utils/git-detector.js +35 -0
- package/lib/utils/logger.js +64 -0
- package/lib/utils/node-detector.js +58 -0
- package/lib/utils/os-detector.js +74 -0
- package/lib/utils/yaml-utils.js +87 -0
- package/lib/yanstaller/agent-launcher.js +348 -0
- package/lib/yanstaller/backuper.js +108 -0
- package/lib/yanstaller/detector.js +141 -0
- package/lib/yanstaller/index.js +139 -0
- package/lib/yanstaller/installer.js +140 -0
- package/lib/yanstaller/interviewer.js +88 -0
- package/lib/yanstaller/platform-selector.js +328 -0
- package/lib/yanstaller/recommender.js +102 -0
- package/lib/yanstaller/troubleshooter.js +89 -0
- package/lib/yanstaller/validator.js +198 -0
- package/lib/yanstaller/wizard.js +109 -0
- package/package.json +3 -1
- package/setup-turbo-whisper.js +687 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate project-agents.md from Phase 2 analysis results
|
|
3
|
+
* Creates a structured document describing the agent ecosystem for the project
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generate the project-agents.md file
|
|
11
|
+
* @param {Object} phase2Results - JSON from yanstaller-phase2 agent
|
|
12
|
+
* @param {Object} genericAnswers - Q1-Q10 answers
|
|
13
|
+
* @param {Object} domainAnswers - Domain-specific answers
|
|
14
|
+
* @param {string} outputDir - Directory to write the file
|
|
15
|
+
* @returns {string} Path to generated file
|
|
16
|
+
*/
|
|
17
|
+
async function generateProjectAgentsDoc(phase2Results, genericAnswers, domainAnswers, outputDir) {
|
|
18
|
+
const lines = [];
|
|
19
|
+
|
|
20
|
+
// Header
|
|
21
|
+
lines.push('# Project Agents Configuration');
|
|
22
|
+
lines.push('');
|
|
23
|
+
lines.push('> Generated by BYAN Yanstaller - Intelligent Interview');
|
|
24
|
+
lines.push(`> Date: ${new Date().toISOString().split('T')[0]}`);
|
|
25
|
+
lines.push('');
|
|
26
|
+
|
|
27
|
+
// Project Overview
|
|
28
|
+
lines.push('## Project Overview');
|
|
29
|
+
lines.push('');
|
|
30
|
+
lines.push(`- **Type**: ${genericAnswers.projectType}`);
|
|
31
|
+
lines.push(`- **Domain**: ${genericAnswers.domain}`);
|
|
32
|
+
lines.push(`- **Team Size**: ${genericAnswers.teamSize}`);
|
|
33
|
+
lines.push(`- **Methodology**: ${genericAnswers.methodology}`);
|
|
34
|
+
lines.push(`- **Quality Level**: ${genericAnswers.quality}`);
|
|
35
|
+
lines.push('');
|
|
36
|
+
|
|
37
|
+
// Domain Details
|
|
38
|
+
if (Object.keys(domainAnswers).length > 0) {
|
|
39
|
+
lines.push('### Tech Stack');
|
|
40
|
+
lines.push('');
|
|
41
|
+
for (const [key, value] of Object.entries(domainAnswers)) {
|
|
42
|
+
lines.push(`- **${formatKey(key)}**: ${value}`);
|
|
43
|
+
}
|
|
44
|
+
lines.push('');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Core Agents
|
|
48
|
+
if (phase2Results.coreAgents && phase2Results.coreAgents.length > 0) {
|
|
49
|
+
lines.push('## Core Agents');
|
|
50
|
+
lines.push('');
|
|
51
|
+
lines.push('These agents are essential for your project workflow.');
|
|
52
|
+
lines.push('');
|
|
53
|
+
|
|
54
|
+
for (const agent of phase2Results.coreAgents) {
|
|
55
|
+
lines.push(`### ${formatAgentName(agent.name)}`);
|
|
56
|
+
lines.push('');
|
|
57
|
+
lines.push(`**Role**: ${agent.role}`);
|
|
58
|
+
lines.push('');
|
|
59
|
+
lines.push(`**Expertise**: ${agent.expertise.join(', ')}`);
|
|
60
|
+
lines.push('');
|
|
61
|
+
lines.push(`**Complexity**: ${getComplexityBadge(agent.complexity)}`);
|
|
62
|
+
lines.push('');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Optional Agents
|
|
67
|
+
if (phase2Results.optionalAgents && phase2Results.optionalAgents.length > 0) {
|
|
68
|
+
lines.push('## Optional Agents');
|
|
69
|
+
lines.push('');
|
|
70
|
+
lines.push('Activate these agents when specific conditions are met.');
|
|
71
|
+
lines.push('');
|
|
72
|
+
|
|
73
|
+
for (const agent of phase2Results.optionalAgents) {
|
|
74
|
+
lines.push(`### ${formatAgentName(agent.name)}`);
|
|
75
|
+
lines.push('');
|
|
76
|
+
lines.push(`**Role**: ${agent.role}`);
|
|
77
|
+
lines.push('');
|
|
78
|
+
lines.push(`**When to use**: ${agent.when || 'As needed'}`);
|
|
79
|
+
lines.push('');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Agent Relationships
|
|
84
|
+
if (phase2Results.agentRelationships && phase2Results.agentRelationships.length > 0) {
|
|
85
|
+
lines.push('## Agent Relationships');
|
|
86
|
+
lines.push('');
|
|
87
|
+
lines.push('```mermaid');
|
|
88
|
+
lines.push('graph LR');
|
|
89
|
+
|
|
90
|
+
for (const rel of phase2Results.agentRelationships) {
|
|
91
|
+
const arrow = getRelationshipArrow(rel.type);
|
|
92
|
+
lines.push(` ${rel.from}${arrow}${rel.to}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
lines.push('```');
|
|
96
|
+
lines.push('');
|
|
97
|
+
lines.push('### Relationship Details');
|
|
98
|
+
lines.push('');
|
|
99
|
+
lines.push('| From | To | Type | Description |');
|
|
100
|
+
lines.push('|------|-----|------|-------------|');
|
|
101
|
+
|
|
102
|
+
for (const rel of phase2Results.agentRelationships) {
|
|
103
|
+
lines.push(`| ${rel.from} | ${rel.to} | ${rel.type} | ${rel.description || '-'} |`);
|
|
104
|
+
}
|
|
105
|
+
lines.push('');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Project Structure
|
|
109
|
+
if (phase2Results.projectStructure) {
|
|
110
|
+
lines.push('## Project Structure');
|
|
111
|
+
lines.push('');
|
|
112
|
+
lines.push(`**Architecture**: ${phase2Results.projectStructure.type}`);
|
|
113
|
+
lines.push('');
|
|
114
|
+
|
|
115
|
+
if (phase2Results.projectStructure.folders) {
|
|
116
|
+
lines.push('### Recommended Folders');
|
|
117
|
+
lines.push('');
|
|
118
|
+
lines.push('```');
|
|
119
|
+
for (const folder of phase2Results.projectStructure.folders) {
|
|
120
|
+
lines.push(folder);
|
|
121
|
+
}
|
|
122
|
+
lines.push('```');
|
|
123
|
+
lines.push('');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (phase2Results.projectStructure.keyFiles) {
|
|
127
|
+
lines.push('### Key Files');
|
|
128
|
+
lines.push('');
|
|
129
|
+
for (const file of phase2Results.projectStructure.keyFiles) {
|
|
130
|
+
lines.push(`- \`${file}\``);
|
|
131
|
+
}
|
|
132
|
+
lines.push('');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Custom Agents to Create
|
|
137
|
+
if (phase2Results.customAgentsToCreate && phase2Results.customAgentsToCreate.length > 0) {
|
|
138
|
+
lines.push('## Custom Agents to Create');
|
|
139
|
+
lines.push('');
|
|
140
|
+
lines.push('These specialized agents will be generated for your project.');
|
|
141
|
+
lines.push('');
|
|
142
|
+
|
|
143
|
+
for (const agent of phase2Results.customAgentsToCreate) {
|
|
144
|
+
lines.push(`### ${formatAgentName(agent.name)}`);
|
|
145
|
+
lines.push('');
|
|
146
|
+
lines.push(`**Based on**: ${agent.template} template`);
|
|
147
|
+
lines.push('');
|
|
148
|
+
lines.push(`**Focus**: ${agent.focus}`);
|
|
149
|
+
lines.push('');
|
|
150
|
+
if (agent.mantras && agent.mantras.length > 0) {
|
|
151
|
+
lines.push('**Key Mantras**:');
|
|
152
|
+
for (const mantra of agent.mantras) {
|
|
153
|
+
lines.push(`- ${mantra}`);
|
|
154
|
+
}
|
|
155
|
+
lines.push('');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Configuration
|
|
161
|
+
lines.push('## Configuration');
|
|
162
|
+
lines.push('');
|
|
163
|
+
lines.push(`**Recommended Model**: \`${phase2Results.recommendedModel || 'gpt-5-mini'}\``);
|
|
164
|
+
lines.push('');
|
|
165
|
+
if (phase2Results.rationale) {
|
|
166
|
+
lines.push('### Rationale');
|
|
167
|
+
lines.push('');
|
|
168
|
+
lines.push(phase2Results.rationale);
|
|
169
|
+
lines.push('');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Usage Instructions
|
|
173
|
+
lines.push('## Usage');
|
|
174
|
+
lines.push('');
|
|
175
|
+
lines.push('### Activate Core Agents');
|
|
176
|
+
lines.push('');
|
|
177
|
+
lines.push('```bash');
|
|
178
|
+
lines.push('copilot');
|
|
179
|
+
lines.push('# Then type: /agent');
|
|
180
|
+
lines.push('# Select the agent you need');
|
|
181
|
+
lines.push('```');
|
|
182
|
+
lines.push('');
|
|
183
|
+
lines.push('### Create Custom Agents');
|
|
184
|
+
lines.push('');
|
|
185
|
+
lines.push('```bash');
|
|
186
|
+
lines.push('copilot --agent=byan');
|
|
187
|
+
lines.push('# Select: 1. Create New Agent');
|
|
188
|
+
lines.push('# Follow the interview process');
|
|
189
|
+
lines.push('```');
|
|
190
|
+
lines.push('');
|
|
191
|
+
|
|
192
|
+
// Write file
|
|
193
|
+
const content = lines.join('\n');
|
|
194
|
+
const filePath = path.join(outputDir, 'project-agents.md');
|
|
195
|
+
await fs.ensureDir(outputDir);
|
|
196
|
+
await fs.writeFile(filePath, content, 'utf8');
|
|
197
|
+
|
|
198
|
+
return filePath;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Helper functions
|
|
202
|
+
|
|
203
|
+
function formatKey(key) {
|
|
204
|
+
return key
|
|
205
|
+
.replace(/([A-Z])/g, ' $1')
|
|
206
|
+
.replace(/^./, str => str.toUpperCase())
|
|
207
|
+
.trim();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function formatAgentName(name) {
|
|
211
|
+
return name
|
|
212
|
+
.split('-')
|
|
213
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
214
|
+
.join(' ');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function getComplexityBadge(complexity) {
|
|
218
|
+
switch (complexity) {
|
|
219
|
+
case 'simple': return '🟢 Simple (1-2 mantras)';
|
|
220
|
+
case 'medium': return '🟡 Medium (3-5 mantras)';
|
|
221
|
+
case 'complex': return '🔴 Complex (6+ mantras)';
|
|
222
|
+
default: return '⚪ Unknown';
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function getRelationshipArrow(type) {
|
|
227
|
+
switch (type) {
|
|
228
|
+
case 'triggers': return ' -->|triggers| ';
|
|
229
|
+
case 'blocks': return ' -.->|blocks| ';
|
|
230
|
+
case 'informs': return ' -->|informs| ';
|
|
231
|
+
case 'depends': return ' ==>|depends| ';
|
|
232
|
+
default: return ' --> ';
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = {
|
|
237
|
+
generateProjectAgentsDoc
|
|
238
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Loader Utility
|
|
3
|
+
*
|
|
4
|
+
* Loads configuration files with variable resolution.
|
|
5
|
+
*
|
|
6
|
+
* @module utils/config-loader
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const yamlUtils = require('./yaml-utils');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Load config file and resolve variables
|
|
14
|
+
*
|
|
15
|
+
* @param {string} configPath - Path to config.yaml
|
|
16
|
+
* @param {Object} context - Context for variable resolution
|
|
17
|
+
* @returns {Promise<Object>}
|
|
18
|
+
*/
|
|
19
|
+
async function loadConfig(configPath, context = {}) {
|
|
20
|
+
const config = await yamlUtils.readYAML(configPath);
|
|
21
|
+
return resolveVariables(config, context);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolve variables in config object
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} config - Config object
|
|
28
|
+
* @param {Object} context - Context for variable resolution
|
|
29
|
+
* @returns {Object}
|
|
30
|
+
*/
|
|
31
|
+
function resolveVariables(config, context) {
|
|
32
|
+
const resolved = {};
|
|
33
|
+
|
|
34
|
+
for (const [key, value] of Object.entries(config)) {
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
resolved[key] = resolveVariable(value, context);
|
|
37
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
38
|
+
resolved[key] = resolveVariables(value, context);
|
|
39
|
+
} else {
|
|
40
|
+
resolved[key] = value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return resolved;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Resolve single variable string
|
|
49
|
+
*
|
|
50
|
+
* @param {string} value - Value with potential variables
|
|
51
|
+
* @param {Object} context - Context for variable resolution
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
function resolveVariable(value, context) {
|
|
55
|
+
let resolved = value;
|
|
56
|
+
|
|
57
|
+
// Replace {project-root}
|
|
58
|
+
if (context.projectRoot) {
|
|
59
|
+
resolved = resolved.replace(/\{project-root\}/g, context.projectRoot);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Replace {output_folder}
|
|
63
|
+
if (context.outputFolder) {
|
|
64
|
+
resolved = resolved.replace(/\{output_folder\}/g, context.outputFolder);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Replace {user_name}
|
|
68
|
+
if (context.userName) {
|
|
69
|
+
resolved = resolved.replace(/\{user_name\}/g, context.userName);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return resolved;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
loadConfig,
|
|
77
|
+
resolveVariables,
|
|
78
|
+
resolveVariable
|
|
79
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Utilities
|
|
3
|
+
*
|
|
4
|
+
* Wrapper around fs-extra for common file operations.
|
|
5
|
+
*
|
|
6
|
+
* @module utils/file-utils
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs-extra');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Copy file or directory
|
|
14
|
+
*
|
|
15
|
+
* @param {string} src - Source path
|
|
16
|
+
* @param {string} dest - Destination path
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
*/
|
|
19
|
+
async function copy(src, dest) {
|
|
20
|
+
await fs.copy(src, dest);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if path exists
|
|
25
|
+
*
|
|
26
|
+
* @param {string} filePath - File or directory path
|
|
27
|
+
* @returns {Promise<boolean>}
|
|
28
|
+
*/
|
|
29
|
+
async function exists(filePath) {
|
|
30
|
+
return fs.pathExists(filePath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Ensure directory exists (create if not)
|
|
35
|
+
*
|
|
36
|
+
* @param {string} dirPath - Directory path
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
*/
|
|
39
|
+
async function ensureDir(dirPath) {
|
|
40
|
+
await fs.ensureDir(dirPath);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Remove file or directory
|
|
45
|
+
*
|
|
46
|
+
* @param {string} filePath - File or directory path
|
|
47
|
+
* @returns {Promise<void>}
|
|
48
|
+
*/
|
|
49
|
+
async function remove(filePath) {
|
|
50
|
+
await fs.remove(filePath);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Read JSON file
|
|
55
|
+
*
|
|
56
|
+
* @param {string} filePath - JSON file path
|
|
57
|
+
* @returns {Promise<Object>}
|
|
58
|
+
*/
|
|
59
|
+
async function readJSON(filePath) {
|
|
60
|
+
return fs.readJSON(filePath);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Write JSON file
|
|
65
|
+
*
|
|
66
|
+
* @param {string} filePath - JSON file path
|
|
67
|
+
* @param {Object} data - Data to write
|
|
68
|
+
* @returns {Promise<void>}
|
|
69
|
+
*/
|
|
70
|
+
async function writeJSON(filePath, data) {
|
|
71
|
+
await fs.writeJSON(filePath, data, { spaces: 2 });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Read text file
|
|
76
|
+
*
|
|
77
|
+
* @param {string} filePath - Text file path
|
|
78
|
+
* @returns {Promise<string>}
|
|
79
|
+
*/
|
|
80
|
+
async function readFile(filePath) {
|
|
81
|
+
return fs.readFile(filePath, 'utf8');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Write text file
|
|
86
|
+
*
|
|
87
|
+
* @param {string} filePath - Text file path
|
|
88
|
+
* @param {string} content - Content to write
|
|
89
|
+
* @returns {Promise<void>}
|
|
90
|
+
*/
|
|
91
|
+
async function writeFile(filePath, content) {
|
|
92
|
+
await fs.writeFile(filePath, content, 'utf8');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
copy,
|
|
97
|
+
exists,
|
|
98
|
+
ensureDir,
|
|
99
|
+
remove,
|
|
100
|
+
readJSON,
|
|
101
|
+
writeJSON,
|
|
102
|
+
readFile,
|
|
103
|
+
writeFile
|
|
104
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Detector Utility
|
|
3
|
+
*
|
|
4
|
+
* Detects if Git is installed.
|
|
5
|
+
*
|
|
6
|
+
* @module utils/git-detector
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { execSync } = require('child_process');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Detect if Git is installed
|
|
13
|
+
*
|
|
14
|
+
* @returns {Promise<{installed: boolean, version: string | null}>}
|
|
15
|
+
*/
|
|
16
|
+
async function detect() {
|
|
17
|
+
try {
|
|
18
|
+
const version = execSync('git --version', { encoding: 'utf8' }).trim();
|
|
19
|
+
const versionMatch = version.match(/git version ([\d.]+)/);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
installed: true,
|
|
23
|
+
version: versionMatch ? versionMatch[1] : null
|
|
24
|
+
};
|
|
25
|
+
} catch {
|
|
26
|
+
return {
|
|
27
|
+
installed: false,
|
|
28
|
+
version: null
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
detect
|
|
35
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility
|
|
3
|
+
*
|
|
4
|
+
* Wrapper around chalk and console for colored logging.
|
|
5
|
+
*
|
|
6
|
+
* @module utils/logger
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Log info message
|
|
13
|
+
*
|
|
14
|
+
* @param {string} message - Message to log
|
|
15
|
+
*/
|
|
16
|
+
function info(message) {
|
|
17
|
+
console.log(chalk.blue('ℹ'), message);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Log success message
|
|
22
|
+
*
|
|
23
|
+
* @param {string} message - Message to log
|
|
24
|
+
*/
|
|
25
|
+
function success(message) {
|
|
26
|
+
console.log(chalk.green('✓'), message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Log warning message
|
|
31
|
+
*
|
|
32
|
+
* @param {string} message - Message to log
|
|
33
|
+
*/
|
|
34
|
+
function warn(message) {
|
|
35
|
+
console.log(chalk.yellow('⚠'), message);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Log error message
|
|
40
|
+
*
|
|
41
|
+
* @param {string} message - Message to log
|
|
42
|
+
*/
|
|
43
|
+
function error(message) {
|
|
44
|
+
console.error(chalk.red('✖'), message);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Log debug message (only if DEBUG env var set)
|
|
49
|
+
*
|
|
50
|
+
* @param {string} message - Message to log
|
|
51
|
+
*/
|
|
52
|
+
function debug(message) {
|
|
53
|
+
if (process.env.DEBUG) {
|
|
54
|
+
console.log(chalk.gray('[DEBUG]'), message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
info,
|
|
60
|
+
success,
|
|
61
|
+
warn,
|
|
62
|
+
error,
|
|
63
|
+
debug
|
|
64
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Detector Utility
|
|
3
|
+
*
|
|
4
|
+
* Detects Node.js version.
|
|
5
|
+
*
|
|
6
|
+
* @module utils/node-detector
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Detect Node.js version
|
|
11
|
+
*
|
|
12
|
+
* @returns {string} - Version string (e.g., '18.19.0')
|
|
13
|
+
*/
|
|
14
|
+
function detect() {
|
|
15
|
+
return process.version.slice(1); // Remove 'v' prefix
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Compare two semver versions
|
|
20
|
+
*
|
|
21
|
+
* Strips version suffixes (-beta, -rc1, etc.) before comparison.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} version1 - First version
|
|
24
|
+
* @param {string} version2 - Second version
|
|
25
|
+
* @returns {number} - -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
26
|
+
*/
|
|
27
|
+
function compareVersions(version1, version2) {
|
|
28
|
+
// Strip suffixes: '18.0.0-beta' → '18.0.0'
|
|
29
|
+
const cleanV1 = version1.replace(/-.*$/, '');
|
|
30
|
+
const cleanV2 = version2.replace(/-.*$/, '');
|
|
31
|
+
|
|
32
|
+
const v1Parts = cleanV1.split('.').map(Number);
|
|
33
|
+
const v2Parts = cleanV2.split('.').map(Number);
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < 3; i++) {
|
|
36
|
+
if (v1Parts[i] > v2Parts[i]) return 1;
|
|
37
|
+
if (v1Parts[i] < v2Parts[i]) return -1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if Node version meets minimum requirement
|
|
45
|
+
*
|
|
46
|
+
* @param {string} currentVersion - Current Node version
|
|
47
|
+
* @param {string} requiredVersion - Required Node version
|
|
48
|
+
* @returns {boolean}
|
|
49
|
+
*/
|
|
50
|
+
function meetsRequirement(currentVersion, requiredVersion) {
|
|
51
|
+
return compareVersions(currentVersion, requiredVersion) >= 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
detect,
|
|
56
|
+
compareVersions,
|
|
57
|
+
meetsRequirement
|
|
58
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS Detector Utility
|
|
3
|
+
*
|
|
4
|
+
* Detects operating system and version.
|
|
5
|
+
*
|
|
6
|
+
* @module utils/os-detector
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const os = require('os');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Detect operating system
|
|
13
|
+
*
|
|
14
|
+
* @returns {{name: string, version: string, platform: string}}
|
|
15
|
+
*/
|
|
16
|
+
function detect() {
|
|
17
|
+
const platform = os.platform();
|
|
18
|
+
const release = os.release();
|
|
19
|
+
|
|
20
|
+
let name;
|
|
21
|
+
switch (platform) {
|
|
22
|
+
case 'win32':
|
|
23
|
+
name = 'windows';
|
|
24
|
+
break;
|
|
25
|
+
case 'darwin':
|
|
26
|
+
name = 'macos';
|
|
27
|
+
break;
|
|
28
|
+
case 'linux':
|
|
29
|
+
name = 'linux';
|
|
30
|
+
break;
|
|
31
|
+
default:
|
|
32
|
+
name = 'unknown';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
name,
|
|
37
|
+
version: release,
|
|
38
|
+
platform
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if running on Windows
|
|
44
|
+
*
|
|
45
|
+
* @returns {boolean}
|
|
46
|
+
*/
|
|
47
|
+
function isWindows() {
|
|
48
|
+
return os.platform() === 'win32';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Check if running on macOS
|
|
53
|
+
*
|
|
54
|
+
* @returns {boolean}
|
|
55
|
+
*/
|
|
56
|
+
function isMacOS() {
|
|
57
|
+
return os.platform() === 'darwin';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check if running on Linux
|
|
62
|
+
*
|
|
63
|
+
* @returns {boolean}
|
|
64
|
+
*/
|
|
65
|
+
function isLinux() {
|
|
66
|
+
return os.platform() === 'linux';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = {
|
|
70
|
+
detect,
|
|
71
|
+
isWindows,
|
|
72
|
+
isMacOS,
|
|
73
|
+
isLinux
|
|
74
|
+
};
|