fix(repos): return the file body and allow the root path #16

Merged
dsh merged 1 commit from fix/3-repo-contents-body into master 2026-10-23 22:52:53 +00:00
Owner

What

gitea_repo_contents returned a filename instead of the file:

path: AGENTS.md → AGENTS.md
path: ""        → gitea_repo_contents failed: invalid owner, repo, or filePath

Two causes: formatToolResult hit if (data.path) return data.path before any content-aware branch, and getContents() rejected an empty path so the root (an array response) was unreachable.

This PR:

  • adds a gitea_repo_contents formatter: base64-decodes files, shows File: <path> (<size>, encoding), html_url and download_url;
  • truncates the body at 20 000 chars with an explicit … [truncated … download the full file at …] note;
  • distinguishes an empty file from an API response with no content (submodules);
  • allows path: "" and requests /contents for the repository root (generic listing handles the array);
  • keeps traversal rejection (../) and path segment encoding.

Verification

  • test/repo-contents.test.mjs: body + links, unicode round-trip, 25 000-char truncation bound, empty/missing content, root /contents URL, path encoding and traversal rejection.
  • npm test 66/66, leak scan clean, node --check clean.
  • Live through the running plugin:
    • AGENTS.md → File: AGENTS.md (38003 bytes, encoding: base64) + body, truncated at 20 000 chars with the download link;
    • path: "" → root listing (.forgejo, AGENTS.md, lib, …).

Acceptance (from #3)

  • AGENTS.md → first ~20 KB of text plus a download link.
  • Root path → directory listing.
  • A 500 KB file returns a truncated body, not a 500 KB tool result.

Closes #3.

## What `gitea_repo_contents` returned a filename instead of the file: ``` path: AGENTS.md → AGENTS.md path: "" → gitea_repo_contents failed: invalid owner, repo, or filePath ``` Two causes: `formatToolResult` hit `if (data.path) return data.path` before any content-aware branch, and `getContents()` rejected an empty path so the root (an array response) was unreachable. This PR: - adds a `gitea_repo_contents` formatter: base64-decodes files, shows `File: <path> (<size>, encoding)`, `html_url` and `download_url`; - truncates the body at **20 000 chars** with an explicit `… [truncated … download the full file at …]` note; - distinguishes an empty file from an API response with no content (submodules); - allows `path: ""` and requests `/contents` for the repository root (generic listing handles the array); - keeps traversal rejection (`../`) and path segment encoding. ## Verification - `test/repo-contents.test.mjs`: body + links, unicode round-trip, 25 000-char truncation bound, empty/missing content, root `/contents` URL, path encoding and traversal rejection. - `npm test` 66/66, leak scan clean, `node --check` clean. - Live through the running plugin: - `AGENTS.md` → `File: AGENTS.md (38003 bytes, encoding: base64)` + body, truncated at 20 000 chars with the download link; - `path: ""` → root listing (`.forgejo`, `AGENTS.md`, `lib`, …). ## Acceptance (from #3) - [x] `AGENTS.md` → first ~20 KB of text plus a download link. - [x] Root path → directory listing. - [x] A 500 KB file returns a truncated body, not a 500 KB tool result. Closes #3.
The formatter matched data.path before any content-aware branch, so files printed their path; getContents also rejected an empty path. Decode base64 with a 20 KB visible cap and links, and request /contents for the root listing.
dsh merged commit 077ffc6034 into master 2026-10-23 22:52:53 +00:00
Sign in to join this conversation.
No description provided.