ronds_ai 0.1.24 → 0.1.26
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 +33 -25
- package/bin/ronds_ai.js +48 -33
- package/lib/analyze_claude.js +27 -460
- package/lib/analyze_config.js +35 -13
- package/lib/analyze_diagnostics.js +606 -0
- package/lib/analyze_session.js +130 -0
- package/lib/doctor.js +85 -12
- package/lib/hooks_auto_sync.js +22 -8
- package/lib/hooks_deploy.js +72 -20
- package/package.json +2 -7
- package/lib/analyze_langfuse.js +0 -736
- package/lib/analyze_transcript.js +0 -803
package/README.md
CHANGED
|
@@ -174,30 +174,34 @@ Codex 输出示例:
|
|
|
174
174
|
|
|
175
175
|
### `analyze`
|
|
176
176
|
|
|
177
|
-
从 Claude Code 的会话 transcript
|
|
177
|
+
从 Claude Code 的会话 transcript 生成一次性会话摘要:
|
|
178
178
|
|
|
179
179
|
```bash
|
|
180
180
|
ronds_ai analyze claude
|
|
181
181
|
```
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
该命令仅用于 `SessionEnd` Hook,会由 `hooks deploy` 自动安装(project 或 user scope,未用 `--tool` 排除 claude 时)。它只上报:
|
|
184
184
|
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
- 一层 Subagent 的 Generation 和 Tool 轨迹
|
|
185
|
+
- 会话起止时间与有效用户轮数
|
|
186
|
+
- Skill 名称及调用次数
|
|
187
|
+
- 首轮输入的最多 150 个字符预览
|
|
188
|
+
- repo、branch、worker_id 和 Git 身份
|
|
190
189
|
|
|
191
|
-
|
|
190
|
+
扫描采用流式 JSONL 读取,内存不会随 transcript 大小增长,不上传逐轮内容或代码。
|
|
191
|
+
|
|
192
|
+
每次 Hook 调用仅生成一条会话级结构化快照;网络失败时写入有界队列,后续 Hook 最多补发少量旧快照。内部部署仅保留用户、会话、项目目录等必要定位字段。
|
|
193
|
+
|
|
194
|
+
运行期间只写一个轻量 active 标记,结束时一次性转换为 `~/.ronds_ai/analyze/` 下的待补传 spool。网络失败不会影响 Hook 主流程,并按 5、15、60 分钟退避补传;平台按 `diagnostic_event_id` 幂等去重。可选配置:
|
|
195
|
+
|
|
196
|
+
- `ANALYZE_DIAGNOSTICS_URL`:覆盖诊断事件上报地址
|
|
197
|
+
- `ANALYZE_DIAGNOSTICS_TOKEN`:诊断入口的 Bearer Token
|
|
198
|
+
- `ANALYZE_DIAGNOSTICS_TIMEOUT_MS`:单次补传超时,默认 `400`
|
|
192
199
|
|
|
193
|
-
|
|
200
|
+
会话入口地址默认为本机 API,也可按部署环境覆盖:
|
|
194
201
|
|
|
195
|
-
- `
|
|
196
|
-
- `CC_LANGFUSE_MAX_CHARS=<正整数>`:单个文本字段的最大字符数,默认 `20000`
|
|
197
|
-
- `CC_LANGFUSE_SKILL_TAGS=false`:关闭 `skill:<name>` 标签
|
|
198
|
-
- `CC_LANGFUSE_CAPTURE_SKILL_CONTENT=true`:把 Skill 注入内容写入 Tool 输出
|
|
202
|
+
- `AI_USAGE_SESSIONS_URL`:会话摘要 API 地址
|
|
199
203
|
|
|
200
|
-
`hooks deploy` 会自动把以下条目写入 Claude Code 的 hooks
|
|
204
|
+
`hooks deploy` 会自动把以下条目写入 Claude Code 的 hooks 配置:
|
|
201
205
|
|
|
202
206
|
```json
|
|
203
207
|
{
|
|
@@ -236,29 +240,33 @@ Node.js 低于 20 或安装时使用了 `--omit=optional` 时,只有 `analyze`
|
|
|
236
240
|
ronds_ai doctor <tool>
|
|
237
241
|
```
|
|
238
242
|
|
|
239
|
-
支持的 `tool`:
|
|
240
|
-
|
|
241
|
-
- `
|
|
243
|
+
支持的 `tool`:
|
|
244
|
+
|
|
245
|
+
- `analyze`
|
|
246
|
+
- `claude`
|
|
242
247
|
- `codex`
|
|
243
248
|
- `cursor`
|
|
244
249
|
- `hermes`
|
|
245
250
|
|
|
246
251
|
示例:
|
|
247
252
|
|
|
248
|
-
```bash
|
|
249
|
-
npx ronds_ai@latest doctor
|
|
253
|
+
```bash
|
|
254
|
+
npx ronds_ai@latest doctor analyze
|
|
255
|
+
npx ronds_ai@latest doctor claude
|
|
250
256
|
npx ronds_ai@latest doctor codex
|
|
251
257
|
npx ronds_ai@latest doctor cursor
|
|
252
258
|
npx ronds_ai@latest doctor hermes
|
|
253
259
|
```
|
|
254
260
|
|
|
255
|
-
输出内容包括:
|
|
261
|
+
输出内容包括:
|
|
256
262
|
|
|
257
263
|
- 当前检查的工具类型
|
|
258
264
|
- 当前目标目录
|
|
259
265
|
- 当前目录下的 `git user.email`
|
|
260
266
|
- 关键配置文件是否存在
|
|
261
|
-
- 今天的最近错误日志内容
|
|
267
|
+
- 今天的最近错误日志内容
|
|
268
|
+
|
|
269
|
+
`doctor analyze` 还会检查 Node.js 版本、用户级 `SessionEnd` Hook、工号和 Git 用户身份,以及本地待补传会话数量。它只做只读探测,不会发送会话数据。
|
|
262
270
|
|
|
263
271
|
Claude 会检查:
|
|
264
272
|
|
|
@@ -297,14 +305,14 @@ npx ronds_ai@latest hooks deploy --scope user
|
|
|
297
305
|
默认 scope 为 `project`(项目级部署),行为:
|
|
298
306
|
|
|
299
307
|
- 写入或更新 `.cursor/hooks.json`
|
|
300
|
-
- 写入或更新 `.claude/settings.json`(`PostToolUse` 的 `record claude` 采集 hook,以及 `
|
|
308
|
+
- 写入或更新 `.claude/settings.json`(`PostToolUse` 的 `record claude` 采集 hook,以及 `SessionEnd` 的 `analyze claude` 会话摘要 hook)
|
|
301
309
|
- 清理项目内旧版 Cursor、Claude、Codex hook 脚本
|
|
302
310
|
- 跳过 Codex 项目级配置部署,并在输出中说明原因
|
|
303
311
|
|
|
304
312
|
`--scope user`(用户级部署),行为:
|
|
305
313
|
|
|
306
314
|
- 写入或更新用户级 Cursor hook 配置
|
|
307
|
-
- 写入或更新用户级 Claude hook 配置(同上:`record claude` 采集 hook + `analyze claude`
|
|
315
|
+
- 写入或更新用户级 Claude hook 配置(同上:`record claude` 采集 hook + `analyze claude` 会话摘要 hook)
|
|
308
316
|
- 写入或更新系统级 `requirements.toml`(Windows: `C:\ProgramData\OpenAI\Codex\requirements.toml`,Linux / macOS: `/etc/codex/requirements.toml`),包含 `[features] hooks = true`、`[hooks]` 托管目录配置以及 `PostToolUse` 的 `apply_patch` hook
|
|
309
317
|
- 清理用户目录中的旧版 hook 脚本文件
|
|
310
318
|
|
|
@@ -342,7 +350,7 @@ hooks_auto_accept: true
|
|
|
342
350
|
- `updatedFiles`
|
|
343
351
|
- `removedFiles`
|
|
344
352
|
- `skipped`: 被跳过的工具及原因(仅在 project scope 下出现)
|
|
345
|
-
- `analyze`: Claude
|
|
353
|
+
- `analyze`: Claude 会话摘要 hook 的安装状态(`installed`、`events`、`command`)
|
|
346
354
|
|
|
347
355
|
### Hooks Auto Sync(自动后台同步)
|
|
348
356
|
|
|
@@ -365,7 +373,7 @@ RONDS_AI_DISABLE_HOOKS_AUTO_SYNC=1
|
|
|
365
373
|
注意:
|
|
366
374
|
|
|
367
375
|
- auto-sync 只维护**用户级** hooks,不影响项目级配置
|
|
368
|
-
- auto-sync 会同步维护用户级 Claude 的 `analyze claude`
|
|
376
|
+
- auto-sync 会同步维护用户级 Claude 的 `analyze claude` 会话摘要 hook
|
|
369
377
|
- 后台同步失败时,错误记录到 `~/.ronds_ai/hooks_auto_sync_error.log`,不污染当前命令输出
|
|
370
378
|
- 首次安装 hooks 仍建议执行 `ronds_ai hooks deploy --scope user`
|
|
371
379
|
|
package/bin/ronds_ai.js
CHANGED
|
@@ -62,10 +62,10 @@ const SUPPORTED_SOURCES = new Set(['claude', 'cursor', 'codex', 'hermes']);
|
|
|
62
62
|
|
|
63
63
|
function printUsage() {
|
|
64
64
|
process.stderr.write([
|
|
65
|
-
'Usage:',
|
|
66
|
-
' ronds_ai record <tool>',
|
|
67
|
-
' ronds_ai analyze claude',
|
|
68
|
-
' ronds_ai check record',
|
|
65
|
+
'Usage:',
|
|
66
|
+
' ronds_ai record <tool>',
|
|
67
|
+
' ronds_ai analyze claude',
|
|
68
|
+
' ronds_ai check record',
|
|
69
69
|
' ronds_ai doctor <tool>',
|
|
70
70
|
' ronds_ai hooks deploy [--scope project|user] [--tool cursor|claude|codex]',
|
|
71
71
|
' ronds_ai hooks deploy hermes',
|
|
@@ -78,11 +78,12 @@ function printUsage() {
|
|
|
78
78
|
' hermes',
|
|
79
79
|
'',
|
|
80
80
|
'Examples:',
|
|
81
|
-
' npx ronds_ai@latest record claude',
|
|
82
|
-
' npx ronds_ai@latest analyze claude',
|
|
81
|
+
' npx ronds_ai@latest record claude',
|
|
82
|
+
' npx ronds_ai@latest analyze claude',
|
|
83
83
|
' npx ronds_ai@latest record cursor',
|
|
84
84
|
' npx ronds_ai@latest check record',
|
|
85
|
-
' npx ronds_ai@latest doctor claude',
|
|
85
|
+
' npx ronds_ai@latest doctor claude',
|
|
86
|
+
' npx ronds_ai@latest doctor analyze',
|
|
86
87
|
' npx ronds_ai@latest doctor cursor',
|
|
87
88
|
' npx ronds_ai@latest hooks deploy',
|
|
88
89
|
' npx ronds_ai@latest hooks deploy --scope user',
|
|
@@ -282,7 +283,7 @@ async function run() {
|
|
|
282
283
|
|
|
283
284
|
maybeStartHooksAutoSync({ command, args });
|
|
284
285
|
|
|
285
|
-
if (command === 'record') {
|
|
286
|
+
if (command === 'record') {
|
|
286
287
|
const [source] = args;
|
|
287
288
|
const normalizedSource = String(source || '').trim().toLowerCase();
|
|
288
289
|
|
|
@@ -291,30 +292,30 @@ async function run() {
|
|
|
291
292
|
}
|
|
292
293
|
|
|
293
294
|
await runCodeRecord(normalizedSource);
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (command === 'analyze') {
|
|
298
|
-
const [source] = args;
|
|
299
|
-
if (String(source || '').trim().toLowerCase() !== 'claude') {
|
|
300
|
-
throw new Error(`Unsupported analyze tool: ${source || ''}`);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// 仅在
|
|
304
|
-
const { runClaudeAnalyze } = require('../lib/analyze_claude');
|
|
305
|
-
await runClaudeAnalyze();
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (command === 'doctor') {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (command === 'analyze') {
|
|
299
|
+
const [source] = args;
|
|
300
|
+
if (String(source || '').trim().toLowerCase() !== 'claude') {
|
|
301
|
+
throw new Error(`Unsupported analyze tool: ${source || ''}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// analyze 仅在 SessionEnd 生成会话级摘要,使用 Node 16 原生 API。
|
|
305
|
+
const { runClaudeAnalyze } = require('../lib/analyze_claude');
|
|
306
|
+
await runClaudeAnalyze();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (command === 'doctor') {
|
|
310
311
|
const [tool] = args;
|
|
311
312
|
const normalizedTool = String(tool || '').trim().toLowerCase();
|
|
312
313
|
|
|
313
|
-
if (!SUPPORTED_SOURCES.has(normalizedTool)) {
|
|
314
|
+
if (!SUPPORTED_SOURCES.has(normalizedTool) && normalizedTool !== 'analyze') {
|
|
314
315
|
throw new Error(`Unsupported doctor tool: ${tool || ''}`);
|
|
315
316
|
}
|
|
316
317
|
|
|
317
|
-
const result = runDoctor(normalizedTool, process.cwd());
|
|
318
|
+
const result = await runDoctor(normalizedTool, process.cwd());
|
|
318
319
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
319
320
|
return;
|
|
320
321
|
}
|
|
@@ -342,14 +343,28 @@ async function run() {
|
|
|
342
343
|
throw new Error(`Unsupported command: ${command || ''}`);
|
|
343
344
|
}
|
|
344
345
|
|
|
345
|
-
run().catch((error) => {
|
|
346
|
+
run().catch((error) => {
|
|
346
347
|
if (error && error.code === 'ANALYZE_PREFLIGHT') {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
348
|
+
// 旁路 analyze 错误时静默记录,避免干扰宿主 Hook。
|
|
349
|
+
try {
|
|
350
|
+
const fs = require('fs');
|
|
351
|
+
const os = require('os');
|
|
352
|
+
const path = require('path');
|
|
353
|
+
const analyzeDir = path.join(os.homedir(), '.ronds_ai', 'analyze');
|
|
354
|
+
fs.mkdirSync(analyzeDir, { recursive: true });
|
|
355
|
+
fs.appendFileSync(
|
|
356
|
+
path.join(analyzeDir, 'session_hook.log'),
|
|
357
|
+
`${new Date().toISOString()} [INFO] analyze preflight skipped: ${error.message}\n`,
|
|
358
|
+
'utf8',
|
|
359
|
+
);
|
|
360
|
+
} catch {
|
|
361
|
+
// 忽略日志写入错误
|
|
362
|
+
}
|
|
363
|
+
process.exitCode = 0;
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const [, , command, ...args] = process.argv;
|
|
353
368
|
let savedPath = '';
|
|
354
369
|
|
|
355
370
|
try {
|