# AGENTS.md — instructions for AI agents working in this repository You are contributing to **dsh-project-prompt**, a [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH) profile bundle: a plugin that injects private, per-project prompt rules into sessions matched by git remote / repo path / cwd prefix. It is worktree-aware and keeps all configuration on the local machine (`$DSH_HOME`), never inside the repository. This file is the project-facing companion to `README.md`: the README explains the plugin to humans, this file explains the repository to you. ## Layout ``` index.js the entire plugin (single dependency-free ESM file) cordis.patch.yml bundle layer: inserts the plugin row (id: project-prompt) package.json dsh.bundle manifest + npm metadata test/project-prompt.test.mjs node --test suite; git fixtures are created in a temp dir README.md / README.zh.md bilingual human docs (EN is the source of truth; keep zh in sync) ``` ## Hard constraints — do not relax these 1. **`index.js` imports Node builtins only** (`node:fs`, `node:path`, `node:crypto`). The plugin must also load from paths where bare `@deepseek-ai/*` specifiers cannot resolve (e.g. `$DSH_HOME/plugins/…`, when someone mounts it without installing the bundle). Never add a runtime dependency. 2. **No build step, no `prepare` script.** The committed source is the shipped artifact; git installs (`dsh plugin add github:…`) must work without a `pnpm allowBuilds` entry. Plain JavaScript only — no TypeScript, no JSX, no transpiler. 3. **No `Config` schema export.** Cordis passes the raw config through when a plugin declares no schema (verified in the vendored `fiber.ts` `resolveConfig`). Validation is hand-rolled in `apply()` and must fail **loudly at load time** — a malformed rule must never survive to per-request prompt assembly. 4. **Every side effect stays reversible.** The only registrations are `ctx.on('agent/session-start', …)` (fiber-scoped, auto-disposed on unload) and per-agent `agent.ctx.systemPrompt.section(...)` / `agent.inject(...)` (agent-scoped, discarded with the agent). Do not add timers, watchers, or session-log writes — in particular, never append custom event types to the session log (DSH rejects unknown event types when replaying after restart). ## DSH extension points used (verified against 0.1.1-rc.2) - `ctx.on('agent/session-start', ({ agent, source }) => …)` — emit-mode lifecycle event, fired synchronously before the first model request of every agent publish (fresh, resume, clear, compact). Subagents inherit the session cwd, so they match the same rules. - `agent.session.header.cwd` — validated absolute workspace path; may be `undefined` (guard it). - `agent.ctx.systemPrompt.section({ name, order, text })` — agent-scoped system-prompt segment. `text` is strict template interpolation: `{{cwd}}`/`{{model}}` resolve; any other complete `{{...}}` group throws at assembly — hence the load-time validation with a pointer to `mode: 'inject'`. - `agent.inject(message)` — queues a user message for the next step boundary. The message is hand-built (frozen `{ id: randomUUID(), role: 'user', content: [{ type: 'text', text }], source: { kind: 'plugin', plugin: 'dsh-project-prompt', form: 'instructions' } }`) because importing `createUserMessage` from `@deepseek-ai/dsh-llm` would violate constraint 1. Git identity resolution (the worktree feature) intentionally uses no child processes: walk up from cwd to `.git`; a directory means the main repo, a file containing `gitdir:
/.git/worktrees/` traces back to the main root; `remote..url` is parsed from `
/.git/config` (worktrees share it). Remote URLs are normalized (scheme, `user@`, scp `:`, trailing `.git`/slashes, case) before comparison. ## Development loop ```bash npm test # node --test; creates its own git repos/worktrees in a temp dir node --check index.js ``` - Tests must stay hermetic: no reliance on repositories outside the temp fixture, no network, no `$DSH_HOME`. - Target DSH ≥ 0.1.1-rc.2 and Node ≥ 18. When DSH's public extension points change (check `docs/` in the harness repo), update this file and the README mechanism section together. ## Conventions - English throughout the codebase, docs, and commit messages (imperative subject line, e.g. `Trace submodules back to their parent repo`). `README.zh.md` mirrors `README.md`; when you change one, change the other in the same commit. - Versioning: semver. A breaking change to the rule schema (keys, semantics) is major; a new optional key is minor; fixes are patch. - Release: bump `version` in `package.json`, update both READMEs if behavior changed, commit, tag `vX.Y.Z`, push. The bundle installs from GitHub by default; npm publishing is optional (`pnpm pack` output should contain `index.js`, `cordis.patch.yml`, both READMEs, and `LICENSE` only). ## What NOT to build here - File-reading rule sources inside the workspace (reintroduces shared state — the problem this plugin exists to avoid). - A settings UI for rules; the YAML patch file is the interface. Keep scope tight.