Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Lint + tests before allowing commit — same gate as `task check`.
# Set SKIP_TESTS=1 to skip the heavier test suite (lint still runs).

set -e

if command -v task >/dev/null 2>&1; then
task check
else
npm run lint
if [ -z "$SKIP_TESTS" ]; then
npm test
fi
fi
37 changes: 30 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,38 @@ Grab the latest release for your platform:
- **Linux**: `build-essential`, `python3` (`sudo apt install build-essential python3`)
- **Windows**: Visual Studio Build Tools or `npm install -g windows-build-tools`

## Tooling

[task](https://taskfile.dev) is the preferred entrypoint for all dev operations. Install it once (`brew install go-task` / `snap install task --classic` / see taskfile.dev for other platforms), then:

```bash
task install # npm install
task dev # launch Electron (--no-sandbox, required on Linux)
task test # node --test (24 tests)
task lint # eslint .
task check # test + lint — pre-commit / pre-push gate
task ci # same as check but sequential, verbose
task build # npm run build:linux
task clean # wipe dist/, codemirror bundle, local DB (asks for confirmation)
task db:reset # wipe ~/.switchboard/switchboard.db only
```

Run `task` (no args) to list all tasks with descriptions.

The npm scripts are still present and work as before; `task` just wraps them as a consistent entrypoint.

## Development Setup

```bash
# Install dependencies (runs postinstall automatically)
npm install
task install # install dependencies (runs postinstall automatically)
task dev # launch Electron
```

# Start the app
npm start
Or with npm directly:

```bash
npm install
npm start # bundles CodeMirror then launches Electron
```

`npm start` bundles CodeMirror and launches Electron. For faster iteration after the first run:
Expand All @@ -99,10 +123,9 @@ npm run electron
All build commands bundle CodeMirror first, then invoke electron-builder.

```bash
# Current platform
npm run build
task build # AppImage + deb (Linux)

# Platform-specific
# npm equivalents:
npm run build:mac # DMG + zip (arm64 + x64)
npm run build:win # NSIS installer (x64 + arm64)
npm run build:linux # AppImage + deb (x64 + arm64)
Expand Down
100 changes: 100 additions & 0 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# https://taskfile.dev

version: "3"

set: [pipefail]

vars:
SWITCHBOARD_DB: "{{.HOME}}/.switchboard/switchboard.db"
SWITCHBOARD_DEV_DATA_DIR: "{{.HOME}}/.switchboard-dev"

tasks:
default:
desc: List all available tasks
silent: true
cmds:
- task --list

install:
desc: Install npm dependencies
cmds:
- npm install

dev:
desc: Launch Switchboard in development mode (Electron, no-sandbox, isolated DB)
silent: true
env:
# Force a separate SQLite DB so a running AppImage isn't disturbed by the
# dev electron sharing session_cache. Override per-run with
# `SWITCHBOARD_DATA_DIR=/some/other/dir task dev`.
SWITCHBOARD_DATA_DIR: "{{.SWITCHBOARD_DEV_DATA_DIR}}"
cmds:
- npx electron . --no-sandbox

"db:reset:dev":
desc: Wipe just the dev SQLite database
cmds:
- rm -f "{{.SWITCHBOARD_DEV_DATA_DIR}}/switchboard.db" "{{.SWITCHBOARD_DEV_DATA_DIR}}/switchboard.db-wal" "{{.SWITCHBOARD_DEV_DATA_DIR}}/switchboard.db-shm"
- echo "Removed {{.SWITCHBOARD_DEV_DATA_DIR}}/switchboard.db"

build:
desc: Build Linux distribution packages (AppImage + deb)
silent: true
cmds:
- npm run build:linux

test:
desc: Run the node:test suite (24 tests)
cmds:
- npm test

lint:
desc: Run ESLint across the codebase
cmds:
- |
if [ ! -f eslint.config.js ] && [ ! -f .eslintrc.js ] && [ ! -f .eslintrc.json ] && [ ! -f .eslintrc.yaml ] && [ ! -f .eslintrc.yml ]; then
echo "ESLint not yet configured; run \`task install:lint\` first"
exit 1
fi
npx eslint .

"install:lint":
desc: Install ESLint + jsdom dev deps (idempotent)
cmds:
- |
MISSING=""
node -e "require('eslint')" 2>/dev/null || MISSING="$MISSING eslint"
node -e "require('jsdom')" 2>/dev/null || MISSING="$MISSING jsdom"
if [ -n "$MISSING" ]; then
npm install --save-dev $MISSING
else
echo "ESLint and jsdom already installed — nothing to do."
fi

check:
desc: Run tests + lint (pre-commit / pre-push gate)
deps: [test, lint]

ci:
desc: CI gate — runs test then lint, verbose, exits on first failure
cmds:
- npm test
- npx eslint .

clean:
desc: Remove dist/, codemirror bundle, and local DB (asks for confirmation)
interactive: true
cmds:
- |
read -p "This will delete dist/, public/codemirror-bundle.js, and {{.SWITCHBOARD_DB}}. Type YES to confirm: " ans
[ "$ans" = "YES" ]
- rm -rf dist/
- rm -f public/codemirror-bundle.js
- rm -f "{{.SWITCHBOARD_DB}}"
- echo "Clean complete."

"db:reset":
desc: Wipe the local Switchboard SQLite database (no confirmation)
cmds:
- rm -f "{{.SWITCHBOARD_DB}}"
- echo "Removed {{.SWITCHBOARD_DB}}"
14 changes: 12 additions & 2 deletions db.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ const Database = require('better-sqlite3');
const path = require('path');
const os = require('os');

const DATA_DIR = path.join(os.homedir(), '.switchboard');
// SWITCHBOARD_DATA_DIR lets dev/agent runs use a separate DB from the
// installed AppImage so they don't race on session_cache. Default stays
// ~/.switchboard so existing installs keep working. Resolve env var at
// require-time (any later mutation would be ignored).
const DATA_DIR = process.env.SWITCHBOARD_DATA_DIR
? path.resolve(process.env.SWITCHBOARD_DATA_DIR.replace(/^~(?=$|\/)/, os.homedir()))
: path.join(os.homedir(), '.switchboard');
const fs = require('fs');
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });

Expand All @@ -14,7 +20,11 @@ const OLD_LOCATIONS = [
path.join(os.homedir(), '.claude', 'browser', 'session-browser.db'),
path.join(os.homedir(), '.claude', 'session-browser.db'),
];
if (!fs.existsSync(DB_PATH)) {
// Skip the legacy ~/.claude/browser/ migration when running with a custom
// DATA_DIR (typical dev/agent setup) — otherwise a fresh dev DB would steal
// the AppImage's old data on first launch.
const IS_DEFAULT_DATA_DIR = !process.env.SWITCHBOARD_DATA_DIR;
if (IS_DEFAULT_DATA_DIR && !fs.existsSync(DB_PATH)) {
for (const oldPath of OLD_LOCATIONS) {
if (fs.existsSync(oldPath)) {
fs.renameSync(oldPath, DB_PATH);
Expand Down
Loading
Loading