Skip to content

Architecture review: two shells, one engine — hotspots, drift risks, and refactor roadmap #5

Description

@realsigridjin

TL;DR

After reading the current repository snapshot on March 31, 2026, my main conclusion is:

The codebase has a solid shared execution core, but too many orchestration surfaces around it.

The simplest mental model is:

  • interactive shell: src/screens/REPL.tsx
  • headless shell: src/cli/print.ts
  • bridge supervisor: src/bridge/bridgeMain.ts
  • shared engine: src/QueryEngine.ts + src/query.ts

That foundation is strong. The main risk is drift between the shells and the bridge path.


Current structure

Layer Primary files
Bootstrap src/entrypoints/cli.tsx, src/entrypoints/init.ts, src/main.tsx, src/replLauncher.tsx
Interactive shell src/screens/REPL.tsx, src/components/App.tsx
Headless shell src/cli/print.ts, src/cli/structuredIO.ts
Shared engine src/QueryEngine.ts, src/query.ts
Bridge src/bridge/bridgeMain.ts, src/bridge/sessionRunner.ts, src/bridge/bridgeMessaging.ts, src/hooks/useReplBridge.tsx

Representative refs:

  • bootstrap/router: src/entrypoints/cli.tsx:28-33, 108-162, 182-208
  • main.tsx orchestration: src/main.tsx:585-760, 1903-1935, 2404-2453, 3761-3798
  • REPL shell: src/screens/REPL.tsx:680-835, 2392-2523, 2855-3055, 3831-4043
  • headless shell: src/cli/print.ts:455-620, 1471-1510, 2130-2205
  • shared query loop: src/query.ts:219-320, 551-740
  • bridge supervisor: src/bridge/bridgeMain.ts:141-220, 1980-2057

What looks good

1. Shared lower turn engine

The repo does not duplicate the core model/tool execution loop across interactive/headless/bridge. That is the biggest architectural strength.

2. Registry-first command/tool model

Commands and tools are explicitly assembled rather than scattered ad hoc.

Refs:

  • src/commands.ts:258-346, 449-517
  • src/tools.ts:193-250, 271-327, 345-389

3. Real extensibility model

Skills, plugins, workflows, MCP commands, and MCP tools are part of the actual runtime capability model.

Refs:

  • src/commands.ts:353-398, 449-517, 547-586
  • src/skills/loadSkillsDir.ts
  • src/utils/plugins/loadPluginCommands.ts
  • src/tools.ts:345-389

Main risks

1. REPL vs headless drift

The biggest structural risk is that there are two large orchestration shells:

  • src/screens/REPL.tsx
  • src/cli/print.ts

They share the lower engine, but a lot of setup/runtime assembly still happens separately.

Likely drift points:

  • tool availability
  • MCP timing
  • permission handling
  • startup behavior
  • bridge child-session behavior (which inherits headless semantics)

2. Oversized orchestration hotspots

The main files carrying too many boundaries right now are:

  • src/main.tsx
  • src/screens/REPL.tsx
  • src/cli/print.ts
  • src/bridge/bridgeMain.ts

These files mix routing, capability assembly, lifecycle management, task/queue/mailbox handling, and teardown.

3. “Task” means two different things

There appear to be two task domains:

  1. runtime/background execution tasks

    • src/Task.ts
    • src/tasks.ts
    • src/tasks/*
  2. file-backed todo/planning task state

    • src/utils/tasks.ts
    • src/hooks/useTasksV2.ts

Refs:

  • src/Task.ts:6-29, 44-125
  • src/tasks.ts:17-38
  • src/utils/tasks.ts:69-89, 190-210
  • src/hooks/useTasksV2.ts:20-29, 113-151, 218-249

4. Capability resolution is too distributed

The active command/tool surface depends on built-ins, skills, bundled skills, plugins, workflows, MCP, feature flags, permission mode, and runtime state.

That flexibility is good, but the resolution logic is spread across too many places instead of being represented as one clear capability snapshot.

5. App state and tool runtime context are broad

Refs:

  • src/state/AppStateStore.ts
  • src/Tool.ts:123-239

AppState and ToolUseContext both look broad enough to become accidental coupling points.


Recommended refactor order

  1. Clarify task terminology
  2. Introduce a shared capability snapshot
  3. Extract shared session bootstrap
  4. Split REPL orchestration
  5. Split headless shell
  6. Split bridge supervisor
  7. Split AppState by domain
  8. Slim ToolUseContext

Best immediate next step

If I had to choose one next refactor, it would be:

Introduce a shared capability snapshot layer

Why:

  • high leverage
  • low relative behavior risk
  • gives one clearer answer to “what is active right now?”
  • helps later REPL/headless/bootstrap cleanup

Concretely, I would add:

  • src/capabilities/types.ts
  • src/capabilities/buildCommandSnapshot.ts
  • src/capabilities/buildToolSnapshot.ts
  • src/capabilities/buildCapabilitySnapshot.ts

Then make existing APIs in src/commands.ts and src/tools.ts delegate to those builders while preserving behavior.

Critical invariants:

  • command ordering unchanged
  • dynamic skill insertion unchanged
  • tool ordering unchanged
  • built-in tool precedence over MCP tools unchanged

Why this issue exists

This repository already has a real architecture. The problem is not lack of structure; it is that several important boundaries are still implicit:

  • REPL shell vs headless shell
  • bridge supervisor vs child headless sessions
  • runtime tasks vs todo/planning tasks
  • built-in definitions vs resolved active capabilities

Making those boundaries explicit would reduce drift and make future maintenance safer.

Activity

  1. DevMatei commented on Mar 31, 2026

    @DevMatei

    looks like AI wrote this issue

  2. realsigridjin commented on Mar 31, 2026

    @realsigridjin
    ContributorAuthor
  3. Boyee2022 commented on Mar 31, 2026

    @Boyee2022

    cc 写的审查 cc 的源码

  4. realsigridjin commented on Mar 31, 2026

    @realsigridjin
    ContributorAuthor

    Follow-up with a diagram-first architecture view.

    Architecture diagram

    flowchart TD
      subgraph Bootstrap
        CLI[src/entrypoints/cli.tsx]
        INIT[src/entrypoints/init.ts]
        MAIN[src/main.tsx]
        LAUNCH[src/replLauncher.tsx]
      end
    
      subgraph InteractiveShell
        APP[src/components/App.tsx]
        REPL[src/screens/REPL.tsx]
        INPUT[src/utils/processUserInput/processUserInput.ts]
      end
    
      subgraph HeadlessShell
        PRINT[src/cli/print.ts]
        SIO[src/cli/structuredIO.ts]
        ASK[src/QueryEngine.ts]
      end
    
      subgraph Bridge
        BMAIN[src/bridge/bridgeMain.ts]
        SRUN[src/bridge/sessionRunner.ts]
        BMSG[src/bridge/bridgeMessaging.ts]
        RBRIDGE[src/hooks/useReplBridge.tsx]
      end
    
      subgraph SharedEngine
        QUERY[src/query.ts]
        ORCH[src/services/tools/toolOrchestration.ts\n+ StreamingToolExecutor]
      end
    
      CLI -->|interactive path| MAIN
      MAIN --> INIT
      MAIN --> LAUNCH
      LAUNCH --> APP
      APP --> REPL
      REPL --> INPUT
      INPUT --> QUERY
    
      MAIN -->|headless path| PRINT
      PRINT --> SIO
      PRINT --> ASK
      ASK --> QUERY
    
      CLI -->|bridge fast-path| BMAIN
      BMAIN --> SRUN
      BMAIN --> BMSG
      SRUN -->|spawns child CLI in --print --stream-json mode| PRINT
      RBRIDGE --> REPL
      RBRIDGE --> BMSG
    
      QUERY --> ORCH
      ORCH --> QUERY
    
    Loading

    Focused review

    • The architecture is best understood as two shells around one shared engine, plus a bridge supervisor around headless child sessions.
    • The strongest design choice is that interactive, headless, and bridge flows all converge on the same lower turn engine instead of duplicating model/tool execution logic.
    • The biggest risk is not the engine itself, but the amount of orchestration still split across src/main.tsx, src/screens/REPL.tsx, src/cli/print.ts, and src/bridge/bridgeMain.ts.
    • The bridge is important to think of as a supervisor over headless child CLI sessions, not just a UI transport layer, because that determines how behavior drifts and where shared abstractions matter most.

    Short version: the engine looks shared and healthy; the shell/supervisor boundaries are where cleanup buys the most.

  5. realsigridjin commented on Mar 31, 2026

    @realsigridjin
    ContributorAuthor

    Follow-up focused specifically on the proposed capability snapshot refactor.

    Capability snapshot target diagram

    flowchart TD
      subgraph CapabilitySources
        BUILTIN_CMDS[src/commands.ts\nBuilt-in command registry]
        BUILTIN_TOOLS[src/tools.ts\nBuilt-in tool registry]
        SKILLS[src/skills/loadSkillsDir.ts]
        BUNDLED[src/skills/bundledSkills.ts]
        PLUGINS[src/utils/plugins/loadPluginCommands.ts]
        WORKFLOWS[workflow commands]
        MCP_CMDS[MCP commands]
        MCP_TOOLS[MCP tools]
        DYNAMIC[dynamic skills]
      end
    
      subgraph SnapshotLayer[Proposed snapshot layer]
        CS[src/capabilities/buildCommandSnapshot.ts]
        TS[src/capabilities/buildToolSnapshot.ts]
        CAPS[src/capabilities/buildCapabilitySnapshot.ts]
        TYPES[src/capabilities/types.ts]
      end
    
      subgraph ExistingPublicAPIs
        GETCMDS[getCommands / skill-command views]
        GETTOOLS[assembleToolPool / getMergedTools]
      end
    
      subgraph Shells
        REPL[src/screens/REPL.tsx]
        PRINT[src/cli/print.ts]
        BRIDGE[src/bridge/sessionRunner.ts -> print mode]
      end
    
      BUILTIN_CMDS --> CS
      SKILLS --> CS
      BUNDLED --> CS
      PLUGINS --> CS
      WORKFLOWS --> CS
      MCP_CMDS --> CS
      DYNAMIC --> CS
    
      BUILTIN_TOOLS --> TS
      MCP_TOOLS --> TS
    
      TYPES --> CS
      TYPES --> TS
      CS --> CAPS
      TS --> CAPS
    
      CS --> GETCMDS
      TS --> GETTOOLS
    
      GETCMDS --> REPL
      GETCMDS --> PRINT
      GETTOOLS --> REPL
      GETTOOLS --> PRINT
      PRINT --> BRIDGE
    
    Loading

    Why this should be the first refactor

    • Today the active capability surface is assembled from built-ins, skills, bundled skills, plugins, workflows, MCP, feature flags, permission mode, and runtime state.
    • That flexibility is useful, but the resolution logic is distributed enough that it is hard to answer a simple question like: what commands/tools are active right now?
    • A snapshot layer is a good first PR because it is high leverage, relatively low risk, and easy to verify with ordering/collision tests.
    • The main invariant is simple: this refactor should change where capability resolution lives, not how capability resolution behaves.

    Key rule: src/commands.ts and src/tools.ts should still own built-in definitions; the new snapshot layer should assemble the active surface, not redefine it.

  6. emsi commented on Mar 31, 2026

    @emsi

    up

  7. uukkc618 commented on Mar 31, 2026

    @uukkc618

    cc 写的审查 cc 的源码

    cool

  8. Sakura4036 commented on Mar 31, 2026

    @Sakura4036

    good

  9. jinyule commented on Mar 31, 2026

    @jinyule

    cool

  10. varunmehrishi commented on Mar 31, 2026

    @varunmehrishi

    Also found some hidden features - #933

  11. added 2 commits that reference this issue on Apr 26, 2026
  12. added a commit that references this issue on May 11, 2026
  13. added a commit that references this issue on Jun 3, 2026
  14. added 2 commits that reference this issue on Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions