Skip to content
dsh.fish
Bundle

dsh-spec-loop

Spec-driven development loop for DeepSeek Harness: a /spec command family drives propose → approve → implement → verify → archive with OpenSpec-compatible change directories, a durable session projection, and a change card in the composer dock.

Source
tianji-qingtian
stars
4 stars
License
MIT
Updated
Updated 24 days ago

Readme

# dsh-spec-loop

Spec-driven development loop for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`): a `/spec` command family drives **propose → approve → implement → verify → archive**, with change directories compatible with the [OpenSpec](https://github.com/Fission-AI/OpenSpec) layout under `<workspace>/openspec/`.

> The harness is in developer preview and iterates quickly — expect compatibility-breaking changes.

> 中文说明见 [README.zh.md](README.zh.md)。

## Features

- **`/spec` command family** — `init` · `new <goal>` · `status` · `list` · `show <id>` · `approve <id>` · `implement <id>` · `verify <id> [--deep]` · `archive <id>` · `validate [id]` · `edit <id>`. Commands orchestrate and touch the filesystem only; proposal and implementation content is generated by the agent's main model through `agent.steer` with the full tool set.
- **Clarification on `/spec new`** — up to three built-in choice questions (scope / constraints / acceptance), in the language of the goal; answered through the harness question UI. Subagent sessions and missing UI providers fall back to proceeding without clarification.
- **OpenSpec-compatible layout** — `openspec/project.md`, `openspec/specs/<capability>/spec.md`, `openspec/changes/<change-id>/{proposal.md,tasks.md,design.md,verify.md,specs/<cap>/spec.md}`, archived to `changes/archive/YYYY-MM-DD-<id>/`. Spec deltas use `## ADDED|MODIFIED|REMOVED Requirements` with `#### Scenario:` per requirement.
- **Built-in validator** — the plugin ships the same core rules as the OpenSpec CLI (sections, requirements, scenarios, change-id shape) and runs them automatically after proposal generation: on failure it steers a correction request back to the agent (bounded retries). `approve` refuses changes that do not validate; `archive` refuses missing changes.
- **Approval gate** — `implement` refuses changes whose state is not `approved` (or later in the implementation chain). The gate reads the same durable projection the panel renders, so display state and behavior state can never disagree.
- **Per-scenario verification** — `verify` judges every Requirement/Scenario against the workspace with a bounded judge call (flash by default, main model with `--deep`), runs ```` ```bash ```` verification commands declared in `proposal.md`, and writes `verify.md` with a ✅/❌ table plus the raw judge output.
- **Durable state machine** — `proposed → approved → implemented → verified → archived` (any stage can `edit` back to `proposed`) is a session projection folded from standard events (`command/run`/`command/done` pairs + the agent's machine-readable markers), so the change card, stage, and gate survive restarts. No custom event types are appended to the log.
- **Change card in the composer dock** — a full-width row above the input card (`conversation.input.dock`) shows the current change-id, stage, task progress `x/y`, and the next command to run. Task progress reads the standard `todos` projection (the implement prompt mirrors `tasks.md` into `todo_write`) — zero RPC. UI strings are localized through the harness `locale` service (zh/en).

## Install

### Prerequisites

The `dsh` CLI must be on your `PATH`. If you only ever ran the harness through `npx`, `dsh` is not installed and you will get `zsh: command not found: dsh` — install it globally first:

```sh
npm install -g @deepseek-ai/dsh
```

`pnpm add -g @deepseek-ai/dsh` also works if your pnpm global bin dir is on `PATH` (otherwise pnpm asks you to run `pnpm setup` first). Alternatively skip the global install and prefix the commands below with `npx @deepseek-ai/dsh …`.

### Add the bundle

```sh
# 1. add the bundle to your web profile (pnpm-backed; the built lib/ artifacts
#    are committed in this repo, so no build script runs at install time).
#    Prefer a release tag (#v0.1.2); #main tracks the latest commit.
dsh plugin --profile web add "github:tianji-qingtian/dsh-spec-loop#v0.1.2"

# 2. restart the harness with that profile — `add` only edits the profile
#    files; a running instance does not hot-load the new bundle
dsh --profile web
```

After the restart the 📐 Spec card appears above the composer in the Web UI and the `/spec` command is registered once the host half loads. Verify under Settings → Plugins that `dsh-spec-loop` is listed.

## Usage

```sh
/spec init                          # create openspec/project.md + directory layout
/spec new 用户登录功能               # clarify → agent writes proposal/tasks/deltas → auto-validate
/spec status                        # read-only card: current change-id, stage, x/y progress
/spec list                          # active changes (x/y tasks) + capability specs
/spec show add-user-login           # read a proposal (plus design.md when present)
/spec approve add-user-login        # approve → implementation gate opens
/spec implement add-user-login      # agent implements tasks.md in order, ticks checkboxes
/spec verify add-user-login         # per-scenario judge (flash); --deep for the main model
/spec verify add-user-login --deep
/spec archive add-user-login        # merge deltas into specs/, move to changes/archive/
/spec validate [id]                 # OpenSpec format check (auto-run after /spec new)
/spec edit add-user-login           # revise a proposal, back to proposed
```

The change card above the input shows the current change-id, stage, and `x/y` progress, plus the next command to run.

## How it works

| Piece | Mechanism |
|---|---|
| Commands | `commands.register` — one `/spec` command with a subcommand router. Handlers orchestrate and touch the filesystem (`ctx.fs`); generation is steered to the agent (`agent.steer` with a plugin-sourced `UserMessage`). |
| State machine | A session projection (`specLoop`) folds standard events: `command/run`+`command/done` pairs transition on success only, and the agent's reply markers (`SPEC_CHANGE_ID: <id>`, `SPEC_IMPLEMENTED`) complete the async stages. `implement` gates on the same projection. |
| Task progress | The implement prompt mirrors `tasks.md` into the `todo_write` tool; the panel reads the shipped `todos` projection. |
| Verification | Handler-side bounded judge call (`ctx.llm.stream`, `reasoningEffort: 'off'`, flash by default); ```` ```bash ```` blocks from `proposal.md` run first via `ctx.shell` and their output is included in the judge prompt. |
| Archive merge | Deltas are merged requirement-by-requirement into `specs/<cap>/spec.md` (ADDED append, MODIFIED replace, REMOVED drop), then the change dir moves with one `mv` (`ctx.shell`). |
| UI | `conversation.input.dock` slot entry; reads `useProjection('specLoop')` + `useProjection('todos')`; `locale` service for zh/en strings. |

## Compatibility notes

- **No custom session event types.** Out-of-repo plugins cannot register new `SessionEventMap` members safely (the persistence read path refuses unknown non-ignorable types), so every state transition rides standard events. This keeps session logs readable even if the plugin is later removed.
- **`ctx.fs` has no move/delete**, so archive uses `ctx.shell` (`mkdir && mv`) for the physical move; the workspace sandbox allows it because `openspec/` lives inside the workspace.
- **Approval state is per-session**, folded from that session's command log — approving in one session does not approve in another.

## Development

```sh
pnpm install
pnpm build      # tsdown: lib/index.js (host) + lib/client.js (client bundle)
pnpm test       # three suites: mock-runtime unit tests, real-filesystem smoke,
                # and a real-composition integration test (cordis + real
                # fs/commands/session-projection/llm services)
```

Requirements baseline: [REQUIREMENTS.md](./REQUIREMENTS.md).

## License

MIT

Install

dsh plugin --profile web add github:tianji-qingtian/dsh-spec-loop

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