mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 22:35:51 +00:00
Fix/ripgrep fallback (#1273)
* fix: tmp 目录改用 os.tmpdir() + ripgrep 缺失时自动 fallback 系统 rg 1. Shell.ts / imagePaste.ts / filesystem.ts: Linux/macOS 默认 tmp 路径 从硬编码 '/tmp' 改为 os.tmpdir(),自动适配 Termux/Android 等无 /tmp 的环境;macOS 桌面零变化;CLAUDE_CODE_TMPDIR 仍优先级最高。 2. ripgrep.ts: builtin rg 二进制缺失时(Android/Termux、不完整安装) 自动 fallback 到 PATH 上的系统 rg,通过 note 字段携带人读提示; /doctor 渲染 note;init 启动时写一行 stderr warning。 Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win> * fix: review fix — ripgrep note 文案修正 + init catch 加调试日志 - ripgrep "no ripgrep available" note 去掉无意义的 USE_BUILTIN_RIPGREP=0 建议 - init.ts ripgrep status check 的空 catch 加 logForDebugging Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win> --------- Co-authored-by: glm-5.2 <zai-org@claude-code-best.win>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { ChildProcess, ExecFileException } from 'child_process'
|
||||
import { execFile, spawn } from 'child_process'
|
||||
import { existsSync } from 'fs'
|
||||
import memoize from 'lodash-es/memoize.js'
|
||||
import { homedir } from 'os'
|
||||
import * as path from 'path'
|
||||
@@ -24,9 +25,10 @@ type RipgrepConfig = {
|
||||
command: string
|
||||
args: string[]
|
||||
argv0?: string
|
||||
note?: string
|
||||
}
|
||||
|
||||
const getRipgrepConfig = memoize((): RipgrepConfig => {
|
||||
export const getRipgrepConfig = memoize((): RipgrepConfig => {
|
||||
const userWantsSystemRipgrep = isEnvDefinedFalsy(
|
||||
process.env.USE_BUILTIN_RIPGREP,
|
||||
)
|
||||
@@ -59,9 +61,61 @@ const getRipgrepConfig = memoize((): RipgrepConfig => {
|
||||
? path.resolve(rgRoot, `${process.arch}-win32`, 'rg.exe')
|
||||
: path.resolve(rgRoot, `${process.arch}-${process.platform}`, 'rg')
|
||||
|
||||
return { mode: 'builtin', command, args: [] }
|
||||
return resolveBuiltinWithFallback(command)
|
||||
})
|
||||
|
||||
/**
|
||||
* Pure function: decide what to do when the builtin rg binary may be missing.
|
||||
* Extracted so it can be tested without any module mocking.
|
||||
*
|
||||
* @param builtinPath Path to the vendored rg binary.
|
||||
* @param systemRgPath When omitted, calls `findExecutable('rg')` (production path).
|
||||
* Pass a string to force a specific system path, or `null` to
|
||||
* simulate "system rg not found".
|
||||
* @param platform Override for `process.platform` (tests only).
|
||||
*/
|
||||
export function resolveBuiltinWithFallback(
|
||||
builtinPath: string,
|
||||
systemRgPath?: string | null,
|
||||
platform?: string,
|
||||
): {
|
||||
mode: 'system' | 'builtin'
|
||||
command: string
|
||||
args: string[]
|
||||
note?: string
|
||||
} {
|
||||
const p = platform ?? process.platform
|
||||
|
||||
// Builtin exists — use it, no note.
|
||||
if (existsSync(builtinPath)) {
|
||||
return { mode: 'builtin', command: builtinPath, args: [] }
|
||||
}
|
||||
|
||||
// Builtin missing — check system rg.
|
||||
// When systemRgPath is explicitly passed (including null), use it directly.
|
||||
// When undefined, call findExecutable (production path).
|
||||
const resolvedSystem =
|
||||
systemRgPath === undefined
|
||||
? findExecutable('rg', []).cmd
|
||||
: (systemRgPath ?? 'rg')
|
||||
if (resolvedSystem !== 'rg') {
|
||||
return {
|
||||
mode: 'system',
|
||||
command: 'rg',
|
||||
args: [],
|
||||
note: `fallback: builtin rg unavailable on ${p}, using system rg`,
|
||||
}
|
||||
}
|
||||
|
||||
// Neither available.
|
||||
return {
|
||||
mode: 'builtin',
|
||||
command: builtinPath,
|
||||
args: [],
|
||||
note: `no ripgrep available on ${p}; install ripgrep via apt/pkg/brew`,
|
||||
}
|
||||
}
|
||||
|
||||
export function ripgrepCommand(): {
|
||||
rgPath: string
|
||||
rgArgs: string[]
|
||||
@@ -524,6 +578,7 @@ let ripgrepStatus: {
|
||||
working: boolean
|
||||
lastTested: number
|
||||
config: RipgrepConfig
|
||||
note?: string
|
||||
} | null = null
|
||||
|
||||
/**
|
||||
@@ -534,12 +589,14 @@ export function getRipgrepStatus(): {
|
||||
mode: 'system' | 'builtin' | 'embedded'
|
||||
path: string
|
||||
working: boolean | null // null if not yet tested
|
||||
note?: string
|
||||
} {
|
||||
const config = getRipgrepConfig()
|
||||
return {
|
||||
mode: config.mode,
|
||||
path: config.command,
|
||||
working: ripgrepStatus?.working ?? null,
|
||||
note: ripgrepStatus?.note ?? config.note,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,6 +650,7 @@ const testRipgrepOnFirstUse = memoize(async (): Promise<void> => {
|
||||
working,
|
||||
lastTested: Date.now(),
|
||||
config,
|
||||
note: config.note,
|
||||
}
|
||||
|
||||
logForDebugging(
|
||||
@@ -609,6 +667,7 @@ const testRipgrepOnFirstUse = memoize(async (): Promise<void> => {
|
||||
working: false,
|
||||
lastTested: Date.now(),
|
||||
config,
|
||||
note: config.note,
|
||||
}
|
||||
logError(error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user