kiro-spec-engine 1.0.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/CHANGELOG.md +60 -0
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/README.zh.md +242 -0
- package/bin/kiro-spec-engine.js +229 -0
- package/lib/commands/doctor.js +59 -0
- package/lib/i18n.js +79 -0
- package/lib/python-checker.js +209 -0
- package/locales/en.json +114 -0
- package/locales/zh.json +114 -0
- package/package.json +78 -0
- package/template/.kiro/README.md +288 -0
- package/template/.kiro/specs/SPEC_WORKFLOW_GUIDE.md +134 -0
- package/template/.kiro/steering/CORE_PRINCIPLES.md +140 -0
- package/template/.kiro/steering/CURRENT_CONTEXT.md +85 -0
- package/template/.kiro/steering/ENVIRONMENT.md +115 -0
- package/template/.kiro/steering/RULES_GUIDE.md +46 -0
- package/template/.kiro/tools/ultrawork_enhancer.py +676 -0
- package/template/README.md +109 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const inquirer = require('inquirer');
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
const { getI18n } = require('../lib/i18n');
|
|
10
|
+
const doctorCommand = require('../lib/commands/doctor');
|
|
11
|
+
|
|
12
|
+
const i18n = getI18n();
|
|
13
|
+
const t = (key, params) => i18n.t(key, params);
|
|
14
|
+
|
|
15
|
+
// Read version from package.json
|
|
16
|
+
const packageJson = require('../package.json');
|
|
17
|
+
|
|
18
|
+
const program = new Command();
|
|
19
|
+
|
|
20
|
+
// 版本和基本信息
|
|
21
|
+
program
|
|
22
|
+
.name(t('cli.name'))
|
|
23
|
+
.description(t('cli.description'))
|
|
24
|
+
.version(packageJson.version, '-v, --version', 'Display version number')
|
|
25
|
+
.option('-l, --lang <locale>', 'Set language (en/zh)', (locale) => {
|
|
26
|
+
i18n.setLocale(locale);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// 初始化项目命令
|
|
30
|
+
program
|
|
31
|
+
.command('init [project-name]')
|
|
32
|
+
.description(t('cli.commands.init.description'))
|
|
33
|
+
.option('-f, --force', t('cli.commands.init.forceOption'))
|
|
34
|
+
.action(async (projectName, options) => {
|
|
35
|
+
console.log(chalk.red('🔥') + ' ' + t('cli.commands.init.description'));
|
|
36
|
+
console.log();
|
|
37
|
+
|
|
38
|
+
// 获取项目名称
|
|
39
|
+
if (!projectName) {
|
|
40
|
+
const answers = await inquirer.prompt([
|
|
41
|
+
{
|
|
42
|
+
type: 'input',
|
|
43
|
+
name: 'projectName',
|
|
44
|
+
message: t('cli.commands.init.projectNamePrompt'),
|
|
45
|
+
default: path.basename(process.cwd())
|
|
46
|
+
}
|
|
47
|
+
]);
|
|
48
|
+
projectName = answers.projectName;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 检查是否已存在 .kiro 目录
|
|
52
|
+
const kiroDir = path.join(process.cwd(), '.kiro');
|
|
53
|
+
if (fs.existsSync(kiroDir) && !options.force) {
|
|
54
|
+
console.log(chalk.yellow(t('cli.commands.init.alreadyExists')));
|
|
55
|
+
const { overwrite } = await inquirer.prompt([
|
|
56
|
+
{
|
|
57
|
+
type: 'confirm',
|
|
58
|
+
name: 'overwrite',
|
|
59
|
+
message: t('cli.commands.init.overwritePrompt'),
|
|
60
|
+
default: false
|
|
61
|
+
}
|
|
62
|
+
]);
|
|
63
|
+
if (!overwrite) {
|
|
64
|
+
console.log(t('cli.commands.init.cancelled'));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
// 复制模板文件
|
|
71
|
+
const templateDir = path.join(__dirname, '../template');
|
|
72
|
+
await fs.copy(templateDir, process.cwd(), { overwrite: true });
|
|
73
|
+
|
|
74
|
+
// 更新项目配置
|
|
75
|
+
await updateProjectConfig(projectName);
|
|
76
|
+
|
|
77
|
+
console.log();
|
|
78
|
+
console.log(chalk.green(t('cli.commands.init.success')));
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(chalk.blue(t('cli.commands.init.nextSteps')));
|
|
81
|
+
console.log(' 1. ' + t('cli.commands.init.step1'));
|
|
82
|
+
console.log(' 2. ' + t('cli.commands.init.step2'));
|
|
83
|
+
console.log(' 3. ' + t('cli.commands.init.step3'));
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(chalk.red('🔥') + ' ' + t('cli.commands.init.startJourney'));
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(chalk.red(t('cli.commands.init.error')), error.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// 增强文档命令
|
|
93
|
+
program
|
|
94
|
+
.command('enhance <stage> <file>')
|
|
95
|
+
.description('Enhance document quality with Ultrawork spirit')
|
|
96
|
+
.option('-r, --requirements <file>', 'Requirements file (needed for design stage)')
|
|
97
|
+
.action(async (stage, file, options) => {
|
|
98
|
+
console.log(chalk.red('🔥') + ` Starting ${stage} stage Ultrawork enhancement...`);
|
|
99
|
+
|
|
100
|
+
// 检查 Python 和工具是否可用
|
|
101
|
+
const toolPath = path.join(process.cwd(), '.kiro/tools/ultrawork_enhancer.py');
|
|
102
|
+
if (!fs.existsSync(toolPath)) {
|
|
103
|
+
console.error(chalk.red('❌ Ultrawork tool not found. Please run: kiro-spec-engine init'));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 构建 Python 命令
|
|
108
|
+
let args = [toolPath, stage, file];
|
|
109
|
+
if (stage === 'design' && options.requirements) {
|
|
110
|
+
args.push(options.requirements);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 执行 Python 工具
|
|
114
|
+
const python = spawn('python', args, { stdio: 'inherit' });
|
|
115
|
+
|
|
116
|
+
python.on('close', (code) => {
|
|
117
|
+
if (code === 0) {
|
|
118
|
+
console.log(chalk.green('✅ Ultrawork enhancement completed!'));
|
|
119
|
+
} else {
|
|
120
|
+
console.error(chalk.red('❌ Enhancement failed with code:'), code);
|
|
121
|
+
process.exit(code);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
python.on('error', (error) => {
|
|
126
|
+
console.error(chalk.red('❌ Error running Python tool:'), error.message);
|
|
127
|
+
console.log(chalk.yellow('💡 Make sure Python 3.8+ is installed and in PATH'));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// 创建 Spec 命令
|
|
133
|
+
program
|
|
134
|
+
.command('create-spec <spec-name>')
|
|
135
|
+
.description('Create a new spec directory')
|
|
136
|
+
.action(async (specName) => {
|
|
137
|
+
const specPath = path.join(process.cwd(), '.kiro/specs', specName);
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await fs.ensureDir(specPath);
|
|
141
|
+
console.log(chalk.green('✅ Created spec directory:'), specPath);
|
|
142
|
+
console.log();
|
|
143
|
+
console.log(chalk.blue('📋 Next steps:'));
|
|
144
|
+
console.log(' 1. Create requirements.md in the spec directory');
|
|
145
|
+
console.log(' 2. Enhance with: ' + chalk.cyan(`kiro-spec-engine enhance requirements ${specPath}/requirements.md`));
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(chalk.red('❌ Error creating spec:'), error.message);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// 系统诊断命令
|
|
153
|
+
program
|
|
154
|
+
.command('doctor')
|
|
155
|
+
.description(t('cli.commands.doctor.description'))
|
|
156
|
+
.action(() => {
|
|
157
|
+
doctorCommand();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// 状态检查命令
|
|
161
|
+
program
|
|
162
|
+
.command('status')
|
|
163
|
+
.description('Check project status and available specs')
|
|
164
|
+
.action(async () => {
|
|
165
|
+
const kiroDir = path.join(process.cwd(), '.kiro');
|
|
166
|
+
|
|
167
|
+
if (!fs.existsSync(kiroDir)) {
|
|
168
|
+
console.log(chalk.yellow('⚠️ No Kiro Spec Engine project found in current directory'));
|
|
169
|
+
console.log('Run: ' + chalk.cyan('kiro-spec-engine init') + ' to initialize');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log(chalk.red('🔥') + ' Kiro Spec Engine Project Status');
|
|
174
|
+
console.log();
|
|
175
|
+
|
|
176
|
+
// 检查工具状态
|
|
177
|
+
const toolPath = path.join(kiroDir, 'tools/ultrawork_enhancer.py');
|
|
178
|
+
const toolStatus = fs.existsSync(toolPath) ? chalk.green('✅ Available') : chalk.red('❌ Missing');
|
|
179
|
+
console.log('Ultrawork Tool:', toolStatus);
|
|
180
|
+
|
|
181
|
+
// 列出 Specs
|
|
182
|
+
const specsDir = path.join(kiroDir, 'specs');
|
|
183
|
+
if (fs.existsSync(specsDir)) {
|
|
184
|
+
const specs = fs.readdirSync(specsDir).filter(item =>
|
|
185
|
+
fs.statSync(path.join(specsDir, item)).isDirectory()
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
console.log();
|
|
189
|
+
console.log(chalk.blue('📋 Available Specs:'));
|
|
190
|
+
if (specs.length === 0) {
|
|
191
|
+
console.log(' No specs found');
|
|
192
|
+
} else {
|
|
193
|
+
specs.forEach(spec => {
|
|
194
|
+
const specPath = path.join(specsDir, spec);
|
|
195
|
+
const hasReq = fs.existsSync(path.join(specPath, 'requirements.md'));
|
|
196
|
+
const hasDesign = fs.existsSync(path.join(specPath, 'design.md'));
|
|
197
|
+
const hasTasks = fs.existsSync(path.join(specPath, 'tasks.md'));
|
|
198
|
+
|
|
199
|
+
console.log(` ${spec}:`);
|
|
200
|
+
console.log(` Requirements: ${hasReq ? chalk.green('✅') : chalk.gray('⚪')}`);
|
|
201
|
+
console.log(` Design: ${hasDesign ? chalk.green('✅') : chalk.gray('⚪')}`);
|
|
202
|
+
console.log(` Tasks: ${hasTasks ? chalk.green('✅') : chalk.gray('⚪')}`);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// 更新项目配置的辅助函数
|
|
209
|
+
async function updateProjectConfig(projectName) {
|
|
210
|
+
const envPath = path.join(process.cwd(), '.kiro/steering/ENVIRONMENT.md');
|
|
211
|
+
const contextPath = path.join(process.cwd(), '.kiro/steering/CURRENT_CONTEXT.md');
|
|
212
|
+
|
|
213
|
+
// 更新 ENVIRONMENT.md
|
|
214
|
+
if (fs.existsSync(envPath)) {
|
|
215
|
+
let content = await fs.readFile(envPath, 'utf8');
|
|
216
|
+
content = content.replace(/\[请修改为你的项目名称\]/g, projectName);
|
|
217
|
+
await fs.writeFile(envPath, content);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 更新 CURRENT_CONTEXT.md
|
|
221
|
+
if (fs.existsSync(contextPath)) {
|
|
222
|
+
let content = await fs.readFile(contextPath, 'utf8');
|
|
223
|
+
content = content.replace(/新项目/g, projectName);
|
|
224
|
+
await fs.writeFile(contextPath, content);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 解析命令行参数
|
|
229
|
+
program.parse();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const pythonChecker = require('../python-checker');
|
|
3
|
+
const { getI18n } = require('../i18n');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CLI Doctor Command Component
|
|
7
|
+
*
|
|
8
|
+
* Verifies system requirements and provides diagnostics.
|
|
9
|
+
* Checks Node.js version and Python availability.
|
|
10
|
+
*
|
|
11
|
+
* Requirements: 7.5
|
|
12
|
+
*/
|
|
13
|
+
function doctorCommand() {
|
|
14
|
+
const i18n = getI18n();
|
|
15
|
+
|
|
16
|
+
console.log(chalk.red('🔥') + ' ' + i18n.t('cli.commands.doctor.title'));
|
|
17
|
+
console.log();
|
|
18
|
+
console.log(i18n.t('cli.commands.doctor.checking'));
|
|
19
|
+
console.log();
|
|
20
|
+
|
|
21
|
+
// Check Node.js version
|
|
22
|
+
const nodeVersion = process.version;
|
|
23
|
+
const nodeStatus = chalk.green('✓');
|
|
24
|
+
console.log(`${nodeStatus} ${i18n.t('cli.commands.doctor.nodejs')}: ${chalk.cyan(nodeVersion)}`);
|
|
25
|
+
|
|
26
|
+
// Check Python availability
|
|
27
|
+
const pythonStatus = pythonChecker.checkPython();
|
|
28
|
+
|
|
29
|
+
if (pythonStatus.available) {
|
|
30
|
+
const pythonOk = chalk.green('✓');
|
|
31
|
+
console.log(`${pythonOk} ${i18n.t('cli.commands.doctor.python')}: ${chalk.cyan(pythonStatus.version)}`);
|
|
32
|
+
} else {
|
|
33
|
+
const pythonFail = chalk.red('✗');
|
|
34
|
+
console.log(`${pythonFail} ${i18n.t('cli.commands.doctor.python')}: ${chalk.yellow(pythonStatus.message)}`);
|
|
35
|
+
console.log();
|
|
36
|
+
console.log(chalk.yellow(i18n.t('cli.commands.doctor.python_note')));
|
|
37
|
+
console.log();
|
|
38
|
+
console.log(chalk.blue(i18n.t('python.install_header')));
|
|
39
|
+
console.log(pythonChecker.getInstallInstructions());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log();
|
|
43
|
+
console.log(chalk.blue('─'.repeat(60)));
|
|
44
|
+
console.log();
|
|
45
|
+
|
|
46
|
+
// Summary
|
|
47
|
+
if (pythonStatus.available) {
|
|
48
|
+
console.log(chalk.green('✅ ' + i18n.t('cli.commands.doctor.all_good')));
|
|
49
|
+
console.log(i18n.t('cli.commands.doctor.ready'));
|
|
50
|
+
} else {
|
|
51
|
+
console.log(chalk.yellow('⚠️ ' + i18n.t('cli.commands.doctor.python_missing')));
|
|
52
|
+
console.log(i18n.t('cli.commands.doctor.basic_features'));
|
|
53
|
+
console.log(i18n.t('cli.commands.doctor.ultrawork_unavailable'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = doctorCommand;
|
package/lib/i18n.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
class I18n {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.locale = this.detectLocale();
|
|
7
|
+
this.messages = this.loadMessages(this.locale);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
detectLocale() {
|
|
11
|
+
// 优先级: 环境变量 > 系统语言 > 默认英文
|
|
12
|
+
const envLocale = process.env.KIRO_LANG || process.env.LANG;
|
|
13
|
+
|
|
14
|
+
if (envLocale) {
|
|
15
|
+
if (envLocale.startsWith('zh')) return 'zh';
|
|
16
|
+
if (envLocale.startsWith('en')) return 'en';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 检测系统语言
|
|
20
|
+
const systemLocale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
21
|
+
if (systemLocale.startsWith('zh')) return 'zh';
|
|
22
|
+
|
|
23
|
+
return 'en'; // 默认英文
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
loadMessages(locale) {
|
|
27
|
+
try {
|
|
28
|
+
const messagesPath = path.join(__dirname, '../locales', `${locale}.json`);
|
|
29
|
+
return JSON.parse(fs.readFileSync(messagesPath, 'utf8'));
|
|
30
|
+
} catch (error) {
|
|
31
|
+
// 如果加载失败,回退到英文
|
|
32
|
+
const fallbackPath = path.join(__dirname, '../locales', 'en.json');
|
|
33
|
+
return JSON.parse(fs.readFileSync(fallbackPath, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
t(key, params = {}) {
|
|
38
|
+
const keys = key.split('.');
|
|
39
|
+
let value = this.messages;
|
|
40
|
+
|
|
41
|
+
for (const k of keys) {
|
|
42
|
+
if (value && typeof value === 'object') {
|
|
43
|
+
value = value[k];
|
|
44
|
+
} else {
|
|
45
|
+
return key; // 如果找不到,返回 key 本身
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 替换参数
|
|
50
|
+
if (typeof value === 'string') {
|
|
51
|
+
return value.replace(/\{(\w+)\}/g, (match, param) => {
|
|
52
|
+
return params[param] !== undefined ? params[param] : match;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return value || key;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
setLocale(locale) {
|
|
60
|
+
this.locale = locale;
|
|
61
|
+
this.messages = this.loadMessages(locale);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getLocale() {
|
|
65
|
+
return this.locale;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 单例模式
|
|
70
|
+
let instance = null;
|
|
71
|
+
|
|
72
|
+
function getI18n() {
|
|
73
|
+
if (!instance) {
|
|
74
|
+
instance = new I18n();
|
|
75
|
+
}
|
|
76
|
+
return instance;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { getI18n, I18n };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const { getI18n } = require('./i18n');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Python Dependency Detection Component
|
|
6
|
+
*
|
|
7
|
+
* Detects Python availability and provides user guidance for installation.
|
|
8
|
+
* Supports Windows, Linux, and macOS platforms.
|
|
9
|
+
*
|
|
10
|
+
* Requirements: 3.1, 3.2, 3.3, 3.4, 3.5
|
|
11
|
+
*/
|
|
12
|
+
class PythonChecker {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.minMajor = 3;
|
|
15
|
+
this.minMinor = 8;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if Python is available and meets version requirements
|
|
20
|
+
*
|
|
21
|
+
* @returns {Object} { available: boolean, version: string|null, message: string }
|
|
22
|
+
*
|
|
23
|
+
* Requirements:
|
|
24
|
+
* - 3.1: Verify Python availability before executing Python code
|
|
25
|
+
* - 3.4: Detect Python 3.8 or higher
|
|
26
|
+
* - 3.5: Inform user of minimum required version when too old
|
|
27
|
+
*/
|
|
28
|
+
checkPython() {
|
|
29
|
+
const i18n = getI18n();
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// Try to execute python --version
|
|
33
|
+
const versionOutput = execSync('python --version', {
|
|
34
|
+
encoding: 'utf8',
|
|
35
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
36
|
+
}).trim();
|
|
37
|
+
|
|
38
|
+
const parsed = this.parseVersion(versionOutput);
|
|
39
|
+
|
|
40
|
+
if (!parsed) {
|
|
41
|
+
return {
|
|
42
|
+
available: false,
|
|
43
|
+
version: versionOutput,
|
|
44
|
+
message: i18n.t('python.malformed_version', { version: versionOutput })
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { major, minor, patch } = parsed;
|
|
49
|
+
const meetsRequirement = this.meetsVersionRequirement(major, minor);
|
|
50
|
+
|
|
51
|
+
if (meetsRequirement) {
|
|
52
|
+
return {
|
|
53
|
+
available: true,
|
|
54
|
+
version: `Python ${major}.${minor}.${patch}`,
|
|
55
|
+
message: i18n.t('python.available', { version: `${major}.${minor}.${patch}` })
|
|
56
|
+
};
|
|
57
|
+
} else {
|
|
58
|
+
return {
|
|
59
|
+
available: false,
|
|
60
|
+
version: `Python ${major}.${minor}.${patch}`,
|
|
61
|
+
message: i18n.t('python.version_too_old', {
|
|
62
|
+
version: `${major}.${minor}.${patch}`,
|
|
63
|
+
required: `${this.minMajor}.${this.minMinor}+`
|
|
64
|
+
})
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
// Python not found or command failed
|
|
69
|
+
return {
|
|
70
|
+
available: false,
|
|
71
|
+
version: null,
|
|
72
|
+
message: i18n.t('python.not_found')
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parse Python version string to extract version numbers
|
|
79
|
+
*
|
|
80
|
+
* @param {string} versionString - Python version output (e.g., "Python 3.10.0")
|
|
81
|
+
* @returns {Object|null} { major, minor, patch, meetsRequirement } or null if parsing fails
|
|
82
|
+
*
|
|
83
|
+
* Requirements:
|
|
84
|
+
* - 3.4: Extract version numbers for comparison
|
|
85
|
+
*/
|
|
86
|
+
parseVersion(versionString) {
|
|
87
|
+
if (!versionString || typeof versionString !== 'string') {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Match pattern: Python X.Y.Z
|
|
92
|
+
const versionMatch = versionString.match(/Python\s+(\d+)\.(\d+)\.(\d+)/i);
|
|
93
|
+
|
|
94
|
+
if (!versionMatch) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const major = parseInt(versionMatch[1], 10);
|
|
99
|
+
const minor = parseInt(versionMatch[2], 10);
|
|
100
|
+
const patch = parseInt(versionMatch[3], 10);
|
|
101
|
+
|
|
102
|
+
if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
major,
|
|
108
|
+
minor,
|
|
109
|
+
patch,
|
|
110
|
+
meetsRequirement: this.meetsVersionRequirement(major, minor)
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check if version meets minimum requirement (3.8+)
|
|
116
|
+
*
|
|
117
|
+
* @param {number} major - Major version number
|
|
118
|
+
* @param {number} minor - Minor version number
|
|
119
|
+
* @returns {boolean} True if version meets requirement
|
|
120
|
+
*/
|
|
121
|
+
meetsVersionRequirement(major, minor) {
|
|
122
|
+
if (major > this.minMajor) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (major === this.minMajor && minor >= this.minMinor) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get installation instructions for the current OS
|
|
133
|
+
*
|
|
134
|
+
* @returns {string} OS-specific installation instructions
|
|
135
|
+
*
|
|
136
|
+
* Requirements:
|
|
137
|
+
* - 3.3: Provide installation instructions for user's operating system
|
|
138
|
+
*/
|
|
139
|
+
getInstallInstructions() {
|
|
140
|
+
const i18n = getI18n();
|
|
141
|
+
const platform = process.platform;
|
|
142
|
+
|
|
143
|
+
// Map platform to locale key
|
|
144
|
+
const platformKey = this.getPlatformKey(platform);
|
|
145
|
+
|
|
146
|
+
// Try to get platform-specific instructions
|
|
147
|
+
const instructions = i18n.t(`python.install.${platformKey}`);
|
|
148
|
+
|
|
149
|
+
// If translation not found (returns key), use default
|
|
150
|
+
if (instructions === `python.install.${platformKey}`) {
|
|
151
|
+
return i18n.t('python.install.default');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return instructions;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Map Node.js platform identifier to locale key
|
|
159
|
+
*
|
|
160
|
+
* @param {string} platform - process.platform value
|
|
161
|
+
* @returns {string} Platform key for locale lookup
|
|
162
|
+
*/
|
|
163
|
+
getPlatformKey(platform) {
|
|
164
|
+
switch (platform) {
|
|
165
|
+
case 'win32':
|
|
166
|
+
return 'windows';
|
|
167
|
+
case 'darwin':
|
|
168
|
+
return 'macos';
|
|
169
|
+
case 'linux':
|
|
170
|
+
return 'linux';
|
|
171
|
+
default:
|
|
172
|
+
return 'default';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get friendly error message when Python is not available
|
|
178
|
+
*
|
|
179
|
+
* @returns {string} User-friendly error message with installation guidance
|
|
180
|
+
*
|
|
181
|
+
* Requirements:
|
|
182
|
+
* - 3.2: Display friendly error message in user's language
|
|
183
|
+
* - 3.3: Provide installation instructions
|
|
184
|
+
*/
|
|
185
|
+
getErrorMessage() {
|
|
186
|
+
const i18n = getI18n();
|
|
187
|
+
const status = this.checkPython();
|
|
188
|
+
|
|
189
|
+
if (status.available) {
|
|
190
|
+
return null; // No error
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const errorLines = [
|
|
194
|
+
i18n.t('python.error_header'),
|
|
195
|
+
'',
|
|
196
|
+
status.message,
|
|
197
|
+
'',
|
|
198
|
+
i18n.t('python.install_header'),
|
|
199
|
+
this.getInstallInstructions(),
|
|
200
|
+
'',
|
|
201
|
+
i18n.t('python.help_footer')
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
return errorLines.join('\n');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Export singleton instance
|
|
209
|
+
module.exports = new PythonChecker();
|
package/locales/en.json
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli": {
|
|
3
|
+
"name": "kiro-spec-engine",
|
|
4
|
+
"description": "🔥 Kiro Spec Engine - Spec-driven development with steering rules",
|
|
5
|
+
"commands": {
|
|
6
|
+
"init": {
|
|
7
|
+
"description": "Initialize a new Kiro Ultrawork project",
|
|
8
|
+
"projectNamePrompt": "Enter project name:",
|
|
9
|
+
"forceOption": "Force initialization even if .kiro directory exists",
|
|
10
|
+
"alreadyExists": "⚠️ .kiro directory already exists",
|
|
11
|
+
"overwritePrompt": "Do you want to overwrite it?",
|
|
12
|
+
"cancelled": "Initialization cancelled.",
|
|
13
|
+
"success": "🎉 Kiro Ultrawork project initialized successfully!",
|
|
14
|
+
"nextSteps": "📋 Next steps:",
|
|
15
|
+
"step1": "Create your first spec: mkdir .kiro/specs/01-00-your-feature",
|
|
16
|
+
"step2": "Write basic requirements.md",
|
|
17
|
+
"step3": "Enhance with Ultrawork: kiro-spec-engine enhance requirements .kiro/specs/01-00-your-feature/requirements.md",
|
|
18
|
+
"startJourney": "🔥 Start your Ultrawork journey!",
|
|
19
|
+
"error": "❌ Error initializing project:"
|
|
20
|
+
},
|
|
21
|
+
"enhance": {
|
|
22
|
+
"description": "Enhance document quality with Ultrawork spirit",
|
|
23
|
+
"starting": "🔥 Starting {stage} stage Ultrawork enhancement...",
|
|
24
|
+
"toolNotFound": "❌ Ultrawork tool not found. Please run: kiro-spec-engine init",
|
|
25
|
+
"completed": "✅ Ultrawork enhancement completed!",
|
|
26
|
+
"failed": "❌ Enhancement failed with code:",
|
|
27
|
+
"pythonError": "❌ Error running Python tool:",
|
|
28
|
+
"pythonTip": "💡 Make sure Python 3.8+ is installed and in PATH"
|
|
29
|
+
},
|
|
30
|
+
"createSpec": {
|
|
31
|
+
"description": "Create a new spec directory",
|
|
32
|
+
"success": "✅ Created spec directory:",
|
|
33
|
+
"nextSteps": "📋 Next steps:",
|
|
34
|
+
"step1": "Create requirements.md in the spec directory",
|
|
35
|
+
"step2": "Enhance with: kiro-spec-engine enhance requirements {specPath}/requirements.md",
|
|
36
|
+
"error": "❌ Error creating spec:"
|
|
37
|
+
},
|
|
38
|
+
"status": {
|
|
39
|
+
"description": "Check project status and available specs",
|
|
40
|
+
"noProject": "⚠️ No Kiro Ultrawork project found in current directory",
|
|
41
|
+
"initTip": "Run: kiro-spec-engine init to initialize",
|
|
42
|
+
"title": "🔥 Kiro Ultrawork Project Status",
|
|
43
|
+
"toolStatus": "Ultrawork Tool:",
|
|
44
|
+
"available": "✅ Available",
|
|
45
|
+
"missing": "❌ Missing",
|
|
46
|
+
"specsTitle": "📋 Available Specs:",
|
|
47
|
+
"noSpecs": "No specs found",
|
|
48
|
+
"requirements": "Requirements:",
|
|
49
|
+
"design": "Design:",
|
|
50
|
+
"tasks": "Tasks:"
|
|
51
|
+
},
|
|
52
|
+
"doctor": {
|
|
53
|
+
"description": "Check system requirements and diagnostics",
|
|
54
|
+
"title": "System Diagnostics",
|
|
55
|
+
"checking": "Checking system requirements...",
|
|
56
|
+
"nodejs": "Node.js",
|
|
57
|
+
"python": "Python",
|
|
58
|
+
"python_note": "Note: Python is required for Ultrawork quality enhancement features.",
|
|
59
|
+
"all_good": "All system requirements are met!",
|
|
60
|
+
"ready": "You're ready to use all Kiro Spec Engine features including Ultrawork enhancements.",
|
|
61
|
+
"python_missing": "Python is not available",
|
|
62
|
+
"basic_features": "Basic CLI features (init, status, create-spec) will work normally.",
|
|
63
|
+
"ultrawork_unavailable": "Ultrawork enhancement features require Python 3.8 or higher."
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"ultrawork": {
|
|
68
|
+
"spirit": {
|
|
69
|
+
"title": "🔥 Ultrawork Spirit",
|
|
70
|
+
"subtitle": "Like Sisyphus pushing the boulder up the mountain",
|
|
71
|
+
"principles": {
|
|
72
|
+
"relentless": "Never give up, relentless effort",
|
|
73
|
+
"excellence": "Pursue professional-grade quality",
|
|
74
|
+
"improvement": "Continuous improvement and optimization",
|
|
75
|
+
"completion": "Perfect completion of every task"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"quality": {
|
|
79
|
+
"scoring": "Quality Scoring: {score}/10",
|
|
80
|
+
"professional": "✅ Document has reached professional standards!",
|
|
81
|
+
"improving": "🔄 Round {iteration} improvement: {improvements}",
|
|
82
|
+
"noImprovements": "⚠️ No more improvements identified, stopping iteration",
|
|
83
|
+
"scoreNotImproved": "⚠️ Quality score not improved, stopping iteration"
|
|
84
|
+
},
|
|
85
|
+
"stages": {
|
|
86
|
+
"requirements": "Requirements",
|
|
87
|
+
"design": "Design",
|
|
88
|
+
"tasks": "Tasks"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"messages": {
|
|
92
|
+
"success": "✅ Success",
|
|
93
|
+
"error": "❌ Error",
|
|
94
|
+
"warning": "⚠️ Warning",
|
|
95
|
+
"info": "💡 Info",
|
|
96
|
+
"processing": "🔄 Processing",
|
|
97
|
+
"completed": "🎉 Completed"
|
|
98
|
+
},
|
|
99
|
+
"python": {
|
|
100
|
+
"available": "Python {version} is available",
|
|
101
|
+
"not_found": "Python is not installed or not found in PATH",
|
|
102
|
+
"version_too_old": "Python {version} is installed, but version {required} or higher is required",
|
|
103
|
+
"malformed_version": "Unable to parse Python version: {version}",
|
|
104
|
+
"error_header": "✗ Python is required for Ultrawork quality enhancement features",
|
|
105
|
+
"install_header": "Installation instructions:",
|
|
106
|
+
"help_footer": "For more help, visit: https://github.com/USERNAME/kiro-spec-engine#python-setup",
|
|
107
|
+
"install": {
|
|
108
|
+
"windows": "Windows:\n1. Download Python from https://www.python.org/downloads/\n2. Run the installer and check 'Add Python to PATH'\n3. Restart your terminal and run: python --version",
|
|
109
|
+
"macos": "macOS:\n1. Install Homebrew if not already installed: /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n2. Install Python: brew install python\n3. Verify installation: python3 --version",
|
|
110
|
+
"linux": "Linux:\n1. Ubuntu/Debian: sudo apt-get update && sudo apt-get install python3 python3-pip\n2. Fedora/RHEL: sudo dnf install python3 python3-pip\n3. Verify installation: python3 --version",
|
|
111
|
+
"default": "Please visit https://www.python.org/downloads/ to download and install Python 3.8 or higher for your operating system."
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|