SlowGuardian V10 — ground-up LTS recode (Scramjet-only, Astro frontend)#19
Open
zgr2575 wants to merge 31 commits into
Open
SlowGuardian V10 — ground-up LTS recode (Scramjet-only, Astro frontend)#19zgr2575 wants to merge 31 commits into
zgr2575 wants to merge 31 commits into
Conversation
Adds V10_RECODE_PLAN.md — a stability-first, ground-up recode plan derived from a full V9 code audit and research into modern web-unblocker architecture, stealth/cloaking, frontend, and backend/LTS practices. Core decisions proposed: - Collapse 5 proxy engines to one (Scramjet) over a single Wisp transport - One service worker, one auth path; cut Spotify/KeyAuth/AdSense/Replit DB - Astro MPA frontend; Express backend kept; better-sqlite3/memory storage - Real test pyramid incl. Playwright proxy smoke test; LTS branch + release-please - Honest stealth: default tab-cloak + fixed panic key, opt-in about:blank/blob Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Revise the deploy plan to keep Vercel support (per maintainer): static Astro frontend on Vercel, persistent Wisp proxy backend on Render/Railway/ Fly/self-host. Adds section 6.1 documenting the split-deploy topology and why running Wisp as Vercel serverless functions is not viable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
- DESIGN_V10.md: judged design-system spec (NEON DECK arcade-console direction chosen for surviving the Chromebook perf budget; absorbs command palette + film-grain from the runner-up directions). Full token set, theming architecture, component inventory, motion set, performance-mode strategy, and CI-enforced budgets. - docs/design/v10-neon-deck-mockup.html: self-contained clickable mockup — live theme (Arcade/Midnight/Carbon/Paper) + accent switching, catalog search/filter over real titles, command palette, tab-cloak and panic demos. All effects static CSS or compositor-only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Scrap the arcade/neon mockup per maintainer direction. New UI is Apple Liquid Glass, dark-mode-first: translucent frosted material, specular edges, ambient light field, floating panels, one restrained accent. Real backdrop-filter on capable devices with a solid-glass fallback (@supports + data-perf) so old Chromebooks stay smooth. Light Liquid Glass available via toggle. Includes live accent switching, catalog search, and a Cmd-K command palette. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Start the ground-up recode under v10/ (alongside V9 until cutover so main stays shippable). Adds the design-token stylesheet extracted from the approved Liquid Glass direction, and a BUILD_STATUS tracker with the locked decisions and phase checklist. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Replace the flat filter-grid (felt cluttered) with a featured billboard hero + side-scrolling category rows of landscape tiles (title overlaid on the art, scroll-snap, bleeds off the edge). Search collapses rows into a results grid. Fixes a .row class collision with the command palette that broke row-header layout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Maximize screen usage per feedback: widen container to 1440, cut excess vertical padding, shrink the featured billboard, and densify library tiles (6/row on a 1366 Chromebook, 2-up on phone with the next peeking). Drop the redundant monogram from tiles (collided with the title on short tiles); title now sits on a bottom bar with ellipsis for long names. Add scroll-margin so anchored sections clear the sticky nav. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Ditch the generic nav bar for an iOS Dynamic Island: a compact centered glass pill (brand + live status) that morphs open on hover/tap to reveal the links, search, and theme toggle (springy width transition, ambient status collapses as controls expand). Merge the two search boxes into one — the island search / Cmd-K palette, which now also searches game titles. Tuned to fit on phones. Removes the duplicate library search. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Each nav tab gets an icon (Games=controller, Apps=grid, Proxy=globe, Privacy=lock); active tab icon tints with the accent. The island now collapses label text away below 900px so the pill reduces to icon-only on small laptops and phones, keeping it compact on small screens. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Per maintainer: remove Replit (nix + dead Replit DB dep); keep Vercel (split-deploy, already set); keep the about:blank cloak (opt-in, gesture- triggered, never auto-on) and the multi-tab in-app browser in 1.0 scope. Update BUILD_STATUS locked decisions + phase checklist accordingly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Per maintainer: ship a single engine (Scramjet) over Wisp for maximum LTS stability and bisectable bug reports. Ultraviolet stays a documented post-1.0 escape hatch, added only if the nightly live-site test proves a real compatibility gap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Resolved with maintainer: - Accounts: public proxy + single admin gate (no user sign-ups) - Admin auth: pluggable adapter; will use forthcoming ZADMIN Auth system, env-password placeholder for 1.0 - Storage: better-sqlite3 (no MongoDB) - Deploy backend: Render (frontend stays on Vercel) - Music/Spotify slot: reserved for a future project integration (TBD) - Mirror/reachability: post-1.0 - LTS window: 6 months active + 6 security-only - Git history: leave intact, stop tracking big files going forward All Section 9 decisions now closed; BUILD_STATUS is the source of truth. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Per maintainer: do not do any ZADMIN work yet (not even an adapter). 1.0 ships a simple env-password admin gate; ZADMIN is a separate future project to be picked up only on the maintainer's go-ahead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
The working single-engine proxy pipeline: - Express serves the app + scramjet/bare-mux/epoxy browser bundles straight from node_modules (no guessed paths); raw node:http owns the 'upgrade' event so wisp-js routes /wisp/ tunnels, non-wisp upgrades dropped. - COOP same-origin + COEP require-corp on every response (cross-origin isolation for Scramjet WASM + epoxy). - One service worker (public/sw.js) via $scramjetLoadWorker. - Client (public/boot.js) registers the SW + sets the epoxy transport at /wisp/ eagerly on load, then navigates via the Scramjet controller. - envalid fail-fast config, pino logging, crash-only handlers that LOG (no process.exit mid-request), graceful shutdown. Pinned co-released set: scramjet 1.1.0, wisp-js 0.4.1, bare-mux 2.1.9, epoxy-transport 2.1.28 (2.x keeps the node epoxyPath helper). Verified locally: /healthz, COOP/COEP headers, all bundles 200, wisp handshake (101 / dropped), and a live HTTP site rendered fully-styled inside the Scramjet frame end-to-end. HTTPS blocked only by this dev sandbox's TLS MITM (epoxy correctly rejects the private CA) — confirm on first Render deploy. Includes zero-dep test/wisp-handshake.mjs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
…y UI Ports the approved design to an Astro 5 static MPA under v10/web/, served by the Phase 1 Express server (now serves web/dist/, falling back to public/ when unbuilt): - Liquid Glass, dark-first, with the Dynamic Island nav (per-tab icons, icon-only collapse), theme/accent/perf controls, command palette. - Netflix-style library over the REAL catalog: content collections (Astro 5 file() loader + Zod) built from V9's 284 games / 55 apps, featured billboard + side-scrolling shelves, real icons. - Proxy-integrated /go page: Liquid Glass multi-tab browser (tabs, address bar, always-visible Exit/quick-exit) reusing the verified Phase 1 Scramjet/Wisp wiring. - Settings/privacy pages with honest 'quiet on a shared screen' copy. Verified: astro build green (5 pages); home/apps/settings render; the new multi-tab go page proxied a live site (httpforever.com) end-to-end. Icons synced from committed static/ at build (prebuild) — not duplicated in git; dist/.astro/node_modules gitignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
…e.html) The showcase is a self-contained single-file build of the V10 UI with real catalog icons embedded as data URIs (~1.4MB) for the clickable Artifact. It's reproducible from the mockup + catalog, so keep it out of git rather than committing the heavy generated file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
The Dynamic Island nav now switches between distinct pages — Games, Apps, Proxy, Privacy, Settings — instead of one long scroll, matching the real Astro MPA. Adds: a client-side router (showView) wired to the island links + brand + omnibox + palette; an Apps page (its own catalog + shelves); a Proxy page with a multi-tab browser mock (tab strip, address bar, always-there Exit); tiles now support real icon images (falling back to gradient). Settings gets its own nav entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
- Home is now a clean, search-first page (centered logo + omnibox + Games/Apps/Surprise-me quick links), not a marketing lander; it's the default view. The Netflix library moves to its own Games page. - Proxy page rebuilt as a real browser: dropped the marketing heading, the tab strip + address bar + frame now fill the viewport edge-to-edge (no wasted space). - Content views get top padding so they clear the floating island. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Every view now fills the viewport with the footer anchored to the bottom instead of floating up mid-screen. Content-rich views (games/apps/settings) sit below the Dynamic Island and scroll; sparse views (home/privacy) center their content in a sticky-footer flex column. Settings was the emptiest screen — replaced the lone appearance bar with a three-card grid (Performance toggles, Privacy & exit, About/version) so it reads as a genuine settings page and has no dead space. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
… space Proxy view now reads like Google Chrome: real tab strip (rounded connected tabs, hover states, close affordances), a Chrome toolbar with circular nav buttons, a pill omnibox with a lock glyph, and a bookmark / menu / profile-avatar cluster. The empty content frame is replaced by a Chrome-style new-tab page — wordmark, search pill, and a grid of shortcut tiles — so the workspace fills the viewport instead of showing a lone centered note. Apps now mirrors Games: a featured billboard sits above the Netflix-style shelves so the page opens with the same weight as the game library. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Adds a packed, edge-to-edge grid of the entire catalog below the Netflix shelves on both library views (auto-fill columns, 6 on a 1280 screen / 9 on 1920), and widens the library column to 1780px so wide monitors aren't half-empty. Search hides the games grid so results stay clean. Work in progress under review — not final. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Home is no longer a lone centered search box: below the search hero it now carries real content shelves (Jump back in / Popular games / Top apps) that fill the page top to bottom. Proxy new-tab page top-aligns like real Chrome and gains a "Recently visited" card row, so the workspace fills instead of centering a small block in an empty frame. With the earlier dense All grids on games/apps and the three-card settings page, no view leaves a large empty band. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Revert the home content shelves — the Google-style centered lander (logo + search + quicklinks) was the intended design. Games/apps keep their dense grids and the proxy keeps its filled new-tab page; only home goes back to the minimal search-first landing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Bring the real app in line with the approved mockup (docs/design): - Home "/" is now the Google-style search lander (logo + omnibox + quick links); the games library moved to /games. Nav + ⌘K routes updated. - Games/Apps gain a dense full-width "All" grid below the Netflix shelves so wide screens fill edge to edge; dropped the in-library search field (search is unified into the ⌘K palette). Library column widened to 1780px. - /go proxy reskinned to look like Google Chrome — Chrome tab strip, pill omnibox with lock/globe, menu + profile avatar, and a Chrome-style new-tab page with shortcut tiles. The verified multi-tab Scramjet wiring (IDs, JS) is untouched; this is purely the skin + the full-height shell. - /settings rebuilt as the three-card grid (Performance / Privacy & exit / About); performance mode, tab disguise, and clear-traces stay wired. - Sticky-footer full-height layout across every page. Build green (6 routes); every page verified against src/server.js with no console errors and the proxy engine reaching "ready". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Complete the opt-in about:blank cloak on /privacy as a clean port of V8's proven createAboutBlank (static/assets/scripts/h.js), minus the V9 auto-run bug. The prior Phase-2 launcher only opened a blank window; this restores the three parts of the working V8 flow that make it an actual cloak: - redirect the ORIGIN tab to the decoy (sg:panicUrl) after launch, so nothing left on screen points back at SlowGuardian; - a beforeunload guard in the blank document so an accidental close asks first; - a Firefox guard (Firefox blocks scripting an about:blank popup) with an honest fallback message. Still gesture-triggered and opt-in (sg:aboutblank); the blank window loads SlowGuardian in a full-page iframe wearing the sg:cloak title/favicon. Copy updated to state plainly that this tab switches to the decoy and that it does not bypass network filtering or device monitoring. Verified end-to-end in headless Chromium: popup opens with the disguise title, iframe src = origin, beforeunload set, origin tab redirects to the decoy, no console errors. Mirror rotation stays post-1.0 per the locked decisions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Health & readiness:
- /healthz now returns {ok, version, uptime}; new /readyz returns 200/503
with a per-asset check list (proxy bundles + built frontend must actually
resolve). Load balancers gate on /readyz.
Proxy smoke test (Playwright, test/e2e.spec.mjs):
- Boots the real server (webServer waits on /readyz), asserts the lander and
games grid render, health + COOP/COEP headers, and — the key case — proxies
a LOCAL fixture end-to-end (SW → bare-mux → epoxy → wisp TCP → Scramjet),
so it never depends on live internet. 5/5 pass in headless Chromium.
- wisp-js blocks loopback/private IPs by default (SSRF hygiene); enabled
allow_loopback_ips ONLY under NODE_ENV=test so the fixture is reachable —
dev/prod stay locked (allow_private_ips stays false everywhere).
CI (.github/workflows/v10-ci.yml, scoped to v10/**, independent of the legacy
test-sw.yml): build frontend → install Chromium → wisp handshake → Playwright
e2e; plus a parallel docker-build job that builds the image (no push) so the
Dockerfile is validated on every change.
Docker (v10/Dockerfile): multi-stage — build Astro frontend, resolve prod-only
server deps (drops the test browser), slim runtime with a /readyz HEALTHCHECK
running as the node user. Builds from the repo root (the web build sources the
catalog icons from repo-root static/); .dockerignore added at the root.
Tooling: swap the unused puppeteer-core devDep for @playwright/test; add
build:web / test:wisp / test:e2e scripts; version 10.0.0-phase1 → 10.0.0-rc.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
Audited V9 against V10 and closed the real gaps (clean rebuilds, no bloat): - Onboarding: a one-time first-run modal (web/src/components/Onboarding.astro, rendered from Base) — welcome → appearance (theme+accent, applied live) → privacy (tab disguise / about:blank opt-in) → done; guard sg:onboarded. Uses the shared window.SG API so choices persist. Playwright-tested. - Search-engine choice: Settings picker (Google/Bing/DuckDuckGo/Startpage/ Brave/Ecosia/custom) → sg:engine template; both the lander and the proxy omniboxes honor it instead of hardcoding Google. - Tab-cloak presets expanded 4 → 12 (adds Slides, Gmail, Meet, Canvas, Schoology, Khan, Wikipedia, PowerSchool) alongside custom title/favicon. - Proxy utilities: Fullscreen + Open-in-new-window (Ctrl+T/W/L intentionally skipped — they collide with the host browser's own bindings). README rewritten with a transparent carried-over / intentionally-dropped / deferred map so nothing is silently lost. Legacy code preserved on the legacy/v8 and legacy/v9 branches. Build green (6 routes); Playwright 6/6 (adds a first-run onboarding test). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
https://slowguardian-v10.onrender.com is up and working: /healthz + /readyz all green, lander + onboarding render, and the proxy loaded a live HTTPS site (example.com) end-to-end through Wisp/Scramjet on a real network — closing the Phase 1 "re-confirm HTTPS on a real egress" caveat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
- Settings: drop the marketing headings/jargon; present a plain labeled list (Appearance/Accent/Performance/Search/Tab disguise/Privacy/Clear/ Version) closer to V8/V9 — no "straight talk" copy, no section headers. - Remove the jargon footer globally (Base.astro). - Proxy (/go): make the in-app browser fullscreen — full-bleed edge to edge, fills the viewport top to bottom (thin gap for the floating island), no footer, no rounded box. Verified: shell spans full width, footer gone. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
…-style settings - Dynamic Island now docks to the top-left and shows icons only until hovered/focused/tapped, then morphs open to reveal labels (icon-only on phones). - Launching a game/app from the library opens the proxy in an immersive mode (?app=1): the tab strip, toolbar, status bar, and global nav island are all stripped, leaving a full-viewport view of just the content with a small floating control cluster (reload / fullscreen / quick-exit / close). - Settings rewritten as a plain card grid resembling V8/V9: one clear title + one-line description + control per card, no marketing copy or page heading. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
The previous change rewrote the shared Dynamic Island for every page. Restore the original centered island everywhere and move the left-docked, shield- collapsed (expands on hover/focus/tap) variant behind a .sg-proxy class that only go.astro sets — set synchronously in <head> so the pill never flashes centered first. Every non-proxy page keeps the original centered pill. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BzgycoxZ3LNqvFLv8r4SPg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ground-up, LTS-focused rebuild of SlowGuardian, developed under
v10/so this PR is purely additive — V9 at the repo root is untouched. The V8 and V9 code is preserved on thelegacy/v8(a91599b, Version8.9a) andlegacy/v9(currentmain) branches before any future cutover.Version
10.0.0-rc.1. This lands the app + tests + CI + Docker; the 1.0 flip, GHCR publish, and the actual root cutover are deliberately left as a follow-up.What's inside
wisp-js), a single service worker,bare-mux+epoxy-transport, COOP/COEP cross-origin isolation. Express on a rawnode:httpserver, crash-only, envalid config, pino logging. Bundles are served straight fromnode_modulesso the bytes always match the installed version./go) — Chrome-style multi-tab in-app browser (tab strip, pill omnibox, fullscreen, open-in-new-window, always-there Quick Exit, Chrome-style new-tab page).createAboutBlank, minus the V9 auto-run bug), and clear-traces.Phases (all verified)
/healthz+/readyz, Playwright smoke test, CI, Docker.Testing / CI / Docker
npm run test:e2e— Playwright, 6/6: lander, library, health, COOP/COEP, first-run onboarding, and a full proxy round-trip against a local fixture (SW → bare-mux → epoxy → wisp → Scramjet), so it never depends on live internet.npm run test:wisp— zero-dep/wisp/upgrade handshake..github/workflows/v10-ci.yml— build frontend → Chromium → wisp handshake → Playwright e2e, plus a parallel docker-build job. Scoped tov10/**, independent of the legacytest-sw.yml.v10/Dockerfile— multi-stage, non-root,/readyzHEALTHCHECK. Build from the repo root:docker build -f v10/Dockerfile -t slowguardian:10 .Intentionally not carried over from V9
Full transparency map in
v10/README.md. In short (all previously agreed): one proxy engine only (dropped UV / Dynamic / Rammerhead / failover), no accounts / premium / ads / KeyAuth (public proxy + single env-password admin gate; ZADMIN is a separate future project), no MongoDB (embedded SQLite), no monitoring-evasion (privacy defends against a shoulder-glance, not network/device monitoring — and the UI says so), music/Spotify reserved for a future integration, and the theme-zoo / particles / plugins / movable-buttons / 100-feature-manager / home widgets superseded by the approved Liquid-Glass design.Deploy
Split: static frontend on Vercel, persistent Wisp backend on Render; one Docker image is the canonical artifact. Re-confirm HTTPS proxying on the first real deploy (a TLS-intercepting dev egress correctly fails epoxy's cert validation).
🤖 Generated with Claude Code
Generated by Claude Code