bb-cc-lite 0.1.6 → 0.1.8

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.
Files changed (51) hide show
  1. package/README.md +38 -29
  2. package/assets/statusline-demo.gif +0 -0
  3. package/dist/baseline-builder.js +64 -64
  4. package/dist/baseline-builder.js.map +1 -1
  5. package/dist/baseline-refresh.d.ts +66 -0
  6. package/dist/baseline-refresh.js +214 -0
  7. package/dist/baseline-refresh.js.map +1 -0
  8. package/dist/baseline.d.ts +11 -1
  9. package/dist/baseline.js +128 -4
  10. package/dist/baseline.js.map +1 -1
  11. package/dist/cli.js +33 -2
  12. package/dist/cli.js.map +1 -1
  13. package/dist/doctor.d.ts +1 -0
  14. package/dist/doctor.js +29 -0
  15. package/dist/doctor.js.map +1 -1
  16. package/dist/event-store-persistence.js +21 -3
  17. package/dist/event-store-persistence.js.map +1 -1
  18. package/dist/event-store-queries.d.ts +4 -1
  19. package/dist/event-store-queries.js +38 -11
  20. package/dist/event-store-queries.js.map +1 -1
  21. package/dist/failure-episodes.d.ts +21 -0
  22. package/dist/failure-episodes.js +266 -0
  23. package/dist/failure-episodes.js.map +1 -0
  24. package/dist/historical-replay.d.ts +21 -0
  25. package/dist/historical-replay.js +263 -0
  26. package/dist/historical-replay.js.map +1 -0
  27. package/dist/hook-payload.js +11 -7
  28. package/dist/hook-payload.js.map +1 -1
  29. package/dist/hook-summary.d.ts +4 -5
  30. package/dist/hook-summary.js +37 -11
  31. package/dist/hook-summary.js.map +1 -1
  32. package/dist/recovery-stats.d.ts +57 -0
  33. package/dist/recovery-stats.js +234 -0
  34. package/dist/recovery-stats.js.map +1 -0
  35. package/dist/settings.d.ts +1 -1
  36. package/dist/settings.js +7 -0
  37. package/dist/settings.js.map +1 -1
  38. package/dist/signals.js +173 -68
  39. package/dist/signals.js.map +1 -1
  40. package/dist/statusline.d.ts +5 -1
  41. package/dist/statusline.js +8 -3
  42. package/dist/statusline.js.map +1 -1
  43. package/dist/tool-metadata.d.ts +10 -0
  44. package/dist/tool-metadata.js +50 -2
  45. package/dist/tool-metadata.js.map +1 -1
  46. package/dist/transcript.js +106 -25
  47. package/dist/transcript.js.map +1 -1
  48. package/dist/types.d.ts +53 -0
  49. package/dist/why.js +1 -4
  50. package/dist/why.js.map +1 -1
  51. package/package.json +1 -1
@@ -0,0 +1,21 @@
1
+ import { type FailureRecoveryCategory } from "./recovery-stats.js";
2
+ export interface HistoricalReplayOptions {
3
+ homeDir?: string;
4
+ claudeProjectsDir?: string;
5
+ maxFiles?: number;
6
+ maxBytesPerTranscript?: number;
7
+ holdoutRatio?: number;
8
+ }
9
+ export interface HistoricalReplayMetrics {
10
+ holdoutSessions: number;
11
+ evaluatedFailureEpisodes: number;
12
+ stopPrecisionOnUnrecoveredEpisodes: number | undefined;
13
+ falseStopCountOnRecoveredEpisodes: number;
14
+ missedUnrecoveredLoopCount: number;
15
+ blindRetryPrecision: number | undefined;
16
+ averageAttemptsBeforeWarning: number;
17
+ lowSampleSuppressions: number;
18
+ categoryCoverage: Partial<Record<FailureRecoveryCategory, number>>;
19
+ }
20
+ export declare function evaluateHistoricalReplay(options?: HistoricalReplayOptions): Promise<HistoricalReplayMetrics>;
21
+ export declare function formatHistoricalReplayMetrics(metrics: HistoricalReplayMetrics): string;
@@ -0,0 +1,263 @@
1
+ import { readdir, stat } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { extractFailureEpisodesFromTranscriptLines, extractSafeToolResultEventsFromTranscriptLines } from "./failure-episodes.js";
5
+ import { addFailureEpisodeToRecoveryCounters, blindRetryAggregatesFromCounters, emptyRecoveryBuildCounters, FAILURE_RECOVERY_CATEGORIES, recoveryAggregatesFromCounters, recoveryInsight } from "./recovery-stats.js";
6
+ import { readTranscriptTail } from "./transcript-reader.js";
7
+ const DEFAULT_MAX_FILES = 1500;
8
+ const DEFAULT_MAX_BYTES_PER_TRANSCRIPT = 1024 * 1024;
9
+ const DEFAULT_HOLDOUT_RATIO = 0.2;
10
+ export async function evaluateHistoricalReplay(options = {}) {
11
+ const homeDir = options.homeDir ?? homedir();
12
+ const claudeProjectsDir = options.claudeProjectsDir ?? join(homeDir, ".claude", "projects");
13
+ const maxFiles = options.maxFiles ?? DEFAULT_MAX_FILES;
14
+ const maxBytesPerTranscript = options.maxBytesPerTranscript ?? DEFAULT_MAX_BYTES_PER_TRANSCRIPT;
15
+ const files = await listTranscriptFiles(claudeProjectsDir, maxFiles);
16
+ const holdoutCount = holdoutSessionCount(files.length, options.holdoutRatio ?? DEFAULT_HOLDOUT_RATIO);
17
+ const holdoutFiles = files.slice(0, holdoutCount);
18
+ const trainingFiles = files.slice(holdoutCount);
19
+ const trainingEpisodes = await readEpisodes(trainingFiles, maxBytesPerTranscript);
20
+ const holdoutEventsBySession = await readEventsBySession(holdoutFiles, maxBytesPerTranscript);
21
+ const baseline = baselineFromEpisodes(trainingEpisodes);
22
+ return evaluateHoldout(holdoutEventsBySession, baseline);
23
+ }
24
+ export function formatHistoricalReplayMetrics(metrics) {
25
+ return [
26
+ `holdout sessions ${metrics.holdoutSessions}`,
27
+ `evaluated failure episodes ${metrics.evaluatedFailureEpisodes}`,
28
+ `Stop precision on unrecovered episodes ${formatMetricRate(metrics.stopPrecisionOnUnrecoveredEpisodes)}`,
29
+ `false Stop count on recovered episodes ${metrics.falseStopCountOnRecoveredEpisodes}`,
30
+ `missed unrecovered loop count ${metrics.missedUnrecoveredLoopCount}`,
31
+ `blind retry precision ${formatMetricRate(metrics.blindRetryPrecision)}`,
32
+ `average attempts before warning ${metrics.averageAttemptsBeforeWarning.toFixed(2)}`,
33
+ `low-sample suppressions ${metrics.lowSampleSuppressions}`,
34
+ `category coverage ${formatCategoryCoverage(metrics.categoryCoverage)}`
35
+ ].join("; ");
36
+ }
37
+ function evaluateHoldout(holdoutEventsBySession, baseline) {
38
+ let evaluatedFailureEpisodes = 0;
39
+ let stopTruePositive = 0;
40
+ let stopTotal = 0;
41
+ let falseStopCountOnRecoveredEpisodes = 0;
42
+ let missedUnrecoveredLoopCount = 0;
43
+ let blindRetryTruePositive = 0;
44
+ let blindRetryTotal = 0;
45
+ let lowSampleSuppressions = 0;
46
+ const attemptsBeforeWarning = [];
47
+ const categoryCoverage = {};
48
+ for (const events of holdoutEventsBySession) {
49
+ const replay = replaySession(events, baseline);
50
+ lowSampleSuppressions += replay.lowSampleSuppressions;
51
+ for (const outcome of replay.outcomes) {
52
+ evaluatedFailureEpisodes += 1;
53
+ categoryCoverage[outcome.category] = (categoryCoverage[outcome.category] || 0) + 1;
54
+ if (outcome.warningAttempt !== undefined) {
55
+ attemptsBeforeWarning.push(outcome.warningAttempt);
56
+ }
57
+ if (outcome.stopIssued) {
58
+ stopTotal += 1;
59
+ if (outcome.recovered) {
60
+ falseStopCountOnRecoveredEpisodes += 1;
61
+ }
62
+ else {
63
+ stopTruePositive += 1;
64
+ }
65
+ }
66
+ const unrecoveredLoop = !outcome.recovered && outcome.attemptCount >= 3;
67
+ if (unrecoveredLoop && !outcome.stopIssued) {
68
+ missedUnrecoveredLoopCount += 1;
69
+ }
70
+ if (outcome.blindRetryWarningIssued) {
71
+ blindRetryTotal += 1;
72
+ if (!outcome.recovered) {
73
+ blindRetryTruePositive += 1;
74
+ }
75
+ }
76
+ }
77
+ }
78
+ return {
79
+ holdoutSessions: holdoutEventsBySession.length,
80
+ evaluatedFailureEpisodes,
81
+ stopPrecisionOnUnrecoveredEpisodes: stopTotal > 0 ? roundRate(stopTruePositive / stopTotal) : undefined,
82
+ falseStopCountOnRecoveredEpisodes,
83
+ missedUnrecoveredLoopCount,
84
+ blindRetryPrecision: blindRetryTotal > 0 ? roundRate(blindRetryTruePositive / blindRetryTotal) : undefined,
85
+ averageAttemptsBeforeWarning: attemptsBeforeWarning.length > 0
86
+ ? Number((attemptsBeforeWarning.reduce((total, value) => total + value, 0) / attemptsBeforeWarning.length).toFixed(2))
87
+ : 0,
88
+ lowSampleSuppressions,
89
+ categoryCoverage
90
+ };
91
+ }
92
+ function replaySession(events, baseline) {
93
+ const active = new Map();
94
+ const outcomes = [];
95
+ let lowSampleSuppressions = 0;
96
+ for (const event of events) {
97
+ if (event.outcome === "success") {
98
+ const sameIdentityEpisode = active.get(event.identity);
99
+ if (sameIdentityEpisode) {
100
+ outcomes.push(toReplayOutcome(sameIdentityEpisode, true));
101
+ active.delete(event.identity);
102
+ }
103
+ if (event.isEdit || event.isValidation) {
104
+ for (const episode of active.values()) {
105
+ episode.meaningfulInterventionSinceFailure = true;
106
+ }
107
+ }
108
+ continue;
109
+ }
110
+ const episode = active.get(event.identity) || {
111
+ identity: event.identity,
112
+ category: event.category,
113
+ attemptCount: 0,
114
+ blindRunCount: 0,
115
+ maxBlindRunCount: 0,
116
+ meaningfulInterventionSinceFailure: false,
117
+ stopIssued: false,
118
+ blindRetryWarningIssued: false
119
+ };
120
+ if (episode.attemptCount === 0 || episode.meaningfulInterventionSinceFailure) {
121
+ episode.blindRunCount = 1;
122
+ }
123
+ else {
124
+ episode.blindRunCount += 1;
125
+ }
126
+ episode.attemptCount += 1;
127
+ episode.maxBlindRunCount = Math.max(episode.maxBlindRunCount, episode.blindRunCount);
128
+ episode.meaningfulInterventionSinceFailure = false;
129
+ const currentState = replayDecisionState(episode);
130
+ if (currentState !== "Healthy" && episode.warningAttempt === undefined) {
131
+ episode.warningAttempt = episode.attemptCount;
132
+ }
133
+ if (currentState !== "Healthy" && !recoveryInsight(baseline, episode.category, episode.attemptCount)) {
134
+ lowSampleSuppressions += 1;
135
+ }
136
+ if (episode.maxBlindRunCount >= 2) {
137
+ episode.blindRetryWarningIssued = true;
138
+ }
139
+ if (currentState === "Stop") {
140
+ episode.stopIssued = true;
141
+ }
142
+ active.set(event.identity, episode);
143
+ }
144
+ for (const episode of active.values()) {
145
+ outcomes.push(toReplayOutcome(episode, false));
146
+ }
147
+ return { outcomes, lowSampleSuppressions };
148
+ }
149
+ function replayDecisionState(episode) {
150
+ if (episode.maxBlindRunCount >= 3 || episode.attemptCount >= 3) {
151
+ return "Stop";
152
+ }
153
+ if (episode.maxBlindRunCount >= 2 || episode.attemptCount >= 2) {
154
+ return "Careful";
155
+ }
156
+ return "Healthy";
157
+ }
158
+ function toReplayOutcome(episode, recovered) {
159
+ return {
160
+ category: episode.category,
161
+ attemptCount: episode.attemptCount,
162
+ recovered,
163
+ warningAttempt: episode.warningAttempt,
164
+ stopIssued: episode.stopIssued,
165
+ blindRetryWarningIssued: episode.blindRetryWarningIssued
166
+ };
167
+ }
168
+ function baselineFromEpisodes(episodes) {
169
+ const counters = emptyRecoveryBuildCounters();
170
+ for (const episode of episodes) {
171
+ addFailureEpisodeToRecoveryCounters(counters, episode);
172
+ }
173
+ return {
174
+ failureRecovery: recoveryAggregatesFromCounters(counters),
175
+ blindRetry: blindRetryAggregatesFromCounters(counters)
176
+ };
177
+ }
178
+ async function readEpisodes(files, maxBytesPerTranscript) {
179
+ const sessions = await readEpisodesBySession(files, maxBytesPerTranscript);
180
+ return sessions.flat();
181
+ }
182
+ async function readEpisodesBySession(files, maxBytesPerTranscript) {
183
+ const sessions = [];
184
+ for (const file of files) {
185
+ const tail = await readTranscriptTail(file, { maxBytes: maxBytesPerTranscript });
186
+ if (tail.pathReadable) {
187
+ sessions.push(extractFailureEpisodesFromTranscriptLines(tail.lines));
188
+ }
189
+ }
190
+ return sessions;
191
+ }
192
+ async function readEventsBySession(files, maxBytesPerTranscript) {
193
+ const sessions = [];
194
+ for (const file of files) {
195
+ const tail = await readTranscriptTail(file, { maxBytes: maxBytesPerTranscript });
196
+ if (tail.pathReadable) {
197
+ sessions.push(extractSafeToolResultEventsFromTranscriptLines(tail.lines));
198
+ }
199
+ }
200
+ return sessions;
201
+ }
202
+ async function listTranscriptFiles(root, maxFiles) {
203
+ if (maxFiles <= 0) {
204
+ return [];
205
+ }
206
+ const candidates = [];
207
+ const pending = [root];
208
+ while (pending.length > 0) {
209
+ const current = pending.pop();
210
+ let entries;
211
+ try {
212
+ entries = await readdir(current, { withFileTypes: true });
213
+ }
214
+ catch {
215
+ continue;
216
+ }
217
+ for (const entry of entries) {
218
+ const child = join(current, entry.name);
219
+ if (entry.isDirectory()) {
220
+ pending.push(child);
221
+ }
222
+ else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
223
+ const mtimeMs = await readableFileMtimeMs(child);
224
+ if (mtimeMs !== undefined) {
225
+ candidates.push({ path: child, mtimeMs });
226
+ }
227
+ }
228
+ }
229
+ }
230
+ return candidates
231
+ .sort((left, right) => right.mtimeMs - left.mtimeMs || left.path.localeCompare(right.path))
232
+ .slice(0, maxFiles)
233
+ .map((candidate) => candidate.path);
234
+ }
235
+ async function readableFileMtimeMs(path) {
236
+ try {
237
+ const fileStat = await stat(path);
238
+ return fileStat.isFile() ? fileStat.mtimeMs : undefined;
239
+ }
240
+ catch {
241
+ return undefined;
242
+ }
243
+ }
244
+ function holdoutSessionCount(total, ratio) {
245
+ if (total <= 1) {
246
+ return total;
247
+ }
248
+ return Math.max(1, Math.min(total - 1, Math.ceil(total * Math.max(0.05, Math.min(0.8, ratio)))));
249
+ }
250
+ function formatMetricRate(value) {
251
+ return value === undefined ? "n/a" : value.toFixed(2);
252
+ }
253
+ function formatCategoryCoverage(coverage) {
254
+ const parts = FAILURE_RECOVERY_CATEGORIES.flatMap((category) => {
255
+ const count = coverage[category] || 0;
256
+ return count > 0 ? [`${category}:${count}`] : [];
257
+ });
258
+ return parts.length > 0 ? parts.join(", ") : "none";
259
+ }
260
+ function roundRate(value) {
261
+ return Number(value.toFixed(4));
262
+ }
263
+ //# sourceMappingURL=historical-replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"historical-replay.js","sourceRoot":"","sources":["../src/historical-replay.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { hashValue } from "./paths.js";
2
2
  import { asRecord, numberField, stringField } from "./status-input.js";
3
- import { classifyToolPurpose, safeToolName } from "./tool-metadata.js";
3
+ import { classifyToolIdentity } from "./tool-metadata.js";
4
4
  export const SAFE_HOOK_EVENTS = [
5
5
  "PostToolUse",
6
6
  "PostToolUseFailure",
@@ -30,21 +30,25 @@ export function parseHookPayload(raw, fallbackEventName) {
30
30
  sessionKey: hashValue(sessionId)
31
31
  };
32
32
  if (hookEventName === "PostToolUseFailure") {
33
- const toolName = safeToolName(stringField(root.tool_name) || stringField(root.toolName));
33
+ const identity = classifyToolIdentity(stringField(root.tool_name) || stringField(root.toolName), root.tool_input ?? root.toolInput);
34
34
  return {
35
35
  ...base,
36
36
  kind: "tool_failure",
37
- toolName,
38
- purpose: classifyToolPurpose(toolName, root.tool_input ?? root.toolInput)
37
+ toolName: identity.displayName,
38
+ purpose: identity.purpose,
39
+ category: identity.category,
40
+ identityHash: identity.identityHash
39
41
  };
40
42
  }
41
43
  if (hookEventName === "PostToolUse") {
42
- const toolName = safeToolName(stringField(root.tool_name) || stringField(root.toolName));
44
+ const identity = classifyToolIdentity(stringField(root.tool_name) || stringField(root.toolName), root.tool_input ?? root.toolInput);
43
45
  return {
44
46
  ...base,
45
47
  kind: "tool_success",
46
- toolName,
47
- purpose: classifyToolPurpose(toolName, root.tool_input ?? root.toolInput)
48
+ toolName: identity.displayName,
49
+ purpose: identity.purpose,
50
+ category: identity.category,
51
+ identityHash: identity.identityHash
48
52
  };
49
53
  }
50
54
  if (hookEventName === "PostToolBatch") {
@@ -1 +1 @@
1
- {"version":3,"file":"hook-payload.js","sourceRoot":"","sources":["../src/hook-payload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hook-payload.js","sourceRoot":"","sources":["../src/hook-payload.ts"],"names":[],"mappings":"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"}
@@ -3,10 +3,9 @@ export declare function mergeHookSummary(transcript: TranscriptSummary, hookData
3
3
  failedToolResults: number;
4
4
  toolCalls: number;
5
5
  compactionEvents: number;
6
- repeatedFailures: Array<{
7
- toolName: string;
8
- count: number;
9
- purpose?: string;
10
- }>;
6
+ postCompactionActivity: number;
7
+ repeatedFailures: TranscriptSummary["repeatedFailures"];
8
+ blindRetry?: TranscriptSummary["blindRetry"];
11
9
  latestTimestamp?: string;
10
+ latestCompactionTimestamp?: string;
12
11
  }): TranscriptSummary;
@@ -1,25 +1,51 @@
1
1
  export function mergeHookSummary(transcript, hookData) {
2
2
  const repeatedFailures = new Map();
3
3
  for (const failure of [...transcript.repeatedFailures, ...hookData.repeatedFailures]) {
4
- const key = `${failure.toolName}:${failure.purpose || ""}`;
4
+ const key = failureKey(failure);
5
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
- });
6
+ repeatedFailures.set(key, { ...failure, count: Math.max(existing?.count || 0, failure.count) });
11
7
  }
8
+ const latestTimestamp = latestIsoTimestamp(transcript.latestTimestamp, hookData.latestTimestamp);
9
+ const latestCompactionTimestamp = latestIsoTimestamp(transcript.latestCompactionTimestamp, hookData.latestCompactionTimestamp);
12
10
  return {
13
11
  ...transcript,
14
12
  toolCalls: Math.max(transcript.toolCalls, hookData.toolCalls),
15
13
  failedToolResults: Math.max(transcript.failedToolResults, hookData.failedToolResults),
16
14
  repeatedFailures: [...repeatedFailures.values()].filter((failure) => failure.count >= 2),
15
+ blindRetry: strongestBlindRetry(transcript.blindRetry, hookData.blindRetry),
17
16
  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
17
+ postCompactionActivity: mergedPostCompactionActivity(transcript, hookData, latestTimestamp, latestCompactionTimestamp),
18
+ latestTimestamp,
19
+ latestCompactionTimestamp
23
20
  };
24
21
  }
22
+ function strongestBlindRetry(first, second) {
23
+ if (!first) {
24
+ return second;
25
+ }
26
+ if (!second) {
27
+ return first;
28
+ }
29
+ return second.blindRetryFailureCount > first.blindRetryFailureCount ? second : first;
30
+ }
31
+ function failureKey(failure) {
32
+ return failure.category === "MCP" && failure.identityHash
33
+ ? `MCP:${failure.identityHash}`
34
+ : `${failure.toolName}:${failure.purpose || ""}`;
35
+ }
36
+ function latestIsoTimestamp(first, second) {
37
+ if (first && second) {
38
+ return first > second ? first : second;
39
+ }
40
+ return first || second;
41
+ }
42
+ function mergedPostCompactionActivity(transcript, hookData, latestTimestamp, latestCompactionTimestamp) {
43
+ if (!latestCompactionTimestamp) {
44
+ return Math.max(transcript.postCompactionActivity, hookData.postCompactionActivity);
45
+ }
46
+ const transcriptActivity = transcript.latestCompactionTimestamp === latestCompactionTimestamp ? transcript.postCompactionActivity : 0;
47
+ const hookActivity = hookData.latestCompactionTimestamp === latestCompactionTimestamp ? hookData.postCompactionActivity : 0;
48
+ const laterActivitySeen = latestTimestamp !== undefined && latestTimestamp > latestCompactionTimestamp;
49
+ return Math.max(transcriptActivity, hookActivity, laterActivitySeen ? 1 : 0);
50
+ }
25
51
  //# sourceMappingURL=hook-summary.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"hook-summary.js","sourceRoot":"","sources":["../src/hook-summary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { DecisionConfidence, DecisionPersonalBaseline, FailureEpisodeSummary } from "./types.js";
2
+ export type FailureRecoveryCategory = "tests" | "lint" | "typecheck" | "build" | "read" | "grep" | "glob" | "ls" | "edit" | "mcp" | "tool";
3
+ export interface FailureRecoveryAggregate {
4
+ episodes: number;
5
+ recovered: number;
6
+ unrecovered: number;
7
+ activeEnded: number;
8
+ recoveryRate: number;
9
+ medianAttemptsBeforeRecovery: number;
10
+ p75AttemptsBeforeRecovery: number;
11
+ blindRetryEpisodes: number;
12
+ blindRetryRecovered: number;
13
+ blindRetryUnrecovered: number;
14
+ confidence: DecisionConfidence;
15
+ }
16
+ export interface BlindRetryAggregate {
17
+ episodes: number;
18
+ recovered: number;
19
+ unrecovered: number;
20
+ recoveryRate: number;
21
+ carefulLikeEpisodes: number;
22
+ stopLikeEpisodes: number;
23
+ confidence: DecisionConfidence;
24
+ }
25
+ export interface FailureRecoveryInsight {
26
+ kind: "usually_recovers" | "usually_unrecovered";
27
+ confidence: Exclude<DecisionConfidence, "low">;
28
+ category: FailureRecoveryCategory;
29
+ diagnosis: string;
30
+ baselineNote: string;
31
+ }
32
+ export interface RecoveryBuildCounters {
33
+ episodes: number;
34
+ recovered: number;
35
+ unrecovered: number;
36
+ activeEnded: number;
37
+ attemptsBeforeRecovery: number[];
38
+ blindRetryEpisodes: number;
39
+ blindRetryRecovered: number;
40
+ blindRetryUnrecovered: number;
41
+ blindRetryCarefulLikeEpisodes: number;
42
+ blindRetryStopLikeEpisodes: number;
43
+ }
44
+ export declare const FAILURE_RECOVERY_CATEGORIES: FailureRecoveryCategory[];
45
+ export declare function emptyRecoveryBuildCounters(): Record<FailureRecoveryCategory, RecoveryBuildCounters>;
46
+ export declare function mergeRecoveryBuildCounters(target: Record<FailureRecoveryCategory, RecoveryBuildCounters>, source: Record<FailureRecoveryCategory, RecoveryBuildCounters>): void;
47
+ export declare function addFailureEpisodeToRecoveryCounters(target: Record<FailureRecoveryCategory, RecoveryBuildCounters>, episode: FailureEpisodeSummary): void;
48
+ export declare function recoveryAggregatesFromCounters(counters: Record<FailureRecoveryCategory, RecoveryBuildCounters>): Partial<Record<FailureRecoveryCategory, FailureRecoveryAggregate>>;
49
+ export declare function blindRetryAggregatesFromCounters(counters: Record<FailureRecoveryCategory, RecoveryBuildCounters>): Partial<Record<FailureRecoveryCategory, BlindRetryAggregate>>;
50
+ export declare function recoveryInsight(baseline: DecisionPersonalBaseline | undefined, category: FailureRecoveryCategory, attempts: number): FailureRecoveryInsight | undefined;
51
+ export declare function recoveryInsightFromAggregate(category: FailureRecoveryCategory, aggregate: FailureRecoveryAggregate, attempts: number): FailureRecoveryInsight | undefined;
52
+ export declare function confidenceForSeen(seen: number): DecisionConfidence;
53
+ export declare function categoryFailureSingular(category: FailureRecoveryCategory): string;
54
+ export declare function categoryFailurePlural(category: FailureRecoveryCategory): string;
55
+ export declare function rate(count: number, total: number): number;
56
+ export declare function average(values: number[]): number;
57
+ export declare function percentile(values: number[], percentileValue: number): number;