bootstrap
Adom Hydrogen (platform base)
Public Made by Adomby adom
Layer Hydrogen's workspace onto a standard Adom workstation.
← Commit history
Delete skills
106 files changed
−7122
skills/._hd-adom-authskills/._hd-adom-desktopskills/._hd-adom-menuskills/._hd-apiskills/._hd-bridgesskills/._hd-captionsskills/._hd-capture-shareskills/._hd-claude-authskills/._hd-containerskills/._hd-container-statsskills/._hd-desktop-sseskills/._hd-eda-discoveryskills/._hd-evalskills/._hd-instapcbskills/._hd-monitorskills/._hd-notificationsskills/._hd-open-urlskills/._hd-overviewskills/._hd-permissionsskills/._hd-profile-menuskills/._hd-self-screenshotskills/._hd-self-updateskills/._hd-settingsskills/._hd-setupskills/._hd-setup-stepsskills/._hd-tab-iconsskills/._hd-uiskills/._hd-welcomeskills/._hd-who-am-iskills/._hd-workspace-monitoringskills/._hd-workspace-updaterskills/hd-adom-auth/._SKILL.mdskills/hd-adom-auth/SKILL.md−326skills/hd-adom-desktop/._SKILL.mdskills/hd-adom-desktop/SKILL.md−235skills/hd-adom-menu/._SKILL.mdskills/hd-adom-menu/SKILL.md−90skills/hd-api/._SKILL.mdskills/hd-api/SKILL.md−354skills/hd-bridges/._SKILL.mdskills/hd-bridges/SKILL.md−390skills/hd-browser-picker/SKILL.md−474skills/hd-captions/._SKILL.mdskills/hd-captions/SKILL.md−198skills/hd-capture-share/._SKILL.mdskills/hd-capture-share/SKILL.md−148skills/hd-claude-auth/._SKILL.mdskills/hd-claude-auth/SKILL.md−86skills/hd-claude-management/SKILL.md−214skills/hd-container-stats/._SKILL.mdskills/hd-container-stats/SKILL.md−134skills/hd-container/._SKILL.mdskills/hd-container/SKILL.md−131skills/hd-desktop-sse/._SKILL.mdskills/hd-desktop-sse/SKILL.md−242skills/hd-eda-discovery/._SKILL.mdskills/hd-eda-discovery/SKILL.md−102skills/hd-embedded-ad/SKILL.md−141skills/hd-eval/._SKILL.mdskills/hd-eval/SKILL.md−159skills/hd-file-transfer/SKILL.md−92skills/hd-golden-image/SKILL.md−128skills/hd-instapcb/._SKILL.mdskills/hd-instapcb/SKILL.md−77skills/hd-monitor/._SKILL.mdskills/hd-monitor/SKILL.md−183skills/hd-notifications/._SKILL.mdskills/hd-notifications/SKILL.md−126skills/hd-open-url/._SKILL.mdskills/hd-open-url/SKILL.md−210skills/hd-overview/._SKILL.mdskills/hd-overview/SKILL.md−90skills/hd-permissions/._SKILL.mdskills/hd-permissions/SKILL.md−76skills/hd-profile-menu/._SKILL.mdskills/hd-profile-menu/SKILL.md−83skills/hd-pup/SKILL.md−57skills/hd-recording/SKILL.md−342skills/hd-runtime-mode/SKILL.md−81skills/hd-screen-lock/SKILL.md−89skills/hd-self-screenshot/._SKILL.mdskills/hd-self-screenshot/SKILL.md−288skills/hd-self-update/._SKILL.mdskills/hd-self-update/SKILL.md−50skills/hd-settings/._SKILL.mdskills/hd-settings/SKILL.md−110skills/hd-setup-steps/._SKILL.mdskills/hd-setup-steps/SKILL.md−134skills/hd-setup/._SKILL.mdskills/hd-setup/SKILL.md−192skills/hd-skill-catalog/SKILL.md−119skills/hd-skill-loop/SKILL.md−120skills/hd-tab-icons/._SKILL.mdskills/hd-tab-icons/SKILL.md−338skills/hd-ui/._SKILL.mdskills/hd-ui/SKILL.md−218skills/hd-webview/SKILL.md−55skills/hd-welcome/._SKILL.mdskills/hd-welcome/SKILL.md−79skills/hd-who-am-i/._SKILL.mdskills/hd-who-am-i/SKILL.md−94skills/hd-wiki-review/SKILL.md−48skills/hd-workspace-monitoring/._SKILL.mdskills/hd-workspace-monitoring/SKILL.md−160skills/hd-workspace-updater/._SKILL.mdskills/hd-workspace-updater/SKILL.md−59skills/._hd-adom-authdeleted⋯ 1 unchanged line ⋯
skills/._hd-adom-desktopdeleted⋯ 1 unchanged line ⋯
skills/._hd-adom-menudeleted⋯ 1 unchanged line ⋯
skills/._hd-apideleted⋯ 1 unchanged line ⋯
skills/._hd-bridgesdeleted⋯ 1 unchanged line ⋯
skills/._hd-captionsdeleted⋯ 1 unchanged line ⋯
skills/._hd-capture-sharedeleted⋯ 1 unchanged line ⋯
skills/._hd-claude-authdeleted⋯ 1 unchanged line ⋯
skills/._hd-containerdeleted⋯ 1 unchanged line ⋯
skills/._hd-container-statsdeleted⋯ 1 unchanged line ⋯
skills/._hd-desktop-ssedeleted⋯ 1 unchanged line ⋯
skills/._hd-eda-discoverydeleted⋯ 1 unchanged line ⋯
skills/._hd-evaldeleted⋯ 1 unchanged line ⋯
skills/._hd-instapcbdeleted⋯ 1 unchanged line ⋯
skills/._hd-monitordeleted⋯ 1 unchanged line ⋯
skills/._hd-notificationsdeleted⋯ 1 unchanged line ⋯
skills/._hd-open-urldeleted⋯ 1 unchanged line ⋯
skills/._hd-overviewdeleted⋯ 1 unchanged line ⋯
skills/._hd-permissionsdeleted⋯ 1 unchanged line ⋯
skills/._hd-profile-menudeleted⋯ 1 unchanged line ⋯
skills/._hd-self-screenshotdeleted⋯ 1 unchanged line ⋯
skills/._hd-self-updatedeleted⋯ 1 unchanged line ⋯
skills/._hd-settingsdeleted⋯ 1 unchanged line ⋯
skills/._hd-setupdeleted⋯ 1 unchanged line ⋯
skills/._hd-setup-stepsdeleted⋯ 1 unchanged line ⋯
skills/._hd-tab-iconsdeleted⋯ 1 unchanged line ⋯
skills/._hd-uideleted⋯ 1 unchanged line ⋯
skills/._hd-welcomedeleted⋯ 1 unchanged line ⋯
skills/._hd-who-am-ideleted⋯ 1 unchanged line ⋯
skills/._hd-workspace-monitoringdeleted⋯ 1 unchanged line ⋯
skills/._hd-workspace-updaterdeleted⋯ 1 unchanged line ⋯
skills/hd-adom-auth/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-adom-auth/SKILL.mddeleted−326@@ -1,326 +0,0 @@-name: hd-adom-auth-description: >- Adom auth in HD — how login/logout works (auth intent flow), where the- session token lives, the profile menu, and how the Adom session feeds the- setup steps. For Claude Code auth specifically, see hd-claude-auth.- READ when troubleshooting Adom login failures, session token expiry, the- profile dropdown, or virgin-reset Adom-auth options. Trigger words —- adom login, login, logout, auth, session token, auth intent, confirmation- code, profile menu, avatar, sign in, sign out, hydrogen-session.txt, auth- proxy, clear session, HD adom login, Your profile, Your repositories.--# Hydrogen Desktop -- Authentication Reference--## Auth Flow Overview--HD uses an **auth intent** flow (no email/password entry in the app).-The Tauri backend creates a short-lived intent token on the Carbon API,-opens the user's default browser to the Adom login page, and polls until-the user completes sign-in in the browser.--### Sequence--1. HD calls `invoke('auth_intent_start')` -> Rust POSTs to- `https://carbon.adom.inc/auth/intents` with `{"max_age": 7776000}`- (90-day session).-2. Carbon returns `{token, confirmation_code, expires_at, auth_url}`.-3. HD opens the browser to the **`auth_url` Carbon returned**, falling back to- `https://hydrogen.adom.inc/auth/intent?token={token}` (singular `intent`,- token as a query param — NOT `/auth/intents/{token}`).-4. HD displays the confirmation code on screen and shows a spinner.-5. Frontend polls `invoke('auth_intent_poll', {token})` every 2 seconds.- Rust GETs `https://carbon.adom.inc/auth/intents/{token}/status`.-6. When the user confirms in the browser, the response includes- `{state: "authenticated", session_token: "..."}`.-7. Rust saves the session token to `%APPDATA%\hydrogen-desktop\hydrogen-session.txt`- and a replay copy to `%APPDATA%\hydrogen-desktop\replay-session.txt`.-8. Frontend redirects to the dashboard.--### Source files--- **Login page**: `src/routes/auth/login/+page.svelte`-- **Auth Rust backend**: `src-tauri/crates/hd-app/src/lib.rs` (functions- `auth_intent_start`, `auth_intent_poll`, `auth_proxy`,- `check_replay_credentials`, `replay_adom_login`). NOTE: the old- `src-tauri/src/lib.rs` is a pre-crate-split vestige — the live code is in the- `hd-app` crate.-- **Auth components**: `src/lib/components/auth/` (Header, Form, Footer,- SubmitButton, Error, Success)-- **User API**: `src/lib/api/authenticated_user.ts`---## Session Token--**Location**: `%APPDATA%\hydrogen-desktop\hydrogen-session.txt`--A plain-text file containing the Adom session cookie value. Created by either:-- The auth intent flow (on successful poll)-- The auth proxy (when Carbon returns a `set-cookie: session_token=...` header)--The auth proxy (`invoke('auth_proxy', ...)`) reads this file and attaches it-as a `Cookie: session_token=...` header on every request to `carbon.adom.inc`.--### Token lifecycle--- **Created**: on login (auth intent or browser set-cookie capture)-- **Refreshed**: automatically when the auth proxy receives a new set-cookie-- **Cleared**: on logout (`auth_proxy({path: "/auth/clear-local"})`) or on- 401 response (Rust auto-deletes the file)-- **Max age**: 90 days (set at intent creation)--### Reading the token programmatically--```bash-# From the control API-curl http://127.0.0.1:47084/auth-token--# From inside the workspace (injected by setup step inject-api-key, mode 644)-# run inside the workspace — WSL2: `wsl -d Adom-Workspace -u adom -- cat /var/run/adom/api-key`; Docker: `docker exec <container> cat /var/run/adom/api-key`-cat /var/run/adom/api-key-```---## Auth Proxy--The Tauri command `auth_proxy` is the gateway for all authenticated requests-to the Adom Carbon API. It:--1. Reads the session token from disk-2. Attaches it as a cookie to the outgoing request-3. Captures any `set-cookie` response and updates the saved token-4. On 401, deletes the token and returns `{expired: true}`-5. Supports a special `path: "/auth/clear-local"` to delete the token file--**Base URL**: `https://carbon.adom.inc`--```javascript-// Example: get the current user-const result = await invoke('auth_proxy', {- path: '/api/me',- method: 'GET'-});-// result = { status: 200, data: { id: "...", name: "jlauer12", ... }, ok: true }-```---## Credential Replay--On login page mount, HD checks for saved credentials before starting the-full auth intent flow:--1. `invoke('check_replay_credentials')` checks if replay files exist on disk.-2. If `replay-session.txt` exists (has_adom = true), HD shows a "REPLAY"- badge and a 5-second countdown.-3. `invoke('replay_adom_login')` attempts to authenticate using the saved- session token.-4. If replay succeeds, the user is logged in without opening a browser.-5. If replay fails, HD falls back to the normal auth intent flow.--This makes re-login after a virgin reset instant when the user chose to-preserve the Adom session token.---## Claude Credentials--**Location**: `%APPDATA%\hydrogen-desktop\replay-claude-credentials.json`--Stores Claude Code OAuth credentials so they can be replayed into the-workspace during bootstrap. The setup step `inject-api-key` reads the-session token to authenticate with the Adom API, and Claude credentials-are injected separately.--### Virgin reset options for auth--The virgin reset panel (Setup Panel -> Virgin Reset) has per-item toggles:--| Toggle | File | What it clears |-|--------|------|----------------|-| Adom session token | `hydrogen-session.txt` | Adom login. Next launch requires re-auth. |-| Claude credentials | `replay-claude-credentials.json` | Claude Code OAuth. Next bootstrap re-authenticates Claude. |--These are independent -- you can wipe Claude creds without losing Adom login,-or vice versa.--### CSS selectors for virgin reset auth options--```-label.virgin-opt -- each checkbox row-input[type="checkbox"] -- the toggle (bind:checked to virginOpts.adom_token / virginOpts.claude_token)-.virgin-hint -- shows filename (hydrogen-session.txt / replay-claude-credentials.json)-.vp-badge -- status badge ("exists" / "clean")-.vp-exists -- green badge (file exists)-.vp-clean -- gray badge (file gone)-```---## Profile Menu--**Location**: Top-right corner of the editor nav bar.-**Source**: `src/lib/components/editor/EditorNav.svelte` (lines ~2403-2443)--Click the circular user avatar to open a dropdown with two sections.--### Menu items--| Item | Icon | Action |-|------|------|--------|-| Your profile | `mdi:account-circle-outline` | Navigates to `/users/{username}` |-| Your repositories | `mdi:folder-outline` | Navigates to `/users/{username}/repositories` |-| Your molecules | `mdi:atom` | Navigates to `/users/{username}/molecules` |-| Log out | `mdi:logout` | Confirms, then clears session + redirects to login |--**CRITICAL**: Logout is ONLY in the profile menu (top-right avatar), NOT in-the Adom menu (top-left logo). Do not look for logout in the wrong menu.--### CSS selectors--| Element | Selector |-|---------|----------|-| Profile dropdown container | `.profile-dropdown-container` |-| Avatar button | `.profile-dropdown-button` |-| Avatar image | `.profile-dropdown-button img` |-| Dropdown (open) | `.profile-dropdown-content.open` |-| Menu items | `.profile-dropdown-content .dropdown-link` |-| Menu item icons | `.dropdown-link span` |--### CDP eval examples--```javascript-// Open the profile menu-document.querySelector('.profile-dropdown-button')?.click();--// Click "Your profile"-var links = document.querySelectorAll('.profile-dropdown-content .dropdown-link');-if (links[0]) links[0].click();--// Click "Your repositories"-if (links[1]) links[1].click();--// Click "Your molecules"-if (links[2]) links[2].click();--// Click "Log out"-var logoutBtn = Array.from(document.querySelectorAll('.profile-dropdown-content .dropdown-link'))- .find(function(e) { return e.textContent.includes('Log out'); });-if (logoutBtn) logoutBtn.click();-// Note: this triggers a confirm dialog. To auto-confirm, you need to handle the dialog.--// Read the current user's avatar URL-var avatar = document.querySelector('.profile-dropdown-button img');-avatar ? avatar.src : 'no avatar';--// Read the current username from the avatar alt text-avatar ? avatar.alt : 'unknown';-```---## Logout Flow--1. User clicks "Log out" in profile menu.-2. `showConfirm('Are you sure you want to log out?', 'Log Out')` -- confirmation dialog.-3. If confirmed (Tauri mode):- - `invoke('auth_proxy', {path: '/auth/logout', method: 'DELETE'})` -- server-side logout- - `invoke('auth_proxy', {path: '/auth/clear-local', method: 'POST'})` -- deletes `hydrogen-session.txt`- - Clears auth-related domain preferences from `localStorage['hd-url-routing-prefs']`-4. Redirects to `/auth/login`.---## Login Page UI--**Route**: `/auth/login`--### Tauri mode (auth intent)-- Adom logo + "Log in to Hydrogen" header-- Confirmation code in a monospace box (`.confirmation-code`)-- Spinner + "Waiting for you to sign in..."-- "Open browser again" button (`.retry-button`)-- "Continue without login" link at the bottom (`.skip-login a`)--### Browser mode (email/password)-- Standard form with email + password fields-- "Log In" submit button-- Footer with registration link--### Auth intent CSS selectors--| Element | Selector |-|---------|----------|-| Container | `.intent-container` |-| Confirmation code | `.confirmation-code` |-| Spinner | `.spinner` |-| Status message | `.intent-message` |-| Hint text | `.intent-hint` |-| Success message | `.intent-success` |-| Error message | `.intent-error` |-| Retry button | `.retry-button` |-| Replay badge | `.replay-badge` |-| Skip login link | `.skip-login a` |--### CDP eval on the login page--```javascript-// Check auth intent state-document.querySelector('.intent-message')?.textContent;-// -> "Waiting for you to sign in..." or "Authenticated! Redirecting..."--// Read the confirmation code-document.querySelector('.confirmation-code')?.textContent;--// Click retry-document.querySelector('.retry-button')?.click();--// Skip login (continue without auth)-document.querySelector('.skip-login a')?.click();-```---## How Auth Affects Setup Steps--The setup/bootstrap process depends on a valid session token:--1. **inject-api-key** -- reads the HD-side `hydrogen-session.txt`, writes the- token into the workspace at `/var/run/adom/api-key` (mode 644) as the Adom- API key. If the token is missing or expired, this step fails and downstream- steps (adom-cli, gallia, etc.) cannot authenticate.--2. **Claude auth** -- uses `replay-claude-credentials.json` if available;- otherwise requires the user to complete the Claude OAuth flow inside the- VS Code iframe.--If bootstrap fails on auth, the fix is usually: log in again via the profile-menu login page, then re-run bootstrap from the Adom menu.---## Control API Auth Endpoints--| Method | Path | Description |-|--------|------|-------------|-| GET | `/auth-token` | Read the saved Adom session token |-| POST | `/claude-auth` | Start Claude Code OAuth (PKCE) |-| GET | `/oauth/callback` | Receives OAuth redirect (auto, do not call) |-| POST | `/start-oauth-proxy` | Spin up temp TCP listener for OAuth callback |-| POST | `/auth/proxy` | Proxy request to Carbon API with session token |--See the **hd-settings** skill (API Explorer section) for the-full endpoint reference.---## Cross-references--- **hd-ui** -- menu layout, where profile menu lives-- **hd-setup** -- bootstrap steps that depend on auth-- **hd-settings** -- API Explorer (auth endpoints tab)-- **hd-networking** -- control API port (47084)-- **carbon-preferences** -- user preferences that require auth
skills/hd-adom-desktop/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-adom-desktop/SKILL.mddeleted−235@@ -1,235 +0,0 @@-name: hd-adom-desktop-description: >- What Hydrogen Desktop (HD) and Adom Desktop (AD) are, what each does, and how- they work together — so the AI in the workspace knows it has HANDS on the- user's real operating system. HD is the workspace app the user looks at; AD is- the bridge that reaches the user's actual Windows machine (macOS/Linux coming- soon) to create files & folders, run shell commands, take OS screenshots,- launch apps, open browsers (Pup / native), and drive KiCad / Fusion 360. HD- bundles AD during install and runs it in embedded mode, but AD is a standalone- product that ALSO connects Adom cloud containers to a user's machine. HD also- has a BUILT-IN desktop bridge of its own that serves a subset of AD's verbs.- READ THIS to understand your reach beyond the workspace and how to invoke it.- Trigger words — adom desktop, AD, ad cli, what can you do on my computer, reach- my machine, create a file on my pc, make a folder on my desktop, run a command- on my machine, control the host, host screenshot, screenshot my screen, os- screenshot, open a file on my computer, control my desktop, bridge to my pc,- send file to my desktop, pull file from my desktop, hd and ad, two apps, how- do hd and ad work together, talk to hydrogen desktop, desktop bridge, relay,- embedded mode, AD hidden, hd_status, hd_api, shell_execute, adom-desktop ping,- adom-desktop status.--# Hydrogen Desktop ↔ Adom Desktop — your reach onto the user's real machine--You (the AI) run inside the **HD workspace** (a WSL2 Ubuntu distro by default).-That's a sandbox. **Adom Desktop (AD) is how you reach OUT of it onto the user's-actual operating system** — their real files, shell, screen, and desktop apps.-Knowing AD exists is the difference between "I can only work in here" and "I can-create that file on your Desktop, screenshot your screen, and open it in KiCad."--> **Platform reality (2026-06-12):** AD reaches the user's real **Windows**-> machine. macOS and Linux are *coming soon* — HD's host layer (screenshots,-> notifications, captions, window control) is currently Windows-only. Don't-> promise file/shell/screenshot actions on a Mac or Linux box yet.--## Two apps, two jobs--| | **Hydrogen Desktop (HD)** | **Adom Desktop (AD)** |-|---|---|---|-| What it is | The user-facing workspace app (Tauri). VS Code pane + Wiki + the runtime that hosts your workspace. | A bridge app that talks to the user's real OS and local apps. |-| What the user sees | The window they work in. | Usually nothing — HD hides it (embedded mode). |-| What it does for YOU | Hosts you, your editor, your tools. | Lets you act on the host: files, folders, shell, OS screenshots, app launch, KiCad/Fusion, browsers. |-| Runs without the other? | HD spawns AD. | **Yes** — AD is a standalone product; it also connects Adom *cloud* containers to a user's machine. |--**Why two apps:** AD came first and is genuinely powerful — it's what lets any-Adom cloud container reach into a user's desktop. Rather than reinvent it, HD-**bundles AD and makes it part of HD's install**, then puts it in *embedded mode*-so the user only ever manages one app (HD). The two talk to each other-constantly. See [hd-embedded-ad](../hd-embedded-ad/SKILL.md) for the embedding-mechanics (tray ownership, lifecycle, standalone↔embedded transitions).--## HD's built-in bridge vs the embedded AD (dual-client)--Here's the subtlety that matters when you reason about "who serves this verb?":-**there are two desktop clients on the host, not one.**--1. **HD's own built-in bridge** — HD's `hd-app` crate implements a desktop bridge- *itself* (`commands.rs::handle_command` → `screenshot.rs::handle_desktop_command`).- It serves a **subset** of AD's verbs directly inside HD's process:- `list_windows, screenshot_window, screenshot_screen, open_folder, open_url,- bring_to_front, set_window_state, list_files, watch_files, install_kicad,- install_node, list_browsers` under the `desktop` app, plus the top-level apps- `caption, notify, shell, kicad, fusion360, browser` and the desktop verbs- `set_project_watch, trigger_project_watch, revoke_approvals`.-2. **The standalone AD, spawned `--embedded`** by HD's supervisor- (`ad_supervisor.rs`). AD serves the **full** verb surface — including ones- HD's built-in bridge does NOT implement:- `desktop_record_*`, `desktop_recorder_*`, `desktop_list_monitors`,- `desktop_pull_glob`, `desktop_embedded_*`.--Both ends sit behind the same `adom-desktop` relay, so from the CLI you don't-choose between them — the relay routes to whichever client is connected. But-"will this verb work right now?" depends on which client is up. **Don't assume a-verb exists — verify capability first:**--```bash-adom-desktop status # → .capabilities lists what the connected client(s) can do-```--If `.capabilities` is missing `record` (or `desktop_list_monitors` errors with-"unknown desktop command"), the embedded AD isn't connected — only HD's built-in-bridge is — so recording / multi-monitor / glob-pull aren't available until AD-is up. Screenshots, window control, files, shell, captions, notifications and-the KiCad/Fusion/browser bridges all work off HD's built-in bridge regardless.--## What AD/HD let you do on the user's real Windows machine--All via the `adom-desktop` CLI from inside the workspace. Rather than-re-document every verb here, this is a **capability map → the focused skill**:--| Capability | Verb(s) / surface | Where it's documented |-|---|---|---|-| **Create files/folders on the host** | `send_files` (container → desktop) | [hd-file-transfer](../hd-file-transfer/SKILL.md) |-| **Pull files from the host** | `pull_file` (desktop → container, SHA256-verified); browse + watch via `list_files` / `watch_files` | [hd-file-transfer](../hd-file-transfer/SKILL.md) |-| **Run a shell command on the host** | `shell_execute` (real PowerShell/shell) | shell-trust model below |-| **Screen/window control + screenshots** | `desktop_screenshot_screen` / `desktop_screenshot_window` / `desktop_list_windows`, `desktop_set_window_state`, `desktop_bring_to_front` | [hd-self-screenshot](../hd-self-screenshot/SKILL.md) |-| **Record the screen / a tab** | `desktop_record_*` (whole screen), `browser_record_*` (one tab) | [hd-recording](../hd-recording/SKILL.md) — AD-served, verify `record` capability |-| **Desktop notifications** | `notify_user` (title/body/level + action buttons) | [hd-notifications](../hd-notifications/SKILL.md) |-| **On-screen captions** | `caption` (always-on-top click-through overlay) | [hd-captions](../hd-captions/SKILL.md) |-| **Open browsers** | `browser_*` (Pup) and the Browser Picker | [hd-open-url](../hd-open-url/SKILL.md), `pup` |-| **Drive KiCad / Fusion 360 / Pup** | `kicad_*`, `fusion_*`, `browser_*` | [hd-bridges](../hd-bridges/SKILL.md) |-| **Embedded mechanics (tray/lifecycle)** | `desktop_embedded_*` | [hd-embedded-ad](../hd-embedded-ad/SKILL.md) — AD-served |--For the complete verb list run `adom-desktop list_commands` (or see the-`adom-desktop` skill).--### Host shell-trust model--`shell_execute` runs a **real command on the user's machine** (`cmd /C` on-Windows). In **embedded mode HD auto-approves** shell commands so the AI isn't-blocked on per-command approval dialogs — convenient, but it means anything you-run there really runs. Revoke the standing auto-approval at any time:--```bash-adom-desktop shell_auto_approve '{"duration_secs":0}' # turn auto-approve off now-```--When you *don't* need a free-form shell, prefer the **no-escape structured-runners** over hand-quoted `shell_execute` (AD ≥ 1.8.50):-`adom-desktop run_script '{"interpreter":"bash|cmd|powershell","scriptB64":"<b64>"}'`-and `adom-desktop wsl_exec '{"distro":"Adom-Workspace","user":"adom","scriptB64":"<b64>"}'`-— base64 the script so no quoting survives to be mangled.--### Auto-installing missing apps (winget)--If KiCad or Node.js is missing on the host, you don't have to stop at a download-link — trigger an unattended winget install:--```bash-adom-desktop desktop_install_kicad '{}' # winget install KiCad-adom-desktop desktop_install_node '{}' # winget install Node.js (unblocks the Pup bridge)-```--(`install_kicad` fires when `kicad_list_versions` returns-`errorCode:"kicad_not_installed"`; `install_node` when a `browser_*` call returns-`errorCode:"node_not_found"`.)--### KiCad project auto-sync--`set_project_watch` / `trigger_project_watch` keep a KiCad project mirrored-between the workspace and the host. The watcher fires on changes to-`.kicad_sch` / `.kicad_pcb` / `.kicad_pro` files-(`project_watch.rs::KICAD_EXTENSIONS = ["kicad_sch", "kicad_pcb", "kicad_pro"]`),-so edits you make in the workspace land on the user's machine (and vice-versa)-without manual `send_files`/`pull_file` round-trips.--## Talking to HD + AD from the CLI--You reach the host through the **`adom-desktop` CLI**, which relays your commands-over a WebSocket to the desktop client(s).--**Host shell + files:**-```bash-adom-desktop shell_execute '{"command":"<cmd>","timeoutSeconds":60}'-adom-desktop pull_file '{"filePaths":["C:\\path\\file"],"saveTo":"/tmp"}'-adom-desktop send_files '{"filePaths":["/tmp/x"],"saveTo":"C:\\Users\\<you>"}'-adom-desktop desktop_list_windows-```--**Hydrogen Desktop control (the HD app itself):**-```bash-adom-desktop hd_status # is HD running + composed runtime state-adom-desktop hd_screenshot # window-bounded HD shot (resize <1500px before reading!)-adom-desktop hd_log # tail HD's log-adom-desktop hd_launch # start HD detached-# Direct HD control API (HTTP, no shell-escaping pain):-adom-desktop hd_api '{"method":"POST","path":"/setup/run-all","body":{}}'-adom-desktop hd_api '{"method":"GET","path":"/health"}'-```-`hd_api` hits HD's **control API**, discovering its real port from-`%APPDATA%\hydrogen-desktop\ports.json` key `control` (fallback **47084**).--**Embedded-mode introspection (AD-served):**-```bash-adom-desktop desktop_embedded_status # embedded=true/false, enteredVia=launch-flag|runtime-adopt-```--## How the connection works (and how you check it)--```-You (AI in the workspace)- └─ adom-desktop <verb> '<json>' ← CLI in the distro- └─ relay (adom-desktop serve: WS :8765 + HTTP :8766, started in the workspace)- └─ WebSocket ──► HD's built-in bridge AND/OR the embedded AD on the user's Windows machine- └─ acts on the OS / KiCad / Fusion / Chrome-```--- In HD the relay is started for you by the **`start-relay`** setup step; HD's- built-in bridge and the embedded AD both connect to it. Under WSL2 the relay- binds `0.0.0.0:8765` in the distro and WSL2 forwards it to the host's- `localhost:8765` (HTTP on `8766`), which is how the host-side clients reach- your in-workspace relay (see the `hd-networking` skill). It's reached- **same-port** under WSL2 — there's no host 47081/47082 remap (that was the old- Docker model).-- **Always verify the bridge before acting on the host:**- ```bash- adom-desktop ping # → {"status":"connected", ...} means a client is reachable- adom-desktop status # who's connected + .capabilities + which local apps are installed/running- ```- If `ping` fails, no desktop client is connected — tell the user, and (if needed)- walk the `adom-desktop` setup steps. Don't claim you acted on their machine- without a successful round-trip. Don't try to "fix" a dead host client from in- here — surface it.--## What NOT to do--- **Never `taskkill /F /IM adom-desktop.exe`** — the host AD is your relay bridge;- killing it severs your control channel (you can't get it back from in here).-- **Never hardcode `:9001` as HD's control API.** `9001` is real — it's AD's- liveness beacon that AD probes to detect HD — but HD's actual control API is- **47084** (use `hd_api`, which reads `ports.json` → `control`).-- **Don't broad-kill `node`** — VS Code Server runs as node; killing it kills the- whole workspace.-- Resize screenshots to `<1500px` before reading them, and inject every screenshot- into shotlog with a real description.--## Mental model for answering the user--When the user asks for something that lives on their real computer — "save this-to my Desktop", "what's on my screen", "open this in KiCad", "run this command on-my machine" — that's an **AD** job, and you can do it. When it's about the-workspace itself (editor, files under `/home/adom/project`, the runtime), that's-**HD**. Lead with what you can do, verify with `adom-desktop ping`, then act.--## Related skills-- [hd-embedded-ad](../hd-embedded-ad/SKILL.md) — how HD bundles + embeds AD (tray, lifecycle, standalone↔embedded)-- [hd-bridges](../hd-bridges/SKILL.md) — the KiCad / Fusion / Puppeteer bridge ecosystem-- [hd-file-transfer](../hd-file-transfer/SKILL.md) — send/pull files, browse + watch the host filesystem-- [hd-self-screenshot](../hd-self-screenshot/SKILL.md) — host screenshots + window control-- [hd-recording](../hd-recording/SKILL.md) — screen vs tab recording (AD-served)-- [hd-notifications](../hd-notifications/SKILL.md) — desktop toasts; [hd-captions](../hd-captions/SKILL.md) — on-screen caption overlay-- [hd-open-url](../hd-open-url/SKILL.md) — the ways to open a URL (Pup, native browser, picker, …)-- `pup` — driving Pup browser windows; `adom-desktop` — the full CLI verb reference-- [hd-runtime-mode](../hd-runtime-mode/SKILL.md) — the HD↔AD relationship is identical on WSL2 and Docker
skills/hd-adom-menu/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-adom-menu/SKILL.mddeleted−90@@ -1,90 +0,0 @@-name: hd-adom-menu-description: >- The Adom menu — the logo dropdown in the UPPER-LEFT corner of Hydrogen Desktop- (click the teal Adom logo). It is the hub for app-level actions: Go to- Dashboard, Settings, Ports, API Explorer, and a Desktop section (Adom Desktop,- Zoom, Fullscreen, Console, Developer Tools, Dev Toolbar), a- Container section (Start/Restart/Stop), and an Admin section (Setup Steps,- Virgin Reset, Browser Picker Manager). Also covers the window- TITLE BAR version readout and the About dialog (version/sha/build/signed +- Adom Desktop version + wiki/repo links). Use when the user asks "what's in the- Adom menu", "where's settings/devtools/about", "what version am I running", or- about any upper-left menu item. NOTE: this is the upper-LEFT logo menu — the- upper-RIGHT avatar dropdown is hd-profile-menu. Trigger words: adom menu, logo- menu, upper left menu, hamburger menu, settings, ports, api explorer, developer- tools, devtools, dev toolbar, adom desktop, about, about dialog, what version,- build info, setup steps menu, virgin reset menu, browser picker, console,- fullscreen, zoom.--# The Adom menu (upper-left logo dropdown)--Click the **teal Adom logo** in the top-left corner to open it. (Double-click the-logo = go straight to the Dashboard.) Source: `EditorNav.svelte` → `.logo-button`-→ `.logo-dropdown-container .dropdown-link` items.--> This is the **upper-LEFT** menu. The **upper-RIGHT** avatar dropdown (profile,-> repositories, logout) is a different menu — see **hd-profile-menu**.--## Items (top to bottom)--| Item | What it does |-|---|---|-| **Go to Dashboard** | Navigate HD to your Adom dashboard (`/`) |-| **Settings** | Opens the Settings dialog (`SettingsDialog.svelte`) |-| **Ports…** | Configure local host port assignments for HD services (`SettingsPortsDialog`) |-| **API Explorer…** | Browse + test every HD control-API endpoint (`ApiExplorerDialog`); see **hd-api** |-| *— Desktop —* | (desktop-only section, shown when running in HD/Tauri; in order) |-| **Adom Desktop** | Bring the embedded Adom Desktop window to the foreground (taskbar) — FIRST item in the section |-| **Zoom** | −/+ zoom the whole HD UI (persisted) |-| **Fullscreen** | Toggle fullscreen |-| **Console** | Show/hide the native HD debug console window |-| **Developer Tools** | Opens WebView2 DevTools (separate window) — see below |-| **Dev Toolbar** | Show/hide the Adom Dev Toolbar window (eval-in / shot / Claude control endpoints) |-| *— Container —* | (state shown: running/stopped/…) |-| **Start / Restart / Stop Container** | Workspace lifecycle (see **hd-workspace-lifecycle**) |-| *— Admin —* | |-| **Setup Steps** | Show the setup-steps panel — the setup cascade for the active runtime (16 steps under WSL2); see **hd-setup-steps** / **hd-setup** |-| **Virgin Reset** | Open the virgin-reset panel — wipe + re-run setup (see **hd-setup**) |-| **Browser Picker Manager** | Manage saved browser choices per domain (see **hd-browser-picker**) |--> **Removed items (do not list):** "Bridge Manager" (was a no-op with no backing-> panel) and "Port Mappings…" (no such action exists in the code).--Drive it programmatically with **hd-eval** (`target:"shell"`): click-`.logo-button`, then click the `.dropdown-link` whose text matches the item.--## Developer Tools--The `devtools` capability is compiled into **all** builds (dev and release), so-"Open Developer Tools" works for end users too — HD is a developer tool. It opens-WebView2 DevTools as a **separate top-level window** (a window-bounded HD-screenshot will NOT show it). Also reachable from the tray menu and via-`POST /devtools` (or the `open_devtools` Tauri command). Pair with **hd-eval** to-inspect/inject into any panel.--## Version readout — title bar + About dialog--**Title bar** (always visible) shows, in order:-`Hydrogen Desktop v<version> · <dev|release> · <sha>-dirty · <build time> · <signed|unsigned> · AD <ad-version>`-— a fast way to confirm which build is running and whether it's signed.--**About dialog** (deep version info) — open it from the **tray menu → "About-Hydrogen Desktop"** (emits the `show-about` event; the dialog is mounted-app-wide). It shows, for **both** Hydrogen Desktop and the embedded **Adom-Desktop**:-- Version, channel (dev/release), signed yes/no, commit SHA (+dirty), build time,- install path, and reachability/endpoint (for AD)-- Links to each app's GitHub repo and **Wiki v1 + Wiki v2** pages (they're git- repos now)--Backed by the `get_about_info` Tauri command.--## Related-- **hd-profile-menu** — the upper-RIGHT avatar dropdown (don't confuse the two)-- **hd-settings** — the settings tree behind the **Settings** item-- **hd-api** — the control-API surface behind **API Explorer…**-- **hd-capture-share** / **hd-recording** — the AV capture/record controls (Dev Toolbar)-- **hd-setup** / **hd-setup-steps** — what Setup Steps / Virgin Reset do-- **hd-eval** — drive these menu items / inject UI from the workspace
skills/hd-api/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-api/SKILL.mddeleted−354@@ -1,354 +0,0 @@-name: hd-api-description: >- Complete reference for Hydrogen Desktop control API endpoints. The control- API runs on a dynamic port (find it in the HD log or via port discovery). Use- these endpoints to drive HD programmatically: manage the workspace runtime (WSL2 distro or legacy Docker container), control- Claude Code, manipulate VS Code, manage workspace tabs, run setup steps, trigger- virgin reset, and more. ~159 endpoints. Two self-documenting sources of truth- list them live: GET /_manifest (curated grouped list) and GET /test/help- (the /test/* diagnostics).- Trigger words: HD API, control API, HD endpoint, container exec, claude api,- iframe eval, workspace tabs, setup run-all, docker status, reload vscode,- browser profiles, open url, virgin reset api, reboot api, diagnostics,- ad health, test window-tree, ai cursor, setup panel, screenshot mode.--# HD Control API Reference--The control API exposes **~159 endpoints**. Don't trust a static count or list in-this file to stay exhaustive — the API is **self-documenting**, with two live-sources of truth you should hit first:--| Source | What it gives you | Where |-|---|---|---|-| `GET /_manifest` | Curated, grouped, machine-readable list of the highest-value endpoints (path, method, args, description, `visibility`). `hd_api` forwards anything not listed. | `lib.rs:9144` |-| `GET /test/help` | Lists the `/test/*` diagnostics (window enumeration, UIA click, dialog classification, cascade reset). Gated behind `HD_TEST_API_ENABLED=1`. | `lib.rs:9723` |--**Base URL:** `http://127.0.0.1:<dynamic>` — the same loopback address from inside the-workspace (WSL2 mirrored networking shares loopback with the Windows host) and on the-Windows host. The port is dynamic per launch; read the live URL from-`~/.adom/hd-control-url` — see "Finding the Control Port" below.--## Finding the Control Port--The control API port is dynamic (default 47084, auto-resolves conflicts).--```bash-# From HD log-adom-desktop hd_log '{"tail":100}' | grep "control="--# From ports.json-type %APPDATA%\hydrogen-desktop\ports.json--# From inside the WSL2 workspace — USE THIS. HD writes its live control URL to a discovery-# file every launch (mirrored loopback), so you never hunt for the dynamic port:-CTRL="$(cat ~/.adom/hd-control-url)"; curl -sf "$CTRL/health"-```--⭐ **From the workspace, read `~/.adom/hd-control-url`** (e.g. `http://127.0.0.1:<control>`) — a-file, NOT an env var (your non-interactive Bash shells don't source `.bashrc`/`profile.d`). HD-rewrites it with the live dynamic port every launch. This is the channel for the UI command bus,-the live ports map, Claude control, etc. — distinct from `adom-cli` (`ADOM_HYDROGEN_URL` → SSE),-which is unchanged for web-hydrogen parity.--## Health & Status--| Method | Path | Description |-|---|---|---|-| GET | `/health` | HD alive check — returns `{ok, port, service}` |-| GET | `/buildinfo` | Running build git SHA + build metadata (the only trustworthy "did my build ship?" check) |-| GET | `/ports` | The 4 editable PortConfig ports (proxy, control, cdp, code_server) |-| GET | `/ports/all` | LIVE full port map — every host + WSL2 port HD has bound, with status + mirrored-mode info |--## AD-health diagnostics (`/ad/*`)--These verify the **adom-desktop / relay / embedded-AD** half of HD is alive (the-bridge that connects this cloud container to HD). All GET, all return `{ok, ...}`.--| Method | Path | Description |-|---|---|---|-| GET | `/ad/health` | Overall embedded-AD health roll-up |-| GET | `/ad/process` | Is the adom-desktop process running |-| GET | `/ad/embedded` | Embedded-AD module status |-| GET | `/ad/workspace-direct` | ⭐ The **converged** check — direct path to the workspace relay (use this one) |-| GET | `/ad/relay` | Relay (WS/HTTP) reachability |-| GET | `/ad/liveness-beacon` | Liveness beacon freshness |-| GET | `/ad/direct-api` | Direct-API reachability |-| GET | `/ad/discovery-file` | Discovery file present + parseable |--## Driving HD's UI (command bus)--Open/close/toggle any HD menu, dialog, or panel — the first-class alternative to CDP clicking. See the `hd-ui` skill.--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/ui/actions` | — | List every drivable UI action `{id,label,description,group}` (+ `_hints`) |-| POST | `/ui/invoke` | `{"id":"ports.open"}` | Run a UI action; returns `{ok,value}` only after the frontend ran it |-| GET | `/auth-token` | HD session token |-| GET | `/setup-status` | Overall setup completion state |-| GET | `/diagnostics/all` | Run all diagnostic checks |-| GET | `/diagnostics/{name}` | Run a single diagnostic check |--## Workspace / Container Management--These endpoints manage the active workspace runtime (a WSL2 distro by default, or a Docker container under `HD_RUNTIME=docker`).--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/container-status` | — | Workspace runtime available, image/distro present, workspace exists/running |-| GET | `/container-stats` | — | CPU, memory, disk usage |-| POST | `/container-restart` | — | Restart the active workspace runtime (5s timeout) |-| POST | `/container-stop` | — | Stop the active workspace runtime |-| POST | `/container-exec` | `{"command":"..."}` | Run command in the workspace as user `adom` |-| POST | `/container-delete` | — | Remove the workspace |-| POST | `/volume-delete` | — | Remove project volume (DESTRUCTIVE). **Docker runtime only** — no volume exists under WSL. |-| POST | `/image-delete` | — | Remove workspace image |-| GET | `/container-images` | — | List available workspace images |-| POST | `/container-switch-image` | `{"image":"..."}` | Switch to different image |-| POST | `/container-pull-image` | `{"image":"..."}` | Pull an image |-| GET | `/container/logs` | — | Workspace stdout/stderr |-| GET | `/container/env` | — | Workspace environment variables |-| GET | `/container/ports` | — | Published port mappings |-| GET | `/container/disk` | — | Disk usage inside the workspace |-| POST | `/container/restart` | — | Alias for `/container-restart` |--### WSL runtime endpoints (default runtime)--When HD runs the workspace as a WSL2 distro, it additionally exposes:--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/wsl/status` | — | WSL distro state (registered, running, code-server reachable) |-| GET | `/workspace/health` | — | Deep workspace health (distro + code-server + host reachability) |-| GET | `/virgin-reset-progress` | — | Live progress of an in-flight virgin reset |-| POST | `/workspace/terminal-profile` | — | Ensure the stable `Adom-Workspace` Windows Terminal profile (idempotent) |-| POST | `/workspace/terminal-profile/remove` | — | Remove the `Adom-Workspace` Windows Terminal profile(s) |-| POST | `/workspace/check-updates` | — | Check upstream for newer code-server + Claude extension; stage them |-| POST | `/workspace/apply-updates` | — | Apply staged code-server/extension updates + reload editor |--> ⛔ `POST /wsl/unregister` is **REFUSED as deprecated** — it was a headless-> destructive reset (deletes `/home/adom/project`). Use the Setup panel's Virgin-> Reset or `POST /setup/panel/run-virgin-reset` instead.--## Docker Desktop (legacy Docker runtime only — `HD_RUNTIME=docker`)--This `/docker/*` table applies only when HD is running the legacy Docker runtime.--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/docker/status` | — | Docker Desktop running state |-| GET | `/docker/version` | — | Docker engine version |-| GET | `/docker/containers` | — | List all Docker containers |-| GET | `/docker/images` | — | List all Docker images |-| POST | `/docker/start` | — | Launch Docker Desktop + start container |-| POST | `/docker/stop` | — | Stop Docker Desktop |-| POST | `/docker/restart` | — | Restart Docker Desktop |-| POST | `/docker/wait-ready` | — | Block until Docker daemon responds |-| POST | `/docker/install` | — | Install Docker Desktop (downloads ~730 MB) |-| POST | `/docker/uninstall` | — | Uninstall Docker Desktop |-| POST | `/docker/clean-reinstall` | — | Uninstall + reinstall Docker Desktop |-| GET | `/docker/install-progress` | — | Current install step + progress |--## WebView & VS Code--| Method | Path | Body | Description |-|---|---|---|---|-| POST | `/eval` | `{"js":"..."}` | JS eval in main HD webview |-| POST | `/iframe-eval` | `{"js":"...","contextIndex":N}` | JS eval in a specific iframe context (0-14) |-| POST | `/cdp-eval` | `{"expression":"..."}` | Direct Chrome DevTools Protocol eval |-| POST | `/click` | `{"x":N,"y":N}` | Click at coordinates in webview |-| POST | `/mouse-click` | `{"x":N,"y":N,"button":"left"}` | Mouse click with options |-| POST | `/query` | `{"selector":"..."}` | Query DOM elements |-| POST | `/reload-vscode` | — | Reload the VS Code iframe |-| POST | `/vscode/hide-activity-items` | — | Hide activity-bar items (persisted, overflow-proof) |-| GET | `/targets` | — | List CDP targets (all webview contexts) |-| GET | `/cdp-info` | — | CDP connection info (port, URL) |-| GET | `/devtools` | — | DevTools status |--### AI cursor (visible cursor for demos / clicking)--Distinct from `/click` and `/mouse-click` — this renders a **visible** AI cursor,-glides it, and can click. Use for demos or when the user should see where the AI clicks.--| Method | Path | Body | Description |-|---|---|---|---|-| POST | `/ai-cursor/move` | `{"x":N,"y":N}` | Move (glide) the visible AI cursor to coordinates |-| POST | `/ai-cursor/click` | `{"x":N,"y":N}` | Move the visible AI cursor there + click |-| POST | `/ai-cursor/hide` | — | Hide the visible AI cursor |--### Screenshot primitives--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/capture/viewport` | — | Capture the HD webview viewport (PNG) |-| POST | `/shot` | `{...}` | Screenshot primitive (see **hd-self-screenshot** for the full workflow) |--### Port hints--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/adom-port-hints` | — | Read the workspace port-hints map |-| POST | `/adom-port-hints` | `{...}` | Write/merge the workspace port-hints map |--## Claude Code--### Status / auth state (GET)--| Method | Path | Description |-|---|---|---|-| GET | `/claude/status` | Install state, auth, active session, version |-| GET | `/claude/auth-check` | Deep auth check — file exists, token, expiry |-| GET | `/claude/auth-status` | Auth-status detail |-| GET | `/claude/auth-page-state` | Which auth screen the panel shows: `continue-in-browser` \| `subscription-button` \| `chat`; returns `oauth_url` + `at_browser_auth_gate` |-| GET | `/claude/detect-auth-failure` | Scan webview contexts for auth button/errors |-| GET | `/claude/sessions` | List active Claude sessions |--### Auth actions (POST)--| Method | Path | Body | Description |-|---|---|---|---|-| POST | `/claude/find-auth-button` | — | Find + highlight the sign-in button WITHOUT clicking (validation) |-| POST | `/claude/start-auth` | — | Find + highlight + CLICK the "Claude.ai Subscription" button to begin OAuth |-| POST | `/claude/trigger-auth` | — | Alias for start-auth |-| POST | `/claude/detect-state` | — | Detect current auth/panel state |-| POST | `/claude/authorize-in-browser` | `{"timeout_ms":N,"dry_run":B,"fresh":B,"oauth_url":"..."}` | Auto-click the "Authorize" button on the claude.ai OAuth page (unattended sign-in) |-| POST | `/claude/authorize-tab-enter` | — | Approve the Adom OAuth tab via Enter (branded picker flow) |-| POST | `/claude/inject-creds` | — | Push host backup credentials into the workspace |-| POST | `/claude/wipe-creds` | `{"backup":B}` | Wipe Claude credentials (destructive); `backup:false` keeps the host backup so reinject can restore |--### Panel / conversation control (POST unless noted)--| Method | Path | Body | Description |-|---|---|---|---|-| POST | `/claude/open` | — | Open Claude Code panel (`claude-vscode.editor.open`) |-| POST | `/claude/ensure-open` | — | Ensure the panel is open (strict readiness; recovery if not rendered) |-| POST | `/claude/open-with-message` | `{"message":"..."}` | Open + pre-fill message |-| POST | `/claude/focus` | — | Focus Claude panel |-| POST | `/claude/new-conversation` | — | Start a fresh conversation (shows sign-in panel when unauthenticated) |-| POST | `/claude/new-chat-and-send` | `{"text":"...","typed":B,"submit":B,"delay_ms":N}` | Open fresh conversation, wait for compose box, type + (default) submit |-| POST | `/claude/submit` | — | Find + click send button |-| POST | `/claude/send` | `{"message":"..."}` | Send message to Claude |-| POST | `/claude/inject` | `{"message":"..."}` | Inject text into the compose box |-| POST | `/claude/inject-message` | `{"message":"..."}` | Inject into conversation |-| POST | `/claude/type-and-submit` | `{"message":"...","delay_ms":N}` | Type char-by-char (human-paced) + submit |-| POST | `/claude/remote-control` | `{"name":"..."}` | Turn on Claude Code Remote Control (submits `/remote-control` slash command) |-| POST | `/claude/highlight-open-button` | `{"duration":N}` | Glow animation on Claude button |-| POST | `/claude/clear-open-highlight` | — | Clear the open-button highlight |--## OAuth & Auth--| Method | Path | Body | Description |-|---|---|---|---|-| POST | `/claude-auth` | — | Full PKCE OAuth flow — opens browser, sets up callback |-| GET | `/oauth/callback` | — | OAuth callback handler (receives auth code) |-| POST | `/auth/proxy` | varies | Proxy auth requests to carbon.adom.inc |-| POST | `/open-url` | `{"url":"..."}` | Open URL (triggers browser picker) |-| POST | `/start-oauth-proxy` | `{"port":N}` | Start OAuth callback proxy on a port |-| GET | `/browser-profiles` | — | List browsers + profiles with avatars |-| POST | `/open-in-profile` | `{"url":"...","browser":"...","profileDir":"..."}` | Open URL in specific browser profile |-| POST | `/browser-picker/opened` | `{...}` | Frontend reports the browser picker was opened |-| GET | `/browser-picker/last-open` | — | Last browser-picker open event |--## Setup Steps & Setup Panel--### State / trigger / step control--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/setup/state` | — | Get current setup state (`never-run` \| `in-progress` \| `complete`) |-| POST | `/setup/state` | `{"state":"..."}` | Set setup state |-| POST | `/setup/step/<id>` | `{"continue_after":B}` | ⭐ Run one step by id (forces panel visible first); `continue_after:true` runs this step + every step after it |-| POST | `/setup/force-close` | — | Force-close setup panel |-| POST | `/setup/countdown` | `{"seconds":N,"message":"..."}` | Show countdown overlay |-| GET | `/setup/trigger` | — | Read trigger file |-| DELETE | `/setup/trigger` | — | Delete trigger file |-| POST | `/config-reset` | — | Reset HD config |--> ⛔ **REFUSED as deprecated** (headless runs with no visible UI):-> `POST /setup/run-all`, `POST /setup/run-step`, `POST /setup/virgin-reset`,-> `POST /wsl/unregister`. They return a refusal pointing at the UI-visible path.-> To run steps: use `POST /setup/step/<id>` or relaunch HD to auto-run the cascade.--### Setup panel control--| Method | Path | Body | Description |-|---|---|---|---|-| POST | `/setup/panel/show` | — | Force the setup panel visible |-| POST | `/setup/panel/hide` | — | Hide the setup panel |-| POST | `/setup/panel/show-virgin-reset` | — | Show panel + expand the Virgin Reset section |-| POST | `/setup/panel/virgin/show` | — | Show the Virgin Reset section only |-| POST | `/setup/panel/virgin/hide` | — | Hide the Virgin Reset section |-| POST | `/setup/panel/set-virgin-options` | `{"options":{...}}` | Set the virgin-reset checkboxes |-| POST | `/setup/panel/run-virgin-reset` | — | ⭐ The **ONLY** allowed virgin-reset trigger — UI-visible by construction (shows panel + checkboxes + progress before deleting anything) |-| POST | `/setup/panel/wipe-keep-auth` | — | Synchronous wipe that KEEPS Adom + Claude tokens |-| POST | `/setup/screenshot-mode` | `{"enabled":B}` | Toggle per-step screenshot capture (default OFF) |-| GET | `/setup/screenshot-mode` | — | Whether per-step screenshot mode is on |--## Workspace Tabs--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/workspace/tabs` | — | List all workspace tabs |-| GET | `/workspace/tabs/find?name=X` | — | Find tab by name (404 if not found) |-| POST | `/workspace/tabs` | `{tab spec}` | Add a new tab |--## System--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/system/nonce` | — | Get nonce for authenticated reboot |-| GET | `/system/reboot-state` | — | Check reboot pending state |-| POST | `/system/reboot` | `{"nonce":"..."}` | Reboot Windows (nonce-authenticated) |-| POST | `/system/cancel-reboot` | — | Cancel pending reboot |--## App lifecycle & window--| Method | Path | Body | Description |-|---|---|---|---|-| POST | `/app/restart` | — | Restart the HD app (kills + respawns the binary) |-| POST | `/app/relaunch-frontend` | — | Relaunch the HD GUI (kill+respawn) while it is responsive |-| POST | `/window/foreground` | — | Bring the HD window to the foreground (unminimize + focus) |--## Diagnostics (`/test/*`)--Window enumeration, UIA clicking, native-dialog classification, and cascade-diagnostics. **Gated behind `HD_TEST_API_ENABLED=1`** (return 404 / "test API-disabled" otherwise). `GET /test/help` self-lists the whole cluster.--| Method | Path | Body | Description |-|---|---|---|---|-| GET | `/test/help` | — | Self-lists every `/test/*` endpoint |-| GET | `/test/window-tree` | — | Enumerate all visible top-level windows |-| POST | `/test/focus` | `{"window":"..."}` | Focus HD (`self`) or a window by title substring |-| POST | `/test/click-uia` | `{...}` | Poll UIAutomation for a Button by Name; Invoke when found |-| GET | `/test/setup-steps` | — | Raw setup-steps.json + derived summary (alias: `/test/install-steps`) |-| POST | `/test/dump-state` | `{...}` | Write a diagnostic bundle (logs, install-steps, docker, tasklist, window-tree) |-| POST | `/test/reset-cascade` | — | Delete install-steps.json + emit setup-reset event |-| POST | `/test/screenshot-hd-window` | `{...}` | Capture HD's own window to a PNG file |-| GET | `/test/probe-dialogs` | — | Enumerate visible windows, classify against the cascade-blocker catalog (UAC, WSL2 update, MSI installer, Docker EULA, …); returns `kind` + `is_blocking` per window |--## Key Patterns--### iframe-eval context discovery-Contexts 0-14 cover all webview frames. Context numbers shift when panels open/close.-Always scan, never hardcode. The Claude Code extension webview IS reachable through-these contexts.--```-POST /iframe-eval {"js":"document.body.innerText.substring(0,100)","contextIndex":3}-```--### container-exec-Runs as user `adom` inside the workspace. Use full paths (`/home/adom/`-not `~/`). Tilde doesn't expand through the exec chain.--```-POST /container-exec {"command":"ls /home/adom/project"}-```--### Trigger setup re-run after completion-The trigger file poller STOPS when all steps are done. Use the API instead:-```-POST /setup/run-all-```
skills/hd-bridges/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-bridges/SKILL.mddeleted−390@@ -1,390 +0,0 @@-name: hd-bridges-description: >- Complete reference for the HD bridge ecosystem: the built-in KiCad, Fusion 360,- and Puppeteer bridges, plus the extensible wiki bridge catalog (e.g. Blender)- and the full set of Adom Desktop capabilities. How they connect through adom-desktop, the Bridge Manager- dialog, bridge status polling, and all bridge CLI commands. Use when the AI- needs to take screenshots, control KiCad/Fusion/browser, transfer files,- interact with the user's desktop, or check bridge status. Trigger words --- screenshot, desktop screenshot, list windows, capture screen, kicad, fusion,- browser, puppeteer, pup, send file, pull file, notify, desktop, bridge,- bridge manager, bridge status, bridge install.--# Hydrogen Desktop -- Bridge Ecosystem--HD is the bridge between your Docker container and the user's Windows desktop.-All commands go through the relay at ws://localhost:8765/.---## Bridge Architecture--HD launches external bridge processes **on demand**. This is an **extensible**-ecosystem, not a fixed list — new bridges ship via the wiki catalog (see-"Extensible bridge catalog" below). The core/first-party bridges:--| Bridge | Display name | Port (default) | Target app | Description |-|--------|-------------|------|------------|-------------|-| `kicad-bridge` | KiCad Bridge | 8771 | KiCad | Control KiCad PCB editor from Adom containers |-| `fusion-bridge` | Fusion 360 Bridge | 8773 | Fusion 360 | Control Fusion 360 CAD/CAM and EAGLE PCB |-| `puppeteer-bridge` | Puppeteer Bridge | 8851 | Chrome for Testing | Chrome automation, screenshots, and recording |--**Bridge ports have conventional defaults** (KiCad **8771**, Fusion **8773**, Pup **8851**), but adom-desktop registers the **live** port at bridge launch and may pick a different one if a default is taken. To find the actual live port, run `adom-desktop status` and look at `desktop.apps.<bridge>.bridgePort` (or `adom-desktop bridge_list`). In practice you don't need the port directly — use the `adom-desktop <verb>` CLI which routes to the right bridge automatically.--Bridges are launched on demand by HD. They are NOT always running. The Bridge-Manager polls status every 15 seconds via Tauri `invoke('get_bridge_status')`.--### What Adom Desktop can do (live capability list)--Bridges are only part of AD. Query the **authoritative, machine-specific** list:--```bash-adom-desktop status # → .capabilities and .desktop.apps-```--On a typical machine `.capabilities` =-`notify, files, kicad, fusion360, shell, usb, screenshot, browser, caption, record`.-Mapping to where each is documented:--| capability | what it is | skill |-|---|---|---|-| `kicad` / `fusion360` / `browser` | the three built-in app bridges | this skill |-| `screenshot` / `record` / `caption` | OS screenshots, screen/tab recording, recording captions | this skill (screenshots/recording); `caption` is recording-caption support |-| `files` | send_files / pull_file | **hd-file-transfer**, **hd-adom-desktop** |-| `notify` | desktop toast notifications | this skill |-| `shell` | run a command on the user's PC (approval-gated) | **hd-adom-desktop** |-| `usb` | USB passthrough to the workspace (e.g. workcell hardware over USBIP) | not yet covered — query `adom-desktop status`; future skill |--> `.desktop.apps` also reports, per app: `bridgeRunning`, `installed`, `running`,-> `version` — use it to check whether the target app (KiCad/Fusion/Chrome) is-> present before driving its bridge.--### Bridge status values--| Status | Meaning | Icon | Color |-|--------|---------|------|-------|-| `running` | Bridge process is alive and connected | `mdi:circle` (filled) | `#00b8b0` (teal) |-| `stopped` | Bridge exists but not running | `mdi:circle-outline` | `#808080` (gray) |-| `not_installed` | Bridge binary not found | `mdi:download` | `#e8a838` (amber) |--### BridgeInfo type--```typescript-interface BridgeInfo {- name: string // e.g. "kicad-bridge"- display_name: string // e.g. "KiCad Bridge"- version: string // e.g. "1.0.0"- description: string- port: number // conventional default; live port via `adom-desktop status`- status: 'running' | 'stopped' | 'not_installed'- app_detected: boolean // true if target app (KiCad, Fusion, Chrome) is found- app_name: string // e.g. "KiCad"- install_url: string // e.g. "https://www.kicad.org/download/"- install_method: string // "free_download" or "auto_download"-}-```---## Bridge Manager Dialog--**Access**: Adom menu (top-left logo) -> Desktop section -> Bridge Manager-**Source**: `src/lib/components/editor/BridgeManager.svelte`--Renders as a panel (not an overlay dialog) with a vertical card list showing-each bridge's status. Auto-refreshes every 15 seconds.--### Features-- Status badges with color-coded icons-- Per-bridge install links when the target app is missing-- "Browse Bridge Catalog" button linking to the wiki (`/apps?tag=hd-bridge`)-- Polls `invoke('get_bridge_status')` on mount and every 15 seconds--> **App missing? You can auto-install it.** When the target app isn't installed,-> the dialog shows an install link — but the AI doesn't have to stop there. For-> KiCad and Node.js you can trigger an **unattended winget install** instead of-> asking the user to download:-> `adom-desktop desktop_install_kicad '{}'` / `adom-desktop desktop_install_node '{}'`.-> (Fusion 360 cannot be installed programmatically — surface the link for that.)--### CSS selectors--| Element | Selector |-|---------|----------|-| Container | `.bridge-manager` |-| Header title | `.bridge-manager .header h2` |-| Subtitle | `.bridge-manager .subtitle` |-| Bridge card grid | `.bridge-grid` |-| Bridge card | `.bridge-card` |-| Running bridge card | `.bridge-card.running` |-| Bridge icon | `.bridge-icon` |-| Bridge name | `.bridge-name` |-| Bridge version | `.bridge-version` |-| Bridge description | `.bridge-description` |-| Bridge status | `.bridge-status` |-| App missing link | `.bridge-app-missing a` |-| Browse catalog button | `.browse-btn` |--### CDP eval examples--```javascript-// Read bridge statuses-JSON.stringify(Array.from(document.querySelectorAll('.bridge-card')).map(function(card) {- return {- name: card.querySelector('.bridge-name')?.textContent?.trim(),- status: card.querySelector('.bridge-status')?.textContent?.trim(),- running: card.classList.contains('running')- };-}));--// Click Browse Bridge Catalog-document.querySelector('.browse-btn')?.click();-```--### Opening the Bridge Manager from the Adom menu--```javascript-// Open Adom menu, then click Bridge Manager-document.querySelector('.logo-button')?.click();-setTimeout(function() {- var items = document.querySelectorAll('.dropdown-link');- Array.from(items).find(function(e) { return e.textContent.includes('Bridge Manager'); })?.click();-}, 200);-```---## Prerequisites--Check connection: `adom-desktop ping` -- returns "pong" if HD is connected.-If not connected, the user needs to launch HD on their Windows machine.---## Screenshots (zero dialogs, instant)--```bash-# Full screen capture-adom-desktop desktop_screenshot_screen--# Resized for Claude vision (default to this)-adom-desktop desktop_screenshot_screen '{"maxWidth": 1500}'--# Specific window by HWND-adom-desktop desktop_screenshot_window '{"hwnd": 12345}'--# List all capturable windows-adom-desktop desktop_list_windows-```--**AI guidance:**-- Default to `maxWidth: 1500` -- Claude vision hard limit is 2000px-- Use `format: "png"` for UI/text (default, lossless)-- Use `format: "jpeg", quality: 80` for photos/3D renders-- Use `format: "webp"` for smaller file sizes---## Browser Automation (Puppeteer Bridge)--Port: default **8851** (adom-desktop registers the live port; see `adom-desktop status`). Auto-downloads Chrome for Testing if not present.--```bash-# Open browser window-adom-desktop browser_open_window '{"url": "https://example.com"}'--# Screenshot current tab-adom-desktop browser_screenshot '{"sessionId": "default"}'--# Evaluate JavaScript-adom-desktop browser_eval '{"js": "document.title", "sessionId": "default"}'--# Navigate-adom-desktop browser_navigate '{"url": "https://...", "sessionId": "default"}'--# List open windows/tabs-adom-desktop browser_list_windows--# Record tab (CDP screencast, zero dialogs)-adom-desktop browser_record_start '{"sessionId": "default"}'-adom-desktop browser_record_stop '{"sessionId": "default"}'-```---## KiCad Bridge--Port: default **8771** (adom-desktop registers the live port; see `adom-desktop status`). Requires KiCad to be installed on the user's Windows machine.--```bash-adom-desktop kicad_open_board '{"path": "C:\\path\\to\\board.kicad_pcb"}'-adom-desktop kicad_open_schematic '{"path": "C:\\path\\to\\schematic.kicad_sch"}'-adom-desktop kicad_open_3d_viewer-adom-desktop kicad_run_drc '{"path": "..."}'-adom-desktop kicad_screenshot_all-adom-desktop kicad_window_info-adom-desktop kicad_send_key '{"key": "ctrl+s"}'-adom-desktop kicad_list_versions-adom-desktop kicad_install_symbol '{"path": "/tmp/symbol.kicad_sym", "library": "MyLib"}'-adom-desktop kicad_install_library '{"path": "/tmp/footprints", "name": "MyFootprints"}'-```--**Always run `kicad_window_info` BEFORE any KiCad operation** to check for modal dialogs —-and AFTER any save/DRC/open to catch errors KiCad raised.--### Finding + reading KiCad errors (modal dialogs)-`kicad_window_info` returns `hasModalDialogs` (bool) + a `modalDialogs` array — **this is how-you detect a KiCad error/blocking dialog** (a save-permission error, a file-format warning, a-DRC blocker). Each entry is `{hwnd, ownerHwnd, title}` (e.g. `title:"Error"`):--```bash-adom-desktop kicad_window_info '{}'-# → { editors:[...], hasModalDialogs:true,-# modalDialogs:[{ "hwnd":200934, "ownerHwnd":6163368, "title":"Error" }], projectManager:{...} }-```-- ⚠️ `modalDialogs` gives the **title + hwnd, NOT the body text**. To read *what the error says*,- **screenshot the dialog by its hwnd**: `adom-desktop desktop_screenshot_window '{"hwnd":200934}'`- → read the saved PNG. (Verified live 2026-06-15: a `ctrl+s` on a read-only board surfaced- `hasModalDialogs:true` + a `title:"Error"` entry; screenshotting its hwnd read *"Insufficient- permissions to save file …"*.)-- **Dismiss** it before proceeding: `kicad_send_key '{"hwnd":<dialog hwnd>,"key":"return"}'`- (OK) or `"escape"` (Cancel). A lingering modal blocks every subsequent KiCad action.-- So the loop is: **operate → `kicad_window_info` → if `hasModalDialogs`, screenshot each- `modalDialogs[].hwnd` to read it → handle/dismiss.** Don't assume an operation succeeded just- because the verb returned `success:true` — KiCad can still pop a blocking error.---## Fusion 360 Bridge--Port: default **8773** (adom-desktop registers the live port; see `adom-desktop status`). Requires Fusion 360 to be installed on the user's Windows machine.--```bash-adom-desktop fusion_start-adom-desktop fusion_import_step '{"path": "C:\\path\\to\\model.step"}'-adom-desktop fusion_export_step '{"path": "C:\\path\\to\\output.step"}'-adom-desktop fusion_board_info-adom-desktop fusion_electron_run '{"command": "..."}'-```---## Extensible bridge catalog (bridges are NOT a fixed set)--Bridges are a growing ecosystem. The three above (KiCad / Fusion 360 / Puppeteer)-are **built into Adom Desktop**. Additional bridges are published to the **wiki-bridge catalog** and installed on demand — for example the **Blender bridge**-(`adom-blender-bridge`), which is NOT built in (it won't appear in `adom-desktop-status` until installed). To use a catalog bridge:--- **Discover:** Bridge Manager → "Browse Bridge Catalog" → wiki `/apps?tag=hd-bridge`.-- **Reserved ports:** the `8900-8999` range is set aside for 3rd-party bridges.-- **Enumerate what's actually live:** `adom-desktop status` → `desktop.apps.*` lists- every bridge AD currently knows about, with `bridgePort`, `status`, and whether- the target app was detected. **Treat that as the source of truth** for "what- bridges exist right now" — this skill's table lists the core ones, but new- catalog bridges will appear in `status` without being in this table.-- **Invoke any bridge** through the `adom-desktop <verb>` CLI; it routes to the- right bridge automatically (you rarely need the port directly).--> Maintainer note: keep this skill in sync by periodically running-> `adom-desktop status` against a fully-bridged machine and reconciling the table.---## File Transfer--```bash-# Send file from Docker to Windows desktop-adom-desktop send_files '{"files": [{"path": "/home/adom/project/output.pdf"}]}'--# Pull file from Windows to Docker-adom-desktop pull_file '{"remotePath": "C:\\Users\\john\\Downloads\\data.csv", "localPath": "/tmp/data.csv"}'-```---## Desktop Interaction--```bash-# Toast notification-adom-desktop notify_user '{"message": "Build complete!", "duration_ms": 5000}'--# Open URL in user's browser-adom-desktop desktop_open_url '{"url": "https://..."}'--# Open folder in Explorer-adom-desktop desktop_open_folder '{"path": "C:\\Users\\john\\project"}'--# Execute shell command (requires user approval)-adom-desktop shell_execute '{"command": "dir C:\\Github"}'-```---## Screen Recording--> **Served by the embedded AD process, NOT HD's built-in bridge.** The-> `desktop_record_*` / `desktop_recorder_*` verbs only exist when the standalone-> AD (spawned `--embedded`) is connected. Verify before using:-> `adom-desktop status` → `.capabilities` must include `record`. If it's absent,-> only HD's built-in bridge is up and these verbs return "unknown desktop-> command". See **hd-recording** for full details.--> **Tab-vs-desktop footgun.** `desktop_record_start` records the **whole screen**-> and requires an explicit `confirmDesktopNotTabRecording:true` guard so you don't-> accidentally capture the entire desktop when you meant one tab. To record a-> single Chrome tab, use `browser_record_start` instead (CDP screencast, served by-> the Puppeteer bridge, no whole-screen capture).--```bash-# Record entire desktop (AD-served; needs the confirm guard, zero dialogs)-adom-desktop desktop_record_start '{"reason": "Demo recording", "confirmDesktopNotTabRecording": true}'-adom-desktop desktop_record_stop--# Record a single browser tab (CDP screencast, zero dialogs) — prefer this for one tab-adom-desktop browser_record_start '{"sessionId": "default"}'-adom-desktop browser_record_stop '{"sessionId": "default"}'-```---## Ralph Loop Pattern--```bash-# 1. Take screenshot-adom-desktop desktop_screenshot_screen '{"maxWidth": 1500}'-# 2. Read the screenshot (it's saved to /screenshots/)-# 3. Analyze with Claude vision-# 4. Make changes-# 5. Take another screenshot-# 6. Repeat until done-```---## Browser Profiles--HD can detect all browser profiles installed on the user's machine and open-URLs in specific browsers/profiles.--**Control API endpoints**:-- `GET /browser-profiles` -- detect Chrome, Edge, Brave, Firefox, Opera profiles-- `POST /open-in-profile` -- open URL in a specific browser and profile-- `POST /open-url` -- open URL in the default browser--**Source**: `src/lib/components/BrowserProfileDialog.svelte`---## Cross-references--- **hd-ui** -- Adom menu layout where Bridge Manager lives-- **hd-networking** -- port architecture (conventional bridge ports + dynamic registration)-- **hd-adom-menu** -- the API Explorer (has bridge-related endpoints)-- **hd-settings** -- HD settings surface-- **hd-self-screenshot** -- host screenshots, window control, and the ralph loop-- **kicad-interaction** -- foundational KiCad desktop interaction rules-- **pup** -- Puppeteer browser control via pup CLI
skills/hd-browser-picker/SKILL.mddeleted−474@@ -1,474 +0,0 @@-name: hd-browser-picker-description: HD's Browser Picker — the canonical handler for EVERY URL that gets opened inside HD. Click a link anywhere (main page, code-server iframe, webview panel, PortMappingsDialog, OAuth redirect, localhost link, external link) and HD intercepts it via 5 layers (on_new_window / on_navigation / NewWindowRequested / AddScriptToExecuteOnDocumentCreated / ContextMenuRequested) and routes it through the BrowserProfileDialog so the user (or an AI driving automation) picks where it opens — native browser profile, Hydrogen tab, HD window, or Pup. 5-second auto-countdown so AIs don't block waiting on a click. Read BEFORE adding a URL-opening feature, debugging dead links, fixing `shell.open not allowed`, or automating browser interactions from HD. Trigger words — browser picker, BrowserProfileDialog, hd-open-url, hd-open-url-force, open with browser picker, shell.open not allowed, shell:default, shell:allow-open, plugin-shell, url interception, link click, target blank, claude auth url, adom auth url, oauth callback in browser, default browser, browser profile, AI browser automation, countdown picker, auto-timer, shift click force, right-click open with, context menu, webview links, on_new_window, hd-links (legacy name).--# Hydrogen Desktop — Browser Picker (URL Interception)--## The Problem--HD wraps a WebView2 that contains:-- The Hydrogen SvelteKit frontend (localhost:1420)-- A code-server iframe (localhost:8080) with VS Code + Claude Code-- Webview panel iframes (any external URL like wiki, google, etc.)--When a link is clicked anywhere in this stack, HD needs to:-1. Catch it before it opens uncontrolled-2. Route it through the Browser Picker dialog-3. Let the user choose: native browser (Chrome/Edge), Hydrogen Tab, HD Browser Window, or Pup--## The 5 Interception Layers--Each layer catches a different type of URL event. All registered in `setup_frame_navigation_interception()` in `hd-app/src/lib.rs` via `webview.with_webview()`.--### Layer 1: `on_new_window` (Tauri wrapper)-**Catches:** `window.open()`, `<a target="_blank">` from the TOP-LEVEL webview and direct child frames-**Action:** Deny ALL. Dispatch to Browser Picker via CustomEvent.-**Shift detection:** Yes — `GetAsyncKeyState(0x10)` checks if Shift is held, dispatches `hd-open-url-force` for Browser Picker override.-**Debounce:** 500ms to prevent event loops.--### Layer 2: `on_navigation` (Tauri wrapper)-**Catches:** Top-level frame navigations (someone navigating the main webview to an external URL)-**Action:** Allow localhost/tauri/data/about. Block all external HTTPS. Dispatch to Browser Picker.--### Layer 3: `add_FrameNavigationStarting` (raw COM)-**Catches:** ALL child frame navigations (iframe navigating itself)-**Action:** Log only — don't block. Webview panels legitimately load external URLs in iframes.-**Why not block:** Blocking breaks webview panels (they need external URLs). Same-domain navigations (wiki → wiki/skills) work fine.--### Layer 4: `add_NewWindowRequested` (raw COM)-**Catches:** New window requests that Tauri's wrapper might miss-**Action:** `SetHandled(true)` for external URLs. No event dispatch (Tauri's handler does that).-**Purpose:** Safety net — prevents any uncontrolled window from being created.--### Layer 5: `AddScriptToExecuteOnDocumentCreated` (WebView2 API)-**Catches:** `<a target="_blank">` clicks inside cross-origin iframes-**Why needed:** Cross-origin iframe `target="_blank"` clicks don't fire `NewWindowRequested` on the parent WebView2. This is a WebView2 security boundary.-**How:** Injects a click listener into ALL frames (including cross-origin) that intercepts `target="_blank"` clicks and dispatches a CustomEvent to `window.top`.-**Survives:** Page reloads, iframe navigations — WebView2 auto-injects on every document load.--### Layer 6: `add_ContextMenuRequested` (raw COM, ICoreWebView2_11)-**Catches:** Right-click on links-**Action:** Adds "Open Link with Browser Picker" or "Open Page with Browser Picker" to the native WebView2 context menu.-**How:** Cast `ICoreWebView2` → `ICoreWebView2_11`, register handler. On right-click, check `HasLinkUri()`, create custom menu item via `ICoreWebView2Environment9.CreateContextMenuItem()`, register `add_CustomItemSelected` handler that dispatches `hd-open-url-force`.--## What Each Click Type Triggers--| User action | Where | Layer that catches | Result |-|---|---|---|---|-| Left-click `<a target="_blank">` in webview panel iframe | Cross-origin iframe | Layer 5 (JS script) | Browser Picker |-| Left-click regular `<a href>` in webview panel iframe | Cross-origin iframe | Layer 3 (logged) | Navigates iframe normally |-| Right-click → "Open in new window" | Any iframe | Layer 1 (on_new_window) | Browser Picker |-| Right-click → "Open Link with Browser Picker" | Any link | Layer 6 (context menu) | Browser Picker (force) |-| Shift+click any link | Anywhere | Layer 1 + shift detection | Browser Picker (force, no auto-timer) |-| `window.open()` from code-server | Code-server iframe | Layer 1 (on_new_window) | Browser Picker |-| `vscode.env.openExternal()` from Claude Code | Code-server → window.open | Layer 1 (on_new_window) | Browser Picker |-| Claude Code auth button | Code-server extension | Layer 1 (on_new_window) | Claude-branded Browser Picker |--## Browser Picker Dialog--`src/lib/components/BrowserProfileDialog.svelte`--### 3 Branded Variants-1. **Claude auth** — Claude logo, "Connect to Claude Code" (domains: `*.claude.ai`, `*.claude.com`)-2. **Adom auth** — Adom logo, "Sign in to Hydrogen Desktop" (domains: `*.adom.inc` + `/auth/intent`)-3. **General link** — "Browser Picker" title, no logo--### Consistent Elements (all variants)-- "Browser Picker" title at top-- Native Browsers section with real Google profile avatars-- Hydrogen Desktop section (collapsible, collapsed for auth)-- Details section (collapsible, collapsed) — URL, remember checkbox, trigger info, build version-- 5-second auto-timer with progress bar (cancelled by mouse movement)-- Keyboard navigation (arrow keys + Enter)-- 800ms delayed tooltips--### Auto-Timer Behavior-- Picks best default: work browser profile (non-gmail email) for auth, Pup for right-click/iframe opens-- 5-second countdown with progress bar-- ANY mouse movement cancels the timer-- Shift+click (force-show) disables timer entirely-- Selected destination highlighted with teal border--### ⭐ Fresh-window option (first-class)-A **freshly-launched browser window/process auto-foregrounds on Windows** — focus-stealing-prevention does NOT block a process's own first window. A URL opened as a *tab* in an-already-running browser does NOT reliably come to the front. So "fresh" is the proven way-to make the **Claude/Adom consent page pop to the front by itself** (removes the manual-foreground step that used to block hands-free auth).--- Mechanism: launch the browser exe with `--new-window` (Chromium) / `-new-window`- (Firefox) before the URL, instead of `open::that(url)` (shell-open can't force a window).-- UI: an **"Open in a fresh window"** checkbox above the Native Browsers list. **Defaulted- ON for auth URLs** (Claude/Adom), OFF otherwise. Only native-browser destinations honor- it; internal HD destinations ignore it. The dialog dispatches `select` with- `fresh: dest.category==='native' ? freshWindow : false`; `onProfileSelect` threads it to- `open_in_browser_profile` / `/open-in-profile`.-- `fresh` is honored by: Tauri `open_in_browser_profile` (direct), `POST /open-in-profile`- (direct), and `POST /open-url` (where it **pre-seeds the picker's toggle**; add- `direct:true` to open immediately instead). Direct paths echo- `{"fresh":…, "method":"<exe … fresh-window>"}`.-- Dev Toolbar A/B debug buttons: **▶ Open URL — FRESH window** vs **▶ Open URL — normal**.--### URL Event Deduplication-Frontend deduplicates URL events within 1-second window (same URL) to prevent double-open from multiple layers firing for the same click. Force events (Shift) bypass dedup.--### Prefs Sync-Preferences re-read from localStorage on every dialog open. If user deletes a domain in Browser Picker Manager, the next dialog open reflects it immediately.--## Custom Context Menu Implementation--Uses raw COM API via `ICoreWebView2_11` (cast from `ICoreWebView2` using `windows::core::Interface::cast`).--Key COM types used:-- `ICoreWebView2_11` — has `add_ContextMenuRequested`-- `ICoreWebView2Environment9` — has `CreateContextMenuItem`-- `ICoreWebView2ContextMenuTarget` — `HasLinkUri()`, `LinkUri()`, `PageUri()`-- `ICoreWebView2ContextMenuItemCollection` — `Count()`, `InsertValueAtIndex()`-- `ICoreWebView2ContextMenuItem` — `add_CustomItemSelected()`-- `COREWEBVIEW2_CONTEXT_MENU_ITEM_KIND_COMMAND` — command-type menu item--webview2-com 0.38 handler types used: `ContextMenuRequestedEventHandler`, `CustomItemSelectedEventHandler`--### How we got the context menu working (the full story)--**Initial assumption:** webview2-com 0.38 doesn't have context menu types. WRONG.--**First attempt:** Upgrade to webview2-com 0.39 which obviously has them. FAILED — 0.39 requires `windows 0.62` but Tauri pins `windows 0.61`. The upgrade broke 15+ API call sites due to `PCWSTR`/`HSTRING` type changes.--**Second attempt:** Write raw COM vtable bindings for ICoreWebView2_11 manually. UNNECESSARY.--**The discovery:** webview2-com 0.38 DOES export all the types. They're in `webview2_com::Microsoft::Web::WebView2::Win32::*` — the same module as everything else. The standard `.cast()` pattern works:--```rust-use windows::core::Interface;-use webview2_com::Microsoft::Web::WebView2::Win32::{- ICoreWebView2_11,- ICoreWebView2Environment9,- COREWEBVIEW2_CONTEXT_MENU_ITEM_KIND_COMMAND,-};--// Cast from base ICoreWebView2 — no raw vtable, no version upgrade-let core11: ICoreWebView2_11 = core.cast()?;-let env9: ICoreWebView2Environment9 = env.cast()?;-```--**The BOOL gotcha:** The one type that didn't work was `windows::Win32::Foundation::BOOL`. In windows 0.61, `BOOL(0)` isn't valid (it's not a tuple struct). `BOOL::default()` also fails (can't find it in the module). The fix is a raw cast:--```rust-let has_link = {- let mut val = 0i32;- target.HasLinkUri(&mut val as *mut i32 as *mut _)?;- val != 0-};-```--COM `BOOL` is just an `i32` under the hood (0 = false, non-zero = true). Casting the pointer works perfectly.--**The full chain:**-```rust-// 1. Cast to ICoreWebView2_11-let core11: ICoreWebView2_11 = core.cast()?;--// 2. Register ContextMenuRequested handler-core11.add_ContextMenuRequested(&handler, &mut token)?;--// 3. In the handler: get the right-click target-let target = args.ContextMenuTarget()?;--// 4. Check if it's a link (BOOL workaround)-let mut val = 0i32;-target.HasLinkUri(&mut val as *mut i32 as *mut _)?;--// 5. Get the link URL-let mut uri = PWSTR::null();-target.LinkUri(&mut uri)?;-let url = uri.to_string().unwrap_or_default();--// 6. Create custom menu item via Environment9-let env9: ICoreWebView2Environment9 = env.cast()?;-let item = env9.CreateContextMenuItem(&label, None, KIND_COMMAND)?;--// 7. Add click handler-item.add_CustomItemSelected(&click_handler, &mut token)?;--// 8. Insert at top of context menu-let items = args.MenuItems()?;-items.InsertValueAtIndex(0, &item)?;-```--**Lesson:** Don't assume a crate is missing types because the docs are sparse. The `webview2_com::Microsoft::Web::WebView2::Win32::*` re-export brings in the ENTIRE WebView2 COM type library. If the COM interface exists in the WebView2 SDK, the Rust binding exists too — you just need to cast to the right version interface.--## AddScriptToExecuteOnDocumentCreated Script--```js-(function() {- if (window.__hdLinkIntercept) return;- window.__hdLinkIntercept = true;- document.addEventListener('click', function(e) {- var a = e.target.closest('a[target="_blank"]');- if (!a) return;- var href = a.href;- if (!href || href.startsWith('javascript:')) return;- if (new URL(href).hostname === window.location.hostname) return;- e.preventDefault();- e.stopPropagation();- try {- window.top.dispatchEvent(new CustomEvent('hd-open-url', {- detail: JSON.stringify({- url: href,- trigger: 'target_blank_click',- shiftHeld: e.shiftKey- })- }));- } catch(err) {- window.parent.postMessage({- type: 'hd-open-url', url: href, trigger: 'target_blank_click'- }, '*');- }- }, true);-})();-```--**Why same-domain is allowed:** `if (new URL(href).hostname === window.location.hostname) return;` — wiki links to other wiki pages should navigate the iframe normally. Only cross-domain links get intercepted.--## WebView2 Event Architecture — What We Learned--### How to access raw WebView2 COM APIs from Tauri--Tauri exposes the platform webview via `webview.with_webview(|platform_webview| { ... })`. The closure runs on the main (UI) thread where COM objects live.--```rust-let controller = platform_webview.controller(); // ICoreWebView2Controller-let env = platform_webview.environment(); // ICoreWebView2Environment-let core = controller.CoreWebView2().unwrap(); // ICoreWebView2-```--From the base `ICoreWebView2`, you can cast to higher interfaces:-```rust-use windows::core::Interface;-let core4: ICoreWebView2_4 = Interface::cast(&core).unwrap(); // for add_FrameCreated-let core11: ICoreWebView2_11 = core.cast().unwrap(); // for add_ContextMenuRequested-let env9: ICoreWebView2Environment9 = env.cast().unwrap(); // for CreateContextMenuItem-```--### WebView2 event handler types in webview2-com 0.38--The crate provides `*EventHandler::create(Box::new(|sender, args| { ... }))` factory methods:-- `NavigationStartingEventHandler` — for `add_NavigationStarting` / `add_FrameNavigationStarting`-- `NewWindowRequestedEventHandler` — for `add_NewWindowRequested`-- `ContextMenuRequestedEventHandler` — for `add_ContextMenuRequested`-- `CustomItemSelectedEventHandler` — for context menu item click-- `AddScriptToExecuteOnDocumentCreatedCompletedHandler` — callback for script registration-- `FrameCreatedEventHandler` — for `add_FrameCreated` (on ICoreWebView2_4)--All handlers register with a `&mut i64` token parameter (for later removal).--### The event token pattern-```rust-let mut token = 0i64;-core.add_FrameNavigationStarting(&handler, &mut token)?;-// token is now set — save it to call remove_FrameNavigationStarting later-```--### Which events fire at which iframe depth--| Event | Top-level | Direct child iframe | Cross-origin child iframe |-|---|---|---|---|-| `NavigationStarting` (on_navigation) | Yes | No | No |-| `FrameNavigationStarting` | N/A | Yes | Yes (same-domain only for some) |-| `NewWindowRequested` (on_new_window) | Yes | Yes (same-origin) | **NO** (cross-origin) |-| `ContextMenuRequested` | Yes | Yes | Yes |--The critical gap: **cross-origin iframe `target="_blank"` clicks don't fire `NewWindowRequested`** on the parent. This is a WebView2 security boundary, not a bug.--### AddScriptToExecuteOnDocumentCreated — the secret weapon--This is a WebView2 configuration API, not eval injection. Key properties:--1. **Registered once** on `ICoreWebView2`, applies forever-2. **Runs in ALL frames** — top-level, child iframes, cross-origin iframes, nested iframes-3. **Runs BEFORE page JavaScript** — your script is guaranteed to execute first-4. **Survives navigations** — when the frame navigates to a new page, the script runs again-5. **Survives reloads** — F5, location.reload(), everything-6. **Controlled by WebView2** — not by the page, not by JS, not by CORS--This is how Electron handles preload scripts. It's the ONLY reliable way to run code inside a cross-origin iframe without the iframe's cooperation.--```rust-let script = windows::core::HSTRING::from("(function() { /* your code */ })();");-let handler = webview2_com::AddScriptToExecuteOnDocumentCreatedCompletedHandler::create(- Box::new(|_, _| Ok(()))-);-core.AddScriptToExecuteOnDocumentCreated(&script, &handler)?;-```--The script has access to:-- `window.top` — can dispatch CustomEvents to the top-level page (works even cross-origin in WebView2)-- `window.parent.postMessage()` — fallback if `window.top` access is blocked-- `document.addEventListener()` — full DOM access within the frame-- `window.location` — the frame's current URL--We use it to intercept `target="_blank"` clicks that would otherwise be invisible to the Rust layer.--### COM method signature patterns in webview2-com 0.38--The types use a mix of safe wrappers and raw out-parameters:--**Safe wrappers (return `Result<T>`):**-```rust-let target = args.ContextMenuTarget()?; // returns Result<ICoreWebView2ContextMenuTarget>-let items = args.MenuItems()?; // returns Result<ICoreWebView2ContextMenuItemCollection>-let item = env9.CreateContextMenuItem(&label, None, KIND_COMMAND)?; // returns Result<ICoreWebView2ContextMenuItem>-```--**Out-parameter methods:**-```rust-let mut has_link = windows::Win32::Foundation::BOOL(0);-target.HasLinkUri(&mut has_link)?; // writes to has_link-if has_link.as_bool() { ... }--let mut uri = windows::core::PWSTR::null();-target.LinkUri(&mut uri)?; // writes to uri-let url = uri.to_string().unwrap_or_default();--let mut count = 0u32;-items.Count(&mut count)?; // writes to count-```--**Event registration:**-```rust-let mut token = 0i64;-menu_item.add_CustomItemSelected(&handler, &mut token)?;-items.InsertValueAtIndex(count, &menu_item)?;-```--### Cargo.toml requirements--The `Win32_Foundation` and `Win32_System_Com` features must be enabled on the windows crate for BOOL and COM types:-```toml-[target.'cfg(windows)'.dependencies]-webview2-com = "0.38"-windows = { version = "0.61", features = [- "Win32_Foundation",- "Win32_System_Com",- "Win32_System_WinRT",- ...-] }-```--## Hard-Won Lessons--### `NewWindowResponse::Allow` must NEVER be returned-Original code returned `Allow` for localhost URLs. Code-server's `openExternal` wraps URLs in localhost routes. Returning `Allow` created uncontrolled WebView2 windows with no handlers. Fix: Deny everything, always.--### Cross-origin iframe `target="_blank"` is invisible to WebView2-No COM event fires on the parent WebView2 when a cross-origin child iframe has a `target="_blank"` click. Not `NewWindowRequested`, not `FrameNavigationStarting`, not `NavigationStarting`. The ONLY way to catch it: `AddScriptToExecuteOnDocumentCreated`.--### `on_new_window` features param isn't a string-Tauri's `on_new_window` callback receives `(Url, NewWindowFeatures)`. `NewWindowFeatures` is a struct with `size()` and `position()` methods, NOT a string. Can't call `.is_empty()`.--### `dispatch_url_event` needs 200ms delay-At 50ms, the webview eval sometimes doesn't process. 200ms is reliable.--### Domain check must only check the domain, not query params-URLs like `https://claude.com/oauth?redirect_uri=http://localhost:PORT` contain "localhost" in the query string. The `is_internal` check must parse the domain from `url.split('/').nth(2)`, not `url.contains("localhost")`.--### webview2-com 0.38 vs 0.39-0.38 is pinned by Tauri v2 (requires windows 0.61). 0.39 requires windows 0.62 and breaks 15+ API signatures. But 0.38 DOES have `ICoreWebView2_11`, `ContextMenuRequestedEventHandler`, and related types — they just use out-parameter patterns, not safe wrappers. Use `core.cast::<ICoreWebView2_11>()` which works in 0.38.--## Files--| File | What |-|---|---|-| `src-tauri/crates/hd-app/src/lib.rs` | All 6 interception layers, dispatch_url_event, context menu |-| `src/lib/components/BrowserProfileDialog.svelte` | Browser Picker dialog (3 variants, auto-timer, keyboard nav) |-| `src/lib/components/editor/EditorNav.svelte` | Browser Picker Manager modal |-| `src/routes/+layout.svelte` | CustomEvent listeners, URL event parsing, deduplication |-| `src/lib/assets/claude-logo.svg` | Real Claude logo from claude.ai/favicon.svg |-| `src/lib/assets/adom-logo-teal.svg` | Adom brand logo |-| `src/lib/buildVersion.ts` | Build version tracking for verification |-| `src-tauri/capabilities/default.json` + `src-tauri/crates/hd-app/capabilities/default.json` | Tauri permissions — MUST include `shell:default` AND `shell:allow-open` |--## The 5-second auto-countdown (why AI loves it)--The picker opens with a countdown on its best-default destination. If nothing interrupts, the URL auto-routes there. This is the key feature for AI automation: triggers like OAuth redirects, "open in browser" links, or wiki references work hands-free.--- **AI scripts can fire a URL open and the right thing happens within 5s** — no clicking, no blocking on a dialog.-- **Mouse movement cancels the timer** — if a human is at the keyboard, just wiggle the mouse and the picker waits.-- **Shift+click disables the timer entirely** — forces an explicit human choice.--Best-default heuristic:-- `*.claude.ai` / `*.claude.com` → "Claude auth" variant, defaults to work browser profile-- `*.adom.inc` + `/auth/intent` → "Adom auth" variant, defaults to work browser-- Right-click / iframe / unknown context → Pup (AI-drivable browser)-- General → user's last pick for that domain--## Localhost URLs route through the picker too--**Rule:** ALL http(s) URLs go through the picker, including `http://localhost:*` and `http://127.0.0.1:*`. PortMappingsDialog's container-service links must open in the user's chosen browser, NOT in a popup-blocked WebView2 child window.--The `on_new_window` handler in `lib.rs` (line ~1986) MUST dispatch `hd-open-url` for both internal AND external URLs. If you see `is_internal` short-circuiting the dispatch with no `dispatch_url_event` call, that's the bug — internal URLs fall through to Tauri's default `shell.open()` which then fails with the permission error below.--## `shell:default` is non-negotiable--The picker's "open in native browser" path ultimately calls `@tauri-apps/plugin-shell.open()`. That requires BOTH `shell:allow-open` AND `shell:default` in BOTH capability files. Without `shell:default` you get this exact rejection in the HD log:--```-[JS UNHANDLED REJECTION] shell.open not allowed. Permissions associated with this command: shell:allow-open, shell:default-```--Both files must list both permissions:-- `src-tauri/capabilities/default.json`-- `src-tauri/crates/hd-app/capabilities/default.json` (the hd-app crate's own capabilities)--Tauri compiles capabilities INTO the binary at build time. Capability changes require a Rust rebuild (`hd_build_rust`), not just a frontend rebuild.--## AI automation recipe--```bash-# 1. (Optional) Set Pup as default for domains you want to drive-# HD UI → Settings → Browser Picker Manager → add domain → choose Pup-# OR let the 5s auto-countdown pick Pup for unknown URLs.--# 2. Trigger a URL open-adom-desktop shell_execute '{"command":"curl -s -X POST -d {\"url\":\"https://example.com\"} http://127.0.0.1:47084/open-url"}'--# 3. Within 5s, picker auto-routes to Pup (or saved pref)-# 4. Drive Pup with adom-desktop browser_* verbs-adom-desktop browser_eval '{"sessionId":"default","js":"document.title"}'--# Force a picker dialog (no auto-timer)-adom-desktop shell_execute '{"command":"curl -s -X POST -d {\"url\":\"https://example.com\",\"force\":true} http://127.0.0.1:47084/open-url"}'--# Route through the picker with the fresh-window toggle PRE-CHECKED (force = skip the-# 5s timer). The user/AI then picks a native browser → opens fresh + auto-foregrounds.-adom-desktop hd_api '{"method":"POST","path":"/open-url","body":{"url":"https://claude.ai/","fresh":true,"force":true}}'-# BYPASS the picker and open directly in a specific browser+profile, fresh:-adom-desktop hd_api '{"method":"POST","path":"/open-in-profile","body":{"url":"https://claude.ai/","browser":"edge","profileDir":"Default","fresh":true}}'-# /open-url with direct:true also bypasses the picker (opens immediately, default browser):-adom-desktop hd_api '{"method":"POST","path":"/open-url","body":{"url":"https://claude.ai/","fresh":true,"direct":true}}'-```--`/open-url` ROUTES THROUGH THE PICKER by default (emits `browser-open-request`):-`fresh` pre-seeds the "Open in a fresh window" toggle, `force:true` skips the 5s-auto-timer, `direct:true` (or naming a concrete `browser`) bypasses the picker and-opens immediately. The response `routed` field is `"browser-picker"` or `"direct"`.-`/open-in-profile` always opens directly (no picker). Both direct paths echo-`method:"msedge.exe … fresh-window"` — read it before claiming success.--## Prefs are now an API — the picker is a FALLBACK, not a gate--The per-domain routing memory is CRUD over the control API, so the **AI governs routing** and-the picker only pops for a domain with no policy + no direct call:-`GET /browser-picker/prefs` (read `{domains:{<host>:{destination,label}}}`),-`PUT /browser-picker/prefs {domain,destination,label?}` (set/override),-`DELETE /browser-picker/prefs/<host>` (forget one), `DELETE /browser-picker/prefs` (clear all).-`destination` is an ID: `hydrogen-tab` | `webview-window` | `pup-window`, or-`browser:<browser>:<dir>` (profile dir encoded in the ID). `fresh` is per-open, not stored.-Writes hit the Manager's live state (localStorage + `hd-url-routing-prefs-changed`). Full-routing-policy guidance lives in **[hd-open-url](../hd-open-url/SKILL.md)**.--## How HD skills get deployed (workflow note)--HD public skills (this one, hd-open-url, hd-monitor, etc.) live in the HD repo under `skills/public-facing/{shared,wsl2,docker}/<name>/SKILL.md`. They're deployed into the user's workspace by the `install-hd-skills` setup step, which is runtime-aware: it installs `shared/` plus the active runtime's bucket (`wsl2/` by default via the `/mnt/c` auto-mount; `docker/` via `docker cp` under `HD_RUNTIME=docker`), flat into `~/.claude/skills/<name>/`. See `hd-skill-catalog` (public) and `hd-skills-structure` (dev-internal) for the full layout.
skills/hd-captions/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-captions/SKILL.mddeleted−198@@ -1,198 +0,0 @@-name: hd-captions-description: >- Paint labeled caption text on screen for demos/walkthroughs — captions are- CAPTURED BY RECORDINGS so they show up in the finished video. HD has TWO caption- surfaces: (1) a WORKSPACE caption rendered inside the HD window via- `adom-cli hydrogen caption` — THE default, identical on web-Hydrogen AND HD- (text positional; `-d` duration in SECONDS not ms; 30s auto-expire by default, 5-min- hard cap, newest-wins, `hide` to clear; a raw-HTTP `POST .../current/caption` fallback- exists for scripts hitting HD's API directly), and- (2) a full-DESKTOP overlay over ALL windows via `adom-desktop desktop_caption` — HD's- built-in Win32 always-on-top, click-through overlay (duration in MILLISECONDS — opposite- unit). Pick by what you're recording: workspace recording → workspace- caption; whole-screen / other-apps (KiCad, browser) recording → desktop overlay.- Covers both, the desktop-overlay args (text, id, position top/center/bottom/corners,- normalized x/y, size/fontSize, duration, action: hide|force-clear) and the id rule- (same id replaces, different ids coexist). Windows-only for the desktop overlay.- Distinct from toasts (notifications) and TTS (audio). Trigger words — caption,- on-screen caption, workspace caption, hydrogen caption, screen overlay, screen- label, callout, annotate the screen, step label, put text on screen, overlay text,- always on top text, click-through overlay, desktop caption, caption command,- adom-cli hydrogen caption, walkthrough label, demo callout, status overlay, label- the screen, point at the screen, hide caption, clear captions, step 3 callout,- caption duration, caption seconds, caption auto-expire, caption stuck on screen,- caption units, caption position, caption size, persist caption.--# Hydrogen Desktop — On-Screen Captions--You can **paint labeled caption text on screen** for demos and walkthroughs — and-because captions are real pixels, **they're captured by recordings**, so they end up-in the finished video ("Step 3: approve the UAC dialog →").--> Captions are **visual on-screen narration** — centered status / countdowns / "what-> I'm doing," great during demos and recordings. Distinct from:-> - **toasts** — corner pop-ups via `POST /ui/toast` (transient notifications, see-> `hd-notifications`); a caption is centered narration, a toast is a corner alert.-> - **TTS** *audio* narration (`adom-tts`) — pair a caption with TTS for a narrated walkthrough.-> - the **native Win32 auth-overlay caption** — a separate internal surface HD draws for-> login/auth flows; not something you trigger for demos. The two surfaces below are the-> ones you drive.--## Two caption surfaces — pick by WHAT you're recording--HD has **two** caption surfaces. Choosing the right one is the key decision, because-each is captured by a *different* kind of recording:--| Surface | Command | Renders | Captured by | Use when |-|---|---|---|---|---|-| **Workspace caption** | `adom-cli hydrogen caption` | **inside the HD workspace** (webview overlay, web-Hydrogen style) | a recording/screenshot of the **HD window** (panel/workspace) | the demo is **inside HD** — captioning the workspace itself |-| **Desktop overlay** | `adom-desktop desktop_caption` | **over the WHOLE desktop**, above every window (Win32) | a **full-screen / desktop** recording (native `recording --share screen`, AD desktop) | the demo shows **other apps** (KiCad, Fusion, a browser) or the whole screen |--**Match the caption to the recording.** A native `screenshot panel` / workspace-Element-Capture sees the **webview** content, so it captures the **workspace** caption-but NOT the desktop overlay (a separate OS window). A full-**screen** recording-captures the **desktop overlay** (it's screen pixels). Pick wrong and your caption-won't be in the video.--> Quick rule: "caption the Hydrogen workspace" → `adom-cli hydrogen caption`;-> "caption over the whole screen / while I record KiCad / a corner status label" →-> `adom-desktop desktop_caption` (the only one with corners, ids, and x/y).--## (1) Workspace caption — `adom-cli hydrogen caption` ⭐ THE DEFAULT--**Reach for this FIRST.** It's the *same* command on web Hydrogen and HD (adom-cli-routes through HD's workspace-events server → SSE → the frontend `captionStore`), so it-stays consistent across both — John's principle, same as screenshots. Renders centered-**inside the HD workspace webview**. `show` takes the TEXT as a **positional arg** (NOT-`--text`); the only flags are `-d`/`--duration`, `-p`/`--position`, `-s`/`--size` (no-`id`, `x`/`y`, or `fontSize` — those are desktop-overlay-only). Subcommands: `show` / `hide`.--```bash-adom-cli hydrogen caption show "Step 1: open the board" -p center -s large # 30s auto-expire-adom-cli hydrogen caption show "Step 2: run DRC" -d 60 -p center -s large # hold 60 SECONDS-adom-cli hydrogen caption hide # clear now-```--### ⚠️⚠️ `-d` / `duration` is in **SECONDS — NOT milliseconds** ⚠️⚠️-THE gotcha. `-d 30` = 30 seconds. `-d 4000` = **66 MINUTES**, not 4 seconds. (This bit-the build thread — `-d 4000` left a caption stuck on screen for over an hour.) If you're-thinking in ms you're off by **1000×**. You almost never need a big number — the 30s-default already covers "leave it up while I do a thing." (Note: the *desktop overlay* in-§2 uses **milliseconds** — the two surfaces differ; don't cross the units.)--> **Web-Hydrogen vs HD parity (checked live 2026-06-14):** the **command + the SECONDS-> unit + `hide` are identical** on both (verified by firing `adom-cli hydrogen caption` in-> a cloud web-Hydrogen workspace — center/large rendered, `-d 20` held visibly). BUT the-> sane **lifecycle below (30s default / 300s cap / persist) is verified on HD v0.1.34+**;-> web Hydrogen still returns the *older terse `_hint`* ("auto-hides after the specified-> duration") and may not enforce the 30s-default/300s-cap yet. **So on web Hydrogen, don't-> assume a fat `-d` self-heals — pass a sane duration.** (Web-Hydrogen backend sync is a-> Colby/carbon item.)--### Lifecycle (HD v0.1.34+, verified live 2026-06-14 — sane, never abandoned)-- **No `-d` (or `-d 0`) → auto-expires after 30s.** The right default — you do NOT need- to `hide` it.-- **Positive `-d <seconds>` → holds that many seconds, HARD-CAPPED at 300s (5 min).** A- larger value still clears at 5 min, so a fat-fingered `-d 4000` now self-heals.-- **Newest wins:** showing another caption replaces the current one; `hide` clears immediately.-- **No "forever" via adom-cli** — its `-d` is an unsigned int (rejects negatives) by- design. This is a **feature, not a gap**: adom-cli is the same binary on web-Hydrogen and- HD, so "no persist via adom-cli" keeps both consistent and bounded (no abandoned- captions). A persist-until-hidden escape exists ONLY on the raw HTTP endpoint- (`{"duration":-1}`, below) — rarely needed.-- **No `caption status` verb (yet).** There's no way to query "is a caption up / how long- left" from adom-cli — and with newest-wins + the 30s auto-expire you rarely need to. (A- read-only `caption status` is a possible future verb, not a current one.)-- `-p` position: `top` / `center` / `bottom` (NO corners — use the desktop overlay for- those). `-s` size: `small` / `medium` / `large`.-- Needs a live workspace/editor session (the server returns 409 if none).--### Direct HTTP fallback — only for scripts already hitting HD's API-adom-cli is the consistent default; use HTTP **only** when a script talks to HD's API-directly. It's the **same caption, same lifecycle** — adom-cli just wraps this path with-the workspace owner/repo. The caption lives on the **workspace-events server**-(`ADOM_HYDROGEN_URL`, the proxy) — **NOT the control port**:--```bash-# show — duration in SECONDS, identical body/lifecycle to adom-cli-curl -s -X POST "$ADOM_HYDROGEN_URL/api/workspaces/editor/<owner>/<repo>/current/caption" \- -H 'Content-Type: application/json' \- -d '{"text":"Step 1: open the board","duration":60,"position":"center","size":"large"}'-# hide-curl -s -X POST "$ADOM_HYDROGEN_URL/api/workspaces/editor/<owner>/<repo>/current/caption" \- -d '{"hide":true}'-# persist until hidden (HTTP-ONLY escape — adom-cli can't send a negative):-# -d '{"text":"Recording…","duration":-1}'-```-Body: `{text, duration (SECONDS), position, size}`; clear with `{hide:true}`. Returns the-same `_hint` as adom-cli (verified live).--## (2) Desktop overlay caption — `adom-desktop desktop_caption`--HD's built-in **Win32 always-on-top, click-through overlay** — floats over every-window, doesn't steal focus or intercept clicks (the user works "through" it). The-full-featured surface (ids, corners, x/y, force-clear). **Windows-only** (non-Windows-no-ops).--## Firing a caption--Source: `src-tauri/crates/hd-app/src/caption.rs` (`handle_caption`). The AD CLI verb is-**`desktop_caption`** (the internal message type is `caption`; `adom-desktop status`-lists the `caption` capability). Plain `adom-desktop caption` is NOT a verb — it errors.--```bash-adom-desktop desktop_caption '{"text":"Step 3: approve the UAC dialog →","position":"center","size":"large","duration":6000}'-```--### Arguments--| Arg | Type | Meaning |-|---|---|---|-| `text` | string | The caption text. Omit (with no `action`) to hide. |-| `id` | string | Caption identity. **Same id → replaces** that caption; **different ids → coexist.** Defaults to `_default`. |-| `position` | string | `top`, `bottom` (default), `center`, `top-left`, `top-right`, `bottom-left`, `bottom-right`. |-| `x` / `y` | float | **Normalized 0.0–1.0** screen coordinates (clamped). Use for precise placement instead of a named `position`. |-| `size` | string | `large` / `medium` (default) / `small`. |-| `fontSize` | int | Explicit pixel font height (also accepts `font_size`). Overrides `size`. |-| `duration` | int | **Milliseconds** on screen (default 4000), then a short fade-out. ⚠️ **NOTE: this surface is ms — the OPPOSITE of the §1 workspace caption, whose `-d` is SECONDS.** `4000` here = 4s; `4000` there = 66 min. |-| `action` | string | `hide` (dismiss this id, or all if id empty) or `force-clear` (destroy **all** captions regardless of id). |--### The id rule (this is the powerful part)--Captions are keyed by `id`. **Reuse an id to update a caption in place**; **use-different ids to show several at once.** That lets you run, e.g., a persistent-status caption in a corner *and* a moving center callout simultaneously:--```bash-# persistent status in the corner-adom-desktop desktop_caption '{"id":"status","text":"Recording…","position":"top-right","size":"small","duration":60000}'--# step callouts in the center, each replacing the last (same id)-adom-desktop desktop_caption '{"id":"step","text":"Step 1: open the board","position":"center","duration":4000}'-adom-desktop desktop_caption '{"id":"step","text":"Step 2: run DRC","position":"center","duration":4000}'--# clear just the step callout-adom-desktop desktop_caption '{"id":"step","action":"hide"}'--# nuke everything-adom-desktop desktop_caption '{"action":"force-clear"}'-```--## Patterns--- **Walkthrough / demo callouts** — drop a `center` caption per step (same `id` so- each replaces the last). Combine with a recording (see Related skills) so the- callouts end up in the finished video. Pair with TTS for narration.-- **Status corner** — a long-`duration` small caption in a corner (`top-right`),- separate `id`, while you drive the rest of the screen.-- **Point at a thing** — use normalized `x`/`y` to sit a label next to a button or- dialog you're about to act on.-- **Cleanup** — `action:"hide"` per id when a step is done; `action:"force-clear"`- to wipe the screen of all captions at the end of a tour.--## Related skills-- [hd-recording](../hd-recording/SKILL.md) — captions are captured *into* screen recordings; record a walkthrough with live callouts-- [hd-adom-desktop](../hd-adom-desktop/SKILL.md) — AD carries the `caption` / `desktop_caption` verb to the host; verify the `caption` capability via `adom-desktop status`-- `adom-tts` — *audio* narration to pair with on-screen captions (captions are visual-only)
skills/hd-capture-share/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-capture-share/SKILL.mddeleted−148@@ -1,148 +0,0 @@-name: hd-capture-share-description: >- The in-HD screen-share + AI-consent surface that lets YOU (the AI) screenshot- the editor or the whole screen into the container. Covers the Screen Capture- dropdown in the editor toolbar (Share this tab / Share entire screen / Stop- sharing, plus Workspace / Full-screen screenshot), the Webview-vs-Pup Window- mode toggle, the panel/workspace/screen capture scopes + Element-Capture- RestrictionTarget, and the AI sharing-approval flow: SharingApprovalDialog- ("AI wants access" + your reason), the ~3s auto-approve in HD, and the- cancelable CountdownToast before capture starts. READ THIS before trying to- screenshot the editor — a screenshot of HD's own UI requires an active share.- Trigger words — screen share, share this tab, share screen, screen capture- dropdown, capture the editor, screenshot the workspace, workspace screenshot,- AI wants access, sharing approval, consent dialog, countdown toast, start- sharing, stop sharing, window mode, webview vs pup, RestrictionTarget, element- capture, capture scope, panel screenshot, capture frame, share with the AI.--# Hydrogen Desktop — Screen Capture & Sharing (the AI-consent surface)--This is the surface **you (the AI) trigger to see HD's own screen.** To screenshot-the *editor* (panels, the workspace, or the whole monitor) into the container, a-**screen share must be active first.** You request it, the user consents once (HD-auto-approves in ~3s), a brief countdown plays, then you can capture frames.--> This is **not** the same as host OS screenshots. `desktop_screenshot_*`-> (see [hd-adom-desktop](../hd-adom-desktop/SKILL.md)) take true OS screenshots via-> AD with zero dialogs. This skill is the **in-webview** share that captures what-> HD itself is rendering (the editor) via the browser's `getDisplayMedia`/Element-> Capture, gated by an in-app consent dialog. And it is distinct from-> [hd-permissions](../hd-permissions/SKILL.md) (WebView2 auto-granting mic/cam OS-> permissions) — that's a different layer.--## The Screen Capture dropdown (editor toolbar)--Source: `src/lib/components/editor/EditorNav.svelte` (~L2227–2332).-Store: `src/lib/stores/screenCaptureStore.ts`.--**When not yet sharing**, the dropdown offers two ways to start:--| Option | Description | What you get |-|---|---|---|-| **Share this tab** | "Panels and workspace screenshots" | Captures HD's own webview — panels + workspace |-| **Share entire screen** | "Full display including other apps" | Captures the whole monitor (or a chosen window) |--**While sharing**, it shows `Sharing <surface>` ("entire screen" / "window" / "this-tab") plus capture actions:--| Action | Description |-|---|---|-| **Workspace screenshot** | Save to container's `screenshots` folder |-| **Full screen screenshot** | Save to container's `screenshots` folder (only when the surface is a monitor/window) |-| **Stop sharing** | Ends the share |--### Window mode toggle (Webview vs Pup)--Below the share options is a **Window mode** toggle with two buttons —-**Webview** and **Pup** (`windowModeStore`). It selects which window surface-capture/recording targets: HD's own embedded Webview, or a Pup (Puppeteer)-browser window. Webview = "see HD itself"; Pup = "see the AI-driven browser."--## Capture scopes & how a frame is taken--`screenCaptureStore.ts` exposes the scope type and the capture functions:--```ts-type CaptureScope = 'screen' | 'workspace' | 'panel';-```--| Scope | Function | What it captures |-|---|---|---|-| `panel` | `capturePanel(panelId)` | a single editor panel |-| `workspace` | `captureWorkspace()` | the whole HD workspace surface |-| `screen` | `captureScreen()` | the full display / shared window |--Other store entry points you'll see: `startTabCapture()`, `startScreenCapture()`,-`stopScreenCapture()`, `captureFrame()`, `isCaptureActive()`,-`promptScreenCapture(message?)` (asks the user to enable sharing).--State shape: `{ isSharing, displaySurface, hasElementCapture, promptMessage }`,-where `displaySurface` is `'browser'` (tab) / `'window'` / `'monitor'`.--### Element Capture (RestrictionTarget)--When `'RestrictionTarget' in window` and `getDisplayMedia` are available, HD can-**restrict a tab capture to a single DOM element** so only that panel is in frame:-`restrictCaptureTo(el)` builds a `RestrictionTarget.fromElement(el)` and calls-`track.restrictTo(target)` (a short ~3s settle window). This is how a panel-scoped-capture yields just the panel, not the whole webview.--> Native (Tauri) note: inside HD, tab/screen snapshots come from the native CDP-> path rather than a live `MediaStream`; the store still flips `isSharing` and sets-> `displaySurface` to `'window'`/`'monitor'` so the same UI flow applies.--## The AI sharing-approval flow--When you (or a bridge) need screen/mic access, HD shows a consent gate before any-capture. Source: `src/lib/components/editor/SharingApprovalDialog.svelte`,-store `src/lib/stores/sharingApprovalStore.ts`.--The request you send carries:--```ts-{ bridgeKey, reason, shareType: 'tab' | 'screen' | null, audio: boolean }-```--- The dialog header reads **"AI wants access"** and shows **your `reason`** in a- highlighted box. **Send a clear, specific reason** — it's the only thing the user- sees to decide. ("Screenshot the schematic panel to verify the layout," not- "capture screen.")-- **HD auto-approves after ~3 s** (`AUTO_MS = 3000`), showing- "Auto permission will be given in N…". This is the HD-only fast path so you're- not blocked on hands-free flows.-- **The user can override before it auto-fires:** flip share-type between- **Share this tab** and **Share entire screen**, toggle the **mic**, or pick a mic- device. Any interaction cancels the auto-countdown so they stay in control.-- On approve, HD starts the screen share first (tab or screen), then mic if- requested, and returns a result (`status: 'approved'`, with `sharing` + `mic`- details) to the requesting bridge.--### CountdownToast — the cancelable lead-in--Source: `src/lib/components/editor/CountdownToast.svelte`. Before capture/recording-actually starts, a countdown toast ("Recording in N…", default 3) plays, optionally-showing the sources and reason. It has a **cancel (×)** — the user can abort right-up to the last second. Don't assume capture started the instant you requested it;-the toast is a real, abortable delay.--## What this means for you (the AI)--1. **To screenshot HD's editor you need an active share.** If you're not sharing,- call `promptScreenCapture(...)` / send a sharing request with a reason; don't- silently fail a `capturePanel`/`captureWorkspace`.-2. **Write the reason for a human.** It's your one chance to justify the access.-3. **Respect the override + countdown.** The user may switch share-type, deny mic,- or cancel the countdown — read the returned result before claiming success.-4. **Pick the right scope:** `panel` (one panel, Element-Capture-restricted),- `workspace` (the HD surface), or `screen` (full display). Use Window mode- (Webview vs Pup) to choose HD's own UI vs the AI browser.-5. **For host-OS screenshots that need no share at all,** use AD's- `desktop_screenshot_*` instead (see Related skills).--## Related skills-- [hd-recording](../hd-recording/SKILL.md) — screen/mic recording (same share + countdown machinery; in-app .webm recorder + AD desktop recording)-- [hd-self-screenshot](../hd-self-screenshot/SKILL.md) — CDP per-panel screenshots + shotlog visual-verify loop-- [hd-permissions](../hd-permissions/SKILL.md) — WebView2 auto-granting OS mic/cam/notification permissions (a *different* layer from this consent dialog)-- [hd-adom-desktop](../hd-adom-desktop/SKILL.md) — AD's dialog-free host OS screenshots (`desktop_screenshot_screen/_window`)
skills/hd-claude-auth/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-claude-auth/SKILL.mddeleted−86@@ -1,86 +0,0 @@-name: hd-claude-auth-description: How Claude Code authenticates inside HD — the OAuth flow that opens in your browser via the Browser Picker, where credentials are stored on host vs container, how HD backs them up so a container rebuild doesn't make you re-auth, the virgin-reset toggle that wipes Claude creds, and the 4-technique auth cascade HD runs if Claude isn't authenticated. READ when troubleshooting "Claude not authenticated", expired Claude tokens, re-authenticating after a virgin reset, or wondering where Claude credentials live. Trigger words — claude auth, claude code login, claude code authentication, claude not authenticated, claude oauth, claude credentials, claude session, claude.ai subscription, replay-claude-credentials.json, claude-auth, claude credential backup, claude re-auth, claude expired, claude.ai subscription button.--# HD Claude Auth — how Claude Code signs in--Claude Code (the VS Code extension inside your HD workspace) authenticates separately from your Adom login. HD takes responsibility for making sure Claude is signed in throughout workspace rebuilds so you don't have to re-authenticate every time.--For Adom auth (your hydrogen.adom.inc login), see [hd-adom-auth](../hd-adom-auth/SKILL.md). This skill is Claude-Code-specific.--## The flow--1. The Claude Code extension panel shows a "Claude.ai Subscription" button when it isn't authenticated.-2. Clicking opens an OAuth URL to `claude.ai`.-3. HD's Browser Picker intercepts the URL (since it's a `*.claude.ai` link). The "Claude auth" branded variant of the picker appears with the work browser profile pre-selected and a 5-second auto-countdown.-4. The browser opens the Claude OAuth page; you complete sign-in (or it auto-completes if you're already signed in to claude.ai).-5. Claude returns OAuth credentials, which the extension stores at `/home/adom/.claude/.credentials.json` inside the workspace.-6. HD's setup step `claude-auth` notices the new credentials and **backs them up to the host** at `%APPDATA%\hydrogen-desktop\replay-claude-credentials.json` so future workspace rebuilds can replay them.--## Where Claude credentials live--| Layer | Path | Purpose |-|-------|------|---------|-| Inside workspace (live) | `/home/adom/.claude/.credentials.json` | Active credentials the extension and `claude` CLI both read |-| Host-side backup | `%APPDATA%\hydrogen-desktop\replay-claude-credentials.json` | Backup so a fresh workspace can be re-authenticated without re-OAuth |-| VS Code secret store (in code-server's user data) | webview-internal, can't be inspected directly | The extension also keeps a secret per panel; this is what dictates the chat UI showing vs the auth button |--**Key insight**: each Claude Code panel instance keeps its own auth state. If one panel says "not authenticated", opening a NEW Claude Code conversation panel often shows the auth button fresh and lets you sign in cleanly.--## How HD keeps you signed in across workspace rebuilds--When you virgin-reset and rebuild the workspace (assuming you DON'T check the "Claude credentials" toggle):--1. Workspace deleted + recreated by setup steps-2. Step `claude-auth` runs early — reads `replay-claude-credentials.json` from `%APPDATA%`-3. If the file exists AND the `expiresAt` is still in the future → copies it into the new workspace at `/home/adom/.claude/.credentials.json`-4. Claude Code extension reads it on first launch → user stays signed in--If credentials are expired or missing, HD runs the **auth cascade** to get you signed in without manual fiddling.--## The 4-technique auth cascade--If Claude isn't authenticated after setup, HD tries each technique in order until one works. Each call logs to the setup panel output so you see exactly what's happening.--| # | Technique | What it does |-|---|-----------|--------------|-| 1 | Detect auth failure | Scans all VS Code webview contexts for the "Claude.ai Subscription" button / "How do you want to log in?" text. Confirms whether the auth UI is reachable. |-| 2 | Open NEW conversation + auto-click auth button | The new-conversation panel re-evaluates auth state and shows the button cleanly. HD clicks it via CDP. |-| 3 | Open Claude editor + auto-click auth button | If technique 2 didn't trigger a fresh auth state, opens a Claude editor (different VS Code command) and tries the same click. |-| 4 | CLI fallback (`claude auth login`) | Runs `claude auth login` inside the workspace, extracts the OAuth URL from stdout, opens it in your browser via the Browser Picker. You paste the returned code into a VS Code terminal. |--After authentication, the new credentials are immediately backed up to the host via step 6 of the flow above.--## Virgin reset — what each option does--The setup panel's Virgin Reset section has a per-Claude-creds toggle:--| Toggle | What happens to Claude auth | When to use |-|--------|----------------------------|-------------|-| **Claude credentials** (default OFF) | Deletes `replay-claude-credentials.json` from host AND the credentials inside the workspace. **You will need to re-OAuth.** | When Claude is stuck in a broken auth state and you want a true fresh sign-in. |-| **Workspace** (default ON) | The workspace is rebuilt. If Claude creds toggle is OFF (default), `claude-auth` replays the backup → you stay signed in. | Normal virgin reset for testing. |-| **Workspace data** (default ON) | Wipes your `/home/adom/project/` work. Doesn't directly affect Claude creds, but rebuilds the workspace — same as above. | When you want a totally fresh `~/project/`. |--**Standard pattern for AI-driven testing**: keep the "Claude credentials" toggle OFF so testing doesn't bounce you out of Claude every cycle.--## Common failures--**"Claude.ai Subscription" button keeps appearing even though I just signed in**-- A SECOND panel still has stale auth state. Close all Claude Code panels and open one fresh — it'll read the new credentials.--**Credentials backup file exists but Claude says expired**-- Open `%APPDATA%\hydrogen-desktop\replay-claude-credentials.json` and check the `expiresAt` (epoch ms). Compare to current time. If past, you need to re-OAuth. HD's auth cascade will handle this on next setup run.--**`claude auth status` says `loggedIn: true` but the extension says not authenticated**-- **Known quirk**: the CLI and the VS Code extension use SEPARATE credential stores. `claude auth status` checks the CLI store; the extension uses VS Code secrets. Trust the extension UI (the "Claude.ai Subscription" button) over the CLI's status output.--**OAuth callback fails / browser shows "connection refused"**-- The callback URL is hitting a localhost port that HD hasn't bound on Windows. Check the HD log for `[oauth] Callback port N detected` — HD should auto-start a proxy for it. If not, register the port manually via `adom-cli port-forward register <port> --visibility external`.--## Related skills--- [hd-adom-auth](../hd-adom-auth/SKILL.md) — Adom (hydrogen.adom.inc) login, separate from Claude-- [hd-browser-picker](../hd-browser-picker/SKILL.md) — the Browser Picker that routes the Claude OAuth URL, including the branded "Claude auth" variant-- `hd-setup-steps` — the `claude-auth` step and the auth cascade are part of setup (the runtime-specific hd-setup-steps shows where it falls in the cascade)-- `hd-volume` — explains why the host-side backup survives workspace rebuild
skills/hd-claude-management/SKILL.mddeleted−214@@ -1,214 +0,0 @@-name: hd-claude-management-description: >- First-class API for managing the Claude Code conversations inside Hydrogen Desktop:- list/open/close/switch conversation tabs, read each conversation's full state (title,- message count, compose box, and the on-disk session GUID), type messages into a SPECIFIC- conversation (instant or with a human typing animation) and submit them, and turn on- Remote Control to get a claude.ai cloud URL so the user can view+drive that exact- conversation from claude.ai or the Claude mobile app. Every conversation is addressed by a- stable execution-context id (deterministic, background, no foreground/focus needed). Also- explains how a live conversation is matched to its ~/.claude/projects/*.jsonl log.- Trigger words: claude tabs, claude conversations, list conversations, open claude tab,- close claude tab, switch claude tab, new conversation, type into claude, inject prompt,- remote control, claude.ai url, view on mobile, session id, conversation guid, jsonl,- claude session, context_id, multi-tab claude, conversation management.--# HD Claude Conversation Management--Hydrogen Desktop runs Claude Code as webview tabs inside VS Code. This skill is the-**first-class API** for managing those conversations programmatically — from HD's control-API, from the user's AI inside the WSL2 workspace, or from setup steps.--All endpoints are on the **HD control API** (a dynamic localhost port). From inside the-workspace, examples below use `$HD` for `http://127.0.0.1:<port>` (find the port in the HD-log / port discovery). From an **external** context (a cloud container), call the same-endpoints with the AD verb `adom-desktop hd_api '{"path":"/claude/…","method":"POST","body":{…}}'`-— it auto-discovers the port. Everything here works in the **background** — no window focus,-no foreground, no keystroke stealing.--## The core idea: address conversations by CONTEXT ID, list from the TAB BAR--Each Claude conversation is its own webview **execution context** with a **stable integer id**.-That id — not the tab index, not the title, not visibility — is how you reliably *target* one-conversation (type into it, remote-control it). VS Code keeps multiple webviews "visible" at-once, so visibility/tab-index are NOT reliable addresses; the context id is.--But VS Code also **disposes the webview of a background tab**, so a context id only exists for-*loaded* tabs. That's why `GET /claude/conversations` **merges three sources** into one view:-the **tab bar** (every open conversation + which is active — what you see), the **live webview-contexts** (rich data for loaded tabs), and the **on-disk session logs** (the GUIDs). Each row-carries a `loaded` flag; `loaded:false` means activate the tab first to read its live details.--## GET /claude/conversations — everything in one call--```bash-curl -s $HD/claude/conversations-```-```jsonc-{- "ok": true,- "count": 4,- "conversations": [- {- "ordinal": 3, // its position in the tab bar- "active": true, // the currently-focused tab- "loaded": true, // webview is live → rich fields are populated- "context_id": 7, // STABLE address for inject/remote-control (null if not loaded)- "title": "Calculate 10 times 10", // authoritative title (from the session log when matched)- "tab_label": "Calculate 10 times 10", // raw tab label (may be ellipsis-truncated)- "compose_text": "", // what's typed but not yet sent (null if not loaded)- "msg_count": 16, // null if not loaded- "session_id": "566cec21-…", // on-disk log GUID (read ~/.claude/projects/*/<id>.jsonl)- "rc_active": false, // Remote Control on? (null if not loaded)- "remote_control_url": null // claude.ai cloud URL when rc_active- },- { "ordinal": 1, "active": false, "loaded": false, "context_id": null,- "title": "Find the capital of France", "tab_label": "Find the capital of Fran…",- "compose_text": null, "msg_count": null, "session_id": "b352055b-…", "rc_active": null, "remote_control_url": null }- ]-}-```-- **Merged view:** every tab is listed (from the tab bar). `loaded:true` rows have live fields- (`context_id`, `compose_text`, `msg_count`, `rc_active`); `loaded:false` rows are background tabs- VS Code disposed — **activate them** (`/claude/tabs/activate`) to read those. Even a disposed tab- resolves its `session_id` when its title matches the on-disk log.-- A conversation is **`Untitled`** with **`session_id: null`** until its first message is submitted —- then Claude names it and writes the session log.-- `?with_session=false` skips the disk read (faster; omits `session_id`).--`GET /claude/contexts` is the lighter version (just the *loaded* contexts: `context_id` + `compose_text`).--## Open / close / switch tabs--```bash-# Open a fresh conversation (new tab)-curl -s -X POST $HD/claude/new-conversation--# Open the Claude panel if it isn't open-curl -s -X POST $HD/claude/open--# List raw editor tabs (label/active/dirty)-curl -s $HD/claude/tabs--# Close ALL conversation tabs (also removes the leftover empty editor group)-curl -s -X POST $HD/claude/tabs/close-all--# Switch the visible tab by index or label substring (background)-curl -s -X POST $HD/claude/tabs/activate -d '{"index":1}'-curl -s -X POST $HD/claude/tabs/activate -d '{"label":"Eiffel"}'--# Remove a stale EMPTY editor group (the dead blank panel) WITHOUT collapsing a-# real 2-group left/right layout. (merge-groups is a back-compat alias.)-curl -s -X POST $HD/claude/tabs/close-empty-groups-```--## Type a message into a SPECIFIC conversation--`POST /claude/inject` types into the conversation you name with `context_index`. Two speeds:--```bash-# INSTANT paste (fast) — no typing animation. Best for slash commands and scripted input.-curl -s -X POST $HD/claude/inject -d '{"text":"summarize this repo","context_index":16,"typed":false,"submit":true}'--# SLOW human typing animation (the "watch the AI type" effect), then submit.-curl -s -X POST $HD/claude/inject -d '{"text":"hello there","context_index":16,"typed":true,"delay_ms":50,"submit":true}'--# Type but DON'T send (leave it sitting in the compose box):-curl -s -X POST $HD/claude/inject -d '{"text":"draft…","context_index":16,"typed":false,"submit":false}'-```-- `typed`: `true` = char-by-char with ±50% jitter (avg `delay_ms`); `false` = instant paste.-- `submit`: `true` = press Enter; `false` = leave in the box.-- Omit `context_index` ONLY when a single conversation is open. With **more than one** conversation- open, omitting it can type your text into **several compose boxes at once** (it falls back to a- visibility heuristic that isn't reliable when the window isn't foreground). When in doubt, pass it.-- Targeting by `context_index` is deterministic — it lands in exactly that tab even when it's- not the foreground one and even with many tabs open (no fan-out into other tabs). **This is the- safe default: list `/claude/conversations`, take the row with `active:true` (or the tab you want),- use its `context_id`, then inject.**--Typical "open a new tab and ask a question":-```bash-curl -s -X POST $HD/claude/new-conversation-CTX=$(curl -s $HD/claude/contexts | jq '.contexts[-1].context_id')-curl -s -X POST $HD/claude/inject -d "{\"text\":\"what does this project do?\",\"context_index\":$CTX,\"typed\":true,\"submit\":true}"-```--## ⭐ Remote Control → view the conversation on claude.ai / mobile--This is the big one. Turn on Remote Control for a conversation and HD returns a **claude.ai-cloud URL**. Open it on claude.ai in any browser or in the Claude **mobile app** to see and-drive that exact conversation in the cloud — not tied to HD. The user can be on their phone-editing the same conversation.--```bash-curl -s -X POST $HD/claude/remote-control -d '{"context_index":7}'-```-```jsonc-{- "ok": true,- "rc_active": true,- "toggled": true, // false + already_in_desired_state:true if it was already on- "context_id": 7,- "remote_control_url": "https://claude.ai/code/session_014koKPJxmjYpnX2SjRRqrD3"-}-```-- ⚠️ **`/remote-control` is a TOGGLE.** This endpoint **detects** the current state first and only- toggles when needed — re-calling it on an already-on session is a **no-op** that returns the- existing URL (it will NOT turn it off; doing that blindly archives the cloud session).-- `{enable:false}` turns Remote Control **off**.-- The `session_…` id in the URL is the **cloud** session id — distinct from the local on-disk- `session_id` GUID. The cloud URL is the shareable one for claude.ai / mobile.-- `{name}` → `/remote-control <name>` to label the remote session.-- Each conversation's current state is also reported as `rc_active` in `/claude/conversations`.-- **Opening it on the user's machine (external/cloud agent):** get the URL, then open it in their- Chrome and screenshot to confirm — all via AD verbs (no synthetic keystrokes):- `desktop_find_window {titleContains:"Claude Code - Google Chrome"}` → `desktop_screenshot_window- {hwnd}`. To type INTO the claude.ai page, use the native-browser bridge `nbrowser_eval- {tabId, expression}` (the composer is shadow-DOM; do NOT blind-type with SendInput). See the- `driving-hd-remotely` guide on the Hydrogen Desktop wiki page.--## How a live conversation maps to its on-disk log (JSONL)--Claude Code stores each conversation as `~/.claude/projects/<cwd>/<uuid>.jsonl` containing-`sessionId`, `aiTitle` (the generated title), and every message. **The session GUID is NOT-exposed in the conversation's webview DOM**, so HD correlates a live conversation to its log by-matching the **first user message** (exact + unique; present in both the DOM and the JSONL).-On a match, `/claude/conversations` fills in `session_id` and uses the log's `aiTitle` as the-authoritative `title`. (Matching on the title alone is unreliable — the rendered title and the-persisted `aiTitle` can differ.)--Read a conversation's full transcript from the workspace:-```bash-cat ~/.claude/projects/*/<session_id>.jsonl-```--## Why this is valuable--- **Multi-conversation orchestration:** open N conversations, drive each independently by- context id, read each one's state — all from a script or an agent, in the background.-- **Cloud + mobile continuity:** Remote Control hands you a claude.ai URL so a conversation- started in HD continues on the phone / web. Huge for working away from the desk.-- **Deterministic + safe:** context-id addressing never types into the wrong tab; the empty- blank panel is cleaned up automatically; a real 2-group left/right layout is preserved.-- **Auditable:** every conversation maps to an on-disk JSONL you can read, diff, or archive.--## Endpoint quick reference--| Endpoint | Method | What it does |-|---|---|---|-| `/claude/conversations` | GET | Full per-conversation data incl. `session_id` (the canonical list) |-| `/claude/contexts` | GET | Light list: `context_id` + `compose_text` |-| `/claude/tabs` | GET | Raw editor tabs (label/active/dirty) |-| `/claude/new-conversation` | POST | Open a fresh conversation tab |-| `/claude/open` | POST | Open the Claude panel if closed |-| `/claude/tabs/close-all` | POST | Close all tabs + remove the leftover empty group |-| `/claude/tabs/activate` | POST | Switch visible tab by `{index}` or `{label}` |-| `/claude/tabs/close-empty-groups` | POST | Remove stale empty editor groups (preserves populated) |-| `/claude/inject` | POST | Type into `{context_index}` — `{typed,submit,delay_ms}` |-| `/claude/remote-control` | POST | Turn on RC for `{context_index}` → returns `remote_control_url` |-| `/claude/cross-eval` | POST | (Diagnostic) run JS across all workbench/webview contexts |--These are the same endpoints HD's own setup steps use to open Claude and inject the first-prompt — so behavior is identical whether a human, setup, or the workspace AI drives them.
skills/hd-container-stats/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-container-stats/SKILL.mddeleted−134@@ -1,134 +0,0 @@-name: hd-container-stats-description: HD's workspace stats indicator in the top-right of the title bar — two thin progress bars (CPU + RAM) labelled "Container" plus an info tooltip on hover that shows the workspace name, CPU vs total cores, RAM usage vs limit, disk usage, image, ID, and creation date. Polls once per second when running, every 5 seconds when stopped. Status badges show stopped / starting / restarting / restart-needed states. Use this skill when the user asks about CPU/RAM usage, the resource bars in HD's title bar, workspace disk usage, the "restart needed" badge, why bars are red/yellow, or what's in the tooltip. Trigger words — container stats, workspace stats, container cpu, container ram, container disk, resource bars, cpu bar, ram bar, container tooltip, container indicator, top bar stats, restart needed badge, container stopped badge, container starting, workspace_stats, workspace stats.--# HD Workspace Stats Indicator--The top-right of HD's title bar (next to the audio/video icons and your profile avatar) shows a tiny `Container` block with two horizontal progress bars — CPU% and RAM% — that animate in real-time. Hovering brings up a tooltip with the full workspace details. The numbers reflect your **local HD workspace**; the header reads "Container" on all platforms.--> Platform layers add a `*-windows` companion ([[hd-container-stats-windows]]) with the host-specific probe — the WSL `workspace_stats()` health check, `wsl --terminate` restart, the benign `[docker]` log line, and distro disk semantics.--## How often it polls--There's an active polling loop in HD's title bar that asks the runtime for fresh stats on a timer:--- **Every 1 second** when the workspace is `running` — for live, smooth bar animation-- **Every 5 seconds** when it's `stopped`, `starting`, `restarting`, or `restart needed` — saves cycles when nothing's changing--Each poll calls Tauri `invoke('get_container_stats')`, which dispatches through the runtime trait (`WorkspaceRuntime::get_stats`) to the active host's probe. The probe runs with short timeouts so a hung host subsystem can't freeze the UI.--If you ever want to lighten the load (e.g. on a battery-constrained laptop), the relevant code is in `EditorNav.svelte`'s `pollStats()` closure — adjust the `pollInterval` ternary.--## What the bars show--| Bar | What it measures |-|-----|------------------|-| CPU | % of the workspace's available CPU being consumed (1.0 = 1 full vCPU). Capped at 100% display |-| RAM | % of the workspace's memory in use vs its limit |--Both bars share the same color-threshold rules:-- **Teal / green** — ≤ 50% usage (normal)-- **Yellow** (`warn` class) — 51-80% usage-- **Red** (`danger` class) — > 80% usage--## What the tooltip shows--When you hover over the bars, a tooltip appears with:--| Line | Example |-|------|---------|-| Container | the workspace name |-| CPU + RAM totals | `CPU: 1.7% of 16 cores | RAM: 0.7 GB / 16.0 GB` |-| Disk | `Disk: 1.3 GB` (workspace disk usage) |-| Image | `Image: Ubuntu 24.04` |-| ID | the registered workspace identifier |-| Created | `5/24/2026, 3:18:09 PM` (locale-formatted) |--If the workspace is in a non-healthy state, the tooltip shows a single red line instead:-- `Workspace is stopped — start from the menu` (when stopped)-- `Workspace runtime is broken — restart from the menu` (when the workspace / code-server is unreachable, e.g. after sleep/hibernate)--## Status badge in the header--The `Container` text in the section header gets an inline status badge depending on state:--| Badge | Meaning | Color |-|-------|---------|-------|-| (no badge) | Running normally | normal |-| `stopped` | Workspace registered but not running | red |-| `starting` | HD is starting the workspace / code-server | yellow |-| `restarting` | HD is restarting the workspace | yellow |-| `restart needed` | The workspace or code-server is unreachable (usually a hiccup after the host wakes from sleep) | red |--When `restart needed` shows, open the Adom menu → Restart Workspace (see the `[[hd-ui]]` skill for the Adom menu). A "Restart" here terminates and re-ensures the workspace + code-server.--## How it works under the hood--- **Polling frequency**: see "How often it polls" above — 1s when running, 5s when not.-- **Backend command**: Tauri `invoke('get_container_stats')` → `WorkspaceRuntime::get_stats`, routed to the active host runtime's probe.-- **Two health levels** the probe checks: (1) is the host runtime itself available; (2) is the workspace registered + running with code-server reachable on host port 7380. If either fails, the probe returns a needs-restart signal instead of stats and the UI shows the red "restart needed" badge.-- **Rate-limited error logging**: the "needs restart" log line is printed at most once every 30 seconds, even though the frontend polls every 1s, so the log stays readable.--The authoritative "which runtime am I on" log line is `[app] Active workspace runtime: …`. (Platform-specific log signals — e.g. the benign Windows `[docker]` heartbeat — are covered in `[[hd-container-stats-windows]]`.)--## API access--If you want the same stats data your AI can hit directly via the control API:--```bash-# From inside the workspace — query the control API on the host-curl -s "$(cat ~/.adom/hd-control-url)/container-stats"-```--(Read the live URL from `~/.adom/hd-control-url` rather than hardcoding it — the port is **dynamic per launch**.)--Returns JSON like:-```json-{- "ok": true,- "container_name": "Adom-Workspace",- "container_id": "Adom-Workspace",- "container_created": "2026-05-24T20:18:09Z",- "image": "Ubuntu 24.04",- "cpu_percent": 1.7,- "cpu_cores": 16,- "mem_usage_mb": 716,- "mem_limit_mb": 16384,- "mem_percent": 4.4,- "disk_usage_mb": 1300-}-```--Or if the workspace is in a broken state:-```json-{ "ok": false, "needs_restart": true, "error": "Workspace needs restart" }-```--## CSS selectors--| Element | Selector |-|---------|----------|-| The whole indicator block | `.resource-bars` |-| Header with title + status badge | `.resource-bars-header` |-| Title text ("Container") | `.resource-bars-title` |-| One bar row (CPU or RAM) | `.resource-bar-item` |-| Bar fill | `.resource-bar-fill` (modifier classes: `.warn`, `.danger`) |-| Bar percentage label | `.resource-bar-value` |-| Hover tooltip container | `.resource-bars-tooltip` |-| One tooltip line | `.resource-bars-tooltip-row` |--## Common questions--**"Why is CPU showing 200% / 1600%?"** It's not — the displayed % is capped at 100 for the bar fill, but the tooltip shows the real number. CPU of `100% of 16 cores` means 1 full vCPU pegged.--**"Why does RAM stay constant at ~4% even when I'm doing nothing?"** Code-server, the relay, the adom-vscode HTTP API, etc. all run in the workspace all the time. ~700 MB baseline is normal.--**"The 'restart needed' badge appeared — what happened?"** Almost always a hiccup after the host woke from sleep/hibernate, leaving the workspace or code-server unreachable. Click Restart Workspace in the Adom menu (top-left); HD restarts the workspace, re-ensures code-server, and the badge clears.--**"Disk is 1.3 GB — does that include my project files?"** Yes — `/home/adom/project` lives inside the workspace, so the `Disk:` figure includes your project.--## Related skills--- `[[hd-workspace-monitoring]]` — the 15s state poll + floaty states behind these badges-- `[[hd-container]]` — workspace context: relay, bridges, env vars
skills/hd-container/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-container/SKILL.mddeleted−131@@ -1,131 +0,0 @@-name: hd-container-description: Context for Claude Code running inside a Hydrogen Desktop workspace. Documents the exact OS (Ubuntu 24.04, code-server, the local workspace), explains how setup differs from Adom cloud containers (HD setup-steps NOT bootstrap.sh), what bridges are available, and how to use the relay. Trigger on startup, adom-cli errors, bridge commands, screenshot requests, container-platform questions, code-server / VS Code extension issues, or when the AI needs concrete facts about its environment instead of guessing.--# Hydrogen Desktop Workspace--You are running inside the **local workspace** managed by Hydrogen Desktop (HD). HD is the flagship Adom desktop app — it manages your workspace, bridges to desktop apps, and provides VS Code + Claude Code. HD runs on Windows, macOS, and Ubuntu; the platform-specific mechanics of how the workspace is hosted are abstracted away from inside the workspace — the facts below hold regardless of host OS.--> Platform layers add a `*-windows` companion to this skill (`[[hd-container-windows]]`) with the host-specific spine — the WSL2 `Adom-Workspace` distro, `wsl --import`, code-server port auto-forward, the runtime source, and the Cloud-vs-local comparison table.--## The workspace — exact facts (don't guess)--| Field | Value |-|-------|-------|-| Provisioned from | a pre-baked golden image (full image, not bootstrapped from scratch) |-| Base | Ubuntu 24.04 |-| Architecture | matches host (`dpkg --print-architecture`) — `linux-x64` or `linux-arm64` |-| code-server | pinned via the golden image |-| code-server binary | `/usr/lib/code-server/bin/code-server` |-| Extensions dir | `~/.local/share/code-server/extensions/` |-| Settings | `~/.local/share/code-server/User/settings.json` |-| Work user | `adom` (passwordless sudo, home `/home/adom`) |-| Workspace | `/home/adom/project` |-| Pre-installed | Node 18 + npm, Python 3.12, git, gh, curl, wget, jq, build-essential, cmake, pkg-config, libssl-dev |-| Code-server runs as | `code-server --bind-addr 0.0.0.0:7380 --auth none --disable-telemetry /home/adom/project` |--If you ever need to confirm: `cat /etc/os-release`, `uname -m`, `/usr/lib/code-server/bin/code-server --version`. Don't speculate about alpine/arm64 — Ubuntu 24.04 matches the cloud container.--## Setup is via HD's setup-steps, NOT the legacy bootstrap.sh--HD provisions this workspace through its own **setup-steps** flow, shown as the "Install Tools" panel in the HD UI. It is a **different code path** than cloud containers, which run the legacy `scripts/bootstrap.sh`.--Most tooling is NOT installed by a step — it's BAKED into the golden image at image-build time (the Adom CLIs, the adompkg skills, the claude CLI + Code extension, code-server, VS Code settings, and all `hd-*` skills). The provisioning step imports the golden image if the workspace isn't already registered, and the relay is started by the `start-relay` step. See `[[hd-setup-steps]]`.--When debugging install issues here, the source of truth is HD's setup-steps code — its step IDs (`ensure-workspace`, `install-adom-vscode`, `set-env-vars`, `inject-api-key`, `start-relay`, `claude-auth`, etc.) map 1:1 to what ran on this workspace. There is NO `install-claude-ext` / `install-claude-cli` / `install-skills` / `write-vscode-settings` step — those are baked. Do NOT chase bugs into `bootstrap.sh` — that script lives in the baked image but HD itself never invokes it.--## VS Code extension caveats--- The Claude Code extension and `code-server` settings (including `extensions.autoUpdate: true` / `extensions.autoCheckUpdates: true`) are BAKED into the golden image — there is no `write-vscode-settings` or `install-claude-ext` setup step. Auto-update **must** stay on so every installed extension auto-updates from the marketplace; the image bakes it on. Do not regress this.-- Bare `code-server --install-extension <id>` (no version pin) historically resolves to the **universal** target-platform build. For some extensions (notably `anthropic.claude-code` after 2.1.89), Anthropic stopped shipping universal builds — so bare install permanently sticks at 2.1.89 even when newer per-arch builds exist. The golden-image bake installs the latest per-arch build pinned, so the workspace ships current. It also heals orphan `extensions.json` entries (entries pointing to deleted dirs cause a permanent "Invalid extensions detected" banner).-- Marketplace: code-server uses **Open VSX** (open-vsx.org), not Microsoft Marketplace. No `EXTENSIONS_GALLERY` env override is set; `product.json` has no gallery URL — it falls through to Open VSX defaults.--## adom-cli is authenticated--HD auto-injects the user's session token on every launch. To verify:-```bash-adom-cli carbon user get-```-If this returns user data, all adom-cli commands work (containers, repos, orgs, wiki, etc.). If it fails, the user needs to log in via HD's login page.--## Relay server is running--The adom-desktop relay runs on ports 8765 (WebSocket) / 8766 (HTTP) inside this workspace. The `start-relay` setup step launches it.--Check health: `curl -sf http://127.0.0.1:8766/health`-Check desktop connection: `adom-desktop ping`--The host control API is reachable from inside the workspace at `127.0.0.1`. The port is dynamic per launch, so read the live URL from `~/.adom/hd-control-url` (`http://127.0.0.1:<dynamic>`): `BASE="$(cat ~/.adom/hd-control-url)"; curl "$BASE/health"`.--## Available bridge commands (when desktop is connected)--### Screenshots (zero dialogs, instant)-```bash-adom-desktop desktop_screenshot_screen # full screen capture-adom-desktop desktop_screenshot_screen '{"maxWidth":1500}' # resized for Claude vision-adom-desktop desktop_screenshot_window '{"hwnd": N}' # capture specific window-adom-desktop desktop_list_windows # list all windows with handles-```-Default to `maxWidth: 1500, format: "png"` for UI screenshots. Use `"jpeg"` for natural photos, `"webp"` for smaller files.--### Browser automation (Puppeteer)-```bash-adom-desktop browser_open_window '{"url": "https://..."}'-adom-desktop browser_screenshot '{"sessionId": "default"}'-adom-desktop browser_eval '{"js": "document.title"}'-adom-desktop browser_navigate '{"url": "..."}'-```--### KiCad bridge-```bash-adom-desktop kicad_open_board '{"path": "/path/to/board.kicad_pcb"}'-adom-desktop kicad_screenshot_all-adom-desktop kicad_run_drc '{"path": "..."}'-adom-desktop kicad_window_info-```-(Paths to host files use the host's native path convention.)--### Fusion 360 bridge-```bash-adom-desktop fusion_start-adom-desktop fusion_import_step '{"path": "..."}'-adom-desktop fusion_export_step '{"path": "..."}'-```--### File transfer-```bash-adom-desktop send_files '{"files": [{"path": "/home/adom/file.txt"}]}'-adom-desktop pull_file '{"remotePath": "<host path>", "localPath": "/tmp/file.txt"}'-```--### Desktop interaction-```bash-adom-desktop notify_user '{"message": "Build complete", "duration_ms": 3000}'-adom-desktop desktop_open_url '{"url": "https://..."}'-adom-desktop desktop_open_folder '{"path": "<host folder path>"}'-```--## What's different from Adom cloud containers--| Feature | Adom Cloud | HD Local |-|---------|-----------|----------|-| Workspace provisioning | Adom platform API | golden image imported on the user's machine |-| Relay connection | wss:// through Cloudflare | ws://localhost:8765 (direct, no TLS) |-| Bridge latency | ~50-100ms (internet round-trip) | <1ms (localhost) |-| VSCODE_PROXY_URI | Coder proxy URL | http://localhost:7380/proxy/{{port}}/ |-| Socket.IO (collab) | Connected to iron.adom.inc | Disabled (single-user) |-| Service containers | Adjacent containers | Remote via Adom cloud |--## Environment variables--- `ADOM_DESKTOP_MODE=local` — indicates HD local mode-- `ADOM_SERVICE=local` — marks this as a local workspace-- `VSCODE_PROXY_URI=http://localhost:7380/proxy/{{port}}/` — code-server's proxy--(Platform layers may set additional host-specific env vars — see `[[hd-container-windows]]`.)--## Related skills--- `[[hd-container-stats]]` — the title-bar CPU/RAM indicator for this workspace-- `[[hd-setup-steps]]` — what each setup step did to prepare your workspace
skills/hd-desktop-sse/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-desktop-sse/SKILL.mddeleted−242@@ -1,242 +0,0 @@-name: hd-desktop-sse-description: How HD's local workspace API + SSE works (a.k.a. the workspace API) — replaces Carbon for workspace commands so the workspace's adom-cli controls HD, not cloud Hydrogen. The workspace is the WSL2 Adom-Workspace distro by default (or a Docker container under HD_RUNTIME=docker). Covers the full pipeline, routing, env vars, field format differences, and hard-won lessons. Trigger words — HD SSE, workspace API, local API, adom-cli workspace, add-tab, move-tab, webview tab, EventSource, workspace_updated, ADOM_CARBON_URL, ADOM_HYDROGEN_URL.--# Hydrogen Desktop — Local Workspace API + SSE--## Architecture--```-Workspace adom-cli (WSL2 Adom-Workspace distro by default, or Docker container)- → http://127.0.0.1:47083 (HD's proxy)- → HD handles workspace commands locally + broadcasts SSE- → HD frontend receives SSE, re-fetches workspace, renders--Cloud Docker's adom-cli (unchanged)- → carbon.adom.inc → Hydrogen web-```--HD runs its own workspace API on port 47083 (reverse proxy, default — see PortConfig) so the workspace's adom-cli controls HD's UI, not cloud Hydrogen. No cross-talk between environments. (Under WSL2 the workspace reaches HD at `127.0.0.1:47083` — mirrored networking shares loopback with the Windows host.)--## Port 47083 routing (the hd-proxy / reverse-proxy)--All requests hit port 47083 (reverse proxy, default — see PortConfig). The router in `handle_request()` decides:--| Path pattern | Handler | Where |-|---|---|---|-| `/api/workspaces/editor/{owner}/{repo}/current/*` | Local workspace API | In-memory |-| `/api/panels/webview/*`, `/api/panels/sandbox/*` | Local panel API | SSE broadcast |-| `/containers/*` | Local container discovery | Canned response |-| `/proxy/{port}/{path}` | Workspace proxy | TCP forward to the workspace (WSL2 distro) |-| Everything else (`/api/*`, etc.) | Carbon proxy | HTTPS to carbon.adom.inc |--The dev file server on port 1420 forwards `/api/*` directly to `handle_request()` (same function, not TCP — supports SSE streaming via BoxBody).--## SSE Connection--**Endpoint:** `GET /api/workspaces/editor/{owner}/{repo}/current/events`--Uses `tokio::sync::broadcast` channel. On connect sends `{"type":"connected","connectionId":"hd-local"}`. Workspace mutations broadcast `{"type":"workspace_updated"}`.--**Frontend connects** in `PanelWorkspaceComponent.svelte` → `setupSSE()`. In Tauri mode, `$page.params` may be empty (adapter-static), so it falls back to `owner=local, repo=workspace`.--**Critical lesson:** The `onopen` handler PUTs the workspace state. This broadcasts `workspace_updated`, which the frontend re-fetches. Set `skipNextSSESync = true` before the PUT to prevent the feedback loop that kills the connection.--## Container Discovery--adom-cli calls `/containers/{slug}` to get owner/repo. The slug comes from parsing `VSCODE_PROXY_URI`'s hostname — split on `-`, take the LAST segment.--**Response format** (must match Carbon's exactly):-```json-{- "repository": {- "owner": { "name": "jlauer12" },- "name": "workspace"- },- "slug": "hdlocal",- "status": "running"-}-```--NOT `{"owner": "jlauer12", "repository": "workspace"}` — adom-cli reads `container["repository"]["owner"]["name"]`.--## Workspace Env Vars--Set when the workspace is created (e.g. `create_container()` in `hd-docker/src/lib.rs` under the Docker runtime):--| Var | Value | Why |-|---|---|---|-| `VSCODE_PROXY_URI` | `http://hd-hdlocal/proxy/{{port}}/` | Slug = `hdlocal` (last segment after `-`) |-| `ADOM_CARBON_URL` | `http://127.0.0.1:47083` | Container discovery + non-workspace API |-| `ADOM_HYDROGEN_URL` | `http://127.0.0.1:47083` | Workspace mutations (adom-cli sends these HERE, not to Carbon) |-| `ADOM_DESKTOP_MODE` | `local` | Tells adom-cli it's a local environment |--Under WSL2, `127.0.0.1` reaches the Windows host directly — mirrored networking shares loopback with the host, so the same `127.0.0.1:<port>` works from inside the distro and on Windows. The canonical code-server port HD exposes on the host is 7380.--**Also set in adom-cli config** (persists across sessions):-```bash-adom-cli config set-url http://127.0.0.1:47083-adom-cli config set-token <session-token>-```-Plus manually write `hydrogen_url` to `~/.config/adom-cli/config.json` (no CLI command for it).--**API key** injected at `/var/run/adom/api-key` by bootstrap via `GET "$(cat ~/.adom/hd-control-url)/auth-token"` (the control URL is `http://127.0.0.1:<dynamic>`).--## Workspace Tree Format--The frontend serializes splits with `first`/`second`, NOT `children`:-```json-{- "type": "split",- "id": "desktop-root-split",- "direction": "horizontal",- "ratio": 0.5,- "first": { "type": "leaf", "id": "pane-left", ... },- "second": { "type": "leaf", "id": "pane-right", ... }-}-```--All tree traversal functions (`find_leaf_path`, `find_split_path`, `resolve_path`) must handle BOTH `children` array and `first`/`second` keys.--## REST Endpoint Field Names (adom-cli vs HD)--adom-cli sends different field names than you might expect:--| Endpoint | adom-cli sends | HD must read |-|---|---|---|-| POST /tabs | `{panelId, panelType, displayName, initialState}` | Construct tab with UUID, merge initialState into panelState |-| POST /moves | `{sourcePanelId, tabId, targetPanelId}` | NOT `sourcePanel`/`targetPanel` |-| DELETE /tabs | `{panelId, tabId}` or `{name}` | Support both |--## Smoke Test (from inside the workspace)--```bash-# 1. Probe — confirms SSE alive-adom-cli hydrogen probe-# → browser_connected: true, sse_connections: 1--# 2. Get workspace-adom-cli hydrogen workspace get--# 3. Add tab-adom-cli hydrogen workspace add-tab \- --panel-id desktop-pane-right \- --panel-type adom/a1b2c3d4-0031-4000-a000-000000000031 \- --display-name "Test Page" \- --initial-state '{"url":"https://wiki-ufypy5dpx93o.adom.cloud/apps/shotlog"}'--# 4. Move tab-adom-cli hydrogen workspace move-tab \- --from-panel-id desktop-pane-right \- --tab-id <tab-id-from-step-3> \- --to-panel-id desktop-pane-left--# 5. Screenshot to verify-adom-desktop hd_screenshot-```--## What Broke and Why--### SSE never connected on fresh launch-`setupSSE()` checks `$page.params.owner` and `$page.params.repo`. In Tauri mode with adapter-static, these are empty. **Fix:** fall back to `owner=local, repo=workspace` when in Tauri mode.--### SSE connected then immediately disconnected-The `onopen` handler PUTs workspace state → broadcasts `workspace_updated` → frontend re-fetches → gets the state it just PUT → re-applies → triggers autosave → another PUT → loop. **Fix:** set `skipNextSSESync = true` before the `onopen` PUT.--### adom-cli "Could not get owner from container info"-Three stacked issues:-1. `VSCODE_PROXY_URI` was `http://localhost:8080/...` → slug parsed as `8080` → called `/containers/8080` → Carbon 404-2. Container info response had flat `{"owner": "..."}` but adom-cli reads `container["repository"]["owner"]["name"]`-3. `ADOM_HYDROGEN_URL` wasn't set → workspace commands went to `hydrogen.adom.inc` instead of HD--### add-tab returned "Missing 'tab' in body"-HD expected `{"panelId", "tab": {id, panelType, panelState}}` but adom-cli sends `{"panelId", "panelType", "displayName", "initialState"}` — flat format. **Fix:** accept both, construct tab with UUID for the flat format.--### move-tab returned "Source/target panel not found"-Two issues:-1. Handler read `sourcePanel`/`targetPanel` but adom-cli sends `sourcePanelId`/`targetPanelId`-2. `find_leaf_path` only traversed `children` array, not `first`/`second` keys--### Webview tabs were blank + opened in native browser-`add_FrameNavigationStarting` was blocking ALL external URLs in child iframes, including legitimate webview panel content. **Fix:** stop blocking frame navigations entirely — webview panels need external URLs. URL interception for window.open still works via `on_new_window`.--### Google showed "blocked" icon-Not an HD bug. Google sends `X-Frame-Options: SAMEORIGIN` which prevents iframe embedding. Sites that allow embedding (adom.inc, wiki) load fine.--## Port Discovery — How Any Client Finds HD--All HD ports are dynamic (configurable via `ports.json`). The ONE constant is the **discovery port: 47080**. It never changes.--```-GET http://127.0.0.1:47080/discover-```--Returns:-```json-{- "ok": true,- "code_server": 7380,- "proxy": 47083,- "control": 47084,- "cdp": 47085,- "hostname": "hd.localhost"-}-```--> **Relay ports:** under the WSL2 runtime the relay listens **same-port 8765-> (WS) / 8766 (HTTP)** inside the distro and is reached same-port via mirrored-> loopback (or via `/proxy/`). There are **no host 47081/47082 listeners**.-> Clients discover the relay ports from `~/.adom/cli-relay-ports.json` (fallback-> 8765/8766), not from `/discover`.--### Who uses it--| Client | What it discovers | How |-|---|---|---|-| adom-desktop relay | Control API port (was hardcoded 9001) | `curl :47080/discover` → use `control` field |-| adom-cli in the workspace (WSL2 distro / Docker) | Proxy + Hydrogen URL | Already set via `ADOM_CARBON_URL` env var at workspace creation |-| External tools / diagnostics | Any port | Hit discovery, get the full map |-| Cloud Docker (this container) | All HD ports | `curl http://<desktop-ip>:47080/discover` via relay |--### Implementation--Tiny `std::net::TcpListener` on port 47080 in `hd-app/src/lib.rs`. Runs on a plain OS thread (no tokio). Reads `get_runtime_ports()` on each request. Returns raw HTTP response with JSON body.--```rust-std::thread::spawn(move || {- let listener = std::net::TcpListener::bind("127.0.0.1:47080").unwrap();- for stream in listener.incoming() {- let ports = hd_control::get_runtime_ports();- let body = serde_json::json!({ "ok": true, ...ports });- // write HTTP/1.1 200 OK + JSON- }-});-```--### Port defaults (PortConfig)--| Service | Default Port | User-visible? | Workspace Port |-|---|---|---|---|-| Discovery | **47080** (FIXED) | No | n/a |-| Code-server | 7380 | Yes (in webview URLs) | 8080 |-| Proxy | 47083 | No | n/a |-| Control API | 47084 | No | n/a |-| CDP | 47085 | No | n/a |-| Relay WS | — (same-port under WSL2) | No | 8765 |-| Relay HTTP | — (same-port under WSL2) | No | 8766 |--Config stored at `%APPDATA%\hydrogen-desktop\ports.json`. On first launch, defaults are written without scanning. Workspace-side ports (8080, 8765, 8766) are always fixed — only host-side ports are dynamic. The relay (8765/8766) is reached **same-port** under WSL2 (mirrored loopback) — no host 47081/47082 mapping; discover it from `~/.adom/cli-relay-ports.json` (fallback 8765/8766).--## Files--| File | What |-|---|---|-| `src-tauri/crates/hd-proxy/src/lib.rs` | Workspace API, SSE, Carbon proxy, tree mutations (~1500 lines) |-| `src-tauri/crates/hd-docker/src/lib.rs` | Container env vars (CARBON_URL, HYDROGEN_URL, VSCODE_PROXY_URI) |-| `src-tauri/crates/hd-control/src/lib.rs` | `/auth-token` endpoint, `/ports` endpoint, `/setup/*` routes |-| `src-tauri/crates/hd-control/src/ports.rs` | PortConfig struct, load/save/resolve, DISCOVERY_PORT constant |-| `src-tauri/crates/hd-app/src/lib.rs` | Discovery server on :47080, port resolution at startup |-| `src/lib/stores/portStore.ts` | Frontend Svelte store, `initPorts()`, `controlUrl()`, `codeServerUrl()` |-| `src/lib/components/editor/workspaces/PanelWorkspaceComponent.svelte` | Frontend SSE setup, autosave, workspace re-fetch |-| `scripts/hd-bootstrap.sh` | Sets adom-cli config, injects API key, uses `HD_CONTROL_PORT` env var |
skills/hd-eda-discovery/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-eda-discovery/SKILL.mddeleted−102@@ -1,102 +0,0 @@-name: hd-eda-discovery-description: >- The onboarding playbook a (second, background) Claude conversation runs after HD setup to- discover which EDA / electronics-design tools the user runs — KiCad, Fusion 360, Altium,- OrCAD/Cadence — and onboard them into the AI-first electronics workflow. If KiCad/Fusion- (we have bridges): remember their preferred tool in a prefs file, offer to open example- files, and pitch the gerber-export → order-at-JLCPCB/PCBWay/OSHPark loop. If Altium/OrCAD- (no bridge yet): pitch co-building a bridge with the AI and publishing it to the Adom wiki- for the community (KiCad/Fusion bridges are the reference). READ + RUN this when a new- thread is asked to "explore what EDA software I use" / on the post-setup discovery pass.- Trigger words — eda discovery, what eda software, what cad do i use, electronics design- tools, detect kicad, detect fusion, detect altium, detect orcad, onboard eda, pcb workflow,- order pcb, gerbers, jlcpcb, pcbway, oshpark, write a bridge, altium bridge, orcad bridge,- publish to adom wiki, ai-first electronics, who am i and what can i do, post-setup explore.--# EDA tool discovery & onboarding (run in the background after setup)--You're (likely) a **second Claude conversation** launched after HD setup to quietly figure out-the user's electronics-design stack and show them what the AI-first Adom workflow unlocks. Be-**exploratory and opt-in** — detect first, then offer; never auto-install or auto-build. Do the-detection in the background, then come back with a short, friendly summary + one clear offer.--## 1. Detect (via `adom-desktop` — see `hd-adom-desktop`, `hd-bridges`, `pup`)--```bash-adom-desktop status # relay up? caps include kicad / fusion360?-adom-desktop kicad_list_versions '{}' # KiCad installed (+ which versions)-adom-desktop fusion_status '{}' # Fusion 360 installed? ("installed but not running" = yes)-# No bridge for these — detect by install path / running process:-adom-desktop run_script '{"interpreter":"powershell","scriptB64":"<b64 of:>"}'-# Get-ChildItem 'C:\Program Files','C:\Program Files (x86)' -Directory -EA 0 |-# ? { $_.Name -match 'Altium|OrCAD|Cadence|Allegro|EAGLE|DipTrace|Eplan' } | % Name-# plus: Get-Process | ? { $_.Name -match 'Altium|OrCAD|Capture|Allegro' } | % Name-```-(Base64 the PowerShell for `run_script`; use absolute paths, and treat **empty output as a-timeout**, not a negative result.)--## 2. Branch on what you found--### A) KiCad and/or Fusion 360 found — we have bridges, go deep (opt-in)-1. **Remember it.** Write a small prefs file so future threads skip re-detection:- ```bash- mkdir -p ~/.adom- cat > ~/.adom/eda-prefs.json <<'JSON'- {"primary":"kicad","versions":["10.0"],"fusion":true,"detectedAt":"<date>",- "notes":"user runs KiCad v10; Fusion installed (read-only/expired)"}- JSON- ```- (Also worth a one-line memory note if you keep memories.)-2. **Show, don't tell** — offer to open an example so they *see* it work:- *"Want me to open a sample board in KiCad and pop the 3D viewer so you can see what I can drive?"* → use the `pic_programmer` demo flow (`hd-self-screenshot`/`adom-desktop-kicad`): open `.kicad_sch` + `.kicad_pcb`, symbol/footprint editors, then the **board** 3D viewer (close the footprint+symbol editors first — `kicad_open_3d_viewer` attaches to the active editor otherwise).-3. **Pitch the loop** that AI makes effortless — *"From here I can export gerbers and help you- order the board — JLCPCB, PCBWay, or OSHPark."* If they're keen, that's the on-ramp to (4).-4. **The real unlock — build the glue, publish it.** Most fab vendors have no clean API, so the- value is AI-built automation: *pull from KiCad → stage in the workspace → drive the- vendor's upload flow (pup) → order.* Offer to **vibe-code an uploader/automation app** for- their fab of choice and **publish it to the Adom wiki** so the whole community gets it. This- is exactly what the wiki is for (see §3).--### B) Altium / OrCAD / Allegro / EAGLE / other — no bridge yet (the community pitch)-We don't have a bridge for these — **but that's an opportunity, not a dead end.** Offer:--> *"You're on **Altium** — we don't have an Adom bridge for it yet, but we have a wiki for-> sharing apps with the whole community, and we could **build one together right now**. The-> KiCad and Fusion 360 bridges are great references for the AI to model the design on — it's-> probably ~30 minutes of vibe-coding. Then we publish it to the Adom wiki so every Altium-> user gets it. We could all use it. Want to?"*--- Reference bridges: `adom-inc/adom-desktop/plugins/kicad/` and `…/plugins/fusion360/` (a Python- bridge that the desktop app dispatches to; see `adom-desktop-kicad`/`-fusion` for the verb- shape). A new bridge = the same pattern (launch app, open files, screenshot, send keys, an- optional in-process add-in for the richer API).-- Publish flow: the Adom wiki hosts shared apps (`apps/<name>`) — see the wiki publish skills.--### C) Nothing detected / unsure-Ask what they design boards in, and whether they'd like a bridge or workflow built. Keep it-one friendly question, not an interrogation.--## 3. The frame (why this matters — say it briefly, don't lecture)-The electronics industry is moving **AI-first**, and the tools mostly aren't built for it yet.-**Adom is where that comes together:** you build an AI-first app (a bridge, an uploader, a-fab-automation glue), publish it to the **Adom wiki**, and the whole community can install and-improve it. KiCad/Fusion bridges, a JLCPCB/PCBWay/OSHPark uploader, an Altium bridge — each one-makes the next person's AI-first workflow turnkey. The user isn't just configuring their setup;-they can **contribute the missing piece** in half an hour.--## 4. Etiquette-- **Opt-in always.** Detect silently; *offer* the demo/build — don't open apps or write code- unprompted. One clear offer beats five questions.-- **Don't overpromise vendor APIs.** JLCPCB/PCBWay/OSHPark uploads are mostly UI automation- (pup), not clean APIs — frame the uploader as AI-driven glue, and verify pages exist before- templating URLs (`pup` skill).-- **Respect read-only states** (e.g. an expired Fusion subscription is view/query-only).-- Report back to the user concisely: *what you found, one thing you can show them now, and one- thing worth building.*--## Related skills-- [hd-adom-desktop](../hd-adom-desktop/SKILL.md) / [hd-bridges](../hd-bridges/SKILL.md) — the `adom-desktop` relay + KiCad/Fusion bridge verbs (in-distro names; the `adom-desktop-*` skills are the cloud-side equivalents)-- `pup` — driving fab-vendor upload pages; [hd-open-url](../hd-open-url/SKILL.md) — opening vendor sites-- the Adom wiki publish skills — sharing the bridge/app you build with the community
skills/hd-embedded-ad/SKILL.mddeleted−141@@ -1,141 +0,0 @@-name: hd-embedded-ad-description: How Hydrogen Desktop (HD) and Adom Desktop (AD) interact via "embedded mode". Use whenever the user asks about the system tray icon, why AD isn't visible, why two desktop apps are installed, the "Embedded · HD" footer pill, or how to make AD show up standalone. Trigger words — embedded mode, system tray, AD tray, why is adom desktop hidden, where's my tray icon, HD vs AD, both apps installed, why two installs, ad missing from tray, switch AD to standalone, AD won't show, run AD without HD, decouple AD from HD.--# HD ↔ AD Embedded Mode--There are two desktop apps in the Adom ecosystem: **Hydrogen Desktop (HD)** — the user-facing workspace app — and **Adom Desktop (AD)** — the bridge that talks to local apps (KiCad / Fusion 360 / Chrome / files). HD's installer silently bundles AD and runs it in **embedded mode**, where AD is invisible and HD's tray menu mirrors AD's controls.--This skill explains how the two interact so you can answer "why isn't AD in my tray?", "should I have two installs?", "why did the tray icon disappear?" and similar.--## Two modes--### Standalone AD (no HD anywhere)--How AD launches when nobody owns it:-- Tray icon visible in Windows notification area-- Main window opens on launch (unless `--start-hidden`)-- Autostart shortcut in `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\AdomDesktop.lnk` → launches on every boot-- Registered in Add/Remove Programs (HKLM `Uninstall\AdomDesktop`)-- Auto-update polls `wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/version.json` every 4h-- Footer shows the user's email + a "Sign in" / "Sign out" link--This is what AD looked like before HD existed and what it reverts to when HD vanishes.--### Embedded AD (HD owns it)--HD's installer puts AD into embedded mode by:-1. Writing `%LOCALAPPDATA%\Adom Desktop\embedded.json` marker (`{"owner":"hydrogen-desktop", ...}`)-2. Deleting AD's Desktop / Start Menu / Startup `.lnk` shortcuts so the user can't double-launch standalone AD-3. Configuring HD's own systray to mirror AD's menu (Open AD, Connect All, Disconnect All, Settings, etc.)--When HD spawns AD with `--embedded --start-hidden --relay-url ws://127.0.0.1:8765 --relay-name hydrogen-desktop`, AD detects embedded mode and:-- **Hides** its system tray (HD owns the one tray)-- **Suppresses** its autostart shortcut creation-- **Disables** its auto-updater (HD owns AD's update channel — fresh AD bundled in each HD release)-- **Hides** its main window on boot (HD's tray "Open Adom Desktop" menu item opens it on demand)-- **Skips** HKLM Uninstall registration (HD's installer offers cascade-uninstall of AD)-- Footer shows a read-only email + an "Embedded · HD" pill--Result: the user only ever sees one app (HD) in the tray + Start Menu, even though two processes (HD + AD) are running.--## How AD detects "am I embedded?" — 3-signal cascade--AD calls `embedded::detect_embedded_mode()` at boot. First match wins:--1. **CLI flag**: `--embedded` (or `--embedded-mode`) on the command line. Writes the marker file, owner defaults to `hydrogen-desktop`. **This is what HD spawns AD with.**-2. **Env var**: `ADOM_EMBEDDED=1` (or `true`). Same behavior as the flag.-3. **Marker file**: `%LOCALAPPDATA%\Adom Desktop\embedded.json` exists. AD then probes HD's **liveness beacon** on `127.0.0.1:9001` (a real port AD listens for — NOT HD's control API; HD's actual control API is **47084**):- - If HD answers the beacon → embedded mode- - If HD doesn't answer → AD treats the marker as stale, deletes it, falls back to standalone--If none of the three signals fire → standalone.--## Lifecycle ownership in embedded mode--HD's runtime supervisor:-- Reads HD's session token from `%APPDATA%\hydrogen-desktop\hydrogen-session.txt`-- Spawns AD detached with the embedded flags above-- Polls `http://127.0.0.1:47200/health` every 5s-- If AD's health probe fails → HD respawns AD with the same args--HD's tray menu (mirrors AD's controls so the user gets all the AD verbs without seeing AD's tray):-```-Open Hydrogen Desktop-Reload Workspace-── Adom Desktop ──-Open Adom Desktop → POST 127.0.0.1:47200 desktop/window_show-Connect All → POST desktop/connect_all-Disconnect All → POST desktop/disconnect_all-Settings (Adom Desktop)→ POST desktop/window_show-─────-Quit → POST desktop/shutdown, 200ms grace, then app.exit(0)-```--HD's uninstaller PREUNINSTALL hook prompts "Also remove Adom Desktop?" → cascade-uninstalls AD on Yes.--## Runtime transitions--**AD ≥ v1.8.47** re-checks HD's liveness every 60s during runtime, not just at boot. This is the normal daily cycle for most users, not an edge case:--- Morning: user launches HD → HD spawns AD with `--embedded` → AD's tray hides, pill appears-- Evening: user quits HD → AD's next poll fails the HD probe → AD reverts to standalone → tray reappears-- Next morning: user launches HD again → AD's next poll succeeds → AD re-embeds → tray hides--The same AD process can flip mode back and forth indefinitely across an HD-quit / HD-relaunch cycle. Transitions:--| Current mode | HD probe says | What AD does |-|---|---|---|-| Standalone | dead | stay standalone (no-op) |-| Standalone | alive | → **embed**: hide tray, write marker, hide main window, show "Embedded · HD" pill |-| Embedded | dead | → **unembed**: show tray, delete marker, hide pill (main window left as-is) |-| Embedded | alive | stay embedded (refresh marker heartbeat in `last_seen`) |--Both transitions emit a `mode-changed` event so the frontend can update the footer pill in real time.--**AD ≤ v1.8.46** only checks at boot. If HD dies while AD is running, AD stays invisible in embedded mode until restart.--## Common questions--### "I don't see AD in my system tray on a Windows box with HD installed"--Expected. HD's installer deleted AD's autostart shortcut, so AD doesn't launch on boot. AD only runs when HD spawns it. HD's tray includes AD's menu items.--To verify: `tasklist | findstr adom-desktop`. If AD's running, it's just hidden (per embedded mode). If not running, HD isn't either.--### "I want AD standalone — how do I unembed?"--- Quit HD-- Delete `%LOCALAPPDATA%\Adom Desktop\embedded.json`-- Launch AD via `%LOCALAPPDATA%\Adom Desktop\adom-desktop.exe`-- AD will detect no marker + no CLI flag → standalone mode → tray reappears--Or uninstall HD with "Yes, also remove Adom Desktop" → No → HD is gone, AD's marker is cleared, AD reverts to standalone on next launch. (Note: AD's autostart shortcut was deleted by HD's installer, so AD still won't auto-launch on boot until you re-run AD's installer to recreate it.)--### "Both HD and AD are installed — is that normal?"--Yes. HD's NSIS POSTINSTALL hook silently runs the bundled AD installer with `/S /NOLAUNCH`. AD lives at `%LOCALAPPDATA%\Adom Desktop\`, HD at `%LOCALAPPDATA%\Hydrogen Desktop\`. Two separate exes, two separate Add/Remove Programs entries (well, only HD's is in Add/Remove if HD's installer ran the embedded-mode AD which skips HKLM register — AD ≥ v1.8.46).--### "AD's tray suddenly appeared even though HD is installed"--HD probably crashed or was closed. AD ≥ v1.8.47's runtime poll noticed HD is dead and reverted to standalone tray. The fix: relaunch HD from the Start Menu → AD's next poll detects HD → tray hides again.--### "Direct API for AD verbs"--AD listens on `127.0.0.1:47200` for `POST /command` with `{"app":"desktop","command":"<verb>","args":{...}}`. The 7 user-visible verbs HD's tray uses:-- `window_show`, `window_hide`-- `connect_all`, `disconnect_all`-- `shutdown`-- `embedded_status` (returns `{embedded, owner, source, ...}`)-- `logout`--Port discovery if 47200 is taken: read `%USERPROFILE%\.adom\direct-api-port` (single line `host:port`).--## Source-of-truth references--- AD's mode detection: `adom-desktop/src-tauri/src/embedded.rs:detect_embedded_mode()`-- HD's AD spawn: `hydrogen-desktop/src-tauri/crates/hd-app/src/ad_supervisor.rs:spawn_ad_embedded()`-- HD's tray menu: `hydrogen-desktop/src-tauri/crates/hd-app/src/tray.rs:install_tray()`-- HD's NSIS hook (embed-AD on install / offer-uninstall): `hydrogen-desktop/src-tauri/crates/hd-app/installer/hd-install.nsh.template`-- AD's release manifest (used by HD's release pipeline to fetch bundled AD): `wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-desktop/version.json`
skills/hd-eval/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-eval/SKILL.mddeleted−159@@ -1,159 +0,0 @@-name: hd-eval-description: >- Inject JavaScript into ANY surface of the Hydrogen Desktop window from inside- the workspace — the app shell (top nav, dialogs), the VS Code workbench, the- wiki panel, or the Claude chat webview. Backed by the control API's POST- /eval-in, which connects directly to the right CDP target and returns the JS- value. Use it to build add-ons (e.g. a usage meter in the top bar), demo- overlays (a moving arrow that points at things), highlight elements, read DOM- state, or drive the UI. Works in dev AND release builds. Trigger words:- eval-in, inject ui, eval into hd, hd overlay, demo arrow, highlight element,- add-on, addon, usage meter, inject script, target shell, target workbench,- target claude, find probe, eval-in targets, point at element, nav widget.--# hd-eval — inject JS into any HD surface (`POST /eval-in`)--The HD window is **not one page**. It's a tree of cross-origin surfaces, each a-separate Chrome DevTools Protocol (CDP) target/context:--| friendly target | what it is | typical use |-|---|---|---|-| `shell` | the HD app shell (Svelte): top nav, logo menu, dialogs, the floating layer | inject add-ons/overlays that float over everything |-| `workbench` | the VS Code editor (code-server) | activity bar, tabs, the "Claude Code: Open" button |-| `wiki` | the wiki panel iframe | wiki DOM |-| `claude` | the Claude Code chat **webview** (only exists while the panel is open) | the chat input + send button |--`/eval-in` figures out where a surface lives (it differs between dev and release —-see "Why two paths" below) and runs your JS there, returning the value.--## Finding the control port + reaching it from the workspace--The control API port is **dynamic** (auto-resolves conflicts per launch). From inside-the workspace, reach HD on `127.0.0.1` (WSL2 mirrored networking shares loopback with the-Windows host). Read the live control URL from the discovery file `~/.adom/hd-control-url`-(your non-interactive Bash shells don't source `.bashrc`/`profile.d`, so the env var alone-is unreliable):--```bash-BASE="$(cat ~/.adom/hd-control-url)" # http://127.0.0.1:<dynamic>-```--(Never hardcode the port — read the live URL from `~/.adom/hd-control-url`.)--## Discover the surfaces--```bash-curl -sf $BASE/targets | jq .-# each surface has a friendly alias (shell|workbench|wiki|claude), url, id.-# the claude target (Claude chat webview) appears only when that panel is open.-```--`POST /eval-in` with no `target` (or no `js`) also returns the surface list, so an-agent can ask "what can I inject into?" and pick.--## The call--```-POST /eval-in-{- "target": "shell" | "workbench" | "wiki" | "claude" | "<url-substring>" | "<id>",- "js": "<javascript; value of last expression is returned>",- "find": "<OPTIONAL JS expression>", // disambiguate among same-origin contexts- "frame": "<url-substring | index>", // OPTIONAL nested-context selector- "awaitPromise": true // OPTIONAL (default true)-}-→ { "ok": true, "value": <result>, "target": "<matched url>", "contextId": N, "frames": [...] }-```--### `find` — the important one--A surface can expose **several same-origin nested contexts** (the workbench-exposes ~6 `localhost:7380` contexts). Origin alone is ambiguous, so pass `find` —-a JS **expression** (e.g. `document.querySelector('...')`, NOT a bare CSS selector).-`/eval-in` evals it in each candidate context and runs your `js` in the **first-one where `find` is truthy**.--## Recipes--### Add-on: a widget in the top nav (e.g. a usage meter) — target `shell`-Inject once; it persists until reload. Append your element to `nav` in the shell.--### Demo overlay: a red arrow over everything that you reposition — target `shell`-Inject a `position:fixed; z-index:2147483647; pointer-events:none` element with a-CSS `transition` into the shell `body`, then move it with cheap follow-up evals-(it glides). The shell floats over all iframes, so window coords work everywhere.--### Highlight an element (even deep in the workbench / Claude webview)-Eval **inside the element's own surface** and set `outline` — no coordinate math.-Use `find` to land in the right context, e.g.-`find: document.querySelector("a[aria-label='Claude Code: Open']")`.--### Type into & submit the Claude chat (panel must be open)-Stable selectors (NOT the CSS-module hash classes like `.messageInput_xxxx`,-which change per build):-- input = `[aria-label='Message input']` (a `contenteditable` div) — focus it,- then `document.execCommand('insertText', false, text)`.-- submit = `button[type='submit'][data-permission-mode]` (disabled until text) —- `.click()` it; fallback = dispatch an `Enter` keydown on the input.--## Why two paths (dev vs release) — you don't have to care--- **Dev build:** shell is `localhost:1420`; the workbench (`localhost:7380`) is- same-process, so it's a context INSIDE the shell page (not a separate target).- `/eval-in` falls back to the shell and filters contexts by origin.-- **Release build:** shell is `tauri.localhost`; the workbench is a different site →- an out-of-process iframe (OOPIF) → its own top-level target. `/eval-in` connects- directly.--You just pass `target:"workbench"` (or `claude`) and `/eval-in` picks the right-path. Prefer it over the older `/eval` (shell only) and `/iframe-eval`-(page-context walk, breaks in release).--## Verify-then-fallback (recommended)--`/eval-in` returns the JS value, so always verify: after an action, eval a probe-and check it; if it fails, try a second technique. The HD "eval-in demo" floating-toolbar does exactly this and logs endpoint + params + verify + fallback per step.--## Don'ts-- Don't hardcode the control port — read `/ports`.-- Don't pass `find` as a bare CSS selector — it's a JS expression.-- Don't select by CSS-module hash classes — use stable attributes (`aria-label`, `data-*`, `role`).-- Don't expect the `claude` webview to exist unless the Claude panel is open.-- Don't use `/eval` for workbench/Claude — it only reaches the shell page.--## Cross-references-- **hd-api** — the full control API surface-- **hd-ui** / **hd-adom-menu** — where UI elements live-- **hd-self-screenshot** — capture the result of your injection--## Recipe: hide/show activity-bar icons (the RIGHT way — persisted, overflow-proof)--Do NOT hide activity-bar icons with `display:none` on `.action-item` — that misses-icons collapsed into the "…" (Additional Views) **overflow** and doesn't persist.-Instead write VS Code's own source-of-truth state, then reload the workbench:--- **DB:** `vscode-web-state-db-global` → store `ItemTable` → key `workbench.activity.pinnedViewlets2`-- It's an array of `{id, pinned, visible, order}`. Set **`pinned:false` to hide** an- icon from the activity bar (works even if it's in the overflow), **`pinned:true` to restore**.-- **IDs:** Explorer `workbench.view.explorer` · Search `workbench.view.search` ·- Source Control `workbench.view.scm` · Run&Debug `workbench.view.debug` ·- Extensions `workbench.view.extensions` (extension views: `workbench.view.extension.<id>`).-- IndexedDB is **per-origin**, so read/write from **any** `localhost:7380` context- (target `workbench`, `find: "(typeof indexedDB!=='undefined')"`) — don't require the- workbench main doc (it's absent during a reload).-- After writing, **reload the workbench** (`iframe.src=iframe.src` on the `shell`, or- `POST /reload-vscode`) so VS Code re-reads `pinnedViewlets2`.--```jsonc-// hide Search / Source Control / Run&Debug (then reload)-{ "target":"workbench", "find":"(typeof indexedDB!=='undefined')", "awaitPromise":true,- "js":"(async function(){function g(d,k){return new Promise(function(r){var o=indexedDB.open(d);o.onsuccess=function(){var b=o.result;var q=b.transaction('ItemTable','readonly').objectStore('ItemTable').get(k);q.onsuccess=function(){b.close();r(q.result)}}})}function p(d,k,v){return new Promise(function(r){var o=indexedDB.open(d);o.onsuccess=function(){var b=o.result;var t=b.transaction('ItemTable','readwrite');t.objectStore('ItemTable').put(v,k);t.oncomplete=function(){b.close();r(true)}}})}var DB='vscode-web-state-db-global',K='workbench.activity.pinnedViewlets2';var a=JSON.parse(await g(DB,K));var ids=['workbench.view.search','workbench.view.scm','workbench.view.debug'];a.forEach(function(e){if(ids.indexOf(e.id)>-1)e.pinned=false});await p(DB,K,JSON.stringify(a));return 'hidden';})()" }-```--The HD demo toolbar's **Hide / Restore activity icons** buttons (`/demo/run-{action:"hide-activitybar"|"show-activitybar"}`) do exactly this. The setup-steps-`configure-vscode` step should hide icons this way too (persisted across the-mid-setup reload), not via CSS or transient `:8821` layout calls.
skills/hd-file-transfer/SKILL.mddeleted−92@@ -1,92 +0,0 @@-name: hd-file-transfer-description: >- Move files BOTH WAYS between the HD WSL2 Ubuntu workspace (the Adom-Workspace- distro, where your code lives at /home/adom/project) and the user's Windows PC- — including their Desktop, Downloads, and any folder. Three methods: (1) the- auto-mounted C: drive at /mnt/c inside the distro, (2) the \\wsl$ network path- from Windows Explorer, and (3) the adom-desktop relay (send_files / pull_file)- which also works on the legacy Docker runtime. Use when the user asks "how do I- get this file onto my desktop", "copy this out of the workspace", "get a file- from my PC into the workspace", "move a file in/out", "where do my downloads- go", or "drag a file into the container". Trigger words: copy file out, copy- file in, move file to desktop, get file from desktop, file transfer, /mnt/c,- wsl$, \\wsl.localhost, send_files, pull_file, export file, import file, drag- into workspace, file to my pc, download to desktop, upload to workspace.--# Move files between the workspace and the Windows PC--HD's default workspace is the **WSL2 `Adom-Workspace` distro**. Your work lives-at `/home/adom/project` inside it. There are three ways to move files to/from the-user's real Windows machine. Methods 1–2 are WSL2-native (fast, no relay);-method 3 (the relay) also works on the legacy Docker runtime.--## Method 1 — `/mnt/c` (from INSIDE the distro) — simplest--Windows drives are auto-mounted in the distro: `C:\` is `/mnt/c`. So you can `cp`-straight to/from the user's Desktop or Downloads. (Find the Windows username from-`/mnt/c/Users/` if you don't know it.)--```bash-WINUSER=$(ls /mnt/c/Users | grep -viE 'public|default|all users' | head -1)--# Workspace → Desktop-cp /home/adom/project/report.pdf "/mnt/c/Users/$WINUSER/Desktop/"--# Desktop → workspace-cp "/mnt/c/Users/$WINUSER/Desktop/data.csv" /home/adom/project/--# Downloads folder works the same: /mnt/c/Users/$WINUSER/Downloads/-```--## Method 2 — `\\wsl$` (from Windows Explorer) — drag & drop--From Windows, the distro is a network share. In Explorer's address bar:--```-\\wsl$\Adom-Workspace\home\adom\project- (newer Windows also: \\wsl.localhost\Adom-Workspace\home\adom\project)-```--The user can **drag files in/out** of that folder to/from their Desktop. Tell-them this path when they want to grab their code or drop a file in by hand.--## Method 3 — the adom-desktop relay (works on ANY runtime)--HD embeds Adom Desktop, which bridges the workspace to the real OS. Use this when-you want the AI to push/pull files programmatically (and it's the ONLY method on-the legacy Docker runtime, which has no `/mnt/c`). See **hd-adom-desktop**.--```bash-# Workspace → PC (lands in the user's Downloads by default; large files OK)-adom-desktop send_files '{"filePaths":["/home/adom/project/out.zip"],"saveTo":"C:\\Users\\john\\Desktop"}'--# PC → workspace-adom-desktop pull_file '{"filePaths":["C:\\Users\\john\\Desktop\\photo.png"],"saveTo":"/home/adom/project"}'-```--Notes:-- Confirm the bridge first: `adom-desktop ping` → `pong`. If not connected, the- user must have HD running/foregrounded.-- Files whose basename starts with `_` are silently dropped by the relay — use- plain names.--## Which method to use--| Situation | Use |-|---|---|-| You're scripting inside the workspace (WSL2) | **Method 1** (`/mnt/c`) — fastest |-| User wants to drag files by hand | **Method 2** (`\\wsl$` path) |-| Programmatic, or runtime is Docker, or you need a Save-As target dir | **Method 3** (relay) |--## Where things persist-`/home/adom/project` survives a workspace restart; it is wiped by a **virgin-reset** (`wsl --unregister Adom-Workspace`). Copy anything important to the PC-(any method above) before a virgin reset. See **hd-volume** for the full-persistent-vs-ephemeral map.--## Related-- **hd-volume** — what persists, where files live, backing up before virgin reset-- **hd-adom-desktop** — the relay (send_files / pull_file) and OS reach-- **hd-workspace-lifecycle** — distro import/export/unregister
skills/hd-golden-image/SKILL.mddeleted−128@@ -1,128 +0,0 @@-name: hd-golden-image-description: >- Your Hydrogen Desktop workspace is a PRE-BAKED golden WSL2 image, not a- machine that installed itself step-by-step. gallia, the 8 Adom CLIs, the- `claude` CLI + Claude Code VS Code extension, code-server, the VS Code- settings / trusted-domains / activity-bar config, AND all the hd-* self-- awareness skills are BAKED at image-build time — none of them were installed- by a setup step. READ THIS when a user asks "why is X already here", "how do- I update gallia", "re-run install-hd-skills / install-gallia / install-claude",- or "where did these skills/CLIs come from": the answer is the image, and- updates come from a tarball-version bump, NOT a setup step. Trigger words —- golden image, pre-baked, baked image, adom-golden, adom-golden.tar.gz, hd-wsl2-- image, wsl --import, tarball version, adom-tarball-version, migrate tarball,- update gallia, reinstall gallia, install-hd-skills, install-gallia, install-- claude-cli, install-claude-ext, verify-adom-desktop, why is gallia here, where- did the CLIs come from, how do I update the workspace, re-run setup, lightweight- image, install on demand, command not found, ffmpeg not installed, install a tool,- apt-get install, missing tool, is ffmpeg installed, install imagemagick/pandoc.--# HD Workspace = a pre-baked golden WSL2 image--Your workspace did **not** build itself by installing things one step at a time.-It was imported, fully-formed, from a **golden WSL2 image** that already contains-everything. Understanding this answers a whole class of "why is X here / how do I-update X" questions correctly.--## What's baked in (NOT installed at first-run)--The golden rootfs ships, pre-installed and smoke-gated, with:--- **gallia** (deployed via its `install.mjs` snapshot)-- the **8 Adom CLIs** (`adom-cli`, `adom-desktop`, etc. — at `/usr/local/bin`)-- the **`claude` CLI** + the **Claude Code VS Code extension** (the extension- auto-updates via VS Code's `extensions.autoUpdate`)-- **code-server** + node, plus a baked **systemd** unit that owns code-server- across distro boots and HD restarts-- VS Code **settings.json** (dark theme, Claude permissions), the- **trusted-domains** list, and the **activity-bar trim** (all patched into- `workbench.html` at bake time)-- all **hd-\* self-awareness skills** (the `shared/` + `wsl2/` buckets, flattened- into `~/.claude/skills/<name>/`)--Because all of this is baked, the OLD setup steps that used to install it were-**removed**: `install-gallia`, `install-adom-cli`, `install-hd-skills`,-`install-claude-cli`, `install-claude-ext`, and `verify-adom-desktop` are gone.-You can see the removal comments in `setup_steps_wsl.rs` (around lines 421-450),-e.g. *"install-gallia + install-adom-cli REMOVED — both baked into the golden-image"* and *"install-hd-skills REMOVED — the hd-\* skills are baked"*. The-`install-adom-vscode` step still exists but **installs nothing** — it only proves-the baked extension *activated* (the `:8821` editor-control API answers).--## Lightweight by design — install other tools on demand--The golden image is deliberately **minimal**: it bakes the **Adom essentials** above-and little else. General-purpose tools are **not** pre-installed — **ffmpeg/ffprobe,-imagemagick, pandoc, jq, and friends are absent on purpose**, to keep the image small-and fast to download and migrate.--That is **not a limitation.** When a task needs a tool that isn't here, **install it on-demand and keep going.** The workspace has **passwordless sudo** + apt:--```bash-sudo apt-get update -qq && sudo apt-get install -y ffmpeg # or imagemagick, pandoc, jq, …-```--So the rule when you hit **"command not found"**: **install it and proceed** — never-tell the user the workspace "can't do that." (This mirrors how `adompkg` helps install-an app's dependencies for Adom Wiki apps: lightweight base, add what the user needs,-when they need it.) Don't push to bake every tool into the image — the image stays-lean and the AI provisions per task.--## Where the image comes from--The image is built and released from the **public** `adom-inc/hd-wsl2-image`-repo. HD downloads the release asset `adom-golden.tar.gz` **anonymously** (HD-users have no GitHub identity), verifies its SHA-256, then `wsl --import`s it as-the `Adom-Workspace` distro. Evidence in `runtime/wsl.rs`:--- `TARBALL_URL_PLACEHOLDER` / `TARBALL_SHA256_PLACEHOLDER` (~lines 142-145) — the- GitHub-release download URL + expected hash-- `download_tarball()` (~line 1893) — streams the ~550 MB asset to a cached- `adom-golden.tar.gz` (under `%LocalAppData%\Adom\HydrogenDesktop\cache`),- reusing the cache if present so a re-setup doesn't re-download-- `fresh_import()` (~line 307) — `wsl --import Adom-Workspace … --version 2`--So when a user asks **"why is gallia/the CLIs/these skills already here?"** the-answer is: they came pre-installed in the golden image — nothing installed them-on your machine.--## How updates actually happen — a tarball-version bump, NOT a setup step--The installed image carries a version label at **`/etc/adom-tarball-version`**-(const `TARBALL_VERSION_FILE`), written on import. HD's expected version is the-`TARBALL_VERSION` const in `runtime/wsl.rs` (~line 152). On each launch HD-compares them:--- `read_installed_version()` reads `/etc/adom-tarball-version` from inside the- distro-- if it differs from `TARBALL_VERSION`, HD runs **`migrate_to_new_tarball()`**- (~line 2076): terminate → full `wsl --export` rollback backup → back up- `/home/adom` → `wsl --unregister` → `fresh_import()` the new image → restore- `/home/adom` → `write_installed_version()`. Your files in `/home/adom` survive;- the system layer is swapped wholesale. On any failure it rolls back from the- export so you're never left with no distro.--The practical upshot for answering users:--| User asks | Correct answer |-|---|---|-| "How do I update gallia / the CLIs / the skills?" | You don't run an installer. A new golden image (`adom-inc/hd-wsl2-image`) is released, HD's `TARBALL_VERSION` is bumped, and on next launch HD migrates the distro — gallia/CLIs/skills come along baked. |-| "Can I re-run `install-hd-skills` / `install-gallia`?" | No — those setup steps were **removed**. They don't exist anymore because the artifacts are baked. To refresh them, take a new image (tarball-version bump), not a setup step. |-| "Why is `claude` / the extension already signed-in-ready?" | The CLI + extension are baked; the only thing the cascade does is the one human **sign-in** (the `claude-auth` step). |-| "How do I reset to a clean image?" | `wsl --unregister Adom-Workspace` (or HD's Virgin Reset) → next setup re-imports the golden tarball (the cache can be cleared so it re-downloads). |--## The cascade now just CONFIGURES the baked image--Because installation moved into the image, the WSL setup cascade shrank to **16-steps** that *configure* the already-complete workspace (ensure/start code-server,-activate the baked extension, set env vars, inject the API key, configure VS-Code, the relay/connectivity gates, Claude sign-in, and Welcome). See-[hd-setup-steps](../../wsl2/hd-setup-steps/SKILL.md) for the per-step detail.--## Related skills-- [hd-runtime-mode](../hd-runtime-mode/SKILL.md) — WSL2 (this golden-image path) vs the legacy Docker runtime-- [hd-workspace-lifecycle](../../wsl2/hd-workspace-lifecycle/SKILL.md) — start/stop/reset/migrate the imported distro-- [hd-setup-steps](../../wsl2/hd-setup-steps/SKILL.md) — the 16 configure-the-image steps (what's left after baking)
skills/hd-instapcb/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-instapcb/SKILL.mddeleted−77@@ -1,77 +0,0 @@-name: hd-instapcb-description: >- The playbook a (second, background) Claude conversation runs after setup to wire up the user's- InstaPCB quoting workflow inside Hydrogen Desktop: find any InstaPCB quotes they uploaded- earlier, bring one up in the InstaPCB quoting engine in HD, and — if they want — set up a- live loop so KiCad design changes auto-import and the quote re-quotes on-the-fly with zero- manual steps. Detect + report + offer first; build the watch→reload loop only on a yes. READ- + RUN this when a thread is asked about InstaPCB quotes / "bring up my instapcb quote" / "auto- reload my quote as I change my board". Trigger words — instapcb, insta pcb, instapcb quote,- quoting engine, bring up my quote, reload quote, on-the-fly quote, auto quote, requote, pcb- quote, board quote, kicad to quote, watch kicad reload, live quote, instapcb-quote app.--# InstaPCB quoting workflow — discover & wire the live loop (background, opt-in)--You're (likely) a **second background Claude conversation** asked to set up the user's InstaPCB-quoting loop. The dream: they tweak their KiCad board, and their InstaPCB quote **updates by-itself** — no export, no re-upload, no clicking. Get there in stages: **discover → bring up a-quote → offer the live loop → build it only on a yes.** Don't change anything until they confirm.--## 1. Discover (report, don't assume)--The InstaPCB quoting engine is the **`instapcb-quote` app** (Ray's app), surfaced inside HD as a-webview/app — it is **NOT** part of HD itself, so locate it rather than guess:--```bash-# Is the quoting app running / reachable? (check the running webviews + workspace tabs)-adom-cli hydrogen probe # window_mode, sse-adom-cli hydrogen workspace get # open tabs — is an InstaPCB/quote tab already up?-# Where did earlier uploads land? look for staged quotes/uploads:-ls -la ~/project 2>/dev/null | grep -iE "instapcb|quote|gerber"-find ~/project -maxdepth 3 -iname "*instapcb*" -o -iname "*quote*.json" -o -iname "*gerber*" 2>/dev/null | head-```-- If you can't find the app or prior quotes, **say so plainly** and ask the user where the- InstaPCB app lives (port/URL) and where they uploaded quotes — don't fabricate a "no quotes- found." (Unknown ≠ none.)-- Report: *which* prior quotes you found (names/ids), and whether the quoting engine is open.--## 2. Bring up a quote in the engine-Open the InstaPCB app in the user's chosen surface and load the quote they pick (see-[hd-open-url](../hd-open-url/SKILL.md) for webview-vs-pup; **wv** is the default for a tab they-keep). If the app exposes a quote-by-id URL or an import action, use it; otherwise drive the UI-(`pup` for a scriptable window). Verify it actually loaded (title/eval), don't assume `ok`.--## 3. Offer the live loop (the payoff) — build only on a yes-*"Want me to wire it so your KiCad edits flow into the quote automatically? You change the-board, I re-export and the quote refreshes — no clicking."* If yes, build the loop:--1. **Watch the KiCad design** — watch the user's `.kicad_pcb` (+ schematic) for saves. Use a- file watcher in the workspace, or `adom-desktop watch` around a poll of the board's mtime- (see `adom-desktop-fusion`'s `watch` wrapper for the streaming pattern).-2. **On change → produce the artifact InstaPCB needs.** Most quoting wants **gerbers** — export- them from KiCad (kicad-cli / the PCB editor's plot), staged into `~/project`. (KiCad bridge:- open/drive the board — see `hd-bridges`/`adom-desktop-kicad`.)-3. **Push to the quoting engine + re-quote.** Feed the new gerbers/board to the `instapcb-quote`- app (its import/upload path) and trigger a re-quote; if it has no API, drive the upload UI via- `pup` and reload. **Reload the quote view** so the user sees the new number appear on its own.-4. **Debounce + show state** — don't re-quote on every keystroke-save; debounce, and surface a- small "re-quoting…" / "quote updated" status so the loop is never silently running.--This is the AI-first electronics loop in miniature: **KiCad → workspace → quote**, glued by the-AI. If it works well, it's exactly the kind of thing worth **publishing to the Adom wiki** so-every InstaPCB user gets the live loop (see [hd-eda-discovery](../hd-eda-discovery/SKILL.md) §3).--## 4. Etiquette-- **Detect + offer; don't auto-build.** The watch loop changes files / drives the app — confirm first.-- **Verify the engine + quotes exist before claiming anything** ("I found 2 quotes: …" or "I- can't find the InstaPCB app — where is it?"). Never report a false negative.-- Keep the loop **observable + debounced**; let the user pause/stop it.-- InstaPCB is **Ray's `instapcb-quote` app**, not an HD feature — file quirks/bugs to the- instapcb-quote feedback list, not as HD bugs.--## Related skills-- [hd-eda-discovery](../hd-eda-discovery/SKILL.md) — the sibling post-setup discovery tab (EDA tools); same detect→offer pattern + the AI-first/Adom-wiki frame-- [hd-bridges](../hd-bridges/SKILL.md) — KiCad bridge verbs (open/plot/export the board)-- `pup` — driving the InstaPCB upload/quote UI; [hd-open-url](../hd-open-url/SKILL.md) — opening the app (wv vs pup)
skills/hd-monitor/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-monitor/SKILL.mddeleted−183@@ -1,183 +0,0 @@-name: hd-monitor-description: >- Use when the AI needs to watch the Hydrogen workspace in real-time — react to- tab changes, sharing state, user actions, or any workspace mutation as it- happens. Covers the SSE event stream, Monitor tool patterns, and example- scripts. For point-in-time checks, GET /workspace/health and the control-port- GET /workspace/tabs / /workspace/tabs/find are lighter than the proxy SSE.- Trigger words: watch workspace, react to tab change, wait until user opens X,- workspace event, SSE event, monitor tool, on tab change, real-time react,- react to workspace changes, wait for sharing, watch for new panel,- workspace_updated, monitor hydrogen, live workspace events.--# Hydrogen Real-Time Monitoring--Watch the Hydrogen workspace in real-time using Claude Code's Monitor tool + Hydrogen's SSE event stream. React to user actions as they happen — no polling, no "tell me when you're done."--## When to use--- You need to **wait for the user to do something** (enable sharing, open a panel, switch tabs)-- You want to **react to workspace changes** as they happen (auto-screenshot new panels, navigate web views)-- You're running a **multi-step demo** and need to know when the user is ready for the next step-- You want to **watch for errors** or state changes during a long-running task--## When NOT to use--- **One-shot waits** → use `--wait-for-sharing` on screenshot commands instead-- **Checking current state** → use `workspace tabs` or `screenshot status` directly-- **Short operations** → just run the command and check the result--## Lighter than SSE for point-in-time checks--The proxy SSE stream below is for *reacting continuously*. If you just need a-snapshot of current state, the **control port** (`$(cat ~/.adom/hd-control-url)`,-i.e. `http://127.0.0.1:<dynamic>` — same loopback from the workspace and the host-via WSL2 mirrored networking) has cheaper reads:--- `GET /workspace/health` — deep workspace health (distro + code-server + host reachability)-- `GET /workspace/tabs` — list all workspace tabs right now-- `GET /workspace/tabs/find?name=X` — does a named tab exist (404 if not)--Use these in a poll loop when SSE would be overkill. See **hd-api** for the full-control-port surface.--## The SSE endpoint--```text-GET /api/workspaces/editor/{owner}/{repo}/current/events-Header: X-Api-Key: <token>-```--Emits:-- `{"type": "connected"}` — once on connect-- `{"type": "workspace_updated"}` — on every workspace mutation (tab add/remove, split, resize, active tab change, panel state change)--The events don't include details about *what* changed — query `workspace tabs` or other endpoints after receiving an event to see the current state.--## Basic monitor: watch for workspace changes--```bash-# Monitor tool script — reports tab changes in real-time.-# Inside an HD workspace, talk to the LOCAL HD workspace API, not cloud hostnames:-# derive the base from ADOM_CARBON_URL / ADOM_HYDROGEN_URL (they point at-# http://127.0.0.1:<proxy>). The HD-local slug is always `hdlocal`.-API_KEY=$(cat /var/run/adom/api-key)-CARBON="${ADOM_CARBON_URL:-http://127.0.0.1:47083}"-HYDROGEN="${ADOM_HYDROGEN_URL:-$CARBON}"-SLUG="hdlocal"-INFO=$(curl -s -H "Authorization: Bearer $API_KEY" "$CARBON/containers/$SLUG")-OWNER=$(echo "$INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['repository']['owner']['name'])")-REPO=$(echo "$INFO" | python3 -c "import sys,json; print(json.load(sys.stdin)['repository']['name'])")-BASE="$HYDROGEN/api/workspaces/editor/$OWNER/$REPO/current"-LAST=""--curl -s -N -H "X-Api-Key: $API_KEY" "$BASE/events" 2>/dev/null | while IFS= read -r line; do- cleaned=$(echo "$line" | sed 's/^data: //')- [ -z "$cleaned" ] && continue- type=$(echo "$cleaned" | python3 -c "import sys,json; print(json.load(sys.stdin).get('type',''))" 2>/dev/null)- [ -z "$type" ] && continue-- if [ "$type" = "workspace_updated" ]; then- NOW=$(curl -s -H "X-Api-Key: $API_KEY" "$BASE/tabs" 2>/dev/null | python3 -c "-import sys,json-tabs=json.load(sys.stdin).get('tabs',[])-names=[t['name'] for t in tabs]-active=[t['name'] for t in tabs if t.get('isActive')]-print(f'tabs: {len(names)} — {\", \".join(names)} | active: {\", \".join(active)}')-" 2>/dev/null)- if [ "$NOW" != "$LAST" ] && [ -n "$NOW" ]; then- echo "[workspace] $NOW"- LAST="$NOW"- fi- elif [ "$type" = "connected" ]; then- echo "[connected] Monitoring $OWNER/$REPO"- fi-done-```--Use with the Monitor tool:-```text-Monitor(description: "Hydrogen workspace events", persistent: true, command: "<script above>")-```--## Simpler: use adom-cli in the monitor--For lighter-weight monitoring, use `adom-cli` commands inside a poll loop instead of the SSE stream:--```bash-# Poll sharing status every 5s until it activates-while true; do- STATUS=$(adom-cli hydrogen screenshot status 2>/dev/null)- ACTIVE=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sharing',{}).get('active',False))" 2>/dev/null)- if [ "$ACTIVE" = "True" ]; then- echo "[sharing] Screen sharing activated"- break- fi- sleep 5-done-```--```bash-# Watch for a specific tab to appear-while true; do- adom-cli hydrogen workspace find-tab "Schematic Editor" >/dev/null 2>&1 && {- echo "[found] Schematic Editor tab is open"- break- }- sleep 2-done-```--## Reactive patterns--### Auto-screenshot new panels-When the monitor detects a new tab, automatically screenshot it:--```bash-# In your monitor's workspace_updated handler:-if [ "$NOW" != "$LAST" ]; then- # A new tab appeared — find it and screenshot- NEW_TAB=$(diff <(echo "$LAST") <(echo "$NOW") | grep "^>" | head -1)- echo "[new] $NEW_TAB — taking screenshot"- adom-cli hydrogen screenshot workspace >/dev/null 2>&1-fi-```--### Wait for sharing then capture-Instead of `--wait-for-sharing`, use a monitor for more control:--```bash-# Monitor until sharing activates, then take all the screenshots you need-while true; do- ACTIVE=$(adom-cli hydrogen screenshot status 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('sharing',{}).get('active',False))" 2>/dev/null)- if [ "$ACTIVE" = "True" ]; then- echo "[ready] Sharing active — capturing"- adom-cli hydrogen screenshot workspace- adom-cli hydrogen screenshot panel --name "Schematic Editor"- break- fi- sleep 2-done-```--## Best practices--1. **Use `persistent: true`** for session-length watches (workspace monitoring, demo flows)-2. **Filter events** — don't echo every `workspace_updated`, only report when state actually changes (diff against last known state)-3. **Use `--line-buffered`** with grep in pipes so events arrive immediately-4. **Handle connection drops** — the SSE stream can timeout; wrap in a reconnect loop if needed-5. **Prefer `--wait-for-sharing`** for simple "wait then screenshot" — Monitor is for when you need to react to multiple events or do complex logic-6. **Don't spam** — if the monitor emits too many events, Claude Code auto-stops it. Keep output selective--## Monitor vs other approaches--| Need | Approach |-|------|----------|-| Wait for sharing, then screenshot | `screenshot workspace --wait-for-sharing 30` |-| Check current state once | `screenshot status`, `workspace tabs`, `audio status` |-| React to workspace changes in real-time | **Monitor + SSE stream** |-| Run a multi-step demo with user interaction | **Monitor** — watch for user actions between steps |-| Poll until a condition is met | **Monitor** with a poll loop + `break` |-| Long-running background task with status | `Bash` with `run_in_background` |
skills/hd-notifications/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-notifications/SKILL.mddeleted−126@@ -1,126 +0,0 @@-name: hd-notifications-description: >- How Hydrogen Desktop reaches the user OUTSIDE the window — native Windows toast- notifications + taskbar attention — so the AI can proactively tell the user a- long job finished or that it needs them (great for hands-free / away-from-keyboard- work). Covers HD's notify handler (levels info|warning|error|emergency, optional- <actions> buttons; emergency = a persistent orange taskbar flash via- request_user_attention(Critical) until the user looks), how toasts route via a- PowerShell AUMID so they may show an "Adom" title, and how to fire one with the- `notify` desktop command / `adom-desktop notify_user`. Also Pup's- browser_alert_window taskbar flash. Windows-only. Trigger words — notify the- user, send a notification, desktop notification, toast, windows toast, taskbar- flash, get the user's attention, alert the user, notify_user, emergency- notification, urgent alert, notification actions, action buttons, ping the user,- job done notification, tell me when it's done, flash the taskbar, request- attention, browser_alert_window, in-app toast, ui toast, /ui/toast, show a toast- in HD, toast inside the window.--# Hydrogen Desktop — Notifications (reaching the user outside the window)--When the user isn't looking at HD — a long build is running, you've finished a job,-or you're **blocked and need them** — you can reach them on the OS level: a native-Windows **toast**, and for urgent cases a **persistent taskbar flash**. This is what-makes hands-free / away-from-keyboard work usable: don't just print a message into a-panel nobody's watching — fire a notification.--> **Windows-only** for the full experience (WinRT toasts + taskbar attention).-> macOS has a limited `osascript` fallback; Linux has no toast path.--## Firing a notification--Source: `src-tauri/crates/hd-app/src/notifications.rs` (`handle_notify` → `show_toast`).-Dispatched via the `notify` message; from the workspace use the AD CLI:--```bash-adom-desktop notify_user '{"title":"Build complete","body":"hd_build_rust finished — exit 0","level":"info"}'-```--Payload shape (`NotifyPayload`):--```json-{- "title": "...",- "body": "...",- "level": "info | warning | error | emergency", // default: info- "actions": ["Open log", "Dismiss"] // optional buttons-}-```--| Field | Notes |-|---|---|-| `title` | Toast heading. If it doesn't already start with "Adom", HD prefixes it → `"Adom: <title>"`. |-| `body` | The message. |-| `level` | `info` / `warning` / `error` / `emergency`. `warning`/`error`/`emergency` render as **long-duration** toasts; `info` is short. |-| `actions` | Optional array of button labels rendered as `<actions>` in the toast template. Empty/omitted = no buttons. |--### Levels — and the `emergency` taskbar flash--- `info` / `warning` / `error` — a standard toast (warnings and errors stay up- longer).-- **`emergency`** — in addition to the toast, HD calls- **`request_user_attention(Critical)`** on the main window and shows/unminimizes- it. This produces a **persistent orange taskbar flash that keeps flashing until- the user actually clicks the taskbar icon themselves** — it is *not* cleared by a- programmatic focus. Reserve `emergency` for "I genuinely cannot proceed without- you" — it's intentionally hard to ignore.--### The "Adom" title quirk (AUMID routing)--On Windows, HD's toasts route through a **PowerShell AUMID**. A side effect: toasts-may surface under an **"Adom"** identity/title rather than a per-app one. That's-expected — don't treat an "Adom"-labeled toast as a bug.--## In-app toast — `POST /ui/toast` (inside the HD window)--The notifications above reach the user **outside** the window (OS toast / taskbar).-For a lightweight message **inside** the HD window — the same toast UI the app uses-for its own success/error messages — fire a control-API toast:--```bash-# via the control API (or adom-cli hydrogen api, or the API Explorer "Try it")-POST /ui/toast { "message": "Build finished ✅", "type": "success", "duration": 5000 }-```--| Field | Notes |-|---|---|-| `message` | Required. The toast text. |-| `type` | `info` (default) / `success` / `warning` / `error` — sets icon + color. |-| `duration` | ms before auto-dismiss (default 5000). **0 = persistent** until the user dismisses. |-| `groupId` + `detail` | Optional: coalesce related toasts into one expandable group. |--It emits the `hd-toast` Tauri event, which the main window renders via the global-`toastStore` (bottom-stacked toasts). **OS toast vs in-app toast:** use a `notify`-OS toast when the user may be **away from / not looking at** HD; use `/ui/toast` for-an in-window status cue while they're working in HD. (HD's native recording uses this-for its auto-stop notice — see [hd-recording](../hd-recording/SKILL.md).)--## Pup's taskbar flash (`browser_alert_window`)--For AI-driven browser work, Pup has its own attention nudge:--```bash-adom-desktop browser_alert_window '{"sessionId":"default"}'-```--It **flashes the taskbar** for that browser window (and brings the page to front-within Chrome) **without stealing foreground focus** — a gentle "look here." (Use-`browser_focus_window` if you actually want to raise the window.)--## When you (the AI) should notify proactively--- **Long-job completion** — a build, a batch, a render, a deploy finished. Fire an- `info` toast so the user can wander off and get pulled back.-- **You need the user** — an approval, a decision, a credential. Use `warning`, or- `emergency` if you're fully blocked.-- **Hands-free / driving** — pair a notification with audio (TTS) so the user gets- both a glance-able toast and the spoken answer.--Don't spam: one notification per meaningful event. Verify the bridge first-(`adom-desktop ping`) before claiming you notified them.--## Related skills-- [hd-adom-desktop](../hd-adom-desktop/SKILL.md) — AD is what carries `notify_user` to the host; `ping`/`status` to verify the bridge and the `notify` capability-- [hd-bridges](../hd-bridges/SKILL.md) — the full AD capability list (`notify`, …) and Pup's `browser_alert_window`
skills/hd-open-url/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-open-url/SKILL.mddeleted−210@@ -1,210 +0,0 @@-name: hd-open-url-description: >- The decision guide for "open a URL / open a website" inside Hydrogen Desktop.- When the user says "open ti.com" / "pull up the datasheet page" / "show me- that site", HD has SIX distinct ways to do it — a Hydrogen webview tab, an HD- browser window, Pup (AI-drivable Chrome), a native desktop browser profile,- the Browser Picker chooser, or headless curl/fetch — each with different- trade-offs. READ THIS to pick the right one and to explain the options back to- the user instead of silently guessing. For the interception/plumbing behind the- picker see hd-browser-picker; to drive Pup see pup; for webview tabs see- adom-workspace-control. Trigger words — open url, open website, open the site,- open ti.com, open a link, pull up the page, show me the website, view this url,- open in browser, which browser, how do I open, open the datasheet page, open in- a tab, open in pup, open in chrome, headless fetch, ways to open a url, open- link options, wv, open in wv, open as wv, webview tab, wv tab, webview,- show me in webview, open in webview, in a webview, native webview. NEVER- satisfy a webview request with VS Code's built-in Simple Browser — see- hd-webview for the exact command.--# HD — Opening a URL (your options)--When a user says **"open the ti.com website"** (or any URL), HD has several paths-and a Browser Picker — but **don't hand the user the picker to decide.** Pick the-best destination *for them* by use-case and **open it directly**, then offer the-alternatives in one line. The right path depends on what they're doing — look at-it, have you drive it, keep it as a tab, or just read it. See **"Decide for the-user — DON'T hand them the picker"** below for the rule.--## The six ways--| # | Option | Best when… | What the user sees | AI drives it? |-|---|--------|-----------|--------------------|---------------|-| 1 | **Hydrogen webview tab** (aka **wv**) | the user wants the site *inside* HD, next to their work, and to keep it around | a panel/tab in the HD workspace | partly (reload/route) |-| 2 | **HD Browser Window** | a quick popout window owned by HD, not a full browser | a standalone HD-owned window | no |-| 3 | **Pup (Puppeteer Chrome)** | **you** need to act on the page — screenshot, click, scrape, fill a form, verify | a real Chrome window on their desktop | **yes, fully** |-| 4 | **Native desktop browser** | real logins/sessions, downloads, "just open it for me", auth flows | their own Chrome/Edge profile | no |-| 5 | **Browser Picker** | you're not sure, or it's an auth/OAuth link — let the user choose (5s auto-default) | a chooser dialog | n/a (routes to one of the above) |-| 6 | **curl / fetch (headless)** | you only need the *content*, not to show anything (read a datasheet, scrape JSON) | nothing — runs in the workspace | **yes** |--## "wv" = webview — the shortcut (spread it like `pup`)--## ⛔ NEVER: VS Code's "Simple Browser" is NOT a webview--If the ask is "show me X **in webview**", the ONLY correct destination is option 1-(a native Hydrogen Web View tab, `adom-cli hydrogen webview open-or-refresh`).-**Never** open VS Code's built-in Simple Browser / any editor-internal browser page-— that happened once from a setup prompt (2026-07-16) and it's a banned outcome.-The dedicated `hd-webview` skill has the exact command + verification.--**`wv` is the short alias for "webview"** — recognize it **everywhere** a user might say-"webview" (`open it in wv`, `wv tab`, `shot the wv`, `wv mode`). Treat `wv` and `webview` as-identical.--**Help it catch on:** the *first* time in a session a user types the long form **"webview"**,-open it as normal **and** add a one-line nudge — *"(btw you can just say **wv** — fewer-keystrokes)."* Say it **once**, not every time (don't nag). This is exactly how-**puppeteer → `pup`** became universal among Adom folks; `webview → wv` is the same play.-(If the user already says `wv`, they got the memo — skip the nudge.)--## How you invoke each--All of these run from inside the HD workspace. The control API base URL is the live-value in `~/.adom/hd-control-url` — `http://127.0.0.1:<dynamic>` (WSL2 mirrored-networking shares loopback with the Windows host; the port is dynamic per launch).-Read it with `BASE="$(cat ~/.adom/hd-control-url)"` then hit `"$BASE/<endpoint>"`.--**1. Webview tab (wv)** — add a web-view tab pointed at the URL (`adom-workspace-control`,-or `adom-cli hydrogen webview open-or-refresh --name <X> --url <url> --panel-id <id>`). Good-for "keep this open while we work." ⚠️ **`open-or-refresh` needs `--panel-id` to CREATE a new-tab** — without it you get *"Tab 'X' not found and --panel-id not provided"* (verified live-2026-06-14; get a panel id from `adom-cli hydrogen workspace get`). Re-running with the same-`--name` once it exists just navigates/refreshes (no panel-id needed). Same `adom-cli` on-web-Hydrogen and HD — the consistent surface.--**2 / 4 / 5. Through the Browser Picker** — the single unifying entry point. POST-the URL and HD routes it (auto-picking a sensible default after a 5-second-countdown, or showing the chooser):-```bash-BASE="$(cat ~/.adom/hd-control-url)" # http://127.0.0.1:<dynamic>--# Auto-route (picker picks the best default in 5s — hands-free)-curl -s -X POST -H 'Content-Type: application/json' \- -d '{"url":"https://www.ti.com"}' \- "$BASE/open-url"--# Force the chooser dialog (no auto-timer — make the user pick)-curl -s -X POST -H 'Content-Type: application/json' \- -d '{"url":"https://www.ti.com","force":true}' \- "$BASE/open-url"--# Route through the picker with the FRESH-WINDOW toggle pre-checked (the picked-# native browser opens in a new --new-window that auto-foregrounds — use for-# auth/OAuth so the consent page pops to the front by itself):-curl -s -X POST -H 'Content-Type: application/json' \- -d '{"url":"https://claude.ai/","fresh":true,"force":true}' \- "$BASE/open-url"-# …or bypass the picker and open directly+fresh in a specific browser:-curl -s -X POST -H 'Content-Type: application/json' \- -d '{"url":"https://claude.ai/","browser":"edge","profileDir":"Default","fresh":true}' \- "$BASE/open-in-profile"-```-> ⚠️ **`/open-url` often replies `"HD timed out after 5s"` even when it SUCCEEDED**-> (verified live 2026-06-14: `{"url":"https://www.google.com"}` reported the timeout, but the-> page opened in Edge). **Don't treat that as failure** — confirm with-> `GET "$BASE/browser-picker/last-open"` → `{destination, url, fresh, ts}`. `fresh:false` =-> routed to a remembered/default destination (no picker shown — e.g. `browser:edge:Default`);-> `fresh:true` = the picker was shown. (So if every domain has a remembered default, opens are-> silent — the picker only pops for un-remembered domains.)--The picker's auto-default heuristic: `*.claude.ai`/`*.claude.com` and Adom-`/auth/intent` → the user's work browser profile; unknown/right-click/iframe →-Pup; otherwise the user's last choice for that domain. See `hd-browser-picker`.--**Fresh window (`"fresh":true`)** — the native-browser path (#4) can open the URL-either as a tab in an already-running browser (default) or as a **brand-new window/-process** (`--new-window`). A freshly-launched window **auto-foregrounds** on Windows;-a tab does not. So for **auth flows the user must complete** (Claude/Adom sign-in),-prefer `fresh:true` — the consent page comes to the front by itself, no manual focus.-The Browser Picker defaults the "Open in a fresh window" toggle ON for auth URLs.-On `/open-url`, `fresh` pre-seeds that toggle (the picker still shows; add `direct:true`-to skip it); `/open-in-profile` opens fresh directly. See `hd-browser-picker`.--**3. Pup** — open and then drive it with `adom-desktop browser_*` verbs-(`browser_open_window`, `browser_navigate`, `browser_screenshot`, `browser_eval`,-`browser_reload`, …). Either open the URL directly via Pup, or let the picker-route to Pup. See the `pup` skill for the full verb set.-```bash-adom-desktop browser_eval '{"sessionId":"default","js":"document.title"}'-```--**6. Headless content** — if you only need to *read* the page, skip the browser-entirely:-```bash-curl -sL https://www.ti.com/... # or fetch from your own code-```--## Decide for the user — DON'T hand them the picker--**Default behavior: pick the destination by use-case and open it DIRECTLY (bypass-the picker), then give a one-line heads-up + easy undo.** Do NOT fire a plain-`POST /open-url {url}` and let the picker's generic 5-second auto-default decide —-that default is luck-of-the-draw and often wrong for the use-case (a vendor-login-site could land in Pup where the user has no login). Users don't want to make this-choice; make the smart one for them. (Mechanics: `/open-in-profile` opens a-specific native browser+profile directly; `/open-url {direct:true}` bypasses the-picker; Pup and webview-tab are direct calls. Reserve the picker only as below.)--| Use case | Open it in | How (bypass the picker) |-|---|---|---|-| **Login-likely site** — vendor/account/portal/cart: TI, Mouser, DigiKey, any "my…"/sign-in | the user's **native browser + WORK profile** (their saved login/cookies live there) | `GET /browser-profiles` → find the work profile (non-gmail) → `POST /open-in-profile {url, browser, profileDir, fresh:true}` |-| **Building/testing their own app**, or you need to drive/screenshot/scrape the page | **Pup** (AI-drivable) | open via Pup (`browser_*`) / route `/open-url {url, direct:true}` to Pup |-| **Glance at content alongside their work** | **Hydrogen webview tab** | `adom-workspace-control` add web-view tab |-| **Genuinely ambiguous, OR an OAuth/sign-in the user must complete themselves** | let them choose | `POST /open-url {url, force:true}` — the one time the picker is right |--Then say it in one line, with the undo: *"Opened TI in your Chrome work profile-(where your TI login lives) — say the word for a tab here, Pup, or a different-profile."* Decide, act, offer alternatives — don't ask first.--Example — "open the texas instruments website" (login-likely → native work profile):-```bash-BASE="$(cat ~/.adom/hd-control-url)"-curl -s "$BASE/browser-profiles" # pick the work profile (non-gmail email)-curl -s -X POST -H 'Content-Type: application/json' \- -d '{"url":"https://www.ti.com","browser":"chrome","profileDir":"<work profileDir>"}' \- "$BASE/open-in-profile"-```--## Govern routing policy — the picker is now a true FALLBACK (HD prefs API)--The picker's per-domain memory is a first-class API (HD app behavior), so **the AI OWNS URL-routing** — the picker only pops for a domain with **no stored policy AND no direct call**.-Use this to set policy the user would otherwise click through the Manager for ("always open-DigiKey in my work profile", "stop auto-opening X in Edge").--```bash-BASE="$(cat ~/.adom/hd-control-url)"-# READ the policy map-curl -s "$BASE/browser-picker/prefs"-# → {ok, prefs:{domains:{"<host>":{destination,label}, …}}}-# SET / override one domain-curl -s -X PUT "$BASE/browser-picker/prefs" -H 'Content-Type: application/json' \- -d '{"domain":"digikey.com","destination":"browser:edge:Default","label":"Edge — Default"}'-# FORGET one domain (e.g. "stop auto-opening X in Edge")-curl -s -X DELETE "$BASE/browser-picker/prefs/digikey.com"-# CLEAR all-curl -s -X DELETE "$BASE/browser-picker/prefs"-```--- **Stored value = `{destination, label}`** — NOT `profileDir`/`fresh`.-- **`destination` is an ID:**- - **internal:** `hydrogen-tab` (a webview/wv tab) · `webview-window` · `pup-window`- - **native:** `browser:<browser>:<dir>` — e.g. `browser:edge:Default`, `browser:chrome:Profile 1`. **The profile dir is encoded in the ID** (no separate field).-- **Discover valid native IDs** via `GET $BASE/browser-profiles` (maps each detected profile → its `browser`+`dir`); the 3 internal IDs are fixed above.-- **`fresh` is NOT stored in prefs** — it's a per-OPEN flag (`/open-url {fresh:…}` / `/open-in-profile {fresh:…}`). Don't try to persist it here.-- **Writes hit the live Manager UI immediately** (the backend writes `hd-url-routing-prefs`- localStorage + dispatches `hd-url-routing-prefs-changed`; an open Browser Picker Manager- re-reads at once, and re-reads on open anyway).--**Net:** for a one-off open, *decide and call the direct endpoint* (`/open-in-profile`,-`/open-url {direct:true}`, or a wv/pup call). For a *standing* preference, `PUT` the domain's-policy so even user-initiated link clicks route there silently. The picker is now only a-fallback for the genuinely-undecided.--## Related skills-- [hd-browser-picker](../hd-browser-picker/SKILL.md) — the interception layers + picker dialog behind #2/#4/#5-- `pup` — driving Pup browser windows (#3): navigate, screenshot, eval, reload-- `adom-workspace-control` — adding/managing Hydrogen webview tabs (#1)-- [hd-runtime-mode](../hd-runtime-mode/SKILL.md) — none of this changes between the WSL2 and Docker runtimes
skills/hd-overview/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-overview/SKILL.mddeleted−90@@ -1,90 +0,0 @@-name: hd-overview-description: >- Master index for all Hydrogen Desktop skills. Start here to find the right- skill for any HD topic. Skills are split into a runtime-neutral "shared" set- plus a runtime-specific set (WSL2 by default, Docker as legacy fallback).- Trigger words: HD skills, hydrogen desktop, what skills, find skill, HD help,- HD overview, skill index, HD reference.--# Hydrogen Desktop — Skill Index--Hydrogen Desktop (HD) is Adom's standalone Tauri v2 app that runs the full-Hydrogen experience locally. One exe manages your workspace, VS Code,-Claude Code, the bridges, and the Adom Wiki — zero cloud dependency.--## ⚠️ First: know your runtime — **`hd-runtime-mode`**--HD hosts your workspace one of two ways: a **WSL2 Ubuntu distro `Adom-Workspace`**-(the default) or a **Docker container** (legacy, `HD_RUNTIME=docker`). Almost every-fact below — container vs distro, files, ports, stats, setup steps — depends on-which is active. **Read `hd-runtime-mode` first** to confirm your runtime, then use-the matching runtime-specific skill. HD only installs the skills for the active-runtime, so in a given workspace you'll usually have exactly the right set.--## Shared skills (runtime-neutral — always installed)--| Skill | What it covers |-|---|---|-| **hd-runtime-mode** | WSL2 vs Docker runtime: how to tell which you're in and what differs. Read first. |-| **hd-golden-image** | Your workspace is a pre-baked golden WSL2 image — gallia, the CLIs, claude, code-server, settings, and these skills are baked in (not installed by setup steps); how updates work. **Lightweight by design: tools like ffmpeg/imagemagick aren't baked — install on demand (`sudo apt-get install`) rather than refusing.** |-| **hd-skill-loop** | (Maintainers) The loop to fix a wrong/stale HD skill: verify vs the live HD, fix it live, write it back to the repo, bake it into the next image. |-| **hd-who-am-i** | Answer "who am I / what can I do here" — identity, resources, environment intro. |-| **hd-adom-auth** | Adom login/logout, session token, profile menu, how auth feeds setup. |-| **hd-claude-auth** | How Claude Code signs in: OAuth via Browser Picker, credential backup, the `claude-auth` step. |-| **hd-api** | Control API reference (`127.0.0.1:47084`). Runtime-neutral endpoints + runtime-specific notes. |-| **hd-desktop-sse** | Local workspace API + SSE — how adom-cli inside the workspace controls HD (replaces Carbon locally). |-| **hd-monitor** | Real-time SSE event stream + Monitor-tool patterns for watching workspace mutations. |-| **hd-ui** | HD's UI: menus, dialogs, panels, CDP-driven navigation, brand/CSS conventions. |-| **hd-open-url** | The decision guide for "open a website" — the six ways (webview tab, HD window, Pup, native browser, picker, headless curl) and when to pick each. |-| **hd-browser-picker** | URL interception — every link routes through the Browser Profile Dialog. |-| **hd-permissions** | Why mic/camera/clipboard/notifications "just work" in HD — WebView2 permissions are auto-granted, so webview apps see no Allow prompts. |-| **hd-tab-icons** | Webview tab icon system — `displayIcon`, icon resolution, SVG data URLs. |-| **hd-profile-menu** | The avatar dropdown: profile/repos/molecules links and logout. |-| **hd-adom-desktop** | What HD and Adom Desktop (AD) are and how they work together — AD is your hands on the user's real OS (files, shell, OS screenshots, app launch, bridges). |-| **hd-bridges** | Built-in KiCad/Fusion/Puppeteer bridges + extensible wiki catalog (e.g. Blender) + AD capability list. |-| **hd-eval** | Inject JS into any HD surface (shell/workbench/wiki/Claude) via /eval-in — add-ons, overlays, highlights, drive UI. |-| **hd-adom-menu** | The upper-left Adom logo menu (Settings, Ports, API Explorer, DevTools, Setup/Virgin Reset) + About dialog + version readout. |-| **hd-settings** | HD's Settings dialog + the full preferences tree (theme, vim mode, 3D control style, launch-on-boot, AD window behavior). |-| **hd-file-transfer** | Move files both ways between the WSL2 workspace and the Windows PC. |-| **hd-embedded-ad** | HD ↔ Adom Desktop embedded mode: the tray, why AD is hidden, the footer pill. |-| **hd-self-screenshot** | How the AI screenshots HD panels + any host window (window control, multi-monitor), injects to shotlog, runs visual self-checks. |-| **hd-capture-share** | The screen-share + "AI wants access" approval + countdown the AI triggers to capture the editor; tab/screen scopes, Webview/Pup window mode. |-| **hd-recording** | Record the screen/mic — the in-app `.webm` recorder (→ `recordings/`) and AD desktop recording, with the tab-vs-desktop footgun. |-| **hd-notifications** | Reach the user outside the window — native Windows toasts + emergency taskbar flash via `notify_user`. |-| **hd-captions** | Paint labeled text on the user's screen — a Win32 always-on-top caption overlay for demos / step callouts. |-| **hd-welcome** | The static welcome page shown after setup completes. |-| **hd-skill-catalog** | The catalog of skills your Claude has in this workspace. |-| **hd-overview** | This index. |--## Runtime-specific skills (you get the WSL2 *or* the Docker variant)--These topics exist in two parallel implementations — `wsl2/` (default) and-`docker/` (legacy) — with the same skill names. Trust the one for your active-runtime (see `hd-runtime-mode`).--| Skill | What it covers |-|---|---|-| **hd-container** | Context for Claude running inside the workspace: relay, direct connect, bridge commands, env vars. |-| **hd-topology** | The three-layer architecture and how to reach the workspace (WSL2: `wsl -d Adom-Workspace`; Docker: `docker exec`). |-| **hd-networking** | Port architecture, host access (`127.0.0.1` / mirrored networking), code-server proxy. Canonical port behavior. |-| **hd-ports** | Forwarding workspace services to Windows (WSL2 auto-forward vs Docker `-p` + `/proxy/`). |-| **hd-port-watcher** | The dynamic port-forward daemon for extension OAuth callbacks. |-| **hd-volume** | Where your files live, what persists, how to copy out, what a reset wipes. |-| **hd-container-stats** | The title-bar CPU/RAM "Container" indicator and how its stats are gathered. |-| **hd-setup** | The setup panel, Run All, Rollback, Virgin Reset. |-| **hd-setup-steps** | What each setup step did to prepare your workspace. |--### WSL2-only-| Skill | What it covers |-|---|---|-| **hd-workspace-lifecycle** | WSL distro lifecycle: import/export/terminate/unregister, code-server as an HD-owned child, the never-global-WSL rule. |-| **hd-workspace-monitoring** | Distro + code-server health monitoring, floaty states, the 15s poll, auto-start. |--### Docker-only (legacy `HD_RUNTIME=docker`)-| Skill | What it covers |-|---|---|-| **hd-docker-desktop** | Silently install/upgrade/start/monitor Docker Desktop as invisible infrastructure. |-| **hd-docker-lifecycle** | Docker Desktop + container lifecycle: WSL2 recovery, restart/stop rules, health checks. |-| **hd-docker-monitoring** | Frontend monitoring of the Docker container and Docker Desktop. |
skills/hd-permissions/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-permissions/SKILL.mddeleted−76@@ -1,76 +0,0 @@-name: hd-permissions-description: >- Hydrogen Desktop AUTO-GRANTS every WebView2 permission — microphone, camera,- clipboard-read, geolocation, notifications, sensors — so inside HD a webview- app NEVER shows a "this site wants to use your microphone/camera/clipboard/- location" prompt. READ THIS before building or debugging a webview app that- uses getUserMedia / clipboard / geolocation: do NOT tell the user to click- "Allow" — there is no prompt to click; it's suppressed by design. Distinguish- this browser-permission auto-grant from the SEPARATE in-app screen-share / mic- consent the AI itself requests (that one IS user-approved — see hd-capture-- share). Trigger words — permission, permissions, webview2 permission, allow- microphone, allow camera, allow clipboard, clipboard read, getUserMedia, mic- permission, camera permission, geolocation permission, notifications- permission, sensors permission, site wants to use, permission prompt, click- allow, no permission prompt, auto-grant, auto granted, why no prompt,- permission denied webview.--# HD auto-grants every WebView2 permission--Hydrogen Desktop's webview (Windows / WebView2) registers a-`PermissionRequested` handler that returns **ALLOW for every permission kind** —-microphone, camera, clipboard-read, geolocation, notifications, sensors, etc. So-**inside HD the user never sees a "this site wants to use your microphone /-camera / clipboard / location" dialog.** Webview apps that call `getUserMedia`,-read the clipboard, or ask for geolocation simply *work*, with no prompt.--Evidence: `hd-app/src/lib.rs` (~lines 326-349):--```rust-// PermissionRequested: auto-grant EVERY permission kind (clipboard-// read, mic, camera, geolocation, notifications, sensors, …) so the-// user never sees a WebView2 "site wants to" dialog.-…-args.SetState(COREWEBVIEW2_PERMISSION_STATE_ALLOW)?;-hd_log::hd_log!("[perm] auto-granted permission kind {:?}", kind);-```--Every request is unconditionally set to `COREWEBVIEW2_PERMISSION_STATE_ALLOW`. In-the HD log you'll see lines like `[perm] auto-granted permission kind …` and, at-startup, `[perm] Registered PermissionRequested auto-grant handler`.--## Why HD does this--HD is the user's **trusted desktop app** — they've already chosen to run it.-Intermediate per-site Web permission prompts (especially the clipboard-paste-dialog) are pure friction with no added safety here, since there's no untrusted-third-party browsing context: the surfaces are HD's own panels and the user's own-webview apps. So HD grants them all up front.--## What this means for you when building webview apps--- **Don't instruct the user to "click Allow."** There is no prompt — telling them- to look for one is wrong and confusing. `getUserMedia({ audio: true })`,- clipboard reads, geolocation, notifications all resolve without a dialog.-- If a webview app *isn't* getting mic/camera data, the cause is **not** a denied- permission prompt (HD never denies). Look elsewhere: no device present, the- page served over a non-secure context, a code bug, or the OS-level Windows- privacy toggle for the camera/mic being off for the app entirely.-- This is WebView2/Windows-specific behavior baked into HD's webview host — it's- not something the web page or the user configures.--## NOT the same as in-app screen-share / mic consent--This auto-grant is **only** the low-level WebView2 browser-permission layer.-It is separate from the **screen-share / microphone consent the AI itself-requests** when it wants to capture the user's screen or mic for a session — that-flow **is** explicitly user-approved (the user sees and accepts an in-app consent-UI). Don't conflate them: WebView2 site permissions are silently allowed; the AI's-capture/share request is a real, user-facing approval. See `hd-capture-share`.--## Related skills-- `hd-capture-share` — the SEPARATE, user-approved screen-share / mic consent the AI requests-- [hd-open-url](../hd-open-url/SKILL.md) — opening URLs/webview apps inside HD (where these permissions apply)-- [hd-browser-picker](../hd-browser-picker/SKILL.md) — routing URLs to webviews vs native browsers vs Pup
skills/hd-profile-menu/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-profile-menu/SKILL.mddeleted−83@@ -1,83 +0,0 @@-name: hd-profile-menu-description: HD's profile menu — the user-avatar dropdown in the top-right corner that links to your Adom profile/repositories/molecules pages on adom.inc and contains the logout action. Logout is gated by a confirm dialog and returns you to the HD login page; logging in is the gate to using HD at all (no anonymous use). Use when the user asks about "log out", "logout", "sign out", "Your profile", "Your repositories", "Your molecules", the avatar dropdown, the login page, or "why am I locked out". Trigger words — profile menu, avatar menu, top-right avatar, user menu, log out, logout, sign out, confirm logout, login gate, login page, your profile, your repositories, your molecules, adom.inc profile, hd locked out, hd login required, return to login.--# HD Profile Menu--The profile menu is the dropdown that opens when you click your **circular user avatar in the top-right corner** of HD. It contains links to your Adom-account pages on `adom.inc` plus the logout action.--This is **separate from the Adom menu** (top-left logo). Different audiences, different functions — see [hd-ui](../hd-ui/SKILL.md) for the Adom menu.--## Where your avatar image comes from--- **Logged in via Google** — HD displays the Google profile photo that Google gives us as part of the sign-in payload. You change it by changing your Google account photo.-- **Logged in via direct Adom login** (email/password style) — HD shows the avatar you've uploaded on `adom.inc`. To customize it, click **Your profile** in this menu to open your profile page on adom.inc and change the avatar there.--## What's in the menu--| Item | Icon | Behavior |-|------|------|----------|-| **Your profile** | person | `goTo('/users/<name>')` — navigates the HD webview to your Adom profile page |-| **Your repositories** | folder | `goTo('/users/<name>/repositories')` — your repositories list |-| **Your molecules** | atom / molecule | `goTo('/users/<name>/molecules')` — your molecules list |-| **Log out** | exit-arrow | Opens a confirm dialog → on confirm, clears the Adom session and returns to the HD login page |--> **Note (current behavior, verified 2026-06-12)**: the three "Your *" items call `goTo('/users/<name>/...')` (see `EditorNav.svelte` ~L2540-2557), i.e. they navigate the HD webview itself to the in-app `/users/...` route rather than opening adom.inc in the user's external browser. This is in-app navigation, not the Browser Picker. (The earlier "routes to internal HD views" concern has been narrowed to this expected in-app routing.)--## Log out — the confirm dialog--Clicking **Log out** opens this dialog:--```-┌──────────────────────────────────┐-│ Log Out │-│ │-│ Are you sure you want to log out?│-│ │-│ [Cancel] [Confirm] │-└──────────────────────────────────┘-```--- **Cancel** — dismisses the dialog, you stay signed in-- **Confirm** — wipes `hydrogen-session.txt` from `%APPDATA%\hydrogen-desktop\`, clears in-memory session state, and routes the webview to `/auth/login`--After Confirm: you land on the HD login page and **cannot use HD until you log in again.**--## Login is a gate to using HD--HD is **not usable without an Adom login**. There is no anonymous mode, no skip-login option once you're signed out. The (app)/+layout.svelte enforces this — if the session token is missing or expired, every protected route redirects to `/auth/login`.--The login itself uses the auth-intent flow (no email/password entry in HD — you complete sign-in in your default browser, then HD picks up the session). See [hd-adom-auth](../hd-adom-auth/SKILL.md) for the full flow.--After successful login, HD restores you to the route you were trying to reach (or the dashboard).--## CSS selectors (for CDP eval / Claude automation)--| Element | Selector |-|---------|----------|-| Avatar button (top-right) | `.profile-dropdown-button` |-| Open dropdown | `.profile-dropdown-content` |-| Your profile item | first `.profile-dropdown-content .dropdown-link` |-| Log out item | `.profile-dropdown-content .dropdown-link` whose text is "Log out" |-| Logout confirm dialog | `.confirm-dialog` containing text "Log Out" |-| Confirm button | `.confirm-dialog .confirm-btn`, `button:has-text("Confirm")` |-| Cancel button | `.confirm-dialog .cancel-btn`, `button:has-text("Cancel")` |--```bash-# Open profile menu-echo "document.querySelector('.profile-dropdown-button')?.click()" | node C:\Users\john\hd-cdp.js--# Trigger logout-echo "Array.from(document.querySelectorAll('.dropdown-link')).find(e => e.textContent.includes('Log out'))?.click()" | node C:\Users\john\hd-cdp.js--# Confirm logout (after dialog appears)-echo "Array.from(document.querySelectorAll('button')).find(b => b.textContent.trim() === 'Confirm')?.click()" | node C:\Users\john\hd-cdp.js-```--## Related skills--- [hd-adom-auth](../hd-adom-auth/SKILL.md) — the Adom session token, login flow, profile loading-- [hd-claude-auth](../hd-claude-auth/SKILL.md) — Claude Code auth (separate from Adom auth; logging out of Adom does NOT log you out of Claude)-- [hd-browser-picker](../hd-browser-picker/SKILL.md) — what opens the Your-*/external URLs in the user's chosen browser-- [hd-ui](../hd-ui/SKILL.md) — the Adom menu (top-left, different dropdown)
skills/hd-pup/SKILL.mddeleted−57@@ -1,57 +0,0 @@-name: hd-pup-description: >- PUP — what "pup" means in Hydrogen Desktop and exactly how to open one. When- the user says "show me in pup", "open it in pup", "pup window", "in pup", or- asks to demo/drive ANY page "in pup", it means a real, AI-drivable Chrome/Edge- window opened by Adom Desktop's Puppeteer bridge (auto-installed with HD) on- THIS machine — never a webview (see hd-webview), never the user's personal- signed-in browser (that's nbrowser). CRITICAL HD DIFFERENCE: in HD, localhost- URLs are the NORM for pup (everything binds localhost on the same machine) —- the cloud-container rule "pup can't see localhost" is INVERTED here. Trigger- words — pup, open in pup, show me in pup, in pup, pup window, puppeteer,- pup browser, drive a browser, show the page in pup.--# hd-pup — "pup" is a real drivable browser window on this machine--**pup** = Adom Desktop's Puppeteer bridge driving a fresh, isolated Chrome (or-Edge) process. HD embeds Adom Desktop and auto-installs the bridge, so pup is-always available — no setup questions, just check readiness and go.--## ⚠️ THE LOCALHOST RULE IS INVERTED IN HD (do not import cloud instincts)--- **Web Hydrogen (cloud container):** pup runs on the *user's desktop*, so the- container's `localhost` is unreachable — the bridge sanity-checks localhost- URLs because they're almost always a mistake, and pages get slug/proxy URLs- (`vscode_proxy_uri` style) instead.-- **Hydrogen Desktop (here):** the workspace, HD, and pup all live on the SAME- machine. **~95% of pup URLs are `http://localhost:<port>` and that is- CORRECT.** Never rewrite a localhost URL to a slug/proxy URL, and never- refuse it — HD binds its web servers to localhost by design.--## ✅ How to open a page in pup (from the workspace)--```bash-adom-desktop browser_readiness '{}' # is the bridge ready? which browser?-adom-desktop browser_open_window '{"url":"http://localhost:<port>/...","sessionId":"<task-name>"}'-adom-desktop browser_screenshot '{"sessionId":"<task-name>"}' # verify what's on screen-```--- Reuse ONE window per task via a stable `sessionId` — open tabs in it rather- than spawning window after window.-- pup opens in the background by default; pass `foreground:true` only when the- user should see it appear right now.-- Verify by screenshot, not by assumption: if `browser_screenshot` doesn't show- your page, it isn't open.--## ⛔ Never confuse the three browser surfaces--| Ask | Surface | Skill |-|---|---|---|-| "show me in **webview**" | native Hydrogen Web View tab | `hd-webview` |-| "show me in **pup**" / drive it | isolated Puppeteer Chrome (this skill) | `pup` for deep driving |-| use **my** logins / my real browser | user's signed-in Chrome/Edge | nbrowser / adom-browser-extension |--For deep pup driving (tabs, sessions, recording, eval), the full `pup` skill-applies — this skill exists so the word "pup" always routes correctly in HD.
skills/hd-recording/SKILL.mddeleted−342@@ -1,342 +0,0 @@-name: hd-recording-description: >- How to record screen + window video from Hydrogen Desktop. In HD the recorder is- NATIVE: Windows.Graphics.Capture (per-window, "record kicad") + DXGI full-screen,- hardware H.264 (default, universally playable) or H.265 (smaller, not web-playable)- → mp4 in ~/project/recordings/, with NO picker, NO "you're sharing" banner, real- 30-60fps, and NO display wake-lock. Driven by `adom-cli hydrogen recording- start/stop` (which routes to the native control API) or the control endpoints- directly (POST /recording/native/start|stop, GET /recording/native/status|sources).- A server-side max-duration cap (default 600s) auto-stops + toasts. Read for codec- choice, source selection, the cap+reason, and the "● Recording" indicator. Also- covers AUDIO-ONLY / narration capture (`adom-cli hydrogen audio` → WebM/Opus in- ~/project/audio/), AD host desktop recording (desktop_record_start, Windows-only),- the tab-vs-desktop footgun, and what `--mic` does. Trigger words — record,- recording, screen recording, record my screen, record kicad, record a window,- record whole screen, record the workspace, record a demo, record with mic, record- no audio, mic on, mic off, voiceover, record audio, audio-only, record a narration,- record my voice, narrate, narration track, record a voice track, h264, h265, hevc,- mp4, webm, opus, codec, recordings folder, audio folder, ffmpeg, stitch clips,- max duration, recording indicator, desktop_record_start, browser_record_start,- start recording, stop and save.--# Hydrogen Desktop — Screen & Window Recording--In HD the primary recorder is **native** (Windows.Graphics.Capture + DXGI) — not-`getDisplayMedia`. It produces a real high-fps mp4, asks for no permission, shows no-"you're sharing" banner, and holds **no display wake-lock** (see-[hd-screen-lock](../hd-screen-lock/SKILL.md)). Pick the mechanism by *what* you're-recording — this is the single most important decision and a known footgun.--**DEFAULT — drive it with `adom-cli hydrogen recording start` / `stop`.** That IS the-HD native recorder (h264 mp4 → `~/project/recordings/`). Do **NOT** reach for-`adom-desktop desktop_record_start` for an ordinary "record my screen / window / the-workspace" — that's the AD **host** fallback: it writes a `.webm` into AD's-bridges-cache (`…/Adom Desktop/bridges-cache/…`), **not** your `~/project/recordings/`,-and is only for when HD's native recorder is genuinely busy. "record my screen",-"record the workspace", "record a demo", "record kicad" all → `adom-cli hydrogen-recording`. (Same trap as screenshots: don't fall through to `adom-desktop` for-anything inside HD.)--## Which recorder? — the priority ladder (read first)--Same shape as [hd-self-screenshot](../hd-self-screenshot/SKILL.md): **lead with `adom-cli`-(consistent with web Hydrogen), drop to the richer HD/AD surfaces only for what it can't-do.** Default to HD-native.--1. **HD content OR the whole screen (the 99% case): `adom-cli hydrogen recording start- --share tab|screen` — THE DEFAULT.** The *same* command on web-Hydrogen and HD (web-H- records via `getDisplayMedia`; HD records **natively** via WGC/DXGI — no picker, no "you're- sharing" banner, no display wake-lock). `--share tab` = the HD workspace; `--share screen`- = the entire display (KiCad/Fusion/desktop apps). Saves **h264 mp4 → `~/project/recordings/`**.- Verified live 2026-06-14.-2. **A SPECIFIC WINDOW** (record just KiCad / Fusion, not the whole screen): **HD native- control API `POST /recording/native/start {source:"window:<title-substring>"}`.** adom-cli's- `--share` is **tab|screen only — it has no per-window flag**, so per-window recording is an- **HD-native exclusive** (neither adom-cli nor AD can scope a recording to one window).- Resolve the title via `GET /recording/native/sources`. Same endpoint does `{source:"screen"}`,- `{source:"screen:2"}`, codec, custom cap.-3. **The whole DESKTOP when HD's native recorder is already busy:** `adom-desktop- desktop_record_start {confirmDesktopNotTabRecording:true}`. **Whole-desktop ONLY (no- per-window), via Chrome `getDisplayMedia` → it DOES hold the display wake-lock** (unlike HD- native) and writes a **`.webm` into AD's cache** (not `~/project/recordings/`). AD's own help- says "prefer Hydrogen's `recording start --share screen`" — this is for the- parallel-with-an-HD-capture case, not the default.-4. **A single Chrome tab** (AI-driven Pup browser — a clean per-tab clip): `adom-desktop- browser_record_start {sessionId}` / `browser_record_stop`. CDP screencast, ~50fps, no HUD,- no wake-lock, coexists with everything.-5. **Just audio — a voice / narration track, no video** ("record a narration", "record my- voice"): **`adom-cli hydrogen audio start/stop`** → `~/project/audio/<name>.webm` (Opus).- See "Audio-only" below.--### What each recorder can actually scope (don't assume symmetry with screenshots)-Unlike screenshots — where AD grabs a window *or* the screen — **recording is NOT uniform**:--| Recorder | Window | Full desktop | Browser tab | Backend | Wake-lock | Output |-|---|---|---|---|---|---|---|-| `adom-cli hydrogen recording` (HD) | — (use control API) | ✅ `--share screen` | ✅ `--share tab` | native WGC/DXGI | **NO** | mp4 → `~/project/recordings/` |-| HD native control API | ✅ `source:"window:X"` | ✅ `source:"screen"` | — | native WGC/DXGI | **NO** | mp4 → `~/project/recordings/` |-| AD `desktop_record_start` | ❌ **none** | ✅ whole desktop | ❌ | Chrome `getDisplayMedia` | **YES** | `.webm` → AD cache |-| pup `browser_record_start` | — | ❌ | ✅ one tab | CDP screencast | **NO** | tab clip |--So: **per-window recording = HD-native only; whole-desktop = HD-native (no wake-lock) preferred-over AD (wake-lock); single tab = pup.** When someone says "record a window," that's #2 (HD-control API) — AD cannot do it.---## (a) HD native recording (WGC + DXGI) — the default--**Mechanism:** Rust `crates/hd-control/src/recorder.rs` — Windows.Graphics.Capture-per-window + DXGI Desktop Duplication full-screen → Media Foundation hardware-encoder → mp4 in `~/project/recordings/` (born `adom`-owned). Real 30–60fps at full-source resolution. **No picker, no banner, no display wake-lock.**--### Drive it via adom-cli (the usual way)--```bash-adom-cli hydrogen recording start --share screen --mic false --countdown 0 --reason "demo"-# ... do the thing (wait / drive the app you're recording) ...-adom-cli hydrogen recording stop # → filePath: recordings/recording-<ts>.mp4 (relative to ~/project)-```--**All four `start` flags are REQUIRED** (`--share`, `--mic`, `--countdown`, `--reason`)-— the CLI rejects the call otherwise (it forces you to think through scope, audio,-pacing, intent). `--share screen` records the whole (primary) screen with no approval-dialog (native capture); re-running `start` with the same `--share`/`--mic` skips-straight to countdown. In HD the result JSON carries fields web-Hydrogen doesn't:-`native:true`, `codec` (h264), `maxSeconds`, `reason`, `source` (e.g. `monitor:primary`),-and `pendingFile` / `filePath` (relative `recordings/<name>.mp4`).--⚠️ **The `stop` `_hint` text currently LIES about the format** — it says *"WebM format-(VP9 + Opus)"*, but HD native recordings are **h264 mp4** (the file is `.mp4`, and the-`codec` field says `h264`). Trust the `.mp4` `filePath` + `codec` field, not the hint.-(Known adom-cli bug, on Colby's list.)--### Drive it via the control API (full control — window by name, codec, cap)--The AI can call the control endpoints directly for capabilities adom-cli flags don't-yet expose (a specific window, codec, a custom cap):--```bash-# Discover sources first (resolve a fuzzy name like "kicad")-GET /recording/native/sources # → { windows:[...titles...], monitors:[{index,width,height}] }--# Start — source is the key arg:-POST /recording/native/start { "source": "window:KiCad" } # a window by title substring-POST /recording/native/start { "source": "screen" } # primary monitor ("record whole screen")-POST /recording/native/start { "source": "screen:2" } # monitor by 1-based index--POST /recording/native/stop # → { file: "recordings/<name>.mp4", fileAbsolute, seconds, source }-GET /recording/native/status # → { recording, source, seconds, maxSeconds, reason, remainingSeconds }-```--So **"record kicad"** = `POST /recording/native/start {source:"window:kicad"}` (after-confirming the window title via `/sources`); **"record whole screen"** =-`{source:"screen"}`. No picker, no asking the user which window.--### Codec — H.264 (default) vs H.265--`POST /recording/native/start {"codec": "h264" | "h265"}` (default **h264**):--- **h264** — universally playable: browsers (Chrome/Edge `<video>`), the wiki, every- player. **Use this for anything shared, embedded, or streamed.** (Default.)-- **h265 / hevc** — ~40% smaller at equal quality, BUT not playable in Chrome/Edge- `<video>` (needs hardware/codec support). **Only for archival / local use** where- you control the player.--When unsure, keep h264. The start response repeats this tradeoff in `_hints.codec`.-(adom-cli's `recording start` has no `--codec` flag yet — it always sends h264; use-the control API to choose h265. A `--codec` flag is on the adom-cli wishlist.)--### Audio (microphone)--`--mic true` (adom-cli) / `{"mic": true}` (control API) mixes the **default input-device** (the user's mic, via cpal/WASAPI) into the mp4's audio track (48 kHz AAC) —-for narration/voiceover. WGC is video-only, so HD opens the mic itself and feeds PCM-to the encoder. If no mic is available the recording proceeds **video-only** (graceful).-`--mic false` omits the voice. Notes:-- **`--mic false` is NOT "no audio track."** Either way the native mp4 carries an aac- stream; with mic off it's a harmless EMPTY stream (0 frames, no audio, plays fine) —- a windows-capture limitation, intentionally not stripped to avoid an ffmpeg- dependency on clean VMs. There is **no recorder flag for a fully audio-free file**; if- a user truly needs zero audio, strip it post-hoc (`ffmpeg -i in.mp4 -c copy -an out.mp4`).-- **~1s startup gap:** the audio stream begins once the encoder is created on the first- video frame, so the mic track can be ~1s shorter than the video and slightly lead.- Fine for casual narration; flag if you need frame-accurate sync.-- **adom-cli `_hint` bugs (Colby) — don't be misled:** the `start` hint always says- *"No mic in this recording — pass --mic true"* even when you DID pass `--mic true`;- and the `stop` hint says *"WebM (VP9 + Opus)"* though the file is h264 mp4. Trust the- `.mp4` `filePath` + the `codec` field, not the hints.--### Max-duration cap + "● Recording" indicator (safety nets)--- A **`maxSeconds`** cap (default **600s / 10 min**) is enforced server-side: a- watchdog auto-stops + finalizes the mp4 and fires a UI toast. To override, pass- `maxSeconds` AND a **required `reason`** (e.g. `{"maxSeconds":1800,"reason":"30-min- walkthrough the user asked for"}`) — the reason is shown in the toast when the cap- hits. Overriding without a reason is rejected (400).-- While recording, HD shows a persistent **"● Recording · <source>"** badge so it's- never silently on.-- HD does **not** stop on blur/minimize — recording while HD is backgrounded (to- capture KiCad/Fusion) is the normal case. See [hd-screen-lock](../hd-screen-lock/SKILL.md).--### Taking screenshots WHILE recording (multi-AI-thread capture contention)--Adom users run several AI threads against one HD at once, so a recording started by one-thread and a screenshot requested by another routinely overlap. **Almost every capture-method coexists with a live recording — only the `adom-cli` SSE screenshot path does not.**--- ✅ **Coexist with a recording** (use these while recording): CDP `POST /screenshot`- (HD-native, the default), `adom-desktop desktop_screenshot_window` (AD per-window),- `adom-desktop desktop_screenshot_screen` (AD host GDI), pup `browser_screenshot`.-- ⚠️ **The ONE exception** — `adom-cli hydrogen screenshot panel|workspace|screen` (the SSE- path, `PATCH /current/screenshot`). The moment a native recording is active this returns- **`{ok:false, error:"capture_busy", retry_after_ms, alternatives:[...], _hint}`** instead of- silently failing. **Don't blind-retry it — read `alternatives` and switch to a coexisting- method** (CDP `POST /screenshot` is the drop-in).-- **Pre-flight when unsure:** `GET /capture/availability` reports `recording:{active,source}`,- per-method reliability + `coexists_with_recording`, and a `recommended` method. Check it- before a screenshot if you might be mid-recording.--Full screenshot routing + the coexistence matrix live in-[hd-self-screenshot](../hd-self-screenshot/SKILL.md) ("During a recording"). The takeaway for-*this* skill: **starting a recording does NOT lock out screenshots** — it only redirects the-SSE path to `capture_busy`; every other method keeps working.--## Audio-only — record a narration / voice track (`adom-cli hydrogen audio`)--When the user wants **just their voice, no video** — "record a narration track for-the video", "record my voice", "let me record some audio to narrate this" — use the-dedicated **`audio`** verb, NOT the video recorder:--```bash-adom-cli hydrogen audio enable # activate mic (silent if cached)-adom-cli hydrogen audio start --countdown 0 --reason "narration for the demo video"-# ... user speaks ...-adom-cli hydrogen audio stop # → filePath: audio/<name>.webm (relative to ~/project)-```--- Output is a **WebM / Opus** audio file in **`~/project/audio/`** (the `audio` verb's- WebM hint is **correct**, unlike the video recorder's). Reason is required.-- Other subcommands: `audio status` (mic active? device label), `audio level`- (rms/peak/clipping/gain — warn if clipping), `audio gain <0.0-4.0>` (persists),- `audio disable`.-- Audio-only files land in `audio/`; screen videos land in `recordings/`.--### Stitching / post-processing (ffmpeg) — install it on demand--A common follow-on is "stitch these clips together" or "lay this narration over the-video." **ffmpeg/ffprobe are NOT pre-installed** — the HD workspace image is-intentionally **lightweight**, and the AI adds tools as the user needs them. That's-not a blocker: when you need to concatenate clips, mux a narration `.webm` onto an-`.mp4`, transcode, or probe a file, **just install ffmpeg and proceed** (passwordless-sudo + apt are available; ffmpeg ~6.1 is in the repos):--```bash-sudo apt-get update -qq && sudo apt-get install -y ffmpeg # pulls ffprobe too-```--Don't tell the user "ffmpeg isn't available here" — install it (a few seconds) and do-the job. Same pattern for any other missing tool (imagemagick, pandoc, …): install on-demand rather than declaring it impossible.---## (b) HD's in-app combined recorder (web-Hydrogen / legacy getDisplayMedia)--> **In HD this path is superseded by (a) native recording.** The-> getDisplayMedia-based combined recorder below is the **web-Hydrogen** mechanism-> (and the mic/level-meter detail still applies there). In HD, `adom-cli hydrogen-> recording` and the Recording dropdown route to the native recorder in (a).--Source: `src/lib/components/editor/EditorNav.svelte` (~L2418–2519).-Stores: `src/lib/stores/recordingStore.ts` + `src/lib/stores/audioCaptureStore.ts`.--The **Recording dropdown** in the editor toolbar records the workspace (and/or mic)-into one file.--**Sources** — you choose **Screen** and/or **Mic** (a recording needs at least one-enabled; the **Start recording** button is disabled with "Enable screen sharing or-microphone to start recording" until one is active):-- **Screen** row — shows the current share state; click to set up sharing (this- routes through the Screen Capture share flow — see- [hd-capture-share](../hd-capture-share/SKILL.md)).-- **Microphone** row — shows the selected device label or "Not enabled."--**While recording** the dropdown shows a **live MM:SS timer** (`recordingSeconds`),-a "Recording" status dot, and tags for the active sources (Screen / Mic), with:-- **Stop & save** — finalizes the recording.-- **Discard** — throws it away (`discardCombinedRecording()`).--**Output:** a **single `.webm`** (VP9+Opus preferred, then VP8+Opus / WebM, MP4 as-last resort). `startCombinedRecording()` muxes the screen + audio streams; **Stop &-save** finalizes and **uploads to the container's `recordings/` folder.** If the-upload fails, HD **falls back to a local browser download** named-`recording-<timestamp>.webm`.--**AI-requested auto-open:** `promptRecording(message?)` sets a prompt and the-Recording dropdown can **auto-open** so the user sees what you're asking to record-(default message: "Enable screen sharing and microphone to allow AI recording").-Store state: `{ isRecording, recordingSeconds, hasVideo, hasAudio, promptMessage }`.-API-side finalizers exist too: `stopCombinedRecordingForAPI()` (base64 data URL)-and `stopCombinedRecordingAsBlob()` (raw blob) for programmatic capture.--### Mic device picker + level/gain meter--`audioCaptureStore.ts` drives the microphone:-- **Device picker:** `enumerateAudioDevices()` lists `audioinput` devices;- `startAudioCapture(deviceId?)` selects one; the last device persists as- `adom:lastMicDeviceId`.-- **Level + gain meter:** the store exposes `audioLevel` (0–1 RMS), `rms`, `peak`,- and `clipping` (peak ≥ 0.99) from an analyser node, updated ~every 50 ms — this- is the live mic meter. **Gain** is adjustable via `setAudioGain(v)` (clamped- **0–4**, persisted as `adom:micGain`). Watch `clipping` to warn the user their- mic is too hot.---## (c) AD host desktop recording (Windows-only)--Served by the embedded **Adom Desktop** process (see-[hd-adom-desktop](../hd-adom-desktop/SKILL.md)), not by the HD frontend. These-record the **real OS desktop**, not HD's webview:--```bash-adom-desktop desktop_record_start '{"reason":"Demo recording","confirmDesktopNotTabRecording":true}'-adom-desktop desktop_record_stop-adom-desktop desktop_record_status-adom-desktop desktop_recorder_open # open the recorder UI on the host-```--**Verify the capability first** — desktop recording is **Windows-only** and only-present when AD advertises it:--```bash-adom-desktop status # → .capabilities must include "record"-```--If `record` isn't listed, the host/runtime doesn't support it — fall back to the-in-app recorder (for the workspace) or `browser_record_start` (for a tab).---## Quick reference--| Intent | Command / surface | Output |-|---|---|---|-| **Record whole screen (HD)** | `adom-cli hydrogen recording start --share screen …` or `POST /recording/native/start {source:"screen"}` | **h264 mp4** → `~/project/recordings/` |-| **Record a window (HD), e.g. "record kicad"** | `POST /recording/native/start {source:"window:KiCad"}` | **h264 mp4** → `~/project/recordings/` |-| **Smaller archival recording (HD)** | `POST /recording/native/start {source:…, codec:"h265"}` | **h265 mp4** (not web-playable) |-| Record one AI browser tab | `adom-desktop browser_record_start` (pup) | tab screencast |-| Record host desktop via AD (HD busy) | `adom-desktop desktop_record_start '{"confirmDesktopNotTabRecording":true}'` | host recording |-| Record the workspace (web-Hydrogen) | In-app Recording dropdown (or `promptRecording()`) | `.webm` → container `recordings/` |--## Related skills-- [hd-screen-lock](../hd-screen-lock/SKILL.md) — native capture holds NO display wake-lock; the max-duration cap + "● Recording" indicator-- [hd-self-screenshot](../hd-self-screenshot/SKILL.md) — native CDP screenshots (the 90% "ralph test the webview" case); the capture-contention matrix — which screenshot methods coexist with a recording and which (`adom-cli` SSE) return `capture_busy`-- [hd-capture-share](../hd-capture-share/SKILL.md) — the (web-Hydrogen) screen-share + AI-consent flow incl. the cancelable CountdownToast-- [hd-bridges](../hd-bridges/SKILL.md) — `browser_record_start` (Pup tab recording) and the AD capability list (`record`, `screenshot`, …)-- `pup` — driving the Pup browser windows you'd record per-tab
skills/hd-runtime-mode/SKILL.mddeleted−81@@ -1,81 +0,0 @@-name: hd-runtime-mode-description: >- Which workspace runtime your Hydrogen Desktop is using — a WSL2 Ubuntu distro- (default) or a legacy Docker container (HD_RUNTIME=docker) — how to tell which- one you're in, what changes between them, and which runtime-specific skills to- trust. READ THIS FIRST when anything about the workspace's container/distro,- networking, volumes, ports, or setup steps seems contradictory: the answer- almost always depends on the runtime. Trigger words — runtime, wsl, wsl2,- docker mode, HD_RUNTIME, Adom-Workspace, which runtime, container or distro,- workspace runtime, container runtime unhealthy, am I in docker or wsl.--# HD Runtime Mode — WSL2 (default) vs Docker (legacy)--Hydrogen Desktop hosts your workspace one of two ways. Everything Claude knows-about "the container" — its filesystem, networking, ports, stats, and setup-steps — depends on which one is active. **Check the runtime before trusting any-runtime-specific advice.**--| `HD_RUNTIME` | Runtime | What the workspace is | Setup cascade | State file |-|---|---|---|---|---|-| unset / `wsl2` | **WslDistro — DEFAULT** | WSL2 Ubuntu distro `Adom-Workspace` (`wsl --import`) | 16 steps | `setup-steps-wsl.json` |-| `docker` | DockerDesktop — legacy fallback | Docker container (Docker Desktop) | 24 steps | `setup-steps.json` |--WSL2 is the default — you only get Docker if `HD_RUNTIME=docker` was set before-HD launched. The selector is `runtime/mod.rs::select_runtime()`; the WSL setup-executor is `setup_steps_wsl.rs`, the Docker one is `setup_steps.rs`. Both paths-are kept alive on purpose: the Docker runtime is a pristine fallback in case a-machine can't run WSL2.--## Which runtime am I in? (authoritative checks)--1. **Startup log line:** `[app] Active workspace runtime: WslDistro` (or- `DockerDesktop`). This is the single source of truth.-2. **WSL only:** `wsl -l -v` shows `Adom-Workspace Running`; HD logs- `[wsl-trigger] backend poller armed (...setup-trigger-wsl.json)` and- `[run_all_wsl] ...`.-3. **Docker only:** a `hydrogen-*` container appears in `docker ps`.-4. From inside the workspace, ask HD: `GET "$(cat ~/.adom/hd-control-url)/wsl/status"`- (loopback `http://127.0.0.1:<dynamic>`) succeeds (with distro info) on the WSL runtime.--### ⚠️ Benign noise: `[docker] Container runtime unhealthy — exec failed, needs restart`-This line can appear **even on the WSL2 path** — a legacy `hd-docker` heartbeat-polls for a Docker container that doesn't exist and logs "unhealthy". It is-cosmetic and does NOT mean you're in Docker mode. The real WSL health signal is-`[wsl] Workspace unhealthy …`. Confirm the runtime via check #1, never by this line.--## What actually differs between the runtimes--| Concern | WSL2 (default) | Docker (legacy) |-|---|---|---|-| Workspace | `Adom-Workspace` distro, imported from the pre-baked `adom-golden.tar.gz` | container from a ~390 MB image |-| Run a command in it | `wsl -d Adom-Workspace -u adom -- <cmd>` | `docker exec <container> <cmd>` |-| Your files | `/home/adom/project` in the distro's ext4 fs (no volume); browse from Windows at `\\wsl$\Adom-Workspace\…` | Docker named volume; copy out via `docker cp` |-| Ports | code-server binds `0.0.0.0:7380`; WSL2 auto-forwards `0.0.0.0` listeners to Windows `localhost` | single `-p 7380:8080` map; other ports via `/proxy/<port>/` |-| Reach the host | `127.0.0.1` (WSL2 mirrored networking shares loopback with the Windows host); HD's live control URL is in `~/.adom/hd-control-url` | `host.docker.internal` (Docker-provided) |-| Wipe/reset | `wsl --unregister Adom-Workspace` (only that distro) | `docker rm` + volume/image removal |--## Which skills to trust--HD only installs the skills for the **active** runtime (plus the shared ones), so-in most workspaces you'll already have the right set. The runtime-specific skills-exist in two buckets in the HD repo — `wsl2/` and `docker/` — and share names:--- `hd-networking`, `hd-ports`, `hd-port-watcher`, `hd-volume`, `hd-container`,- `hd-topology`, `hd-container-stats`, `hd-setup`, `hd-setup-steps`-- WSL-only workspace skills: `hd-workspace-lifecycle`, `hd-workspace-monitoring`-- Docker-only skills: `hd-docker-desktop`, `hd-docker-lifecycle`,- `hd-docker-monitoring`--If you ever see Docker-specific advice (`docker exec`, named volumes, `-p` port-maps) while check #1 says `WslDistro`, you're reading the wrong bucket — defer to-the WSL2 mechanics above.--## Switching runtimes (rare; usually a dev/support action)-HD reads `HD_RUNTIME` from its environment at process start.-- → WSL2 (default): ensure `HD_RUNTIME` is unset or `wsl2`, relaunch HD. If a- prior run set Docker: `setx HD_RUNTIME wsl2` then relaunch.-- → Docker (fallback): `setx HD_RUNTIME docker`, relaunch HD (requires Docker- Desktop running).
skills/hd-screen-lock/SKILL.mddeleted−89@@ -1,89 +0,0 @@-name: hd-screen-lock-description: >- HD screen capture (screenshots + recording) and the display wake-lock. A browser- getDisplayMedia capture asserts ES_DISPLAY_REQUIRED, which blocks the user's- screensaver and stops the display turning off → burn-in. HD is NATIVE: its- screenshots use CDP and its recording uses Windows.Graphics.Capture (WGC) /- DXGI — neither holds the display wake-lock — so HD does not have the web leak.- HD still must start→capture→release per op, cap recordings, and show a "●- recording" indicator so a forgotten recording is never silent. Read before- adding or using HD screenshot / screen-recording features. Trigger words: hd- screenshot, hd recording, screen capture, wake lock, screensaver blocked,- display won't sleep, ES_DISPLAY_REQUIRED, release capture, burn-in, powercfg- requests, getDisplayMedia, recording auto-stop, max duration.--# HD screen capture & the display wake-lock--A browser/`getDisplayMedia` screen capture asserts **`ES_DISPLAY_REQUIRED`** →-Windows won't run the screensaver or turn the display off while it's held →-**burn-in**. Web Hydrogen is forced to keep a capture stream alive all session-(the browser re-prompts otherwise), so it **leaks this lock constantly** — that-has cost real nights of burn-in. `powercfg /requests` shows the holder, e.g.-`DISPLAY: msedge.exe — screen capture`.--## HD is native — it does NOT have this leak--HD never uses `getDisplayMedia`:--- **Screenshots** use **CDP** (`Page.captureScreenshot` via `/capture/viewport` and- `/shot`) — a one-shot grab, no `MediaStream`, no wake-lock. See- [hd-self-screenshot](../hd-self-screenshot/SKILL.md).-- **Recording** uses **Windows.Graphics.Capture (WGC)** per-window + **DXGI Desktop- Duplication** full-screen (Rust, `crates/hd-control/src/recorder.rs`). **WGC/DXGI- do NOT call `SetThreadExecutionState` / hold `ES_DISPLAY_REQUIRED`** — so a native- recording does **not** block the screensaver or keep the display awake. See- [hd-recording](../hd-recording/SKILL.md).--So the burn-in failure mode that bites web Hydrogen **cannot happen** from HD's own-capture. The remaining risk is only a *forgotten* recording wasting CPU/disk — which-HD guards with the rules below.--## Rules HD must follow (and already does)--- **Screenshots:** grab the frame → done. Never hold a capture between shots.- (CDP capture is inherently one-shot; there is nothing to release.)-- **Recording:** has a server-side **max-duration auto-stop** (`maxSeconds`, default- **600s / 10 min**; a watchdog finalizes the mp4 and emits `recording-auto-stopped`- → a UI toast). Overriding the cap requires a `reason` so the toast can explain it.-- **Loud indicator:** while a native recording runs, HD shows a persistent **"●- Recording · <source>"** badge (bottom-right) so it's never silently on.-- **No blur-stop for screen recording:** HD being backgrounded is the *normal* case- for native screen recording (you're recording KiCad / Fusion / the desktop), so HD- does **not** stop on blur/minimize — that would break the core use. The- max-duration cap + indicator are the right nets here. (Blur-stop only makes sense- for a `getDisplayMedia` *tab* capture, which HD doesn't use.)-- **Treat a held wake-lock as a bug.** HD shouldn't ever hold one; if you find one,- diagnose with `powercfg /requests` (elevated) — the holder is listed under- `DISPLAY:`.--## If you DO find the display pinned on--1. `powercfg /requests` (elevated) — read the `DISPLAY:` section for the holder.-2. If it's a **browser** (`msedge.exe`/`chrome.exe — screen capture`), that's a- `getDisplayMedia` leak from web Hydrogen or a pup tab capture — stop that capture- (close the tab / `browser_record_stop`), not an HD problem.-3. If it's **HD**, that's unexpected (native capture holds no lock) — capture the- `powercfg` output and treat it as a bug to file against the recorder.--A screensaver can't clear another process's power request, so "release at the-source" is the fix. HD's native capture is release-by-construction.--## Separate concern: capture CONTENTION while recording (multi-thread)--Distinct from the wake-lock: when one AI thread is recording, another thread's-**screenshot** must not silently fail. HD handles this without holding any lock —-CDP `POST /screenshot`, AD `desktop_screenshot_window`/`_screen`, and pup-`browser_screenshot` all **coexist** with a live recording; only the `adom-cli` SSE-screenshot path returns **`capture_busy`** (with `alternatives`) so the caller switches-methods instead of getting an empty frame. Pre-flight with `GET /capture/availability`.-This is a *contention* guard, not a power-state one — full detail in-[hd-self-screenshot](../hd-self-screenshot/SKILL.md) ("During a recording") and-[hd-recording](../hd-recording/SKILL.md).--## Related skills-- [hd-recording](../hd-recording/SKILL.md) — native WGC/DXGI recording, the max-duration cap + reason, the "● Recording" indicator-- [hd-self-screenshot](../hd-self-screenshot/SKILL.md) — native CDP screenshots (no wake-lock)-- [hd-notifications](../hd-notifications/SKILL.md) — OS toasts + the in-app `/ui/toast`-- gallia `screen-capture-wake-lock` — the **web-Hydrogen** side of this issue (where the leak actually happens)
skills/hd-self-screenshot/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-self-screenshot/SKILL.mddeleted−288@@ -1,288 +0,0 @@-name: hd-self-screenshot-description: >- Screenshot HD from inside the workspace — a panel/webview (the welcome page, a- tab), the whole workspace, OR the whole screen/desktop — using the BUILT-IN- `adom-cli hydrogen screenshot`. It saves a PNG into the container's- `screenshots/` folder and prints the path to Read. THE answer for "take a- screenshot of the welcome page", "screenshot the webview", "screenshot this- panel", "screenshot the editor", "screenshot my whole screen / the desktop",- "show me what HD looks like", or verifying a UI change (ralph loop). Use this- even for "my whole screen / my desktop / my monitor" — that's the `screen`- scope, NOT adom-desktop. Do NOT curl host.docker.internal, do NOT use- adom-desktop's OS screenshot for anything inside HD (or for the screen), and you- do NOT need any image resizer. For a SPECIFIC element/region (a CPU meter, a menu,- a dialog, a toolbar) or an inline-b64 PNG, use the canonical `POST /screenshot`- control endpoint with a CSS `selector` (it ALWAYS region-clips to that element) —- and combine it with the UI command bus (`hd-ui`: `ui/invoke` to open a menu/dialog,- then `/screenshot` its selector). Trigger words — screenshot HD, screenshot the- welcome page, screenshot the webview, screenshot a panel, screenshot a tab,- screenshot the workspace, screenshot the editor, screenshot my screen, screenshot- the whole screen, screenshot the desktop, capture my monitor, full screen capture,- screenshot everything on screen, what does HD look like, adom-cli hydrogen- screenshot, capture panel, capture workspace, capture screen, screenshot a region,- clip to selector, screenshot an element, screenshot a menu, screenshot a dialog,- region screenshot, b64 screenshot, POST /screenshot, /shot, screenshot without flash,- no-flash screenshot, hd_shot, hd_screenshot, screenshot KiCad, screenshot another app,- screenshot a non-HD window, desktop_screenshot_window, desktop_screenshot_screen, which- screenshot tool, verify visually, ralph loop, shotlog, image resizer, resize screenshot.--# HD — Screenshot a panel / webview / the workspace / the screen--The built-in way to screenshot anything inside HD is the **`adom-cli hydrogen-screenshot`** command — the *same* `adom-cli` you already use. It captures the-real rendered surface, drops a PNG into the container's `screenshots/` folder,-and prints the path. Just `Read` that path. **No `host.docker.internal`, no-ImageMagick.** (`adom-desktop`'s host verbs DO have a place — other apps, no-flash-grabs, the whole desktop — see the ladder; just not as the default for HD content.)--## Which screenshot tool? — the priority ladder--You have HD-native capture AND AD's host verbs. **What** you're capturing (and whether-you want the teal flash) picks the tool. Default to HD-native:--1. **HD content — a panel, the workspace, the editor, a webview, the screen- (the 99% case): `adom-cli hydrogen screenshot panel|workspace|screen`. THIS IS THE- DEFAULT — reach for it first.** It's the *same* command in web Hydrogen and HD (HD is- meant to behave like web Hydrogen, and `adom-cli` is the one surface that's identical on- both), saves a PNG to your container's `screenshots/`, and you just `Read` it. Don't lead- with an HD-only endpoint.- - **HD-only enhancement / power path — `POST /screenshot {target, selector, b64, silent}`- (CDP).** Use ONLY when you need something `adom-cli` doesn't give you: a precise CSS- `selector` region-clip, an inline-base64 PNG (`b64`), or a `silent` (no-flash) grab — see- below. It does NOT exist in web Hydrogen, so it's a deliberate step OFF the consistent path.- - **The one time you switch to CDP for an ordinary shot:** during another thread's- recording the `adom-cli` SSE path returns `capture_busy` — then fall to `POST /screenshot`- (CDP), which coexists (see "During a recording" below). That's a fallback, not the default.-2. **HD content, NO flash** (clean shot, or HD's CDP/SSE isn't mounted): `adom-desktop hd_shot- '{"region":"full|titlebar|setup-panel|…"}'` (named HD-window regions) or `adom-desktop- hd_screenshot` (the whole HD window). Host-side, deliberately **no teal flash**.-3. **A specific NON-HD app window** (KiCad, Fusion 360, a native dialog — **or an HD Tauri browser- window, which is less-tested**): `adom-desktop desktop_list_windows` → `desktop_screenshot_window- '{"hwnd":N}'`. OS PrintWindow of one window; coexists with a recording. See `hd-bridges`.-4. **Your app in a Pup browser window** (AI-driven Chrome — hero images, clean app shots):- **`adom-desktop browser_screenshot '{"sessionId":"…"}'`** (the `pup` skill). Full-page, no HD- chrome, no flash — separate Chrome, so it coexists with an HD recording.-5. **The whole physical desktop** (every app, taskbar): `adom-cli hydrogen screenshot screen` (HD- scope) OR `adom-desktop desktop_screenshot_screen` (AD host GDI). Both **coexist with a recording**- (independent of HD's WGC/DXGI recorder — corrected 2026-06-14; `ad_screen` can be intermittent- under heavy load).--## During a recording (multi-thread) — pick a coexisting method, never blind-retry--Adom users run **multiple AI threads on one HD**, so another thread may be recording the screen-while you capture. **Only the `adom-cli` SSE path (`screenshot panel/workspace/screen`) is affected**-— everything else coexists. HD makes this deterministic:--- **Pre-flight `GET /capture/availability`** → `{recording:{active,source}, methods:{cdp, ad_window,- ad_screen, pup, sse} each w/ reliability + coexists_with_recording, recommended, _sharing_note,- _hint}`. Check it **only when a recording might be running.**- - **`recommended` is STATE-DEPENDENT (HD v0.1.40+) and agrees with the default:** when- `recording.active === false` it returns **`"sse"`** (≡ `adom-cli hydrogen screenshot`, the- web-Hydrogen-consistent default, rung #1); when `recording.active === true` it returns- **`"cdp"`** (the SSE path would `capture_busy`, so CDP is correct). So you can follow- `recommended` directly — it will never push you off `adom-cli` while idle. (Verified live- 2026-06-14: idle → `recommended:"sse"`.) Your everyday default remains `adom-cli`; CDP is the- during-a-recording fallback.- - **`_sharing_note`:** HD-native capture uses CDP, so there is **no real screen-share to- detect** (any "sharing" field would be a meaningless `false`) — the truthful signal is the note- + the recording status, not a sharing flag. The display-hold / screensaver-block footgun is a- **web-Hydrogen-only** problem (its `getDisplayMedia` holds `ES_DISPLAY_REQUIRED`); if you hit a- pinned display, the fix is **`adom-cli hydrogen sharing stop`**, not anything HD-side. See- [hd-screen-lock](../hd-screen-lock/SKILL.md).-- **`capture_busy`:** the SSE path (`PATCH /current/screenshot`, i.e. `adom-cli screenshot- panel/workspace`) returns, the moment a recording is active, `{ok:false, error:"capture_busy",- retry_after_ms, alternatives:[{method:"cdp"},{ad_window},{ad_screen},{pup}], _hint}` — never an- empty body. **On `capture_busy`: read `alternatives` and SWITCH — do NOT blindly retry the SSE path.**-- **Coexistence matrix (verified live):** `POST /screenshot` (CDP) ✅ *best during a recording* ·- `desktop_screenshot_window` ✅ · `desktop_screenshot_screen` ✅ (medium) · `pup browser_screenshot`- ✅ · `adom-cli screenshot panel/workspace/screen` ⚠️ → `capture_busy`.-- Authoritative recording state: `GET /recording/native/status`. See `hd-recording`, `hd-screen-lock`.--**Quick map:** in-HD → #1 (or #2 for no-flash) · other app → #3 · whole desktop → #4.--## Pick the scope — panel / workspace / screen--| You want… | Command | Sub-command |-|---|---|---|-| ONE panel or webview tab (the welcome page, a viewer, the editor) | `adom-cli hydrogen screenshot panel …` | `panel` |-| The whole HD **workspace** (every panel side by side) | `adom-cli hydrogen screenshot workspace` | `workspace` |-| The whole **screen** — desktop, taskbar, OTHER apps | `adom-cli hydrogen screenshot screen` | `screen` |--**"My whole screen / my desktop / my monitor / everything"** → `adom-cli hydrogen-screenshot screen`. Do **NOT** reach for `adom-desktop` here — that's only for one-SPECIFIC non-HD window (KiCad, Fusion). The HD `screen` scope captures the real-desktop and saves the PNG into your container, same as the others (verified live).--## Do this — it saves a PNG and prints the path; then Read it--```bash-# Whole workspace (simplest — no tab name needed):-adom-cli hydrogen screenshot workspace-# → prints screenshots/<ts>.png (relative to ~/project) → Read ~/project/screenshots/<ts>.png--# Whole screen / desktop (other apps, taskbar):-adom-cli hydrogen screenshot screen --reason "show the desktop"--# A specific panel / webview tab. Tab names are whatever the USER has open — DON'T-# guess (a guess like --name welcome 404s if the tab is actually named "Wiki").-# If you're not certain the tab exists by that name, LIST tabs first:-adom-cli hydrogen workspace get # shows every panel/tab: its id AND name-# then capture by --tab-id (most reliable) or the EXACT name you saw:-adom-cli hydrogen screenshot panel --tab-id <tabId>-adom-cli hydrogen screenshot panel --name "3D Viewer"-```--The welcome page is *usually* a tab named "Welcome", but on an active workspace it-may be replaced (e.g. by a "Wiki" tab) — so if `--name welcome` 404s with-`No tab matching 'welcome'`, run `adom-cli hydrogen workspace get` and screenshot-the webview tab you find by its `--tab-id`.--### What it returns — a saved path; just Read it-The command saves the PNG into the container's `screenshots/` folder and prints the-path. **Read it — done.** No decoding, no resizer, no second tool. The printed path-is **relative to `~/project`** (HD v0.1.27+ matches the cloud contract); the `_hint`-gives the full path:--```-screenshots/2026-06-13T20-04-43.png-_hint: Screenshot saved to ~/project/screenshots/<ts>.png. Read the file to view it.-```-→ `Read ~/project/screenshots/<ts>.png` (prepend `~/project/` to the line-1 path, or-just use the full path from the `_hint`).--(Legacy/edge case: an older HD build could return a doubled `~/project/~/project/`-prefix, or inline JSON with `"dataUrl"` + `"uploadFailed": true` — both fixed in-v0.1.27. If you ever see the `dataUrl` shape, base64-decode it to a .png and Read it;-don't build the capture around it.)--### If a capture errors, retry once-A COLD capture can occasionally fail with a proxy URL containing a literal `{{port}}`-(e.g. `…/proxy/{{port}}/: error sending request`) — a known **adom-cli** cold-start-race (the proxy port isn't substituted yet). Just **run the same command again**; the-warm retry succeeds. Don't switch tools or reach for adom-desktop over this.--## Sharing must be active (it usually is; here's the gate)--Capturing a panel/workspace needs an active tab/screen share. Check and, if-needed, the `--reason` you pass pops a one-time approval the user clicks:--```bash-adom-cli hydrogen screenshot status # → {"sharing":{"active":true},"availableScopes":["panel","workspace"]}-# If a panel/workspace shot 409s or says sharing isn't active, just include-# --reason "<why>" (it triggers the approval dialog), or explicitly request it:-adom-cli hydrogen sharing request --share tab # panel/workspace scopes-adom-cli hydrogen sharing request --share screen # adds the 'screen' scope-```-The `screen` scope is for the whole desktop; `panel`/`workspace` come from tab-sharing. See `hd-capture-share` for the sharing/approval UX.--**Release SCREEN sharing when you're done.** A `screen` share (getDisplayMedia) holds-the display **awake** — leaving it active blocks the user's screensaver. After a-one-off `screen` capture, run `adom-cli hydrogen sharing stop` (and close any Pup-windows you opened). Tab `panel`/`workspace` sharing doesn't hold the display, so this-only matters once you've requested the `screen` scope.--## Why this works the same in HD as it does for web Hydrogen--`adom-cli` is **one binary** used by both web Hydrogen and HD — you can test these-commands anywhere. The difference is purely *where the work runs*: HD intercepts-what adom-cli talks to. `adom-cli hydrogen <cmd>` hits `ADOM_HYDROGEN_URL`, which-in HD points at **HD's local proxy** (`http://127.0.0.1:<proxy>`). HD's proxy-pulls out the **local hydrogen/SSE commands** (screenshot, webview, caption,-notify, recording…) and runs them against the HD frontend over SSE, while-**carbon/cloud calls pass straight through** to the cloud. For a screenshot:-`PATCH /current/screenshot` → HD broadcasts an SSE `screenshot_request` → the-frontend Element-Captures the panel → uploads it into **this container's**-`screenshots/` folder → the CLI returns the saved path. So the command, and the-returned path, behave the same — the bytes just land in your local container.--## What NOT to do (these are the traps)--- **Don't curl `host.docker.internal`.** That's a Docker-ism HD does not use. HD's- host services are reached at `http://127.0.0.1:<port>` (WSL2 mirrored- networking); the live control URL is in `~/.adom/hd-control-url`. But for- screenshots you don't need any of that — use the CLI above.-- **Don't use `adom-desktop desktop_screenshot_*` for an HD panel/webview.** That- grabs the whole Windows window/desktop, not the panel — it's the wrong tool for- anything inside HD. Use it only for **non-HD** apps (KiCad, Fusion, a native- window — see `hd-bridges`).-- **You do NOT need an image resizer.** `adom-cli hydrogen screenshot` produces a- normal PNG and the `Read` tool reads PNGs directly. (Don't reach for ImageMagick- `convert` or Pillow — neither is guaranteed in the golden image, and you don't- need them.)--## Capture ANY region, element, or surface — `POST /screenshot`--`adom-cli hydrogen screenshot` above is the easy "save a panel/workspace/screen PNG"-path. For **precise control** — a specific VS Code or webview surface, **any CSS-region** (a meter, a menu, a dialog, a toolbar), or an inline PNG with no flash — use-HD's **canonical capture endpoint `POST /screenshot`** (aliases: `/shot`,-`/capture/viewport`). Read the control URL from the FILE — it's the dynamic port, and-your non-login Bash shell won't have the env var:--```bash-CTRL="$(cat ~/.adom/hd-control-url)" # http://127.0.0.1:<dynamic>-curl -s -X POST "$CTRL/screenshot" -H 'Content-Type: application/json' \- -d '{"target":"full","b64":true}'-```--| Field | Meaning |-|---|---|-| `target` | the surface — `full`/`window` (whole HD window) · `vscode`/`editor`/`workbench`/`code` (the VS Code panel) · `shell`/`hd` (the Svelte shell: title bar, menus, panels) · a **webview url-substring** (e.g. `wiki`) · **or a TAB DISPLAY NAME** (e.g. `"ScreenA"`, `"VS Code"`) — see by-name note below |-| `selector` | **optional CSS selector — it ALWAYS WINS and region-clips to that element.** e.g. `.resource-bars` (CPU/RAM meter), `iframe[src*='wiki']`, `.setup-panel`, `.dialog`, `.dropdown-content.open` |-| `b64` | `true` → returns the PNG inline as a `dataUrl` (decode + Read); else saved to a file in `screenshots/` |-| `silent` | `true` → suppress the capture flash |-| `name` | optional label for the saved file |--A `selector` clips to the **first matching element** in that surface — grab JUST a-meter, a menu, a dialog, a toolbar instead of the whole window (verified live:-`{selector:".resource-bars"}` → 192×61, vs `{target:"full"}` → the whole window).--**Screenshot a tab by its display NAME — incl. a BACKGROUND tab (fixed in HD v0.1.42).**-`POST /screenshot {target:"<tab name>"}` resolves the name → the workspace panel, **activates-that tab** (so a *background* tab actually paints — a webview is an OOPIF and can't be CDP-shot-while hidden), captures the shell cropped to that panel, then **restores the previously-active-tab**. So in a pane stacked `VS Code | ScreenA | ScreenB` (ScreenB showing), `{target:"ScreenA"}`-returns ScreenA's pixels and leaves ScreenB active. **Verified live 2026-06-14:** CDP *and* the-`adom-cli` SSE path (`screenshot panel --name ScreenA`) both returned the background tab's content-(example.com, not the active httpbin tab) and restored the prior tab — `target:"<name>"` was-NO-PNG before v0.1.42. (The `adom-cli` SSE path has always activated; v0.1.42 brings the CDP-`/screenshot` by-name path to parity.) This is the right call for "shot tab #2 while #4 is showing."--**The teal zap:** every HD-driven capture flashes a teal (`#00b8b0`) highlight on the-captured **region** (just the panel/menu/region; full-window flash only for-`target:full`). Teal = HD-driven. (The `adom-desktop` host verbs `hd_shot`/`hd_screenshot`-deliberately do NOT flash — different tool.) Pass `silent:true` to suppress it.--### Screenshot a menu / dialog / panel — open it, then clip to it-The killer combo with the UI command bus (see **`hd-ui`**): **open a surface →-screenshot just that surface → close it** — no screen-clicking, no guessing where it is.-```bash-CTRL="$(cat ~/.adom/hd-control-url)"-curl -s -X POST "$CTRL/ui/invoke" -H 'Content-Type: application/json' -d '{"id":"adom-menu.open"}'-curl -s -X POST "$CTRL/screenshot" -H 'Content-Type: application/json' -d '{"target":"shell","selector":".dropdown-content.open","b64":true}'-curl -s -X POST "$CTRL/ui/invoke" -H 'Content-Type: application/json' -d '{"id":"adom-menu.close"}'-```-Resolved selectors (verified live): **Adom menu & profile menu** → `.dropdown-content.open`-(only one open at a time); **Setup panel** → `.setup-panel`; **API Explorer** → `.dialog`.-`GET $CTRL/ui/actions` for the live set of menus/dialogs you can open (see `hd-ui`).--## Ralph loop + shotlog (optional)--Verify a UI change: make the change → (rebuild if needed) →-`adom-cli hydrogen screenshot panel --name <panel>` → `Read` it → wrong? fix and-repeat. Log shots for the user with `shotlog inject -c hd-ui -d "<desc>" <path>`-(shotlog resizes internally — another reason you don't need `convert`).--## Host window control (for NON-HD windows only)-`adom-desktop desktop_list_windows` → `desktop_set_window_state {hwnd,state}` →-`desktop_bring_to_front {hwnd}` → then `desktop_screenshot_window`. Windows-only.--## Related skills-- [hd-capture-share](../hd-capture-share/SKILL.md) — the sharing/approval UX behind `screenshot`-- [hd-desktop-sse](../hd-desktop-sse/SKILL.md) — the adom-cli → HD-proxy → SSE interception model-- [hd-recording](../hd-recording/SKILL.md) · [hd-captions](../hd-captions/SKILL.md) — sibling `adom-cli hydrogen` AV verbs-- [hd-bridges](../hd-bridges/SKILL.md) — `desktop_screenshot_*` for NON-HD apps-- `shotlog` — the screenshot log viewer/injector
skills/hd-self-update/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-self-update/SKILL.mddeleted−50@@ -1,50 +0,0 @@-name: hd-self-update-description: "How the AI keeps the Hydrogen Desktop APP itself (the Windows/macOS frontend, not the workspace) up to date — AI-first, consent-based. Use when asked \"is there an HD update\", \"update Hydrogen Desktop\", \"is my HD current\", \"what version of HD am I on\", \"update the app\", \"new HD version\". Trigger words: HD update, update hydrogen desktop, app update, is HD current, HD version, new version of HD, relaunch to update, self-update."--# HD Self-Update — the AI updates the app for you (consent-based)--This is how the **Hydrogen Desktop app itself** (the Windows/macOS shell around your editor)-stays current. It is NOT the `workspace-updater` (that's the in-distro tooling). It's **AI-first**:-there is no nagging button — the AI notices a new HD version, tells you both version numbers, and-on your OK downloads + relaunches onto the new version.--## When to check-Check at the **start of a session** and whenever the user asks about updates. Don't poll-constantly.--## How to check (know both version numbers)-```-GET http://127.0.0.1:<control-port>/updater/status-```-Returns `{ current_version, latest_version, update_available, staged, ... }`:-- `update_available: true` → a newer HD exists.-- `staged: true` → it's already downloaded + SHA-verified in the background, so applying is an- **instant relaunch** (no wait).-(`current_version` is also derivable from `/buildinfo`; `latest_version` comes from the wiki-`version.json`. `/updater/status` gives you both in one call.)--## What to do-If `update_available`, **tell the user both versions and ASK** — e.g.:-> "Hydrogen Desktop **vX → vY** is available (already downloaded). Want me to update and relaunch?-> Takes a few seconds; this workspace and our chat reconnect right after."--On **explicit consent**:-```-POST http://127.0.0.1:<control-port>/updater/install-```-HD applies the staged update (instant if `staged`, else downloads first) and relaunches onto the-new version. **The Linux workspace + code-server keep running**, so your session reconnects in a-few seconds — you can pick the conversation right back up.--## Rules-- **Consent-first.** Never relaunch HD without the user's OK (it restarts their app). Pick a- natural break — not mid-task.-- **State both versions** (current → latest) so the user knows what's changing.-- This updates the **app shell only**. Editor extensions/code-server use the reload banner;- in-workspace tooling/skills use the `workspace-updater`. Don't confuse the three.--## Resolving the control port-Read it from `VSCODE_PROXY_URI` / the HD `[ports]` log line (default control API `47084`), or-reach it through the relay/proxy like other HD control endpoints.
skills/hd-settings/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-settings/SKILL.mddeleted−110@@ -1,110 +0,0 @@-name: hd-settings-description: >- Hydrogen Desktop's Settings dialog and the full preference tree the user can- change — theme, vim mode, launch-on-boot, console-on-startup, 3D control style,- font, line numbers, schematic nudge, the embedded Adom Desktop options, and- more. READ THIS when the user asks "how do I turn on vim mode", "change the- theme", "make HD launch on boot", "stop Adom Desktop's window popping up",- "switch to blender/solidworks orbit controls", "hide line numbers", "show the- console on startup", "make the UI compact", or "where are the settings". Opened- from the Adom logo menu → Settings. Trigger words — settings, preferences,- settings dialog, options, config, theme, dark mode, light mode, high contrast,- compact ui, developer mode, vim mode, vim emulation, line numbers, code font,- editor font, launch on boot, start with windows, show console, console on- startup, 3d controls, control style, fusion solidworks blender orbit, schematic- nudge, auto approve, foreground hd, adom desktop window, turn on, turn off,- change setting, how do I configure.--# HD Settings — the preference tree--Open it from the **Adom logo menu** (upper-left teal logo) → **Settings** (see-[hd-adom-menu](../hd-adom-menu/SKILL.md)). The dialog is-`src/lib/components/editor/settings/SettingsDialog.svelte`; the canonical tree of-sections and keys lives in `src/lib/stores/settings.ts` (`settingsTree`).--Preferences are saved per-user (`saveUserSetting` → `PATCH /user/hydrogen/settings`)-and persist across sessions. Setting a key back to its default removes the-override. Keys are dotted (`section.subsection.name`).--> **The Settings search box is currently a no-op** — it's a placeholder input-> with a `<!-- TODO: Implement settings search -->` in `SettingsDialog.svelte`.-> Typing in it filters nothing yet; scroll/scan the sections instead.--## Sections, keys, and what each does--### general-| Key | Default | What it does |-|---|---|---|-| `general.developer_mode` | `false` | Enables extra developer/debugging features in the app |-| `general.pane_focus_mode` | `true` | Only the focused pane is active + highlighted; turn OFF to keep every pane always active |--**general.appearance** (subsection)-| Key | Default | What it does |-|---|---|---|-| `general.appearance.theme` | `adom-dark` | App-wide theme. Options: `adom-dark`, `adom-light`, `adom-dark-high-contrast`, `adom-light-high-contrast` (→ "change the theme" / "light mode" / "high contrast") |-| `general.appearance.user_interface_style` | `standard` | UI compactness: `standard` or `compact` (→ "make the UI more compact") |--### desktop *(Hydrogen Desktop app itself)*-| Key | Default | What it does |-|---|---|---|-| `desktop.show_console_on_startup` | `false` | Show the debug console window when HD starts (→ "show the console on startup") |-| `desktop.launch_on_boot` | `true` | Auto-launch HD when Windows starts (→ "make HD launch on boot" / "stop HD starting with Windows" = set false) |--### adom_desktop *(the embedded Adom Desktop / AD that HD runs)*-When HD is running it takes over AD, so AD's options live here. See-[hd-adom-desktop](../hd-adom-desktop/SKILL.md) for the HD↔AD relationship.-| Key | Type / default | What it does |-|---|---|---|-| `adom_desktop.open` | action button | "Open Adom Desktop" — brings the AD window to the foreground (persists nothing) |-| `adom_desktop.auto_approve_when_running` | bool, `true` | While HD runs, keep AD in permanent auto-approve mode (auto-accept AD permission prompts) |-| `adom_desktop.show_window_on_launch` | bool, `true` | Show AD's window + taskbar icon when HD launches, so you can see it's running (→ "stop AD's window popping up" = set false) |-| `adom_desktop.foreground_hd_on_launch` | bool, `true` | Bring HD to the front on launch; AD still shows in the taskbar but stays behind HD |--### schematic *(Schematic Workspace)*-| Key | Default | What it does |-|---|---|---|-| `schematic.nudge_amount.small` | `1` px | Arrow-key nudge distance for components |-| `schematic.nudge_amount.large` | `8` px | Shift+arrow nudge distance |-| `schematic.invert_zoom_direction` | `false` | Invert mouse-wheel zoom direction |-| `schematic.default_cursor` | `crosshair` | Default cursor: `crosshair` or `pointer` |--### 3d *(3D Settings)*-| Key | Default | What it does |-|---|---|---|-| `3d.control_style` | `fusion` | Orbit/pan/zoom control scheme: `fusion`, `solidworks`, or `blender` (→ "use blender/solidworks controls") |-| `3d.invert_zoom_direction` | `false` | Invert mouse-wheel zoom direction in 3D |--### code *(Code Workspace)*-| Key | Default | What it does |-|---|---|---|-| `code.appearance.font.family` | `DM Mono` | Code editor font family (→ "change the code font") |-| `code.line_numbers.enabled` | `true` | Show line numbers (→ "hide line numbers" = set false) |-| `code.line_numbers.style` | `absolute` | Line-number style: `absolute` or `relative` |-| `code.vim_emulation_mode` | `false` | Vim motions in the code editor (→ "turn on vim mode" = set true) |--### simulator *(Simulator Workspace)*-| Key | Default | What it does |-|---|---|---|-| `simulator.code_editor_position` | `right` | Code editor position relative to the 3D environment: `right` or `left` |--### extensions-Present in the tree but currently has **no properties** (reserved for future-extension-specific settings).--## Answering common asks--- **"Turn on vim mode"** → `code.vim_emulation_mode` = true-- **"Change the theme / dark mode / high contrast"** → `general.appearance.theme`-- **"Make HD launch on boot"** / "don't start with Windows" → `desktop.launch_on_boot`-- **"Stop Adom Desktop's window popping up"** → `adom_desktop.show_window_on_launch` = false-- **"Blender / SolidWorks orbit controls"** → `3d.control_style`-- **"Hide line numbers / change code font"** → `code.line_numbers.enabled` / `code.appearance.font.family`-- **"Show the console when HD starts"** → `desktop.show_console_on_startup`-- **"Make the UI compact"** → `general.appearance.user_interface_style` = compact--## Related skills-- [hd-adom-menu](../hd-adom-menu/SKILL.md) — the logo menu that opens Settings (and Ports, API Explorer, etc.)-- [hd-ui](../hd-ui/SKILL.md) — the broader HD UI / panel layout this dialog sits in-- [hd-adom-desktop](../hd-adom-desktop/SKILL.md) — what the `adom_desktop.*` prefs control (the embedded AD)
skills/hd-setup-steps/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-setup-steps/SKILL.mddeleted−134@@ -1,134 +0,0 @@-name: hd-setup-steps-description: What HD's setup steps did to prepare your workspace — the install sequence that provisions the golden workspace image, injects your Adom session, wires up the relay, and walks the Claude auth gate. Use this skill when the user asks "what did setup do", "why is X installed", "re-run a setup step", "what's a virgin reset", or "why did step N fail". Trigger words — setup steps, install steps, setup panel, virgin reset, re-run step, Run All, what did setup do, why is X installed, setup failed, install-tools, hd setup, claude code extension install, skills install, hd workspace ready.--# HD Setup Steps — what was done to prepare your workspace--This is the platform-generic, user-facing Q&A about what HD's setup did. The-exact per-platform step list, the workspace-provisioning commands, and the-state-file name live in the companion [[hd-setup-steps-windows]] skill (for-Windows/WSL2).--When you first launched Hydrogen Desktop (or after a virgin reset), HD ran an-install sequence to turn a blank machine into a fully-tooled Adom workspace.-The heavy tooling (code-server, the Adom CLIs, the adompkg skills, the claude CLI + Code-extension, VS Code settings, and all `hd-*` skills) is already BAKED into the-golden image; setup just provisions that image, injects your Adom session,-wires up the relay, and walks the Claude auth gate. This skill explains what-each phase did so you can help the user understand their environment, re-run-failed steps, or wipe and rebuild. See `hd-golden-image` for the baked-image-model.--The cascade is halt-on-failure, resume-not-restart, with per-step 3x-auto-retry. The exact steps and ordering are platform-specific — see-[[hd-setup-steps-windows]].--## The setup state machine — what each phase does--| Phase | Purpose |-|---|---|-| Provision the workspace | Ensure the runtime is available (install it if needed), provision the workspace from the golden image, create the workspace user, start code-server, verify host loopback. SUBSUMES the old pull-image / create-instance / start-instance steps. |-| wait-codeserver | Host TCP probe of the code-server port until it answers |-| install-adom-vscode | "Activate editor extensions" — installs NOTHING (binary + extension are baked); waits for Adom sign-in, reloads the editor iframe, and proves the editor-control API answers |-| set-env-vars | Set `ADOM_CARBON_URL`, `ADOM_HYDROGEN_URL`, `ADOM_HD_CONTROL_URL`, `VSCODE_PROXY_URI`, `ADOM_DESKTOP_MODE` |-| inject-api-key | Write the Adom session token into the workspace so adom-cli works |-| configure-vscode | settings.json / trusted-domains / activity-bar are BAKED; re-asserts theme + workbench backstops idempotently, and applies the per-session layout |-| ensure-adom-desktop | Verify the Adom Desktop companion app is running (the relay's desktop bridge) |-| start-relay | Start the adom-desktop relay inside the workspace |-| test-direct-connect | Prove the fast workspace→desktop command path |-| test-relay | Register the relay with Adom Desktop for file streaming (file transfer + shell exec) |-| test-adom-cli | GATE: carbon path + hydrogen-proxy reachability via adom-cli |-| claude-auth | The single human gate — restore Claude creds or drive the in-editor Claude.ai sign-in via the native browser Browser Picker; runs LAST before the payoff steps |-| ensure-sse | GATE: confirm the editor browser SSE session is connected so Welcome's webview-open doesn't 409 |-| verify-workspace | GATE: battery confirming the proxy holds a real layout |-| welcome | Open Claude Code, authenticate if needed, send the first prompt |-| open-welcome | GATE (final): open welcome.html in HD's right pane; hard-fails if the Welcome tab doesn't appear |--### Steps that NO LONGER EXIST (baked into the golden image)--These are NOT setup steps anymore — they are baked at image-build time:-`install-hd-skills`, `verify-adom-desktop`,-`install-claude-cli`, `install-claude-ext`, `write-vscode-settings`,-`set-trusted-domains`, `clean-layout`. If a user asks "what installed-the Claude extension / my skills / VS Code settings", the answer is: **baked-into the golden image at build time, not a setup step.** See `hd-golden-image`.--## Common user questions--**"Why is X installed in my workspace?"** → If X is an adompkg skill, an Adom CLI, the-claude CLI, the Claude Code extension, VS Code settings, or an `hd-*` skill, it-was **baked into the golden image** — not run as a setup step. The setup steps-only provision the image, inject your session, wire the relay, and walk Claude-auth. The setup panel (HD UI → bottom) lists every step with its current-status.--**"Setup failed at step N"** → The setup panel shows the error. Most common-failures: the provision step (runtime unavailable, or the golden-image-download), test-adom-cli (carbon/hydrogen-proxy not reachable yet), and-claude-auth (Claude credentials expired). Each step has a retry button (and the-cascade already retried it 3x). Platform-specific failure causes are in-[[hd-setup-steps-windows]].--**"Re-run a single step"** → In the setup panel, click the step row and use the-Re-run action. Or via API: `POST http://127.0.0.1:47084/setup/step/<id>`-(optional body `{"continue_after":true}` to keep going after it).--**"Re-run all steps"** → Click "Run All" in the setup panel; it resumes from the-first not-done step. (The old headless `POST /setup/run-all` is now REFUSED.)--## Virgin reset--Virgin reset wipes parts of your workspace so setup can rebuild them cleanly.-The ONLY programmatic trigger is `POST /setup/panel/run-virgin-reset`. Toggle-which artifacts to delete (option keys in parentheses):--| Toggle (key) | What gets wiped | When to use |-|---|---|---|-| Install step state (`install_state`) | Marks all steps as pending so they re-run | Always (default on) |-| Workspace (`container`) | PRISTINE wipe + re-provision — deletes the workspace AND everything in it, including your project files | Always (default on); your work is GONE |-| Image tarball (`tarball`, legacy `image`) | Deletes the cached golden image (full re-download) | Rarely — only if the image is corrupted |-| Webview storage (`webview_storage`) | Queued wipe, flushed at next HD launch (restart_required) | Rarely |-| VS Code state (`vscode_state`) | Queued wipe, flushed at next HD launch (restart_required) | Rarely |-| Adom session token (`adom_token`) | Deletes `hydrogen-session.txt`; forces re-login | Rarely |-| Claude credentials (`claude_token`) | Deletes the host-side Claude creds backup; forces Claude re-auth | When Claude auth is stuck |--`confirmed_destructive` must be set. After toggling, hit "Wipe Selected", then-"Run All" to rebuild. The workspace reset backs up Claude creds first UNLESS-`claude_token` is checked; the `webview_storage` / `vscode_state` wipes are-queued and flushed at the next HD launch (restart_required), not immediate.--CRITICAL: the workspace reset ONLY ever wipes HD's own workspace. It never-touches other workspaces, distros, or runtimes the user may have on the-machine, and it performs NO reboot for the wipe. The platform-specific wipe-commands are in [[hd-setup-steps-windows]].--## Setup panel APIs (useful for AI automation)--| Endpoint | What it does |-|---|---|-| `GET /setup/state` | Current state — `never-run`, `in-progress`, `complete`, or `error` |-| `POST /setup/step/<id>` | Re-run a single named step (optional `{"continue_after":true}`) |-| `POST /setup/panel/run-virgin-reset` | Trigger virgin reset with the supplied toggle options |-| `POST /setup/panel/show` | Open the setup panel from anywhere |--All on HD's control API at `http://127.0.0.1:47084`.--**DEPRECATED — now REFUSED, do not use:** `POST /wsl/unregister`,-`POST /setup/virgin-reset`, `POST /setup/run-all`, `POST /setup/run-step`. Use-`POST /setup/panel/run-virgin-reset` and `POST /setup/step/<id>` instead.--## Setup state file--Setup writes its state to a per-step state file (one entry per step with-`status: pending|running|done|failed`, output text, percent complete). HD reads-this on launch to decide whether the setup panel auto-opens. The exact file-name and location are platform-specific — see [[hd-setup-steps-windows]].--## Related skills--- [[hd-setup-steps-windows]] — the concrete WSL2 16-step cascade, the import step, `wsl --unregister`, `setup-steps-wsl.json`, and the golden tarball-- `hd-golden-image` — the baked-image model: what's pre-installed vs. what setup does-- `hd-setup` — the setup-panel UX and virgin-reset toggles-- `hd-browser-picker` — used by claude-auth for the Claude auth OAuth flow-- `hd-adom-auth` — how the Adom session token (inject-api-key) gets injected
skills/hd-setup/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-setup/SKILL.mddeleted−192@@ -1,192 +0,0 @@-name: hd-setup-description: >- HD setup panel: the install-step cascade, Run All, Rollback All, Virgin Reset- with toggles, and automated testing patterns. MUST READ before running setup,- testing steps, or doing virgin resets. Covers the step list, the virgin- reset toggle panel, how to keep auth during resets, and how Run All handles- failures. Trigger words — setup panel, install steps, run all, virgin reset,- rollback, step failed, wipe, reset workspace, keep auth, test setup.--# Hydrogen Desktop -- Setup Panel & Virgin Reset--This is the platform-generic reference for the HD setup panel and virgin-reset. The host-platform specifics (the exact install cascade on Windows/WSL2,-distro import/unregister mechanics, the per-platform step list and state file)-live in the companion [[hd-setup-windows]] skill.--## Zero-Click Goal--The HD setup process targets ZERO manual clicks for the user. The heavy-lifting is already done at image-build time: the golden workspace image ships-with code-server, the Adom CLIs, the adompkg skills, the claude CLI + Code-extension, VS Code settings, and all `hd-*` skills BAKED IN. Setup just provisions that-image, injects the user's session, wires up the relay, and walks the single-human auth gate. If something is missing, HD downloads it — never tell the-user to install something manually. See the `hd-golden-image` skill for the-baked-image model.--## The Install Steps--The setup cascade is a halt-on-failure, resume-not-restart sequence with-per-step 3x auto-retry. State is tracked per-step (status pending / running /-done / failed, output text, percent complete) and HD reads it on launch to-decide whether the setup panel auto-opens. The exact step list, ordering, and-state-file name are platform-specific — see [[hd-setup-windows]].--Conceptually the steps fall into these phases:--1. **Provision the workspace** — ensure the runtime is available, import the- golden image, create the workspace user, start code-server, verify host- loopback reachability. This single step subsumes any older- pull-image / create-instance / start-instance trio.-2. **Activate the editor** — wait for code-server, activate editor extensions- (baked, installs nothing), reload the editor iframe, prove the- editor-control API answers.-3. **Wire identity + environment** — set the Adom env URLs, inject the Adom- session token, re-assert VS Code config backstops idempotently, apply the- per-session layout.-4. **Wire the relay** — verify the Adom Desktop companion app, start the- relay inside the workspace, prove direct-connect and register the relay for- file streaming.-5. **Gates + payoff** — gate on adom-cli reachability (carbon +- hydrogen-proxy), walk the single Claude auth gate, confirm the editor SSE- session, verify the proxy holds a real layout, open Claude Code and send the- first prompt, and finally open welcome.html in HD's right pane (hard-fails- if the Welcome tab doesn't appear).--### Steps that NO LONGER EXIST (baked into the golden image)--These are NOT setup steps anymore — they are baked at image-build time and-must not be listed or "run": `install-hd-skills`,-`verify-adom-desktop`, `install-claude-cli`, `install-claude-ext`,-`write-vscode-settings`, `set-trusted-domains`, `clean-layout`. The-Adom CLIs, the adompkg skills, the claude CLI + Code extension, code-server, settings, and all-`hd-*` skills are baked into the golden image. See `hd-golden-image`.--## Panel Buttons--- **Run All** — executes all pending steps sequentially. STOPS on first failure.-- **Rollback All** — rolls back all completed steps in reverse order.-- **Virgin Reset ▾** — opens the toggle panel (see below).-- **Run** (per-step) — runs a single step.-- **Rollback** (per-step) — rolls back a single step.--## CRITICAL: Run All Stops on Failure--If any step fails, Run All ABORTS (halt-on-failure, after the per-step 3x-auto-retry is exhausted). Remaining steps are NOT executed. The cascade-resumes — it does NOT restart from the first step — so to recover after a-failure: fix the issue, then either:-- Click "Run All" again (it resumes from the first not-done step)-- Click "Run" on the failed step to retry just that one--## Virgin Reset Toggle Panel--Click "Virgin Reset ▾" to expand the wipe options. Each toggle has a-live status badge showing the current state. The ONLY programmatic trigger is-`POST /setup/panel/run-virgin-reset` (see below).--| Toggle (option key) | What it wipes | Status badges |-|---|---|---|-| Install step state (`install_state`) | Resets all steps to pending | CLEAN / EXISTS |-| Workspace (`container`) | PRISTINE wipe + re-provision of the workspace (ONLY HD's own workspace) | GONE / RUNNING / EXISTS |-| Image tarball (`tarball`, legacy `image`) | Deletes the cached golden image (full re-download) | CACHED / GONE |-| Webview storage (`webview_storage`) | Queued wipe, flushed at next HD launch (restart_required) | CLEAN / EXISTS |-| VS Code state (`vscode_state`) | Queued wipe, flushed at next HD launch (restart_required) | CLEAN / EXISTS |-| Adom session token (`adom_token`) | Deletes `hydrogen-session.txt` | CLEAN / EXISTS |-| Claude credentials (`claude_token`) | Deletes the host-side Claude creds backup | CLEAN / EXISTS |--`confirmed_destructive` must be set for the reset to proceed. **"Wipe-Selected"** at the bottom executes the reset for all checked toggles.--The workspace reset is a PRISTINE wipe + re-provision — it backs up Claude-creds first UNLESS `claude_token` is checked. The `webview_storage` /-`vscode_state` wipes are queued and flushed at the next HD launch-(restart_required), NOT immediate; there is no reboot for the wipe.--CRITICAL — the virgin reset only ever touches HD's OWN workspace. It NEVER-touches other workspaces, distros, or runtimes the user may have on the-machine. The platform-specific commands (and the never-touch-global rule)-are in [[hd-setup-windows]].--## Automated Testing Pattern--For AI-driven ralph loop testing, keep auth tokens so steps don't fail:--```-Checked: install_state, container-Unchecked: tarball (cached, saves the full-image re-download),- adom_token, claude_token-```--This gives a freshly-provisioned workspace while keeping auth working — the-inject-api-key step and the claude-auth step won't fail.--### Programmatic triggers (the ONLY supported headless paths)--```bash-# Virgin reset — the ONE allowed programmatic trigger-curl -X POST http://127.0.0.1:47084/setup/panel/run-virgin-reset \- -H Content-Type:application/json \- -d '{"confirmed_destructive":true,"install_state":true,"container":true,- "tarball":false,"webview_storage":false,"vscode_state":false,- "adom_token":false,"claude_token":false}'--# Re-run a single step (optional continue_after to keep going)-curl -X POST http://127.0.0.1:47084/setup/step/test-direct-connect \- -H Content-Type:application/json -d '{"continue_after":true}'-```--DEPRECATED — do NOT use these; they are now REFUSED (deprecated headless-triggers): `POST /wsl/unregister`, `POST /setup/virgin-reset`,-`POST /setup/run-all`, `POST /setup/run-step`.--## Key Steps Explained--### Provision the workspace (step 1)-The big one. Ensures the runtime is available (installing it if needed), then-provisions the workspace from the full pre-baked golden image, creates the-workspace user, starts code-server, and verifies host loopback reachability.-This single step replaces the old pull-image / create-instance /-start-instance trio. Platform-specific provisioning commands and any-runtime-install reboot are in [[hd-setup-windows]].--### inject-api-key-Writes the Adom session token into the workspace so adom-cli works. Requires-the user to be signed in. Keep the `adom_token` toggle UNCHECKED during-automated test resets.--### test-direct-connect-Proves the fast workspace→desktop command path. The CLI auto-probes the host-control API and routes through Adom Desktop's direct connect API.--### test-relay (register relay)-Registers the relay (started by the start-relay step) with Adom Desktop for-file streaming via the code-server proxy path. This enables `pull_file`,-`send_files`, and `shell_execute`.--### claude-auth-The single human gate. Restores / validates Claude creds in the workspace-(backed up host-side) and, when needed, drives the in-editor Claude.ai sign-in-via the native browser Browser Picker. Runs LAST before the payoff steps.--### The final gates-`ensure-sse` confirms the editor browser SSE session is connected (so the-Welcome webview-open doesn't 409); `verify-workspace` confirms the proxy holds-a real layout; `welcome` opens Claude Code and sends the first prompt; the-final `open-welcome` gate opens welcome.html in HD's right pane and HARD-FAILS-if the Welcome tab doesn't appear.--> Note: the Adom CLIs, the adompkg skills, the claude CLI + Code extension, VS Code-> settings, and `hd-*` skills are NOT setup steps — they are baked into the-> golden image. See `hd-golden-image`.--## Workspace Recreation--When the workspace is wiped and re-provisioned (step 1), the new workspace is-a fully pre-baked image. Host port-forwarding for code-server is handled by the-runtime, so the port-conflict / stale-mapping class of bug does not apply.-Other services tunnel through the code-server `/proxy/{port}/` path. The-platform-specific port-forwarding behavior is in [[hd-setup-windows]].
skills/hd-skill-catalog/SKILL.mddeleted−119@@ -1,119 +0,0 @@-name: hd-skill-catalog-description: The catalog of skills available to your Claude inside this Hydrogen Desktop workspace. Lists every public `hd-*` skill that ships with HD and explains what each one is for, so you can pick the right skill before doing anything HD-related. READ THIS FIRST when you're in a fresh HD workspace and want to know what Claude knows. Trigger words — hd skills, what skills do I have, hd catalog, skill index, skill list, what can claude do in hd, hd-* skills, browser picker, hd setup, hd container info, hd networking, hd volume, hd ports.--# HD Skill Catalog--You're running inside a Hydrogen Desktop workspace — a **WSL2 Ubuntu distro-`Adom-Workspace`** by default, or a **Docker container** under the legacy-`HD_RUNTIME=docker` runtime. HD ships a curated catalog of public skills (all-named `hd-*`) so your Claude knows about HD's own interface, networking, setup-flow, and gotchas. The `install-hd-skills` setup step deploys them **flat into-`~/.claude/skills/<name>/SKILL.md`** automatically.--## How the catalog is bucketed (and what you actually have)--The public skills live in three buckets in the HD repo under-`skills/public-facing/`:--- **`shared/`** — runtime-neutral. **Always installed.**-- **`wsl2/`** — the WSL2 implementations of runtime-specific topics. Installed- when HD runs the **default WSL2 runtime**.-- **`docker/`** — the legacy Docker implementations of the same topics, plus the- Docker-Desktop-only skills. Installed only when `HD_RUNTIME=docker`.--`install-hd-skills` deploys `shared/` **plus the active runtime's bucket** —-mirroring `runtime/mod.rs::select_runtime()`. So you have exactly one runtime's-flavor of each topic, never both. **Read `hd-runtime-mode` first** to confirm-which runtime you're in.--## Shared skills (always present)--| Skill | What it covers |-|-------|----------------|-| `hd-skill-catalog` | This file — read first to know what's available. |-| `hd-runtime-mode` | WSL2 vs Docker: which runtime you're in and what differs. |-| `hd-golden-image` | Your workspace is a pre-baked golden WSL2 image — gallia, CLIs, claude, code-server, settings, and these skills are baked in (not installed by setup); how to update. |-| `hd-skill-loop` | (Maintainers) The loop to fix a wrong/stale HD skill: verify vs the live HD, fix it live, write it back to the repo, bake it into the next image. |-| `hd-who-am-i` | Answer "who am I / what can I do here" — the auto-question HD injects on first launch. |-| `hd-adom-auth` | Adom login/logout, auth intent flow, session token, profile menu. |-| `hd-claude-auth` | How Claude Code signs in: OAuth via Browser Picker, credential backup, the `claude-auth` step. |-| `hd-api` | Control API reference (`127.0.0.1:47084`). |-| `hd-desktop-sse` | HD's local workspace API + SSE — adom-cli inside the workspace talks to HD, not cloud Hydrogen. |-| `hd-monitor` | Watch the workspace in real-time via SSE; composes with Claude's Monitor tool. |-| `hd-ui` | HD's menus, dialogs, panels, programmatic UI control via CDP, CSS selectors. |-| `hd-open-url` | The decision guide for "open a website": the six ways to open a URL (webview tab, HD window, Pup, native browser, picker, headless curl) and when to pick each. |-| `hd-browser-picker` | Every URL HD opens routes through a 5-layer interception into a picker dialog (AI-friendly 5s countdown). |-| `hd-wiki-review` | After editing any wiki.adom.inc page, load it in pup so the user can review the rendered result (don't just report the commit). |-| `hd-claude-management` | First-class API for the Claude conversation tabs: list/open/close/switch, read per-conversation state + on-disk session GUID, type into a SPECIFIC conversation (instant/animated) + submit, and turn on Remote Control for a claude.ai/mobile cloud URL. Address by stable context id. |-| `hd-permissions` | Why mic/camera/clipboard/notifications "just work" — HD auto-grants WebView2 permissions, so webview apps see no Allow prompts. |-| `hd-adom-desktop` | What HD and Adom Desktop (AD) are + how they work together — AD is your reach onto the user's real OS (create files/folders, run shell, OS screenshots, launch apps, browsers, KiCad/Fusion). |-| `hd-bridges` | Built-in KiCad/Fusion/Puppeteer bridges + extensible wiki catalog (e.g. Blender) + full AD capability list (usb, caption, record…). |-| `hd-eval` | Inject JS into ANY HD surface (shell/workbench/wiki/Claude) via POST /eval-in; build add-ons, overlays, highlights. |-| `hd-adom-menu` | The upper-left Adom logo menu: Settings, Ports, API Explorer, DevTools, Setup Steps, Virgin Reset, About dialog, version readout. |-| `hd-settings` | HD's Settings dialog + the full preferences tree (theme, vim mode, 3D control style, launch-on-boot, AD window behavior). |-| `hd-file-transfer` | Move files BOTH ways between the WSL2 workspace and the Windows PC (/mnt/c, \\wsl$, adom-desktop relay). |-| `hd-tab-icons` | Webview tab icon customization (displayIcon API, brand colors, MDI/SVG). |-| `hd-profile-menu` | The avatar dropdown: profile/repos/molecules links and logout. |-| `hd-embedded-ad` | HD ↔ Adom Desktop embedded mode: tray, hidden AD, footer pill. |-| `hd-self-screenshot` | How Claude screenshots HD panels + any host window (window control, multi-monitor), ralph loops, resize-before-Read rule. |-| `hd-capture-share` | The screen-share + "AI wants access" approval + countdown the AI triggers to capture the editor; tab/screen scopes, Webview/Pup window mode. |-| `hd-recording` | Record screen/window — HD's NATIVE WGC/DXGI recorder (h264/h265 mp4 → `recordings/`, no picker/banner, "record kicad"/"record whole screen", max-duration cap), plus AD desktop recording + the tab-vs-desktop footgun. |-| `hd-screen-lock` | HD screen capture and the display wake-lock: HD's native screenshots (CDP) + recording (WGC/DXGI) hold NO wake-lock, so HD has no burn-in leak; the cap + "● Recording" indicator; `powercfg /requests` to diagnose. |-| `hd-notifications` | Reach the user — OS Windows toasts + emergency taskbar flash via `notify_user`, AND the in-app `POST /ui/toast` (toast inside the HD window). |-| `hd-captions` | Paint labeled text on the user's screen — a Win32 always-on-top caption overlay for demos / step callouts. |-| `hd-welcome` | The static welcome page shown after setup completes. |-| `hd-overview` | Master skill index, grouped by category. |--## Runtime-specific skills (you get the variant for your runtime)--Same names across `wsl2/` and `docker/`; you have whichever matches your runtime.--| Skill | What it covers |-|-------|----------------|-| `hd-container` | This workspace's spec — Ubuntu 24.04, code-server 4.112.0, what's pre-installed, env vars. |-| `hd-topology` | The three layers and how commands route to reach the workspace. |-| `hd-networking` | Port architecture, hostnames, code-server proxy, host access, NEVER-do-these rules. |-| `hd-ports` | Forwarding workspace services to Windows. |-| `hd-port-watcher` | The dynamic port-forward daemon for extension OAuth callbacks. |-| `hd-volume` | Where your code lives, what persists, what a reset wipes, how to copy files out. |-| `hd-container-stats` | The title-bar CPU/RAM indicator and how its stats are gathered. |-| `hd-setup` | The setup panel UI — Run All, Rollback, Virgin Reset toggles. |-| `hd-setup-steps` | What each setup step did to tool your workspace. |--**WSL2-only:** `hd-workspace-lifecycle`, `hd-workspace-monitoring`.-**Docker-only (legacy):** `hd-docker-desktop`, `hd-docker-lifecycle`, `hd-docker-monitoring`.--## What's NOT in your workspace — internal dev skills--The HD repo also has skills under `skills/dev-internal-facing/` (hd-debug, hd-dev,-hd-docker, hd-pitfalls, hd-settings, an internal hd-setup-steps deep-dive,-hd-vm-test, and more). These are **Adom-employee-only development skills** — HD-build pipeline, debugging the Tauri/WebView2 source, internal architecture,-postmortems. They live in the HD repo but are **NEVER** deployed to user-workspaces.--The split is enforced by the `install-hd-skills` step, which only reads-`skills/public-facing/{shared,<runtime>}/hd-*/` — never `dev-internal-facing/`.--| Folder | Audience | Deployed to user workspaces? |-|--------|----------|------------------------------|-| `public-facing/shared/` | End users of HD | **Yes** (always) |-| `public-facing/wsl2/` | End users on the WSL2 runtime | **Yes** when WSL2 (default) |-| `public-facing/docker/` | End users on the Docker runtime | **Yes** when `HD_RUNTIME=docker` |-| `dev-internal-facing/` | Adom employees who develop HD | **No** (repo-only) |--## How to use this catalog--1. In a fresh HD workspace, scan this table for the most relevant `hd-*` skill- before answering an HD-related question — and check `hd-runtime-mode` so you- trust the right runtime's facts.-2. Skill descriptions are loaded automatically by skill-pilot at session start.-3. If a topic seems missing, it may be a dev-internal-facing skill (repo-only) —- ask the user to check the HD repo.--## When skills get refreshed--The `install-hd-skills` step deploys skills. Re-run it via-`POST /setup/run-step {"id":"install-hd-skills"}` to refresh after an HD update.
skills/hd-skill-loop/SKILL.mddeleted−120@@ -1,120 +0,0 @@-name: hd-skill-loop-description: >- The maintainer loop for making HD's own hd-* skills perfect: when a skill is- wrong, stale, or confusing, verify the truth against the RUNNING HD, fix the- skill live in the workspace, then write the corrected file back into the core- hydrogen-desktop repo so the next golden-image bake ships it. These skills are- the ONLY thing the in-workspace AI knows about HD — a wrong one confuses every- Adom user. For Adom devs improving HD. Trigger words — this skill is wrong,- stale skill, fix the hd skill, update a skill, the AI got confused by a skill,- improve hd skills, skill is inaccurate, debug a skill, skill loop, write skill- back to repo, bake skills, golden image skills, skill feedback loop, perfect- the skills, skill says X but it actually does Y.--# HD Skill Loop — verify → fix live → write back to the repo → bake--HD's `hd-*` skills are baked into the WSL2 golden image and are the **only** way-the in-workspace AI knows anything about HD. So a wrong skill silently breaks-every user's AI (it curls a dead hostname, hunts a command that doesn't exist,-claims it needs a tool that isn't installed…). This skill is the loop to find-those and make them perfect *before* the next bake.--## Cardinal rule: TEST, don't assert--Never write a skill claim from memory, old skill text, or docs — **verify it-against the running system first.** Run the command. Read the live env. Read the-code. Observe the real return shape. If you can't test a claim, don't state it as-fact. (Most skill bugs are confident, untested assertions.)--## Where skills live — two copies--| Copy | Path | Role |-|---|---|---|-| **Live (ephemeral)** | `~/.claude/skills/<name>/SKILL.md` (flat) | What the running AI reads NOW. Editing here fixes THIS session and lets you test — but it's wiped on virgin reset / replaced by the next image. |-| **Canonical (baked)** | repo `skills/public-facing/{shared,wsl2,docker}/<name>/SKILL.md` | What the golden image is built from. On the laptop the repo is auto-mounted at `/mnt/c/Github/hydrogen-desktop/...` (the same tree the install step reads). The bake pulls from `origin`. |--A fix isn't done until it's in the **canonical** copy and pushed.--## The loop--### 1. Reproduce / notice-A skill made the AI do the wrong thing. Name the skill and the exact wrong claim.--### 2. Get ground truth from the LIVE system-- **Run the real command** and read its `--help` and actual output:- `adom-cli hydrogen <verb> --help` → then run it → note the EXACT return shape- (e.g. HD's `screenshot` returns a `dataUrl` inline when `uploadFailed:true`,- not a saved path).-- **Read the live env**, never hardcode: `cat ~/.adom/hd-control-url` (the real- control URL — `http://127.0.0.1:<dynamic-port>`, NOT `host.docker.internal`,- NOT a fixed 47084), `env | grep ADOM_`, `adom-cli hydrogen probe`.-- **Read the HD source** for the endpoint/behavior (the repo checkout).-- **Not inside HD?** `adom-cli` is the SAME binary in web Hydrogen — test there.- Or reach the user's running HD through Adom Desktop:- ```bash- adom-desktop ping && adom-desktop status # confirm AD is on the right host- # run anything inside the HD distro:- adom-desktop wsl_exec '{"distro":"Adom-Workspace","user":"adom","scriptB64":"<base64 of your script>"}'- ```--### 3. Fix it live + re-test-Edit `~/.claude/skills/<name>/SKILL.md` to match what you verified. Re-read it,-then **re-run the failing action** to confirm the fix actually works.--### 3b. GRADE the fix (don't just eyeball it)-Re-running the action by hand proves little. If you're an Adom maintainer, run the-**`hd-skill-test`** grader (dev-internal): add/update a case encoding the *correct*-behavior plus the anti-pattern that was the bug (e.g. `must_not_appear:-host\.docker\.internal`), then grade against the live (fixed) skill via a fresh-headless `claude` session. PASS means the fix actually changed model behavior, not-just the prose. Gate the write-back/commit on a green battery so fixing skill A-can't silently regress skill B. (See the `hd-skill-test` skill.)--### 4. Pick the bucket-Runtime-neutral → `shared/`; runtime-specific → `wsl2/` (+ keep `docker/`-pristine). `name:` must equal the dir; skills install flat. (See the dev skill-`hd-skills-structure` for the full rules.)--### 5. Write the corrected file back to the repo-Copy the same corrected SKILL.md into the canonical bucket:-```bash-cp ~/.claude/skills/<name>/SKILL.md \- /mnt/c/Github/hydrogen-desktop/skills/public-facing/<bucket>/<name>/SKILL.md-```-If you added / renamed / removed a skill, also update the indexes-`hd-overview` and `hd-skill-catalog` (both in `shared/`).--### 6. Commit + push so the next bake ships it-From the laptop checkout (it has git + credentials), via the relay:-```bash-adom-desktop run_script '{"interpreter":"powershell","scriptB64":"<base64 of:- cd C:\Github\hydrogen-desktop; git add skills; git commit -m \"skills: fix <name> (verified vs live HD)\"; git push>"}'-```-The next **golden-image bake** (`golden-image-bake`) pulls `origin` and bakes the-corrected skills into the image, so the fix reaches every user's next HD.--## Validation checklist before you commit a skill-- [ ] Every command in the skill was actually RUN and produced the documented output.-- [ ] No `host.docker.internal` anywhere — HD reaches the host at `127.0.0.1` /- `$(cat ~/.adom/hd-control-url)` (WSL2 mirrored networking; ports are dynamic).-- [ ] No invented CLI verbs — each confirmed with `--help`.-- [ ] No tool assumed present that isn't in the golden image (ImageMagick `convert`,- Pillow, `jq`…) — test it in the distro before relying on it.-- [ ] Step counts / endpoint paths / ports match CURRENT code (they drift fast).-- [ ] Cross-links use sibling skill names that resolve flat at deploy.-- [ ] `name:` matches the directory.--## Why this matters-The skills ARE HD's self-knowledge for the AI. Ship them wrong and every Adom-user's AI is confidently wrong with them. Treat "the skill is inaccurate" as a-product bug and run this loop until the validation checklist is clean.--## Related skills-- `hd-skill-test` (dev-internal) — the automated GRADE step (step 3b): headless-`claude` battery that asserts tool choices / forbidden patterns-- `hd-skills-structure` (dev-internal) — bucket rules, naming, the deploy/bake mechanism-- [hd-golden-image](../hd-golden-image/SKILL.md) — the baked-image model these skills ship in-- `golden-image-bake` — the tooling that rebuilds the image from the repo-- [hd-self-screenshot](../hd-self-screenshot/SKILL.md) — a skill fixed via this loop (the `adom-cli hydrogen screenshot` / `dataUrl` truth)-- [hd-desktop-sse](../hd-desktop-sse/SKILL.md) — the adom-cli → HD-proxy → SSE interception model you test against
skills/hd-tab-icons/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-tab-icons/SKILL.mddeleted−338@@ -1,338 +0,0 @@-name: hd-tab-icons-description: >- How Hydrogen **webview** tab icons work in the Adom editor — panels with- `panelType: adom/a1b2c3d4-0031-4000-a000-000000000031`. (Native panels- like Schematic Editor, 3D Editor, Desktop Demo, KiCad panel have their- own icon mechanism and are NOT covered here.) The critical gotcha:- `displayIcon` (HTTP API) / `--display-icon` (CLI) is only the- **pre-load placeholder** — the loaded page's own `<link rel="icon">`- overrides it as soon as the page loads. The only stable way to control- a webview tab's icon is to serve the favicon you want from your own app.- Covers the override mechanism, shipping a custom favicon from a- Rust/Node/Python app, using `mdi:<name>` and `data:image/svg+xml;base64,...`- placeholders, common icons, and troubleshooting. Trigger words: hydrogen- webview tab icon, webview tab icon, mdi icon webview tab, displayIcon- webview, display-icon, webview tab icon not showing, custom webview tab- icon, colored webview tab icon, tab chip icon, webview tab icon svg,- webview tab icon base64, favicon override webview tab, webview tab icon- keeps resetting, webview tab icon reverts after page load.--# Hydrogen Webview Tab Icons--How Hydrogen **webview** workspace tabs actually get their icons in-the Adom editor. **Read the "Real mechanism" section first** — the-placeholder vs. favicon distinction is the thing future Claude-sessions usually get wrong, including an earlier version of this-skill.--## Scope--This skill is **only** about tabs whose `panelType` is-`adom/a1b2c3d4-0031-4000-a000-000000000031` — the generic Hydrogen-webview that loads an HTML page from a URL. The favicon-override-mechanism described below is specific to webview panels because-only they load an HTML page that can ship its own-`<link rel="icon">`.--Other panel types — Schematic Editor, 3D Editor, KiCad panel,-Desktop Demo, Movie Maker, Adom Viewer native panel, etc. —-have their icons assigned by Hydrogen's internal panel registry-keyed off `panelType`, not by anything the tab adder passes in.-If your tab isn't a webview and its icon is wrong, **this skill-will not help you** — open a Hydrogen issue instead.--## Real mechanism (the thing everyone misses)--Hydrogen webview tabs have **two** sources for their displayed icon,-and the loaded page wins:--1. **`panelState.displayIcon`** — a placeholder you pass via- `--display-icon` / HTTP `displayIcon`. Shown **only until the page- loads**.-2. **The loaded page's `<link rel="icon">`** — Hydrogen's Web View- panel reads the favicon after `load` and writes it into- `panelState.displayIcon`, overriding whatever the placeholder was.--**Practical consequence:** once any page with a favicon finishes-loading, your `--display-icon "mdi:chip"` is gone. If you want the-icon to be **stable and predictable**, you have to serve the favicon-you want **from the page you load**.--Empirical proof: create a tab with `--display-icon "mdi:rocket-launch"`,-navigate it to `https://example.com`, then inspect the tab's-`panelState.displayIcon` via `adom-cli hydrogen workspace tabs` — it-will be a `data:image/svg+xml;base64,...` value matching example.com's-favicon, not `mdi:rocket-launch`.--## Brand rule: icons must be monochrome `#e6edf3`--Per `gallia/skills/brand/SKILL.md`, **all Adom icons must be monochrome-white (`#e6edf3`)** — no colored icons, no multi-color, no brand-palette-colors in icons. This applies to tab icons too.--- **First choice:** use an `mdi:<name>` placeholder for the `--display-icon`- flag. MDI icons are already monochrome and inherit the theme color- automatically, so you get brand compliance for free. Search- https://pictogrammers.com/library/mdi/ for a matching icon.-- **If you need a custom icon** (no MDI match, or you want to ship your- own favicon for the page-load override): hand-roll an SVG with- `fill="#e6edf3"` (or `fill="currentColor"` so it inherits), a- `24x24` viewBox, and clean filled paths or 2px strokes. **No- gradients, no shadows, no color.** Details that break at 16 px lose.--Concrete example of a brand-compliant custom icon (the adom-tsci-chip) — 318 bytes, single path, monochrome `#e6edf3`:--```svg-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#e6edf3">- <path d="M6 2a2 2 0 0 0-2 2v2H2v2h2v3H2v2h2v3H2v2h2v2a2 2 0 0 0 2 2h2v2h2v-2h3v2h2v-2h3v2h2v-2a2 2 0 0 0 2-2v-2h2v-2h-2v-3h2v-2h-2V8h2V6h-2V4a2 2 0 0 0-2-2h-2V0h-2v2h-3V0h-2v2H9V0H7v2H6zm0 2h12v16H6V4zm2 3v10h8V7H8zm2 2h4v6h-4V9z"/>-</svg>-```--Rendered in a dark-background Hydrogen tab bar, that's a white chip-with pins sticking out — visually identical to `mdi:chip` but served-from your own app so it can't be overridden.--## The right way: serve your own favicon from your app--If you're writing a Rust/Node/Python/Go app that opens a Hydrogen-webview tab, **put a `<link rel="icon">` in the HTML you serve**.-Hydrogen will pick it up on load and that's what the user will see.-Use a **brand-compliant `#e6edf3` monochrome SVG** (see section above).--### Rust + tiny_http example (the `adom-tsci` pattern)--```html-<!-- src/assets/shell.html, embedded in the binary via include_str! -->-<!DOCTYPE html>-<html lang="en">-<head>-<meta charset="UTF-8">-<title>My App</title>-<!-- Hydrogen reads this and uses it as the tab icon. -->-<link rel="icon" type="image/svg+xml" href="favicon.svg">-...-```--```rust-// Serve favicon.svg from the tiny_http route handler.-if url == "/favicon.svg" || url == "/favicon.ico" {- let body = include_str!("../assets/icon.svg");- return Ok(request.respond(- Response::from_string(body)- .with_header(- Header::from_bytes(&b"Content-Type"[..], &b"image/svg+xml"[..]).unwrap()- ),- )?);-}-```--```svg-<!-- src/assets/icon.svg — MONOCHROME per the brand rule. fill must be- "#e6edf3" (or "currentColor"), NEVER a colored palette value. -->-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#e6edf3">- <rect x="5" y="5" width="14" height="14" rx="1"/>- <rect x="3" y="7" width="2" height="2"/>- <rect x="3" y="11" width="2" height="2"/>- <rect x="3" y="15" width="2" height="2"/>- <rect x="19" y="7" width="2" height="2"/>- <rect x="19" y="11" width="2" height="2"/>- <rect x="19" y="15" width="2" height="2"/>- <rect x="7" y="3" width="2" height="2"/>- <rect x="11" y="3" width="2" height="2"/>- <rect x="15" y="3" width="2" height="2"/>- <rect x="7" y="19" width="2" height="2"/>- <rect x="11" y="19" width="2" height="2"/>- <rect x="15" y="19" width="2" height="2"/>-</svg>-```--(Perfectly symmetric chip: body + 12 pins, 3 per side, even 4-unit-spacing, rx=1 rounded corner, all in brand `#e6edf3`. This is the-exact `adom-tsci` icon — taken from the repo's `src/assets/icon.svg`.)--When the page loads from `https://<workspace>.adom.cloud/proxy/<port>/`,-the browser fetches `/proxy/<port>/favicon.svg`, your app returns the-SVG, Hydrogen syncs it onto the tab, and the icon stays put.--**Rules for the favicon file:**--- **Square `viewBox`** (e.g. `0 0 64 64` or `0 0 24 24`).-- **Self-contained SVG** — no external fonts, no `xlink:href` to- other files, no embedded images.-- **Bold, filled shapes over thin strokes** — the tab bar is- ~16–20 px so details get lost.-- **Transparent background** is fine, the tab bar shows through.-- **Brand color in the SVG itself** if you want a colored icon. MDI- icons can't be tinted.--## Placeholder-only case: `mdi:<name>` / `data:...` via add-tab--If the page you're loading **doesn't set its own favicon**, or if-you're creating a tab that navigates to about:blank or similar, the-`--display-icon` placeholder is all you get — and it will stick.--Examples where this is actually useful:--- **A tab that loads an external site with no favicon.** Rare in- practice; most real sites have a favicon.-- **A stopgap during the ~100ms the page takes to load** — the user- sees the placeholder, then it swaps to the page's favicon.-- **When you don't own the page** — you can't inject a `<link>`, so- the placeholder is your only control point, knowing it's about to- get overridden.--### CLI--```bash-adom-cli hydrogen workspace add-tab \- --panel-id <leaf-id> \- --panel-type "adom/a1b2c3d4-0031-4000-a000-000000000031" \- --display-name "My App" \- --display-icon "mdi:chip" \- --initial-state '{"url":"https://example.com/"}'-```--### HTTP API--```bash-curl -s -X POST \- -H "X-Api-Key: $API_KEY" $TARGET_HEADER \- -H "Content-Type: application/json" \- -d '{- "panelId": "<leaf-id>",- "panelType": "adom/a1b2c3d4-0031-4000-a000-000000000031",- "displayName": "My App",- "displayIcon": "mdi:chip",- "initialState": { "url": "https://example.com/" }- }' \- "https://hydrogen.adom.inc/api/workspaces/editor/$OWNER/$REPO/current/tabs"-```--### Two placeholder formats--| Format | When to use | Example |-|---|---|---|-| `mdi:<icon-name>` | Monochrome icon from [Material Design Icons](https://icon-sets.iconify.design/mdi/). Inherits the editor theme's foreground color. Can't be tinted. | `mdi:chip`, `mdi:bug`, `mdi:eye` |-| `data:image/svg+xml;base64,<b64>` | Custom SVG, full control. **Must still be monochrome `#e6edf3`** per the brand rule — don't color your own placeholder. | `data:image/svg+xml;base64,PHN2...` |--**Again: both are placeholders. The page's favicon overrides them-both as soon as it loads.**--### Common MDI icons used in existing Adom tabs--| Icon | Typical use |-|---|---|-| `mdi:chip` | chips, ICs, boards, PCB/tscircuit tools |-| `mdi:eye` | viewers (AV) |-| `mdi:bug` | bug-filing tools |-| `mdi:chart-line` | visualizers |-| `mdi:monitor-cellphone` | demo/desktop tools |-| `mdi:github` | repo browsers |-| `mdi:book-open-variant` | docs viewers |-| `mdi:cart` | commerce tools |-| `mdi:robot` | automation / AI |-| `mdi:folder` | file explorers |-| `mdi:code-tags` | code editors |-| `mdi:palette` | design tools |-| `mdi:camera` | screenshot tools |--Full catalog: https://icon-sets.iconify.design/mdi/.--## Building a custom SVG data URL by hand--When you need a one-off custom icon and don't want to ship a file,-you can build a `data:` URL inline:--```bash-SVG='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#00b8b0" d="M13.13 22.19L11.5 18.36..."/></svg>'-B64=$(echo -n "$SVG" | base64 -w0)-DATA_URL="data:image/svg+xml;base64,$B64"--adom-cli hydrogen workspace add-tab \- --panel-id <leaf-id> \- --panel-type "adom/a1b2c3d4-0031-4000-a000-000000000031" \- --display-name "My App" \- --display-icon "$DATA_URL" \- --initial-state '{"url":"about:blank"}'-```--`base64 -w0` (no line wrapping) is important — newlines inside the-value break the JSON it gets embedded in.--This is still a **placeholder**. If the tab ever navigates to a page-with its own favicon, the placeholder is lost.--## Troubleshooting--### "I pass `mdi:chip` but the tab shows a different icon"--Your loaded page has a favicon that overrode the placeholder.-Options:--- **Best:** serve your own favicon from your app (see Real mechanism- above). Pick an SVG that matches the brand/icon you want. Hydrogen- will sync it onto the tab.-- **Acceptable:** if you don't control the page, you can re-set the- `displayIcon` after load with another `add-tab`/PATCH call — but- the next navigation will override it again.--### "My placeholder flickers for a moment then reverts"--That's the expected behavior — the placeholder shows until the page-finishes loading, then Hydrogen swaps in the page's favicon.--### "My tab has no icon at all"--The loaded page has no `<link rel="icon">` **and** no-`--display-icon` was passed. Hydrogen falls back to the default Web-View icon (a globe). Add a favicon to your served HTML.--### "I pass a `data:image/svg+xml;base64,...` and it's broken"--Usually a base64 encoding issue:--- Use `base64 -w0` (no wrap) so the output is a single line.-- Make sure the raw SVG has no unescaped newlines or tabs that break- the JSON wrapper.-- The SVG must be self-contained: no `xlink:href` to other files, no- `@import` in `<style>`, no embedded `<image>` pointing at URLs.--### "I set the tab to `mdi:chip` but `workspace tabs` shows a data URL"--Normal. Hydrogen rewrites `panelState.displayIcon` after page load-with whatever the page's favicon was (usually a data URL because-some sites inline their favicons). This is the override in action.--## How the `adom-tsci` app handles this--1. `src/cli/start.rs` passes `--display-icon "mdi:chip"` to- `adom-cli hydrogen workspace add-tab` — this shows the MDI chip- icon for ~100ms while the page loads.-2. `src/assets/shell.html` has- `<link rel="icon" type="image/svg+xml" href="favicon.svg">`.-3. `src/server/mod.rs` serves the favicon at `/favicon.svg` with the- content of `src/assets/icon.svg` — a teal-colored (`#00b8b0`)- custom SVG chip.-4. When the shell loads, Hydrogen fetches `favicon.svg`, gets the- teal chip SVG, and writes that into `panelState.displayIcon`.-5. The tab now **permanently** shows the teal chip — because every- future reload re-serves the same favicon.--The MDI `mdi:chip` placeholder and the served `favicon.svg` are-visually similar by design — the transition is invisible. If they-were different, you'd see a brief flash.--## See also--- `~/.claude/skills/adom/guides/adom-workspace-control.md` — the full- workspace API reference that documents `displayIcon` as a- `panelState` field (but buries the favicon-override behavior).-- `~/.claude/skills/adom/webview/SKILL.md` — Web View panel actions- (navigate, refresh, setHeaderHidden, setProxyEnabled).-- `~/.claude/skills/adom/guides/app-creator.md` — app conventions- including the `docs/icon.svg` brand-icon requirement.-- [iconify MDI catalog](https://icon-sets.iconify.design/mdi/) — the- searchable list of every `mdi:<name>`.
skills/hd-ui/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-ui/SKILL.mddeleted−218@@ -1,218 +0,0 @@-name: hd-ui-description: >- HD's user interface: menus, dialogs, panels, login/logout, profile menu,- workspace layout, setup panel — and how to DRIVE them from the workspace via- the UI command bus (GET /ui/actions → POST /ui/invoke), the first-class way to- open/close/toggle any HD menu or dialog (no CDP clicking). READ when the user- asks you to open/close a dialog or menu, operate HD's UI, or you need to know- where a UI element is. Trigger words — HD menu, HD dialog, HD panel, open- dialog, open menu, close dialog, drive UI, ui/actions, ui/invoke, command bus,- ports.open, settings.open, about dialog, profile menu, login, logout, settings,- setup panel, Adom menu, HD UI.--# Hydrogen Desktop — UI Reference--## Window Layout--HD's window has these regions (top to bottom, left to right):--### Title Bar-- **Left**: "jlauer12 / Hydrogen Desktop" — shows user + app name-- **Right**: CPU/RAM badges, window controls (minimize/maximize/close)--### Tab Bar-- **VS Code** tab — the main code editor (iframe to code-server)-- **Webview** tab — wiki/app content panel-- Additional tabs for bridges, sensors, etc.--### Main Content Area-- **Left pane**: Usually VS Code iframe-- **Right pane**: Usually webview (wiki, shotlog, etc.)-- Panes are split and resizable--### Setup Panel (bottom)-- Collapsible panel showing the setup-cascade steps (16 steps under the WSL2 runtime)-- See `hd-setup` skill for details--## Menus--### Adom Menu (top-left logo dropdown)-The main menu, opened by clicking the Adom logo at the top-left. Items are grouped into labelled sections (in order, top to bottom):--**(top, unlabelled)**-- **Go to Dashboard** — switches to the Hydrogen dashboard view-- **Settings** — opens the Settings dialog-- **Ports...** — opens the port configuration dialog (see hd-ports)-- **API Explorer...** — opens the control-API endpoint explorer dialog--**DESKTOP** (Tauri-only — won't appear in browser-served HD; in order)-- **Adom Desktop** — brings the embedded Adom Desktop window to the foreground (taskbar); bus id `adom-desktop.open`-- **Zoom** — webview zoom controls (− % +) with persistence across launches-- **Fullscreen** — toggle fullscreen-- **Console** — show/hide the HD debug console window-- **Developer Tools** — open WebView2 DevTools-- **Dev Toolbar** — show/hide the Adom Dev Toolbar window (eval-in / shot / Claude control endpoints); bus id `dev-toolbar.toggle`--> The Bridges section / "Bridge Manager" item was removed (it was a no-op with no backing panel). For the bridge panel itself, see `hd-bridges`.--**Workspace / runtime section** (section label shows live status badge: `RUNNING`, `STOPPED`, etc.)-- **Restart** — restarts the workspace; under WSL2 this terminates the `Adom-Workspace` distro and re-ensures code-server, under the Docker runtime it's `docker restart <container>`.-- **Stop** — stops the workspace; under WSL2 this terminates the `Adom-Workspace` distro, under the Docker runtime it's `docker stop <container>`.--**ADMIN**-- **Setup Steps** — opens the setup panel (the setup cascade for the active runtime; 16 steps under WSL2); see hd-setup-steps-- **Virgin Reset** — opens the virgin reset section of the setup panel; see hd-volume for what each toggle deletes-- **Browser Picker Manager** — configure per-domain default browser/profile prefs; see hd-browser-picker--### Profile Menu (top-right avatar)-Click the user avatar (circular photo) in the top-right corner.-- **Your profile** — view Adom profile-- **Your repositories** — list repos-- **Your molecules** — list molecules-- **Log out** — signs out of Adom, removes session token--**CRITICAL**: Logout is ONLY in the profile menu (top-right avatar),-NOT in the Adom menu (top-left). Future AI threads: don't look for-logout in the wrong menu.--## Dialogs--### Port Settings Dialog-Access: Adom menu → Ports...-Shows all configurable ports with live conflict detection.-Fields: Code Editor, Workspace Proxy, Control API, DevTools (CDP), Hostname.-See `hd-networking` skill for port details.--### Settings Dialog-Access: Adom menu → Settings-Application-level preferences.--### Browser Picker Dialog-Appears when clicking external links. Shows detected browser profiles-(Edge, Chrome, etc.) with per-domain "remember" option.--## Driving the UI — the command bus (PREFERRED, first-class)--⭐ **Every menu / dialog / panel HD has wired is a first-class action you drive over the control-API — no CDP, no `.click()`, no selector guessing.** This is how you operate HD's UI when the-user says "open the Ports dialog", "open About", "show settings", etc.--**Get the control URL from the discovery file** `~/.adom/hd-control-url` (HD rewrites it with-the live, dynamic control port every launch; it's the host loopback, shared with the distro by-WSL2 mirrored networking). Read it from a file, not an env var — your Bash tool runs-non-interactive shells that don't source `.bashrc`/`profile.d`:--```bash-CTRL="$(cat ~/.adom/hd-control-url)"-```--**Always do this in order:**--1. **Discover** what's drivable (ids + descriptions):- ```bash- curl -s "$CTRL/ui/actions"- # → {ok, count, actions:[{id,label,description,group}], _hints}- ```-2. **Drive** one — returns ONLY after the frontend actually ran it:- ```bash- curl -s -X POST "$CTRL/ui/invoke" \- -H 'Content-Type: application/json' -d '{"id":"ports.open"}'- # → {ok:true, id:"ports.open", value:…}- ```-3. **Not in the list?** That absence is the signal: the element isn't wired to the bus yet. It- needs a 2-line `registerUiAction(...)` in its Svelte component (a dev change in the- hydrogen-desktop repo — see that repo's `hd-ui-actions` skill). Don't fall back to clicking;- wire it. The response's `_hints.if_missing` says the same.--Common ids (always `GET /ui/actions` for the live set): `adom-menu.open`/`.close`,-**`profile-menu.open`/`.close`**, `ports.open`/`.close`, `settings.open`/`.close`,-`api-explorer.open`/`.close`, `browser-picker.open`/`.close`, `about.open`/`.close`,-`adom-desktop.open`, `setup-panel.show`, `virgin-reset.open`, `dashboard.go`,-`fullscreen.toggle`, `console.toggle`, `devtools.open`, `dev-toolbar.toggle`,-`container.start`/`.stop`/`.restart`.--**Menu ITEMS, not just open/close:** the profile menu also exposes per-item actions —-**`profile.view`**, **`profile.repositories`**, **`profile.molecules`**, **`profile.logout`** —-so you can trigger a specific item directly (it navigates as if the human clicked it),-WITHOUT opening the menu + screen-clicking. (`profile.logout` is the canonical logout — see-the logout note above.) `GET /ui/actions` for the live set; the Adom menu will get the same-item-level treatment over time.--> Note: there is **no** `port-mappings.open`/`.close` id — that action was never-> registered. Don't try to invoke it.-> **Gap:** `setup-panel` has `.show` but **no `.hide`** — to close it, eval-> `window.__hdSetSetupPanel(false)` (via `/eval-in`) or open another surface. A-> `setup-panel.hide` action is on the wishlist.--### Open a surface, then screenshot it (the killer combo)-Combine the bus with HD's canonical capture endpoint to grab any menu/dialog/panel with-**no screen-clicking**: `POST /ui/invoke {id:"<x>.open"}` → `POST /screenshot-{target:"shell", selector:"<open-state selector>"}` → `POST /ui/invoke {id:"<x>.close"}`.-The `/screenshot` region-flash lands on that selector. Resolved selectors (verified live):-**Adom & profile menus** → `.dropdown-content.open` (one open at a time); **Setup panel** →-`.setup-panel`; **API Explorer** → `.dialog`. Full `/screenshot` reference (target/selector/-b64/silent) is in **[hd-self-screenshot](../hd-self-screenshot/SKILL.md)**.--> **This is HD-desktop-only and SEPARATE from `adom-cli`.** `adom-cli` drives *web-hydrogen's*-> UI via the SSE bridge (`ADOM_HYDROGEN_URL`) and is unchanged. For HD desktop's far richer UI-> control, use the command bus above — never `adom-cli` for this.--## Legacy: raw CDP clicking (deprecated fallback — the command bus above is canonical)--⚠ The command bus (`/ui/actions` → `/ui/invoke`) is the canonical way to drive HD's UI. Before-it existed, UI was driven by CDP `.click()` on CSS selectors via hd-cdp.js. This is brittle-(selectors drift; multiple `localhost:1420` CDP targets) and the selectors below may be stale.-Reach for CDP ONLY for an element not yet on the bus — and the better fix is to register it.--### Find and click menu items-```bash-# Open Adom menu-echo document.querySelector('.logo-button')?.click() | node C:\Users\john\hd-cdp.js--# Find a menu item by text-echo var items=document.querySelectorAll('.dropdown-link,.menu-item');-var target=Array.from(items).find(function(e){return e.textContent.includes('Ports')});-if(target)target.click() | node C:\Users\john\hd-cdp.js-```--### Click setup panel buttons-```bash-# Run All-echo document.querySelector('.run-all').click() | node C:\Users\john\hd-cdp.js--# Virgin Reset dropdown-echo document.querySelector('.virgin-reset-btn')?.click() | node C:\Users\john\hd-cdp.js--# Wipe Selected (execute virgin reset)-echo document.querySelector('.virgin-go')?.click() | node C:\Users\john\hd-cdp.js--# Close setup panel-echo document.querySelector('.close-btn,.setup-close')?.click() | node C:\Users\john\hd-cdp.js-```--### Query step states-```bash-echo JSON.stringify(Array.from(document.querySelectorAll('.step-row')).map(-function(e,i){- var icon=e.querySelector('.step-icon');- return{n:i+1, s:icon?.textContent?.trim()}-})) | node C:\Users\john\hd-cdp.js-```--## CSS Selectors for Key Elements--| Element | Selector |-|---------|----------|-| VS Code iframe | `iframe.vis-studio-iframe` or `iframe[title="code"]` |-| Setup panel | `.setup-panel` |-| Run All button | `.run-all` |-| Virgin Reset button | `.virgin-reset-btn` |-| Wipe Selected button | `.virgin-go` |-| Step rows | `.step-row` |-| Step icon (✅/❌/○) | `.step-icon` |-| Step label | `.step-label` |-| Adom menu button | `.logo-button` |-| Profile menu button | `.profile-dropdown-button` |-| Tab bar | tab container at top |
skills/hd-webview/SKILL.mddeleted−55@@ -1,55 +0,0 @@-name: hd-webview-description: >- WEBVIEW — what the word "webview" means in Hydrogen Desktop and exactly how to- open one. When the user says "show me in webview", "open it in webview", "in a- webview", "webview panel", "wv", or asks to show ANY app/page/tool "in webview",- it ALWAYS means a native Hydrogen workspace Web View tab opened via- `adom-cli hydrogen webview open-or-refresh` — NEVER VS Code's built-in Simple- Browser, never an editor-internal browser, never an iframe hack. Read this the- moment the word webview (or wv) appears. Trigger words — webview, wv, show me- in webview, open in webview, in a webview, webview tab, webview panel, native- webview, show in webview, launch in webview, webview app.--# hd-webview — "webview" means a native Hydrogen Web View tab. Period.--When a user (or a setup prompt) says **"show me X in webview"**, they mean a-**native Hydrogen Desktop Web View tab** — a first-class panel in the HD-workspace, next to their editor. Nothing else qualifies.--## ⛔ NEVER (the 2026-07-16 incident — do not repeat)--- **NEVER open VS Code's built-in "Simple Browser"** (or any editor-internal- browser page) to satisfy a webview request. A setup-thread AI did this once —- the user called it "an awful outcome that can never happen again." The Simple- Browser is not the HD webview: it's cramped, it isn't a workspace tab, and it- breaks every downstream flow (screenshots, captions, panel routing).-- Don't fall back silently to pup or a native browser either — those are- *different, deliberate* destinations (see `hd-open-url` for the decision- guide). "webview" is not ambiguous; it names exactly one thing.--## ✅ The exact command (idempotent — safe to re-run)--```bash-# open (or refresh, if a tab with this name already exists) a Web View tab:-adom-cli hydrogen webview open-or-refresh --name "<Tab Name>" --url "<url>" --panel-id <id>--# ⚠️ --panel-id is REQUIRED to CREATE a new tab (without it: "Tab 'X' not found-# and --panel-id not provided"). Get a panel id first:-adom-cli hydrogen workspace get # or: adom-cli hydrogen workspace tabs-# Re-running with the same --name once the tab exists just navigates/refreshes-# (no --panel-id needed). The --name is your stable handle.-```--Local Adom apps (parts search, chip-fetcher, adom-tsci previews, wiki pages)-are all shown this way: start/locate the app's local URL, then-`open-or-refresh` it into a named tab.--## Verify you actually did it--The tab must appear in `adom-cli hydrogen workspace tabs` with your `--name`.-If it isn't there, you did NOT open a webview — go again.--For the broader "which surface should this URL open in?" decision (webview vs-pup vs native browser vs headless fetch), see **hd-open-url**.
skills/hd-welcome/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-welcome/SKILL.mddeleted−79@@ -1,79 +0,0 @@-name: hd-welcome-description: >- The HD welcome page — shown after setup steps complete. Served from- static/welcome.html at http://localhost:1420/welcome.html. Shows Adom- branding, "Build and test electronics in the cloud", and an "Open Claude- Code" button. This is NOT the adom.inc homepage — it's a local static page- bundled with HD. Trigger words: welcome page, welcome tab, show welcome,- onboarding page, setup complete page, first launch page.--# HD Welcome Page--The welcome page is a static HTML page bundled with HD at `static/welcome.html`.-It's served locally at `http://localhost:1420/welcome.html` by HD's dev file server.--## What it shows--- Adom logo and branding-- "Build and test electronics in the cloud"-- Description of the Adom cloud factory-- "Open Claude Code" button-- Links to documentation--## When it appears--- Automatically as the final setup step. On the WSL2 runtime (default) that is- the `open-welcome` gate (step 16 of 16); on the legacy Docker runtime it's- the last of the 24 steps.-- Can be opened manually via the API--## How to open it--### From inside the Docker container (the standard way)--This is how the setup steps do it, and how any AI inside the container should:--```bash-adom-cli hydrogen webview open-or-refresh \- --name Welcome \- --url http://localhost:1420/welcome.html \- --panel-id desktop-pane-right-```--The URL is rendered by HD's WebView2 on Windows, so `localhost:1420` resolves-to HD's dev file server on Windows, NOT to anything inside the container.--### From outside (via HD control API)--The workspace tabs API doesn't reliably store the URL in panelState when-creating tabs. The reliable method is to navigate an existing webview iframe:--```bash-# Method 1: Navigate right-pane iframe via eval (reliable)-curl -X POST http://127.0.0.1:47084/eval \- -H Content-Type:application/json \- -d '{"js":"(function(){var iframes=document.querySelectorAll(\"iframe\");for(var i=0;i<iframes.length;i++){var r=iframes[i].getBoundingClientRect();if(r.x>400&&r.width>200){iframes[i].src=\"http://localhost:1420/welcome.html\";return \"ok\"}}return \"no iframe\"})()"}'--# Method 2: Create tab then navigate (2 steps)-# Step 1: Create tab-curl -X POST http://127.0.0.1:47083/api/workspaces/editor/{owner}/{repo}/current/tabs \- -H Content-Type:application/json \- -d '{"name":"Welcome","panelType":"adom/a1b2c3d4-0031-4000-a000-000000000031","panelId":"desktop-pane-right","panelState":{"url":"http://localhost:1420/welcome.html","displayName":"Welcome","displayIcon":"mdi:hand-wave"}}'-# Step 2: If URL didn't load, navigate the iframe via eval (Method 1)-```--## IMPORTANT: This is NOT--- NOT the adom.inc homepage (https://adom.inc)-- NOT the wiki (https://git-wiki-*.adom.cloud)-- NOT the VS Code welcome tab inside code-server--It is a LOCAL static page at `http://localhost:1420/welcome.html`, bundled-in `static/welcome.html` in the HD repo.--## Panel type--Webview tabs in HD use panel type `adom/a1b2c3d4-0031-4000-a000-000000000031`.-Do NOT use `panelType: "webview"` — that shows "Unknown panel type" error.
skills/hd-who-am-i/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-who-am-i/SKILL.mddeleted−94@@ -1,94 +0,0 @@-name: hd-who-am-i-description: How to answer "who am I and what can I do here?" — the auto-question HD's welcome step injects on first launch. Tells Claude what data sources to pull (Adom session token, adom-cli user/repo/container queries), the Linux-username-vs-Adom-username distinction (everyone is `adom` inside the container), and the template answer that names the user, shows their resources, and explains what HD + the Adom ecosystem can do for them. READ this skill when answering "who am i", "what can i do here", or "what is this environment". Trigger words — who am i, what can I do here, who is this user, welcome question, first AI question, introduce yourself, what is HD, what can hydrogen desktop do, user identity, user introspection, my profile in HD, my repos, my containers, linux username vs adom username, adom username container.--# HD Who Am I — answer the welcome-step prompt coherently--When HD finishes setup (the final setup step, `welcome`), it auto-injects this prompt into your Claude Code chat:--> **who am i and what can i do here**--Your job is to answer that prompt with a warm, accurate, **personalized** intro that confirms identity, surfaces resources, explains the environment, and points at what's possible. This skill tells you exactly where to get the data and how to phrase the answer.--## Data sources--Run these first (they're all fast — under 5s total):--| Field | Command | Notes |-|-------|---------|-------|-| Name + email + Adom username | `adom-cli carbon user get` | Returns `{name, formatted_name, email, ...}`. Adom username = the `name` field. |-| Repo count | `adom-cli carbon user repos --format json \| jq length` | Or count lines without jq |-| Cloud containers | `adom-cli carbon containers list --format json \| jq length` | Number of cloud-hosted Adom containers under this user |-| Workspace hostname | `hostname` | The workspace hostname (varies by runtime) |-| OS | `cat /etc/os-release \| grep PRETTY_NAME` | Always Ubuntu 24.04 |--If `adom-cli carbon user get` fails with no session token, the user isn't authenticated. In that case answer: "I don't see an Adom session — please log in via the HD login page first."--## The Linux-username-vs-Adom-username quirk (always mention this)--Every user's Linux username inside the HD workspace is **`adom`** — not their Adom username. This is intentional (consistency across all Adom workspaces makes filesystem paths, scripts, and skill examples portable). The user's Adom identity is John, Jane, etc.; their shell prompt just always says `adom@<workspace-hostname>`.--Mention this in the welcome answer so they don't get confused when their `whoami` says `adom`.--## What HD + the Adom ecosystem can do (the capabilities pitch)--Tell them they have access to a full electronics prototyping ecosystem. Specifically:--- **Build PCBs** — InstaPCB workflow with tscircuit, KiCad, our autorouter-- **Build apps** — wiki webview apps via the app-creator skill-- **Build CLIs** — automate workflows from the terminal-- **3D viewers** — full 3D component / STEP / GLB viewing in HD (via adom-step / adom-tsci panels)-- **Open KiCad or Fusion 360** on their Windows machine — HD bridges to the local apps-- **File transfer** — send/receive files between container and their Windows desktop (`adom-desktop send_files` / `pull_file`)-- **Talk to their cloud containers** — adom-cli reaches Adom-cloud-hosted containers under their account-- **Main AI dev environment** — HD is designed as the primary AI-powered dev surface for hardware + software--## Answer template--Adapt this to whatever the user's data is. Don't bullet-dump everything — write it as a warm welcome paragraph followed by capabilities. Use markdown.--```markdown-You're **{user.formatted_name}**, signed in to Adom as **{user.name}** ({user.email}). You're running in **Hydrogen Desktop**, Adom's local AI dev environment, on your Windows machine. The workspace itself is Ubuntu 24.04 with code-server, the `claude` CLI, and the full Adom skill ecosystem (gallia + HD skills) already loaded.--A few quick orientation notes:-- Your **Linux username here is `adom`** (not `{user.name}`) — that's intentional, every Adom workspace uses `adom` for consistency. Your Adom identity (`{user.name}`) is what shows up across the Adom ecosystem.-- You have **{N} repos** and **{M} cloud containers** under your Adom account.--Here's what we can do together in HD:--**Electronics & hardware**-- Build PCBs end-to-end (tscircuit → KiCad → Adom's autorouter → InstaPCB fab)-- Open KiCad or Fusion 360 on your Windows machine directly from here-- Browse parts (DigiKey, Mouser, JLCPCB) with live pricing + Adom-internal inventory-- Generate KiCad symbols, footprints, and 3D models from datasheets--**Apps & automation**-- Build webview apps for viewing symbols, footprints, 3D chips, boards, layouts, schematics, oscilloscopes, etc.-- Write CLI tools to automate hardware workflows-- View 3D STEP/GLB files in a Fusion-360-feel viewer--**Talk to your stuff**-- Reach your cloud Adom containers from here-- Send and pull files between this workspace and your Windows desktop-- Drive Chrome via Puppeteer for browser automation-- Take screenshots, record videos of your screen--Ask me to do any of those, or just ask "what's the best way to start a new PCB project?" if you want a guided tour.-```--## How to deliver the answer--- **Don't just bullet-dump every capability** — group them, lead with the personalized intro, end with an invitation.-- **Pull real numbers** from `adom-cli` — generic "you have some repos" feels worse than "you have 4 repos."-- **Don't list internal jargon** — say "Adom's autorouter", not "the gallia/autorouter/v2 skill".-- **Mention the Linux username quirk early** — it WILL confuse users on first `whoami`.-- **If a data fetch fails** (e.g. adom-cli not yet configured), gracefully say "I can't read your Adom session right now, but here's what HD can do once you're signed in" and pivot to capabilities only.--## Related skills--- [hd-adom-auth](../hd-adom-auth/SKILL.md) — the session token that gives `adom-cli` its identity-- [hd-claude-auth](../hd-claude-auth/SKILL.md) — Claude Code's own auth (separate)-- [hd-skill-catalog](../hd-skill-catalog/SKILL.md) — the full list of public hd-* skills your AI has access to-- `hd-container` — the exact Ubuntu image / arch / pre-installed tools you'll mention-- [hd-bridges](../hd-bridges/SKILL.md) — KiCad / Fusion / Puppeteer bridge capabilities to mention
skills/hd-wiki-review/SKILL.mddeleted−48@@ -1,48 +0,0 @@-name: hd-wiki-review-description: >- After editing ANY wiki.adom.inc page (an adom-wiki push/publish, a README/doc/hero change),- immediately LOAD that page in pup so the user can see and double-check the rendered result —- don't just report the commit hash and move on. Covers how to open the page in pup on the- user's machine, refresh it, and post a screenshot to the shotlog for review.- Trigger words: wiki update, adom-wiki push, adom-wiki publish, wiki page, show wiki in pup,- review wiki, verify wiki page, load wiki page, wiki hero, wiki readme, double-check wiki.--# Show every wiki edit in pup for review--Whenever you change a wiki.adom.inc page — `adom-wiki repo push`, `adom-wiki pkg publish`, or-any edit to a page's README / docs / hero — **open that page in pup right away** so the user-can eyeball the rendered result. A successful push is not proof the page looks right; the user-wants to verify it. Never stop at "pushed, commit abc123."--## Do this after every wiki change--```bash-# 1. Push / publish the page (the change itself).-adom-wiki repo push <slug> -m "…" --files README.md docs/foo.md-# (or: adom-wiki pkg publish --org adom)--# 2. Immediately load the page in pup on the user's machine so they can review it.-adom-desktop browser_open_window '{"sessionId":"wiki-review","profile":"wiki-review","url":"https://wiki.adom.inc/<owner>/<slug>"}'-# Already open from a prior review? Refresh + cache-bust instead of opening a duplicate:-# adom-desktop browser_navigate '{"sessionId":"wiki-review","url":"https://wiki.adom.inc/<owner>/<slug>?cb=2"}'--# 3. Screenshot it and post to the shotlog with a one-line description, then hand the user the URL.-```--## Rules--- **Always the real `https://wiki.adom.inc/...` URL** — never a `localhost:<port>` (pup/webviews- can't reach those).-- **Proactive, not offered** — load it without being asked; the whole point is the user reviews- every wiki change as it happens.-- **Wiki ops go through the `adom-wiki` CLI**, not raw API calls.-- Keep related content on ONE page (e.g. all Hydrogen Desktop docs live on the `hydrogen-desktop`- page) — don't spray a new wiki page per small doc.--## Why--The user runs the same setup as every Adom user and validates the shared experience by eye.-Loading each wiki edit in pup closes the loop: they confirm the page renders correctly (hero,-screenshots, links, formatting) instead of trusting a silent push.
skills/hd-workspace-monitoring/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-workspace-monitoring/SKILL.mddeleted−160@@ -1,160 +0,0 @@-name: hd-workspace-monitoring-description: >- How Hydrogen Desktop monitors and manages the local workspace and code-server.- Covers the 15s workspace state poll, the two health levels (runtime available,- workspace Running + code-server reachable), the floaty states, auto-start via- setup_and_start, auto-reload of the VS Code iframe, and the lifecycle dialog- system. Read BEFORE touching PanelVisualStudioCode.svelte,- ContainerLifecycleDialog.svelte, or any workspace state handling code. Trigger- words: workspace stopped, workspace monitoring, workspace poll, workspace floaty,- start workspace, code-server reachable, lifecycle dialog, workspace state,- workspace running.--# HD Workspace Monitoring--This is the platform-generic reference for how HD monitors workspace health.-The host-platform specifics (the runtime-unavailable floaty wording, the stop-command, the exact code-server reachability port, and the no-daemon framing)-live in the companion [[hd-workspace-monitoring-windows]] skill.--## Architecture--The workspace is HD's local code-server-backed dev environment. HD monitors-workspace health at two levels:--1. **Runtime available** — is the workspace runtime itself installed and- responding on this machine?-2. **Workspace healthy** — is the workspace present AND Running AND is- code-server reachable on its host port?--The second level is a real reachability check, not just "is the workspace-present." A resume reality-check (after sleep, or after an HD rebuild/relaunch)-requires code-server to actually answer before HD trusts the workspace as-healthy — a workspace can be Running while code-server is dead.--The VS Code panel (`PanelVisualStudioCode.svelte`) polls workspace state every-15s and shows appropriate UI for each state. These frontend components are-runtime-agnostic; they front the active runtime via the runtime trait.--## Workspace State Poll--`PanelVisualStudioCode.svelte` calls `get_container_status` (Tauri command)-every 15s. The command dispatches through the runtime trait to the active-runtime impl. Returns a status struct shaped like:-```rust-pub struct ContainerStatus {- pub docker_available: bool, // is the workspace runtime available?- pub image_pulled: bool, // is the workspace image/tarball present?- pub container_exists: bool, // is the workspace present (Running or Stopped)?- pub container_running: bool, // is it Running AND code-server reachable?- pub container_id: Option<String>,- pub error: Option<String>,-}-```-The field names are inherited from the Docker era; under any runtime they map-onto the two health levels above (runtime availability, then-workspace-present → workspace-Running + code-server-reachable).--## Floaty States--All floaties appear INSIDE the VS Code panel only — not as full-app modals.-They are draggable (grab header to move). The rest of the app (wiki tab, menu,-etc.) remains fully interactive.--### 1. Runtime Not Available (red dot)-- **When**: the workspace runtime itself is not installed / not responding- (level-1 health fails)-- **Action**: "Run Setup Steps" button → dispatches `hd-show-setup` event- (which walks the user through enabling the runtime)-- **Color**: Red dot in header--The exact runtime name and floaty message are platform-specific — see-[[hd-workspace-monitoring-windows]].--### 2. Workspace Stopped / Not Ready (red dot)-- **When**: the runtime is available but the workspace isn't Running, or it's- Running but code-server isn't reachable on its host port- (`container_running == false`)-- **Message**: "The Adom workspace is not running."-- **Action**: "Start Workspace" button → calls `setup_and_start` Tauri command-- **Lifecycle dialog**: Shows the draggable lifecycle dialog with live setup- output-- **Auto-reload**: After the workspace + code-server come up, waits for- code-server health, then reloads the VS Code iframe--## Auto-Start via `setup_and_start`--The "Start Workspace" path calls Tauri `setup_and_start`, which delegates to-the active runtime's `setup_and_start`:-- If the workspace isn't present yet, `fresh_import` = download the golden- image + provision the workspace + start code-server.-- If the workspace is present but code-server is down, it just (re-)starts- code-server.--Importantly, code-server runs as an **HD-owned child process** (not a detached-`nohup`) — it dies with HD. So HD also **re-ensures code-server on every-launch** (look for `re-ensured code-server on launch` in the log). Without-that, a freshly relaunched HD would show the workspace Running but the editor-stuck on "Starting AI Environment" forever, because the previous HD's-code-server child was orphaned. See `hd-workspace-lifecycle` for the full-lifecycle detail.--## Lifecycle Dialog--`ContainerLifecycleDialog.svelte` — a draggable floating card (no background-blur) that shows live command output for start/stop/restart operations on the-workspace.--### Events-Listens for `hd-container-lifecycle` CustomEvents on `window`:-- `action: 'restart' | 'stop' | 'start'` — opens dialog, starts spinner-- `action: 'step', step: string` — appends a log line-- `action: 'done'` — stops spinner, shows Close button, auto-reloads VS Code-- `action: 'stopped'` — stops spinner (for stop-only operations)-- `action: 'error', message: string` — shows error state--### Title-Dynamic based on `actionType`:-- Restarting: "Workspace Restarting..." → "Restart Complete"-- Stopping: "Stopping Workspace..." → "Workspace Stopped"-- Starting: "Starting Workspace..." → "Workspace Started"-- On error: "Restart/Stop/Start Failed"--### VS Code Auto-Reload-After the `done` event, the dialog polls code-server health (`/proxy/8080/`)-every 5s for up to 2 minutes. When healthy, it reloads all iframes whose src-points at the host-side code-server port, then shows "VS Code iframe reloaded"-and stops.--## Tauri Commands--| Command | What it does |-|---|---|-| `get_container_status` | Returns workspace status — runtime available, workspace present, Running + code-server reachable |-| `setup_and_start` | `fresh_import` (provision workspace) + start code-server, up to 10 min timeout |-| `stop_container` | Stops the workspace |-| `toggle_console` | Show/hide the debug console window |-| `is_console_visible` | Returns true if console window exists |--The exact stop command is platform-specific — see-[[hd-workspace-monitoring-windows]].--## Menu Items (Adom Menu → Workspace Section)-- **Restart Workspace** — stop the workspace + re-ensure code-server; emits- lifecycle events, shows dialog-- **Stop Workspace** — stops the workspace; emits lifecycle events, shows- dialog, triggers stopped floaty-- **Start Workspace** — available when stopped (from floaty button or menu)--## Settings (Desktop section in Settings dialog)-- `desktop.show_console_on_startup` — boolean, default false-- `desktop.launch_on_boot` — boolean, default true (auto-start on boot)--## Related skills--- [[hd-workspace-monitoring-windows]] — the WSL2 "Not Available" floaty, `wsl --terminate` stop, the 7380 reachability check, the no-daemon framing-- `hd-workspace-lifecycle` — provision/export/remove/stop, the never-touch-global rule, control endpoints-- `hd-container-stats` — the title-bar CPU/RAM bars and `workspace_stats()` probe-- `hd-networking` — code-server port, control port
skills/hd-workspace-updater/._SKILL.mddeleted⋯ 1 unchanged line ⋯
skills/hd-workspace-updater/SKILL.mddeleted−59@@ -1,59 +0,0 @@-name: hd-workspace-updater-description: "How Hydrogen Desktop keeps THIS workspace's Adom tooling up to date, and how an Adom employee ships a skill / extension / CLI to EVERY Adom workspace automatically. Use when asked \"how do I get the latest skills or tooling from Adom\", \"how are updates delivered here\", \"what's the reload banner\", \"how do I make a skill that distributes to all Adom users\", \"how do I publish tooling to every workspace\". Trigger words: workspace updater, adom-workspace-updater, latest skills, get updates, how do updates work, auto update, distribute a skill, publish to all users, ship to every workspace, reload banner."--# How this workspace stays up to date — and how to ship to every workspace--## TL;DR (anyone)-You don't do anything. A gentle background daemon, **`adom-workspace-updater`**, keeps this-workspace's Adom tooling current automatically. It runs **~5 minutes after the workspace-starts**, then **every ~2 hours** — low-priority and **out-of-band** (it is never part of-startup and can't slow or break it). When an update needs the editor reloaded (e.g. a new VS-Code extension landed), Hydrogen Desktop fades in a banner at the top of the editor:-**"<Tool> is ready — click to reload."** Click it. Done.--Why a daemon at all? Your workspace is imaged **once at install and never re-imaged** — so-in-place delivery is the *only* way you ever get new tooling. This daemon is that delivery.--## "How do I get the latest skills / tooling from Adom?"-You already are — automatically. To force a check now instead of waiting:-```-adom-workspace-updater --once-```-It converges this workspace to a wiki **desired-state manifest**: SHA-verified, never-downgrades, and **surgical** — it only touches the specific named tooling, never your files or-anything you installed yourself.-- Installed skill-bundle version: `cat ~/.claude/skills/.hd-skills-version`-- Last run + what changed: `cat ~/.adom/workspace-updater-status.json`--(code-server and the Claude Code extension are **baked fundamentals** that self-update — the-daemon does *not* manage them; it just notices a change and offers the reload.)--## "How do I make a skill that gets distributed to ALL Adom workspaces automatically?" (Adom employees)-1. Add it to `skills/public-facing/shared/<your-skill>/SKILL.md` in the **hydrogen-desktop** repo.-2. Publish the bundle:- ```- scripts/publish-tooling.sh publish-skills- ```- (bundles every public hd-* skill, pushes it to the `hd-workspace-tooling` wiki page, bumps the version.)-3. That's it — every workspace's daemon picks it up on its next tick and extracts it into `~/.claude/skills/`.--To distribute an **extension** or a **CLI** instead, add a manifest entry then publish:-```-scripts/publish-tooling.sh add <name> --kind extension --id <publisher.name> --version X # Open VSX-scripts/publish-tooling.sh add <name> --kind bin --url <wiki blob> --version X # a CLI-scripts/publish-tooling.sh publish-```-Operator details (the manifest, the kinds, the visibility flow) are in the **`workspace-tooling`**-skill in the hydrogen-desktop repo.--## The reload banner-When a new version is on disk but not yet active (a daemon-installed extension, or one the-editor auto-updated), HD shows **"<Tool> is ready / has an update — click to reload"** at the-top of the editor; clicking reloads it to activate the new code. **Skills need no reload** —-Claude re-reads them itself.--*Meta: this very skill was delivered to your workspace by the mechanism it describes — which is-the proof that the pipeline works end to end.*