@llmist/cli 16.0.1 → 16.0.3

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.js.map CHANGED
@@ -1 +1 @@
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 maxConcurrent: 1, // Sequential execution to prevent race conditions\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 maxConcurrent: 1, // Sequential execution to prevent race conditions\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":";AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAMV,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YAAY,WAAmB,QAAgB;AAC7C,UAAM,uBAAuB,SAAS,KAAK,MAAM,EAAE;AACnD,SAAK,OAAO;AAAA,EACd;AACF;AAYO,SAAS,wBAAwB,WAA2B;AACjE,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,eAAe,KAAK,QAAQ,KAAK,SAAS;AAGhD,MAAI;AACJ,MAAI;AACF,gBAAY,GAAG,aAAa,YAAY;AAAA,EAC1C,SAAS,OAAO;AAEd,UAAM,YAAY;AAClB,QAAI,UAAU,SAAS,UAAU;AAC/B,kBAAY;AAAA,IACd,OAAO;AAEL,YAAM;AAAA,IACR;AAAA,EACF;AAIA,QAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,CAAC,UAAU,WAAW,UAAU,KAAK,cAAc,KAAK;AAC1D,UAAM,IAAI,qBAAqB,WAAW,+CAA+C;AAAA,EAC3F;AAEA,SAAO;AACT;;;ACnDA,SAAS,cAAc,qBAAqB;AAC5C,SAAS,oBAAoB;AAC7B,SAAS,SAAS;;;ACalB,IAAM,kBAA0C;AAAA,EAC9C,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,cAAc;AAChB;AAUO,SAAS,UACd,SACA,QACA,UAAwB,CAAC,GACL;AACpB,QAAM,OAAO,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AAG9C,QAAM,aAGD;AAAA,IACH,EAAE,MAAM,SAAS,IAAI,WAAW;AAAA,IAChC,EAAE,MAAM,cAAc,IAAI,gBAAgB;AAAA,IAC1C,EAAE,MAAM,eAAe,IAAI,iBAAiB;AAAA,IAC5C,EAAE,MAAM,SAAS,IAAI,CAAC,GAAG,MAAM,WAAW,GAAG,GAAG,KAAK,cAAc,EAAE;AAAA,EACvE;AAEA,aAAW,EAAE,MAAM,GAAG,KAAK,YAAY;AACrC,UAAM,SAAS,GAAG,SAAS,MAAM;AACjC,QAAI,QAAQ;AACV,aAAO,EAAE,GAAG,QAAQ,UAAU,KAAK;AAAA,IACrC;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,iBAAiB,SAAiB,OAAoB,aAA6B;AACjG,SAAO,QAAQ,MAAM,GAAG,MAAM,UAAU,IAAI,cAAc,QAAQ,MAAM,MAAM,QAAQ;AACxF;AAKO,SAAS,gBACd,SACA,QACA,UAAwB,CAAC,GACX;AACd,QAAM,OAAO,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AAC9C,QAAM,cAAc,gBAAgB,SAAS,QAAQ,KAAK,gBAAgB,KAAK,cAAc;AAC7F,QAAM,gBACJ,YAAY,SAAS,IAAI,WAAW,SAAS,YAAY,CAAC,EAAE,YAAY,KAAK,YAAY,IAAI;AAE/F,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,EACF;AACF;AAMA,SAAS,WAAW,SAAiB,QAAoC;AACvE,QAAM,QAAQ,QAAQ,QAAQ,MAAM;AACpC,MAAI,UAAU,GAAI,QAAO;AAEzB,QAAM,EAAE,WAAW,QAAQ,IAAI,eAAe,SAAS,OAAO,QAAQ,OAAO,MAAM;AAEnF,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,UAAU,QAAQ,OAAO;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACF;AAMA,SAAS,gBAAgB,SAAiB,QAAoC;AAE5E,QAAM,cAAc,CAAC,MAAc,EAAE,QAAQ,WAAW,GAAG;AAE3D,QAAM,oBAAoB,YAAY,OAAO;AAC7C,QAAM,mBAAmB,YAAY,MAAM;AAE3C,QAAM,kBAAkB,kBAAkB,QAAQ,gBAAgB;AAClE,MAAI,oBAAoB,GAAI,QAAO;AAGnC,QAAM,EAAE,eAAe,YAAY,IAAI;AAAA,IACrC;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,EACnB;AAEA,QAAM,iBAAiB,QAAQ,MAAM,eAAe,WAAW;AAC/D,QAAM,EAAE,WAAW,QAAQ,IAAI,eAAe,SAAS,eAAe,WAAW;AAEjF,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF;AACF;AAMA,SAAS,iBAAiB,SAAiB,QAAoC;AAE7E,QAAM,cAAc,CAAC,MACnB,EACG,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,UAAU,CAAC,EAC9B,KAAK,IAAI;AAEd,QAAM,iBAAiB,YAAY,MAAM;AACzC,QAAM,eAAe,QAAQ,MAAM,IAAI;AAGvC,QAAM,kBAAkB,OAAO,MAAM,IAAI,EAAE;AAE3C,WAAS,IAAI,GAAG,KAAK,aAAa,SAAS,iBAAiB,KAAK;AAC/D,UAAM,cAAc,aAAa,MAAM,GAAG,IAAI,eAAe;AAC7D,UAAM,iBAAiB,YAAY,YAAY,KAAK,IAAI,CAAC;AAEzD,QAAI,mBAAmB,gBAAgB;AAErC,YAAM,aAAa,aAAa,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,EAAE,UAAU,IAAI,IAAI,IAAI;AAC7E,YAAM,iBAAiB,YAAY,KAAK,IAAI;AAC5C,YAAM,WAAW,aAAa,eAAe;AAC7C,YAAM,EAAE,WAAW,QAAQ,IAAI,eAAe,SAAS,YAAY,QAAQ;AAE3E,aAAO;AAAA,QACL,OAAO;AAAA,QACP,UAAU;AAAA,QACV,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,WAAW,SAAiB,QAAgB,WAAuC;AAC1F,QAAM,cAAc,OAAO,MAAM,IAAI;AACrC,QAAM,eAAe,QAAQ,MAAM,IAAI;AAEvC,MAAI,YAAY,SAAS,aAAa,OAAQ,QAAO;AAErD,MAAI,YAIO;AAGX,WAAS,IAAI,GAAG,KAAK,aAAa,SAAS,YAAY,QAAQ,KAAK;AAClE,UAAM,cAAc,aAAa,MAAM,GAAG,IAAI,YAAY,MAAM;AAChE,UAAM,aAAa,wBAAwB,aAAa,WAAW;AAEnE,QAAI,cAAc,cAAc,CAAC,aAAa,aAAa,UAAU,aAAa;AAChF,kBAAY;AAAA,QACV,gBAAgB;AAAA,QAChB,cAAc,IAAI,YAAY;AAAA,QAC9B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,UAAW,QAAO;AAGvB,QAAM,aACJ,aAAa,MAAM,GAAG,UAAU,cAAc,EAAE,KAAK,IAAI,EAAE,UAC1D,UAAU,iBAAiB,IAAI,IAAI;AACtC,QAAM,iBAAiB,aACpB,MAAM,UAAU,gBAAgB,UAAU,YAAY,EACtD,KAAK,IAAI;AACZ,QAAM,WAAW,aAAa,eAAe;AAC7C,QAAM,EAAE,WAAW,QAAQ,IAAI,eAAe,SAAS,YAAY,QAAQ;AAE3E,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,YAAY,UAAU;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMA,SAAS,gBACP,SACA,QACA,gBACA,eACmB;AACnB,QAAM,cAAc,OAAO,MAAM,IAAI;AACrC,QAAM,eAAe,QAAQ,MAAM,IAAI;AACvC,QAAM,cAAiF,CAAC;AAGxF,QAAM,sBAAsB,KAAK,IAAI,KAAK,gBAAgB,GAAG;AAG7D,WAAS,IAAI,GAAG,KAAK,aAAa,SAAS,YAAY,QAAQ,KAAK;AAClE,UAAM,cAAc,aAAa,MAAM,GAAG,IAAI,YAAY,MAAM;AAChE,UAAM,aAAa,wBAAwB,aAAa,WAAW;AAEnE,QAAI,cAAc,qBAAqB;AACrC,kBAAY,KAAK;AAAA,QACf,WAAW;AAAA,QACX;AAAA,QACA,SAAS,YAAY,KAAK,IAAI;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,EACF;AAGA,cAAY,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU;AAEtD,SAAO,YAAY,MAAM,GAAG,cAAc,EAAE,IAAI,CAAC,OAAO;AAAA,IACtD,SAAS,EAAE;AAAA,IACX,YAAY,EAAE,YAAY;AAAA;AAAA,IAC1B,YAAY,EAAE;AAAA,EAChB,EAAE;AACJ;AASA,SAAS,wBAAwB,GAAa,GAAqB;AACjE,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,MAAI,EAAE,WAAW,EAAG,QAAO;AAE3B,MAAI,kBAAkB;AACtB,MAAI,cAAc;AAElB,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,UAAM,QAAQ,EAAE,CAAC;AACjB,UAAM,QAAQ,EAAE,CAAC;AAEjB,UAAM,SAAS,KAAK,IAAI,MAAM,QAAQ,MAAM,QAAQ,CAAC;AACrD,UAAM,aAAa,iBAAiB,OAAO,KAAK;AAChD,uBAAmB,aAAa;AAChC,mBAAe;AAAA,EACjB;AAEA,SAAO,cAAc,IAAI,kBAAkB,cAAc;AAC3D;AAKA,SAAS,iBAAiB,GAAW,GAAmB;AACtD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EAAG,QAAO;AAE7C,QAAM,WAAW,oBAAoB,GAAG,CAAC;AACzC,QAAM,SAAS,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AAC1C,SAAO,IAAI,WAAW;AACxB;AAKA,SAAS,oBAAoB,GAAW,GAAmB;AACzD,QAAM,SAAqB,CAAC;AAE5B,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,WAAO,CAAC,IAAI,CAAC,CAAC;AAAA,EAChB;AACA,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,WAAO,CAAC,EAAE,CAAC,IAAI;AAAA,EACjB;AAEA,WAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,aAAS,IAAI,GAAG,KAAK,EAAE,QAAQ,KAAK;AAClC,UAAI,EAAE,OAAO,IAAI,CAAC,MAAM,EAAE,OAAO,IAAI,CAAC,GAAG;AACvC,eAAO,CAAC,EAAE,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC;AAAA,MACpC,OAAO;AACL,eAAO,CAAC,EAAE,CAAC,IAAI,KAAK;AAAA,UAClB,OAAO,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI;AAAA;AAAA,UACvB,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI;AAAA;AAAA,UACnB,OAAO,IAAI,CAAC,EAAE,CAAC,IAAI;AAAA;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM;AAClC;AAKA,SAAS,eACP,SACA,YACA,UACwC;AACxC,QAAM,cAAc,QAAQ,MAAM,GAAG,UAAU;AAC/C,QAAM,YAAY,QAAQ,MAAM,GAAG,QAAQ;AAE3C,QAAM,aAAa,YAAY,MAAM,KAAK,KAAK,CAAC,GAAG,SAAS;AAC5D,QAAM,WAAW,UAAU,MAAM,KAAK,KAAK,CAAC,GAAG,SAAS;AAExD,SAAO,EAAE,WAAW,QAAQ;AAC9B;AAKA,SAAS,uBAAuB,MAAuB;AACrD,SAAO,SAAS,OAAO,SAAS;AAClC;AAMA,SAAS,wBACP,UACA,iBACA,kBACgD;AAChD,QAAM,gBAAgB,kBAAkB,UAAU,eAAe;AACjE,QAAM,cAAc,kBAAkB,UAAU,kBAAkB,gBAAgB;AAClF,SAAO,EAAE,eAAe,aAAa,gBAAgB,KAAK,SAAS,SAAS,YAAY;AAC1F;AAKA,SAAS,kBAAkB,UAAkB,qBAAqC;AAChF,MAAI,gBAAgB;AACpB,MAAI,eAAe;AAEnB,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,QAAI,kBAAkB,qBAAqB;AACzC,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,uBAAuB,SAAS,CAAC,CAAC;AAC/C,QAAI,QAAQ,CAAC,cAAc;AACzB;AACA,qBAAe;AAAA,IACjB,WAAW,CAAC,MAAM;AAChB;AACA,qBAAe;AAAA,IACjB;AAAA,EACF;AAEA,SAAO,kBAAkB,sBAAsB,SAAS,SAAS;AACnE;AAKA,SAAS,WAAW,SAAiB,YAAoB,cAA8B;AACrF,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,QAAQ,KAAK,IAAI,GAAG,aAAa,IAAI,YAAY;AACvD,QAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,aAAa,YAAY;AAE5D,QAAM,qBAAqB,MAAM,MAAM,OAAO,GAAG,EAAE,IAAI,CAAC,MAAM,MAAM;AAClE,UAAM,MAAM,QAAQ,IAAI;AACxB,UAAM,SAAS,QAAQ,aAAa,MAAM;AAC1C,WAAO,GAAG,MAAM,GAAG,OAAO,GAAG,EAAE,SAAS,CAAC,CAAC,MAAM,IAAI;AAAA,EACtD,CAAC;AAED,SAAO,mBAAmB,KAAK,IAAI;AACrC;;;AD/ZA,SAAS,cACP,UACA,QACA,SACA,aACQ;AACR,QAAM,QAAkB;AAAA,IACtB,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,UAAU,QAAQ,MAAM;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,QAAQ,YAAY,SAAS,GAAG;AAClC,UAAM,KAAK,IAAI,sCAAsC;AACrD,eAAW,cAAc,QAAQ,aAAa;AAC5C,YAAM,UAAU,KAAK,MAAM,WAAW,aAAa,GAAG;AACtD,YAAM;AAAA,QACJ;AAAA,QACA,QAAQ,WAAW,UAAU,KAAK,OAAO;AAAA,QACzC;AAAA,QACA,WAAW;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAEA,QAAI,QAAQ,eAAe;AACzB,YAAM,KAAK,IAAI,YAAY,QAAQ,aAAa;AAAA,IAClD;AAAA,EACF;AAEA,QAAM,KAAK,IAAI,yBAAyB,OAAO,aAAa,KAAK;AAEjE,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,IAAM,WAAW,aAAa;AAAA,EACnC,MAAM;AAAA,EACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUb,eAAe;AAAA;AAAA,EACf,QAAQ,EAAE,OAAO;AAAA,IACf,UAAU,EAAE,OAAO,EAAE,SAAS,iDAAiD;AAAA,IAC/E,QAAQ,EAAE,OAAO,EAAE,SAAS,uCAAuC;AAAA,IACnE,SAAS,EAAE,OAAO,EAAE,SAAS,yDAAyD;AAAA,EACxF,CAAC;AAAA,EACD,UAAU;AAAA,IACR;AAAA,MACE,QAAQ;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,SAAS;AAAA,MACX;AAAA,MACA,QACE;AAAA,MACF,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA;AAAA;AAAA,QAGR,SAAS;AAAA;AAAA;AAAA,MAGX;AAAA,MACA,QACE;AAAA,MACF,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,SAAS;AAAA,MACX;AAAA,MACA,QACE;AAAA,MACF,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,WAAW;AAAA,EACX,SAAS,CAAC,EAAE,UAAU,QAAQ,QAAQ,MAAM;AAE1C,QAAI,OAAO,KAAK,MAAM,IAAI;AACxB,aAAO,QAAQ,QAAQ;AAAA;AAAA;AAAA,IACzB;AAGA,QAAI;AACJ,QAAI;AACF,sBAAgB,wBAAwB,QAAQ;AAAA,IAClD,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,aAAO,QAAQ,QAAQ;AAAA;AAAA,SAA2B,OAAO;AAAA,IAC3D;AAGA,QAAI;AACJ,QAAI;AACF,gBAAU,aAAa,eAAe,OAAO;AAAA,IAC/C,SAAS,OAAO;AACd,YAAM,YAAY;AAClB,UAAI,UAAU,SAAS,UAAU;AAC/B,eAAO,QAAQ,QAAQ;AAAA;AAAA,yBAA2C,QAAQ;AAAA,MAC5E;AACA,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,aAAO,QAAQ,QAAQ;AAAA;AAAA,sBAAwC,OAAO;AAAA,IACxE;AAGA,UAAM,QAAQ,UAAU,SAAS,MAAM;AAEvC,QAAI,CAAC,OAAO;AAEV,YAAM,UAAU,gBAAgB,SAAS,MAAM;AAC/C,aAAO,cAAc,UAAU,QAAQ,SAAS,OAAO;AAAA,IACzD;AAGA,UAAM,aAAa,iBAAiB,SAAS,OAAO,OAAO;AAG3D,QAAI;AACF,oBAAc,eAAe,YAAY,OAAO;AAAA,IAClD,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,aAAO,QAAQ,QAAQ;AAAA;AAAA,sBAAwC,OAAO;AAAA,IACxE;AAEA,WAAO,QAAQ,QAAQ,4BAA4B,MAAM,QAAQ,UAAU,MAAM,SAAS,IAAI,MAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAAwE,UAAU;AAAA;AAAA,EAC/L;AACF,CAAC;;;AE/JD,OAAOA,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,KAAAC,UAAS;AAwBlB,SAAS,UACP,SACA,WAAmB,SACnB,WAAmB,GACnB,eAAuB,GACV;AACb,QAAM,UAAuB,CAAC;AAE9B,MAAI;AACF,UAAM,QAAQC,IAAG,YAAY,OAAO;AAEpC,eAAW,QAAQ,OAAO;AACxB,YAAM,WAAWC,MAAK,KAAK,SAAS,IAAI;AACxC,YAAM,eAAeA,MAAK,SAAS,UAAU,QAAQ;AAErD,UAAI;AACF,cAAM,QAAQD,IAAG,UAAU,QAAQ;AACnC,YAAI;AACJ,YAAI;AAEJ,YAAI,MAAM,eAAe,GAAG;AAC1B,iBAAO;AACP,iBAAO;AAAA,QACT,WAAW,MAAM,YAAY,GAAG;AAC9B,iBAAO;AACP,iBAAO;AAAA,QACT,OAAO;AACL,iBAAO;AACP,iBAAO,MAAM;AAAA,QACf;AAEA,gBAAQ,KAAK;AAAA,UACX,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU,KAAK,MAAM,MAAM,MAAM,QAAQ,IAAI,GAAI;AAAA,QACnD,CAAC;AAGD,YAAI,SAAS,eAAe,eAAe,UAAU;AAEnD,cAAI;AACF,oCAAwB,QAAQ;AAChC,kBAAM,aAAa,UAAU,UAAU,UAAU,UAAU,eAAe,CAAC;AAC3E,oBAAQ,KAAK,GAAG,UAAU;AAAA,UAC5B,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAEN,WAAO,CAAC;AAAA,EACV;AAEA,SAAO;AACT;AASA,SAAS,UAAU,cAA8B;AAC/C,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,UAAU,MAAM;AAEtB,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,MAAI,QAAQ,GAAI,QAAO,GAAG,KAAK;AAC/B,QAAM,OAAO,KAAK,MAAM,QAAQ,EAAE;AAClC,MAAI,OAAO,EAAG,QAAO,GAAG,IAAI;AAC5B,QAAM,QAAQ,KAAK,MAAM,OAAO,CAAC;AACjC,MAAI,QAAQ,EAAG,QAAO,GAAG,KAAK;AAC9B,QAAM,SAAS,KAAK,MAAM,OAAO,EAAE;AACnC,MAAI,SAAS,GAAI,QAAO,GAAG,MAAM;AACjC,QAAM,QAAQ,KAAK,MAAM,OAAO,GAAG;AACnC,SAAO,GAAG,KAAK;AACjB;AAUA,SAAS,sBAAsB,SAA8B;AAC3D,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,EACT;AAGA,QAAM,gBAAgB,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM;AAChD,UAAM,YAAY,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,EAAE;AACtD,UAAM,cAAc,UAAU,EAAE,IAAI,IAAI,UAAU,EAAE,IAAI;AACxD,QAAI,gBAAgB,EAAG,QAAO;AAC9B,WAAO,EAAE,aAAa,cAAc,EAAE,YAAY;AAAA,EACpD,CAAC;AAGD,QAAM,WAA8C;AAAA,IAClD,WAAW;AAAA,IACX,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAGA,QAAM,aAAa,CAAC,SAAiB,KAAK,QAAQ,OAAO,KAAK,EAAE,QAAQ,OAAO,KAAK;AAGpF,QAAM,SAAS;AACf,QAAM,OAAO,cAAc;AAAA,IACzB,CAAC,MAAM,GAAG,SAAS,EAAE,IAAI,CAAC,IAAI,WAAW,EAAE,YAAY,CAAC,IAAI,EAAE,IAAI,IAAI,UAAU,EAAE,QAAQ,CAAC;AAAA,EAC7F;AAEA,SAAO,CAAC,QAAQ,GAAG,IAAI,EAAE,KAAK,IAAI;AACpC;AAMO,IAAM,gBAAgBE,cAAa;AAAA,EACxC,MAAM;AAAA,EACN,aACE;AAAA,EACF,QAAQC,GAAE,OAAO;AAAA,IACf,eAAeA,GAAE,OAAO,EAAE,QAAQ,GAAG,EAAE,SAAS,+BAA+B;AAAA,IAC/E,UAAUA,GACP,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,EAAE,EACN,QAAQ,CAAC,EACT;AAAA,MACC;AAAA,IACF;AAAA,EACJ,CAAC;AAAA,EACD,UAAU;AAAA,IACR;AAAA,MACE,QAAQ,EAAE,eAAe,KAAK,UAAU,EAAE;AAAA,MAC1C,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,eAAe,OAAO,UAAU,EAAE;AAAA,MAC5C,QACE;AAAA,MACF,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,SAAS,CAAC,EAAE,eAAe,SAAS,MAAM;AAExC,UAAM,gBAAgB,wBAAwB,aAAa;AAG3D,UAAM,QAAQH,IAAG,SAAS,aAAa;AACvC,QAAI,CAAC,MAAM,YAAY,GAAG;AACxB,YAAM,IAAI,MAAM,4BAA4B,aAAa,EAAE;AAAA,IAC7D;AAGA,UAAM,UAAU,UAAU,eAAe,eAAe,QAAQ;AAChE,UAAM,gBAAgB,sBAAsB,OAAO;AAGnD,WAAO,QAAQ,aAAa,aAAa,QAAQ;AAAA;AAAA,EAAO,aAAa;AAAA,EACvE;AACF,CAAC;;;AC7MD,OAAOI,SAAQ;AACf,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,KAAAC,UAAS;AAOX,IAAM,WAAWC,cAAa;AAAA,EACnC,MAAM;AAAA,EACN,aACE;AAAA,EACF,QAAQC,GAAE,OAAO;AAAA,IACf,UAAUA,GAAE,OAAO,EAAE,SAAS,iDAAiD;AAAA,EACjF,CAAC;AAAA,EACD,UAAU;AAAA,IACR;AAAA,MACE,QAAQ,EAAE,UAAU,eAAe;AAAA,MACnC,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,UAAU,eAAe;AAAA,MACnC,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,SAAS,CAAC,EAAE,SAAS,MAAM;AAEzB,UAAM,gBAAgB,wBAAwB,QAAQ;AAGtD,UAAM,UAAUC,IAAG,aAAa,eAAe,OAAO;AAGtD,WAAO,QAAQ,QAAQ;AAAA;AAAA,EAAO,OAAO;AAAA,EACvC;AACF,CAAC;;;ACtCD,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,KAAAC,UAAS;AAQX,IAAM,YAAYC,cAAa;AAAA,EACpC,MAAM;AAAA,EACN,aACE;AAAA,EACF,eAAe;AAAA;AAAA,EACf,QAAQC,GAAE,OAAO;AAAA,IACf,UAAUA,GAAE,OAAO,EAAE,SAAS,kDAAkD;AAAA,IAChF,SAASA,GAAE,OAAO,EAAE,SAAS,8BAA8B;AAAA,EAC7D,CAAC;AAAA,EACD,UAAU;AAAA,IACR;AAAA,MACE,QAAQ,EAAE,UAAU,cAAc,SAAS,gBAAgB;AAAA,MAC3D,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ;AAAA,QACN,UAAU;AAAA,QACV,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAUX;AAAA,MACA,QAAQ;AAAA,MACR,SACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,SAAS,CAAC,EAAE,UAAU,QAAQ,MAAM;AAElC,UAAM,gBAAgB,wBAAwB,QAAQ;AAGtD,UAAM,YAAYC,MAAK,QAAQ,aAAa;AAC5C,QAAI,aAAa;AACjB,QAAI,CAACC,IAAG,WAAW,SAAS,GAAG;AAE7B,8BAAwB,SAAS;AACjC,MAAAA,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAC3C,mBAAa;AAAA,IACf;AAGA,IAAAA,IAAG,cAAc,eAAe,SAAS,OAAO;AAChD,UAAM,eAAe,OAAO,WAAW,SAAS,OAAO;AAGvD,UAAM,UAAU,aAAa,wBAAwBD,MAAK,QAAQ,QAAQ,CAAC,MAAM;AACjF,WAAO,QAAQ,QAAQ;AAAA;AAAA,QAAa,YAAY,SAAS,OAAO;AAAA,EAClE;AACF,CAAC;;;ACnED,SAAS,gBAAAE,qBAAoB;AAC7B,SAAS,KAAAC,UAAS;;;ACQlB,SAAS,SAAS,iBAAiB;AAiDnC,SAAS,2BACP,YACmC;AACnC,MAAI,CAAC,WAAY,QAAO;AAExB,SAAO,IAAI,eAA2B;AAAA,IACpC,MAAM,YAAY;AAChB,iBAAW,GAAG,QAAQ,CAAC,UAAkB;AACvC,mBAAW,QAAQ,IAAI,WAAW,KAAK,CAAC;AAAA,MAC1C,CAAC;AACD,iBAAW,GAAG,OAAO,MAAM;AACzB,mBAAW,MAAM;AAAA,MACnB,CAAC;AACD,iBAAW,GAAG,SAAS,CAAC,QAAQ;AAC9B,mBAAW,MAAM,GAAG;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,IACA,SAAS;AACP,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF,CAAC;AACH;AASO,SAAS,MAAM,MAAgB,UAAwB,CAAC,GAAgB;AAC7E,QAAM,CAAC,SAAS,GAAG,IAAI,IAAI;AAC3B,QAAM,OAAO,UAAU,SAAS,MAAM;AAAA,IACpC,KAAK,QAAQ;AAAA,IACb,OAAO;AAAA,MACL,QAAQ,UAAU,SAAS,SAAU,QAAQ,SAAS;AAAA,MACtD,QAAQ,WAAW,SAAS,SAAU,QAAQ,UAAU;AAAA,MACxD,QAAQ,WAAW,SAAS,SAAU,QAAQ,UAAU;AAAA,IAC1D;AAAA,EACF,CAAC;AAGD,QAAM,SAAS,IAAI,QAAgB,CAAC,SAAS,WAAW;AACtD,SAAK,GAAG,QAAQ,CAAC,SAAS;AACxB,cAAQ,QAAQ,CAAC;AAAA,IACnB,CAAC;AACD,SAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,aAAO,GAAG;AAAA,IACZ,CAAC;AAAA,EACH,CAAC;AAGD,QAAM,QAA2B,KAAK,QAClC;AAAA,IACE,MAAM,MAAc;AAClB,WAAK,OAAO,MAAM,IAAI;AAAA,IACxB;AAAA,IACA,MAAM;AACJ,WAAK,OAAO,IAAI;AAAA,IAClB;AAAA,EACF,IACA;AAEJ,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,2BAA2B,KAAK,MAAM;AAAA,IAC9C,QAAQ,2BAA2B,KAAK,MAAM;AAAA,IAC9C;AAAA,IACA,OAAO;AACL,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AACF;;;ADnHO,IAAM,aAAaC,cAAa;AAAA,EACrC,MAAM;AAAA,EACN,aACE;AAAA,EACF,QAAQC,GAAE,OAAO;AAAA,IACf,MAAMA,GACH,MAAMA,GAAE,OAAO,CAAC,EAChB,SAAS,2EAA2E;AAAA,IACvF,KAAKA,GACF,OAAO,EACP,SAAS,EACT,SAAS,gEAAgE;AAAA,IAC5E,SAASA,GAAE,OAAO,EAAE,QAAQ,GAAK,EAAE,SAAS,0CAA0C;AAAA,EACxF,CAAC;AAAA,EACD,UAAU;AAAA,IACR;AAAA,MACE,QAAQ,EAAE,MAAM,CAAC,MAAM,KAAK,GAAG,SAAS,IAAM;AAAA,MAC9C,QACE;AAAA,MACF,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,MAAM,CAAC,QAAQ,aAAa,GAAG,SAAS,IAAM;AAAA,MACxD,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,MAAM,CAAC,OAAO,iBAAiB,GAAG,SAAS,IAAM;AAAA,MAC3D,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,MAAM,CAAC,KAAK,GAAG,KAAK,QAAQ,SAAS,IAAM;AAAA,MACrD,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,SAAS;AAAA,MACX;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,QAAQ;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,SAAS;AAAA,MACX;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,SAAS,OAAO,EAAE,MAAM,KAAK,QAAQ,MAAM;AACzC,UAAM,aAAa,OAAO,QAAQ,IAAI;AAEtC,QAAI,KAAK,WAAW,GAAG;AACrB,aAAO;AAAA,IACT;AAEA,QAAI;AAEJ,QAAI;AAEF,YAAM,OAAO,MAAM,MAAM;AAAA,QACvB,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV,CAAC;AAGD,YAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD,oBAAY,WAAW,MAAM;AAC3B,eAAK,KAAK;AACV,iBAAO,IAAI,MAAM,2BAA2B,OAAO,IAAI,CAAC;AAAA,QAC1D,GAAG,OAAO;AAAA,MACZ,CAAC;AAKD,YAAM,CAAC,UAAU,QAAQ,MAAM,IAAI,MAAM,QAAQ,KAAK;AAAA,QACpD,QAAQ,IAAI;AAAA,UACV,KAAK;AAAA,UACL,IAAI,SAAS,KAAK,MAAM,EAAE,KAAK;AAAA,UAC/B,IAAI,SAAS,KAAK,MAAM,EAAE,KAAK;AAAA,QACjC,CAAC;AAAA,QACD;AAAA,MACF,CAAC;AAGD,UAAI,WAAW;AACb,qBAAa,SAAS;AAAA,MACxB;AAGA,YAAM,SAAS,CAAC,QAAQ,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK;AAEhE,aAAO,UAAU,QAAQ;AAAA;AAAA,EAAO,UAAU,aAAa;AAAA,IACzD,SAAS,OAAO;AAEd,UAAI,WAAW;AACb,qBAAa,SAAS;AAAA,MACxB;AACA,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,aAAO;AAAA;AAAA,SAAsB,OAAO;AAAA,IACtC;AAAA,EACF;AACF,CAAC;","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"]}
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, MatchResult } from \"./editfile/index.js\";\nimport {\n adjustIndentation,\n applyReplacement,\n findAllMatches,\n findMatch,\n formatEditContext,\n formatMultipleMatches,\n getMatchFailure,\n} 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)\n5. DMP (diff-match-patch) - handles heavily refactored code\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\nOptions:\n- replaceAll: Replace all occurrences instead of just the first\n- expectedCount: Validate exact number of matches before applying`,\n maxConcurrent: 1, // Sequential execution to prevent race conditions\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 replaceAll: z\n .boolean()\n .optional()\n .default(false)\n .describe(\"Replace all occurrences instead of just the first match\"),\n expectedCount: z\n .number()\n .int()\n .positive()\n .optional()\n .describe(\"Expected number of matches. Edit fails if actual count differs\"),\n }),\n examples: [\n {\n params: {\n filePath: \"src/config.ts\",\n search: \"const DEBUG = false;\",\n replace: \"const DEBUG = true;\",\n replaceAll: false,\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 replaceAll: false,\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 replaceAll: false,\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 params: {\n filePath: \"src/constants.ts\",\n search: \"OLD_VALUE\",\n replace: \"NEW_VALUE\",\n replaceAll: true,\n },\n output:\n \"path=src/constants.ts status=success matches=3 lines=[2-2, 5-5, 8-8]\\n\\nReplaced 3 occurrences\\n\\nUPDATED FILE CONTENT:\\n```\\n// constants.ts\\nexport const A = NEW_VALUE;\\nexport const B = NEW_VALUE;\\nexport const C = NEW_VALUE;\\n```\",\n comment: \"Replace all occurrences with replaceAll=true\",\n },\n ],\n timeoutMs: 30000,\n execute: ({ filePath, search, replace, replaceAll, expectedCount }) => {\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 all matches to detect multiple occurrences\n const allMatches = findAllMatches(content, search);\n\n if (allMatches.length === 0) {\n // No match found - provide helpful suggestions\n const failure = getMatchFailure(content, search);\n return formatFailure(filePath, search, failure, content);\n }\n\n // Validate expectedCount if provided.\n // This check happens before the ambiguous-match detection below because it's stricter:\n // if user specifies expectedCount=1 but 2 matches are found, they get the count error,\n // not the \"add more context or use replaceAll\" suggestion.\n if (expectedCount !== undefined && allMatches.length !== expectedCount) {\n return `path=${filePath} status=error\\n\\nError: Expected ${expectedCount} match(es) but found ${allMatches.length}.\\n\\n${formatMultipleMatches(content, allMatches)}`;\n }\n\n // Handle multiple matches\n if (allMatches.length > 1 && !replaceAll) {\n // Ambiguous match - require explicit replaceAll or more context\n const matchSummary = formatMultipleMatches(content, allMatches);\n return `path=${filePath} status=error\\n\\nError: Found ${allMatches.length} matches. Please either:\\n1. Add more context to your search to make it unique\\n2. Use replaceAll=true to replace all occurrences\\n\\n${matchSummary}`;\n }\n\n // Apply replacement(s)\n let newContent: string;\n let editSummary: string;\n\n if (replaceAll && allMatches.length > 1) {\n // Replace all matches in reverse order to preserve indices\n newContent = executeReplaceAll(content, allMatches, replace);\n // Limit displayed line ranges to prevent very long output strings\n const MAX_DISPLAYED_RANGES = 5;\n const displayMatches = allMatches.slice(0, MAX_DISPLAYED_RANGES);\n const lineRanges = displayMatches.map((m) => `${m.startLine}-${m.endLine}`).join(\", \");\n const suffix =\n allMatches.length > MAX_DISPLAYED_RANGES\n ? `, +${allMatches.length - MAX_DISPLAYED_RANGES} more`\n : \"\";\n editSummary = `matches=${allMatches.length} lines=[${lineRanges}${suffix}]`;\n } else {\n // Single match replacement\n const match = allMatches[0];\n const finalReplace = prepareReplacement(match, replace);\n newContent = applyReplacement(content, match, finalReplace);\n editSummary = `strategy=${match.strategy} lines=${match.startLine}-${match.endLine}`;\n }\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 // Generate edit context showing before/after diff\n const diffContext =\n allMatches.length === 1\n ? formatEditContext(content, allMatches[0], prepareReplacement(allMatches[0], replace))\n : `Replaced ${allMatches.length} occurrences`;\n\n return `path=${filePath} status=success ${editSummary}\\n\\n${diffContext}\\n\\nUPDATED FILE CONTENT:\\n\\`\\`\\`\\n${newContent}\\n\\`\\`\\``;\n },\n});\n\n/**\n * Prepare the replacement text, applying indentation adjustments if needed.\n */\nfunction prepareReplacement(match: MatchResult, replace: string): string {\n // Apply indentation delta when using indentation strategy\n if (match.strategy === \"indentation\" && match.indentationDelta) {\n return adjustIndentation(replace, match.indentationDelta);\n }\n return replace;\n}\n\n/**\n * Execute replaceAll by applying replacements in reverse order.\n * This preserves indices since we modify from end to start.\n */\nfunction executeReplaceAll(content: string, matches: MatchResult[], replace: string): string {\n // Sort matches by startIndex descending (reverse order)\n const sortedMatches = [...matches].sort((a, b) => b.startIndex - a.startIndex);\n\n let result = content;\n for (const match of sortedMatches) {\n const finalReplace = prepareReplacement(match, replace);\n result = applyReplacement(result, match, finalReplace);\n }\n\n return result;\n}\n","/**\n * Layered matching algorithm for EditFile gadget.\n *\n * Tries strategies in order: exact -> whitespace -> indentation -> fuzzy -> dmp\n * This approach reduces edit errors by ~9x (per Aider benchmarks).\n */\n\nimport DiffMatchPatch from \"diff-match-patch\";\nimport type {\n MatchFailure,\n MatchOptions,\n MatchResult,\n MatchStrategy,\n SuggestionMatch,\n} from \"./types.js\";\n\n// Singleton DMP instance\nconst dmp = new DiffMatchPatch();\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 // Early return for empty search - prevents matching at every position\n if (!search) return null;\n\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 { name: \"dmp\", fn: (c, s) => dmpMatch(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 const searchLines = search.split(\"\\n\");\n\n // Sliding window search\n const searchLineCount = searchLines.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 // Compute indentation delta (difference between file's indent and search's indent)\n const indentationDelta = computeIndentationDelta(searchLines, windowLines);\n\n return {\n found: true,\n strategy: \"indentation\",\n confidence: 0.9,\n matchedContent,\n startIndex,\n endIndex,\n startLine,\n endLine,\n indentationDelta,\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// Strategy 5: DMP (diff-match-patch) Match\n// ============================================================================\n\n/**\n * DMP matching strategy using Google's diff-match-patch algorithm.\n * Handles heavily refactored code where other strategies fail.\n *\n * - Short patterns (≤32 chars): Uses native bitap algorithm\n * - Long patterns (>32 chars): Uses 32-char prefix for region finding + Levenshtein\n * - Skips patterns >1000 chars (too slow)\n */\nfunction dmpMatch(content: string, search: string, threshold: number): MatchResult | null {\n // Skip empty strings - prevent DMP from failing silently\n if (!search || !content) return null;\n\n // Skip very long patterns (too slow and unlikely to match)\n if (search.length > 1000) return null;\n\n // DMP works better with single-line or short patterns\n // For long multiline searches, fuzzy is usually better\n if (search.split(\"\\n\").length > 20) return null;\n\n const matchIndex =\n search.length <= 32\n ? dmpMatchShortPattern(content, search, threshold)\n : dmpMatchLongPattern(content, search, threshold);\n\n if (matchIndex === -1) return null;\n\n // Determine the actual matched content length\n // For DMP, we find the region and then find the best ending point\n const matchedContent = findBestMatchExtent(content, matchIndex, search, threshold);\n if (!matchedContent) return null;\n\n const startIndex = matchIndex;\n const endIndex = matchIndex + matchedContent.length;\n const { startLine, endLine } = getLineNumbers(content, startIndex, endIndex);\n\n // Calculate confidence based on similarity\n const similarity = stringSimilarity(search, matchedContent);\n\n return {\n found: true,\n strategy: \"dmp\",\n confidence: similarity,\n matchedContent,\n startIndex,\n endIndex,\n startLine,\n endLine,\n };\n}\n\n/**\n * DMP matching for short patterns (≤32 chars) using native bitap.\n */\nfunction dmpMatchShortPattern(content: string, search: string, threshold: number): number {\n // DMP's match_main uses bitap for patterns ≤32 chars\n // Threshold is inverted: DMP uses 0.0 = perfect, we use 1.0 = perfect\n dmp.Match_Threshold = 1 - threshold;\n dmp.Match_Distance = 1000; // Allow matches within reasonable distance\n\n const index = dmp.match_main(content, search, 0);\n return index;\n}\n\n/**\n * DMP matching for long patterns (>32 chars).\n * Uses a 32-char prefix to find candidate regions, then verifies with Levenshtein.\n */\nfunction dmpMatchLongPattern(content: string, search: string, threshold: number): number {\n // Extract 32-char prefix for region finding\n const prefix = search.slice(0, 32);\n // Use same threshold as short patterns - DMP uses inverted threshold (0 = perfect)\n dmp.Match_Threshold = 1 - threshold;\n dmp.Match_Distance = 1000;\n\n const prefixIndex = dmp.match_main(content, prefix, 0);\n if (prefixIndex === -1) return -1;\n\n // Now verify the full pattern matches starting from this region\n // Look in a window around the prefix match - scale padding with pattern length\n const windowPadding = Math.max(50, Math.floor(search.length / 2));\n const windowStart = Math.max(0, prefixIndex - windowPadding);\n const windowEnd = Math.min(content.length, prefixIndex + search.length + windowPadding);\n const window = content.slice(windowStart, windowEnd);\n\n // Find best match within window using sliding approach\n let bestIndex = -1;\n let bestSimilarity = 0;\n\n for (let i = 0; i <= window.length - search.length; i++) {\n const candidate = window.slice(i, i + search.length);\n const similarity = stringSimilarity(search, candidate);\n\n if (similarity >= threshold && similarity > bestSimilarity) {\n bestSimilarity = similarity;\n bestIndex = windowStart + i;\n }\n }\n\n return bestIndex;\n}\n\n/**\n * Find the best extent of a match starting at matchIndex.\n * Handles cases where the matched content length differs from search length.\n */\nfunction findBestMatchExtent(\n content: string,\n matchIndex: number,\n search: string,\n threshold: number,\n): string | null {\n // Try exact length first\n const exactLength = content.slice(matchIndex, matchIndex + search.length);\n if (stringSimilarity(search, exactLength) >= threshold) {\n return exactLength;\n }\n\n // Try line-based matching (match same number of lines)\n const searchLines = search.split(\"\\n\").length;\n const contentFromMatch = content.slice(matchIndex);\n const contentLines = contentFromMatch.split(\"\\n\");\n\n if (contentLines.length >= searchLines) {\n const lineBasedMatch = contentLines.slice(0, searchLines).join(\"\\n\");\n if (stringSimilarity(search, lineBasedMatch) >= threshold) {\n return lineBasedMatch;\n }\n }\n\n return null;\n}\n\n// ============================================================================\n// Multi-Match Support\n// ============================================================================\n\n/**\n * Find all matches for the search string in content.\n * Returns matches in order of appearance (not by confidence).\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 Array of MatchResult for all matches found\n */\nexport function findAllMatches(\n content: string,\n search: string,\n options: MatchOptions = {},\n): MatchResult[] {\n const results: MatchResult[] = [];\n let searchStart = 0;\n\n while (searchStart < content.length) {\n // Try to find a match starting from current position\n const remainingContent = content.slice(searchStart);\n const match = findMatch(remainingContent, search, options);\n\n if (!match) break;\n\n // Adjust indices to be relative to original content\n results.push({\n ...match,\n startIndex: searchStart + match.startIndex,\n endIndex: searchStart + match.endIndex,\n // Recalculate line numbers for original content\n ...getLineNumbers(content, searchStart + match.startIndex, searchStart + match.endIndex),\n });\n\n // Move search start past this match to find next\n searchStart = searchStart + match.endIndex;\n // Safety: advance at least one character to prevent infinite loop on zero-width matches\n if (match.endIndex === 0) {\n searchStart++;\n }\n }\n\n return results;\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\n// ============================================================================\n// Indentation Delta Functions\n// ============================================================================\n\n/**\n * Extract leading whitespace from a line.\n */\nfunction getLeadingWhitespace(line: string): string {\n const match = line.match(/^[ \\t]*/);\n return match ? match[0] : \"\";\n}\n\n/**\n * Compute the indentation delta between search and matched content.\n * Returns the whitespace prefix that should be added to each line.\n *\n * @param searchLines Lines from the search pattern\n * @param matchedLines Lines from the matched content in the file\n * @returns The indentation delta string (e.g., \" \" for 4-space indent)\n */\nfunction computeIndentationDelta(searchLines: string[], matchedLines: string[]): string {\n // Find the first non-empty line in both to compare indentation\n for (let i = 0; i < Math.min(searchLines.length, matchedLines.length); i++) {\n const searchLine = searchLines[i];\n const matchedLine = matchedLines[i];\n\n // Skip empty lines\n if (searchLine.trim() === \"\" && matchedLine.trim() === \"\") continue;\n\n const searchIndent = getLeadingWhitespace(searchLine);\n const matchedIndent = getLeadingWhitespace(matchedLine);\n\n // Return the difference in indentation\n if (matchedIndent.length > searchIndent.length) {\n return matchedIndent.slice(searchIndent.length);\n }\n // If matched has less indent, return empty (can't have negative delta)\n return \"\";\n }\n\n return \"\";\n}\n\n/**\n * Adjust indentation of replacement text based on the indentation delta.\n * Adds the delta to each line of the replacement.\n *\n * @param replacement The replacement text\n * @param delta The indentation delta to apply\n * @returns The replacement with adjusted indentation\n */\nexport function adjustIndentation(replacement: string, delta: string): string {\n if (!delta) return replacement;\n\n return replacement\n .split(\"\\n\")\n .map((line, index) => {\n // Don't add indent to empty lines or the first line if it's aligned with search\n if (line.trim() === \"\") return line;\n return delta + line;\n })\n .join(\"\\n\");\n}\n\n// ============================================================================\n// Before/After Context Formatting\n// ============================================================================\n\n/**\n * Format context showing before and after state of an edit.\n * Shows 5 lines of context around the edit with diff-style markers.\n *\n * @param originalContent The original file content\n * @param match The match result\n * @param replacement The replacement text\n * @param contextLines Number of context lines to show (default: 5)\n * @returns Formatted string showing the edit context\n */\nexport function formatEditContext(\n originalContent: string,\n match: MatchResult,\n replacement: string,\n contextLines = 5,\n): string {\n const lines = originalContent.split(\"\\n\");\n const startLine = match.startLine - 1; // Convert to 0-based\n const endLine = match.endLine; // Already exclusive for slicing\n\n const contextStart = Math.max(0, startLine - contextLines);\n const contextEnd = Math.min(lines.length, endLine + contextLines);\n\n const output: string[] = [];\n output.push(`=== Edit (lines ${match.startLine}-${match.endLine}) ===`);\n output.push(\"\");\n\n // Show before section\n // Context lines before the change\n for (let i = contextStart; i < startLine; i++) {\n output.push(` ${String(i + 1).padStart(4)} | ${lines[i]}`);\n }\n\n // Lines being removed (marked with <)\n for (let i = startLine; i < endLine; i++) {\n output.push(`< ${String(i + 1).padStart(4)} | ${lines[i]}`);\n }\n\n // Lines being added (marked with >)\n const replacementLines = replacement.split(\"\\n\");\n for (let i = 0; i < replacementLines.length; i++) {\n const lineNum = startLine + i + 1;\n output.push(`> ${String(lineNum).padStart(4)} | ${replacementLines[i]}`);\n }\n\n // Context lines after the change\n for (let i = endLine; i < contextEnd; i++) {\n output.push(` ${String(i + 1).padStart(4)} | ${lines[i]}`);\n }\n\n return output.join(\"\\n\");\n}\n\n/**\n * Format a summary of multiple matches for disambiguation.\n *\n * @param content The file content\n * @param matches Array of matches found\n * @param maxMatches Maximum matches to show (default: 5)\n * @returns Formatted string showing match locations\n */\nexport function formatMultipleMatches(\n content: string,\n matches: MatchResult[],\n maxMatches = 5,\n): string {\n const lines = content.split(\"\\n\");\n const output: string[] = [];\n\n output.push(`Found ${matches.length} matches:`);\n output.push(\"\");\n\n const displayMatches = matches.slice(0, maxMatches);\n\n for (let i = 0; i < displayMatches.length; i++) {\n const match = displayMatches[i];\n output.push(`Match ${i + 1} (lines ${match.startLine}-${match.endLine}):`);\n\n // Show a few lines of context\n const contextStart = Math.max(0, match.startLine - 2);\n const contextEnd = Math.min(lines.length, match.endLine + 1);\n\n for (let j = contextStart; j < contextEnd; j++) {\n const marker = j >= match.startLine - 1 && j < match.endLine ? \">\" : \" \";\n output.push(`${marker}${String(j + 1).padStart(4)} | ${lines[j]}`);\n }\n\n output.push(\"\");\n }\n\n if (matches.length > maxMatches) {\n output.push(`... and ${matches.length - maxMatches} more matches`);\n }\n\n return output.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 maxConcurrent: 1, // Sequential execution to prevent race conditions\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": "16.0.1",
3
+ "version": "16.0.3",
4
4
  "description": "CLI for llmist - run LLM agents from the command line",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -57,8 +57,9 @@
57
57
  "node": ">=22.0.0"
58
58
  },
59
59
  "dependencies": {
60
- "llmist": "^16.0.1",
60
+ "llmist": "^16.0.3",
61
61
  "@unblessed/node": "^1.0.0-alpha.23",
62
+ "diff-match-patch": "^1.0.5",
62
63
  "chalk": "^5.6.2",
63
64
  "commander": "^12.1.0",
64
65
  "diff": "^8.0.2",
@@ -71,8 +72,9 @@
71
72
  "zod": "^4.1.12"
72
73
  },
73
74
  "devDependencies": {
74
- "@llmist/testing": "^16.0.1",
75
+ "@llmist/testing": "^16.0.3",
75
76
  "@types/diff": "^8.0.0",
77
+ "@types/diff-match-patch": "^1.0.36",
76
78
  "@types/js-yaml": "^4.0.9",
77
79
  "@types/marked-terminal": "^6.1.1",
78
80
  "@types/node": "^20.12.7",