Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions src/hooks/comment-checker/hook.output-guard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, it, expect, mock } from "bun:test"

mock.module("./cli-runner", () => ({
initializeCommentCheckerCli: () => {},
getCommentCheckerCliPathPromise: () => Promise.resolve("/tmp/fake-comment-checker"),
isCliPathUsable: () => true,
processWithCli: async () => {},
processApplyPatchEditsWithCli: async () => {},
}))

const { createCommentCheckerHooks } = await import("./hook")

describe("comment-checker output guard", () => {
//#given output.output is undefined
//#when tool.execute.after is called
//#then should return without throwing
it("should not throw when output.output is undefined", async () => {
const hooks = createCommentCheckerHooks()
const input = { tool: "Write", sessionID: "ses_test", callID: "call_test" }
const output = { title: "ok", output: undefined as unknown as string, metadata: {} }

await expect(hooks["tool.execute.after"](input, output)).resolves.toBeUndefined()
})
})
2 changes: 2 additions & 0 deletions src/hooks/comment-checker/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export function createCommentCheckerHooks(config?: CommentCheckerConfig) {
): Promise<void> => {
debugLog("tool.execute.after:", { tool: input.tool, callID: input.callID })

if (!output.output || typeof output.output !== "string") return
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Early-returning on missing output.output prevents the hook from running comment checks for write/edit/apply_patch cases where tools omit output.output. Instead of returning, coerce to an empty string so the hook can still process metadata/pending calls.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/hooks/comment-checker/hook.ts, line 92:

<comment>Early-returning on missing output.output prevents the hook from running comment checks for write/edit/apply_patch cases where tools omit output.output. Instead of returning, coerce to an empty string so the hook can still process metadata/pending calls.</comment>

<file context>
@@ -89,6 +89,8 @@ export function createCommentCheckerHooks(config?: CommentCheckerConfig) {
     ): Promise<void> => {
       debugLog("tool.execute.after:", { tool: input.tool, callID: input.callID })
 
+      if (!output.output || typeof output.output !== "string") return
+
       const toolLower = input.tool.toLowerCase()
</file context>
Suggested change
if (!output.output || typeof output.output !== "string") return
if (typeof output.output !== "string") output.output = ""
Fix with Cubic


const toolLower = input.tool.toLowerCase()

// Only skip if the output indicates a tool execution failure
Expand Down
1 change: 1 addition & 0 deletions src/hooks/edit-error-recovery/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function createEditErrorRecoveryHook(_ctx: PluginInput) {
output: { title: string; output: string; metadata: unknown }
) => {
if (input.tool.toLowerCase() !== "edit") return
if (!output.output || typeof output.output !== "string") return

const outputLower = output.output.toLowerCase()
const hasEditError = EDIT_ERROR_PATTERNS.some((pattern) =>
Expand Down
11 changes: 11 additions & 0 deletions src/hooks/edit-error-recovery/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ describe("createEditErrorRecoveryHook", () => {
metadata: {},
})

describe("#given output.output is undefined", () => {
//#when tool.execute.after is called
//#then should return without throwing
it("#then should not throw", async () => {
const input = createInput("Edit")
const output = { title: "Edit", output: undefined as unknown as string, metadata: {} }

await expect(hook["tool.execute.after"](input, output)).resolves.toBeUndefined()
})
})

describe("#given Edit tool with oldString/newString same error", () => {
describe("#when the error message is detected", () => {
it("#then should append the recovery reminder", async () => {
Expand Down
17 changes: 17 additions & 0 deletions src/hooks/task-resume-info/hook.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { describe, it, expect } from "bun:test"
import { createTaskResumeInfoHook } from "./hook"

describe("createTaskResumeInfoHook", () => {
describe("tool.execute.after", () => {
//#given output.output is undefined
//#when tool.execute.after is called
//#then should return without throwing
it("should not throw when output.output is undefined", async () => {
const hook = createTaskResumeInfoHook()
const input = { tool: "Task", sessionID: "ses_test", callID: "call_test" }
const output = { title: "Result", output: undefined as unknown as string, metadata: {} }

await expect(hook["tool.execute.after"](input, output)).resolves.toBeUndefined()
})
})
})
1 change: 1 addition & 0 deletions src/hooks/task-resume-info/hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function createTaskResumeInfoHook() {
output: { title: string; output: string; metadata: unknown }
) => {
if (!TARGET_TOOLS.includes(input.tool)) return
if (!output.output || typeof output.output !== "string") return
if (output.output.startsWith("Error:") || output.output.startsWith("Failed")) return
if (output.output.includes("\nto continue:")) return

Expand Down