@timetotest/cli 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +49 -40
  2. package/dist/bin/ttt.js +0 -2
  3. package/dist/bin/ttt.js.map +1 -1
  4. package/dist/package.json +8 -3
  5. package/dist/src/commands/chat/ChatApp.js +249 -67
  6. package/dist/src/commands/chat/ChatApp.js.map +1 -1
  7. package/dist/src/commands/chat/OnboardingApp.js +49 -0
  8. package/dist/src/commands/chat/OnboardingApp.js.map +1 -0
  9. package/dist/src/commands/chat/components/Banner.js +1 -1
  10. package/dist/src/commands/chat/components/Banner.js.map +1 -1
  11. package/dist/src/commands/chat/components/ChatInput.js +61 -22
  12. package/dist/src/commands/chat/components/ChatInput.js.map +1 -1
  13. package/dist/src/commands/chat/components/ChatMessage.js +102 -0
  14. package/dist/src/commands/chat/components/ChatMessage.js.map +1 -0
  15. package/dist/src/commands/chat/components/MessageBubble.js +1 -1
  16. package/dist/src/commands/chat/components/MessageBubble.js.map +1 -1
  17. package/dist/src/commands/chat/components/PermissionPrompt.js +92 -0
  18. package/dist/src/commands/chat/components/PermissionPrompt.js.map +1 -0
  19. package/dist/src/commands/chat/components/StatusIndicator.js +21 -5
  20. package/dist/src/commands/chat/components/StatusIndicator.js.map +1 -1
  21. package/dist/src/commands/chat/components/ToolCallDisplay.js +141 -0
  22. package/dist/src/commands/chat/components/ToolCallDisplay.js.map +1 -0
  23. package/dist/src/commands/chat-ink.js +328 -93
  24. package/dist/src/commands/chat-ink.js.map +1 -1
  25. package/dist/src/commands/login.js +5 -5
  26. package/dist/src/commands/login.js.map +1 -1
  27. package/dist/src/commands/test.js +14 -291
  28. package/dist/src/commands/test.js.map +1 -1
  29. package/dist/src/lib/__tests__/code-mode-integration.test.js +381 -0
  30. package/dist/src/lib/__tests__/code-mode-integration.test.js.map +1 -0
  31. package/dist/src/lib/__tests__/config-manager.test.js +81 -0
  32. package/dist/src/lib/__tests__/config-manager.test.js.map +1 -0
  33. package/dist/src/lib/__tests__/mode-persistence-integration.test.js +75 -0
  34. package/dist/src/lib/__tests__/mode-persistence-integration.test.js.map +1 -0
  35. package/dist/src/lib/__tests__/permission-flow-integration.test.js +145 -0
  36. package/dist/src/lib/__tests__/permission-flow-integration.test.js.map +1 -0
  37. package/dist/src/lib/__tests__/permissions.test.js +132 -0
  38. package/dist/src/lib/__tests__/permissions.test.js.map +1 -0
  39. package/dist/src/lib/agent-orchestrator.js +259 -5
  40. package/dist/src/lib/agent-orchestrator.js.map +1 -1
  41. package/dist/src/lib/config.js +40 -0
  42. package/dist/src/lib/config.js.map +1 -1
  43. package/dist/src/lib/context-compactor.js +310 -0
  44. package/dist/src/lib/context-compactor.js.map +1 -0
  45. package/dist/src/lib/http.js +8 -0
  46. package/dist/src/lib/http.js.map +1 -1
  47. package/dist/src/lib/local-tools/code/__tests__/grep-search.test.js +146 -0
  48. package/dist/src/lib/local-tools/code/__tests__/grep-search.test.js.map +1 -0
  49. package/dist/src/lib/local-tools/code/__tests__/list-directory.test.js +192 -0
  50. package/dist/src/lib/local-tools/code/__tests__/list-directory.test.js.map +1 -0
  51. package/dist/src/lib/local-tools/code/__tests__/read-file.test.js +169 -0
  52. package/dist/src/lib/local-tools/code/__tests__/read-file.test.js.map +1 -0
  53. package/dist/src/lib/local-tools/code/__tests__/run-command.test.js +101 -0
  54. package/dist/src/lib/local-tools/code/__tests__/run-command.test.js.map +1 -0
  55. package/dist/src/lib/local-tools/code/__tests__/search-files.test.js +191 -0
  56. package/dist/src/lib/local-tools/code/__tests__/search-files.test.js.map +1 -0
  57. package/dist/src/lib/local-tools/code/grep-search.js +404 -0
  58. package/dist/src/lib/local-tools/code/grep-search.js.map +1 -0
  59. package/dist/src/lib/local-tools/code/index.js +11 -0
  60. package/dist/src/lib/local-tools/code/index.js.map +1 -0
  61. package/dist/src/lib/local-tools/code/list-directory.js +276 -0
  62. package/dist/src/lib/local-tools/code/list-directory.js.map +1 -0
  63. package/dist/src/lib/local-tools/code/read-file.js +301 -0
  64. package/dist/src/lib/local-tools/code/read-file.js.map +1 -0
  65. package/dist/src/lib/local-tools/code/run-command.js +235 -0
  66. package/dist/src/lib/local-tools/code/run-command.js.map +1 -0
  67. package/dist/src/lib/local-tools/code/search-files.js +297 -0
  68. package/dist/src/lib/local-tools/code/search-files.js.map +1 -0
  69. package/dist/src/lib/local-tools/code/types.js +6 -0
  70. package/dist/src/lib/local-tools/code/types.js.map +1 -0
  71. package/dist/src/lib/permissions.js +94 -0
  72. package/dist/src/lib/permissions.js.map +1 -0
  73. package/dist/src/lib/prompts/builder.js +13 -10
  74. package/dist/src/lib/prompts/builder.js.map +1 -1
  75. package/dist/src/lib/prompts/templates.js +78 -0
  76. package/dist/src/lib/prompts/templates.js.map +1 -1
  77. package/dist/src/lib/session-manager.js.map +1 -1
  78. package/dist/src/lib/testing-mode.js +2 -2
  79. package/dist/src/lib/testing-mode.js.map +1 -1
  80. package/dist/src/lib/tool-executor.js +131 -2
  81. package/dist/src/lib/tool-executor.js.map +1 -1
  82. package/dist/src/lib/tool-registry.js +171 -3
  83. package/dist/src/lib/tool-registry.js.map +1 -1
  84. package/dist/src/lib/tool-result-pruner.js +4 -4
  85. package/dist/src/lib/tool-result-pruner.js.map +1 -1
  86. package/dist/src/lib/tui/ink/components/AppFrame.js +17 -0
  87. package/dist/src/lib/tui/ink/components/AppFrame.js.map +1 -0
  88. package/dist/src/lib/tui/ink/components/CommandPalette.js +24 -0
  89. package/dist/src/lib/tui/ink/components/CommandPalette.js.map +1 -0
  90. package/dist/src/lib/tui/ink/components/Pill.js +19 -0
  91. package/dist/src/lib/tui/ink/components/Pill.js.map +1 -0
  92. package/dist/src/lib/tui/ink/components/TimetoTestLogo.js +30 -0
  93. package/dist/src/lib/tui/ink/components/TimetoTestLogo.js.map +1 -0
  94. package/dist/src/lib/tui/ink/theme.js +13 -6
  95. package/dist/src/lib/tui/ink/theme.js.map +1 -1
  96. package/dist/src/lib/tui/interactive-chat.js +35 -35
  97. package/dist/src/lib/tui/interactive-chat.js.map +1 -1
  98. package/dist/src/lib/tui/print.js +18 -18
  99. package/dist/src/lib/tui/print.js.map +1 -1
  100. package/dist/src/lib/tui/prompt.js +3 -3
  101. package/dist/src/lib/tui/prompt.js.map +1 -1
  102. package/dist/src/lib/tui/status.js +1 -1
  103. package/dist/src/lib/tui/status.js.map +1 -1
  104. package/dist/src/lib/update.js +10 -10
  105. package/dist/src/lib/update.js.map +1 -1
  106. package/package.json +8 -3
  107. package/dist/src/commands/start-test.js +0 -180
  108. package/dist/src/commands/start-test.js.map +0 -1
  109. package/dist/src/commands/stream/StreamApp.js +0 -127
  110. package/dist/src/commands/stream/StreamApp.js.map +0 -1
  111. package/dist/src/commands/stream.js +0 -52
  112. package/dist/src/commands/stream.js.map +0 -1
  113. package/dist/src/commands/test/TestRunApp.js +0 -183
  114. package/dist/src/commands/test/TestRunApp.js.map +0 -1
@@ -0,0 +1,235 @@
1
+ /**
2
+ * run_command tool - Execute shell commands with safety constraints.
3
+ * Supports timeout enforcement, working directory validation, and user permission prompts.
4
+ *
5
+ * Security (TR-5):
6
+ * - Working directory constrained to project root
7
+ * - Timeout enforced (default 30s)
8
+ * - No shell expansion (uses spawn, not exec)
9
+ * - User must approve command execution via permission prompt
10
+ */
11
+ import { spawn } from "child_process";
12
+ import * as path from "path";
13
+ // Default timeout in milliseconds (30 seconds as per design)
14
+ const DEFAULT_TIMEOUT_MS = 30000;
15
+ // Maximum output size to capture (1MB per stream)
16
+ const MAX_OUTPUT_SIZE = 1024 * 1024;
17
+ /**
18
+ * Validate and resolve a working directory path to prevent directory traversal.
19
+ * Returns the resolved absolute path if valid, or an error.
20
+ *
21
+ * Security (TR-5):
22
+ * - All paths normalized and validated against project root
23
+ * - Reject paths containing `..` that escape root
24
+ * - Use `path.resolve()` and check prefix
25
+ */
26
+ function validateWorkingDirectory(cwd, workingDirectory) {
27
+ // Normalize the root working directory to absolute path
28
+ const rootDir = path.resolve(workingDirectory);
29
+ // If no cwd specified, use the root directory
30
+ if (!cwd) {
31
+ return { valid: true, resolvedPath: rootDir };
32
+ }
33
+ // Resolve the requested path relative to working directory
34
+ const resolvedPath = path.resolve(rootDir, cwd);
35
+ // Security check: ensure resolved path is within root directory
36
+ // Use path.sep to ensure we're checking directory boundaries properly
37
+ const normalizedRoot = rootDir.endsWith(path.sep)
38
+ ? rootDir
39
+ : rootDir + path.sep;
40
+ const normalizedResolved = resolvedPath + path.sep;
41
+ if (!normalizedResolved.startsWith(normalizedRoot) &&
42
+ resolvedPath !== rootDir) {
43
+ return {
44
+ valid: false,
45
+ error: "Working directory validation failed: access denied",
46
+ };
47
+ }
48
+ return { valid: true, resolvedPath };
49
+ }
50
+ /**
51
+ * Truncate output if it exceeds the maximum size.
52
+ *
53
+ * @param output - The output string to potentially truncate
54
+ * @param maxSize - Maximum allowed size in bytes
55
+ * @returns Truncated output with indicator if truncated
56
+ */
57
+ function truncateOutput(output, maxSize = MAX_OUTPUT_SIZE) {
58
+ if (Buffer.byteLength(output, "utf-8") <= maxSize) {
59
+ return output;
60
+ }
61
+ // Truncate to max size and add indicator
62
+ const truncated = Buffer.from(output, "utf-8")
63
+ .subarray(0, maxSize)
64
+ .toString("utf-8");
65
+ return truncated + "\n... [output truncated]";
66
+ }
67
+ /**
68
+ * Execute a command with timeout and capture output.
69
+ *
70
+ * @param command - The command to execute
71
+ * @param args - Command arguments
72
+ * @param cwd - Working directory
73
+ * @param timeout - Timeout in milliseconds
74
+ * @param env - Additional environment variables
75
+ * @returns Promise resolving to command result
76
+ */
77
+ function executeCommand(command, args, cwd, timeout, env) {
78
+ return new Promise((resolve) => {
79
+ const startTime = Date.now();
80
+ let stdout = "";
81
+ let stderr = "";
82
+ let timedOut = false;
83
+ let childProcess = null;
84
+ let timeoutId = null;
85
+ try {
86
+ // Spawn the process without shell to prevent shell expansion (TR-5)
87
+ childProcess = spawn(command, args, {
88
+ cwd,
89
+ env: { ...process.env, ...env },
90
+ // No shell to prevent command injection
91
+ shell: false,
92
+ // Detach to allow proper cleanup
93
+ detached: false,
94
+ // Set stdio to pipe for capturing output
95
+ stdio: ["ignore", "pipe", "pipe"],
96
+ });
97
+ // Set up timeout
98
+ timeoutId = setTimeout(() => {
99
+ timedOut = true;
100
+ if (childProcess && !childProcess.killed) {
101
+ // Kill the process and all its children
102
+ childProcess.kill("SIGTERM");
103
+ // Force kill after 5 seconds if still running
104
+ setTimeout(() => {
105
+ if (childProcess && !childProcess.killed) {
106
+ childProcess.kill("SIGKILL");
107
+ }
108
+ }, 5000);
109
+ }
110
+ }, timeout);
111
+ // Capture stdout with size limit
112
+ if (childProcess.stdout) {
113
+ childProcess.stdout.on("data", (data) => {
114
+ const chunk = data.toString("utf-8");
115
+ if (Buffer.byteLength(stdout, "utf-8") < MAX_OUTPUT_SIZE) {
116
+ stdout += chunk;
117
+ }
118
+ });
119
+ }
120
+ // Capture stderr with size limit
121
+ if (childProcess.stderr) {
122
+ childProcess.stderr.on("data", (data) => {
123
+ const chunk = data.toString("utf-8");
124
+ if (Buffer.byteLength(stderr, "utf-8") < MAX_OUTPUT_SIZE) {
125
+ stderr += chunk;
126
+ }
127
+ });
128
+ }
129
+ // Handle process completion
130
+ childProcess.on("close", (code) => {
131
+ if (timeoutId) {
132
+ clearTimeout(timeoutId);
133
+ }
134
+ const durationMs = Date.now() - startTime;
135
+ resolve({
136
+ exitCode: code ?? (timedOut ? 124 : 1), // 124 is conventional timeout exit code
137
+ stdout: truncateOutput(stdout),
138
+ stderr: truncateOutput(stderr),
139
+ timedOut,
140
+ durationMs,
141
+ });
142
+ });
143
+ // Handle spawn errors
144
+ childProcess.on("error", (error) => {
145
+ if (timeoutId) {
146
+ clearTimeout(timeoutId);
147
+ }
148
+ const durationMs = Date.now() - startTime;
149
+ resolve({
150
+ exitCode: 1,
151
+ stdout: truncateOutput(stdout),
152
+ stderr: truncateOutput(stderr + `\nSpawn error: ${error.message}`),
153
+ timedOut: false,
154
+ durationMs,
155
+ });
156
+ });
157
+ }
158
+ catch (error) {
159
+ if (timeoutId) {
160
+ clearTimeout(timeoutId);
161
+ }
162
+ const durationMs = Date.now() - startTime;
163
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
164
+ resolve({
165
+ exitCode: 1,
166
+ stdout: "",
167
+ stderr: `Failed to spawn process: ${errorMessage}`,
168
+ timedOut: false,
169
+ durationMs,
170
+ });
171
+ }
172
+ });
173
+ }
174
+ /**
175
+ * Run a command with safety constraints.
176
+ *
177
+ * Security features (TR-5):
178
+ * - User must approve command via permission prompt before execution
179
+ * - Working directory is validated to be within project root
180
+ * - Timeout is enforced (default 30s)
181
+ * - No shell expansion (uses spawn, not exec)
182
+ * - Output is captured with size limits
183
+ *
184
+ * @param args - RunCommandArgs containing command, args, cwd, timeout, and env
185
+ * @param context - CodeContext with working directory
186
+ * @returns RunCommandResult with execution results
187
+ */
188
+ export async function runCommand(args, context) {
189
+ const { command, args: commandArgs = [], cwd, timeout = DEFAULT_TIMEOUT_MS, env, } = args;
190
+ // Validate working directory (TR-5)
191
+ const cwdValidation = validateWorkingDirectory(cwd, context.workingDirectory);
192
+ if (!cwdValidation.valid) {
193
+ return {
194
+ success: false,
195
+ exit_code: 1,
196
+ stdout: "",
197
+ stderr: "",
198
+ duration_ms: 0,
199
+ timed_out: false,
200
+ error: cwdValidation.error,
201
+ };
202
+ }
203
+ const resolvedCwd = cwdValidation.resolvedPath;
204
+ // Validate timeout is reasonable (between 1 second and 10 minutes)
205
+ const effectiveTimeout = Math.max(1000, Math.min(timeout, 600000));
206
+ try {
207
+ // Execute the command
208
+ const result = await executeCommand(command, commandArgs, resolvedCwd, effectiveTimeout, env);
209
+ return {
210
+ success: result.exitCode === 0 && !result.timedOut,
211
+ exit_code: result.exitCode,
212
+ stdout: result.stdout,
213
+ stderr: result.stderr,
214
+ duration_ms: result.durationMs,
215
+ timed_out: result.timedOut,
216
+ error: result.timedOut
217
+ ? `Command timed out after ${effectiveTimeout}ms`
218
+ : undefined,
219
+ };
220
+ }
221
+ catch (error) {
222
+ // Handle unexpected errors
223
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
224
+ return {
225
+ success: false,
226
+ exit_code: 1,
227
+ stdout: "",
228
+ stderr: "",
229
+ duration_ms: 0,
230
+ timed_out: false,
231
+ error: `Failed to execute command: ${errorMessage}`,
232
+ };
233
+ }
234
+ }
235
+ //# sourceMappingURL=run-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-command.js","sourceRoot":"","sources":["../../../../../src/lib/local-tools/code/run-command.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,297 @@
1
+ /**
2
+ * search_files tool - Search for files matching a glob pattern.
3
+ * Supports .gitignore respect, exclude patterns, and result limits.
4
+ *
5
+ * Security: Implements path validation to prevent directory traversal (TR-5).
6
+ * Respects .gitignore by default and skips common ignored directories.
7
+ */
8
+ import * as fs from "fs/promises";
9
+ import * as path from "path";
10
+ // Default maximum results to return
11
+ const DEFAULT_MAX_RESULTS = 100;
12
+ // Directories to always skip unless explicitly requested (TR-5)
13
+ const DEFAULT_IGNORED_DIRS = new Set([
14
+ "node_modules",
15
+ ".git",
16
+ "__pycache__",
17
+ ".pytest_cache",
18
+ ".mypy_cache",
19
+ ".tox",
20
+ ".nox",
21
+ ".eggs",
22
+ "*.egg-info",
23
+ ".venv",
24
+ "venv",
25
+ "env",
26
+ ".env",
27
+ "dist",
28
+ "build",
29
+ ".next",
30
+ ".nuxt",
31
+ ".output",
32
+ ".cache",
33
+ ".parcel-cache",
34
+ "coverage",
35
+ ".nyc_output",
36
+ ]);
37
+ /**
38
+ * Parse a .gitignore file and return an array of patterns.
39
+ */
40
+ async function parseGitignore(gitignorePath) {
41
+ try {
42
+ const content = await fs.readFile(gitignorePath, "utf-8");
43
+ return content
44
+ .split("\n")
45
+ .map((line) => line.trim())
46
+ .filter((line) => line && !line.startsWith("#"));
47
+ }
48
+ catch {
49
+ return [];
50
+ }
51
+ }
52
+ /**
53
+ * Convert a glob pattern to a RegExp.
54
+ * Supports *, **, and ? wildcards.
55
+ */
56
+ function globToRegex(pattern) {
57
+ // Escape special regex characters except glob wildcards
58
+ let regexStr = pattern
59
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
60
+ // Handle ** (matches any path including separators)
61
+ .replace(/\*\*/g, "{{GLOBSTAR}}")
62
+ // Handle * (matches anything except path separator)
63
+ .replace(/\*/g, "[^/]*")
64
+ // Handle ? (matches single character except path separator)
65
+ .replace(/\?/g, "[^/]")
66
+ // Restore globstar
67
+ .replace(/\{\{GLOBSTAR\}\}/g, ".*");
68
+ return new RegExp(`^${regexStr}$`, "i");
69
+ }
70
+ /**
71
+ * Check if a path matches a glob pattern.
72
+ */
73
+ function matchesGlob(filePath, pattern) {
74
+ // Normalize path separators
75
+ const normalizedPath = filePath.replace(/\\/g, "/");
76
+ const normalizedPattern = pattern.replace(/\\/g, "/");
77
+ // Handle patterns that start with /
78
+ const patternToUse = normalizedPattern.startsWith("/")
79
+ ? normalizedPattern.slice(1)
80
+ : normalizedPattern;
81
+ const regex = globToRegex(patternToUse);
82
+ return regex.test(normalizedPath);
83
+ }
84
+ /**
85
+ * Check if a path should be ignored based on gitignore patterns.
86
+ */
87
+ function isIgnoredByPatterns(relativePath, patterns) {
88
+ const normalizedPath = relativePath.replace(/\\/g, "/");
89
+ for (const pattern of patterns) {
90
+ if (!pattern)
91
+ continue;
92
+ // Handle negation patterns (!)
93
+ if (pattern.startsWith("!")) {
94
+ // Negation - if it matches, it's NOT ignored
95
+ const negatedPattern = pattern.slice(1);
96
+ if (matchesGlob(normalizedPath, negatedPattern)) {
97
+ return false;
98
+ }
99
+ continue;
100
+ }
101
+ // Handle directory-only patterns (ending with /)
102
+ const isDirectoryPattern = pattern.endsWith("/");
103
+ const cleanPattern = isDirectoryPattern ? pattern.slice(0, -1) : pattern;
104
+ // Check if pattern matches
105
+ if (matchesGlob(normalizedPath, cleanPattern)) {
106
+ return true;
107
+ }
108
+ // Also check if any path component matches (for patterns like "node_modules")
109
+ const pathParts = normalizedPath.split("/");
110
+ for (const part of pathParts) {
111
+ if (matchesGlob(part, cleanPattern)) {
112
+ return true;
113
+ }
114
+ }
115
+ // Check with ** prefix for patterns that should match anywhere
116
+ if (!cleanPattern.includes("/") && !cleanPattern.startsWith("*")) {
117
+ if (matchesGlob(normalizedPath, `**/${cleanPattern}`)) {
118
+ return true;
119
+ }
120
+ }
121
+ }
122
+ return false;
123
+ }
124
+ /**
125
+ * Check if a directory name is in the default ignored list.
126
+ */
127
+ function isDefaultIgnoredDir(dirName) {
128
+ return DEFAULT_IGNORED_DIRS.has(dirName);
129
+ }
130
+ /**
131
+ * Validate and resolve a path to prevent directory traversal.
132
+ * Returns the resolved absolute path if valid, or an error.
133
+ *
134
+ * Security (TR-5):
135
+ * - All paths normalized and validated against project root
136
+ * - Reject paths containing `..` that escape root
137
+ */
138
+ function validatePath(targetPath, workingDirectory) {
139
+ const rootDir = path.resolve(workingDirectory);
140
+ const resolvedPath = path.resolve(rootDir, targetPath);
141
+ // Security check: ensure resolved path is within root directory
142
+ const normalizedRoot = rootDir.endsWith(path.sep)
143
+ ? rootDir
144
+ : rootDir + path.sep;
145
+ const normalizedResolved = resolvedPath + path.sep;
146
+ if (!normalizedResolved.startsWith(normalizedRoot) &&
147
+ resolvedPath !== rootDir) {
148
+ return {
149
+ valid: false,
150
+ error: "Path validation failed: access denied",
151
+ };
152
+ }
153
+ return { valid: true, resolvedPath };
154
+ }
155
+ /**
156
+ * Recursively search for files matching a glob pattern.
157
+ */
158
+ async function searchRecursive(currentDir, rootDir, pattern, options) {
159
+ // Check if we've reached the max results
160
+ if (options.results.length >= options.maxResults) {
161
+ return;
162
+ }
163
+ try {
164
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
165
+ for (const entry of entries) {
166
+ // Check max results limit
167
+ if (options.results.length >= options.maxResults) {
168
+ return;
169
+ }
170
+ const fullPath = path.join(currentDir, entry.name);
171
+ const relativePath = path.relative(rootDir, fullPath);
172
+ // Skip default ignored directories (TR-5)
173
+ if (entry.isDirectory() && isDefaultIgnoredDir(entry.name)) {
174
+ continue;
175
+ }
176
+ // Check gitignore patterns if enabled
177
+ if (options.respectGitignore &&
178
+ isIgnoredByPatterns(relativePath, options.gitignorePatterns)) {
179
+ continue;
180
+ }
181
+ // Check exclude patterns
182
+ if (isIgnoredByPatterns(relativePath, options.excludePatterns)) {
183
+ continue;
184
+ }
185
+ if (entry.isDirectory()) {
186
+ // Recurse into subdirectory
187
+ await searchRecursive(currentDir + path.sep + entry.name, rootDir, pattern, options);
188
+ }
189
+ else if (entry.isFile()) {
190
+ // Check if file matches the search pattern
191
+ const normalizedRelativePath = relativePath.replace(/\\/g, "/");
192
+ if (pattern.test(normalizedRelativePath)) {
193
+ options.results.push(relativePath);
194
+ }
195
+ }
196
+ }
197
+ }
198
+ catch {
199
+ // Skip directories we can't read (permission issues, etc.)
200
+ }
201
+ }
202
+ /**
203
+ * Search for files matching a glob pattern.
204
+ *
205
+ * @param args - SearchFilesArgs containing pattern and options
206
+ * @param context - CodeContext with working directory
207
+ * @returns SearchFilesResult with matching files
208
+ */
209
+ export async function searchFiles(args, context) {
210
+ const { pattern, path: basePath = ".", exclude = [], respect_gitignore = true, max_results = DEFAULT_MAX_RESULTS, } = args;
211
+ // Validate the base path
212
+ const pathValidation = validatePath(basePath, context.workingDirectory);
213
+ if (!pathValidation.valid) {
214
+ return {
215
+ success: false,
216
+ files: [],
217
+ total_found: 0,
218
+ truncated: false,
219
+ error: pathValidation.error,
220
+ };
221
+ }
222
+ const resolvedBasePath = pathValidation.resolvedPath;
223
+ try {
224
+ // Check if base path exists and is a directory
225
+ const stats = await fs.stat(resolvedBasePath);
226
+ if (!stats.isDirectory()) {
227
+ return {
228
+ success: false,
229
+ files: [],
230
+ total_found: 0,
231
+ truncated: false,
232
+ error: "Base path is not a directory",
233
+ };
234
+ }
235
+ // Parse .gitignore if respect_gitignore is enabled
236
+ let gitignorePatterns = [];
237
+ if (respect_gitignore) {
238
+ const gitignorePath = path.join(context.workingDirectory, ".gitignore");
239
+ gitignorePatterns = await parseGitignore(gitignorePath);
240
+ }
241
+ // Convert the search pattern to a regex
242
+ const searchRegex = globToRegex(pattern);
243
+ // Search for matching files
244
+ const results = [];
245
+ await searchRecursive(resolvedBasePath, resolvedBasePath, searchRegex, {
246
+ gitignorePatterns,
247
+ excludePatterns: exclude,
248
+ respectGitignore: respect_gitignore,
249
+ maxResults: max_results + 1, // Get one extra to detect truncation
250
+ results,
251
+ });
252
+ // Check if results were truncated
253
+ const truncated = results.length > max_results;
254
+ const finalResults = truncated ? results.slice(0, max_results) : results;
255
+ // Sort results alphabetically for consistent output
256
+ finalResults.sort((a, b) => a.localeCompare(b));
257
+ return {
258
+ success: true,
259
+ files: finalResults,
260
+ total_found: finalResults.length,
261
+ truncated,
262
+ };
263
+ }
264
+ catch (error) {
265
+ // Handle specific error types without exposing sensitive info (TR-5)
266
+ if (error instanceof Error) {
267
+ const nodeError = error;
268
+ if (nodeError.code === "ENOENT") {
269
+ return {
270
+ success: false,
271
+ files: [],
272
+ total_found: 0,
273
+ truncated: false,
274
+ error: "Base path not found",
275
+ };
276
+ }
277
+ if (nodeError.code === "EACCES") {
278
+ return {
279
+ success: false,
280
+ files: [],
281
+ total_found: 0,
282
+ truncated: false,
283
+ error: "Permission denied",
284
+ };
285
+ }
286
+ }
287
+ // Generic error without exposing details (TR-5)
288
+ return {
289
+ success: false,
290
+ files: [],
291
+ total_found: 0,
292
+ truncated: false,
293
+ error: "Failed to search files",
294
+ };
295
+ }
296
+ }
297
+ //# sourceMappingURL=search-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-files.js","sourceRoot":"","sources":["../../../../../src/lib/local-tools/code/search-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Shared types for code analysis tools.
3
+ * These interfaces define the arguments and results for all code tools.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/lib/local-tools/code/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Permission Guard - Controls access to sensitive operations
3
+ * Prompts user for permission before executing file writes or shell commands
4
+ */
5
+ export class PermissionGuard {
6
+ sessionGrants;
7
+ promptFn = null;
8
+ constructor() {
9
+ this.sessionGrants = {
10
+ file_write: false,
11
+ file_delete: false,
12
+ run_command: false,
13
+ };
14
+ }
15
+ /**
16
+ * Set the prompt function that will be called when permission is needed
17
+ */
18
+ setPromptFn(fn) {
19
+ this.promptFn = fn;
20
+ }
21
+ /**
22
+ * Check if an operation is allowed
23
+ * Returns immediately if session grant exists, otherwise prompts user
24
+ */
25
+ async check(request) {
26
+ // If session grant exists, allow immediately
27
+ if (this.sessionGrants[request.category]) {
28
+ return { allowed: true };
29
+ }
30
+ // If no prompt function set, deny by default (fail-safe)
31
+ if (!this.promptFn) {
32
+ return {
33
+ allowed: false,
34
+ feedback: `Permission required for ${request.category}. No permission handler configured.`,
35
+ };
36
+ }
37
+ // Prompt user for permission
38
+ const response = await this.promptFn(request);
39
+ switch (response) {
40
+ case "allow_session":
41
+ this.grantSession(request.category);
42
+ return { allowed: true };
43
+ case "allow_once":
44
+ return { allowed: true };
45
+ case "deny":
46
+ default:
47
+ return {
48
+ allowed: false,
49
+ feedback: this.buildDenyFeedback(request),
50
+ };
51
+ }
52
+ }
53
+ /**
54
+ * Grant session-wide permission for a category
55
+ */
56
+ grantSession(category) {
57
+ this.sessionGrants[category] = true;
58
+ }
59
+ /**
60
+ * Check if a category has session-wide permission
61
+ */
62
+ hasSessionGrant(category) {
63
+ return this.sessionGrants[category];
64
+ }
65
+ /**
66
+ * Reset all session grants
67
+ */
68
+ reset() {
69
+ this.sessionGrants = {
70
+ file_write: false,
71
+ file_delete: false,
72
+ run_command: false,
73
+ };
74
+ }
75
+ /**
76
+ * Build feedback message for denied operations
77
+ */
78
+ buildDenyFeedback(request) {
79
+ const { category, details } = request;
80
+ switch (category) {
81
+ case "run_command":
82
+ return `User denied permission to run command: ${details.command}${details.args?.length ? ` ${details.args.join(" ")}` : ""}. Please suggest an alternative approach or ask the user what they'd like to do instead.`;
83
+ case "file_write":
84
+ return `User denied permission to write to: ${details.path}. Please suggest an alternative approach or ask the user what they'd like to do instead.`;
85
+ case "file_delete":
86
+ return `User denied permission to delete: ${details.path}. Please suggest an alternative approach or ask the user what they'd like to do instead.`;
87
+ default:
88
+ return `User denied permission for ${category}. Please suggest an alternative approach.`;
89
+ }
90
+ }
91
+ }
92
+ // Singleton instance for use across the application
93
+ export const permissionGuard = new PermissionGuard();
94
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../../src/lib/permissions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+BH,MAAM,OAAO,eAAe;IAClB,aAAa,CAAqB;IAClC,QAAQ,GAA8B,IAAI,CAAC;IAEnD;QACE,IAAI,CAAC,aAAa,GAAG;YACnB,UAAU,EAAE,KAAK;YACjB,WAAW,EAAE,KAAK;YAClB,WAAW,EAAE,KAAK;SACnB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,EAAsB;QAChC,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,OAA0B;QACpC,6CAA6C;QAC7C,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;QACzB,CAAC;QAED,yDAAyD;QACzD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,2BAA2B,OAAO,CAAC,QAAQ,qCAAqC;aAC3F,CAAC;QACJ,CAAC;QAED,6BAA6B;QAC7B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE9C,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,eAAe;gBAClB,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACpC,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;YAEzB,KAAK,YAAY;gBACf,OAAO,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC;YAEzB,KAAK,MAAM,CAAC;YACZ;gBACE,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;iBAC1C,CAAC;QACN,CAAC;IACH,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,QAA4B;QACvC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,QAA4B;QAC1C,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,aAAa,GAAG;YACnB,UAAU,EAAE,KAAK;YACjB,WAAW,EAAE,KAAK;YAClB,WAAW,EAAE,KAAK;SACnB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,iBAAiB,CAAC,OAA0B;QAClD,MAAM,EAAC,QAAQ,EAAE,OAAO,EAAC,GAAG,OAAO,CAAC;QAEpC,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,aAAa;gBAChB,OAAO,0CAA0C,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,0FAA0F,CAAC;YAExN,KAAK,YAAY;gBACf,OAAO,uCAAuC,OAAO,CAAC,IAAI,0FAA0F,CAAC;YAEvJ,KAAK,aAAa;gBAChB,OAAO,qCAAqC,OAAO,CAAC,IAAI,0FAA0F,CAAC;YAErJ;gBACE,OAAO,8BAA8B,QAAQ,2CAA2C,CAAC;QAC7F,CAAC;IACH,CAAC;CACF;AAED,oDAAoD;AACpD,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC"}