Skip to content

feat: AgentField Desktop (Mac-first) + Windows enablement, bundled CLI, tray, deep links#752

Draft
AbirAbbas wants to merge 27 commits into
mainfrom
feat/desktop-app
Draft

feat: AgentField Desktop (Mac-first) + Windows enablement, bundled CLI, tray, deep links#752
AbirAbbas wants to merge 27 commits into
mainfrom
feat/desktop-app

Conversation

@AbirAbbas

@AbirAbbas AbirAbbas commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

AgentField Desktop + Windows enablement

A Mac-first desktop companion (Docker-Desktop analog) plus the Windows fixes that make the whole stack run natively there. Everything below is runtime-verified on real Windows 11 (packaged builds, not dev mode); macOS is compile/package/test-verified in CI on macos-14 and needs one manual smoke (checklist at the bottom).

The app

Views — Dashboard (stat tiles + recent activity), Agents (status badges + Start / Stop / Restart per row), Activity (in-flight runs with live pulse + recent tail), Install (curated catalog: every Agent-Field org repo with an agentfield-package.yaml — swe-planner, pr-af, sec-af, cloudsecurity-af — one-click af install with streamed progress), Settings.

Mac-first chrome — no menu-bar clutter (minimal app menu on macOS only), hiddenInset titlebar + traffic-light inset + sidebar vibrancy (macOS) / native control overlay (Windows), system font, light/dark from the OS, brand "•af" icon set rendered from the web-UI logo paths (real ICNS, exe icon, window icon, tray glyphs — npm run icons regenerates).

Autopilot (the point: agents are already answering when Claude/Codex asks) —

  • Open at login (hidden, tray-only) · start the control plane when nothing is listening · per-agent auto-start; stale post-reboot registry entries get restarted, not skipped.
  • Windows/Linux tray with status glyph (brand dot goes gold when the control plane runs), close-to-tray; macOS keeps af-tray as its menu-bar companion.
  • agentfield://dashboard|agents|activity|install|settings deep links + single instance; the macOS af-tray now opens the desktop app via deep link when installed and falls back to the web UI (the failed open is the detection).

Bundled CLI — the package carries af (extraResources; npm run bundle-cli stages it). Per-launch resolution: managed ~/.agentfield/bin (same location/names as the curl installer → no double install) → PATH → bundled, with a MIN_AF_VERSION gate and an "Update AgentField" button in Settings. Fresh machines get auto-provisioned (both binary names, Windows user PATH registered). Launch also runs af skill install --non-interactive (toggleable) so detected coding agents always know AgentField.

Windows enablement (control plane)

  • Checked-in symlink control-plane/agentfield.yaml removed (materializes as a broken text file on Windows and kills viper).
  • Local-mode storage path defaulting fixed (cmd/af/main.go).
  • Python interpreter probing that survives Microsoft Store aliases (python3 stub answering exit 9009) — run-probes candidates instead of LookPath, with uv provisioning verified in production during a real SWE-AF install.
  • PYTHONUTF8=1 for spawned agents (cp1252 + emoji log spam), on both spawn paths.
  • Services-package test compile fix under GOOS=windows.

af-tray (macOS)

Deep-links into the desktop app with browser fallback; assets/appicon.icns was a renamed PNG, now a real ICNS wearing the same mark.

CI added

  • desktop.yml: typecheck + 90 vitest tests + unsigned electron-builder package on macos-14 and windows-latest.
  • control-plane.yml gains tray-darwin (macos-14 go build + go vet of cmd/af-tray) — previously the darwin CGO tray was only compiled at release time.

Known gaps / notes

  • Not yet live-exercised on Windows (blocked by a foreign service owning :8080 during testing): autostart actually spawning af server (the don't-fight-the-port guard is what got exercised instead), the "unhealthy control plane" visual state, and the gold/active tray glyph on a live control plane. Logic is unit-tested; these fall out of any run against a free :8080.
  • The NSIS/DMG installers build but haven't been installed end-to-end (testing used unpacked builds).
  • Unsigned packaging; signing/notarization before distribution. Release pipeline doesn't publish desktop artifacts yet.
  • The embedded skill teaches building on AgentField; an agentfield-use skill (using installed agents) is a queued follow-up.

macOS smoke checklist

cd desktop && npm ci
npm run bundle-cli        # needs Go; add `-- full` for embedded web UI (build web/client first)
npm run dist              # DMG + zip in release/ (unsigned: right-click → Open)
  1. Chrome: traffic lights inset over the sidebar, vibrancy, no File/Edit menus beyond the minimal app menu, light/dark.
  2. Deep links (after first launch): open agentfield://agents, open agentfield://settings — window focuses and switches view.
  3. Settings → AgentField CLI card: with a curl-installed af it should say "installed in ~/.agentfield"; on a clean account it should auto-provision the bundled copy.
  4. Autostart: quit everything, toggle "Start the control plane automatically", relaunch → af server comes up (port 8080 free), tray dot on the af-tray side goes active.
  5. af-tray → desktop handoff: build cmd/af-tray, run it, click "Open Dashboard" — should open the desktop app (not the browser); delete the app and it should fall back to the web UI.
  6. Install + lifecycle: install an agent from the catalog, Start/Restart/Stop it from Agents.

🤖 Generated with Claude Code

AbirAbbas and others added 5 commits July 10, 2026 15:20
Docker-Desktop-style dashboard for non-technical users: shows control
plane health (GET /health) and the locally installed agent nodes from
~/.agentfield/installed.yaml, cross-checked against GET /api/v1/nodes
for a running/stopped/unknown badge per agent. Polls every 5s with a
manual Refresh button and graceful empty states.

Electron + electron-vite + React + TypeScript, plain CSS. Secure
defaults: contextIsolation on, nodeIntegration off, sandboxed renderer,
single contextBridge API. All data access is isolated in
src/main/agentfield.ts with a marked seam to later swap the registry
read to `af list -o json`. 29 vitest unit tests cover registry parsing,
health mapping, and badge derivation.

Self-contained under desktop/ (own package.json); no packaging
(electron-builder) yet, and the GUI is untested in this headless
environment — typecheck, production build, and unit tests all pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the stale commented-out windows block with a real
agentfield-windows-amd64 build mirroring the linux/darwin ones
(goreleaser appends .exe on its own). Groundwork only: the release
workflow's build matrix filters by --id and does not build this id yet;
shipping the artifact needs a follow-up matrix entry (windows runner,
or mingw-w64 on the linux runner for the CGO sqlite dependency).

Also modernizes archives.builds/format to ids/formats so
`goreleaser check` passes clean again (both were deprecated).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`af stop` used process.Signal(os.Interrupt) and a signal-0 probe, both
unsupported on Windows (and os.FindProcess always succeeds there, so
the liveness check was meaningless). Extract the two process operations
into build-tagged helpers: proc_unix.go keeps the existing SIGINT +
signal-0 behaviour; proc_windows.go uses taskkill for the graceful
request and a tasklist PID query for liveness.

stop.go changes are limited to swapping the two call sites and the
now-unused syscall import. Windows paths are compile-verified only
(GOOS=windows cross-build), not yet tested on a real Windows machine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`af logs` shelled out to tail(1), which does not exist on Windows. The
tail/follow commands now go through one tailCommand helper: unchanged
tail(1) invocations on Unix, PowerShell Get-Content -Tail (-Wait for
follow) on Windows, with proper single-quote escaping of the log path.
Windows path is compile-verified only, not yet run on a real machine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Go agent nodes declare unix-style binary paths in their manifests
(entrypoint.start: bin/foo). On Windows the install-time `go build -o`
output now carries the conventional .exe extension, and the runner's
GoBinaryProgram resolves an extensionless start path to the built .exe
when present. No behaviour change on other platforms; windows path is
compile-verified only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage gate

Thresholds from .coverage-gate.toml: per-surface ≥ 84%, aggregate ≥ 85%, max per-surface regression ≤ 1.0 pp, max aggregate regression ≤ 0.50 pp.

Surface Current Baseline Δ
control-plane 87.00% 87.40% ↓ -0.40 pp 🟡
sdk-go 92.50% 92.00% ↑ +0.50 pp 🟢
sdk-python 93.76% 93.73% ↑ +0.03 pp 🟢
sdk-typescript 90.42% 90.42% → +0.00 pp 🟢
web-ui 84.76% 84.79% ↓ -0.03 pp 🟡
aggregate 85.59% 85.75% ↓ -0.16 pp 🟡

✅ Gate passed

No surface regressed past the allowed threshold and the aggregate stayed above the floor.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

📐 Patch coverage gate

Threshold: 80% on lines this PR touches vs origin/main (from .coverage-gate.toml:thresholds.min_patch).

Surface Touched lines Patch coverage Status
control-plane 102 97.00%
sdk-go 0 ➖ no changes
sdk-python 0 ➖ no changes
sdk-typescript 0 ➖ no changes
web-ui 0 ➖ no changes

✅ Patch gate passed

Every surface whose lines were touched by this PR has patch coverage at or above the threshold.

AbirAbbas and others added 22 commits July 10, 2026 16:21
…ed helpers

The patch-coverage gate flagged the runtime.GOOS-gated windows branches
(PowerShell tail construction in logs.go, .exe naming/resolution in
gointerp.go) as untestable on linux CI. Extract each into a pure helper
taking an explicit goos string — tailCommandArgs, withExeSuffixFor,
goBinaryProgramFor — with the exported wrappers passing runtime.GOOS,
so behavior is unchanged while both platform paths are unit-testable
anywhere. Table-driven tests cover the windows tail command (incl.
single-quote escaping), .exe suffixing, and the built-.exe fallback
resolution; refactored regions now profile with zero uncovered blocks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…p on Windows

control-plane/agentfield.yaml was a checked-in symlink to
config/agentfield.yaml. On Windows checkouts (core.symlinks=false, the
default) git materializes it as a plain text file containing the literal
target path, which Viper then finds via its . search path and fails to
parse: `cannot unmarshal !!str config/...` — a fatal error on every
`af server` run from the control-plane directory.

The symlink is redundant on every platform: both cmd/af and
cmd/agentfield-server already search ./config and <execDir>/config, which
resolve to the same file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The af binary only filled in DatabasePath/KVStorePath when storage.mode
was absent entirely. A config file that sets mode: "local" while leaving
the paths empty (which the repo's own sample config/agentfield.yaml does,
and which af picks up automatically when running next to it) skipped the
defaulting block and failed startup with "database path is empty".

Default the paths whenever the effective mode is local, mirroring
cmd/agentfield-server which already had this shape. Found on Windows but
platform-independent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
exec.LookPath alone cannot pick a Python on Windows: stock machines ship
Microsoft Store "app execution alias" stubs named python3.exe (and often
python.exe) that resolve like real binaries but exit 9009 without running
anything. resolveVenvInterpreter took the first PATH hit and only
version-checked that one, so a node declaring requires-python failed with
"no compatible interpreter" even when a perfectly good python was next in
line. Verified live on Windows 11: python3 -> dead Store stub (exit
9009), python -> real 3.11.9, never consulted.

ambientPythonInterpreter now probes each candidate by actually running it
(-c version query) and takes the first that answers. The candidate list
gains "py", the Windows launcher: python.org installers register it even
when "add python to PATH" is left unchecked (the default), where it is
the only working entry. It does not exist on Unix, so probing it there is
a no-op.

The legacy no-requires-python path in InstallPythonDependencies now uses
the same probe instead of blindly running python3 -m venv and falling
back, and fails with an actionable error listing the probed candidates
when nothing on PATH runs.

Test stubs that previously only handled -m venv now answer the -c version
probe, matching how real interpreters behave; the venv-creation-failure
contracts now expect the earlier, more actionable error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Spawned agents log through stdout/stderr redirected to a log file. On
Windows, Python then encodes with the legacy ANSI code page (cp1252),
which cannot represent the SDK's emoji log prefixes - every heartbeat
flooded the log with UnicodeEncodeError tracebacks from the logging
module. Verified live: with PYTHONUTF8=1 the same agent logs cleanly.

Applied in both spawn paths (services.buildProcessConfig, which backs
af run, and the legacy packages runner). An explicit PYTHONUTF8 in the
caller's environment wins. UTF-8 mode is a no-op where UTF-8 is already
the default, so this is safe on every platform.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dows

dev_service.go is build-tagged !windows (Windows gets a stub service),
but two pieces of its test surface were not:

- mockFileSystemAdapter lived in dev_service_test.go (!windows) while
  untagged test files (package_service_test.go, coverage_additional_
  test.go) use it. Moved to a new untagged mocks_fs_test.go.
- Three tests in the untagged coverage_additional_test.go exercised
  Unix-only DefaultDevService methods (loadDevEnvFile, startDevProcess,
  port helpers) that the Windows stub does not define. Moved into the
  !windows-tagged dev_service_test.go.

Before this, go vet / go test of internal/core/services failed to
compile on Windows. Also carries the one-line stub update from the
interpreter-probe change (the fake python in coverage_additional_test.go
now answers the -c version query); no other test logic changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…control plane

checkControlPlane treated any HTTP 200 from {baseUrl}/health as
"healthy", so anything squatting on the popular default port lit the
dashboard green. Found live on Windows: an unrelated dev server answering
{"status":"alive"} on /health showed as a running control plane.

The probe now recognizes an AgentField control plane by its health
payload shape (status: healthy|unhealthy, per routes_core.go). Anything
else reachable on the port reports recognized: false with an explanatory
error, renders as a yellow "Another service is on this port" state, and
is excluded from the nodes cross-check so a foreign /api/v1/nodes
response cannot corrupt agent badges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Data layer growth behind the same single-snapshot IPC:
- fetchExecutions parses GET /api/ui/v2/workflow-runs into in-flight runs
  plus a short tail of finished ones.
- fetchDashboardMetrics parses GET /api/ui/v1/dashboard/summary (agents
  running/total, runs today/yesterday, success rate).
- Both are only consulted on a recognized control plane, so a foreign
  service on 8080 can't inject activity or metrics.

Install flow, with the af CLI as the single contract:
- src/shared/catalog.ts is a curated hard-coded list of installable nodes
  (the pre-marketplace seam; swap for a remote catalog fetch later). Entry
  names must equal the node's manifest name — that is the registry key the
  app uses to detect installed state (SWE-AF installs as "swe-planner").
- src/main/installer.ts spawns `af install <source>`, sanitizes
  ANSI/spinner output into displayable lines, and streams them to the
  renderer over agentfield:install-progress. The renderer only ever sends
  catalog names over IPC; unknown names are refused, raw sources never
  reach a shell. A missing af CLI degrades to an actionable message.

Verified live on Windows: installed SWE-AF from the app end-to-end —
including uv provisioning Python 3.12.13 because the node requires >=3.12
and the ambient interpreter is 3.11 (the interpreter-probing fix working
in production).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native-feeling chrome:
- No default File/Edit/View menu bar anywhere. macOS keeps the minimal
  app menu it needs for Cmd+Q/copy-paste; Windows/Linux drop the bar
  entirely (Menu.setApplicationMenu(null)).
- Seamless titlebar: hiddenInset + sidebar vibrancy + traffic-light inset
  on macOS, hidden titlebar with the native control overlay on Windows.
  The sidebar rail and view header are draggable regions.
- System font stack, light/dark from the OS, hairline borders.

Layout: left sidebar (Dashboard / Agents / Activity / Install + a
control-plane status pill pinned at the bottom), right content view.
The Dashboard leads with stat tiles (agents running, executing now, runs
today vs yesterday, success rate) over a recent-activity list; Agents and
Activity render as clean row panels; Install streams per-row progress.

macOS-specific chrome is behind platform guards and still needs one smoke
run on a real Mac; everything else verified live on Windows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`npm run dist` produces DMG+zip on macOS and a one-click NSIS installer
on Windows into release/ (git-ignored); `npm run dist:dir` for a quick
unpacked smoke. electron-builder is pinned to v25 — v26 requires
require(ESM) support (Node 20.19+/22.12+) that older Node 22 lacks.

Unsigned for now, default Electron icon; signing/notarization and a real
icon come before distribution. Verified on Windows: the packaged
AgentField.exe boots and opens its window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the default Electron icon everywhere. scripts/make-icons.mjs
renders the brand mark (the exact outlined "af" + dot paths from the web
UI logo, so no font dependency) via an offscreen Electron window into:

- build/icon.icns — a real ICNS container (Apple-grid margins, baked
  shadow) that electron-builder ships on macOS
- build/icon.png — the 1024px source electron-builder converts to the
  Windows/Linux app icon (verified: the packaged exe carries the mark)
- resources/icon.png — runtime window/taskbar icon for win/linux
- resources/tray/* — tray glyphs at 1x/1.5x/2x, active/inactive crossed
  with light/dark-taskbar variants; the gold dot doubles as the status
  light

Outputs are committed (`npm run icons` regenerates), resources/** now
ships inside the app package, and desktop/build is un-ignored for
exactly the two icon files the root .gitignore would otherwise drop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tray (Windows/Linux only — macOS has af-tray, installed with AgentField
itself): a status glyph whose brand dot goes gold while the control
plane runs, tooltip + disabled menu row naming the state (running /
unhealthy / port-in-use / stopped), Open AgentField / Open web UI /
Quit. Closing the window now hides to the tray, Docker-Desktop style;
presentation logic is pure in tray-model.ts and unit-tested. If tray
creation fails (some Linux desktops) the app logs why and keeps classic
quit-on-close.

Deep links: the app registers the agentfield:// scheme (declared for
macOS via electron-builder `protocols`, HKCU-registered at runtime on
Windows) and holds the single-instance lock — a relaunch or an
agentfield://dashboard|agents|activity|install URL focuses the running
app and switches it to that view. Parsing is pure in shared/deeplink.ts
(unit-tested); the view union now lives there as the one canonical list.

Verified live on Windows (packaged build): deep link cold-open, deep
link into a tray-resident app, view switching, single-instance focus,
close-to-tray, and the port-in-use state against a foreign service
squatting on 8080.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The macOS menu-bar tray now prefers the AgentField desktop app when it
is installed: every "open" action first tries the agentfield:// deep
link for the equivalent view (dashboard/agents/activity) and falls back
to the web UI in the browser. Detection is the deep link itself —
`open agentfield://…` exits non-zero fast when nothing registered the
scheme, so there is no separate probe to drift. Page→view mapping and
the browser fallback are pure helpers in shared.go with contract tests;
the darwin file only gains the two-line try/fallback.

assets/appicon.icns was a renamed 512px PNG, not an ICNS container —
Finder/dock could show a generic icon. It is now a real icns (PNG
members at 32…1024 on Apple's grid), generated by the same
desktop/scripts/make-icons.mjs that produces the desktop app's icons,
so both apps wear the same mark.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replacing the icon and context menu on every 5s poll churns native tray
APIs for nothing and can dismiss a menu the user has open on Windows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Swept the org: a repo is installable iff it has agentfield-package.yaml
at its root (the manifest `af install` requires) — that held for four of
fifteen repos, and the catalog now carries all of them: swe-planner
(SWE-AF), pr-af, sec-af, and cloudsecurity-af, each keyed by its
manifest `name:` so installed-state detection keeps working. The sweep
rule is documented at the top of catalog.ts for the next addition.

Required secrets (e.g. OPENROUTER_API_KEY) are resolved at `af run`
time, not install time, so installs from the app stream cleanly and
setup prompts happen where a terminal exists.

Verified in the packaged app: all four render, swe-planner shows
Installed ✓.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Agents view: Start / Stop / Restart per row, shelling out to the af CLI
(`af run` / `af stop`; restart is stop-then-run — the CLI has no restart
verb). Names are validated against installed.yaml before anything is
spawned; the renderer only ever sends names.

Settings view: open at login (OS login item, packaged builds only —
launches hidden with --hidden, tray-only), start the control plane
automatically, and per-agent auto-start switches. Persisted to
settings.json in userData, normalized on load so hand-edits and old
shapes can't break the app.

Autostart on every launch (src/main/autostart.ts, planning pure and
unit-tested): spawn `af server` detached (logs to
~/.agentfield/logs/control-plane.log, same file macOS launchd uses) only
when nothing answers on the port — never over a live control plane or a
foreign service — then start the selected agents. Agents whose registry
entry went stale (running with no control-plane presence, e.g. after a
reboot; Windows never reconciles that live) are restarted, not skipped.
The point: agents are already answering when Claude/Codex/anything
queries them — nobody has to start a server first.

Also fixes a deep-link race this surfaced: a link that cold-starts the
app pushed the view at did-finish-load, before React subscribed, and got
dropped. The renderer now announces readiness and collects the pending
view (agentfield:renderer-ready), so agentfield://settings into a hidden
app lands on Settings.

Verified live on Windows (packaged build): start/restart/stop from the
UI (badge, port, registry all agree), settings round-trip, login-item
registration with --hidden, hidden tray-only launch, autostart bringing
a stopped agent up on relaunch, and the port-in-use guard skipping the
control-plane start while a foreign service owns 8080.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Non-technical users install ONLY the desktop app and get all of
AgentField: the package now carries the af CLI (extraResources from
vendor/, staged by `npm run bundle-cli` or the release pipeline).

Every launch resolves which af to drive (src/main/cli.ts):
managed (~/.agentfield/bin — the same location the curl installer uses,
so the two installers converge instead of double-installing) → PATH →
bundled. A copy older than MIN_AF_VERSION is skipped — the app runs on
its bundled CLI meanwhile, and Settings grows an "AgentField CLI" card
showing version + source with an Update button that installs the
bundled copy into the managed location (never over a newer one; dev
builds are trusted). With no CLI anywhere, first launch auto-provisions
~/.agentfield/bin (agentfield + af alias, curl-installer naming), copes
with a running binary via rename-aside, and registers the Windows user
PATH so terminals get `af` too.

Launches also run `af skill install --non-interactive` (Settings toggle,
default on) so detected coding agents — Claude Code, Codex, Gemini … —
always know how to use AgentField; skillkit's state file makes this
idempotent and shared with the curl installer.

Verified live on Windows from a simulated fresh machine (no managed
copy, no PATH af): first launch provisioned both binaries, registered
the user PATH, installed the skill into ~/.claude/skills, resolved to
the managed copy, and drove agent start/stop through it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two blind spots closed:

- The desktop app had no CI at all — its tests only ever ran on a dev
  machine. New desktop.yml runs typecheck, vitest, and an unsigned
  electron-builder package on macos-14 and windows-latest (a stub in
  vendor/ validates the bundled-CLI extraResources wiring), so the
  platform-guarded chrome and packaging config prove out on both ship
  targets per PR.

- cmd/af-tray's darwin implementation is CGO code that Linux CI never
  compiles (it builds the !darwin stub, and the CGO_ENABLED=0 matrix
  can't touch it) — a type error in tray_darwin.go would only surface
  at release time. control-plane.yml grows a macos-14 job that builds
  and vets the tray, wired into required-checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@AbirAbbas AbirAbbas changed the title feat(desktop): Electron desktop app skeleton + Windows enablement groundwork feat: AgentField Desktop (Mac-first) + Windows enablement, bundled CLI, tray, deep links Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant