@agora-build/tcap 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +146 -0
  2. package/bin/tcap +5 -0
  3. package/install.js +99 -0
  4. package/package.json +47 -0
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # @agora-build/tcap
2
+
3
+ Capture the last command and its output from your terminal, ready to pipe into an LLM.
4
+
5
+ ```console
6
+ $ npm run build
7
+ > app@1.0.0 build
8
+ > tsc
9
+ Error: Cannot find module 'foo'
10
+
11
+ $ tcap | sgpt "tell me how to fix it"
12
+ ```
13
+
14
+ `tcap` gives the model the command, its exit code, where it ran, and the output:
15
+
16
+ ```console
17
+ $ npm run build
18
+ # exit: 1 cwd: ~/Dev/app took: 3.2s
19
+
20
+ > app@1.0.0 build
21
+ > tsc
22
+ Error: Cannot find module 'foo'
23
+ ```
24
+
25
+ The exit code carries more weight than it looks — it tells the model the command
26
+ genuinely failed, instead of leaving it to infer that from the text.
27
+
28
+ ## Install
29
+
30
+ ```sh
31
+ npm i -g @agora-build/tcap
32
+ ```
33
+
34
+ The postinstall step downloads a prebuilt binary for your platform (macOS and
35
+ Linux, x64 and arm64). If it can't reach GitHub it falls back to
36
+ `dl.agora.build`, and if both fail it exits cleanly rather than breaking your
37
+ install — `tcap` then tells you how to recover.
38
+
39
+ Then enable shell integration and open a new terminal:
40
+
41
+ ```sh
42
+ echo 'eval "$(tcap init zsh)"' >> ~/.zshrc
43
+ ```
44
+
45
+ ```sh
46
+ # bash
47
+ echo 'eval "$(tcap init bash)"' >> ~/.bashrc
48
+ # fish
49
+ echo 'tcap init fish | source' >> ~/.config/fish/config.fish
50
+ ```
51
+
52
+ Confirm it's wired up:
53
+
54
+ ```sh
55
+ tcap doctor
56
+ ```
57
+
58
+ Shell integration is what supplies the exit code, duration and command text.
59
+ Without it you still get output, and `tcap` tells you what's missing and why.
60
+
61
+ ## Usage
62
+
63
+ Two axes, each with an index and a count form, under one rule:
64
+ **lowercase selects the Nth from the end, uppercase takes the last N.**
65
+
66
+ | | index (lowercase) | count (uppercase) |
67
+ |---|---|---|
68
+ | **commands** | `-c 3` — 3rd-from-last block | `-C 3` — last 3 blocks |
69
+ | **lines** | `-l 3` — 3rd-from-last line | `-L 3` — last 3 lines |
70
+
71
+ They combine, which is what makes long build logs usable:
72
+
73
+ ```sh
74
+ tcap # last command + its output
75
+ tcap -c 2 # the command before the last one
76
+ tcap -C 3 # the last 3 commands, oldest first
77
+ tcap -c 2 -L 50 # 2nd-to-last command, tail 50 lines of its output
78
+ ```
79
+
80
+ ### Output formats
81
+
82
+ ```sh
83
+ tcap --output # output only, no header
84
+ tcap --command # the command text only
85
+ tcap --raw # verbatim, ANSI colour intact
86
+ tcap --json # {command, exit_code, cwd, duration_ms, output, source}
87
+ tcap --copy # also copy to the clipboard
88
+ tcap --max-bytes 8000 # elide the middle to fit a smaller context window
89
+ ```
90
+
91
+ `--command` prints the command; `-c` selects *which* command. Similar spelling,
92
+ unrelated jobs.
93
+
94
+ ## Terminal support
95
+
96
+ | Terminal | Last command | Older (`-c 2`) | Needs |
97
+ |---|---|---|---|
98
+ | **tmux** | yes | **yes** | nothing |
99
+ | **kitty** | yes | no | `allow_remote_control yes` |
100
+ | **iTerm2** | yes | no | Shell Integration + `pip install iterm2` |
101
+ | **WezTerm** | not yet | no | run inside tmux |
102
+ | Ghostty, Terminal.app, Alacritty | no | no | run inside tmux |
103
+
104
+ Two things worth knowing up front:
105
+
106
+ **Only tmux can reach past the most recent command.** kitty exposes a
107
+ `last_cmd_output` extent and nothing older; iTerm2 exposes only its latest
108
+ prompt. So `tcap -c 2` fails there with a pointer to tmux rather than quietly
109
+ returning the wrong block.
110
+
111
+ **tmux wins whenever it's running.** Inside kitty running tmux, asking kitty for
112
+ its scrollback returns tmux's rendered viewport rather than the shell's real
113
+ history, so tmux is always treated as authoritative when it's in the stack.
114
+
115
+ Anything missing is reported with its cause and fix, on stderr so `tcap | sgpt`
116
+ still pipes clean text:
117
+
118
+ ```console
119
+ $ tcap
120
+ tcap: captured output only — no command, exit code or duration.
121
+ Cause: tcap's shell integration is not recording in this session,
122
+ so there is nothing to annotate the output with.
123
+ Fix: add to your ~/.zshrc then open a new shell:
124
+ eval "$(tcap init zsh)"
125
+ ```
126
+
127
+ ## Configuration
128
+
129
+ Optional, at `~/.config/tcap/config.toml`:
130
+
131
+ ```toml
132
+ max_bytes = 8000 # smaller budget for a smaller context window
133
+ format = "json" # default output format
134
+ quiet = false # suppress the stderr hints
135
+ ```
136
+
137
+ Precedence is `flags > environment > config > defaults`. An unknown key is an
138
+ error rather than a silent no-op, and `--no-quiet` / `--no-copy` escape a config
139
+ boolean for a single run.
140
+
141
+ ## Links
142
+
143
+ - Source, full docs and issues: <https://github.com/Agora-Build/TermCap>
144
+ - Standalone install: `curl -fsSL https://dl.agora.build/tcap/install.sh | bash`
145
+
146
+ MIT
package/bin/tcap ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ # Placeholder, replaced by install.js with the real binary on postinstall.
3
+ echo "tcap: binary not installed. Run 'npm rebuild @agora-build/tcap'," >&2
4
+ echo "tcap: or: curl -fsSL https://dl.agora.build/tcap/install.sh | bash" >&2
5
+ exit 1
package/install.js ADDED
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ // postinstall: download the prebuilt tcap binary for this platform into bin/.
3
+ //
4
+ // Falls back to dl.agora.build if GitHub is unreachable, and never hard-fails
5
+ // `npm install` — a broken download leaves the placeholder in bin/tcap, which
6
+ // prints how to recover rather than a confusing ENOENT.
7
+ "use strict";
8
+
9
+ const https = require("https");
10
+ const fs = require("fs");
11
+ const path = require("path");
12
+ const os = require("os");
13
+ const { execSync } = require("child_process");
14
+
15
+ const pkg = require("./package.json");
16
+ const REPO = process.env.TCAP_REPO || "Agora-Build/TermCap";
17
+ const VERSION = pkg.version;
18
+ const TAG = `v${VERSION}`;
19
+ const BIN_DIR = path.join(__dirname, "bin");
20
+ const BIN_PATH = path.join(BIN_DIR, "tcap");
21
+
22
+ const ARCH = { x64: "x86_64", arm64: "aarch64" }[process.arch];
23
+ const OS = { darwin: "darwin", linux: "linux" }[process.platform];
24
+
25
+ function bail(msg) {
26
+ // Exit 0: a failed optional download must not fail the consumer's whole
27
+ // dependency tree. bin/tcap stays as the placeholder that explains itself.
28
+ console.error(`tcap: ${msg}`);
29
+ console.error(`tcap: install manually from https://github.com/${REPO}/releases/tag/${TAG}`);
30
+ console.error(`tcap: or run: curl -fsSL https://dl.agora.build/tcap/install.sh | bash`);
31
+ process.exit(0);
32
+ }
33
+
34
+ if (!OS || !ARCH) {
35
+ bail(`unsupported platform ${process.platform}/${process.arch}`);
36
+ }
37
+
38
+ const target = `${OS}-${ARCH}`;
39
+ const asset = `tcap-${VERSION}-${target}.tar.gz`;
40
+ const sources = [
41
+ `https://github.com/${REPO}/releases/download/${TAG}/${asset}`,
42
+ `https://dl.agora.build/tcap/releases/${TAG}/${asset}`,
43
+ ];
44
+
45
+ function download(url, dest, redirects = 0) {
46
+ return new Promise((resolve, reject) => {
47
+ if (redirects > 5) return reject(new Error("too many redirects"));
48
+ https
49
+ .get(url, { headers: { "User-Agent": "tcap-npm-installer" } }, (res) => {
50
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
51
+ res.resume();
52
+ return resolve(download(res.headers.location, dest, redirects + 1));
53
+ }
54
+ if (res.statusCode !== 200) {
55
+ res.resume();
56
+ return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
57
+ }
58
+ const f = fs.createWriteStream(dest);
59
+ res.pipe(f);
60
+ f.on("finish", () => f.close(() => resolve()));
61
+ f.on("error", reject);
62
+ })
63
+ .on("error", reject);
64
+ });
65
+ }
66
+
67
+ async function install() {
68
+ fs.mkdirSync(BIN_DIR, { recursive: true });
69
+ const tmp = path.join(os.tmpdir(), `tcap-${Date.now()}.tar.gz`);
70
+
71
+ let lastErr;
72
+ for (const url of sources) {
73
+ try {
74
+ console.log(`tcap: downloading ${url}`);
75
+ await download(url, tmp);
76
+ lastErr = null;
77
+ break;
78
+ } catch (e) {
79
+ lastErr = e;
80
+ console.error(`tcap: ${e.message}`);
81
+ }
82
+ }
83
+ if (lastErr) throw lastErr;
84
+
85
+ try {
86
+ // Flat tarball: the executable sits at the archive root.
87
+ execSync(`tar -xzf "${tmp}" -C "${BIN_DIR}"`, { stdio: "pipe" });
88
+ } finally {
89
+ try {
90
+ fs.unlinkSync(tmp);
91
+ } catch {}
92
+ }
93
+
94
+ fs.chmodSync(BIN_PATH, 0o755);
95
+ console.log(`tcap: installed ${VERSION} (${target}) to ${BIN_PATH}`);
96
+ console.log('tcap: enable shell integration: eval "$(tcap init zsh)" in ~/.zshrc');
97
+ }
98
+
99
+ install().catch((err) => bail(`download failed: ${err.message}`));
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@agora-build/tcap",
3
+ "version": "0.1.0",
4
+ "description": "Capture the last command and its output from your terminal, ready to pipe into an LLM",
5
+ "bin": {
6
+ "tcap": "bin/tcap"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node install.js"
10
+ },
11
+ "files": [
12
+ "bin/",
13
+ "install.js",
14
+ "README.md"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/Agora-Build/TermCap.git"
19
+ },
20
+ "keywords": [
21
+ "terminal",
22
+ "scrollback",
23
+ "capture",
24
+ "tmux",
25
+ "kitty",
26
+ "iterm2",
27
+ "llm",
28
+ "cli"
29
+ ],
30
+ "author": "Agora Build <build@agora.io>",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/Agora-Build/TermCap/issues"
34
+ },
35
+ "homepage": "https://github.com/Agora-Build/TermCap#readme",
36
+ "os": [
37
+ "linux",
38
+ "darwin"
39
+ ],
40
+ "cpu": [
41
+ "x64",
42
+ "arm64"
43
+ ],
44
+ "engines": {
45
+ "node": ">=16"
46
+ }
47
+ }