mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-30 14:03:29 +08:00
fix: avoid opencode shell file probes
This commit is contained in:
committed by
Affaan Mustafa
parent
9627c201c7
commit
affbd33485
@@ -43,6 +43,14 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({
|
||||
return path.join(worktreePath, p)
|
||||
}
|
||||
|
||||
function hasProjectFile(relativePath: string): boolean {
|
||||
try {
|
||||
return fs.existsSync(resolvePath(relativePath))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const pendingToolChanges = new Map<string, { path: string; type: "added" | "modified" }>()
|
||||
let writeCounter = 0
|
||||
|
||||
@@ -275,13 +283,8 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({
|
||||
log("info", `[ECC] Session started - profile=${currentProfile}`)
|
||||
|
||||
// Check for project-specific context files
|
||||
try {
|
||||
const hasClaudeMd = await $`test -f ${worktree}/CLAUDE.md && echo "yes"`.text()
|
||||
if (hasClaudeMd.trim() === "yes") {
|
||||
log("info", "[ECC] Found CLAUDE.md - loading project context")
|
||||
}
|
||||
} catch {
|
||||
// No CLAUDE.md found
|
||||
if (hasProjectFile("CLAUDE.md")) {
|
||||
log("info", "[ECC] Found CLAUDE.md - loading project context")
|
||||
}
|
||||
},
|
||||
|
||||
@@ -400,7 +403,7 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({
|
||||
ECC_PLUGIN: "true",
|
||||
ECC_HOOK_PROFILE: currentProfile,
|
||||
ECC_DISABLED_HOOKS: process.env.ECC_DISABLED_HOOKS || "",
|
||||
PROJECT_ROOT: worktree || directory,
|
||||
PROJECT_ROOT: worktreePath,
|
||||
}
|
||||
|
||||
// Detect package manager
|
||||
@@ -411,12 +414,9 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({
|
||||
"package-lock.json": "npm",
|
||||
}
|
||||
for (const [lockfile, pm] of Object.entries(lockfiles)) {
|
||||
try {
|
||||
await $`test -f ${worktree}/${lockfile}`
|
||||
if (hasProjectFile(lockfile)) {
|
||||
env.PACKAGE_MANAGER = pm
|
||||
break
|
||||
} catch {
|
||||
// Not found, try next
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,11 +430,8 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({
|
||||
}
|
||||
const detected: string[] = []
|
||||
for (const [file, lang] of Object.entries(langDetectors)) {
|
||||
try {
|
||||
await $`test -f ${worktree}/${file}`
|
||||
if (hasProjectFile(file)) {
|
||||
detected.push(lang)
|
||||
} catch {
|
||||
// Not found
|
||||
}
|
||||
}
|
||||
if (detected.length > 0) {
|
||||
|
||||
137
tests/opencode-plugin-hooks.test.js
Normal file
137
tests/opencode-plugin-hooks.test.js
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Tests for the published OpenCode hook plugin surface.
|
||||
*/
|
||||
|
||||
const assert = require("node:assert")
|
||||
const fs = require("node:fs")
|
||||
const os = require("node:os")
|
||||
const path = require("node:path")
|
||||
const { spawnSync } = require("node:child_process")
|
||||
const { pathToFileURL } = require("node:url")
|
||||
|
||||
function runTest(name, fn) {
|
||||
return Promise.resolve()
|
||||
.then(fn)
|
||||
.then(() => {
|
||||
console.log(` ✓ ${name}`)
|
||||
return { passed: 1, failed: 0 }
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(` ✗ ${name}`)
|
||||
console.error(` ${error.stack || error.message}`)
|
||||
return { passed: 0, failed: 1 }
|
||||
})
|
||||
}
|
||||
|
||||
async function loadPlugin() {
|
||||
const repoRoot = path.join(__dirname, "..")
|
||||
const buildResult = spawnSync("node", [path.join(repoRoot, "scripts", "build-opencode.js")], {
|
||||
cwd: repoRoot,
|
||||
encoding: "utf8",
|
||||
})
|
||||
assert.strictEqual(buildResult.status, 0, buildResult.stderr || buildResult.stdout)
|
||||
const pluginUrl = pathToFileURL(
|
||||
path.join(repoRoot, ".opencode", "dist", "plugins", "ecc-hooks.js")
|
||||
).href
|
||||
return import(pluginUrl)
|
||||
}
|
||||
|
||||
function createClient() {
|
||||
const logs = []
|
||||
return {
|
||||
logs,
|
||||
app: {
|
||||
log: ({ body }) => {
|
||||
logs.push(body)
|
||||
return Promise.resolve()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createFailingShell() {
|
||||
const calls = []
|
||||
const shell = (strings, ...values) => {
|
||||
calls.push(String.raw({ raw: strings }, ...values))
|
||||
const error = new Error("OpenCode plugin file probes must not use shell commands")
|
||||
return {
|
||||
then: (_resolve, reject) => reject(error),
|
||||
text: async () => {
|
||||
throw error
|
||||
},
|
||||
}
|
||||
}
|
||||
shell.calls = calls
|
||||
return shell
|
||||
}
|
||||
|
||||
async function withTempProject(files, fn) {
|
||||
const projectDir = fs.mkdtempSync(path.join(os.tmpdir(), "ecc-opencode-plugin-"))
|
||||
try {
|
||||
for (const file of files) {
|
||||
const filePath = path.join(projectDir, file)
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
||||
fs.writeFileSync(filePath, "")
|
||||
}
|
||||
return await fn(projectDir)
|
||||
} finally {
|
||||
fs.rmSync(projectDir, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("\n=== Testing OpenCode plugin hooks ===\n")
|
||||
|
||||
const { ECCHooksPlugin } = await loadPlugin()
|
||||
const tests = [
|
||||
[
|
||||
"shell.env detects project markers without shelling out to test -f",
|
||||
async () => withTempProject(
|
||||
["pnpm-lock.yaml", "tsconfig.json", "pyproject.toml"],
|
||||
async (projectDir) => {
|
||||
const client = createClient()
|
||||
const $ = createFailingShell()
|
||||
const hooks = await ECCHooksPlugin({ client, $, directory: projectDir })
|
||||
|
||||
const env = await hooks["shell.env"]()
|
||||
|
||||
assert.deepStrictEqual($.calls, [], `Unexpected shell probes: ${$.calls.join(", ")}`)
|
||||
assert.strictEqual(env.PROJECT_ROOT, projectDir)
|
||||
assert.strictEqual(env.PACKAGE_MANAGER, "pnpm")
|
||||
assert.strictEqual(env.DETECTED_LANGUAGES, "typescript,python")
|
||||
assert.strictEqual(env.PRIMARY_LANGUAGE, "typescript")
|
||||
}
|
||||
),
|
||||
],
|
||||
[
|
||||
"session.created checks CLAUDE.md through fs instead of shell test",
|
||||
async () => withTempProject(["CLAUDE.md"], async (projectDir) => {
|
||||
const client = createClient()
|
||||
const $ = createFailingShell()
|
||||
const hooks = await ECCHooksPlugin({ client, $, directory: projectDir })
|
||||
|
||||
await hooks["session.created"]()
|
||||
|
||||
assert.deepStrictEqual($.calls, [], `Unexpected shell probes: ${$.calls.join(", ")}`)
|
||||
assert.ok(
|
||||
client.logs.some((entry) => entry.message === "[ECC] Found CLAUDE.md - loading project context"),
|
||||
"Expected CLAUDE.md detection log"
|
||||
)
|
||||
}),
|
||||
],
|
||||
]
|
||||
|
||||
let passed = 0
|
||||
let failed = 0
|
||||
for (const [name, fn] of tests) {
|
||||
const result = await runTest(name, fn)
|
||||
passed += result.passed
|
||||
failed += result.failed
|
||||
}
|
||||
|
||||
console.log(`\nPassed: ${passed}`)
|
||||
console.log(`Failed: ${failed}`)
|
||||
process.exit(failed > 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user