用途
该技能把空文件夹或仅有文档的文件夹转换为符合统一规范的 Expo 应用。生成结果是仓库根目录下的单一应用,使用 Expo Router,路由位于 src/app/,并包含全部根级配置、质量门禁脚本、依赖策略、app.config.ts(含 iOS 与 Android 的三种变体)、起始路由与页面,以及占位图标素材。所有内容来自已安装并端到端运行过的模板;生成后第一天所有门禁均为绿色,并由检查器验证骨架维持该状态。多包形式在 references/project-layout.md 的“When multi-package”一节中有说明。
主要能力
- 使用 scaffold-app.mjs 生成骨架,不手工编写配置。每个模板都经过 tsc、ESLint、Prettier、Jest、knip、expo config 的安装与运行验证。
- 在 git 仓库的根目录执行引导,保留既有文件夹原位不动。npm install 仅在仓库中安装 git hooks。既有文件夹(文档、设计导出)会被写入 .prettierignore、ESLint 的 PRE_EXISTING 和根 tsconfig 的 exclude,不移动也不重新格式化。
- 对一个身份生成三种变体,同时覆盖 iOS 和 Android。生产 id 为开发者在 App Store Connect 与 Google Play 注册的反向 DNS id,需同时满足 iOS bundle id 与 Android package 规则(小写、点分隔、每段以字母开头、不得为 Kotlin 或 Java 关键字);development 与 preview 分别追加 .dev 和 .preview,使三者可并排安装。app.config.ts 据此设置 ios.bundleIdentifier、android.package 与自适应图标,且引用的每个图标均存在。com.example id 仅允许使用到首次商店构建之前。
- 严格按依赖策略安装版本,并提交锁文件。版本说明符来自 contracts/dependency-policy.json:Expo SDK 模块保留 npx expo install 写入的 ~,其余(含 React 与 React Native)精确锁定到策略版本。overrides 将十个 @typescript-eslint/* 包固定到 typescript-eslint 的版本,避免出现第二份副本导致 ESLint 报 Cannot redefine plugin。
- 只安装骨架使用的依赖。knip 会对未使用依赖报错;后续包随首次引用它的技能一起引入,走 dependency-management。
- 完整保留 .npmrc 策略;带日期的 exclude 仅在其 expires= 日期前有效。
- 安装脚本逐包审批:strict-allow-scripts 会在遇到未审查脚本时停止安装,审批绑定到已审查的版本,禁止使用 --dangerously-allow-all-scripts。
- 不为让 npm run verify 通过而为缺失的门禁脚本造假。i18n:verify 等待 i18n-strings-and-catalogs,应以 pending 状态报告。
- 为智能体提供统一契约:AGENTS.md 与 CLAUDE.md = @AGENTS.md;嵌套在更大仓库中的应用可从该仓库获取(agentInstructions: parent-repo,仅限该仓库顶层)。
- 检查器同时保护骨架内由其他技能负责的部分(规范脚本、Prettier 与 TypeScript 选项、门禁接线、应用配置、未填充模板占位符),防止引导结果发生漂移。
使用方式
- 工具链:阅读 references/toolchain-setup.md 并执行其“安装前检查”(Node 26.4.0、npm 11.17.0、git;Xcode 与 Android SDK 仅从首次原生构建开始需要)。向开发者询问缺失的人工步骤。
- 身份:向开发者索取显示名称与商店 id(com.<org>.<app>);slug 与 scheme 默认由名称派生。阅读 references/project-layout.md;运行 git status;仅在文件夹尚非仓库时 git init;绝不丢弃非自己创建的改动。
- 只做计划不写入:node ${CLAUDE_SKILL_DIR}/scripts/scaffold-app.mjs . --app-id com.acme.tasks --name "Tasks"。阅读计划:将被忽略的既有条目、合并项、每个冲突与遗留物。
- 按 references/root-files.md(“Merging an existing repo”)解决冲突:将文件编辑为模板内容,或当文件仅为旧草稿时用 --replace <file> 重跑;若某条设置规则将被丢弃,询问开发者。移动应用仍需要的部分后删除每个遗留物(如 app.json 的值移入 app.config.ts)。
- 写入:同一命令加 --write。再跑一次试运行:必须输出 0 new、0 merged 和 RESULT: PASS。
- 按 references/first-install-and-verify.md(“Install and approve install scripts”)安装:npm install,逐个阅读待处理脚本,逐包审批,直到 npm approve-scripts --allow-scripts-pending 无输出;npm ls react react-native @typescript-eslint/eslint-plugin 各显示一个版本。
- 门禁:逐项运行,且不先做格式化步骤(export:audit 是唯一经 Metro 打包的门禁,随后 audit:licenses 读取其 source maps),并验证三种变体在两个平台上的 Expo 配置。修复失败原因所在的文件,绝不放松门禁。
- 检查:node ${CLAUDE_SKILL_DIR}/scripts/check-skeleton.mjs .;修复每条 FAIL 行并重跑至 RESULT: PASS。
- 按“The first commit”提交(精确路径、Gate-Change: initial quality gates、经过 hooks)。
- 按 examples/bootstrap-report.md 的格式报告:结果、最多一项请求、需要查看的内容、用平实语言描述的待定门禁,以及各项数字。按 project-layout.md 的“What the bootstrap leaves out”顺序交接。
完成定义
scaffold-app.mjs 试运行输出 0 new、0 merged 与 RESULT: PASS(无冲突、无遗留);既有文件夹未被改动且被 Prettier、ESLint 和根 tsconfig 忽略;package-lock.json 已提交;npm approve-scripts --allow-scripts-pending 无包;npm ls react react-native @typescript-eslint/eslint-plugin 各一个版本;check:fast、lint、typecheck、test:coverage(达到阈值)、check:unused、export:audit(iOS 与 Android 包)、audit:licenses、check-quality-gates.ts 与 check:deps 全部通过;npx lefthook validate 输出 All good;npx expo config --type public 对 development、preview 与 production 均可解析(各自带相同 APP_VARIANT 与 EXPO_PUBLIC_APP_VARIANT),三个 id、名称、scheme 与图标在 iOS 与 Android 上齐备,仅 APP_VARIANT=production 会失败;首次提交经 hooks 并带 Gate-Change: trailer,未经开发者不推送;报告列出待定门禁(i18n:verify、i18n-strings-and-catalogs)及后续技能顺序;check-skeleton.mjs 输出 RESULT: PASS(com.example id 会多一行 NOTE ...[app-id],无 SKIP 行)。
适用场景
- 从零初始化新的 Expo 应用项目。
- 在已有仓库中引导 Expo 应用,并保留原有文档与设计资源。
- 为团队项目统一根级配置、质量门禁与依赖策略。
限制与约束
- 不在第一天安装整套技术栈;每个包随其首个使用者引入。
- 不得手工编写或裁剪 create-expo-app 演示项目;遗留物会残存且配置缺失门禁。
- 遇到 ETARGET 安装错误时不删除 package-lock.json 或放松 .npmrc,应使用 dependency-management 的带日期例外流程。
- 不提交 ios/ 或 android/,它们由 npx expo prebuild 生成。
- 人工步骤(Xcode 及其许可、Android SDK 许可、sudo、商店账号、签名凭证、在智能体中信任该文件夹)需停止并询问开发者,绝不读取或创建签名密钥。
- 不使用 sudo xcode-select 选择 Xcode,应使用 DEVELOPER_DIR,并明确命名每个模拟器、仿真器与设备。
- 不使用 git add -A,以免混入开发者未提交的工作。