@kairyou/agent-tools 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 agent-tools Developers and Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # Agent Tools
2
+
3
+ Reusable skills and runtime integrations for Codex, Claude Code, and opencode. This repository keeps each capability in predictable locations so projects can opt into only what they need.
4
+
5
+ [中文](README.zh-CN.md)
6
+
7
+ ## Directory Layout
8
+
9
+ ```text
10
+ agent-tools/
11
+ ├── .claude-plugin/ # Claude Code/plugin ecosystem manifest.
12
+ ├── .codex-plugin/ # Codex plugin manifest.
13
+ ├── hooks/ # Shared and agent-specific hook integrations.
14
+ ├── plugins/ # Runtime plugins loaded by supported agents.
15
+ ├── scripts/ # Install, sync, validation, and maintenance scripts.
16
+ ├── skills/ # Reusable Agent Skills for CLI discovery and plugin manifests.
17
+ │ └── workflow/ # Workflow-oriented skills.
18
+ │ ├── at-commit/ # Conventional Commit message skill.
19
+ │ ├── at-review/ # Review changes for bugs and regressions.
20
+ │ └── at-simplify/ # Reduce complexity and duplication in changes.
21
+ ├── statusline/ # Statusline scripts/templates, grouped by agent.
22
+ │ └── claude/ # Claude command-backed statusLine script + example config.
23
+ └── lib/ # Shared implementation used by hooks, statuslines, and installers.
24
+ ```
25
+
26
+ ## Current Skills
27
+
28
+ - `at-commit`: Generate a Conventional Commits message from staged changes and wait for user confirmation before committing.
29
+ - `at-review`: Review changes for correctness bugs, regressions, convention violations, and high-value cleanup findings.
30
+ - `at-simplify`: Refactor changes to reduce duplication, lower complexity, and improve code quality.
31
+
32
+ ## Usage
33
+
34
+ List available skills:
35
+
36
+ ```bash
37
+ npx -y skills@latest add kairyou/agent-tools --list
38
+ ```
39
+
40
+ Install skills globally:
41
+
42
+ ```bash
43
+ npx -y skills@latest add kairyou/agent-tools --skill at-commit -g -y
44
+ npx -y skills@latest add kairyou/agent-tools --skill at-review -g -y
45
+ npx -y skills@latest add kairyou/agent-tools --skill at-simplify -g -y
46
+ ```
47
+
48
+ Pass multiple skills after `--skill`, for example `--skill at-commit at-review at-simplify`.
49
+
50
+ ## Runtime integrations
51
+
52
+ ### Claude Code
53
+
54
+ #### Statusline
55
+
56
+ ```bash
57
+ # Install or update
58
+ npx -y github:kairyou/agent-tools statusline -a claude
59
+
60
+ # Preview or uninstall
61
+ npx -y github:kairyou/agent-tools statusline -a claude --dry-run
62
+ npx -y github:kairyou/agent-tools statusline -a claude --uninstall
63
+ ```
64
+
65
+ The installer writes `statusLine` to `~/.claude/settings.json`. The default
66
+ output is:
67
+
68
+ ```text
69
+ ⎇ main | Opus 4.8 | 5h 7% ⟳2h54m | w 41% ⟳3d1h
70
+ ```
71
+
72
+ Here `5h` and `w` are Claude's rolling usage windows; `⟳` is the reset countdown.
73
+ When a compatible API relay is active, the statusline also appends provider usage.
74
+
75
+ To choose what appears, edit `statusline.fields` in
76
+ `~/.agent-tools/config.jsonc`. The installer may add new default keys on update;
77
+ it preserves top-of-file comments and existing values.
78
+
79
+ ### Codex
80
+
81
+ #### Provider usage hook
82
+
83
+ ```bash
84
+ # Install or update
85
+ npx -y github:kairyou/agent-tools usage -a codex
86
+
87
+ # Preview or uninstall
88
+ npx -y github:kairyou/agent-tools usage -a codex --dry-run
89
+ npx -y github:kairyou/agent-tools usage -a codex --uninstall
90
+ ```
91
+
92
+ The installer adds the hook to `UserPromptSubmit` and `Stop` in
93
+ `~/.codex/hooks.json`. After installation, run `/hooks` inside Codex and approve
94
+ the agent-tools usage hooks.
95
+
96
+ Output examples:
97
+
98
+ ```text
99
+ # Subscription / plan quota.
100
+ warning: API | D $0.0/$100 | W $0.0/$300 | Exp 07-08
101
+
102
+ # Wallet balance.
103
+ warning: API | balance $362 | today $61.7 | 30d $566
104
+ ```
105
+
106
+ Fields: `D/W/M` are daily/weekly/monthly spend against plan limits; `Exp` is
107
+ the plan expiry; `balance` is wallet credit; `today` and `30d` are API spend.
108
+
109
+ ### OpenCode
110
+
111
+ #### Provider usage plugin
112
+
113
+ ```bash
114
+ # Install or update
115
+ npx -y github:kairyou/agent-tools usage -a opencode
116
+
117
+ # Preview or uninstall
118
+ npx -y github:kairyou/agent-tools usage -a opencode --dry-run
119
+ npx -y github:kairyou/agent-tools usage -a opencode --uninstall
120
+ ```
121
+
122
+ The installer adds global server and TUI plugins. After the active session
123
+ becomes idle, the server plugin refreshes usage and shows it as a toast. The TUI
124
+ plugin also registers `/at-usage` for the latest cached value. Restart opencode
125
+ after installing or updating the plugins.
126
+
127
+ Toast example:
128
+
129
+ ```text
130
+ Provider usage
131
+ balance $244 | today $45.8 | 30d $604
132
+ ```
133
+
134
+ ### Supported gateways
135
+
136
+ Balance, quota, and plan usage queries support compatible Sub2API-like,
137
+ NewAPI/OneAPI/OneHub/DoneHub/Veloera/AnyRouter-like, and OpenRouter gateways.
138
+
139
+ ## FAQ
140
+
141
+ ### Why does a global install report `Failed to install 1`?
142
+
143
+ `PromptScript does not support global skill installation` means that the
144
+ PromptScript agent does not support global installation. It does not affect
145
+ other agents and can be ignored. See [`skills` issue #1352](https://github.com/vercel-labs/skills/issues/1352).
146
+
147
+ ## Notes
148
+
149
+ - `skills/` contains reusable `SKILL.md` capabilities.
150
+ - `hooks/` keeps stable ownership directories for shared and agent-specific integrations.
151
+ - `plugins/` contains runtime plugin implementations such as opencode usage.
152
+ - `statusline/claude/` contains the command-backed Claude statusLine script.
153
+ - `lib/` contains shared implementation such as API usage query logic.
154
+ - The installer marks and removes only the config entries it owns.
155
+
156
+ Run local checks with `npm test`.
@@ -0,0 +1,149 @@
1
+ # Agent Tools
2
+
3
+ 面向 Codex、Claude Code 与 opencode 的可复用 skills 和 runtime integrations. 本仓库把各项能力放在可预期的位置; 不同项目可以只安装自己需要的部分.
4
+
5
+ ## 目录结构
6
+
7
+ ```text
8
+ agent-tools/
9
+ ├── .claude-plugin/ # Claude Code/plugin 生态的 manifest。
10
+ ├── .codex-plugin/ # Codex plugin manifest。
11
+ ├── hooks/ # 通用 hook 逻辑及各 agent 的适配实现。
12
+ ├── plugins/ # 由各 agent 加载的 runtime plugins.
13
+ ├── scripts/ # 安装、同步、校验和仓库维护脚本。
14
+ ├── skills/ # 可复用 Agent Skills,供 CLI 扫描和 plugin manifest 声明。
15
+ │ └── workflow/ # 工作流类 skills。
16
+ │ ├── at-commit/ # 生成 Conventional Commits message.
17
+ │ ├── at-review/ # 审查改动中的 bug 与回归风险.
18
+ │ └── at-simplify/ # 减少改动中的冗余和复杂度.
19
+ ├── statusline/ # Statusline 配置片段/模板,按 agent 分组。
20
+ │ └── claude/ # Claude command-backed statusLine 脚本和示例配置。
21
+ └── lib/ # hooks、statusline、installer 复用的共享实现。
22
+ ```
23
+
24
+ ## 当前 Skills
25
+
26
+ - `at-commit`: 根据暂存区改动生成 Conventional Commits message, 并在提交前等待用户确认.
27
+ - `at-review`: 审查改动中的正确性 bug, 回归风险, 约定违规和高价值清理项.
28
+ - `at-simplify`: 重构改动, 减少冗余, 降低复杂度, 提升代码质量.
29
+
30
+ ## 使用方式
31
+
32
+ 查看可用 skills:
33
+
34
+ ```bash
35
+ npx -y skills@latest add kairyou/agent-tools --list
36
+ ```
37
+
38
+ 全局安装 skill:
39
+
40
+ ```bash
41
+ npx -y skills@latest add kairyou/agent-tools --skill at-commit -g -y
42
+ npx -y skills@latest add kairyou/agent-tools --skill at-review -g -y
43
+ npx -y skills@latest add kairyou/agent-tools --skill at-simplify -g -y
44
+ ```
45
+
46
+ 多个 skill 可以跟在 `--skill` 后面,例如 `--skill at-commit at-review at-simplify`。
47
+
48
+ ## Runtime integrations
49
+
50
+ ### Claude Code
51
+
52
+ #### Statusline
53
+
54
+ ```bash
55
+ # 安装或更新
56
+ npx -y github:kairyou/agent-tools statusline -a claude
57
+
58
+ # 预览或卸载
59
+ npx -y github:kairyou/agent-tools statusline -a claude --dry-run
60
+ npx -y github:kairyou/agent-tools statusline -a claude --uninstall
61
+ ```
62
+
63
+ 安装器会把 `statusLine` 写入 `~/.claude/settings.json`. 默认显示:
64
+
65
+ ```text
66
+ ⎇ main | Opus 4.8 | 5h 7% ⟳2h54m | w 41% ⟳3d1h
67
+ ```
68
+
69
+ 其中 `5h` / `w` 是 Claude 的滚动用量窗口, `⟳` 后面是重置倒计时.
70
+ 使用兼容的 API 中转时, statusline 还会追加 provider usage.
71
+
72
+ 如需控制显示项, 修改 `~/.agent-tools/config.jsonc` 里的
73
+ `statusline.fields`. 安装器更新时可能补充新的默认键; 文件顶部注释和已有值会保留.
74
+
75
+ ### Codex
76
+
77
+ #### Provider usage hook
78
+
79
+ ```bash
80
+ # 安装或更新
81
+ npx -y github:kairyou/agent-tools usage -a codex
82
+
83
+ # 预览或卸载
84
+ npx -y github:kairyou/agent-tools usage -a codex --dry-run
85
+ npx -y github:kairyou/agent-tools usage -a codex --uninstall
86
+ ```
87
+
88
+ 安装器会把 hook 添加到 `~/.codex/hooks.json` 的 `UserPromptSubmit` 和
89
+ `Stop`. 安装后, 在 Codex 里运行 `/hooks` 并批准 agent-tools usage hooks.
90
+
91
+ 显示效果示例:
92
+
93
+ ```text
94
+ # 订阅/套餐额度.
95
+ warning: API | D $0.0/$100 | W $0.0/$300 | Exp 07-08
96
+
97
+ # 钱包余额.
98
+ warning: API | balance $362 | today $61.7 | 30d $566
99
+ ```
100
+
101
+ 字段含义: `D/W/M` 是日/周/月套餐消耗与上限, `Exp` 是套餐到期日,
102
+ `balance` 是钱包余额, `today` / `30d` 是今日与近 30 天 API 消耗.
103
+
104
+ ### OpenCode
105
+
106
+ #### Provider usage plugin
107
+
108
+ ```bash
109
+ # 安装或更新
110
+ npx -y github:kairyou/agent-tools usage -a opencode
111
+
112
+ # 预览或卸载
113
+ npx -y github:kairyou/agent-tools usage -a opencode --dry-run
114
+ npx -y github:kairyou/agent-tools usage -a opencode --uninstall
115
+ ```
116
+
117
+ 安装器会添加全局 server plugin 和 TUI plugin. 当前 session 进入 idle 后, server
118
+ plugin 会刷新 usage 并通过 toast 显示; TUI plugin 还会注册 `/at-usage`, 用于查看最新
119
+ 缓存值. 安装或更新 plugin 后需要重启 opencode.
120
+
121
+ 显示效果示例:
122
+
123
+ ```text
124
+ Provider usage
125
+ balance $244 | today $45.8 | 30d $604
126
+ ```
127
+
128
+ ### 支持的网关
129
+
130
+ 余额, 额度和套餐用量查询支持兼容 Sub2API-like,
131
+ NewAPI/OneAPI/OneHub/DoneHub/Veloera/AnyRouter-like 与 OpenRouter 的网关.
132
+
133
+ ## FAQ
134
+
135
+ ### 全局安装为什么显示 `Failed to install 1`?
136
+
137
+ `PromptScript does not support global skill installation` 表示 PromptScript Agent 不支持全局
138
+ 安装,不影响其他 Agent,可忽略。参见 [`skills` issue #1352](https://github.com/vercel-labs/skills/issues/1352)。
139
+
140
+ ## 说明
141
+
142
+ - `skills/` 放可复用的 `SKILL.md` 能力。项目可以只安装自己需要的 skills。
143
+ - `hooks/` 按通用逻辑和各 agent 适配实现划分目录。
144
+ - `plugins/` 放 opencode usage 等 runtime plugin 实现.
145
+ - `statusline/claude/` 放 Claude command-backed statusLine 脚本。
146
+ - `lib/` 放 API usage 查询等共享实现。
147
+ - 安装器只标记并移除自己写入的配置项。
148
+
149
+ 本地检查运行 `npm test`。
@@ -0,0 +1,23 @@
1
+ // agent-tools config.
2
+ // statusline.fields controls both visibility and order.
3
+ // Remove a field to hide it; move a field to reorder it.
4
+ // Available statusline fields: branch, model, fiveHour, week, context, directory.
5
+ // providerUsage is used by the shared usage runtime.
6
+ {
7
+ "statusline": {
8
+ "fields": ["branch", "model", "fiveHour", "week"],
9
+ "separator": " | ",
10
+ "symbols": {
11
+ "branch": "⎇",
12
+ "reset": "⟳",
13
+ "empty": "–",
14
+ "fiveHour": "5h",
15
+ "week": "w",
16
+ "context": "ctx"
17
+ }
18
+ },
19
+ "providerUsage": {
20
+ "days": 30,
21
+ "debug": false
22
+ }
23
+ }
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env node
2
+ // Codex usage hook wrapper.
3
+ // Keeps hook failures actionable: infrastructure errors are logged and the hook
4
+ // still exits 0 with a short message instead of surfacing only "exit code 1".
5
+
6
+ import fs from "node:fs";
7
+ import os from "node:os";
8
+ import path from "node:path";
9
+ import { spawn } from "node:child_process";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
13
+ const AGENT_TOOLS_HOME = process.env.AGENT_TOOLS_HOME || path.resolve(SCRIPT_DIR, "..", "..");
14
+ const USAGE_SCRIPT = path.join(AGENT_TOOLS_HOME, "lib", "usage.mjs");
15
+ const LOG_PATH = path.join(AGENT_TOOLS_HOME, "logs", "usage-hook.log");
16
+ const TIMEOUT_MS = Number(process.env.AGENT_TOOLS_USAGE_HOOK_TIMEOUT_MS || 4500);
17
+ const MAX_LOG_BYTES = Number(process.env.AGENT_TOOLS_USAGE_HOOK_LOG_BYTES || 256 * 1024);
18
+ const KEEP_LOG_BYTES = 128 * 1024;
19
+
20
+ function hookOut(message) {
21
+ const payload = { continue: true };
22
+ if (message) payload.systemMessage = message;
23
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
24
+ }
25
+
26
+ function preview(text) {
27
+ return String(text || "").replace(/\s+/g, " ").trim().slice(0, 500);
28
+ }
29
+
30
+ function logFailure(event) {
31
+ try {
32
+ fs.mkdirSync(path.dirname(LOG_PATH), { recursive: true });
33
+ rotateLogIfNeeded();
34
+ fs.appendFileSync(
35
+ LOG_PATH,
36
+ `${JSON.stringify({ at: new Date().toISOString(), ...event })}\n`
37
+ );
38
+ } catch {
39
+ // Nothing else to do; the wrapper must fail open.
40
+ }
41
+ }
42
+
43
+ function rotateLogIfNeeded() {
44
+ const maxBytes = Number.isFinite(MAX_LOG_BYTES) && MAX_LOG_BYTES > 0 ? MAX_LOG_BYTES : 256 * 1024;
45
+ if (!fs.existsSync(LOG_PATH)) return;
46
+ const stat = fs.statSync(LOG_PATH);
47
+ if (stat.size <= maxBytes) return;
48
+ const keepBytes = Math.min(KEEP_LOG_BYTES, Math.floor(maxBytes / 2));
49
+ const fd = fs.openSync(LOG_PATH, "r");
50
+ try {
51
+ const buffer = Buffer.alloc(keepBytes);
52
+ fs.readSync(fd, buffer, 0, keepBytes, Math.max(0, stat.size - keepBytes));
53
+ fs.writeFileSync(
54
+ LOG_PATH,
55
+ `${JSON.stringify({ at: new Date().toISOString(), reason: "log rotated", previousBytes: stat.size })}\n` +
56
+ buffer.toString("utf8").replace(/^[^\n]*\n?/, "")
57
+ );
58
+ } finally {
59
+ fs.closeSync(fd);
60
+ }
61
+ }
62
+
63
+ function failureMessage() {
64
+ return `API usage hook failed; see ${LOG_PATH.replace(/\\/g, "/")}`;
65
+ }
66
+
67
+ function parseHookJson(stdout) {
68
+ const text = stdout.trim();
69
+ if (!text) return { continue: true };
70
+ return JSON.parse(text);
71
+ }
72
+
73
+ async function runUsageScript() {
74
+ if (!fs.existsSync(USAGE_SCRIPT)) {
75
+ logFailure({ reason: "missing usage script", usageScript: USAGE_SCRIPT });
76
+ hookOut(failureMessage());
77
+ return;
78
+ }
79
+
80
+ const result = await new Promise((resolve) => {
81
+ const child = spawn(process.execPath, [USAGE_SCRIPT, "hook", "--agent", "codex"], {
82
+ cwd: process.cwd(),
83
+ env: process.env,
84
+ stdio: ["ignore", "pipe", "pipe"],
85
+ windowsHide: true,
86
+ });
87
+ let stdout = "";
88
+ let stderr = "";
89
+ let settled = false;
90
+ const timeout = setTimeout(() => {
91
+ if (settled) return;
92
+ settled = true;
93
+ child.kill();
94
+ resolve({ status: null, signal: "timeout", stdout, stderr });
95
+ }, Number.isFinite(TIMEOUT_MS) && TIMEOUT_MS > 0 ? TIMEOUT_MS : 4500);
96
+
97
+ child.stdout.on("data", (chunk) => {
98
+ stdout += chunk;
99
+ });
100
+ child.stderr.on("data", (chunk) => {
101
+ stderr += chunk;
102
+ });
103
+ child.on("error", (error) => {
104
+ if (settled) return;
105
+ settled = true;
106
+ clearTimeout(timeout);
107
+ resolve({ status: null, error, stdout, stderr });
108
+ });
109
+ child.on("exit", (status, signal) => {
110
+ if (settled) return;
111
+ settled = true;
112
+ clearTimeout(timeout);
113
+ resolve({ status, signal, stdout, stderr });
114
+ });
115
+ });
116
+
117
+ if (result.status !== 0) {
118
+ logFailure({
119
+ reason: "usage script exited non-zero",
120
+ status: result.status,
121
+ signal: result.signal || "",
122
+ error: result.error?.message || "",
123
+ stdout: preview(result.stdout),
124
+ stderr: preview(result.stderr),
125
+ usageScript: USAGE_SCRIPT,
126
+ node: process.version,
127
+ platform: `${process.platform} ${os.release()}`,
128
+ });
129
+ hookOut(failureMessage());
130
+ return;
131
+ }
132
+
133
+ try {
134
+ const payload = parseHookJson(result.stdout);
135
+ process.stdout.write(`${JSON.stringify(payload)}\n`);
136
+ } catch (error) {
137
+ logFailure({
138
+ reason: "usage script returned invalid hook JSON",
139
+ error: error.message,
140
+ stdout: preview(result.stdout),
141
+ stderr: preview(result.stderr),
142
+ usageScript: USAGE_SCRIPT,
143
+ });
144
+ hookOut(failureMessage());
145
+ }
146
+ }
147
+
148
+ try {
149
+ await runUsageScript();
150
+ } catch (error) {
151
+ logFailure({
152
+ reason: "wrapper exception",
153
+ error: error?.stack || error?.message || String(error),
154
+ usageScript: USAGE_SCRIPT,
155
+ });
156
+ hookOut(failureMessage());
157
+ }
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+