Expand description
A command’s own output: the tail of the file the CLI streams it to.
The CLI’s local-bash tasks write the command’s stdout to a file on disk
rather than sending it over the wire (confirmed in
~/Projects/forge/.claude/skills/claude-cli-upgrade/reference-captures/monitor.jsonl),
so the tail is a file read. The readers are the transport - which
resolves the path from its held conversation and answers a view’s
ReadCallOutput with the result - and the views that hold a path already
(a view tails a Monitor’s file on its own tick).
Two ways a path is found, and the ending frame’s own word wins when both
exist: task_notification.output_file names the file when the task ends,
and until then the path is composed from the task id the wire carries
(compose_task_output_path, under task_output_base - both crate-internal,
so named rather than linked). Neither reads the launch sentence: forge
resolves the file structurally, never from prose.
Re-exports§
use std::collections::VecDeque;🔒use std::ffi::OsString;🔒use std::fs::File;🔒use std::io::BufRead;🔒use std::io::BufReader;🔒use std::io::Seek;🔒use std::io::SeekFrom;🔒use std::path::Path;🔒use std::path::PathBuf;🔒
Constants§
- CALL_
OUTPUT_ MAX_ LINES - How many lines of a call’s output a
ReadCallOutputanswers with. - TAIL_
WINDOW_ BYTES - Largest slice of the file read at a time. Output files grow without bound (cargo build, npm install) and this runs inline on a view’s own tick, so the read seeks to within this window of the end rather than reading the whole file each time. 64 KiB comfortably holds the last few lines at any realistic line length.
Statics§
- LAYOUT_
PROBED 🔒 - The one check
probe_task_output_layoutguards.
Functions§
- base_of 🔒
task_output_base’s own rule, split out so it can be tested without writing the process’s environment - edition 2024 makes that unsafe and this workspace forbids unsafe code.- compose_
task_ 🔒output_ path - The path a running task’s own output file sits at, composed from the
fields the wire carries rather than parsed out of prose:
<base>/claude-<uid>/<cwd-slug>/<session-id>/tasks/<task-id>.output. - probe_
task_ 🔒output_ layout - One check per process that
<base>/claude-<uid>is where this build expects it, so a wholesale CLI layout change announces itself once rather than starving every tail in silence. - read_
output_ file_ tail - Read the last
max_lineslines ofpathinto aVecordered oldest-first. ReturnsNoneon any read error (file missing, permission denied, a read that failed) so the caller can distinguish “could not read, keep the tail already held” from “the file is genuinely empty”. The empty-file case returnsSome(vec![]). - resolve_
call_ output_ path - The file a call’s output went to, from the frames a conversation carries.
- resolve_
call_ 🔒output_ path_ at resolve_call_output_pathwith the base named, so the composition can be exercised against a scratch tree without writing the process env.- task_
output_ 🔒base - The base directory the CLI keeps its per-session task files under:
realpath($CLAUDE_CODE_TMPDIR ?? /tmp), read from the same environment the spawned child runs with (forge sets none of its own). Rule 14: never forge’s own cwd.