Desktop GitHub Copilot chat client — Electron + React 19 + TypeScript + Vite, wrapping the @github/copilot-sdk.
- GitHub device-flow sign-in with cached credentials.
- Multi-session chat with streaming responses, abort, optimistic rendering, persisted history under
~/.jarvis/. - Model picker per session, sourced from the SDK.
- Settings dialog — theme (dark/light/system), default model, shell permissions (auto-approve read-only, allow/block patterns, default tier), hotkeys, close-to-tray, launch-on-startup.
- Permission engine for shell/MCP/write/read requests — auto-allow / always-allow / per-session / once / deny, with a JSONL audit log at
~/.jarvis/audit.log. - Image attachments — paste, drag-drop, or pick PNG/JPEG/WEBP/GIF; vision-capable model warning when the current model can't handle them.
- System tray — Show / Hide / New chat / Mini mode / Settings / Sign out / Quit.
- Global hotkeys — show/hide the main window and open mini mode from anywhere.
- Mini-mode — frameless, always-on-top quick-prompt window that posts to the most-recent session.
- Frontend: React 19 + TypeScript 5.7 + Vite 6 + Tailwind v4 + shadcn/ui (Radix primitives) + TanStack Query 5 + React Router 7 (HashRouter).
- Desktop: Electron 33 (sandboxed renderer,
contextIsolation: true, single global per IPC namespace viacontextBridge). - Backend:
@github/copilot-sdkdriven from the main process; oneSessionManagerkeeps SDK sessions, the local index, and IPC events in sync. - Testing: Vitest + happy-dom.
- Packaging: electron-builder (DMG / NSIS / AppImage).
- Package manager: pnpm 10.
-
Node.js 22.18 or newer — check with
node -v. Theengines.nodefield requires>=22.18.0. -
pnpm 10.32.1 — enable through corepack:
corepack enable corepack prepare pnpm@10.32.1 --activate
-
A GitHub account with Copilot access — required for the device-flow sign-in once the app boots.
From the Jarvis/ folder:
pnpm installThis pulls public npm packages only — no Azure DevOps or other private feeds.
pnpm run electron:devWhat this does:
- Runs
vite buildonce to producedist-electron/main.jsanddist-electron/preload.cjs. - Spawns
electron .which loads the main process and points the renderer at the dev server. - The renderer hot-reloads on source changes in
src/. Electron-main changes require restarting the command.
On first launch you'll see the Sign in screen — click Sign in with GitHub, copy the displayed device code, paste it into the browser tab Jarvis opens, and the app will pick up the session automatically.
If you're only iterating on UI and don't need the Electron shell, pnpm run dev starts the Vite dev server alone at http://localhost:5173. Most features (auth, sessions, chat) will fail because they require the preload-exposed IPC bridge.
pnpm typecheck # tsc --noEmit
pnpm test # vitest run (single pass)
pnpm run test:watch # vitest watch mode
pnpm run build:vite # renderer + electron bundle only — fastest "does it compile"
pnpm run build # full: typecheck + vite build + electron-builder installerAll Jarvis state lives under ~/.jarvis/ (%USERPROFILE%\.jarvis\ on Windows), mode 0o700:
| Path | Contents |
|---|---|
settings.json |
Theme, default model, permission rules, hotkeys, window prefs |
sessions/sessions-index.json |
Lightweight session metadata (title, model, timestamps) |
attachments/<sessionId>/ |
Pasted/dropped images, mode 0o600 |
audit.log |
JSONL append-only record of every permission decision |
The Copilot SDK keeps its own credentials under ~/.copilot/. Sign out clears both.
Electron failed to install correctly, please delete node_modules/electron and try installing again— pnpm 10 skips lifecycle scripts by default; thepnpm.onlyBuiltDependenciesarray inpackage.jsonwhitelistselectronandesbuild. If you still hit this (e.g. after wipingnode_moduleswith a different pnpm version), runpnpm rebuild electron esbuildto re-trigger the postinstall download.- "Unsupported engine" warning from pnpm — upgrade Node to 22.18+; older 22.x will still run but is not supported.
- Sign-in window doesn't open the browser — check
appAPI.openExternalpermissions; on Windows make sure your default browser is set. - Global hotkey doesn't fire — the OS may have it bound. Change it in Settings → Hotkeys, then close and re-open Settings to re-register.
- Tray icon missing on Windows — the bundled icon is a placeholder data-URL PNG; drop a real
tray.pngunderresources/and rebuild.
Jarvis/
├─ common/ Shared types between main and renderer
│ ├─ ipc-contract.ts Single source of truth for IPC channels
│ ├─ settings-schema.ts
│ └─ logger.ts
├─ electron/ Main process (Node)
│ ├─ main.ts Composition root
│ ├─ preload.cjs Renderer bridge
│ ├─ mini-mode-preload.cjs
│ ├─ mini-mode-window.ts
│ ├─ tray.ts
│ ├─ global-shortcuts.ts
│ ├─ auth-manager.ts
│ ├─ sessions/ SessionManager + on-disk index
│ ├─ permissions/ Classifier + policy + audit + approval broker
│ ├─ attachments/ Image save/remove helpers
│ ├─ settings/ Atomic settings store
│ └─ ipc/ One file per IPC namespace
├─ src/ React renderer (Vite entry: src/main.tsx)
│ ├─ App.tsx
│ ├─ components/ui/ shadcn/ui primitives
│ ├─ features/
│ │ ├─ auth/
│ │ ├─ sessions/
│ │ ├─ chat/
│ │ ├─ models/
│ │ ├─ settings/
│ │ ├─ permissions/
│ │ ├─ attachments/
│ │ ├─ home/
│ │ └─ theme/
│ └─ mini-mode/ Separate entry for the quick-prompt window
├─ index.html
├─ mini-mode.html
├─ vite.config.ts Multi-entry: index + mini-mode + vite-plugin-electron
├─ vitest.config.ts
├─ tsconfig.json / tsconfig.node.json
├─ electron-builder.json5
├─ package.json
└─ jarvis.code-workspace
# 1. Install
pnpm install
# 2. Run the desktop app
pnpm run electron:dev
# 3. Iterate
pnpm typecheck
pnpm test
pnpm run build:vite