Skip to content
Draft

wip #1303

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
eff4ac4
init
Davidknp Mar 4, 2026
f893894
cp
Davidknp Mar 4, 2026
5696dfb
add view mgmt
Davidknp Mar 4, 2026
1f9181a
migrate app to new view mgmt
Davidknp Mar 4, 2026
9c63dcb
remove auto collapse from settings
Davidknp Mar 4, 2026
bc0221e
fix titlebar
Davidknp Mar 4, 2026
6d9d8dd
layout migration
Davidknp Mar 4, 2026
58074d2
migrate diff viewer
Davidknp Mar 4, 2026
580bdb3
checkpoint
Davidknp Mar 4, 2026
25c067c
migrated event system
Davidknp Mar 4, 2026
e85d038
create agent provider
Davidknp Mar 4, 2026
4115cd2
conversation
Davidknp Mar 5, 2026
45977ef
checkpoint
Davidknp Mar 5, 2026
72311bd
upgrade electron and node and add proper bundling through electron-vite
Davidknp Mar 5, 2026
bf0ce90
migrated to better-sqlite3 and drizzle migrator
Davidknp Mar 5, 2026
1c0ba58
ipc migration (1)
Davidknp Mar 5, 2026
3eed5f1
ipc migration (2)
Davidknp Mar 5, 2026
7906317
refactor Integrationscard to use gh context
Davidknp Mar 5, 2026
980c2e9
create integrations provider
Davidknp Mar 5, 2026
ec77e22
added code editor
Davidknp Mar 5, 2026
5d3fb3c
fix layout performance
Davidknp Mar 5, 2026
ec79f4f
fix unused vars
Davidknp Mar 5, 2026
66725f3
checkpoint
Davidknp Mar 6, 2026
31deaec
checkpoint
Davidknp Mar 7, 2026
f834f2d
checkpoint
Davidknp Mar 7, 2026
a13aa1c
checkpoint
Davidknp Mar 7, 2026
a4d4e96
checkpoint
Davidknp Mar 8, 2026
af159e9
checkpoint
Davidknp Mar 8, 2026
36049a7
checkpoint
Davidknp Mar 8, 2026
d3126e8
checkpoint
Davidknp Mar 8, 2026
4923883
checkpoint
Davidknp Mar 8, 2026
01649d1
checkpoint
Davidknp Mar 8, 2026
36aab35
fix theme flashing
Davidknp Mar 8, 2026
511f643
restore file
Davidknp Mar 8, 2026
a68a170
improve theming
Davidknp Mar 8, 2026
6092b03
migrate to baseui
Davidknp Mar 8, 2026
4402713
fix vitest config
Davidknp Mar 8, 2026
30b9f19
add cache to skills
Davidknp Mar 8, 2026
3e8217c
add ssh events and split up sidebar
Davidknp Mar 10, 2026
43635e4
add combobox and add project modal wip
Davidknp Mar 10, 2026
f6a7f4d
checkpoint
Davidknp Mar 11, 2026
08e5caf
checkpoint
Davidknp Mar 11, 2026
f4c83b8
checkpoint
Davidknp Mar 11, 2026
e9b8a33
checkpoint
Davidknp Mar 12, 2026
b0b2e94
checkpoint
Davidknp Mar 12, 2026
11fcf9f
checkpoint
Davidknp Mar 13, 2026
f8d2598
checkpoint
Davidknp Mar 13, 2026
12e8f91
checkpoint
Davidknp Mar 13, 2026
e875a00
checkpoint
Davidknp Mar 13, 2026
61c1dc0
checkpoint
Davidknp Mar 13, 2026
175daf8
checkpoint
Davidknp Mar 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 2 additions & 8 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,14 @@
}
],
"@typescript-eslint/no-explicit-any": "warn",

"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off",
"prefer-const": "warn",
"react-hooks/set-state-in-effect": "warn"
},
"overrides": [
{
"files": ["src/main/**/*.ts", "src/main/**/*.tsx"],
"rules": {
"@typescript-eslint/no-var-requires": "off"
}
}
],
"overrides": [],
"env": {
"browser": true,
"es2020": true,
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Dependencies
node_modules/
dist/
out/
release/

# Logs
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22.20.0
24.14.0
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
**/.next
**/build
**/.turbo
**/out

# ignore config files that shouldn't be formatted
**/.env*
Expand Down
12 changes: 11 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,15 @@
"tabWidth": 2,
"bracketSpacing": true,
"bracketSameLine": false,
"plugins": ["prettier-plugin-tailwindcss"]
"plugins": ["prettier-plugin-tailwindcss", "@ianvs/prettier-plugin-sort-imports"],
"tailwindStylesheet": "./src/renderer/index.css",
"importOrder": [
"^@/(.*)$",
"^@root/(.*)$",
"^@shared/(.*)$",
"^@main/(.*)$",
"^@renderer/(.*)$",
"^[./]"
],
"importOrderTypeScriptVersion": "5.0.0"
}
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
default_branch: main
package_manager: pnpm
node_version: "22.20.0"
node_version: "24.14.0"
start_command: "pnpm run d"
dev_command: "pnpm run dev"
build_command: "pnpm run build"
Expand Down Expand Up @@ -29,7 +29,7 @@ Cross-platform Electron app that orchestrates multiple CLI coding agents (Claude

### Tech Stack

- **Runtime**: Electron 30.5.1, Node.js >=20.0.0 <23.0.0 (recommended: 22.20.0 via `.nvmrc`)
- **Runtime**: Electron 40.7.0, Node.js >=24.0.0 (recommended: 24.14.0 via `.nvmrc`)
- **Frontend**: React 18, TypeScript 5.3, Vite 5, Tailwind CSS 3
- **Backend**: Node.js, TypeScript, Drizzle ORM 0.32, SQLite3 5.1
- **Editor**: Monaco Editor 0.55, **Terminal**: @xterm/xterm 6.0 + node-pty 1.0
Expand All @@ -39,7 +39,7 @@ Cross-platform Electron app that orchestrates multiple CLI coding agents (Claude

## Quickstart

1. `nvm use` (installs Node 22.20.0 if missing) or install Node 22.x manually.
1. `nvm use` (installs Node 24.14.0 if missing) or install Node 24.x manually.
2. `pnpm run d` to install dependencies and launch Electron + Vite.
3. If `pnpm run d` fails mid-stream, rerun `pnpm install`, then `pnpm run dev` (main + renderer).

Expand Down Expand Up @@ -337,7 +337,7 @@ All optional:
- `tsconfig.json` — Renderer/shared TypeScript config (`module: ESNext`, `noEmit: true` — Vite does compilation)
- `tsconfig.main.json` — Main process TypeScript config (`module: CommonJS` — required by Electron main)
- `tailwind.config.js` — Tailwind configuration
- `.nvmrc` — Node version (22.20.0)
- `.nvmrc` — Node version (24.14.0)
- Electron Builder config is in `package.json` under `"build"` key

## Pre-PR Checklist
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Thanks for your interest in contributing! We favor small, focused PRs and clear

Prerequisites

- **Node.js 20.0.0+ (recommended: 22.20.0)** and Git
- **Node.js 24.0.0+ (recommended: 24.14.0)** and Git
- Optional (recommended for end‑to‑end testing):
- GitHub CLI (`brew install gh`; then `gh auth login`)
- At least one supported coding agent CLI (see docs for list)
Expand Down
14 changes: 7 additions & 7 deletions components.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"style": "base-vega",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"config": "",
"css": "src/renderer/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
"components": "@renderer/components",
"utils": "@renderer/lib/utils",
"ui": "@renderer/components/ui",
"lib": "@renderer/lib",
"hooks": "@renderer/hooks"
},
"registries": {
"@ai-elements": "https://registry.ai-sdk.dev/{name}.json"
Expand Down
50 changes: 50 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# ──────────────────────────────────────────────────────────────────────────────
# Emdash SSH Dev Container — docker-compose
#
# Usage:
# cp .env.example .env # fill in your API keys
# docker compose up --build -d
#
# Connect from emdash:
# host: localhost port: 2222 user: devuser auth: password pass: devpass
# ──────────────────────────────────────────────────────────────────────────────
services:
ssh-dev:
build:
context: ./docker-ssh
dockerfile: dockerfile
container_name: emdash-ssh-dev
ports:
- "2222:22"
environment:
# API keys forwarded into SSH sessions via ~/.ssh/environment.
# Define these in a .env file next to this compose file (gitignored).
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- GH_TOKEN=${GH_TOKEN:-}
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
- GEMINI_API_KEY=${GEMINI_API_KEY:-}
- GOOGLE_API_KEY=${GOOGLE_API_KEY:-}
- AMP_API_KEY=${AMP_API_KEY:-}
- DASHSCOPE_API_KEY=${DASHSCOPE_API_KEY:-}
- KIMI_API_KEY=${KIMI_API_KEY:-}
- MISTRAL_API_KEY=${MISTRAL_API_KEY:-}
- CODEBUFF_API_KEY=${CODEBUFF_API_KEY:-}
- FACTORY_API_KEY=${FACTORY_API_KEY:-}
- CURSOR_API_KEY=${CURSOR_API_KEY:-}
volumes:
# ── Project mounts ───────────────────────────────────────────────────
# Option A: mount a real local git repo from your Mac (recommended):
# - /Users/yourname/code/myrepo:/home/devuser/projects/myrepo
#
# Option B: use a named volume for a self-contained sandbox:
- projects:/home/devuser/projects
restart: unless-stopped
# Needed for tmux PTY allocation over SSH
tty: true
stdin_open: true
shm_size: '256mb'

volumes:
projects:
driver: local
128 changes: 128 additions & 0 deletions docker-ssh/dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# ──────────────────────────────────────────────────────────────────────────────
# Emdash SSH Dev Container
#
# A self-contained Linux environment you can SSH into from emdash's SSH remote
# development feature. Includes git, gh, tmux, Node.js, and Claude Code out
# of the box. Other agents (codex, gemini, etc.) can be installed on top.
#
# Usage:
# docker compose up --build -d
# Then add an SSH connection in emdash:
# host: localhost port: 2222 user: devuser auth: password pass: devpass
# ──────────────────────────────────────────────────────────────────────────────
FROM ubuntu:24.04

# Avoid interactive prompts during package install
ENV DEBIAN_FRONTEND=noninteractive

# ── System packages ─────────────────────────────────────────────────────────
RUN apt-get update && apt-get install -y --no-install-recommends \
# SSH server
openssh-server \
# Core tools
git \
curl \
wget \
tmux \
sudo \
ca-certificates \
gnupg \
# Shell utilities
bash \
zsh \
vim \
nano \
less \
# Build tools (needed by some npm native modules)
build-essential \
python3 \
# Process utilities
procps \
&& rm -rf /var/lib/apt/lists/*

# ── GitHub CLI (gh) ─────────────────────────────────────────────────────────
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| gpg --dearmor -o /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list \
&& apt-get update \
&& apt-get install -y gh \
&& rm -rf /var/lib/apt/lists/*

# ── Node.js 22 LTS (via NodeSource) ─────────────────────────────────────────
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/*

# ── Global npm config: faster installs, no fund/audit noise ─────────────────
RUN npm config set fund false && npm config set audit false

# ── Claude Code ─────────────────────────────────────────────────────────────
# Primary agent. Installed globally so it's on PATH for all users.
RUN npm install -g @anthropic-ai/claude-code

# ── Optional: additional agents (uncomment as needed) ───────────────────────
# RUN npm install -g @openai/codex # OpenAI Codex CLI
# RUN npm install -g @google/gemini-cli # Gemini CLI
# RUN npm install -g opencode # OpenCode

# ── Dev user ─────────────────────────────────────────────────────────────────
# 'devuser' is the account emdash will SSH into.
# Password is 'devpass' — change or replace with key auth for security.
RUN useradd -m -s /bin/bash devuser \
&& echo 'devuser:devpass' | chpasswd \
&& usermod -aG sudo devuser \
&& echo 'devuser ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/devuser

# ── SSH server config ────────────────────────────────────────────────────────
# Generate host keys and configure sshd for interactive use.
RUN mkdir -p /var/run/sshd \
&& ssh-keygen -A

# Allow password auth and keep SSH sessions alive.
# PermitUserEnvironment lets ~/.ssh/environment set API keys on login.
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config \
&& sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config \
&& echo 'PermitUserEnvironment yes' >> /etc/ssh/sshd_config \
&& echo 'ClientAliveInterval 60' >> /etc/ssh/sshd_config \
&& echo 'ClientAliveCountMax 10' >> /etc/ssh/sshd_config \
&& echo 'X11Forwarding no' >> /etc/ssh/sshd_config

# ── Git global defaults for devuser ─────────────────────────────────────────
USER devuser
WORKDIR /home/devuser

RUN git config --global user.email "devuser@emdash-dev" \
&& git config --global user.name "Emdash Dev" \
&& git config --global init.defaultBranch main \
# Required: allow git operations in worktrees under /home/devuser
&& git config --global safe.directory '*'

# ── Projects directory ───────────────────────────────────────────────────────
# Mount your git repos here, e.g.:
# volumes:
# - /path/to/my/project:/home/devuser/projects/myproject
RUN mkdir -p /home/devuser/projects

# ── tmux default config: no status bar clutter ──────────────────────────────
RUN echo 'set -g default-terminal "xterm-256color"' > /home/devuser/.tmux.conf \
&& echo 'set -g history-limit 50000' >> /home/devuser/.tmux.conf \
&& echo 'set-option -g allow-rename on' >> /home/devuser/.tmux.conf

# ── Optional: SSH authorized_keys ───────────────────────────────────────────
# Uncomment and mount your public key for key-based auth:
# COPY --chown=devuser:devuser authorized_keys /home/devuser/.ssh/authorized_keys
# RUN chmod 700 /home/devuser/.ssh && chmod 600 /home/devuser/.ssh/authorized_keys
RUN mkdir -p /home/devuser/.ssh && chmod 700 /home/devuser/.ssh

# ── Startup ──────────────────────────────────────────────────────────────────
# sshd must run as root; use an entrypoint script to inject env vars
# from docker-compose into the user's environment file, then start sshd.
USER root

COPY --chmod=755 entrypoint.sh /entrypoint.sh

EXPOSE 22

ENTRYPOINT ["/entrypoint.sh"]
35 changes: 35 additions & 0 deletions docker-ssh/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash
set -e

# Write any ANTHROPIC_API_KEY / GH_TOKEN / etc. passed as container env vars
# into devuser's ~/.ssh/environment so they're available in SSH sessions
# (requires PermitUserEnvironment yes in sshd_config).
ENV_FILE=/home/devuser/.ssh/environment
: > "$ENV_FILE"
chown devuser:devuser "$ENV_FILE"
chmod 600 "$ENV_FILE"

AGENT_VARS=(
ANTHROPIC_API_KEY
OPENAI_API_KEY
GH_TOKEN
GITHUB_TOKEN
GEMINI_API_KEY
GOOGLE_API_KEY
AMP_API_KEY
DASHSCOPE_API_KEY
KIMI_API_KEY
MISTRAL_API_KEY
CODEBUFF_API_KEY
FACTORY_API_KEY
CURSOR_API_KEY
)

for var in "${AGENT_VARS[@]}"; do
val="${!var:-}"
if [ -n "$val" ]; then
echo "${var}=${val}" >> "$ENV_FILE"
fi
done

exec /usr/sbin/sshd -D -e
2 changes: 1 addition & 1 deletion docs/app/[[...slug]]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/lib/layout.shared';
import { source } from '@/lib/source';

export default function Layout({ children }: { children: ReactNode }) {
return (
Expand Down
8 changes: 4 additions & 4 deletions docs/app/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { source } from '@/lib/source';
import { DocsPage, DocsBody, DocsDescription, DocsTitle } from 'fumadocs-ui/page';
import { notFound } from 'next/navigation';
import { getGithubLastEdit } from 'fumadocs-core/content/github';
import defaultMdxComponents from 'fumadocs-ui/mdx';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { CopyMarkdownButton } from '@/components/CopyMarkdownButton';
import { LastUpdated } from '@/components/LastUpdated';
import { getGithubLastEdit } from 'fumadocs-core/content/github';
import { source } from '@/lib/source';

async function getLastModifiedFromGitHub(filePath: string): Promise<Date | null> {
if (process.env.NODE_ENV === 'development') {
Expand Down
2 changes: 1 addition & 1 deletion docs/app/api/search/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';
import { source } from '@/lib/source';

export const { GET } = createFromSource(source, {
language: 'english',
Expand Down
2 changes: 1 addition & 1 deletion docs/lib/layout.shared.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Image from 'next/image';
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
import Image from 'next/image';

function Logo() {
return (
Expand Down
2 changes: 1 addition & 1 deletion docs/lib/source.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { loader } from 'fumadocs-core/source';
import * as icons from 'lucide-static';
import { docs } from 'fumadocs-mdx:collections/server';
import * as icons from 'lucide-static';

export const source = loader({
source: docs.toFumadocsSource(),
Expand Down
Loading