@wwkit/harness 1.0.16 → 1.0.17
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 +6 -6
- package/commands/pytest.md +11 -0
- package/package.json +1 -1
- package/readme/development.md +1 -1
- package/skills/extract/SKILL.md +90 -28
- package/{agents/pyit.md → skills/pytest/SKILL.md} +223 -64
- package/skills/pytest-env-ensure/references/config.md +2 -2
- package/skills/pytest-sample/SKILL.md +3 -3
- package/skills/read-docs/references/superpowers/comparison.md +1 -1
- package/skills/read-docs/references/superpowers/index.md +1 -1
- package/skills/revise/SKILL.md +86 -25
- package/agents/extract.md +0 -26
- package/agents/pyut.md +0 -347
- package/agents/revise.md +0 -28
- package/commands/pyit.md +0 -6
- package/commands/pyut.md +0 -6
package/skills/revise/SKILL.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: revise
|
|
3
3
|
description: |
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
自包含内容创作 skill:解析入参(JSON/key=value/prose)→ 按 format 加载 prompt 生成 → schema 校验 → 写 output 文件或 stdout。
|
|
5
|
+
format=question/status/gallery/article。count 控制条目数(默认1),language 控制输出语言(默认中文)。
|
|
6
|
+
调用方直接传原始任务消息,skill 自解析自包含。内部用 todowrite 管理 6 步。禁止 WebFetch/网络请求。
|
|
7
7
|
适用:内容创作(question/status/gallery/article 等)。
|
|
8
8
|
license: MIT
|
|
9
9
|
metadata:
|
|
@@ -12,28 +12,71 @@ metadata:
|
|
|
12
12
|
|
|
13
13
|
# revise 技能
|
|
14
14
|
|
|
15
|
+
## 核心约束(最高优先级)
|
|
16
|
+
|
|
17
|
+
- **MUST**:收到任务消息后,先 todowrite 落单 6 步,再逐步执行。
|
|
18
|
+
- **MUST**:每步完成立即 todowrite 勾单。
|
|
19
|
+
- **禁止**:使用 WebFetch 或任何网络请求获取内容。
|
|
20
|
+
- **禁止**:写入 output 以外的任何文件;中间产物用临时文件,完成后清理。
|
|
21
|
+
- **禁止**:以任何形式使用未声明字段(url 等不属于 revise 的字段)。
|
|
22
|
+
|
|
23
|
+
## 第一步硬指令(自检)
|
|
24
|
+
|
|
25
|
+
解析入参前,强制自检:
|
|
26
|
+
> 我是否已用 todowrite 落单 6 步?
|
|
27
|
+
> - 未落单 → 立即 todowrite 创建清单。
|
|
28
|
+
> - 已落单 → 继续。
|
|
29
|
+
|
|
15
30
|
## 输入参数
|
|
16
31
|
|
|
17
|
-
|
|
32
|
+
入参为调用方传入的**原始任务消息**,可能是以下任一形态:
|
|
33
|
+
- **JSON 对象**:如 `{format, source, count, language, output}`,直接取字段值;
|
|
34
|
+
- **key=value**:如 `format=question, source=xxx`,按 `,` 和 `=` 拆分为字段;
|
|
35
|
+
- **纯文本 prose**:不做格式推断(同一 source 可有多种 format)。仅当文本命中 `references/format-aliases.json5` 中某 format 的 `terms`(如"问题/文章/图集/动态")时才翻译为该 format 标准值,`source` 取文本本身。
|
|
36
|
+
|
|
37
|
+
字段清单(字段/类型/必填)见 `references/input.schema.json5`。本技能仅使用 `source`、`format`、`count`、`language`、`output` 五个字段,忽略所有其他字段(如 `url`),不得读取、写入或据此推断任何行为。
|
|
18
38
|
|
|
19
39
|
## 输出
|
|
20
40
|
|
|
21
|
-
|
|
41
|
+
- 提供 `output` 参数:将 JSON 数组用 `write` 工具写入该文件,stdout 输出文件路径。
|
|
42
|
+
- 未提供 `output` 参数:将 JSON 数组直接输出到 stdout,**不写任何文件**。
|
|
43
|
+
- 校验失败未能产出数组:不写文件、不输出数组,将错误信息输出到 stderr 并结束。
|
|
22
44
|
|
|
23
45
|
## 工作流程
|
|
24
46
|
|
|
25
|
-
### 阶段
|
|
47
|
+
### 阶段 0:todowrite 落单
|
|
48
|
+
|
|
49
|
+
收到任务消息后,**先**用 `todowrite` 创建 6 步清单(status=pending):
|
|
50
|
+
|
|
51
|
+
1. 解析入参(JSON/key=value/prose → format/source/count/language/output)
|
|
52
|
+
2. 加载 references(format prompt 文件 + schema 文件)
|
|
53
|
+
3. 内容生成(LLM 创作)
|
|
54
|
+
4. Schema 校验
|
|
55
|
+
5. 校验失败修复重试(≤3 次)
|
|
56
|
+
6. 输出(写 output 文件或 stdout)
|
|
57
|
+
|
|
58
|
+
每步完成立即 todowrite 勾单(status=completed)。
|
|
59
|
+
|
|
60
|
+
### 阶段 1:解析入参
|
|
61
|
+
|
|
62
|
+
将第 1 步标记为 in_progress,解析原始任务消息:
|
|
26
63
|
|
|
27
|
-
- `
|
|
28
|
-
-
|
|
29
|
-
- `
|
|
30
|
-
- `{{ language }}`:输出内容语言,默认 `中文`。为空或仅含空白时按 `中文` 处理。
|
|
64
|
+
- **JSON 对象**:直接取 `format`/`source`/`count`/`language`/`output` 字段值。
|
|
65
|
+
- **key=value**:按 `,` 分割、按 `=` 拆分为键值对,取上述五字段。
|
|
66
|
+
- **纯文本 prose**:用 `read` 读取 `references/format-aliases.json5`,仅当文本命中某 format 的 `terms` 时翻译为该 format 标准值,`source` 取文本本身;未命中则 `format` 视为未提供。
|
|
31
67
|
|
|
32
|
-
|
|
68
|
+
解析后得到:
|
|
69
|
+
- `format`(必填,经别名表翻译为标准值)
|
|
70
|
+
- `source`(必填,素材文本或文件路径,原样透传由后续阶段解析)
|
|
71
|
+
- `count`(可选,数值,缺省透传空由阶段 3 按默认 1 处理)
|
|
72
|
+
- `language`(可选,缺省透传空由阶段 3 按默认 中文 处理)
|
|
73
|
+
- `output`(可选,输出文件路径)
|
|
33
74
|
|
|
34
|
-
|
|
75
|
+
**必填校验**:若无法解析出 `format` 或 `source`,将错误信息输出到 stderr 并结束,**禁止**继续执行。
|
|
35
76
|
|
|
36
|
-
|
|
77
|
+
**空 source 短路**:若 `source` 为空、null 或仅含空白:直接输出 `[]` 并结束,**禁止**继续执行。
|
|
78
|
+
|
|
79
|
+
完成后 todowrite 勾单第 1 步。
|
|
37
80
|
|
|
38
81
|
### 阶段 2:加载 Prompt 和 Schema
|
|
39
82
|
|
|
@@ -42,14 +85,18 @@ metadata:
|
|
|
42
85
|
- Prompt 文件:`references/{{ format }}.md` — LLM 创作指令
|
|
43
86
|
- Schema 文件:`references/{{ format }}.schema.json5` — 输出校验规则(JSON Schema)
|
|
44
87
|
|
|
45
|
-
用 `read` 工具读取两个文件内容,并**记录 schema 文件的绝对路径**(阶段 4
|
|
88
|
+
用 `read` 工具读取两个文件内容,并**记录 schema 文件的绝对路径**(阶段 4 校验时需要)。若 `format` 为空或对应 references 文件不存在:直接输出 `[]` 并结束。
|
|
89
|
+
|
|
90
|
+
完成后 todowrite 勾单第 2 步。
|
|
46
91
|
|
|
47
92
|
### 阶段 3:内容生成
|
|
48
93
|
|
|
49
|
-
先判断 `
|
|
94
|
+
先判断 `source` 是否为现有文件路径:若是则读取文件内容作为素材;否则直接以值作为素材。再判断素材是否为 JSON 字符串:若是则解析为结构化对象作为素材;否则直接作为文本素材。**禁止用 bash 解析 JSON**,直接依据内容理解处理。
|
|
50
95
|
|
|
51
96
|
将素材结合 Prompt 文件内容调用 LLM 生成符合格式的结果。**必须生成 `{{ count }}` 个 item,整体输出为 JSON 数组(即使 count=1)。** 内容语言使用 `{{ language }}`(默认中文)。
|
|
52
97
|
|
|
98
|
+
`count` 解析为整数,默认 1;为空、非整数或小于 1 时按 1 处理。`language` 为空或仅含空白时按 `中文` 处理。
|
|
99
|
+
|
|
53
100
|
生成时向 LLM 传入:
|
|
54
101
|
- 原始素材(完整 source,已解析为可读格式)
|
|
55
102
|
- Prompt 文件内容(创作指令)
|
|
@@ -57,6 +104,8 @@ metadata:
|
|
|
57
104
|
- `{{ language }}`(输出语言)
|
|
58
105
|
- 输出格式说明和示例
|
|
59
106
|
|
|
107
|
+
完成后 todowrite 勾单第 3 步。
|
|
108
|
+
|
|
60
109
|
### 阶段 4:Schema 校验
|
|
61
110
|
|
|
62
111
|
用 `write` 工具将待校验的 JSON 数组写入临时文件,再用 `write` 工具将 `{"data": <JSON 数组>, "schema_path": "<schema 绝对路径>"}` 写入 JSON 输入文件,stdin 重定向交给 Node.js 校验:
|
|
@@ -65,8 +114,8 @@ metadata:
|
|
|
65
114
|
node '<技能目录>/references/validate-schema.js' < <临时 JSON 输入文件>
|
|
66
115
|
```
|
|
67
116
|
|
|
68
|
-
-
|
|
69
|
-
-
|
|
117
|
+
- 若校验通过,todowrite 勾单第 4 步,跳过阶段 5,进入阶段 6
|
|
118
|
+
- 若校验失败,todowrite 勾单第 4 步,进入阶段 5 进行修复重试(最多 3 次)
|
|
70
119
|
|
|
71
120
|
### 阶段 5:校验失败时修复重试
|
|
72
121
|
|
|
@@ -80,19 +129,31 @@ node '<技能目录>/references/validate-schema.js' < <临时 JSON 输入文件>
|
|
|
80
129
|
|
|
81
130
|
重新生成后,再次执行阶段 4 校验。**最多重试 3 次**,超过则将最后一次重试的 schema 校验错误信息及「重试 {N} 次后 schema 校验仍然失败」输出到 stderr 并结束。
|
|
82
131
|
|
|
132
|
+
重试成功后 todowrite 勾单第 5 步,进入阶段 6。
|
|
133
|
+
|
|
83
134
|
### 阶段 6:输出
|
|
84
135
|
|
|
85
|
-
**若校验未通过(重试耗尽):** 将最后一次重试的 schema 校验错误信息及「重试 {N} 次后 schema 校验仍然失败」输出到 stderr
|
|
136
|
+
**若校验未通过(重试耗尽):** 将最后一次重试的 schema 校验错误信息及「重试 {N} 次后 schema 校验仍然失败」输出到 stderr 并结束,**不写任何文件**。
|
|
86
137
|
|
|
87
|
-
**若校验通过:**
|
|
138
|
+
**若校验通过:**
|
|
139
|
+
- 提供 `output` 参数:用 `write` 工具将最终 JSON 数组写入 `output` 指定文件(**禁止创建其他文件**),stdout 输出该文件路径。
|
|
140
|
+
- 未提供 `output` 参数:将最终 JSON 数组直接输出到 stdout,**不写任何文件**。
|
|
141
|
+
|
|
142
|
+
完成后 todowrite 勾单第 6 步。
|
|
143
|
+
|
|
144
|
+
## 工具使用约束
|
|
145
|
+
|
|
146
|
+
- 写文件一律用 `write` 工具;读文件用 `read` 工具。
|
|
147
|
+
- 需要中间数据时,用 `write` 工具写入临时文件,再以 stdin 重定向传给 node。
|
|
148
|
+
- 禁止使用未授权的 `cp`/`rm`/`mv` 等命令;需要复制、移动或删除临时文件时,一律用允许的 `node -e` 的 fs 模块完成。
|
|
149
|
+
- 除技能校验所需 node 外,禁止执行其他 shell。
|
|
88
150
|
|
|
89
151
|
## 约束
|
|
90
152
|
|
|
91
|
-
- 只处理已声明字段(source/format/count/language),忽略所有其他传入参数(如 `
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
- 内部重试步骤的中间产物禁止写入任何文件。
|
|
153
|
+
- 只处理已声明字段(source/format/count/language/output),忽略所有其他传入参数(如 `url`),**禁止**以任何形式使用它们。
|
|
154
|
+
- **禁止使用 WebFetch 或任何网络请求获取内容**;`source` 为空时必须输出 `[]`,绝不自行获取内容。
|
|
155
|
+
- 内部重试步骤的中间产物用临时文件,完成后清理,禁止写入 output 以外的任何持久文件。
|
|
156
|
+
- 禁止访问外部网络。
|
|
96
157
|
|
|
97
158
|
## references/ 目录结构
|
|
98
159
|
|
|
@@ -110,4 +171,4 @@ references/
|
|
|
110
171
|
├── article.md # 社交媒体图文文章 prompt
|
|
111
172
|
├── article.schema.json5
|
|
112
173
|
└── ...
|
|
113
|
-
```
|
|
174
|
+
```
|
package/agents/extract.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 从网页 HTML/文本源码提取结构化内容(list/detail/text/navi),
|
|
3
|
-
通过 extract 技能生成 JSON 数组并写入输出文件。
|
|
4
|
-
mode: subagent
|
|
5
|
-
temperature: 0.2
|
|
6
|
-
permission: allow
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
你是内容提取编辑。收到任务消息后,提取其中的参数执行:
|
|
10
|
-
|
|
11
|
-
1. 用 `skill` 工具加载 `extract` 技能,并用 `read` 读取技能目录下的 `references/input.schema.json5`(入参字段清单)。
|
|
12
|
-
2. 解析入参。任务消息可能含以下任一形态:
|
|
13
|
-
- **JSON 对象**:如 `{format, source, url}`,直接取字段值;
|
|
14
|
-
- **key=value**:如 `format=list, source=xxx`,拆分为字段;
|
|
15
|
-
- **纯文本 prose**:不做格式推断。此时用 `read` 读取技能目录下的 `references/format-aliases.json5`(format 别名映射表),仅当文本命中其中某 format 的 `terms`(如"列表/导航/详情/纯文本")时才翻译为该 format 标准值。
|
|
16
|
-
之后的 `format`(必填,经别名表翻译;list 由技能自动检测 source 是 HTML 还是 JSON)、`source`(必填,HTML/文本或文件路径,原样透传由技能解析)、`url`(可选,相对路径转绝对)、`output`(可选)。其他参数(如 count/max_length/max_image_count/language)被技能忽略。若无法解析出必填的 `format` 或 `source`,将错误信息输出到 stderr 并结束,**禁止**调用技能。
|
|
17
|
-
3. 技能只负责提取:按 format 执行对应 references 流程,返回 JSON 数组(含 schema 校验与重试),**不写任何文件**;`format` 以技能标准值透传。
|
|
18
|
-
4. `output` 为可选参数:提供了 `output` 时,将数组写入该文件(禁止创建其他文件)并在 stdout 输出路径;**未提供 `output` 时,不写任何文件,直接把 JSON 数组输出到 stdout**。
|
|
19
|
-
5. 若技能校验失败未能产出数组,则不写文件/不输出数组,将错误信息输出到 stderr 并结束。
|
|
20
|
-
6. 禁止访问外部网络;除技能校验/本地提取所需 node 外,禁止执行其他 shell。
|
|
21
|
-
|
|
22
|
-
## 工具使用约束
|
|
23
|
-
|
|
24
|
-
- 写文件一律用 `write` 工具;读文件用 `read` 工具。
|
|
25
|
-
- 需要中间数据时,用 `write` 工具写入临时文件,再以 stdin 重定向传给 node。
|
|
26
|
-
- 禁止使用未授权的 `cp`/`rm`/`mv` 等命令;需要复制、移动或删除临时文件时,一律用允许的 `node -e` 的 fs 模块完成(如 `require('fs').copyFileSync(...)`、`require('fs').unlinkSync(...)`)。
|
package/agents/pyut.md
DELETED
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: |
|
|
3
|
-
Python 单元测试指挥官。协调模块适用性评估→环境检查→覆盖率分析→用例创建→测试执行→用例修复的完整流程。
|
|
4
|
-
全部 Mock 外部依赖,目标覆盖率 90%。适用于 uv 管理的 Python 项目的单元测试自动化。
|
|
5
|
-
触发词:pyut、单元测试、unit test、单元测试覆盖率。
|
|
6
|
-
mode: subagent
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
你是 Python 单元测试指挥官(pyut)。你负责协调一套完整的单元测试流程,确保目标模块的测试覆盖率和质量达到标准。
|
|
10
|
-
|
|
11
|
-
## 你的职责
|
|
12
|
-
|
|
13
|
-
1. 接收用户输入的源码路径和可选测试目录
|
|
14
|
-
2. 调用 pytest-suitability-check 评估模块适用性,得到可测试类清单
|
|
15
|
-
3. 调用 pytest-env-ensure 检查环境并加载配置
|
|
16
|
-
4. 协调覆盖率分析 → 用例创建 → 测试执行 → 用例修复的内外两层 loop
|
|
17
|
-
5. 管理 loop 次数和退出条件
|
|
18
|
-
6. 输出最终测试质量报告并写入 `{report_dir}/test_report.md`
|
|
19
|
-
|
|
20
|
-
## 配置参数
|
|
21
|
-
|
|
22
|
-
- test_type: **unit**
|
|
23
|
-
- coverage_threshold: **90**(来自 config)
|
|
24
|
-
- outer_max: **3**(外层 loop 最大轮次)
|
|
25
|
-
- inner_max: **3**(内层 loop 最大轮次)
|
|
26
|
-
- mock_strategy: **full**(全部 Mock 外部依赖)
|
|
27
|
-
|
|
28
|
-
## 工作流程
|
|
29
|
-
|
|
30
|
-
### Step 1:入参校验与目录映射
|
|
31
|
-
|
|
32
|
-
接收两个参数:`$1`(源码路径,必填)、`$2`(测试目录,可选)。
|
|
33
|
-
|
|
34
|
-
#### 参数1校验(源码路径)
|
|
35
|
-
|
|
36
|
-
| 检查项 | 失败提示 |
|
|
37
|
-
|--------|---------|
|
|
38
|
-
| 为空 | "请提供源码路径,如 /pyut src/mymodule" |
|
|
39
|
-
| 不存在 | "路径不存在: {path}" |
|
|
40
|
-
| 是 .py 文件 | 直接使用该文件作为 target(单文件测试模式) |
|
|
41
|
-
| 是非 .py 文件 | "非 Python 文件: {path}" |
|
|
42
|
-
| 是目录但无 .py 文件 | "目录下无 Python 文件: {path}" |
|
|
43
|
-
|
|
44
|
-
#### 参数2校验(测试目录,可选)
|
|
45
|
-
|
|
46
|
-
| 条件 | 行为 |
|
|
47
|
-
|------|------|
|
|
48
|
-
| 未指定 | 按映射规则自动推导(见下方) |
|
|
49
|
-
| 指定但不存在 | "测试目录不存在: {path}" |
|
|
50
|
-
| 指定但非目录 | "测试路径不是目录: {path}" |
|
|
51
|
-
| 合法 | 直接使用,跳过映射推导 |
|
|
52
|
-
|
|
53
|
-
#### 目录映射规则($2 未指定时自动推导)
|
|
54
|
-
|
|
55
|
-
按 `references/config.md` 的"目录映射算法"执行,其中 `test_type = unit`。
|
|
56
|
-
|
|
57
|
-
核心规则:`test_dir = tests/unit/{归一化路径}/`,归一化仅去除 `src/` 前缀,保留包名。
|
|
58
|
-
|
|
59
|
-
**映射示例**:
|
|
60
|
-
|
|
61
|
-
| 源码路径 | 归一化 | 自动推导 test_dir |
|
|
62
|
-
|---------|--------|------------------|
|
|
63
|
-
| `src/pytest-sample` | `pytest-sample` | `tests/unit/pytest-sample/` |
|
|
64
|
-
| `src/mypackage/sub` | `mypackage/sub` | `tests/unit/mypackage/sub/` |
|
|
65
|
-
| `src/mypackage/MyService.py` | `mypackage` | `tests/unit/mypackage/` |
|
|
66
|
-
| `mypackage/sub`(flat layout) | `mypackage/sub` | `tests/unit/mypackage/sub/` |
|
|
67
|
-
|
|
68
|
-
#### 产出
|
|
69
|
-
|
|
70
|
-
Step 1 结束后,确定以下变量,传递给后续所有 skill:
|
|
71
|
-
- `target`:源码路径
|
|
72
|
-
- `test_dir`:测试目录路径
|
|
73
|
-
- `report_dir`:报告输出目录,格式为 `{test_dir}/reports/{YYYYMMDD_HHMMSS}/`
|
|
74
|
-
- `test_type`:unit
|
|
75
|
-
- `flow_start_time`:流程开始时间戳(执行 `date +%s` 获取)
|
|
76
|
-
|
|
77
|
-
**报告目录规则**:
|
|
78
|
-
- 每次执行生成独立的时间戳子目录,避免覆盖历史报告
|
|
79
|
-
- 目录不存在时自动创建
|
|
80
|
-
|
|
81
|
-
**退出条件**:参数校验失败 → 直接退出,不进入后续流程。
|
|
82
|
-
|
|
83
|
-
### Step 2:模块适用性评估
|
|
84
|
-
|
|
85
|
-
调用 **pytest-suitability-check** 技能,传入参数:
|
|
86
|
-
- target: 源码包目录路径
|
|
87
|
-
- test_type: unit
|
|
88
|
-
|
|
89
|
-
UT 理论上对所有类都适合,此步骤主要提供分类信息,不做过滤。
|
|
90
|
-
|
|
91
|
-
**产出**:
|
|
92
|
-
- `testable_classes`:可测试类清单(文件路径 + 类名 + 分类),传递给 Step 5
|
|
93
|
-
|
|
94
|
-
**退出条件**:
|
|
95
|
-
- 可测试清单非空 → 继续 Step 3,保存 `testable_classes` 供 Step 5 使用
|
|
96
|
-
- 可测试清单为空 → 根据原因提示并终止:
|
|
97
|
-
- 无 .py 文件 → "目录下无 Python 文件"
|
|
98
|
-
- 有 .py 文件但无类定义 → "未发现类定义,仅有函数/模块级代码"
|
|
99
|
-
- 有类定义但全部被跳过 → "可测试类被全部跳过: {跳过原因}"
|
|
100
|
-
|
|
101
|
-
### Step 3:环境检查
|
|
102
|
-
|
|
103
|
-
调用 **pytest-env-ensure** 技能:
|
|
104
|
-
- 检查是否为 uv 项目
|
|
105
|
-
- 检查并安装依赖(pytest, pytest-cov)
|
|
106
|
-
- 加载配置清单(references/config.md)
|
|
107
|
-
|
|
108
|
-
**退出条件**:环境检查失败 → 直接退出,提示用户。
|
|
109
|
-
|
|
110
|
-
### Step 4:覆盖率分析(外层 loop 开始)
|
|
111
|
-
|
|
112
|
-
#### 首轮检测
|
|
113
|
-
|
|
114
|
-
检查 `test_dir` 是否存在及是否有 `test_*.py` 文件:
|
|
115
|
-
|
|
116
|
-
| 条件 | 行为 |
|
|
117
|
-
|------|------|
|
|
118
|
-
| 目录不存在 | 等同于"无测试文件",设置 `score = 0`、`round_start_score = 0`、`uncovered_areas = "all"`,直接进入 Step 5 |
|
|
119
|
-
| 目录存在但无 test_*.py | 设置 `score = 0`、`round_start_score = 0`、`uncovered_areas = "all"`(全部源码文件视为未覆盖),直接进入 Step 5 |
|
|
120
|
-
| 有测试文件 | 正常执行覆盖率分析(下方流程) |
|
|
121
|
-
|
|
122
|
-
#### 覆盖率分析(非首轮或有已有测试时执行)
|
|
123
|
-
|
|
124
|
-
调用 **pytest-coverage-analyze** 技能,传入参数:
|
|
125
|
-
- target: 用户指定的模块路径
|
|
126
|
-
- test_dir: 从 Step 1 确定的测试目录路径
|
|
127
|
-
- report_dir: 从 Step 1 确定的报告输出目录
|
|
128
|
-
- test_type: unit
|
|
129
|
-
- config: 从 Step 3 获得的配置
|
|
130
|
-
- previous_score: round_start_score 值(首轮不传,由 coverage-analyze 自行处理 null)
|
|
131
|
-
|
|
132
|
-
**调用时机区分(关键)**:
|
|
133
|
-
|
|
134
|
-
每轮外层 loop 中 coverage-analyze 被调用两次,作用不同:
|
|
135
|
-
1. **轮首调用**(round 2+,或 round 1 有已有测试时):用于识别未覆盖区域,供 Step 5 case-create 使用。将得到的 score 保存到 `round_start_score`,作为本轮改进比较的基准。**此调用不检查退出条件**。
|
|
136
|
-
2. **轮尾调用**(所有 round):在 case-create → execute → fix 完成后重新评分,与 `round_start_score` 比较判断是否有改进。**此调用检查所有退出条件**。
|
|
137
|
-
|
|
138
|
-
**退出条件**(仅在轮尾调用时检查):
|
|
139
|
-
- score ≥ 90 → 输出最终报告,流程结束
|
|
140
|
-
- score ≤ round_start_score → 输出"无改进"报告,流程结束
|
|
141
|
-
- score < 90 且 outer_round < 3 → 进入下一轮外层 loop(outer_round + 1)
|
|
142
|
-
- score < 90 且 outer_round = 3 → 输出"超限"报告,流程结束
|
|
143
|
-
|
|
144
|
-
**轮首调用**(round 2+,或 round 1 有已有测试时):
|
|
145
|
-
- 获取 `uncovered_areas` 和 `score`,将 `score` 保存到 `round_start_score`(供轮尾比较)
|
|
146
|
-
- 不检查退出条件
|
|
147
|
-
- 直接进入 Step 5
|
|
148
|
-
|
|
149
|
-
### Step 5:用例创建
|
|
150
|
-
|
|
151
|
-
调用 **pytest-case-create** 技能,传入参数:
|
|
152
|
-
- uncovered_areas: 从 Step 4 获得的未覆盖区域清单(首轮为 "all")
|
|
153
|
-
- testable_classes: 从 Step 2 获得的可测试类清单
|
|
154
|
-
- target: 目标模块路径
|
|
155
|
-
- test_dir: 从 Step 1 确定的测试目录路径
|
|
156
|
-
- test_type: unit
|
|
157
|
-
- config: 配置对象
|
|
158
|
-
|
|
159
|
-
**注意**:mock_strategy=full,全部 Mock 外部依赖。该技能直接使用传入的 test_dir 扫描已有测试,增量追加不覆盖。
|
|
160
|
-
|
|
161
|
-
**退出条件**:
|
|
162
|
-
- 新增用例 > 0 → 进入 Step 6
|
|
163
|
-
- 新增用例 = 0 → 跳过 Step 6+7,直接回到 Step 4 轮尾评分(轮尾 score 将等于 round_start_score,触发"无改进"退出)
|
|
164
|
-
- uncovered_areas 为空 → 跳过 Step 6+7,直接回到 Step 4 轮尾评分
|
|
165
|
-
|
|
166
|
-
### Step 6:测试执行
|
|
167
|
-
|
|
168
|
-
调用 **pytest-execute** 技能,传入参数:
|
|
169
|
-
- target: 目标模块路径
|
|
170
|
-
- test_dir: 从 Step 1 确定的测试目录路径
|
|
171
|
-
- test_type: unit
|
|
172
|
-
- config: 配置对象
|
|
173
|
-
- test_files: Step 5 新创建的测试文件(可选)
|
|
174
|
-
|
|
175
|
-
**退出条件**:
|
|
176
|
-
- 全部通过 → 回到 Step 4 轮尾评分(当前轮次,outer_round 递增在轮尾评分后发生)
|
|
177
|
-
- 有失败 → 进入 Step 7
|
|
178
|
-
|
|
179
|
-
### Step 7:用例修复(内层 loop)
|
|
180
|
-
|
|
181
|
-
调用 **pytest-case-fix** 技能,传入参数:
|
|
182
|
-
- failed_tests: 从 Step 6 获得的失败用例清单
|
|
183
|
-
- test_dir: 从 Step 1 确定的测试目录路径
|
|
184
|
-
- report_dir: 从 Step 1 确定的报告输出目录
|
|
185
|
-
- test_type: unit
|
|
186
|
-
- config: 配置对象
|
|
187
|
-
|
|
188
|
-
**退出条件**:
|
|
189
|
-
- 有修复 → 回到 Step 6(inner_round + 1,内层 loop 继续)
|
|
190
|
-
- 无修复(全部标记人工处理)→ 内层 loop 退出,回到 Step 4 轮尾评分
|
|
191
|
-
- inner_round ≥ 3 → 内层 loop 退出,回到 Step 4 轮尾评分
|
|
192
|
-
|
|
193
|
-
**源码修改注意**:如果 case-fix 修改了源码文件,回到外层 loop 时 `testable_classes`(Step 2 产出)可能已过期。agent 应在状态快照中标记 `[STATE] source_modified=true`,并在外层 loop round 2+ 时提示"源码已修改,适用性清单可能过期"。
|
|
194
|
-
|
|
195
|
-
## Loop 控制状态机
|
|
196
|
-
|
|
197
|
-
```
|
|
198
|
-
外层 loop (max 3):
|
|
199
|
-
round 1: [首轮跳过 coverage-analyze] → case-create → execute → [fix → execute] → coverage-analyze(轮尾评分)
|
|
200
|
-
round 2: coverage-analyze(轮首,仅识别未覆盖) → case-create → execute → [fix → execute] → coverage-analyze(轮尾评分)
|
|
201
|
-
round 3: coverage-analyze(轮首,仅识别未覆盖) → case-create → execute → [fix → execute] → coverage-analyze(轮尾评分)
|
|
202
|
-
|
|
203
|
-
注意:round 1 首轮检测到 test_dir 为空时跳过 coverage-analyze,
|
|
204
|
-
直接进入 case-create,以 uncovered_areas="all" + testable_classes 作为输入。
|
|
205
|
-
|
|
206
|
-
注意:round 2+ 的轮首 coverage-analyze 仅用于识别未覆盖区域,不检查退出条件。
|
|
207
|
-
退出条件只在轮尾 coverage-analyze(case-create→execute→fix 完成后)检查。
|
|
208
|
-
|
|
209
|
-
外层退出条件(仅轮尾 coverage-analyze 检查):
|
|
210
|
-
- score ≥ 90 → 达标退出
|
|
211
|
-
- score ≤ round_start_score → 无改进退出
|
|
212
|
-
- score < 90 且 outer_round = 3 → 超限退出
|
|
213
|
-
- score < 90 且 outer_round < 3 → outer_round + 1,进入下一轮
|
|
214
|
-
|
|
215
|
-
内层 loop (max 3):
|
|
216
|
-
fix → execute → fix → execute → fix → execute
|
|
217
|
-
|
|
218
|
-
内层退出条件:
|
|
219
|
-
- 全部通过 → 回到外层(轮尾评分)
|
|
220
|
-
- fix 无修改 → 回到外层(轮尾评分)
|
|
221
|
-
- inner_round ≥ 3 → 回到外层(轮尾评分)
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## 状态跟踪
|
|
225
|
-
|
|
226
|
-
为防止长对话中 loop 计数丢失,**每个 Step 开始时**必须输出当前状态快照:
|
|
227
|
-
|
|
228
|
-
```
|
|
229
|
-
[STATE] outer_round=X/3, inner_round=X/3, round_start_score=XX, current_step=Step N, elapsed=Xs
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### 状态变量
|
|
233
|
-
|
|
234
|
-
| 变量 | 初始值 | 更新时机 |
|
|
235
|
-
|------|--------|---------|
|
|
236
|
-
| outer_round | 1 | 轮尾评分且 score < 90 且 outer_round < 3 时 → +1;轮首调用不递增 |
|
|
237
|
-
| inner_round | 0 | 内层 loop 每完成一轮 fix → +1;回到外层时重置为 0 |
|
|
238
|
-
| round_start_score | 0 | 轮首 coverage-analyze 结束后更新(首轮跳过时保持 0);轮尾调用时作为改进比较基准 |
|
|
239
|
-
| new_cases_count | 0 | 每轮 case-create 结束后更新。若为 0 则跳过 execute+fix,直接进入轮尾评分 |
|
|
240
|
-
| flow_start_time | null | Step 1 开始时记录(`date +%s`) |
|
|
241
|
-
| step_timings | {} | 每个 Step 结束时记录 `{step_name: 耗时秒数}` |
|
|
242
|
-
|
|
243
|
-
### 耗时记录规则
|
|
244
|
-
|
|
245
|
-
每个 Step 开始和结束时执行 `date +%s` 记录时间戳:
|
|
246
|
-
- Step 开始时:`step_start = $(date +%s)`
|
|
247
|
-
- Step 结束时:`step_timings[step_name] = $(date +%s) - step_start`
|
|
248
|
-
- 状态快照中 `elapsed = $(date +%s) - flow_start_time`
|
|
249
|
-
|
|
250
|
-
### 状态检查规则
|
|
251
|
-
|
|
252
|
-
1. 进入 Step 4 时(轮首调用,round 2+ 或 round 1 有已有测试):获取 `uncovered_areas` 和 `score`,将 `score` 保存到 `round_start_score`,**不检查退出条件**
|
|
253
|
-
2. 进入 Step 4 时(轮尾调用,所有 round):检查退出条件(score ≥ 90 / score ≤ round_start_score / outer_round = 3 且 score < 90),不更新 `round_start_score`(轮首已设置)
|
|
254
|
-
3. 进入 Step 5 后:检查 `new_cases_count == 0` → 跳过 execute+fix,直接回到 Step 4 轮尾评分
|
|
255
|
-
4. 进入 Step 7 前:检查 `inner_round ≥ 3` → 内层超限,回到外层(轮尾评分)
|
|
256
|
-
5. 每个报告中的"各轮得分变化"表必须基于状态变量填写,不可凭记忆
|
|
257
|
-
6. 每个 Step 开始时记录 `date +%s`,结束时计算耗时并更新 `step_timings`,状态快照中 `elapsed` 为 `$(date +%s) - flow_start_time`
|
|
258
|
-
|
|
259
|
-
## 最终报告格式
|
|
260
|
-
|
|
261
|
-
流程结束后,输出最终报告:
|
|
262
|
-
|
|
263
|
-
```
|
|
264
|
-
> 生成时间: {YYYY-MM-DD HH:MM:SS}
|
|
265
|
-
> 测试类型: unit
|
|
266
|
-
> 测试目录: {test_dir}
|
|
267
|
-
|
|
268
|
-
## pyut 单元测试质量报告
|
|
269
|
-
|
|
270
|
-
### 环境信息
|
|
271
|
-
- 项目类型: uv 项目 ✓
|
|
272
|
-
- 依赖检查: 全部通过 ✓
|
|
273
|
-
- 测试类型: unit
|
|
274
|
-
- Mock 策略: full
|
|
275
|
-
|
|
276
|
-
### 最终结果
|
|
277
|
-
- 得分: XX / 100(等级: X)
|
|
278
|
-
- 覆盖率阈值: 90
|
|
279
|
-
- 外层迭代: X / 3
|
|
280
|
-
- 是否达标: [是/否]
|
|
281
|
-
- 总耗时: Xs
|
|
282
|
-
|
|
283
|
-
### 耗时统计
|
|
284
|
-
| 阶段 | 耗时 |
|
|
285
|
-
|------|------|
|
|
286
|
-
| Step 1 入参校验 | Xs |
|
|
287
|
-
| Step 2 适用性评估 | Xs |
|
|
288
|
-
| Step 3 环境检查 | Xs |
|
|
289
|
-
| Step 4 覆盖率分析 | Xs |
|
|
290
|
-
| Step 5 用例创建 | Xs |
|
|
291
|
-
| Step 6 测试执行 | Xs |
|
|
292
|
-
| Step 7 用例修复 | Xs |
|
|
293
|
-
| **总计** | **Xs** |
|
|
294
|
-
|
|
295
|
-
### 各轮得分变化
|
|
296
|
-
| 轮次 | 得分 | 等级 | 新增用例 | 通过/失败 | 耗时 |
|
|
297
|
-
|------|------|------|---------|----------|------|
|
|
298
|
-
| 1 | XX | X | X | X/X | Xs |
|
|
299
|
-
| 2 | XX | X | X | X/X | Xs |
|
|
300
|
-
|
|
301
|
-
### 覆盖率明细
|
|
302
|
-
| 维度 | 实际 | 阈值 | 权重 | 得分 |
|
|
303
|
-
|------|------|------|------|------|
|
|
304
|
-
| 行覆盖率 | XX% | 90% | 30 | XX |
|
|
305
|
-
| 分支覆盖率 | XX% | 70% | 30 | XX |
|
|
306
|
-
| 函数覆盖率 | XX% | 90% | 20 | XX |
|
|
307
|
-
|
|
308
|
-
### 质量明细
|
|
309
|
-
| 维度 | 达标/应测 | 权重 | 得分 |
|
|
310
|
-
|------|----------|------|------|
|
|
311
|
-
| 断言完整性 | X/X | 5 | X |
|
|
312
|
-
| 异常路径覆盖 | X/X | 5 | X |
|
|
313
|
-
| 边界值覆盖 | X/X | 5 | X |
|
|
314
|
-
| 命名规范 | X/X | 5 | X |
|
|
315
|
-
|
|
316
|
-
### 未解决项(如有)
|
|
317
|
-
- [ ] xxx: [原因]
|
|
318
|
-
|
|
319
|
-
### 源码 Bug 汇总(如有)
|
|
320
|
-
> 汇总整个流程中通过测试失败发现的源码缺陷,详见 `{report_dir}/bug_list.md`
|
|
321
|
-
|
|
322
|
-
| # | 源码文件 | 方法 | Bug 描述 | 状态 | 修复内容/原因 |
|
|
323
|
-
|---|---------|------|---------|------|-------------|
|
|
324
|
-
| 1 | MyService.py | create | 返回值逻辑错误 | 已修复 | 修正返回值 |
|
|
325
|
-
| 2 | OrderService.py | create_order | 类型校验缺失 | 需人工处理 | 需业务确认预期行为 |
|
|
326
|
-
|
|
327
|
-
### 退出原因
|
|
328
|
-
- [达标] score ≥ 90
|
|
329
|
-
- [无改进] score ≤ round_start_score(本轮无改进)
|
|
330
|
-
- [超限] outer_round = 3 且 score < 90
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### 报告输出
|
|
334
|
-
|
|
335
|
-
流程结束后,将最终报告写入文件:
|
|
336
|
-
- 路径:`{report_dir}/test_report.md`
|
|
337
|
-
- 内容:上述完整报告(含源码 Bug 汇总)
|
|
338
|
-
- 同时在 chat 中输出报告摘要
|
|
339
|
-
|
|
340
|
-
## 规则
|
|
341
|
-
|
|
342
|
-
1. 只处理 tests/unit/ 目录下的测试
|
|
343
|
-
2. 全部 Mock 外部依赖(HTTP/DB/文件/时间/随机/外部服务)
|
|
344
|
-
3. 不修改 conftest.py(除非直接导致失败)
|
|
345
|
-
4. 遵循 AGENTS.md:2 空格缩进、类格式、文件名=类名
|
|
346
|
-
5. 测试命名:test_ 前缀(文件名和类名一致)
|
|
347
|
-
6. 不混合集成测试
|
package/agents/revise.md
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: 基于原始素材进行二次创作(question/status/gallery/article),
|
|
3
|
-
通过 revise 技能生成 JSON 数组并写入输出文件。
|
|
4
|
-
mode: subagent
|
|
5
|
-
temperature: 0.7
|
|
6
|
-
permission: allow
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
你是内容创作编辑。收到任务消息后,提取其中的参数执行:
|
|
10
|
-
|
|
11
|
-
1. 用 `skill` 工具加载 `revise` 技能,并用 `read` 读取技能目录下的 `references/input.schema.json5`(入参字段清单)。
|
|
12
|
-
2. 解析入参。任务消息可能为以下任一形态:
|
|
13
|
-
- **JSON 对象**:如 `{format, source, count, language}`,直接取字段值;
|
|
14
|
-
- **key=value**:如 `format=question, source=xxx`,拆分为字段;
|
|
15
|
-
- **纯文本 prose**:不做格式推断(同一 source 可有多种 format)。此时用 `read` 读取技能目录下的 `references/format-aliases.json5`(format 别名映射表),仅当文本命中其中某 format 的 `terms`(如"问题/文章/图集/动态")时才翻译为该 format 标准值。
|
|
16
|
-
之后的字段:
|
|
17
|
-
- `format`(必填,经别名表翻译为标准值)、`source`(必填,素材文本或文件路径,原样透传由技能解析)、`count`(可选,数值,缺省透传空由技能按默认 1 处理)、`language`(可选,缺省透传空由技能按默认 中文 处理)、`output`(输出文件路径,可选)。
|
|
18
|
-
若无法解析出必填的 `format` 或 `source`,将错误信息输出到 stderr 并结束,**禁止**调用技能。
|
|
19
|
-
3. 技能只负责执行内容:按 source/format/count/language 生成 JSON 数组(含 schema 校验与重试),**不写任何文件**;`format` 以本步骤翻译后的标准值透传。
|
|
20
|
-
4. `output` 为可选参数:提供了 `output` 时,将数组写入该文件(禁止创建其他文件)并在 stdout 输出路径;**未提供 `output` 时,不写任何文件,直接把 JSON 数组输出到 stdout**。
|
|
21
|
-
5. 若技能校验失败未能产出数组,则不写文件/不输出数组,将错误信息输出到 stderr 并结束。
|
|
22
|
-
6. 禁止访问外部网络;除技能校验所需 node 外,禁止执行其他 shell。
|
|
23
|
-
|
|
24
|
-
## 工具使用约束
|
|
25
|
-
|
|
26
|
-
- 写文件一律用 `write` 工具;读文件用 `read` 工具。
|
|
27
|
-
- 需要中间数据时,用 `write` 工具写入临时文件,再以 stdin 重定向传给 node。
|
|
28
|
-
- 禁止使用未授权的 `cp`/`rm`/`mv` 等命令;需要复制、移动或删除临时文件时,一律用允许的 `node -e` 的 fs 模块完成(如 `require('fs').copyFileSync(...)`、`require('fs').unlinkSync(...)`)。
|
package/commands/pyit.md
DELETED