重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
incremental-commits by epicenterhq/epicenter
npx skills add https://github.com/epicenterhq/epicenter --skill incremental-commits当一个功能涉及多个文件时,采用分波次的方式实现。每个波次对应一个逻辑关注点,一次提交。这样可以创建清晰、有故事性的 Git 历史记录。
相关技能:关于提交信息规范和 PR 指南,请参阅
git。
Wave 1: 基础(类型、接口)
↓
Wave 2: 工厂/构建器(创建实例的函数)
↓
Wave 3: 契约/API(使用类型的公共接口)
↓
Wave 4: 基础设施(工具、转换器、依赖项)
↓
Wave 5: 消费者(应用、UI、集成)
并非所有变更都需要所有波次。一个简单的错误修复可能只需要一个波次。一个横切重构可能需要五个波次。
每个波次必须满足:
| 属性 | 描述 |
|---|---|
| 原子性 | 每个波次一个逻辑关注点 |
| 可构建性 | 此波次后代码可编译(运行类型检查) |
| 专注性 | 变更仅涉及一个层/关注点 |
| 完整性 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 波次内没有未完成的工作 |
此功能将元数据从工作空间移动到表。共五个波次:
feat(schema): 添加 IconDefinition、CoverDefinition 和 FieldMetadata 类型
- 添加可辨识联合类型 IconDefinition (emoji | external)
- 添加可辨识联合类型 CoverDefinition (external)
- 为所有字段类型添加带有可选 name/description 的 FieldMetadata
- 更新 TableDefinition 以使用 icon/cover 替代 emoji/order
文件:仅 types.ts。为其他所有内容奠定基础。
feat(schema): 为字段工厂函数添加可选的 name 和 description 参数
所有工厂函数 (id, text, richtext, integer, real, boolean, date,
select, tags, json) 现在都接受可选的 name 和 description 参数。
文件:仅 factories.ts。使用波次 1 中的类型。
feat(schema): 从 WorkspaceSchema 中移除 emoji 和 description
Workspace 现在只是一个包含 guid, id, name, tables 和 kv 的容器。
视觉元数据 (icon, cover, description) 现在位于 TableDefinition 上。
文件:仅 contract.ts。使用新类型的 API 变更。
feat(schema): 使用 slugify 生成人类可读的 SQL 列名
- 添加 @sindresorhus/slugify 依赖项
- 添加使用 slugify 和 '_' 分隔符的 toSqlIdentifier() 辅助函数
- SQLite 列现在使用 field.name(或从 key 派生)而不是 key
文件:to-drizzle.ts, package.json。使用字段元数据的工具。
feat(schema): 更新 epicenter 应用以使用 TablesWithMetadata
- WorkspaceSchema 现在接受 TablesSchema | TablesWithMetadata
- 从包索引导出新类型
- 更新应用以创建带有元数据的正确 TableDefinition
文件:使用新类型的应用文件。
编码前规划波次
实现一个波次
验证该波次
bun run tsc --noEmit提交该波次
为下一个波次重复此过程
| 场景 | 使用波次? | 原因 |
|---|---|---|
| 单文件错误修复 | 否 | 一次变更,一次提交 |
| 添加新类型 + 工厂 | 可能 | 可以是 1-2 个波次 |
| 跨 5+ 个文件重构 | 是 | 需要逻辑分组 |
| 破坏性 API 变更 | 是 | 类型 → API → 消费者 |
| 添加依赖项并使用它 | 是 | 基础设施波次,然后使用波次 |
refactor: 更新模式系统
- 添加新类型
- 更新工厂
- 更改契约
- 添加 slugify
- 更新应用
问题:一个庞大的提交。无法二分查找,无法部分回滚,没有故事性。
feat: 添加 IconDefinition 类型
feat: 添加 CoverDefinition 类型
feat: 添加 FieldMetadata 类型
feat: 更新 IdFieldSchema
feat: 更新 TextFieldSchema
...
问题:过于细化。一个逻辑变更有 20 次提交。产生噪音。
Wave 1: 更新应用以使用新类型 ❌
Wave 2: 添加类型 ❌
问题:波次 1 无法编译。应该是自底向上,而不是自顶向下。
决定波次顺序时,问:“这个文件导入了什么?”
types.ts → 不导入任何内容(基础)
factories.ts → 导入 types.ts
contract.ts → 导入 types.ts
converters.ts → 导入 types.ts,可能添加依赖项
app/ → 导入上述所有内容
不导入任何内容的文件优先。导入所有内容的文件最后。
对于多波次工作:
# 创建功能分支
git checkout -b feat/my-feature
# 波次 1
# ... 进行更改 ...
git add <files> && git commit -m "feat(scope): 波次 1 描述"
# 波次 2
# ... 进行更改 ...
git add <files> && git commit -m "feat(scope): 波次 2 描述"
# ... 继续后续波次 ...
# 完成后,所有波次都是分支上的独立提交
# PR 展示了功能如何演变的清晰历史
开始前:
对于每个波次:
所有波次完成后:
每周安装次数
49
仓库
GitHub 星标数
4.3K
首次出现
2026年1月28日
安全审计
安装于
opencode46
codex46
gemini-cli46
cursor45
claude-code44
github-copilot44
When a feature touches multiple files, implement in waves. Each wave is one logical concern, one commit. This creates a clean git history that tells a story.
Related Skills : See
gitfor commit message conventions and PR guidelines.
Wave 1: Foundation (types, interfaces)
↓
Wave 2: Factories/Builders (functions that create instances)
↓
Wave 3: Contracts/APIs (public interfaces that use types)
↓
Wave 4: Infrastructure (utilities, converters, dependencies)
↓
Wave 5: Consumers (apps, UI, integrations)
Not every change needs all waves. A simple bugfix might be one wave. A cross-cutting refactor might need five.
Each wave must be:
| Property | Description |
|---|---|
| Atomic | One logical concern per wave |
| Buildable | Code compiles after this wave (run type-check) |
| Focused | Changes relate to ONE layer/concern |
| Complete | No half-done work within a wave |
This feature moved metadata from workspace to tables. Five waves:
feat(schema): add IconDefinition, CoverDefinition, and FieldMetadata types
- Add IconDefinition discriminated union (emoji | external)
- Add CoverDefinition discriminated union (external)
- Add FieldMetadata with optional name/description to all field types
- Update TableDefinition to use icon/cover instead of emoji/order
Files: types.ts only. Foundation for everything else.
feat(schema): add optional name/description to field factory functions
All factory functions (id, text, richtext, integer, real, boolean, date,
select, tags, json) now accept optional name and description parameters.
Files: factories.ts only. Uses types from Wave 1.
feat(schema): remove emoji and description from WorkspaceSchema
Workspace is now just a container with guid, id, name, tables, and kv.
Visual metadata (icon, cover, description) now lives on TableDefinition.
Files: contract.ts only. API change using new types.
feat(schema): use slugify for human-readable SQL column names
- Add @sindresorhus/slugify dependency
- Add toSqlIdentifier() helper using slugify with '_' separator
- SQLite columns now use field.name (or derived from key) instead of key
Files: to-drizzle.ts, package.json. Utility that uses field metadata.
feat(schema): update epicenter app to use TablesWithMetadata
- WorkspaceSchema now accepts TablesSchema | TablesWithMetadata
- Export new types from package index
- Update app to create proper TableDefinition with metadata
Files: App files that consume the new types.
Plan waves before coding
Implement one wave
Verify the wave
bun run tsc --noEmitCommit the wave
Repeat for next wave
| Scenario | Waves? | Why |
|---|---|---|
| Single file bugfix | No | One change, one commit |
| Add new type + factory | Maybe | Could be 1-2 waves |
| Refactor across 5+ files | Yes | Need logical grouping |
| Breaking API change | Yes | Types → API → Consumers |
| Add dependency + use it | Yes | Infra wave then usage wave |
refactor: update schema system
- Add new types
- Update factories
- Change contracts
- Add slugify
- Update app
Problem: One monolithic commit. Can't bisect, can't revert partially, no story.
feat: add IconDefinition type
feat: add CoverDefinition type
feat: add FieldMetadata type
feat: update IdFieldSchema
feat: update TextFieldSchema
...
Problem: Too granular. 20 commits for one logical change. Noise.
Wave 1: Update app to use new types ❌
Wave 2: Add the types ❌
Problem: Wave 1 won't compile. Bottom-up, not top-down.
When deciding wave order, ask: "What does this file import?"
types.ts → imports nothing (foundation)
factories.ts → imports types.ts
contract.ts → imports types.ts
converters.ts → imports types.ts, may add deps
app/ → imports everything above
Files that import nothing come first. Files that import everything come last.
For multi-wave work:
# Create feature branch
git checkout -b feat/my-feature
# Wave 1
# ... make changes ...
git add <files> && git commit -m "feat(scope): wave 1 description"
# Wave 2
# ... make changes ...
git add <files> && git commit -m "feat(scope): wave 2 description"
# ... continue waves ...
# When done, all waves are individual commits on the branch
# PR shows clean history of how the feature evolved
Before starting:
For each wave:
After all waves:
Weekly Installs
49
Repository
GitHub Stars
4.3K
First Seen
Jan 28, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode46
codex46
gemini-cli46
cursor45
claude-code44
github-copilot44
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
127,000 周安装