bgo-cli 0.2.0__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.
- bgo_cli-0.2.0/.gitignore +29 -0
- bgo_cli-0.2.0/AGENTS.md +191 -0
- bgo_cli-0.2.0/LICENSE +21 -0
- bgo_cli-0.2.0/PKG-INFO +273 -0
- bgo_cli-0.2.0/README.md +239 -0
- bgo_cli-0.2.0/TEAM.md +154 -0
- bgo_cli-0.2.0/bgo +2041 -0
- bgo_cli-0.2.0/install.sh +115 -0
- bgo_cli-0.2.0/pyproject.toml +99 -0
- bgo_cli-0.2.0/src/bgo_cli/__init__.py +50 -0
- bgo_cli-0.2.0/src/bgo_cli/__main__.py +8 -0
- bgo_cli-0.2.0/tests/conftest.py +44 -0
- bgo_cli-0.2.0/tests/test_state.py +73 -0
- bgo_cli-0.2.0/tests/test_table.py +136 -0
- bgo_cli-0.2.0/tests/test_utils.py +187 -0
bgo_cli-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Hindsight memory bank pointer (local)
|
|
2
|
+
.bank
|
|
3
|
+
|
|
4
|
+
# Serena MCP project state
|
|
5
|
+
.serena/
|
|
6
|
+
|
|
7
|
+
# PROJECT_INDEX (auto-generated)
|
|
8
|
+
PROJECT_INDEX.json
|
|
9
|
+
PROJECT_INDEX.md
|
|
10
|
+
|
|
11
|
+
# Hindsight migration markers
|
|
12
|
+
.hindsight-migrate-declined
|
|
13
|
+
|
|
14
|
+
# Python
|
|
15
|
+
__pycache__/
|
|
16
|
+
*.pyc
|
|
17
|
+
*.pyo
|
|
18
|
+
.pytest_cache/
|
|
19
|
+
|
|
20
|
+
# Build artifacts
|
|
21
|
+
build/
|
|
22
|
+
dist/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
src/*.egg-info/
|
|
25
|
+
|
|
26
|
+
# Local virtualenvs
|
|
27
|
+
.venv/
|
|
28
|
+
venv/
|
|
29
|
+
.env
|
bgo_cli-0.2.0/AGENTS.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
<!-- TEAM_MODE:START -->
|
|
4
|
+
## ⚡ Team Mode is ACTIVE
|
|
5
|
+
IMPORTANT: Read `TEAM.md` in the project root IN FULL before processing any task.
|
|
6
|
+
You are operating as Tech Lead of a multi-agent team, not as a solo developer.
|
|
7
|
+
If you don't remember Team Mode being activated, re-read `TEAM.md` NOW — it contains all instructions.
|
|
8
|
+
<!-- TEAM_MODE:END -->
|
|
9
|
+
|
|
10
|
+
<!-- myc:agents-start v=3 -->
|
|
11
|
+
## Project Management with Mycelium
|
|
12
|
+
|
|
13
|
+
This project uses [Mycelium](https://github.com/tcsenpai/mycelium) (`myc`) for task and epic management.
|
|
14
|
+
|
|
15
|
+
### Quick Reference
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Initialize mycelium in this project (creates .mycelium/ directory)
|
|
19
|
+
myc init
|
|
20
|
+
|
|
21
|
+
# Create an epic (a large body of work)
|
|
22
|
+
myc epic create --title "Feature X" --description "Build feature X"
|
|
23
|
+
|
|
24
|
+
# Create tasks within an epic
|
|
25
|
+
myc task create --title "Implement Y" --description "Build the implementation for Y" --epic 1 --priority high --due 2025-12-31
|
|
26
|
+
|
|
27
|
+
# Task priorities: low, medium, high, critical
|
|
28
|
+
# Task status: open, closed
|
|
29
|
+
|
|
30
|
+
# List tasks
|
|
31
|
+
myc task list
|
|
32
|
+
myc task list --epic 1
|
|
33
|
+
myc task list --overdue
|
|
34
|
+
myc task list --blocked
|
|
35
|
+
|
|
36
|
+
# Manage dependencies (task 1 blocks task 2)
|
|
37
|
+
myc task link blocks --task 1 2
|
|
38
|
+
myc deps show 2
|
|
39
|
+
|
|
40
|
+
# Close tasks (blocked tasks cannot be closed without --force)
|
|
41
|
+
myc task close 1
|
|
42
|
+
|
|
43
|
+
# Assign tasks
|
|
44
|
+
myc assignee create --name "Alice" --github "alice"
|
|
45
|
+
myc task assign 1 1
|
|
46
|
+
|
|
47
|
+
# Link to external resources
|
|
48
|
+
myc task link github-issue --task 1 "owner/repo#123"
|
|
49
|
+
myc task link github-pr --task 1 "owner/repo#456"
|
|
50
|
+
myc task link url --task 1 "https://example.com"
|
|
51
|
+
|
|
52
|
+
# Project overview
|
|
53
|
+
myc summary
|
|
54
|
+
|
|
55
|
+
# Export data
|
|
56
|
+
myc export json
|
|
57
|
+
myc export csv
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Data Model
|
|
61
|
+
|
|
62
|
+
- **Epic**: A large body of work with a title and optional description (e.g., a feature or milestone)
|
|
63
|
+
- **Task**: A unit of work with a title and optional description, optionally linked to an epic
|
|
64
|
+
- **Dependency**: Task A blocks Task B (B cannot close until A is closed)
|
|
65
|
+
- **Assignee**: Person assigned to a task (can have GitHub username)
|
|
66
|
+
- **External Ref**: Link to GitHub issues/PRs or URLs
|
|
67
|
+
|
|
68
|
+
### Git Tracking
|
|
69
|
+
|
|
70
|
+
The `.mycelium/` directory contains the SQLite database and should be committed to git:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git add .mycelium/
|
|
74
|
+
git commit -m "Add mycelium project tracking"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Follow-ups (`myc followup`, alias `myc fu`)
|
|
78
|
+
|
|
79
|
+
Lightweight scratch table for non-blocking "oh-by-the-way" items
|
|
80
|
+
captured mid-work — bugs, questions, ideas, things the user should look
|
|
81
|
+
at later. **Separate from tasks** (no epic/priority/deps/assignee). Most
|
|
82
|
+
follow-ups are resolved by the user, not the agent.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
myc followup add "body text" # capture (body required)
|
|
86
|
+
myc followup add "body text" --title "tag" # optional short title
|
|
87
|
+
myc fu add "short form alias works too"
|
|
88
|
+
|
|
89
|
+
myc followup list # all (default)
|
|
90
|
+
myc followup list -o # only active (open + in_progress)
|
|
91
|
+
myc followup list -c # only closed (done + wontfix)
|
|
92
|
+
myc followup list --status done # exact status
|
|
93
|
+
|
|
94
|
+
myc followup show <id> # full detail
|
|
95
|
+
myc followup next # lowest-ID active (agent loop)
|
|
96
|
+
myc followup count # JSON: {open, in_progress, done, wontfix}
|
|
97
|
+
|
|
98
|
+
myc followup start <id> # → in_progress
|
|
99
|
+
myc followup done <id> [--reason "..."] # → done
|
|
100
|
+
myc followup wontfix <id> [--reason "..."] # → wontfix
|
|
101
|
+
myc followup reopen <id> # → open
|
|
102
|
+
|
|
103
|
+
myc followup edit <id> --body "new body" [--title -|"new title"]
|
|
104
|
+
myc followup append <id> "more context" # timestamped, preserves existing
|
|
105
|
+
myc followup rm <id> [--force]
|
|
106
|
+
myc followup promote <id> [--epic N] [--priority high] # convert to task
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Agent rule — end-of-task follow-up check** (MANDATORY)
|
|
110
|
+
|
|
111
|
+
At the end of every mycelium-tracked unit of work (closing a task,
|
|
112
|
+
finishing a user-requested change that touched myc state), the agent
|
|
113
|
+
MUST:
|
|
114
|
+
|
|
115
|
+
1. Run `myc followup list --format json` (or `myc followup count
|
|
116
|
+
--format json`).
|
|
117
|
+
2. If `active > 0`, surface them to the user before wrapping:
|
|
118
|
+
> "Before we wrap — N open follow-up(s): [titles/bodies]. Want me to
|
|
119
|
+
> handle any now, or leave for later?"
|
|
120
|
+
3. **Never silently process them.** Always ask.
|
|
121
|
+
|
|
122
|
+
`myc task close` itself also prints a one-line reminder, but the agent
|
|
123
|
+
should still proactively check.
|
|
124
|
+
|
|
125
|
+
Use `myc followup add` during work to capture anything you notice but
|
|
126
|
+
shouldn't act on right now.
|
|
127
|
+
|
|
128
|
+
### For AI Agents
|
|
129
|
+
|
|
130
|
+
When working on this project:
|
|
131
|
+
|
|
132
|
+
1. Check existing tasks: `myc task list`
|
|
133
|
+
2. Check blocked tasks: `myc task list --blocked`
|
|
134
|
+
3. Create tasks for new work: `myc task create --title "..." --description "..." --epic N`
|
|
135
|
+
4. Capture incidental observations as follow-ups: `myc followup add "..."`
|
|
136
|
+
5. At end of task: `myc followup list` and surface open ones to the user
|
|
137
|
+
6. Mark tasks complete when done: `myc task close N`
|
|
138
|
+
7. Use `--format json` for machine-readable output: `myc task list --format json`
|
|
139
|
+
|
|
140
|
+
## Mental Frameworks for Mycelium Usage
|
|
141
|
+
|
|
142
|
+
### 1. INVEST — Task Quality Gate
|
|
143
|
+
|
|
144
|
+
Before creating or updating any task, validate it against these criteria.
|
|
145
|
+
A task that fails more than one is not ready to be written.
|
|
146
|
+
|
|
147
|
+
| Criterion | Rule |
|
|
148
|
+
|---|---|
|
|
149
|
+
| **Independent** | Can be completed without unblocking other tasks first |
|
|
150
|
+
| **Negotiable** | The *what* is fixed; the *how* remains open |
|
|
151
|
+
| **Valuable** | Produces a verifiable, concrete outcome |
|
|
152
|
+
| **Estimable** | If you cannot size it, it is too vague or too large |
|
|
153
|
+
| **Small** | If it spans more than one work cycle, split it |
|
|
154
|
+
| **Testable** | Has an explicit, binary done condition |
|
|
155
|
+
|
|
156
|
+
> If a task fails **Estimable** or **Testable**, convert it to an Epic and decompose.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### 2. DAG — Dependency Graph Thinking
|
|
161
|
+
|
|
162
|
+
Before scheduling or prioritizing, model the implicit dependency graph.
|
|
163
|
+
|
|
164
|
+
**Rules:**
|
|
165
|
+
- No task moves to `in_progress` if it has an unresolved upstream blocker
|
|
166
|
+
- Priority is a function of both urgency **and fan-out** (how many tasks does completing this one unlock?)
|
|
167
|
+
- Always work the **critical path** first — not the task that feels most urgent
|
|
168
|
+
|
|
169
|
+
**Prioritization heuristic:**
|
|
170
|
+
```
|
|
171
|
+
score = urgency + (blocked_tasks_count × 1.5)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
When creating a task, explicitly ask: *"What does this block, and what blocks this?"*
|
|
175
|
+
Set dependency links in Mycelium before touching status.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### 3. Principle of Minimal Surprise (PMS)
|
|
180
|
+
|
|
181
|
+
Mycelium's state must remain predictable and auditable at all times.
|
|
182
|
+
|
|
183
|
+
**Rules:**
|
|
184
|
+
- **Prefer idempotent operations** — update before you create; never duplicate
|
|
185
|
+
- **Check before write** — search for an equivalent item before creating a new one
|
|
186
|
+
- **Always annotate mutations** — every status change, priority shift, or reassignment must carry an explicit `reason` field
|
|
187
|
+
- **No orphan tasks** — every task must be linked to an Epic; every Epic to a strategic goal
|
|
188
|
+
- Deletions are a last resort; prefer `cancelled` status with a reason
|
|
189
|
+
|
|
190
|
+
> The state of Mycelium after any operation must be explainable to another agent with zero context.
|
|
191
|
+
<!-- myc:agents-end -->
|
bgo_cli-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tcsenpai
|
|
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.
|
bgo_cli-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bgo-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Lightweight, zero-dependency background process manager (pm2-style) with watch mode and auto-restart.
|
|
5
|
+
Project-URL: Homepage, https://github.com/tcsenpai/bgo
|
|
6
|
+
Project-URL: Repository, https://github.com/tcsenpai/bgo
|
|
7
|
+
Project-URL: Issues, https://github.com/tcsenpai/bgo/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/tcsenpai/bgo/commits/main
|
|
9
|
+
Author-email: tcsenpai <tcsenpai@discus.sh>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: background,cli,daemon,pm2,process-manager,supervisor,watchdog
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: System Administrators
|
|
17
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Topic :: System :: Systems Administration
|
|
27
|
+
Classifier: Topic :: Utilities
|
|
28
|
+
Requires-Python: >=3.9
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: twine>=4.0; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# bgo - Background Go
|
|
36
|
+
|
|
37
|
+
Lightweight, zero-dep background process manager inspired by pm2.
|
|
38
|
+
Detach any binary or script from your shell with one command.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- 🚀 **Simple syntax** — `bgo <name> -- <command>`
|
|
43
|
+
- 🐧 **Unix-style aliases** — `bgo open` / `kill` / `rm` / `ls`
|
|
44
|
+
- 📊 **Status monitoring** — CPU, memory, uptime in plain / normal / fancy tables (auto-detect)
|
|
45
|
+
- 📝 **Log management** — stdout / stderr / watcher logs with follow mode
|
|
46
|
+
- 🔄 **Lifecycle** — start, stop, restart, restart-stopped, restart-last, resurrect
|
|
47
|
+
- 👁 **Watch mode** — auto-restart crashed processes with fast-crash backoff
|
|
48
|
+
- 🧹 **Auto-cleanup** — clean stopped procs; keep logs on delete if desired
|
|
49
|
+
- 🤖 **Scriptable** — `--json` output for any pipeline
|
|
50
|
+
- ⚡ **Zero runtime dependencies** — pure Python 3.9+
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Via PyPI
|
|
56
|
+
pip install bgo-cli
|
|
57
|
+
# or
|
|
58
|
+
pipx install bgo-cli
|
|
59
|
+
# or
|
|
60
|
+
uv tool install bgo-cli
|
|
61
|
+
|
|
62
|
+
# Via the install script (system-wide, needs sudo)
|
|
63
|
+
./install.sh
|
|
64
|
+
|
|
65
|
+
# User-local (~/.local/bin, no sudo)
|
|
66
|
+
./install.sh --local
|
|
67
|
+
|
|
68
|
+
# Or manually — bgo is a single file
|
|
69
|
+
cp bgo ~/.local/bin/ # or /usr/local/bin/
|
|
70
|
+
ln -s "$(pwd)/bgo" ~/.local/bin/bgo
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Run `./install.sh --help` for `--force` and `--uninstall` options.
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Start a process
|
|
79
|
+
bgo myserver -- python3 -m http.server 8080
|
|
80
|
+
bgo open myserver -- python3 -m http.server 8080 # alias
|
|
81
|
+
|
|
82
|
+
# Check status
|
|
83
|
+
bgo status # full alias chain: status / ls / list
|
|
84
|
+
bgo ls
|
|
85
|
+
|
|
86
|
+
# Status detail for one proc (or: bgo <registered-name>)
|
|
87
|
+
bgo myserver
|
|
88
|
+
|
|
89
|
+
# View logs
|
|
90
|
+
bgo logs myserver
|
|
91
|
+
bgo logs myserver -f # follow (tail -f)
|
|
92
|
+
bgo follow myserver # alias for logs -f
|
|
93
|
+
|
|
94
|
+
# Stop / restart / delete
|
|
95
|
+
bgo stop myserver # or: bgo kill myserver
|
|
96
|
+
bgo restart myserver
|
|
97
|
+
bgo delete myserver # or: bgo rm myserver
|
|
98
|
+
bgo rm myserver --keep-logs # preserve log files
|
|
99
|
+
|
|
100
|
+
# Bare `bgo` prints help; `bgo <unknown>` errors out (never silently spawns)
|
|
101
|
+
bgo
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Commands
|
|
105
|
+
|
|
106
|
+
### Lifecycle
|
|
107
|
+
|
|
108
|
+
| Command | Description |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `bgo start <name> -- <cmd>` | Start a process (alias: `open`) |
|
|
111
|
+
| `bgo <name> -- <cmd>` | Shorthand for start |
|
|
112
|
+
| `bgo stop <name>` | Stop (SIGTERM, alias: `kill`) |
|
|
113
|
+
| `bgo stop <name> -f` | Force kill (SIGKILL) |
|
|
114
|
+
| `bgo restart <name>` | Restart; preserves watch state and counters |
|
|
115
|
+
| `bgo restart <name> --reset-counters` | Also zero `watch.restarts` |
|
|
116
|
+
| `bgo restart-stopped` | Pick stopped procs to restart (interactive) |
|
|
117
|
+
| `bgo restart-stopped --all` | Restart every stopped proc |
|
|
118
|
+
| `bgo restart-stopped <name>...` | Restart named stopped procs |
|
|
119
|
+
| `bgo restart-last` | Menu sorted most-recent-first |
|
|
120
|
+
| `bgo restart-last --all` | Restart all not-running procs in recent order |
|
|
121
|
+
| `bgo resurrect` | Restart all procs that were running before shutdown |
|
|
122
|
+
| `bgo delete <name>` | Remove proc + logs (alias: `rm`) |
|
|
123
|
+
| `bgo delete <name> --keep-logs` | Remove proc, keep logs |
|
|
124
|
+
| `bgo clean` | Drop all stopped procs from the list |
|
|
125
|
+
|
|
126
|
+
### Inspection
|
|
127
|
+
|
|
128
|
+
| Command | Description |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `bgo status` | Process table (alias: `ls`, `list`) |
|
|
131
|
+
| `bgo status <name>` | Detail view for one proc |
|
|
132
|
+
| `bgo status -w` | Watch mode (auto-refresh every 2s) |
|
|
133
|
+
| `bgo status -w --interval N` | Custom refresh interval |
|
|
134
|
+
| `bgo status --json` | Machine-readable output |
|
|
135
|
+
| `bgo status --plain` | ASCII-only output (no color, no glyphs) |
|
|
136
|
+
| `bgo status --fancy` | Force Unicode box-drawing rendering |
|
|
137
|
+
| `bgo <registered-name>` | Shorthand for `bgo status <name>` |
|
|
138
|
+
| `bgo logs <name>` | Last 50 lines |
|
|
139
|
+
| `bgo logs <name> -f` | Follow logs |
|
|
140
|
+
| `bgo logs <name> -n 100` | Last 100 lines |
|
|
141
|
+
| `bgo logs <name> --stderr` | Only stderr |
|
|
142
|
+
| `bgo logs <name> --watcher` | Watcher event log |
|
|
143
|
+
| `bgo follow <name>` | Alias for `logs -f` (also: `tail`) |
|
|
144
|
+
|
|
145
|
+
### Watch mode
|
|
146
|
+
|
|
147
|
+
| Command | Description |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `bgo start -w <name> -- <cmd>` | Start with watcher attached |
|
|
150
|
+
| `bgo -w <name> <cmd>` | Direct mode with watcher |
|
|
151
|
+
| `bgo watch <name>` | Attach watcher to a running proc |
|
|
152
|
+
| `bgo watch <name> --interval N --min-uptime N --on-fast-crash MODE` | Tune |
|
|
153
|
+
| `bgo unwatch <name>` | Detach watcher, keep proc |
|
|
154
|
+
|
|
155
|
+
## Examples
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# Python HTTP server
|
|
159
|
+
bgo web -- python3 -m http.server 8080
|
|
160
|
+
|
|
161
|
+
# Node.js app with watcher
|
|
162
|
+
bgo -w api -- npm start
|
|
163
|
+
|
|
164
|
+
# Custom binary with working directory
|
|
165
|
+
bgo start dashboard --cwd /opt/app -- node server.js
|
|
166
|
+
|
|
167
|
+
# Inspect one proc
|
|
168
|
+
bgo web
|
|
169
|
+
|
|
170
|
+
# Scripted: stop every online proc via JSON
|
|
171
|
+
bgo status --json | python3 -c '
|
|
172
|
+
import json, sys, subprocess
|
|
173
|
+
for p in json.load(sys.stdin):
|
|
174
|
+
if p["status"] == "online":
|
|
175
|
+
subprocess.run(["bgo", "stop", p["name"]])
|
|
176
|
+
'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Status Table
|
|
180
|
+
|
|
181
|
+
The table auto-detects terminal capabilities and picks the best rendering:
|
|
182
|
+
|
|
183
|
+
| Level | Trigger | What you get |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `plain` | non-TTY (pipes, CI logs), `TERM=dumb`, or `--plain` | ASCII only, no color, no glyphs |
|
|
186
|
+
| `normal` | TTY without UTF-8 locale | ANSI color + ASCII rules |
|
|
187
|
+
| `fancy` | TTY + UTF-8 locale (default) | ANSI color + Unicode box-drawing |
|
|
188
|
+
|
|
189
|
+
Override with `--plain` / `--fancy` or `BGO_TABLE=plain|normal|fancy`.
|
|
190
|
+
|
|
191
|
+
Sample (fancy):
|
|
192
|
+
```
|
|
193
|
+
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
194
|
+
┃ NAME STATUS PID CPU MEM UPTIME WATCH COMMAND ┃
|
|
195
|
+
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
196
|
+
┃ web ● online 12345 2.5 0.1 00:05 ✓ 0 python3 -m http.server 8080 ┃
|
|
197
|
+
┃ api ● online 12346 0.0 0.0 01:23 ✓ 3 node server.js ┃
|
|
198
|
+
┃ worker ○ stopped - - - - ⚠ errored python3 worker.py ┃
|
|
199
|
+
┃ batch ● online 12347 0.0 0.0 02:11 · ./batch ┃
|
|
200
|
+
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
201
|
+
Total: 4 | ● online: 3 | ○ stopped: 1
|
|
202
|
+
|
|
203
|
+
⚠ 1 errored:
|
|
204
|
+
worker — 4 consecutive fast-crashes
|
|
205
|
+
bgo logs worker --watcher | bgo restart worker
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
CPU / MEM / uptime are pulled in a single batched `ps` call regardless of how many procs are running.
|
|
209
|
+
|
|
210
|
+
## Watch Mode
|
|
211
|
+
|
|
212
|
+
Watch mode runs a sidecar process per watched proc that polls the
|
|
213
|
+
target and restarts it on crash. State (restart count, error reason,
|
|
214
|
+
last stderr tail) is recorded in the proc JSON and surfaced via
|
|
215
|
+
`bgo status`.
|
|
216
|
+
|
|
217
|
+
### Quick start
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
bgo start -w myapi -- node server.js # start with watcher
|
|
221
|
+
bgo -w myapi node server.js # direct-mode variant
|
|
222
|
+
bgo watch myapi # attach to a running proc
|
|
223
|
+
bgo watch myapi --interval 5 --min-uptime 3 --on-fast-crash backoff
|
|
224
|
+
bgo unwatch myapi # detach, keep proc
|
|
225
|
+
bgo logs myapi --watcher # inspect events
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Fast-crash policy
|
|
229
|
+
|
|
230
|
+
If a process dies before `--min-uptime` (default 2s) it's a *fast crash*. Reaction depends on `--on-fast-crash`:
|
|
231
|
+
|
|
232
|
+
| Mode | Behavior |
|
|
233
|
+
|---|---|
|
|
234
|
+
| `backoff` (default) | Wait 2s, retry. Then 4s, then 8s. After 4 consecutive fast-crashes, mark `errored` and exit watcher. |
|
|
235
|
+
| `stop` | Mark `errored` on the first fast-crash. |
|
|
236
|
+
| `retry` | Retry indefinitely, capped at 8s backoff. |
|
|
237
|
+
|
|
238
|
+
When a proc enters `errored`:
|
|
239
|
+
- WATCH column shows `⚠ errored` (or `[!] errored` in plain).
|
|
240
|
+
- Status footer summarizes errored procs and hints at the recovery commands.
|
|
241
|
+
- `bgo status <name>` detail shows the error reason and last stderr tail.
|
|
242
|
+
- `bgo restart <name>` clears the errored flag and re-spawns the watcher. Restart counter is **preserved** by default — use `--reset-counters` to zero it.
|
|
243
|
+
|
|
244
|
+
### Tunables
|
|
245
|
+
|
|
246
|
+
| Flag | Default | Notes |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| `--interval N` | 3 | Poll interval after the initial uptime window |
|
|
249
|
+
| `--min-uptime N` | 2 | Crash threshold; sub-window polled at high frequency |
|
|
250
|
+
| `--on-fast-crash MODE` | `backoff` | One of `backoff`, `stop`, `retry` |
|
|
251
|
+
| `--reset` | off | `bgo watch` only — reset prior watch config to defaults |
|
|
252
|
+
|
|
253
|
+
## Storage
|
|
254
|
+
|
|
255
|
+
- State: `~/.bgo/procs/<name>.json` — one file per process, written atomically (tmp + `os.replace`)
|
|
256
|
+
- Logs: `~/.bgo/logs/<name>.out.log`, `<name>.err.log`, `<name>.watcher.log`
|
|
257
|
+
|
|
258
|
+
## Testing
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
python3 -m pytest tests/ -v
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
54 tests covering state I/O, atomic writes, command-shape detection, name derivation, liveness + zombie filtering, watch-config inheritance, and table rendering across all three levels.
|
|
265
|
+
|
|
266
|
+
## Requirements
|
|
267
|
+
|
|
268
|
+
- Python 3.9+
|
|
269
|
+
- Linux or macOS (zombie detection is platform-aware: `/proc` on Linux, `ps -o stat=` on macOS)
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
MIT
|