pi-cursor-bridge 0.2.5 → 0.2.7
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 +362 -86
- 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.7",
|
|
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.7 embeds Cursor Bridge 6.0.7, 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
|
@@ -12907,6 +12907,11 @@ async function ensureCursorViaSupervisor(options = {}) {
|
|
|
12907
12907
|
ok: !!response.ok,
|
|
12908
12908
|
status: response.status,
|
|
12909
12909
|
message: response.message,
|
|
12910
|
+
needsAction: response.needsAction || null,
|
|
12911
|
+
nextStep: response.nextStep || null,
|
|
12912
|
+
retryable: response.retryable === true,
|
|
12913
|
+
cursorExecutable: response.cursorExecutable || null,
|
|
12914
|
+
cursorExecutableSource: response.cursorExecutableSource || null,
|
|
12910
12915
|
port: response.port,
|
|
12911
12916
|
exe: response.exe,
|
|
12912
12917
|
cursorPid: response.cursorPid || null,
|
|
@@ -12989,8 +12994,8 @@ function withLifecycle(result, mode, extra = {}) {
|
|
|
12989
12994
|
};
|
|
12990
12995
|
}
|
|
12991
12996
|
async function ensureCursorRunning(options = {}) {
|
|
12992
|
-
const bindingFile = options.workspaceFile
|
|
12993
|
-
const bindingKey = options.workspaceKey || resolveWorkspaceBindingKey();
|
|
12997
|
+
const bindingFile = Object.hasOwn(options, "workspaceFile") ? options.workspaceFile : resolveWorkspaceBindingFile();
|
|
12998
|
+
const bindingKey = options.workspaceKey || resolveWorkspaceBindingKey(process.env, { cwd: options.adapterStartCwd ?? process.cwd() });
|
|
12994
12999
|
const persistedBinding = readWorkspaceBinding(bindingFile, bindingKey);
|
|
12995
13000
|
const hostProjectPath = resolveClaudeProjectPath();
|
|
12996
13001
|
const projectPath = Object.hasOwn(options, "projectPath") ? options.projectPath : resolveProjectPath(persistedBinding && persistedBinding.projectPath || process.env.CURSOR_PROJECT_PATH, {
|
|
@@ -22985,7 +22990,7 @@ function updateCursorSessionRegistry(filePath, mutator, options = {}) {
|
|
|
22985
22990
|
// server.mjs
|
|
22986
22991
|
init_cursor_ensure_core();
|
|
22987
22992
|
init_lifecycle_paths();
|
|
22988
|
-
var PLUGIN_VERSION = "6.0.
|
|
22993
|
+
var PLUGIN_VERSION = "6.0.7";
|
|
22989
22994
|
var CDP_PORT2 = Number(process.env.CURSOR_BRIDGE_CDP_PORT || 9223);
|
|
22990
22995
|
var ORIGIN = `http://localhost:${CDP_PORT2}`;
|
|
22991
22996
|
var QUERY_TIMEOUT = Number(process.env.CURSOR_BRIDGE_TIMEOUT || 3e5);
|
|
@@ -23373,6 +23378,24 @@ var EXPR_PROVIDER_ERROR = `(function(){
|
|
|
23373
23378
|
const signature=[title,message,requestId||''].join('|');
|
|
23374
23379
|
return JSON.stringify({found:true,title,message,requestId,retryAvailable,signature});
|
|
23375
23380
|
})()`;
|
|
23381
|
+
var STOP_COMPOSER_BODY = `
|
|
23382
|
+
const findStopComposers=(rawId)=>{
|
|
23383
|
+
const legacy=[...document.querySelectorAll('.composer-bar[data-composer-id]')]
|
|
23384
|
+
.filter(e=>e.offsetParent!==null&&e.dataset.composerId&&(!rawId||e.dataset.composerId===rawId));
|
|
23385
|
+
if(legacy.length)return {modern:false,composers:legacy};
|
|
23386
|
+
return {modern:true,composers:[...document.querySelectorAll('[data-agent-panel-conversation-shell][data-glass-agent-id]')]
|
|
23387
|
+
.filter(e=>e.offsetParent!==null&&e.dataset.glassAgentId&&(!rawId||e.dataset.glassAgentId===rawId))};
|
|
23388
|
+
};
|
|
23389
|
+
const modernComposerStatus=(composer,entries)=>{
|
|
23390
|
+
const ownStatus=composer.dataset.composerStatus;
|
|
23391
|
+
if(ownStatus)return ownStatus;
|
|
23392
|
+
const matches=entries.filter(e=>e&&e.id==='local:'+composer.dataset.glassAgentId);
|
|
23393
|
+
if(matches.length!==1)return null;
|
|
23394
|
+
const entry=matches[0];
|
|
23395
|
+
if(entry.showSpinner||entry.icon==='loading')return 'generating';
|
|
23396
|
+
return ({'check-circled':'completed','circle-slash':'cancelled',warning:'failed','needs-attention':'needs_attention'})[entry.icon]||null;
|
|
23397
|
+
};
|
|
23398
|
+
`;
|
|
23376
23399
|
function exprClickSelectedAgentStop(agentId) {
|
|
23377
23400
|
const expected = JSON.stringify(String(agentId));
|
|
23378
23401
|
return `(function(){
|
|
@@ -23385,52 +23408,52 @@ function exprClickSelectedAgentStop(agentId) {
|
|
|
23385
23408
|
if(!selected||selected.id!==${expected}){
|
|
23386
23409
|
return JSON.stringify({clicked:false,state:'selected_agent_mismatch',selectedId:selected&&selected.id||null,selectedCount:selectedEntries.length});
|
|
23387
23410
|
}
|
|
23411
|
+
${STOP_COMPOSER_BODY}
|
|
23388
23412
|
const expectedComposerId=String(${expected}).replace(/^local:/,'');
|
|
23389
|
-
const composers=
|
|
23390
|
-
.filter(e=>e.offsetParent!==null&&e.dataset.composerId===expectedComposerId);
|
|
23413
|
+
const {modern,composers}=findStopComposers(expectedComposerId);
|
|
23391
23414
|
if(composers.length!==1){
|
|
23392
23415
|
return JSON.stringify({clicked:false,state:'composer_identity_mismatch',selectedId:selected.id,count:composers.length});
|
|
23393
23416
|
}
|
|
23394
23417
|
const composer=composers[0];
|
|
23395
|
-
|
|
23396
|
-
|
|
23418
|
+
const status=modern?modernComposerStatus(composer,entries):composer.dataset.composerStatus||null;
|
|
23419
|
+
if(status!=='generating'){
|
|
23420
|
+
return JSON.stringify({clicked:false,state:'composer_not_generating',selectedId:selected.id,status});
|
|
23397
23421
|
}
|
|
23398
|
-
const
|
|
23399
|
-
|
|
23422
|
+
const belongs=button=>button.offsetParent!==null&&!button.disabled&&button.closest(modern?'[data-glass-agent-id]':'.composer-bar')===composer;
|
|
23423
|
+
const generationButtons=[...composer.querySelectorAll(modern?'button.ui-prompt-input-submit-button[aria-label="Stop generation"]':'button.ui-prompt-input-submit-button[data-state="stop"][aria-label="Stop generation"]')]
|
|
23424
|
+
.filter(belongs);
|
|
23400
23425
|
const commandButtons=[...composer.querySelectorAll('button.ui-shell-tool-call__glass-stop[aria-label="Stop command"]')]
|
|
23401
|
-
.filter(
|
|
23426
|
+
.filter(belongs);
|
|
23402
23427
|
const buttons=[...generationButtons,...commandButtons];
|
|
23403
23428
|
if(buttons.length!==1){
|
|
23404
23429
|
return JSON.stringify({clicked:false,state:buttons.length?'ambiguous_stop_controls':'stop_control_missing',selectedId:selected.id,count:buttons.length});
|
|
23405
23430
|
}
|
|
23406
23431
|
buttons[0].click();
|
|
23407
|
-
return JSON.stringify({clicked:true,state:'clicked',selectedId:selected.id,composerId:
|
|
23432
|
+
return JSON.stringify({clicked:true,state:'clicked',selectedId:selected.id,composerId:expectedComposerId,control:generationButtons.length?'stop_generation':'stop_command'});
|
|
23408
23433
|
})()`;
|
|
23409
23434
|
}
|
|
23410
|
-
var EXPR_VISIBLE_COMPOSER = `(function(){
|
|
23411
|
-
const composers=[...document.querySelectorAll('.composer-bar[data-composer-id]')]
|
|
23412
|
-
.filter(e=>e.offsetParent!==null&&e.dataset.composerId);
|
|
23413
|
-
if(composers.length!==1){
|
|
23414
|
-
return JSON.stringify({ok:false,state:composers.length?'ambiguous_composers':'composer_missing',count:composers.length});
|
|
23415
|
-
}
|
|
23416
|
-
const composer=composers[0];
|
|
23417
|
-
return JSON.stringify({
|
|
23418
|
-
ok:true,
|
|
23419
|
-
id:'local:'+composer.dataset.composerId,
|
|
23420
|
-
composerId:composer.dataset.composerId,
|
|
23421
|
-
status:composer.dataset.composerStatus||null
|
|
23422
|
-
});
|
|
23423
|
-
})()`;
|
|
23424
23435
|
function exprClickBoundComposerStop(agentId) {
|
|
23425
23436
|
const expected = JSON.stringify(String(agentId));
|
|
23426
23437
|
return `(function(){
|
|
23438
|
+
${STOP_COMPOSER_BODY}
|
|
23427
23439
|
const expectedComposerId=String(${expected}).replace(/^local:/,'');
|
|
23428
|
-
const composers=
|
|
23429
|
-
.filter(e=>e.offsetParent!==null&&e.dataset.composerId===expectedComposerId);
|
|
23440
|
+
const {modern,composers}=findStopComposers(expectedComposerId);
|
|
23430
23441
|
if(composers.length!==1){
|
|
23431
23442
|
return JSON.stringify({clicked:false,state:'composer_identity_mismatch',count:composers.length});
|
|
23432
23443
|
}
|
|
23433
23444
|
const composer=composers[0];
|
|
23445
|
+
if(modern){
|
|
23446
|
+
${REACT_ADAPTER_BODY}
|
|
23447
|
+
const status=modernComposerStatus(composer,a?a.entries():[]);
|
|
23448
|
+
if(status!=='generating')return JSON.stringify({clicked:false,state:'composer_not_generating',status,composerId:expectedComposerId});
|
|
23449
|
+
const belongs=button=>button.offsetParent!==null&&!button.disabled&&button.closest('[data-glass-agent-id]')===composer;
|
|
23450
|
+
const generationButtons=[...composer.querySelectorAll('button.ui-prompt-input-submit-button[aria-label="Stop generation"]')].filter(belongs);
|
|
23451
|
+
const commandButtons=[...composer.querySelectorAll('button.ui-shell-tool-call__glass-stop[aria-label="Stop command"]')].filter(belongs);
|
|
23452
|
+
const buttons=[...generationButtons,...commandButtons];
|
|
23453
|
+
if(buttons.length!==1)return JSON.stringify({clicked:false,state:buttons.length?'ambiguous_stop_controls':'stop_control_missing',count:buttons.length,composerId:expectedComposerId});
|
|
23454
|
+
buttons[0].click();
|
|
23455
|
+
return JSON.stringify({clicked:true,state:'clicked',composerId:expectedComposerId,control:generationButtons.length?'stop_generation':'stop_command'});
|
|
23456
|
+
}
|
|
23434
23457
|
if(composer.dataset.composerStatus!=='generating'){
|
|
23435
23458
|
return JSON.stringify({clicked:false,state:'composer_not_generating',status:composer.dataset.composerStatus||null,composerId:composer.dataset.composerId});
|
|
23436
23459
|
}
|
|
@@ -23540,12 +23563,90 @@ var EXPR_SELECTED_AGENT_MODEL_CONFIG = `(function(){
|
|
|
23540
23563
|
if(data&&data.modelConfig&&!configs.includes(data.modelConfig))configs.push(data.modelConfig);
|
|
23541
23564
|
}
|
|
23542
23565
|
}
|
|
23543
|
-
if(configs.length
|
|
23544
|
-
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
|
|
23548
|
-
|
|
23566
|
+
if(configs.length>1)return JSON.stringify({found:false,state:'model_config_ambiguous',configCount:configs.length});
|
|
23567
|
+
if(configs.length===1){
|
|
23568
|
+
const config=configs[0];
|
|
23569
|
+
return JSON.stringify({found:true,modelName:String(config.modelName||''),selectedModels:(config.selectedModels||[]).map(model=>({
|
|
23570
|
+
modelId:String(model&&model.modelId||''),
|
|
23571
|
+
parameters:Object.fromEntries((model&&model.parameters||[]).map(parameter=>[String(parameter&¶meter.id||''),String(parameter&¶meter.value||'')]))
|
|
23572
|
+
}))});
|
|
23573
|
+
}
|
|
23574
|
+
// Cursor 3.23's IDE input is Solid; its model trigger is a mounted React
|
|
23575
|
+
// picker whose initialSelections/initialAutoMode come from this composer.
|
|
23576
|
+
const source='legacy_composer_model_trigger';
|
|
23577
|
+
const fail=(state)=>JSON.stringify({found:false,state,source});
|
|
23578
|
+
const input=inputs[0],composer=input.closest('.composer-bar[data-composer-id]');
|
|
23579
|
+
if(!composer||input.closest('.composer-bar')!==composer
|
|
23580
|
+
||composer.parentElement&&composer.parentElement.closest('.composer-bar')
|
|
23581
|
+
||composer.querySelectorAll('.composer-bar').length)return fail('legacy_composer_ambiguous');
|
|
23582
|
+
const composerId=composer.getAttribute('data-composer-id');
|
|
23583
|
+
const owners=[...document.querySelectorAll('.composer-bar[data-composer-id]')]
|
|
23584
|
+
.filter(node=>node.getAttribute('data-composer-id')===composerId);
|
|
23585
|
+
if(typeof composerId!=='string'||!composerId.trim()||owners.length!==1||owners[0]!==composer)return fail('legacy_composer_ambiguous');
|
|
23586
|
+
const triggers=[...composer.querySelectorAll('.vscode-model-picker__trigger')];
|
|
23587
|
+
if(triggers.length!==1||!triggers[0].classList.contains('vscode-model-picker__trigger')
|
|
23588
|
+
||triggers[0].closest('.composer-bar')!==composer)return fail('legacy_model_trigger_ambiguous');
|
|
23589
|
+
const trigger=triggers[0],keys=Object.keys(trigger).filter(key=>key.startsWith('__reactFiber$'));
|
|
23590
|
+
if(keys.length!==1)return fail('legacy_model_tree_unconfirmed');
|
|
23591
|
+
const levels=[],visited=new Set();
|
|
23592
|
+
let root=null,currentRoot=null;
|
|
23593
|
+
for(let fiber=trigger[keys[0]];fiber;fiber=fiber.return){
|
|
23594
|
+
if(levels.length>=256||visited.has(fiber))return fail('legacy_model_tree_unconfirmed');
|
|
23595
|
+
const alternate=fiber.alternate||null;
|
|
23596
|
+
if(alternate&&(alternate===fiber||alternate.alternate!==fiber||visited.has(alternate)))return fail('legacy_model_tree_unconfirmed');
|
|
23597
|
+
visited.add(fiber);if(alternate)visited.add(alternate);
|
|
23598
|
+
const pair=alternate?[fiber,alternate]:[fiber];
|
|
23599
|
+
levels.push(pair);
|
|
23600
|
+
const state=fiber.stateNode;
|
|
23601
|
+
if(state&&state.current){
|
|
23602
|
+
if(fiber.tag!==3||fiber.return||!pair.includes(state.current)||alternate&&(alternate.tag!==3||alternate.stateNode!==state))return fail('legacy_model_tree_unconfirmed');
|
|
23603
|
+
root=state;currentRoot=state.current;break;
|
|
23604
|
+
}
|
|
23605
|
+
}
|
|
23606
|
+
if(!root)return fail('legacy_model_tree_unconfirmed');
|
|
23607
|
+
// DOM expandos may retain an old fiber or a borrowed return chain. Each
|
|
23608
|
+
// edge must belong to the committed parent's actual child/sibling list.
|
|
23609
|
+
const currentPath=[currentRoot],currentVisited=new Set(currentPath);
|
|
23610
|
+
let parent=currentRoot,nodeBudget=2048;
|
|
23611
|
+
for(let depth=levels.length-2;depth>=0;depth--){
|
|
23612
|
+
const candidates=[],siblings=new Set();
|
|
23613
|
+
for(let child=parent.child;child;child=child.sibling){
|
|
23614
|
+
if(--nodeBudget<0||siblings.has(child))return fail('legacy_model_tree_unconfirmed');
|
|
23615
|
+
siblings.add(child);
|
|
23616
|
+
if(levels[depth].includes(child))candidates.push(child);
|
|
23617
|
+
}
|
|
23618
|
+
if(candidates.length!==1||currentVisited.has(candidates[0]))return fail('legacy_model_tree_unconfirmed');
|
|
23619
|
+
parent=candidates[0];currentVisited.add(parent);currentPath.push(parent);
|
|
23620
|
+
}
|
|
23621
|
+
if(parent.stateNode!==trigger)return fail('legacy_model_tree_unconfirmed');
|
|
23622
|
+
const snapshots=new Set();
|
|
23623
|
+
for(const fiber of currentPath){
|
|
23624
|
+
const props=fiber.memoizedProps;
|
|
23625
|
+
if(!props||!Object.prototype.hasOwnProperty.call(props,'initialSelections')
|
|
23626
|
+
&&!Object.prototype.hasOwnProperty.call(props,'initialAutoMode'))continue;
|
|
23627
|
+
if(!Object.prototype.hasOwnProperty.call(props,'initialSelections')||!Object.prototype.hasOwnProperty.call(props,'initialAutoMode')
|
|
23628
|
+
||!Array.isArray(props.initialSelections)||typeof props.initialAutoMode!=='boolean')return fail('legacy_model_schema_unconfirmed');
|
|
23629
|
+
// Auto is never evidence for a named model, even if stale selections remain.
|
|
23630
|
+
if(props.initialAutoMode)return fail('legacy_model_auto');
|
|
23631
|
+
if(props.initialSelections.length!==1)return fail('legacy_model_selection_ambiguous');
|
|
23632
|
+
const selected=props.initialSelections[0];
|
|
23633
|
+
if(!selected||typeof selected.modelId!=='string'||!selected.modelId.trim()
|
|
23634
|
+
||selected.modelId!==selected.modelId.trim()||selected.count!==1||!Array.isArray(selected.parameters))return fail('legacy_model_schema_unconfirmed');
|
|
23635
|
+
if(selected.modelId.toLowerCase()==='default')return fail('legacy_model_auto');
|
|
23636
|
+
const parameters=[];
|
|
23637
|
+
for(const parameter of selected.parameters){
|
|
23638
|
+
if(!parameter||typeof parameter.id!=='string'||!parameter.id.trim()||parameter.id!==parameter.id.trim()
|
|
23639
|
+
||typeof parameter.value!=='string'||!parameter.value.length||parameters.some(entry=>entry[0]===parameter.id))return fail('legacy_model_schema_unconfirmed');
|
|
23640
|
+
parameters.push([parameter.id,parameter.value]);
|
|
23641
|
+
}
|
|
23642
|
+
parameters.sort((a,b)=>a[0]<b[0]?-1:a[0]>b[0]?1:0);
|
|
23643
|
+
snapshots.add(JSON.stringify({modelName:selected.modelId,selectedModels:[{modelId:selected.modelId,parameters:Object.fromEntries(parameters)}]}));
|
|
23644
|
+
}
|
|
23645
|
+
if(!snapshots.size)return fail('legacy_model_schema_unconfirmed');
|
|
23646
|
+
if(snapshots.size!==1)return fail('legacy_model_config_ambiguous');
|
|
23647
|
+
const snapshot=JSON.parse([...snapshots][0]);
|
|
23648
|
+
if(root.current!==currentRoot)return fail('legacy_model_tree_unconfirmed');
|
|
23649
|
+
return JSON.stringify({found:true,...snapshot,source});
|
|
23549
23650
|
})()`;
|
|
23550
23651
|
function normalizeModelPickerText(value) {
|
|
23551
23652
|
return String(value || "").trim().toLowerCase().replace(/extra[\s_-]*high/g, "xhigh").replace(/[^a-z0-9]+/g, "");
|
|
@@ -23709,30 +23810,93 @@ function matchSelectedAgentModelConfig(snapshot, requestedModel, requestedEffort
|
|
|
23709
23810
|
return { modelId: match.modelId, effort: effort || null };
|
|
23710
23811
|
}
|
|
23711
23812
|
function exprSelectAgentWorkspace(projectPath) {
|
|
23712
|
-
return `(async function(){
|
|
23713
|
-
const
|
|
23714
|
-
const normalize
|
|
23813
|
+
return String.raw`(async function(){
|
|
23814
|
+
const normalize=value=>String(value||'').replace(/\\/g,'/').replace(/^\/([a-z]:\/)/i,'$1').replace(/\/+$/,'').toLowerCase();
|
|
23815
|
+
const wanted=normalize(${JSON.stringify(String(projectPath || ""))});
|
|
23715
23816
|
const visible=node=>!!(node&&(node.offsetParent!==null||(node.getClientRects&&node.getClientRects().length>0)));
|
|
23716
23817
|
const pathTriggers=()=>[...document.querySelectorAll('button.ui-select-trigger,button[aria-haspopup="menu"]')]
|
|
23717
|
-
.filter(visible).filter(node=>/^[a-z]
|
|
23818
|
+
.filter(visible).filter(node=>/^[a-z]:\//i.test(normalize(node.innerText||node.textContent)));
|
|
23718
23819
|
const triggers=pathTriggers();
|
|
23719
23820
|
const fail=(state,extra={})=>JSON.stringify({ok:false,state,wanted,...extra,
|
|
23720
23821
|
nextStep:'Select the exact local workspace '+${JSON.stringify(String(projectPath || ""))}+' in the new Agent project picker before retrying.'});
|
|
23721
23822
|
if(triggers.length!==1)return fail(triggers.length?'workspace_project_selector_ambiguous':'workspace_project_selector_unavailable',{triggerCount:triggers.length});
|
|
23722
23823
|
triggers[0].click();
|
|
23723
23824
|
await new Promise(resolve=>setTimeout(resolve,350));
|
|
23724
|
-
const menus=[...document.querySelectorAll('[data-component="menu-popup"],[role="menu"]')].filter(visible)
|
|
23725
|
-
.
|
|
23825
|
+
const menus=[...new Set([...document.querySelectorAll('[data-component="menu-popup"],[role="menu"]')].filter(visible)
|
|
23826
|
+
.map(menu=>menu.getAttribute('role')==='menu'&&menu.getAttribute('aria-label')==='Select a project'
|
|
23827
|
+
?menu:menu.querySelector('[role="menu"][aria-label="Select a project"]')).filter(visible))];
|
|
23828
|
+
if(menus.length!==1)return fail(menus.length?'workspace_project_option_ambiguous':'workspace_project_option_unavailable',{menuCount:menus.length});
|
|
23726
23829
|
const rows=[];const seen=new Set();
|
|
23727
23830
|
for(const menu of menus){
|
|
23728
23831
|
for(const row of menu.querySelectorAll('[data-component="menu-row"][role="menuitem"],[role="option"]')){
|
|
23729
23832
|
if(!visible(row)||seen.has(row))continue;seen.add(row);rows.push(row);
|
|
23730
23833
|
}
|
|
23731
23834
|
}
|
|
23732
|
-
const available=rows.map(row=>String(row.innerText||row.textContent||'').replace(
|
|
23733
|
-
const
|
|
23835
|
+
const available=rows.map(row=>String(row.innerText||row.textContent||'').replace(/\s+/g,' ').trim());
|
|
23836
|
+
const rowWorkspace=row=>{
|
|
23837
|
+
const key=Object.keys(row).find(key=>key.startsWith('__reactFiber$'));
|
|
23838
|
+
const levels=[];const visited=new Set();
|
|
23839
|
+
let root=null,currentRoot=null,hasProject=false,belowMenu=true;
|
|
23840
|
+
for(let fiber=key&&row[key];fiber;fiber=fiber.return){
|
|
23841
|
+
if(levels.length>=256||visited.has(fiber))return null;
|
|
23842
|
+
visited.add(fiber);
|
|
23843
|
+
const alternate=fiber.alternate||null;
|
|
23844
|
+
if(alternate&&(alternate===fiber||alternate.alternate!==fiber||visited.has(alternate)))return null;
|
|
23845
|
+
const pair=alternate?[fiber,alternate]:[fiber];
|
|
23846
|
+
levels.push(pair);
|
|
23847
|
+
if(pair.some(node=>node.stateNode===menus[0]))belowMenu=false;
|
|
23848
|
+
if(belowMenu&&pair.some(node=>node.memoizedProps&&Object.prototype.hasOwnProperty.call(node.memoizedProps,'project')))hasProject=true;
|
|
23849
|
+
const state=fiber.stateNode;
|
|
23850
|
+
if(state&&state.current){
|
|
23851
|
+
if(!pair.includes(state.current)||alternate&&alternate.stateNode!==state)return null;
|
|
23852
|
+
root=state;currentRoot=state.current;break;
|
|
23853
|
+
}
|
|
23854
|
+
}
|
|
23855
|
+
// Older pickers expose the complete path as row text without project metadata.
|
|
23856
|
+
if(!hasProject)return {path:normalize(row.innerText||row.textContent)};
|
|
23857
|
+
if(!root)return null;
|
|
23858
|
+
// A DOM expando can retain an old fiber, including a borrowed current return
|
|
23859
|
+
// chain. Prove every edge through the committed parent's child/sibling list.
|
|
23860
|
+
const currentPath=[currentRoot];const currentVisited=new Set(currentPath);
|
|
23861
|
+
let parent=currentRoot,nodeBudget=2048;
|
|
23862
|
+
for(let depth=levels.length-2;depth>=0;depth--){
|
|
23863
|
+
const candidates=[];const siblings=new Set();
|
|
23864
|
+
for(let child=parent.child;child;child=child.sibling){
|
|
23865
|
+
if(--nodeBudget<0||siblings.has(child))return null;
|
|
23866
|
+
siblings.add(child);
|
|
23867
|
+
if(levels[depth].includes(child))candidates.push(child);
|
|
23868
|
+
}
|
|
23869
|
+
if(candidates.length!==1||currentVisited.has(candidates[0]))return null;
|
|
23870
|
+
parent=candidates[0];currentVisited.add(parent);currentPath.push(parent);
|
|
23871
|
+
}
|
|
23872
|
+
if(parent.stateNode!==row)return null;
|
|
23873
|
+
const menuIndexes=currentPath.flatMap((node,index)=>node.stateNode===menus[0]?[index]:[]);
|
|
23874
|
+
if(menuIndexes.length!==1)return null;
|
|
23875
|
+
const projects=new Set();
|
|
23876
|
+
for(const fiber of currentPath.slice(menuIndexes[0]+1)){
|
|
23877
|
+
const props=fiber.memoizedProps;
|
|
23878
|
+
if(props&&Object.prototype.hasOwnProperty.call(props,'project'))projects.add(props.project);
|
|
23879
|
+
}
|
|
23880
|
+
if(!projects.size)return null;
|
|
23881
|
+
const identities=new Set();
|
|
23882
|
+
for(const project of projects){
|
|
23883
|
+
const identifier=project&&project.workspaceIdentifier;
|
|
23884
|
+
const uri=identifier&&(identifier.uri||identifier.configPath);
|
|
23885
|
+
if(!project||project.remoteAuthority||!identifier||identifier.remoteAuthority||typeof identifier.id!=='string'||!identifier.id.trim()
|
|
23886
|
+
||!uri||uri.scheme!=='file'||uri.authority||uri.remoteAuthority)return null;
|
|
23887
|
+
const paths=[uri.fsPath,uri._fsPath,uri.path].filter(value=>typeof value==='string'&&value.length).map(normalize);
|
|
23888
|
+
if(!paths.length||paths.some(path=>path!==paths[0]))return null;
|
|
23889
|
+
identities.add(JSON.stringify([identifier.id,paths[0]]));
|
|
23890
|
+
}
|
|
23891
|
+
if(identities.size!==1)return null;
|
|
23892
|
+
const [workspaceId,path]=JSON.parse([...identities][0]);
|
|
23893
|
+
return {path,workspaceId,root,currentRoot};
|
|
23894
|
+
};
|
|
23895
|
+
const matches=rows.map(row=>({row,identity:rowWorkspace(row)})).filter(match=>match.identity&&match.identity.path===wanted);
|
|
23734
23896
|
if(matches.length!==1)return fail(matches.length?'workspace_project_option_ambiguous':'workspace_project_option_unavailable',{available});
|
|
23735
|
-
matches[0]
|
|
23897
|
+
const selected=matches[0];
|
|
23898
|
+
if(selected.identity.root&&selected.identity.root.current!==selected.identity.currentRoot)return fail('workspace_project_option_unavailable',{available});
|
|
23899
|
+
selected.row.click();
|
|
23736
23900
|
await new Promise(resolve=>setTimeout(resolve,350));
|
|
23737
23901
|
return JSON.stringify({ok:true,state:'workspace_project_selection_requested',workspace:wanted,available});
|
|
23738
23902
|
})()`;
|
|
@@ -23902,7 +24066,11 @@ var REACT_ADAPTER_BODY = `
|
|
|
23902
24066
|
let globalSelectAgent=null;
|
|
23903
24067
|
let globalSelectedAgentId=null;
|
|
23904
24068
|
let sectionIdByAgentId=null;
|
|
23905
|
-
|
|
24069
|
+
const roots=new Set([
|
|
24070
|
+
...document.querySelectorAll('.glass-sidebar-agent-list-container'),
|
|
24071
|
+
...document.querySelectorAll('.ui-sidebar-section-head')
|
|
24072
|
+
]);
|
|
24073
|
+
for(const root of roots){
|
|
23906
24074
|
const nodes=[]; let n=root;
|
|
23907
24075
|
for(let i=0;n&&i<24;i++,n=n.parentElement)nodes.push(n);
|
|
23908
24076
|
for(const node of nodes){
|
|
@@ -24021,6 +24189,26 @@ var REACT_ADAPTER_BODY = `
|
|
|
24021
24189
|
};
|
|
24022
24190
|
};
|
|
24023
24191
|
const a=findAdapter();`;
|
|
24192
|
+
var EXPR_VISIBLE_COMPOSER = `(function(){
|
|
24193
|
+
${STOP_COMPOSER_BODY}
|
|
24194
|
+
const {modern,composers}=findStopComposers(null);
|
|
24195
|
+
if(composers.length!==1){
|
|
24196
|
+
return JSON.stringify({ok:false,state:composers.length?'ambiguous_composers':'composer_missing',count:composers.length});
|
|
24197
|
+
}
|
|
24198
|
+
const composer=composers[0];
|
|
24199
|
+
let status=composer.dataset.composerStatus||null;
|
|
24200
|
+
if(modern){
|
|
24201
|
+
${REACT_ADAPTER_BODY}
|
|
24202
|
+
status=modernComposerStatus(composer,a?a.entries():[]);
|
|
24203
|
+
}
|
|
24204
|
+
const composerId=modern?composer.dataset.glassAgentId:composer.dataset.composerId;
|
|
24205
|
+
return JSON.stringify({
|
|
24206
|
+
ok:true,
|
|
24207
|
+
id:'local:'+composerId,
|
|
24208
|
+
composerId,
|
|
24209
|
+
status
|
|
24210
|
+
});
|
|
24211
|
+
})()`;
|
|
24024
24212
|
var EXPR_AGENT_ADAPTER_READY = `(function(){${REACT_ADAPTER_BODY}return !!a;})()`;
|
|
24025
24213
|
var EXPR_PAGE_CAPABILITIES = `(function(){${INPUT_PICKER_BODY}
|
|
24026
24214
|
const visible=node=>!!(node&&(node.offsetParent!==null||(typeof node.getClientRects==='function'&&node.getClientRects().length>0)));
|
|
@@ -24282,12 +24470,12 @@ var CursorBridge = class {
|
|
|
24282
24470
|
this.runtimeModeSource = options.runtimeMode !== void 0 ? "constructor" : persistedRuntimeMode ? "persistent" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
24283
24471
|
this.runtimeModeScope = persistedRuntimeMode ? "persistent" : options.runtimeMode !== void 0 ? "constructor" : process.env.CURSOR_BRIDGE_RUNTIME_MODE ? "environment" : "default";
|
|
24284
24472
|
this.workspaceFile = options.workspaceFile === null ? null : resolve8(options.workspaceFile || resolveWorkspaceBindingFile());
|
|
24285
|
-
this.workspaceKey = options.workspaceKey || resolveWorkspaceBindingKey();
|
|
24473
|
+
this.workspaceKey = options.workspaceKey || resolveWorkspaceBindingKey(process.env, { cwd: this.adapterStartCwd });
|
|
24286
24474
|
const persistedWorkspace = options.projectPath === void 0 ? readWorkspaceBinding(this.workspaceFile, this.workspaceKey) : null;
|
|
24287
24475
|
this.projectPath = options.projectPath !== void 0 ? resolve8(String(options.projectPath)) : persistedWorkspace && persistedWorkspace.projectPath || null;
|
|
24288
24476
|
this.workspaceSource = options.projectPath !== void 0 ? "constructor" : persistedWorkspace ? "persistent_init" : "auto_detect";
|
|
24289
24477
|
this.workspaceUpdatedAt = persistedWorkspace && persistedWorkspace.updatedAt || null;
|
|
24290
|
-
this.workspaceConfirmationRequired = this.workspaceKey === "default" &&
|
|
24478
|
+
this.workspaceConfirmationRequired = this.workspaceKey === "default" && options.projectPath === void 0;
|
|
24291
24479
|
this.modelPreferencesFile = options.modelPreferencesFile === null ? null : resolve8(options.modelPreferencesFile || resolveCursorModelPreferencesFile());
|
|
24292
24480
|
this.modelPreferences = readCursorModelPreferences(this.modelPreferencesFile);
|
|
24293
24481
|
this.sessionFile = options.sessionFile === null ? null : resolve8(options.sessionFile || resolveCursorSessionRegistryFile());
|
|
@@ -24303,6 +24491,8 @@ var CursorBridge = class {
|
|
|
24303
24491
|
this.activeParallel = /* @__PURE__ */ new Map();
|
|
24304
24492
|
this.currentJob = null;
|
|
24305
24493
|
this._uiTail = Promise.resolve();
|
|
24494
|
+
this._workspaceTail = Promise.resolve();
|
|
24495
|
+
this._workspacePending = 0;
|
|
24306
24496
|
this.parallelRestoreAgentId = null;
|
|
24307
24497
|
this.parallelRestoreTargetId = null;
|
|
24308
24498
|
}
|
|
@@ -24310,6 +24500,8 @@ var CursorBridge = class {
|
|
|
24310
24500
|
const resolvedProjectPath = this.projectPath || this._lastLifecycle && this._lastLifecycle.projectPath || null;
|
|
24311
24501
|
return {
|
|
24312
24502
|
workspaceKey: this.workspaceKey,
|
|
24503
|
+
workspaceIdentitySource: this.workspaceKey === "default" ? "unavailable" : this.workspaceKey.split(":")[0],
|
|
24504
|
+
workspaceBusy: this._workspacePending > 0,
|
|
24313
24505
|
workspaceConfirmationRequired: this.workspaceConfirmationRequired,
|
|
24314
24506
|
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
24507
|
projectPath: resolvedProjectPath,
|
|
@@ -24692,8 +24884,12 @@ var CursorBridge = class {
|
|
|
24692
24884
|
if (!["reconcile", "collect_result", "close", "forget", "abandon"].includes(action)) {
|
|
24693
24885
|
throw cursorSessionError("SESSION_ACTION_UNSUPPORTED", "expected reconcile, collect_result, close, forget, or abandon");
|
|
24694
24886
|
}
|
|
24695
|
-
if (action === "reconcile"
|
|
24696
|
-
|
|
24887
|
+
if (action === "reconcile" || action === "collect_result") {
|
|
24888
|
+
return this._withWorkspaceAdmission(() => {
|
|
24889
|
+
this._assertWorkspaceConfirmed();
|
|
24890
|
+
return this._reconcileSession(id, { collectResult: action === "collect_result" });
|
|
24891
|
+
});
|
|
24892
|
+
}
|
|
24697
24893
|
let result;
|
|
24698
24894
|
updateCursorSessionRegistry(this.sessionFile, (registry2) => {
|
|
24699
24895
|
const session = registry2.sessions[id];
|
|
@@ -24746,6 +24942,10 @@ var CursorBridge = class {
|
|
|
24746
24942
|
return result;
|
|
24747
24943
|
}
|
|
24748
24944
|
async initializeWorkspace(projectPath) {
|
|
24945
|
+
const result = await this._withWorkspaceAdmission(() => this._initializeWorkspace(projectPath));
|
|
24946
|
+
return { ...result, workspaceBusy: this._workspacePending > 0 };
|
|
24947
|
+
}
|
|
24948
|
+
async _initializeWorkspace(projectPath) {
|
|
24749
24949
|
if (this._healing) await this._healing.catch(() => {
|
|
24750
24950
|
});
|
|
24751
24951
|
if (this.busy || this.activeParallel.size > 0 || this.queue.length > 0) {
|
|
@@ -24971,19 +25171,23 @@ var CursorBridge = class {
|
|
|
24971
25171
|
if (!text) throw new Error("query must not be empty");
|
|
24972
25172
|
if (text.length > 2e4) throw new Error("query exceeds the 20,000-character limit");
|
|
24973
25173
|
const requestContext = normalizeRequestContext(options.requestContext);
|
|
24974
|
-
this.
|
|
24975
|
-
|
|
24976
|
-
|
|
24977
|
-
|
|
24978
|
-
|
|
24979
|
-
|
|
24980
|
-
|
|
24981
|
-
|
|
24982
|
-
|
|
24983
|
-
|
|
24984
|
-
|
|
24985
|
-
|
|
24986
|
-
|
|
25174
|
+
const job = await this._withWorkspaceAdmission(async () => {
|
|
25175
|
+
this._assertWorkspaceConfirmed(options.workspacePath);
|
|
25176
|
+
if (this._hasGlobalReservation()) {
|
|
25177
|
+
throw new Error("A global Cursor reservation has an unconfirmed Stop state; resolve blockingTaskIds from cursor_status first");
|
|
25178
|
+
}
|
|
25179
|
+
await this._ensureCursor();
|
|
25180
|
+
this._assertWorkspaceConfirmed(options.workspacePath);
|
|
25181
|
+
return this._enqueue("context_engine", buildContextEnginePrompt(text, requestContext), {
|
|
25182
|
+
requestContext,
|
|
25183
|
+
timeoutMs: QUERY_TIMEOUT,
|
|
25184
|
+
newChat: true,
|
|
25185
|
+
execution: "fifo",
|
|
25186
|
+
readOnly: true,
|
|
25187
|
+
allowedPaths: [],
|
|
25188
|
+
modelPreference: this._modelPreferenceFor("cce"),
|
|
25189
|
+
projectPath: this._lastLifecycle?.projectPath || this.projectPath || null
|
|
25190
|
+
});
|
|
24987
25191
|
});
|
|
24988
25192
|
const result = normalizeCceSearchResult(await job.promise);
|
|
24989
25193
|
this._markTaskResultCollected(job);
|
|
@@ -24997,6 +25201,14 @@ var CursorBridge = class {
|
|
|
24997
25201
|
return this.contextEngine(query);
|
|
24998
25202
|
}
|
|
24999
25203
|
async doTask(prompt, options = {}) {
|
|
25204
|
+
const admitted = await this._withWorkspaceAdmission(() => this._prepareTask(prompt, options));
|
|
25205
|
+
if (admitted.duplicate) return admitted.response;
|
|
25206
|
+
const job = admitted.job;
|
|
25207
|
+
if (options.background !== false) return this._compactTaskView(job);
|
|
25208
|
+
await job.promise;
|
|
25209
|
+
return this._collectedTaskView(job);
|
|
25210
|
+
}
|
|
25211
|
+
async _prepareTask(prompt, options = {}) {
|
|
25000
25212
|
if (!this.delegationEnabled) {
|
|
25001
25213
|
throw new Error("cursor_do is disabled by CURSOR_BRIDGE_DELEGATION=off; restart the MCP server without that setting to enable delegation");
|
|
25002
25214
|
}
|
|
@@ -25004,7 +25216,7 @@ var CursorBridge = class {
|
|
|
25004
25216
|
if (!text) throw new Error("prompt must not be empty");
|
|
25005
25217
|
if (text.length > 1e5) throw new Error("prompt exceeds the 100,000-character limit");
|
|
25006
25218
|
const requestContext = normalizeRequestContext(options.requestContext);
|
|
25007
|
-
this._assertWorkspaceConfirmed();
|
|
25219
|
+
this._assertWorkspaceConfirmed(options.workspacePath);
|
|
25008
25220
|
if (this._hasGlobalReservation()) {
|
|
25009
25221
|
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
25222
|
}
|
|
@@ -25049,6 +25261,7 @@ var CursorBridge = class {
|
|
|
25049
25261
|
throw cursorSessionError("SESSION_SCOPE_REQUIRED", "repeat read_only=true or an allowed_paths subset for every continued turn");
|
|
25050
25262
|
}
|
|
25051
25263
|
await this._ensureCursor();
|
|
25264
|
+
this._assertWorkspaceConfirmed(options.workspacePath);
|
|
25052
25265
|
const projectPath = this._lastLifecycle && this._lastLifecycle.projectPath || this.projectPath || null;
|
|
25053
25266
|
if (sessionMode !== "isolated" && !projectPath) {
|
|
25054
25267
|
throw cursorSessionError("SESSION_WORKSPACE_REQUIRED", "initialize one workspace before creating or continuing a session");
|
|
@@ -25089,7 +25302,8 @@ var CursorBridge = class {
|
|
|
25089
25302
|
modelPreference = session.modelPreference || null;
|
|
25090
25303
|
if (duplicate) {
|
|
25091
25304
|
const existing = this.tasks.get(session.activeTaskId || session.lastTask && session.lastTask.taskId || "");
|
|
25092
|
-
|
|
25305
|
+
const response = existing ? { duplicate: true, ...options.background === false ? this._collectedTaskView(existing) : this._compactTaskView(existing) } : { duplicate: true, ...this._sessionView(session) };
|
|
25306
|
+
return { duplicate: true, response };
|
|
25093
25307
|
}
|
|
25094
25308
|
}
|
|
25095
25309
|
const job = this._enqueue("do", fullPrompt, {
|
|
@@ -25113,9 +25327,7 @@ var CursorBridge = class {
|
|
|
25113
25327
|
agentId: sessionMode === "continue" ? session && session.agentId : null,
|
|
25114
25328
|
agentLabel: sessionMode === "continue" ? session && session.agentLabel : null
|
|
25115
25329
|
});
|
|
25116
|
-
|
|
25117
|
-
await job.promise;
|
|
25118
|
-
return this._collectedTaskView(job);
|
|
25330
|
+
return { job };
|
|
25119
25331
|
}
|
|
25120
25332
|
_assertNoParallelPathConflict(allowedPaths) {
|
|
25121
25333
|
if (this._hasGlobalReservation()) {
|
|
@@ -25305,6 +25517,17 @@ var CursorBridge = class {
|
|
|
25305
25517
|
});
|
|
25306
25518
|
return run;
|
|
25307
25519
|
}
|
|
25520
|
+
// Serialize target changes with preparation through enqueue, never with a running Agent.
|
|
25521
|
+
// The UI lock cannot serve this purpose: initialization may acquire it to register a project.
|
|
25522
|
+
_withWorkspaceAdmission(fn) {
|
|
25523
|
+
this._workspacePending++;
|
|
25524
|
+
const run = this._workspaceTail.then(fn, fn).finally(() => {
|
|
25525
|
+
this._workspacePending--;
|
|
25526
|
+
});
|
|
25527
|
+
this._workspaceTail = run.catch(() => {
|
|
25528
|
+
});
|
|
25529
|
+
return run;
|
|
25530
|
+
}
|
|
25308
25531
|
_withJobLock(job, fn) {
|
|
25309
25532
|
const previous = job.controlTail || Promise.resolve();
|
|
25310
25533
|
const run = previous.then(fn, fn);
|
|
@@ -25336,6 +25559,8 @@ var CursorBridge = class {
|
|
|
25336
25559
|
reason: "adapter-heal",
|
|
25337
25560
|
runtimeMode: this.runtimeMode,
|
|
25338
25561
|
adapterStartCwd: this.adapterStartCwd,
|
|
25562
|
+
workspaceKey: this.workspaceKey,
|
|
25563
|
+
workspaceFile: this.workspaceFile,
|
|
25339
25564
|
...this.projectPath ? { projectPath: this.projectPath } : {}
|
|
25340
25565
|
});
|
|
25341
25566
|
this._lastLifecycle = lifecycleFromEnsureResult(rr, this.runtimeMode);
|
|
@@ -27537,6 +27762,8 @@ var CursorBridge = class {
|
|
|
27537
27762
|
pluginVersion: PLUGIN_VERSION,
|
|
27538
27763
|
statusPath: "json-list",
|
|
27539
27764
|
workspaceKey: workspace.workspaceKey,
|
|
27765
|
+
workspaceIdentitySource: workspace.workspaceIdentitySource,
|
|
27766
|
+
workspaceBusy: workspace.workspaceBusy,
|
|
27540
27767
|
workspaceConfirmationRequired: workspace.workspaceConfirmationRequired,
|
|
27541
27768
|
workspaceBindingWarning: workspace.workspaceBindingWarning,
|
|
27542
27769
|
projectPath: workspace.projectPath,
|
|
@@ -27568,9 +27795,45 @@ var CursorBridge = class {
|
|
|
27568
27795
|
cursorPid: lifecycle.cursorPid
|
|
27569
27796
|
};
|
|
27570
27797
|
}
|
|
27571
|
-
|
|
27798
|
+
_workspaceRecoveryError(code, message, expectedProjectPath = null) {
|
|
27799
|
+
const error2 = new Error(`${code}: ${message}`);
|
|
27800
|
+
error2.code = code;
|
|
27801
|
+
error2.workspaceRecovery = {
|
|
27802
|
+
workspaceKey: this.workspaceKey,
|
|
27803
|
+
currentProjectPath: this._lastLifecycle?.projectPath || this.projectPath || null,
|
|
27804
|
+
expectedProjectPath,
|
|
27805
|
+
tool: "cursor_init",
|
|
27806
|
+
arguments: expectedProjectPath ? { path: expectedProjectPath } : null,
|
|
27807
|
+
requiresIdle: true,
|
|
27808
|
+
retrySafe: true,
|
|
27809
|
+
submissionStarted: false,
|
|
27810
|
+
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."
|
|
27811
|
+
};
|
|
27812
|
+
return error2;
|
|
27813
|
+
}
|
|
27814
|
+
_assertWorkspaceConfirmed(workspacePath) {
|
|
27815
|
+
let expected = null;
|
|
27816
|
+
if (workspacePath !== void 0) {
|
|
27817
|
+
if (typeof workspacePath !== "string" || !isAbsoluteWorkspacePath(workspacePath)) {
|
|
27818
|
+
throw cursorSessionError("WORKSPACE_PATH_INVALID", "workspace_path must be an absolute project directory or .code-workspace path");
|
|
27819
|
+
}
|
|
27820
|
+
expected = normalizeWorkspacePath(workspacePath);
|
|
27821
|
+
}
|
|
27572
27822
|
if (this.workspaceConfirmationRequired) {
|
|
27573
|
-
throw
|
|
27823
|
+
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);
|
|
27824
|
+
}
|
|
27825
|
+
if (expected) {
|
|
27826
|
+
const current = this._lastLifecycle?.projectPath || this.projectPath;
|
|
27827
|
+
if (!current) {
|
|
27828
|
+
throw this._workspaceRecoveryError("WORKSPACE_INITIALIZATION_REQUIRED", "Run cursor_init with workspace_path before submitting work.", expected);
|
|
27829
|
+
}
|
|
27830
|
+
const canonical = (path) => {
|
|
27831
|
+
const normalized = normalizeWorkspacePath(path);
|
|
27832
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
27833
|
+
};
|
|
27834
|
+
if (canonical(current) !== canonical(expected)) {
|
|
27835
|
+
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);
|
|
27836
|
+
}
|
|
27574
27837
|
}
|
|
27575
27838
|
}
|
|
27576
27839
|
_ensureTaskCapacity() {
|
|
@@ -27618,10 +27881,10 @@ var CursorBridge = class {
|
|
|
27618
27881
|
...this.runtimeModeView(),
|
|
27619
27882
|
...this.modelPreferencesView(),
|
|
27620
27883
|
...this.sessionRegistryView(),
|
|
27621
|
-
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
|
|
27884
|
+
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0 || this._workspacePending > 0,
|
|
27622
27885
|
uiBusy,
|
|
27623
27886
|
parallelRunning,
|
|
27624
|
-
idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0,
|
|
27887
|
+
idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0 && this._workspacePending === 0,
|
|
27625
27888
|
queued: this.queue.length,
|
|
27626
27889
|
blockingTaskIds: [...this.activeParallel.values()].filter((job) => !isTerminalTask(job)).map((job) => job.id),
|
|
27627
27890
|
globallyBlocked: globalBlocked,
|
|
@@ -27650,10 +27913,10 @@ var CursorBridge = class {
|
|
|
27650
27913
|
}
|
|
27651
27914
|
} : {
|
|
27652
27915
|
...this._compactStatusCommon(),
|
|
27653
|
-
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0,
|
|
27916
|
+
busy: uiBusy || parallelRunning > 0 || this.queue.length > 0 || this._workspacePending > 0,
|
|
27654
27917
|
uiBusy,
|
|
27655
27918
|
parallelRunning,
|
|
27656
|
-
idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0,
|
|
27919
|
+
idle: !uiBusy && parallelRunning === 0 && this.queue.length === 0 && this._workspacePending === 0,
|
|
27657
27920
|
queued: this.queue.length,
|
|
27658
27921
|
blockingTaskIds: [...this.activeParallel.values()].filter((job) => !isTerminalTask(job)).map((job) => job.id),
|
|
27659
27922
|
globallyBlocked: globalBlocked,
|
|
@@ -27678,6 +27941,7 @@ function buildSearchInputSchema() {
|
|
|
27678
27941
|
type: "object",
|
|
27679
27942
|
properties: {
|
|
27680
27943
|
query: { type: "string", description: "Describe the behavior, concept, symbol relationship, or ownership boundary to locate. State intent instead of guessing a directory." },
|
|
27944
|
+
workspace_path: WORKSPACE_PATH_SCHEMA,
|
|
27681
27945
|
request_context: REQUEST_CONTEXT_SCHEMA
|
|
27682
27946
|
},
|
|
27683
27947
|
required: ["query"]
|
|
@@ -27699,6 +27963,10 @@ var REQUEST_CONTEXT_SCHEMA = {
|
|
|
27699
27963
|
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
27964
|
}
|
|
27701
27965
|
};
|
|
27966
|
+
var WORKSPACE_PATH_SCHEMA = {
|
|
27967
|
+
type: "string",
|
|
27968
|
+
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."
|
|
27969
|
+
};
|
|
27702
27970
|
function buildToolDefinitions(bridgeInstance) {
|
|
27703
27971
|
return [
|
|
27704
27972
|
{
|
|
@@ -27724,6 +27992,7 @@ function buildToolDefinitions(bridgeInstance) {
|
|
|
27724
27992
|
type: "object",
|
|
27725
27993
|
properties: {
|
|
27726
27994
|
prompt: { type: "string", description: "The task Cursor should receive. State the goal, boundaries, and what a complete result looks like." },
|
|
27995
|
+
workspace_path: WORKSPACE_PATH_SCHEMA,
|
|
27727
27996
|
request_context: REQUEST_CONTEXT_SCHEMA,
|
|
27728
27997
|
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
27998
|
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 +28085,35 @@ var server = new Server(
|
|
|
27816
28085
|
{ capabilities: { tools: { listChanged: true } } }
|
|
27817
28086
|
);
|
|
27818
28087
|
async function ensureBridgeCursor(targetBridge, reason) {
|
|
27819
|
-
targetBridge.
|
|
27820
|
-
|
|
27821
|
-
|
|
27822
|
-
|
|
27823
|
-
|
|
27824
|
-
|
|
27825
|
-
|
|
28088
|
+
return targetBridge._withWorkspaceAdmission(async () => {
|
|
28089
|
+
targetBridge._refreshPersistedRuntimeMode();
|
|
28090
|
+
const { ensureCursorRunning: ensureCursorRunning2 } = await Promise.resolve().then(() => (init_launch_cursor(), launch_cursor_exports));
|
|
28091
|
+
const r = await ensureCursorRunning2({
|
|
28092
|
+
reason,
|
|
28093
|
+
runtimeMode: targetBridge.runtimeMode,
|
|
28094
|
+
adapterStartCwd: targetBridge.adapterStartCwd,
|
|
28095
|
+
workspaceKey: targetBridge.workspaceKey,
|
|
28096
|
+
workspaceFile: targetBridge.workspaceFile,
|
|
28097
|
+
// Prewarming Cursor is not permission to open another task's saved default project.
|
|
28098
|
+
...targetBridge.workspaceConfirmationRequired ? { projectPath: null } : targetBridge.projectPath ? { projectPath: targetBridge.projectPath } : {}
|
|
28099
|
+
});
|
|
28100
|
+
targetBridge._lastLifecycle = lifecycleFromEnsureResult(r, targetBridge.runtimeMode);
|
|
28101
|
+
if (targetBridge.runtimeMode === "minimal") {
|
|
28102
|
+
targetBridge._lastPresentation = r.presentation ? { ...r.presentation, at: (/* @__PURE__ */ new Date()).toISOString() } : await targetBridge.applyRuntimePresentation("hide");
|
|
28103
|
+
}
|
|
28104
|
+
return r;
|
|
27826
28105
|
});
|
|
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
28106
|
}
|
|
27833
28107
|
function toolErrorResult(error2) {
|
|
27834
28108
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
27835
|
-
if (error2 && (error2.uiDiagnostic || error2.modelSelection || error2.pickerRead || error2.cdp)) {
|
|
28109
|
+
if (error2 && (error2.uiDiagnostic || error2.modelSelection || error2.pickerRead || error2.cdp || error2.workspaceRecovery)) {
|
|
27836
28110
|
const payload = {
|
|
27837
28111
|
error: { code: error2.code || error2.uiDiagnostic?.code || "CURSOR_BRIDGE_ERROR", message },
|
|
27838
28112
|
...error2.uiDiagnostic ? { uiDiagnostic: error2.uiDiagnostic } : {},
|
|
27839
28113
|
...error2.modelSelection ? { modelSelection: error2.modelSelection } : {},
|
|
27840
28114
|
...error2.pickerRead ? { pickerRead: error2.pickerRead } : {},
|
|
27841
|
-
...error2.cdp ? { cdp: error2.cdp } : {}
|
|
28115
|
+
...error2.cdp ? { cdp: error2.cdp } : {},
|
|
28116
|
+
...error2.workspaceRecovery ? { workspaceRecovery: error2.workspaceRecovery } : {}
|
|
27842
28117
|
};
|
|
27843
28118
|
return {
|
|
27844
28119
|
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
@@ -27859,11 +28134,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
|
27859
28134
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
27860
28135
|
}
|
|
27861
28136
|
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 });
|
|
28137
|
+
const result = await bridge.contextEngine(String(args && args.query || ""), { requestContext: args && args.request_context, workspacePath: args && args.workspace_path });
|
|
27863
28138
|
return { content: [{ type: "text", text: String(result) }] };
|
|
27864
28139
|
}
|
|
27865
28140
|
if (name === "cursor_do") {
|
|
27866
28141
|
const result = await bridge.doTask(String(args && args.prompt || ""), {
|
|
28142
|
+
workspacePath: args && args.workspace_path,
|
|
27867
28143
|
requestContext: args && args.request_context,
|
|
27868
28144
|
background: !args || args.background !== false,
|
|
27869
28145
|
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.7",
|
|
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.7",
|
|
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.7"
|
|
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
|
|