context-forge
Version:
AI orchestration platform with autonomous teams, enhancement planning, migration tools, 25+ slash commands, checkpoints & hooks. Multi-IDE: Claude, Cursor, Windsurf, Cline, Copilot
179 lines ⢠7.45 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValidationExecutor = void 0;
const child_process_1 = require("child_process");
const util_1 = require("util");
const validationCommands_1 = require("../data/validationCommands");
const validationReport_1 = require("../generators/validationReport");
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const execAsync = (0, util_1.promisify)(child_process_1.exec);
class ValidationExecutor {
constructor(projectPath, config) {
this.results = [];
this.projectPath = projectPath;
this.config = config;
}
async runValidation(levels) {
console.log(chalk_1.default.bold.blue('\nš Running validation commands...\n'));
const commands = (0, validationCommands_1.getValidationCommands)(this.config.techStack);
const levelsToRun = levels || ['syntax', 'lint', 'tests', 'build'];
for (const level of levelsToRun) {
if (level === 'syntax' && commands.syntax) {
await this.runCommandSet('syntax', commands.syntax);
}
else if (level === 'lint' && commands.lint) {
await this.runCommand('lint', commands.lint);
}
else if (level === 'tests' && commands.tests) {
await this.runCommandSet('tests', commands.tests);
}
else if (level === 'build' && commands.build) {
await this.runCommand('build', commands.build);
}
else if (level === 'coverage' && commands.coverage) {
await this.runCommand('coverage', commands.coverage);
}
else if (level === 'security' && commands.security) {
await this.runCommandSet('security', commands.security);
}
}
const report = this.generateReport();
await this.saveReport(report);
// Save markdown report
const reportPath = await (0, validationReport_1.saveValidationReport)(report, this.projectPath);
console.log(chalk_1.default.gray(`\nš Full report saved to: ${reportPath}`));
this.printSummary(report);
return report;
}
async runCommand(level, command) {
const spinner = (0, ora_1.default)(`Running ${level}: ${command}`).start();
const startTime = Date.now();
try {
const { stdout } = await execAsync(command, {
cwd: this.projectPath,
env: { ...process.env, CI: 'true' },
});
const duration = Date.now() - startTime;
this.results.push({
level,
command,
success: true,
output: stdout,
duration,
});
spinner.succeed(chalk_1.default.green(`ā ${level} passed (${duration}ms)`));
if (stdout && process.env.VERBOSE) {
console.log(chalk_1.default.gray(stdout));
}
}
catch (error) {
const duration = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
const errorOutput = error instanceof Error && 'stdout' in error ? error.stdout : '';
this.results.push({
level,
command,
success: false,
error: errorMessage,
output: errorOutput,
duration,
});
spinner.fail(chalk_1.default.red(`ā ${level} failed (${duration}ms)`));
if (errorOutput || (error instanceof Error && 'stderr' in error)) {
const errorOutput2 = error instanceof Error && 'stderr' in error ? error.stderr : '';
console.log(chalk_1.default.red(errorOutput || errorOutput2));
}
// Only throw for critical errors
const levelInfo = validationCommands_1.validationLevels[level];
if (levelInfo?.critical) {
throw new Error(`Critical validation failed: ${level}`);
}
}
}
async runCommandSet(level, commands) {
for (const command of commands) {
await this.runCommand(`${level}:${command}`, command);
}
}
generateReport() {
const passed = this.results.filter((r) => r.success).length;
const failed = this.results.filter((r) => !r.success).length;
return {
projectName: this.config.projectName,
timestamp: new Date().toISOString(),
overallSuccess: failed === 0,
results: this.results,
summary: {
total: this.results.length,
passed,
failed,
skipped: 0,
},
};
}
async saveReport(report) {
const reportsDir = path_1.default.join(this.projectPath, '.validation-reports');
await fs_extra_1.default.ensureDir(reportsDir);
const filename = `validation-${Date.now()}.json`;
const filepath = path_1.default.join(reportsDir, filename);
await fs_extra_1.default.writeJson(filepath, report, { spaces: 2 });
// Also save as latest
const latestPath = path_1.default.join(reportsDir, 'latest.json');
await fs_extra_1.default.writeJson(latestPath, report, { spaces: 2 });
}
printSummary(report) {
console.log((0, validationReport_1.generateValidationSummary)(report));
}
// Generate a validation gate for PRP
async generateValidationGate() {
const commands = (0, validationCommands_1.getValidationCommands)(this.config.techStack);
let gate = '## Validation Gate\n\n';
gate += 'Run the following commands to validate your implementation:\n\n';
gate += '### Syntax & Type Checking\n';
gate += '```bash\n';
if (commands.syntax) {
commands.syntax.forEach((cmd) => {
gate += `${cmd}\n`;
});
}
gate += '```\n\n';
gate += '### Linting\n';
gate += '```bash\n';
gate += `${commands.lint || 'npm run lint'}\n`;
gate += '```\n\n';
gate += '### Tests\n';
gate += '```bash\n';
if (commands.tests) {
commands.tests.forEach((cmd) => {
gate += `${cmd}\n`;
});
}
gate += '```\n\n';
gate += '### Build\n';
gate += '```bash\n';
gate += `${commands.build}\n`;
gate += '```\n\n';
gate += '### Start Application\n';
gate += '```bash\n';
gate += `${commands.start}\n`;
gate += '```\n\n';
if (commands.security) {
gate += '### Security Checks\n';
gate += '```bash\n';
commands.security.forEach((cmd) => {
gate += `${cmd}\n`;
});
gate += '```\n\n';
}
gate += 'ā
All commands must pass before considering the implementation complete.\n';
return gate;
}
}
exports.ValidationExecutor = ValidationExecutor;
//# sourceMappingURL=validationExecutor.js.map