@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.
Files changed (284) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +659 -0
  3. package/bin/devcc.mjs +66 -0
  4. package/dist/devcc/app/App.d.ts +178 -0
  5. package/dist/devcc/app/App.d.ts.map +1 -0
  6. package/dist/devcc/app/App.js +1255 -0
  7. package/dist/devcc/app/App.js.map +1 -0
  8. package/dist/devcc/app/builtinCommands.d.ts +17 -0
  9. package/dist/devcc/app/builtinCommands.d.ts.map +1 -0
  10. package/dist/devcc/app/builtinCommands.js +389 -0
  11. package/dist/devcc/app/builtinCommands.js.map +1 -0
  12. package/dist/devcc/app/keymap.d.ts +21 -0
  13. package/dist/devcc/app/keymap.d.ts.map +1 -0
  14. package/dist/devcc/app/keymap.js +162 -0
  15. package/dist/devcc/app/keymap.js.map +1 -0
  16. package/dist/devcc/app/theme.d.ts +49 -0
  17. package/dist/devcc/app/theme.d.ts.map +1 -0
  18. package/dist/devcc/app/theme.js +68 -0
  19. package/dist/devcc/app/theme.js.map +1 -0
  20. package/dist/devcc/cli.d.ts +3 -0
  21. package/dist/devcc/cli.d.ts.map +1 -0
  22. package/dist/devcc/cli.js +113 -0
  23. package/dist/devcc/cli.js.map +1 -0
  24. package/dist/devcc/components/CommandPalette.d.ts +26 -0
  25. package/dist/devcc/components/CommandPalette.d.ts.map +1 -0
  26. package/dist/devcc/components/CommandPalette.js +113 -0
  27. package/dist/devcc/components/CommandPalette.js.map +1 -0
  28. package/dist/devcc/components/ConfirmDialog.d.ts +31 -0
  29. package/dist/devcc/components/ConfirmDialog.d.ts.map +1 -0
  30. package/dist/devcc/components/ConfirmDialog.js +147 -0
  31. package/dist/devcc/components/ConfirmDialog.js.map +1 -0
  32. package/dist/devcc/components/Footer.d.ts +22 -0
  33. package/dist/devcc/components/Footer.d.ts.map +1 -0
  34. package/dist/devcc/components/Footer.js +72 -0
  35. package/dist/devcc/components/Footer.js.map +1 -0
  36. package/dist/devcc/components/Header.d.ts +12 -0
  37. package/dist/devcc/components/Header.d.ts.map +1 -0
  38. package/dist/devcc/components/Header.js +84 -0
  39. package/dist/devcc/components/Header.js.map +1 -0
  40. package/dist/devcc/components/HelpOverlay.d.ts +18 -0
  41. package/dist/devcc/components/HelpOverlay.d.ts.map +1 -0
  42. package/dist/devcc/components/HelpOverlay.js +48 -0
  43. package/dist/devcc/components/HelpOverlay.js.map +1 -0
  44. package/dist/devcc/components/Overlay.d.ts +31 -0
  45. package/dist/devcc/components/Overlay.d.ts.map +1 -0
  46. package/dist/devcc/components/Overlay.js +60 -0
  47. package/dist/devcc/components/Overlay.js.map +1 -0
  48. package/dist/devcc/components/ProjectPicker.d.ts +26 -0
  49. package/dist/devcc/components/ProjectPicker.d.ts.map +1 -0
  50. package/dist/devcc/components/ProjectPicker.js +114 -0
  51. package/dist/devcc/components/ProjectPicker.js.map +1 -0
  52. package/dist/devcc/components/Sidebar.d.ts +42 -0
  53. package/dist/devcc/components/Sidebar.d.ts.map +1 -0
  54. package/dist/devcc/components/Sidebar.js +127 -0
  55. package/dist/devcc/components/Sidebar.js.map +1 -0
  56. package/dist/devcc/components/StatusBadge.d.ts +16 -0
  57. package/dist/devcc/components/StatusBadge.d.ts.map +1 -0
  58. package/dist/devcc/components/StatusBadge.js +30 -0
  59. package/dist/devcc/components/StatusBadge.js.map +1 -0
  60. package/dist/devcc/components/primitives.d.ts +62 -0
  61. package/dist/devcc/components/primitives.d.ts.map +1 -0
  62. package/dist/devcc/components/primitives.js +169 -0
  63. package/dist/devcc/components/primitives.js.map +1 -0
  64. package/dist/devcc/components/styled.d.ts +16 -0
  65. package/dist/devcc/components/styled.d.ts.map +1 -0
  66. package/dist/devcc/components/styled.js +35 -0
  67. package/dist/devcc/components/styled.js.map +1 -0
  68. package/dist/devcc/core/commands.d.ts +89 -0
  69. package/dist/devcc/core/commands.d.ts.map +1 -0
  70. package/dist/devcc/core/commands.js +68 -0
  71. package/dist/devcc/core/commands.js.map +1 -0
  72. package/dist/devcc/core/config.d.ts +81 -0
  73. package/dist/devcc/core/config.d.ts.map +1 -0
  74. package/dist/devcc/core/config.js +313 -0
  75. package/dist/devcc/core/config.js.map +1 -0
  76. package/dist/devcc/core/events.d.ts +21 -0
  77. package/dist/devcc/core/events.d.ts.map +1 -0
  78. package/dist/devcc/core/events.js +27 -0
  79. package/dist/devcc/core/events.js.map +1 -0
  80. package/dist/devcc/core/state.d.ts +104 -0
  81. package/dist/devcc/core/state.d.ts.map +1 -0
  82. package/dist/devcc/core/state.js +139 -0
  83. package/dist/devcc/core/state.js.map +1 -0
  84. package/dist/devcc/deps/DependencyService.d.ts +50 -0
  85. package/dist/devcc/deps/DependencyService.d.ts.map +1 -0
  86. package/dist/devcc/deps/DependencyService.js +109 -0
  87. package/dist/devcc/deps/DependencyService.js.map +1 -0
  88. package/dist/devcc/deps/parse.d.ts +49 -0
  89. package/dist/devcc/deps/parse.d.ts.map +1 -0
  90. package/dist/devcc/deps/parse.js +158 -0
  91. package/dist/devcc/deps/parse.js.map +1 -0
  92. package/dist/devcc/git/GitService.d.ts +60 -0
  93. package/dist/devcc/git/GitService.d.ts.map +1 -0
  94. package/dist/devcc/git/GitService.js +160 -0
  95. package/dist/devcc/git/GitService.js.map +1 -0
  96. package/dist/devcc/git/parse.d.ts +57 -0
  97. package/dist/devcc/git/parse.d.ts.map +1 -0
  98. package/dist/devcc/git/parse.js +174 -0
  99. package/dist/devcc/git/parse.js.map +1 -0
  100. package/dist/devcc/index.d.ts +37 -0
  101. package/dist/devcc/index.d.ts.map +1 -0
  102. package/dist/devcc/index.js +36 -0
  103. package/dist/devcc/index.js.map +1 -0
  104. package/dist/devcc/logs/LogBuffer.d.ts +86 -0
  105. package/dist/devcc/logs/LogBuffer.d.ts.map +1 -0
  106. package/dist/devcc/logs/LogBuffer.js +200 -0
  107. package/dist/devcc/logs/LogBuffer.js.map +1 -0
  108. package/dist/devcc/logs/merge.d.ts +36 -0
  109. package/dist/devcc/logs/merge.d.ts.map +1 -0
  110. package/dist/devcc/logs/merge.js +71 -0
  111. package/dist/devcc/logs/merge.js.map +1 -0
  112. package/dist/devcc/processes/ManagedProcess.d.ts +129 -0
  113. package/dist/devcc/processes/ManagedProcess.d.ts.map +1 -0
  114. package/dist/devcc/processes/ManagedProcess.js +80 -0
  115. package/dist/devcc/processes/ManagedProcess.js.map +1 -0
  116. package/dist/devcc/processes/ProcessManager.d.ts +112 -0
  117. package/dist/devcc/processes/ProcessManager.d.ts.map +1 -0
  118. package/dist/devcc/processes/ProcessManager.js +525 -0
  119. package/dist/devcc/processes/ProcessManager.js.map +1 -0
  120. package/dist/devcc/processes/graph.d.ts +35 -0
  121. package/dist/devcc/processes/graph.d.ts.map +1 -0
  122. package/dist/devcc/processes/graph.js +68 -0
  123. package/dist/devcc/processes/graph.js.map +1 -0
  124. package/dist/devcc/projects/ProjectRegistry.d.ts +52 -0
  125. package/dist/devcc/projects/ProjectRegistry.d.ts.map +1 -0
  126. package/dist/devcc/projects/ProjectRegistry.js +177 -0
  127. package/dist/devcc/projects/ProjectRegistry.js.map +1 -0
  128. package/dist/devcc/services/ServiceManager.d.ts +48 -0
  129. package/dist/devcc/services/ServiceManager.d.ts.map +1 -0
  130. package/dist/devcc/services/ServiceManager.js +146 -0
  131. package/dist/devcc/services/ServiceManager.js.map +1 -0
  132. package/dist/devcc/services/docker.d.ts +37 -0
  133. package/dist/devcc/services/docker.d.ts.map +1 -0
  134. package/dist/devcc/services/docker.js +179 -0
  135. package/dist/devcc/services/docker.js.map +1 -0
  136. package/dist/devcc/services/node.d.ts +40 -0
  137. package/dist/devcc/services/node.d.ts.map +1 -0
  138. package/dist/devcc/services/node.js +107 -0
  139. package/dist/devcc/services/node.js.map +1 -0
  140. package/dist/devcc/services/ollama.d.ts +27 -0
  141. package/dist/devcc/services/ollama.d.ts.map +1 -0
  142. package/dist/devcc/services/ollama.js +66 -0
  143. package/dist/devcc/services/ollama.js.map +1 -0
  144. package/dist/devcc/services/postgres.d.ts +30 -0
  145. package/dist/devcc/services/postgres.d.ts.map +1 -0
  146. package/dist/devcc/services/postgres.js +47 -0
  147. package/dist/devcc/services/postgres.js.map +1 -0
  148. package/dist/devcc/services/redis.d.ts +20 -0
  149. package/dist/devcc/services/redis.d.ts.map +1 -0
  150. package/dist/devcc/services/redis.js +39 -0
  151. package/dist/devcc/services/redis.js.map +1 -0
  152. package/dist/devcc/services/types.d.ts +51 -0
  153. package/dist/devcc/services/types.d.ts.map +1 -0
  154. package/dist/devcc/services/types.js +10 -0
  155. package/dist/devcc/services/types.js.map +1 -0
  156. package/dist/devcc/system/SystemMonitor.d.ts +87 -0
  157. package/dist/devcc/system/SystemMonitor.d.ts.map +1 -0
  158. package/dist/devcc/system/SystemMonitor.js +142 -0
  159. package/dist/devcc/system/SystemMonitor.js.map +1 -0
  160. package/dist/devcc/utils/errors.d.ts +31 -0
  161. package/dist/devcc/utils/errors.d.ts.map +1 -0
  162. package/dist/devcc/utils/errors.js +52 -0
  163. package/dist/devcc/utils/errors.js.map +1 -0
  164. package/dist/devcc/utils/exec.d.ts +41 -0
  165. package/dist/devcc/utils/exec.d.ts.map +1 -0
  166. package/dist/devcc/utils/exec.js +112 -0
  167. package/dist/devcc/utils/exec.js.map +1 -0
  168. package/dist/devcc/utils/format.d.ts +18 -0
  169. package/dist/devcc/utils/format.d.ts.map +1 -0
  170. package/dist/devcc/utils/format.js +85 -0
  171. package/dist/devcc/utils/format.js.map +1 -0
  172. package/dist/devcc/utils/fuzzy.d.ts +12 -0
  173. package/dist/devcc/utils/fuzzy.d.ts.map +1 -0
  174. package/dist/devcc/utils/fuzzy.js +20 -0
  175. package/dist/devcc/utils/fuzzy.js.map +1 -0
  176. package/dist/devcc/utils/logger.d.ts +41 -0
  177. package/dist/devcc/utils/logger.d.ts.map +1 -0
  178. package/dist/devcc/utils/logger.js +117 -0
  179. package/dist/devcc/utils/logger.js.map +1 -0
  180. package/dist/devcc/utils/packageManager.d.ts +39 -0
  181. package/dist/devcc/utils/packageManager.d.ts.map +1 -0
  182. package/dist/devcc/utils/packageManager.js +85 -0
  183. package/dist/devcc/utils/packageManager.js.map +1 -0
  184. package/dist/devcc/utils/paths.d.ts +12 -0
  185. package/dist/devcc/utils/paths.d.ts.map +1 -0
  186. package/dist/devcc/utils/paths.js +39 -0
  187. package/dist/devcc/utils/paths.js.map +1 -0
  188. package/dist/devcc/views/DependenciesView.d.ts +34 -0
  189. package/dist/devcc/views/DependenciesView.d.ts.map +1 -0
  190. package/dist/devcc/views/DependenciesView.js +170 -0
  191. package/dist/devcc/views/DependenciesView.js.map +1 -0
  192. package/dist/devcc/views/GitView.d.ts +29 -0
  193. package/dist/devcc/views/GitView.d.ts.map +1 -0
  194. package/dist/devcc/views/GitView.js +226 -0
  195. package/dist/devcc/views/GitView.js.map +1 -0
  196. package/dist/devcc/views/LogsView.d.ts +39 -0
  197. package/dist/devcc/views/LogsView.d.ts.map +1 -0
  198. package/dist/devcc/views/LogsView.js +249 -0
  199. package/dist/devcc/views/LogsView.js.map +1 -0
  200. package/dist/devcc/views/OverviewView.d.ts +16 -0
  201. package/dist/devcc/views/OverviewView.d.ts.map +1 -0
  202. package/dist/devcc/views/OverviewView.js +135 -0
  203. package/dist/devcc/views/OverviewView.js.map +1 -0
  204. package/dist/devcc/views/ProcessesView.d.ts +26 -0
  205. package/dist/devcc/views/ProcessesView.d.ts.map +1 -0
  206. package/dist/devcc/views/ProcessesView.js +161 -0
  207. package/dist/devcc/views/ProcessesView.js.map +1 -0
  208. package/dist/devcc/views/ScriptsView.d.ts +30 -0
  209. package/dist/devcc/views/ScriptsView.d.ts.map +1 -0
  210. package/dist/devcc/views/ScriptsView.js +168 -0
  211. package/dist/devcc/views/ScriptsView.js.map +1 -0
  212. package/dist/devcc/views/ServicesView.d.ts +28 -0
  213. package/dist/devcc/views/ServicesView.d.ts.map +1 -0
  214. package/dist/devcc/views/ServicesView.js +153 -0
  215. package/dist/devcc/views/ServicesView.js.map +1 -0
  216. package/dist/devcc/views/SettingsView.d.ts +20 -0
  217. package/dist/devcc/views/SettingsView.d.ts.map +1 -0
  218. package/dist/devcc/views/SettingsView.js +114 -0
  219. package/dist/devcc/views/SettingsView.js.map +1 -0
  220. package/dist/devcc/views/SystemView.d.ts +13 -0
  221. package/dist/devcc/views/SystemView.d.ts.map +1 -0
  222. package/dist/devcc/views/SystemView.js +82 -0
  223. package/dist/devcc/views/SystemView.js.map +1 -0
  224. package/dist/devcc/views/View.d.ts +35 -0
  225. package/dist/devcc/views/View.d.ts.map +1 -0
  226. package/dist/devcc/views/View.js +42 -0
  227. package/dist/devcc/views/View.js.map +1 -0
  228. package/package.json +79 -0
  229. package/src/devcc/app/App.ts +1463 -0
  230. package/src/devcc/app/builtinCommands.ts +429 -0
  231. package/src/devcc/app/keymap.ts +167 -0
  232. package/src/devcc/app/theme.ts +83 -0
  233. package/src/devcc/cli.ts +122 -0
  234. package/src/devcc/components/CommandPalette.ts +136 -0
  235. package/src/devcc/components/ConfirmDialog.ts +174 -0
  236. package/src/devcc/components/Footer.ts +85 -0
  237. package/src/devcc/components/Header.ts +93 -0
  238. package/src/devcc/components/HelpOverlay.ts +62 -0
  239. package/src/devcc/components/Overlay.ts +91 -0
  240. package/src/devcc/components/ProjectPicker.ts +138 -0
  241. package/src/devcc/components/Sidebar.ts +163 -0
  242. package/src/devcc/components/StatusBadge.ts +36 -0
  243. package/src/devcc/components/primitives.ts +213 -0
  244. package/src/devcc/components/styled.ts +44 -0
  245. package/src/devcc/core/commands.ts +140 -0
  246. package/src/devcc/core/config.ts +405 -0
  247. package/src/devcc/core/events.ts +41 -0
  248. package/src/devcc/core/state.ts +236 -0
  249. package/src/devcc/deps/DependencyService.ts +152 -0
  250. package/src/devcc/deps/parse.ts +210 -0
  251. package/src/devcc/git/GitService.ts +205 -0
  252. package/src/devcc/git/parse.ts +203 -0
  253. package/src/devcc/index.ts +115 -0
  254. package/src/devcc/logs/LogBuffer.ts +237 -0
  255. package/src/devcc/logs/merge.ts +87 -0
  256. package/src/devcc/processes/ManagedProcess.ts +195 -0
  257. package/src/devcc/processes/ProcessManager.ts +625 -0
  258. package/src/devcc/processes/graph.ts +98 -0
  259. package/src/devcc/projects/ProjectRegistry.ts +219 -0
  260. package/src/devcc/services/ServiceManager.ts +180 -0
  261. package/src/devcc/services/docker.ts +220 -0
  262. package/src/devcc/services/node.ts +137 -0
  263. package/src/devcc/services/ollama.ts +80 -0
  264. package/src/devcc/services/postgres.ts +57 -0
  265. package/src/devcc/services/redis.ts +47 -0
  266. package/src/devcc/services/types.ts +59 -0
  267. package/src/devcc/system/SystemMonitor.ts +207 -0
  268. package/src/devcc/utils/errors.ts +64 -0
  269. package/src/devcc/utils/exec.ts +165 -0
  270. package/src/devcc/utils/format.ts +83 -0
  271. package/src/devcc/utils/fuzzy.ts +44 -0
  272. package/src/devcc/utils/logger.ts +140 -0
  273. package/src/devcc/utils/packageManager.ts +112 -0
  274. package/src/devcc/utils/paths.ts +39 -0
  275. package/src/devcc/views/DependenciesView.ts +206 -0
  276. package/src/devcc/views/GitView.ts +259 -0
  277. package/src/devcc/views/LogsView.ts +300 -0
  278. package/src/devcc/views/OverviewView.ts +187 -0
  279. package/src/devcc/views/ProcessesView.ts +177 -0
  280. package/src/devcc/views/ScriptsView.ts +187 -0
  281. package/src/devcc/views/ServicesView.ts +188 -0
  282. package/src/devcc/views/SettingsView.ts +146 -0
  283. package/src/devcc/views/SystemView.ts +110 -0
  284. 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.