@kairyou/agent-tools 0.22.0 → 0.23.1
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/.claude-plugin/plugin.json +17 -0
- package/.codex-plugin/plugin.json +7 -0
- package/.zcode-plugin/plugin.json +17 -0
- package/README.md +28 -9
- package/README.zh-CN.md +26 -9
- package/capabilities/usage/lib/context.mjs +1 -0
- package/capabilities/vision/lib/tool-contract.mjs +14 -0
- package/capabilities/vision/mcp-server.mjs +5 -13
- package/capabilities/vision/pi-extension.mjs +77 -0
- package/dist/usage/core.mjs +1 -0
- package/dist/vision/mcp-server.mjs +471 -149
- package/dist/vision/pi-extension.mjs +6306 -0
- package/docs/en/extras.md +21 -21
- package/docs/en/repository-structure.md +3 -1
- package/docs/zh-CN/extras.md +19 -19
- package/docs/zh-CN/repository-structure.md +3 -1
- package/package.json +10 -5
- package/scripts/build.mjs +1 -0
- package/scripts/install.mjs +265 -10
- package/scripts/sync-plugin-version.mjs +22 -0
- package/skills/systems/at-zentao/SKILL.md +32 -11
- package/skills/systems/at-zentao/scripts/zentao-cli.mjs +48 -2
|
@@ -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);
|