@miphamai/cli 0.45.0 → 0.45.1
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/package.json +1 -1
- package/src/shared/package-info.ts +1 -1
- package/src/tools/file/grep.ts +60 -19
- package/src/ui/chat.tsx +4 -0
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
export const PACKAGE_NAME = '@miphamai/cli' as const
|
|
10
10
|
|
|
11
11
|
/** 当前发布版本 */
|
|
12
|
-
export const PACKAGE_VERSION = '0.45.
|
|
12
|
+
export const PACKAGE_VERSION = '0.45.1' as const
|
|
13
13
|
|
|
14
14
|
/** npm install 全局安装命令 */
|
|
15
15
|
export const NPM_INSTALL_COMMAND = `npm install -g ${PACKAGE_NAME}` as const
|
package/src/tools/file/grep.ts
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
|
-
import { $ } from 'bun'
|
|
2
1
|
import type { ToolDefinition } from '../../shared/index.ts'
|
|
3
2
|
import { resolveSafe } from '../../security/path'
|
|
4
3
|
|
|
4
|
+
/** Grep stall guard: a search over a huge tree (or a pathological regex) must
|
|
5
|
+
* not hang the session for minutes. Matches the Bash tool's 120s ceiling. */
|
|
6
|
+
const GREP_TIMEOUT_MS = 120_000
|
|
7
|
+
|
|
8
|
+
/** Run a search command with a kill-timer; report whether the timeout fired. */
|
|
9
|
+
export async function runSearch(
|
|
10
|
+
cmd: string[],
|
|
11
|
+
cwd: string,
|
|
12
|
+
timeoutMs: number,
|
|
13
|
+
): Promise<{ stdout: string; timedOut: boolean; exitCode: number | null }> {
|
|
14
|
+
const proc = Bun.spawn(cmd, { cwd, stdout: 'pipe', stderr: 'pipe' })
|
|
15
|
+
let timedOut = false
|
|
16
|
+
const timer = setTimeout(() => {
|
|
17
|
+
timedOut = true
|
|
18
|
+
proc.kill()
|
|
19
|
+
}, timeoutMs)
|
|
20
|
+
const stdout = await new Response(proc.stdout).text()
|
|
21
|
+
await proc.exited
|
|
22
|
+
clearTimeout(timer)
|
|
23
|
+
return { stdout, timedOut, exitCode: proc.exitCode }
|
|
24
|
+
}
|
|
25
|
+
|
|
5
26
|
export const grepTool: ToolDefinition = {
|
|
6
27
|
name: 'Grep',
|
|
7
28
|
description:
|
|
@@ -29,32 +50,52 @@ export const grepTool: ToolDefinition = {
|
|
|
29
50
|
const searchPath = resolveSafe(ctx.cwd, (params.path as string) || '.')
|
|
30
51
|
const include = params.include as string | undefined
|
|
31
52
|
|
|
53
|
+
// 1. ripgrep (fast path)
|
|
54
|
+
const rgArgs = ['rg', '-n', '--heading', '--color=never', '-M', '500', pattern]
|
|
55
|
+
if (include) rgArgs.push('--glob', include)
|
|
56
|
+
rgArgs.push(searchPath)
|
|
32
57
|
try {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
args.push(searchPath)
|
|
36
|
-
|
|
37
|
-
const result = await $`rg ${args}`.cwd(ctx.cwd).quiet().text()
|
|
38
|
-
return { success: true, content: result || '(no matches)' }
|
|
39
|
-
} catch (err: unknown) {
|
|
40
|
-
const exitErr = err as { exitCode?: number }
|
|
41
|
-
if (exitErr.exitCode === 1) {
|
|
42
|
-
return { success: true, content: '(no matches)' }
|
|
43
|
-
}
|
|
44
|
-
// Fallback to grep
|
|
45
|
-
try {
|
|
46
|
-
const content = await $`grep -rn ${pattern} ${searchPath}`.cwd(ctx.cwd).quiet().text()
|
|
58
|
+
const { stdout, timedOut, exitCode } = await runSearch(rgArgs, ctx.cwd, GREP_TIMEOUT_MS)
|
|
59
|
+
if (timedOut) {
|
|
47
60
|
return {
|
|
48
|
-
success:
|
|
49
|
-
content:
|
|
61
|
+
success: false,
|
|
62
|
+
content: '',
|
|
63
|
+
error: `Grep timed out after ${GREP_TIMEOUT_MS / 1000}s — narrow scope with "path" and "include".`,
|
|
50
64
|
}
|
|
51
|
-
}
|
|
65
|
+
}
|
|
66
|
+
if (exitCode === 1) return { success: true, content: '(no matches)' }
|
|
67
|
+
if (exitCode === 0) return { success: true, content: stdout || '(no matches)' }
|
|
68
|
+
// rg exit 2 (error) or null (killed) → fall through to grep
|
|
69
|
+
} catch {
|
|
70
|
+
// rg not installed → fall through to grep
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 2. fallback to plain grep
|
|
74
|
+
const grepArgs = ['grep', '-rn', pattern, searchPath]
|
|
75
|
+
try {
|
|
76
|
+
const { stdout, timedOut, exitCode } = await runSearch(grepArgs, ctx.cwd, GREP_TIMEOUT_MS)
|
|
77
|
+
if (timedOut) {
|
|
52
78
|
return {
|
|
53
79
|
success: false,
|
|
54
80
|
content: '',
|
|
55
|
-
error:
|
|
81
|
+
error: `Grep timed out after ${GREP_TIMEOUT_MS / 1000}s — narrow scope with "path" and "include".`,
|
|
56
82
|
}
|
|
57
83
|
}
|
|
84
|
+
if (exitCode === 1) return { success: true, content: '(no matches)' }
|
|
85
|
+
if (exitCode === 0) {
|
|
86
|
+
return { success: true, content: stdout.slice(0, 50000) || '(no matches)' }
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
content: '',
|
|
91
|
+
error: 'grep failed. Install ripgrep: brew install ripgrep',
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
return {
|
|
95
|
+
success: false,
|
|
96
|
+
content: '',
|
|
97
|
+
error: 'grep failed. Install ripgrep: brew install ripgrep',
|
|
98
|
+
}
|
|
58
99
|
}
|
|
59
100
|
},
|
|
60
101
|
}
|
package/src/ui/chat.tsx
CHANGED
|
@@ -83,6 +83,10 @@ const MessageRow = React.memo(
|
|
|
83
83
|
<Text bold color="green">
|
|
84
84
|
▸ {displayCwd()}:
|
|
85
85
|
</Text>
|
|
86
|
+
) : msg.role === 'assistant' ? (
|
|
87
|
+
<Text bold color="magenta">
|
|
88
|
+
◆ {t('ui.assistant.role_label')}:
|
|
89
|
+
</Text>
|
|
86
90
|
) : msg.role === 'system' ? (
|
|
87
91
|
<Text bold color="yellow">
|
|
88
92
|
⚠ {t('ui.system.role_label')}:
|