Adding file attachment with and stdout redirection.

This commit is contained in:
Morpheus Sandmann
2026-09-17 13:48:39 +01:00
parent 1c92cdff60
commit be5b2b8ff0
6 changed files with 320 additions and 26 deletions
+110 -1
View File
@@ -2,8 +2,10 @@
from __future__ import annotations
import io
import json
from shellbound import attach
from shellbound import modes
from shellbound.personas import (
AmbiguousPersona,
@@ -231,4 +233,111 @@ def test_renderer_plain_content_streams():
renderer = StreamRenderer(plain=True)
renderer.add_content("just text")
assert renderer.content_parts == ["just text"]
assert renderer.finish() == "just text"
assert renderer.finish() == "just text"
# ---- attachments / piped stdin --------------------------------------------
def _warnings():
seen: list[str] = []
def warn(message: str) -> None:
seen.append(message)
return seen, warn
def test_fence_structure():
assert attach.fence("file: a.txt", "one\ntwo") == (
"[file: a.txt]\n```text\none\ntwo\n```"
)
assert attach.fence("stdin", "x", note="[truncated]") == (
"[stdin]\n```text\nx\n[truncated]\n```"
)
assert attach.fence("clipboard", "\n") == "[clipboard]\n```text\n\n```"
def test_assemble_order_and_stripping():
blocks = [
attach.fence("stdin", "pipe"),
attach.fence("file: a.txt", "file body"),
attach.fence("clipboard", "clip"),
]
assert attach.assemble("hello", blocks) == (
"hello\n\n"
"[stdin]\n```text\npipe\n```\n\n"
"[file: a.txt]\n```text\nfile body\n```\n\n"
"[clipboard]\n```text\nclip\n```"
)
def test_assemble_no_prompt_or_blocks():
assert attach.assemble("", []) == ""
assert attach.assemble("just prompt", []) == "just prompt"
assert attach.assemble("", [attach.fence("stdin", "s")]) == "[stdin]\n```text\ns\n```"
def test_read_file_binary_skipped(tmp_path):
p = tmp_path / "bin.dat"
p.write_bytes(b"\x00\x01\x02")
seen, warn = _warnings()
assert attach.read_file(str(p), 1024, warn=warn) is None
assert any("binary" in m for m in seen)
def test_read_file_truncates(tmp_path):
p = tmp_path / "big.txt"
p.write_text("a" * 5000)
seen, warn = _warnings()
result = attach.read_file(str(p), 100, warn=warn)
assert result.truncated
assert result.content == "a" * 100
assert not result.binary
assert any("truncating" in m for m in seen)
def test_read_file_missing_raises(tmp_path):
try:
attach.read_file(str(tmp_path / "nope.txt"), 1024)
except FileNotFoundError:
pass
else:
raise AssertionError("expected FileNotFoundError")
def test_read_stdin_truncates_and_warns():
seen, warn = _warnings()
result = attach.read_stdin(io.BytesIO(b"b" * 500), 10, warn=warn)
assert result.truncated and result.content == "b" * 10
assert any("truncating" in m for m in seen)
def test_read_stdin_empty_and_binary():
result = attach.read_stdin(io.BytesIO(b""), 100)
assert not result.content and not result.truncated
seen, warn = _warnings()
result = attach.read_stdin(io.BytesIO(b"\x00data"), 100, warn=warn)
assert result.binary and result.content == "\x00data"
assert any("binary" in m for m in seen)
def test_truncation_note():
assert attach.truncation_note("stdin", 42) == "[truncated: kept the first 42 bytes of stdin]"
def test_config_attach_max_bytes():
from shellbound.config import Config
assert Config({}).attach_max_bytes == 102_400
assert Config({"attach_max_bytes": "oops"}).attach_max_bytes == 102_400
assert Config({"attach_max_bytes": 0}).attach_max_bytes == 1
assert Config({"attach_max_bytes": 5_000}).attach_max_bytes == 5_000
def test_config_attach_max_bytes_environ(tmp_path, monkeypatch):
from shellbound import config as c
monkeypatch.setattr(c, "CONFIG_FILE", tmp_path / "missing.json")
monkeypatch.setenv("SHELLBOUND_ATTACH_MAX_BYTES", "4048")
assert c.Config.load().attach_max_bytes == 4048