Skip to content
dsh.fish
Bundle

dsh-claude-live

Run Claude Code headless as a subagent from the DeepSeek Harness, streaming a live digest of its steps into the session view while it works.

Source
flykit-cc
License
MIT
Updated
Updated 17 hours ago

Readme

# dsh-claude-live

Run **Claude Code** headless as a subagent from the DeepSeek Harness, with its
steps streaming into the session view **while it works**.

One tool. One child process. No registry, no role library, no continuable
sessions.

## Why this exists

Two things that don't work today:

- **Polling is a lie.** `subagent_progress` only reports what the child has
  already flushed; between polls you see nothing, and the model burns turns
  asking. Progress has to be *pushed*, not sampled.
- **Custom events break the viewer.** `dsh-background-agents` writes its own
  `background-agents/fact` event type. The rc.2 persistence read path refuses
  to interpret any event outside `KNOWN_SESSION_EVENT_TYPES`, so the web UI
  cannot replay those sessions at all.

This plugin pushes progress through an event pair the installed harness
already understands: `tool/code-dispatch-start` / `tool/code-dispatch` — the
same pair the core's own Code Mode bridge uses to log nested sub-calls. Both
are documented as *log-only*: `deriveMessages()` ignores them, so the digest
lines never re-enter model context, but UIs use them for live per-sub-call
state. The digest therefore renders as sub-rows under the still-running
`claude_live` call, exactly like nested tool calls under `run_code`.

## Requirements

- DeepSeek Harness `0.1.1-rc.2` or later.
- `claude` on `PATH` (or `~/.local/bin`, `~/.claude/local`, `/usr/local/bin`,
  `/opt/homebrew/bin`), authenticated.
- Node ≥ 18.

## Install

```bash
dsh plugin --profile web add dsh-claude-live
dsh plugin --profile headless add dsh-claude-live   # optional
```

For a local checkout, install the directory into the profile with pnpm and add
the layer yourself:

```bash
cd ~/.dsh/profiles/web
pnpm add file:/path/to/dsh-claude-live
```

```yaml
# ~/.dsh/profiles/web/cordis.patch.yml
- insert:
    - id: claude-live
      name: 'dsh-claude-live'
      config:
        timeoutMs: 1800000
```

Restart the harness afterwards. See `TESTPLAN.md` for the full install +
smoke-test sequence.

## Usage

```
claude_live task="Refactor src/calc.js and run its tests" permissionMode="full" cwd="/srv/project"
```

| Argument | Required | Meaning |
|---|---|---|
| `task` | yes | Complete, self-contained prompt. The child does not see this conversation. |
| `cwd` | no | Working directory. Defaults to the harness process directory. |
| `permissionMode` | no | `readonly` → `--permission-mode plan`; `full` → `--dangerously-skip-permissions`; `default` (the default) → Claude's own configuration. |
| `model` | no | `--model` override. Omit for Claude's default. |

Returns `{ ok, result, stats: { events, toolCalls, durationMs } }`, or
`{ ok: false, error }` when the CLI could not start, timed out, was cancelled,
or exited without a result event. Every return value is sanitized to strict
lossless JSON (no `undefined`, no `NaN`/`Infinity`/`-0`, no class instances,
no cycles) — the harness rejects anything else.

While the run is in flight you see ONE self-updating row under the
`claude_live` call (throttled ≤1/s) — the header tracks the current step,
the body is a rolling window of recent digest lines plus a live counter:

```
claude:Bash · npm test          ← header = current step
▸ claude full: Refactor src/calc.js and run its tests
✱ Reading the module to see what the tests cover
▸ Read: /srv/project/src/calc.js
▸ Bash: npm test
◦ 4 steps · 0:12                ← rolling counter
```

The row settles to `claude:done` when Claude finishes. No row-per-step
stacking — the transcript stays flat, like the Claude Code CLI.

## Config

| Key | Default | Meaning |
|---|---|---|
| `timeoutMs` | `1800000` (30 min) | Hard cap per run. On expiry the child's whole process group gets SIGTERM, then SIGKILL after 5s. |
| `command` | auto-detected | Explicit path to the `claude` binary. Used strictly — a bad value fails loudly rather than falling back. |

Cancelling the tool call (`exec.signal`) kills the child the same way.

## Coexistence

Distinct plugin id (`claude-live`) and tool name (`claude_live`), so it runs
alongside `dsh-plugin-product-subagents` without collision.

## License

MIT


## Prior art

- [relay-dsh-plugin-claude](https://github.com/yangbobo2021/relay-dsh-plugin-claude) also streams Claude Code activity into the DSH conversation — as a **session backend**: you pick "Claude Code" as the session mode and DSH's own agent loop steps aside. dsh-claude-live is the **subagent shape**: DSH stays the driving agent and delegates, so DeepSeek plans/orchestrates and Claude executes chunks, with both visible in one session.
- [dsh-plugin-product-subagents](https://github.com/shaokeyibb/dsh-plugin-product-subagents) runs Claude as a subagent (richer lifecycle: roles, continuable children) but its trace tool currently fails with "value is not lossless JSON" ([issue #4](https://github.com/shaokeyibb/dsh-plugin-product-subagents/issues/4)), so no live steps.
- [dsh-background-agents](https://github.com/PerryLink/dsh-background-agents) streams via custom event types the released harness can't replay (needs unreleased core).

## Contributing

Small by design, and there's plenty to do: streaming for **codex** and ACP CLIs as the child, cancellation from the UI, richer digests (diffs, token counts), tests against future dsh releases. Issues and PRs welcome.

Install

dsh plugin --profile web add github:flykit-cc/dsh-claude-live

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source