Back to home@hunterxxn

deep-flow

deepseek-harness tui

Stars
0
Language
TypeScript
Created
Aug 14, 2026
Updated
Aug 29, 2026
GitHub repo

Introduction

deep-flow

A terminal UI (TUI) for DeepSeek Harness: an interactive Ink REPL shipped as an out-of-tree dsh bundle riding over dsh-base. No Host, HTTP server, or browser — everything runs in-process against the composed Cordis tree.

Published on npm as @jkxie/dsh-deep-flow. 中文说明见 README.zh-CN.md

░████  ░█████ ░█████ ░████     ░█████ ░█     ░████ ░█   █
░█   █ ░█     ░█     ░█   █    ░█     ░█     ░█  █ ░█   █
░█   █ ░███   ░███   ░████     ░███   ░█     ░█  █ ░█   █
░█   █ ░█     ░█     ░█        ░█     ░█     ░█  █ ░█ █ █
░████  ░█████ ░█████ ░█        ░█     ░█████ ░████  ░█ █

Features

  • Default new session — boot drops you straight into a fresh conversation; /sessions opens the session picker.
  • Session management/sessions opens a Gemini-style searchable session picker (create / resume / switch); the live session title shows above the composer and can be pinned with /rename.
  • Streaming conversation — assistant output streams in, rendered as Markdown with lowlight (highlight.js) syntax highlighting, in a scrollable transcript with auto-follow.
  • Rich tool cards — file edits as inline diffs, reads with line numbers + highlighting, terminal output, search results, and web sources, driven by the tool render-intent contract.
  • Inline human collaboration — slash commands (local /new /rename /init /sessions /models /keys /help /exit plus the harness's own commands), permission prompts (y/n), and ask-user questions, all answered in a focused dialog layer.
  • Input UX/ command completion and @path completion with inline ghost text (Tab to accept), plus / input history.
  • Model switching/models opens a picker over ctx.llm's provider/model catalog, persisted through the default-model settings.
  • Provider setup/provider opens a picker over a curated provider roster (OpenAI / Anthropic / OpenCode), then collects the API key inline (masked) and wires it end to end: the key goes to the harness credential store and the llm-pi-ai provider profile lands in the user settings document, live — no restart.
  • Gemini-style theme — semantic color tokens, gradient logo + spinner, > prompt, and a centralized, documented keymap.
  • Status observability — a live working line (driven by the dsh-working-activity plugin) over a segmented context bar (system / prompt / assistant / thinking / tools / free), a TPS gauge + sparkline, and per-run stats — cache hit rate, reasoning effort, and input → output tokens — on the status line.
  • Git branch badge — when the working directory is a git repo, the current branch shows as a ⎇ <branch> badge on the status line (read at boot and refreshed each time a model turn completes).
  • Session metrics commands/status (model, effort, session id, cwd, tokens, context %, tps), /cost (input / output / cache read / cache write), and /tokens (input → output) report right in the transcript.
  • Goals & todos/goal and /todo open panels projecting goal/change and todo/write session events into a live goal + todo list.
  • Trace timeline/trace opens a filterable session timeline (turn / tool / reasoning / token categories), with / switching filters.
  • Export to Markdown/export writes the current session (user / assistant / tool sections) to a Markdown file in the current working directory (cwd).
  • Agent presets/preset opens a picker over the harness's agent-preset roster; picking one switches the agent's preset on a blank session (a session with history reports that preset switching needs an empty conversation).
  • Session modesShift+Tab cycles Default / Plan / Full access: each mode is a named bundle of optional DSH plane switches — plan mode (dsh-plan-mode /plan), sandbox policy, and approval policy.
  • Side questions/btw <question> fires an independent llm.stream call against the current model selection, shown in a panel that never blocks or interrupts the main turn.
  • Rewind — double-Esc on an empty input opens a picker of past user messages; picking one forks the conversation back to that point (a fresh agent via sessions.fork + agents.create) and pre-fills the input with that message.
  • Startup tips — on an empty session, three randomized usage tips show under the logo/version — command token highlighted, description dimmed — chosen by system locale; the tips live in src/tips.txt and are editable without touching code.

Requirements

  • Node ^22.19.0 or >= 24 (older 22.x lacks node:zlib.createZstdDecompress).
  • pnpm >= 11dsh plugin add shells out to pnpm; pnpm 10.x hits ERR_PNPM_ADDING_TO_ROOT.
  • DEEPSEEK_API_KEY — only required to send real model requests; boot and the UI work without it.

Install and run

# Prerequisite: the official harness CLI
npm install -g @deepseek-ai/dsh

# Install deep-flow globally (first run auto-initializes the profile)
npm install -g @jkxie/dsh-deep-flow

# Start
deep-flow

The deep-flow launcher keeps the profile in sync with the installed package:

  • First run — the uninitialized profile is bootstrapped automatically via dsh plugin --profile deep-flow add @jkxie/dsh-deep-flow@<version>, then it launches.
  • Version drift — if the installed profile version differs from the globally installed launcher's version, the profile is re-pinned to the launcher's version automatically, then it launches. So upgrading is just npm install -g @jkxie/dsh-deep-flow@<new version> followed by deep-flow — the profile updates itself on the next launch.
  • In sync — launches directly.

Manual / advanced equivalent:

dsh plugin --profile deep-flow add @jkxie/dsh-deep-flow@latest
dsh --profile deep-flow

Install a specific version:

dsh plugin --profile deep-flow add @jkxie/dsh-deep-flow@0.2.0

Update to the latest:

dsh plugin --profile deep-flow add @jkxie/dsh-deep-flow@latest

@deepseek-ai/* packages resolve from the dsh installation's flat profile fallback (tsx paths in source launches), so they are not npm dependencies of this package — you never install them yourself.

Keymap

SurfaceKeysAction
Conversation / recall previous inputs
/ move the cursor
PgUp / PgDn / mouse wheelscroll the transcript
Tabaccept a / command or @path suggestion
Shift+Tabcycle session mode (default / plan / full access)
Entersend the message
Ctrl-Cclear input → cancel the turn → quit
/newstart a new session
/renamerename the current session
/initgenerate AGENTS.md by analyzing this directory
/sessionspick a session
/modelspick a model
/providerset up a model provider (API key)
/keysmanage API keys
/helplist slash commands
/statusshow session info
/costshow token usage
/tokensshow token details
/goalshow the goal panel
/todoshow the todo panel
/exportexport session to Markdown
/traceshow session trace timeline
Esc Esccancel the running turn / rewind to a past message (empty input)
/presetswitch the agent preset
/btwask a side question (non-blocking)
/exitquit
API keys / k · / jmove selection
Enteredit the selected key (masked)
Escback to sessions
q / Ctrl-Cquit
Promptsy / nallow / reject a permission request
1-9pick a question option
ctype a custom answer
Enterconfirm / skip a question
Escdismiss (cancel the request / question)

How it works

deep-flow is a Cordis bundle (dsh.bundle.patchcordis.patch.yml) that disables the shared module-reload hmr row and inserts the deep-flow-runner plugin. The runner injects the core services (agents, sessions, agentDefaultModel, tools, commands, userQuestions, approval), awaits loader settlement, then renders an Ink tree that:

  • reads the durable session log through session/event and projects it through a Channel (src/store/channel.ts) into the immutable snapshot the React layer subscribes to via useSyncExternalStore; TPS, context-bar and token metrics derive from that session/event projection (assistant/message usage, request/header, request/context, user/message, tool/call), while live activity/status frames from dsh-working-activity feed only the working-status line,
  • submits user input through agent.followup(),
  • cancels in-flight turns through agent.cancel(),
  • creates / resumes agents through ctx.agents.create() / ctx.agents.resume() — resume first runs src/compat/sessionLog.ts, which repairs the persisted log in place so the ephemeral activity/status frames are marked ignorable and seed validation accepts the session,
  • answers the interaction seams through ctx.commands, the approval/request waterfall, and ctx.userQuestions.
ConceptMechanism
Event streamsession/event
Prompt the agentagent.followup()
Interruptagent.cancel()
Create / resume sessionctx.agents.create() / ctx.agents.resume()
Permissions / commands / Q&Actx.approval / ctx.commands / ctx.userQuestions
Model catalog / selectionctx.llm / ctx.agentDefaultModel
Status observabilityactivity/status (dsh-working-activity) → working-status line only; TPS / context-bar metrics come from the session/event projection

The working line comes from the dsh-working-activity plugin, re-exported from src/working-activity.ts under this package's own @jkxie/dsh-deep-flow/working-activity subpath so the dsh loader always resolves it from the profile's direct dependency (pnpm's isolated layout never links transitive deps into the profile root).

Project layout

src/
  index.tsx             entry — re-exports name/inject/apply from plugin.tsx
  plugin.tsx            runner plugin boundary (services, controller, boot, render)
  app.tsx               App surface (view state, single useInput dispatcher, dialogs wiring)
  commands.ts           deep-flow slash commands + parser (/status /cost /tokens /goal /todo /export /trace, pure)
  controller.ts         Controller / HomeSession / CommandOutcome / StatusSnapshot types
  prompt.ts             prompt queue bridging boot ↔ React
  working-activity.ts   re-export of dsh-working-activity (loader-resolvable subpath)
  store/
    channel.ts          Channel — session/event → transcript lines + live metrics snapshot
    metrics.ts          context bar, TPS gauge + sparkline, token formatters (pure)
    goal-todo.ts        goal/change + todo/write event reducers (pure, replay-safe)
    rewind.ts           rewind candidates + fork-boundary computation (pure, replay-safe)
    session-modes.ts    configurable session modes (plan/sandbox/approval bundles, pure)
    trace.ts            bounded, filterable trace timeline projection (pure)
  screens/
    chat.tsx            ChatScreen — transcript + composer + status line
    status-line.tsx     status line + segmented context bar footer
  components/
    goal-panel.tsx      goal panel (/goal) — live goal from goal/change events
    todo-panel.tsx      todo panel (/todo) — whole-list todo/write snapshot
    trace-view.tsx      /trace filterable timeline view
    btw-panel.tsx       /btw side-question panel (independent llm.stream call)
    preset-picker.tsx   /preset agent-preset picker
    rewind-picker.tsx   double-Esc rewind picker (past user messages)
    session-picker.tsx  /sessions Gemini-style session picker
  hooks/
    useStore.ts         thin useSyncExternalStore wrapper
  compat/
    sessionLog.ts       pre-resume session-log repair for third-party events
  transcript-view.tsx   ToolLine / LineView
  header.tsx            gradient logo + version + startup tips (top of the transcript)
  tips.ts               parse tips.txt, pick randomized subset, locale detection (pure)
  tips.txt              bilingual startup-tip data (one cmd|desc|cmd|desc line per tip; edit freely)
  composer.tsx          bordered input box + spinner/footer
  file-completion.ts    @ path completion (pure)
  git-branch.ts         readBranch — current git branch of the cwd (pure)
  init-prompt.ts        buildInitPrompt — /init analysis prompt (pure)
  spinner.tsx           gradient color-cycling spinner
  useTerminalSize.ts    terminal size hook
  markdown.tsx          markdown-it → Ink renderer (streaming-aware)
  highlight.ts          lowlight/highlight.js syntax highlighter (→ Ink token colors)
  tool-cards.tsx        read / terminal / search / web result cards
  diff.tsx              inline file-diff view
  dialogs.tsx           approval / question / model-picker / help dialogs
  keys.tsx              API-key management view (masked editor over ctx.credentials)
  theme.ts              color theme (single dark theme, decoupled)
  keymap.ts             centralized keymap + help text
  logo.tsx              the startup ASCII art (gradient; swap this to rebrand)
cordis.patch.yml      bundle patch (disables hmr, inserts deep-flow-runner)

Development

Build (produces lib/index.js, ESM):

pnpm run build

Run locally from a deepseek-harness checkout (requires pnpm install and at least pnpm run build:lib:host):

pnpm dsh plugin --profile deep-flow add ../deep-flow
pnpm dsh --profile deep-flow

There is no test, lint, or typecheck script — pnpm run build is the main verification command, plus pnpm run verify:metrics / verify:goal-todo / verify:trace / verify:rewind / verify:session-mode for the pure logic (metrics, goal/todo reducers, trace projection, rewind candidates/boundaries, session-mode folding). Rendering is verified offline by mounting components over a fake stdin/stdout with interactive: false; interactive behavior requires a real terminal. See PLAN.md for the milestone plan (M0–M5, all complete) and the design lessons recorded along the way.

Publishing

npm version patch    # or minor / major — npm forbids re-publishing the same version
npm publish --access public --registry https://registry.npmjs.org/

prepublishOnly runs pnpm run build automatically. Use --tag beta to publish a pre-release without moving the latest tag.

License

MIT