@kienntpixon/dieu-phoi 0.1.0 → 0.1.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/CHANGELOG.md +11 -0
- package/README.md +1 -0
- package/hook.cjs +39 -4
- package/lib/aggregate.cjs +16 -1
- package/lib/commands.cjs +4 -0
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog — @kienntpixon/dieu-phoi
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- Sửa: Claude Code bắn `SubagentStop` cả cho subagent nội bộ của nó (không có transcript `agent-<id>.jsonl`, không qua Agent tool). Bản 0.1.0 ghi các lần này thành `complete` rỗng (`subagent_type ""`, `model null`, `tokens null`). Hook giờ bỏ qua khi transcript thiếu / rỗng / không có dòng assistant nào có usage **và** không có `dispatch` cho `agent_id` đó; số lần bỏ qua đếm ở `data/skipped-internal.json` (`doctor` hiển thị).
|
|
6
|
+
- `stats` / `choose` / `retro` (và sink) bỏ qua các `complete` rỗng đã lỡ ghi bởi 0.1.0 — không cần sửa file log.
|
|
7
|
+
- `fixtures/parity-events.jsonl` thêm hai bản ghi dạng này; kết quả parity không đổi.
|
|
8
|
+
|
|
9
|
+
## 0.1.0
|
|
10
|
+
|
|
11
|
+
- Bản đầu: hook PostToolUse(Agent) + SubagentStop, `setup/doctor/verdict/steer/stats/choose/plan-table/sync/retro/uninstall`.
|
package/README.md
CHANGED
|
@@ -82,6 +82,7 @@ Kết quả `--json`: `{"action":"dispatch","model":"sonnet","model_id":"claude-
|
|
|
82
82
|
|
|
83
83
|
- Sự kiện: `~/.claude/dieu-phoi/data/events.jsonl` (`dispatch`, `complete`, `verdict`, `steer`, `override`; `schema: 1`), xem `CONTRACT.md`. File chỉ ghi nối (append-only), không bao giờ viết lại. Tiến độ đẩy lên sink nằm ở `data/sync-offset.json` (byte offset sau dòng cuối đã đẩy); trạng thái segment từng agent ở `data/segments/<agent_id>.json`.
|
|
84
84
|
- Lỗi của hook: `~/.claude/dieu-phoi/hook-errors.log`.
|
|
85
|
+
- Subagent nội bộ của Claude Code (không có transcript, không qua Agent tool) không được ghi; số lần bỏ qua ở `data/skipped-internal.json`. Log cũ của 0.1.0 có các `complete` rỗng loại này — `stats`/`retro` tự bỏ qua, không cần sửa file (từ 0.1.1).
|
|
85
86
|
- Người dùng và phiên chỉ lưu dạng sha256 rút gọn 8 ký tự (người dùng = `git config user.email`, không có thì tên tài khoản máy).
|
|
86
87
|
- Chi phí tính theo giá API trong `pricing.json` (USD / 1M token), đếm usage một lần cho mỗi `requestId`. Hệ số ghi cache (×1.25 cho 5 phút, ×2 cho 1 giờ) là **giả định**. Model không có giá → `cost_usd: null`. Ghi đè giá trong `config.json`:
|
|
87
88
|
|
package/hook.cjs
CHANGED
|
@@ -19,7 +19,7 @@ const path = require('path');
|
|
|
19
19
|
const os = require('os');
|
|
20
20
|
const crypto = require('crypto');
|
|
21
21
|
|
|
22
|
-
const CORE_VERSION = '0.1.
|
|
22
|
+
const CORE_VERSION = '0.1.1';
|
|
23
23
|
const SCHEMA = 1;
|
|
24
24
|
const TASK_TYPES = [
|
|
25
25
|
'search', 'extract', 'mechanical-edit', 'feature-impl', 'debug', 'refactor',
|
|
@@ -119,6 +119,7 @@ function pathsFor(home) {
|
|
|
119
119
|
data,
|
|
120
120
|
events: path.join(data, 'events.jsonl'),
|
|
121
121
|
segments: path.join(data, 'segments'),
|
|
122
|
+
skippedInternal: path.join(data, 'skipped-internal.json'),
|
|
122
123
|
syncOffset: path.join(data, 'sync-offset.json'),
|
|
123
124
|
syncSpawn: path.join(data, 'sync-spawn.json'),
|
|
124
125
|
lastSync: path.join(data, 'last-sync.json'),
|
|
@@ -562,6 +563,33 @@ function handlePostToolUse(p, ctx) {
|
|
|
562
563
|
return ev;
|
|
563
564
|
}
|
|
564
565
|
|
|
566
|
+
function hasAssistantUsage(lines) {
|
|
567
|
+
return lines.some((l) => l && l.type === 'assistant' && l.message && l.message.usage
|
|
568
|
+
&& l.message.model !== '<synthetic>');
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/** Whether events.jsonl holds a dispatch for this agent (cheap substring scan, rare path). */
|
|
572
|
+
function hasDispatch(ctx, agentId) {
|
|
573
|
+
let txt;
|
|
574
|
+
try { txt = fs.readFileSync(ctx.paths.events, 'utf8'); } catch (_) { return false; }
|
|
575
|
+
const needle = `"agent_id":${JSON.stringify(agentId)}`;
|
|
576
|
+
let i = txt.indexOf(needle);
|
|
577
|
+
while (i >= 0) {
|
|
578
|
+
const start = txt.lastIndexOf('\n', i) + 1;
|
|
579
|
+
const end = txt.indexOf('\n', i);
|
|
580
|
+
if (txt.slice(start, end < 0 ? undefined : end).includes('"kind":"dispatch"')) return true;
|
|
581
|
+
i = txt.indexOf(needle, i + needle.length);
|
|
582
|
+
}
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function countSkippedInternal(ctx) {
|
|
587
|
+
try {
|
|
588
|
+
const cur = readJsonFile(ctx.paths.skippedInternal, {}) || {};
|
|
589
|
+
writeFileAtomic(ctx.paths.skippedInternal, `${JSON.stringify({ count: (Number(cur.count) || 0) + 1, last_ts: nowIso() })}\n`);
|
|
590
|
+
} catch (_) { /* best effort */ }
|
|
591
|
+
}
|
|
592
|
+
|
|
565
593
|
function handleSubagentStop(p, ctx) {
|
|
566
594
|
const agentId = typeof p.agent_id === 'string' && p.agent_id ? p.agent_id : null;
|
|
567
595
|
if (!agentId) return null;
|
|
@@ -570,8 +598,15 @@ function handleSubagentStop(p, ctx) {
|
|
|
570
598
|
let sum = null;
|
|
571
599
|
let tag = null;
|
|
572
600
|
const tpath = p.agent_transcript_path;
|
|
573
|
-
|
|
574
|
-
|
|
601
|
+
const exists = typeof tpath === 'string' && tpath && fs.existsSync(tpath);
|
|
602
|
+
const lines = exists ? readTranscript(tpath) : [];
|
|
603
|
+
// Claude Code also fires SubagentStop for its own internal subagents: no transcript on disk (or no
|
|
604
|
+
// API usage in it) and no Agent-tool dispatch. Those are not user work; record nothing.
|
|
605
|
+
if (!hasAssistantUsage(lines) && !hasDispatch(ctx, agentId)) {
|
|
606
|
+
countSkippedInternal(ctx);
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
if (exists) {
|
|
575
610
|
const last = lastUuidOf(lines);
|
|
576
611
|
if (last && seen.has(last)) return null; // same segment reported twice
|
|
577
612
|
sum = summarizeTranscript(lines, { afterUuids: seen, pricing: loadPricing(ctx) });
|
|
@@ -815,7 +850,7 @@ module.exports = {
|
|
|
815
850
|
priceFor, usageBuckets, requestCost, parseJsonl, readTranscript, lastUuidOf,
|
|
816
851
|
tagFromTranscript, summarizeTranscript, appendEvent, eventLinesFrom, readEventLines, readEvents,
|
|
817
852
|
loadSyncOffset, saveSyncOffset, recordRejected, unsyncedInfo, loadSegments, saveSegments, segmentFile,
|
|
818
|
-
extractAgentId, buildDispatch, handlePostToolUse, handleSubagentStop, maybeSpawnSync, runHook, runSync,
|
|
853
|
+
extractAgentId, hasAssistantUsage, hasDispatch, buildDispatch, handlePostToolUse, handleSubagentStop, maybeSpawnSync, runHook, runSync,
|
|
819
854
|
};
|
|
820
855
|
|
|
821
856
|
if (require.main === module) {
|
package/lib/aggregate.cjs
CHANGED
|
@@ -59,6 +59,19 @@ function normalizeVerdict(ev) {
|
|
|
59
59
|
return { by, outcome: ev.outcome, cause, round: Number(ev.round) || null };
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function blank(v) {
|
|
63
|
+
return v === '' || v === null || v === undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* `complete` written by core 0.1.0 for Claude Code's internal subagents (no transcript, no Agent-tool
|
|
68
|
+
* dispatch): subagent_type "" + tokens null + model null + no dispatch for that agent_id. Ignored.
|
|
69
|
+
*/
|
|
70
|
+
function isInternalComplete(ev, dispatched) {
|
|
71
|
+
return ev.kind === 'complete' && blank(ev.subagent_type) && blank(ev.model)
|
|
72
|
+
&& (ev.tokens === null || ev.tokens === undefined) && !dispatched.has(ev.agent_id);
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
function steerIdOf(ev) {
|
|
63
76
|
return typeof ev.steer_id === 'string' && ev.steer_id ? ev.steer_id : `${ev.agent_id}|${ev.ts}`;
|
|
64
77
|
}
|
|
@@ -75,8 +88,10 @@ function buildAgents(events) {
|
|
|
75
88
|
if (!map.has(id)) map.set(id, { agent_id: id, dispatch: null, completes: [], verdicts: [], steers: [] });
|
|
76
89
|
return map.get(id);
|
|
77
90
|
};
|
|
91
|
+
const dispatched = new Set(events.filter((ev) => ev && ev.kind === 'dispatch').map((ev) => ev.agent_id));
|
|
78
92
|
events.forEach((ev, idx) => {
|
|
79
93
|
if (!ev || typeof ev !== 'object') return;
|
|
94
|
+
if (isInternalComplete(ev, dispatched)) return;
|
|
80
95
|
const e = Object.assign({ _idx: idx }, ev);
|
|
81
96
|
if (ev.kind === 'override') { overrides.push(e); return; }
|
|
82
97
|
if (ev.kind === 'verdict') {
|
|
@@ -332,6 +347,6 @@ function analyze(events, opts) {
|
|
|
332
347
|
}
|
|
333
348
|
|
|
334
349
|
module.exports = {
|
|
335
|
-
MIN_N, FAIL_OUTCOMES, HEAVY_STEERS, CANONICAL_CELL_FIELDS, normalizeVerdict, buildAgents, buildTasks,
|
|
350
|
+
MIN_N, FAIL_OUTCOMES, HEAVY_STEERS, CANONICAL_CELL_FIELDS, normalizeVerdict, isInternalComplete, buildAgents, buildTasks,
|
|
336
351
|
buildCells, buildSteers, canonicalCells, analyze, cellKey, mean, percentile, tsOf, steerIdOf,
|
|
337
352
|
};
|
package/lib/commands.cjs
CHANGED
|
@@ -120,6 +120,10 @@ async function cmdDoctor(opts) {
|
|
|
120
120
|
let errSize = 0;
|
|
121
121
|
try { errSize = fs.statSync(ctx.paths.errlog).size; } catch (_) { /* none */ }
|
|
122
122
|
add(errSize === 0, 'hook-errors', errSize ? `${ctx.paths.errlog} có ${errSize} byte lỗi` : 'không có lỗi', 'CẢNH BÁO');
|
|
123
|
+
const skipped = core.readJsonFile(ctx.paths.skippedInternal, null);
|
|
124
|
+
add(true, 'internal-subagents', skipped && skipped.count
|
|
125
|
+
? `bỏ qua ${skipped.count} SubagentStop của subagent nội bộ Claude Code (lần cuối ${skipped.last_ts})`
|
|
126
|
+
: 'chưa bỏ qua SubagentStop nội bộ nào');
|
|
123
127
|
if (ctx.config.sink_url && ctx.config.sink_token) {
|
|
124
128
|
try {
|
|
125
129
|
const u = new URL(ctx.config.sink_url);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kienntpixon/dieu-phoi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Hook + CLI ghi log subagent của Claude Code và chọn model/effort theo chi phí + thời gian tới lúc nghiệm thu.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "kiennt <kiennt@pixon.games>",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"hook.cjs",
|
|
16
16
|
"pricing.json",
|
|
17
17
|
"README.md",
|
|
18
|
-
"LICENSE"
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"CHANGELOG.md"
|
|
19
20
|
],
|
|
20
21
|
"engines": {
|
|
21
22
|
"node": ">=18"
|