bb-cc-lite 0.1.0

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.
@@ -0,0 +1,245 @@
1
+ import { hashValue } from "./paths.js";
2
+ import { mergeUsage } from "./status-input.js";
3
+ export function decide(input, transcript, options = {}) {
4
+ const now = new Date().toISOString();
5
+ const usage = mergeUsage(input.usage, transcript.usage);
6
+ const contextPercent = input.contextPercent;
7
+ const sessionKey = hashValue(input.sessionId);
8
+ const costUsd = input.costUsd;
9
+ const costSource = input.costSource;
10
+ if (!input.rawValid) {
11
+ return baseDecision({
12
+ state: "Careful",
13
+ reasonCode: "statusline_input_unavailable",
14
+ primaryEvidence: "statusline input unavailable",
15
+ impact: "Claude Code did not provide readable status JSON",
16
+ action: "run bb-cc-lite doctor and check Claude Code settings",
17
+ input,
18
+ usage,
19
+ transcript,
20
+ now,
21
+ sessionKey,
22
+ costUsd,
23
+ costSource
24
+ });
25
+ }
26
+ const repeatedFailure = transcript.repeatedFailures
27
+ .filter((item) => item.count >= 3)
28
+ .sort((a, b) => b.count - a.count)[0];
29
+ if (repeatedFailure) {
30
+ const runningTests = repeatedFailure.toolName === "Bash" && repeatedFailure.purpose === "tests";
31
+ return baseDecision({
32
+ state: "Stop",
33
+ reasonCode: "repeated_tool_failure",
34
+ primaryEvidence: `${repeatedFailure.toolName} failed ${repeatedFailure.count}x${runningTests ? " running tests" : ""}`,
35
+ impact: runningTests ? "Claude is retrying a broken test loop" : "Claude is retrying the same failing tool",
36
+ action: runningTests
37
+ ? "fix the test setup manually, then ask Claude to rerun only that test"
38
+ : repeatedFailure.toolName === "Bash"
39
+ ? "fix the failing command manually, then ask Claude to rerun only that command"
40
+ : `inspect the failing ${repeatedFailure.toolName} step manually before more retries`,
41
+ input,
42
+ usage,
43
+ transcript,
44
+ now,
45
+ sessionKey,
46
+ costUsd,
47
+ costSource
48
+ });
49
+ }
50
+ if (transcript.editTestLoopFailures >= 2) {
51
+ return baseDecision({
52
+ state: "Stop",
53
+ reasonCode: "edit_test_retry_loop",
54
+ primaryEvidence: `edit-test loop failed ${transcript.editTestLoopFailures}x`,
55
+ impact: "More edits are likely to churn without a narrower failure target",
56
+ action: "inspect the failing test manually, then ask Claude for one focused fix",
57
+ input,
58
+ usage,
59
+ transcript,
60
+ now,
61
+ sessionKey,
62
+ costUsd,
63
+ costSource
64
+ });
65
+ }
66
+ const earlyRepeatedFailure = transcript.repeatedFailures
67
+ .filter((item) => item.count >= 2)
68
+ .sort((a, b) => b.count - a.count)[0];
69
+ if (earlyRepeatedFailure) {
70
+ const runningTests = earlyRepeatedFailure.toolName === "Bash" && earlyRepeatedFailure.purpose === "tests";
71
+ return baseDecision({
72
+ state: "Careful",
73
+ reasonCode: "tool_failure_repeated",
74
+ primaryEvidence: `${earlyRepeatedFailure.toolName} failed ${earlyRepeatedFailure.count}x${runningTests ? " running tests" : ""}`,
75
+ impact: runningTests ? "Tests are failing repeatedly" : "A tool is starting to repeat failures",
76
+ action: runningTests
77
+ ? "pause and inspect the failing test before another retry"
78
+ : `inspect the failing ${earlyRepeatedFailure.toolName} step before another retry`,
79
+ input,
80
+ usage,
81
+ transcript,
82
+ now,
83
+ sessionKey,
84
+ costUsd,
85
+ costSource
86
+ });
87
+ }
88
+ if (contextPercent !== undefined && contextPercent >= 92) {
89
+ return baseDecision({
90
+ state: "Stop",
91
+ reasonCode: "context_critical",
92
+ primaryEvidence: `ctx ${contextPercent}%`,
93
+ impact: "Context is almost full",
94
+ action: "ask Claude for a handoff now, then compact or restart",
95
+ input,
96
+ usage,
97
+ transcript,
98
+ now,
99
+ sessionKey,
100
+ costUsd,
101
+ costSource
102
+ });
103
+ }
104
+ if (contextPercent !== undefined && contextPercent >= 80) {
105
+ return baseDecision({
106
+ state: "Careful",
107
+ reasonCode: "context_high",
108
+ primaryEvidence: `ctx ${contextPercent}%`,
109
+ impact: "Context is getting tight",
110
+ action: "ask Claude for a 6-bullet handoff before more work",
111
+ input,
112
+ usage,
113
+ transcript,
114
+ now,
115
+ sessionKey,
116
+ costUsd,
117
+ costSource
118
+ });
119
+ }
120
+ if (transcript.compactionEvents > 0) {
121
+ return baseDecision({
122
+ state: "Careful",
123
+ reasonCode: "compaction_boundary",
124
+ primaryEvidence: `compaction event seen`,
125
+ impact: "Session continuity may have shifted",
126
+ action: "ask Claude to restate current goal and next 3 steps",
127
+ input,
128
+ usage,
129
+ transcript,
130
+ now,
131
+ sessionKey,
132
+ costUsd,
133
+ costSource
134
+ });
135
+ }
136
+ if (cacheWritesHigh(usage)) {
137
+ return baseDecision({
138
+ state: "Careful",
139
+ reasonCode: "cache_writes_high",
140
+ primaryEvidence: "cache writes high",
141
+ impact: "Prompt cache is being created more than reused",
142
+ action: "keep the next prompt narrow and avoid broad repo scans",
143
+ input,
144
+ usage,
145
+ transcript,
146
+ now,
147
+ sessionKey,
148
+ costUsd,
149
+ costSource
150
+ });
151
+ }
152
+ if (input.rateLimitPercent !== undefined && input.rateLimitPercent >= 85) {
153
+ return baseDecision({
154
+ state: "Careful",
155
+ reasonCode: "rate_limit_high",
156
+ primaryEvidence: `rate limit ${input.rateLimitPercent}%`,
157
+ impact: "You are close to throttling",
158
+ action: "slow down and keep the next request small",
159
+ input,
160
+ usage,
161
+ transcript,
162
+ now,
163
+ sessionKey,
164
+ costUsd,
165
+ costSource
166
+ });
167
+ }
168
+ const costDelta = costUsd !== undefined && options.previous?.costUsd !== undefined ? costUsd - options.previous.costUsd : 0;
169
+ if (costDelta >= 0.5) {
170
+ return baseDecision({
171
+ state: "Careful",
172
+ reasonCode: "cost_growth",
173
+ primaryEvidence: `cost +${formatCost(costDelta)}`,
174
+ impact: "Spend rose quickly since the last statusline update",
175
+ action: "ask Claude to summarize progress before continuing",
176
+ input,
177
+ usage,
178
+ transcript,
179
+ now,
180
+ sessionKey,
181
+ costUsd,
182
+ costSource
183
+ });
184
+ }
185
+ return baseDecision({
186
+ state: "Healthy",
187
+ reasonCode: "healthy",
188
+ primaryEvidence: contextPercent !== undefined ? `ctx ${contextPercent}%` : "no stop-level findings",
189
+ impact: cacheWarm(usage) ? "cache warm" : "session stable",
190
+ action: "continue normally",
191
+ input,
192
+ usage,
193
+ transcript,
194
+ now,
195
+ sessionKey,
196
+ costUsd,
197
+ costSource
198
+ });
199
+ }
200
+ export function formatCost(value) {
201
+ if (value < 0.01) {
202
+ return `$${value.toFixed(4)}`;
203
+ }
204
+ if (value < 10) {
205
+ return `$${value.toFixed(2)}`;
206
+ }
207
+ return `$${Math.round(value).toString()}`;
208
+ }
209
+ function baseDecision(args) {
210
+ const evidence = [{ label: args.primaryEvidence }];
211
+ if (args.costUsd !== undefined) {
212
+ evidence.push({
213
+ label: args.costSource === "estimated" ? `${formatCost(args.costUsd)} est` : formatCost(args.costUsd),
214
+ detail: args.costSource === "estimated" ? "estimated" : args.costSource
215
+ });
216
+ }
217
+ if (args.impact && args.impact !== args.primaryEvidence) {
218
+ evidence.push({ label: args.impact });
219
+ }
220
+ return {
221
+ state: args.state,
222
+ reasonCode: args.reasonCode,
223
+ primaryEvidence: args.primaryEvidence,
224
+ evidence,
225
+ impact: args.impact,
226
+ action: args.action,
227
+ costUsd: args.costUsd,
228
+ costSource: args.costSource,
229
+ contextPercent: args.input.contextPercent,
230
+ rateLimitPercent: args.input.rateLimitPercent,
231
+ sessionKey: args.sessionKey,
232
+ createdAt: args.now
233
+ };
234
+ }
235
+ function cacheWritesHigh(usage) {
236
+ const writes = usage.cacheCreationInputTokens || 0;
237
+ const reads = usage.cacheReadInputTokens || 0;
238
+ return writes >= 10_000 && reads < writes * 0.2;
239
+ }
240
+ function cacheWarm(usage) {
241
+ const reads = usage.cacheReadInputTokens || 0;
242
+ const writes = usage.cacheCreationInputTokens || 0;
243
+ return reads > 0 && reads >= writes;
244
+ }
245
+ //# sourceMappingURL=signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signals.js","sourceRoot":"","sources":["../src/signals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { StatusLineInput, TokenUsage } from "./types.js";
2
+ export declare function readStdin(maxBytes?: number): Promise<string>;
3
+ export declare function parseStatusLineInput(raw: string): StatusLineInput;
4
+ export declare function mergeUsage(...items: Array<TokenUsage | undefined>): TokenUsage;
5
+ export declare function extractUsage(value: unknown): TokenUsage;
6
+ export declare function asRecord(value: unknown): Record<string, unknown> | undefined;
7
+ export declare function numberField(value: unknown): number | undefined;
8
+ export declare function stringField(value: unknown): string | undefined;
@@ -0,0 +1,208 @@
1
+ const MAX_STDIN_BYTES = 1024 * 1024;
2
+ export async function readStdin(maxBytes = MAX_STDIN_BYTES) {
3
+ const chunks = [];
4
+ let total = 0;
5
+ for await (const chunk of process.stdin) {
6
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
7
+ total += buffer.length;
8
+ if (total > maxBytes) {
9
+ const remaining = Math.max(0, maxBytes - (total - buffer.length));
10
+ chunks.push(buffer.subarray(0, remaining));
11
+ break;
12
+ }
13
+ chunks.push(buffer);
14
+ }
15
+ return Buffer.concat(chunks).toString("utf8");
16
+ }
17
+ export function parseStatusLineInput(raw) {
18
+ const trimmed = raw.trim();
19
+ if (!trimmed) {
20
+ return emptyInput("empty stdin");
21
+ }
22
+ let parsed;
23
+ try {
24
+ parsed = JSON.parse(trimmed);
25
+ }
26
+ catch (error) {
27
+ return emptyInput(error instanceof Error ? error.message : "invalid JSON");
28
+ }
29
+ const root = asRecord(parsed);
30
+ if (!root) {
31
+ return emptyInput("stdin JSON is not an object");
32
+ }
33
+ const model = asRecord(root.model);
34
+ const workspace = asRecord(root.workspace);
35
+ const cost = asRecord(root.cost);
36
+ const contextWindow = asRecord(root.context_window) || asRecord(root.contextWindow);
37
+ const usage = mergeUsage(extractUsage(root), extractUsage(asRecord(root.usage)), extractUsage(asRecord(root.token_usage)), extractUsage(asRecord(root.tokens)), extractUsage(asRecord(contextWindow?.current_usage) || asRecord(contextWindow?.currentUsage)), extractUsage(cost));
38
+ return {
39
+ rawValid: true,
40
+ sessionId: stringField(root.session_id) || stringField(root.sessionId),
41
+ transcriptPath: stringField(root.transcript_path) || stringField(root.transcriptPath),
42
+ cwd: stringField(root.cwd) ||
43
+ stringField(workspace?.current_dir) ||
44
+ stringField(workspace?.project_dir),
45
+ model: {
46
+ id: stringField(model?.id) || stringField(root.model_id) || stringField(root.model),
47
+ displayName: stringField(model?.display_name) ||
48
+ stringField(model?.displayName) ||
49
+ stringField(model?.name)
50
+ },
51
+ costUsd: numberField(cost?.total_cost_usd) ||
52
+ numberField(cost?.totalCostUsd) ||
53
+ numberField(cost?.cost_usd) ||
54
+ numberField(root.total_cost_usd),
55
+ costSource: numberField(cost?.total_cost_usd) === undefined ? undefined : "claude",
56
+ durationMs: numberField(cost?.total_duration_ms) || numberField(root.duration_ms),
57
+ contextPercent: extractPercent(root, ["context", "context_window", "contextWindow"]),
58
+ rateLimitPercent: extractPercent(root, ["rate_limit", "rateLimit", "rate_limits", "rateLimits"]),
59
+ usage,
60
+ terminalWidth: numberField(root.terminal_width) ||
61
+ numberField(root.terminalWidth) ||
62
+ numberFromString(process.env.BB_CC_LITE_WIDTH) ||
63
+ numberFromString(process.env.COLUMNS)
64
+ };
65
+ }
66
+ export function mergeUsage(...items) {
67
+ const result = {};
68
+ for (const item of items) {
69
+ if (!item) {
70
+ continue;
71
+ }
72
+ result.inputTokens = preferNumber(result.inputTokens, item.inputTokens);
73
+ result.outputTokens = preferNumber(result.outputTokens, item.outputTokens);
74
+ result.cacheCreationInputTokens = preferNumber(result.cacheCreationInputTokens, item.cacheCreationInputTokens);
75
+ result.cacheReadInputTokens = preferNumber(result.cacheReadInputTokens, item.cacheReadInputTokens);
76
+ result.totalTokens = preferNumber(result.totalTokens, item.totalTokens);
77
+ }
78
+ return result;
79
+ }
80
+ export function extractUsage(value) {
81
+ const root = asRecord(value);
82
+ if (!root) {
83
+ return {};
84
+ }
85
+ const usage = asRecord(root.usage) || root;
86
+ const cacheCreationDetails = asRecord(usage.cache_creation);
87
+ const cacheReadDetails = asRecord(usage.cache_read);
88
+ return {
89
+ inputTokens: numberField(usage.input_tokens) ||
90
+ numberField(usage.inputTokens) ||
91
+ numberField(usage.prompt_tokens) ||
92
+ numberField(usage.promptTokens),
93
+ outputTokens: numberField(usage.output_tokens) ||
94
+ numberField(usage.outputTokens) ||
95
+ numberField(usage.completion_tokens) ||
96
+ numberField(usage.completionTokens),
97
+ cacheCreationInputTokens: numberField(usage.cache_creation_input_tokens) ||
98
+ numberField(usage.cacheCreationInputTokens) ||
99
+ numberField(usage.cache_write_input_tokens) ||
100
+ numberField(usage.cacheWriteInputTokens) ||
101
+ numberField(cacheCreationDetails?.input_tokens),
102
+ cacheReadInputTokens: numberField(usage.cache_read_input_tokens) ||
103
+ numberField(usage.cacheReadInputTokens) ||
104
+ numberField(cacheReadDetails?.input_tokens),
105
+ totalTokens: numberField(usage.total_tokens) ||
106
+ numberField(usage.totalTokens) ||
107
+ numberField(usage.context_tokens) ||
108
+ numberField(usage.contextTokens)
109
+ };
110
+ }
111
+ export function asRecord(value) {
112
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
113
+ return value;
114
+ }
115
+ return undefined;
116
+ }
117
+ export function numberField(value) {
118
+ if (typeof value === "number" && Number.isFinite(value)) {
119
+ return value;
120
+ }
121
+ if (typeof value === "string") {
122
+ return numberFromString(value);
123
+ }
124
+ return undefined;
125
+ }
126
+ export function stringField(value) {
127
+ return typeof value === "string" && value.length > 0 ? value : undefined;
128
+ }
129
+ function emptyInput(parseError) {
130
+ return {
131
+ rawValid: false,
132
+ model: {},
133
+ usage: {},
134
+ parseError
135
+ };
136
+ }
137
+ function numberFromString(value) {
138
+ if (!value) {
139
+ return undefined;
140
+ }
141
+ const parsed = Number(value.replace("%", ""));
142
+ return Number.isFinite(parsed) ? parsed : undefined;
143
+ }
144
+ function preferNumber(current, next) {
145
+ if (next === undefined) {
146
+ return current;
147
+ }
148
+ if (current === undefined) {
149
+ return next;
150
+ }
151
+ return Math.max(current, next);
152
+ }
153
+ function extractPercent(root, keys) {
154
+ for (const key of keys) {
155
+ const candidate = root[key];
156
+ const direct = numberField(candidate);
157
+ if (direct !== undefined) {
158
+ return normalizePercent(direct);
159
+ }
160
+ const record = asRecord(candidate);
161
+ if (!record) {
162
+ continue;
163
+ }
164
+ const explicit = numberField(record.percent) ||
165
+ numberField(record.percentage) ||
166
+ numberField(record.used_percent) ||
167
+ numberField(record.usedPercentage) ||
168
+ numberField(record.used_percentage) ||
169
+ numberField(record.usedPercent);
170
+ if (explicit !== undefined) {
171
+ return normalizePercent(explicit);
172
+ }
173
+ const nested = Object.values(record)
174
+ .flatMap((value) => {
175
+ const child = asRecord(value);
176
+ if (!child) {
177
+ return [];
178
+ }
179
+ const childPercent = numberField(child.used_percentage) ||
180
+ numberField(child.usedPercentage) ||
181
+ numberField(child.percent) ||
182
+ numberField(child.percentage);
183
+ return childPercent === undefined ? [] : [childPercent];
184
+ })
185
+ .sort((a, b) => b - a)[0];
186
+ if (nested !== undefined) {
187
+ return normalizePercent(nested);
188
+ }
189
+ const used = numberField(record.used) ||
190
+ numberField(record.used_tokens) ||
191
+ numberField(record.usedTokens) ||
192
+ numberField(record.current);
193
+ const total = numberField(record.total) ||
194
+ numberField(record.limit) ||
195
+ numberField(record.window) ||
196
+ numberField(record.max) ||
197
+ numberField(record.context_window);
198
+ if (used !== undefined && total !== undefined && total > 0) {
199
+ return normalizePercent((used / total) * 100);
200
+ }
201
+ }
202
+ return undefined;
203
+ }
204
+ function normalizePercent(value) {
205
+ const normalized = value <= 1 ? value * 100 : value;
206
+ return Math.max(0, Math.min(100, Math.round(normalized)));
207
+ }
208
+ //# sourceMappingURL=status-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-input.js","sourceRoot":"","sources":["../src/status-input.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Decision, DerivedHookEvent, EventStoreData, StoredDecision, StoredHookEvent, ToolFailureSummary } from "./types.js";
2
+ export declare function readStore(storePath?: string): Promise<EventStoreData>;
3
+ export declare function recordDecision(decision: Decision, storePath?: string): Promise<StoredDecision>;
4
+ export declare function recordHookEvent(event: DerivedHookEvent, storePath?: string): Promise<StoredHookEvent>;
5
+ export declare function latestDecision(sessionKey?: string, storePath?: string): Promise<StoredDecision | undefined>;
6
+ export declare function hookSummary(sessionKey: string | undefined, storePath?: string): Promise<{
7
+ failedToolResults: number;
8
+ toolCalls: number;
9
+ compactionEvents: number;
10
+ repeatedFailures: ToolFailureSummary[];
11
+ latestTimestamp?: string;
12
+ }>;
package/dist/store.js ADDED
@@ -0,0 +1,111 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { dirname } from "node:path";
4
+ import { eventStorePath } from "./paths.js";
5
+ const STORE_LIMIT = 100;
6
+ const HOOK_STORE_LIMIT = 500;
7
+ export async function readStore(storePath = eventStorePath()) {
8
+ try {
9
+ const parsed = JSON.parse(await readFile(storePath, "utf8"));
10
+ return {
11
+ version: 1,
12
+ updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : new Date(0).toISOString(),
13
+ decisions: Array.isArray(parsed.decisions) ? parsed.decisions.filter(isStoredDecision).slice(-STORE_LIMIT) : [],
14
+ hookEvents: Array.isArray(parsed.hookEvents) ? parsed.hookEvents.filter(isStoredHookEvent).slice(-HOOK_STORE_LIMIT) : []
15
+ };
16
+ }
17
+ catch {
18
+ return { version: 1, updatedAt: new Date(0).toISOString(), decisions: [], hookEvents: [] };
19
+ }
20
+ }
21
+ export async function recordDecision(decision, storePath = eventStorePath()) {
22
+ const store = await readStore(storePath);
23
+ const stored = {
24
+ ...decision,
25
+ id: randomUUID()
26
+ };
27
+ store.decisions.push(stored);
28
+ store.decisions = store.decisions.slice(-STORE_LIMIT);
29
+ store.updatedAt = new Date().toISOString();
30
+ await writeStore(store, storePath);
31
+ return stored;
32
+ }
33
+ export async function recordHookEvent(event, storePath = eventStorePath()) {
34
+ const store = await readStore(storePath);
35
+ const stored = {
36
+ ...event,
37
+ id: randomUUID()
38
+ };
39
+ store.hookEvents.push(stored);
40
+ store.hookEvents = store.hookEvents.slice(-HOOK_STORE_LIMIT);
41
+ store.updatedAt = new Date().toISOString();
42
+ await writeStore(store, storePath);
43
+ return stored;
44
+ }
45
+ export async function latestDecision(sessionKey, storePath = eventStorePath()) {
46
+ const store = await readStore(storePath);
47
+ const decisions = sessionKey ? store.decisions.filter((decision) => decision.sessionKey === sessionKey) : store.decisions;
48
+ return decisions.at(-1);
49
+ }
50
+ export async function hookSummary(sessionKey, storePath = eventStorePath()) {
51
+ const store = await readStore(storePath);
52
+ const events = store.hookEvents.filter((event) => !sessionKey || event.sessionKey === sessionKey);
53
+ const failures = new Map();
54
+ let failedToolResults = 0;
55
+ let toolCalls = 0;
56
+ let compactionEvents = 0;
57
+ let latestTimestamp;
58
+ for (const event of events) {
59
+ latestTimestamp = !latestTimestamp || event.timestamp > latestTimestamp ? event.timestamp : latestTimestamp;
60
+ if (event.kind === "tool_failure") {
61
+ failedToolResults += 1;
62
+ toolCalls += 1;
63
+ const toolName = event.toolName || "tool";
64
+ const key = `${toolName}:${event.purpose || ""}`;
65
+ const existing = failures.get(key);
66
+ failures.set(key, {
67
+ toolName,
68
+ purpose: event.purpose,
69
+ count: (existing?.count || 0) + 1
70
+ });
71
+ }
72
+ else if (event.kind === "tool_success") {
73
+ toolCalls += 1;
74
+ }
75
+ else if (event.kind === "tool_batch") {
76
+ toolCalls += event.toolCount || 0;
77
+ }
78
+ else if (event.kind === "compaction") {
79
+ compactionEvents += 1;
80
+ }
81
+ }
82
+ return {
83
+ failedToolResults,
84
+ toolCalls,
85
+ compactionEvents,
86
+ repeatedFailures: [...failures.values()].filter((failure) => failure.count >= 2),
87
+ latestTimestamp
88
+ };
89
+ }
90
+ async function writeStore(store, storePath) {
91
+ await mkdir(dirname(storePath), { recursive: true, mode: 0o700 });
92
+ const tempPath = `${storePath}.${process.pid}.tmp`;
93
+ await writeFile(tempPath, `${JSON.stringify(store, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
94
+ await chmod(tempPath, 0o600);
95
+ await rename(tempPath, storePath);
96
+ }
97
+ function isStoredDecision(value) {
98
+ if (typeof value !== "object" || value === null) {
99
+ return false;
100
+ }
101
+ const record = value;
102
+ return typeof record.id === "string" && typeof record.state === "string" && typeof record.action === "string";
103
+ }
104
+ function isStoredHookEvent(value) {
105
+ if (typeof value !== "object" || value === null) {
106
+ return false;
107
+ }
108
+ const record = value;
109
+ return typeof record.id === "string" && typeof record.kind === "string" && typeof record.timestamp === "string";
110
+ }
111
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { TranscriptSummary } from "./types.js";
2
+ export interface ParseTranscriptOptions {
3
+ maxBytes?: number;
4
+ }
5
+ export declare function parseTranscriptTail(transcriptPath: string | undefined, options?: ParseTranscriptOptions): Promise<TranscriptSummary>;
6
+ export declare function parseTranscriptLines(lines: string[], bytesRead?: number): TranscriptSummary;