Repository navigation
Replies: 1 comment
|
Rethinking this since most things have changed. closing the discussion |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
This is a common question/curiosity about a rewrite of TUI in a different language, so creating a discussion here.
Tl;Dr: It is possible but unlikely that it will have the benefits one would expect. The reason is, the agent has to support both IDE and CLI. The IDE code (including the task loop, the tooling, the api connections etc) is written in TypeScript and we can not completely move away from TS anyways as VSCode/IDEs force this
There are two ways to go about it: modify the existing client or go the ACP route (arguably much better). None of them are likely worth it. The bottleneck is the typescript runtime anyways since so putting in extraordinary amount of create/maintain effort has little benefit
Below are LLM generated architecture diagrams
CLI ↔ Backend Protocol Analysis
Architecture Overview
Dirac has two runtime modes, both running the full TypeScript backend in a single Node.js process:
dirac task "prompt"/diracdirac acpThe critical insight: the CLI does not talk to a backend — the CLI is the backend. The
Controller,Task,StateManager, and all tool implementations are imported and instantiated directly within the CLI process.Mode 1: Interactive Ink Mode
Everything runs in a single Node.js process. The CLI directly imports and calls backend modules.
Key Data Flow
ChatInputBar→useChatInputHandlerhookcontroller.initTask(prompt, images, files)orcontroller.task.submitCardResponse(...)Controllercreates aTaskinstance, which starts the LLM conversation loopTaskproducesDiracMessageobjects viaMessageStateHandlerMessageStateHandleremits"diracMessagesChanged"eventsKey Interfaces (Ink Mode)
Controller.initTask()src/core/controller/index.tscontroller.task.submitCardResponse()src/core/task/index.tscontroller.task.messageStateHandlersrc/core/task/message-state.tsDiracMessageChangecontroller.cancelTask()src/core/controller/index.tsHostProvider.initialize()src/hosts/host-provider.tsMode 2: ACP Mode
Still a single Node.js process, but with a structured protocol boundary on the stdin/stdout side for external clients.
ACP Methods
initializeDiracAgent.initialize()newSessionDiracAgent.newSession()loadSessionDiracAgent.loadSession()promptDiracAgent.prompt()cancelDiracAgent.cancel()setSessionModeDiracAgent.setSessionMode()setSessionModelDiracAgent.unstable_setSessionModel()setSessionConfigOptionDiracAgent.unstable_setSessionConfigOption()authenticateDiracAgent.authenticate()ACP Session Updates (server→client push)
agent_message_chunkagent_thought_chunktool_calltool_call_updateuser_message_chunkplancurrent_mode_updateconfig_option_updatesession_info_updateavailable_commands_updateTranslation Layer
TaskMessageBridge(cli/src/agent/taskMessageBridge.ts) +translateMessage(cli/src/agent/messageTranslator.ts):DiracMessageChangeevents fromMessageStateHandlerDiracMessageType.MARKDOWN→agent_message_chunk/agent_thought_chunkDiracMessageType.CARD→tool_call/tool_call_updateDiracMessageType.API_STATUS→ skipped (not forwarded)WAITING_FOR_INPUT, bridges to ACP'srequestPermissionflowCoupling Surface
The CLI imports from
src/(the "backend") extensively. This is what makes a clean separation difficult:Direct Backend Imports
Controller— the central orchestrator (imported byDiracAgent,taskMessageBridge,useChatTask,useChatInputHandler, settings hooks, etc.)StateManager— persistent storage (imported bysessionConfig,sessionHistory,useChatInputHandler, auth, provider-config, settings)HostProvider— host abstraction (imported byDiracAgent,ACPDiffViewProvider)AuthHandler,Session,Logger,telemetryService— shared servicesbuildApiHandler— API provider construction (imported byreview.ts,provider-config.ts)refreshOpenRouterModels,refreshGithubCopilotModels,refreshVercelAiGatewayModels— model managementshowTaskWithId,subscribeToState,getRequestRegistry— controller internalsFileEditProvider,StandaloneTerminalManager— integration abstractionsDiracWebviewProvider,CommentReviewController— UI abstractions@shared/proto/— protobuf message definitionsCLI-Provided Implementations
The CLI provides implementations of backend interfaces via
HostProvider.initialize():DiracWebviewProviderCliWebviewProvider/ExternalDiracWebviewProvidercli/src/controllers/DiffViewProviderFileEditProvider/ACPDiffViewProvidercli/src/acp/CommentReviewControllerCliCommentReviewController/ExternalCommentReviewControllercli/src/controllers/ITerminalManagerStandaloneTerminalManager/AcpTerminalManagercli/src/acp/HostBridgeClientProvidercreateCliHostBridgeProvider()/ACPHostBridgeClientProvidercli/src/controllers/,cli/src/acp/VSCode Compatibility Layer
cli/src/vscode-shim.ts(350 lines) — Stubs for VSCode APIs:EnvironmentVariableCollection,ExtensionMode,ExtensionKind,window,commands,envcli/src/vscode-context.ts(174 lines) — MockDiracExtensionContextwithMementoAdapter,StorageContext, CLI state overridesThese exist because the
src/backend was originally built for the VSCode extension and imports VSCode types directly.Hypothetical Native Client
A Rust/Go CLI client communicating over ACP:
What It Would Gain
What It Still Needs
What Would Need to Change in the TypeScript Codebase
searchFiles/listFiles(file mention autocomplete)getDiagnostics(lint display)bun compileor Node SEA)Benefits vs Costs
Benefits
Costs
Summary of Data Flows
All reactions