mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
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:
@@ -4,8 +4,8 @@
|
||||
* This package provides a complete OpenCode plugin with:
|
||||
* - 13 specialized agents (planner, architect, code-reviewer, etc.)
|
||||
* - 31 commands (/plan, /tdd, /code-review, etc.)
|
||||
* - Plugin hooks (auto-format, TypeScript check, console.log warning, etc.)
|
||||
* - Custom tools (run-tests, check-coverage, security-audit)
|
||||
* - Plugin hooks (auto-format, TypeScript check, console.log warning, env injection, etc.)
|
||||
* - Custom tools (run-tests, check-coverage, security-audit, format-code, lint-check, git-summary)
|
||||
* - 37 skills (coding-standards, security-review, tdd-workflow, etc.)
|
||||
*
|
||||
* Usage:
|
||||
@@ -39,7 +39,7 @@ export { ECCHooksPlugin, default } from "./plugins/index.js"
|
||||
export * from "./plugins/index.js"
|
||||
|
||||
// Version export
|
||||
export const VERSION = "1.4.1"
|
||||
export const VERSION = "1.6.0"
|
||||
|
||||
// Plugin metadata
|
||||
export const metadata = {
|
||||
@@ -59,13 +59,18 @@ export const metadata = {
|
||||
"session.idle",
|
||||
"session.deleted",
|
||||
"file.watcher.updated",
|
||||
"permission.asked",
|
||||
"permission.ask",
|
||||
"todo.updated",
|
||||
"shell.env",
|
||||
"experimental.session.compacting",
|
||||
],
|
||||
customTools: [
|
||||
"run-tests",
|
||||
"check-coverage",
|
||||
"security-audit",
|
||||
"format-code",
|
||||
"lint-check",
|
||||
"git-summary",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,11 +4,19 @@
|
||||
"small_model": "anthropic/claude-haiku-4-5",
|
||||
"default_agent": "build",
|
||||
"instructions": [
|
||||
"AGENTS.md",
|
||||
"CONTRIBUTING.md",
|
||||
".opencode/instructions/INSTRUCTIONS.md",
|
||||
"skills/tdd-workflow/SKILL.md",
|
||||
"skills/security-review/SKILL.md",
|
||||
"skills/coding-standards/SKILL.md"
|
||||
"skills/coding-standards/SKILL.md",
|
||||
"skills/frontend-patterns/SKILL.md",
|
||||
"skills/backend-patterns/SKILL.md",
|
||||
"skills/e2e-testing/SKILL.md",
|
||||
"skills/verification-loop/SKILL.md",
|
||||
"skills/api-design/SKILL.md",
|
||||
"skills/strategic-compact/SKILL.md",
|
||||
"skills/eval-harness/SKILL.md"
|
||||
],
|
||||
"plugin": [
|
||||
"./.opencode/plugins"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ecc-universal",
|
||||
"version": "1.4.1",
|
||||
"version": "1.6.0",
|
||||
"description": "Everything Claude Code (ECC) plugin for OpenCode - agents, commands, hooks, and skills",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -261,17 +261,6 @@ export const ECCHooksPlugin = async ({
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Permission Asked Hook
|
||||
* OpenCode-only feature
|
||||
*
|
||||
* Triggers: When permission is requested
|
||||
* Action: Logs for audit trail
|
||||
*/
|
||||
"permission.asked": async (event: { tool: string; args: unknown }) => {
|
||||
log("info", `[ECC] Permission requested for: ${event.tool}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* Todo Updated Hook
|
||||
* OpenCode-only feature
|
||||
@@ -286,6 +275,131 @@ export const ECCHooksPlugin = async ({
|
||||
log("info", `[ECC] Progress: ${completed}/${total} tasks completed`)
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shell Environment Hook
|
||||
* OpenCode-specific: Inject environment variables into shell commands
|
||||
*
|
||||
* Triggers: Before shell command execution
|
||||
* Action: Sets PROJECT_ROOT, PACKAGE_MANAGER, DETECTED_LANGUAGES, ECC_VERSION
|
||||
*/
|
||||
"shell.env": async () => {
|
||||
const env: Record<string, string> = {
|
||||
ECC_VERSION: "1.6.0",
|
||||
ECC_PLUGIN: "true",
|
||||
PROJECT_ROOT: worktree || directory,
|
||||
}
|
||||
|
||||
// Detect package manager
|
||||
const lockfiles: Record<string, string> = {
|
||||
"bun.lockb": "bun",
|
||||
"pnpm-lock.yaml": "pnpm",
|
||||
"yarn.lock": "yarn",
|
||||
"package-lock.json": "npm",
|
||||
}
|
||||
for (const [lockfile, pm] of Object.entries(lockfiles)) {
|
||||
try {
|
||||
await $`test -f ${worktree}/${lockfile}`
|
||||
env.PACKAGE_MANAGER = pm
|
||||
break
|
||||
} catch {
|
||||
// Not found, try next
|
||||
}
|
||||
}
|
||||
|
||||
// Detect languages
|
||||
const langDetectors: Record<string, string> = {
|
||||
"tsconfig.json": "typescript",
|
||||
"go.mod": "go",
|
||||
"pyproject.toml": "python",
|
||||
"Cargo.toml": "rust",
|
||||
"Package.swift": "swift",
|
||||
}
|
||||
const detected: string[] = []
|
||||
for (const [file, lang] of Object.entries(langDetectors)) {
|
||||
try {
|
||||
await $`test -f ${worktree}/${file}`
|
||||
detected.push(lang)
|
||||
} catch {
|
||||
// Not found
|
||||
}
|
||||
}
|
||||
if (detected.length > 0) {
|
||||
env.DETECTED_LANGUAGES = detected.join(",")
|
||||
env.PRIMARY_LANGUAGE = detected[0]
|
||||
}
|
||||
|
||||
return env
|
||||
},
|
||||
|
||||
/**
|
||||
* Session Compacting Hook
|
||||
* OpenCode-specific: Control context compaction behavior
|
||||
*
|
||||
* Triggers: Before context compaction
|
||||
* Action: Push ECC context block and custom compaction prompt
|
||||
*/
|
||||
"experimental.session.compacting": async () => {
|
||||
const contextBlock = [
|
||||
"# ECC Context (preserve across compaction)",
|
||||
"",
|
||||
"## Active Plugin: Everything Claude Code v1.6.0",
|
||||
"- Hooks: file.edited, tool.execute.before/after, session.created/idle/deleted, shell.env, compacting, permission.ask",
|
||||
"- Tools: run-tests, check-coverage, security-audit, format-code, lint-check, git-summary",
|
||||
"- Agents: 13 specialized (planner, architect, tdd-guide, code-reviewer, security-reviewer, build-error-resolver, e2e-runner, refactor-cleaner, doc-updater, go-reviewer, go-build-resolver, database-reviewer, python-reviewer)",
|
||||
"",
|
||||
"## Key Principles",
|
||||
"- TDD: write tests first, 80%+ coverage",
|
||||
"- Immutability: never mutate, always return new copies",
|
||||
"- Security: validate inputs, no hardcoded secrets",
|
||||
"",
|
||||
]
|
||||
|
||||
// Include recently edited files
|
||||
if (editedFiles.size > 0) {
|
||||
contextBlock.push("## Recently Edited Files")
|
||||
for (const f of editedFiles) {
|
||||
contextBlock.push(`- ${f}`)
|
||||
}
|
||||
contextBlock.push("")
|
||||
}
|
||||
|
||||
return {
|
||||
context: contextBlock.join("\n"),
|
||||
compaction_prompt: "Focus on preserving: 1) Current task status and progress, 2) Key decisions made, 3) Files created/modified, 4) Remaining work items, 5) Any security concerns flagged. Discard: verbose tool outputs, intermediate exploration, redundant file listings.",
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Permission Auto-Approve Hook
|
||||
* OpenCode-specific: Auto-approve safe operations
|
||||
*
|
||||
* Triggers: When permission is requested
|
||||
* Action: Auto-approve reads, formatters, and test commands; log all for audit
|
||||
*/
|
||||
"permission.ask": async (event: { tool: string; args: unknown }) => {
|
||||
log("info", `[ECC] Permission requested for: ${event.tool}`)
|
||||
|
||||
const cmd = String((event.args as Record<string, unknown>)?.command || event.args || "")
|
||||
|
||||
// Auto-approve: read/search tools
|
||||
if (["read", "glob", "grep", "search", "list"].includes(event.tool)) {
|
||||
return { approved: true, reason: "Read-only operation" }
|
||||
}
|
||||
|
||||
// Auto-approve: formatters
|
||||
if (event.tool === "bash" && /^(npx )?(prettier|biome|black|gofmt|rustfmt|swift-format)/.test(cmd)) {
|
||||
return { approved: true, reason: "Formatter execution" }
|
||||
}
|
||||
|
||||
// Auto-approve: test execution
|
||||
if (event.tool === "bash" && /^(npm test|npx vitest|npx jest|pytest|go test|cargo test)/.test(cmd)) {
|
||||
return { approved: true, reason: "Test execution" }
|
||||
}
|
||||
|
||||
// Everything else: let user decide
|
||||
return { approved: undefined }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
66
.opencode/tools/format-code.ts
Normal file
66
.opencode/tools/format-code.ts
Normal 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" }
|
||||
}
|
||||
},
|
||||
})
|
||||
56
.opencode/tools/git-summary.ts
Normal file
56
.opencode/tools/git-summary.ts
Normal 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
|
||||
},
|
||||
})
|
||||
@@ -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"
|
||||
|
||||
74
.opencode/tools/lint-check.ts
Normal file
74
.opencode/tools/lint-check.ts
Normal 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 || "",
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user