@johannes.latzel/llm-chat-file 0.1.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 (63) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +34 -0
  3. package/dist/index.d.ts +15 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +14 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/config.d.ts +89 -0
  8. package/dist/lib/config.d.ts.map +1 -0
  9. package/dist/lib/config.js +155 -0
  10. package/dist/lib/config.js.map +1 -0
  11. package/dist/lib/helpers.d.ts +9 -0
  12. package/dist/lib/helpers.d.ts.map +1 -0
  13. package/dist/lib/helpers.js +17 -0
  14. package/dist/lib/helpers.js.map +1 -0
  15. package/dist/lib/types.d.ts +23 -0
  16. package/dist/lib/types.d.ts.map +1 -0
  17. package/dist/lib/types.js +9 -0
  18. package/dist/lib/types.js.map +1 -0
  19. package/dist/lib/workspace.d.ts +75 -0
  20. package/dist/lib/workspace.d.ts.map +1 -0
  21. package/dist/lib/workspace.js +168 -0
  22. package/dist/lib/workspace.js.map +1 -0
  23. package/dist/tools/create-folder.d.ts +12 -0
  24. package/dist/tools/create-folder.d.ts.map +1 -0
  25. package/dist/tools/create-folder.js +39 -0
  26. package/dist/tools/create-folder.js.map +1 -0
  27. package/dist/tools/delete-file.d.ts +12 -0
  28. package/dist/tools/delete-file.d.ts.map +1 -0
  29. package/dist/tools/delete-file.js +54 -0
  30. package/dist/tools/delete-file.js.map +1 -0
  31. package/dist/tools/entry-info.d.ts +9 -0
  32. package/dist/tools/entry-info.d.ts.map +1 -0
  33. package/dist/tools/entry-info.js +107 -0
  34. package/dist/tools/entry-info.js.map +1 -0
  35. package/dist/tools/file-access-info.d.ts +12 -0
  36. package/dist/tools/file-access-info.d.ts.map +1 -0
  37. package/dist/tools/file-access-info.js +23 -0
  38. package/dist/tools/file-access-info.js.map +1 -0
  39. package/dist/tools/list-directory.d.ts +15 -0
  40. package/dist/tools/list-directory.d.ts.map +1 -0
  41. package/dist/tools/list-directory.js +120 -0
  42. package/dist/tools/list-directory.js.map +1 -0
  43. package/dist/tools/move-file.d.ts +12 -0
  44. package/dist/tools/move-file.d.ts.map +1 -0
  45. package/dist/tools/move-file.js +71 -0
  46. package/dist/tools/move-file.js.map +1 -0
  47. package/dist/tools/read-file.d.ts +14 -0
  48. package/dist/tools/read-file.d.ts.map +1 -0
  49. package/dist/tools/read-file.js +86 -0
  50. package/dist/tools/read-file.js.map +1 -0
  51. package/dist/tools/search.d.ts +17 -0
  52. package/dist/tools/search.d.ts.map +1 -0
  53. package/dist/tools/search.js +237 -0
  54. package/dist/tools/search.js.map +1 -0
  55. package/dist/tools/switch-workspace.d.ts +12 -0
  56. package/dist/tools/switch-workspace.d.ts.map +1 -0
  57. package/dist/tools/switch-workspace.js +34 -0
  58. package/dist/tools/switch-workspace.js.map +1 -0
  59. package/dist/tools/write-file.d.ts +15 -0
  60. package/dist/tools/write-file.d.ts.map +1 -0
  61. package/dist/tools/write-file.js +56 -0
  62. package/dist/tools/write-file.js.map +1 -0
  63. package/package.json +88 -0
@@ -0,0 +1,168 @@
1
+ import { Mutex } from 'async-mutex';
2
+ import * as fs from 'node:fs';
3
+ import * as fsp from 'node:fs/promises';
4
+ import * as path from 'node:path';
5
+ function isWithin(resolved, dirs) {
6
+ const real = path.resolve(resolved);
7
+ for (const dir of dirs) {
8
+ const d = path.resolve(dir);
9
+ const rel = path.relative(d, real);
10
+ if (rel && !rel.startsWith('..') && !path.isAbsolute(rel))
11
+ return true;
12
+ if (rel === '')
13
+ return true;
14
+ }
15
+ return false;
16
+ }
17
+ /**
18
+ * Manages the current workspace path and enforces access control for all file operations.
19
+ *
20
+ * Tracks which directories are accessible (read and/or write) and provides methods
21
+ * to check permissions, resolve paths, and switch the active workspace directory.
22
+ * Uses a mutex to ensure thread-safe workspace switching.
23
+ */
24
+ export class Workspace {
25
+ /** The currently active workspace directory (absolute path). */
26
+ currentPath;
27
+ cfg;
28
+ mutex;
29
+ /**
30
+ * @param config - Directory configuration defining accessible paths and their permission levels.
31
+ * @throws {Error} If the configuration contains no access entries.
32
+ */
33
+ constructor(config) {
34
+ const cleaned = config.deduplicate();
35
+ if (cleaned.accesses.length === 0) {
36
+ throw new Error('At least one access directory is required');
37
+ }
38
+ this.cfg = cleaned;
39
+ this.mutex = new Mutex();
40
+ if (cleaned.workspacePath) {
41
+ this.currentPath = path.resolve(cleaned.workspacePath);
42
+ }
43
+ else {
44
+ const writeDir = cleaned.accesses.find((a) => a.type === 'write');
45
+ this.currentPath = writeDir
46
+ ? path.resolve(writeDir.path)
47
+ : path.resolve(cleaned.accesses[0].path);
48
+ }
49
+ this.currentPath = this.resolvePath(this.currentPath);
50
+ }
51
+ /**
52
+ * Changes the current workspace path to a new directory, thread-safe with a mutex.
53
+ *
54
+ * @param target - Path to the new workspace directory (may be relative or absolute).
55
+ * @throws {Error} If the target is not within any configured accessible directory.
56
+ */
57
+ async switchWorkspace(target) {
58
+ await this.mutex.runExclusive(async () => {
59
+ let resolved = path.resolve(target);
60
+ resolved = this.resolvePath(resolved);
61
+ const allDirs = this.cfg.accesses.map((a) => path.resolve(a.path));
62
+ if (!isWithin(resolved, allDirs)) {
63
+ throw new Error(`Path is not within any configured directory: ${target}`);
64
+ }
65
+ this.currentPath = resolved;
66
+ });
67
+ }
68
+ /**
69
+ * Resolves a path against the current workspace. If the input is already absolute, `path.resolve` returns it as-is.
70
+ *
71
+ * @param input - Path to resolve (relative or absolute).
72
+ * @returns The resolved absolute path.
73
+ */
74
+ normalize(input) {
75
+ return this.resolvePath(input);
76
+ }
77
+ /**
78
+ * Checks whether the given absolute path is within any directory configured for read (or write) access.
79
+ *
80
+ * @param absPath - Absolute path to check.
81
+ * @returns `true` if the path is readable.
82
+ */
83
+ canRead(absPath) {
84
+ const pathToCheck = this.resolvePath(absPath);
85
+ const readDirs = this.cfg.accesses
86
+ .filter((a) => a.type === 'read')
87
+ .map((a) => path.resolve(a.path));
88
+ const writeDirs = this.cfg.accesses
89
+ .filter((a) => a.type === 'write')
90
+ .map((a) => path.resolve(a.path));
91
+ return isWithin(pathToCheck, [...readDirs, ...writeDirs]);
92
+ }
93
+ /**
94
+ * Checks whether the given absolute path is within any directory configured for write access.
95
+ *
96
+ * @param absPath - Absolute path to check.
97
+ * @returns `true` if the path is writable.
98
+ */
99
+ canWrite(absPath) {
100
+ const pathToCheck = this.resolvePath(absPath);
101
+ const writeDirs = this.cfg.accesses
102
+ .filter((a) => a.type === 'write')
103
+ .map((a) => path.resolve(a.path));
104
+ return isWithin(pathToCheck, writeDirs);
105
+ }
106
+ /**
107
+ * Returns the list of configured directory accesses with their types.
108
+ */
109
+ getAccesses() {
110
+ return this.cfg.accesses.map((a) => ({ type: a.type, path: path.resolve(a.path) }));
111
+ }
112
+ /**
113
+ * Returns the list of directory names to skip when walking (e.g. `node_modules`, `.git`).
114
+ */
115
+ get skipDirs() {
116
+ return this.cfg.skipDirs;
117
+ }
118
+ /** Whether symlink resolution is enabled for access checks. */
119
+ get resolveSymlinks() {
120
+ return this.cfg.resolveSymlinks;
121
+ }
122
+ resolvePath(input) {
123
+ const abs = path.resolve(this.currentPath, input);
124
+ if (!this.cfg.resolveSymlinks)
125
+ return abs;
126
+ try {
127
+ return fs.realpathSync.native(abs);
128
+ }
129
+ catch {
130
+ return abs;
131
+ }
132
+ }
133
+ /**
134
+ * Recursively walks a directory, yielding entries for files and directories.
135
+ * Skips directories whose names are listed in `cfg.skipDirs`.
136
+ *
137
+ * @param dir - Directory to walk.
138
+ * @param onError - Optional callback invoked when a subdirectory cannot be read.
139
+ * Receives the directory path and the error. The walk continues with other subtrees.
140
+ * @yields {WalkEntry} Entries for each file and subdirectory found.
141
+ */
142
+ async *walk(dir, onError) {
143
+ const resolved = this.resolvePath(dir);
144
+ if (!this.canRead(resolved))
145
+ return;
146
+ let entries;
147
+ try {
148
+ entries = await fsp.readdir(resolved, { withFileTypes: true });
149
+ }
150
+ catch (e) {
151
+ onError?.(resolved, e);
152
+ return;
153
+ }
154
+ for (const entry of entries) {
155
+ const fullPath = path.join(resolved, entry.name);
156
+ if (entry.isDirectory()) {
157
+ if (this.cfg.skipDirs.includes(entry.name))
158
+ continue;
159
+ yield { filePath: fullPath, dirent: entry };
160
+ yield* this.walk(fullPath, onError);
161
+ }
162
+ else if (entry.isFile()) {
163
+ yield { filePath: fullPath, dirent: entry };
164
+ }
165
+ }
166
+ }
167
+ }
168
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../src/lib/workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
2
+ import type { Workspace } from '../lib/workspace.js';
3
+ /** Tool that creates directories within the allowed workspace directories. */
4
+ export declare class CreateFolderTool extends Tool {
5
+ private ws;
6
+ /**
7
+ * @param workspace - Workspace instance for path resolution and access control.
8
+ */
9
+ constructor(workspace: Workspace);
10
+ protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
11
+ }
12
+ //# sourceMappingURL=create-folder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-folder.d.ts","sourceRoot":"","sources":["../../src/tools/create-folder.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAEjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,8EAA8E;AAC9E,qBAAa,gBAAiB,SAAQ,IAAI;IACtC,OAAO,CAAC,EAAE,CAAY;IAEtB;;OAEG;gBACS,SAAS,EAAE,SAAS;cAchB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAuBvF"}
@@ -0,0 +1,39 @@
1
+ import { ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
2
+ import * as fsp from 'node:fs/promises';
3
+ /** Tool that creates directories within the allowed workspace directories. */
4
+ export class CreateFolderTool extends Tool {
5
+ ws;
6
+ /**
7
+ * @param workspace - Workspace instance for path resolution and access control.
8
+ */
9
+ constructor(workspace) {
10
+ super('create_folder', 'Creates a directory and any necessary parent directories.', new ToolParameters({
11
+ path: new ToolParameterProperty('Directory path')
12
+ }, ['path']));
13
+ this.ws = workspace;
14
+ }
15
+ async onExecute(args) {
16
+ const raw = args.path;
17
+ if (typeof raw !== 'string' || !raw.trim()) {
18
+ return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
19
+ }
20
+ const resolved = this.ws.normalize(raw.trim());
21
+ if (!this.ws.canWrite(resolved)) {
22
+ return {
23
+ result: 'Invalid or inaccessible path (must be within writable directory)',
24
+ status: ResultStatus.Error
25
+ };
26
+ }
27
+ try {
28
+ await fsp.mkdir(resolved, { recursive: true });
29
+ return { result: `Created directory: ${resolved}/`, status: ResultStatus.Success };
30
+ }
31
+ catch (e) {
32
+ return {
33
+ result: `Error creating directory: ${e.message}`,
34
+ status: ResultStatus.Error
35
+ };
36
+ }
37
+ }
38
+ }
39
+ //# sourceMappingURL=create-folder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-folder.js","sourceRoot":"","sources":["../../src/tools/create-folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,YAAY,EACZ,IAAI,EACJ,qBAAqB,EACrB,cAAc,EACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AAGxC,8EAA8E;AAC9E,MAAM,OAAO,gBAAiB,SAAQ,IAAI;IAC9B,EAAE,CAAY;IAEtB;;OAEG;IACH,YAAY,SAAoB;QAC5B,KAAK,CACD,eAAe,EACf,2DAA2D,EAC3D,IAAI,cAAc,CACd;YACI,IAAI,EAAE,IAAI,qBAAqB,CAAC,gBAAgB,CAAC;SACpD,EACD,CAAC,MAAM,CAAC,CACX,CACJ,CAAC;QACF,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;IACxB,CAAC;IAES,KAAK,CAAC,SAAS,CAAC,IAA6B;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YACzC,OAAO,EAAE,MAAM,EAAE,8BAA8B,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;QAClF,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,OAAO;gBACH,MAAM,EAAE,kEAAkE;gBAC1E,MAAM,EAAE,YAAY,CAAC,KAAK;aAC7B,CAAC;QACN,CAAC;QAED,IAAI,CAAC;YACD,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,OAAO,EAAE,MAAM,EAAE,sBAAsB,QAAQ,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;QACvF,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO;gBACH,MAAM,EAAE,6BAA8B,CAAW,CAAC,OAAO,EAAE;gBAC3D,MAAM,EAAE,YAAY,CAAC,KAAK;aAC7B,CAAC;QACN,CAAC;IACL,CAAC;CACJ"}
@@ -0,0 +1,12 @@
1
+ import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
2
+ import type { Workspace } from '../lib/workspace.js';
3
+ /** Tool that deletes files and directories within the allowed workspace directories. */
4
+ export declare class DeleteFileTool extends Tool {
5
+ private ws;
6
+ /**
7
+ * @param workspace - Workspace instance for path resolution and access control.
8
+ */
9
+ constructor(workspace: Workspace);
10
+ protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
11
+ }
12
+ //# sourceMappingURL=delete-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-file.d.ts","sourceRoot":"","sources":["../../src/tools/delete-file.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAEjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,wFAAwF;AACxF,qBAAa,cAAe,SAAQ,IAAI;IACpC,OAAO,CAAC,EAAE,CAAY;IAEtB;;OAEG;gBACS,SAAS,EAAE,SAAS;cAiBhB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAqCvF"}
@@ -0,0 +1,54 @@
1
+ import { ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
2
+ import * as fsp from 'node:fs/promises';
3
+ /** Tool that deletes files and directories within the allowed workspace directories. */
4
+ export class DeleteFileTool extends Tool {
5
+ ws;
6
+ /**
7
+ * @param workspace - Workspace instance for path resolution and access control.
8
+ */
9
+ constructor(workspace) {
10
+ super('delete_file', 'Deletes a file or empty directory. Use recursive=true to delete non-empty directories and their contents.', new ToolParameters({
11
+ path: new ToolParameterProperty('File or directory path'),
12
+ recursive: new ToolParameterProperty('Delete directories and their contents recursively')
13
+ }, ['path']));
14
+ this.ws = workspace;
15
+ }
16
+ async onExecute(args) {
17
+ const raw = args.path;
18
+ if (typeof raw !== 'string' || !raw.trim()) {
19
+ return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
20
+ }
21
+ const resolved = this.ws.normalize(raw.trim());
22
+ if (!this.ws.canWrite(resolved)) {
23
+ return {
24
+ result: 'Invalid or inaccessible path (must be within writable directory)',
25
+ status: ResultStatus.Error
26
+ };
27
+ }
28
+ let stat;
29
+ try {
30
+ stat = await fsp.stat(resolved);
31
+ }
32
+ catch {
33
+ return { result: 'Path does not exist', status: ResultStatus.Error };
34
+ }
35
+ const label = stat.isDirectory() ? 'directory' : 'file';
36
+ const recursive = args.recursive === true;
37
+ try {
38
+ if (stat.isDirectory() && !recursive) {
39
+ await fsp.rmdir(resolved);
40
+ }
41
+ else {
42
+ await fsp.rm(resolved, { recursive: true });
43
+ }
44
+ return { result: `Deleted ${label}: ${resolved}`, status: ResultStatus.Success };
45
+ }
46
+ catch (e) {
47
+ return {
48
+ result: `Error deleting ${label}: ${e.message}`,
49
+ status: ResultStatus.Error
50
+ };
51
+ }
52
+ }
53
+ }
54
+ //# sourceMappingURL=delete-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delete-file.js","sourceRoot":"","sources":["../../src/tools/delete-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,YAAY,EACZ,IAAI,EACJ,qBAAqB,EACrB,cAAc,EACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AAGxC,wFAAwF;AACxF,MAAM,OAAO,cAAe,SAAQ,IAAI;IAC5B,EAAE,CAAY;IAEtB;;OAEG;IACH,YAAY,SAAoB;QAC5B,KAAK,CACD,aAAa,EACb,2GAA2G,EAC3G,IAAI,cAAc,CACd;YACI,IAAI,EAAE,IAAI,qBAAqB,CAAC,wBAAwB,CAAC;YACzD,SAAS,EAAE,IAAI,qBAAqB,CAChC,mDAAmD,CACtD;SACJ,EACD,CAAC,MAAM,CAAC,CACX,CACJ,CAAC;QACF,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;IACxB,CAAC;IAES,KAAK,CAAC,SAAS,CAAC,IAA6B;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YACzC,OAAO,EAAE,MAAM,EAAE,8BAA8B,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;QAClF,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,OAAO;gBACH,MAAM,EAAE,kEAAkE;gBAC1E,MAAM,EAAE,YAAY,CAAC,KAAK;aAC7B,CAAC;QACN,CAAC;QAED,IAAI,IAAI,CAAC;QACT,IAAI,CAAC;YACD,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;QACzE,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;QACxD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC;QAE1C,IAAI,CAAC;YACD,IAAI,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnC,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACJ,MAAM,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,WAAW,KAAK,KAAK,QAAQ,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;QACrF,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,OAAO;gBACH,MAAM,EAAE,kBAAkB,KAAK,KAAM,CAAW,CAAC,OAAO,EAAE;gBAC1D,MAAM,EAAE,YAAY,CAAC,KAAK;aAC7B,CAAC;QACN,CAAC;IACL,CAAC;CACJ"}
@@ -0,0 +1,9 @@
1
+ import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
2
+ import type { Workspace } from '../lib/workspace.js';
3
+ /** Tool that returns metadata about a filesystem entry (file, directory, symlink, etc.). */
4
+ export declare class EntryInfoTool extends Tool {
5
+ private ws;
6
+ constructor(workspace: Workspace);
7
+ protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
8
+ }
9
+ //# sourceMappingURL=entry-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-info.d.ts","sourceRoot":"","sources":["../../src/tools/entry-info.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAEjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAoDrD,4FAA4F;AAC5F,qBAAa,aAAc,SAAQ,IAAI;IACnC,OAAO,CAAC,EAAE,CAAY;gBAEV,SAAS,EAAE,SAAS;cAchB,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA2CvF"}
@@ -0,0 +1,107 @@
1
+ import { ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
2
+ import * as fsp from 'node:fs/promises';
3
+ import * as fs from 'node:fs';
4
+ /**
5
+ * Categorises the type of a filesystem entry based on `fs.Stats`.
6
+ * Used by `EntryInfoTool` to describe the entry in its output.
7
+ */
8
+ var EntryType;
9
+ (function (EntryType) {
10
+ /** Regular file. */
11
+ EntryType["File"] = "file";
12
+ /** Directory. */
13
+ EntryType["Directory"] = "directory";
14
+ /** Symbolic link. */
15
+ EntryType["Symlink"] = "symlink";
16
+ /** Named pipe (FIFO). */
17
+ EntryType["FIFO"] = "FIFO";
18
+ /** Unix domain socket. */
19
+ EntryType["Socket"] = "socket";
20
+ /** Character device. */
21
+ EntryType["CharacterDevice"] = "character device";
22
+ /** Block device. */
23
+ EntryType["BlockDevice"] = "block device";
24
+ /** Any other entry type not covered above. */
25
+ EntryType["Unknown"] = "unknown";
26
+ })(EntryType || (EntryType = {}));
27
+ function formatMode(mode) {
28
+ const usr = (mode & fs.constants.S_IRUSR ? 'r' : '-') +
29
+ (mode & fs.constants.S_IWUSR ? 'w' : '-') +
30
+ (mode & fs.constants.S_IXUSR ? 'x' : '-');
31
+ const grp = (mode & fs.constants.S_IRGRP ? 'r' : '-') +
32
+ (mode & fs.constants.S_IWGRP ? 'w' : '-') +
33
+ (mode & fs.constants.S_IXGRP ? 'x' : '-');
34
+ const oth = (mode & fs.constants.S_IROTH ? 'r' : '-') +
35
+ (mode & fs.constants.S_IWOTH ? 'w' : '-') +
36
+ (mode & fs.constants.S_IXOTH ? 'x' : '-');
37
+ return `${usr}${grp}${oth}`;
38
+ }
39
+ function entryType(stats) {
40
+ if (stats.isSymbolicLink())
41
+ return EntryType.Symlink;
42
+ if (stats.isDirectory())
43
+ return EntryType.Directory;
44
+ if (stats.isFile())
45
+ return EntryType.File;
46
+ if (stats.isFIFO())
47
+ return EntryType.FIFO;
48
+ if (stats.isSocket())
49
+ return EntryType.Socket;
50
+ if (stats.isCharacterDevice())
51
+ return EntryType.CharacterDevice;
52
+ if (stats.isBlockDevice())
53
+ return EntryType.BlockDevice;
54
+ return EntryType.Unknown;
55
+ }
56
+ /** Tool that returns metadata about a filesystem entry (file, directory, symlink, etc.). */
57
+ export class EntryInfoTool extends Tool {
58
+ ws;
59
+ constructor(workspace) {
60
+ super('entry_info', 'Returns metadata about a filesystem entry (file, directory, symlink, etc.) including its type, size, timestamps, permissions, and symlink target if applicable.', new ToolParameters({
61
+ path: new ToolParameterProperty('File system path')
62
+ }, ['path']));
63
+ this.ws = workspace;
64
+ }
65
+ async onExecute(args) {
66
+ const raw = args.path;
67
+ if (typeof raw !== 'string' || !raw.trim()) {
68
+ return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
69
+ }
70
+ const resolved = this.ws.normalize(raw.trim());
71
+ if (!this.ws.canRead(resolved)) {
72
+ return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
73
+ }
74
+ let stats;
75
+ try {
76
+ stats = await fsp.lstat(resolved);
77
+ }
78
+ catch (e) {
79
+ return {
80
+ result: `Path not found: ${e.message}`,
81
+ status: ResultStatus.Error
82
+ };
83
+ }
84
+ const type = entryType(stats);
85
+ const lines = [
86
+ `Path: ${resolved}`,
87
+ `Type: ${type}`,
88
+ `Size: ${stats.size} bytes`,
89
+ `Permissions: ${formatMode(stats.mode)}`,
90
+ `Created: ${stats.birthtime.toISOString()}`,
91
+ `Modified (content): ${stats.mtime.toISOString()}`,
92
+ `Accessed: ${stats.atime.toISOString()}`,
93
+ `Changed (metadata): ${stats.ctime.toISOString()}`
94
+ ];
95
+ if (stats.isSymbolicLink()) {
96
+ try {
97
+ const target = await fsp.readlink(resolved);
98
+ lines.push(`Symlink target: ${target}`);
99
+ }
100
+ catch {
101
+ lines.push('Symlink target: (unreadable)');
102
+ }
103
+ }
104
+ return { result: lines.join('\n'), status: ResultStatus.Success };
105
+ }
106
+ }
107
+ //# sourceMappingURL=entry-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-info.js","sourceRoot":"","sources":["../../src/tools/entry-info.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
2
+ import type { Workspace } from '../lib/workspace.js';
3
+ /** Tool that reports which directories are accessible and their access levels. */
4
+ export declare class FileAccessInfoTool extends Tool {
5
+ private ws;
6
+ /**
7
+ * @param workspace - Workspace instance providing access configuration.
8
+ */
9
+ constructor(workspace: Workspace);
10
+ protected onExecute(_args: Record<string, unknown>): Promise<PartialToolResult>;
11
+ }
12
+ //# sourceMappingURL=file-access-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-access-info.d.ts","sourceRoot":"","sources":["../../src/tools/file-access-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAgB,IAAI,EAAkB,MAAM,2BAA2B,CAAC;AAClG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,kFAAkF;AAClF,qBAAa,kBAAmB,SAAQ,IAAI;IACxC,OAAO,CAAC,EAAE,CAAY;IAEtB;;OAEG;gBACS,SAAS,EAAE,SAAS;cAShB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAYxF"}
@@ -0,0 +1,23 @@
1
+ import { ResultStatus, Tool, ToolParameters } from '@johannes.latzel/llm-chat';
2
+ /** Tool that reports which directories are accessible and their access levels. */
3
+ export class FileAccessInfoTool extends Tool {
4
+ ws;
5
+ /**
6
+ * @param workspace - Workspace instance providing access configuration.
7
+ */
8
+ constructor(workspace) {
9
+ super('file_access_info', 'Returns a list of all configured directories with their access levels (read/write) and indicates which one is the current workspace path. Use this to discover which parts of the filesystem are available for reading and writing.', new ToolParameters({}, []));
10
+ this.ws = workspace;
11
+ }
12
+ async onExecute(_args) {
13
+ const accesses = this.ws.getAccesses();
14
+ const current = this.ws.currentPath;
15
+ const lines = ['Configured file system access:'];
16
+ for (const a of accesses) {
17
+ const marker = a.path === current ? ' (current workspace)' : '';
18
+ lines.push(` ${a.path.padEnd(60)} ${a.type}${marker}`);
19
+ }
20
+ return { result: lines.join('\n'), status: ResultStatus.Success };
21
+ }
22
+ }
23
+ //# sourceMappingURL=file-access-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-access-info.js","sourceRoot":"","sources":["../../src/tools/file-access-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGlG,kFAAkF;AAClF,MAAM,OAAO,kBAAmB,SAAQ,IAAI;IAChC,EAAE,CAAY;IAEtB;;OAEG;IACH,YAAY,SAAoB;QAC5B,KAAK,CACD,kBAAkB,EAClB,qOAAqO,EACrO,IAAI,cAAc,CAAC,EAAE,EAAE,EAAE,CAAC,CAC7B,CAAC;QACF,IAAI,CAAC,EAAE,GAAG,SAAS,CAAC;IACxB,CAAC;IAES,KAAK,CAAC,SAAS,CAAC,KAA8B;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC;QAEpC,MAAM,KAAK,GAAa,CAAC,gCAAgC,CAAC,CAAC;QAC3D,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;IACtE,CAAC;CACJ"}
@@ -0,0 +1,15 @@
1
+ import { PartialToolResult, Tool } from '@johannes.latzel/llm-chat';
2
+ import { SearchConfiguration } from '../lib/config.js';
3
+ import type { Workspace } from '../lib/workspace.js';
4
+ /** Tool that lists files and directories in a given path within the workspace. */
5
+ export declare class ListDirectoryTool extends Tool {
6
+ private ws;
7
+ private sc;
8
+ /**
9
+ * @param workspace - Workspace instance for path resolution and access control.
10
+ * @param config - Optional search configuration (display/total entry limits). Defaults to a new `SearchConfiguration`.
11
+ */
12
+ constructor(workspace: Workspace, config?: SearchConfiguration);
13
+ protected onExecute(args: Record<string, unknown>): Promise<PartialToolResult>;
14
+ }
15
+ //# sourceMappingURL=list-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-directory.d.ts","sourceRoot":"","sources":["../../src/tools/list-directory.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,iBAAiB,EAGjB,IAAI,EAGP,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,kFAAkF;AAClF,qBAAa,iBAAkB,SAAQ,IAAI;IACvC,OAAO,CAAC,EAAE,CAAY;IACtB,OAAO,CAAC,EAAE,CAAsB;IAEhC;;;OAGG;gBACS,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,mBAAmB;cAmB9C,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA6GvF"}
@@ -0,0 +1,120 @@
1
+ import { PropertyType, ResultStatus, Tool, ToolParameterProperty, ToolParameters } from '@johannes.latzel/llm-chat';
2
+ import * as fsp from 'node:fs/promises';
3
+ import * as path from 'node:path';
4
+ import { SearchConfiguration } from '../lib/config.js';
5
+ /** Tool that lists files and directories in a given path within the workspace. */
6
+ export class ListDirectoryTool extends Tool {
7
+ ws;
8
+ sc;
9
+ /**
10
+ * @param workspace - Workspace instance for path resolution and access control.
11
+ * @param config - Optional search configuration (display/total entry limits). Defaults to a new `SearchConfiguration`.
12
+ */
13
+ constructor(workspace, config) {
14
+ super('list_directory', 'Lists files and directories in a path. Returns one entry per line; directories are suffixed with "/".', new ToolParameters({
15
+ path: new ToolParameterProperty('Directory path'),
16
+ recursive: new ToolParameterProperty('Set to true to list recursively depth-first', PropertyType.Boolean)
17
+ }, ['path']));
18
+ this.ws = workspace;
19
+ this.sc = config ?? new SearchConfiguration();
20
+ }
21
+ async onExecute(args) {
22
+ const raw = args.path;
23
+ if (typeof raw !== 'string' || !raw.trim()) {
24
+ return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
25
+ }
26
+ const resolved = this.ws.normalize(raw.trim());
27
+ if (!this.ws.canRead(resolved)) {
28
+ return { result: 'Invalid or inaccessible path', status: ResultStatus.Error };
29
+ }
30
+ let stat;
31
+ try {
32
+ stat = await fsp.stat(resolved);
33
+ }
34
+ catch (e) {
35
+ return {
36
+ result: `Path not found: ${e.message}`,
37
+ status: ResultStatus.Error
38
+ };
39
+ }
40
+ if (!stat.isDirectory()) {
41
+ return { result: 'Path is not a directory', status: ResultStatus.Error };
42
+ }
43
+ const recursive = args.recursive === true;
44
+ const entries = [];
45
+ let totalEntries = 0;
46
+ let totalFiles = 0;
47
+ let totalDirs = 0;
48
+ const walkErrors = [];
49
+ try {
50
+ if (recursive) {
51
+ for await (const entry of this.ws.walk(resolved, (dirPath, _err) => {
52
+ walkErrors.push(dirPath);
53
+ })) {
54
+ totalEntries++;
55
+ if (entry.dirent.isDirectory())
56
+ totalDirs++;
57
+ else
58
+ totalFiles++;
59
+ if (totalEntries >= this.sc.maxTotalEntries) {
60
+ const ew = walkErrors.length > 0
61
+ ? `(Warning: could not read ${walkErrors.length} director${walkErrors.length === 1 ? 'y' : 'ies'}, listing may be incomplete)\n\n`
62
+ : '';
63
+ return {
64
+ result: `${ew}Directory contains too many entries (${totalEntries}), refusing to list`,
65
+ status: ResultStatus.Error
66
+ };
67
+ }
68
+ if (totalEntries <= this.sc.maxDisplayEntries) {
69
+ entries.push(entry.dirent.isDirectory() ? entry.filePath + '/' : entry.filePath);
70
+ }
71
+ }
72
+ }
73
+ else {
74
+ const dir = await fsp.readdir(resolved, { withFileTypes: true });
75
+ const filtered = dir.filter((d) => !d.isDirectory() || !this.ws.skipDirs.includes(d.name));
76
+ totalEntries = filtered.length;
77
+ totalFiles = filtered.filter((d) => d.isFile()).length;
78
+ totalDirs = totalEntries - totalFiles;
79
+ if (totalEntries >= this.sc.maxTotalEntries) {
80
+ return {
81
+ result: `Directory contains too many entries (${totalEntries}), refusing to list`,
82
+ status: ResultStatus.Error
83
+ };
84
+ }
85
+ if (totalEntries <= this.sc.maxDisplayEntries) {
86
+ for (const d of filtered) {
87
+ const fullPath = path.join(resolved, d.name);
88
+ entries.push(d.isDirectory() ? fullPath + '/' : fullPath);
89
+ }
90
+ }
91
+ }
92
+ const errorWarning = recursive && walkErrors.length > 0
93
+ ? `(Warning: could not read ${walkErrors.length} director${walkErrors.length === 1 ? 'y' : 'ies'}, listing may be incomplete)\n\n`
94
+ : '';
95
+ if (totalEntries > this.sc.maxDisplayEntries) {
96
+ return {
97
+ result: `${errorWarning}Found ${totalFiles} files and ${totalDirs} directories`,
98
+ status: ResultStatus.Success
99
+ };
100
+ }
101
+ if (entries.length === 0) {
102
+ return {
103
+ result: `${errorWarning}(empty directory)`,
104
+ status: ResultStatus.Success
105
+ };
106
+ }
107
+ return {
108
+ result: errorWarning + entries.join('\n'),
109
+ status: ResultStatus.Success
110
+ };
111
+ }
112
+ catch (e) {
113
+ return {
114
+ result: `Error listing directory: ${e.message}`,
115
+ status: ResultStatus.Error
116
+ };
117
+ }
118
+ }
119
+ }
120
+ //# sourceMappingURL=list-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-directory.js","sourceRoot":"","sources":["../../src/tools/list-directory.ts"],"names":[],"mappings":"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"}