bb-cc-lite 0.1.0 → 0.1.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 +45 -6
- package/assets/statusline-demo.gif +0 -0
- package/dist/baseline-builder.d.ts +13 -0
- package/dist/baseline-builder.js +666 -0
- package/dist/baseline-builder.js.map +1 -0
- package/dist/baseline.d.ts +113 -0
- package/dist/baseline.js +383 -0
- package/dist/baseline.js.map +1 -0
- package/dist/cli.js +41 -50
- package/dist/cli.js.map +1 -1
- package/dist/decision-presentation.d.ts +11 -0
- package/dist/decision-presentation.js +12 -0
- package/dist/decision-presentation.js.map +1 -0
- package/dist/doctor.d.ts +17 -0
- package/dist/doctor.js +161 -8
- package/dist/doctor.js.map +1 -1
- package/dist/event-store-persistence.d.ts +5 -0
- package/dist/event-store-persistence.js +172 -0
- package/dist/event-store-persistence.js.map +1 -0
- package/dist/event-store-queries.d.ts +9 -0
- package/dist/event-store-queries.js +51 -0
- package/dist/event-store-queries.js.map +1 -0
- package/dist/hook-payload.d.ts +3 -0
- package/dist/hook-payload.js +90 -0
- package/dist/hook-payload.js.map +1 -0
- package/dist/hook-summary.d.ts +12 -0
- package/dist/hook-summary.js +25 -0
- package/dist/hook-summary.js.map +1 -0
- package/dist/hooks.d.ts +2 -14
- package/dist/hooks.js +2 -126
- package/dist/hooks.js.map +1 -1
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +3 -0
- package/dist/paths.js.map +1 -1
- package/dist/renderer.d.ts +2 -2
- package/dist/renderer.js +15 -7
- package/dist/renderer.js.map +1 -1
- package/dist/session.d.ts +3 -0
- package/dist/session.js +9 -0
- package/dist/session.js.map +1 -0
- package/dist/settings.d.ts +2 -1
- package/dist/settings.js +23 -42
- package/dist/settings.js.map +1 -1
- package/dist/signals.d.ts +2 -1
- package/dist/signals.js +156 -6
- package/dist/signals.js.map +1 -1
- package/dist/statusline.d.ts +1 -0
- package/dist/statusline.js +34 -0
- package/dist/statusline.js.map +1 -0
- package/dist/store.d.ts +3 -10
- package/dist/store.js +3 -84
- package/dist/store.js.map +1 -1
- package/dist/tool-metadata.d.ts +8 -0
- package/dist/tool-metadata.js +52 -0
- package/dist/tool-metadata.js.map +1 -0
- package/dist/transcript-reader.d.ts +9 -0
- package/dist/transcript-reader.js +45 -0
- package/dist/transcript-reader.js.map +1 -0
- package/dist/transcript.d.ts +2 -3
- package/dist/transcript.js +54 -66
- package/dist/transcript.js.map +1 -1
- package/dist/types.d.ts +56 -0
- package/dist/why.d.ts +7 -0
- package/dist/why.js +32 -0
- package/dist/why.js.map +1 -0
- package/package.json +7 -7
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { eventStorePath } from "./paths.js";
|
|
4
|
+
export const STORE_LIMIT = 100;
|
|
5
|
+
export const HOOK_STORE_LIMIT = 500;
|
|
6
|
+
export async function readStore(storePath = eventStorePath()) {
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(await readFile(storePath, "utf8"));
|
|
9
|
+
return {
|
|
10
|
+
version: 1,
|
|
11
|
+
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : new Date(0).toISOString(),
|
|
12
|
+
decisions: Array.isArray(parsed.decisions)
|
|
13
|
+
? parsed.decisions.flatMap((decision) => sanitizeStoredDecision(decision) ?? []).slice(-STORE_LIMIT)
|
|
14
|
+
: [],
|
|
15
|
+
hookEvents: Array.isArray(parsed.hookEvents)
|
|
16
|
+
? parsed.hookEvents.flatMap((event) => sanitizeStoredHookEvent(event) ?? []).slice(-HOOK_STORE_LIMIT)
|
|
17
|
+
: []
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return { version: 1, updatedAt: new Date(0).toISOString(), decisions: [], hookEvents: [] };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function writeStore(store, storePath) {
|
|
25
|
+
await mkdir(dirname(storePath), { recursive: true, mode: 0o700 });
|
|
26
|
+
const tempPath = `${storePath}.${process.pid}.tmp`;
|
|
27
|
+
await writeFile(tempPath, `${JSON.stringify(store, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
28
|
+
await chmod(tempPath, 0o600);
|
|
29
|
+
await rename(tempPath, storePath);
|
|
30
|
+
}
|
|
31
|
+
function sanitizeStoredDecision(value) {
|
|
32
|
+
const record = asRecord(value);
|
|
33
|
+
if (!record || containsForbiddenRawDataKey(record)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const id = stringField(record.id);
|
|
37
|
+
const state = decisionState(record.state);
|
|
38
|
+
const action = stringField(record.action);
|
|
39
|
+
if (!id || !state || !action) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
id,
|
|
44
|
+
state,
|
|
45
|
+
reasonCode: stringField(record.reasonCode) || "unknown",
|
|
46
|
+
diagnosisCode: stringField(record.diagnosisCode),
|
|
47
|
+
diagnosis: stringField(record.diagnosis),
|
|
48
|
+
confidence: confidence(record.confidence),
|
|
49
|
+
baselineNote: stringField(record.baselineNote),
|
|
50
|
+
primaryEvidence: stringField(record.primaryEvidence) || "stored decision",
|
|
51
|
+
evidence: sanitizeEvidence(record.evidence),
|
|
52
|
+
impact: stringField(record.impact) || "",
|
|
53
|
+
action,
|
|
54
|
+
costUsd: numberField(record.costUsd),
|
|
55
|
+
costSource: costSource(record.costSource),
|
|
56
|
+
contextPercent: numberField(record.contextPercent),
|
|
57
|
+
rateLimitPercent: numberField(record.rateLimitPercent),
|
|
58
|
+
sessionKey: stringField(record.sessionKey),
|
|
59
|
+
createdAt: stringField(record.createdAt) || new Date(0).toISOString()
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function sanitizeStoredHookEvent(value) {
|
|
63
|
+
const record = asRecord(value);
|
|
64
|
+
if (!record || containsForbiddenRawDataKey(record)) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
const id = stringField(record.id);
|
|
68
|
+
const kind = hookKind(record.kind);
|
|
69
|
+
const timestamp = stringField(record.timestamp);
|
|
70
|
+
if (!id || !kind || !timestamp) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
id,
|
|
75
|
+
kind,
|
|
76
|
+
timestamp,
|
|
77
|
+
hookEventName: stringField(record.hookEventName) || "unknown",
|
|
78
|
+
sessionKey: stringField(record.sessionKey),
|
|
79
|
+
toolName: stringField(record.toolName),
|
|
80
|
+
purpose: stringField(record.purpose),
|
|
81
|
+
toolCount: numberField(record.toolCount)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function sanitizeEvidence(value) {
|
|
85
|
+
if (!Array.isArray(value)) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
return value.flatMap((item) => {
|
|
89
|
+
const record = asRecord(item);
|
|
90
|
+
const label = stringField(record?.label);
|
|
91
|
+
if (!record || containsForbiddenRawDataKey(record) || !label) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
const detail = stringField(record.detail);
|
|
95
|
+
return detail ? [{ label, detail }] : [{ label }];
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function asRecord(value) {
|
|
99
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
100
|
+
}
|
|
101
|
+
function stringField(value) {
|
|
102
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
103
|
+
}
|
|
104
|
+
function numberField(value) {
|
|
105
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
106
|
+
}
|
|
107
|
+
function decisionState(value) {
|
|
108
|
+
return value === "Healthy" || value === "Careful" || value === "Stop" ? value : undefined;
|
|
109
|
+
}
|
|
110
|
+
function confidence(value) {
|
|
111
|
+
return value === "low" || value === "medium" || value === "high" ? value : undefined;
|
|
112
|
+
}
|
|
113
|
+
function costSource(value) {
|
|
114
|
+
return value === "claude" || value === "estimated" ? value : undefined;
|
|
115
|
+
}
|
|
116
|
+
function hookKind(value) {
|
|
117
|
+
return value === "tool_success" ||
|
|
118
|
+
value === "tool_failure" ||
|
|
119
|
+
value === "tool_batch" ||
|
|
120
|
+
value === "compaction" ||
|
|
121
|
+
value === "stop" ||
|
|
122
|
+
value === "session_end"
|
|
123
|
+
? value
|
|
124
|
+
: undefined;
|
|
125
|
+
}
|
|
126
|
+
function containsForbiddenRawDataKey(value) {
|
|
127
|
+
if (Array.isArray(value)) {
|
|
128
|
+
return value.some((item) => containsForbiddenRawDataKey(item));
|
|
129
|
+
}
|
|
130
|
+
const record = asRecord(value);
|
|
131
|
+
if (!record) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
for (const [key, child] of Object.entries(record)) {
|
|
135
|
+
if (FORBIDDEN_RAW_DATA_KEYS_NORMALIZED.has(normalizeKey(key)) || containsForbiddenRawDataKey(child)) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const FORBIDDEN_RAW_DATA_KEYS_NORMALIZED = new Set([
|
|
142
|
+
"assistantText",
|
|
143
|
+
"command",
|
|
144
|
+
"commands",
|
|
145
|
+
"cwd",
|
|
146
|
+
"cwds",
|
|
147
|
+
"fileContent",
|
|
148
|
+
"fileContents",
|
|
149
|
+
"prompt",
|
|
150
|
+
"prompts",
|
|
151
|
+
"promptText",
|
|
152
|
+
"rawCommand",
|
|
153
|
+
"rawCommands",
|
|
154
|
+
"rawPrompt",
|
|
155
|
+
"rawPrompts",
|
|
156
|
+
"rawSessionId",
|
|
157
|
+
"rawSessionIds",
|
|
158
|
+
"rawToolOutput",
|
|
159
|
+
"rawToolOutputs",
|
|
160
|
+
"sessionId",
|
|
161
|
+
"sessionIds",
|
|
162
|
+
"toolOutput",
|
|
163
|
+
"toolOutputs",
|
|
164
|
+
"transcriptPath",
|
|
165
|
+
"transcriptPaths",
|
|
166
|
+
"workspacePath",
|
|
167
|
+
"workspacePaths"
|
|
168
|
+
].map(normalizeKey));
|
|
169
|
+
function normalizeKey(value) {
|
|
170
|
+
return value.replaceAll(/[_-]/gu, "").toLowerCase();
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=event-store-persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-store-persistence.js","sourceRoot":"","sources":["../src/event-store-persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAC/B,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAEpC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAS,GAAG,cAAc,EAAE;IAC1D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAA4B,CAAC;QACxF,OAAO;YACL,OAAO,EAAE,CAAC;YACV,SAAS,EAAE,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;YAC9F,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;gBACxC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,sBAAsB,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC;gBACpG,CAAC,CAAC,EAAE;YACN,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;gBAC1C,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAuB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC;gBACrG,CAAC,CAAC,EAAE;SACP,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAC7F,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAqB,EAAE,SAAiB;IACvE,MAAM,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,GAAG,SAAS,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IACnD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpG,MAAM,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC7B,MAAM,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAc;IAC5C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,IAAI,2BAA2B,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO;QACL,EAAE;QACF,KAAK;QACL,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,SAAS;QACvD,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,aAAa,CAAC;QAChD,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC;QACxC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC;QACzC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,YAAY,CAAC;QAC9C,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,iBAAiB;QACzE,QAAQ,EAAE,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC3C,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QACxC,MAAM;QACN,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC;QACpC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC;QACzC,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,cAAc,CAAC;QAClD,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC;QACtD,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC;QAC1C,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;KACtE,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAc;IAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,IAAI,2BAA2B,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO;QACL,EAAE;QACF,IAAI;QACJ,SAAS;QACT,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,SAAS;QAC7D,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC;QAC1C,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC;QACtC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC;QACpC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC;KACzC,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,IAAI,2BAA2B,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7D,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1C,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAiC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/H,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3E,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACjF,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5F,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvF,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACzE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,cAAc;QAC7B,KAAK,KAAK,cAAc;QACxB,KAAK,KAAK,YAAY;QACtB,KAAK,KAAK,YAAY;QACtB,KAAK,KAAK,MAAM;QAChB,KAAK,KAAK,aAAa;QACvB,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED,SAAS,2BAA2B,CAAC,KAAc;IACjD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,kCAAkC,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;YACpG,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,kCAAkC,GAAG,IAAI,GAAG,CAChD;IACE,eAAe;IACf,SAAS;IACT,UAAU;IACV,KAAK;IACL,MAAM;IACN,aAAa;IACb,cAAc;IACd,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,WAAW;IACX,YAAY;IACZ,cAAc;IACd,eAAe;IACf,eAAe;IACf,gBAAgB;IAChB,WAAW;IACX,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,gBAAgB;IAChB,iBAAiB;IACjB,eAAe;IACf,gBAAgB;CACjB,CAAC,GAAG,CAAC,YAAY,CAAC,CACpB,CAAC;AAEF,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { StoredDecision, ToolFailureSummary } from "./types.js";
|
|
2
|
+
export declare function latestDecision(sessionKey?: string, storePath?: string): Promise<StoredDecision | undefined>;
|
|
3
|
+
export declare function hookSummary(sessionKey: string | undefined, storePath?: string): Promise<{
|
|
4
|
+
failedToolResults: number;
|
|
5
|
+
toolCalls: number;
|
|
6
|
+
compactionEvents: number;
|
|
7
|
+
repeatedFailures: ToolFailureSummary[];
|
|
8
|
+
latestTimestamp?: string;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { readStore } from "./event-store-persistence.js";
|
|
2
|
+
export async function latestDecision(sessionKey, storePath) {
|
|
3
|
+
const store = await readStore(storePath);
|
|
4
|
+
const decisions = sessionKey ? store.decisions.filter((decision) => decision.sessionKey === sessionKey) : store.decisions;
|
|
5
|
+
return decisions.at(-1);
|
|
6
|
+
}
|
|
7
|
+
export async function hookSummary(sessionKey, storePath) {
|
|
8
|
+
const store = await readStore(storePath);
|
|
9
|
+
const events = store.hookEvents.filter((event) => !sessionKey || event.sessionKey === sessionKey);
|
|
10
|
+
const failures = new Map();
|
|
11
|
+
let failedToolResults = 0;
|
|
12
|
+
let toolCalls = 0;
|
|
13
|
+
let compactionEvents = 0;
|
|
14
|
+
let latestTimestamp;
|
|
15
|
+
for (const event of events) {
|
|
16
|
+
latestTimestamp = !latestTimestamp || event.timestamp > latestTimestamp ? event.timestamp : latestTimestamp;
|
|
17
|
+
if (event.kind === "tool_failure") {
|
|
18
|
+
failedToolResults += 1;
|
|
19
|
+
toolCalls += 1;
|
|
20
|
+
const toolName = event.toolName || "tool";
|
|
21
|
+
const key = failureKey(toolName, event.purpose);
|
|
22
|
+
const existing = failures.get(key);
|
|
23
|
+
failures.set(key, {
|
|
24
|
+
toolName,
|
|
25
|
+
purpose: event.purpose,
|
|
26
|
+
count: (existing?.count || 0) + 1
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
else if (event.kind === "tool_success") {
|
|
30
|
+
toolCalls += 1;
|
|
31
|
+
failures.delete(failureKey(event.toolName || "tool", event.purpose));
|
|
32
|
+
}
|
|
33
|
+
else if (event.kind === "tool_batch") {
|
|
34
|
+
toolCalls += event.toolCount || 0;
|
|
35
|
+
}
|
|
36
|
+
else if (event.kind === "compaction") {
|
|
37
|
+
compactionEvents += 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
failedToolResults,
|
|
42
|
+
toolCalls,
|
|
43
|
+
compactionEvents,
|
|
44
|
+
repeatedFailures: [...failures.values()].filter((failure) => failure.count >= 2),
|
|
45
|
+
latestTimestamp
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function failureKey(toolName, purpose) {
|
|
49
|
+
return `${toolName}:${purpose || ""}`;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=event-store-queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-store-queries.js","sourceRoot":"","sources":["../src/event-store-queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAAmB,EAAE,SAAkB;IAC1E,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;IAC1H,OAAO,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,UAA8B,EAC9B,SAAkB;IAQlB,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;IAClG,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA8B,CAAC;IACvD,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,eAAmC,CAAC;IAExC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,eAAe,GAAG,CAAC,eAAe,IAAI,KAAK,CAAC,SAAS,GAAG,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC;QAC5G,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,iBAAiB,IAAI,CAAC,CAAC;YACvB,SAAS,IAAI,CAAC,CAAC;YACf,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,MAAM,CAAC;YAC1C,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACnC,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE;gBAChB,QAAQ;gBACR,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,KAAK,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC;aAClC,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACzC,SAAS,IAAI,CAAC,CAAC;YACf,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,IAAI,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QACvE,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACvC,SAAS,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC;QACpC,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACvC,gBAAgB,IAAI,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,OAAO;QACL,iBAAiB;QACjB,SAAS;QACT,gBAAgB;QAChB,gBAAgB,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;QAChF,eAAe;KAChB,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB,EAAE,OAAgB;IACpD,OAAO,GAAG,QAAQ,IAAI,OAAO,IAAI,EAAE,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DerivedHookEvent } from "./types.js";
|
|
2
|
+
export declare const SAFE_HOOK_EVENTS: readonly ["PostToolUse", "PostToolUseFailure", "PostToolBatch", "PreCompact", "PostCompact", "Stop", "SessionEnd"];
|
|
3
|
+
export declare function parseHookPayload(raw: string, fallbackEventName?: string): DerivedHookEvent | undefined;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { hashValue } from "./paths.js";
|
|
2
|
+
import { asRecord, numberField, stringField } from "./status-input.js";
|
|
3
|
+
import { classifyToolPurpose, safeToolName } from "./tool-metadata.js";
|
|
4
|
+
export const SAFE_HOOK_EVENTS = [
|
|
5
|
+
"PostToolUse",
|
|
6
|
+
"PostToolUseFailure",
|
|
7
|
+
"PostToolBatch",
|
|
8
|
+
"PreCompact",
|
|
9
|
+
"PostCompact",
|
|
10
|
+
"Stop",
|
|
11
|
+
"SessionEnd"
|
|
12
|
+
];
|
|
13
|
+
export function parseHookPayload(raw, fallbackEventName) {
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = JSON.parse(raw.trim() || "{}");
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const root = asRecord(parsed);
|
|
22
|
+
if (!root) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const hookEventName = stringField(root.hook_event_name) || stringField(root.event) || fallbackEventName || "unknown";
|
|
26
|
+
const sessionId = stringField(root.session_id) || stringField(root.sessionId);
|
|
27
|
+
const base = {
|
|
28
|
+
timestamp: stringField(root.timestamp) || new Date().toISOString(),
|
|
29
|
+
hookEventName,
|
|
30
|
+
sessionKey: hashValue(sessionId)
|
|
31
|
+
};
|
|
32
|
+
if (hookEventName === "PostToolUseFailure") {
|
|
33
|
+
const toolName = safeToolName(stringField(root.tool_name) || stringField(root.toolName));
|
|
34
|
+
return {
|
|
35
|
+
...base,
|
|
36
|
+
kind: "tool_failure",
|
|
37
|
+
toolName,
|
|
38
|
+
purpose: classifyToolPurpose(toolName, root.tool_input ?? root.toolInput)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (hookEventName === "PostToolUse") {
|
|
42
|
+
const toolName = safeToolName(stringField(root.tool_name) || stringField(root.toolName));
|
|
43
|
+
return {
|
|
44
|
+
...base,
|
|
45
|
+
kind: "tool_success",
|
|
46
|
+
toolName,
|
|
47
|
+
purpose: classifyToolPurpose(toolName, root.tool_input ?? root.toolInput)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (hookEventName === "PostToolBatch") {
|
|
51
|
+
return {
|
|
52
|
+
...base,
|
|
53
|
+
kind: "tool_batch",
|
|
54
|
+
toolCount: countBatchTools(root)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (hookEventName === "PreCompact" || hookEventName === "PostCompact") {
|
|
58
|
+
return {
|
|
59
|
+
...base,
|
|
60
|
+
kind: "compaction"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (hookEventName === "Stop" || hookEventName === "StopFailure") {
|
|
64
|
+
return {
|
|
65
|
+
...base,
|
|
66
|
+
kind: "stop"
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (hookEventName === "SessionEnd") {
|
|
70
|
+
return {
|
|
71
|
+
...base,
|
|
72
|
+
kind: "session_end"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
function countBatchTools(root) {
|
|
78
|
+
const values = [root.tools, root.tool_uses, root.toolUses, root.results, root.tool_results, root.toolResults];
|
|
79
|
+
for (const value of values) {
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
return value.length;
|
|
82
|
+
}
|
|
83
|
+
const count = numberField(value);
|
|
84
|
+
if (count !== undefined) {
|
|
85
|
+
return Math.max(0, Math.floor(count));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=hook-payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-payload.js","sourceRoot":"","sources":["../src/hook-payload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TranscriptSummary } from "./types.js";
|
|
2
|
+
export declare function mergeHookSummary(transcript: TranscriptSummary, hookData: {
|
|
3
|
+
failedToolResults: number;
|
|
4
|
+
toolCalls: number;
|
|
5
|
+
compactionEvents: number;
|
|
6
|
+
repeatedFailures: Array<{
|
|
7
|
+
toolName: string;
|
|
8
|
+
count: number;
|
|
9
|
+
purpose?: string;
|
|
10
|
+
}>;
|
|
11
|
+
latestTimestamp?: string;
|
|
12
|
+
}): TranscriptSummary;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function mergeHookSummary(transcript, hookData) {
|
|
2
|
+
const repeatedFailures = new Map();
|
|
3
|
+
for (const failure of [...transcript.repeatedFailures, ...hookData.repeatedFailures]) {
|
|
4
|
+
const key = `${failure.toolName}:${failure.purpose || ""}`;
|
|
5
|
+
const existing = repeatedFailures.get(key);
|
|
6
|
+
repeatedFailures.set(key, {
|
|
7
|
+
toolName: failure.toolName,
|
|
8
|
+
purpose: failure.purpose,
|
|
9
|
+
count: Math.max(existing?.count || 0, failure.count)
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
...transcript,
|
|
14
|
+
toolCalls: Math.max(transcript.toolCalls, hookData.toolCalls),
|
|
15
|
+
failedToolResults: Math.max(transcript.failedToolResults, hookData.failedToolResults),
|
|
16
|
+
repeatedFailures: [...repeatedFailures.values()].filter((failure) => failure.count >= 2),
|
|
17
|
+
compactionEvents: Math.max(transcript.compactionEvents, hookData.compactionEvents),
|
|
18
|
+
latestTimestamp: transcript.latestTimestamp && hookData.latestTimestamp
|
|
19
|
+
? transcript.latestTimestamp > hookData.latestTimestamp
|
|
20
|
+
? transcript.latestTimestamp
|
|
21
|
+
: hookData.latestTimestamp
|
|
22
|
+
: transcript.latestTimestamp || hookData.latestTimestamp
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=hook-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-summary.js","sourceRoot":"","sources":["../src/hook-summary.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gBAAgB,CAC9B,UAA6B,EAC7B,QAMC;IAED,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAiE,CAAC;IAClG,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,UAAU,CAAC,gBAAgB,EAAE,GAAG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACrF,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAC3D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE;YACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC;SACrD,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,GAAG,UAAU;QACb,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC;QAC7D,iBAAiB,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB,CAAC;QACrF,gBAAgB,EAAE,CAAC,GAAG,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;QACxF,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC;QAClF,eAAe,EACb,UAAU,CAAC,eAAe,IAAI,QAAQ,CAAC,eAAe;YACpD,CAAC,CAAC,UAAU,CAAC,eAAe,GAAG,QAAQ,CAAC,eAAe;gBACrD,CAAC,CAAC,UAAU,CAAC,eAAe;gBAC5B,CAAC,CAAC,QAAQ,CAAC,eAAe;YAC5B,CAAC,CAAC,UAAU,CAAC,eAAe,IAAI,QAAQ,CAAC,eAAe;KAC7D,CAAC;AACJ,CAAC"}
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,14 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export declare function parseHookPayload(raw: string, fallbackEventName?: string): DerivedHookEvent | undefined;
|
|
4
|
-
export declare function mergeHookSummary(transcript: TranscriptSummary, hookData: {
|
|
5
|
-
failedToolResults: number;
|
|
6
|
-
toolCalls: number;
|
|
7
|
-
compactionEvents: number;
|
|
8
|
-
repeatedFailures: Array<{
|
|
9
|
-
toolName: string;
|
|
10
|
-
count: number;
|
|
11
|
-
purpose?: string;
|
|
12
|
-
}>;
|
|
13
|
-
latestTimestamp?: string;
|
|
14
|
-
}): TranscriptSummary;
|
|
1
|
+
export { SAFE_HOOK_EVENTS, parseHookPayload } from "./hook-payload.js";
|
|
2
|
+
export { mergeHookSummary } from "./hook-summary.js";
|
package/dist/hooks.js
CHANGED
|
@@ -1,127 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const TEST_COMMAND_RE = /\b(npm|pnpm|yarn|bun)\s+(run\s+)?(test|vitest|jest)|\b(vitest|jest|mocha|pytest|cargo\s+test|go\s+test|rspec|playwright\s+test)\b/i;
|
|
4
|
-
export const SAFE_HOOK_EVENTS = [
|
|
5
|
-
"PostToolUse",
|
|
6
|
-
"PostToolUseFailure",
|
|
7
|
-
"PostToolBatch",
|
|
8
|
-
"PreCompact",
|
|
9
|
-
"PostCompact",
|
|
10
|
-
"Stop",
|
|
11
|
-
"SessionEnd"
|
|
12
|
-
];
|
|
13
|
-
export function parseHookPayload(raw, fallbackEventName) {
|
|
14
|
-
let parsed;
|
|
15
|
-
try {
|
|
16
|
-
parsed = JSON.parse(raw.trim() || "{}");
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
const root = asRecord(parsed);
|
|
22
|
-
if (!root) {
|
|
23
|
-
return undefined;
|
|
24
|
-
}
|
|
25
|
-
const hookEventName = stringField(root.hook_event_name) || stringField(root.event) || fallbackEventName || "unknown";
|
|
26
|
-
const sessionId = stringField(root.session_id) || stringField(root.sessionId);
|
|
27
|
-
const base = {
|
|
28
|
-
timestamp: stringField(root.timestamp) || new Date().toISOString(),
|
|
29
|
-
hookEventName,
|
|
30
|
-
sessionKey: hashValue(sessionId)
|
|
31
|
-
};
|
|
32
|
-
if (hookEventName === "PostToolUseFailure") {
|
|
33
|
-
const toolName = safeToolName(stringField(root.tool_name) || stringField(root.toolName));
|
|
34
|
-
return {
|
|
35
|
-
...base,
|
|
36
|
-
kind: "tool_failure",
|
|
37
|
-
toolName,
|
|
38
|
-
purpose: classifyToolPurpose(toolName, root.tool_input ?? root.toolInput)
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
if (hookEventName === "PostToolUse") {
|
|
42
|
-
const toolName = safeToolName(stringField(root.tool_name) || stringField(root.toolName));
|
|
43
|
-
return {
|
|
44
|
-
...base,
|
|
45
|
-
kind: "tool_success",
|
|
46
|
-
toolName,
|
|
47
|
-
purpose: classifyToolPurpose(toolName, root.tool_input ?? root.toolInput)
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
if (hookEventName === "PostToolBatch") {
|
|
51
|
-
return {
|
|
52
|
-
...base,
|
|
53
|
-
kind: "tool_batch",
|
|
54
|
-
toolCount: countBatchTools(root)
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
if (hookEventName === "PreCompact" || hookEventName === "PostCompact") {
|
|
58
|
-
return {
|
|
59
|
-
...base,
|
|
60
|
-
kind: "compaction"
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
if (hookEventName === "Stop" || hookEventName === "StopFailure") {
|
|
64
|
-
return {
|
|
65
|
-
...base,
|
|
66
|
-
kind: "stop"
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
if (hookEventName === "SessionEnd") {
|
|
70
|
-
return {
|
|
71
|
-
...base,
|
|
72
|
-
kind: "session_end"
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
return undefined;
|
|
76
|
-
}
|
|
77
|
-
export function mergeHookSummary(transcript, hookData) {
|
|
78
|
-
const repeatedFailures = new Map();
|
|
79
|
-
for (const failure of [...transcript.repeatedFailures, ...hookData.repeatedFailures]) {
|
|
80
|
-
const key = `${failure.toolName}:${failure.purpose || ""}`;
|
|
81
|
-
const existing = repeatedFailures.get(key);
|
|
82
|
-
repeatedFailures.set(key, {
|
|
83
|
-
toolName: failure.toolName,
|
|
84
|
-
purpose: failure.purpose,
|
|
85
|
-
count: Math.max(existing?.count || 0, failure.count)
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
...transcript,
|
|
90
|
-
toolCalls: Math.max(transcript.toolCalls, hookData.toolCalls),
|
|
91
|
-
failedToolResults: Math.max(transcript.failedToolResults, hookData.failedToolResults),
|
|
92
|
-
repeatedFailures: [...repeatedFailures.values()].filter((failure) => failure.count >= 2),
|
|
93
|
-
compactionEvents: Math.max(transcript.compactionEvents, hookData.compactionEvents),
|
|
94
|
-
latestTimestamp: transcript.latestTimestamp && hookData.latestTimestamp
|
|
95
|
-
? transcript.latestTimestamp > hookData.latestTimestamp
|
|
96
|
-
? transcript.latestTimestamp
|
|
97
|
-
: hookData.latestTimestamp
|
|
98
|
-
: transcript.latestTimestamp || hookData.latestTimestamp
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
function classifyToolPurpose(toolName, input) {
|
|
102
|
-
if (toolName !== "Bash") {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
const command = stringField(asRecord(input)?.command);
|
|
106
|
-
return command && TEST_COMMAND_RE.test(command) ? "tests" : undefined;
|
|
107
|
-
}
|
|
108
|
-
function countBatchTools(root) {
|
|
109
|
-
const values = [root.tools, root.tool_uses, root.toolUses, root.results, root.tool_results, root.toolResults];
|
|
110
|
-
for (const value of values) {
|
|
111
|
-
if (Array.isArray(value)) {
|
|
112
|
-
return value.length;
|
|
113
|
-
}
|
|
114
|
-
const count = numberField(value);
|
|
115
|
-
if (count !== undefined) {
|
|
116
|
-
return Math.max(0, Math.floor(count));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return 0;
|
|
120
|
-
}
|
|
121
|
-
function safeToolName(toolName) {
|
|
122
|
-
if (!toolName) {
|
|
123
|
-
return "tool";
|
|
124
|
-
}
|
|
125
|
-
return /^[A-Za-z][A-Za-z0-9_-]{0,32}$/u.test(toolName) ? toolName : "tool";
|
|
126
|
-
}
|
|
1
|
+
export { SAFE_HOOK_EVENTS, parseHookPayload } from "./hook-payload.js";
|
|
2
|
+
export { mergeHookSummary } from "./hook-summary.js";
|
|
127
3
|
//# sourceMappingURL=hooks.js.map
|
package/dist/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/paths.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare const APP_DIR_NAME = "bb-cc-lite";
|
|
2
2
|
export declare function appHome(homeDir?: string): string;
|
|
3
3
|
export declare function eventStorePath(): string;
|
|
4
|
+
export declare function baselinePath(homeDir?: string): string;
|
|
4
5
|
export declare function backupDir(homeDir?: string): string;
|
|
5
6
|
export declare function pricingCachePath(): string;
|
|
6
7
|
export declare function cliPath(): string;
|
package/dist/paths.js
CHANGED
|
@@ -9,6 +9,9 @@ export function appHome(homeDir = homedir()) {
|
|
|
9
9
|
export function eventStorePath() {
|
|
10
10
|
return process.env.BB_CC_LITE_STORE || join(appHome(), "events.json");
|
|
11
11
|
}
|
|
12
|
+
export function baselinePath(homeDir) {
|
|
13
|
+
return join(appHome(homeDir), "baseline.json");
|
|
14
|
+
}
|
|
12
15
|
export function backupDir(homeDir) {
|
|
13
16
|
return join(appHome(homeDir), "backups");
|
|
14
17
|
}
|
package/dist/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAC;AAEzC,MAAM,UAAU,OAAO,CAAC,OAAO,GAAG,OAAO,EAAE;IACzC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAgB;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,sBAAsB,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QACzD,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC5D,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAyB;IACjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC;AAC/C,CAAC"}
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAC;AAEzC,MAAM,UAAU,OAAO,CAAC,OAAO,GAAG,OAAO,EAAE;IACzC,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAgB;IACxC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,sBAAsB,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,OAAO,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QACzD,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC5D,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAyB;IACjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC;AAC/C,CAAC"}
|
package/dist/renderer.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function renderStatusLine(decision:
|
|
1
|
+
import type { DecisionPresentation } from "./decision-presentation.js";
|
|
2
|
+
export declare function renderStatusLine(decision: DecisionPresentation, width?: number): string;
|
package/dist/renderer.js
CHANGED
|
@@ -17,18 +17,26 @@ export function renderStatusLine(decision, width) {
|
|
|
17
17
|
}
|
|
18
18
|
function defaultCandidates(decision) {
|
|
19
19
|
const evidence = decision.evidence.map((item) => item.label);
|
|
20
|
+
const headline = decision.diagnosis || decision.primaryEvidence;
|
|
21
|
+
const badge = decision.baselineNote || "";
|
|
22
|
+
if (decision.diagnosis) {
|
|
23
|
+
return [
|
|
24
|
+
[`bb: ${decision.state}`, headline, badge, decision.action],
|
|
25
|
+
[`bb: ${decision.state}`, headline, decision.action],
|
|
26
|
+
[`bb: ${decision.state}`, headline]
|
|
27
|
+
];
|
|
28
|
+
}
|
|
20
29
|
return [
|
|
21
|
-
[`bb: ${decision.state}`, ...evidence, decision.action],
|
|
22
|
-
[`bb: ${decision.state}`,
|
|
23
|
-
[`bb: ${decision.state}`,
|
|
30
|
+
[`bb: ${decision.state}`, headline, badge, ...evidence.filter((item) => item !== headline), decision.action],
|
|
31
|
+
[`bb: ${decision.state}`, headline, decision.action],
|
|
32
|
+
[`bb: ${decision.state}`, headline]
|
|
24
33
|
];
|
|
25
34
|
}
|
|
26
35
|
function stopCandidates(decision) {
|
|
27
36
|
const costEvidence = decision.evidence.filter((item) => item.detail).map((item) => item.label);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const shortWhy = `why: ${decision.primaryEvidence}`;
|
|
37
|
+
const headline = decision.diagnosis || decision.primaryEvidence;
|
|
38
|
+
const fullWhy = decision.impact && decision.impact !== headline ? `why: ${headline}; ${decision.impact}` : `why: ${headline}`;
|
|
39
|
+
const shortWhy = `why: ${headline}`;
|
|
32
40
|
const action = `do: ${decision.action}`;
|
|
33
41
|
return [
|
|
34
42
|
[`bb: ${decision.state}`, fullWhy, ...costEvidence, action],
|