swift-tangled #
swift-tangled provides the tng command-line tool for Tangled. tng is the
package's only supported public product; the SwiftTangled and
TangledLexicons targets are internal implementation layers and are not
exposed for use as libraries.
Read the tng CLI manual for the complete command reference, installation guide, and quick start.
Requirements #
- macOS 15 or later, or Linux
- Swift 6.3 or later
- Git access to the package dependencies
- A Swift toolchain that supports
swift package experimental-installwhen installingtngwith the provided script
Install tng #
On an Apple Silicon Mac running macOS 15 or later, install the prebuilt binary from the Tangled-hosted Homebrew tap:
brew tap nnabeyang/tap \
https://tangled.org/nnabeyang.tngl.sh/homebrew-tap
brew install nnabeyang/tap/tng
To build and install from source instead, run this from the repository root:
./install.sh
The script builds tng in release mode and installs it into
~/.swiftpm/bin. Add that directory to PATH if necessary:
export PATH="$HOME/.swiftpm/bin:$PATH"
Run ./install.sh again after updating the repository to replace the installed
executable.
Quick Start #
Confirm the installed version, sign in when a write operation requires authentication, and inspect a repository:
tng --version
tng auth login YOUR_ATPROTO_HANDLE
tng auth status
tng repo view OWNER/REPOSITORY
Public read commands do not require a session. On macOS, authenticated sessions
are stored per DID in Keychain. On Linux, they are stored per DID as plaintext
OAuth tokens and DPoP key material under $XDG_STATE_HOME/tng/accounts, or
$HOME/.local/state/tng/accounts when XDG_STATE_HOME is not usable. Protect
that directory and exclude it from backups and synchronization. An existing
single-account session is migrated only after the per-account copy is verified.
New logins use an OAuth loopback client by default, so tng does not depend on
a hosted client metadata document. The authorization page describes it as an
application on your device. To use hosted client metadata instead, pass its
HTTPS URL directly or through the environment:
tng auth login YOUR_ATPROTO_HANDLE \
--client-id https://example.com/tng-client-metadata.json
TNG_CLIENT_ID=https://example.com/tng-client-metadata.json \
tng auth login YOUR_ATPROTO_HANDLE
--client-id takes precedence over TNG_CLIENT_ID. Existing sessions keep
using the client ID with which they were created. See the
OAuth client configuration
guide for loopback compatibility and a complete self-hosted metadata example.
List accounts, change the default, or select an account for one command:
tng auth list
tng auth switch HANDLE_OR_DID
tng --account HANDLE_OR_DID repo star OWNER/REPOSITORY
tng auth logout
tng auth logout --all
Use the DID when handles are ambiguous. --account overrides the active
account without changing it. It cannot be combined with TNG_AUTH_AGENT or
TNG_SESSION_FILE; those external authentication sources intentionally bypass
the account registry. Git remains independent of tng: configure the SSH key
for each checkout with Git's core.sshCommand or an equivalent SSH setup.
For a self-hosted VM worker, an enrolled token-mediating backend (TMB) can keep authorization and refresh separate from direct PDS requests. After enrollment and browser authorization, expose the named TMB session through a private host Unix socket:
tng auth agent tmb login CI_HANDLE --instance ci-reporting
tng auth agent tmb verify --refresh --instance ci-reporting
tng auth agent service install \
--tmb-instance ci-reporting \
--socket /absolute/private/path/ci-agent.sock \
--profile ci-reporting \
--instance ci-reporting
The macOS service starts immediately, returns after crashes, and starts again
when the user logs in after a host restart. Use service status, restart,
stop, start, and uninstall with the same instance name. For foreground
operation or Linux, use tng auth agent serve --tmb-instance ci-reporting.
The existing native OAuth workflow remains available with a private
TNG_SESSION_FILE and --session-file; do not configure both sources.
The socket's parent directory must be owned by the current user and inaccessible
to group and other users. A trusted VM host bridge binds each connection to one
job, repository DID, deadline, and the allowed artifact/comment operations.
Inside the VM, ordinary authenticated commands select the broker through
TNG_AUTH_AGENT=vsock://host:10241; OAuth tokens and the session file remain
on the host. Use a separate named authentication source and broker socket for
each CI account. TNG_AUTH_AGENT takes precedence over TNG_SESSION_FILE;
interactive login and logout are disabled while the agent variable is set.
Use built-in help for command details:
tng --help
tng pr --help
tng capabilities --json
capabilities returns a versioned description of every executable command,
including its arguments, options, read/write classification, authentication
requirement, and supported platforms.
CLI Features #
| Command | Purpose |
|---|---|
tng auth |
Sign in, inspect the current session, or sign out |
tng repo |
View and browse repositories, branches, tags, files, and commits |
tng issue |
List, view, create, comment on, edit, close, and reopen issues |
tng pr |
List, view, diff, create, resubmit, review, close, reopen, and merge pull requests |
tng pipeline |
List, inspect, and watch Spindle CI pipelines |
tng artifact |
Publish and download artifacts attached to annotated Git tags |
tng events |
Watch live Tangled records through Jetstream |
tng search |
Search public Tangled records |
tng api |
Call an allowlisted read-only Bobbin query |
tng completion |
Generate shell completion scripts |
tng capabilities |
Describe the CLI for tools and agents |
Most repository-aware commands infer the repository from the Git origin.
Pass --repo OWNER/REPOSITORY to target one explicitly. Commands intended for
automation support --json where documented by their help.
Terminal output #
When standard output is a terminal, tng experimentally renders human-readable
Markdown, tables, and details with ANSI styling and terminal-width-aware
wrapping on macOS and Linux. OSC-8 hyperlinks are disabled. When output is piped
or redirected, tng keeps the existing plain output and avoids
terminal-width-dependent formatting. Use --json when scripts or agents need a
stable, structured contract.
For terminal-style output, tng repo view, tng issue view, tng pr view,
and tng pr diff use the pager configured by TNG_PAGER or PAGER.
TNG_PAGER takes precedence, including when it is set to an empty value. If
neither variable is set, tng uses less by default. An empty selected value,
cat, or TERM=dumb disables paging. If the selected pager is unavailable,
tng warns and writes the output directly. A pager that fails after starting
produces a warning without changing the command's exit status or repeating
potentially partial output. JSON, list, raw, binary, and streaming output is
never sent to a pager.
The following environment variables control paging, terminal detection, width, and ANSI styling:
| Variable | Behavior |
|---|---|
TNG_PAGER |
Set the pager command for supported human-readable output. Takes precedence over PAGER; an empty value disables paging. |
PAGER |
Set the pager command when TNG_PAGER is not set. An empty value disables paging. If neither pager variable is set, tng uses less. |
TNG_FORCE_TTY |
Force terminal-style output. A positive integer sets the viewport width; a positive percentage uses that share of the detected terminal width. |
TNG_MDWIDTH |
Set the maximum width available to width-aware Markdown, table, and details rendering. |
NO_COLOR |
Disable ANSI styling when set to a nonempty value. |
CLICOLOR |
Disable ANSI styling when set to 0. |
CLICOLOR_FORCE |
Force ANSI styling, including for piped output, when set to a nonempty value other than 0. |
NO_COLOR and CLICOLOR=0 take precedence over forced color. JSON, raw,
binary, and streaming output never gain human-readable decoration from these
settings.
tng selects the read source according to the operation:
| Operation | Source |
|---|---|
| Repository listings | Repository owner's PDS |
| Repository, issue, and pull request records | Record owner's PDS, with Bobbin fallback for temporary PDS failures |
Pull request listings with --author |
Author and repository owner PDSes |
Issue listings, pull request listings without --author, comments, and search |
Bobbin |
| Artifact listings | Bobbin, merged with the signed-in account's PDS records |
| Pipelines | Spindle |
| Live events | Jetstream |
Bobbin-backed aggregate reads report a warning on standard error when index coverage is unavailable or incomplete or when an initial query returns no results. JSON output on standard output keeps the same schema when these diagnostics are present.
Pull Requests #
Create a pull request after pushing its source branch:
git push origin feature/my-change
tng pr create --base main --head feature/my-change \
--title "Describe the change" \
--body "Why this change is useful"
Create one dependent pull request per commit with --stack:
tng pr create --base main --head feature/my-stack --stack
Every commit in a stack must contain a unique Change-Id commit header.
Jujutsu users can enable git.write-change-id-header = true. Stack titles and
bodies come from the individual commits, so --stack cannot be combined with
--title, --body, or --body-file. Fork stacks use the same --repo
workflow as other fork pull requests.
After pushing more commits to the source branch, add a new review round:
git push origin feature/my-change
tng pr resubmit PULL_REQUEST_AT_URI
For a patch-based pull request, provide a cumulative git diff or
git format-patch file:
tng pr resubmit PULL_REQUEST_AT_URI --patch-file changes.patch
For a fork-based pull request, run the command from a checkout whose origin
is the source fork. tng refreshes the fork's hidden tracking ref from the
target branch and prepares the new round on the fork's Knot.
To resubmit a dependent stack, inspect its operation plan before writing:
tng pr resubmit PULL_REQUEST_AT_URI --stack --dry-run
tng pr resubmit PULL_REQUEST_AT_URI --stack
The stack is matched to its existing pull requests by the unique Change-Id
header in each commit. The command can reorder, update, and add stack entries
in one PDS transaction. If the plan deletes pull request records, rerun it with
--yes after reviewing --dry-run. Merged entries are preserved. For a
patch-based stack, pass a multi-message git format-patch file with
--patch-file.
To list one author's pull requests without waiting for Bobbin indexing, read that author's PDS directly:
tng pr list OWNER/REPOSITORY --author AUTHOR_HANDLE
This mode resolves pull request status records from the author and repository
owner PDSes. Comment counts are not available: the table displays -, and
JSON output uses -1. Its pagination cursor is specific to the author,
repository, filters, and sort order; pass it back unchanged to the same mode.
The source and target commits must already exist locally. tng does not run
git fetch or git push. Use tng pr view, tng pr diff, tng pr comment,
tng pr close, tng pr reopen, and tng pr merge to continue the review
workflow. State changes use the pull request AT URI:
tng pr close PULL_REQUEST_AT_URI --json
tng pr reopen PULL_REQUEST_AT_URI --json
Issues #
tng issue list OWNER/REPOSITORY
tng issue view ISSUE_AT_URI --comments
tng issue create --repo OWNER/REPOSITORY --title "Describe the issue"
Issue write commands also support commenting, editing, closing, and reopening.
Pipelines #
tng pipeline list OWNER/REPOSITORY
tng pipeline view PIPELINE_ID --spindle spindle.tangled.sh
tng pipeline status PIPELINE_ID --spindle spindle.tangled.sh
tng pipeline watch PIPELINE_ID --repo OWNER/REPOSITORY
Pipeline commands discover the repository's current Spindle by default.
Use --spindle with list, view, status, or watch to select an endpoint
explicitly. pipeline watch reports workflow state changes until the pipeline
completes.
Artifacts #
Artifacts are files attached to remote annotated Git tags. Git remains responsible for creating and pushing the tag:
git tag -a v1.0.0 -m "v1.0.0"
git push origin v1.0.0
tng artifact upload v1.0.0 .build/release/tng \
--repo OWNER/REPOSITORY \
--name tng
tng artifact download v1.0.0 tng \
--repo OWNER/REPOSITORY \
-o ./tng
When signed in, artifact listings merge the account's authoritative PDS
records with Bobbin results and prefer the PDS value for the same record URI.
Pagination cursors are opaque and must be passed back unchanged with the same
sort order. Cursors produced by earlier tng versions remain accepted.
Live Events #
Watch Tangled records through Jetstream, optionally filtering by collection or DID:
tng events watch \
--collection sh.tangled.feed.comment \
--did did:plc:example \
--json
tng reads current record state from authoritative PDS records, uses Bobbin
for indexed aggregate reads, Spindle for pipelines, and Jetstream for live
events.
Agent Skill #
The repository includes a reusable tng Agent Skill
for agents that work with Tangled repositories, issues, pull requests, rounds,
comments, artifacts, and Spindle pipelines.
Point an Agent Skills-compatible tool at the complete skills/tng directory,
or copy or symlink it into the tool's skill directory so its workflow reference
remains available.
Contributing #
See CONTRIBUTING.md for development environment, testing, design, and contribution guidelines.
License #
swift-tangled is available under the MIT License.
Release archives also include the applicable third-party notices and license
texts in THIRD_PARTY_NOTICES.md and LICENSES/.