microsoft / microsoft/skill-recorder

Terminal capture: use a recorded-terminal (PTY) approach, not a global shell hook

Open
#5 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
TypeScript
Stars
4k
Forks
412
Avg merge
3h 16m
Merged PRs (30d)
10

Description

Context

The original terminal-capture feature installed a persistent shell hook (zsh preexec/precmd, bash DEBUG trap + PROMPT_COMMAND, pwsh prompt) into the user's profile. That hook appended one record per command to ~/.skill-recorder/shell.jsonl. The TerminalCollector tailed that file during a recording.

We removed that implementation because, evaluated honestly, its ROI was negative for our current audience (mostly non-technical users, where terminal work is uncommon):

  • Always-on, not recording-scoped. Once installed, the hook wrote on every command in every interactive shell, forever — even when the app wasn't running.
  • Per-prompt overhead. ~10ms/command measured on a dev Mac (it forked date, two base64, two tr, plus a file append every prompt).
  • Security liability. A permanent, unbounded, base64 (not encrypted) log of every command — including secrets typed inline (export TOKEN=…, curl -H "Authorization: …").
  • Fragility. The pwsh hook replaced the prompt function outright, clobbering Starship/oh-my-posh/p10k. Install also required opening a new shell to take effect (confusing).
  • Redundant. The describer already reads screen frames (list_frames/get_frames, incl. region crop + higher-fps resample), so on-screen terminal work is already legible via video. Terminal window titles are also captured cross-platform with no hook.

Proposed approach when we revisit this: recorded terminal (PTY)

Model it on asciinema / script, not a global keylogger. During a recording, the app provides a terminal the user works in; only that session is captured.

  • Embedded terminal pane (xterm.js) backed by node-pty running the user's shell, shown next to the record controls.
  • Inject our command-capture hook only into that spawned shell via a temporary ZDOTDIR (zsh) / bash --rcfile / temp $PROFILE (pwsh) that chains the user's real config, so aliases/prompt/PATH still work and no dotfiles are edited.
  • Capture is session-scoped and in-memory / temp, deleted after analysis. Nothing global, nothing persisted forever.
  • Emit the existing terminal.command events (the describer vocabulary is intentionally retained), and optionally capture terminal output too (richer than video OCR).
Properties
  • ✅ Zero dotfile edits, zero idle cost, nothing persisted globally, auto-gone when the recording ends.
  • ✅ Cross-platform (node-pty on macOS + Windows).
  • ⚠️ Tradeoff: only commands run in the app's terminal are captured, not pre-existing terminal windows. Acceptable for a "start / do the work / stop" recorder.

Alternatives considered

  • Gated global hook (keep hooks in the user's real shells, but guard with a recording sentinel → one builtin test -f when idle; write to a session-scoped file; add atuin-style regex secret redaction; append to the pwsh prompt instead of replacing it). Keeps capture of the user's existing terminals, but still edits dotfiles and needs an uninstaller. Pick this only if capturing pre-existing terminal windows becomes a hard requirement.
  • OS-native audit (macOS Endpoint Security exec events, Windows Sysmon / PowerShell ScriptBlock logging). Most powerful but system-wide, always-on, and privileged (Apple-granted entitlement / admin / GPO). Overkill and worse for privacy in a consumer app. Rejected.

Acceptance criteria (future work)

  • node-pty-backed terminal available during a recording (embedded pane).
  • User's real shell config is chained via temp ZDOTDIR/--rcfile/temp $PROFILE; no permanent dotfile changes.
  • Command records captured only for the recorded session, stored session-scoped, removed after analysis.
  • Secret redaction (regex filter) before anything is persisted.
  • Cross-platform verified on macOS + Windows.

References

  • asciinema — PTY-wrapped session recording (docs.asciinema.org/how-it-works)
  • Atuin — preexec/precmd hooks, SQLite storage, history_filter secret redaction
  • zsh ZDOTDIR / bash --rcfile for dotfile-free per-session hook injection

Removed in the accompanying PR; this issue tracks the safer re-introduction.


Ported from https://github.com/adilei/skill-recorder/issues/7 (originally opened by @adilei on 2026-07-25).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating TerminalCollector and the existing terminal.command event vocabulary, then trace the recording controls and describer integration. The work is done when a node-pty terminal is available during recording, chains the user's shell configuration without editing dotfiles, captures and redacts session commands, removes session data after analysis, and works on macOS and Windows.

Written by the indexing model from the issue text.

Assessment

Tech stack
bash, electron, node.js, powershell, typescript, zsh
Domain
cli, desktop, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.