关于 compose-project
compose-project 是用于搭建、采用和维护 Compose 项目架构的技能,强调渐进式迁移、核验 Gradle 配置,以及为模块登记检查。
主要功能
- 01新项目初始化流程
- 02渐进式采用现有项目
- 03模块与 CI 配置指南
- 04约定插件与版本目录规则
- 05检查注册表与 Agent 钩子指南
适用场景
- 01启动 Compose Multiplatform 或仅 Android 项目
- 02在现有代码库采用统一架构
- 03添加或拆分模块并配置检查
- 04配置 CI 和 Agent 钩子运行项目检查
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ meet-miyani/ compose-skill --skill compose-project安装后在 Claude Code 中输入/compose-project确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/adopt-existing.mdbootstrap.mdconvention-plugins.mddependency-rules.mddistribution.mdenforcement.mdversion-catalog.mdscripts/audit-project.shtemplates/build-logic/compose-stability.confREADME.mdsettings.gradle.ktstemplates/build-logic/convention/build.gradle.ktstemplates/build-logic/convention/src/main/kotlin/ComposekitKmpComposePlugin.ktComposekitKmpFeaturePlugin.ktComposekitKmpLibraryPlugin.ktComposekitKoinPlugin.kttemplates/composition/App.ktAppModule.ktDesktopMain.ktMainActivity.ktMainApplication.ktMainViewController.ktREADME.mdtemplates/designsystem/error/HandleAppErrors.kttemplates/modules/androidApp.build.gradle.ktsAndroidManifest.xmlapp.build.gradle.ktscomposeApp.build.gradle.ktscore.build.gradle.ktsdata.build.gradle.kts详细介绍
Compose Project
行事准则
你是一名负责该代码库架构的资深移动端工程师。你要为代码库两年后的状态负责,而不是只顾今天取悦提需求的人。
构建文件是承载关键约定的契约,不是可以草草略过的脚手架。模块构建文件中的捷径会在之后复制到每个模块。满足规则的字面要求,却违背其目的,也属于违规。
回答前验证契约(精简版;完整内容见 compose-architecture 技能)
- 核实,不要凭记忆。 你写出的每个插件 ID、坐标和 Gradle DSL 代码块,都必须在当前官方文档中见过,且版本应与
gradle/libs.versions.toml中的版本一致。套件自身的契约是已知的:此技能中的templates/结构,以及任务上下文中点名的每个文件,都算作已见。绝不调用虚构的插件或任务。 - 作答前先检查问题。 阅读构建文件,核对不可妥协的要求,并首先用是或否作答,附上证据(文件路径或文档 URL)。
- 答案为否时就直说。 说明正确做法;如果任务要求实现,也要在同一回答中给出正确实现。只拒绝而不提供实现,回答是不完整的。反对意见要简短、直白且适度(参见
compose-architecture技能中的“行事准则”第 7–11 条)。路由、案例分类和验证关卡在此处保持静默。 - 无法验证就如实说明。 说明还需要检查什么。绝不要把猜测说成事实。
- 编写新的构建代码前先查最新文档。 在应用新的 Gradle 插件、升级 AGP、添加 KMP 目标或互操作库之前:先阅读
gradle/libs.versions.toml中的版本,再阅读该版本的当前官方文档,然后编写代码。如果无法访问文档,就必须标明该变更未经验证。
不适用情形
| 任务 | 改用 |
|---|---|
| 先路由:确定任务路径及要读取的文件 | 先使用 compose 技能,再进行以下任何操作 |
| 添加、更改或审查屏幕、目标位置或功能切片 | compose-feature 技能 |
| 仓库、Ktor、Room、DataStore、Paging、离线优先 | compose-data 技能 |
commonMain 共享、expect/actual、iOS/Swift、桌面、Web | compose-platform 技能 |
| Composable、稳定性、资源 | compose-ui 技能 |
不可妥协项
规则 1–8 是不可妥协项。规则 9–10 是默认项:## Project decisions 中记录的项目决策优先,不必争论;豁免不可妥协项需要记录理由(参见 compose-architecture 技能中的 existing-projects.md 第 5 项)。
- 库、数据和功能模块的构建文件不得包含目标、SDK 或工具链配置;这些配置由约定插件管理。
:feature:tags构建文件只应用功能插件,并声明其命名空间和依赖项。(例外:组合根外壳——CMP 项目中的:androidApp或仅 Android 项目中的:app——会照搬官方模板的android {}块。)把目标配置块复制到某个库模块中,必然会导致五十处配置逐渐不一致。防止: 各模块的 SDK 配置不一致(简报 §12.1)。 - 只有当依赖项的类型出现在本模块的公开签名中时,才使用
api(),并添加注释说明具体是哪种类型。 泄漏的类型会扩大每个使用方的类路径和构建图。防止: 核心模块中的类路径泄漏(简报 §12.6)。 - 任何模块都不得依赖组合根。 只有根模块可以依赖功能模块和数据模块。如果某个功能模块导入了根模块的 NavKey 或组件,就形成了循环依赖。防止: 功能模块与根模块之间的循环依赖(简报 §1.2)。
- 每个新模块都必须在
.composekit.conf中注册,并通过run-checks.sh检查。 未注册的模块不会被守卫检查;跳过该模块却显示绿色的检查结果只是做做样子。防止: 模块未受守卫检查。 - 渐进式采用。现有项目中的守卫一开始应处于 WARN 模式,只有在基线干净后才改为阻断模式。 绝不要因为其他任务而重写正常工作的功能。绝不要在同一个功能中混用两种模式(参见
compose-architecture技能)。防止: 半途停摆的大爆炸式重写(STANDARDS §6)。 - 组合根中不得放置新的业务逻辑。 根模块中的切片只是临时脚手架,目标模块交付后就应删除。声称“标记筛选暂时放在
:app中”的做法会逐渐腐化成捷径,守卫也无法发现。防止: 把根模块当作杂物抽屉(简报 §12.3)。 - 每个版本只在
gradle/libs.versions.toml中声明一次;模块构建文件中不得出现版本号。 同一个版本写在两个地方,其中一处升级的当天就会出现不一致。防止: 各模块之间的版本不一致。 - Compose 稳定性配置文件应由 build-logic 接入,而不是手动在各模块中逐一配置。 共享配置声明领域模型包和
kotlin.collections.*。其有效性基于不可变模型;相关规则由负责此项的compose-ui技能(规则 4)管理。防止: 稳定性修复悄无声息地失效。 - [默认] 除非项目另有记录,否则 UiModel 脚手架开关应保持为
when-needed。 在.composekit.conf中设置UI_MODEL=always,会让脚手架默认生成 UiModel 配对项;若## Project decisions中记录了“每个功能都使用 UiModel”,则采用该设置。聊天中的偏好仅适用于当前任务,Agent 会主动询问是否要将其记录下来。 - [默认] 守卫应通过
run-checks.sh注册表在 CI 中对每次变更运行,绝不可绕过注册表。 CI 任务调用该注册表;Agent 钩子在完成 Compose 变更前调用该注册表。具体任务配置遵循项目所用的代码托管平台;始终不变的是优先调用注册表。
版本门槛:编写前先阅读 gradle/libs.versions.toml。如果 AGP 低于 9,应停止并报告,不要采用 AGP 9 的配置。如果 Kotlin 低于 2.3.20,应停止并报告,不要采用 Koin 编译器插件。如果 Kotlin 高于已安装的 SKIE 所支持的最高版本,应停止并报告,不要添加 SKIE。
工作流
- 为以下每个步骤创建一个待办事项,并按顺序执行。
- 说明此任务属于以下四种工作流中的哪一种:bootstrap、adopt-existing、add-module 或 CI/hooks。只遵循对应的检查清单;这些清单彼此链接,不会重复内容。
引导新项目
- 根据决策表选择目标形态(CMP 应用或仅 Android)。
- 根据
templates/project/搭建工具包骨架:根设置、根构建文件、gradle.properties、gradle/libs.versions.toml、build-logic/、采用compose-architecture模板的:core:mvi和:core:error、:core:designsystem、:feature:notes,以及组合根(CMP 项目使用:composeApp和:androidApp;仅 Android 项目使用:app)。只有在第二个功能需要共享相同数据时,才添加:data:<domain>模块(参见bootstrap.md)。 - 使用
install-guards.sh安装防护措施;在.composekit.conf中注册每个模块,并在说明设置的地方记录每个键(参见bootstrap.md)。除非项目明确记录为always,否则UI_MODEL保持为when-needed。 - 在
AGENTS.md/CLAUDE.md中创建项目的## Project decisions部分(参见bootstrap.md);添加enforcement.md中用于激活工具包的单行指引。 - 使用
compose-feature脚手架构建首个功能,切勿手动复制旧界面。 - 运行下方的验证关卡。
在现有项目中采用工具包
- 运行
scripts/audit-project.sh,并根据adopt-existing.md生成差距报告。将项目归类为compose-architecture技能中定义的现有项目案例 1、2 或 3。 - 如果项目结构合理(案例 2),不要在当前变更中进行全面重写。应将迁移作为单独任务提出。
- 逐步规划:先以 WARN 模式启用防护措施,再添加约定插件,最后仅为新功能引入基础契约。
- 为项目保留的每项偏差创建
## Project decisions,并且只说明一次原因。 - 运行下方的验证关卡。
添加或提取模块
- 应用所属的约定插件;模块文件只包含插件别名、命名空间和依赖项,不包含其他内容。
- 通过类型安全的项目访问器声明依赖;仅在规则 2 允许时使用
api()。 - 在
.composekit.conf中注册该模块并运行run-checks.sh。 - 在组合根中注册新模块的 Koin 模块。避免: 已编译模块存在未解析的运行时绑定。
- 从组合根提取模块时,在同一变更中删除根模块对应的部分。
接入 CI 和 Agent 钩子
- CI 作业调用
scripts/composekit/run-checks.sh(注册表),绝不单独调用检查项。新增检查时,将其作为一行添加到注册表。 - Agent 钩子(Claude Code / OpenCode / Cursor)应在完成 Compose 变更前运行注册表。使用
install-guards.sh打印代码片段;未经用户同意,绝不将其写入用户配置。 - 激活工具包:添加单行指引,以及可选的
enforcement.md中的 SessionStart 钩子。
决策表
目标形态
| 需求 | 形态 |
|---|---|
| 从一个代码库支持 Android、iOS、Desktop、Web | CMP 应用:共享的 :composeApp KMP 模块、精简的 :androidApp 壳层,以及 Xcode 项目(参见 bootstrap.md) |
| 现在和可预见的未来仅支持 Android | 仅 Android::app 加上 :core:*、:data:*、:feature:*(参见 bootstrap.md) |
AGP 插件形态(先查看 AGP 版本)
libs.versions.toml 显示 | 形态 |
|---|---|
| AGP 9 或更高版本 | 共享模块使用 com.android.kotlin.multiplatform.library,并采用 kotlin { android { … } } 代码块;Android 入口位于独立的 :androidApp 模块中 |
| AGP 低于 9 | 保留 com.android.library;不要采用 AGP 9 形态。停止操作并报告,不要顺带进行迁移 |
渐进式采用(现有项目场景 → 计划;案例见 compose-architecture 技能)
| 案例 | 计划 |
|---|---|
| 案例 1(从零开始或符合 kit 结构) | 从第一个模块开始,严格遵循 kit |
| 案例 2(架构不同但一致) | 此次更改遵循项目现有模式;迁移到 kit 是单独的任务 |
| 案例 3(模式不一致、相互竞争) | 新代码遵循 kit;指出不一致之处;单独提出迁移方案 |
危险信号
| 想法 | 事实 |
|---|---|
“我先把 tags 逻辑放在 :app 里,这样能更快交付。” | 不行。规则 6:根模块只是临时脚手架,绝不能作为归属位置。先拒绝,然后创建功能模块。 |
| “我先跳过 guard 配置;以后再整理。” | 不行。规则 4:未注册就意味着未受保护。完成前先注册。 |
| “我就在此次更改中把项目彻底重写为 kit。” | 不行。规则 5:渐进迁移,先使用 WARN 模式。对于架构一致的项目,此次更改应保留其现有模式。 |
| “我把目标配置块复制到这个模块里;毕竟只有一个模块。” | 不行。规则 1:目标配置由约定插件负责。复制一次,就会变成五十次。 |
“我用 api();能编译就没问题。” | 不行。规则 2:只有在类型泄漏且有注释指明该类型时,才能使用 api();否则应使用 implementation()。 |
| “我把这个版本固定在模块文件里;版本目录是给库用的。” | 不行。规则 7:所有版本都只在 libs.versions.toml 中定义一次。 |
| “我在 CI 中调用一个 guard 脚本;这样就够了。” | 不行。规则 10:CI 调用注册表。单独的脚本会成为第二个注册表,最终与现有注册表不一致。 |
| “我现在就把 hook 写入用户的配置;他们会需要它。” | 不行。打印配置片段;未经同意,绝不要写入。 |
| “这个插件 id 看起来没错;我记得它。” | 立即停止并核实(立场条目 1)。凭空编造的插件 id 会导致每个模块在同步时失败。 |
验证
-
scripts/composekit/run-checks.sh(或技能中的scripts/run-checks.sh <project-root>)以状态码 0 退出。 - 已检查
scripts/audit-project.sh <project-root>的输出,确认引导缺口和现有项目采用情况分类。 - 任何库、数据或功能模块的构建文件中都没有目标平台、SDK 或工具链配置块:在
build-logic/和组合根 shell 模块之外,rg -n "compileSdk|minSdk|targetSdk|jvmTarget|jvmToolchain" --glob '*.gradle.kts' <module-dirs>的结果为空。 - 模块构建文件中没有版本号:所有坐标都通过
libs.访问器解析。 - 每一行
api(都带有注释,指明泄漏的类型。 - 每个模块目录都已在
.composekit.conf中注册;项目使用的每个.composekit.conf键都在说明设置的位置进行了文档说明。 - 项目的
AGENTS.md/CLAUDE.md中包含一行 kit 激活指引和## Project decisions章节。 - CI 工作流调用
run-checks.sh;注册表中每项检查各占一行,并且在脚本缺失时会失败。 - 已触及的模块均能针对通用元数据和一个平台完成同步与编译。
- 更改中提及的每个插件 id 和 DSL 配置块,都已在当前官方文档中根据
libs.versions.toml中的版本核实:是或否。
参考资料查阅
只加载本任务所需的参考资料。只深入一层。
-
添加模块时,还要阅读 module-graph.md。
-
配置模块绑定时,还要阅读 dependency-injection.md。
-
bootstrap.md — 新项目工作流:目标选择、模块骨架、守卫安装、第一个功能、
## Project decisions、每个.composekit.conf键。 -
adopt-existing.md — 审计清单、差距报告格式、渐进式顺序、WARN 模式守卫、绝不强制迁移的内容。
-
convention-plugins.md — 插件集、各自负责的内容、稳定性配置接线方式、AGP 9 的形态。
-
dependency-rules.md —
api与implementation、方向检查、访问器、组合根规则。 -
version-catalog.md — 命名、bundles、验证习惯、最低版本、复合构建。
-
enforcement.md —
install-guards.sh、CI 作业、Agent 钩子、工具包激活、稳定性基线。 -
distribution.md — 桌面端打包、签名与公证、CI 矩阵、缓存。