@spencer-kit/coder-studio 0.4.3 → 0.4.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.
@@ -931,10 +931,10 @@ function resolveSpawnArgv(argv, deps = {}) {
931
931
  }
932
932
  const restArgs = argv.slice(1);
933
933
  const readFileSync10 = deps.readFileSync ?? ((file) => fs2.readFileSync(file, "utf8"));
934
- const existsSync13 = deps.existsSync ?? fs2.existsSync;
934
+ const existsSync14 = deps.existsSync ?? fs2.existsSync;
935
935
  const pathEnv = deps.pathEnv ?? process.env.Path ?? process.env.PATH ?? "";
936
936
  const pathExt = deps.pathExt ?? process.env.PATHEXT ?? DEFAULT_PATHEXT;
937
- const resolved = resolveExecutablePath(command, pathEnv, pathExt, existsSync13);
937
+ const resolved = resolveExecutablePath(command, pathEnv, pathExt, existsSync14);
938
938
  if (!resolved) {
939
939
  return [...argv];
940
940
  }
@@ -983,17 +983,17 @@ function expandShimVars(value, dp0Dir) {
983
983
  function parsePathExt(pathExt) {
984
984
  return pathExt.split(";").map((entry) => entry.trim().toLowerCase()).filter((entry) => entry.length > 0);
985
985
  }
986
- function resolveExecutablePath(command, pathEnv, pathExt, existsSync13) {
986
+ function resolveExecutablePath(command, pathEnv, pathExt, existsSync14) {
987
987
  const hasExt = path3.win32.extname(command).length > 0;
988
988
  const extensions = parsePathExt(pathExt);
989
989
  if (path3.win32.isAbsolute(command)) {
990
- if (existsSync13(command)) {
990
+ if (existsSync14(command)) {
991
991
  return command;
992
992
  }
993
993
  if (!hasExt) {
994
994
  for (const ext of extensions) {
995
995
  const candidate = command + ext;
996
- if (existsSync13(candidate)) {
996
+ if (existsSync14(candidate)) {
997
997
  return candidate;
998
998
  }
999
999
  }
@@ -1004,14 +1004,14 @@ function resolveExecutablePath(command, pathEnv, pathExt, existsSync13) {
1004
1004
  for (const dir of dirs) {
1005
1005
  if (hasExt) {
1006
1006
  const candidate = path3.win32.join(dir, command);
1007
- if (existsSync13(candidate)) {
1007
+ if (existsSync14(candidate)) {
1008
1008
  return candidate;
1009
1009
  }
1010
1010
  continue;
1011
1011
  }
1012
1012
  for (const ext of extensions) {
1013
1013
  const candidate = path3.win32.join(dir, command + ext);
1014
- if (existsSync13(candidate)) {
1014
+ if (existsSync14(candidate)) {
1015
1015
  return candidate;
1016
1016
  }
1017
1017
  }
@@ -2532,12 +2532,12 @@ async function ensureSafeUploadDir(rootDir, targetDir2) {
2532
2532
  await mkdir3(resolvedRoot, { recursive: true });
2533
2533
  await assertDirectorySegmentSafe(resolvedRoot);
2534
2534
  }
2535
- const relative3 = path7.relative(resolvedRoot, resolvedTarget);
2536
- if (!relative3) {
2535
+ const relative4 = path7.relative(resolvedRoot, resolvedTarget);
2536
+ if (!relative4) {
2537
2537
  return;
2538
2538
  }
2539
2539
  let current = resolvedRoot;
2540
- for (const segment of relative3.split(path7.sep)) {
2540
+ for (const segment of relative4.split(path7.sep)) {
2541
2541
  current = path7.join(current, segment);
2542
2542
  try {
2543
2543
  await assertDirectorySegmentSafe(current);
@@ -8055,7 +8055,7 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
8055
8055
  const arch = deps.arch ?? process.arch;
8056
8056
  const resolve4 = deps.resolve ?? ((id) => require4.resolve(id));
8057
8057
  const fileExists = deps.existsSync ?? existsSync6;
8058
- const stat9 = deps.statSync ?? statSync;
8058
+ const stat10 = deps.statSync ?? statSync;
8059
8059
  const chmod = deps.chmodSync ?? chmodSync2;
8060
8060
  let packageJsonPath;
8061
8061
  try {
@@ -8073,7 +8073,7 @@ function ensureNodePtySpawnHelperExecutable(deps = {}) {
8073
8073
  if (!fileExists(helperPath)) {
8074
8074
  return;
8075
8075
  }
8076
- const currentMode = stat9(helperPath).mode;
8076
+ const currentMode = stat10(helperPath).mode;
8077
8077
  const executableMode = currentMode | 73;
8078
8078
  if (executableMode === currentMode) {
8079
8079
  return;
@@ -8279,13 +8279,19 @@ import { spawn as spawn3 } from "node:child_process";
8279
8279
  function buildPrompt(context, mode) {
8280
8280
  if (mode === "decompose") {
8281
8281
  return [
8282
- "You are an autonomous supervisor for a target-scoped software task.",
8282
+ "You are an autonomous planner-supervisor for this target-scoped software task.",
8283
+ "Your purpose is to drive the work from objective to high-quality delivery with minimal babysitting.",
8284
+ 'Do not optimize for merely reaching "done"; optimize for a result that is correct, verified, coherent, and not obviously low-quality or rushed.',
8283
8285
  "Your first job is to decompose the target into a supervision structure before evaluation begins.",
8284
8286
  "",
8285
8287
  "Return JSON only.",
8286
8288
  "No prose before or after the JSON.",
8287
8289
  "",
8288
8290
  "Decomposition policy:",
8291
+ "- Create an execution plan, not just a task list.",
8292
+ "- Break the objective into the smallest reasonable set of milestones that maximize clarity, reduce uncertainty, and preserve steady forward progress.",
8293
+ "- Order milestones by dependency, risk reduction, and delivery leverage.",
8294
+ "- Prefer a plan structure that makes execution easier, verification clearer, and replanning cheaper.",
8289
8295
  "- Do not ask the user any questions.",
8290
8296
  "- Do not ask for clarification, confirmation, or approval.",
8291
8297
  "- Do not propose options for the user to choose from.",
@@ -8299,6 +8305,30 @@ function buildPrompt(context, mode) {
8299
8305
  "- Each item must be concrete, milestone-sized, and useful for subsequent evaluation.",
8300
8306
  "- Do not leave the structure empty.",
8301
8307
  "",
8308
+ "Decomposition principles:",
8309
+ "- Prefer milestones that produce a concrete artifact, observable behavior change, test result, or verification result.",
8310
+ "- Make dependencies explicit.",
8311
+ "- Separate implementation, verification, integration, and cleanup when that improves delivery reliability.",
8312
+ "- If a step is too vague to verify independently, split it further.",
8313
+ "- Prefer plans that keep the agent moving with minimal ambiguity between milestones.",
8314
+ "- Use stage-based planning by default unless there are clearly independent deliverables that justify subtargets.",
8315
+ "- Build the plan so it can recover from failed attempts: prefer decompositions that allow narrowing scope, isolating failures, checking assumptions, and restoring a working baseline when needed.",
8316
+ "- Keep the decomposition practical for execution, not merely neat on paper.",
8317
+ "",
8318
+ "Planning quality bar:",
8319
+ "- Prefer fewer, stronger milestones over many thin or vague ones.",
8320
+ "- Every item should imply a concrete deliverable and observable acceptance criteria.",
8321
+ '- Avoid vague items such as "improve", "clean up", or "refactor" unless tied to a specific delivery or verification target.',
8322
+ "- Include quality and verification checkpoints where they materially improve the final result.",
8323
+ "- Do not decompose in a way that encourages superficial completion.",
8324
+ "",
8325
+ "Planning boundary:",
8326
+ "- You are responsible for execution structure, sequencing, quality control, and verification structure.",
8327
+ "- Do not hard-code unnecessary implementation detail too early.",
8328
+ "- If multiple implementation paths exist, prefer a plan that keeps execution adaptable until evidence makes one path clearly better.",
8329
+ "- Do not hide assumptions inside the plan.",
8330
+ "- Do not create a brittle plan that depends on perfect execution.",
8331
+ "",
8302
8332
  "Item requirements:",
8303
8333
  '- Each item must include "id", "kind", "title", "objective", "deliverable", "acceptanceCriteria", and "status".',
8304
8334
  '- "kind" must match the selected decompositionMode: all "stage" or all "subtarget".',
@@ -8331,8 +8361,13 @@ function buildPrompt(context, mode) {
8331
8361
  ].join("\n");
8332
8362
  }
8333
8363
  const lines = [
8334
- "You are an autonomous supervisor for a target-scoped software task.",
8335
- "Your job is to keep the agent moving toward the objective until the objective is complete.",
8364
+ "You are an autonomous planner-supervisor for this target-scoped software task.",
8365
+ "Your purpose is to drive the work from objective to high-quality delivery with minimal babysitting.",
8366
+ 'Do not optimize for merely reaching "done"; optimize for a result that is correct, verified, coherent, and not obviously low-quality or rushed.',
8367
+ "Act as an autonomous execution supervisor.",
8368
+ "Your job is to keep the agent moving toward the objective, maintain delivery quality, detect low-yield paths early, and redirect work when needed.",
8369
+ "Do not passively observe progress; actively steer it toward successful, high-quality completion.",
8370
+ "Drive execution through the supervised agent rather than by independently performing the work yourself.",
8336
8371
  "",
8337
8372
  "Return JSON only.",
8338
8373
  "No prose before or after the JSON.",
@@ -8363,7 +8398,12 @@ function buildPrompt(context, mode) {
8363
8398
  '- When advancing to the next item, mark the previous item as "done" and set activeItemId to the next item explicitly.',
8364
8399
  "- If the active item is blocked, give guidance that is most likely to unblock it.",
8365
8400
  "- If the active item is obsolete, explain the reason briefly and move to the next useful item.",
8366
- "- If the agent appears stuck or repeated the same action, give a different concrete next action.",
8401
+ "- If the current path is low-yield, brittle, repetitive, or producing low-quality output, redirect early.",
8402
+ "- Diagnose stalls precisely: implementation failure, verification failure, environment failure, scope misframing, weak solution quality, or missing evidence.",
8403
+ "- Choose the next action that most improves objective-level progress, not merely the most local continuation.",
8404
+ "- Do not repeat the same tactic after failure unless new evidence justifies retrying it.",
8405
+ "- Maintain commitment to the objective, not blind commitment to the current tactic.",
8406
+ "- Replan locally when needed, but keep the overall execution coherent and objective-driven.",
8367
8407
  "- Do not rewrite the decomposition structure during normal evaluation cycles.",
8368
8408
  "",
8369
8409
  "Allowed statuses:",
@@ -8388,6 +8428,21 @@ function buildPrompt(context, mode) {
8388
8428
  "- If implementation is needed, point to the likely area, behavior, or file/module based on available evidence.",
8389
8429
  "- If the agent asked a question, answer it directly in the guidance and continue with a concrete next action.",
8390
8430
  "",
8431
+ "Delivery quality bar:",
8432
+ "- Do not accept shallow, brittle, or obviously rushed solutions.",
8433
+ "- Do not optimize for the smallest change if it leads to poor maintainability, weak verification, or fragile behavior.",
8434
+ "- Prefer solutions that are robust, coherent with the existing codebase, and likely to hold up beyond the happy path.",
8435
+ "- Require appropriate verification for the kind of work being done.",
8436
+ "- Consider edge cases, integration impact, regressions, and maintainability where relevant.",
8437
+ "- If a solution technically works but is low-quality, incomplete, poorly verified, or obviously a shortcut, treat the milestone as not yet complete.",
8438
+ "- Do not let superficial progress masquerade as real delivery.",
8439
+ "",
8440
+ "Completion standard:",
8441
+ "- A milestone is complete only when its deliverable and acceptanceCriteria are supported by observable evidence and the result meets a reasonable quality bar.",
8442
+ "- The objective is complete only when the final result is implemented, verified, and not obviously compromised in quality.",
8443
+ "- Do not mark work complete merely because code was changed, a command passed once, or a minimal patch exists.",
8444
+ "- Optimize for finished, verified, and defensible delivery.",
8445
+ "",
8391
8446
  "Evaluation policy:",
8392
8447
  "- Update progress incrementally against the existing decomposition.",
8393
8448
  "- Use itemUpdates to reflect evidence-backed status changes only.",
@@ -14872,20 +14927,260 @@ var init_session2 = __esm({
14872
14927
  }
14873
14928
  });
14874
14929
 
14930
+ // packages/server/src/fs/content-search.ts
14931
+ import { spawn as spawn5 } from "child_process";
14932
+ import { existsSync as existsSync8 } from "fs";
14933
+ import { readdir as readdir4, readFile as readFile4, stat as stat8 } from "fs/promises";
14934
+ import { basename as basename2, join as join10, relative as relative2 } from "path";
14935
+ import { createInterface } from "readline";
14936
+ async function searchFileContents(rootPath, options) {
14937
+ const query = options.query.trim();
14938
+ if (!query) {
14939
+ return {
14940
+ files: [],
14941
+ totalMatchCount: 0,
14942
+ hasMoreFiles: false,
14943
+ truncatedMatchFileCount: 0
14944
+ };
14945
+ }
14946
+ const result = await searchWithRipgrep(rootPath, query, options.maxFiles).catch(
14947
+ async (error) => {
14948
+ if (error.code === "ENOENT") {
14949
+ return searchWithNode(rootPath, query, options.maxFiles);
14950
+ }
14951
+ throw error;
14952
+ }
14953
+ );
14954
+ return finalizeResults(result, options.maxFiles, options.maxMatchesPerFile);
14955
+ }
14956
+ async function searchWithRipgrep(rootPath, query, maxFiles) {
14957
+ const hasGitignore = existsSync8(join10(rootPath, ".gitignore"));
14958
+ const args = [
14959
+ "--json",
14960
+ "--line-number",
14961
+ "--column",
14962
+ "--fixed-strings",
14963
+ "--sort",
14964
+ "path",
14965
+ "--with-filename",
14966
+ "--glob",
14967
+ "!**/.git/**",
14968
+ "--glob",
14969
+ "!**/node_modules/**"
14970
+ ];
14971
+ if (hasGitignore) {
14972
+ args.push("--hidden");
14973
+ args.push("--no-require-git");
14974
+ }
14975
+ args.push(query, ".");
14976
+ return new Promise((resolve4, reject) => {
14977
+ const child = spawn5("rg", args, { cwd: rootPath, stdio: ["ignore", "pipe", "pipe"] });
14978
+ const stdout = createInterface({ input: child.stdout });
14979
+ const files = /* @__PURE__ */ new Map();
14980
+ let totalMatchCount = 0;
14981
+ let hasMoreFiles = false;
14982
+ let stderr = "";
14983
+ stdout.on("line", (line) => {
14984
+ if (!line.trim()) {
14985
+ return;
14986
+ }
14987
+ const event = JSON.parse(line);
14988
+ if (event.type !== "match") {
14989
+ return;
14990
+ }
14991
+ const rawPath = event.data?.path?.text;
14992
+ if (!rawPath) {
14993
+ return;
14994
+ }
14995
+ const relativePath = normalizeRelativePath(relative2(rootPath, join10(rootPath, rawPath)));
14996
+ const preview = (event.data?.lines?.text ?? "").replace(/\r?\n$/, "");
14997
+ const lineNumber = event.data?.line_number ?? 1;
14998
+ const submatches = event.data?.submatches ?? [];
14999
+ totalMatchCount += submatches.length;
15000
+ if (!files.has(relativePath) && files.size >= maxFiles) {
15001
+ hasMoreFiles = true;
15002
+ return;
15003
+ }
15004
+ for (const submatch of submatches) {
15005
+ pushMatch(files, relativePath, {
15006
+ line: lineNumber,
15007
+ column: byteOffsetToColumn(preview, submatch.start),
15008
+ endColumn: byteOffsetToColumn(preview, submatch.end),
15009
+ preview,
15010
+ previewColumnStart: byteOffsetToColumn(preview, submatch.start),
15011
+ previewColumnEnd: byteOffsetToColumn(preview, submatch.end)
15012
+ });
15013
+ }
15014
+ });
15015
+ child.stderr.on("data", (chunk) => {
15016
+ stderr += chunk.toString();
15017
+ });
15018
+ child.on("error", (error) => {
15019
+ void stdout.close();
15020
+ reject(error);
15021
+ });
15022
+ child.on("close", (code) => {
15023
+ void stdout.close();
15024
+ if (code === 0 || code === 1) {
15025
+ resolve4({
15026
+ files: sortAccumulators(files),
15027
+ totalMatchCount,
15028
+ hasMoreFiles
15029
+ });
15030
+ return;
15031
+ }
15032
+ reject(
15033
+ Object.assign(new Error(stderr || `rg exited with code ${code ?? "unknown"}`), { code })
15034
+ );
15035
+ });
15036
+ });
15037
+ }
15038
+ async function searchWithNode(rootPath, query, maxFiles) {
15039
+ const files = /* @__PURE__ */ new Map();
15040
+ let totalMatchCount = 0;
15041
+ let hasMoreFiles = false;
15042
+ async function walk(dirPath) {
15043
+ const filter = createGitignoreFilter(rootPath, dirPath);
15044
+ const entries = await readdir4(dirPath, { withFileTypes: true });
15045
+ const filteredEntries = entries.filter((entry) => filter(entry.name));
15046
+ filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
15047
+ for (const entry of filteredEntries) {
15048
+ const fullPath = join10(dirPath, entry.name);
15049
+ if (entry.isDirectory()) {
15050
+ await walk(fullPath);
15051
+ continue;
15052
+ }
15053
+ if (!entry.isFile()) {
15054
+ continue;
15055
+ }
15056
+ const fileStat = await stat8(fullPath);
15057
+ if (fileStat.size > FALLBACK_MAX_FILE_BYTES) {
15058
+ continue;
15059
+ }
15060
+ const buffer = await readFile4(fullPath);
15061
+ if (isBinaryFile(buffer)) {
15062
+ continue;
15063
+ }
15064
+ const relativePath = normalizeRelativePath(relative2(rootPath, fullPath));
15065
+ const file = collectMatchesFromText(relativePath, buffer.toString("utf-8"), query);
15066
+ if (!file) {
15067
+ continue;
15068
+ }
15069
+ totalMatchCount += file.matchCount;
15070
+ if (files.size >= maxFiles) {
15071
+ hasMoreFiles = true;
15072
+ continue;
15073
+ }
15074
+ files.set(relativePath, file);
15075
+ }
15076
+ }
15077
+ await walk(rootPath);
15078
+ return {
15079
+ files: sortAccumulators(files),
15080
+ totalMatchCount,
15081
+ hasMoreFiles
15082
+ };
15083
+ }
15084
+ function collectMatchesFromText(relativePath, content, query) {
15085
+ const file = {
15086
+ path: relativePath,
15087
+ name: basename2(relativePath),
15088
+ matches: [],
15089
+ matchCount: 0
15090
+ };
15091
+ const lines = content.split(/\r?\n/);
15092
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
15093
+ const preview = lines[lineIndex] ?? "";
15094
+ if (!preview) {
15095
+ continue;
15096
+ }
15097
+ let fromIndex = 0;
15098
+ while (fromIndex <= preview.length) {
15099
+ const matchIndex = preview.indexOf(query, fromIndex);
15100
+ if (matchIndex === -1) {
15101
+ break;
15102
+ }
15103
+ const startColumn = matchIndex + 1;
15104
+ const endColumn = startColumn + query.length;
15105
+ file.matches.push({
15106
+ line: lineIndex + 1,
15107
+ column: startColumn,
15108
+ endColumn,
15109
+ preview,
15110
+ previewColumnStart: startColumn,
15111
+ previewColumnEnd: endColumn
15112
+ });
15113
+ file.matchCount += 1;
15114
+ fromIndex = matchIndex + Math.max(query.length, 1);
15115
+ }
15116
+ }
15117
+ return file.matchCount > 0 ? file : null;
15118
+ }
15119
+ function pushMatch(files, relativePath, match) {
15120
+ let file = files.get(relativePath);
15121
+ if (!file) {
15122
+ file = {
15123
+ path: relativePath,
15124
+ name: basename2(relativePath),
15125
+ matches: [],
15126
+ matchCount: 0
15127
+ };
15128
+ files.set(relativePath, file);
15129
+ }
15130
+ file.matches.push(match);
15131
+ file.matchCount += 1;
15132
+ }
15133
+ function sortAccumulators(files) {
15134
+ return Array.from(files.values()).sort((a, b) => a.path.localeCompare(b.path));
15135
+ }
15136
+ function finalizeResults(result, maxFiles, maxMatchesPerFile) {
15137
+ const visibleFiles = result.files.slice(0, maxFiles).map((file) => ({
15138
+ path: file.path,
15139
+ name: file.name,
15140
+ matchCount: file.matchCount,
15141
+ hasMoreMatches: file.matchCount > maxMatchesPerFile,
15142
+ matches: file.matches.slice(0, maxMatchesPerFile)
15143
+ }));
15144
+ return {
15145
+ files: visibleFiles,
15146
+ totalMatchCount: result.totalMatchCount,
15147
+ hasMoreFiles: result.hasMoreFiles || result.files.length > maxFiles,
15148
+ truncatedMatchFileCount: visibleFiles.filter((file) => file.hasMoreMatches).length
15149
+ };
15150
+ }
15151
+ function normalizeRelativePath(path14) {
15152
+ return path14.replace(/\\/g, "/");
15153
+ }
15154
+ function byteOffsetToColumn(preview, byteOffset) {
15155
+ return Buffer.from(preview, "utf8").subarray(0, byteOffset).toString("utf8").length + 1;
15156
+ }
15157
+ function isBinaryFile(buffer) {
15158
+ const sample = buffer.subarray(0, 8e3);
15159
+ return sample.includes(0);
15160
+ }
15161
+ var FALLBACK_MAX_FILE_BYTES;
15162
+ var init_content_search = __esm({
15163
+ "packages/server/src/fs/content-search.ts"() {
15164
+ "use strict";
15165
+ init_gitignore();
15166
+ FALLBACK_MAX_FILE_BYTES = 1e6;
15167
+ }
15168
+ });
15169
+
14875
15170
  // packages/server/src/fs/tree.ts
14876
- import { readdir as readdir4, stat as stat8 } from "fs/promises";
14877
- import { join as join10, relative as relative2 } from "path";
15171
+ import { readdir as readdir5, stat as stat9 } from "fs/promises";
15172
+ import { join as join11, relative as relative3 } from "path";
14878
15173
  async function readTree(rootPath, subdir) {
14879
- const targetPath = subdir ? join10(rootPath, subdir) : rootPath;
15174
+ const targetPath = subdir ? join11(rootPath, subdir) : rootPath;
14880
15175
  const filter = createTreeVisibilityFilter();
14881
- const entries = await readdir4(targetPath, { withFileTypes: true });
15176
+ const entries = await readdir5(targetPath, { withFileTypes: true });
14882
15177
  const nodes = [];
14883
15178
  for (const entry of entries) {
14884
15179
  if (!filter(entry.name)) {
14885
15180
  continue;
14886
15181
  }
14887
- const fullPath = join10(targetPath, entry.name);
14888
- const relPath = relative2(rootPath, fullPath);
15182
+ const fullPath = join11(targetPath, entry.name);
15183
+ const relPath = relative3(rootPath, fullPath);
14889
15184
  if (entry.isDirectory()) {
14890
15185
  nodes.push({
14891
15186
  name: entry.name,
@@ -14895,7 +15190,7 @@ async function readTree(rootPath, subdir) {
14895
15190
  // Not loaded yet - client will request on expand
14896
15191
  });
14897
15192
  } else if (entry.isFile()) {
14898
- const stats = await stat8(fullPath);
15193
+ const stats = await stat9(fullPath);
14899
15194
  nodes.push({
14900
15195
  name: entry.name,
14901
15196
  path: relPath,
@@ -14924,18 +15219,18 @@ async function searchFiles(rootPath, query, limit = 10) {
14924
15219
  const matches = [];
14925
15220
  async function walk(dirPath) {
14926
15221
  const filter = createGitignoreFilter(rootPath, dirPath);
14927
- const entries = await readdir4(dirPath, { withFileTypes: true });
15222
+ const entries = await readdir5(dirPath, { withFileTypes: true });
14928
15223
  const filteredEntries = entries.filter((entry) => filter(entry.name));
14929
15224
  filteredEntries.sort((a, b) => a.name.localeCompare(b.name));
14930
15225
  for (const entry of filteredEntries) {
14931
- const fullPath = join10(dirPath, entry.name);
14932
- const relPath = relative2(rootPath, fullPath);
15226
+ const fullPath = join11(dirPath, entry.name);
15227
+ const relPath = relative3(rootPath, fullPath);
14933
15228
  if (entry.isDirectory()) {
14934
15229
  await walk(fullPath);
14935
15230
  continue;
14936
15231
  }
14937
15232
  if (entry.isFile()) {
14938
- const rank = scoreFilenameMatch(entry.name, normalizedQuery);
15233
+ const rank = scoreFileMatch(relPath, entry.name, normalizedQuery);
14939
15234
  if (rank === null) {
14940
15235
  continue;
14941
15236
  }
@@ -14964,7 +15259,7 @@ async function searchFiles(rootPath, query, limit = 10) {
14964
15259
  }
14965
15260
  return a.path.toLowerCase().localeCompare(b.path.toLowerCase());
14966
15261
  }).slice(0, limit)) {
14967
- const stats = await stat8(match.fullPath);
15262
+ const stats = await stat9(match.fullPath);
14968
15263
  files.push({
14969
15264
  name: match.name,
14970
15265
  path: match.path,
@@ -14975,6 +15270,13 @@ async function searchFiles(rootPath, query, limit = 10) {
14975
15270
  }
14976
15271
  return { files };
14977
15272
  }
15273
+ function scoreFileMatch(path14, name, query) {
15274
+ const filenameRank = scoreFilenameMatch(name, query);
15275
+ if (filenameRank !== null) {
15276
+ return filenameRank;
15277
+ }
15278
+ return scorePathMatch(path14, query);
15279
+ }
14978
15280
  function scoreFilenameMatch(name, query) {
14979
15281
  const normalizedName = name.toLowerCase();
14980
15282
  const baseName = normalizedName.replace(/\.[^.]+$/, "");
@@ -15001,6 +15303,22 @@ function scoreFilenameMatch(name, query) {
15001
15303
  }
15002
15304
  return null;
15003
15305
  }
15306
+ function scorePathMatch(path14, query) {
15307
+ const normalizedPath = path14.toLowerCase();
15308
+ if (normalizedPath === query) {
15309
+ return 7;
15310
+ }
15311
+ if (normalizedPath.startsWith(query)) {
15312
+ return 8;
15313
+ }
15314
+ if (normalizedPath.includes(query)) {
15315
+ return 9;
15316
+ }
15317
+ if (isSubsequence(query, normalizedPath)) {
15318
+ return 10;
15319
+ }
15320
+ return null;
15321
+ }
15004
15322
  function isSubsequence(query, candidate) {
15005
15323
  let index = 0;
15006
15324
  for (const char of candidate) {
@@ -15025,6 +15343,7 @@ import { z as z13 } from "zod";
15025
15343
  var init_file = __esm({
15026
15344
  "packages/server/src/commands/file.ts"() {
15027
15345
  "use strict";
15346
+ init_content_search();
15028
15347
  init_file_io();
15029
15348
  init_tree();
15030
15349
  init_dispatch();
@@ -15057,6 +15376,26 @@ var init_file = __esm({
15057
15376
  return searchFiles(workspace.path, args.query, args.limit ?? 10);
15058
15377
  }
15059
15378
  );
15379
+ registerCommand(
15380
+ "file.searchContent",
15381
+ z13.object({
15382
+ workspaceId: z13.string(),
15383
+ query: z13.string(),
15384
+ maxFiles: z13.number().int().positive().max(100),
15385
+ maxMatchesPerFile: z13.number().int().positive().max(100)
15386
+ }),
15387
+ async (args, ctx) => {
15388
+ const workspace = ctx.workspaceMgr.get(args.workspaceId);
15389
+ if (!workspace) {
15390
+ throw { code: "workspace_not_found", message: `Workspace not found: ${args.workspaceId}` };
15391
+ }
15392
+ return searchFileContents(workspace.path, {
15393
+ query: args.query,
15394
+ maxFiles: args.maxFiles,
15395
+ maxMatchesPerFile: args.maxMatchesPerFile
15396
+ });
15397
+ }
15398
+ );
15060
15399
  registerCommand(
15061
15400
  "file.read",
15062
15401
  z13.object({
@@ -15179,7 +15518,7 @@ var init_file = __esm({
15179
15518
  });
15180
15519
 
15181
15520
  // packages/server/src/git/diff.ts
15182
- import { mkdtemp as mkdtemp3, readFile as readFile4, rm as rm6 } from "fs/promises";
15521
+ import { mkdtemp as mkdtemp3, readFile as readFile5, rm as rm6 } from "fs/promises";
15183
15522
  import os3 from "os";
15184
15523
  import path11 from "path";
15185
15524
  async function isTrackedPath(cwd, filePath) {
@@ -15219,7 +15558,7 @@ async function getUntrackedFileDiff(cwd, filePath) {
15219
15558
  }
15220
15559
  async function pathExists(cwd, filePath) {
15221
15560
  try {
15222
- await readFile4(resolveSafe(cwd, filePath));
15561
+ await readFile5(resolveSafe(cwd, filePath));
15223
15562
  return true;
15224
15563
  } catch {
15225
15564
  return false;
@@ -15227,7 +15566,7 @@ async function pathExists(cwd, filePath) {
15227
15566
  }
15228
15567
  async function readTextAtRevision(cwd, revision, filePath) {
15229
15568
  if (revision === "WORKTREE") {
15230
- return readFile4(resolveSafe(cwd, filePath), "utf-8");
15569
+ return readFile5(resolveSafe(cwd, filePath), "utf-8");
15231
15570
  }
15232
15571
  try {
15233
15572
  const gitSpec = revision === "INDEX" ? `:${filePath}` : `${revision}:${filePath}`;
@@ -15645,33 +15984,33 @@ var init_git2 = __esm({
15645
15984
  });
15646
15985
 
15647
15986
  // packages/server/src/config/config-io.ts
15648
- import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "node:fs";
15987
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync7, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "node:fs";
15649
15988
  import { homedir as homedir3 } from "node:os";
15650
- import { basename as basename2, dirname as dirname7, join as join11 } from "node:path";
15989
+ import { basename as basename3, dirname as dirname7, join as join12 } from "node:path";
15651
15990
  function resolveConfigPath(configType) {
15652
15991
  if (configType === "codex") {
15653
15992
  const testHome = process.env.CODER_STUDIO_CODEX_HOME;
15654
15993
  if (testHome && testHome.trim()) {
15655
- return join11(testHome, "config.toml");
15994
+ return join12(testHome, "config.toml");
15656
15995
  }
15657
15996
  const codexHome = process.env.CODEX_HOME;
15658
15997
  if (codexHome && codexHome.trim()) {
15659
- return join11(codexHome, "config.toml");
15998
+ return join12(codexHome, "config.toml");
15660
15999
  }
15661
- return join11(homedir3(), ".codex", "config.toml");
16000
+ return join12(homedir3(), ".codex", "config.toml");
15662
16001
  }
15663
16002
  if (configType === "claude") {
15664
16003
  const testHome = process.env.CODER_STUDIO_CLAUDE_HOME;
15665
16004
  if (testHome && testHome.trim()) {
15666
- return join11(testHome, "settings.json");
16005
+ return join12(testHome, "settings.json");
15667
16006
  }
15668
- return join11(homedir3(), ".claude", "settings.json");
16007
+ return join12(homedir3(), ".claude", "settings.json");
15669
16008
  }
15670
16009
  throw new Error(`Unknown config type: ${configType}`);
15671
16010
  }
15672
16011
  function readConfigFile(configType) {
15673
16012
  const configPath = resolveConfigPath(configType);
15674
- if (!existsSync8(configPath)) {
16013
+ if (!existsSync9(configPath)) {
15675
16014
  return { configPath, content: "", exists: false };
15676
16015
  }
15677
16016
  try {
@@ -15685,11 +16024,11 @@ function writeConfigFile(configType, content) {
15685
16024
  try {
15686
16025
  const configPath = resolveConfigPath(configType);
15687
16026
  const parentDir = dirname7(configPath);
15688
- if (!existsSync8(parentDir)) {
16027
+ if (!existsSync9(parentDir)) {
15689
16028
  mkdirSync7(parentDir, { recursive: true });
15690
16029
  }
15691
16030
  let backupPath = null;
15692
- if (existsSync8(configPath)) {
16031
+ if (existsSync9(configPath)) {
15693
16032
  backupPath = createBackup(configPath);
15694
16033
  }
15695
16034
  const tempPath = `${configPath}.tmp`;
@@ -15707,10 +16046,10 @@ function writeConfigFile(configType, content) {
15707
16046
  function createBackup(filePath) {
15708
16047
  const original = readFileSync7(filePath, "utf-8");
15709
16048
  const ext = filePath.split(".").pop() ?? "";
15710
- const base = basename2(filePath, `.${ext}`);
16049
+ const base = basename3(filePath, `.${ext}`);
15711
16050
  const dir = dirname7(filePath);
15712
16051
  const ts = formatTimestamp(/* @__PURE__ */ new Date());
15713
- const backupPath = join11(dir, `${base}.bak.${ts}.${ext}`);
16052
+ const backupPath = join12(dir, `${base}.bak.${ts}.${ext}`);
15714
16053
  writeFileSync5(backupPath, original, "utf-8");
15715
16054
  return backupPath;
15716
16055
  }
@@ -17070,12 +17409,12 @@ var init_commands = __esm({
17070
17409
  // packages/server/src/server.ts
17071
17410
  import { mkdtempSync, rmSync as rmSync2 } from "node:fs";
17072
17411
  import { tmpdir } from "node:os";
17073
- import { join as join12 } from "node:path";
17412
+ import { join as join13 } from "node:path";
17074
17413
  async function createServer(configOverrides) {
17075
17414
  const config = parseServerConfig(configOverrides);
17076
17415
  const configuredStateDir = resolveConfiguredStateDir(config);
17077
17416
  const shouldCleanupStateRoot = configuredStateDir === IN_MEMORY_STATE_DIR;
17078
- const stateRoot = shouldCleanupStateRoot ? mkdtempSync(join12(tmpdir(), "coder-studio-state-")) : configuredStateDir;
17417
+ const stateRoot = shouldCleanupStateRoot ? mkdtempSync(join13(tmpdir(), "coder-studio-state-")) : configuredStateDir;
17079
17418
  ensureStateDir(config);
17080
17419
  const eventBus = new EventBus();
17081
17420
  const activationMgr = new ActivationManager();
@@ -17085,10 +17424,10 @@ async function createServer(configOverrides) {
17085
17424
  let commandContext;
17086
17425
  let lspMgr = null;
17087
17426
  const terminalRepo = new TerminalRepo({
17088
- filePath: join12(stateRoot, "state", "terminals.json")
17427
+ filePath: join13(stateRoot, "state", "terminals.json")
17089
17428
  });
17090
17429
  const sessionRepo = new SessionRepo({
17091
- filePath: join12(stateRoot, "state", "sessions.json")
17430
+ filePath: join13(stateRoot, "state", "sessions.json")
17092
17431
  });
17093
17432
  const terminalMgr = new TerminalManager({
17094
17433
  ptyHost: createPtyHost(),
@@ -17096,10 +17435,10 @@ async function createServer(configOverrides) {
17096
17435
  db: terminalRepo
17097
17436
  });
17098
17437
  const settingsRepo = new SettingsRepo({
17099
- filePath: join12(stateRoot, "state", "settings.json")
17438
+ filePath: join13(stateRoot, "state", "settings.json")
17100
17439
  });
17101
17440
  const updateStateRepo = new UpdateStateRepo({
17102
- filePath: join12(stateRoot, "state", "update-state.json"),
17441
+ filePath: join13(stateRoot, "state", "update-state.json"),
17103
17442
  currentVersion: config.appVersion ?? "0.0.0"
17104
17443
  });
17105
17444
  const autoFetch = new AutoFetchScheduler({
@@ -17132,10 +17471,10 @@ async function createServer(configOverrides) {
17132
17471
  }
17133
17472
  });
17134
17473
  const providerConfigRepo = new ProviderConfigRepo({
17135
- filePath: join12(stateRoot, "state", "provider-configs.json")
17474
+ filePath: join13(stateRoot, "state", "provider-configs.json")
17136
17475
  });
17137
17476
  const workspaceRepo = new WorkspaceRepo({
17138
- filePath: join12(stateRoot, "state", "workspaces.json")
17477
+ filePath: join13(stateRoot, "state", "workspaces.json")
17139
17478
  });
17140
17479
  const sessionMgr = new SessionManager({
17141
17480
  terminalMgr,
@@ -17170,10 +17509,10 @@ async function createServer(configOverrides) {
17170
17509
  )
17171
17510
  });
17172
17511
  const authSessionRepo = new AuthSessionRepo({
17173
- filePath: join12(stateRoot, "state", "auth-sessions.json")
17512
+ filePath: join13(stateRoot, "state", "auth-sessions.json")
17174
17513
  });
17175
17514
  const authLoginBlockRepo = new AuthLoginBlockRepo({
17176
- filePath: join12(stateRoot, "state", "auth-login-blocks.json")
17515
+ filePath: join13(stateRoot, "state", "auth-login-blocks.json")
17177
17516
  });
17178
17517
  const app = await buildFastifyApp({
17179
17518
  wsHub,
@@ -17248,7 +17587,7 @@ async function createServer(configOverrides) {
17248
17587
  ...config.update,
17249
17588
  currentVersion: config.appVersion ?? "0.0.0"
17250
17589
  },
17251
- updateWorkerLogFilePath: join12(stateRoot, "logs", "update-worker.log"),
17590
+ updateWorkerLogFilePath: join13(stateRoot, "logs", "update-worker.log"),
17252
17591
  countRunningTerminals: () => terminalMgr.getAll().filter((terminal) => terminal.alive).length,
17253
17592
  countRunningSessions: () => sessionMgr.getAll().filter((session) => session.state === "starting" || session.state === "running").length,
17254
17593
  countActiveSupervisors: () => supervisorMgr?.countActive() ?? 0
@@ -17476,18 +17815,18 @@ import { fileURLToPath as fileURLToPath4 } from "url";
17476
17815
 
17477
17816
  // packages/cli/src/config-store.ts
17478
17817
  init_state_paths();
17479
- import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
17818
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
17480
17819
  import { homedir as homedir4 } from "os";
17481
- import { join as join13 } from "path";
17820
+ import { join as join14 } from "path";
17482
17821
  function getCliConfigPath() {
17483
- return join13(homedir4(), ".coder-studio", "config.json");
17822
+ return join14(homedir4(), ".coder-studio", "config.json");
17484
17823
  }
17485
17824
  function normalizeLegacyDataDir(input) {
17486
17825
  return normalizeLegacyStateDir(input);
17487
17826
  }
17488
17827
  function readCliConfig() {
17489
17828
  const path14 = getCliConfigPath();
17490
- if (!existsSync9(path14)) {
17829
+ if (!existsSync10(path14)) {
17491
17830
  return null;
17492
17831
  }
17493
17832
  try {
@@ -17507,7 +17846,7 @@ function readCliConfig() {
17507
17846
  }
17508
17847
 
17509
17848
  // packages/cli/src/embed.ts
17510
- import { existsSync as existsSync10 } from "fs";
17849
+ import { existsSync as existsSync11 } from "fs";
17511
17850
  import { dirname as dirname8, resolve as resolve3 } from "path";
17512
17851
  import { fileURLToPath as fileURLToPath2 } from "url";
17513
17852
  var __filename = fileURLToPath2(import.meta.url);
@@ -17517,7 +17856,7 @@ function getStaticAssetsDir() {
17517
17856
  return WEB_ASSETS_DIR;
17518
17857
  }
17519
17858
  function hasWebAssets() {
17520
- return existsSync10(WEB_ASSETS_DIR);
17859
+ return existsSync11(WEB_ASSETS_DIR);
17521
17860
  }
17522
17861
 
17523
17862
  // packages/cli/src/node-version.ts
@@ -17551,12 +17890,12 @@ function assertSupportedNodeVersion(version = process.versions.node) {
17551
17890
  }
17552
17891
 
17553
17892
  // packages/cli/src/package-manifest.ts
17554
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
17893
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
17555
17894
  function resolveCliPackageManifestUrl(importMetaUrl) {
17556
17895
  const manifestUrl = [
17557
17896
  new URL("../package.json", importMetaUrl),
17558
17897
  new URL("../../package.json", importMetaUrl)
17559
- ].find((candidate) => existsSync11(candidate));
17898
+ ].find((candidate) => existsSync12(candidate));
17560
17899
  if (!manifestUrl) {
17561
17900
  throw new Error("Unable to locate CLI package.json");
17562
17901
  }
@@ -17575,16 +17914,16 @@ function getCliPackageName(importMetaUrl) {
17575
17914
  }
17576
17915
 
17577
17916
  // packages/cli/src/update-runtime.ts
17578
- import { existsSync as existsSync12 } from "node:fs";
17579
- import { dirname as dirname9, join as join14 } from "node:path";
17917
+ import { existsSync as existsSync13 } from "node:fs";
17918
+ import { dirname as dirname9, join as join15 } from "node:path";
17580
17919
  import { fileURLToPath as fileURLToPath3 } from "node:url";
17581
17920
  function resolveWorkerEntryPath(importMetaUrl) {
17582
17921
  const currentDir = dirname9(fileURLToPath3(importMetaUrl));
17583
17922
  const candidates = [
17584
- join14(currentDir, "update-worker.mjs"),
17585
- join14(currentDir, "../src/update-worker.ts")
17923
+ join15(currentDir, "update-worker.mjs"),
17924
+ join15(currentDir, "../src/update-worker.ts")
17586
17925
  ];
17587
- return candidates.find((candidate) => existsSync12(candidate));
17926
+ return candidates.find((candidate) => existsSync13(candidate));
17588
17927
  }
17589
17928
  function getUpdateRuntimeInfo(importMetaUrl) {
17590
17929
  const workerEntryPath = resolveWorkerEntryPath(importMetaUrl);