@elmundi/ship-cli 0.11.2 → 0.12.1

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/README.md CHANGED
@@ -1,9 +1,32 @@
1
1
  # @elmundi/ship-cli
2
2
 
3
- **Ship** in your repository: agents get a standing policy to consume Ship **artifacts** (patterns, tools, workflows, collections, docs) via the **`shipctl`** CLI and the snippets `shipctl init` writes.
3
+ `shipctl` is the command-line interface to **Ship**. Three jobs:
4
+
5
+ 1. **Bootstrap a repo** so its agents (Cursor, Codex, Claude, Aider, Cline,
6
+ Continue, Windsurf, Zed, Gemini, OpenCode, Copilot, …) can consume Ship
7
+ artifacts the same way every other client does.
8
+ 2. **Sync the catalog** of `pattern` / `tool` / `collection` artifacts into
9
+ `.ship/cache/` and pin versions for reproducible runs.
10
+ 3. **Run lanes** (one-shot dispatch + GitHub Actions wrappers) and
11
+ **report Runs** so the operator console can render outcomes and route any
12
+ escalations into the Inbox.
4
13
 
5
14
  Published as **`@elmundi/ship-cli`** under the [elmundi](https://www.npmjs.com/org/elmundi) org; the binary name is **`shipctl`**.
6
15
 
16
+ > **Vocabulary.** The CLI speaks the protocol layer, the operator console
17
+ > speaks the product layer. Both are correct; they refer to the same things.
18
+ >
19
+ > | CLI / YAML / API (literal) | Operator console (prose) |
20
+ > |----------------------------|--------------------------|
21
+ > | `lanes:` entries in `.ship/config.yml`, `--lane <id>` | **Automations** |
22
+ > | `pattern:` artifacts (RFC-0001) | **Plays** |
23
+ > | `pipeline_runs` rows + `shipctl callback` payloads | **Runs** |
24
+ > | clarifications / improvements / approvals queue | **Inbox** items |
25
+ >
26
+ > The CLI keeps `lanes:` / `pattern:` / `--lane` literal forever; we are
27
+ > never going to break the YAML and flag surface. Help text and prose
28
+ > reach for the operator nouns when describing what users see.
29
+
7
30
  ## Requirements
8
31
 
9
32
  - **Node.js 20+**
@@ -78,7 +101,7 @@ Interactive run prints the plan and asks **Apply these changes? [y/N]**:
78
101
  npx @elmundi/ship-cli init
79
102
  ```
80
103
 
81
- After you confirm **`y`**, it writes/updates the files above. Injected content tells agents to **resolve Ship artifacts before use** via the CLI: **search → fetch → record `kind:id@version`** workflow, **`shipctl docs fetch`** for documentation paths, **`shipctl pattern|tool|workflow|collection`** for catalog bodies, and **`shipctl docs feedback`** for safe retro notes.
104
+ After you confirm **`y`**, it writes/updates the files above. Injected content tells agents to **resolve Ship artifacts before use** via the CLI: **search → fetch → record `kind:id@version`** workflow, **`shipctl docs fetch`** for documentation paths, **`shipctl pattern|tool|collection`** for catalog bodies, and **`shipctl docs feedback`** for safe retro notes.
82
105
 
83
106
  ### 5. Non-interactive (CI or scripts)
84
107
 
@@ -198,16 +221,30 @@ Next:
198
221
 
199
222
  ## Commands (quick reference)
200
223
 
201
- | Command | Role |
202
- |--------|------|
203
- | **`shipctl init`** | Inject agent-facing rules / sections with your **`SHIP_API_BASE`** (or **`--base-url`**). |
204
- | **`shipctl search …`** | Vector search over methodology corpus (`POST /search`). |
205
- | **`shipctl docs fetch …`**, **`shipctl docs feedback …`** | Documentation file fetch and retro feedback (`POST /fetch` with `path`, `POST /feedback`). |
206
- | **`shipctl pattern\|tool\|workflow\|collection`** **`list` \| `show` \| `fetch` \| `search`** | Artifact bodies; hosted mode uses the same API (including **`fetch`** via `POST /fetch` with `kind` + `id` + optional `version`). Plural aliases (`patterns`, `tools`, …) work. |
224
+ | Command | Role | Manual |
225
+ |---------|------|--------|
226
+ | **`shipctl init`** | Bootstrap an existing repo: agent rules, `.ship/config.yml`, optional CI scaffolding. | `documentation/discovery.md` |
227
+ | **`shipctl new`** | Greenfield: `git init` + minimal README + `init`. | this README, `New & Verify` |
228
+ | **`shipctl doctor`** | Inspect repo, propose stack, optionally write `.ship/inventory.json`. | this README, `Doctor` |
229
+ | **`shipctl config`** | Safe edits to `.ship/config.yml` (`init` / `get` / `set` / `validate` / `show` / `path`). | `documentation/configuration.md` |
230
+ | **`shipctl search`** | Vector search over docs + prompts (`POST /search`). | `documentation/discovery.md` |
231
+ | **`shipctl docs fetch`**, **`shipctl docs feedback`** | Documentation file fetch and retro feedback. | `documentation/discovery.md` |
232
+ | **`shipctl pattern\|tool\|collection`** **`list \| show \| fetch \| search`** | Versioned artifact bodies; plural aliases (`patterns`, `tools`, `collections`) work. | `documentation/authoring.md` |
233
+ | **`shipctl sync`** | Pull artifacts into `.ship/cache/`; with `--lock` writes `.ship/shipctl.lock.json` covering every Play the declared lanes depend on. | this README, `Config & Sync` |
234
+ | **`shipctl run`** | One-shot dispatch entry point. `kind: once` runs locally; other lane kinds are queued for the workspace runner. Reports its terminal status via the callback URL Ship injected. | `documentation/automations.md`, this README |
235
+ | **`shipctl lanes`** | Generate / inspect / delete the `.github/workflows/ship-<lane>.yml` thin wrappers (`install` / `list` / `remove`). | `documentation/automations.md`, this README |
236
+ | **`shipctl kickoff`** | Print a Play's pattern body for piping into the customer's agent in CI. | `documentation/automations.md` |
237
+ | **`shipctl callback`** | Pattern-side: report a Run's terminal status + RunSummary outcome so Ship can render the row and route escalations into the Inbox. | this README |
238
+ | **`shipctl knowledge init`** | Open a PR that seeds `.ship/knowledge/*.md` starter buckets. | `documentation/knowledge-buckets.md` |
239
+ | **`shipctl telemetry`** | Opt-in anonymous usage events (default OFF). | this README, `Telemetry & Feedback` |
240
+ | **`shipctl feedback`** | Local markdown drafts → POST `/feedback` → GitHub issue. | this README |
241
+ | **`shipctl verify`** | Post-adoption liveness checks (local + config + network). | this README, `New & Verify` |
242
+ | **`shipctl migrate`** | Upgrade `.ship/config.yml` from v1 to v2 (lanes-as-config). | `documentation/configuration.md` |
243
+ | **`shipctl help`** | Top-level command list with the same vocabulary callout. | — |
207
244
 
208
245
  **Maintainers / full Ship checkout:** if the current directory (or **`SHIP_REPO`**) is inside the Ship monorepo, **`list` / `show` / `fetch`** for catalogs can read manifests from **disk** instead of HTTP. **`shipctl search`** always uses HTTP.
209
246
 
210
- Run **`shipctl help`** for full usage.
247
+ Run **`shipctl help`** for the operator-first overview with the same vocabulary callout printed at the top.
211
248
 
212
249
  ## Doctor
213
250
 
@@ -264,16 +301,20 @@ repo git history — `shipctl sync` caches them in `.ship/cache/`, which is
264
301
 
265
302
  ```
266
303
  .ship/
267
- ├── config.yml # RFC-0002 schema; committed
304
+ ├── config.yml # RFC-0002 schema; committed. `lanes:` entries
305
+ │ # are what the operator console renders as
306
+ │ # Automations.
268
307
  ├── state.json # last_sync_at, last_manifest_hash; gitignored
308
+ ├── shipctl.lock.json # set by `shipctl sync --lock`; pins every
309
+ │ # pattern the declared lanes depend on
269
310
  ├── cache/ # per-repo artifact cache (gitignored)
270
311
  │ ├── pattern/<id>@<v>/
271
312
  │ │ ├── ARTIFACT.md # full body (frontmatter + content), per RFC-0005
272
313
  │ │ └── .meta.json # source, sha256, fetched_at, etc.
273
314
  │ ├── tool/<id>@<v>/…
274
- │ ├── workflow/<id>@<v>/…
275
315
  │ ├── collection/<id>@<v>/…
276
316
  │ └── doc/…
317
+ ├── knowledge/ # operator-edited markdown buckets, committed
277
318
  ├── telemetry-outbox.jsonl # buffered telemetry events (gitignored)
278
319
  └── feedback-drafts/ # feedback draft markdowns (gitignored)
279
320
  ```
@@ -297,12 +338,12 @@ Value parsing for `config set`:
297
338
  - Anything else → string.
298
339
 
299
340
  Dotted keys under `artifacts.pins` preserve the embedded slash:
300
- `artifacts.pins.pattern/cloud-developer`.
341
+ `artifacts.pins.pattern/role-developer`.
301
342
 
302
343
  ```bash
303
344
  shipctl config set stack.agents [cursor,codex]
304
345
  shipctl config set api.channel edge
305
- shipctl config set artifacts.pins.pattern/cloud-developer 1.4.2
346
+ shipctl config set artifacts.pins.pattern/role-developer 1.4.2
306
347
  ```
307
348
 
308
349
  ### `shipctl sync`
@@ -311,9 +352,10 @@ shipctl config set artifacts.pins.pattern/cloud-developer 1.4.2
311
352
  shipctl sync # pull latest for this stack
312
353
  shipctl sync --check-only # report changes without writing cache
313
354
  shipctl sync --dry-run # --check-only + planned HTTP calls
314
- shipctl sync --only pattern:cloud-developer [--only tool:gh-actions]
355
+ shipctl sync --only pattern:role-developer [--only tool:gh-actions]
315
356
  shipctl sync --channel edge
316
357
  shipctl sync --force-unpin # temporarily ignore version pins
358
+ shipctl sync --lock # write .ship/shipctl.lock.json after sync
317
359
  ```
318
360
 
319
361
  Summary format:
@@ -329,11 +371,189 @@ failed: 0
329
371
 
330
372
  Pins are honoured: an entry whose manifest version does not satisfy the pin is
331
373
  reported as `skipped_pin` unless `--force-unpin` is set. After a successful
332
- sync, `.ship/state.json` records `last_sync_at` and `last_manifest_hash`.
374
+ sync, `.ship/state.json` records `last_sync_at` and `last_manifest_hash`. With
375
+ `--lock`, `shipctl sync` also writes `.ship/shipctl.lock.json` covering every
376
+ Play that the declared lanes depend on, so subsequent `shipctl run` invocations
377
+ can refuse to drift off the pinned set.
333
378
 
334
379
  > Methodology docs never live in your repo. `shipctl sync` caches them in
335
380
  > `.ship/cache/`, sealed by `content_sha256` from the Ship manifest.
336
381
 
382
+ ## Run
383
+
384
+ `shipctl run` is the **one-shot dispatch entry point** for an Automation
385
+ (YAML key: `lanes:`). What it does depends on the lane's `kind`:
386
+
387
+ | `kind:` value | Behaviour of `shipctl run` |
388
+ |---------------|----------------------------|
389
+ | `once` | Executes the lane fully on the local machine (the pattern body is fed to the configured agent; the result is reported back via `shipctl callback`). |
390
+ | `lane` / `event` / `schedule` | Refuses to execute locally; the workspace's GitHub Actions runner picks the lane up via `.github/workflows/run-agent.yml`. The CLI exits with a clear message naming the lane id and its kind. |
391
+
392
+ ```bash
393
+ # preview which patterns + parameters the lane would dispatch with
394
+ shipctl run --lane pr-self-review --dry-run
395
+
396
+ # fanout: the same pattern over every repo in the workspace
397
+ shipctl run --lane fleet-mobile-knowledge-refresh \
398
+ --pattern fleet-knowledge-pack \
399
+ --fanout matrix \
400
+ --trigger event \
401
+ --json
402
+
403
+ # CI usage: shipctl injects --ship-run-id / --ship-callback-url / --ship-run-token
404
+ # automatically; you only set them by hand when running outside the workspace runner
405
+ shipctl run --lane release-cut \
406
+ --ship-run-id "$SHIP_RUN_ID" \
407
+ --ship-callback-url "$SHIP_CALLBACK_URL" \
408
+ --ship-run-token "$SHIP_RUN_TOKEN"
409
+ ```
410
+
411
+ Important flags:
412
+
413
+ - `--pattern <id>` — override the lane's default pattern (a composite Play may
414
+ declare several; this lets you target one specifically).
415
+ - `--fanout matrix|sequential|concurrent` — only meaningful for fleet-scope
416
+ lanes that target multiple repos.
417
+ - `--trigger event|schedule|manual|once` — the trigger the lane was wired for;
418
+ used to choose the payload shape.
419
+ - `--offline` — skip every HTTP probe (resolves patterns from `.ship/cache/`
420
+ only); useful for hermetic CI.
421
+ - `--dry-run` — print the dispatch plan and exit 0; no agent invocation.
422
+
423
+ A full Run lifecycle in production looks like: console (or schedule) creates a
424
+ `pipeline_run` row → workspace runner is dispatched → runner calls
425
+ `shipctl run --lane <id>` for `kind: once` lanes (or invokes the agent
426
+ directly for `kind: lane`) → the pattern calls `shipctl callback` with the
427
+ RunSummary → console renders the outcome row in `/runs` and any escalations
428
+ land in `/inbox`.
429
+
430
+ ## Lanes
431
+
432
+ `shipctl lanes` manages the **thin GitHub Actions wrappers** that delegate to
433
+ the reusable `run-agent.yml` workflow. Each lane in `.ship/config.yml`
434
+ generates one `.github/workflows/ship-<lane>.yml` file. The file itself is a
435
+ ~12-line yaml that does nothing more than `uses: ./.github/workflows/run-agent.yml`
436
+ with the right `lane:` input — all the logic lives in the reusable workflow,
437
+ so wrappers can be regenerated without touching execution semantics.
438
+
439
+ ```bash
440
+ # write workflow files for every lane in config.yml
441
+ shipctl lanes install --dry-run
442
+ shipctl lanes install --yes
443
+
444
+ # only one lane (or a few)
445
+ shipctl lanes install --only pr-self-review,release-cut
446
+
447
+ # wire to a specific shipctl version pin
448
+ shipctl lanes install --shipctl-version 0.12.1 \
449
+ --owner elmundi --repo ship --ref v0.12.1
450
+
451
+ # inspect what's on disk vs config.yml
452
+ shipctl lanes list --json
453
+
454
+ # remove generated wrappers (does NOT touch lanes: config)
455
+ shipctl lanes remove --dry-run
456
+ shipctl lanes remove --only deprecated-lane --yes
457
+ ```
458
+
459
+ Notes:
460
+
461
+ - `lanes install` writes files **only** for lanes with a configured trigger
462
+ (`on.push`, `on.schedule`, `on.workflow_dispatch`). `kind: once` lanes
463
+ intended to run only via `shipctl run` don't get a wrapper.
464
+ - The same wrapper covers a fleet-scope lane (one workflow file in your
465
+ pilot repo, fanout happens server-side via the matrix Ship dispatches).
466
+ - Removing a wrapper does **not** remove the lane from `.ship/config.yml`;
467
+ to fully retire an Automation, drop the `lanes.<id>` block from the YAML
468
+ and re-run `shipctl lanes install` so the file is reconciled.
469
+
470
+ ## Callback
471
+
472
+ `shipctl callback` is what a Play's pattern calls to **close the loop on a
473
+ Run**. It POSTs a structured payload to the URL Ship injected into the
474
+ runner (`SHIP_CALLBACK_URL` env or `--callback-url`) so the operator console
475
+ can render an outcome-first row in `/runs` and route any escalations into
476
+ `/inbox`.
477
+
478
+ The flag surface is **protocol-stable** (the workspace API depends on it);
479
+ the help is grouped by intent.
480
+
481
+ ### Identity (one of these is required)
482
+
483
+ ```
484
+ --run-id <uuid> # falls back to SHIP_RUN_ID
485
+ --callback-url <url> # falls back to SHIP_CALLBACK_URL
486
+ SHIP_RUN_TOKEN=<jwt> # bearer for the callback URL (CI sets this)
487
+ SHIP_API_BASE=<url> # base for {api}/v1/runs/<id>/callback when only --run-id is set
488
+ ```
489
+
490
+ ### Status & summary
491
+
492
+ ```
493
+ --status ok|fail|cancelled # required terminal state
494
+ --summary "Free text" # short human readout (kept in addition to outcome)
495
+ --metric key=value # repeatable; persisted as-is
496
+ ```
497
+
498
+ ### RunSummary outcome (Phase 3)
499
+
500
+ These map 1:1 to the `outcome:` JSONB column on `pipeline_runs` and to what
501
+ the console renders:
502
+
503
+ ```
504
+ --outcome-text "Reviewed PR · 3 suggestions · 1 fix applied"
505
+ --findings-count 3
506
+ --severity high=1 --severity medium=2 # repeatable; map of sev → count
507
+ --artifact pr:"Auto-fix: typo":"https://...": # repeatable; type:title[:ref]
508
+ --artifact comment:"Self-review summary":"https://github.com/.../pull/42#…"
509
+ --escalation clarification:"agent_low_confidence" # repeatable; type:reason
510
+ --requires-approval # toggle the approval gate
511
+ --approval-payload '{"...": "..."}' # JSON forwarded to the Inbox item
512
+ ```
513
+
514
+ You can also pass the full RunSummary as JSON via:
515
+
516
+ - `SHIP_RUN_OUTCOME=$JSON_STRING`
517
+ - `SHIP_RUN_OUTCOME_FILE=/path/to/outcome.json`
518
+
519
+ Flags merge on top of the env / file payload (CLI wins).
520
+
521
+ ### Example (canonical pattern recipe)
522
+
523
+ ```bash
524
+ shipctl callback --status ok \
525
+ --outcome-text "Reviewed PR · 3 suggestions · 1 fix applied" \
526
+ --findings-count 3 \
527
+ --severity high=1 --severity medium=2 \
528
+ --artifact comment:"PR self-review summary":"https://github.com/elmundi/ship/pull/42#issuecomment-…" \
529
+ --artifact pr:"Auto-fix: typo in README":"https://github.com/elmundi/ship/pull/43"
530
+ ```
531
+
532
+ The same `## Reporting` block lives at the bottom of every top-Play pattern
533
+ (see `artifacts/patterns/flow-pr-self-review/ARTIFACT.md` for the canonical
534
+ example). When you author a new Play, copy that block as the contract you
535
+ expect runners to honour.
536
+
537
+ ## Knowledge
538
+
539
+ `shipctl knowledge init` opens a PR in the target repo that seeds the
540
+ `.ship/knowledge/` starter buckets (e.g. `code-style.md`, `ui-runbook.md`).
541
+ The PR is intentionally minimal — operators are expected to fill the buckets
542
+ in over time.
543
+
544
+ ```bash
545
+ # requires SHIP_API_TOKEN; targets the workspace's wired GitHub installation
546
+ shipctl knowledge init \
547
+ --workspace 11111111-1111-1111-1111-111111111111 \
548
+ --repo elmundi/ship-pilot \
549
+ --only code-style,ui-runbook \
550
+ --json
551
+ ```
552
+
553
+ Behind the scenes, this hits the workspace API which uses the GitHub App
554
+ installation to open the PR. The buckets the Plays read at runtime live
555
+ under the same `.ship/knowledge/` tree the PR seeds.
556
+
337
557
  ## Telemetry & Feedback
338
558
 
339
559
  Anonymous telemetry is **opt-in and OFF by default** (RFC-0003). Nothing leaves
@@ -376,18 +596,18 @@ anywhere:
376
596
 
377
597
  ```bash
378
598
  # create a draft
379
- shipctl feedback draft --kind pattern --id cloud-developer --version 1.4.2 \
599
+ shipctl feedback draft --kind pattern --id role-developer --version 1.4.2 \
380
600
  --title "Missing mobile preview step" \
381
601
  --summary "Evidence checklist misses mobile preview" \
382
602
  --recommendation "Add a bullet under Evidence"
383
603
 
384
604
  # review / edit (uses $EDITOR)
385
605
  shipctl feedback list
386
- shipctl feedback show .ship/feedback-drafts/2026-04-17-11-30-15-pattern-cloud-developer.md
387
- shipctl feedback edit .ship/feedback-drafts/2026-04-17-11-30-15-pattern-cloud-developer.md
606
+ shipctl feedback show .ship/feedback-drafts/2026-04-17-11-30-15-pattern-role-developer.md
607
+ shipctl feedback edit .ship/feedback-drafts/2026-04-17-11-30-15-pattern-role-developer.md
388
608
 
389
609
  # submit → POST /feedback → GitHub issue URL; draft moves to sent/
390
- shipctl feedback submit .ship/feedback-drafts/2026-04-17-11-30-15-pattern-cloud-developer.md --yes
610
+ shipctl feedback submit .ship/feedback-drafts/2026-04-17-11-30-15-pattern-role-developer.md --yes
391
611
  ```
392
612
 
393
613
  Submission requires `kind`, `id`, `title`, and `summary`; missing fields fail
@@ -437,9 +657,9 @@ Post-adoption liveness check. A collection of independent checks under
437
657
  on-disk signals.
438
658
  - **network** (skip with `--no-network`) — `/health` (or `/patterns` as a
439
659
  fallback) reachable, local cache matches the channel catalog aggregated
440
- across `/patterns`, `/tools`, `/workflows`, `/collections`, Linear labels
441
- exist (needs `LINEAR_API_KEY`), every `${{ secrets.X }}` reference in
442
- gh-actions workflows is declared in `.env.example`.
660
+ across `/patterns`, `/tools`, `/collections`, Linear labels exist (needs
661
+ `LINEAR_API_KEY`), every `${{ secrets.X }}` reference in gh-actions
662
+ workflows is declared in `.env.example`.
443
663
 
444
664
  Exit `0` when no check returned `fail`; warnings do not fail.
445
665
 
@@ -482,3 +702,16 @@ git push --follow-tags # publish workflow picks up v0.11.0
482
702
  Repository secret **`NPM_TOKEN`** is required. Publish from the monorepo
483
703
  root: **`npm publish -w @elmundi/ship-cli`** — not `npm publish --prefix cli`
484
704
  (the root package is private).
705
+
706
+ ## Reference
707
+
708
+ Protocol-stable surfaces:
709
+
710
+ - **Artifacts protocol** (`POST /search`, `POST /fetch`) — RFC-0001.
711
+ - **Stack block + presets + adapters** — RFCs 0002 / 0004 / 0006.
712
+ - **Telemetry** — RFC-0003.
713
+ - **Operator IA** (Plays / Automations / Runs / Inbox) — RFC-0010.
714
+ - **HTTP schemas live next to the source**: `artifacts/tools/methodology-api/ARTIFACT.md`.
715
+
716
+ Every consumed artifact should be recorded in the PR or commit log as
717
+ `<kind>:<id>@<version>` so reviewers can replay what the agent saw.
package/bin/shipctl.mjs CHANGED
@@ -24,6 +24,7 @@ if (raw[0] === "--version" || raw[0] === "-v" || raw[0] === "version") {
24
24
  const { _, ...g } = extractGlobalArgv(raw);
25
25
  const ctx = {
26
26
  baseUrl: g.baseUrl,
27
+ baseUrlSource: g.baseUrlSource,
27
28
  json: g.json,
28
29
  yes: g.yes,
29
30
  force: g.force,
@@ -85,12 +86,6 @@ try {
85
86
  process.exit(0);
86
87
  }
87
88
 
88
- if (cmd === "doctor") {
89
- const { doctorCommand } = await import("../lib/commands/doctor.mjs");
90
- await doctorCommand(ctx, rest);
91
- process.exit(0);
92
- }
93
-
94
89
  if (cmd === "verify") {
95
90
  const { verifyCommand } = await import("../lib/commands/verify.mjs");
96
91
  await verifyCommand(ctx, rest);
@@ -127,6 +122,12 @@ try {
127
122
  process.exit(0);
128
123
  }
129
124
 
125
+ if (cmd === "trigger") {
126
+ const { triggerCommand } = await import("../lib/commands/trigger.mjs");
127
+ await triggerCommand(ctx, rest);
128
+ process.exit(0);
129
+ }
130
+
130
131
  if (cmd === "kickoff") {
131
132
  const { kickoffCommand } = await import("../lib/commands/kickoff.mjs");
132
133
  await kickoffCommand(ctx, rest);
@@ -151,7 +152,10 @@ try {
151
152
  process.exit(0);
152
153
  }
153
154
 
154
- if (cmd === "lanes") {
155
+ /* `lanes` is the protocol-stable name; `automations` is the
156
+ * operator-friendly soft alias. Both dispatch to the same handler
157
+ * indefinitely — we are not deprecating the original. */
158
+ if (cmd === "lanes" || cmd === "automations") {
155
159
  const { lanesCommand } = await import("../lib/commands/lanes.mjs");
156
160
  await lanesCommand(ctx, rest);
157
161
  process.exit(0);
@@ -263,6 +263,55 @@ function buildRecommendedTools({ preset }) {
263
263
  "Detox or Maestro for device-farm E2E",
264
264
  "Expo Updates or CodePush for OTA patches",
265
265
  ],
266
+ "mobile-app-deep": [
267
+ "EAS Build / Fastlane for iOS + Android signed builds",
268
+ "Detox or Maestro for device-farm E2E",
269
+ "Expo Updates or CodePush for OTA patches",
270
+ "Crashlytics or Sentry for post-release crash-rate tracking",
271
+ "TestFlight external testing + Play closed-testing tracks",
272
+ ],
273
+ "ml-project": [
274
+ "DVC / LakeFS / Delta Lake for dataset + model versioning",
275
+ "MLflow or Weights & Biases for eval tracking",
276
+ "Feast / Tecton / Databricks Feature Store for feature contracts",
277
+ "Great Expectations / Pandera for data validation",
278
+ ],
279
+ platform: [
280
+ "Terraform + a state backend you can audit (S3 / Terraform Cloud)",
281
+ "Kyverno / OPA / Conftest for manifest-level policy gating",
282
+ "Infracost or cloud-pricing API for per-PR cost deltas",
283
+ "Prometheus / Datadog / CloudWatch for SLO burn-rate queries",
284
+ "Syft / Trivy / Grype for SBOM generation at release",
285
+ ],
286
+ regulated: [
287
+ "Presidio / Nightfall / Transcend for PII detection helpers",
288
+ "An audit log store with hash-chain integrity (append-only DB / S3 with object-lock)",
289
+ "Vault / AWS Secrets Manager for scoped compliance credentials",
290
+ "Drata / Vanta / Secureframe for evidence-bundle sync (optional)",
291
+ ],
292
+ "desktop-app": [
293
+ "Apple notarytool + Developer ID cert for macOS notarization",
294
+ "Windows Authenticode / EV code-signing cert + SignTool",
295
+ "electron-updater / Sparkle / Squirrel for staged auto-update",
296
+ "A crash-telemetry SDK (Sentry / Bugsnag) scoped to desktop builds",
297
+ "Per-platform installer toolchain (dmg, msi, deb, AppImage)",
298
+ ],
299
+ firmware: [
300
+ "A cross-toolchain (arm-none-eabi-gcc / clang-embedded / IDF / Zephyr SDK)",
301
+ "PlatformIO / ESP-IDF / Zephyr / Yocto build system tied into CI",
302
+ "Renode or QEMU for simulated HIL runs on PR",
303
+ "An OTA backend (AWS IoT Jobs / Azure IoT Hub / Mender / Balena / Nerves)",
304
+ "Octopart / Digi-Key / Mouser API for BOM pricing + lifecycle",
305
+ "A bench power analyzer or logged DMM for power-profile ground truth",
306
+ ],
307
+ game: [
308
+ "Your engine (Unity / Unreal / Godot) hooked into CI with headless builds",
309
+ "Perforce or Git LFS for binary art assets",
310
+ "Unity Cloud Build / UGS / Jenkins for per-PR cook builds",
311
+ "RenderDoc / Tracy / Unreal Insights for frametime capture",
312
+ "A crash-telemetry SDK (Backtrace / Sentry / Unity Cloud Diagnostics)",
313
+ "A localization TMS (Crowdin / Lokalise / Smartling) for event strings",
314
+ ],
266
315
  "web-app": [
267
316
  "Playwright (hosted) for PR preview E2E",
268
317
  "Preview deployments (Vercel / Netlify / Fly) per PR",