@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 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
- Config: `~/.agent-tools/config.jsonc` → `"zentao": { "url", "account", "password" }`. The password can be entered directly or referenced with `"password": { "env": "ZENTAO_PASSWORD" }`; never paste credentials or tokens into chat.
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 situational content.
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
- 配置: `~/.agent-tools/config.jsonc` → `"zentao": { "url", "account", "password" }`. 密码可以直接填写, 也可以使用 `"password": { "env": "ZENTAO_PASSWORD" }` 引用环境变量; 不要在对话中粘贴凭据或 token.
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
- Fully usable, just situational. Install each on demand.
3
+ This page lists optional tools outside the day-to-day development workflow.
4
4
 
5
5
  ## at-self-eval
6
6
 
7
- Summarize git history, a work log you provide, or both into a self-evaluation
8
- for performance cycles (quarterly, semi-annual, promotion).
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; runs when invoked.
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 entry per active day, empty days skipped
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; days without commits are not written
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`:
@@ -1,10 +1,10 @@
1
1
  # Extras
2
2
 
3
- 完整可用, 只是场景较窄. 按需单独安装.
3
+ 这里收录日常开发主流程之外的可选工具.
4
4
 
5
5
  ## at-self-eval
6
6
 
7
- git 历史或你提供的工作日志归纳成自评产出清单, 用于季度/半年/晋升等绩效周期.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kairyou/agent-tools",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Reusable Agent Skills, plus runtime capabilities (statusline, provider usage, vision) for Codex, Claude Code, and opencode.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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 and result, commit abc1234."}
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. Read every downloaded image
163
- now so screenshots inform the fix. If no image-inspection tool is available,
164
- continue from text and state that screenshots were skipped.
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 is one sentence containing root cause, change summary, and
189
- the commit hash when committed.
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` and draft a `finish` write. Never infer consumed
204
- hours. Show all submitted values and require the same explicit ZenTao
205
- confirmation before either write.
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
- return { raw: detail, safe: pick(detail, fields) };
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 { item: detail.safe, attachments };
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);