@llmist/cli 15.2.0 → 15.2.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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,24 @@
1
1
  import * as llmist from 'llmist';
2
2
 
3
+ /**
4
+ * Exception thrown when a path validation fails due to sandbox constraints.
5
+ * This ensures all file operations are restricted to the current working directory.
6
+ */
7
+ declare class PathSandboxException extends Error {
8
+ constructor(inputPath: string, reason: string);
9
+ }
10
+ /**
11
+ * Validates that a given path is within the current working directory.
12
+ * This prevents directory traversal attacks and ensures all file operations
13
+ * are sandboxed to the CWD and its subdirectories.
14
+ *
15
+ * @param inputPath - Path to validate (can be relative or absolute)
16
+ * @returns The validated absolute path
17
+ * @throws PathSandboxException if the path is outside the CWD
18
+ * @throws Error for other file system errors
19
+ */
20
+ declare function validatePathIsWithinCwd(inputPath: string): string;
21
+
3
22
  declare const editFile: llmist.AbstractGadget;
4
23
 
5
24
  /**
@@ -34,23 +53,4 @@ declare const writeFile: llmist.AbstractGadget;
34
53
  */
35
54
  declare const runCommand: llmist.AbstractGadget;
36
55
 
37
- /**
38
- * Exception thrown when a path validation fails due to sandbox constraints.
39
- * This ensures all file operations are restricted to the current working directory.
40
- */
41
- declare class PathSandboxException extends Error {
42
- constructor(inputPath: string, reason: string);
43
- }
44
- /**
45
- * Validates that a given path is within the current working directory.
46
- * This prevents directory traversal attacks and ensures all file operations
47
- * are sandboxed to the CWD and its subdirectories.
48
- *
49
- * @param inputPath - Path to validate (can be relative or absolute)
50
- * @returns The validated absolute path
51
- * @throws PathSandboxException if the path is outside the CWD
52
- * @throws Error for other file system errors
53
- */
54
- declare function validatePathIsWithinCwd(inputPath: string): string;
55
-
56
56
  export { editFile as EditFile, listDirectory as ListDirectory, PathSandboxException, readFile as ReadFile, runCommand as RunCommand, writeFile as WriteFile, editFile, listDirectory, readFile, runCommand, validatePathIsWithinCwd, writeFile };
package/dist/index.js CHANGED
@@ -1,3 +1,33 @@
1
+ // src/builtins/filesystem/utils.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ var PathSandboxException = class extends Error {
5
+ constructor(inputPath, reason) {
6
+ super(`Path access denied: ${inputPath}. ${reason}`);
7
+ this.name = "PathSandboxException";
8
+ }
9
+ };
10
+ function validatePathIsWithinCwd(inputPath) {
11
+ const cwd = process.cwd();
12
+ const resolvedPath = path.resolve(cwd, inputPath);
13
+ let finalPath;
14
+ try {
15
+ finalPath = fs.realpathSync(resolvedPath);
16
+ } catch (error) {
17
+ const nodeError = error;
18
+ if (nodeError.code === "ENOENT") {
19
+ finalPath = resolvedPath;
20
+ } else {
21
+ throw error;
22
+ }
23
+ }
24
+ const cwdWithSep = cwd + path.sep;
25
+ if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {
26
+ throw new PathSandboxException(inputPath, "Path is outside the current working directory");
27
+ }
28
+ return finalPath;
29
+ }
30
+
1
31
  // src/builtins/filesystem/edit-file.ts
2
32
  import { readFileSync, writeFileSync } from "fs";
3
33
  import { createGadget } from "llmist";
@@ -253,36 +283,6 @@ function getContext(content, lineNumber, contextLines) {
253
283
  return contextWithNumbers.join("\n");
254
284
  }
255
285
 
256
- // src/builtins/filesystem/utils.ts
257
- import fs from "fs";
258
- import path from "path";
259
- var PathSandboxException = class extends Error {
260
- constructor(inputPath, reason) {
261
- super(`Path access denied: ${inputPath}. ${reason}`);
262
- this.name = "PathSandboxException";
263
- }
264
- };
265
- function validatePathIsWithinCwd(inputPath) {
266
- const cwd = process.cwd();
267
- const resolvedPath = path.resolve(cwd, inputPath);
268
- let finalPath;
269
- try {
270
- finalPath = fs.realpathSync(resolvedPath);
271
- } catch (error) {
272
- const nodeError = error;
273
- if (nodeError.code === "ENOENT") {
274
- finalPath = resolvedPath;
275
- } else {
276
- throw error;
277
- }
278
- }
279
- const cwdWithSep = cwd + path.sep;
280
- if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {
281
- throw new PathSandboxException(inputPath, "Path is outside the current working directory");
282
- }
283
- return finalPath;
284
- }
285
-
286
286
  // src/builtins/filesystem/edit-file.ts
287
287
  function formatFailure(filePath, search, failure, fileContent) {
288
288
  const lines = [
@@ -423,8 +423,8 @@ ${newContent}
423
423
  // src/builtins/filesystem/list-directory.ts
424
424
  import fs2 from "fs";
425
425
  import path2 from "path";
426
- import { z as z2 } from "zod";
427
426
  import { createGadget as createGadget2 } from "llmist";
427
+ import { z as z2 } from "zod";
428
428
  function listFiles(dirPath, basePath = dirPath, maxDepth = 1, currentDepth = 1) {
429
429
  const entries = [];
430
430
  try {
@@ -545,8 +545,8 @@ ${formattedList}`;
545
545
 
546
546
  // src/builtins/filesystem/read-file.ts
547
547
  import fs3 from "fs";
548
- import { z as z3 } from "zod";
549
548
  import { createGadget as createGadget3 } from "llmist";
549
+ import { z as z3 } from "zod";
550
550
  var readFile = createGadget3({
551
551
  name: "ReadFile",
552
552
  description: "Read the entire content of a file and return it as text. The file path must be within the current working directory or its subdirectories.",
@@ -577,8 +577,8 @@ ${content}`;
577
577
  // src/builtins/filesystem/write-file.ts
578
578
  import fs4 from "fs";
579
579
  import path3 from "path";
580
- import { z as z4 } from "zod";
581
580
  import { createGadget as createGadget4 } from "llmist";
581
+ import { z as z4 } from "zod";
582
582
  var writeFile = createGadget4({
583
583
  name: "WriteFile",
584
584
  description: "Write content to a file. Creates parent directories if needed. Overwrites existing files. The file path must be within the current working directory or its subdirectories.",
@@ -629,8 +629,8 @@ Wrote ${bytesWritten} bytes${dirNote}`;
629
629
  });
630
630
 
631
631
  // src/builtins/run-command.ts
632
- import { z as z5 } from "zod";
633
632
  import { createGadget as createGadget5 } from "llmist";
633
+ import { z as z5 } from "zod";
634
634
 
635
635
  // src/spawn.ts
636
636
  import { spawn as nodeSpawn } from "child_process";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/builtins/filesystem/edit-file.ts","../src/builtins/filesystem/editfile/matcher.ts","../src/builtins/filesystem/utils.ts","../src/builtins/filesystem/list-directory.ts","../src/builtins/filesystem/read-file.ts","../src/builtins/filesystem/write-file.ts","../src/builtins/run-command.ts","../src/spawn.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from \"node:fs\";\nimport { createGadget } from \"llmist\";\nimport { z } from \"zod\";\nimport type { MatchFailure } from \"./editfile/index.js\";\nimport { applyReplacement, findMatch, getMatchFailure } from \"./editfile/index.js\";\nimport { validatePathIsWithinCwd } from \"./utils.js\";\n\n/**\n * EditFile gadget - Edit files using search/replace with layered matching.\n *\n * Uses layered matching strategies: exact -> whitespace -> indentation -> fuzzy\n * This approach reduces edit errors by ~9x (per Aider benchmarks).\n */\n\nfunction formatFailure(\n filePath: string,\n search: string,\n failure: MatchFailure,\n fileContent: string,\n): string {\n const lines: string[] = [\n `path=${filePath} status=failed`,\n \"\",\n `Error: ${failure.reason}`,\n \"\",\n \"SEARCH CONTENT:\",\n \"```\",\n search,\n \"```\",\n ];\n\n if (failure.suggestions.length > 0) {\n lines.push(\"\", \"SUGGESTIONS (similar content found):\");\n for (const suggestion of failure.suggestions) {\n const percent = Math.round(suggestion.similarity * 100);\n lines.push(\n \"\",\n `Line ${suggestion.lineNumber} (${percent}% similar):`,\n \"```\",\n suggestion.content,\n \"```\",\n );\n }\n\n if (failure.nearbyContext) {\n lines.push(\"\", \"CONTEXT:\", failure.nearbyContext);\n }\n }\n\n lines.push(\"\", \"CURRENT FILE CONTENT:\", \"```\", fileContent, \"```\");\n\n return lines.join(\"\\n\");\n}\n\nexport const editFile = createGadget({\n name: \"EditFile\",\n description: `Edit a file by searching for content and replacing it.\n\nUses layered matching strategies (in order):\n1. Exact match - byte-for-byte comparison\n2. Whitespace-insensitive - ignores differences in spaces/tabs\n3. Indentation-preserving - matches structure ignoring leading whitespace\n4. Fuzzy match - similarity-based matching (80% threshold)\n\nFor multiple edits to the same file, call this gadget multiple times.\nEach call provides immediate feedback, allowing you to adjust subsequent edits.`,\n schema: z.object({\n filePath: z.string().describe(\"Path to the file to edit (relative or absolute)\"),\n search: z.string().describe(\"The content to search for in the file\"),\n replace: z.string().describe(\"The content to replace it with (empty string to delete)\"),\n }),\n examples: [\n {\n params: {\n filePath: \"src/config.ts\",\n search: \"const DEBUG = false;\",\n replace: \"const DEBUG = true;\",\n },\n output:\n \"path=src/config.ts status=success strategy=exact lines=5-5\\n\\nReplaced content successfully.\\n\\nUPDATED FILE CONTENT:\\n```\\n// config.ts\\nconst DEBUG = true;\\nexport default { DEBUG };\\n```\",\n comment: \"Simple single-line edit\",\n },\n {\n params: {\n filePath: \"src/utils.ts\",\n search: `function oldHelper() {\n return 1;\n}`,\n replace: `function newHelper() {\n return 2;\n}`,\n },\n output:\n \"path=src/utils.ts status=success strategy=exact lines=10-12\\n\\nReplaced content successfully.\\n\\nUPDATED FILE CONTENT:\\n```\\n// utils.ts\\nfunction newHelper() {\\n return 2;\\n}\\n```\",\n comment: \"Multi-line replacement\",\n },\n {\n params: {\n filePath: \"src/app.ts\",\n search: \"unusedImport\",\n replace: \"\",\n },\n output:\n 'path=src/app.ts status=success strategy=exact lines=3-3\\n\\nReplaced content successfully.\\n\\nUPDATED FILE CONTENT:\\n```\\n// app.ts\\nimport { usedImport } from \"./lib\";\\n```',\n comment: \"Delete content by replacing with empty string\",\n },\n ],\n timeoutMs: 30000,\n execute: ({ filePath, search, replace }) => {\n // Validate search is not empty\n if (search.trim() === \"\") {\n return `path=${filePath} status=error\\n\\nError: Search content cannot be empty.`;\n }\n\n // Validate and resolve path\n let validatedPath: string;\n try {\n validatedPath = validatePathIsWithinCwd(filePath);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return `path=${filePath} status=error\\n\\nError: ${message}`;\n }\n\n // Read file content\n let content: string;\n try {\n content = readFileSync(validatedPath, \"utf-8\");\n } catch (error) {\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === \"ENOENT\") {\n return `path=${filePath} status=error\\n\\nError: File not found: ${filePath}`;\n }\n const message = error instanceof Error ? error.message : String(error);\n return `path=${filePath} status=error\\n\\nError reading file: ${message}`;\n }\n\n // Find match using layered strategies\n const match = findMatch(content, search);\n\n if (!match) {\n // No match found - provide helpful suggestions\n const failure = getMatchFailure(content, search);\n return formatFailure(filePath, search, failure, content);\n }\n\n // Apply replacement\n const newContent = applyReplacement(content, match, replace);\n\n // Write file\n try {\n writeFileSync(validatedPath, newContent, \"utf-8\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return `path=${filePath} status=error\\n\\nError writing file: ${message}`;\n }\n\n return `path=${filePath} status=success strategy=${match.strategy} lines=${match.startLine}-${match.endLine}\\n\\nReplaced content successfully.\\n\\nUPDATED FILE CONTENT:\\n\\`\\`\\`\\n${newContent}\\n\\`\\`\\``;\n },\n});\n","/**\n * Layered matching algorithm for EditFile gadget.\n *\n * Tries strategies in order: exact -> whitespace -> indentation -> fuzzy\n * This approach reduces edit errors by ~9x (per Aider benchmarks).\n */\n\nimport type {\n MatchFailure,\n MatchOptions,\n MatchResult,\n MatchStrategy,\n SuggestionMatch,\n} from \"./types.js\";\n\nconst DEFAULT_OPTIONS: Required<MatchOptions> = {\n fuzzyThreshold: 0.8,\n maxSuggestions: 3,\n contextLines: 5,\n};\n\n/**\n * Find a match for the search string in content using layered strategies.\n *\n * @param content The file content to search in\n * @param search The string to search for\n * @param options Matching options\n * @returns MatchResult if found, null if not found\n */\nexport function findMatch(\n content: string,\n search: string,\n options: MatchOptions = {},\n): MatchResult | null {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Try each strategy in order\n const strategies: Array<{\n name: MatchStrategy;\n fn: (content: string, search: string) => MatchResult | null;\n }> = [\n { name: \"exact\", fn: exactMatch },\n { name: \"whitespace\", fn: whitespaceMatch },\n { name: \"indentation\", fn: indentationMatch },\n { name: \"fuzzy\", fn: (c, s) => fuzzyMatch(c, s, opts.fuzzyThreshold) },\n ];\n\n for (const { name, fn } of strategies) {\n const result = fn(content, search);\n if (result) {\n return { ...result, strategy: name };\n }\n }\n\n return null;\n}\n\n/**\n * Apply replacement to content at the matched location.\n */\nexport function applyReplacement(content: string, match: MatchResult, replacement: string): string {\n return content.slice(0, match.startIndex) + replacement + content.slice(match.endIndex);\n}\n\n/**\n * Get failure details with suggestions when no match is found.\n */\nexport function getMatchFailure(\n content: string,\n search: string,\n options: MatchOptions = {},\n): MatchFailure {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const suggestions = findSuggestions(content, search, opts.maxSuggestions, opts.fuzzyThreshold);\n const nearbyContext =\n suggestions.length > 0 ? getContext(content, suggestions[0].lineNumber, opts.contextLines) : \"\";\n\n return {\n reason: \"Search content not found in file\",\n suggestions,\n nearbyContext,\n };\n}\n\n// ============================================================================\n// Strategy 1: Exact Match\n// ============================================================================\n\nfunction exactMatch(content: string, search: string): MatchResult | null {\n const index = content.indexOf(search);\n if (index === -1) return null;\n\n const { startLine, endLine } = getLineNumbers(content, index, index + search.length);\n\n return {\n found: true,\n strategy: \"exact\",\n confidence: 1.0,\n matchedContent: search,\n startIndex: index,\n endIndex: index + search.length,\n startLine,\n endLine,\n };\n}\n\n// ============================================================================\n// Strategy 2: Whitespace-Insensitive Match\n// ============================================================================\n\nfunction whitespaceMatch(content: string, search: string): MatchResult | null {\n // Normalize runs of spaces/tabs to single space, preserve newlines\n const normalizeWs = (s: string) => s.replace(/[ \\t]+/g, \" \");\n\n const normalizedContent = normalizeWs(content);\n const normalizedSearch = normalizeWs(search);\n\n const normalizedIndex = normalizedContent.indexOf(normalizedSearch);\n if (normalizedIndex === -1) return null;\n\n // Map normalized index back to original content\n const { originalStart, originalEnd } = mapNormalizedToOriginal(\n content,\n normalizedIndex,\n normalizedSearch.length,\n );\n\n const matchedContent = content.slice(originalStart, originalEnd);\n const { startLine, endLine } = getLineNumbers(content, originalStart, originalEnd);\n\n return {\n found: true,\n strategy: \"whitespace\",\n confidence: 0.95,\n matchedContent,\n startIndex: originalStart,\n endIndex: originalEnd,\n startLine,\n endLine,\n };\n}\n\n// ============================================================================\n// Strategy 3: Indentation-Preserving Match\n// ============================================================================\n\nfunction indentationMatch(content: string, search: string): MatchResult | null {\n // Strip leading whitespace from each line for comparison\n const stripIndent = (s: string) =>\n s\n .split(\"\\n\")\n .map((line) => line.trimStart())\n .join(\"\\n\");\n\n const strippedSearch = stripIndent(search);\n const contentLines = content.split(\"\\n\");\n\n // Sliding window search\n const searchLineCount = search.split(\"\\n\").length;\n\n for (let i = 0; i <= contentLines.length - searchLineCount; i++) {\n const windowLines = contentLines.slice(i, i + searchLineCount);\n const strippedWindow = stripIndent(windowLines.join(\"\\n\"));\n\n if (strippedWindow === strippedSearch) {\n // Found match - calculate original indices\n const startIndex = contentLines.slice(0, i).join(\"\\n\").length + (i > 0 ? 1 : 0);\n const matchedContent = windowLines.join(\"\\n\");\n const endIndex = startIndex + matchedContent.length;\n const { startLine, endLine } = getLineNumbers(content, startIndex, endIndex);\n\n return {\n found: true,\n strategy: \"indentation\",\n confidence: 0.9,\n matchedContent,\n startIndex,\n endIndex,\n startLine,\n endLine,\n };\n }\n }\n\n return null;\n}\n\n// ============================================================================\n// Strategy 4: Fuzzy Match\n// ============================================================================\n\nfunction fuzzyMatch(content: string, search: string, threshold: number): MatchResult | null {\n const searchLines = search.split(\"\\n\");\n const contentLines = content.split(\"\\n\");\n\n if (searchLines.length > contentLines.length) return null;\n\n let bestMatch: {\n startLineIndex: number;\n endLineIndex: number;\n similarity: number;\n } | null = null;\n\n // Sliding window\n for (let i = 0; i <= contentLines.length - searchLines.length; i++) {\n const windowLines = contentLines.slice(i, i + searchLines.length);\n const similarity = calculateLineSimilarity(searchLines, windowLines);\n\n if (similarity >= threshold && (!bestMatch || similarity > bestMatch.similarity)) {\n bestMatch = {\n startLineIndex: i,\n endLineIndex: i + searchLines.length,\n similarity,\n };\n }\n }\n\n if (!bestMatch) return null;\n\n // Calculate original indices\n const startIndex =\n contentLines.slice(0, bestMatch.startLineIndex).join(\"\\n\").length +\n (bestMatch.startLineIndex > 0 ? 1 : 0);\n const matchedContent = contentLines\n .slice(bestMatch.startLineIndex, bestMatch.endLineIndex)\n .join(\"\\n\");\n const endIndex = startIndex + matchedContent.length;\n const { startLine, endLine } = getLineNumbers(content, startIndex, endIndex);\n\n return {\n found: true,\n strategy: \"fuzzy\",\n confidence: bestMatch.similarity,\n matchedContent,\n startIndex,\n endIndex,\n startLine,\n endLine,\n };\n}\n\n// ============================================================================\n// Suggestion Finding\n// ============================================================================\n\nfunction findSuggestions(\n content: string,\n search: string,\n maxSuggestions: number,\n minSimilarity: number,\n): SuggestionMatch[] {\n const searchLines = search.split(\"\\n\");\n const contentLines = content.split(\"\\n\");\n const suggestions: Array<{ lineIndex: number; similarity: number; content: string }> = [];\n\n // Reduce threshold for suggestions (show more potential matches)\n const suggestionThreshold = Math.max(0.5, minSimilarity - 0.2);\n\n // Find best matches using sliding window\n for (let i = 0; i <= contentLines.length - searchLines.length; i++) {\n const windowLines = contentLines.slice(i, i + searchLines.length);\n const similarity = calculateLineSimilarity(searchLines, windowLines);\n\n if (similarity >= suggestionThreshold) {\n suggestions.push({\n lineIndex: i,\n similarity,\n content: windowLines.join(\"\\n\"),\n });\n }\n }\n\n // Sort by similarity descending\n suggestions.sort((a, b) => b.similarity - a.similarity);\n\n return suggestions.slice(0, maxSuggestions).map((s) => ({\n content: s.content,\n lineNumber: s.lineIndex + 1, // 1-based\n similarity: s.similarity,\n }));\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Calculate similarity between two line arrays using line-by-line comparison.\n */\nfunction calculateLineSimilarity(a: string[], b: string[]): number {\n if (a.length !== b.length) return 0;\n if (a.length === 0) return 1;\n\n let totalSimilarity = 0;\n let totalWeight = 0;\n\n for (let i = 0; i < a.length; i++) {\n const lineA = a[i];\n const lineB = b[i];\n // Weight by line length (longer lines matter more)\n const weight = Math.max(lineA.length, lineB.length, 1);\n const similarity = stringSimilarity(lineA, lineB);\n totalSimilarity += similarity * weight;\n totalWeight += weight;\n }\n\n return totalWeight > 0 ? totalSimilarity / totalWeight : 0;\n}\n\n/**\n * Calculate similarity between two strings using Levenshtein distance.\n */\nfunction stringSimilarity(a: string, b: string): number {\n if (a === b) return 1;\n if (a.length === 0 || b.length === 0) return 0;\n\n const distance = levenshteinDistance(a, b);\n const maxLen = Math.max(a.length, b.length);\n return 1 - distance / maxLen;\n}\n\n/**\n * Levenshtein distance between two strings.\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1, // substitution\n matrix[i][j - 1] + 1, // insertion\n matrix[i - 1][j] + 1, // deletion\n );\n }\n }\n }\n\n return matrix[b.length][a.length];\n}\n\n/**\n * Get 1-based line numbers for a range in content.\n */\nfunction getLineNumbers(\n content: string,\n startIndex: number,\n endIndex: number,\n): { startLine: number; endLine: number } {\n const beforeStart = content.slice(0, startIndex);\n const beforeEnd = content.slice(0, endIndex);\n\n const startLine = (beforeStart.match(/\\n/g) || []).length + 1;\n const endLine = (beforeEnd.match(/\\n/g) || []).length + 1;\n\n return { startLine, endLine };\n}\n\n/**\n * Check if character is horizontal whitespace (space or tab).\n */\nfunction isHorizontalWhitespace(char: string): boolean {\n return char === \" \" || char === \"\\t\";\n}\n\n/**\n * Map normalized string index back to original string.\n * Uses a two-pass approach: first find start, then find end.\n */\nfunction mapNormalizedToOriginal(\n original: string,\n normalizedStart: number,\n normalizedLength: number,\n): { originalStart: number; originalEnd: number } {\n const originalStart = findOriginalIndex(original, normalizedStart);\n const originalEnd = findOriginalIndex(original, normalizedStart + normalizedLength);\n return { originalStart, originalEnd: originalEnd === -1 ? original.length : originalEnd };\n}\n\n/**\n * Find original string index for a given normalized position.\n */\nfunction findOriginalIndex(original: string, targetNormalizedPos: number): number {\n let normalizedPos = 0;\n let inWhitespace = false;\n\n for (let i = 0; i < original.length; i++) {\n if (normalizedPos === targetNormalizedPos) {\n return i;\n }\n\n const isWs = isHorizontalWhitespace(original[i]);\n if (isWs && !inWhitespace) {\n normalizedPos++;\n inWhitespace = true;\n } else if (!isWs) {\n normalizedPos++;\n inWhitespace = false;\n }\n }\n\n return normalizedPos === targetNormalizedPos ? original.length : -1;\n}\n\n/**\n * Get context lines around a line number.\n */\nfunction getContext(content: string, lineNumber: number, contextLines: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, lineNumber - 1 - contextLines);\n const end = Math.min(lines.length, lineNumber + contextLines);\n\n const contextWithNumbers = lines.slice(start, end).map((line, i) => {\n const num = start + i + 1;\n const marker = num === lineNumber ? \">\" : \" \";\n return `${marker}${String(num).padStart(4)} | ${line}`;\n });\n\n return contextWithNumbers.join(\"\\n\");\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Exception thrown when a path validation fails due to sandbox constraints.\n * This ensures all file operations are restricted to the current working directory.\n */\nexport class PathSandboxException extends Error {\n constructor(inputPath: string, reason: string) {\n super(`Path access denied: ${inputPath}. ${reason}`);\n this.name = \"PathSandboxException\";\n }\n}\n\n/**\n * Validates that a given path is within the current working directory.\n * This prevents directory traversal attacks and ensures all file operations\n * are sandboxed to the CWD and its subdirectories.\n *\n * @param inputPath - Path to validate (can be relative or absolute)\n * @returns The validated absolute path\n * @throws PathSandboxException if the path is outside the CWD\n * @throws Error for other file system errors\n */\nexport function validatePathIsWithinCwd(inputPath: string): string {\n const cwd = process.cwd();\n const resolvedPath = path.resolve(cwd, inputPath);\n\n // Try to get the real path to handle symlinks securely\n let finalPath: string;\n try {\n finalPath = fs.realpathSync(resolvedPath);\n } catch (error) {\n // If path doesn't exist, use the resolved path for validation\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === \"ENOENT\") {\n finalPath = resolvedPath;\n } else {\n // Re-throw other errors (permission denied, etc.)\n throw error;\n }\n }\n\n // Ensure the path is within CWD or is CWD itself\n // Use path.sep to prevent matching partial directory names\n const cwdWithSep = cwd + path.sep;\n if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {\n throw new PathSandboxException(inputPath, \"Path is outside the current working directory\");\n }\n\n return finalPath;\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { createGadget } from \"llmist\";\nimport { validatePathIsWithinCwd } from \"./utils.js\";\n\n/**\n * Represents metadata for a file system entry\n */\ninterface FileEntry {\n name: string;\n relativePath: string;\n type: \"file\" | \"directory\" | \"symlink\";\n size: number;\n modified: number; // Unix epoch seconds\n}\n\n/**\n * Lists all files and directories in a given path with optional recursion.\n * Skips entries that cannot be accessed due to permissions.\n *\n * @param dirPath - Absolute path to the directory\n * @param basePath - Base path for calculating relative paths (defaults to dirPath)\n * @param maxDepth - Maximum depth to recurse (1 = immediate children only)\n * @param currentDepth - Current recursion depth (internal use)\n * @returns Array of file entries with metadata\n */\nfunction listFiles(\n dirPath: string,\n basePath: string = dirPath,\n maxDepth: number = 1,\n currentDepth: number = 1,\n): FileEntry[] {\n const entries: FileEntry[] = [];\n\n try {\n const items = fs.readdirSync(dirPath);\n\n for (const item of items) {\n const fullPath = path.join(dirPath, item);\n const relativePath = path.relative(basePath, fullPath);\n\n try {\n const stats = fs.lstatSync(fullPath);\n let type: \"file\" | \"directory\" | \"symlink\";\n let size: number;\n\n if (stats.isSymbolicLink()) {\n type = \"symlink\";\n size = 0;\n } else if (stats.isDirectory()) {\n type = \"directory\";\n size = 0;\n } else {\n type = \"file\";\n size = stats.size;\n }\n\n entries.push({\n name: item,\n relativePath,\n type,\n size,\n modified: Math.floor(stats.mtime.getTime() / 1000),\n });\n\n // Recurse into directories if we haven't reached max depth\n if (type === \"directory\" && currentDepth < maxDepth) {\n // Validate subdirectory is still within CWD (security check)\n try {\n validatePathIsWithinCwd(fullPath);\n const subEntries = listFiles(fullPath, basePath, maxDepth, currentDepth + 1);\n entries.push(...subEntries);\n } catch {\n // Skip directories outside CWD or inaccessible\n }\n }\n } catch {\n // Skip entries that can't be accessed (permission denied, etc.)\n }\n }\n } catch {\n // If we can't read the directory, return empty array\n return [];\n }\n\n return entries;\n}\n\n/**\n * Formats age from Unix epoch timestamp to human-readable string.\n * Uses compact format: 5m, 2h, 3d, 2w, 4mo, 1y\n *\n * @param epochSeconds - Unix timestamp in seconds\n * @returns Compact age string\n */\nfunction formatAge(epochSeconds: number): string {\n const now = Math.floor(Date.now() / 1000);\n const seconds = now - epochSeconds;\n\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h`;\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d`;\n const weeks = Math.floor(days / 7);\n if (weeks < 4) return `${weeks}w`;\n const months = Math.floor(days / 30);\n if (months < 12) return `${months}mo`;\n const years = Math.floor(days / 365);\n return `${years}y`;\n}\n\n/**\n * Formats file entries as a compact pipe-separated DSL.\n * Format: #T|N|S|A header (Type, Name, Size, Age)\n * Optimized for LLM token efficiency (~70% savings vs table format).\n *\n * @param entries - Array of file entries to format\n * @returns Compact DSL string\n */\nfunction formatEntriesAsString(entries: FileEntry[]): string {\n if (entries.length === 0) {\n return \"#empty\";\n }\n\n // Sort: directories first, then files, then symlinks, alphabetically within each\n const sortedEntries = [...entries].sort((a, b) => {\n const typeOrder = { directory: 0, file: 1, symlink: 2 };\n const typeCompare = typeOrder[a.type] - typeOrder[b.type];\n if (typeCompare !== 0) return typeCompare;\n return a.relativePath.localeCompare(b.relativePath);\n });\n\n // Type code mapping\n const typeCode: Record<FileEntry[\"type\"], string> = {\n directory: \"D\",\n file: \"F\",\n symlink: \"L\",\n };\n\n // URL-encode special chars that would break parsing\n const encodeName = (name: string) => name.replace(/\\|/g, \"%7C\").replace(/\\n/g, \"%0A\");\n\n // Build compact output\n const header = \"#T|N|S|A\";\n const rows = sortedEntries.map(\n (e) => `${typeCode[e.type]}|${encodeName(e.relativePath)}|${e.size}|${formatAge(e.modified)}`,\n );\n\n return [header, ...rows].join(\"\\n\");\n}\n\n/**\n * ListDirectory gadget - Lists files and directories with full metadata.\n * All directory paths are validated to be within the current working directory.\n */\nexport const listDirectory = createGadget({\n name: \"ListDirectory\",\n description:\n \"List files and directories in a directory with full details (names, types, sizes, modification dates). Use maxDepth to explore subdirectories recursively. The directory path must be within the current working directory or its subdirectories.\",\n schema: z.object({\n directoryPath: z.string().default(\".\").describe(\"Path to the directory to list\"),\n maxDepth: z\n .number()\n .int()\n .min(1)\n .max(10)\n .default(3)\n .describe(\n \"Maximum depth to recurse (1 = immediate children only, 2 = include grandchildren, etc.)\",\n ),\n }),\n examples: [\n {\n params: { directoryPath: \".\", maxDepth: 1 },\n output: \"path=. maxDepth=1\\n\\n#T|N|S|A\\nD|src|0|2h\\nD|tests|0|1d\\nF|package.json|2841|3h\",\n comment: \"List current directory\",\n },\n {\n params: { directoryPath: \"src\", maxDepth: 2 },\n output:\n \"path=src maxDepth=2\\n\\n#T|N|S|A\\nD|components|0|1d\\nD|utils|0|2d\\nF|index.ts|512|1h\\nF|components/Button.tsx|1024|3h\",\n comment: \"List src directory recursively\",\n },\n ],\n execute: ({ directoryPath, maxDepth }) => {\n // Validate path is within CWD\n const validatedPath = validatePathIsWithinCwd(directoryPath);\n\n // Verify it's actually a directory\n const stats = fs.statSync(validatedPath);\n if (!stats.isDirectory()) {\n throw new Error(`Path is not a directory: ${directoryPath}`);\n }\n\n // List files and format output\n const entries = listFiles(validatedPath, validatedPath, maxDepth);\n const formattedList = formatEntriesAsString(entries);\n\n // Show params on first line, listing follows\n return `path=${directoryPath} maxDepth=${maxDepth}\\n\\n${formattedList}`;\n },\n});\n","import fs from \"node:fs\";\nimport { z } from \"zod\";\nimport { createGadget } from \"llmist\";\nimport { validatePathIsWithinCwd } from \"./utils.js\";\n\n/**\n * ReadFile gadget - Reads the entire content of a file and returns it as text.\n * All file paths are validated to be within the current working directory.\n */\nexport const readFile = createGadget({\n name: \"ReadFile\",\n description:\n \"Read the entire content of a file and return it as text. The file path must be within the current working directory or its subdirectories.\",\n schema: z.object({\n filePath: z.string().describe(\"Path to the file to read (relative or absolute)\"),\n }),\n examples: [\n {\n params: { filePath: \"package.json\" },\n output: 'path=package.json\\n\\n{\\n \"name\": \"my-project\",\\n \"version\": \"1.0.0\"\\n ...\\n}',\n comment: \"Read a JSON config file\",\n },\n {\n params: { filePath: \"src/index.ts\" },\n output: \"path=src/index.ts\\n\\nexport function main() { ... }\",\n comment: \"Read a source file\",\n },\n ],\n execute: ({ filePath }) => {\n // Validate path is within CWD\n const validatedPath = validatePathIsWithinCwd(filePath);\n\n // Read and return file content\n const content = fs.readFileSync(validatedPath, \"utf-8\");\n\n // Show params on first line, content follows\n return `path=${filePath}\\n\\n${content}`;\n },\n});\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { createGadget } from \"llmist\";\nimport { validatePathIsWithinCwd } from \"./utils.js\";\n\n/**\n * WriteFile gadget - Writes content to a file.\n * Creates parent directories if needed. Overwrites existing files.\n * All file paths are validated to be within the current working directory.\n */\nexport const writeFile = createGadget({\n name: \"WriteFile\",\n description:\n \"Write content to a file. Creates parent directories if needed. Overwrites existing files. The file path must be within the current working directory or its subdirectories.\",\n schema: z.object({\n filePath: z.string().describe(\"Path to the file to write (relative or absolute)\"),\n content: z.string().describe(\"Content to write to the file\"),\n }),\n examples: [\n {\n params: { filePath: \"output.txt\", content: \"Hello, World!\" },\n output: \"path=output.txt\\n\\nWrote 13 bytes\",\n comment: \"Write a simple text file\",\n },\n {\n params: {\n filePath: \"src/server.ts\",\n content: `import { serve } from \"bun\";\n\nconst port = 3000;\n\nserve({\n port,\n fetch: (req) => new Response(\\`Hello from \\${req.url}\\`),\n});\n\nconsole.log(\\`Server running on http://localhost:\\${port}\\`);`,\n },\n output: \"path=src/server.ts\\n\\nWrote 198 bytes (created directory: src)\",\n comment:\n \"Write code with template literals - NO escaping needed inside heredoc (use <<<EOF...EOF)\",\n },\n ],\n execute: ({ filePath, content }) => {\n // Validate path is within CWD\n const validatedPath = validatePathIsWithinCwd(filePath);\n\n // Ensure parent directory exists (create if needed)\n const parentDir = path.dirname(validatedPath);\n let createdDir = false;\n if (!fs.existsSync(parentDir)) {\n // Validate parent dir is also within CWD before creating\n validatePathIsWithinCwd(parentDir);\n fs.mkdirSync(parentDir, { recursive: true });\n createdDir = true;\n }\n\n // Write the file\n fs.writeFileSync(validatedPath, content, \"utf-8\");\n const bytesWritten = Buffer.byteLength(content, \"utf-8\");\n\n // Format output following the established pattern\n const dirNote = createdDir ? ` (created directory: ${path.dirname(filePath)})` : \"\";\n return `path=${filePath}\\n\\nWrote ${bytesWritten} bytes${dirNote}`;\n },\n});\n","import { z } from \"zod\";\nimport { createGadget } from \"llmist\";\nimport { spawn } from \"../spawn.js\";\n\n/**\n * RunCommand gadget - Executes a command with arguments and returns its output.\n *\n * Uses argv array to bypass shell interpretation entirely - arguments are\n * passed directly to the process without any escaping or shell expansion.\n * This allows special characters (quotes, backticks, newlines) to work correctly.\n *\n * Safety should be added externally via the hook system (see example 10).\n *\n * Output format follows the established pattern: `status=N\\n\\n<output>`\n */\nexport const runCommand = createGadget({\n name: \"RunCommand\",\n description:\n \"Execute a command with arguments and return its output. Uses argv array to bypass shell - arguments are passed directly without interpretation. Returns stdout/stderr combined with exit status.\",\n schema: z.object({\n argv: z\n .array(z.string())\n .describe(\"Command and arguments as array (e.g., ['git', 'commit', '-m', 'message'])\"),\n cwd: z\n .string()\n .optional()\n .describe(\"Working directory for the command (default: current directory)\"),\n timeout: z.number().default(30000).describe(\"Timeout in milliseconds (default: 30000)\"),\n }),\n examples: [\n {\n params: { argv: [\"ls\", \"-la\"], timeout: 30000 },\n output:\n \"status=0\\n\\ntotal 24\\ndrwxr-xr-x 5 user staff 160 Nov 27 10:00 .\\ndrwxr-xr-x 3 user staff 96 Nov 27 09:00 ..\\n-rw-r--r-- 1 user staff 1024 Nov 27 10:00 package.json\",\n comment: \"List directory contents with details\",\n },\n {\n params: { argv: [\"echo\", \"Hello World\"], timeout: 30000 },\n output: \"status=0\\n\\nHello World\",\n comment: \"Echo without shell - argument passed directly\",\n },\n {\n params: { argv: [\"cat\", \"nonexistent.txt\"], timeout: 30000 },\n output: \"status=1\\n\\ncat: nonexistent.txt: No such file or directory\",\n comment: \"Command that fails returns non-zero status\",\n },\n {\n params: { argv: [\"pwd\"], cwd: \"/tmp\", timeout: 30000 },\n output: \"status=0\\n\\n/tmp\",\n comment: \"Execute command in a specific directory\",\n },\n {\n params: {\n argv: [\n \"gh\",\n \"pr\",\n \"review\",\n \"123\",\n \"--comment\",\n \"--body\",\n \"Review with `backticks` and 'quotes'\",\n ],\n timeout: 30000,\n },\n output: \"status=0\\n\\n(no output)\",\n comment: \"Complex arguments with special characters - no escaping needed\",\n },\n {\n params: {\n argv: [\n \"gh\",\n \"pr\",\n \"review\",\n \"123\",\n \"--approve\",\n \"--body\",\n \"## Review Summary\\n\\n**Looks good!**\\n\\n- Clean code\\n- Tests pass\",\n ],\n timeout: 30000,\n },\n output: \"status=0\\n\\nApproving pull request #123\",\n comment: \"Multiline body: --body flag and content must be SEPARATE array elements\",\n },\n ],\n execute: async ({ argv, cwd, timeout }) => {\n const workingDir = cwd ?? process.cwd();\n\n if (argv.length === 0) {\n return \"status=1\\n\\nerror: argv array cannot be empty\";\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n try {\n // Spawn process directly without shell - arguments passed as-is\n const proc = spawn(argv, {\n cwd: workingDir,\n stdout: \"pipe\",\n stderr: \"pipe\",\n });\n\n // Create a timeout promise with cleanup\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n proc.kill();\n reject(new Error(`Command timed out after ${timeout}ms`));\n }, timeout);\n });\n\n // Wait for process and consume streams concurrently to prevent deadlock.\n // If we await proc.exited first, large output can fill pipe buffers,\n // causing the process to block on write while we block on exit.\n const [exitCode, stdout, stderr] = await Promise.race([\n Promise.all([\n proc.exited,\n new Response(proc.stdout).text(),\n new Response(proc.stderr).text(),\n ]),\n timeoutPromise,\n ]);\n\n // Clear timeout on normal exit to prevent dangling timer\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Combine output (stdout first, then stderr if any)\n const output = [stdout, stderr].filter(Boolean).join(\"\\n\").trim();\n\n return `status=${exitCode}\\n\\n${output || \"(no output)\"}`;\n } catch (error) {\n // Clear timeout on error path too\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n const message = error instanceof Error ? error.message : String(error);\n return `status=1\\n\\nerror: ${message}`;\n }\n },\n});\n","/**\n * Spawn utility for Node.js child processes.\n *\n * Provides a consistent API for spawning child processes using Node.js\n * child_process module with ReadableStream output.\n *\n * @module cli/spawn\n */\n\nimport { spawn as nodeSpawn } from \"node:child_process\";\nimport type { Readable } from \"node:stream\";\n\n/**\n * Stdio configuration for spawn.\n */\ntype StdioOption = \"pipe\" | \"inherit\" | \"ignore\";\n\n/**\n * Options for spawn function.\n */\nexport interface SpawnOptions {\n /** Working directory for the child process */\n cwd?: string;\n /** stdin configuration */\n stdin?: StdioOption;\n /** stdout configuration */\n stdout?: StdioOption;\n /** stderr configuration */\n stderr?: StdioOption;\n}\n\n/**\n * Writable stdin interface for child processes.\n */\nexport interface SpawnStdin {\n write(data: string): void;\n end(): void;\n}\n\n/**\n * Result from spawn function with ReadableStream output.\n */\nexport interface SpawnResult {\n /** Promise that resolves to exit code when process exits */\n exited: Promise<number>;\n /** stdout stream (null if not piped) */\n stdout: ReadableStream<Uint8Array> | null;\n /** stderr stream (null if not piped) */\n stderr: ReadableStream<Uint8Array> | null;\n /** stdin writer (null if not piped) */\n stdin: SpawnStdin | null;\n /** Kill the child process */\n kill(): void;\n}\n\n/**\n * Convert a Node.js Readable stream to a web ReadableStream.\n */\nfunction nodeStreamToReadableStream(\n nodeStream: Readable | null,\n): ReadableStream<Uint8Array> | null {\n if (!nodeStream) return null;\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n nodeStream.on(\"data\", (chunk: Buffer) => {\n controller.enqueue(new Uint8Array(chunk));\n });\n nodeStream.on(\"end\", () => {\n controller.close();\n });\n nodeStream.on(\"error\", (err) => {\n controller.error(err);\n });\n },\n cancel() {\n nodeStream.destroy();\n },\n });\n}\n\n/**\n * Spawn a child process with ReadableStream output.\n *\n * @param argv - Command and arguments as array (first element is the command)\n * @param options - Spawn options (cwd, stdin, stdout, stderr)\n * @returns SpawnResult with exited promise, streams, and kill function\n */\nexport function spawn(argv: string[], options: SpawnOptions = {}): SpawnResult {\n const [command, ...args] = argv;\n const proc = nodeSpawn(command, args, {\n cwd: options.cwd,\n stdio: [\n options.stdin === \"pipe\" ? \"pipe\" : (options.stdin ?? \"ignore\"),\n options.stdout === \"pipe\" ? \"pipe\" : (options.stdout ?? \"ignore\"),\n options.stderr === \"pipe\" ? \"pipe\" : (options.stderr ?? \"ignore\"),\n ],\n });\n\n // Create exited promise\n const exited = new Promise<number>((resolve, reject) => {\n proc.on(\"exit\", (code) => {\n resolve(code ?? 1);\n });\n proc.on(\"error\", (err) => {\n reject(err);\n });\n });\n\n // Create stdin wrapper\n const stdin: SpawnStdin | null = proc.stdin\n ? {\n write(data: string) {\n proc.stdin?.write(data);\n },\n end() {\n proc.stdin?.end();\n },\n }\n : null;\n\n return {\n exited,\n stdout: nodeStreamToReadableStream(proc.stdout),\n stderr: nodeStreamToReadableStream(proc.stderr),\n stdin,\n kill() {\n proc.kill();\n },\n };\n}\n"],"mappings":"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","names":["fs","path","z","createGadget","fs","path","createGadget","z","fs","z","createGadget","createGadget","z","fs","fs","path","z","createGadget","createGadget","z","path","fs","z","createGadget","createGadget","z"]}
1
+ {"version":3,"sources":["../src/builtins/filesystem/utils.ts","../src/builtins/filesystem/edit-file.ts","../src/builtins/filesystem/editfile/matcher.ts","../src/builtins/filesystem/list-directory.ts","../src/builtins/filesystem/read-file.ts","../src/builtins/filesystem/write-file.ts","../src/builtins/run-command.ts","../src/spawn.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\n/**\n * Exception thrown when a path validation fails due to sandbox constraints.\n * This ensures all file operations are restricted to the current working directory.\n */\nexport class PathSandboxException extends Error {\n constructor(inputPath: string, reason: string) {\n super(`Path access denied: ${inputPath}. ${reason}`);\n this.name = \"PathSandboxException\";\n }\n}\n\n/**\n * Validates that a given path is within the current working directory.\n * This prevents directory traversal attacks and ensures all file operations\n * are sandboxed to the CWD and its subdirectories.\n *\n * @param inputPath - Path to validate (can be relative or absolute)\n * @returns The validated absolute path\n * @throws PathSandboxException if the path is outside the CWD\n * @throws Error for other file system errors\n */\nexport function validatePathIsWithinCwd(inputPath: string): string {\n const cwd = process.cwd();\n const resolvedPath = path.resolve(cwd, inputPath);\n\n // Try to get the real path to handle symlinks securely\n let finalPath: string;\n try {\n finalPath = fs.realpathSync(resolvedPath);\n } catch (error) {\n // If path doesn't exist, use the resolved path for validation\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === \"ENOENT\") {\n finalPath = resolvedPath;\n } else {\n // Re-throw other errors (permission denied, etc.)\n throw error;\n }\n }\n\n // Ensure the path is within CWD or is CWD itself\n // Use path.sep to prevent matching partial directory names\n const cwdWithSep = cwd + path.sep;\n if (!finalPath.startsWith(cwdWithSep) && finalPath !== cwd) {\n throw new PathSandboxException(inputPath, \"Path is outside the current working directory\");\n }\n\n return finalPath;\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { createGadget } from \"llmist\";\nimport { z } from \"zod\";\nimport type { MatchFailure } from \"./editfile/index.js\";\nimport { applyReplacement, findMatch, getMatchFailure } from \"./editfile/index.js\";\nimport { validatePathIsWithinCwd } from \"./utils.js\";\n\n/**\n * EditFile gadget - Edit files using search/replace with layered matching.\n *\n * Uses layered matching strategies: exact -> whitespace -> indentation -> fuzzy\n * This approach reduces edit errors by ~9x (per Aider benchmarks).\n */\n\nfunction formatFailure(\n filePath: string,\n search: string,\n failure: MatchFailure,\n fileContent: string,\n): string {\n const lines: string[] = [\n `path=${filePath} status=failed`,\n \"\",\n `Error: ${failure.reason}`,\n \"\",\n \"SEARCH CONTENT:\",\n \"```\",\n search,\n \"```\",\n ];\n\n if (failure.suggestions.length > 0) {\n lines.push(\"\", \"SUGGESTIONS (similar content found):\");\n for (const suggestion of failure.suggestions) {\n const percent = Math.round(suggestion.similarity * 100);\n lines.push(\n \"\",\n `Line ${suggestion.lineNumber} (${percent}% similar):`,\n \"```\",\n suggestion.content,\n \"```\",\n );\n }\n\n if (failure.nearbyContext) {\n lines.push(\"\", \"CONTEXT:\", failure.nearbyContext);\n }\n }\n\n lines.push(\"\", \"CURRENT FILE CONTENT:\", \"```\", fileContent, \"```\");\n\n return lines.join(\"\\n\");\n}\n\nexport const editFile = createGadget({\n name: \"EditFile\",\n description: `Edit a file by searching for content and replacing it.\n\nUses layered matching strategies (in order):\n1. Exact match - byte-for-byte comparison\n2. Whitespace-insensitive - ignores differences in spaces/tabs\n3. Indentation-preserving - matches structure ignoring leading whitespace\n4. Fuzzy match - similarity-based matching (80% threshold)\n\nFor multiple edits to the same file, call this gadget multiple times.\nEach call provides immediate feedback, allowing you to adjust subsequent edits.`,\n schema: z.object({\n filePath: z.string().describe(\"Path to the file to edit (relative or absolute)\"),\n search: z.string().describe(\"The content to search for in the file\"),\n replace: z.string().describe(\"The content to replace it with (empty string to delete)\"),\n }),\n examples: [\n {\n params: {\n filePath: \"src/config.ts\",\n search: \"const DEBUG = false;\",\n replace: \"const DEBUG = true;\",\n },\n output:\n \"path=src/config.ts status=success strategy=exact lines=5-5\\n\\nReplaced content successfully.\\n\\nUPDATED FILE CONTENT:\\n```\\n// config.ts\\nconst DEBUG = true;\\nexport default { DEBUG };\\n```\",\n comment: \"Simple single-line edit\",\n },\n {\n params: {\n filePath: \"src/utils.ts\",\n search: `function oldHelper() {\n return 1;\n}`,\n replace: `function newHelper() {\n return 2;\n}`,\n },\n output:\n \"path=src/utils.ts status=success strategy=exact lines=10-12\\n\\nReplaced content successfully.\\n\\nUPDATED FILE CONTENT:\\n```\\n// utils.ts\\nfunction newHelper() {\\n return 2;\\n}\\n```\",\n comment: \"Multi-line replacement\",\n },\n {\n params: {\n filePath: \"src/app.ts\",\n search: \"unusedImport\",\n replace: \"\",\n },\n output:\n 'path=src/app.ts status=success strategy=exact lines=3-3\\n\\nReplaced content successfully.\\n\\nUPDATED FILE CONTENT:\\n```\\n// app.ts\\nimport { usedImport } from \"./lib\";\\n```',\n comment: \"Delete content by replacing with empty string\",\n },\n ],\n timeoutMs: 30000,\n execute: ({ filePath, search, replace }) => {\n // Validate search is not empty\n if (search.trim() === \"\") {\n return `path=${filePath} status=error\\n\\nError: Search content cannot be empty.`;\n }\n\n // Validate and resolve path\n let validatedPath: string;\n try {\n validatedPath = validatePathIsWithinCwd(filePath);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return `path=${filePath} status=error\\n\\nError: ${message}`;\n }\n\n // Read file content\n let content: string;\n try {\n content = readFileSync(validatedPath, \"utf-8\");\n } catch (error) {\n const nodeError = error as NodeJS.ErrnoException;\n if (nodeError.code === \"ENOENT\") {\n return `path=${filePath} status=error\\n\\nError: File not found: ${filePath}`;\n }\n const message = error instanceof Error ? error.message : String(error);\n return `path=${filePath} status=error\\n\\nError reading file: ${message}`;\n }\n\n // Find match using layered strategies\n const match = findMatch(content, search);\n\n if (!match) {\n // No match found - provide helpful suggestions\n const failure = getMatchFailure(content, search);\n return formatFailure(filePath, search, failure, content);\n }\n\n // Apply replacement\n const newContent = applyReplacement(content, match, replace);\n\n // Write file\n try {\n writeFileSync(validatedPath, newContent, \"utf-8\");\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return `path=${filePath} status=error\\n\\nError writing file: ${message}`;\n }\n\n return `path=${filePath} status=success strategy=${match.strategy} lines=${match.startLine}-${match.endLine}\\n\\nReplaced content successfully.\\n\\nUPDATED FILE CONTENT:\\n\\`\\`\\`\\n${newContent}\\n\\`\\`\\``;\n },\n});\n","/**\n * Layered matching algorithm for EditFile gadget.\n *\n * Tries strategies in order: exact -> whitespace -> indentation -> fuzzy\n * This approach reduces edit errors by ~9x (per Aider benchmarks).\n */\n\nimport type {\n MatchFailure,\n MatchOptions,\n MatchResult,\n MatchStrategy,\n SuggestionMatch,\n} from \"./types.js\";\n\nconst DEFAULT_OPTIONS: Required<MatchOptions> = {\n fuzzyThreshold: 0.8,\n maxSuggestions: 3,\n contextLines: 5,\n};\n\n/**\n * Find a match for the search string in content using layered strategies.\n *\n * @param content The file content to search in\n * @param search The string to search for\n * @param options Matching options\n * @returns MatchResult if found, null if not found\n */\nexport function findMatch(\n content: string,\n search: string,\n options: MatchOptions = {},\n): MatchResult | null {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Try each strategy in order\n const strategies: Array<{\n name: MatchStrategy;\n fn: (content: string, search: string) => MatchResult | null;\n }> = [\n { name: \"exact\", fn: exactMatch },\n { name: \"whitespace\", fn: whitespaceMatch },\n { name: \"indentation\", fn: indentationMatch },\n { name: \"fuzzy\", fn: (c, s) => fuzzyMatch(c, s, opts.fuzzyThreshold) },\n ];\n\n for (const { name, fn } of strategies) {\n const result = fn(content, search);\n if (result) {\n return { ...result, strategy: name };\n }\n }\n\n return null;\n}\n\n/**\n * Apply replacement to content at the matched location.\n */\nexport function applyReplacement(content: string, match: MatchResult, replacement: string): string {\n return content.slice(0, match.startIndex) + replacement + content.slice(match.endIndex);\n}\n\n/**\n * Get failure details with suggestions when no match is found.\n */\nexport function getMatchFailure(\n content: string,\n search: string,\n options: MatchOptions = {},\n): MatchFailure {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const suggestions = findSuggestions(content, search, opts.maxSuggestions, opts.fuzzyThreshold);\n const nearbyContext =\n suggestions.length > 0 ? getContext(content, suggestions[0].lineNumber, opts.contextLines) : \"\";\n\n return {\n reason: \"Search content not found in file\",\n suggestions,\n nearbyContext,\n };\n}\n\n// ============================================================================\n// Strategy 1: Exact Match\n// ============================================================================\n\nfunction exactMatch(content: string, search: string): MatchResult | null {\n const index = content.indexOf(search);\n if (index === -1) return null;\n\n const { startLine, endLine } = getLineNumbers(content, index, index + search.length);\n\n return {\n found: true,\n strategy: \"exact\",\n confidence: 1.0,\n matchedContent: search,\n startIndex: index,\n endIndex: index + search.length,\n startLine,\n endLine,\n };\n}\n\n// ============================================================================\n// Strategy 2: Whitespace-Insensitive Match\n// ============================================================================\n\nfunction whitespaceMatch(content: string, search: string): MatchResult | null {\n // Normalize runs of spaces/tabs to single space, preserve newlines\n const normalizeWs = (s: string) => s.replace(/[ \\t]+/g, \" \");\n\n const normalizedContent = normalizeWs(content);\n const normalizedSearch = normalizeWs(search);\n\n const normalizedIndex = normalizedContent.indexOf(normalizedSearch);\n if (normalizedIndex === -1) return null;\n\n // Map normalized index back to original content\n const { originalStart, originalEnd } = mapNormalizedToOriginal(\n content,\n normalizedIndex,\n normalizedSearch.length,\n );\n\n const matchedContent = content.slice(originalStart, originalEnd);\n const { startLine, endLine } = getLineNumbers(content, originalStart, originalEnd);\n\n return {\n found: true,\n strategy: \"whitespace\",\n confidence: 0.95,\n matchedContent,\n startIndex: originalStart,\n endIndex: originalEnd,\n startLine,\n endLine,\n };\n}\n\n// ============================================================================\n// Strategy 3: Indentation-Preserving Match\n// ============================================================================\n\nfunction indentationMatch(content: string, search: string): MatchResult | null {\n // Strip leading whitespace from each line for comparison\n const stripIndent = (s: string) =>\n s\n .split(\"\\n\")\n .map((line) => line.trimStart())\n .join(\"\\n\");\n\n const strippedSearch = stripIndent(search);\n const contentLines = content.split(\"\\n\");\n\n // Sliding window search\n const searchLineCount = search.split(\"\\n\").length;\n\n for (let i = 0; i <= contentLines.length - searchLineCount; i++) {\n const windowLines = contentLines.slice(i, i + searchLineCount);\n const strippedWindow = stripIndent(windowLines.join(\"\\n\"));\n\n if (strippedWindow === strippedSearch) {\n // Found match - calculate original indices\n const startIndex = contentLines.slice(0, i).join(\"\\n\").length + (i > 0 ? 1 : 0);\n const matchedContent = windowLines.join(\"\\n\");\n const endIndex = startIndex + matchedContent.length;\n const { startLine, endLine } = getLineNumbers(content, startIndex, endIndex);\n\n return {\n found: true,\n strategy: \"indentation\",\n confidence: 0.9,\n matchedContent,\n startIndex,\n endIndex,\n startLine,\n endLine,\n };\n }\n }\n\n return null;\n}\n\n// ============================================================================\n// Strategy 4: Fuzzy Match\n// ============================================================================\n\nfunction fuzzyMatch(content: string, search: string, threshold: number): MatchResult | null {\n const searchLines = search.split(\"\\n\");\n const contentLines = content.split(\"\\n\");\n\n if (searchLines.length > contentLines.length) return null;\n\n let bestMatch: {\n startLineIndex: number;\n endLineIndex: number;\n similarity: number;\n } | null = null;\n\n // Sliding window\n for (let i = 0; i <= contentLines.length - searchLines.length; i++) {\n const windowLines = contentLines.slice(i, i + searchLines.length);\n const similarity = calculateLineSimilarity(searchLines, windowLines);\n\n if (similarity >= threshold && (!bestMatch || similarity > bestMatch.similarity)) {\n bestMatch = {\n startLineIndex: i,\n endLineIndex: i + searchLines.length,\n similarity,\n };\n }\n }\n\n if (!bestMatch) return null;\n\n // Calculate original indices\n const startIndex =\n contentLines.slice(0, bestMatch.startLineIndex).join(\"\\n\").length +\n (bestMatch.startLineIndex > 0 ? 1 : 0);\n const matchedContent = contentLines\n .slice(bestMatch.startLineIndex, bestMatch.endLineIndex)\n .join(\"\\n\");\n const endIndex = startIndex + matchedContent.length;\n const { startLine, endLine } = getLineNumbers(content, startIndex, endIndex);\n\n return {\n found: true,\n strategy: \"fuzzy\",\n confidence: bestMatch.similarity,\n matchedContent,\n startIndex,\n endIndex,\n startLine,\n endLine,\n };\n}\n\n// ============================================================================\n// Suggestion Finding\n// ============================================================================\n\nfunction findSuggestions(\n content: string,\n search: string,\n maxSuggestions: number,\n minSimilarity: number,\n): SuggestionMatch[] {\n const searchLines = search.split(\"\\n\");\n const contentLines = content.split(\"\\n\");\n const suggestions: Array<{ lineIndex: number; similarity: number; content: string }> = [];\n\n // Reduce threshold for suggestions (show more potential matches)\n const suggestionThreshold = Math.max(0.5, minSimilarity - 0.2);\n\n // Find best matches using sliding window\n for (let i = 0; i <= contentLines.length - searchLines.length; i++) {\n const windowLines = contentLines.slice(i, i + searchLines.length);\n const similarity = calculateLineSimilarity(searchLines, windowLines);\n\n if (similarity >= suggestionThreshold) {\n suggestions.push({\n lineIndex: i,\n similarity,\n content: windowLines.join(\"\\n\"),\n });\n }\n }\n\n // Sort by similarity descending\n suggestions.sort((a, b) => b.similarity - a.similarity);\n\n return suggestions.slice(0, maxSuggestions).map((s) => ({\n content: s.content,\n lineNumber: s.lineIndex + 1, // 1-based\n similarity: s.similarity,\n }));\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Calculate similarity between two line arrays using line-by-line comparison.\n */\nfunction calculateLineSimilarity(a: string[], b: string[]): number {\n if (a.length !== b.length) return 0;\n if (a.length === 0) return 1;\n\n let totalSimilarity = 0;\n let totalWeight = 0;\n\n for (let i = 0; i < a.length; i++) {\n const lineA = a[i];\n const lineB = b[i];\n // Weight by line length (longer lines matter more)\n const weight = Math.max(lineA.length, lineB.length, 1);\n const similarity = stringSimilarity(lineA, lineB);\n totalSimilarity += similarity * weight;\n totalWeight += weight;\n }\n\n return totalWeight > 0 ? totalSimilarity / totalWeight : 0;\n}\n\n/**\n * Calculate similarity between two strings using Levenshtein distance.\n */\nfunction stringSimilarity(a: string, b: string): number {\n if (a === b) return 1;\n if (a.length === 0 || b.length === 0) return 0;\n\n const distance = levenshteinDistance(a, b);\n const maxLen = Math.max(a.length, b.length);\n return 1 - distance / maxLen;\n}\n\n/**\n * Levenshtein distance between two strings.\n */\nfunction levenshteinDistance(a: string, b: string): number {\n const matrix: number[][] = [];\n\n for (let i = 0; i <= b.length; i++) {\n matrix[i] = [i];\n }\n for (let j = 0; j <= a.length; j++) {\n matrix[0][j] = j;\n }\n\n for (let i = 1; i <= b.length; i++) {\n for (let j = 1; j <= a.length; j++) {\n if (b.charAt(i - 1) === a.charAt(j - 1)) {\n matrix[i][j] = matrix[i - 1][j - 1];\n } else {\n matrix[i][j] = Math.min(\n matrix[i - 1][j - 1] + 1, // substitution\n matrix[i][j - 1] + 1, // insertion\n matrix[i - 1][j] + 1, // deletion\n );\n }\n }\n }\n\n return matrix[b.length][a.length];\n}\n\n/**\n * Get 1-based line numbers for a range in content.\n */\nfunction getLineNumbers(\n content: string,\n startIndex: number,\n endIndex: number,\n): { startLine: number; endLine: number } {\n const beforeStart = content.slice(0, startIndex);\n const beforeEnd = content.slice(0, endIndex);\n\n const startLine = (beforeStart.match(/\\n/g) || []).length + 1;\n const endLine = (beforeEnd.match(/\\n/g) || []).length + 1;\n\n return { startLine, endLine };\n}\n\n/**\n * Check if character is horizontal whitespace (space or tab).\n */\nfunction isHorizontalWhitespace(char: string): boolean {\n return char === \" \" || char === \"\\t\";\n}\n\n/**\n * Map normalized string index back to original string.\n * Uses a two-pass approach: first find start, then find end.\n */\nfunction mapNormalizedToOriginal(\n original: string,\n normalizedStart: number,\n normalizedLength: number,\n): { originalStart: number; originalEnd: number } {\n const originalStart = findOriginalIndex(original, normalizedStart);\n const originalEnd = findOriginalIndex(original, normalizedStart + normalizedLength);\n return { originalStart, originalEnd: originalEnd === -1 ? original.length : originalEnd };\n}\n\n/**\n * Find original string index for a given normalized position.\n */\nfunction findOriginalIndex(original: string, targetNormalizedPos: number): number {\n let normalizedPos = 0;\n let inWhitespace = false;\n\n for (let i = 0; i < original.length; i++) {\n if (normalizedPos === targetNormalizedPos) {\n return i;\n }\n\n const isWs = isHorizontalWhitespace(original[i]);\n if (isWs && !inWhitespace) {\n normalizedPos++;\n inWhitespace = true;\n } else if (!isWs) {\n normalizedPos++;\n inWhitespace = false;\n }\n }\n\n return normalizedPos === targetNormalizedPos ? original.length : -1;\n}\n\n/**\n * Get context lines around a line number.\n */\nfunction getContext(content: string, lineNumber: number, contextLines: number): string {\n const lines = content.split(\"\\n\");\n const start = Math.max(0, lineNumber - 1 - contextLines);\n const end = Math.min(lines.length, lineNumber + contextLines);\n\n const contextWithNumbers = lines.slice(start, end).map((line, i) => {\n const num = start + i + 1;\n const marker = num === lineNumber ? \">\" : \" \";\n return `${marker}${String(num).padStart(4)} | ${line}`;\n });\n\n return contextWithNumbers.join(\"\\n\");\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createGadget } from \"llmist\";\nimport { z } from \"zod\";\nimport { validatePathIsWithinCwd } from \"./utils.js\";\n\n/**\n * Represents metadata for a file system entry\n */\ninterface FileEntry {\n name: string;\n relativePath: string;\n type: \"file\" | \"directory\" | \"symlink\";\n size: number;\n modified: number; // Unix epoch seconds\n}\n\n/**\n * Lists all files and directories in a given path with optional recursion.\n * Skips entries that cannot be accessed due to permissions.\n *\n * @param dirPath - Absolute path to the directory\n * @param basePath - Base path for calculating relative paths (defaults to dirPath)\n * @param maxDepth - Maximum depth to recurse (1 = immediate children only)\n * @param currentDepth - Current recursion depth (internal use)\n * @returns Array of file entries with metadata\n */\nfunction listFiles(\n dirPath: string,\n basePath: string = dirPath,\n maxDepth: number = 1,\n currentDepth: number = 1,\n): FileEntry[] {\n const entries: FileEntry[] = [];\n\n try {\n const items = fs.readdirSync(dirPath);\n\n for (const item of items) {\n const fullPath = path.join(dirPath, item);\n const relativePath = path.relative(basePath, fullPath);\n\n try {\n const stats = fs.lstatSync(fullPath);\n let type: \"file\" | \"directory\" | \"symlink\";\n let size: number;\n\n if (stats.isSymbolicLink()) {\n type = \"symlink\";\n size = 0;\n } else if (stats.isDirectory()) {\n type = \"directory\";\n size = 0;\n } else {\n type = \"file\";\n size = stats.size;\n }\n\n entries.push({\n name: item,\n relativePath,\n type,\n size,\n modified: Math.floor(stats.mtime.getTime() / 1000),\n });\n\n // Recurse into directories if we haven't reached max depth\n if (type === \"directory\" && currentDepth < maxDepth) {\n // Validate subdirectory is still within CWD (security check)\n try {\n validatePathIsWithinCwd(fullPath);\n const subEntries = listFiles(fullPath, basePath, maxDepth, currentDepth + 1);\n entries.push(...subEntries);\n } catch {\n // Skip directories outside CWD or inaccessible\n }\n }\n } catch {\n // Skip entries that can't be accessed (permission denied, etc.)\n }\n }\n } catch {\n // If we can't read the directory, return empty array\n return [];\n }\n\n return entries;\n}\n\n/**\n * Formats age from Unix epoch timestamp to human-readable string.\n * Uses compact format: 5m, 2h, 3d, 2w, 4mo, 1y\n *\n * @param epochSeconds - Unix timestamp in seconds\n * @returns Compact age string\n */\nfunction formatAge(epochSeconds: number): string {\n const now = Math.floor(Date.now() / 1000);\n const seconds = now - epochSeconds;\n\n if (seconds < 60) return `${seconds}s`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m`;\n const hours = Math.floor(minutes / 60);\n if (hours < 24) return `${hours}h`;\n const days = Math.floor(hours / 24);\n if (days < 7) return `${days}d`;\n const weeks = Math.floor(days / 7);\n if (weeks < 4) return `${weeks}w`;\n const months = Math.floor(days / 30);\n if (months < 12) return `${months}mo`;\n const years = Math.floor(days / 365);\n return `${years}y`;\n}\n\n/**\n * Formats file entries as a compact pipe-separated DSL.\n * Format: #T|N|S|A header (Type, Name, Size, Age)\n * Optimized for LLM token efficiency (~70% savings vs table format).\n *\n * @param entries - Array of file entries to format\n * @returns Compact DSL string\n */\nfunction formatEntriesAsString(entries: FileEntry[]): string {\n if (entries.length === 0) {\n return \"#empty\";\n }\n\n // Sort: directories first, then files, then symlinks, alphabetically within each\n const sortedEntries = [...entries].sort((a, b) => {\n const typeOrder = { directory: 0, file: 1, symlink: 2 };\n const typeCompare = typeOrder[a.type] - typeOrder[b.type];\n if (typeCompare !== 0) return typeCompare;\n return a.relativePath.localeCompare(b.relativePath);\n });\n\n // Type code mapping\n const typeCode: Record<FileEntry[\"type\"], string> = {\n directory: \"D\",\n file: \"F\",\n symlink: \"L\",\n };\n\n // URL-encode special chars that would break parsing\n const encodeName = (name: string) => name.replace(/\\|/g, \"%7C\").replace(/\\n/g, \"%0A\");\n\n // Build compact output\n const header = \"#T|N|S|A\";\n const rows = sortedEntries.map(\n (e) => `${typeCode[e.type]}|${encodeName(e.relativePath)}|${e.size}|${formatAge(e.modified)}`,\n );\n\n return [header, ...rows].join(\"\\n\");\n}\n\n/**\n * ListDirectory gadget - Lists files and directories with full metadata.\n * All directory paths are validated to be within the current working directory.\n */\nexport const listDirectory = createGadget({\n name: \"ListDirectory\",\n description:\n \"List files and directories in a directory with full details (names, types, sizes, modification dates). Use maxDepth to explore subdirectories recursively. The directory path must be within the current working directory or its subdirectories.\",\n schema: z.object({\n directoryPath: z.string().default(\".\").describe(\"Path to the directory to list\"),\n maxDepth: z\n .number()\n .int()\n .min(1)\n .max(10)\n .default(3)\n .describe(\n \"Maximum depth to recurse (1 = immediate children only, 2 = include grandchildren, etc.)\",\n ),\n }),\n examples: [\n {\n params: { directoryPath: \".\", maxDepth: 1 },\n output: \"path=. maxDepth=1\\n\\n#T|N|S|A\\nD|src|0|2h\\nD|tests|0|1d\\nF|package.json|2841|3h\",\n comment: \"List current directory\",\n },\n {\n params: { directoryPath: \"src\", maxDepth: 2 },\n output:\n \"path=src maxDepth=2\\n\\n#T|N|S|A\\nD|components|0|1d\\nD|utils|0|2d\\nF|index.ts|512|1h\\nF|components/Button.tsx|1024|3h\",\n comment: \"List src directory recursively\",\n },\n ],\n execute: ({ directoryPath, maxDepth }) => {\n // Validate path is within CWD\n const validatedPath = validatePathIsWithinCwd(directoryPath);\n\n // Verify it's actually a directory\n const stats = fs.statSync(validatedPath);\n if (!stats.isDirectory()) {\n throw new Error(`Path is not a directory: ${directoryPath}`);\n }\n\n // List files and format output\n const entries = listFiles(validatedPath, validatedPath, maxDepth);\n const formattedList = formatEntriesAsString(entries);\n\n // Show params on first line, listing follows\n return `path=${directoryPath} maxDepth=${maxDepth}\\n\\n${formattedList}`;\n },\n});\n","import fs from \"node:fs\";\nimport { createGadget } from \"llmist\";\nimport { z } from \"zod\";\nimport { validatePathIsWithinCwd } from \"./utils.js\";\n\n/**\n * ReadFile gadget - Reads the entire content of a file and returns it as text.\n * All file paths are validated to be within the current working directory.\n */\nexport const readFile = createGadget({\n name: \"ReadFile\",\n description:\n \"Read the entire content of a file and return it as text. The file path must be within the current working directory or its subdirectories.\",\n schema: z.object({\n filePath: z.string().describe(\"Path to the file to read (relative or absolute)\"),\n }),\n examples: [\n {\n params: { filePath: \"package.json\" },\n output: 'path=package.json\\n\\n{\\n \"name\": \"my-project\",\\n \"version\": \"1.0.0\"\\n ...\\n}',\n comment: \"Read a JSON config file\",\n },\n {\n params: { filePath: \"src/index.ts\" },\n output: \"path=src/index.ts\\n\\nexport function main() { ... }\",\n comment: \"Read a source file\",\n },\n ],\n execute: ({ filePath }) => {\n // Validate path is within CWD\n const validatedPath = validatePathIsWithinCwd(filePath);\n\n // Read and return file content\n const content = fs.readFileSync(validatedPath, \"utf-8\");\n\n // Show params on first line, content follows\n return `path=${filePath}\\n\\n${content}`;\n },\n});\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { createGadget } from \"llmist\";\nimport { z } from \"zod\";\nimport { validatePathIsWithinCwd } from \"./utils.js\";\n\n/**\n * WriteFile gadget - Writes content to a file.\n * Creates parent directories if needed. Overwrites existing files.\n * All file paths are validated to be within the current working directory.\n */\nexport const writeFile = createGadget({\n name: \"WriteFile\",\n description:\n \"Write content to a file. Creates parent directories if needed. Overwrites existing files. The file path must be within the current working directory or its subdirectories.\",\n schema: z.object({\n filePath: z.string().describe(\"Path to the file to write (relative or absolute)\"),\n content: z.string().describe(\"Content to write to the file\"),\n }),\n examples: [\n {\n params: { filePath: \"output.txt\", content: \"Hello, World!\" },\n output: \"path=output.txt\\n\\nWrote 13 bytes\",\n comment: \"Write a simple text file\",\n },\n {\n params: {\n filePath: \"src/server.ts\",\n content: `import { serve } from \"bun\";\n\nconst port = 3000;\n\nserve({\n port,\n fetch: (req) => new Response(\\`Hello from \\${req.url}\\`),\n});\n\nconsole.log(\\`Server running on http://localhost:\\${port}\\`);`,\n },\n output: \"path=src/server.ts\\n\\nWrote 198 bytes (created directory: src)\",\n comment:\n \"Write code with template literals - NO escaping needed inside heredoc (use <<<EOF...EOF)\",\n },\n ],\n execute: ({ filePath, content }) => {\n // Validate path is within CWD\n const validatedPath = validatePathIsWithinCwd(filePath);\n\n // Ensure parent directory exists (create if needed)\n const parentDir = path.dirname(validatedPath);\n let createdDir = false;\n if (!fs.existsSync(parentDir)) {\n // Validate parent dir is also within CWD before creating\n validatePathIsWithinCwd(parentDir);\n fs.mkdirSync(parentDir, { recursive: true });\n createdDir = true;\n }\n\n // Write the file\n fs.writeFileSync(validatedPath, content, \"utf-8\");\n const bytesWritten = Buffer.byteLength(content, \"utf-8\");\n\n // Format output following the established pattern\n const dirNote = createdDir ? ` (created directory: ${path.dirname(filePath)})` : \"\";\n return `path=${filePath}\\n\\nWrote ${bytesWritten} bytes${dirNote}`;\n },\n});\n","import { createGadget } from \"llmist\";\nimport { z } from \"zod\";\nimport { spawn } from \"../spawn.js\";\n\n/**\n * RunCommand gadget - Executes a command with arguments and returns its output.\n *\n * Uses argv array to bypass shell interpretation entirely - arguments are\n * passed directly to the process without any escaping or shell expansion.\n * This allows special characters (quotes, backticks, newlines) to work correctly.\n *\n * Safety should be added externally via the hook system (see example 10).\n *\n * Output format follows the established pattern: `status=N\\n\\n<output>`\n */\nexport const runCommand = createGadget({\n name: \"RunCommand\",\n description:\n \"Execute a command with arguments and return its output. Uses argv array to bypass shell - arguments are passed directly without interpretation. Returns stdout/stderr combined with exit status.\",\n schema: z.object({\n argv: z\n .array(z.string())\n .describe(\"Command and arguments as array (e.g., ['git', 'commit', '-m', 'message'])\"),\n cwd: z\n .string()\n .optional()\n .describe(\"Working directory for the command (default: current directory)\"),\n timeout: z.number().default(30000).describe(\"Timeout in milliseconds (default: 30000)\"),\n }),\n examples: [\n {\n params: { argv: [\"ls\", \"-la\"], timeout: 30000 },\n output:\n \"status=0\\n\\ntotal 24\\ndrwxr-xr-x 5 user staff 160 Nov 27 10:00 .\\ndrwxr-xr-x 3 user staff 96 Nov 27 09:00 ..\\n-rw-r--r-- 1 user staff 1024 Nov 27 10:00 package.json\",\n comment: \"List directory contents with details\",\n },\n {\n params: { argv: [\"echo\", \"Hello World\"], timeout: 30000 },\n output: \"status=0\\n\\nHello World\",\n comment: \"Echo without shell - argument passed directly\",\n },\n {\n params: { argv: [\"cat\", \"nonexistent.txt\"], timeout: 30000 },\n output: \"status=1\\n\\ncat: nonexistent.txt: No such file or directory\",\n comment: \"Command that fails returns non-zero status\",\n },\n {\n params: { argv: [\"pwd\"], cwd: \"/tmp\", timeout: 30000 },\n output: \"status=0\\n\\n/tmp\",\n comment: \"Execute command in a specific directory\",\n },\n {\n params: {\n argv: [\n \"gh\",\n \"pr\",\n \"review\",\n \"123\",\n \"--comment\",\n \"--body\",\n \"Review with `backticks` and 'quotes'\",\n ],\n timeout: 30000,\n },\n output: \"status=0\\n\\n(no output)\",\n comment: \"Complex arguments with special characters - no escaping needed\",\n },\n {\n params: {\n argv: [\n \"gh\",\n \"pr\",\n \"review\",\n \"123\",\n \"--approve\",\n \"--body\",\n \"## Review Summary\\n\\n**Looks good!**\\n\\n- Clean code\\n- Tests pass\",\n ],\n timeout: 30000,\n },\n output: \"status=0\\n\\nApproving pull request #123\",\n comment: \"Multiline body: --body flag and content must be SEPARATE array elements\",\n },\n ],\n execute: async ({ argv, cwd, timeout }) => {\n const workingDir = cwd ?? process.cwd();\n\n if (argv.length === 0) {\n return \"status=1\\n\\nerror: argv array cannot be empty\";\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n try {\n // Spawn process directly without shell - arguments passed as-is\n const proc = spawn(argv, {\n cwd: workingDir,\n stdout: \"pipe\",\n stderr: \"pipe\",\n });\n\n // Create a timeout promise with cleanup\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n proc.kill();\n reject(new Error(`Command timed out after ${timeout}ms`));\n }, timeout);\n });\n\n // Wait for process and consume streams concurrently to prevent deadlock.\n // If we await proc.exited first, large output can fill pipe buffers,\n // causing the process to block on write while we block on exit.\n const [exitCode, stdout, stderr] = await Promise.race([\n Promise.all([\n proc.exited,\n new Response(proc.stdout).text(),\n new Response(proc.stderr).text(),\n ]),\n timeoutPromise,\n ]);\n\n // Clear timeout on normal exit to prevent dangling timer\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n // Combine output (stdout first, then stderr if any)\n const output = [stdout, stderr].filter(Boolean).join(\"\\n\").trim();\n\n return `status=${exitCode}\\n\\n${output || \"(no output)\"}`;\n } catch (error) {\n // Clear timeout on error path too\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n const message = error instanceof Error ? error.message : String(error);\n return `status=1\\n\\nerror: ${message}`;\n }\n },\n});\n","/**\n * Spawn utility for Node.js child processes.\n *\n * Provides a consistent API for spawning child processes using Node.js\n * child_process module with ReadableStream output.\n *\n * @module cli/spawn\n */\n\nimport { spawn as nodeSpawn } from \"node:child_process\";\nimport type { Readable } from \"node:stream\";\n\n/**\n * Stdio configuration for spawn.\n */\ntype StdioOption = \"pipe\" | \"inherit\" | \"ignore\";\n\n/**\n * Options for spawn function.\n */\nexport interface SpawnOptions {\n /** Working directory for the child process */\n cwd?: string;\n /** stdin configuration */\n stdin?: StdioOption;\n /** stdout configuration */\n stdout?: StdioOption;\n /** stderr configuration */\n stderr?: StdioOption;\n}\n\n/**\n * Writable stdin interface for child processes.\n */\nexport interface SpawnStdin {\n write(data: string): void;\n end(): void;\n}\n\n/**\n * Result from spawn function with ReadableStream output.\n */\nexport interface SpawnResult {\n /** Promise that resolves to exit code when process exits */\n exited: Promise<number>;\n /** stdout stream (null if not piped) */\n stdout: ReadableStream<Uint8Array> | null;\n /** stderr stream (null if not piped) */\n stderr: ReadableStream<Uint8Array> | null;\n /** stdin writer (null if not piped) */\n stdin: SpawnStdin | null;\n /** Kill the child process */\n kill(): void;\n}\n\n/**\n * Convert a Node.js Readable stream to a web ReadableStream.\n */\nfunction nodeStreamToReadableStream(\n nodeStream: Readable | null,\n): ReadableStream<Uint8Array> | null {\n if (!nodeStream) return null;\n\n return new ReadableStream<Uint8Array>({\n start(controller) {\n nodeStream.on(\"data\", (chunk: Buffer) => {\n controller.enqueue(new Uint8Array(chunk));\n });\n nodeStream.on(\"end\", () => {\n controller.close();\n });\n nodeStream.on(\"error\", (err) => {\n controller.error(err);\n });\n },\n cancel() {\n nodeStream.destroy();\n },\n });\n}\n\n/**\n * Spawn a child process with ReadableStream output.\n *\n * @param argv - Command and arguments as array (first element is the command)\n * @param options - Spawn options (cwd, stdin, stdout, stderr)\n * @returns SpawnResult with exited promise, streams, and kill function\n */\nexport function spawn(argv: string[], options: SpawnOptions = {}): SpawnResult {\n const [command, ...args] = argv;\n const proc = nodeSpawn(command, args, {\n cwd: options.cwd,\n stdio: [\n options.stdin === \"pipe\" ? \"pipe\" : (options.stdin ?? \"ignore\"),\n options.stdout === \"pipe\" ? \"pipe\" : (options.stdout ?? \"ignore\"),\n options.stderr === \"pipe\" ? \"pipe\" : (options.stderr ?? \"ignore\"),\n ],\n });\n\n // Create exited promise\n const exited = new Promise<number>((resolve, reject) => {\n proc.on(\"exit\", (code) => {\n resolve(code ?? 1);\n });\n proc.on(\"error\", (err) => {\n reject(err);\n });\n });\n\n // Create stdin wrapper\n const stdin: SpawnStdin | null = proc.stdin\n ? {\n write(data: string) {\n proc.stdin?.write(data);\n },\n end() {\n proc.stdin?.end();\n },\n }\n : null;\n\n return {\n exited,\n stdout: nodeStreamToReadableStream(proc.stdout),\n stderr: nodeStreamToReadableStream(proc.stderr),\n stdin,\n kill() {\n proc.kill();\n },\n };\n}\n"],"mappings":"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","names":["fs","path","createGadget","z","fs","path","createGadget","z","fs","createGadget","z","createGadget","z","fs","fs","path","createGadget","z","createGadget","z","path","fs","createGadget","z","createGadget","z"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmist/cli",
3
- "version": "15.2.0",
3
+ "version": "15.2.2",
4
4
  "description": "CLI for llmist - run LLM agents from the command line",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -56,7 +56,7 @@
56
56
  "node": ">=22.0.0"
57
57
  },
58
58
  "dependencies": {
59
- "llmist": "^15.2.0",
59
+ "llmist": "^15.2.2",
60
60
  "@unblessed/node": "^1.0.0-alpha.23",
61
61
  "chalk": "^5.6.2",
62
62
  "commander": "^12.1.0",
@@ -70,7 +70,7 @@
70
70
  "zod": "^4.1.12"
71
71
  },
72
72
  "devDependencies": {
73
- "@llmist/testing": "^15.2.0",
73
+ "@llmist/testing": "^15.2.2",
74
74
  "@types/diff": "^8.0.0",
75
75
  "@types/js-yaml": "^4.0.9",
76
76
  "@types/marked-terminal": "^6.1.1",
@@ -7,16 +7,9 @@
7
7
  *
8
8
  * Fix: Create symlink dist/data -> ../data
9
9
  */
10
- import {
11
- existsSync,
12
- symlinkSync,
13
- mkdirSync,
14
- readdirSync,
15
- copyFileSync,
16
- statSync,
17
- } from "node:fs";
18
- import { join, dirname } from "node:path";
10
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, symlinkSync } from "node:fs";
19
11
  import { createRequire } from "node:module";
12
+ import { dirname, join } from "node:path";
20
13
 
21
14
  // Use require.resolve to find @unblessed/core regardless of hoisting
22
15
  const require = createRequire(import.meta.url);
@@ -27,9 +20,7 @@ try {
27
20
  const coreDir = dirname(coreIndexPath);
28
21
 
29
22
  // Handle both dist/index.js and top-level index.js layouts
30
- const packageRoot = coreDir.endsWith("dist")
31
- ? dirname(coreDir)
32
- : coreDir;
23
+ const packageRoot = coreDir.endsWith("dist") ? dirname(coreDir) : coreDir;
33
24
 
34
25
  const distDir = join(packageRoot, "dist");
35
26
  const dataDir = join(packageRoot, "data");