pi-cursor-bridge 0.1.2 → 0.1.4

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.
@@ -601,8 +601,8 @@ async function ensureCursorRunningLocal(options = {}) {
601
601
  windowGuard: windowGuard2,
602
602
  needsAction: "install_or_locate_cursor",
603
603
  retryable: true,
604
- nextStep: "\u8BF7\u786E\u8BA4 Cursor \u5DF2\u5B89\u88C5\u5E76\u767B\u5F55\u3002\u82E5\u4F7F\u7528\u4FBF\u643A\u7248\u6216\u81EA\u5B9A\u4E49\u76EE\u5F55\uFF0C\u8BF7\u8BBE\u7F6E CURSOR_EXE \u540E\u91CD\u65B0\u6267\u884C\u540C\u4E00\u53E5\u521D\u59CB\u5316\u547D\u4EE4\u3002",
605
- message: `CCE \u5DF2\u8FDE\u63A5 Cursor\uFF0C\u4F46\u8FD8\u4E0D\u80FD\u6253\u5F00\u5DE5\u4F5C\u533A ${projectPath}\uFF0C\u56E0\u4E3A\u6CA1\u6709\u627E\u5230 Cursor \u7A0B\u5E8F\u3002`
604
+ nextStep: "Confirm that Cursor is installed and signed in. For a portable or custom installation, set CURSOR_EXE and run the same initialization command again.",
605
+ message: `CCE connected to Cursor but cannot open workspace ${projectPath} because the Cursor executable was not found.`
606
606
  };
607
607
  }
608
608
  const beforeTargetIds = new Set(currentTargets.map((target2) => target2.id));
@@ -629,8 +629,8 @@ async function ensureCursorRunningLocal(options = {}) {
629
629
  cursorExecutableSource: cursorExecutable2.source,
630
630
  needsAction: "retry_initialization",
631
631
  retryable: true,
632
- nextStep: "\u8BF7\u7B49\u5F85 Cursor \u5B8C\u6210\u6253\u5F00\u9879\u76EE\uFF0C\u7136\u540E\u91CD\u65B0\u6267\u884C\u540C\u4E00\u53E5\u521D\u59CB\u5316\u547D\u4EE4\u3002",
633
- message: `Cursor \u5DF2\u6253\u5F00\u9879\u76EE\uFF0C\u4F46 CCE \u8FD8\u6CA1\u6709\u786E\u8BA4\u5DE5\u4F5C\u533A ${projectPath} \u5DF2\u51C6\u5907\u597D\uFF1B\u4E3A\u907F\u514D\u641C\u7D22\u9519\u9879\u76EE\uFF0C\u672C\u6B21\u521D\u59CB\u5316\u5DF2\u5B89\u5168\u505C\u6B62\u3002`
632
+ nextStep: "Wait for Cursor to finish opening the project, then run the same initialization command again.",
633
+ message: `Cursor opened the project, but CCE has not confirmed that workspace ${projectPath} is ready. Initialization stopped safely to avoid searching the wrong project.`
634
634
  };
635
635
  }
636
636
  targetId2 = openedTarget2.id;
@@ -647,7 +647,7 @@ async function ensureCursorRunningLocal(options = {}) {
647
647
  windowGuard: windowGuard2,
648
648
  targetId: targetId2,
649
649
  workspaceAction,
650
- message: workspaceAction === "reused-agents-window" ? `CDP ${CDP_PORT} \u5DF2\u54CD\u5E94\u4E14\u662F Cursor\uFF1B\u5DF2\u590D\u7528 Agents Window\uFF08${targetId2}\uFF09\uFF0C\u4E0D\u4F1A\u518D\u6253\u5F00 IDE \u65B0\u7A97\u53E3\u3002` : `CDP ${CDP_PORT} \u5DF2\u54CD\u5E94\u4E14\u662F Cursor\uFF1B\u76EE\u6807\u5DE5\u4F5C\u533A\u5DF2\u7ED1\u5B9A\u5230 CDP target ${targetId2 || "default"}\u3002`
650
+ message: workspaceAction === "reused-agents-window" ? `CDP ${CDP_PORT} responded as Cursor; Agents Window ${targetId2} was reused without opening another IDE window.` : `CDP ${CDP_PORT} responded as Cursor; the target workspace is bound to CDP target ${targetId2 || "default"}.`
651
651
  };
652
652
  }
653
653
  return {
@@ -656,8 +656,8 @@ async function ensureCursorRunningLocal(options = {}) {
656
656
  port: CDP_PORT,
657
657
  needsAction: "free_cce_port",
658
658
  retryable: true,
659
- nextStep: `\u672C\u673A\u7AEF\u53E3 ${CDP_PORT} \u6B63\u88AB\u5176\u4ED6\u7A0B\u5E8F\u4F7F\u7528\u3002\u5173\u95ED\u5360\u7528\u5B83\u7684\u7A0B\u5E8F\u540E\uFF0C\u91CD\u65B0\u6267\u884C\u540C\u4E00\u53E5\u521D\u59CB\u5316\u547D\u4EE4\u3002`,
660
- message: `CCE \u73B0\u5728\u65E0\u6CD5\u8FDE\u63A5 Cursor\uFF0C\u56E0\u4E3A\u6240\u9700\u7684\u672C\u673A\u7AEF\u53E3 ${CDP_PORT} \u6B63\u88AB\u5176\u4ED6\u7A0B\u5E8F\u5360\u7528\u3002`
659
+ nextStep: `Local port ${CDP_PORT} is in use by another program. Close that program, then run the same initialization command again.`,
660
+ message: `CCE cannot connect to Cursor because required local port ${CDP_PORT} is occupied by another program.`
661
661
  };
662
662
  }
663
663
  if (cursorRunningImpl()) {
@@ -670,8 +670,8 @@ async function ensureCursorRunningLocal(options = {}) {
670
670
  cursorExecutableSource: cursorExecutable2 && cursorExecutable2.source || null,
671
671
  needsAction: "close_cursor_and_retry",
672
672
  retryable: true,
673
- nextStep: projectPath ? `\u4FDD\u5B58\u624B\u5934\u5185\u5BB9\u5E76\u6B63\u5E38\u9000\u51FA Cursor \u4E00\u6B21\uFF0C\u7136\u540E\u518D\u6B21\u8BF4\u201C\u521D\u59CB\u5316 CCE \u5DE5\u4F5C\u533A\u4E3A ${projectPath}\u201D\u3002` : "\u4FDD\u5B58\u624B\u5934\u5185\u5BB9\u5E76\u6B63\u5E38\u9000\u51FA Cursor \u4E00\u6B21\uFF0C\u7136\u540E\u91CD\u8BD5\u521A\u624D\u7684 CCE \u64CD\u4F5C\u3002",
674
- message: "Cursor \u5DF2\u7ECF\u63D0\u524D\u6253\u5F00\uFF0CCCE \u65E0\u6CD5\u5728\u8FD0\u884C\u4E2D\u4E3A\u5B83\u8865\u4E0A\u8FDE\u63A5\u80FD\u529B\u3002\u4E3A\u4FDD\u62A4\u672A\u4FDD\u5B58\u5185\u5BB9\uFF0CCursor Bridge \u4E0D\u4F1A\u5F3A\u5236\u5173\u95ED\u5B83\u3002"
673
+ nextStep: projectPath ? `Save your work, exit Cursor normally once, then initialize CCE for workspace ${projectPath} again.` : "Save your work, exit Cursor normally once, then retry the previous CCE operation.",
674
+ message: "Cursor was already running, so CCE cannot add the required connection capability in place. Cursor Bridge will not force-close it, protecting unsaved work."
675
675
  };
676
676
  }
677
677
  const cursorExecutable = findCursorExeDetailsImpl();
@@ -683,8 +683,8 @@ async function ensureCursorRunningLocal(options = {}) {
683
683
  port: CDP_PORT,
684
684
  needsAction: "install_or_locate_cursor",
685
685
  retryable: true,
686
- nextStep: "\u8BF7\u5148\u5B89\u88C5\u5E76\u767B\u5F55 Cursor\u3002\u82E5\u4F7F\u7528\u4FBF\u643A\u7248\u6216\u81EA\u5B9A\u4E49\u76EE\u5F55\uFF0C\u8BF7\u8BBE\u7F6E CURSOR_EXE \u540E\u91CD\u65B0\u6267\u884C\u540C\u4E00\u53E5\u521D\u59CB\u5316\u547D\u4EE4\u3002",
687
- message: "\u6CA1\u6709\u627E\u5230 Cursor\u3002\u6807\u51C6 Windows \u4E0E macOS \u5B89\u88C5\u4F1A\u81EA\u52A8\u8BC6\u522B\uFF0C\u901A\u5E38\u4E0D\u9700\u8981\u586B\u5199\u7A0B\u5E8F\u8DEF\u5F84\u3002"
686
+ nextStep: "Install and sign in to Cursor first. For a portable or custom installation, set CURSOR_EXE and run the same initialization command again.",
687
+ message: "Cursor was not found. Standard Windows and macOS installations are detected automatically and normally do not require an explicit executable path."
688
688
  };
689
689
  }
690
690
  const launchPort = resolveCursorLaunchCdpPort(CDP_PORT);
@@ -719,8 +719,8 @@ async function ensureCursorRunningLocal(options = {}) {
719
719
  cursorExecutableSource: cursorExecutable.source,
720
720
  needsAction: "retry_initialization",
721
721
  retryable: true,
722
- nextStep: "\u8BF7\u7A0D\u7B49\u7247\u523B\uFF0C\u7136\u540E\u91CD\u65B0\u6267\u884C\u540C\u4E00\u53E5\u521D\u59CB\u5316\u547D\u4EE4\u3002",
723
- message: "Cursor \u5DF2\u7ECF\u542F\u52A8\uFF0C\u4F46 CCE \u8FD8\u6CA1\u51C6\u5907\u597D\uFF1B\u65E0\u9700\u4FEE\u6539\u4EFB\u4F55\u7AEF\u53E3\u8BBE\u7F6E\u3002"
722
+ nextStep: "Wait a moment, then run the same initialization command again.",
723
+ message: "Cursor started, but CCE is not ready yet. No port setting needs to be changed."
724
724
  };
725
725
  }
726
726
  const cursorPid = findCursorPidByPort(CDP_PORT) || child.pid || null;
@@ -740,13 +740,13 @@ async function ensureCursorRunningLocal(options = {}) {
740
740
  cursorExecutableSource: cursorExecutable.source,
741
741
  needsAction: "retry_initialization",
742
742
  retryable: true,
743
- nextStep: "\u8BF7\u7B49\u5F85 Cursor \u5B8C\u6210\u6253\u5F00\u9879\u76EE\uFF0C\u7136\u540E\u91CD\u65B0\u6267\u884C\u540C\u4E00\u53E5\u521D\u59CB\u5316\u547D\u4EE4\u3002",
744
- message: `Cursor \u5DF2\u542F\u52A8\uFF0C\u4F46 CCE \u8FD8\u6CA1\u6709\u786E\u8BA4\u5DE5\u4F5C\u533A ${projectPath} \u5DF2\u51C6\u5907\u597D\uFF1B\u4E3A\u907F\u514D\u641C\u7D22\u9519\u9879\u76EE\uFF0C\u672C\u6B21\u521D\u59CB\u5316\u5DF2\u5B89\u5168\u505C\u6B62\u3002`
743
+ nextStep: "Wait for Cursor to finish opening the project, then run the same initialization command again.",
744
+ message: `Cursor started, but CCE has not confirmed that workspace ${projectPath} is ready. Initialization stopped safely to avoid searching the wrong project.`
745
745
  };
746
746
  }
747
747
  if (projectPath && targetId) PROJECT_TARGETS.set(normalizeProjectKey(projectPath), targetId);
748
748
  const windowGuard = effectiveRuntimeMode === "minimal" && cursorPid ? startMinimalWindowGuard(cursorPid) : null;
749
- const target = projectPath ? `\u6253\u5F00 ${projectPath}` : "\u6062\u590D\u4E0A\u6B21\u5DE5\u4F5C\u533A";
749
+ const target = projectPath ? `opening ${projectPath}` : "restoring the previous workspace";
750
750
  const presentation = effectiveRuntimeMode === "minimal" ? setCursorWindowPresentation({ action: "hide", port: CDP_PORT, pid: cursorPid }) : null;
751
751
  return {
752
752
  ok: true,
@@ -763,7 +763,7 @@ async function ensureCursorRunningLocal(options = {}) {
763
763
  cursorExecutableSource: cursorExecutable.source,
764
764
  targetId,
765
765
  workspaceAction: projectPath ? "launched-project" : "launched-last-workspace",
766
- message: `\u5DF2\u542F\u52A8 Cursor\uFF08${exe}\uFF0C${target}\uFF09\uFF0CCDP ${CDP_PORT} \u5C31\u7EEA\u3002`
766
+ message: `Cursor started (${exe}, ${target}); CDP ${CDP_PORT} is ready.`
767
767
  };
768
768
  }
769
769
  function normalizeProjectKey(projectPath) {
@@ -1,28 +1,28 @@
1
- // @ts-nocheck
2
- import { dirname, join, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { createStdioMcpExtension } from "./mcp-stdio.ts";
5
-
6
- const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
7
- const hostCwd = resolve(process.cwd());
8
- const hostWorkspaceId = hostCwd.replace(/\\/g, "/").toLowerCase();
9
-
10
- export default createStdioMcpExtension({
11
- label: "Cursor Bridge",
12
- clientName: "pi-cursor-bridge",
13
- packageVersion: "0.1.2",
14
- serverName: "cursor-bridge",
15
- serverScript: join(packageRoot, "dist", "cursor-bridge.mjs"),
16
- cwd: hostCwd,
17
- env: {
18
- // A Pi process can be launched from Codex or Claude Code during testing.
19
- // Do not let inherited host variables steal Pi's workspace identity.
20
- CODEX_THREAD_ID: undefined,
21
- CLAUDE_PROJECT_DIR: undefined,
22
- CLAUDE_CODE_PROJECT_DIR: undefined,
23
- CLAUDE_CODE_SESSION_ID: undefined,
24
- CLAUDE_SESSION_ID: undefined,
25
- CURSOR_BRIDGE_HOST_ID: `pi:${hostWorkspaceId}`,
26
- CURSOR_PROJECT_PATH: hostCwd,
27
- },
28
- });
1
+ // @ts-nocheck
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { createStdioMcpExtension } from "./mcp-stdio.ts";
5
+
6
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
7
+ const hostCwd = resolve(process.cwd());
8
+ const hostWorkspaceId = hostCwd.replace(/\\/g, "/").toLowerCase();
9
+
10
+ export default createStdioMcpExtension({
11
+ label: "Cursor Bridge",
12
+ clientName: "pi-cursor-bridge",
13
+ packageVersion: "0.1.3",
14
+ serverName: "cursor-bridge",
15
+ serverScript: join(packageRoot, "dist", "cursor-bridge.mjs"),
16
+ cwd: hostCwd,
17
+ env: {
18
+ // A Pi process can be launched from Codex or Claude Code during testing.
19
+ // Do not let inherited host variables steal Pi's workspace identity.
20
+ CODEX_THREAD_ID: undefined,
21
+ CLAUDE_PROJECT_DIR: undefined,
22
+ CLAUDE_CODE_PROJECT_DIR: undefined,
23
+ CLAUDE_CODE_SESSION_ID: undefined,
24
+ CLAUDE_SESSION_ID: undefined,
25
+ CURSOR_BRIDGE_HOST_ID: `pi:${hostWorkspaceId}`,
26
+ CURSOR_PROJECT_PATH: hostCwd,
27
+ },
28
+ });
@@ -1,92 +1,92 @@
1
- // @ts-nocheck
2
- import { existsSync } from "node:fs";
3
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
- import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5
-
6
- const DEFAULT_TOOL_TIMEOUT_MS = 15 * 60 * 1000;
7
-
8
- function stringEnvironment(extra = {}) {
9
- return Object.fromEntries(
10
- Object.entries({ ...process.env, ...extra })
11
- .filter(([, value]) => typeof value === "string"),
12
- );
13
- }
14
-
15
- function toolContent(result) {
16
- const items = Array.isArray(result?.content) ? result.content : [];
17
- if (items.length === 0) {
18
- return [{ type: "text", text: JSON.stringify(result ?? null) }];
19
- }
20
- return items.map((item) => {
21
- if (item?.type === "text" && typeof item.text === "string") {
22
- return { type: "text", text: item.text };
23
- }
24
- return { type: "text", text: JSON.stringify(item) };
25
- });
26
- }
27
-
28
- export function createStdioMcpExtension(options) {
29
- return async function registerStdioMcp(pi) {
30
- if (!existsSync(options.serverScript)) {
31
- throw new Error(`${options.label} MCP entrypoint is missing: ${options.serverScript}`);
32
- }
33
-
34
- const client = new Client(
35
- { name: options.clientName, version: options.packageVersion },
36
- { capabilities: {} },
37
- );
38
- const toolTimeoutMs = Number.isInteger(options.toolTimeoutMs) && options.toolTimeoutMs > 0
39
- ? options.toolTimeoutMs
40
- : DEFAULT_TOOL_TIMEOUT_MS;
41
- const transport = new StdioClientTransport({
42
- command: options.nodeCommand || "node",
43
- args: [options.serverScript],
44
- cwd: options.cwd,
45
- env: stringEnvironment(options.env),
46
- stderr: "pipe",
47
- });
48
-
49
- try {
50
- await client.connect(transport);
51
- const listed = await client.listTools();
52
- for (const tool of listed.tools || []) {
53
- if (!tool?.name || !tool?.inputSchema) continue;
54
- pi.registerTool({
55
- name: tool.name,
56
- label: tool.title || tool.name,
57
- description: tool.description || `${options.label} MCP tool ${tool.name}`,
58
- parameters: tool.inputSchema,
59
- async execute(_toolCallId, params, signal) {
60
- const result = await client.callTool(
61
- { name: tool.name, arguments: params || {} },
62
- undefined,
63
- {
64
- signal,
65
- timeout: toolTimeoutMs,
66
- maxTotalTimeout: toolTimeoutMs,
67
- },
68
- );
69
- return {
70
- content: toolContent(result),
71
- details: {
72
- mcpServer: options.serverName,
73
- mcpTool: tool.name,
74
- isError: result?.isError === true,
75
- },
76
- };
77
- },
78
- });
79
- }
80
- } catch (error) {
81
- await client.close().catch(() => {});
82
- throw new Error(`${options.label} MCP startup failed: ${error instanceof Error ? error.message : String(error)}`);
83
- }
84
-
85
- let closed = false;
86
- pi.on("session_shutdown", async () => {
87
- if (closed) return;
88
- closed = true;
89
- await client.close().catch(() => {});
90
- });
91
- };
92
- }
1
+ // @ts-nocheck
2
+ import { existsSync } from "node:fs";
3
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
5
+
6
+ const DEFAULT_TOOL_TIMEOUT_MS = 15 * 60 * 1000;
7
+
8
+ function stringEnvironment(extra = {}) {
9
+ return Object.fromEntries(
10
+ Object.entries({ ...process.env, ...extra })
11
+ .filter(([, value]) => typeof value === "string"),
12
+ );
13
+ }
14
+
15
+ function toolContent(result) {
16
+ const items = Array.isArray(result?.content) ? result.content : [];
17
+ if (items.length === 0) {
18
+ return [{ type: "text", text: JSON.stringify(result ?? null) }];
19
+ }
20
+ return items.map((item) => {
21
+ if (item?.type === "text" && typeof item.text === "string") {
22
+ return { type: "text", text: item.text };
23
+ }
24
+ return { type: "text", text: JSON.stringify(item) };
25
+ });
26
+ }
27
+
28
+ export function createStdioMcpExtension(options) {
29
+ return async function registerStdioMcp(pi) {
30
+ if (!existsSync(options.serverScript)) {
31
+ throw new Error(`${options.label} MCP entrypoint is missing: ${options.serverScript}`);
32
+ }
33
+
34
+ const client = new Client(
35
+ { name: options.clientName, version: options.packageVersion },
36
+ { capabilities: {} },
37
+ );
38
+ const toolTimeoutMs = Number.isInteger(options.toolTimeoutMs) && options.toolTimeoutMs > 0
39
+ ? options.toolTimeoutMs
40
+ : DEFAULT_TOOL_TIMEOUT_MS;
41
+ const transport = new StdioClientTransport({
42
+ command: options.nodeCommand || "node",
43
+ args: [options.serverScript],
44
+ cwd: options.cwd,
45
+ env: stringEnvironment(options.env),
46
+ stderr: "pipe",
47
+ });
48
+
49
+ try {
50
+ await client.connect(transport);
51
+ const listed = await client.listTools();
52
+ for (const tool of listed.tools || []) {
53
+ if (!tool?.name || !tool?.inputSchema) continue;
54
+ pi.registerTool({
55
+ name: tool.name,
56
+ label: tool.title || tool.name,
57
+ description: tool.description || `${options.label} MCP tool ${tool.name}`,
58
+ parameters: tool.inputSchema,
59
+ async execute(_toolCallId, params, signal) {
60
+ const result = await client.callTool(
61
+ { name: tool.name, arguments: params || {} },
62
+ undefined,
63
+ {
64
+ signal,
65
+ timeout: toolTimeoutMs,
66
+ maxTotalTimeout: toolTimeoutMs,
67
+ },
68
+ );
69
+ return {
70
+ content: toolContent(result),
71
+ details: {
72
+ mcpServer: options.serverName,
73
+ mcpTool: tool.name,
74
+ isError: result?.isError === true,
75
+ },
76
+ };
77
+ },
78
+ });
79
+ }
80
+ } catch (error) {
81
+ await client.close().catch(() => {});
82
+ throw new Error(`${options.label} MCP startup failed: ${error instanceof Error ? error.message : String(error)}`);
83
+ }
84
+
85
+ let closed = false;
86
+ pi.on("session_shutdown", async () => {
87
+ if (closed) return;
88
+ closed = true;
89
+ await client.close().catch(() => {});
90
+ });
91
+ };
92
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cursor-bridge",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Use Cursor Context Engine and bounded 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": "5.4.2"
50
+ "embeddedProductVersion": "5.5.0"
51
51
  }
52
52
  }
@@ -37,6 +37,7 @@ Do not submit the same lookup to CCE and another semantic system in parallel. Us
37
37
  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.
38
38
 
39
39
  - Describe the relationship or behavior to establish and the evidence needed.
40
+ - Preserve the language of the user's current substantive request unless the user explicitly asks for another language. Do not infer or persist a different language from the operating system when the conversation already provides a clear signal.
40
41
  - Do not prescribe Cursor's internal search sequence, harness, Explore usage, or number of files.
41
42
  - Do not invent hidden parameters; the public input is only `query`.
42
43
  - Allow a cold or large workspace enough time to complete its serialized Cursor UI turn.
@@ -50,4 +51,6 @@ Treat CCE output as evidence leads, not final authority.
50
51
  3. If CCE returns `NOT_FOUND` or names gaps, report those gaps or perform one bounded fallback search; do not guess from framework convention.
51
52
  4. Keep edits, final review, tests, and acceptance with the primary agent unless a separate bounded delegation is appropriate.
52
53
 
54
+ Explain the result in the language of the user's current task unless an explicit language override applies. Preserve `CCE_SEARCH_RESULT`, field names, enum values, `NOT_FOUND`, paths, symbols, line anchors, hashes, IDs, commands, and error/status codes verbatim. When a result was authored in another host task or language, keep the evidence unchanged and explain it in the current task language rather than rewriting the artifact.
55
+
53
56
  CCE is strongly prompted and audited for read-only investigation, but it is not a filesystem sandbox. Preserve user changes and normal workspace safety boundaries.
@@ -56,11 +56,11 @@ For an exact known-file or known-symbol lookup, establish the cheapest determini
56
56
 
57
57
  Do not choose parallel execution merely because there are many tasks. When dependency or path relationships are unclear, use `fifo`.
58
58
 
59
- ## Dispatch a task
60
-
61
- 1. Record the relevant pre-dispatch workspace state so later review can distinguish existing user changes.
62
- 2. Form one independent task envelope per task using [delegation-contract.md](references/delegation-contract.md).
63
- 3. Call `cursor_do` with `background=true`; do not invent a chat-selection parameter.
59
+ ## Dispatch a task
60
+
61
+ 1. Record the relevant pre-dispatch workspace state so later review can distinguish existing user changes.
62
+ 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.
63
+ 3. Call `cursor_do` with `background=true`; do not invent a chat-selection parameter.
64
64
  4. Save each returned `task_id`; also save `agent_id` whenever `cursor_status` publishes one.
65
65
  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.
66
66
 
@@ -72,9 +72,11 @@ The envelope may contain a small number of local implementation `open_questions`
72
72
  2. Treat `submitting`, `running`, and `collecting` as normal in-progress states. More than two minutes is not itself a failure; wait for an explicit terminal state.
73
73
  3. Compare Cursor's claimed work with the real diff, `allowed_paths`, and acceptance contract.
74
74
  4. Run risk-proportionate verification in the primary agent. Cursor's response alone cannot support a formal pass, verified state, or governance transition.
75
- 5. Record each task as complete, partial, failed, timed out, or ambiguous before summarizing the batch.
76
-
77
- Read [delegation-contract.md](references/delegation-contract.md) for state interpretation and recovery details.
75
+ 5. Record each task as complete, partial, failed, timed out, or ambiguous before summarizing the batch.
76
+
77
+ Report the accepted result in the language of the user's current task. Keep `task_id`, `agent_id`, tool names, states, enum values, paths, commands, hashes, exact permission options, and error/status codes verbatim. If Cursor returned an artifact or report in another language, preserve it and summarize the relevant facts in the current task language.
78
+
79
+ Read [delegation-contract.md](references/delegation-contract.md) for state interpretation and recovery details.
78
80
 
79
81
  ## Handle abnormal states
80
82
 
@@ -15,11 +15,11 @@ Provide every task independently:
15
15
 
16
16
  | Field | Requirement |
17
17
  |---|---|
18
- | `prompt` | State one objective, the necessary context, prohibited actions, and the expected report. Do not ask Cursor to repeat the primary agent's scope decision. |
18
+ | `prompt` | State one objective, the necessary context, prohibited actions, and the expected report. Use the current user-task language unless the user explicitly requests another language. Do not ask Cursor to repeat the primary agent's scope decision. |
19
19
  | `execution` | Use only `fifo` or `parallel_agent`. Use `fifo` when safe parallelism cannot be demonstrated. |
20
20
  | `read_only` | Use `true` for lookup and analysis; use `false` for any file modification. |
21
21
  | `allowed_paths` | Required when `read_only=false`. Provide the smallest workspace-relative path set, with no glob, absolute path, or workspace-escaping `..`. Omit it when `read_only=true`. This is not a filesystem sandbox. |
22
- | `completion_contract` | State the deliverables, validation commands, permitted incomplete items, and final report format. |
22
+ | `completion_contract` | State the deliverables, validation commands, permitted incomplete items, final report format, and that narrative output should follow the task language. Preserve paths, commands, identifiers, and machine tokens verbatim. |
23
23
  | `background` | Default to `true` so the primary agent may continue independent work. |
24
24
 
25
25
  ## Routing contract