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,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
|
+
};
|