@buaa_smat/hometrans 0.1.5 → 0.1.7
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/LICENSE +21 -21
- package/README.md +112 -55
- package/agents/{logic-coding.md → logic-coder.md} +2 -2
- package/agents/logic-context-builder.md +1 -1
- package/package.json +1 -1
- package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/SKILL.md +3 -3
- package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/conversion-procedure.md +2 -2
- package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mappings/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +3 -5
- package/skills/{convert_pipeline → hmos-convert-pipeline}/SKILL.md +4 -4
- package/skills/hmos-fix-build-errors/SKILL.md +265 -0
- package/skills/hmos-fix-build-errors/references/arkts-strict-patterns.md +219 -0
- package/skills/hmos-fix-build-errors/references/known-patterns.md +157 -0
- package/skills/hmos-fix-build-errors/references/rdb-entity-pattern.md +131 -0
- package/skills/{hmos-ui-align → hmos-incremental-ui-align}/SKILL.md +20 -16
- package/skills/{hmos-ui-align → hmos-incremental-ui-align}/readme.md +5 -6
- package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +3 -5
- package/skills/{self-test → hmos-integration-test}/SKILL.md +1 -1
- package/skills/{spec-generator-skill → hmos-spec-generate}/SKILL.md +1 -1
- package/skills/skill-quality-evaluator/SKILL.md +138 -0
- package/skills/skill-quality-evaluator/assets/SKILL_TEMPLATE.md +77 -0
- package/skills/skill-quality-evaluator/references/Best-practices-for-skill-creators.md +277 -0
- package/skills/skill-quality-evaluator/references/Evaluating-skill-output-quality.md +300 -0
- package/skills/skill-quality-evaluator/references/Optimizing-skill-descriptions.md +196 -0
- package/skills/skill-quality-evaluator/references/Specification.md +272 -0
- package/skills/skill-quality-evaluator/references/Using-scripts-in-skills.md +308 -0
- package/skills/skill-quality-evaluator/references/report-template.md +163 -0
- package/skills/skill-quality-evaluator/references/scoring-rubric.md +269 -0
- package/agents/code-review-fix.md +0 -356
- package/dist/cli/config-store.js +0 -148
- package/dist/cli/config.js +0 -40
- package/dist/cli/index.js +0 -43
- package/dist/cli/init.js +0 -378
- package/dist/cli/mcp-setup.js +0 -262
- package/dist/cli/mcp.js +0 -94
- package/dist/cli/uninstall.js +0 -310
- package/dist/context/index.js +0 -792
- package/skills/code-dev-review-fix/SKILL.md +0 -279
- package/skills/code-dev-review-fix-workspace/evals/evals.json +0 -56
- package/skills/code-dev-review-fix-workspace/iteration-1/routing-results.md +0 -23
- package/skills/hmos-ui-align/config.json +0 -11
- /package/agents/{logic-coding → logic-coder}/scripts/platform_context_query.py +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mappings/android-to-harmonyOS-ui-interaction-mapping-reference.md +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mappings/android-to-harmonyOS-ui-layout-mapping-reference.md +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/references/mvvm//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md" +0 -0
- /package/skills/{hmos-ui-align-batch → hmos-batch-ui-align}/scripts/android_parse_fast.py +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/config-example.json +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/diff_analysis.md +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/page_align.md +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/Comparison_Template.md +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/MVVM/346/250/241/345/274/217V1.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/MVVM/345/274/200/345/217/221/346/226/207/346/241/243//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md" +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/UI_Analysis_Template.md +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/android-to-harmonyOS-ui-interaction-mapping-reference.md +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/references/android-to-harmonyOS-ui-layout-mapping-reference.md +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/scripts/app_feature_verify.py +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/scripts/navigation-capure.md +0 -0
- /package/skills/{hmos-ui-align → hmos-incremental-ui-align}/scripts/page_capture.py +0 -0
- /package/skills/{self-test → hmos-integration-test}/readme.md +0 -0
- /package/skills/{spec-generator-skill → hmos-spec-generate}/references/android-platform-tokens.md +0 -0
- /package/skills/{spec-generator-skill → hmos-spec-generate}/references/spec-sample-1.md +0 -0
- /package/skills/{spec-generator-skill → hmos-spec-generate}/references/spec-sample-2.md +0 -0
- /package/skills/{spec-generator-skill → hmos-spec-generate}/references/spec-sample-3.md +0 -0
- /package/skills/{spec-generator-skill → hmos-spec-generate}/references/step4-report-template.md +0 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# ArkTS 严格模式确定性修复 Pattern
|
|
2
|
+
# 适用于: ArkTS 严格模式编译错误
|
|
3
|
+
# 触发场景: 编译报错 arkts-no-xxx 系列
|
|
4
|
+
# 经验来源: EinkBro 迁移 (2026-03-26)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Pattern 1: throw 语句
|
|
9
|
+
|
|
10
|
+
### 错误写法
|
|
11
|
+
```typescript
|
|
12
|
+
throw err; // ERROR: arkts-limited-throw
|
|
13
|
+
throw 'error string'; // ERROR: arkts-limited-throw
|
|
14
|
+
throw unknownVariable; // ERROR: arkts-limited-throw
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 正确写法
|
|
18
|
+
```typescript
|
|
19
|
+
throw new Error(String(error));
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 不需要修改的情况
|
|
23
|
+
```typescript
|
|
24
|
+
throw new Error('specific message'); // OK - 已经是 Error 实例
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Pattern 2: 对象字面量作为参数 (arkts-no-untyped-obj-literals)
|
|
30
|
+
|
|
31
|
+
### 错误写法
|
|
32
|
+
```typescript
|
|
33
|
+
// ERROR: Object literal must correspond to some explicitly declared class or interface
|
|
34
|
+
const response = await this.sendRequest(url, { contents });
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 正确写法 — 先声明变量
|
|
38
|
+
```typescript
|
|
39
|
+
interface GeminiRequestBody {
|
|
40
|
+
contents: GeminiContent[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const requestBody: GeminiRequestBody = { contents };
|
|
44
|
+
const response = await this.sendRequest(url, requestBody);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 内联参数场景
|
|
48
|
+
```typescript
|
|
49
|
+
// 方法参数也是对象字面量,同样需要处理
|
|
50
|
+
httpRequest.request(url, {
|
|
51
|
+
method: http.RequestMethod.POST,
|
|
52
|
+
header: { 'Content-Type': 'application/json' },
|
|
53
|
+
extraData: JSON.stringify(body),
|
|
54
|
+
connectTimeout: 30000,
|
|
55
|
+
readTimeout: 30000
|
|
56
|
+
});
|
|
57
|
+
// ^ 这种场景 SDK 方法签名本身接受 object 类型,通常 OK
|
|
58
|
+
// 但自定义方法调用时必须用已声明接口的变量
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Pattern 3: Margin vertical 简写不支持
|
|
64
|
+
|
|
65
|
+
### 错误写法
|
|
66
|
+
```typescript
|
|
67
|
+
.margin({ vertical: 8 }); // ERROR: 'vertical' does not exist in type
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 正确写法
|
|
71
|
+
```typescript
|
|
72
|
+
.margin({ top: 8, bottom: 8 });
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 常见场景
|
|
76
|
+
| 错误 | 正确 |
|
|
77
|
+
|------|------|
|
|
78
|
+
| `.margin({ vertical: 8 })` | `.margin({ top: 8, bottom: 8 })` |
|
|
79
|
+
| `.margin({ horizontal: 16 })` | `.margin({ left: 16, right: 16 })` |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Pattern 4: List 内不能直接放 Divider
|
|
84
|
+
|
|
85
|
+
### 错误写法
|
|
86
|
+
```typescript
|
|
87
|
+
List() {
|
|
88
|
+
ForEach(items, (item) => {
|
|
89
|
+
ListItem() { /* ... */ }
|
|
90
|
+
})
|
|
91
|
+
Divider() // ERROR: List children must be ListItem only
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 正确写法 — 用 ListItem + border
|
|
96
|
+
```typescript
|
|
97
|
+
List() {
|
|
98
|
+
ForEach(items, (item) => {
|
|
99
|
+
ListItem() {
|
|
100
|
+
Column() {
|
|
101
|
+
Text(item.name)
|
|
102
|
+
}
|
|
103
|
+
.width('100%')
|
|
104
|
+
.padding(12)
|
|
105
|
+
.border({ width: { bottom: 1 }, color: '#E0E0E0' }) // 替代 Divider 分隔线
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 另一种方案 — border 直接在 ListItem 上
|
|
112
|
+
```typescript
|
|
113
|
+
ListItem() {
|
|
114
|
+
// content
|
|
115
|
+
}
|
|
116
|
+
.border({ width: { bottom: 1 }, color: '#E0E0E0' })
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Pattern 5: 数组字面量类型推断 (arkts-no-noninferrable-arr-literals)
|
|
122
|
+
|
|
123
|
+
### 错误写法
|
|
124
|
+
```typescript
|
|
125
|
+
const arr = [1, 2, 3].map(x => ({ val: x }));
|
|
126
|
+
// ERROR: Array literals must contain elements of only inferrable types
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 正确写法 — 显式类型
|
|
130
|
+
```typescript
|
|
131
|
+
interface Item { val: number; }
|
|
132
|
+
const arr: Item[] = [1, 2, 3].map((x): Item => ({ val: x }));
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Pattern 6: TextDecoder 不存在
|
|
138
|
+
|
|
139
|
+
### 错误写法
|
|
140
|
+
```typescript
|
|
141
|
+
const decoder = new TextDecoder(); // ERROR: Cannot find name 'TextDecoder'
|
|
142
|
+
const str = decoder.decode(data);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 正确写法
|
|
146
|
+
```typescript
|
|
147
|
+
const bytes = new Uint8Array(data);
|
|
148
|
+
let chunk = '';
|
|
149
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
150
|
+
chunk += String.fromCharCode(bytes[i]);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Pattern 7: HTTP 组件事件名
|
|
157
|
+
|
|
158
|
+
| 错误事件 | 正确事件 | 说明 |
|
|
159
|
+
|---------|---------|------|
|
|
160
|
+
| `request.on('requestEnd', ...)` | `request.on('dataEnd', ...)` | 请求结束事件 |
|
|
161
|
+
| `request.on('error', ...)` | 改用 Promise 的 catch | HTTP 组件 error 事件签名不标准 |
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Pattern 8: GeminiService 等自定义类缺少成员
|
|
166
|
+
|
|
167
|
+
### 错误场景
|
|
168
|
+
```typescript
|
|
169
|
+
class GeminiService {
|
|
170
|
+
private async sendRequest(url: string, body: object): Promise<Object> {
|
|
171
|
+
httpRequest.request(url, { extraData: JSON.stringify(body),
|
|
172
|
+
connectTimeout: this.timeout, // ERROR: Property 'timeout' does not exist
|
|
173
|
+
readTimeout: this.timeout
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// timeout 属性忘记声明
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### 正确 — 确保所有成员都在类体内声明
|
|
181
|
+
```typescript
|
|
182
|
+
class GeminiService {
|
|
183
|
+
private timeout: number = 30000; // 在类体内声明
|
|
184
|
+
private async sendRequest(...) { ... }
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Pattern 9: Button 不支持 emoji
|
|
191
|
+
|
|
192
|
+
### 错误写法
|
|
193
|
+
```typescript
|
|
194
|
+
Button('X') // 有时 emoji 渲染异常
|
|
195
|
+
Button('🗑')
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 正确写法
|
|
199
|
+
```typescript
|
|
200
|
+
Button('Del') // 纯文本
|
|
201
|
+
Button('Copy')
|
|
202
|
+
Button('Back')
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 快速检查清单
|
|
208
|
+
|
|
209
|
+
遇到 ArkTS 编译错误时,按序检查:
|
|
210
|
+
|
|
211
|
+
1. throw → `throw new Error(String(x))`
|
|
212
|
+
2. 对象字面量作为参数 → 先赋值给显式类型变量
|
|
213
|
+
3. any / unknown → `Object` + `as Function` / `as SomeType`
|
|
214
|
+
4. // @ts-ignore → 禁止,必须用类型操作替代
|
|
215
|
+
5. ValuesBucket → 普通对象字面量 `{}`
|
|
216
|
+
6. margin { vertical } → `{ top, bottom }`
|
|
217
|
+
7. List 内 Divider → ListItem + border
|
|
218
|
+
8. TextDecoder → Uint8Array 遍历
|
|
219
|
+
9. `new SomeType()` 报错 → 检查是否是 type alias 而非 class
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Known Compile Error Patterns
|
|
2
|
+
|
|
3
|
+
> **This file is auto-maintained by `a2h-retrospect`.**
|
|
4
|
+
> Manual edits are allowed but may be overwritten when retrospect detects updated patterns.
|
|
5
|
+
> Last updated: 2026-03-27
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Pattern Index
|
|
10
|
+
|
|
11
|
+
| # | Error Code / Type | Frequency | Short Description |
|
|
12
|
+
|---|-------------------|-----------|-------------------|
|
|
13
|
+
| 1 | arkts-identifiers-as-prop-names | 85 | ValuesBucket computed property name |
|
|
14
|
+
| 2 | type mismatch (EventData) | 16 | EventData double type cast |
|
|
15
|
+
| 3 | arkts-no-untyped-obj-literals | 9 | Untyped object literal |
|
|
16
|
+
| 4 | type mismatch (promptAction) | 8 | promptAction button color type |
|
|
17
|
+
| 5 | type error (@Prop) | 3 | @Prop naming conflict |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. ValuesBucket Computed Property Name
|
|
22
|
+
|
|
23
|
+
- **Date**: 2026-03-27
|
|
24
|
+
- **Error code**: `arkts-identifiers-as-prop-names`
|
|
25
|
+
- **Frequency**: 85 occurrences
|
|
26
|
+
- **Source**: AntennaPod V1 migration (a2h-retrospect-report-2026-03-27)
|
|
27
|
+
|
|
28
|
+
**Error code**:
|
|
29
|
+
```typescript
|
|
30
|
+
const bucket: ValuesBucket = {
|
|
31
|
+
[ColumnName.TITLE]: title, // ERROR: computed property name not allowed
|
|
32
|
+
[ColumnName.URL]: url,
|
|
33
|
+
};
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Correct code**:
|
|
37
|
+
```typescript
|
|
38
|
+
const bucket: ValuesBucket = {};
|
|
39
|
+
bucket[ColumnName.TITLE] = title;
|
|
40
|
+
bucket[ColumnName.URL] = url;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Rule**: ArkTS forbids computed property names (`[expr]: value`) in object literals. Build the object first, then assign properties via bracket notation.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. EventData Double Type Cast
|
|
48
|
+
|
|
49
|
+
- **Date**: 2026-03-27
|
|
50
|
+
- **Error code**: type mismatch
|
|
51
|
+
- **Frequency**: 16 occurrences
|
|
52
|
+
- **Source**: AntennaPod V1 migration (a2h-retrospect-report-2026-03-27)
|
|
53
|
+
|
|
54
|
+
**Error code**:
|
|
55
|
+
```typescript
|
|
56
|
+
emitter.on({ eventId: 1 }, (data: emitter.EventData) => {
|
|
57
|
+
const value = data.data?.['key'] as string; // ERROR: data.data is optional Record, direct cast fails
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Correct code**:
|
|
62
|
+
```typescript
|
|
63
|
+
emitter.on({ eventId: 1 }, (data: emitter.EventData) => {
|
|
64
|
+
const raw = data.data as Record<string, Object>;
|
|
65
|
+
const value = raw['key'] as string;
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Rule**: `EventData.data` is typed `Record<string, Object> | undefined`. Access requires two steps: (1) cast to `Record<string, Object>`, (2) cast the extracted value to the target type.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 3. Untyped Object Literal
|
|
74
|
+
|
|
75
|
+
- **Date**: 2026-03-27
|
|
76
|
+
- **Error code**: `arkts-no-untyped-obj-literals`
|
|
77
|
+
- **Frequency**: 9 occurrences
|
|
78
|
+
- **Source**: AntennaPod V1 migration (a2h-retrospect-report-2026-03-27)
|
|
79
|
+
|
|
80
|
+
**Error code**:
|
|
81
|
+
```typescript
|
|
82
|
+
const options = {
|
|
83
|
+
title: 'Hello',
|
|
84
|
+
message: 'World',
|
|
85
|
+
}; // ERROR: object literal without explicit type annotation
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Correct code**:
|
|
89
|
+
```typescript
|
|
90
|
+
interface DialogOptions {
|
|
91
|
+
title: string;
|
|
92
|
+
message: string;
|
|
93
|
+
}
|
|
94
|
+
const options: DialogOptions = {
|
|
95
|
+
title: 'Hello',
|
|
96
|
+
message: 'World',
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Rule**: ArkTS requires all object literals to have an explicit type. Define an `interface` or `class` and annotate the variable.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 4. promptAction Button Color Type
|
|
105
|
+
|
|
106
|
+
- **Date**: 2026-03-27
|
|
107
|
+
- **Error code**: type mismatch
|
|
108
|
+
- **Frequency**: 8 occurrences
|
|
109
|
+
- **Source**: AntennaPod V1 migration (a2h-retrospect-report-2026-03-27)
|
|
110
|
+
|
|
111
|
+
**Error code**:
|
|
112
|
+
```typescript
|
|
113
|
+
promptAction.showDialog({
|
|
114
|
+
buttons: [
|
|
115
|
+
{ text: 'OK', color: '#ff0000' } // ERROR: color expects ResourceColor, not string
|
|
116
|
+
]
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Correct code**:
|
|
121
|
+
```typescript
|
|
122
|
+
promptAction.showDialog({
|
|
123
|
+
buttons: [
|
|
124
|
+
{ text: 'OK', color: Color.Red }
|
|
125
|
+
]
|
|
126
|
+
});
|
|
127
|
+
// Or use $r('app.color.xxx') for resource reference
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Rule**: `promptAction.showDialog` button `color` property expects `ResourceColor` type (Color enum or `$r()` resource reference), not a raw hex string.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 5. @Prop Naming Conflict
|
|
135
|
+
|
|
136
|
+
- **Date**: 2026-03-27
|
|
137
|
+
- **Error code**: type error
|
|
138
|
+
- **Frequency**: 3 occurrences
|
|
139
|
+
- **Source**: AntennaPod V1 migration (a2h-retrospect-report-2026-03-27)
|
|
140
|
+
|
|
141
|
+
**Error code**:
|
|
142
|
+
```typescript
|
|
143
|
+
@Component
|
|
144
|
+
struct MyComponent {
|
|
145
|
+
@Prop title: string = ''; // ERROR: 'title' conflicts with built-in component property
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Correct code**:
|
|
150
|
+
```typescript
|
|
151
|
+
@Component
|
|
152
|
+
struct MyComponent {
|
|
153
|
+
@Prop itemTitle: string = ''; // Renamed to avoid conflict
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Rule**: Certain property names (`title`, `content`, `action`, etc.) conflict with built-in component attributes. Prefix with a domain-specific qualifier (e.g., `itemTitle`, `feedTitle`).
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# RDB Entity 确定性修复 Pattern
|
|
2
|
+
# 适用于: ArkTS 严格模式 + @kit.ArkData relationalStore
|
|
3
|
+
# 触发场景: 迁移 Android RDB 实体类到 ArkTS 时
|
|
4
|
+
# 经验来源: EinkBro 迁移 (2026-03-26)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Pattern 1: ValuesBucket 正确用法
|
|
9
|
+
|
|
10
|
+
### 错误写法 (编译报错)
|
|
11
|
+
```typescript
|
|
12
|
+
import { relationalStore, ValuesBucket } from '@kit.ArkData';
|
|
13
|
+
|
|
14
|
+
toValuesBucket(): ValuesBucket {
|
|
15
|
+
const bucket = new ValuesBucket(); // ERROR: 'ValuesBucket' only refers to a type
|
|
16
|
+
bucket.put('key', value); // ERROR: ValuesBucket has no .put() method
|
|
17
|
+
return bucket;
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 正确写法
|
|
22
|
+
```typescript
|
|
23
|
+
type EntityValuesBucket = Record<string, number | string | boolean | Uint8Array | null>;
|
|
24
|
+
|
|
25
|
+
toValuesBucket(): EntityValuesBucket {
|
|
26
|
+
const bucket: EntityValuesBucket = {};
|
|
27
|
+
bucket['key'] = value;
|
|
28
|
+
return bucket;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**原因**: `ValuesBucket` 在 SDK 中是 type alias (`Record<string, ValueType>`),不是可实例化的类。
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Pattern 2: fromResultSet 参数类型 (禁止 any)
|
|
37
|
+
|
|
38
|
+
### 错误写法 (编译报错)
|
|
39
|
+
```typescript
|
|
40
|
+
// ArkTS 严格模式禁止 any
|
|
41
|
+
static fromResultSet(resultSet: any): EntityName { // ERROR: arkts-no-any-unknown
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 正确写法
|
|
45
|
+
```typescript
|
|
46
|
+
static fromResultSet(resultSet: Object): EntityName {
|
|
47
|
+
const rs = resultSet as Record<string, Object>;
|
|
48
|
+
const colIndex = (name: string): number =>
|
|
49
|
+
(rs['getColumnIndex'] as Function)(name) as number;
|
|
50
|
+
|
|
51
|
+
entity.field = (rs['getLong'] as Function)(colIndex('field_name')) as number;
|
|
52
|
+
entity.name = (rs['getString'] as Function)(colIndex('name')) as string;
|
|
53
|
+
return entity;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**替代方案** (更简洁但运行时开销略高):
|
|
58
|
+
```typescript
|
|
59
|
+
// 使用类型断言避免 any,运行时通过 Record<string, Function> 调用
|
|
60
|
+
static fromResultSet(resultSet: Object): EntityName {
|
|
61
|
+
const getCol = (name: string): number =>
|
|
62
|
+
(resultSet as Record<string, Function>)['getColumnIndex'](name) as number;
|
|
63
|
+
const getLong = (name: string): number =>
|
|
64
|
+
(resultSet as Record<string, Function>)['getLong'](getCol(name)) as number;
|
|
65
|
+
const getStr = (name: string): string =>
|
|
66
|
+
(resultSet as Record<string, Function>)['getString'](getCol(name)) as string;
|
|
67
|
+
|
|
68
|
+
const entity = new EntityName();
|
|
69
|
+
entity.id = getLong('id');
|
|
70
|
+
entity.name = getStr('name');
|
|
71
|
+
return entity;
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Pattern 3: // @ts-ignore 不可用
|
|
78
|
+
|
|
79
|
+
### 错误尝试
|
|
80
|
+
```typescript
|
|
81
|
+
// @ts-ignore // ERROR: Switching off type checks with in-place comments is not allowed
|
|
82
|
+
static fromResultSet(resultSet: any): EntityName {
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 必须用 Pattern 2 替代
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Pattern 4: toValuesBucket() 返回类型注解
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
type EntityValuesBucket = Record<string, number | string | boolean | Uint8Array | null>;
|
|
93
|
+
|
|
94
|
+
toValuesBucket(): EntityValuesBucket {
|
|
95
|
+
const bucket: EntityValuesBucket = {} as EntityValuesBucket;
|
|
96
|
+
// ...
|
|
97
|
+
return bucket;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 完整 Entity 模板
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
type EntityValuesBucket = Record<string, number | string | boolean | Uint8Array | null>;
|
|
107
|
+
|
|
108
|
+
export class MyEntity {
|
|
109
|
+
id: number = 0;
|
|
110
|
+
name: string = '';
|
|
111
|
+
value: number = 0;
|
|
112
|
+
|
|
113
|
+
static fromResultSet(resultSet: Object): MyEntity {
|
|
114
|
+
const rs = resultSet as Record<string, Object>;
|
|
115
|
+
const col = (n: string): number => (rs['getColumnIndex'] as Function)(n) as number;
|
|
116
|
+
const entity = new MyEntity();
|
|
117
|
+
entity.id = (rs['getLong'] as Function)(col('id')) as number;
|
|
118
|
+
entity.name = (rs['getString'] as Function)(col('name')) as string;
|
|
119
|
+
entity.value = (rs['getLong'] as Function)(col('value')) as number;
|
|
120
|
+
return entity;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
toValuesBucket(): EntityValuesBucket {
|
|
124
|
+
const bucket: EntityValuesBucket = {} as EntityValuesBucket;
|
|
125
|
+
if (this.id > 0) { bucket['id'] = this.id; }
|
|
126
|
+
bucket['name'] = this.name;
|
|
127
|
+
bucket['value'] = this.value;
|
|
128
|
+
return bucket;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: hmos-ui-align
|
|
2
|
+
name: hmos-incremental-ui-align
|
|
3
3
|
description: "Automated HarmonyOS-Android UI alignment pipeline. Takes a natural language task description, automatically navigates to the target pages on both Android and HarmonyOS devices, captures view trees and screenshots, then aligns HarmonyOS code to match Android. Use this skill whenever the user wants to align a HarmonyOS page with its Android counterpart, fix visual differences between Android and HarmonyOS, add missing HarmonyOS pages based on Android, or describes a page path like '首页-设置-关于' that needs UI alignment. Also trigger on phrases like 'UI对齐', '页面对齐', '和安卓对齐', '鸿蒙页面修复','UI增量开发','align HarmonyOS with Android', or any request involving comparing and fixing HarmonyOS UI to match Android."
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -17,21 +17,25 @@ You are writing ArkTS codes.
|
|
|
17
17
|
- 不要做和用户需求无关的其他修改
|
|
18
18
|
-
|
|
19
19
|
## Step 0: Load Config
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
The user MUST provide a `config-path` when invoking this skill (e.g., `config-path: D:\path\to\config.json`). The config file must exist before running — verify with a Read, and if it's missing or unreadable, ask the user for a valid path. Refer to `config-example.json` in this skill's directory for the expected schema.
|
|
21
|
+
|
|
22
|
+
Config fields:
|
|
23
|
+
|
|
24
|
+
| Field | Description |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `android.app_name` | Android app name for navigation |
|
|
27
|
+
| `android.package` | Android app package name |
|
|
28
|
+
| `android.project_dir` | Android source code root path |
|
|
29
|
+
| `harmony.app_name` | HarmonyOS app name for navigation |
|
|
30
|
+
| `harmony.package` | HarmonyOS app package name |
|
|
31
|
+
| `harmony.project_dir` | HarmonyOS project root path |
|
|
32
|
+
| `hmos_sdk_dir` | HarmonyOS SDK path (ETS API reference) |
|
|
33
|
+
| `glm_api_key` | Zhipu GLM API key for phone-agent |
|
|
34
|
+
| `capture_output_dir` | Base directory for captured page data |
|
|
31
35
|
|
|
32
36
|
## Step 1: Capture All Related Pages on Android & HarmonyOS Devices
|
|
33
37
|
|
|
34
|
-
Read `scripts/navigation-capure.md` to learn the usage of `
|
|
38
|
+
Read `scripts/navigation-capure.md` to learn the usage of `scripts/app_feature_verify.py` (navigation) and `scripts/page_capture.py` (capture).
|
|
35
39
|
|
|
36
40
|
### Step 1.1: Parse User Request and Build Capture Plan
|
|
37
41
|
Analyze the user's description and build a list of **base pages** to capture.
|
|
@@ -46,8 +50,8 @@ Create the timestamped output directory and per-page sub-directories following t
|
|
|
46
50
|
|
|
47
51
|
### Step 1.2: Capture Base Pages
|
|
48
52
|
For each base page in the plan, on **both** Android and HarmonyOS devices:
|
|
49
|
-
1. Use `
|
|
50
|
-
2. On success, use `
|
|
53
|
+
1. Use `scripts/app_feature_verify.py` to navigate to the page.
|
|
54
|
+
2. On success, use `scripts/page_capture.py` to capture the view tree and screenshot.
|
|
51
55
|
3. For HarmonyOS pages that don't exist yet, navigation will fail — leave the directory empty (expected).
|
|
52
56
|
|
|
53
57
|
### Step 1.3: Discover and Capture Interactive States
|
|
@@ -59,7 +63,7 @@ After capturing each base page, scan its view tree for **interactive elements th
|
|
|
59
63
|
|
|
60
64
|
**For each interactive element found:**
|
|
61
65
|
1. Create a separate capture directory: `{platform}_page_{i}_{base_name}_{state_type}_{state_name}` (e.g., `android_page_1_detail_tab_city`, `android_page_1_detail_popup_filter_identity`).
|
|
62
|
-
2. Navigate to the base page (reuse the same nav path), then append the click action to trigger the state change. Capture with `
|
|
66
|
+
2. Navigate to the base page (reuse the same nav path), then append the click action to trigger the state change. Capture with `scripts/page_capture.py`.
|
|
63
67
|
3. Repeat for **both** Android and HarmonyOS devices.
|
|
64
68
|
4. Each captured state is treated as a separate page pair in Step 2 and Step 3.
|
|
65
69
|
|
|
@@ -45,7 +45,7 @@ HarmonyOS-Android UI 自动对齐流水线。
|
|
|
45
45
|
|
|
46
46
|
## 配置 `config.json`
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
首次使用前,自行准备一份 `config.json`(放任何位置都可以),调用 skill 时把它的**绝对路径**作为参数传给 skill。可复制同目录下的 `config-example.json` 作为模板。字段说明:
|
|
49
49
|
|
|
50
50
|
| 字段 | 说明 | 示例 |
|
|
51
51
|
|---|---|---|
|
|
@@ -65,10 +65,10 @@ HarmonyOS-Android UI 自动对齐流水线。
|
|
|
65
65
|
|
|
66
66
|
## 使用方式
|
|
67
67
|
|
|
68
|
-
在 Claude Code 里直接调用 skill
|
|
68
|
+
在 Claude Code 里直接调用 skill,**必须把准备好的 `config.json` 的绝对路径附在需求里**:
|
|
69
69
|
|
|
70
70
|
```
|
|
71
|
-
/hmos-ui-align <自然语言需求,描述要对齐的页面+点击路径>
|
|
71
|
+
/hmos-ui-align config: <绝对路径/config.json> <自然语言需求,描述要对齐的页面+点击路径>
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
### 写需求的三个要点
|
|
@@ -112,7 +112,7 @@ HarmonyOS-Android UI 自动对齐流水线。
|
|
|
112
112
|
skill 会严格按 `SKILL.md` 里的 5 步流水线跑:
|
|
113
113
|
|
|
114
114
|
### Step 0 · 加载 config
|
|
115
|
-
|
|
115
|
+
读用户指定路径下的 `config.json`,解析出所有路径和 API Key。
|
|
116
116
|
|
|
117
117
|
### Step 1 · 双端页面采集
|
|
118
118
|
1.1 解析你的需求,拆成一组「基础页」,每个基础页有 `android_nav_path` 和 `hmos_nav_path`
|
|
@@ -161,8 +161,7 @@ skill 会严格按 `SKILL.md` 里的 5 步流水线跑:
|
|
|
161
161
|
Agents/hmos-ui-align/
|
|
162
162
|
├── SKILL.md # 流水线定义(主 agent 执行逻辑)
|
|
163
163
|
├── readme.md # 本文档
|
|
164
|
-
├── config.json
|
|
165
|
-
├── config-example.json # 配置样例
|
|
164
|
+
├── config-example.json # 配置样例(实际使用的 config.json 由用户自备并显式传路径)
|
|
166
165
|
├── page_align.md # Step 3 的转换规则
|
|
167
166
|
├── diff_analysis.md # 内部说明
|
|
168
167
|
├── scripts/
|
|
@@ -2524,11 +2524,9 @@ Column() {
|
|
|
2524
2524
|
|
|
2525
2525
|
## 参考链接
|
|
2526
2526
|
|
|
2527
|
-
1. **
|
|
2528
|
-
2. **
|
|
2529
|
-
3. **
|
|
2530
|
-
4. **Android UI 组件总览**: https://developer.android.com/develop/ui
|
|
2531
|
-
5. **Material Design 3 规范**: https://m3.material.io/components
|
|
2527
|
+
1. **HarmonyOS 开发者文档**: https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/
|
|
2528
|
+
2. **Android UI 组件总览**: https://developer.android.com/develop/ui
|
|
2529
|
+
3. **Material Design 3 规范**: https://m3.material.io/components
|
|
2532
2530
|
|
|
2533
2531
|
---
|
|
2534
2532
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: hmos-integration-test
|
|
3
3
|
description: "Run on-device self-test for a HarmonyOS app. Parse test_case.md, install the HAP, execute AutoTest, and produce a verification report. Optionally enters a test-and-fix loop. Triggers on phrases like '跑自测', '运行自测', '执行自测', '自动测试', '设备测试', '跑用例', '跑自动化测试', 'self test', 'run autotest'."
|
|
4
4
|
allowed-tools:
|
|
5
5
|
- Read
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: spec-
|
|
2
|
+
name: hmos-spec-generate
|
|
3
3
|
description: "Generate atomic-scenario requirement specs (markdown) from raw .txt requirement batches for Android-to-HarmonyOS migration. Reads a single .txt holding multiple REQ blocks (blank-line separated), explores the Android code graph via GitNexus, writes per-REQ trace files first then synthesizes specs from the trace. Triggers: spec generation, generate spec, requirement to spec, atomic scenarios, scenario decomposition, decompose scenarios, req to spec, code-trace-first, batch req txt."
|
|
4
4
|
license: MIT
|
|
5
5
|
allowed-tools:
|