@bugmole/cli 0.4.0
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/.bugmole.env.example +20 -0
- package/LICENSE +7 -0
- package/README.md +293 -0
- package/TESTING.md +117 -0
- package/bugmole.config.yaml +39 -0
- package/package.json +85 -0
- package/scripts/billing/paypal-setup.mjs +121 -0
- package/scripts/bugmole-continue.ts +318 -0
- package/scripts/bugmole-init.ts +188 -0
- package/scripts/bugmole.cjs +17 -0
- package/scripts/bugmole.test.ts +344 -0
- package/scripts/bugmole.ts +657 -0
- package/scripts/ensure-maestro.cjs +79 -0
- package/scripts/ios-tunnel-keeper.sh +45 -0
- package/scripts/ios-wda-keeper.sh +66 -0
- package/scripts/sync-plan-catalog.d.mts +3 -0
- package/scripts/sync-plan-catalog.mjs +16 -0
- package/scripts/ui-parity-diff.py +65 -0
- package/scripts/ui-parity-requirements.txt +1 -0
- package/scripts/verify-manage-to-plans.mts +194 -0
- package/spec/app-ui-audit.schema.json +176 -0
- package/spec/blockers.yaml +79 -0
- package/spec/bugs.index.json +42 -0
- package/spec/design-dna.schema.json +38 -0
- package/spec/domain_rules.yaml +24 -0
- package/spec/flows/flow_manage-to-plans-64c3c61b.yaml +8 -0
- package/spec/flows/flow_screen-to-evidence-6c3ab309.yaml +7 -0
- package/spec/journey_graph.yaml +126 -0
- package/spec/journeys.graph.json +2618 -0
- package/spec/plans/dashboard-smoke.flow.yaml +20 -0
- package/spec/plans/example.flow.yaml +99 -0
- package/spec/plans/flow_api-keys-to-logout-7396cd5d.flow.yaml +33 -0
- package/spec/plans/flow_api-keys-to-screen-157e10a6.flow.yaml +33 -0
- package/spec/plans/flow_manage-to-audits-176c1eb8.flow.yaml +33 -0
- package/spec/plans/flow_manage-to-blockers-633282c8.flow.yaml +112 -0
- package/spec/plans/flow_manage-to-devices-ad57e202.flow.yaml +33 -0
- package/spec/plans/flow_manage-to-logout-86c54656.flow.yaml +33 -0
- package/spec/plans/flow_manage-to-manage-resource-action-6325f99a.flow.yaml +185 -0
- package/spec/plans/flow_manage-to-parity-issues-01fa2579.flow.yaml +34 -0
- package/spec/plans/flow_manage-to-plans-64c3c61b.flow.yaml +123 -0
- package/spec/plans/flow_manage-to-screen-6857f915.flow.yaml +106 -0
- package/spec/plans/flow_manage-to-tasks-c5791c95.flow.yaml +33 -0
- package/spec/plans/flow_profile-to-screen-6045aa3d.flow.yaml +33 -0
- package/spec/plans/flow_register-to-api-auth-login-460a9cc8.flow.yaml +34 -0
- package/spec/plans/flow_screen-to-audits-07666356.flow.yaml +33 -0
- package/spec/plans/flow_screen-to-blockers-091d258b.flow.yaml +33 -0
- package/spec/plans/flow_screen-to-devices-cad72f75.flow.yaml +33 -0
- package/spec/plans/flow_screen-to-evidence-6c3ab309.flow.yaml +126 -0
- package/spec/plans/flow_screen-to-parity-issues-2218e3ad.flow.yaml +34 -0
- package/spec/plans/flow_screen-to-plans-8232a94f.flow.yaml +33 -0
- package/spec/plans/flow_screen-to-tasks-540671d1.flow.yaml +33 -0
- package/spec/plans/login.flow.yaml +22 -0
- package/spec/plans/owner-operations.flow.yaml +20 -0
- package/spec/project_config.yaml +55 -0
- package/spec/roles.yaml +30 -0
- package/spec/schema.md +394 -0
- package/spec/test-case-results.schema.json +62 -0
- package/spec/test-cases.schema.json +85 -0
- package/spec/ui-parity-audit.schema.json +194 -0
- package/spec/ui-reverse-engineering.schema.json +94 -0
- package/src/billing/plan-catalog.test.ts +46 -0
- package/src/billing/plan-catalog.ts +199 -0
- package/src/integrations/aws-sigv4.test.ts +42 -0
- package/src/integrations/aws-sigv4.ts +72 -0
- package/src/integrations/device-farm.ts +155 -0
- package/src/integrations/github-app.test.ts +57 -0
- package/src/integrations/github-app.ts +143 -0
- package/src/integrations/gitlab.ts +81 -0
- package/src/integrations/temp-email.test.ts +123 -0
- package/src/integrations/temp-email.ts +175 -0
- package/src/integrations/testflight-feedback.test.ts +51 -0
- package/src/integrations/testflight-feedback.ts +173 -0
- package/src/integrations/webdriver-client.ts +131 -0
- package/src/mcp/server.test.ts +1220 -0
- package/src/mcp/server.ts +3064 -0
- package/src/mcp/write-test-cases.test.ts +287 -0
- package/src/registry/api-key-client.ts +39 -0
- package/src/registry/control-plane-client.ts +212 -0
- package/src/registry/migrations/0001_registry.sql +47 -0
- package/src/registry/migrations/0002_device_authorizations.sql +23 -0
- package/src/registry/migrations/0003_project_environments.sql +25 -0
- package/src/registry/migrations/0004_device_authorization_email.sql +1 -0
- package/src/registry/migrations/0005_testing_control_plane.sql +55 -0
- package/src/registry/migrations/0006_workspaces.sql +36 -0
- package/src/registry/migrations/0007_project_apps.sql +26 -0
- package/src/registry/migrations/0008_agent_tasks.sql +30 -0
- package/src/registry/migrations/0009_journey_revisions.sql +17 -0
- package/src/registry/migrations/0010_agent_task_journey.sql +2 -0
- package/src/registry/migrations/0011_run_journey_revision.sql +1 -0
- package/src/registry/migrations/0012_canonical_flow_execution.sql +10 -0
- package/src/registry/migrations/0013_device_sessions.sql +22 -0
- package/src/registry/migrations/0014_agent_task_step.sql +1 -0
- package/src/registry/migrations/0015_agent_task_attempts.sql +6 -0
- package/src/registry/migrations/0016_github_issue_tracker.sql +54 -0
- package/src/registry/migrations/0017_run_targets.sql +7 -0
- package/src/registry/migrations/0018_run_fix_from.sql +4 -0
- package/src/registry/migrations/0019_workspace_flags.sql +9 -0
- package/src/registry/migrations/0020_orgs.sql +40 -0
- package/src/registry/migrations/0021_billing_core.sql +58 -0
- package/src/registry/migrations/0022_cloud_runners.sql +19 -0
- package/src/registry/migrations/0023_signup.sql +4 -0
- package/src/registry/migrations/0024_billing.sql +67 -0
- package/src/registry/migrations/0025_notifications.sql +47 -0
- package/src/registry/migrations/0026_repo_bindings.sql +28 -0
- package/src/registry/migrations/0027_feedback.sql +29 -0
- package/src/registry/migrations/0028_devices.sql +48 -0
- package/src/registry/migrations/0029_sso.sql +31 -0
- package/src/registry/migrations/0030_workspace_domains.sql +18 -0
- package/src/registry/migrations/0031_gitlab_and_teams.sql +45 -0
- package/src/registry/migrations/0032_personas.sql +15 -0
- package/src/registry/migrations/0033_bugmole_rename.sql +11 -0
- package/src/registry/task-scheduling.test.ts +100 -0
- package/src/registry/task-scheduling.ts +80 -0
- package/src/registry-worker/ai/platform-model.ts +77 -0
- package/src/registry-worker/ai/routes.test.ts +88 -0
- package/src/registry-worker/ai/routes.ts +90 -0
- package/src/registry-worker/artifacts.test.ts +98 -0
- package/src/registry-worker/artifacts.ts +85 -0
- package/src/registry-worker/billing/billing-core.test.ts +175 -0
- package/src/registry-worker/billing/checkout-routes.ts +209 -0
- package/src/registry-worker/billing/enforcement.ts +69 -0
- package/src/registry-worker/billing/entitlements.ts +108 -0
- package/src/registry-worker/billing/ledger.ts +186 -0
- package/src/registry-worker/billing/paypal/api.ts +259 -0
- package/src/registry-worker/billing/paypal/client.ts +91 -0
- package/src/registry-worker/billing/paypal/provider.ts +143 -0
- package/src/registry-worker/billing/paypal.test.ts +466 -0
- package/src/registry-worker/billing/provider.ts +114 -0
- package/src/registry-worker/billing/routes.ts +66 -0
- package/src/registry-worker/billing/subscriptions.ts +780 -0
- package/src/registry-worker/billing/thresholds.ts +107 -0
- package/src/registry-worker/core.ts +308 -0
- package/src/registry-worker/devices/devices.test.ts +185 -0
- package/src/registry-worker/devices/policy.ts +71 -0
- package/src/registry-worker/devices/routes.ts +453 -0
- package/src/registry-worker/domains/domains.test.ts +210 -0
- package/src/registry-worker/domains/routes.ts +139 -0
- package/src/registry-worker/domains.ts +88 -0
- package/src/registry-worker/email/sender.ts +75 -0
- package/src/registry-worker/env.d.ts +14716 -0
- package/src/registry-worker/features.ts +20 -0
- package/src/registry-worker/feedback/feedback.test.ts +230 -0
- package/src/registry-worker/feedback/format.ts +148 -0
- package/src/registry-worker/feedback/routes.ts +386 -0
- package/src/registry-worker/flags.ts +39 -0
- package/src/registry-worker/github/checks.test.ts +177 -0
- package/src/registry-worker/github/checks.ts +374 -0
- package/src/registry-worker/gitlab/checks.test.ts +141 -0
- package/src/registry-worker/gitlab/checks.ts +349 -0
- package/src/registry-worker/hooks.ts +54 -0
- package/src/registry-worker/index.ts +2077 -0
- package/src/registry-worker/jobs/index.ts +29 -0
- package/src/registry-worker/jobs/retention.ts +68 -0
- package/src/registry-worker/mcp/mcp.test.ts +355 -0
- package/src/registry-worker/mcp/routes.ts +215 -0
- package/src/registry-worker/mcp/token.ts +126 -0
- package/src/registry-worker/mcp/tools.ts +563 -0
- package/src/registry-worker/notifications/alerts.ts +212 -0
- package/src/registry-worker/notifications/notifications.test.ts +298 -0
- package/src/registry-worker/notifications/outbox.ts +83 -0
- package/src/registry-worker/notifications/routes.ts +280 -0
- package/src/registry-worker/notifications/secrets.ts +49 -0
- package/src/registry-worker/notifications/slack.ts +96 -0
- package/src/registry-worker/notifications/teams.ts +46 -0
- package/src/registry-worker/org/audit.ts +116 -0
- package/src/registry-worker/org/routes.test.ts +163 -0
- package/src/registry-worker/org/routes.ts +302 -0
- package/src/registry-worker/personas/personas.test.ts +78 -0
- package/src/registry-worker/personas/routes.ts +100 -0
- package/src/registry-worker/repo-triggers.ts +20 -0
- package/src/registry-worker/routes/index.ts +74 -0
- package/src/registry-worker/run-events.ts +24 -0
- package/src/registry-worker/runner/dispatch.ts +219 -0
- package/src/registry-worker/runner/jobs.ts +43 -0
- package/src/registry-worker/runner/metering.ts +82 -0
- package/src/registry-worker/runner/policy.ts +59 -0
- package/src/registry-worker/runner/routes.ts +171 -0
- package/src/registry-worker/runner/runner.test.ts +358 -0
- package/src/registry-worker/runner/tokens.ts +93 -0
- package/src/registry-worker/runs.test.ts +60 -0
- package/src/registry-worker/signup/policy.ts +57 -0
- package/src/registry-worker/signup/routes.ts +106 -0
- package/src/registry-worker/signup/signup.test.ts +81 -0
- package/src/registry-worker/sso/aegis.ts +141 -0
- package/src/registry-worker/sso/membership.ts +157 -0
- package/src/registry-worker/sso/routes.ts +458 -0
- package/src/registry-worker/sso/sso.test.ts +344 -0
- package/src/registry-worker/testing/d1-shim.ts +180 -0
- package/src/registry-worker/testing/harness.ts +137 -0
- package/src/runner-worker/index.ts +108 -0
- package/src/runtime/ai-analysis.ts +97 -0
- package/src/runtime/ai-exploration.test.ts +32 -0
- package/src/runtime/ai-exploration.ts +69 -0
- package/src/runtime/ai-repair.ts +74 -0
- package/src/runtime/ai-work.test.ts +99 -0
- package/src/runtime/android-screen-record.test.ts +75 -0
- package/src/runtime/android-screen-record.ts +192 -0
- package/src/runtime/app-understanding.test.ts +123 -0
- package/src/runtime/app-understanding.ts +201 -0
- package/src/runtime/appium-driver.test.ts +179 -0
- package/src/runtime/appium-driver.ts +295 -0
- package/src/runtime/blocker-resolution.test.ts +113 -0
- package/src/runtime/blocker-resolution.ts +111 -0
- package/src/runtime/browser-matrix.integration.test.ts +212 -0
- package/src/runtime/browser-matrix.test.ts +143 -0
- package/src/runtime/browser-matrix.ts +200 -0
- package/src/runtime/canonical-flow.test.ts +52 -0
- package/src/runtime/config-validate.ts +185 -0
- package/src/runtime/continuous-execution.ts +291 -0
- package/src/runtime/cursor-applescript.ts +573 -0
- package/src/runtime/cursor-cli-driver.test.ts +78 -0
- package/src/runtime/cursor-cli-driver.ts +156 -0
- package/src/runtime/cursor-driver-example.ts +117 -0
- package/src/runtime/cursor-driver-index.ts +65 -0
- package/src/runtime/cursor-driver-init.ts +277 -0
- package/src/runtime/cursor-driver-run.test.ts +15 -0
- package/src/runtime/cursor-driver-run.ts +323 -0
- package/src/runtime/cursor-driver.ts +332 -0
- package/src/runtime/cursor-llm-example.ts +90 -0
- package/src/runtime/cursor-llm.ts +206 -0
- package/src/runtime/cursor-mcp-monitor.ts +386 -0
- package/src/runtime/device-clouds/browserstack.ts +73 -0
- package/src/runtime/device-clouds/device-farm.ts +52 -0
- package/src/runtime/device-clouds/index.ts +92 -0
- package/src/runtime/device-clouds/kobiton.ts +70 -0
- package/src/runtime/device-clouds/targets.ts +44 -0
- package/src/runtime/device-clouds/types.ts +62 -0
- package/src/runtime/diff-proposal.ts +84 -0
- package/src/runtime/discovery-task.test.ts +29 -0
- package/src/runtime/discovery-task.ts +284 -0
- package/src/runtime/driver-recovery.ts +69 -0
- package/src/runtime/driver.ts +79 -0
- package/src/runtime/environment.test.ts +104 -0
- package/src/runtime/environment.ts +137 -0
- package/src/runtime/executor.test.ts +509 -0
- package/src/runtime/executor.ts +921 -0
- package/src/runtime/explorer.test.ts +101 -0
- package/src/runtime/explorer.ts +1013 -0
- package/src/runtime/failure-analysis.test.ts +111 -0
- package/src/runtime/failure-analysis.ts +272 -0
- package/src/runtime/fixtures/fake-maestro.sh +36 -0
- package/src/runtime/flow-language.test.ts +268 -0
- package/src/runtime/flow-language.ts +414 -0
- package/src/runtime/init-wizard.ts +354 -0
- package/src/runtime/ios-screen-record.test.ts +68 -0
- package/src/runtime/ios-screen-record.ts +155 -0
- package/src/runtime/journey-editor.ts +452 -0
- package/src/runtime/journey-evidence.test.ts +161 -0
- package/src/runtime/journey-evidence.ts +180 -0
- package/src/runtime/journey-graph.test.ts +257 -0
- package/src/runtime/journey-graph.ts +170 -0
- package/src/runtime/legacy-names.ts +32 -0
- package/src/runtime/llm-example.ts +105 -0
- package/src/runtime/llm.ts +527 -0
- package/src/runtime/local-browser.test.ts +45 -0
- package/src/runtime/local-browser.ts +48 -0
- package/src/runtime/local-registry-stub.test.ts +325 -0
- package/src/runtime/local-registry-stub.ts +803 -0
- package/src/runtime/maestro-driver.test.ts +84 -0
- package/src/runtime/maestro-driver.ts +209 -0
- package/src/runtime/mole-voice.ts +21 -0
- package/src/runtime/nav-crawl.test.ts +100 -0
- package/src/runtime/nav-crawl.ts +153 -0
- package/src/runtime/pipeline.test.ts +405 -0
- package/src/runtime/pipeline.ts +833 -0
- package/src/runtime/planner.test.ts +37 -0
- package/src/runtime/planner.ts +274 -0
- package/src/runtime/platform-ai.ts +76 -0
- package/src/runtime/playwright-driver.test.ts +93 -0
- package/src/runtime/playwright-driver.ts +620 -0
- package/src/runtime/project-spec.ts +140 -0
- package/src/runtime/record-run-verdicts.ts +68 -0
- package/src/runtime/reporter.test.ts +56 -0
- package/src/runtime/reporter.ts +158 -0
- package/src/runtime/reset.test.ts +44 -0
- package/src/runtime/reset.ts +61 -0
- package/src/runtime/reviewer.test.ts +73 -0
- package/src/runtime/reviewer.ts +158 -0
- package/src/runtime/run-job.ts +136 -0
- package/src/runtime/run-once.test.ts +207 -0
- package/src/runtime/run-once.ts +168 -0
- package/src/runtime/run.ts +132 -0
- package/src/runtime/screen-recording.ts +34 -0
- package/src/runtime/serve-gateway.test.ts +74 -0
- package/src/runtime/serve-gateway.ts +164 -0
- package/src/runtime/serve-worker.test.ts +23 -0
- package/src/runtime/serve-worker.ts +278 -0
- package/src/runtime/site-discovery.test.ts +168 -0
- package/src/runtime/site-discovery.ts +308 -0
- package/src/runtime/target-runner.ts +144 -0
- package/src/runtime/test-case-verdicts.test.ts +94 -0
- package/src/runtime/test-case-verdicts.ts +120 -0
- package/src/runtime/ui-reverse-engineering/coordinator.test.ts +59 -0
- package/src/runtime/ui-reverse-engineering/coordinator.ts +391 -0
- package/src/runtime/ui-reverse-engineering/types.ts +197 -0
- package/src/runtime/web-suite.test.ts +97 -0
- package/src/runtime/web-suite.ts +176 -0
- package/src/storage/create-object-store.ts +144 -0
- package/src/storage/keys.ts +34 -0
- package/src/storage/local-artifact-server.test.ts +314 -0
- package/src/storage/local-artifact-server.ts +357 -0
- package/src/storage/object-store.test.ts +28 -0
- package/src/storage/object-store.ts +101 -0
- package/src/storage/registry-object-store.ts +88 -0
- package/src/storage/remote-object-store.ts +104 -0
- package/src/storage/storage-directory.test.ts +43 -0
- package/src/storage/storage-directory.ts +24 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Using CURSOR_DRIVER as a simple LLM replacement
|
|
3
|
+
*
|
|
4
|
+
* This demonstrates the simplest way to use CURSOR_DRIVER - just like any LLM API.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { cursorLLM, cursorLLMStructured } from "./cursor-llm.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Example 1: Simple text completion (like OpenAI API)
|
|
11
|
+
*
|
|
12
|
+
* Just pass a prompt, get back text. That's it.
|
|
13
|
+
*/
|
|
14
|
+
export async function exampleSimple() {
|
|
15
|
+
const answer = await cursorLLM("Analyze the error handling patterns in this codebase");
|
|
16
|
+
console.log(answer);
|
|
17
|
+
return answer;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Example 2: Structured response with actions and artifacts
|
|
22
|
+
*/
|
|
23
|
+
export async function exampleStructured() {
|
|
24
|
+
const response = await cursorLLMStructured(
|
|
25
|
+
"Review test failures and propose fixes. Focus on selector mismatches.",
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (response.success && response.payload) {
|
|
29
|
+
console.log("Status:", response.payload.status);
|
|
30
|
+
console.log("Summary:", response.payload.summary);
|
|
31
|
+
console.log("Actions:", response.payload.actions);
|
|
32
|
+
console.log("Artifacts:", response.payload.artifacts);
|
|
33
|
+
console.log("Full Answer:", response.payload.answer_markdown);
|
|
34
|
+
} else {
|
|
35
|
+
console.error("Error:", response.error);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return response;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Example 3: With custom options (optional)
|
|
43
|
+
*/
|
|
44
|
+
export async function exampleWithOptions() {
|
|
45
|
+
const answer = await cursorLLM("Refactor the authentication module", {
|
|
46
|
+
timeout: 600000, // 10 minutes
|
|
47
|
+
requestId: `refactor_${Date.now()}`,
|
|
48
|
+
customInstructions: "Focus on security best practices",
|
|
49
|
+
});
|
|
50
|
+
return answer;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Example 4: Using in a workflow
|
|
55
|
+
*/
|
|
56
|
+
export async function exampleWorkflow() {
|
|
57
|
+
// Step 1: Analyze
|
|
58
|
+
const analysis = await cursorLLM("Analyze the current test coverage");
|
|
59
|
+
|
|
60
|
+
// Step 2: Generate plan
|
|
61
|
+
const plan = await cursorLLMStructured(
|
|
62
|
+
"Based on this analysis, create a test plan:\n\n" + analysis,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Step 3: Execute (if needed)
|
|
66
|
+
if (plan.success && plan.payload?.actions) {
|
|
67
|
+
console.log("Actions to take:", plan.payload.actions);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { analysis, plan };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Example 5: Error handling
|
|
75
|
+
*/
|
|
76
|
+
export async function exampleWithErrorHandling() {
|
|
77
|
+
try {
|
|
78
|
+
const answer = await cursorLLM("Your task here");
|
|
79
|
+
return answer;
|
|
80
|
+
} catch (error: any) {
|
|
81
|
+
if (error.message?.includes("MCP_UNAVAILABLE")) {
|
|
82
|
+
console.error("MCP server not available. Start it with: npm run bugmole:mcp");
|
|
83
|
+
} else if (error.message?.includes("Timeout")) {
|
|
84
|
+
console.error("Task timed out. Try increasing timeout or simplifying the task.");
|
|
85
|
+
} else {
|
|
86
|
+
console.error("Error:", error.message);
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CURSOR_DRIVER as a simple LLM API replacement
|
|
3
|
+
*
|
|
4
|
+
* This module provides a drop-in replacement for LLM API calls.
|
|
5
|
+
* Just pass a prompt, and it handles everything else using config.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { cursorLLM } from './cursor-llm.js';
|
|
9
|
+
* const response = await cursorLLM('Your prompt here');
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
import dotenv from "dotenv";
|
|
16
|
+
import { preferExisting } from "./legacy-names.js";
|
|
17
|
+
import YAML from "yaml";
|
|
18
|
+
import { runCursorDriver } from "./cursor-driver-run.js";
|
|
19
|
+
import type { CursorDriverResponse, CursorDriverRunOptions } from "./cursor-driver.js";
|
|
20
|
+
|
|
21
|
+
// Cache config to avoid reloading
|
|
22
|
+
let cachedConfig: any = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Loads and caches the Bugmole MCP configuration
|
|
26
|
+
*/
|
|
27
|
+
function loadConfig(configPath: string = preferExisting("bugmole.config.yaml", "valkyrie.config.yaml")): any {
|
|
28
|
+
if (cachedConfig) {
|
|
29
|
+
return cachedConfig;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const scriptPath = fileURLToPath(import.meta.url);
|
|
33
|
+
const scriptDir = path.dirname(scriptPath);
|
|
34
|
+
const projectRoot = path.resolve(scriptDir, "../..");
|
|
35
|
+
|
|
36
|
+
// Change to project root to ensure relative paths work
|
|
37
|
+
const originalCwd = process.cwd();
|
|
38
|
+
process.chdir(projectRoot);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
dotenv.config({ quiet: true });
|
|
42
|
+
|
|
43
|
+
const fullConfigPath = path.join(projectRoot, configPath);
|
|
44
|
+
if (!fs.existsSync(fullConfigPath)) {
|
|
45
|
+
throw new Error(`Config file not found: ${fullConfigPath}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const raw = fs.readFileSync(fullConfigPath, "utf-8");
|
|
49
|
+
const parsed = YAML.parse(raw);
|
|
50
|
+
|
|
51
|
+
// Interpolate environment variables
|
|
52
|
+
cachedConfig = interpolateEnv(parsed);
|
|
53
|
+
return cachedConfig;
|
|
54
|
+
} finally {
|
|
55
|
+
process.chdir(originalCwd);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// tiny ${ENV} interpolator for yaml configs
|
|
60
|
+
function interpolateEnv(obj: any): any {
|
|
61
|
+
if (typeof obj === "string") {
|
|
62
|
+
return obj.replace(/\$\{([A-Z0-9_]+)(?::-([^}]+))?\}/g, (_, k, defaultValue) => {
|
|
63
|
+
const envValue = process.env[k];
|
|
64
|
+
if (envValue !== undefined && envValue !== "") {
|
|
65
|
+
return envValue;
|
|
66
|
+
}
|
|
67
|
+
return defaultValue !== undefined ? defaultValue : "";
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(obj)) return obj.map(interpolateEnv);
|
|
71
|
+
if (obj && typeof obj === "object") {
|
|
72
|
+
const out: any = {};
|
|
73
|
+
for (const [k, v] of Object.entries(obj)) out[k] = interpolateEnv(v);
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
return obj;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Options for cursorLLM (all optional, uses config defaults)
|
|
81
|
+
*/
|
|
82
|
+
export interface CursorLLMOptions {
|
|
83
|
+
/**
|
|
84
|
+
* Custom config file path (default: bugmole.config.yaml)
|
|
85
|
+
*/
|
|
86
|
+
configPath?: string;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Override MCP host (default: from config)
|
|
90
|
+
*/
|
|
91
|
+
mcpHost?: string;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Override MCP port (default: from config)
|
|
95
|
+
*/
|
|
96
|
+
mcpPort?: number;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Override artifacts directory (default: from config)
|
|
100
|
+
*/
|
|
101
|
+
artifactsDir?: string;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Timeout in milliseconds (default: 300000 = 5 minutes)
|
|
105
|
+
*/
|
|
106
|
+
timeout?: number;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Project directory to open in Cursor (default: from config)
|
|
110
|
+
*/
|
|
111
|
+
projectDirectory?: string;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Request ID for tracking
|
|
115
|
+
*/
|
|
116
|
+
requestId?: string;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Custom instructions to append before finalization protocol
|
|
120
|
+
*/
|
|
121
|
+
customInstructions?: string;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Disable nudging (default: enabled)
|
|
125
|
+
*/
|
|
126
|
+
disableNudging?: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Simple LLM-like interface - just pass a prompt
|
|
131
|
+
*
|
|
132
|
+
* @param prompt - The prompt/task to send to Cursor
|
|
133
|
+
* @param options - Optional overrides (uses config defaults)
|
|
134
|
+
* @returns The response text (markdown answer or summary)
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* import { cursorLLM } from './cursor-llm.js';
|
|
139
|
+
*
|
|
140
|
+
* const answer = await cursorLLM('Analyze the error handling in this codebase');
|
|
141
|
+
* console.log(answer);
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export async function cursorLLM(prompt: string, options: CursorLLMOptions = {}): Promise<string> {
|
|
145
|
+
const cfg = loadConfig(options.configPath);
|
|
146
|
+
|
|
147
|
+
const response = await runCursorDriver(prompt, {
|
|
148
|
+
mcpHost: options.mcpHost || cfg.mcp?.host || "127.0.0.1",
|
|
149
|
+
mcpPort: options.mcpPort || cfg.mcp?.port || 3187,
|
|
150
|
+
artifactsDir: options.artifactsDir || cfg.runtime?.artifacts_dir || "./runs",
|
|
151
|
+
timeout: options.timeout || 300000,
|
|
152
|
+
projectDirectory: options.projectDirectory || cfg.cursor_driver?.project_directory,
|
|
153
|
+
requestId: options.requestId,
|
|
154
|
+
customInstructions: options.customInstructions,
|
|
155
|
+
nudgeOptions: options.disableNudging ? { enabled: false } : undefined,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (!response.success || !response.payload) {
|
|
159
|
+
throw new Error(response.error || "Cursor driver failed to complete task");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return response.payload.answer_markdown || response.payload.summary;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* LLM-like interface that returns structured response
|
|
167
|
+
*
|
|
168
|
+
* @param prompt - The prompt/task to send to Cursor
|
|
169
|
+
* @param options - Optional overrides (uses config defaults)
|
|
170
|
+
* @returns Full structured response with actions and artifacts
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```typescript
|
|
174
|
+
* import { cursorLLMStructured } from './cursor-llm.js';
|
|
175
|
+
*
|
|
176
|
+
* const response = await cursorLLMStructured('Review test failures and propose fixes');
|
|
177
|
+
* if (response.success) {
|
|
178
|
+
* console.log('Actions:', response.payload?.actions);
|
|
179
|
+
* console.log('Answer:', response.payload?.answer_markdown);
|
|
180
|
+
* }
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
export async function cursorLLMStructured(
|
|
184
|
+
prompt: string,
|
|
185
|
+
options: CursorLLMOptions = {},
|
|
186
|
+
): Promise<CursorDriverResponse> {
|
|
187
|
+
const cfg = loadConfig(options.configPath);
|
|
188
|
+
|
|
189
|
+
return runCursorDriver(prompt, {
|
|
190
|
+
mcpHost: options.mcpHost || cfg.mcp?.host || "127.0.0.1",
|
|
191
|
+
mcpPort: options.mcpPort || cfg.mcp?.port || 3187,
|
|
192
|
+
artifactsDir: options.artifactsDir || cfg.runtime?.artifacts_dir || "./runs",
|
|
193
|
+
timeout: options.timeout || 300000,
|
|
194
|
+
projectDirectory: options.projectDirectory || cfg.cursor_driver?.project_directory,
|
|
195
|
+
requestId: options.requestId,
|
|
196
|
+
customInstructions: options.customInstructions,
|
|
197
|
+
nudgeOptions: options.disableNudging ? { enabled: false } : undefined,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Reset the config cache (useful for testing or when config changes)
|
|
203
|
+
*/
|
|
204
|
+
export function resetConfigCache(): void {
|
|
205
|
+
cachedConfig = null;
|
|
206
|
+
}
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP commit response monitor
|
|
3
|
+
*
|
|
4
|
+
* Polls the MCP server for commit responses from Cursor.
|
|
5
|
+
* This is how we receive structured output from Cursor tasks.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import http from "node:http";
|
|
11
|
+
|
|
12
|
+
export interface CommitResponse {
|
|
13
|
+
id: string;
|
|
14
|
+
timestamp: string;
|
|
15
|
+
payload: {
|
|
16
|
+
status: "ok" | "error";
|
|
17
|
+
summary: string;
|
|
18
|
+
answer_markdown?: string;
|
|
19
|
+
actions?: Array<{
|
|
20
|
+
type: "edit" | "create" | "delete" | "command";
|
|
21
|
+
target: string;
|
|
22
|
+
details: string;
|
|
23
|
+
}>;
|
|
24
|
+
artifacts?: Array<{
|
|
25
|
+
type: "patch" | "config" | "command" | "note";
|
|
26
|
+
content: string;
|
|
27
|
+
}>;
|
|
28
|
+
next_steps?: string[];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ProgressReport {
|
|
33
|
+
id: string;
|
|
34
|
+
timestamp: string;
|
|
35
|
+
status: "working" | "blocked" | "completed";
|
|
36
|
+
message: string;
|
|
37
|
+
details?: string;
|
|
38
|
+
estimated_time_remaining?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface MonitorOptions {
|
|
42
|
+
/**
|
|
43
|
+
* MCP server host
|
|
44
|
+
* @default '127.0.0.1'
|
|
45
|
+
*/
|
|
46
|
+
host?: string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* MCP server port
|
|
50
|
+
* @default 3187
|
|
51
|
+
*/
|
|
52
|
+
port?: number;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Artifacts directory where commits are stored
|
|
56
|
+
*/
|
|
57
|
+
artifactsDir: string;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Polling interval in ms
|
|
61
|
+
* @default 1000
|
|
62
|
+
*/
|
|
63
|
+
pollInterval?: number;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Maximum wait time in ms
|
|
67
|
+
* @default 300000 (5 minutes)
|
|
68
|
+
*/
|
|
69
|
+
timeout?: number;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Request ID to filter commits (optional)
|
|
73
|
+
*/
|
|
74
|
+
requestId?: string;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Nudge configuration for inactivity detection
|
|
78
|
+
*/
|
|
79
|
+
nudgeOptions?: {
|
|
80
|
+
enabled?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Time without progress report before nudging (default: 300000 = 5 minutes)
|
|
83
|
+
*/
|
|
84
|
+
progressReportTimeout?: number;
|
|
85
|
+
/**
|
|
86
|
+
* Minimum time between nudges (default: 120000 = 2 minutes)
|
|
87
|
+
*/
|
|
88
|
+
nudgeInterval?: number;
|
|
89
|
+
maxNudges?: number;
|
|
90
|
+
customMessage?: string;
|
|
91
|
+
onNudge?: (nudgeCount: number) => Promise<void>;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Reads a commit file from the artifacts directory
|
|
97
|
+
*/
|
|
98
|
+
function readCommitFile(filePath: string): CommitResponse | null {
|
|
99
|
+
try {
|
|
100
|
+
if (!fs.existsSync(filePath)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
105
|
+
return JSON.parse(content) as CommitResponse;
|
|
106
|
+
} catch {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Lists all commit files in the cursor_commits directory
|
|
113
|
+
*/
|
|
114
|
+
function listCommitFiles(artifactsDir: string): string[] {
|
|
115
|
+
const commitDir = path.join(artifactsDir, "cursor_commits");
|
|
116
|
+
|
|
117
|
+
if (!fs.existsSync(commitDir)) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
return fs
|
|
123
|
+
.readdirSync(commitDir)
|
|
124
|
+
.filter((f) => f.endsWith(".json"))
|
|
125
|
+
.map((f) => path.join(commitDir, f))
|
|
126
|
+
.sort((a, b) => {
|
|
127
|
+
// Sort by modification time (newest first)
|
|
128
|
+
const statA = fs.statSync(a);
|
|
129
|
+
const statB = fs.statSync(b);
|
|
130
|
+
return statB.mtimeMs - statA.mtimeMs;
|
|
131
|
+
});
|
|
132
|
+
} catch {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Lists all progress report files in the progress_reports directory
|
|
139
|
+
*/
|
|
140
|
+
function listProgressReportFiles(artifactsDir: string): string[] {
|
|
141
|
+
const progressDir = path.join(artifactsDir, "progress_reports");
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(progressDir)) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
return fs
|
|
149
|
+
.readdirSync(progressDir)
|
|
150
|
+
.filter((f) => f.endsWith(".json"))
|
|
151
|
+
.map((f) => path.join(progressDir, f))
|
|
152
|
+
.sort((a, b) => {
|
|
153
|
+
// Sort by modification time (newest first)
|
|
154
|
+
const statA = fs.statSync(a);
|
|
155
|
+
const statB = fs.statSync(b);
|
|
156
|
+
return statB.mtimeMs - statA.mtimeMs;
|
|
157
|
+
});
|
|
158
|
+
} catch {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Reads a progress report file
|
|
165
|
+
*/
|
|
166
|
+
function readProgressReportFile(filePath: string): ProgressReport | null {
|
|
167
|
+
try {
|
|
168
|
+
if (!fs.existsSync(filePath)) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
173
|
+
return JSON.parse(content) as ProgressReport;
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Gets the most recent progress report after a given timestamp
|
|
181
|
+
*/
|
|
182
|
+
function getLatestProgressReport(
|
|
183
|
+
artifactsDir: string,
|
|
184
|
+
afterTimestamp: number,
|
|
185
|
+
): ProgressReport | null {
|
|
186
|
+
const progressFiles = listProgressReportFiles(artifactsDir);
|
|
187
|
+
|
|
188
|
+
for (const filePath of progressFiles) {
|
|
189
|
+
const report = readProgressReportFile(filePath);
|
|
190
|
+
if (!report) continue;
|
|
191
|
+
|
|
192
|
+
const reportTime = new Date(report.timestamp).getTime();
|
|
193
|
+
if (reportTime >= afterTimestamp) {
|
|
194
|
+
return report;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Checks MCP server health
|
|
203
|
+
*/
|
|
204
|
+
async function checkMcpHealth(host: string, port: number): Promise<boolean> {
|
|
205
|
+
return new Promise((resolve) => {
|
|
206
|
+
const req = http.get(`http://${host}:${port}/health`, (res) => {
|
|
207
|
+
resolve(res.statusCode === 200);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
req.on("error", () => resolve(false));
|
|
211
|
+
req.setTimeout(2000, () => {
|
|
212
|
+
req.destroy();
|
|
213
|
+
resolve(false);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Monitors for a commit response from Cursor
|
|
220
|
+
*
|
|
221
|
+
* Polls the artifacts directory for new commit files.
|
|
222
|
+
* Returns the first commit that appears after the start time.
|
|
223
|
+
* Includes nudge mechanism to detect and handle inactivity.
|
|
224
|
+
*/
|
|
225
|
+
export async function waitForCommitResponse(options: MonitorOptions): Promise<CommitResponse> {
|
|
226
|
+
const {
|
|
227
|
+
host = "127.0.0.1",
|
|
228
|
+
port = 3187,
|
|
229
|
+
artifactsDir,
|
|
230
|
+
pollInterval = 1000,
|
|
231
|
+
timeout = 300000,
|
|
232
|
+
requestId,
|
|
233
|
+
nudgeOptions,
|
|
234
|
+
} = options;
|
|
235
|
+
|
|
236
|
+
const startTime = Date.now();
|
|
237
|
+
const seenCommitIds = new Set<string>();
|
|
238
|
+
|
|
239
|
+
// Nudge tracking - now based on progress reports
|
|
240
|
+
const nudgeEnabled = nudgeOptions?.enabled !== false;
|
|
241
|
+
const progressReportTimeout = nudgeOptions?.progressReportTimeout ?? 300000; // 5 minutes default - only nudge if no progress report for this long
|
|
242
|
+
const nudgeInterval = nudgeOptions?.nudgeInterval ?? 120000; // 2 minutes minimum between nudges
|
|
243
|
+
const maxNudges = nudgeOptions?.maxNudges ?? 10;
|
|
244
|
+
const customMessage = nudgeOptions?.customMessage;
|
|
245
|
+
const onNudge = nudgeOptions?.onNudge;
|
|
246
|
+
|
|
247
|
+
let lastProgressReportTime = startTime;
|
|
248
|
+
let lastNudgeTime = 0;
|
|
249
|
+
let nudgeCount = 0;
|
|
250
|
+
|
|
251
|
+
// First, verify MCP server is running
|
|
252
|
+
const isHealthy = await checkMcpHealth(host, port);
|
|
253
|
+
if (!isHealthy) {
|
|
254
|
+
throw new Error(`MCP server at ${host}:${port} is not responding`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return new Promise((resolve, reject) => {
|
|
258
|
+
const poll = async () => {
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
|
|
261
|
+
// Check timeout
|
|
262
|
+
if (now - startTime > timeout) {
|
|
263
|
+
reject(new Error(`Timeout waiting for commit response after ${timeout}ms`));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// List commit files
|
|
268
|
+
const commitFiles = listCommitFiles(artifactsDir);
|
|
269
|
+
|
|
270
|
+
// Check each commit file
|
|
271
|
+
let foundNewCommit = false;
|
|
272
|
+
for (const filePath of commitFiles) {
|
|
273
|
+
const commit = readCommitFile(filePath);
|
|
274
|
+
if (!commit) continue;
|
|
275
|
+
|
|
276
|
+
// Skip if we've already seen this commit
|
|
277
|
+
if (seenCommitIds.has(commit.id)) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Check if commit is after start time (new commit)
|
|
282
|
+
const commitTime = new Date(commit.timestamp).getTime();
|
|
283
|
+
if (commitTime >= startTime) {
|
|
284
|
+
// If requestId is specified, check if it matches
|
|
285
|
+
// (This would require requestId to be embedded in the commit payload)
|
|
286
|
+
// For now, we just return the first new commit
|
|
287
|
+
|
|
288
|
+
seenCommitIds.add(commit.id);
|
|
289
|
+
foundNewCommit = true;
|
|
290
|
+
resolve(commit);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Check for progress reports (even if no commit yet)
|
|
296
|
+
const latestProgressReport = getLatestProgressReport(artifactsDir, startTime);
|
|
297
|
+
if (latestProgressReport) {
|
|
298
|
+
const reportTime = new Date(latestProgressReport.timestamp).getTime();
|
|
299
|
+
if (reportTime > lastProgressReportTime) {
|
|
300
|
+
lastProgressReportTime = reportTime;
|
|
301
|
+
console.log(
|
|
302
|
+
`[CURSOR_DRIVER] Progress report received: ${latestProgressReport.status} - ${latestProgressReport.message}`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!foundNewCommit) {
|
|
308
|
+
// Add grace period before checking for MCP tool calls
|
|
309
|
+
// Planning models may take 10-30 seconds to think before calling tools
|
|
310
|
+
// Only start checking after initial grace period has passed
|
|
311
|
+
const gracePeriod = 15000; // 15 seconds grace period for planning models
|
|
312
|
+
const timeSinceStart = now - startTime;
|
|
313
|
+
|
|
314
|
+
if (timeSinceStart >= gracePeriod) {
|
|
315
|
+
// Periodically try to approve MCP tool dialogs that might be waiting
|
|
316
|
+
// Cursor sometimes shows confirmation dialogs for MCP tool execution
|
|
317
|
+
// But only after grace period to avoid interrupting model's thinking
|
|
318
|
+
try {
|
|
319
|
+
const { approveMcpTool } = await import("./cursor-applescript.js");
|
|
320
|
+
await approveMcpTool();
|
|
321
|
+
} catch (error: any) {
|
|
322
|
+
// Non-critical - approval might not be needed
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Check if we haven't received a progress report for too long
|
|
327
|
+
// Only nudge if no progress report has been received for the timeout period
|
|
328
|
+
const timeSinceLastProgressReport = now - lastProgressReportTime;
|
|
329
|
+
const timeSinceLastNudge = now - lastNudgeTime;
|
|
330
|
+
|
|
331
|
+
if (
|
|
332
|
+
nudgeEnabled &&
|
|
333
|
+
timeSinceLastProgressReport >= progressReportTimeout &&
|
|
334
|
+
timeSinceLastNudge >= nudgeInterval &&
|
|
335
|
+
nudgeCount < maxNudges
|
|
336
|
+
) {
|
|
337
|
+
// Send nudge - agent hasn't reported progress for a while
|
|
338
|
+
nudgeCount++;
|
|
339
|
+
lastNudgeTime = now;
|
|
340
|
+
|
|
341
|
+
console.log(
|
|
342
|
+
`[CURSOR_DRIVER] Sending nudge #${nudgeCount} (no progress report for ${Math.round(
|
|
343
|
+
timeSinceLastProgressReport / 1000,
|
|
344
|
+
)}s)`,
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
if (onNudge) {
|
|
349
|
+
await onNudge(nudgeCount);
|
|
350
|
+
} else {
|
|
351
|
+
// Default nudge implementation
|
|
352
|
+
const { sendNudge } = await import("./cursor-applescript.js");
|
|
353
|
+
const message =
|
|
354
|
+
customMessage ||
|
|
355
|
+
"Please continue working on the task. If you are still working, please call bugmole_report_progress to let me know. Remember to call bugmole_commit_response when you are fully finished.";
|
|
356
|
+
await sendNudge(message);
|
|
357
|
+
}
|
|
358
|
+
} catch (error: any) {
|
|
359
|
+
console.warn(`[CURSOR_DRIVER] Failed to send nudge: ${error.message}`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// No new commit yet, poll again
|
|
365
|
+
setTimeout(poll, pollInterval);
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// Start polling
|
|
369
|
+
poll();
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Gets the most recent commit response
|
|
375
|
+
*/
|
|
376
|
+
export function getLatestCommit(artifactsDir: string): CommitResponse | null {
|
|
377
|
+
const commitFiles = listCommitFiles(artifactsDir);
|
|
378
|
+
|
|
379
|
+
if (commitFiles.length === 0) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Get the most recent file
|
|
384
|
+
const latestFile = commitFiles[0];
|
|
385
|
+
return readCommitFile(latestFile);
|
|
386
|
+
}
|