@dexto/tools-filesystem 1.7.1 → 1.8.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/dist/directory-approval.cjs +5 -4
- package/dist/directory-approval.d.ts +2 -1
- package/dist/directory-approval.d.ts.map +1 -1
- package/dist/directory-approval.integration.test.cjs +73 -33
- package/dist/directory-approval.integration.test.js +73 -33
- package/dist/directory-approval.js +2 -1
- package/dist/edit-file-tool.cjs +52 -37
- package/dist/edit-file-tool.d.ts +1 -1
- package/dist/edit-file-tool.d.ts.map +1 -1
- package/dist/edit-file-tool.js +43 -29
- package/dist/edit-file-tool.test.cjs +159 -2
- package/dist/edit-file-tool.test.js +159 -2
- package/dist/errors.cjs +53 -53
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/file-tool-types.d.ts +1 -1
- package/dist/file-tool-types.d.ts.map +1 -1
- package/dist/filesystem-service.cjs +60 -60
- package/dist/filesystem-service.d.ts +1 -1
- package/dist/filesystem-service.d.ts.map +1 -1
- package/dist/filesystem-service.js +5 -5
- package/dist/filesystem-service.test.cjs +1 -3
- package/dist/filesystem-service.test.js +1 -3
- package/dist/glob-files-tool.cjs +27 -24
- package/dist/glob-files-tool.d.ts +1 -1
- package/dist/glob-files-tool.d.ts.map +1 -1
- package/dist/glob-files-tool.js +24 -21
- package/dist/glob-files-tool.test.cjs +100 -88
- package/dist/glob-files-tool.test.js +101 -67
- package/dist/grep-content-tool.cjs +129 -44
- package/dist/grep-content-tool.d.ts +1 -1
- package/dist/grep-content-tool.d.ts.map +1 -1
- package/dist/grep-content-tool.js +120 -41
- package/dist/grep-content-tool.test.cjs +122 -87
- package/dist/grep-content-tool.test.js +123 -66
- package/dist/index.d.cts +3 -4
- package/dist/path-validator.d.ts +1 -1
- package/dist/path-validator.d.ts.map +1 -1
- package/dist/read-file-tool.cjs +43 -14
- package/dist/read-file-tool.d.ts +1 -1
- package/dist/read-file-tool.d.ts.map +1 -1
- package/dist/read-file-tool.js +40 -11
- package/dist/read-file-tool.test.cjs +119 -0
- package/dist/read-file-tool.test.d.ts +2 -0
- package/dist/read-file-tool.test.d.ts.map +1 -0
- package/dist/read-file-tool.test.js +96 -0
- package/dist/read-media-file-tool.cjs +4 -4
- package/dist/read-media-file-tool.d.ts +1 -1
- package/dist/read-media-file-tool.d.ts.map +1 -1
- package/dist/read-media-file-tool.js +1 -1
- package/dist/tool-factory.cjs +2 -2
- package/dist/tool-factory.js +1 -1
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/workspace-paths.cjs +87 -0
- package/dist/workspace-paths.d.ts +4 -0
- package/dist/workspace-paths.d.ts.map +1 -0
- package/dist/workspace-paths.js +51 -0
- package/dist/write-file-tool.cjs +74 -34
- package/dist/write-file-tool.d.ts +1 -2
- package/dist/write-file-tool.d.ts.map +1 -1
- package/dist/write-file-tool.js +68 -29
- package/dist/write-file-tool.test.cjs +262 -11
- package/dist/write-file-tool.test.js +262 -11
- package/package.json +3 -3
|
@@ -1,31 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
-
for (let key of __getOwnPropNames(from))
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
-
}
|
|
14
|
-
return to;
|
|
15
|
-
};
|
|
16
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
-
mod
|
|
23
|
-
));
|
|
24
2
|
var import_vitest = require("vitest");
|
|
25
|
-
var
|
|
26
|
-
var fs = __toESM(require("node:fs/promises"), 1);
|
|
27
|
-
var os = __toESM(require("node:os"), 1);
|
|
28
|
-
var import_filesystem_service = require("./filesystem-service.js");
|
|
3
|
+
var import_core = require("@dexto/core");
|
|
29
4
|
var import_glob_files_tool = require("./glob-files-tool.js");
|
|
30
5
|
const createMockLogger = () => {
|
|
31
6
|
const logger = {
|
|
@@ -44,72 +19,109 @@ const createMockLogger = () => {
|
|
|
44
19
|
};
|
|
45
20
|
return logger;
|
|
46
21
|
};
|
|
47
|
-
function createToolContext(logger) {
|
|
48
|
-
|
|
22
|
+
function createToolContext(logger, glob) {
|
|
23
|
+
const workspaceManager = {
|
|
24
|
+
open: import_vitest.vi.fn(async () => ({
|
|
25
|
+
context: {
|
|
26
|
+
id: "test-workspace",
|
|
27
|
+
path: "/repo",
|
|
28
|
+
createdAt: Date.now(),
|
|
29
|
+
lastActiveAt: Date.now()
|
|
30
|
+
},
|
|
31
|
+
capabilities: ["files"],
|
|
32
|
+
files: {
|
|
33
|
+
readFile: import_vitest.vi.fn(async () => ""),
|
|
34
|
+
readText: import_vitest.vi.fn(async () => ""),
|
|
35
|
+
glob,
|
|
36
|
+
writeFile: import_vitest.vi.fn(async () => void 0),
|
|
37
|
+
listFiles: import_vitest.vi.fn(async () => [])
|
|
38
|
+
}
|
|
39
|
+
}))
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
logger,
|
|
43
|
+
services: {
|
|
44
|
+
approval: {},
|
|
45
|
+
search: {},
|
|
46
|
+
resources: {},
|
|
47
|
+
prompts: {},
|
|
48
|
+
skills: {},
|
|
49
|
+
mcp: {},
|
|
50
|
+
taskForker: null,
|
|
51
|
+
workspaceManager
|
|
52
|
+
}
|
|
53
|
+
};
|
|
49
54
|
}
|
|
50
55
|
(0, import_vitest.describe)("glob_files tool", () => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
|
|
56
|
+
(0, import_vitest.it)("searches through WorkspaceManager.open without FileSystemService execution", async () => {
|
|
57
|
+
const mockLogger = createMockLogger();
|
|
58
|
+
const getFileSystemService = import_vitest.vi.fn(async () => {
|
|
59
|
+
throw new Error("glob_files execute must not use FileSystemService");
|
|
60
|
+
});
|
|
61
|
+
const glob = import_vitest.vi.fn(async () => ["src/a.ts", "src/b.ts", "src/c.ts"]);
|
|
62
|
+
const tool = (0, import_glob_files_tool.createGlobFilesTool)(getFileSystemService);
|
|
63
|
+
const parsedInput = tool.inputSchema.parse({
|
|
64
|
+
pattern: "**/*.ts",
|
|
65
|
+
path: "src",
|
|
66
|
+
max_results: 2
|
|
67
|
+
});
|
|
68
|
+
const result = await tool.execute(parsedInput, createToolContext(mockLogger, glob));
|
|
69
|
+
(0, import_vitest.expect)(getFileSystemService).not.toHaveBeenCalled();
|
|
70
|
+
(0, import_vitest.expect)(glob).toHaveBeenCalledWith("src/**/*.ts");
|
|
71
|
+
(0, import_vitest.expect)(result).toMatchObject({
|
|
72
|
+
files: [{ path: "src/a.ts" }, { path: "src/b.ts" }],
|
|
73
|
+
total_found: 2,
|
|
74
|
+
truncated: true
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
(0, import_vitest.it)("normalizes workspace-contained absolute search paths before globbing", async () => {
|
|
78
|
+
const mockLogger = createMockLogger();
|
|
79
|
+
const glob = import_vitest.vi.fn(async () => ["src/a.ts"]);
|
|
80
|
+
const tool = (0, import_glob_files_tool.createGlobFilesTool)(import_vitest.vi.fn());
|
|
81
|
+
await tool.execute(
|
|
82
|
+
tool.inputSchema.parse({
|
|
83
|
+
pattern: "**/*.ts",
|
|
84
|
+
path: "/repo/src"
|
|
85
|
+
}),
|
|
86
|
+
createToolContext(mockLogger, glob)
|
|
69
87
|
);
|
|
70
|
-
|
|
88
|
+
(0, import_vitest.expect)(glob).toHaveBeenCalledWith("src/**/*.ts");
|
|
71
89
|
});
|
|
72
|
-
(0, import_vitest.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
90
|
+
(0, import_vitest.it)("rejects external absolute search paths before file provider calls", async () => {
|
|
91
|
+
const mockLogger = createMockLogger();
|
|
92
|
+
const glob = import_vitest.vi.fn(async () => ["src/a.ts"]);
|
|
93
|
+
const tool = (0, import_glob_files_tool.createGlobFilesTool)(import_vitest.vi.fn());
|
|
94
|
+
await (0, import_vitest.expect)(
|
|
95
|
+
tool.execute(
|
|
96
|
+
tool.inputSchema.parse({
|
|
97
|
+
pattern: "**/*.ts",
|
|
98
|
+
path: "/outside/src"
|
|
99
|
+
}),
|
|
100
|
+
createToolContext(mockLogger, glob)
|
|
101
|
+
)
|
|
102
|
+
).rejects.toMatchObject({ code: import_core.ToolErrorCode.VALIDATION_FAILED });
|
|
103
|
+
(0, import_vitest.expect)(glob).not.toHaveBeenCalled();
|
|
78
104
|
});
|
|
79
|
-
(0, import_vitest.it)("
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
path: `~/${path.basename(homeTempDir)}`
|
|
101
|
-
});
|
|
102
|
-
const result = await tool.execute(parsedInput, createToolContext(mockLogger));
|
|
103
|
-
(0, import_vitest.expect)(result.total_found).toBe(1);
|
|
104
|
-
(0, import_vitest.expect)(result.files).toEqual([
|
|
105
|
-
{
|
|
106
|
-
path: filePath,
|
|
107
|
-
size: 5,
|
|
108
|
-
modified: import_vitest.expect.any(String)
|
|
109
|
-
}
|
|
110
|
-
]);
|
|
111
|
-
} finally {
|
|
112
|
-
await fs.rm(homeTempDir, { recursive: true, force: true });
|
|
113
|
-
}
|
|
105
|
+
(0, import_vitest.it)("rejects absolute and escaping glob patterns before file provider calls", async () => {
|
|
106
|
+
const mockLogger = createMockLogger();
|
|
107
|
+
const glob = import_vitest.vi.fn(async () => ["src/a.ts"]);
|
|
108
|
+
const tool = (0, import_glob_files_tool.createGlobFilesTool)(import_vitest.vi.fn());
|
|
109
|
+
await (0, import_vitest.expect)(
|
|
110
|
+
tool.execute(
|
|
111
|
+
tool.inputSchema.parse({
|
|
112
|
+
pattern: "/outside/**/*.ts"
|
|
113
|
+
}),
|
|
114
|
+
createToolContext(mockLogger, glob)
|
|
115
|
+
)
|
|
116
|
+
).rejects.toMatchObject({ code: import_core.ToolErrorCode.VALIDATION_FAILED });
|
|
117
|
+
await (0, import_vitest.expect)(
|
|
118
|
+
tool.execute(
|
|
119
|
+
tool.inputSchema.parse({
|
|
120
|
+
pattern: "../**/*.ts"
|
|
121
|
+
}),
|
|
122
|
+
createToolContext(mockLogger, glob)
|
|
123
|
+
)
|
|
124
|
+
).rejects.toMatchObject({ code: import_core.ToolErrorCode.VALIDATION_FAILED });
|
|
125
|
+
(0, import_vitest.expect)(glob).not.toHaveBeenCalled();
|
|
114
126
|
});
|
|
115
127
|
});
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import { describe, it, expect,
|
|
2
|
-
import
|
|
3
|
-
import * as fs from "node:fs/promises";
|
|
4
|
-
import * as os from "node:os";
|
|
5
|
-
import { FileSystemService } from "./filesystem-service.js";
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { ToolErrorCode } from "@dexto/core";
|
|
6
3
|
import { createGlobFilesTool } from "./glob-files-tool.js";
|
|
7
4
|
const createMockLogger = () => {
|
|
8
5
|
const logger = {
|
|
@@ -21,72 +18,109 @@ const createMockLogger = () => {
|
|
|
21
18
|
};
|
|
22
19
|
return logger;
|
|
23
20
|
};
|
|
24
|
-
function createToolContext(logger) {
|
|
25
|
-
|
|
21
|
+
function createToolContext(logger, glob) {
|
|
22
|
+
const workspaceManager = {
|
|
23
|
+
open: vi.fn(async () => ({
|
|
24
|
+
context: {
|
|
25
|
+
id: "test-workspace",
|
|
26
|
+
path: "/repo",
|
|
27
|
+
createdAt: Date.now(),
|
|
28
|
+
lastActiveAt: Date.now()
|
|
29
|
+
},
|
|
30
|
+
capabilities: ["files"],
|
|
31
|
+
files: {
|
|
32
|
+
readFile: vi.fn(async () => ""),
|
|
33
|
+
readText: vi.fn(async () => ""),
|
|
34
|
+
glob,
|
|
35
|
+
writeFile: vi.fn(async () => void 0),
|
|
36
|
+
listFiles: vi.fn(async () => [])
|
|
37
|
+
}
|
|
38
|
+
}))
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
logger,
|
|
42
|
+
services: {
|
|
43
|
+
approval: {},
|
|
44
|
+
search: {},
|
|
45
|
+
resources: {},
|
|
46
|
+
prompts: {},
|
|
47
|
+
skills: {},
|
|
48
|
+
mcp: {},
|
|
49
|
+
taskForker: null,
|
|
50
|
+
workspaceManager
|
|
51
|
+
}
|
|
52
|
+
};
|
|
26
53
|
}
|
|
27
54
|
describe("glob_files tool", () => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
|
|
55
|
+
it("searches through WorkspaceManager.open without FileSystemService execution", async () => {
|
|
56
|
+
const mockLogger = createMockLogger();
|
|
57
|
+
const getFileSystemService = vi.fn(async () => {
|
|
58
|
+
throw new Error("glob_files execute must not use FileSystemService");
|
|
59
|
+
});
|
|
60
|
+
const glob = vi.fn(async () => ["src/a.ts", "src/b.ts", "src/c.ts"]);
|
|
61
|
+
const tool = createGlobFilesTool(getFileSystemService);
|
|
62
|
+
const parsedInput = tool.inputSchema.parse({
|
|
63
|
+
pattern: "**/*.ts",
|
|
64
|
+
path: "src",
|
|
65
|
+
max_results: 2
|
|
66
|
+
});
|
|
67
|
+
const result = await tool.execute(parsedInput, createToolContext(mockLogger, glob));
|
|
68
|
+
expect(getFileSystemService).not.toHaveBeenCalled();
|
|
69
|
+
expect(glob).toHaveBeenCalledWith("src/**/*.ts");
|
|
70
|
+
expect(result).toMatchObject({
|
|
71
|
+
files: [{ path: "src/a.ts" }, { path: "src/b.ts" }],
|
|
72
|
+
total_found: 2,
|
|
73
|
+
truncated: true
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
it("normalizes workspace-contained absolute search paths before globbing", async () => {
|
|
77
|
+
const mockLogger = createMockLogger();
|
|
78
|
+
const glob = vi.fn(async () => ["src/a.ts"]);
|
|
79
|
+
const tool = createGlobFilesTool(vi.fn());
|
|
80
|
+
await tool.execute(
|
|
81
|
+
tool.inputSchema.parse({
|
|
82
|
+
pattern: "**/*.ts",
|
|
83
|
+
path: "/repo/src"
|
|
84
|
+
}),
|
|
85
|
+
createToolContext(mockLogger, glob)
|
|
46
86
|
);
|
|
47
|
-
|
|
87
|
+
expect(glob).toHaveBeenCalledWith("src/**/*.ts");
|
|
48
88
|
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
89
|
+
it("rejects external absolute search paths before file provider calls", async () => {
|
|
90
|
+
const mockLogger = createMockLogger();
|
|
91
|
+
const glob = vi.fn(async () => ["src/a.ts"]);
|
|
92
|
+
const tool = createGlobFilesTool(vi.fn());
|
|
93
|
+
await expect(
|
|
94
|
+
tool.execute(
|
|
95
|
+
tool.inputSchema.parse({
|
|
96
|
+
pattern: "**/*.ts",
|
|
97
|
+
path: "/outside/src"
|
|
98
|
+
}),
|
|
99
|
+
createToolContext(mockLogger, glob)
|
|
100
|
+
)
|
|
101
|
+
).rejects.toMatchObject({ code: ToolErrorCode.VALIDATION_FAILED });
|
|
102
|
+
expect(glob).not.toHaveBeenCalled();
|
|
55
103
|
});
|
|
56
|
-
it("
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
path: `~/${path.basename(homeTempDir)}`
|
|
78
|
-
});
|
|
79
|
-
const result = await tool.execute(parsedInput, createToolContext(mockLogger));
|
|
80
|
-
expect(result.total_found).toBe(1);
|
|
81
|
-
expect(result.files).toEqual([
|
|
82
|
-
{
|
|
83
|
-
path: filePath,
|
|
84
|
-
size: 5,
|
|
85
|
-
modified: expect.any(String)
|
|
86
|
-
}
|
|
87
|
-
]);
|
|
88
|
-
} finally {
|
|
89
|
-
await fs.rm(homeTempDir, { recursive: true, force: true });
|
|
90
|
-
}
|
|
104
|
+
it("rejects absolute and escaping glob patterns before file provider calls", async () => {
|
|
105
|
+
const mockLogger = createMockLogger();
|
|
106
|
+
const glob = vi.fn(async () => ["src/a.ts"]);
|
|
107
|
+
const tool = createGlobFilesTool(vi.fn());
|
|
108
|
+
await expect(
|
|
109
|
+
tool.execute(
|
|
110
|
+
tool.inputSchema.parse({
|
|
111
|
+
pattern: "/outside/**/*.ts"
|
|
112
|
+
}),
|
|
113
|
+
createToolContext(mockLogger, glob)
|
|
114
|
+
)
|
|
115
|
+
).rejects.toMatchObject({ code: ToolErrorCode.VALIDATION_FAILED });
|
|
116
|
+
await expect(
|
|
117
|
+
tool.execute(
|
|
118
|
+
tool.inputSchema.parse({
|
|
119
|
+
pattern: "../**/*.ts"
|
|
120
|
+
}),
|
|
121
|
+
createToolContext(mockLogger, glob)
|
|
122
|
+
)
|
|
123
|
+
).rejects.toMatchObject({ code: ToolErrorCode.VALIDATION_FAILED });
|
|
124
|
+
expect(glob).not.toHaveBeenCalled();
|
|
91
125
|
});
|
|
92
126
|
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
var grep_content_tool_exports = {};
|
|
20
30
|
__export(grep_content_tool_exports, {
|
|
@@ -22,9 +32,12 @@ __export(grep_content_tool_exports, {
|
|
|
22
32
|
});
|
|
23
33
|
module.exports = __toCommonJS(grep_content_tool_exports);
|
|
24
34
|
var import_zod = require("zod");
|
|
25
|
-
var
|
|
35
|
+
var import_safe_regex = __toESM(require("safe-regex"), 1);
|
|
36
|
+
var import_tools = require("@dexto/core/tools");
|
|
26
37
|
var import_directory_approval = require("./directory-approval.js");
|
|
27
38
|
var import_path_utils = require("./path-utils.js");
|
|
39
|
+
var import_errors = require("./errors.js");
|
|
40
|
+
var import_workspace_paths = require("./workspace-paths.js");
|
|
28
41
|
const GrepContentInputSchema = import_zod.z.object({
|
|
29
42
|
pattern: import_zod.z.string().describe("Regular expression pattern to search for"),
|
|
30
43
|
path: import_zod.z.string().optional().describe("Directory to search in (defaults to working directory)"),
|
|
@@ -36,7 +49,7 @@ const GrepContentInputSchema = import_zod.z.object({
|
|
|
36
49
|
max_results: import_zod.z.number().int().positive().optional().default(100).describe("Maximum number of results to return (default: 100)")
|
|
37
50
|
}).strict();
|
|
38
51
|
function createGrepContentTool(getFileSystemService) {
|
|
39
|
-
return (0,
|
|
52
|
+
return (0, import_tools.defineTool)({
|
|
40
53
|
id: "grep_content",
|
|
41
54
|
aliases: ["grep"],
|
|
42
55
|
description: 'Search for text patterns in files using regular expressions. Returns matching lines with file path, line number, and optional context lines. Use glob parameter to filter specific file types (e.g., "*.ts"). Supports case-insensitive search. Great for finding code patterns, function definitions, or specific text across multiple files.',
|
|
@@ -47,9 +60,9 @@ function createGrepContentTool(getFileSystemService) {
|
|
|
47
60
|
if (input.glob) bits.push(`glob=${input.glob}`);
|
|
48
61
|
if (input.path) bits.push(`path=${input.path}`);
|
|
49
62
|
if (typeof input.max_results === "number") bits.push(`max=${input.max_results}`);
|
|
50
|
-
return (0,
|
|
63
|
+
return (0, import_tools.createLocalToolCallHeader)({
|
|
51
64
|
title: "Search Files",
|
|
52
|
-
argsText: (0,
|
|
65
|
+
argsText: (0, import_tools.truncateForHeader)(bits.join(", "), 140)
|
|
53
66
|
});
|
|
54
67
|
}
|
|
55
68
|
},
|
|
@@ -65,7 +78,6 @@ function createGrepContentTool(getFileSystemService) {
|
|
|
65
78
|
}
|
|
66
79
|
}),
|
|
67
80
|
async execute(input, context) {
|
|
68
|
-
const resolvedFileSystemService = await getFileSystemService(context);
|
|
69
81
|
const {
|
|
70
82
|
pattern,
|
|
71
83
|
path: searchPath,
|
|
@@ -74,49 +86,122 @@ function createGrepContentTool(getFileSystemService) {
|
|
|
74
86
|
case_insensitive,
|
|
75
87
|
max_results
|
|
76
88
|
} = input;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
89
|
+
if (!context.services) {
|
|
90
|
+
throw new Error("grep_content requires ToolExecutionContext.services");
|
|
91
|
+
}
|
|
92
|
+
if (!(0, import_safe_regex.default)(pattern)) {
|
|
93
|
+
throw import_errors.FileSystemError.invalidPattern(
|
|
94
|
+
pattern,
|
|
95
|
+
"Pattern may cause catastrophic backtracking (ReDoS). Please simplify the regex."
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
const handle = await context.services.workspaceManager.open({ intent: "read" });
|
|
99
|
+
const regex = createRegex(pattern, case_insensitive);
|
|
100
|
+
const files = await findSearchFiles(
|
|
101
|
+
handle.files,
|
|
102
|
+
handle.context.path,
|
|
103
|
+
searchPath,
|
|
104
|
+
glob
|
|
105
|
+
);
|
|
106
|
+
const matches = [];
|
|
107
|
+
let filesSearched = 0;
|
|
108
|
+
for (const file of files) {
|
|
109
|
+
let content;
|
|
110
|
+
try {
|
|
111
|
+
content = await handle.files.readText(file);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
context.logger.debug(
|
|
114
|
+
`Skipping file ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
115
|
+
);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
filesSearched += 1;
|
|
119
|
+
const lines = content.split("\n");
|
|
120
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
121
|
+
const line = lines[index] ?? "";
|
|
122
|
+
regex.lastIndex = 0;
|
|
123
|
+
if (!regex.test(line)) continue;
|
|
124
|
+
const matchContext = context_lines > 0 ? collectContext(lines, index, context_lines) : void 0;
|
|
125
|
+
matches.push({
|
|
126
|
+
file,
|
|
127
|
+
lineNumber: index + 1,
|
|
128
|
+
line,
|
|
129
|
+
...matchContext !== void 0 && { context: matchContext }
|
|
130
|
+
});
|
|
131
|
+
if (matches.length >= max_results) {
|
|
132
|
+
return formatSearchResult(pattern, matches, filesSearched, true);
|
|
110
133
|
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
truncated: result.truncated,
|
|
115
|
-
_display
|
|
116
|
-
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return formatSearchResult(pattern, matches, filesSearched, false);
|
|
117
137
|
}
|
|
118
138
|
});
|
|
119
139
|
}
|
|
140
|
+
function createRegex(pattern, caseInsensitive) {
|
|
141
|
+
try {
|
|
142
|
+
return new RegExp(pattern, caseInsensitive ? "i" : "");
|
|
143
|
+
} catch {
|
|
144
|
+
throw import_errors.FileSystemError.invalidPattern(pattern, "Invalid regular expression syntax");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function findSearchFiles(files, workspaceRoot, searchPath, globPattern) {
|
|
148
|
+
const workspacePath = (0, import_workspace_paths.toWorkspaceRelativePath)("grep_content", workspaceRoot, searchPath || ".");
|
|
149
|
+
if (searchPath && !globPattern) {
|
|
150
|
+
try {
|
|
151
|
+
await files.readText(workspacePath);
|
|
152
|
+
return [workspacePath];
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (!(0, import_workspace_paths.isWorkspaceFileNotFound)(error)) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const pattern = globPattern ?? "**/*";
|
|
160
|
+
(0, import_workspace_paths.assertWorkspaceRelativeGlob)("grep_content", pattern);
|
|
161
|
+
if (workspacePath === "." || workspacePath === "") {
|
|
162
|
+
return files.glob(pattern);
|
|
163
|
+
}
|
|
164
|
+
return files.glob(`${workspacePath.replace(/\/$/, "")}/${pattern}`);
|
|
165
|
+
}
|
|
166
|
+
function collectContext(lines, matchIndex, contextLines) {
|
|
167
|
+
return {
|
|
168
|
+
before: lines.slice(Math.max(0, matchIndex - contextLines), matchIndex),
|
|
169
|
+
after: lines.slice(matchIndex + 1, matchIndex + contextLines + 1)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function formatSearchResult(pattern, matches, filesSearched, truncated) {
|
|
173
|
+
const _display = {
|
|
174
|
+
type: "search",
|
|
175
|
+
pattern,
|
|
176
|
+
matches: matches.map((match) => ({
|
|
177
|
+
file: match.file,
|
|
178
|
+
line: match.lineNumber,
|
|
179
|
+
content: match.line,
|
|
180
|
+
...match.context && {
|
|
181
|
+
context: [...match.context.before, ...match.context.after]
|
|
182
|
+
}
|
|
183
|
+
})),
|
|
184
|
+
totalMatches: matches.length,
|
|
185
|
+
truncated
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
matches: matches.map((match) => ({
|
|
189
|
+
file: match.file,
|
|
190
|
+
line_number: match.lineNumber,
|
|
191
|
+
line: match.line,
|
|
192
|
+
...match.context && {
|
|
193
|
+
context: {
|
|
194
|
+
before: match.context.before,
|
|
195
|
+
after: match.context.after
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
})),
|
|
199
|
+
total_matches: matches.length,
|
|
200
|
+
files_searched: filesSearched,
|
|
201
|
+
truncated,
|
|
202
|
+
_display
|
|
203
|
+
};
|
|
204
|
+
}
|
|
120
205
|
// Annotate the CommonJS export names for ESM import in node:
|
|
121
206
|
0 && (module.exports = {
|
|
122
207
|
createGrepContentTool
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Internal tool for searching file contents using regex patterns
|
|
5
5
|
*/
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
import type { Tool } from '@dexto/core';
|
|
7
|
+
import type { Tool } from '@dexto/core/tools';
|
|
8
8
|
import type { FileSystemServiceGetter } from './file-tool-types.js';
|
|
9
9
|
declare const GrepContentInputSchema: z.ZodObject<{
|
|
10
10
|
pattern: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grep-content-tool.d.ts","sourceRoot":"","sources":["../src/grep-content-tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"grep-content-tool.d.ts","sourceRoot":"","sources":["../src/grep-content-tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAqB,IAAI,EAAwB,MAAM,mBAAmB,CAAC;AACvF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAUpE,QAAA,MAAM,sBAAsB;;;;;;;kBAiCf,CAAC;AAEd;;GAEG;AACH,wBAAgB,qBAAqB,CACjC,oBAAoB,EAAE,uBAAuB,GAC9C,IAAI,CAAC,OAAO,sBAAsB,CAAC,CA0GrC"}
|