@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
@@ -0,0 +1,429 @@
1
+ import type { CommandConfigEntry } from "../core/config.js"
2
+ import type { CommandContext, CommandDefinition } from "../core/commands.js"
3
+ import { isActive } from "../processes/ManagedProcess.js"
4
+ import { VIEW_IDS } from "../core/state.js"
5
+ import { selectedPackageIndex } from "../views/DependenciesView.js"
6
+ import { errorMessage } from "../utils/errors.js"
7
+
8
+ /**
9
+ * The built-in command set.
10
+ *
11
+ * Everything the UI can do is registered here (or contributed by config), so
12
+ * the palette is a complete inventory of the app's capabilities rather than a
13
+ * subset of them.
14
+ */
15
+ export function createBuiltinCommands(): CommandDefinition[] {
16
+ const commands: CommandDefinition[] = []
17
+
18
+ // --- Navigation ---------------------------------------------------------
19
+ for (const view of VIEW_IDS) {
20
+ commands.push({
21
+ id: `view.${view}`,
22
+ title: `Go to ${titleCase(view)}`,
23
+ category: "Navigation",
24
+ keywords: ["open", "show", view],
25
+ execute: (context) => context.navigate(view),
26
+ })
27
+ }
28
+
29
+ // --- Processes ----------------------------------------------------------
30
+ commands.push(
31
+ {
32
+ id: "process.startAll",
33
+ title: "Start all managed processes (in dependency order)",
34
+ category: "Processes",
35
+ keywords: ["run", "boot", "up", "stack"],
36
+ execute: async (context) => {
37
+ const before = context.processes.list().filter((process) => !isActive(process.status)).length
38
+ if (before === 0) {
39
+ context.notify("info", "Everything is already running")
40
+ return
41
+ }
42
+ const plan = await context.processes.startAll()
43
+ const summary = plan.waves.map((wave) => wave.join("+")).join(" → ")
44
+ context.notify(
45
+ plan.cycle.length > 0 ? "warn" : "success",
46
+ `Started ${before} process(es): ${summary}`,
47
+ )
48
+ },
49
+ },
50
+ {
51
+ id: "process.stopAll",
52
+ title: "Stop all managed processes",
53
+ category: "Processes",
54
+ keywords: ["kill", "halt", "down"],
55
+ confirm: {
56
+ title: "Stop all managed processes?",
57
+ message: "Every process devcc started will receive SIGTERM, then SIGKILL after the grace period.",
58
+ },
59
+ execute: async (context) => {
60
+ await context.processes.stopAll()
61
+ context.notify("info", "Stopped all managed processes")
62
+ },
63
+ },
64
+ {
65
+ id: "process.restartAll",
66
+ title: "Restart all running processes",
67
+ category: "Processes",
68
+ keywords: ["reload", "bounce"],
69
+ execute: async (context) => {
70
+ const running = context.processes.list().filter((process) => isActive(process.status))
71
+ for (const process of running) {
72
+ try {
73
+ await context.processes.restart(process.id)
74
+ } catch (error) {
75
+ context.notify("error", errorMessage(error))
76
+ }
77
+ }
78
+ context.notify("success", `Restarted ${running.length} process(es)`)
79
+ },
80
+ },
81
+ )
82
+
83
+ // --- Git ----------------------------------------------------------------
84
+ commands.push(
85
+ {
86
+ id: "git.refresh",
87
+ title: "Git: refresh status",
88
+ category: "Git",
89
+ shortcut: "r",
90
+ execute: async (context) => {
91
+ await context.git.refresh()
92
+ context.notify("info", "Git status refreshed")
93
+ },
94
+ },
95
+ {
96
+ id: "git.fetch",
97
+ title: "Git: fetch",
98
+ category: "Git",
99
+ keywords: ["remote", "prune"],
100
+ execute: async (context) => {
101
+ context.notify("info", "Fetching…")
102
+ try {
103
+ await context.git.fetch()
104
+ context.notify("success", "Fetched from remote")
105
+ } catch (error) {
106
+ context.notify("error", errorMessage(error))
107
+ }
108
+ },
109
+ },
110
+ {
111
+ id: "git.pull",
112
+ title: "Git: pull (fast-forward only)",
113
+ category: "Git",
114
+ execute: async (context) => {
115
+ context.notify("info", "Pulling…")
116
+ try {
117
+ await context.git.pull()
118
+ context.notify("success", "Pulled")
119
+ } catch (error) {
120
+ context.notify("error", errorMessage(error))
121
+ }
122
+ },
123
+ },
124
+ {
125
+ id: "git.push",
126
+ title: "Git: push",
127
+ category: "Git",
128
+ confirm: {
129
+ title: "Push to remote?",
130
+ message: "This publishes your local commits. Make sure the branch is what you expect.",
131
+ },
132
+ execute: async (context) => {
133
+ context.notify("info", "Pushing…")
134
+ try {
135
+ await context.git.push()
136
+ context.notify("success", "Pushed")
137
+ } catch (error) {
138
+ context.notify("error", errorMessage(error))
139
+ }
140
+ },
141
+ },
142
+ {
143
+ id: "git.stageAll",
144
+ title: "Git: stage all changes",
145
+ category: "Git",
146
+ execute: async (context) => {
147
+ try {
148
+ await context.git.stageAll()
149
+ context.notify("success", "Staged all changes")
150
+ } catch (error) {
151
+ context.notify("error", errorMessage(error))
152
+ }
153
+ },
154
+ },
155
+ {
156
+ id: "git.commit",
157
+ title: "Git: commit staged changes",
158
+ category: "Git",
159
+ execute: async (context) => {
160
+ const message = await context.prompt("Commit message", "Describe the change")
161
+ if (message === null || message.trim() === "") return
162
+ try {
163
+ await context.git.commit(message)
164
+ context.notify("success", "Committed")
165
+ } catch (error) {
166
+ context.notify("error", errorMessage(error))
167
+ }
168
+ },
169
+ },
170
+ )
171
+
172
+ // --- Docker -------------------------------------------------------------
173
+ commands.push(
174
+ {
175
+ id: "docker.up",
176
+ title: "Docker: compose up (detached)",
177
+ category: "Docker",
178
+ when: (context) => context.services.get("docker") !== undefined,
179
+ execute: async (context) => {
180
+ context.notify("info", "docker compose up…")
181
+ try {
182
+ await context.services.runAction("docker", "start", { confirm: context.confirm })
183
+ context.notify("success", "Compose services started")
184
+ } catch (error) {
185
+ context.notify("error", errorMessage(error))
186
+ }
187
+ },
188
+ },
189
+ {
190
+ id: "docker.stop",
191
+ title: "Docker: compose stop",
192
+ category: "Docker",
193
+ when: (context) => context.services.get("docker") !== undefined,
194
+ execute: async (context) => {
195
+ try {
196
+ await context.services.runAction("docker", "stop", { confirm: context.confirm })
197
+ context.notify("success", "Compose services stopped")
198
+ } catch (error) {
199
+ context.notify("error", errorMessage(error))
200
+ }
201
+ },
202
+ },
203
+ {
204
+ id: "docker.down",
205
+ title: "Docker: compose down",
206
+ category: "Docker",
207
+ keywords: ["remove", "destroy"],
208
+ when: (context) => context.services.get("docker") !== undefined,
209
+ confirm: {
210
+ title: "docker compose down?",
211
+ message: "This removes the containers and networks compose created. Volumes are kept.",
212
+ },
213
+ execute: async (context) => {
214
+ const adapter = context.services.getAdapter("docker")
215
+ if (!adapter || !("down" in adapter)) {
216
+ context.notify("warn", "Docker adapter unavailable")
217
+ return
218
+ }
219
+ try {
220
+ // The adapter asks for its own confirmation too; auto-approve the second one.
221
+ await (adapter as { down(ctx: { confirm: () => Promise<boolean> }): Promise<void> }).down({
222
+ confirm: async () => true,
223
+ })
224
+ context.notify("success", "Compose stack removed")
225
+ } catch (error) {
226
+ context.notify("error", errorMessage(error))
227
+ }
228
+ },
229
+ },
230
+ )
231
+
232
+ // --- Dependencies -------------------------------------------------------
233
+ commands.push(
234
+ {
235
+ id: "deps.check",
236
+ title: "Dependencies: check for updates",
237
+ category: "Dependencies",
238
+ shortcut: "r",
239
+ keywords: ["outdated", "upgrade", "npm", "registry"],
240
+ execute: async (context) => {
241
+ context.navigate("dependencies")
242
+ context.notify("info", "Checking the registry…")
243
+ const snapshot = await context.deps.check()
244
+ if (snapshot.error) return
245
+ context.notify(
246
+ snapshot.packages.length === 0 ? "success" : "info",
247
+ snapshot.packages.length === 0
248
+ ? "All dependencies are up to date"
249
+ : `${snapshot.packages.length} outdated package(s)`,
250
+ )
251
+ },
252
+ },
253
+ {
254
+ id: "deps.updateSelected",
255
+ title: "Dependencies: update the selected package",
256
+ category: "Dependencies",
257
+ keywords: ["upgrade", "bump"],
258
+ when: (context) => context.store.getState().dependencies.packages.length > 0,
259
+ execute: async (context) => {
260
+ const state = context.store.getState()
261
+ const target = state.dependencies.packages[selectedPackageIndex(state.ui.selection.dependencies)]
262
+ if (!target) {
263
+ context.notify("warn", "No package selected")
264
+ return
265
+ }
266
+ await context.updateDependency(target.name, target.latest)
267
+ },
268
+ },
269
+ )
270
+
271
+ // --- Projects -----------------------------------------------------------
272
+ commands.push(
273
+ {
274
+ id: "project.next",
275
+ title: "Next open project",
276
+ category: "Project",
277
+ shortcut: "ctrl+o",
278
+ keywords: ["cycle", "tab", "workspace"],
279
+ when: (context) => context.openProjects().length > 1,
280
+ execute: async (context) => {
281
+ const open = context.openProjects()
282
+ const index = open.findIndex((entry) => entry.source === "current")
283
+ const next = open[(index + 1) % open.length]
284
+ if (next) await context.switchProject(next.path)
285
+ },
286
+ },
287
+ {
288
+ id: "project.close",
289
+ title: "Close current project",
290
+ category: "Project",
291
+ keywords: ["remove", "exit", "workspace"],
292
+ when: (context) => context.openProjects().length > 1,
293
+ execute: async (context) => {
294
+ await context.closeProject()
295
+ },
296
+ },
297
+ )
298
+ commands.push({
299
+ id: "project.switch",
300
+ title: "Open project…",
301
+ category: "Project",
302
+ shortcut: "ctrl+p",
303
+ keywords: ["open", "workspace", "repo", "cd"],
304
+ execute: async (context) => {
305
+ const projects = context.listProjects().filter((entry) => entry.source !== "current")
306
+ if (projects.length === 0) {
307
+ context.notify("warn", "No other projects found nearby")
308
+ return
309
+ }
310
+ const answer = await context.prompt("Switch to project", projects[0]?.name ?? "path or name")
311
+ if (answer === null || answer.trim() === "") return
312
+
313
+ const query = answer.trim().toLowerCase()
314
+ const match =
315
+ projects.find((entry) => entry.path.toLowerCase() === query) ??
316
+ projects.find((entry) => entry.name.toLowerCase() === query) ??
317
+ projects.find((entry) => entry.name.toLowerCase().includes(query))
318
+
319
+ if (!match) {
320
+ context.notify("error", `No project matching "${answer}"`)
321
+ return
322
+ }
323
+ await context.switchProject(match.path)
324
+ },
325
+ })
326
+
327
+ // --- Logs & app ---------------------------------------------------------
328
+ commands.push(
329
+ {
330
+ id: "logs.clearCurrent",
331
+ title: "Logs: clear current source",
332
+ category: "Logs",
333
+ execute: (context) => {
334
+ const source = context.store.getState().ui.logSource
335
+ if (!source) {
336
+ context.notify("warn", "No log source selected")
337
+ return
338
+ }
339
+ context.logs.clear(source)
340
+ context.notify("info", "Cleared logs")
341
+ },
342
+ },
343
+ {
344
+ id: "logs.clearAll",
345
+ title: "Logs: clear all sources",
346
+ category: "Logs",
347
+ confirm: {
348
+ title: "Clear every log buffer?",
349
+ message: "Buffered output for all sources will be discarded.",
350
+ },
351
+ execute: (context) => {
352
+ context.logs.clearAll()
353
+ context.notify("info", "Cleared all logs")
354
+ },
355
+ },
356
+ {
357
+ id: "app.refresh",
358
+ title: "Refresh everything",
359
+ category: "App",
360
+ keywords: ["reload", "poll"],
361
+ execute: async (context) => {
362
+ await Promise.all([context.git.refresh(), context.services.refresh(), context.system.sample()])
363
+ context.notify("info", "Refreshed")
364
+ },
365
+ },
366
+ {
367
+ id: "help.manual",
368
+ title: "Open the manual",
369
+ category: "App",
370
+ shortcut: "? / F1 / ctrl+k",
371
+ keywords: ["help", "docs", "documentation", "keys", "shortcuts", "how", "guide", "readme"],
372
+ execute: (context) => context.showHelp(),
373
+ },
374
+ {
375
+ id: "app.quit",
376
+ title: "Quit Dev Command Center",
377
+ category: "App",
378
+ shortcut: "q",
379
+ keywords: ["exit", "close"],
380
+ execute: (context) => context.quit(),
381
+ },
382
+ )
383
+
384
+ return commands
385
+ }
386
+
387
+ /** Turn `package.json` scripts into palette commands. */
388
+ export function createScriptCommands(scripts: readonly { name: string }[]): CommandDefinition[] {
389
+ return scripts.map((script) => ({
390
+ id: `script.${script.name}`,
391
+ title: `Run script: ${script.name}`,
392
+ category: "Scripts",
393
+ keywords: ["npm", "run", script.name],
394
+ execute: async (context: CommandContext) => {
395
+ await context.runScript(script.name)
396
+ },
397
+ }))
398
+ }
399
+
400
+ /** Turn config-declared commands into palette commands. */
401
+ export function createConfigCommands(
402
+ entries: readonly CommandConfigEntry[],
403
+ run: (entry: CommandConfigEntry) => Promise<void>,
404
+ ): CommandDefinition[] {
405
+ return entries.map((entry) => ({
406
+ id: `config.${entry.id}`,
407
+ title: entry.title,
408
+ category: entry.category ?? "Project",
409
+ keywords: entry.keywords,
410
+ confirm: resolveConfirm(entry),
411
+ execute: async () => {
412
+ await run(entry)
413
+ },
414
+ }))
415
+ }
416
+
417
+ function resolveConfirm(entry: CommandConfigEntry): CommandDefinition["confirm"] {
418
+ if (entry.confirm === true) {
419
+ return {
420
+ title: `Run "${entry.title}"?`,
421
+ message: `${entry.command} ${(entry.args ?? []).join(" ")}`.trim(),
422
+ }
423
+ }
424
+ return entry.confirm === undefined || entry.confirm === false ? undefined : entry.confirm
425
+ }
426
+
427
+ function titleCase(value: string): string {
428
+ return value.charAt(0).toUpperCase() + value.slice(1)
429
+ }
@@ -0,0 +1,167 @@
1
+ import type { HelpSection } from "../components/HelpOverlay.js"
2
+ import type { ViewId } from "../core/state.js"
3
+
4
+ /** Global single-key bindings that jump straight to a view. */
5
+ export const VIEW_SHORTCUTS: Readonly<Record<string, ViewId>> = {
6
+ "1": "overview",
7
+ "2": "processes",
8
+ "3": "services",
9
+ "4": "scripts",
10
+ "5": "git",
11
+ "6": "logs",
12
+ "7": "dependencies",
13
+ "8": "system",
14
+ "9": "settings",
15
+ p: "processes",
16
+ g: "git",
17
+ l: "logs",
18
+ d: "dependencies",
19
+ }
20
+
21
+ /**
22
+ * The hint shown next to a section in the sidebar: its number, plus the
23
+ * mnemonic letter when it has one (`"7 d"`).
24
+ *
25
+ * Derived from the bindings above so the rail can never advertise a key that
26
+ * is not actually bound, or omit one that is.
27
+ */
28
+ export function shortcutHintFor(view: ViewId): string {
29
+ const keys = Object.entries(VIEW_SHORTCUTS)
30
+ .filter(([, target]) => target === view)
31
+ .map(([key]) => key)
32
+
33
+ const digits = keys.filter((key) => /^\d$/u.test(key)).sort()
34
+ const letters = keys.filter((key) => !/^\d$/u.test(key)).sort()
35
+ return [...digits, ...letters].join(" ")
36
+ }
37
+
38
+ /**
39
+ * The in-app manual (`?`, `F1`, or "Open the manual" in the palette).
40
+ *
41
+ * Starts with what each section is for, then the keys, then the config fields
42
+ * you are most likely to want. Kept next to the bindings it documents so the
43
+ * two cannot drift apart.
44
+ */
45
+ export const HELP_SECTIONS: HelpSection[] = [
46
+ {
47
+ title: "What each section does",
48
+ entries: [
49
+ { keys: "Overview", description: "git, running processes, services and load at a glance" },
50
+ { keys: "Processes", description: "dev servers devcc launched: start, stop, restart, inspect" },
51
+ { keys: "Services", description: "things detected on this machine: Docker, databases, Ollama" },
52
+ { keys: "Scripts", description: "package.json scripts, run through your package manager" },
53
+ { keys: "Git", description: "branch, changes, commits; stage, commit, pull, push" },
54
+ { keys: "Logs", description: "output of everything devcc started, per source or merged" },
55
+ { keys: "Dependencies", description: "outdated packages, checked only when you ask" },
56
+ { keys: "System", description: "host, toolchain versions, CPU, memory, disk" },
57
+ { keys: "Settings", description: "detection results, config issues, diagnostics history" },
58
+ ],
59
+ },
60
+ {
61
+ title: "Getting started",
62
+ entries: [
63
+ { keys: ".devcc.ts", description: "declare processes devcc can control (see Config below)" },
64
+ { keys: "ctrl+p", description: "open another project; the current one keeps running" },
65
+ { keys: "/", description: "the palette lists everything the app can do" },
66
+ { keys: ".devcc/devcc.log", description: "internal diagnostics, if something looks wrong" },
67
+ ],
68
+ },
69
+ {
70
+ title: "Navigation",
71
+ entries: [
72
+ { keys: "↑ / k", description: "previous item" },
73
+ { keys: "↓ / j", description: "next item" },
74
+ { keys: "← / h", description: "previous section" },
75
+ { keys: "→", description: "next section" },
76
+ { keys: "tab / S-tab", description: "cycle sections" },
77
+ { keys: "1…9", description: "jump to a section" },
78
+ { keys: "p / g / l / d", description: "processes / git / logs / dependencies" },
79
+ { keys: "enter", description: "open or activate" },
80
+ { keys: "esc", description: "close overlay / back" },
81
+ ],
82
+ },
83
+ {
84
+ title: "Processes & services",
85
+ entries: [
86
+ { keys: "s", description: "start or stop the selection" },
87
+ { keys: "r", description: "restart the selection" },
88
+ { keys: "i", description: "inspect the selection" },
89
+ { keys: "d", description: "docker compose down (confirmed)" },
90
+ ],
91
+ },
92
+ {
93
+ title: "Scripts",
94
+ entries: [
95
+ { keys: "enter", description: "run the selected script" },
96
+ { keys: "x", description: "stop a running script" },
97
+ { keys: "f", description: "filter the list" },
98
+ { keys: "c", description: "clear the filter" },
99
+ ],
100
+ },
101
+ {
102
+ title: "Git",
103
+ entries: [
104
+ { keys: "s / u", description: "stage / unstage the selected file" },
105
+ { keys: "d", description: "discard file changes (confirmed)" },
106
+ { keys: "c", description: "commit staged changes" },
107
+ { keys: "p / P", description: "pull / push (push is confirmed)" },
108
+ { keys: "f", description: "fetch" },
109
+ { keys: "r", description: "refresh status" },
110
+ ],
111
+ },
112
+ {
113
+ title: "Logs",
114
+ entries: [
115
+ { keys: "j / k", description: "scroll" },
116
+ { keys: "PgUp / PgDn", description: "page" },
117
+ { keys: "home / end", description: "jump to start / end" },
118
+ { keys: "f", description: "toggle follow" },
119
+ { keys: "s", description: "search (text or regex)" },
120
+ { keys: "x", description: "clear the search" },
121
+ { keys: "e", description: "toggle errors only" },
122
+ { keys: "a", description: "merged stream of all sources" },
123
+ { keys: "tab / ← →", description: "change log source" },
124
+ { keys: "c", description: "clear the current buffer" },
125
+ ],
126
+ },
127
+ {
128
+ title: "Dependencies",
129
+ entries: [
130
+ { keys: "r", description: "check the registry for updates" },
131
+ { keys: "u", description: "update the selected package (confirmed)" },
132
+ ],
133
+ },
134
+ {
135
+ title: "Projects",
136
+ entries: [
137
+ { keys: "ctrl+p", description: "open another project (others stay running)" },
138
+ { keys: "ctrl+o", description: "cycle through open projects" },
139
+ { keys: "enter", description: "open the highlighted project" },
140
+ { keys: "/ close", description: "close the current project (palette)" },
141
+ ],
142
+ },
143
+ {
144
+ title: "Global",
145
+ entries: [
146
+ { keys: "? / F1 / ctrl+k", description: "this manual" },
147
+ { keys: "/", description: "command palette" },
148
+ { keys: "ctrl+p / ctrl+o", description: "open / cycle projects" },
149
+ { keys: "?", description: "this help" },
150
+ { keys: "R", description: "refresh everything" },
151
+ { keys: "q / ctrl+c", description: "quit" },
152
+ ],
153
+ },
154
+ {
155
+ title: "Config quick reference (.devcc.ts)",
156
+ entries: [
157
+ { keys: "processes[]", description: "id, name, command, args, cwd, autoStart, url, port" },
158
+ { keys: " restartOnCrash", description: "auto-restart with backoff; maxCrashRestarts caps it" },
159
+ { keys: " dependsOn", description: "ids that must be ready first (start all / autostart)" },
160
+ { keys: " readiness", description: "{ port } or { logPattern }: running means usable" },
161
+ { keys: "commands[]", description: "id, title, command, args, confirm - shown in the palette" },
162
+ { keys: "services[]", description: "extra ports to watch: id, name, port, healthPath" },
163
+ { keys: "projects[]", description: "extra paths offered by the project switcher" },
164
+ { keys: "refresh", description: "{ system, services, git, clock } intervals in ms" },
165
+ ],
166
+ },
167
+ ]
@@ -0,0 +1,83 @@
1
+ import type { ProcessStatus } from "../processes/ManagedProcess.js"
2
+ import type { ServiceState } from "../services/types.js"
3
+
4
+ /**
5
+ * One restrained palette for the whole app.
6
+ *
7
+ * Colour carries meaning only: state (running/stopped/failed), selection, and a
8
+ * single accent for headings. Everything else is one of three greys.
9
+ */
10
+ export const theme = {
11
+ background: "#0b0f14",
12
+ panel: "#0f141b",
13
+ panelAlt: "#131a23",
14
+ border: "#243040",
15
+ borderFocus: "#3b6ea5",
16
+
17
+ text: "#c6d0da",
18
+ textDim: "#6b7885",
19
+ textBright: "#eaf0f6",
20
+
21
+ accent: "#6aa9ff",
22
+ success: "#4cc38a",
23
+ warning: "#d9a441",
24
+ danger: "#f2685e",
25
+ info: "#6aa9ff",
26
+
27
+ selectionBackground: "#1d3b63",
28
+ selectionText: "#ffffff",
29
+
30
+ headerBackground: "#131a23",
31
+ footerBackground: "#131a23",
32
+ } as const
33
+
34
+ export const glyphs = {
35
+ running: "●",
36
+ stopped: "○",
37
+ starting: "◐",
38
+ stopping: "◑",
39
+ failed: "✖",
40
+ degraded: "◍",
41
+ unknown: "·",
42
+ bullet: "•",
43
+ arrowRight: "›",
44
+ check: "✓",
45
+ cross: "✗",
46
+ warning: "⚠",
47
+ } as const
48
+
49
+ export interface StatusStyle {
50
+ glyph: string
51
+ color: string
52
+ label: string
53
+ }
54
+
55
+ export function processStatusStyle(status: ProcessStatus): StatusStyle {
56
+ switch (status) {
57
+ case "running":
58
+ return { glyph: glyphs.running, color: theme.success, label: "running" }
59
+ case "starting":
60
+ return { glyph: glyphs.starting, color: theme.warning, label: "starting" }
61
+ case "stopping":
62
+ return { glyph: glyphs.stopping, color: theme.warning, label: "stopping" }
63
+ case "failed":
64
+ return { glyph: glyphs.failed, color: theme.danger, label: "failed" }
65
+ case "stopped":
66
+ return { glyph: glyphs.stopped, color: theme.textDim, label: "stopped" }
67
+ }
68
+ }
69
+
70
+ export function serviceStateStyle(state: ServiceState): StatusStyle {
71
+ switch (state) {
72
+ case "running":
73
+ return { glyph: glyphs.running, color: theme.success, label: "running" }
74
+ case "degraded":
75
+ return { glyph: glyphs.degraded, color: theme.warning, label: "degraded" }
76
+ case "stopped":
77
+ return { glyph: glyphs.stopped, color: theme.textDim, label: "stopped" }
78
+ case "unavailable":
79
+ return { glyph: glyphs.cross, color: theme.danger, label: "unavailable" }
80
+ case "unknown":
81
+ return { glyph: glyphs.unknown, color: theme.textDim, label: "unknown" }
82
+ }
83
+ }