// The `lchat agents` subcommand family (docs/superpowers/specs/ // 2026-08-05-eleven-claude-agents-design.md): // // - `lchat agents run` IS the workstation bridge (internal/agents) — run on // the machine whose Claude Code sessions a group should share. A pure // outbound client in the bot-sidecar mould: it dials the instance, holds // its member key and the machine's authority HERE, and opens no ports. // // Unlike bot/hooks there is no add-agents control verb: the bridge runs on a // developer workstation, not the router box, so an operator stands it up by // hand — mint an agent invite, write the config, run the command. package main import ( "context" "flag" "fmt" "log" "os" "strings" "lchat/internal/agents" "lchat/internal/bot" ) func runAgentsCmd(args []string) { if len(args) < 1 { fmt.Fprintln(os.Stderr, `usage: lchat agents commands: run run the workstation bridge: front this machine's Claude Code sessions into the group threads the config grants (config: agents.json)`) os.Exit(2) } switch args[0] { case "run": runAgentsRun(args[1:]) default: die("agents: unknown command %q", args[0]) } } func runAgentsRun(args []string) { fs := flag.NewFlagSet("agents run", flag.ExitOnError) base := fs.String("base", envOr("LCHAT_BASE", ""), "instance URL (env: LCHAT_BASE)") session := fs.String("session", envOr("LCHAT_SESSION", "agents-session.json"), "member session file (env: LCHAT_SESSION)") config := fs.String("config", envOr("LCHAT_AGENTS_CONFIG", "agents.json"), "bridge config: which groups get which roots (env: LCHAT_AGENTS_CONFIG)") _ = fs.Parse(reorderFlagsFirst(fs, args)) if *base == "" { die("agents run: set -base (or LCHAT_BASE) to your instance URL") } // The config is the blast radius — fail-closed before touching the network. cfg, err := agents.LoadConfig(*config) if err != nil { die("agents run: %v", err) } // Self-provision on first boot from a one-time agent invite, exactly like // the bot sidecar (#806). Env-only — a flag would leak it into the process // table. sess, err := bot.EnsureSession(strings.TrimRight(*base, "/"), *session, os.Getenv("LCHAT_INVITE"), "Claude", log.Printf) if err != nil { die("agents run: %v", err) } if sess == nil { die("agents run: no session at %s and no LCHAT_INVITE — mint an agent invite (admin → helpers) first", *session) } b := agents.New(strings.TrimRight(*base, "/"), sess, cfg, log.Printf) log.Printf("agents run: bridging %d group(s) on %s", len(cfg.Groups), *base) if err := b.Run(context.Background()); err != nil { die("agents run: %v", err) } }