Created
May 21, 2026 05:47
-
-
Save AceCodePt/2267470254b1b06feae3c9f501a136e1 to your computer and use it in GitHub Desktop.
Opencode standard injector plugin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import type { Plugin } from "@opencode-ai/plugin" | |
| import { readFileSync, existsSync } from "fs" | |
| import path from "path" | |
| /** | |
| * Standards Injector Plugin | |
| * | |
| * When an agent reads a file, this plugin walks up from the file's directory | |
| * looking for `standards.md`. If found, it appends the standards content to | |
| * the read output — ONCE per standards.md file per session. | |
| * | |
| * Convention: place a `standards.md` (or `_standards.md` inside src/pages/ | |
| * to avoid Astro routing) in any directory to define coding standards for | |
| * that directory and its children. | |
| * | |
| * Example: | |
| * src/lib/entities/standards.md → injected when reading any entity file | |
| * src/pages/_standards.md → injected when reading any page file | |
| * src/pages/api/_standards.md → injected when reading any API endpoint | |
| * migrations/standards.md → injected when reading any migration | |
| * tests/standards.md → injected when reading any test | |
| */ | |
| // Look for both variants: `standards.md` (general) and `_standards.md` | |
| // (underscore-prefixed to avoid Astro routing in src/pages/) | |
| const STANDARDS_FILENAMES = ["standards.md", "_standards.md"] | |
| // Cache file contents in memory after first read | |
| const fileCache = new Map<string, string>() | |
| function readCached(filePath: string): string | null { | |
| if (fileCache.has(filePath)) return fileCache.get(filePath)! | |
| try { | |
| const content = readFileSync(filePath, "utf-8") | |
| fileCache.set(filePath, content) | |
| return content | |
| } catch { | |
| return null | |
| } | |
| } | |
| /** | |
| * Walk up from `startDir` toward `stopDir` (worktree root), collecting | |
| * all standards.md files found along the way. Returns them deepest-first. | |
| */ | |
| function findStandardsFiles(startDir: string, stopDir: string): string[] { | |
| const found: string[] = [] | |
| let current = startDir | |
| while (true) { | |
| for (const filename of STANDARDS_FILENAMES) { | |
| const candidate = path.join(current, filename) | |
| if (existsSync(candidate)) { | |
| found.push(candidate) | |
| break // only one per directory | |
| } | |
| } | |
| // Stop at worktree root | |
| if (current === stopDir) break | |
| const parent = path.dirname(current) | |
| // Safety: stop if we can't go higher | |
| if (parent === current) break | |
| current = parent | |
| } | |
| return found | |
| } | |
| export const StandardsInjector: Plugin = async ({ worktree }) => { | |
| // Track which standards.md files have already been injected this session | |
| const injected = new Set<string>() | |
| return { | |
| "tool.execute.after": async (input, output) => { | |
| if (input.tool !== "read") return | |
| const filePath: string = input.args?.filePath || "" | |
| if (!filePath) return | |
| // Skip if outside worktree | |
| const relative = path.relative(worktree, filePath) | |
| if (relative.startsWith("..")) return | |
| // Skip if reading a standards file itself | |
| const basename = path.basename(filePath) | |
| if (STANDARDS_FILENAMES.includes(basename)) return | |
| // Walk up from file's directory to worktree root | |
| const fileDir = path.dirname(filePath) | |
| const standardsFiles = findStandardsFiles(fileDir, worktree) | |
| // Inject any standards.md files not yet seen this session | |
| const toInject: string[] = [] | |
| for (const sf of standardsFiles) { | |
| if (!injected.has(sf)) { | |
| injected.add(sf) | |
| toInject.push(sf) | |
| } | |
| } | |
| if (toInject.length === 0) return | |
| // Append standards (deepest first, which is natural reading order) | |
| for (const sf of toInject) { | |
| const content = readCached(sf) | |
| if (!content) continue | |
| const relPath = path.relative(worktree, sf) | |
| output.output += `\n\n---\n📋 CODING STANDARDS (from ${relPath})\n---\n${content}` | |
| } | |
| }, | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment