opencode-orchestrator-uni 0.5.0 → 0.5.2
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/README.md +244 -508
- package/README.ru.md +247 -519
- package/agents/en/orchestrator.md +42 -5
- package/agents/ru/orchestrator.md +42 -5
- package/docs/en/call-mechanics.md +6 -4
- package/docs/en/instruction-other-session.md +6 -6
- package/docs/en/reference.md +482 -0
- package/docs/en/root-runbook.md +1 -1
- package/docs/en/session-discipline.md +1 -1
- package/docs/ru/call-mechanics.md +6 -4
- package/docs/ru/instruction-other-session.md +6 -6
- package/docs/ru/reference.md +482 -0
- package/docs/ru/root-runbook.md +1 -1
- package/docs/ru/session-discipline.md +1 -1
- package/index.ts +17 -1
- package/lib/write-guard.ts +283 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,577 +1,313 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
1
|
+
<a id="readme-top"></a>
|
|
2
|
+
|
|
3
|
+
[![npm version][npm-shield]][npm-url]
|
|
4
|
+
[![CI][ci-shield]][ci-url]
|
|
5
|
+
[![License][license-shield]][license-url]
|
|
6
|
+
[![OpenCode V2][opencode-shield]][opencode-url]
|
|
7
|
+
|
|
8
|
+
<br />
|
|
9
|
+
|
|
10
|
+
<div align="center">
|
|
11
|
+
<h3 align="center">opencode-orchestrator-uni</h3>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
An OpenCode V2 plugin that runs coding work as an evidence-driven audit loop.
|
|
15
|
+
<br />
|
|
16
|
+
<a href="https://github.com/OniVe/opencode-orchestrator-uni/tree/main/docs/en"><strong>Explore the docs »</strong></a>
|
|
17
|
+
<br />
|
|
18
|
+
<br />
|
|
19
|
+
<a href="https://github.com/OniVe/opencode-orchestrator-uni/issues/new">Report Bug</a>
|
|
20
|
+
·
|
|
21
|
+
<a href="https://github.com/OniVe/opencode-orchestrator-uni/issues/new">Request Feature</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<p align="center">
|
|
25
|
+
<strong>English</strong> · <a href="https://github.com/OniVe/opencode-orchestrator-uni/blob/main/README.ru.md">Русский</a>
|
|
26
|
+
</p>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<details>
|
|
30
|
+
<summary>Table of Contents</summary>
|
|
31
|
+
<ol>
|
|
32
|
+
<li><a href="#about-the-project">About The Project</a>
|
|
33
|
+
<ul>
|
|
34
|
+
<li><a href="#what-you-get">What You Get</a></li>
|
|
35
|
+
<li><a href="#built-with">Built With</a></li>
|
|
36
|
+
</ul>
|
|
37
|
+
</li>
|
|
38
|
+
<li><a href="#getting-started">Getting Started</a>
|
|
39
|
+
<ul>
|
|
40
|
+
<li><a href="#prerequisites">Prerequisites</a></li>
|
|
41
|
+
<li><a href="#installation">Installation</a></li>
|
|
42
|
+
<li><a href="#configure">Configure</a></li>
|
|
43
|
+
</ul>
|
|
44
|
+
</li>
|
|
45
|
+
<li><a href="#usage">Usage</a>
|
|
46
|
+
<ul>
|
|
47
|
+
<li><a href="#quick-start">Quick Start</a></li>
|
|
48
|
+
<li><a href="#the-audit-loop">The Audit Loop</a></li>
|
|
49
|
+
<li><a href="#agents">Agents</a></li>
|
|
50
|
+
<li><a href="#commands">Commands</a></li>
|
|
51
|
+
</ul>
|
|
52
|
+
</li>
|
|
53
|
+
<li><a href="#reference">Reference</a></li>
|
|
54
|
+
<li><a href="#development">Development</a></li>
|
|
55
|
+
<li><a href="#roadmap">Roadmap</a></li>
|
|
56
|
+
<li><a href="#contributing">Contributing</a></li>
|
|
57
|
+
<li><a href="#license">License</a></li>
|
|
58
|
+
<li><a href="#contact">Contact</a></li>
|
|
59
|
+
<li><a href="#acknowledgments">Acknowledgments</a></li>
|
|
60
|
+
</ol>
|
|
61
|
+
</details>
|
|
62
|
+
|
|
63
|
+
## About The Project
|
|
64
|
+
|
|
65
|
+
Agent sessions drift: the same agent writes the code, judges the result, and
|
|
66
|
+
summarizes its own success. This plugin makes the separation explicit for
|
|
67
|
+
[OpenCode V2](https://opencode.ai/v2/docs/build/plugins). It ships the
|
|
68
|
+
**orchestrator agent kit**: a root orchestrator with a built-in audit loop,
|
|
69
|
+
read-only audit subagents, one writer, deterministic verification tools, and the
|
|
70
|
+
process documents that keep the loop honest. Plugin id: `orchestrator.uni`.
|
|
71
|
+
|
|
72
|
+
The kit is a protocol, not a sandbox. Auditors are read-only by tool permission
|
|
73
|
+
and prompt; the orchestrator delegates every code/doc/test edit; audit artifacts
|
|
74
|
+
stay in writable paths. The write guard keeps the orchestrator out of source —
|
|
75
|
+
see [Write Guard](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/docs/en/reference.md#write-guard).
|
|
76
|
+
|
|
77
|
+
### What You Get
|
|
78
|
+
|
|
79
|
+
| Layer | What ships |
|
|
64
80
|
| --- | --- |
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
81
|
+
| Agents | `orchestrator` (primary); read-only `factcheck`, `empiric`, `invariant`, `arbiter`, `meta`, `redteam`, `context-lint`; writer `fix-runner` |
|
|
82
|
+
| Tools | Five read-only tools: `orch_uni_validate_report`, `orch_uni_context_lint`, `orch_uni_snapshot`, `orch_uni_sessions`, `orch_uni_session_stats` |
|
|
83
|
+
| Commands | Three slash commands: `/agents-md`, `/spec`, `/handoff` |
|
|
84
|
+
| Reference | Process documents exposed as the `audit-kit` reference; deep details in [Reference](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/docs/en/reference.md) |
|
|
68
85
|
|
|
69
|
-
|
|
70
|
-
current session. The orchestrator then follows the usual evidence and approval rules.
|
|
86
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
71
87
|
|
|
72
|
-
|
|
88
|
+
### Built With
|
|
73
89
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
| `orch_uni_sessions` | yes | Child sessions of a session: agent, model, cost, tokens, time |
|
|
80
|
-
| `orch_uni_session_stats` | yes | Session protocol counters: prompt, compactions, tools/turn, capped outputs |
|
|
90
|
+
- [![OpenCode V2][opencode-shield]][opencode-url]
|
|
91
|
+
- [![TypeScript][typescript-shield]][typescript-url]
|
|
92
|
+
- [![Bun][bun-shield]][bun-url]
|
|
93
|
+
- [![Biome][biome-shield]][biome-url]
|
|
94
|
+
- [![semantic-release][semantic-release-shield]][semantic-release-url]
|
|
81
95
|
|
|
82
|
-
|
|
83
|
-
(`E_INPUT`, `E_NO_JSON`, …) instead of thrown tool errors, so an agent can branch on the
|
|
84
|
-
code without parsing prose. None of the five writes to the workspace, the session store
|
|
85
|
-
or the configuration.
|
|
96
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
86
97
|
|
|
87
|
-
|
|
98
|
+
## Getting Started
|
|
88
99
|
|
|
89
|
-
|
|
90
|
-
{ "text": "<report text>" } // or
|
|
91
|
-
{ "path": "reports/empiric-r1.md", // absolute, or relative to the location directory
|
|
92
|
-
"schema": "empiric/v1" } // optional: expected schema id
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Provide exactly one of `text` / `path`. The tool takes the **last** fenced ```` ```json ````
|
|
96
|
-
block that parses and carries a `schema` field, resolves the schema by that field and
|
|
97
|
-
validates the document.
|
|
100
|
+
### Prerequisites
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
- **OpenCode 2.x** with the V2 plugin API (`plugins` field). V1's loader expects
|
|
103
|
+
the legacy `server()` export and will not load this plugin.
|
|
104
|
+
- **Bun** for tests and check scripts, **Node.js 22.14+** for release tooling.
|
|
105
|
+
The plugin has no build step and no `dist/`.
|
|
106
|
+
- A git checkout for `npm run check:locales` and `npm run verify:kit`; the npm
|
|
107
|
+
package ships no `tools/` directory.
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
| ------------------- | ---------------------------------------------------------------- |
|
|
108
|
-
| `E_INPUT` | Bad call: neither/both of `text`/`path`, wrong types, unreadable file |
|
|
109
|
-
| `E_NO_JSON` | No fenced ```` ```json ```` block found |
|
|
110
|
-
| `E_PARSE` | A block exists but none parses as JSON |
|
|
111
|
-
| `E_UNKNOWN_SCHEMA` | The report names an id that is not shipped (or no id at all) |
|
|
112
|
-
| `E_SCHEMA_MISMATCH` | The report names another schema than the expected one |
|
|
113
|
-
| `E_INVALID` | The document does not satisfy its schema (see `errors`) |
|
|
114
|
-
| `E_SCHEMA_IO` | `schemas/*.json` unreadable/malformed (see `directory`, `file`) |
|
|
115
|
-
| `E_ABORTED` | The session was stopped while reading the report |
|
|
116
|
-
|
|
117
|
-
`warnings` carry the prompt rules a *structurally valid* report can still miss — things
|
|
118
|
-
JSON Schema cannot express: a missing `rc` on an `empiric` claim whose `method` is
|
|
119
|
-
`run|measure|compare_base`, `self_check` items other than 1–19, an `invariant`
|
|
120
|
-
invariant that is `violated` without a counterexample, an uncovered mandatory
|
|
121
|
-
`lines_checked` line, `meta`'s `verdict` diverging from its `gate`, and
|
|
122
|
-
`redteam`'s `closing_list`/`verdict` invariants.
|
|
123
|
-
|
|
124
|
-
The schemas ship as `schemas/*.json` and are derived **strictly** from the SGR
|
|
125
|
-
sections of `agents/*.md`, which stay the source of truth: the test suite parses every
|
|
126
|
-
agent's SGR template and fails when a prompt field or enum value is not accepted by its
|
|
127
|
-
schema. `context-lint-failure/v1` is the typed failure envelope of
|
|
128
|
-
`orch_uni_context_lint` — it has no agent template and is never a report. Schemas set
|
|
129
|
-
`additionalProperties` permissively — agents may add fields of their
|
|
130
|
-
own (`verdict_label`, `check`, …).
|
|
131
|
-
|
|
132
|
-
### `orch_uni_context_lint`
|
|
109
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
133
110
|
|
|
134
|
-
|
|
135
|
-
{ "path": "AGENTS.md" } // file or directory, relative to the location
|
|
136
|
-
{ "path": ".", "max_lines": 150, "max_tokens": 2000 }
|
|
137
|
-
{ "path": ".", "max_words": 2000 } // SPEC word budget; default 1500 (target), ceiling 2000
|
|
138
|
-
{ "path": ".", "max_file_bytes": 500000, "max_files": 100, "max_findings": 50, "max_total_findings": 500 }
|
|
139
|
-
```
|
|
111
|
+
### Installation
|
|
140
112
|
|
|
141
|
-
|
|
142
|
-
`{ schema: "context-lint/v1", ok: true, verdict, closing_list, files, summary, truncated? }`;
|
|
143
|
-
each finding carries `id`, `severity`, `kind`, `evidence`, `detail` and `fix`. Checks:
|
|
144
|
-
line/token/word budget, distinct vague phrases, duplicate lines, broken relative links,
|
|
145
|
-
placeholders, dates/versions in the first three lines, required sections and AC test
|
|
146
|
-
columns. Links and explicit targets are confined by their **real** path: a symlink or
|
|
147
|
-
junction that resolves outside the root is skipped, and such a target is refused
|
|
148
|
-
(`E_OUT_OF_ROOT`). Code fences, inline code and HTML comments are masked, so a sample is
|
|
149
|
-
never a rule: structural checks (commands, boundaries, verification) read the full mask,
|
|
150
|
-
while requirements, AC ids and non-goals read the fence mask. Link targets are
|
|
151
|
-
streamed from the line scanner one at a time (no candidate array), so memory does
|
|
152
|
-
not grow with the number of links; checks are capped per file at 50 000
|
|
153
|
-
candidates, the signal is checked per line and every 100 candidates, and the
|
|
154
|
-
link caches keep at most 5 000 entries, so an abort wins; 2001 links are checked
|
|
155
|
-
normally, 50 000 valid links still pass, and the 50 001st candidate makes the list
|
|
156
|
-
incomplete (`truncated: true` plus a MAJOR `link candidates truncated` marker in
|
|
157
|
-
`closing_list`), so a capped run can never pass. Broken
|
|
158
|
-
links have no separate per-file cap: the finding caps bound the output and report the
|
|
159
|
-
overflow. A line is scanned however long it is: the link scanner and the placeholder regex
|
|
160
|
-
are linear, so a 4100-character line is checked like any other and a broken link or
|
|
161
|
-
placeholder on it is the real finding, not a marker. Duplicate lines have no inner cap.
|
|
162
|
-
UNC/network targets are never probed. Measured (bun 1.4.2, Windows, fresh
|
|
163
|
-
process): 50 001 broken links with `max_findings: 2` → ~1.4 s and ~67 MB peak
|
|
164
|
-
RSS; 50k plain lines → ~0.14 s / ~65 MB; a 2 MB single line with links →
|
|
165
|
-
~3.1 s / ~116 MB (~41 MB without links). Masking reuses a line as-is when it
|
|
166
|
-
has no backtick and no `<pre>`/`<code>`/`<!--`, so it never copies a line
|
|
167
|
-
character by character, and a 2 MB single line stays capped at 50 000
|
|
168
|
-
candidates instead of materializing every link.
|
|
169
|
-
Limits: ≤ 2 MB per file, ≤ 500 files per scan. `max_findings` (per file) is N per
|
|
170
|
-
severity: up to N MAJOR and N MINOR/INFO are kept, and any excess is replaced by one
|
|
171
|
-
marker (MAJOR when a MAJOR was dropped, otherwise INFO); a MAJOR marker stays in
|
|
172
|
-
`closing_list`. `max_total_findings` (global, default 1000) keeps MAJOR
|
|
173
|
-
findings first; when MAJOR overflow, `max_total` findings plus one MAJOR `truncated`
|
|
174
|
-
marker are kept. A capped list sets `truncated: true` instead of hiding findings.
|
|
175
|
-
Markers are additional to `max_findings` and do not consume its budget: a capped
|
|
176
|
-
link-candidate list and a capped findings list report one marker each in the same run, so
|
|
177
|
-
`max_findings: 2` over 20 001 broken links yields 2 kept findings plus 2 markers. Cap
|
|
178
|
-
markers point at `file:1` in `evidence`.
|
|
179
|
-
Non-goals are counted as top-level items: a wrapped paragraph is one item, inline
|
|
180
|
-
`;`/`,`/`and`/`и` split only at the top level (not inside `(…)`, `[…]`, backticks of any
|
|
181
|
-
width or `<code>`), and a bullet at 0–4 spaces under the label is an item — fenced,
|
|
182
|
-
`<pre>` and deeper-indented code is not.
|
|
183
|
-
Placeholders are `<...>` (three or more dots) and `TODO`/`TBD`/`FIXME`; `<DoD>` and
|
|
184
|
-
`<name>` are markup.
|
|
185
|
-
`max_lines` is the single 200-line root budget the tool applies: the 60/200/100
|
|
186
|
-
per-level figures in [`docs/`](./docs) are authoring guidance, not separate tool limits.
|
|
187
|
-
`max_words` defaults to the 1500-word SPEC target; pass `max_words: 2000` to enforce the
|
|
188
|
-
documented ceiling instead.
|
|
189
|
-
Typed failures: `E_INPUT`, `E_NOT_FOUND`, `E_NO_FILES`, `E_IO`, `E_ABORTED`,
|
|
190
|
-
`E_LIMIT_FILE`, `E_LIMIT_FILES`, `E_OUT_OF_ROOT`.
|
|
191
|
-
A failure is a typed payload `{schema:"context-lint-failure/v1", ok:false, code, message, path?, bytes?, files?, max?}`
|
|
192
|
-
that validates only against `context-lint-failure/v1`; a report is
|
|
193
|
-
`{schema:"context-lint/v1", ok, verdict, closing_list, files, summary, truncated?}` and every file carries `lines`,
|
|
194
|
-
`words`, `chars` and `tokens_est` (tokens are estimated as Cyrillic/3 + the rest/4). A directory
|
|
195
|
-
target is scanned to depth 3, and `max_files` refuses early: the walk stops at the first file above
|
|
196
|
-
the limit. Nothing is dropped silently: a directory below the depth limit, a link or junction that
|
|
197
|
-
resolves outside the workspace root, and an unclosed fence, `<pre>` or HTML comment are INFO
|
|
198
|
-
findings, so a `pass` never hides an unchecked region. An unclosed `<code>` is a
|
|
199
|
-
line-local mask: it hides the rest of its line and is not reported (only fence,
|
|
200
|
-
`<pre>` and HTML comments span lines and get an INFO marker). An abort is observed
|
|
201
|
-
between files, every 2048 lines of a line-oriented check and at each gate, so the
|
|
202
|
-
worst-case latency is one mask pass over a file capped by `max_file_bytes` (tens of
|
|
203
|
-
milliseconds per 2 MB), not the whole scan. Commands count only inside code, a Task table
|
|
204
|
-
or a Commands section, an HTML comment such as `<!-- npm install -->` is not a command, and `make`
|
|
205
|
-
needs a target, so prose such as "Make sure …" is not a command. A scan started at the root also
|
|
206
|
-
picks up fixture trees such as `eval/`; pass an explicit path for the canonical files.
|
|
207
|
-
|
|
208
|
-
### `orch_uni_snapshot`
|
|
113
|
+
With the OpenCode CLI:
|
|
209
114
|
|
|
210
|
-
```
|
|
211
|
-
|
|
115
|
+
```sh
|
|
116
|
+
opencode plugin add opencode-orchestrator-uni
|
|
212
117
|
```
|
|
213
118
|
|
|
119
|
+
Or add it to the `plugins` array in `~/.config/opencode/opencode.json(c)`:
|
|
120
|
+
|
|
214
121
|
```jsonc
|
|
215
|
-
{
|
|
216
|
-
"
|
|
217
|
-
"
|
|
122
|
+
{
|
|
123
|
+
"$schema": "https://opencode.ai/config.json",
|
|
124
|
+
"plugins": ["opencode-orchestrator-uni"]
|
|
125
|
+
}
|
|
218
126
|
```
|
|
219
127
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
whenever one file's path or content changes.
|
|
223
|
-
- Excluded by default (always): `node_modules`, `.git`, `.ae`; `exclude` adds names,
|
|
224
|
-
whole relative paths or relative prefixes (`lab/tmp`). Symlinks are skipped.
|
|
225
|
-
- Limits: ≤ 5000 files, ≤ 50 MiB per file — `E_LIMIT_FILES` / `E_LIMIT_BYTES` (fail the
|
|
226
|
-
whole snapshot; a truncated snapshot would be a wrong aggregate). `E_NOT_FOUND` for a
|
|
227
|
-
missing path or a non-directory, `E_ABORTED` when the session is stopped.
|
|
128
|
+
Pin a version with `opencode-orchestrator-uni@0.5.1`. For model options, use the
|
|
129
|
+
object form — see [Configure](#configure) and [Models](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/docs/en/reference.md#models).
|
|
228
130
|
|
|
229
|
-
|
|
131
|
+
Restart the service after changing the config:
|
|
230
132
|
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
{ "sessionID": "ses_…", "recursive": true } // whole subtree of another session
|
|
133
|
+
```sh
|
|
134
|
+
opencode service restart
|
|
234
135
|
```
|
|
235
136
|
|
|
236
|
-
|
|
237
|
-
{ "sessionID": "ses_parent", "recursive": false, "source": "session.index",
|
|
238
|
-
"authoritative": false, "indexSize": 1, "count": 1,
|
|
239
|
-
"limitations": [ … ],
|
|
240
|
-
"sessions": [ { "sessionID": "ses_child", "parentID": "ses_parent", "agent": "explore",
|
|
241
|
-
"model": "deepseek/deepseek-flash", "variant": "default", "title": "…",
|
|
242
|
-
"cost": 0.0005, "tokens": { "input": 3491, "output": 2 },
|
|
243
|
-
"created": "2026-10-15T04:01:06.627Z", "updated": "…" } ] }
|
|
244
|
-
```
|
|
137
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
245
138
|
|
|
246
|
-
|
|
139
|
+
### Configure
|
|
247
140
|
|
|
248
|
-
|
|
249
|
-
**authoritative** and complete (`recursive` walks the subtree page by page).
|
|
250
|
-
- `session.index` — the fallback used by OpenCode 2.0.28: `ctx.session` has
|
|
251
|
-
`get/create/…` but **no `list`**, so children cannot be enumerated from the store. The
|
|
252
|
-
plugin subscribes to `session.created` / `session.deleted` events at setup and keeps
|
|
253
|
-
`child → parent` in its plugin-scoped storage; per-child details are read from
|
|
254
|
-
`ctx.session.get()` at call time. The answer is **not authoritative**; the caveats are
|
|
255
|
-
returned in `limitations` (sessions created before the plugin instance started are
|
|
256
|
-
invisible, and an event gap hides children created while the plugin was unloaded).
|
|
257
|
-
- `E_NO_API` — neither route exists. The context-scan fallback (looking for the
|
|
258
|
-
`subagent` tool parts in `ctx.session.context`) was evaluated and rejected: in-flight
|
|
259
|
-
children carry no session id in their tool part and compaction drops the history.
|
|
260
|
-
Open question: a plugin-facing session listing (or a `parentID` filter on
|
|
261
|
-
`ctx.session.context`) would remove the fallback entirely.
|
|
141
|
+
All options are optional and live on the plugin entry:
|
|
262
142
|
|
|
263
|
-
`
|
|
264
|
-
`
|
|
143
|
+
- `locale` — `"en"` or `"ru"`; full tags such as `"ru-RU"` are accepted.
|
|
144
|
+
- `model` / `agents` — default and per-agent models in
|
|
145
|
+
`"provider/model[#variant]"` or `{ providerID, model, variant }` form.
|
|
146
|
+
- `sessionGuard` — prompt-budget reminders and the compaction limit.
|
|
147
|
+
- `toolOutputGuard` — caps large `shell`/`bash`/`webfetch` output.
|
|
265
148
|
|
|
266
|
-
|
|
149
|
+
Invalid values are ignored with an `[orchestrator.uni] …` warning. Full option
|
|
150
|
+
reference: [Models](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/docs/en/reference.md#models),
|
|
151
|
+
[Locale](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/docs/en/reference.md#locale), and
|
|
152
|
+
[Session Hygiene](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/docs/en/reference.md#session-hygiene).
|
|
267
153
|
|
|
268
|
-
|
|
269
|
-
{} // counters of the calling session
|
|
270
|
-
{ "sessionID": "ses_…" } // any session on this server, not only a child
|
|
271
|
-
```
|
|
154
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
272
155
|
|
|
273
|
-
|
|
274
|
-
{ "ok": true, "sessionID": "ses_…",
|
|
275
|
-
"counters": {
|
|
276
|
-
"requests": 12, "promptTokensLast": 151204, "promptTokensMax": 161002,
|
|
277
|
-
"promptTokensMedian": 148900, "band": 1, "compactions": 1,
|
|
278
|
-
"compactionAttempts": 1, "compactionFailures": 0, "compactionSource": "event",
|
|
279
|
-
"reminders": 2, "toolCalls": 31, "outputsCapped": 1,
|
|
280
|
-
"outputCharsSaved": 18422, "errors": 0,
|
|
281
|
-
"toolCallsByTool": { "shell": 18, "bash": 6, "webfetch": 2, "read": 5 },
|
|
282
|
-
"toolOutputsCappedByTool": { "shell": 1, "bash": 0, "webfetch": 0, "read": 0 }
|
|
283
|
-
},
|
|
284
|
-
"limits": { "promptTokensSoft": 150000, "maxCompactions": 1, "maxToolOutputChars": 20000 },
|
|
285
|
-
"overBudget": true, "compactionLimitReached": true, "toolsPerTurn": 2.58,
|
|
286
|
-
"tracked": true,
|
|
287
|
-
"guard": { "available": true, "hooksRegistered": true, "sessionEnabled": true,
|
|
288
|
-
"toolOutputEnabled": true,
|
|
289
|
-
"toolOutputTools": ["shell", "bash", "webfetch"],
|
|
290
|
-
"compactionSource": "event", "eventLive": true,
|
|
291
|
-
"streamFailures": 0, "unkeyedCalls": 0 } }
|
|
292
|
-
```
|
|
156
|
+
## Usage
|
|
293
157
|
|
|
294
|
-
|
|
295
|
-
that is deliberate — the orchestrator uses the report to control every slice it runs.
|
|
296
|
-
Counters are per plugin instance, and `tracked` is `true` once any event or hook
|
|
297
|
-
observed the session; a `session.deleted` event forgets it. `band` is the highest
|
|
298
|
-
budget level reached (`0` below the soft threshold); `promptTokensMedian` is the
|
|
299
|
-
median over the last 256 requests; `toolCalls` and the per-tool counters include
|
|
300
|
-
nested Code Mode calls; `toolCallsByTool` and `toolOutputsCappedByTool` split calls
|
|
301
|
-
and capped outputs by the normalized tool name (namespace stripped, lower-case);
|
|
302
|
-
`outputCharsSaved` is a character count, not bytes, and an upper estimate (the
|
|
303
|
-
injected stub is subtracted). `compactions` counts confirmed
|
|
304
|
-
`session.compaction.ended` events only; `compactionAttempts` counts every
|
|
305
|
-
`session.compaction` hook call, so on a host with a dead or absent event stream
|
|
306
|
-
attempts keep growing while `compactions` and `compactionLimitReached` stay put (a
|
|
307
|
-
failed attempt is never a stop signal). A dead stream is reported once in
|
|
308
|
-
`guard.streamFailures`; the per-session `errors` counts hook failures only, so a
|
|
309
|
-
transport incident is not attributed to every session. Duplicate compaction events
|
|
310
|
-
are counted once only when the host supplies `event.id` or a durable position;
|
|
311
|
-
without either identity a re-delivered event may be counted again (fail-open in the
|
|
312
|
-
direction of counting). A
|
|
313
|
-
session the store does not return and this instance never observed is
|
|
314
|
-
`E_NOT_FOUND` (recognized by the typed `_tag`/`name`/`code` — `Session.NotFoundError`,
|
|
315
|
-
`SessionNotFoundError`, `NotFoundError`, `ENOENT` — with a narrow message fallback);
|
|
316
|
-
`session.get` returning `undefined` for an untracked session is the same not-found
|
|
317
|
-
answer; a transient `session.get` failure (a lock, permission, `unknown database`) is
|
|
318
|
-
`E_INTERNAL`; a known session with zero requests is a valid report with
|
|
319
|
-
`tracked: false`. Without `session.get`,
|
|
320
|
-
an untracked session is `E_NO_API`, a tracked one is `ok` with `session: null` and a
|
|
321
|
-
`limitations` entry. Codes: `E_INPUT`, `E_NOT_FOUND`, `E_INTERNAL`, `E_NO_API`,
|
|
322
|
-
`E_UNAVAILABLE` (the guard is not registered or its hooks failed), `E_ABORTED`.
|
|
323
|
-
|
|
324
|
-
## Session hygiene
|
|
325
|
-
|
|
326
|
-
The plugin implements the [`session-discipline.md`](./docs/en/session-discipline.md)
|
|
327
|
-
protocol: a session is one slice, the prompt budget is ≤150k tokens, at most one
|
|
328
|
-
compaction, tool output goes to a file, and a slice ends with a handoff.
|
|
329
|
-
|
|
330
|
-
The runtime part is three hooks (`session.context`, `session.compaction`,
|
|
331
|
-
`tool.execute.after`) plus a compaction-event subscription:
|
|
332
|
-
|
|
333
|
-
- `session.context` — estimates the prompt (system + messages) and, when it crosses
|
|
334
|
-
a budget level, adds one short reminder. The reminder is injected **once, when a
|
|
335
|
-
new level is crossed**, not on every request; within a level the text is stable,
|
|
336
|
-
so the prefix cache is not broken.
|
|
337
|
-
- compaction events — the `session.compaction` hook is registered on **every** host:
|
|
338
|
-
it counts attempts (`compactionAttempts`) and sends a tentative note to the
|
|
339
|
-
nearest request; the note names no compaction number and claims no limit. Only
|
|
340
|
-
`session.compaction.ended` confirms a compaction (deduplicated by `event.id` or
|
|
341
|
-
the durable position) and raises a confirmed note with the number and the limit;
|
|
342
|
-
`session.compaction.failed` increments `compactionFailures`, drops the tentative
|
|
343
|
-
note and keeps a confirmed note that is still waiting for delivery. If the event
|
|
344
|
-
stream ends or throws, `guard.eventLive` turns `false`,
|
|
345
|
-
`guard.compactionSource` becomes `hook` and `guard.streamFailures` becomes `1`
|
|
346
|
-
(the per-session `errors` stays `0`); the hook keeps counting attempts, but no
|
|
347
|
-
confirmed compaction and no false "limit reached" is invented on that path.
|
|
348
|
-
- `tool.execute.after` — for `shell`, `bash`, `webfetch` it caps output over
|
|
349
|
-
`maxChars`: the full text goes to a private per-process root
|
|
350
|
-
`os.tmpdir()/opencode-tool-output-<random>/` (POSIX 0700 for the root and the
|
|
351
|
-
session directory, 0600 for the file). Windows ignores mode (measured: 0666),
|
|
352
|
-
so privacy there rests on the ACL of `os.tmpdir()` and is not checked by code.
|
|
353
|
-
The model gets a head and the path; the root is created with `mkdtemp` at plugin
|
|
354
|
-
setup, so a
|
|
355
|
-
pre-created `opencode-tool-output` directory or junction is never used; on POSIX
|
|
356
|
-
the root must belong to the current uid with no group/other access, otherwise
|
|
357
|
-
the guard disables itself with a warning (fail-open: results are untouched).
|
|
358
|
-
Session directories (`s-<uuid>`) and files (`c-<uuid>.txt`, written with `wx`)
|
|
359
|
-
are random, and each `realpath` is proven inside the root. The root is removed
|
|
360
|
-
when the plugin unloads (best effort). A lone surrogate is normalized once
|
|
361
|
-
(U+FFFD), so the file, the head and the counters describe the same string.
|
|
362
|
-
Residual risk accepted: a same-user
|
|
363
|
-
attacker can still swap a session directory between `realpath` and `writeFile`
|
|
364
|
-
(Node has no `openat`). If `TMPDIR` points inside the workspace, the files land
|
|
365
|
-
there instead. `read`, `write`, `edit`, `execute` and the kit's own tools are
|
|
366
|
-
not capped, and a kit tool named without its namespace (`session_stats`) is
|
|
367
|
-
refused in the config with a warning.
|
|
368
|
-
|
|
369
|
-
`maxCompactions: 0` disables the compaction limit. The guard is fail-open: a hook
|
|
370
|
-
error neither breaks the request nor corrupts the result. Either part can be
|
|
371
|
-
disabled with its `enabled` flag:
|
|
158
|
+
### Quick Start
|
|
372
159
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
"package": "opencode-orchestrator-uni",
|
|
378
|
-
"options": {
|
|
379
|
-
"sessionGuard": { "enabled": true, "promptTokensSoft": 150000, "maxCompactions": 1 },
|
|
380
|
-
"toolOutputGuard": {
|
|
381
|
-
"enabled": true,
|
|
382
|
-
"maxChars": 20000,
|
|
383
|
-
"headChars": 4000,
|
|
384
|
-
"tools": ["shell", "bash", "webfetch"]
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
]
|
|
389
|
-
}
|
|
390
|
-
```
|
|
160
|
+
1. Install the plugin, restart OpenCode, and open a session in the project directory.
|
|
161
|
+
2. Set `model` — or leave it unset so subagents inherit the session model.
|
|
162
|
+
3. Give the orchestrator a slice. Use `/spec` before implementation, `/agents-md`
|
|
163
|
+
for an `AGENTS.md`, and `/handoff` to close the slice.
|
|
391
164
|
|
|
392
|
-
|
|
165
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
393
166
|
|
|
394
|
-
|
|
167
|
+
### The Audit Loop
|
|
395
168
|
|
|
396
|
-
|
|
397
|
-
`
|
|
169
|
+
`factcheck` marks stale work; `empiric` reproduces and measures; `invariant`
|
|
170
|
+
probes states and hostile input; `arbiter` resolves disputes. `meta` gates the
|
|
171
|
+
reports, `redteam` checks acceptance, and `fix-runner` closes the list. The
|
|
172
|
+
process documents ship as the `audit-kit` reference and live in
|
|
173
|
+
[`docs/en`](https://github.com/OniVe/opencode-orchestrator-uni/tree/main/docs/en).
|
|
398
174
|
|
|
399
|
-
|
|
400
|
-
{
|
|
401
|
-
"$schema": "https://opencode.ai/config.json",
|
|
402
|
-
"plugins": [
|
|
403
|
-
{
|
|
404
|
-
"package": "opencode-orchestrator-uni",
|
|
405
|
-
"options": {
|
|
406
|
-
// Default for every kit agent.
|
|
407
|
-
"model": "deepseek/deepseek-flash",
|
|
408
|
-
// Per-agent overrides (id -> string or expanded form).
|
|
409
|
-
"agents": {
|
|
410
|
-
"meta": "deepseek/deepseek-reasoner",
|
|
411
|
-
"fix-runner": { "providerID": "anthropic", "model": "claude-sonnet-4-5", "variant": "high" }
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
]
|
|
416
|
-
}
|
|
417
|
-
```
|
|
175
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
418
176
|
|
|
419
|
-
|
|
420
|
-
Both accept `"provider/model[#variant]"` or `{ providerID, model, variant }`.
|
|
421
|
-
- Without options, kit agents keep no model, so subagents inherit the session's model.
|
|
422
|
-
- Invalid values are ignored with a warning (`[orchestrator.uni] …`).
|
|
423
|
-
- Subagents run on their configured model; the primary `orchestrator` runs on the
|
|
424
|
-
session's model, so a model set for `orchestrator` is informational for clients
|
|
425
|
-
(as of OpenCode 2.0.28).
|
|
426
|
-
- An `agents` config entry or a local Markdown agent file that defines its own
|
|
427
|
-
model still wins — configuration is applied after package plugins.
|
|
177
|
+
### Agents
|
|
428
178
|
|
|
429
|
-
|
|
179
|
+
| Agent | Mode | Role |
|
|
180
|
+
| -------------- | ---------- | ----------------------------------------------------------------- |
|
|
181
|
+
| `orchestrator` | `primary` | Root orchestrator; delegates edits; writes audit/state artifacts |
|
|
182
|
+
| `factcheck` | `subagent` | Read-only: staleness pre-pass (already done / outdated) |
|
|
183
|
+
| `empiric` | `subagent` | Read-only: reproductions, measurements, first sources |
|
|
184
|
+
| `invariant` | `subagent` | Read-only: states, cancels, races, hostile input |
|
|
185
|
+
| `arbiter` | `subagent` | Read-only: resolves disputed findings only |
|
|
186
|
+
| `meta` | `subagent` | Read-only: meta-audit of reports (pass/fail gate) |
|
|
187
|
+
| `redteam` | `subagent` | Read-only: acceptance red team (release gates) |
|
|
188
|
+
| `context-lint` | `subagent` | Read-only: audits AGENTS.md/SPEC.md for token efficiency |
|
|
189
|
+
| `fix-runner` | `subagent` | Writer: applies the fix list and proves closure |
|
|
430
190
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
191
|
+
All agents ship without a model; definitions live in [`agents/`](https://github.com/OniVe/opencode-orchestrator-uni/tree/main/agents) and
|
|
192
|
+
are parsed at setup. "Read-only" means `edit` is denied and the prompt forbids
|
|
193
|
+
writes; `shell` stays allowed for verification (accepted risk I-M15).
|
|
434
194
|
|
|
435
|
-
|
|
436
|
-
2. auto-detection: `LC_ALL` / `LC_MESSAGES` / `LANG`, then the runtime's Intl
|
|
437
|
-
locale (the OS setting);
|
|
438
|
-
3. the default: `en`.
|
|
195
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
439
196
|
|
|
440
|
-
|
|
441
|
-
{
|
|
442
|
-
"$schema": "https://opencode.ai/config.json",
|
|
443
|
-
"plugins": [
|
|
444
|
-
{
|
|
445
|
-
"package": "opencode-orchestrator-uni",
|
|
446
|
-
"options": { "locale": "ru", "model": "deepseek/deepseek-flash" }
|
|
447
|
-
}
|
|
448
|
-
]
|
|
449
|
-
}
|
|
450
|
-
```
|
|
197
|
+
### Commands
|
|
451
198
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
199
|
+
| Command | What it does |
|
|
200
|
+
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
201
|
+
| `/agents-md` | Injects the AGENTS.md audit/draft workflow: `context-efficiency.md` template, `orch_uni_context_lint`, `context-lint`, approval before writing. |
|
|
202
|
+
| `/spec` | Injects the SPEC.md workflow: brief, clarifying questions, EARS/AC/traceability, `orch_uni_context_lint`, `context-lint`, approval before writing. |
|
|
203
|
+
| `/handoff` | Injects the slice-closing workflow: session and working-tree facts, the `session-discipline.md` handoff template, writes `docs/planning/handoff-<YYYY-MM-DD>.md`. |
|
|
456
204
|
|
|
457
|
-
|
|
205
|
+
A command is a prompt, not a writer: it injects a workflow into the current
|
|
206
|
+
session, and the orchestrator follows the usual evidence and approval rules.
|
|
458
207
|
|
|
459
|
-
|
|
460
|
-
loader expects the legacy `server()` export and will not load this plugin.
|
|
208
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
461
209
|
|
|
462
|
-
##
|
|
210
|
+
## Reference
|
|
463
211
|
|
|
464
|
-
|
|
212
|
+
The deep reference lives in [`docs/en/reference.md`](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/docs/en/reference.md):
|
|
213
|
+
write guard, all five tools with schemas and limits, session hygiene, models,
|
|
214
|
+
locale, precedence, and notes.
|
|
465
215
|
|
|
466
|
-
|
|
467
|
-
opencode plugin add opencode-orchestrator-uni
|
|
468
|
-
```
|
|
216
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
469
217
|
|
|
470
|
-
|
|
218
|
+
## Development
|
|
471
219
|
|
|
472
|
-
|
|
473
|
-
`~/.config/opencode/opencode.json(c)`:
|
|
220
|
+
There is no build. `index.ts` is the shipped entry point.
|
|
474
221
|
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
222
|
+
```sh
|
|
223
|
+
npm install
|
|
224
|
+
npm run typecheck
|
|
225
|
+
npm test
|
|
226
|
+
npm run check:locales
|
|
227
|
+
npm run verify:kit
|
|
228
|
+
npm run lint
|
|
229
|
+
npm run lint:fix
|
|
480
230
|
```
|
|
481
231
|
|
|
482
|
-
|
|
232
|
+
`npm test` scans only `./test/` and now runs **438 tests in 11 files** — 437 pass
|
|
233
|
+
and 1 skip. A bare `bun test` from the root is red by design: it also picks up
|
|
234
|
+
intentionally failing fixtures under `eval/cases/` and drafts under `lab/`; use
|
|
235
|
+
`npm test`.
|
|
483
236
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
237
|
+
`check:locales` and `verify:kit` need a git checkout — the npm package ships no
|
|
238
|
+
`tools/` (`stand: unavailable`). [Biome](https://biomejs.org)
|
|
239
|
+
([`biome.json`](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/biome.json)) covers `index.ts`, `lib/`, `test/` and `eval/*.ts`,
|
|
240
|
+
skips `lab/` and `eval/results/`, and freezes `eval/cases/**` and
|
|
241
|
+
`eval/baselines/**`; baseline records use `JSON.stringify(…, null, 2)` style, so
|
|
242
|
+
reformatting them would churn on `eval:recompute`. `lint/style/noNonNullAssertion`
|
|
243
|
+
is the one rule disabled.
|
|
490
244
|
|
|
491
|
-
|
|
245
|
+
For a live server, point a project config at the checkout:
|
|
246
|
+
`"plugins": ["file:///C:/path/to/opencode-orchestrator-uni"]`. Local plugin
|
|
247
|
+
paths must be directories; OpenCode reloads them on change.
|
|
492
248
|
|
|
493
|
-
|
|
494
|
-
{
|
|
495
|
-
"$schema": "https://opencode.ai/config.json",
|
|
496
|
-
"plugins": [
|
|
497
|
-
{
|
|
498
|
-
"package": "opencode-orchestrator-uni",
|
|
499
|
-
"options": { "model": "deepseek/deepseek-flash" }
|
|
500
|
-
}
|
|
501
|
-
]
|
|
502
|
-
}
|
|
503
|
-
```
|
|
249
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
504
250
|
|
|
505
|
-
|
|
251
|
+
## Roadmap
|
|
506
252
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
## Precedence
|
|
253
|
+
The current published release is **0.5.1**. Pre-1.0, a breaking change bumps the
|
|
254
|
+
minor version. Release notes live on [GitHub Releases][releases-url]; planned
|
|
255
|
+
work and known issues live in the [issue tracker][issues-url].
|
|
512
256
|
|
|
513
|
-
|
|
514
|
-
agent with the same id coming from your configuration — an `agents` config entry, or a
|
|
515
|
-
Markdown file under `~/.config/opencode/agents/` (or `.opencode/agents/`) — wins and the
|
|
516
|
-
plugin skips its definition. A **configured model** (see [Models](#models)) is still
|
|
517
|
-
applied to kit agents; a model defined by your own agent entry or file wins over it,
|
|
518
|
-
because configuration is applied after package plugins. The same skip-if-taken rule
|
|
519
|
-
applies to the `audit-kit` reference.
|
|
257
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
520
258
|
|
|
521
|
-
|
|
522
|
-
**remove those copies** after enabling the plugin, otherwise the stale copies keep
|
|
523
|
-
overriding the plugin-provided definitions.
|
|
259
|
+
## Contributing
|
|
524
260
|
|
|
525
|
-
|
|
261
|
+
See [`CONTRIBUTING.md`](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/CONTRIBUTING.md). Fork, create a branch
|
|
262
|
+
(`git checkout -b feature/AmazingFeature`), commit with
|
|
263
|
+
[Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`,
|
|
264
|
+
`docs:`; `commitlint` enforces the format), push, and open a pull request. `ru` is
|
|
265
|
+
the reference locale; `en` must mirror it. After editing agents or docs, run
|
|
266
|
+
`npm run check:locales`; after README edits, run `npm test` (heading and fence
|
|
267
|
+
parity).
|
|
526
268
|
|
|
527
|
-
|
|
269
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
528
270
|
|
|
529
|
-
|
|
530
|
-
npm install # installs gray-matter + dev tooling
|
|
531
|
-
npm run typecheck # tsc --noEmit
|
|
532
|
-
npm test # bun test ./test/ (hermetic: registration, tools, validator, snapshot, prompt↔schema + identifier guards)
|
|
533
|
-
npm run check:locales # bun tools/locale-check.ts (ru↔en parity of agents + docs)
|
|
534
|
-
npm run verify:kit # bun tools/kit-verify.ts (locales, tests, lint, types — the kit's stand)
|
|
535
|
-
npm run lint # biome check . (lint + format + import order; read-only)
|
|
536
|
-
npm run lint:fix # biome check --write . (applies safe fixes)
|
|
537
|
-
```
|
|
271
|
+
## License
|
|
538
272
|
|
|
539
|
-
|
|
273
|
+
Distributed under the MIT License. See [`LICENSE`](https://github.com/OniVe/opencode-orchestrator-uni/blob/main/LICENSE).
|
|
540
274
|
|
|
541
|
-
|
|
542
|
-
72 in 5 before the identifier guards). A bare `bun test` from the repository root is red by
|
|
543
|
-
design: the positional argument of `bun test` is a path substring filter, so the
|
|
544
|
-
bare command also picks up the intentionally failing fixtures under `eval/cases/`
|
|
545
|
-
and the scratch drafts under `lab/`. Use `npm test` [F5].
|
|
275
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
546
276
|
|
|
547
|
-
|
|
548
|
-
`index.ts`, `lib/`, `test/`, `eval/*.ts` — and skips `lab/` and `eval/results/` through the
|
|
549
|
-
git ignore file, plus two frozen data kinds: `eval/cases/**` (fixtures with intentional
|
|
550
|
-
defects) and `eval/baselines/**` (records generated in `JSON.stringify(…, null, 2)` style;
|
|
551
|
-
reformatting them would churn on the next `eval:recompute`). `lint/style/noNonNullAssertion`
|
|
552
|
-
is the one rule turned off — the dev contour and tests use `!` deliberately after in-code
|
|
553
|
-
checks.
|
|
277
|
+
## Contact
|
|
554
278
|
|
|
555
|
-
|
|
279
|
+
Questions, bugs and feature requests: [GitHub Issues][issues-url].
|
|
556
280
|
|
|
557
|
-
|
|
558
|
-
{
|
|
559
|
-
"$schema": "https://opencode.ai/config.json",
|
|
560
|
-
"plugins": ["file:///C:/path/to/opencode-orchestrator-uni"]
|
|
561
|
-
}
|
|
562
|
-
```
|
|
281
|
+
Project Link: [https://github.com/OniVe/opencode-orchestrator-uni][repo-url]
|
|
563
282
|
|
|
564
|
-
|
|
565
|
-
OpenCode watches them and reloads on change.
|
|
283
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
566
284
|
|
|
567
|
-
##
|
|
285
|
+
## Acknowledgments
|
|
568
286
|
|
|
569
|
-
-
|
|
570
|
-
|
|
571
|
-
-
|
|
572
|
-
-
|
|
573
|
-
|
|
287
|
+
- [Best-README-Template](https://github.com/othneildrew/Best-README-Template) — the README structure.
|
|
288
|
+
- [OpenCode V2 plugin documentation](https://opencode.ai/v2/docs/build/plugins) — the plugin API.
|
|
289
|
+
- [shields.io](https://shields.io) — the badges.
|
|
290
|
+
- [Bun](https://bun.sh), [Biome](https://biomejs.org) and
|
|
291
|
+
[semantic-release](https://semantic-release.gitbook.io/) — the toolchain.
|
|
574
292
|
|
|
575
|
-
|
|
293
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
576
294
|
|
|
577
|
-
|
|
295
|
+
[npm-shield]: https://img.shields.io/npm/v/opencode-orchestrator-uni?style=for-the-badge
|
|
296
|
+
[npm-url]: https://www.npmjs.com/package/opencode-orchestrator-uni
|
|
297
|
+
[ci-shield]: https://img.shields.io/github/actions/workflow/status/OniVe/opencode-orchestrator-uni/ci.yml?branch=main&style=for-the-badge
|
|
298
|
+
[ci-url]: https://github.com/OniVe/opencode-orchestrator-uni/actions/workflows/ci.yml
|
|
299
|
+
[license-shield]: https://img.shields.io/github/license/OniVe/opencode-orchestrator-uni?style=for-the-badge
|
|
300
|
+
[license-url]: https://github.com/OniVe/opencode-orchestrator-uni/blob/main/LICENSE
|
|
301
|
+
[opencode-shield]: https://img.shields.io/badge/OpenCode-V2-000000?style=for-the-badge
|
|
302
|
+
[opencode-url]: https://opencode.ai/v2/docs/build/plugins
|
|
303
|
+
[typescript-shield]: https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white
|
|
304
|
+
[typescript-url]: https://www.typescriptlang.org/
|
|
305
|
+
[bun-shield]: https://img.shields.io/badge/Bun-000000?style=for-the-badge&logo=bun&logoColor=white
|
|
306
|
+
[bun-url]: https://bun.sh/
|
|
307
|
+
[biome-shield]: https://img.shields.io/badge/Biome-60A5FA?style=for-the-badge&logo=biome&logoColor=white
|
|
308
|
+
[biome-url]: https://biomejs.org/
|
|
309
|
+
[semantic-release-shield]: https://img.shields.io/badge/semantic--release-E10079?style=for-the-badge
|
|
310
|
+
[semantic-release-url]: https://semantic-release.gitbook.io/
|
|
311
|
+
[repo-url]: https://github.com/OniVe/opencode-orchestrator-uni
|
|
312
|
+
[issues-url]: https://github.com/OniVe/opencode-orchestrator-uni/issues
|
|
313
|
+
[releases-url]: https://github.com/OniVe/opencode-orchestrator-uni/releases
|