sparkecoder 0.1.59 → 0.1.60

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.
Files changed (95) hide show
  1. package/dist/agent/index.js +74 -27
  2. package/dist/agent/index.js.map +1 -1
  3. package/dist/cli.js +88 -34
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.js +87 -33
  6. package/dist/index.js.map +1 -1
  7. package/dist/server/index.js +87 -33
  8. package/dist/server/index.js.map +1 -1
  9. package/dist/skills/default/browser.md +143 -0
  10. package/dist/skills/default/code-review.md +122 -0
  11. package/dist/skills/default/debugging.md +105 -0
  12. package/dist/skills/default/refactoring.md +197 -0
  13. package/dist/tools/index.d.ts +16 -1
  14. package/dist/tools/index.js +74 -27
  15. package/dist/tools/index.js.map +1 -1
  16. package/package.json +4 -1
  17. package/src/skills/default/browser.md +143 -0
  18. package/src/skills/default/code-review.md +122 -0
  19. package/src/skills/default/debugging.md +105 -0
  20. package/src/skills/default/refactoring.md +197 -0
  21. package/web/.next/BUILD_ID +1 -1
  22. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/build-manifest.json +2 -2
  24. package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
  25. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  26. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  27. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  28. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  30. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  31. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  32. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  33. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
  34. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  35. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  37. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  38. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  40. package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
  41. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
  48. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
  49. package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
  50. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
  51. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
  52. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
  53. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
  54. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
  56. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
  59. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
  60. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
  62. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
  63. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
  64. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
  65. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
  66. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
  67. package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
  68. package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
  69. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
  70. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
  71. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
  72. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
  73. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
  74. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
  75. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  76. package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
  77. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
  78. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
  79. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
  80. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  81. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  82. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  83. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  84. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  85. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  86. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  87. /package/web/.next/standalone/web/.next/static/{static/u5qqIWWrYpWW_mZUgKKjg → R5xiWSOp_Nqqe_js-LROo}/_buildManifest.js +0 -0
  88. /package/web/.next/standalone/web/.next/static/{static/u5qqIWWrYpWW_mZUgKKjg → R5xiWSOp_Nqqe_js-LROo}/_clientMiddlewareManifest.json +0 -0
  89. /package/web/.next/standalone/web/.next/static/{static/u5qqIWWrYpWW_mZUgKKjg → R5xiWSOp_Nqqe_js-LROo}/_ssgManifest.js +0 -0
  90. /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/R5xiWSOp_Nqqe_js-LROo}/_buildManifest.js +0 -0
  91. /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/R5xiWSOp_Nqqe_js-LROo}/_clientMiddlewareManifest.json +0 -0
  92. /package/web/.next/standalone/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → static/R5xiWSOp_Nqqe_js-LROo}/_ssgManifest.js +0 -0
  93. /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → R5xiWSOp_Nqqe_js-LROo}/_buildManifest.js +0 -0
  94. /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → R5xiWSOp_Nqqe_js-LROo}/_clientMiddlewareManifest.json +0 -0
  95. /package/web/.next/static/{u5qqIWWrYpWW_mZUgKKjg → R5xiWSOp_Nqqe_js-LROo}/_ssgManifest.js +0 -0
@@ -521,8 +521,15 @@ function discoverSkillDirectories(workingDir) {
521
521
  if (existsSync(agentsMd)) {
522
522
  agentsMdPath = agentsMd;
523
523
  }
524
- const builtInSkillsDir = resolve(dirname(import.meta.url.replace("file://", "")), "../skills/default");
525
- if (existsSync(builtInSkillsDir)) {
524
+ const baseDir = dirname(import.meta.url.replace("file://", ""));
525
+ const builtInCandidates = [
526
+ resolve(baseDir, "../skills/default"),
527
+ // dev: src/config → src/skills/default
528
+ resolve(baseDir, "./skills/default")
529
+ // prod: dist/ → dist/skills/default
530
+ ];
531
+ const builtInSkillsDir = builtInCandidates.find((p) => existsSync(p));
532
+ if (builtInSkillsDir) {
526
533
  onDemandDirs.push({ path: builtInSkillsDir, priority: 100 });
527
534
  allDirectories.push(builtInSkillsDir);
528
535
  }
@@ -874,7 +881,7 @@ __export(skills_exports, {
874
881
  loadSkillsFromDirectory: () => loadSkillsFromDirectory
875
882
  });
876
883
  import { readFile as readFile6, readdir } from "fs/promises";
877
- import { resolve as resolve6, basename, extname as extname3, relative as relative4 } from "path";
884
+ import { resolve as resolve6, basename, extname as extname4, relative as relative4 } from "path";
878
885
  import { existsSync as existsSync8 } from "fs";
879
886
  import { minimatch } from "minimatch";
880
887
  function parseSkillFrontmatter(content) {
@@ -945,7 +952,7 @@ function parseSkillFrontmatter(content) {
945
952
  }
946
953
  }
947
954
  function getSkillNameFromPath(filePath) {
948
- return basename(filePath, extname3(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
955
+ return basename(filePath, extname4(filePath)).replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
949
956
  }
950
957
  async function loadSkillsFromDirectory(directory, options = {}) {
951
958
  const {
@@ -1250,7 +1257,7 @@ var init_hasher = __esm({
1250
1257
  });
1251
1258
 
1252
1259
  // src/semantic/chunker.ts
1253
- import { extname as extname5, basename as basename2 } from "path";
1260
+ import { extname as extname6, basename as basename2 } from "path";
1254
1261
  var init_chunker = __esm({
1255
1262
  "src/semantic/chunker.ts"() {
1256
1263
  "use strict";
@@ -1615,7 +1622,7 @@ import { zValidator } from "@hono/zod-validator";
1615
1622
  import { z as z13 } from "zod";
1616
1623
  import { existsSync as existsSync13, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync, statSync as statSync2, unlinkSync } from "fs";
1617
1624
  import { readdir as readdir5 } from "fs/promises";
1618
- import { join as join5, basename as basename4, extname as extname6, relative as relative9 } from "path";
1625
+ import { join as join5, basename as basename4, extname as extname7, relative as relative9 } from "path";
1619
1626
  import { nanoid as nanoid4 } from "nanoid";
1620
1627
 
1621
1628
  // src/agent/index.ts
@@ -2226,26 +2233,41 @@ Terminal output is stored in the global SparkECoder data directory. Use the \`ta
2226
2233
  import { tool as tool2 } from "ai";
2227
2234
  import { z as z3 } from "zod";
2228
2235
  import { readFile as readFile2, stat } from "fs/promises";
2229
- import { resolve as resolve2, relative, isAbsolute } from "path";
2236
+ import { resolve as resolve2, relative, isAbsolute, extname } from "path";
2230
2237
  import { existsSync as existsSync3 } from "fs";
2231
2238
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
2239
+ var MAX_IMAGE_SIZE = 20 * 1024 * 1024;
2232
2240
  var MAX_OUTPUT_CHARS3 = 5e4;
2241
+ var IMAGE_EXTENSIONS = {
2242
+ ".png": "image/png",
2243
+ ".jpg": "image/jpeg",
2244
+ ".jpeg": "image/jpeg",
2245
+ ".gif": "image/gif",
2246
+ ".webp": "image/webp"
2247
+ };
2248
+ function isImageFile(filePath) {
2249
+ return extname(filePath).toLowerCase() in IMAGE_EXTENSIONS;
2250
+ }
2251
+ function getImageMediaType(filePath) {
2252
+ return IMAGE_EXTENSIONS[extname(filePath).toLowerCase()] || "image/png";
2253
+ }
2233
2254
  var readFileInputSchema = z3.object({
2234
- path: z3.string().describe("The path to the file to read. Can be relative to working directory or absolute."),
2235
- startLine: z3.number().optional().describe("Optional: Start reading from this line number (1-indexed)"),
2236
- endLine: z3.number().optional().describe("Optional: Stop reading at this line number (1-indexed, inclusive)")
2255
+ path: z3.string().describe("The path to the file to read. Can be relative to working directory or absolute. Supports text files and images (png, jpg, jpeg, gif, webp)."),
2256
+ startLine: z3.number().optional().describe("Optional: Start reading from this line number (1-indexed). Only for text files."),
2257
+ endLine: z3.number().optional().describe("Optional: Stop reading at this line number (1-indexed, inclusive). Only for text files.")
2237
2258
  });
2238
2259
  function createReadFileTool(options) {
2239
2260
  return tool2({
2240
2261
  description: `Read the contents of a file. Provide a path relative to the working directory (${options.workingDirectory}) or an absolute path.
2241
- Large files will be automatically truncated. Binary files are not supported.
2242
- Use this to understand existing code, check file contents, or gather context.`,
2262
+ Supports text files (automatically truncated if large) and image files (png, jpg, jpeg, gif, webp).
2263
+ For images, the file contents are returned as visual data you can see and analyze.
2264
+ Use this to understand existing code, check file contents, view screenshots, or gather context.`,
2243
2265
  inputSchema: readFileInputSchema,
2244
- execute: async ({ path, startLine, endLine }) => {
2266
+ execute: async ({ path: filePath, startLine, endLine }) => {
2245
2267
  try {
2246
- const absolutePath = isAbsolute(path) ? path : resolve2(options.workingDirectory, path);
2268
+ const absolutePath = isAbsolute(filePath) ? filePath : resolve2(options.workingDirectory, filePath);
2247
2269
  const relativePath = relative(options.workingDirectory, absolutePath);
2248
- if (relativePath.startsWith("..") && !isAbsolute(path)) {
2270
+ if (relativePath.startsWith("..") && !isAbsolute(filePath)) {
2249
2271
  return {
2250
2272
  success: false,
2251
2273
  error: "Path escapes the working directory. Use an absolute path if intentional.",
@@ -2255,22 +2277,43 @@ Use this to understand existing code, check file contents, or gather context.`,
2255
2277
  if (!existsSync3(absolutePath)) {
2256
2278
  return {
2257
2279
  success: false,
2258
- error: `File not found: ${path}`,
2280
+ error: `File not found: ${filePath}`,
2259
2281
  content: null
2260
2282
  };
2261
2283
  }
2262
2284
  const stats = await stat(absolutePath);
2263
- if (stats.size > MAX_FILE_SIZE) {
2285
+ if (stats.isDirectory()) {
2264
2286
  return {
2265
2287
  success: false,
2266
- error: `File is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_FILE_SIZE / 1024 / 1024}MB.`,
2288
+ error: 'Path is a directory, not a file. Use bash with "ls" to list directory contents.',
2267
2289
  content: null
2268
2290
  };
2269
2291
  }
2270
- if (stats.isDirectory()) {
2292
+ if (isImageFile(absolutePath)) {
2293
+ if (stats.size > MAX_IMAGE_SIZE) {
2294
+ return {
2295
+ success: false,
2296
+ error: `Image is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_IMAGE_SIZE / 1024 / 1024}MB.`,
2297
+ content: null
2298
+ };
2299
+ }
2300
+ const buffer = await readFile2(absolutePath);
2301
+ const base64 = buffer.toString("base64");
2302
+ const mediaType = getImageMediaType(absolutePath);
2303
+ return {
2304
+ success: true,
2305
+ path: absolutePath,
2306
+ relativePath: relative(options.workingDirectory, absolutePath),
2307
+ content: `[Image: ${relativePath} (${mediaType}, ${(stats.size / 1024).toFixed(1)}KB)]`,
2308
+ mediaType,
2309
+ imageData: base64,
2310
+ sizeBytes: stats.size
2311
+ };
2312
+ }
2313
+ if (stats.size > MAX_FILE_SIZE) {
2271
2314
  return {
2272
2315
  success: false,
2273
- error: 'Path is a directory, not a file. Use bash with "ls" to list directory contents.',
2316
+ error: `File is too large (${(stats.size / 1024 / 1024).toFixed(2)}MB). Maximum size is ${MAX_FILE_SIZE / 1024 / 1024}MB.`,
2274
2317
  content: null
2275
2318
  };
2276
2319
  }
@@ -2286,9 +2329,7 @@ Use this to understand existing code, check file contents, or gather context.`,
2286
2329
  content: null
2287
2330
  };
2288
2331
  }
2289
- content = lines.slice(start, end).join("\n");
2290
- const lineNumbers = lines.slice(start, end).map((line, idx) => `${(start + idx + 1).toString().padStart(4)}: ${line}`).join("\n");
2291
- content = lineNumbers;
2332
+ content = lines.slice(start, end).map((line, idx) => `${(start + idx + 1).toString().padStart(4)}: ${line}`).join("\n");
2292
2333
  }
2293
2334
  const truncatedContent = truncateOutput(content, MAX_OUTPUT_CHARS3);
2294
2335
  const wasTruncated = truncatedContent.length < content.length;
@@ -2315,6 +2356,19 @@ Use this to understand existing code, check file contents, or gather context.`,
2315
2356
  content: null
2316
2357
  };
2317
2358
  }
2359
+ },
2360
+ toModelOutput: ({ output }) => {
2361
+ if (output && typeof output === "object" && "imageData" in output && output.imageData) {
2362
+ const result = output;
2363
+ return {
2364
+ type: "content",
2365
+ value: [
2366
+ { type: "text", text: result.content },
2367
+ { type: "image-data", data: result.imageData, mediaType: result.mediaType }
2368
+ ]
2369
+ };
2370
+ }
2371
+ return typeof output === "string" ? { type: "text", value: output } : { type: "json", value: output };
2318
2372
  }
2319
2373
  });
2320
2374
  }
@@ -2515,7 +2569,7 @@ function clearCheckpointManager(sessionId) {
2515
2569
  }
2516
2570
 
2517
2571
  // src/lsp/index.ts
2518
- import { extname as extname2, dirname as dirname4 } from "path";
2572
+ import { extname as extname3, dirname as dirname4 } from "path";
2519
2573
 
2520
2574
  // src/lsp/servers.ts
2521
2575
  import { spawn } from "child_process";
@@ -2638,9 +2692,9 @@ import {
2638
2692
  import { pathToFileURL, fileURLToPath } from "url";
2639
2693
  import { readFile as readFile4 } from "fs/promises";
2640
2694
  import { existsSync as existsSync6 } from "fs";
2641
- import { extname, normalize } from "path";
2695
+ import { extname as extname2, normalize } from "path";
2642
2696
  function getLanguageId(filePath) {
2643
- const ext = extname(filePath).toLowerCase();
2697
+ const ext = extname2(filePath).toLowerCase();
2644
2698
  const map = {
2645
2699
  ".ts": "typescript",
2646
2700
  ".tsx": "typescriptreact",
@@ -3028,7 +3082,7 @@ var state = {
3028
3082
  };
3029
3083
  async function getClientForFile(filePath) {
3030
3084
  const normalized = normalizePath(filePath);
3031
- const ext = extname2(normalized);
3085
+ const ext = extname3(normalized);
3032
3086
  const serverDef = getServerForExtension(ext);
3033
3087
  if (!serverDef) {
3034
3088
  return null;
@@ -3121,7 +3175,7 @@ async function formatDiagnosticsOutput(filePath, options = {}) {
3121
3175
  return formatDiagnosticsForAgent(filePath, diagnostics, options);
3122
3176
  }
3123
3177
  function isSupported(filePath) {
3124
- const ext = extname2(filePath);
3178
+ const ext = extname3(filePath);
3125
3179
  return getServerForExtension(ext) !== null;
3126
3180
  }
3127
3181
 
@@ -3548,7 +3602,7 @@ Once loaded, a skill's content will be available in the conversation context.`,
3548
3602
  // src/tools/linter.ts
3549
3603
  import { tool as tool6 } from "ai";
3550
3604
  import { z as z7 } from "zod";
3551
- import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as extname4 } from "path";
3605
+ import { resolve as resolve7, relative as relative5, isAbsolute as isAbsolute3, extname as extname5 } from "path";
3552
3606
  import { existsSync as existsSync9 } from "fs";
3553
3607
  import { readdir as readdir2, stat as stat2 } from "fs/promises";
3554
3608
  var linterInputSchema = z7.object({
@@ -3571,7 +3625,7 @@ async function findSupportedFiles(dir, workingDirectory, maxFiles = 50) {
3571
3625
  }
3572
3626
  await walk(fullPath);
3573
3627
  } else if (entry.isFile()) {
3574
- const ext = extname4(entry.name);
3628
+ const ext = extname5(entry.name);
3575
3629
  if (supportedExtensions.includes(ext)) {
3576
3630
  files.push(fullPath);
3577
3631
  }
@@ -6395,7 +6449,7 @@ sessions.post("/:id/attachments", async (c) => {
6395
6449
  }
6396
6450
  const dir = ensureAttachmentsDir(sessionId);
6397
6451
  const id = nanoid4(10);
6398
- const ext = extname6(file.name) || "";
6452
+ const ext = extname7(file.name) || "";
6399
6453
  const safeFilename = `${id}_${basename4(file.name).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
6400
6454
  const filePath = join5(dir, safeFilename);
6401
6455
  const arrayBuffer = await file.arrayBuffer();
@@ -6421,7 +6475,7 @@ sessions.post("/:id/attachments", async (c) => {
6421
6475
  }
6422
6476
  const dir = ensureAttachmentsDir(sessionId);
6423
6477
  const id = nanoid4(10);
6424
- const ext = extname6(body.filename) || "";
6478
+ const ext = extname7(body.filename) || "";
6425
6479
  const safeFilename = `${id}_${basename4(body.filename).replace(/[^a-zA-Z0-9._-]/g, "_")}`;
6426
6480
  const filePath = join5(dir, safeFilename);
6427
6481
  let base64Data = body.data;
@@ -6551,7 +6605,7 @@ async function listWorkspaceFiles(baseDir, currentDir, query, limit, results = [
6551
6605
  if (entry.name.startsWith(".")) {
6552
6606
  continue;
6553
6607
  }
6554
- const ext = extname6(entry.name).toLowerCase();
6608
+ const ext = extname7(entry.name).toLowerCase();
6555
6609
  if (IGNORED_EXTENSIONS.has(ext)) {
6556
6610
  continue;
6557
6611
  }