feat: replace native edit with hashline-powered custom tool + merge oh-my-opentools patterns#5
Open
aorizondo wants to merge 2 commits into
Open
feat: replace native edit with hashline-powered custom tool + merge oh-my-opentools patterns#5aorizondo wants to merge 2 commits into
aorizondo wants to merge 2 commits into
Conversation
…h-my-opentools patterns
## Problem
The hashline plugin v1 annotated reads with stable #HL refs (e.g.
`12#A3F#9BC`) but the edit workflow was broken: `translateHashlineEditArgs`
converted hashline operations to full-file `oldString`/`newString`, making edits
fragile — any file change between read and edit caused failure. The edit hook
also had a missing `.js` extension in `src/index.ts` that prevented ESM loading.
## Solution
Replace the entire edit mechanism with a properly registered `tool.edit`
custom tool that:
1. **Registers via `Hooks.tool.edit`** — overrides the native edit tool
through the standard SDK `tool()` API (zod schema + execute handler)
2. **Applies edits directly on file content** — reads the file, applies
splice-based operations (replace, range replace, append, prepend) on the
line array, and writes back. No fragile full-file `oldString`/`newString`
translation needed.
3. **Supports both hashline AND legacy modes** — accepts `operations[]` with
hashline refs for batch editing, plus `oldString`/`newString` fallback
for simple text replacements.
4. **File text canonicalization** — handles BOM stripping/restore and CRLF/LF
normalization before editing (ported from oh-my-opentools).
5. **HashlineMismatchError with context** — when refs become stale, shows a
`>>>`-marked diff of the ±2 lines around each mismatch with corrected refs.
6. **ctx.ask() permissions** — calls `context.ask({ permission: 'edit',
patterns: [filePath], always: ['*'] })` for proper OpenCode permission
integration.
7. **SHA1 hex + anchor hash (3-4 chars)** — uses the more collision-resistant
hash format from opencode-hashline v1 (4096-65536 values), NOT the 2-char
CID format (256 values) from oh-my-opentools.
## Structural changes
- Deleted `.opencode/` directory — all source moves to `src/`
- Deleted `tsconfig.build.json` — replaced by single `tsconfig.json`
- Deleted `src/codemap.md` — obsolete, described tool registration that
didn't exist
- New `tsconfig.json` — `strict: true`, `rootDir: ./src`, `sourceMap: true`
- `src/index.ts` — entry point registers `tool.edit` + 4 hooks (read
annotation, system transform, chat annotation, tool definitions)
- `src/hash.ts` — SHA1 hex hashing + anchor hash + computeFileRev
- `src/ref.ts` — ref parsing, formatting, validation
- `src/file-text.ts` — BOM/CRLF canonicalization
- `src/edit-ops.ts` — operations engine: primitives (set/replace/insert/
append/prepend) + orchestrator + dedup + overlap detection +
HashlineMismatchError
- `src/edit-executor.ts` — file I/O, ref resolution, ctx.ask(), metadata
- `src/edit-tool.ts` — `tool()` definition with zod schema
- `src/hooks.ts` — `tool.execute.after` for read annotation,
`experimental.chat.system.transform` for instruction injection,
`chat.message` for file annotation, `tool.definition` for descriptions
- `src/shared.ts` — config loading, LRU cache, format/strip helpers,
exclude patterns
## Tests
33 tests covering: hash functions, ref parsing/formatting, file-text
canonicalization, all edit operations (replace, range, append, prepend,
insert after/before, noop detection), mismatch errors with remaps,
format/strip round-trip, shouldExclude, cache, cache eviction, hooks
(read annotation, system transform, tool definitions), and full
integration tests of the edit executor with real file I/O.
## Comparison with PR AngDrew#4
This implementation differs from IshanArya's PR AngDrew#4 in several key ways:
- **Zero external dependencies**: no `diff` library needed
- **Canonicalization**: BOM + CRLF handling (PR AngDrew#4 lacks this)
- **Dual-mode API**: supports both `operations[]` and
`oldString`/`newString` (PR AngDrew#4 only supports operations[])
- **ctx.ask()**: proper OpenCode permission integration (PR AngDrew#4 omits this)
- **Deduplication**: identical replace edits at the same position are
collapsed (PR AngDrew#4 relies on core engine only)
- **Cleaner structure**: all source in `src/`, outputs to `dist/` with
`rootDir: ./src` (PR AngDrew#4 keeps `.opencode/`)
- **33 tests vs ~10**: broader coverage of all edge cases
Closes AngDrew#3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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 plugin v1 annotated reads with stable
#HLrefs (e.g.12#A3F#9BC) but the edit workflow was broken:translateHashlineEditArgsconverted hashline operations to full-fileoldString/newString, making edits fragile — any file change between read and edit caused failure..jsextension insrc/index.tsimport prevented ESM loading.strict: falseTypeScript config allowed implicit anys.rootDir: "."produced nesteddist/src/index.jspath..opencode/— a hidden directory not suitable for npm distribution.Solution: Hybrid Architecture
This PR merges the best patterns from opencode-hashline v1 (SHA1 hex hashing, anchor hash, read annotation, system prompt injection) with oh-my-opentools (direct file manipulation, edit tool registration, file canonicalization, strict TypeScript).
Core changes
tool.editapplies edits directly on line arrays.opencode/directory for plugin sourcesrc/, compiled todist/with clean rootDirstrict: false,rootDir: "."strict: true,rootDir: ./src.jsextension in entry import.jsextensionctx.ask()integrationHashlineMismatchErrorwith context diff + corrected refsWhat stays the same
tool.execute.afterhookexperimental.chat.system.transformchat.messagehookopencode-hashline.jsonconfig (global + project)Files changed
Deleted (12 files):
.opencode/directory moved tosrc/,tsconfig.build.json,src/codemap.mdCreated (9 files):
src/hash.ts,src/ref.ts,src/file-text.ts,src/edit-ops.ts,src/edit-executor.ts,src/edit-tool.ts,src/hooks.ts,src/shared.ts,tsconfig.jsonModified (4 files):
src/index.ts,package.json,opencode.json, test suite (10 -> 33 tests)Test results
Comparison with PR #4
difflibraryCloses #3