mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 06:15:51 +00:00
Feat/integrate lint preview (#285)
* feat: 适配 zed acp 协议 * docs: 完善 acp 文档 * feat: integrate feature branches + daemon/job 命令层级化 + 跨平台后台引擎 Cherry-picked from origin/lint/preview (637c908), excluding lint-only changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct detectMimeFromBase64 to decode raw bytes from base64 Cherry-picked from origin/lint/preview (ee36954). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: daemon 子进程 spawn 跨平台修复 + CliLaunchSpec 集中化重构 Cherry-picked from origin/lint/preview (c5f52cd), excluding lint-only formatting changes. - 新建 src/utils/cliLaunch.ts: 集中化 CLI 子进程启动层 - 修复 --daemon-worker=kind 等号格式解析 - 修复 daemon/bg fast path 缺少 setShellIfWindows() - 修复 checkPathExists 用 existsSync 替代 execSync('dir') - 7 个 spawn 站点迁移到 CliLaunchSpec Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: merge tsconfig.base.json into tsconfig.json with full compiler options The cherry-pick from637c908dropped jsx/strict/etc settings when removing tsconfig.base.json. This commit restores them in a single tsconfig.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: merge tsconfig.base.json into tsconfig.json with full compiler options The cherry-pick from637c908dropped jsx/strict/etc settings when removing tsconfig.base.json. This commit restores them in a single tsconfig.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,11 @@ import {
|
||||
type PipeIpcSlaveState,
|
||||
} from '../utils/pipeTransport.js'
|
||||
import { logForDebugging } from '../utils/debug.js'
|
||||
import {
|
||||
isMasterPipeMuted,
|
||||
hasSendOverride,
|
||||
removeSendOverride,
|
||||
} from '../utils/pipeMuteState.js'
|
||||
|
||||
/** Session history entry for pipe IPC monitoring. */
|
||||
export type SessionEntry = {
|
||||
@@ -113,6 +118,28 @@ function isMonitoredPipeEntryType(type: string): boolean {
|
||||
return MONITORED_PIPE_ENTRY_TYPES.includes(type)
|
||||
}
|
||||
|
||||
/** Business message types that should be dropped when a slave is muted. */
|
||||
const MUTED_DROPPABLE_TYPES = new Set([
|
||||
'prompt_ack',
|
||||
'stream',
|
||||
'tool_start',
|
||||
'tool_result',
|
||||
'done',
|
||||
'error',
|
||||
'permission_request',
|
||||
'permission_cancel',
|
||||
])
|
||||
|
||||
/**
|
||||
* Centralized mute check used by both attachPipeEntryEmitter and
|
||||
* useMasterMonitor's inline handler — keeps the two gates in sync.
|
||||
*/
|
||||
export function shouldDropMutedMessage(slaveName: string, msgType: string): boolean {
|
||||
if (hasSendOverride(slaveName)) return false
|
||||
if (!isMasterPipeMuted(slaveName)) return false
|
||||
return MUTED_DROPPABLE_TYPES.has(msgType)
|
||||
}
|
||||
|
||||
function pipeMessageToSessionEntry(
|
||||
slaveName: string,
|
||||
msg: PipeMessage,
|
||||
@@ -153,6 +180,35 @@ function attachPipeEntryEmitter(name: string, client: PipeClient): void {
|
||||
if (typeof client.on !== 'function') return
|
||||
const handler = (msg: PipeMessage) => {
|
||||
if (!isMonitoredPipeEntryType(msg.type)) return
|
||||
|
||||
// Mute gate: drop business messages from muted slaves
|
||||
if (shouldDropMutedMessage(name, msg.type)) {
|
||||
// Auto-deny permission_request to prevent slave deadlock
|
||||
if (msg.type === 'permission_request') {
|
||||
try {
|
||||
const payload = JSON.parse(msg.data ?? '{}')
|
||||
if (payload.requestId) {
|
||||
client.send({
|
||||
type: 'permission_response',
|
||||
data: JSON.stringify({
|
||||
requestId: payload.requestId,
|
||||
behavior: 'deny',
|
||||
feedback: 'Permission auto-denied: pipe is logically disconnected.',
|
||||
}),
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
// Malformed payload — safe to ignore
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Clear /send override when slave turn completes
|
||||
if ((msg.type === 'done' || msg.type === 'error') && hasSendOverride(name)) {
|
||||
removeSendOverride(name)
|
||||
}
|
||||
|
||||
emitPipeEntry(name, pipeMessageToSessionEntry(name, msg))
|
||||
}
|
||||
_pipeEntryHandlers.set(name, handler)
|
||||
@@ -166,14 +222,14 @@ function emitSlaveClientRegistryChanged(): void {
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeToSlaveClientRegistry(listener: () => void): () => void {
|
||||
export function subscribeToSlaveClientRegistry(listener: () => void): () => void {
|
||||
_slaveClientRegistryListeners.add(listener)
|
||||
return () => {
|
||||
_slaveClientRegistryListeners.delete(listener)
|
||||
}
|
||||
}
|
||||
|
||||
function getSlaveClientRegistryVersion(): number {
|
||||
export function getSlaveClientRegistryVersion(): number {
|
||||
return _slaveClientRegistryVersion
|
||||
}
|
||||
|
||||
@@ -248,13 +304,23 @@ export function useMasterMonitor(): void {
|
||||
|
||||
for (const [slaveName, client] of _slaveClients.entries()) {
|
||||
const handler = (msg: PipeMessage) => {
|
||||
const entry = pipeMessageToSessionEntry(slaveName, msg)
|
||||
|
||||
// Only record relevant message types
|
||||
if (!isMonitoredPipeEntryType(msg.type)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Mute gate (second gate, same helper as attachPipeEntryEmitter)
|
||||
if (shouldDropMutedMessage(slaveName, msg.type)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Clear /send override when slave turn completes
|
||||
if ((msg.type === 'done' || msg.type === 'error') && hasSendOverride(slaveName)) {
|
||||
removeSendOverride(slaveName)
|
||||
}
|
||||
|
||||
const entry = pipeMessageToSessionEntry(slaveName, msg)
|
||||
|
||||
setAppState(prev => {
|
||||
const slave = getPipeIpc(prev).slaves[slaveName]
|
||||
if (!slave) return prev
|
||||
@@ -294,6 +360,8 @@ export function useMasterMonitor(): void {
|
||||
// Handle slave disconnect
|
||||
const onDisconnect = () => {
|
||||
logForDebugging(`[MasterMonitor] Slave "${slaveName}" disconnected`)
|
||||
// Clear any lingering /send override before removing client
|
||||
removeSendOverride(slaveName)
|
||||
removeSlaveClient(slaveName)
|
||||
setAppState(prev => {
|
||||
const { [slaveName]: _removed, ...remainingSlaves } =
|
||||
|
||||
Reference in New Issue
Block a user