Skip to main content

Development

How to build and run yv locally.


Tech stack

LayerTechnology
Desktop shellWails v2
BackendGo 1.25
FrontendSolidJS + TypeScript
PTYcreack/pty
Peer transportlibp2p + mDNS
ChartsChart.js

Prerequisites

ToolVersionInstall
Go1.25+go.dev/dl
Bunlatestbun.sh
Wails CLIv2go install github.com/wailsapp/wails/v2/cmd/wails@latest

Node.js 18+ works in place of Bun if you prefer — Bun is faster for bun install and bun run test.

Linux: additional system packages

make deps-linux # installs GTK, WebKit2 and the other packages Wails needs
make doctor-linux # checks that everything is present

Run in dev mode

make run # hot reload — frontend and backend update on save
make run-linux # same, with the Linux build tags

The first run downloads the Wails CLI if it is not already on your PATH. Subsequent runs are fast.


Tests

make test # Go tests (./...) + frontend tests (vitest)
make test-go # Go tests only
make test-frontend # frontend tests only (vitest, node environment)

The test suite covers all four internal packages (config, runner, monitor, env) and the frontend utilities (search, heatmap, drone, audio, boar, river, sea).


Formatting

make fmt # gofmt -w on all .go files (skips wailsjs/ generated files)

Build

make build # macOS ARM64 app bundle in build/bin/

Platform packaging

make dmg # macOS: yv.dmg in the project root (requires create-dmg or hdiutil)
make deb # Linux: a .deb in build/bin/
make appimage # Linux: the self-updating AppImage (requires appimagetool)
make install-linux # build the .deb and install it locally

A build that goes through the Makefile embeds the current version. A build that skips it reports dev and never checks for updates.


Project structure

yv/
├── main.go — Wails bootstrap, macOS title bar config, quit dialog
├── app.go — App facade: thin Wails-bound wrappers for all methods
├── models.go — type aliases re-exporting internal/models (keeps Wails TypeScript namespace)
├── go.mod / go.sum
├── wails.json — build config, productVersion
├── Makefile
├── internal/
│ ├── models/ — all struct types shared across packages
│ ├── runner/ — PTY execution (ExecuteCommand, StopCommand, SendInput)
│ ├── config/ — persistence (LoadProjects, SaveProjects, Export/Import)
│ ├── monitor/ — resource stats polling (CPU, memory)
│ ├── metrics/ — append-only JSONL samples, retention
│ ├── env/ — environment variable store (0600, never in exports)
│ ├── audio/ — user's own sound clips, read for the discovery view
│ ├── share/ — peer discovery (mDNS + libp2p), connect-by-code, transfer
│ ├── updater/ — update check, download, verify, apply per platform
│ ├── updatesign/ — signature format for update verification
│ ├── settings/ — settings store, validation, normalization
│ └── atomicfile/ — write-beside-fsync-rename; every config write goes through it
└── frontend/
├── index.html
└── src/
├── index.tsx — SolidJS entry point
├── App.tsx — root: project loading, keyboard shortcuts, event wiring
├── store.ts — SolidJS signals (projects, selectedId, cmdState, etc.)
├── types.ts — TypeScript interfaces mirroring Go models
├── wails.ts — typed Go bindings + Wails runtime re-export
├── styles.css — all styles (CSS variables, grid, components)
├── lib/
│ ├── commands.ts — runCommand, runShortcut, step tracking
│ ├── search.ts — Spotlight: token matching, scoring, global search
│ ├── audio.ts — clip assignment, playback, loop management
│ ├── drone.ts — drone geometry, patrol route, burst shards
│ └── landscape/ — procedural island: world, geometry, river, sea, palette
└── components/
├── Sidebar.tsx
├── GroupsPanel.tsx
├── MainPanel.tsx
├── CommandRow.tsx
├── Terminal.tsx
├── Spotlight.tsx
├── DiscoveryPanel.tsx
├── StatusBar.tsx
└── modals/
├── EditCommandModal.tsx
├── ShortcutModal.tsx
├── ProjectSettingsModal.tsx
├── EnvironmentsModal.tsx
├── ShareModal.tsx
├── IncomingShareModal.tsx
├── PeerConnectModal.tsx
├── IncomingConnectModal.tsx
├── NoDevicesModal.tsx
├── SettingsModal.tsx
└── UpdateModal.tsx

Import graph (no cycles): modelsrunner, config, monitor, env, settings; monitor also imports runner.


Versioning

Versions are managed with changesets.

Each PR should include a changeset:

bunx changeset # interactive: pick patch / minor / major, describe the change

To cut a release locally (for testing the sync script):

bun run version # runs `changeset version` + syncs the version into wails.json

The version lives in two files — package.json and wails.json — and version_test.go fails the build if they disagree.

Pushing a v* tag triggers CI: all three platforms build, artifacts are signed and uploaded, and a GitHub Release is created. See RELEASING.md for the full pipeline and the signing key notes.


Config files (runtime)

PlatformDirectory
macOS~/Library/Application Support/yv/
Linux$XDG_CONFIG_HOME/yv/ (usually ~/.config/yv/)
Windows%AppData%\yv\
FileHolds
projects.jsonprojects, groups, commands, shortcuts
settings.jsonapp settings
environments.jsonenvironment variables (mode 0600, never exported)
metrics/resource samples (only if metrics are enabled in Settings)

Linting / type checking

cd frontend && bunx tsc --noEmit # TypeScript type check (should be clean)

The Go side has no linter config beyond gofmt; the Makefile's fmt target covers it.


Opening a pull request

  1. Fork the repo and create a branch.
  2. Run make test — all tests should pass.
  3. Add a changeset: bunx changeset.
  4. Open a PR against main.

A CI check (changeset status) will fail if the PR has no changeset file. That is intentional — it keeps the changelog and version in sync with every merge.