ace-overseer 0.15.5 → 0.15.6
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +21 -1
- data/docs/usage.md +41 -3
- data/lib/ace/overseer/cli/commands/agents.rb +23 -0
- data/lib/ace/overseer/cli/commands/prepare.rb +38 -0
- data/lib/ace/overseer/cli/commands/projects.rb +23 -0
- data/lib/ace/overseer/cli/commands/prompt.rb +35 -0
- data/lib/ace/overseer/cli/commands/prune.rb +32 -1
- data/lib/ace/overseer/cli/commands/review.rb +24 -0
- data/lib/ace/overseer/cli/commands/status.rb +18 -2
- data/lib/ace/overseer/cli/commands/stop.rb +23 -0
- data/lib/ace/overseer/cli/commands/work_on.rb +19 -3
- data/lib/ace/overseer/cli.rb +19 -1
- data/lib/ace/overseer/molecules/lab_client.rb +35 -0
- data/lib/ace/overseer/version.rb +1 -1
- data/lib/ace/overseer.rb +1 -0
- metadata +17 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f64d7b81728d6636ebc933e6c30f119bc6aba514a8652562f393e5ab7f3a5540
|
|
4
|
+
data.tar.gz: abdc381e8d76669ba78bb871280704efd979a62df58aeecc703d912cd99a5275
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d079fea537a3941ecb55459d52793bf1a6a5d55e411e05dabc4154561536de0cdda61c4b4a8fb6c4fc10170f19fd255e0b66a1da4d0823d332e62aac8511d9d2
|
|
7
|
+
data.tar.gz: a718d278ba5223bc08589f6627b6fb72f92d433703b7ef69c83a4cc8f06182afc79241754a383f743f5c98b690ce77bbe250c3cd04d97b9a8e5c931e86b38146
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
- Added an explicit Lab runtime adapter for listing projects and agents, preparing and dispatching Works, forwarding prompts over stdin, starting exact-head reviews, and stopping workers.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Kept tmux as the default runtime while allowing `work-on`, `status`, and dry-run `prune` to delegate through the system-owned `/usr/local/bin/lab` boundary.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [0.15.6] - 2026-09-02
|
|
18
|
+
|
|
19
|
+
### Technical
|
|
20
|
+
- Included in the coordinated all-package patch release preparation for ACE monorepo review.
|
|
10
21
|
## [0.15.5] - 2026-08-12
|
|
11
22
|
|
|
12
23
|
### Technical
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1> ACE - Overseer </h1>
|
|
3
3
|
|
|
4
|
-
One command
|
|
4
|
+
One command surface for local tmux work and isolated Lab workers.
|
|
5
5
|
|
|
6
6
|
<img src="../docs/brand/AgenticCodingEnvironment.Logo.XS.jpg" alt="ACE Logo" width="480">
|
|
7
7
|
<br><br>
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
|
|
21
21
|
Starting task work means creating a worktree, opening a tmux window, and preparing an assignment - three manual steps before you even begin coding. ace-overseer collapses that into a single command, tracks what is running where, and cleans up finished worktrees so nothing lingers. You can jump straight to a focused worktree any time with a single invocation.
|
|
22
22
|
|
|
23
|
+
On a configured Lab machine, the same CLI can prepare and dispatch the Lab's
|
|
24
|
+
system-owned `Work` objects. The default remains the existing tmux runtime;
|
|
25
|
+
using Lab always requires an explicit `--runtime lab` or a Lab-only command.
|
|
26
|
+
|
|
23
27
|
## How It Works
|
|
24
28
|
|
|
25
29
|
1. Resolve task refs and create a scoped worktree.
|
|
@@ -42,6 +46,22 @@ ace-overseer work-on --task 8q4 --preset work-on-task
|
|
|
42
46
|
ace-overseer status --watch
|
|
43
47
|
```
|
|
44
48
|
|
|
49
|
+
**Operate Lab workers** - prepare a reviewed Work, dispatch it to a configured
|
|
50
|
+
agent, and inspect it without teaching ACE how to manage containers or Herdr:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
ace-overseer prepare --runtime lab --project nervus \
|
|
54
|
+
--source lab-plan:oauth-callback --work W142 \
|
|
55
|
+
--planner admin-agy --title "OAuth callback validation"
|
|
56
|
+
ace-overseer work-on --runtime lab --work W142 --agent builder-codex
|
|
57
|
+
ace-overseer status --runtime lab --project nervus
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Lab owns worktrees, credentials, Podman, and Herdr. Overseer decides what to
|
|
61
|
+
run and delegates only through the absolute `/usr/local/bin/lab` boundary.
|
|
62
|
+
Use `ace-overseer prune WORK... --runtime lab --dry-run` to preview exact Work
|
|
63
|
+
destruction, then repeat with `--yes` to delegate it to Lab.
|
|
64
|
+
|
|
45
65
|
**Clean up finished work** - [`ace-overseer prune`](docs/usage.md#ace-overseer-prune) removes completed worktrees safely. It checks three conditions before removing: assignment completed, task marked done, and git working tree clean. Use `--dry-run` to preview what would be pruned, `--force` for worktrees that fail safety checks, or `--assignment` to prune a single stale assignment.
|
|
46
66
|
|
|
47
67
|
**Customize the workflow** - the full pipeline is defined in two layers: the [assignment preset](../ace-assign/.ace-defaults/assign/presets/work-on-task.yml) controls which steps run and in what order (onboard, implement, test, release, review, PR), while each step references a [workflow instruction](../ace-task/handbook/workflow-instructions/task/work.wf.md) that defines how it executes. Browse [available presets](../ace-assign/.ace-defaults/assign/presets/) or create your own to tailor the pipeline to your project.
|
data/docs/usage.md
CHANGED
|
@@ -3,8 +3,8 @@ doc-type: user
|
|
|
3
3
|
title: ace-overseer Usage
|
|
4
4
|
purpose: Full CLI reference for ace-overseer commands and options.
|
|
5
5
|
ace-docs:
|
|
6
|
-
last-updated: 2026-
|
|
7
|
-
last-checked: 2026-
|
|
6
|
+
last-updated: 2026-08-30
|
|
7
|
+
last-checked: 2026-08-30
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Usage
|
|
@@ -14,6 +14,12 @@ ace-docs:
|
|
|
14
14
|
- `ace-overseer work-on`
|
|
15
15
|
- `ace-overseer status`
|
|
16
16
|
- `ace-overseer prune`
|
|
17
|
+
- `ace-overseer projects`
|
|
18
|
+
- `ace-overseer agents`
|
|
19
|
+
- `ace-overseer prepare`
|
|
20
|
+
- `ace-overseer prompt`
|
|
21
|
+
- `ace-overseer review`
|
|
22
|
+
- `ace-overseer stop`
|
|
17
23
|
|
|
18
24
|
## `ace-overseer work-on`
|
|
19
25
|
|
|
@@ -23,8 +29,11 @@ Invocation: `ace-overseer work-on --task <task-ref>`.
|
|
|
23
29
|
|
|
24
30
|
Options:
|
|
25
31
|
|
|
26
|
-
- `--task`, `-t` (required): task reference(s); repeatable and comma-separated values supported
|
|
32
|
+
- `--task`, `-t` (required for tmux): task reference(s); repeatable and comma-separated values supported
|
|
27
33
|
- `--preset`, `-p`: assignment preset name
|
|
34
|
+
- `--runtime`: `tmux` (default) or `lab`
|
|
35
|
+
- `--work`: existing Lab Work ID; required with `--runtime lab`
|
|
36
|
+
- `--agent`: configured Lab agent ID; required with `--runtime lab`
|
|
28
37
|
- `--quiet`, `-q`: suppress non-essential output
|
|
29
38
|
- `--debug`, `-d`: show debug output
|
|
30
39
|
- `--help`, `-h`: show help
|
|
@@ -41,6 +50,8 @@ Options:
|
|
|
41
50
|
|
|
42
51
|
- `--format`: output format (`table`, `json`)
|
|
43
52
|
- `--watch`, `-w`: auto-refresh dashboard
|
|
53
|
+
- `--runtime`: `tmux` (default) or `lab`
|
|
54
|
+
- `--project`: filter Lab Works by project
|
|
44
55
|
- `--quiet`, `-q`: suppress non-essential output
|
|
45
56
|
- `--debug`, `-d`: show debug output
|
|
46
57
|
- `--help`, `-h`: show help
|
|
@@ -61,6 +72,7 @@ Options:
|
|
|
61
72
|
- `--force`, `-f`: force-remove unsafe worktrees
|
|
62
73
|
- `--yes`, `-y`: skip interactive confirmation
|
|
63
74
|
- `--dry-run`: list prune candidates only
|
|
75
|
+
- `--runtime`: `tmux` (default) or `lab`; Lab accepts dry-run only and delegates destruction to the exact `lab work destroy WORK --confirm` command
|
|
64
76
|
- `--quiet`, `-q`: suppress non-essential output
|
|
65
77
|
- `--debug`, `-d`: show debug output
|
|
66
78
|
- `--help`, `-h`: show help
|
|
@@ -73,6 +85,32 @@ Check dashboard: `ace-overseer status`.
|
|
|
73
85
|
|
|
74
86
|
Preview then prune: `ace-overseer prune --dry-run`, then `ace-overseer prune --yes`.
|
|
75
87
|
|
|
88
|
+
## Lab Runtime
|
|
89
|
+
|
|
90
|
+
Lab commands are available only where `/usr/local/bin/lab` is installed. ACE
|
|
91
|
+
does not read Lab credentials and does not call Podman or Herdr directly.
|
|
92
|
+
|
|
93
|
+
- `ace-overseer projects`: list registered Lab projects.
|
|
94
|
+
- `ace-overseer agents`: list registered Lab agents and concurrency limits.
|
|
95
|
+
- `ace-overseer prepare --runtime lab --project PROJECT --source KIND:ID --work WORK --planner AGENT --title TITLE`: create a reviewed Work and its isolated worktree.
|
|
96
|
+
- `ace-overseer work-on --runtime lab --work WORK --agent AGENT`: reserve the agent, create or reuse its Herdr workspace, and dispatch it.
|
|
97
|
+
- `ace-overseer status --runtime lab [--project PROJECT] [--format table|json]`: show Lab Work state. Continuous status lives in each project Herdr session, so `--watch` is intentionally rejected for Lab.
|
|
98
|
+
- `ace-overseer prompt --work WORK --file PATH`: forward prompt text from a file to the Work pane. Piped stdin is also supported; prompt text is never passed as a process argument.
|
|
99
|
+
- `ace-overseer review --work WORK --pr NUMBER`: prepare an exact-head admin review checkout and pane.
|
|
100
|
+
- `ace-overseer stop --work WORK`: stop the assigned process without destroying Work state.
|
|
101
|
+
- `ace-overseer prune WORK... --runtime lab --dry-run`: preview exact Work destruction commands; rerun with `--yes` to delegate each destruction to Lab.
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
ace-overseer prepare --runtime lab --project nervus \
|
|
107
|
+
--source nervus-thread:67611c0b-f44c-4ac4-ae4e-55773b175617 \
|
|
108
|
+
--work W321 --planner admin-agy --title "Reviewed task title"
|
|
109
|
+
ace-overseer work-on --runtime lab --work W321 --agent builder-codex
|
|
110
|
+
ace-overseer prompt --work W321 --file .ace-local/prompts/W321.md
|
|
111
|
+
ace-overseer status --runtime lab --project nervus --format json
|
|
112
|
+
```
|
|
113
|
+
|
|
76
114
|
## Public Verification Paths
|
|
77
115
|
|
|
78
116
|
Use these user-visible checks when validating behavior end-to-end:
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "json"
|
|
3
|
+
module Ace
|
|
4
|
+
module Overseer
|
|
5
|
+
module CLI
|
|
6
|
+
module Commands
|
|
7
|
+
class Agents < Ace::Support::Cli::Command
|
|
8
|
+
include Ace::Support::Cli::Base
|
|
9
|
+
desc "List configured Lab agents"
|
|
10
|
+
def initialize(client: nil)
|
|
11
|
+
super()
|
|
12
|
+
@client = client || Molecules::LabClient.new
|
|
13
|
+
end
|
|
14
|
+
def call(**)
|
|
15
|
+
puts JSON.pretty_generate(@client.call("agents", "--json"))
|
|
16
|
+
rescue => error
|
|
17
|
+
raise Ace::Support::Cli::Error.new(error.message)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Ace
|
|
3
|
+
module Overseer
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Prepare < Ace::Support::Cli::Command
|
|
7
|
+
include Ace::Support::Cli::Base
|
|
8
|
+
desc "Prepare a Work through the Lab runtime"
|
|
9
|
+
option :runtime, required: true, desc: "Runtime (lab)"
|
|
10
|
+
option :project, required: true, desc: "Lab project slug"
|
|
11
|
+
option :source, required: true, desc: "Source KIND:ID"
|
|
12
|
+
option :work, required: true, desc: "Work ID"
|
|
13
|
+
option :planner, required: true, desc: "Planner agent ID"
|
|
14
|
+
option :title, required: true, desc: "Reviewed Work title"
|
|
15
|
+
def initialize(client: nil)
|
|
16
|
+
super()
|
|
17
|
+
@client = client || Molecules::LabClient.new
|
|
18
|
+
end
|
|
19
|
+
def call(runtime:, project:, source:, work:, planner:, title:, **)
|
|
20
|
+
raise Ace::Support::Cli::Error, "unsupported runtime: #{runtime}" unless runtime == "lab"
|
|
21
|
+
puts @client.call(
|
|
22
|
+
"work", "prepare", project,
|
|
23
|
+
"--source", source,
|
|
24
|
+
"--work", work,
|
|
25
|
+
"--planner", planner,
|
|
26
|
+
"--title", title,
|
|
27
|
+
json: false
|
|
28
|
+
)
|
|
29
|
+
rescue Ace::Support::Cli::Error
|
|
30
|
+
raise
|
|
31
|
+
rescue => error
|
|
32
|
+
raise Ace::Support::Cli::Error.new(error.message)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "json"
|
|
3
|
+
module Ace
|
|
4
|
+
module Overseer
|
|
5
|
+
module CLI
|
|
6
|
+
module Commands
|
|
7
|
+
class Projects < Ace::Support::Cli::Command
|
|
8
|
+
include Ace::Support::Cli::Base
|
|
9
|
+
desc "List configured Lab projects"
|
|
10
|
+
def initialize(client: nil)
|
|
11
|
+
super()
|
|
12
|
+
@client = client || Molecules::LabClient.new
|
|
13
|
+
end
|
|
14
|
+
def call(**)
|
|
15
|
+
puts JSON.pretty_generate(@client.call("project", "list", "--json"))
|
|
16
|
+
rescue => error
|
|
17
|
+
raise Ace::Support::Cli::Error.new(error.message)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Ace
|
|
3
|
+
module Overseer
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Prompt < Ace::Support::Cli::Command
|
|
7
|
+
include Ace::Support::Cli::Base
|
|
8
|
+
desc "Prompt a running Lab Work from stdin"
|
|
9
|
+
option :work, required: true, desc: "Work ID"
|
|
10
|
+
option :file, desc: "Read prompt text from a file instead of stdin"
|
|
11
|
+
def initialize(client: nil, input: $stdin)
|
|
12
|
+
super()
|
|
13
|
+
@client = client || Molecules::LabClient.new
|
|
14
|
+
@input = input
|
|
15
|
+
end
|
|
16
|
+
def call(work:, file: nil, **)
|
|
17
|
+
if file
|
|
18
|
+
prompt = File.read(file)
|
|
19
|
+
else
|
|
20
|
+
if @input.respond_to?(:tty?) && @input.tty?
|
|
21
|
+
raise Ace::Support::Cli::Error, "prompt text is required via --file or stdin"
|
|
22
|
+
end
|
|
23
|
+
prompt = @input.read
|
|
24
|
+
end
|
|
25
|
+
raise Ace::Support::Cli::Error, "prompt text must contain non-whitespace characters" if prompt.strip.empty?
|
|
26
|
+
|
|
27
|
+
puts @client.call("work", "prompt", work, stdin_data: prompt, json: false)
|
|
28
|
+
rescue => error
|
|
29
|
+
raise Ace::Support::Cli::Error.new(error.message)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -17,15 +17,24 @@ module Ace
|
|
|
17
17
|
option :dry_run, type: :boolean, default: false, desc: "Show candidates only"
|
|
18
18
|
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
19
19
|
option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
|
|
20
|
+
option :runtime, default: "tmux", desc: "Runtime (tmux, lab)"
|
|
20
21
|
|
|
21
|
-
def initialize(orchestrator: nil, input: $stdin, output: $stdout)
|
|
22
|
+
def initialize(orchestrator: nil, input: $stdin, output: $stdout, lab_client: nil)
|
|
22
23
|
super()
|
|
23
24
|
@orchestrator = orchestrator || Organisms::PruneOrchestrator.new
|
|
24
25
|
@input = input
|
|
25
26
|
@output = output
|
|
27
|
+
@lab_client = lab_client || Molecules::LabClient.new
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def call(**options)
|
|
31
|
+
runtime = options.fetch(:runtime, "tmux")
|
|
32
|
+
if runtime == "lab"
|
|
33
|
+
prune_lab(**options)
|
|
34
|
+
return
|
|
35
|
+
end
|
|
36
|
+
raise Ace::Support::Cli::Error, "unsupported runtime: #{runtime}" unless runtime == "tmux"
|
|
37
|
+
|
|
29
38
|
Atoms::RepoGuard.ensure_repo!
|
|
30
39
|
|
|
31
40
|
targets = Array(options[:targets] || [])
|
|
@@ -65,6 +74,28 @@ module Ace
|
|
|
65
74
|
|
|
66
75
|
private
|
|
67
76
|
|
|
77
|
+
def prune_lab(**options)
|
|
78
|
+
raise Ace::Support::Cli::Error, "--assignment is not supported with Lab runtime; provide exact Work IDs" if options[:assignment]
|
|
79
|
+
raise Ace::Support::Cli::Error, "--force is not supported with Lab runtime" if options[:force]
|
|
80
|
+
|
|
81
|
+
works = Array(options[:targets]).map(&:to_s)
|
|
82
|
+
raise Ace::Support::Cli::Error, "provide at least one exact Lab Work ID to prune" if works.empty?
|
|
83
|
+
|
|
84
|
+
if options[:dry_run]
|
|
85
|
+
return if options[:quiet]
|
|
86
|
+
|
|
87
|
+
works.each { |work| puts "Would destroy Lab Work #{work}: /usr/local/bin/lab work destroy #{work} --confirm" }
|
|
88
|
+
return
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
raise Ace::Support::Cli::Error, "Lab prune requires --yes after reviewing --dry-run" unless options[:yes]
|
|
92
|
+
|
|
93
|
+
works.each do |work|
|
|
94
|
+
result = @lab_client.call("work", "destroy", work, "--confirm", json: false)
|
|
95
|
+
puts result unless options[:quiet]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
68
99
|
def print_assignment_result(result)
|
|
69
100
|
candidate = result[:assignment_candidate]
|
|
70
101
|
if result[:dry_run]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Ace
|
|
3
|
+
module Overseer
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Review < Ace::Support::Cli::Command
|
|
7
|
+
include Ace::Support::Cli::Base
|
|
8
|
+
desc "Start an exact-head Lab review"
|
|
9
|
+
option :work, required: true, desc: "Work ID"
|
|
10
|
+
option :pr, required: true, type: :integer, desc: "Forgejo PR number"
|
|
11
|
+
def initialize(client: nil)
|
|
12
|
+
super()
|
|
13
|
+
@client = client || Molecules::LabClient.new
|
|
14
|
+
end
|
|
15
|
+
def call(work:, pr:, **)
|
|
16
|
+
puts @client.call("work", "review", work, "--pr", pr, json: false)
|
|
17
|
+
rescue => error
|
|
18
|
+
raise Ace::Support::Cli::Error.new(error.message)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -15,14 +15,30 @@ module Ace
|
|
|
15
15
|
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
16
16
|
option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
|
|
17
17
|
option :watch, aliases: ["-w"], type: :boolean, default: false, desc: "Auto-refresh dashboard"
|
|
18
|
+
option :runtime, default: "tmux", desc: "Runtime (tmux, lab)"
|
|
19
|
+
option :project, desc: "Filter Lab status by project"
|
|
18
20
|
|
|
19
|
-
def initialize(collector: nil, config: nil)
|
|
21
|
+
def initialize(collector: nil, config: nil, lab_client: nil)
|
|
20
22
|
super()
|
|
21
23
|
@collector = collector || Organisms::StatusCollector.new
|
|
22
24
|
@config = config
|
|
25
|
+
@lab_client = lab_client || Molecules::LabClient.new
|
|
23
26
|
end
|
|
24
27
|
|
|
25
|
-
def call(format:, **options)
|
|
28
|
+
def call(format:, runtime: "tmux", project: nil, **options)
|
|
29
|
+
if runtime == "lab"
|
|
30
|
+
if options[:watch]
|
|
31
|
+
raise Ace::Support::Cli::Error, "Lab watch runs in the project Herdr status pane; omit --watch"
|
|
32
|
+
end
|
|
33
|
+
arguments = ["work", "status"]
|
|
34
|
+
arguments.concat(["--project", project]) unless project.to_s.empty?
|
|
35
|
+
arguments << "--json" if format == "json"
|
|
36
|
+
output = @lab_client.call(*arguments, json: false)
|
|
37
|
+
puts output unless options[:quiet]
|
|
38
|
+
return
|
|
39
|
+
end
|
|
40
|
+
raise Ace::Support::Cli::Error, "unsupported runtime: #{runtime}" unless runtime == "tmux"
|
|
41
|
+
|
|
26
42
|
Atoms::RepoGuard.ensure_repo!
|
|
27
43
|
return if options[:quiet]
|
|
28
44
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Ace
|
|
3
|
+
module Overseer
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
class Stop < Ace::Support::Cli::Command
|
|
7
|
+
include Ace::Support::Cli::Base
|
|
8
|
+
desc "Stop a Lab Work"
|
|
9
|
+
option :work, required: true, desc: "Work ID"
|
|
10
|
+
def initialize(client: nil)
|
|
11
|
+
super()
|
|
12
|
+
@client = client || Molecules::LabClient.new
|
|
13
|
+
end
|
|
14
|
+
def call(work:, **)
|
|
15
|
+
puts @client.call("work", "stop", work, json: false)
|
|
16
|
+
rescue => error
|
|
17
|
+
raise Ace::Support::Cli::Error.new(error.message)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -9,18 +9,34 @@ module Ace
|
|
|
9
9
|
|
|
10
10
|
desc "Create worktree, open tmux window, and prepare assignment"
|
|
11
11
|
|
|
12
|
-
option :task, aliases: ["-t"], type: :array,
|
|
12
|
+
option :task, aliases: ["-t"], type: :array,
|
|
13
13
|
desc: "Task reference(s), repeatable and comma-separated (e.g., 230 --task 231,232)"
|
|
14
14
|
option :preset, aliases: ["-p"], desc: "Assignment preset name"
|
|
15
|
+
option :runtime, default: "tmux", desc: "Runtime (tmux, lab)"
|
|
16
|
+
option :work, desc: "Existing Lab Work ID"
|
|
17
|
+
option :agent, desc: "Configured Lab agent ID"
|
|
15
18
|
option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
|
|
16
19
|
option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
|
|
17
20
|
|
|
18
|
-
def initialize(orchestrator: nil)
|
|
21
|
+
def initialize(orchestrator: nil, lab_client: nil)
|
|
19
22
|
super()
|
|
20
23
|
@orchestrator = orchestrator || Organisms::WorkOnOrchestrator.new
|
|
24
|
+
@lab_client = lab_client || Molecules::LabClient.new
|
|
21
25
|
end
|
|
22
26
|
|
|
23
|
-
def call(task: nil, preset: nil, **options)
|
|
27
|
+
def call(task: nil, preset: nil, runtime: "tmux", work: nil, agent: nil, **options)
|
|
28
|
+
if runtime == "lab"
|
|
29
|
+
if task || preset
|
|
30
|
+
raise Ace::Support::Cli::Error, "--task and --preset are not supported with Lab runtime"
|
|
31
|
+
end
|
|
32
|
+
raise Ace::Support::Cli::Error, "--work and --agent are required for Lab runtime" if work.to_s.empty? || agent.to_s.empty?
|
|
33
|
+
|
|
34
|
+
output = @lab_client.call("work", "dispatch", work, "--agent", agent, json: false)
|
|
35
|
+
puts output unless options[:quiet]
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
raise Ace::Support::Cli::Error, "unsupported runtime: #{runtime}" unless runtime == "tmux"
|
|
39
|
+
|
|
24
40
|
Atoms::RepoGuard.ensure_repo!
|
|
25
41
|
|
|
26
42
|
task_refs = normalize_task_refs(task)
|
data/lib/ace/overseer/cli.rb
CHANGED
|
@@ -5,6 +5,12 @@ require "ace/core"
|
|
|
5
5
|
require_relative "cli/commands/work_on"
|
|
6
6
|
require_relative "cli/commands/status"
|
|
7
7
|
require_relative "cli/commands/prune"
|
|
8
|
+
require_relative "cli/commands/projects"
|
|
9
|
+
require_relative "cli/commands/agents"
|
|
10
|
+
require_relative "cli/commands/prepare"
|
|
11
|
+
require_relative "cli/commands/prompt"
|
|
12
|
+
require_relative "cli/commands/review"
|
|
13
|
+
require_relative "cli/commands/stop"
|
|
8
14
|
|
|
9
15
|
module Ace
|
|
10
16
|
module Overseer
|
|
@@ -16,7 +22,13 @@ module Ace
|
|
|
16
22
|
REGISTERED_COMMANDS = [
|
|
17
23
|
["work-on", "Work on a task in isolated worktree"],
|
|
18
24
|
["status", "Show status of task worktrees"],
|
|
19
|
-
["prune", "Remove stale task worktrees"]
|
|
25
|
+
["prune", "Remove stale task worktrees"],
|
|
26
|
+
["projects", "List configured Lab projects"],
|
|
27
|
+
["agents", "List configured Lab agents"],
|
|
28
|
+
["prepare", "Prepare a Lab Work"],
|
|
29
|
+
["prompt", "Prompt a Lab Work from stdin"],
|
|
30
|
+
["review", "Start a Lab Work review"],
|
|
31
|
+
["stop", "Stop a Lab Work"]
|
|
20
32
|
].freeze
|
|
21
33
|
|
|
22
34
|
HELP_EXAMPLES = [
|
|
@@ -28,6 +40,12 @@ module Ace
|
|
|
28
40
|
register "work-on", Commands::WorkOn
|
|
29
41
|
register "status", Commands::Status
|
|
30
42
|
register "prune", Commands::Prune
|
|
43
|
+
register "projects", Commands::Projects
|
|
44
|
+
register "agents", Commands::Agents
|
|
45
|
+
register "prepare", Commands::Prepare
|
|
46
|
+
register "prompt", Commands::Prompt
|
|
47
|
+
register "review", Commands::Review
|
|
48
|
+
register "stop", Commands::Stop
|
|
31
49
|
|
|
32
50
|
version_cmd = Ace::Support::Cli::VersionCommand.build(
|
|
33
51
|
gem_name: "ace-overseer",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
|
|
6
|
+
module Ace
|
|
7
|
+
module Overseer
|
|
8
|
+
module Molecules
|
|
9
|
+
class LabClient
|
|
10
|
+
BINARY = "/usr/local/bin/lab"
|
|
11
|
+
|
|
12
|
+
def initialize(runner: Open3)
|
|
13
|
+
@runner = runner
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(*arguments, stdin_data: nil, json: true)
|
|
17
|
+
stdout, stderr, status = @runner.capture3(
|
|
18
|
+
BINARY,
|
|
19
|
+
*arguments.map(&:to_s),
|
|
20
|
+
stdin_data: stdin_data.to_s
|
|
21
|
+
)
|
|
22
|
+
raise Error, stderr.to_s.strip.empty? ? "Lab command failed" : stderr.to_s.strip unless status.success?
|
|
23
|
+
|
|
24
|
+
return stdout unless json
|
|
25
|
+
|
|
26
|
+
JSON.parse(stdout)
|
|
27
|
+
rescue Errno::ENOENT
|
|
28
|
+
raise Error, "Lab runtime unavailable: #{BINARY} is not installed"
|
|
29
|
+
rescue JSON::ParserError => error
|
|
30
|
+
raise Error, "Lab returned invalid JSON: #{error.message}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/ace/overseer/version.rb
CHANGED
data/lib/ace/overseer.rb
CHANGED
|
@@ -16,6 +16,7 @@ require_relative "overseer/atoms/repo_guard"
|
|
|
16
16
|
require_relative "overseer/atoms/preset_resolver"
|
|
17
17
|
require_relative "overseer/atoms/status_formatter"
|
|
18
18
|
require_relative "overseer/molecules/worktree_provisioner"
|
|
19
|
+
require_relative "overseer/molecules/lab_client"
|
|
19
20
|
require_relative "overseer/molecules/tmux_window_opener"
|
|
20
21
|
require_relative "overseer/molecules/assignment_launcher"
|
|
21
22
|
require_relative "overseer/molecules/worktree_context_collector"
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-overseer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.15.
|
|
4
|
+
version: 0.15.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-09-05 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-cli
|
|
@@ -43,14 +43,14 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '0.
|
|
46
|
+
version: '0.18'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0.
|
|
53
|
+
version: '0.18'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: ace-support-fs
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -71,42 +71,42 @@ dependencies:
|
|
|
71
71
|
requirements:
|
|
72
72
|
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '0.
|
|
74
|
+
version: '0.56'
|
|
75
75
|
type: :runtime
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '0.
|
|
81
|
+
version: '0.56'
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
83
|
name: ace-git
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
86
|
- - "~>"
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: '0.
|
|
88
|
+
version: '0.23'
|
|
89
89
|
type: :runtime
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '0.
|
|
95
|
+
version: '0.23'
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
97
|
name: ace-git-worktree
|
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - "~>"
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '0.
|
|
102
|
+
version: '0.22'
|
|
103
103
|
type: :runtime
|
|
104
104
|
prerelease: false
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - "~>"
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '0.
|
|
109
|
+
version: '0.22'
|
|
110
110
|
- !ruby/object:Gem::Dependency
|
|
111
111
|
name: ace-task
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -221,14 +221,21 @@ files:
|
|
|
221
221
|
- lib/ace/overseer/atoms/repo_guard.rb
|
|
222
222
|
- lib/ace/overseer/atoms/status_formatter.rb
|
|
223
223
|
- lib/ace/overseer/cli.rb
|
|
224
|
+
- lib/ace/overseer/cli/commands/agents.rb
|
|
225
|
+
- lib/ace/overseer/cli/commands/prepare.rb
|
|
226
|
+
- lib/ace/overseer/cli/commands/projects.rb
|
|
227
|
+
- lib/ace/overseer/cli/commands/prompt.rb
|
|
224
228
|
- lib/ace/overseer/cli/commands/prune.rb
|
|
229
|
+
- lib/ace/overseer/cli/commands/review.rb
|
|
225
230
|
- lib/ace/overseer/cli/commands/status.rb
|
|
231
|
+
- lib/ace/overseer/cli/commands/stop.rb
|
|
226
232
|
- lib/ace/overseer/cli/commands/work_on.rb
|
|
227
233
|
- lib/ace/overseer/models/assignment_prune_candidate.rb
|
|
228
234
|
- lib/ace/overseer/models/prune_candidate.rb
|
|
229
235
|
- lib/ace/overseer/models/work_context.rb
|
|
230
236
|
- lib/ace/overseer/molecules/assignment_launcher.rb
|
|
231
237
|
- lib/ace/overseer/molecules/assignment_prune_safety_checker.rb
|
|
238
|
+
- lib/ace/overseer/molecules/lab_client.rb
|
|
232
239
|
- lib/ace/overseer/molecules/prune_safety_checker.rb
|
|
233
240
|
- lib/ace/overseer/molecules/tmux_window_opener.rb
|
|
234
241
|
- lib/ace/overseer/molecules/worktree_context_collector.rb
|