Skip to content
dsh.fish
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

  • 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.
Source