project-sandbox-tui 0.1.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.
- project_sandbox_tui-0.1.0/.gitignore +13 -0
- project_sandbox_tui-0.1.0/LICENSE +21 -0
- project_sandbox_tui-0.1.0/PKG-INFO +96 -0
- project_sandbox_tui-0.1.0/README.md +68 -0
- project_sandbox_tui-0.1.0/docs/architecture.md +124 -0
- project_sandbox_tui-0.1.0/docs/cleanup.md +43 -0
- project_sandbox_tui-0.1.0/docs/command-workflow.md +133 -0
- project_sandbox_tui-0.1.0/docs/configuration.md +265 -0
- project_sandbox_tui-0.1.0/docs/development.md +150 -0
- project_sandbox_tui-0.1.0/docs/images/command-workflow/01-repository-command.svg +137 -0
- project_sandbox_tui-0.1.0/docs/images/command-workflow/02-repository-task.svg +132 -0
- project_sandbox_tui-0.1.0/docs/images/command-workflow/03-workspace-tasks.svg +134 -0
- project_sandbox_tui-0.1.0/docs/images/command-workflow/04-failure-output.svg +130 -0
- project_sandbox_tui-0.1.0/docs/merging.md +31 -0
- project_sandbox_tui-0.1.0/docs/models.md +29 -0
- project_sandbox_tui-0.1.0/docs/sessions.md +101 -0
- project_sandbox_tui-0.1.0/docs/usage.md +422 -0
- project_sandbox_tui-0.1.0/pyproject.toml +99 -0
- project_sandbox_tui-0.1.0/scripts/make-release.sh +173 -0
- project_sandbox_tui-0.1.0/scripts/test.sh +8 -0
- project_sandbox_tui-0.1.0/scripts/update-pins.py +305 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/__init__.py +8 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/__main__.py +3 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/app.py +2534 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/cleanup.py +459 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/cli.py +343 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/commands.py +288 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/config.py +812 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/doctor.py +209 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/history.py +103 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/initialization.py +57 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/jobs.py +713 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/launchers.py +193 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/models.py +406 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/notify.py +56 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/octopus.py +292 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/opgraph.py +607 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/processes.py +16 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/review.py +95 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/sandbox.py +317 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/__init__.py +0 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/command.py +156 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/dialogs.py +563 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/history.py +111 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/launch.py +529 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/log.py +204 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/oplog.py +281 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/review.py +200 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/screens/tasks.py +208 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/settings.py +230 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/skills/sbx-tui-config/SKILL.md +125 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/tasks.py +383 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/terminal.py +71 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/vcs.py +708 -0
- project_sandbox_tui-0.1.0/src/project_sandbox_tui/widgets.py +311 -0
- project_sandbox_tui-0.1.0/tests/__init__.py +0 -0
- project_sandbox_tui-0.1.0/tests/conftest.py +231 -0
- project_sandbox_tui-0.1.0/tests/fake_interactive_agent.py +43 -0
- project_sandbox_tui-0.1.0/tests/fake_project_sandbox.py +101 -0
- project_sandbox_tui-0.1.0/tests/test_app.py +2605 -0
- project_sandbox_tui-0.1.0/tests/test_cleanup.py +560 -0
- project_sandbox_tui-0.1.0/tests/test_cli.py +272 -0
- project_sandbox_tui-0.1.0/tests/test_command_line.py +555 -0
- project_sandbox_tui-0.1.0/tests/test_commands.py +245 -0
- project_sandbox_tui-0.1.0/tests/test_config.py +382 -0
- project_sandbox_tui-0.1.0/tests/test_container_smoke.py +209 -0
- project_sandbox_tui-0.1.0/tests/test_dialogs.py +328 -0
- project_sandbox_tui-0.1.0/tests/test_doctor.py +125 -0
- project_sandbox_tui-0.1.0/tests/test_history.py +159 -0
- project_sandbox_tui-0.1.0/tests/test_jobs.py +626 -0
- project_sandbox_tui-0.1.0/tests/test_keys.py +89 -0
- project_sandbox_tui-0.1.0/tests/test_launchers.py +225 -0
- project_sandbox_tui-0.1.0/tests/test_log.py +158 -0
- project_sandbox_tui-0.1.0/tests/test_maintenance_scripts.py +356 -0
- project_sandbox_tui-0.1.0/tests/test_models.py +600 -0
- project_sandbox_tui-0.1.0/tests/test_notify.py +96 -0
- project_sandbox_tui-0.1.0/tests/test_octopus.py +174 -0
- project_sandbox_tui-0.1.0/tests/test_opgraph.py +382 -0
- project_sandbox_tui-0.1.0/tests/test_packaging.py +76 -0
- project_sandbox_tui-0.1.0/tests/test_processes.py +103 -0
- project_sandbox_tui-0.1.0/tests/test_pty_session.py +267 -0
- project_sandbox_tui-0.1.0/tests/test_review.py +59 -0
- project_sandbox_tui-0.1.0/tests/test_sandbox.py +299 -0
- project_sandbox_tui-0.1.0/tests/test_settings.py +231 -0
- project_sandbox_tui-0.1.0/tests/test_tasks.py +240 -0
- project_sandbox_tui-0.1.0/tests/test_terminal.py +51 -0
- project_sandbox_tui-0.1.0/tests/test_vcs.py +786 -0
- project_sandbox_tui-0.1.0/tests/test_widgets.py +78 -0
- project_sandbox_tui-0.1.0/uv.lock +275 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Peter Krusche
|
|
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.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: project-sandbox-tui
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Minimal vim/ranger-style TUI for running project-sandbox sessions per branch
|
|
5
|
+
Project-URL: Homepage, https://github.com/pkrusche/project-sandbox-tui
|
|
6
|
+
Project-URL: Documentation, https://github.com/pkrusche/project-sandbox-tui/tree/main/docs
|
|
7
|
+
Project-URL: Issues, https://github.com/pkrusche/project-sandbox-tui/issues
|
|
8
|
+
Project-URL: Source, https://github.com/pkrusche/project-sandbox-tui
|
|
9
|
+
Author: Peter Krusche
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: coding-agent,jj,jujutsu,sandbox,textual,tui
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console :: Curses
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: Software Development :: Version Control
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Requires-Dist: textual<9,>=6.0
|
|
26
|
+
Requires-Dist: tomlkit<0.16,>=0.13
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# project-sandbox-tui
|
|
30
|
+
|
|
31
|
+
A [vim](https://www.vim.org/)/[ranger](https://ranger.fm/)-style terminal UI for
|
|
32
|
+
running coding agents in [project-sandbox](https://github.com/pkrusche/project-sandbox)
|
|
33
|
+
containers, with one sandbox per [jj bookmark](https://docs.jj-vcs.dev/latest/bookmarks/).
|
|
34
|
+
|
|
35
|
+
> 🤖 Written largely by AI, reviewed by a human. Use at your own risk.
|
|
36
|
+
|
|
37
|
+
- Run agents interactively or in the background with completion notifications.
|
|
38
|
+
- Merge or rebase tasks from separate jj workspaces with agent assistance.
|
|
39
|
+
- Follow each bookmark's prompts and merges in a history graph (`L` for every run); delete and undo with `dd` / `u`.
|
|
40
|
+
- Run shell or configured commands in the foreground (`:!make`, `:test`), once in the repository background (`:&test`), or in each selected workspace (`:*test`); see the [command comparison](docs/usage.md#choosing-how-to-run-a-shell-command). Map any form to leader keys.
|
|
41
|
+
- Let project-sandbox handle isolation, credentials and workspace teardown.
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
sbx-tui ~/workspace/myproject 5 bookmarks · 1 running
|
|
45
|
+
bookmark vs main reviewed head subject status
|
|
46
|
+
@ working copy ● ↑1 qzmvtlno (no description set)
|
|
47
|
+
▶─╮ merge/parser-tui ↑6 Δ9 0/9 wnrtlxqz Merge feature/parser, spike… merging
|
|
48
|
+
✓ │ feature/parser ↑3 Δ5 5/5 sutnpmvo Add streaming parser completed
|
|
49
|
+
│ ✗ spike/tui ↑2 ↓1 Δ4 1/4 zsuskuln Try a ranger-style layout failed (1)
|
|
50
|
+
│ │ ○ fix/flaky-tests ↑1 Δ2 0/2 rlvkpnrz Stabilise integration tests
|
|
51
|
+
○─┴─╯ main ● = kxyrwpsn Initial import
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- Linux or macOS (WSL on Windows)
|
|
57
|
+
- Python ≥ 3.12 and [uv](https://docs.astral.sh/uv/)
|
|
58
|
+
- [jj](https://github.com/jj-vcs/jj) ≥ 0.46 on `PATH`; for an existing Git repository, run `jj git init --colocate`
|
|
59
|
+
- [project-sandbox](https://github.com/pkrusche/project-sandbox) ≥ 0.2.2 and Apple Containers, Docker or Podman
|
|
60
|
+
|
|
61
|
+
## Getting started
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
uv tool install project-sandbox
|
|
65
|
+
uv tool install git+https://github.com/pkrusche/project-sandbox-tui
|
|
66
|
+
# From a checkout: uv tool install . (or uv sync && uv run sbx-tui)
|
|
67
|
+
|
|
68
|
+
cd ~/workspace/myproject
|
|
69
|
+
sbx-tui --init # detect the project type, write config and install a local setup skill
|
|
70
|
+
sbx-tui doctor # check dependencies, runtime and configuration
|
|
71
|
+
sbx-tui --dry-run # preview project-sandbox calls
|
|
72
|
+
sbx-tui # launch
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Press `?` for full help.
|
|
76
|
+
|
|
77
|
+
`--init` detects a root `Dockerfile`, Python/uv, Rust/Cargo or Node/npm project
|
|
78
|
+
(in that order). It installs a gitignored `sbx-tui-config` agent skill to help
|
|
79
|
+
tailor the configuration. Existing config and skill files are preserved; see
|
|
80
|
+
[initialization](docs/configuration.md#initialization) for detection and skill paths.
|
|
81
|
+
|
|
82
|
+
## Documentation
|
|
83
|
+
|
|
84
|
+
- [Usage and keys](docs/usage.md)
|
|
85
|
+
- [Worked example: foreground and background commands](docs/command-workflow.md)
|
|
86
|
+
- [Sessions, terminals and notifications](docs/sessions.md)
|
|
87
|
+
- [Merging and rebasing](docs/merging.md)
|
|
88
|
+
- [Workspace cleanup](docs/cleanup.md)
|
|
89
|
+
- [Configuration](docs/configuration.md)
|
|
90
|
+
- [Model suggestions](docs/models.md)
|
|
91
|
+
- [Architecture](docs/architecture.md)
|
|
92
|
+
- [Development](docs/development.md)
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# project-sandbox-tui
|
|
2
|
+
|
|
3
|
+
A [vim](https://www.vim.org/)/[ranger](https://ranger.fm/)-style terminal UI for
|
|
4
|
+
running coding agents in [project-sandbox](https://github.com/pkrusche/project-sandbox)
|
|
5
|
+
containers, with one sandbox per [jj bookmark](https://docs.jj-vcs.dev/latest/bookmarks/).
|
|
6
|
+
|
|
7
|
+
> 🤖 Written largely by AI, reviewed by a human. Use at your own risk.
|
|
8
|
+
|
|
9
|
+
- Run agents interactively or in the background with completion notifications.
|
|
10
|
+
- Merge or rebase tasks from separate jj workspaces with agent assistance.
|
|
11
|
+
- Follow each bookmark's prompts and merges in a history graph (`L` for every run); delete and undo with `dd` / `u`.
|
|
12
|
+
- Run shell or configured commands in the foreground (`:!make`, `:test`), once in the repository background (`:&test`), or in each selected workspace (`:*test`); see the [command comparison](docs/usage.md#choosing-how-to-run-a-shell-command). Map any form to leader keys.
|
|
13
|
+
- Let project-sandbox handle isolation, credentials and workspace teardown.
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
sbx-tui ~/workspace/myproject 5 bookmarks · 1 running
|
|
17
|
+
bookmark vs main reviewed head subject status
|
|
18
|
+
@ working copy ● ↑1 qzmvtlno (no description set)
|
|
19
|
+
▶─╮ merge/parser-tui ↑6 Δ9 0/9 wnrtlxqz Merge feature/parser, spike… merging
|
|
20
|
+
✓ │ feature/parser ↑3 Δ5 5/5 sutnpmvo Add streaming parser completed
|
|
21
|
+
│ ✗ spike/tui ↑2 ↓1 Δ4 1/4 zsuskuln Try a ranger-style layout failed (1)
|
|
22
|
+
│ │ ○ fix/flaky-tests ↑1 Δ2 0/2 rlvkpnrz Stabilise integration tests
|
|
23
|
+
○─┴─╯ main ● = kxyrwpsn Initial import
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Requirements
|
|
27
|
+
|
|
28
|
+
- Linux or macOS (WSL on Windows)
|
|
29
|
+
- Python ≥ 3.12 and [uv](https://docs.astral.sh/uv/)
|
|
30
|
+
- [jj](https://github.com/jj-vcs/jj) ≥ 0.46 on `PATH`; for an existing Git repository, run `jj git init --colocate`
|
|
31
|
+
- [project-sandbox](https://github.com/pkrusche/project-sandbox) ≥ 0.2.2 and Apple Containers, Docker or Podman
|
|
32
|
+
|
|
33
|
+
## Getting started
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
uv tool install project-sandbox
|
|
37
|
+
uv tool install git+https://github.com/pkrusche/project-sandbox-tui
|
|
38
|
+
# From a checkout: uv tool install . (or uv sync && uv run sbx-tui)
|
|
39
|
+
|
|
40
|
+
cd ~/workspace/myproject
|
|
41
|
+
sbx-tui --init # detect the project type, write config and install a local setup skill
|
|
42
|
+
sbx-tui doctor # check dependencies, runtime and configuration
|
|
43
|
+
sbx-tui --dry-run # preview project-sandbox calls
|
|
44
|
+
sbx-tui # launch
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Press `?` for full help.
|
|
48
|
+
|
|
49
|
+
`--init` detects a root `Dockerfile`, Python/uv, Rust/Cargo or Node/npm project
|
|
50
|
+
(in that order). It installs a gitignored `sbx-tui-config` agent skill to help
|
|
51
|
+
tailor the configuration. Existing config and skill files are preserved; see
|
|
52
|
+
[initialization](docs/configuration.md#initialization) for detection and skill paths.
|
|
53
|
+
|
|
54
|
+
## Documentation
|
|
55
|
+
|
|
56
|
+
- [Usage and keys](docs/usage.md)
|
|
57
|
+
- [Worked example: foreground and background commands](docs/command-workflow.md)
|
|
58
|
+
- [Sessions, terminals and notifications](docs/sessions.md)
|
|
59
|
+
- [Merging and rebasing](docs/merging.md)
|
|
60
|
+
- [Workspace cleanup](docs/cleanup.md)
|
|
61
|
+
- [Configuration](docs/configuration.md)
|
|
62
|
+
- [Model suggestions](docs/models.md)
|
|
63
|
+
- [Architecture](docs/architecture.md)
|
|
64
|
+
- [Development](docs/development.md)
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
The Textual app calls `jj` and `project-sandbox` and reads session records from
|
|
4
|
+
disk. Project-sandbox manages containers, credentials and runtimes. Repositories
|
|
5
|
+
require a `.jj/` directory.
|
|
6
|
+
|
|
7
|
+
## Modules (`src/project_sandbox_tui/`)
|
|
8
|
+
|
|
9
|
+
| Module | Role |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `cli.py`, `settings.py`, `doctor.py` | Entry point, config commands, dependency checks |
|
|
12
|
+
| `config.py` | Typed TOML loading, validation, trust, state paths and launch defaults |
|
|
13
|
+
| `vcs.py` | Bookmark discovery, creation, changed files, working-copy moves and fast-forwarding |
|
|
14
|
+
| `sandbox.py` | `LaunchSpec`, argv construction and session matching |
|
|
15
|
+
| `jobs.py` | Detached jobs, persistence, polling, adoption and stopping |
|
|
16
|
+
| `tasks.py` | `:&` / `:*` host commands: detached, output and exit status captured |
|
|
17
|
+
| `launchers.py`, `terminal.py` | External terminals and terminal restoration |
|
|
18
|
+
| `octopus.py`, `cleanup.py` | Host-side integration and workspace cleanup |
|
|
19
|
+
| `models.py`, `history.py`, `notify.py` | Suggestions, prompt and command history, notifications |
|
|
20
|
+
| `commands.py` | `:` command parsing (`:!`, `:&`, `:*`, `[actions]`), completion and leader-key mappings |
|
|
21
|
+
| `review.py` | Diff review: files marked seen per bookmark |
|
|
22
|
+
| `opgraph.py` | Bookmark history graph, op log, undo stack and jj-style graph layout |
|
|
23
|
+
| `app.py`, `widgets.py`, `screens/` | Bookmark list, forms, dialogs, logs, op history and prompt search |
|
|
24
|
+
|
|
25
|
+
## Launches and job lifecycle
|
|
26
|
+
|
|
27
|
+
`sandbox.py` maps [configuration](configuration.md) to `--flag=value` arguments,
|
|
28
|
+
preventing leading `-` characters from becoming options. Bookmark launches add
|
|
29
|
+
`--branch`; background launches add `--prompt-text`, `--log`, `--json-summary`
|
|
30
|
+
and optional `--timeout`.
|
|
31
|
+
|
|
32
|
+
1. **Guard:** reject busy workspaces. Project-sandbox records in
|
|
33
|
+
`$XDG_STATE_HOME/project-sandbox/sessions/` lack branch names; match
|
|
34
|
+
`workspace_path` using its slug rule
|
|
35
|
+
(`/` → `-`, plus sha256[:6]).
|
|
36
|
+
2. **Spawn:** detach with `start_new_session=True`, capture output and write a
|
|
37
|
+
job record under a repository lock.
|
|
38
|
+
3. **Poll:** every `refresh_seconds`, finalise dead processes using the summary's
|
|
39
|
+
exit code, then the child's, else `orphaned`. A summary never closes
|
|
40
|
+
a live process.
|
|
41
|
+
4. **Notify:** claim `notified` atomically so only one instance announces completion.
|
|
42
|
+
5. **Restart:** adopt live processes; finalise dead ones.
|
|
43
|
+
|
|
44
|
+
Detached helpers (notifications, editors, terminal launchers and container stop
|
|
45
|
+
commands) are reaped by daemon waiters. Forgotten or replaced job handles are
|
|
46
|
+
handed to the same waiters so their children are collected even without another
|
|
47
|
+
refresh. Waiters do not stop live processes or delay quitting the TUI.
|
|
48
|
+
|
|
49
|
+
Inline sessions use temporary job records to mark workspaces busy across
|
|
50
|
+
instances. Job records keep only each bookmark's latest run; `opgraph.json`
|
|
51
|
+
keeps every run as a node of the [bookmark history](usage.md#bookmark-history),
|
|
52
|
+
finalised with the bookmark's commit on the first refresh that lists bookmarks
|
|
53
|
+
after the run ended. See [sessions](sessions.md) for terminal behavior and
|
|
54
|
+
[state storage](configuration.md#state-directory) for paths and permissions.
|
|
55
|
+
|
|
56
|
+
## Concurrency and locking
|
|
57
|
+
|
|
58
|
+
Several TUI instances may run on one repository, and each does its jj, file and
|
|
59
|
+
process work in worker threads. These locks order what they do:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
this TUI only _history_lock (asyncio) held for a whole action that
|
|
63
|
+
│ changes the repository: n dd u
|
|
64
|
+
│ ctrl+r f @ e, merge or rebase
|
|
65
|
+
│ preparation, W removal
|
|
66
|
+
▼
|
|
67
|
+
every TUI on the opgraph.json.lock bookmark history and undo stack
|
|
68
|
+
repository │
|
|
69
|
+
(flock on files ▼
|
|
70
|
+
in the state jobs.lock job records, and the launch
|
|
71
|
+
directory) │ guard: busy checks and spawns
|
|
72
|
+
│ happen under it
|
|
73
|
+
▼
|
|
74
|
+
tasks.lock background task records
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A lock is only taken while holding the ones above it, never the reverse, so
|
|
78
|
+
actions cannot deadlock. The diff review, prompt history and command history
|
|
79
|
+
each have their own `<file>.lock`, taken alone.
|
|
80
|
+
|
|
81
|
+
Which actions take which locks. *history* is `_history_lock`, *graph*
|
|
82
|
+
`opgraph.json.lock`; `A → B` takes B while holding A, `A, B` one after the other:
|
|
83
|
+
|
|
84
|
+
| Action | Locks | Done under them |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| Refresh (every `ui.refresh_seconds`) | graph, jobs, tasks | Adopt new bookmarks and record finished runs; poll jobs and claim their notifications; the same for tasks |
|
|
87
|
+
| `r` / `i`, `:NAME` actions | jobs, graph | Recheck busy, spawn and write the job record; add the run node. A new bookmark typed into the form is recorded first, under graph |
|
|
88
|
+
| `r` / `i` with bookmarks selected | history → (jobs, graph); then as `r` / `i` | Check the result bookmark is free, merge or rebase with jj, record the result bookmark |
|
|
89
|
+
| `n` | history → graph | Create with jj, then record the creation and its undo entry |
|
|
90
|
+
| `dd` | history → graph → jobs | Busy check, delete, undo entry |
|
|
91
|
+
| `u` / `ctrl+r` | history → graph → jobs | Busy check, delete or restore, move the undo position |
|
|
92
|
+
| `f` | history → graph | Fast-forward main, undo entry |
|
|
93
|
+
| `@` | history → jobs | Busy check of the working copy (and of the target's workspace before starting a change there), move it |
|
|
94
|
+
| `e` | history | Describe, only if the target is still the commit that was edited |
|
|
95
|
+
| `:*` | jobs, tasks | Create a missing workspace unless a session is starting; start the task |
|
|
96
|
+
| `:&` | jobs, tasks | Start one task in the repository under `@`; warn if a session is running there |
|
|
97
|
+
| `o` | jobs | Create a missing workspace unless a session is starting |
|
|
98
|
+
| `x` | tasks, jobs, graph | Signal tasks; signal or forget the run; record a forgotten run's outcome |
|
|
99
|
+
| `W` | tasks; removal: history → graph → jobs → tasks | List without the launch guard; recheck each chosen workspace right before deleting it |
|
|
100
|
+
| `D` in the cleanup list | jobs → tasks | Recheck, then force-delete |
|
|
101
|
+
| `sbx-tui --cleanup-workspaces` | tasks; jobs → tasks | As `W`, without the bookmark history |
|
|
102
|
+
|
|
103
|
+
- Checks that guard an action are repeated under its lock with records and
|
|
104
|
+
session files read again. The UI's cached state only decides what to offer.
|
|
105
|
+
- Inline sessions hold `jobs.lock` only while starting and finishing, not
|
|
106
|
+
while the agent runs.
|
|
107
|
+
- Readers take no lock. Files are replaced atomically, and a reload swaps the
|
|
108
|
+
in-memory records in one assignment, so readers see the old state or the new.
|
|
109
|
+
- Locks are `flock`s on separately opened files, so they also order the threads
|
|
110
|
+
of one TUI. They are not re-entrant: code holding one uses unlocked helpers
|
|
111
|
+
such as `JobManager._busy_reason`.
|
|
112
|
+
- Workers that change the repository are not `exclusive`. A second action waits
|
|
113
|
+
for `_history_lock` instead of cancelling the first after jj has already run.
|
|
114
|
+
- `last_used.json` and the trusted project config are replaced atomically
|
|
115
|
+
without a lock; the last write wins.
|
|
116
|
+
|
|
117
|
+
## Limitations
|
|
118
|
+
|
|
119
|
+
- Inline sessions, diffs and shell commands suspend the event loop;
|
|
120
|
+
notifications arrive on return.
|
|
121
|
+
- External launches, stale records and PID reuse after crashes may need
|
|
122
|
+
[manual recovery](sessions.md#interactive-sessions).
|
|
123
|
+
- Working-copy dirty markers, prompt files, remote bookmarks and switching
|
|
124
|
+
repositories are not implemented.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Workspace cleanup
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
sbx-tui --cleanup-workspaces --dry-run # preview
|
|
5
|
+
sbx-tui --cleanup-workspaces # remove eligible workspaces and merged bookmarks
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
Press `W` to review candidates: eligible rows are preselected; others show why
|
|
9
|
+
they are kept. `j` `k` move, `space` toggles a row, `a` selects all/none,
|
|
10
|
+
`enter` lists what goes and asks for `y`, and `esc` or `q` cancels. The CLI removes all eligible candidates, reporting removals and
|
|
11
|
+
reasons for keeping others.
|
|
12
|
+
|
|
13
|
+
Candidates come from `jj workspace list`, regardless of name or location. The
|
|
14
|
+
current workspace is excluded. Normal cleanup keeps `default` and workspaces
|
|
15
|
+
with running sessions or [`:*` tasks](usage.md#running-in-each-workspace),
|
|
16
|
+
uncommitted changes or files too large to snapshot; workspaces in use cannot
|
|
17
|
+
be force-removed either.
|
|
18
|
+
Directories containing another registered workspace are protected from both
|
|
19
|
+
normal and force removal; remove the nested workspace first and scan again.
|
|
20
|
+
Running sessions also protect enclosing and nested workspace directories.
|
|
21
|
+
|
|
22
|
+
## Merged work
|
|
23
|
+
|
|
24
|
+
The [main bookmark](configuration.md#settings-reference) defines merged work:
|
|
25
|
+
|
|
26
|
+
- Workspaces with only empty revisions outside `::main` can be removed, deleting
|
|
27
|
+
ignored files or unregistering missing directories. Their bookmarks are also
|
|
28
|
+
deleted if the bookmarks themselves are fully merged, except main. A
|
|
29
|
+
workspace and its namesake bookmark can point at different revisions.
|
|
30
|
+
- Fully merged bookmarks without workspaces appear as separate rows.
|
|
31
|
+
|
|
32
|
+
Unmerged workspaces with ignored files or missing directories are kept. Removal
|
|
33
|
+
checks each workspace again just before deleting it, then runs
|
|
34
|
+
`jj workspace forget` and deletes eligible bookmarks;
|
|
35
|
+
tracked remote bookmarks are deleted on the next `jj git push`. Job history and the
|
|
36
|
+
[bookmark history](usage.md#bookmark-history) stay. In the TUI, `u` restores the
|
|
37
|
+
deleted bookmarks; removed workspaces cannot be undone.
|
|
38
|
+
|
|
39
|
+
## Force delete
|
|
40
|
+
|
|
41
|
+
Highlight a kept workspace, press `D` and confirm to unregister it and delete
|
|
42
|
+
its directory, including uncommitted, untracked and ignored files. Bookmarks
|
|
43
|
+
stay. Running sessions and `default` cannot be force-deleted.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Workflow: run tests while keeping sbx-tui open
|
|
2
|
+
|
|
3
|
+
This example follows a Python project from a quick foreground check to a
|
|
4
|
+
repository background task, then tests two bookmarks in parallel. It uses
|
|
5
|
+
`feature/parser` and `fix/cache` as example bookmarks; substitute your own.
|
|
6
|
+
The illustrations use the actual TUI renderer with staged project data and
|
|
7
|
+
test results. They show the workflow, not results from this repository's tests.
|
|
8
|
+
|
|
9
|
+
## 1. Define one test command
|
|
10
|
+
|
|
11
|
+
From your project directory, configure a reusable command:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
sbx-tui config set --project commands.test 'uv run pytest -q'
|
|
15
|
+
sbx-tui
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This adds `test = "uv run pytest -q"` under `[commands]` in the project
|
|
19
|
+
configuration. The project must already have pytest available through uv.
|
|
20
|
+
These commands run on the host; they do not launch a sandbox or an agent.
|
|
21
|
+
|
|
22
|
+
You can run the same configured command three ways:
|
|
23
|
+
|
|
24
|
+
| Enter in sbx-tui | Where the tests start | What you can do meanwhile |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `:test` | Once in the repository root | Use the command's terminal; return when it exits |
|
|
27
|
+
| `:&test` | Once in the repository root, ignoring marks | Keep navigating sbx-tui |
|
|
28
|
+
| `:*test` | Once in each marked workspace, or the highlighted workspace if none are marked | Keep navigating sbx-tui |
|
|
29
|
+
|
|
30
|
+
The literal equivalents are `:!uv run pytest -q`, `:&!uv run pytest -q`,
|
|
31
|
+
and `:*!uv run pytest -q`. The prefix changes execution; the configured script
|
|
32
|
+
stays the same. See the [full comparison](usage.md#choosing-how-to-run-a-shell-command)
|
|
33
|
+
for environment variables and task persistence.
|
|
34
|
+
|
|
35
|
+
## 2. Do a quick foreground check
|
|
36
|
+
|
|
37
|
+
Press `:`, type `test`, and press `Enter`. The command takes over the terminal:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
── sbx-tui: :test → uv run pytest -q ──
|
|
41
|
+
|
|
42
|
+
.......................................... [100%]
|
|
43
|
+
42 passed in 3.12s
|
|
44
|
+
|
|
45
|
+
── exited with 0; press Enter to return to sbx-tui ──
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Press `Enter` to return. If the tests take longer than expected, `Ctrl+C`
|
|
49
|
+
interrupts them; press `Enter` at the exit prompt to return to the TUI.
|
|
50
|
+
An exit status of `-2` or `130` means the command was interrupted. This form
|
|
51
|
+
does not create a background task or a captured log in the task list.
|
|
52
|
+
|
|
53
|
+
## 3. Run once in the repository background
|
|
54
|
+
|
|
55
|
+
Use `j` / `k` to move to `feature/parser` and press `Space` to mark it.
|
|
56
|
+
Move to `fix/cache` and mark it too. `Space` marks a row and moves down;
|
|
57
|
+
move back to `feature/parser` to leave it highlighted.
|
|
58
|
+
|
|
59
|
+
The marks are deliberately left in place for the next command:
|
|
60
|
+
|
|
61
|
+

|
|
62
|
+
|
|
63
|
+
*Two rows are marked, but `:&test` will ignore them.*
|
|
64
|
+
|
|
65
|
+
Enter `:&test` to launch **one task in the repository root**, then press `T`
|
|
66
|
+
to open the task list:
|
|
67
|
+
|
|
68
|
+

|
|
69
|
+
|
|
70
|
+
*The task belongs to `@`, even though `feature/parser` was highlighted.*
|
|
71
|
+
|
|
72
|
+
The TUI remains usable. In the task list, select the task and press `Enter`
|
|
73
|
+
or `l` to follow its captured output; `q` returns to the task list, and another
|
|
74
|
+
`q` returns to the bookmark list. Let this run finish before the next step.
|
|
75
|
+
Its result remains available in `T`.
|
|
76
|
+
|
|
77
|
+
`:&test` does not create or enter the highlighted bookmark's workspace.
|
|
78
|
+
Use the `&` prefix as shown: a trailing shell `&` (`:!uv run pytest -q &`)
|
|
79
|
+
does not create a tracked sbx-tui task.
|
|
80
|
+
|
|
81
|
+
## 4. Run in both marked workspaces
|
|
82
|
+
|
|
83
|
+
Back on the bookmark list, keep `feature/parser` and `fix/cache` marked and
|
|
84
|
+
enter `:*test`. This starts two background tasks, each in its own workspace.
|
|
85
|
+
sbx-tui creates a missing workspace before launching its task. For Python
|
|
86
|
+
projects, uv prepares that workspace's environment as needed.
|
|
87
|
+
|
|
88
|
+
Press `T` again. In this example, the parser tests pass and the cache tests fail:
|
|
89
|
+
|
|
90
|
+

|
|
91
|
+
|
|
92
|
+
*Three records: the earlier repository run, plus one run for each marked bookmark.*
|
|
93
|
+
|
|
94
|
+
The separate results matter: a passing repository run says nothing about
|
|
95
|
+
changes that exist only in another bookmark's workspace. With no rows marked,
|
|
96
|
+
`:*test` runs only in the highlighted workspace. `Esc` on the bookmark list
|
|
97
|
+
clears marks when you want that single-workspace behavior.
|
|
98
|
+
|
|
99
|
+
## 5. Inspect the failure
|
|
100
|
+
|
|
101
|
+
In `T`, use `j` / `k` to select the failed `fix/cache` task, then press `Enter`:
|
|
102
|
+
|
|
103
|
+

|
|
104
|
+
|
|
105
|
+
*The log belongs to the selected task; the title identifies the bookmark and exit code.*
|
|
106
|
+
|
|
107
|
+
Use `j` / `k` to scroll, `gg` / `G` for the top or bottom, and `q` to go back.
|
|
108
|
+
For a running task, the log follows new output automatically.
|
|
109
|
+
|
|
110
|
+
To rerun just the cache tests after making a fix, return to the bookmark list,
|
|
111
|
+
press `Esc` to clear marks, highlight `fix/cache`, and enter `:*test` again.
|
|
112
|
+
Each invocation creates a new task record, so you can compare it with the
|
|
113
|
+
earlier failure in `T`.
|
|
114
|
+
|
|
115
|
+
## 6. Cancel or leave a task running
|
|
116
|
+
|
|
117
|
+
To stop a background task, open `T`, select its running row, and press `x`.
|
|
118
|
+
That targets just that task. If it does not stop, further presses escalate
|
|
119
|
+
from SIGINT to SIGTERM and SIGKILL. On a finished row, `x` forgets the task
|
|
120
|
+
and removes its captured output instead.
|
|
121
|
+
|
|
122
|
+
On the bookmark list, `x` acts on the target bookmarks and can affect several
|
|
123
|
+
running tasks or sandbox runs. Use the task list when you want to cancel only
|
|
124
|
+
one command.
|
|
125
|
+
|
|
126
|
+
Both `:&` and `:*` tasks keep running if you quit sbx-tui. Confirm the quit
|
|
127
|
+
prompt when tasks are still running; reopening sbx-tui in the same repository
|
|
128
|
+
lets you inspect their status and logs. Background commands capture output
|
|
129
|
+
and have no interactive input, so use the foreground form for commands that
|
|
130
|
+
need terminal interaction.
|
|
131
|
+
|
|
132
|
+
For the complete controls, see [Tasks](usage.md#tasks) and the
|
|
133
|
+
[log viewer](usage.md#log-viewer).
|