@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.
- package/README.md +49 -40
- package/dist/bin/ttt.js +0 -2
- package/dist/bin/ttt.js.map +1 -1
- package/dist/package.json +7 -3
- package/dist/src/commands/chat/ChatApp.js +249 -67
- package/dist/src/commands/chat/ChatApp.js.map +1 -1
- package/dist/src/commands/chat/components/ChatInput.js +61 -22
- package/dist/src/commands/chat/components/ChatInput.js.map +1 -1
- package/dist/src/commands/chat/components/ChatMessage.js +102 -0
- package/dist/src/commands/chat/components/ChatMessage.js.map +1 -0
- package/dist/src/commands/chat/components/MessageBubble.js +1 -1
- package/dist/src/commands/chat/components/MessageBubble.js.map +1 -1
- package/dist/src/commands/chat/components/PermissionPrompt.js +92 -0
- package/dist/src/commands/chat/components/PermissionPrompt.js.map +1 -0
- package/dist/src/commands/chat/components/StatusIndicator.js +21 -5
- package/dist/src/commands/chat/components/StatusIndicator.js.map +1 -1
- package/dist/src/commands/chat/components/ToolCallDisplay.js +141 -0
- package/dist/src/commands/chat/components/ToolCallDisplay.js.map +1 -0
- package/dist/src/commands/chat-ink.js +298 -79
- package/dist/src/commands/chat-ink.js.map +1 -1
- package/dist/src/commands/login.js +5 -5
- package/dist/src/commands/login.js.map +1 -1
- package/dist/src/commands/test.js +14 -291
- package/dist/src/commands/test.js.map +1 -1
- package/dist/src/lib/__tests__/code-mode-integration.test.js +381 -0
- package/dist/src/lib/__tests__/code-mode-integration.test.js.map +1 -0
- package/dist/src/lib/__tests__/config-manager.test.js +81 -0
- package/dist/src/lib/__tests__/config-manager.test.js.map +1 -0
- package/dist/src/lib/__tests__/mode-persistence-integration.test.js +75 -0
- package/dist/src/lib/__tests__/mode-persistence-integration.test.js.map +1 -0
- package/dist/src/lib/__tests__/permission-flow-integration.test.js +145 -0
- package/dist/src/lib/__tests__/permission-flow-integration.test.js.map +1 -0
- package/dist/src/lib/__tests__/permissions.test.js +132 -0
- package/dist/src/lib/__tests__/permissions.test.js.map +1 -0
- package/dist/src/lib/agent-orchestrator.js +259 -5
- package/dist/src/lib/agent-orchestrator.js.map +1 -1
- package/dist/src/lib/config.js +40 -0
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/context-compactor.js +310 -0
- package/dist/src/lib/context-compactor.js.map +1 -0
- package/dist/src/lib/http.js +8 -0
- package/dist/src/lib/http.js.map +1 -1
- package/dist/src/lib/local-tools/code/__tests__/grep-search.test.js +146 -0
- package/dist/src/lib/local-tools/code/__tests__/grep-search.test.js.map +1 -0
- package/dist/src/lib/local-tools/code/__tests__/list-directory.test.js +192 -0
- package/dist/src/lib/local-tools/code/__tests__/list-directory.test.js.map +1 -0
- package/dist/src/lib/local-tools/code/__tests__/read-file.test.js +169 -0
- package/dist/src/lib/local-tools/code/__tests__/read-file.test.js.map +1 -0
- package/dist/src/lib/local-tools/code/__tests__/run-command.test.js +101 -0
- package/dist/src/lib/local-tools/code/__tests__/run-command.test.js.map +1 -0
- package/dist/src/lib/local-tools/code/__tests__/search-files.test.js +191 -0
- package/dist/src/lib/local-tools/code/__tests__/search-files.test.js.map +1 -0
- package/dist/src/lib/local-tools/code/grep-search.js +404 -0
- package/dist/src/lib/local-tools/code/grep-search.js.map +1 -0
- package/dist/src/lib/local-tools/code/index.js +11 -0
- package/dist/src/lib/local-tools/code/index.js.map +1 -0
- package/dist/src/lib/local-tools/code/list-directory.js +276 -0
- package/dist/src/lib/local-tools/code/list-directory.js.map +1 -0
- package/dist/src/lib/local-tools/code/read-file.js +301 -0
- package/dist/src/lib/local-tools/code/read-file.js.map +1 -0
- package/dist/src/lib/local-tools/code/run-command.js +235 -0
- package/dist/src/lib/local-tools/code/run-command.js.map +1 -0
- package/dist/src/lib/local-tools/code/search-files.js +297 -0
- package/dist/src/lib/local-tools/code/search-files.js.map +1 -0
- package/dist/src/lib/local-tools/code/types.js +6 -0
- package/dist/src/lib/local-tools/code/types.js.map +1 -0
- package/dist/src/lib/permissions.js +94 -0
- package/dist/src/lib/permissions.js.map +1 -0
- package/dist/src/lib/prompts/builder.js +13 -10
- package/dist/src/lib/prompts/builder.js.map +1 -1
- package/dist/src/lib/prompts/templates.js +78 -0
- package/dist/src/lib/prompts/templates.js.map +1 -1
- package/dist/src/lib/session-manager.js.map +1 -1
- package/dist/src/lib/testing-mode.js +2 -2
- package/dist/src/lib/testing-mode.js.map +1 -1
- package/dist/src/lib/tool-executor.js +131 -2
- package/dist/src/lib/tool-executor.js.map +1 -1
- package/dist/src/lib/tool-registry.js +171 -3
- package/dist/src/lib/tool-registry.js.map +1 -1
- package/dist/src/lib/tool-result-pruner.js +4 -4
- package/dist/src/lib/tool-result-pruner.js.map +1 -1
- package/dist/src/lib/tui/ink/components/AppFrame.js +17 -0
- package/dist/src/lib/tui/ink/components/AppFrame.js.map +1 -0
- package/dist/src/lib/tui/ink/components/CommandPalette.js +24 -0
- package/dist/src/lib/tui/ink/components/CommandPalette.js.map +1 -0
- package/dist/src/lib/tui/ink/components/Pill.js +19 -0
- package/dist/src/lib/tui/ink/components/Pill.js.map +1 -0
- package/dist/src/lib/tui/ink/components/TimetoTestLogo.js +30 -0
- package/dist/src/lib/tui/ink/components/TimetoTestLogo.js.map +1 -0
- package/dist/src/lib/tui/ink/theme.js +13 -6
- package/dist/src/lib/tui/ink/theme.js.map +1 -1
- package/dist/src/lib/tui/interactive-chat.js +35 -35
- package/dist/src/lib/tui/interactive-chat.js.map +1 -1
- package/dist/src/lib/tui/print.js +18 -18
- package/dist/src/lib/tui/print.js.map +1 -1
- package/dist/src/lib/tui/prompt.js +3 -3
- package/dist/src/lib/tui/prompt.js.map +1 -1
- package/dist/src/lib/tui/status.js +1 -1
- package/dist/src/lib/tui/status.js.map +1 -1
- package/dist/src/lib/update.js +10 -10
- package/dist/src/lib/update.js.map +1 -1
- package/package.json +7 -3
- package/dist/src/commands/start-test.js +0 -180
- package/dist/src/commands/start-test.js.map +0 -1
- package/dist/src/commands/stream/StreamApp.js +0 -127
- package/dist/src/commands/stream/StreamApp.js.map +0 -1
- package/dist/src/commands/stream.js +0 -52
- package/dist/src/commands/stream.js.map +0 -1
- package/dist/src/commands/test/TestRunApp.js +0 -183
- 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":"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"}
|
|
@@ -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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"}
|