mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 05:45:51 +00:00
更新大量 tsx 原始文件; 已经迁移 login panel; 部分 (#121)
* style(B1-1): 格式化 ink/buddy/cli/context/screens/tasks/services/keybindings/state (43 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 修复了 Box.tsx 和 ScrollBox.tsx 中无效的 global.d.ts import。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-2): 格式化 commands (79 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-3): 格式化 components/messages,permissions,mcp,sandbox,shell (104 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-4): 格式化 components/PromptInput,FeedbackSurvey,tasks,agents,skills,design-system,wizard (73 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-5): 格式化 components其余 + hooks + tools (232 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-6): 格式化 main/entrypoints/utils/moreright (21 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: 更新 README,新增 Run.ps1/TODO.md,删除 V6.md - README.md: 大幅重写,更详细版本历史和配置示例 - Run.ps1: 新增 Windows 启动脚本 - TODO.md: 新增包完成清单 - V6.md: 删除(架构重构规划已不适用) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: 修复以前的问题 * fix: 修复 login 面板的问题 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,69 +1,97 @@
|
||||
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources';
|
||||
import { randomUUID } from 'crypto';
|
||||
import * as React from 'react';
|
||||
import { BashModeProgress } from 'src/components/BashModeProgress.js';
|
||||
import type { SetToolJSXFn } from 'src/Tool.js';
|
||||
import { BashTool } from 'src/tools/BashTool/BashTool.js';
|
||||
import type { AttachmentMessage, SystemMessage, UserMessage } from 'src/types/message.js';
|
||||
import type { ShellProgress } from 'src/types/tools.js';
|
||||
import { logEvent } from '../../services/analytics/index.js';
|
||||
import { errorMessage, ShellError } from '../errors.js';
|
||||
import { createSyntheticUserCaveatMessage, createUserInterruptionMessage, createUserMessage, prepareUserContent } from '../messages.js';
|
||||
import { resolveDefaultShell } from '../shell/resolveDefaultShell.js';
|
||||
import { isPowerShellToolEnabled } from '../shell/shellToolUtils.js';
|
||||
import { processToolResultBlock } from '../toolResultStorage.js';
|
||||
import { escapeXml } from '../xml.js';
|
||||
import type { ProcessUserInputContext } from './processUserInput.js';
|
||||
export async function processBashCommand(inputString: string, precedingInputBlocks: ContentBlockParam[], attachmentMessages: AttachmentMessage[], context: ProcessUserInputContext, setToolJSX: SetToolJSXFn): Promise<{
|
||||
messages: (UserMessage | AttachmentMessage | SystemMessage)[];
|
||||
shouldQuery: boolean;
|
||||
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources'
|
||||
import { randomUUID } from 'crypto'
|
||||
import * as React from 'react'
|
||||
import { BashModeProgress } from 'src/components/BashModeProgress.js'
|
||||
import type { SetToolJSXFn } from 'src/Tool.js'
|
||||
import { BashTool } from 'src/tools/BashTool/BashTool.js'
|
||||
import type {
|
||||
AttachmentMessage,
|
||||
SystemMessage,
|
||||
UserMessage,
|
||||
} from 'src/types/message.js'
|
||||
import type { ShellProgress } from 'src/types/tools.js'
|
||||
import { logEvent } from '../../services/analytics/index.js'
|
||||
import { errorMessage, ShellError } from '../errors.js'
|
||||
import {
|
||||
createSyntheticUserCaveatMessage,
|
||||
createUserInterruptionMessage,
|
||||
createUserMessage,
|
||||
prepareUserContent,
|
||||
} from '../messages.js'
|
||||
import { resolveDefaultShell } from '../shell/resolveDefaultShell.js'
|
||||
import { isPowerShellToolEnabled } from '../shell/shellToolUtils.js'
|
||||
import { processToolResultBlock } from '../toolResultStorage.js'
|
||||
import { escapeXml } from '../xml.js'
|
||||
import type { ProcessUserInputContext } from './processUserInput.js'
|
||||
|
||||
export async function processBashCommand(
|
||||
inputString: string,
|
||||
precedingInputBlocks: ContentBlockParam[],
|
||||
attachmentMessages: AttachmentMessage[],
|
||||
context: ProcessUserInputContext,
|
||||
setToolJSX: SetToolJSXFn,
|
||||
): Promise<{
|
||||
messages: (UserMessage | AttachmentMessage | SystemMessage)[]
|
||||
shouldQuery: boolean
|
||||
}> {
|
||||
// Shell routing (docs/design/ps-shell-selection.md §5.2): consult
|
||||
// defaultShell, fall back to bash. isPowerShellToolEnabled() applies the
|
||||
// same platform + env-var gate as tools.ts so input-box routing matches
|
||||
// tool-list visibility. Computed up front so telemetry records the
|
||||
// actual shell, not the raw setting.
|
||||
const usePowerShell = isPowerShellToolEnabled() && resolveDefaultShell() === 'powershell';
|
||||
logEvent('tengu_input_bash', {
|
||||
powershell: usePowerShell
|
||||
});
|
||||
const usePowerShell =
|
||||
isPowerShellToolEnabled() && resolveDefaultShell() === 'powershell'
|
||||
|
||||
logEvent('tengu_input_bash', { powershell: usePowerShell })
|
||||
|
||||
const userMessage = createUserMessage({
|
||||
content: prepareUserContent({
|
||||
inputString: `<bash-input>${inputString}</bash-input>`,
|
||||
precedingInputBlocks
|
||||
})
|
||||
});
|
||||
precedingInputBlocks,
|
||||
}),
|
||||
})
|
||||
|
||||
// ctrl+b to background indicator
|
||||
let jsx: React.ReactNode;
|
||||
let jsx: React.ReactNode
|
||||
|
||||
// Just show initial UI
|
||||
setToolJSX({
|
||||
jsx: <BashModeProgress input={inputString} progress={null} verbose={context.options.verbose} />,
|
||||
shouldHidePromptInput: false
|
||||
});
|
||||
jsx: (
|
||||
<BashModeProgress
|
||||
input={inputString}
|
||||
progress={null}
|
||||
verbose={context.options.verbose}
|
||||
/>
|
||||
),
|
||||
shouldHidePromptInput: false,
|
||||
})
|
||||
|
||||
try {
|
||||
const bashModeContext: ProcessUserInputContext = {
|
||||
...context,
|
||||
// TODO: Clean up this hack
|
||||
setToolJSX: _ => {
|
||||
jsx = _?.jsx;
|
||||
}
|
||||
};
|
||||
jsx = _?.jsx
|
||||
},
|
||||
}
|
||||
|
||||
// Progress UI — shared across both shell backends (both emit ShellProgress)
|
||||
const onProgress = (progress: {
|
||||
data: ShellProgress;
|
||||
}) => {
|
||||
const onProgress = (progress: { data: ShellProgress }) => {
|
||||
setToolJSX({
|
||||
jsx: <>
|
||||
<BashModeProgress input={inputString!} progress={progress.data} verbose={context.options.verbose} />
|
||||
jsx: (
|
||||
<>
|
||||
<BashModeProgress
|
||||
input={inputString!}
|
||||
progress={progress.data}
|
||||
verbose={context.options.verbose}
|
||||
/>
|
||||
{jsx}
|
||||
</>,
|
||||
</>
|
||||
),
|
||||
shouldHidePromptInput: false,
|
||||
showSpinner: false
|
||||
});
|
||||
};
|
||||
showSpinner: false,
|
||||
})
|
||||
}
|
||||
|
||||
// User-initiated `!` commands run outside sandbox. Both shell tools honor
|
||||
// dangerouslyDisableSandbox (checked against areUnsandboxedCommandsAllowed()
|
||||
@@ -71,69 +99,107 @@ export async function processBashCommand(inputString: string, precedingInputBloc
|
||||
// native, shouldUseSandbox() returns false regardless (unsupported platform).
|
||||
// Lazy-require PowerShellTool so its ~300KB chunk only loads when the
|
||||
// user has actually selected the powershell default shell.
|
||||
type PSMod = typeof import('src/tools/PowerShellTool/PowerShellTool.js');
|
||||
let PowerShellTool: PSMod['PowerShellTool'] | null = null;
|
||||
type PSMod = typeof import('src/tools/PowerShellTool/PowerShellTool.js')
|
||||
let PowerShellTool: PSMod['PowerShellTool'] | null = null
|
||||
if (usePowerShell) {
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
PowerShellTool = (require('src/tools/PowerShellTool/PowerShellTool.js') as PSMod).PowerShellTool;
|
||||
PowerShellTool = (
|
||||
require('src/tools/PowerShellTool/PowerShellTool.js') as PSMod
|
||||
).PowerShellTool
|
||||
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||
}
|
||||
const shellTool = PowerShellTool ?? BashTool;
|
||||
const response = PowerShellTool ? await PowerShellTool.call({
|
||||
command: inputString,
|
||||
dangerouslyDisableSandbox: true
|
||||
}, bashModeContext, undefined, undefined, onProgress) : await BashTool.call({
|
||||
command: inputString,
|
||||
dangerouslyDisableSandbox: true
|
||||
}, bashModeContext, undefined, undefined, onProgress);
|
||||
const data = response.data;
|
||||
const shellTool = PowerShellTool ?? BashTool
|
||||
|
||||
const response = PowerShellTool
|
||||
? await PowerShellTool.call(
|
||||
{ command: inputString, dangerouslyDisableSandbox: true },
|
||||
bashModeContext,
|
||||
undefined,
|
||||
undefined,
|
||||
onProgress,
|
||||
)
|
||||
: await BashTool.call(
|
||||
{
|
||||
command: inputString,
|
||||
dangerouslyDisableSandbox: true,
|
||||
},
|
||||
bashModeContext,
|
||||
undefined,
|
||||
undefined,
|
||||
onProgress,
|
||||
)
|
||||
const data = response.data
|
||||
|
||||
if (!data) {
|
||||
throw new Error('No result received from shell command');
|
||||
throw new Error('No result received from shell command')
|
||||
}
|
||||
const stderr = data.stderr;
|
||||
|
||||
const stderr = data.stderr
|
||||
// Reuse the same formatting pipeline as inline !`cmd` bash (promptShellExecution)
|
||||
// and model-initiated Bash. When BashTool.call() persists large output to disk,
|
||||
// data.persistedOutputPath is set and the formatter wraps in <persisted-output>.
|
||||
// Pass stderr:'' to keep it separate for the <bash-stderr> UI tag.
|
||||
const mapped = await processToolResultBlock(shellTool, {
|
||||
...data,
|
||||
stderr: ''
|
||||
}, randomUUID());
|
||||
const mapped = await processToolResultBlock(
|
||||
shellTool,
|
||||
{ ...data, stderr: '' },
|
||||
randomUUID(),
|
||||
)
|
||||
// mapped.content may contain our own <persisted-output> wrapper (trusted
|
||||
// XML from buildLargeToolResultMessage). Escaping it would turn structural
|
||||
// tags into <persisted-output>, breaking the model's parse and
|
||||
// UserBashOutputMessage's extractTag. Escape the raw fallback only.
|
||||
const stdout = typeof mapped.content === 'string' ? mapped.content : escapeXml(data.stdout);
|
||||
const stdout =
|
||||
typeof mapped.content === 'string'
|
||||
? mapped.content
|
||||
: escapeXml(data.stdout)
|
||||
return {
|
||||
messages: [createSyntheticUserCaveatMessage(), userMessage, ...attachmentMessages, createUserMessage({
|
||||
content: `<bash-stdout>${stdout}</bash-stdout><bash-stderr>${escapeXml(stderr)}</bash-stderr>`
|
||||
})],
|
||||
shouldQuery: false
|
||||
};
|
||||
messages: [
|
||||
createSyntheticUserCaveatMessage(),
|
||||
userMessage,
|
||||
...attachmentMessages,
|
||||
createUserMessage({
|
||||
content: `<bash-stdout>${stdout}</bash-stdout><bash-stderr>${escapeXml(stderr)}</bash-stderr>`,
|
||||
}),
|
||||
],
|
||||
shouldQuery: false,
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof ShellError) {
|
||||
if (e.interrupted) {
|
||||
return {
|
||||
messages: [createSyntheticUserCaveatMessage(), userMessage, createUserInterruptionMessage({
|
||||
toolUse: false
|
||||
}), ...attachmentMessages],
|
||||
shouldQuery: false
|
||||
};
|
||||
messages: [
|
||||
createSyntheticUserCaveatMessage(),
|
||||
userMessage,
|
||||
createUserInterruptionMessage({ toolUse: false }),
|
||||
...attachmentMessages,
|
||||
],
|
||||
shouldQuery: false,
|
||||
}
|
||||
}
|
||||
return {
|
||||
messages: [createSyntheticUserCaveatMessage(), userMessage, ...attachmentMessages, createUserMessage({
|
||||
content: `<bash-stdout>${escapeXml(e.stdout)}</bash-stdout><bash-stderr>${escapeXml(e.stderr)}</bash-stderr>`
|
||||
})],
|
||||
shouldQuery: false
|
||||
};
|
||||
messages: [
|
||||
createSyntheticUserCaveatMessage(),
|
||||
userMessage,
|
||||
...attachmentMessages,
|
||||
createUserMessage({
|
||||
content: `<bash-stdout>${escapeXml(e.stdout)}</bash-stdout><bash-stderr>${escapeXml(e.stderr)}</bash-stderr>`,
|
||||
}),
|
||||
],
|
||||
shouldQuery: false,
|
||||
}
|
||||
}
|
||||
return {
|
||||
messages: [createSyntheticUserCaveatMessage(), userMessage, ...attachmentMessages, createUserMessage({
|
||||
content: `<bash-stderr>Command failed: ${escapeXml(errorMessage(e))}</bash-stderr>`
|
||||
})],
|
||||
shouldQuery: false
|
||||
};
|
||||
messages: [
|
||||
createSyntheticUserCaveatMessage(),
|
||||
userMessage,
|
||||
...attachmentMessages,
|
||||
createUserMessage({
|
||||
content: `<bash-stderr>Command failed: ${escapeXml(errorMessage(e))}</bash-stderr>`,
|
||||
}),
|
||||
],
|
||||
shouldQuery: false,
|
||||
}
|
||||
} finally {
|
||||
setToolJSX(null);
|
||||
setToolJSX(null)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user