Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 0 additions & 72 deletions .surface
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
72 changes: 72 additions & 0 deletions .surface-breaking
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
},
Expand Down
4 changes: 3 additions & 1 deletion internal/commands/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <file> 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`,
Expand Down
10 changes: 8 additions & 2 deletions internal/commands/connect_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
90 changes: 37 additions & 53 deletions skills/basecamp-connect/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -31,7 +31,6 @@ triggers:
- release the hold
- start the connector
- stop the connector
- basecamp connect service install
- is the connector running
---

Expand Down Expand Up @@ -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 '<profile>'

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 <id>`
(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 '<profile>'` 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 '<folder>' && basecamp connect -P '<profile>'

### 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 '<profile>'`,
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 <id>` (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-'<profile>'.service
systemctl --user status basecamp-connect-'<profile>'.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 '<profile>'`
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 '<profile>'`,
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-'<profile>'.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
Expand All @@ -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.
Loading