DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

Scratch Vault — DeepSeek Harness 插件(DSH Plugin)
DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
S

dsh-scratch-vault

Scratch Vault

面向 DeepSeek Harness 的 Codex 风格临时执行:操作脚本从会话临时根目录运行,并收集到插件自有的存储库中,因此工作区始终只保留真正的交付物。转移新的临时写入,并处理已存在的残留内容。

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:TINGOD2006/dsh-scratch-vault#f4335fbfb131bbfbde49978d641e90816c445fb1
README兼容性版本

兼容性与来源证明

Scratch Vault 以 dsh-scratch-vault 发布,当前版本为 0.2.1。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
any
发布来源
github
Registry 更新时间
2026/10/19

版本

0.2.1stable
2026/10/19

相关插件

正在加载相关插件…

最新版
0.2.1
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
any
许可证
MIT
发布源
github
GitHub
★ 0
周下载
0
最近提交
2026/10/19
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

相关插件

继续浏览 developer-tools 分类下经过校验的插件。

Opsdsh-ops适用于 Windows x64、Linux x64 和 macOS Apple Silicon 上 DSH 的原生文件和 PTC 工具;仅限 Windows 的可选 Bash 和 PowerShell 7 载荷Cc Safety Netcc-safety-net编码代理 CLI 钩子——阻止破坏性命令和访问机密文件Skills Manager@michengai/dsh-skills-managerDSH Skills Manager — 在 DeepSeek Harness 中统一加载并安全管理本机 Agent Skills · 在 DSH 中加载并安全管理本地 Agent SkillsMarket Plusdsh-market-plusDSH 插件市场增强版:dsh-market 基座 + dsh-store 的 npm 权威源/质量验证/GitHub 热度/代理注入/回退安装(MIT,融合 dsh-market 与 dsh-store)

README

dsh-scratch-vault

Codex-style scratch execution for DeepSeek Harness — operation scripts run outside the workspace, and get collected in the plugin.

English | 中文

A DSH plugin that keeps agent workspaces to deliverables. The throwaway probes, one-off converters and diagnostics an agent writes only to run once no longer land in your repository: they execute from a session scratch root outside the workspace, and every one of them is archived in a plugin-owned vault you can list, read, promote, or purge.

before                                    after
repo/                                     repo/
├── src/                                  ├── src/
├── _probe_models.py        ← noise       ├── package.json
├── _recon_api.py           ← noise       └── (deliverables only)
├── tmp_convert.mjs         ← noise
├── apply-paths.ps1         ← noise       vault/
└── package.json                          └── entries/20261012…-a1b2c3/script

Why

Long-running agent tasks accumulate intermediate files. The files themselves cost nothing on disk; the cost is that every later glob, grep and directory listing has to wade through them, which pushes irrelevant content into context and makes the agent re-derive what it already did.

DSH already has most of the machinery to prevent this. Its workspace-write sandbox grants writes under the workspace root or a platform temporary root (writableRoots returns the workspace root, /tmp and os.tmpdir()), and the workspace change summary already excludes temporary roots (isTemporaryPath — "scratch paths that never enter the change summary"). What is missing is the part that routes scratch work there, and the part that remembers it.

This plugin is those two parts.

Install

dsh plugin --profile desktop add github:TINGOD2006/dsh-scratch-vault

Then restart DSH. From a local checkout:

dsh plugin --profile desktop add link:C:/path/to/dsh-scratch-vault

On Windows use forward slashes in the path, or pnpm will splice the drive letter into the profile directory and the plugin will install without activating.

Requirements: Node ≥ 22 and a DSH build that mounts the tools service. The plugin declares no dependencies at all — not even @deepseek-ai/dsh-tools. A linked plugin's bare specifiers resolve against its real path, which never reaches the profile's node_modules, so one host import is enough to make the whole plugin fail to load with nothing but failed to import to show for it. Tool definitions are therefore assembled by hand in lib/tools.js, producing exactly the shapes defineTool produces. Nothing to install, nothing to keep in sync.

Editing any file under lib/ requires a full DSH restart. The loader builds its module graph at boot and caches each module by its resolved URL, so disabling and re-enabling the bundle re-runs the cached module's apply() — it does not re-import the file.

What it does

L2 — discipline, environment, inline execution

  • A system-prompt section telling the model that operation scripts belong in the scratch root, not the workspace.
  • $DSH_SCRATCH and $DSH_VAULT, injected into every shell call through ctx.shellEnv. The namespace is rebuilt per shell call, so the value is always the calling session's own directory.
  • run_script({ language, code, description }) — the "run it without writing a file into the workspace" tool. The program is written to a scratch file outside the workspace and executed through ctx.shell, so it runs under the session's sandbox policy. This is the right answer to "I just need to run something".

L3 — interception

tools/execute is the only seam that runs around dispatch and can therefore change what a tool body receives: the registry passes the mutable execution into the waterfall, and dispatchToolBody reads exec.arguments afterwards. (tools/pre-execute cannot do this — its decision type is allow | deny | cancel | ask, with no place to put arguments.)

When a write or edit targets a scratch script inside the workspace, its file_path is rewritten to the session scratch root, and the tool result tells the model the real absolute path plus the vault entry id. Shell commands that reference the redirected script are rewritten too.

Two properties keep this safe:

  • The mapping is a pure function of the path (scratchPathFor). DSH dispatches tool calls in parallel groups, so a write and the pwsh that runs it can be in flight simultaneously; anything order-dependent would race.
  • A shell token is only rewritten when the workspace file does not exist. A legacy leftover that really is on disk keeps running from disk.

The shell rewriter is deliberately conservative, because a missed rewrite costs one uncollected file while a wrong rewrite breaks a command that used to work. A token is left alone when it sits next to a glob metacharacter, extends a longer filename (x.py.bak), is part of a shell variable reference ($env:X\a.py, $DSH_SCRATCH/a.py, %TEMP%\a.py), or is relative and its working directory cannot be determined — cd $env:WORK, cd %WORK%, a bare cd (which goes to the home directory), cd ~/proj.

A relative token after an earlier cd/Set-Location/pushd is resolved against the directory that change put the shell in, not blindly against the workspace root: cd <workspace> then python _probe.py is the everyday shape and still redirects to the scratch path the write produced, while cd …\lib then Get-ChildItem notes.js refers to a real file and is left alone. Absolute paths stay unambiguous and are always redirected, drive letter included.

L4 — the vault

Every intercepted script is archived under the vault directory with a JSONL ledger, plus scratch_list, scratch_show, scratch_promote (copy one back into the workspace as a real deliverable) and scratch_purge.

scratch_import retrofits residue that was already in the workspace before the plugin existed: it collects the paths you name, verifies the vault copy byte-for-byte, and only then removes the original (keepOriginal: true keeps it, dryRun: true reports without touching either side).

Every vault write happens inside the plugin's own process, and that is load bearing. <vaultDir> lives under DSH_HOME, outside the session workspace and therefore outside the sandbox's writable roots: a program the agent runs through the shell cannot write the vault, and neither can a sandboxed tool. Collection only works on the host side of that boundary — which is precisely where these tools run.

Configuration

There is no Cordis Config schema on purpose. A profile that cannot resolve a schema library's peer dependency would fail to mount the plugin entirely, and a workspace-hygiene plugin that refuses to load is worse than one with no settings form. Configuration is a plain JSON file instead:

<vaultDir>/scratch-vault.json          # default: ~/.dsh/scratch-vault/scratch-vault.json
$DSH_SCRATCH_VAULT_CONFIG              # or point at any JSON file

Every key is optional; missing keys fall back to the built-in defaults.

KeyDefaultMeaning
enabledtrueMaster switch.
interceptMode"redirect""redirect" moves the write; "deny" refuses it and explains the alternative; "report" changes nothing but logs every file that would have been intercepted.
scratchRoot"" → <os.tmpdir()>/dsh-scratchWhere scratch files live. Must be outside the workspace.
vaultDir"" → <DSH_HOME>/scratch-vaultWhere collected scripts are archived.
interceptWritestrueWhether write/edit are inspected at all.
rewriteShelltrueWhether shell command references follow a redirected write.
injectPrompttrueWhether the discipline section is added to the prompt.
exposeEnvtrueWhether DSH_SCRATCH/DSH_VAULT are injected into shell calls.
registerRunScripttrueWhether run_script is offered.
interpreters{python,node,powershell,bash}Executable per language.
extensions{python:".py",node:".mjs",…}Scratch file extension per language.
scriptExtensions27 entriesWhich extensions can be scratch scripts.
scriptNamePatternsnarrow setBase-name regexes marking a scratch script.
scratchDirNames_scratch, tmp, …Directory names that mark everything beneath them.

Start with report mode

On an unfamiliar repository, set "interceptMode": "report" first. It records what the classifier would have done without moving a single file, which is the cheap way to find out whether scriptNamePatterns needs tuning for your layout.

How the classifier decides

A false positive silently diverts a real deliverable; a false negative merely leaves a helper script behind — the status quo. Every default therefore leans toward keeping the file, and the rules are ordered worst-case-first:

  1. excludeGlobs matched → keep (the user named this file).
  2. A directory segment is in protectDirs → keep (real source wins over any scratch heuristic).
  3. A directory segment is in scratchDirNames → vault.
  4. The base name matches scriptNamePatterns (_probe.py, .check.mjs, tmp_x.sh, recon_*.py) and its extension is in scriptExtensions → vault. The extension gate is what keeps .gitignore, .env and .editorconfig out of the vault — their names look like scratch files, but they are ordinary repository files.
  5. A script-extension file sits directly at the root of a workspace with no project manifest → vault. This is the rule that separates "a scratch folder" from "a repository".
  6. Otherwise → keep.

scratch_promote is the escape hatch for rule 4 or 5 guessing wrong.

The tools

ToolPurpose
run_scriptRun a program from a scratch file; no workspace write.
scratch_importCollect scripts already in the workspace; verify, then remove.
scratch_listNewest-first listing, optionally per session.
scratch_showOne entry plus its source.
scratch_promoteCopy a vaulted script back into the workspace.
scratch_purgeDelete entries past the retention window; dry run by default.
scratch_statusEffective configuration and vault counts.

Known limits

  • The vault is not a security boundary. It records what the plugin moved; it does not stop an agent that decides to write a file into the workspace by some path the classifier calls a deliverable.
  • Interception is per-write. A script produced by a shell redirect (python … > out.py) or by a program the agent runs is only caught if the shell token matched a rewritten reference; the file itself is not moved.
  • The shell rewriter under-covers on purpose. Shell variables, globs, longer filenames and relative paths after a cd are all left untouched (see L3), so a scratch script referenced in one of those shapes is not redirected. Redirecting it would have broken a working command.
  • The classifier is name- and location-based. It does not read the file.
  • The vault grows until purged. scratch_purge exists; it is not scheduled.
  • systemPrompt.section cannot name the per-session path (its text function receives only a scope key), which is why the path travels through $DSH_SCRATCH instead of the prompt.
  • run_script requires ctx.shell. Without it the tool is not registered, and the other layers still work.
  • The vault cannot be written from inside the sandbox. It sits under DSH_HOME, outside the workspace and therefore outside writableRoots; only plugin-side code can collect. A shell program the agent runs cannot.
  • Collecting is not automatic. scratch_import is opt-in and explicit. The plugin never scans or sweeps the workspace on its own; it only diverts writes it intercepts and paths you hand it.
  • Editing the plugin needs a restart. The loader caches the module by its resolved URL; disabling and re-enabling the bundle re-runs the cached module's apply(), it does not re-import the file.

Development

node test/classify.test.mjs
node test/intercept.test.mjs
node test/paths.test.mjs
node test/plugin.test.mjs
node test/shellroots.test.mjs
node test/shellscratch.test.mjs
node test/tools.test.mjs
node test/vault.test.mjs

The suite is offline and fully dependency-free: every module under lib/ is imported by the tests, lib/tools.js included — registerTools touches only ctx.tools.register, ctx.get and a vault, so a two-method stub context is a faithful harness for it.

Run the files directly rather than node --test. Under a confined DSH sandbox a child process with piped stdio fails with EPERM, which is what the test runner's file isolation uses.

License

MIT

protectDirs
src, lib, test, …
Never touched — a project's real source.
excludeGlobs[]Deliverable allowlist, workspace-relative.
treatBareRootScriptsAsScratchtrueA script at the root of a workspace with no project manifest is scratch.
retainDays14Purge cutoff.
maxVaultEntries2000Ledger cap; oldest are dropped.
maxEntryBytes4194304Above this the entry is recorded but its bytes are not stored.