Prettier had never been run in --check mode here before, so this had
drifted across most files (markdown tables, long option() chains,
line wrapping). Purely formatting, no logic changes - needed so a CI
format:check gate can actually pass. Adds .prettierignore for
pnpm-lock.yaml specifically: prettier's YAML formatter rewrites every
quoted key (single -> double quotes) producing an ~8700-line diff of
pure noise on a file pnpm itself owns the formatting of.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
`run <scriptId>` auto-detects whether the web server is already
reachable (a quick /api/healthz check):
- If it is, the run goes through the existing POST
/api/scripts/:id/runs endpoint (token-authenticated, same as any
other API client) and the CLI subscribes over /ws/runs exactly like
a browser tab - so the run shows up live in Run History and any open
browser watching it, with zero server-side changes, since the
broadcast path has no idea a run was triggered by a click vs a CLI
invocation.
- If nothing's reachable, it calls startRun() directly in its own
process (after its own migrateOnBoot/reconcileOrphanedRuns, so a
from-scratch .triggershell/ works standalone) and streams output by
listening on the same in-process runEvents emitter a WS client would
otherwise be fed from - read-log-then-listen, the same ordering
ws/server.ts's subscribe() already uses, so a fast script finishing
before the listener attaches still gets its output printed.
Both modes support --var name=value (repeatable; repeat a name for
multiselect), --no-wait, and Ctrl-C cancellation through the same
mechanism the web UI's Cancel button uses (a WS cancel message
remotely, cancelRun() directly locally). `scripts list`/`scripts show`
are local-only, no network - same direct-config-read pattern as
`validate`/`doctor`.
Extracts defaultValuesForScript() out of dynamic-form.tsx into
src/lib/config/defaults.ts so the CLI's --var handling and the web
form fill in a script's configured defaults identically instead of
duplicating that logic.
Verified live end-to-end: a CLI-triggered remote run was observed
streaming to both the triggering CLI process and an independent WS
client (simulating a browser tab) simultaneously; local-mode Ctrl-C
confirmed to actually kill the spawned child process, not just the
CLI; token, wrong-token, and TRIGGERSHELL_API_TOKEN auth paths all
verified against a running auth-enabled server.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The nav's Terminal icon uses text-primary, which resolves to a
different brass shade in light vs dark mode - the favicon was
hardcoded to only the dark-mode shade. Uses prefers-color-scheme
so the favicon glyph always matches whichever primary the header
is actually showing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the default shadcn grayscale/Geist look with a deliberate
identity built around the product's own subject - a control panel for
running scripts and watching their output live:
- Type: Bricolage Grotesque for page/card titles (via the existing
--font-heading token, used with restraint), IBM Plex Sans for UI body
text, IBM Plex Mono for technical data (run IDs, commands, timestamps,
status labels) - all self-hosted at build time via next/font/google,
no runtime CDN dependency for a self-hosted tool.
- Color: a cool graphite ink/paper base with a warm brass signal accent
in both themes. The brass tone doubles as the "running" status color,
so an active run literally lights the UI up with the brand color.
New --status-* tokens give queued/running/succeeded/failed/cancelled/
warn a single source of truth instead of ad-hoc Tailwind color classes.
- Structural language: small tracked-out uppercase mono labels mark
technical fields (Command, Variables, Triggered by...) consistently;
the live-output terminal gets an instrument-bezel frame (header bar +
panel) instead of floating loose above the xterm canvas.
Layout/IA is unchanged throughout - this is a token- and detail-level
pass, not a restructuring.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Now that the CLI and the Next.js app are one package, nesting it inside
app/ served no purpose - the repo root itself becomes the published
npm package. Merges app/.gitignore and app/README.md into the root
versions, drops the now-duplicate app/LICENSE, and updates path
references (README, docs/ARCHITECTURE.md, docs/CONFIG_REFERENCE.md,
package.json's repository.directory) that assumed the app/ nesting.
Also fixes a real bug this surfaced: the in-app docs viewer resolved
docs/ relative to process.cwd(), which only worked by accident when the
CLI happened to be invoked from app/'s parent directory. A first attempt
at fixing it with import.meta.dirname broke instead, for the same
cross-module-graph reason config-path resolution already documented -
Next compiles Route Handlers through a separate module graph that
doesn't preserve source-relative import.meta paths. Fixed by exposing
the app root via TRIGGERSHELL_APP_ROOT (set once in server.ts, where
import.meta *does* resolve correctly), the same pattern already used
for TRIGGERSHELL_CONFIG_PATH.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>