svharness 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +531 -0
- package/bin/cli.js +3 -0
- package/dist/adapters/_frontmatter.js +24 -0
- package/dist/adapters/claude-code.js +12 -0
- package/dist/adapters/codechat.js +12 -0
- package/dist/adapters/cursor.js +19 -0
- package/dist/adapters/generic.js +19 -0
- package/dist/adapters/index.js +26 -0
- package/dist/adapters/qoder.js +12 -0
- package/dist/commands/apply.js +272 -0
- package/dist/commands/init.js +420 -0
- package/dist/core/agent-injector.js +192 -0
- package/dist/core/next-steps.js +91 -0
- package/dist/core/render-meta.js +81 -0
- package/dist/core/repomix-pack.js +54 -0
- package/dist/core/scaffold.js +93 -0
- package/dist/core/state.js +80 -0
- package/dist/index.js +239 -0
- package/dist/types.js +5 -0
- package/dist/utils/baseline-copy.js +591 -0
- package/dist/utils/baseline-defaults.js +106 -0
- package/dist/utils/logger.js +56 -0
- package/dist/utils/validate-args.js +132 -0
- package/dist/utils/version.js +23 -0
- package/dist/wiki/abort.js +30 -0
- package/dist/wiki/config.js +79 -0
- package/dist/wiki/defaults.js +16 -0
- package/dist/wiki/envLoader.js +78 -0
- package/dist/wiki/index.js +29 -0
- package/dist/wiki/openaiCompat.js +219 -0
- package/dist/wiki/repowikiCanonicalSections.js +67 -0
- package/dist/wiki/repowikiCheckpoint.js +106 -0
- package/dist/wiki/repowikiConfig.js +9 -0
- package/dist/wiki/repowikiGit.js +73 -0
- package/dist/wiki/repowikiIndexer.js +824 -0
- package/dist/wiki/repowikiMarkdownPost.js +123 -0
- package/dist/wiki/repowikiMetadataContent.js +64 -0
- package/dist/wiki/repowikiMetadataJson.js +15 -0
- package/dist/wiki/repowikiScanner.js +156 -0
- package/dist/wiki/repowikiStructureNav.js +286 -0
- package/dist/wiki/repowikiStructureNormalize.js +218 -0
- package/dist/wiki/wikiStructureXml.js +316 -0
- package/dist/wiki/wikiTasksWriter.js +127 -0
- package/package.json +57 -0
- package/templates/_shared/apply-skills/harness-apply-skills-main.md +91 -0
- package/templates/_shared/build-rules/harness-build-rule-agent-agnostic.md +35 -0
- package/templates/_shared/build-rules/harness-build-rule-chinese-only.md +49 -0
- package/templates/_shared/build-rules/harness-build-rule-memory-write.md +31 -0
- package/templates/_shared/build-rules/harness-build-rule-orchestrator-flow.md +25 -0
- package/templates/_shared/build-rules/harness-build-rule-skills-tasks-output.md +35 -0
- package/templates/_shared/build-rules/harness-build-rule-specs-schema.md +32 -0
- package/templates/_shared/build-rules/harness-build-rule-user-interaction.md +63 -0
- package/templates/_shared/build-skills/harness-build-skill-knowledge-builder.md +120 -0
- package/templates/_shared/build-skills/harness-build-skill-orchestrator.md +87 -0
- package/templates/_shared/build-skills/harness-build-skill-spec-builder.md +85 -0
- package/templates/_shared/build-skills/harness-build-skill-wiki-writer.md +77 -0
- package/templates/_shared/meta/AGENTS.md.ejs +53 -0
- package/templates/_shared/meta/CHANGELOG.md.ejs +15 -0
- package/templates/_shared/meta/README.md.ejs +51 -0
- package/templates/_shared/meta/VERSION.ejs +1 -0
- package/templates/_shared/meta/harness.yaml.ejs +52 -0
- package/templates/_shared/skeleton/agent-env/memory/categories/.gitkeep +1 -0
- package/templates/_shared/skeleton/agent-env/memory/inbox/.gitkeep +1 -0
- package/templates/_shared/skeleton/agent-env/skills/.gitkeep +1 -0
- package/templates/_shared/skeleton/agent-env/tools/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/baseline/code/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/baseline/repomix/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/baseline/wiki/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/raw/.gitkeep +1 -0
- package/templates/_shared/skeleton/assets/requirements/.gitkeep +1 -0
- package/templates/_shared/skeleton/commands/install/.gitkeep +1 -0
- package/templates/_shared/skeleton/commands/update/.gitkeep +1 -0
- package/templates/_shared/skeleton/specs/behavior/schema.json +39 -0
- package/templates/_shared/skeleton/specs/interfaces/schema.json +38 -0
- package/templates/_shared/skeleton/specs/signals/schema.json +37 -0
- package/templates/_shared/skeleton/specs/ui/schema.json +44 -0
- package/templates/_shared/skeleton/tasks/templates/.gitkeep +0 -0
- package/templates/android-compose/skeleton/agent-env/rules/harness-compose-mandatory.mdc +49 -0
- package/templates/android-compose/skeleton/agent-env/rules/harness-coroutines-scope.mdc +52 -0
- package/templates/android-compose/skeleton/agent-env/rules/harness-hilt-injection.mdc +47 -0
- package/templates/android-compose/skeleton/agent-env/rules/harness-mvi-layering.mdc +58 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/SKILL.md +260 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/gradle-module-patterns.md +66 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/implementation-checklist.md +45 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/references/udf-data-flow.md +80 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/SKILL.md +79 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/references/interact.md +83 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-android-cli/references/journeys.md +97 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/SKILL.md +162 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/canonical-sources.md +116 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/diagnostics.md +182 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/report-template.md +135 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/scoring.md +277 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/references/search-playbook.md +303 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-audit/scripts/compose-reports.init.gradle +58 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-state/SKILL.md +196 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/SKILL.md +192 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/composable-api-guide.md +123 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/performance-recipes.md +97 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-compose-ui/references/state-patterns.md +93 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-kotlin-coroutines/SKILL.md +167 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/SKILL.md +45 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/CONFIGURATION.md +44 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/KEEP-RULES-IMPACT-HIERARCHY.md +83 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/REDUNDANT-RULES.md +222 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/REFLECTION-GUIDE.md +139 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/android/topic/performance/app-optimization/enable-app-optimization.md +176 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-r8-analyzer/references/android/training/testing/other-components/ui-automator.md +312 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/SKILL.md +87 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/analysis-of-the-project-and-layout.md +42 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/android/develop/ui/compose/designsystems/migrate-xml-theme-to-compose.md +168 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/android/develop/ui/compose/setup-compose-dependencies-and-compiler.md +183 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/identify-optimal-xml-candidate.md +31 -0
- package/templates/android-compose/skeleton/agent-env/skills/harness-xml-to-compose/references/xml-layout-migration.md +86 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-aidl-thread.md +29 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-lifecycle-awareness.md +32 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-mvc-layering.md +32 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-view-binding.md +33 -0
- package/templates/android-xml/skeleton/agent-env/rules/seed-xml-styling.md +27 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-cmake-explicit-sources.md +31 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-header-guards.md +34 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-include-layering.md +39 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-no-cyclic-deps.md +29 -0
- package/templates/cpp/skeleton/agent-env/rules/seed-raii.md +30 -0
- package/templates/python/skeleton/agent-env/rules/seed-context-managers.md +60 -0
- package/templates/python/skeleton/agent-env/rules/seed-docstrings.md +48 -0
- package/templates/python/skeleton/agent-env/rules/seed-import-order.md +49 -0
- package/templates/python/skeleton/agent-env/rules/seed-pep8-naming.md +45 -0
- package/templates/python/skeleton/agent-env/rules/seed-type-annotations.md +43 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-controlled-component.md +43 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-effect-cleanup.md +43 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-hook-rules.md +42 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-key-stability.md +39 -0
- package/templates/web-react/skeleton/agent-env/rules/seed-no-props-drilling.md +43 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Hilt 依赖注入约束,确保 ViewModel 和 UseCase 的正确注入方式
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hilt 依赖注入约束
|
|
7
|
+
|
|
8
|
+
## ViewModel 注入
|
|
9
|
+
|
|
10
|
+
### ViewModel 必须使用 @HiltViewModel + @Inject constructor
|
|
11
|
+
- 正例:`@HiltViewModel class MyViewModel @Inject constructor(private val useCase: MyUseCase) : BaseViewModel<...>()`
|
|
12
|
+
- 反例:手动创建 `ViewModelProvider.Factory` 来实例化可由 Hilt 管理的 ViewModel
|
|
13
|
+
- 检测:grep ViewModel 类声明,缺少 `@HiltViewModel` 或 `@Inject constructor`
|
|
14
|
+
|
|
15
|
+
### Compose 容器层必须使用 hiltViewModel() 获取 ViewModel
|
|
16
|
+
- 正例:`val viewModel: MyViewModel = hiltViewModel()`
|
|
17
|
+
- 反例:`viewModel(factory = MyViewModel.Factory)` — 手动 Factory(除非非 Activity 场景)
|
|
18
|
+
- 检测:grep `viewModel(` 后跟 `factory =` 且非非 Activity 场景
|
|
19
|
+
|
|
20
|
+
### 禁止为传 UseCase 创建包装接口
|
|
21
|
+
- 正例:ViewModel 构造函数直接注入 `UseCase` 接口
|
|
22
|
+
- 反例:创建 `FeatureDependencies` / `XxxDependencies` 包装接口来传 UseCase
|
|
23
|
+
- 检测:grep `Dependencies` 接口/类名出现于 feature 模块
|
|
24
|
+
|
|
25
|
+
## Application 配置
|
|
26
|
+
|
|
27
|
+
### Application 必须使用 @HiltAndroidApp 且 Manifest 配置 android:name
|
|
28
|
+
- 正例:`@HiltAndroidApp class MyApp : Application()` + Manifest `<application android:name=".MyApp">`
|
|
29
|
+
- 反例:Application 未标注 `@HiltAndroidApp` 或 Manifest 缺少 `android:name`
|
|
30
|
+
- 检测:grep Application 类缺少 `@HiltAndroidApp`,或 Manifest `<application>` 缺少 `android:name`
|
|
31
|
+
|
|
32
|
+
### Activity 必须使用 @AndroidEntryPoint
|
|
33
|
+
- 正例:`@AndroidEntryPoint class MainActivity : ComponentActivity()`
|
|
34
|
+
- 反例:Activity 未标注 `@AndroidEntryPoint`
|
|
35
|
+
- 检测:grep 继承 `ComponentActivity` / `AppCompatActivity` 的类缺少 `@AndroidEntryPoint`
|
|
36
|
+
|
|
37
|
+
## 依赖方向
|
|
38
|
+
|
|
39
|
+
### 禁止从 Activity 向 Composable 层层透传 UseCase
|
|
40
|
+
- 正例:Compose 通过 `hiltViewModel()` 获取 ViewModel,只传 UI 回调和页面参数
|
|
41
|
+
- 反例:`MyScreen(useCase = myUseCase)` — 透传 UseCase 到 Composable
|
|
42
|
+
- 检测:grep Composable 函数参数中出现 `UseCase` 类型
|
|
43
|
+
|
|
44
|
+
### Repository 接口定义在 Domain 层,实现在 Foundation 层
|
|
45
|
+
- 正例:Domain 模块定义 `interface XxxRepository`,Foundation 模块提供 `XxxRepositoryImpl`
|
|
46
|
+
- 反例:Repository 接口和实现都在 Foundation 层 — 违反依赖倒置
|
|
47
|
+
- 检测:grep foundation 模块中定义的 Repository 接口(非 impl 类)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: MVI 平台化架构分层铁律,约束层级依赖与数据流方向
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MVI 分层铁律
|
|
7
|
+
|
|
8
|
+
## 层级依赖规则
|
|
9
|
+
|
|
10
|
+
### 依赖方向严格向内(向下),禁止反向依赖
|
|
11
|
+
- Feature → Domain → Foundation → 架构层(MVI Core)
|
|
12
|
+
- 正例:Feature 依赖 Domain,Domain 依赖 Foundation
|
|
13
|
+
- 反例:Foundation 依赖 Domain、Domain 依赖 Feature — 反向依赖
|
|
14
|
+
- 检测:grep 各模块 `build.gradle*` 的 `implementation` / `api` 声明,Foundation 不应出现 Domain/Feature 依赖
|
|
15
|
+
|
|
16
|
+
### Feature 层禁止直接依赖 Foundation 层
|
|
17
|
+
- 正例:Feature → Domain UseCase → Foundation Repository
|
|
18
|
+
- 反例:Feature 直接调用 Repository / Room / Retrofit
|
|
19
|
+
- 检测:grep feature 模块源码中出现 `Repository` / `Dao` / `ApiService` 直接引用
|
|
20
|
+
|
|
21
|
+
### Domain 层禁止持有 View/Context 引用
|
|
22
|
+
- 正例:UseCase 只注入 Repository 接口
|
|
23
|
+
- 反例:UseCase 构造函数接收 `Context` 或 `View`
|
|
24
|
+
- 检测:grep domain 模块源码中 `import android.view` / `import android.content.Context`
|
|
25
|
+
|
|
26
|
+
## 模块规则
|
|
27
|
+
|
|
28
|
+
### Foundation 必须独立仓库
|
|
29
|
+
- 正例:foundation-repo 独立 Git 仓库,发布为 AAR/Maven 坐标
|
|
30
|
+
- 反例:foundation 与 domain/feature 共仓
|
|
31
|
+
- 检测:验证 foundation 目录是否与 domain/feature 在同一 Git 仓库
|
|
32
|
+
|
|
33
|
+
### Domain 与 Feature 必须是不同 Gradle module
|
|
34
|
+
- 正例:`include(":domain")` 和 `include(":feature")` 分开声明
|
|
35
|
+
- 反例:domain 和 feature 在同一个 module 内
|
|
36
|
+
- 检测:grep `settings.gradle*` 确认 domain/feature 为独立 module
|
|
37
|
+
|
|
38
|
+
### 跨仓依赖通过 AAR/Maven 坐标,禁止跨仓 project 引用
|
|
39
|
+
- 正例:`implementation("com.xxx:foundation:1.0.0")`
|
|
40
|
+
- 反例:`implementation(project(":foundation"))` — 跨仓不允许
|
|
41
|
+
- 检测:grep 跨仓模块的 `build.gradle*` 出现 `project(":")` 引用其他仓库模块
|
|
42
|
+
|
|
43
|
+
## MVI 协议规则
|
|
44
|
+
|
|
45
|
+
### Reducer 是纯函数,只做 state.copy()
|
|
46
|
+
- 正例:`is Event.Loaded -> state.copy(isLoading = false, data = event.data)`
|
|
47
|
+
- 反例:`is Event.Loaded -> { repository.cache(event.data); state.copy(...) }` — Reducer 内有副作用
|
|
48
|
+
- 检测:grep Reducer lambda 内出现 `repository.` / `useCase.` / `viewModelScope` 调用
|
|
49
|
+
|
|
50
|
+
### EffectHandler 不修改 State,只发射 Effect
|
|
51
|
+
- 正例:`is Event.Error -> emitEffect(Effect.ShowToast(event.message))`
|
|
52
|
+
- 反例:EffectHandler 内调用 `dispatch(Event)` 或修改 `_uiState`
|
|
53
|
+
- 检测:grep EffectHandler lambda 内出现 `dispatch(` / `state.copy(`
|
|
54
|
+
|
|
55
|
+
### State 为不可变 data class,更新只用 .copy()
|
|
56
|
+
- 正例:`data class UiState(val items: List<Item> = emptyList())`
|
|
57
|
+
- 反例:`data class UiState(var items: MutableList<Item>)` — var + MutableList 可变
|
|
58
|
+
- 检测:grep State data class 中出现 `var` 属性或 `MutableList` / `MutableMap` 类型
|
package/templates/android-compose/skeleton/agent-env/skills/harness-android-architecture/SKILL.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: harness-android-architecture
|
|
3
|
+
description: 设计或审查基于 MVI 的 Android 平台化架构时使用。覆盖四层职责、依赖规则、UDF 数据流、ViewModel/UseCase/Repository 接线模式和实现检查清单。刚性约束由 rules 加载,本技能聚焦实现指引与模板代码。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MVI 平台化架构 — 实现指引
|
|
7
|
+
|
|
8
|
+
## 概述
|
|
9
|
+
|
|
10
|
+
本项目采用 **MVI + 平台化分层** 架构,遵循严格**单向数据流(UDF)**。层间依赖严格向内(向下),禁止反向依赖。
|
|
11
|
+
|
|
12
|
+
> **刚性约束已由 rules 加载,本技能不再重复。** 详见:
|
|
13
|
+
> - `harness-mvi-layering` — 层级依赖、模块规则、MVI 协议约束
|
|
14
|
+
> - `harness-hilt-injection` — Hilt 注入约束
|
|
15
|
+
> - `harness-coroutines-scope` — 协程作用域约束
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 分层模型
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
Feature 层(Compose UI / ViewModel)
|
|
23
|
+
│ 发送 Intent,收集 State/Effect
|
|
24
|
+
▼
|
|
25
|
+
Domain 层(UseCase)
|
|
26
|
+
│ 业务逻辑,通过 Registrable Callback 回传结果
|
|
27
|
+
▼
|
|
28
|
+
Foundation 层(Repository / Room / Retrofit)
|
|
29
|
+
│ 纯技术能力,不含业务状态
|
|
30
|
+
▼
|
|
31
|
+
架构层(MVI Core)
|
|
32
|
+
定义 MVI 协议,不含业务逻辑
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| 层级 | 职责 | 禁止事项 |
|
|
36
|
+
|------|------|---------|
|
|
37
|
+
| **架构层** | 定义 MVI 协议,提供 BaseViewModel/MviStore | 禁止业务 if-else,禁止持有视图引用 |
|
|
38
|
+
| **基础层** | 网络、持久化、日志等纯技术能力 | 禁止持有业务状态 |
|
|
39
|
+
| **领域层** | UseCase 封装业务逻辑,Registrable 回调异步结果 | 禁止直接依赖 Feature 层,禁止持有 View/Context |
|
|
40
|
+
| **特性层** | Compose UI 渲染 State,发送 Intent,收集 Effect | 禁止直接调用 Foundation 层 |
|
|
41
|
+
|
|
42
|
+
仓库/模块规则详见 [references/gradle-module-patterns.md](references/gradle-module-patterns.md)。
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## UDF 数据流
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
View ──Intent──> ViewModel.process()
|
|
50
|
+
│
|
|
51
|
+
▼
|
|
52
|
+
handleIntent() --> UseCase 调用
|
|
53
|
+
│ Callback
|
|
54
|
+
▼
|
|
55
|
+
dispatch(Event)
|
|
56
|
+
│
|
|
57
|
+
┌────────────────┴────────────────┐
|
|
58
|
+
▼ ▼
|
|
59
|
+
Reducer EffectHandler
|
|
60
|
+
│ │
|
|
61
|
+
新 State Effect
|
|
62
|
+
│ │
|
|
63
|
+
▼ ▼
|
|
64
|
+
StateFlow (持久) SharedFlow (一次性)
|
|
65
|
+
│ │
|
|
66
|
+
collectAsState() LaunchedEffect 收集
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
详细数据流图解见 [references/udf-data-flow.md](references/udf-data-flow.md)。
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 各层实现模板
|
|
74
|
+
|
|
75
|
+
### 1. 特性层 — ViewModel + Compose UI
|
|
76
|
+
|
|
77
|
+
```kotlin
|
|
78
|
+
@HiltViewModel
|
|
79
|
+
class BluetoothViewModel @Inject constructor(
|
|
80
|
+
private val cases: BluetoothUseCase
|
|
81
|
+
) : BaseViewModel<
|
|
82
|
+
BluetoothContract.State,
|
|
83
|
+
BluetoothContract.Event,
|
|
84
|
+
BluetoothContract.Effect,
|
|
85
|
+
BluetoothContract.Intent,
|
|
86
|
+
BluetoothContract.Callback>(useCase = cases) {
|
|
87
|
+
|
|
88
|
+
override val initialState = BluetoothContract.State()
|
|
89
|
+
|
|
90
|
+
override val useCaseCallback = object : BluetoothContract.Callback {
|
|
91
|
+
override fun onDeviceConnected(device: BluetoothDevice) {
|
|
92
|
+
dispatch(BluetoothContract.Event.Connected(device))
|
|
93
|
+
}
|
|
94
|
+
override fun onOperationFailed(reason: String) {
|
|
95
|
+
dispatch(BluetoothContract.Event.OperationFailed(reason))
|
|
96
|
+
}
|
|
97
|
+
// ... 其他回调
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
override val reducer = Reducer<BluetoothContract.State, BluetoothContract.Event> { state, event ->
|
|
101
|
+
when (event) {
|
|
102
|
+
is Event.Connected -> state.copy(isLoading = false, connectedDevice = event.device)
|
|
103
|
+
is Event.OperationFailed -> state.copy(isLoading = false)
|
|
104
|
+
else -> state
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
override val effectHandler = EffectHandler<BluetoothContract.Event, BluetoothContract.Effect> { event, emitEffect ->
|
|
109
|
+
when (event) {
|
|
110
|
+
is Event.OperationFailed -> emitEffect(Effect.ShowToast(event.reason))
|
|
111
|
+
else -> { /* 无副作用 */ }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
override fun handleIntent(intent: BluetoothContract.Intent) {
|
|
116
|
+
when (intent) {
|
|
117
|
+
is Intent.ConnectToDevice -> {
|
|
118
|
+
dispatch(Event.ConnectionStarted)
|
|
119
|
+
cases.connect(intent.deviceAddress)
|
|
120
|
+
}
|
|
121
|
+
is Intent.StartScan -> cases.startScan()
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
init { startUseCaseListening() }
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Compose 容器层 / 纯 UI 层分离:
|
|
130
|
+
|
|
131
|
+
```kotlin
|
|
132
|
+
// 容器层:持有 ViewModel,处理 Effect
|
|
133
|
+
@Composable
|
|
134
|
+
fun BluetoothScreen(viewModel: BluetoothViewModel = hiltViewModel()) {
|
|
135
|
+
val state by viewModel.state.collectAsStateWithLifecycle()
|
|
136
|
+
LaunchedEffect(Unit) {
|
|
137
|
+
viewModel.effect.collect { effect ->
|
|
138
|
+
when (effect) {
|
|
139
|
+
is Effect.ShowToast -> Toast.makeText(LocalContext.current, effect.message, Toast.LENGTH_SHORT).show()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
BluetoothContent(
|
|
144
|
+
state = state,
|
|
145
|
+
onIntent = { viewModel.process(it) }
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 纯 UI 层:无 ViewModel 依赖
|
|
150
|
+
@Composable
|
|
151
|
+
fun BluetoothContent(
|
|
152
|
+
state: BluetoothContract.State,
|
|
153
|
+
onIntent: (BluetoothContract.Intent) -> Unit
|
|
154
|
+
) { /* 纯渲染逻辑 */ }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 2. 领域层 — UseCase
|
|
158
|
+
|
|
159
|
+
```kotlin
|
|
160
|
+
interface BluetoothUseCase : Registrable<BluetoothContract.Callback> {
|
|
161
|
+
fun connect(deviceAddress: String)
|
|
162
|
+
fun startScan()
|
|
163
|
+
fun stopScan()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
class BluetoothUseCaseImpl @Inject constructor(
|
|
167
|
+
private val repository: BluetoothRepository
|
|
168
|
+
) : BluetoothUseCase {
|
|
169
|
+
private var callback: BluetoothContract.Callback? = null
|
|
170
|
+
|
|
171
|
+
override fun register(callback: BluetoothContract.Callback) {
|
|
172
|
+
this.callback = callback
|
|
173
|
+
startObserving()
|
|
174
|
+
}
|
|
175
|
+
override fun unRegister() { callback = null }
|
|
176
|
+
|
|
177
|
+
override fun connect(deviceAddress: String) {
|
|
178
|
+
repository.connect(deviceAddress) { result ->
|
|
179
|
+
result.fold(
|
|
180
|
+
onSuccess = { callback?.onDeviceConnected(it) },
|
|
181
|
+
onFailure = { callback?.onOperationFailed(it.message ?: "连接失败") }
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 3. 基础层 — Repository
|
|
189
|
+
|
|
190
|
+
```kotlin
|
|
191
|
+
// 接口定义在 Domain 层,此处只放实现
|
|
192
|
+
class BluetoothRepositoryImpl @Inject constructor(
|
|
193
|
+
private val bluetoothDao: BluetoothDao,
|
|
194
|
+
private val bluetoothApi: BluetoothApiService
|
|
195
|
+
) : BluetoothRepository {
|
|
196
|
+
override fun connect(deviceAddress: String, onResult: (Result<BluetoothDevice>) -> Unit) {
|
|
197
|
+
// 实现连接逻辑
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Room / Retrofit / 数据映射模板详见 [references/implementation-checklist.md](references/implementation-checklist.md) 的基础层部分。
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 多 UseCase 组合
|
|
207
|
+
|
|
208
|
+
ViewModel 可注入多个 UseCase,各自独立注册/注销:
|
|
209
|
+
|
|
210
|
+
```kotlin
|
|
211
|
+
@HiltViewModel
|
|
212
|
+
class DashboardViewModel @Inject constructor(
|
|
213
|
+
private val bluetoothUseCase: BluetoothUseCase,
|
|
214
|
+
private val settingsUseCase: SettingsUseCase
|
|
215
|
+
) : BaseViewModel<...>(useCase = bluetoothUseCase) {
|
|
216
|
+
init {
|
|
217
|
+
startUseCaseListening()
|
|
218
|
+
settingsUseCase.register(settingsCallback)
|
|
219
|
+
}
|
|
220
|
+
override fun onCleared() {
|
|
221
|
+
super.onCleared()
|
|
222
|
+
settingsUseCase.unRegister()
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 非 Activity 场景
|
|
230
|
+
|
|
231
|
+
只有无法使用 `@AndroidEntryPoint` + `hiltViewModel()` 的宿主(Service / 自定义 Window),才使用手动 ViewModelStore:
|
|
232
|
+
|
|
233
|
+
```kotlin
|
|
234
|
+
class CustomWindow @Inject constructor(
|
|
235
|
+
private val viewModelFactory: BluetoothViewModel.Factory
|
|
236
|
+
) {
|
|
237
|
+
private val viewModelStore = ViewModelStore()
|
|
238
|
+
private val viewModel: BluetoothViewModel by lazy {
|
|
239
|
+
ViewModelProvider(viewModelStore, viewModelFactory)[BluetoothViewModel::class.java]
|
|
240
|
+
}
|
|
241
|
+
fun onDestroy() { viewModelStore.clear() }
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## 实现检查清单
|
|
248
|
+
|
|
249
|
+
完整清单见 [references/implementation-checklist.md](references/implementation-checklist.md)。
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 必须遵守的约束规则
|
|
254
|
+
|
|
255
|
+
> rules 引用路径:`../rules/<rule-name>.mdc`
|
|
256
|
+
|
|
257
|
+
- harness-mvi-layering
|
|
258
|
+
- harness-hilt-injection
|
|
259
|
+
- harness-coroutines-scope
|
|
260
|
+
- harness-compose-mandatory
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 仓库与模块模式
|
|
2
|
+
|
|
3
|
+
## 仓库拓扑(两种合法形态)
|
|
4
|
+
|
|
5
|
+
### 形态 A:三仓分离
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
9
|
+
│ foundation-repo │ │ domain-repo │ │ feature-repo │
|
|
10
|
+
│ :foundation │◄──│ :domain │◄──│ :feature │
|
|
11
|
+
└─────────────┘ └──────────────┘ └──────────────┘
|
|
12
|
+
独立仓库 独立仓库 独立仓库
|
|
13
|
+
发布为 AAR/Maven 依赖 foundation AAR 依赖 domain AAR
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 形态 B:domain + feature 同仓(foundation 仍独立)
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
┌─────────────┐ ┌──────────────────────────┐
|
|
20
|
+
│ foundation-repo │ │ app-repo │
|
|
21
|
+
│ :foundation │◄──│ :domain :feature │
|
|
22
|
+
└─────────────┘ │ (业务逻辑)──►(UI/ViewModel)│
|
|
23
|
+
独立仓库 └──────────────────────────┘
|
|
24
|
+
发布为 AAR/Maven 同仓不同 module,project(":domain") 依赖
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Gradle 依赖声明
|
|
28
|
+
|
|
29
|
+
```kotlin
|
|
30
|
+
// :feature 模块的 build.gradle.kts
|
|
31
|
+
dependencies {
|
|
32
|
+
// 同仓场景
|
|
33
|
+
implementation(project(":domain"))
|
|
34
|
+
// 跨仓场景
|
|
35
|
+
// implementation("com.xxx:domain:<version>")
|
|
36
|
+
// 不直接依赖 foundation(通过 domain 传递)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// :domain 模块的 build.gradle.kts
|
|
40
|
+
dependencies {
|
|
41
|
+
// foundation 始终通过 AAR 引入
|
|
42
|
+
implementation("com.xxx:foundation:<version>")
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 目录结构约定
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
foundation-repo/
|
|
50
|
+
└── :foundation
|
|
51
|
+
├── data/ ← RepositoryImpl、DAO、ApiService
|
|
52
|
+
└── di/ ← Hilt Module(DatabaseModule、RepositoryModule)
|
|
53
|
+
|
|
54
|
+
domain-repo/(或 app-repo/:domain)
|
|
55
|
+
└── :domain
|
|
56
|
+
├── contract/ ← State/Event/Effect/Intent/Callback 定义
|
|
57
|
+
├── usecase/ ← UseCase 接口与实现
|
|
58
|
+
└── di/ ← Hilt Module(DomainModule)
|
|
59
|
+
|
|
60
|
+
app-repo/:feature
|
|
61
|
+
└── :feature
|
|
62
|
+
├── ui/ ← Composable 函数(Screen + Content)
|
|
63
|
+
├── viewmodel/ ← ViewModel
|
|
64
|
+
├── navigation/ ← Navigation Graph
|
|
65
|
+
└── di/ ← 如需 Feature 级 Hilt Module
|
|
66
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# MVI 实现检查清单
|
|
2
|
+
|
|
3
|
+
## 特性层(Feature)
|
|
4
|
+
|
|
5
|
+
- [ ] ViewModel 使用 `@HiltViewModel` + `@Inject constructor(...)` 注入 UseCase
|
|
6
|
+
- [ ] 继承 `BaseViewModel`,不自行声明 `MutableStateFlow` / `MutableSharedFlow`
|
|
7
|
+
- [ ] `initialState` 赋予所有字段默认值,写法统一(`= State()`)
|
|
8
|
+
- [ ] `reducer` 是纯函数,只用 `state.copy()`,不含 IO / UseCase 调用
|
|
9
|
+
- [ ] `effectHandler` 不修改 State,只 `emitEffect()`;无副作用事件空实现
|
|
10
|
+
- [ ] `handleIntent` 中调用 UseCase 方法,需要立即反馈 Loading 时先 `dispatch(LoadingEvent)`
|
|
11
|
+
- [ ] `init { startUseCaseListening() }` 必须调用
|
|
12
|
+
- [ ] Compose 容器层使用 `hiltViewModel()` 获取 ViewModel
|
|
13
|
+
- [ ] Activity 使用 `@AndroidEntryPoint`,Application 使用 `@HiltAndroidApp` 且 Manifest 配置 `android:name`
|
|
14
|
+
- [ ] Composable 分为容器层(ViewModel)和纯 UI 层(参数)
|
|
15
|
+
- [ ] Effect 用 `LaunchedEffect(Unit)` 收集
|
|
16
|
+
- [ ] 用户操作只通过 `viewModel.process(Intent)` 传递
|
|
17
|
+
- [ ] 非 Activity 场景手动调用 `viewModelStore.clear()`
|
|
18
|
+
|
|
19
|
+
## 领域层(Domain)
|
|
20
|
+
|
|
21
|
+
- [ ] domain 是独立 Gradle module,不与 feature 合并
|
|
22
|
+
- [ ] foundation 通过 AAR 引入:`implementation("com.xxx:foundation:<version>")`
|
|
23
|
+
- [ ] 不依赖 feature 模块
|
|
24
|
+
- [ ] UseCase 接口继承 `Registrable<Callback>`
|
|
25
|
+
- [ ] `register()` 保存 callback 引用并启动监听;`unRegister()` 置 null
|
|
26
|
+
- [ ] 只通过 Callback 接口上报结果,不直接调用 ViewModel
|
|
27
|
+
- [ ] UseCase 不持有 View / Context 引用
|
|
28
|
+
- [ ] 多 UseCase 场景:每个独立注册/注销,`onCleared()` 全部注销
|
|
29
|
+
- [ ] Repository 接口定义在 Domain 层,实现在 Foundation 层
|
|
30
|
+
|
|
31
|
+
## 基础层(Foundation)
|
|
32
|
+
|
|
33
|
+
- [ ] Foundation 在独立仓库,以 AAR/Maven 坐标发布
|
|
34
|
+
- [ ] DAO 方法返回 `Flow<T>` 实现响应式数据
|
|
35
|
+
- [ ] 网络调用使用 `suspend` 函数,错误用 `Result` 包裹
|
|
36
|
+
- [ ] Repository 不持有业务状态,不实现 MVI 协议(State/Intent/Event/Effect)
|
|
37
|
+
- [ ] Entity / DTO 与 Domain Model 严格分离,转换在 Repository 层完成
|
|
38
|
+
- [ ] Foundation 层不被 Feature 层直接依赖
|
|
39
|
+
|
|
40
|
+
## Hilt 模块
|
|
41
|
+
|
|
42
|
+
- [ ] DatabaseModule:`@Provides @Singleton` 提供 Room Database 和 DAO
|
|
43
|
+
- [ ] RepositoryModule:`@Binds @Singleton` 绑定 Repository 接口到实现类
|
|
44
|
+
- [ ] DomainModule:`@Binds @Singleton` 绑定 UseCase 接口到实现类
|
|
45
|
+
- [ ] CoroutineModule:`@Provides` 提供注入的 Dispatcher(IO / Default / Main)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# UDF 数据流详解
|
|
2
|
+
|
|
3
|
+
## 完整数据流图
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
[用户操作]
|
|
7
|
+
│
|
|
8
|
+
▼
|
|
9
|
+
Composable.onClick / onValueChange
|
|
10
|
+
│
|
|
11
|
+
▼
|
|
12
|
+
viewModel.process(Intent) ← 统一入口
|
|
13
|
+
│
|
|
14
|
+
▼
|
|
15
|
+
ViewModel.handleIntent(intent)
|
|
16
|
+
│
|
|
17
|
+
├── 需要 UseCase?
|
|
18
|
+
│ │
|
|
19
|
+
│ ▼
|
|
20
|
+
│ UseCase.语义方法() ← 如 cases.connect(addr)
|
|
21
|
+
│ │
|
|
22
|
+
│ ▼
|
|
23
|
+
│ Repository / 数据源
|
|
24
|
+
│ │
|
|
25
|
+
│ ▼
|
|
26
|
+
│ UseCase Callback 回调 ← callback?.onDeviceConnected(device)
|
|
27
|
+
│ │
|
|
28
|
+
│ ▼
|
|
29
|
+
│ ViewModel useCaseCallback ← dispatch(Event.Connected(device))
|
|
30
|
+
│ │
|
|
31
|
+
│ ▼
|
|
32
|
+
│ ┌─── MviStore.dispatch(Event)
|
|
33
|
+
│ │
|
|
34
|
+
│ ├── Reducer ← 纯函数:state.copy(...)
|
|
35
|
+
│ │ │
|
|
36
|
+
│ │ ▼
|
|
37
|
+
│ │ 新 State → StateFlow ← 持久状态,UI collectAsState
|
|
38
|
+
│ │
|
|
39
|
+
│ └── EffectHandler ← 只发射 Effect,不修改 State
|
|
40
|
+
│ │
|
|
41
|
+
│ ▼
|
|
42
|
+
│ Effect → SharedFlow ← 一次性消费,LaunchedEffect 收集
|
|
43
|
+
│
|
|
44
|
+
└── 无需 UseCase(纯 UI 逻辑)
|
|
45
|
+
│
|
|
46
|
+
▼
|
|
47
|
+
直接 dispatch(Event) ← 如 dispatch(Event.Loading)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 关键规则
|
|
51
|
+
|
|
52
|
+
| 环节 | 规则 |
|
|
53
|
+
|------|------|
|
|
54
|
+
| Intent | 所有用户操作统一通过 `viewModel.process(Intent)`,禁止直接调用 ViewModel 方法 |
|
|
55
|
+
| handleIntent | 只做 Intent → UseCase 调用映射,可立即 dispatch 加载状态 |
|
|
56
|
+
| UseCase Callback | 只调用 `dispatch(Event)`,不做 State 修改 |
|
|
57
|
+
| Reducer | 纯函数,只 `state.copy()`,禁止 IO / UseCase 调用 |
|
|
58
|
+
| EffectHandler | 只 `emitEffect()`,不修改 State,无副作用事件空实现 |
|
|
59
|
+
| State | 不可变 `data class`,所有字段 `val`,更新只用 `.copy()` |
|
|
60
|
+
| Effect | 一次性消费,`SharedFlow(replay = 0)`,用 `LaunchedEffect(Unit)` 收集 |
|
|
61
|
+
|
|
62
|
+
## 反向数据流(发送信号到 MCU / 外设)
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
UI 用户操作
|
|
66
|
+
│
|
|
67
|
+
▼
|
|
68
|
+
viewModel.process(Intent.SetEnabled(true))
|
|
69
|
+
│
|
|
70
|
+
▼
|
|
71
|
+
handleIntent → UseCase.setEnabled(true)
|
|
72
|
+
│
|
|
73
|
+
▼
|
|
74
|
+
Repository.sendSignal(...)
|
|
75
|
+
│
|
|
76
|
+
▼
|
|
77
|
+
MCU / 外设
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
反向流不经过 Reducer / EffectHandler,除非需要更新本地状态(如 Loading)。
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: harness-android-cli
|
|
3
|
+
description: 使用 `android` 命令行工具编排 Android 开发任务,包括项目创建、部署、SDK 管理和环境诊断。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Android CLI 专家
|
|
7
|
+
|
|
8
|
+
本技能提供使用 `android` CLI 工具的说明。该工具包含用于创建项目、运行应用、与设备交互以及管理 CLI 环境的各种命令。
|
|
9
|
+
|
|
10
|
+
## SDK 管理
|
|
11
|
+
|
|
12
|
+
- `android sdk install <package>[@<version>]...`:安装指定的包
|
|
13
|
+
- `android sdk update [<pkg-name>]`:将指定的包或所有包更新到最新版本
|
|
14
|
+
- `android sdk remove <pkg-name>`:从本地 SDK 中移除一个包
|
|
15
|
+
- `android sdk list --all`:列出已安装和可用的 SDK 包
|
|
16
|
+
|
|
17
|
+
## 项目创建
|
|
18
|
+
|
|
19
|
+
`android create empty-activity --name="My App" --output=./my-app`
|
|
20
|
+
|
|
21
|
+
## 与设备交互
|
|
22
|
+
|
|
23
|
+
详见 [references/interact.md](references/interact.md)
|
|
24
|
+
|
|
25
|
+
## 运行旅程测试
|
|
26
|
+
|
|
27
|
+
详见 [references/journeys.md](references/journeys.md)
|
|
28
|
+
|
|
29
|
+
## 文档搜索
|
|
30
|
+
|
|
31
|
+
`android docs` 命令在 Android 知识库中搜索权威、高质量的 Android 开发者文档。始终使用此工具获取有关 Android 概念的最新信息。
|
|
32
|
+
|
|
33
|
+
## 运行 APK
|
|
34
|
+
|
|
35
|
+
使用 `android run` 命令运行 Android 应用。
|
|
36
|
+
|
|
37
|
+
## 管理模拟器
|
|
38
|
+
|
|
39
|
+
使用 `android emulator` 命令管理 Android 虚拟设备(AVD)
|
|
40
|
+
|
|
41
|
+
## 截图
|
|
42
|
+
|
|
43
|
+
使用 `android screenshot` 命令捕获已连接 Android 设备的当前屏幕图像并输出到文件。
|
|
44
|
+
|
|
45
|
+
## 管理技能
|
|
46
|
+
|
|
47
|
+
使用 `android skills` 命令管理 Android 的 antigravity agent 技能。
|
|
48
|
+
|
|
49
|
+
## 检查 UI 布局
|
|
50
|
+
|
|
51
|
+
使用 `android layout` 命令检查 Android 应用的 UI 布局。它以 JSON 格式返回 Android 应用的布局树。
|
|
52
|
+
|
|
53
|
+
## 更新 CLI
|
|
54
|
+
|
|
55
|
+
使用 `android update` 命令更新 Android CLI。
|
|
56
|
+
|
|
57
|
+
## 命令速查
|
|
58
|
+
|
|
59
|
+
| 命令 | 用途 |
|
|
60
|
+
|------|------|
|
|
61
|
+
| `android create` | 创建新的 Android 项目 |
|
|
62
|
+
| `android describe` | 分析 Android 项目生成描述性元数据 |
|
|
63
|
+
| `android docs` | 搜索/获取 Android 文档 |
|
|
64
|
+
| `android emulator` | 管理虚拟设备 |
|
|
65
|
+
| `android info` | 打印环境信息 |
|
|
66
|
+
| `android init` | 初始化 CLI 环境 |
|
|
67
|
+
| `android layout` | 返回应用的布局树 |
|
|
68
|
+
| `android run` | 部署 Android 应用 |
|
|
69
|
+
| `android sdk` | 下载和列出 SDK 包 |
|
|
70
|
+
| `android skills` | 管理技能 |
|
|
71
|
+
| `android update` | 更新 CLI |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 必须遵守的约束规则
|
|
76
|
+
|
|
77
|
+
> rules 引用路径:`../rules/<rule-name>.mdc`
|
|
78
|
+
|
|
79
|
+
无(本技能为 CLI 工具操作,不涉及代码生成)
|