diff --git a/.surface b/.surface index dc6d51651..68eba3d39 100644 --- a/.surface +++ b/.surface @@ -677,9 +677,6 @@ CMD basecamp connect doctor CMD basecamp connect import CMD basecamp connect redispatch CMD basecamp connect release -CMD basecamp connect service -CMD basecamp connect service install -CMD basecamp connect service uninstall CMD basecamp connect setup CMD basecamp connect shadow CMD basecamp connect shadow promote @@ -5489,72 +5486,6 @@ FLAG basecamp connect release --stats type=bool FLAG basecamp connect release --styled type=bool FLAG basecamp connect release --todolist type=string FLAG basecamp connect release --verbose type=count -FLAG basecamp connect service --account type=string -FLAG basecamp connect service --agent type=bool -FLAG basecamp connect service --cache-dir type=string -FLAG basecamp connect service --count type=bool -FLAG basecamp connect service --help type=bool -FLAG basecamp connect service --hints type=bool -FLAG basecamp connect service --ids-only type=bool -FLAG basecamp connect service --in type=string -FLAG basecamp connect service --jq type=string -FLAG basecamp connect service --json type=bool -FLAG basecamp connect service --markdown type=bool -FLAG basecamp connect service --md type=bool -FLAG basecamp connect service --no-hints type=bool -FLAG basecamp connect service --no-stats type=bool -FLAG basecamp connect service --profile type=string -FLAG basecamp connect service --project type=string -FLAG basecamp connect service --quiet type=bool -FLAG basecamp connect service --stats type=bool -FLAG basecamp connect service --styled type=bool -FLAG basecamp connect service --todolist type=string -FLAG basecamp connect service --verbose type=count -FLAG basecamp connect service install --account type=string -FLAG basecamp connect service install --agent type=bool -FLAG basecamp connect service install --cache-dir type=string -FLAG basecamp connect service install --count type=bool -FLAG basecamp connect service install --help type=bool -FLAG basecamp connect service install --hints type=bool -FLAG basecamp connect service install --hold type=bool -FLAG basecamp connect service install --ids-only type=bool -FLAG basecamp connect service install --in type=string -FLAG basecamp connect service install --jq type=string -FLAG basecamp connect service install --json type=bool -FLAG basecamp connect service install --markdown type=bool -FLAG basecamp connect service install --md type=bool -FLAG basecamp connect service install --no-enable type=bool -FLAG basecamp connect service install --no-hints type=bool -FLAG basecamp connect service install --no-stats type=bool -FLAG basecamp connect service install --profile type=string -FLAG basecamp connect service install --project type=string -FLAG basecamp connect service install --quiet type=bool -FLAG basecamp connect service install --shadow type=bool -FLAG basecamp connect service install --stats type=bool -FLAG basecamp connect service install --styled type=bool -FLAG basecamp connect service install --todolist type=string -FLAG basecamp connect service install --verbose type=count -FLAG basecamp connect service uninstall --account type=string -FLAG basecamp connect service uninstall --agent type=bool -FLAG basecamp connect service uninstall --cache-dir type=string -FLAG basecamp connect service uninstall --count type=bool -FLAG basecamp connect service uninstall --help type=bool -FLAG basecamp connect service uninstall --hints type=bool -FLAG basecamp connect service uninstall --ids-only type=bool -FLAG basecamp connect service uninstall --in type=string -FLAG basecamp connect service uninstall --jq type=string -FLAG basecamp connect service uninstall --json type=bool -FLAG basecamp connect service uninstall --markdown type=bool -FLAG basecamp connect service uninstall --md type=bool -FLAG basecamp connect service uninstall --no-hints type=bool -FLAG basecamp connect service uninstall --no-stats type=bool -FLAG basecamp connect service uninstall --profile type=string -FLAG basecamp connect service uninstall --project type=string -FLAG basecamp connect service uninstall --quiet type=bool -FLAG basecamp connect service uninstall --stats type=bool -FLAG basecamp connect service uninstall --styled type=bool -FLAG basecamp connect service uninstall --todolist type=string -FLAG basecamp connect service uninstall --verbose type=count FLAG basecamp connect setup --account type=string FLAG basecamp connect setup --agent type=bool FLAG basecamp connect setup --allow type=stringArray @@ -18859,9 +18790,6 @@ SUB basecamp connect doctor SUB basecamp connect import SUB basecamp connect redispatch SUB basecamp connect release -SUB basecamp connect service -SUB basecamp connect service install -SUB basecamp connect service uninstall SUB basecamp connect setup SUB basecamp connect shadow SUB basecamp connect shadow promote diff --git a/.surface-breaking b/.surface-breaking index 48c4d5bb5..f4e10ee61 100644 --- a/.surface-breaking +++ b/.surface-breaking @@ -1148,3 +1148,75 @@ SUB basecamp uploads vaults create SUB basecamp uploads vaults list FLAG basecamp connect setup --remove-route type=stringArray FLAG basecamp connect setup --route type=stringArray +CMD basecamp connect service +CMD basecamp connect service install +CMD basecamp connect service uninstall +FLAG basecamp connect service --account type=string +FLAG basecamp connect service --agent type=bool +FLAG basecamp connect service --cache-dir type=string +FLAG basecamp connect service --count type=bool +FLAG basecamp connect service --help type=bool +FLAG basecamp connect service --hints type=bool +FLAG basecamp connect service --ids-only type=bool +FLAG basecamp connect service --in type=string +FLAG basecamp connect service --jq type=string +FLAG basecamp connect service --json type=bool +FLAG basecamp connect service --markdown type=bool +FLAG basecamp connect service --md type=bool +FLAG basecamp connect service --no-hints type=bool +FLAG basecamp connect service --no-stats type=bool +FLAG basecamp connect service --profile type=string +FLAG basecamp connect service --project type=string +FLAG basecamp connect service --quiet type=bool +FLAG basecamp connect service --stats type=bool +FLAG basecamp connect service --styled type=bool +FLAG basecamp connect service --todolist type=string +FLAG basecamp connect service --verbose type=count +FLAG basecamp connect service install --account type=string +FLAG basecamp connect service install --agent type=bool +FLAG basecamp connect service install --cache-dir type=string +FLAG basecamp connect service install --count type=bool +FLAG basecamp connect service install --help type=bool +FLAG basecamp connect service install --hints type=bool +FLAG basecamp connect service install --hold type=bool +FLAG basecamp connect service install --ids-only type=bool +FLAG basecamp connect service install --in type=string +FLAG basecamp connect service install --jq type=string +FLAG basecamp connect service install --json type=bool +FLAG basecamp connect service install --markdown type=bool +FLAG basecamp connect service install --md type=bool +FLAG basecamp connect service install --no-enable type=bool +FLAG basecamp connect service install --no-hints type=bool +FLAG basecamp connect service install --no-stats type=bool +FLAG basecamp connect service install --profile type=string +FLAG basecamp connect service install --project type=string +FLAG basecamp connect service install --quiet type=bool +FLAG basecamp connect service install --shadow type=bool +FLAG basecamp connect service install --stats type=bool +FLAG basecamp connect service install --styled type=bool +FLAG basecamp connect service install --todolist type=string +FLAG basecamp connect service install --verbose type=count +FLAG basecamp connect service uninstall --account type=string +FLAG basecamp connect service uninstall --agent type=bool +FLAG basecamp connect service uninstall --cache-dir type=string +FLAG basecamp connect service uninstall --count type=bool +FLAG basecamp connect service uninstall --help type=bool +FLAG basecamp connect service uninstall --hints type=bool +FLAG basecamp connect service uninstall --ids-only type=bool +FLAG basecamp connect service uninstall --in type=string +FLAG basecamp connect service uninstall --jq type=string +FLAG basecamp connect service uninstall --json type=bool +FLAG basecamp connect service uninstall --markdown type=bool +FLAG basecamp connect service uninstall --md type=bool +FLAG basecamp connect service uninstall --no-hints type=bool +FLAG basecamp connect service uninstall --no-stats type=bool +FLAG basecamp connect service uninstall --profile type=string +FLAG basecamp connect service uninstall --project type=string +FLAG basecamp connect service uninstall --quiet type=bool +FLAG basecamp connect service uninstall --stats type=bool +FLAG basecamp connect service uninstall --styled type=bool +FLAG basecamp connect service uninstall --todolist type=string +FLAG basecamp connect service uninstall --verbose type=count +SUB basecamp connect service +SUB basecamp connect service install +SUB basecamp connect service uninstall diff --git a/internal/commands/commands.go b/internal/commands/commands.go index 30f473b32..a742a76a3 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -146,7 +146,7 @@ func CommandCategories() []CommandCategory { {Name: "bonfire", Category: "additional", Description: "Multi-chat orchestration", Actions: []string{"split", "layout"}, Experimental: true, DevOnly: true}, {Name: "api", Category: "additional", Description: "Raw API access"}, {Name: "mcp", Category: "additional", Description: "Serve Basecamp to MCP clients over stdio"}, - {Name: "connect", Category: "additional", Description: "Run a local agent connector for a Basecamp agent, and see and decide what it runs", Actions: []string{"setup", "show", "status", "doctor", "redispatch", "discard", "release", "shadow", "import", "service"}}, + {Name: "connect", Category: "additional", Description: "Run a local agent connector for a Basecamp agent, and see and decide what it runs", Actions: []string{"setup", "show", "status", "doctor", "redispatch", "discard", "release", "shadow", "import"}}, {Name: "help", Category: "additional", Description: "Show help"}, {Name: "version", Category: "additional", Description: "Show version"}, }, diff --git a/internal/commands/connect.go b/internal/commands/connect.go index 5f46a60b0..50d88cb3e 100644 --- a/internal/commands/connect.go +++ b/internal/commands/connect.go @@ -61,7 +61,9 @@ wait for review, until basecamp connect release. Linux only. basecamp connect release clear the hold basecamp connect shadow promote make the shadow ledger the connector's, held basecamp connect import apply a cutover reconciliation file - basecamp connect service install have the OS keep it running`, + +Start it in the folder it should work in and leave it running: workers run +there and may change files in it without asking.`, Example: ` basecamp connect setup -P agent --operator-profile me --serve 12345 basecamp connect -P agent basecamp connect -P agent --project 12345 --shadow`, diff --git a/internal/commands/connect_service.go b/internal/commands/connect_service.go index f3a58d6cd..824940ff1 100644 --- a/internal/commands/connect_service.go +++ b/internal/commands/connect_service.go @@ -101,8 +101,14 @@ type connectServiceFlags struct { // newConnectServiceCmd is the service group. func newConnectServiceCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "service", - Short: "Install or remove the OS service that keeps the connector running", + Use: "service", + // Hidden until it's ready: the unit runs the connector in the home + // directory, where the worker may change files without asking, + // freezes PATH at install, is Linux-only, and is untried on real + // machines. People start the connector themselves in the folder it + // should work in. + Hidden: true, + Short: "Install or remove the OS service that keeps the connector running", Long: `Install or remove a systemd user unit that runs the connector and starts it again when it stops. diff --git a/skills/basecamp-connect/SKILL.md b/skills/basecamp-connect/SKILL.md index a274d8831..2e3554b7a 100644 --- a/skills/basecamp-connect/SKILL.md +++ b/skills/basecamp-connect/SKILL.md @@ -7,9 +7,9 @@ description: | and readiness (basecamp connect setup). Explains every setup result and failure. Also reads what the connector ran (status, doctor) and carries out a person's decisions on its records (redispatch, discard, - release, the cutover's shadow promote and import). Starts and supervises the - connector through its systemd service, and reads the pointer lines it - writes. + release, the cutover's shadow promote and import). Tells the person how to + start the connector in the folder it should work in, and reads the pointer + lines it writes. Use when asked to connect an agent, set up or change the connector, add or remove a project, change who can drive the agent, find out why setup says the connector is not ready, or see, retry, close or release what the @@ -31,7 +31,6 @@ triggers: - release the hold - start the connector - stop the connector - - basecamp connect service install - is the connector running --- @@ -465,65 +464,50 @@ consent. **Never run `basecamp connect` yourself.** It runs in the foreground until it is interrupted, so a session that starts it never gets its turn back, and a -connector that lives only as long as your session dies with it. It is a -service. Install the service and let the OS run it. - - basecamp connect service install -P '' - -That writes a systemd user unit, enables it and starts it. The unit restarts -the connector whenever it stops, so a crash or a kill brings it back. - -**A reboot is the exception.** A user manager starts at login, so unless -lingering is on, the connector stays down after a restart until someone signs -in. Install says so when it finds lingering off, and gives the -`sudo loginctl enable-linger` command. Pass that on; do not run it, and do not -tell anyone an unattended machine will come back on its own until they have. -Pass the run's shape to install, not to the connector: `--project ` -(repeatable) to hear only some projects, `--shadow` to admit and log without -dispatching or posting anything, `--hold` to run with the durable hold set. -Installing again over an existing service rewrites the unit and restarts it, so -that is how a project is added or shadow is turned off. - -`basecamp connect service uninstall -P ''` stops it and removes the -unit. The stop is a SIGTERM, which is how the connector is meant to be asked: -it cancels its live workers, posts their completions and exits. Nothing else is -removed — the ledger, the checkpoint and connect.json stay, and installing -again resumes from them. - -Linux only, and the refusal says so: the connector runs nowhere else, so there -is no macOS service to install. If a person on a Mac asks, say that rather than -looking for a launchd flag. +connector that lives only as long as your session dies with it. The person +runs it, in a terminal of their own, and leaves it running: -Quote the profile, as everywhere else in this skill. + cd '' && basecamp connect -P '' -### Seeing what it is doing +**The folder is the agent's workspace.** Every worker runs there and may change +files in it without asking. Suggest a folder made for the agent, or the project +the person wants it to work on. Never suggest their home directory, and if the +connector is already running from it, say so. -Two different questions, two different places. +Stopping it is safe: Ctrl-C (a SIGINT) cancels live workers, posts their +completions and exits. Once it has run and read the feed, it keeps its place: +started again, it catches up on what people asked while it was stopped. Its +very first start has no place to resume from, so it begins at that moment, and +anything asked before then is not picked up. On a server, the person can keep +it running in `tmux` or `screen`. -**What it has heard and run** is `basecamp connect status -P ''`, -which reads the ledger. That is the answer to "is it working", "what is it -holding", "did that mention get picked up". It works whether or not the service -is running. +There is no background service yet. If someone asks for one, say it isn't +available, and that once it has run, the connector catches up whenever it is +started again. -**Whether it is up** is the service: +Pass the run's shape to the connector itself: `--project ` (repeatable) to +hear only some projects, `--shadow` to admit and log without dispatching or +posting anything, `--hold` to run with the durable hold set. - systemctl --user is-active basecamp-connect-''.service - systemctl --user status basecamp-connect-''.service +Linux only for now: on any other system the connector refuses to start, and +says so. If a person on a Mac asks, say that. -A unit in `failed` after several restarts is a connector that cannot start: -the unit gives up after five tries rather than retrying forever and reporting -`activating` while nothing works. Run `basecamp connect doctor -P ''` -and explain the failed check. Once the cause is fixed, `service install` again -— it clears the failure counter, which a bare `systemctl --user restart` does -not, so restarting by hand from `failed` is refused until the window passes. +Quote the profile, as everywhere else in this skill. + +### Seeing what it is doing + +**What it has heard and run** is `basecamp connect status -P ''`, +which reads the ledger. That is the answer to "is it working", "what is it +holding", "did that mention get picked up". It also shows what the last +connector wrote beside its lock, but that is a hint, not an answer: a crashed +connector leaves it behind, and a process id can since belong to something +else. Whether the connector is running is in the person's terminal: ask them. ### The pointer lines While it runs, the connector writes one JSON object per line on stdout — a pointer line per event it saw and per decision it took — and its logs on -stderr. Under the service both land in the journal: - - journalctl --user -u basecamp-connect-''.service -n 50 -o cat +stderr, in the terminal the person started it from. **A pointer line carries no content, and you must not try to make it.** It names ids, the trigger, the class, the route and the state, and nothing of what @@ -532,6 +516,6 @@ person wants to know what was said, the answer is in Basecamp, not here — open the recording the pointer names. Never quote a pointer line into a Basecamp comment, and never reconstruct a message from one. -Use the journal to answer "what happened at 14:32" or "why did that event not +Use those lines to answer "what happened at 14:32" or "why did that event not dispatch". For anything about the current state of a record, prefer `status`: -it is the ledger's own answer, and the journal is a log of how it got there. +it is the ledger's own answer, and the lines are a log of how it got there.