* feat(website): serve paseo.sh in Simplified Chinese
Every page of the website now has a Simplified Chinese version under
/zh-CN: the interface, docs, blog posts, comparison pages, legal pages,
all changelog releases and the homepage mockup. English URLs are unchanged.
The URL alone decides the language. There is no browser-language redirect
and no stored preference, because either would have to redirect the
unprefixed English URLs that people share and search engines index.
Internal links carry the page's language instead, and hreflang alternates
point Chinese search results at /zh-CN.
Translated headings pin the English anchor with {#id}, so section links
work in both languages. content-translations.test.ts fails when a page
has no translation or a translation's headings drift from the English.
Changelog releases and plugin listings without a translation render
whole in English under a notice, so a release never waits on one.
Plugin manifests and the registry gain optional translations and
OVERVIEW.zh-CN.md fields; older clients and registries ignore them.
* feat(website): replace the native language select with an EN/ZH menu
The header showed an OS-native select whose popup did not match the site.
The trigger now shows the language abbreviated (EN, ZH) and opens a menu in
the app's dropdown style that names each language in itself, with its name
in the page language underneath. Rows are real links to the same page in
that language; arrows, Escape, outside presses and taps behave as a menu
button, and the menu keeps itself on screen when a narrow header pushes the
trigger toward an edge.
Also drops the stray space after the full-width comma in the Chinese
homepage subtitle.
* docs(website): translate main's new docs and 0.12.0 release notes
Main added Keep run workspaces to the schedules docs, rewrote plugin icons
around inline SVG, and promoted 0.12.0 with new entries. Bring the Chinese
pages and changelog in line so no section falls back to English.
* fix(server): leave plugin listing translations to the registry
The daemon rejected manifests whose translations the registry would refuse,
so a plugin main installs could fail to load here. Paseo never reads
translations; the registry validates them at publish. Docs now say which
checks are automated at publish and which are human review.
* docs(plugins): describe the registry's final overview translation discovery
* style(server): format manifest test
* test(app): measure sidebar rows one at a time
After a reload the developer error toast appears while expectVerticalOrder
measures every row at once. Each concurrent boundingBox runs the toast
locator handler, and only one of them resumes, so the poll timed out on
every attempt since #7424. Measuring sequentially keeps one action in flight.
* test(app): return sidebar row measuring to main
#7480 hides the developer toast instead of running a locator handler, so
measuring the rows concurrently no longer races it. Reverts 41ac23016.
* test(app): wait for the compact shell before opening compact Changes
The shell survives the breakpoint, so right after the resize the wide
Explorer could still be visible: the test skipped the Explorer toggle and
the tab then disappeared, or toggled at the wrong time. Wait for the
compact-shell marker, as the composer helpers do since #7424.
* docs(website): translate main's plugin file panel docs
* test(app): edit the code font size after the font family remount
A committed appearance change remounts the settings screen through
AppearanceStyleBoundary, and the new form starts from the stored size.
Filling the size before that remount lost the edit in CI.
* docs(website): translate main's client/ui import docs
Follows #7474: one /client/ui entry, StreamedText instead of useRevealedText,
SettingsGroup dropped from the settings table, and the new
'UI imports in v0.13' migration section.
* test(app): wait for the previous layout's file panel picker to leave
After a resize the old layout's picker animates out while the new one is
already mounted, so the Use button matched two elements and the strict
locator failed. Wait for the shell's layout and a single picker first.
9.1 KiB
title, description, nav
| title | description | nav |
|---|---|---|
| Git worktree | 在隔离的 Git worktree 中运行 Agent,并配置 setup 钩子、脚本和长时间运行的服务。 | Git worktree |
Git worktree
Git worktree 是工作区的一种。
工作区是任务发生的地方。当工作区由 Git worktree 支撑时,Paseo 会在单独的分支上创建一个单独的目录,让并行的 Agent 互不干扰。
本页介绍 Git 相关的细节:worktree 存放在哪里、分支如何选择,以及如何通过 paseo.json 配置 setup 钩子、脚本、终端和长时间运行的服务。
目录结构与工作流程
worktree 默认存放在 $PASEO_HOME/worktrees/ 下,按源检出路径的哈希分组。你可以用 config.json 中的 worktrees.root 修改基础目录。每个 worktree 在其工作区创建时获得一个 slug 和一个分支。
~/.paseo/worktrees/
└── 1vnnm9k3/ # hash of source checkout path
├── tidy-fox/ # worktree slug
└── bold-owl/
使用自定义根目录时,Paseo 会在该目录下保持相同的哈希结构:
{
"worktrees": {
"root": "/mnt/fast/paseo-worktrees"
}
}
- 创建一个使用 worktree 隔离的工作区,Paseo 会创建 worktree 并运行你的 setup 钩子
- 在该工作区中启动一个或多个 Agent
- 对照基础分支审查 diff
- 合并或归档该工作区;使用该 worktree 的最后一个工作区被归档后,Paseo 会运行 teardown 并删除 worktree
定时任务的运行默认会在结束时归档其工作区。在定时任务的应用表单中关闭 Archive on finish,即可保留运行工作区及其 worktree,直到你手动归档。
创建由 worktree 支撑的工作区
下面的示例以当前目录作为源检出。传入 --path ~/dev/my-app 可以从另一个检出创建工作区。
从基础分支分出新分支:
paseo workspace create \
--isolation worktree \
--mode branch-off \
--new-branch feature/auth \
--worktree-slug feature-auth \
--base origin/main
请使用 origin/main 而不是 main。Paseo 会在后台获取远程引用,因此远程跟踪分支是最新的,而你本地的 main 停留在你上次 pull 时的状态。不带前缀的 main 会优先解析到这个本地分支,worktree 就会从过时的历史开始。加上远程前缀可以明确指定已获取的引用。
检出一个已有分支:
paseo workspace create \
--isolation worktree \
--mode checkout-branch \
--branch feature/existing \
--worktree-slug existing-copy
或者在独立的工作区中打开一个 Pull Request:
paseo workspace create \
--isolation worktree \
--mode checkout-pr \
--pr-number 2186
当 Paseo 无法从源检出推断代码托管平台时,添加 --forge <name>。
paseo.json
在仓库根目录放一个 paseo.json。Paseo 从你所选基础分支的已提交版本中读取它,因此其他分支中未提交的改动不会生效。
{
"worktree": {
"setup": "npm ci",
"teardown": "rm -rf .cache"
},
"scripts": {
"test": { "command": "npm test" },
"web": { "command": "npm run dev", "type": "service", "port": 3000 }
}
}
Setup 与 teardown
setup 在 worktree 创建后运行一次。新的 worktree 没有安装依赖,也没有被忽略的文件(例如 .env),因此请用 setup 安装和复制你需要的内容。teardown 在归档期间、目录被删除之前运行。
{
"worktree": {
"setup": "npm ci\ncp \"$PASEO_SOURCE_CHECKOUT_PATH/.env\" .env\nnpm run db:migrate",
"teardown": "npm run db:drop || true"
}
}
两个字段都接受多行 shell 脚本或命令数组;无论哪种形式,命令都按顺序运行。
命令以 worktree 作为 cwd 运行。使用 $PASEO_SOURCE_CHECKOUT_PATH 访问原始检出中的文件(未跟踪的配置、本地缓存等)。
脚本与服务
scripts 是可以在 worktree 中按需运行的具名命令。把其中一个标记为 service,Paseo 就会把它作为长时间运行的进程进行监管,为它分配端口,并通过 daemon 的反向代理把 HTTP 流量路由给它。
可以在应用中运行它们,也可以在自动化中用 paseo script 和工作区脚本 MCP 工具管理它们。
普通脚本
{
"scripts": {
"test": { "command": "npm test" },
"lint": { "command": "npm run lint" },
"generate": { "command": "npm run codegen" }
}
}
服务
{
"scripts": {
"web": {
"type": "service",
"command": "npm run dev -- --port $PASEO_PORT",
"port": 3000
},
"api": {
"type": "service",
"command": "npm run api -- --port $PASEO_PORT"
}
}
}
省略 port 可以让 Paseo 自动分配端口。让你的进程绑定 $PASEO_PORT,而不是写死端口;每个 worktree 获得不同的端口,因此同一服务的多个副本可以共存。
动态端口分配
默认情况下,Paseo 向操作系统请求一个可用的临时端口。可以在 ~/.paseo/config.json 中全局配置端口范围,也可以在 paseo.json 中按项目配置:
// ~/.paseo/config.json
{
"worktrees": {
"servicePorts": { "range": "3000-4000" }
}
}
// paseo.json
{
"worktree": {
"servicePorts": { "range": "3000-4000" }
}
}
范围包含两端。项目的 servicePorts 块会替换全局块。服务上显式设置的 port 总是优先于这两种设置。
如果使用外部分配器,请改为配置 portScript:
{
"worktree": {
"servicePorts": { "portScript": "/usr/bin/portmake" }
}
}
Paseo 在工作区目录中运行该可执行文件,并传入四个参数:服务名、工作区 ID、分支名和 worktree 路径。由于脚本是直接执行的、不经过 shell,portScript 必须指向一个真正的可执行文件(例如编译后的二进制文件,或带有正确 shebang 行如 #!/bin/bash 的脚本),而不是内联的 shell 命令或管道。如果需要 shell 求值或管道,请把它们包装在一个小的可执行脚本中。没有分支时会传入空字符串。相同的值也可以通过 PASEO_SCRIPTNAME、PASEO_WORKSPACE_ID、PASEO_BRANCH_NAME 和 PASEO_WORKTREE_PATH 获取。它必须向 stdout 输出一个有效的 TCP 端口。同一块中 portScript 优先于 range。Paseo 信任外部分配器,因此返回的端口可能已经被占用,例如被 Paseo 将要连接的某个服务占用。
反向代理
每个服务都可以通过 daemon 以一个确定的主机名访问:
http://<script>--<branch>--<project>.localhost:<daemon-port>
# on the default branch, the branch label is dropped:
http://<script>--<project>.localhost:<daemon-port>
在现代系统上,*.localhost 解析为 127.0.0.1,因此这些 URL 开箱即用。代理支持 WebSocket 升级。
服务之间的访问
从同一工作区启动的服务可以看到彼此的端口和代理 URL。以上面的 web 和 api 为例,每个进程都会得到:
PASEO_PORT=3000 # this service's port
PASEO_URL=http://web--my-app.localhost:6767 # this service's proxy URL
PASEO_SERVICE_API_PORT=51732
PASEO_SERVICE_API_URL=http://api--my-app.localhost:6767
PASEO_SERVICE_WEB_PORT=3000
PASEO_SERVICE_WEB_URL=http://web--my-app.localhost:6767
脚本名会转为大写,非字母数字字符变为 _。让你的前端指向 $PASEO_SERVICE_API_URL,而不是写死端口。
终端
在 worktree 创建时自动打开终端。适合查看日志,或预先准备好一个 REPL。
{
"worktree": {
"terminals": [
{ "name": "logs", "command": "tail -f dev.log" },
{ "name": "shell", "command": "bash" }
]
}
}
环境变量
Setup、teardown、脚本和服务都能看到:
$PASEO_SOURCE_CHECKOUT_PATH,原始仓库根目录$PASEO_WORKTREE_PATH,worktree 目录$PASEO_BRANCH_NAME,worktree 的分支$PASEO_WORKTREE_PORT,旧版的每个 worktree 一个的端口(在服务中优先使用$PASEO_PORT)
服务还会额外得到:
$PASEO_PORT,该服务分配到的端口$PASEO_URL,该服务的代理 URL$PASEO_SERVICE_<NAME>_PORT/_URL,其他服务的端口和 URL$HOST,仅本地访问的 daemon 为127.0.0.1,daemon 绑定所有网络接口时为0.0.0.0
管理工作区
paseo workspace ls
paseo run --workspace <workspace-id> "implement auth"
paseo workspace rename <workspace-id> "Auth rework"
paseo workspace archive <workspace-id>
对于常见情况,paseo run --new-workspace worktree --worktree-mode branch-off --new-branch feature/auth --base origin/main "implement auth" 会同时创建工作区和它的第一个 Agent。