
基于与 AI 的对话整理,帮助理解 gstack 项目的核心设计理念和实现机制
1. gstack 是什么
gstack 是 Y Combinator CEO Garry Tan 创建的 AI 工程工作流工具集。其核心理念是:让一个人能像一支二十人的工程团队一样高效工作。
核心特点
- • 给 AI 用的工具:不是用户直接操作,而是给 AI agent 提供"操作手册"
- • SKILL 系统:28+ 个专业技能(
/office-hours、/qa、/review、/browse等) - • 每个 skill 都有 SKILL.md:AI 执行任务时读取的操作手册
快速理解
gstack ≈ 螺丝刀套装(给 AI 用的)
SKILL.md ≈ 说明书(已经印刷好在盒子里)
用户 = 使用工具的人
AI = 工具的操作者2. SKILL.md 模板系统概述
2.1 问题背景
传统文档维护的问题:
开发者写代码 → 手动更新文档 → 时间久了可能忘记 → 文档和代码不一致 → AI 操作失败2.2 解决方案
SKILL.md.tmpl(人工编写)→ gen-skill-docs.ts(构建时生成)→ SKILL.md(自动同步)2.3 文件结构
gstack 项目/
├── browse/
│ ├── SKILL.md.tmpl ← 模板(人工写)
│ └── SKILL.md ← 生成的文档(自动更新)
├── qa/
│ ├── SKILL.md.tmpl
│ └── SKILL.md
└── scripts/
└── gen-skill-docs.ts ← 构建脚本2.4 处理流程
1. 读取 browse/SKILL.md.tmpl
2. 找到 {{PLACEHOLDER}}
3. 调用对应的 RESOLVER 函数
4. 替换成生成的内容
5. 输出 browse/SKILL.md3. 代码即文档:核心设计理念
3.1 什么是"代码即文档"
源代码是唯一真相,文档从代码自动生成,而不是人工维护。
3.2 传统方式 vs gstack 方式
| 传统方式 | gstack 方式 |
|---|---|
| 代码和文档分开维护 | 代码是唯一真相 |
| 人手动同步 | 构建时自动同步 |
| 可能不一致 | 验证保证不可能不一致 |
| 文档可能过时 | 文档永远和代码同步 |
3.3 强制验证机制
// commands.ts 末尾的验证代码
const allCmds = new Set([...READ_COMMANDS, ...WRITE_COMMANDS, ...META_COMMANDS]);
const descKeys = new Set(Object.keys(COMMAND_DESCRIPTIONS));
// 你加了命令但没加描述?构建直接报错!
for (const cmd of allCmds) {
if (!descKeys.has(cmd)) throw new Error(`COMMAND_DESCRIPTIONS missing entry for: ${cmd}`);
}3.4 为什么对 AI 特别重要
人读文档错了 → 问个问题就发现了
AI 读文档错了 → 整个任务就失败了对于 AI 依赖的系统,文档准确不是"加分项",而是"生存必需"。
4. commands.ts:命令注册表
4.1 文件位置
browse/src/commands.ts4.2 核心数据结构
// 命令分类
export const READ_COMMANDS = new Set([
'text', 'html', 'links', 'forms', 'accessibility',
'js', 'eval', 'css', 'attrs',
'console', 'network', 'cookies', 'storage', 'perf',
'dialog', 'is',
]);
export const WRITE_COMMANDS = new Set([
'goto', 'back', 'forward', 'reload',
'click', 'fill', 'select', 'hover', 'type', 'press', 'scroll', 'wait',
'viewport', 'cookie', 'cookie-import', 'cookie-import-browser',
'header', 'useragent', 'upload', 'dialog-accept', 'dialog-dismiss',
]);
export const META_COMMANDS = new Set([
'tabs', 'tab', 'newtab', 'closetab',
'status', 'stop', 'restart',
'screenshot', 'pdf', 'responsive',
'chain', 'diff', 'url', 'snapshot',
'handoff', 'resume',
]);
// 命令描述
export const COMMAND_DESCRIPTIONS: Record<string, {
category: string;
description: string;
usage?: string;
}> = {
'goto': { category: 'Navigation', description: 'Navigate to URL', usage: 'goto <url>' },
'click': { category: 'Interaction', description: 'Click element', usage: 'click <sel>' },
// ... 共 40+ 个命令
};4.3 依赖图
commands.ts ──▶ server.ts (运行时分发)
──▶ gen-skill-docs.ts (文档生成)
──▶ skill-parser.ts (验证)
──▶ skill-check.ts (健康报告)4.4 设计原则
| 原则 | 说明 |
|---|---|
| 单一来源 | 命令定义在一个地方,不会重复 |
| 自动同步 | 构建时自动生成文档 |
| 类型安全 | TypeScript 检查所有命令都有描述 |
| 零副作用 | 可以安全地被构建脚本和测试导入 |
5. gen-skill-docs.ts:模板处理器
5.1 核心逻辑
// 1. 找到所有模板文件
function findTemplates(): string[] {
const templates: string[] = [];
const rootTmpl = path.join(ROOT, 'SKILL.md.tmpl');
if (fs.existsSync(rootTmpl)) templates.push(rootTmpl);
for (const entry of fs.readdirSync(ROOT, { withFileTypes: true })) {
const tmpl = path.join(ROOT, entry.name, 'SKILL.md.tmpl');
if (fs.existsSync(tmpl)) templates.push(tmpl);
}
return templates;
}
// 2. 替换占位符
let content = tmplContent.replace(/\{\{(\w+)\}\}/g, (match, name) => {
const resolver = RESOLVERS[name];
if (!resolver) throw new Error(`Unknown placeholder {{${name}}}...`);
return resolver(ctx);
});5.2 RESOLVERS 字典
const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = {
COMMAND_REFERENCE: generateCommandReference, // 从 commands.ts 读取
SNAPSHOT_FLAGS: generateSnapshotFlags, // 从 snapshot.ts 读取
PREAMBLE: generatePreamble, // 初始化脚本
QA_METHODOLOGY: generateQAMethodology, // QA 方法论
// ... 共 21 个占位符
};5.3 处理示例
模板文件 browse/SKILL.md.tmpl:
{{PREAMBLE}}
# browse
{{COMMAND_REFERENCE}}经过处理后 browse/SKILL.md:
## Preamble (run first)
```bash
# bash 初始化脚本...browse
Navigation
| Command | Description |
|---|---|
goto <url> | Navigate to URL |
| ... |
---
## 6. 占位符系统详解
### 6.1 全部 21 个占位符
| 占位符 | 来源 | 注入内容 |
|--------|------|---------|
| `{{COMMAND_REFERENCE}}` | commands.ts | 从代码读取的所有命令表格 |
| `{{SNAPSHOT_FLAGS}}` | snapshot.ts | snapshot 命令的所有 flag |
| `{{PREAMBLE}}` | gen-skill-docs.ts | 50+ 行 bash 初始化脚本 |
| `{{BROWSE_SETUP}}` | gen-skill-docs.ts | 二进制发现逻辑 |
| `{{BASE_BRANCH_DETECT}}` | gen-skill-docs.ts | PR 基础分支检测 |
| `{{QA_METHODOLOGY}}` | gen-skill-docs.ts | QA 方法论 |
| `{{DESIGN_METHODOLOGY}}` | gen-skill-docs.ts | 设计评审方法论 |
| `{{TEST_BOOTSTRAP}}` | gen-skill-docs.ts | 测试框架检测 |
| `{{TEST_FAILURE_TRIAGE}}` | gen-skill-docs.ts | 测试失败分类 |
| `{{REVIEW_DASHBOARD}}` | gen-skill-docs.ts | 审核仪表板 |
| `{{DEPLOY_BOOTSTRAP}}` | gen-skill-docs.ts | 部署环境检测 |
| `{{SEARCH_BEFORE_BUILDING}}` | gen-skill-docs.ts | 三层知识体系 |
| `{{CODEX_REVIEW_STEP}}` | gen-skill-docs.ts | 对抗性评审 |
| `{{ADVERSARIAL_STEP}}` | gen-skill-docs.ts | 对抗性评审 |
| `{{DESIGN_REVIEW_LITE}}` | gen-skill-docs.ts | 简化设计评审 |
| `{{DESIGN_SKETCH}}` | gen-skill-docs.ts | 设计草图生成 |
| `{{TEST_COVERAGE_AUDIT_PLAN}}` | gen-skill-docs.ts | 测试覆盖率审计 |
| `{{TEST_COVERAGE_AUDIT_SHIP}}` | gen-skill-docs.ts | 测试覆盖率审计 |
| `{{TEST_COVERAGE_AUDIT_REVIEW}}` | gen-skill-docs.ts | 测试覆盖率审计 |
| `{{PLAN_FILE_REVIEW_REPORT}}` | gen-skill-docs.ts | 计划评审报告 |
| `{{BENEFITS_FROM}}` | frontmatter | 依赖的 skill 列表 |
### 6.2 占位符分类
**通用占位符**(几乎所有 skill 都有):
- `{{PREAMBLE}}` - 初始化脚本
- `{{TEST_BOOTSTRAP}}` - 测试框架检测
- `{{TEST_FAILURE_TRIAGE}}` - 测试失败处理
- `{{SEARCH_BEFORE_BUILDING}}` - 建造前搜索
**专用占位符**(特定 skill 使用):
- `{{COMMAND_REFERENCE}}` - 仅 browse 使用
- `{{SNAPSHOT_FLAGS}}` - 仅 browse 使用
- `{{QA_METHODOLOGY}}` - /qa 和 /qa-only 使用
---
## 7. PREAMBLE 的作用
### 7.1 组成结构
`{{PREAMBLE}}` 由 10 个子函数组成:
```typescript
function generatePreamble(ctx: TemplateContext): string {
return [
generatePreambleBash(ctx), // 1. bash 初始化脚本
generateUpgradeCheck(ctx), // 2. 升级检查
generateLakeIntro(), // 3. Boil the Lake 原则
generateTelemetryPrompt(ctx), // 4. 遥测提示
generateAskUserFormat(ctx), // 5. AskUserQuestion 格式
generateCompletenessSection(), // 6. 完整性原则
generateRepoModeSection(), // 7. Repo 所有权模式
generateSearchBeforeBuildingSection(ctx), // 8. 建造前搜索
generateContributorMode(), // 9. 贡献者模式
generateCompletionStatus(), // 10. 完成状态
].join('\n\n');
}7.2 generatePreambleBash 的内容
# 1. 更新检查
_UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || ...)
# 2. 会话跟踪
mkdir -p ~/.gstack/sessions
touch ~/.gstack/sessions/"$PPID"
_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l)
# 3. 获取配置
_CONTRIB=$(~/.claude/skills/gstack/bin/gstack-config get gstack_contributor ...)
_PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive ...)
# 4. 获取 Git 状态
_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
# 5. 获取 Repo 模式
source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null)
REPO_MODE=${REPO_MODE:-unknown}
# 6. 遥测记录
echo '{"skill":"...","ts":"...","repo":"..."}' >> ~/.gstack/analytics/skill-usage.jsonl7.3 PREAMBLE 的输出示例
运行后 AI 会看到:
BRANCH: feature/new-login
PROACTIVE: true
REPO_MODE: solo
LAKE_INTRO: yes
TELEMETRY: community
TEL_PROMPTED: yes7.4 PREAMBLE 的作用总结
| 功能 | 说明 |
|---|---|
| 更新检查 | 检查 gstack 是否有新版本 |
| 会话跟踪 | 追踪用户开了多少个会话 |
| 配置读取 | 获取用户偏好 |
| 分支检测 | 知道当前在哪个分支 |
| Repo 模式 | 知道是 solo 还是 collaborative |
| 遥测 | 记录使用情况 |
8. Claude 与 Codex 的差异
8.1 路径差异
| 方面 | Claude | Codex |
|---|---|---|
| skillRoot | ~/.claude/skills/gstack | ~/.codex/skills/gstack |
| localSkillRoot | .claude/skills/gstack | .agents/skills/gstack |
| 输出位置 | browse/SKILL.md | .agents/skills/gstack-browse/SKILL.md |
8.2 frontmatter 差异
function transformFrontmatter(content: string, host: Host): string {
if (host === 'claude') return content; // 原样返回
// Codex: 只保留 name + description
// 删除 allowed-tools, hooks, version 等
}8.3 安全处理差异
- • Claude:通过 Agent SDK 的 hooks 系统处理
- • Codex:生成内联安全提示文本
8.4 运行方式
# 生成 Claude 版
bun run gen:skill-docs
# 生成 Codex 版
bun run gen:skill-docs --host codex9. 如何新增一个 Skill
9.1 创建目录和模板
doc-write/
└── SKILL.md.tmpl9.2 编写模板内容
---
name: doc-write
description: |
AI-first documentation writing workflow. Generates, updates, and maintains
documentation with structured review loops.
allowed-tools:
- Bash
- Read
- AskUserQuestion
---
{{PREAMBLE}}
# doc-write: Documentation Workflow
## When to Use
- Creating new documentation
- Updating existing README
{{TEST_BOOTSTRAP}}9.3 运行构建
bun run gen:skill-docs9.4 检查生成结果
自动生成 doc-write/SKILL.md
9.5 如果需要自定义占位符
// 1. 在 gen-skill-docs.ts 添加生成函数
function generateDocTemplate(_ctx: TemplateContext): string {
return `## Documentation Template
1. Overview
2. Quick Start
...
`;
}
// 2. 在 RESOLVERS 中注册
const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = {
// ...
DOC_TEMPLATE: generateDocTemplate,
};
// 3. 在模板中使用
{{DOC_TEMPLATE}}10. 学习路径建议
阶段 1:从使用者的角度(先懂"是什么")
- 1. 读 README.md - 了解 gstack 是干嘛的
- 2. 读 AGENTS.md - 快速浏览所有技能
- 3. 实际用一次
/office-hours、/browse
阶段 2:从贡献者的角度(懂"怎么改")
- 1. 理解 SKILL.md 模板系统
- 2. 在 commands.ts 加一个新命令
- 3. 运行
bun run gen-skill-docs验证
阶段 3:从设计者的角度(懂"为什么")
- 1. 理解"代码即文档"理念
- 2. 理解构建时强制同步机制
- 3. 理解 AI-first 设计原则
附录:关键文件索引
| 文件 | 作用 |
|---|---|
browse/src/commands.ts | 所有命令的单一来源 |
scripts/gen-skill-docs.ts | 模板处理器 |
browse/SKILL.md.tmpl | browse 的模板 |
browse/SKILL.md | 生成的 browse 文档 |
ARCHITECTURE.md | 技术架构文档 |
文档整理时间:2026-04-10
夜雨聆风