fix: emit formatter events from hashline-edit tool (fixes #2117)#2768
Merged
code-yeongyu merged 2 commits intodevfrom Mar 23, 2026
Merged
fix: emit formatter events from hashline-edit tool (fixes #2117)#2768code-yeongyu merged 2 commits intodevfrom
code-yeongyu merged 2 commits intodevfrom
Conversation
There was a problem hiding this comment.
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 onproc.exitedbefore drainingstderrcan hang the formatter process if the pipe buffer fills, which is a likely functional blocker. - Additional correctness issues increase regression risk:
replace()insrc/tools/hashline-edit/formatter-trigger.tsshould use a function to avoid$&/$1substitution side effects, andsrc/tools/hashline-edit/hashline-edit-executor.tsreports stale metadata after renames. - Rename handling in
src/tools/hashline-edit/hashline-edit-executor.tsalso 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.tsandsrc/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 |
There was a problem hiding this comment.
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>
| } | ||
|
|
||
| function buildFormatterCommand(command: string[], filePath: string): string[] { | ||
| return command.map((arg) => arg.replace(/\$FILE/g, filePath)) |
There was a problem hiding this comment.
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>
Comment on lines
+139
to
+140
| const formattedMeta = buildSuccessMeta( | ||
| filePath, |
There was a problem hiding this comment.
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, |
e31280e to
f95d3b1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.tsthat emits the same formatter events as OpenCode's native edit tool after successful hashline-edit operations. Also updatedtool-registry.tsandhashline-edit-executor.tsto integrate the formatter trigger.Changes:
src/tools/hashline-edit/formatter-trigger.ts(122 lines)hashline-edit-executor.ts— call formatter trigger after edittools.ts— pass context for formatter integrationtool-registry.ts— minor fix4 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-editby emitting formatter events like the built-in edit tool. Fixes #2117 so tools likeprettier/eslint --fixandfile_editedhooks execute and their changes are saved.src/tools/hashline-edit/formatter-trigger.tsto resolve per-extension formatters from client config and run commands with$FILEsubstitution and env vars.hashline-edit-executorto run after writes, then update metadata and handle renames if formatting changes content.PluginContextthroughcreateHashlineEditTool(ctx)andtool-registryso the trigger can accessclient.config.config-handlerto pick up new formatter settings.FormatterClient).Written for commit 4ba2da7. Summary will update on new commits.