huxin7735-collab/dsh-maintainer-doc-guard ↗★ 0
dsh-maintainer-doc-guard
Keeps a long agent turn answerable to the user's actual request: a standing maintainer-document reminder, two pre-execute gates (read a same-basename precedent before editing infrastructure; say what a side-effecting step is for), an objective anchor that quotes the user's latest instruction plus an explicit precedence rule, and a nudge stage that corrects before it blocks. Details: (1) A standing system-prompt section that tells the model to read the workspace maintainer documents (plan.md / conventions.md / stack.md / state.md / maintainer/README.md) before every operation, so small-model context compaction cannot erase long-term project memory. (2) A `tools/pre-execute` precedent gate that denies a write/edit inside a guarded infrastructure area until a working same-basename precedent has been read. (3) A `tools/pre-execute` intent gate that polices the turn's side-effecting calls when the model never said what the step is for. (4) An OBJECTIVE ANCHOR: the user's latest instruction, quoted verbatim in a second prompt section with an explicit precedence rule (user's latest instruction > your own last stated plan > a lead you found yourself), so a long turn cannot silently redefine its own task. (5) A NUDGE stage that corrects before it blocks — the first unexplained action only injects a reminder the model reads at its next step, a drift reminder fires once per long turn, and only a repeated offence is denied. Plus a right-sidebar tab that views and edits those documents, and ten runtime knobs exposed in the harness settings page (设置 → 插件 → 插件配置). 适合需要防止智能体在长对话中偏离用户实际需求的用户。
Install
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:huxin7735-collab/dsh-maintainer-doc-guardREADME
Read the full README ↗Configuration
Row config (all optional):
| key | default | meaning |
|---|---|---|
enabled | true | turn the section off without removing the row |
docs | ["plan.md","conventions.md","stack.md","state.md","maintainer/README.md"] | document names to probe |
onlyWhenPresent | false | when true, stay silent unless at least one document exists |
inSubagents | false | also inject the document reminder into delegated children (true opts in) |
order | 100 | section sort order (after the persona prefix at 0) |
walkUp | 6 | ancestor levels to walk looking for the project root |
projectMarkers | [".git"] | root markers for the walk-up |
title / intro | built-in | override the reminder's heading / body text |
Gate config lives under gate: and is also all optional:
| key | default | meaning |
|---|---|---|
gate.enabled | true | mount the tools/pre-execute guard at all |
gate.dryRun | false | observe only: count what it would deny, never block |
gate.tools | ["write","edit"] | tool names the gate examines |
gate.guardedGlobs | [".dsh/profiles/", ".dsh/skills/", ".dsh/.agent-presets/", "node_modules/@deepseek-ai/"] | POSIX substrings of the resolved target path that make it guarded |
gate.exemptGlobs | [] | POSIX substrings that exempt a target even if guarded |
gate.maxDeniesPerTarget | 2 | denials for one target before the gate gives up and lets it through |
gate.maxCandidates | 3 | precedents listed in one denial |
gate.scanLevels | 4 | ancestor levels scanned for sibling precedents |
gate.entriesPerLevel | 400 | directory entries examined per level (caps node_modules scans) |
Example — observe before enforcing:
- id: maintainer-doc-guard
config:
gate:
dryRun: true
The gate is off for anything outside guardedGlobs, so ordinary project files
(plan.md, source, notes, scratch scripts) are never touched by it.
Each document is probed in the session working directory first, then in the project root, so a document kept at the repo root is found even from a nested cwd.
Everything this plugin copies from outside itself — the quoted user instruction, document names,
title/introoverrides — is defused for the harness's{{variable}}interpolation before it reaches the prompt (the two-character opener and closer are split apart), because rendering throws on an unregistered reference. A user who types{{x}}therefore cannot break prompt assembly for the rest of the session.
Anchor config lives under anchor: and is also all optional:
| key | default | meaning |
|---|---|---|
anchor.enabled | true | render the standing objective section at all |
anchor.inSubagents | false | also render inside delegated children, quoting the child's own delegation under child-specific wording (true opts in) |
anchor.order | 10150 | section sort order — late on purpose, so the section that changes on every user message invalidates as little prefix as possible |
anchor.maxChars | 800 | clip the quoted instruction, marking the cut with … (truncated) |
anchor.title / anchor.intro | built-in | override the heading / the framing sentence |
anchor.priorities | the three-step rule | the precedence list, rendered as a numbered list after the quote |
anchor.childTitle / anchor.childIntro / anchor.childPriorities | built-in | override all three for the child reading — each defaults to wording that says "your delegated task" rather than "the user's latest instruction" |
Nudge config lives under nudge: and is also all optional:
| key | default | meaning |
|---|---|---|
nudge.enabled | true | stage the intent judgement at all; false denies the first offence, as 0.4 did |
nudge.dryRun | false | observe only: count what it would inject, inject nothing |
nudge.grace | 1 | unexplained actions allowed through with a reminder before any denial |
nudge.maxPerTurn | 3 | reminders per turn before it stops injecting (the deny budget is separate) |
nudge.afterSteps | 12 | step count that triggers the once-per-turn drift check; 0 disables it |