dev-cockpit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +131 -0
  4. package/bin/dev-cockpit.mjs +20 -0
  5. package/dist/buildCli.d.ts +17 -0
  6. package/dist/buildCli.d.ts.map +1 -0
  7. package/dist/buildCli.js +107 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +2 -0
  11. package/dist/cockpit/Cockpit.d.ts +33 -0
  12. package/dist/cockpit/Cockpit.d.ts.map +1 -0
  13. package/dist/cockpit/Cockpit.js +73 -0
  14. package/dist/cockpit/Footer.d.ts +22 -0
  15. package/dist/cockpit/Footer.d.ts.map +1 -0
  16. package/dist/cockpit/Footer.js +33 -0
  17. package/dist/cockpit/TabBar.d.ts +3 -0
  18. package/dist/cockpit/TabBar.d.ts.map +1 -0
  19. package/dist/cockpit/TabBar.js +12 -0
  20. package/dist/cockpit/help/content.d.ts +12 -0
  21. package/dist/cockpit/help/content.d.ts.map +1 -0
  22. package/dist/cockpit/help/content.js +22 -0
  23. package/dist/cockpit/help/loader.d.ts +65 -0
  24. package/dist/cockpit/help/loader.d.ts.map +1 -0
  25. package/dist/cockpit/help/loader.js +118 -0
  26. package/dist/cockpit/help/renderer.d.ts +16 -0
  27. package/dist/cockpit/help/renderer.d.ts.map +1 -0
  28. package/dist/cockpit/help/renderer.js +35 -0
  29. package/dist/cockpit/help/types.d.ts +12 -0
  30. package/dist/cockpit/help/types.d.ts.map +1 -0
  31. package/dist/cockpit/help/types.js +1 -0
  32. package/dist/cockpit/hooks/useCockpitStore.d.ts +3 -0
  33. package/dist/cockpit/hooks/useCockpitStore.d.ts.map +1 -0
  34. package/dist/cockpit/hooks/useCockpitStore.js +5 -0
  35. package/dist/cockpit/hooks/useGlobalKeys.d.ts +56 -0
  36. package/dist/cockpit/hooks/useGlobalKeys.d.ts.map +1 -0
  37. package/dist/cockpit/hooks/useGlobalKeys.js +173 -0
  38. package/dist/cockpit/panes/FilterModal.d.ts +3 -0
  39. package/dist/cockpit/panes/FilterModal.d.ts.map +1 -0
  40. package/dist/cockpit/panes/FilterModal.js +22 -0
  41. package/dist/cockpit/panes/Health.d.ts +13 -0
  42. package/dist/cockpit/panes/Health.d.ts.map +1 -0
  43. package/dist/cockpit/panes/Health.js +30 -0
  44. package/dist/cockpit/panes/Help.d.ts +14 -0
  45. package/dist/cockpit/panes/Help.d.ts.map +1 -0
  46. package/dist/cockpit/panes/Help.js +81 -0
  47. package/dist/cockpit/panes/Output.d.ts +14 -0
  48. package/dist/cockpit/panes/Output.d.ts.map +1 -0
  49. package/dist/cockpit/panes/Output.js +108 -0
  50. package/dist/cockpit/panes/Repos.d.ts +3 -0
  51. package/dist/cockpit/panes/Repos.d.ts.map +1 -0
  52. package/dist/cockpit/panes/Repos.js +48 -0
  53. package/dist/cockpit/panes/SearchModal.d.ts +3 -0
  54. package/dist/cockpit/panes/SearchModal.d.ts.map +1 -0
  55. package/dist/cockpit/panes/SearchModal.js +31 -0
  56. package/dist/cockpit/state/store.d.ts +93 -0
  57. package/dist/cockpit/state/store.d.ts.map +1 -0
  58. package/dist/cockpit/state/store.js +111 -0
  59. package/dist/cockpit/tab-state.d.ts +4 -0
  60. package/dist/cockpit/tab-state.d.ts.map +1 -0
  61. package/dist/cockpit/tab-state.js +7 -0
  62. package/dist/commands/dev.d.ts +20 -0
  63. package/dist/commands/dev.d.ts.map +1 -0
  64. package/dist/commands/dev.js +158 -0
  65. package/dist/commands/doctor.d.ts +20 -0
  66. package/dist/commands/doctor.d.ts.map +1 -0
  67. package/dist/commands/doctor.js +66 -0
  68. package/dist/commands/init-config-wizard.d.ts +84 -0
  69. package/dist/commands/init-config-wizard.d.ts.map +1 -0
  70. package/dist/commands/init-config-wizard.js +818 -0
  71. package/dist/commands/init-config.d.ts +35 -0
  72. package/dist/commands/init-config.d.ts.map +1 -0
  73. package/dist/commands/init-config.js +131 -0
  74. package/dist/commands/mount.d.ts +48 -0
  75. package/dist/commands/mount.d.ts.map +1 -0
  76. package/dist/commands/mount.js +150 -0
  77. package/dist/core/config.d.ts +391 -0
  78. package/dist/core/config.d.ts.map +1 -0
  79. package/dist/core/config.js +152 -0
  80. package/dist/core/logger.d.ts +6 -0
  81. package/dist/core/logger.d.ts.map +1 -0
  82. package/dist/core/logger.js +38 -0
  83. package/dist/core/notifier.d.ts +23 -0
  84. package/dist/core/notifier.d.ts.map +1 -0
  85. package/dist/core/notifier.js +100 -0
  86. package/dist/core/paths.d.ts +15 -0
  87. package/dist/core/paths.d.ts.map +1 -0
  88. package/dist/core/paths.js +18 -0
  89. package/dist/core/subprocess.d.ts +20 -0
  90. package/dist/core/subprocess.d.ts.map +1 -0
  91. package/dist/core/subprocess.js +82 -0
  92. package/dist/core/types.d.ts +125 -0
  93. package/dist/core/types.d.ts.map +1 -0
  94. package/dist/core/types.js +1 -0
  95. package/dist/docker/highlights.d.ts +48 -0
  96. package/dist/docker/highlights.d.ts.map +1 -0
  97. package/dist/docker/highlights.js +79 -0
  98. package/dist/docker/logs.d.ts +84 -0
  99. package/dist/docker/logs.d.ts.map +1 -0
  100. package/dist/docker/logs.js +172 -0
  101. package/dist/docker/restart.d.ts +26 -0
  102. package/dist/docker/restart.d.ts.map +1 -0
  103. package/dist/docker/restart.js +45 -0
  104. package/dist/docker/stack-trace.d.ts +25 -0
  105. package/dist/docker/stack-trace.d.ts.map +1 -0
  106. package/dist/docker/stack-trace.js +44 -0
  107. package/dist/health/builtin.d.ts +8 -0
  108. package/dist/health/builtin.d.ts.map +1 -0
  109. package/dist/health/builtin.js +144 -0
  110. package/dist/health/context.d.ts +3 -0
  111. package/dist/health/context.d.ts.map +1 -0
  112. package/dist/health/context.js +31 -0
  113. package/dist/health/notify-resolver.d.ts +18 -0
  114. package/dist/health/notify-resolver.d.ts.map +1 -0
  115. package/dist/health/notify-resolver.js +28 -0
  116. package/dist/health/registry.d.ts +20 -0
  117. package/dist/health/registry.d.ts.map +1 -0
  118. package/dist/health/registry.js +64 -0
  119. package/dist/health/remediations.d.ts +6 -0
  120. package/dist/health/remediations.d.ts.map +1 -0
  121. package/dist/health/remediations.js +41 -0
  122. package/dist/health/runner.d.ts +4 -0
  123. package/dist/health/runner.d.ts.map +1 -0
  124. package/dist/health/runner.js +22 -0
  125. package/dist/health/scheduler.d.ts +41 -0
  126. package/dist/health/scheduler.d.ts.map +1 -0
  127. package/dist/health/scheduler.js +107 -0
  128. package/dist/health/types.d.ts +73 -0
  129. package/dist/health/types.d.ts.map +1 -0
  130. package/dist/health/types.js +1 -0
  131. package/dist/health/useHealth.d.ts +40 -0
  132. package/dist/health/useHealth.d.ts.map +1 -0
  133. package/dist/health/useHealth.js +122 -0
  134. package/dist/index.d.ts +50 -0
  135. package/dist/index.d.ts.map +1 -0
  136. package/dist/index.js +53 -0
  137. package/dist/ink.d.ts +3 -0
  138. package/dist/ink.d.ts.map +1 -0
  139. package/dist/ink.js +1 -0
  140. package/dist/lint/reactive.d.ts +38 -0
  141. package/dist/lint/reactive.d.ts.map +1 -0
  142. package/dist/lint/reactive.js +131 -0
  143. package/dist/react.d.ts +3 -0
  144. package/dist/react.d.ts.map +1 -0
  145. package/dist/react.js +1 -0
  146. package/dist/runCockpit.d.ts +34 -0
  147. package/dist/runCockpit.d.ts.map +1 -0
  148. package/dist/runCockpit.js +75 -0
  149. package/dist/watchers/manager.d.ts +63 -0
  150. package/dist/watchers/manager.d.ts.map +1 -0
  151. package/dist/watchers/manager.js +239 -0
  152. package/dist/watchers/path-mapper.d.ts +23 -0
  153. package/dist/watchers/path-mapper.d.ts.map +1 -0
  154. package/dist/watchers/path-mapper.js +29 -0
  155. package/dist/watchers/types.d.ts +22 -0
  156. package/dist/watchers/types.d.ts.map +1 -0
  157. package/dist/watchers/types.js +9 -0
  158. package/docs/commands.md +71 -0
  159. package/docs/config-reference.md +20 -0
  160. package/docs/getting-started.md +39 -0
  161. package/docs/health.md +120 -0
  162. package/docs/index.md +13 -0
  163. package/docs/init-config.md +46 -0
  164. package/docs/mount.md +55 -0
  165. package/docs/notifications.md +39 -0
  166. package/docs/panes.md +45 -0
  167. package/docs/watchers.md +27 -0
  168. package/examples/cockpit.yaml +116 -0
  169. package/package.json +91 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Watcher subsystem types.
3
+ *
4
+ * The watcher manager works on a *normalized* repo config — a per-repo
5
+ * declaration that says where the repo lives, which paths trigger work,
6
+ * and what commands to run. Profiles produce these from their own config
7
+ * shapes before constructing a WatcherManager.
8
+ */
9
+ export interface NormalizedRepoConfig {
10
+ name: string;
11
+ /** Glob patterns (chokidar shape) — paths that should trigger watcher / lint. */
12
+ discover: string[];
13
+ /** Shell command spawned as the persistent watcher (or undefined for none). */
14
+ watch: string | undefined;
15
+ /** Per-extension lint commands. Undefined = no lint for that family. */
16
+ lint: {
17
+ js: string | undefined;
18
+ css: string | undefined;
19
+ php: string | undefined;
20
+ };
21
+ }
22
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/watchers/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,wEAAwE;IACxE,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;QACvB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;KACzB,CAAC;CACH"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Watcher subsystem types.
3
+ *
4
+ * The watcher manager works on a *normalized* repo config — a per-repo
5
+ * declaration that says where the repo lives, which paths trigger work,
6
+ * and what commands to run. Profiles produce these from their own config
7
+ * shapes before constructing a WatcherManager.
8
+ */
9
+ export {};
@@ -0,0 +1,71 @@
1
+ # Commands
2
+
3
+ `dev-cockpit` ships four core commands. A profile contributes its own commands via `setupCli(program)` — these register **first**, so a profile can take over a core command name (the matching core command is then skipped).
4
+
5
+ | Command | What it does |
6
+ |---------------|-------------------------------------------------------------------------------|
7
+ | `dev` | Boots the three-pane TUI (Repos / Output / Health / Help). |
8
+ | `doctor` | Runs every registered health check once and prints a status table. |
9
+ | `init-config` | Writes a starter `cockpit.yaml` in the current directory. |
10
+ | `mount` | Generates a Docker compose overlay for declared bind mounts. |
11
+
12
+ ## `dev`
13
+
14
+ ```
15
+ dev-cockpit dev [-c <path>]
16
+ ```
17
+
18
+ Loads `cockpit.yaml`, wires the active profile + config-declared health checks into the cockpit shell, and starts an optional Docker log tailer if the config declares services to tail.
19
+
20
+ ## `doctor`
21
+
22
+ ```
23
+ dev-cockpit doctor [-c <path>]
24
+ ```
25
+
26
+ Builds the health registry from `config.health[]` plus `profile.healthChecks`, runs the `startup` trigger set once, and prints `✓ / ⚠ / ✗` per check. Exits non-zero on any `error` severity.
27
+
28
+ ## `init-config`
29
+
30
+ ```
31
+ dev-cockpit init-config [--with-docker] [-f|--force]
32
+ ```
33
+
34
+ Writes a `cockpit.yaml` template at the current directory. `--with-docker` uncomments the docker block. The template is otherwise heavily commented — uncomment the sections you need.
35
+
36
+ ## `mount`
37
+
38
+ ```
39
+ dev-cockpit mount [-c <path>] [-s|--service <name>]
40
+ dev-cockpit mount status [-c <path>]
41
+ dev-cockpit mount clear [-c <path>]
42
+ ```
43
+
44
+ Merges `config.mounts[]` with `profile.mountCandidatesProvider?.()`, then writes:
45
+
46
+ - `<workspace>/docker-compose.dev-cockpit.yml` — the overlay
47
+ - `<state-dir>/mount.manifest.json` — what was applied + when
48
+
49
+ See [mount.md](./mount.md) for the full mount story.
50
+
51
+ ## Profile setupCli
52
+
53
+ A profile bundle implements the `Profile` type's `setupCli(program)` to register additional commands or override core ones:
54
+
55
+ ```ts
56
+ import type { Profile } from 'dev-cockpit';
57
+
58
+ export const myProfile: Profile = {
59
+ appName: 'my-app',
60
+ setupCli(program) {
61
+ program
62
+ .command('release')
63
+ .description('cut a release')
64
+ .action(async () => {
65
+ /* … */
66
+ });
67
+ },
68
+ };
69
+ ```
70
+
71
+ The profile is invoked **before** the core commands register. Any name the profile claims (`dev`, `doctor`, etc.) silently skips its core counterpart — no overrides API, no array mutation, just registration order.
@@ -0,0 +1,20 @@
1
+ # Config reference
2
+
3
+ A `cockpit.yaml` lives at the root of your project. The cockpit reads it on startup and seeds the store from it.
4
+
5
+ ## Top-level keys
6
+
7
+ - `version` (number, required) — schema version. Must match the running build's `CONFIG_VERSION`.
8
+ - `appName` (string, required) — used as the state-dir name (`~/.local/state/<appName>/...`) and the OS-notification title prefix.
9
+ - `watchers` (array) — see watchers.md.
10
+ - `repos` (array) — items to show in the Repos pane.
11
+ - `docker` (object) — `composeFile` + `services[]`. Omit the whole block on plain-Node projects.
12
+ - `highlights` (array) — patterns applied to all log streams.
13
+ - `health` (array) — see health.md.
14
+ - `help` (object) — extra docs sources + `defaultPage`.
15
+ - `notifications` (object) — see notifications.md.
16
+ - `profile` (object) — namespace for profile-specific extension keys (e.g. `profile.myapp = { ... }`). Each profile validates its own block via its `configSchemaExt`.
17
+
18
+ ## Example
19
+
20
+ A fully-commented example lives at `examples/cockpit.yaml` in the dev-cockpit package. Copy it as a starting point and trim what doesn't apply.
@@ -0,0 +1,39 @@
1
+ # Getting started
2
+
3
+ Run `dev-cockpit dev` from a directory that contains a `cockpit.yaml`. The TUI boots into the **Repos** tab and starts any watchers + health checks declared in your config.
4
+
5
+ ## At a glance
6
+
7
+ - **Cycle tabs** — `←` / `→` (or `Tab` / `Shift-Tab`)
8
+ - **Quit** — `q`
9
+ - **Toggle notifications for this session** — `n`
10
+
11
+ Every tab fills the full terminal. Modals (filter, search) render in place of the active pane.
12
+
13
+ ## What the panes do
14
+
15
+ - **Repos** — list of repos and (optionally) Docker services with watcher / lint status; per-row actions live here.
16
+ - **Output** — streaming logs from watchers and Docker services, with filter and search modals.
17
+ - **Health** — workspace health checks; each check has a one-keystroke remediation.
18
+ - **Help** — this guide. `j` / `k` to flip pages, `↑` / `↓` to scroll.
19
+
20
+ ## Try it on a fixture
21
+
22
+ The repo ships two minimal sample projects under `tests/fixtures/`:
23
+
24
+ - `tests/fixtures/plain-node/` — no docker dependency. Watcher (`tsc --watch`), `http-ok` health against a tiny http server, and a `file-exists` health on `package-lock.json`.
25
+ - `tests/fixtures/dockerized/` — a `docker compose` stack (alpine heartbeat + redis), `container-running` and `port-open` health checks, log tailing.
26
+
27
+ ```sh
28
+ cd tests/fixtures/plain-node
29
+ npm install
30
+ node src/server.mjs & # so the http-ok check goes green
31
+ dev-cockpit dev
32
+
33
+ # Or for the docker variant:
34
+ cd tests/fixtures/dockerized
35
+ docker compose up -d
36
+ dev-cockpit dev
37
+ ```
38
+
39
+ These fixtures double as the project's smoke tests (`tests/fixtures/smoke.test.ts`) — running `npm test` parses each `cockpit.yaml` and asserts the shell renders.
package/docs/health.md ADDED
@@ -0,0 +1,120 @@
1
+ # Health
2
+
3
+ A health check is a small predicate that decides whether something in your workspace is `ok`, `warn`, or `error`. Each check pairs with a one-keystroke remediation that the user can press from the Health pane to fix it.
4
+
5
+ The cockpit re-runs health checks on three triggers:
6
+
7
+ | Trigger | When |
8
+ |------------|-----------------------------------------------------------------------|
9
+ | `startup` | once, at boot |
10
+ | `fsevent` | when the watcher reports a file change (debounced ~500 ms) |
11
+ | `lockfile` | when `composer.lock`, `package-lock.json`, or `yarn.lock` changes |
12
+ | `docker` | when a docker event is signalled (e.g. by the docker subsystem) |
13
+
14
+ Transitions (`ok → error`, `error → ok`) fire OS notifications according to the global `notifications:` block, optionally overridden per-check.
15
+
16
+ ## Built-in check vocabulary
17
+
18
+ | Type | Required args | Optional args | Default triggers |
19
+ |---------------------|----------------------|------------------------------|---------------------------|
20
+ | `container-running` | `container` | | `startup`, `docker` |
21
+ | `port-open` | `port` (number) | `host` (default `127.0.0.1`) | `startup` |
22
+ | `http-ok` | `url` | `expectStatus` (default 200) | `startup` |
23
+ | `file-exists` | `path` | | `startup`, `fsevent` |
24
+ | `exec-zero` | `command` | `args[]`, `cwd` | `startup` |
25
+
26
+ Defaults can be overridden by setting `triggers:` and `severity:` on the entry.
27
+
28
+ ## Config shape
29
+
30
+ ```yaml
31
+ health:
32
+ - id: api-up
33
+ label: API responds
34
+ type: http-ok
35
+ url: http://localhost:3000/health
36
+ expectStatus: 200
37
+ remediation:
38
+ key: A
39
+ label: restart api
40
+ command: docker compose restart api
41
+
42
+ - id: db-up
43
+ label: DB reachable
44
+ type: port-open
45
+ port: 5432
46
+ triggers: [startup]
47
+ remediation:
48
+ key: D
49
+ label: start db
50
+ command: docker compose up -d db
51
+ notify:
52
+ onTransitionTo: [health-failed] # only notify on going-down
53
+ ```
54
+
55
+ ## Remediations
56
+
57
+ Each health entry declares one remediation. It can be either:
58
+
59
+ - **Declarative shell command** — `command: "npm install"` (with optional `cwd`). Stdout/stderr stream to the Output pane tagged `health:<id>`.
60
+ - **Programmatic function** — only available to checks contributed by a profile (TypeScript code), via `run(ctx, workspaceRoot)`.
61
+
62
+ The remediation `key` is a single uppercase letter. Pressing the key (lowercase or upper) on the Health pane runs the remediation; the row shows `… (running…)` until completion, with a minimum 1.5 s banner hold so fast-finishing actions stay visible.
63
+
64
+ ## Notification policy
65
+
66
+ Per-check `notify:` overrides:
67
+
68
+ | Setting | Effect |
69
+ |-----------------------------------------------|-------------------------------------------------------|
70
+ | omitted | inherit global `notifications:` policy |
71
+ | `notify: false` | always silent for this check |
72
+ | `notify: { onTransitionTo: [health-failed] }` | only notify on the listed transition events |
73
+
74
+ The session toggle (`n` keystroke) and global `notifications.enabled: false` always silence everything — per-check overrides cannot opt back in.
75
+
76
+ ## Profile-contributed checks
77
+
78
+ A profile bundles pre-built checks (TypeScript with closures over project-specific helpers) via `Profile.healthChecks`. They register **after** config-driven entries and may use any custom `type` id. **Built-in `type` ids cannot be overridden** by profile checks — the registry rejects them with a warning, so the built-in vocabulary stays predictable.
79
+
80
+ ```ts
81
+ import type { Profile } from 'dev-cockpit';
82
+
83
+ export const myProfile: Profile = {
84
+ appName: 'my-cockpit',
85
+ healthChecks: [
86
+ {
87
+ id: 'composer-installed',
88
+ label: 'Composer',
89
+ severity: 'error',
90
+ triggers: ['startup', 'lockfile'],
91
+ predicate: async (ctx) => { /* … */ },
92
+ remediation: { key: 'R', label: 'composer install', run: async (ctx, root) => { /* … */ } },
93
+ },
94
+ ],
95
+ };
96
+ ```
97
+
98
+ ## Wiring
99
+
100
+ In your `dev` command, pass `health: { … }` to `runCockpit`:
101
+
102
+ ```ts
103
+ import { runCockpit, buildHealthContext } from 'dev-cockpit';
104
+
105
+ await runCockpit({
106
+ profile: myProfile,
107
+ footerLegends: { /* … */ },
108
+ health: {
109
+ workspaceRoot,
110
+ profileChecks: myProfile.healthChecks,
111
+ configEntries: config.health,
112
+ ctx: buildHealthContext(workspaceRoot, appendOutput),
113
+ notifications: config.notifications,
114
+ appName: 'my-cockpit',
115
+ subscribeFsEvents: (listener) => watcherManager.subscribeFsEvents(listener),
116
+ },
117
+ }).waitUntilExit();
118
+ ```
119
+
120
+ `useHealth` (called internally by `<Cockpit>`) owns the scheduler lifecycle, transition diffing, notification dispatch, and remediation routing. Consumers do not instantiate `HealthScheduler` directly.
package/docs/index.md ADDED
@@ -0,0 +1,13 @@
1
+ # dev-cockpit help
2
+
3
+ This is the in-cockpit Help tab. The pages below explain what each pane does, how to drive the cockpit from the keyboard, and how to configure it for your project.
4
+
5
+ - [Getting started](./getting-started.md) — first run and what to expect
6
+ - [Panes](./panes.md) — Repos, Output, Health, Help
7
+ - [Watchers](./watchers.md) — long-running commands streamed into Output
8
+ - [Health](./health.md) — checks and one-keystroke remediations
9
+ - [Commands](./commands.md) — the four core CLI commands + profile `setupCli`
10
+ - [init-config](./init-config.md) — scaffold a starter `cockpit.yaml`
11
+ - [Mount](./mount.md) — host ↔ container bind-mount overlay
12
+ - [Notifications](./notifications.md) — transition-only OS notifications
13
+ - [Config reference](./config-reference.md) — `cockpit.yaml` schema
@@ -0,0 +1,46 @@
1
+ # init-config
2
+
3
+ `dev-cockpit init-config` writes a starter `cockpit.yaml` at the current working directory. Two modes:
4
+
5
+ ```
6
+ dev-cockpit init-config [--with-docker] [-f|--force] # static template
7
+ dev-cockpit init-config -i [-f|--force] # interactive wizard
8
+ ```
9
+
10
+ ## Flags
11
+
12
+ | Flag | Default | Effect |
13
+ |----------------------|---------|-----------------------------------------------------------------------------------------------|
14
+ | `-i, --interactive` | off | Walk through prompts to populate the file. Skips the static template. |
15
+ | `--with-docker` | off | Uncomments the `docker:` block in the static template (ignored in `--interactive` mode). |
16
+ | `-f, --force` | off | Overwrites an existing `cockpit.yaml` instead of refusing. |
17
+
18
+ ## Static template
19
+
20
+ `version: 1` and `appName` are required and uncommented. Everything else (watchers, repos, docker, highlights, health, help, notifications, mounts) ships as a commented example. You uncomment what you need. Best when you already know what you want — fastest path from scratch to "edit me".
21
+
22
+ ## Interactive wizard
23
+
24
+ The recommended path the first time you set the cockpit up on a project. It walks through seven steps, with project sniffing pre-populating sensible defaults so you usually just press enter:
25
+
26
+ | Step | Section | What it asks + auto-detects |
27
+ |------|---------|------------------------------|
28
+ | 1 | **App name** | Defaults to `package.json` `name`, then the cwd basename. |
29
+ | 2 | **Docker** | Detects `compose.yaml` / `docker-compose.yml` and parses its services. Offers them as a multi-select. Skip entirely for non-compose projects. |
30
+ | 3 | **Watchers** | Surfaces `package.json` scripts (`dev`, `watch`, `start`, `serve`) as pre-checked candidates. Anything that runs forever and writes to stdout — `vite`, `tsc --watch`, `php artisan serve`, `tail -f storage/logs/laravel.log` — fits here. |
31
+ | 4 | **Repos** | Detects npm/yarn/pnpm workspaces; falls back to a single `.` entry. Each entry becomes a row in the Repos pane (with `r/w/l` keystroke actions when a profile bundle wires them). |
32
+ | 5 | **Highlights** | Regex patterns that re-colour matching Output lines. `error`-severity matches additionally feed the Recent Errors strip + OS notifications. |
33
+ | 6 | **Health** | Reframed as test-and-fix pairs. Pick one of the five built-in types (`container-running`, `port-open`, `http-ok`, `file-exists`, `exec-zero`), supply the type-specific arg(s), then bind a single-keystroke remediation. Add as many checks as you like — same type repeats freely (e.g. `port-open` for 5432, 6379, 8108). `container-running` is hidden when no docker block was declared. |
34
+ | 7 | **Notifications** | Toggle native OS notifications on state transitions. |
35
+
36
+ After writing the file, the wizard offers to run `dev-cockpit doctor` immediately so you get instant feedback that the config parses + each declared health check has a baseline state.
37
+
38
+ The wizard scaffolds a *working* config. Anything more advanced (custom triggers, `expectStatus`, exec args, mounts, profile namespaces) is a hand-edit on the generated file.
39
+
40
+ ## Profile-driven defaults
41
+
42
+ When a profile is active (e.g. `<profile-bin> init-config -i`), the wizard defaults `appName` to the profile's. Profile bundles can also register their own `init-config` via `setupCli(program)` if they want a domain-specific template — the generic command is skipped when the profile claims the slot.
43
+
44
+ ## Schema reference
45
+
46
+ The full schema definition lives at [config-reference.md](./config-reference.md).
package/docs/mount.md ADDED
@@ -0,0 +1,55 @@
1
+ # Mount
2
+
3
+ `dev-cockpit mount` generates a Docker compose overlay describing host ↔ container bind mounts so you can swap a containerised dependency for a local checkout.
4
+
5
+ ## When to use it
6
+
7
+ When you're developing a containerised app and want changes in a local checkout to flow live into the running container — e.g. mount your local `./packages/api` into `/srv/api` inside the `web` service.
8
+
9
+ ## Mount sources
10
+
11
+ Mounts come from two places, merged on `containerPath` (config wins on collision):
12
+
13
+ 1. **`config.mounts[]`** — explicit, declared in `cockpit.yaml`:
14
+ ```yaml
15
+ mounts:
16
+ - hostPath: ./packages/api
17
+ containerPath: /srv/api
18
+ ```
19
+ 2. **`profile.mountCandidatesProvider?.()`** — discovered programmatically by the active profile (e.g. walking a manifest file to find local checkouts of declared dependencies).
20
+
21
+ ## Output
22
+
23
+ - `<workspace>/docker-compose.dev-cockpit.yml` — the overlay file. Apply it alongside your existing compose file:
24
+ ```
25
+ docker compose -f docker-compose.yml -f docker-compose.dev-cockpit.yml up -d
26
+ ```
27
+ - `<state-dir>/mount.manifest.json` — records what was applied + when. Used by `mount status` and `mount clear`.
28
+
29
+ The state dir lives under `XDG_STATE_HOME/<appName>/<workspace-hash>/`.
30
+
31
+ ## Subcommands
32
+
33
+ ```
34
+ dev-cockpit mount [-s|--service <name>] # write/refresh the overlay
35
+ dev-cockpit mount status # print active overlay manifest
36
+ dev-cockpit mount clear # delete overlay + manifest
37
+ ```
38
+
39
+ If `--service` is omitted, the first entry in `config.docker.services` is used. Pass `--service` explicitly when your compose file has multiple services and you want mounts attached to a non-default one.
40
+
41
+ ## Profile-side discovery
42
+
43
+ A profile that scans a manifest (e.g. `composer.json`, `package.json` workspaces) for local checkouts implements `mountCandidatesProvider` and returns `Mount[]`. Discovered candidates blend with `config.mounts[]` automatically:
44
+
45
+ ```ts
46
+ export const myProfile: Profile = {
47
+ appName: 'my-app',
48
+ mountCandidatesProvider(): Mount[] {
49
+ return findLocalCheckouts().map((c) => ({
50
+ hostPath: c.hostPath,
51
+ containerPath: c.containerPath,
52
+ }));
53
+ },
54
+ };
55
+ ```
@@ -0,0 +1,39 @@
1
+ # Notifications
2
+
3
+ OS notifications fire on **state transitions only** — never on every check, never on every log line. The transition detector compares the previous health snapshot to the new one and emits one event per check that crossed the `error` threshold.
4
+
5
+ ## Layered policy
6
+
7
+ 1. **Global default** — `notifications:` block in `cockpit.yaml`:
8
+
9
+ ```yaml
10
+ notifications:
11
+ enabled: true
12
+ onTransitionTo:
13
+ - error
14
+ - recovered
15
+ ```
16
+
17
+ 2. **Per-item override** — a `notify:` field on any watcher or health check:
18
+
19
+ ```yaml
20
+ - id: lint
21
+ command: npx eslint . --watch
22
+ notify: false # mute this item
23
+
24
+ - id: api-up
25
+ type: http-ok
26
+ url: http://localhost:3000/health
27
+ notify:
28
+ onTransitionTo: [error] # only on going-down, not on recovery
29
+ ```
30
+
31
+ 3. **Session toggle** — press `n` in the cockpit to mute *all* notifications for the current session (overrides config). The Health pane shows the current session state at the bottom.
32
+
33
+ ## Events
34
+
35
+ - `health-failed` / `health-recovered` — emitted by the health framework
36
+ - `build-failed` / `build-recovered` — emitted by watchers
37
+ - Profiles can emit their own event names; they flow through the same pipeline.
38
+
39
+ The `appName` configured in `cockpit.yaml` is used as the title prefix on every notification (e.g. `my-app — API responds`).
package/docs/panes.md ADDED
@@ -0,0 +1,45 @@
1
+ # Panes
2
+
3
+ ## Repos
4
+
5
+ Glyphs:
6
+
7
+ - ● running (green)
8
+ - ○ idle (gray/dim)
9
+ - ✗ failing (red)
10
+
11
+ Lint sub-glyph (after the name): ✓ pass · ! fail · (none) unknown.
12
+
13
+ Selected row is marked with a leading `>` and the pane border is highlighted when this pane has focus.
14
+
15
+ Actions (when a row is selected):
16
+
17
+ - `r` — primary action (rebuild for repo entries, restart for Docker entries)
18
+ - `w` — toggle watcher (repo entries only)
19
+ - `l` — run lint (repo entries only)
20
+
21
+ In-flight actions show a banner at the top of the pane plus an inline `(running…)` next to the active row.
22
+
23
+ ## Output
24
+
25
+ Streaming source-tagged logs.
26
+
27
+ - `↑` / `↓` — scroll one line
28
+ - `PgUp` / `PgDn` — scroll one page
29
+ - `Enter` — jump to bottom (auto-scroll resumes)
30
+ - `c` — clear
31
+ - `/` — open Search modal
32
+ - `f` — open Filter modal
33
+ - `e` — open the most recent error with a `file:line` reference in `$EDITOR`
34
+
35
+ A "Recent errors" strip appears at the top when one or more error lines have been pushed (most-recent first, capped at 3 visible).
36
+
37
+ ## Health
38
+
39
+ Each row shows: severity glyph, label, remediation key (if any). `Enter` toggles the diagnostic detail for the selected row. Press the remediation letter to fix.
40
+
41
+ While a remediation runs, a banner appears at the top and the running row shows `… (running…)`.
42
+
43
+ ## Help
44
+
45
+ This pane. `j` / `k` (or `Shift-Tab` / `Tab`) cycle pages. `↑` / `↓` scroll. `g` / `G` jump to top / bottom. `←` / `→` always exit Help to the next tab.
@@ -0,0 +1,27 @@
1
+ # Watchers
2
+
3
+ A watcher is a long-running command (typically `tsc --watch`, `vitest --watch`, `eslint --watch`) declared in `cockpit.yaml` under `watchers:`. The cockpit spawns each watcher lazily on first focus and streams its stdout/stderr into the Output pane, prefixed by source id.
4
+
5
+ ## Config shape
6
+
7
+ ```yaml
8
+ watchers:
9
+ - id: typecheck
10
+ label: tsc --watch
11
+ command: npx tsc --watch --noEmit
12
+ color: cyan
13
+ - id: test
14
+ label: vitest
15
+ command: npx vitest --watch
16
+ color: magenta
17
+ notify: false # mute notifications for this watcher
18
+ restartOn:
19
+ - package.json
20
+ - tsconfig.json
21
+ ```
22
+
23
+ - `id` is required; it appears as the source prefix on every emitted line.
24
+ - `restartOn` lets you re-spawn the watcher when specific files change.
25
+ - `notify` overrides the global notifications policy for this watcher (see notifications.md).
26
+
27
+ This page is a stub — full watcher behavior is fleshed out in subsequent phases.
@@ -0,0 +1,116 @@
1
+ # cockpit.yaml — example config
2
+ # Run `dev-cockpit dev` from the directory containing this file.
3
+
4
+ version: 1 # config schema version; required
5
+ appName: my-app # state dir: ~/.local/state/my-app/
6
+
7
+ # Watchers — long-running commands streamed into the Output pane.
8
+ watchers:
9
+ - id: typecheck
10
+ label: tsc --watch
11
+ command: npx tsc --watch --noEmit
12
+ color: cyan
13
+ - id: test
14
+ label: vitest
15
+ command: npx vitest --watch
16
+ color: magenta
17
+ - id: lint
18
+ label: eslint
19
+ command: npx eslint . --watch
20
+ color: yellow
21
+ notify: false # mute notifications for this watcher
22
+ restartOn:
23
+ - package.json
24
+ - .eslintrc.*
25
+
26
+ # Repos — what shows in the Repos pane (optional; default = empty).
27
+ repos:
28
+ - id: api
29
+ path: ./packages/api
30
+ label: API
31
+ - id: web
32
+ path: ./packages/web
33
+ label: Web
34
+
35
+ # Docker — enables container log tailing, container health checks, mount overlay.
36
+ # Omit this whole block for plain-Node projects; the docker tab/section won't appear.
37
+ docker:
38
+ composeFile: ./docker-compose.yml
39
+ services:
40
+ - name: web
41
+ tail: true
42
+ - name: db
43
+ tail: true
44
+ - name: redis
45
+ tail: false
46
+
47
+ # Highlight patterns — applied to all log streams.
48
+ highlights:
49
+ - pattern: ERROR
50
+ severity: error
51
+ - pattern: FATAL
52
+ severity: error
53
+ - pattern: WARN
54
+ severity: warn
55
+ - pattern: "Stack trace"
56
+ severity: error
57
+
58
+ # Health checks — `type` references a built-in or profile-registered check id.
59
+ # Each check has a one-keystroke remediation.
60
+ health:
61
+ - id: api-up
62
+ label: API responds
63
+ type: http-ok
64
+ url: http://localhost:3000/health
65
+ notify:
66
+ onTransitionTo: [error] # only notify on going-down, not on recovery
67
+ remediation:
68
+ key: a
69
+ label: restart api
70
+ command: docker compose restart web
71
+
72
+ - id: db-running
73
+ label: DB container running
74
+ type: container-running
75
+ container: db
76
+ remediation:
77
+ key: d
78
+ label: bring db up
79
+ command: docker compose up -d db
80
+
81
+ - id: env-file
82
+ label: .env exists
83
+ type: file-exists
84
+ path: ./.env
85
+ remediation:
86
+ key: e
87
+ label: copy .env.example
88
+ command: cp .env.example .env
89
+
90
+ - id: migrations
91
+ label: migrations applied
92
+ type: exec-zero
93
+ command: npm run db:check
94
+ remediation:
95
+ key: m
96
+ label: run migrations
97
+ command: npm run db:migrate
98
+
99
+ # Help — extra markdown directories layered on top of core's built-in docs.
100
+ help:
101
+ sources:
102
+ - ./docs
103
+ defaultPage: getting-started
104
+
105
+ # Notifications — fire on state transitions only.
106
+ notifications:
107
+ enabled: true
108
+ onTransitionTo:
109
+ - error
110
+ - recovered
111
+
112
+ # Profile-specific config lives under `profile.<name>` — validated by the
113
+ # profile's `configSchemaExt` zod extension. Generic core ignores this block.
114
+ # profile:
115
+ # myapp:
116
+ # someKey: value