Skip to content
dsh.fish
Bundle

dsh-research-first

Research-first assistant for DeepSeek Harness: gently nudge the agent to investigate docs / code / community before edits, detect blocked research, and record every hint in the session log.

Source
outnever
stars
1 stars
License
MIT
Updated
Updated 5 days ago

Readme

# dsh-research-first

**A research-first assistant plugin** for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH).

Gently nudges the agent to do a cheap, deterministic investigation before editing code — without hard-blocking (so "can't reach docs" never stalls the work).

[简体中文](README.zh.md)

```
agent about to write/edit/bash without investigating
        │
        ▼
  soft nudge: allow + inject a reminder (model-visible, logged)
        │
  investigation blocked (web_search failed / GitHub unreachable)
        │
        ▼
  remind the user why, offer to continue anyway — fully logged, replayable
```

---

## Why

Three real failure modes of LLM coding, which compound each other:

| Failure | Symptom |
|---|---|
| **Guessing before acting** | Writes code from memory instead of checking requirements / API / docs |
| **Trial-and-error loops** | Repeated failures without looking up known community workarounds |
| **Stale knowledge** | Acts on outdated versions / endpoints that have since changed |

**The core insight**: one cheap lookup prevents one expensive rework.

---

## Design philosophy

> Make "verify first" a **preferred attempt + frictionless nudge + blocked-notice + full trace**, not a hard prerequisite.

**Why not hard-block?** Investigation can be genuinely impossible (air-gapped environments, network restrictions, no official docs). If "can't verify" halts development, the cure is worse than the disease. So the default is a **soft nudge** — remind without blocking, leaving the choice to the agent and the user.

| Behavior | Description |
|---|---|
| **Soft nudge** (default `remind`) | Editing without investigating → not blocked, but a reminder is injected (model-visible + logged) |
| **Blocked-research detection** | Investigation tools (`web_search`, etc.) failing → reminder: "research blocked, ask the user whether to continue" |
| **Research-norm injection** | Adds "golden research habits" to systemPrompt: search with versions, prefer official tools, explain when blocked |
| **Failure reminder** | After repeated failures, remind to check community feedback (GitHub issues) first |
| **Full trace** | Every reminder is a plugin-sourced message in the session log — replayable, auditable |

Set `intensity: warn` (hold for approval) or `intensity: block` (deny) when you want hard enforcement.

---

## Comparison with similar plugins

| Plugin | Focus | Difference from this one |
|---|---|---|
| [`dsh-doublecheck`](https://github.com/PerryLink/dsh-doublecheck) | Engineering-discipline gates (grill requirements, red/green tests, adversarial review) | Defaults to **hard** enforcement; this plugin defaults to **soft** |
| [`dsh-pain-point-check`](https://github.com/ICCuse/dsh-pain-point-check) | Veto-based stop after failed experiments | It **denies** non-investigative tools; this plugin **reminds** without blocking |
| **dsh-research-first** | Lightweight research-first assistant | Frictionless, non-blocking, hands decisions back to the user when blocked |

They compose — use doublecheck for strict discipline, or this plugin for a lightweight, unobtrusive default.

---

## Install

### Option 1: official (recommended, needs pnpm)

```sh
dsh plugin --profile web add github:outnever/dsh-research-first
```

Restart:

```sh
dsh web
```

### Option 2: manual (no pnpm)

```sh
# 1. symlink into the profile's node_modules
ln -sfn /absolute/path/to/dsh-research-first ~/.dsh/profiles/web/node_modules/dsh-research-first

# 2. add to ~/.dsh/profiles/web/cordis.patch.yml:
# - insert:
#     - id: research-first
#       name: 'dsh-research-first'
#       config: { intensity: remind }

# 3. restart dsh web
```

Verify:

```sh
dsh --profile web --dump-config | grep -A2 research-first
# expect: id: research-first / name: dsh-research-first / intensity: remind
```

---

## Configuration

| Field | Default | Meaning |
|---|---|---|
| `intensity` | `remind` | `remind` (nudge) / `warn` (approval) / `block` (deny) |
| `failureThreshold` | `2` | Consecutive failures before the community-check reminder |
| `injectNorm` | `true` | Whether to inject the research norm into systemPrompt |
| `detectBlocked` | `true` | Whether to detect failing investigation tools and remind |
| `investigationTools` | `read, grep, glob, web_search, read_image, ask_user_question, skill` | Tool names counted as "investigation" |
| `mutationTools` | `write, edit, bash, pwsh, str_replace_editor` | Tool names counted as "editing" |

---

## How it works

Built on DSH's official extension points only — no private machinery:

- `tools/pre-execute` (waterfall): mark investigation / decide allow · remind · deny
- `tools/post-execute` (waterfall): inject `additionalContexts` reminders, detect blocked research, count failures
- `agent/pre-step` (waterfall): reset the investigation flag on `step===1`
- `systemPrompt.section`: inject the research norm

**Key implementation detail**: reminders are hand-written `UserMessage` shapes (`{ id, role:'user', content, source:{kind:'plugin',...} }`) with `id` from Node's built-in `crypto.randomUUID()`, keeping **zero dsh package imports** — so the plugin loads via symlink without pnpm resolving dependencies.

---

## Testing

```sh
node test.mjs
# 19 mock unit tests: soft nudge, blocked-research, failure reminder, block/warn modes, norm injection, turn reset
```

Runs with no dependencies — `lib/index.js` only imports `node:crypto`.

---

## Known limitations

- The `remind` reminder is a soft hint in the session — not enforced; the model may ignore it. Use `block` to enforce.
- "Search with versions / prefer official tools" is soft-guided via systemPrompt — rules can't hard-guarantee it (it's ultimately the model's willingness).

---

## Contributing

Issues and PRs welcome. When changing:

1. Keep zero dsh package imports (prerequisite for symlink loading without pnpm);
2. Update `test.mjs` and keep `node test.mjs` green;
3. Update the README's behavior description.

---

## License

[MIT](LICENSE)

Install

dsh plugin --profile web add github:outnever/dsh-research-first

Profile: web

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