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
package/dist/store.d.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import type { Decision, DerivedHookEvent,
|
|
2
|
-
export
|
|
1
|
+
import type { Decision, DerivedHookEvent, StoredDecision, StoredHookEvent } from "./types.js";
|
|
2
|
+
export { readStore } from "./event-store-persistence.js";
|
|
3
|
+
export { hookSummary, latestDecision } from "./event-store-queries.js";
|
|
3
4
|
export declare function recordDecision(decision: Decision, storePath?: string): Promise<StoredDecision>;
|
|
4
5
|
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
CHANGED
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
import { dirname } from "node:path";
|
|
2
|
+
import { HOOK_STORE_LIMIT, STORE_LIMIT, readStore, writeStore } from "./event-store-persistence.js";
|
|
4
3
|
import { eventStorePath } from "./paths.js";
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
}
|
|
4
|
+
export { readStore } from "./event-store-persistence.js";
|
|
5
|
+
export { hookSummary, latestDecision } from "./event-store-queries.js";
|
|
21
6
|
export async function recordDecision(decision, storePath = eventStorePath()) {
|
|
22
7
|
const store = await readStore(storePath);
|
|
23
8
|
const stored = {
|
|
@@ -42,70 +27,4 @@ export async function recordHookEvent(event, storePath = eventStorePath()) {
|
|
|
42
27
|
await writeStore(store, storePath);
|
|
43
28
|
return stored;
|
|
44
29
|
}
|
|
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
30
|
//# sourceMappingURL=store.js.map
|
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,UAAU,EACX,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEvE,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAAkB,EAAE,SAAS,GAAG,cAAc,EAAE;IACnF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,MAAM,GAAmB;QAC7B,GAAG,QAAQ;QACX,EAAE,EAAE,UAAU,EAAE;KACjB,CAAC;IACF,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC;IACtD,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAuB,EAAE,SAAS,GAAG,cAAc,EAAE;IACzF,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,MAAM,GAAoB;QAC9B,GAAG,KAAK;QACR,EAAE,EAAE,UAAU,EAAE;KACjB,CAAC;IACF,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC7D,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface SafeToolNameOptions {
|
|
2
|
+
basenameOnly?: boolean;
|
|
3
|
+
}
|
|
4
|
+
export declare function classifyToolPurpose(toolName: string, input: unknown, options?: SafeToolNameOptions): string | undefined;
|
|
5
|
+
export declare function classifyResultPurpose(part: Record<string, unknown>): string | undefined;
|
|
6
|
+
export declare function isEditTool(toolName: string, options?: SafeToolNameOptions): boolean;
|
|
7
|
+
export declare function safeToolName(toolName: string | undefined, options?: SafeToolNameOptions): string;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { asRecord, stringField } from "./status-input.js";
|
|
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
|
+
const LINT_COMMAND_RE = /\b(npm|pnpm|yarn|bun)\s+(run\s+)?lint\b|\b(eslint|ruff|flake8|cargo\s+clippy)\b/i;
|
|
5
|
+
const TYPECHECK_COMMAND_RE = /\b(npm|pnpm|yarn|bun)\s+(run\s+)?typecheck\b|\btsc\s+--noEmit\b|\bmypy\b/i;
|
|
6
|
+
const BUILD_COMMAND_RE = /\b(npm|pnpm|yarn|bun)\s+(run\s+)?(build|compile)\b|\b(tsc|vite\s+build|cargo\s+build|go\s+build)\b/i;
|
|
7
|
+
export function classifyToolPurpose(toolName, input, options = {}) {
|
|
8
|
+
if (safeToolName(toolName, options) !== "Bash") {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
const command = stringField(asRecord(input)?.command);
|
|
12
|
+
if (command && TEST_COMMAND_RE.test(command)) {
|
|
13
|
+
return "tests";
|
|
14
|
+
}
|
|
15
|
+
if (command && LINT_COMMAND_RE.test(command)) {
|
|
16
|
+
return "lint";
|
|
17
|
+
}
|
|
18
|
+
if (command && TYPECHECK_COMMAND_RE.test(command)) {
|
|
19
|
+
return "typecheck";
|
|
20
|
+
}
|
|
21
|
+
if (command && BUILD_COMMAND_RE.test(command)) {
|
|
22
|
+
return "build";
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
export function classifyResultPurpose(part) {
|
|
27
|
+
const title = stringField(part.title) || stringField(part.summary);
|
|
28
|
+
if (title && /test/i.test(title)) {
|
|
29
|
+
return "tests";
|
|
30
|
+
}
|
|
31
|
+
if (title && /lint/i.test(title)) {
|
|
32
|
+
return "lint";
|
|
33
|
+
}
|
|
34
|
+
if (title && /typecheck|type check|tsc/i.test(title)) {
|
|
35
|
+
return "typecheck";
|
|
36
|
+
}
|
|
37
|
+
if (title && /build|compile/i.test(title)) {
|
|
38
|
+
return "build";
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
export function isEditTool(toolName, options = {}) {
|
|
43
|
+
return /^(Edit|MultiEdit|Write|NotebookEdit)$/u.test(safeToolName(toolName, options));
|
|
44
|
+
}
|
|
45
|
+
export function safeToolName(toolName, options = {}) {
|
|
46
|
+
if (!toolName) {
|
|
47
|
+
return "tool";
|
|
48
|
+
}
|
|
49
|
+
const candidate = options.basenameOnly ? basename(toolName) : toolName;
|
|
50
|
+
return /^[A-Za-z][A-Za-z0-9_-]{0,32}$/u.test(candidate) ? candidate : "tool";
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=tool-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-metadata.js","sourceRoot":"","sources":["../src/tool-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE1D,MAAM,eAAe,GACnB,oIAAoI,CAAC;AACvI,MAAM,eAAe,GAAG,kFAAkF,CAAC;AAC3G,MAAM,oBAAoB,GAAG,2EAA2E,CAAC;AACzG,MAAM,gBAAgB,GAAG,qGAAqG,CAAC;AAM/H,MAAM,UAAU,mBAAmB,CACjC,QAAgB,EAChB,KAAc,EACd,UAA+B,EAAE;IAEjC,IAAI,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC;IACtD,IAAI,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,OAAO,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAClD,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,IAAI,OAAO,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAA6B;IACjE,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnE,IAAI,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,KAAK,IAAI,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,IAAI,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,UAA+B,EAAE;IAC5E,OAAO,wCAAwC,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAA4B,EAAE,UAA+B,EAAE;IAC1F,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACvE,OAAO,gCAAgC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface ReadTranscriptTailOptions {
|
|
2
|
+
maxBytes?: number;
|
|
3
|
+
}
|
|
4
|
+
export interface TranscriptTail {
|
|
5
|
+
pathReadable: boolean;
|
|
6
|
+
bytesRead: number;
|
|
7
|
+
lines: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function readTranscriptTail(transcriptPath: string | undefined, options?: ReadTranscriptTailOptions): Promise<TranscriptTail>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { open, stat } from "node:fs/promises";
|
|
2
|
+
const DEFAULT_MAX_BYTES = 512 * 1024;
|
|
3
|
+
export async function readTranscriptTail(transcriptPath, options = {}) {
|
|
4
|
+
if (!transcriptPath) {
|
|
5
|
+
return unreadableTail();
|
|
6
|
+
}
|
|
7
|
+
try {
|
|
8
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
9
|
+
const fileStat = await stat(transcriptPath);
|
|
10
|
+
const bytesRead = Math.min(fileStat.size, maxBytes);
|
|
11
|
+
const start = Math.max(0, fileStat.size - bytesRead);
|
|
12
|
+
const handle = await open(transcriptPath, "r");
|
|
13
|
+
try {
|
|
14
|
+
const buffer = Buffer.alloc(bytesRead);
|
|
15
|
+
await handle.read(buffer, 0, bytesRead, start);
|
|
16
|
+
const text = buffer.toString("utf8");
|
|
17
|
+
return {
|
|
18
|
+
pathReadable: true,
|
|
19
|
+
bytesRead,
|
|
20
|
+
lines: trimPartialFirstLine(text, start).split(/\r?\n/).filter(Boolean)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
await handle.close();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return unreadableTail();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function unreadableTail() {
|
|
32
|
+
return {
|
|
33
|
+
pathReadable: false,
|
|
34
|
+
bytesRead: 0,
|
|
35
|
+
lines: []
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function trimPartialFirstLine(text, start) {
|
|
39
|
+
if (start === 0) {
|
|
40
|
+
return text;
|
|
41
|
+
}
|
|
42
|
+
const newline = text.indexOf("\n");
|
|
43
|
+
return newline === -1 ? "" : text.slice(newline + 1);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=transcript-reader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-reader.js","sourceRoot":"","sources":["../src/transcript-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,CAAC;AAYrC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,cAAkC,EAClC,UAAqC,EAAE;IAEvC,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,OAAO,cAAc,EAAE,CAAC;IAC1B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;QACvD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QAC/C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACvC,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;YAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACrC,OAAO;gBACL,YAAY,EAAE,IAAI;gBAClB,SAAS;gBACT,KAAK,EAAE,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;aACxE,CAAC;QACJ,CAAC;gBAAS,CAAC;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,EAAE,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,SAAS,cAAc;IACrB,OAAO;QACL,YAAY,EAAE,KAAK;QACnB,SAAS,EAAE,CAAC;QACZ,KAAK,EAAE,EAAE;KACV,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY,EAAE,KAAa;IACvD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;AACvD,CAAC"}
|
package/dist/transcript.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { type ReadTranscriptTailOptions } from "./transcript-reader.js";
|
|
1
2
|
import type { TranscriptSummary } from "./types.js";
|
|
2
|
-
export
|
|
3
|
-
maxBytes?: number;
|
|
4
|
-
}
|
|
3
|
+
export type ParseTranscriptOptions = ReadTranscriptTailOptions;
|
|
5
4
|
export declare function parseTranscriptTail(transcriptPath: string | undefined, options?: ParseTranscriptOptions): Promise<TranscriptSummary>;
|
|
6
5
|
export declare function parseTranscriptLines(lines: string[], bytesRead?: number): TranscriptSummary;
|
package/dist/transcript.js
CHANGED
|
@@ -1,39 +1,25 @@
|
|
|
1
|
-
import { open, stat } from "node:fs/promises";
|
|
2
|
-
import { basename } from "node:path";
|
|
3
1
|
import { asRecord, extractUsage, mergeUsage, stringField } from "./status-input.js";
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { classifyResultPurpose, classifyToolPurpose, isEditTool, safeToolName } from "./tool-metadata.js";
|
|
3
|
+
import { readTranscriptTail } from "./transcript-reader.js";
|
|
6
4
|
export async function parseTranscriptTail(transcriptPath, options = {}) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
try {
|
|
11
|
-
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
12
|
-
const fileStat = await stat(transcriptPath);
|
|
13
|
-
const bytesRead = Math.min(fileStat.size, maxBytes);
|
|
14
|
-
const start = Math.max(0, fileStat.size - bytesRead);
|
|
15
|
-
const handle = await open(transcriptPath, "r");
|
|
16
|
-
try {
|
|
17
|
-
const buffer = Buffer.alloc(bytesRead);
|
|
18
|
-
await handle.read(buffer, 0, bytesRead, start);
|
|
19
|
-
const text = buffer.toString("utf8");
|
|
20
|
-
const lines = trimPartialFirstLine(text, start).split(/\r?\n/).filter(Boolean);
|
|
21
|
-
return parseTranscriptLines(lines, bytesRead);
|
|
22
|
-
}
|
|
23
|
-
finally {
|
|
24
|
-
await handle.close();
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
5
|
+
const tail = await readTranscriptTail(transcriptPath, options);
|
|
6
|
+
if (!tail.pathReadable) {
|
|
28
7
|
return emptySummary(false);
|
|
29
8
|
}
|
|
9
|
+
return parseTranscriptLines(tail.lines, tail.bytesRead);
|
|
30
10
|
}
|
|
31
11
|
export function parseTranscriptLines(lines, bytesRead = Buffer.byteLength(lines.join("\n"))) {
|
|
32
12
|
const toolById = new Map();
|
|
33
13
|
const failureCounts = new Map();
|
|
34
14
|
let recentEditBeforeTest = false;
|
|
15
|
+
let hasUnvalidatedEdits = false;
|
|
16
|
+
let unvalidatedEditStep;
|
|
17
|
+
let validationFailedSinceSuccess = false;
|
|
18
|
+
let validationRecovered = false;
|
|
35
19
|
let editTestLoopFailures = 0;
|
|
20
|
+
let toolResultStep = 0;
|
|
36
21
|
let toolCalls = 0;
|
|
22
|
+
let readToolCalls = 0;
|
|
37
23
|
let failedToolResults = 0;
|
|
38
24
|
let malformedLines = 0;
|
|
39
25
|
let compactionEvents = 0;
|
|
@@ -60,26 +46,49 @@ export function parseTranscriptLines(lines, bytesRead = Buffer.byteLength(lines.
|
|
|
60
46
|
}
|
|
61
47
|
for (const toolUse of extractToolUses(entry)) {
|
|
62
48
|
toolCalls += 1;
|
|
49
|
+
if (isReadLikeTool(toolUse.name)) {
|
|
50
|
+
readToolCalls += 1;
|
|
51
|
+
}
|
|
63
52
|
if (toolUse.id) {
|
|
53
|
+
const isEdit = isEditTool(toolUse.name, { basenameOnly: true });
|
|
64
54
|
toolById.set(toolUse.id, {
|
|
65
|
-
name: safeToolName(toolUse.name),
|
|
66
|
-
purpose: classifyToolPurpose(toolUse.name, toolUse.input)
|
|
55
|
+
name: safeToolName(toolUse.name, { basenameOnly: true }),
|
|
56
|
+
purpose: classifyToolPurpose(toolUse.name, toolUse.input, { basenameOnly: true }),
|
|
57
|
+
isEdit
|
|
67
58
|
});
|
|
68
59
|
}
|
|
69
|
-
if (isEditTool(toolUse.name)) {
|
|
60
|
+
if (isEditTool(toolUse.name, { basenameOnly: true })) {
|
|
70
61
|
recentEditBeforeTest = true;
|
|
71
62
|
}
|
|
72
63
|
}
|
|
73
64
|
for (const toolResult of extractToolResults(entry)) {
|
|
65
|
+
toolResultStep += 1;
|
|
66
|
+
const meta = (toolResult.toolUseId ? toolById.get(toolResult.toolUseId) : undefined) ||
|
|
67
|
+
(toolResult.toolName
|
|
68
|
+
? { name: safeToolName(toolResult.toolName, { basenameOnly: true }), purpose: undefined, isEdit: false }
|
|
69
|
+
: undefined) ||
|
|
70
|
+
{ name: "tool", purpose: undefined, isEdit: false };
|
|
71
|
+
const purpose = toolResult.purpose || meta.purpose;
|
|
72
|
+
const key = `${meta.name}:${purpose || ""}`;
|
|
74
73
|
if (!toolResult.isError) {
|
|
74
|
+
failureCounts.delete(key);
|
|
75
|
+
if (meta.isEdit) {
|
|
76
|
+
hasUnvalidatedEdits = true;
|
|
77
|
+
unvalidatedEditStep = toolResultStep;
|
|
78
|
+
}
|
|
79
|
+
else if (meta.name === "Bash" && purpose === "tests") {
|
|
80
|
+
if (validationFailedSinceSuccess) {
|
|
81
|
+
validationRecovered = true;
|
|
82
|
+
}
|
|
83
|
+
validationFailedSinceSuccess = false;
|
|
84
|
+
editTestLoopFailures = 0;
|
|
85
|
+
recentEditBeforeTest = false;
|
|
86
|
+
hasUnvalidatedEdits = false;
|
|
87
|
+
unvalidatedEditStep = undefined;
|
|
88
|
+
}
|
|
75
89
|
continue;
|
|
76
90
|
}
|
|
77
91
|
failedToolResults += 1;
|
|
78
|
-
const meta = (toolResult.toolUseId ? toolById.get(toolResult.toolUseId) : undefined) ||
|
|
79
|
-
(toolResult.toolName ? { name: safeToolName(toolResult.toolName), purpose: undefined } : undefined) ||
|
|
80
|
-
{ name: "tool", purpose: undefined };
|
|
81
|
-
const purpose = toolResult.purpose || meta.purpose;
|
|
82
|
-
const key = `${meta.name}:${purpose || ""}`;
|
|
83
92
|
const existing = failureCounts.get(key);
|
|
84
93
|
failureCounts.set(key, {
|
|
85
94
|
toolName: meta.name,
|
|
@@ -90,6 +99,9 @@ export function parseTranscriptLines(lines, bytesRead = Buffer.byteLength(lines.
|
|
|
90
99
|
editTestLoopFailures += 1;
|
|
91
100
|
recentEditBeforeTest = false;
|
|
92
101
|
}
|
|
102
|
+
if (meta.name === "Bash" && purpose === "tests") {
|
|
103
|
+
validationFailedSinceSuccess = true;
|
|
104
|
+
}
|
|
93
105
|
}
|
|
94
106
|
}
|
|
95
107
|
return {
|
|
@@ -98,9 +110,13 @@ export function parseTranscriptLines(lines, bytesRead = Buffer.byteLength(lines.
|
|
|
98
110
|
linesRead: lines.length,
|
|
99
111
|
malformedLines,
|
|
100
112
|
toolCalls,
|
|
113
|
+
readToolCalls,
|
|
101
114
|
failedToolResults,
|
|
102
115
|
repeatedFailures: [...failureCounts.values()].filter((item) => item.count >= 2),
|
|
103
116
|
editTestLoopFailures,
|
|
117
|
+
hasUnvalidatedEdits,
|
|
118
|
+
unvalidatedEditToolSteps: hasUnvalidatedEdits && unvalidatedEditStep !== undefined ? toolResultStep - unvalidatedEditStep : undefined,
|
|
119
|
+
validationRecovered,
|
|
104
120
|
compactionEvents,
|
|
105
121
|
usage,
|
|
106
122
|
latestTimestamp
|
|
@@ -113,19 +129,18 @@ function emptySummary(pathReadable) {
|
|
|
113
129
|
linesRead: 0,
|
|
114
130
|
malformedLines: 0,
|
|
115
131
|
toolCalls: 0,
|
|
132
|
+
readToolCalls: 0,
|
|
116
133
|
failedToolResults: 0,
|
|
117
134
|
repeatedFailures: [],
|
|
118
135
|
editTestLoopFailures: 0,
|
|
136
|
+
hasUnvalidatedEdits: false,
|
|
137
|
+
validationRecovered: false,
|
|
119
138
|
compactionEvents: 0,
|
|
120
139
|
usage: {}
|
|
121
140
|
};
|
|
122
141
|
}
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
return text;
|
|
126
|
-
}
|
|
127
|
-
const newline = text.indexOf("\n");
|
|
128
|
-
return newline === -1 ? "" : text.slice(newline + 1);
|
|
142
|
+
function isReadLikeTool(toolName) {
|
|
143
|
+
return /^(Read|Grep|Glob|LS|WebFetch|WebSearch)$/u.test(safeToolName(toolName, { basenameOnly: true }));
|
|
129
144
|
}
|
|
130
145
|
function extractToolUses(entry) {
|
|
131
146
|
const result = [];
|
|
@@ -191,33 +206,6 @@ function truthyError(value) {
|
|
|
191
206
|
const exitCode = value.exit_code ?? value.exitCode;
|
|
192
207
|
return typeof exitCode === "number" && exitCode !== 0;
|
|
193
208
|
}
|
|
194
|
-
function classifyToolPurpose(toolName, input) {
|
|
195
|
-
if (safeToolName(toolName) !== "Bash") {
|
|
196
|
-
return undefined;
|
|
197
|
-
}
|
|
198
|
-
const command = stringField(asRecord(input)?.command);
|
|
199
|
-
if (command && TEST_COMMAND_RE.test(command)) {
|
|
200
|
-
return "tests";
|
|
201
|
-
}
|
|
202
|
-
return undefined;
|
|
203
|
-
}
|
|
204
|
-
function classifyResultPurpose(part) {
|
|
205
|
-
const title = stringField(part.title) || stringField(part.summary);
|
|
206
|
-
if (title && /test/i.test(title)) {
|
|
207
|
-
return "tests";
|
|
208
|
-
}
|
|
209
|
-
return undefined;
|
|
210
|
-
}
|
|
211
|
-
function isEditTool(toolName) {
|
|
212
|
-
return /^(Edit|MultiEdit|Write|NotebookEdit)$/u.test(safeToolName(toolName));
|
|
213
|
-
}
|
|
214
|
-
function safeToolName(toolName) {
|
|
215
|
-
if (!toolName) {
|
|
216
|
-
return "tool";
|
|
217
|
-
}
|
|
218
|
-
const base = basename(toolName);
|
|
219
|
-
return /^[A-Za-z][A-Za-z0-9_-]{0,32}$/u.test(base) ? base : "tool";
|
|
220
|
-
}
|
|
221
209
|
function isCompactionEvent(entry) {
|
|
222
210
|
const event = stringField(entry.hook_event_name) || stringField(entry.event) || stringField(entry.type);
|
|
223
211
|
if (event && /compact/i.test(event)) {
|
package/dist/transcript.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type DecisionState = "Healthy" | "Careful" | "Stop";
|
|
2
|
+
export type DecisionConfidence = "low" | "medium" | "high";
|
|
2
3
|
export interface StatusLineModel {
|
|
3
4
|
id?: string;
|
|
4
5
|
displayName?: string;
|
|
@@ -36,9 +37,13 @@ export interface TranscriptSummary {
|
|
|
36
37
|
linesRead: number;
|
|
37
38
|
malformedLines: number;
|
|
38
39
|
toolCalls: number;
|
|
40
|
+
readToolCalls: number;
|
|
39
41
|
failedToolResults: number;
|
|
40
42
|
repeatedFailures: ToolFailureSummary[];
|
|
41
43
|
editTestLoopFailures: number;
|
|
44
|
+
hasUnvalidatedEdits: boolean;
|
|
45
|
+
unvalidatedEditToolSteps?: number;
|
|
46
|
+
validationRecovered: boolean;
|
|
42
47
|
compactionEvents: number;
|
|
43
48
|
usage: TokenUsage;
|
|
44
49
|
latestTimestamp?: string;
|
|
@@ -60,9 +65,60 @@ export interface DecisionEvidence {
|
|
|
60
65
|
label: string;
|
|
61
66
|
detail?: string;
|
|
62
67
|
}
|
|
68
|
+
export interface BaselineScenarioSummary {
|
|
69
|
+
seen: number;
|
|
70
|
+
recentSeen?: number;
|
|
71
|
+
confidence?: DecisionConfidence;
|
|
72
|
+
}
|
|
73
|
+
export interface DecisionPersonalBaseline {
|
|
74
|
+
recent?: {
|
|
75
|
+
windowKind?: "newest_files";
|
|
76
|
+
windowSize?: number;
|
|
77
|
+
transcriptFilesScanned?: number;
|
|
78
|
+
sessionsSeen?: number;
|
|
79
|
+
};
|
|
80
|
+
scenarios?: Partial<Record<string, BaselineScenarioSummary>>;
|
|
81
|
+
outcomes?: {
|
|
82
|
+
healthyLike?: Partial<Record<string, number>>;
|
|
83
|
+
carefulLike?: Partial<Record<string, number>>;
|
|
84
|
+
stopLike?: Partial<Record<string, number>>;
|
|
85
|
+
};
|
|
86
|
+
rates?: Partial<Record<string, number>>;
|
|
87
|
+
validation?: Partial<Record<"tests" | "lint" | "typecheck" | "build", {
|
|
88
|
+
calls?: number;
|
|
89
|
+
failures?: number;
|
|
90
|
+
failureRate?: number;
|
|
91
|
+
recovered?: number;
|
|
92
|
+
unrecovered?: number;
|
|
93
|
+
recoveryRate?: number;
|
|
94
|
+
averageFailuresBeforeRecovery?: number;
|
|
95
|
+
medianFailuresBeforeRecovery?: number;
|
|
96
|
+
p75FailuresBeforeRecovery?: number;
|
|
97
|
+
fivePlusFailuresBeforeRecovery?: number;
|
|
98
|
+
}>>;
|
|
99
|
+
editValidation?: {
|
|
100
|
+
editsFollowedByValidation?: number;
|
|
101
|
+
editsWithoutValidation?: number;
|
|
102
|
+
editWithoutValidationRate?: number;
|
|
103
|
+
medianToolStepsFromEditToValidation?: number;
|
|
104
|
+
p75ToolStepsFromEditToValidation?: number;
|
|
105
|
+
};
|
|
106
|
+
toolCategories?: Partial<Record<string, {
|
|
107
|
+
calls?: number;
|
|
108
|
+
failures?: number;
|
|
109
|
+
repeatedFailureSessions?: number;
|
|
110
|
+
recovered?: number;
|
|
111
|
+
unrecovered?: number;
|
|
112
|
+
recoveryRate?: number;
|
|
113
|
+
}>>;
|
|
114
|
+
}
|
|
63
115
|
export interface Decision {
|
|
64
116
|
state: DecisionState;
|
|
65
117
|
reasonCode: string;
|
|
118
|
+
diagnosisCode?: string;
|
|
119
|
+
diagnosis?: string;
|
|
120
|
+
confidence?: DecisionConfidence;
|
|
121
|
+
baselineNote?: string;
|
|
66
122
|
primaryEvidence: string;
|
|
67
123
|
evidence: DecisionEvidence[];
|
|
68
124
|
impact: string;
|
package/dist/why.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Decision, StoredDecision } from "./types.js";
|
|
2
|
+
interface WhyOptions {
|
|
3
|
+
sessionId?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function getWhyDecision(options?: WhyOptions): Promise<StoredDecision | undefined>;
|
|
6
|
+
export declare function formatWhy(decision: Decision): string;
|
|
7
|
+
export {};
|
package/dist/why.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { latestDecisionForSession } from "./session.js";
|
|
2
|
+
import { latestDecision } from "./store.js";
|
|
3
|
+
export async function getWhyDecision(options = {}) {
|
|
4
|
+
return options.sessionId ? latestDecisionForSession(options.sessionId) : latestDecision();
|
|
5
|
+
}
|
|
6
|
+
export function formatWhy(decision) {
|
|
7
|
+
const cost = decision.costUsd === undefined
|
|
8
|
+
? ""
|
|
9
|
+
: `\nCost evidence: ${decision.costSource === "estimated" ? "estimated " : ""}$${decision.costUsd.toFixed(4)}.`;
|
|
10
|
+
const baseline = baselineWhyLine(decision);
|
|
11
|
+
return [
|
|
12
|
+
`Last decision: ${decision.state}.`,
|
|
13
|
+
`Reason: ${decision.primaryEvidence}. ${decision.impact}.`,
|
|
14
|
+
baseline,
|
|
15
|
+
`Next action: ${decision.action}.${cost}`
|
|
16
|
+
]
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.join("\n");
|
|
19
|
+
}
|
|
20
|
+
function baselineWhyLine(decision) {
|
|
21
|
+
if (!decision.baselineNote) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
if (decision.diagnosisCode === "read_heavy_debugging") {
|
|
25
|
+
return "Baseline: read-heavy sessions were usually Healthy-like for you.";
|
|
26
|
+
}
|
|
27
|
+
if (decision.baselineNote === "usually Stop-like for you") {
|
|
28
|
+
return "Baseline: this pattern was Stop-like in past sessions.";
|
|
29
|
+
}
|
|
30
|
+
return `Baseline: ${decision.baselineNote}.`;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=why.js.map
|
package/dist/why.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"why.js","sourceRoot":"","sources":["../src/why.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAO5C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAAsB,EAAE;IAC3D,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,wBAAwB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;AAC5F,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAkB;IAC1C,MAAM,IAAI,GACR,QAAQ,CAAC,OAAO,KAAK,SAAS;QAC5B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,oBAAoB,QAAQ,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,IAAI,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACpH,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,OAAO;QACL,kBAAkB,QAAQ,CAAC,KAAK,GAAG;QACnC,WAAW,QAAQ,CAAC,eAAe,KAAK,QAAQ,CAAC,MAAM,GAAG;QAC1D,QAAQ;QACR,gBAAgB,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE;KAC1C;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,QAAkB;IACzC,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,QAAQ,CAAC,aAAa,KAAK,sBAAsB,EAAE,CAAC;QACtD,OAAO,kEAAkE,CAAC;IAC5E,CAAC;IACD,IAAI,QAAQ,CAAC,YAAY,KAAK,2BAA2B,EAAE,CAAC;QAC1D,OAAO,wDAAwD,CAAC;IAClE,CAAC;IACD,OAAO,aAAa,QAAQ,CAAC,YAAY,GAAG,CAAC;AAC/C,CAAC"}
|