refactor(tui): split kimi-tui God-class into controllers and command modules - #142
Merged
Conversation
将 /tasks 浏览器相关的 16 个方法(~450 行)从 KimiTUI 提取到独立的 TasksBrowserController。KimiTUI 通过 Host 接口传 this 给 controller, 保留 4 个一行委托方法供外部调用点使用。 kimi-tui.ts: 6160 → 5711 行 (-449)
将 Streaming Flush(15 个方法)和 Live Render Hooks(15 个方法) 共 ~475 行代码从 KimiTUI 提取到 StreamingUIController。这两个区域 之间紧密耦合(flush 方法直接调用 render hooks),合并提取避免循环依赖。 同时迁移了 5 个实例变量:flushTimer, lastFlushAt, pendingAssistantFlush, pendingThinkingFlush, pendingToolCallFlushIds。 kimi-tui.ts: 5711 → 5268 行 (-443)
将 Session Events(39 个方法)和 Background Task Lifecycle(4 个方法) 共 ~960 行代码从 KimiTUI 提取到 SessionEventHandler。包含事件分发、 turn/step 生命周期、thinking/assistant delta 处理、tool call 管理、 compaction 流程、subagent 管理、MCP 状态渲染、background task 事件。 kimi-tui.ts: 5268 → 4316 行 (-952)
将 Session Replay 的 21 个方法(~410 行)从 KimiTUI 提取到 SessionReplayRenderer。包含会话历史回放水合(snapshot hydration)、 replay record 渲染、thinking/assistant/tool call 回放、skill 激活、 权限变更、审批结果、background task 通知等逻辑。 kimi-tui.ts: 4316 → 3911 行 (-405)
将 Slash Command Handlers 区域(~765 行)从 KimiTUI 提取到两个文件: - slash-commands.ts (684 行):命令逻辑(plan/yolo/compact/editor/theme/ model/title/fork/init/login/connect/logout/feedback) - slash-command-prompts.ts (183 行):UI prompt 函数(platform selection、 logout provider、feedback input、API key、model selector 等) prompt 函数拆为独立模块避免 ESM 同模块内部调用 mock 不生效的问题。 测试改为 vi.mock 对应的 prompt 模块。 kimi-tui.ts: 3911 → 3163 行 (-748)
将 Auth / Model Bootstrap 的 6 个方法(~115 行)从 KimiTUI 提取到 AuthFlowController。包含 refreshAvailableModels、 enterLoginRequiredStartupState、activateModelAfterLogin、 clearActiveSessionAfterLogout、refreshConfigAfterLogin、 refreshConfigAfterLogout。 kimi-tui.ts: 3163 → 3063 行 (-100)
移除 kimi-tui.ts 和 controller 文件中因代码提取而残留的 55+ 个 无用 import(SDK event types、OAuth 工具函数、catalog 类型等)。 kimi-tui.ts: 3063 → 3005 行 (-58)
Phase A: 删除 14 个零调用者的纯委托方法
Phase B: 内联 8 个少量调用者委托(调用处直接引用 controller)
Phase C: controller 之间通过 host 上的 controller 引用直接协作,
不再绕 kimi-tui 中转(SessionEventHandler → tasksBrowserController,
SlashCommands → authFlow)
kimi-tui.ts: 3005 → 2956 行 (-49)
…mands controller Phase D: 将 pickers(editor/model/theme/permission/settings)、 config apply(applyEditorChoice/applyThemeChoice/applyPermissionChoice/ performModelSwitch/persistModelSelection)、info commands (showUsage/showStatusReport/showMcpServers + load* helpers)共 ~400 行 从 kimi-tui.ts 搬到 slash-commands controller。 Phase E: handleBuiltInSlashCommand 中所有 case 直接调用 slashCommands.*, 删除全部 13 个 slash command 委托方法。测试改为直接调用 controller 函数。 kimi-tui.ts: 2956 → 2566 行 (-390)
删除 finalizeTurn 和 activateModelAfterLogin 委托方法, slash-commands 直接通过 host.streamingUI / host.authFlow 调用。 清理 15 个因搬运产生的 dead import。 kimi-tui.ts: 2566 → 2545 行 (-21)
清理第二轮搬运后残留的 19 个 dead import 和 3 个空 import 块 (api-key-input-dialog、feedback-input-dialog、settings-selector、 feedback constants 等已搬到 slash-commands controller)。 kimi-tui.ts: 2545 → 2517 行 (-28)
将 refreshAvailableModels 和 enterLoginRequiredStartupState 的 调用方改为直接访问 this.authFlow.*,删除最后两个委托方法。 kimi-tui.ts: 2517 → 2510 行 (-7)
将 executeSlashCommand(~45 行)和 handleBuiltInSlashCommand(~80 行) 从 kimi-tui.ts 搬到 slash-commands controller。kimi-tui.ts 的 handleUserInput 现在只做空检查、replay guard、历史持久化, 然后调 slashCommands.dispatchInput(this, text)。 kimi-tui.ts: 2510 → 2367 行 (-143)
移除因前几轮搬运残留的 import(message-replay、mcp-server-status、 background-*-status、hook-result-format、event-payload utils、 streaming/theme constants 等)和 SessionUsageResult / RuntimeStatusResult / ManagedUsageResult 接口定义。 kimi-tui.ts: 2367 → 2311 行 (-56)
…redError 将 combineStartupNotice(kimi-tui.ts + auth-flow.ts 各一份)和 isOAuthLoginRequiredError 统一到 constant/kimi-tui.ts,两边改为 import。 kimi-tui.ts: 2311 → 2296 行 (-15)
combineStartupNotice 和 isOAuthLoginRequiredError 是工具函数不是常量, 从 constant/kimi-tui.ts 移到 utils/startup.ts。
删除 handleEvent、startSessionEventSubscription、 hydrateTranscriptFromReplay 三个委托方法。 kimi-tui 内部和 auth-flow controller 改为直接访问 sessionEventHandler.startSubscription() 和 sessionReplay.hydrateFromReplay()。 测试改为通过 driver.sessionEventHandler 直接调用。 kimi-tui.ts: 2296 → 2278 行 (-18)
…ing pairs Delete 4 redundant fields (AppState.yolo, AppState.isStreaming, TUIState.backgroundAgents, TUIState.assistantStreamActive) that were derivable from existing state. Merge 4 pairs of always-coupled fields into single objects (streamingBlock, subagentInfo, activitySpinner, activeDialog). Demote 3 fields only used by KimiTUI itself to private class fields. Net reduction: AppState 22→20, TUIState 55→48 fields.
…treamingUIController Break the type-level circular dependency where controllers imported TUIState from kimi-tui.ts while kimi-tui.ts imported runtime values from controllers. Shared types now live in tui-state.ts (TUIState, createTUIState) and types.ts (KimiTUIOptions, LoginProgressSpinnerHandle, etc.) so the import graph is strictly unidirectional. Move 12 streaming-related fields (currentTurnId, currentStep, assistantDraft, thinkingDraft, streamingBlock, activeThinkingComponent, activeCompactionBlock, activeToolCalls, streamingToolCallArguments, pendingToolComponents, pendingAgentGroup, pendingReadGroup) from the flat TUIState bag into StreamingUIController as instance properties, along with 3 dispose methods. TUIState shrinks from 40+ to 28 fields.
Extract the 1223-line slash-commands.ts into domain-specific files: - auth-commands.ts (~350 lines): login, logout, connect, OAuth flows - config-commands.ts (~380 lines): plan, yolo, compact, model/theme/ editor/permission pickers and apply logic - info-commands.ts (~185 lines): feedback, usage, status, mcp reports - session-commands.ts (~105 lines): title, fork, init slash-commands.ts is now a slim routing hub (~210 lines) that owns SlashCommandHost, dispatchInput, and the builtin command switch. All public handlers are re-exported so existing consumers are unaffected.
Relocate the 6 command-related files into the existing commands/ directory where parsing and routing logic already lives: controllers/slash-commands.ts → commands/dispatch.ts controllers/slash-command-prompts.ts → commands/prompts.ts controllers/auth-commands.ts → commands/auth.ts controllers/config-commands.ts → commands/config.ts controllers/info-commands.ts → commands/info.ts controllers/session-commands.ts → commands/session.ts controllers/ now contains only state/lifecycle controllers (auth-flow, session-event-handler, session-replay, streaming-ui, tasks-browser). commands/index.ts re-exports all public symbols.
…ndler Move 7 fields from TUIState into SessionEventHandler as instance properties: backgroundAgentMetadata, backgroundTasks, backgroundTaskTranscriptedTerminal, subagentInfo, renderedSkillActivationIds, renderedMcpServerStatusKeys, mcpServerStatusSpinners. Add resetRuntimeState() to SessionEventHandler that clears all 7 fields in one call. TUIState shrinks from 28 to 21 fields. TasksBrowserHost.backgroundTasks is now a top-level ReadonlyMap property instead of being embedded in the state subset, with a getter on KimiTUI that delegates to sessionEventHandler.
…d semantic methods Make 12 fields private and expose 18 semantic methods instead of letting SessionEventHandler, SessionReplayRenderer, and KimiTUI directly manipulate internal maps and counters. Key methods: registerToolCall(), accumulateToolCallDelta(), completeToolResult(), markStepTruncated(), appendThinkingDelta(), appendAssistantDelta(), cleanupAfterReplay().
Move editor callback wiring, pending-exit state, clipboard media handling, and external editor logic into a dedicated controller. KimiTUI drops from 2070 to 1814 lines.
…e sections Remove ~70 single-line comments that merely restated the method name. Condense multi-paragraph inline comments (signal handlers, start()) to one-liners that capture the WHY. Reorganize sections: merge the one- method "Layout" section into Lifecycle, rename "Startup Helpers" to "Autocomplete & Skill Commands", move stray accessors into "State & Accessors", delete the empty trailing section, relocate input-history helpers next to each other. 1814 → 1639 lines.
Controllers may still read host.state freely, but all direct field assignments now go through setter methods on the host: setStartupReady, clearQueuedMessages, shiftQueuedMessage, pushTranscriptEntry, setExternalEditorRunning, setTasksBrowser. This prevents controllers from silently mutating shared state without KimiTUI's knowledge.
Resolved kimi-tui.ts conflict by keeping refactored structure and porting main's new features into their proper modules: - /export-md, /export-debug-zip → commands/session.ts + dispatch.ts - thinking spinner leak fix → controllers/streaming-ui.ts - debug timing → controllers/session-event-handler.ts - fork return hint → commands/session.ts - paste marker expansion → auto-merged (custom-editor.ts) - permission policies rewrite → auto-merged (agent-core)
合并 main 上自 2c7a8cc 之后的 4 个 commit: - ebf6e81 feat: add plugin manager and official plugins (#119) - 50251a1 fix(approval): show file content/diff and full-screen preview (#139) - 16e881e docs(changelog): sync 0.4.0 (#125) - fa114c1 ci: release packages (#93) 主要适配工作: - /plugins 的 10 个 UI 方法从 main 的 kimi-tui.ts 抽出,新建 apps/kimi-code/src/tui/commands/plugins.ts(参照 commands/info.ts 模式), 分发接到 commands/dispatch.ts 的 'plugins' case - approval-preview 改动(activeApprovalPanel/approvalPreview 字段、 openApprovalPreview/closeApprovalPreview 方法、ApprovalPreviewViewer import、showApprovalPanel 第 5 个 onPreview 回调参数)补到 kimi-tui.ts,保持原 show/hide 调用点 - 删除冲突中带来的 1152 行已迁出 controller 的 God-class 方法 - 删除孤立的 resolvePluginInstallSource helper(已搬到 commands/plugins.ts) 详细分析见 docs/refactor-kimi-tui-merge-plan.md。
- delete docs/refactor-kimi-tui-{analysis,merge-plan}.md (working notes)
- add changeset for the kimi-tui split refactor
- session-event-handler.ts: drop unused `state` destructure in finishCompaction
- editor-keyboard.ts: void-mark fire-and-forget session.cancel() promise
🦋 Changeset detectedLatest commit: 39e6289 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
No linked issue — this is an internal refactor of the TUI host class.
Problem
apps/kimi-code/src/tui/kimi-tui.tshad grown to ~6700 lines with all session, streaming, replay, editor, task-browser, auth, and slash-command logic inlined. Adding a new feature meant scrolling and grepping a single file; testing required spinning up the full host; reviewers had to context-switch between concerns inside one class.What changed
Split
kimi-tui.tsinto a thin host (~1700 lines) plus focused units, with no user-visible behavior changes.Controllers (
apps/kimi-code/src/tui/controllers/) — each owns one slice of state and the host's host-level methods become the only mutation surface:auth-flow.ts—/login,/connect,/logout, provider/model selectorseditor-keyboard.ts— keybindings, exit-confirm, stream/compaction cancelsession-event-handler.ts— turn lifecycle, tool calls, thinking, compaction, subagentssession-replay.ts— historical message rendering on resumestreaming-ui.ts— live assistant/tool-call streaming UItasks-browser.ts—/tasksfull-screen overlaySlash-command modules (
apps/kimi-code/src/tui/commands/):dispatch.ts— single entry point, definesSlashCommandHostinterfaceauth.ts/config.ts/info.ts/plugins.ts/prompts.ts/session.ts— per-domain handlers, all accept the host interface rather than the full TUIState:
TUIStatefields are now mutated only through host methods (setAppState,pushTranscriptEntry,setTasksBrowser, etc.) so controllers can't silently desync shared state.Imports: dropped ~40 dead imports and 3 dead interfaces no longer needed at the host level.
Also merged the 4 main commits that landed during the refactor (plugin manager, approval-preview viewer, 0.4.0 changelog, release version bump) —
/pluginsUI lives in the newcommands/plugins.tsrather than back in the host.Checklist