7602
阅读时间 26 分钟

仓库起盘:workspace、目录结构、环境与命令入口先搭起来

先把仓库骨架和统一入口搭好,再让 AI 进入持续开发状态

仓库起盘,搭的不是脚手架,是协作界面#

很多项目在这个阶段都会说一句话:先把仓库搭起来。
但真到动手的时候,这句话经常会滑成另一件事:先把一堆代码生成出来。

这两件事看上去很像,实际差得很远。

“先把仓库搭起来”的真正意思,不是先把页面、接口、模块和脚手架铺满,而是先给后续开发立一个稳定的协作界面。这个界面既是给人看的,也是给 AI 看的。因为在 AI Native 开发里,仓库从来不只是代码容器,它本身就是上下文的一部分。

AI 第一次进一个仓库,最先看到的通常不是你的脑内蓝图,而是这些东西:

  1. 根目录下有哪些文件和目录。
  2. READMEAGENTS.md、spec、proposal 这些文档在说什么。
  3. 有哪些 package,有哪些命令入口。
  4. 环境变量怎么组织,测试怎么跑,什么算通过。

如果这些入口是乱的,AI 后面再强,也只能一边猜一边写。
这也是为什么仓库起盘不是一个“顺手做”的准备动作,而是项目真正开工前的第一层控制面。说人话,就是先把后面所有人和 AI 会沿着哪几条路进入项目,先修成正路。

这一步真正要解决的,不是“能不能跑”,而是“会不会漂”#

很多人会觉得,仓库骨架这种事没什么好讲的,不就是新建目录、装依赖、配脚本吗?

问题在于,项目早期最贵的风险,从来不只是“今天跑不起来”,而是“每个人都能跑,但每个人都沿着不同方向在跑”。AI 会把这个问题放大得特别明显。因为它很擅长补全,但它补全的前提,是它先看到了一套足够稳定的边界。

如果没有边界,AI 一般会在下面几件事上自由发挥:

  • 它会自己决定目录怎么拆。
  • 它会自己决定哪些命令应该存在。
  • 它会把占位包顺手写成半成品功能。
  • 它会在不同 package 里塞入不同风格的脚本和依赖。
  • 它会把本来应该先讨论清楚的职责分工,直接固化成代码事实。

于是仓库虽然“热闹起来了”,但很快会出现一种特别典型的混乱:目录很多、命令很多、代码也不少,可下一次让另一个 AI 接手时,大家还是得先花半天时间重新理解“这个仓库到底打算怎么长”。

所以仓库起盘真正要解决的问题,是让项目先具备最基本的可持续性。
不是只让它今天能跑,而是让它接下来每多开一个 issue、每多接入一个 AI、每多补一层实现,都不需要重新猜一遍入口和边界。

workspace 不是越早越大越好,而是越早越清楚越好#

讲“仓库起盘”,很多人第一反应会落到 monorepo 工具本身,比如到底用 Bunpnpm workspace,还是别的管理方式。这个问题当然重要,但它不是第一问。

第一问其实应该是:这个项目当前阶段,究竟有没有必要把职责拆成多个稳定边界?

如果只有一个可执行体、一个部署单元、一个很明确的产品面,那就没必要一上来拆成五六个 package。
反过来,如果从一开始就已经明确至少存在几类不同职责,比如:

  • 命令入口
  • 核心逻辑
  • 可视化或前端
  • 协作文档与规范资产

那这时候提早把边界立住,价值就很高。因为这不是为了“看起来像大项目”,而是为了后面的人和 AI 都不会把不同职责继续揉在一起。

真正有用的 workspace,不是目录多,而是边界清。
也就是说:

  1. 每个 package 为什么存在,要说得清楚。
  2. 每个 package 当前能做什么、不能做什么,要写得清楚。
  3. 哪些只是占位,哪些已经进入真实实现,要写得清楚。

如果这三件事写不清楚,就算真的上了 monorepo,也只是把混乱分摊到了多个目录里。

什么时候该先用单包,什么时候该先切 workspace

可以用一个很实用的判断:

  • 如果当前阶段只有一个主要进程、一个交付面、一个实现主线,先单包往往更省。
  • 如果已经明确存在多个稳定职责,而且这些职责后面会被不同 issue 分别推进,那就早点切 workspace。

这里的关键不是“项目大不大”,而是“边界稳不稳”。

仓库骨架的第一职责,是把职责先写出来#

很多人以为“骨架”指的是一堆目录。
我现在更倾向于把它理解成一组先被写出来的职责约定。

比如一个初期仓库,真正值得先固定下来的通常是这些东西:

  1. 根目录要有哪些项目级资产。

    至少包括:

    • 项目说明,告诉后来的人这是什么项目、现在到了哪一步。
    • 协作说明,告诉 AI 和人应该遵守什么规则。
    • 需求与规范资产,告诉大家当前总纲、issue 背景和后续收敛方式在哪里。
  2. package 之间的职责边界。

    不只是“有 clicoreweb 三个目录”,而是要明确:

    • cli 是命令入口,不在这里偷偷长核心业务。
    • core 是稳定能力边界,不在这里先绑死 UI 或宿主。
    • web 是浏览与交互层,不直接吞掉整个后端或核心逻辑。
  3. 根命令和子命令的统一入口。

    仓库刚起步时,命令越杂,后面越容易漂。
    所以最先要固定的,不是复杂脚本,而是“统一从哪里进”。

  4. 占位实现的上限。

    这一步非常关键。
    如果没有明确约束,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 一进仓库就知道:

  1. 项目说明在哪。
  2. 协作规则在哪。
  3. 规范资产在哪。
  4. 命令入口、核心能力、交互层分别准备落在哪里。
占位不是偷懒,而是护栏

很多项目一开始就烂,不是因为起盘太慢,而是因为起盘时顺手写了太多“看起来已经差不多”的代码。

占位骨架真正保护的是两件事:

  1. 保护需求还没收紧时,不会被提前实现绑架。
  2. 保护后续 issue / spec 还有地方落,而不是一开始就被半成品代码抢跑。

README、AGENTS、Proposal,不是附属文档,而是仓库的一部分#

代码仓库最容易被低估的一类文件,就是那些“不产出功能”的文档。
但在 AI Native 开发里,它们其实不是附属品,而是仓库骨架本身。

原因很简单:AI 不是凭空理解项目的。它读文件、看命令、看目录、看说明。
如果 README 讲的是一套口径,AGENTS.md 限制的是另一套口径,proposal 又在说第三套方向,那后面每一轮实现都很容易漂。

所以仓库起盘阶段,至少要先把三类协作资产对齐:

  1. README

    它回答的是:这个项目是什么、当前进度在哪、现在能做什么、不能做什么、怎么跑基础命令。

  2. AGENTS.md

    它回答的是:AI 进入这个仓库后该遵守什么工作方式、什么能自动做、什么需要人工确认、哪些文档和目录要先读。

  3. proposal / spec / task 这类规范资产

    它们回答的是:为什么现在先做这一步、这一步的边界在哪、后面 issue 应该怎么切。

这三类东西如果能在项目起盘时就先对齐,后面一个非常现实的收益是:
每次新开 issue,不需要从头给 AI 补全项目背景。仓库本身已经能说话了。

这里还有一个很容易被忽略、但在 AI 协作里非常关键的点:
READMEAGENTS.md 不是只能放在根目录。

很多项目真正稳定之后,都会自然长出一种分层说明结构:

  1. 根目录的 README / AGENTS.md 负责全局口径。

    比如项目定位、总工作流、统一命令入口、全局护栏、哪些目录是正式主线。

  2. package 目录下的 README / AGENTS.md 负责局部职责。

    比如这个 package 是干什么的、当前已经能做什么、明确不做什么、公共 API 在哪、测试怎么跑。

  3. 更细的模块目录,也可以继续有自己的 README / AGENTS.md

    如果一个模块已经复杂到会反复被单独修改,那它通常就值得拥有自己的局部说明和局部规范。

你可以把这理解成一种很实用的渐进式索引机制:
根目录先给总地图,进入某个子目录后,再读离自己最近的说明。这样人和 AI 都不会一上来就被整仓上下文淹没,也不会因为只看根目录而误解局部约束。

这件事对 AI 特别重要,因为不同 Agent 的默认习惯并不一样。有些 Agent 会主动往目标目录附近继续读,有些不会。如果发现手上的 Agent 不会自动检查子目录说明,那最稳的办法不是寄希望于它“下次聪明一点”,而是直接把规则写进根目录 AGENTS.md

  1. 进入目标目录前,先找最近的 AGENTS.md
  2. 如果没有,再找最近的 README.md
  3. 更近目录的规则优先于根目录规则。

一旦这条规则被写成仓库事实,后面无论是人接手,还是换另一种 Agent 进来,都会稳定得多。

什么时候该给子目录单独写 README 或 AGENTS

一个很简单的判断是:

  • 如果这个目录只是几个很薄的文件,职责一眼能看明白,那不一定需要再补局部说明。
  • 如果这个目录已经承载明确子域,而且实现者很可能会反复在这里独立工作,那就值得单独写。

常见场景包括:

  • packages/core
  • packages/web
  • packages/cli
  • packages/core/parser
  • packages/web/components

这些地方一旦开始长复杂,子目录说明文件带来的上下文收益通常很高。

GraphSpec 为什么把“协作资产对齐”单独当成起盘 issue

GraphSpec 的 Phase 0 任务里,起盘不是只有“建目录”这一件事。

对应的总纲里,至少有三类东西被明确收进了起盘阶段:

  1. 仓库骨架与入口约束
  2. 协作资产与项目章程
  3. 最小基础 Harness

也就是说,真正的起盘不是把 packages/ 建出来就算完,而是让 READMEAGENTS.md、OpenSpec change、命令入口和占位实现一起对齐。

可以对照这些文档看:

  1. README.md
  2. proposal.md
  3. tasks.md

命令入口和环境边界,要先统一,不要后补#

很多仓库一开始的问题,不是没有命令,而是命令太多,而且谁都不敢删。
有人习惯 npm run dev,有人直接进子目录手敲脚本,有人新加了一个 start:local,AI 又补了一个 dev:web。过几轮之后,仓库里会形成一种特别典型的味道:能跑,但不知道官方入口到底是哪一个。

所以起盘阶段有一个特别值得先做的小动作:把命令面收紧。

最少要先统一三类命令:

  1. 安装与准备命令

    比如依赖怎么装、环境变量从哪来、第一次进入仓库应该做什么。

  2. 基础校验命令

    先别急着上重型 E2E。
    formatlinttypecheckcheckverify 这一类最小秩序命令,先统一起来,已经能拦掉大量“仓库还没开始长就长歪了”的问题。

  3. 最小运行入口

    当前阶段真正的主入口是什么,要尽量只保留一套官方路径。
    不是每个 package 都要有花哨脚本,而是任何人第一次进仓库,都知道应该先从哪个命令开始。

环境变量也是同样的道理。
不是等功能写到一半再想 .env 怎么拆,而是起盘时就先决定:

  • 哪些是项目级环境变量
  • 哪些只属于某个 package
  • 哪些现在根本还不应该出现

否则 AI 很容易为了“先跑起来”直接补一堆环境变量名,把未来架构还没收紧的部分提前写死。

起盘阶段更值得保留的是 4 类根入口

这里更重要的不是具体命令名,而是根目录至少要有几类稳定入口。不同项目名字可以不一样,但职责最好清楚:

  1. 环境准备入口

    比如 installsetupbootstrap,负责安装依赖、准备本地环境、同步必要资源。

  2. 一致性检查入口

    比如 lintcheckverify,负责把风格、静态错误、约定漂移这类问题先拦住。

  3. 最小运行入口

    比如 devstartpreview,负责让实现者和 AI 能快速确认“骨架是通的,主入口是清楚的”。

  4. 当前阶段的最小验收入口

    这一个最不该写死。有的项目会是 typecheck,有的会是 test,有的会是 build,也有的会是一个很薄的 smoke。关键不是名字,而是当前阶段至少要有一个“跑完它,就知道仓库没完全坏掉”的入口。

真正该避免的,不是少几个命令,而是每个人、每个 AI 都在自己发明一套入口。

Harness 在起盘阶段只需要秩序,不需要气势#

很多团队一提“工程化”,就会本能地往大而全的方向走:CI、E2E、覆盖率门槛、回归测试矩阵、预发布流水线,一套全上。

这种做法的问题,不是它永远不对,而是它经常发生得太早。

项目刚起盘时,最真实的风险通常不是“复杂交互回归没兜住”,而是下面这些更朴素的问题:

  • 目录长歪了
  • 包边界混了
  • 命令不统一
  • 类型炸了没人知道
  • AI 顺手往占位包里塞了太多实现

所以这个阶段的 Harness,重点应该是“仓库秩序”,不是“完整质量体系”。

一个比较健康的起盘节奏通常是:

  1. Phase 0 先收环境准备、静态一致性检查、最小运行验证和当前阶段的最小验收入口
  2. 开始进入真实功能后,再逐步补契约测试、fixture、CLI 行为验证
  3. 前端交互复杂起来之后,再考虑更重的浏览器级测试

这背后的判断很简单:
Harness 要跟着风险长,而不是跟着焦虑长。
如果当前阶段只是要把骨架立住,那最小 Harness 就该服务这个目标,而不是先把后面几章的活抢着做完。

GraphSpec 这个案例里,为什么先停在 hello world 级骨架#

GraphSpec 这条线很能说明这个问题。

如果只看产品想象,它其实很容易让 AI 直接往下冲:CLI、图模型、可视化、扫描器、MCP,看上去每一块都很想立刻开始写。
但真正往前推进的时候,项目反而先做了一件更克制的事:把真实功能全部收住,只保留仓库骨架、命令入口、规范资产和 hello world 级占位。

这不是因为“还不会写”,而是因为团队已经意识到一件更要命的事:
如果骨架阶段就让 AI 顺着想象一路铺实现,后面 proposal、issue、spec 再怎么补,都要反过来替既成代码擦屁股。

GraphSpec 起盘阶段真正先搭了什么

按当前总纲和仓库现状,GraphSpec 起盘阶段先固定下来的,其实是这些东西:

  • packages/clipackages/corepackages/web 三个职责边界
  • 统一命令入口和最小运行路径
  • READMEAGENTS.md、OpenSpec change 这组协作资产
  • 只到占位为止的包实现标准
  • 基础命令入口与最小一致性校验约束

换句话说,先落地的是“后续开发怎么有序发生”,而不是“第一批功能怎么赶紧写出来”。

如果仓库起盘做对了,后面会出现什么变化#

这一步做好之后,最直接的变化不是功能突然变多,而是后面的开发开始有节奏了。

通常会出现三个很明显的变化:

  1. issue 更容易切薄

    因为仓库边界已经在,后续 issue 更容易围绕单一职责推进,而不是总在“顺手改一下目录”“顺手补个脚本”。

  2. AI 更不容易偷跑实现

    因为命令、目录、职责和占位上限都已经先写清楚了,AI 的自由发挥空间会被约束在正确边界里。

  3. 后面的系统设计更容易落地

    下一章要讲系统设计,但系统设计不是飘在文档里的。
    只有当前仓库已经有了清楚的入口、边界和资产位置,后面的架构、数据、接口和模块拆分才有地方落。

如果这一章最后只留下五句话#

  • 仓库起盘不是先生成一堆代码,而是先搭一个给人和 AI 共用的协作界面。
  • workspace 的价值不在于目录多,而在于职责边界先被写清楚。
  • 起盘阶段最重要的不是功能,而是 READMEAGENTS.md、proposal、命令入口和占位标准这些协作资产。
  • 占位骨架应该停在 hello world 级别,它的作用是立边界,不是偷跑实现。
  • Harness 在这一阶段只需要先守住仓库秩序,不需要提前摆出整套重武器。

下一篇:《系统设计:架构、数据、接口、模块边界先立住》

仓库起盘:workspace、目录结构、环境与命令入口先搭起来
更新于
2026-04-18
© 2026 AI 原生工程(AI Native Engineering)
内容版权归对应作者与贡献者所有;项目汇编与品牌归项目维护方所有。
文稿默认采用 CC BY-NC-SA 4.0,示例代码采用 MIT License。
Powered by Next.js & Fumadocs
This site is powered by Netlify
Theme inspired by Fuwari