laracrew 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,188 +1,207 @@
1
- # Changelog
2
-
3
- All notable changes to this project are documented here.
4
-
5
- The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
6
- adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). While the version is
7
- below `1.0.0`, minor versions may contain breaking config changes; those are always listed
8
- under **Changed** with a migration note.
9
-
10
- ## [Unreleased]
11
-
12
- Planned, in order — see `.claude/PLAN.md`:
13
-
14
- - `laracrew scan` to discover Laravel projects and scaffold a stack
15
- - Live queue depth and Redis stream consumer lag in the process tree
16
- - File watching with graceful `queue:restart`
17
- - `laracrew run <task>` for one-shot cross-project sequences
18
- - Background daemon: `up --detach`, `attach`, `status`, `logs -f`
19
-
20
- ## [0.2.1] — 2026-09-20
21
-
22
- ### Added
23
-
24
- - **A stack builder**, [`docs/stack-builder.html`](docs/stack-builder.html) — a single self-contained
25
- page that writes a `stack.yaml` from projects and process commands as you fill them in. It
26
- encodes the rules the config resolver enforces, so it catches the mistakes before laracrew does:
27
- a service with no command, `stop.artisan` on a service with no project, a `needs:` pointing at
28
- nothing, an auto-started service depending on one you have to start by hand, a dependency cycle.
29
- It also shows the boot order the `needs:` graph produces, level by level.
30
-
31
- Open it from disk or serve `docs/` with GitHub Pages. It is not part of the npm package — no
32
- runtime dependencies, no build step, nothing to install.
33
-
34
- ## [0.2.0] — 2026-09-20
35
-
36
- Graceful shutdown for any process, not only Laravel queue workers, and a `doctor` that only
37
- applies its Laravel checks to Laravel.
38
-
39
- laracrew has always been able to *run* anything — the supervisor takes a command and knows
40
- nothing about the language behind it. But the two things that made it more than a process
41
- runner, the graceful stop and the pre-flight checks, both assumed PHP. This release closes that
42
- gap. Existing stack files keep working unchanged.
43
-
44
- ### Added
45
-
46
- - **`stop.exec`** — any command as the first step of the stop ladder. It runs in the service's
47
- `cwd`, with the service's environment, and laracrew waits `graceMs` for the process to exit by
48
- itself before escalating to the signal and the tree kill. A Celery worker, a BullMQ consumer or
49
- a Compose project now shuts down as carefully as a queue worker:
50
-
51
- ```yaml
52
- stop: { exec: ["celery", "-A", "app", "control", "shutdown"], graceMs: 25000 }
53
- stop: { exec: "npm run drain", graceMs: 10000 }
54
- stop: { exec: ["docker", "compose", "stop"], graceMs: 30000 }
55
- ```
56
-
57
- - **`doctor` checks external dependencies.** Every `external: true` service is probed through the
58
- `tcp` or `http` gate it already declares, so a stack's Postgres, RabbitMQ or HTTP dependency is
59
- verified before boot — whatever the stack is written in.
60
- - `doctor` reads `REDIS_URL` when a project sets it instead of `REDIS_HOST` / `REDIS_PORT`.
61
-
62
- ### Changed
63
-
64
- - **`stop.artisan` is now shorthand for `stop.exec`.** It resolves to `<project php> artisan
65
- <command>`, run from the project root even when the service sets its own `cwd`. Behaviour for
66
- existing Laravel stacks is unchanged; a service may declare one or the other, not both. A
67
- service that declares its own graceful step now replaces an inherited one in either direction,
68
- so a stack can default to `artisan: queue:restart` and still give one service its own `exec`.
69
- - **`doctor` applies each check only where it means something.** It works out which projects run
70
- PHP (from their commands and from `stop.artisan`) and which talk to Redis (from their `.env`
71
- and their commands). A stack with no PHP in it gets no PHP findings; a stack with no Redis gets
72
- no Redis findings. Laravel stacks see exactly what they saw before.
73
- - A Redis address already covered by an `external: true` service's gate is no longer probed a
74
- second time from the project's `.env`.
75
- - `laracrew --help` and the generated `projects.yaml` no longer describe projects as Laravel-only.
76
- `php:` is documented as what it is: the binary the `stop.artisan` shorthand runs.
77
-
78
- ### Fixed
79
-
80
- - **`laracrew --version` reported `0.1.0` on every release.** The version was a hardcoded
81
- constant that nobody bumped, so `0.1.1` and `0.1.2` both identified themselves as `0.1.0`. It is
82
- now baked in from `package.json` at build time, with a test that fails if the two ever disagree.
83
- - **`stop.artisan` on a service with no project was silently ignored** — the graceful step was
84
- skipped and nothing said so. It is now a config error that names `stop.exec` as the way out.
85
- - **`doctor` failed on a machine without PHP** for a stack that contains no PHP: `php --version`
86
- ran for every declared project and a missing binary was a blocking `✖` with exit code 1.
87
- - **False Redis collision between projects that never touch Redis.** Two projects with no `.env`
88
- both resolved to the same synthesized default namespace and were reported as sharing it.
89
- - The "no `artisan` file" and "`.env` is missing or empty" warnings no longer fire for projects
90
- that are not PHP projects.
91
-
92
- ### Notes
93
-
94
- - 244 tests. The graceful stop step had no coverage before this release; it now has unit tests
95
- for both outcomes (the process exits by itself, and the ladder escalating when it does not),
96
- plus tests for every new config error.
97
- - The npm description and `laracrew --help` now say "your projects" rather than "your Laravel
98
- projects". The README still leads with the Laravel story, which is what the tool was built for.
99
-
100
- ## [0.1.2] — 2026-09-20
101
-
102
- ### Added
103
-
104
- - `repository`, `homepage` and `bugs` metadata, so the npm page links back to the source.
105
-
106
- ## [0.1.1] — 2026-09-20
107
-
108
- First release published to npm. No code changes from `0.1.0`.
109
-
110
- ## [0.1.0] — 2026-09-19
111
-
112
- First release. Boots and supervises the long-running processes of several Laravel projects at
113
- once, from one command.
114
-
115
- ### Added
116
-
117
- **Supervision**
118
-
119
- - Dependency-ordered boot (`needs:`), with independent services started in parallel.
120
- - Readiness gates before a dependent service starts: `tcp`, `http`, `logMatch`, `delayMs`.
121
- - Restart policies (`never`, `on-failure`, `always`) with exponential backoff, a restart
122
- ceiling, and an attempt counter that resets after a service has been stable.
123
- - A Laravel-aware stop ladder: `php artisan queue:restart` (or `horizon:terminate`) first, wait
124
- for the worker to finish the job it is holding, then terminate — and always as a process
125
- tree, so `artisan serve`'s child PHP server and `npm run dev`'s Vite are never orphaned.
126
- - Rollback on a failed boot: a readiness gate that never opens stops everything that had
127
- already started, instead of leaving a half-booted stack behind.
128
- - `external: true` for services laracrew health-checks but never starts, such as Redis.
129
- - `autostart: false` for services defined but not launched, started later on demand.
130
-
131
- **Interface**
132
-
133
- - A full-screen process tree as the default view, which renders no log output at all; logs are
134
- opened one process at a time. Built on plain ANSI — no TUI framework.
135
- - Per-process log inspection, a merged log across all services, restart/stop/start, and help.
136
- - `external: true` dependencies are listed above the tree under their own heading rather than
137
- numbered among the managed processes, so a digit key always selects something startable.
138
- - `--plain` prefixed interleaved logs, selected automatically when stdout is not a TTY.
139
- - `--json` newline-delimited events for scripting.
140
- - A broken pipe (`laracrew up --json | head`) shuts the stack down cleanly instead of crashing.
141
-
142
- **Logs**
143
-
144
- - Every line is written to `~/.laracrew/logs/<stack>/<service>.log`, on by default, rotated by
145
- size. Plain text with a sortable local timestamp and no colour codes, so `grep` works.
146
- - `laracrew logs [service]` reads them back after the fact, with `-n`, `-f`, `--since 10m`,
147
- `--all` to merge every service in time order, and `--list`.
148
- - Writes go through a held file descriptor rather than a stream, so a line reaches disk before
149
- a crashing process can lose it, and rotation can close the handle deterministically — a
150
- rename with an open handle fails outright on Windows.
151
-
152
- **Configuration**
153
-
154
- - `~/.laracrew/` home: `config.yaml`, `projects.yaml`, one folder per stack, tasks, fragments.
155
- - YAML stacks validated against a schema, with errors that name the file, the field, and a
156
- spelling suggestion.
157
- - Interpolation: `${env:VAR}`, `${env:VAR:fallback}`, `${project.path}`, `${project.env:VAR}`,
158
- `${stack.dir}`, `${port:N}`.
159
- - Groups, profiles, `--only` / `--except` filtering, and `services/*.yaml` fragments.
160
-
161
- **Commands**
162
-
163
- - `laracrew init [--examples]`, `ls`, `up`, `doctor`, `link`, `unlink`.
164
- - `laracrew link <stack>` installs a global command that boots one stack, so a project set is
165
- one word to launch. Generated shims are marked, and laracrew refuses to overwrite files it
166
- did not write or to shadow names like `npm` and `git`.
167
-
168
- **Checks (`laracrew doctor`)**
169
-
170
- - Two projects sharing a Redis database *and* a queue or stream name, computing the effective
171
- prefix the way Laravel does (`REDIS_PREFIX`, else a slug of `APP_NAME`) so it does not report
172
- a collision that isn't one.
173
- - `QUEUE_CONNECTION=sync` in a project that runs a queue worker.
174
- - Port clashes, missing project paths, an unreachable Redis, and a PHP binary that will not run.
175
-
176
- ### Notes
177
-
178
- - Requires Node 20 or newer. Windows, macOS and Linux.
179
- - Runtime dependencies: `commander`, `yaml`, `zod`.
180
- - Accepted by the schema but not yet acted on: `watch`, `metrics` (read by `doctor` only), and
181
- `hooks`. Stack files written today stay valid.
182
-
183
- [Unreleased]: https://github.com/vidux/laracrew/compare/v0.2.1...HEAD
184
- [0.2.1]: https://github.com/vidux/laracrew/compare/v0.2.0...v0.2.1
185
- [0.2.0]: https://github.com/vidux/laracrew/compare/v0.1.2...v0.2.0
186
- [0.1.2]: https://github.com/vidux/laracrew/compare/v0.1.1...v0.1.2
187
- [0.1.1]: https://github.com/vidux/laracrew/releases/tag/v0.1.1
188
- [0.1.0]: https://github.com/vidux/laracrew/releases/tag/v0.1.0
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
6
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). While the version is
7
+ below `1.0.0`, minor versions may contain breaking config changes; those are always listed
8
+ under **Changed** with a migration note.
9
+
10
+ ## [Unreleased]
11
+
12
+ Planned, in order — see `.claude/PLAN.md`:
13
+
14
+ - `laracrew scan` to discover Laravel projects and scaffold a stack
15
+ - Live queue depth and Redis stream consumer lag in the process tree
16
+ - File watching with graceful `queue:restart`
17
+ - `laracrew run <task>` for one-shot cross-project sequences
18
+ - Background daemon: `up --detach`, `attach`, `status`, `logs -f`
19
+
20
+ ## [0.2.2] — 2026-09-30
21
+
22
+ ### Added
23
+
24
+ - **`R` restarts every running process** with one keypress, in dependency order, so a
25
+ dependency is back and ready before the services that need it are bounced. Only what is
26
+ `running` is touched: a service you stopped, one defined with `autostart: false`, one that
27
+ exited on its own and one that gave up all stay exactly as they are. Plain and JSON runs see
28
+ the usual stop and start events plus a notice naming the services.
29
+
30
+ ### Notes
31
+
32
+ - Releases are now built and published by GitHub Actions. `test.yml` runs the suite and the
33
+ packed-tarball smoke test on Windows and Ubuntu for every push and pull request; `publish.yml`
34
+ is a manual release through npm trusted publishing, with a dry-run rehearsal as the default.
35
+ - Three tests left a log file handle open, which made the suite's teardown fail on Windows with
36
+ Node 20. Fixed; 248 tests.
37
+
38
+ ## [0.2.1] — 2026-09-20
39
+
40
+ ### Added
41
+
42
+ - **A stack builder**, [`docs/stack-builder.html`](docs/stack-builder.html) — a single self-contained
43
+ page that writes a `stack.yaml` from projects and process commands as you fill them in. It
44
+ encodes the rules the config resolver enforces, so it catches the mistakes before laracrew does:
45
+ a service with no command, `stop.artisan` on a service with no project, a `needs:` pointing at
46
+ nothing, an auto-started service depending on one you have to start by hand, a dependency cycle.
47
+ It also shows the boot order the `needs:` graph produces, level by level.
48
+
49
+ Open it from disk or serve `docs/` with GitHub Pages. It is not part of the npm package — no
50
+ runtime dependencies, no build step, nothing to install.
51
+
52
+ ## [0.2.0] — 2026-09-20
53
+
54
+ Graceful shutdown for any process, not only Laravel queue workers, and a `doctor` that only
55
+ applies its Laravel checks to Laravel.
56
+
57
+ laracrew has always been able to *run* anything — the supervisor takes a command and knows
58
+ nothing about the language behind it. But the two things that made it more than a process
59
+ runner, the graceful stop and the pre-flight checks, both assumed PHP. This release closes that
60
+ gap. Existing stack files keep working unchanged.
61
+
62
+ ### Added
63
+
64
+ - **`stop.exec`** — any command as the first step of the stop ladder. It runs in the service's
65
+ `cwd`, with the service's environment, and laracrew waits `graceMs` for the process to exit by
66
+ itself before escalating to the signal and the tree kill. A Celery worker, a BullMQ consumer or
67
+ a Compose project now shuts down as carefully as a queue worker:
68
+
69
+ ```yaml
70
+ stop: { exec: ["celery", "-A", "app", "control", "shutdown"], graceMs: 25000 }
71
+ stop: { exec: "npm run drain", graceMs: 10000 }
72
+ stop: { exec: ["docker", "compose", "stop"], graceMs: 30000 }
73
+ ```
74
+
75
+ - **`doctor` checks external dependencies.** Every `external: true` service is probed through the
76
+ `tcp` or `http` gate it already declares, so a stack's Postgres, RabbitMQ or HTTP dependency is
77
+ verified before boot — whatever the stack is written in.
78
+ - `doctor` reads `REDIS_URL` when a project sets it instead of `REDIS_HOST` / `REDIS_PORT`.
79
+
80
+ ### Changed
81
+
82
+ - **`stop.artisan` is now shorthand for `stop.exec`.** It resolves to `<project php> artisan
83
+ <command>`, run from the project root even when the service sets its own `cwd`. Behaviour for
84
+ existing Laravel stacks is unchanged; a service may declare one or the other, not both. A
85
+ service that declares its own graceful step now replaces an inherited one in either direction,
86
+ so a stack can default to `artisan: queue:restart` and still give one service its own `exec`.
87
+ - **`doctor` applies each check only where it means something.** It works out which projects run
88
+ PHP (from their commands and from `stop.artisan`) and which talk to Redis (from their `.env`
89
+ and their commands). A stack with no PHP in it gets no PHP findings; a stack with no Redis gets
90
+ no Redis findings. Laravel stacks see exactly what they saw before.
91
+ - A Redis address already covered by an `external: true` service's gate is no longer probed a
92
+ second time from the project's `.env`.
93
+ - `laracrew --help` and the generated `projects.yaml` no longer describe projects as Laravel-only.
94
+ `php:` is documented as what it is: the binary the `stop.artisan` shorthand runs.
95
+
96
+ ### Fixed
97
+
98
+ - **`laracrew --version` reported `0.1.0` on every release.** The version was a hardcoded
99
+ constant that nobody bumped, so `0.1.1` and `0.1.2` both identified themselves as `0.1.0`. It is
100
+ now baked in from `package.json` at build time, with a test that fails if the two ever disagree.
101
+ - **`stop.artisan` on a service with no project was silently ignored** — the graceful step was
102
+ skipped and nothing said so. It is now a config error that names `stop.exec` as the way out.
103
+ - **`doctor` failed on a machine without PHP** for a stack that contains no PHP: `php --version`
104
+ ran for every declared project and a missing binary was a blocking `✖` with exit code 1.
105
+ - **False Redis collision between projects that never touch Redis.** Two projects with no `.env`
106
+ both resolved to the same synthesized default namespace and were reported as sharing it.
107
+ - The "no `artisan` file" and "`.env` is missing or empty" warnings no longer fire for projects
108
+ that are not PHP projects.
109
+
110
+ ### Notes
111
+
112
+ - 244 tests. The graceful stop step had no coverage before this release; it now has unit tests
113
+ for both outcomes (the process exits by itself, and the ladder escalating when it does not),
114
+ plus tests for every new config error.
115
+ - The npm description and `laracrew --help` now say "your projects" rather than "your Laravel
116
+ projects". The README still leads with the Laravel story, which is what the tool was built for.
117
+
118
+ ## [0.1.2] — 2026-09-20
119
+
120
+ ### Added
121
+
122
+ - `repository`, `homepage` and `bugs` metadata, so the npm page links back to the source.
123
+
124
+ ## [0.1.1] — 2026-09-20
125
+
126
+ First release published to npm. No code changes from `0.1.0`.
127
+
128
+ ## [0.1.0] — 2026-09-19
129
+
130
+ First release. Boots and supervises the long-running processes of several Laravel projects at
131
+ once, from one command.
132
+
133
+ ### Added
134
+
135
+ **Supervision**
136
+
137
+ - Dependency-ordered boot (`needs:`), with independent services started in parallel.
138
+ - Readiness gates before a dependent service starts: `tcp`, `http`, `logMatch`, `delayMs`.
139
+ - Restart policies (`never`, `on-failure`, `always`) with exponential backoff, a restart
140
+ ceiling, and an attempt counter that resets after a service has been stable.
141
+ - A Laravel-aware stop ladder: `php artisan queue:restart` (or `horizon:terminate`) first, wait
142
+ for the worker to finish the job it is holding, then terminate — and always as a process
143
+ tree, so `artisan serve`'s child PHP server and `npm run dev`'s Vite are never orphaned.
144
+ - Rollback on a failed boot: a readiness gate that never opens stops everything that had
145
+ already started, instead of leaving a half-booted stack behind.
146
+ - `external: true` for services laracrew health-checks but never starts, such as Redis.
147
+ - `autostart: false` for services defined but not launched, started later on demand.
148
+
149
+ **Interface**
150
+
151
+ - A full-screen process tree as the default view, which renders no log output at all; logs are
152
+ opened one process at a time. Built on plain ANSI — no TUI framework.
153
+ - Per-process log inspection, a merged log across all services, restart/stop/start, and help.
154
+ - `external: true` dependencies are listed above the tree under their own heading rather than
155
+ numbered among the managed processes, so a digit key always selects something startable.
156
+ - `--plain` prefixed interleaved logs, selected automatically when stdout is not a TTY.
157
+ - `--json` newline-delimited events for scripting.
158
+ - A broken pipe (`laracrew up --json | head`) shuts the stack down cleanly instead of crashing.
159
+
160
+ **Logs**
161
+
162
+ - Every line is written to `~/.laracrew/logs/<stack>/<service>.log`, on by default, rotated by
163
+ size. Plain text with a sortable local timestamp and no colour codes, so `grep` works.
164
+ - `laracrew logs [service]` reads them back after the fact, with `-n`, `-f`, `--since 10m`,
165
+ `--all` to merge every service in time order, and `--list`.
166
+ - Writes go through a held file descriptor rather than a stream, so a line reaches disk before
167
+ a crashing process can lose it, and rotation can close the handle deterministically — a
168
+ rename with an open handle fails outright on Windows.
169
+
170
+ **Configuration**
171
+
172
+ - `~/.laracrew/` home: `config.yaml`, `projects.yaml`, one folder per stack, tasks, fragments.
173
+ - YAML stacks validated against a schema, with errors that name the file, the field, and a
174
+ spelling suggestion.
175
+ - Interpolation: `${env:VAR}`, `${env:VAR:fallback}`, `${project.path}`, `${project.env:VAR}`,
176
+ `${stack.dir}`, `${port:N}`.
177
+ - Groups, profiles, `--only` / `--except` filtering, and `services/*.yaml` fragments.
178
+
179
+ **Commands**
180
+
181
+ - `laracrew init [--examples]`, `ls`, `up`, `doctor`, `link`, `unlink`.
182
+ - `laracrew link <stack>` installs a global command that boots one stack, so a project set is
183
+ one word to launch. Generated shims are marked, and laracrew refuses to overwrite files it
184
+ did not write or to shadow names like `npm` and `git`.
185
+
186
+ **Checks (`laracrew doctor`)**
187
+
188
+ - Two projects sharing a Redis database *and* a queue or stream name, computing the effective
189
+ prefix the way Laravel does (`REDIS_PREFIX`, else a slug of `APP_NAME`) so it does not report
190
+ a collision that isn't one.
191
+ - `QUEUE_CONNECTION=sync` in a project that runs a queue worker.
192
+ - Port clashes, missing project paths, an unreachable Redis, and a PHP binary that will not run.
193
+
194
+ ### Notes
195
+
196
+ - Requires Node 20 or newer. Windows, macOS and Linux.
197
+ - Runtime dependencies: `commander`, `yaml`, `zod`.
198
+ - Accepted by the schema but not yet acted on: `watch`, `metrics` (read by `doctor` only), and
199
+ `hooks`. Stack files written today stay valid.
200
+
201
+ [Unreleased]: https://github.com/vidux/laracrew/compare/v0.2.2...HEAD
202
+ [0.2.2]: https://github.com/vidux/laracrew/compare/v0.2.1...v0.2.2
203
+ [0.2.1]: https://github.com/vidux/laracrew/compare/v0.2.0...v0.2.1
204
+ [0.2.0]: https://github.com/vidux/laracrew/compare/v0.1.2...v0.2.0
205
+ [0.1.2]: https://github.com/vidux/laracrew/compare/v0.1.1...v0.1.2
206
+ [0.1.1]: https://github.com/vidux/laracrew/releases/tag/v0.1.1
207
+ [0.1.0]: https://github.com/vidux/laracrew/releases/tag/v0.1.0
package/README.md CHANGED
@@ -310,6 +310,7 @@ the stack with a message naming both services rather than hanging.
310
310
  | `esc` | Back to the tree |
311
311
  | `a` | Merged log across every service - the firehose, on demand |
312
312
  | `r` | Restart the selected process (graceful: `queue:restart` first) |
313
+ | `R` | Restart every running process, in dependency order. Stopped, idle and failed ones stay as they are |
313
314
  | `s` | Stop it, or start it again if stopped |
314
315
  | `f` / `g` / `G` | Follow-pause tailing; jump to top or bottom |
315
316
  | `?` | Help |
@@ -580,13 +581,13 @@ project, `LARACREW_PROJECT=<key>`.
580
581
 
581
582
  ## Status
582
583
 
583
- **v0.2.1 — supervisor and full-screen view are built and tested.**
584
+ **v0.2.2 — supervisor and full-screen view are built and tested.**
584
585
 
585
586
  Working now: config pipeline, dependency-ordered boot with readiness gates, restart policies with
586
587
  exponential backoff, the graceful stop ladder — `stop.exec` for any process, `stop.artisan` as the
587
- Laravel shorthand — the full-screen process tree with per-process log inspection, logs persisted to
588
- disk with `laracrew logs` to read them back, plain and JSON renderers, per-stack global commands
589
- (`link` / `unlink`), `doctor`, `init`, `ls`.
588
+ Laravel shorthand — the full-screen process tree with per-process log inspection and a one-key
589
+ restart of everything running, logs persisted to disk with `laracrew logs` to read them back,
590
+ plain and JSON renderers, per-stack global commands (`link` / `unlink`), `doctor`, `init`, `ls`.
590
591
 
591
592
  See [CHANGELOG.md](CHANGELOG.md) for what changed in each release.
592
593
 
@@ -620,7 +621,7 @@ The full plan lives in [.claude/PLAN.md](.claude/PLAN.md), with the design in
620
621
  npm install
621
622
  npm run dev -- up example # tsx, no build step
622
623
  npm run build # tsup -> dist/index.js
623
- npm test # vitest, 244 tests
624
+ npm test # vitest, 248 tests
624
625
  npm run typecheck
625
626
  npm link # put `laracrew` on PATH while hacking on it
626
627
  ```
package/dist/index.js CHANGED
@@ -1612,6 +1612,45 @@ var Supervisor = class {
1612
1612
  const process2 = this.#requireProcess(name);
1613
1613
  await process2.restart();
1614
1614
  }
1615
+ /**
1616
+ * Restarts every service that is `running` right now and nothing else: a stopped or idle
1617
+ * service stays where it is, a failed one is not resurrected, one still starting is left to
1618
+ * finish. Dependency order, parallel within a level, so a dependency is back and ready
1619
+ * before the services that need it are bounced. Returns the names restarted, in that order.
1620
+ */
1621
+ async restartRunning() {
1622
+ const isTarget = (process2) => !process2.service.external && process2.state === "running";
1623
+ const wanted = new Set(this.processes.filter(isTarget).map((process2) => process2.service.name));
1624
+ if (wanted.size === 0) return [];
1625
+ this.#bus.emit({
1626
+ type: "notice",
1627
+ level: "info",
1628
+ message: `restarting ${wanted.size} running service${wanted.size === 1 ? "" : "s"}: ${[...wanted].join(", ")}`,
1629
+ at: Date.now()
1630
+ });
1631
+ const restarted = /* @__PURE__ */ new Set();
1632
+ for (const level of this.stack.levels) {
1633
+ await Promise.all(
1634
+ level.filter((name) => wanted.has(name)).map(async (name) => {
1635
+ const process2 = this.#processes.get(name);
1636
+ if (!isTarget(process2)) return;
1637
+ try {
1638
+ await process2.restart();
1639
+ restarted.add(name);
1640
+ } catch (error) {
1641
+ this.#bus.emit({
1642
+ type: "notice",
1643
+ level: "error",
1644
+ service: name,
1645
+ message: `restart failed: ${error instanceof Error ? error.message : String(error)}`,
1646
+ at: Date.now()
1647
+ });
1648
+ }
1649
+ })
1650
+ );
1651
+ }
1652
+ return this.stack.levels.flat().filter((name) => restarted.has(name));
1653
+ }
1615
1654
  async stopService(name) {
1616
1655
  await this.#requireProcess(name).stop();
1617
1656
  }
@@ -1834,6 +1873,7 @@ var handleKey = (ui, key, rowCount) => {
1834
1873
  case "t":
1835
1874
  return { ui, command: { type: "toggle", index: ui.selected } };
1836
1875
  case "r":
1876
+ if (key.shift) return { ui, command: { type: "restart-running" } };
1837
1877
  return { ui, command: { type: "restart", index: ui.selected } };
1838
1878
  case "a":
1839
1879
  return none({ ...ui, view: ui.view === "merged" ? "overview" : "merged", scrollBack: 0, follow: true });
@@ -1997,6 +2037,7 @@ var KEYS = {
1997
2037
  ["enter", "inspect log"],
1998
2038
  ["0-9", "jump"],
1999
2039
  ["r", "restart"],
2040
+ ["R", "restart all running"],
2000
2041
  ["s", "stop/start"],
2001
2042
  ["a", "all logs"],
2002
2043
  ["?", "help"],
@@ -2008,6 +2049,7 @@ var KEYS = {
2008
2049
  ["g/G", "top/bottom"],
2009
2050
  ["f", "follow"],
2010
2051
  ["r", "restart"],
2052
+ ["R", "restart all running"],
2011
2053
  ["q", "quit"]
2012
2054
  ],
2013
2055
  merged: [
@@ -2015,6 +2057,7 @@ var KEYS = {
2015
2057
  ["up/down", "scroll"],
2016
2058
  ["g/G", "top/bottom"],
2017
2059
  ["f", "follow"],
2060
+ ["R", "restart all running"],
2018
2061
  ["q", "quit"]
2019
2062
  ],
2020
2063
  help: [["any key", "back"]]
@@ -2141,6 +2184,7 @@ var help = ({ paint: paint2, glyph }) => [
2141
2184
  ["esc, s", "back to the stack overview"],
2142
2185
  ["a", "merged log across every service"],
2143
2186
  ["r", "restart the selected process (graceful)"],
2187
+ ["R", "restart every running process, in dependency order; stopped and idle stay put"],
2144
2188
  ["t", "toggle the selected process: stop if running, start if stopped"],
2145
2189
  ["f", "follow / pause tailing in a log view"],
2146
2190
  ["g / G", "jump to the top / bottom of a log"],
@@ -2370,7 +2414,7 @@ var runTui = async (supervisor, bus, options = {}) => {
2370
2414
  };
2371
2415
  const perform = (command) => {
2372
2416
  const stack = buildStackView(supervisor);
2373
- const row = stack.rows[command.type === "none" || command.type === "quit" || command.type === "force-quit" ? ui.selected : command.index];
2417
+ const row = stack.rows["index" in command ? command.index : ui.selected];
2374
2418
  switch (command.type) {
2375
2419
  case "quit":
2376
2420
  void shutdown("user quit");
@@ -2384,6 +2428,14 @@ var runTui = async (supervisor, bus, options = {}) => {
2384
2428
  setNote(`restarting ${row.name}\u2026`);
2385
2429
  void supervisor.restart(row.name).then(() => setNote(`${row.name} restarted`)).catch((error) => setNote(`restart failed: ${error instanceof Error ? error.message : String(error)}`));
2386
2430
  break;
2431
+ case "restart-running":
2432
+ setNote("restarting every running process\u2026");
2433
+ void supervisor.restartRunning().then(
2434
+ (names) => setNote(
2435
+ names.length === 0 ? "nothing is running \u2014 nothing to restart" : `restarted ${names.length} process${names.length === 1 ? "" : "es"}: ${names.join(", ")}`
2436
+ )
2437
+ ).catch((error) => setNote(`restart failed: ${error instanceof Error ? error.message : String(error)}`));
2438
+ break;
2387
2439
  case "toggle": {
2388
2440
  if (!row) break;
2389
2441
  if (row.external) {
@@ -3555,7 +3607,7 @@ ${paint("bold", "TASKS")}`);
3555
3607
  };
3556
3608
 
3557
3609
  // src/cli/program.ts
3558
- var VERSION = true ? "0.2.1" : versionFromPackageJson();
3610
+ var VERSION = true ? "0.2.2" : versionFromPackageJson();
3559
3611
  var resolveStackName = (explicit, env = process.env) => {
3560
3612
  if (explicit) return explicit;
3561
3613
  const configured = loadGlobalConfig(env).defaultStack;