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.

或使用命令行安装(适合开发者)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 remote URL (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 .git file's gitdir: pointer, so a rule written once covers worktrees created later, and their subdirectories.
  • Two injection modessection (a stable system-prompt segment, KV-cache friendly, supports {{cwd}}/{{model}}) and inject (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 the agent/session-start event, systemPrompt.section, and agent.inject extension 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.githttps://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

  1. DSH emits agent/session-start synchronously before the first model request of every new session (fresh, resumed, after clear/compact — each publish mints a new agent scope).
  2. The plugin reads agent.session.header.cwd and tests each rule; git repository identity is resolved by walking up from the cwd to .git (a file means a linked worktree, whose gitdir: pointer leads back to the main repository root and its remote URLs).
  3. 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.
  4. 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

  1. Check the row composed: dsh --profile web --dump-config | grep -A3 project-prompt.
  2. Start a session in a matched directory and ask the model a question the rule text should influence.
  3. Or inspect the recorded prompt: the session's session.jsonl.zstd contains 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 remote rule (that is usually the point).
  • Submodule directories (.git pointing into .git/modules/…) are not traced; use a path rule 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.md loading.

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.

License

MIT © roc

上一个 Prev dsh-action-outbox 下一个 Next dsh-workspace-enhance