@deepwhale/coding-agent 1.0.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 (138) hide show
  1. package/bin/deepwhale.js +299 -0
  2. package/dist/agent/agent-compaction.d.ts +74 -0
  3. package/dist/agent/agent-compaction.d.ts.map +1 -0
  4. package/dist/agent/agent-compaction.js +145 -0
  5. package/dist/agent/agent-compaction.js.map +1 -0
  6. package/dist/agent/index.d.ts +16 -0
  7. package/dist/agent/index.d.ts.map +1 -0
  8. package/dist/agent/index.js +17 -0
  9. package/dist/agent/index.js.map +1 -0
  10. package/dist/agent/session-adapter.d.ts +177 -0
  11. package/dist/agent/session-adapter.d.ts.map +1 -0
  12. package/dist/agent/session-adapter.js +340 -0
  13. package/dist/agent/session-adapter.js.map +1 -0
  14. package/dist/agent/tool-loop.d.ts +123 -0
  15. package/dist/agent/tool-loop.d.ts.map +1 -0
  16. package/dist/agent/tool-loop.js +425 -0
  17. package/dist/agent/tool-loop.js.map +1 -0
  18. package/dist/env/load-project-env.d.ts +40 -0
  19. package/dist/env/load-project-env.d.ts.map +1 -0
  20. package/dist/env/load-project-env.js +80 -0
  21. package/dist/env/load-project-env.js.map +1 -0
  22. package/dist/index.d.ts +25 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +24 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/llm-factory.d.ts +47 -0
  27. package/dist/llm-factory.d.ts.map +1 -0
  28. package/dist/llm-factory.js +88 -0
  29. package/dist/llm-factory.js.map +1 -0
  30. package/dist/modes/index.d.ts +14 -0
  31. package/dist/modes/index.d.ts.map +1 -0
  32. package/dist/modes/index.js +14 -0
  33. package/dist/modes/index.js.map +1 -0
  34. package/dist/modes/print.d.ts +50 -0
  35. package/dist/modes/print.d.ts.map +1 -0
  36. package/dist/modes/print.js +236 -0
  37. package/dist/modes/print.js.map +1 -0
  38. package/dist/modes/rpc.d.ts +52 -0
  39. package/dist/modes/rpc.d.ts.map +1 -0
  40. package/dist/modes/rpc.js +316 -0
  41. package/dist/modes/rpc.js.map +1 -0
  42. package/dist/modes/tui.d.ts +54 -0
  43. package/dist/modes/tui.d.ts.map +1 -0
  44. package/dist/modes/tui.js +361 -0
  45. package/dist/modes/tui.js.map +1 -0
  46. package/dist/policy/args-digest.d.ts +13 -0
  47. package/dist/policy/args-digest.d.ts.map +1 -0
  48. package/dist/policy/args-digest.js +29 -0
  49. package/dist/policy/args-digest.js.map +1 -0
  50. package/dist/policy/chain.d.ts +19 -0
  51. package/dist/policy/chain.d.ts.map +1 -0
  52. package/dist/policy/chain.js +24 -0
  53. package/dist/policy/chain.js.map +1 -0
  54. package/dist/policy/sanitize-reason.d.ts +11 -0
  55. package/dist/policy/sanitize-reason.d.ts.map +1 -0
  56. package/dist/policy/sanitize-reason.js +24 -0
  57. package/dist/policy/sanitize-reason.js.map +1 -0
  58. package/dist/policy/static-rules.d.ts +32 -0
  59. package/dist/policy/static-rules.d.ts.map +1 -0
  60. package/dist/policy/static-rules.js +106 -0
  61. package/dist/policy/static-rules.js.map +1 -0
  62. package/dist/policy/types.d.ts +56 -0
  63. package/dist/policy/types.d.ts.map +1 -0
  64. package/dist/policy/types.js +13 -0
  65. package/dist/policy/types.js.map +1 -0
  66. package/dist/repl/repl-confirm.d.ts +49 -0
  67. package/dist/repl/repl-confirm.d.ts.map +1 -0
  68. package/dist/repl/repl-confirm.js +88 -0
  69. package/dist/repl/repl-confirm.js.map +1 -0
  70. package/dist/repl.d.ts +126 -0
  71. package/dist/repl.d.ts.map +1 -0
  72. package/dist/repl.js +734 -0
  73. package/dist/repl.js.map +1 -0
  74. package/dist/sandbox/docker-runner.d.ts +147 -0
  75. package/dist/sandbox/docker-runner.d.ts.map +1 -0
  76. package/dist/sandbox/docker-runner.js +426 -0
  77. package/dist/sandbox/docker-runner.js.map +1 -0
  78. package/dist/sandbox/env-gate.d.ts +28 -0
  79. package/dist/sandbox/env-gate.d.ts.map +1 -0
  80. package/dist/sandbox/env-gate.js +65 -0
  81. package/dist/sandbox/env-gate.js.map +1 -0
  82. package/dist/sandbox/local-runner.d.ts +29 -0
  83. package/dist/sandbox/local-runner.d.ts.map +1 -0
  84. package/dist/sandbox/local-runner.js +79 -0
  85. package/dist/sandbox/local-runner.js.map +1 -0
  86. package/dist/sandbox/types.d.ts +80 -0
  87. package/dist/sandbox/types.d.ts.map +1 -0
  88. package/dist/sandbox/types.js +25 -0
  89. package/dist/sandbox/types.js.map +1 -0
  90. package/dist/tools/bash.d.ts +35 -0
  91. package/dist/tools/bash.d.ts.map +1 -0
  92. package/dist/tools/bash.js +233 -0
  93. package/dist/tools/bash.js.map +1 -0
  94. package/dist/tools/edit-file.d.ts +22 -0
  95. package/dist/tools/edit-file.d.ts.map +1 -0
  96. package/dist/tools/edit-file.js +79 -0
  97. package/dist/tools/edit-file.js.map +1 -0
  98. package/dist/tools/find.d.ts +21 -0
  99. package/dist/tools/find.d.ts.map +1 -0
  100. package/dist/tools/find.js +168 -0
  101. package/dist/tools/find.js.map +1 -0
  102. package/dist/tools/grep.d.ts +19 -0
  103. package/dist/tools/grep.d.ts.map +1 -0
  104. package/dist/tools/grep.js +170 -0
  105. package/dist/tools/grep.js.map +1 -0
  106. package/dist/tools/index.d.ts +10 -0
  107. package/dist/tools/index.d.ts.map +1 -0
  108. package/dist/tools/index.js +10 -0
  109. package/dist/tools/index.js.map +1 -0
  110. package/dist/tools/read-file.d.ts +18 -0
  111. package/dist/tools/read-file.d.ts.map +1 -0
  112. package/dist/tools/read-file.js +52 -0
  113. package/dist/tools/read-file.js.map +1 -0
  114. package/dist/tools/registry.d.ts +39 -0
  115. package/dist/tools/registry.d.ts.map +1 -0
  116. package/dist/tools/registry.js +67 -0
  117. package/dist/tools/registry.js.map +1 -0
  118. package/dist/tools/write-file.d.ts +18 -0
  119. package/dist/tools/write-file.d.ts.map +1 -0
  120. package/dist/tools/write-file.js +47 -0
  121. package/dist/tools/write-file.js.map +1 -0
  122. package/dist/types.d.ts +89 -0
  123. package/dist/types.d.ts.map +1 -0
  124. package/dist/types.js +5 -0
  125. package/dist/types.js.map +1 -0
  126. package/dist/verify/format-report.d.ts +57 -0
  127. package/dist/verify/format-report.d.ts.map +1 -0
  128. package/dist/verify/format-report.js +128 -0
  129. package/dist/verify/format-report.js.map +1 -0
  130. package/dist/verify/index.d.ts +8 -0
  131. package/dist/verify/index.d.ts.map +1 -0
  132. package/dist/verify/index.js +8 -0
  133. package/dist/verify/index.js.map +1 -0
  134. package/dist/verify/verify-runner.d.ts +125 -0
  135. package/dist/verify/verify-runner.d.ts.map +1 -0
  136. package/dist/verify/verify-runner.js +524 -0
  137. package/dist/verify/verify-runner.js.map +1 -0
  138. package/package.json +30 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * edit_file 工具 — 通过 EditEngine 抽象修改文件
3
+ *
4
+ * Sprint 0.2 关键设计:edit_file 内部**不直接 import hashline**,
5
+ * 只走 EditEngine 接口(createDefaultEngine 工厂)。
6
+ * 这是 arch §2.3.2 抽象不破的核心证据。
7
+ *
8
+ * Sprint 0.2 范围:单文件 edit(patch 文本含 file path)
9
+ * Sprint 1 扩展:Recovery 3-way + multi-block + 跨文件 batch
10
+ */
11
+ import type { ToolName } from '@deepwhale/core';
12
+ import type { Tool, ToolInputSchema, ToolResult } from '../types.js';
13
+ export declare class EditFileTool implements Tool {
14
+ readonly name: ToolName;
15
+ readonly description = "Edit a local file using a hashline-format patch (3-hex TAG anchors). The patch is applied through the EditEngine abstraction \u2014 engine can be swapped via EDIT_ENGINE env var.";
16
+ readonly risk: 'low' | 'medium' | 'high';
17
+ readonly schema: ToolInputSchema;
18
+ /** Sprint 0.2 工厂:默认用 hashline,未来走 createEngine(name) */
19
+ private engine;
20
+ execute(input: Record<string, unknown>): Promise<ToolResult>;
21
+ }
22
+ //# sourceMappingURL=edit-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-file.d.ts","sourceRoot":"","sources":["../../src/tools/edit-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErE,qBAAa,YAAa,YAAW,IAAI;IACvC,QAAQ,CAAC,IAAI,EAAkB,QAAQ,CAAC;IACxC,QAAQ,CAAC,WAAW,wLAC8J;IAClL,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAY;IAEpD,QAAQ,CAAC,MAAM,EAAE,eAAe,CAO9B;IAEF,wDAAwD;IACxD,OAAO,CAAC,MAAM;IAIR,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;CAmDnE"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * edit_file 工具 — 通过 EditEngine 抽象修改文件
3
+ *
4
+ * Sprint 0.2 关键设计:edit_file 内部**不直接 import hashline**,
5
+ * 只走 EditEngine 接口(createDefaultEngine 工厂)。
6
+ * 这是 arch §2.3.2 抽象不破的核心证据。
7
+ *
8
+ * Sprint 0.2 范围:单文件 edit(patch 文本含 file path)
9
+ * Sprint 1 扩展:Recovery 3-way + multi-block + 跨文件 batch
10
+ */
11
+ import { promises as fs } from 'node:fs';
12
+ import { createDefaultEngine } from '@deepwhale/edit-engine';
13
+ export class EditFileTool {
14
+ name = 'edit_file';
15
+ description = 'Edit a local file using a hashline-format patch (3-hex TAG anchors). The patch is applied through the EditEngine abstraction — engine can be swapped via EDIT_ENGINE env var.';
16
+ risk = 'medium';
17
+ schema = {
18
+ type: 'object',
19
+ properties: {
20
+ path: { type: 'string', description: 'Absolute path to the file to edit' },
21
+ patch: { type: 'string', description: 'Hashline-format patch (use edit_engine to generate)' },
22
+ },
23
+ required: ['path', 'patch'],
24
+ };
25
+ /** Sprint 0.2 工厂:默认用 hashline,未来走 createEngine(name) */
26
+ engine() {
27
+ return createDefaultEngine();
28
+ }
29
+ async execute(input) {
30
+ const path = input['path'];
31
+ const patch = input['patch'];
32
+ if (typeof path !== 'string' || path.length === 0) {
33
+ return { success: false, content: '', error: 'invalid-input: path is required' };
34
+ }
35
+ if (typeof patch !== 'string' || patch.length === 0) {
36
+ return { success: false, content: '', error: 'invalid-input: patch is required' };
37
+ }
38
+ let original;
39
+ try {
40
+ original = await fs.readFile(path, 'utf8');
41
+ }
42
+ catch (err) {
43
+ const e = err;
44
+ if (e.code === 'ENOENT') {
45
+ return { success: false, content: '', error: `not-found: ${path}` };
46
+ }
47
+ return { success: false, content: '', error: `io-error: ${e.message}`, meta: { path } };
48
+ }
49
+ const target = { path, text: original };
50
+ const result = this.engine().apply(target, patch);
51
+ if (!result.ok) {
52
+ return {
53
+ success: false,
54
+ content: '',
55
+ error: `apply-${result.error.kind}: ${JSON.stringify(result.error)}`,
56
+ meta: { engine: this.engine().name },
57
+ };
58
+ }
59
+ // Sprint 1 加原子写(write to tmp + rename)。Sprint 0.2 简化:直接写。
60
+ try {
61
+ await fs.writeFile(path, result.newText, 'utf8');
62
+ return {
63
+ success: true,
64
+ content: `Applied patch via ${result.engine}. ${original.length} → ${result.newText.length} bytes.`,
65
+ meta: {
66
+ path,
67
+ engine: result.engine,
68
+ bytesBefore: original.length,
69
+ bytesAfter: result.newText.length,
70
+ },
71
+ };
72
+ }
73
+ catch (err) {
74
+ const e = err;
75
+ return { success: false, content: '', error: `io-error: ${e.message}`, meta: { path } };
76
+ }
77
+ }
78
+ }
79
+ //# sourceMappingURL=edit-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-file.js","sourceRoot":"","sources":["../../src/tools/edit-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * find 工具 — 文件系统查找
3
+ *
4
+ * Sprint 0.2 范围:跨平台 Node 实现(不走 find/ripgrep,避开 Windows find.exe 陷阱)
5
+ * Sprint 2+ 候选:换成 napi natives(Node 实现 + Profile 验证瓶颈)
6
+ *
7
+ * 设计:基于 fs.lstatSync(不跟随 symlink)+ readdirSync。lstat 而非 stat 是关键:
8
+ * - stat 跟随 symlink,type='l' 分支永远 false,且 visited 拿不到 realdir 去重
9
+ * - lstat 给出 link 自身属性,type='l' 才能正确命中
10
+ * - 递归不跟随 symlink 目录:避免 symlink 环路 + 减少越界
11
+ */
12
+ import type { ToolName } from '@deepwhale/core';
13
+ import type { Tool, ToolInputSchema, ToolResult } from '../types.js';
14
+ export declare class FindTool implements Tool {
15
+ readonly name: ToolName;
16
+ readonly description = "Find files by name pattern in a directory tree. Cross-platform Node implementation (no shell).";
17
+ readonly risk: 'low' | 'medium' | 'high';
18
+ readonly schema: ToolInputSchema;
19
+ execute(input: Record<string, unknown>): Promise<ToolResult>;
20
+ }
21
+ //# sourceMappingURL=find.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../src/tools/find.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKrE,qBAAa,QAAS,YAAW,IAAI;IACnC,QAAQ,CAAC,IAAI,EAAa,QAAQ,CAAC;IACnC,QAAQ,CAAC,WAAW,oGAC+E;IACnG,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAS;IAEjD,QAAQ,CAAC,MAAM,EAAE,eAAe,CAa9B;IAEI,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;CAgHnE"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * find 工具 — 文件系统查找
3
+ *
4
+ * Sprint 0.2 范围:跨平台 Node 实现(不走 find/ripgrep,避开 Windows find.exe 陷阱)
5
+ * Sprint 2+ 候选:换成 napi natives(Node 实现 + Profile 验证瓶颈)
6
+ *
7
+ * 设计:基于 fs.lstatSync(不跟随 symlink)+ readdirSync。lstat 而非 stat 是关键:
8
+ * - stat 跟随 symlink,type='l' 分支永远 false,且 visited 拿不到 realdir 去重
9
+ * - lstat 给出 link 自身属性,type='l' 才能正确命中
10
+ * - 递归不跟随 symlink 目录:避免 symlink 环路 + 减少越界
11
+ */
12
+ import { lstatSync, readdirSync, realpathSync } from 'node:fs';
13
+ import { resolve, join, relative } from 'node:path';
14
+ import process from 'node:process';
15
+ const MAX_DEPTH_DEFAULT = 10;
16
+ const MAX_RESULTS_DEFAULT = 1000;
17
+ export class FindTool {
18
+ name = 'find';
19
+ description = 'Find files by name pattern in a directory tree. Cross-platform Node implementation (no shell).';
20
+ risk = 'low';
21
+ schema = {
22
+ type: 'object',
23
+ properties: {
24
+ path: { type: 'string', description: 'Directory to search (default: cwd)' },
25
+ name: { type: 'string', description: 'Filename pattern (glob, e.g. *.ts)' },
26
+ type: {
27
+ type: 'string',
28
+ description: 'File type filter',
29
+ enum: ['f', 'd', 'l'],
30
+ },
31
+ maxDepth: { type: 'number', description: 'Max depth (default: 10)' },
32
+ },
33
+ required: ['path', 'name'],
34
+ };
35
+ async execute(input) {
36
+ const path = input['path'];
37
+ const name = input['name'];
38
+ const type = input['type'];
39
+ const maxDepth = typeof input['maxDepth'] === 'number' && input['maxDepth'] >= 0
40
+ ? Math.floor(input['maxDepth'])
41
+ : MAX_DEPTH_DEFAULT;
42
+ if (typeof path !== 'string' || path.length === 0) {
43
+ return { success: false, content: '', error: 'invalid-input: path is required' };
44
+ }
45
+ if (typeof name !== 'string' || name.length === 0) {
46
+ return { success: false, content: '', error: 'invalid-input: name is required' };
47
+ }
48
+ const rootPath = resolve(path);
49
+ let rootStat;
50
+ try {
51
+ rootStat = lstatSync(rootPath);
52
+ }
53
+ catch (err) {
54
+ const e = err;
55
+ return {
56
+ success: false,
57
+ content: '',
58
+ error: `not-found: ${e.message} (${e.code ?? 'UNKNOWN'})`,
59
+ };
60
+ }
61
+ if (!rootStat.isDirectory()) {
62
+ return {
63
+ success: false,
64
+ content: '',
65
+ error: `not-a-directory: '${path}' is not a directory`,
66
+ };
67
+ }
68
+ const wantFile = type === 'f';
69
+ const wantDir = type === 'd';
70
+ const wantLink = type === 'l';
71
+ const typeFilter = type === undefined ? null : { wantFile, wantDir, wantLink };
72
+ let regex;
73
+ try {
74
+ regex = globToRegExp(name);
75
+ }
76
+ catch (err) {
77
+ return {
78
+ success: false,
79
+ content: '',
80
+ error: `invalid-input: name glob invalid: ${err.message}`,
81
+ };
82
+ }
83
+ const matches = [];
84
+ // visited 用 realpath 算:symlink 指向的目录必须按真实路径去重,
85
+ // 否则 `dir -> /elsewhere/loop` 这类环会死循环 / 重复遍历。
86
+ const visited = new Set();
87
+ const walk = (dir, depth) => {
88
+ if (matches.length >= MAX_RESULTS_DEFAULT)
89
+ return;
90
+ // realpath 会跟随 symlink,realpathSync 失败(ENOENT 等)就跳过
91
+ let realDir;
92
+ try {
93
+ realDir = realpathSync(dir);
94
+ }
95
+ catch {
96
+ return;
97
+ }
98
+ if (visited.has(realDir))
99
+ return;
100
+ visited.add(realDir);
101
+ let entries;
102
+ try {
103
+ // 指定 encoding='utf8' 让 @types/node 推出 string[](避免 Dirent<NonSharedBuffer> 分支)
104
+ entries = readdirSync(dir, { encoding: 'utf8' });
105
+ }
106
+ catch {
107
+ return; // 跳过无权限 / 已删除目录
108
+ }
109
+ for (const name of entries) {
110
+ if (matches.length >= MAX_RESULTS_DEFAULT)
111
+ return;
112
+ const full = join(dir, name);
113
+ // lstatSync 不跟随 symlink — 这正是 type='l' 能正确命中的关键
114
+ const st = lstatSyncSafe(full);
115
+ if (st === null)
116
+ continue;
117
+ const isFile = st.isFile();
118
+ const isDir = st.isDirectory();
119
+ const isLink = st.isSymbolicLink();
120
+ const passType = typeFilter === null
121
+ ? true
122
+ : (typeFilter.wantFile && isFile) ||
123
+ (typeFilter.wantDir && isDir) ||
124
+ (typeFilter.wantLink && isLink);
125
+ if (passType && regex.test(name)) {
126
+ matches.push(relative(process.cwd(), full) || full);
127
+ }
128
+ // 只递归真实目录(不跟随 symlink 目录),避开环路 + 越界
129
+ if (isDir && !isLink && depth < maxDepth) {
130
+ walk(full, depth + 1);
131
+ }
132
+ }
133
+ };
134
+ walk(rootPath, 0);
135
+ return {
136
+ success: true,
137
+ content: matches.join('\n'),
138
+ meta: {
139
+ path,
140
+ name,
141
+ count: matches.length,
142
+ truncated: matches.length >= MAX_RESULTS_DEFAULT,
143
+ },
144
+ };
145
+ }
146
+ }
147
+ /**
148
+ * 把 `*.ts` 这类 glob 转成正则。Sprint 0.2 简化版只支持 `*` 和 `?`。
149
+ * Sprint 1+ 考虑 minimatch 之类。
150
+ */
151
+ function globToRegExp(glob) {
152
+ // 转义所有 regex 特殊字符,再把 `\*` 和 `\?` 还原
153
+ const escaped = glob
154
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
155
+ .replace(/\*/g, '.*')
156
+ .replace(/\?/g, '.');
157
+ return new RegExp(`^${escaped}$`);
158
+ }
159
+ /** 静默 lstatSync 失败(无权限 / 路径已消失),返回 null 让 caller 跳过。 */
160
+ function lstatSyncSafe(p) {
161
+ try {
162
+ return lstatSync(p);
163
+ }
164
+ catch {
165
+ return null;
166
+ }
167
+ }
168
+ //# sourceMappingURL=find.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find.js","sourceRoot":"","sources":["../../src/tools/find.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * grep 工具 — 文本搜索
3
+ *
4
+ * Sprint 0.2 范围:跨平台 Node 实现(不走 GNU grep,避开 Windows 缺失问题)
5
+ * Sprint 2+ 候选:ripgrep 子进程(oh-my-pi 借鉴)/ Rust napi
6
+ *
7
+ * 实现策略:基于 fs.readdirSync({ recursive: true }) + 行级正则匹配。
8
+ * 比 GNU grep 慢,但 v1.0 单人本地够用,跨平台 0 依赖。
9
+ */
10
+ import type { ToolName } from '@deepwhale/core';
11
+ import type { Tool, ToolInputSchema, ToolResult } from '../types.js';
12
+ export declare class GrepTool implements Tool {
13
+ readonly name: ToolName;
14
+ readonly description = "Search for a text pattern in files. Cross-platform Node implementation (no shell). Supports include glob for file filtering.";
15
+ readonly risk: 'low' | 'medium' | 'high';
16
+ readonly schema: ToolInputSchema;
17
+ execute(input: Record<string, unknown>): Promise<ToolResult>;
18
+ }
19
+ //# sourceMappingURL=grep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../../src/tools/grep.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKrE,qBAAa,QAAS,YAAW,IAAI;IACnC,QAAQ,CAAC,IAAI,EAAa,QAAQ,CAAC;IACnC,QAAQ,CAAC,WAAW,kIAC6G;IACjI,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAS;IAEjD,QAAQ,CAAC,MAAM,EAAE,eAAe,CAU9B;IAEI,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;CAgGnE"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * grep 工具 — 文本搜索
3
+ *
4
+ * Sprint 0.2 范围:跨平台 Node 实现(不走 GNU grep,避开 Windows 缺失问题)
5
+ * Sprint 2+ 候选:ripgrep 子进程(oh-my-pi 借鉴)/ Rust napi
6
+ *
7
+ * 实现策略:基于 fs.readdirSync({ recursive: true }) + 行级正则匹配。
8
+ * 比 GNU grep 慢,但 v1.0 单人本地够用,跨平台 0 依赖。
9
+ */
10
+ import { readdirSync, readFileSync, statSync } from 'node:fs';
11
+ import { resolve, join, relative } from 'node:path';
12
+ const MAX_RESULTS_DEFAULT = 100;
13
+ const MAX_FILE_BYTES = 2 * 1024 * 1024; // 2MB 文本上限(Sprint 0.2 简化:大文件直接跳过)
14
+ export class GrepTool {
15
+ name = 'grep';
16
+ description = 'Search for a text pattern in files. Cross-platform Node implementation (no shell). Supports include glob for file filtering.';
17
+ risk = 'low';
18
+ schema = {
19
+ type: 'object',
20
+ properties: {
21
+ pattern: { type: 'string', description: 'Search pattern (regex supported with regex=true)' },
22
+ path: { type: 'string', description: 'Directory or file to search (default: cwd)' },
23
+ include: { type: 'string', description: 'File glob filter, e.g. *.ts' },
24
+ regex: { type: 'boolean', description: 'Treat pattern as extended regex (default: true)' },
25
+ maxResults: { type: 'number', description: 'Cap number of matching lines (default: 100)' },
26
+ },
27
+ required: ['pattern'],
28
+ };
29
+ async execute(input) {
30
+ const pattern = input['pattern'];
31
+ const path = input['path'];
32
+ const include = input['include'];
33
+ const useRegex = input['regex'] !== false; // default true
34
+ const maxResults = typeof input['maxResults'] === 'number' && input['maxResults'] > 0
35
+ ? Math.floor(input['maxResults'])
36
+ : MAX_RESULTS_DEFAULT;
37
+ if (typeof pattern !== 'string' || pattern.length === 0) {
38
+ return { success: false, content: '', error: 'invalid-input: pattern is required' };
39
+ }
40
+ const searchPath = typeof path === 'string' && path.length > 0 ? path : '.';
41
+ const rootPath = resolve(searchPath);
42
+ let re;
43
+ try {
44
+ re = useRegex ? new RegExp(pattern) : escapeLiteralRegex(pattern);
45
+ }
46
+ catch (err) {
47
+ return {
48
+ success: false,
49
+ content: '',
50
+ error: `invalid-input: pattern is not a valid regex: ${err.message}`,
51
+ };
52
+ }
53
+ const includeRe = typeof include === 'string' ? globToRegExp(include) : null;
54
+ let rootStat;
55
+ try {
56
+ rootStat = statSync(rootPath);
57
+ }
58
+ catch (err) {
59
+ const e = err;
60
+ return {
61
+ success: false,
62
+ content: '',
63
+ error: `not-found: ${e.message} (${e.code ?? 'UNKNOWN'})`,
64
+ };
65
+ }
66
+ const matches = [];
67
+ const filesToScan = [];
68
+ if (rootStat.isFile()) {
69
+ filesToScan.push(rootPath);
70
+ }
71
+ else if (rootStat.isDirectory()) {
72
+ collectFiles(rootPath, filesToScan, includeRe, 0, 10);
73
+ }
74
+ else {
75
+ return {
76
+ success: false,
77
+ content: '',
78
+ error: `not-searchable: '${searchPath}' is not a file or directory`,
79
+ };
80
+ }
81
+ for (const file of filesToScan) {
82
+ if (matches.length >= maxResults)
83
+ break;
84
+ let text;
85
+ try {
86
+ const st = statSync(file);
87
+ if (st.size > MAX_FILE_BYTES)
88
+ continue; // 跳过超大文件
89
+ text = readFileSync(file, 'utf8');
90
+ }
91
+ catch {
92
+ continue; // 二进制 / 无权限 / race condition
93
+ }
94
+ // 检测 BOM。split 用 /\r?\n/:文件原文用什么换行就按什么切,
95
+ // 避免在 Windows 上 EOL='\r\n' 时把 LF 文件整段当一行(用户报告 P2-1)。
96
+ if (text.charCodeAt(0) === 0xfeff)
97
+ text = text.slice(1);
98
+ const lines = text.split(/\r?\n/);
99
+ for (let i = 0; i < lines.length; i++) {
100
+ const line = lines[i] ?? '';
101
+ if (re.test(line)) {
102
+ matches.push(`${relative(process.cwd(), file) || file}:${i + 1}:${line}`);
103
+ if (matches.length >= maxResults)
104
+ break;
105
+ }
106
+ }
107
+ }
108
+ const truncated = matches.length >= maxResults;
109
+ const content = truncated
110
+ ? matches.join('\n') + `\n\n[truncated to ${maxResults} matches]`
111
+ : matches.join('\n');
112
+ return {
113
+ success: true,
114
+ content,
115
+ meta: {
116
+ pattern,
117
+ searchPath,
118
+ matchCount: matches.length,
119
+ truncated,
120
+ filesScanned: filesToScan.length,
121
+ },
122
+ };
123
+ }
124
+ }
125
+ function collectFiles(dir, out, includeRe, depth, maxDepth) {
126
+ let entries;
127
+ try {
128
+ entries = readdirSync(dir, { encoding: 'utf8' });
129
+ }
130
+ catch {
131
+ return;
132
+ }
133
+ for (const name of entries) {
134
+ const full = join(dir, name);
135
+ const st = statSyncSafe(full);
136
+ if (st === null)
137
+ continue;
138
+ if (st.isFile()) {
139
+ if (includeRe === null || includeRe.test(name)) {
140
+ out.push(full);
141
+ }
142
+ }
143
+ else if (st.isDirectory() && depth < maxDepth) {
144
+ // 跳过常见噪声目录
145
+ if (name === 'node_modules' || name === '.git' || name === 'dist')
146
+ continue;
147
+ collectFiles(full, out, includeRe, depth + 1, maxDepth);
148
+ }
149
+ }
150
+ }
151
+ /** 静默 statSync 失败(无权限 / 路径已消失),返回 null 让 caller 跳过。 */
152
+ function statSyncSafe(p) {
153
+ try {
154
+ return statSync(p);
155
+ }
156
+ catch {
157
+ return null;
158
+ }
159
+ }
160
+ function escapeLiteralRegex(s) {
161
+ return new RegExp(s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
162
+ }
163
+ function globToRegExp(glob) {
164
+ const escaped = glob
165
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
166
+ .replace(/\*/g, '.*')
167
+ .replace(/\?/g, '.');
168
+ return new RegExp(`^${escaped}$`);
169
+ }
170
+ //# sourceMappingURL=grep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grep.js","sourceRoot":"","sources":["../../src/tools/grep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 6 工具(v1.0 Sprint 1 范围)
3
+ */
4
+ export { ReadFileTool } from './read-file.js';
5
+ export { WriteFileTool } from './write-file.js';
6
+ export { EditFileTool } from './edit-file.js';
7
+ export { BashTool } from './bash.js';
8
+ export { FindTool } from './find.js';
9
+ export { GrepTool } from './grep.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 6 工具(v1.0 Sprint 1 范围)
3
+ */
4
+ export { ReadFileTool } from './read-file.js';
5
+ export { WriteFileTool } from './write-file.js';
6
+ export { EditFileTool } from './edit-file.js';
7
+ export { BashTool } from './bash.js';
8
+ export { FindTool } from './find.js';
9
+ export { GrepTool } from './grep.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * read_file 工具 — 读取本地文件
3
+ *
4
+ * Sprint 0.2 范围:单文件 + offset/limit
5
+ * Sprint 1 扩展:行号 + 编码探测
6
+ *
7
+ * 沙箱:v1.0 本地直接读,Sprint 2 接入 Docker
8
+ */
9
+ import type { ToolName } from '@deepwhale/core';
10
+ import type { Tool, ToolInputSchema, ToolResult } from '../types.js';
11
+ export declare class ReadFileTool implements Tool {
12
+ readonly name: ToolName;
13
+ readonly description = "Read a local file. Returns file content with line numbers. Use offset/limit to read a slice.";
14
+ readonly risk: 'low' | 'medium' | 'high';
15
+ readonly schema: ToolInputSchema;
16
+ execute(input: Record<string, unknown>): Promise<ToolResult>;
17
+ }
18
+ //# sourceMappingURL=read-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-file.d.ts","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErE,qBAAa,YAAa,YAAW,IAAI;IACvC,QAAQ,CAAC,IAAI,EAAkB,QAAQ,CAAC;IACxC,QAAQ,CAAC,WAAW,kGAC6E;IACjG,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAS;IAEjD,QAAQ,CAAC,MAAM,EAAE,eAAe,CAQ9B;IAEI,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;CA4BnE"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * read_file 工具 — 读取本地文件
3
+ *
4
+ * Sprint 0.2 范围:单文件 + offset/limit
5
+ * Sprint 1 扩展:行号 + 编码探测
6
+ *
7
+ * 沙箱:v1.0 本地直接读,Sprint 2 接入 Docker
8
+ */
9
+ import { promises as fs } from 'node:fs';
10
+ export class ReadFileTool {
11
+ name = 'read_file';
12
+ description = 'Read a local file. Returns file content with line numbers. Use offset/limit to read a slice.';
13
+ risk = 'low';
14
+ schema = {
15
+ type: 'object',
16
+ properties: {
17
+ path: { type: 'string', description: 'Absolute path to the file' },
18
+ offset: { type: 'number', description: 'Starting line (0-indexed, default 0)' },
19
+ limit: { type: 'number', description: 'Max lines to read (default: all)' },
20
+ },
21
+ required: ['path'],
22
+ };
23
+ async execute(input) {
24
+ const path = input['path'];
25
+ if (typeof path !== 'string' || path.length === 0) {
26
+ return { success: false, content: '', error: 'invalid-input: path is required' };
27
+ }
28
+ const offset = typeof input['offset'] === 'number' ? input['offset'] : 0;
29
+ const limit = typeof input['limit'] === 'number' ? input['limit'] : Infinity;
30
+ try {
31
+ const text = await fs.readFile(path, 'utf8');
32
+ const lines = text.split('\n');
33
+ const slice = lines.slice(offset, offset + limit);
34
+ const numbered = slice
35
+ .map((l, i) => `${String(offset + i + 1).padStart(6, ' ')}\t${l}`)
36
+ .join('\n');
37
+ return {
38
+ success: true,
39
+ content: numbered,
40
+ meta: { totalLines: lines.length, readLines: slice.length },
41
+ };
42
+ }
43
+ catch (err) {
44
+ const e = err;
45
+ if (e.code === 'ENOENT') {
46
+ return { success: false, content: '', error: `not-found: ${path}` };
47
+ }
48
+ return { success: false, content: '', error: `io-error: ${e.message}`, meta: { path } };
49
+ }
50
+ }
51
+ }
52
+ //# sourceMappingURL=read-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AAIzC,MAAM,OAAO,YAAY;IACd,IAAI,GAAG,WAAuB,CAAC;IAC/B,WAAW,GAClB,8FAA8F,CAAC;IACxF,IAAI,GAA8B,KAAK,CAAC;IAExC,MAAM,GAAoB;QACjC,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE;YAClE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sCAAsC,EAAE;YAC/E,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kCAAkC,EAAE;SAC3E;QACD,QAAQ,EAAE,CAAC,MAAM,CAAC;KACnB,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,KAA8B;QAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;QACnF,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzE,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAE7E,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC;YAClD,MAAM,QAAQ,GAAG,KAAK;iBACnB,GAAG,CAAC,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;iBACjF,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,QAAQ;gBACjB,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE;aAC5D,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,GAAkE,CAAC;YAC7E,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACxB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,cAAc,IAAI,EAAE,EAAE,CAAC;YACtE,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;QAC1F,CAAC;IACH,CAAC;CACF"}