仓库起盘:workspace、目录结构、环境与命令入口先搭起来
先把仓库骨架和统一入口搭好,再让 AI 进入持续开发状态
仓库起盘,搭的不是脚手架,是协作界面#
很多项目在这个阶段都会说一句话:先把仓库搭起来。
但真到动手的时候,这句话经常会滑成另一件事:先把一堆代码生成出来。
这两件事看上去很像,实际差得很远。
“先把仓库搭起来”的真正意思,不是先把页面、接口、模块和脚手架铺满,而是先给后续开发立一个稳定的协作界面。这个界面既是给人看的,也是给 AI 看的。因为在 AI Native 开发里,仓库从来不只是代码容器,它本身就是上下文的一部分。
AI 第一次进一个仓库,最先看到的通常不是你的脑内蓝图,而是这些东西:
- 根目录下有哪些文件和目录。
README、AGENTS.md、spec、proposal 这些文档在说什么。- 有哪些 package,有哪些命令入口。
- 环境变量怎么组织,测试怎么跑,什么算通过。
如果这些入口是乱的,AI 后面再强,也只能一边猜一边写。
这也是为什么仓库起盘不是一个“顺手做”的准备动作,而是项目真正开工前的第一层控制面。说人话,就是先把后面所有人和 AI 会沿着哪几条路进入项目,先修成正路。
这一步真正要解决的,不是“能不能跑”,而是“会不会漂”#
很多人会觉得,仓库骨架这种事没什么好讲的,不就是新建目录、装依赖、配脚本吗?
问题在于,项目早期最贵的风险,从来不只是“今天跑不起来”,而是“每个人都能跑,但每个人都沿着不同方向在跑”。AI 会把这个问题放大得特别明显。因为它很擅长补全,但它补全的前提,是它先看到了一套足够稳定的边界。
如果没有边界,AI 一般会在下面几件事上自由发挥:
- 它会自己决定目录怎么拆。
- 它会自己决定哪些命令应该存在。
- 它会把占位包顺手写成半成品功能。
- 它会在不同 package 里塞入不同风格的脚本和依赖。
- 它会把本来应该先讨论清楚的职责分工,直接固化成代码事实。
于是仓库虽然“热闹起来了”,但很快会出现一种特别典型的混乱:目录很多、命令很多、代码也不少,可下一次让另一个 AI 接手时,大家还是得先花半天时间重新理解“这个仓库到底打算怎么长”。
所以仓库起盘真正要解决的问题,是让项目先具备最基本的可持续性。
不是只让它今天能跑,而是让它接下来每多开一个 issue、每多接入一个 AI、每多补一层实现,都不需要重新猜一遍入口和边界。
workspace 不是越早越大越好,而是越早越清楚越好#
讲“仓库起盘”,很多人第一反应会落到 monorepo 工具本身,比如到底用 Bun、pnpm workspace,还是别的管理方式。这个问题当然重要,但它不是第一问。
第一问其实应该是:这个项目当前阶段,究竟有没有必要把职责拆成多个稳定边界?
如果只有一个可执行体、一个部署单元、一个很明确的产品面,那就没必要一上来拆成五六个 package。
反过来,如果从一开始就已经明确至少存在几类不同职责,比如:
- 命令入口
- 核心逻辑
- 可视化或前端
- 协作文档与规范资产
那这时候提早把边界立住,价值就很高。因为这不是为了“看起来像大项目”,而是为了后面的人和 AI 都不会把不同职责继续揉在一起。
真正有用的 workspace,不是目录多,而是边界清。
也就是说:
- 每个 package 为什么存在,要说得清楚。
- 每个 package 当前能做什么、不能做什么,要写得清楚。
- 哪些只是占位,哪些已经进入真实实现,要写得清楚。
如果这三件事写不清楚,就算真的上了 monorepo,也只是把混乱分摊到了多个目录里。
什么时候该先用单包,什么时候该先切 workspace可以用一个很实用的判断:
- 如果当前阶段只有一个主要进程、一个交付面、一个实现主线,先单包往往更省。
- 如果已经明确存在多个稳定职责,而且这些职责后面会被不同 issue 分别推进,那就早点切 workspace。
这里的关键不是“项目大不大”,而是“边界稳不稳”。
仓库骨架的第一职责,是把职责先写出来#
很多人以为“骨架”指的是一堆目录。
我现在更倾向于把它理解成一组先被写出来的职责约定。
比如一个初期仓库,真正值得先固定下来的通常是这些东西:
-
根目录要有哪些项目级资产。
至少包括:
- 项目说明,告诉后来的人这是什么项目、现在到了哪一步。
- 协作说明,告诉 AI 和人应该遵守什么规则。
- 需求与规范资产,告诉大家当前总纲、issue 背景和后续收敛方式在哪里。
-
package 之间的职责边界。
不只是“有
cli、core、web三个目录”,而是要明确:cli是命令入口,不在这里偷偷长核心业务。core是稳定能力边界,不在这里先绑死 UI 或宿主。web是浏览与交互层,不直接吞掉整个后端或核心逻辑。
-
根命令和子命令的统一入口。
仓库刚起步时,命令越杂,后面越容易漂。
所以最先要固定的,不是复杂脚本,而是“统一从哪里进”。 -
占位实现的上限。
这一步非常关键。
如果没有明确约束,AI 很容易把“先占位”理解成“顺手把大半功能也写了”。于是你本来只是想先把边界立住,最后却在没有规范、没有验收、没有讨论收敛的情况下,提前背上了一堆半成品实现。
所以我现在更认同一种很克制的起盘方式:
目录可以先建,入口可以先跑,包可以先互相引用,但功能只保留 hello world 级别的最小占位。这个占位不是为了装样子,而是为了把“边界已经存在,但实现还没开始”这件事写成仓库事实。
一个足够克制的仓库骨架,大概长这样下面这个例子不是标准答案,但它能说明“骨架先服务边界,而不是先服务实现”:
repo/ AGENTS.md README.md package.json tsconfig.json openspec/ changes/ specs/ packages/ cli/ README.md src/ core/ README.md src/ web/ README.md src/这个骨架里最重要的不是目录数量,而是读者和 AI 一进仓库就知道:
- 项目说明在哪。
- 协作规则在哪。
- 规范资产在哪。
- 命令入口、核心能力、交互层分别准备落在哪里。
占位不是偷懒,而是护栏很多项目一开始就烂,不是因为起盘太慢,而是因为起盘时顺手写了太多“看起来已经差不多”的代码。
占位骨架真正保护的是两件事:
- 保护需求还没收紧时,不会被提前实现绑架。
- 保护后续 issue / spec 还有地方落,而不是一开始就被半成品代码抢跑。
README、AGENTS、Proposal,不是附属文档,而是仓库的一部分#
代码仓库最容易被低估的一类文件,就是那些“不产出功能”的文档。
但在 AI Native 开发里,它们其实不是附属品,而是仓库骨架本身。
原因很简单:AI 不是凭空理解项目的。它读文件、看命令、看目录、看说明。
如果 README 讲的是一套口径,AGENTS.md 限制的是另一套口径,proposal 又在说第三套方向,那后面每一轮实现都很容易漂。
所以仓库起盘阶段,至少要先把三类协作资产对齐:
-
README它回答的是:这个项目是什么、当前进度在哪、现在能做什么、不能做什么、怎么跑基础命令。
-
AGENTS.md它回答的是:AI 进入这个仓库后该遵守什么工作方式、什么能自动做、什么需要人工确认、哪些文档和目录要先读。
-
proposal / spec / task 这类规范资产
它们回答的是:为什么现在先做这一步、这一步的边界在哪、后面 issue 应该怎么切。
这三类东西如果能在项目起盘时就先对齐,后面一个非常现实的收益是:
每次新开 issue,不需要从头给 AI 补全项目背景。仓库本身已经能说话了。
这里还有一个很容易被忽略、但在 AI 协作里非常关键的点:
README 和 AGENTS.md 不是只能放在根目录。
很多项目真正稳定之后,都会自然长出一种分层说明结构:
-
根目录的
README/AGENTS.md负责全局口径。比如项目定位、总工作流、统一命令入口、全局护栏、哪些目录是正式主线。
-
package 目录下的
README/AGENTS.md负责局部职责。比如这个 package 是干什么的、当前已经能做什么、明确不做什么、公共 API 在哪、测试怎么跑。
-
更细的模块目录,也可以继续有自己的
README/AGENTS.md。如果一个模块已经复杂到会反复被单独修改,那它通常就值得拥有自己的局部说明和局部规范。
你可以把这理解成一种很实用的渐进式索引机制:
根目录先给总地图,进入某个子目录后,再读离自己最近的说明。这样人和 AI 都不会一上来就被整仓上下文淹没,也不会因为只看根目录而误解局部约束。
这件事对 AI 特别重要,因为不同 Agent 的默认习惯并不一样。有些 Agent 会主动往目标目录附近继续读,有些不会。如果发现手上的 Agent 不会自动检查子目录说明,那最稳的办法不是寄希望于它“下次聪明一点”,而是直接把规则写进根目录 AGENTS.md:
- 进入目标目录前,先找最近的
AGENTS.md。 - 如果没有,再找最近的
README.md。 - 更近目录的规则优先于根目录规则。
一旦这条规则被写成仓库事实,后面无论是人接手,还是换另一种 Agent 进来,都会稳定得多。
什么时候该给子目录单独写 README 或 AGENTS一个很简单的判断是:
- 如果这个目录只是几个很薄的文件,职责一眼能看明白,那不一定需要再补局部说明。
- 如果这个目录已经承载明确子域,而且实现者很可能会反复在这里独立工作,那就值得单独写。
常见场景包括:
packages/corepackages/webpackages/clipackages/core/parserpackages/web/components这些地方一旦开始长复杂,子目录说明文件带来的上下文收益通常很高。
GraphSpec 为什么把“协作资产对齐”单独当成起盘 issue在
GraphSpec的 Phase 0 任务里,起盘不是只有“建目录”这一件事。对应的总纲里,至少有三类东西被明确收进了起盘阶段:
- 仓库骨架与入口约束
- 协作资产与项目章程
- 最小基础 Harness
也就是说,真正的起盘不是把
packages/建出来就算完,而是让README、AGENTS.md、OpenSpec change、命令入口和占位实现一起对齐。可以对照这些文档看:
命令入口和环境边界,要先统一,不要后补#
很多仓库一开始的问题,不是没有命令,而是命令太多,而且谁都不敢删。
有人习惯 npm run dev,有人直接进子目录手敲脚本,有人新加了一个 start:local,AI 又补了一个 dev:web。过几轮之后,仓库里会形成一种特别典型的味道:能跑,但不知道官方入口到底是哪一个。
所以起盘阶段有一个特别值得先做的小动作:把命令面收紧。
最少要先统一三类命令:
-
安装与准备命令
比如依赖怎么装、环境变量从哪来、第一次进入仓库应该做什么。
-
基础校验命令
先别急着上重型 E2E。
像format、lint、typecheck、check、verify这一类最小秩序命令,先统一起来,已经能拦掉大量“仓库还没开始长就长歪了”的问题。 -
最小运行入口
当前阶段真正的主入口是什么,要尽量只保留一套官方路径。
不是每个 package 都要有花哨脚本,而是任何人第一次进仓库,都知道应该先从哪个命令开始。
环境变量也是同样的道理。
不是等功能写到一半再想 .env 怎么拆,而是起盘时就先决定:
- 哪些是项目级环境变量
- 哪些只属于某个 package
- 哪些现在根本还不应该出现
否则 AI 很容易为了“先跑起来”直接补一堆环境变量名,把未来架构还没收紧的部分提前写死。
起盘阶段更值得保留的是 4 类根入口这里更重要的不是具体命令名,而是根目录至少要有几类稳定入口。不同项目名字可以不一样,但职责最好清楚:
环境准备入口
比如
install、setup、bootstrap,负责安装依赖、准备本地环境、同步必要资源。一致性检查入口
比如
lint、check、verify,负责把风格、静态错误、约定漂移这类问题先拦住。最小运行入口
比如
dev、start、preview,负责让实现者和 AI 能快速确认“骨架是通的,主入口是清楚的”。当前阶段的最小验收入口
这一个最不该写死。有的项目会是
typecheck,有的会是test,有的会是build,也有的会是一个很薄的smoke。关键不是名字,而是当前阶段至少要有一个“跑完它,就知道仓库没完全坏掉”的入口。真正该避免的,不是少几个命令,而是每个人、每个 AI 都在自己发明一套入口。
Harness 在起盘阶段只需要秩序,不需要气势#
很多团队一提“工程化”,就会本能地往大而全的方向走:CI、E2E、覆盖率门槛、回归测试矩阵、预发布流水线,一套全上。
这种做法的问题,不是它永远不对,而是它经常发生得太早。
项目刚起盘时,最真实的风险通常不是“复杂交互回归没兜住”,而是下面这些更朴素的问题:
- 目录长歪了
- 包边界混了
- 命令不统一
- 类型炸了没人知道
- AI 顺手往占位包里塞了太多实现
所以这个阶段的 Harness,重点应该是“仓库秩序”,不是“完整质量体系”。
一个比较健康的起盘节奏通常是:
- Phase 0 先收环境准备、静态一致性检查、最小运行验证和当前阶段的最小验收入口
- 开始进入真实功能后,再逐步补契约测试、fixture、CLI 行为验证
- 前端交互复杂起来之后,再考虑更重的浏览器级测试
这背后的判断很简单:
Harness 要跟着风险长,而不是跟着焦虑长。
如果当前阶段只是要把骨架立住,那最小 Harness 就该服务这个目标,而不是先把后面几章的活抢着做完。
GraphSpec 这个案例里,为什么先停在 hello world 级骨架#
GraphSpec 这条线很能说明这个问题。
如果只看产品想象,它其实很容易让 AI 直接往下冲:CLI、图模型、可视化、扫描器、MCP,看上去每一块都很想立刻开始写。
但真正往前推进的时候,项目反而先做了一件更克制的事:把真实功能全部收住,只保留仓库骨架、命令入口、规范资产和 hello world 级占位。
这不是因为“还不会写”,而是因为团队已经意识到一件更要命的事:
如果骨架阶段就让 AI 顺着想象一路铺实现,后面 proposal、issue、spec 再怎么补,都要反过来替既成代码擦屁股。
GraphSpec 起盘阶段真正先搭了什么按当前总纲和仓库现状,
GraphSpec起盘阶段先固定下来的,其实是这些东西:
packages/cli、packages/core、packages/web三个职责边界- 统一命令入口和最小运行路径
README、AGENTS.md、OpenSpec change 这组协作资产- 只到占位为止的包实现标准
- 基础命令入口与最小一致性校验约束
换句话说,先落地的是“后续开发怎么有序发生”,而不是“第一批功能怎么赶紧写出来”。
如果仓库起盘做对了,后面会出现什么变化#
这一步做好之后,最直接的变化不是功能突然变多,而是后面的开发开始有节奏了。
通常会出现三个很明显的变化:
-
issue 更容易切薄
因为仓库边界已经在,后续 issue 更容易围绕单一职责推进,而不是总在“顺手改一下目录”“顺手补个脚本”。
-
AI 更不容易偷跑实现
因为命令、目录、职责和占位上限都已经先写清楚了,AI 的自由发挥空间会被约束在正确边界里。
-
后面的系统设计更容易落地
下一章要讲系统设计,但系统设计不是飘在文档里的。
只有当前仓库已经有了清楚的入口、边界和资产位置,后面的架构、数据、接口和模块拆分才有地方落。
如果这一章最后只留下五句话#
- 仓库起盘不是先生成一堆代码,而是先搭一个给人和 AI 共用的协作界面。
- workspace 的价值不在于目录多,而在于职责边界先被写清楚。
- 起盘阶段最重要的不是功能,而是
README、AGENTS.md、proposal、命令入口和占位标准这些协作资产。 - 占位骨架应该停在
hello world级别,它的作用是立边界,不是偷跑实现。 - Harness 在这一阶段只需要先守住仓库秩序,不需要提前摆出整套重武器。