Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ name: CI
# after a crash. Those need a privileged, reboot-capable host and are validated
# by hand / in a later wave, NOT here. This job verifies the cross-platform UNIT
# LOGIC (templates render, argv resolves, the suite is green on each OS) and
# that the bin builds + packs clean on each OS - nothing about live service
# lifecycle.
# that the bin builds + packs clean on each OS. A dedicated executable adapter
# fixture below drives every real platform adapter through an in-memory manager
# boundary; it is explicit simulation and does not claim live/reboot proof.
#
# Node is pinned to the package's engine floor (22.x). The cross-OS proof is the
# dimension that matters, so the matrix spends its axes on OS, not Node majors.
Expand Down Expand Up @@ -76,12 +77,21 @@ jobs:
- name: Doctor gate (typecheck + test)
run: npm run ci

# GitHub-hosted runners are not reboot-capable service test hosts. Execute the
# real adapter transactions against a deterministic manager fixture on every OS
# without claiming this is live launchd/systemd/Task Scheduler service proof.
- name: Native adapter integration fixture (simulated manager boundary)
run: npx vitest run tests/service/native-adapter.integration.test.ts

# The build is `tsc && node esbuild.config.mjs` - both must run. Proves the
# single-file bin bundles on each OS (esbuild.config.mjs chmod is a no-op on
# win32 but exercised, and the shebang/define injection is validated).
- name: Build (tsc + esbuild bin)
run: npm run build

- name: Packed CLI conformance
run: npm run test:packed-cli

# Prove `npm pack`'s tarball carries no forbidden/secret filenames, no
# source/test/dist leak, and the bin is present - on each OS (pack-check has
# a win32 branch for the npm.cmd shell-out). `pack:check` runs `prepack` ->
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## v0.6.0 — 2026-07-14

Doctor's CLI now standardizes on the Apiary command contract: canonical `service-install`/`service-uninstall`, a new `logs`/`telemetry` surface, stable JSON output, and a verified `update` command with health-checked rollback, while keeping the previous commands available as deprecated aliases.

## Unreleased

- Standardize Doctor on the Apiary CLI contract: canonical `service-install` / `service-uninstall`, stable JSON envelopes, grouped Doctor branding, service-isolated `logs`, read-only `telemetry`, and shared exit codes.
- Preserve `install-service` / `uninstall-service` as deprecated aliases, preserve fleet incident records as `doctor incidents`, and preserve the former primary-daemon updater as `doctor daemon-update`.
- Make canonical `doctor update` update Doctor itself with pinned release resolution, restart/health verification, and rollback to the installed version on failed verification. `doctor self-update` remains a deprecated compatibility alias.

## v0.5.0 — 2026-07-12

Doctor now re-reads the daemon registry at runtime and automatically supervises newly registered daemons (or drops removed ones) without requiring a restart, fixing onboarding hangs where services registered after boot were never watched.
Expand Down
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ To install or update it on its own:

```bash
npm install -g @legioncodeinc/doctor
doctor install-service # register the OS service (restart-on-crash, start-on-boot)
doctor service-install # register the OS service (restart-on-crash, start-on-boot)
```

<details>
Expand Down Expand Up @@ -161,23 +161,28 @@ The dashboard is **Hive portal at `http://127.0.0.1:3853`**: fleet health lives

Run `doctor` with no arguments for the banner and menu. The full surface:

The [Apiary suite command matrix](https://github.com/legioncodeinc/cli-kit/blob/main/library/notes/prd-003-command-matrix.md) is the normative cross-product contract; Doctor-specific behavior and compatibility commands are listed below.

| Command | What it does |
|---|---|
| `doctor status` | daemon health, service state, versions, last heal, opt-out flags |
| `doctor start` / `doctor stop` / `doctor restart` | control Doctor's installed OS service; restart waits for the service to report running |
| `doctor install` / `doctor uninstall --yes` | onboard or remove Doctor and Doctor-owned state |
| `doctor service-install` / `doctor service-uninstall` | reconcile or remove only Doctor's OS service definition |
| `doctor update [--check]` | safely update Doctor, verify the restarted service, and roll back on failed health verification |
| `doctor status` | standard service/install/health/path snapshot plus supervised-daemon detail |
| `doctor logs [--lines N] [--no-follow] [--since 30m]` | tail only Doctor's authoritative service log with secret redaction |
| `doctor telemetry` | show read-only collection state, controlling setting, and destination class |
| `doctor diagnose` | classify health and print the recommended fix, taking **no** action |
| `doctor heal` | run the remediation ladder once (gated steps confirm first) |
| `doctor start` / `doctor stop` | start or stop Doctor's own daemon (via the OS service when registered) |
| `doctor restart` | restart the primary daemon (rung 1) |
| `doctor reinstall` | reinstall the primary daemon (rung 2) |
| `doctor uninstall-hivemind` | remove a conflicting `@deeplake/hivemind` global (rung 3, confirms) |
| `doctor update [--check]` | update the primary daemon via the blessed gate |
| `doctor self-update` | update Doctor's own package (the **only** thing that does) |
| `doctor install-service` / `uninstall-service` | register or remove the OS service |
| `doctor uninstall` | remove Doctor's service unit, registry entry, and state dir (npm package stays) |
| `doctor daemon-update [--check]` | compatibility surface for the former primary-daemon `update` behavior |
| `doctor self-update` | deprecated alias for canonical `doctor update` |
| `doctor install-service` / `uninstall-service` | deprecated aliases for the canonical service commands |
| `doctor purge` | **DESTRUCTIVE:** wipe every Apiary asset on the machine (confirms, see below) |
| `doctor logs` | tail incident logs for all daemons, or one via `--daemon <name>` |
| `doctor incidents [--daemon name]` | read Doctor's product-specific fleet incident records |

Doctor never updates itself in the background. `self-update` is the single, explicit way to bump it, and there is deliberately no `clear-credentials` command: credential purges are only ever recommended via escalation, never automated.
Doctor never updates itself in the background. Canonical `update` is the explicit, verified Doctor update transaction; the old primary-daemon meaning moved to `daemon-update`. There is deliberately no `clear-credentials` command: credential purges are only ever recommended via escalation, never automated.

### The escape hatch: `doctor purge`

Expand Down
16 changes: 14 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@legioncodeinc/doctor",
"version": "0.5.0",
"version": "0.6.0",
"//version": "INT placeholder, still 0.0.0 so a stray publish is obvious AND the release workflow's preflight fails closed on it (a real cut bumps this deliberately, mirroring the parent's `npm version` flow). Doctor versions INDEPENDENTLY of the root honeycomb package (PRD-063 OD-6) and never auto-updates its own package (AC-6); esbuild reads THIS version and inlines it via `define` (__DOCTOR_VERSION__). This one manifest IS the single source of truth - there is no cross-manifest sync to run.",
"description": "Doctor: a deliberately tiny, dependency-light, self-healing watchdog that keeps the Honeycomb primary daemon alive and reports when it cannot.",
"license": "AGPL-3.0-or-later",
Expand All @@ -26,6 +26,7 @@
"bundle": "node esbuild.config.mjs",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:packed-cli": "node scripts/verify-packed-cli.mjs",
"test:watch": "vitest",
"ci": "npm run typecheck && npm run test",
"pack:check": "node scripts/pack-check.mjs",
Expand All @@ -44,5 +45,8 @@
},
"allowScripts": {
"esbuild@0.28.1": true
},
"dependencies": {
"@legioncodeinc/cli-kit": "^0.3.0"
}
}
Loading
Loading