# Contributing

Thanks for your interest in dsh-github-router. This plugin is small,
plain-ESM, and dependency-free at runtime — the development loop is
correspondingly simple.

## Development loop

```bash
npm test
# or: node --test --test-isolation=none "test/*.test.js"
```

The tests run with Node directly, with zero network access. A mock cordis
context stands in for the DSH services, while the real
`@deepseek-ai/dsh-tools` `defineTool` validates every tool schema — so
schema mistakes fail in `test/apply.test.js` rather than at host boot.

After changing anything in `lib/`, re-run the tests and then reinstall the
plugin into a local profile:

```bash
dsh plugin --profile <profile> add link:<absolute-path-to-this-repo>
```

Restart the DSH backend to pick up host-side changes (the host composition
loads at process start).

## Offline peer resolution

The plugin's imports (`@deepseek-ai/dsh-tools`, `dsh-credentials`,
`schemastery`, `cordis`) are peer dependencies and resolve from the DSH install
in production — the profile's `node_modules/@deepseek-ai` mirrors the checkout
that ran last. To run the tests without a registry round-trip, create a local
`node_modules/@deepseek-ai` inside this checkout and junction the packages the
tests import to the framework copies of the DSH you run:

```powershell
$dsh = '<path to the DSH checkout you run>'
New-Item -ItemType Directory -Force node_modules\@deepseek-ai | Out-Null
foreach ($p in 'schemastery', 'cordis', 'dsh-tools', 'dsh-settings') {
  New-Item -ItemType Junction -Path "node_modules\@deepseek-ai\$p" -Target "$dsh\apps\cli\node_modules\@deepseek-ai\$p"
}
New-Item -ItemType Junction -Path node_modules\@deepseek-ai\dsh-credentials -Target "$dsh\packages\credentials\credentials"
```

For an npm-installed DSH the framework packages live under
`<DSH_HOME>\node_modules\@deepseek-ai\dsh\node_modules\@deepseek-ai` instead.
`schemastery` must be ≥ 3.18.3: `.volatile()`, which every settings field uses,
does not exist in 3.18.2. (`node_modules/` is git-ignored; the junction is a
local convenience only.)

## Conventions

- **Plain ESM, no build step.** No TypeScript, no bundler.
- **One module per concern** — `lib/routes/*` are transport adapters with no
  knowledge of the tools; `lib/core/*` aggregate routes; `lib/tools/*` are
  the only modules that touch `defineTool`.
- **No shell interpolation.** Every subprocess is an argv array; anything
  user-derived must pass a guard in `lib/util.js` first.
- **Lossless JSON.** Tool execute results must round-trip
  `JSON.parse(JSON.stringify(value))` unchanged — never attach enumerable
  side properties to arrays, never return `undefined`-valued fields.
- **Tests for every guard and parser.** Input guards, the JSON-island
  walker, the tunnel request head, and the TTL cache all have offline
  tests; add one whenever behavior changes.

## Security rules for contributions

This plugin runs host-side with the host token. Before adding anything,
re-read [SECURITY.md](SECURITY.md). Hard rules:

1. **No write capability, ever.** No POST/PATCH/PUT/DELETE, no push, no
   comments. If a mutating capability is ever proposed, it must be a
   separate tool behind an explicit user-facing consent surface — and it
   still needs a strong justification to exist here.
2. **No execution of remote content.** Page payloads are `JSON.parse`-only;
   never introduce `eval`/`Function` or any HTML/script interpretation.
3. **Secrets stay out of outputs.** Tokens attach only to api.github.com;
   proxy URLs in error text must be credential-stripped.
4. **Bounded everything.** Every response body, walk, list, and retry needs
   an explicit cap with a visible truncation note.

## Releasing

1. Bump `version` in `package.json`, update `CHANGELOG.md` (Keep a
   Changelog), commit.
2. `npm test` (runs via `prepublishOnly` as well).
3. `npm publish` and verify the package through
   `dsh plugin --profile <profile> add dsh-github-router` in a fresh
   profile.
