@vasilvestre/opencode-plain-style 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.
@@ -0,0 +1,51 @@
1
+ # PLAIN STYLE
2
+
3
+ Write all prose in plain language. Goal, from ISO 24495-1: the reader gets what
4
+ they need (relevant), finds it easily (findable), understands it easily
5
+ (understandable), and uses it easily (usable).
6
+
7
+ This applies to commit messages, code comments, READMEs and docs, PR
8
+ descriptions, and your explanations in chat. Code itself is exempt.
9
+
10
+ ## Rules
11
+
12
+ 1. Use active voice. "The parser rejects the file", not "the file is rejected".
13
+ 2. Address the reader as "you" in instructions and docs.
14
+ 3. Use present tense. "The function returns", not "the function will return".
15
+ 4. Keep sentences short: about 25 words or fewer. Split longer ones.
16
+ 5. Give one action per step. Never chain two actions with "and" in one step.
17
+ 6. Use the imperative for steps. "Run the tests", not "you should run the tests".
18
+ 7. Use "must" for requirements. Do not use "should", "may", or "might" for them.
19
+ 8. Define every term and abbreviation on first use, unless it is common in
20
+ software (for example: API, URL, JSON).
21
+ 9. Use one term per concept, and one concept per term. Do not alternate between
22
+ synonyms like "workspace" and "project" for the same thing.
23
+ 10. Do not use vague intensifiers: very, really, quite, just, simply, actually,
24
+ basically, obviously. Delete them or state the fact they hide.
25
+ 11. Prefer lists and headings over walls of text. If a paragraph has more than
26
+ four sentences, consider splitting it or making it a list.
27
+ 12. Put the most important information first: conclusion before explanation,
28
+ warning before the step it applies to.
29
+ 12. State prerequisites before the steps that need them.
30
+ 13. Give an example for every concept that is not trivial. Show input and
31
+ output when you explain a command or function.
32
+ 14. Format code, commands, flags, file names, and paths in backticks.
33
+ 15. Be concrete. Replace "handle errors appropriately" with what to do:
34
+ "log the error and return a 400 response".
35
+
36
+ ## Code comments
37
+
38
+ - Explain why the code exists, not what it does. The code shows what.
39
+ - Do not leave commented-out code. Delete it. Git keeps the history.
40
+ - Write TODO comments as `TODO(name): action — reason`, one line.
41
+
42
+ ## Commit messages
43
+
44
+ - Subject line: imperative mood, 72 characters or fewer, no trailing period.
45
+ - Say what changed and why, not how. The diff shows how.
46
+
47
+ ## What not to do
48
+
49
+ - Do not pad. If one sentence says it, write one sentence.
50
+ - Do not hedge instructions. "This usually works" is a bug report, not a step.
51
+ - Do not simplify away precision: keep error codes, names, and numbers exact.
@@ -0,0 +1,25 @@
1
+ // plain-style — OpenCode plugin.
2
+ //
3
+ // Injects the plain-language rulebook (plain-style.md, beside this file) into
4
+ // every chat's system prompt on every LLM request — primary agent and
5
+ // subagents alike — so the rules survive context compaction. Pattern cloned
6
+ // from the ponytail plugin (experimental.chat.system.transform).
7
+ //
8
+ // Registered in opencode.jsonc: { "plugin": [..., "./plugins/plain-style.mjs"] }
9
+
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ // Read once at load; a missing file throws here so startup fails loud instead
15
+ // of silently injecting nothing.
16
+ const rulebook = fs.readFileSync(
17
+ path.join(path.dirname(fileURLToPath(import.meta.url)), 'plain-style.md'),
18
+ 'utf8',
19
+ );
20
+
21
+ export default async () => ({
22
+ 'experimental.chat.system.transform': async (_input, output) => {
23
+ output.system.push(rulebook);
24
+ },
25
+ });
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vasilvestre
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 ADDED
@@ -0,0 +1,156 @@
1
+ # opencode-plain-style
2
+
3
+ Plain-language writing rules for opencode agents, injected into every system prompt.
4
+
5
+ **Measured effect: fully-compliant prose rises from 66.7% to 93.3% of runs (+26.6 points).**
6
+ See the [benchmark](bench/BENCHMARK.md) for method, raw data, and reproduction steps.
7
+
8
+ ## What it does
9
+
10
+ The plugin adds one rulebook to the system prompt on every LLM call.
11
+ The rulebook derives from ISO 24495-1 (plain language) and the Google developer documentation style guide.
12
+ It governs commit messages, code comments, READMEs, docs, PR descriptions, and chat explanations.
13
+ Code itself is exempt.
14
+
15
+ Rules include:
16
+
17
+ - Active voice, second person, present tense.
18
+ - Sentences of 25 words or fewer.
19
+ - Imperative mood for steps, one action per step.
20
+ - `must` for requirements, never hedging modal verbs.
21
+ - A banned list of vague intensifiers.
22
+ - Lists and headings over walls of text, conclusion first.
23
+
24
+ Because the injection happens per request, the rules survive context compaction.
25
+ AGENTS.md instructions, by contrast, get summarized away during compaction.
26
+
27
+ ## The rules, by example
28
+
29
+ Five of the sixteen rules, before and after.
30
+ Full rulebook: [.opencode/plugins/plain-style.md](.opencode/plugins/plain-style.md).
31
+
32
+ ```text
33
+ Rule: imperative mood for steps
34
+ Before: You should run the tests before merging.
35
+ After: Run the tests.
36
+
37
+ Rule: must for requirements, no hedging
38
+ Before: The token may need a refresh during long sessions.
39
+ After: Refresh the token. Sessions over 1 hour must use a fresh token.
40
+
41
+ Rule: no vague intensifiers
42
+ Before: A very simple, basically automatic setup.
43
+ After: A setup with two commands.
44
+
45
+ Rule: active voice
46
+ Before: The file is rejected by the parser.
47
+ After: The parser rejects the file.
48
+
49
+ Rule: one action per step
50
+ Before: Install the dependencies and then start the dev server.
51
+ After: 1. Install the dependencies. 2. Start the dev server.
52
+ ```
53
+
54
+ The pattern: every "before" hides a fact or an action. Every "after" states it.
55
+
56
+ ## Install
57
+
58
+ Add the package to the `plugin` array in `~/.config/opencode/opencode.json`:
59
+
60
+ ```json
61
+ {
62
+ "plugin": ["@vasilvestre/opencode-plain-style"]
63
+ }
64
+ ```
65
+
66
+ Restart opencode. The plugin loads at startup and works for the primary agent and every subagent.
67
+
68
+ ## How it works
69
+
70
+ The plugin registers one hook: `experimental.chat.system.transform`.
71
+ On each request it pushes the rulebook text into the system prompt array.
72
+ The rulebook is read from disk once at module load, so each request costs one array push and zero I/O.
73
+ Total plugin code: about 25 lines, no dependencies.
74
+
75
+ Note: the hook is marked experimental in opencode.
76
+ If opencode renames it, the plugin fails loudly at startup rather than injecting nothing.
77
+
78
+ ## Example output
79
+
80
+ Same blind prompt, same model, same agent: "Describe what happens when an HTTP request reaches an Express server."
81
+
82
+ Without the plugin (control), the agent writes dense prose with vague intensifiers:
83
+
84
+ ```text
85
+ Express is really just a callback you handed to http.createServer() —
86
+ app.listen(port) is sugar for http.createServer(app).listen(port).
87
+ It's middleware all the way down: routes are just middleware that
88
+ happens to match on path + method.
89
+ ```
90
+
91
+ With the plugin (treatment), the same agent writes numbered steps and adds an example:
92
+
93
+ ```text
94
+ 1. Node's HTTP server receives it. Node parses the raw bytes into req
95
+ (request) and res (response) objects, then calls your Express app
96
+ as a handler function.
97
+ 2. Express wraps the request. Express extends req and res with its own
98
+ helpers (like req.query, res.json()), then starts walking the
99
+ middleware stack.
100
+
101
+ Example:
102
+
103
+ const express = require('express');
104
+ const app = express();
105
+ app.use(express.json()); // parses JSON body
106
+ app.get('/users/:id', (req, res) => {
107
+ res.json({ id: req.params.id });
108
+ });
109
+ ```
110
+
111
+ Both excerpts are real benchmark outputs, unedited.
112
+ Full transcripts: [bench/runs/20260729-105827/](bench/runs/20260729-105827/).
113
+
114
+ ## Benchmark
115
+
116
+ A/B design: same agent, same pinned model, two config replicas identical except this plugin.
117
+
118
+ | Rule | Control | Treatment | Delta |
119
+ | ----------------------- | ------: | --------: | -----: |
120
+ | Banned-intensifier-free | 80.0% | 100% | +20.0 |
121
+ | Hedge-free | 93.3% | 100% | +6.7 |
122
+ | Sentences ≤25 words | 73.3% | 93.3% | +20.0 |
123
+ | Fully compliant | 66.7% | 93.3% | +26.6 |
124
+
125
+ 30 runs total: 5 blind probes × 3 repetitions × 2 arms, deterministic scoring, zero LLM judging.
126
+ Injection shifts probability; it does not guarantee compliance.
127
+ For a hard guarantee, add deterministic linting (for example Vale) to CI.
128
+
129
+ Full details: [bench/BENCHMARK.md](bench/BENCHMARK.md).
130
+
131
+ ## Customize the rules
132
+
133
+ The rulebook is plain markdown at `.opencode/plugins/plain-style.md`.
134
+ Fork the repo, edit the file, and point your config at the local copy:
135
+
136
+ ```json
137
+ {
138
+ "plugin": ["file:///path/to/your/fork/.opencode/plugins/plain-style.mjs"]
139
+ }
140
+ ```
141
+
142
+ ## Development
143
+
144
+ The `bench/` directory holds the full benchmark suite: harness, scorer, aggregator, raw results.
145
+
146
+ ```bash
147
+ cd bench
148
+ ./harness.sh 3 --jobs 2
149
+ node aggregate.mjs runs/<timestamp>
150
+ ```
151
+
152
+ The harness measures the machine's live opencode config, control arm versus treatment arm.
153
+
154
+ ## License
155
+
156
+ MIT.
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@vasilvestre/opencode-plain-style",
3
+ "version": "0.1.0",
4
+ "description": "Plain-language writing rules for opencode agents, injected into every system prompt. Enforces ISO 24495-1 principles on commits, comments, docs, and chat prose.",
5
+ "keywords": ["opencode-plugin", "opencode", "plain-language", "writing", "style-guide"],
6
+ "license": "MIT",
7
+ "author": {
8
+ "name": "vasilvestre",
9
+ "url": "https://github.com/vasilvestre"
10
+ },
11
+ "homepage": "https://github.com/vasilvestre/opencode-plain-style",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/vasilvestre/opencode-plain-style.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/vasilvestre/opencode-plain-style/issues"
18
+ },
19
+ "type": "module",
20
+ "main": "./.opencode/plugins/plain-style.mjs",
21
+ "exports": {
22
+ ".": "./.opencode/plugins/plain-style.mjs",
23
+ "./plugin": "./.opencode/plugins/plain-style.mjs"
24
+ },
25
+ "files": [
26
+ ".opencode/",
27
+ "LICENSE"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ }
32
+ }