diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index e02cddcb..ff7d73d8 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -1426,9 +1426,9 @@ Plans: **Requirements**: TBD **Success Criteria** (what must be TRUE): - 1. A new `diary` module (beach-themed README, root table row) owns the application's logging, modelled on Laravel's `config/logging.php`: `log.level` and `log.channel` config keys, channels for stderr, a `stack` of several channels, a daily file with retention and JSON output, a console handler that renders records in `bonfire`'s style with a component prefix, and named component loggers. `sunscreen` redaction wraps every channel; `sunscreen.Install` keeps working as a compatibility shim. Standard library only, or a decision note for any new dependency. + 1. A new `diary` module (beach-themed README, root table row) owns the application's logging, modelled on Laravel's `config/logging.php`: `log.default`, `log.level`, `log.channels` and `log.components` config keys, channels for stderr, a `stack` of several channels, a daily file with retention and JSON output, a console handler that renders records in `bonfire`'s style with a component prefix, and named component loggers. `sunscreen` redaction wraps every channel; `sunscreen.Install` keeps working as a compatibility shim. Daily files under `storage/logs/` are on by default. The console uses the charm stack (lipgloss, bubbles/bubbletea, huh, fang) after SSU's terminal UX, with a decision note for the new dependencies before code. 2. `bonfire` provides global flags on every command, modelled on Artisan: `-v`/`-vv`/`-vvv`, `-q`/`--quiet`, `--no-ansi`/`--ansi`, `-n`/`--no-interaction` and `--env`. Verbosity sets the `diary` level at runtime, so framework and third-party logs (River, plugins) are hidden or formatted consistently at the default level and shown in full at `-vv`. - 3. Every command has a description, a long help with usage examples, and documented arguments and options with defaults. `list` output is grouped by namespace like `php artisan list`, and `summer help ` and ` help ` work. A test fails when a registered command lacks a description or examples. + 3. Every command has a description, a long help with usage examples, and documented arguments and options with defaults. `list` output is grouped by namespace like `php artisan list`, and `summer help ` and ` help ` work. A coverage check reports registered commands lacking a description or examples: it fails for the framework's own commands and warns in plugin repos. 4. A sweep of all existing commands (framework modules and shared plugins) checks them against an Artisan feature checklist and fixes the gaps found, without changing command logic or breaking existing names and flags. The checklist covers confirmation with `--force` on destructive actions, `--pretend`/dry-run where it applies, progress bars and tables for bulk work, and `--json` output for scripting. Long-running commands such as `serve` and `queue:work` print a startup summary of what booted (environment, plugins, database and migrations state, queues, scheduler, mail, cache and realtime drivers, URLs) and report what they did on shutdown. One-shot commands end with a result line and a meaningful exit code. 5. The `diary` and `bonfire` READMEs, the docs console and logging pages, the summercms.io landing site and its roadmap presentation reflect the new console. The new code has unit tests, delivered in the phase's last plan. diff --git a/.planning/phases/19.8-console-commands-overhaul/19.8-CONTEXT.md b/.planning/phases/19.8-console-commands-overhaul/19.8-CONTEXT.md new file mode 100644 index 00000000..f6b46d66 --- /dev/null +++ b/.planning/phases/19.8-console-commands-overhaul/19.8-CONTEXT.md @@ -0,0 +1,214 @@ +# Phase 19.8: Console commands overhaul - Context + +**Gathered:** 2026-10-21 +**Status:** Ready for planning + + +## Phase Boundary + +Console commands in `summer` and in application binaries reach the bar set by Laravel's Artisan. Three pieces: a new `diary` logging module, the shared console layer in `bonfire` (global flags, help, `list`, widgets, errors), and a sweep of every existing command's help and output across `summercms.go` and the shared plugin repos. Command logic, names and flags do not change. Planned shape stays at 4 plans: (1) `diary`, (2) `bonfire` flags/help/list/widgets, (3) sweep, (4) unit tests. + +The phase was added as Phase 32 on 2026-10-20 and renumbered to 19.8 on 2026-10-21. + + + + +## Implementation Decisions + +### Console stack and look +- **D-01:** The console look follows SSU's terminal UX (`/media/nvme/dev/pxpx/ssu/internal/cli/tui`): spinner with counter and current item, multi-row live process view, progress model, arrow-key selector with confirmation screen, lipgloss status styles. The earlier Scala `summer-bonfire` design was itself modelled on SSU; the braille spinner and cyan-to-green gradient bar already in Go `bonfire` came from it. — **Reversibility:** costly — every command's output and the widget API depend on it +- **D-02:** Adopt the charm stack in `bonfire`: `charmbracelet/lipgloss` (styling, tables), `charmbracelet/bubbles` + `bubbletea` (live views, spinner, progress), `charmbracelet/huh` (prompts), `charmbracelet/fang` (cobra styling and error rendering). A decision note naming these dependencies and versions lands before any code (project rule: new dependency needs a decision note). Port SSU's widgets rather than inventing new ones. The existing `bonfire.Output` interface stays source-compatible for plugins (no breaking changes in shared plugins). — **Reversibility:** costly — adds dependencies to every application binary; removing them later means rewriting the widgets +- **D-03:** Every widget degrades on non-TTY, `-q`, `--no-ansi` and `-n`: no animation, plain lines, prompts return defaults (or fail with a hint when no default exists). + +### What `serve` shows +- **D-04:** At default verbosity `serve` prints a startup summary and then **all INFO logs**, restyled through `diary`'s console handler (consistent prefixes per component instead of today's raw `slog` text mixed with styled lines). `-q` reduces to warnings/errors; `-v`/`-vv`/`-vvv` add debug detail. +- **D-05:** The output format follows the TTY: styled console output on a terminal; structured logs (JSON or logfmt per channel config) when stdout is not a TTY (systemd, Docker). The startup summary is also emitted as log records, so journald keeps it. +- **D-06:** Startup summary is a compact aligned block of about 8–12 lines: app name and version (plus framework version), environment, URL(s), database and migration state ("up to date" / "3 pending"), plugins (count and names), queue and scheduler, mail, cache and realtime drivers. `-v` adds per-plugin details. +- **D-07:** Shutdown (Ctrl+C / SIGTERM) prints a short report: draining, requests drained, workers stopped with job counts (done, in flight returned to the queue), uptime, and any drain errors. The same pattern applies to other long-running commands (`queue:work`, `dev`). + +### Logging (`diary`) +- **D-08:** `config/log.yaml` mirrors Laravel's `config/logging.php` shape: `log.default` names the default channel, `log.level` the global level, `log.channels` is a map of named channels with `driver` (`console`, `stderr`, `daily`, `stack`, `null`), `level`, `format`, `path`, `days`, and `channels` for a stack. `SUMMER_LOG_*` env overrides work through `compass` as for any key. This replaces the `log.level`/`log.channel` pair in the roadmap text with `log.level`/`log.default`. — **Reversibility:** costly — published config keys in every application's `config/log.yaml` + ```yaml + # config/log.yaml + default: stack + level: info + channels: + stack: + driver: stack + channels: [console, daily] + console: + driver: console # styled on TTY, json/logfmt otherwise + format: auto + daily: + driver: daily + path: storage/logs/summer.log + days: 14 + format: json + level: warn + components: + river: warn + surf: info + ``` +- **D-09:** Per-component levels through a `log.components` map (`river: warn`, `journal: debug`). Plugins and modules get a named logger from `diary` (for example `diary.For("journal")`) instead of `slog.Default`; verbosity flags raise every component. +- **D-10:** Daily log files are **on everywhere** by default, like Laravel: `summer new` and the framework defaults use a `stack` of `console` + `daily` at `storage/logs/`, with retention (14 days default). Console output stays on as well. +- **D-11:** The request log is INFO from `surf` under a named `http` component (method, path, status, duration, request id), so it shows by default and can be silenced with `components.http: warn`. +- **D-12:** `sunscreen` redaction wraps every channel; `sunscreen.Install`/`InstallDefault` keep working as compatibility shims. + +### Help and `list` +- **D-13:** Bare `` / `summer` prints an Artisan-style list: version header (`SummerCMS acme v1.4.0 (framework v0.3.2)`), usage, global options, then commands grouped by namespace (top-level first, then `admin`, `make`, `migrate`…) with aligned descriptions. Styled with lipgloss in the same palette as the rest of the console. + ``` + SummerCMS acme v1.4.0 (framework v0.3.2) + + Usage: + bin/acme [options] [arguments] + + Options: + -h, --help Display help for the given command + -q, --quiet Do not output any message + -v|vv|vvv Increase verbosity + --ansi|no-ansi Force (or disable) ANSI output + -n, --no-interaction Do not ask any interactive question + --env The environment the command runs under + + Available commands: + list List commands + migrate Run the database migrations + serve Serve the application + admin + admin:create Create a backend administrator + admin:token:create Create a personal API token + make + make:command Create a new console command + make:model Create a new model and migration + ``` +- **D-14:** Per-command help uses the Artisan layout: Description / Usage / Arguments / Options (with defaults) / Examples. fang is used for styling and error rendering, not for its own help layout. + ``` + Description: + Create a personal API token for a backend admin + + Usage: + admin:token:create [options] + + Arguments: + login The admin's login + + Options: + --scopes=SCOPES Comma-separated scopes [default: "*"] + --expires=DAYS Days until expiry [default: 90] + --json Print the token as JSON + + Examples: + # Token for the MCP server, journal only + bin/acme admin:token:create jane --scopes=journal.* + + # Script-friendly output + bin/acme admin:token:create jane --json + ``` +- **D-15:** New additive `bonfire.Command` fields: `Examples []Example` (command line + description, rendered as a section) and `Hidden bool` (runnable, left out of `list`; `list --all` shows it). Additive only, so existing plugins compile unchanged. +- **D-16:** `list --format=json` outputs every command with arguments, options and examples, for docs checks and AI agents. +- **D-17:** Documentation coverage check: `bonfire` exports a helper that reports commands lacking a description or examples. It **fails** for `summercms.go`'s own commands and **warns only** in plugin repos. Roadmap success criterion 3 is reworded to match. + +### Errors +- **D-18:** Errors render as a styled block: the cause, a short explanation, then a `Try:` hint with the exact command that fixes it, then "Run with -v for details". `-v` adds the wrapped error chain. Exit codes: 1 for failure, 2 for usage errors. + ``` + ERROR No application found in /home/jane/acme/plugins. + + summer make:command needs an application root (summer.yaml). + + Try: + cd ..; bin/acme make:command acme.blog:publish + + Run with -v for details. + ``` +- **D-19:** Hints for known failure modes are part of the sweep: run from the wrong directory (suggest `cd ..; bin/ `), pending migrations (suggest `bin/ migrate`), missing config or env, database unreachable. Detecting the database when run from the wrong directory is **not** fixed; only the message improves. + +### Sweep +- **D-20:** The sweep covers `summercms.go` and every shared plugin repo with commands: `sm-paymentgateway-plugin`, `sm-oauthserver-plugin`, `sm-journal-plugin`, `sm-sitemap-plugin`, `sm-chat-plugin`, `sm-chatchannels-plugin`, `sm-chatvideo-plugin`, `sm-faq-plugin`, `sm-menu-plugin`, `sm-herald-plugin`, `sm-forum-plugin`, `sm-pgstripe-plugin`, and `sm-user-plugin`. Changes per plugin: description, long help, examples, named loggers, output polish. One commit per plugin repo, pushed first, then pointer bumps in the apps as separate commits. No breaking changes to command names or flags. +- **D-21:** The Artisan checklist applies where it fits, not uniformly: `--force` confirmation on destructive commands, `--pretend` on migrate-like and bulk writers, `--json` on commands whose output scripts read (lists, tokens, status), progress for bulk loops. The plan records each command's checklist result so the gaps are explicit. +- **D-22:** One-shot commands end with a result line and a meaningful exit code. + +### Claude's Discretion +- Exact palette, symbols and spacing, as long as they match SSU's styles and stay consistent between `list`, help, widgets and log lines. +- Console handler choice inside `diary` (lipgloss-styled handler of our own, or `charmbracelet/log`), to be settled in the dependency decision note. +- Exact semantics of `--env` and `-n` defaults, following Artisan. + +### Folded Todos +- **Reject duplicate console command names in bonfire.NewRoot** (`.planning/todos/pending/bonfire-duplicate-command-names.md`): `NewRoot` fails with an error naming the duplicated command; update the bonfire README and `docs/console/writing-commands.md`. Fits plan 2. +- **Scaffolded files carry a DO NOT EDIT header, and make:command cannot reach the application** (`.planning/todos/pending/scaffold-generated-header-and-command-deps.md`): folded **partially**. The scaffolder itself is not changed; the errors these commands throw get proper messages with actionable hints (D-18, D-19). The todo stays open for the scaffolder fix. + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +### Phase scope +- `.planning/ROADMAP.md` § "Phase 19.8: Console commands overhaul": goal, success criteria, planned shape + +### Console look (reference implementation) +- `/media/nvme/dev/pxpx/ssu/internal/cli/tui/`: SSU's widgets on the charm stack (`spinner.go`, `process.go`, `progress.go`, `selector.go`, `selector_keys.go`, `confirm.go`, `styles.go`, `tui.go`); the target look +- `../.depreciated/modules/summer-bonfire/PLAN.md` § "Custom-Built Console Widgets" and "SSU Feature Mapping": the earlier Scala design mapping SSU's widgets, including non-TTY fallbacks + +### Existing modules +- `modules/bonfire/README.md`, `modules/bonfire/command.go`, `modules/bonfire/root.go`, `modules/bonfire/output.go`, `modules/bonfire/widgets.go`: current command API, cobra root, Output interface, spinner and gradient bar +- `modules/sunscreen/sunscreen.go`, `modules/sunscreen/README.md`: redaction handler and `InstallDefault` (one hard-coded TextHandler at INFO) +- `modules/compass/README.md`: config layering, `SUMMER_` env overrides, per-environment directories +- `internal/build/build.go`: generated `main`, where `sunscreen` is installed and plugin commands are appended + +### Todos +- `.planning/todos/pending/bonfire-duplicate-command-names.md` +- `.planning/todos/pending/scaffold-generated-header-and-command-deps.md` + +### Project rules +- `.planning/research/STACK.md`: decided dependency set; new charm dependencies need a decision note in `.planning/notes/` +- `CLAUDE.md` § Documentation rules and § Sibling repositories: README/docs in the same commit; plugin commit first, then app pointer bump + + + + +## Existing Code Insights + +### Reusable Assets +- `bonfire.Output` (`output.go`): Info/Success/Error/Warning, Table, Spinner, Progress, Ask/Confirm/Choice/Secret; has TTY, interactive and color detection already. Keep the interface; re-implement behind it with the charm stack. +- `bonfire.Command`/`Flag`/`Arg` (`command.go`): add `Examples` and `Hidden` additively; flag defaults are already declared and can render as `[default: …]`. +- `widgets.go`: braille spinner frames and 256-color gradient (cyan to green), the Scala/SSU heritage to keep visually. +- `sunscreen.Wrap(slog.Handler)`: wraps any handler, so every `diary` channel can be wrapped. + +### Established Patterns +- Commands are `namespace:verb` (plus bare `build`, `dev`, `new`, `serve`, `migrate`); grouping by namespace in `list` follows the name before the colon. +- cobra root with `SilenceUsage`/`SilenceErrors`; help currently falls back to cobra's default layout. +- Config is YAML sections per file through `compass` (`config/log.yaml` → `log.*`), with per-environment overrides. + +### Integration Points +- `internal/build` generated `main`: replaces the `sunscreen.InstallDefault` call with `diary` setup and builds the root through `bonfire`. +- `surf`: request logging under the `http` component. +- `conga`/River: River's logger is routed through a `diary` component logger (`river`). +- `serve`, `dev`, `queue:work`: startup summary and shutdown report. +- About 40 real commands: 13 framework files in `modules/`, `cmd/summer`, `internal/`, plus 12 plugin repos and `sm-user-plugin`. + + + + +## Specific Ideas + +- "Magic animations" means SSU's terminal UX: the Scala-era bonfire plan was explicitly "inspired by SSU", so SSU's TUI package is the source to port. +- The Artisan list and help layouts above (D-13, D-14) and the error block (D-18) are the approved previews. +- Error hints must be useful in practice, e.g. `cd ..; bin/ ` when a command runs from the wrong directory. + + + + +## Deferred Ideas + +None; the discussion stayed within phase scope. + +### Reviewed Todos (not folded) +- The 20 other keyword matches (benchmark, cache purge, uploads XSS, plugin ports, OIDC parity, etc.) are unrelated to the console and stay in their own phases or todos. + + + +--- + +*Phase: 19.8-console-commands-overhaul* +*Context gathered: 2026-10-21* diff --git a/.planning/phases/19.8-console-commands-overhaul/19.8-DISCUSSION-LOG.md b/.planning/phases/19.8-console-commands-overhaul/19.8-DISCUSSION-LOG.md new file mode 100644 index 00000000..f5b64036 --- /dev/null +++ b/.planning/phases/19.8-console-commands-overhaul/19.8-DISCUSSION-LOG.md @@ -0,0 +1,162 @@ +# Phase 19.8: Console commands overhaul - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** 2026-10-21 +**Phase:** 19.8-console-commands-overhaul +**Areas discussed:** Todos, What serve shows, Console stack, Logging config shape, Help and list look, Sweep depth and plugins + +--- + +## Todos + +| Option | Description | Selected | +|--------|-------------|----------| +| Duplicate command names | bonfire.NewRoot fails on duplicate names | ✓ | +| make:command app access | Scaffolder header and app access for generated commands | partial | + +**User's choice:** Duplicate names folded fully. For make:command: "errors like these that make:command throw need a proper messaging too. They may error, but they should suggest `cd ..; bin/ ` and be useful. We don't need to fix database detection when run from wrong directory, but think about these when working on commands console outputs." + +--- + +## What serve shows + +| Option | Description | Selected | +|--------|-------------|----------| +| Summary + requests | Summary, then one line per request; plugin INFO at -v | | +| Summary only | Summary, then warnings/errors only | | +| Summary + all INFO | Summary, then every INFO log restyled | ✓ | + +| Option | Description | Selected | +|--------|-------------|----------| +| TTY decides | Styled on terminal, structured logs otherwise | ✓ | +| Config decides | `log.channel` alone picks the format | | +| Env decides | app.env picks the format | | + +| Option | Description | Selected | +|--------|-------------|----------| +| Compact block | ~8–12 aligned lines | ✓ | +| One line | Single summary line | | +| Full table | Every subsystem as a table | | + +**Notes:** The user asked about "magic animations from the previous Scala project". The Scala research repo only had a static ASCII sun banner; the deprecated Scala `summer-bonfire` plan was "inspired by SSU's terminal UX". The user confirmed SSU (`/media/nvme/dev/pxpx/ssu`) is the source to reuse. Banner options were withdrawn. + +| Option | Description | Selected | +|--------|-------------|----------| +| Short report | Drained requests, workers, uptime, errors | ✓ | +| Single line | "stopped after …" | | +| You decide | Planner picks | | + +--- + +## Console stack + +| Option | Description | Selected | +|--------|-------------|----------| +| Adopt charmbracelet | bubbletea/bubbles/lipgloss as in SSU | | +| Port to stdlib | Hand-written ANSI widgets | | +| lipgloss only | Styling only | | + +**User's choice:** asked for Go alternatives. Presented: charm stack (bubbletea, bubbles, lipgloss, huh, fang, log), tint, pterm, mpb, go-pretty, termenv. + +| Option | Description | Selected | +|--------|-------------|----------| +| Charm stack | lipgloss + bubbles/bubbletea + huh + fang | ✓ | +| pterm | One batteries-included dependency | | +| Researcher decides | Research compares first | | + +--- + +## Logging config shape + +| Option | Description | Selected | +|--------|-------------|----------| +| Laravel shape | `default`, `channels` map, drivers | ✓ | +| Flat keys | `log.level`, `log.format`, `log.file` | | + +| Option | Description | Selected | +|--------|-------------|----------| +| Yes, config map | `log.components` per-logger levels | ✓ | +| Global level only | | | + +| Option | Description | Selected | +|--------|-------------|----------| +| Off by default | Console only; daily channel commented out | | +| On in production | Daily JSON in production | | +| On everywhere | Always write storage/logs plus console | ✓ | + +| Option | Description | Selected | +|--------|-------------|----------| +| INFO from surf | `http` component at INFO | ✓ | +| Only at -v | DEBUG | | +| Separate access channel | Own channel | | + +--- + +## Help and list look + +| Option | Description | Selected | +|--------|-------------|----------| +| Artisan list | Version header, options, namespace groups | ✓ | +| fang default | fang root help, flat list | | +| fang + namespace groups | fang look with cobra groups | | + +**Notes:** The user asked for a preview of fang's default, which was missing from the first question; the question was re-asked with previews for all options. + +| Option | Description | Selected | +|--------|-------------|----------| +| Examples field | `Command.Examples []Example`, checked | ✓ | +| Inside Long | Text only | | + +| Option | Description | Selected | +|--------|-------------|----------| +| Yes, Hidden field | Out of `list`, `list --all` shows | ✓ | +| No | | | + +| Option | Description | Selected | +|--------|-------------|----------| +| list --format=json | Machine-readable list | ✓ | +| Not needed | | | + +| Option | Description | Selected | +|--------|-------------|----------| +| Artisan layout | Description/Usage/Arguments/Options/Examples | ✓ | +| fang layout | Uppercase sections | | + +--- + +## Sweep depth and plugins + +| Option | Description | Selected | +|--------|-------------|----------| +| Framework + all plugins | summercms.go and every sm-*-plugin with commands | ✓ | +| Framework + core only | user, journal, paymentgateway, oauthserver | | +| Framework only | | | + +| Option | Description | Selected | +|--------|-------------|----------| +| Where it applies | --force/--pretend/--json/progress where they fit | ✓ | +| Uniform everywhere | | | + +| Option | Description | Selected | +|--------|-------------|----------| +| Error + hint | Styled block with `Try:` fix command | ✓ | +| fang default errors | Message only | | + +| Option | Description | Selected | +|--------|-------------|----------| +| Fail in each repo | Coverage test fails everywhere | | +| Warn only | Logs gaps, passes | ✓ | + +**Notes:** Warn-only conflicted with roadmap criterion 3 (test fails). Resolved: fail for framework commands, warn in plugin repos; criterion 3 reworded. + +## Claude's Discretion + +- Exact palette, symbols, spacing (consistent with SSU's styles) +- Console slog handler inside diary (own lipgloss handler or charmbracelet/log), settled in the decision note +- Exact `--env` and `-n` semantics, following Artisan + +## Deferred Ideas + +None.