feat: add Cursor, Codex, and OpenCode harnesses — maximize every AI coding tool

- AGENTS.md: universal cross-tool file read by Claude Code, Cursor, Codex, and OpenCode
- .cursor/: 15 hook events via hooks.json, 16 hook scripts with DRY adapter pattern,
  29 rules (9 common + 20 language-specific) with Cursor YAML frontmatter
- .codex/: reference config.toml, Codex-specific AGENTS.md supplement,
  10 skills ported to .agents/skills/ with openai.yaml metadata
- .opencode/: 3 new tools (format-code, lint-check, git-summary), 3 new hooks
  (shell.env, experimental.session.compacting, permission.ask), expanded instructions,
  version bumped to 1.6.0
- README: fixed Cursor section, added Codex section, added cross-tool parity table
- install.sh: now copies hooks.json + hooks/ for --target cursor
This commit is contained in:
Affaan Mustafa
2026-02-25 10:45:29 -08:00
parent a9b104fc23
commit d70bab85e3
83 changed files with 6249 additions and 212 deletions

View File

@@ -0,0 +1,66 @@
/**
* ECC Custom Tool: Format Code
*
* Language-aware code formatter that auto-detects the project's formatter.
* Supports: Biome/Prettier (JS/TS), Black (Python), gofmt (Go), rustfmt (Rust)
*/
import { tool } from "@opencode-ai/plugin"
import { z } from "zod"
export default tool({
name: "format-code",
description: "Format a file using the project's configured formatter. Auto-detects Biome, Prettier, Black, gofmt, or rustfmt.",
parameters: z.object({
filePath: z.string().describe("Path to the file to format"),
formatter: z.string().optional().describe("Override formatter: biome, prettier, black, gofmt, rustfmt (default: auto-detect)"),
}),
execute: async ({ filePath, formatter }, { $ }) => {
const ext = filePath.split(".").pop()?.toLowerCase() || ""
// Auto-detect formatter based on file extension and config files
let detected = formatter
if (!detected) {
if (["ts", "tsx", "js", "jsx", "json", "css", "scss"].includes(ext)) {
// Check for Biome first, then Prettier
try {
await $`test -f biome.json || test -f biome.jsonc`
detected = "biome"
} catch {
detected = "prettier"
}
} else if (["py", "pyi"].includes(ext)) {
detected = "black"
} else if (ext === "go") {
detected = "gofmt"
} else if (ext === "rs") {
detected = "rustfmt"
}
}
if (!detected) {
return { formatted: false, message: `No formatter detected for .${ext} files` }
}
const commands: Record<string, string> = {
biome: `npx @biomejs/biome format --write ${filePath}`,
prettier: `npx prettier --write ${filePath}`,
black: `black ${filePath}`,
gofmt: `gofmt -w ${filePath}`,
rustfmt: `rustfmt ${filePath}`,
}
const cmd = commands[detected]
if (!cmd) {
return { formatted: false, message: `Unknown formatter: ${detected}` }
}
try {
const result = await $`${cmd}`.text()
return { formatted: true, formatter: detected, output: result }
} catch (error: unknown) {
const err = error as { stderr?: string }
return { formatted: false, formatter: detected, error: err.stderr || "Format failed" }
}
},
})

View File

@@ -0,0 +1,56 @@
/**
* ECC Custom Tool: Git Summary
*
* Provides a comprehensive git status including branch info, status,
* recent log, and diff against base branch.
*/
import { tool } from "@opencode-ai/plugin"
import { z } from "zod"
export default tool({
name: "git-summary",
description: "Get comprehensive git summary: branch, status, recent log, and diff against base branch.",
parameters: z.object({
depth: z.number().optional().describe("Number of recent commits to show (default: 5)"),
includeDiff: z.boolean().optional().describe("Include diff against base branch (default: true)"),
baseBranch: z.string().optional().describe("Base branch for comparison (default: main)"),
}),
execute: async ({ depth = 5, includeDiff = true, baseBranch = "main" }, { $ }) => {
const results: Record<string, string> = {}
try {
results.branch = (await $`git branch --show-current`.text()).trim()
} catch {
results.branch = "unknown"
}
try {
results.status = (await $`git status --short`.text()).trim()
} catch {
results.status = "unable to get status"
}
try {
results.log = (await $`git log --oneline -${depth}`.text()).trim()
} catch {
results.log = "unable to get log"
}
if (includeDiff) {
try {
results.stagedDiff = (await $`git diff --cached --stat`.text()).trim()
} catch {
results.stagedDiff = ""
}
try {
results.branchDiff = (await $`git diff ${baseBranch}...HEAD --stat`.text()).trim()
} catch {
results.branchDiff = `unable to diff against ${baseBranch}`
}
}
return results
},
})

View File

@@ -8,3 +8,6 @@
export { default as runTests } from "./run-tests.js"
export { default as checkCoverage } from "./check-coverage.js"
export { default as securityAudit } from "./security-audit.js"
export { default as formatCode } from "./format-code.js"
export { default as lintCheck } from "./lint-check.js"
export { default as gitSummary } from "./git-summary.js"

View File

@@ -0,0 +1,74 @@
/**
* ECC Custom Tool: Lint Check
*
* Multi-language linter that auto-detects the project's linting tool.
* Supports: ESLint/Biome (JS/TS), Pylint/Ruff (Python), golangci-lint (Go)
*/
import { tool } from "@opencode-ai/plugin"
import { z } from "zod"
export default tool({
name: "lint-check",
description: "Run linter on files or directories. Auto-detects ESLint, Biome, Ruff, Pylint, or golangci-lint.",
parameters: z.object({
target: z.string().optional().describe("File or directory to lint (default: current directory)"),
fix: z.boolean().optional().describe("Auto-fix issues if supported (default: false)"),
linter: z.string().optional().describe("Override linter: eslint, biome, ruff, pylint, golangci-lint (default: auto-detect)"),
}),
execute: async ({ target = ".", fix = false, linter }, { $ }) => {
// Auto-detect linter
let detected = linter
if (!detected) {
try {
await $`test -f biome.json || test -f biome.jsonc`
detected = "biome"
} catch {
try {
await $`test -f .eslintrc.json || test -f .eslintrc.js || test -f .eslintrc.cjs || test -f eslint.config.js || test -f eslint.config.mjs`
detected = "eslint"
} catch {
try {
await $`test -f pyproject.toml && grep -q "ruff" pyproject.toml`
detected = "ruff"
} catch {
try {
await $`test -f .golangci.yml || test -f .golangci.yaml`
detected = "golangci-lint"
} catch {
// Fall back based on file extensions in target
detected = "eslint"
}
}
}
}
}
const fixFlag = fix ? " --fix" : ""
const commands: Record<string, string> = {
biome: `npx @biomejs/biome lint${fix ? " --write" : ""} ${target}`,
eslint: `npx eslint${fixFlag} ${target}`,
ruff: `ruff check${fixFlag} ${target}`,
pylint: `pylint ${target}`,
"golangci-lint": `golangci-lint run${fixFlag} ${target}`,
}
const cmd = commands[detected]
if (!cmd) {
return { success: false, message: `Unknown linter: ${detected}` }
}
try {
const result = await $`${cmd}`.text()
return { success: true, linter: detected, output: result, issues: 0 }
} catch (error: unknown) {
const err = error as { stdout?: string; stderr?: string }
return {
success: false,
linter: detected,
output: err.stdout || "",
errors: err.stderr || "",
}
}
},
})