Skip to content

fix: emit formatter events from hashline-edit tool (fixes #2117)#2768

Merged
code-yeongyu merged 2 commits intodevfrom
fix/issue-2117
Mar 23, 2026
Merged

fix: emit formatter events from hashline-edit tool (fixes #2117)#2768
code-yeongyu merged 2 commits intodevfrom
fix/issue-2117

Conversation

@code-yeongyu
Copy link
Copy Markdown
Owner

@code-yeongyu code-yeongyu commented Mar 23, 2026

Problem

The hashline-edit tool completely replaces OpenCode's built-in edit tool but does not emit formatter/afterEdit events, so user-configured formatters (prettier, eslint --fix, etc.) never run after edits.

Fix

Added formatter-trigger.ts that emits the same formatter events as OpenCode's native edit tool after successful hashline-edit operations. Also updated tool-registry.ts and hashline-edit-executor.ts to integrate the formatter trigger.

Changes:

  • New: src/tools/hashline-edit/formatter-trigger.ts (122 lines)
  • Modified: hashline-edit-executor.ts — call formatter trigger after edit
  • Modified: tools.ts — pass context for formatter integration
  • Modified: tool-registry.ts — minor fix

4 files changed, 157 insertions(+), 4 deletions(-)

Fixes #2117

Automated fix by Sisyphus (oh-my-opencode)


Summary by cubic

Ensures user-configured formatters run after hashline-edit by emitting formatter events like the built-in edit tool. Fixes #2117 so tools like prettier/eslint --fix and file_edited hooks execute and their changes are saved.

  • Bug Fixes
    • Added src/tools/hashline-edit/formatter-trigger.ts to resolve per-extension formatters from client config and run commands with $FILE substitution and env vars.
    • Integrated trigger into hashline-edit-executor to run after writes, then update metadata and handle renames if formatting changes content.
    • Passed PluginContext through createHashlineEditTool(ctx) and tool-registry so the trigger can access client.config.
    • Clear formatter cache on config reload via config-handler to pick up new formatter settings.
    • Added tests for the formatter trigger (resolution, caching, error handling, and command building) and tightened typings (FormatterClient).

Written for commit 4ba2da7. Summary will update on new commits.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 4 files

Confidence score: 2/5

  • There is a high-confidence deadlock risk in src/tools/hashline-edit/formatter-trigger.ts: waiting on proc.exited before draining stderr can hang the formatter process if the pipe buffer fills, which is a likely functional blocker.
  • Additional correctness issues increase regression risk: replace() in src/tools/hashline-edit/formatter-trigger.ts should use a function to avoid $&/$1 substitution side effects, and src/tools/hashline-edit/hashline-edit-executor.ts reports stale metadata after renames.
  • Rename handling in src/tools/hashline-edit/hashline-edit-executor.ts also runs formatters against the old path, which can apply the wrong formatter/language rules and produce inconsistent output.
  • Pay close attention to src/tools/hashline-edit/formatter-trigger.ts and src/tools/hashline-edit/hashline-edit-executor.ts - process I/O ordering and rename-path propagation need to be corrected to avoid hangs and path/format mismatches.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/tools/hashline-edit/formatter-trigger.ts">

<violation number="1" location="src/tools/hashline-edit/formatter-trigger.ts:80">
P2: Pass a function to `replace()` to prevent special string replacement patterns (like `$&` or `$1`) from corrupting the file path.</violation>

<violation number="2" location="src/tools/hashline-edit/formatter-trigger.ts:105">
P1: Read the `stderr` stream before or concurrently with `proc.exited` to prevent the child process from deadlocking if the pipe buffer fills up.</violation>
</file>

<file name="src/tools/hashline-edit/hashline-edit-executor.ts">

<violation number="1" location="src/tools/hashline-edit/hashline-edit-executor.ts:135">
P2: Formatters are run against the original `filePath` rather than the new `rename` path, which may use incorrect language formatting rules.</violation>

<violation number="2" location="src/tools/hashline-edit/hashline-edit-executor.ts:139">
P2: When a file is renamed, the tool returns the old `filePath` in its metadata instead of the new path.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

stdout: "ignore",
stderr: "pipe",
})
await proc.exited
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 23, 2026

Choose a reason for hiding this comment

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

P1: Read the stderr stream before or concurrently with proc.exited to prevent the child process from deadlocking if the pipe buffer fills up.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tools/hashline-edit/formatter-trigger.ts, line 105:

<comment>Read the `stderr` stream before or concurrently with `proc.exited` to prevent the child process from deadlocking if the pipe buffer fills up.</comment>

<file context>
@@ -0,0 +1,122 @@
+        stdout: "ignore",
+        stderr: "pipe",
+      })
+      await proc.exited
+      if (proc.exitCode !== 0) {
+        const stderr = await new Response(proc.stderr).text()
</file context>
Fix with Cubic

}

function buildFormatterCommand(command: string[], filePath: string): string[] {
return command.map((arg) => arg.replace(/\$FILE/g, filePath))
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 23, 2026

Choose a reason for hiding this comment

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

P2: Pass a function to replace() to prevent special string replacement patterns (like $& or $1) from corrupting the file path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tools/hashline-edit/formatter-trigger.ts, line 80:

<comment>Pass a function to `replace()` to prevent special string replacement patterns (like `$&` or `$1`) from corrupting the file path.</comment>

<file context>
@@ -0,0 +1,122 @@
+}
+
+function buildFormatterCommand(command: string[], filePath: string): string[] {
+  return command.map((arg) => arg.replace(/\$FILE/g, filePath))
+}
+
</file context>
Fix with Cubic

Comment on lines +139 to +140
const formattedMeta = buildSuccessMeta(
filePath,
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 23, 2026

Choose a reason for hiding this comment

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

P2: When a file is renamed, the tool returns the old filePath in its metadata instead of the new path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tools/hashline-edit/hashline-edit-executor.ts, line 139:

<comment>When a file is renamed, the tool returns the old `filePath` in its metadata instead of the new path.</comment>

<file context>
@@ -129,6 +131,34 @@ export async function executeHashlineEditTool(args: HashlineEditArgs, context: T
+      const formattedContent = Buffer.from(await Bun.file(filePath).arrayBuffer()).toString("utf8")
+      if (formattedContent !== writeContent) {
+        const formattedEnvelope = canonicalizeFileText(formattedContent)
+        const formattedMeta = buildSuccessMeta(
+          filePath,
+          oldEnvelope.content,
</file context>
Suggested change
const formattedMeta = buildSuccessMeta(
filePath,
const effectivePath = rename && rename !== filePath ? rename : filePath
const formattedMeta = buildSuccessMeta(
effectivePath,
Fix with Cubic

@code-yeongyu code-yeongyu merged commit 331f7ec into dev Mar 23, 2026
7 checks passed
@code-yeongyu code-yeongyu deleted the fix/issue-2117 branch March 23, 2026 09:49
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

0 issues found across 4 files (changes from recent commits).

Requires human review: Auto-approval blocked by 3 unresolved issues from previous reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Formatters defined in opencode don't work

1 participant