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
25 changes: 0 additions & 25 deletions AGENTS.md

This file was deleted.

1 change: 1 addition & 0 deletions AGENTS.md
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Notes:
- `store.rs` — persists recent repos, PR cache, per-repo config and worktree root overrides, and default terminal as JSON to `~/.grove/store.json`. All functions (`persist`, `load_store`, `store_path`) are Tauri-independent — no `AppHandle` required.
- `cli.rs` — clap-based CLI. Worktree lifecycle verbs live under `grove worktree` (`list`, `new`, `detach`, `attach`, `rm`), with hidden top-level aliases (`grove new`/`detach`/`attach`/`rm`) kept for ergonomics. `detach`/`grove worktree detach` also accepts the legacy alias `convert` (the command was first shipped as `convert`). Other groups: `grove open`, `grove hook run/list/edit`, `grove config …`, `grove cd`, `grove shell-init`. `attach` is the inverse of `detach`: it removes a linked worktree and switches the main worktree onto its branch, carrying uncommitted changes via a stash. The binary is dual-mode: CLI args → terminal mode, no args → GUI. Detected in `main.rs` before Tauri init.
- `platform/` — cross-platform terminal launch abstraction (`open_terminal_at`, `open_terminal_app`) with per-OS implementations (macOS/Windows/Linux)
- `watcher.rs` — `notify`-based filesystem watcher on the repo's `.git/worktrees/` dir (falls back to `.git/` until it exists), emitting events so the frontend auto-refreshes the worktree list on external git changes. Managed in `lib.rs`; guarded against restart/event loops (#36).

**IPC pattern**: Rust structs use `#[serde(rename_all = "camelCase")]`. The frontend calls `invoke<T>("command_name", { input })` and receives camelCase JSON. When adding a new command: register in `lib.rs` `invoke_handler` macro → add TS wrapper in `api.ts` → add type in `types.ts`.

Expand Down Expand Up @@ -84,6 +85,7 @@ All UI work **must** follow `DESIGN_SYSTEM.md`. Key rules:
- `run_command_streaming` treats stderr as info-level during streaming; only escalates to error if exit code is non-zero (git writes informational messages like "Preparing worktree..." to stderr).
- Split components when necessary—don’t let a single file take on too much responsibility (for example, exceeding 500 lines). This applies to both React UI components and Rust functional modules.
- **Reuse `src/components/` components** — use `Input`, `Textarea`, `Select` from `FormControls.tsx` instead of native `<input>`, `<textarea>`, `<select>`; use `ModalShell` for modals, `Alert` for error banners. Do not introduce new wrapper components for things that already exist.
- User-facing docs live in `docs/` (`cli.md`, `hooks.md`, `configuration.md`), linked from `README.md` / `README_CN.md`. Keep them in sync when changing CLI commands, hook step types, or config keys. `AGENTS.md` is a symlink to this file.

## UI Layout

Expand Down
166 changes: 27 additions & 139 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,114 +1,38 @@
# Grove

A macOS-first desktop app for managing Git worktrees — launch editors, terminals, and AI coding agents from worktrees, with lifecycle hooks for automating setup.
A macOS-first desktop app for managing Git worktrees — launch editors,
terminals, and AI coding agents from worktrees, with lifecycle hooks for
automating setup.

English | [中文](./README_CN.md)

## Screenshot

![Image.png](./docs/screenshot.png)
![Grove](./docs/grove_0.13.1.png)

## Features

### Worktree

- **Scan** — Parses all worktrees via `git worktree list --porcelain`, showing dirty / ahead / behind / prunable / locked status
- **Create** — New branch, existing local branch, or remote tracking branch modes with branch dropdowns, random branch name suggestions, and auto-filled target paths
- **Remove** — Streamed execution logs, preview and execute `git worktree prune`
- **Launch** — Built-in launchers for Terminal, Ghostty, iTerm2, VS Code, Cursor, Claude CLI, Codex CLI, Gemini CLI, plus custom launchers

### Hooks

6 lifecycle hook events to automate custom actions during worktree creation, launch, and removal:

| Event | Fires | Working directory |
|-------|-------|-------------------|
| `pre-create` | **Before** worktree creation | Repo root |
| `post-create` | **After** worktree creation | Worktree path |
| `pre-launch` | **Before** launcher execution | Worktree path |
| `post-launch` | **After** launcher execution | Worktree path |
| `pre-remove` | **Before** worktree removal | Worktree path |
| `post-remove` | **After** worktree removal | Repo root |

Each hook consists of one or more **steps**, with 4 step types:

#### `script` — Run any shell command

```toml
[[hooks.post-create]]
type = "script"
run = "echo 'Worktree {branch} ready at {worktree_path}'"
```

#### `install` — Auto-detect package manager and install dependencies

Without `run`, auto-detects: pnpm → bun → yarn → npm, poetry → pdm → pipenv → uv → pip, bundle, cargo build, go mod download, composer, dotnet restore, gradlew, mvn, etc. Or specify a custom command.

```toml
[[hooks.post-create]]
type = "install"

# Or specify manually
[[hooks.post-create]]
type = "install"
run = "pip install -r requirements.txt"
```

#### `copy-files` — Copy files from repo root to new worktree

For copying untracked config files like `.env.local`. Skips if target already exists.

```toml
[[hooks.post-create]]
type = "copy-files"
paths = [".env.local", ".npmrc", ".env.production"]
```

#### `launch` — Trigger a launcher within a hook

```toml
[[hooks.post-create]]
type = "launch"
launcherId = "vscode"
```

#### Template variables

Use `{variable}` syntax in `run` fields to reference context variables:

| Variable | Description | Example |
|----------|-------------|---------|
| `{repo_root}` | Repo root path | `/Users/me/myrepo` |
| `{worktree_path}` | Worktree path | `/Users/me/myrepo/.worktrees/feat` |
| `{branch}` | Branch name | `feature/new-ui` |
| `{base_branch}` | Base branch name | `main` |
| `{head_sha}` | HEAD commit SHA | `a1b2c3d4...` |
| `{default_remote}` | Default remote | `origin` |
| `{is_main_worktree}` | Is main worktree | `true` / `false` |

Scripts also receive uppercase environment variables: `$REPO_ROOT`, `$WORKTREE_PATH`, `$BRANCH`, `$BASE_BRANCH`, `$HEAD_SHA`, `$IS_MAIN_WORKTREE`, `$DEFAULT_REMOTE`.

#### Re-run hooks

Manually trigger any configured hook event from the "Re-run Hooks" section in the worktree detail panel.

### CLI

Grove provides a `grove` CLI command (like VS Code's `code`), following the `<noun> <verb>` pattern:

```bash
grove . # Open current repo in Grove GUI
grove open [path] # Open a repository in the GUI
grove hook run <event> # Run hooks for a given event
grove hook list # List configured hooks
grove worktree list # List worktrees
```

Install the CLI via **Settings → CLI Command → Install CLI**, which creates a symlink at `/usr/local/bin/grove`. The `hook run` command auto-detects the repo root and worktree from the current directory.

### Other

- **GitHub PR integration** — Auto-queries and caches associated Pull Requests via `gh` CLI
- **Single instance** — Multiple `grove open` calls reuse the running app instance
- **i18n** — Chinese (default) and English
- **Worktrees** — Scan, create, and remove worktrees with dirty / ahead /
behind / prunable / locked status. Create from a new branch, an existing local
branch, or a remote tracking branch, with random branch-name suggestions and
auto-filled paths.
- **Launchers** — Built-in launchers for Terminal, Ghostty, iTerm2, VS Code,
Cursor, Claude CLI, Codex CLI, and Gemini CLI, plus custom launchers.
- **Hooks** — 6 lifecycle events automate dependency installs, config-file
copying, and launches around worktree create / launch / remove. See
[docs/hooks.md](./docs/hooks.md).
- **CLI** — A `grove` command (like VS Code's `code`) for managing worktrees,
hooks, and config from the terminal. See [docs/cli.md](./docs/cli.md).
- **GitHub PRs** — Auto-queries and caches associated Pull Requests via the `gh`
CLI.
- **Single instance** — Multiple `grove open` calls reuse the running app.
- **i18n** — Chinese (default) and English.

## Documentation

- [CLI reference](./docs/cli.md) — every `grove` subcommand, flags, and examples
- [Hooks](./docs/hooks.md) — lifecycle events, step types, template variables
- [Configuration](./docs/configuration.md) — config layers, settings keys, storage

## Stack

Expand All @@ -126,42 +50,6 @@ cd src-tauri && cargo test # Rust tests
cd src-tauri && cargo clippy # Rust lint
```

## Config

All config and app state is stored at `~/.grove/store.json` (recent repos, per-repo config, worktree root settings, default terminal, etc.). Grove does not write config files into the repository.

Per-repo config is edited via the in-app settings page (TOML format). Config is merged in order (later overrides earlier):

1. **Built-in defaults** — worktree root `.claude`, base branch `main`, built-in launchers
2. **Repo config** — TOML config edited in the UI

### Example config

```toml
[settings]
worktree-root = ".worktrees"
default-base-branch = "main"

[[hooks.post-create]]
type = "copy-files"
paths = [".env.local", ".env.production"]

[[hooks.post-create]]
type = "install"

[[hooks.post-create]]
type = "script"
run = "echo 'Worktree ready at $WORKTREE_PATH'"

[[hooks.post-create]]
type = "launch"
launcherId = "vscode"

[[hooks.pre-remove]]
type = "script"
run = "echo 'Cleaning up {branch}...'"
```

## License

MIT
150 changes: 22 additions & 128 deletions README_CN.md
Original file line number Diff line number Diff line change
@@ -1,104 +1,34 @@
# Grove

一个管理 Git Worktree 的 GUI 应用,帮你
一个管理 Git Worktree 的 macOS 桌面应用 —— 从 Worktree 一键启动编辑器、终端和 AI
编程助手,并通过生命周期 Hooks 自动完成环境准备。

- 快速从 Worktree 目录启动 VsCode / Ghostty 等应用,或者自己定义启动器
- 自定义 WorkTree Hooks,快速完成新建 Worktree 之后的依赖安装、配置文件复制等功能
[English](./README.md) | 中文

## 界面

![Image.png](./docs/screenshot.png)
![Grove](./docs/grove_0.13.1.png)

## 功能

### Worktree

- **工作树扫描** — 使用 `git worktree list --porcelain` 解析所有工作树,显示 dirty / ahead / behind / prunable / locked 状态
- **创建工作树** — 支持新分支、已有本地分支、远程分支三种模式,分支下拉选择,自动生成随机分支名,自动填充目标路径
- **删除工作树** — 流式执行日志,支持预览和执行 `git worktree prune`
- **一键启动** — 支持 Terminal、Ghostty、iTerm2、VS Code、Cursor、Claude CLI、Codex CLI、Gemini CLI

### Hooks

支持 6 个生命周期钩子事件,在工作树的创建、启动、删除各阶段自动执行自定义操作:

| 事件 | 触发时机 | 工作目录 |
| ------------- | ---------------- | ---------- |
| `pre-create` | 工作树创建**前** | 仓库根目录 |
| `post-create` | 工作树创建**后** | 工作树目录 |
| `pre-launch` | 启动器执行**前** | 工作树目录 |
| `post-launch` | 启动器执行**后** | 工作树目录 |
| `pre-remove` | 工作树删除**前** | 工作树目录 |
| `post-remove` | 工作树删除**后** | 仓库根目录 |

每个钩子由一个或多个**步骤**组成,支持 4 种步骤类型:

> 其实本质是 script 和 launch 两种,只是为了方便

#### `script` — 执行任意 Shell 命令

```toml
[[hooks.post-create]]
type = "script"
run = "echo 'Worktree {branch} ready at {worktree_path}'"
```

#### `install` — 自动检测包管理器并安装依赖

不指定 `run` 时自动检测:pnpm → bun → yarn → npm、poetry → pdm → pipenv → uv → pip、bundle、cargo build、go mod download、composer、dotnet restore、gradlew、mvn 等。也可手动指定命令。

```toml
[[hooks.post-create]]
type = "install"

# 或手动指定
[[hooks.post-create]]
type = "install"
run = "pip install -r requirements.txt"
```

#### `copy-files` — 从仓库根目录复制文件到新工作树

适合复制 `.env.local` 等不受版本控制的配置文件。目标已存在则跳过。

```toml
[[hooks.post-create]]
type = "copy-files"
paths = [".env.local", ".npmrc", ".env.production"]
```

#### `launch` — 在钩子中触发一个启动器

```toml
[[hooks.post-create]]
type = "launch"
launcherId = "vscode"
```

#### 模板变量(TODO)

在 `run` 字段中可使用 `{variable}` 语法引用上下文变量:

| 变量 | 说明 | 示例 |
| -------------------- | --------------- | ------------------------------------- |
| `{repo_root}` | 仓库根目录路径 | `/Users/me/myrepo` |
| `{worktree_path}` | 工作树目录路径 | `/Users/me/myrepo/.worktrees/feat` |
| `{branch}` | 分支名 | `feature/new-ui` |
| `{base_branch}` | 基础分支名 | `main` |
| `{head_sha}` | HEAD commit SHA | `a1b2c3d4...` |
| `{default_remote}` | 默认远程名 | `origin` |
| `{is_main_worktree}` | 是否为主工作树 | `true` / `false` |

脚本执行时同时注入大写环境变量:`$REPO_ROOT`、`$WORKTREE_PATH`、`$BRANCH`、`$BASE_BRANCH`、`$HEAD_SHA`、`$IS_MAIN_WORKTREE`、`$DEFAULT_REMOTE`。

#### 手动重新运行

在工作树详情面板的「Re-run Hooks」区域可手动触发任意已配置的钩子事件。

### 其他特性

- **GitHub PR 集成** — 通过 `gh` CLI 自动查询并缓存关联的 Pull Request
- **i18n** — 中文(默认)和英文
- **Worktree 管理** —— 扫描、创建、删除 Worktree,显示 dirty / ahead / behind /
prunable / locked 状态。支持新分支、已有本地分支、远程分支三种创建模式,自动生成
随机分支名并填充目标路径。
- **启动器** —— 内置 Terminal、Ghostty、iTerm2、VS Code、Cursor、Claude CLI、
Codex CLI、Gemini CLI 启动器,也支持自定义启动器。
- **Hooks** —— 6 个生命周期事件,在 Worktree 创建 / 启动 / 删除时自动安装依赖、
复制配置文件、触发启动器。详见 [docs/hooks.md](./docs/hooks.md)。
- **CLI** —— 提供 `grove` 命令(类似 VS Code 的 `code`),在终端中管理 Worktree、
Hooks 和配置。详见 [docs/cli.md](./docs/cli.md)。
- **GitHub PR 集成** —— 通过 `gh` CLI 自动查询并缓存关联的 Pull Request。
- **单实例** —— 多次 `grove open` 复用同一个运行中的应用窗口。
- **i18n** —— 中文(默认)和英文。

## 文档

- [CLI 参考](./docs/cli.md) —— 所有 `grove` 子命令、参数与示例
- [Hooks](./docs/hooks.md) —— 生命周期事件、步骤类型、模板变量
- [配置](./docs/configuration.md) —— 配置层级、设置项、存储位置

## 技术栈

Expand All @@ -116,42 +46,6 @@ cd src-tauri && cargo test # Rust 测试
cd src-tauri && cargo clippy # Rust lint
```

## 配置

所有配置和应用状态统一存储在 `~/.grove/store.json`(最近仓库、各仓库配置、工作树目录设置、默认终端等)。Grove 不会在仓库目录中写入任何配置文件。

在应用内通过设置页面编辑每个仓库的配置(TOML 格式),配置按以下优先级合并(后者覆盖前者):

1. **内置默认** — 默认工作树目录 `.claude`,基础分支 `main`,内置启动器
2. **仓库配置** — 在 UI 中编辑的 TOML 配置

### 配置示例

```toml
[settings]
worktree-root = ".worktrees"
default-base-branch = "main"

[[hooks.post-create]]
type = "copy-files"
paths = [".env.local", ".env.production"]

[[hooks.post-create]]
type = "install"

[[hooks.post-create]]
type = "script"
run = "echo 'Worktree ready at $WORKTREE_PATH'"

[[hooks.post-create]]
type = "launch"
launcherId = "vscode"

[[hooks.pre-remove]]
type = "script"
run = "echo 'Cleaning up {branch}...'"
```

## License

MIT
Loading