mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-02-07 23:23:08 +08:00
Major OpenCode integration overhaul: - llms.txt: Comprehensive OpenCode documentation for LLMs (642 lines) - .opencode/plugins/ecc-hooks.ts: All Claude Code hooks translated to OpenCode's plugin system - .opencode/tools/*.ts: 3 custom tools (run-tests, check-coverage, security-audit) - .opencode/commands/*.md: All 24 commands in OpenCode format - .opencode/package.json: npm package structure for opencode-ecc - .opencode/index.ts: Main plugin entry point - Delete incorrect LIMITATIONS.md (hooks ARE supported via plugins) - Rewrite MIGRATION.md with correct hook event mapping - Update README.md OpenCode section to show full feature parity OpenCode has 20+ events vs Claude Code's 3 phases: - PreToolUse → tool.execute.before - PostToolUse → tool.execute.after - Stop → session.idle - SessionStart → session.created - SessionEnd → session.deleted - Plus: file.edited, file.watcher.updated, permission.asked, todo.updated - 12 agents: Full parity - 24 commands: Full parity (+1 from original 23) - 16 skills: Full parity - Hooks: OpenCode has MORE (20+ events vs 3 phases) - Custom Tools: 3 native OpenCode tools The OpenCode configuration can now be: 1. Used directly: cd everything-claude-code && opencode 2. Installed via npm: npm install opencode-ecc
140 lines
3.4 KiB
TypeScript
140 lines
3.4 KiB
TypeScript
/**
|
|
* Run Tests Tool
|
|
*
|
|
* Custom OpenCode tool to run test suites with various options.
|
|
* Automatically detects the package manager and test framework.
|
|
*/
|
|
|
|
import { tool } from "@opencode-ai/plugin"
|
|
import * as path from "path"
|
|
import * as fs from "fs"
|
|
|
|
export default tool({
|
|
description:
|
|
"Run the test suite with optional coverage, watch mode, or specific test patterns. Automatically detects package manager (npm, pnpm, yarn, bun) and test framework.",
|
|
args: {
|
|
pattern: tool.schema
|
|
.string()
|
|
.optional()
|
|
.describe("Test file pattern or specific test name to run"),
|
|
coverage: tool.schema
|
|
.boolean()
|
|
.optional()
|
|
.describe("Run with coverage reporting (default: false)"),
|
|
watch: tool.schema
|
|
.boolean()
|
|
.optional()
|
|
.describe("Run in watch mode for continuous testing (default: false)"),
|
|
updateSnapshots: tool.schema
|
|
.boolean()
|
|
.optional()
|
|
.describe("Update Jest/Vitest snapshots (default: false)"),
|
|
},
|
|
async execute(args, context) {
|
|
const { pattern, coverage, watch, updateSnapshots } = args
|
|
const cwd = context.worktree || context.directory
|
|
|
|
// Detect package manager
|
|
const packageManager = await detectPackageManager(cwd)
|
|
|
|
// Detect test framework
|
|
const testFramework = await detectTestFramework(cwd)
|
|
|
|
// Build command
|
|
let cmd: string[] = [packageManager]
|
|
|
|
if (packageManager === "npm") {
|
|
cmd.push("run", "test")
|
|
} else {
|
|
cmd.push("test")
|
|
}
|
|
|
|
// Add options based on framework
|
|
const testArgs: string[] = []
|
|
|
|
if (coverage) {
|
|
testArgs.push("--coverage")
|
|
}
|
|
|
|
if (watch) {
|
|
testArgs.push("--watch")
|
|
}
|
|
|
|
if (updateSnapshots) {
|
|
testArgs.push("-u")
|
|
}
|
|
|
|
if (pattern) {
|
|
if (testFramework === "jest" || testFramework === "vitest") {
|
|
testArgs.push("--testPathPattern", pattern)
|
|
} else {
|
|
testArgs.push(pattern)
|
|
}
|
|
}
|
|
|
|
// Add -- separator for npm
|
|
if (testArgs.length > 0) {
|
|
if (packageManager === "npm") {
|
|
cmd.push("--")
|
|
}
|
|
cmd.push(...testArgs)
|
|
}
|
|
|
|
const command = cmd.join(" ")
|
|
|
|
return {
|
|
command,
|
|
packageManager,
|
|
testFramework,
|
|
options: {
|
|
pattern: pattern || "all tests",
|
|
coverage: coverage || false,
|
|
watch: watch || false,
|
|
updateSnapshots: updateSnapshots || false,
|
|
},
|
|
instructions: `Run this command to execute tests:\n\n${command}`,
|
|
}
|
|
},
|
|
})
|
|
|
|
async function detectPackageManager(cwd: string): Promise<string> {
|
|
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)) {
|
|
if (fs.existsSync(path.join(cwd, lockFile))) {
|
|
return pm
|
|
}
|
|
}
|
|
|
|
return "npm"
|
|
}
|
|
|
|
async function detectTestFramework(cwd: string): Promise<string> {
|
|
const packageJsonPath = path.join(cwd, "package.json")
|
|
|
|
if (fs.existsSync(packageJsonPath)) {
|
|
try {
|
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"))
|
|
const deps = {
|
|
...packageJson.dependencies,
|
|
...packageJson.devDependencies,
|
|
}
|
|
|
|
if (deps.vitest) return "vitest"
|
|
if (deps.jest) return "jest"
|
|
if (deps.mocha) return "mocha"
|
|
if (deps.ava) return "ava"
|
|
if (deps.tap) return "tap"
|
|
} catch {
|
|
// Ignore parse errors
|
|
}
|
|
}
|
|
|
|
return "unknown"
|
|
}
|