Skip to content

Instantly share code, notes, and snippets.

@AceCodePt
Created May 21, 2026 05:47
Show Gist options
  • Select an option

  • Save AceCodePt/2267470254b1b06feae3c9f501a136e1 to your computer and use it in GitHub Desktop.

Select an option

Save AceCodePt/2267470254b1b06feae3c9f501a136e1 to your computer and use it in GitHub Desktop.
Opencode standard injector plugin
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