Skip to content

[codex] add Go read-only core tools - #47

Merged
Vasanthdev2004 merged 2 commits into
mainfrom
codex/go-m0-tools-core
Jun 4, 2026
Merged

[codex] add Go read-only core tools#47
Vasanthdev2004 merged 2 commits into
mainfrom
codex/go-m0-tools-core

Conversation

@Vasanthdev2004

@Vasanthdev2004 Vasanthdev2004 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add the first Go internal/tools package for Zero.
  • Define tool metadata, JSON-schema-compatible parameter shapes, safety metadata, results, and registry safe-run behavior.
  • Add read-only core tools: read_file, list_directory, glob, and grep.
  • Keep tools workspace-scoped and read-only for this first Vasanth M0 slice.
  • Add Go tests for metadata/schema, registry behavior, workspace rejection, line ranges, directory listing, glob matching, and grep output modes.

Why

This starts Vasanth's Go M0 tools/TUI path without jumping into Bubble Tea or mutation tools too early. The TUI and agent loop can now consume a clean Go tool contract and safe read/search tools.

Validation

Local:

  • git diff --cached --check passed before commit.
  • git diff --check passed.
  • bun run typecheck passed.
  • bun test ./tests --timeout 15000 passed: 277 tests.
  • bun run build passed.
  • bun run smoke:build passed.

Blocked locally:

  • go test ./... and go build ./cmd/zero could not run because go is not installed on this Windows machine.

CI:

  • Existing CI runs go test ./... and go build ./cmd/zero on Ubuntu, macOS, and Windows.

Summary by CodeRabbit

  • New Features

    • Added file tools: read file, list directory, glob matching, and grep-style search, plus a registry exposing read-only tools and safety metadata.
  • Tests

    • Added comprehensive unit tests covering file operations, glob/grep behaviors, directory listing, and registry safety paths.
  • Chores

    • Introduced core tooling types, workspace path handling, and argument validation utilities to support the new tools.

@Vasanthdev2004
Vasanthdev2004 marked this pull request as ready for review June 4, 2026 07:22
@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Zero automated PR review

Verdict: No blockers found

Blockers

  • None found.

Validation

  • [pass] Diff hygiene: git diff --check
  • [pass] Typecheck: bun run typecheck
  • [pass] Tests: bun run test
  • [pass] Build: bun run build
  • [pass] Smoke build: bun run smoke:build

Scope

Head: 0c63a3427411
Changed files (10): internal/tools/args.go, internal/tools/file_tools_test.go, internal/tools/glob.go, internal/tools/grep.go, internal/tools/list_directory.go, internal/tools/read_file.go, internal/tools/registry.go, internal/tools/registry_test.go, internal/tools/types.go, internal/tools/workspace.go

This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3fdd4b04-d7b5-49e3-a8ac-77e78329c586

📥 Commits

Reviewing files that changed from the base of the PR and between c44e06a and 0c63a34.

📒 Files selected for processing (5)
  • internal/tools/file_tools_test.go
  • internal/tools/glob.go
  • internal/tools/grep.go
  • internal/tools/read_file.go
  • internal/tools/workspace.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/tools/read_file.go
  • internal/tools/file_tools_test.go
  • internal/tools/grep.go
  • internal/tools/glob.go

Walkthrough

Adds an internal tools subsystem: core Tool types and arg helpers, workspace path safety, four read-only file tools (read_file, list_directory, glob, grep), a registry enforcing PermissionAllow, and unit tests validating functionality and metadata.

Changes

Tools Package

Layer / File(s) Summary
Core type system and foundational utilities
internal/tools/types.go, internal/tools/args.go, internal/tools/workspace.go
Type contracts define the Tool interface, Result/Schema types, and enums. Argument helpers (stringArg, boolArg, intArg, intPtr) validate typed values from generic maps. Workspace utilities normalize roots, resolve paths safely (symlink-aware), and filter ignored directories.
Read file tool
internal/tools/read_file.go
ReadFileTool constructor and Run: read workspace-relative files, normalize CRLF, support 1-based inclusive start_line/end_line, enforce end_line >= start_line, apply max_lines truncation, and return formatted numbered output with Truncated/Meta.
List directory tool
internal/tools/list_directory.go
ListDirectoryTool defines parameters (path, recursive, max_depth), resolves workspace path, walks directories (skipping ignored dirs), sorts directories before files, enforces depth limits, formats indented listing, and handles empty directories.
Glob tool
internal/tools/glob.go
GlobTool compiles slash-normalized glob patterns to anchored regex, scans workspace with WalkDir, optionally includes directories, skips ignored dirs, sorts matches, applies limit truncation, and returns newline-separated matches with Meta.pattern.
Grep tool
internal/tools/grep.go
GrepTool compiles regex (supports case_insensitive/-i), resolves target path, optionally filters candidates by glob, scans files line-by-line aggregating filename, 1-based line numbers, matched text, and per-line hit counts; supports content, files_with_matches, and count output modes with head_limit truncation.
Registry and execution orchestration
internal/tools/registry.go
Registry manages registration and lookup, Run enforces tool.Safety().Permission == PermissionAllow before delegating to tool.Run. CoreReadOnlyTools returns the four read-only file tools bound to a workspace root.
Test coverage
internal/tools/file_tools_test.go, internal/tools/registry_test.go
Unit tests cover read_file ranges and workspace escapes, list_directory recursion and ignored dirs, glob matching/limit/include_dirs, grep search and output modes, registry metadata and safe execution path, unknown-tool error handling, and include writeTestFile test helper.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • anandh8x
  • gnanam1990
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically identifies the main change: adding Go read-only core tools to the tools package. It directly corresponds to the primary objective and the bulk of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/go-m0-tools-core

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/tools/file_tools_test.go`:
- Around line 161-167: The test currently only asserts the output string from
NewGrepTool(...).Run(...); also assert the returned result's status equals
StatusOK so execution errors are caught (e.g., add an assertion that the result
from NewGrepTool(root).Run(...) has result.Status == StatusOK). Reference the
Run call and the result variable (count) and use the StatusOK constant to
perform the status check before asserting the output string.
- Around line 95-100: The test currently uses strings.Count(result.Output,
".go") which is brittle; instead parse result.Output into lines and count only
the lines that represent matched file paths (e.g., use a regexp on result.Output
like regexp.MustCompile(`(?m)^[^\n]*\.go\b(:\d+)?`) to find path/line matches)
and assert that the number of those matches equals 1; update the assertion that
references result.Output to use this filtered count and keep the existing check
on result.Truncated unchanged.

In `@internal/tools/glob.go`:
- Around line 94-97: The WalkDir callback currently swallows filesystem errors
by returning nil when walkErr != nil; update the callback used in the
filepath.WalkDir call (inside the glob traversal function) to return the
incoming walkErr (or wrap it with context) instead of nil so WalkDir will
propagate the error to the caller; ensure the surrounding function (the glob
traversal function) checks and returns any error from filepath.WalkDir to the
caller so filesystem traversal failures are not hidden.

In `@internal/tools/grep.go`:
- Around line 169-172: The WalkDir callback passed to filepath.WalkDir currently
ignores the incoming walkErr, suppressing filesystem errors; in the anonymous
func used by filepath.WalkDir (parameters path, entry, walkErr) return the
walkErr (or wrap it with contextual info using fmt.Errorf) instead of nil so
upstream callers receive the error; alternatively, explicitly handle known
ignorable errors but log and propagate other errors from the WalkDir callback
(refer to the anonymous func around filepath.WalkDir in grep.go).

In `@internal/tools/read_file.go`:
- Around line 66-70: The current splitting logic inflates line counts for files
ending with a newline because strings.Split on "\n" yields a trailing empty
element; before computing lines and using variables like lines, total, startLine
and okResult/relativePath, trim a single trailing "\n" (after the CRLF
replacement) or otherwise remove a final empty line (e.g., via
strings.TrimSuffix or similar) so that splitting produces the true number of
lines; update the code that builds lines to trim the trailing newline before
splitting and keep the rest of the checks (startLine > total) unchanged.

In `@internal/tools/workspace.go`:
- Around line 51-66: The current check only resolves a single Readlink and can
miss symlinked parent segments; instead fully resolve symlinks before enforcing
the workspace boundary: compute absolute paths for both root and the requested
target (use filepath.Abs on target/root), then call filepath.EvalSymlinks (or
iteratively EvalSymlinks on path prefixes) on the absolute target (and root) to
get their real paths, and only then use filepath.Rel and the existing checks
(relative == ".." etc.) against the resolved paths; ensure errors from
EvalSymlinks are propagated and keep references to target, root,
filepath.EvalSymlinks, filepath.Abs, filepath.Rel and requestedPath to locate
the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e97ff2ca-0092-40df-919e-ddf7a7bb226c

📥 Commits

Reviewing files that changed from the base of the PR and between 711a5f9 and c44e06a.

📒 Files selected for processing (10)
  • internal/tools/args.go
  • internal/tools/file_tools_test.go
  • internal/tools/glob.go
  • internal/tools/grep.go
  • internal/tools/list_directory.go
  • internal/tools/read_file.go
  • internal/tools/registry.go
  • internal/tools/registry_test.go
  • internal/tools/types.go
  • internal/tools/workspace.go

Comment thread internal/tools/file_tools_test.go
Comment thread internal/tools/file_tools_test.go
Comment thread internal/tools/glob.go
Comment thread internal/tools/grep.go
Comment thread internal/tools/read_file.go Outdated
Comment thread internal/tools/workspace.go Outdated

@anandh8x anandh8x left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What's good

  • Clean separation in internal/tools/types.go: Tool interface with baseTool covering the four boilerplate methods, JSON-schema-compatible Schema / PropertySchema, and the SideEffect / Permission enums. readOnlySafety is a tidy factory.
  • Workspace boundary is enforced correctly: resolveWorkspacePath (internal/tools/workspace.go:32-69) runs filepath.EvalSymlinks on both the root and the target before the .. / abs-path check, so symlinked parent segments can't smuggle a path out of the workspace. This was one of CodeRabbit's earlier findings and is now done.
  • All four tools share a single ignoredDirectories set (.git, node_modules, dist, build, .next, .turbo, coverage, .cache, tmp, temp) via shouldSkipDirectory — used consistently by glob.scanGlob, grep.grepFiles, and listDirectoryEntries.
  • WalkDir callbacks now propagate walkErr (both internal/tools/glob.go:96-98 and internal/tools/grep.go:171-173), so filesystem traversal failures reach the caller instead of being silently dropped.
  • read_file.go:64-69 trims a single trailing \n before splitting, so a file ending in \n reports the correct line count. Also normalises CRLF → LF and treats start_line > total as a soft "past the end" result rather than an error.
  • Registry.Run (internal/tools/registry.go:30-41) gates on tool.Safety().Permission == PermissionAllow before delegating — gives a single safe-run choke point for future tools.
  • Test coverage: metadata + schema invariants, workspace rejection, line ranges, recursive listing with junk-dir skipping, glob ** + limit, glob include-dirs, grep content / files_with_matches / count modes, and the glob test now uses regexp.MustCompile to count .go matches instead of strings.Count(".go") (also a prior CodeRabbit concern, now addressed).
  • Follow-up commit 0c63a34 fix: harden go read-only tools addresses each of CodeRabbit's actionable items; CI is green on all four matrices (ubuntu, macos, windows, Performance Smoke) and CodeRabbit reports SUCCESS on the latest SHA.

Observations (non-blocking)

  1. read_file on a missing path leaks the absolute path via EvalSymlinks. resolveWorkspacePath runs filepath.EvalSymlinks(target) before os.ReadFile, so a missing file surfaces as Error reading file <requested>: lstat /workspace/<requested>: no such file or directory (internal/tools/read_file.go:54-58). The message is technically informative but the EvalSymlinks failure mode is opaque to a non-engineer caller — catching errors.Is(err, fs.ErrNotExist) (or a ENOENT check) and returning a dedicated "file not found" result would tidy this up.

  2. listDirectoryEntries swallows subdirectory errors silently. When the recursive os.ReadDir on a child fails, the child is dropped from the output with no diagnostic (internal/tools/list_directory.go:88-92). Graceful degradation is fine for a read tool, but list_directory --recursive will silently show a directory as empty when a permissions error blocks its children. A surface as a trailing ! permission denied: <dir> line, or at least a Meta field, would help.

  3. head_limit has no upper bound in grep. intArg(args, "head_limit", 50, 1, 0) passes max=0 (meaning "no max" in the helper's convention), so a caller can ask for a million-line result. Glob caps at 1000; grep is unbounded. Picking a sensible cap (1000?) and clamping would mirror the glob behaviour.

  4. shouldSkipDirectory is not unit-tested directly. It's exercised transitively through every other tool's tests, but a TestIgnoredDirectories_AreSkipped asserting the exact set and behaviour would document the policy and catch accidental additions.

  5. The "-i" schema key is unusual. internal/tools/grep.go:37 registers the case-insensitive flag under the property name "-i". Standard for CLI parity, but the tool interface's JSON-schema consumer (TUI / LLM-side) might prefer a separate case_insensitive (already present alongside it) and treat -i as an alias. Worth a doc comment in the Schema description.

No blockers. Ship it.

@Vasanthdev2004
Vasanthdev2004 merged commit c9d0856 into main Jun 4, 2026
6 checks passed
@Vasanthdev2004
Vasanthdev2004 deleted the codex/go-m0-tools-core branch June 28, 2026 08:27
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.

2 participants