@markstanden/opencode-docs-plugin 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONVENTION.md +59 -0
- package/LICENSE +21 -0
- package/README.md +41 -2
- package/package.json +40 -5
- package/src/SKILL.md +86 -0
- package/src/index.ts +1330 -0
package/CONVENTION.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Knowledge-doc convention
|
|
2
|
+
|
|
3
|
+
This plugin owns this convention: it is what the `docs` tool's `add` writes,
|
|
4
|
+
what `check` validates, and what the registered `docs` skill teaches. A
|
|
5
|
+
repository that adopts the plugin takes the convention with it.
|
|
6
|
+
|
|
7
|
+
## Where knowledge lives
|
|
8
|
+
|
|
9
|
+
Durable knowledge is one Markdown file per topic under `docs/`, grouped by area:
|
|
10
|
+
`docs/<area>/<name>.md`. The excluded areas — never scanned, never validated —
|
|
11
|
+
are `plans/` (scratch, gitignored, deleted when the work lands),
|
|
12
|
+
`requirements/` and `wiki/` (vendored mirrors).
|
|
13
|
+
|
|
14
|
+
## Front-matter
|
|
15
|
+
|
|
16
|
+
Every doc opens with a `---` front-matter block. The fields, in canonical order:
|
|
17
|
+
|
|
18
|
+
| Field | Required | Meaning |
|
|
19
|
+
| ------------ | -------- | ----------------------------------------------------------------------- |
|
|
20
|
+
| `Area` | yes | One path segment under `docs/`; the doc's group. |
|
|
21
|
+
| `Keywords` | yes | Comma-separated search terms; the second half of the doc's identity. |
|
|
22
|
+
| `Summary` | yes | One self-contained sentence: enough to decide whether to open the file. |
|
|
23
|
+
| `Status` | no | `active` (the default) or `superseded`. |
|
|
24
|
+
| `Supersedes` | no | The doc this one replaces. |
|
|
25
|
+
|
|
26
|
+
`Date` is **derived, never written**: the file's last git commit date, falling
|
|
27
|
+
back to mtime. A hand-maintained `Date` is a finding, not a field.
|
|
28
|
+
|
|
29
|
+
The tool serialises the front-matter — never copy it by hand.
|
|
30
|
+
|
|
31
|
+
## Body
|
|
32
|
+
|
|
33
|
+
- An `H1` title: the first half of the doc's identity.
|
|
34
|
+
- At least one claim section as an `H2`/`H3` heading — `Facts`, `Inferred`,
|
|
35
|
+
`Tested`, `Open questions` — so a reader knows how much to rely on each claim.
|
|
36
|
+
Group claims by section rather than mixing them.
|
|
37
|
+
|
|
38
|
+
## Identity and duplicates
|
|
39
|
+
|
|
40
|
+
- A doc's identity is its `H1` plus its `Keywords`, and it must carry a term of
|
|
41
|
+
at least `MIN_TERM_LENGTH` (3) characters. A shorter identity is too weak to
|
|
42
|
+
search and is rejected.
|
|
43
|
+
- `check` flags **near-twins**: docs in the same area whose identities overlap.
|
|
44
|
+
`add` refuses a near-twin write and returns the candidates to reconcile
|
|
45
|
+
against — edit before add, merge near-twins, split a bloated doc into
|
|
46
|
+
per-topic files.
|
|
47
|
+
|
|
48
|
+
## Lifecycle
|
|
49
|
+
|
|
50
|
+
`Status` and `Supersedes` are load-bearing, not decoration. A doc marked
|
|
51
|
+
`Status: superseded` is flagged `(superseded)` in search results and ranks below
|
|
52
|
+
an active doc on a tie; `check` flags a `Supersedes` that names no doc in the
|
|
53
|
+
tree.
|
|
54
|
+
|
|
55
|
+
## Adopting it
|
|
56
|
+
|
|
57
|
+
Run `check` to audit an existing tree, and the gate's `defined comply` before
|
|
58
|
+
proposing a commit. A missing `docs/` tree is not an error: the plugin scans
|
|
59
|
+
what is there and reports per-doc findings for what is not.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mark Standen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
1
|
-
#
|
|
1
|
+
# opencode-docs-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An [OpenCode](https://opencode.ai) V2 plugin that gives an agent a knowledge
|
|
4
|
+
tree it can actually use: a `docs` tool and a `/docs` command over a
|
|
5
|
+
repository's `docs/**/*.md`, plus the writing convention those docs follow.
|
|
6
|
+
|
|
7
|
+
- `docs` (tool) — blank search lists the 10 most recently changed docs
|
|
8
|
+
(`path — Area — date — Summary`); a search ranks docs over their
|
|
9
|
+
front-matter, path and body identifiers, falling back to the closest
|
|
10
|
+
matches rather than a dead end; `add` writes knowledge through a typed DTO;
|
|
11
|
+
`check` validates the tree against the convention.
|
|
12
|
+
- `/docs` (command) — the same core, driven from the prompt:
|
|
13
|
+
`search <text>` (bare text searches too), `add <text>`, `check`, `help`.
|
|
14
|
+
- The plugin also registers the `docs` skill, so the convention travels with
|
|
15
|
+
the tool rather than living in always-loaded prose.
|
|
16
|
+
|
|
17
|
+
Reading a matched doc stays OpenCode's built-in `read`.
|
|
18
|
+
|
|
19
|
+
## Status
|
|
20
|
+
|
|
21
|
+
Standalone extract of the docs tooling, under active development.
|
|
22
|
+
|
|
23
|
+
## Development
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install
|
|
27
|
+
npm test # node --test
|
|
28
|
+
npm run typecheck
|
|
29
|
+
npm run lint
|
|
30
|
+
defined comply # the gate: bootstrap, repair and verify
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Tests run the plugin source directly under Node's type-stripping (`noEmit`,
|
|
34
|
+
`erasableSyntaxOnly`), so the file OpenCode loads is the file under test.
|
|
35
|
+
|
|
36
|
+
`tests/fixtures/docs-corpus.json` is a hand-authored synthetic corpus the
|
|
37
|
+
ranking harness pins behaviour against — edit it directly. It holds invented
|
|
38
|
+
paths and summaries only; never snapshot a real docs tree into it.
|
|
39
|
+
|
|
40
|
+
## Licence
|
|
41
|
+
|
|
42
|
+
MIT — see [LICENSE](LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@markstanden/opencode-docs-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "OpenCode V2 plugin: a `docs` tool and `/docs` command over a repository's knowledge tree, with the writing convention it owns",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/markstanden/opencode-docs-plugin.git"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"CONVENTION.md",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=26"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "node --test 'tests/**/*.test.ts'",
|
|
24
|
+
"test:watch": "node --test --watch 'tests/**/*.test.ts'",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"lint": "eslint ."
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@opencode/plugin": "2.0.23",
|
|
30
|
+
"@opencode/schema": "2.0.23",
|
|
31
|
+
"effect": "4.0.0-rc.112"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^26.6.4",
|
|
35
|
+
"eslint": "^10.12.0",
|
|
36
|
+
"eslint-config-prettier": "^10.1.8",
|
|
37
|
+
"typescript": "^6.0.3",
|
|
38
|
+
"typescript-eslint": "^8.71.0"
|
|
39
|
+
},
|
|
40
|
+
"license": "MIT"
|
|
41
|
+
}
|
package/src/SKILL.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Docs
|
|
2
|
+
|
|
3
|
+
The repository's durable knowledge lives under `docs/`. This skill covers both
|
|
4
|
+
halves of the loop: **find** (discover with the `docs` tool, read the match) and
|
|
5
|
+
**record** (write or update a doc for the current task).
|
|
6
|
+
|
|
7
|
+
Call the `docs` tool directly at top level. It is a plugin-registered tool and
|
|
8
|
+
is **not** reachable from the `execute` (Code Mode) catalog — do not try to
|
|
9
|
+
batch it inside `execute`, and do not be misled by the unknown-tool suggestion.
|
|
10
|
+
|
|
11
|
+
## Find it
|
|
12
|
+
|
|
13
|
+
Call the `docs` tool:
|
|
14
|
+
|
|
15
|
+
- **blank `search`** — the most recently changed docs, one line per doc
|
|
16
|
+
(`path — Area — date — Summary`).
|
|
17
|
+
- **a `search`** — those docs ranked over title, path, keywords, body headings
|
|
18
|
+
and code spans, area and summary. When no doc contains every term it returns
|
|
19
|
+
the closest matches under a `Closest matches` header rather than a dead end.
|
|
20
|
+
A term must appear as a **substring** to match — a typo returns nothing, by
|
|
21
|
+
design. Terms shorter than `MIN_TERM_LENGTH` (3) are ignored unless they are
|
|
22
|
+
the whole query, so filler words cannot drag in the catalogue. The shared
|
|
23
|
+
`docs/` path prefix itself is not searched; real path segments still are.
|
|
24
|
+
- **every call** caps at `MAX_RESULTS` (10), preferring the best matches and
|
|
25
|
+
then the most recent edits, and ends with `… and N more not shown.` when
|
|
26
|
+
anything was cut.
|
|
27
|
+
- **exactly `help`** — plain text explaining the weights, the hit qualities and
|
|
28
|
+
the outcomes, instead of a search.
|
|
29
|
+
|
|
30
|
+
Then open the match with the built-in `read` tool. The tool only discovers; it
|
|
31
|
+
never returns a doc's body. `/docs [search]` runs the same search into the
|
|
32
|
+
session (`/docs search <text>` is the explicit form).
|
|
33
|
+
|
|
34
|
+
Every `docs/**/*.md` is covered except the excluded areas — scratch (`plans/`)
|
|
35
|
+
and vendored mirrors (`requirements/`, `wiki/`). `Area` comes from the path,
|
|
36
|
+
`date` from git. A doc marked `Status: superseded` carries a `(superseded)`
|
|
37
|
+
marker in results and ranks below an active doc on a tie.
|
|
38
|
+
|
|
39
|
+
## Read it
|
|
40
|
+
|
|
41
|
+
- Consult matching docs before a broad grep: they are the curated knowledge.
|
|
42
|
+
- A `Summary` is a self-contained sentence — enough to decide whether to open
|
|
43
|
+
the file.
|
|
44
|
+
|
|
45
|
+
## Record it
|
|
46
|
+
|
|
47
|
+
Write durable findings and task documentation to `docs/<area>/<title>.md` so the
|
|
48
|
+
next human or agent skips the rediscovery. The convention is this plugin's
|
|
49
|
+
`CONVENTION.md`, restated here.
|
|
50
|
+
|
|
51
|
+
Before writing:
|
|
52
|
+
|
|
53
|
+
1. **Read first, never duplicate.** List `docs/**` with the `docs` tool and read
|
|
54
|
+
the docs relevant to the task: the same `docs/<area>/` neighbourhood, keyword
|
|
55
|
+
hits on the task's entities (service names, file paths, error strings, class
|
|
56
|
+
names), or links from touched docs.
|
|
57
|
+
2. **Reconcile, then add.** Edit before add; merge near-twins; split a bloated
|
|
58
|
+
doc into per-topic files; after any split, merge or move, grep for inbound
|
|
59
|
+
references and repoint them, leaving a stub if needed.
|
|
60
|
+
|
|
61
|
+
Then record through the `docs` tool's typed `add`: metadata (`Area`, `Keywords`
|
|
62
|
+
as a list, `Summary`; optionally `Status`/`Supersedes`) plus a body carrying an
|
|
63
|
+
`H1` and the claim sections (`Facts`, `Inferred`, `Tested`, `Open questions`).
|
|
64
|
+
The tool serialises the front-matter — never copy it — and derives
|
|
65
|
+
the target (`docs/<Area>/<slug>.md`) when `path` is omitted; pass `path` to
|
|
66
|
+
update an existing doc, replacing its body wholesale. Its guardrails refuse
|
|
67
|
+
blank metadata, path traversal, a body without an `H1`, and a same-area
|
|
68
|
+
near-twin (returning the candidates to reconcile against). A plan for an
|
|
69
|
+
in-flight effort is scratch, not a doc — `docs/plans/PLAN_<effort>.md`,
|
|
70
|
+
gitignored and deleted when the work lands. `/docs add <text>` runs this record
|
|
71
|
+
loop for a fact supplied inline.
|
|
72
|
+
|
|
73
|
+
## Check it
|
|
74
|
+
|
|
75
|
+
To audit the tree instead of writing it, call the tool's `check` (`/docs check`
|
|
76
|
+
runs it into the session): it validates every doc against the convention —
|
|
77
|
+
required front-matter, an `H1`, at least one claim section, no hand-maintained
|
|
78
|
+
`Date`, an `H1`+`Keywords` identity with a term of `MIN_TERM_LENGTH` (3) or
|
|
79
|
+
longer, a `Supersedes` naming a doc in the tree, and same-area near-twins — and
|
|
80
|
+
reports findings per doc. An empty report means compliant. `add` enforces the
|
|
81
|
+
same rules at the trust boundary.
|
|
82
|
+
|
|
83
|
+
Finally, run the repo's gate (`defined comply` where `.defined.json` exists,
|
|
84
|
+
else what its README documents) before proposing a commit. Fix failures; never
|
|
85
|
+
paper over a red gate with a docs commit. Prepare the commit and **wait for
|
|
86
|
+
explicit approval** — recording findings is not commit approval. Never push.
|