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,836 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Auto Mode Business Logic
|
|
3
|
+
* Common auto mode functionality used by both CLI and GUI
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
|
|
9
|
+
// Try to require the actual modules, fall back to mocks if not available
|
|
10
|
+
let AppleScriptManager, ClineCLIManager, AiderCLIManager, ClaudeCodeCLIManager, logIDEMessage, runContinueCLIAutoMode, CDPManager, QuotaDetector, ChatManager;
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const applescriptManager = require('../ide-integration/applescript-manager.cjs');
|
|
14
|
+
AppleScriptManager = applescriptManager.AppleScriptManager;
|
|
15
|
+
ClineCLIManager = applescriptManager.ClineCLIManager;
|
|
16
|
+
AiderCLIManager = applescriptManager.AiderCLIManager;
|
|
17
|
+
ClaudeCodeCLIManager = applescriptManager.ClaudeCodeCLIManager;
|
|
18
|
+
logIDEMessage = applescriptManager.logIDEMessage;
|
|
19
|
+
runContinueCLIAutoMode = applescriptManager.runContinueCLIAutoMode;
|
|
20
|
+
} catch (e) {
|
|
21
|
+
// Mock implementations
|
|
22
|
+
AppleScriptManager = class MockAppleScriptManager {};
|
|
23
|
+
ClineCLIManager = class MockClineCLIManager {};
|
|
24
|
+
AiderCLIManager = class MockAiderCLIManager {};
|
|
25
|
+
ClaudeCodeCLIManager = class MockClaudeCodeCLIManager {};
|
|
26
|
+
logIDEMessage = () => {};
|
|
27
|
+
runContinueCLIAutoMode = () => {};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
CDPManager = require('../ide-integration/cdp-manager.cjs').CDPManager;
|
|
32
|
+
} catch (e) {
|
|
33
|
+
CDPManager = class MockCDPManager {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
QuotaDetector = require('../ide-integration/quota-detector.cjs').QuotaDetector;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
QuotaDetector = class MockQuotaDetector {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
ChatManager = require('../chat-management/chat-manager.cjs').ChatManager;
|
|
44
|
+
} catch (e) {
|
|
45
|
+
ChatManager = class MockChatManager {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class AutoModeBusinessLogic {
|
|
49
|
+
constructor() {
|
|
50
|
+
this.state = {
|
|
51
|
+
isAutoMode: false,
|
|
52
|
+
currentIDE: 'vscode',
|
|
53
|
+
chatCounter: 0,
|
|
54
|
+
maxChats: 100,
|
|
55
|
+
neverStop: false,
|
|
56
|
+
autoModeEvent: null,
|
|
57
|
+
currentMode: 'requirements', // 'spec' | 'requirements'
|
|
58
|
+
currentSpecPath: null,
|
|
59
|
+
currentSpecId: null,
|
|
60
|
+
specsList: null,
|
|
61
|
+
currentSpecIndex: 0,
|
|
62
|
+
powerSaveBlockerId: null
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get available LLM providers from config and environment variables
|
|
68
|
+
* @param {Object} savedConfig - Config object
|
|
69
|
+
* @returns {Array<{provider: string, model: string, apiKey?: string}>}
|
|
70
|
+
*/
|
|
71
|
+
getAvailableProviders(savedConfig) {
|
|
72
|
+
const providers = [];
|
|
73
|
+
|
|
74
|
+
// Check for Groq (current primary)
|
|
75
|
+
const groqApiKey = process.env.GROQ_API_KEY || savedConfig.groqApiKey;
|
|
76
|
+
if (groqApiKey && groqApiKey.trim()) {
|
|
77
|
+
providers.push({
|
|
78
|
+
provider: 'groq',
|
|
79
|
+
model: savedConfig.aiderModel || 'groq/llama-3.3-70b-versatile',
|
|
80
|
+
apiKey: groqApiKey
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check for Anthropic (Claude)
|
|
85
|
+
const anthropicApiKey = process.env.ANTHROPIC_API_KEY || savedConfig.anthropicApiKey;
|
|
86
|
+
if (anthropicApiKey && anthropicApiKey.trim()) {
|
|
87
|
+
providers.push({
|
|
88
|
+
provider: 'anthropic',
|
|
89
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
90
|
+
apiKey: anthropicApiKey
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check for OpenAI
|
|
95
|
+
const openaiApiKey = process.env.OPENAI_API_KEY || savedConfig.openaiApiKey;
|
|
96
|
+
if (openaiApiKey && openaiApiKey.trim()) {
|
|
97
|
+
providers.push({
|
|
98
|
+
provider: 'openai',
|
|
99
|
+
model: 'gpt-4-turbo-2024-04-09',
|
|
100
|
+
apiKey: openaiApiKey
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Always add Ollama as fallback (local, no API key needed)
|
|
105
|
+
providers.push({
|
|
106
|
+
provider: 'ollama',
|
|
107
|
+
model: 'qwen2.5-coder:32b'
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return providers;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if current requirement is DONE and actually completed
|
|
115
|
+
* @param {string} repoPath - Repository path
|
|
116
|
+
* @returns {Promise<{isDone: boolean, actuallyComplete: boolean, reason?: string}>}
|
|
117
|
+
*/
|
|
118
|
+
async isRequirementDone(repoPath) {
|
|
119
|
+
try {
|
|
120
|
+
const { getRequirementsPath } = require('../index');
|
|
121
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
122
|
+
|
|
123
|
+
if (!reqPath || !await fs.pathExists(reqPath)) {
|
|
124
|
+
return { isDone: false, actuallyComplete: false };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
128
|
+
const lines = content.split('\n');
|
|
129
|
+
|
|
130
|
+
// Check RESPONSE section for "Status updated from VERIFY to DONE"
|
|
131
|
+
let inResponseSection = false;
|
|
132
|
+
let responseContent = '';
|
|
133
|
+
let statusIsDone = false;
|
|
134
|
+
|
|
135
|
+
for (const line of lines) {
|
|
136
|
+
const trimmed = line.trim();
|
|
137
|
+
|
|
138
|
+
if (trimmed.includes('## RESPONSE FROM LAST CHAT')) {
|
|
139
|
+
inResponseSection = true;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (inResponseSection) {
|
|
144
|
+
if (trimmed.startsWith('##') && !trimmed.includes('RESPONSE')) {
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
if (trimmed && !trimmed.startsWith('###')) {
|
|
148
|
+
responseContent += trimmed + ' ';
|
|
149
|
+
|
|
150
|
+
// Check for status progression completion
|
|
151
|
+
const upperLine = trimmed.toUpperCase();
|
|
152
|
+
if (upperLine.includes('STATUS UPDATED FROM VERIFY TO DONE') ||
|
|
153
|
+
upperLine.includes('STATUS: DONE') ||
|
|
154
|
+
upperLine.includes('MOVED TO VERIFIED')) {
|
|
155
|
+
statusIsDone = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!statusIsDone) {
|
|
162
|
+
return { isDone: false, actuallyComplete: false };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check if response shows actual work was done
|
|
166
|
+
const responseUpper = responseContent.toUpperCase();
|
|
167
|
+
const hasSubstantialResponse = responseContent.length > 100 && (
|
|
168
|
+
responseUpper.includes('COMPLETE') ||
|
|
169
|
+
responseUpper.includes('IMPLEMENTED') ||
|
|
170
|
+
responseUpper.includes('CREATED') ||
|
|
171
|
+
responseUpper.includes('UPDATED') ||
|
|
172
|
+
responseUpper.includes('ADDED') ||
|
|
173
|
+
responseUpper.includes('FIXED') ||
|
|
174
|
+
responseUpper.includes('VERIFIED') ||
|
|
175
|
+
responseUpper.match(/DONE.*-.*completed/i) ||
|
|
176
|
+
responseUpper.includes('100') && responseUpper.includes('REQUIREMENT') // For "create 100 requirements"
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Also check for vague responses that indicate incomplete work
|
|
180
|
+
const isVagueResponse = responseUpper.includes('I WILL') ||
|
|
181
|
+
responseUpper.includes('I\'LL') ||
|
|
182
|
+
responseUpper.includes('LET ME') ||
|
|
183
|
+
responseUpper.includes('I CAN') ||
|
|
184
|
+
responseUpper.includes('I SHOULD') ||
|
|
185
|
+
(responseUpper.includes('READ') && !responseUpper.includes('CREATED')) ||
|
|
186
|
+
(responseUpper.includes('UNDERSTAND') && !responseUpper.includes('IMPLEMENTED'));
|
|
187
|
+
|
|
188
|
+
if (statusIsDone && hasSubstantialResponse && !isVagueResponse) {
|
|
189
|
+
return { isDone: true, actuallyComplete: true };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (statusIsDone && (!hasSubstantialResponse || isVagueResponse)) {
|
|
193
|
+
return {
|
|
194
|
+
isDone: true,
|
|
195
|
+
actuallyComplete: false,
|
|
196
|
+
reason: isVagueResponse ? 'Vague response detected' : 'No substantial work in response'
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { isDone: false, actuallyComplete: false };
|
|
201
|
+
} catch (error) {
|
|
202
|
+
return { isDone: false, actuallyComplete: false };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Move a requirement from Verified back to Requirements not yet completed
|
|
208
|
+
* @param {string} repoPath - Repository path
|
|
209
|
+
* @param {string} requirementText - The requirement text to move back
|
|
210
|
+
* @returns {Promise<{success: boolean, error?: string}>}
|
|
211
|
+
*/
|
|
212
|
+
async moveRequirementBackToTodo(repoPath, requirementText) {
|
|
213
|
+
try {
|
|
214
|
+
const { getRequirementsPath } = require('../index');
|
|
215
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
216
|
+
|
|
217
|
+
if (!reqPath || !await fs.pathExists(reqPath)) {
|
|
218
|
+
return { success: false, error: 'Requirements file not found' };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
222
|
+
const lines = content.split('\n');
|
|
223
|
+
const updatedLines = [];
|
|
224
|
+
|
|
225
|
+
let inVerifiedSection = false;
|
|
226
|
+
let requirementFound = false;
|
|
227
|
+
let inNotYetCompleted = false;
|
|
228
|
+
|
|
229
|
+
for (let i = 0; i < lines.length; i++) {
|
|
230
|
+
const line = lines[i];
|
|
231
|
+
const trimmed = line.trim();
|
|
232
|
+
|
|
233
|
+
// Find verified section
|
|
234
|
+
if (line.includes('## ✅ Verified by AI screenshot')) {
|
|
235
|
+
inVerifiedSection = true;
|
|
236
|
+
updatedLines.push(line);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Find not yet completed section
|
|
241
|
+
if (line.includes('## ⏳ Requirements not yet completed')) {
|
|
242
|
+
inNotYetCompleted = true;
|
|
243
|
+
inVerifiedSection = false;
|
|
244
|
+
updatedLines.push(line);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Check if we're in verified section and this is requirement to move
|
|
249
|
+
if (inVerifiedSection && trimmed.startsWith('- ')) {
|
|
250
|
+
const lineRequirement = line.substring(2).trim();
|
|
251
|
+
// Remove date prefix if present (format: "2025-11-05: **requirement**")
|
|
252
|
+
const cleanRequirement = lineRequirement.replace(/^\d{4}-\d{2}-\d{2}:\s*\*\*/, '').replace(/\*\*$/, '').trim();
|
|
253
|
+
const cleanRequirementText = requirementText.replace(/\*\*/g, '').trim();
|
|
254
|
+
|
|
255
|
+
if (lineRequirement.includes(requirementText) || cleanRequirement === cleanRequirementText) {
|
|
256
|
+
// Skip this line (don't add it to updatedLines)
|
|
257
|
+
requirementFound = true;
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// If we're in not yet completed section and haven't added requirement yet, add it at the top
|
|
263
|
+
if (inNotYetCompleted && !requirementFound && trimmed.startsWith('- ')) {
|
|
264
|
+
// Add the requirement before the first existing requirement
|
|
265
|
+
const newUpdatedLines = lines.slice(0, i);
|
|
266
|
+
newUpdatedLines.push(`- ${requirementText}`);
|
|
267
|
+
newUpdatedLines.push('');
|
|
268
|
+
newUpdatedLines.push(...lines.slice(i));
|
|
269
|
+
await fs.writeFile(reqPath, newUpdatedLines.join('\n'));
|
|
270
|
+
return { success: true };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Reset section flags when hitting new sections
|
|
274
|
+
if (trimmed.startsWith('##')) {
|
|
275
|
+
inVerifiedSection = false;
|
|
276
|
+
inNotYetCompleted = false;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
updatedLines.push(line);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// If requirement wasn't found in verified section, try to add it to not yet completed anyway
|
|
283
|
+
if (!requirementFound) {
|
|
284
|
+
// Find the not yet completed section and add requirement at the top
|
|
285
|
+
const newUpdatedLines = lines.slice();
|
|
286
|
+
for (let i = 0; i < updatedLines.length; i++) {
|
|
287
|
+
if (updatedLines[i].includes('## ⏳ Requirements not yet completed')) {
|
|
288
|
+
// Find the first requirement line after this section
|
|
289
|
+
let insertIndex = i + 1;
|
|
290
|
+
while (insertIndex < updatedLines.length &&
|
|
291
|
+
(updatedLines[insertIndex].trim() === '' || updatedLines[insertIndex].trim().startsWith('#'))) {
|
|
292
|
+
insertIndex++;
|
|
293
|
+
}
|
|
294
|
+
updatedLines.splice(insertIndex, 0, `- ${requirementText}`, '');
|
|
295
|
+
await fs.writeFile(reqPath, updatedLines.join('\n'));
|
|
296
|
+
return { success: true };
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return { success: false, error: 'Requirement not found in verified section' };
|
|
302
|
+
} catch (error) {
|
|
303
|
+
return { success: false, error: error.message };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Move a requirement from TODO to "Requirements needing manual feedback" section
|
|
309
|
+
* @param {string} repoPath - Repository path
|
|
310
|
+
* @param {string} requirementText - The requirement text to move
|
|
311
|
+
* @param {string} questions - Clarifying questions to add
|
|
312
|
+
* @returns {Promise<{success: boolean, error?: string}>}
|
|
313
|
+
*/
|
|
314
|
+
async moveRequirementToFeedback(repoPath, requirementText, questions, findings = null) {
|
|
315
|
+
try {
|
|
316
|
+
const { getRequirementsPath } = require('../index');
|
|
317
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
318
|
+
|
|
319
|
+
if (!reqPath || !await fs.pathExists(reqPath)) {
|
|
320
|
+
return { success: false, error: 'Requirements file not found' };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
324
|
+
const lines = content.split('\n');
|
|
325
|
+
|
|
326
|
+
let inTodoSection = false;
|
|
327
|
+
let requirementRemoved = false;
|
|
328
|
+
let feedbackAdded = false;
|
|
329
|
+
|
|
330
|
+
for (const line of lines) {
|
|
331
|
+
const trimmed = line.trim();
|
|
332
|
+
|
|
333
|
+
// Find TODO section
|
|
334
|
+
if (trimmed.startsWith('##') && trimmed.includes('Requirements not yet completed')) {
|
|
335
|
+
inTodoSection = true;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Find "Requirements needing manual feedback" section
|
|
340
|
+
if (trimmed.startsWith('##') && (trimmed.includes('Requirements needing manual feedback') || trimmed.includes('❓ Requirements needing'))) {
|
|
341
|
+
inTodoSection = false;
|
|
342
|
+
const updatedLines = lines.slice(0, lines.indexOf(line));
|
|
343
|
+
updatedLines.push(line);
|
|
344
|
+
|
|
345
|
+
// Add blank line if not already present
|
|
346
|
+
if (updatedLines[updatedLines.length - 2]?.trim() !== '') {
|
|
347
|
+
updatedLines.push('');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Add requirement with findings and questions at the top of feedback section
|
|
351
|
+
if (!feedbackAdded) {
|
|
352
|
+
const today = new Date().toISOString().split('T')[0];
|
|
353
|
+
updatedLines.push(`- ${today}: **${requirementText}**`);
|
|
354
|
+
|
|
355
|
+
// Add AI findings if available
|
|
356
|
+
if (findings) {
|
|
357
|
+
updatedLines.push(' **AI found in codebase:**');
|
|
358
|
+
updatedLines.push(` ${findings}`);
|
|
359
|
+
updatedLines.push('');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
updatedLines.push(' **Clarifying questions:**');
|
|
363
|
+
const questionLines = questions.split('\n');
|
|
364
|
+
for (const q of questionLines) {
|
|
365
|
+
updatedLines.push(` ${q}`);
|
|
366
|
+
}
|
|
367
|
+
updatedLines.push('');
|
|
368
|
+
feedbackAdded = true;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
updatedLines.push(...lines.slice(lines.indexOf(line) + 1));
|
|
372
|
+
await fs.writeFile(reqPath, updatedLines.join('\n'));
|
|
373
|
+
return { success: true };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Stop sections at next header
|
|
377
|
+
if (trimmed.startsWith('##')) {
|
|
378
|
+
inTodoSection = false;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Remove requirement from TODO section (match first occurrence)
|
|
382
|
+
if (inTodoSection && !requirementRemoved && trimmed.startsWith('- ')) {
|
|
383
|
+
const reqText = trimmed.substring(2).trim();
|
|
384
|
+
// Clean both for comparison
|
|
385
|
+
const cleanReq = reqText.replace(/\*\*/g, '').replace(/^\d{4}-\d{2}-\d{2}:\s*/, '').trim();
|
|
386
|
+
const cleanTarget = requirementText.replace(/\*\*/g, '').trim();
|
|
387
|
+
|
|
388
|
+
if (cleanReq === cleanTarget || reqText.includes(requirementText) || requirementText.includes(cleanReq)) {
|
|
389
|
+
requirementRemoved = true;
|
|
390
|
+
continue; // Skip this line
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
updatedLines.push(line);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// If feedback section doesn't exist, create it
|
|
398
|
+
if (!feedbackAdded) {
|
|
399
|
+
// Find where to insert the feedback section (after TODO, before VERIFIED)
|
|
400
|
+
let insertIndex = -1;
|
|
401
|
+
for (let i = 0; i < updatedLines.length; i++) {
|
|
402
|
+
if (updatedLines[i].includes('## ✅ Verified by AI') || updatedLines[i].includes('---')) {
|
|
403
|
+
insertIndex = i;
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (insertIndex === -1) {
|
|
409
|
+
insertIndex = updatedLines.length;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const today = new Date().toISOString().split('T')[0];
|
|
413
|
+
const feedbackSection = [
|
|
414
|
+
'',
|
|
415
|
+
'## ❓ Requirements needing manual feedback',
|
|
416
|
+
'',
|
|
417
|
+
`- ${today}: **${requirementText}**`
|
|
418
|
+
];
|
|
419
|
+
|
|
420
|
+
// Add AI findings if available
|
|
421
|
+
if (findings) {
|
|
422
|
+
feedbackSection.push(' **AI found in codebase:**');
|
|
423
|
+
feedbackSection.push(` ${findings}`);
|
|
424
|
+
feedbackSection.push('');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
feedbackSection.push(' **Clarifying questions:**');
|
|
428
|
+
|
|
429
|
+
const questionLines = questions.split('\n');
|
|
430
|
+
for (const q of questionLines) {
|
|
431
|
+
feedbackSection.push(` ${q}`);
|
|
432
|
+
}
|
|
433
|
+
feedbackSection.push('');
|
|
434
|
+
|
|
435
|
+
updatedLines.splice(insertIndex, 0, ...feedbackSection);
|
|
436
|
+
feedbackAdded = true;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
await fs.writeFile(reqPath, updatedLines.join('\n'));
|
|
440
|
+
return { success: true };
|
|
441
|
+
} catch (error) {
|
|
442
|
+
return { success: false, error: error.message };
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Move completed requirement from TODO to TO VERIFY section (for human verification)
|
|
448
|
+
* @param {string} repoPath - Repository path
|
|
449
|
+
* @param {string} completedRequirement - The completed requirement text
|
|
450
|
+
* @returns {Promise<{success: boolean, error?: string}>}
|
|
451
|
+
*/
|
|
452
|
+
async moveCompletedRequirement(repoPath, completedRequirement) {
|
|
453
|
+
try {
|
|
454
|
+
const { getRequirementsPath } = require('../index');
|
|
455
|
+
const reqPath = await getRequirementsPath(repoPath);
|
|
456
|
+
|
|
457
|
+
if (!reqPath || !await fs.pathExists(reqPath)) {
|
|
458
|
+
return { success: false, error: 'Requirements file not found' };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const content = await fs.readFile(reqPath, 'utf8');
|
|
462
|
+
const lines = content.split('\n');
|
|
463
|
+
|
|
464
|
+
// First, check if requirement is already in TO VERIFY section
|
|
465
|
+
const verifySectionVariants = [
|
|
466
|
+
'## 🔍 TO VERIFY BY HUMAN',
|
|
467
|
+
'## 🔍 TO VERIFY',
|
|
468
|
+
'## TO VERIFY',
|
|
469
|
+
'## ✅ TO VERIFY',
|
|
470
|
+
'## ✅ Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG'
|
|
471
|
+
];
|
|
472
|
+
|
|
473
|
+
let inVerifySection = false;
|
|
474
|
+
let alreadyInVerify = false;
|
|
475
|
+
const normalizedCompleted = completedRequirement.trim();
|
|
476
|
+
const snippet = normalizedCompleted.substring(0, 80);
|
|
477
|
+
|
|
478
|
+
for (let i = 0; i < lines.length; i++) {
|
|
479
|
+
const line = lines[i];
|
|
480
|
+
const trimmed = line.trim();
|
|
481
|
+
|
|
482
|
+
// Check if we're entering TO VERIFY section
|
|
483
|
+
if (trimmed.startsWith('##')) {
|
|
484
|
+
inVerifySection = verifySectionVariants.some(variant => {
|
|
485
|
+
const variantTrimmed = variant.trim();
|
|
486
|
+
return trimmed === variantTrimmed || trimmed.startsWith(variantTrimmed);
|
|
487
|
+
}) && !trimmed.includes('## 📝 VERIFIED') && !trimmed.match(/^##\s+VERIFIED$/i);
|
|
488
|
+
|
|
489
|
+
if (inVerifySection && (trimmed.includes('TO VERIFY') || trimmed.includes('Verified by AI screenshot'))) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Check if we're leaving TO VERIFY section
|
|
495
|
+
if (inVerifySection && trimmed.startsWith('##') && !trimmed.startsWith('###')) {
|
|
496
|
+
const isVerifyHeader = verifySectionVariants.some(variant => {
|
|
497
|
+
const variantTrimmed = variant.trim();
|
|
498
|
+
return trimmed === variantTrimmed || trimmed.startsWith(variantTrimmed);
|
|
499
|
+
});
|
|
500
|
+
if (!isVerifyHeader) {
|
|
501
|
+
inVerifySection = false;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Check if requirement is already in TO VERIFY
|
|
506
|
+
if (inVerifySection && trimmed.startsWith('###')) {
|
|
507
|
+
const title = trimmed.replace(/^###\s*/, '').trim();
|
|
508
|
+
if (title) {
|
|
509
|
+
const normalizedTitle = title.trim();
|
|
510
|
+
if (normalizedTitle === normalizedCompleted ||
|
|
511
|
+
normalizedTitle.includes(normalizedCompleted) ||
|
|
512
|
+
normalizedCompleted.includes(normalizedTitle) ||
|
|
513
|
+
normalizedTitle.includes(snippet) ||
|
|
514
|
+
snippet.includes(normalizedTitle.substring(0, 80))) {
|
|
515
|
+
alreadyInVerify = true;
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (alreadyInVerify) {
|
|
523
|
+
return { success: true }; // Already in TO VERIFY, nothing to do
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Find requirement by its title (in ### header format)
|
|
527
|
+
// Normalize completed requirement text for matching
|
|
528
|
+
let requirementStartIndex = -1;
|
|
529
|
+
let requirementEndIndex = -1;
|
|
530
|
+
|
|
531
|
+
// Try to find the requirement in TODO section first
|
|
532
|
+
let inTodoSection = false;
|
|
533
|
+
for (let i = 0; i < lines.length; i++) {
|
|
534
|
+
const line = lines[i];
|
|
535
|
+
const trimmed = line.trim();
|
|
536
|
+
|
|
537
|
+
// Check if we're entering TODO section
|
|
538
|
+
if (trimmed.startsWith('##') && trimmed.includes('Requirements not yet completed')) {
|
|
539
|
+
inTodoSection = true;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Check if we're leaving TODO section
|
|
544
|
+
if (inTodoSection && trimmed.startsWith('##') && !trimmed.startsWith('###') && !trimmed.includes('Requirements not yet completed')) {
|
|
545
|
+
inTodoSection = false;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Only look for requirements in TODO section
|
|
549
|
+
if (inTodoSection && trimmed.startsWith('###')) {
|
|
550
|
+
const title = trimmed.replace(/^###\s*/, '').trim();
|
|
551
|
+
if (title) {
|
|
552
|
+
// Try multiple matching strategies
|
|
553
|
+
const normalizedTitle = title.trim();
|
|
554
|
+
const titleSnippet = normalizedTitle.substring(0, 80);
|
|
555
|
+
|
|
556
|
+
// Exact match
|
|
557
|
+
if (normalizedTitle === normalizedCompleted) {
|
|
558
|
+
requirementStartIndex = i;
|
|
559
|
+
}
|
|
560
|
+
// Check if either contains the other (for partial matches)
|
|
561
|
+
else if (normalizedTitle.includes(normalizedCompleted) || normalizedCompleted.includes(normalizedTitle)) {
|
|
562
|
+
requirementStartIndex = i;
|
|
563
|
+
}
|
|
564
|
+
// Check snippet matches
|
|
565
|
+
else if (normalizedTitle.includes(snippet) || snippet.includes(titleSnippet)) {
|
|
566
|
+
requirementStartIndex = i;
|
|
567
|
+
}
|
|
568
|
+
// Check if they start with the same (for REGRESSION: prefix issues)
|
|
569
|
+
else if (normalizedTitle.replace(/^REGRESSION:\s*/i, '') === normalizedCompleted.replace(/^REGRESSION:\s*/i, '') ||
|
|
570
|
+
normalizedTitle.replace(/^REGRESSION:\s*/i, '').includes(snippet.replace(/^REGRESSION:\s*/i, '')) ||
|
|
571
|
+
snippet.replace(/^REGRESSION:\s*/i, '').includes(normalizedTitle.replace(/^REGRESSION:\s*/i, ''))) {
|
|
572
|
+
requirementStartIndex = i;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (requirementStartIndex !== -1) {
|
|
576
|
+
// Find the end of this requirement (next ### or ## header)
|
|
577
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
578
|
+
const nextLine = lines[j].trim();
|
|
579
|
+
if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
|
|
580
|
+
requirementEndIndex = j;
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (requirementEndIndex === -1) {
|
|
585
|
+
requirementEndIndex = lines.length;
|
|
586
|
+
}
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (requirementStartIndex === -1) {
|
|
594
|
+
return { success: false, error: `Could not find requirement "${completedRequirement.substring(0, 60)}..." in TODO section` };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Extract the entire requirement block
|
|
598
|
+
const requirementBlock = lines.slice(requirementStartIndex, requirementEndIndex);
|
|
599
|
+
|
|
600
|
+
// Remove the requirement from its current location
|
|
601
|
+
lines.splice(requirementStartIndex, requirementEndIndex - requirementStartIndex);
|
|
602
|
+
|
|
603
|
+
// Find or create TO VERIFY BY HUMAN section (reuse the variants we defined earlier)
|
|
604
|
+
let verifyIndex = -1;
|
|
605
|
+
for (let i = 0; i < lines.length; i++) {
|
|
606
|
+
const line = lines[i];
|
|
607
|
+
const trimmed = line.trim();
|
|
608
|
+
|
|
609
|
+
// Check each variant more carefully
|
|
610
|
+
for (const variant of verifySectionVariants) {
|
|
611
|
+
const variantTrimmed = variant.trim();
|
|
612
|
+
// Exact match or line starts with variant
|
|
613
|
+
if (trimmed === variantTrimmed || trimmed.startsWith(variantTrimmed)) {
|
|
614
|
+
// Double-check: make sure it's NOT a VERIFIED section (without TO VERIFY)
|
|
615
|
+
if (!trimmed.includes('## 📝 VERIFIED') && !trimmed.match(/^##\s+VERIFIED$/i) &&
|
|
616
|
+
(trimmed.includes('TO VERIFY') || trimmed.includes('Verified by AI screenshot'))) {
|
|
617
|
+
verifyIndex = i;
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
if (verifyIndex !== -1) break;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (verifyIndex === -1) {
|
|
626
|
+
// Create TO VERIFY section - place it BEFORE VERIFIED section if one exists, otherwise before CHANGELOG
|
|
627
|
+
const verifiedIndex = lines.findIndex(line => {
|
|
628
|
+
const trimmed = line.trim();
|
|
629
|
+
return trimmed === '## 📝 VERIFIED' || trimmed.startsWith('## 📝 VERIFIED') ||
|
|
630
|
+
(trimmed.startsWith('##') && trimmed.includes('VERIFIED') && !trimmed.includes('TO VERIFY'));
|
|
631
|
+
});
|
|
632
|
+
const changelogIndex = lines.findIndex(line => line.includes('## CHANGELOG'));
|
|
633
|
+
const manualFeedbackIndex = lines.findIndex(line => line.trim().startsWith('## ❓'));
|
|
634
|
+
|
|
635
|
+
// Prefer: before VERIFIED > before CHANGELOG > before manual feedback > at end
|
|
636
|
+
let insertionIndex = lines.length;
|
|
637
|
+
if (verifiedIndex > 0) {
|
|
638
|
+
insertionIndex = verifiedIndex;
|
|
639
|
+
} else if (changelogIndex > 0) {
|
|
640
|
+
insertionIndex = changelogIndex;
|
|
641
|
+
} else if (manualFeedbackIndex > 0) {
|
|
642
|
+
insertionIndex = manualFeedbackIndex;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const block = [];
|
|
646
|
+
if (insertionIndex > 0 && lines[insertionIndex - 1].trim() !== '') {
|
|
647
|
+
block.push('');
|
|
648
|
+
}
|
|
649
|
+
block.push('## 🔍 TO VERIFY BY HUMAN', '');
|
|
650
|
+
lines.splice(insertionIndex, 0, ...block);
|
|
651
|
+
verifyIndex = lines.findIndex(line => {
|
|
652
|
+
const trimmed = line.trim();
|
|
653
|
+
return trimmed === '## 🔍 TO VERIFY BY HUMAN' || trimmed.startsWith('## 🔍 TO VERIFY BY HUMAN');
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
if (verifyIndex === -1) {
|
|
657
|
+
return { success: false, error: 'Failed to create TO VERIFY BY HUMAN section' };
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Safety check: verify we're not inserting into a VERIFIED section
|
|
662
|
+
const verifyLine = lines[verifyIndex] || '';
|
|
663
|
+
if (verifyLine.includes('## 📝 VERIFIED') || (verifyLine.trim().startsWith('##') && verifyLine.includes('VERIFIED') && !verifyLine.includes('TO VERIFY'))) {
|
|
664
|
+
return { success: false, error: 'Attempted to insert into VERIFIED section instead of TO VERIFY' };
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Insert requirement block at the TOP of TO VERIFY section (right after section header)
|
|
668
|
+
let insertIndex = verifyIndex + 1;
|
|
669
|
+
|
|
670
|
+
// Ensure there's a blank line after the section header
|
|
671
|
+
if (lines[insertIndex]?.trim() !== '') {
|
|
672
|
+
lines.splice(insertIndex, 0, '');
|
|
673
|
+
insertIndex++;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Insert the requirement block
|
|
677
|
+
lines.splice(insertIndex, 0, ...requirementBlock);
|
|
678
|
+
|
|
679
|
+
// Ensure there's a blank line after the requirement block
|
|
680
|
+
const afterIndex = insertIndex + requirementBlock.length;
|
|
681
|
+
if (afterIndex < lines.length && lines[afterIndex]?.trim() !== '') {
|
|
682
|
+
lines.splice(afterIndex, 0, '');
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Write the file
|
|
686
|
+
await fs.writeFile(reqPath, lines.join('\n'));
|
|
687
|
+
return { success: true };
|
|
688
|
+
} catch (error) {
|
|
689
|
+
return { success: false, error: error.message };
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Handle sending text to IDE with proper error handling and session limits
|
|
695
|
+
* @param {Object} event - Event object
|
|
696
|
+
* @param {string} ide - IDE name
|
|
697
|
+
* @param {string} text - Text to send
|
|
698
|
+
* @param {string} extension - Extension name
|
|
699
|
+
* @returns {Promise<Object>} Send result
|
|
700
|
+
*/
|
|
701
|
+
async handleSendText(event, ide, text, extension) {
|
|
702
|
+
try {
|
|
703
|
+
const manager = new AppleScriptManager();
|
|
704
|
+
|
|
705
|
+
// IMPORTANT: Distinguish between 'windsurf' IDE and 'vscode' with windsurf extension
|
|
706
|
+
// If ide is 'windsurf', use the standalone Windsurf IDE, NOT VS Code with extension
|
|
707
|
+
// Only route to VS Code if ide is explicitly 'vscode' with windsurf extension
|
|
708
|
+
let result;
|
|
709
|
+
if (ide === 'vscode' && extension === 'windsurf') {
|
|
710
|
+
// Use VS Code with Windsurf extension
|
|
711
|
+
result = await manager.sendText(text, 'vscode', global.currentRepoPath);
|
|
712
|
+
} else if (ide === 'windsurf') {
|
|
713
|
+
// For standalone Windsurf IDE, send directly to Windsurf
|
|
714
|
+
result = await manager.sendText(text, 'windsurf', global.currentRepoPath);
|
|
715
|
+
} else {
|
|
716
|
+
result = await manager.sendText(text, ide, global.currentRepoPath);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Handle Claude session limit
|
|
720
|
+
if (ide === 'claude' && result.success) {
|
|
721
|
+
// Wait a moment for Claude to respond
|
|
722
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
723
|
+
|
|
724
|
+
const sessionCheck = await manager.checkClaudeSessionLimit();
|
|
725
|
+
|
|
726
|
+
if (sessionCheck.sessionLimitReached) {
|
|
727
|
+
// Notify renderer process
|
|
728
|
+
if (global.mainWindow && global.mainWindow.webContents) {
|
|
729
|
+
global.mainWindow.webContents.send('claude-session-limit-reached', {
|
|
730
|
+
message: sessionCheck.message,
|
|
731
|
+
note: sessionCheck.note,
|
|
732
|
+
resetTime: sessionCheck.resetTime, // Pass reset time to UI
|
|
733
|
+
recentOutput: sessionCheck.recentOutput
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Pause auto mode if it's running
|
|
738
|
+
if (this.state.isAutoMode) {
|
|
739
|
+
this.state.isAutoMode = false;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
return {
|
|
743
|
+
success: false,
|
|
744
|
+
sessionLimitReached: true,
|
|
745
|
+
error: 'Claude session limit reached',
|
|
746
|
+
...sessionCheck
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Handle other IDE quota limits
|
|
752
|
+
if (result.success) {
|
|
753
|
+
const quotaChecks = [
|
|
754
|
+
{ ide: 'antigravity', checkMethod: 'checkAntigravityQuotaLimit', eventName: 'antigravity-quota-limit-reached' },
|
|
755
|
+
{ ide: 'cursor', checkMethod: 'checkCursorQuotaLimit', eventName: 'cursor-quota-limit-reached' },
|
|
756
|
+
{ ide: 'kiro', checkMethod: 'checkKiroQuotaLimit', eventName: 'kiro-quota-limit-reached' },
|
|
757
|
+
{ ide: 'windsurf', checkMethod: 'checkWindsurfRateLimit', eventName: 'windsurf-rate-limit-reached' }
|
|
758
|
+
];
|
|
759
|
+
|
|
760
|
+
for (const { ide: targetIde, checkMethod, eventName } of quotaChecks) {
|
|
761
|
+
if (ide === targetIde) {
|
|
762
|
+
try {
|
|
763
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
764
|
+
const quotaCheck = await manager[checkMethod]();
|
|
765
|
+
|
|
766
|
+
if (quotaCheck && quotaCheck.isRateLimited) {
|
|
767
|
+
// Notify renderer process
|
|
768
|
+
if (global.mainWindow && global.mainWindow.webContents) {
|
|
769
|
+
global.mainWindow.webContents.send(eventName, {
|
|
770
|
+
message: quotaCheck.message,
|
|
771
|
+
resumeAt: quotaCheck.resumeAt || null
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Pause auto mode if it's running
|
|
776
|
+
if (this.state.isAutoMode) {
|
|
777
|
+
this.state.isAutoMode = false;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return {
|
|
781
|
+
success: false,
|
|
782
|
+
rateLimited: true,
|
|
783
|
+
error: `${targetIde} quota limit reached`,
|
|
784
|
+
...quotaCheck
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
} catch (quotaErr) {
|
|
788
|
+
// Continue on quota check errors
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
return result;
|
|
795
|
+
} catch (error) {
|
|
796
|
+
return { success: false, error: error.message };
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Update auto mode state
|
|
802
|
+
* @param {Object} newState - New state to merge
|
|
803
|
+
*/
|
|
804
|
+
updateState(newState) {
|
|
805
|
+
Object.assign(this.state, newState);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Get current auto mode state
|
|
810
|
+
* @returns {Object} Current state
|
|
811
|
+
*/
|
|
812
|
+
getState() {
|
|
813
|
+
return { ...this.state };
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Reset auto mode state
|
|
818
|
+
*/
|
|
819
|
+
resetState() {
|
|
820
|
+
this.state = {
|
|
821
|
+
isAutoMode: false,
|
|
822
|
+
currentIDE: 'vscode',
|
|
823
|
+
chatCounter: 0,
|
|
824
|
+
maxChats: 100,
|
|
825
|
+
neverStop: false,
|
|
826
|
+
autoModeEvent: null,
|
|
827
|
+
currentMode: 'requirements',
|
|
828
|
+
currentSpecPath: null,
|
|
829
|
+
currentSpecId: null,
|
|
830
|
+
specsList: null,
|
|
831
|
+
currentSpecIndex: 0
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
module.exports = AutoModeBusinessLogic;
|