feat(tools): read_file slice/gutter + new grep_file (PR-R1)#20
Merged
Conversation
Bite-sized plan covering: - read_file: add offset/limit (1-based line slicing), line-number gutter on output, touchesFS:false. Keeps existing 64KB truncate semantics for the no-args case. - grep_file: new server-side regex tool over R2 text files. path | prefix scope, regex_flags, context_lines, max_matches. Binary files skipped. - One-sentence main-agent system prompt update. - 9-step agent-driven smoke (no unit tests per memory). - PR raised against main, independent of the sync-agent spec PR. Decisions locked pre-plan: keep truncate semantics (not error-on-overflow), line gutter on output, regex string for grep pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Smoke caught read_file refusing seed-sample.ndjson as binary. Root cause: .ndjson wasn't in the EXT_CONTENT_TYPE map, so the path-extension sniff fell back to application/octet-stream — and per commit-driver.ts:637 the materialize step always re-sniffs from path on canonical writes, so custom contentTypes passed at writeFile() time don't survive the git-pipeline commit anyway. The only viable fix is the extension map. - env-fs.ts: map ndjson + jsonl → application/x-ndjson. - fs-tools.ts: extend TEXT_CT_RE to accept application/x-ndjson and application/x-jsonl so read_file / grep_file recognize them as text. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Smoke caught a second issue: even after fixing the EXT_CONTENT_TYPE map, canonical R2 holds the old "application/octet-stream" httpMetadata for files written before the fix. materializeMainToCanonical only writes canonical when the git blob sha changes — same-content rewrites don't refresh stored metadata. So a fresh sniff returns x-ndjson, but the stored type stays binary, and the tool's gate rejects. Adds `resolveTextContentType(stored, path)`: trust stored if it's already text-shaped; otherwise re-sniff from the path extension and prefer that when it lands in TEXT_CT_RE. Applied to read_file, edit_file, and grep_file (both single-path and prefix-loop branches). The error path still surfaces the stored type so the agent's reasoning matches what the agent sees in stat() output. Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Summary
read_filewithoffset/limit(1-based line slicing) plus a line-number gutter on output. Default no-args behavior unchanged except for the gutter (still truncates at 64KB withtruncated:true).grep_file: server-side regex over R2 text files. Scope by singlepathor recursiveprefix. Returns matching lines + N context lines. Caps at 100 matches by default. Binary files skipped automatically.touchesFS: falseonread_file(small bug fix — read-only tools shouldn't open a per-call worktree).grep_fileand teaching thetruncated:true → switch to greppattern.Stacked on #19
Base =
feat/sync-agent-designbecause the plan references the spec doc on that branch. Retarget tomainonce #19 merges.Spec reference
docs/superpowers/specs/2026-05-17-sync-agent-design.md§3.1.1 + §6 PR-R1Test plan
r2://my-files/read_fileon the NDJSON returns 64KB truncated head with line gutter + sizeread_filewith{offset: 2500, limit: 11}returns the explicit slice, no truncationgrep_fileonpathfor a single id returns exactly one match with contextgrep_fileonprefixmatches across the small filegrep_filewithmax_matches: 50returns 50 matches +truncated: truegrep_filewithpattern: "[unclosed"returns{ok:false, error:"bad_pattern"}{ok:false, error:"binary_not_supported"}🤖 Generated with Claude Code