Introduction

The default Headless runner in DeepSeek Harness (DSH) is usually one-shot, making it difficult to handle complex scenarios that require multi-turn interaction or to obtain structured output. The dsh-plugin-cli-session plugin solves this problem. It is a resumable Headless CLI session runner maintained by user ghbhiee. It merges the original conversational runner with a claude -p-style runner, unifying session parsing, driver loop, and summarization logic, while also supporting multi-directory isolation and tag management.

Core Features

The plugin mainly provides the following capabilities:

  1. Session resume: Supports continuing the latest session or specifying a session by ID.
  2. Multi-directory isolation: Use --workdir to specify the working directory; sessions in different directories do not interfere with each other.
  3. Machine-readable output: Supports JSON output for easy script processing.
  4. Session management: Supports managing sessions through tags and listing.

Installation

The plugin is installed via the command line, with no build step required.

dsh plugin --profile chat add github:ghbhiee/dsh-plugin-cli-session

You can also install it from a local clone to facilitate local rebuilding and debugging.

git clone https://github.com/ghbhiee/dsh-plugin-cli-session.git
dsh plugin --profile chat add ./dsh-plugin-cli-session

Configuration

The plugin manages session configuration through configuration files. You typically need to modify ~/.dsh/profiles/chat/cordis.patch.yml or ~/.dsh/profiles/api/cordis.patch.yml.

You need to reset the config field and specify request as !!js ctx.cliStartup. The following are two typical configuration examples:

Conversational mode:

- id: cli-runner
  config:
    request: !!js ctx.cliStartup
    sessionTag: chat-cli
    announceSessionId: true

Machine-readable mode:

- id: cli-runner
  config:
    request: !!js ctx.cliStartup
    sessionTag: api
    announceSessionId: false

Configuration options:

  • sessionTag: Defaults to cli. It is written to the session’s agentPreset and also defines the scope for --list and --resume, ensuring that sessions with different configurations in the same directory do not interfere with each other.
  • announceSessionId: Defaults to false. Determines whether to write the session ID to standard error (stderr), keeping stdout clean.
  • exitGraceMs: Defaults to 1500. The wait time before graceful exit.

Typical Usage

The plugin provides multiple command-line parameters to control behavior.

  1. Create a new session
    Start a new session in the default directory:
    dsh --profile chat "explain this file"
  1. Resume a session
    Continue the latest session in the current directory:
    dsh --profile chat -r "follow-up"
Continue a session with a specified ID:
    dsh --profile chat -s <id> "follow-up"
  1. Multi-directory sessions
    Start a new session in a specified directory:
    dsh --profile chat -w ./scratch "start here"
  1. List sessions
    List all sessions in the current configuration and exit:
    dsh --profile chat -l
  1. Machine-readable output
    Output results in JSON format, without conversational text:
    dsh --profile api -o json "summarize this"
Output in streaming JSON:
    dsh --profile api -o stream-json "summarize"

Important Notes

Keep the following in mind when using the plugin:

  • Meaning of --json-schema: This parameter passes prompt text requiring the model to output JSON according to the given schema; it does not enable the provider’s structured output mode. The model may still return code blocks or comments, so parsing should be defensive.
  • Output content: CLI mode returns only the last assistant message; intermediate conversation turns are recorded in the session log.
  • Coverage of is_error: As long as the session state is not completed, the is_error field is true, including turns that have not started.
  • Storage of sessionTag: This field is stored in agentPreset. It is a borrowed field and not an extension point defined in the official documentation.
  • Hard exit mechanism: exitGraceMs sets the timeout for graceful exit. After the timeout, the process is forcibly killed, which may truncate very slow flushing.