Summary
Feature request: make AFT's hoisted edit tool composable with third-party edit engines, specifically oh-my-openagent's hashline_edit (LINE#ID-anchored engine). Goal: run AFT's guardrails (permission preview, external-directory checks, diff formatting) around the hashline engine instead of only around opencode's builtin edit.
Why this matters
AFT's edit is a wrapper, not an engine — it dispatches to whatever the harness resolves as edit and adds guardrails around that call:
// packages/opencode-plugin/src/tools/hoisted.ts (createEditTool)
const preview = await callToolCall(ctx, context, "edit", rawArgs, { preview: true })
// ...permission gating, external-directory check...
const data = await callToolCall(ctx, context, "edit", rawArgs)
opencode's ToolRegistry emits [...builtin, ...plugin tools] with no dedupe, and the harness resolves one implementation per tool id (last-registered wins). So today the user must choose one:
hashline_edit: true in OMO → OMO's edit shadows AFT's wrapper entirely; guardrails are lost, and the model gets only the LINE#ID engine.
hashline_edit off (default) → AFT's wrapper runs over opencode's builtin edit; hashline precision is unavailable.
Both tools have value: hashline's hash-verified line anchors make wrong-line edits nearly impossible; AFT's guardrails add preview/permission/external-dir safety that hashline lacks. Users of both plugins should not have to pick one.
Proposed design (AFT side)
- Add a configurable edit-engine target (e.g.
edit_engine in aft.jsonc, or extend hoist_builtin_tools semantics) that lets AFT's wrapper target a named tool instead of edit:
- default:
"edit" (current behavior — wraps builtin / whatever owns the name)
- optional:
"hashline_edit" — AFT's wrapper dispatches to that name
- Arg-schema adaptation: when the target engine is not opencode's builtin schema, translate AFT's
edits[] ({oldString,newString} / {startLine,endLine,content}) into the target's schema. For hashline this means producing {op: replace|append|prepend, pos/end: "LINE#ID", lines} operations — which requires the caller to know the LINE#ID anchors, so:
- Guardrail ordering: keep permission-preview + external-dir checks on AFT's side (they're schema-agnostic), and let the engine-specific translation live in a small adapter module (or accept hashline-shaped args pass-through when targeting
hashline_edit).
Simplest viable slice: when edit_engine: "hashline_edit", AFT passes hashline-shaped args through unchanged (the model reads LINE#ID anchors via the read tool anyway) and keeps its guardrails on top. Full translation of builtin-shaped → hashline-shaped args can be a follow-up.
Enabling prerequisite (OMO side, referenced for coordination)
- oh-my-openagent registers hashline under a stable distinct tool id (
hashline_edit) rather than only clobbering edit, so another plugin can target it without a name collision. See code-yeongyu/oh-my-openagent packages/omo-opencode/src/tools/hashline-edit/ (tools.ts registers { edit: createHashlineEditTool(ctx) } when config.hashline_edit).
- Document the LINE#ID schema (
{line}#{hash} anchors, ops replace/append/prepend) as the engine contract.
Acceptance criteria
Related
Happy to prototype the adapter if the direction is approved.
Summary
Feature request: make AFT's hoisted
edittool composable with third-party edit engines, specifically oh-my-openagent'shashline_edit(LINE#ID-anchored engine). Goal: run AFT's guardrails (permission preview, external-directory checks, diff formatting) around the hashline engine instead of only around opencode's builtin edit.Why this matters
AFT's
editis a wrapper, not an engine — it dispatches to whatever the harness resolves aseditand adds guardrails around that call:opencode's ToolRegistry emits
[...builtin, ...plugin tools]with no dedupe, and the harness resolves one implementation per tool id (last-registered wins). So today the user must choose one:hashline_edit: truein OMO → OMO'seditshadows AFT's wrapper entirely; guardrails are lost, and the model gets only the LINE#ID engine.hashline_editoff (default) → AFT's wrapper runs over opencode's builtin edit; hashline precision is unavailable.Both tools have value: hashline's hash-verified line anchors make wrong-line edits nearly impossible; AFT's guardrails add preview/permission/external-dir safety that hashline lacks. Users of both plugins should not have to pick one.
Proposed design (AFT side)
edit_enginein aft.jsonc, or extendhoist_builtin_toolssemantics) that lets AFT's wrapper target a named tool instead ofedit:"edit"(current behavior — wraps builtin / whatever owns the name)"hashline_edit"— AFT's wrapper dispatches to that nameedits[]({oldString,newString}/{startLine,endLine,content}) into the target's schema. For hashline this means producing{op: replace|append|prepend, pos/end: "LINE#ID", lines}operations — which requires the caller to know the LINE#ID anchors, so:hashline_edit).Simplest viable slice: when
edit_engine: "hashline_edit", AFT passes hashline-shaped args through unchanged (the model reads LINE#ID anchors via the read tool anyway) and keeps its guardrails on top. Full translation of builtin-shaped → hashline-shaped args can be a follow-up.Enabling prerequisite (OMO side, referenced for coordination)
hashline_edit) rather than only clobberingedit, so another plugin can target it without a name collision. Seecode-yeongyu/oh-my-openagentpackages/omo-opencode/src/tools/hashline-edit/(tools.tsregisters{ edit: createHashlineEditTool(ctx) }whenconfig.hashline_edit).{line}#{hash}anchors, ops replace/append/prepend) as the engine contract.Acceptance criteria
edit_engine: "hashline_edit"set, the model'seditcalls run hashline's engine and AFT's guardrails (preview + permission prompt appear; external-directory edits blocked).edit_engineunset) behaves exactly as today.Related
docs/tools.md(hoisted tools replace host builtins),docs/config.md(hoist_builtin_tools,disabled_tools)disabled_tools)Happy to prototype the adapter if the direction is approved.