@doriandev/devcc 0.1.2 → 0.2.0
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.
- package/README.md +556 -386
- package/bin/devcc.mjs +50 -23
- package/dist/devcc/agents/agents.d.ts +112 -0
- package/dist/devcc/agents/agents.d.ts.map +1 -0
- package/dist/devcc/agents/agents.js +179 -0
- package/dist/devcc/agents/agents.js.map +1 -0
- package/dist/devcc/app/App.d.ts +15 -0
- package/dist/devcc/app/App.d.ts.map +1 -1
- package/dist/devcc/app/App.js +326 -17
- package/dist/devcc/app/App.js.map +1 -1
- package/dist/devcc/app/controlPlaneCommands.d.ts +14 -0
- package/dist/devcc/app/controlPlaneCommands.d.ts.map +1 -0
- package/dist/devcc/app/controlPlaneCommands.js +179 -0
- package/dist/devcc/app/controlPlaneCommands.js.map +1 -0
- package/dist/devcc/app/keymap.d.ts.map +1 -1
- package/dist/devcc/app/keymap.js +24 -3
- package/dist/devcc/app/keymap.js.map +1 -1
- package/dist/devcc/automation.d.ts +31 -0
- package/dist/devcc/automation.d.ts.map +1 -0
- package/dist/devcc/automation.js +202 -0
- package/dist/devcc/automation.js.map +1 -0
- package/dist/devcc/cli.d.ts.map +1 -1
- package/dist/devcc/cli.js +46 -3
- package/dist/devcc/cli.js.map +1 -1
- package/dist/devcc/components/Sidebar.d.ts.map +1 -1
- package/dist/devcc/components/Sidebar.js +1 -0
- package/dist/devcc/components/Sidebar.js.map +1 -1
- package/dist/devcc/components/primitives.d.ts.map +1 -1
- package/dist/devcc/components/primitives.js +3 -0
- package/dist/devcc/components/primitives.js.map +1 -1
- package/dist/devcc/core/commands.d.ts +25 -0
- package/dist/devcc/core/commands.d.ts.map +1 -1
- package/dist/devcc/core/commands.js.map +1 -1
- package/dist/devcc/core/config.d.ts +69 -3
- package/dist/devcc/core/config.d.ts.map +1 -1
- package/dist/devcc/core/config.js +185 -5
- package/dist/devcc/core/config.js.map +1 -1
- package/dist/devcc/core/state.d.ts +19 -1
- package/dist/devcc/core/state.d.ts.map +1 -1
- package/dist/devcc/core/state.js +6 -0
- package/dist/devcc/core/state.js.map +1 -1
- package/dist/devcc/environment/EnvironmentService.d.ts +58 -0
- package/dist/devcc/environment/EnvironmentService.d.ts.map +1 -0
- package/dist/devcc/environment/EnvironmentService.js +159 -0
- package/dist/devcc/environment/EnvironmentService.js.map +1 -0
- package/dist/devcc/environment/integrations.d.ts +52 -0
- package/dist/devcc/environment/integrations.d.ts.map +1 -0
- package/dist/devcc/environment/integrations.js +94 -0
- package/dist/devcc/environment/integrations.js.map +1 -0
- package/dist/devcc/environment/tools.d.ts +51 -0
- package/dist/devcc/environment/tools.d.ts.map +1 -0
- package/dist/devcc/environment/tools.js +99 -0
- package/dist/devcc/environment/tools.js.map +1 -0
- package/dist/devcc/git/GitService.d.ts +26 -0
- package/dist/devcc/git/GitService.d.ts.map +1 -1
- package/dist/devcc/git/GitService.js +71 -2
- package/dist/devcc/git/GitService.js.map +1 -1
- package/dist/devcc/git/worktrees.d.ts +88 -0
- package/dist/devcc/git/worktrees.d.ts.map +1 -0
- package/dist/devcc/git/worktrees.js +226 -0
- package/dist/devcc/git/worktrees.js.map +1 -0
- package/dist/devcc/index.d.ts +13 -3
- package/dist/devcc/index.d.ts.map +1 -1
- package/dist/devcc/index.js +11 -1
- package/dist/devcc/index.js.map +1 -1
- package/dist/devcc/platform/launch.d.ts +30 -0
- package/dist/devcc/platform/launch.d.ts.map +1 -0
- package/dist/devcc/platform/launch.js +93 -0
- package/dist/devcc/platform/launch.js.map +1 -0
- package/dist/devcc/services/ServiceManager.d.ts +10 -2
- package/dist/devcc/services/ServiceManager.d.ts.map +1 -1
- package/dist/devcc/services/ServiceManager.js +18 -4
- package/dist/devcc/services/ServiceManager.js.map +1 -1
- package/dist/devcc/services/ollama.d.ts +64 -5
- package/dist/devcc/services/ollama.d.ts.map +1 -1
- package/dist/devcc/services/ollama.js +118 -17
- package/dist/devcc/services/ollama.js.map +1 -1
- package/dist/devcc/services/remote.d.ts +90 -0
- package/dist/devcc/services/remote.d.ts.map +1 -0
- package/dist/devcc/services/remote.js +233 -0
- package/dist/devcc/services/remote.js.map +1 -0
- package/dist/devcc/services/types.d.ts +8 -1
- package/dist/devcc/services/types.d.ts.map +1 -1
- package/dist/devcc/services/types.js.map +1 -1
- package/dist/devcc/utils/exec.d.ts +6 -0
- package/dist/devcc/utils/exec.d.ts.map +1 -1
- package/dist/devcc/utils/exec.js +12 -2
- package/dist/devcc/utils/exec.js.map +1 -1
- package/dist/devcc/verify/verify.d.ts +107 -0
- package/dist/devcc/verify/verify.d.ts.map +1 -0
- package/dist/devcc/verify/verify.js +203 -0
- package/dist/devcc/verify/verify.js.map +1 -0
- package/dist/devcc/views/OverviewView.d.ts.map +1 -1
- package/dist/devcc/views/OverviewView.js +55 -1
- package/dist/devcc/views/OverviewView.js.map +1 -1
- package/dist/devcc/views/ServicesView.d.ts +2 -0
- package/dist/devcc/views/ServicesView.d.ts.map +1 -1
- package/dist/devcc/views/ServicesView.js +15 -0
- package/dist/devcc/views/ServicesView.js.map +1 -1
- package/dist/devcc/views/SystemView.d.ts +10 -1
- package/dist/devcc/views/SystemView.d.ts.map +1 -1
- package/dist/devcc/views/SystemView.js +127 -19
- package/dist/devcc/views/SystemView.js.map +1 -1
- package/dist/devcc/views/WorktreesView.d.ts +48 -0
- package/dist/devcc/views/WorktreesView.d.ts.map +1 -0
- package/dist/devcc/views/WorktreesView.js +235 -0
- package/dist/devcc/views/WorktreesView.js.map +1 -0
- package/package.json +1 -1
- package/src/devcc/agents/agents.ts +282 -0
- package/src/devcc/app/App.ts +376 -20
- package/src/devcc/app/controlPlaneCommands.ts +197 -0
- package/src/devcc/app/keymap.ts +24 -3
- package/src/devcc/automation.ts +242 -0
- package/src/devcc/cli.ts +45 -3
- package/src/devcc/components/Sidebar.ts +1 -0
- package/src/devcc/components/primitives.ts +3 -0
- package/src/devcc/core/commands.ts +26 -0
- package/src/devcc/core/config.ts +271 -11
- package/src/devcc/core/state.ts +25 -0
- package/src/devcc/environment/EnvironmentService.ts +215 -0
- package/src/devcc/environment/integrations.ts +133 -0
- package/src/devcc/environment/tools.ts +151 -0
- package/src/devcc/git/GitService.ts +104 -2
- package/src/devcc/git/worktrees.ts +256 -0
- package/src/devcc/index.ts +57 -2
- package/src/devcc/platform/launch.ts +125 -0
- package/src/devcc/services/ServiceManager.ts +15 -3
- package/src/devcc/services/ollama.ts +162 -19
- package/src/devcc/services/remote.ts +306 -0
- package/src/devcc/services/types.ts +9 -1
- package/src/devcc/utils/exec.ts +26 -2
- package/src/devcc/verify/verify.ts +316 -0
- package/src/devcc/views/OverviewView.ts +76 -1
- package/src/devcc/views/ServicesView.ts +17 -0
- package/src/devcc/views/SystemView.ts +144 -22
- package/src/devcc/views/WorktreesView.ts +270 -0
package/README.md
CHANGED
|
@@ -1,50 +1,75 @@
|
|
|
1
1
|
# Dev Command Center
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@doriandev/devcc)
|
|
4
|
+
[](LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
A single keyboard-driven screen that replaces the pile of terminal tabs you keep open while working on a
|
|
7
|
+
project. Built with [OpenTUI](https://opentui.com).
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
`git status`, another for `pnpm test`, another where you `docker ps` or check
|
|
10
|
-
whether Postgres is up, and a fifth for actually typing commands. State ends up
|
|
11
|
-
scattered: you cannot see at a glance whether the dev server is up, whether your
|
|
12
|
-
tree is dirty, or which of those tabs has the error in it.
|
|
9
|
+
## Quick start
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
- **Controls** - starts, stops and restarts the dev processes you declare in
|
|
20
|
-
`.devcc.ts`; runs your `package.json` scripts; stages, commits, pulls and
|
|
21
|
-
pushes.
|
|
22
|
-
- **Collects** - everything it launches streams into one log viewer, per source
|
|
23
|
-
or merged and searchable, so the error you are chasing is not in a tab you
|
|
24
|
-
have scrolled away from.
|
|
25
|
-
|
|
26
|
-
### More than a launcher
|
|
27
|
-
|
|
28
|
-
Three things it does that running the commands by hand does not:
|
|
29
|
-
|
|
30
|
-
- **Brings a stack up in order.** `dependsOn` plus a readiness check means `web`
|
|
31
|
-
waits until `api` is genuinely accepting connections, not merely spawned.
|
|
32
|
-
- **Recovers from crashes.** Opt-in automatic restarts with exponential backoff,
|
|
33
|
-
and it gives up rather than thrashing.
|
|
34
|
-
- **Holds several projects open at once.** Each keeps its processes alive in the
|
|
35
|
-
background while you flip between them with `ctrl+o`.
|
|
36
|
-
|
|
37
|
-
### Where the line is
|
|
38
|
-
|
|
39
|
-
It is not a terminal replacement - there is no shell in it, and it will not run
|
|
40
|
-
interactive commands. It is not a deploy tool or a container manager; it observes
|
|
41
|
-
Docker rather than replacing compose. And it deliberately will not touch
|
|
42
|
-
processes it did not start: anything you launched in another tab shows up as
|
|
43
|
-
detected and read-only.
|
|
11
|
+
```bash
|
|
12
|
+
npm i -g @doriandev/devcc # or: pnpm add -g @doriandev/devcc
|
|
13
|
+
cd ~/code/example.com
|
|
14
|
+
devcc
|
|
15
|
+
```
|
|
44
16
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
17
|
+
You need [Bun](https://bun.sh) 1.3+ installed (or Node 26.4+, see [Requirements](#requirements)). No
|
|
18
|
+
configuration is needed to get a useful screen. Press `?` inside the app for the manual.
|
|
19
|
+
|
|
20
|
+
In scripts, CI and AI coding agents, `devcc verify` is the project's quality gate: it runs your checks and
|
|
21
|
+
exits 0 only when all of them pass (see [Verification](#verification)).
|
|
22
|
+
|
|
23
|
+
## Contents
|
|
24
|
+
|
|
25
|
+
- [Why devcc](#why-devcc)
|
|
26
|
+
- [At a glance](#at-a-glance)
|
|
27
|
+
- [Requirements](#requirements)
|
|
28
|
+
- [Usage](#usage)
|
|
29
|
+
- [Worktrees and AI agents](#worktrees-and-ai-agents)
|
|
30
|
+
- [Verification](#verification)
|
|
31
|
+
- [Environment](#environment)
|
|
32
|
+
- [Remote machines](#remote-machines)
|
|
33
|
+
- [Configuration](#configuration)
|
|
34
|
+
- [Keyboard shortcuts](#keyboard-shortcuts)
|
|
35
|
+
- [Known limitations](#known-limitations)
|
|
36
|
+
- [Contributing](#contributing)
|
|
37
|
+
|
|
38
|
+
## Why devcc
|
|
39
|
+
|
|
40
|
+
Working on a real project normally means one tab running `next dev`, another for `git status`, another
|
|
41
|
+
for `pnpm test`, another where you `docker ps` or check whether Postgres is up, and a fifth for actually
|
|
42
|
+
typing commands. You cannot see at a glance whether the dev server is up, whether your tree is dirty, or
|
|
43
|
+
which tab has the error in it.
|
|
44
|
+
|
|
45
|
+
devcc puts that in one place:
|
|
46
|
+
|
|
47
|
+
- **Watches** git branch and changes, which services are actually listening (Docker, Postgres, Redis,
|
|
48
|
+
Ollama, a framework dev server), host CPU / memory / disk, and outdated dependencies.
|
|
49
|
+
- **Controls** the dev processes you declare in `.devcc.ts` (start, stop, restart), runs your
|
|
50
|
+
`package.json` scripts, and stages, commits, pulls and pushes.
|
|
51
|
+
- **Collects** everything it launches into one log viewer, per source or merged and searchable.
|
|
52
|
+
|
|
53
|
+
It also does things that running the commands by hand does not:
|
|
54
|
+
|
|
55
|
+
- **Brings a stack up in order.** `dependsOn` plus a readiness check means `web` waits until `api` is
|
|
56
|
+
genuinely accepting connections, not merely spawned.
|
|
57
|
+
- **Recovers from crashes.** Opt-in automatic restarts with exponential backoff, and it gives up rather
|
|
58
|
+
than thrashing.
|
|
59
|
+
- **Holds several projects open at once.** Each keeps its processes alive in the background while you flip
|
|
60
|
+
between them with `ctrl+o`.
|
|
61
|
+
- **Runs AI agents side by side.** Claude Code and Codex, each in its own git worktree, with their output
|
|
62
|
+
in the log viewer.
|
|
63
|
+
- **Gates the work.** `devcc verify` runs the same checks for you, for CI and for an agent, with an exit
|
|
64
|
+
code that means something.
|
|
65
|
+
|
|
66
|
+
**What it is not.** It is not a terminal replacement: there is no shell in it and it will not run
|
|
67
|
+
interactive commands. It is not a deploy tool or a container manager; it observes Docker rather than
|
|
68
|
+
replacing compose. It orchestrates git, mise, direnv, Claude Code, Codex, Tailscale, SSH and Ollama; it
|
|
69
|
+
replaces none of them. It will never touch a process it did not start.
|
|
70
|
+
|
|
71
|
+
It pays off on projects with more than one long-running process, or when you bounce between repositories.
|
|
72
|
+
For a single `next dev` you would barely notice the difference.
|
|
48
73
|
|
|
49
74
|
## At a glance
|
|
50
75
|
|
|
@@ -71,263 +96,244 @@ the difference - the payoff scales with how many moving parts your setup has.
|
|
|
71
96
|
|
|
72
97
|
## Requirements
|
|
73
98
|
|
|
74
|
-
OpenTUI renders through a native Zig core over FFI, so the dashboard needs one
|
|
75
|
-
of:
|
|
99
|
+
OpenTUI renders through a native Zig core over FFI, so the dashboard needs one of:
|
|
76
100
|
|
|
77
101
|
- **Bun 1.3.0 or newer** (recommended), or
|
|
78
102
|
- **Node.js 26.4.0 or newer**, started with `--experimental-ffi`
|
|
79
103
|
|
|
80
|
-
The `devcc` launcher
|
|
81
|
-
|
|
82
|
-
installation, linting, type checking and the unit tests all run on plain Node.
|
|
104
|
+
You do not have to pick. The `devcc` launcher runs on any Node ≥ 20 and re-execs into Bun, or into Node
|
|
105
|
+
with the right flag, automatically. If neither is available it says so and stops.
|
|
83
106
|
|
|
84
|
-
##
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
npm i -g @doriandev/devcc # or: pnpm add -g @doriandev/devcc
|
|
88
|
-
cd ~/code/example.com
|
|
89
|
-
devcc
|
|
90
|
-
```
|
|
107
|
+
## Usage
|
|
91
108
|
|
|
92
|
-
`devcc` runs against the project you are standing in. To point it somewhere else
|
|
93
|
-
without changing directory:
|
|
109
|
+
`devcc` runs against the project you are standing in. To point it somewhere else:
|
|
94
110
|
|
|
95
111
|
```bash
|
|
96
112
|
devcc -C ~/code/example.com
|
|
97
113
|
```
|
|
98
114
|
|
|
99
|
-
|
|
115
|
+
| Command | What it does |
|
|
116
|
+
| ------------------------------ | ------------------------------------------------------------- |
|
|
117
|
+
| `devcc` | Open the dashboard |
|
|
118
|
+
| `devcc verify [--json]` | Run the project's verification steps; exit 0 only if all pass |
|
|
119
|
+
| `devcc worktree list [--json]` | List git worktrees |
|
|
120
|
+
| `devcc env [--json]` | Report installed tools, mise runtimes and direnv status |
|
|
100
121
|
|
|
101
|
-
| Option | Description
|
|
102
|
-
| ------------------- |
|
|
103
|
-
| `-C`, `--cwd <dir>` | Project directory (default: nearest project root)
|
|
104
|
-
|
|
|
105
|
-
| `-
|
|
122
|
+
| Option | Description |
|
|
123
|
+
| ------------------- | ------------------------------------------------------ |
|
|
124
|
+
| `-C`, `--cwd <dir>` | Project directory (default: nearest project root) |
|
|
125
|
+
| `--json` | One JSON document on stdout, no colour or escape codes |
|
|
126
|
+
| `-h`, `--help` | Show usage |
|
|
127
|
+
| `-v`, `--version` | Show the version |
|
|
106
128
|
|
|
107
|
-
`
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
### Running from source
|
|
129
|
+
`verify`, `worktree list` and `env` do not load the dashboard, so they run on any Node ≥ 20 without Bun,
|
|
130
|
+
which is what you want in CI or inside an agent's sandbox.
|
|
111
131
|
|
|
112
132
|
```bash
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
node bin/devcc.mjs -C ~/code/example.com # or against another project
|
|
118
|
-
pnpm build # compile to dist/
|
|
133
|
+
devcc verify # human-readable, failing output shown inline
|
|
134
|
+
devcc verify --json | jq .summary # "4/5 passed, 1 failed (Tests)"
|
|
135
|
+
devcc worktree list --json | jq -r '.worktrees[].path'
|
|
136
|
+
devcc env # what is installed on this machine
|
|
119
137
|
```
|
|
120
138
|
|
|
121
|
-
|
|
139
|
+
Set `DEVCC_LOG_LEVEL` (`debug` \| `info` \| `warn` \| `error` \| `silent`) to control the internal
|
|
140
|
+
diagnostics log written to `.devcc/devcc.log`.
|
|
141
|
+
|
|
142
|
+
## Worktrees and AI agents
|
|
122
143
|
|
|
123
|
-
|
|
144
|
+
Press `w` (or `0`) for the Worktrees view: every git worktree of the repository, and below them the AI
|
|
145
|
+
agents devcc has started. The worktree devcc is running in is marked `●`.
|
|
124
146
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
147
|
+
```
|
|
148
|
+
WORKTREES
|
|
149
|
+
● main ~/code/devcc main
|
|
150
|
+
○ feature/ai-agents ~/code/devcc-feature-ai-agents 1 agent ▸
|
|
151
|
+
○ fix/process-restart ~/code/devcc-fix-process-restart
|
|
152
|
+
|
|
153
|
+
AI AGENTS
|
|
154
|
+
● running Claude Code feature/ai-agents Add retry to the process manager 2m 10s
|
|
155
|
+
○ stopped Codex fix/process-restart Find why restart loses the pid exit 0
|
|
156
|
+
```
|
|
130
157
|
|
|
131
|
-
|
|
158
|
+
**Worktrees.** `n` asks for a branch (created if it does not exist), a base (default: your current branch)
|
|
159
|
+
and a directory (default: `../<repo>-<branch>`), then runs `git worktree add`. `d` removes the selected
|
|
160
|
+
worktree after a confirmation, with plain `git worktree remove`: devcc never passes `--force`, so git
|
|
161
|
+
refuses when there are uncommitted or untracked changes, and devcc shows that refusal. It also refuses
|
|
162
|
+
to remove the main worktree, the one it is running in, or one where an agent is still working. `o` opens a
|
|
163
|
+
worktree as another project in devcc, `t` opens a terminal there, `e` opens your editor.
|
|
132
164
|
|
|
133
|
-
|
|
134
|
-
|
|
165
|
+
**AI agents** (optional: needs the `claude` or `codex` CLI). With a worktree selected, `c` launches Claude
|
|
166
|
+
Code and `x` launches Codex. devcc asks for the task and starts the agent with that worktree as its working
|
|
167
|
+
directory. Agents run non-interactively (`claude -p <task>`, `codex exec <task>`) as managed processes:
|
|
168
|
+
their output streams into the Logs view (`enter` on a session), `s` stops one, and quitting devcc stops
|
|
169
|
+
them all. For an interactive session, open a terminal in the worktree with `t` and run the agent there.
|
|
135
170
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
| Project root | Walks up from the working directory looking for `package.json` or `.git` |
|
|
139
|
-
| Package manager | `packageManager` field → lockfile → the manager that launched it → npm |
|
|
140
|
-
| Your scripts | Reads `scripts` from `package.json` |
|
|
141
|
-
| Git state | Runs `git status --porcelain=v2` directly - branch, staged/modified/untracked, ahead/behind, recent commits |
|
|
142
|
-
| Local services | TCP-probes Postgres, Redis and Ollama; queries the Docker daemon and any compose file; infers dev-server ports from your dependencies (`next` → 3000, `vite` → 5173, `typesense` → 8108) |
|
|
143
|
-
| Host metrics | Node's `os` and `fs` APIs - CPU, memory, disk, load, uptime |
|
|
144
|
-
| Tool versions | `git`, `docker` and your package manager, read once in the background |
|
|
171
|
+
devcc passes no permission flags of its own, so each agent runs with its own defaults, and headless Claude
|
|
172
|
+
Code cannot edit files by default. Grant what you are comfortable with in config:
|
|
145
173
|
|
|
146
|
-
|
|
147
|
-
|
|
174
|
+
```ts
|
|
175
|
+
agents: {
|
|
176
|
+
claude: { args: ["--permission-mode", "acceptEdits"] },
|
|
177
|
+
codex: { args: ["--sandbox", "workspace-write"] },
|
|
178
|
+
}
|
|
179
|
+
```
|
|
148
180
|
|
|
149
|
-
|
|
181
|
+
## Verification
|
|
150
182
|
|
|
151
|
-
|
|
152
|
-
it to own, that it should wait for port 3000 before calling it ready, or that it
|
|
153
|
-
is safe to restart. Nothing is launched or controlled until you declare it in
|
|
154
|
-
`.devcc.ts`:
|
|
183
|
+
Declare the checks that decide whether the project can ship:
|
|
155
184
|
|
|
156
185
|
```ts
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
processes: [
|
|
160
|
-
{
|
|
161
|
-
id: "web",
|
|
162
|
-
name: "Next.js",
|
|
163
|
-
command: "pnpm",
|
|
164
|
-
args: ["dev"],
|
|
165
|
-
url: "localhost:3000",
|
|
166
|
-
readiness: { port: 3000 }, // "running" means actually serving
|
|
167
|
-
restartOnCrash: true, // comes back after a crash, with backoff
|
|
168
|
-
},
|
|
169
|
-
],
|
|
170
|
-
commands: [
|
|
171
|
-
{
|
|
172
|
-
id: "migrate",
|
|
173
|
-
title: "Run migrations",
|
|
174
|
-
category: "Database",
|
|
175
|
-
command: "pnpm",
|
|
176
|
-
args: ["db:migrate"],
|
|
177
|
-
confirm: true, // asks first: it mutates the database
|
|
178
|
-
},
|
|
179
|
-
],
|
|
180
|
-
}
|
|
186
|
+
// command ids from `commands`, package.json script names, or inline steps
|
|
187
|
+
verify: ["format:check", "lint", "typecheck", "test", "build"],
|
|
181
188
|
```
|
|
182
189
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
started. The declared command appears in the palette (`/`) and asks before it
|
|
186
|
-
runs.
|
|
190
|
+
A name is looked up in `commands` first, then in `package.json` scripts, so you rarely need to repeat
|
|
191
|
+
anything. An inline step is `{ id, name?, command, args?, cwd? }`.
|
|
187
192
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
193
|
+
```
|
|
194
|
+
$ devcc verify
|
|
195
|
+
devcc verify · ~/code/shop · 3 step(s)
|
|
196
|
+
|
|
197
|
+
→ lint: pnpm run lint
|
|
198
|
+
✓ lint (2.7s)
|
|
199
|
+
→ test: pnpm run test
|
|
200
|
+
✗ test (1.7s · exit 1)
|
|
201
|
+
FAIL tests/cart.test.ts > applies the discount
|
|
202
|
+
AssertionError: expected 90 to be 81
|
|
203
|
+
→ build: pnpm run build
|
|
204
|
+
✓ build (6.0s)
|
|
205
|
+
|
|
206
|
+
✗ 2/3 passed, 1 failed (test) in 10.4s
|
|
207
|
+
```
|
|
191
208
|
|
|
192
|
-
|
|
209
|
+
Steps run one at a time, in order, and a failure does not stop the run, so one pass shows every problem.
|
|
210
|
+
Colour is switched off for the steps and escape codes are stripped from captured output.
|
|
193
211
|
|
|
194
|
-
|
|
212
|
+
| Exit code | Meaning |
|
|
213
|
+
| --------- | ------------------------------------------------------------------------- |
|
|
214
|
+
| `0` | Every step passed |
|
|
215
|
+
| `1` | At least one step failed (or was cancelled) |
|
|
216
|
+
| `2` | Nothing was verified: no steps configured, or a step name matches nothing |
|
|
195
217
|
|
|
196
|
-
|
|
218
|
+
A missing configuration is an error on purpose: a gate that passes because it checked nothing is worse
|
|
219
|
+
than no gate. That makes a line like this safe to put in an agent's instructions:
|
|
197
220
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
| `←` / `h`, `→` | Previous / next section |
|
|
202
|
-
| `tab`, `shift+tab` | Cycle sections |
|
|
203
|
-
| `1`…`9` | Jump straight to a section |
|
|
204
|
-
| `p`, `g`, `l`, `d` | Processes, Git, Logs, Dependencies |
|
|
205
|
-
| `enter` | Open / activate the selection |
|
|
206
|
-
| `esc` | Close an overlay |
|
|
207
|
-
| `/` | Command palette |
|
|
208
|
-
| `ctrl+p` | Open another project (others keep running) |
|
|
209
|
-
| `ctrl+o` | Cycle through open projects |
|
|
210
|
-
| `?` / `F1` / `ctrl+k` | The in-app manual (also "Open the manual" in the palette) |
|
|
211
|
-
| `shift+R` | Refresh everything |
|
|
212
|
-
| `q` or `ctrl+c` | Quit (stops the processes devcc started) |
|
|
221
|
+
```text
|
|
222
|
+
Do not finish until `devcc verify` succeeds.
|
|
223
|
+
```
|
|
213
224
|
|
|
214
|
-
|
|
225
|
+
In the dashboard, `shift+V` (or "Project: verify" in the palette) runs the same steps as managed tasks:
|
|
226
|
+
progress shows on the Overview, and each step's output has its own log source.
|
|
215
227
|
|
|
216
|
-
|
|
217
|
-
| ------- | --------------------------------- |
|
|
218
|
-
| `s` | Start or stop the selection |
|
|
219
|
-
| `r` | Restart the selection |
|
|
220
|
-
| `i` | Inspect the selection |
|
|
221
|
-
| `enter` | Jump to that process's logs |
|
|
222
|
-
| `d` | `docker compose down` (confirmed) |
|
|
228
|
+
## Environment
|
|
223
229
|
|
|
224
|
-
|
|
230
|
+
The System view (`8`) starts with the environment, detected in the background when a project opens and
|
|
231
|
+
re-checked with `r`. `devcc env` prints the same report.
|
|
225
232
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
233
|
+
- **Tools**: Node, pnpm, npm, Bun, Git, mise, direnv, Docker, Rust/Cargo, Claude Code, Codex, Tailscale and
|
|
234
|
+
Ollama, each with its version, or "not installed". Missing tools are normal. Every probe has a 3-second
|
|
235
|
+
timeout, so a hung executable shows as a warning instead of freezing anything.
|
|
236
|
+
- **mise** (optional). When mise is installed, the runtimes pinned for this project, read from
|
|
237
|
+
`mise ls --current --json`. An untrusted `mise.toml` is reported, never trusted automatically.
|
|
238
|
+
- **direnv** (optional). Whether the project has an `.envrc`, whether it is allowed, and whether devcc
|
|
239
|
+
itself was started with it loaded. "Environment: direnv allow" in the palette runs `direnv allow` after
|
|
240
|
+
a confirmation; devcc never approves an `.envrc` on its own.
|
|
231
241
|
|
|
232
|
-
|
|
242
|
+
Anything that needs attention is summarised in one line on the Overview.
|
|
233
243
|
|
|
234
|
-
|
|
235
|
-
| --------------- | --------------------------------- |
|
|
236
|
-
| `s` / `u` | Stage / unstage the selected file |
|
|
237
|
-
| `d` | Discard file changes (confirmed) |
|
|
238
|
-
| `c` | Commit staged changes |
|
|
239
|
-
| `p` / `shift+P` | Pull / push (push is confirmed) |
|
|
240
|
-
| `f` | Fetch |
|
|
241
|
-
| `r` | Refresh status |
|
|
244
|
+
## Remote machines
|
|
242
245
|
|
|
243
|
-
|
|
246
|
+
Declare the machines you develop on. `host` can be an alias from `~/.ssh/config`: devcc resolves it with
|
|
247
|
+
`ssh -G`, so your existing SSH settings apply, and it never stores users, keys or addresses of its own.
|
|
244
248
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
+
```ts
|
|
250
|
+
remoteMachines: [
|
|
251
|
+
{ id: "gpu", name: "GPU Server", host: "gpu-server", transport: "ssh", services: ["gpu-ollama"] },
|
|
252
|
+
],
|
|
253
|
+
```
|
|
249
254
|
|
|
250
|
-
|
|
255
|
+
Each machine appears under REMOTE in the Services view: whether its SSH port answers and how quickly,
|
|
256
|
+
whether it is online in your tailnet, and the status of its associated services. `o` on a machine opens an
|
|
257
|
+
SSH session in a terminal. Reachability is a TCP connection to the SSH port, made at most once a minute
|
|
258
|
+
while the project is open, plus whenever you refresh.
|
|
251
259
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
**Tailscale** (optional). When the `tailscale` CLI is available (including the macOS app's bundled one),
|
|
261
|
+
devcc reads `tailscale status --json` every 30 seconds: connected or not, this node's name, and which of
|
|
262
|
+
your remote machines are online.
|
|
263
|
+
|
|
264
|
+
**Remote Ollama** (optional). Point a service entry at any Ollama server, whether on localhost, a LAN
|
|
265
|
+
hostname or a Tailscale name:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
services: [{ id: "gpu-ollama", type: "ollama", baseUrl: "http://gpu-server:11434" }],
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The entry lists the server's models with parameter size, quantisation and disk size, marks the ones
|
|
272
|
+
currently loaded (with VRAM use) from `/api/ps`, and says "unreachable" when the server is down. Remote
|
|
273
|
+
endpoints are checked at most once a minute. The local Ollama on port 11434 (or `OLLAMA_HOST`) is still
|
|
274
|
+
detected automatically.
|
|
264
275
|
|
|
265
276
|
## Configuration
|
|
266
277
|
|
|
267
|
-
|
|
268
|
-
package manager, reads `package.json` scripts, shows git status, probes for
|
|
269
|
-
local services, and monitors the host. A config file adds processes devcc can
|
|
270
|
-
control and project-specific commands.
|
|
278
|
+
### Without a config file
|
|
271
279
|
|
|
272
|
-
devcc
|
|
273
|
-
|
|
274
|
-
`devcc.config.mjs`, `.devcc.json` in the project root. TypeScript config files
|
|
275
|
-
are transpiled on the fly, so no build step is needed.
|
|
280
|
+
Run `devcc` in a project with no configuration and it detects all of this, so Overview, Scripts, Git,
|
|
281
|
+
Services and System are populated on first launch:
|
|
276
282
|
|
|
277
|
-
|
|
283
|
+
| Detects | How |
|
|
284
|
+
| --------------- | -------------------------------------------------------------------------------------------- |
|
|
285
|
+
| Project root | Walks up from the working directory looking for `package.json` or `.git` |
|
|
286
|
+
| Package manager | `packageManager` field → lockfile → the manager that launched it → npm |
|
|
287
|
+
| Scripts | `scripts` in `package.json` |
|
|
288
|
+
| Git state | `git status --porcelain=v2`: branch, staged/modified/untracked, ahead/behind, recent commits |
|
|
289
|
+
| Local services | TCP probes for Postgres, Redis and Ollama; the Docker daemon and any compose file |
|
|
290
|
+
| Dev servers | Ports inferred from your dependencies (`next` → 3000, `vite` → 5173, `typesense` → 8108) |
|
|
291
|
+
| Host metrics | Node's `os` and `fs` APIs: CPU, memory, disk, load, uptime |
|
|
292
|
+
| Tool versions | `git`, `docker` and your package manager, read once in the background |
|
|
278
293
|
|
|
279
|
-
|
|
280
|
-
import { defineConfig } from "./src/devcc/index.js"
|
|
294
|
+
### Adding processes and commands
|
|
281
295
|
|
|
282
|
-
|
|
283
|
-
|
|
296
|
+
devcc cannot guess that `pnpm dev` is the thing you want it to own, that it should wait for port 3000
|
|
297
|
+
before calling it ready, or that it is safe to restart. Nothing is launched or controlled until you declare
|
|
298
|
+
it in `.devcc.ts` in the project root:
|
|
284
299
|
|
|
300
|
+
```ts
|
|
301
|
+
// ~/code/example.com/.devcc.ts
|
|
302
|
+
export default {
|
|
285
303
|
processes: [
|
|
286
304
|
{
|
|
287
|
-
id: "
|
|
288
|
-
name: "
|
|
289
|
-
command: "pnpm",
|
|
290
|
-
args: ["exec", "tsc", "-p", "tsconfig.json", "--noEmit", "--watch", "--pretty", "false"],
|
|
291
|
-
kind: "service",
|
|
292
|
-
},
|
|
293
|
-
{
|
|
294
|
-
id: "vitest",
|
|
295
|
-
name: "vitest --watch",
|
|
305
|
+
id: "web",
|
|
306
|
+
name: "Next.js",
|
|
296
307
|
command: "pnpm",
|
|
297
|
-
args: ["
|
|
298
|
-
|
|
308
|
+
args: ["dev"],
|
|
309
|
+
url: "localhost:3000",
|
|
310
|
+
readiness: { port: 3000 }, // "running" means actually serving
|
|
311
|
+
restartOnCrash: true, // comes back after a crash, with backoff
|
|
299
312
|
},
|
|
300
313
|
],
|
|
301
|
-
|
|
302
314
|
commands: [
|
|
303
315
|
{
|
|
304
|
-
id: "
|
|
305
|
-
title: "
|
|
306
|
-
category: "
|
|
316
|
+
id: "migrate",
|
|
317
|
+
title: "Run migrations",
|
|
318
|
+
category: "Database",
|
|
307
319
|
command: "pnpm",
|
|
308
|
-
args: ["
|
|
309
|
-
|
|
310
|
-
},
|
|
311
|
-
{
|
|
312
|
-
id: "clean",
|
|
313
|
-
title: "Remove dist/",
|
|
314
|
-
category: "Project",
|
|
315
|
-
command: "rm",
|
|
316
|
-
args: ["-rf", "dist"],
|
|
317
|
-
confirm: true,
|
|
320
|
+
args: ["db:migrate"],
|
|
321
|
+
confirm: true, // asks first: it mutates the database
|
|
318
322
|
},
|
|
319
323
|
],
|
|
320
|
-
|
|
321
|
-
refresh: { system: 1_500, services: 5_000, git: 8_000 },
|
|
322
|
-
})
|
|
324
|
+
}
|
|
323
325
|
```
|
|
324
326
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
327
|
+
Restart devcc and the Processes pane is live: `s` starts or stops, `r` restarts, `enter` jumps to that
|
|
328
|
+
process's logs, and quitting stops everything devcc started. The declared command appears in the palette
|
|
329
|
+
(`/`) and asks before it runs.
|
|
330
|
+
|
|
331
|
+
**Type-checked config.** If the project has `@doriandev/devcc` installed as a dependency, wrap the config
|
|
332
|
+
in `defineConfig` for editor autocompletion. The shape is the same and it is validated either way. A
|
|
333
|
+
monorepo example:
|
|
328
334
|
|
|
329
335
|
```ts
|
|
330
|
-
import { defineConfig } from "
|
|
336
|
+
import { defineConfig } from "@doriandev/devcc"
|
|
331
337
|
|
|
332
338
|
export default defineConfig({
|
|
333
339
|
processes: [
|
|
@@ -362,44 +368,43 @@ export default defineConfig({
|
|
|
362
368
|
})
|
|
363
369
|
```
|
|
364
370
|
|
|
365
|
-
|
|
371
|
+
**File names.** devcc uses the first of `.devcc.ts`, `devcc.config.ts`, `.devcc.mts`, `devcc.config.mts`,
|
|
372
|
+
`.devcc.js`, `devcc.config.js`, `.devcc.mjs`, `devcc.config.mjs` or `.devcc.json` in the project root.
|
|
373
|
+
TypeScript is transpiled on the fly, so no build step is needed.
|
|
366
374
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
export default {
|
|
370
|
-
processes: [{ id: "web", name: "Web", command: "pnpm", args: ["dev"], port: 3000 }],
|
|
371
|
-
}
|
|
372
|
-
```
|
|
375
|
+
**Mistakes are safe.** Invalid entries are reported in **Settings → Config issues** and skipped; a typo
|
|
376
|
+
never stops the dashboard from starting.
|
|
373
377
|
|
|
374
|
-
### Process
|
|
378
|
+
### Process fields
|
|
375
379
|
|
|
376
380
|
| Field | Type | Description |
|
|
377
381
|
| ------------------- | ----------------------- | ------------------------------------------------------------------- |
|
|
378
|
-
| `id` | `string` | Unique id
|
|
379
|
-
| `
|
|
380
|
-
| `command` | `string` | Executable. **Required** - never a shell string |
|
|
382
|
+
| `id` | `string` | **Required.** Unique id, also used as the log source id |
|
|
383
|
+
| `command` | `string` | **Required.** Executable, never a shell string |
|
|
381
384
|
| `args` | `string[]` | Arguments passed as an array, so quoting is never an issue |
|
|
385
|
+
| `name` | `string` | Display name (defaults to `id`) |
|
|
382
386
|
| `cwd` | `string` | Absolute, or relative to the project root |
|
|
383
387
|
| `env` | `Record<string,string>` | Extra environment variables |
|
|
384
388
|
| `kind` | `"service" \| "task"` | Long-running vs. expected to exit (default `service`) |
|
|
385
389
|
| `autoStart` | `boolean` | Start with the dashboard (default `false`) |
|
|
386
390
|
| `url` | `string` | Informational address shown next to the process |
|
|
387
391
|
| `port` | `number` | Port to associate with the process |
|
|
392
|
+
| `dependsOn` | `string[]` | Ids that must be _ready_ before this one starts |
|
|
393
|
+
| `readiness` | `object` | How devcc decides the process is usable, see below |
|
|
388
394
|
| `killGraceMs` | `number` | SIGTERM → SIGKILL grace period (default 5000) |
|
|
389
395
|
| `restartOnCrash` | `boolean` | Bring it back automatically after a non-zero exit (default `false`) |
|
|
390
396
|
| `maxCrashRestarts` | `number` | Give up after this many consecutive crashes (default 5) |
|
|
391
397
|
| `crashBackoffMs` | `number` | First backoff delay, doubling per crash (default 1000) |
|
|
392
398
|
| `maxCrashBackoffMs` | `number` | Backoff ceiling (default 30000) |
|
|
393
399
|
| `healthyAfterMs` | `number` | A run lasting this long resets the crash budget (default 30000) |
|
|
394
|
-
|
|
395
|
-
|
|
400
|
+
|
|
401
|
+
A manual stop cancels any pending crash restart and resets the budget.
|
|
396
402
|
|
|
397
403
|
### Readiness checks
|
|
398
404
|
|
|
399
|
-
Without a readiness block a process is `running` the moment it has spawned.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
layer is actually usable.
|
|
405
|
+
Without a readiness block a process is `running` the moment it has spawned. With one, it stays `starting`
|
|
406
|
+
until the check passes, and `dependsOn` waits for that. So "start all" brings a stack up in the right
|
|
407
|
+
order, and only once each layer is actually usable.
|
|
403
408
|
|
|
404
409
|
```ts
|
|
405
410
|
{
|
|
@@ -423,25 +428,86 @@ layer is actually usable.
|
|
|
423
428
|
| `timeoutMs` | Give up waiting and report `running` anyway, with a warning (default 30000) |
|
|
424
429
|
| `intervalMs` | Port probe interval (default 250) |
|
|
425
430
|
|
|
426
|
-
Either `port` or `logPattern` is required; both may be given. A dependency
|
|
427
|
-
|
|
428
|
-
|
|
431
|
+
Either `port` or `logPattern` is required; both may be given. A dependency cycle is reported and those
|
|
432
|
+
processes are left alone rather than started in an arbitrary order.
|
|
433
|
+
|
|
434
|
+
### Command fields
|
|
435
|
+
|
|
436
|
+
| Field | Type | Description |
|
|
437
|
+
| ------------------------ | ------------------------------- | ------------------------------------------- |
|
|
438
|
+
| `id` | `string` | **Required.** Unique id |
|
|
439
|
+
| `command`, `args`, `cwd` | | As for processes. **`command` is required** |
|
|
440
|
+
| `title` | `string` | Palette entry text |
|
|
441
|
+
| `category` | `string` | Palette grouping (default `Project`) |
|
|
442
|
+
| `keywords` | `string[]` | Extra fuzzy-search terms |
|
|
443
|
+
| `confirm` | `boolean \| { title, message }` | Ask before running |
|
|
444
|
+
|
|
445
|
+
### Service fields
|
|
446
|
+
|
|
447
|
+
Extra ports to watch: `{ id, name, port, host?, healthPath?, category? }`. `healthPath` upgrades the check
|
|
448
|
+
from a TCP probe to an HTTP request. An Ollama server is `{ id, name?, type: "ollama", baseUrl }`; see
|
|
449
|
+
[Remote machines](#remote-machines).
|
|
429
450
|
|
|
430
|
-
###
|
|
451
|
+
### Control-plane fields
|
|
431
452
|
|
|
432
|
-
|
|
433
|
-
| ------------------------ | ------------------------------- | ---------------------------------------- |
|
|
434
|
-
| `id` | `string` | Unique id. **Required** |
|
|
435
|
-
| `title` | `string` | Palette entry text |
|
|
436
|
-
| `category` | `string` | Palette grouping (default `Project`) |
|
|
437
|
-
| `command`, `args`, `cwd` | | As for processes. **`command` required** |
|
|
438
|
-
| `keywords` | `string[]` | Extra fuzzy-search terms |
|
|
439
|
-
| `confirm` | `boolean \| { title, message }` | Ask before running |
|
|
453
|
+
All optional. Nothing here is required for devcc to work.
|
|
440
454
|
|
|
441
|
-
|
|
455
|
+
| Field | Description |
|
|
456
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
457
|
+
| `verify` | Steps for `devcc verify`: command ids, script names, or `{ id, name?, command, args?, cwd? }` |
|
|
458
|
+
| `agents` | Per agent id: `{ command?, args?, name?, enabled? }`. `claude` and `codex` are built in; any other key adds an agent whose task is the last argument |
|
|
459
|
+
| `worktrees.directory` | Where new worktrees go, relative to the main worktree (default `..`) |
|
|
460
|
+
| `editor` | `{ command: "zed" }` (also `code`, `cursor`, `webstorm`, with optional `args`) or, on macOS, `{ application: "WebStorm" }` |
|
|
461
|
+
| `terminal` | macOS default: Terminal. `{ application: "iTerm" }`, or `{ command, args, execArgs }` where `{path}` in `args` is the directory and `execArgs` makes it run a program (used for SSH) |
|
|
462
|
+
| `remoteMachines` | `{ id, name?, host, transport: "ssh", port?, user?, services? }` |
|
|
463
|
+
|
|
464
|
+
### A full example
|
|
465
|
+
|
|
466
|
+
A Next.js app on pnpm, with Claude Code and Codex working in worktrees, models served from a GPU box, and a
|
|
467
|
+
verification gate:
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
// ~/code/shop/.devcc.ts
|
|
471
|
+
import { defineConfig } from "@doriandev/devcc"
|
|
472
|
+
|
|
473
|
+
export default defineConfig({
|
|
474
|
+
name: "shop",
|
|
475
|
+
editor: { command: "zed" },
|
|
476
|
+
|
|
477
|
+
processes: [
|
|
478
|
+
{
|
|
479
|
+
id: "web",
|
|
480
|
+
name: "Next.js",
|
|
481
|
+
command: "pnpm",
|
|
482
|
+
args: ["dev"],
|
|
483
|
+
url: "localhost:3000",
|
|
484
|
+
readiness: { port: 3000 },
|
|
485
|
+
restartOnCrash: true,
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
|
|
489
|
+
agents: {
|
|
490
|
+
claude: { args: ["--permission-mode", "acceptEdits"] },
|
|
491
|
+
codex: { args: ["--sandbox", "workspace-write"] },
|
|
492
|
+
},
|
|
493
|
+
worktrees: { directory: "../shop-worktrees" },
|
|
494
|
+
|
|
495
|
+
verify: [
|
|
496
|
+
"lint",
|
|
497
|
+
"typecheck",
|
|
498
|
+
"test",
|
|
499
|
+
{ id: "build", name: "Next.js build", command: "pnpm", args: ["build"] },
|
|
500
|
+
],
|
|
501
|
+
|
|
502
|
+
services: [{ id: "gpu-ollama", type: "ollama", baseUrl: "http://gpu-server:11434" }],
|
|
503
|
+
remoteMachines: [
|
|
504
|
+
{ id: "gpu", name: "RTX 3090 Server", host: "gpu-server", transport: "ssh", services: ["gpu-ollama"] },
|
|
505
|
+
],
|
|
506
|
+
})
|
|
507
|
+
```
|
|
442
508
|
|
|
443
|
-
|
|
444
|
-
`
|
|
509
|
+
Open it with `devcc`, press `w`, then `n` to create `feature/checkout` and `c` to give Claude a task in it.
|
|
510
|
+
Press `shift+V` before you merge; the agent can run `devcc verify` itself.
|
|
445
511
|
|
|
446
512
|
### Other options
|
|
447
513
|
|
|
@@ -454,19 +520,167 @@ Extra ports to watch: `{ id, name, port, host?, healthPath?, category? }`.
|
|
|
454
520
|
| `projects` | Extra project paths offered in the switcher (absolute or relative) |
|
|
455
521
|
| `projectSearchDirs` | Extra directories to scan for sibling projects |
|
|
456
522
|
|
|
457
|
-
|
|
458
|
-
|
|
523
|
+
## Keyboard shortcuts
|
|
524
|
+
|
|
525
|
+
You do not need to memorise these: press `?`, `F1` or `ctrl+k` in the app for the manual, or `/` to search
|
|
526
|
+
every action in the command palette. The footer shows key hints for the current view.
|
|
527
|
+
|
|
528
|
+
### Global
|
|
529
|
+
|
|
530
|
+
| Key | Action |
|
|
531
|
+
| ----------------------- | --------------------------------------------- |
|
|
532
|
+
| `↑` / `k`, `↓` / `j` | Previous / next item |
|
|
533
|
+
| `←` / `h`, `→` | Previous / next section |
|
|
534
|
+
| `tab`, `shift+tab` | Cycle sections |
|
|
535
|
+
| `1`…`9`, `0` | Jump straight to a section (`0` is Worktrees) |
|
|
536
|
+
| `p`, `g`, `w`, `l`, `d` | Processes, Git, Worktrees, Logs, Dependencies |
|
|
537
|
+
| `enter` | Open / activate the selection |
|
|
538
|
+
| `esc` | Close an overlay |
|
|
539
|
+
| `/` | Command palette |
|
|
540
|
+
| `ctrl+p` | Open another project (others keep running) |
|
|
541
|
+
| `ctrl+o` | Cycle through open projects |
|
|
542
|
+
| `?` / `F1` / `ctrl+k` | The in-app manual |
|
|
543
|
+
| `shift+R` | Refresh everything |
|
|
544
|
+
| `shift+V` | Verify the project |
|
|
545
|
+
| `q` or `ctrl+c` | Quit (stops the processes devcc started) |
|
|
546
|
+
|
|
547
|
+
### Processes and Services
|
|
548
|
+
|
|
549
|
+
| Key | Action |
|
|
550
|
+
| ------- | ---------------------------------- |
|
|
551
|
+
| `s` | Start or stop the selection |
|
|
552
|
+
| `r` | Restart the selection |
|
|
553
|
+
| `i` | Inspect the selection |
|
|
554
|
+
| `enter` | Jump to that process's logs |
|
|
555
|
+
| `d` | `docker compose down` (confirmed) |
|
|
556
|
+
| `o` | SSH to the selected remote machine |
|
|
557
|
+
|
|
558
|
+
### Worktrees
|
|
559
|
+
|
|
560
|
+
| Key | Action |
|
|
561
|
+
| ----------- | ------------------------------------------------------ |
|
|
562
|
+
| `n` | New worktree (branch, base, directory) |
|
|
563
|
+
| `d` | Remove the worktree (confirmed, never forced) |
|
|
564
|
+
| `o` / enter | Open the worktree in devcc; on an agent, its logs |
|
|
565
|
+
| `t` / `e` | Open a terminal / your editor in the worktree |
|
|
566
|
+
| `c` / `x` | Launch Claude Code / Codex with a task in the worktree |
|
|
567
|
+
| `s` | Stop the selected agent |
|
|
568
|
+
| `r` | Refresh the list |
|
|
569
|
+
|
|
570
|
+
### Scripts
|
|
571
|
+
|
|
572
|
+
| Key | Action |
|
|
573
|
+
| --------- | ---------------------------------- |
|
|
574
|
+
| `enter` | Run the selected script |
|
|
575
|
+
| `x` | Stop a running script |
|
|
576
|
+
| `f` / `c` | Filter the list / clear the filter |
|
|
577
|
+
|
|
578
|
+
### Git
|
|
579
|
+
|
|
580
|
+
| Key | Action |
|
|
581
|
+
| --------------- | --------------------------------- |
|
|
582
|
+
| `s` / `u` | Stage / unstage the selected file |
|
|
583
|
+
| `c` | Commit staged changes |
|
|
584
|
+
| `f` | Fetch |
|
|
585
|
+
| `p` / `shift+P` | Pull / push (push is confirmed) |
|
|
586
|
+
| `d` | Discard file changes (confirmed) |
|
|
587
|
+
| `r` | Refresh status |
|
|
588
|
+
|
|
589
|
+
### Logs
|
|
590
|
+
|
|
591
|
+
| Key | Action |
|
|
592
|
+
| ---------------- | -------------------------------------------------- |
|
|
593
|
+
| `j` / `k` | Scroll |
|
|
594
|
+
| `PgUp` / `PgDn` | Page |
|
|
595
|
+
| `home` / `end` | Jump to start / end |
|
|
596
|
+
| `f` | Toggle follow |
|
|
597
|
+
| `s` | Search: plain text, or a regex if it parses |
|
|
598
|
+
| `x` | Clear the search |
|
|
599
|
+
| `e` | Toggle errors only (stderr) |
|
|
600
|
+
| `a` | Merged stream of every source, colour-keyed |
|
|
601
|
+
| `tab`, `←` / `→` | Change log source |
|
|
602
|
+
| `c` | Clear the current buffer (all, in the merged view) |
|
|
603
|
+
|
|
604
|
+
### Dependencies
|
|
459
605
|
|
|
460
|
-
|
|
606
|
+
| Key | Action |
|
|
607
|
+
| --- | --------------------------------------- |
|
|
608
|
+
| `r` | Check the registry for updates |
|
|
609
|
+
| `u` | Update the selected package (confirmed) |
|
|
610
|
+
|
|
611
|
+
## Known limitations
|
|
612
|
+
|
|
613
|
+
- **Runtime.** The dashboard needs Bun, or Node ≥ 26.4 with `--experimental-ffi`. This is an OpenTUI/FFI
|
|
614
|
+
constraint, not a design choice.
|
|
615
|
+
- **Services.** Adapters ship for Docker Compose, PostgreSQL, Redis, Ollama (local or remote), Tailscale,
|
|
616
|
+
SSH machines and HTTP/port services. Others (Kubernetes, Vercel, Azure, …) are designed for but not
|
|
617
|
+
implemented.
|
|
618
|
+
- **Agents run headless.** Claude Code and Codex are started in their non-interactive modes with a task;
|
|
619
|
+
devcc cannot host their interactive interfaces. Use `t` for a terminal in the worktree instead.
|
|
620
|
+
- **Opening things outside devcc.** On macOS, terminals, editors and SSH use `open`. Elsewhere, set
|
|
621
|
+
`editor.command` and `terminal.command` (with `execArgs` for SSH).
|
|
622
|
+
- **direnv "loaded"** describes devcc's own environment (whether you started it from a direnv-enabled
|
|
623
|
+
shell), not your other terminals.
|
|
624
|
+
- **Remote reachability** is a TCP connection to the SSH port. It does not log in, so a machine that
|
|
625
|
+
accepts connections but rejects your key still shows as reachable.
|
|
626
|
+
- **Git.** The panel covers status, staging, discard, commit, fetch, pull and push. Branch switching,
|
|
627
|
+
rebasing, merge-conflict resolution and diff viewing are not implemented.
|
|
628
|
+
- **Detected services are read-only.** devcc will not start or stop something it did not launch, apart
|
|
629
|
+
from Docker Compose. `docker compose down --volumes` is deliberately not bound to a key.
|
|
630
|
+
- **Mouse.** Whatever OpenTUI provides by default; the interface is designed and tested for the keyboard.
|
|
631
|
+
- **Small terminals.** Below roughly 60 columns the sidebar hides and columns are trimmed. The layout
|
|
632
|
+
degrades rather than breaking, but it is cramped.
|
|
633
|
+
|
|
634
|
+
## Contributing
|
|
635
|
+
|
|
636
|
+
### Running from source
|
|
637
|
+
|
|
638
|
+
```bash
|
|
639
|
+
git clone https://github.com/dordevgit/devcc.git
|
|
640
|
+
cd devcc
|
|
641
|
+
pnpm install
|
|
642
|
+
pnpm dev:center # launch against this repo
|
|
643
|
+
node bin/devcc.mjs -C ~/code/example.com # or against another project
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
This repository's own [`.devcc.ts`](.devcc.ts) is a working config example.
|
|
647
|
+
|
|
648
|
+
### Scripts
|
|
649
|
+
|
|
650
|
+
```bash
|
|
651
|
+
pnpm lint # eslint (flat config, type-checked rules)
|
|
652
|
+
pnpm typecheck # tsc --noEmit, strict
|
|
653
|
+
pnpm test # vitest: core logic, no terminal required
|
|
654
|
+
pnpm test:tui # end-to-end smoke test against OpenTUI's headless renderer (Bun only)
|
|
655
|
+
pnpm build # tsc -> dist/
|
|
656
|
+
pnpm verify # lint + typecheck + test + build
|
|
657
|
+
pnpm format # prettier
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
`pnpm test` covers the non-UI core: package-manager detection, config parsing and validation, the command
|
|
661
|
+
registry, the log ring buffer, process state transitions and real process lifecycles, service detection
|
|
662
|
+
and adapter failure handling, git porcelain and worktree parsing (plus create/remove against a real
|
|
663
|
+
repository), agent launching, tool/mise/direnv detection, verification and its CLI exit codes, Tailscale
|
|
664
|
+
and remote Ollama parsing, the observable store, and the formatting helpers. No optional tool (Claude,
|
|
665
|
+
Codex, Ollama, Tailscale, mise, direnv) needs to be installed.
|
|
666
|
+
|
|
667
|
+
`pnpm test:tui` boots the real application against OpenTUI's test renderer and drives it with synthetic
|
|
668
|
+
keystrokes: navigation, palette, help, starting/restarting/stopping a managed process, running a package
|
|
669
|
+
script, log capture, creating and removing worktrees, launching an agent in a worktree, verification, and
|
|
670
|
+
clean shutdown. It asserts on behaviour rather than on frame snapshots.
|
|
671
|
+
|
|
672
|
+
### Architecture
|
|
461
673
|
|
|
462
674
|
```
|
|
463
675
|
src/devcc/
|
|
464
|
-
cli.ts argv parsing, TTY check,
|
|
676
|
+
cli.ts argv parsing, subcommands, TTY check, exit code
|
|
677
|
+
automation.ts devcc verify / worktree list / env (no TUI, JSON output)
|
|
465
678
|
index.ts public API (defineConfig, App, all reusable pieces)
|
|
466
679
|
|
|
467
680
|
app/
|
|
468
681
|
App.ts composition root: renderer, services, state, input
|
|
469
682
|
builtinCommands.ts the built-in command set
|
|
683
|
+
controlPlaneCommands.ts worktree, agent, verify, environment and remote commands
|
|
470
684
|
keymap.ts global bindings + the help cheat sheet
|
|
471
685
|
theme.ts palette and status glyphs
|
|
472
686
|
|
|
@@ -478,25 +692,26 @@ src/devcc/
|
|
|
478
692
|
|
|
479
693
|
components/ Header, Footer, Sidebar, RowList, StatusBadge, Overlay,
|
|
480
694
|
ConfirmDialog, CommandPalette, ProjectPicker, HelpOverlay
|
|
481
|
-
views/ Overview, Processes, Services, Scripts, Git,
|
|
482
|
-
Dependencies, System, Settings (all extend View.ts)
|
|
695
|
+
views/ Overview, Processes, Services, Scripts, Git, Worktrees,
|
|
696
|
+
Logs, Dependencies, System, Settings (all extend View.ts)
|
|
483
697
|
|
|
484
|
-
processes/ ManagedProcess types
|
|
698
|
+
processes/ ManagedProcess types, ProcessManager (incl. crash policy),
|
|
699
|
+
graph.ts: dependsOn planner (waves + cycle detection)
|
|
485
700
|
services/ ServiceAdapter interface, ServiceManager, and the
|
|
486
|
-
docker / node / postgres / redis / ollama adapters
|
|
701
|
+
docker / node / postgres / redis / ollama adapters,
|
|
702
|
+
remote.ts: SSH machines + Tailscale
|
|
703
|
+
git/ GitService, pure porcelain-v2 parsers, worktrees.ts
|
|
704
|
+
agents/ AgentAdapter (Claude Code, Codex, custom) + AgentManager
|
|
705
|
+
environment/ tool detection, mise/direnv parsers, EnvironmentService
|
|
706
|
+
verify/ step resolution, the runner loop, the spawn executor
|
|
707
|
+
platform/ editor / terminal / SSH launch commands
|
|
708
|
+
logs/ LogBuffer ring buffer, LogStore, merge.ts (merged stream, search)
|
|
487
709
|
deps/ DependencyService + per-manager `outdated` parsers
|
|
488
710
|
projects/ ProjectRegistry: discovery, recents, switcher list
|
|
489
|
-
processes/graph.ts dependsOn planner (waves + cycle detection)
|
|
490
|
-
logs/merge.ts merged stream, search filter
|
|
491
|
-
git/ GitService + pure porcelain-v2 parsers
|
|
492
711
|
system/ SystemMonitor (Node APIs only)
|
|
493
|
-
|
|
494
|
-
utils/ exec, packageManager, paths, format, fuzzy, logger,
|
|
495
|
-
errors
|
|
712
|
+
utils/ exec, packageManager, paths, format, fuzzy, logger, errors
|
|
496
713
|
```
|
|
497
714
|
|
|
498
|
-
### How data flows
|
|
499
|
-
|
|
500
715
|
```
|
|
501
716
|
ProcessManager ─┐
|
|
502
717
|
ServiceManager ─┤ typed events ┌─────────┐ watched slices ┌───────┐
|
|
@@ -505,65 +720,61 @@ SystemMonitor ─┤ └─────────┘
|
|
|
505
720
|
LogStore ─┘
|
|
506
721
|
```
|
|
507
722
|
|
|
508
|
-
Backends never touch renderables. They emit typed events; `App` folds those into
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
panel. List rendering recycles `TextRenderable`s and only materialises the rows
|
|
723
|
+
Backends never touch renderables. They emit typed events; `App` folds those into a single `AppState`;
|
|
724
|
+
views subscribe to the slices they care about via `store.watch(selector, listener)`, so a 1 Hz CPU sample
|
|
725
|
+
does not repaint the git panel. List rendering recycles `TextRenderable`s and only materialises the rows
|
|
512
726
|
that fit the viewport.
|
|
513
727
|
|
|
514
|
-
### Design decisions
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
- **Detected ≠ owned.** A process devcc launched is `ownership: "managed"`.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
-
|
|
527
|
-
|
|
528
|
-
`
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
- **
|
|
541
|
-
|
|
728
|
+
### Design decisions
|
|
729
|
+
|
|
730
|
+
How the worktree, agent, environment, verification and remote features fit onto the existing abstractions
|
|
731
|
+
is written up in [docs/control-plane.md](docs/control-plane.md).
|
|
732
|
+
|
|
733
|
+
**Safety**
|
|
734
|
+
|
|
735
|
+
- **Nothing runs through a shell.** Every child process is `spawn(executable, args[])`, so arguments
|
|
736
|
+
containing spaces or quotes cannot become injection.
|
|
737
|
+
- **Detected ≠ owned.** A process devcc launched is `ownership: "managed"`. Anything merely observed on the
|
|
738
|
+
host is `"detected"` and can never be signalled by devcc.
|
|
739
|
+
- **Destructive actions are always confirmed:** `git restore`, `git push`, `docker compose down`, stop-all,
|
|
740
|
+
clear-all-logs, and any config command marked `confirm`. Discarding untracked files is refused outright.
|
|
741
|
+
- **Process trees die properly.** Children are spawned detached (their own process group) and stopped with
|
|
742
|
+
`kill(-pgid, SIGTERM)`, escalating to `SIGKILL` after the grace period, so dev servers do not leave
|
|
743
|
+
orphaned workers behind.
|
|
744
|
+
|
|
745
|
+
**Performance**
|
|
746
|
+
|
|
747
|
+
- **Startup never blocks.** The first frame renders before git, Docker, service detection or tool versions
|
|
748
|
+
have answered; each populates asynchronously with its own timeout.
|
|
749
|
+
- **Memory is bounded.** Each log source is a fixed-capacity ring buffer (5000 lines by default). The
|
|
750
|
+
merged view materialises at most 2,000 recent lines on demand. Search is plain substring, or a regex when
|
|
751
|
+
the query parses as one, so typing `(` never breaks the view.
|
|
752
|
+
- **Dependency checks never poll.** They reach the network, so they run only when you ask, and updating a
|
|
753
|
+
package is confirmed because it rewrites the lockfile.
|
|
754
|
+
- **Remote checks are rate-limited.** An adapter can set `refreshIntervalMs`; the poller skips it until that
|
|
755
|
+
has passed, so an open dashboard does not keep hitting a remote machine. Explicit refresh ignores it.
|
|
756
|
+
- **Agents are processes.** An agent adapter only describes a command line; the session is a managed task
|
|
757
|
+
in the same `ProcessManager`, so status, logs, stop and shutdown work exactly as for anything else.
|
|
758
|
+
|
|
759
|
+
**Structure**
|
|
760
|
+
|
|
761
|
+
- **Starts are ordered.** `dependsOn` and `readiness` turn "start all" into a wave-by-wave bring-up. The
|
|
542
762
|
ordering is a pure function (`planStartOrder`) with its own tests.
|
|
543
|
-
- **
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
you ask, and updating a package is confirmed because it rewrites the lockfile.
|
|
556
|
-
- **Errors are never swallowed.** They are normalised into `DevccError`, written
|
|
557
|
-
to `.devcc/devcc.log`, shown in the footer, and kept for inspection in
|
|
558
|
-
**Settings → Diagnostics**.
|
|
559
|
-
|
|
560
|
-
## Extension points
|
|
561
|
-
|
|
562
|
-
**Add a command** - register it once and it appears in the palette, with an
|
|
563
|
-
optional confirmation gate:
|
|
763
|
+
- **Several projects stay open.** Each open project keeps its processes and log capture alive in the
|
|
764
|
+
background; only the visible one polls git, services and system. Closing a project (or quitting) stops
|
|
765
|
+
what devcc started there, with a confirmation.
|
|
766
|
+
- **Switching project rebuilds the session, not the UI.** The renderer, views and store survive; the
|
|
767
|
+
process manager, service adapters, git service, logger and command registry are rebuilt for the new
|
|
768
|
+
directory.
|
|
769
|
+
- **Errors are never swallowed.** They are normalised into `DevccError`, written to `.devcc/devcc.log`,
|
|
770
|
+
shown in the footer, and kept for inspection in **Settings → Diagnostics**.
|
|
771
|
+
|
|
772
|
+
### Extending devcc
|
|
773
|
+
|
|
774
|
+
**Add a command.** Register it once and it appears in the palette, with an optional confirmation gate:
|
|
564
775
|
|
|
565
776
|
```ts
|
|
566
|
-
import { type CommandDefinition } from "
|
|
777
|
+
import { type CommandDefinition } from "@doriandev/devcc"
|
|
567
778
|
|
|
568
779
|
const command: CommandDefinition = {
|
|
569
780
|
id: "deploy.preview",
|
|
@@ -578,15 +789,15 @@ const command: CommandDefinition = {
|
|
|
578
789
|
}
|
|
579
790
|
```
|
|
580
791
|
|
|
581
|
-
`CommandContext` exposes the store, the process/service/git/log/system services,
|
|
582
|
-
|
|
792
|
+
`CommandContext` exposes the store, the process/service/git/log/system services, and `navigate`,
|
|
793
|
+
`notify`, `confirm`, `prompt`, `runScript` and `quit`.
|
|
583
794
|
|
|
584
|
-
**Add a service adapter
|
|
585
|
-
`
|
|
586
|
-
|
|
795
|
+
**Add a service adapter.** Implement `ServiceAdapter` and register it with the `ServiceManager`.
|
|
796
|
+
`detect()` decides whether the service is relevant at all, so adapters cost nothing in projects that do not
|
|
797
|
+
use them:
|
|
587
798
|
|
|
588
799
|
```ts
|
|
589
|
-
import { type ServiceAdapter } from "
|
|
800
|
+
import { type ServiceAdapter } from "@doriandev/devcc"
|
|
590
801
|
|
|
591
802
|
export class TailscaleService implements ServiceAdapter {
|
|
592
803
|
readonly id = "tailscale"
|
|
@@ -608,52 +819,11 @@ export class TailscaleService implements ServiceAdapter {
|
|
|
608
819
|
}
|
|
609
820
|
```
|
|
610
821
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
optional `start`/`stop`/`restart`.
|
|
614
|
-
|
|
615
|
-
**Add a view** - extend `views/View.ts`, register it in `App`, and add an entry
|
|
616
|
-
to `SIDEBAR_ITEMS`.
|
|
617
|
-
|
|
618
|
-
## Development
|
|
619
|
-
|
|
620
|
-
```bash
|
|
621
|
-
pnpm lint # eslint (flat config, type-checked rules)
|
|
622
|
-
pnpm typecheck # tsc --noEmit, strict
|
|
623
|
-
pnpm test # vitest - core logic, no terminal required
|
|
624
|
-
pnpm test:tui # end-to-end smoke test against OpenTUI's headless renderer
|
|
625
|
-
pnpm build # tsc -> dist/
|
|
626
|
-
pnpm verify # lint + typecheck + test + build
|
|
627
|
-
pnpm format # prettier
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
`pnpm test` covers the non-UI core: package-manager detection, config parsing
|
|
631
|
-
and validation, the command registry, the log ring buffer, process state
|
|
632
|
-
transitions and real process lifecycles, service detection and adapter failure
|
|
633
|
-
handling, git porcelain parsing, the observable store, and the formatting
|
|
634
|
-
helpers.
|
|
822
|
+
Azure, Vercel, GitHub, Kubernetes, remote SSH hosts or CI pipelines would be added the same way: a
|
|
823
|
+
`detect()` plus a `getStatus()`, with optional `start`/`stop`/`restart`.
|
|
635
824
|
|
|
636
|
-
|
|
637
|
-
renderer (Bun only) and drives it with synthetic keystrokes: navigation,
|
|
638
|
-
palette, help, starting/restarting/stopping a managed process, running a
|
|
639
|
-
package script, log capture, and clean shutdown. It asserts on behaviour rather
|
|
640
|
-
than on frame snapshots.
|
|
825
|
+
**Add a view.** Extend `views/View.ts`, register it in `App`, and add an entry to `SIDEBAR_ITEMS`.
|
|
641
826
|
|
|
642
|
-
##
|
|
827
|
+
## License
|
|
643
828
|
|
|
644
|
-
|
|
645
|
-
this is an OpenTUI/FFI constraint, not a design choice.
|
|
646
|
-
- Service adapters ship for Docker Compose, PostgreSQL, Redis, Ollama and
|
|
647
|
-
HTTP/port services. Others (Kubernetes, Vercel, Azure, Tailscale, …) are
|
|
648
|
-
designed for but not implemented.
|
|
649
|
-
- The Git panel covers status, staging, discard, commit, fetch, pull and push.
|
|
650
|
-
Branch switching, rebasing, merge-conflict resolution and diff viewing in the
|
|
651
|
-
UI are not implemented (`GitService.diff()` exists but has no view yet).
|
|
652
|
-
- Detected services are read-only: devcc will not start or stop something it did
|
|
653
|
-
not launch, apart from Docker Compose.
|
|
654
|
-
- `docker compose down --volumes` is available through `DockerService.down()`
|
|
655
|
-
but is deliberately not bound to a key.
|
|
656
|
-
- Mouse support is whatever OpenTUI provides by default; the interface is
|
|
657
|
-
designed and tested for the keyboard.
|
|
658
|
-
- Below roughly 60 columns the sidebar hides and columns are trimmed; the layout
|
|
659
|
-
degrades rather than breaking, but very small terminals are cramped.
|
|
829
|
+
[ISC](LICENSE)
|