aque 0.0.1__tar.gz
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.
- aque-0.0.1/.gitignore +6 -0
- aque-0.0.1/LICENSE +21 -0
- aque-0.0.1/PKG-INFO +156 -0
- aque-0.0.1/README.md +124 -0
- aque-0.0.1/aque/__init__.py +1 -0
- aque-0.0.1/aque/cli.py +136 -0
- aque-0.0.1/aque/config.py +37 -0
- aque-0.0.1/aque/desk.py +804 -0
- aque-0.0.1/aque/history.py +50 -0
- aque-0.0.1/aque/monitor.py +144 -0
- aque-0.0.1/aque/run.py +62 -0
- aque-0.0.1/aque/state.py +158 -0
- aque-0.0.1/config/default.yaml +11 -0
- aque-0.0.1/demo/config.yaml +3 -0
- aque-0.0.1/demo/demo.cast +1808 -0
- aque-0.0.1/demo/fake_agent.py +75 -0
- aque-0.0.1/demo/record.sh +91 -0
- aque-0.0.1/docs/demo.gif +0 -0
- aque-0.0.1/docs/logo-light.svg +6 -0
- aque-0.0.1/docs/logo-wordmark.svg +7 -0
- aque-0.0.1/docs/logo.svg +6 -0
- aque-0.0.1/features/agent_lifecycle.feature +70 -0
- aque-0.0.1/features/auto_attach.feature +78 -0
- aque-0.0.1/features/dashboard.feature +129 -0
- aque-0.0.1/features/detach_workflow.feature +46 -0
- aque-0.0.1/features/idle_detection.feature +93 -0
- aque-0.0.1/features/new_agent.feature +66 -0
- aque-0.0.1/pyproject.toml +46 -0
- aque-0.0.1/tests/__init__.py +0 -0
- aque-0.0.1/tests/conftest.py +51 -0
- aque-0.0.1/tests/test_config.py +31 -0
- aque-0.0.1/tests/test_desk.py +45 -0
- aque-0.0.1/tests/test_history.py +38 -0
- aque-0.0.1/tests/test_integration.py +82 -0
- aque-0.0.1/tests/test_monitor.py +79 -0
- aque-0.0.1/tests/test_run.py +57 -0
- aque-0.0.1/tests/test_state.py +189 -0
aque-0.0.1/.gitignore
ADDED
aque-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aque Contributors
|
|
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.
|
aque-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aque
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A tmux-based agent queue manager. You sit at the desk, agents come to you.
|
|
5
|
+
Project-URL: Homepage, https://github.com/can-can/aque
|
|
6
|
+
Project-URL: Repository, https://github.com/can-can/aque
|
|
7
|
+
Project-URL: Issues, https://github.com/can-can/aque/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: agent,ai,cli,queue,textual,tmux
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Topic :: System :: Shells
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: libtmux>=0.37.0
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Requires-Dist: textual>=0.47.0
|
|
25
|
+
Requires-Dist: typer>=0.12.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src="docs/logo.svg" width="80" alt="aque logo">
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
<h1 align="center">Aque</h1>
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<a href="https://pypi.org/project/aque/"><img src="https://img.shields.io/pypi/v/aque.svg" alt="PyPI version"></a>
|
|
41
|
+
<a href="https://pypi.org/project/aque/"><img src="https://img.shields.io/pypi/pyversions/aque.svg" alt="Python"></a>
|
|
42
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
A tmux-based agent queue manager. You sit at one "desk" and your AI agents come to you.
|
|
46
|
+
|
|
47
|
+
## Why?
|
|
48
|
+
|
|
49
|
+
Running multiple AI coding agents (Claude Code, aider, Codex) at once? They all need your attention at different times. Aque queues them so you work through them one at a time — no forgotten terminal tabs, no context switching.
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
## Install
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pipx install aque
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Or with pip:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install aque
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Requires: Python 3.11+, [tmux](https://github.com/tmux/tmux)
|
|
66
|
+
|
|
67
|
+
### Development
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/can-can/aque.git
|
|
71
|
+
cd aque
|
|
72
|
+
pip install -e ".[dev]"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
Launch agents:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
aque run --dir ~/projects/api --label "auth fix" -- claude --model opus
|
|
81
|
+
aque run --dir ~/projects/web -- aider --model gpt-4
|
|
82
|
+
aque run --dir ~/code/tests -- codex
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Sit at your desk:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
aque desk
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The desk shows a **unified dashboard** with all your agents, their states, and a live preview of the selected agent's terminal output.
|
|
92
|
+
|
|
93
|
+
### Dashboard Keys
|
|
94
|
+
|
|
95
|
+
| Key | Action |
|
|
96
|
+
|-----|--------|
|
|
97
|
+
| ↑↓ | Navigate agent list |
|
|
98
|
+
| Enter | Attach to selected agent |
|
|
99
|
+
| n | Create new agent |
|
|
100
|
+
| k | Kill selected agent (moves to history) |
|
|
101
|
+
| h | Toggle hold on selected agent |
|
|
102
|
+
| q | Quit desk |
|
|
103
|
+
|
|
104
|
+
### Detach Behavior
|
|
105
|
+
|
|
106
|
+
When you detach from a tmux session (`Ctrl-b d`), aque handles the transition automatically:
|
|
107
|
+
|
|
108
|
+
- **Running/waiting agent** — auto-dismissed back to running, returns to dashboard
|
|
109
|
+
- **Exited agent** — auto-marked as done and moved to history
|
|
110
|
+
|
|
111
|
+
No action menu, no extra steps.
|
|
112
|
+
|
|
113
|
+
### Auto-Attach
|
|
114
|
+
|
|
115
|
+
When a waiting agent is detected (on the dashboard or after detaching), aque shows a **3-second countdown modal** and auto-attaches to the top-priority waiting agent. Press **Esc** to cancel and stay on the dashboard.
|
|
116
|
+
|
|
117
|
+
### Idle Detection
|
|
118
|
+
|
|
119
|
+
Aque monitors tmux panes for prompt markers (`❯`, `$`, `>>>`) to detect when an agent is waiting for input. After the configured idle timeout (default: 10s), the agent transitions from `running` to `waiting` and enters the queue.
|
|
120
|
+
|
|
121
|
+
### Agent States
|
|
122
|
+
|
|
123
|
+
| State | Meaning |
|
|
124
|
+
|-------|---------|
|
|
125
|
+
| running | Agent is actively working |
|
|
126
|
+
| waiting | Agent is idle, queued for your attention |
|
|
127
|
+
| focused | You are currently attached to this agent |
|
|
128
|
+
| on_hold | Paused, skipped in the queue |
|
|
129
|
+
| exited | Tmux session has ended |
|
|
130
|
+
| done | Completed, moved to history |
|
|
131
|
+
|
|
132
|
+
### Other Commands
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
aque list # show all agents and states
|
|
136
|
+
aque kill 3 # terminate an agent
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
Edit `~/.aque/config.yaml`:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
idle_timeout: 10
|
|
145
|
+
snapshot_interval: 2
|
|
146
|
+
session_prefix: aque
|
|
147
|
+
action_keys:
|
|
148
|
+
dismiss: d
|
|
149
|
+
done: k
|
|
150
|
+
skip: s
|
|
151
|
+
hold: h
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
[MIT](LICENSE)
|
aque-0.0.1/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/logo.svg" width="80" alt="aque logo">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Aque</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://pypi.org/project/aque/"><img src="https://img.shields.io/pypi/v/aque.svg" alt="PyPI version"></a>
|
|
9
|
+
<a href="https://pypi.org/project/aque/"><img src="https://img.shields.io/pypi/pyversions/aque.svg" alt="Python"></a>
|
|
10
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
A tmux-based agent queue manager. You sit at one "desk" and your AI agents come to you.
|
|
14
|
+
|
|
15
|
+
## Why?
|
|
16
|
+
|
|
17
|
+
Running multiple AI coding agents (Claude Code, aider, Codex) at once? They all need your attention at different times. Aque queues them so you work through them one at a time — no forgotten terminal tabs, no context switching.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pipx install aque
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or with pip:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install aque
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires: Python 3.11+, [tmux](https://github.com/tmux/tmux)
|
|
34
|
+
|
|
35
|
+
### Development
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/can-can/aque.git
|
|
39
|
+
cd aque
|
|
40
|
+
pip install -e ".[dev]"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
Launch agents:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
aque run --dir ~/projects/api --label "auth fix" -- claude --model opus
|
|
49
|
+
aque run --dir ~/projects/web -- aider --model gpt-4
|
|
50
|
+
aque run --dir ~/code/tests -- codex
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Sit at your desk:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
aque desk
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The desk shows a **unified dashboard** with all your agents, their states, and a live preview of the selected agent's terminal output.
|
|
60
|
+
|
|
61
|
+
### Dashboard Keys
|
|
62
|
+
|
|
63
|
+
| Key | Action |
|
|
64
|
+
|-----|--------|
|
|
65
|
+
| ↑↓ | Navigate agent list |
|
|
66
|
+
| Enter | Attach to selected agent |
|
|
67
|
+
| n | Create new agent |
|
|
68
|
+
| k | Kill selected agent (moves to history) |
|
|
69
|
+
| h | Toggle hold on selected agent |
|
|
70
|
+
| q | Quit desk |
|
|
71
|
+
|
|
72
|
+
### Detach Behavior
|
|
73
|
+
|
|
74
|
+
When you detach from a tmux session (`Ctrl-b d`), aque handles the transition automatically:
|
|
75
|
+
|
|
76
|
+
- **Running/waiting agent** — auto-dismissed back to running, returns to dashboard
|
|
77
|
+
- **Exited agent** — auto-marked as done and moved to history
|
|
78
|
+
|
|
79
|
+
No action menu, no extra steps.
|
|
80
|
+
|
|
81
|
+
### Auto-Attach
|
|
82
|
+
|
|
83
|
+
When a waiting agent is detected (on the dashboard or after detaching), aque shows a **3-second countdown modal** and auto-attaches to the top-priority waiting agent. Press **Esc** to cancel and stay on the dashboard.
|
|
84
|
+
|
|
85
|
+
### Idle Detection
|
|
86
|
+
|
|
87
|
+
Aque monitors tmux panes for prompt markers (`❯`, `$`, `>>>`) to detect when an agent is waiting for input. After the configured idle timeout (default: 10s), the agent transitions from `running` to `waiting` and enters the queue.
|
|
88
|
+
|
|
89
|
+
### Agent States
|
|
90
|
+
|
|
91
|
+
| State | Meaning |
|
|
92
|
+
|-------|---------|
|
|
93
|
+
| running | Agent is actively working |
|
|
94
|
+
| waiting | Agent is idle, queued for your attention |
|
|
95
|
+
| focused | You are currently attached to this agent |
|
|
96
|
+
| on_hold | Paused, skipped in the queue |
|
|
97
|
+
| exited | Tmux session has ended |
|
|
98
|
+
| done | Completed, moved to history |
|
|
99
|
+
|
|
100
|
+
### Other Commands
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
aque list # show all agents and states
|
|
104
|
+
aque kill 3 # terminate an agent
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Configuration
|
|
108
|
+
|
|
109
|
+
Edit `~/.aque/config.yaml`:
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
idle_timeout: 10
|
|
113
|
+
snapshot_interval: 2
|
|
114
|
+
session_prefix: aque
|
|
115
|
+
action_keys:
|
|
116
|
+
dismiss: d
|
|
117
|
+
done: k
|
|
118
|
+
skip: s
|
|
119
|
+
hold: h
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
aque-0.0.1/aque/cli.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
|
|
9
|
+
from aque.config import load_config
|
|
10
|
+
from aque.monitor import start_monitor_daemon, stop_monitor
|
|
11
|
+
from aque.run import launch_agent
|
|
12
|
+
from aque.state import AgentState, StateManager
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(help="Aque — a tmux-based agent queue manager.")
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
AQUE_DIR = Path.home() / ".aque"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@app.callback()
|
|
21
|
+
def main(
|
|
22
|
+
aque_dir: Optional[str] = typer.Option(None, "--aque-dir", help="Aque state directory (default: ~/.aque)"),
|
|
23
|
+
) -> None:
|
|
24
|
+
global AQUE_DIR
|
|
25
|
+
if aque_dir:
|
|
26
|
+
AQUE_DIR = Path(aque_dir)
|
|
27
|
+
AQUE_DIR.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_state_manager() -> StateManager:
|
|
31
|
+
return StateManager(AQUE_DIR)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def ensure_monitor_running() -> None:
|
|
35
|
+
mgr = get_state_manager()
|
|
36
|
+
state = mgr.load()
|
|
37
|
+
if state.monitor_pid:
|
|
38
|
+
try:
|
|
39
|
+
os.kill(state.monitor_pid, 0)
|
|
40
|
+
return
|
|
41
|
+
except ProcessLookupError:
|
|
42
|
+
state.monitor_pid = None
|
|
43
|
+
mgr.save(state)
|
|
44
|
+
start_monitor_daemon(AQUE_DIR)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.command()
|
|
48
|
+
def run(
|
|
49
|
+
dir: str = typer.Option(..., "--dir", help="Working directory for the agent"),
|
|
50
|
+
label: Optional[str] = typer.Option(None, "--label", help="Human-readable label"),
|
|
51
|
+
command: list[str] = typer.Argument(..., help="Agent command and arguments"),
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Launch an agent in a managed tmux session."""
|
|
54
|
+
config = load_config(AQUE_DIR)
|
|
55
|
+
mgr = get_state_manager()
|
|
56
|
+
agent_id = launch_agent(
|
|
57
|
+
command=command,
|
|
58
|
+
working_dir=dir,
|
|
59
|
+
label=label,
|
|
60
|
+
state_manager=mgr,
|
|
61
|
+
prefix=config["session_prefix"],
|
|
62
|
+
)
|
|
63
|
+
ensure_monitor_running()
|
|
64
|
+
console.print(f"[green]Agent #{agent_id} launched[/green]: {label or command[0]}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@app.command(name="list")
|
|
68
|
+
def list_agents() -> None:
|
|
69
|
+
"""Show all managed agents and their states."""
|
|
70
|
+
mgr = get_state_manager()
|
|
71
|
+
state = mgr.load()
|
|
72
|
+
|
|
73
|
+
if not state.agents:
|
|
74
|
+
console.print("[dim]No agents running.[/dim]")
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
table = Table()
|
|
78
|
+
table.add_column("ID", style="bold")
|
|
79
|
+
table.add_column("STATE")
|
|
80
|
+
table.add_column("LABEL")
|
|
81
|
+
table.add_column("DIR")
|
|
82
|
+
|
|
83
|
+
state_colors = {
|
|
84
|
+
AgentState.RUNNING: "green",
|
|
85
|
+
AgentState.WAITING: "yellow",
|
|
86
|
+
AgentState.FOCUSED: "blue",
|
|
87
|
+
AgentState.EXITED: "dim",
|
|
88
|
+
AgentState.ON_HOLD: "magenta",
|
|
89
|
+
AgentState.DONE: "red",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for agent in state.agents:
|
|
93
|
+
color = state_colors.get(agent.state, "white")
|
|
94
|
+
table.add_row(
|
|
95
|
+
str(agent.id),
|
|
96
|
+
f"[{color}]{agent.state.value}[/{color}]",
|
|
97
|
+
agent.label,
|
|
98
|
+
agent.dir,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
console.print(table)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@app.command()
|
|
105
|
+
def kill(agent_id: int = typer.Argument(..., help="Agent ID to terminate")) -> None:
|
|
106
|
+
"""Terminate an agent and clean up its tmux session."""
|
|
107
|
+
import libtmux
|
|
108
|
+
from aque.history import HistoryManager
|
|
109
|
+
|
|
110
|
+
mgr = get_state_manager()
|
|
111
|
+
hmgr = HistoryManager(AQUE_DIR)
|
|
112
|
+
state = mgr.load()
|
|
113
|
+
agent = next((a for a in state.agents if a.id == agent_id), None)
|
|
114
|
+
|
|
115
|
+
if agent is None:
|
|
116
|
+
console.print(f"[red]Agent #{agent_id} not found.[/red]")
|
|
117
|
+
raise typer.Exit(1)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
server = libtmux.Server()
|
|
121
|
+
session = server.sessions.get(session_name=agent.tmux_session)
|
|
122
|
+
if session:
|
|
123
|
+
session.kill()
|
|
124
|
+
except Exception:
|
|
125
|
+
pass
|
|
126
|
+
|
|
127
|
+
mgr.done_agent(agent_id, hmgr)
|
|
128
|
+
console.print(f"[red]Agent #{agent_id} done — moved to history.[/red]")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@app.command()
|
|
132
|
+
def desk() -> None:
|
|
133
|
+
"""Open the desk TUI. Agents come to you."""
|
|
134
|
+
from aque.desk import DeskApp
|
|
135
|
+
desk_app = DeskApp(aque_dir=AQUE_DIR)
|
|
136
|
+
desk_app.run()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
DEFAULT_CONFIG = {
|
|
8
|
+
"idle_timeout": 10,
|
|
9
|
+
"snapshot_interval": 2,
|
|
10
|
+
"action_keys": {
|
|
11
|
+
"dismiss": "d",
|
|
12
|
+
"done": "k",
|
|
13
|
+
"skip": "s",
|
|
14
|
+
"hold": "h",
|
|
15
|
+
},
|
|
16
|
+
"queue_order": "fifo",
|
|
17
|
+
"session_prefix": "aque",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _deep_merge(base: dict, override: dict) -> dict:
|
|
22
|
+
result = copy.deepcopy(base)
|
|
23
|
+
for key, value in override.items():
|
|
24
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
25
|
+
result[key] = _deep_merge(result[key], value)
|
|
26
|
+
else:
|
|
27
|
+
result[key] = value
|
|
28
|
+
return result
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_config(aque_dir: Path) -> dict:
|
|
32
|
+
config_path = Path(aque_dir) / "config.yaml"
|
|
33
|
+
if not config_path.exists():
|
|
34
|
+
return copy.deepcopy(DEFAULT_CONFIG)
|
|
35
|
+
with open(config_path) as f:
|
|
36
|
+
user_config = yaml.safe_load(f) or {}
|
|
37
|
+
return _deep_merge(DEFAULT_CONFIG, user_config)
|