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,326 @@
1
+ /**
2
+ * Requirement Parser Utilities
3
+ * Utility functions for parsing and processing requirement text
4
+ */
5
+
6
+ // Default instruction text
7
+ const DEFAULT_INSTRUCTION_TEXT = 'Follow INSTRUCTIONS.md from .vibecodingmachine directory. CRITICAL: You MUST work through ALL status stages (PREPARE โ†’ ACT โ†’ CLEAN UP โ†’ VERIFY โ†’ DONE) and set the status to DONE in the REQUIREMENTS file before stopping. DO NOT stop working until the "๐Ÿšฆ Current Status" section shows "DONE". The VibeCodingMachine app is running in autonomous mode and depends on you completing the requirement fully.';
8
+
9
+ /**
10
+ * Get ordinal suffix for numbers (1st, 2nd, 3rd, 4th, etc.)
11
+ * @param {number} num - The number
12
+ * @returns {string} The ordinal suffix
13
+ */
14
+ function getOrdinalSuffix(num) {
15
+ const j = num % 10;
16
+ const k = num % 100;
17
+ if (j === 1 && k !== 11) {
18
+ return 'st';
19
+ }
20
+ if (j === 2 && k !== 12) {
21
+ return 'nd';
22
+ }
23
+ if (j === 3 && k !== 13) {
24
+ return 'rd';
25
+ }
26
+ return 'th';
27
+ }
28
+
29
+ /**
30
+ * Add or increment TRY AGAIN prefix to requirement text
31
+ * @param {string} requirementText - The requirement text
32
+ * @returns {string} Requirement text with TRY AGAIN prefix
33
+ */
34
+ function addTryAgainPrefix(requirementText) {
35
+ const tryAgainMatch = requirementText.match(/^TRY AGAIN \((\d+)(?:st|nd|rd|th) time\): (.+)$/);
36
+
37
+ if (tryAgainMatch) {
38
+ const currentCount = parseInt(tryAgainMatch[1]);
39
+ const baseRequirement = tryAgainMatch[2];
40
+ return `TRY AGAIN (${currentCount + 1}${getOrdinalSuffix(currentCount + 1)} time): ${baseRequirement}`;
41
+ } else {
42
+ return `TRY AGAIN (1st time): ${requirementText}`;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Parse requirement from markdown line
48
+ * @param {string} line - Markdown line (e.g., "- Requirement text")
49
+ * @returns {string} Requirement text without markdown prefix
50
+ */
51
+ function parseRequirementLine(line) {
52
+ if (line.startsWith('- ')) {
53
+ return line.substring(2);
54
+ }
55
+ return line;
56
+ }
57
+
58
+ /**
59
+ * Extract requirement title from line (removes timestamp if present)
60
+ * @param {string} line - Requirement line
61
+ * @returns {string} Requirement title without timestamp
62
+ */
63
+ function extractRequirementTitle(line) {
64
+ const lineText = parseRequirementLine(line);
65
+ // Extract title part (before timestamp in parentheses)
66
+ const titleMatch = lineText.match(/^(.+?)\s*\([\d-]+\)$/);
67
+ return titleMatch ? titleMatch[1] : lineText;
68
+ }
69
+
70
+ /**
71
+ * Check if a line contains a requirement title
72
+ * @param {string} line - Line to check
73
+ * @param {string} requirementTitle - Requirement title to match
74
+ * @returns {boolean} Whether the line contains the requirement title
75
+ */
76
+ function isRequirementMatch(line, requirementTitle) {
77
+ const lineTitle = extractRequirementTitle(line);
78
+ const reqTitleMatch = requirementTitle.match(/^(.+?)\s*\([\d-]+\)$/);
79
+ const reqTitleOnly = reqTitleMatch ? reqTitleMatch[1] : requirementTitle;
80
+
81
+ return lineTitle === reqTitleOnly ||
82
+ lineTitle.includes(reqTitleOnly) ||
83
+ reqTitleOnly.includes(lineTitle);
84
+ }
85
+
86
+ /**
87
+ * Parse requirement block from lines
88
+ * @param {string[]} lines - Array of lines
89
+ * @param {number} startIndex - Start index of requirement block
90
+ * @returns {Object} Parsed requirement block with title and content
91
+ */
92
+ function parseRequirementBlock(lines, startIndex) {
93
+ const title = lines[startIndex].replace(/^###\s*/, '').trim();
94
+ const content = [];
95
+
96
+ // Find the end of this requirement block
97
+ let endIndex = lines.length;
98
+ for (let j = startIndex + 1; j < lines.length; j++) {
99
+ const nextLine = lines[j].trim();
100
+ if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
101
+ endIndex = j;
102
+ break;
103
+ }
104
+ }
105
+
106
+ // Extract content (everything between the title and the next header)
107
+ for (let i = startIndex + 1; i < endIndex; i++) {
108
+ content.push(lines[i]);
109
+ }
110
+
111
+ return {
112
+ title,
113
+ content: content.join('\n'),
114
+ startIndex,
115
+ endIndex
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Find requirement block by title in lines
121
+ * @param {string[]} lines - Array of lines
122
+ * @param {string} requirementTitle - Title to search for
123
+ * @param {string} sectionType - Type of section ('todo', 'verify', 'current')
124
+ * @returns {Object|null} Requirement block info or null if not found
125
+ */
126
+ function findRequirementBlock(lines, requirementTitle, sectionType = 'todo') {
127
+ const normalizedTitle = requirementTitle.trim();
128
+ let inTargetSection = false;
129
+ let requirementStartIndex = -1;
130
+ let requirementEndIndex = -1;
131
+
132
+ // Section identifiers
133
+ const sectionIdentifiers = {
134
+ todo: ['## โณ Requirements not yet completed'],
135
+ verify: ['## โœ… Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG', '## โœ… Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG.md'],
136
+ current: ['## ๐Ÿ”จ Current In Progress Requirement']
137
+ };
138
+
139
+ const targetSectionIds = sectionIdentifiers[sectionType] || [];
140
+
141
+ for (let i = 0; i < lines.length; i++) {
142
+ const line = lines[i];
143
+ const trimmed = line.trim();
144
+
145
+ // Check if we're entering the target section
146
+ if (targetSectionIds.some(id => trimmed.includes(id))) {
147
+ inTargetSection = true;
148
+ continue;
149
+ }
150
+
151
+ // Check if we're leaving the target section
152
+ if (inTargetSection && trimmed.startsWith('##') && !trimmed.startsWith('###')) {
153
+ if (!targetSectionIds.some(id => trimmed.includes(id))) {
154
+ break;
155
+ }
156
+ }
157
+
158
+ // Look for requirement in target section (### header format)
159
+ if (inTargetSection && trimmed.startsWith('###')) {
160
+ const title = trimmed.replace(/^###\s*/, '').trim();
161
+ if (title === normalizedTitle || title.includes(normalizedTitle) || normalizedTitle.includes(title)) {
162
+ requirementStartIndex = i;
163
+
164
+ // Find the end of this requirement block
165
+ for (let j = i + 1; j < lines.length; j++) {
166
+ const nextLine = lines[j].trim();
167
+ if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
168
+ requirementEndIndex = j;
169
+ break;
170
+ }
171
+ }
172
+
173
+ if (requirementEndIndex === -1) {
174
+ requirementEndIndex = lines.length;
175
+ }
176
+
177
+ return parseRequirementBlock(lines, requirementStartIndex);
178
+ }
179
+ }
180
+ }
181
+
182
+ return null;
183
+ }
184
+
185
+ /**
186
+ * Find all requirement blocks in a section
187
+ * @param {string[]} lines - Array of lines
188
+ * @param {string} sectionType - Type of section ('todo', 'verify', 'current')
189
+ * @returns {Array} Array of requirement blocks
190
+ */
191
+ function findAllRequirementsInSection(lines, sectionType = 'todo') {
192
+ const requirements = [];
193
+ let inTargetSection = false;
194
+
195
+ // Section identifiers
196
+ const sectionIdentifiers = {
197
+ todo: ['## โณ Requirements not yet completed'],
198
+ verify: ['## โœ… Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG', '## โœ… Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG.md'],
199
+ current: ['## ๐Ÿ”จ Current In Progress Requirement']
200
+ };
201
+
202
+ const targetSectionIds = sectionIdentifiers[sectionType] || [];
203
+
204
+ for (let i = 0; i < lines.length; i++) {
205
+ const line = lines[i];
206
+ const trimmed = line.trim();
207
+
208
+ // Check if we're entering the target section
209
+ if (targetSectionIds.some(id => trimmed.includes(id))) {
210
+ inTargetSection = true;
211
+ continue;
212
+ }
213
+
214
+ // Check if we're leaving the target section
215
+ if (inTargetSection && trimmed.startsWith('##') && !trimmed.startsWith('###')) {
216
+ if (!targetSectionIds.some(id => trimmed.includes(id))) {
217
+ break;
218
+ }
219
+ }
220
+
221
+ // Look for requirements in target section
222
+ if (inTargetSection && trimmed.startsWith('###')) {
223
+ const requirement = parseRequirementBlock(lines, i);
224
+ requirements.push(requirement);
225
+
226
+ // Skip to the end of this requirement block
227
+ i = requirement.endIndex - 1;
228
+ }
229
+ }
230
+
231
+ return requirements;
232
+ }
233
+
234
+ /**
235
+ * Validate requirement format
236
+ * @param {string} requirementText - Requirement text to validate
237
+ * @returns {Object} Validation result
238
+ */
239
+ function validateRequirementFormat(requirementText) {
240
+ const result = {
241
+ isValid: true,
242
+ errors: [],
243
+ warnings: []
244
+ };
245
+
246
+ if (!requirementText || typeof requirementText !== 'string') {
247
+ result.isValid = false;
248
+ result.errors.push('Requirement text must be a non-empty string');
249
+ return result;
250
+ }
251
+
252
+ const trimmedText = requirementText.trim();
253
+
254
+ if (trimmedText.length === 0) {
255
+ result.isValid = false;
256
+ result.errors.push('Requirement text cannot be empty');
257
+ return result;
258
+ }
259
+
260
+ // Check for TRY AGAIN prefix
261
+ if (trimmedText.startsWith('TRY AGAIN')) {
262
+ const tryAgainMatch = trimmedText.match(/^TRY AGAIN \((\d+)(?:st|nd|rd|th) time\): (.+)$/);
263
+ if (!tryAgainMatch) {
264
+ result.warnings.push('TRY AGAIN prefix format may be incorrect');
265
+ }
266
+ }
267
+
268
+ // Check length
269
+ if (trimmedText.length > 500) {
270
+ result.warnings.push('Requirement text is quite long, consider breaking it down');
271
+ }
272
+
273
+ // Check for common formatting issues
274
+ if (trimmedText.includes(' ')) {
275
+ result.warnings.push('Requirement text contains double spaces');
276
+ }
277
+
278
+ return result;
279
+ }
280
+
281
+ /**
282
+ * Format requirement for display
283
+ * @param {string} requirementText - Requirement text
284
+ * @param {Object} options - Formatting options
285
+ * @returns {string} Formatted requirement text
286
+ */
287
+ function formatRequirementForDisplay(requirementText, options = {}) {
288
+ const {
289
+ maxLength = 100,
290
+ showTryAgainCount = true,
291
+ truncate = true
292
+ } = options;
293
+
294
+ let formatted = requirementText;
295
+
296
+ // Extract TRY AGAIN count if needed
297
+ if (showTryAgainCount) {
298
+ const tryAgainMatch = formatted.match(/^TRY AGAIN \((\d+)(?:st|nd|rd|th) time\): (.+)$/);
299
+ if (tryAgainMatch) {
300
+ const count = tryAgainMatch[1];
301
+ const baseText = tryAgainMatch[2];
302
+ formatted = `${baseText} (Attempt ${count})`;
303
+ }
304
+ }
305
+
306
+ // Truncate if needed
307
+ if (truncate && formatted.length > maxLength) {
308
+ formatted = formatted.substring(0, maxLength - 3) + '...';
309
+ }
310
+
311
+ return formatted;
312
+ }
313
+
314
+ module.exports = {
315
+ DEFAULT_INSTRUCTION_TEXT,
316
+ getOrdinalSuffix,
317
+ addTryAgainPrefix,
318
+ parseRequirementLine,
319
+ extractRequirementTitle,
320
+ isRequirementMatch,
321
+ parseRequirementBlock,
322
+ findRequirementBlock,
323
+ findAllRequirementsInSection,
324
+ validateRequirementFormat,
325
+ formatRequirementForDisplay
326
+ };
@@ -0,0 +1,320 @@
1
+ const fs = require('fs-extra');
2
+ const { findRequirementBlock } = require('./requirement-parser.js');
3
+
4
+ /**
5
+ * Requirement Status Management
6
+ * Handles reading and updating requirement status information
7
+ */
8
+
9
+ /**
10
+ * Extract current status from requirements content
11
+ * @param {string} content - Requirements file content
12
+ * @returns {string} Current status
13
+ */
14
+ function extractCurrentStatus(content) {
15
+ if (!content) return '';
16
+
17
+ const lines = content.split('\n');
18
+ for (const line of lines) {
19
+ if (line.includes('๐Ÿšฆ Current Status')) {
20
+ const match = line.match(/\*\*([A-Z_]+)\*\*/);
21
+ if (match) {
22
+ return match[1];
23
+ }
24
+ }
25
+ }
26
+
27
+ return '';
28
+ }
29
+
30
+ /**
31
+ * Update current status in requirements file
32
+ * @param {string} reqPath - Path to requirements file
33
+ * @param {string} newStatus - New status value
34
+ * @returns {Promise<boolean>} Success status
35
+ */
36
+ async function updateCurrentStatus(reqPath, newStatus) {
37
+ try {
38
+ const content = await fs.readFile(reqPath, 'utf-8');
39
+ const lines = content.split('\n');
40
+ let statusUpdated = false;
41
+
42
+ for (let i = 0; i < lines.length; i++) {
43
+ const line = lines[i];
44
+ if (line.includes('๐Ÿšฆ Current Status')) {
45
+ // Replace the status value
46
+ lines[i] = line.replace(/\*\*[A-Z_]+\*\*/, `**${newStatus.toUpperCase()}**`);
47
+ statusUpdated = true;
48
+ break;
49
+ }
50
+ }
51
+
52
+ if (!statusUpdated) {
53
+ // Add status section if it doesn't exist
54
+ lines.push('## ๐Ÿšฆ Current Status');
55
+ lines.push('');
56
+ lines.push(`**${newStatus.toUpperCase()}**`);
57
+ }
58
+
59
+ await fs.writeFile(reqPath, lines.join('\n'), 'utf-8');
60
+ console.log(`โœ… Updated status to: ${newStatus}`);
61
+ return true;
62
+
63
+ } catch (error) {
64
+ console.error(`Error updating status: ${error.message}`);
65
+ return false;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Get current status from requirements file
71
+ * @param {string} reqPath - Path to requirements file
72
+ * @returns {Promise<string>} Current status
73
+ */
74
+ async function getCurrentStatus(reqPath) {
75
+ try {
76
+ const content = await fs.readFile(reqPath, 'utf-8');
77
+ return extractCurrentStatus(content);
78
+ } catch (error) {
79
+ console.error(`Error getting current status: ${error.message}`);
80
+ return '';
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Check if status is DONE
86
+ * @param {string} status - Status to check
87
+ * @returns {boolean} Whether status is DONE
88
+ */
89
+ function isStatusDone(status) {
90
+ return status && status.toUpperCase() === 'DONE';
91
+ }
92
+
93
+ /**
94
+ * Check if status is in progress (not DONE)
95
+ * @param {string} status - Status to check
96
+ * @returns {boolean} Whether status is in progress
97
+ */
98
+ function isStatusInProgress(status) {
99
+ return status && !isStatusDone(status);
100
+ }
101
+
102
+ /**
103
+ * Get status progression stage
104
+ * @param {string} status - Current status
105
+ * @returns {string} Stage name
106
+ */
107
+ function getStatusStage(status) {
108
+ if (!status) return 'UNKNOWN';
109
+
110
+ const statusUpper = status.toUpperCase().trim();
111
+
112
+ if (statusUpper === 'PREPARE') return 'PREPARE';
113
+ if (statusUpper === 'CREATE') return 'CREATE';
114
+ if (statusUpper === 'CLEAN_UP') return 'CLEAN_UP';
115
+ if (statusUpper === 'VERIFY') return 'VERIFY';
116
+ if (statusUpper === 'DONE') return 'DONE';
117
+
118
+ return 'UNKNOWN';
119
+ }
120
+
121
+ /**
122
+ * Validate status value
123
+ * @param {string} status - Status to validate
124
+ * @returns {Object} Validation result
125
+ */
126
+ function validateStatus(status) {
127
+ const validStatuses = ['PREPARE', 'CREATE', 'CLEAN_UP', 'VERIFY', 'DONE'];
128
+ const result = {
129
+ isValid: true,
130
+ errors: [],
131
+ warnings: []
132
+ };
133
+
134
+ if (!status || typeof status !== 'string') {
135
+ result.isValid = false;
136
+ result.errors.push('Status must be a non-empty string');
137
+ return result;
138
+ }
139
+
140
+ const normalizedStatus = status.toUpperCase().trim();
141
+
142
+ if (!validStatuses.includes(normalizedStatus)) {
143
+ result.isValid = false;
144
+ result.errors.push(`Invalid status: "${status}". Valid statuses are: ${validStatuses.join(', ')}`);
145
+ }
146
+
147
+ return result;
148
+ }
149
+
150
+ /**
151
+ * Get next status in progression
152
+ * @param {string} currentStatus - Current status
153
+ * @returns {string|null} Next status or null if current is DONE
154
+ */
155
+ function getNextStatus(currentStatus) {
156
+ const progression = ['PREPARE', 'CREATE', 'CLEAN_UP', 'VERIFY', 'DONE'];
157
+ const currentIndex = progression.indexOf(currentStatus?.toUpperCase());
158
+
159
+ if (currentIndex === -1 || currentIndex === progression.length - 1) {
160
+ return null;
161
+ }
162
+
163
+ return progression[currentIndex + 1];
164
+ }
165
+
166
+ /**
167
+ * Get previous status in progression
168
+ * @param {string} currentStatus - Current status
169
+ * @returns {string|null} Previous status or null if current is first
170
+ */
171
+ function getPreviousStatus(currentStatus) {
172
+ const progression = ['PREPARE', 'CREATE', 'CLEAN_UP', 'VERIFY', 'DONE'];
173
+ const currentIndex = progression.indexOf(currentStatus?.toUpperCase());
174
+
175
+ if (currentIndex <= 0) {
176
+ return null;
177
+ }
178
+
179
+ return progression[currentIndex - 1];
180
+ }
181
+
182
+ /**
183
+ * Check if status transition is valid
184
+ * @param {string} fromStatus - Current status
185
+ * @param {string} toStatus - Target status
186
+ * @returns {boolean} Whether transition is valid
187
+ */
188
+ function isValidStatusTransition(fromStatus, toStatus) {
189
+ const progression = ['PREPARE', 'CREATE', 'CLEAN_UP', 'VERIFY', 'DONE'];
190
+
191
+ const fromIndex = progression.indexOf(fromStatus?.toUpperCase());
192
+ const toIndex = progression.indexOf(toStatus?.toUpperCase());
193
+
194
+ // Allow going backwards (for corrections) and forward one step
195
+ return fromIndex !== -1 && toIndex !== -1 &&
196
+ (toIndex === fromIndex + 1 || toIndex < fromIndex);
197
+ }
198
+
199
+ /**
200
+ * Get status change history from content
201
+ * @param {string} content - Requirements file content
202
+ * @returns {Array} Array of status changes
203
+ */
204
+ function getStatusHistory(content) {
205
+ const history = [];
206
+ const lines = content.split('\n');
207
+ let currentSection = null;
208
+
209
+ for (let i = 0; i < lines.length; i++) {
210
+ const line = lines[i].trim();
211
+
212
+ if (line.includes('## ๐Ÿšฆ Current Status')) {
213
+ currentSection = 'status';
214
+ continue;
215
+ }
216
+
217
+ if (currentSection === 'status' && line.includes('**')) {
218
+ const match = line.match(/\*\*([A-Z_]+)\*\*/);
219
+ if (match) {
220
+ history.push({
221
+ status: match[1],
222
+ line: i + 1,
223
+ content: line
224
+ });
225
+ }
226
+ }
227
+ }
228
+
229
+ return history;
230
+ }
231
+
232
+ /**
233
+ * Format status for display
234
+ * @param {string} status - Status to format
235
+ * @param {Object} options - Formatting options
236
+ * @returns {string} Formatted status
237
+ */
238
+ function formatStatusForDisplay(status, options = {}) {
239
+ const {
240
+ showEmoji = true,
241
+ uppercase = true,
242
+ bold = false
243
+ } = options;
244
+
245
+ let formatted = status || 'UNKNOWN';
246
+
247
+ if (uppercase) {
248
+ formatted = formatted.toUpperCase();
249
+ }
250
+
251
+ if (bold) {
252
+ formatted = `**${formatted}**`;
253
+ }
254
+
255
+ if (showEmoji) {
256
+ const emojiMap = {
257
+ 'PREPARE': '๐Ÿ”',
258
+ 'CREATE': '๐Ÿ”จ',
259
+ 'CLEAN_UP': '๐Ÿงน',
260
+ 'VERIFY': 'โœ…',
261
+ 'DONE': '๐ŸŽ‰',
262
+ 'UNKNOWN': 'โ“'
263
+ };
264
+ formatted = `${emojiMap[formatted] || 'โ“'} ${formatted}`;
265
+ }
266
+
267
+ return formatted;
268
+ }
269
+
270
+ /**
271
+ * Get status summary for a requirements file
272
+ * @param {string} reqPath - Path to requirements file
273
+ * @returns {Promise<Object>} Status summary
274
+ */
275
+ async function getStatusSummary(reqPath) {
276
+ try {
277
+ const content = await fs.readFile(reqPath, 'utf-8');
278
+ const currentStatus = extractCurrentStatus(content);
279
+ const stage = getStatusStage(currentStatus);
280
+ const isDone = isStatusDone(currentStatus);
281
+ const nextStatus = getNextStatus(currentStatus);
282
+ const history = getStatusHistory(content);
283
+
284
+ return {
285
+ current: currentStatus,
286
+ stage,
287
+ isDone,
288
+ nextStatus,
289
+ history,
290
+ lastUpdated: new Date().toISOString()
291
+ };
292
+ } catch (error) {
293
+ console.error(`Error getting status summary: ${error.message}`);
294
+ return {
295
+ current: '',
296
+ stage: 'UNKNOWN',
297
+ isDone: false,
298
+ nextStatus: null,
299
+ history: [],
300
+ lastUpdated: null,
301
+ error: error.message
302
+ };
303
+ }
304
+ }
305
+
306
+ module.exports = {
307
+ extractCurrentStatus,
308
+ updateCurrentStatus,
309
+ getCurrentStatus,
310
+ isStatusDone,
311
+ isStatusInProgress,
312
+ getStatusStage,
313
+ validateStatus,
314
+ getNextStatus,
315
+ getPreviousStatus,
316
+ isValidStatusTransition,
317
+ getStatusHistory,
318
+ formatStatusForDisplay,
319
+ getStatusSummary
320
+ };
@@ -0,0 +1,55 @@
1
+ // Import all refactored modules
2
+ const { getOrdinalSuffix, addTryAgainPrefix, parseRequirementLine } = require('./requirement-text-utils');
3
+ const {
4
+ promoteToVerified,
5
+ demoteFromVerifiedToTodo,
6
+ promoteTodoToVerify,
7
+ demoteVerifyToTodo
8
+ } = require('./requirement-movement');
9
+ const {
10
+ loadVerifiedFromChangelog,
11
+ getProjectRequirementStats,
12
+ getCurrentRequirementProgress
13
+ } = require('./requirement-statistics');
14
+ const {
15
+ DEFAULT_INSTRUCTION_TEXT,
16
+ createDefaultRequirementsFile,
17
+ getOrCreateRequirementsFilePath
18
+ } = require('./requirement-file-management');
19
+ const {
20
+ getCurrentRequirement,
21
+ isCurrentRequirementDone,
22
+ moveToNextRequirement,
23
+ getCurrentRequirementProgress: getCurrentProgress,
24
+ getCurrentRequirementName
25
+ } = require('./current-requirement-manager');
26
+
27
+ // Re-export everything for backward compatibility
28
+ module.exports = {
29
+ // Text utilities
30
+ getOrdinalSuffix,
31
+ addTryAgainPrefix,
32
+ parseRequirementLine,
33
+
34
+ // Movement functions
35
+ promoteToVerified,
36
+ demoteFromVerifiedToTodo,
37
+ promoteTodoToVerify,
38
+ demoteVerifyToTodo,
39
+
40
+ // Statistics functions
41
+ loadVerifiedFromChangelog,
42
+ getProjectRequirementStats,
43
+ getCurrentRequirementProgress,
44
+
45
+ // File management
46
+ DEFAULT_INSTRUCTION_TEXT,
47
+ createDefaultRequirementsFile,
48
+ getOrCreateRequirementsFilePath,
49
+
50
+ // Current requirement management
51
+ getCurrentRequirement,
52
+ isCurrentRequirementDone,
53
+ moveToNextRequirement,
54
+ getCurrentRequirementName
55
+ };