@kairyou/agent-tools 0.22.0 → 0.23.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 +15 -2
- package/README.zh-CN.md +15 -2
- package/docs/en/extras.md +21 -21
- package/docs/zh-CN/extras.md +19 -19
- package/package.json +1 -1
- package/skills/systems/at-zentao/SKILL.md +32 -11
- package/skills/systems/at-zentao/scripts/zentao-cli.mjs +48 -2
package/README.md
CHANGED
|
@@ -70,7 +70,20 @@ Usage:
|
|
|
70
70
|
- `/at-zentao task <id> [request]` — work a task, start/pause/resume it, or log hours with a natural-language request
|
|
71
71
|
- `/at-zentao story <id>` — read requirement scope and acceptance context without writing back
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
Configure `~/.agent-tools/config.jsonc`:
|
|
74
|
+
|
|
75
|
+
```jsonc
|
|
76
|
+
{
|
|
77
|
+
"zentao": {
|
|
78
|
+
"url": "https://zentao.example.com",
|
|
79
|
+
"account": "user",
|
|
80
|
+
"password": "your-password",
|
|
81
|
+
// "password": { "env": "ZENTAO_PASSWORD" }, // Or read the password from an environment variable.
|
|
82
|
+
// Configure commentPrompt to customize bug/task comment formatting.
|
|
83
|
+
// "commentPrompt": "Use exactly this multiline format and field order:\nRoot cause: ...\nFix: ...\nBranch: ...\nVerification: ... (omit when not performed)\nCommit: ..."
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
74
87
|
|
|
75
88
|
## Capabilities
|
|
76
89
|
|
|
@@ -224,7 +237,7 @@ npx -y @kairyou/agent-tools@latest inspect-image <path|url> -q "What are the nav
|
|
|
224
237
|
|
|
225
238
|
## Extras
|
|
226
239
|
|
|
227
|
-
See [extras](docs/en/extras.md) for
|
|
240
|
+
See [extras](docs/en/extras.md) for optional tools outside the day-to-day development workflow.
|
|
228
241
|
|
|
229
242
|
## FAQ
|
|
230
243
|
|
package/README.zh-CN.md
CHANGED
|
@@ -70,7 +70,20 @@ npx -y skills@latest add kairyou/agent-tools --skill at-zentao -g -y
|
|
|
70
70
|
- `/at-zentao task <id> [请求]` — 处理指定 task, 用自然语言开始/暂停/继续或记录工时
|
|
71
71
|
- `/at-zentao story <id>` — 只读查看研发需求范围和验收信息
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
配置 `~/.agent-tools/config.jsonc`:
|
|
74
|
+
|
|
75
|
+
```jsonc
|
|
76
|
+
{
|
|
77
|
+
"zentao": {
|
|
78
|
+
"url": "https://zentao.example.com",
|
|
79
|
+
"account": "user",
|
|
80
|
+
"password": "your-password",
|
|
81
|
+
// "password": { "env": "ZENTAO_PASSWORD" }, // 或者从环境变量读取密码.
|
|
82
|
+
// 需要自定义 bug/task 评论(备注)格式时, 可配置 commentPrompt.
|
|
83
|
+
// "commentPrompt": "严格使用以下多行格式和字段顺序:\n原因: ...\n修复: ...\n分支: ...\n验证: ...(未验证时省略)\nCommit: ..."
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
74
87
|
|
|
75
88
|
## Capabilities
|
|
76
89
|
|
|
@@ -218,7 +231,7 @@ npx -y @kairyou/agent-tools@latest inspect-image <path|url> -q "导航栏的背
|
|
|
218
231
|
|
|
219
232
|
## Extras
|
|
220
233
|
|
|
221
|
-
[extras](docs/zh-CN/extras.md)
|
|
234
|
+
[extras](docs/zh-CN/extras.md) 介绍日常开发主流程之外的可选工具.
|
|
222
235
|
|
|
223
236
|
## FAQ
|
|
224
237
|
|
package/docs/en/extras.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Extras
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This page lists optional tools outside the day-to-day development workflow.
|
|
4
4
|
|
|
5
5
|
## at-self-eval
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
for
|
|
7
|
+
Turn Git commit history, a work log you provide, or both into a performance
|
|
8
|
+
self-evaluation for quarterly reviews, half-year reviews, or promotion materials.
|
|
9
9
|
|
|
10
10
|
```bash
|
|
11
11
|
npx -y skills@latest add kairyou/agent-tools --skill at-self-eval -g -y
|
|
@@ -25,7 +25,7 @@ Review the generated result before use.
|
|
|
25
25
|
|
|
26
26
|
## at-daily-log
|
|
27
27
|
|
|
28
|
-
Distill each day's Git commits across projects into a work report
|
|
28
|
+
Distill each day's Git commits across projects into a work report.
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
npx -y skills@latest add kairyou/agent-tools --skill at-daily-log -g -y
|
|
@@ -35,12 +35,12 @@ Usage:
|
|
|
35
35
|
|
|
36
36
|
- `/at-daily-log` — print today's log
|
|
37
37
|
- `/at-daily-log 2026-07-31` — pick the date
|
|
38
|
-
- `/at-daily-log last week` / `/at-daily-log the last two weeks` — one
|
|
38
|
+
- `/at-daily-log last week` / `/at-daily-log the last two weeks` — one report per date with Git commits; dates without commits are skipped
|
|
39
39
|
- `/at-daily-log summarize today for C:\projects\project-a` — only that project
|
|
40
40
|
- `/at-daily-log also include C:\projects\project-c` — add to the default scope
|
|
41
41
|
- `/at-daily-log record the log` — record to the configured file; rerunning a day only refreshes the generated part
|
|
42
42
|
- `/at-daily-log record it to C:\logs\daily-log.md` — record to that file
|
|
43
|
-
- `/at-daily-log record daily at 18:00` — guides you through an OS scheduled task;
|
|
43
|
+
- `/at-daily-log record daily at 18:00` — guides you through an OS scheduled task; no report is written when the day has no Git commits
|
|
44
44
|
|
|
45
45
|
Recorded output example:
|
|
46
46
|
|
|
@@ -73,21 +73,6 @@ npx -y @kairyou/agent-tools@latest log -a claude codex opencode
|
|
|
73
73
|
- `format: "daily"`: writes to a single Markdown file grouped by date; each completed answer with substantive results is recorded as one line, while pending prompts are omitted; content may be truncated by the length limit, so use it only as a lightweight activity index
|
|
74
74
|
- Codex: run `/hooks` once after installing to approve it; opencode: restart after installing or updating
|
|
75
75
|
|
|
76
|
-
`daily` output example:
|
|
77
|
-
|
|
78
|
-
```markdown
|
|
79
|
-
+ 2026-08-03
|
|
80
|
-
<!-- log:2026-08-03:start -->
|
|
81
|
-
1. project-a: Found the cause of the login timeout: the renewal branch never updated the cache expiry, so the second request still read the old value. Patched session.ts and verified...
|
|
82
|
-
2. project-a: Fix is in, with 3 regression tests covering the renewal path; all passing.
|
|
83
|
-
3. project-b: Empty report exports came from an inverted permission filter, now corrected and confirmed working.
|
|
84
|
-
<!-- log:2026-08-03:end -->
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
One line per turn, taken verbatim from the closing text of that turn's AI reply
|
|
88
|
-
(truncated when long); no distilling, no cross-turn consolidation. Updates likewise
|
|
89
|
-
rewrite only what sits between the markers.
|
|
90
|
-
|
|
91
76
|
`detailed` output example (excerpt):
|
|
92
77
|
|
|
93
78
|
```markdown
|
|
@@ -114,6 +99,21 @@ Changes
|
|
|
114
99
|
- src/auth/session.ts | diff +42/-8 | 3 ops
|
|
115
100
|
```
|
|
116
101
|
|
|
102
|
+
`daily` output example:
|
|
103
|
+
|
|
104
|
+
```markdown
|
|
105
|
+
+ 2026-08-03
|
|
106
|
+
<!-- log:2026-08-03:start -->
|
|
107
|
+
1. project-a: Found the cause of the login timeout: the renewal branch never updated the cache expiry, so the second request still read the old value. Patched session.ts and verified...
|
|
108
|
+
2. project-a: Fix is in, with 3 regression tests covering the renewal path; all passing.
|
|
109
|
+
3. project-b: Empty report exports came from an inverted permission filter, now corrected and confirmed working.
|
|
110
|
+
<!-- log:2026-08-03:end -->
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
One line per turn, taken verbatim from the closing text of that turn's AI reply
|
|
114
|
+
(truncated when long); no distilling, no cross-turn consolidation. Updates likewise
|
|
115
|
+
rewrite only what sits between the markers.
|
|
116
|
+
|
|
117
117
|
## Configuration
|
|
118
118
|
|
|
119
119
|
All in `~/.agent-tools/config.jsonc`:
|
package/docs/zh-CN/extras.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Extras
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
这里收录日常开发主流程之外的可选工具.
|
|
4
4
|
|
|
5
5
|
## at-self-eval
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
基于 Git 提交历史或你提供的工作日志生成绩效自评, 可用于季度总结, 半年总结及晋升材料撰写.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx -y skills@latest add kairyou/agent-tools --skill at-self-eval -g -y
|
|
@@ -24,7 +24,7 @@ npx -y skills@latest add kairyou/agent-tools --skill at-self-eval -g -y
|
|
|
24
24
|
|
|
25
25
|
## at-daily-log
|
|
26
26
|
|
|
27
|
-
按天把各项目的 Git
|
|
27
|
+
按天把各项目的 Git 提交提炼成工作日报.
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
npx -y skills@latest add kairyou/agent-tools --skill at-daily-log -g -y
|
|
@@ -34,12 +34,12 @@ npx -y skills@latest add kairyou/agent-tools --skill at-daily-log -g -y
|
|
|
34
34
|
|
|
35
35
|
- `/at-daily-log` — 输出今天的日报
|
|
36
36
|
- `/at-daily-log 2026-07-31` — 指定日期
|
|
37
|
-
- `/at-daily-log 上周` / `/at-daily-log 最近两周` —
|
|
37
|
+
- `/at-daily-log 上周` / `/at-daily-log 最近两周` — 按日期生成日报, 跳过没有 Git 提交的日期
|
|
38
38
|
- `/at-daily-log 统计 C:\projects\project-a 今天的工作` — 只统计指定项目
|
|
39
39
|
- `/at-daily-log 另外包含 C:\projects\project-c` — 在默认范围上追加
|
|
40
40
|
- `/at-daily-log 记录日报` — 记录到配置的文件; 同一天重复执行只更新生成的部分
|
|
41
41
|
- `/at-daily-log 记录到 C:\logs\daily-log.md` — 记录到指定文件
|
|
42
|
-
- `/at-daily-log 每天 18:00 自动记录` — 引导搭建系统定时任务;
|
|
42
|
+
- `/at-daily-log 每天 18:00 自动记录` — 引导搭建系统定时任务; 当天没有 Git 提交时不写入日报
|
|
43
43
|
|
|
44
44
|
记录到文件的示例:
|
|
45
45
|
|
|
@@ -71,20 +71,6 @@ npx -y @kairyou/agent-tools@latest log -a claude codex opencode
|
|
|
71
71
|
- `format: "daily"`: 写入单个 Markdown 文件, 并按日期归档; 每个已完成且有实质结果的回答记录为一行, 不记录未完成的提问; 内容可能因长度限制被截断, 因此仅适合作为轻量活动索引
|
|
72
72
|
- Codex 安装后运行 `/hooks` 批准一次; opencode 安装或更新后需要重启
|
|
73
73
|
|
|
74
|
-
`daily` 输出示例:
|
|
75
|
-
|
|
76
|
-
```markdown
|
|
77
|
-
+ 2026-08-03
|
|
78
|
-
<!-- log:2026-08-03:start -->
|
|
79
|
-
1. project-a: 已定位登录超时的原因: 会话缓存在续期分支上没有更新过期时间, 第二次请求拿到的还是旧值。已经在 session.ts 补上续期并本地验证通过, 接下来...
|
|
80
|
-
2. project-a: 修复完成, 新增 3 个回归用例覆盖续期路径, 全部通过。
|
|
81
|
-
3. project-b: 报表导出为空定位到权限过滤条件写反, 已修正并确认导出恢复正常。
|
|
82
|
-
<!-- log:2026-08-03:end -->
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
每轮一行, 直接摘录那一轮 AI 回复的收尾内容(过长会截断), 不做提炼也不跨轮归纳;
|
|
86
|
-
同样只重写标记之间的部分, 文件里的其他内容不会被碰.
|
|
87
|
-
|
|
88
74
|
`detailed` 输出示例(节选):
|
|
89
75
|
|
|
90
76
|
```markdown
|
|
@@ -111,6 +97,20 @@ Changes
|
|
|
111
97
|
- src/auth/session.ts | 变更 +42/-8 | 操作 3 次
|
|
112
98
|
```
|
|
113
99
|
|
|
100
|
+
`daily` 输出示例:
|
|
101
|
+
|
|
102
|
+
```markdown
|
|
103
|
+
+ 2026-08-03
|
|
104
|
+
<!-- log:2026-08-03:start -->
|
|
105
|
+
1. project-a: 已定位登录超时的原因: 会话缓存在续期分支上没有更新过期时间, 第二次请求拿到的还是旧值. 已经在 session.ts 补上续期并本地验证通过, 接下来...
|
|
106
|
+
2. project-a: 修复完成, 新增 3 个回归用例覆盖续期路径, 全部通过.
|
|
107
|
+
3. project-b: 报表导出为空定位到权限过滤条件写反, 已修正并确认导出恢复正常.
|
|
108
|
+
<!-- log:2026-08-03:end -->
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
每轮一行, 直接摘录那一轮 AI 回复的收尾内容(过长会截断), 不做提炼也不跨轮归纳;
|
|
112
|
+
同样只重写标记之间的部分, 文件里的其他内容不会被碰.
|
|
113
|
+
|
|
114
114
|
## 配置
|
|
115
115
|
|
|
116
116
|
都在 `~/.agent-tools/config.jsonc`:
|
package/package.json
CHANGED
|
@@ -78,6 +78,12 @@ node <skill-root>/scripts/zentao-cli.mjs hours task <id>
|
|
|
78
78
|
`get` downloads token-gated inline images and attachments into a temporary
|
|
79
79
|
directory by default and returns only local paths. Inspect those local files;
|
|
80
80
|
never pass the original ZenTao URL to an image tool.
|
|
81
|
+
For Bugs and Tasks, a configured `zentao.commentPrompt` is returned as
|
|
82
|
+
`writeback.commentPrompt`. Use it only when drafting a write-back comment; it
|
|
83
|
+
is not item data or an existing comment. `get` also returns safe `comments`
|
|
84
|
+
from action history, keeping only id, actor, action, date, and non-empty
|
|
85
|
+
comment. An omitted `comments` means actions were unavailable; an empty array
|
|
86
|
+
means actions were available but contained no comments.
|
|
81
87
|
|
|
82
88
|
Write commands require JSON on stdin and are allowed only after the explicit
|
|
83
89
|
confirmation steps below:
|
|
@@ -98,11 +104,11 @@ Input shapes:
|
|
|
98
104
|
|
|
99
105
|
```json
|
|
100
106
|
{"comment":"Root cause and result."}
|
|
101
|
-
{"resolution":"fixed","resolvedBuild":"trunk","comment":"Root cause
|
|
107
|
+
{"resolution":"fixed","resolvedBuild":"trunk","comment":"Root cause: stale session cache; Fix: refresh it during renewal; Commit: abc1234."}
|
|
102
108
|
{"realStarted":"2026-08-11 09:00:00","comment":"Started implementation."}
|
|
103
109
|
{"date":"2026-08-11","consumed":2,"left":14,"work":"Implemented the first part of the task."}
|
|
104
110
|
{"work":"Corrected work description, commit abc1234."}
|
|
105
|
-
{"currentConsumed":1.5,"realStarted":"2026-08-11 09:00:00","finishedDate":"2026-08-11 10:30:00"}
|
|
111
|
+
{"currentConsumed":1.5,"realStarted":"2026-08-11 09:00:00","finishedDate":"2026-08-11 10:30:00","comment":"Completed: implemented session renewal; Commit: abc1234."}
|
|
106
112
|
```
|
|
107
113
|
|
|
108
114
|
For `duplicate`, also pass `"duplicateBug": <id>`. Send JSON through stdin,
|
|
@@ -114,7 +120,7 @@ the task's current hours when starting or resuming it.
|
|
|
114
120
|
`log-hours` defaults `date` to today, requires positive remaining hours,
|
|
115
121
|
and keeps the task open. `hours` is read-only. `edit-hours` preserves omitted
|
|
116
122
|
fields from the existing record and updates it through ZenTao's native effort
|
|
117
|
-
workflow. Use `finish` when the task is complete.
|
|
123
|
+
workflow. Use `finish` when the task is complete; its `comment` is optional.
|
|
118
124
|
|
|
119
125
|
## Usage
|
|
120
126
|
|
|
@@ -159,9 +165,11 @@ the current hours and never implies new consumed time.
|
|
|
159
165
|
|
|
160
166
|
Follow these steps in order:
|
|
161
167
|
|
|
162
|
-
1. **Fetch details** — use the CLI `get` command.
|
|
163
|
-
|
|
164
|
-
|
|
168
|
+
1. **Fetch details** — use the CLI `get` command. Review every returned comment,
|
|
169
|
+
including comments attached to resolution, activation, and lifecycle actions.
|
|
170
|
+
Read every downloaded image now so screenshots inform the fix. If no
|
|
171
|
+
image-inspection tool is available, continue from text and state that
|
|
172
|
+
screenshots were skipped.
|
|
165
173
|
2. **Restate and confirm** — explain the problem and intended fix in your own
|
|
166
174
|
words. Ask before editing when the item is ambiguous.
|
|
167
175
|
3. **Locate the code** — search the current project and explain how the relevant
|
|
@@ -185,8 +193,20 @@ Follow these steps in order:
|
|
|
185
193
|
|
|
186
194
|
Bug resolutions are `fixed`, `notrepro`, `duplicate`, `bydesign`, `external`,
|
|
187
195
|
`postponed`, and `willnotfix`. Choose what matches the verified outcome. A
|
|
188
|
-
write-back comment
|
|
189
|
-
the
|
|
196
|
+
write-back comment follows the current user request, then
|
|
197
|
+
`writeback.commentPrompt`. Otherwise use the item or user's language and these
|
|
198
|
+
defaults:
|
|
199
|
+
|
|
200
|
+
- For a fixed Bug, use `Root cause`, `Fix`, optional `Verification`, and the
|
|
201
|
+
real `Commit`.
|
|
202
|
+
- For a finished Task, use `Completed`, optional `Verification`, and `Commit`
|
|
203
|
+
only when the work has a related commit.
|
|
204
|
+
- For other Bug resolutions, use `Conclusion` and applicable `Reason` or
|
|
205
|
+
`Evidence`.
|
|
206
|
+
- Keep standalone and lifecycle comments free-form but concise.
|
|
207
|
+
|
|
208
|
+
Formatting controls wording and layout only. Never invent missing facts;
|
|
209
|
+
surface required gaps and put `Commit: <hash>` last.
|
|
190
210
|
|
|
191
211
|
Immediately before confirming any ZenTao write that cites the latest commit,
|
|
192
212
|
run `git rev-parse HEAD` and `git log -1 --format=%h`. Do not reuse a hash from
|
|
@@ -200,9 +220,10 @@ current entry and make that estimate editable in the confirmation; ask only
|
|
|
200
220
|
when no reliable suggestion is possible. Draft `work` from the verified result
|
|
201
221
|
when context is available. It is optional, so mention the omission without
|
|
202
222
|
blocking the write when there is nothing useful to add. For a completed task,
|
|
203
|
-
collect `currentConsumed
|
|
204
|
-
|
|
205
|
-
|
|
223
|
+
collect `currentConsumed`, draft the completion comment from established facts,
|
|
224
|
+
and include both in the `finish` write. Never infer consumed hours or a
|
|
225
|
+
verification result. Show all submitted values and require the same explicit
|
|
226
|
+
ZenTao confirmation before either write.
|
|
206
227
|
|
|
207
228
|
To correct an existing time entry, use `hours` to select its effort id. Show
|
|
208
229
|
the current and proposed `date`, `consumed`, `left`, and `work`, then obtain
|
|
@@ -9,6 +9,8 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
const JSON_LIMIT = 4 * 1024 * 1024;
|
|
10
10
|
const BINARY_LIMIT = 20 * 1024 * 1024;
|
|
11
11
|
const INPUT_LIMIT = 1024 * 1024;
|
|
12
|
+
// Keep write-back guidance compact enough to remain useful in an agent response.
|
|
13
|
+
const COMMENT_PROMPT_LIMIT = 1000;
|
|
12
14
|
const SECRET_KEYS = /^(?:password|token|authorization|cookie|set-cookie)$/i;
|
|
13
15
|
const RESOLUTIONS = new Set([
|
|
14
16
|
"fixed",
|
|
@@ -148,6 +150,7 @@ export function loadConfig({ env = process.env, file = configFile(env) } = {}) {
|
|
|
148
150
|
const rawAccount = env.ZENTAO_ACCOUNT || section.account;
|
|
149
151
|
const rawPassword = env.ZENTAO_PASSWORD || section.password;
|
|
150
152
|
const rawToken = env.ZENTAO_TOKEN;
|
|
153
|
+
const rawCommentPrompt = section.commentPrompt;
|
|
151
154
|
const urlText = resolveValue(rawUrl, env, "zentao.url");
|
|
152
155
|
let parsedUrl;
|
|
153
156
|
try {
|
|
@@ -171,12 +174,23 @@ export function loadConfig({ env = process.env, file = configFile(env) } = {}) {
|
|
|
171
174
|
const token = resolveValue(rawToken, env, "zentao.token", { required: false });
|
|
172
175
|
const account = resolveValue(rawAccount, env, "zentao.account", { required: !token });
|
|
173
176
|
const password = resolveValue(rawPassword, env, "zentao.password", { required: !token });
|
|
177
|
+
if (rawCommentPrompt !== undefined && typeof rawCommentPrompt !== "string") {
|
|
178
|
+
throw new CliError("config_error", "zentao.commentPrompt must be a string");
|
|
179
|
+
}
|
|
180
|
+
const commentPrompt = rawCommentPrompt?.trim() || null;
|
|
181
|
+
if (commentPrompt && commentPrompt.length > COMMENT_PROMPT_LIMIT) {
|
|
182
|
+
throw new CliError(
|
|
183
|
+
"config_error",
|
|
184
|
+
`zentao.commentPrompt must not exceed ${COMMENT_PROMPT_LIMIT} characters`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
174
187
|
return {
|
|
175
188
|
url: parsedUrl.href.replace(/\/$/, ""),
|
|
176
189
|
account,
|
|
177
190
|
password,
|
|
178
191
|
token,
|
|
179
192
|
tokenOnly: Boolean(token),
|
|
193
|
+
commentPrompt,
|
|
180
194
|
secrets: [account, password, token].filter(Boolean),
|
|
181
195
|
};
|
|
182
196
|
}
|
|
@@ -385,6 +399,23 @@ function pick(source, keys) {
|
|
|
385
399
|
return output;
|
|
386
400
|
}
|
|
387
401
|
|
|
402
|
+
function normalizeComments(actions) {
|
|
403
|
+
const entries = Array.isArray(actions) ? actions : Object.values(actions || {});
|
|
404
|
+
const comments = [];
|
|
405
|
+
for (const entry of entries) {
|
|
406
|
+
if (!entry || typeof entry !== "object" || typeof entry.comment !== "string") continue;
|
|
407
|
+
const comment = entry.comment.trim();
|
|
408
|
+
if (!comment) continue;
|
|
409
|
+
const output = {};
|
|
410
|
+
for (const key of ["id", "actor", "action", "date"]) {
|
|
411
|
+
if (typeof entry[key] === "string" || typeof entry[key] === "number") output[key] = entry[key];
|
|
412
|
+
}
|
|
413
|
+
output.comment = comment;
|
|
414
|
+
comments.push(output);
|
|
415
|
+
}
|
|
416
|
+
return comments;
|
|
417
|
+
}
|
|
418
|
+
|
|
388
419
|
function normalizeDetail(kind, response) {
|
|
389
420
|
const container = response?.data && typeof response.data === "object" ? response.data : response;
|
|
390
421
|
const detail = container?.[kind] || container;
|
|
@@ -414,7 +445,12 @@ function normalizeDetail(kind, response) {
|
|
|
414
445
|
} else if (kind === "story") {
|
|
415
446
|
fields.push("stage", "category", "plan", "estimate", "spec", "verify", "source", "sourceNote");
|
|
416
447
|
}
|
|
417
|
-
|
|
448
|
+
const safe = pick(detail, fields);
|
|
449
|
+
const actions = detail.actions ?? container?.actions;
|
|
450
|
+
if ((kind === "bug" || kind === "task") && actions && typeof actions === "object") {
|
|
451
|
+
safe.comments = normalizeComments(actions);
|
|
452
|
+
}
|
|
453
|
+
return { raw: detail, safe };
|
|
418
454
|
}
|
|
419
455
|
|
|
420
456
|
function attachmentUrls(detail) {
|
|
@@ -682,7 +718,13 @@ export async function run(argv, { env = process.env } = {}) {
|
|
|
682
718
|
const resource = kind === "story" ? "stories" : `${kind}s`;
|
|
683
719
|
const detail = normalizeDetail(kind, await client.json(`api.php/v1/${resource}/${id}`));
|
|
684
720
|
const attachments = await downloadAttachments(client, detail.raw, directory);
|
|
685
|
-
return {
|
|
721
|
+
return {
|
|
722
|
+
item: detail.safe,
|
|
723
|
+
attachments,
|
|
724
|
+
...(kind !== "story" && config.commentPrompt
|
|
725
|
+
? { writeback: { commentPrompt: config.commentPrompt } }
|
|
726
|
+
: {}),
|
|
727
|
+
};
|
|
686
728
|
}
|
|
687
729
|
|
|
688
730
|
if (command === "comment") {
|
|
@@ -842,6 +884,9 @@ export async function run(argv, { env = process.env } = {}) {
|
|
|
842
884
|
const input = await readInput();
|
|
843
885
|
const current = Number(input.currentConsumed);
|
|
844
886
|
if (!Number.isFinite(current) || current <= 0) throw new CliError("usage_error", "currentConsumed must be positive");
|
|
887
|
+
if (input.comment !== undefined && typeof input.comment !== "string") {
|
|
888
|
+
throw new CliError("usage_error", "comment must be a string when provided");
|
|
889
|
+
}
|
|
845
890
|
const form = decodeLegacy(await client.json(`task-finish-${id}.json`));
|
|
846
891
|
const task = form?.task || {};
|
|
847
892
|
const previous = Number(task.consumed || 0);
|
|
@@ -855,6 +900,7 @@ export async function run(argv, { env = process.env } = {}) {
|
|
|
855
900
|
consumed: previous + current,
|
|
856
901
|
realStarted,
|
|
857
902
|
finishedDate: input.finishedDate || localDateTime(),
|
|
903
|
+
comment: input.comment?.trim(),
|
|
858
904
|
}),
|
|
859
905
|
});
|
|
860
906
|
return legacyResult(response);
|