cursor-opencode-provider 0.7.1 → 0.7.2

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.
@@ -472,6 +472,8 @@ export function createMessageTypes() {
472
472
  { id: 1, name: "matches", type: "GrepFileMatch", repeated: true },
473
473
  { id: 2, name: "total_lines", type: "int32" },
474
474
  { id: 3, name: "total_matched_lines", type: "int32" },
475
+ { id: 4, name: "client_truncated", type: "bool" },
476
+ { id: 5, name: "ripgrep_truncated", type: "bool" },
475
477
  ]);
476
478
  addType(root, "GrepUnionResult", [
477
479
  { id: 2, name: "files", type: "GrepFilesResult" },
@@ -1049,7 +1049,9 @@ export function parseExecServerMessage(msg, dialect = OPENCODE_1_TOOL_DIALECT) {
1049
1049
  ? shellStreamResultMetadata(rawArgs)
1050
1050
  : execVariant === "read_args" || execVariant === "pi_read_args"
1051
1051
  ? readRequestResultMetadata(rawArgs, mapped.args)
1052
- : undefined;
1052
+ : execVariant === "grep_args"
1053
+ ? grepRequestResultMetadata(rawArgs)
1054
+ : undefined;
1053
1055
  if (resultMetadata
1054
1056
  && resultMetadata.timeout_behavior !== 2
1055
1057
  && typeof resultMetadata.timeout_ms === "number") {
@@ -2063,6 +2065,70 @@ function rewriteShellPathLine(line, root) {
2063
2065
  return resolveToolPath(trimmed, root);
2064
2066
  return line;
2065
2067
  }
2068
+ const OPENCODE_GREP_LINE = /^[ \t]+Line (\d+):[ \t]?(.*)$/;
2069
+ /**
2070
+ * OpenCode 1.x and 2.0 both render grep as `Found N matches`, a `path:` header,
2071
+ * and indented `Line N: text` previews. A page with no previews is not content.
2072
+ */
2073
+ function parseOpenCodeGrepContent(output, workspaceRoot) {
2074
+ const lines = normalizeToolText(output).split("\n");
2075
+ const first = lines[0]?.trim() ?? "";
2076
+ if (!/^Found \d+ matches\b/.test(first))
2077
+ return undefined;
2078
+ const matches = [];
2079
+ let current;
2080
+ let truncated = /\bmore matches available\b/.test(first);
2081
+ let sawLine = false;
2082
+ for (const raw of lines.slice(1)) {
2083
+ if (!raw.trim())
2084
+ continue;
2085
+ const preview = OPENCODE_GREP_LINE.exec(raw);
2086
+ if (preview) {
2087
+ sawLine = true;
2088
+ if (!current)
2089
+ continue;
2090
+ const lineNumber = Number(preview[1]);
2091
+ if (!Number.isSafeInteger(lineNumber) || lineNumber < 1)
2092
+ continue;
2093
+ current.matches.push({ line_number: lineNumber, content: preview[2] ?? "" });
2094
+ continue;
2095
+ }
2096
+ if (raw.trimStart().startsWith("(")) {
2097
+ if (/truncat/i.test(raw))
2098
+ truncated = true;
2099
+ continue;
2100
+ }
2101
+ if (raw.startsWith(" ") || raw.startsWith("\t"))
2102
+ continue;
2103
+ const header = /^(.*):$/.exec(raw.trim());
2104
+ if (!header || !header[1] || header[1].includes("://"))
2105
+ continue;
2106
+ current = { file: resolveToolPath(header[1], workspaceRoot), matches: [] };
2107
+ matches.push(current);
2108
+ }
2109
+ if (!sawLine)
2110
+ return undefined;
2111
+ const withHits = matches.filter((file) => file.matches.length > 0);
2112
+ if (withHits.length === 0)
2113
+ return undefined;
2114
+ return {
2115
+ matches: withHits,
2116
+ truncated,
2117
+ totalMatchedLines: withHits.reduce((count, file) => count + file.matches.length, 0),
2118
+ };
2119
+ }
2120
+ function grepRequestResultMetadata(raw) {
2121
+ const pattern = str(raw.pattern);
2122
+ const outputMode = str(raw.output_mode);
2123
+ const searchPath = str(raw.path);
2124
+ if (!pattern && !outputMode && !searchPath)
2125
+ return undefined;
2126
+ return {
2127
+ ...(pattern ? { pattern } : {}),
2128
+ ...(outputMode ? { output_mode: outputMode } : {}),
2129
+ ...(searchPath ? { path: searchPath } : {}),
2130
+ };
2131
+ }
2066
2132
  function extractGroundedPaths(output, workspaceRoot) {
2067
2133
  const normalized = normalizeToolText(output);
2068
2134
  const directory = parseOpenCode2DirectoryListing(normalized, workspaceRoot);
@@ -2231,21 +2297,50 @@ export function buildTypedExecResult(resultField, output, error, toolName, resul
2231
2297
  case "grep_result": {
2232
2298
  if (error)
2233
2299
  return { error: { error } };
2234
- // Prefer files_with_matches: OpenCode glob/grep often returns path lists.
2235
- // Content-mode GrepSuccess also works but needs GrepFileMatch nesting.
2236
- const files = extractGroundedPaths(output, resultRoot);
2237
2300
  const cwd = resultRoot ?? "";
2301
+ const pattern = str(resultMetadata?.pattern) ?? "";
2302
+ const requestedPath = str(resultMetadata?.path) ?? cwd;
2303
+ const requestedMode = str(resultMetadata?.output_mode);
2304
+ // OpenCode always returns match previews. Cursor's native Grep defaults
2305
+ // to content; encoding that as files_with_matches drops the previews and
2306
+ // the model only sees paths. Keep files_with_matches for an explicit
2307
+ // files-only request, and for path lists (glob remaps, no matches).
2308
+ const parsedContent = parseOpenCodeGrepContent(output, resultRoot);
2309
+ const content = requestedMode === "files_with_matches"
2310
+ ? undefined
2311
+ : parsedContent;
2312
+ if (content) {
2313
+ return {
2314
+ success: {
2315
+ pattern,
2316
+ path: requestedPath,
2317
+ output_mode: "content",
2318
+ workspace_results: {
2319
+ [cwd]: {
2320
+ content: {
2321
+ matches: content.matches,
2322
+ total_lines: content.totalMatchedLines,
2323
+ total_matched_lines: content.totalMatchedLines,
2324
+ client_truncated: content.truncated,
2325
+ ripgrep_truncated: false,
2326
+ },
2327
+ },
2328
+ },
2329
+ },
2330
+ };
2331
+ }
2332
+ const files = extractGroundedPaths(output, resultRoot);
2238
2333
  return {
2239
2334
  success: {
2240
- pattern: "",
2241
- path: cwd,
2335
+ pattern,
2336
+ path: requestedPath,
2242
2337
  output_mode: "files_with_matches",
2243
2338
  workspace_results: {
2244
2339
  [cwd]: {
2245
2340
  files: {
2246
2341
  files,
2247
2342
  total_files: files.length,
2248
- client_truncated: false,
2343
+ client_truncated: parsedContent?.truncated ?? false,
2249
2344
  },
2250
2345
  },
2251
2346
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursor-opencode-provider",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Use Cursor subscription models from OpenCode via Cursor's Connect-RPC agent protocol",
5
5
  "type": "module",
6
6
  "license": "MIT",