@doriandev/devcc 0.1.2
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/LICENSE +15 -0
- package/README.md +659 -0
- package/bin/devcc.mjs +66 -0
- package/dist/devcc/app/App.d.ts +178 -0
- package/dist/devcc/app/App.d.ts.map +1 -0
- package/dist/devcc/app/App.js +1255 -0
- package/dist/devcc/app/App.js.map +1 -0
- package/dist/devcc/app/builtinCommands.d.ts +17 -0
- package/dist/devcc/app/builtinCommands.d.ts.map +1 -0
- package/dist/devcc/app/builtinCommands.js +389 -0
- package/dist/devcc/app/builtinCommands.js.map +1 -0
- package/dist/devcc/app/keymap.d.ts +21 -0
- package/dist/devcc/app/keymap.d.ts.map +1 -0
- package/dist/devcc/app/keymap.js +162 -0
- package/dist/devcc/app/keymap.js.map +1 -0
- package/dist/devcc/app/theme.d.ts +49 -0
- package/dist/devcc/app/theme.d.ts.map +1 -0
- package/dist/devcc/app/theme.js +68 -0
- package/dist/devcc/app/theme.js.map +1 -0
- package/dist/devcc/cli.d.ts +3 -0
- package/dist/devcc/cli.d.ts.map +1 -0
- package/dist/devcc/cli.js +113 -0
- package/dist/devcc/cli.js.map +1 -0
- package/dist/devcc/components/CommandPalette.d.ts +26 -0
- package/dist/devcc/components/CommandPalette.d.ts.map +1 -0
- package/dist/devcc/components/CommandPalette.js +113 -0
- package/dist/devcc/components/CommandPalette.js.map +1 -0
- package/dist/devcc/components/ConfirmDialog.d.ts +31 -0
- package/dist/devcc/components/ConfirmDialog.d.ts.map +1 -0
- package/dist/devcc/components/ConfirmDialog.js +147 -0
- package/dist/devcc/components/ConfirmDialog.js.map +1 -0
- package/dist/devcc/components/Footer.d.ts +22 -0
- package/dist/devcc/components/Footer.d.ts.map +1 -0
- package/dist/devcc/components/Footer.js +72 -0
- package/dist/devcc/components/Footer.js.map +1 -0
- package/dist/devcc/components/Header.d.ts +12 -0
- package/dist/devcc/components/Header.d.ts.map +1 -0
- package/dist/devcc/components/Header.js +84 -0
- package/dist/devcc/components/Header.js.map +1 -0
- package/dist/devcc/components/HelpOverlay.d.ts +18 -0
- package/dist/devcc/components/HelpOverlay.d.ts.map +1 -0
- package/dist/devcc/components/HelpOverlay.js +48 -0
- package/dist/devcc/components/HelpOverlay.js.map +1 -0
- package/dist/devcc/components/Overlay.d.ts +31 -0
- package/dist/devcc/components/Overlay.d.ts.map +1 -0
- package/dist/devcc/components/Overlay.js +60 -0
- package/dist/devcc/components/Overlay.js.map +1 -0
- package/dist/devcc/components/ProjectPicker.d.ts +26 -0
- package/dist/devcc/components/ProjectPicker.d.ts.map +1 -0
- package/dist/devcc/components/ProjectPicker.js +114 -0
- package/dist/devcc/components/ProjectPicker.js.map +1 -0
- package/dist/devcc/components/Sidebar.d.ts +42 -0
- package/dist/devcc/components/Sidebar.d.ts.map +1 -0
- package/dist/devcc/components/Sidebar.js +127 -0
- package/dist/devcc/components/Sidebar.js.map +1 -0
- package/dist/devcc/components/StatusBadge.d.ts +16 -0
- package/dist/devcc/components/StatusBadge.d.ts.map +1 -0
- package/dist/devcc/components/StatusBadge.js +30 -0
- package/dist/devcc/components/StatusBadge.js.map +1 -0
- package/dist/devcc/components/primitives.d.ts +62 -0
- package/dist/devcc/components/primitives.d.ts.map +1 -0
- package/dist/devcc/components/primitives.js +169 -0
- package/dist/devcc/components/primitives.js.map +1 -0
- package/dist/devcc/components/styled.d.ts +16 -0
- package/dist/devcc/components/styled.d.ts.map +1 -0
- package/dist/devcc/components/styled.js +35 -0
- package/dist/devcc/components/styled.js.map +1 -0
- package/dist/devcc/core/commands.d.ts +89 -0
- package/dist/devcc/core/commands.d.ts.map +1 -0
- package/dist/devcc/core/commands.js +68 -0
- package/dist/devcc/core/commands.js.map +1 -0
- package/dist/devcc/core/config.d.ts +81 -0
- package/dist/devcc/core/config.d.ts.map +1 -0
- package/dist/devcc/core/config.js +313 -0
- package/dist/devcc/core/config.js.map +1 -0
- package/dist/devcc/core/events.d.ts +21 -0
- package/dist/devcc/core/events.d.ts.map +1 -0
- package/dist/devcc/core/events.js +27 -0
- package/dist/devcc/core/events.js.map +1 -0
- package/dist/devcc/core/state.d.ts +104 -0
- package/dist/devcc/core/state.d.ts.map +1 -0
- package/dist/devcc/core/state.js +139 -0
- package/dist/devcc/core/state.js.map +1 -0
- package/dist/devcc/deps/DependencyService.d.ts +50 -0
- package/dist/devcc/deps/DependencyService.d.ts.map +1 -0
- package/dist/devcc/deps/DependencyService.js +109 -0
- package/dist/devcc/deps/DependencyService.js.map +1 -0
- package/dist/devcc/deps/parse.d.ts +49 -0
- package/dist/devcc/deps/parse.d.ts.map +1 -0
- package/dist/devcc/deps/parse.js +158 -0
- package/dist/devcc/deps/parse.js.map +1 -0
- package/dist/devcc/git/GitService.d.ts +60 -0
- package/dist/devcc/git/GitService.d.ts.map +1 -0
- package/dist/devcc/git/GitService.js +160 -0
- package/dist/devcc/git/GitService.js.map +1 -0
- package/dist/devcc/git/parse.d.ts +57 -0
- package/dist/devcc/git/parse.d.ts.map +1 -0
- package/dist/devcc/git/parse.js +174 -0
- package/dist/devcc/git/parse.js.map +1 -0
- package/dist/devcc/index.d.ts +37 -0
- package/dist/devcc/index.d.ts.map +1 -0
- package/dist/devcc/index.js +36 -0
- package/dist/devcc/index.js.map +1 -0
- package/dist/devcc/logs/LogBuffer.d.ts +86 -0
- package/dist/devcc/logs/LogBuffer.d.ts.map +1 -0
- package/dist/devcc/logs/LogBuffer.js +200 -0
- package/dist/devcc/logs/LogBuffer.js.map +1 -0
- package/dist/devcc/logs/merge.d.ts +36 -0
- package/dist/devcc/logs/merge.d.ts.map +1 -0
- package/dist/devcc/logs/merge.js +71 -0
- package/dist/devcc/logs/merge.js.map +1 -0
- package/dist/devcc/processes/ManagedProcess.d.ts +129 -0
- package/dist/devcc/processes/ManagedProcess.d.ts.map +1 -0
- package/dist/devcc/processes/ManagedProcess.js +80 -0
- package/dist/devcc/processes/ManagedProcess.js.map +1 -0
- package/dist/devcc/processes/ProcessManager.d.ts +112 -0
- package/dist/devcc/processes/ProcessManager.d.ts.map +1 -0
- package/dist/devcc/processes/ProcessManager.js +525 -0
- package/dist/devcc/processes/ProcessManager.js.map +1 -0
- package/dist/devcc/processes/graph.d.ts +35 -0
- package/dist/devcc/processes/graph.d.ts.map +1 -0
- package/dist/devcc/processes/graph.js +68 -0
- package/dist/devcc/processes/graph.js.map +1 -0
- package/dist/devcc/projects/ProjectRegistry.d.ts +52 -0
- package/dist/devcc/projects/ProjectRegistry.d.ts.map +1 -0
- package/dist/devcc/projects/ProjectRegistry.js +177 -0
- package/dist/devcc/projects/ProjectRegistry.js.map +1 -0
- package/dist/devcc/services/ServiceManager.d.ts +48 -0
- package/dist/devcc/services/ServiceManager.d.ts.map +1 -0
- package/dist/devcc/services/ServiceManager.js +146 -0
- package/dist/devcc/services/ServiceManager.js.map +1 -0
- package/dist/devcc/services/docker.d.ts +37 -0
- package/dist/devcc/services/docker.d.ts.map +1 -0
- package/dist/devcc/services/docker.js +179 -0
- package/dist/devcc/services/docker.js.map +1 -0
- package/dist/devcc/services/node.d.ts +40 -0
- package/dist/devcc/services/node.d.ts.map +1 -0
- package/dist/devcc/services/node.js +107 -0
- package/dist/devcc/services/node.js.map +1 -0
- package/dist/devcc/services/ollama.d.ts +27 -0
- package/dist/devcc/services/ollama.d.ts.map +1 -0
- package/dist/devcc/services/ollama.js +66 -0
- package/dist/devcc/services/ollama.js.map +1 -0
- package/dist/devcc/services/postgres.d.ts +30 -0
- package/dist/devcc/services/postgres.d.ts.map +1 -0
- package/dist/devcc/services/postgres.js +47 -0
- package/dist/devcc/services/postgres.js.map +1 -0
- package/dist/devcc/services/redis.d.ts +20 -0
- package/dist/devcc/services/redis.d.ts.map +1 -0
- package/dist/devcc/services/redis.js +39 -0
- package/dist/devcc/services/redis.js.map +1 -0
- package/dist/devcc/services/types.d.ts +51 -0
- package/dist/devcc/services/types.d.ts.map +1 -0
- package/dist/devcc/services/types.js +10 -0
- package/dist/devcc/services/types.js.map +1 -0
- package/dist/devcc/system/SystemMonitor.d.ts +87 -0
- package/dist/devcc/system/SystemMonitor.d.ts.map +1 -0
- package/dist/devcc/system/SystemMonitor.js +142 -0
- package/dist/devcc/system/SystemMonitor.js.map +1 -0
- package/dist/devcc/utils/errors.d.ts +31 -0
- package/dist/devcc/utils/errors.d.ts.map +1 -0
- package/dist/devcc/utils/errors.js +52 -0
- package/dist/devcc/utils/errors.js.map +1 -0
- package/dist/devcc/utils/exec.d.ts +41 -0
- package/dist/devcc/utils/exec.d.ts.map +1 -0
- package/dist/devcc/utils/exec.js +112 -0
- package/dist/devcc/utils/exec.js.map +1 -0
- package/dist/devcc/utils/format.d.ts +18 -0
- package/dist/devcc/utils/format.d.ts.map +1 -0
- package/dist/devcc/utils/format.js +85 -0
- package/dist/devcc/utils/format.js.map +1 -0
- package/dist/devcc/utils/fuzzy.d.ts +12 -0
- package/dist/devcc/utils/fuzzy.d.ts.map +1 -0
- package/dist/devcc/utils/fuzzy.js +20 -0
- package/dist/devcc/utils/fuzzy.js.map +1 -0
- package/dist/devcc/utils/logger.d.ts +41 -0
- package/dist/devcc/utils/logger.d.ts.map +1 -0
- package/dist/devcc/utils/logger.js +117 -0
- package/dist/devcc/utils/logger.js.map +1 -0
- package/dist/devcc/utils/packageManager.d.ts +39 -0
- package/dist/devcc/utils/packageManager.d.ts.map +1 -0
- package/dist/devcc/utils/packageManager.js +85 -0
- package/dist/devcc/utils/packageManager.js.map +1 -0
- package/dist/devcc/utils/paths.d.ts +12 -0
- package/dist/devcc/utils/paths.d.ts.map +1 -0
- package/dist/devcc/utils/paths.js +39 -0
- package/dist/devcc/utils/paths.js.map +1 -0
- package/dist/devcc/views/DependenciesView.d.ts +34 -0
- package/dist/devcc/views/DependenciesView.d.ts.map +1 -0
- package/dist/devcc/views/DependenciesView.js +170 -0
- package/dist/devcc/views/DependenciesView.js.map +1 -0
- package/dist/devcc/views/GitView.d.ts +29 -0
- package/dist/devcc/views/GitView.d.ts.map +1 -0
- package/dist/devcc/views/GitView.js +226 -0
- package/dist/devcc/views/GitView.js.map +1 -0
- package/dist/devcc/views/LogsView.d.ts +39 -0
- package/dist/devcc/views/LogsView.d.ts.map +1 -0
- package/dist/devcc/views/LogsView.js +249 -0
- package/dist/devcc/views/LogsView.js.map +1 -0
- package/dist/devcc/views/OverviewView.d.ts +16 -0
- package/dist/devcc/views/OverviewView.d.ts.map +1 -0
- package/dist/devcc/views/OverviewView.js +135 -0
- package/dist/devcc/views/OverviewView.js.map +1 -0
- package/dist/devcc/views/ProcessesView.d.ts +26 -0
- package/dist/devcc/views/ProcessesView.d.ts.map +1 -0
- package/dist/devcc/views/ProcessesView.js +161 -0
- package/dist/devcc/views/ProcessesView.js.map +1 -0
- package/dist/devcc/views/ScriptsView.d.ts +30 -0
- package/dist/devcc/views/ScriptsView.d.ts.map +1 -0
- package/dist/devcc/views/ScriptsView.js +168 -0
- package/dist/devcc/views/ScriptsView.js.map +1 -0
- package/dist/devcc/views/ServicesView.d.ts +28 -0
- package/dist/devcc/views/ServicesView.d.ts.map +1 -0
- package/dist/devcc/views/ServicesView.js +153 -0
- package/dist/devcc/views/ServicesView.js.map +1 -0
- package/dist/devcc/views/SettingsView.d.ts +20 -0
- package/dist/devcc/views/SettingsView.d.ts.map +1 -0
- package/dist/devcc/views/SettingsView.js +114 -0
- package/dist/devcc/views/SettingsView.js.map +1 -0
- package/dist/devcc/views/SystemView.d.ts +13 -0
- package/dist/devcc/views/SystemView.d.ts.map +1 -0
- package/dist/devcc/views/SystemView.js +82 -0
- package/dist/devcc/views/SystemView.js.map +1 -0
- package/dist/devcc/views/View.d.ts +35 -0
- package/dist/devcc/views/View.d.ts.map +1 -0
- package/dist/devcc/views/View.js +42 -0
- package/dist/devcc/views/View.js.map +1 -0
- package/package.json +79 -0
- package/src/devcc/app/App.ts +1463 -0
- package/src/devcc/app/builtinCommands.ts +429 -0
- package/src/devcc/app/keymap.ts +167 -0
- package/src/devcc/app/theme.ts +83 -0
- package/src/devcc/cli.ts +122 -0
- package/src/devcc/components/CommandPalette.ts +136 -0
- package/src/devcc/components/ConfirmDialog.ts +174 -0
- package/src/devcc/components/Footer.ts +85 -0
- package/src/devcc/components/Header.ts +93 -0
- package/src/devcc/components/HelpOverlay.ts +62 -0
- package/src/devcc/components/Overlay.ts +91 -0
- package/src/devcc/components/ProjectPicker.ts +138 -0
- package/src/devcc/components/Sidebar.ts +163 -0
- package/src/devcc/components/StatusBadge.ts +36 -0
- package/src/devcc/components/primitives.ts +213 -0
- package/src/devcc/components/styled.ts +44 -0
- package/src/devcc/core/commands.ts +140 -0
- package/src/devcc/core/config.ts +405 -0
- package/src/devcc/core/events.ts +41 -0
- package/src/devcc/core/state.ts +236 -0
- package/src/devcc/deps/DependencyService.ts +152 -0
- package/src/devcc/deps/parse.ts +210 -0
- package/src/devcc/git/GitService.ts +205 -0
- package/src/devcc/git/parse.ts +203 -0
- package/src/devcc/index.ts +115 -0
- package/src/devcc/logs/LogBuffer.ts +237 -0
- package/src/devcc/logs/merge.ts +87 -0
- package/src/devcc/processes/ManagedProcess.ts +195 -0
- package/src/devcc/processes/ProcessManager.ts +625 -0
- package/src/devcc/processes/graph.ts +98 -0
- package/src/devcc/projects/ProjectRegistry.ts +219 -0
- package/src/devcc/services/ServiceManager.ts +180 -0
- package/src/devcc/services/docker.ts +220 -0
- package/src/devcc/services/node.ts +137 -0
- package/src/devcc/services/ollama.ts +80 -0
- package/src/devcc/services/postgres.ts +57 -0
- package/src/devcc/services/redis.ts +47 -0
- package/src/devcc/services/types.ts +59 -0
- package/src/devcc/system/SystemMonitor.ts +207 -0
- package/src/devcc/utils/errors.ts +64 -0
- package/src/devcc/utils/exec.ts +165 -0
- package/src/devcc/utils/format.ts +83 -0
- package/src/devcc/utils/fuzzy.ts +44 -0
- package/src/devcc/utils/logger.ts +140 -0
- package/src/devcc/utils/packageManager.ts +112 -0
- package/src/devcc/utils/paths.ts +39 -0
- package/src/devcc/views/DependenciesView.ts +206 -0
- package/src/devcc/views/GitView.ts +259 -0
- package/src/devcc/views/LogsView.ts +300 -0
- package/src/devcc/views/OverviewView.ts +187 -0
- package/src/devcc/views/ProcessesView.ts +177 -0
- package/src/devcc/views/ScriptsView.ts +187 -0
- package/src/devcc/views/ServicesView.ts +188 -0
- package/src/devcc/views/SettingsView.ts +146 -0
- package/src/devcc/views/SystemView.ts +110 -0
- package/src/devcc/views/View.ts +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Boomspot
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
# Dev Command Center
|
|
2
|
+
|
|
3
|
+
A single keyboard-driven screen that replaces the pile of terminal tabs you keep
|
|
4
|
+
open while working on a project. Built with [OpenTUI](https://opentui.com).
|
|
5
|
+
|
|
6
|
+
## What it is for
|
|
7
|
+
|
|
8
|
+
Working on a real project normally means one tab running `next dev`, another for
|
|
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.
|
|
13
|
+
|
|
14
|
+
Dev Command Center puts that in one place.
|
|
15
|
+
|
|
16
|
+
- **Watches** - git branch and changes, which services are actually listening
|
|
17
|
+
(Docker, Postgres, Redis, Ollama, a framework dev server), host CPU / memory /
|
|
18
|
+
disk, and outdated dependencies.
|
|
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.
|
|
44
|
+
|
|
45
|
+
It earns its place on projects with more than one long-running process, or when
|
|
46
|
+
you bounce between repositories. For a single `next dev` you would barely notice
|
|
47
|
+
the difference - the payoff scales with how many moving parts your setup has.
|
|
48
|
+
|
|
49
|
+
## At a glance
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
┌──────────────────────────────────────────────────────────────────────────────────┐
|
|
53
|
+
│ DEV COMMAND CENTER command-center · pnpm · ~/dev/cc 21:04 │
|
|
54
|
+
├──────────────────────┬───────────────────────────────────────────────────────────┤
|
|
55
|
+
│ PROJECT │ GIT │
|
|
56
|
+
│ │ branch main │
|
|
57
|
+
│ ● Overview 1 │ status 2 modified / 1 untracked │
|
|
58
|
+
│ ○ Processes 2 p │ tracking ahead 2 │
|
|
59
|
+
│ ○ Services 3 │ │
|
|
60
|
+
│ ○ Scripts 4 │ DEVELOPMENT │
|
|
61
|
+
│ ○ Git 5 g │ ● tsc --watch running pid 48213 2m 10s │
|
|
62
|
+
│ ○ Logs 6 l │ ○ vitest stopped │
|
|
63
|
+
│ ○ Dependencies 7 d │ │
|
|
64
|
+
│ ○ System 8 │ SYSTEM │
|
|
65
|
+
│ ○ Settings 9 │ cpu ███░░░░░░░░░ 21% load 2.14 │
|
|
66
|
+
│ │ memory ████████░░░░ 7.2 / 36.0 GB │
|
|
67
|
+
├──────────────────────┴───────────────────────────────────────────────────────────┤
|
|
68
|
+
│ ↑↓ navigate p processes g git l logs / commands ? help q quit │
|
|
69
|
+
└──────────────────────────────────────────────────────────────────────────────────┘
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Requirements
|
|
73
|
+
|
|
74
|
+
OpenTUI renders through a native Zig core over FFI, so the dashboard needs one
|
|
75
|
+
of:
|
|
76
|
+
|
|
77
|
+
- **Bun 1.3.0 or newer** (recommended), or
|
|
78
|
+
- **Node.js 26.4.0 or newer**, started with `--experimental-ffi`
|
|
79
|
+
|
|
80
|
+
The `devcc` launcher detects this for you: run it with any Node ≥ 20 and it will
|
|
81
|
+
re-exec into Bun (or into Node with the right flag) automatically. Dependency
|
|
82
|
+
installation, linting, type checking and the unit tests all run on plain Node.
|
|
83
|
+
|
|
84
|
+
## Install and run
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm i -g @doriandev/devcc # or: pnpm add -g @doriandev/devcc
|
|
88
|
+
cd ~/code/example.com
|
|
89
|
+
devcc
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`devcc` runs against the project you are standing in. To point it somewhere else
|
|
93
|
+
without changing directory:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
devcc -C ~/code/example.com
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### CLI options
|
|
100
|
+
|
|
101
|
+
| Option | Description |
|
|
102
|
+
| ------------------- | ------------------------------------------------- |
|
|
103
|
+
| `-C`, `--cwd <dir>` | Project directory (default: nearest project root) |
|
|
104
|
+
| `-h`, `--help` | Show usage |
|
|
105
|
+
| `-v`, `--version` | Show the version |
|
|
106
|
+
|
|
107
|
+
`DEVCC_LOG_LEVEL` (`debug` \| `info` \| `warn` \| `error` \| `silent`) controls
|
|
108
|
+
the internal diagnostics log written to `.devcc/devcc.log`.
|
|
109
|
+
|
|
110
|
+
### Running from source
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
git clone https://github.com/dordevgit/devcc.git
|
|
114
|
+
cd devcc
|
|
115
|
+
pnpm install
|
|
116
|
+
pnpm dev:center # launch against this repo
|
|
117
|
+
node bin/devcc.mjs -C ~/code/example.com # or against another project
|
|
118
|
+
pnpm build # compile to dist/
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## How much setup is needed
|
|
122
|
+
|
|
123
|
+
Two prerequisites, then nothing else is required to get a useful screen:
|
|
124
|
+
|
|
125
|
+
1. **Install it** - `npm i -g @doriandev/devcc`.
|
|
126
|
+
2. **A runtime with FFI** - Bun ≥ 1.3, or Node ≥ 26.4 run with
|
|
127
|
+
`--experimental-ffi` (see [Requirements](#requirements)). The launcher
|
|
128
|
+
handles this for you if Bun is installed; if neither is available it says so
|
|
129
|
+
and stops.
|
|
130
|
+
|
|
131
|
+
### What works with no configuration
|
|
132
|
+
|
|
133
|
+
Run `devcc` in `~/code/example.com` having written nothing, and it works these
|
|
134
|
+
out for itself:
|
|
135
|
+
|
|
136
|
+
| It figures out | How |
|
|
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 |
|
|
145
|
+
|
|
146
|
+
That is real detection rather than defaults, so Overview, Scripts, Git, Services
|
|
147
|
+
and System are all populated on first launch.
|
|
148
|
+
|
|
149
|
+
### What does require configuration
|
|
150
|
+
|
|
151
|
+
**Managed processes.** devcc cannot guess that `pnpm dev` is the thing you want
|
|
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`:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
// ~/code/example.com/.devcc.ts
|
|
158
|
+
export default {
|
|
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
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Restart devcc and the Processes pane is live: `s` starts or stops, `r` restarts,
|
|
184
|
+
`enter` jumps to that process's logs, and quitting stops everything devcc
|
|
185
|
+
started. The declared command appears in the palette (`/`) and asks before it
|
|
186
|
+
runs.
|
|
187
|
+
|
|
188
|
+
This is deliberate. The alternative is a tool that spawns processes you did not
|
|
189
|
+
ask for, and devcc will never touch a process it did not start - see
|
|
190
|
+
[Configuration](#configuration) for every available field.
|
|
191
|
+
|
|
192
|
+
## Keyboard shortcuts
|
|
193
|
+
|
|
194
|
+
Press `?`, `F1` or `ctrl+k` in the app for the manual, which also explains what each section is for and the main config fields.
|
|
195
|
+
|
|
196
|
+
### Global
|
|
197
|
+
|
|
198
|
+
| Key | Action |
|
|
199
|
+
| --------------------- | --------------------------------------------------------- |
|
|
200
|
+
| `↑` / `k`, `↓` / `j` | Previous / next item |
|
|
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) |
|
|
213
|
+
|
|
214
|
+
### Processes and Services
|
|
215
|
+
|
|
216
|
+
| Key | Action |
|
|
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) |
|
|
223
|
+
|
|
224
|
+
### Scripts
|
|
225
|
+
|
|
226
|
+
| Key | Action |
|
|
227
|
+
| --------- | ---------------------------------- |
|
|
228
|
+
| `enter` | Run the selected script |
|
|
229
|
+
| `x` | Stop a running script |
|
|
230
|
+
| `f` / `c` | Filter the list / clear the filter |
|
|
231
|
+
|
|
232
|
+
### Git
|
|
233
|
+
|
|
234
|
+
| Key | Action |
|
|
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 |
|
|
242
|
+
|
|
243
|
+
### Dependencies
|
|
244
|
+
|
|
245
|
+
| Key | Action |
|
|
246
|
+
| --- | --------------------------------------- |
|
|
247
|
+
| `r` | Check the registry for updates |
|
|
248
|
+
| `u` | Update the selected package (confirmed) |
|
|
249
|
+
|
|
250
|
+
### Logs
|
|
251
|
+
|
|
252
|
+
| Key | Action |
|
|
253
|
+
| ---------------- | -------------------------------------------------- |
|
|
254
|
+
| `j` / `k` | Scroll |
|
|
255
|
+
| `PgUp` / `PgDn` | Page |
|
|
256
|
+
| `home` / `end` | Jump to start / end |
|
|
257
|
+
| `f` | Toggle follow |
|
|
258
|
+
| `s` | Search - plain text, or a regex if it parses |
|
|
259
|
+
| `x` | Clear the search |
|
|
260
|
+
| `e` | Toggle errors only (stderr) |
|
|
261
|
+
| `a` | Merged stream of every source, colour-keyed |
|
|
262
|
+
| `tab`, `←` / `→` | Change log source |
|
|
263
|
+
| `c` | Clear the current buffer (all, in the merged view) |
|
|
264
|
+
|
|
265
|
+
## Configuration
|
|
266
|
+
|
|
267
|
+
Configuration is **optional** - without it the dashboard still detects your
|
|
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.
|
|
271
|
+
|
|
272
|
+
devcc looks for the first of `.devcc.ts`, `devcc.config.ts`, `.devcc.mts`,
|
|
273
|
+
`devcc.config.mts`, `.devcc.js`, `devcc.config.js`, `.devcc.mjs`,
|
|
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.
|
|
276
|
+
|
|
277
|
+
A real example (this repository's own `.devcc.ts`):
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { defineConfig } from "./src/devcc/index.js"
|
|
281
|
+
|
|
282
|
+
export default defineConfig({
|
|
283
|
+
name: "dev command center",
|
|
284
|
+
|
|
285
|
+
processes: [
|
|
286
|
+
{
|
|
287
|
+
id: "typecheck",
|
|
288
|
+
name: "tsc --watch",
|
|
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",
|
|
296
|
+
command: "pnpm",
|
|
297
|
+
args: ["exec", "vitest", "--watch"],
|
|
298
|
+
kind: "service",
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
|
|
302
|
+
commands: [
|
|
303
|
+
{
|
|
304
|
+
id: "verify",
|
|
305
|
+
title: "Verify (lint, typecheck, test, build)",
|
|
306
|
+
category: "Project",
|
|
307
|
+
command: "pnpm",
|
|
308
|
+
args: ["run", "verify"],
|
|
309
|
+
keywords: ["ci", "check", "all"],
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
id: "clean",
|
|
313
|
+
title: "Remove dist/",
|
|
314
|
+
category: "Project",
|
|
315
|
+
command: "rm",
|
|
316
|
+
args: ["-rf", "dist"],
|
|
317
|
+
confirm: true,
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
|
|
321
|
+
refresh: { system: 1_500, services: 5_000, git: 8_000 },
|
|
322
|
+
})
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
A monorepo example. In a project that has devcc installed, import `defineConfig`
|
|
326
|
+
from the package name (`command-center`); in a project that does not, export a
|
|
327
|
+
plain object instead - the shape is identical and it is validated either way:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
import { defineConfig } from "command-center"
|
|
331
|
+
|
|
332
|
+
export default defineConfig({
|
|
333
|
+
processes: [
|
|
334
|
+
{
|
|
335
|
+
id: "web",
|
|
336
|
+
name: "Next.js",
|
|
337
|
+
command: "pnpm",
|
|
338
|
+
args: ["dev"],
|
|
339
|
+
cwd: "./apps/web",
|
|
340
|
+
url: "localhost:3000",
|
|
341
|
+
port: 3000,
|
|
342
|
+
autoStart: true,
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
id: "api",
|
|
346
|
+
name: "API",
|
|
347
|
+
command: "pnpm",
|
|
348
|
+
args: ["dev"],
|
|
349
|
+
cwd: "./apps/api",
|
|
350
|
+
url: "localhost:3001",
|
|
351
|
+
port: 3001,
|
|
352
|
+
},
|
|
353
|
+
{ id: "worker", name: "Worker", command: "pnpm", args: ["start"], cwd: "./apps/worker" },
|
|
354
|
+
],
|
|
355
|
+
services: [
|
|
356
|
+
{ id: "typesense", name: "Typesense", port: 8108, category: "database" },
|
|
357
|
+
{ id: "mailhog", name: "Mailhog", port: 8025, healthPath: "/" },
|
|
358
|
+
],
|
|
359
|
+
commands: [
|
|
360
|
+
{ id: "db:reset", title: "Reset the database", command: "pnpm", args: ["db:reset"], confirm: true },
|
|
361
|
+
],
|
|
362
|
+
})
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Without the package installed, the same config works as a plain object:
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
// .devcc.ts in any project - no import, no dependency
|
|
369
|
+
export default {
|
|
370
|
+
processes: [{ id: "web", name: "Web", command: "pnpm", args: ["dev"], port: 3000 }],
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Process definitions
|
|
375
|
+
|
|
376
|
+
| Field | Type | Description |
|
|
377
|
+
| ------------------- | ----------------------- | ------------------------------------------------------------------- |
|
|
378
|
+
| `id` | `string` | Unique id. Also the log source id. **Required** |
|
|
379
|
+
| `name` | `string` | Display name (defaults to `id`) |
|
|
380
|
+
| `command` | `string` | Executable. **Required** - never a shell string |
|
|
381
|
+
| `args` | `string[]` | Arguments passed as an array, so quoting is never an issue |
|
|
382
|
+
| `cwd` | `string` | Absolute, or relative to the project root |
|
|
383
|
+
| `env` | `Record<string,string>` | Extra environment variables |
|
|
384
|
+
| `kind` | `"service" \| "task"` | Long-running vs. expected to exit (default `service`) |
|
|
385
|
+
| `autoStart` | `boolean` | Start with the dashboard (default `false`) |
|
|
386
|
+
| `url` | `string` | Informational address shown next to the process |
|
|
387
|
+
| `port` | `number` | Port to associate with the process |
|
|
388
|
+
| `killGraceMs` | `number` | SIGTERM → SIGKILL grace period (default 5000) |
|
|
389
|
+
| `restartOnCrash` | `boolean` | Bring it back automatically after a non-zero exit (default `false`) |
|
|
390
|
+
| `maxCrashRestarts` | `number` | Give up after this many consecutive crashes (default 5) |
|
|
391
|
+
| `crashBackoffMs` | `number` | First backoff delay, doubling per crash (default 1000) |
|
|
392
|
+
| `maxCrashBackoffMs` | `number` | Backoff ceiling (default 30000) |
|
|
393
|
+
| `healthyAfterMs` | `number` | A run lasting this long resets the crash budget (default 30000) |
|
|
394
|
+
| `dependsOn` | `string[]` | Ids that must be _ready_ before this one starts |
|
|
395
|
+
| `readiness` | `object` | How devcc decides the process is usable - see below |
|
|
396
|
+
|
|
397
|
+
### Readiness checks
|
|
398
|
+
|
|
399
|
+
Without a readiness block a process is `running` the moment it has spawned.
|
|
400
|
+
With one, it stays `starting` until the check passes, and `dependsOn` waits for
|
|
401
|
+
that - so "start all" brings a stack up in the right order and only once each
|
|
402
|
+
layer is actually usable.
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
{
|
|
406
|
+
id: "web",
|
|
407
|
+
command: "pnpm", args: ["dev"],
|
|
408
|
+
dependsOn: ["api"],
|
|
409
|
+
readiness: { port: 3000 }, // TCP connect succeeds
|
|
410
|
+
}
|
|
411
|
+
{
|
|
412
|
+
id: "api",
|
|
413
|
+
command: "pnpm", args: ["dev"], cwd: "./apps/api",
|
|
414
|
+
dependsOn: ["db"],
|
|
415
|
+
readiness: { logPattern: "listening on \\d+" }, // a line of output matches
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
| Field | Description |
|
|
420
|
+
| -------------- | ---------------------------------------------------------------------------- |
|
|
421
|
+
| `port`, `host` | Ready once something accepts a TCP connection (host defaults to `127.0.0.1`) |
|
|
422
|
+
| `logPattern` | Ready once a line of stdout/stderr matches this regex (case-insensitive) |
|
|
423
|
+
| `timeoutMs` | Give up waiting and report `running` anyway, with a warning (default 30000) |
|
|
424
|
+
| `intervalMs` | Port probe interval (default 250) |
|
|
425
|
+
|
|
426
|
+
Either `port` or `logPattern` is required; both may be given. A dependency
|
|
427
|
+
cycle is reported and those processes are left alone rather than started in an
|
|
428
|
+
arbitrary order.
|
|
429
|
+
|
|
430
|
+
### Command definitions
|
|
431
|
+
|
|
432
|
+
| Field | Type | Description |
|
|
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 |
|
|
440
|
+
|
|
441
|
+
### Service definitions
|
|
442
|
+
|
|
443
|
+
Extra ports to watch: `{ id, name, port, host?, healthPath?, category? }`.
|
|
444
|
+
`healthPath` upgrades the check from a TCP probe to an HTTP request.
|
|
445
|
+
|
|
446
|
+
### Other options
|
|
447
|
+
|
|
448
|
+
| Field | Description |
|
|
449
|
+
| ------------------- | ------------------------------------------------------------------ |
|
|
450
|
+
| `name` | Overrides the project name in the header |
|
|
451
|
+
| `refresh` | `{ system, services, git, clock }` intervals in ms |
|
|
452
|
+
| `maxLogLines` | Ring-buffer size per log source (default 5000) |
|
|
453
|
+
| `logLevel` | Level for `.devcc/devcc.log` |
|
|
454
|
+
| `projects` | Extra project paths offered in the switcher (absolute or relative) |
|
|
455
|
+
| `projectSearchDirs` | Extra directories to scan for sibling projects |
|
|
456
|
+
|
|
457
|
+
Invalid entries are reported in **Settings → Config issues** and skipped; a typo
|
|
458
|
+
never stops the dashboard from starting.
|
|
459
|
+
|
|
460
|
+
## Architecture
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
src/devcc/
|
|
464
|
+
cli.ts argv parsing, TTY check, process exit code
|
|
465
|
+
index.ts public API (defineConfig, App, all reusable pieces)
|
|
466
|
+
|
|
467
|
+
app/
|
|
468
|
+
App.ts composition root: renderer, services, state, input
|
|
469
|
+
builtinCommands.ts the built-in command set
|
|
470
|
+
keymap.ts global bindings + the help cheat sheet
|
|
471
|
+
theme.ts palette and status glyphs
|
|
472
|
+
|
|
473
|
+
core/
|
|
474
|
+
state.ts AppState shape + the observable Store
|
|
475
|
+
commands.ts CommandRegistry and CommandContext
|
|
476
|
+
config.ts defineConfig, discovery, validation, loading
|
|
477
|
+
events.ts typed event bus + subscription bag
|
|
478
|
+
|
|
479
|
+
components/ Header, Footer, Sidebar, RowList, StatusBadge, Overlay,
|
|
480
|
+
ConfirmDialog, CommandPalette, ProjectPicker, HelpOverlay
|
|
481
|
+
views/ Overview, Processes, Services, Scripts, Git, Logs,
|
|
482
|
+
Dependencies, System, Settings (all extend View.ts)
|
|
483
|
+
|
|
484
|
+
processes/ ManagedProcess types + ProcessManager (incl. crash policy)
|
|
485
|
+
services/ ServiceAdapter interface, ServiceManager, and the
|
|
486
|
+
docker / node / postgres / redis / ollama adapters
|
|
487
|
+
deps/ DependencyService + per-manager `outdated` parsers
|
|
488
|
+
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
|
+
system/ SystemMonitor (Node APIs only)
|
|
493
|
+
logs/ LogBuffer ring buffer + LogStore
|
|
494
|
+
utils/ exec, packageManager, paths, format, fuzzy, logger,
|
|
495
|
+
errors
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### How data flows
|
|
499
|
+
|
|
500
|
+
```
|
|
501
|
+
ProcessManager ─┐
|
|
502
|
+
ServiceManager ─┤ typed events ┌─────────┐ watched slices ┌───────┐
|
|
503
|
+
GitService ─┼──────────────────►│ Store │───────────────────►│ Views │
|
|
504
|
+
SystemMonitor ─┤ └─────────┘ └───────┘
|
|
505
|
+
LogStore ─┘
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Backends never touch renderables. They emit typed events; `App` folds those into
|
|
509
|
+
a single `AppState`; views subscribe to the slices they care about via
|
|
510
|
+
`store.watch(selector, listener)`, so a 1 Hz CPU sample does not repaint the git
|
|
511
|
+
panel. List rendering recycles `TextRenderable`s and only materialises the rows
|
|
512
|
+
that fit the viewport.
|
|
513
|
+
|
|
514
|
+
### Design decisions worth knowing
|
|
515
|
+
|
|
516
|
+
- **Nothing runs through a shell.** Every child process is
|
|
517
|
+
`spawn(executable, args[])`, so arguments containing spaces or quotes cannot
|
|
518
|
+
become injection.
|
|
519
|
+
- **Process trees die properly.** Children are spawned detached (their own
|
|
520
|
+
process group) and stopped with `kill(-pgid, SIGTERM)`, escalating to
|
|
521
|
+
`SIGKILL` after the grace period, so dev servers do not leave orphaned
|
|
522
|
+
workers behind.
|
|
523
|
+
- **Detected ≠ owned.** A process devcc launched is `ownership: "managed"`.
|
|
524
|
+
Anything merely observed on the host is `"detected"` and can never be
|
|
525
|
+
signalled by devcc.
|
|
526
|
+
- **Destructive actions are always confirmed**: `git restore`, `git push`,
|
|
527
|
+
`docker compose down`, stop-all, clear-all-logs, and any config command marked
|
|
528
|
+
`confirm`. Discarding untracked files is refused outright.
|
|
529
|
+
- **Memory is bounded by construction.** Each log source is a fixed-capacity
|
|
530
|
+
ring buffer (5000 lines by default); nothing else retains log lines.
|
|
531
|
+
- **Startup never blocks.** The first frame renders before git, Docker, service
|
|
532
|
+
detection or tool versions have answered; each populates asynchronously with
|
|
533
|
+
its own timeout.
|
|
534
|
+
- **Several projects can be open at once.** `ctrl+p` opens another project
|
|
535
|
+
without closing the current one; each keeps its processes and log capture
|
|
536
|
+
alive in the background, only the visible project polls git/services/system,
|
|
537
|
+
and the sidebar grows a PROJECTS block with per-project running counts.
|
|
538
|
+
Closing a project (or quitting) stops what devcc started there, with a
|
|
539
|
+
confirmation.
|
|
540
|
+
- **Starts are ordered.** `dependsOn` and `readiness` turn "start all" into a
|
|
541
|
+
wave-by-wave bring-up: each wave waits for the previous one to be ready. The
|
|
542
|
+
ordering is a pure function (`planStartOrder`) with its own tests.
|
|
543
|
+
- **The merged log stream is bounded.** Per-source views window straight into
|
|
544
|
+
that source's ring buffer; the merged view materialises at most 2,000 recent
|
|
545
|
+
lines on demand. Searching is plain substring, or a regex when the query
|
|
546
|
+
parses as one, so typing `(` never breaks the view.
|
|
547
|
+
- **Crash recovery is opt-in.** A process only comes back automatically if its
|
|
548
|
+
definition says `restartOnCrash`, with exponential backoff and a hard attempt
|
|
549
|
+
limit; a manual stop cancels any pending restart and clears the budget.
|
|
550
|
+
- **Switching project rebuilds the session, not the UI.** The renderer, views and
|
|
551
|
+
store survive; the process manager, service adapters, git service, logger and
|
|
552
|
+
command registry are torn down and rebuilt for the new directory, and anything
|
|
553
|
+
devcc had started is stopped first (with a confirmation).
|
|
554
|
+
- **Dependency checks never poll.** They reach the network, so they run only when
|
|
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:
|
|
564
|
+
|
|
565
|
+
```ts
|
|
566
|
+
import { type CommandDefinition } from "command-center"
|
|
567
|
+
|
|
568
|
+
const command: CommandDefinition = {
|
|
569
|
+
id: "deploy.preview",
|
|
570
|
+
title: "Deploy a preview",
|
|
571
|
+
category: "Deploy",
|
|
572
|
+
keywords: ["vercel", "ship"],
|
|
573
|
+
confirm: { title: "Deploy a preview?", message: "This publishes to your team." },
|
|
574
|
+
execute: async (context) => {
|
|
575
|
+
context.notify("info", "Deploying…")
|
|
576
|
+
await context.runScript("deploy:preview")
|
|
577
|
+
},
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
`CommandContext` exposes the store, the process/service/git/log/system services,
|
|
582
|
+
and `navigate`, `notify`, `confirm`, `prompt`, `runScript` and `quit`.
|
|
583
|
+
|
|
584
|
+
**Add a service adapter** - implement `ServiceAdapter` and register it with the
|
|
585
|
+
`ServiceManager`. `detect()` decides whether the service is relevant at all, so
|
|
586
|
+
adapters cost nothing in projects that do not use them:
|
|
587
|
+
|
|
588
|
+
```ts
|
|
589
|
+
import { type ServiceAdapter } from "command-center"
|
|
590
|
+
|
|
591
|
+
export class TailscaleService implements ServiceAdapter {
|
|
592
|
+
readonly id = "tailscale"
|
|
593
|
+
readonly name = "Tailscale"
|
|
594
|
+
readonly category = "runtime" as const
|
|
595
|
+
|
|
596
|
+
async detect() {
|
|
597
|
+
return (await exec("tailscale", ["version"])).ok
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
async getStatus() {
|
|
601
|
+
const result = await exec("tailscale", ["status", "--json"])
|
|
602
|
+
return {
|
|
603
|
+
state: result.ok ? "running" : "stopped",
|
|
604
|
+
detail: result.ok ? "connected" : "not running",
|
|
605
|
+
checkedAt: new Date(),
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
The same shape is how Azure, Vercel, GitHub, Kubernetes, remote SSH hosts or CI
|
|
612
|
+
pipelines would be added later - each is a `detect()` plus a `getStatus()`, with
|
|
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.
|
|
635
|
+
|
|
636
|
+
`pnpm test:tui` boots the actual application against OpenTUI's supported test
|
|
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.
|
|
641
|
+
|
|
642
|
+
## Known limitations
|
|
643
|
+
|
|
644
|
+
- The dashboard itself requires Bun (or Node ≥ 26.4 with `--experimental-ffi`);
|
|
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.
|