pi-cursor-bridge 0.2.5 → 0.2.6
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/.codex-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/cursor-bridge.mjs +134 -46
- package/extensions/index.ts +1 -1
- package/package.json +2 -2
- package/skills/cce-routing/SKILL.md +20 -2
- package/skills/cursor-delegate/SKILL.md +9 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-bridge",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.6",
|
|
4
4
|
"description": "Evidence-backed Cursor Context Engine search and bounded Cursor Agent execution, including a UI-suppressed minimal runtime.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Vanyangyang"
|
package/README.md
CHANGED
|
@@ -12,6 +12,6 @@ pi install npm:pi-cursor-bridge
|
|
|
12
12
|
|
|
13
13
|
Restart Pi after installation. Initialize the current project by asking Pi to initialize Cursor Bridge for the absolute project path, then use it normally. The package includes the `cce-routing` and `cursor-delegate` Skills and registers the native Cursor Bridge MCP tools directly in Pi.
|
|
14
14
|
|
|
15
|
-
Pi wrapper 0.2.
|
|
15
|
+
Pi wrapper 0.2.6 embeds Cursor Bridge 6.0.6, including Cursor 3.21 model-provider alias matching and `reasoning_effort` verification for Grok 4.7/high. Its result contract returns a compact receipt for background `cursor_do` work: poll `cursor_status(task_id)` normally, retrieve the terminal raw reply with `cursor_status(task_id, detail="result")`, check `isError` before using its content, and use `detail="full"` only when diagnostics are needed. Cursor must be installed and signed in. Current end-to-end compatibility claims remain scoped to the environments documented in the main repository.
|
|
16
16
|
|
|
17
17
|
Full documentation: [English](https://github.com/Vanyangyang/cursor-bridge#readme) · [简体中文](https://github.com/Vanyangyang/cursor-bridge/blob/main/README.zh-CN.md)
|
package/dist/cursor-bridge.mjs
CHANGED
|
@@ -12989,8 +12989,8 @@ function withLifecycle(result, mode, extra = {}) {
|
|
|
12989
12989
|
};
|
|
12990
12990
|
}
|
|
12991
12991
|
async function ensureCursorRunning(options = {}) {
|
|
12992
|
-
const bindingFile = options.workspaceFile
|
|
12993
|
-
const bindingKey = options.workspaceKey || resolveWorkspaceBindingKey();
|
|
12992
|
+
const bindingFile = Object.hasOwn(options, "workspaceFile") ? options.workspaceFile : resolveWorkspaceBindingFile();
|
|
12993
|
+
const bindingKey = options.workspaceKey || resolveWorkspaceBindingKey(process.env, { cwd: options.adapterStartCwd ?? process.cwd() });
|
|
12994
12994
|
const persistedBinding = readWorkspaceBinding(bindingFile, bindingKey);
|
|
12995
12995
|
const hostProjectPath = resolveClaudeProjectPath();
|
|
12996
12996
|
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath : resolveProjectPath(persistedBinding && persistedBinding.projectPath || process.env.CURSOR_PROJECT_PATH, {
|
|
@@ -22985,7 +22985,7 @@ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
|
|
|
22985
22985
|
// server.mjs
|
|
22986
22986
|
init_cursor_ensure_core();
|
|
22987
22987
|
init_lifecycle_paths();
|
|
22988
|
-
var PLUGIN_VERSION = "6.0.
|
|
22988
|
+
var PLUGIN_VERSION = "6.0.6";
|
|
22989
22989
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
22990
22990
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
22991
22991
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -24282,12 +24282,12 @@ var CursorBridge = class {
|
|
|
24282
24282
|
this.runtimeModeSource = options.runtimeMode !== void 0 ? "constructor" : persistedRuntimeMode ? "persistent" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
24283
24283
|
this.runtimeModeScope = persistedRuntimeMode ? "persistent" : options.runtimeMode !== void 0 ? "constructor" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
24284
24284
|
this.workspaceFile = options.workspaceFile === null ? null : resolve8(options.workspaceFile || resolveWorkspaceBindingFile());
|
|
24285
|
-
this.workspaceKey = options.workspaceKey || resolveWorkspaceBindingKey();
|
|
24285
|
+
this.workspaceKey = options.workspaceKey || resolveWorkspaceBindingKey(process.env, { cwd: this.adapterStartCwd });
|
|
24286
24286
|
const persistedWorkspace = options.projectPath === void 0 ? readWorkspaceBinding(this.workspaceFile, this.workspaceKey) : null;
|
|
24287
24287
|
this.projectPath = options.projectPath !== void 0 ? resolve8(String(options.projectPath)) : persistedWorkspace && persistedWorkspace.projectPath || null;
|
|
24288
24288
|
this.workspaceSource = options.projectPath !== void 0 ? "constructor" : persistedWorkspace ? "persistent_init" : "auto_detect";
|
|
24289
24289
|
this.workspaceUpdatedAt = persistedWorkspace && persistedWorkspace.updatedAt || null;
|
|
24290
|
-
this.workspaceConfirmationRequired = this.workspaceKey === "default" &&
|
|
24290
|
+
this.workspaceConfirmationRequired = this.workspaceKey === "default" && options.projectPath === void 0;
|
|
24291
24291
|
this.modelPreferencesFile = options.modelPreferencesFile === null ? null : resolve8(options.modelPreferencesFile || resolveCursorModelPreferencesFile());
|
|
24292
24292
|
this.modelPreferences = readCursorModelPreferences(this.modelPreferencesFile);
|
|
24293
24293
|
this.sessionFile = options.sessionFile === null ? null : resolve8(options.sessionFile || resolveCursorSessionRegistryFile());
|
|
@@ -24303,6 +24303,8 @@ var CursorBridge = class {
|
|
|
24303
24303
|
this.activeParallel = /* @__PURE__ */ new Map();
|
|
24304
24304
|
this.currentJob = null;
|
|
24305
24305
|
this._uiTail = Promise.resolve();
|
|
24306
|
+
this._workspaceTail = Promise.resolve();
|
|
24307
|
+
this._workspacePending = 0;
|
|
24306
24308
|
this.parallelRestoreAgentId = null;
|
|
24307
24309
|
this.parallelRestoreTargetId = null;
|
|
24308
24310
|
}
|
|
@@ -24310,6 +24312,8 @@ var CursorBridge = class {
|
|
|
24310
24312
|
const resolvedProjectPath = this.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || null;
|
|
24311
24313
|
return {
|
|
24312
24314
|
workspaceKey: this.workspaceKey,
|
|
24315
|
+
workspaceIdentitySource: this.workspaceKey === "default" ? "unavailable" : this.workspaceKey.split(":")[0],
|
|
24316
|
+
workspaceBusy: this._workspacePending > 0,
|
|
24313
24317
|
workspaceConfirmationRequired: this.workspaceConfirmationRequired,
|
|
24314
24318
|
workspaceBindingWarning: this.workspaceConfirmationRequired ? "This adapter has no host workspace identity. Run cursor_init with the intended project before using its shared saved binding." : null,
|
|
24315
24319
|
projectPath: resolvedProjectPath,
|
|
@@ -24692,8 +24696,12 @@ var CursorBridge = class {
|
|
|
24692
24696
|
if (!["reconcile", "collect_result", "close", "forget", "abandon"].includes(action)) {
|
|
24693
24697
|
throw cursorSessionError("SESSION_ACTION_UNSUPPORTED", "expected reconcile, collect_result, close, forget, or abandon");
|
|
24694
24698
|
}
|
|
24695
|
-
if (action === "reconcile"
|
|
24696
|
-
|
|
24699
|
+
if (action === "reconcile" || action === "collect_result") {
|
|
24700
|
+
return this._withWorkspaceAdmission(() => {
|
|
24701
|
+
this._assertWorkspaceConfirmed();
|
|
24702
|
+
return this._reconcileSession(id, { collectResult: action === "collect_result" });
|
|
24703
|
+
});
|
|
24704
|
+
}
|
|
24697
24705
|
let result;
|
|
24698
24706
|
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
24699
24707
|
const session = registry2.sessions[id];
|
|
@@ -24746,6 +24754,10 @@ var CursorBridge = class {
|
|
|
24746
24754
|
return result;
|
|
24747
24755
|
}
|
|
24748
24756
|
async initializeWorkspace(projectPath) {
|
|
24757
|
+
const result = await this._withWorkspaceAdmission(() => this._initializeWorkspace(projectPath));
|
|
24758
|
+
return { ...result, workspaceBusy: this._workspacePending > 0 };
|
|
24759
|
+
}
|
|
24760
|
+
async _initializeWorkspace(projectPath) {
|
|
24749
24761
|
if (this._healing) await this._healing.catch(() => {
|
|
24750
24762
|
});
|
|
24751
24763
|
if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
|
|
@@ -24971,19 +24983,23 @@ var CursorBridge = class {
|
|
|
24971
24983
|
if (!text) throw new Error("query must not be empty");
|
|
24972
24984
|
if (text.length > 2e4) throw new Error("query exceeds the 20,000-character limit");
|
|
24973
24985
|
const requestContext = normalizeRequestContext(options.requestContext);
|
|
24974
|
-
this.
|
|
24975
|
-
|
|
24976
|
-
|
|
24977
|
-
|
|
24978
|
-
|
|
24979
|
-
|
|
24980
|
-
|
|
24981
|
-
|
|
24982
|
-
|
|
24983
|
-
|
|
24984
|
-
|
|
24985
|
-
|
|
24986
|
-
|
|
24986
|
+
const job = await this._withWorkspaceAdmission(async () => {
|
|
24987
|
+
this._assertWorkspaceConfirmed(options.workspacePath);
|
|
24988
|
+
if (this._hasGlobalReservation()) {
|
|
24989
|
+
throw new Error("A global Cursor reservation has an unconfirmed Stop state; resolve blockingTaskIds from cursor_status first");
|
|
24990
|
+
}
|
|
24991
|
+
await this._ensureCursor();
|
|
24992
|
+
this._assertWorkspaceConfirmed(options.workspacePath);
|
|
24993
|
+
return this._enqueue("context_engine", buildContextEnginePrompt(text, requestContext), {
|
|
24994
|
+
requestContext,
|
|
24995
|
+
timeoutMs: QUERY_TIMEOUT,
|
|
24996
|
+
newChat: true,
|
|
24997
|
+
execution: "fifo",
|
|
24998
|
+
readOnly: true,
|
|
24999
|
+
allowedPaths: [],
|
|
25000
|
+
modelPreference: this._modelPreferenceFor("cce"),
|
|
25001
|
+
projectPath: this._lastLifecycle?.projectPath || this.projectPath || null
|
|
25002
|
+
});
|
|
24987
25003
|
});
|
|
24988
25004
|
const result = normalizeCceSearchResult(await job.promise);
|
|
24989
25005
|
this._markTaskResultCollected(job);
|
|
@@ -24997,6 +25013,14 @@ var CursorBridge = class {
|
|
|
24997
25013
|
return this.contextEngine(query);
|
|
24998
25014
|
}
|
|
24999
25015
|
async doTask(prompt, options = {}) {
|
|
25016
|
+
const admitted = await this._withWorkspaceAdmission(() => this._prepareTask(prompt, options));
|
|
25017
|
+
if (admitted.duplicate) return admitted.response;
|
|
25018
|
+
const job = admitted.job;
|
|
25019
|
+
if (options.background !== false) return this._compactTaskView(job);
|
|
25020
|
+
await job.promise;
|
|
25021
|
+
return this._collectedTaskView(job);
|
|
25022
|
+
}
|
|
25023
|
+
async _prepareTask(prompt, options = {}) {
|
|
25000
25024
|
if (!this.delegationEnabled) {
|
|
25001
25025
|
throw new Error("cursor_do is disabled by CURSOR_BRIDGE_DELEGATION=off; restart the MCP server without that setting to enable delegation");
|
|
25002
25026
|
}
|
|
@@ -25004,7 +25028,7 @@ var CursorBridge = class {
|
|
|
25004
25028
|
if (!text) throw new Error("prompt must not be empty");
|
|
25005
25029
|
if (text.length > 1e5) throw new Error("prompt exceeds the 100,000-character limit");
|
|
25006
25030
|
const requestContext = normalizeRequestContext(options.requestContext);
|
|
25007
|
-
this._assertWorkspaceConfirmed();
|
|
25031
|
+
this._assertWorkspaceConfirmed(options.workspacePath);
|
|
25008
25032
|
if (this._hasGlobalReservation()) {
|
|
25009
25033
|
throw new Error("A global Cursor reservation has an unconfirmed Stop state; no new task may be submitted until it is explicitly recovered or released");
|
|
25010
25034
|
}
|
|
@@ -25049,6 +25073,7 @@ var CursorBridge = class {
|
|
|
25049
25073
|
throw cursorSessionError("SESSION_SCOPE_REQUIRED", "repeat read_only=true or an allowed_paths subset for every continued turn");
|
|
25050
25074
|
}
|
|
25051
25075
|
await this._ensureCursor();
|
|
25076
|
+
this._assertWorkspaceConfirmed(options.workspacePath);
|
|
25052
25077
|
const projectPath = this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null;
|
|
25053
25078
|
if (sessionMode !== "isolated" && !projectPath) {
|
|
25054
25079
|
throw cursorSessionError("SESSION_WORKSPACE_REQUIRED", "initialize one workspace before creating or continuing a session");
|
|
@@ -25089,7 +25114,8 @@ var CursorBridge = class {
|
|
|
25089
25114
|
modelPreference = session.modelPreference || null;
|
|
25090
25115
|
if (duplicate) {
|
|
25091
25116
|
const existing = this.tasks.get(session.activeTaskId || session.lastTask && session.lastTask.taskId || "");
|
|
25092
|
-
|
|
25117
|
+
const response = existing ? { duplicate: true, ...options.background === false ? this._collectedTaskView(existing) : this._compactTaskView(existing) } : { duplicate: true, ...this._sessionView(session) };
|
|
25118
|
+
return { duplicate: true, response };
|
|
25093
25119
|
}
|
|
25094
25120
|
}
|
|
25095
25121
|
const job = this._enqueue("do", fullPrompt, {
|
|
@@ -25113,9 +25139,7 @@ var CursorBridge = class {
|
|
|
25113
25139
|
agentId: sessionMode === "continue" ? session && session.agentId : null,
|
|
25114
25140
|
agentLabel: sessionMode === "continue" ? session && session.agentLabel : null
|
|
25115
25141
|
});
|
|
25116
|
-
|
|
25117
|
-
await job.promise;
|
|
25118
|
-
return this._collectedTaskView(job);
|
|
25142
|
+
return { job };
|
|
25119
25143
|
}
|
|
25120
25144
|
_assertNoParallelPathConflict(allowedPaths) {
|
|
25121
25145
|
if (this._hasGlobalReservation()) {
|
|
@@ -25305,6 +25329,17 @@ var CursorBridge = class {
|
|
|
25305
25329
|
});
|
|
25306
25330
|
return run;
|
|
25307
25331
|
}
|
|
25332
|
+
// Serialize target changes with preparation through enqueue, never with a running Agent.
|
|
25333
|
+
// The UI lock cannot serve this purpose: initialization may acquire it to register a project.
|
|
25334
|
+
_withWorkspaceAdmission(fn) {
|
|
25335
|
+
this._workspacePending++;
|
|
25336
|
+
const run = this._workspaceTail.then(fn, fn).finally(() => {
|
|
25337
|
+
this._workspacePending--;
|
|
25338
|
+
});
|
|
25339
|
+
this._workspaceTail = run.catch(() => {
|
|
25340
|
+
});
|
|
25341
|
+
return run;
|
|
25342
|
+
}
|
|
25308
25343
|
_withJobLock(job, fn) {
|
|
25309
25344
|
const previous = job.controlTail || Promise.resolve();
|
|
25310
25345
|
const run = previous.then(fn, fn);
|
|
@@ -25336,6 +25371,8 @@ var CursorBridge = class {
|
|
|
25336
25371
|
reason: "adapter-heal",
|
|
25337
25372
|
runtimeMode: this.runtimeMode,
|
|
25338
25373
|
adapterStartCwd: this.adapterStartCwd,
|
|
25374
|
+
workspaceKey: this.workspaceKey,
|
|
25375
|
+
workspaceFile: this.workspaceFile,
|
|
25339
25376
|
...this.projectPath ? { projectPath: this.projectPath } : {}
|
|
25340
25377
|
});
|
|
25341
25378
|
this._lastLifecycle = lifecycleFromEnsureResult(rr, this.runtimeMode);
|
|
@@ -27537,6 +27574,8 @@ var CursorBridge = class {
|
|
|
27537
27574
|
pluginVersion: PLUGIN_VERSION,
|
|
27538
27575
|
statusPath: "json-list",
|
|
27539
27576
|
workspaceKey: workspace.workspaceKey,
|
|
27577
|
+
workspaceIdentitySource: workspace.workspaceIdentitySource,
|
|
27578
|
+
workspaceBusy: workspace.workspaceBusy,
|
|
27540
27579
|
workspaceConfirmationRequired: workspace.workspaceConfirmationRequired,
|
|
27541
27580
|
workspaceBindingWarning: workspace.workspaceBindingWarning,
|
|
27542
27581
|
projectPath: workspace.projectPath,
|
|
@@ -27568,9 +27607,45 @@ var CursorBridge = class {
|
|
|
27568
27607
|
cursorPid: lifecycle.cursorPid
|
|
27569
27608
|
};
|
|
27570
27609
|
}
|
|
27571
|
-
|
|
27610
|
+
_workspaceRecoveryError(code, message, expectedProjectPath = null) {
|
|
27611
|
+
const error2 = new Error(`${code}: ${message}`);
|
|
27612
|
+
error2.code = code;
|
|
27613
|
+
error2.workspaceRecovery = {
|
|
27614
|
+
workspaceKey: this.workspaceKey,
|
|
27615
|
+
currentProjectPath: this._lastLifecycle?.projectPath || this.projectPath || null,
|
|
27616
|
+
expectedProjectPath,
|
|
27617
|
+
tool: "cursor_init",
|
|
27618
|
+
arguments: expectedProjectPath ? { path: expectedProjectPath } : null,
|
|
27619
|
+
requiresIdle: true,
|
|
27620
|
+
retrySafe: true,
|
|
27621
|
+
submissionStarted: false,
|
|
27622
|
+
nextStep: "Read cursor_status. When idle with no blocking tasks, run cursor_init with the caller's intended absolute project path, verify ready and the exact path, then retry this unsent request once. Never infer the target from a saved default binding or repository name."
|
|
27623
|
+
};
|
|
27624
|
+
return error2;
|
|
27625
|
+
}
|
|
27626
|
+
_assertWorkspaceConfirmed(workspacePath) {
|
|
27627
|
+
let expected = null;
|
|
27628
|
+
if (workspacePath !== void 0) {
|
|
27629
|
+
if (typeof workspacePath !== "string" || !isAbsoluteWorkspacePath(workspacePath)) {
|
|
27630
|
+
throw cursorSessionError("WORKSPACE_PATH_INVALID", "workspace_path must be an absolute project directory or .code-workspace path");
|
|
27631
|
+
}
|
|
27632
|
+
expected = normalizeWorkspacePath(workspacePath);
|
|
27633
|
+
}
|
|
27572
27634
|
if (this.workspaceConfirmationRequired) {
|
|
27573
|
-
throw
|
|
27635
|
+
throw this._workspaceRecoveryError("WORKSPACE_CONFIRMATION_REQUIRED", "this adapter has no host workspace identity. Run cursor_init with the intended project before submitting work; a saved default path is not confirmation.", expected);
|
|
27636
|
+
}
|
|
27637
|
+
if (expected) {
|
|
27638
|
+
const current = this._lastLifecycle?.projectPath || this.projectPath;
|
|
27639
|
+
if (!current) {
|
|
27640
|
+
throw this._workspaceRecoveryError("WORKSPACE_INITIALIZATION_REQUIRED", "Run cursor_init with workspace_path before submitting work.", expected);
|
|
27641
|
+
}
|
|
27642
|
+
const canonical = (path) => {
|
|
27643
|
+
const normalized = normalizeWorkspacePath(path);
|
|
27644
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
27645
|
+
};
|
|
27646
|
+
if (canonical(current) !== canonical(expected)) {
|
|
27647
|
+
throw this._workspaceRecoveryError("WORKSPACE_MISMATCH", "workspace_path does not match the current binding. Run cursor_init with the intended project when idle; no work was submitted.", expected);
|
|
27648
|
+
}
|
|
27574
27649
|
}
|
|
27575
27650
|
}
|
|
27576
27651
|
_ensureTaskCapacity() {
|
|
@@ -27618,10 +27693,10 @@ var CursorBridge = class {
|
|
|
27618
27693
|
...this.runtimeModeView(),
|
|
27619
27694
|
...this.modelPreferencesView(),
|
|
27620
27695
|
...this.sessionRegistryView(),
|
|
27621
|
-
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
|
|
27696
|
+
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0 || this._workspacePending > 0,
|
|
27622
27697
|
uiBusy,
|
|
27623
27698
|
parallelRunning,
|
|
27624
|
-
idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0,
|
|
27699
|
+
idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0 && this._workspacePending === 0,
|
|
27625
27700
|
queued: this.queue.length,
|
|
27626
27701
|
blockingTaskIds: [...this.activeParallel.values()].filter((job) => !isTerminalTask(job)).map((job) => job.id),
|
|
27627
27702
|
globallyBlocked: globalBlocked,
|
|
@@ -27650,10 +27725,10 @@ var CursorBridge = class {
|
|
|
27650
27725
|
}
|
|
27651
27726
|
} : {
|
|
27652
27727
|
...this._compactStatusCommon(),
|
|
27653
|
-
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
|
|
27728
|
+
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0 || this._workspacePending > 0,
|
|
27654
27729
|
uiBusy,
|
|
27655
27730
|
parallelRunning,
|
|
27656
|
-
idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0,
|
|
27731
|
+
idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0 && this._workspacePending === 0,
|
|
27657
27732
|
queued: this.queue.length,
|
|
27658
27733
|
blockingTaskIds: [...this.activeParallel.values()].filter((job) => !isTerminalTask(job)).map((job) => job.id),
|
|
27659
27734
|
globallyBlocked: globalBlocked,
|
|
@@ -27678,6 +27753,7 @@ function buildSearchInputSchema() {
|
|
|
27678
27753
|
type: "object",
|
|
27679
27754
|
properties: {
|
|
27680
27755
|
query: { type: "string", description: "Describe the behavior, concept, symbol relationship, or ownership boundary to locate. State intent instead of guessing a directory." },
|
|
27756
|
+
workspace_path: WORKSPACE_PATH_SCHEMA,
|
|
27681
27757
|
request_context: REQUEST_CONTEXT_SCHEMA
|
|
27682
27758
|
},
|
|
27683
27759
|
required: ["query"]
|
|
@@ -27699,6 +27775,10 @@ var REQUEST_CONTEXT_SCHEMA = {
|
|
|
27699
27775
|
source: { type: "string", enum: ["user", "model", "mixed", "unknown"], description: "user for explicitly supplied user requirements, model for model-authored tasks/inferences, mixed when the prompt clearly separates both; unknown when not established." }
|
|
27700
27776
|
}
|
|
27701
27777
|
};
|
|
27778
|
+
var WORKSPACE_PATH_SCHEMA = {
|
|
27779
|
+
type: "string",
|
|
27780
|
+
description: "The caller's intended absolute project directory or .code-workspace path. Supply it when known to reject stale or cross-task bindings before sending. This is a target assertion, not an automatic workspace switch. On a workspace confirmation, initialization, or mismatch error, check idle status, run cursor_init with this path, verify ready, and retry once."
|
|
27781
|
+
};
|
|
27702
27782
|
function buildToolDefinitions(bridgeInstance) {
|
|
27703
27783
|
return [
|
|
27704
27784
|
{
|
|
@@ -27724,6 +27804,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
27724
27804
|
type: "object",
|
|
27725
27805
|
properties: {
|
|
27726
27806
|
prompt: { type: "string", description: "The task Cursor should receive. State the goal, boundaries, and what a complete result looks like." },
|
|
27807
|
+
workspace_path: WORKSPACE_PATH_SCHEMA,
|
|
27727
27808
|
request_context: REQUEST_CONTEXT_SCHEMA,
|
|
27728
27809
|
background: { type: "boolean", default: true, description: "When true, return the task ID immediately. When false, wait for the task to finish or need attention." },
|
|
27729
27810
|
execution: { type: "string", enum: ["fifo", "parallel_agent"], default: "fifo", description: "fifo is the first-in, first-out serial queue and runs one task at a time in a clean chat. parallel_agent creates a separate top-level Cursor Agent." },
|
|
@@ -27816,29 +27897,35 @@ var server = new Server(
|
|
|
27816
27897
|
{ capabilities: { tools: { listChanged: true } } }
|
|
27817
27898
|
);
|
|
27818
27899
|
async function ensureBridgeCursor(targetBridge, reason) {
|
|
27819
|
-
targetBridge.
|
|
27820
|
-
|
|
27821
|
-
|
|
27822
|
-
|
|
27823
|
-
|
|
27824
|
-
|
|
27825
|
-
|
|
27900
|
+
return targetBridge._withWorkspaceAdmission(async () => {
|
|
27901
|
+
targetBridge._refreshPersistedRuntimeMode();
|
|
27902
|
+
const { ensureCursorRunning: ensureCursorRunning2 } = await Promise.resolve().then(() => (init_launch_cursor(), launch_cursor_exports));
|
|
27903
|
+
const r = await ensureCursorRunning2({
|
|
27904
|
+
reason,
|
|
27905
|
+
runtimeMode: targetBridge.runtimeMode,
|
|
27906
|
+
adapterStartCwd: targetBridge.adapterStartCwd,
|
|
27907
|
+
workspaceKey: targetBridge.workspaceKey,
|
|
27908
|
+
workspaceFile: targetBridge.workspaceFile,
|
|
27909
|
+
// Prewarming Cursor is not permission to open another task's saved default project.
|
|
27910
|
+
...targetBridge.workspaceConfirmationRequired ? { projectPath: null } : targetBridge.projectPath ? { projectPath: targetBridge.projectPath } : {}
|
|
27911
|
+
});
|
|
27912
|
+
targetBridge._lastLifecycle = lifecycleFromEnsureResult(r, targetBridge.runtimeMode);
|
|
27913
|
+
if (targetBridge.runtimeMode === "minimal") {
|
|
27914
|
+
targetBridge._lastPresentation = r.presentation ? { ...r.presentation, at: (/* @__PURE__ */ new Date()).toISOString() } : await targetBridge.applyRuntimePresentation("hide");
|
|
27915
|
+
}
|
|
27916
|
+
return r;
|
|
27826
27917
|
});
|
|
27827
|
-
targetBridge._lastLifecycle = lifecycleFromEnsureResult(r, targetBridge.runtimeMode);
|
|
27828
|
-
if (targetBridge.runtimeMode === "minimal") {
|
|
27829
|
-
targetBridge._lastPresentation = r.presentation ? { ...r.presentation, at: (/* @__PURE__ */ new Date()).toISOString() } : await targetBridge.applyRuntimePresentation("hide");
|
|
27830
|
-
}
|
|
27831
|
-
return r;
|
|
27832
27918
|
}
|
|
27833
27919
|
function toolErrorResult(error2) {
|
|
27834
27920
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
27835
|
-
if (error2 && (error2.uiDiagnostic || error2.modelSelection || error2.pickerRead || error2.cdp)) {
|
|
27921
|
+
if (error2 && (error2.uiDiagnostic || error2.modelSelection || error2.pickerRead || error2.cdp || error2.workspaceRecovery)) {
|
|
27836
27922
|
const payload = {
|
|
27837
27923
|
error: { code: error2.code || error2.uiDiagnostic?.code || "CURSOR_BRIDGE_ERROR", message },
|
|
27838
27924
|
...error2.uiDiagnostic ? { uiDiagnostic: error2.uiDiagnostic } : {},
|
|
27839
27925
|
...error2.modelSelection ? { modelSelection: error2.modelSelection } : {},
|
|
27840
27926
|
...error2.pickerRead ? { pickerRead: error2.pickerRead } : {},
|
|
27841
|
-
...error2.cdp ? { cdp: error2.cdp } : {}
|
|
27927
|
+
...error2.cdp ? { cdp: error2.cdp } : {},
|
|
27928
|
+
...error2.workspaceRecovery ? { workspaceRecovery: error2.workspaceRecovery } : {}
|
|
27842
27929
|
};
|
|
27843
27930
|
return {
|
|
27844
27931
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
@@ -27859,11 +27946,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
27859
27946
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
27860
27947
|
}
|
|
27861
27948
|
if (name === "cursor_context_engine" || name === "cursor_search" || name === "cursor_search_deep") {
|
|
27862
|
-
const result = await bridge.contextEngine(String(args && args.query || ""), { requestContext: args && args.request_context });
|
|
27949
|
+
const result = await bridge.contextEngine(String(args && args.query || ""), { requestContext: args && args.request_context, workspacePath: args && args.workspace_path });
|
|
27863
27950
|
return { content: [{ type: "text", text: String(result) }] };
|
|
27864
27951
|
}
|
|
27865
27952
|
if (name === "cursor_do") {
|
|
27866
27953
|
const result = await bridge.doTask(String(args && args.prompt || ""), {
|
|
27954
|
+
workspacePath: args && args.workspace_path,
|
|
27867
27955
|
requestContext: args && args.request_context,
|
|
27868
27956
|
background: !args || args.background !== false,
|
|
27869
27957
|
execution: args && args.execution,
|
package/extensions/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ const hostWorkspaceId = hostCwd.replace(/\\/g, "/").toLowerCase();
|
|
|
10
10
|
export default createStdioMcpExtension({
|
|
11
11
|
label: "Cursor Bridge",
|
|
12
12
|
clientName: "pi-cursor-bridge",
|
|
13
|
-
packageVersion: "0.2.
|
|
13
|
+
packageVersion: "0.2.6",
|
|
14
14
|
serverName: "cursor-bridge",
|
|
15
15
|
serverScript: join(packageRoot, "dist", "cursor-bridge.mjs"),
|
|
16
16
|
cwd: hostCwd,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cursor-bridge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Windows only. Use Cursor Context Engine and bounded, explicitly continuous Cursor Agent execution from the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,6 @@
|
|
|
47
47
|
},
|
|
48
48
|
"piPackage": {
|
|
49
49
|
"embeddedProduct": "Cursor Bridge",
|
|
50
|
-
"embeddedProductVersion": "6.0.
|
|
50
|
+
"embeddedProductVersion": "6.0.6"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
@@ -20,7 +20,7 @@ Call `cursor_context_engine` when one or more of these are true:
|
|
|
20
20
|
|
|
21
21
|
Prefer one CCE investigation over starting an Explore subagent merely to locate or understand project code. Let Cursor choose focused or extended depth from what it discovers.
|
|
22
22
|
|
|
23
|
-
When these semantic conditions match, make CCE the first project-discovery surface. Do not establish the answer through generic context-mode, grep, or blind local exploration before trying CCE. If Claude Code denies an initial context-mode collection call with a CCE routing message, call `cursor_context_engine` once instead of retrying another `ctx_*` tool. A failed, denied, unavailable, or `NOT_FOUND` CCE attempt releases this priority and allows a bounded local fallback.
|
|
23
|
+
When these semantic conditions match, make CCE the first project-discovery surface. Do not establish the answer through generic context-mode, grep, or blind local exploration before trying CCE. If Claude Code denies an initial context-mode collection call with a CCE routing message, call `cursor_context_engine` once instead of retrying another `ctx_*` tool. A failed, denied, unavailable, or `NOT_FOUND` CCE attempt releases this priority and allows a bounded local fallback. A pre-send workspace-confirmation error may be recoverable; handle it below before falling back.
|
|
24
24
|
|
|
25
25
|
## Keep deterministic work local
|
|
26
26
|
|
|
@@ -30,10 +30,28 @@ Do not call CCE when any of these apply:
|
|
|
30
30
|
- The needed code is already present in the current context.
|
|
31
31
|
- The work is a trivial single-file edit or only requires running tests, inspecting logs, checking a build, or examining Git state.
|
|
32
32
|
- The question concerns external libraries, APIs, current documentation, or the public web.
|
|
33
|
-
- The user opted out of Cursor
|
|
33
|
+
- The user opted out of Cursor or CCE is unavailable.
|
|
34
34
|
|
|
35
35
|
Do not submit the same lookup to CCE and another semantic system in parallel. Use a second evidence surface only to close a specific gap or verify a consequential claim.
|
|
36
36
|
|
|
37
|
+
## Bind a known workspace before sending
|
|
38
|
+
|
|
39
|
+
Pass the intended absolute `workspace_path` to `cursor_context_engine` when the installed schema supports it. Use the host-provided workspace root/current-task cwd unless the request explicitly targets another project. The field asserts the target before sending; it never switches or registers a project. Do not infer the target from a repository basename or a saved default binding.
|
|
40
|
+
|
|
41
|
+
For an older schema without `workspace_path`, use `cursor_status` to verify the exact ready project path before sending. `initialized=true` only shows that a binding exists; it does not prove the current request is confirmed for the intended workspace. `cursor_init` is the only explicit operation that may initialize, register, or switch a workspace.
|
|
42
|
+
|
|
43
|
+
If no target is known, do not infer or add `workspace_path`, and never call `cursor_init` for a guessed path. Existing safe host bindings may still be used; a workspace-confirmation error without a known target cannot use this recovery and should be reported before any necessary local fallback.
|
|
44
|
+
|
|
45
|
+
## Recover one known workspace
|
|
46
|
+
|
|
47
|
+
For a pre-send `WORKSPACE_CONFIRMATION_REQUIRED`, `WORKSPACE_INITIALIZATION_REQUIRED`, or `WORKSPACE_MISMATCH` error with a known target:
|
|
48
|
+
|
|
49
|
+
1. Call `cursor_status`. Continue only when it confirms idle, no queued or blocking work, and `workspaceBusy=false` when that field is exposed.
|
|
50
|
+
2. Call `cursor_init` with that exact absolute path. Require `ready=true`, `workspaceConfirmationRequired=false`, and the exact `projectPath`; in Agents Window also check `workspaceBinding.ok`, the local file-URI identity and workspace ID.
|
|
51
|
+
3. Retry the original CCE query once.
|
|
52
|
+
|
|
53
|
+
Do not treat these errors as CCE unavailability before this recovery. Do not initialize when status is busy, identity is ambiguous, `needs_attention` is present, or send state is uncertain; explain the condition and use only the necessary local fallback. Do not cancel other work, change the model, or retry in a loop.
|
|
54
|
+
|
|
37
55
|
## Submit one natural-language intent
|
|
38
56
|
|
|
39
57
|
Call `cursor_context_engine` once with the question's real intent. Include a known symbol, subsystem, or path only when it is a useful lead.
|
|
@@ -9,6 +9,14 @@ Use Cursor as an execution partner. Keep direction, scope decisions, risk owners
|
|
|
9
9
|
|
|
10
10
|
Declare `request_context` for each call: an AI caller uses `sender="model"`; set `source="user"` for explicitly supplied user requirements, `"model"` for your own task/inference, or `"mixed"` when both appear. In mixed prompts, label user-confirmed requirements separately from your additions. Use `"unknown"` where provenance is unavailable; a user asking you to use Cursor does not make your authored message user-authored. These labels are declarations, not authentication or extra permission, and do not carry over to later session turns.
|
|
11
11
|
|
|
12
|
+
## Bind a known workspace
|
|
13
|
+
|
|
14
|
+
For CCE or `cursor_do`, pass the intended absolute `workspace_path` when the installed tool schema supports it. Use the host-provided workspace root/current-task cwd unless the request explicitly targets another project. The field is a pre-send assertion: it never selects, switches, or registers a workspace. Do not infer the target from a repository basename or a saved default binding.
|
|
15
|
+
|
|
16
|
+
With an older schema, use `cursor_status` to verify the exact ready project path before sending. `initialized=true` only means a binding exists; it does not confirm the workspace for this request. `cursor_init` remains the only explicit initialization, registration, or switch operation.
|
|
17
|
+
|
|
18
|
+
On pre-send `WORKSPACE_CONFIRMATION_REQUIRED`, `WORKSPACE_INITIALIZATION_REQUIRED`, or `WORKSPACE_MISMATCH` with a known target, first call `cursor_status`. Only if it confirms idle, no queued or blocking work, and `workspaceBusy=false` when exposed, call `cursor_init` for the exact path. Require `ready=true`, `workspaceConfirmationRequired=false`, and the exact `projectPath`; in Agents Window also check `workspaceBinding.ok`, the local file-URI identity and workspace ID. Retry the original CCE query or `cursor_do` submission once. Do not run this recovery for `needs_attention`, uncertain send state, busy status, or ambiguous identity; report the condition and use the necessary local fallback. Do not cancel other work, change model settings, or retry in a loop.
|
|
19
|
+
|
|
12
20
|
## Respect execution controls
|
|
13
21
|
|
|
14
22
|
- Do not call `cursor_do` when the user explicitly says not to use Cursor or not to delegate. A direct user opt-out always wins.
|
|
@@ -75,7 +83,7 @@ Do not choose parallel execution merely because there are many tasks. When depen
|
|
|
75
83
|
|
|
76
84
|
1. Record the relevant pre-dispatch workspace state so later review can distinguish existing user changes.
|
|
77
85
|
2. Form one independent task envelope per task using [delegation-contract.md](references/delegation-contract.md). Write its narrative instructions in the language of the user's current substantive task unless the user explicitly requests another language. Do not persist an inferred language or replace a clear conversational signal with the host/OS locale.
|
|
78
|
-
3. Call `cursor_do` with `background=true` and save its compact submission receipt. Use `background=false` only when an immediate synchronous full result is required. Use only the documented `session_mode` and `session_id` fields when continuity is explicit; never infer continuity from the visible chat.
|
|
86
|
+
3. Call `cursor_do` with `background=true` and save its compact submission receipt. Include `workspace_path` when the exact target is known and the schema supports it. Use `background=false` only when an immediate synchronous full result is required. Use only the documented `session_mode` and `session_id` fields when continuity is explicit; never infer continuity from the visible chat.
|
|
79
87
|
4. Save each returned `task_id`; for persistent work also save `session_id`. Treat `agent_id` as verification evidence, not the continuation handle.
|
|
80
88
|
5. If a parallel submission does not return a usable `agent_id`, stop expanding the parallel batch and use `fifo` or report the ambiguous state.
|
|
81
89
|
|