dsh-web-search-ddg
aooyoo
Zero-token DuckDuckGo search provider for the DeepSeek Harness (DSH) web seam — local headless browser, no API key, no m…
PROJECT TOPICS
PROJECT README
English · 中文

Let your agent tell you when a long task is done — no more staring at the screen.
dsh-speak reads the final assistant reply aloud through system speech synthesis —
on Windows using natural voices (Windows 11 built-in, or
NaturalVoiceSAPIAdapter on Windows 10) with graceful fallback to stock voices;
on macOS using the built-in say (can follow a Siri natural voice). It was built
for DeepSeek Harness
and is structured so any harness can plug in.
Project status: this project exists only to provide an already-verified solution for users who want their harness to speak. Barring unexpected circumstances, it will not be updated further.
Install the package into your web profile (pick one):
dsh plugin --profile web add dsh-speak
# or, without pnpm:
npm install --prefix "$env:USERPROFILE\.dsh\profiles\web" dsh-speak
On macOS (bash):
npm install --prefix "$HOME/.dsh/profiles/web" dsh-speak
Append to ~/.dsh/profiles/web/cordis.patch.yml:
- insert:
- id: speech-hook
name: 'dsh-speak'
Restart the DSH web app — replies are now announced aloud.
Let your agent do it? Paste this repo URL (
https://github.com/Alan2Z/dsh-speak) into your DSH session and ask it to install the plugin — your agent follows this very README. Approving the out-of-workspace writes (~/.dsh) is all that's needed.
harness event (DSH session event / Claude Code Stop hook / anything)
│
▼ adapters/… (harness-specific trigger: filter, throttle, cancel)
▼ engine/speak.ps1 / speak.sh (harness-agnostic: clean text → SAPI5 / say)
▼ 🔊 you hear the final reply
ask_user_question.dsh.profile.bundles
and the plugin registers itself via the bundled cordis.patch.yml — no manual
patch entry needed.powershell -File speak.ps1 -Text "你好" / macOS ./speak.sh -t "你好".Windows:
macOS:
say command — no extra software.# 1. install the plugin into your web profile (adds dsh-speak to
# ~/.dsh/profiles/web/package.json dependencies)
dsh plugin --profile web add dsh-speak
# 2. register it in ~/.dsh/profiles/web/cordis.patch.yml
# (for npm packages the bare package name is used — no file:/// URL needed):
# - insert:
# - id: speech-hook
# name: 'dsh-speak'
# 3. restart the DSH web app — replies are now announced automatically
No pnpm?
dsh pluginforwards to pnpm, which is not installed on every machine. The exact same install can be done with npm directly:npm install --prefix "$env:USERPROFILE\.dsh\profiles\web" dsh-speak
The engine ships inside the package (node_modules/dsh-speak/engine/), so no extra
copying is needed.
# 1. clone
git clone https://github.com/Alan2Z/dsh-speak.git
cd dsh-speak
# 2. one-command install: copies engine + plugin, registers in cordis.patch.yml
powershell.exe -NoProfile -ExecutionPolicy Bypass -File adapters\dsh\install.ps1
# 3. verify the engine speaks
powershell -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.dsh\hooks\speak.ps1" -Text "你好,语音播报已就绪。"
# 4. restart the DSH web app — replies are now announced automatically
What the file installer did:
| file | destination |
|---|---|
engine/*.ps1 |
%USERPROFILE%\.dsh\hooks\ |
adapters/dsh/speech-hook.js |
%USERPROFILE%\.dsh\profiles\web\plugins\ |
| registration entry | appended to %USERPROFILE%\.dsh\profiles\web\cordis.patch.yml (backed up first) |
The same adapter runs natively on macOS — the plugin auto-detects the platform and
calls engine/speak.sh (the built-in say command) instead of speak.ps1.
Since 1.2.0 the macOS engine ships in the npm package — no extra software.
# 1. install into your web profile (no pnpm needed — only `dsh plugin` requires it)
npm install --prefix "$HOME/.dsh/profiles/web" dsh-speak
# 2. register in ~/.dsh/profiles/web/cordis.patch.yml (bare package name — no file:/// URL):
# - insert:
# - id: speech-hook
# name: 'dsh-speak'
# 3. no restart needed — the patch watcher hot-reloads; pure-text replies are
# announced after ~1.5 s (tool-calling replies are intentionally not announced)
With pnpm installed,
dsh plugin --profile web add dsh-speakworks identically.
say -v '?' and
cannot be selected by name — they only work as the system default.$TMPDIR/dsh-speech-hook.log
(os.tmpdir() — not /tmp).-v Eddy|Flo|Tingting to force a specific voice (say -v '?' lists them).say has no volume flag — volume follows the system output volume.curl -sfL -o ~/speak.sh "https://cdn.jsdelivr.net/gh/Alan2Z/dsh-speak@main/engine/speak.sh"
chmod +x ~/speak.sh
~/speak.sh -t "你好,Mac 版语音播报测试"
~/speak.sh -t "测试" -v Eddy -r 200 # explicit voice + rate
Register the Stop hook in ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\\path\\to\\dsh-speak\\adapters\\claude-code\\stop-hook.ps1"
}
]
}
]
}
}
Call the engine directly from your agent / wrapper / script:
# announce a one-liner
powershell -NoProfile -ExecutionPolicy Bypass -File engine\speak.ps1 -Text "构建完成"
# announce a long summary (from a file)
powershell -NoProfile -ExecutionPolicy Bypass -File engine\speech-summary.ps1 -Text "…"
# ask for user attention (blocking, for prompts/approvals)
powershell -NoProfile -ExecutionPolicy Bypass -File engine\speech-prompt.ps1 -Text "请做出选择"
Engine parameters (see docs/DESIGN.md):
speak.ps1 -Text "…" -Volume 50 -Rate 1 -MaxChars 300 -LongTextMessage "本次播报内容较长,请自行阅读。"
DSH plugin configuration — prefer the profile patch config block (visible in
dsh --dump-config, per-profile, survives npm updates):
# ~/.dsh/profiles/web/cordis.patch.yml
- insert:
- id: speech-hook
name: 'dsh-speak'
config:
throttleMs: 1500 # merge delay before announcing (ms)
engine: '' # engine path override; '' = auto-resolve
announceApprovals: true # speak approval requests
announceQuestions: true # speak ask_user_question content
stripApprovalPrefix: true # strip the "escalate sandbox to ...: " prefix
longTextMode: message # message | heading (speak largest md heading)
maxChars: 300 # engine per-utterance ceiling
volume: 50 # Windows only
rate: 0 # 0 = engine default (Windows SAPI scale / macOS wpm)
See docs/CUSTOMIZATION.md for the full customization guide.
You can tune behavior without forking, and your changes survive npm update:
Copy the engine out and edit it (recommended — this is where defaults live: volume,
rate, MaxChars, LongTextMessage, voice logic):
# Windows
Copy-Item "$env:USERPROFILE\.dsh\profiles\web\node_modules\dsh-speak\engine\speak.ps1" "$env:USERPROFILE\.dsh\hooks\my-speak.ps1"
# macOS
cp ~/.dsh/profiles/web/node_modules/dsh-speak/engine/speak.sh ~/.dsh/hooks/my-speak.sh
Then point the plugin at your copy in the config block:
- insert:
- id: speech-hook
name: 'dsh-speak'
config:
engine: 'C:/Users/<you>/.dsh/hooks/my-speak.ps1' # or ~/.dsh/hooks/my-speak.sh on macOS
The plugin resolves the engine as config.engine → package engine → ~/.dsh/hooks/,
so your copy wins. npm update only touches the package — your engine stays.
Edit the file inside node_modules — works, but the next npm update overwrites it.
Fork the repo — full control, publish your own package if you want.
| symptom | cause | fix |
|---|---|---|
| No sound at all, no error | no natural voice enabled/installed | Win11: enable a natural voice in Settings → Narrator / Speech; Win10: install NaturalVoiceSAPIAdapter + a voice pack. Test speak.ps1 directly |
| Long replies never spoken | adapter per-Speak character ceiling |
already guarded at 300 chars — lower -MaxChars if needed |
| Emoji-heavy text silent | SAPI fails silently on emoji | already stripped by the engine |
| Plugin not loading | raw Windows path as plugin name | use the file:///C:/… URL form (installer does this) |
| macOS: voice suddenly became "婷婷" | opening the "Spoken Content / Siri Voice" pane drifted the system voice | re-pick via Settings → Accessibility → Spoken Content → System Voice → ⓘ entry |
macOS: no log at /tmp |
os.tmpdir() is /var/folders/.../T, not /tmp |
log is at $TMPDIR/dsh-speech-hook.log |
Plugin diagnostics: Windows %TEMP%\dsh-speech-hook.log; macOS $TMPDIR/dsh-speech-hook.log
engine/ harness-agnostic speech engine (PowerShell + SAPI5 / bash + say)
speak.ps1 / speak.sh clean + speak (the only seam any adapter needs)
speech-prompt.ps1 blocking short announcement
speech-summary.ps1 blocking reply-summary announcement
adapters/
dsh/ DSH web plugin + one-command installer
speech-hook.js session-event trigger (throttle + tool-call cancel)
install.ps1 copies + registers + backs up
claude-code/
stop-hook.ps1 Claude Code Stop hook trigger
docs/
DESIGN.md full design rationale, pitfalls, extension guide
Three reference patterns exist: event-stream (DSH), stop-hook (Claude Code),
agent-called (speech-summary.ps1 from a shell). In every case the adapter only
needs to: capture the final reply text → invoke the engine. See
docs/DESIGN.md §7.
MIT — see LICENSE.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。