A command-line interface for tangled, the decentralized Git collaboration platform built on the AT Protocol. eric-wien.tngl.io/tng-cli
cli git atproto tangled
README.md

tng #

A command-line interface for Tangled, the decentralized Git collaboration platform built on the AT Protocol.

tng brings the full Tangled workflow to your terminal: authenticate with your AT Protocol identity, manage repositories, create and review pull requests, file and track issues -- all without leaving the command line.

Known Issues #

  • PR/issue list scope: tng pr list and tng issue list show only records authored by the authenticated user. The appview aggregates records from all authors but does not expose a public JSON API, so cross-user listing is not yet possible from the CLI.
  • OAuth session expiry: The public client OAuth flow produces tokens that cannot always be refreshed silently. If you encounter invalid_grant errors, run tng auth login to re-authenticate.
  • Repo view on empty repos: tng repo view returns a 404 for repos with no commits, since the knot has no ref to resolve. Push at least one commit first.

Repositories created by current versions of tng include the knot-minted repository DID required by Tangled's appview ingester. The target knot must support repository DIDs (Tangled core v1.13 or newer).

Installation #

From source #

go install tangled.org/eric.wien/tng-cli/cmd/tng@latest

Build from source #

git clone git@tangled.sh:eric.wien/tng-cli
cd tng-cli
make build
# Binary is at ./bin/tng

Updating #

tng upgrade        # Check the Go module proxy and install the latest release via go install
tng upgrade --check  # Only check whether a newer release exists

tng upgrade is equivalent to go install tangled.org/eric.wien/tng-cli/cmd/tng@latest and requires a Go toolchain. For source builds, use git pull && make install instead.

Makefile targets #

make build     # Build the binary
make test      # Run tests
make lint      # Run golangci-lint
make install   # Install to /usr/local/bin
make clean     # Remove build artifacts

Quick Start #

# 1. Authenticate with your AT Protocol handle
tng auth login --handle example.bsky.social

# 2. Create a repository on a knot
tng repo create my-project --knot knot.example.com

# 3. Clone it
tng repo clone example.bsky.social/my-project

# 4. Work on code, then create a pull request
cd my-project
git checkout -b feature-branch
# ... make changes, commit ...
tng pr create --title "Add new feature" -b main

# 5. List and view your pull requests
tng pr list
tng pr view <rkey>

# 6. Open in browser
tng browse

Authentication #

tng uses AT Protocol OAuth 2.0 with PKCE and DPoP. Your handle can be from any AT Protocol provider -- Bluesky (example.bsky.social), Tangled (example.tngl.sh), or any self-hosted PDS.

# Interactive login (opens browser)
tng auth login

# Login with a specific handle
tng auth login --handle example.bsky.social

# Login from an SSH or other headless session
tng auth login --manual --handle example.bsky.social

# Check who you're logged in as
tng auth status

# Log out
tng auth logout

Sessions are stored in ~/.config/tng/. Tokens are persisted locally and refreshed automatically. The public client flow is used, so tokens expire after approximately two weeks.

When run over SSH, tng auth login automatically uses manual browser authentication. Open the displayed authorization URL on your local machine, authorize the CLI, then copy the full localhost callback URL from the browser's address bar back into the terminal. Use --manual to select the same flow in other headless environments.

Environment variables #

Variable Description
TNG_HOST Override API host (default: https://tangled.sh)
TNG_CONFIG_DIR Override config directory
TNG_REPO Override repository context (owner/name)
TNG_FORCE_TTY Force TTY output in non-TTY contexts
NO_COLOR Disable color output

Commands #

tng auth -- Authentication #

tng auth login [--handle <handle>] [--manual] # Log in via browser OAuth
tng auth logout [--did <did>]        # Log out and revoke tokens
tng auth status                      # Show authenticated accounts

tng repo -- Repositories #

tng repo create <name> [flags]       # Create a new repository
tng repo clone <owner/repo> [dir]    # Clone via SSH
tng repo delete <owner/repo>         # Delete a repository
tng repo view [owner/repo]           # View repository details
tng repo list [owner]                # List repositories

Flags for repo create:

  • -d, --description -- Repository description
  • --default-branch -- Default branch name (default: main)
  • -k, --knot -- Knot URL to create the repo on (required unless default_knot is set in config)
  • -c, --clone -- Clone after creation

Flags for repo clone:

  • -k, --knot -- Knot hostname to clone from (skips AT Protocol record lookup)

Flags for repo delete:

  • -y, --yes -- Skip confirmation prompt

Flags for repo view:

  • -w, --web -- Open in browser instead

Flags for repo list:

  • -L, --limit -- Maximum number of results (default: 30)

tng pr -- Pull Requests #

Pull requests are AT Protocol records identified by a TID rkey (e.g. 3jxn7q7bklr2r), not sequential numbers. The rkey is shown in tng pr list output. Patches are stored as gzip-compressed blobs on the user's PDS.

tng pr create [flags]                # Create a pull request
tng pr list [flags]                  # List your pull requests
tng pr view <rkey> [flags]           # View PR details
tng pr diff <rkey>                   # View PR patch
tng pr merge <rkey>                  # Merge a pull request
tng pr close <rkey>                  # Close a pull request
tng pr comment <rkey> --body "..."   # Comment on a PR

Flags for pr create:

  • -t, --title -- PR title (required)
  • -B, --body -- PR description
  • -b, --base -- Target branch (default: the repo's default branch, falling back to main)
  • -H, --head -- Source branch (default: current branch)

Flags for pr list:

  • -R, --repo -- Filter by repository (owner/name)
  • -w, --web -- Open pull requests page on tangled.sh

Flags for pr view, pr diff, pr merge, pr close, pr comment:

  • --author -- DID of the PR author (defaults to the authenticated user)

tng issue -- Issues #

Issues are AT Protocol records identified by a TID rkey, similar to pull requests.

tng issue create [flags]                 # Create an issue
tng issue list [flags]                   # List your issues
tng issue view <rkey> [flags]            # View issue details
tng issue close <rkey>                   # Close an issue
tng issue comment <rkey> --body "..."    # Comment on an issue

Flags for issue create:

  • -t, --title -- Issue title (required)
  • -b, --body -- Issue body
  • -R, --repo -- Repository override (owner/name)

Flags for issue list:

  • -R, --repo -- Filter by repository (owner/name)
  • -w, --web -- Open issues page on tangled.sh

Flags for issue view, issue close, issue comment:

  • --author -- DID of the issue author (defaults to the authenticated user)

tng browse -- Open in Browser #

tng browse                           # Open current repo
tng browse src/main.go               # Open specific file
tng browse -n                        # Print URL without opening
tng browse -R owner/repo             # Open specific repo

Utilities #

tng upgrade [--check]                # Upgrade to the latest release (alias: update)
tng version                          # Print version
tng completion <bash|zsh|fish>       # Generate shell completions

Repository Context #

When you run commands inside a cloned Tangled repository, tng automatically detects the repository from git remotes. Both tangled.sh and knot hostnames (e.g. knot.example.com) are recognized. Remote priority:

  1. upstream remote
  2. tangled remote
  3. origin remote

You can override this with:

  • The -R / --repo flag on any command
  • The TNG_REPO environment variable
  • git config tng.default-repo owner/name

Shell Completions #

# Bash
tng completion bash > /etc/bash_completion.d/tng

# Zsh
tng completion zsh > "${fpath[1]}/_tng"

# Fish
tng completion fish > ~/.config/fish/completions/tng.fish

Configuration #

Config file is at ~/.config/tng/config.yml:

default_knot: ""     # Default knot for repo creation
api_host: ""         # Override API host

Architecture #

tng follows the same architectural patterns as the GitHub CLI:

  • Written in Go with cobra for command parsing
  • Factory pattern for dependency injection with lazy initialization
  • AT Protocol OAuth via indigo (PKCE + DPoP)
  • XRPC API client for Tangled's AT Protocol-based endpoints
  • Automatic git remote detection for Tangled repositories

Key differences from gh #

  • Authentication: AT Protocol OAuth 2.0 (not GitHub's device flow). Requires identity resolution through the AT Protocol stack (handle -> DID -> PDS -> auth server).
  • Data model: Issues, PRs, and other collaboration data are AT Protocol records stored on the user's PDS, not on a central server. They are identified by TID record keys, not sequential numbers.
  • Pull requests: Patches are generated via git format-patch, gzip-compressed, and uploaded as blobs to the PDS. Merging sends the patch to the knot for server-side application.
  • Infrastructure: Repositories live on knots (lightweight git servers), not a single monolithic service. The CLI resolves which knot hosts a repo via AT Protocol records.

Project Structure #

cmd/tng/              Entry point
internal/
  tngcmd/             Root command assembly, exit codes
  authflow/           AT Protocol OAuth (PKCE, DPoP, callback server)
  config/             Config files, session storage
  build/              Version info (ldflags, Go build metadata fallback)
pkg/
  cmd/                Command implementations
    auth/             login, logout, status
    repo/             create, clone, delete, view, list
    pr/               create, list, view, diff, merge, close, comment
    issue/            create, list, view, close, comment
    browse/           Open in browser
    upgrade/          Upgrade via the Go module proxy + go install
  cmdutil/            Factory, errors, arg validators
  iostreams/          TTY detection, color, terminal width
api/                  XRPC API client and domain methods
context/              Git remote -> Tangled repo resolution
git/                  Git CLI wrapper, remote URL parsing

License #

MIT