Fix/openclaude diagnostics settings (#483)
* fix: use openclaude paths in diagnostics and settings * fix: strip leaked reasoning from assistant output * fix: preserve legacy claude config compatibility * fix: tighten path and reasoning compatibility * fix: buffer streamed reasoning leak preambles * test: cover openclaude migration and reasoning fixes * test: isolate execFileNoThrow from cross-file mocks
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { access, chmod, writeFile } from 'fs/promises'
|
||||
import { homedir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { type ReleaseChannel, saveGlobalConfig } from './config.js'
|
||||
import { getClaudeConfigHomeDir } from './envUtils.js'
|
||||
@@ -19,16 +20,45 @@ import { jsonStringify } from './slowOperations.js'
|
||||
function getLocalInstallDir(): string {
|
||||
return join(getClaudeConfigHomeDir(), 'local')
|
||||
}
|
||||
|
||||
function getLegacyLocalInstallDir(homeDir = homedir()): string {
|
||||
return join(homeDir, '.claude', 'local')
|
||||
}
|
||||
|
||||
export function getCandidateLocalInstallDirs(options?: {
|
||||
configHomeDir?: string
|
||||
homeDir?: string
|
||||
}): string[] {
|
||||
const homeDir = options?.homeDir ?? homedir()
|
||||
const configHomeDir = options?.configHomeDir ?? getClaudeConfigHomeDir()
|
||||
return Array.from(
|
||||
new Set([join(configHomeDir, 'local'), getLegacyLocalInstallDir(homeDir)]),
|
||||
)
|
||||
}
|
||||
|
||||
function getCandidateLocalBinaryPaths(localInstallDir: string): string[] {
|
||||
return [
|
||||
join(localInstallDir, 'node_modules', '.bin', 'openclaude'),
|
||||
join(localInstallDir, 'node_modules', '.bin', 'claude'),
|
||||
]
|
||||
}
|
||||
|
||||
export function isManagedLocalInstallationPath(execPath: string): boolean {
|
||||
return (
|
||||
execPath.includes('/.openclaude/local/node_modules/') ||
|
||||
execPath.includes('/.claude/local/node_modules/')
|
||||
)
|
||||
}
|
||||
|
||||
export function getLocalClaudePath(): string {
|
||||
return join(getLocalInstallDir(), 'claude')
|
||||
return join(getLocalInstallDir(), 'openclaude')
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're running from our managed local installation
|
||||
*/
|
||||
export function isRunningFromLocalInstallation(): boolean {
|
||||
const execPath = process.argv[1] || ''
|
||||
return execPath.includes('/.claude/local/node_modules/')
|
||||
return isManagedLocalInstallationPath(process.argv[1] || '')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,17 +94,17 @@ export async function ensureLocalPackageEnvironment(): Promise<boolean> {
|
||||
await writeIfMissing(
|
||||
join(localInstallDir, 'package.json'),
|
||||
jsonStringify(
|
||||
{ name: 'claude-local', version: '0.0.1', private: true },
|
||||
{ name: 'openclaude-local', version: '0.0.1', private: true },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
)
|
||||
|
||||
// Create the wrapper script if it doesn't exist
|
||||
const wrapperPath = join(localInstallDir, 'claude')
|
||||
const wrapperPath = getLocalClaudePath()
|
||||
const created = await writeIfMissing(
|
||||
wrapperPath,
|
||||
`#!/bin/sh\nexec "${localInstallDir}/node_modules/.bin/claude" "$@"`,
|
||||
`#!/bin/sh\nexec "${localInstallDir}/node_modules/.bin/openclaude" "$@"`,
|
||||
0o755,
|
||||
)
|
||||
if (created) {
|
||||
@@ -142,12 +172,31 @@ export async function installOrUpdateClaudePackage(
|
||||
* Pure existence probe — callers use this to choose update path / UI hints.
|
||||
*/
|
||||
export async function localInstallationExists(): Promise<boolean> {
|
||||
try {
|
||||
await access(join(getLocalInstallDir(), 'node_modules', '.bin', 'claude'))
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
for (const localInstallDir of getCandidateLocalInstallDirs()) {
|
||||
for (const binaryPath of getCandidateLocalBinaryPaths(localInstallDir)) {
|
||||
try {
|
||||
await access(binaryPath)
|
||||
return true
|
||||
} catch {
|
||||
// Try next candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export async function getDetectedLocalInstallDir(): Promise<string | null> {
|
||||
for (const localInstallDir of getCandidateLocalInstallDirs()) {
|
||||
for (const binaryPath of getCandidateLocalBinaryPaths(localInstallDir)) {
|
||||
try {
|
||||
await access(binaryPath)
|
||||
return localInstallDir
|
||||
} catch {
|
||||
// Try next candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user