agentp 2.0.1 → 2.1.0
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.
- package/CONTRIBUTING.md +7 -5
- package/README.md +97 -23
- package/bin/agentp +3 -6
- package/bin/ocmux +564 -244
- package/bin/tgagentp +121 -130
- package/docs/specification.md +2 -1
- package/docs/specification_v2.md +188 -624
- package/lib/ocmux.js +26 -289
- package/lib/opencode.js +186 -43
- package/lib/tui-cmd.js +12 -5
- package/lib/tui-registry.js +313 -0
- package/package.json +1 -1
package/CONTRIBUTING.md
CHANGED
|
@@ -6,7 +6,8 @@ agentp is a collection of three **zero-dependency** Node.js CLI tools that
|
|
|
6
6
|
extend [OpenCode](https://opencode.ai) v2:
|
|
7
7
|
|
|
8
8
|
- **`agentp`** — pipes prompt text into a running OpenCode session and streams the answer back to stdout.
|
|
9
|
-
- **`ocmux`** —
|
|
9
|
+
- **`ocmux`** — routes project sessions and optional user-placed dedicated/shared
|
|
10
|
+
TUI panes in tmux on top of user-managed OpenCode servers.
|
|
10
11
|
- **`tgagentp`** — bridges a Telegram bot chat with OpenCode (multi-chat, multi-server, file sharing). *Experimental.*
|
|
11
12
|
|
|
12
13
|
The project aims to stay **zero npm dependencies** — everything uses only the
|
|
@@ -42,12 +43,13 @@ npm install -g .
|
|
|
42
43
|
agentp/
|
|
43
44
|
├── bin/
|
|
44
45
|
│ ├── agentp — stdin-to-session pipe
|
|
45
|
-
│ ├── ocmux — project/
|
|
46
|
+
│ ├── ocmux — project/session router + interactive pickers
|
|
46
47
|
│ └── tgagentp — Telegram bot bridge
|
|
47
48
|
├── lib/
|
|
48
49
|
│ ├── opencode.js — OpenCode v2 HTTP/SSE client (shared by all three)
|
|
49
|
-
│ ├── ocmux.js —
|
|
50
|
+
│ ├── ocmux.js — registered-TUI routing helpers
|
|
50
51
|
│ ├── project-state.js — `.ocmux.json` v2 schema + per-session reminders
|
|
52
|
+
│ ├── tui-registry.js — private runtime registry + tmux pane operations
|
|
51
53
|
│ ├── tui-cmd.js — tmux send-keys passthrough (tgagentp)
|
|
52
54
|
│ ├── file-share.js — telegram-shared directory + upload/download
|
|
53
55
|
│ └── telegram-*.js — Telegram API + formatting helpers
|
|
@@ -72,8 +74,8 @@ agentp/
|
|
|
72
74
|
### Conventions
|
|
73
75
|
|
|
74
76
|
- **HTTP:** use `lib/opencode.js` helpers — never raw `http.request`.
|
|
75
|
-
- **tmux:** use `lib/
|
|
76
|
-
|
|
77
|
+
- **tmux TUI registrations:** use `lib/tui-registry.js`; keep socket, pane,
|
|
78
|
+
token, and PID data out of `.ocmux.json`.
|
|
77
79
|
- **State:** `.ocmux.json` I/O goes through `lib/project-state.js`
|
|
78
80
|
(`readProjectState`, `writeProjectState`, `readAnnotations`, `writeAnnotation`,
|
|
79
81
|
atomic writes). Never hand-roll reads/writes.
|
package/README.md
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
This package provides three CLI tools:
|
|
9
9
|
|
|
10
10
|
- **`agentp`** — pipes prompt text into a running OpenCode server and streams the assistant final answer back to stdout
|
|
11
|
-
- **`ocmux`** —
|
|
11
|
+
- **`ocmux`** — routes project sessions and optionally drives user-placed,
|
|
12
|
+
registered OpenCode TUI panes in tmux (session picker, project switcher,
|
|
13
|
+
create/rename/delete/annotate sessions)
|
|
12
14
|
- **`tgagentp`** — bridges a Telegram bot chat with all running OpenCode servers (receives messages from Telegram, routes them to the active server, sends answers back). Supports slash commands for multi-server management, session switching, agent/model listing, including file sharing from the chat.
|
|
13
15
|
|
|
14
16
|
It is designed for prompt-driven workflows where you want to do things like:
|
|
@@ -42,7 +44,7 @@ npm link
|
|
|
42
44
|
|
|
43
45
|
- Node.js 18+
|
|
44
46
|
- **OpenCode v2** (`opencode serve`) — the server is user-managed; these tools only check it is reachable and complain otherwise.
|
|
45
|
-
- [tmux](https://github.com/tmux/tmux) when using `ocmux`
|
|
47
|
+
- [tmux](https://github.com/tmux/tmux) when using registered `ocmux` TUIs.
|
|
46
48
|
|
|
47
49
|
## Servers
|
|
48
50
|
|
|
@@ -323,18 +325,21 @@ Useful to grab recent answers without sending a new prompt.
|
|
|
323
325
|
|
|
324
326
|
## ocmux
|
|
325
327
|
|
|
326
|
-
Manage
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
328
|
+
Manage project/session routing on top of a user-managed OpenCode server. A
|
|
329
|
+
project is a directory holding a `.ocmux.json` state file recording its target
|
|
330
|
+
session. TUIs are optional: `ocmux tui` can register any tmux pane as a
|
|
331
|
+
project-dedicated display, while `ocmux tui --shared` registers one fallback
|
|
332
|
+
display that follows session switches from every project.
|
|
330
333
|
|
|
331
334
|
```bash
|
|
332
|
-
ocmux [-l] [<subcommand>] [<directory>]
|
|
335
|
+
ocmux [-l] [--all-projects] [<subcommand>] [<directory>]
|
|
333
336
|
```
|
|
334
337
|
|
|
335
338
|
Without arguments (and with a TTY), opens an **interactive session picker** for
|
|
336
339
|
the project found upward from `<directory>` (default: `$PWD`):
|
|
337
340
|
|
|
341
|
+
- the title bar names the project being worked on — `ocmux — <project> sessions`
|
|
342
|
+
— so the list is never ambiguous
|
|
338
343
|
- sessions are listed most-recently-viewed first
|
|
339
344
|
- `/` starts an **incremental search** of the list (matches title/id,
|
|
340
345
|
case-insensitive, space-separated tokens are ANDed). While the search line is
|
|
@@ -351,12 +356,17 @@ the project found upward from `<directory>` (default: `$PWD`):
|
|
|
351
356
|
every selected session.
|
|
352
357
|
- `n` create (name input) · `r` rename (edit in place) · `R` set a reminder ·
|
|
353
358
|
`d` delete (confirm) · `a` switch agent · `m` switch model · `p` project
|
|
354
|
-
switcher · `h` help · `q` quit
|
|
359
|
+
switcher (inspect-only by default) · `h` help · `q` quit
|
|
360
|
+
- `m` opens the model list **sorted by provider** with the cursor already on the
|
|
361
|
+
session's **current model** — also in broadcast mode, where it starts from the
|
|
362
|
+
cursor session's model, so a bulk change begins where you are
|
|
355
363
|
- **`q` only quits the session picker.** In every other menu (model/agent
|
|
356
364
|
pickers, project switcher, help/input prompts) `q`/`ESC` just closes that menu
|
|
357
365
|
and returns to the previous one. **`Ctrl+C` fully exits** `ocmux` from any menu.
|
|
358
|
-
- switching updates `.ocmux.json` and
|
|
359
|
-
|
|
366
|
+
- switching updates `.ocmux.json` and refreshes the project's dedicated TUI, or
|
|
367
|
+
the shared TUI when no live dedicated one exists; silent on success. The file
|
|
368
|
+
updated is always the one of the project being listed — your own project
|
|
369
|
+
unless ocmux was started with `--all-projects`
|
|
360
370
|
- new sessions inherit the model of the previously selected session (v2
|
|
361
371
|
sessions created via the API have no model and won't run a prompt until set)
|
|
362
372
|
|
|
@@ -370,32 +380,96 @@ reported individually with session name, id, error, and timestamp.
|
|
|
370
380
|
|
|
371
381
|
Subcommands:
|
|
372
382
|
|
|
373
|
-
- **`serve [--server <url>] [--git|--GIT] [dir]`** —
|
|
383
|
+
- **`serve [--server <url>] [--git|--GIT] [dir]`** — initialize project state
|
|
374
384
|
(checks the server is reachable first). Aliased as `new` for backwards
|
|
375
385
|
compatibility. `--git`/`--GIT` resolve `dir` to the nearest parent with a
|
|
376
386
|
`.git` entry / directory.
|
|
387
|
+
- **`tui [--shared] [--server <url>] [dir]`** — register the current tmux pane and run OpenCode
|
|
388
|
+
in it. Without `--shared`, the pane follows only that project. With
|
|
389
|
+
`--shared`, it becomes the one fallback TUI for all projects and can reconnect
|
|
390
|
+
across server URLs as selections change. Re-registering a slot stops its old
|
|
391
|
+
wrapper without destroying the old pane. Closing the TUI unregisters it.
|
|
392
|
+
`tui --list` lists live registrations; `tui [--shared] --status` inspects a
|
|
393
|
+
dedicated/shared slot; `tui [--shared] --detach` unregisters that slot without
|
|
394
|
+
closing the pane or its current TUI.
|
|
377
395
|
- **`session <id|title> [dir]`** — non-interactive session switch.
|
|
378
|
-
- **`list [-l]`** — list
|
|
379
|
-
|
|
380
|
-
- **`kill [dir]`** — close the project's TUI window. **Keeps `.ocmux.json`**
|
|
381
|
-
(session memory; marks it `stopped`).
|
|
382
|
-
- **`resurrect [dir]`** — recreate the project window from its state file.
|
|
396
|
+
- **`list [-l]`** — list configured projects and whether their route is
|
|
397
|
+
`project`, `shared`, or `headless` (with `-l`, their server URL).
|
|
383
398
|
- **`migrate`** — rewrite legacy (v1-style) `.ocmux.json` files to the v2 schema.
|
|
384
399
|
|
|
385
400
|
The old `switch` subcommand is gone: press **`p`** inside the session picker to
|
|
386
|
-
open the
|
|
401
|
+
open the project switcher instead. The switcher is a **foldable tree**: `▸`/`▾`
|
|
402
|
+
marks a project as folded/unfolded, `Space` folds/unfolds a project's sessions
|
|
403
|
+
(fetched once per project, most recent first), and `/` searches projects *and*
|
|
404
|
+
their unfolded sessions (a matching session keeps its project header visible).
|
|
405
|
+
|
|
406
|
+
By default the switcher is an **inspector**: `Enter` on a project row routes its
|
|
407
|
+
current session to the applicable registered TUI, `Enter`/`Space` on a session
|
|
408
|
+
row shows that session, and you **always come back to your own project's
|
|
409
|
+
list** when you leave it (`q`). No `.ocmux.json` is ever written from there, so
|
|
410
|
+
`agentp` — which reads the state file of the directory it runs in — keeps
|
|
411
|
+
prompting your own project's session.
|
|
412
|
+
|
|
413
|
+
Pass **`--all-projects`** to turn it into a real switcher: selecting a project
|
|
414
|
+
then moves the session picker over to that project, and picking a session there
|
|
415
|
+
updates *that* project's `.ocmux.json` (which is what `agentp` reads when it
|
|
416
|
+
runs in that directory). The title bar always names the project being listed, so
|
|
417
|
+
either way you can tell where you are. `ocmux` never starts or stops the
|
|
387
418
|
OpenCode server — run `opencode serve` yourself (see [Versioning](#versioning)
|
|
388
419
|
for the pairing policy).
|
|
389
420
|
|
|
390
|
-
Options: `-l` · `--
|
|
421
|
+
Options: `-l` · `--all-projects` · `--shared` (only with `tui`) · `--version` ·
|
|
422
|
+
`-h` · `--` (treat the next argument as a directory). There is no `--global`
|
|
423
|
+
alias.
|
|
391
424
|
|
|
392
425
|
Notes:
|
|
393
426
|
|
|
394
|
-
- If `<directory>` is not a valid path, `ocmux` matches it against the basenames
|
|
395
|
-
of existing project windows (exact unique match).
|
|
396
427
|
- If the server is password-protected (`OPENCODE_SERVER_PASSWORD`), both
|
|
397
428
|
`agentp` and `ocmux` send the required HTTP Basic Auth credentials.
|
|
398
429
|
|
|
430
|
+
### TUI runtime registry
|
|
431
|
+
|
|
432
|
+
TUI placement is ephemeral and is never written to `.ocmux.json`. Registrations
|
|
433
|
+
live in `$XDG_RUNTIME_DIR/agentp/ocmux-tuis.json` (falling back to a private
|
|
434
|
+
`/tmp/agentp-<uid>/` directory); `OCMUX_RUNTIME_DIR` overrides that location.
|
|
435
|
+
The directory is mode `0700`, registry files are mode `0600`, and updates use a
|
|
436
|
+
lock plus atomic rename.
|
|
437
|
+
|
|
438
|
+
A registration stores the tmux socket, pane ID, diagnostic wrapper PID, and a
|
|
439
|
+
random pane verification token. Matching ID/token options are also written to
|
|
440
|
+
the pane before it can be respawned. This prevents stale entries from targeting
|
|
441
|
+
an unrelated pane and lets a pane move between windows or tmux sessions on the
|
|
442
|
+
same socket without re-registering. Dead registrations are pruned when queried.
|
|
443
|
+
|
|
444
|
+
Closing OpenCode makes its foreground `ocmux tui` wrapper unregister itself if
|
|
445
|
+
it still owns the token. Replacing a dedicated/shared registration interrupts
|
|
446
|
+
the previous wrapper but preserves its pane. `--detach` only unregisters and
|
|
447
|
+
clears the pane markers; it does not close the pane or the OpenCode process.
|
|
448
|
+
|
|
449
|
+
Registry commands:
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
ocmux tui # dedicated TUI for the current project
|
|
453
|
+
ocmux tui /path/to/project # dedicated TUI for another project
|
|
454
|
+
ocmux tui --shared # the single cross-project fallback TUI
|
|
455
|
+
ocmux tui --list # all live dedicated/shared registrations
|
|
456
|
+
ocmux tui --status # current project's dedicated slot
|
|
457
|
+
ocmux tui --status --shared # shared slot
|
|
458
|
+
ocmux tui --detach # unregister current project's slot
|
|
459
|
+
ocmux tui --detach --shared # unregister shared slot
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Routing precedence is:
|
|
463
|
+
|
|
464
|
+
1. a live TUI dedicated to the selected project;
|
|
465
|
+
2. the single live shared TUI;
|
|
466
|
+
3. headless operation (the session still switches successfully).
|
|
467
|
+
|
|
468
|
+
Switching respawns the registered pane with `ocmux tui`, which reconnects
|
|
469
|
+
OpenCode to the selected server, directory, and session. Server-side work keeps
|
|
470
|
+
running, but client-local TUI state such as a draft prompt or scroll position is
|
|
471
|
+
lost during the refresh.
|
|
472
|
+
|
|
399
473
|
### State file
|
|
400
474
|
|
|
401
475
|
`.ocmux.json` (in the project directory) stores `version`, `directory`,
|
|
@@ -408,8 +482,8 @@ gitignored — never commit it.
|
|
|
408
482
|
2. Resolves the target from the nearest `.ocmux.json`: server URL, project
|
|
409
483
|
directory, and the stored session id (falls back to the most recently viewed
|
|
410
484
|
session in that directory, or creates one pinned to the directory).
|
|
411
|
-
3.
|
|
412
|
-
|
|
485
|
+
3. Prepends the session annotation (if any) and sends the prompt via the v2
|
|
486
|
+
session API (`POST /api/session/:id/prompt`,
|
|
413
487
|
delivering to that session regardless of what the TUI shows).
|
|
414
488
|
4. Attaches to the SSE stream first so no events are missed, and streams text
|
|
415
489
|
until the session is quiescent after its terminal signal.
|
|
@@ -451,7 +525,7 @@ Non-text Telegram updates (photos, stickers, etc.) are silently ignored.
|
|
|
451
525
|
| `/servers` | List all ocmux-served projects (▶ active, 🔌 disconnected, 💀 dead) |
|
|
452
526
|
| `/server <name>` | Switch active server; matches by full path, basename, or substring |
|
|
453
527
|
| `/server --force <name>` | Take over a server from another chat |
|
|
454
|
-
| `/resurrect [path]` |
|
|
528
|
+
| `/resurrect [path]` | Check the configured user-managed server and explain how to restart it externally |
|
|
455
529
|
| `/sessions` | List sessions (numbered, newest first) |
|
|
456
530
|
| `/session <name-or-number>` | Switch to a session by name or position |
|
|
457
531
|
| `/session new [name]` | Create a new session |
|
package/bin/agentp
CHANGED
|
@@ -21,7 +21,6 @@ const {
|
|
|
21
21
|
getActiveSessions,
|
|
22
22
|
} = require('../lib/opencode');
|
|
23
23
|
const projectState = require('../lib/project-state');
|
|
24
|
-
const ocmuxLib = require('../lib/ocmux');
|
|
25
24
|
|
|
26
25
|
const TGAGENTP_PORT_FILE = '/tmp/tgagentp-port';
|
|
27
26
|
|
|
@@ -1151,7 +1150,6 @@ async function main() {
|
|
|
1151
1150
|
let sessionId = null;
|
|
1152
1151
|
let answer;
|
|
1153
1152
|
if (broadcastIds) {
|
|
1154
|
-
try { if (projectDir) ocmuxLib.focusWindowByDir(projectDir); } catch {}
|
|
1155
1153
|
const bres = await runBroadcastSend(serverBase, broadcastIds, actualPromptText, {
|
|
1156
1154
|
getActiveSessions,
|
|
1157
1155
|
getSession,
|
|
@@ -1173,17 +1171,16 @@ async function main() {
|
|
|
1173
1171
|
preferredSession,
|
|
1174
1172
|
});
|
|
1175
1173
|
|
|
1176
|
-
//
|
|
1177
|
-
//
|
|
1174
|
+
// Prepend the session's annotation (if any) to the prompt. TUI routing is
|
|
1175
|
+
// owned by explicit ocmux session switches, not by prompt submission.
|
|
1178
1176
|
let prompt = actualPromptText;
|
|
1179
1177
|
try {
|
|
1180
1178
|
if (ctx && ctx.statefile) {
|
|
1181
1179
|
const ann = projectState.readAnnotations(ctx.statefile)[sessionId];
|
|
1182
1180
|
if (ann) prompt = ann + '\n\n' + actualPromptText;
|
|
1183
1181
|
}
|
|
1184
|
-
if (projectDir) ocmuxLib.focusWindowByDir(projectDir);
|
|
1185
1182
|
} catch {
|
|
1186
|
-
//
|
|
1183
|
+
// Annotation lookup is best-effort.
|
|
1187
1184
|
}
|
|
1188
1185
|
|
|
1189
1186
|
try {
|