@lyra-ai/toolkit 0.2.1 → 0.2.3
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/QUICKSTART.md +93 -1
- package/README.md +41 -2
- package/bin/skillhub.mjs +4 -0
- package/bin/toolkit.mjs +24 -16
- package/package.json +4 -2
- package/src/config-cmd.mjs +32 -39
- package/src/flush.mjs +3 -14
- package/src/install.mjs +10 -14
- package/src/logquery.mjs +5 -25
- package/src/shared/config.mjs +125 -0
- package/src/skillhub.mjs +5836 -0
- package/src/status.mjs +10 -11
- package/src/zentao.mjs +58 -10
package/QUICKSTART.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @lyra-ai/toolkit Quickstart
|
|
2
2
|
|
|
3
|
-
面向 AI agent
|
|
3
|
+
面向 AI agent 的零依赖 CLI 工具集:**zentao**(禅道项目管理)和 **skillhub**(AI 技能包管理)。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
@@ -12,6 +12,7 @@ npm install -g @lyra-ai/toolkit
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
zentao --help
|
|
15
|
+
skillhub --help
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
## 初始化
|
|
@@ -89,3 +90,94 @@ zentao bug get 42
|
|
|
89
90
|
| `ZENTAO_DEFAULT_PROJECT` | | 默认产品/项目 ID |
|
|
90
91
|
| `ZENTAO_TOKEN_TTL` | | Token 有效期(秒,默认 3600) |
|
|
91
92
|
| `ZENTAO_CACHE_STALE_MS` | | 缓存过期阈值(ms,默认 3600000) |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## skillhub 快速开始
|
|
97
|
+
|
|
98
|
+
### 登录
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
skillhub login --token <your-token>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Token 从 SkillHub registry 获取,登录后凭证存储在 `~/.skillhub/credentials.json`。
|
|
105
|
+
|
|
106
|
+
验证:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
skillhub whoami
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 搜索与安装技能包
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# 搜索技能包
|
|
116
|
+
skillhub search code-review
|
|
117
|
+
|
|
118
|
+
# 安装到用户级(所有项目可用)
|
|
119
|
+
skillhub install code-review --scope user
|
|
120
|
+
|
|
121
|
+
# 安装到项目级(仅当前项目)
|
|
122
|
+
skillhub install code-review --scope project
|
|
123
|
+
|
|
124
|
+
# 指定 agent profile
|
|
125
|
+
skillhub install code-review --agent claude-code --agent cursor
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 管理技能包
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# 查看已安装
|
|
132
|
+
skillhub list --json
|
|
133
|
+
|
|
134
|
+
# 诊断问题
|
|
135
|
+
skillhub doctor
|
|
136
|
+
|
|
137
|
+
# 更新技能包
|
|
138
|
+
skillhub update --scope user
|
|
139
|
+
|
|
140
|
+
# 移除
|
|
141
|
+
skillhub remove code-review --scope user
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 发布技能包
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
skillhub publish ./my-skill
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## skillhub 工作流
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
login(认证) → search(发现) → install(安装) → list/doctor(管理) → update(更新)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- **`login`** 存储 registry token
|
|
157
|
+
- **`search`** 在 registry 搜索技能包
|
|
158
|
+
- **`install`** 下载并安装到指定 scope
|
|
159
|
+
- **`list`** 列出本地已安装技能包
|
|
160
|
+
- **`doctor`** 诊断安装问题(缺失文件、冲突等)
|
|
161
|
+
|
|
162
|
+
## skillhub Agent 集成要点
|
|
163
|
+
|
|
164
|
+
- 默认输出 **JSON → stdout**,错误 → stderr
|
|
165
|
+
- 退出码:`0` 成功 / `1` 通用错误 / `2` 参数错误 / `3` 认证失败 / `4` 网络错误
|
|
166
|
+
- `--json` 强制 JSON 输出(部分命令默认即为 JSON)
|
|
167
|
+
- `--agent` 可重复指定多个 profile
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Agent 典型用法
|
|
171
|
+
skillhub login --token "$SKILLHUB_TOKEN"
|
|
172
|
+
skillhub search code-review --json --limit 5
|
|
173
|
+
skillhub install code-review --scope project --agent claude-code
|
|
174
|
+
skillhub doctor --json
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## skillhub 配置参考
|
|
178
|
+
|
|
179
|
+
| 变量 | 必填 | 说明 |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| `SKILLHUB_TOKEN` | ✅ | Registry 认证 token |
|
|
182
|
+
| `SKILLHUB_REGISTRY` | | Registry 地址(默认 `https://registry.skillhub.com`) |
|
|
183
|
+
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
安装后,你的 AI 编码数据自动进入组织分析平台,生成专属的多维度效能看板。
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
toolkit install claudecode --registry http://your-platform:38000
|
|
16
|
+
toolkit install --agent claudecode --registry http://your-platform:38000
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
- 会话结束后自动同步,无需手动操作
|
|
@@ -49,7 +49,7 @@ http://your-host:38000
|
|
|
49
49
|
|
|
50
50
|
Elasticsearch 日志查询工具,支持链路追踪、时间窗口、关键词搜索、自动根因解析。首次使用运行向导配置。
|
|
51
51
|
|
|
52
|
-
> 目前支持 [PlumeLog](https://
|
|
52
|
+
> 目前支持 [PlumeLog](https://gitee.com/plumeorg/plumelog) 索引格式(字段:`dateTime` / `dtTime` / `traceId` / `appName` / `className` / `content` / `logLevel`)。
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
log-query init # 向导式配置(ES 地址 + 索引 + 应用名)
|
|
@@ -99,3 +99,42 @@ cd <本目录> && npm link
|
|
|
99
99
|
```json
|
|
100
100
|
{"total":1,"count":1,"bugs":[{"id":"9","title":"t","severity":"1","priority":"2","status":"active","assignedTo":"a"}]}
|
|
101
101
|
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## skillhub 命令速查
|
|
106
|
+
|
|
107
|
+
AI 技能包管理器:搜索、安装、发布、管理 AI coding agent 技能包。
|
|
108
|
+
|
|
109
|
+
- `skillhub version [--json]` → 输出版本号
|
|
110
|
+
- `skillhub help [topic]` → 帮助信息(`topic` 为命令名)
|
|
111
|
+
- `skillhub login --token <token> [--registry <url>]` → 登录 registry
|
|
112
|
+
- `skillhub logout` → 清除本地凭证
|
|
113
|
+
- `skillhub whoami` → 显示当前登录用户
|
|
114
|
+
- `skillhub search [query] [--limit N] [--json]` → 搜索技能包
|
|
115
|
+
- `skillhub install <slug> [--namespace <ns>] [--agent <profile>] [--scope user|project] [--max-file-size <size>] [--json]` → 安装技能包
|
|
116
|
+
- `skillhub list [--scope user|project] [--json]` → 列出已安装技能包
|
|
117
|
+
- `skillhub remove <slug> [--scope user|project] [--agent <profile>] [--json]` → 移除技能包
|
|
118
|
+
- `skillhub doctor [--fix] [--json]` → 诊断技能包安装状态
|
|
119
|
+
- `skillhub publish <path> [--json]` → 发布技能包到 registry
|
|
120
|
+
- `skillhub update [--scope user|project] [--agent <profile>] [--json]` → 更新已安装技能包
|
|
121
|
+
|
|
122
|
+
### Agent profiles(`--agent`)
|
|
123
|
+
|
|
124
|
+
支持 15 种 AI coding agent 配置文件:`claude-code`、`codex`、`cursor`、`cody`、`continue`、`augment`、`copilot`、`tabnine`、`qoder`、`windsurf`、`aider`、`cline`、`roo`、`trae`、`generic`。可重复使用 `--agent` 指定多个。
|
|
125
|
+
|
|
126
|
+
### 数据存储
|
|
127
|
+
|
|
128
|
+
- 凭证:`~/.skillhub/credentials.json`
|
|
129
|
+
- 配置:`~/.skillhub/config.json`
|
|
130
|
+
- 用户级技能包:`~/.skillhub/skills/`
|
|
131
|
+
- 项目级技能包:`<cwd>/.skillhub/skills/`
|
|
132
|
+
|
|
133
|
+
默认输出 JSON(stdout),错误走 stderr。退出码:0 成功 / 1 通用错误 / 2 参数配置 / 3 认证失败 / 4 网络错误。
|
|
134
|
+
|
|
135
|
+
## skillhub JSON 示例
|
|
136
|
+
```json
|
|
137
|
+
{"ok":true,"version":"0.1.0"}
|
|
138
|
+
{"ok":true,"skills":[{"slug":"code-review","version":"1.2.0","scope":"user"}]}
|
|
139
|
+
```
|
|
140
|
+
|
package/bin/skillhub.mjs
ADDED
package/bin/toolkit.mjs
CHANGED
|
@@ -23,15 +23,18 @@ const cmd = args[0];
|
|
|
23
23
|
const HELP = `toolkit — 研发工具集
|
|
24
24
|
|
|
25
25
|
命令:
|
|
26
|
-
install
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
install --agent <name> --registry <url> 安装插件(适配指定 AI Coding Agent)
|
|
27
|
+
--agent claudecode Claude Code(SessionEnd hook 插件)
|
|
28
|
+
status 查看安装状态 + 待同步会话
|
|
29
|
+
flush 手动触发一次同步
|
|
30
|
+
config 配置管理
|
|
31
|
+
uninstall [--purge] 卸载插件(--purge 同时删数据)
|
|
32
|
+
help 显示此帮助
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
zentao <subcommand>
|
|
34
|
+
其他(独立命令):
|
|
35
|
+
zentao <subcommand> 禅道 CLI
|
|
36
|
+
log-query <subcommand> 日志查询 CLI
|
|
37
|
+
skillhub <subcommand> 技能包管理 CLI
|
|
35
38
|
`;
|
|
36
39
|
|
|
37
40
|
async function main() {
|
|
@@ -41,24 +44,29 @@ async function main() {
|
|
|
41
44
|
return;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
// toolkit install
|
|
47
|
+
// toolkit install --agent <name> --registry <url>
|
|
45
48
|
if (cmd === 'install') {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
console.error('
|
|
49
|
+
const agentIdx = args.indexOf('--agent');
|
|
50
|
+
const agent = agentIdx >= 0 ? args[agentIdx + 1] : null;
|
|
51
|
+
if (!agent) {
|
|
52
|
+
console.error('ERROR: 缺少 --agent <name>');
|
|
53
|
+
console.error(' 用法: toolkit install --agent claudecode --registry <url>');
|
|
54
|
+
console.error(' 支持: claudecode');
|
|
55
|
+
process.exit(2);
|
|
56
|
+
}
|
|
57
|
+
if (agent !== 'claudecode') {
|
|
58
|
+
console.error(`ERROR: 暂不支持 agent "${agent}"。目前支持: claudecode`);
|
|
50
59
|
process.exit(2);
|
|
51
60
|
}
|
|
52
|
-
// 解析 --registry
|
|
53
61
|
const regIdx = args.indexOf('--registry');
|
|
54
62
|
let registry = regIdx >= 0 ? args[regIdx + 1] : null;
|
|
55
63
|
if (!registry) {
|
|
56
64
|
console.error('ERROR: 缺少 --registry <url>');
|
|
57
|
-
console.error(' 用法: toolkit install claudecode --registry
|
|
65
|
+
console.error(' 用法: toolkit install --agent claudecode --registry <url>');
|
|
58
66
|
process.exit(2);
|
|
59
67
|
}
|
|
60
68
|
const { install } = await import(pathToFileURL(path.join(srcDir, 'install.mjs')).href);
|
|
61
|
-
install(registry);
|
|
69
|
+
install(registry, { agent });
|
|
62
70
|
return;
|
|
63
71
|
}
|
|
64
72
|
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lyra-ai/toolkit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "面向 AI agent 的零依赖开发工具集",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"toolkit": "./bin/toolkit.mjs",
|
|
8
8
|
"zentao": "./bin/zentao.mjs",
|
|
9
|
-
"log-query": "./bin/log-query.mjs"
|
|
9
|
+
"log-query": "./bin/log-query.mjs",
|
|
10
|
+
"skillhub": "./bin/skillhub.mjs"
|
|
10
11
|
},
|
|
11
12
|
"engines": {
|
|
12
13
|
"node": ">=18"
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"license": "MIT",
|
|
30
31
|
"keywords": [
|
|
31
32
|
"zentao",
|
|
33
|
+
"skillhub",
|
|
32
34
|
"cli",
|
|
33
35
|
"ai-agent",
|
|
34
36
|
"zero-dependency"
|
package/src/config-cmd.mjs
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* config-cmd.mjs — `toolkit config [get|set|unset|list]`
|
|
4
|
-
* 管理 ~/.toolkit/config.json(registry / project-filter)。
|
|
4
|
+
* 管理 ~/.toolkit/config.json(registry / project-filter / es)。
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import os from 'node:os';
|
|
9
|
-
|
|
10
|
-
const TOOLKIT_DIR = path.join(os.homedir(), '.toolkit');
|
|
11
|
-
const CONFIG_PATH = path.join(TOOLKIT_DIR, 'config.json');
|
|
6
|
+
import { load, save, get, set, unset, CONFIG_PATH } from './shared/config.mjs';
|
|
12
7
|
|
|
13
8
|
const HELP = `toolkit config — 配置管理
|
|
14
9
|
|
|
@@ -20,29 +15,28 @@ const HELP = `toolkit config — 配置管理
|
|
|
20
15
|
|
|
21
16
|
可用配置项:
|
|
22
17
|
registry 平台根地址(如 http://your-host:38000)
|
|
23
|
-
project-filter
|
|
18
|
+
project-filter 项目过滤关键词,逗号分隔;留空 = 全部上传
|
|
19
|
+
es.url ES 地址(log-query 用)
|
|
20
|
+
es.index ES 索引模式
|
|
21
|
+
es.app 默认应用名
|
|
24
22
|
`;
|
|
25
23
|
|
|
26
|
-
function load() {
|
|
27
|
-
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); }
|
|
28
|
-
catch { console.error('配置文件不存在。先运行: toolkit install claudecode --registry <url>'); process.exit(1); }
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function save(config) {
|
|
32
|
-
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
24
|
export function configCmd(args) {
|
|
36
25
|
const sub = args[0];
|
|
37
26
|
|
|
38
27
|
if (!sub || sub === 'list' || sub === 'help' || sub === '--help') {
|
|
39
28
|
const config = load();
|
|
29
|
+
if (!config.registry && !config.uid) {
|
|
30
|
+
console.error('配置文件不存在。先运行: toolkit install claudecode --registry <url>');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
40
33
|
const pf = config['project-filter'];
|
|
41
34
|
console.log('Toolkit 配置 (' + CONFIG_PATH + ')');
|
|
42
35
|
console.log('════════════════════════════════════════');
|
|
43
36
|
console.log(` registry: ${config.registry || '(未设置)'}`);
|
|
44
37
|
console.log(` project-filter: ${pf && pf.length ? pf.join(', ') : '(空 = 全部上传)'}`);
|
|
45
38
|
console.log(` uid: ${config.uid?.slice(0, 12)}...`);
|
|
39
|
+
if (config.es) console.log(` es.url: ${config.es.url || '(未配置)'}`);
|
|
46
40
|
console.log('════════════════════════════════════════');
|
|
47
41
|
return;
|
|
48
42
|
}
|
|
@@ -50,12 +44,16 @@ export function configCmd(args) {
|
|
|
50
44
|
if (sub === 'get') {
|
|
51
45
|
const key = args[1];
|
|
52
46
|
if (!key) { console.error('用法: toolkit config get <key>'); process.exit(2); }
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
if (key.includes('.')) {
|
|
48
|
+
const [ns, ...rest] = key.split('.');
|
|
49
|
+
let cur = load()[ns];
|
|
50
|
+
for (const k of rest) cur = cur?.[k];
|
|
51
|
+
console.log(cur ?? '');
|
|
52
|
+
} else if (key === 'project-filter') {
|
|
53
|
+
const pf = get('project-filter');
|
|
56
54
|
console.log(pf && pf.length ? pf.join(', ') : '');
|
|
57
55
|
} else {
|
|
58
|
-
console.log(
|
|
56
|
+
console.log(get(key) ?? '');
|
|
59
57
|
}
|
|
60
58
|
return;
|
|
61
59
|
}
|
|
@@ -64,32 +62,27 @@ export function configCmd(args) {
|
|
|
64
62
|
const key = args[1];
|
|
65
63
|
const value = args.slice(2).join(' ');
|
|
66
64
|
if (!key || !value) { console.error('用法: toolkit config set <key> <value>'); process.exit(2); }
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
config
|
|
70
|
-
|
|
71
|
-
config.
|
|
65
|
+
if (key.startsWith('es.')) {
|
|
66
|
+
const [, esKey] = key.split('.');
|
|
67
|
+
const config = load();
|
|
68
|
+
if (!config.es) config.es = {};
|
|
69
|
+
config.es[esKey] = esKey === 'url' ? value.replace(/\/$/, '') : value;
|
|
70
|
+
save(config);
|
|
71
|
+
console.log(`✅ ${key} = ${config.es[esKey]}`);
|
|
72
72
|
} else {
|
|
73
|
-
|
|
73
|
+
set(key, value);
|
|
74
|
+
const display = key === 'project-filter'
|
|
75
|
+
? (get('project-filter').join(', ') || '(空)')
|
|
76
|
+
: get(key);
|
|
77
|
+
console.log(`✅ ${key} = ${display}`);
|
|
74
78
|
}
|
|
75
|
-
save(config);
|
|
76
|
-
const display = key === 'project-filter'
|
|
77
|
-
? (config['project-filter'].join(', ') || '(空)')
|
|
78
|
-
: config[key];
|
|
79
|
-
console.log(`✅ ${key} = ${display}`);
|
|
80
79
|
return;
|
|
81
80
|
}
|
|
82
81
|
|
|
83
82
|
if (sub === 'unset') {
|
|
84
83
|
const key = args[1];
|
|
85
84
|
if (!key) { console.error('用法: toolkit config unset <key>'); process.exit(2); }
|
|
86
|
-
|
|
87
|
-
if (key === 'project-filter') {
|
|
88
|
-
config['project-filter'] = [];
|
|
89
|
-
} else {
|
|
90
|
-
delete config[key];
|
|
91
|
-
}
|
|
92
|
-
save(config);
|
|
85
|
+
unset(key);
|
|
93
86
|
console.log(`✅ ${key} 已清除`);
|
|
94
87
|
return;
|
|
95
88
|
}
|
package/src/flush.mjs
CHANGED
|
@@ -10,13 +10,9 @@ import os from 'node:os';
|
|
|
10
10
|
import { deflateRawSync } from 'node:zlib';
|
|
11
11
|
import { randomUUID } from 'node:crypto';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { load as loadConfig, UPLOADED_PATH, LOG_DIR } from './shared/config.mjs';
|
|
13
14
|
|
|
14
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
15
|
const HOME = os.homedir();
|
|
16
|
-
const TOOLKIT_DIR = path.join(HOME, '.toolkit');
|
|
17
|
-
const CONFIG_PATH = path.join(TOOLKIT_DIR, 'config.json');
|
|
18
|
-
const UPLOADED_PATH = path.join(TOOLKIT_DIR, 'uploaded.json');
|
|
19
|
-
const LOG_DIR = path.join(TOOLKIT_DIR, 'logs');
|
|
20
16
|
const CLAUDE_PROJECTS = path.join(HOME, '.claude', 'projects');
|
|
21
17
|
|
|
22
18
|
// ── 日志 ──────────────────────────────────────────────
|
|
@@ -30,15 +26,8 @@ function log(...args) {
|
|
|
30
26
|
if (process.env.TOOLKIT_VERBOSE) process.stderr.write(line);
|
|
31
27
|
}
|
|
32
28
|
|
|
33
|
-
// ── 读配置
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
|
|
37
|
-
} catch {
|
|
38
|
-
log('ERROR: config.json not found. Run `toolkit install` first.');
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
29
|
+
// ── 读配置(走 shared/config.mjs)─────────────────────
|
|
30
|
+
// loadConfig 从 shared/config.mjs 导入
|
|
42
31
|
|
|
43
32
|
function loadUploaded() {
|
|
44
33
|
try {
|
package/src/install.mjs
CHANGED
|
@@ -8,11 +8,11 @@ import path from 'node:path';
|
|
|
8
8
|
import os from 'node:os';
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { load as loadConfig, save as saveConfig, TOOLKIT_DIR, UPLOADED_PATH, LOG_DIR } from './shared/config.mjs';
|
|
11
12
|
|
|
12
13
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const HOME = os.homedir();
|
|
14
15
|
const PLUGIN_DIR = path.join(HOME, '.claude', 'plugins', 'toolkit');
|
|
15
|
-
const TOOLKIT_DIR = path.join(HOME, '.toolkit');
|
|
16
16
|
const SETTINGS_PATH = path.join(HOME, '.claude', 'settings.json');
|
|
17
17
|
const PLUGIN_KEY = 'toolkit@local';
|
|
18
18
|
|
|
@@ -36,7 +36,9 @@ const HOOKS_JSON = {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
export function install(registry, opts = {}) {
|
|
39
|
+
const agent = opts.agent || 'claudecode';
|
|
39
40
|
// 1. 检测 Claude Code
|
|
41
|
+
// (后续 --agent qoder 时,检测 ~/.qoder)
|
|
40
42
|
const claudeDir = path.join(HOME, '.claude');
|
|
41
43
|
if (!fs.existsSync(claudeDir)) {
|
|
42
44
|
console.error('ERROR: ~/.claude not found. Is Claude Code installed?');
|
|
@@ -62,24 +64,18 @@ export function install(registry, opts = {}) {
|
|
|
62
64
|
fs.copyFileSync(path.join(srcDir, 'hook.mjs'), path.join(PLUGIN_DIR, 'hook.mjs'));
|
|
63
65
|
fs.copyFileSync(path.join(srcDir, 'flush.mjs'), path.join(PLUGIN_DIR, 'flush.mjs'));
|
|
64
66
|
|
|
65
|
-
// 5. 创建 ~/.toolkit/ 配置
|
|
66
|
-
fs.mkdirSync(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// 读取已有 config(保留 uid)或新建
|
|
70
|
-
const configPath = path.join(TOOLKIT_DIR, 'config.json');
|
|
71
|
-
let config = {};
|
|
72
|
-
try { config = JSON.parse(fs.readFileSync(configPath, 'utf-8')); } catch {}
|
|
67
|
+
// 5. 创建 ~/.toolkit/ 配置(走 shared/config.mjs)
|
|
68
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
69
|
+
const config = loadConfig();
|
|
73
70
|
if (!config.uid) config.uid = randomUUID();
|
|
74
71
|
config.registry = registry.replace(/\/$/, '');
|
|
75
72
|
config.contexts = { metrics: '/ai-metrics', skills: '/skillhub' };
|
|
76
73
|
config.installed_at = new Date().toISOString();
|
|
77
|
-
|
|
74
|
+
saveConfig(config);
|
|
78
75
|
|
|
79
76
|
// 6. uploaded.json 初始化(如果不存在)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
fs.writeFileSync(uploadedPath, JSON.stringify({ uploaded_sids: [], last_flush: null }, null, 2));
|
|
77
|
+
if (!fs.existsSync(UPLOADED_PATH)) {
|
|
78
|
+
fs.writeFileSync(UPLOADED_PATH, JSON.stringify({ uploaded_sids: [], last_flush: null }, null, 2));
|
|
83
79
|
}
|
|
84
80
|
|
|
85
81
|
// 7. 启用插件(settings.json)
|
|
@@ -92,7 +88,7 @@ export function install(registry, opts = {}) {
|
|
|
92
88
|
// 8. 输出
|
|
93
89
|
console.log('✅ 安装完成。\n');
|
|
94
90
|
console.log(` 插件目录: ${PLUGIN_DIR}`);
|
|
95
|
-
console.log(` 配置文件: ${
|
|
91
|
+
console.log(` 配置文件: ${path.join(TOOLKIT_DIR, 'config.json')}`);
|
|
96
92
|
console.log(` 身份 UID: ${config.uid.slice(0, 12)}...`);
|
|
97
93
|
console.log(` 平台地址: ${config.registry}${config.contexts.metrics}/`);
|
|
98
94
|
console.log(`\n Claude Code 会话结束后,数据自动上报到上述地址。`);
|
package/src/logquery.mjs
CHANGED
|
@@ -24,32 +24,12 @@ import fs from 'node:fs';
|
|
|
24
24
|
import path from 'node:path';
|
|
25
25
|
import os from 'node:os';
|
|
26
26
|
import readline from 'node:readline';
|
|
27
|
+
import { load, save, getEs, setEs, CONFIG_PATH } from './shared/config.mjs';
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
function loadConfig() {
|
|
33
|
-
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); }
|
|
34
|
-
catch { return {}; }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function saveConfig(config) {
|
|
38
|
-
fs.mkdirSync(TOOLKIT_DIR, { recursive: true });
|
|
39
|
-
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function getEsConfig() {
|
|
43
|
-
const config = loadConfig();
|
|
44
|
-
return {
|
|
45
|
-
url: config.es?.url || '',
|
|
46
|
-
index: config.es?.index || 'plume_log_run_*',
|
|
47
|
-
app: config.es?.app || '',
|
|
48
|
-
user: config.es?.user || '',
|
|
49
|
-
pass: config.es?.pass || '',
|
|
50
|
-
defaults: { level: 'ERROR', window: 5, hours: 1 },
|
|
51
|
-
};
|
|
52
|
-
}
|
|
29
|
+
// ── 配置(走 shared/config.mjs)─────────────────────────
|
|
30
|
+
const loadConfig = load;
|
|
31
|
+
const saveConfig = save;
|
|
32
|
+
const getEsConfig = getEs;
|
|
53
33
|
|
|
54
34
|
// ── 向导 ──────────────────────────────────────────────
|
|
55
35
|
async function ask(rl, prompt, def) {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shared/config.mjs — 统一配置管理(~/.toolkit/config.json)
|
|
3
|
+
*
|
|
4
|
+
* 所有 toolkit 工具(install/flush/config/status/logquery)共用。
|
|
5
|
+
* zentao 仍用独立的 ~/.zentao-cli/.env(向后兼容)。
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
|
|
11
|
+
export const TOOLKIT_DIR = process.env.TOOLKIT_HOME || path.join(os.homedir(), '.toolkit');
|
|
12
|
+
export const CONFIG_PATH = path.join(TOOLKIT_DIR, 'config.json');
|
|
13
|
+
export const UPLOADED_PATH = path.join(TOOLKIT_DIR, 'uploaded.json');
|
|
14
|
+
export const LOG_DIR = path.join(TOOLKIT_DIR, 'logs');
|
|
15
|
+
|
|
16
|
+
/** 读全部配置。文件不存在返回 {}。 */
|
|
17
|
+
export function load() {
|
|
18
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); }
|
|
19
|
+
catch { return {}; }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 写全部配置(自动建目录)。 */
|
|
23
|
+
export function save(config) {
|
|
24
|
+
fs.mkdirSync(TOOLKIT_DIR, { recursive: true });
|
|
25
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 取单个配置项。
|
|
30
|
+
* @param {string} key - 顶层 key(registry / uid / project-filter / es / ...)
|
|
31
|
+
* @returns {any}
|
|
32
|
+
*/
|
|
33
|
+
export function get(key) {
|
|
34
|
+
return load()[key];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 设置单个配置项。
|
|
39
|
+
* project-filter 特殊处理:逗号分隔字符串 → 数组。
|
|
40
|
+
* registry 特殊处理:去尾部 /。
|
|
41
|
+
* @param {string} key
|
|
42
|
+
* @param {string} value
|
|
43
|
+
*/
|
|
44
|
+
export function set(key, value) {
|
|
45
|
+
const config = load();
|
|
46
|
+
if (key === 'project-filter') {
|
|
47
|
+
config['project-filter'] = String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
48
|
+
} else if (key === 'registry') {
|
|
49
|
+
config.registry = String(value).replace(/\/$/, '');
|
|
50
|
+
} else {
|
|
51
|
+
config[key] = value;
|
|
52
|
+
}
|
|
53
|
+
save(config);
|
|
54
|
+
return config[key];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 删除单个配置项。project-filter 置空数组,其他 delete。 */
|
|
58
|
+
export function unset(key) {
|
|
59
|
+
const config = load();
|
|
60
|
+
if (key === 'project-filter') {
|
|
61
|
+
config['project-filter'] = [];
|
|
62
|
+
} else {
|
|
63
|
+
delete config[key];
|
|
64
|
+
}
|
|
65
|
+
save(config);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** 读取 ES 配置子段(给 log-query 用)。 */
|
|
69
|
+
export function getEs() {
|
|
70
|
+
const es = load().es || {};
|
|
71
|
+
return {
|
|
72
|
+
url: es.url || '',
|
|
73
|
+
index: es.index || 'plume_log_run_*',
|
|
74
|
+
app: es.app || '',
|
|
75
|
+
user: es.user || '',
|
|
76
|
+
pass: es.pass || '',
|
|
77
|
+
defaults: { level: 'ERROR', window: 5, hours: 1 },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** 写入 ES 配置子段(给 log-query init 用)。 */
|
|
82
|
+
export function setEs(partial) {
|
|
83
|
+
const config = load();
|
|
84
|
+
config.es = { ...(config.es || {}), ...partial };
|
|
85
|
+
save(config);
|
|
86
|
+
return config.es;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── zentao 段 ────────────────────────────────────────
|
|
90
|
+
// 键名映射:JSON camelCase ↔ .env ZENTAO_UPPER_CASE
|
|
91
|
+
const ZENTAO_KEY_MAP = {
|
|
92
|
+
'url': 'ZENTAO_URL',
|
|
93
|
+
'username': 'ZENTAO_USERNAME',
|
|
94
|
+
'password': 'ZENTAO_PASSWORD',
|
|
95
|
+
'default-project': 'ZENTAO_DEFAULT_PROJECT',
|
|
96
|
+
'token-ttl': 'ZENTAO_TOKEN_TTL',
|
|
97
|
+
'cache-stale-ms': 'ZENTAO_CACHE_STALE_MS',
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** 读 zentao 段(给 zentao CLI 用)。 */
|
|
101
|
+
export function getZentao() {
|
|
102
|
+
return load().zentao || {};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** 设置 zentao 段的单个 key。 */
|
|
106
|
+
export function setZentao(key, value) {
|
|
107
|
+
const config = load();
|
|
108
|
+
if (!config.zentao) config.zentao = {};
|
|
109
|
+
config.zentao[key] = value;
|
|
110
|
+
save(config);
|
|
111
|
+
return config.zentao[key];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** 删除 zentao 段的单个 key。 */
|
|
115
|
+
export function unsetZentao(key) {
|
|
116
|
+
const config = load();
|
|
117
|
+
if (config.zentao) {
|
|
118
|
+
delete config.zentao[key];
|
|
119
|
+
save(config);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** 导出键名映射(给 zentao 迁移用)。 */
|
|
124
|
+
export { ZENTAO_KEY_MAP };
|
|
125
|
+
|