opencode-rules-md 0.6.5
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/LICENSE +21 -0
- package/README.md +650 -0
- package/dist/cli.mjs +374 -0
- package/dist/src/active-rules-state.d.ts +12 -0
- package/dist/src/active-rules-state.d.ts.map +1 -0
- package/dist/src/active-rules-state.js +118 -0
- package/dist/src/active-rules-state.js.map +1 -0
- package/dist/src/cli/config.d.ts +62 -0
- package/dist/src/cli/config.d.ts.map +1 -0
- package/dist/src/cli/config.js +246 -0
- package/dist/src/cli/config.js.map +1 -0
- package/dist/src/cli/install.d.ts +21 -0
- package/dist/src/cli/install.d.ts.map +1 -0
- package/dist/src/cli/install.js +63 -0
- package/dist/src/cli/install.js.map +1 -0
- package/dist/src/cli/main.d.ts +19 -0
- package/dist/src/cli/main.d.ts.map +1 -0
- package/dist/src/cli/main.js +135 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/cli/real-fs.d.ts +26 -0
- package/dist/src/cli/real-fs.d.ts.map +1 -0
- package/dist/src/cli/real-fs.js +42 -0
- package/dist/src/cli/real-fs.js.map +1 -0
- package/dist/src/cli/status.d.ts +19 -0
- package/dist/src/cli/status.d.ts.map +1 -0
- package/dist/src/cli/status.js +58 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/debug.d.ts +3 -0
- package/dist/src/debug.d.ts.map +1 -0
- package/dist/src/debug.js +8 -0
- package/dist/src/debug.js.map +1 -0
- package/dist/src/git-branch.d.ts +2 -0
- package/dist/src/git-branch.d.ts.map +1 -0
- package/dist/src/git-branch.js +30 -0
- package/dist/src/git-branch.js.map +1 -0
- package/dist/src/index.d.ts +26 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +55 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp-tools.d.ts +8 -0
- package/dist/src/mcp-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools.js +18 -0
- package/dist/src/mcp-tools.js.map +1 -0
- package/dist/src/message-context.d.ts +55 -0
- package/dist/src/message-context.d.ts.map +1 -0
- package/dist/src/message-context.js +110 -0
- package/dist/src/message-context.js.map +1 -0
- package/dist/src/message-paths.d.ts +34 -0
- package/dist/src/message-paths.d.ts.map +1 -0
- package/dist/src/message-paths.js +112 -0
- package/dist/src/message-paths.js.map +1 -0
- package/dist/src/project-fingerprint.d.ts +2 -0
- package/dist/src/project-fingerprint.d.ts.map +1 -0
- package/dist/src/project-fingerprint.js +61 -0
- package/dist/src/project-fingerprint.js.map +1 -0
- package/dist/src/rule-discovery.d.ts +49 -0
- package/dist/src/rule-discovery.d.ts.map +1 -0
- package/dist/src/rule-discovery.js +143 -0
- package/dist/src/rule-discovery.js.map +1 -0
- package/dist/src/rule-filter.d.ts +61 -0
- package/dist/src/rule-filter.d.ts.map +1 -0
- package/dist/src/rule-filter.js +180 -0
- package/dist/src/rule-filter.js.map +1 -0
- package/dist/src/rule-metadata.d.ts +29 -0
- package/dist/src/rule-metadata.d.ts.map +1 -0
- package/dist/src/rule-metadata.js +100 -0
- package/dist/src/rule-metadata.js.map +1 -0
- package/dist/src/runtime-chat.d.ts +34 -0
- package/dist/src/runtime-chat.d.ts.map +1 -0
- package/dist/src/runtime-chat.js +39 -0
- package/dist/src/runtime-chat.js.map +1 -0
- package/dist/src/runtime-context.d.ts +25 -0
- package/dist/src/runtime-context.d.ts.map +1 -0
- package/dist/src/runtime-context.js +109 -0
- package/dist/src/runtime-context.js.map +1 -0
- package/dist/src/runtime.d.ts +31 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +238 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/session-store.d.ts +34 -0
- package/dist/src/session-store.d.ts.map +1 -0
- package/dist/src/session-store.js +90 -0
- package/dist/src/session-store.js.map +1 -0
- package/dist/src/utils.d.ts +15 -0
- package/dist/src/utils.d.ts.map +1 -0
- package/dist/src/utils.js +19 -0
- package/dist/src/utils.js.map +1 -0
- package/dist/tui/data/rules.d.ts +63 -0
- package/dist/tui/data/rules.d.ts.map +1 -0
- package/dist/tui/data/rules.js +173 -0
- package/dist/tui/data/rules.js.map +1 -0
- package/dist/tui/index.d.ts +8 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +13 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/slots/sidebar-content.d.ts +11 -0
- package/dist/tui/slots/sidebar-content.d.ts.map +1 -0
- package/dist/tui/slots/sidebar-content.js +150 -0
- package/dist/tui/slots/sidebar-content.js.map +1 -0
- package/package.json +101 -0
- package/src/active-rules-state.ts +166 -0
- package/src/cli/config.ts +312 -0
- package/src/cli/install.ts +94 -0
- package/src/cli/main.ts +167 -0
- package/src/cli/real-fs.ts +65 -0
- package/src/cli/status.ts +80 -0
- package/src/debug.ts +9 -0
- package/src/git-branch.ts +37 -0
- package/src/index.ts +68 -0
- package/src/mcp-tools.ts +24 -0
- package/src/message-context.ts +139 -0
- package/src/message-paths.ts +153 -0
- package/src/project-fingerprint.ts +72 -0
- package/src/rule-discovery.ts +205 -0
- package/src/rule-filter.ts +269 -0
- package/src/rule-metadata.ts +161 -0
- package/src/runtime-chat.ts +68 -0
- package/src/runtime-context.ts +136 -0
- package/src/runtime.ts +369 -0
- package/src/session-store.ts +129 -0
- package/src/utils.ts +36 -0
- package/tui/.atl/.skill-registry.cache.json +3 -0
- package/tui/.atl/skill-registry.md +57 -0
- package/tui/data/rules.ts +230 -0
- package/tui/index.tsx +23 -0
- package/tui/slots/sidebar-content.tsx +364 -0
- package/tui/types/opencode-plugin-tui.d.ts +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Joe Maples <joe@maples.dev>
|
|
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,650 @@
|
|
|
1
|
+
# opencode-rules-md
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/opencode-rules-md)
|
|
4
|
+
[](https://www.npmjs.com/package/opencode-rules-md)
|
|
5
|
+
|
|
6
|
+
A lightweight OpenCode plugin that discovers and injects markdown rule files into AI agent system prompts, enabling flexible behavior customization without per-project configuration.
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
opencode-rules-md automatically loads rule files from standard directories and integrates them into AI agent prompts, allowing you to:
|
|
11
|
+
|
|
12
|
+
- Define global coding standards that apply across all projects
|
|
13
|
+
- Create project-specific rules for team collaboration
|
|
14
|
+
- Apply conditional rules based on file patterns, prompt keywords, available tools, model, agent, branch, OS, CI, and more
|
|
15
|
+
- Control matching behavior with `match: any` (default) or `match: all`
|
|
16
|
+
- Maintain zero-configuration workflow with sensible defaults
|
|
17
|
+
|
|
18
|
+
This approach allows you to dynamically include rules automatically like style guides for specific languages,
|
|
19
|
+
guidance on specific actions, etc. Unlike skills, which are called on by the agent, rules use a simple matching
|
|
20
|
+
approach.
|
|
21
|
+
|
|
22
|
+
> [!NOTE]
|
|
23
|
+
> The name `opencode-rules-md` is to be concise about what this plugin does. It is in no way affiliated with Anomaly Co. or
|
|
24
|
+
> the official OpenCode project.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Dual-format support**: Load rules from both `.md` and `.mdc` files
|
|
29
|
+
- **Conditional rules**: Apply rules based on file paths, prompt keywords, or available tools
|
|
30
|
+
- **Runtime filtering**: Filter rules by model, agent, command, project type, git branch, OS, and CI
|
|
31
|
+
- **Branch glob patterns**: Match branches using glob patterns (e.g., `feature/*`, `release/**`)
|
|
32
|
+
- **Matching modes**: Use `match: any` (default) for OR logic or `match: all` for AND logic
|
|
33
|
+
- **Keyword matching**: Apply rules when the user's prompt contains specific keywords
|
|
34
|
+
- **Tool-based rules**: Apply rules only when specific MCP tools are available
|
|
35
|
+
- **Global and project-level rules**: Define rules at both system and project scopes
|
|
36
|
+
- **Context-aware injection**: Rules filtered by extracted file paths and user prompts
|
|
37
|
+
- **Zero-configuration**: Works out of the box with XDG Base Directory specification
|
|
38
|
+
- **TypeScript-first**: Built with TypeScript for type safety and developer experience
|
|
39
|
+
- **Performance optimized**: Efficient file discovery and minimal startup overhead
|
|
40
|
+
- **TUI sidebar**: Real-time sidebar in the OpenCode TUI showing rule status with active/inactive indicators
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
opencode plugin opencode-rules-md@latest --global
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
<details>
|
|
51
|
+
<summary>Manual installation</summary>
|
|
52
|
+
|
|
53
|
+
Add the plugin to your opencode config:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"$schema": "https://opencode.ai/config.json",
|
|
58
|
+
"plugin": ["opencode-rules-md@latest"]
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
To enable the TUI sidebar, add the same plugin entry to your TUI config:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
// ~/.config/opencode/tui.json
|
|
66
|
+
{
|
|
67
|
+
"plugin": ["opencode-rules-md@latest"]
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
</details>
|
|
72
|
+
|
|
73
|
+
### Create Your First Rule
|
|
74
|
+
|
|
75
|
+
1. Create the global rules directory:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
mkdir -p ~/.config/opencode/rules
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
2. Add a simple rule file:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
cat > ~/.config/opencode/rules/coding-standards.md << 'EOF'
|
|
85
|
+
# Coding Standards
|
|
86
|
+
|
|
87
|
+
- Use meaningful variable names
|
|
88
|
+
- Follow the project's code style guide
|
|
89
|
+
- Write self-documenting code
|
|
90
|
+
EOF
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
That's it! The rule will now be automatically injected into all AI agent prompts.
|
|
94
|
+
|
|
95
|
+
## How It Works
|
|
96
|
+
|
|
97
|
+
1. **Discovery**: Scan global and project directories for `.md` and `.mdc` files (at plugin init)
|
|
98
|
+
2. **Parsing**: Extract metadata from files with YAML front matter
|
|
99
|
+
3. **Tool Execution**: `tool.execute.before` hook captures file paths before tools run
|
|
100
|
+
4. **Message Flow**: `chat.message` hook updates user prompt as messages arrive
|
|
101
|
+
5. **Initial Seeding**: `experimental.chat.messages.transform` extracts context from message history once
|
|
102
|
+
6. **Rule Filtering**: `experimental.chat.system.transform` evaluates rules based on context and injects into system prompt
|
|
103
|
+
7. **State Persistence**: After filtering, matched rule paths are written to `~/.opencode/state/opencode-rules-md/{sessionId}.json` for TUI consumption
|
|
104
|
+
8. **Compaction Persistence**: `experimental.session.compacting` preserves context during session compression
|
|
105
|
+
|
|
106
|
+
## Performance
|
|
107
|
+
|
|
108
|
+
- Rule discovery performed once at plugin initialization
|
|
109
|
+
- Rule content cached with mtime-based invalidation for fast re-reads
|
|
110
|
+
- Incremental session state tracking (set of paths, not message rescanning)
|
|
111
|
+
- Per-session state pruned after 100 concurrent sessions to prevent memory growth
|
|
112
|
+
- Efficient glob matching with `minimatch`
|
|
113
|
+
- Tool-based path capture is non-blocking with minimal overhead
|
|
114
|
+
- Session context cleaned up when exceeded (LRU eviction)
|
|
115
|
+
- Minimal memory footprint with efficient state management
|
|
116
|
+
|
|
117
|
+
## Configuration
|
|
118
|
+
|
|
119
|
+
### Rule Discovery Locations
|
|
120
|
+
|
|
121
|
+
Rules are automatically discovered from these directories (including all subdirectories):
|
|
122
|
+
|
|
123
|
+
1. **Global rules**: `$OPENCODE_CONFIG_DIR/rules/` if set, otherwise `$XDG_CONFIG_HOME/opencode/rules/` (typically `~/.config/opencode/rules/`)
|
|
124
|
+
2. **Project rules**: `.opencode/rules/` (in your project root)
|
|
125
|
+
|
|
126
|
+
Both directories are scanned recursively, allowing you to organize rules into subdirectories.
|
|
127
|
+
|
|
128
|
+
### Supported File Formats
|
|
129
|
+
|
|
130
|
+
- `.md` - Standard markdown files with optional metadata
|
|
131
|
+
- `.mdc` - Markdown files with optional metadata
|
|
132
|
+
|
|
133
|
+
## Metadata Format
|
|
134
|
+
|
|
135
|
+
Both `.md` and `.mdc` files support optional YAML metadata for conditional rule application:
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
---
|
|
139
|
+
globs:
|
|
140
|
+
- 'src/**/*.ts'
|
|
141
|
+
- 'lib/**/*.js'
|
|
142
|
+
keywords:
|
|
143
|
+
- 'refactoring'
|
|
144
|
+
- 'cleanup'
|
|
145
|
+
tools:
|
|
146
|
+
- 'mcp_websearch'
|
|
147
|
+
- 'mcp_lsp'
|
|
148
|
+
model:
|
|
149
|
+
- gpt-5.3-codex
|
|
150
|
+
- claude-sonnet-4
|
|
151
|
+
agent:
|
|
152
|
+
- programmer
|
|
153
|
+
command:
|
|
154
|
+
- /plan
|
|
155
|
+
- /review
|
|
156
|
+
project:
|
|
157
|
+
- node
|
|
158
|
+
- monorepo
|
|
159
|
+
branch:
|
|
160
|
+
- main
|
|
161
|
+
- feature/*
|
|
162
|
+
os:
|
|
163
|
+
- linux
|
|
164
|
+
- darwin
|
|
165
|
+
ci: false
|
|
166
|
+
# Matching mode
|
|
167
|
+
match: any
|
|
168
|
+
---
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Supported Fields
|
|
172
|
+
|
|
173
|
+
- `globs` (optional): Array of glob patterns for file-based matching
|
|
174
|
+
- Rule applies when any file in context matches a pattern
|
|
175
|
+
- `keywords` (optional): Array of keywords for prompt-based matching
|
|
176
|
+
- Rule applies when the user's prompt contains any keyword
|
|
177
|
+
- Case-insensitive, word-boundary matching (e.g., "test" matches "testing")
|
|
178
|
+
- Does NOT match mid-word (e.g., "test" does NOT match "contest")
|
|
179
|
+
- `tools` (optional): Array of tool IDs for tool-availability matching
|
|
180
|
+
- Rule applies when any listed tool is available to the agent
|
|
181
|
+
- Uses exact string matching against tool IDs (e.g., `mcp_websearch`, `mcp_bash`)
|
|
182
|
+
- Enable debug logging (`OPENCODE_RULES_DEBUG=1`) to see available tool IDs
|
|
183
|
+
- `model` (optional): Array of model IDs to match against the current LLM
|
|
184
|
+
- Example: `['gpt-5.3-codex', 'claude-sonnet-4']`
|
|
185
|
+
- `agent` (optional): Array of agent types to match
|
|
186
|
+
- Example: `['programmer', 'planner']`
|
|
187
|
+
- `command` (optional): Array of slash commands to match
|
|
188
|
+
- Example: `['/plan', '/review']`
|
|
189
|
+
- `project` (optional): Array of project type tags to match
|
|
190
|
+
- Detected automatically from marker files (e.g., `package.json` -> `node`)
|
|
191
|
+
- Supported tags: `node`, `python`, `go`, `rust`, `monorepo`, `browser-extension`
|
|
192
|
+
- `branch` (optional): Array of git branch patterns to match
|
|
193
|
+
- Supports exact names and glob patterns (e.g., `feature/*`, `release/**`)
|
|
194
|
+
- Uses minimatch for glob matching
|
|
195
|
+
- `os` (optional): Array of operating systems to match
|
|
196
|
+
- Values: `linux`, `darwin`, `win32`
|
|
197
|
+
- `ci` (optional): Boolean to match CI environment
|
|
198
|
+
- `true` matches when running in CI, `false` matches when not in CI
|
|
199
|
+
- `match` (optional): Matching mode for multiple conditions
|
|
200
|
+
- `any` (default): Rule applies if ANY declared condition matches
|
|
201
|
+
- `all`: Rule applies only if ALL declared conditions match
|
|
202
|
+
|
|
203
|
+
> [!NOTE]
|
|
204
|
+
> When a runtime context value is unavailable (e.g., not in a git repository), that dimension is treated as a non-match.
|
|
205
|
+
|
|
206
|
+
### Matching Behavior
|
|
207
|
+
|
|
208
|
+
- **No metadata**: Rule applies unconditionally (always included)
|
|
209
|
+
- **Only globs**: Rule applies when any context file matches
|
|
210
|
+
- **Only keywords**: Rule applies when the user's prompt contains any keyword
|
|
211
|
+
- **Only tools**: Rule applies when any listed tool is available
|
|
212
|
+
- **Multiple conditions with `match: any` (default)**: Rule applies when ANY condition matches (OR logic across all fields)
|
|
213
|
+
- **Multiple conditions with `match: all`**: Rule applies only when ALL declared conditions match
|
|
214
|
+
|
|
215
|
+
## Glob Pattern Reference
|
|
216
|
+
|
|
217
|
+
The plugin uses `minimatch` for pattern matching:
|
|
218
|
+
|
|
219
|
+
| Pattern | Matches |
|
|
220
|
+
| ----------------------------- | ----------------------------------------------- |
|
|
221
|
+
| `src/**/*.ts` | All TypeScript files in src and subdirectories |
|
|
222
|
+
| `**/*.test.ts` | All test files at any depth |
|
|
223
|
+
| `src/components/**/*.tsx` | React components in components directory |
|
|
224
|
+
| `*.json` | JSON files in root directory only |
|
|
225
|
+
| `lib/{utils,helpers}/**/*.js` | JavaScript files in specific lib subdirectories |
|
|
226
|
+
|
|
227
|
+
## Included Skill: crafting-rules
|
|
228
|
+
|
|
229
|
+
This repository includes a `crafting-rules/` skill that teaches AI agents how to create well-formatted rules. The skill provides:
|
|
230
|
+
|
|
231
|
+
- **Rule format reference** - Frontmatter fields (`globs`, `keywords`, `tools`, `model`, `agent`, `command`, `project`, `branch`, `os`, `ci`, `match`) and markdown body structure
|
|
232
|
+
- **Matching strategy guidance** - When to use globs vs keywords vs runtime filters vs combinations
|
|
233
|
+
- **Pattern extraction workflow** - How to identify repeated conversation patterns that should become rules
|
|
234
|
+
- **Keyword safety guidelines** - Denylist of overly broad keywords to avoid, allowlist of safe alternatives, and an audit checklist
|
|
235
|
+
|
|
236
|
+
To use the skill, copy `skills/crafting-rules/` to `~/.config/opencode/skills/` or reference it directly. The skill triggers when users ask to create rules, codify preferences, or persist guidance across sessions.
|
|
237
|
+
|
|
238
|
+
## Usage Examples
|
|
239
|
+
|
|
240
|
+
For real-world examples, see the [`.opencode/rules/`](.opencode/rules/) directory in this repository.
|
|
241
|
+
|
|
242
|
+
### Basic Rule File
|
|
243
|
+
|
|
244
|
+
Create `~/.config/opencode/rules/naming-convention.md`:
|
|
245
|
+
|
|
246
|
+
```markdown
|
|
247
|
+
# Naming Convention Rules
|
|
248
|
+
|
|
249
|
+
- Use camelCase for variables and functions
|
|
250
|
+
- Use PascalCase for classes and interfaces
|
|
251
|
+
- Use UPPER_SNAKE_CASE for constants
|
|
252
|
+
- Prefix private properties with underscore
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Conditional Rule with Metadata
|
|
256
|
+
|
|
257
|
+
Create `~/.config/opencode/rules/typescript.mdc`:
|
|
258
|
+
|
|
259
|
+
```markdown
|
|
260
|
+
---
|
|
261
|
+
globs:
|
|
262
|
+
- '**/*.ts'
|
|
263
|
+
- '**/*.tsx'
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
# TypeScript Best Practices
|
|
267
|
+
|
|
268
|
+
- Always use `const` and `let`, avoid `var`
|
|
269
|
+
- Use interfaces for object types
|
|
270
|
+
- Add type annotations for function parameters
|
|
271
|
+
- Avoid `any` type without justification
|
|
272
|
+
- Enable strict mode in tsconfig.json
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This rule only applies when processing TypeScript files.
|
|
276
|
+
|
|
277
|
+
### Keyword-Based Rule
|
|
278
|
+
|
|
279
|
+
Create `~/.config/opencode/rules/testing.mdc`:
|
|
280
|
+
|
|
281
|
+
```markdown
|
|
282
|
+
---
|
|
283
|
+
keywords:
|
|
284
|
+
- 'testing'
|
|
285
|
+
- 'unit test'
|
|
286
|
+
- 'jest'
|
|
287
|
+
- 'vitest'
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
# Testing Best Practices
|
|
291
|
+
|
|
292
|
+
- Write tests before implementing features (TDD)
|
|
293
|
+
- Use descriptive test names that explain the expected behavior
|
|
294
|
+
- Mock external dependencies
|
|
295
|
+
- Aim for high test coverage on critical paths
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
This rule applies when the user's prompt mentions testing-related terms.
|
|
299
|
+
|
|
300
|
+
### Tool-Based Rule
|
|
301
|
+
|
|
302
|
+
Create `~/.config/opencode/rules/websearch.mdc`:
|
|
303
|
+
|
|
304
|
+
```markdown
|
|
305
|
+
---
|
|
306
|
+
tools:
|
|
307
|
+
- 'mcp_websearch'
|
|
308
|
+
- 'mcp_codesearch'
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
# Web Search Best Practices
|
|
312
|
+
|
|
313
|
+
- Always verify search results with multiple sources
|
|
314
|
+
- Prefer official documentation over third-party tutorials
|
|
315
|
+
- Check publication dates for time-sensitive information
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
This rule only applies when the websearch or codesearch MCP tools are available.
|
|
319
|
+
|
|
320
|
+
NOTE: Due to limitations on how opencode provides tools via the SDK, individual
|
|
321
|
+
MCP tools cannot be matched. Only built-in tools, plugin tools, and whole MCPs
|
|
322
|
+
can be matched.
|
|
323
|
+
|
|
324
|
+
### Combined Globs and Keywords Rule
|
|
325
|
+
|
|
326
|
+
Create `~/.config/opencode/rules/test-files.mdc`:
|
|
327
|
+
|
|
328
|
+
```markdown
|
|
329
|
+
---
|
|
330
|
+
globs:
|
|
331
|
+
- '**/*.test.ts'
|
|
332
|
+
- '**/*.spec.ts'
|
|
333
|
+
keywords:
|
|
334
|
+
- 'testing'
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
# Test File Standards
|
|
338
|
+
|
|
339
|
+
- Use `describe` blocks to group related tests
|
|
340
|
+
- Use `it` or `test` with clear descriptions
|
|
341
|
+
- Follow AAA pattern: Arrange, Act, Assert
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
This rule applies when EITHER a test file is in context OR the user mentions testing (OR logic).
|
|
345
|
+
|
|
346
|
+
### Combined Tools with Other Conditions
|
|
347
|
+
|
|
348
|
+
Create `~/.config/opencode/rules/lsp-typescript.mdc`:
|
|
349
|
+
|
|
350
|
+
```markdown
|
|
351
|
+
---
|
|
352
|
+
tools:
|
|
353
|
+
- 'mcp_lsp'
|
|
354
|
+
globs:
|
|
355
|
+
- '**/*.ts'
|
|
356
|
+
keywords:
|
|
357
|
+
- 'type checking'
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
# LSP-Enabled TypeScript Development
|
|
361
|
+
|
|
362
|
+
- Use LSP hover to check inferred types
|
|
363
|
+
- Navigate to definitions using goToDefinition
|
|
364
|
+
- Find all references before refactoring
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
This rule applies when the LSP tool is available OR TypeScript files are in context OR the user mentions type checking.
|
|
368
|
+
|
|
369
|
+
### Runtime Environment Filtering
|
|
370
|
+
|
|
371
|
+
Create `~/.config/opencode/rules/feature-branch-dev.mdc`:
|
|
372
|
+
|
|
373
|
+
```markdown
|
|
374
|
+
---
|
|
375
|
+
model:
|
|
376
|
+
- gpt-5.3-codex
|
|
377
|
+
- claude-sonnet-4
|
|
378
|
+
agent:
|
|
379
|
+
- programmer
|
|
380
|
+
branch:
|
|
381
|
+
- feature/*
|
|
382
|
+
os:
|
|
383
|
+
- linux
|
|
384
|
+
- darwin
|
|
385
|
+
ci: false
|
|
386
|
+
match: all
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
# Feature Branch Development
|
|
390
|
+
|
|
391
|
+
When working on feature branches locally:
|
|
392
|
+
|
|
393
|
+
- Create atomic commits with clear messages
|
|
394
|
+
- Run tests before pushing
|
|
395
|
+
- Keep changes focused and reviewable
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
This rule uses `match: all` and only applies when ALL conditions are met: specific model, programmer agent, feature branch, Unix OS, and not in CI.
|
|
399
|
+
|
|
400
|
+
### Organized Rules with Subdirectories
|
|
401
|
+
|
|
402
|
+
You can organize rules into subdirectories for better management. Rules are discovered recursively from all subdirectories:
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
~/.config/opencode/rules/
|
|
406
|
+
├── coding-standards.md # Always applied
|
|
407
|
+
├── typescript/
|
|
408
|
+
│ ├── general.md # TypeScript general rules
|
|
409
|
+
│ └── react.mdc # React-specific rules (conditional)
|
|
410
|
+
├── testing/
|
|
411
|
+
│ └── vitest.md # Testing guidelines
|
|
412
|
+
└── security/
|
|
413
|
+
└── api-keys.md # Security rules
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Hidden directories (starting with `.`) are automatically excluded from discovery.
|
|
417
|
+
|
|
418
|
+
### Project-Specific Rules
|
|
419
|
+
|
|
420
|
+
Create `.opencode/rules/react-components.mdc` in your project:
|
|
421
|
+
|
|
422
|
+
```markdown
|
|
423
|
+
---
|
|
424
|
+
globs:
|
|
425
|
+
- 'src/components/**/*.tsx'
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
# React Component Guidelines
|
|
429
|
+
|
|
430
|
+
- Use functional components with hooks
|
|
431
|
+
- Export components as named exports
|
|
432
|
+
- Include PropTypes or TypeScript interfaces
|
|
433
|
+
- Use React.memo for expensive components
|
|
434
|
+
- Co-locate styles with components
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Development
|
|
438
|
+
|
|
439
|
+
### Project Structure (Abridged)
|
|
440
|
+
|
|
441
|
+
The following shows the key source modules. Additional test files (`*.test.ts`) and type-checking utilities exist but are omitted for brevity.
|
|
442
|
+
|
|
443
|
+
```
|
|
444
|
+
opencode-rules-md/
|
|
445
|
+
├── src/
|
|
446
|
+
│ ├── index.ts # Main plugin entry point and exports
|
|
447
|
+
│ ├── runtime.ts # OpenCodeRulesRuntime class (hook orchestration)
|
|
448
|
+
│ ├── runtime-context.ts # Context-building helpers (filter context, project detection)
|
|
449
|
+
│ ├── runtime-chat.ts # Chat message handling and text extraction
|
|
450
|
+
│ ├── rule-discovery.ts # Rule file scanning and discovery
|
|
451
|
+
│ ├── rule-metadata.ts # YAML frontmatter parsing
|
|
452
|
+
│ ├── rule-filter.ts # Rule filtering logic (globs, keywords, tools, runtime)
|
|
453
|
+
│ ├── message-paths.ts # Path extraction from messages
|
|
454
|
+
│ ├── message-context.ts # User prompt extraction from message parts
|
|
455
|
+
│ ├── session-store.ts # Per-session state management
|
|
456
|
+
│ ├── project-fingerprint.ts # Project type detection (Node.js, Python, etc.)
|
|
457
|
+
│ ├── mcp-tools.ts # MCP tool ID extraction
|
|
458
|
+
│ ├── git-branch.ts # Git branch detection
|
|
459
|
+
│ ├── active-rules-state.ts # Persists matched rules per session for TUI
|
|
460
|
+
│ ├── debug.ts # Debug logging utilities
|
|
461
|
+
│ ├── utils.ts # Re-export facade for backwards compatibility
|
|
462
|
+
│ ├── test-fixtures.ts # Shared test fixtures and builders
|
|
463
|
+
│ └── *.test.ts # Unit/integration tests (11 test files)
|
|
464
|
+
├── tui/
|
|
465
|
+
│ ├── index.tsx # TUI entrypoint, exports { id, tui }
|
|
466
|
+
│ ├── slots/
|
|
467
|
+
│ │ └── sidebar-content.tsx # Sidebar widget component
|
|
468
|
+
│ ├── data/
|
|
469
|
+
│ │ ├── rules.ts # Rule discovery + formatting for sidebar
|
|
470
|
+
│ │ └── rules.test.ts # Data layer tests
|
|
471
|
+
│ └── types/
|
|
472
|
+
│ └── opencode-plugin-tui.d.ts # Vendored type shim
|
|
473
|
+
├── docs/
|
|
474
|
+
│ └── rules.md # Detailed usage documentation
|
|
475
|
+
├── openspec/ # Project specifications and proposals
|
|
476
|
+
└── dist/ # Compiled JavaScript output
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
#### Key Module Responsibilities
|
|
480
|
+
|
|
481
|
+
The following highlights the primary runtime modules:
|
|
482
|
+
|
|
483
|
+
- **runtime.ts** - Orchestrates hooks (`tool.execute.before`, `chat.message`, `experimental.chat.*`)
|
|
484
|
+
- **runtime-context.ts** - Builds `RuleFilterContext` from session state and environment
|
|
485
|
+
- **runtime-chat.ts** - Extracts text from chat message parts for keyword matching
|
|
486
|
+
- **rule-discovery.ts** - Recursively scans directories for `.md`/`.mdc` rule files
|
|
487
|
+
- **rule-metadata.ts** - Parses YAML frontmatter into typed `RuleMetadata`
|
|
488
|
+
- **rule-filter.ts** - Evaluates rules against context (globs, keywords, tools, runtime filters); returns `FilterResult` with `formattedRules` and `matchedPaths`
|
|
489
|
+
- **message-paths.ts** - Extracts file paths from tool invocation arguments and message text
|
|
490
|
+
- **message-context.ts** - Extracts user prompt text, slash commands, and session IDs from message parts
|
|
491
|
+
- **session-store.ts** - Manages per-session state with LRU eviction
|
|
492
|
+
- **project-fingerprint.ts** - Detects project type from marker files (e.g., `package.json`)
|
|
493
|
+
- **mcp-tools.ts** - Maps connected MCP clients to tool IDs for `tools` condition matching
|
|
494
|
+
- **git-branch.ts** - Resolves current git branch for `branch` condition matching
|
|
495
|
+
- **active-rules-state.ts** - Persists which rules matched per session to `~/.opencode/state/opencode-rules-md/{sessionId}.json` for TUI consumption (atomic writes, per-session queuing)
|
|
496
|
+
- **utils.ts** - Thin facade re-exporting from decomposed modules
|
|
497
|
+
|
|
498
|
+
### TUI Sidebar
|
|
499
|
+
|
|
500
|
+
The plugin registers a `sidebar_content` slot in the OpenCode TUI, displaying all discovered rules (global and project-local) with their active state and metadata.
|
|
501
|
+
|
|
502
|
+
**Requirements:** `@opencode-ai/plugin` ^1.3.7 with TUI support.
|
|
503
|
+
|
|
504
|
+
**What it shows:**
|
|
505
|
+
|
|
506
|
+
- Collapsible "Project" and "Global" sections grouping rules by scope
|
|
507
|
+
- Active/inactive status indicators (green bullet for active, muted for inactive) based on persisted state from the current session
|
|
508
|
+
- Condition summary for conditional rules ("always active" for unconditional ones)
|
|
509
|
+
- Expandable detail panel with all metadata fields (globs, keywords, tools, model, agent, command, project, branch, os, ci, match)
|
|
510
|
+
- Loading, error, and empty states
|
|
511
|
+
|
|
512
|
+
**Behavior:**
|
|
513
|
+
|
|
514
|
+
- Active rules are sorted to the top within each section
|
|
515
|
+
- Subscribes to `message.updated` and `session.status` events for real-time refresh (150ms debounce, filtered by session ID)
|
|
516
|
+
- Active state is read from `~/.opencode/state/opencode-rules-md/{sessionId}.json`, written by the server plugin after each rule evaluation
|
|
517
|
+
|
|
518
|
+
### Build and Test
|
|
519
|
+
|
|
520
|
+
```bash
|
|
521
|
+
# Install dependencies
|
|
522
|
+
bun install
|
|
523
|
+
|
|
524
|
+
# Run tests in watch mode
|
|
525
|
+
bun run test
|
|
526
|
+
|
|
527
|
+
# Run tests once
|
|
528
|
+
bun run test:run
|
|
529
|
+
|
|
530
|
+
# Build the project
|
|
531
|
+
bun run build
|
|
532
|
+
|
|
533
|
+
# Watch for changes and rebuild
|
|
534
|
+
bun run dev
|
|
535
|
+
|
|
536
|
+
# Format code
|
|
537
|
+
bun run format
|
|
538
|
+
|
|
539
|
+
# Lint code
|
|
540
|
+
bun run lint
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Tech Stack
|
|
544
|
+
|
|
545
|
+
- **TypeScript** - Type-safe development
|
|
546
|
+
- **@opencode-ai/plugin** - OpenCode plugin framework
|
|
547
|
+
- **Vitest** - Fast unit testing
|
|
548
|
+
- **Prettier** - Code formatting
|
|
549
|
+
- **ESLint** - Linting and code quality
|
|
550
|
+
|
|
551
|
+
## Architecture
|
|
552
|
+
|
|
553
|
+
This plugin uses OpenCode's hook system for incremental, stateful rule injection:
|
|
554
|
+
|
|
555
|
+
### Hook-Based Approach
|
|
556
|
+
|
|
557
|
+
1. **`tool.execute.before`** - Authoritative path capture from tool execution
|
|
558
|
+
- Fires before each tool runs (read, edit, write, glob, grep, etc.)
|
|
559
|
+
- Captures `filePath` or `path` arguments authoritative from the tool definition
|
|
560
|
+
- Updates session state with normalized, verified context paths
|
|
561
|
+
- Provides real-time context as tools are executed
|
|
562
|
+
|
|
563
|
+
2. **`chat.message`** - Incremental user prompt capture
|
|
564
|
+
- Fires as each user message arrives
|
|
565
|
+
- Extracts and stores the latest user prompt text
|
|
566
|
+
- Enables keyword-based rule matching across the conversation flow
|
|
567
|
+
|
|
568
|
+
3. **`experimental.chat.messages.transform`** - One-time seeding fallback
|
|
569
|
+
- Fires before the first LLM call only (skipped on subsequent turns)
|
|
570
|
+
- Seeds session state from full message history if needed
|
|
571
|
+
- Provides fallback context extraction from all visible messages
|
|
572
|
+
- Ensures rules apply even if initial context wasn't captured by tool hooks
|
|
573
|
+
|
|
574
|
+
4. **`experimental.chat.system.transform`** - Rule injection and filtering
|
|
575
|
+
- Fires before each LLM system prompt is constructed
|
|
576
|
+
- Receives full runtime filter context: model, agent, command, project type, git branch, OS, and CI environment
|
|
577
|
+
- Reads discovered rule files and filters based on:
|
|
578
|
+
- Extracted file paths from session state (`globs`)
|
|
579
|
+
- Latest user prompt (`keywords`)
|
|
580
|
+
- Available tool IDs (`tools`)
|
|
581
|
+
- Runtime environment (model, agent, command, project, branch, OS, CI)
|
|
582
|
+
- Command is inferred from the leading slash token (first token) of the latest user prompt
|
|
583
|
+
- Appends formatted rules to the system prompt
|
|
584
|
+
|
|
585
|
+
5. **`experimental.session.compacting`** - Compaction context preservation
|
|
586
|
+
- Fires when a session is compacted (summarized)
|
|
587
|
+
- Injects current context paths into the compaction context
|
|
588
|
+
- Prevents rules from being lost during session compression
|
|
589
|
+
|
|
590
|
+
### Experimental API Notice
|
|
591
|
+
|
|
592
|
+
This plugin depends on experimental OpenCode APIs:
|
|
593
|
+
|
|
594
|
+
- `experimental.chat.messages.transform` (fallback seeding)
|
|
595
|
+
- `experimental.chat.system.transform` (rule injection)
|
|
596
|
+
- `experimental.session.compacting` (compaction context)
|
|
597
|
+
|
|
598
|
+
These APIs may change in future OpenCode versions. Check OpenCode release notes when upgrading.
|
|
599
|
+
|
|
600
|
+
## Debug Logging
|
|
601
|
+
|
|
602
|
+
To enable debug logging, set the `OPENCODE_RULES_DEBUG` environment variable:
|
|
603
|
+
|
|
604
|
+
```bash
|
|
605
|
+
OPENCODE_RULES_DEBUG=1 opencode
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
This will log information about:
|
|
609
|
+
|
|
610
|
+
- Rule discovery (files found)
|
|
611
|
+
- Cache hits/misses
|
|
612
|
+
- Rule filtering (which rules are included/skipped)
|
|
613
|
+
- Available tool IDs (useful for writing `tools` conditions)
|
|
614
|
+
|
|
615
|
+
## Troubleshooting
|
|
616
|
+
|
|
617
|
+
### Rules Not Appearing
|
|
618
|
+
|
|
619
|
+
1. Verify directories exist: `~/.config/opencode/rules/` and/or `.opencode/rules/`
|
|
620
|
+
2. Check file extensions are `.md` or `.mdc`
|
|
621
|
+
3. Ensure files with metadata have properly formatted YAML
|
|
622
|
+
4. Enable debug logging (`OPENCODE_RULES_DEBUG=1`) to see which rules are being matched
|
|
623
|
+
|
|
624
|
+
### Common Issues
|
|
625
|
+
|
|
626
|
+
- **Missing directories**: Plugin gracefully handles missing directories
|
|
627
|
+
- **Invalid YAML**: Metadata parsing errors are logged but don't crash the plugin
|
|
628
|
+
- **Pattern mismatches**: Use relative paths from project root for glob patterns
|
|
629
|
+
|
|
630
|
+
## Contributing
|
|
631
|
+
|
|
632
|
+
We welcome contributions! Please:
|
|
633
|
+
|
|
634
|
+
1. Fork the repository
|
|
635
|
+
2. Create a feature branch
|
|
636
|
+
3. Add tests for new functionality
|
|
637
|
+
4. Ensure all tests pass: `bun run test:run`
|
|
638
|
+
5. Format code: `bun run format`
|
|
639
|
+
6. Submit a pull request
|
|
640
|
+
|
|
641
|
+
### Development Guidelines
|
|
642
|
+
|
|
643
|
+
- Follow existing code style (Prettier configuration)
|
|
644
|
+
- Add comprehensive tests for new features
|
|
645
|
+
- Update documentation for API changes
|
|
646
|
+
- Use TypeScript for all new code
|
|
647
|
+
|
|
648
|
+
## See Also
|
|
649
|
+
|
|
650
|
+
- [OpenCode Documentation](https://docs.opencode.ai/)
|