imroc/dsh-project-prompt
Private, per-project prompt rules for DeepSeek Harness — matched by git remote/repo/path, worktree-aware, never committed to the repo
项目介绍Project Overview
dsh-project-prompt 是 DeepSeek Harness 的插件,将私有、按项目区分的提示规则保存在 $DSH_HOME 中,而非仓库内。它通过远程 URL、仓库路径或工作目录前缀匹配会话,支持子代理和 git worktree,提供 section 与 inject 两种注入模式。适合存放环境专属流程或内部端点等私有指令。注意:规则为静态配置,不读取仓库内文件。
dsh-project-prompt is a DeepSeek Harness plugin that keeps private, per-project prompt rules in $DSH_HOME instead of the repository. It matches sessions by git remote URL, local repo path, or cwd prefix, covers subagents and git worktrees, and offers section and inject injection modes. Use it for environment-specific procedures or internal endpoints. Note: rules are static and do not read files from inside the repository.
请帮我了解并安装插件:【dsh-project-prompt】【https://github.com/imroc/dsh-project-prompt】
把上面这条消息直接发给当前会话里的 DSH,让它帮你了解并安装。安装命令不一定准确,发给 DSH 更稳。Send this message to DSH in your current session. CLI install commands may not be accurate across systems — DSH will figure it out for you.
或使用命令行安装(适合开发者)Or use CLI install (for developers)
命令行安装CLI Install
dsh plugin --profile web add dsh-project-prompt
把 imroc/dsh-project-prompt 加入你的 DSH 配置(web profile)即可启用。
READMEREADME
dsh-project-prompt
English | 中文
Private, per-project prompt rules for DeepSeek Harness (DSH).
Some agent instructions belong to your machine, not to the repository: environment-specific E2E procedures, internal-only endpoints, personal workflow preferences. AGENTS.md is committed and shared, so it is the wrong place for them. This plugin keeps such text in $DSH_HOME, never in the repo, and injects it into every session whose workspace matches a rule — including every subagent and every git worktree of the repository.
flowchart LR
subgraph repo["git repository (any clone / worktree / subdir)"]
wt1["main checkout"]
wt2["linked worktree A"]
wt3["worktree B"]
end
cfg["$DSH_HOME/cordis.patch.yml\nrules (machine-local, private)"]
plugin["dsh-project-prompt\nagent/session-start listener"]
sp["session system prompt\n(or first injected message)"]
cfg --> plugin
wt1 & wt2 & wt3 -->|match by remote / repo / path| plugin
plugin -->|section / inject| sp
Features
- Three matchers — match sessions by git
remoteURL (any clone, anywhere on disk), by local main-repo path, or by plain cwd prefix. Keys within a rule are OR-combined. - Worktree-aware — a linked worktree is traced back to its main repository through the
.gitfile'sgitdir:pointer, so a rule written once covers worktrees created later, and their subdirectories. - Two injection modes —
section(a stable system-prompt segment, KV-cache friendly, supports{{cwd}}/{{model}}) andinject(a first context message, no template interpolation — use it when the text contains literal{{...}}braces, e.g. Helm or Go templates). - Covers subagents — subagents inherit the session cwd, so delegated work follows the same rules.
- Fails loudly at load time — malformed rules (missing
text, unknown{{var}}in section mode) throw when the plugin loads, not on every request. - Zero dependencies — a single ESM file importing only Node builtins; no build step, nothing to compile.
Requirements
- DeepSeek Harness ≥
0.1.1-rc.2(uses theagent/session-startevent,systemPrompt.section, andagent.injectextension points). - Node.js ≥ 18 (DSH itself currently requires ≥ 22).
Install
Install into a profile with DSH's plugin command:
dsh plugin --profile web add dsh-project-prompt
# or install straight from the GitHub repository:
dsh plugin --profile web add github:imroc/dsh-project-prompt
Restart DSH afterwards — bundles are composed at startup.
Uninstall:
dsh plugin --profile web remove dsh-project-prompt
Configure
Rules live in $DSH_HOME/cordis.patch.yml (~/.dsh/cordis.patch.yml by default) — the machine-local layer that applies to every profile on the machine. Override the row the bundle installed:
- id: project-prompt
config:
rules:
# Matches this repository wherever it is cloned, and in all worktrees.
- remote: github.com/my-org/my-project
text: |
## E2E testing for this project (environment-specific, private)
1. Test entry point: http://e2e.internal.example.net (internal only)
2. Run `make e2e-prepare` before the first E2E run.
3. On failures, check ... first.
# Matches by local main-repo path; the rule text contains literal
# {{...}} braces, so it must use inject mode.
- repo: /home/me/dev/another-project
mode: inject
text: |
Render values like {{ .Values.replicas }} are literal here.
# Matches any session started under a plain directory prefix.
- path: /home/me/dev
text: |
...
The file is watched for changes — saving it hot-reloads the plugin row (take effect for sessions started afterwards).
Rule reference
| Key | Type | Default | Description |
|---|---|---|---|
remote |
string | — | Git remote URL (origin) to match. Compared after normalization: scheme, user@, scp-style : separator, trailing .git and slashes, and case are ignored. git@github.com:u/r.git ≡ https://github.com/u/r. A bare host/org/repo suffix also matches. |
repo |
string | — | Local absolute path of the git main repository. Any linked worktree and any subdirectory of it matches (worktrees are traced back via the .git gitdir pointer). |
path |
string | — | Cwd prefix: the session workspace equals this directory or lives underneath it. |
text |
string | required | The prompt text to inject. |
mode |
section | inject |
section |
Injection mode, see below. |
sectionName |
string | project-prompt (auto-increments when several section rules match one session) |
System-prompt section name. |
order |
number | 50 |
System-prompt section order (DSH conventions: 0 persona, 100–199 tool guidance). |
A rule needs at least one of remote / repo / path; several matching rules all apply.
Injection modes
section (default) |
inject |
|
|---|---|---|
| Lands as | System-prompt segment on the agent-scoped systemPrompt service |
First user-side context message (same path AGENTS.md content takes) |
| Visible | Every request of the session | Every request of the session |
| Template interpolation | Yes — {{cwd}} and {{model}} resolve; any other complete {{...}} group throws |
No — braces are literal |
| Best for | Stable instructions; KV-cache-friendly | Text containing literal {{...}} (Helm/Go templates, Terraform, etc.) |
The plugin validates section text at load time: an unknown {{var}} fails the load with a message telling you to switch that rule to mode: inject.
How it works
- DSH emits
agent/session-startsynchronously before the first model request of every new session (fresh, resumed, after clear/compact — each publish mints a new agent scope). - The plugin reads
agent.session.header.cwdand tests each rule; git repository identity is resolved by walking up from the cwd to.git(a file means a linked worktree, whosegitdir:pointer leads back to the main repository root and itsremoteURLs). - On match, the rule's text is registered on
agent.ctx— the agent-scoped context — so it applies to that agent only and is discarded with it. - Every model request of that session re-assembles the system prompt (or re-reads the injected message), so the rule text is present throughout.
"Model-visible means logged": injected sections appear in the request headers recorded in the session log ($DSH_HOME/sessions/…), which is also how you verify a rule landed.
Verify an installation
- Check the row composed:
dsh --profile web --dump-config | grep -A3 project-prompt. - Start a session in a matched directory and ask the model a question the rule text should influence.
- Or inspect the recorded prompt: the session's
session.jsonl.zstdcontains the assembled system prompt with your section.
Limitations
- Matching is identity-by-path or by-remote, not by content: two different clones of the same remote both match a
remoterule (that is usually the point). - Submodule directories (
.gitpointing into.git/modules/…) are not traced; use apathrule for them. - Rules are static configuration — this plugin intentionally does not read files from inside the repository (that would reintroduce shared state). For workspace-local files, see DSH's built-in
AGENTS.md/CLAUDE.mdloading.
Development
git clone github:imroc/dsh-project-prompt && cd dsh-project-prompt
npm test # node --test; git fixtures are created in a temp dir
node --check index.js
The plugin is a single dependency-free ESM file; the committed source is the shipped artifact (no build, no prepare script — which is also why git installs need no pnpm allowBuilds entry). See AGENTS.md for the constraints that keep it that way.
nexu-io/open-design
freestylefly/awesome-gpt-image-2
anywhere-labs/dsh-desktop
walkinglabs/learn-harness-engineering
awesome-dsh-plugin/awesome-dsh-plugin
MemTensor/MemOS