taskmux 0.2.7__tar.gz → 0.3.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.
- taskmux-0.3.1/PKG-INFO +581 -0
- taskmux-0.3.1/README.md +546 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/pyproject.toml +1 -1
- taskmux-0.3.1/taskmux/_log_pipe.py +76 -0
- taskmux-0.3.1/taskmux/cli.py +505 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/config.py +9 -1
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/daemon.py +4 -54
- taskmux-0.3.1/taskmux/events.py +85 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/hooks.py +12 -8
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/models.py +31 -1
- taskmux-0.3.1/taskmux/output.py +32 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/tmux_manager.py +420 -98
- taskmux-0.2.7/PKG-INFO +0 -385
- taskmux-0.2.7/README.md +0 -350
- taskmux-0.2.7/taskmux/cli.py +0 -224
- {taskmux-0.2.7 → taskmux-0.3.1}/.gitignore +0 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/LICENSE +0 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/__init__.py +0 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/agent.py +0 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/init.py +0 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/main.py +0 -0
- {taskmux-0.2.7 → taskmux-0.3.1}/taskmux/templates/claude.md +0 -0
taskmux-0.3.1/PKG-INFO
ADDED
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: taskmux
|
|
3
|
+
Version: 0.3.1
|
|
4
|
+
Summary: Modern tmux-based task manager for LLM development tools
|
|
5
|
+
Project-URL: Homepage, https://github.com/nc9/taskmux
|
|
6
|
+
Project-URL: Repository, https://github.com/nc9/taskmux
|
|
7
|
+
Project-URL: Issues, https://github.com/nc9/taskmux/issues
|
|
8
|
+
Author-email: Nik Cubrilovic <git@nikcub.me>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: daemon,development,manager,monitoring,session,terminal,tmux
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
22
|
+
Classifier: Topic :: System :: Monitoring
|
|
23
|
+
Classifier: Topic :: Terminals
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: aiofiles>=23.0.0
|
|
26
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
27
|
+
Requires-Dist: libtmux>=0.37.0
|
|
28
|
+
Requires-Dist: pydantic>=2.0
|
|
29
|
+
Requires-Dist: rich>=13.0.0
|
|
30
|
+
Requires-Dist: tomlkit>=0.13
|
|
31
|
+
Requires-Dist: typer>=0.12.0
|
|
32
|
+
Requires-Dist: watchdog>=3.0.0
|
|
33
|
+
Requires-Dist: websockets>=12.0
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# Taskmux
|
|
37
|
+
|
|
38
|
+
A modern tmux session manager for LLM development tools with persistent logs, JSON output, health monitoring, restart policies, event history, and WebSocket API.
|
|
39
|
+
|
|
40
|
+
## Why Taskmux?
|
|
41
|
+
|
|
42
|
+
LLM coding tools like Claude Code and Cursor struggle with background tasks. Taskmux provides an LLM-friendly CLI for managing multiple background processes — restarting, checking status, reading logs — all from within your AI coding environment. Every command supports `--json` for programmatic consumption, logs persist to disk with timestamps, and all lifecycle events are recorded for debugging.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
### Prerequisites
|
|
47
|
+
|
|
48
|
+
- [tmux](https://github.com/tmux/tmux)
|
|
49
|
+
- [uv](https://docs.astral.sh/uv/) (Python 3.11+)
|
|
50
|
+
|
|
51
|
+
### Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Recommended (global install)
|
|
55
|
+
uv tool install taskmux
|
|
56
|
+
|
|
57
|
+
# From source
|
|
58
|
+
git clone https://github.com/nc9/taskmux
|
|
59
|
+
cd taskmux
|
|
60
|
+
uv tool install .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Initialize in your project (creates taskmux.toml, injects agent context)
|
|
67
|
+
taskmux init
|
|
68
|
+
|
|
69
|
+
# Add tasks
|
|
70
|
+
taskmux add server "npm run dev"
|
|
71
|
+
taskmux add build "npm run build:watch"
|
|
72
|
+
taskmux add db "docker compose up postgres"
|
|
73
|
+
|
|
74
|
+
# Start all auto_start tasks
|
|
75
|
+
taskmux start
|
|
76
|
+
|
|
77
|
+
# Check status
|
|
78
|
+
taskmux status
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Or create a `taskmux.toml` manually:
|
|
82
|
+
|
|
83
|
+
```toml
|
|
84
|
+
name = "myproject"
|
|
85
|
+
|
|
86
|
+
[hooks]
|
|
87
|
+
before_start = "echo starting stack"
|
|
88
|
+
after_stop = "echo stack stopped"
|
|
89
|
+
|
|
90
|
+
[tasks.server]
|
|
91
|
+
command = "npm run dev"
|
|
92
|
+
|
|
93
|
+
[tasks.server.hooks]
|
|
94
|
+
before_start = "npm run build"
|
|
95
|
+
|
|
96
|
+
[tasks.build]
|
|
97
|
+
command = "npm run build:watch"
|
|
98
|
+
|
|
99
|
+
[tasks.test]
|
|
100
|
+
command = "npm run test:watch"
|
|
101
|
+
|
|
102
|
+
[tasks.db]
|
|
103
|
+
command = "docker compose up postgres"
|
|
104
|
+
auto_start = false
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Full Example
|
|
108
|
+
|
|
109
|
+
A full-stack app with a database, API server, and frontend — using health checks to ensure each service is ready before starting its dependents:
|
|
110
|
+
|
|
111
|
+
```toml
|
|
112
|
+
name = "fullstack-app"
|
|
113
|
+
|
|
114
|
+
[tasks.db]
|
|
115
|
+
command = "docker compose up postgres redis"
|
|
116
|
+
health_check = "pg_isready -h localhost -p 5432"
|
|
117
|
+
health_interval = 3
|
|
118
|
+
|
|
119
|
+
[tasks.migrate]
|
|
120
|
+
command = "python manage.py migrate && echo 'done' && sleep infinity"
|
|
121
|
+
cwd = "apps/api"
|
|
122
|
+
depends_on = ["db"]
|
|
123
|
+
health_check = "test -f .migrate-complete"
|
|
124
|
+
|
|
125
|
+
[tasks.api]
|
|
126
|
+
command = "python manage.py runserver 0.0.0.0:8000"
|
|
127
|
+
cwd = "apps/api"
|
|
128
|
+
port = 8000
|
|
129
|
+
depends_on = ["migrate"]
|
|
130
|
+
health_check = "curl -sf http://localhost:8000/health"
|
|
131
|
+
stop_grace_period = 10
|
|
132
|
+
|
|
133
|
+
[tasks.worker]
|
|
134
|
+
command = "celery -A myapp worker -l info"
|
|
135
|
+
cwd = "apps/api"
|
|
136
|
+
depends_on = ["db"]
|
|
137
|
+
restart_policy = "always"
|
|
138
|
+
max_restarts = 10
|
|
139
|
+
restart_backoff = 3.0
|
|
140
|
+
|
|
141
|
+
[tasks.web]
|
|
142
|
+
command = "bun dev"
|
|
143
|
+
cwd = "apps/web"
|
|
144
|
+
port = 3000
|
|
145
|
+
depends_on = ["api"]
|
|
146
|
+
health_check = "curl -sf http://localhost:3000"
|
|
147
|
+
|
|
148
|
+
[tasks.storybook]
|
|
149
|
+
command = "bun storybook"
|
|
150
|
+
cwd = "apps/web"
|
|
151
|
+
auto_start = false
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
What happens on `taskmux start`:
|
|
155
|
+
|
|
156
|
+
1. **db** starts first (no dependencies)
|
|
157
|
+
2. **migrate** and **worker** wait for db's health check (`pg_isready`) to pass
|
|
158
|
+
3. **api** waits for migrate's health check
|
|
159
|
+
4. **web** waits for api's health check (`curl localhost:8000/health`)
|
|
160
|
+
5. **storybook** is skipped (`auto_start = false`) — start it manually with `taskmux start storybook`
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
taskmux start # Starts everything in dependency order
|
|
164
|
+
taskmux logs # Interleaved logs from all tasks (persistent, timestamped)
|
|
165
|
+
taskmux logs -g "ERROR" # Grep all tasks for errors
|
|
166
|
+
taskmux logs api --since 5m # API logs from last 5 minutes
|
|
167
|
+
taskmux logs -f api # Follow API logs live
|
|
168
|
+
taskmux status --json # Machine-readable status for agents
|
|
169
|
+
taskmux health --json # Health check as JSON
|
|
170
|
+
taskmux events --task worker # Lifecycle events for the worker
|
|
171
|
+
taskmux inspect api # JSON state for a single task
|
|
172
|
+
taskmux restart worker # Restart just the worker
|
|
173
|
+
taskmux start storybook # Start a manual task
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Commands
|
|
177
|
+
|
|
178
|
+
All commands support `--json` for machine-readable output (see [JSON Output](#json-output)).
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Session lifecycle
|
|
182
|
+
taskmux start # Start all auto_start tasks in dependency order
|
|
183
|
+
taskmux start <task> [task2...] # Start specific tasks
|
|
184
|
+
taskmux start -m # Start + stay in foreground monitoring health/restarting
|
|
185
|
+
taskmux stop # Stop all (C-c → SIGTERM → SIGKILL), prevents auto-restart
|
|
186
|
+
taskmux stop <task> [task2...] # Stop specific tasks
|
|
187
|
+
taskmux restart # Restart all tasks
|
|
188
|
+
taskmux restart <task> [task2...] # Restart specific tasks, re-enables auto-restart
|
|
189
|
+
|
|
190
|
+
# Task management
|
|
191
|
+
taskmux kill <task> # Hard-kill (SIGKILL + destroy window), prevents auto-restart
|
|
192
|
+
taskmux add <task> "<command>" # Add task to taskmux.toml
|
|
193
|
+
taskmux remove <task> # Remove task (kills if running)
|
|
194
|
+
taskmux inspect <task> # JSON state: pid, health, restart_policy, pane info
|
|
195
|
+
|
|
196
|
+
# Status & health
|
|
197
|
+
taskmux status # Session + task overview (aliases: list, ls)
|
|
198
|
+
taskmux health # Health check table for all tasks
|
|
199
|
+
|
|
200
|
+
# Logs (persistent, timestamped — stored at ~/.taskmux/logs/)
|
|
201
|
+
taskmux logs # Interleaved logs from all tasks
|
|
202
|
+
taskmux logs <task> # Recent logs for a task
|
|
203
|
+
taskmux logs -f [task] # Follow logs live (colored prefixes)
|
|
204
|
+
taskmux logs -n 200 <task> # Last N lines
|
|
205
|
+
taskmux logs -g "error" # Grep all tasks
|
|
206
|
+
taskmux logs <task> -g "err" -C 5 # Grep one task with context
|
|
207
|
+
taskmux logs --since 5m # Logs from last 5 minutes
|
|
208
|
+
taskmux logs --since "2024-01-01T14:00" # Logs since timestamp
|
|
209
|
+
taskmux logs-clean # Delete all log files for this session
|
|
210
|
+
taskmux logs-clean <task> # Delete logs for a specific task
|
|
211
|
+
|
|
212
|
+
# Event history (stored at ~/.taskmux/events.jsonl)
|
|
213
|
+
taskmux events # Recent lifecycle events (last 50)
|
|
214
|
+
taskmux events --task server # Filter by task
|
|
215
|
+
taskmux events --since 1h # Events from last hour
|
|
216
|
+
taskmux events -n 100 # Show more events
|
|
217
|
+
|
|
218
|
+
# Setup & monitoring
|
|
219
|
+
taskmux init # Interactive project setup + agent context injection
|
|
220
|
+
taskmux init --defaults # Non-interactive setup
|
|
221
|
+
taskmux watch # Watch taskmux.toml, reload on change
|
|
222
|
+
taskmux daemon --port 8765 # Daemon mode: WebSocket API + health monitoring
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### stop vs kill vs restart
|
|
226
|
+
|
|
227
|
+
| Command | Signal | Window | Auto-restart |
|
|
228
|
+
|---------|--------|--------|--------------|
|
|
229
|
+
| `stop` | C-c → SIGTERM → SIGKILL (graceful) | Stays alive | Blocked (manually stopped) |
|
|
230
|
+
| `kill` | SIGKILL (immediate) | Destroyed | Blocked (manually stopped) |
|
|
231
|
+
| `restart` | Full stop + restart | Reused | Re-enabled |
|
|
232
|
+
|
|
233
|
+
Both `stop` and `kill` mark the task as **manually stopped**, preventing auto-restart even with `restart_policy = "always"`. Use `restart` or `start` to clear this flag and re-enable auto-restart.
|
|
234
|
+
|
|
235
|
+
## Configuration
|
|
236
|
+
|
|
237
|
+
### Format
|
|
238
|
+
|
|
239
|
+
Config file is `taskmux.toml` in the current directory:
|
|
240
|
+
|
|
241
|
+
```toml
|
|
242
|
+
name = "session-name"
|
|
243
|
+
auto_start = true # global toggle, default true
|
|
244
|
+
|
|
245
|
+
[hooks]
|
|
246
|
+
before_start = "echo starting"
|
|
247
|
+
after_stop = "echo done"
|
|
248
|
+
|
|
249
|
+
[tasks.server]
|
|
250
|
+
command = "python manage.py runserver"
|
|
251
|
+
cwd = "apps/api"
|
|
252
|
+
port = 8000
|
|
253
|
+
health_check = "curl -sf http://localhost:8000/health"
|
|
254
|
+
stop_grace_period = 10
|
|
255
|
+
depends_on = ["db"]
|
|
256
|
+
|
|
257
|
+
[tasks.server.hooks]
|
|
258
|
+
before_start = "python manage.py migrate"
|
|
259
|
+
|
|
260
|
+
[tasks.db]
|
|
261
|
+
command = "docker compose up postgres"
|
|
262
|
+
health_check = "pg_isready -h localhost"
|
|
263
|
+
|
|
264
|
+
[tasks.worker]
|
|
265
|
+
command = "celery worker -A myapp"
|
|
266
|
+
depends_on = ["db"]
|
|
267
|
+
restart_policy = "always"
|
|
268
|
+
max_restarts = 10
|
|
269
|
+
|
|
270
|
+
[tasks.tailwind]
|
|
271
|
+
command = "npx tailwindcss -w"
|
|
272
|
+
auto_start = false
|
|
273
|
+
restart_policy = "no"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Fields
|
|
277
|
+
|
|
278
|
+
| Field | Default | Description |
|
|
279
|
+
|-------|---------|-------------|
|
|
280
|
+
| `name` | `"taskmux"` | tmux session name |
|
|
281
|
+
| `auto_start` | `true` | Global toggle — if false, `start` creates session but launches nothing |
|
|
282
|
+
| `hooks.before_start` | — | Run before starting tasks |
|
|
283
|
+
| `hooks.after_start` | — | Run after starting tasks |
|
|
284
|
+
| `hooks.before_stop` | — | Run before stopping tasks |
|
|
285
|
+
| `hooks.after_stop` | — | Run after stopping tasks |
|
|
286
|
+
| `tasks.<name>.command` | — | Shell command to run |
|
|
287
|
+
| `tasks.<name>.auto_start` | `true` | Start with `taskmux start` |
|
|
288
|
+
| `tasks.<name>.cwd` | — | Working directory for the task |
|
|
289
|
+
| `tasks.<name>.port` | — | Port to clean up before starting (kills orphaned listeners) |
|
|
290
|
+
| `tasks.<name>.health_check` | — | Shell command to check health (exit 0 = healthy) |
|
|
291
|
+
| `tasks.<name>.health_interval` | `10` | Seconds between health checks |
|
|
292
|
+
| `tasks.<name>.health_timeout` | `5` | Seconds before health check times out |
|
|
293
|
+
| `tasks.<name>.health_retries` | `3` | Consecutive health failures before triggering a restart |
|
|
294
|
+
| `tasks.<name>.stop_grace_period` | `5` | Seconds to wait after C-c before escalating to SIGTERM |
|
|
295
|
+
| `tasks.<name>.restart_policy` | `"on-failure"` | When to auto-restart: `"no"`, `"on-failure"`, or `"always"` (see below) |
|
|
296
|
+
| `tasks.<name>.max_restarts` | `5` | Max auto-restarts before giving up (resets after 60s healthy) |
|
|
297
|
+
| `tasks.<name>.restart_backoff` | `2.0` | Exponential backoff base for restart delay (1s, 2s, 4s… capped at 60s) |
|
|
298
|
+
| `tasks.<name>.log_file` | — | Override log file path (default: `~/.taskmux/logs/{session}/{task}.log`) |
|
|
299
|
+
| `tasks.<name>.log_max_size` | `"10MB"` | Max log file size before rotation (e.g. `"500KB"`, `"1GB"`) |
|
|
300
|
+
| `tasks.<name>.log_max_files` | `3` | Number of rotated log files to keep |
|
|
301
|
+
| `tasks.<name>.depends_on` | `[]` | Task names that must be healthy before this task starts |
|
|
302
|
+
| `tasks.<name>.hooks.*` | — | Per-task lifecycle hooks (same fields as global) |
|
|
303
|
+
|
|
304
|
+
### Dependency Ordering
|
|
305
|
+
|
|
306
|
+
Tasks with `depends_on` are started in topological order. Before starting a task, taskmux waits for each dependency's health check to pass (up to `health_retries * health_interval` seconds). If a dependency never becomes healthy, the dependent task is skipped with a warning.
|
|
307
|
+
|
|
308
|
+
Circular dependencies and references to nonexistent tasks are rejected at config load time.
|
|
309
|
+
|
|
310
|
+
When starting a single task with `taskmux start <task>`, dependencies are not auto-started — you get a warning if they aren't running.
|
|
311
|
+
|
|
312
|
+
### Restart Policies
|
|
313
|
+
|
|
314
|
+
Each task has a `restart_policy` that controls automatic restart behavior. Restart policies are enforced by `taskmux start --monitor` and `taskmux daemon`.
|
|
315
|
+
|
|
316
|
+
| Policy | Behavior |
|
|
317
|
+
|--------|----------|
|
|
318
|
+
| `"no"` | Never auto-restart. Task stays stopped after crash or health failure. |
|
|
319
|
+
| `"on-failure"` | **(default)** Restart on crash (process exits) or after `health_retries` consecutive health check failures. |
|
|
320
|
+
| `"always"` | Restart whenever the task stops, including clean exits. |
|
|
321
|
+
|
|
322
|
+
**Manual stops override all policies.** Running `taskmux stop` or `taskmux kill` marks the task as manually stopped — it will not auto-restart even with `restart_policy = "always"`. Use `taskmux restart` or `taskmux start` to clear this flag.
|
|
323
|
+
|
|
324
|
+
**`restart_policy` vs `auto_start`** — these are orthogonal. `auto_start` controls whether a task launches on `taskmux start`. `restart_policy` controls what happens after a running task exits or fails. A task with `auto_start = false` and `restart_policy = "always"` won't start automatically, but once started manually, it will auto-restart on exit.
|
|
325
|
+
|
|
326
|
+
| `restart_policy` | `auto_start` | Behavior |
|
|
327
|
+
|---|---|---|
|
|
328
|
+
| `"no"` | `true` | Starts with session, never auto-restarts |
|
|
329
|
+
| `"no"` | `false` | Manual start only, never auto-restarts |
|
|
330
|
+
| `"on-failure"` | `true` | Starts with session, restarts on crash/health failure |
|
|
331
|
+
| `"on-failure"` | `false` | Manual start, restarts on crash/health failure once running |
|
|
332
|
+
| `"always"` | `true` | Starts with session, restarts on any exit |
|
|
333
|
+
| `"always"` | `false` | Manual start, restarts on any exit once running |
|
|
334
|
+
|
|
335
|
+
**Backoff & limits:** When a task keeps failing, restart delays increase exponentially: `restart_backoff ^ attempt` seconds (capped at 60s). After `max_restarts` consecutive restarts, the task is left stopped. The restart counter resets after 60 seconds of healthy uptime.
|
|
336
|
+
|
|
337
|
+
### Health Checks
|
|
338
|
+
|
|
339
|
+
If `health_check` is set, taskmux runs it as a shell command. Exit code 0 means healthy. If not set, taskmux falls back to checking if the tmux pane has a running process (not just a shell prompt).
|
|
340
|
+
|
|
341
|
+
A task must fail `health_retries` consecutive health checks (default 3) before being considered unhealthy and triggering a restart. If the task becomes healthy again, the failure counter resets.
|
|
342
|
+
|
|
343
|
+
Health checks are used by:
|
|
344
|
+
- `taskmux health` — shows a table of all task health
|
|
345
|
+
- `taskmux start` — waits for dependencies to be healthy before starting dependents
|
|
346
|
+
- `taskmux start --monitor` — continuously monitors and auto-restarts per restart_policy
|
|
347
|
+
- `taskmux daemon` — same as --monitor, plus WebSocket API and config watching
|
|
348
|
+
|
|
349
|
+
### Hook Cascade
|
|
350
|
+
|
|
351
|
+
Hooks fire in this order:
|
|
352
|
+
1. **Start**: global `before_start` → task `before_start` → _run command_ → task `after_start` → global `after_start`
|
|
353
|
+
2. **Stop**: global `before_stop` → task `before_stop` → _send C-c_ → task `after_stop` → global `after_stop`
|
|
354
|
+
|
|
355
|
+
If a `before_*` hook fails (non-zero exit), the action is aborted.
|
|
356
|
+
|
|
357
|
+
### Process Lifecycle
|
|
358
|
+
|
|
359
|
+
Taskmux ensures processes are fully stopped before restarting and that orphaned port listeners don't block new starts.
|
|
360
|
+
|
|
361
|
+
**Stop escalation** (`stop`, `restart`):
|
|
362
|
+
|
|
363
|
+
1. **C-c** (SIGINT) — waits `stop_grace_period` seconds (default 5)
|
|
364
|
+
2. **SIGTERM** to process group — waits 3 seconds
|
|
365
|
+
3. **SIGKILL** to process group — force kill
|
|
366
|
+
|
|
367
|
+
**Port cleanup** (`start`, `restart`): If `port` is configured, taskmux kills any process listening on that port before starting. This handles orphaned processes from crashed sessions.
|
|
368
|
+
|
|
369
|
+
**Auto-restart** (`start --monitor`, `daemon`): Tasks with `restart_policy = "on-failure"` or `"always"` are automatically restarted. Health checks must fail `health_retries` times before triggering a restart. Restart delays increase exponentially (`restart_backoff` base, capped at 60s). After `max_restarts` failures, the task is left stopped. The counter resets after 60 seconds of healthy uptime.
|
|
370
|
+
|
|
371
|
+
### Init & Agent Context
|
|
372
|
+
|
|
373
|
+
`taskmux init` bootstraps your project:
|
|
374
|
+
1. Creates `taskmux.toml` with session name (defaults to directory name)
|
|
375
|
+
2. Detects installed AI coding agents (Claude, Codex, OpenCode)
|
|
376
|
+
3. Injects taskmux usage instructions into agent context files:
|
|
377
|
+
- Claude: `.claude/rules/taskmux.md`
|
|
378
|
+
- Codex/OpenCode: `AGENTS.md`
|
|
379
|
+
|
|
380
|
+
Use `--defaults` to skip prompts (CI/automation).
|
|
381
|
+
|
|
382
|
+
### Inspect
|
|
383
|
+
|
|
384
|
+
`taskmux inspect <task>` returns JSON with task state:
|
|
385
|
+
|
|
386
|
+
```json
|
|
387
|
+
{
|
|
388
|
+
"name": "api",
|
|
389
|
+
"command": "python manage.py runserver 0.0.0.0:8000",
|
|
390
|
+
"auto_start": true,
|
|
391
|
+
"restart_policy": "on-failure",
|
|
392
|
+
"log_file": "/home/user/.taskmux/logs/myproject/api.log",
|
|
393
|
+
"cwd": "apps/api",
|
|
394
|
+
"health_check": "curl -sf http://localhost:8000/health",
|
|
395
|
+
"depends_on": ["db"],
|
|
396
|
+
"running": true,
|
|
397
|
+
"healthy": true,
|
|
398
|
+
"pid": "12345",
|
|
399
|
+
"pane_current_command": "python",
|
|
400
|
+
"pane_current_path": "/home/user/project/apps/api",
|
|
401
|
+
"window_id": "@1",
|
|
402
|
+
"pane_id": "%1"
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Persistent Logs
|
|
407
|
+
|
|
408
|
+
Task output is automatically piped to timestamped log files at `~/.taskmux/logs/{session}/{task}.log` using tmux's `pipe-pane`. Logs persist after session kill, survive tmux scrollback overflow, and include UTC timestamps on every line:
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
2024-01-01T14:00:00.123 Server started on port 3000
|
|
412
|
+
2024-01-01T14:00:01.456 GET /health 200 2ms
|
|
413
|
+
2024-01-01T14:00:05.789 Connected to database
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**Time-based filtering** with `--since`:
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
taskmux logs server --since 5m # last 5 minutes
|
|
420
|
+
taskmux logs --since 1h # all tasks, last hour
|
|
421
|
+
taskmux logs api --since "2024-01-01T14:00" # since ISO timestamp
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Log rotation:** When a log file exceeds `log_max_size` (default 10MB), it rotates: `task.log` → `task.log.1` → `task.log.2`, keeping up to `log_max_files` (default 3) rotated files.
|
|
425
|
+
|
|
426
|
+
**Cleanup:**
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
taskmux logs-clean # delete all logs for this session
|
|
430
|
+
taskmux logs-clean server # delete only server's logs
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
When log files exist, `taskmux logs` reads from them (full history with timestamps). Falls back to tmux scrollback for tasks started before log persistence was enabled.
|
|
434
|
+
|
|
435
|
+
### JSON Output
|
|
436
|
+
|
|
437
|
+
Every command supports `--json` for machine-readable output. This makes taskmux fully programmable by coding agents and scripts.
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
taskmux status --json
|
|
441
|
+
taskmux health --json
|
|
442
|
+
taskmux start server --json
|
|
443
|
+
taskmux logs server --json
|
|
444
|
+
taskmux events --json
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
**Status:**
|
|
448
|
+
|
|
449
|
+
```json
|
|
450
|
+
{
|
|
451
|
+
"session": "myproject",
|
|
452
|
+
"running": true,
|
|
453
|
+
"active_tasks": 3,
|
|
454
|
+
"tasks": [
|
|
455
|
+
{
|
|
456
|
+
"name": "server",
|
|
457
|
+
"running": true,
|
|
458
|
+
"healthy": true,
|
|
459
|
+
"command": "npm run dev",
|
|
460
|
+
"port": 3000,
|
|
461
|
+
"restart_policy": "on-failure"
|
|
462
|
+
}
|
|
463
|
+
]
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Health:**
|
|
468
|
+
|
|
469
|
+
```json
|
|
470
|
+
{
|
|
471
|
+
"healthy_count": 2,
|
|
472
|
+
"total_count": 3,
|
|
473
|
+
"tasks": [
|
|
474
|
+
{"name": "server", "healthy": true},
|
|
475
|
+
{"name": "worker", "healthy": false}
|
|
476
|
+
]
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Action commands** (`start`, `stop`, `restart`, `kill`, `add`, `remove`):
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
{"ok": true, "task": "server", "action": "started"}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Logs** (non-follow):
|
|
487
|
+
|
|
488
|
+
```json
|
|
489
|
+
{"task": "server", "lines": ["2024-01-01T14:00:00.123 Listening on :3000"]}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
**Errors:**
|
|
493
|
+
|
|
494
|
+
```json
|
|
495
|
+
{"ok": false, "error": "Task 'ghost' not found in config"}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Event History
|
|
499
|
+
|
|
500
|
+
Taskmux records lifecycle events to `~/.taskmux/events.jsonl`:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
taskmux events # recent events
|
|
504
|
+
taskmux events --task server # filter by task
|
|
505
|
+
taskmux events --since 10m # last 10 minutes
|
|
506
|
+
taskmux events -n 100 --json # last 100 as JSON
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
**Recorded events:**
|
|
510
|
+
|
|
511
|
+
| Event | Trigger |
|
|
512
|
+
|-------|---------|
|
|
513
|
+
| `task_started` | `start` command |
|
|
514
|
+
| `task_stopped` | `stop` command |
|
|
515
|
+
| `task_restarted` | `restart` command |
|
|
516
|
+
| `task_killed` | `kill` command |
|
|
517
|
+
| `session_started` | `start` (all tasks) |
|
|
518
|
+
| `session_stopped` | `stop` (all tasks) |
|
|
519
|
+
| `health_check_failed` | Health check fails (includes attempt count) |
|
|
520
|
+
| `auto_restart` | Auto-restart triggered (includes reason) |
|
|
521
|
+
| `max_restarts_reached` | Task hit max_restarts limit |
|
|
522
|
+
| `config_reloaded` | Config file changed (daemon/watch mode) |
|
|
523
|
+
|
|
524
|
+
**Example output:**
|
|
525
|
+
|
|
526
|
+
```
|
|
527
|
+
2024-01-01T14:00:00 [server] task_started
|
|
528
|
+
2024-01-01T14:05:00 [server] health_check_failed (attempt=1)
|
|
529
|
+
2024-01-01T14:05:30 [server] health_check_failed (attempt=2)
|
|
530
|
+
2024-01-01T14:06:00 [server] health_check_failed (attempt=3)
|
|
531
|
+
2024-01-01T14:06:00 [server] auto_restart (reason=health_retries_exceeded)
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
The events file auto-rotates, trimming to 10,000 lines when it exceeds 15,000.
|
|
535
|
+
|
|
536
|
+
## Monitoring & Auto-restart
|
|
537
|
+
|
|
538
|
+
### start --monitor (lightweight)
|
|
539
|
+
|
|
540
|
+
Start tasks and stay in the foreground monitoring health:
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
taskmux start --monitor # or: taskmux start -m
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
Checks health every 30 seconds and auto-restarts tasks according to their `restart_policy`. No WebSocket API — just monitoring and restart. Press Ctrl+C to stop monitoring (tasks keep running).
|
|
547
|
+
|
|
548
|
+
### Daemon Mode (full)
|
|
549
|
+
|
|
550
|
+
Run as a background daemon with WebSocket API, config watching, and auto-restart:
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
taskmux daemon # Default port 8765
|
|
554
|
+
taskmux daemon --port 9000 # Custom port
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
The daemon monitors task health every 30 seconds. Tasks are restarted per their `restart_policy` with exponential backoff (controlled by `restart_backoff` and `max_restarts`). Tasks that stay healthy for 60+ seconds have their restart counter reset. Config file changes are detected and applied automatically.
|
|
558
|
+
|
|
559
|
+
WebSocket API:
|
|
560
|
+
|
|
561
|
+
```javascript
|
|
562
|
+
const ws = new WebSocket('ws://localhost:8765');
|
|
563
|
+
|
|
564
|
+
ws.send(JSON.stringify({ command: "status" }));
|
|
565
|
+
ws.send(JSON.stringify({ command: "restart", params: { task: "server" } }));
|
|
566
|
+
ws.send(JSON.stringify({ command: "logs", params: { task: "server", lines: 50 } }));
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
## Tmux Integration
|
|
570
|
+
|
|
571
|
+
Taskmux creates standard tmux sessions — all tmux commands work:
|
|
572
|
+
|
|
573
|
+
```bash
|
|
574
|
+
tmux attach-session -t myproject # Attach to session
|
|
575
|
+
tmux list-sessions # List all sessions
|
|
576
|
+
# Ctrl+b 1/2/3 to switch windows, Ctrl+b d to detach
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## License
|
|
580
|
+
|
|
581
|
+
MIT
|