@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.
- package/README.md +146 -0
- package/bin/tcap +5 -0
- package/install.js +99 -0
- 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
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
|
+
}
|