@bondsmith/bondsmithd 0.1.14 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +43 -15
  2. package/dist/index.js +557 -243
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -82,20 +82,20 @@ that pane belongs to:
82
82
  tmux set-option -t "$TMUX_PANE" -q @bondsmith-agent-state "waiting:$(date +%s)"
83
83
  ```
84
84
 
85
- The value is `<state>:<epoch seconds>`, where state is `working`, `waiting` (needs the user) or `done`.
85
+ The value is `<state>:<epoch seconds>`, where state is `working`, `waiting` (needs the user) or `done` (finished a turn, shown until the user attaches, types or resizes after it).
86
86
  The daemon reads it on its 5-second tick and shows it on the session. Anything else is ignored, as is
87
87
  a report dated in the future. A `waiting` or `done` report is dropped once the pane writes output more
88
88
  than 2 seconds after it, since that means the agent moved on. A `working` report is dropped once the
89
89
  pane has been silent for 2 minutes, since an interrupted or crashed agent never reports again.
90
90
 
91
91
  Claude Code sessions that Bondsmith starts as plain `claude` report this on their own (see the end of the
92
- plan section below): `working` when a prompt is sent, `waiting` when it asks for permission, finishes a
93
- turn or fails with an API error. Nothing needs installing for those.
92
+ plan section below): `working` when a prompt is sent, `waiting` when it asks for permission or fails with
93
+ an API error, `done` when it finishes a turn. Nothing needs installing for those.
94
94
 
95
95
  For an agent the daemon does not set up (a wrapper, `npx claude`, another agent), note that every session
96
96
  Bondsmith starts has `BONDSMITH_SESSION` set to its id, so a hook installed once for all your projects can
97
97
  stay silent everywhere else. This entry in `~/.claude/settings.json` reports a Claude Code session as
98
- waiting the moment it asks for permission or finishes a turn:
98
+ waiting the moment it asks for permission, and as done when it finishes a turn:
99
99
 
100
100
  ```json
101
101
  {
@@ -116,7 +116,7 @@ waiting the moment it asks for permission or finishes a turn:
116
116
  "hooks": [
117
117
  {
118
118
  "type": "command",
119
- "command": "[ -n \"$BONDSMITH_SESSION\" ] && tmux set-option -t \"$TMUX_PANE\" -q @bondsmith-agent-state \"waiting:$(date +%s)\"; exit 0"
119
+ "command": "[ -n \"$BONDSMITH_SESSION\" ] && tmux set-option -t \"$TMUX_PANE\" -q @bondsmith-agent-state \"done:$(date +%s)\"; exit 0"
120
120
  }
121
121
  ]
122
122
  }
@@ -127,7 +127,7 @@ waiting the moment it asks for permission or finishes a turn:
127
127
 
128
128
  This is optional, and nothing edits your settings for you. The command does nothing outside a Bondsmith
129
129
  session, and sessions started before the daemon set the variable are skipped by it. It reports only
130
- `waiting`. A `working` report is dropped after the pane has been silent for 2 minutes, so one that an
130
+ `waiting` and `done`. A `working` report is dropped after the pane has been silent for 2 minutes, so one that an
131
131
  interrupted Claude Code never clears (it does not run `Stop` then, and has no hook for Escape) expires on
132
132
  its own.
133
133
 
@@ -143,24 +143,25 @@ A hook can store the plan outside the repository instead, in `~/.bondsmith/plans
143
143
  option, so a plan written there is never left over from an earlier session.
144
144
 
145
145
  For Claude Code the daemon sets this up itself. When a session's command is `claude` (that word alone, not
146
- behind `npx`, an environment variable or a wrapper), the daemon starts it as
146
+ behind `npx` or a wrapper; `VAR=value` words before it are fine), the daemon starts it as
147
147
  `claude --settings ~/.bondsmith/hooks/claude-settings.json …`. That file adds hooks to the ones you
148
- already have: after you approve a plan in plan mode, one stores the plan as above, and four report the agent
149
- state (`working` on a prompt; `waiting` on a permission request, the end of a turn, or a turn that fails with
148
+ already have: one stores the plan as above as soon as plan mode proposes it, before you approve it;
149
+ another logs the agent's task calls (`TaskCreate`, `TaskUpdate`, `TodoWrite`) to `~/.bondsmith/plans/<session id>.tasks.jsonl`,
150
+ which the daemon replays into the session's task list (shown under the plan in the browser); and four
151
+ report the agent state (`working` on a prompt; `waiting` on a permission request, the end of a turn, or a turn that fails with
150
152
  an API error). Nothing in `~/.claude` changes, the session still shows the command you typed, and the hooks
151
153
  do nothing outside a Bondsmith session. Claude Code's `/hooks` menu does not list hooks passed with
152
154
  `--settings`, though they run.
153
155
 
154
- A fifth hook stores the latest prompt as the session's headline, which the sessions list and the session bar
155
- show. It is the prompt on one line, cut at 200 characters, in the tmux option `@bondsmith-headline`. A reply
156
- that only answers the agent (`yes`, `continue`, `ok`, `thanks`, and a few like them) or a prompt under 4
157
- characters leaves the earlier headline in place; a built-in slash command such as `/help` sends no prompt to
158
- the hook, so it does too. The prompt is stored as typed, so a secret in a prompt is in the headline.
156
+ A fifth hook watches Bash calls for `gh pr create`. When one succeeds and prints a pull request URL, the URL is
157
+ appended to the tmux option `@bondsmith-prs` (space separated, each once, the newest 20 kept), and the daemon
158
+ reports it on the session as `pullRequestUrls`. It matches `gh pr create` only as a command of its own, so
159
+ `echo "gh pr create"` or `gh pr view` record nothing, and a failed command never reaches it.
159
160
 
160
161
  The daemon leaves the command alone if it already has `--settings`, if the installed `claude` does not list
161
162
  `--settings` in `claude --help` (asked once per daemon run; if `claude` cannot be run at all, that is logged
162
163
  and asked again after a minute), or if `BONDSMITH_AGENT_HOOKS` is set to `off`, `0`, `false` or `no` where
163
- the daemon starts. Each hook has a timeout (5 seconds for a state report or the headline, 10 for the plan), so a stuck command
164
+ the daemon starts. Each hook has a timeout (5 seconds for a state report, 10 for the plan and the task list), so a stuck command
164
165
  cannot hold up the agent. Plans are written to `~/.bondsmith/plans` readable only by you, and a symlink in
165
166
  the way is replaced, not followed. A plan the agent changes later without going back through plan mode is
166
167
  not seen; the hook's plan keeps winning over the folder's file until the next approval.
@@ -204,6 +205,33 @@ tmux only flags a bell when no client is attached to the session or the bell is
204
205
  the one the attached client has open. A session whose terminal is open in a browser tab, even a hidden
205
206
  one, counts as being looked at, so a bell in it is not flagged; use the hook report for that case.
206
207
 
208
+ ## Adding an agent
209
+
210
+ The daemon splits what is the same for every agent from what is one agent's own.
211
+
212
+ | Part | Where | Agent-specific? |
213
+ | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------------- |
214
+ | What an agent reports: state, plan, task list, pull requests (the channels below) | `src/sessions/agent-reports/` | No |
215
+ | Turning reports into what the UI shows (`readSessionReport`: the plan and the task list for `getPlan`; attention state) | `agent-reports/`, `tmux/agent-state.ts` | No |
216
+ | Recognising an agent's command and launching it so it reports | `src/sessions/agents/<agent>/` | Yes |
217
+ | The list of agents | `AGENT_IDS` in `@bondsmith/local-agent-protocol`; `agents/registry.ts` | Add one line each |
218
+
219
+ An agent reports through these channels, and the daemon reads them without caring who wrote them:
220
+
221
+ | Report | Channel |
222
+ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
223
+ | State | tmux option `@bondsmith-agent-state` = `<working\|waiting\|done>:<epoch seconds>` on the session's pane; or ring the bell; or nothing (a pane gone quiet counts as waiting) |
224
+ | Plan | `~/.bondsmith/plans/<session id>.md`, or `.bondsmith/plan.md` in the session's folder, or tmux option `@bondsmith-plan` |
225
+ | Task list | `~/.bondsmith/plans/<session id>.tasks.jsonl`: one JSON `TaskEvent` per line, appended (`add`, `update`, `remove`, `replace`; see `agent-reports/task-list.ts`) |
226
+ | Pull requests | tmux option `@bondsmith-prs`, space-separated URLs |
227
+
228
+ To add an agent: add its id to `AGENT_IDS`, which makes `registry.ts` refuse to compile until it has an adapter. A new
229
+ `AgentAdapter` needs `matches(command)` and `prepareCommand(command)`. With nothing to set up use `plainAdapter(id)`,
230
+ as Codex and opencode do today (they report through the bell and the quiet-pane check). With hooks, put the agent's
231
+ settings and payload mapping in `agents/<agent>/` and build its hook commands from the helpers in
232
+ `agent-reports/hook-commands.ts` and `agent-reports/hook-scripts.ts`; the agent's file only says where in its
233
+ payload the plan, the task event and the shell command are. `agents/claude/` is the worked example.
234
+
207
235
  ## Production connectivity (current status)
208
236
 
209
237
  `bondsmithd` connects outbound to the Bondsmith relay by default (`BONDSMITH_RELAY_ENABLED=true`,