deep-flow
deepseek-harness tui
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 14, 2026
- Updated
- Aug 29, 2026
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;
/sessionsopens the session picker. - Session management —
/sessionsopens 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/exitplus 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@pathcompletion with inline ghost text (Tabto accept), plus↑/↓input history. - Model switching —
/modelsopens a picker overctx.llm's provider/model catalog, persisted through the default-model settings. - Provider setup —
/provideropens 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 thellm-pi-aiprovider 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-activityplugin) 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 —
/goaland/todoopen panels projectinggoal/changeandtodo/writesession events into a live goal + todo list. - Trace timeline —
/traceopens a filterable session timeline (turn / tool / reasoning / token categories), with↑/↓switching filters. - Export to Markdown —
/exportwrites the current session (user / assistant / tool sections) to a Markdown file in the current working directory (cwd). - Agent presets —
/presetopens 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 modes —
Shift+Tabcycles 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 independentllm.streamcall against the current model selection, shown in a panel that never blocks or interrupts the main turn. - Rewind — double-
Escon an empty input opens a picker of past user messages; picking one forks the conversation back to that point (a fresh agent viasessions.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.txtand are editable without touching code.
Requirements
- Node
^22.19.0or>= 24(older 22.x lacksnode:zlib.createZstdDecompress). pnpm >= 11—dsh plugin addshells out to pnpm; pnpm 10.x hitsERR_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 bydeep-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
| Surface | Keys | Action |
|---|---|---|
| Conversation | ↑ / ↓ | recall previous inputs |
← / → | move the cursor | |
PgUp / PgDn / mouse wheel | scroll the transcript | |
Tab | accept a / command or @path suggestion | |
Shift+Tab | cycle session mode (default / plan / full access) | |
Enter | send the message | |
Ctrl-C | clear input → cancel the turn → quit | |
/new | start a new session | |
/rename | rename the current session | |
/init | generate AGENTS.md by analyzing this directory | |
/sessions | pick a session | |
/models | pick a model | |
/provider | set up a model provider (API key) | |
/keys | manage API keys | |
/help | list slash commands | |
/status | show session info | |
/cost | show token usage | |
/tokens | show token details | |
/goal | show the goal panel | |
/todo | show the todo panel | |
/export | export session to Markdown | |
/trace | show session trace timeline | |
Esc Esc | cancel the running turn / rewind to a past message (empty input) | |
/preset | switch the agent preset | |
/btw | ask a side question (non-blocking) | |
/exit | quit | |
| API keys | ↑ / k · ↓ / j | move selection |
Enter | edit the selected key (masked) | |
Esc | back to sessions | |
q / Ctrl-C | quit | |
| Prompts | y / n | allow / reject a permission request |
1-9 | pick a question option | |
c | type a custom answer | |
Enter | confirm / skip a question | |
Esc | dismiss (cancel the request / question) |
How it works
deep-flow is a Cordis bundle (dsh.bundle.patch → cordis.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/eventand projects it through aChannel(src/store/channel.ts) into the immutable snapshot the React layer subscribes to viauseSyncExternalStore; TPS, context-bar and token metrics derive from thatsession/eventprojection (assistant/messageusage,request/header,request/context,user/message,tool/call), while liveactivity/statusframes fromdsh-working-activityfeed 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 runssrc/compat/sessionLog.ts, which repairs the persisted log in place so the ephemeralactivity/statusframes are markedignorableand seed validation accepts the session, - answers the interaction seams through
ctx.commands, theapproval/requestwaterfall, andctx.userQuestions.
| Concept | Mechanism |
|---|---|
| Event stream | session/event |
| Prompt the agent | agent.followup() |
| Interrupt | agent.cancel() |
| Create / resume session | ctx.agents.create() / ctx.agents.resume() |
| Permissions / commands / Q&A | ctx.approval / ctx.commands / ctx.userQuestions |
| Model catalog / selection | ctx.llm / ctx.agentDefaultModel |
| Status observability | activity/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.