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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +650 -0
  3. package/dist/cli.mjs +374 -0
  4. package/dist/src/active-rules-state.d.ts +12 -0
  5. package/dist/src/active-rules-state.d.ts.map +1 -0
  6. package/dist/src/active-rules-state.js +118 -0
  7. package/dist/src/active-rules-state.js.map +1 -0
  8. package/dist/src/cli/config.d.ts +62 -0
  9. package/dist/src/cli/config.d.ts.map +1 -0
  10. package/dist/src/cli/config.js +246 -0
  11. package/dist/src/cli/config.js.map +1 -0
  12. package/dist/src/cli/install.d.ts +21 -0
  13. package/dist/src/cli/install.d.ts.map +1 -0
  14. package/dist/src/cli/install.js +63 -0
  15. package/dist/src/cli/install.js.map +1 -0
  16. package/dist/src/cli/main.d.ts +19 -0
  17. package/dist/src/cli/main.d.ts.map +1 -0
  18. package/dist/src/cli/main.js +135 -0
  19. package/dist/src/cli/main.js.map +1 -0
  20. package/dist/src/cli/real-fs.d.ts +26 -0
  21. package/dist/src/cli/real-fs.d.ts.map +1 -0
  22. package/dist/src/cli/real-fs.js +42 -0
  23. package/dist/src/cli/real-fs.js.map +1 -0
  24. package/dist/src/cli/status.d.ts +19 -0
  25. package/dist/src/cli/status.d.ts.map +1 -0
  26. package/dist/src/cli/status.js +58 -0
  27. package/dist/src/cli/status.js.map +1 -0
  28. package/dist/src/debug.d.ts +3 -0
  29. package/dist/src/debug.d.ts.map +1 -0
  30. package/dist/src/debug.js +8 -0
  31. package/dist/src/debug.js.map +1 -0
  32. package/dist/src/git-branch.d.ts +2 -0
  33. package/dist/src/git-branch.d.ts.map +1 -0
  34. package/dist/src/git-branch.js +30 -0
  35. package/dist/src/git-branch.js.map +1 -0
  36. package/dist/src/index.d.ts +26 -0
  37. package/dist/src/index.d.ts.map +1 -0
  38. package/dist/src/index.js +55 -0
  39. package/dist/src/index.js.map +1 -0
  40. package/dist/src/mcp-tools.d.ts +8 -0
  41. package/dist/src/mcp-tools.d.ts.map +1 -0
  42. package/dist/src/mcp-tools.js +18 -0
  43. package/dist/src/mcp-tools.js.map +1 -0
  44. package/dist/src/message-context.d.ts +55 -0
  45. package/dist/src/message-context.d.ts.map +1 -0
  46. package/dist/src/message-context.js +110 -0
  47. package/dist/src/message-context.js.map +1 -0
  48. package/dist/src/message-paths.d.ts +34 -0
  49. package/dist/src/message-paths.d.ts.map +1 -0
  50. package/dist/src/message-paths.js +112 -0
  51. package/dist/src/message-paths.js.map +1 -0
  52. package/dist/src/project-fingerprint.d.ts +2 -0
  53. package/dist/src/project-fingerprint.d.ts.map +1 -0
  54. package/dist/src/project-fingerprint.js +61 -0
  55. package/dist/src/project-fingerprint.js.map +1 -0
  56. package/dist/src/rule-discovery.d.ts +49 -0
  57. package/dist/src/rule-discovery.d.ts.map +1 -0
  58. package/dist/src/rule-discovery.js +143 -0
  59. package/dist/src/rule-discovery.js.map +1 -0
  60. package/dist/src/rule-filter.d.ts +61 -0
  61. package/dist/src/rule-filter.d.ts.map +1 -0
  62. package/dist/src/rule-filter.js +180 -0
  63. package/dist/src/rule-filter.js.map +1 -0
  64. package/dist/src/rule-metadata.d.ts +29 -0
  65. package/dist/src/rule-metadata.d.ts.map +1 -0
  66. package/dist/src/rule-metadata.js +100 -0
  67. package/dist/src/rule-metadata.js.map +1 -0
  68. package/dist/src/runtime-chat.d.ts +34 -0
  69. package/dist/src/runtime-chat.d.ts.map +1 -0
  70. package/dist/src/runtime-chat.js +39 -0
  71. package/dist/src/runtime-chat.js.map +1 -0
  72. package/dist/src/runtime-context.d.ts +25 -0
  73. package/dist/src/runtime-context.d.ts.map +1 -0
  74. package/dist/src/runtime-context.js +109 -0
  75. package/dist/src/runtime-context.js.map +1 -0
  76. package/dist/src/runtime.d.ts +31 -0
  77. package/dist/src/runtime.d.ts.map +1 -0
  78. package/dist/src/runtime.js +238 -0
  79. package/dist/src/runtime.js.map +1 -0
  80. package/dist/src/session-store.d.ts +34 -0
  81. package/dist/src/session-store.d.ts.map +1 -0
  82. package/dist/src/session-store.js +90 -0
  83. package/dist/src/session-store.js.map +1 -0
  84. package/dist/src/utils.d.ts +15 -0
  85. package/dist/src/utils.d.ts.map +1 -0
  86. package/dist/src/utils.js +19 -0
  87. package/dist/src/utils.js.map +1 -0
  88. package/dist/tui/data/rules.d.ts +63 -0
  89. package/dist/tui/data/rules.d.ts.map +1 -0
  90. package/dist/tui/data/rules.js +173 -0
  91. package/dist/tui/data/rules.js.map +1 -0
  92. package/dist/tui/index.d.ts +8 -0
  93. package/dist/tui/index.d.ts.map +1 -0
  94. package/dist/tui/index.js +13 -0
  95. package/dist/tui/index.js.map +1 -0
  96. package/dist/tui/slots/sidebar-content.d.ts +11 -0
  97. package/dist/tui/slots/sidebar-content.d.ts.map +1 -0
  98. package/dist/tui/slots/sidebar-content.js +150 -0
  99. package/dist/tui/slots/sidebar-content.js.map +1 -0
  100. package/package.json +101 -0
  101. package/src/active-rules-state.ts +166 -0
  102. package/src/cli/config.ts +312 -0
  103. package/src/cli/install.ts +94 -0
  104. package/src/cli/main.ts +167 -0
  105. package/src/cli/real-fs.ts +65 -0
  106. package/src/cli/status.ts +80 -0
  107. package/src/debug.ts +9 -0
  108. package/src/git-branch.ts +37 -0
  109. package/src/index.ts +68 -0
  110. package/src/mcp-tools.ts +24 -0
  111. package/src/message-context.ts +139 -0
  112. package/src/message-paths.ts +153 -0
  113. package/src/project-fingerprint.ts +72 -0
  114. package/src/rule-discovery.ts +205 -0
  115. package/src/rule-filter.ts +269 -0
  116. package/src/rule-metadata.ts +161 -0
  117. package/src/runtime-chat.ts +68 -0
  118. package/src/runtime-context.ts +136 -0
  119. package/src/runtime.ts +369 -0
  120. package/src/session-store.ts +129 -0
  121. package/src/utils.ts +36 -0
  122. package/tui/.atl/.skill-registry.cache.json +3 -0
  123. package/tui/.atl/skill-registry.md +57 -0
  124. package/tui/data/rules.ts +230 -0
  125. package/tui/index.tsx +23 -0
  126. package/tui/slots/sidebar-content.tsx +364 -0
  127. 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
+ [![npm version](https://img.shields.io/npm/v/opencode-rules-md)](https://www.npmjs.com/package/opencode-rules-md)
4
+ [![npm downloads](https://img.shields.io/npm/dm/opencode-rules-md)](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/)