Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tcap"
version = "0.1.1"
version = "0.1.2"
edition = "2021"
description = "Capture the last command and its output from your terminal"
license = "MIT"
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,8 @@ wrong block.
**On kitty, output is the last *non-empty* output.** kitty counts the running
`tcap` as the current command, so it has to be asked for the last output that
wasn't empty. The consequence: if your last command printed nothing, kitty
returns an *older* command's output while the header names the last one. The
annotated header carries a `# note:` line saying so, and `--json` sets
`"approximate": true`. Two `tcap` runs in a row are a **hard error** rather than
returns an *older* command's output while the header names the last one. The annotated header carries a `# note:` line saying so and `--json` sets
`"approximate": true`; a one-line reminder goes to stderr once per session. Two `tcap` runs in a row are a **hard error** rather than
a label, since there the mismatch is certain. tmux has no such
ambiguity, because the shell hook records real boundaries. kitty also
needs remote control reachable — either `allow_remote_control yes`, or
Expand Down
5 changes: 3 additions & 2 deletions npm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ block.

**On kitty, output is the last *non-empty* output**, because kitty counts the
running `tcap` as the current command. A command that printed nothing therefore
yields an older output under the current header; it is labelled with a `# note:`
line and `"approximate": true` in `--json`. Two `tcap` runs in a row are a hard
yields an older output under the current header; it is labelled with a `# note:` line and
`"approximate": true` in `--json`, plus a one-line stderr reminder once per
session. Two `tcap` runs in a row are a hard
error instead, since the mismatch is certain there. tmux has no such ambiguity.

**tmux wins whenever it's running.** Inside kitty running tmux, asking kitty for
Expand Down
2 changes: 1 addition & 1 deletion npm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agora-build/tcap",
"version": "0.1.1",
"version": "0.1.2",
"description": "Capture the last command and its output from your terminal, ready to pipe into an LLM",
"bin": {
"tcap": "bin/tcap"
Expand Down
14 changes: 9 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,16 @@ fn emit_hints(caps: &[Capture], backend: &dyn backend::Backend, mode: cli::Mode)
//
// The *certain* mismatch — a capture straight after another one — does not
// reach here at all; capture_and_print refuses it outright.
if mode != cli::Mode::Command && caps.iter().any(|c| c.approximate) {
// Once per session, and one line. This is true of *every* kitty capture, so
// repeating it on each one is noise that buries the hints that are specific
// — and `tcap | llm` is the workflow it interrupted most.
if mode != cli::Mode::Command
&& caps.iter().any(|c| c.approximate)
&& state::first_time("approximate")
{
eprintln!(
"tcap: {} returns the last non-empty output, which may not belong to the\n\
\x20 command named above — a silent command yields an earlier one's output,\n\
\x20 and a previous `tcap | …` leaves its pipeline's. tmux records exact\n\
\x20 boundaries; --quiet silences this.",
"tcap: {} cannot tie output to a command, so this may be an earlier one's \
— see `tcap doctor`. Shown once per session; --quiet silences it.",
backend.name()
);
printed = true;
Expand Down
14 changes: 14 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ pub fn take_capture_marker() -> bool {
fs::remove_file(marker_path()).is_ok()
}

/// True the first time it is asked for `key` in this session.
///
/// A caveat that is always true — kitty can never attribute output to a command
/// — says nothing when repeated on every capture, and trains the reader to skip
/// it. Said once, it still lands.
pub fn first_time(key: &str) -> bool {
let path = state_dir().join(format!("{}.said-{key}", session_key()));
if fs::symlink_metadata(&path).is_ok() {
return false;
}
// If the note cannot be recorded, saying it again is the safer failure.
secure_write(&path, b"1").is_ok()
}

/// Why the state directory is unusable, if it is.
///
/// `load` failing closed to an empty history also switches off the guard that
Expand Down
Loading