Something went wrong. Try again.
atproto git client
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118#!/usr/bin/env python3"""Tell a Claude Code session what Tangled is and how it signs in to atgc.
Runs at SessionStart and prints a few lines of context, then nothing for therest of the session: `atgc agent` is the full briefing, and this only gets anagent to the point of running it. The lines cover what the briefing does not:which account to sign in as, that `atgc auth login` blocks until the sign-inis approved, and that the account has to be named on every command.
**When it prints.** Only when `atgc` is on `PATH`. Without it the hook printsnothing, and it never names an install command: a hook's output is aninstruction to the agent, and installing software is the human's call. Theplugin's README says how to install atgc.
**Whose account.** On a machine running didbot, each session gets an agentidentity of its own, and its DID reaches the agent in didbot's hook output onits first tool call. This hook runs before that and cannot see it, so it sayswhere the DID appears and writes `<your DID>` in its place. didbot counts aspresent when the `didbot` command is on `PATH` and its agent daemon's socketexists, which is how didbot's own hook and vibescrobble's find it. Withoutdidbot, the session belongs to whoever runs it, and the text drops thesign-in lines.
**Why the account is named on every command.** atgc treats a Claude Codesession as an agent, and an agent never falls back to the default account. Alinked worktree does not read the account `atgc repo configure` set in themain checkout, and the session's shell does not keep an exported variablefrom one command to the next, so `--account` or `ATGC_ACCOUNT` goes on eachcommand.
The hook always exits 0, so a fault in it never blocks a session."""
import jsonimport osimport shutilimport statimport sys
TANGLED = ( "Tangled (tangled.org) is a git forge built on atproto. Repositories are " "hosted on knot servers; pull requests, issues and comments are records " "in each author's own atproto account. `atgc` is its command-line client.")
SIGN_IN = ( "You can sign in to atgc as the agent identity didbot gives this " "session, and open pull requests as yourself. Your DID appears in " "didbot's hook output. The login waits until the sign-in is approved, so " "run it in the background:\n" "\n" " atgc auth login <your DID>\n" "\n" "didbot then shows the sign-in request; approve it with the " "`didbot oauth approve` command it gives.")
ACCOUNT = ( "Name the account on every atgc command, with `--account {who}` or " "`ATGC_ACCOUNT={who}` in front of it. A linked worktree does not read " "the account `atgc repo configure` set in the main checkout, and a " "variable exported in one shell command is gone by the next.")
PATCH_ONLY = ( "Without push access to a repository, open the pull request with " "`atgc pr create --patch-only`, which needs no SSH key.")
BRIEFING = "Run `atgc agent` for everything else."
def daemon_socket(): """Where the didbot agent daemon listens, as didbot's own hook finds it.""" override = os.environ.get("DIDBOT_SOCK") if override: return override runtime = os.environ.get("XDG_RUNTIME_DIR") or "/tmp" return os.path.join(runtime, "didbot-agent", "agent.sock")
def has_didbot(): """Whether didbot gives this session an agent identity.""" if shutil.which("didbot") is None: return False try: return stat.S_ISSOCK(os.stat(daemon_socket()).st_mode) except OSError: return False
def context(): """The text for this session, or `None` when atgc is not installed.""" if shutil.which("atgc") is None: return None if has_didbot(): parts = [TANGLED, SIGN_IN, ACCOUNT.format(who="<your DID>")] else: parts = [TANGLED, ACCOUNT.format(who="<handle|DID>")] parts += [PATCH_ONLY, BRIEFING] return "\n\n".join(parts)
def main(): text = context() if text is None: return output = {"hookEventName": "SessionStart", "additionalContext": text} print(json.dumps({"hookSpecificOutput": output}))
if __name__ == "__main__": try: main() except Exception: pass sys.exit(0)