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 ADDED
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ .superpowers/
5
+ docs/superpowers/
6
+ dist/
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
+ ![Demo](docs/demo.gif)
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
+ ![Demo](docs/demo.gif)
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)