docs: accuracy sweep, architecture restructure, two ADRs, review skill
- AGENTS.md Commands: fix typecheck/build descriptions; add lint, lint:fix, test:coverage, knip, publint, hygiene (were undocumented). - Drop the bare `yarn demo` for explicit `demo:echo` + `demo:coding`; update README, examples READMEs (and document coding-agent in examples/README). - New cookbook guide: adding-a-vendored-package.md (the missing "add" half of vendor/README's update-only procedure). - architecture.md: add a table-of-contents and extract the Extension cookbook to docs/cookbook/extension-cookbook.md (link-preserving); drop the completed "restructure this document" TODO. - ADR 0009 (capability seams) + 0010 (twin LLM adapters), and a "when to write an ADR" standard in adr/README. - Add a committed dsh-code-review skill under .agents/skills, exposed to Claude Code via a tracked .claude/skills symlink (gitignore carve-out).
This commit is contained in:
@@ -47,7 +47,7 @@ Follow tool-bash's background pattern: a `run_in_background` flag returns a task
|
||||
|
||||
## Permissions / sandboxing
|
||||
|
||||
Prefer not to build policy into the tool. The seam is the `tools/execute` waterfall (veto or wrap — see the permission-gate example in docs/architecture.md), or a sandboxing implementation behind the tool's executor seam.
|
||||
Prefer not to build policy into the tool. The seam is the `tools/execute` waterfall (veto or wrap — see the permission-gate example in [extension-cookbook.md](./extension-cookbook.md)), or a sandboxing implementation behind the tool's executor seam.
|
||||
|
||||
## Tests every tool needs
|
||||
|
||||
|
||||
56
docs/cookbook/adding-a-vendored-package.md
Normal file
56
docs/cookbook/adding-a-vendored-package.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Cookbook: adding a vendored package
|
||||
|
||||
When the harness needs another upstream Cordis package (e.g. `@cordisjs/plugin-http`), it is **vendored** as pinned source under `vendor/`, not added as an npm dependency — see [ADR 0001](../adr/0001-vendor-cordis-as-source.md) for why. [vendor/README.md](../../vendor/README.md) covers *updating* an already-vendored package; this guide is the file-by-file checklist for adding a **new** one. (Verified against the existing vendored set; if it drifts, fix it here.)
|
||||
|
||||
## 1. Copy the source in
|
||||
|
||||
```
|
||||
vendor/<dir>/
|
||||
package.json # from upstream; set "private": true, keep name/exports/type
|
||||
tsconfig.json # extends ../../tsconfig.base.json (see shape below)
|
||||
src/ # the upstream src/ verbatim
|
||||
README.md LICENSE # if upstream ships them
|
||||
```
|
||||
|
||||
`tsconfig.json` mirrors the other vendored packages — `rootDir: src`, `outDir: lib`, the strictness relaxations upstream code needs, and a `references` entry for every other vendored package it imports:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src", "outDir": "lib",
|
||||
"noUncheckedIndexedAccess": false, "exactOptionalPropertyTypes": false,
|
||||
"noImplicitOverride": false, "noUnusedLocals": false, "noUnusedParameters": false
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "../cordis" }, { "path": "../cosmokit" }]
|
||||
}
|
||||
```
|
||||
|
||||
`package.json` invariants: `"private": true` (vendored packages are never published), keep upstream's `name`/`version`/`exports`/`type`, and list its cordis deps in `peerDependencies` (matching the upstream manifest). Transitive upstream deps must themselves be vendored or already present — vendoring one package often means vendoring its dependency tree (e.g. `@cordisjs/plugin-http` pulls `@cordisjs/fetch-file`).
|
||||
|
||||
## 2. Register it in the root configs
|
||||
|
||||
| File | Change |
|
||||
|---|---|
|
||||
| `tsconfig.base.json` | add `"<npm-name>": ["./vendor/<dir>/src"]` to `paths` |
|
||||
| `tsconfig.typecheck.json` | add `"<npm-name>": ["./vendor/<dir>/lib"]` — this file points at built declarations, not src. If the package's `types` entry isn't `lib/index.d.ts`, point at that built file instead (e.g. `logger-console` maps to `./vendor/logger-console/lib/shared`, matching its `"types": "lib/shared.d.ts"`). |
|
||||
| `tsconfig.build.json` | add `{ "path": "./vendor/<dir>" }` to `references` (before the `packages/*` entries) |
|
||||
| `vendor/README.md` | add a manifest table row (dir, npm name, version, upstream repo, commit SHA) and log any local modifications |
|
||||
| `scripts/publint-all.ts` | only if the vendored package is itself published from here (vendored deps normally are not — skip) |
|
||||
|
||||
Covered automatically by globs — no edits needed: root `package.json` workspaces (`vendor/*`), `tsdown.config.ts`, `vitest.config.ts`, `eslint.config.mjs`. A per-package `vendor/<dir>/tsdown.config.ts` is needed ONLY if the build shape diverges from the root default (dual ESM/CJS or multiple entries — see `vendor/schemastery` and `vendor/logger-console`).
|
||||
|
||||
## 3. Mind the manifest guard
|
||||
|
||||
`scripts/check-vendor-manifest.sh` (a pre-commit hook) fails if anything under `vendor/*/src` is staged without `vendor/README.md` also staged. Stage the manifest update alongside the source so the commit passes.
|
||||
|
||||
## 4. Verify
|
||||
|
||||
```sh
|
||||
yarn install # registers the workspace
|
||||
yarn typecheck # the base→lib path split means: run once after a fresh add
|
||||
yarn build && yarn test && yarn constraints
|
||||
```
|
||||
|
||||
Note the `tsconfig` two-map split (called out in [AGENTS.md](../../AGENTS.md) § Secrets/.env): `lint`'s type-aware rules resolve vendored packages through their built `lib/` declarations, so run `yarn typecheck` (which builds them) once after adding the package or lint reports unresolved-type errors.
|
||||
48
docs/cookbook/extension-cookbook.md
Normal file
48
docs/cookbook/extension-cookbook.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Cookbook: extension plugin shapes
|
||||
|
||||
The three plugin shapes you write against the harness extension surface, as illustrative snippets (elided imports and helper stubs — not copy-paste-complete). For the full step-by-step guides see [adding a package](./adding-a-package.md), [adding a tool](./adding-a-tool.md), and [adding an LLM adapter](./adding-an-llm-adapter.md); for the seams these hook into see [docs/architecture.md](../architecture.md).
|
||||
|
||||
## A tool plugin
|
||||
|
||||
A tool registers on `ctx.tools`. The annotated `defineTool` example (typed `execute` args, result shaping, the `run_in_background` pattern) lives in [adding-a-tool.md](./adding-a-tool.md) — that guide is the source of truth for the tool shape. Raw JSON-Schema `ToolDefinition`s are also accepted by `ctx.tools.register()` directly (that is how MCP-sourced tools arrive); `defineTool` is the typed sugar for first-party tools.
|
||||
|
||||
## A hook plugin (permission gate)
|
||||
|
||||
A hook wraps the `tools/execute` waterfall to veto or rewrite a call — the seam where sandbox, permission, and plan-mode plugins live.
|
||||
|
||||
```ts
|
||||
export const name = 'permission-gate'
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.on('tools/execute', async (exec, next) => {
|
||||
if (!(await isAllowed(exec))) {
|
||||
return {
|
||||
callId: exec.callId,
|
||||
content: [{ type: 'text', text: 'Denied by policy.' }],
|
||||
isError: true,
|
||||
}
|
||||
}
|
||||
return next()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## A UI plugin
|
||||
|
||||
A UI plugin consumes `agent/stream-chunk` and session events for rendering, and drives input back in via `agent.send()` / `agent.steer()`.
|
||||
|
||||
```ts
|
||||
export const name = 'my-ui'
|
||||
export const inject = ['agents']
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.on('agent/stream-chunk', (agent, turn, step, chunk) => {
|
||||
if (chunk.type === 'text-delta') render(chunk.text)
|
||||
})
|
||||
onUserInput(text => ctx.agents.get('main')?.send([{ type: 'text', text }]))
|
||||
}
|
||||
```
|
||||
|
||||
## Runnable wirings
|
||||
|
||||
Two complete examples load their plugin trees from `cordis.yml` with HMR: [`examples/echo-agent`](../../examples/echo-agent) (mock model + echo tool — the all-mock skeleton check, `yarn demo:echo`) and [`examples/coding-agent`](../../examples/coding-agent) (DeepSeek V4 + the bash tool suite — the real thing, `yarn demo:coding`).
|
||||
Reference in New Issue
Block a user