@kairyou/agent-tools 0.10.0 → 0.10.2
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 +11 -5
- package/README.zh-CN.md +12 -6
- package/dist/usage/core.mjs +49 -4
- package/docs/en/extras.md +24 -0
- package/docs/zh-CN/custom-gateway-routes.md +1 -1
- package/docs/zh-CN/extras.md +21 -0
- package/integrations/usage/lib/format.mjs +56 -4
- package/package.json +2 -2
- package/skills/workflow/at-self-eval/SKILL.md +186 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent Tools
|
|
2
2
|
|
|
3
|
-
Reusable Agent Skills
|
|
3
|
+
Reusable Agent Skills, plus integrations (statusline, provider usage, vision) that install into Codex, Claude Code, and opencode.
|
|
4
4
|
|
|
5
5
|
Requires Node.js >= 22.
|
|
6
6
|
|
|
@@ -153,21 +153,23 @@ Output examples:
|
|
|
153
153
|
|
|
154
154
|
```text
|
|
155
155
|
# Plan limits (sub2api / openai-compatible).
|
|
156
|
-
D $0.0/$100 | W $0.0/$300 | Exp 07-08
|
|
156
|
+
D $0.0/$100 | W $0.0/$300 ⟳3d1h | Exp 07-08
|
|
157
157
|
|
|
158
158
|
# Multi-window limits (claude-code-hub).
|
|
159
159
|
5h $2.1/$10.0 | D $8.0/$20.0 | T $19.0/$100 | Exp 08-31
|
|
160
160
|
|
|
161
161
|
# Balance and usage (one-api / one-hub / done-hub / new-api / openrouter).
|
|
162
|
-
balance $15.0 | used $5.0/$20.0
|
|
162
|
+
balance $15.0 | used $5.0/$20.0 | ⟳3d1h
|
|
163
163
|
|
|
164
164
|
# Wallet and recent spend (sub2api).
|
|
165
165
|
balance $362 | today $61.7 | 30d $566
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
Fields: `5h/D/W/M/T` are five-hour/daily/weekly/monthly/total spend against
|
|
169
|
-
limits;
|
|
170
|
-
credit; `today` and `30d` are
|
|
169
|
+
limits; `⟳` is the time until a known limit reset; `Exp` is the plan expiry;
|
|
170
|
+
`balance` is wallet credit; `used` is consumed credit; `today` and `30d` are
|
|
171
|
+
today's and the last 30 days' API spend. A reset countdown is shown only when
|
|
172
|
+
the gateway returns enough timing information to determine it reliably.
|
|
171
173
|
|
|
172
174
|
#### Custom gateway routes
|
|
173
175
|
|
|
@@ -216,6 +218,10 @@ To diagnose the provider setup or test recognition quality manually:
|
|
|
216
218
|
npx -y @kairyou/agent-tools@latest inspect-image <path|url> -q "What are the navbar background color and height?"
|
|
217
219
|
```
|
|
218
220
|
|
|
221
|
+
## Extras
|
|
222
|
+
|
|
223
|
+
See [extras](docs/en/extras.md) for situational content.
|
|
224
|
+
|
|
219
225
|
## Run from Git
|
|
220
226
|
|
|
221
227
|
To run directly from the repository, replace the npm package name with
|
package/README.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md)
|
|
4
4
|
|
|
5
|
-
可复用的 Agent Skills,
|
|
5
|
+
可复用的 Agent Skills, 以及可装入 Codex, Claude Code 与 opencode 的 integrations: statusline, provider usage 与跨模型识图.
|
|
6
6
|
|
|
7
7
|
需要 Node.js >= 22.
|
|
8
8
|
|
|
@@ -149,20 +149,22 @@ npx -y @kairyou/agent-tools@latest usage -a claude codex opencode
|
|
|
149
149
|
|
|
150
150
|
```text
|
|
151
151
|
# 套餐限额 (sub2api / openai-compatible).
|
|
152
|
-
D $0.0/$100 | W $0.0/$300 | Exp 07-08
|
|
152
|
+
D $0.0/$100 | W $0.0/$300 ⟳3d1h | Exp 07-08
|
|
153
153
|
|
|
154
154
|
# 多窗口限额 (claude-code-hub).
|
|
155
155
|
5h $2.1/$10.0 | D $8.0/$20.0 | T $19.0/$100 | Exp 08-31
|
|
156
156
|
|
|
157
157
|
# 余额与用量 (one-api / one-hub / done-hub / new-api / openrouter).
|
|
158
|
-
balance $15.0 | used $5.0/$20.0
|
|
158
|
+
balance $15.0 | used $5.0/$20.0 | ⟳3d1h
|
|
159
159
|
|
|
160
160
|
# 钱包与近期消耗 (sub2api).
|
|
161
161
|
balance $362 | today $61.7 | 30d $566
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
-
字段含义: `5h/D/W/M/T` 是 5 小时/日/周/月/总消耗与上限,
|
|
165
|
-
|
|
164
|
+
字段含义: `5h/D/W/M/T` 是 5 小时/日/周/月/总消耗与上限, `⟳` 后面是已知限额的
|
|
165
|
+
重置倒计时, `Exp` 是套餐到期日, `balance` 是钱包余额, `used` 是已用额度,
|
|
166
|
+
`today` / `30d` 是今日与近 30 天 API 消耗. 只有网关返回的信息足以可靠确定
|
|
167
|
+
重置时间时才会显示倒计时.
|
|
166
168
|
|
|
167
169
|
#### 自定义网关路由
|
|
168
170
|
|
|
@@ -171,7 +173,7 @@ balance $362 | today $61.7 | 30d $566
|
|
|
171
173
|
|
|
172
174
|
### Vision(跨模型识图)
|
|
173
175
|
|
|
174
|
-
让不支持图片的主模型借助多模态模型识图,
|
|
176
|
+
让不支持图片的主模型借助多模态模型识图, 适用于读取报错截图, 按设计稿还原 UI, 定位测试反馈截图里的界面问题.
|
|
175
177
|
|
|
176
178
|
#### 安装
|
|
177
179
|
|
|
@@ -211,6 +213,10 @@ npx -y @kairyou/agent-tools@latest vision -a claude codex opencode
|
|
|
211
213
|
npx -y @kairyou/agent-tools@latest inspect-image <path|url> -q "导航栏的背景色和高度是多少"
|
|
212
214
|
```
|
|
213
215
|
|
|
216
|
+
## Extras
|
|
217
|
+
|
|
218
|
+
[extras](docs/zh-CN/extras.md) 收录场景较窄的内容.
|
|
219
|
+
|
|
214
220
|
## 从 Git 运行
|
|
215
221
|
|
|
216
222
|
如需直接使用仓库版本, 可将 npm 包名替换为 `github:kairyou/agent-tools` (本机需已安装 Git):
|
package/dist/usage/core.mjs
CHANGED
|
@@ -1309,6 +1309,14 @@ async function requestJson(url, options = {}) {
|
|
|
1309
1309
|
|
|
1310
1310
|
// integrations/usage/lib/format.mjs
|
|
1311
1311
|
var ONE_API_HARD_LIMIT_SENTINEL_USD = 1e6;
|
|
1312
|
+
var RESET_TIME_FIELDS = Object.freeze({
|
|
1313
|
+
// Sub2API rate-limit entries returned alongside quota usage.
|
|
1314
|
+
SUB2API_RATE_LIMIT: "reset_at",
|
|
1315
|
+
// Sub2API /v1/usage response: subscription weekly-window anchor.
|
|
1316
|
+
SUB2API_SUBSCRIPTION_WEEKLY_START: "weekly_window_start",
|
|
1317
|
+
// OpenRouter /api/v1/key response: absolute key-limit reset.
|
|
1318
|
+
OPENROUTER_KEY_LIMIT: "limit_reset"
|
|
1319
|
+
});
|
|
1312
1320
|
function pickNumber(obj, keys) {
|
|
1313
1321
|
for (const key of keys) {
|
|
1314
1322
|
const value = obj?.[key];
|
|
@@ -1337,6 +1345,39 @@ function shortDate(value) {
|
|
|
1337
1345
|
const match = String(value).match(/^(\d{4})-(\d{2})-(\d{2})/);
|
|
1338
1346
|
return match ? `${match[2]}-${match[3]}` : "";
|
|
1339
1347
|
}
|
|
1348
|
+
function timestampMs(value) {
|
|
1349
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1350
|
+
return value < 1e12 ? value * 1e3 : value;
|
|
1351
|
+
}
|
|
1352
|
+
const parsed = Date.parse(value);
|
|
1353
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
1354
|
+
}
|
|
1355
|
+
function compactDurationUntil(value) {
|
|
1356
|
+
const resetMs = timestampMs(value);
|
|
1357
|
+
if (resetMs === void 0) return "";
|
|
1358
|
+
const remainingMs = resetMs - Date.now();
|
|
1359
|
+
if (remainingMs <= 0) return "";
|
|
1360
|
+
let seconds = Math.floor(remainingMs / 1e3);
|
|
1361
|
+
if (seconds <= 0) return "0m";
|
|
1362
|
+
const days = Math.floor(seconds / 86400);
|
|
1363
|
+
seconds %= 86400;
|
|
1364
|
+
const hours = Math.floor(seconds / 3600);
|
|
1365
|
+
seconds %= 3600;
|
|
1366
|
+
const minutes = Math.floor(seconds / 60);
|
|
1367
|
+
if (days) return `${days}d${hours}h`;
|
|
1368
|
+
if (hours) return `${hours}h${minutes}m`;
|
|
1369
|
+
return `${minutes}m`;
|
|
1370
|
+
}
|
|
1371
|
+
function rateLimitResetAt(entry) {
|
|
1372
|
+
const value = entry?.[RESET_TIME_FIELDS.SUB2API_RATE_LIMIT];
|
|
1373
|
+
return timestampMs(value) === void 0 ? void 0 : value;
|
|
1374
|
+
}
|
|
1375
|
+
function weeklyResetAt(subscription) {
|
|
1376
|
+
const startMs = timestampMs(
|
|
1377
|
+
subscription?.[RESET_TIME_FIELDS.SUB2API_SUBSCRIPTION_WEEKLY_START]
|
|
1378
|
+
);
|
|
1379
|
+
return startMs === void 0 ? void 0 : startMs + 7 * 86400 * 1e3;
|
|
1380
|
+
}
|
|
1340
1381
|
function hasSubscriptionLimits(root) {
|
|
1341
1382
|
const sub = root?.subscription || {};
|
|
1342
1383
|
return [
|
|
@@ -1437,7 +1478,7 @@ function formatOpenRouterLine(data) {
|
|
|
1437
1478
|
const limit = pickNumber(root, ["limit", "limit_remaining", "total_credits"]);
|
|
1438
1479
|
const remaining = pickNumber(root, ["limit_remaining", "remaining_credits"]);
|
|
1439
1480
|
const used = pickNumber(root, ["usage", "total_usage", "spend"]);
|
|
1440
|
-
const reset = root?.
|
|
1481
|
+
const reset = compactDurationUntil(root?.[RESET_TIME_FIELDS.OPENROUTER_KEY_LIMIT]);
|
|
1441
1482
|
const parts = [];
|
|
1442
1483
|
if (remaining !== void 0) parts.push(`balance ${formatMoney(remaining)}`);
|
|
1443
1484
|
if (used !== void 0 && limit !== void 0 && limit !== remaining) {
|
|
@@ -1445,7 +1486,7 @@ function formatOpenRouterLine(data) {
|
|
|
1445
1486
|
} else if (used !== void 0) {
|
|
1446
1487
|
parts.push(`used ${formatMoney(used)}`);
|
|
1447
1488
|
}
|
|
1448
|
-
if (reset) parts.push(
|
|
1489
|
+
if (reset) parts.push(`\u27F3${reset}`);
|
|
1449
1490
|
if (parts.length === 0) throw new Error("OpenRouter payload has no usage fields");
|
|
1450
1491
|
return parts.join(" | ");
|
|
1451
1492
|
}
|
|
@@ -1511,7 +1552,8 @@ function formatQuotaLimitedLine(root) {
|
|
|
1511
1552
|
const window = entry?.window;
|
|
1512
1553
|
const rateLimit = pickNumber(entry, ["limit"]);
|
|
1513
1554
|
const rateUsed = pickNumber(entry, ["used"]);
|
|
1514
|
-
|
|
1555
|
+
const reset = compactDurationUntil(rateLimitResetAt(entry));
|
|
1556
|
+
return window && rateLimit !== void 0 && rateUsed !== void 0 ? `${window} ${formatMoney(rateUsed)}/${formatMoney(rateLimit)}${reset ? ` \u27F3${reset}` : ""}` : "";
|
|
1515
1557
|
}).filter(Boolean);
|
|
1516
1558
|
if (rateParts.length > 0) parts.push(rateParts.join(", "));
|
|
1517
1559
|
}
|
|
@@ -1538,9 +1580,12 @@ function formatUsageLine(root) {
|
|
|
1538
1580
|
const monthlyLimit = pickNumber(sub, ["monthly_limit_usd"]);
|
|
1539
1581
|
const monthlyUsage = pickNumber(sub, ["monthly_usage_usd"]);
|
|
1540
1582
|
const expires = shortDate(sub.expires_at);
|
|
1583
|
+
const weeklyReset = compactDurationUntil(weeklyResetAt(sub));
|
|
1541
1584
|
const parts = [];
|
|
1542
1585
|
if (dailyLimit > 0 && dailyUsage !== void 0) parts.push(`D ${formatMoney(dailyUsage)}/${formatMoney(dailyLimit)}`);
|
|
1543
|
-
if (weeklyLimit > 0 && weeklyUsage !== void 0)
|
|
1586
|
+
if (weeklyLimit > 0 && weeklyUsage !== void 0) {
|
|
1587
|
+
parts.push(`W ${formatMoney(weeklyUsage)}/${formatMoney(weeklyLimit)}${weeklyReset ? ` \u27F3${weeklyReset}` : ""}`);
|
|
1588
|
+
}
|
|
1544
1589
|
if (monthlyLimit > 0 && monthlyUsage !== void 0) parts.push(`M ${formatMoney(monthlyUsage)}/${formatMoney(monthlyLimit)}`);
|
|
1545
1590
|
if (expires) parts.push(`Exp ${expires}`);
|
|
1546
1591
|
return parts.join(" | ");
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Extras
|
|
2
|
+
|
|
3
|
+
Fully usable, just situational. Install each on demand.
|
|
4
|
+
|
|
5
|
+
## at-self-eval
|
|
6
|
+
|
|
7
|
+
Summarize a contributor's git history into a review-friendly self-evaluation for
|
|
8
|
+
performance cycles (quarterly, semi-annual, promotion).
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx -y skills@latest add kairyou/agent-tools --skill at-self-eval -g -y
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
|
|
16
|
+
- `/at-self-eval` — current git identity, current quarter
|
|
17
|
+
- `/at-self-eval [<author>] [--from yyyy-mm-dd] [--to yyyy-mm-dd]` — pick author and range
|
|
18
|
+
- Plain language works too: say "summarize the first half of the year" instead of writing flags
|
|
19
|
+
- Paste a daily/weekly log, or give a file path, to add business context (optional)
|
|
20
|
+
- Mention other repositories or branches to aggregate across projects; a remote
|
|
21
|
+
URL prompts before cloning anything
|
|
22
|
+
|
|
23
|
+
Output is a draft: it never invents deliverables, and every line traces back to a
|
|
24
|
+
commit or to a log entry you confirmed. Review it before submitting.
|
|
@@ -36,7 +36,7 @@ export async function run(context, { requestJson, agentConfig }) {
|
|
|
36
36
|
const session = await login.json();
|
|
37
37
|
|
|
38
38
|
// requestJson 会解析 JSON, 并在非 2xx 响应时抛错; 需要时可在这里传入
|
|
39
|
-
// authorization
|
|
39
|
+
// authorization, cookie 等自定义认证 header.
|
|
40
40
|
const me = await requestJson(`${context.baseUrl}/api/user/self`, {
|
|
41
41
|
headers: { authorization: `Bearer ${session?.data?.accessToken}` },
|
|
42
42
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Extras
|
|
2
|
+
|
|
3
|
+
完整可用, 只是场景较窄. 按需单独安装.
|
|
4
|
+
|
|
5
|
+
## at-self-eval
|
|
6
|
+
|
|
7
|
+
把贡献者的 git 历史归纳成评审友好的自评产出清单, 用于季度/半年/晋升等绩效周期.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx -y skills@latest add kairyou/agent-tools --skill at-self-eval -g -y
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
用法:
|
|
14
|
+
|
|
15
|
+
- `/at-self-eval` — 默认当前 git 用户, 当前季度
|
|
16
|
+
- `/at-self-eval [<作者>] [--from yyyy-mm-dd] [--to yyyy-mm-dd]` — 指定作者与日期范围
|
|
17
|
+
- 在对话里直接说 `统计上半年的工作` / `统计 1-3月的产出` 即可, 无需手写参数
|
|
18
|
+
- 可粘贴日报/周报文本或提供文件路径, 补充业务背景(可选)
|
|
19
|
+
- 可提及其他仓库或分支以跨项目聚合; 远程地址会先询问是否 clone 到临时目录
|
|
20
|
+
|
|
21
|
+
输出是草稿: 不编造产出, 每条都能追溯到 commit 或你确认过的日志条目. 提交前请人工核对.
|
|
@@ -6,6 +6,15 @@ import { newApiQuotaScale, providerUsageDays } from "./config.mjs";
|
|
|
6
6
|
|
|
7
7
|
const ONE_API_HARD_LIMIT_SENTINEL_USD = 1_000_000;
|
|
8
8
|
|
|
9
|
+
const RESET_TIME_FIELDS = Object.freeze({
|
|
10
|
+
// Sub2API rate-limit entries returned alongside quota usage.
|
|
11
|
+
SUB2API_RATE_LIMIT: "reset_at",
|
|
12
|
+
// Sub2API /v1/usage response: subscription weekly-window anchor.
|
|
13
|
+
SUB2API_SUBSCRIPTION_WEEKLY_START: "weekly_window_start",
|
|
14
|
+
// OpenRouter /api/v1/key response: absolute key-limit reset.
|
|
15
|
+
OPENROUTER_KEY_LIMIT: "limit_reset",
|
|
16
|
+
});
|
|
17
|
+
|
|
9
18
|
export function pickNumber(obj, keys) {
|
|
10
19
|
for (const key of keys) {
|
|
11
20
|
const value = obj?.[key];
|
|
@@ -40,6 +49,45 @@ function shortDate(value) {
|
|
|
40
49
|
return match ? `${match[2]}-${match[3]}` : "";
|
|
41
50
|
}
|
|
42
51
|
|
|
52
|
+
function timestampMs(value) {
|
|
53
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
54
|
+
return value < 1e12 ? value * 1000 : value;
|
|
55
|
+
}
|
|
56
|
+
const parsed = Date.parse(value);
|
|
57
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function compactDurationUntil(value) {
|
|
61
|
+
const resetMs = timestampMs(value);
|
|
62
|
+
if (resetMs === undefined) return "";
|
|
63
|
+
|
|
64
|
+
const remainingMs = resetMs - Date.now();
|
|
65
|
+
if (remainingMs <= 0) return "";
|
|
66
|
+
|
|
67
|
+
let seconds = Math.floor(remainingMs / 1000);
|
|
68
|
+
if (seconds <= 0) return "0m";
|
|
69
|
+
const days = Math.floor(seconds / 86400);
|
|
70
|
+
seconds %= 86400;
|
|
71
|
+
const hours = Math.floor(seconds / 3600);
|
|
72
|
+
seconds %= 3600;
|
|
73
|
+
const minutes = Math.floor(seconds / 60);
|
|
74
|
+
if (days) return `${days}d${hours}h`;
|
|
75
|
+
if (hours) return `${hours}h${minutes}m`;
|
|
76
|
+
return `${minutes}m`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function rateLimitResetAt(entry) {
|
|
80
|
+
const value = entry?.[RESET_TIME_FIELDS.SUB2API_RATE_LIMIT];
|
|
81
|
+
return timestampMs(value) === undefined ? undefined : value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function weeklyResetAt(subscription) {
|
|
85
|
+
const startMs = timestampMs(
|
|
86
|
+
subscription?.[RESET_TIME_FIELDS.SUB2API_SUBSCRIPTION_WEEKLY_START]
|
|
87
|
+
);
|
|
88
|
+
return startMs === undefined ? undefined : startMs + 7 * 86400 * 1000;
|
|
89
|
+
}
|
|
90
|
+
|
|
43
91
|
function hasSubscriptionLimits(root) {
|
|
44
92
|
const sub = root?.subscription || {};
|
|
45
93
|
return [
|
|
@@ -163,7 +211,7 @@ export function formatOpenRouterLine(data) {
|
|
|
163
211
|
const limit = pickNumber(root, ["limit", "limit_remaining", "total_credits"]);
|
|
164
212
|
const remaining = pickNumber(root, ["limit_remaining", "remaining_credits"]);
|
|
165
213
|
const used = pickNumber(root, ["usage", "total_usage", "spend"]);
|
|
166
|
-
const reset = root?.
|
|
214
|
+
const reset = compactDurationUntil(root?.[RESET_TIME_FIELDS.OPENROUTER_KEY_LIMIT]);
|
|
167
215
|
const parts = [];
|
|
168
216
|
|
|
169
217
|
if (remaining !== undefined) parts.push(`balance ${formatMoney(remaining)}`);
|
|
@@ -172,7 +220,7 @@ export function formatOpenRouterLine(data) {
|
|
|
172
220
|
} else if (used !== undefined) {
|
|
173
221
|
parts.push(`used ${formatMoney(used)}`);
|
|
174
222
|
}
|
|
175
|
-
if (reset) parts.push(
|
|
223
|
+
if (reset) parts.push(`⟳${reset}`);
|
|
176
224
|
|
|
177
225
|
if (parts.length === 0) throw new Error("OpenRouter payload has no usage fields");
|
|
178
226
|
return parts.join(" | ");
|
|
@@ -254,8 +302,9 @@ function formatQuotaLimitedLine(root) {
|
|
|
254
302
|
const window = entry?.window;
|
|
255
303
|
const rateLimit = pickNumber(entry, ["limit"]);
|
|
256
304
|
const rateUsed = pickNumber(entry, ["used"]);
|
|
305
|
+
const reset = compactDurationUntil(rateLimitResetAt(entry));
|
|
257
306
|
return window && rateLimit !== undefined && rateUsed !== undefined
|
|
258
|
-
? `${window} ${formatMoney(rateUsed)}/${formatMoney(rateLimit)}`
|
|
307
|
+
? `${window} ${formatMoney(rateUsed)}/${formatMoney(rateLimit)}${reset ? ` ⟳${reset}` : ""}`
|
|
259
308
|
: "";
|
|
260
309
|
})
|
|
261
310
|
.filter(Boolean);
|
|
@@ -290,10 +339,13 @@ function formatUsageLine(root) {
|
|
|
290
339
|
const monthlyLimit = pickNumber(sub, ["monthly_limit_usd"]);
|
|
291
340
|
const monthlyUsage = pickNumber(sub, ["monthly_usage_usd"]);
|
|
292
341
|
const expires = shortDate(sub.expires_at);
|
|
342
|
+
const weeklyReset = compactDurationUntil(weeklyResetAt(sub));
|
|
293
343
|
|
|
294
344
|
const parts = [];
|
|
295
345
|
if (dailyLimit > 0 && dailyUsage !== undefined) parts.push(`D ${formatMoney(dailyUsage)}/${formatMoney(dailyLimit)}`);
|
|
296
|
-
if (weeklyLimit > 0 && weeklyUsage !== undefined)
|
|
346
|
+
if (weeklyLimit > 0 && weeklyUsage !== undefined) {
|
|
347
|
+
parts.push(`W ${formatMoney(weeklyUsage)}/${formatMoney(weeklyLimit)}${weeklyReset ? ` ⟳${weeklyReset}` : ""}`);
|
|
348
|
+
}
|
|
297
349
|
if (monthlyLimit > 0 && monthlyUsage !== undefined) parts.push(`M ${formatMoney(monthlyUsage)}/${formatMoney(monthlyLimit)}`);
|
|
298
350
|
if (expires) parts.push(`Exp ${expires}`);
|
|
299
351
|
return parts.join(" | ");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kairyou/agent-tools",
|
|
3
|
-
"version": "0.10.
|
|
4
|
-
"description": "Reusable Agent Skills
|
|
3
|
+
"version": "0.10.2",
|
|
4
|
+
"description": "Reusable Agent Skills, plus integrations (statusline, provider usage, vision) that install into Codex, Claude Code, and opencode.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: at-self-eval
|
|
3
|
+
description: "Summarize a contributor's git history into a concise, review-friendly self-evaluation for performance cycles (quarterly / semi-annual / promotion). Use when the user asks for a PBC, self-review, or work summary. A daily/weekly log may be pasted or a path provided to enrich the result; other repos or branches may be mentioned to aggregate across projects (all optional)."
|
|
4
|
+
argument-hint: "[<author>] [--from yyyy-mm-dd] [--to yyyy-mm-dd]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Self-Evaluation Summary
|
|
8
|
+
|
|
9
|
+
`git log → group by business line → dedupe → ≤8 concise output points → human review`
|
|
10
|
+
|
|
11
|
+
## Phase 0 — Gather the evidence
|
|
12
|
+
|
|
13
|
+
Normalize every input form — flags, partial flags, or plain language like
|
|
14
|
+
`统计 1-3月的产出` / `统计上半年的工作` — into a **determined** `(author, from,
|
|
15
|
+
to)` triple, then query. The user's explicit date range always wins; defaults
|
|
16
|
+
only fill what the user did not say. Re-state the resolved window in the output
|
|
17
|
+
header so the user can catch a wrong inference at a glance.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# author — default to the current git identity, do NOT guess variants
|
|
21
|
+
AUTHOR=$(git config user.name)
|
|
22
|
+
# --until is EXCLUSIVE of its date, so push it +1 day to include the last day
|
|
23
|
+
git log --author="$AUTHOR" --since="<from>" --until="<to + 1 day>" --no-merges --format="%ad %s" --date=short
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Author — do not guess:**
|
|
27
|
+
|
|
28
|
+
1. Default `<author>` to `git config user.name`. Query only with that name; do
|
|
29
|
+
not infer or try variants. Note that `git log --author` uses regex matching,
|
|
30
|
+
so this rule limits identity guessing rather than promising strict equality.
|
|
31
|
+
2. If 0 commits come back, do NOT auto-try pinyin/Chinese variants (risk of
|
|
32
|
+
mismatching a different person or inflating counts). Tell the user nothing
|
|
33
|
+
was found and show how to self-identify:
|
|
34
|
+
```bash
|
|
35
|
+
git log --format='%an' | sort -u
|
|
36
|
+
```
|
|
37
|
+
3. If results come back, end the output with a one-line hint: the user may have
|
|
38
|
+
commits under other names and can rerun with `--author "name1\|name2"`.
|
|
39
|
+
|
|
40
|
+
**Date range — `--until` is exclusive, `--since` is inclusive:**
|
|
41
|
+
|
|
42
|
+
`git log --until=2026-06-30` means *before 2026-06-30 00:00:00* — it silently
|
|
43
|
+
drops every commit on June 30. Always pass `<to + 1 day>` as `--until`.
|
|
44
|
+
|
|
45
|
+
Fill missing dimensions (user's explicit range always wins):
|
|
46
|
+
|
|
47
|
+
| User gave | resolved to |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| Both `--from` and `--to` | use as-is (apply the `--until +1 day` fix) |
|
|
50
|
+
| Only `--from` | `to` = today |
|
|
51
|
+
| Only `--to` | `from` = Jan 1 of `to`'s year |
|
|
52
|
+
| Neither / plain-language time | **current quarter** |
|
|
53
|
+
|
|
54
|
+
Plain-language time (`上半年` / `1-3月` / `近三个月` / `Q1` etc.) → parse to dates,
|
|
55
|
+
defaulting to the current year; if the current month is earlier than the spoken
|
|
56
|
+
months (e.g. it's Feb and the user says `统计 11-12 月`), use the previous
|
|
57
|
+
calendar year. `上半年/下半年` → Jan–Jun / Jul–Dec; `Q1…/一季度…` → calendar
|
|
58
|
+
quarter; `近三个月/最近一个月` → rolling window ending today. No time given →
|
|
59
|
+
current quarter.
|
|
60
|
+
|
|
61
|
+
`--no-merges` always — merge commits are not deliverables.
|
|
62
|
+
|
|
63
|
+
**Re-state the window (required):** the output MUST begin with a one-line header
|
|
64
|
+
so the user can verify or correct an auto-inferred window:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
> 作者: <author> · 窗口: 2026-01-01 ~ 2026-03-31 · (时间窗口为自动推断, 如不对请指正后重跑)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
When aggregating multiple repos, append a second line listing each repo and the
|
|
71
|
+
branch used. Use plain words, never git jargon like "HEAD":
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
> 仓库: <repo1> (分支 next) · <repo2> (分支 main) · (未指定的均用各仓库当前分支, 如不对请指正)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
No commit count, no "source" line — they add noise without review value.
|
|
78
|
+
|
|
79
|
+
### Supplementary context (optional, conversation-driven)
|
|
80
|
+
|
|
81
|
+
Daily/weekly logs, other repos, and other branches are all **optional
|
|
82
|
+
supplementary context**, handled the same way — never via flags. The user offers
|
|
83
|
+
them in conversation; the skill asks to clarify only when the user has already
|
|
84
|
+
expressed intent. Do not prompt for these at the start.
|
|
85
|
+
|
|
86
|
+
**Daily/weekly log** (pasted text or a provided file path) is background context:
|
|
87
|
+
1. Format-agnostic — do not assume any date sectioning or schema; read as
|
|
88
|
+
continuous text to pick up business-line attribution and intent that commit
|
|
89
|
+
messages lack.
|
|
90
|
+
2. No fabrication — commit-backed items may be summarized directly. Log-only
|
|
91
|
+
items must not be mixed into confirmed deliverables; if one appears to be a
|
|
92
|
+
real non-code outcome (design, research, review, training, delivery support,
|
|
93
|
+
coordination), list it separately for user confirmation. Include it in the
|
|
94
|
+
final summary only after the user confirms it.
|
|
95
|
+
3. No time re-attribution — dates come from git log only; for a confirmed
|
|
96
|
+
log-only outcome, use only a date explicitly present in the log or provided
|
|
97
|
+
by the user.
|
|
98
|
+
|
|
99
|
+
**Other repos** (user pastes a path or a remote URL):
|
|
100
|
+
- **Local path** → query with `git -C <path> log ...`; resolve that repo's author
|
|
101
|
+
independently (same no-guessing rule as the primary repo). Default to that
|
|
102
|
+
repo's current branch, surfaced in the header for correction.
|
|
103
|
+
- **Remote URL** → do NOT auto-clone. Ask the user whether to clone into a temp
|
|
104
|
+
dir for this run; only clone on explicit yes. On yes, clone to a temp dir,
|
|
105
|
+
query it, then delete the temp dir when done (keep it only if the user asks).
|
|
106
|
+
On no, ask for a local path or have them clone it themselves. Never clone
|
|
107
|
+
silently — clone has side effects (disk, auth, slow, may fail).
|
|
108
|
+
- Path does not exist / not a git repo → ask, do not guess.
|
|
109
|
+
|
|
110
|
+
**Other branches** (user names them, e.g. "也包含 dev 分支"):
|
|
111
|
+
- Query those branches explicitly. Never run `git log --all` unprompted — on
|
|
112
|
+
large repos it pulls tens of thousands of commits and overruns context.
|
|
113
|
+
- Confirm scope with the user before scanning all branches.
|
|
114
|
+
|
|
115
|
+
**Dedup across repos/branches:** identical commit hashes count once. Do not
|
|
116
|
+
mechanically delete commits by date/subject — separate work can share a generic
|
|
117
|
+
message, while cherry-picked work can have different dates. During grouping,
|
|
118
|
+
merge likely duplicates only when repository, subject, files, and change intent
|
|
119
|
+
show they represent the same work; surface uncertain cases for user confirmation.
|
|
120
|
+
|
|
121
|
+
### Language for user-facing text
|
|
122
|
+
|
|
123
|
+
All output and prompts to the user use plain words. Avoid git jargon ("HEAD",
|
|
124
|
+
"ref", "upstream", "cherry-pick") — users who say "I also worked on the dev
|
|
125
|
+
branch" may not know what HEAD means. Keep "commit" and branch names (with
|
|
126
|
+
context) as those are widely understood.
|
|
127
|
+
|
|
128
|
+
## Phase 1 — Group and dedupe
|
|
129
|
+
|
|
130
|
+
Group commits into **business lines / modules** and merge related commits into
|
|
131
|
+
one deliverable. Collapse many commits into the 1–3 outcomes they achieved —
|
|
132
|
+
`fix: lint`, `chore: fmt`, repeated `feat: sync` fold into the larger deliverable
|
|
133
|
+
they supported. If a group has only trivial housekeeping, merge it into a
|
|
134
|
+
related group or drop it. Do not inflate a single commit into a "deliverable."
|
|
135
|
+
|
|
136
|
+
## Phase 2 — Write the summary
|
|
137
|
+
|
|
138
|
+
Output a **numbered list**, one deliverable per line, grouped by business line.
|
|
139
|
+
Each line:
|
|
140
|
+
|
|
141
|
+
- starts with the business line / module, a colon, then 2–4 sub-points or a
|
|
142
|
+
one-sentence outcome;
|
|
143
|
+
- is verb-led and outcome-oriented (built / shipped / refactored / migrated),
|
|
144
|
+
not a feature-name dump;
|
|
145
|
+
- is concrete enough to be credible but concise enough to scan.
|
|
146
|
+
|
|
147
|
+
- **≤8 top-level items — prioritize impact.** When work exceeds 8 lines, keep
|
|
148
|
+
the high-impact deliverables (built from scratch, major customer delivery,
|
|
149
|
+
architectural refactor, key fix) and merge or drop routine housekeeping
|
|
150
|
+
(lint, format, version bumps, repeated sync). Do not split 8 evenly across
|
|
151
|
+
business lines if that dilutes the standout work.
|
|
152
|
+
- A volume/peak note is worth citing inline **only when a business line clearly
|
|
153
|
+
concentrated in one month** (e.g. "5月密集完成 <业务线>"), as a distribution
|
|
154
|
+
cue — not as a self-justifying metric.
|
|
155
|
+
- Match the user's language (Chinese request → Chinese output). Identifiers and
|
|
156
|
+
product names stay as-is.
|
|
157
|
+
- Do NOT fabricate. Every confirmed deliverable must trace back to a commit or
|
|
158
|
+
to a user-confirmed log entry. Surface uncertain or log-only outcomes as
|
|
159
|
+
candidates for confirmation rather than stating it as fact.
|
|
160
|
+
|
|
161
|
+
Example shape (header + numbered list, placeholders only):
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
> 作者: <author> · 窗口: 2026-01-01 ~ 2026-06-30 · (时间窗口为自动推断, 如不对请指正后重跑)
|
|
165
|
+
|
|
166
|
+
1. <业务线A>: <动词开头的产出>; <2–4 个关键点或一句成果>
|
|
167
|
+
2. <业务线B>: ...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
End with a one-line reminder: this is a draft — verify before submitting, since
|
|
171
|
+
AI may merge or misattribute work. Do not write the summary to any file unless
|
|
172
|
+
the user asks.
|
|
173
|
+
|
|
174
|
+
**Conditional hints — only when this run likely under-represents the work.**
|
|
175
|
+
Do NOT prompt every time; a clean, complete result ends with just the draft
|
|
176
|
+
reminder above. Append ONE short line only in these cases:
|
|
177
|
+
|
|
178
|
+
- A log-only item looks like real work but lacks user confirmation → "日志中还有未对应到 commit 的工作; 如属有效产出, 确认后可纳入总结."
|
|
179
|
+
- No daily/weekly log was given AND commit messages are terse / hard to
|
|
180
|
+
attribute → "本次仅基于 commit 归纳; 若有日报/周报可粘贴文本或提供路径, 能补充业务背景使产出更准."
|
|
181
|
+
- There is concrete evidence that other repos/branches may contain relevant
|
|
182
|
+
work (the user mentioned them, or the supplied log names work absent from the
|
|
183
|
+
scanned repo) → "可能还有其他仓库或分支的工作未覆盖; 告知路径/分支后可继续聚合统计."
|
|
184
|
+
|
|
185
|
+
One line, never a follow-up question. Accept pasted text or a path equally —
|
|
186
|
+
log formats vary widely, never assume a schema.
|