@timetotest/cli 0.2.4 → 0.3.0

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 (110) 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 +7 -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/components/ChatInput.js +61 -22
  8. package/dist/src/commands/chat/components/ChatInput.js.map +1 -1
  9. package/dist/src/commands/chat/components/ChatMessage.js +102 -0
  10. package/dist/src/commands/chat/components/ChatMessage.js.map +1 -0
  11. package/dist/src/commands/chat/components/MessageBubble.js +1 -1
  12. package/dist/src/commands/chat/components/MessageBubble.js.map +1 -1
  13. package/dist/src/commands/chat/components/PermissionPrompt.js +92 -0
  14. package/dist/src/commands/chat/components/PermissionPrompt.js.map +1 -0
  15. package/dist/src/commands/chat/components/StatusIndicator.js +21 -5
  16. package/dist/src/commands/chat/components/StatusIndicator.js.map +1 -1
  17. package/dist/src/commands/chat/components/ToolCallDisplay.js +141 -0
  18. package/dist/src/commands/chat/components/ToolCallDisplay.js.map +1 -0
  19. package/dist/src/commands/chat-ink.js +298 -79
  20. package/dist/src/commands/chat-ink.js.map +1 -1
  21. package/dist/src/commands/login.js +5 -5
  22. package/dist/src/commands/login.js.map +1 -1
  23. package/dist/src/commands/test.js +14 -291
  24. package/dist/src/commands/test.js.map +1 -1
  25. package/dist/src/lib/__tests__/code-mode-integration.test.js +381 -0
  26. package/dist/src/lib/__tests__/code-mode-integration.test.js.map +1 -0
  27. package/dist/src/lib/__tests__/config-manager.test.js +81 -0
  28. package/dist/src/lib/__tests__/config-manager.test.js.map +1 -0
  29. package/dist/src/lib/__tests__/mode-persistence-integration.test.js +75 -0
  30. package/dist/src/lib/__tests__/mode-persistence-integration.test.js.map +1 -0
  31. package/dist/src/lib/__tests__/permission-flow-integration.test.js +145 -0
  32. package/dist/src/lib/__tests__/permission-flow-integration.test.js.map +1 -0
  33. package/dist/src/lib/__tests__/permissions.test.js +132 -0
  34. package/dist/src/lib/__tests__/permissions.test.js.map +1 -0
  35. package/dist/src/lib/agent-orchestrator.js +259 -5
  36. package/dist/src/lib/agent-orchestrator.js.map +1 -1
  37. package/dist/src/lib/config.js +40 -0
  38. package/dist/src/lib/config.js.map +1 -1
  39. package/dist/src/lib/context-compactor.js +310 -0
  40. package/dist/src/lib/context-compactor.js.map +1 -0
  41. package/dist/src/lib/http.js +8 -0
  42. package/dist/src/lib/http.js.map +1 -1
  43. package/dist/src/lib/local-tools/code/__tests__/grep-search.test.js +146 -0
  44. package/dist/src/lib/local-tools/code/__tests__/grep-search.test.js.map +1 -0
  45. package/dist/src/lib/local-tools/code/__tests__/list-directory.test.js +192 -0
  46. package/dist/src/lib/local-tools/code/__tests__/list-directory.test.js.map +1 -0
  47. package/dist/src/lib/local-tools/code/__tests__/read-file.test.js +169 -0
  48. package/dist/src/lib/local-tools/code/__tests__/read-file.test.js.map +1 -0
  49. package/dist/src/lib/local-tools/code/__tests__/run-command.test.js +101 -0
  50. package/dist/src/lib/local-tools/code/__tests__/run-command.test.js.map +1 -0
  51. package/dist/src/lib/local-tools/code/__tests__/search-files.test.js +191 -0
  52. package/dist/src/lib/local-tools/code/__tests__/search-files.test.js.map +1 -0
  53. package/dist/src/lib/local-tools/code/grep-search.js +404 -0
  54. package/dist/src/lib/local-tools/code/grep-search.js.map +1 -0
  55. package/dist/src/lib/local-tools/code/index.js +11 -0
  56. package/dist/src/lib/local-tools/code/index.js.map +1 -0
  57. package/dist/src/lib/local-tools/code/list-directory.js +276 -0
  58. package/dist/src/lib/local-tools/code/list-directory.js.map +1 -0
  59. package/dist/src/lib/local-tools/code/read-file.js +301 -0
  60. package/dist/src/lib/local-tools/code/read-file.js.map +1 -0
  61. package/dist/src/lib/local-tools/code/run-command.js +235 -0
  62. package/dist/src/lib/local-tools/code/run-command.js.map +1 -0
  63. package/dist/src/lib/local-tools/code/search-files.js +297 -0
  64. package/dist/src/lib/local-tools/code/search-files.js.map +1 -0
  65. package/dist/src/lib/local-tools/code/types.js +6 -0
  66. package/dist/src/lib/local-tools/code/types.js.map +1 -0
  67. package/dist/src/lib/permissions.js +94 -0
  68. package/dist/src/lib/permissions.js.map +1 -0
  69. package/dist/src/lib/prompts/builder.js +13 -10
  70. package/dist/src/lib/prompts/builder.js.map +1 -1
  71. package/dist/src/lib/prompts/templates.js +78 -0
  72. package/dist/src/lib/prompts/templates.js.map +1 -1
  73. package/dist/src/lib/session-manager.js.map +1 -1
  74. package/dist/src/lib/testing-mode.js +2 -2
  75. package/dist/src/lib/testing-mode.js.map +1 -1
  76. package/dist/src/lib/tool-executor.js +131 -2
  77. package/dist/src/lib/tool-executor.js.map +1 -1
  78. package/dist/src/lib/tool-registry.js +171 -3
  79. package/dist/src/lib/tool-registry.js.map +1 -1
  80. package/dist/src/lib/tool-result-pruner.js +4 -4
  81. package/dist/src/lib/tool-result-pruner.js.map +1 -1
  82. package/dist/src/lib/tui/ink/components/AppFrame.js +17 -0
  83. package/dist/src/lib/tui/ink/components/AppFrame.js.map +1 -0
  84. package/dist/src/lib/tui/ink/components/CommandPalette.js +24 -0
  85. package/dist/src/lib/tui/ink/components/CommandPalette.js.map +1 -0
  86. package/dist/src/lib/tui/ink/components/Pill.js +19 -0
  87. package/dist/src/lib/tui/ink/components/Pill.js.map +1 -0
  88. package/dist/src/lib/tui/ink/components/TimetoTestLogo.js +30 -0
  89. package/dist/src/lib/tui/ink/components/TimetoTestLogo.js.map +1 -0
  90. package/dist/src/lib/tui/ink/theme.js +13 -6
  91. package/dist/src/lib/tui/ink/theme.js.map +1 -1
  92. package/dist/src/lib/tui/interactive-chat.js +35 -35
  93. package/dist/src/lib/tui/interactive-chat.js.map +1 -1
  94. package/dist/src/lib/tui/print.js +18 -18
  95. package/dist/src/lib/tui/print.js.map +1 -1
  96. package/dist/src/lib/tui/prompt.js +3 -3
  97. package/dist/src/lib/tui/prompt.js.map +1 -1
  98. package/dist/src/lib/tui/status.js +1 -1
  99. package/dist/src/lib/tui/status.js.map +1 -1
  100. package/dist/src/lib/update.js +10 -10
  101. package/dist/src/lib/update.js.map +1 -1
  102. package/package.json +7 -3
  103. package/dist/src/commands/start-test.js +0 -180
  104. package/dist/src/commands/start-test.js.map +0 -1
  105. package/dist/src/commands/stream/StreamApp.js +0 -127
  106. package/dist/src/commands/stream/StreamApp.js.map +0 -1
  107. package/dist/src/commands/stream.js +0 -52
  108. package/dist/src/commands/stream.js.map +0 -1
  109. package/dist/src/commands/test/TestRunApp.js +0 -183
  110. package/dist/src/commands/test/TestRunApp.js.map +0 -1
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Unit tests for list_directory tool.
3
+ * Tests directory listing, glob patterns, recursive listing, and hidden files.
4
+ */
5
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
6
+ import * as fs from "fs/promises";
7
+ import * as path from "path";
8
+ import * as os from "os";
9
+ import { listDirectory } from "../list-directory.js";
10
+ describe("listDirectory", () => {
11
+ let tempDir;
12
+ let context;
13
+ beforeEach(async () => {
14
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "list-dir-test-"));
15
+ context = { workingDirectory: tempDir };
16
+ });
17
+ afterEach(async () => {
18
+ await fs.rm(tempDir, { recursive: true, force: true });
19
+ });
20
+ describe("basic directory listing", () => {
21
+ it("should list directory contents", async () => {
22
+ await fs.writeFile(path.join(tempDir, "file1.txt"), "content1");
23
+ await fs.writeFile(path.join(tempDir, "file2.txt"), "content2");
24
+ await fs.mkdir(path.join(tempDir, "subdir"));
25
+ const result = await listDirectory({ path: "." }, context);
26
+ expect(result.success).toBe(true);
27
+ expect(result.total_count).toBe(3);
28
+ expect(result.entries.map((e) => e.name)).toContain("file1.txt");
29
+ expect(result.entries.map((e) => e.name)).toContain("file2.txt");
30
+ expect(result.entries.map((e) => e.name)).toContain("subdir");
31
+ });
32
+ it("should return error for non-existent directory", async () => {
33
+ const result = await listDirectory({ path: "nonexistent" }, context);
34
+ expect(result.success).toBe(false);
35
+ expect(result.error).toBe("Directory not found");
36
+ });
37
+ it("should return error for file path", async () => {
38
+ await fs.writeFile(path.join(tempDir, "file.txt"), "content");
39
+ const result = await listDirectory({ path: "file.txt" }, context);
40
+ expect(result.success).toBe(false);
41
+ expect(result.error).toBe("Path is not a directory");
42
+ });
43
+ it("should use current directory by default", async () => {
44
+ await fs.writeFile(path.join(tempDir, "test.txt"), "content");
45
+ const result = await listDirectory({}, context);
46
+ expect(result.success).toBe(true);
47
+ expect(result.path).toBe(".");
48
+ expect(result.entries.map((e) => e.name)).toContain("test.txt");
49
+ });
50
+ });
51
+ describe("glob pattern filtering", () => {
52
+ it("should filter by glob pattern", async () => {
53
+ await fs.writeFile(path.join(tempDir, "file1.txt"), "content");
54
+ await fs.writeFile(path.join(tempDir, "file2.txt"), "content");
55
+ await fs.writeFile(path.join(tempDir, "file3.js"), "content");
56
+ const result = await listDirectory({ pattern: "*.txt" }, context);
57
+ expect(result.success).toBe(true);
58
+ expect(result.total_count).toBe(2);
59
+ expect(result.entries.map((e) => e.name)).toContain("file1.txt");
60
+ expect(result.entries.map((e) => e.name)).toContain("file2.txt");
61
+ expect(result.entries.map((e) => e.name)).not.toContain("file3.js");
62
+ });
63
+ it("should support wildcard patterns", async () => {
64
+ await fs.writeFile(path.join(tempDir, "test1.ts"), "content");
65
+ await fs.writeFile(path.join(tempDir, "test2.ts"), "content");
66
+ await fs.writeFile(path.join(tempDir, "other.ts"), "content");
67
+ const result = await listDirectory({ pattern: "test*" }, context);
68
+ expect(result.success).toBe(true);
69
+ expect(result.total_count).toBe(2);
70
+ });
71
+ it("should support single character wildcard", async () => {
72
+ await fs.writeFile(path.join(tempDir, "file1.txt"), "content");
73
+ await fs.writeFile(path.join(tempDir, "file2.txt"), "content");
74
+ await fs.writeFile(path.join(tempDir, "file10.txt"), "content");
75
+ const result = await listDirectory({ pattern: "file?.txt" }, context);
76
+ expect(result.success).toBe(true);
77
+ expect(result.total_count).toBe(2);
78
+ expect(result.entries.map((e) => e.name)).not.toContain("file10.txt");
79
+ });
80
+ });
81
+ describe("recursive listing", () => {
82
+ it("should list recursively when enabled", async () => {
83
+ await fs.mkdir(path.join(tempDir, "subdir"));
84
+ await fs.writeFile(path.join(tempDir, "root.txt"), "content");
85
+ await fs.writeFile(path.join(tempDir, "subdir", "nested.txt"), "content");
86
+ const result = await listDirectory({ recursive: true }, context);
87
+ expect(result.success).toBe(true);
88
+ expect(result.entries.map((e) => e.name)).toContain("root.txt");
89
+ // Nested file should have relative path
90
+ const nestedEntry = result.entries.find((e) => e.name.includes("nested.txt"));
91
+ expect(nestedEntry).toBeDefined();
92
+ });
93
+ it("should respect max_depth", async () => {
94
+ await fs.mkdir(path.join(tempDir, "level1"));
95
+ await fs.mkdir(path.join(tempDir, "level1", "level2"));
96
+ await fs.mkdir(path.join(tempDir, "level1", "level2", "level3"));
97
+ await fs.writeFile(path.join(tempDir, "level1", "level2", "level3", "deep.txt"), "content");
98
+ const result = await listDirectory({ recursive: true, max_depth: 1 }, context);
99
+ expect(result.success).toBe(true);
100
+ // Should not include files deeper than max_depth
101
+ const deepFile = result.entries.find((e) => e.name.includes("deep.txt"));
102
+ expect(deepFile).toBeUndefined();
103
+ });
104
+ it("should not recurse by default", async () => {
105
+ await fs.mkdir(path.join(tempDir, "subdir"));
106
+ await fs.writeFile(path.join(tempDir, "subdir", "nested.txt"), "content");
107
+ const result = await listDirectory({}, context);
108
+ expect(result.success).toBe(true);
109
+ const nestedFile = result.entries.find((e) => e.name.includes("nested.txt"));
110
+ expect(nestedFile).toBeUndefined();
111
+ });
112
+ });
113
+ describe("hidden files", () => {
114
+ it("should exclude hidden files by default", async () => {
115
+ await fs.writeFile(path.join(tempDir, "visible.txt"), "content");
116
+ await fs.writeFile(path.join(tempDir, ".hidden"), "content");
117
+ const result = await listDirectory({}, context);
118
+ expect(result.success).toBe(true);
119
+ expect(result.entries.map((e) => e.name)).toContain("visible.txt");
120
+ expect(result.entries.map((e) => e.name)).not.toContain(".hidden");
121
+ });
122
+ it("should include hidden files when include_hidden is true", async () => {
123
+ await fs.writeFile(path.join(tempDir, "visible.txt"), "content");
124
+ await fs.writeFile(path.join(tempDir, ".hidden"), "content");
125
+ const result = await listDirectory({ include_hidden: true }, context);
126
+ expect(result.success).toBe(true);
127
+ expect(result.entries.map((e) => e.name)).toContain("visible.txt");
128
+ expect(result.entries.map((e) => e.name)).toContain(".hidden");
129
+ });
130
+ });
131
+ describe("path validation (security)", () => {
132
+ it("should prevent directory traversal", async () => {
133
+ const result = await listDirectory({ path: "../../../" }, context);
134
+ expect(result.success).toBe(false);
135
+ expect(result.error).toBe("Path validation failed: access denied");
136
+ });
137
+ it("should allow subdirectory access", async () => {
138
+ await fs.mkdir(path.join(tempDir, "subdir"));
139
+ await fs.writeFile(path.join(tempDir, "subdir", "file.txt"), "content");
140
+ const result = await listDirectory({ path: "subdir" }, context);
141
+ expect(result.success).toBe(true);
142
+ expect(result.entries.map((e) => e.name)).toContain("file.txt");
143
+ });
144
+ });
145
+ describe("entry metadata", () => {
146
+ it("should include file type", async () => {
147
+ await fs.writeFile(path.join(tempDir, "file.txt"), "content");
148
+ await fs.mkdir(path.join(tempDir, "dir"));
149
+ const result = await listDirectory({}, context);
150
+ expect(result.success).toBe(true);
151
+ const fileEntry = result.entries.find((e) => e.name === "file.txt");
152
+ const dirEntry = result.entries.find((e) => e.name === "dir");
153
+ expect(fileEntry?.type).toBe("file");
154
+ expect(dirEntry?.type).toBe("directory");
155
+ });
156
+ it("should include file size for files", async () => {
157
+ await fs.writeFile(path.join(tempDir, "file.txt"), "hello");
158
+ const result = await listDirectory({}, context);
159
+ expect(result.success).toBe(true);
160
+ const fileEntry = result.entries.find((e) => e.name === "file.txt");
161
+ expect(fileEntry?.size).toBe(5);
162
+ });
163
+ it("should include modification time", async () => {
164
+ await fs.writeFile(path.join(tempDir, "file.txt"), "content");
165
+ const result = await listDirectory({}, context);
166
+ expect(result.success).toBe(true);
167
+ const fileEntry = result.entries.find((e) => e.name === "file.txt");
168
+ expect(fileEntry?.modified).toBeDefined();
169
+ expect(new Date(fileEntry.modified).getTime()).toBeGreaterThan(0);
170
+ });
171
+ });
172
+ describe("sorting", () => {
173
+ it("should sort directories before files", async () => {
174
+ await fs.writeFile(path.join(tempDir, "aaa.txt"), "content");
175
+ await fs.mkdir(path.join(tempDir, "zzz"));
176
+ const result = await listDirectory({}, context);
177
+ expect(result.success).toBe(true);
178
+ expect(result.entries[0].name).toBe("zzz");
179
+ expect(result.entries[0].type).toBe("directory");
180
+ });
181
+ it("should sort alphabetically within type", async () => {
182
+ await fs.writeFile(path.join(tempDir, "c.txt"), "content");
183
+ await fs.writeFile(path.join(tempDir, "a.txt"), "content");
184
+ await fs.writeFile(path.join(tempDir, "b.txt"), "content");
185
+ const result = await listDirectory({}, context);
186
+ expect(result.success).toBe(true);
187
+ const names = result.entries.map((e) => e.name);
188
+ expect(names).toEqual(["a.txt", "b.txt", "c.txt"]);
189
+ });
190
+ });
191
+ });
192
+ //# sourceMappingURL=list-directory.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-directory.test.js","sourceRoot":"","sources":["../../../../../../src/lib/local-tools/code/__tests__/list-directory.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Unit tests for read_file tool.
3
+ * Tests file reading, line ranges, path validation, and language detection.
4
+ */
5
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
6
+ import * as fs from "fs/promises";
7
+ import * as path from "path";
8
+ import * as os from "os";
9
+ import { readFile } from "../read-file.js";
10
+ describe("readFile", () => {
11
+ let tempDir;
12
+ let context;
13
+ beforeEach(async () => {
14
+ // Create a temporary directory for tests
15
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "read-file-test-"));
16
+ context = { workingDirectory: tempDir };
17
+ });
18
+ afterEach(async () => {
19
+ // Clean up temporary directory
20
+ await fs.rm(tempDir, { recursive: true, force: true });
21
+ });
22
+ describe("basic file reading", () => {
23
+ it("should read a file successfully", async () => {
24
+ const testContent = "line 1\nline 2\nline 3";
25
+ await fs.writeFile(path.join(tempDir, "test.txt"), testContent);
26
+ const result = await readFile({ path: "test.txt" }, context);
27
+ expect(result.success).toBe(true);
28
+ expect(result.path).toBe("test.txt");
29
+ expect(result.total_lines).toBe(3);
30
+ expect(result.start_line).toBe(1);
31
+ expect(result.end_line).toBe(3);
32
+ expect(result.content).toContain("line 1");
33
+ expect(result.content).toContain("line 2");
34
+ expect(result.content).toContain("line 3");
35
+ });
36
+ it("should return error for non-existent file", async () => {
37
+ const result = await readFile({ path: "nonexistent.txt" }, context);
38
+ expect(result.success).toBe(false);
39
+ expect(result.error).toBe("File not found");
40
+ });
41
+ it("should return error for directory path", async () => {
42
+ await fs.mkdir(path.join(tempDir, "subdir"));
43
+ const result = await readFile({ path: "subdir" }, context);
44
+ expect(result.success).toBe(false);
45
+ expect(result.error).toBe("Path is a directory, not a file");
46
+ });
47
+ });
48
+ describe("line range support", () => {
49
+ it("should read specific line range", async () => {
50
+ const testContent = "line 1\nline 2\nline 3\nline 4\nline 5";
51
+ await fs.writeFile(path.join(tempDir, "test.txt"), testContent);
52
+ const result = await readFile({ path: "test.txt", start_line: 2, end_line: 4 }, context);
53
+ expect(result.success).toBe(true);
54
+ expect(result.start_line).toBe(2);
55
+ expect(result.end_line).toBe(4);
56
+ expect(result.content).toContain("line 2");
57
+ expect(result.content).toContain("line 3");
58
+ expect(result.content).toContain("line 4");
59
+ expect(result.content).not.toContain("line 1");
60
+ expect(result.content).not.toContain("line 5");
61
+ });
62
+ it("should clamp start_line to 1 if less than 1", async () => {
63
+ const testContent = "line 1\nline 2\nline 3";
64
+ await fs.writeFile(path.join(tempDir, "test.txt"), testContent);
65
+ const result = await readFile({ path: "test.txt", start_line: -5 }, context);
66
+ expect(result.success).toBe(true);
67
+ expect(result.start_line).toBe(1);
68
+ });
69
+ it("should clamp end_line to total_lines if exceeds file length", async () => {
70
+ const testContent = "line 1\nline 2\nline 3";
71
+ await fs.writeFile(path.join(tempDir, "test.txt"), testContent);
72
+ const result = await readFile({ path: "test.txt", end_line: 100 }, context);
73
+ expect(result.success).toBe(true);
74
+ expect(result.end_line).toBe(3);
75
+ });
76
+ it("should handle start_line greater than total_lines", async () => {
77
+ const testContent = "line 1\nline 2\nline 3";
78
+ await fs.writeFile(path.join(tempDir, "test.txt"), testContent);
79
+ const result = await readFile({ path: "test.txt", start_line: 100 }, context);
80
+ expect(result.success).toBe(true);
81
+ expect(result.start_line).toBe(3);
82
+ });
83
+ });
84
+ describe("line number formatting", () => {
85
+ it("should include line numbers by default", async () => {
86
+ const testContent = "line 1\nline 2";
87
+ await fs.writeFile(path.join(tempDir, "test.txt"), testContent);
88
+ const result = await readFile({ path: "test.txt" }, context);
89
+ expect(result.success).toBe(true);
90
+ expect(result.content).toMatch(/1 \| line 1/);
91
+ expect(result.content).toMatch(/2 \| line 2/);
92
+ });
93
+ it("should exclude line numbers when show_line_numbers is false", async () => {
94
+ const testContent = "line 1\nline 2";
95
+ await fs.writeFile(path.join(tempDir, "test.txt"), testContent);
96
+ const result = await readFile({ path: "test.txt", show_line_numbers: false }, context);
97
+ expect(result.success).toBe(true);
98
+ expect(result.content).toBe("line 1\nline 2");
99
+ expect(result.content).not.toContain("|");
100
+ });
101
+ });
102
+ describe("path validation (security)", () => {
103
+ it("should prevent directory traversal with ../", async () => {
104
+ const result = await readFile({ path: "../../../etc/passwd" }, context);
105
+ expect(result.success).toBe(false);
106
+ expect(result.error).toBe("Path validation failed: access denied");
107
+ });
108
+ it("should allow reading files in subdirectories", async () => {
109
+ await fs.mkdir(path.join(tempDir, "subdir"));
110
+ await fs.writeFile(path.join(tempDir, "subdir", "test.txt"), "content");
111
+ const result = await readFile({ path: "subdir/test.txt" }, context);
112
+ expect(result.success).toBe(true);
113
+ expect(result.content).toContain("content");
114
+ });
115
+ it("should handle absolute paths within working directory", async () => {
116
+ await fs.writeFile(path.join(tempDir, "test.txt"), "content");
117
+ const absolutePath = path.join(tempDir, "test.txt");
118
+ const result = await readFile({ path: absolutePath }, context);
119
+ expect(result.success).toBe(true);
120
+ });
121
+ });
122
+ describe("language detection", () => {
123
+ it("should detect TypeScript files", async () => {
124
+ await fs.writeFile(path.join(tempDir, "test.ts"), "const x = 1;");
125
+ const result = await readFile({ path: "test.ts" }, context);
126
+ expect(result.success).toBe(true);
127
+ expect(result.language).toBe("typescript");
128
+ });
129
+ it("should detect JavaScript files", async () => {
130
+ await fs.writeFile(path.join(tempDir, "test.js"), "const x = 1;");
131
+ const result = await readFile({ path: "test.js" }, context);
132
+ expect(result.success).toBe(true);
133
+ expect(result.language).toBe("javascript");
134
+ });
135
+ it("should detect Python files", async () => {
136
+ await fs.writeFile(path.join(tempDir, "test.py"), "x = 1");
137
+ const result = await readFile({ path: "test.py" }, context);
138
+ expect(result.success).toBe(true);
139
+ expect(result.language).toBe("python");
140
+ });
141
+ it("should detect JSON files", async () => {
142
+ await fs.writeFile(path.join(tempDir, "test.json"), "{}");
143
+ const result = await readFile({ path: "test.json" }, context);
144
+ expect(result.success).toBe(true);
145
+ expect(result.language).toBe("json");
146
+ });
147
+ it("should detect Markdown files", async () => {
148
+ await fs.writeFile(path.join(tempDir, "test.md"), "# Title");
149
+ const result = await readFile({ path: "test.md" }, context);
150
+ expect(result.success).toBe(true);
151
+ expect(result.language).toBe("markdown");
152
+ });
153
+ it("should return undefined for unknown extensions", async () => {
154
+ await fs.writeFile(path.join(tempDir, "test.xyz"), "content");
155
+ const result = await readFile({ path: "test.xyz" }, context);
156
+ expect(result.success).toBe(true);
157
+ expect(result.language).toBeUndefined();
158
+ });
159
+ });
160
+ describe("empty file handling", () => {
161
+ it("should handle empty files", async () => {
162
+ await fs.writeFile(path.join(tempDir, "empty.txt"), "");
163
+ const result = await readFile({ path: "empty.txt" }, context);
164
+ expect(result.success).toBe(true);
165
+ expect(result.total_lines).toBe(1); // Empty file has one empty line
166
+ });
167
+ });
168
+ });
169
+ //# sourceMappingURL=read-file.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-file.test.js","sourceRoot":"","sources":["../../../../../../src/lib/local-tools/code/__tests__/read-file.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAC,MAAM,QAAQ,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAC,QAAQ,EAAC,MAAM,iBAAiB,CAAC;AAGzC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,IAAI,OAAe,CAAC;IACpB,IAAI,OAAoB,CAAC;IAEzB,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,yCAAyC;QACzC,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;QACtE,OAAO,GAAG,EAAC,gBAAgB,EAAE,OAAO,EAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,+BAA+B;QAC/B,MAAM,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,EAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,WAAW,GAAG,wBAAwB,CAAC;YAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,UAAU,EAAC,EAAE,OAAO,CAAC,CAAC;YAE3D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACrC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,iBAAiB,EAAC,EAAE,OAAO,CAAC,CAAC;YAElE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;YAE7C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,QAAQ,EAAC,EAAE,OAAO,CAAC,CAAC;YAEzD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,WAAW,GAAG,wCAAwC,CAAC;YAC7D,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,EAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAC,EAC9C,OAAO,CACR,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YAC/C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,WAAW,GAAG,wBAAwB,CAAC;YAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,EAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,EAAC,EAClC,OAAO,CACR,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;YAC3E,MAAM,WAAW,GAAG,wBAAwB,CAAC;YAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAC,EAAE,OAAO,CAAC,CAAC;YAE1E,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,WAAW,GAAG,wBAAwB,CAAC;YAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,EAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAC,EACnC,OAAO,CACR,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,WAAW,GAAG,gBAAgB,CAAC;YACrC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,UAAU,EAAC,EAAE,OAAO,CAAC,CAAC;YAE3D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;YAC3E,MAAM,WAAW,GAAG,gBAAgB,CAAC;YACrC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,WAAW,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,EAAC,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAC,EAC5C,OAAO,CACR,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,qBAAqB,EAAC,EAAE,OAAO,CAAC,CAAC;YAEtE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC7C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;YAExE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,iBAAiB,EAAC,EAAE,OAAO,CAAC,CAAC;YAElE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;YACrE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YAEpD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,YAAY,EAAC,EAAE,OAAO,CAAC,CAAC;YAE7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,cAAc,CAAC,CAAC;YAElE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,OAAO,CAAC,CAAC;YAE1D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,cAAc,CAAC,CAAC;YAElE,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,OAAO,CAAC,CAAC;YAE1D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;YAE3D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,OAAO,CAAC,CAAC;YAE1D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE;YACxC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,WAAW,EAAC,EAAE,OAAO,CAAC,CAAC;YAE5D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;YAC5C,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;YAE7D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,SAAS,EAAC,EAAE,OAAO,CAAC,CAAC;YAE1D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;YAE9D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,UAAU,EAAC,EAAE,OAAO,CAAC,CAAC;YAE3D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,EAAE,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;YACzC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;YAExD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAC,IAAI,EAAE,WAAW,EAAC,EAAE,OAAO,CAAC,CAAC;YAE5D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,gCAAgC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Unit tests for run_command tool.
3
+ * Tests command execution, timeout, and working directory validation.
4
+ */
5
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
6
+ import * as fs from "fs/promises";
7
+ import * as path from "path";
8
+ import * as os from "os";
9
+ import { runCommand } from "../run-command.js";
10
+ describe("runCommand", () => {
11
+ let tempDir;
12
+ let context;
13
+ beforeEach(async () => {
14
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "run-command-test-"));
15
+ context = { workingDirectory: tempDir };
16
+ });
17
+ afterEach(async () => {
18
+ await fs.rm(tempDir, { recursive: true, force: true });
19
+ });
20
+ describe("command execution", () => {
21
+ it("should execute command successfully", async () => {
22
+ const result = await runCommand({ command: "node", args: ["--version"] }, context);
23
+ expect(result.success).toBe(true);
24
+ expect(result.exit_code).toBe(0);
25
+ expect(result.stdout).toMatch(/v\d+\.\d+\.\d+/);
26
+ });
27
+ it("should capture stdout", async () => {
28
+ const result = await runCommand({ command: "node", args: ["-e", 'console.log("hello")'] }, context);
29
+ expect(result.success).toBe(true);
30
+ expect(result.stdout.trim()).toBe("hello");
31
+ });
32
+ it("should capture stderr", async () => {
33
+ const result = await runCommand({ command: "node", args: ["-e", 'console.error("error msg")'] }, context);
34
+ expect(result.success).toBe(true);
35
+ expect(result.stderr.trim()).toBe("error msg");
36
+ });
37
+ it("should return non-zero exit code for failed commands", async () => {
38
+ const result = await runCommand({ command: "node", args: ["-e", "process.exit(1)"] }, context);
39
+ expect(result.success).toBe(false);
40
+ expect(result.exit_code).toBe(1);
41
+ });
42
+ });
43
+ describe("timeout", () => {
44
+ it("should timeout long-running commands", async () => {
45
+ const result = await runCommand({
46
+ command: "node",
47
+ args: ["-e", "setTimeout(() => {}, 10000)"],
48
+ timeout: 500,
49
+ }, context);
50
+ expect(result.success).toBe(false);
51
+ expect(result.timed_out).toBe(true);
52
+ expect(result.error).toContain("timed out");
53
+ }, 10000);
54
+ it("should complete before timeout for fast commands", async () => {
55
+ const result = await runCommand({ command: "node", args: ["-e", 'console.log("fast")'], timeout: 5000 }, context);
56
+ expect(result.success).toBe(true);
57
+ expect(result.timed_out).toBe(false);
58
+ });
59
+ it("should record duration", async () => {
60
+ const result = await runCommand({ command: "node", args: ["--version"] }, context);
61
+ expect(result.duration_ms).toBeGreaterThan(0);
62
+ expect(result.duration_ms).toBeLessThan(5000);
63
+ });
64
+ });
65
+ describe("working directory validation", () => {
66
+ it("should run command in specified working directory", async () => {
67
+ await fs.mkdir(path.join(tempDir, "subdir"));
68
+ const result = await runCommand({
69
+ command: "node",
70
+ args: ["-e", "console.log(process.cwd())"],
71
+ cwd: "subdir",
72
+ }, context);
73
+ expect(result.success).toBe(true);
74
+ expect(result.stdout).toContain("subdir");
75
+ });
76
+ it("should prevent directory traversal in cwd", async () => {
77
+ const result = await runCommand({ command: "node", args: ["--version"], cwd: "../../../" }, context);
78
+ expect(result.success).toBe(false);
79
+ expect(result.error).toContain("access denied");
80
+ });
81
+ it("should use working directory by default", async () => {
82
+ const result = await runCommand({ command: "node", args: ["-e", "console.log(process.cwd())"] }, context);
83
+ expect(result.success).toBe(true);
84
+ // Use realpath to handle macOS symlinks (/var -> /private/var)
85
+ const realTempDir = await fs.realpath(tempDir);
86
+ expect(result.stdout.trim()).toBe(realTempDir);
87
+ });
88
+ });
89
+ describe("environment variables", () => {
90
+ it("should pass additional environment variables", async () => {
91
+ const result = await runCommand({
92
+ command: "node",
93
+ args: ["-e", "console.log(process.env.TEST_VAR)"],
94
+ env: { TEST_VAR: "test_value" },
95
+ }, context);
96
+ expect(result.success).toBe(true);
97
+ expect(result.stdout.trim()).toBe("test_value");
98
+ });
99
+ });
100
+ });
101
+ //# sourceMappingURL=run-command.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-command.test.js","sourceRoot":"","sources":["../../../../../../src/lib/local-tools/code/__tests__/run-command.test.ts"],"names":[],"mappings":"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"}