Skip to content

feat: add attachments command for download and inspection#121

Merged
aarondpn merged 1 commit into
mainfrom
feat/attachments-command
Jun 15, 2026
Merged

feat: add attachments command for download and inspection#121
aarondpn merged 1 commit into
mainfrom
feat/attachments-command

Conversation

@aarondpn

Copy link
Copy Markdown
Owner

What & why

There is currently no convenient way to download a Redmine attachment through the CLI. Users have to fetch metadata with redmine api /attachments/<id>.json to find the content_url, then curl it with a hand-extracted API key (because redmine api only prints text/JSON and can't stream binary). This PR makes attachment retrieval a one-liner that reuses the CLI's existing auth, profile handling, and server config.

What's new

New attachments command group (aliases attachment, a):

  • redmine attachments get <id> — print attachment metadata in table/json/csv.
  • redmine attachments download <id> — download the file, authenticating with the active profile (no curl, no manual key).
    • -d/--dir <dir> saves into a directory under the real filename.
    • --path <file> saves to an exact path; --path - streams to stdout.
    • Default destination is ./<filename> in the current directory.
    • Streams to disk, never buffering the whole file in memory.

Attachment discoverability on issues get:

  • --attachments lists the issue's attachments (id, filename, size, content type) so you can immediately download one.
  • --download-attachments <dir> downloads every attachment of the issue into <dir> in one step.

Supporting changes:

  • Surface attachments on the typed Issue model, so include=attachments works for both the CLI and MCP get_issue (consolidated the previously wiki-only Attachment type).
  • New read-only MCP tool get_attachment.

Flag-naming note

The original proposal suggested -o <path> / -o - for download, but -o/--output is the global output-format flag across this CLI. To avoid overloading it, the destination is controlled by --dir/--path (the proposal explicitly allowed distinct names), and -o keeps its usual meaning — download respects -o json to emit a structured result object describing the saved file. This is documented in --help and the docs.

Security

While implementing the download path I found (and fixed) a credential-leak vector: the API key was attached to every request hop, so a server or content_url that 302-redirects off-site (e.g. to external object storage) would leak the key to that host. authTransport now only attaches credentials (API key or basic auth) when the request targets the configured server host. This protects all request paths, not just downloads, and still follows legitimate redirects. Covered by tests, including an end-to-end cross-host redirect reproduction.

Docs & skill

  • New Attachments command page in English and Simplified Chinese, plus sidebar entry.
  • Updated the issues get page (both languages) for the new flags.
  • The bundled agent skill now instructs the agent to download and inspect attachments (especially images) before answering, since they often contain information not present in the text.

Tests

  • Unit tests for the API layer (metadata, streaming download, host guard, cross-host redirect non-leak), ops, cmdutil download helpers (filename sanitization, dir creation, partial-file cleanup), and the new commands (formats, dir/path/stdout, default cwd, mutual exclusion, empty-attachments path).
  • E2E tests covering discover -> metadata -> download with byte-identical binary integrity (real PNG signature + NUL bytes), stdout streaming, and issues get --download-attachments.

Verification

gofmt clean, golangci-lint 0 issues, go vet clean, all unit tests pass, e2e compiles, docs site builds, MCP codegen and golden in sync.

Add a top-level `attachments` command group (aliases `attachment`, `a`)
so users and agents can download and inspect Redmine attachments without
dropping to `redmine api` plus a manual curl with a hand-extracted key.

- `attachments get <id>`: metadata in table/json/csv.
- `attachments download <id>`: stream the file to disk (-d/--dir, --path,
  --path - for stdout, default ./<filename>). Reuses the active profile's
  auth and streams rather than buffering the whole file in memory.
- `issues get <id> --attachments`: list an issue's attachments with their
  IDs; `--download-attachments <dir>`: download all of them at once.
- Surface attachments on the typed Issue model (CLI and MCP get_issue).
- New read-only MCP tool `get_attachment`.

Security: only attach credentials to the configured server host, so the
API key and basic-auth are never leaked to an off-site host if the server
or an attachment content_url redirects to external object storage.

Update the bundled skill to tell the agent to download and inspect
attachments (especially images), plus docs in English and Chinese, and
unit and e2e tests.
@aarondpn aarondpn merged commit 53385c0 into main Jun 15, 2026
6 checks passed
@aarondpn aarondpn deleted the feat/attachments-command branch June 15, 2026 10:17
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.

1 participant