diff --git a/.agents/skills/tang/SKILL.md b/.agents/skills/tang/SKILL.md new file mode 100644 index 0000000..b8eb493 --- /dev/null +++ b/.agents/skills/tang/SKILL.md @@ -0,0 +1,74 @@ +--- +name: tang +description: | + Use this skill when the user asks about this repo on Tangled, Tangled issues, + Tangled pull requests, or Tangled operations. The current git remote is a + Tangled repo, so `tang` can usually infer the repository from the worktree. +--- + +# tang — Tangled repo workflow + +This repo packages [`tang`](https://tangled.org/onev.cat/tang) in `devenv`. +Use it for Tangled operations. + +## Repository selection + +Prefer the current git worktree. `tang` discovers the repo from `.git` remotes, +so in this repo use commands without `--repo` / `-R` by default: + +```bash +tang status +tang repo view +tang issue list +tang pr list +``` + +Only add `-R /` when the user explicitly asks about a different +Tangled repo. + +## Auth + +Reads often work without auth. Writes require login: + +```bash +bash .agents/skills/tang/auth.sh +``` + +The bootstrap uses `ATPROTO_HANDLE` / `ATPROTO_APP_PASSWORD`, falling back to +`BSKY_HANDLE` / `BSKY_APP_PASSWORD`. App passwords are created at +. Never use the main account password. + +## Common commands for this repo + +```bash +# Inspect +tang status +tang repo view +tang issue list +tang issue view +tang pr list +tang pr view +tang pr diff + +# Issues +tang issue create "Title" --body "Body" +tang issue comment --body "Comment" +tang issue close +tang issue reopen + +# Pull requests +tang pr create --base main --head --title "Title" +tang pr comment --body "Comment" +tang pr checkout +tang pr merge --subject "Merge pull request #N" +``` + +## Identifiers + +Numeric issue and PR IDs match Tangled web URLs. For raw protocol records, use +`--atproto` with an AT URI, rkey, or unique rkey prefix. + +## Safety + +Ask before performing writes against a different repo than the current worktree, +or when auth is unavailable. diff --git a/.agents/skills/tang/auth.sh b/.agents/skills/tang/auth.sh new file mode 100755 index 0000000..97642e9 --- /dev/null +++ b/.agents/skills/tang/auth.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# Bootstrap tang auth for agent use. +# +# Uses tang directly when available, otherwise runs it through devenv. +# Logs in only when tang reports unauthenticated. + +set -euo pipefail + +run_tang() { + if command -v tang >/dev/null 2>&1; then + tang "$@" + elif command -v devenv >/dev/null 2>&1; then + devenv shell -- tang "$@" + else + echo "tang is not on PATH and devenv is unavailable" >&2 + exit 127 + fi +} + +status="$(run_tang auth status --json 2>/dev/null || true)" +if printf '%s\n' "$status" | grep -q '"authenticated"[[:space:]]*:[[:space:]]*true'; then + printf '%s\n' "$status" + exit 0 +fi + +: "${ATPROTO_HANDLE:=${BSKY_HANDLE:-}}" +: "${ATPROTO_APP_PASSWORD:=${BSKY_APP_PASSWORD:-}}" + +if [ -z "${ATPROTO_HANDLE:-}" ] || [ -z "${ATPROTO_APP_PASSWORD:-}" ]; then + echo "Not authenticated." >&2 + echo "Set ATPROTO_HANDLE / ATPROTO_APP_PASSWORD or BSKY_HANDLE / BSKY_APP_PASSWORD." >&2 + echo "Create an app password at https://bsky.app/settings/app-passwords" >&2 + exit 1 +fi + +export ATPROTO_HANDLE ATPROTO_APP_PASSWORD +run_tang auth login --handle "$ATPROTO_HANDLE" <<< "$ATPROTO_APP_PASSWORD" diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..b7734c2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,5 @@ +# Rssbase + +## Dev environment tips +If `DEVENV_ROOT` env variable is defined, just run project binaries (they are available). +If not, run them by prefixing with `devenv shell ...` diff --git a/devenv.custom-code.nix b/devenv.custom-code.nix new file mode 100644 index 0000000..0dfa950 --- /dev/null +++ b/devenv.custom-code.nix @@ -0,0 +1,67 @@ +{ lib }: + +{ + title, + rows, +}: + +let + statusUrl = row: row.statusUrl or row.pkg.meta.changelog or ""; + repoUrl = pkg: pkg.src.gitRepoUrl or pkg.meta.homepage or ""; + linkColumnWidth = builtins.foldl' + (width: row: lib.max width (builtins.stringLength (statusUrl row))) + 0 + rows; + + renderRow = row: '' + row ${lib.escapeShellArg row.name} ${lib.escapeShellArg row.pkg.version} ${lib.escapeShellArg (statusUrl row)} ${lib.escapeShellArg (repoUrl row.pkg)} + ''; +in +'' + printf '%s\n' ${lib.escapeShellArg title} + dim=$'\e[2m' + muted=$'\e[2;90m' + reset=$'\e[0m' + + supports_links() { + [ -t 1 ] && [ "${TERM:-}" != "dumb" ] + } + + link() { + local url="$1" + local text="$2" + + if [ -n "$url" ] && supports_links; then + printf '\e]8;;%s\e\\%s\e]8;;\e\\' "$url" "$text" + else + printf '%s' "$text" + fi + } + + row() { + local name="$1" + local version="$2" + local changelog_url="$3" + local repo_url="$4" + local padding=0 + + printf '%s- %s%-7s %s%-8s%s ' "$dim" "$reset" "$name" "$dim" "$version" "$reset" + if [ -n "$changelog_url" ]; then + printf '%s' "$muted" + link "$changelog_url" "$changelog_url" + padding=$(( ${toString linkColumnWidth} - ''${#changelog_url} )) + [ "$padding" -gt 0 ] && printf '%*s' "$padding" "" + printf ' ' + elif [ -n "$repo_url" ]; then + printf '%s%*s ' "$muted" ${toString linkColumnWidth} "" + fi + if [ -n "$repo_url" ]; then + printf '%s' "$muted" + link "$repo_url" "$repo_url" + printf ' ' + fi + printf '%s\n' "$reset" + } + + ${lib.concatMapStrings renderRow rows} +'' diff --git a/devenv.nix b/devenv.nix index 37e8f83..66f03ed 100644 --- a/devenv.nix +++ b/devenv.nix @@ -3,15 +3,32 @@ ... }: +let + tang = pkgs.callPackage ./tangled-cli/tang.nix { }; + devenvSummary = pkgs.callPackage ./devenv.custom-code.nix { }; +in { # https://devenv.sh/packages/ - packages = [ pkgs.git ]; + packages = [ + pkgs.skills # used to manage .agents/skills + tang # tangled.org cli + ]; # https://devenv.sh/basics/ - enterShell = '' - echo "rssbase devenv:" - echo "- `git --version`" - ''; + enterShell = devenvSummary { + title = "rssbase devenv:"; + rows = [ + { + name = "skills"; + pkg = pkgs.skills; + statusUrl = "https://github.com/vercel-labs/skills/compare/v${pkgs.skills.version}...main"; + } + { + name = "tang"; + pkg = tang; + } + ]; + }; languages.nix.enable = true; diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 0000000..ff044d1 --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,4 @@ +{ + "version": 1, + "skills": {} +} diff --git a/tangled-cli/README.md b/tangled-cli/README.md new file mode 100644 index 0000000..832b4b2 --- /dev/null +++ b/tangled-cli/README.md @@ -0,0 +1,63 @@ +# tangled-cli + +This directory contains the Nix package for `tang`, a command-line client for [Tangled](https://tangled.org). + +Source: [`tangled.org/onev.cat/tang`](https://tangled.org/onev.cat/tang) + +## Usage + +```bash +tang --help +tang version + +# Auth using an ATProto app password · cf https://bsky.app/settings/app-passwords +tang auth status +tang auth login --handle mykiwi.dev +tang auth refresh +tang auth token --json + +# Inspect Tangled state without leaving the terminal +tang status +tang repo list mykiwi.dev --json +tang repo view -R mykiwi.dev/sandbox --json +tang issue list -R mykiwi.dev/sandbox --json +tang pr list -R mykiwi.dev/sandbox --json + +# Work from current git repo, or override with -R / --repo +tang repo view +tang issue list +tang pr list +tang issue list -R onev.cat/tang +tang pr view 1 -R onev.cat/tang + +# Clone repositories +tang repo clone onev.cat/tang +tang repo clone onev.cat/tang .spokes/tang +tang config set clone.protocol ssh # use SSH for OWNER/REPO clones +tang config set clone.protocol https # default + +# Issues +tang issue create "Bug in X" --body "Details..." +tang issue view 1 +tang issue comment 1 --body "Fixed in v1.2" +tang issue edit 1 --title "Better title" +tang issue close 1 +tang issue reopen 1 + +# Pull requests +git push origin feature/foo +tang pr create --base main --head feature/foo --title "Add foo support" +tang pr view 1 +tang pr diff 1 +tang pr comment 1 --body "LGTM" +tang pr checkout 1 +tang pr close 1 +tang pr reopen 1 +tang pr merge 1 --subject "Merge pull request #1" + +# Browser helpers +tang browse +tang browse issue 1 +``` + +Numeric issue and pull request IDs match Tangled web URLs. Use `--atproto` with an AT URI, rkey, or unique rkey prefix for raw protocol records. diff --git a/tangled-cli/tang.nix b/tangled-cli/tang.nix new file mode 100644 index 0000000..0c970b9 --- /dev/null +++ b/tangled-cli/tang.nix @@ -0,0 +1,50 @@ +{ + lib, + buildGoModule, + fetchgit, + installShellFiles, +}: + +let + version = "0.0.3"; + rev = "b70dd08c7e50d309a9e0364726bb10e0ef5a8698"; +in +buildGoModule { + pname = "tang"; + inherit version; + + src = fetchgit { + url = "https://tangled.org/onev.cat/tang"; + inherit rev; + hash = "sha256-67sM40z9urUB5KxkaiJjRcscCcqdG+WLqjpIxDp1LXg="; + }; + + vendorHash = "sha256-OBKIlZUGbvH6Om5/IicVmpXddROpdsKpJe87YvKZnzo="; + + subPackages = [ "cmd/tang" ]; + + ldflags = [ + "-s" + "-w" + "-X main.version=${version}" + "-X main.commit=${rev}" + ]; + + nativeBuildInputs = [ installShellFiles ]; + + postInstall = '' + installShellCompletion --cmd tang \ + --bash <($out/bin/tang completion bash) \ + --zsh <($out/bin/tang completion zsh) \ + --fish <($out/bin/tang completion fish) + ''; + + meta = { + description = "Command-line client for Tangled"; + homepage = "https://tangled.org/onev.cat/tang"; + changelog = "https://tangled.org/onev.cat/tang/commits/v${version}"; + license = lib.licenses.mit; + mainProgram = "tang"; + platforms = lib.platforms.all; + }; +}