ace-herdr 0.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.
- checksums.yaml +7 -0
- data/.ace-defaults/herdr/config.yml +26 -0
- data/.ace-defaults/herdr/tabs/agent.yml +11 -0
- data/.ace-defaults/herdr/workspaces/development.yml +23 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE +21 -0
- data/README.md +54 -0
- data/Rakefile +12 -0
- data/docs/usage.md +306 -0
- data/exe/ace-herdr +17 -0
- data/lib/ace/herdr/atoms/answer_digest.rb +18 -0
- data/lib/ace/herdr/cli/commands/capture.rb +55 -0
- data/lib/ace/herdr/cli/commands/close.rb +53 -0
- data/lib/ace/herdr/cli/commands/deliver.rb +67 -0
- data/lib/ace/herdr/cli/commands/dispatch.rb +62 -0
- data/lib/ace/herdr/cli/commands/list.rb +71 -0
- data/lib/ace/herdr/cli/commands/list_presets.rb +47 -0
- data/lib/ace/herdr/cli/commands/send.rb +103 -0
- data/lib/ace/herdr/cli/commands/support.rb +68 -0
- data/lib/ace/herdr/cli/commands/tab.rb +54 -0
- data/lib/ace/herdr/cli/commands/tidy.rb +65 -0
- data/lib/ace/herdr/cli/commands/wait.rb +88 -0
- data/lib/ace/herdr/cli/commands/workspace.rb +52 -0
- data/lib/ace/herdr/cli.rb +98 -0
- data/lib/ace/herdr/errors.rb +51 -0
- data/lib/ace/herdr/models/delivery_record.rb +109 -0
- data/lib/ace/herdr/models/dispatch_outcome.rb +29 -0
- data/lib/ace/herdr/molecules/delivery_record_store.rb +150 -0
- data/lib/ace/herdr/molecules/herdr_executor.rb +264 -0
- data/lib/ace/herdr/molecules/pane_tidy_probe.rb +80 -0
- data/lib/ace/herdr/molecules/preset_loader.rb +70 -0
- data/lib/ace/herdr/molecules/preset_resolver.rb +88 -0
- data/lib/ace/herdr/organisms/control_surface.rb +575 -0
- data/lib/ace/herdr/organisms/deliverer.rb +358 -0
- data/lib/ace/herdr/organisms/dispatcher.rb +130 -0
- data/lib/ace/herdr/organisms/tidy.rb +188 -0
- data/lib/ace/herdr/version.rb +7 -0
- data/lib/ace/herdr.rb +103 -0
- metadata +224 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 11805892b3ae5bdf74d4bdd555f8e508e7fe3693476d07fa537a41b9f891857e
|
|
4
|
+
data.tar.gz: 2aa907e23b109838153215ac004f344045220d2c77cf0f295a99c6ff55a75cb2
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 53e2cbd3f3c97db17b5e14609e54deb210e0d7474886dc4f6fe7d78a33154df1cb7fe680a565aa500d534c0ce06235dd20445f9ae6bb867d5b989732c5a5fa70
|
|
7
|
+
data.tar.gz: 9397dfe3b7fb8ac0d9b42f2a43ad7ac56204c7b70197e5f1a6095a00f62f38ecb8d7a0967ca0ab5d68c91864656c86db25a9cc9bc0274fa65c63d74216771e09
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ace-herdr default configuration
|
|
2
|
+
# Override in ~/.ace/herdr/config.yml or .ace/herdr/config.yml
|
|
3
|
+
|
|
4
|
+
# Agent kind used when bootstrapping a pane without an agent
|
|
5
|
+
# (herdr agent start --kind)
|
|
6
|
+
default_agent_kind: pi
|
|
7
|
+
|
|
8
|
+
# Push delivery retries; deterministic fixed backoff, no jitter
|
|
9
|
+
delivery:
|
|
10
|
+
max_attempts: 3
|
|
11
|
+
backoff_seconds: [1, 2, 4]
|
|
12
|
+
|
|
13
|
+
# Timeouts in seconds
|
|
14
|
+
timeouts:
|
|
15
|
+
agent_start: 60
|
|
16
|
+
prompt: 30
|
|
17
|
+
wait: 30
|
|
18
|
+
|
|
19
|
+
# Delivery record root (ADR-029 short-name layout), relative to project root
|
|
20
|
+
deliveries_dir: .ace-local/herdr/deliveries
|
|
21
|
+
|
|
22
|
+
# Tidy (spec 8wq.t.1w0): archive delivered records strictly older than this
|
|
23
|
+
# many days (age = record updated_at); failed/retryable/pending are never
|
|
24
|
+
# touched
|
|
25
|
+
tidy:
|
|
26
|
+
delivered_retention_days: 7
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Default tab preset: one pane hosting a pi agent (no initial prompt;
|
|
2
|
+
# add `prompt:` under agent to submit one at creation).
|
|
3
|
+
#
|
|
4
|
+
# Override in ~/.ace/herdr/tabs/ or .ace/herdr/tabs/
|
|
5
|
+
# (project wins; see ace-herdr docs/usage.md for the full schema).
|
|
6
|
+
label: agent
|
|
7
|
+
panes:
|
|
8
|
+
- label: agent
|
|
9
|
+
agent:
|
|
10
|
+
kind: pi
|
|
11
|
+
name: task-agent
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Default workspace preset: a work tab with a shell and a pi agent side by
|
|
2
|
+
# side, plus a scratch shell tab.
|
|
3
|
+
#
|
|
4
|
+
# Override in ~/.ace/herdr/workspaces/ or .ace/herdr/workspaces/
|
|
5
|
+
# (project wins; see ace-herdr docs/usage.md for the full schema).
|
|
6
|
+
label: development
|
|
7
|
+
focus: true
|
|
8
|
+
tabs:
|
|
9
|
+
- label: work
|
|
10
|
+
panes:
|
|
11
|
+
- label: shell
|
|
12
|
+
- label: agent
|
|
13
|
+
agent:
|
|
14
|
+
kind: pi
|
|
15
|
+
name: development-agent
|
|
16
|
+
prompt: Ready to work on the current task.
|
|
17
|
+
splits:
|
|
18
|
+
- direction: right
|
|
19
|
+
target: shell
|
|
20
|
+
pane: agent
|
|
21
|
+
- label: scratch
|
|
22
|
+
panes:
|
|
23
|
+
- label: shell
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `ace-herdr` will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-09-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `ace-herdr tidy` (spec 8wq.t.1w0): dry-run-by-default cleanup of finished agent panes and delivery records. Pane closure requires positive completion evidence only (observed `done` agent state, or a pane whose native `pane process-info` shows no live foreground process — herdr omits `foreground_processes` when empty) re-confirmed by a fresh probe immediately before the rename-to-`done`-then-close mutation; revived or uncertain candidates are excluded and never mutated. Delivered records strictly older than the new `tidy.delivered_retention_days` config key (default 7) are atomically archived to `deliveries_dir/archive/` after a lock-guarded reload re-proves eligibility; `pending`/`retryable`/`failed` and unreadable records are never touched, an unreadable record encountered during apply is preserved and reported instead of aborting. Archival never breaks delivery idempotency (`deliver`/`--resume` consult the archive copy, so identical re-delivery still short-circuits and conflicts still fail closed). Deterministic one-line JSON report with explicit empty states; an unreachable herdr runtime fails with an explicit error and no partial report.
|
|
12
|
+
- Terminal-control surface matching the ace-tmux intent vocabulary (spec 8wq.t.k84), keeping one-line JSON output: `ace-herdr list` (panes/tabs/workspaces, `--workspace` scoping, explicit empty arrays), `ace-herdr send` (declaration-ordered `--cmd`/`--msg`/`--key`; agent-aware routing sends one self-submitting `agent prompt` on agent panes with `agent_blocked`/`agent_prompt_stalled` gating and drop-and-report of a single trailing `--key Enter`; invalid shapes fail closed before any transport call), `ace-herdr capture` (raw `pane read` text, `--source visible|recent`), `ace-herdr wait --for output --pattern` (literal match, immediate check then poll, seconds→ms conversion, timeout CLI error) alongside the unchanged agent wait, and `--list-presets`.
|
|
13
|
+
- Preset-driven creation: `ace-herdr workspace <preset>` / `ace-herdr tab <preset>` compose YAML layouts through the ADR-022 cascade (`.ace/herdr/` > `~/.ace/herdr/` > gem `.ace-defaults/herdr/`, ships `workspaces/development.yml` + `tabs/agent.yml`), with recursive `preset:` inheritance (deep merge, array concat, cycle detection, fail-closed missing references), deterministic creation order (workspace → tabs → root pane + splits → renames → pane commands → readiness-gated agent starts; herdr's seeded initial tab is closed once the declared tabs exist), CLI `--cwd` overrides (CLI > tab > root > pane), and pre-creation layout validation (unplaced panes, duplicate placements, unknown split targets/directions fail closed and create nothing). Includes a tmux-intent ↔ herdr-command parity table in `docs/usage.md`.
|
|
14
|
+
- `Ace::Herdr::Organisms::ControlSurface`: owner of the terminal-control behavior — native JSON normalization into public payloads, send validation/routing, and preset instantiation.
|
|
15
|
+
- `Ace::Herdr::Molecules::PresetLoader` (ADR-022 cascade discovery) and `Ace::Herdr::Molecules::PresetResolver` (`preset:` reference resolution).
|
|
16
|
+
- `Ace::Herdr::Molecules::HerdrExecutor`: native operations for workspace/tab/pane listing, pane read/send-text/send-keys, pane wait-output, pane split, workspace create, agent send-keys, and focus flags on tab/workspace creation; typed `TabNotFoundError`/`WorkspaceNotFoundError` classification; herdr machine codes are now carried in executor error messages (for example `pane_not_found: ...`); output-wait timeouts raise `WaitTimeoutError`.
|
|
17
|
+
- Gem bootstrap (spec 8wm.t.vs0): push delivery + agent bootstrap for the Herdr runtime, implementing the ace-hitl provider delivery contract (`deliver(ref, answer)` -> `DeliverResult` with `:delivered`/`:retryable`/`:failed`).
|
|
18
|
+
- `Ace::Herdr::Organisms::Deliverer`: idempotent per event id with write-ahead delivery records under `.ace-local/herdr/deliveries/` (0600, atomic writes; the full answer is persisted so a crash never loses it, recoverable via `ace-herdr deliver --resume`), per-event lock serializing concurrent deliveries, duplicate-identical short-circuit, fail-closed content/destination conflicts, bootstrap of missing agents (`herdr agent start` with shell-escaped `HERDR_SESSION`/`HERDR_PANE` exported into the pane shell), a readiness gate before prompting, retry limits with fixed deterministic backoff (covering prompt and probe failures), and terminal-failure reporting — including the ambiguous crash window after a submitted prompt, reported instead of silently resent.
|
|
19
|
+
- `Ace::Herdr::Organisms::Dispatcher`: one-command subagent dispatch — tab + `herdr agent start` + prompt with deterministic defaults (caller's workspace, label = agent/pane name, prompt from file/stdin/`--no-prompt`); workspace and pane identifiers are token-validated and shell-escaped before they reach a pane shell.
|
|
20
|
+
- `ace-herdr` CLI: `deliver`, `dispatch`, `wait`, `close` (JSON output, exception-based exit codes per ADR-023).
|
|
21
|
+
- `Ace::Herdr::Molecules::HerdrExecutor`: the single seam to the herdr CLI (argv arrays per ADR-031) with typed error classification from herdr's machine-readable codes (`agent_blocked`, `agent_prompt_stalled`, `pane_not_found`, `timeout`).
|
|
22
|
+
- Config cascade (ADR-022) defaults: `default_agent_kind`, delivery retry limits/backoff, timeouts, deliveries dir (ADR-029).
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Michal Czyz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1> ACE - HERDR </h1>
|
|
3
|
+
|
|
4
|
+
Zero-token push delivery and agent bootstrap for the Herdr runtime, behind the ace-hitl provider delivery contract.
|
|
5
|
+
|
|
6
|
+
<img src="../docs/brand/AgenticCodingEnvironment.Logo.XS.jpg" alt="ACE Logo" width="480">
|
|
7
|
+
<br><br>
|
|
8
|
+
|
|
9
|
+
<a href="https://rubygems.org/gems/ace-herdr"><img alt="Gem Version" src="https://img.shields.io/gem/v/ace-herdr.svg" /></a>
|
|
10
|
+
<a href="https://www.ruby-lang.org"><img alt="Ruby" src="https://img.shields.io/badge/Ruby-3.2+-CC342D?logo=ruby" /></a>
|
|
11
|
+
<a href="https://opensource.org/licenses/MIT"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg" /></a>
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
> Works with: Claude Code, Codex CLI, OpenCode, Gemini CLI, pi-agent, and more.
|
|
16
|
+
|
|
17
|
+
[Usage Guide](docs/usage.md)
|
|
18
|
+
|
|
19
|
+
`ace-herdr` is to herdr what `ace-tmux` is to tmux: a deterministic, zero-token wrapper over the `herdr` CLI. It implements the ace-hitl push-delivery contract — `deliver(ref, answer)` maps to `herdr agent prompt <pane>`, bootstrapping the agent first (`herdr agent start`) when the pane has none, so an answer is never lost — and adds one-command agent dispatch, noiseless monitoring, and closure on top.
|
|
20
|
+
|
|
21
|
+
## How It Works
|
|
22
|
+
|
|
23
|
+
1. Every ace-hitl ask captures a reverse address (`HERDR_SESSION` / `HERDR_PANE`, schema `ace.hitl.ref/v1`). `ace-herdr deliver` pushes an answer back to that address.
|
|
24
|
+
2. Delivery is idempotent per event id: write-ahead delivery records under `.ace-local/herdr/deliveries/` make retries safe — identical content is never delivered twice.
|
|
25
|
+
3. If the target pane has no agent, `ace-herdr` bootstraps one (`herdr agent start --kind <kind> --pane <pane>`), waits for readiness, and exports `HERDR_SESSION` / `HERDR_PANE` into the agent's environment.
|
|
26
|
+
4. Retryable failures back off on a fixed deterministic schedule; terminal failures are reported and persisted in the delivery record.
|
|
27
|
+
5. `ace-herdr dispatch`, `wait`, and `close` give agents one-command subagent lifecycle with sensible defaults (same workspace as the caller, label = task id, prompt from file or stdin).
|
|
28
|
+
|
|
29
|
+
## Use Cases
|
|
30
|
+
|
|
31
|
+
- Push an HITL answer to the pane that asked, even if its agent was restarted since.
|
|
32
|
+
- Dispatch a subagent in one command with deterministic defaults and no LLM decisions.
|
|
33
|
+
- Wait for agent readiness or completion without scraping pane noise.
|
|
34
|
+
- Close out finished agent panes (rename/close) and return their result.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Install as part of the ACE toolkit (see the repo README), or add to your Gemfile:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
gem "ace-herdr"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Testing
|
|
45
|
+
|
|
46
|
+
Fast tests only (no live herdr needed):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
ace-test ace-herdr
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## License
|
|
53
|
+
|
|
54
|
+
MIT — see [LICENSE](LICENSE).
|
data/Rakefile
ADDED
data/docs/usage.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
---
|
|
2
|
+
doc-type: user
|
|
3
|
+
title: ace-herdr Usage
|
|
4
|
+
purpose: Full CLI and configuration reference for ace-herdr: push delivery, agent bootstrap, the terminal-control surface (list, send, capture, wait, presets), and tidy cleanup.
|
|
5
|
+
ace-docs:
|
|
6
|
+
last-updated: 2026-09-28
|
|
7
|
+
last-checked: 2026-09-28
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Usage
|
|
11
|
+
|
|
12
|
+
`ace-herdr` is a zero-token wrapper over the `herdr` CLI. It implements the ace-hitl push-delivery contract -- `deliver(ref, answer)` -> `herdr agent prompt <pane>` -- one-command agent dispatch, noiseless waiting and pane closure, the terminal-control intents known from `ace-tmux` (`list`, `send`, `capture`, output waits, preset-driven workspace/tab creation), and dry-run-first cleanup of finished panes and delivery records (`tidy`). No LLM is consulted anywhere in the gem.
|
|
13
|
+
|
|
14
|
+
## Command Surface
|
|
15
|
+
|
|
16
|
+
- `ace-herdr deliver [OPTIONS]`
|
|
17
|
+
- `ace-herdr dispatch [OPTIONS]`
|
|
18
|
+
- `ace-herdr list [--panes|--tabs|--workspaces] [--workspace ID] [--quiet]`
|
|
19
|
+
- `ace-herdr send [--cmd TEXT] [--msg TEXT...] [--key NAME...] --pane ID [--quiet]`
|
|
20
|
+
- `ace-herdr capture --pane ID [--lines N] [--source visible|recent]`
|
|
21
|
+
- `ace-herdr wait --pane ID (--for output --pattern PATTERN | --for agent [--until ...]) [--timeout S] [--quiet]`
|
|
22
|
+
- `ace-herdr close [OPTIONS]`
|
|
23
|
+
- `ace-herdr tidy [--apply] [--quiet]`
|
|
24
|
+
- `ace-herdr workspace <preset> [--cwd PATH] [--quiet]`
|
|
25
|
+
- `ace-herdr tab <preset> [--workspace ID] [--cwd PATH] [--quiet]`
|
|
26
|
+
- `ace-herdr --list-presets [workspaces|tabs]`
|
|
27
|
+
|
|
28
|
+
## Output policy
|
|
29
|
+
|
|
30
|
+
Control commands print exactly one deterministic JSON line on stdout; `--quiet` suppresses it. Failures surface herdr's machine error codes in the CLI error message (for example `pane_not_found: pane w9:p1 not found`) and exit non-zero. The one exception is `capture`, which prints raw pane text without any JSON wrapping.
|
|
31
|
+
|
|
32
|
+
herdr *sessions* (server persistence) are intentionally not exposed; the tmux session analogue is the herdr **workspace**.
|
|
33
|
+
|
|
34
|
+
## tmux-intent ↔ herdr-command parity
|
|
35
|
+
|
|
36
|
+
Every common terminal-control intent available through `ace-tmux` is available through `ace-herdr` (herdr 0.9.1). Parity is of INTENT and FLAG VOCABULARY, not byte-format: ace-herdr keeps one-line JSON where ace-tmux renders human tables.
|
|
37
|
+
|
|
38
|
+
| ace-tmux intent | ace-herdr command | Native herdr call |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `list` (panes) | `list` / `list --panes` | `pane list` |
|
|
41
|
+
| `list --windows` | `list --tabs` | `tab list` |
|
|
42
|
+
| `list --sessions` | `list --workspaces` | `workspace list` |
|
|
43
|
+
| `send --cmd` | `send --cmd` | `pane run` (plain) / `agent prompt` (agent pane) |
|
|
44
|
+
| `send --msg` / `--key` | `send --msg` / `--key` | `pane send-text` / `pane send-keys`; `agent prompt` / `agent send-keys` |
|
|
45
|
+
| `capture` | `capture` | `pane read` |
|
|
46
|
+
| `wait --for output` | `wait --for output --pattern` | `pane wait-output` |
|
|
47
|
+
| `wait --for agent` | `wait --until` (agent state) | `agent wait` |
|
|
48
|
+
| `start <preset>` | `workspace <preset>` | `workspace create` + tabs/panes |
|
|
49
|
+
| `window <preset>` | `tab <preset>` | `tab create` + splits |
|
|
50
|
+
| `--list-presets` | `--list-presets` | -- (config cascade) |
|
|
51
|
+
| `attach` / `detach` | ❌ out of scope | human-facing; no automation equivalent |
|
|
52
|
+
| layout strings / `select-layout` | ❌ out of scope | herdr has split/resize only |
|
|
53
|
+
|
|
54
|
+
## `ace-herdr list`
|
|
55
|
+
|
|
56
|
+
Inspect live state. Panes are the default scope; `--workspace <id>` scopes panes and tabs.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ace-herdr list # panes across all workspaces
|
|
60
|
+
ace-herdr list --workspace w1 # panes in one workspace
|
|
61
|
+
ace-herdr list --tabs --workspace w1 # tabs in one workspace
|
|
62
|
+
ace-herdr list --workspaces # workspaces
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Output: `{"panes":[{"id":"w1:p1","tab":"w1:t1","workspace":"w1","title":"~","cwd":"/tmp","focused":true,"agent_status":"idle"},...]}` -- `{"tabs":[{id, workspace, title, number, pane_count, focused}...]}` for `--tabs`, `{"workspaces":[{id, title, number, tab_count, pane_count, focused}...]}` for `--workspaces`. Empty results are a success with an empty array, never an error.
|
|
66
|
+
|
|
67
|
+
## `ace-herdr send`
|
|
68
|
+
|
|
69
|
+
Send a command, raw text, or named keys. Input reaches the pane in **declaration order** (an intentional divergence from ace-tmux's msgs-then-keys; order is expressive here, so it is preserved, not normalized). At least one input token is required -- none is a usage error before any transport call.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ace-herdr send --pane p5 --cmd 'bundle exec rake test'
|
|
73
|
+
ace-herdr send --pane p5 --msg 'continue with option 2' --key Enter
|
|
74
|
+
ace-herdr send --pane p5 --key Esc --cmd 'reset' # rejected -- see rules
|
|
75
|
+
ace-herdr send --pane p5 --key enter
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Plain pane (raw input, declaration order)
|
|
79
|
+
|
|
80
|
+
- `--cmd TEXT` is exactly one `pane run` submission (text + Enter). It must be declared before every `--key`; trailing keys are sent after the submission (post-submission keystrokes such as `y`/`n` confirmations). Leading keys (`--key Esc --cmd run`) are rejected before any transport call.
|
|
81
|
+
- `--msg a --msg b` types raw text without submitting, concatenated in order.
|
|
82
|
+
- `--key K...` sends named keys in order; each `enter` submits pending text.
|
|
83
|
+
- `--cmd` combined with `--msg` is a usage error before any transport call (fail closed, nothing sent).
|
|
84
|
+
- Multi-Enter sequences submit per Enter.
|
|
85
|
+
- Output: `{"pane":"p5","sent":"cmd"}` for command-led sends, `"text"` for message-led sends, `"keys"` for key-only sends.
|
|
86
|
+
|
|
87
|
+
### Agent pane (prompt semantics)
|
|
88
|
+
|
|
89
|
+
When the target pane hosts a live agent (`herdr agent get`), the same flags route to agent transport -- replacing ace-tmux's INTERACTIVE_CLI_COMMANDS/busy-pattern heuristics with native agent state:
|
|
90
|
+
|
|
91
|
+
- `--cmd T`, or concatenated `--msg` texts (joined with newlines), becomes **one** agent prompt that submits itself -- message-only input submits once on an agent pane (intended divergence from plain panes).
|
|
92
|
+
- At most one trailing `--key Enter` is dropped and reported: `{"pane":"p5","sent":"prompt","dropped_keys":["Enter"]}` -- this guarantees exactly-one submission instead of erroring.
|
|
93
|
+
- `--key`-only sequences go to `agent send-keys` (`esc`, `ctrl+c`, ...); at most one `enter` total.
|
|
94
|
+
- Submission is gated natively: a blocked agent rejects pre-send with a CLI error carrying `agent_blocked` (never silently dropped); a stalled prompt surfaces `agent_prompt_stalled`.
|
|
95
|
+
- Rejected before transport: keys interleaved between or after messages other than the single trailing `enter`; multiple `enter` keys; `--cmd` with non-Enter keys; `--cmd` combined with `--msg`.
|
|
96
|
+
|
|
97
|
+
## `ace-herdr capture`
|
|
98
|
+
|
|
99
|
+
Print pane content as raw text (no JSON wrapping). Read-only -- agent routing rules do not apply.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
ace-herdr capture --pane p5 # last 40 lines of history
|
|
103
|
+
ace-herdr capture --pane p5 --lines 40 --source recent
|
|
104
|
+
ace-herdr capture --pane p5 --source visible # the agent screen
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## `ace-herdr wait`
|
|
108
|
+
|
|
109
|
+
Wait for an agent state or matching pane output.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
ace-herdr wait --pane p5 # agent: idle, done, or blocked
|
|
113
|
+
ace-herdr wait --pane p5 --until done --timeout 120 # agent: one state
|
|
114
|
+
ace-herdr wait --pane p5 --for output --pattern 'tests? OK' --timeout 30
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- Agent form (default; unchanged): `--until idle,working,blocked,done,unknown`, output `{"pane":"p5","state":"ready"}`.
|
|
118
|
+
- Output form: `--for output --pattern PATTERN` (literal substring, matching ace-tmux's observable contract). herdr checks existing pane content immediately, then polls; `--timeout` is seconds (gem converts to milliseconds). Output `{"pane":"p5","matched":true}`; a timeout is a CLI error (non-zero exit).
|
|
119
|
+
- The modes are mutually exclusive: `--pattern` requires `--for output`; `--until` requires the agent form.
|
|
120
|
+
|
|
121
|
+
## `ace-herdr workspace` / `ace-herdr tab` (presets)
|
|
122
|
+
|
|
123
|
+
Declare a workspace or tab layout in YAML and create it in one command -- `workspace <preset>` mirrors `ace-tmux start`, `tab <preset>` mirrors `ace-tmux window`.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
ace-herdr workspace development
|
|
127
|
+
ace-herdr workspace development --cwd /path/to/project
|
|
128
|
+
ace-herdr tab agent --workspace w1
|
|
129
|
+
ace-herdr --list-presets # merged inventory, both types
|
|
130
|
+
ace-herdr --list-presets workspaces
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Preset cascade (ADR-022)
|
|
134
|
+
|
|
135
|
+
Presets load nearest-wins and the `--list-presets` inventory reflects the merged set:
|
|
136
|
+
|
|
137
|
+
1. project `.ace/herdr/{workspaces,tabs}/*.yml` (highest)
|
|
138
|
+
2. user `~/.ace/herdr/{workspaces,tabs}/*.yml`
|
|
139
|
+
3. gem `.ace-defaults/herdr/{workspaces,tabs}/*.yml` -- ships `workspaces/development.yml` and `tabs/agent.yml`
|
|
140
|
+
|
|
141
|
+
A project file with the same name overrides a gem/user file; names unique to a level still appear in the merged listing.
|
|
142
|
+
|
|
143
|
+
### Schema
|
|
144
|
+
|
|
145
|
+
```yaml
|
|
146
|
+
# .ace/herdr/workspaces/development.yml
|
|
147
|
+
label: development # workspace label (defaults to the preset name)
|
|
148
|
+
cwd: /workspace/project # root cwd (tab/panes inherit; ~ expands)
|
|
149
|
+
focus: true # pass --focus to workspace create
|
|
150
|
+
tabs:
|
|
151
|
+
- preset: agent # a tab may inherit a tab preset (recursive; overlay wins)
|
|
152
|
+
label: work # ...and override any field
|
|
153
|
+
- label: editor
|
|
154
|
+
cwd: /workspace/project # tab cwd overrides the root cwd
|
|
155
|
+
focus: false
|
|
156
|
+
panes:
|
|
157
|
+
- label: shell # the FIRST pane is the tab's root pane
|
|
158
|
+
- label: agent
|
|
159
|
+
cwd: /workspace/project
|
|
160
|
+
agent: # declares an agent pane
|
|
161
|
+
kind: pi # default: config default_agent_kind
|
|
162
|
+
name: development-agent
|
|
163
|
+
prompt: Review the current task.
|
|
164
|
+
splits: # every pane after the first must be placed by a split
|
|
165
|
+
- direction: right # herdr-native right|down; horizontal|vertical accepted
|
|
166
|
+
target: shell # pane label to split from (default: the root pane)
|
|
167
|
+
pane: agent # the declared pane placed by this split
|
|
168
|
+
ratio: 0.5
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
# .ace/herdr/tabs/agent.yml -- a tab preset uses the tab-level fields directly
|
|
173
|
+
label: agent
|
|
174
|
+
panes:
|
|
175
|
+
- label: agent
|
|
176
|
+
agent:
|
|
177
|
+
kind: pi
|
|
178
|
+
name: task-agent
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Creation is deterministic and ordered: the workspace is created first, then tabs/panes in declared order (root pane from `tab create`, further panes from `pane split`, panes renamed to their labels), then pane `command`s run, then declared agents start readiness-gated (`agent start` blocks until the pane is interactive; vs0 bootstrap order: reverse address exported into the pane shell, agent start, optional prompt). herdr seeds every new workspace with an initial tab; once the preset's declared tabs exist that seeded tab is closed, so only declared tabs remain (a preset with no `tabs:` keeps the seeded one). All layouts are validated before any herdr call -- an unplaced pane, a duplicate split placement, an unknown split target, or an unknown direction fails closed and creates nothing.
|
|
182
|
+
|
|
183
|
+
Output: `{"workspace":"w2","tabs":[{"tab":"w2:t1","panes":["w2:p1","w2:p2"],"commands":1,"agents":["development-agent"]}]}`; the tab command reports the single tab object. `--cwd` overrides the resolved root/tab cwd (CLI > tab > root > pane inheritance).
|
|
184
|
+
|
|
185
|
+
Unknown preset: CLI error listing the available names (ace-tmux `--list-presets` parity), for example `Error: Unknown workspace preset 'nope' (available: development)`.
|
|
186
|
+
|
|
187
|
+
## `ace-herdr deliver`
|
|
188
|
+
|
|
189
|
+
Push an answer to an agent pane.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Answer from a file, explicit ref
|
|
193
|
+
ace-herdr deliver --session ws-1 --pane p5 --event-id evt-1 --answer-file answer.md
|
|
194
|
+
|
|
195
|
+
# Answer from stdin, ref from the environment (inside the asking pane)
|
|
196
|
+
echo 'the answer' | ace-herdr deliver
|
|
197
|
+
|
|
198
|
+
# Bootstrap a specific agent kind if the pane has none
|
|
199
|
+
ace-herdr deliver --session ws-1 --pane p5 --kind codex --label 8wm.t.vs0 --answer-file a.md
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Options: `--session`, `--pane` (default: `HERDR_SESSION` / `HERDR_PANE`), `--event-id` (default: derived from the ref and content digest), `--kind`, `--label`, `--answer-file` (default: stdin), `--resume <event-id>` (re-deliver the stored answer from the record; no ref or answer input needed).
|
|
203
|
+
|
|
204
|
+
Output: one JSON line `{"ref":{...},"state":"delivered|retryable|failed"}`. Exit code is non-zero unless the state is `delivered`.
|
|
205
|
+
|
|
206
|
+
### The delivery contract
|
|
207
|
+
|
|
208
|
+
`ace-hitl ask` captures the asker's reverse address fail-closed from the environment (`HERDR_SESSION` / `HERDR_PANE`, schema `ace.hitl.ref/v1`). `ace-herdr deliver` pushes an answer back to that address:
|
|
209
|
+
|
|
210
|
+
1. A write-ahead delivery record carrying the full answer is persisted under `.ace-local/herdr/deliveries/<event-id>.json` (mode 0600, atomic rename) before any herdr contact, so a crash can never lose the answer. A crashed run is recovered with `--resume <event-id>`.
|
|
211
|
+
2. Delivery is idempotent per event id and serialized by a per-event lock: concurrent deliveries prompt once. Re-delivering identical content after a `delivered` record short-circuits without contacting herdr; different content or a different destination for the same event id fails closed.
|
|
212
|
+
3. If the target pane has no agent, one is bootstrapped (`herdr agent start`), the reverse address is exported into the pane shell (`export HERDR_SESSION=... HERDR_PANE=...`; values are token-validated and shell-escaped), and delivery waits for the agent to become idle before prompting.
|
|
213
|
+
4. Transient failures (readiness timeout, `agent_prompt_stalled`, socket/binary unavailability -- at the probe as well as the prompt) persist their history and report `retryable` within `delivery.max_attempts`.
|
|
214
|
+
5. Terminal failures (`agent_blocked` pre-send rejection, missing pane, agent start failure) report `failed` immediately with the error persisted in the record history.
|
|
215
|
+
6. An interrupted run whose last recorded event is a prompt submission without an outcome is ambiguous: the answer may already have been delivered. Re-running reports `failed` ("previous run crashed after submitting") instead of silently resending.
|
|
216
|
+
|
|
217
|
+
Result states follow the ace-hitl contract (spec 8wm.t.vrz §1.2): `delivered`, `retryable` (safe to re-push identical content), `failed` (terminal).
|
|
218
|
+
|
|
219
|
+
## `ace-herdr dispatch`
|
|
220
|
+
|
|
221
|
+
Start an agent in one command: tab + `herdr agent start` + prompt, all with deterministic defaults.
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
ace-herdr dispatch --label 8wm.t.vs0 --kind pi --prompt-file prompt.md
|
|
225
|
+
ace-herdr dispatch --label review --pane p7 --cwd /path/to/project
|
|
226
|
+
ace-herdr dispatch --label 8wm.t.vs0 --no-prompt
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Defaults: the caller's herdr workspace (flag `--workspace` > `HERDR_WORKSPACE_ID` > `herdr pane current`), the label as agent and pane name, the prompt from `--prompt-file` or stdin (`--no-prompt` skips submission). The new agent's environment receives `HERDR_SESSION` (workspace id) and `HERDR_PANE` (pane id) so its own `ace-hitl ask` calls carry a working reverse address.
|
|
230
|
+
|
|
231
|
+
Output: one JSON line `{"workspace":...,"pane":...,"agent":...,"kind":...,"tab_created":...,"prompted":...}`.
|
|
232
|
+
|
|
233
|
+
## `ace-herdr close`
|
|
234
|
+
|
|
235
|
+
Close out a finished agent pane; optionally rename first.
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
ace-herdr close --pane p5 --rename done # rename, then close
|
|
239
|
+
ace-herdr close --pane p5 --keep --rename wip # rename only
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## `ace-herdr tidy`
|
|
243
|
+
|
|
244
|
+
Report cleanable agent panes and delivery records as one deterministic JSON line; **dry run by default** -- nothing is closed, moved, or removed without `--apply`.
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
ace-herdr tidy # dry run: report candidates, zero side effects
|
|
248
|
+
ace-herdr tidy --apply # close eligible panes, archive old delivered records
|
|
249
|
+
ace-herdr tidy --quiet # suppress the report
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Output: `{"apply":false,"retention_days":7,"panes":{"candidates":[...],"preserved":[...],"closed":[...],"excluded":[...]},"deliveries":{"candidates":[...],"protected":[...],"archived":[...],"preserved":[...]}}`. Entries are ordered by pane id / event id; every bucket is always present (an explicit empty array means "nothing to do", never an error).
|
|
253
|
+
|
|
254
|
+
### Pane closure: positive evidence only
|
|
255
|
+
|
|
256
|
+
A pane qualifies as a `candidate` solely on positive completion evidence:
|
|
257
|
+
|
|
258
|
+
- the agent in the pane was **observed** in state `done` (`agent get`), or
|
|
259
|
+
- the pane has **no foreground process** (`pane process-info` reports an empty process list).
|
|
260
|
+
|
|
261
|
+
`unknown` or unreadable evidence is preserve-only: `unknown` status, a missing field, a malformed response, or a probe failure puts the pane in `preserved` with the reason (`active` / `unknown` / `unreadable`) -- no proof is never treated as dead. Panes whose process could not be read are reported, not closed.
|
|
262
|
+
|
|
263
|
+
With `--apply`, every candidate is **re-probed immediately before mutation**: only a second positive reading closes it (rename to `done`, then close -- the `close` semantics). A candidate that revived in between (`idle`/`working`) or whose fresh probe turned uncertain or failed is moved to `excluded` with the reason and never mutated. A pane that vanished in the meantime is `excluded` as `gone` -- there is nothing left to close.
|
|
264
|
+
|
|
265
|
+
### Delivery record retention
|
|
266
|
+
|
|
267
|
+
Delivered records under `deliveries_dir` whose `updated_at` is **strictly older** than `tidy.delivered_retention_days` (default 7; an exact-threshold record stays) become `candidates`; with `--apply` they are atomically moved to `deliveries_dir/archive/<event-id>.json` (mode 0600 preserved) and reported in `archived` with the archive path. Age parsing is RFC 3339 only; a delivered record with a missing or invalid `updated_at` is `preserved` (`invalid_updated_at`), never guessed.
|
|
268
|
+
|
|
269
|
+
`pending`, `retryable`, and `failed` records are **never touched** (they are audit/recovery state) and are listed in `protected`. Malformed or unreadable record files are `preserved` as `unreadable`, never removed. Lock files, temp files, and the archive directory itself are ignored. Before each archival the record is re-loaded under its per-event lock: a record that changed after discovery (no longer delivered or no longer old enough) is left in place and reported as `preserved` (`changed`); one that turned unreadable is `preserved` (`unreadable`) without aborting the run. Archival never breaks delivery idempotency: `deliver`/`--resume` consult the archive copy, so re-delivering an archived event still short-circuits (identical content) or fails closed (conflicting content) instead of re-prompting.
|
|
270
|
+
|
|
271
|
+
Errors: if the herdr runtime is unreachable, `tidy` fails with an explicit CLI error before reporting anything (no partial report, no mutation). No LLM is consulted anywhere; the report costs zero tokens.
|
|
272
|
+
|
|
273
|
+
## Configuration
|
|
274
|
+
|
|
275
|
+
Defaults (`.ace-defaults/herdr/config.yml`), overridable in `~/.ace/herdr/config.yml` or `.ace/herdr/config.yml`:
|
|
276
|
+
|
|
277
|
+
```yaml
|
|
278
|
+
default_agent_kind: pi # herdr agent kind used for bootstrap/dispatch/preset agents
|
|
279
|
+
delivery:
|
|
280
|
+
max_attempts: 3 # prompt attempts per delivery sequence
|
|
281
|
+
backoff_seconds: [1, 2, 4] # fixed deterministic backoff, no jitter
|
|
282
|
+
timeouts:
|
|
283
|
+
agent_start: 60 # seconds to interactive readiness (also preset agents)
|
|
284
|
+
prompt: 30
|
|
285
|
+
wait: 30 # readiness gate / ace-herdr wait (both wait modes)
|
|
286
|
+
deliveries_dir: .ace-local/herdr/deliveries
|
|
287
|
+
tidy:
|
|
288
|
+
delivered_retention_days: 7 # archive delivered records strictly older than this (tidy)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Delivery records
|
|
292
|
+
|
|
293
|
+
Records are JSON, one file per event id under `deliveries_dir` (relative to the working directory, mode 0600): event id, reverse address, SHA-256 answer digest, the full answer (so a crash never loses content), state (`pending` / `delivered` / `retryable` / `failed`), attempt count, and an append-only history of bootstrap, readiness, and prompt events with errors. Records are written atomically and guarded by a per-event lock. Re-running `deliver` with the same event id and content resumes or short-circuits; with different content or a different destination it fails closed.
|
|
294
|
+
|
|
295
|
+
## Error semantics
|
|
296
|
+
|
|
297
|
+
Commands raise a CLI error (non-zero exit) carrying herdr's machine code where one exists:
|
|
298
|
+
|
|
299
|
+
- Unknown pane/tab/workspace: `pane_not_found: ...`, `tab_not_found: ...`, `workspace_not_found: ...`
|
|
300
|
+
- herdr binary or socket unavailable: explicit CLI error, no partial output
|
|
301
|
+
- Blocked agent: `agent_blocked: ...` (terminal -- never silently dropped); stalled prompt: `agent_prompt_stalled: ...` (transient)
|
|
302
|
+
- Output wait timeout: `timeout: ...`
|
|
303
|
+
- Unknown preset: usage error listing the available preset names
|
|
304
|
+
- Invalid send shapes: usage error **before any transport call** (nothing is sent)
|
|
305
|
+
|
|
306
|
+
Exit `0` means success (including an explicit empty `list` result or a satisfied wait).
|
data/exe/ace-herdr
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "ace/herdr"
|
|
6
|
+
|
|
7
|
+
# No args -> show help
|
|
8
|
+
args = ARGV.empty? ? ["--help"] : ARGV
|
|
9
|
+
|
|
10
|
+
# Start CLI with exception-based exit code handling (per ADR-023)
|
|
11
|
+
begin
|
|
12
|
+
exit_code = Ace::Herdr::CLI.start(args)
|
|
13
|
+
exit(exit_code) if exit_code.is_a?(Integer) && exit_code.nonzero?
|
|
14
|
+
rescue Ace::Support::Cli::Error => e
|
|
15
|
+
warn e.message
|
|
16
|
+
exit(e.exit_code)
|
|
17
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
5
|
+
module Ace
|
|
6
|
+
module Herdr
|
|
7
|
+
module Atoms
|
|
8
|
+
# SHA-256 hex digest of delivery content; pure function
|
|
9
|
+
module AnswerDigest
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def call(content)
|
|
13
|
+
Digest::SHA256.hexdigest(content.to_s)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ace/support/cli"
|
|
4
|
+
require "ace/core"
|
|
5
|
+
require_relative "support"
|
|
6
|
+
|
|
7
|
+
module Ace
|
|
8
|
+
module Herdr
|
|
9
|
+
module CLI
|
|
10
|
+
module Commands
|
|
11
|
+
class Capture < Ace::Support::Cli::Command
|
|
12
|
+
include Ace::Support::Cli::Base
|
|
13
|
+
include Runtime
|
|
14
|
+
|
|
15
|
+
desc <<~DESC.strip
|
|
16
|
+
Print recent pane output as raw text (no JSON wrapping)
|
|
17
|
+
|
|
18
|
+
--source visible reads the current screen; recent (default)
|
|
19
|
+
reads terminal history. Read-only: agent routing rules do not
|
|
20
|
+
apply.
|
|
21
|
+
DESC
|
|
22
|
+
|
|
23
|
+
example [
|
|
24
|
+
"--pane p5",
|
|
25
|
+
"--pane p5 --lines 40",
|
|
26
|
+
"--pane p5 --source visible"
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
option :pane, type: :string, desc: "Target pane id"
|
|
30
|
+
option :lines, type: :integer, desc: "Number of lines (default: 40)"
|
|
31
|
+
option :source, type: :string, desc: "Snapshot source: visible or recent (default: recent)"
|
|
32
|
+
|
|
33
|
+
def initialize(executor: nil)
|
|
34
|
+
@executor = executor
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def call(**options)
|
|
38
|
+
translate_errors do
|
|
39
|
+
cli_error("--pane is required") if options[:pane].to_s.empty?
|
|
40
|
+
source = options[:source] || "recent"
|
|
41
|
+
unless %w[visible recent].include?(source)
|
|
42
|
+
cli_error("--source must be visible or recent")
|
|
43
|
+
end
|
|
44
|
+
control = Organisms::ControlSurface.new(executor: executor)
|
|
45
|
+
print control.capture(
|
|
46
|
+
pane: options.fetch(:pane), source: source,
|
|
47
|
+
lines: options[:lines] || Organisms::ControlSurface::DEFAULT_LINES
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ace/support/cli"
|
|
4
|
+
require "ace/core"
|
|
5
|
+
require_relative "support"
|
|
6
|
+
|
|
7
|
+
module Ace
|
|
8
|
+
module Herdr
|
|
9
|
+
module CLI
|
|
10
|
+
module Commands
|
|
11
|
+
class Close < Ace::Support::Cli::Command
|
|
12
|
+
include Ace::Support::Cli::Base
|
|
13
|
+
include Runtime
|
|
14
|
+
|
|
15
|
+
desc <<~DESC.strip
|
|
16
|
+
Rename and/or close a finished agent pane, returning the result
|
|
17
|
+
DESC
|
|
18
|
+
|
|
19
|
+
example [
|
|
20
|
+
"--pane p5 --rename done",
|
|
21
|
+
"--pane p5",
|
|
22
|
+
"--pane p5 --keep --rename 'review done'"
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
option :pane, type: :string, desc: "Target pane id"
|
|
26
|
+
option :rename, type: :string, desc: "New pane label applied before closing"
|
|
27
|
+
option :keep, type: :boolean, desc: "Rename only; do not close the pane"
|
|
28
|
+
|
|
29
|
+
def initialize(executor: nil)
|
|
30
|
+
@executor = executor
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call(**options)
|
|
34
|
+
translate_errors do
|
|
35
|
+
cli_error("--pane is required") if options[:pane].to_s.empty?
|
|
36
|
+
renamed = false
|
|
37
|
+
closed = false
|
|
38
|
+
if options[:rename]
|
|
39
|
+
executor.pane_rename(options.fetch(:pane), options[:rename])
|
|
40
|
+
renamed = true
|
|
41
|
+
end
|
|
42
|
+
unless options[:keep]
|
|
43
|
+
executor.pane_close(options.fetch(:pane))
|
|
44
|
+
closed = true
|
|
45
|
+
end
|
|
46
|
+
puts JSON.generate(pane: options.fetch(:pane), renamed: renamed, closed: closed)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|