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.
Files changed (202) hide show
  1. package/README.md +240 -0
  2. package/package.json +10 -2
  3. package/src/agents/Agent.js +300 -0
  4. package/src/agents/AgentAdditionService.js +311 -0
  5. package/src/agents/AgentCheckService.js +690 -0
  6. package/src/agents/AgentInstallationService.js +140 -0
  7. package/src/agents/AgentSetupService.js +467 -0
  8. package/src/agents/AgentStatus.js +183 -0
  9. package/src/agents/AgentVerificationService.js +634 -0
  10. package/src/agents/ConfigurationSchemaValidator.js +543 -0
  11. package/src/agents/EnvironmentConfigurationManager.js +602 -0
  12. package/src/agents/InstallationErrorHandler.js +372 -0
  13. package/src/agents/InstallationLog.js +363 -0
  14. package/src/agents/InstallationMethod.js +510 -0
  15. package/src/agents/InstallationOrchestrator.js +352 -0
  16. package/src/agents/InstallationProgressReporter.js +372 -0
  17. package/src/agents/InstallationRetryManager.js +322 -0
  18. package/src/agents/InstallationType.js +254 -0
  19. package/src/agents/OperationTypes.js +310 -0
  20. package/src/agents/PerformanceMetricsCollector.js +493 -0
  21. package/src/agents/SecurityValidationService.js +534 -0
  22. package/src/agents/VerificationTest.js +354 -0
  23. package/src/agents/VerificationType.js +226 -0
  24. package/src/agents/WindowsPermissionHandler.js +518 -0
  25. package/src/agents/config/AgentConfigManager.js +393 -0
  26. package/src/agents/config/AgentDefaultsRegistry.js +373 -0
  27. package/src/agents/config/ConfigValidator.js +281 -0
  28. package/src/agents/discovery/AgentDiscoveryService.js +707 -0
  29. package/src/agents/logging/AgentLogger.js +511 -0
  30. package/src/agents/status/AgentStatusManager.js +481 -0
  31. package/src/agents/storage/FileManager.js +454 -0
  32. package/src/agents/verification/AgentCommunicationTester.js +474 -0
  33. package/src/agents/verification/BaseVerifier.js +430 -0
  34. package/src/agents/verification/CommandVerifier.js +480 -0
  35. package/src/agents/verification/FileOperationVerifier.js +453 -0
  36. package/src/agents/verification/ResultAnalyzer.js +707 -0
  37. package/src/agents/verification/TestRequirementManager.js +495 -0
  38. package/src/agents/verification/VerificationRunner.js +433 -0
  39. package/src/agents/windows/BaseWindowsInstaller.js +441 -0
  40. package/src/agents/windows/ChocolateyInstaller.js +509 -0
  41. package/src/agents/windows/DirectInstaller.js +443 -0
  42. package/src/agents/windows/InstallerFactory.js +391 -0
  43. package/src/agents/windows/NpmInstaller.js +505 -0
  44. package/src/agents/windows/PowerShellInstaller.js +458 -0
  45. package/src/agents/windows/WinGetInstaller.js +390 -0
  46. package/src/analysis/analysis-reporter.js +132 -0
  47. package/src/analysis/boundary-detector.js +712 -0
  48. package/src/analysis/categorizer.js +340 -0
  49. package/src/analysis/codebase-scanner.js +384 -0
  50. package/src/analysis/line-counter.js +513 -0
  51. package/src/analysis/priority-calculator.js +679 -0
  52. package/src/analysis/report/analysis-report.js +250 -0
  53. package/src/analysis/report/package-analyzer.js +278 -0
  54. package/src/analysis/report/recommendation-generator.js +382 -0
  55. package/src/analysis/report/statistics-generator.js +515 -0
  56. package/src/analysis/reports/analysis-report-model.js +101 -0
  57. package/src/analysis/reports/recommendation-generator.js +283 -0
  58. package/src/analysis/reports/report-generators.js +191 -0
  59. package/src/analysis/reports/statistics-calculator.js +231 -0
  60. package/src/analysis/reports/trend-analyzer.js +219 -0
  61. package/src/analysis/strategy-generator.js +814 -0
  62. package/src/auto-mode/AutoModeBusinessLogic.js +836 -0
  63. package/src/config/refactoring-config.js +307 -0
  64. package/src/health-tracking/json-storage.js +38 -2
  65. package/src/ide-integration/applescript-manager-core.js +233 -0
  66. package/src/ide-integration/applescript-manager.cjs +357 -28
  67. package/src/ide-integration/applescript-manager.js +89 -3599
  68. package/src/ide-integration/cdp-manager.js +306 -0
  69. package/src/ide-integration/claude-code-cli-manager.cjs +1 -1
  70. package/src/ide-integration/continuation-handler.js +337 -0
  71. package/src/ide-integration/ide-status-checker.js +292 -0
  72. package/src/ide-integration/macos-ide-manager.js +627 -0
  73. package/src/ide-integration/macos-text-sender.js +528 -0
  74. package/src/ide-integration/response-reader.js +548 -0
  75. package/src/ide-integration/windows-automation-manager.js +121 -0
  76. package/src/ide-integration/windows-ide-manager.js +373 -0
  77. package/src/index.cjs +25 -3
  78. package/src/index.js +15 -1
  79. package/src/llm/direct-llm-manager.cjs +90 -2
  80. package/src/models/compliance-report.js +538 -0
  81. package/src/models/file-analysis.js +681 -0
  82. package/src/models/refactoring-plan.js +770 -0
  83. package/src/monitoring/alert-system.js +834 -0
  84. package/src/monitoring/compliance-progress-tracker.js +437 -0
  85. package/src/monitoring/continuous-scan-notifications.js +661 -0
  86. package/src/monitoring/continuous-scanner.js +279 -0
  87. package/src/monitoring/file-monitor/file-analyzer.js +262 -0
  88. package/src/monitoring/file-monitor/file-monitor.js +237 -0
  89. package/src/monitoring/file-monitor/watcher.js +194 -0
  90. package/src/monitoring/file-monitor.js +17 -0
  91. package/src/monitoring/notification-manager.js +437 -0
  92. package/src/monitoring/scanner-core.js +368 -0
  93. package/src/monitoring/scanner-events.js +214 -0
  94. package/src/monitoring/violation-notification-system.js +515 -0
  95. package/src/refactoring/boundaries/cohesion-analyzer.js +316 -0
  96. package/src/refactoring/boundaries/extraction-result.js +285 -0
  97. package/src/refactoring/boundaries/extraction-strategies.js +392 -0
  98. package/src/refactoring/boundaries/module-boundary.js +209 -0
  99. package/src/refactoring/boundary/boundary-detector.js +741 -0
  100. package/src/refactoring/boundary/boundary-types.js +405 -0
  101. package/src/refactoring/boundary/extraction-strategies.js +554 -0
  102. package/src/refactoring/boundary-extraction-result.js +77 -0
  103. package/src/refactoring/boundary-extraction-strategies.js +330 -0
  104. package/src/refactoring/boundary-extractor.js +384 -0
  105. package/src/refactoring/boundary-types.js +46 -0
  106. package/src/refactoring/circular/circular-dependency.js +88 -0
  107. package/src/refactoring/circular/cycle-detection.js +147 -0
  108. package/src/refactoring/circular/dependency-node.js +82 -0
  109. package/src/refactoring/circular/dependency-result.js +107 -0
  110. package/src/refactoring/circular/dependency-types.js +58 -0
  111. package/src/refactoring/circular/graph-builder.js +213 -0
  112. package/src/refactoring/circular/resolution-strategy.js +72 -0
  113. package/src/refactoring/circular/strategy-generator.js +229 -0
  114. package/src/refactoring/circular-dependency-resolver-original.js +809 -0
  115. package/src/refactoring/circular-dependency-resolver.js +200 -0
  116. package/src/refactoring/code-mover.js +761 -0
  117. package/src/refactoring/file-splitter.js +696 -0
  118. package/src/refactoring/functionality-validator.js +816 -0
  119. package/src/refactoring/import-manager.js +774 -0
  120. package/src/refactoring/module-boundary.js +107 -0
  121. package/src/refactoring/refactoring-executor.js +672 -0
  122. package/src/refactoring/refactoring-rollback.js +614 -0
  123. package/src/refactoring/test-validator.js +631 -0
  124. package/src/requirement-management/default-requirement-manager.js +321 -0
  125. package/src/requirement-management/requirement-file-parser.js +159 -0
  126. package/src/requirement-management/requirement-sequencer.js +221 -0
  127. package/src/rui/commands/AgentCommandParser.js +600 -0
  128. package/src/rui/commands/AgentCommands.js +487 -0
  129. package/src/rui/commands/AgentResponseFormatter.js +832 -0
  130. package/src/scripts/verify-full-compliance.js +269 -0
  131. package/src/sync/sync-engine-core.js +1 -0
  132. package/src/sync/sync-engine-remote-handlers.js +135 -0
  133. package/src/task-generation/automated-task-generator.js +351 -0
  134. package/src/task-generation/prioritizer.js +287 -0
  135. package/src/task-generation/task-list-updater.js +215 -0
  136. package/src/task-generation/task-management-integration.js +480 -0
  137. package/src/task-generation/task-manager-integration.js +270 -0
  138. package/src/task-generation/violation-task-generator.js +474 -0
  139. package/src/task-management/continuous-scan-integration.js +342 -0
  140. package/src/timeout-management/index.js +12 -3
  141. package/src/timeout-management/response-time-tracker.js +167 -0
  142. package/src/timeout-management/timeout-calculator.js +159 -0
  143. package/src/timeout-management/timeout-config-manager.js +172 -0
  144. package/src/utils/ast-analyzer.js +417 -0
  145. package/src/utils/current-requirement-manager.js +276 -0
  146. package/src/utils/current-requirement-operations.js +472 -0
  147. package/src/utils/dependency-mapper.js +456 -0
  148. package/src/utils/download-with-progress.js +4 -2
  149. package/src/utils/electron-update-checker.js +4 -1
  150. package/src/utils/file-size-analyzer.js +272 -0
  151. package/src/utils/import-updater.js +280 -0
  152. package/src/utils/refactoring-tools.js +512 -0
  153. package/src/utils/report-generator.js +569 -0
  154. package/src/utils/reports/report-analysis.js +218 -0
  155. package/src/utils/reports/report-types.js +55 -0
  156. package/src/utils/reports/summary-generators.js +102 -0
  157. package/src/utils/requirement-file-management.js +157 -0
  158. package/src/utils/requirement-helpers/requirement-file-ops.js +392 -0
  159. package/src/utils/requirement-helpers/requirement-mover.js +414 -0
  160. package/src/utils/requirement-helpers/requirement-parser.js +326 -0
  161. package/src/utils/requirement-helpers/requirement-status.js +320 -0
  162. package/src/utils/requirement-helpers-new.js +55 -0
  163. package/src/utils/requirement-helpers-refactored.js +367 -0
  164. package/src/utils/requirement-helpers.js +291 -1191
  165. package/src/utils/requirement-movement-operations.js +450 -0
  166. package/src/utils/requirement-movement.js +312 -0
  167. package/src/utils/requirement-parsing-helpers.js +56 -0
  168. package/src/utils/requirement-statistics.js +200 -0
  169. package/src/utils/requirement-text-utils.js +58 -0
  170. package/src/utils/rollback/rollback-handlers.js +125 -0
  171. package/src/utils/rollback/rollback-operation.js +63 -0
  172. package/src/utils/rollback/rollback-recorder.js +166 -0
  173. package/src/utils/rollback/rollback-state-manager.js +175 -0
  174. package/src/utils/rollback/rollback-types.js +33 -0
  175. package/src/utils/rollback/rollback-utils.js +110 -0
  176. package/src/utils/rollback-manager-original.js +569 -0
  177. package/src/utils/rollback-manager.js +202 -0
  178. package/src/utils/smoke-test-cli.js +362 -0
  179. package/src/utils/smoke-test-gui.js +351 -0
  180. package/src/utils/smoke-test-orchestrator.js +321 -0
  181. package/src/utils/smoke-test-runner.js +60 -0
  182. package/src/utils/smoke-test-web.js +347 -0
  183. package/src/utils/specification-helpers.js +39 -13
  184. package/src/utils/specification-migration.js +97 -0
  185. package/src/utils/test-runner.js +579 -0
  186. package/src/utils/validation-framework.js +518 -0
  187. package/src/validation/compliance-analyzer.js +197 -0
  188. package/src/validation/compliance-report-generator.js +343 -0
  189. package/src/validation/compliance-reporter.js +711 -0
  190. package/src/validation/compliance-rules.js +127 -0
  191. package/src/validation/constitution-validator-new.js +196 -0
  192. package/src/validation/constitution-validator.js +17 -0
  193. package/src/validation/file-validators.js +170 -0
  194. package/src/validation/line-limit/file-analyzer.js +201 -0
  195. package/src/validation/line-limit/line-limit-validator.js +208 -0
  196. package/src/validation/line-limit/validation-result.js +144 -0
  197. package/src/validation/line-limit-core.js +225 -0
  198. package/src/validation/line-limit-reporter.js +134 -0
  199. package/src/validation/line-limit-result.js +125 -0
  200. package/src/validation/line-limit-validator.js +41 -0
  201. package/src/validation/metrics-calculator.js +660 -0
  202. 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- prefix
83
- * @returns {Promise<Array<{directory, title, path, hasPlan, hasPlanPrompt, hasTasks}>>}
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
- if (!await fs.pathExists(specMdPath)) continue;
106
+ const hasSpec = await fs.pathExists(specMdPath);
105
107
 
106
- const content = await fs.readFile(specMdPath, 'utf8').catch(() => '');
107
- const title = extractSpecTitle(content) || entry;
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
+ };