ai-sprint-kit 2.0.4 ā 2.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/README.md +28 -14
- package/bin/ai-sprint.js +439 -8
- package/lib/ci-generator.js +248 -0
- package/lib/interactive.js +249 -0
- package/lib/recovery.js +279 -0
- package/lib/updater.js +340 -0
- package/lib/validator.js +264 -0
- package/package.json +3 -2
package/lib/validator.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
const fs = require('fs/promises');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Validation results
|
|
7
|
+
*/
|
|
8
|
+
class ValidationResult {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.passed = [];
|
|
11
|
+
this.failed = [];
|
|
12
|
+
this.warnings = [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
addPass(check) {
|
|
16
|
+
this.passed.push(check);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
addFail(check, suggestion) {
|
|
20
|
+
this.failed.push({ check, suggestion });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
addWarning(check, message) {
|
|
24
|
+
this.warnings.push({ check, message });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
isValid() {
|
|
28
|
+
return this.failed.length === 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
summary() {
|
|
32
|
+
const { passed, failed, warnings } = this;
|
|
33
|
+
return {
|
|
34
|
+
total: passed.length + failed.length + warnings.length,
|
|
35
|
+
passed: passed.length,
|
|
36
|
+
failed: failed.length,
|
|
37
|
+
warnings: warnings.length
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Validate AI Sprint Kit installation
|
|
44
|
+
* @param {string} targetDir - Directory to validate
|
|
45
|
+
* @returns {Promise<ValidationResult>}
|
|
46
|
+
*/
|
|
47
|
+
async function validateInstallation(targetDir = process.cwd()) {
|
|
48
|
+
const result = new ValidationResult();
|
|
49
|
+
|
|
50
|
+
// 1. Check .claude directory structure
|
|
51
|
+
await validateDirectoryStructure(targetDir, result);
|
|
52
|
+
|
|
53
|
+
// 2. Check required files
|
|
54
|
+
await validateRequiredFiles(targetDir, result);
|
|
55
|
+
|
|
56
|
+
// 3. Check configuration files
|
|
57
|
+
await validateConfiguration(targetDir, result);
|
|
58
|
+
|
|
59
|
+
// 4. Check agents
|
|
60
|
+
await validateAgents(targetDir, result);
|
|
61
|
+
|
|
62
|
+
// 5. Check commands
|
|
63
|
+
await validateCommands(targetDir, result);
|
|
64
|
+
|
|
65
|
+
// 6. Check hooks
|
|
66
|
+
await validateHooks(targetDir, result);
|
|
67
|
+
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validate directory structure
|
|
73
|
+
*/
|
|
74
|
+
async function validateDirectoryStructure(targetDir, result) {
|
|
75
|
+
const requiredDirs = [
|
|
76
|
+
'.claude',
|
|
77
|
+
'.claude/agents',
|
|
78
|
+
'.claude/commands',
|
|
79
|
+
'.claude/workflows',
|
|
80
|
+
'ai_context',
|
|
81
|
+
'ai_context/plans',
|
|
82
|
+
'ai_context/reports',
|
|
83
|
+
'ai_context/memory'
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
for (const dir of requiredDirs) {
|
|
87
|
+
const dirPath = path.join(targetDir, dir);
|
|
88
|
+
try {
|
|
89
|
+
const stat = await fs.stat(dirPath);
|
|
90
|
+
if (stat.isDirectory()) {
|
|
91
|
+
result.addPass(`Directory exists: ${dir}/`);
|
|
92
|
+
} else {
|
|
93
|
+
result.addFail(`Directory exists but is not a directory: ${dir}/`, `Remove file and reinstall`);
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
result.addFail(`Directory missing: ${dir}/`, `Run: ai-sprint repair`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Validate required files
|
|
103
|
+
*/
|
|
104
|
+
async function validateRequiredFiles(targetDir, result) {
|
|
105
|
+
const requiredFiles = [
|
|
106
|
+
'CLAUDE.md',
|
|
107
|
+
'.claude/settings.json',
|
|
108
|
+
'.claude/.env.example'
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
for (const file of requiredFiles) {
|
|
112
|
+
const filePath = path.join(targetDir, file);
|
|
113
|
+
try {
|
|
114
|
+
await fs.access(filePath);
|
|
115
|
+
result.addPass(`File exists: ${file}`);
|
|
116
|
+
} catch {
|
|
117
|
+
result.addFail(`File missing: ${file}`, `Run: ai-sprint repair`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Validate configuration files
|
|
124
|
+
*/
|
|
125
|
+
async function validateConfiguration(targetDir, result) {
|
|
126
|
+
// Check settings.json is valid JSON
|
|
127
|
+
const settingsPath = path.join(targetDir, '.claude/settings.json');
|
|
128
|
+
try {
|
|
129
|
+
const content = await fs.readFile(settingsPath, 'utf8');
|
|
130
|
+
JSON.parse(content);
|
|
131
|
+
result.addPass('settings.json is valid JSON');
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error.code === 'ENOENT') {
|
|
134
|
+
result.addFail('settings.json missing', 'Run: ai-sprint repair');
|
|
135
|
+
} else {
|
|
136
|
+
result.addFail('settings.json is invalid JSON', 'Fix JSON syntax or run: ai-sprint repair');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check .env.example
|
|
141
|
+
const envExamplePath = path.join(targetDir, '.claude/.env.example');
|
|
142
|
+
try {
|
|
143
|
+
await fs.access(envExamplePath);
|
|
144
|
+
result.addPass('.env.example exists');
|
|
145
|
+
} catch {
|
|
146
|
+
result.addWarning('.env.example missing', 'Create .env.example with environment variable templates');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check .mcp.json.example
|
|
150
|
+
const mcpExamplePath = path.join(targetDir, '.mcp.json.example');
|
|
151
|
+
try {
|
|
152
|
+
await fs.access(mcpExamplePath);
|
|
153
|
+
result.addPass('.mcp.json.example exists');
|
|
154
|
+
} catch {
|
|
155
|
+
result.addWarning('.mcp.json.example missing', 'Create .mcp.json.example with MCP configuration template');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Validate agents
|
|
161
|
+
*/
|
|
162
|
+
async function validateAgents(targetDir, result) {
|
|
163
|
+
const agentsDir = path.join(targetDir, '.claude/agents');
|
|
164
|
+
try {
|
|
165
|
+
const files = await fs.readdir(agentsDir);
|
|
166
|
+
const mdFiles = files.filter(f => f.endsWith('.md'));
|
|
167
|
+
|
|
168
|
+
if (mdFiles.length >= 9) {
|
|
169
|
+
result.addPass(`Agents: ${mdFiles.length} agent files found`);
|
|
170
|
+
} else {
|
|
171
|
+
result.addWarning(`Agents: Only ${mdFiles.length} agent files (expected 9+)`, 'Consider adding missing agents');
|
|
172
|
+
}
|
|
173
|
+
} catch {
|
|
174
|
+
result.addFail('Agents directory missing', 'Run: ai-sprint repair');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Validate commands
|
|
180
|
+
*/
|
|
181
|
+
async function validateCommands(targetDir, result) {
|
|
182
|
+
const commandsDir = path.join(targetDir, '.claude/commands');
|
|
183
|
+
try {
|
|
184
|
+
const files = await fs.readdir(commandsDir);
|
|
185
|
+
const mdFiles = files.filter(f => f.endsWith('.md'));
|
|
186
|
+
|
|
187
|
+
if (mdFiles.length >= 13) {
|
|
188
|
+
result.addPass(`Commands: ${mdFiles.length} command files found`);
|
|
189
|
+
} else {
|
|
190
|
+
result.addWarning(`Commands: Only ${mdFiles.length} command files (expected 13+)`, 'Consider adding missing commands');
|
|
191
|
+
}
|
|
192
|
+
} catch {
|
|
193
|
+
result.addFail('Commands directory missing', 'Run: ai-sprint repair');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Validate hooks
|
|
199
|
+
*/
|
|
200
|
+
async function validateHooks(targetDir, result) {
|
|
201
|
+
const hooksDir = path.join(targetDir, '.claude/hooks');
|
|
202
|
+
try {
|
|
203
|
+
const stat = await fs.stat(hooksDir);
|
|
204
|
+
if (stat.isDirectory()) {
|
|
205
|
+
const files = await fs.readdir(hooksDir);
|
|
206
|
+
if (files.length > 0) {
|
|
207
|
+
result.addPass(`Hooks: ${files.length} hook files found`);
|
|
208
|
+
} else {
|
|
209
|
+
result.addWarning('Hooks directory exists but is empty', 'Add PostToolUse, Stop hooks for automation');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
result.addWarning('Hooks directory missing', 'Add hooks for automated formatting and verification');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Format validation results for display
|
|
219
|
+
*/
|
|
220
|
+
function formatResults(result) {
|
|
221
|
+
const lines = [];
|
|
222
|
+
const summary = result.summary();
|
|
223
|
+
|
|
224
|
+
lines.push(chalk.bold('\nš AI Sprint Kit Validation\n'));
|
|
225
|
+
|
|
226
|
+
if (result.passed.length > 0) {
|
|
227
|
+
lines.push(chalk.green.bold('\nā
Passed:'));
|
|
228
|
+
result.passed.forEach(check => {
|
|
229
|
+
lines.push(chalk.green(` ā ${check}`));
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (result.warnings.length > 0) {
|
|
234
|
+
lines.push(chalk.yellow.bold('\nā ļø Warnings:'));
|
|
235
|
+
result.warnings.forEach(({ check, message }) => {
|
|
236
|
+
lines.push(chalk.yellow(` ! ${check}`));
|
|
237
|
+
lines.push(chalk.gray(` ā ${message}`));
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (result.failed.length > 0) {
|
|
242
|
+
lines.push(chalk.red.bold('\nā Failed:'));
|
|
243
|
+
result.failed.forEach(({ check, suggestion }) => {
|
|
244
|
+
lines.push(chalk.red(` ā ${check}`));
|
|
245
|
+
lines.push(chalk.gray(` ā ${suggestion}`));
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
lines.push(chalk.bold(`\nš Summary: ${summary.passed} passed, ${summary.warnings} warnings, ${summary.failed} failed`));
|
|
250
|
+
|
|
251
|
+
if (result.isValid()) {
|
|
252
|
+
lines.push(chalk.green.bold('\nā
Installation is valid!\n'));
|
|
253
|
+
} else {
|
|
254
|
+
lines.push(chalk.red.bold('\nā Installation has issues. Run: ai-sprint repair\n'));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return lines.join('\n');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
module.exports = {
|
|
261
|
+
validateInstallation,
|
|
262
|
+
ValidationResult,
|
|
263
|
+
formatResults
|
|
264
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-sprint-kit",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "CLI installer for AI Sprint autonomous development framework - requires license",
|
|
5
5
|
"main": "lib/installer.js",
|
|
6
6
|
"bin": {
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"commander": "^11.1.0",
|
|
46
46
|
"chalk": "^4.1.2",
|
|
47
|
-
"ora": "^5.4.1"
|
|
47
|
+
"ora": "^5.4.1",
|
|
48
|
+
"semver": "^7.6.0"
|
|
48
49
|
},
|
|
49
50
|
"publishConfig": {
|
|
50
51
|
"access": "public"
|