원클릭으로
env-init
为并行开发初始化每个 worktree 独立隔离的 runtime 环境。在创建新 worktree、dev server 因 port 或 database 冲突、或首次为某个项目搭建多 worktree 并行开发时使用。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
为并行开发初始化每个 worktree 独立隔离的 runtime 环境。在创建新 worktree、dev server 因 port 或 database 冲突、或首次为某个项目搭建多 worktree 并行开发时使用。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
FDD 主流程 step 1(规划与拆解)。覆盖两段——plan(与用户弄清需求、经 investigator 调查代码库、定出 milestone、写出 plan.md 并呈现)与 features(把 milestone 拆成 features.json 并过 coverage 闸)。中间的 contract 段交给 harness-stack:fdd-validation-contract。由 harness-stack:fdd 调用。
构建新特性的主流程编排器。契约优先的多 agent 架构——捕获一个 plan,定义可测试的断言,拆解为多个 feature,再用全新上下文的 implementer/reviewer/validator subagent 驱动一个里程碑设闸的执行循环。当一处改动触及多个文件、有多条验收标准、或跨越多个 feature 时使用。主流程分三步,分发给 fdd-planning(含 fdd-validation-contract)/ fdd-execution / fdd-validate。
为一个 plan 撰写 validation contract——把 definition of done 落成一组可测试、用户可观测的 assertion(VAL-<AREA>-NNN),带 persona 与声明的 Evidence。它是 fdd step 1(规划)里的 contract 阶段。契约通过逐 area 的 investigation subagent 与若干轮 adversarial review 构建,而非一人独写。产出 .harness-runtime/plans/<slug>/validation-contract.md,并经由 fdd init-state 播种 validation-state.json。在项目内首次使用时,还会 bootstrap 项目级约定文档 docs/user-test-patterns.md。
规范 git 工作流实践。任何代码改动都适用。在提交、开分支、解决冲突,或需要把多条并行工作线组织起来时使用。
harness-stack 框架的引导纲要(bootstrap doctrine)。在会话开始时自动加载,用以介绍 lifecycle map、golden rules,以及如何挑选正确的 harness-stack:* skill。在一次会话中首次调用任何 harness-stack:* skill 之前,先读它。
复盘一次 harness-stack 使用,把值得上报的摩擦、缺陷或建议提成 GitHub Issue 反馈给上游。在完成一项任务、用完某个 skill 后有意见或改进想法,或想为框架本身留下改进线索时使用。
| name | env-init |
| description | 为并行开发初始化每个 worktree 独立隔离的 runtime 环境。在创建新 worktree、dev server 因 port 或 database 冲突、或首次为某个项目搭建多 worktree 并行开发时使用。 |
为每个 git worktree 生成独立隔离的 runtime 环境,让多个 worktree 可以同时运行而不会发生 port、database 或进程冲突。
设计上非侵入式:直接复用项目已有的 .env / .env.example 文件。检测 port 变量、注入每个 worktree 专属的值——不会强加一套新的配置 schema。
灵感来自 OpenAI 的 per-worktree booting 模式:每个 worktree 拥有自己的应用实例、日志和状态;当 worktree 被移除时,这一切都会被干净地拆除。
适用于:
不适用于:
.env / .env.example 就直接复用;不引入新的 schema.env.example;各自独立处理,但共享同一个 port offsetscripts/env-* 和 .worktree-runtime/;其余一律复用既有约定找出项目中所有与 env 相关的文件:
# Root level
ls .env .env.example .env.local 2>/dev/null
# Monorepo: per-package
find . -name ".env.example" -not -path "./node_modules/*" -not -path "./.git/*"
find . -name ".env" -not -path "./node_modules/*" -not -path "./.git/*"
建立一份待管理的 env 文件清单:
| Path | 类型 | 说明 |
|---|---|---|
./.env.example | 已提交的模板 | 根目录的 source of truth |
./apps/web/.env.example | Package 模板 | Next.js app |
./apps/api/.env.example | Package 模板 | API server |
对每个 env 文件,识别与 port 相关的变量:
grep -E '^(PORT|[A-Z_]+_PORT|[A-Z_]+PORT)=' .env.example
对每个变量分类:
| 类别 | 示例 | 处理方式 |
|---|---|---|
| 已参数化 | env 中为 PORT=${PORT:-3000},代码中为 process.env.PORT | 注入值,无需重构 |
| env 中为固定默认值 | env 中为 PORT=3000,代码中为 process.env.PORT | 在生成的 .env 中覆盖 |
| 源码中硬编码 | 3000 出现在源码里;不在 env 中 | 抛给用户,建议重构 |
同时检查携带 port 的 URL:
DATABASE_URL=postgresql://localhost:5432/...REDIS_URL=redis://localhost:6379API_URL=http://localhost:4000这些也需要做 port 替换。
若 Step 2 在源码中发现硬编码的 port,停下来询问用户:
以下 port 在源码中被硬编码,不重构就无法隔离:
apps/web/next.config.js:23-- 硬编码 port3000apps/api/src/server.ts:15-- 硬编码 port4000Option A:重构为从 env 变量读取(
process.env.PORT)。推荐——可启用 worktree 隔离。Option B:在 env-init 中跳过这些服务。只对 port 由 env 驱动的服务做 worktree 隔离。
Option C:取消——我先手动重构。
选哪个?
记录这个决定;不要悄悄改动生产代码。
呈现 database 隔离选项让用户选择。完整对比见 references/database-strategy.md。
本项目的 database 隔离策略:
Option A:Multi-database -- 同一个 DB server,每个 worktree 独立的 database 名。 Option B:Embedded database -- 每个 worktree 一个 SQLite/DuckDB 文件。
本项目更适合哪一种?
把选择记录到 .worktree-runtime/state.json。
在 scripts/ 下创建五个脚本:
| Script | 职责 |
|---|---|
scripts/worktree-start.sh | 新 worktree 的入口。按 .worktreeinclude 从 main worktree 复制被 gitignore 的文件,再调用 env-init。幂等——手动调用或从 git hook 触发都安全。 |
scripts/env-init | 推导 worktree ID、计算 port offset、把算好的 port 写入已有的 .env 文件(若 .env 缺失则从 .env.example 复制),创建 / provision database |
scripts/env-start | 启动所有服务,把 PID 记录到 .worktree-runtime/pids/,把日志导向 .worktree-runtime/logs/ |
scripts/env-stop | SIGTERM -> 等待 -> SIGKILL,确认退出,清理 PID 文件 |
scripts/env-teardown | env-stop + drop database + 移除容器 + 清理 .worktree-runtime/ |
env-init 的关键行为:它不会替换 .env.example。它把已有的 .env.example 复制成 .env(在 .env 缺失时),然后只覆盖与 port 相关的变量和 database URL 为 worktree 专属值。若 .env 已存在,则就地 patch,保留无关变量(尤其是 secrets)。
worktree-start.sh 的关键行为:它通过 git worktree list --porcelain 定位 main worktree,读取 .worktreeinclude,把匹配且被 gitignore 的文件(如 .env.local、证书)复制进当前 worktree,且不覆盖已存在的文件。随后委托给 env-init。这是引导一个全新 worktree 的唯一入口。
所有脚本均为 POSIX sh。给它们加可执行权限(chmod +x)。
脚本模板与 port 注入算法见 references/runtime-lifecycle.md。
.worktreeinclude 与 Git Hook配置两个文件,让全新的 worktree 能自动引导。
.worktreeinclude(repo 根目录,已提交):列出必须复制进新 worktree 的、被 gitignore 的文件。使用 .gitignore 语法。只有既匹配某个 pattern 又被 gitignore 的文件才会被复制,因此被追踪的文件永远不会重复。
从项目已有 .gitignore 中通常承载本地配置的条目(.env、.env.local、凭据、证书)来初始化它。把生成的清单呈现给用户审阅。
.env
.env.local
config/secrets.json
.githooks/post-checkout(repo 根目录,已提交,可执行):一个薄封装,在全新 worktree 创建时调用 scripts/worktree-start.sh。Git 的 post-checkout hook 在 git checkout <branch> 和 git worktree add 时都会触发;封装用 .worktree-runtime/state.json 作为哨兵,因此它只在全新 worktree 上运行,后续切分支时则为 no-op。
告知用户每个 clone 运行一次以激活该 hook(Git 把 core.hooksPath 存在本地配置里,不纳入版本管理):
git config core.hooksPath .githooks
若用户创建 worktree 的工具绕过了 git hook,他们仍可在创建 worktree 后手动调用 ./scripts/worktree-start.sh。
hook 与 worktree-start.sh 的模板见 references/runtime-lifecycle.md。
为 runtime 状态搭建 .worktree-runtime/(已 gitignore):
.worktree-runtime/
worktree.id # Worktree identifier
ports.json # Actual allocated ports per service
state.json # Lifecycle state + user decisions
pids/ # PID files, one per service
logs/ # Log files, one per service
data/ # (Optional) embedded DB files
若 .gitignore 中尚无以下条目,则追加:
.worktree-runtime/
.env
.env.local
.env.*.local
# Activate the hook once (if not already done)
git config core.hooksPath .githooks
# Run the unified entry point (idempotent)
./scripts/worktree-start.sh
# Or run env-init directly
./scripts/env-init
# Inspect what was written
cat .env # Port variables should have worktree-specific values
cat .worktree-runtime/ports.json # Allocated ports
cat .worktree-runtime/state.json # DB strategy and state
./scripts/env-start
ls .worktree-runtime/pids/ .worktree-runtime/logs/
./scripts/env-stop
ls .worktree-runtime/pids/ # Should be empty after stop
| 借口 | 现实 |
|---|---|
| “冲突的时候我手动改一下 port 就好” | 你会忘。下一个 agent 不知道你的 port。一次性把它自动化。 |
| “我们一次只跑一个 worktree” | 直到某天不是。env-init 花 5 分钟;debug 一次 port 冲突花 30 分钟。 |
| “Docker 已经隔离了一切” | Docker 隔离的是容器,不是映射到 host 的 port。两个 ports: 3000:3000 的 compose stack 在 host 上仍会冲突。 |
| “我把 database 在多个 worktree 间共享” | 直到分支 A 的 migration 删掉了分支 B 依赖的某列。每个 worktree 独立 database 是廉价的保险。 |
| “追踪 PID 是过度设计” | 没有 PID 你就无法干净地停止。孤儿 dev server 吃内存、占 port。 |
| “干脆加个 .env.template 来替换 .env.example” | 别。项目已有约定。顺着它来。 |
.env 被提交进 gitenv-stop 脚本——服务只能靠关终端来杀.worktree-runtime/logs/.env.example 却还引入一套新的 env schema.env.example 文件都被保留(未被替换).env 文件包含算好的 worktree 专属 port 值.env 与 .worktree-runtime/ 已在 .gitignore 中scripts/worktree-start.sh、env-init、env-start、env-stop、env-teardown 均存在且可执行.worktreeinclude 存在于 repo 根目录,并列出要传播的、被 gitignore 的文件.githooks/post-checkout 存在、可执行,并委托给 scripts/worktree-start.shgit config core.hooksPath .githooks.worktree-runtime/ 含有 ports.json 与 state.jsonenv-stop 不留任何孤儿进程