Terminal Graph
Contents

Documentation

Introduction

Terminal Graph is a native macOS app from INTDEV — an infinite canvas where nodes are real terminals, browsers, notes, editors, and file watchers.

Drop them wherever, wire them up, and keep the whole mess of a project in one spatial view instead of cycling through tabs and tmux panes. The connections pipe data between nodes, which opens up workflows you can’t get from tabs alone. Curious to see what you come up with.

Download & install

Open the DMG, drag TerminalGraphBeta.app into your Applications folder, then eject the DMG.

Unsigned-app workaround

The Apple Developer account for Internet Development Studio Company is still being processed, so macOS will block the app on first launch with an “Apple could not verify this app is free of malware” dialog. Two ways past it:

  1. System Settings path: Attempt to open the app and let it fail. Then open System Settings → Privacy & Security, scroll to the bottom, and click Open Anyway. Confirm in the next dialog.
  2. Terminal path: Run the following command, then open the app normally.
    xattr -dr com.apple.quarantine /Applications/TerminalGraphBeta.app

Auto-updates

Sparkle automatic updates work and the update archives are signed with a separate key that does not need Apple’s notarization blessing. You may see the quarantine dialog once per update until the Developer account is approved.

Getting started

Adding nodes

Right-click anywhere on the canvas to open the node menu. Select the type of node you want to add. Nodes can be repositioned by dragging their title bar and resized by dragging any corner or edge.

Wiring nodes

Drag from one port to another to create a connection. Ports live on the edges of each node; output ports are on the right side, input ports on the left. The port hitbox is a half-circle on the outside of the node edge — hover slowly near the edge to reveal it. Right-click a port to disconnect it.

Press ⌘⇧D to toggle Wiring Mode, which makes all ports visible simultaneously and is easier to use when wiring many connections at once. Wiring works without Wiring Mode too.

Command palette

Press ⌘K to open the command palette. It is the fastest way to reach any action — adding nodes, changing settings, running commands — without navigating menus.

Sidebar

Press ⌘⌥S to toggle the sidebar, which contains a file tree and workspace switcher. The file tree is only populated when the app is opened with a project folder.

Keyboard shortcuts

All shortcuts are also listed next to their corresponding menu items inside the app.

Shortcut Action Category
⌘Q Quit Application App
⌘O Open Folder File
⌘⇧O Open Folder in New Window File
⌘C Copy Edit
⌘V Paste Edit
⌘A Select All Edit
Esc Deselect All Edit
⌘N New Terminal Nodes
⌘⇧N New Note Nodes
⌘⇧B New Browser Nodes
⌘⇧E New File Editor Nodes
⌘D Duplicate Node Nodes
⌘W Close Node Nodes
⌘⇧T Reopen Last Closed Node Nodes
⌘K Command Palette Palette
⌘⇧D Toggle Wiring Mode View
⌘⏎ Toggle Focus Mode View
⌘⌥0 Zoom to Fit All View
⌘⌥F Zoom to Fit Node View
⌘⌥T Tidy Selection View
⌘⌥S Toggle Sidebar View
⌘⌥M Toggle Minimap View
⌘= Increase Font Size View
⌘− Decrease Font Size View
⌘0 Reset Font Size View
⌘⌥← Navigate Left Navigation
⌘⌥→ Navigate Right Navigation
⌘⌥↑ Navigate Up Navigation
⌘⌥↓ Navigate Down Navigation
⌘M Minimize Window Window

Notes: Esc clears multi-selection when two or more nodes are selected. Font size shortcuts apply to adjustable nodes; terminal nodes handle font size directly via Ghostty. The navigation arrow shortcuts are canvas-level keyboard monitor shortcuts, not menu items.

Node types

Each node type exposes a set of typed ports. Connections are validated before wiring; incompatible port types are rejected. Port types are described in the Dataflow section.

Terminal

An interactive shell backed by libghostty with full stdio streams and process lifecycle signals.

Port Type Direction Description
stdout stream output Terminal stdout
stderr stream output Terminal stderr
stdin stream input Inject into process stdin
text stream input Inject raw bytes directly into the Ghostty surface, bypassing the stdin FIFO
exit signal output Process exit event; payload is the exit code
cwd state output Current working directory, tracked via OSC 7

Browser

An embedded web browser backed by WKWebView with navigation, console logging, and DOM inspection.

Port Type Direction Description
url state output Current page URL
console stream output JavaScript console logs — not yet functional
dom state output DOM serialized as HTML, evaluated only when wired
navigate stream input Load a URL string
reload signal input Reload the current page

Note

A free-form Markdown editor with optional file-backing, powered by Monaco.

Port Type Direction Description
content state output Current note text
write stream input Replace content with incoming text
append stream input Append text, preserving cursor position and undo history
save signal output Emitted whenever a save is attempted

Editor

A file-backed code editor powered by Monaco with language detection and external file watching. Auto-reloads on external change when not dirty.

Port Type Direction Description
path state output Absolute path of the open file
content state output Current file content
save signal output Save attempt; optional payload is the file path
write stream input Replace content
append stream input Append to content
open stream input Load a new file by path

Image

Displays images from files or raw binary data. File-backed images auto-reload on change. No output ports.

Port Type Direction Description
path stream input Load image from a file path
data stream input Load image from raw bytes
refresh signal input Reload from the current file

File Watcher

Watches a directory for file changes matching a glob pattern. The pattern is fnmatch-style and is debounced approximately 0.5 seconds to avoid event stampedes.

Port Type Direction Description
changed signal output Fires when a matching file changes; payload is the full file path
path stream input Update the glob pattern

Dataflow

Connections in Terminal Graph carry typed data between node ports. The DataflowRuntime validates type compatibility before a connection is established and rejects wiring between incompatible port types.

Port types

Type Description
stream Continuous text flow backed by named FIFO pipes. Used for stdio, file content, and URL strings. Can be read and written many times. Equivalent to a Unix pipe.
state Persistent key/value backed by a StateStore. Emits on change. New subscribers receive the current value immediately (e.g., current URL, current file path).
signal Discrete fire-and-forget events with optional payloads. Used for process exit codes, save attempts, and file change notifications. Not buffered.

Environment variables

Terminal nodes expose their streams through environment variables that Terminal Graph injects automatically into each shell session:

Variable Description
$TG_STDIN FIFO path for data flowing into this terminal from the graph
$TG_STDOUT FIFO path for data flowing out to whatever is wired to the stdout port
$TG_STDERR FIFO path for data flowing out to whatever is wired to the stderr port
$TG_NODE_ID This node’s unique identifier
$TG_WRITE_TIMEOUT_MS Timeout for write retries (default 2000 ms)

The FIFO files are backed by Unix named pipes, so cat, tee, and shell redirection work on them like any normal file. The tg CLI wraps these operations for common patterns.

CLI reference

The terminalgraph / tg CLI (v0.2.0-beta) is automatically injected into terminal-node shells. No PATH setup is required.

Subcommands

Command Description
tg run [flags] <cmd> [args…] Pipe data through a command. Default: bidirectional stdin/stdout; stderr merged.
tg send [flags] [text…] Write text to $TG_STDOUT (or $TG_STDERR with --err).
tg recv Drain $TG_STDIN to terminal stdout.
tg ports List the FIFO paths for all ports on the current node.
tg env Print all TG_* environment variables.
tg help [--agent|--human] Show usage. Auto-detects agent mode via CLAUDECODE, CURSOR_AGENT, or CI=true.
tg version Print the app version.

Flags: tg run

Flag Description
-i, --in Read $TG_STDIN only; command output goes to terminal.
-o, --out Write to $TG_STDOUT only; read from terminal stdin.
-e, --capture-err Split stderr to $TG_STDERR (default: merge into stdout).
-b N, --batch N Re-invoke the command per N input lines.
-t D, --batch-time D Re-invoke when the current batch is D old. Accepts 500ms, 2s, 1m, 1h, or bare seconds.

Flags: tg send

Flag Description
-e, --err Write to $TG_STDERR instead of stdout.
-n, --no-newline Suppress the trailing newline.

Examples

# Filter inbound stream and push matches onward
tg run grep ERROR

# One-off message to the graph
tg send "deployment complete"

# Generate output from a command that ignores stdin
tg run --out date

# Watch what upstream is pushing (debug)
tg recv

# Batch an infinite stream for a turn-based tool
tg run --batch 50 --batch-time 10s claude -p "Summarize"

Run terminalgraph help for the complete reference.

Data storage

Terminal Graph persists data in two locations:

  • Global configuration~/.config/terminalgraph/. Holds app-level preferences, keybindings, and canvas state for freestanding (no-workspace) windows.
  • Per-workspace data{PROJECT_DIR}/.terminalgraph/ inside each project directory opened as a workspace. Holds canvas state, node layouts, notes, and editor buffers scoped to that workspace.

Back up these directories if you care about preserving your layouts. Deleting them, or uninstalling the app, removes all locally persisted state.

Feedback

The app is still in beta, so expect things to break. There are two ways to report issues from inside the app:

  • Help → Report a Bug… — for something that is broken.
  • Help → Send Feedback… — for ideas, UX gripes, or anything else.

Both menu items open a pre-filled email in your default mail client, addressed to [email protected], with your app version, macOS version, and a few other details pre-populated. Remove anything you’re not comfortable sharing before sending.

You can also email [email protected] directly.

What makes a useful bug report

  • What you did
  • What you expected to happen
  • What actually happened
  • Screenshots are useful. A short screen recording is even better.
  • A half-written “hey this felt weird” is better than nothing.

Notes

  • No telemetry, no analytics, no crash reporters. If I hear about a bug, it’s because you told me.
  • Browser node limitations. The console output port is not yet functional. All other browser node features, including dom, work. Fix for console is on the list.
  • Updates. Updates will land semi-regularly. Some will be rough. Sorry in advance.
  • Sharing. You are free to share pictures, videos, and write about Terminal Graph anywhere. I'd appreciate it if you would tag me on socials (listed below) so I can see what you are making.

Sign-off

That’s it. Go break things.

From the bottom of my heart, thank you.
— Caidan

[email protected] · @caidanwilliams on X · caidan.dev on Bluesky