Commit Graph

922 Commits

Author SHA1 Message Date
Tommy D. Rossi a746f895da release: playwriter@0.0.102 playwriter@0.0.102 2026-03-29 13:25:52 +02:00
Tommy D. Rossi 66e456e440 restore platform-specific Chrome launch instructions in skill.md
These were removed when browser start was introduced. Now that
browser start is deprecated, restore the original manual Chrome
launch commands and tab-capture flags documentation.
2026-03-29 13:23:21 +02:00
Tommy D. Rossi a55191c37a remove browser start references from skill.md
Command is deprecated and hidden from --help, so agent-facing
docs should no longer mention it.
2026-03-29 13:19:13 +02:00
Tommy D. Rossi 9253b57238 Update skill.md 2026-03-29 13:17:38 +02:00
Tommy D. Rossi f1187472d8 deprecate browser start command, hidden from --help
Update goke to 6.3.2 which adds .hidden() for commands.
browser start still works when called directly but no longer
appears in --help output.
2026-03-29 13:15:53 +02:00
Tommy D. Rossi 1588381c7d expose browser object in sandbox, document --direct profile access
- Add 'browser' to VM sandbox context so user code can call
  browser.contexts() to access pages from different Chrome profiles
- Update resetPlaywright to also refresh browser reference
- Document in skill.md: --direct gives access to all profiles via
  browser.contexts(), add browser to context variables list
2026-03-29 13:15:20 +02:00
Tommy D. Rossi b7397f877c simplify PLAYWRITER_DIRECT to only accept '1' for auto-discovery
Remove 'auto' and 'true' aliases — '1' is the standard boolean env var
pattern. Explicit ws:// endpoints still work as before.
2026-03-29 13:08:32 +02:00
Tommy D. Rossi e920c5c723 docs: update --direct references for Chrome 136+ and move to session new only
- skill.md: remove 'playwriter --direct' from MCP examples, use env var only
- MCP.md: add Direct CDP section with PLAYWRITER_DIRECT config example
- CHANGELOG.md: add 0.0.101 entry, fix stale 'playwriter --direct' in 0.0.98
- Bump version to 0.0.101
2026-03-29 13:01:01 +02:00
Tommy D. Rossi f2ee02079b fix(playwriter): support Chrome 136+ direct CDP discovery (404 on /json/version)
Chrome 136+ with chrome://inspect debugging returns 404 on all HTTP
endpoints (/json/version, /json, /json/list) but still accepts WebSocket
connections on /devtools/browser/*. The first WS connection triggers a
user-approval dialog in Chrome.

Previously, discoverChromeInstances() and resolveDirectInput() treated
HTTP 404 as 'blocked' and either returned nothing or threw an error,
making --direct unusable without manually constructing a ws:// URL.

Changes:
- discoverChromeInstances() now returns a synthetic instance for 'blocked'
  probes with a base ws:// URL, so browser list shows detected Chrome
- resolveDirectInput() returns a ws URL instead of throwing on 'blocked'
- appendSessionToWsUrl() appends session ID to bare /devtools/browser/
  paths for unique WS connections; leaves full GUID paths (from live
  probes) untouched
- Relay appends CLI session ID, MCP appends crypto.randomUUID()
- Removed --direct from root CLI command (only on session new now);
  MCP users configure via PLAYWRITER_DIRECT env var
- browser list shows connect hint when direct instances are detected

Discovery never triggers a WebSocket connection, so Chrome's approval
dialog only appears when the user explicitly runs a command.
2026-03-29 12:59:31 +02:00
Tommy D. Rossi 874fb5f060 fix(playwriter): default resizeImageForAgent format to PNG instead of JPEG
resizeImageForAgent() defaulted to JPEG when no format was specified,
causing image/png vs image/jpeg mismatch errors when MCP clients
(like Anthropic API) validated the media type against the actual image data.

All playwriter images are now consistently PNG unless explicitly
overridden with format: 'jpeg'.
2026-03-29 11:12:35 +02:00
Tommy D. Rossi 23bca69782 style(website): increase vertical padding for non-root TOC tree nodes
Non-root tree items (visualLevel > 0) now use 4px vertical padding
instead of 2px, giving nested headings more breathing room while
keeping root-level items compact.
2026-03-29 11:12:22 +02:00
Tommy D. Rossi ad134d3d71 feat(website): add FullWidth MDX component + TOC expand fix + styling tweaks
- Add FullWidth marker component that spans both content and aside grid
  columns (lg:col-[2/-1]). Extracted at parse time like Aside/Hero.
  FullWidth sections get my-5 vertical margin to match content rhythm.

- Fix auto-expand: clicking an expandable TOC item now expands it.
  Previously only ancestors were expanded, not the item itself.

- TOC tree prefix characters use 1.2em font size with negative margin
  so they don't inflate line height. Prefix indentation reduced to 1
  char per level.

- Use FullWidth for the hero screenshot in index.mdx as demo.
2026-03-28 19:19:30 +01:00
Tommy D. Rossi a896fd69cb nicer unicode tree chars 2026-03-28 18:48:57 +01:00
Tommy D. Rossi 41908cd6ad style(website): restyle TOC sidebar — chevron icons, spacing, font weight
- Add SVG ChevronIcon component for expandable items, shown on the right
- Toggle expand only on chevron click, not the whole row
- Remove width: fit-content from aside so it fills its column
- Add 6px margin between root-level TOC groups
- Use regular font weight (400) for all tree items
- Remove lowercase text-transform from tree items
- Remove line-height override from TocLink (inherits from parent)
2026-03-28 18:28:46 +01:00
Tommy D. Rossi d0802128f3 style(website): move font-size from proseStyle to slot-main so asides use toc size
Removed fontSize from the shared proseStyle object and added it to all
slot-main containers instead. Prose elements (P, OL, List) now inherit
font-size from their parent container rather than setting it explicitly,
so inside the aside they respect --type-toc-size (13px) instead of
overriding it with --type-body-size (14px).
2026-03-28 18:11:43 +01:00
Tommy D. Rossi b2adc1caaa style(website): replace aside background with 1px border and match body font size
- SectionRow aside and SidebarBanner now use a border instead of bg fill
- font size changed from --type-toc-size (13px) to --type-body-size (14px)
- line height aligned with prose style (1.6)
2026-03-28 18:07:53 +01:00
Tommy D. Rossi c183e1ba99 style: increase hero top/bottom margin on landing page
Changes Hero className from my-6 to my-16 for more breathing room around
the hero section on the website homepage.
2026-03-28 16:39:07 +01:00
Tommy D. Rossi 65922f1bbf chore: upgrade vite to v8.0.3 across all packages
Bumps vite from various versions (5.x, 6.x, 7.x) to v8.0.3 uniformly:

- root package.json: ^7.2.2 → ^8.0.3
- extension/package.json: ^5.4.21 → ^8.0.3
- website/package.json: ^7.3.0 → ^8.0.3
- playwright submodule package.json: ^6.4.1 → ^8.0.3
2026-03-28 16:39:04 +01:00
Tommy D. Rossi b835e3b907 feat(playwriter): add Kitty Graphics Protocol support to CLI + JPEG→PNG migration
When AGENT_GRAPHICS=kitty is set, the CLI now emits screenshots and resized
images as Kitty Graphics Protocol APC escape sequences to stdout. Agents with
kitty-graphics-agent (or compatible parsers) automatically extract PNG images
and pass them to the LLM as media parts — no extra tool call needed.

Key changes:

- **Screenshots → PNG**: screenshotWithAccessibilityLabels() now captures PNG
  instead of JPEG. PNG is lossless and the only format extracted by the Kitty
  Graphics Protocol (f=100). resizeImage() gains a format option ('jpeg'|'png').

- **kitty-graphics.ts**: New utility with canEmitKittyGraphics() (checks
  AGENT_GRAPHICS env var) and emitKittyImage() (writes chunked APC escape
  sequences to stdout at 4096 bytes per chunk per the Kitty spec).

- **resizeImage → resizeImageForAgent**: Renamed to clarify intent. The wrapper
  in executor.ts now collects resized images into result.images so they are
  emitted via Kitty Graphics in the CLI and included as image parts in MCP.
  Old resizeImage name kept as backward-compatible alias in the sandbox.

- **CLI deduplication**: Uses a Set<base64> to avoid emitting the same image
  twice (screenshots appear in both result.screenshots and result.images).

- **10 new tests** including round-trip validation with kitty-graphics-agent
  parser to verify emitted sequences can be parsed back correctly.

Version 0.0.98 → 0.0.99
2026-03-28 16:37:24 +01:00
Tommy D. Rossi 86773f4cfd Replace TOC search debounce with useTransition for non-blocking input
The 80ms setTimeout debounce blocked the input from reflecting keystrokes
while the TOC re-render was in progress. useTransition splits the work:
setQuery is urgent (input stays responsive), while setSearchState is
deferred and interruptible — new keystrokes cancel in-progress renders.

Input opacity drops to 0.5 during isPending as a subtle loading signal.
2026-03-28 15:09:39 +01:00
Tommy D. Rossi 21cfadce7d feat(website): refactor TOC to recursive tree with multi-page support
Replace flat TocItem/PreparedTocItem/TocGroup types with a recursive
TocTreeNode -> FlatTocItem pipeline that supports arbitrary page nesting.

**Types:**
- TocNodeType = 'page' | 'h1'-'h6' — heading level encoded in type field,
  no separate headingDepth. Smaller objects, self-descriptive.
- TocTreeNode = { label, href, type, children } — recursive tree, the wire format.
- FlatTocItem = { label, href, type, visualLevel, prefix, parentHref, pageHref }
  — flat rendering item with depth clamped to 0-3.

**Depth clamping:**
flattenTocTree() walks the recursive tree and clamps visual depth to max 4
levels (0-3). When pages nest deeply, headings flatten to siblings at level 3.
Formula: visualLevel = min(pageDepth + headingDepth, 3). Preserves correct
visual gap for skipped markdown levels (## -> ####) via the h-type encoding.

**Component changes:**
- TableOfContents accepts FlatTocItem[], renders with visibility filtering
  instead of two-tier TocGroup. Expand/collapse works at any depth by
  tracking expanded hrefs and computing visibility from parentHref chain.
- Search expand override merged into visibility computation so matched items
  inside collapsed branches become visible during search.
- search.ts operates directly on FlatTocItem[] — removed SearchEntry and
  buildSearchEntries(). Walks parentHref chain for ancestor expansion.

**Extraction:**
slugify, extractText, generateTocTree moved to toc-tree.ts (side-effect-free)
so they can be tested without importing the page module.

**Tests:**
21 vitest tests covering slugify, extractText, tree building (nested, skipped
levels, pop-back), flattening (prefixes, depth clamping, page wrapping,
parentHref/pageHref tracking), and full mdast -> tree -> flat pipeline.

**Remaining for multi-page:**
- Multi-page data source (multiple MDX files / manifest)
- Route-aware active tracking (pathname for page, IntersectionObserver for
  headings on current page only)
- Cross-page navigation (SPA or full page load)
2026-03-28 15:03:05 +01:00
Tommy D. Rossi 234b36dae3 feat(website): add Hero MDX component with unified layout gap
Add <Hero> component that gets auto-extracted from MDX at parse time
(same pattern as <Aside>) and rendered above the 3-column grid, aligned
with the center content column.

Layout changes:
- slot-page uses flex column with gap-[--layout-gap] for uniform spacing
  between navbar, hero, and content grid
- --layout-gap (21px) is the single source of truth for both the flex gap
  (flow spacing) and --sticky-top offset (viewport-relative sticky offset)
- Remove --content-top-gap variable and all its usage (spacer divs,
  sidebar padding-top) — the flex gap handles all vertical spacing now
- Hero accepts arbitrary div props for extensibility

Hero extraction pipeline:
- isHeroNode() detects <Hero> mdxJsxFlowElement nodes in the mdast
- Hero nodes are filtered out before TOC generation and section grouping
- Rendered via RenderNodes through SafeMdxRenderer with full component map
- EditorialPage receives hero as a prop, renders it in a matching grid
  (same column widths) so content aligns with the center column
2026-03-28 13:45:40 +01:00
Tommy D. Rossi 64bcd428ed replace index mdx. add hero 2026-03-28 13:43:08 +01:00
Tommy D. Rossi 00242bb71f style: make TOC sidebar scrollable, adjust search input spacing
- Sticky sidebar container gets maxHeight + flex column so TOC scrolls
  when it exceeds viewport height
- Search input stays pinned at top (flexShrink: 0), nav scrolls below
- Nav gets paddingRight 4px for scrollbar gap
- Search input padding-y reduced from 4px to 2px
- Search-to-tree gap set to 12px
2026-03-28 12:55:24 +01:00
Tommy D. Rossi 1ed2077d13 feat: add TOC search with Orama full-text search
Adds a search input above the left sidebar table of contents powered by
Orama (in-memory, typo-tolerant, ~20μs per query).

**search.ts** — flat SearchEntry[] data model with parent/group references,
Orama DB creation, searchToc() that returns matched/dimmed/focusable hrefs
and group expand overrides. Reference: mintlify.com/oramasearch/orama/quickstart.md

**markdown.tsx** changes:
- TocLink: new props dimmed (opacity 0.3 + tabindex=-1), isHighlighted
  (code-bg background), linkRef (scroll-into-view)
- TableOfContents: search input with F hotkey badge, 80ms debounced Orama
  search, arrow key navigation through matched results, Enter navigates
  to section anchor, Escape clears and blurs
- Groups containing matches auto-expand, non-matching items dimmed at 0.3
- IntersectionObserver scroll lock via ref to prevent activeId bouncing
  during programmatic smooth scroll (scrollend + safety timeout)
- Global F key listener focuses search (skips input/textarea/contenteditable)
2026-03-28 12:44:37 +01:00
Tommy D. Rossi 9d03a2d4dd fix: add raw import plugin workaround for Vite 7 + @vitejs/plugin-rsc
The built-in vite:asset load hook uses a regex that doesn't match when
query params get normalized to ?raw= (URLSearchParams.toString() adds
the =). This pre-load plugin handles ?raw imports for non-JS files
explicitly.
2026-03-28 12:44:24 +01:00
Tommy D. Rossi a00c466382 Update package.json 2026-03-28 12:16:24 +01:00
Tommy D. Rossi 0c98e16867 Update pnpm-lock.yaml 2026-03-28 12:16:18 +01:00
Tommy D. Rossi 967ddce431 Update .gitignore 2026-03-28 12:16:13 +01:00
Tommy D. Rossi d33fbf37f9 stabilize useSyncExternalStore callbacks for TOC active heading
- Wrap subscribe, getSnapshot, getServerSnapshot in useCallback to prevent
  React from tearing down and recreating the IntersectionObserver on every render
- Move window.location.hash read from render into subscribe, keeping render pure
- Add emit() helper that deduplicates ref writes and only notifies React on change
- Initialize activeRef with fallbackId; hash is read on first subscribe
2026-03-28 12:15:45 +01:00
Tommy D. Rossi 9869975290 use useSyncExternalStore for TOC active heading to avoid hydration mismatch
Replaces useState + useEffect with a single useSyncExternalStore that:
- Initializes activeRef from window.location.hash to fix flash on first paint
- Uses IntersectionObserver in subscribe to track scroll position
- Returns fallbackId as server snapshot to prevent hydration errors

One less state hook (no useState, no useEffect for the observer).
2026-03-28 12:07:37 +01:00
Tommy D. Rossi 08cdea91d4 simplify TOC initial hash: read in useState initializer instead of useSyncExternalStore
No-op subscribe meant useSyncExternalStore was effectively a constant.
Replaced with a direct window.location.hash read in the useState
initializer, guarded by typeof window for SSR. One less hook.
2026-03-28 11:57:39 +01:00
Tommy D. Rossi a095b52186 fix TOC flash on initial paint by reading URL hash with useSyncExternalStore
Previously the TOC always initialized activeId to the first item's href,
causing a visible flash when the page loaded with a hash (e.g. #getting-started)
until the IntersectionObserver corrected it.

Now useLocationHash() reads location.hash synchronously during hydration via
useSyncExternalStore, so the first render already highlights the correct section.
The hook also subscribes to hashchange events for anchor link clicks.
2026-03-28 11:53:48 +01:00
Tommy D. Rossi 047b47976a update spiceflow to 1.18.0-rsc.18 in website 2026-03-28 10:11:51 +01:00
Tommy D. Rossi e805de49d9 allow all packages to run build scripts
Use onlyBuiltDependencies: ["*"] wildcard instead of listing individual packages.
2026-03-28 10:06:48 +01:00
Tommy D. Rossi 0557279947 fix CI: approve build scripts and update pnpm to v10
- Add pnpm.onlyBuiltDependencies for esbuild/sharp (need native builds)
- Add pnpm.ignoredBuiltDependencies for bufferutil/electron (skip builds)
- Update CI workflow to pnpm 10 to match local version
- Remove .npmrc (strict-builds setting wasn't picked up in CI)
2026-03-28 10:04:55 +01:00
Tommy D. Rossi 834954e61a add .npmrc with strict-builds=false to fix CI pnpm install
pnpm v10 defaults strict-builds to true, which errors on packages with
unapproved build scripts (esbuild, sharp, electron, etc). Disabling it
allows CI installs to succeed without maintaining an approval list.
2026-03-28 10:01:40 +01:00
Tommy D. Rossi 11debee899 Merge branch 'main' of https://github.com/remorses/playwriter 2026-03-28 10:00:09 +01:00
Tommy D. Rossi 1dd91074d8 update spiceflow to 1.18.0 in website
Moves from prerelease 1.18.0-rsc.16 to stable 1.18.0.
2026-03-28 10:00:02 +01:00
Tommy D. Rossi 2d0f01c1c6 Merge pull request #79 from remorses/website/health-route
feat(website): add /api/health route
2026-03-27 18:04:19 +01:00
Tommy D. Rossi 714681e29f feat(website): add /api/health route 2026-03-27 16:06:21 +01:00
Tommy D. Rossi aceada75e5 feat(website): add /api/health route 2026-03-27 16:02:17 +01:00
Tommy D. Rossi 9af952dbf4 Merge pull request #78 from remorses/website/spiceflow-migration
Migrate website from React Router to Spiceflow
2026-03-27 15:56:03 +01:00
Tommy D. Rossi 464c25109e refactor(website): migrate from React Router to Spiceflow
Replace React Router 7 + Vercel adapter with Spiceflow RSC framework.
The landing page is now a statically prerendered page via .staticPage('/').

Deleted 10 boilerplate files (~450 lines):
- entry.server.tsx (stream handling, bot detection)
- entry.client.tsx (hydration)
- root.tsx (HTML shell, error boundary)
- routes.ts, react-router.config.ts
- routes/_index.tsx, routes/github.tsx, routes/defer-example.tsx
- lib/errors.ts (Sentry), lib/utils.ts (cn, sleep)
- components.json (shadcn config)

Removed 18 dependencies: @react-router/* (4), @vercel/react-router,
react-router, isbot, @vitejs/plugin-react, vite-plugin-environment,
@xmorse/deployment-utils, sentries, @sentry/node, clsx, tailwind-merge,
lucide-react, @radix-ui/react-slot, class-variance-authority, tw-animate-css

Created 2 files:
- src/main.tsx: Spiceflow entry with layout, static page, redirect
- src/pages/index.tsx: editorial page (moved from routes/_index.tsx)

markdown.tsx and pages/index.tsx marked 'use client' since they use
React hooks and DOM event handlers (required by RSC boundary).
2026-03-27 15:55:42 +01:00
Tommy D. Rossi fc3bc77334 refactor(website): replace 5-column gap-slot grid with 3-column + CSS gap-x
The page layout grid previously used 5 explicit columns to embed spacing:

  grid-cols: [210px] [50px] [520px] [50px] [210px]
               TOC    gap   content  gap   sidebar
               col1   col2   col3   col4    col5

The two gap columns (2 and 4) were real grid tracks, not CSS gap. This meant:
- Column references were off-by-two (sidebar = col 5, main = col 3)
- SectionRow subgrid spans referenced col-[3/-1] to skip the gap column
- slot-main had overflow:hidden to prevent content from bleeding into the
  adjacent gap column — this later caused visible clipping of content

New layout uses 3 columns + CSS gap-x:

  grid-cols: [210px] [520px] [210px]   gap-x: 50px (--grid-gap)
               TOC   content  sidebar
               col1    col2    col3

Changes:
- markdown.tsx: grid template from 5 cols to 3 cols + lg:gap-x-(--grid-gap)
  - SectionRow: col-[3/-1] → col-[2/-1], aside col-[3] → col-[2]
  - inline subgrid (section-based layout): col-[3/-1] → col-[2/-1]
  - flat layout slot-main: col-[3] → col-[2]
- globals.css: slot-sidebar-right grid-column 5 → 3
- globals.css: remove overflow:hidden from slot-main — no longer needed
  since there is no adjacent gap column for content to bleed into
- AGENTS.md: update layout diagram to reflect the 3-column structure
  and note that spacing comes from CSS gap-x, not a slot

--grid-max-width calc and --grid-gap variable are unchanged; the total
computed width (1040px) is identical.
2026-03-27 15:30:58 +01:00
Tommy D. Rossi ceef6d7154 feat(cli,mcp): wire up resolveDirectInput + unify browser list
cli.ts — session new --direct:
  Previously, a --direct string value was passed verbatim as the CDP endpoint,
  which silently broke when the user passed 'localhost:9222' instead of a full
  ws:// URL. Now resolveDirectInput() is called first: it normalises host:port
  to a WebSocket URL via /json/version probe, passes ws:// URLs through as-is,
  and surfaces a clear error for blocked or dead endpoints before any session
  is created.

cli.ts — browser list command:
  The old implementation only listed raw CDP instances from discoverChromeInstances
  (DevToolsActivePort / port scan). Rewritten to show both:
    - Extension-connected browsers (fetched from relay / waitForConnectedExtensions)
    - Direct CDP instances on port 9222 (from discoverChromeInstances)
  The relay server is started if needed so extensions can connect before the
  2-second poll window. Output uses the shared printBrowserTable() formatter.
  TYPE column now shows '--direct' instead of 'direct' to hint at the flag name.
  Empty state message updated with actionable instructions for each connection type.

mcp.ts — getDirectCdpConfig:
  PLAYWRITER_DIRECT='auto'/'1'/'true' → auto-discover on port 9222 (unchanged behaviour).
  PLAYWRITER_DIRECT='ws://...' or 'host:port' → now routed through resolveDirectInput
  instead of an ad-hoc string check, which also covers the new host:port format.
2026-03-27 15:30:45 +01:00
Tommy D. Rossi 05071af0ba refactor(chrome-discovery): simplify to single port 9222 probe + add resolveDirectInput
The previous implementation used two layered strategies:
1. Glob for DevToolsActivePort files across `~/Library/Application Support`,
   `~/.config`, `%LOCALAPPDATA%` — walks the directory tree up to depth 3,
   reads the file to get port + WS path, then reads `Local State` for profiles.
2. Port scan 9222-9229 via GET /json/version — fills in any instances not found
   by file scan, or verifies/updates live wsUrl for those that were.

This caused real-world issues:
- Stale DevToolsActivePort files left by previous Chrome sessions returned dead
  instances that then failed at connection time.
- Reading `Local State` JSON and walking the directory tree is slow and fragile
  on systems where the browser data dir is not in a standard location.
- The deduplication + merge logic between the two strategies was complex and
  hard to reason about.
- Chrome 136+ on macOS does not expose /json/version on the default profile by
  default, so the HTTP probe already returned null for many users.

New approach:
- `discoverChromeInstances()` probes only port 9222 via GET /json/version.
  Returns one DiscoveredInstance if Chrome responds with webSocketDebuggerUrl,
  otherwise returns []. No file I/O, no port range scan, no profile reading.
- `resolveDirectInput(input)` is a new export that normalises a --direct value
  into a WebSocket URL:
    - ws:// or wss:// → returned as-is
    - host:port        → probed via /json/version; wsUrl returned if live,
                         'blocked' (HTTP 200 but no CDP) gives actionable error,
                         'dead' (ECONNREFUSED) gives clear "nothing listening" message
- Internal `probePortStatus()` replaces `probePort()` with a richer union type
  {type: 'live'|'blocked'|'dead'} so callers can distinguish between "Chrome is
  there but inaccessible" vs "nothing running at all".
- `probePort()` is kept as a thin public wrapper (live → wsUrl+browser, else null)
  for backward compatibility with external callers.
- Removed exports: getChromeProfiles, deriveBrowserName, getDataDirParents,
  findDevToolsActivePortFiles (and internal scanForActivePort).
- Tests updated: remove describe blocks for the deleted functions
  (deriveBrowserName, getDataDirParents, getChromeProfiles), keep
  parseDevToolsActivePort tests since that helper is still used.
2026-03-27 15:30:33 +01:00
Tommy D. Rossi 34ed2f4c50 refactor(cli): replace @xmorse/cac with goke
- swap cac import for goke, add zod import
- remove manual type annotations from action callbacks (goke infers from schema)
- typed options with z.number().default(10000) for --timeout and z.string().default('0.0.0.0') for serve --host; plain string descriptions everywhere else
- removed @xmorse/cac from dependencies
2026-03-27 13:16:12 +01:00
Tommy D. Rossi 3c3fed300a refactor(website): switch editorial layout to slot classes 2026-03-27 13:14:14 +01:00
Tommy D. Rossi 98ca9f4a5f Update index.md 2026-03-27 13:13:09 +01:00