diff --git a/.opencode/plugins/ecc-hooks.ts b/.opencode/plugins/ecc-hooks.ts index 22b1132f0..472f80f5a 100644 --- a/.opencode/plugins/ecc-hooks.ts +++ b/.opencode/plugins/ecc-hooks.ts @@ -481,7 +481,7 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({ * Triggers: Before shell command execution * Action: Sets PROJECT_ROOT, PACKAGE_MANAGER, DETECTED_LANGUAGES, ECC_VERSION */ - "shell.env": async () => { + "shell.env": async (_input: { cwd: string }, output: { env: Record }) => { const env: Record = { ECC_VERSION: getECCVersion(), ECC_PLUGIN: "true", @@ -523,7 +523,7 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({ env.PRIMARY_LANGUAGE = detected[0] } - return env + output.env = { ...output.env, ...env } }, /** @@ -531,9 +531,12 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({ * OpenCode-specific: Control context compaction behavior * * Triggers: Before context compaction - * Action: Push ECC context block and custom compaction prompt + * Action: Push ECC context block and compaction guidance */ - "experimental.session.compacting": async () => { + "experimental.session.compacting": async ( + _input: { sessionID: string }, + output: { context: string[]; prompt?: string } + ) => { const contextBlock = [ "# ECC Context (preserve across compaction)", "", @@ -558,10 +561,11 @@ export const ECCHooksPlugin: ECCHooksPluginFn = async ({ 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.", - } + output.context = [ + ...output.context, + contextBlock.join("\n"), + "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.", + ] }, /** diff --git a/tests/opencode-plugin-hooks.test.js b/tests/opencode-plugin-hooks.test.js index 0d261ef5c..4a6511c53 100644 --- a/tests/opencode-plugin-hooks.test.js +++ b/tests/opencode-plugin-hooks.test.js @@ -179,9 +179,12 @@ async function main() { const $ = createFailingShell() const hooks = await ECCHooksPlugin({ client, $, directory: projectDir }) - const env = await hooks["shell.env"]() + const output = { env: { EXISTING_ENV: "preserved" } } + await hooks["shell.env"]({ cwd: projectDir }, output) + const { env } = output assert.deepStrictEqual($.calls, [], `Unexpected shell probes: ${$.calls.join(", ")}`) + assert.strictEqual(env.EXISTING_ENV, "preserved") assert.strictEqual(env.PROJECT_ROOT, projectDir) assert.strictEqual(env.PACKAGE_MANAGER, "pnpm") assert.strictEqual(env.DETECTED_LANGUAGES, "typescript,python") @@ -243,9 +246,12 @@ async function main() { const $ = createFailingShell() const hooks = await ECCHooksPlugin({ client, $, directory: projectDir }) - const env = await hooks["shell.env"]() + const output = { env: {} } + await hooks["shell.env"]({ cwd: projectDir }, output) + const { env } = output assert.deepStrictEqual($.calls, [], `Unexpected shell probes: ${$.calls.join(", ")}`) + assert.strictEqual(env.PROJECT_ROOT, projectDir) assert.ok(!("PACKAGE_MANAGER" in env), "Lockfile directory should not set PACKAGE_MANAGER") assert.ok(!("DETECTED_LANGUAGES" in env), "Marker directory should not set DETECTED_LANGUAGES") assert.ok(!("PRIMARY_LANGUAGE" in env), "Marker directory should not set PRIMARY_LANGUAGE") @@ -254,6 +260,27 @@ async function main() { } }, ], + [ + "compacting appends ECC context without replacing the host compaction prompt", + async () => withTempProject([], async (projectDir) => { + const client = createClient() + const $ = createFailingShell() + const hooks = await ECCHooksPlugin({ client, $, directory: projectDir }) + const output = { context: ["Existing plugin context"] } + + await hooks["experimental.session.compacting"]({ sessionID: "session-1" }, output) + + assert.strictEqual(output.context[0], "Existing plugin context") + const prompt = output.prompt ?? ["Default compaction prompt", ...output.context].join("\n\n") + assert.ok(prompt.includes("Default compaction prompt")) + assert.ok(prompt.includes("# ECC Context")) + assert.ok(prompt.includes("Current task status and progress")) + const customOutput = { context: [], prompt: "Another plugin's custom prompt" } + await hooks["experimental.session.compacting"]({ sessionID: "session-1" }, customOutput) + assert.strictEqual(customOutput.prompt, "Another plugin's custom prompt") + assert.deepStrictEqual($.calls, []) + }), + ], [ "permission.ask handles read-only tools correctly", async () => withTempProject(