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
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-research-first from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.