vibecodingmachine-cli 2026.3.14-1537 → 2026.6.17-1956
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/bin/auth/auth-compliance.js +7 -7
- package/bin/commands/agent-commands.js +15 -15
- package/bin/commands/auto-commands.js +3 -3
- package/bin/commands/command-aliases.js +13 -4
- package/bin/config/cli-config.js +15 -5
- package/bin/update/update-checker.js +5 -5
- package/bin/vibecodingmachine.js +2 -2
- package/package.json +2 -2
- package/src/commands/agents/add.js +5 -5
- package/src/commands/agents/check.js +19 -19
- package/src/commands/agents/list.js +24 -24
- package/src/commands/agents/remove.js +4 -4
- package/src/commands/agents-check.js +1 -1
- package/src/commands/analyze-file-sizes.js +43 -43
- package/src/commands/auto-direct/auto-provider-manager.js +19 -19
- package/src/commands/auto-direct/auto-start-phases.js +493 -0
- package/src/commands/auto-direct/auto-status-display.js +35 -35
- package/src/commands/auto-direct/auto-utils.js +50 -50
- package/src/commands/auto-direct/cline-installer.js +56 -0
- package/src/commands/auto-direct/code-processor.js +27 -27
- package/src/commands/auto-direct/file-scanner.js +19 -19
- package/src/commands/auto-direct/ide-completion-waiter.js +485 -0
- package/src/commands/auto-direct/ide-fallback-runner.js +226 -0
- package/src/commands/auto-direct/ide-provider-runner.js +103 -0
- package/src/commands/auto-direct/iteration-handlers.js +189 -0
- package/src/commands/auto-direct/iteration-runner.js +485 -0
- package/src/commands/auto-direct/provider-config.js +38 -7
- package/src/commands/auto-direct/provider-manager.js +132 -6
- package/src/commands/auto-direct/requirement-manager.js +169 -104
- package/src/commands/auto-direct/requirement-mover.js +350 -0
- package/src/commands/auto-direct/spec-handlers.js +155 -0
- package/src/commands/auto-direct/spec-ide-runner.js +318 -0
- package/src/commands/auto-direct/spec-processing.js +203 -0
- package/src/commands/auto-direct/status-display.js +9 -9
- package/src/commands/auto-direct/utils.js +83 -1
- package/src/commands/auto-direct-refactored.js +1 -413
- package/src/commands/auto-direct.js +127 -4119
- package/src/commands/auto-execution.js +21 -21
- package/src/commands/auto-status-helpers.js +0 -2
- package/src/commands/auto.js +22 -22
- package/src/commands/check-compliance.js +65 -65
- package/src/commands/computers.js +39 -39
- package/src/commands/continuous-scan.js +19 -19
- package/src/commands/ide.js +4 -4
- package/src/commands/locale.js +7 -7
- package/src/commands/refactor-file.js +59 -59
- package/src/commands/requirements/commands.js +17 -17
- package/src/commands/requirements/default-handlers.js +30 -30
- package/src/commands/requirements/disable.js +3 -3
- package/src/commands/requirements/enable.js +3 -3
- package/src/commands/requirements/utils.js +6 -6
- package/src/commands/requirements-refactored.js +3 -3
- package/src/commands/requirements-remote.js +38 -38
- package/src/commands/requirements.js +3 -3
- package/src/commands/settings.js +111 -0
- package/src/commands/specs/count.js +60 -0
- package/src/commands/specs/disable.js +3 -3
- package/src/commands/specs/enable.js +3 -3
- package/src/commands/status.js +10 -10
- package/src/commands/sync.js +25 -25
- package/src/commands/timeout.js +35 -35
- package/src/trui/TruiInterface.js +2 -2
- package/src/trui/agents/AgentInterface.js +4 -4
- package/src/trui/agents/handlers/CommandHandler.js +4 -4
- package/src/trui/agents/handlers/ContextManager.js +1 -1
- package/src/trui/agents/handlers/DisplayHandler.js +11 -11
- package/src/trui/agents/handlers/HelpHandler.js +1 -1
- package/src/utils/agent-selector.js +6 -6
- package/src/utils/antigravity-installer.js +4 -4
- package/src/utils/asset-cleanup.js +1 -1
- package/src/utils/auth.js +9 -12
- package/src/utils/clarification-actions.js +4 -4
- package/src/utils/cline-js-handler.js +5 -5
- package/src/utils/compliance-check.js +6 -6
- package/src/utils/config.js +12 -12
- package/src/utils/display-formatters-complete.js +2 -2
- package/src/utils/display-formatters-extracted.js +2 -2
- package/src/utils/display-formatters.js +2 -2
- package/src/utils/feedback-handler.js +2 -2
- package/src/utils/first-run.js +7 -7
- package/src/utils/ide-detection.js +1 -1
- package/src/utils/ide-handlers.js +6 -6
- package/src/utils/interactive/clarification-actions.js +3 -3
- package/src/utils/interactive/core-ui.js +7 -7
- package/src/utils/interactive/file-backup.js +6 -6
- package/src/utils/interactive/file-import-export.js +49 -49
- package/src/utils/interactive/file-operations.js +3 -3
- package/src/utils/interactive/file-validation.js +41 -41
- package/src/utils/interactive/interactive-prompts.js +41 -41
- package/src/utils/interactive/requirement-actions.js +5 -5
- package/src/utils/interactive/requirement-crud.js +4 -4
- package/src/utils/interactive/requirements-navigation.js +10 -10
- package/src/utils/interactive-broken.js +6 -6
- package/src/utils/interactive.js +37 -37
- package/src/utils/keyboard-handler.js +4 -4
- package/src/utils/prompt-helper.js +6 -6
- package/src/utils/provider-checker/agent-checker.js +1 -1
- package/src/utils/provider-checker/agent-runner.js +203 -314
- package/src/utils/provider-checker/agents-file-lock.js +134 -0
- package/src/utils/provider-checker/agents-manager.js +224 -36
- package/src/utils/provider-checker/cli-installer.js +28 -28
- package/src/utils/provider-checker/cli-utils.js +2 -2
- package/src/utils/provider-checker/cursor-approval-clicker.js +108 -0
- package/src/utils/provider-checker/format-utils.js +4 -4
- package/src/utils/provider-checker/ide-installer-helper.js +96 -0
- package/src/utils/provider-checker/ide-manager.js +19 -8
- package/src/utils/provider-checker/ide-quota-checker.js +120 -0
- package/src/utils/provider-checker/ide-utils.js +2 -2
- package/src/utils/provider-checker/node-detector.js +4 -4
- package/src/utils/provider-checker/node-utils.js +5 -5
- package/src/utils/provider-checker/opencode-checker.js +107 -73
- package/src/utils/provider-checker/process-utils.js +1 -1
- package/src/utils/provider-checker/provider-validator.js +11 -11
- package/src/utils/provider-checker/quota-checker.js +5 -5
- package/src/utils/provider-checker/quota-detector.js +5 -5
- package/src/utils/provider-checker/requirements-manager.js +6 -6
- package/src/utils/provider-checker/test-requirements.js +1 -1
- package/src/utils/provider-checker/vscode-approval-clicker.js +328 -0
- package/src/utils/provider-checker-new.js +6 -6
- package/src/utils/provider-checker.js +6 -6
- package/src/utils/provider-checkers/ide-manager.js +13 -13
- package/src/utils/provider-checkers/node-executable-finder.js +4 -4
- package/src/utils/provider-checkers/provider-checker-core.js +5 -5
- package/src/utils/provider-checkers/provider-checker-main.js +17 -17
- package/src/utils/provider-registry.js +5 -6
- package/src/utils/provider-utils.js +12 -12
- package/src/utils/quota-detectors.js +32 -32
- package/src/utils/requirement-action-handlers.js +12 -12
- package/src/utils/requirement-actions/requirement-operations.js +3 -3
- package/src/utils/requirement-actions.js +1 -1
- package/src/utils/requirement-file-operations.js +5 -5
- package/src/utils/requirement-helpers.js +1 -1
- package/src/utils/requirement-management.js +5 -5
- package/src/utils/requirement-navigation.js +2 -2
- package/src/utils/requirement-organization.js +3 -3
- package/src/utils/rui-trui-adapter.js +14 -14
- package/src/utils/simple-trui.js +3 -3
- package/src/utils/status-helpers-extracted.js +3 -3
- package/src/utils/trui-clarifications.js +11 -11
- package/src/utils/trui-debug.js +3 -2
- package/src/utils/trui-devin.js +217 -0
- package/src/utils/trui-feedback.js +7 -7
- package/src/utils/trui-kiro-integration.js +34 -34
- package/src/utils/trui-main-handlers.js +20 -21
- package/src/utils/trui-main-menu.js +19 -19
- package/src/utils/trui-nav-agents.js +59 -8
- package/src/utils/trui-nav-requirements.js +3 -3
- package/src/utils/trui-nav-settings.js +10 -10
- package/src/utils/trui-nav-specifications.js +1 -1
- package/src/utils/trui-navigation-backup.js +11 -11
- package/src/utils/trui-navigation.js +9 -9
- package/src/utils/trui-provider-health.js +25 -25
- package/src/utils/trui-provider-manager.js +28 -28
- package/src/utils/trui-quick-menu.js +2 -2
- package/src/utils/trui-req-actions-backup.js +21 -21
- package/src/utils/trui-req-actions.js +20 -20
- package/src/utils/trui-req-editor.js +10 -10
- package/src/utils/trui-req-file-ops.js +3 -3
- package/src/utils/trui-req-tree.js +7 -7
- package/src/utils/trui-windsurf.js +103 -103
- package/src/utils/user-tracking.js +15 -15
- package/src/utils/trui-req-tree-old.js +0 -719
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { execSync, spawn } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Periodically clicks the Allow/Run approval button in VS Code-based IDEs
|
|
10
|
+
* (github-copilot, amazon-q, kiro) which display approval dialogs in WebViews
|
|
11
|
+
* that AppleScript accessibility cannot reach. Uses Python CGEvent for clicking.
|
|
12
|
+
*
|
|
13
|
+
* Returns a cleanup function that stops the interval.
|
|
14
|
+
*/
|
|
15
|
+
function startVSCodeApprovalClicker(providerId, resolvedRef, testMessage) {
|
|
16
|
+
let vsCodeApprovalAttempt = 0;
|
|
17
|
+
let vsCodeApprovalInterval;
|
|
18
|
+
|
|
19
|
+
const clickVSCodeAllow = async () => {
|
|
20
|
+
if (resolvedRef.resolved) { clearInterval(vsCodeApprovalInterval); return; }
|
|
21
|
+
vsCodeApprovalAttempt++;
|
|
22
|
+
console.log(`[AGENT CHECK] ${providerId}: clicking Allow button (attempt ${vsCodeApprovalAttempt})`);
|
|
23
|
+
|
|
24
|
+
// Activate the right app
|
|
25
|
+
const activateScript = providerId === 'kiro'
|
|
26
|
+
? `tell application "Kiro" to activate`
|
|
27
|
+
: `tell application "Visual Studio Code" to activate`;
|
|
28
|
+
try {
|
|
29
|
+
execSync(`osascript -e '${activateScript}'`, { timeout: 3000, stdio: 'pipe' });
|
|
30
|
+
} catch (_) {}
|
|
31
|
+
|
|
32
|
+
// Kiro: dismiss any "Restore Checkpoint" / confirmation modal that blocks the chat input.
|
|
33
|
+
// These are native buttons accessible via AppleScript accessibility.
|
|
34
|
+
if (providerId === 'kiro') {
|
|
35
|
+
const dismissScript = `
|
|
36
|
+
tell application "System Events"
|
|
37
|
+
tell process "Kiro"
|
|
38
|
+
set btnNames to {"Cancel", "No", "Dismiss", "Close"}
|
|
39
|
+
repeat with w from 1 to count of windows
|
|
40
|
+
try
|
|
41
|
+
repeat with btn in buttons of window w
|
|
42
|
+
try
|
|
43
|
+
if name of btn is in btnNames then
|
|
44
|
+
click btn
|
|
45
|
+
return "dismissed: " & (name of btn)
|
|
46
|
+
end if
|
|
47
|
+
end try
|
|
48
|
+
end repeat
|
|
49
|
+
end try
|
|
50
|
+
end repeat
|
|
51
|
+
end tell
|
|
52
|
+
end tell
|
|
53
|
+
`;
|
|
54
|
+
const dismissFile = path.join(os.tmpdir(), 'kiro_dismiss.scpt');
|
|
55
|
+
fs.writeFileSync(dismissFile, dismissScript);
|
|
56
|
+
try { execSync(`osascript "${dismissFile}"`, { timeout: 4000, stdio: 'pipe' }); } catch (_) {}
|
|
57
|
+
try { fs.unlinkSync(dismissFile); } catch (_) {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Get window bounds via Python Quartz
|
|
61
|
+
let winBounds = null;
|
|
62
|
+
try {
|
|
63
|
+
const appName = providerId === 'kiro' ? 'Kiro' : 'Code';
|
|
64
|
+
const pyScript = `import Quartz, sys
|
|
65
|
+
windows = Quartz.CGWindowListCopyWindowInfo(Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID)
|
|
66
|
+
for w in windows:
|
|
67
|
+
if w.get('kCGWindowOwnerName','') == '${appName}':
|
|
68
|
+
b = w.get('kCGWindowBounds',{})
|
|
69
|
+
x,y,w2,h = int(b.get('X',0)),int(b.get('Y',0)),int(b.get('Width',0)),int(b.get('Height',0))
|
|
70
|
+
if w2 > 100:
|
|
71
|
+
print(str(x)+','+str(y)+','+str(w2)+','+str(h))
|
|
72
|
+
sys.exit(0)
|
|
73
|
+
sys.exit(1)
|
|
74
|
+
`;
|
|
75
|
+
const pyFile = path.join(os.tmpdir(), `${providerId}_bounds.py`);
|
|
76
|
+
fs.writeFileSync(pyFile, pyScript);
|
|
77
|
+
const boundsOut = execSync(`python3 "${pyFile}"`, { encoding: 'utf8', timeout: 5000 }).trim();
|
|
78
|
+
try { fs.unlinkSync(pyFile); } catch (_) {}
|
|
79
|
+
if (boundsOut && boundsOut.includes(',')) {
|
|
80
|
+
const nums = boundsOut.match(/-?\d+/g);
|
|
81
|
+
if (nums && nums.length >= 4) {
|
|
82
|
+
const [x, y, w, h] = nums.map(Number);
|
|
83
|
+
if (w > 100) {
|
|
84
|
+
winBounds = { x, y, w, h };
|
|
85
|
+
console.log(`[AGENT CHECK] ${providerId}: window bounds = ${x},${y},${w},${h}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (boundsErr) {
|
|
90
|
+
console.log(`[AGENT CHECK] ${providerId}: bounds error: ${boundsErr.message}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!winBounds) {
|
|
94
|
+
console.log(`[AGENT CHECK] ${providerId}: Could not get window bounds, using full-screen CGEvent coordinate sweep`);
|
|
95
|
+
const fullSweepPy = `
|
|
96
|
+
import Quartz, time
|
|
97
|
+
|
|
98
|
+
def click(x, y):
|
|
99
|
+
e1 = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseDown, (x, y), Quartz.kCGMouseButtonLeft)
|
|
100
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, e1)
|
|
101
|
+
time.sleep(0.04)
|
|
102
|
+
e2 = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseUp, (x, y), Quartz.kCGMouseButtonLeft)
|
|
103
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, e2)
|
|
104
|
+
time.sleep(0.07)
|
|
105
|
+
|
|
106
|
+
for x in [1400, 1500, 1600, 1700, 1200, 1300, 1100]:
|
|
107
|
+
for y in [400, 500, 600, 700, 300, 800]:
|
|
108
|
+
click(x, y)
|
|
109
|
+
`;
|
|
110
|
+
const sweepFile = path.join(os.tmpdir(), `${providerId}_fullsweep.py`);
|
|
111
|
+
fs.writeFileSync(sweepFile, fullSweepPy);
|
|
112
|
+
const sweepChild = spawn('python3', [sweepFile], { detached: true, stdio: 'ignore' });
|
|
113
|
+
sweepChild.on('exit', () => { try { fs.unlinkSync(sweepFile); } catch (_) {} });
|
|
114
|
+
sweepChild.unref();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Try OCR via screenshot
|
|
119
|
+
let ocrResult = '';
|
|
120
|
+
const screenshotPath = path.join(os.tmpdir(), `${providerId}_allow_${Date.now()}.png`);
|
|
121
|
+
let screenshotOk = false;
|
|
122
|
+
try {
|
|
123
|
+
execSync(`screencapture -x -R ${winBounds.x},${winBounds.y},${winBounds.w},${winBounds.h} "${screenshotPath}"`, { timeout: 5000, stdio: 'pipe' });
|
|
124
|
+
screenshotOk = true;
|
|
125
|
+
} catch (_) {
|
|
126
|
+
console.log(`[AGENT CHECK] ${providerId}: Screenshot failed, using coordinate sweep`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (screenshotOk) {
|
|
130
|
+
const swiftScript = `
|
|
131
|
+
import Vision
|
|
132
|
+
import AppKit
|
|
133
|
+
import Foundation
|
|
134
|
+
let screenshotPath = "${screenshotPath}"
|
|
135
|
+
let winX: CGFloat = ${winBounds.x}
|
|
136
|
+
let winY: CGFloat = ${winBounds.y}
|
|
137
|
+
let winW: CGFloat = ${winBounds.w}
|
|
138
|
+
let winH: CGFloat = ${winBounds.h}
|
|
139
|
+
guard let img = NSImage(contentsOfFile: screenshotPath),
|
|
140
|
+
let bitmap = img.representations.first as? NSBitmapImageRep,
|
|
141
|
+
let cgImg = bitmap.cgImage else {
|
|
142
|
+
print("ERROR:no-image")
|
|
143
|
+
exit(1)
|
|
144
|
+
}
|
|
145
|
+
let handler = VNImageRequestHandler(cgImage: cgImg, options: [:])
|
|
146
|
+
let request = VNRecognizeTextRequest()
|
|
147
|
+
request.recognitionLevel = .accurate
|
|
148
|
+
try! handler.perform([request])
|
|
149
|
+
for result in request.results ?? [] {
|
|
150
|
+
for candidate in result.topCandidates(1) {
|
|
151
|
+
let lower = candidate.string.lowercased().trimmingCharacters(in: .whitespaces)
|
|
152
|
+
if lower == "allow" || lower == "yes" || lower == "yes, allow" || lower == "yes, allow this time" || lower == "run" || lower == "approve" || lower == "accept" || lower == "apply" || lower == "apply changes" || lower.hasPrefix("run ") || lower == "insert" || lower == "insert code" || lower == "execute" || lower == "confirm" || lower == "proceed" || lower == "ok" || lower == "continue" || lower == "run tool" || lower == "allow all" {
|
|
153
|
+
let box = result.boundingBox
|
|
154
|
+
let centerX = winX + box.midX * winW
|
|
155
|
+
let centerY = winY + (1.0 - box.midY) * winH
|
|
156
|
+
print("FOUND:\\(candidate.string):\\(Int(centerX)):\\(Int(centerY))")
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
const swiftFile = path.join(os.tmpdir(), `${providerId}_allow_ocr.swift`);
|
|
162
|
+
fs.writeFileSync(swiftFile, swiftScript);
|
|
163
|
+
try {
|
|
164
|
+
ocrResult = execSync(`swift "${swiftFile}" 2>/dev/null`, { encoding: 'utf8', timeout: 20000 }).trim();
|
|
165
|
+
} catch (_) {}
|
|
166
|
+
try { fs.unlinkSync(swiftFile); } catch (_) {}
|
|
167
|
+
try { fs.unlinkSync(screenshotPath); } catch (_) {}
|
|
168
|
+
|
|
169
|
+
if (ocrResult && ocrResult.includes('FOUND:')) {
|
|
170
|
+
const lines = ocrResult.split('\n').filter(l => l.startsWith('FOUND:'));
|
|
171
|
+
for (const line of lines) {
|
|
172
|
+
const parts = line.split(':');
|
|
173
|
+
if (parts.length >= 4) {
|
|
174
|
+
const clickX = parseInt(parts[2]);
|
|
175
|
+
const clickY = parseInt(parts[3]);
|
|
176
|
+
if (!isNaN(clickX) && !isNaN(clickY)) {
|
|
177
|
+
console.log(`[AGENT CHECK] ${providerId}: OCR found Allow button at (${clickX}, ${clickY}), clicking via CGEvent`);
|
|
178
|
+
const appToActivate = providerId === 'kiro' ? `tell application "Kiro" to activate` : `tell application "Visual Studio Code" to activate`;
|
|
179
|
+
const activateScriptOcr = `${appToActivate}\ndelay 0.8`;
|
|
180
|
+
const activateFile = path.join(os.tmpdir(), `${providerId}_activate.scpt`);
|
|
181
|
+
fs.writeFileSync(activateFile, activateScriptOcr);
|
|
182
|
+
try { execSync(`osascript "${activateFile}"`, { timeout: 3000, stdio: 'pipe' }); } catch (_) {}
|
|
183
|
+
try { fs.unlinkSync(activateFile); } catch (_) {}
|
|
184
|
+
const dropdownX = clickX + 30;
|
|
185
|
+
const pyScript = `
|
|
186
|
+
import Quartz
|
|
187
|
+
import time
|
|
188
|
+
|
|
189
|
+
def click(x, y):
|
|
190
|
+
e1 = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseDown, (x, y), Quartz.kCGMouseButtonLeft)
|
|
191
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, e1)
|
|
192
|
+
time.sleep(0.05)
|
|
193
|
+
e2 = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseUp, (x, y), Quartz.kCGMouseButtonLeft)
|
|
194
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, e2)
|
|
195
|
+
time.sleep(0.1)
|
|
196
|
+
|
|
197
|
+
# Click the dropdown arrow (▼) first, then Allow All
|
|
198
|
+
click(${dropdownX}, ${clickY})
|
|
199
|
+
time.sleep(0.5)
|
|
200
|
+
click(${dropdownX}, ${clickY})
|
|
201
|
+
time.sleep(0.3)
|
|
202
|
+
# Try allow button at various Y offsets (OCR can be off by up to 10px)
|
|
203
|
+
for dy in [8, 0, -8, 16, -16, 24, -24]:
|
|
204
|
+
click(${clickX}, ${clickY} + dy)
|
|
205
|
+
time.sleep(0.12)
|
|
206
|
+
`;
|
|
207
|
+
const pyFile = path.join(os.tmpdir(), `${providerId}_click.py`);
|
|
208
|
+
fs.writeFileSync(pyFile, pyScript);
|
|
209
|
+
try {
|
|
210
|
+
execSync(`python3 "${pyFile}"`, { timeout: 8000, stdio: 'pipe' });
|
|
211
|
+
} catch (_) {}
|
|
212
|
+
try { fs.unlinkSync(pyFile); } catch (_) {}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// For amazon-q: on even-numbered attempts, re-send the test message by clicking the
|
|
221
|
+
// chat input at the bottom of the right panel. The initial send via Cmd+Shift+I may
|
|
222
|
+
// have closed the panel (toggle behavior) causing the paste to land in the editor.
|
|
223
|
+
if (providerId === 'amazon-q' && vsCodeApprovalAttempt % 2 === 0 && testMessage) {
|
|
224
|
+
const inputX = Math.round(winBounds.x + winBounds.w * 0.83);
|
|
225
|
+
const inputY = Math.round(winBounds.y + winBounds.h - 62);
|
|
226
|
+
console.log(`[AGENT CHECK] amazon-q: re-sending via chat input at (${inputX}, ${inputY})`);
|
|
227
|
+
const msgFile = path.join(os.tmpdir(), `amazon_q_msg_${Date.now()}.txt`);
|
|
228
|
+
fs.writeFileSync(msgFile, testMessage, 'utf8');
|
|
229
|
+
const reSendPy = `
|
|
230
|
+
import Quartz, time, subprocess
|
|
231
|
+
|
|
232
|
+
def click(x, y):
|
|
233
|
+
p = (x, y)
|
|
234
|
+
e1 = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseDown, p, Quartz.kCGMouseButtonLeft)
|
|
235
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, e1)
|
|
236
|
+
time.sleep(0.05)
|
|
237
|
+
e2 = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseUp, p, Quartz.kCGMouseButtonLeft)
|
|
238
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, e2)
|
|
239
|
+
time.sleep(0.1)
|
|
240
|
+
|
|
241
|
+
# Click the Amazon Q chat input (bottom of right panel)
|
|
242
|
+
click(${inputX}, ${inputY})
|
|
243
|
+
time.sleep(0.4)
|
|
244
|
+
|
|
245
|
+
# Copy message to clipboard via pbcopy
|
|
246
|
+
with open('${msgFile}', 'r') as f:
|
|
247
|
+
msg = f.read()
|
|
248
|
+
subprocess.run(['pbcopy'], input=msg.encode(), check=True)
|
|
249
|
+
time.sleep(0.2)
|
|
250
|
+
|
|
251
|
+
# Select-all, delete old content, paste new message, Enter
|
|
252
|
+
subprocess.run(['osascript', '-e',
|
|
253
|
+
'tell application "System Events" to tell process "Code" to keystroke "a" using command down'])
|
|
254
|
+
time.sleep(0.2)
|
|
255
|
+
subprocess.run(['osascript', '-e',
|
|
256
|
+
'tell application "System Events" to tell process "Code" to key code 51'])
|
|
257
|
+
time.sleep(0.2)
|
|
258
|
+
subprocess.run(['osascript', '-e',
|
|
259
|
+
'tell application "System Events" to tell process "Code" to key code 9 using {command down}'])
|
|
260
|
+
time.sleep(0.5)
|
|
261
|
+
subprocess.run(['osascript', '-e',
|
|
262
|
+
'tell application "System Events" to tell process "Code" to key code 36'])
|
|
263
|
+
time.sleep(0.2)
|
|
264
|
+
import os
|
|
265
|
+
try: os.unlink('${msgFile}')
|
|
266
|
+
except: pass
|
|
267
|
+
`;
|
|
268
|
+
const reSendPyFile = path.join(os.tmpdir(), `amazon_q_resend_${Date.now()}.py`);
|
|
269
|
+
fs.writeFileSync(reSendPyFile, reSendPy);
|
|
270
|
+
try {
|
|
271
|
+
execSync(`python3 "${reSendPyFile}"`, { timeout: 12000, stdio: 'pipe' });
|
|
272
|
+
} catch (e) {
|
|
273
|
+
console.log(`[AGENT CHECK] amazon-q: re-send error: ${e.message}`);
|
|
274
|
+
}
|
|
275
|
+
try { fs.unlinkSync(reSendPyFile); } catch (_) {}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// OCR didn't find the button — fall back to CGEvent coordinate sweep
|
|
280
|
+
console.log(`[AGENT CHECK] ${providerId}: OCR did not find Allow button, trying CGEvent coordinate sweep`);
|
|
281
|
+
const activateCmd2 = providerId === 'kiro' ? `tell application "Kiro" to activate` : `tell application "Visual Studio Code" to activate`;
|
|
282
|
+
const activateScpt = path.join(os.tmpdir(), `${providerId}_activate2.scpt`);
|
|
283
|
+
fs.writeFileSync(activateScpt, `${activateCmd2}\ndelay 0.5`);
|
|
284
|
+
try { execSync(`osascript "${activateScpt}"`, { timeout: 3000, stdio: 'pipe' }); } catch (_) {}
|
|
285
|
+
try { fs.unlinkSync(activateScpt); } catch (_) {}
|
|
286
|
+
const xPcts = [0.96, 0.80, 0.87, 0.75, 0.93, 0.65];
|
|
287
|
+
const yPcts = [0.61, 0.50, 0.58, 0.42, 0.65, 0.35, 0.72, 0.68];
|
|
288
|
+
const sweepCoords = [];
|
|
289
|
+
for (const xp of xPcts) {
|
|
290
|
+
for (const yp of yPcts) {
|
|
291
|
+
sweepCoords.push([Math.round(winBounds.x + winBounds.w * xp), Math.round(winBounds.y + winBounds.h * yp)]);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const fallbackPy = `
|
|
295
|
+
import Quartz
|
|
296
|
+
import time
|
|
297
|
+
|
|
298
|
+
def click(x, y):
|
|
299
|
+
e1 = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseDown, (x, y), Quartz.kCGMouseButtonLeft)
|
|
300
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, e1)
|
|
301
|
+
time.sleep(0.04)
|
|
302
|
+
e2 = Quartz.CGEventCreateMouseEvent(None, Quartz.kCGEventLeftMouseUp, (x, y), Quartz.kCGMouseButtonLeft)
|
|
303
|
+
Quartz.CGEventPost(Quartz.kCGHIDEventTap, e2)
|
|
304
|
+
time.sleep(0.08)
|
|
305
|
+
|
|
306
|
+
coords = ${JSON.stringify(sweepCoords)}
|
|
307
|
+
for x, y in coords:
|
|
308
|
+
click(x, y)
|
|
309
|
+
`;
|
|
310
|
+
const fallbackPyFile = path.join(os.tmpdir(), `${providerId}_sweep.py`);
|
|
311
|
+
fs.writeFileSync(fallbackPyFile, fallbackPy);
|
|
312
|
+
const approveChild = spawn('python3', [fallbackPyFile], { detached: true, stdio: 'ignore' });
|
|
313
|
+
approveChild.on('exit', () => { try { fs.unlinkSync(fallbackPyFile); } catch (_) {} });
|
|
314
|
+
approveChild.unref();
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// Start after 15s, then every 12s
|
|
318
|
+
setTimeout(() => {
|
|
319
|
+
if (!resolvedRef.resolved) {
|
|
320
|
+
clickVSCodeAllow();
|
|
321
|
+
vsCodeApprovalInterval = setInterval(clickVSCodeAllow, 12000);
|
|
322
|
+
}
|
|
323
|
+
}, 15000);
|
|
324
|
+
|
|
325
|
+
return () => clearInterval(vsCodeApprovalInterval);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
module.exports = { startVSCodeApprovalClicker };
|
|
@@ -5,10 +5,10 @@ const { checkProvider, checkAllProviders, formatCheckedAt } = require('./provide
|
|
|
5
5
|
const { DIRECT_TIMEOUT_MS, IDE_TIMEOUT_MS } = require('./provider-checkers/provider-checker-core');
|
|
6
6
|
|
|
7
7
|
// Re-export the main API
|
|
8
|
-
module.exports = {
|
|
9
|
-
checkProvider,
|
|
10
|
-
checkAllProviders,
|
|
11
|
-
formatCheckedAt,
|
|
12
|
-
DIRECT_TIMEOUT_MS,
|
|
13
|
-
IDE_TIMEOUT_MS
|
|
8
|
+
module.exports = {
|
|
9
|
+
checkProvider,
|
|
10
|
+
checkAllProviders,
|
|
11
|
+
formatCheckedAt,
|
|
12
|
+
DIRECT_TIMEOUT_MS,
|
|
13
|
+
IDE_TIMEOUT_MS
|
|
14
14
|
};
|
|
@@ -6,10 +6,10 @@ const { formatCheckedAt } = require('./provider-checker/time-formatter');
|
|
|
6
6
|
const { DIRECT_TIMEOUT_MS, IDE_TIMEOUT_MS } = require('./provider-checker/agent-runner');
|
|
7
7
|
|
|
8
8
|
// Re-export the main API
|
|
9
|
-
module.exports = {
|
|
10
|
-
checkProvider,
|
|
11
|
-
checkAllProviders,
|
|
12
|
-
formatCheckedAt,
|
|
13
|
-
DIRECT_TIMEOUT_MS,
|
|
14
|
-
IDE_TIMEOUT_MS
|
|
9
|
+
module.exports = {
|
|
10
|
+
checkProvider,
|
|
11
|
+
checkAllProviders,
|
|
12
|
+
formatCheckedAt,
|
|
13
|
+
DIRECT_TIMEOUT_MS,
|
|
14
|
+
IDE_TIMEOUT_MS
|
|
15
15
|
};
|
|
@@ -19,15 +19,15 @@ const IDE_INFO = {
|
|
|
19
19
|
linux: ['/usr/bin/cursor', '/usr/local/bin/cursor']
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
|
-
'
|
|
23
|
-
app: '
|
|
24
|
-
binaryName: '
|
|
25
|
-
processName: '
|
|
26
|
-
bundleId: '
|
|
22
|
+
'devin': {
|
|
23
|
+
app: 'Devin',
|
|
24
|
+
binaryName: 'devin',
|
|
25
|
+
processName: 'devin',
|
|
26
|
+
bundleId: 'ai.cognition.devin',
|
|
27
27
|
paths: {
|
|
28
|
-
darwin: ['/Applications/
|
|
29
|
-
win32: ['C:\\Program Files\\
|
|
30
|
-
linux: ['/usr/bin/
|
|
28
|
+
darwin: ['/Applications/Devin.app'],
|
|
29
|
+
win32: ['C:\\Program Files\\Devin\\Devin.exe', 'C:\\Program Files (x86)\\Devin\\Devin.exe'],
|
|
30
|
+
linux: ['/usr/bin/devin', '/usr/local/bin/devin']
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
'vscode': {
|
|
@@ -70,10 +70,10 @@ function getIdeProcess(providerId) {
|
|
|
70
70
|
if (!info) return null;
|
|
71
71
|
|
|
72
72
|
try {
|
|
73
|
-
const cmd = process.platform === 'win32'
|
|
73
|
+
const cmd = process.platform === 'win32'
|
|
74
74
|
? `tasklist /FI "IMAGENAME eq ${info.binaryName}.exe"`
|
|
75
75
|
: `ps aux | grep -i "${info.processName}" | grep -v grep`;
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
const result = execSync(cmd, { encoding: 'utf8' });
|
|
78
78
|
return result.trim().length > 0 ? result.trim() : null;
|
|
79
79
|
} catch {
|
|
@@ -97,19 +97,19 @@ async function launchIde(providerId) {
|
|
|
97
97
|
try {
|
|
98
98
|
if (platform === 'darwin') {
|
|
99
99
|
// macOS: use open command
|
|
100
|
-
spawn('open', ['-a', info.app], {
|
|
100
|
+
spawn('open', ['-a', info.app], {
|
|
101
101
|
stdio: 'ignore',
|
|
102
102
|
detached: true
|
|
103
103
|
}).unref();
|
|
104
104
|
} else if (platform === 'win32') {
|
|
105
105
|
// Windows: spawn the executable
|
|
106
|
-
spawn(idePath, [], {
|
|
106
|
+
spawn(idePath, [], {
|
|
107
107
|
stdio: 'ignore',
|
|
108
108
|
detached: true
|
|
109
109
|
}).unref();
|
|
110
110
|
} else {
|
|
111
111
|
// Linux: spawn the binary
|
|
112
|
-
spawn(info.binaryName, [], {
|
|
112
|
+
spawn(info.binaryName, [], {
|
|
113
113
|
stdio: 'ignore',
|
|
114
114
|
detached: true
|
|
115
115
|
}).unref();
|
|
@@ -13,7 +13,7 @@ function getNodeExecutable() {
|
|
|
13
13
|
if (process.env.NODE_BINARY_PATH) {
|
|
14
14
|
return process.env.NODE_BINARY_PATH;
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
// Try to find node in PATH
|
|
18
18
|
try {
|
|
19
19
|
const nodeCmd = process.platform === 'win32' ? 'where node' : 'which node';
|
|
@@ -22,7 +22,7 @@ function getNodeExecutable() {
|
|
|
22
22
|
return nodePath;
|
|
23
23
|
}
|
|
24
24
|
} catch (_) {}
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
// Fallback: try common node installation paths
|
|
27
27
|
const commonPaths = process.platform === 'win32'
|
|
28
28
|
? [
|
|
@@ -36,13 +36,13 @@ function getNodeExecutable() {
|
|
|
36
36
|
'/usr/local/bin/node',
|
|
37
37
|
'/opt/homebrew/bin/node'
|
|
38
38
|
];
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
for (const nodePath of commonPaths) {
|
|
41
41
|
if (nodePath && fs.existsSync(nodePath)) {
|
|
42
42
|
return nodePath;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
// Last resort: use process.execPath (might be electron but worth trying)
|
|
47
47
|
console.warn('[AGENT CHECK] Could not find node executable, falling back to process.execPath');
|
|
48
48
|
return process.execPath;
|
|
@@ -69,13 +69,13 @@ async function checkProviderReachable(providerId, config, repoPath) {
|
|
|
69
69
|
async function done(result) {
|
|
70
70
|
if (resolved) return;
|
|
71
71
|
resolved = true;
|
|
72
|
-
try {
|
|
73
|
-
if (watcher) watcher.close();
|
|
72
|
+
try {
|
|
73
|
+
if (watcher) watcher.close();
|
|
74
74
|
} catch {}
|
|
75
75
|
clearTimeout(timeout);
|
|
76
76
|
clearInterval(cancelCheckInterval);
|
|
77
|
-
try {
|
|
78
|
-
if (child) child.kill();
|
|
77
|
+
try {
|
|
78
|
+
if (child) child.kill();
|
|
79
79
|
} catch {}
|
|
80
80
|
resolve(result);
|
|
81
81
|
}
|
|
@@ -123,7 +123,7 @@ async function checkProviderReachable(providerId, config, repoPath) {
|
|
|
123
123
|
try {
|
|
124
124
|
const nodePath = getNodeExecutable();
|
|
125
125
|
const args = [CLI_ENTRY_POINT, 'check-provider', providerId];
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
child = spawn(nodePath, args, {
|
|
128
128
|
stdio: 'ignore',
|
|
129
129
|
env,
|
|
@@ -25,12 +25,12 @@ async function checkProvider(providerId, config, repoPath, onProgress, signal) {
|
|
|
25
25
|
if (repoPath) {
|
|
26
26
|
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
27
27
|
let reqPath;
|
|
28
|
-
try {
|
|
29
|
-
reqPath = await getRequirementsPath(repoPath);
|
|
28
|
+
try {
|
|
29
|
+
reqPath = await getRequirementsPath(repoPath);
|
|
30
30
|
} catch {
|
|
31
31
|
reqPath = path.join(repoPath, '.vibecodingmachine', 'REQUIREMENTS.md');
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
if (fs.existsSync(reqPath)) {
|
|
35
35
|
const content = fs.readFileSync(reqPath, 'utf8');
|
|
36
36
|
if (!content.includes('AGENT_CHECK')) {
|
|
@@ -44,10 +44,10 @@ async function checkProvider(providerId, config, repoPath, onProgress, signal) {
|
|
|
44
44
|
return result;
|
|
45
45
|
|
|
46
46
|
} catch (error) {
|
|
47
|
-
return {
|
|
48
|
-
status: 'error',
|
|
47
|
+
return {
|
|
48
|
+
status: 'error',
|
|
49
49
|
message: `Check failed: ${error.message}`,
|
|
50
|
-
error: error.stack
|
|
50
|
+
error: error.stack
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -61,7 +61,7 @@ async function checkAllProviders(config, repoPath, onProgress, signal) {
|
|
|
61
61
|
|
|
62
62
|
for (const id of providerIds) {
|
|
63
63
|
if (signal?.aborted) break;
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
if (onProgress) onProgress(id, 'starting');
|
|
66
66
|
results[id] = await checkProvider(id, config, repoPath, onProgress, signal);
|
|
67
67
|
if (onProgress) onProgress(id, 'done', results[id]);
|
|
@@ -71,16 +71,16 @@ async function checkAllProviders(config, repoPath, onProgress, signal) {
|
|
|
71
71
|
if (repoPath) {
|
|
72
72
|
const { getRequirementsPath } = require('vibecodingmachine-core');
|
|
73
73
|
let reqPath;
|
|
74
|
-
try {
|
|
75
|
-
reqPath = await getRequirementsPath(repoPath);
|
|
74
|
+
try {
|
|
75
|
+
reqPath = await getRequirementsPath(repoPath);
|
|
76
76
|
} catch {
|
|
77
77
|
reqPath = path.join(repoPath, '.vibecodingmachine', 'REQUIREMENTS.md');
|
|
78
78
|
}
|
|
79
|
-
try {
|
|
80
|
-
removeTestRequirement(reqPath);
|
|
79
|
+
try {
|
|
80
|
+
removeTestRequirement(reqPath);
|
|
81
81
|
} catch {}
|
|
82
|
-
try {
|
|
83
|
-
fs.unlinkSync(getResultFilePath(repoPath));
|
|
82
|
+
try {
|
|
83
|
+
fs.unlinkSync(getResultFilePath(repoPath));
|
|
84
84
|
} catch {}
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -100,8 +100,8 @@ function formatCheckedAt(checkedAt) {
|
|
|
100
100
|
return `${months[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()} at ${timePart}`;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
module.exports = {
|
|
104
|
-
checkProvider,
|
|
105
|
-
checkAllProviders,
|
|
106
|
-
formatCheckedAt
|
|
103
|
+
module.exports = {
|
|
104
|
+
checkProvider,
|
|
105
|
+
checkAllProviders,
|
|
106
|
+
formatCheckedAt
|
|
107
107
|
};
|
|
@@ -41,15 +41,14 @@ const PROVIDER_DEFINITIONS = [
|
|
|
41
41
|
estimatedSpeed: 35000
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
|
-
id: '
|
|
45
|
-
name: '
|
|
44
|
+
id: 'devin',
|
|
45
|
+
name: 'Devin AI Agent',
|
|
46
46
|
type: 'ide',
|
|
47
47
|
category: 'ide',
|
|
48
|
-
ide: '
|
|
48
|
+
ide: 'devin',
|
|
49
49
|
defaultModel: 'default',
|
|
50
50
|
subAgents: [
|
|
51
|
-
{ id: '
|
|
52
|
-
{ id: 'windsurf-swe-1-lite', name: 'SWE-1-lite', model: 'swe-1-lite' }
|
|
51
|
+
{ id: 'devin-default', name: 'Default', model: 'default' }
|
|
53
52
|
],
|
|
54
53
|
estimatedSpeed: 90000
|
|
55
54
|
},
|
|
@@ -169,7 +168,7 @@ function getProviderDisplayName(id) {
|
|
|
169
168
|
'ollama': 'provider.ollama.name',
|
|
170
169
|
'lmstudio': 'provider.lmstudio.name',
|
|
171
170
|
'cursor': 'ide.agent.cursor',
|
|
172
|
-
'
|
|
171
|
+
'devin': 'ide.agent.devin',
|
|
173
172
|
'vscode': 'ide.agent.vscode',
|
|
174
173
|
'cline': 'ide.agent.cline',
|
|
175
174
|
'claude-code': 'ide.agent.claude.code',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider Utils Module
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Contains utility functions for provider checking.
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ function getNodeExecutable() {
|
|
|
19
19
|
if (process.env.NODE_BINARY_PATH) {
|
|
20
20
|
return process.env.NODE_BINARY_PATH;
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
// Try to find node in PATH
|
|
24
24
|
try {
|
|
25
25
|
const nodeCmd = process.platform === 'win32' ? 'where node' : 'which node';
|
|
@@ -28,7 +28,7 @@ function getNodeExecutable() {
|
|
|
28
28
|
return nodePath;
|
|
29
29
|
}
|
|
30
30
|
} catch (_) {}
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
// Fallback: try common node installation paths
|
|
33
33
|
const commonPaths = process.platform === 'win32'
|
|
34
34
|
? [
|
|
@@ -42,13 +42,13 @@ function getNodeExecutable() {
|
|
|
42
42
|
'/usr/local/bin/node',
|
|
43
43
|
'/opt/homebrew/bin/node'
|
|
44
44
|
];
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
for (const nodePath of commonPaths) {
|
|
47
47
|
if (nodePath && fs.existsSync(nodePath)) {
|
|
48
48
|
return nodePath;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
// Last resort: use process.execPath (might be electron but worth trying)
|
|
53
53
|
console.warn('[AGENT CHECK] Could not find node executable, falling back to process.execPath');
|
|
54
54
|
return process.execPath;
|
|
@@ -66,10 +66,10 @@ function getResultFilePath(repoPath) {
|
|
|
66
66
|
*/
|
|
67
67
|
function removeTestRequirement(reqPath) {
|
|
68
68
|
if (!fs.existsSync(reqPath)) return;
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
const content = fs.readFileSync(reqPath, 'utf8');
|
|
71
71
|
const lines = content.split('\n');
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
// Find and remove the test requirement
|
|
74
74
|
const startIdx = lines.findIndex(line => line.includes('### AGENT CHECK TEST'));
|
|
75
75
|
if (startIdx !== -1) {
|
|
@@ -113,7 +113,7 @@ It will be automatically removed after the check completes.
|
|
|
113
113
|
try {
|
|
114
114
|
// Ensure directory exists
|
|
115
115
|
fs.mkdirSync(path.dirname(reqPath), { recursive: true });
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
// Read existing content
|
|
118
118
|
let content = '';
|
|
119
119
|
if (fs.existsSync(reqPath)) {
|
|
@@ -121,7 +121,7 @@ It will be automatically removed after the check completes.
|
|
|
121
121
|
} else {
|
|
122
122
|
content = '# Requirements\n\n';
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
// Add test requirement
|
|
126
126
|
if (!content.includes('### AGENT CHECK TEST')) {
|
|
127
127
|
content += '\n' + testReq;
|
|
@@ -140,10 +140,10 @@ function parseResultFile(resultFile) {
|
|
|
140
140
|
if (!fs.existsSync(resultFile)) {
|
|
141
141
|
return null;
|
|
142
142
|
}
|
|
143
|
-
|
|
143
|
+
|
|
144
144
|
const content = fs.readFileSync(resultFile, 'utf8');
|
|
145
145
|
const lines = content.trim().split('\n');
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
// Find the result line (should be the last non-empty line)
|
|
148
148
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
149
149
|
const line = lines[i].trim();
|
|
@@ -158,7 +158,7 @@ function parseResultFile(resultFile) {
|
|
|
158
158
|
} catch (error) {
|
|
159
159
|
console.warn('[AGENT CHECK] Error parsing result file:', error.message);
|
|
160
160
|
}
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
return null;
|
|
163
163
|
}
|
|
164
164
|
|