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
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ jobs:
with:
bun-version: latest

- name: Install dependencies
- name: Install backend dependencies
working-directory: backend
run: bun install --frozen-lockfile

- name: Install frontend dependencies
run: bun install --frozen-lockfile

- name: Typecheck (tsc)
Expand Down
43 changes: 21 additions & 22 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
## Project Overview

AgentForge is a macOS desktop app (Electron + Bun/TypeScript) that provides a kanban-style task board for orchestrating AI coding agents (**Claude Code or OpenAI Codex CLI**). The TypeScript backend (running on Bun) manages task scheduling, execution, and persistence; the React frontend renders the board and streams live output. Tasks can also be created/monitored from chat channels (Telegram / Slack / Feishu / WeChat), and a **Skill Library** distills recurring task patterns into reusable Claude Code skills.
AgentForge is a macOS desktop app (Electrobun + Bun/TypeScript) that provides a kanban-style task board for orchestrating AI coding agents (**Claude Code or OpenAI Codex CLI**). The TypeScript backend (running on Bun) manages task scheduling, execution, and persistence; the React frontend renders the board and streams live output. Tasks can also be created/monitored from chat channels (Telegram / Slack / Feishu / WeChat), and a **Skill Library** distills recurring task patterns into reusable Claude Code skills.

The entire project is TypeScript-only and uses **Bun** to build, run, test, and compile (no Python, no Node toolchain — Electron's bundled Node runs the packaged app, but all tooling is Bun).
The entire project is TypeScript-only and uses **Bun** to build, run, test, and compile (no Python, no Node toolchain; Electrobun's Bun host runs the packaged app).

## Commands

Expand All @@ -14,20 +14,19 @@ cd backend && bun taskboard.ts
# Verify health
curl http://127.0.0.1:9712/api/health

# Compile single-file binary for packaging (replaces PyInstaller)
cd taskboard-electron && bun scripts/build-backend.ts
# (equivalent: cd backend && bun run compile)
# Optional: compile a standalone backend binary
cd backend && bun run compile
```

### Electron App
### Electrobun App
```bash
# Dev: Bun-builds main/preload/renderer, launches Electron with watch + reload
# (spawns `bun backend/taskboard.ts` automatically)
# Dev: Electrobun builds the Bun host + React view, starts the backend in-process,
# watches sources, and relaunches on changes.
cd taskboard-electron && bun run start

# Build distributable DMG (arm64)
cd taskboard-electron && bun run make
# Output: taskboard-electron/out/make/AgentForge-1.0.0-arm64.dmg
# Output: taskboard-electron/build/stable-macos-arm64/AgentForge.dmg
```

### Tests & Quality
Expand All @@ -41,11 +40,11 @@ cd backend && bun test # run the suite directly

# Frontend (⭐ frontend-quality CI job runs all five, from taskboard-electron/)
cd taskboard-electron
bun run typecheck # tsc over renderer + main/preload tsconfigs
bun run typecheck # tsc over renderer + Electrobun host/config tsconfigs
bun run lint # ESLint (flat config, typescript-eslint, eslint.config.mjs)
bun run format:check # Prettier --check (bun run format to apply)
bun run test # bun test (pins TZ=Asia/Shanghai — date tests assert local wall time)
bun run build:check # Bun.build of main/preload/renderer — catches compile/import errors
bun run build:check # Electrobun build — catches compile/import/resource errors
```
CI (`.github/workflows/ci.yml`) runs two jobs: **backend-quality** (`bun run check` in `backend/`)
and **frontend-quality** (typecheck + lint + format check + tests + build). The workflow uses
Expand All @@ -54,8 +53,8 @@ and **frontend-quality** (typecheck + lint + format check + tests + build). The

## Architecture

### Two-process model
The Electron main process (`taskboard-electron/src/main.ts`) spawns the Bun backend on startup and kills it on quit. The React renderer communicates with the backend exclusively via HTTP on `127.0.0.1:9712` (loopback only). There is no WebSocket or IPC for data — the renderer polls the REST API with `fetch()`.
### Desktop host model
The Electrobun Bun main process (`taskboard-electron/src/electrobun/main.ts`) starts the Bun backend in-process on startup and stops it on quit. The React renderer communicates with the backend exclusively via HTTP on `127.0.0.1:9712` (loopback only). There is no WebSocket for app data — the renderer polls the REST API with `fetch()`. Native desktop affordances, such as directory selection, use Electrobun RPC behind the existing renderer bridge facade.

### Bun backend (`backend/`)
TypeScript modules served by `Bun.serve` (entry `backend/taskboard.ts`):
Expand All @@ -71,17 +70,17 @@ TypeScript modules served by `Bun.serve` (entry `backend/taskboard.ts`):
- **Channels** (`src/channels/`) — Optional Telegram / Slack / Feishu / WeChat bridges (a `MessageBus` in `src/bus.ts` decouples them from the scheduler). Feishu uses a lark WebSocket long-connection (`@larksuiteoapi/node-sdk`).
- **REST API** (`src/api.ts`, `src/server.ts`) — Endpoints under `/api/tasks*`, `/api/heartbeats*`, `/api/skill-patterns`, `/api/skills*`, `/api/settings`, `/api/channels/*`, `/api/health`.

### Electron main process (`taskboard-electron/src/main.ts`)
- In **dev mode**: `app.getAppPath()` returns `taskboard-electron/`, so `path.join(app.getAppPath(), '..')` resolves to project root for `bun backend/taskboard.ts`. The `cwd` option must point to project root when spawning.
- In **packaged mode**: uses the `bun build --compile` binary at `resources/taskboard` bundled inside the `.app`.
- Polls `/api/health` (15s timeout) before loading the UI.
- Exposes `window.electronAPI.selectDirectory()` to renderer via context bridge for native directory picker.
### Electrobun main process (`taskboard-electron/src/electrobun/main.ts`)
- Starts `backend/src/server.ts` via `runServer(9712)` inside the Electrobun Bun host.
- Sets packaged resource env vars for the Weixin bridge and vendored `skill-creator`.
- Creates the main `BrowserWindow` at `views://main/index.html`.
- Exposes native directory picking to the renderer through Electrobun RPC while preserving `window.electronAPI.selectDirectory()` as a compatibility facade.

### Build pipeline (`taskboard-electron/scripts/`)
- `build.ts` — `Bun.build` bundles main (CJS, electron external), preload (CJS), and renderer (HTML entrypoint → `.bun/renderer/`). Replaces the old Vite plugin.
- `dev.ts` — watch-rebuild + Electron launcher; renderer rebuilds trigger window reload (main.ts watches `.bun/renderer`), backend `.ts` changes restart the backend.
- `build-backend.ts` — `bun build --compile` of `backend/taskboard.ts` into `resources/taskboard`.
- electron-forge is retained only for packaging/DMG (`bunx electron-forge package|make`); `forge.config.js` ships `.bun/` output via packager ignore rules.
- `electrobun.config.ts` — Electrobun app metadata, Bun host entrypoint, React view entrypoint, copied assets/resources, and macOS packaging options.
- `build-electrobun-resources.ts` — compiles the Weixin bridge sidecar into `resources/weixin-bridge` before Electrobun copies resources.
- `bun run build:check` / `bun run build` — run `electrobun build`.
- `bun run make` — runs `electrobun build --env=stable` and produces the stable macOS DMG.

### React frontend (`taskboard-electron/src/renderer/App.tsx`)
Single large component (~6200 lines). Key design points:
Expand Down
52 changes: 27 additions & 25 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
.PHONY: help build-backend build-electron package-dmg clean install-deps lint format format-check test test-cov check dev-backend dev-electron check-backend check-dmg
.PHONY: help build-resources build-electrobun package-dmg clean install-deps lint format format-check test test-cov check dev-backend dev-electrobun check-backend check-dmg

# 项目配置
PROJECT_NAME = AgentForge
BACKEND_DIR = backend
BACKEND_BINARY = taskboard-electron/resources/taskboard
ELECTRON_DIR = taskboard-electron
DMG_OUTPUT = $(ELECTRON_DIR)/out/make/$(PROJECT_NAME)-1.0.0-arm64.dmg
APP_DIR = taskboard-electron
WEIXIN_BRIDGE = $(APP_DIR)/resources/weixin-bridge
DMG_OUTPUT = $(APP_DIR)/build/stable-macos-arm64/$(PROJECT_NAME).dmg

help:
@echo "AgentForge 打包工具 (Bun + TypeScript)"
@echo ""
@echo "可用命令:"
@echo " make help - 显示此帮助信息"
@echo " make install-deps - 安装项目依赖 (bun install)"
@echo " make build-backend - 编译后端单文件二进制 (bun build --compile)"
@echo " make build-electron - 构建Electron应用"
@echo " make build-resources - 编译 Electrobun 侧车资源"
@echo " make build-electrobun - 构建 Electrobun 应用"
@echo " make package-dmg - 打包为DMG文件(包含所有步骤)"
@echo " make clean - 清理构建文件"
@echo ""
Expand All @@ -23,23 +23,23 @@ help:
install-deps:
@echo "安装后端依赖..."
cd $(BACKEND_DIR) && bun install
@echo "安装Electron依赖..."
cd $(ELECTRON_DIR) && bun install
@echo "安装 Electrobun 应用依赖..."
cd $(APP_DIR) && bun install

build-backend:
@echo "编译后端单文件二进制 (bun build --compile)..."
cd $(ELECTRON_DIR) && bun scripts/build-backend.ts
@echo "后端二进制文件位置: $(BACKEND_BINARY)"
@ls -lh $(BACKEND_BINARY)
build-resources:
@echo "编译 Electrobun 侧车资源..."
cd $(APP_DIR) && bun run build:resources
@echo "Weixin bridge 位置: $(WEIXIN_BRIDGE)"
@ls -lh $(WEIXIN_BRIDGE)

build-electron:
@echo "构建Electron应用..."
cd $(ELECTRON_DIR) && bun scripts/build.ts && SKIP_BACKEND_BUILD=1 bunx electron-forge package
@echo "Electron应用构建完成"
build-electrobun:
@echo "构建 Electrobun 应用..."
cd $(APP_DIR) && bun run build
@echo "Electrobun 应用构建完成"

package-dmg: build-backend
package-dmg:
@echo "打包DMG文件..."
cd $(ELECTRON_DIR) && bun scripts/build.ts && SKIP_BACKEND_BUILD=1 bunx electron-forge make
cd $(APP_DIR) && bun run make
@if [ -f "$(DMG_OUTPUT)" ]; then \
echo "DMG文件生成成功: $(DMG_OUTPUT)"; \
ls -lh "$(DMG_OUTPUT)"; \
Expand All @@ -50,19 +50,21 @@ package-dmg: build-backend

clean:
@echo "清理构建文件..."
rm -rf $(ELECTRON_DIR)/out/
rm -rf $(ELECTRON_DIR)/.bun/
rm -f $(BACKEND_BINARY)
rm -rf $(APP_DIR)/build/
rm -rf $(APP_DIR)/artifacts/
rm -rf $(APP_DIR)/.bun/
rm -f $(APP_DIR)/resources/taskboard
rm -f $(WEIXIN_BRIDGE)
@echo "清理完成"

# 开发相关命令
dev-backend:
@echo "启动后端开发服务器..."
cd $(BACKEND_DIR) && bun taskboard.ts

dev-electron:
@echo "启动Electron开发模式..."
cd $(ELECTRON_DIR) && bun run start
dev-electrobun:
@echo "启动 Electrobun 开发模式..."
cd $(APP_DIR) && bun run start

# 检查命令
check-backend:
Expand Down
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ bun install --ignore-scripts
bun run start
```

The Electron dev command builds the renderer, starts the Bun backend, and reloads the app while you work.
The Electrobun dev command builds the renderer, starts the Bun backend in the desktop host, and relaunches the app while you work.

### Build A DMG

Expand All @@ -47,7 +47,7 @@ cd taskboard-electron
bun run make
```

The packaged app bundles the compiled Bun backend at `taskboard-electron/out/make/AgentForge-1.0.0-arm64.dmg`.
The packaged app is built by Electrobun. Stable macOS builds write the DMG under `taskboard-electron/build/stable-macos-arm64/`.

## What You Can Do

Expand Down Expand Up @@ -82,7 +82,7 @@ The automatic sweep is off by default because it spends tokens. Enable it in Set

```text
┌─────────────────────┐ HTTP/JSON ┌─────────────────────┐
Electron + React UI │ <--------------------> │ Bun TypeScript API │
Electrobun + React │ <--------------------> │ Bun TypeScript API │
│ Task board renderer │ 127.0.0.1:9712 │ Scheduler + runner │
└─────────────────────┘ └──────────┬──────────┘
Expand All @@ -92,7 +92,7 @@ The automatic sweep is off by default because it spends tokens. Enable it in Set
~/.agentforge cron + delayed claude / codex
```

- `taskboard-electron/src/main.ts` starts and stops the backend with the desktop app.
- `taskboard-electron/src/electrobun/main.ts` starts and stops the backend with the desktop app.
- `taskboard-electron/src/renderer/App.tsx` renders the task board, heartbeats, settings, and skills.
- `backend/taskboard.ts` serves the REST API through `Bun.serve`.
- `backend/src/db.ts` stores tasks, runs, output events, settings, heartbeats, and skills in SQLite.
Expand Down Expand Up @@ -172,7 +172,7 @@ cd backend
bun taskboard.ts
```

Frontend and Electron app:
Desktop app:

```bash
cd taskboard-electron
Expand All @@ -196,12 +196,11 @@ bun run build:check

## Troubleshooting

If `bun install` stalls while downloading Electron, use a mirror:
If `bun install` or `bun run build:check` stalls while downloading Electrobun artifacts, use the npm registry mirror:

```bash
export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
cd taskboard-electron
bun install --registry https://registry.npmmirror.com --ignore-scripts
bun install --registry https://registry.npmmirror.com
```

More setup notes live in [docs/installation-troubleshooting.md](docs/installation-troubleshooting.md).
Expand All @@ -215,7 +214,7 @@ More setup notes live in [docs/installation-troubleshooting.md](docs/installatio
Key files:

- `backend/` - Bun/TypeScript backend, scheduler, executor, API, and channels.
- `taskboard-electron/src/main.ts` - Electron main process.
- `taskboard-electron/src/electrobun/main.ts` - Electrobun Bun main process.
- `taskboard-electron/src/renderer/App.tsx` - React renderer.
- `skills/agentforge/` - bundled skill for agent-to-agent delegation.

Expand Down
17 changes: 8 additions & 9 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ bun install --ignore-scripts
bun run start
```

开发命令会构建 Electron/React 前端、启动 Bun 后端,并在改动后自动刷新
开发命令会构建 Electrobun/React 前端,在桌面宿主进程中启动 Bun 后端,并在改动后重新启动 App

### 构建 DMG

Expand All @@ -47,7 +47,7 @@ cd taskboard-electron
bun run make
```

产物位于 `taskboard-electron/out/make/AgentForge-1.0.0-arm64.dmg`。
稳定版 macOS DMG 产物位于 `taskboard-electron/build/stable-macos-arm64/`。

## 核心能力

Expand Down Expand Up @@ -82,7 +82,7 @@ Skill Library 会把你反复做的工作沉淀成智能体可复用技能。

```text
┌─────────────────────┐ HTTP/JSON ┌─────────────────────┐
Electron + React UI │ <--------------------> │ Bun TypeScript API │
Electrobun + React │ <--------------------> │ Bun TypeScript API │
│ Task board renderer │ 127.0.0.1:9712 │ Scheduler + runner │
└─────────────────────┘ └──────────┬──────────┘
Expand All @@ -92,7 +92,7 @@ Skill Library 会把你反复做的工作沉淀成智能体可复用技能。
~/.agentforge cron + delayed claude / codex
```

- `taskboard-electron/src/main.ts` 随桌面 App 启停后端。
- `taskboard-electron/src/electrobun/main.ts` 随桌面 App 启停后端。
- `taskboard-electron/src/renderer/App.tsx` 渲染任务看板、心跳、设置和技能库。
- `backend/taskboard.ts` 通过 `Bun.serve` 暴露 REST API。
- `backend/src/db.ts` 用 SQLite 存储任务、运行记录、输出事件、设置、心跳和技能。
Expand Down Expand Up @@ -172,7 +172,7 @@ cd backend
bun taskboard.ts
```

前端和 Electron App:
桌面 App:

```bash
cd taskboard-electron
Expand All @@ -196,12 +196,11 @@ bun run build:check

## 常见问题

如果 `bun install` 下载 Electron 时卡住,可以使用镜像:
如果 `bun install` 或 `bun run build:check` 下载 Electrobun artifact 时卡住,可以使用 npm 镜像:

```bash
export ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
cd taskboard-electron
bun install --registry https://registry.npmmirror.com --ignore-scripts
bun install --registry https://registry.npmmirror.com
```

更多说明见 [docs/installation-troubleshooting.md](docs/installation-troubleshooting.md)。
Expand All @@ -215,7 +214,7 @@ bun install --registry https://registry.npmmirror.com --ignore-scripts
关键文件:

- `backend/` - Bun/TypeScript 后端、调度器、执行器、API 和渠道。
- `taskboard-electron/src/main.ts` - Electron 主进程。
- `taskboard-electron/src/electrobun/main.ts` - Electrobun Bun 主进程。
- `taskboard-electron/src/renderer/App.tsx` - React 渲染进程。
- `skills/agentforge/` - 智能体委派任务的内置技能。

Expand Down
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"typecheck": "bunx tsc --noEmit",
"format": "prettier --write \"taskboard.ts\" \"src/**/*.ts\" \"tests/**/*.ts\"",
"format:check": "prettier --check \"taskboard.ts\" \"src/**/*.ts\" \"tests/**/*.ts\"",
"compile": "bun build --compile taskboard.ts --outfile ../taskboard-electron/resources/taskboard"
"compile": "mkdir -p ../dist && bun build --compile taskboard.ts --outfile ../dist/taskboard"
},
"dependencies": {
"@larksuiteoapi/node-sdk": "^1.55.0",
Expand Down
1 change: 1 addition & 0 deletions backend/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const MAX_BODY_SIZE = 10 * 1024 * 1024;
function isAllowedOrigin(origin: string): boolean {
if (origin === "null") return true;
if (!origin) return true;
if (origin.startsWith("views://")) return true;
return (
origin === "http://localhost" || origin.startsWith("http://localhost:")
);
Expand Down
Loading