Bundle
@jkxie/dsh-deep-flow
Terminal-UI surface for dsh: an interactive Ink REPL bundle riding over dsh-base
- Source
- hunterxxn
- License
- MIT
- Updated
- Updated 7 days ago
Readme
# deep-flow
A terminal UI (TUI) for [DeepSeek Harness](https://github.com/deepseek-ai/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`](https://www.npmjs.com/package/@jkxie/dsh-deep-flow). 中文说明见 [README.zh-CN.md](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 modes** — `Shift+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 >= 11` — `dsh 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
```sh
# 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:
```sh
dsh plugin --profile deep-flow add @jkxie/dsh-deep-flow@latest
dsh --profile deep-flow
```
Install a specific version:
```sh
dsh plugin --profile deep-flow add @jkxie/dsh-deep-flow@0.2.0
```
Update to the latest:
```sh
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/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`.
| 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):
```sh
pnpm run build
```
Run locally from a `deepseek-harness` checkout (requires `pnpm install` and at least `pnpm run build:lib:host`):
```sh
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](PLAN.md) for the milestone plan (M0–M5, all complete) and the design lessons recorded along the way.
## Publishing
```sh
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](LICENSE)
Install
dsh plugin --profile web add github:hunterxxn/deep-flow
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install jkxie-dsh-deep-flow from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.