vibecodingmachine-core 2026.2.20-438 → 2026.2.26-1739
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 +240 -0
- package/package.json +10 -2
- package/src/agents/Agent.js +300 -0
- package/src/agents/AgentAdditionService.js +311 -0
- package/src/agents/AgentCheckService.js +690 -0
- package/src/agents/AgentInstallationService.js +140 -0
- package/src/agents/AgentSetupService.js +467 -0
- package/src/agents/AgentStatus.js +183 -0
- package/src/agents/AgentVerificationService.js +634 -0
- package/src/agents/ConfigurationSchemaValidator.js +543 -0
- package/src/agents/EnvironmentConfigurationManager.js +602 -0
- package/src/agents/InstallationErrorHandler.js +372 -0
- package/src/agents/InstallationLog.js +363 -0
- package/src/agents/InstallationMethod.js +510 -0
- package/src/agents/InstallationOrchestrator.js +352 -0
- package/src/agents/InstallationProgressReporter.js +372 -0
- package/src/agents/InstallationRetryManager.js +322 -0
- package/src/agents/InstallationType.js +254 -0
- package/src/agents/OperationTypes.js +310 -0
- package/src/agents/PerformanceMetricsCollector.js +493 -0
- package/src/agents/SecurityValidationService.js +534 -0
- package/src/agents/VerificationTest.js +354 -0
- package/src/agents/VerificationType.js +226 -0
- package/src/agents/WindowsPermissionHandler.js +518 -0
- package/src/agents/config/AgentConfigManager.js +393 -0
- package/src/agents/config/AgentDefaultsRegistry.js +373 -0
- package/src/agents/config/ConfigValidator.js +281 -0
- package/src/agents/discovery/AgentDiscoveryService.js +707 -0
- package/src/agents/logging/AgentLogger.js +511 -0
- package/src/agents/status/AgentStatusManager.js +481 -0
- package/src/agents/storage/FileManager.js +454 -0
- package/src/agents/verification/AgentCommunicationTester.js +474 -0
- package/src/agents/verification/BaseVerifier.js +430 -0
- package/src/agents/verification/CommandVerifier.js +480 -0
- package/src/agents/verification/FileOperationVerifier.js +453 -0
- package/src/agents/verification/ResultAnalyzer.js +707 -0
- package/src/agents/verification/TestRequirementManager.js +495 -0
- package/src/agents/verification/VerificationRunner.js +433 -0
- package/src/agents/windows/BaseWindowsInstaller.js +441 -0
- package/src/agents/windows/ChocolateyInstaller.js +509 -0
- package/src/agents/windows/DirectInstaller.js +443 -0
- package/src/agents/windows/InstallerFactory.js +391 -0
- package/src/agents/windows/NpmInstaller.js +505 -0
- package/src/agents/windows/PowerShellInstaller.js +458 -0
- package/src/agents/windows/WinGetInstaller.js +390 -0
- package/src/analysis/analysis-reporter.js +132 -0
- package/src/analysis/boundary-detector.js +712 -0
- package/src/analysis/categorizer.js +340 -0
- package/src/analysis/codebase-scanner.js +384 -0
- package/src/analysis/line-counter.js +513 -0
- package/src/analysis/priority-calculator.js +679 -0
- package/src/analysis/report/analysis-report.js +250 -0
- package/src/analysis/report/package-analyzer.js +278 -0
- package/src/analysis/report/recommendation-generator.js +382 -0
- package/src/analysis/report/statistics-generator.js +515 -0
- package/src/analysis/reports/analysis-report-model.js +101 -0
- package/src/analysis/reports/recommendation-generator.js +283 -0
- package/src/analysis/reports/report-generators.js +191 -0
- package/src/analysis/reports/statistics-calculator.js +231 -0
- package/src/analysis/reports/trend-analyzer.js +219 -0
- package/src/analysis/strategy-generator.js +814 -0
- package/src/auto-mode/AutoModeBusinessLogic.js +836 -0
- package/src/config/refactoring-config.js +307 -0
- package/src/health-tracking/json-storage.js +38 -2
- package/src/ide-integration/applescript-manager-core.js +233 -0
- package/src/ide-integration/applescript-manager.cjs +357 -28
- package/src/ide-integration/applescript-manager.js +89 -3599
- package/src/ide-integration/cdp-manager.js +306 -0
- package/src/ide-integration/claude-code-cli-manager.cjs +1 -1
- package/src/ide-integration/continuation-handler.js +337 -0
- package/src/ide-integration/ide-status-checker.js +292 -0
- package/src/ide-integration/macos-ide-manager.js +627 -0
- package/src/ide-integration/macos-text-sender.js +528 -0
- package/src/ide-integration/response-reader.js +548 -0
- package/src/ide-integration/windows-automation-manager.js +121 -0
- package/src/ide-integration/windows-ide-manager.js +373 -0
- package/src/index.cjs +25 -3
- package/src/index.js +15 -1
- package/src/llm/direct-llm-manager.cjs +90 -2
- package/src/models/compliance-report.js +538 -0
- package/src/models/file-analysis.js +681 -0
- package/src/models/refactoring-plan.js +770 -0
- package/src/monitoring/alert-system.js +834 -0
- package/src/monitoring/compliance-progress-tracker.js +437 -0
- package/src/monitoring/continuous-scan-notifications.js +661 -0
- package/src/monitoring/continuous-scanner.js +279 -0
- package/src/monitoring/file-monitor/file-analyzer.js +262 -0
- package/src/monitoring/file-monitor/file-monitor.js +237 -0
- package/src/monitoring/file-monitor/watcher.js +194 -0
- package/src/monitoring/file-monitor.js +17 -0
- package/src/monitoring/notification-manager.js +437 -0
- package/src/monitoring/scanner-core.js +368 -0
- package/src/monitoring/scanner-events.js +214 -0
- package/src/monitoring/violation-notification-system.js +515 -0
- package/src/refactoring/boundaries/cohesion-analyzer.js +316 -0
- package/src/refactoring/boundaries/extraction-result.js +285 -0
- package/src/refactoring/boundaries/extraction-strategies.js +392 -0
- package/src/refactoring/boundaries/module-boundary.js +209 -0
- package/src/refactoring/boundary/boundary-detector.js +741 -0
- package/src/refactoring/boundary/boundary-types.js +405 -0
- package/src/refactoring/boundary/extraction-strategies.js +554 -0
- package/src/refactoring/boundary-extraction-result.js +77 -0
- package/src/refactoring/boundary-extraction-strategies.js +330 -0
- package/src/refactoring/boundary-extractor.js +384 -0
- package/src/refactoring/boundary-types.js +46 -0
- package/src/refactoring/circular/circular-dependency.js +88 -0
- package/src/refactoring/circular/cycle-detection.js +147 -0
- package/src/refactoring/circular/dependency-node.js +82 -0
- package/src/refactoring/circular/dependency-result.js +107 -0
- package/src/refactoring/circular/dependency-types.js +58 -0
- package/src/refactoring/circular/graph-builder.js +213 -0
- package/src/refactoring/circular/resolution-strategy.js +72 -0
- package/src/refactoring/circular/strategy-generator.js +229 -0
- package/src/refactoring/circular-dependency-resolver-original.js +809 -0
- package/src/refactoring/circular-dependency-resolver.js +200 -0
- package/src/refactoring/code-mover.js +761 -0
- package/src/refactoring/file-splitter.js +696 -0
- package/src/refactoring/functionality-validator.js +816 -0
- package/src/refactoring/import-manager.js +774 -0
- package/src/refactoring/module-boundary.js +107 -0
- package/src/refactoring/refactoring-executor.js +672 -0
- package/src/refactoring/refactoring-rollback.js +614 -0
- package/src/refactoring/test-validator.js +631 -0
- package/src/requirement-management/default-requirement-manager.js +321 -0
- package/src/requirement-management/requirement-file-parser.js +159 -0
- package/src/requirement-management/requirement-sequencer.js +221 -0
- package/src/rui/commands/AgentCommandParser.js +600 -0
- package/src/rui/commands/AgentCommands.js +487 -0
- package/src/rui/commands/AgentResponseFormatter.js +832 -0
- package/src/scripts/verify-full-compliance.js +269 -0
- package/src/sync/sync-engine-core.js +1 -0
- package/src/sync/sync-engine-remote-handlers.js +135 -0
- package/src/task-generation/automated-task-generator.js +351 -0
- package/src/task-generation/prioritizer.js +287 -0
- package/src/task-generation/task-list-updater.js +215 -0
- package/src/task-generation/task-management-integration.js +480 -0
- package/src/task-generation/task-manager-integration.js +270 -0
- package/src/task-generation/violation-task-generator.js +474 -0
- package/src/task-management/continuous-scan-integration.js +342 -0
- package/src/timeout-management/index.js +12 -3
- package/src/timeout-management/response-time-tracker.js +167 -0
- package/src/timeout-management/timeout-calculator.js +159 -0
- package/src/timeout-management/timeout-config-manager.js +172 -0
- package/src/utils/ast-analyzer.js +417 -0
- package/src/utils/current-requirement-manager.js +276 -0
- package/src/utils/current-requirement-operations.js +472 -0
- package/src/utils/dependency-mapper.js +456 -0
- package/src/utils/download-with-progress.js +4 -2
- package/src/utils/electron-update-checker.js +4 -1
- package/src/utils/file-size-analyzer.js +272 -0
- package/src/utils/import-updater.js +280 -0
- package/src/utils/refactoring-tools.js +512 -0
- package/src/utils/report-generator.js +569 -0
- package/src/utils/reports/report-analysis.js +218 -0
- package/src/utils/reports/report-types.js +55 -0
- package/src/utils/reports/summary-generators.js +102 -0
- package/src/utils/requirement-file-management.js +157 -0
- package/src/utils/requirement-helpers/requirement-file-ops.js +392 -0
- package/src/utils/requirement-helpers/requirement-mover.js +414 -0
- package/src/utils/requirement-helpers/requirement-parser.js +326 -0
- package/src/utils/requirement-helpers/requirement-status.js +320 -0
- package/src/utils/requirement-helpers-new.js +55 -0
- package/src/utils/requirement-helpers-refactored.js +367 -0
- package/src/utils/requirement-helpers.js +291 -1191
- package/src/utils/requirement-movement-operations.js +450 -0
- package/src/utils/requirement-movement.js +312 -0
- package/src/utils/requirement-parsing-helpers.js +56 -0
- package/src/utils/requirement-statistics.js +200 -0
- package/src/utils/requirement-text-utils.js +58 -0
- package/src/utils/rollback/rollback-handlers.js +125 -0
- package/src/utils/rollback/rollback-operation.js +63 -0
- package/src/utils/rollback/rollback-recorder.js +166 -0
- package/src/utils/rollback/rollback-state-manager.js +175 -0
- package/src/utils/rollback/rollback-types.js +33 -0
- package/src/utils/rollback/rollback-utils.js +110 -0
- package/src/utils/rollback-manager-original.js +569 -0
- package/src/utils/rollback-manager.js +202 -0
- package/src/utils/smoke-test-cli.js +362 -0
- package/src/utils/smoke-test-gui.js +351 -0
- package/src/utils/smoke-test-orchestrator.js +321 -0
- package/src/utils/smoke-test-runner.js +60 -0
- package/src/utils/smoke-test-web.js +347 -0
- package/src/utils/specification-helpers.js +39 -13
- package/src/utils/specification-migration.js +97 -0
- package/src/utils/test-runner.js +579 -0
- package/src/utils/validation-framework.js +518 -0
- package/src/validation/compliance-analyzer.js +197 -0
- package/src/validation/compliance-report-generator.js +343 -0
- package/src/validation/compliance-reporter.js +711 -0
- package/src/validation/compliance-rules.js +127 -0
- package/src/validation/constitution-validator-new.js +196 -0
- package/src/validation/constitution-validator.js +17 -0
- package/src/validation/file-validators.js +170 -0
- package/src/validation/line-limit/file-analyzer.js +201 -0
- package/src/validation/line-limit/line-limit-validator.js +208 -0
- package/src/validation/line-limit/validation-result.js +144 -0
- package/src/validation/line-limit-core.js +225 -0
- package/src/validation/line-limit-reporter.js +134 -0
- package/src/validation/line-limit-result.js +125 -0
- package/src/validation/line-limit-validator.js +41 -0
- package/src/validation/metrics-calculator.js +660 -0
- package/src/sync/sync-engine-backup.js +0 -559
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Smoke Test Runner
|
|
5
|
+
*
|
|
6
|
+
* Runs smoke tests before and after refactoring to ensure interfaces continue working
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { SmokeTestOrchestrator } = require('./smoke-test-orchestrator');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const phase = args[0] || 'pre-refactor';
|
|
16
|
+
const verbose = args.includes('--verbose') || args.includes('-v');
|
|
17
|
+
const saveReport = args.includes('--save-report');
|
|
18
|
+
|
|
19
|
+
const orchestrator = new SmokeTestOrchestrator({
|
|
20
|
+
verbose,
|
|
21
|
+
rollbackTrigger: async (context) => {
|
|
22
|
+
console.error('🚨 SMOKE TESTS FAILED - ROLLBACK TRIGGERED');
|
|
23
|
+
console.error('Phase:', context.phase);
|
|
24
|
+
console.error('Reason:', context.reason);
|
|
25
|
+
|
|
26
|
+
// In a real implementation, this would trigger git rollback
|
|
27
|
+
console.error('🔄 Rollback would be triggered here');
|
|
28
|
+
|
|
29
|
+
// Exit with error code to indicate failure
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
console.log(`🔥 Running ${phase} smoke tests...`);
|
|
36
|
+
const results = await orchestrator.runSmokeTests(phase);
|
|
37
|
+
|
|
38
|
+
if (saveReport) {
|
|
39
|
+
const reportPath = path.join(__dirname, `../reports/smoke-test-${phase}-${Date.now()}.json`);
|
|
40
|
+
await orchestrator.saveReport(results, reportPath);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (results.success) {
|
|
44
|
+
console.log('✅ All smoke tests passed!');
|
|
45
|
+
process.exit(0);
|
|
46
|
+
} else {
|
|
47
|
+
console.log('❌ Some smoke tests failed!');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('💥 Smoke test runner error:', error.message);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (require.main === module) {
|
|
57
|
+
main();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { main };
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web Smoke Testing Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides smoke testing functionality for the Web interface
|
|
5
|
+
* to ensure it starts and responds to basic requests.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
const http = require('http');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
class WebSmokeTest {
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.webPath = options.webPath || path.join(__dirname, '../../../web');
|
|
16
|
+
this.port = options.port || 3000;
|
|
17
|
+
this.host = options.host || 'localhost';
|
|
18
|
+
this.timeout = options.timeout || 60000; // 60 seconds for web server startup
|
|
19
|
+
this.verbose = options.verbose || false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Run comprehensive Web smoke tests
|
|
24
|
+
* @returns {Promise<Object>} Test results
|
|
25
|
+
*/
|
|
26
|
+
async runSmokeTests() {
|
|
27
|
+
const results = {
|
|
28
|
+
passed: 0,
|
|
29
|
+
failed: 0,
|
|
30
|
+
total: 0,
|
|
31
|
+
details: []
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const tests = [
|
|
35
|
+
{ name: 'Web Directory Exists', test: () => this.testWebDirectoryExists() },
|
|
36
|
+
{ name: 'Package.json Exists', test: () => this.testPackageJsonExists() },
|
|
37
|
+
{ name: 'Dependencies Available', test: () => this.testDependenciesAvailable() },
|
|
38
|
+
{ name: 'Web Server Starts', test: () => this.testWebServerStarts() },
|
|
39
|
+
{ name: 'Basic HTTP Response', test: () => this.testBasicHttpResponse() }
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
for (const test of tests) {
|
|
43
|
+
results.total++;
|
|
44
|
+
try {
|
|
45
|
+
const result = await test.test();
|
|
46
|
+
if (result.passed) {
|
|
47
|
+
results.passed++;
|
|
48
|
+
if (this.verbose) {
|
|
49
|
+
console.log(`✅ ${test.name}: PASSED`);
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
results.failed++;
|
|
53
|
+
console.error(`❌ ${test.name}: FAILED - ${result.error}`);
|
|
54
|
+
}
|
|
55
|
+
results.details.push({
|
|
56
|
+
name: test.name,
|
|
57
|
+
passed: result.passed,
|
|
58
|
+
error: result.error,
|
|
59
|
+
details: result.details
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
results.failed++;
|
|
63
|
+
console.error(`❌ ${test.name}: ERROR - ${error.message}`);
|
|
64
|
+
results.details.push({
|
|
65
|
+
name: test.name,
|
|
66
|
+
passed: false,
|
|
67
|
+
error: error.message,
|
|
68
|
+
details: null
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
success: results.failed === 0,
|
|
75
|
+
...results
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Test if Web directory exists
|
|
81
|
+
*/
|
|
82
|
+
async testWebDirectoryExists() {
|
|
83
|
+
try {
|
|
84
|
+
const exists = fs.existsSync(this.webPath);
|
|
85
|
+
if (!exists) {
|
|
86
|
+
return { passed: false, error: `Web directory not found: ${this.webPath}` };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const stats = fs.statSync(this.webPath);
|
|
90
|
+
if (!stats.isDirectory()) {
|
|
91
|
+
return { passed: false, error: `Web path is not a directory: ${this.webPath}` };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { passed: true, details: { path: this.webPath } };
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return { passed: false, error: error.message };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Test if package.json exists in Web directory
|
|
102
|
+
*/
|
|
103
|
+
async testPackageJsonExists() {
|
|
104
|
+
try {
|
|
105
|
+
const packageJsonPath = path.join(this.webPath, 'package.json');
|
|
106
|
+
const exists = fs.existsSync(packageJsonPath);
|
|
107
|
+
if (!exists) {
|
|
108
|
+
return { passed: false, error: `package.json not found: ${packageJsonPath}` };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
112
|
+
return {
|
|
113
|
+
passed: true,
|
|
114
|
+
details: {
|
|
115
|
+
name: packageJson.name,
|
|
116
|
+
version: packageJson.version,
|
|
117
|
+
hasScripts: !!packageJson.scripts,
|
|
118
|
+
scriptCount: packageJson.scripts ? Object.keys(packageJson.scripts).length : 0
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return { passed: false, error: `package.json error: ${error.message}` };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Test if dependencies are available
|
|
128
|
+
*/
|
|
129
|
+
async testDependenciesAvailable() {
|
|
130
|
+
try {
|
|
131
|
+
const packageJsonPath = path.join(this.webPath, 'package.json');
|
|
132
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
133
|
+
const nodeModulesPath = path.join(this.webPath, 'node_modules');
|
|
134
|
+
|
|
135
|
+
const nodeModulesExists = fs.existsSync(nodeModulesPath);
|
|
136
|
+
const hasDependencies = packageJson.dependencies && Object.keys(packageJson.dependencies).length > 0;
|
|
137
|
+
|
|
138
|
+
if (!nodeModulesExists && hasDependencies) {
|
|
139
|
+
return {
|
|
140
|
+
passed: false,
|
|
141
|
+
error: 'node_modules directory not found but dependencies exist in package.json'
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check for common web dependencies
|
|
146
|
+
const commonDeps = ['react', 'express', 'next', 'vue', 'angular'];
|
|
147
|
+
const foundDeps = commonDeps.filter(dep =>
|
|
148
|
+
packageJson.dependencies && packageJson.dependencies[dep]
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
passed: true,
|
|
153
|
+
details: {
|
|
154
|
+
nodeModulesExists,
|
|
155
|
+
dependencyCount: hasDependencies ? Object.keys(packageJson.dependencies).length : 0,
|
|
156
|
+
commonDeps: foundDeps
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return { passed: false, error: `Dependency check error: ${error.message}` };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Test if web server can start
|
|
166
|
+
*/
|
|
167
|
+
async testWebServerStarts() {
|
|
168
|
+
return new Promise((resolve) => {
|
|
169
|
+
const startTime = Date.now();
|
|
170
|
+
|
|
171
|
+
// Check for common start scripts
|
|
172
|
+
const packageJsonPath = path.join(this.webPath, 'package.json');
|
|
173
|
+
let startScript = 'start';
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
177
|
+
if (packageJson.scripts) {
|
|
178
|
+
if (packageJson.scripts.dev) startScript = 'dev';
|
|
179
|
+
else if (packageJson.scripts.start) startScript = 'start';
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
// Continue with default
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const child = spawn('npm', ['run', startScript], {
|
|
186
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
187
|
+
cwd: this.webPath,
|
|
188
|
+
env: { ...process.env, PORT: this.port.toString() }
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
let stdout = '';
|
|
192
|
+
let stderr = '';
|
|
193
|
+
|
|
194
|
+
child.stdout.on('data', (data) => {
|
|
195
|
+
stdout += data.toString();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
child.stderr.on('data', (data) => {
|
|
199
|
+
stderr += data.toString();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const timeoutId = setTimeout(() => {
|
|
203
|
+
child.kill('SIGTERM');
|
|
204
|
+
resolve({
|
|
205
|
+
passed: false,
|
|
206
|
+
error: `Web server startup timeout after ${this.timeout}ms`,
|
|
207
|
+
details: { stdout, stderr }
|
|
208
|
+
});
|
|
209
|
+
}, this.timeout);
|
|
210
|
+
|
|
211
|
+
// Listen for successful startup indicators
|
|
212
|
+
const startupPatterns = [
|
|
213
|
+
/server.*running/i,
|
|
214
|
+
/listening.*port/i,
|
|
215
|
+
/ready.*server/i,
|
|
216
|
+
/compiled.*successfully/i,
|
|
217
|
+
new RegExp(`localhost:${this.port}`, 'i')
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
child.stdout.on('data', (data) => {
|
|
221
|
+
const output = data.toString();
|
|
222
|
+
if (startupPatterns.some(pattern => pattern.test(output))) {
|
|
223
|
+
clearTimeout(timeoutId);
|
|
224
|
+
child.kill('SIGTERM');
|
|
225
|
+
const duration = Date.now() - startTime;
|
|
226
|
+
resolve({
|
|
227
|
+
passed: true,
|
|
228
|
+
details: {
|
|
229
|
+
startupOutput: output.trim(),
|
|
230
|
+
duration,
|
|
231
|
+
script: startScript
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
child.on('close', (code) => {
|
|
238
|
+
clearTimeout(timeoutId);
|
|
239
|
+
const duration = Date.now() - startTime;
|
|
240
|
+
|
|
241
|
+
if (code === 0) {
|
|
242
|
+
resolve({
|
|
243
|
+
passed: true,
|
|
244
|
+
details: {
|
|
245
|
+
exitCode: code,
|
|
246
|
+
duration,
|
|
247
|
+
script: startScript
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
} else {
|
|
251
|
+
resolve({
|
|
252
|
+
passed: false,
|
|
253
|
+
error: `Web server failed to start - exit code: ${code}`,
|
|
254
|
+
details: { exitCode: code, stdout, stderr, duration, script: startScript }
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
child.on('error', (error) => {
|
|
260
|
+
clearTimeout(timeoutId);
|
|
261
|
+
resolve({
|
|
262
|
+
passed: false,
|
|
263
|
+
error: `Web server start error: ${error.message}`,
|
|
264
|
+
details: { error: error.message }
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Test basic HTTP response from web server
|
|
272
|
+
*/
|
|
273
|
+
async testBasicHttpResponse() {
|
|
274
|
+
return new Promise((resolve) => {
|
|
275
|
+
const startTime = Date.now();
|
|
276
|
+
const requestOptions = {
|
|
277
|
+
hostname: this.host,
|
|
278
|
+
port: this.port,
|
|
279
|
+
path: '/',
|
|
280
|
+
method: 'GET',
|
|
281
|
+
timeout: 10000
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const req = http.request(requestOptions, (res) => {
|
|
285
|
+
let data = '';
|
|
286
|
+
|
|
287
|
+
res.on('data', (chunk) => {
|
|
288
|
+
data += chunk;
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
res.on('end', () => {
|
|
292
|
+
const duration = Date.now() - startTime;
|
|
293
|
+
|
|
294
|
+
// Consider 2xx and 3xx status codes as success
|
|
295
|
+
if (res.statusCode >= 200 && res.statusCode < 400) {
|
|
296
|
+
resolve({
|
|
297
|
+
passed: true,
|
|
298
|
+
details: {
|
|
299
|
+
statusCode: res.statusCode,
|
|
300
|
+
headers: res.headers,
|
|
301
|
+
contentLength: data.length,
|
|
302
|
+
duration
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
} else {
|
|
306
|
+
resolve({
|
|
307
|
+
passed: false,
|
|
308
|
+
error: `HTTP response status: ${res.statusCode}`,
|
|
309
|
+
details: { statusCode: res.statusCode, headers: res.headers, duration }
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
req.on('error', (error) => {
|
|
316
|
+
const duration = Date.now() - startTime;
|
|
317
|
+
|
|
318
|
+
// Common errors that indicate server isn't running
|
|
319
|
+
if (error.code === 'ECONNREFUSED' || error.code === 'ECONNRESET') {
|
|
320
|
+
resolve({
|
|
321
|
+
passed: false,
|
|
322
|
+
error: `Web server not responding on ${this.host}:${this.port}`,
|
|
323
|
+
details: { error: error.code, duration }
|
|
324
|
+
});
|
|
325
|
+
} else {
|
|
326
|
+
resolve({
|
|
327
|
+
passed: false,
|
|
328
|
+
error: `HTTP request error: ${error.message}`,
|
|
329
|
+
details: { error: error.message, duration }
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
req.on('timeout', () => {
|
|
335
|
+
req.destroy();
|
|
336
|
+
resolve({
|
|
337
|
+
passed: false,
|
|
338
|
+
error: `HTTP request timeout after 10 seconds`
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
req.end();
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
module.exports = { WebSmokeTest };
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs-extra');
|
|
9
9
|
const path = require('path');
|
|
10
|
+
const { migrateAllDisabledSpecs, isMigrationNeeded } = require('./specification-migration');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Extract the title from spec.md content (first H1 or H2)
|
|
@@ -79,11 +80,12 @@ async function isSpecComplete(specPath) {
|
|
|
79
80
|
* Get all specifications in repo's specs/ directory
|
|
80
81
|
* @param {string} repoPath
|
|
81
82
|
* @param {Object} [options] - Optional configuration
|
|
82
|
-
* @param {boolean} [options.skipDisabled=false] - Skip specs with DISABLED
|
|
83
|
-
* @
|
|
83
|
+
* @param {boolean} [options.skipDisabled=false] - Skip specs with DISABLED.txt file
|
|
84
|
+
* @param {boolean} [options.includeWithoutSpec=false] - Include spec directories even without spec.md
|
|
85
|
+
* @returns {Promise<Array<{directory, title, path, hasPlan, hasPlanPrompt, hasTasks, hasSpec}>>}
|
|
84
86
|
*/
|
|
85
87
|
async function getAllSpecifications(repoPath, options = {}) {
|
|
86
|
-
const { skipDisabled = false } = options;
|
|
88
|
+
const { skipDisabled = false, includeWithoutSpec = false } = options;
|
|
87
89
|
const specsDir = path.join(repoPath, 'specs');
|
|
88
90
|
if (!await fs.pathExists(specsDir)) return [];
|
|
89
91
|
|
|
@@ -91,25 +93,28 @@ async function getAllSpecifications(repoPath, options = {}) {
|
|
|
91
93
|
const specs = [];
|
|
92
94
|
|
|
93
95
|
for (const entry of entries) {
|
|
94
|
-
// Skip disabled specs if skipDisabled is true
|
|
95
|
-
if (skipDisabled && entry.startsWith('DISABLED-')) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
96
|
const specPath = path.join(specsDir, entry);
|
|
100
97
|
const stat = await fs.stat(specPath).catch(() => null);
|
|
101
98
|
if (!stat || !stat.isDirectory()) continue;
|
|
102
99
|
|
|
100
|
+
// Skip disabled specs if skipDisabled is true and DISABLED.txt exists or directory starts with DISABLED-
|
|
101
|
+
if (skipDisabled && (await fs.pathExists(path.join(specPath, 'DISABLED.txt')) || entry.startsWith('DISABLED-'))) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
103
105
|
const specMdPath = path.join(specPath, 'spec.md');
|
|
104
|
-
|
|
106
|
+
const hasSpec = await fs.pathExists(specMdPath);
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
// Skip specs without spec.md unless includeWithoutSpec is true
|
|
109
|
+
if (!hasSpec && !includeWithoutSpec) continue;
|
|
110
|
+
|
|
111
|
+
const content = hasSpec ? await fs.readFile(specMdPath, 'utf8').catch(() => '') : '';
|
|
112
|
+
const title = extractSpecTitle(content, entry) || entry;
|
|
108
113
|
const hasPlan = await fs.pathExists(path.join(specPath, 'plan.md'));
|
|
109
114
|
const hasPlanPrompt = await fs.pathExists(path.join(specPath, 'plan-prompt.md'));
|
|
110
115
|
const hasTasks = await fs.pathExists(path.join(specPath, 'tasks.md'));
|
|
111
116
|
|
|
112
|
-
specs.push({ directory: entry, title, path: specPath, hasPlan, hasPlanPrompt, hasTasks });
|
|
117
|
+
specs.push({ directory: entry, title, path: specPath, hasPlan, hasPlanPrompt, hasTasks, hasSpec });
|
|
113
118
|
}
|
|
114
119
|
|
|
115
120
|
return specs.sort((a, b) => a.directory.localeCompare(b.directory));
|
|
@@ -202,7 +207,27 @@ async function updateSpecification(specPath, content) {
|
|
|
202
207
|
const specMdPath = path.join(specPath, 'spec.md');
|
|
203
208
|
await fs.writeFile(specMdPath, content, 'utf8');
|
|
204
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Initialize specification system (e.g., run migrations if needed)
|
|
212
|
+
* Call this once during application startup
|
|
213
|
+
* @param {string} repoPath - root repository path
|
|
214
|
+
* @returns {Promise<{migrationRan: boolean, migrationResults: Object|null}>}
|
|
215
|
+
*/
|
|
216
|
+
async function initializeSpecifications(repoPath) {
|
|
217
|
+
try {
|
|
218
|
+
const needsMigration = await isMigrationNeeded(repoPath);
|
|
205
219
|
|
|
220
|
+
if (needsMigration) {
|
|
221
|
+
const migrationResults = await migrateAllDisabledSpecs(repoPath);
|
|
222
|
+
return { migrationRan: true, migrationResults };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { migrationRan: false, migrationResults: null };
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error('Error during specification initialization:', error);
|
|
228
|
+
return { migrationRan: false, migrationResults: null, error: error.message };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
206
231
|
module.exports = {
|
|
207
232
|
extractSpecTitle,
|
|
208
233
|
extractCompletionPercentage,
|
|
@@ -212,5 +237,6 @@ module.exports = {
|
|
|
212
237
|
getNextSpecificationNumber,
|
|
213
238
|
getTodoSpecifications,
|
|
214
239
|
createSpecification,
|
|
215
|
-
updateSpecification
|
|
240
|
+
updateSpecification,
|
|
241
|
+
initializeSpecifications
|
|
216
242
|
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specification Migration Utilities
|
|
3
|
+
* Handles migration from old DISABLED- prefix naming convention
|
|
4
|
+
* to the new DISABLED.txt file-based disable tracking.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Migrate a single specification from DISABLED- prefix to DISABLED.txt file
|
|
12
|
+
* @param {string} specsDir - path to specs/ directory
|
|
13
|
+
* @param {string} dirName - directory name (e.g., "DISABLED-001-example")
|
|
14
|
+
* @returns {Promise<{success: boolean, oldName: string, newName: string|null, error: string|null}>}
|
|
15
|
+
*/
|
|
16
|
+
async function migrateDisabledSpec(specsDir, dirName) {
|
|
17
|
+
if (!dirName.startsWith('DISABLED-')) {
|
|
18
|
+
return { success: false, oldName: dirName, newName: null, error: 'Not a DISABLED- directory' };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const oldPath = path.join(specsDir, dirName);
|
|
22
|
+
const newName = dirName.substring(9); // Remove "DISABLED-" prefix
|
|
23
|
+
const newPath = path.join(specsDir, newName);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Check if spec.md exists (only migrate valid specs)
|
|
27
|
+
const specMdPath = path.join(oldPath, 'spec.md');
|
|
28
|
+
if (!await fs.pathExists(specMdPath)) {
|
|
29
|
+
return { success: false, oldName: dirName, newName: null, error: 'No spec.md found' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Create DISABLED.txt in the old directory before renaming
|
|
33
|
+
const disabledFilePath = path.join(oldPath, 'DISABLED.txt');
|
|
34
|
+
await fs.writeFile(disabledFilePath, '');
|
|
35
|
+
|
|
36
|
+
// Rename directory to remove DISABLED- prefix
|
|
37
|
+
await fs.rename(oldPath, newPath);
|
|
38
|
+
|
|
39
|
+
return { success: true, oldName: dirName, newName, error: null };
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return { success: false, oldName: dirName, newName: null, error: error.message };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Migrate all DISABLED- prefixed specs to the new DISABLED.txt format
|
|
47
|
+
* @param {string} repoPath - root repository path
|
|
48
|
+
* @returns {Promise<{totalFound: number, migrated: number, failed: number, results: Array}>}
|
|
49
|
+
*/
|
|
50
|
+
async function migrateAllDisabledSpecs(repoPath) {
|
|
51
|
+
const specsDir = path.join(repoPath, 'specs');
|
|
52
|
+
|
|
53
|
+
if (!await fs.pathExists(specsDir)) {
|
|
54
|
+
return { totalFound: 0, migrated: 0, failed: 0, results: [] };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const entries = await fs.readdir(specsDir);
|
|
58
|
+
const disabledDirs = entries.filter(entry => entry.startsWith('DISABLED-'));
|
|
59
|
+
|
|
60
|
+
const results = [];
|
|
61
|
+
for (const dirName of disabledDirs) {
|
|
62
|
+
const result = await migrateDisabledSpec(specsDir, dirName);
|
|
63
|
+
results.push(result);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const migrated = results.filter(r => r.success).length;
|
|
67
|
+
const failed = results.filter(r => !r.success).length;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
totalFound: disabledDirs.length,
|
|
71
|
+
migrated,
|
|
72
|
+
failed,
|
|
73
|
+
results
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if migration is needed (any DISABLED- prefixed directories exist)
|
|
79
|
+
* @param {string} repoPath - root repository path
|
|
80
|
+
* @returns {Promise<boolean>}
|
|
81
|
+
*/
|
|
82
|
+
async function isMigrationNeeded(repoPath) {
|
|
83
|
+
const specsDir = path.join(repoPath, 'specs');
|
|
84
|
+
|
|
85
|
+
if (!await fs.pathExists(specsDir)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const entries = await fs.readdir(specsDir);
|
|
90
|
+
return entries.some(entry => entry.startsWith('DISABLED-'));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = {
|
|
94
|
+
migrateDisabledSpec,
|
|
95
|
+
migrateAllDisabledSpecs,
|
|
96
|
+
isMigrationNeeded
|
|
97
|
+
};
|