← Commit history
README.md+73−87
@@ -1,97 +1,77 @@-# adom/hd-bootstrap: Hydrogen Desktop Bootstrap (apps + skills)+# adom/hydrogen-bootstrap -Hydrogen Desktop's workspace layer, as a registry bootstrap. One package that-carries BOTH the dependency list (apps) and the platform-generic hd-* skills-(bundled in `skills/`, deployed by the postinstall hook). Installing it-converges a workspace to "ready to run inside Hydrogen Desktop."+Hydrogen Desktop's workspace layer, as an adompkg bootstrap. It's a thin+package: a dependency list + one config hook. Installing it converges a+workspace to "ready to run inside Hydrogen Desktop."  ```-adom-wiki pkg install adom/hd-bootstrap+adompkg install adom/hydrogen-bootstrap ``` -> **Tooling note (2026-07-16):** `adompkg` is deprecated. All install/update-> flows use the [adom-wiki CLI](/adom/adom-wiki-cli) (`adom-wiki pkg ...`). The-> golden image v17 was still baked with adompkg; **v18 must bake with-> `adom-wiki pkg`** (planned).+The golden WSL2 image runs exactly that line at bake time and ships the+result pre-converged. A live workspace can run the same line on demand. The+bake is an optimization of the runtime flow, not a different mechanism. -The golden WSL2 image runs exactly that install line at bake time and ships the-result pre-converged. A live workspace can run the same line on demand, and-`adom-wiki pkg update` (via the [adom/hook](/adom/hook) prompt hook) keeps every-package, this one included, current at runtime. The bake is an optimization of-the runtime flow, not a different mechanism.+## How it layers -## How it layers (apps as deps, skills bundled per layer)--`adom/hd-bootstrap` depends on [`adom/core`](/adom/core) and adds only what is-specific to running inside HD. A layer is just a bootstrap that depends on the-one below it. Each layer bundles ITS OWN skills (decision John + Kyle,-2026-07-16): skills are documentation for the layer, they live with the layer,-and they install as part of the layer's package. There is no separate skills-package (`adom/hd-skillpack` is deprecated; its content lives here now).+`adom/hydrogen-bootstrap` depends on `adom/core` and adds only what's specific to+running inside HD. Layering is not a special feature — a layer is just a+bootstrap that depends on the one below it. `core` is pulled unmodified.  ```-adom/core                        the Adom ecosystem (shared with web Hydrogen)-  └─ adom/hd-bootstrap           THIS: HD apps (deps) + platform-generic hd-* skills (bundled)-       ├─ adom/hd-windows-bootstrap   Windows/WSL2 layer + wsl2 hd-* skills (bundled)-       └─ adom/hd-mac-bootstrap       macOS sibling + mac skills (in progress, Kyle)+adom/core                ← the Adom ecosystem (shared with web Hydrogen)+  └─ adom/hydrogen-bootstrap    ← THIS: HD-runtime skills + daemon + HD editor config ``` -Tool-specific skills are NOT here: a skill that documents a tool ships inside-that tool's package (the `webview` skill rides-[`adom/adom-workspace-control`](/adom/adom-workspace-control), the `pup` skill-rides the puppeteer bridge, the adom-google skill rides-[`adom/adom-google`](/adom/adom-google)). This package's bundle is HD's own-manual: the hd-* skills.- This is the public **reference design**: other orgs build their own bootstrap-the same way. A vendor publishes `<org>/bootstrap` depending on-[`adom/core`](/adom/core) (plus this package only if their capability needs-HD's runtime), and their users get set up by telling their AI "I use <org>."-*(The BOOTSTRAP-CONVENTION doc is not hosted on the wiki yet. TODO: publish it-and link here.)*+the same way. A vendor publishes `<org>/bootstrap` depending on `adom/core`+(plus `adom/hydrogen-bootstrap` only if their capability needs HD's runtime), and+their users get set up by telling their AI "I use <org>." See+[../BOOTSTRAP-CONVENTION.md](../BOOTSTRAP-CONVENTION.md) for the naming ++layering convention.  ## What's a dependency vs. what's in the hook -The rule: **apps that are packages are dependencies; this layer's skills are-bundled content deployed by the hook; environment config that isn't a package-is also the hook; the OS baseline is the golden image's build (not this-package at all).**+The rule: **anything that is a package is a dependency; environment config+that isn't a package is the `postinstall` hook; the OS baseline is the golden+image's Dockerfile (not this package at all).** -### Dependencies+### Dependencies — all three resolve anonymously (verified 2026-06-19)  | Dep | Why it's here | Status | |-----|---------------|--------|-| [`adom/core`](/adom/core) | The whole Adom ecosystem: skills hub, distributor search, KiCad/Fusion, pup, `adom-cli`, the `adom-vscode` binary, the registry CLI. | ✅ current (4.13.x), anon + sudo-free |-| [`adom/adom-desktop`](/adom/adom-desktop) | The Adom Desktop relay CLI. | ✅ current, anon |--> **Retired (2026-07-16):** [`adom/adom-workspace-updater`](/adom/adom-workspace-updater)-> (the in-distro updater daemon; removed from-> [`adom/hd-windows-bootstrap`](/adom/hd-windows-bootstrap) in 0.2.3) and-> [`adom/hd-skillpack`](/adom/hd-skillpack) (the short-lived separate skills-> package; its skills are bundled HERE as of 0.2.7, hard-deleted 2026-07-16).-Updates are registry-native-> via `adom-wiki pkg update` + [`adom/hook`](/adom/hook). The updater retirement-> also removes the only `needs_sudo` package from the tree, so the old-> `ADOMPKG_ALLOW_SUDO=1` requirement should die with it (verify on the v18 bake).+| `adom/core` | The whole Adom ecosystem — skills hub, distributor search, KiCad/Fusion, pup, `adom-cli`, the `adom-vscode` binary, `adompkg`, etc. Replaces what gallia used to bake. | ✅ published 4.9.1, anon + sudo-free |+| `adom/adom-workspace-updater` | The in-distro updater daemon (systemd .service/.timer). | ✅ published 0.1.11, anon — **`needs_sudo: true`** |+| `adom/adom-desktop` | The Adom Desktop relay CLI. | ✅ published 1.8.147, anon |++> The `hd-*` skills are **bundled into this package** (the `skills/` dir), not a+> dependency — they're HD-specific with no independent lifecycle. The three+> ecosystem CLIs (`adom-google`, `adom-tts`, `adom-gchat`) are **inherited via+> `adom/core`** once added there (they resolve anonymously and are sudo-free) —+> HD does not carry ecosystem apps directly.++> **⚠ Installing this package requires `--allow-sudo`.** `adom/adom-workspace-updater`+> is `needs_sudo: true` (it writes systemd units), and adompkg refuses a sudo+> install script without an explicit opt-in — passwordless sudo alone is NOT+> enough. The bake (and any live install) must run:+> `ADOMPKG_ALLOW_SUDO=1 adompkg install adom/hydrogen-bootstrap`.  ### postinstall.sh (deploy bundled skills + environment config)  Runs as the workspace user after all deps install (`sudo` only for the system-owned workbench file). Mirrors the config sections of-[hd-wsl2-image](https://github.com/adom-inc/hd-wsl2-image)'s `bake-hd-setup.sh`:+`hd-wsl2-image/image/bake-hydrogen-setup.sh`: -0. Deploy the bundled platform-generic hd-* skills (`skills/` dir, 38 of them,-   to `~/.claude/skills/`)-1. Claude Code CLI (claude.ai installer, lands at `~/.local/bin/claude`)+0. Deploy the bundled `hd-*` skills (`skills/` dir → `~/.claude/skills/`)+1. Claude Code CLI (claude.ai installer → `~/.local/bin/claude`) 2. Claude Code extension (Open VSX) registered in code-server 3. `adom-vscode` extension `.vsix` registered in code-server (binary from the `core` dep)-4. code-server `settings.json` (dark theme, Claude Code perms, chat/agent panel-   off, silent ports, git notifications quieted, no model pin)+4. code-server `settings.json` (dark theme, Claude Code perms, chat/agent panel off, silent ports — no model pin) 5. code-server `config.yaml` (telemetry + update-check off) 6. `workbench.html` IndexedDB seed (trusted domains `*` + activity-bar unpin) 7. tidy (`rm ~/project/.mcp.json`) -### NOT in this package (golden-image base)+### NOT in this package (golden-image Dockerfile)  apt baseline, code-server, systemd/systemd-sysv, cron, the gh CLI, user/group/linger/pam fixes. The "hardware" the bootstrap runs on. A live@@ -101,27 +81,33 @@ install assumes a workspace that already has it.  The Carbon API key, `DefaultUid=1001`, the `host.docker.internal` alias. -## Resolved / superseded decisions (history)--- ✅ **Skills live IN the bootstraps (2026-07-16, John + Kyle).** Supersedes the-  one-day experiment with a separate `adom/hd-skillpack` package (1.0.0-1.0.4,-  now deprecated). Rationale: skills are the layer's documentation, they change-  at product cadence, and two humans maintain them next to the layer they-  document. `pkg update` is per-package, so bundling costs only a bootstrap-  version bump per skills change.-- ✅ **The `adom/fusion-update-libraries` signature blocker is RESOLVED.** The-  v17 golden image baked and ships (`adom-golden-v17.tar.gz`).-- ⏳ **Verify [`adom/core`](/adom/core) carries `adom-google` / `adom-tts` /-  `adom-gchat`** so HD inherits them (planned 2026-06-19; core is now 4.13.x,-  confirm before the v18 bake).--## Status (2026-07-16)--**PUBLISHED + PUBLIC**, latest `adom/[email protected]` (apps + skills model,-38 platform-generic skills bundled, incl. `hd-webview` + `hd-pup`; 0.2.10 = renamed hd-workspace-updater skill -> hd-staying-current; 0.2.9 was the-full stale-content audit sweep). Deps resolve anonymously. Golden image **v17**-ships from-[hd-wsl2-image](https://github.com/adom-inc/hd-wsl2-image/releases) (baked with-the deprecated adompkg; **v18 re-bake with `adom-wiki pkg` planned**, picking up-this version, hd-windows-bootstrap 0.2.6, and no retired packages). Build the-image on the laptop via AD or in CI, never in the cloud container.+## Open decisions / remaining work++- ✅ **Skills bundled into `skills/`** — the **31 platform-generic** `hd-*` skills.+  The other 15 are Windows-specific (in `adom/hydrogen-windows-bootstrap`); 13 mixed+  skills were split, generic core here + a `<name>-windows` companion in the+  Windows layer. Decided: bundled, not a separate `adom/hd-skills` package.+- **InstaPCB:** the generic bundle **includes `hydrogen-instapcb`** (and `hydrogen-who-am-i`'s+  InstaPCB lines). This package is published **public WITH InstaPCB included** —+  an explicit owner decision (2026-06-19) overriding the general InstaPCB embargo+  for this package, so the launch can be planned against the real thing.+- **Edit `adom/core` to add `adom-google` / `adom-tts` / `adom-gchat`** so HD+  inherits them (all three verified anon + sudo-free). Needs core's source+  (wiki-git) + a republish.+- **Skill-vs-binary completeness in core:** core deps the distributor *skills*+  (`adom/mouser`…), not the *binaries* (`adom/adom-mouser`…). Confirm whether+  the skills just drive `adom-cli` (in core) — decides whether the Dockerfile+  still bakes the distributor binaries.++## Status (2026-06-19)++**PUBLISHED + PUBLIC** at `adom/[email protected]` on wiki.adom.inc. Deps+resolve anonymously; the `adom/core` contract is **green** (anonymous ++sudo-free across the whole tree). Install requires `ADOMPKG_ALLOW_SUDO=1` (the+Windows layer's updater dep is `needs_sudo`). The golden-image bake+(`bake-via-bootstrap.sh`) installs `adom/hydrogen-windows-bootstrap`, which pulls this++ `adom/core`. **Open blocker for the bake:** `adom/[email protected]`+(a transitive dep of `adom/core`) currently FAILS signature verification —+Colby must republish it before any `adompkg install adom/core` (and thus the+bake) can complete. Build the image on the laptop via AD or in CI, never in the+cloud container.
docs/hero.png
⋯ 1 unchanged line ⋯
package.json+8−17
@@ -1,12 +1,13 @@ {   "schema_version": 1,-  "slug": "hd-bootstrap",+  "slug": "hydrogen-bootstrap",   "type": "bootstrap",-  "title": "Hydrogen Desktop Bootstrap (apps + skills)",+  "title": "Adom Hydrogen (platform base)",   "brief": "Layer Hydrogen Desktop's workspace onto a standard Adom workstation.",-  "version": "0.2.37",-  "description": "Hydrogen Desktop's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen Desktop on ANY platform: the platform-generic hd-* skills (BUNDLED in this package by design; wsl2 skills ride adom/hd-windows-bootstrap, mac skills ride adom/hd-mac-bootstrap) and the generic HD editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hd-windows-bootstrap, adom/hd-mac-bootstrap.",+  "version": "0.3.0",+  "description": "Hydrogen Desktop's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen Desktop on ANY platform: the runtime-neutral hd-* skills and the generic HD editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hd-windows-bootstrap (WSL2), adom/hd-mac-bootstrap, adom/hd-ubuntu-bootstrap. The reference design other orgs copy to build their own bootstrap on adom/core.",   "org": "adom",+  "platform": "any",   "tags": [     "bootstrap",     "hydrogen-desktop",@@ -15,20 +16,10 @@   ],   "needs_sudo": false,   "scripts": {-    "install": "./install.sh"+    "postinstall": "./postinstall.sh"   },   "dependencies": {     "adom/core": "^4.9.0",-    "adom/adom-desktop": "^1.8.0"-  },-  "hero": {-    "path": "docs/hero.png"-  },-  "files": [-    "package.json",-    "install.sh",-    "README.md",-    "skills/**",-    "dock/**"-  ]+    "adom/adom-bridge": "^2.0.0"+  } }\ No newline at end of file
page.json+5−5
@@ -2,10 +2,10 @@   "schema_version": 1,   "slug": "hydrogen-bootstrap",   "type": "bootstrap",-  "title": "Hydrogen Desktop Bootstrap (apps + skills)",+  "title": "Adom Hydrogen (platform base)",   "brief": "Layer Hydrogen Desktop's workspace onto a standard Adom workstation.",-  "version": "0.2.36",-  "description": "Hydrogen Desktop's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen Desktop on ANY platform: the platform-generic hd-* skills (BUNDLED in this package by design; wsl2 skills ride adom/hd-windows-bootstrap, mac skills ride adom/hd-mac-bootstrap) and the generic HD editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hd-windows-bootstrap, adom/hd-mac-bootstrap.",+  "version": "0.3.0",+  "description": "Hydrogen Desktop's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen Desktop on ANY platform: the runtime-neutral hd-* skills and the generic HD editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hd-windows-bootstrap (WSL2), adom/hd-mac-bootstrap, adom/hd-ubuntu-bootstrap. The reference design other orgs copy to build their own bootstrap on adom/core.",   "org": "adom",   "tags": [     "bootstrap",@@ -15,11 +15,11 @@   ],   "needs_sudo": false,   "scripts": {-    "install": "./install.sh"+    "postinstall": "./postinstall.sh"   },   "dependencies": {     "adom/core": "^4.9.0",-    "adom/adom-desktop": "^1.8.0"+    "adom/adom-bridge": "^2.0.0"   },   "author": {     "name": "John Lauer",
postinstall.sh+32−10
@@ -1,26 +1,27 @@ #!/usr/bin/env bash-# postinstall.sh — adom/hd-bootstrap+# postinstall.sh — adom/hydrogen-bootstrap # # Runs (as the workspace user) after every dependency is installed —-# apps/skills install first, bootstraps (and their postinstall) last. This hook applies the HD *environment configuration* that isn't a+# adompkg installs apps/skills first, bootstraps (and their postinstall)+# last. This hook applies the HD *environment configuration* that isn't a # package: the Claude Code + adom-vscode extensions, and the VS Code / code-server # settings that make the editor behave like Hydrogen Desktop. # # What is NOT here, by design: #   - the Adom ecosystem (skills hub, distributor search, KiCad/Fusion, pup, #     adom-cli, adom-vscode binary, ...) — comes from the adom/core dependency-#   - the hd-* runtime skills — BUNDLED in this package (skills/ dir)-#   - the CLIs (adom-cli, adom-desktop) — their own dependencies+#   - the hd-* runtime skills — the adom/hd-skills dependency+#   - the workspace-updater daemon + the CLIs — their own dependencies #   - the OS baseline (apt, code-server, systemd, user/linger/pam) — that's #     the golden image's Dockerfile ("hardware"); a live install assumes a #     workspace that already has it. # # Idempotent + LIVE-INSTALLABLE: this must converge correctly from a cold-# `adom-wiki pkg install adom/hd-bootstrap` on an already-running workspace (the+# `adompkg install adom/hydrogen-bootstrap` on an already-running workspace (the # vendor "am I set up?" path), not only inside the golden-image bake. Re-runs # are safe. The bake runs the exact same command, just earlier. #-# Mirrors the config sections of hd-wsl2-image/image/bake-hd-setup.sh — keep+# Mirrors the config sections of hd-wsl2-image/image/bake-hydrogen-setup.sh — keep # the two in lockstep until the bake is rewritten to just install this. set -euo pipefail log() { echo "[hd-bootstrap] $*"; }@@ -29,7 +30,8 @@ CS="$(command -v code-server 2>/dev/null || echo /usr/lib/code-server/bin/code-s HERE="$(cd "$(dirname "$0")" && pwd)"  # 0. Deploy the bundled hd-* runtime skills into ~/.claude/skills/. These ship-#    INSIDE this package (HD-specific); the package's skills/ dir is the source of truth.+#    INSIDE this package (HD-specific — there is no separate adom/hd-skills+#    package); the package's skills/ dir is the source of truth. if [ -d "$HERE/skills" ]; then   log "deploying bundled hd-* skills"   install -d -m 0755 "$HOME/.claude/skills"@@ -80,7 +82,7 @@ cat > "$HOME/.local/share/code-server/User/settings.json" <<'SETTINGS'   "workbench.startupEditor": "none",   "workbench.activityBar.location": "default",   "workbench.activityBar.iconClickBehavior": "toggle",-  "workbench.colorTheme": "Default Dark Modern",+  "workbench.colorTheme": "Adom Kickstand",   "workbench.statusBar.visible": false,   "workbench.navigationControl.enabled": false,   "workbench.secondarySideBar.visible": false,@@ -118,8 +120,28 @@ CSCONF  # NOTE: the workbench.html IndexedDB seed (trusted domains + activity-bar unpin) # patches the WSL2 code-server install path and is therefore PLATFORM-SPECIFIC —-# it lives in adom/hd-windows-bootstrap, not here. Mac/Ubuntu layers add their own.+# it lives in adom/hydrogen-windows-bootstrap, not here. Mac/Ubuntu layers add their own. -# 6. tidy — install.mjs / tooling leave an empty ~/project/.mcp.json; remove it.+# 6. API-KEY PERSISTENCE (2026-08-04). /var/run is tmpfs — wiped on EVERY distro+#    boot — and HD's launch-time re-injection missed every other boot path, so+#    /var/run/adom/api-key vanished and all in-workspace tools 401'd against+#    carbon/wiki. Fix is DAEMONLESS: systemd-tmpfiles (declarative, runs during+#    sysinit, before code-server) re-materializes the tmpfs key from a persistent+#    copy at /home/adom/.adom/api-key on every boot. HD writes both copies when+#    it has the token (write-through); this section installs the machinery and,+#    if a live key exists but no persistent copy yet, migrates it — so a plain+#    `adom-wiki pkg update` retrofits running workspaces. No key material ships+#    in this package; only the plumbing.+log "api-key persistence: tmpfiles machinery + persistent-copy migration"+install -d -m 0700 "$HOME/.adom"+if [ -s /var/run/adom/api-key ] && [ ! -s "$HOME/.adom/api-key" ]; then+  cp /var/run/adom/api-key "$HOME/.adom/api-key" && chmod 600 "$HOME/.adom/api-key"+  log "  migrated live key to persistent copy"+fi+printf 'd /run/adom 0755 root root -+C /run/adom/api-key 0644 root root - /home/adom/.adom/api-key+'   | sudo tee /etc/tmpfiles.d/adom-api-key.conf >/dev/null   && log "  tmpfiles.d/adom-api-key.conf installed (boot-time re-materialization)"   || log "  WARN: could not install tmpfiles snippet (no sudo?) — HD self-heal will retry"++# 7. tidy — install.mjs / tooling leave an empty ~/project/.mcp.json; remove it. rm -f "$HOME/project/.mcp.json" 2>/dev/null || true log "done"
skills/hydrogen-adom-auth/SKILL.mdadded+332
@@ -0,0 +1,332 @@+---+name: hydrogen-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 hydrogen-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, 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 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')` -> backend 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.+   Backend 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. The backend saves the session token to HD's app-data directory on the host+   (the `hydrogen-session.txt` file) and a replay copy alongside it.+8. Frontend redirects to the dashboard.++### Source files++- **Login page**: `src/routes/auth/login/+page.svelte`+- **Auth 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**: HD's app-data directory on the host (the `hydrogen-session.txt`+file).++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 (backend 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)+cat /var/run/adom/api-key+```++For the host-specific path table and the runtime-specific way to read+`/var/run/adom/api-key` from outside the workspace, see the+[[hydrogen-adom-auth-windows]] companion skill.++---++## Auth Proxy++The backend 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 the replay-session file 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**: HD's app-data directory on the host (the+`replay-claude-credentials.json` file).++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 (app 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`++### App 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 **hydrogen-settings** skill (API Explorer section) for the+full endpoint reference.++---++## Cross-references++- **hydrogen-adom-auth-windows** -- host app-data path table + WSL2 specifics+- **hydrogen-ui** -- menu layout, where profile menu lives+- **hydrogen-setup** -- bootstrap steps that depend on auth+- **hydrogen-settings** -- API Explorer (auth endpoints tab)+- **hydrogen-networking** -- control API port (47084)+- **carbon-preferences** -- user preferences that require auth
skills/hydrogen-adom-desktop/SKILL.mdadded+234
@@ -0,0 +1,234 @@+---+name: hydrogen-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 machine 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 local Linux workspace on the+user's machine). 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 note:** HD's host layer (file/shell/screenshot/notification/window+> control) is platform-specific. Before promising a host action on a given OS,+> verify the capability with `adom-desktop status` rather than assuming it.+> For Windows/WSL2-specific host behavior, see the [[hydrogen-adom-desktop-windows]]+> companion skill.++## 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 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 host 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` | [hydrogen-self-screenshot](../hydrogen-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) | [hydrogen-notifications](../hydrogen-notifications/SKILL.md) |+| **On-screen captions** | `caption` (always-on-top click-through overlay) | [hydrogen-captions](../hydrogen-captions/SKILL.md) |+| **Open browsers** | `browser_*` (Pup) and the Browser Picker | [hydrogen-open-url](../hydrogen-open-url/SKILL.md), `pup` |+| **Drive KiCad / Fusion 360 / Pup** | `kicad_*`, `fusion_*`, `browser_*` | [hydrogen-bridges](../hydrogen-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** (the host's native+shell). 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":"<workspace-distro>","user":"adom","scriptB64":"<b64>"}'`+— base64 the script so no quoting survives to be mangled. (`wsl_exec` is a+Windows/WSL2 path — see the [[hydrogen-adom-desktop-windows]] companion.)++### Auto-installing missing apps++If KiCad or Node.js is missing on the host, HD can trigger an unattended+install on platforms that support it (see the [[hydrogen-adom-desktop-windows]]+companion for the Windows winget verbs). The triggers: `install_kicad` fires+when `kicad_list_versions` returns `errorCode:"kicad_not_installed"`;+`install_node` when a `browser_*` call returns `errorCode:"node_not_found"`+(Node.js unblocks the Pup bridge).++### 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":["<host path>"],"saveTo":"/tmp"}'+adom-desktop send_files '{"filePaths":["/tmp/x"],"saveTo":"<host dir>"}'+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** on its discovered port (fallback **47084**).+See the [[hydrogen-adom-desktop-windows]] companion for the host port-discovery file.++**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 workspace+       └─ 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 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. The host-side clients+  reach your in-workspace relay — the exact networking depends on the runtime+  (see the `hydrogen-networking` skill, and the [[hydrogen-adom-desktop-windows]] companion+  for the WSL2 loopback-forwarding details).+- **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 kill the host AD process** — the host AD is your relay bridge;+  killing it severs your control channel (you can't get it back from in here).+  (Windows-specific `taskkill` warnings live in the+  [[hydrogen-adom-desktop-windows]] companion.)+- **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 the discovered port).+- **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+- [[hydrogen-adom-desktop-windows]] — Windows/WSL2 host specifics (paths, winget installs, loopback forwarding, taskkill)+- [hd-embedded-ad](../hd-embedded-ad/SKILL.md) — how HD bundles + embeds AD (tray, lifecycle, standalone↔embedded)+- [hydrogen-bridges](../hydrogen-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+- [hydrogen-self-screenshot](../hydrogen-self-screenshot/SKILL.md) — host screenshots + window control+- [hd-recording](../hd-recording/SKILL.md) — screen vs tab recording (AD-served)+- [hydrogen-notifications](../hydrogen-notifications/SKILL.md) — desktop toasts; [hydrogen-captions](../hydrogen-captions/SKILL.md) — on-screen caption overlay+- [hydrogen-open-url](../hydrogen-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 across runtimes
skills/hydrogen-adom-menu/SKILL.mdadded+90
@@ -0,0 +1,90 @@+---+name: hydrogen-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 hydrogen-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 **hydrogen-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 **hydrogen-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 **hydrogen-workspace-lifecycle**) |+| *— Admin —* | |+| **Setup Steps** | Show the setup-steps panel — the setup cascade for the active runtime (16 steps under WSL2); see **hydrogen-setup-steps** / **hydrogen-setup** |+| **Virgin Reset** | Open the virgin-reset panel — wipe + re-run setup (see **hydrogen-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 **hydrogen-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 **hydrogen-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+- **hydrogen-profile-menu** — the upper-RIGHT avatar dropdown (don't confuse the two)+- **hydrogen-settings** — the settings tree behind the **Settings** item+- **hydrogen-api** — the control-API surface behind **API Explorer…**+- **hydrogen-capture-share** / **hd-recording** — the AV capture/record controls (Dev Toolbar)+- **hydrogen-setup** / **hydrogen-setup-steps** — what Setup Steps / Virgin Reset do+- **hydrogen-eval** — drive these menu items / inject UI from the workspace
skills/hydrogen-api/SKILL.mdadded+317
@@ -0,0 +1,317 @@+---+name: hydrogen-api+description: >+  Complete reference for Hydrogen Desktop control API endpoints. The control+  API runs on a dynamic port (find it via port discovery). Use these endpoints+  to drive HD programmatically: manage the workspace runtime, 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, runtime status, reload vscode,+  browser profiles, open url, virgin reset 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 loopback address. 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 inside the workspace — USE THIS. HD writes its live control URL to a discovery+# file every launch, 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.++> For the host-side ways to discover the port (HD log/`ports.json`) and the+> host runtime endpoints (`/wsl/*`, `/docker/*`, `/system/reboot`), see the+> [[hydrogen-api-windows]] companion skill.++## 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 port HD has bound, with status 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 `hydrogen-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.++| 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 | `/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` |++> Runtime-specific endpoints — the WSL `/wsl/*` tables, the legacy Docker+> `/docker/*` tables, and `/volume-delete` (Docker-only) — live in the+> [[hydrogen-api-windows]] companion skill.++## 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 **hydrogen-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`.+> 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 |++## 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) |++> Host OS reboot (`/system/*`) is platform-specific — see the+> [[hydrogen-api-windows]] companion skill.++## 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, runtime state, 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 |++> The `/test/probe-dialogs` endpoint (classifies UAC / WSL2-update / MSI /+> Docker-EULA blocker windows) is Windows-specific — see the+> [[hydrogen-api-windows]] companion.++## 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 step API instead:+```+POST /setup/step/<id> {"continue_after": true}+```
skills/hydrogen-bridges/SKILL.mdadded+390
@@ -0,0 +1,390 @@+---+name: hydrogen-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**, **hydrogen-adom-desktop** |+| `notify` | desktop toast notifications | this skill |+| `shell` | run a command on the user's PC (approval-gated) | **hydrogen-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++- **hydrogen-ui** -- Adom menu layout where Bridge Manager lives+- **hydrogen-networking** -- port architecture (conventional bridge ports + dynamic registration)+- **hydrogen-adom-menu** -- the API Explorer (has bridge-related endpoints)+- **hydrogen-settings** -- HD settings surface+- **hydrogen-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/hydrogen-captions/SKILL.mdadded+135
@@ -0,0 +1,135 @@+---+name: hydrogen-captions+description: >+  Paint labeled caption text on the Hydrogen Desktop workspace for demos/walkthroughs —+  captions are CAPTURED BY RECORDINGS so they show up in the finished video. Drive the+  WORKSPACE caption with `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).+  Distinct from toasts (notifications) and TTS (audio). NOTE: a full-DESKTOP overlay+  (over every window, on top of other apps) exists as a platform-specific companion —+  see [[hydrogen-captions-windows]]. 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, caption command, adom-cli hydrogen+  caption, walkthrough label, demo callout, status overlay, label 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 the Hydrogen Desktop workspace** 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: run DRC →").++> 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+>   `hydrogen-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 internal **auth-overlay caption** HD draws for login/auth flows — not something+>   you trigger for demos. The workspace caption below is the one you drive.++## Two caption surfaces — pick by WHAT you're recording++HD has **two** caption surfaces, each 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** | platform-specific (see [[hydrogen-captions-windows]]) | **over the WHOLE desktop**, above every window | a **full-screen / desktop** recording | the demo shows **other apps** (KiCad, Fusion, a browser) or the whole screen |++**Match the caption to the recording.** A `screenshot panel` / workspace capture sees the+**webview** content, so it captures the **workspace** caption but NOT a 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" → the+> desktop overlay in the platform companion (the only one with corners, ids, and x/y).++## 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 — same principle 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. 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+the platform companion uses **milliseconds** — the two surfaces differ; don't cross the+units.)++> **Web-Hydrogen vs HD parity:** the **command + the SECONDS unit + `hide` are identical**+> on both (center/large rendering, `-d 20` held visibly). BUT the sane **lifecycle below+> (30s default / 300s cap / persist)** is verified on HD; web Hydrogen may still return 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.**++### Lifecycle (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` 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.+- `-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}`.++## Desktop overlay caption (over the whole screen)++For a caption that floats over **every window** (above other apps, the whole desktop) —+with ids, corners, and normalized x/y placement — HD provides a native always-on-top,+click-through desktop overlay. That surface and its arguments are **platform-specific**;+see **[[hydrogen-captions-windows]]**.++## Patterns++- **Walkthrough / demo callouts** — drop a `center` caption per step. Combine with a+  recording so the callouts end up in the finished video. Pair with TTS for narration.+- **Status corner / point-at-a-thing** — these need named corners or normalized x/y, which+  only the desktop overlay provides; see [[hydrogen-captions-windows]].++## Related skills+- [hydrogen-adom-desktop](../hydrogen-adom-desktop/SKILL.md) — the relay that carries caption verbs to the host+- `adom-tts` — *audio* narration to pair with on-screen captions (captions are visual-only)+- [[hydrogen-captions-windows]] — the full-desktop always-on-top overlay caption surface
skills/hydrogen-capture-share/SKILL.mdadded+148
@@ -0,0 +1,148 @@+---+name: hydrogen-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 [hydrogen-adom-desktop](../hydrogen-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+> [hydrogen-permissions](../hydrogen-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)+- [hydrogen-self-screenshot](../hydrogen-self-screenshot/SKILL.md) — CDP per-panel screenshots + shotlog visual-verify loop+- [hydrogen-permissions](../hydrogen-permissions/SKILL.md) — WebView2 auto-granting OS mic/cam/notification permissions (a *different* layer from this consent dialog)+- [hydrogen-adom-desktop](../hydrogen-adom-desktop/SKILL.md) — AD's dialog-free host OS screenshots (`desktop_screenshot_screen/_window`)
skills/hydrogen-claude-auth/SKILL.mdadded+86
@@ -0,0 +1,86 @@+---+name: hydrogen-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 workspace, how HD backs them up so a workspace 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 [hydrogen-adom-auth](../hydrogen-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** (in HD's app-data directory, as `replay-claude-credentials.json`) so future workspace rebuilds can replay them. The exact host path is platform-specific — see the platform layer for where it lives on your OS.++## Where Claude credentials live++| Layer | Location | Purpose |+|-------|----------|---------|+| Inside workspace (live) | `/home/adom/.claude/.credentials.json` | Active credentials the extension and `claude` CLI both read |+| Host-side backup | `replay-claude-credentials.json` in HD's app-data dir on the host | 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 the `replay-claude-credentials.json` backup from HD's host app-data dir+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 the webview eval channel. |+| 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 the `replay-claude-credentials.json` backup from the 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 the `replay-claude-credentials.json` backup (in HD's host app-data dir) 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 local port that HD hasn't forwarded to the host. 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`. (How host↔workspace port forwarding works is platform-specific — see the platform layer.)++## Related skills++- [hydrogen-adom-auth](../hydrogen-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+- `hydrogen-setup-steps` — the `claude-auth` step and the auth cascade are part of setup+- `hydrogen-volume` — explains why the host-side backup survives workspace rebuild
skills/hydrogen-container-stats/SKILL.mdadded+134
@@ -0,0 +1,134 @@+---+name: hydrogen-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 ([[hydrogen-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 `[[hydrogen-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 `[[hydrogen-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, gallia, 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++- `[[hydrogen-workspace-monitoring]]` — the 15s state poll + floaty states behind these badges+- `[[hydrogen-container]]` — workspace context: relay, bridges, env vars
skills/hydrogen-container/SKILL.mdadded+131
@@ -0,0 +1,131 @@+---+name: hydrogen-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 (`[[hydrogen-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 gallia 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 `gallia/scripts/bootstrap.sh`.++Most tooling is NOT installed by a step — it's BAKED into the golden image at image-build time (gallia, the Adom CLIs, 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 `[[hydrogen-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-gallia` / `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+- `GALLIA_SERVICE=local` — tells gallia this is 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 `[[hydrogen-container-windows]]`.)++## Related skills++- `[[hydrogen-container-stats]]` — the title-bar CPU/RAM indicator for this workspace+- `[[hydrogen-setup-steps]]` — what each setup step did to prepare your workspace
skills/hydrogen-desktop-sse/SKILL.mdadded+245
@@ -0,0 +1,245 @@+---+name: hydrogen-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. Covers the full pipeline, routing, env vars, field format differences, port discovery, 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, discovery port.+---++# Hydrogen Desktop — Local Workspace API + SSE++> Platform note: HD runs the same local workspace API on every platform (Windows/WSL2,+> macOS, Linux). For the WSL2-specific networking details (mirrored loopback,+> `%APPDATA%\hydrogen-desktop\ports.json`, the `HD_RUNTIME=docker` runtime, relay+> same-port behavior), see **[[hydrogen-desktop-sse-windows]]**.++## Architecture++```+Workspace adom-cli (the local workspace)+    → 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 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. The workspace reaches HD at `127.0.0.1:47083`.++## 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 |+| 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:++| 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 |++`127.0.0.1` reaches HD on the host from inside the workspace. 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:** clients discover the relay ports from `~/.adom/cli-relay-ports.json`+> (fallback 8765/8766), not from `/discover`. Relay port mapping/exposure varies by+> platform — see **[[hydrogen-desktop-sse-windows]]**.++### 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 | 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 | (platform-dependent) | No | 8765 |+| Relay HTTP | (platform-dependent) | No | 8766 |++On first launch, defaults are written without scanning. Workspace-side ports (8080, 8765, 8766) are always fixed — only host-side ports are dynamic. The `ports.json` location and relay host mapping are platform-specific — see **[[hydrogen-desktop-sse-windows]]**.++## 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/hydrogen-eda-discovery/SKILL.mdadded+108
@@ -0,0 +1,108 @@+---+name: hydrogen-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 `hydrogen-adom-desktop`, `hydrogen-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)+```++The KiCad/Fusion bridges work the same on every platform. For tools with **no bridge** (Altium,+OrCAD, Cadence/Allegro, EAGLE, DipTrace, Eplan, ...), there's no bridge verb — detect them by+**install path / running process** using `adom-desktop run_script`. The exact probe is+platform-specific:++- **Windows:** scan `C:\Program Files` / `C:\Program Files (x86)` and `Get-Process` via a+  PowerShell `run_script` — see **[[hydrogen-eda-discovery-windows]]** for the ready-to-run probe.+- **macOS:** scan `/Applications` (and `~/Applications`) for the app bundles, plus `pgrep`.+- **Linux:** `which`/`command -v` the CLI binaries and check common install dirs, plus `pgrep`.++(Whatever the platform: 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 (`hydrogen-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+- [hydrogen-adom-desktop](../hydrogen-adom-desktop/SKILL.md) / [hydrogen-bridges](../hydrogen-bridges/SKILL.md) — the `adom-desktop` relay + KiCad/Fusion bridge verbs (in-distro names; the `adom-desktop-*` skills are the cloud-side equivalents)+- [[hydrogen-eda-discovery-windows]] — the Windows/PowerShell no-bridge install-path + process probe+- `pup` — driving fab-vendor upload pages; [hydrogen-open-url](../hydrogen-open-url/SKILL.md) — opening vendor sites+- the Adom wiki publish skills — sharing the bridge/app you build with the community
skills/hydrogen-eval/SKILL.mdadded+159
@@ -0,0 +1,159 @@+---+name: hydrogen-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.+---++# hydrogen-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+- **hydrogen-api** — the full control API surface+- **hydrogen-ui** / **hydrogen-adom-menu** — where UI elements live+- **hydrogen-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/hydrogen-instapcb/SKILL.mdadded+77
@@ -0,0 +1,77 @@+---+name: hydrogen-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+[hydrogen-open-url](../hydrogen-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 `hydrogen-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 [hydrogen-eda-discovery](../hydrogen-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+- [hydrogen-eda-discovery](../hydrogen-eda-discovery/SKILL.md) — the sibling post-setup discovery tab (EDA tools); same detect→offer pattern + the AI-first/Adom-wiki frame+- [hydrogen-bridges](../hydrogen-bridges/SKILL.md) — KiCad bridge verbs (open/plot/export the board)+- `pup` — driving the InstaPCB upload/quote UI; [hydrogen-open-url](../hydrogen-open-url/SKILL.md) — opening the app (wv vs pup)
skills/hydrogen-monitor/SKILL.mdadded+183
@@ -0,0 +1,183 @@+---+name: hydrogen-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 **hydrogen-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/hydrogen-notifications/SKILL.mdadded+127
@@ -0,0 +1,127 @@+---+name: hydrogen-notifications+description: >+  How Hydrogen Desktop reaches the user OUTSIDE the window — native OS toast+  notifications + taskbar/dock 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 attention request until the user looks)+  and how to fire one with the `notify` desktop command / `adom-desktop notify_user`.+  Also the in-app `/ui/toast` (a message INSIDE the HD window) and Pup's+  browser_alert_window taskbar/dock nudge. The native OS-toast backend is+  platform-specific (see [[hydrogen-notifications-windows]] for the Windows path);+  cross-platform behavior is documented here. Trigger words — notify the user, send a+  notification, desktop notification, toast, os toast, native toast, taskbar flash, dock+  bounce, 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, 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 **toast**,+and for urgent cases a **persistent attention request** (a taskbar flash / dock bounce,+depending on platform). 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.++> **Platform note:** the native OS toast is delivered by a per-platform backend. The full+> experience (a rich OS toast plus a persistent attention request) is best on Windows+> (WinRT toasts + taskbar attention — see [[hydrogen-notifications-windows]]); macOS has a more+> limited path (an `osascript`-style notification); Linux has no native toast path. The+> `notify_user` API itself is the same everywhere; what differs is how richly the OS+> surfaces it.++## 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. (On some platforms HD prefixes the app identity — see the platform companion.) |+| `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 buttons in the toast. Empty/omitted = no buttons. |++### Levels — and the `emergency` attention request++- `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 attention+  request** (a taskbar flash on Windows, a dock bounce on macOS) that **keeps going until+  the user actually interacts with the window/taskbar 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 Windows-specific toast identity/title behavior (the AUMID "Adom" title quirk) and the+> WinRT/orange-taskbar-flash details live in [[hydrogen-notifications-windows]].++## 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/dock nudge (`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 / bounces the dock** 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+- [hydrogen-adom-desktop](../hydrogen-adom-desktop/SKILL.md) — AD is what carries `notify_user` to the host; `ping`/`status` to verify the bridge and the `notify` capability+- [hydrogen-bridges](../hydrogen-bridges/SKILL.md) — the full AD capability list (`notify`, …) and Pup's `browser_alert_window`+- [[hydrogen-notifications-windows]] — the Windows toast backend (WinRT, AUMID "Adom" title, orange taskbar flash)
skills/hydrogen-open-url/SKILL.mdadded+199
@@ -0,0 +1,199 @@+---+name: hydrogen-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.+---++# 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`)++**`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/hydrogen-overview/SKILL.mdadded+69
@@ -0,0 +1,69 @@+---+name: hydrogen-overview+description: >+  Master index for the platform-generic Hydrogen Desktop skills (the+  hd-bootstrap set). Start here to find the right skill for any HD topic.+  These skills are runtime-neutral and apply on every host HD runs on+  (Windows, macOS, Ubuntu); platform layers add their own *-windows (etc.)+  companions. 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. HD runs+on Windows, macOS, and Ubuntu.++This index lists the **platform-generic** HD skills (the `hd-bootstrap` set).+Every fact in these skills holds regardless of host OS. Where a topic has+host-specific mechanics (e.g. how the workspace is hosted, host networking,+host-side recording), the platform layer adds a companion skill — on Windows+those are the **`hydrogen-windows-bootstrap`** `*-windows` skills (and equivalents+for other platforms). The generic skill cross-references its companion.++## The generic HD skills (hd-bootstrap)++| Skill | What it covers |+|---|---|+| **hydrogen-adom-auth** | Adom login/logout, session token, profile menu, how auth feeds setup. |+| **hydrogen-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). |+| **hydrogen-adom-menu** | The upper-left Adom logo menu (Settings, Ports, API Explorer, DevTools, Setup/Virgin Reset) + About dialog + version readout. |+| **hydrogen-api** | Control API reference. Runtime-neutral endpoints. |+| **hydrogen-bridges** | Built-in KiCad/Fusion/Puppeteer bridges + extensible wiki catalog (e.g. Blender) + AD capability list. |+| **hydrogen-captions** | Paint labeled text on the user's screen — an always-on-top caption overlay for demos / step callouts. |+| **hydrogen-capture-share** | The screen-share + "AI wants access" approval + countdown the AI triggers to capture the editor; tab/screen scopes, Webview/Pup window mode. |+| **hydrogen-claude-auth** | How Claude Code signs in: OAuth via Browser Picker, credential backup, the `claude-auth` step. |+| **hydrogen-container** | Context for Claude running inside the workspace: relay, direct connect, bridge commands, env vars. |+| **hydrogen-container-stats** | The title-bar CPU/RAM "Container" indicator and how its stats are gathered. |+| **hydrogen-desktop-sse** | Local workspace API + SSE — how adom-cli inside the workspace controls HD (replaces Carbon locally). |+| **hydrogen-eda-discovery** | EDA tool discovery / setup flow. |+| **hydrogen-eval** | Inject JS into any HD surface (shell/workbench/wiki/Claude) via /eval-in — add-ons, overlays, highlights, drive UI. |+| **hydrogen-instapcb** | InstaPCB integration in HD. |+| **hydrogen-monitor** | Real-time SSE event stream + Monitor-tool patterns for watching workspace mutations. |+| **hydrogen-notifications** | Reach the user outside the window — native OS toasts + emergency taskbar flash via `notify_user`. |+| **hydrogen-open-url** | The decision guide for "open a website" — the ways (webview tab, HD window, Pup, native browser, picker, headless curl) and when to pick each. |+| **hydrogen-overview** | This index. |+| **hydrogen-permissions** | Why mic/camera/clipboard/notifications "just work" in HD — webview permissions are auto-granted, so webview apps see no Allow prompts. |+| **hydrogen-profile-menu** | The avatar dropdown: profile/repos/molecules links and logout. |+| **hydrogen-self-screenshot** | How the AI screenshots HD panels + any host window (window control, multi-monitor), injects to shotlog, runs visual self-checks. |+| **hydrogen-self-update** | How HD updates itself. |+| **hydrogen-settings** | HD's Settings dialog + the full preferences tree (theme, vim mode, 3D control style, launch-on-login, AD window behavior). |+| **hydrogen-setup** | The setup panel, Run All, Rollback, Virgin Reset. |+| **hydrogen-setup-steps** | What each setup step did to prepare your workspace. |+| **hydrogen-tab-icons** | Webview tab icon system — `displayIcon`, icon resolution, SVG data URLs. |+| **hydrogen-ui** | HD's UI: menus, dialogs, panels, CDP-driven navigation, brand/CSS conventions. |+| **hydrogen-welcome** | The static welcome page shown after setup completes. |+| **hydrogen-who-am-i** | Answer "who am I / what can I do here" — identity, resources, environment intro. |+| **hydrogen-workspace-monitoring** | Workspace + code-server health monitoring, floaty states, the 15s poll, auto-start. |+| **hydrogen-workspace-updater** | The workspace-tooling updater daemon. |++## Platform layers++Platform-specific mechanics live in a platform layer that ships alongside this+set and adds `*-windows`-style companion skills (e.g. `hydrogen-container-windows`,+`hydrogen-container-stats-windows`, `hydrogen-networking`, `hydrogen-volume`,+`hydrogen-workspace-lifecycle`). When a generic skill above has a host-specific detail,+it cross-references its companion. On a given host you'll have the generic set+plus that host's platform layer.
skills/hydrogen-permissions/SKILL.mdadded+69
@@ -0,0 +1,69 @@+---+name: hydrogen-permissions+description: >+  Hydrogen Desktop AUTO-GRANTS every webview 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+  hydrogen-capture-share). HD's webview engine is platform-specific; the per-engine+  implementation detail (e.g. WebView2 on Windows) lives in the platform companion (see+  [[hydrogen-permissions-windows]]). Trigger words — permission, permissions, webview 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 webview permission++Hydrogen Desktop's webview registers a permission 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.++Every request is unconditionally allowed. In the HD log you'll see lines like `[perm]+auto-granted permission kind …` and, at startup, `[perm] Registered PermissionRequested+auto-grant handler`.++> **Platform note:** HD uses a different webview engine per platform (e.g. WebView2 on+> Windows, WebKit on mac/Linux). The auto-grant policy is the same everywhere — every+> permission is allowed up front — but the exact engine API is platform-specific. The+> Windows/WebView2 implementation and its code evidence are in [[hydrogen-permissions-windows]].++## 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 privacy toggle for the camera/mic+  being off for the app entirely.+- This is 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 webview 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: webview site+permissions are silently allowed; the AI's capture/share request is a real, user-facing+approval. See `hydrogen-capture-share`.++## Related skills+- `hydrogen-capture-share` — the SEPARATE, user-approved screen-share / mic consent the AI requests+- [hydrogen-open-url](../hydrogen-open-url/SKILL.md) — opening URLs/webview apps inside HD (where these permissions apply)+- [[hydrogen-permissions-windows]] — the WebView2 (Windows) implementation + lib.rs evidence
skills/hydrogen-profile-menu/SKILL.mdadded+83
@@ -0,0 +1,83 @@+---+name: hydrogen-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 [hydrogen-ui](../hydrogen-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 [hydrogen-adom-auth](../hydrogen-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++- [hydrogen-adom-auth](../hydrogen-adom-auth/SKILL.md) — the Adom session token, login flow, profile loading+- [hydrogen-claude-auth](../hydrogen-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+- [hydrogen-ui](../hydrogen-ui/SKILL.md) — the Adom menu (top-left, different dropdown)
skills/hydrogen-self-screenshot/SKILL.mdadded+236
@@ -0,0 +1,236 @@+---+name: hydrogen-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. 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 (`hydrogen-ui`: `ui/invoke`+  to open a menu/dialog, then `/screenshot` its selector). Identical across web+  Hydrogen and HD. 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, 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 image resizer needed.**++## Which screenshot tool? — the priority ladder++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.++## 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** — the CDP path coexists. HD makes this deterministic:++- **Pre-flight `GET /capture/availability`** → reports the recording state plus, per method,+  reliability + whether it coexists with a recording, a `recommended` method, and a `_hint`.+  Check it **only when a recording might be running.**+  - **`recommended` is STATE-DEPENDENT and agrees with the default:** when+    `recording.active === false` it returns **`"sse"`** (≡ `adom-cli hydrogen screenshot`, the+    web-Hydrogen-consistent default); 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. Your everyday default remains+    `adom-cli`; CDP is the during-a-recording fallback.+- **`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"}], _hint}` — never an empty body. **On+  `capture_busy`: read `alternatives` and SWITCH — do NOT blindly retry the SSE path.**+- Authoritative recording state: `GET /recording/native/status`. See `hd-recording`.++## 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`. The HD `screen` scope captures the real desktop and saves the PNG into+your container, same as the others.++## 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`** (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`).++### 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.++## 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 `hydrogen-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**. 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)++- **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.**+`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. The `adom-cli` SSE path+(`screenshot panel --name ScreenA`) also activates + restores. 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. 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 **`hydrogen-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 `hydrogen-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 hydrogen-ui -d "<desc>" <path>`+(shotlog resizes internally — another reason you don't need `convert`).++## Related skills+- [hydrogen-capture-share](../hydrogen-capture-share/SKILL.md) — the sharing/approval UX behind `screenshot`+- [hd-recording](../hd-recording/SKILL.md) · [hydrogen-captions](../hydrogen-captions/SKILL.md) — sibling `adom-cli hydrogen` AV verbs+- `shotlog` — the screenshot log viewer/injector
skills/hydrogen-self-update/SKILL.mdadded+50
@@ -0,0 +1,50 @@+---+name: hydrogen-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/hydrogen-settings/SKILL.mdadded+110
@@ -0,0 +1,110 @@+---+name: hydrogen-settings+description: >+  Hydrogen Desktop's Settings dialog and the full preference tree the user can+  change — theme, vim mode, launch-on-login, 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 at startup", "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 login, launch on startup, launch on boot, 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+[hydrogen-adom-menu](../hydrogen-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 on login / at startup (→ "make HD launch at startup" / "stop HD launching on login" = 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+[hydrogen-adom-desktop](../hydrogen-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 login / at startup"** / "don't launch on login" → `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+- [hydrogen-adom-menu](../hydrogen-adom-menu/SKILL.md) — the logo menu that opens Settings (and Ports, API Explorer, etc.)+- [hydrogen-ui](../hydrogen-ui/SKILL.md) — the broader HD UI / panel layout this dialog sits in+- [hydrogen-adom-desktop](../hydrogen-adom-desktop/SKILL.md) — what the `adom_desktop.*` prefs control (the embedded AD)
skills/hydrogen-setup-steps/SKILL.mdadded+134
@@ -0,0 +1,134 @@+---+name: hydrogen-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, gallia 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 [[hydrogen-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, gallia, the Adom CLIs, 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+[[hydrogen-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-gallia`, `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 gallia /+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 gallia, 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+[[hydrogen-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 [[hydrogen-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 [[hydrogen-setup-steps-windows]].++## Related skills++- [[hydrogen-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+- `hydrogen-setup` — the setup-panel UX and virgin-reset toggles+- `hd-browser-picker` — used by claude-auth for the Claude auth OAuth flow+- `hydrogen-adom-auth` — how the Adom session token (inject-api-key) gets injected
skills/hydrogen-setup/SKILL.mdadded+192
@@ -0,0 +1,192 @@+---+name: hydrogen-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 [[hydrogen-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, gallia, the Adom CLIs, 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 [[hydrogen-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-gallia`, `install-hd-skills`,+`verify-adom-desktop`, `install-claude-cli`, `install-claude-ext`,+`write-vscode-settings`, `set-trusted-domains`, `clean-layout`. gallia, the+Adom CLIs, 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 [[hydrogen-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 [[hydrogen-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: gallia, the Adom CLIs, 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 [[hydrogen-setup-windows]].
skills/hydrogen-tab-icons/SKILL.mdadded+338
@@ -0,0 +1,338 @@+---+name: hydrogen-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/hydrogen-ui/SKILL.mdadded+212
@@ -0,0 +1,212 @@+---+name: hydrogen-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+- See `hydrogen-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 hydrogen-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 the webview developer tools+- **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 `hydrogen-bridges`.++**Workspace / runtime section** (section label shows live status badge: `RUNNING`, `STOPPED`, etc.)+- **Restart** — restarts the workspace (terminates and re-ensures code-server). The exact mechanism is runtime-specific.+- **Stop** — stops the workspace. The exact mechanism is runtime-specific.++**ADMIN**+- **Setup Steps** — opens the setup panel (the setup cascade for the active runtime); see hydrogen-setup-steps+- **Virgin Reset** — opens the virgin reset section of the setup panel; see hydrogen-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 `hydrogen-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, reachable from the+workspace). 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 `hydrogen-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 **[hydrogen-self-screenshot](../hydrogen-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 the HD CDP eval channel. 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. Eval JS against the CDP endpoint (its port comes from `/discover` as `cdp`).++### Find and click menu items+```js+// Open Adom menu+document.querySelector('.logo-button')?.click()++// Find a menu item by text+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()+```++### Click setup panel buttons+```js+document.querySelector('.run-all').click()                 // Run All+document.querySelector('.virgin-reset-btn')?.click()       // Virgin Reset dropdown+document.querySelector('.virgin-go')?.click()              // Wipe Selected (execute virgin reset)+document.querySelector('.close-btn,.setup-close')?.click() // Close setup panel+```++### Query step states+```js+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()}+}))+```++## 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/hydrogen-welcome/SKILL.mdadded+79
@@ -0,0 +1,79 @@+---+name: hydrogen-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 — the `open-welcome` gate (the last step of the+  setup cascade for the active runtime).+- Can be opened manually via the API++## How to open it++### From inside the workspace (the standard way)++This is how the setup steps do it, and how any AI inside the workspace should:++```bash+adom-cli hydrogen webview open-or-refresh \+  --name Welcome \+  --url http://localhost:1420/welcome.html \+  --panel-id desktop-pane-right+```++`localhost:1420` is HD's dev file server running on the host (where HD itself runs),+NOT anything inside the workspace. HD's webview renders the URL against the host, so the+page resolves on whatever platform HD is running on.++### 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 Adom wiki (https://wiki.adom.inc)+- 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/hydrogen-who-am-i/SKILL.mdadded+94
@@ -0,0 +1,94 @@+---+name: hydrogen-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++- [hydrogen-adom-auth](../hydrogen-adom-auth/SKILL.md) — the session token that gives `adom-cli` its identity+- [hydrogen-claude-auth](../hydrogen-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+- `hydrogen-container` — the exact Ubuntu image / arch / pre-installed tools you'll mention+- [hydrogen-bridges](../hydrogen-bridges/SKILL.md) — KiCad / Fusion / Puppeteer bridge capabilities to mention
skills/hydrogen-workspace-monitoring/SKILL.mdadded+160
@@ -0,0 +1,160 @@+---+name: hydrogen-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 [[hydrogen-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+[[hydrogen-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 `hydrogen-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 `hydrogen-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+[[hydrogen-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++- [[hydrogen-workspace-monitoring-windows]] — the WSL2 "Not Available" floaty, `wsl --terminate` stop, the 7380 reachability check, the no-daemon framing+- `hydrogen-workspace-lifecycle` — provision/export/remove/stop, the never-touch-global rule, control endpoints+- `hydrogen-container-stats` — the title-bar CPU/RAM bars and `workspace_stats()` probe+- `hydrogen-networking` — code-server port, control port
skills/hydrogen-workspace-updater/SKILL.mdadded+59
@@ -0,0 +1,59 @@+---+name: hydrogen-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.*