clud-bug 0.6.33 → 0.7.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/clud-bug.js +10 -1353
- package/dist/cli/agents-md.d.ts +16 -0
- package/dist/cli/agents-md.d.ts.map +1 -0
- package/dist/cli/agents-md.js +226 -0
- package/dist/cli/agents-md.js.map +1 -0
- package/dist/cli/audit.d.ts +13 -0
- package/dist/cli/audit.d.ts.map +1 -0
- package/dist/cli/audit.js +90 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/branch-protection.d.ts +57 -0
- package/dist/cli/branch-protection.d.ts.map +1 -0
- package/dist/cli/branch-protection.js +118 -0
- package/dist/cli/branch-protection.js.map +1 -0
- package/dist/cli/edit-workflow.d.ts +18 -0
- package/dist/cli/edit-workflow.d.ts.map +1 -0
- package/dist/cli/edit-workflow.js +43 -0
- package/dist/cli/edit-workflow.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +1336 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/skill-usage.d.ts +109 -0
- package/dist/cli/skill-usage.d.ts.map +1 -0
- package/dist/cli/skill-usage.js +380 -0
- package/dist/cli/skill-usage.js.map +1 -0
- package/dist/cli/skills.d.ts +56 -0
- package/dist/cli/skills.d.ts.map +1 -0
- package/dist/cli/skills.js +292 -0
- package/dist/cli/skills.js.map +1 -0
- package/dist/cli/update.d.ts +29 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/cli/update.js +186 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/usage.d.ts +142 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +348 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/core/audit.d.ts +8 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +47 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/detect.d.ts +77 -0
- package/dist/core/detect.d.ts.map +1 -0
- package/dist/core/detect.js +262 -0
- package/dist/core/detect.js.map +1 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/prompts.d.ts +9 -0
- package/dist/core/prompts.d.ts.map +1 -0
- package/dist/core/prompts.js +401 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/render-review.d.ts +6 -0
- package/dist/core/render-review.d.ts.map +1 -0
- package/dist/core/render-review.js +219 -0
- package/dist/core/render-review.js.map +1 -0
- package/dist/core/render.d.ts +13 -0
- package/dist/core/render.d.ts.map +1 -0
- package/dist/core/render.js +80 -0
- package/dist/core/render.js.map +1 -0
- package/dist/core/review-schema.d.ts +42 -0
- package/dist/core/review-schema.d.ts.map +1 -0
- package/dist/core/review-schema.js +156 -0
- package/dist/core/review-schema.js.map +1 -0
- package/dist/core/skills.d.ts +80 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +510 -0
- package/dist/core/skills.js.map +1 -0
- package/package.json +27 -4
- package/{lib/agents-md.js → src/cli/agents-md.ts} +25 -14
- package/{lib/audit.js → src/cli/audit.ts} +37 -44
- package/{lib/branch-protection.js → src/cli/branch-protection.ts} +75 -11
- package/{lib/edit-workflow.js → src/cli/edit-workflow.ts} +32 -11
- package/src/cli/index.ts +101 -0
- package/src/cli/main.ts +1376 -0
- package/{lib/skill-usage.js → src/cli/skill-usage.ts} +168 -94
- package/src/cli/skills.ts +386 -0
- package/{lib/update.js → src/cli/update.ts} +68 -27
- package/{lib/usage.js → src/cli/usage.ts} +167 -76
- package/src/core/audit.ts +53 -0
- package/{lib/detect.js → src/core/detect.ts} +100 -47
- package/src/core/index.ts +70 -0
- package/{lib/prompts.js → src/core/prompts.ts} +16 -2
- package/{lib/render-review.js → src/core/render-review.ts} +57 -25
- package/{lib/render.js → src/core/render.ts} +36 -10
- package/{lib/review-schema.js → src/core/review-schema.ts} +68 -5
- package/{lib/skills.js → src/core/skills.ts} +172 -343
- package/templates/workflow-py.yml.tmpl +2 -2
- package/templates/workflow-ts.yml.tmpl +2 -2
- package/templates/workflow.yml.tmpl +17 -8
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
export interface ModelPricing {
|
|
2
|
+
input: number;
|
|
3
|
+
output: number;
|
|
4
|
+
cacheRead: number;
|
|
5
|
+
cacheWrite: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const PRICING: Record<string, ModelPricing>;
|
|
8
|
+
export interface TokenCounts {
|
|
9
|
+
input_tokens?: number | undefined;
|
|
10
|
+
output_tokens?: number | undefined;
|
|
11
|
+
cache_read_input_tokens?: number | undefined;
|
|
12
|
+
cache_creation_input_tokens?: number | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface CostParts {
|
|
15
|
+
input: number;
|
|
16
|
+
output: number;
|
|
17
|
+
cacheRead: number;
|
|
18
|
+
cacheWrite: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ReviewCost {
|
|
21
|
+
total: number;
|
|
22
|
+
parts: CostParts;
|
|
23
|
+
model: string;
|
|
24
|
+
unknownModel: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Compute the USD cost of a single clud-bug review from token counts +
|
|
28
|
+
* model. All four token classes are billed independently.
|
|
29
|
+
*/
|
|
30
|
+
export declare function computeReviewCost(tokens: TokenCounts, model: string | null | undefined): ReviewCost;
|
|
31
|
+
/**
|
|
32
|
+
* $/LOC for a single review. PR size denominator is additions + deletions
|
|
33
|
+
* — the same metric `gh pr view --json additions,deletions` returns.
|
|
34
|
+
*
|
|
35
|
+
* Returns 0 if additions + deletions === 0 (avoid div-by-zero on
|
|
36
|
+
* docs-only / empty PRs); callers can filter zero-LOC reviews out of
|
|
37
|
+
* trend lines as outliers.
|
|
38
|
+
*/
|
|
39
|
+
export declare function costPerLOC(cost: number, additions: number | null | undefined, deletions: number | null | undefined): number;
|
|
40
|
+
/**
|
|
41
|
+
* Cache hit rate: cached_read / (cached_read + creation + input).
|
|
42
|
+
* Cached creation is the cost of WRITING new entries (paid 1.25× per
|
|
43
|
+
* Anthropic); cached read is what we get back at 10% of input price.
|
|
44
|
+
* High hit rate proves the v0.6.3 caching layer is firing on
|
|
45
|
+
* re-reviews and fix-pushes.
|
|
46
|
+
*/
|
|
47
|
+
export declare function cacheHitRate(tokens: TokenCounts): number;
|
|
48
|
+
export interface ExtractedTokens {
|
|
49
|
+
model: string | null;
|
|
50
|
+
tokens: Required<TokenCounts> | null;
|
|
51
|
+
ok: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse the model + token counts from a clud-bug-review job log dump.
|
|
55
|
+
*
|
|
56
|
+
* PR #104 fix (token double-count): the SDK's stream-output emits a
|
|
57
|
+
* `"type": "result"` event at the end of a review with a CUMULATIVE
|
|
58
|
+
* `usage` block. It ALSO emits per-turn `"type": "assistant"` events
|
|
59
|
+
* (each with its own usage), AND the result event's usage contains an
|
|
60
|
+
* `iterations` array of per-message breakdowns. Naively summing every
|
|
61
|
+
* `"input_tokens"` occurrence in the log would triple-or-more count
|
|
62
|
+
* the same tokens.
|
|
63
|
+
*
|
|
64
|
+
* Right approach: locate the FINAL `"type": "result"` event and extract
|
|
65
|
+
* the FIRST `"usage": {`-block within it. That's the cumulative bill,
|
|
66
|
+
* the same number Anthropic charges. If no result event exists, the
|
|
67
|
+
* review didn't complete successfully — return ok:false so the caller
|
|
68
|
+
* skips this run rather than trusting partial token data.
|
|
69
|
+
*/
|
|
70
|
+
export declare function extractTokensFromLog(logText: unknown): ExtractedTokens;
|
|
71
|
+
export interface ReviewRecord {
|
|
72
|
+
repo: string;
|
|
73
|
+
pr: number;
|
|
74
|
+
createdAt: string;
|
|
75
|
+
model: string;
|
|
76
|
+
tokens: TokenCounts;
|
|
77
|
+
additions: number;
|
|
78
|
+
deletions: number;
|
|
79
|
+
cost: number;
|
|
80
|
+
costPerLOC: number;
|
|
81
|
+
cacheRate: number;
|
|
82
|
+
unknownModel?: boolean | undefined;
|
|
83
|
+
modelObserved?: string | undefined;
|
|
84
|
+
}
|
|
85
|
+
export interface RollupGroupStats {
|
|
86
|
+
reviews: number;
|
|
87
|
+
cost: number;
|
|
88
|
+
loc: number;
|
|
89
|
+
costPerLOC: number;
|
|
90
|
+
cacheRate: number;
|
|
91
|
+
}
|
|
92
|
+
export interface RollupTotal {
|
|
93
|
+
reviews: number;
|
|
94
|
+
cost: number;
|
|
95
|
+
loc: number;
|
|
96
|
+
costPerLOC: number;
|
|
97
|
+
cacheRate: number;
|
|
98
|
+
}
|
|
99
|
+
export interface RollupTrend {
|
|
100
|
+
current: number;
|
|
101
|
+
previous: number | null;
|
|
102
|
+
slopePct: number | null;
|
|
103
|
+
}
|
|
104
|
+
export interface RollupOutlier {
|
|
105
|
+
repo: string;
|
|
106
|
+
pr: number;
|
|
107
|
+
costPerLOC: number;
|
|
108
|
+
multiple: number;
|
|
109
|
+
cost: number;
|
|
110
|
+
reason: string;
|
|
111
|
+
}
|
|
112
|
+
export interface UnknownModelReview {
|
|
113
|
+
repo: string;
|
|
114
|
+
pr: number;
|
|
115
|
+
modelObserved: string | undefined;
|
|
116
|
+
}
|
|
117
|
+
export interface Rollup {
|
|
118
|
+
total: RollupTotal;
|
|
119
|
+
perRepo: Record<string, RollupGroupStats>;
|
|
120
|
+
perModel: Record<string, RollupGroupStats>;
|
|
121
|
+
trend30d: RollupTrend;
|
|
122
|
+
outliers: RollupOutlier[];
|
|
123
|
+
unknownModelReviews: UnknownModelReview[];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Roll up an array of per-review records into a structured summary.
|
|
127
|
+
*
|
|
128
|
+
* Pre-conditions: callers should drop zero-LOC reviews before passing in.
|
|
129
|
+
*/
|
|
130
|
+
export declare function rollup(reviews: ReviewRecord[]): Rollup;
|
|
131
|
+
export interface FormatRollupOptions {
|
|
132
|
+
json?: boolean | undefined;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Render the rollup as a human-readable table. Mirrors the sample output
|
|
136
|
+
* from the Phase 0.5 plan.
|
|
137
|
+
*
|
|
138
|
+
* Pass `{ json: true }` for the machine-readable form (the same data
|
|
139
|
+
* the rollup() function returns).
|
|
140
|
+
*/
|
|
141
|
+
export declare function formatRollup(rollup: Rollup, opts?: FormatRollupOptions): string;
|
|
142
|
+
//# sourceMappingURL=usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
// src/cli/usage.ts — Q7-clud-bug $/LOC compute.
|
|
2
|
+
//
|
|
3
|
+
// Pure functions, no I/O. Driven from bin/clud-bug.js which fetches workflow
|
|
4
|
+
// run JSON + PR metadata via gh CLI. Implementation of the 0.0.M.1 dashboard
|
|
5
|
+
// per the Phase 0.5 plan.
|
|
6
|
+
//
|
|
7
|
+
// Reads:
|
|
8
|
+
// - clud-bug-review job logs (via `gh api .../jobs/<id>/logs`), which
|
|
9
|
+
// contain the SDK's `result` messages including:
|
|
10
|
+
// "model": "claude-sonnet-4-6"
|
|
11
|
+
// "input_tokens": N
|
|
12
|
+
// "output_tokens": N
|
|
13
|
+
// "cache_read_input_tokens": N
|
|
14
|
+
// "cache_creation_input_tokens": N
|
|
15
|
+
// - `gh pr view --json additions,deletions` for the LOC denominator.
|
|
16
|
+
//
|
|
17
|
+
// Computes:
|
|
18
|
+
// $/LOC = total_cost(tokens, model) / (additions + deletions)
|
|
19
|
+
//
|
|
20
|
+
// Q7-clud-bug enforcement: dashboard reports the 30-day rolling trend; the
|
|
21
|
+
// next Phase 0.5 PR ships when the trend stops declining.
|
|
22
|
+
// Anthropic pricing as of 2026-05 (per MTok). Cache write is 1.25× input
|
|
23
|
+
// per Anthropic's published 5-min-TTL ephemeral cache rate.
|
|
24
|
+
export const PRICING = {
|
|
25
|
+
'claude-sonnet-4-6': {
|
|
26
|
+
input: 3.0, output: 15.0, cacheRead: 0.30, cacheWrite: 3.75,
|
|
27
|
+
},
|
|
28
|
+
'claude-haiku-4-5-20251001': {
|
|
29
|
+
input: 0.80, output: 4.0, cacheRead: 0.08, cacheWrite: 1.0,
|
|
30
|
+
},
|
|
31
|
+
'claude-opus-4-7': {
|
|
32
|
+
input: 15.0, output: 75.0, cacheRead: 1.50, cacheWrite: 18.75,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
// Fallback when the model field is missing or new. Use Sonnet pricing —
|
|
36
|
+
// conservative for unknown-but-likely-Sonnet, undercounts Opus until we
|
|
37
|
+
// update the table. The `unknown` flag in the result lets callers warn.
|
|
38
|
+
const DEFAULT_MODEL = 'claude-sonnet-4-6';
|
|
39
|
+
/**
|
|
40
|
+
* Compute the USD cost of a single clud-bug review from token counts +
|
|
41
|
+
* model. All four token classes are billed independently.
|
|
42
|
+
*/
|
|
43
|
+
export function computeReviewCost(tokens, model) {
|
|
44
|
+
const t = {
|
|
45
|
+
input: tokens.input_tokens || 0,
|
|
46
|
+
output: tokens.output_tokens || 0,
|
|
47
|
+
cacheRead: tokens.cache_read_input_tokens || 0,
|
|
48
|
+
cacheWrite: tokens.cache_creation_input_tokens || 0,
|
|
49
|
+
};
|
|
50
|
+
const normalized = model && PRICING[model] ? model : DEFAULT_MODEL;
|
|
51
|
+
// PRICING[normalized] is guaranteed to exist (normalized is either a
|
|
52
|
+
// known key or DEFAULT_MODEL which is defined above). Non-null assert
|
|
53
|
+
// to satisfy noUncheckedIndexedAccess.
|
|
54
|
+
const p = PRICING[normalized];
|
|
55
|
+
const parts = {
|
|
56
|
+
input: (t.input / 1e6) * p.input,
|
|
57
|
+
output: (t.output / 1e6) * p.output,
|
|
58
|
+
cacheRead: (t.cacheRead / 1e6) * p.cacheRead,
|
|
59
|
+
cacheWrite: (t.cacheWrite / 1e6) * p.cacheWrite,
|
|
60
|
+
};
|
|
61
|
+
const total = parts.input + parts.output + parts.cacheRead + parts.cacheWrite;
|
|
62
|
+
return {
|
|
63
|
+
total,
|
|
64
|
+
parts,
|
|
65
|
+
model: normalized,
|
|
66
|
+
unknownModel: !(model && PRICING[model]),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* $/LOC for a single review. PR size denominator is additions + deletions
|
|
71
|
+
* — the same metric `gh pr view --json additions,deletions` returns.
|
|
72
|
+
*
|
|
73
|
+
* Returns 0 if additions + deletions === 0 (avoid div-by-zero on
|
|
74
|
+
* docs-only / empty PRs); callers can filter zero-LOC reviews out of
|
|
75
|
+
* trend lines as outliers.
|
|
76
|
+
*/
|
|
77
|
+
export function costPerLOC(cost, additions, deletions) {
|
|
78
|
+
const loc = (additions || 0) + (deletions || 0);
|
|
79
|
+
if (loc === 0)
|
|
80
|
+
return 0;
|
|
81
|
+
return cost / loc;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Cache hit rate: cached_read / (cached_read + creation + input).
|
|
85
|
+
* Cached creation is the cost of WRITING new entries (paid 1.25× per
|
|
86
|
+
* Anthropic); cached read is what we get back at 10% of input price.
|
|
87
|
+
* High hit rate proves the v0.6.3 caching layer is firing on
|
|
88
|
+
* re-reviews and fix-pushes.
|
|
89
|
+
*/
|
|
90
|
+
export function cacheHitRate(tokens) {
|
|
91
|
+
const read = tokens.cache_read_input_tokens || 0;
|
|
92
|
+
const write = tokens.cache_creation_input_tokens || 0;
|
|
93
|
+
const input = tokens.input_tokens || 0;
|
|
94
|
+
const denom = read + write + input;
|
|
95
|
+
if (denom === 0)
|
|
96
|
+
return 0;
|
|
97
|
+
return read / denom;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Parse the model + token counts from a clud-bug-review job log dump.
|
|
101
|
+
*
|
|
102
|
+
* PR #104 fix (token double-count): the SDK's stream-output emits a
|
|
103
|
+
* `"type": "result"` event at the end of a review with a CUMULATIVE
|
|
104
|
+
* `usage` block. It ALSO emits per-turn `"type": "assistant"` events
|
|
105
|
+
* (each with its own usage), AND the result event's usage contains an
|
|
106
|
+
* `iterations` array of per-message breakdowns. Naively summing every
|
|
107
|
+
* `"input_tokens"` occurrence in the log would triple-or-more count
|
|
108
|
+
* the same tokens.
|
|
109
|
+
*
|
|
110
|
+
* Right approach: locate the FINAL `"type": "result"` event and extract
|
|
111
|
+
* the FIRST `"usage": {`-block within it. That's the cumulative bill,
|
|
112
|
+
* the same number Anthropic charges. If no result event exists, the
|
|
113
|
+
* review didn't complete successfully — return ok:false so the caller
|
|
114
|
+
* skips this run rather than trusting partial token data.
|
|
115
|
+
*/
|
|
116
|
+
export function extractTokensFromLog(logText) {
|
|
117
|
+
if (typeof logText !== 'string' || logText.length === 0) {
|
|
118
|
+
return { model: null, tokens: null, ok: false };
|
|
119
|
+
}
|
|
120
|
+
// The LAST model field — final message wins (a multi-turn review
|
|
121
|
+
// uses the same model throughout). Captured before the usage parse
|
|
122
|
+
// so model is reported even when we can't find a result event.
|
|
123
|
+
const modelMatches = [...logText.matchAll(/"model"\s*:\s*"([^"]+)"/g)];
|
|
124
|
+
// matchAll yields RegExpMatchArray entries; capture group 1 is the
|
|
125
|
+
// model string. Under noUncheckedIndexedAccess every index is typed
|
|
126
|
+
// possibly-undefined — coalesce so the return type stays string|null.
|
|
127
|
+
const model = modelMatches.length > 0
|
|
128
|
+
? (modelMatches[modelMatches.length - 1]?.[1] ?? null)
|
|
129
|
+
: null;
|
|
130
|
+
// Locate the final result event. There may be multiple over the
|
|
131
|
+
// life of a long-running session — take the LAST one.
|
|
132
|
+
const resultMarkerRe = /"type"\s*:\s*"result"/g;
|
|
133
|
+
let lastResultIdx = -1;
|
|
134
|
+
let m;
|
|
135
|
+
while ((m = resultMarkerRe.exec(logText)) !== null) {
|
|
136
|
+
lastResultIdx = m.index;
|
|
137
|
+
}
|
|
138
|
+
if (lastResultIdx < 0) {
|
|
139
|
+
// No result event — partial log or job that errored before
|
|
140
|
+
// emitting one. Don't sum the per-turn fields; the data isn't
|
|
141
|
+
// billable-equivalent.
|
|
142
|
+
return { model, tokens: null, ok: false };
|
|
143
|
+
}
|
|
144
|
+
// Within the result event, find the first `"usage": {` and extract
|
|
145
|
+
// its top-level token fields. We scope each field's regex to a window
|
|
146
|
+
// starting at the usage block so we don't pick up the `iterations`
|
|
147
|
+
// array's per-message fields (which are nested deeper but still
|
|
148
|
+
// appear within the same overall result block).
|
|
149
|
+
const fromResult = logText.slice(lastResultIdx);
|
|
150
|
+
const usageIdx = fromResult.search(/"usage"\s*:\s*\{/);
|
|
151
|
+
if (usageIdx < 0) {
|
|
152
|
+
return { model, tokens: null, ok: false };
|
|
153
|
+
}
|
|
154
|
+
// Slice up to the start of `"iterations"` (if present) so we don't
|
|
155
|
+
// double-count per-iteration breakdowns nested inside usage.
|
|
156
|
+
const fromUsage = fromResult.slice(usageIdx);
|
|
157
|
+
const iterationsIdx = fromUsage.search(/"iterations"\s*:/);
|
|
158
|
+
const usageOnly = iterationsIdx >= 0
|
|
159
|
+
? fromUsage.slice(0, iterationsIdx)
|
|
160
|
+
: fromUsage;
|
|
161
|
+
const pluck = (re) => {
|
|
162
|
+
const match = usageOnly.match(re);
|
|
163
|
+
return match && match[1] !== undefined ? Number(match[1]) : 0;
|
|
164
|
+
};
|
|
165
|
+
const input = pluck(/"input_tokens"\s*:\s*(\d+)/);
|
|
166
|
+
const output = pluck(/"output_tokens"\s*:\s*(\d+)/);
|
|
167
|
+
const cacheRead = pluck(/"cache_read_input_tokens"\s*:\s*(\d+)/);
|
|
168
|
+
const cacheWrite = pluck(/"cache_creation_input_tokens"\s*:\s*(\d+)/);
|
|
169
|
+
const anyTokens = input + output + cacheRead + cacheWrite;
|
|
170
|
+
if (anyTokens === 0) {
|
|
171
|
+
return { model, tokens: null, ok: false };
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
model,
|
|
175
|
+
tokens: {
|
|
176
|
+
input_tokens: input,
|
|
177
|
+
output_tokens: output,
|
|
178
|
+
cache_read_input_tokens: cacheRead,
|
|
179
|
+
cache_creation_input_tokens: cacheWrite,
|
|
180
|
+
},
|
|
181
|
+
ok: true,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Roll up an array of per-review records into a structured summary.
|
|
186
|
+
*
|
|
187
|
+
* Pre-conditions: callers should drop zero-LOC reviews before passing in.
|
|
188
|
+
*/
|
|
189
|
+
export function rollup(reviews) {
|
|
190
|
+
const valid = reviews.filter((r) => r.costPerLOC > 0);
|
|
191
|
+
const total = {
|
|
192
|
+
reviews: valid.length,
|
|
193
|
+
cost: valid.reduce((a, r) => a + r.cost, 0),
|
|
194
|
+
loc: valid.reduce((a, r) => a + (r.additions + r.deletions), 0),
|
|
195
|
+
costPerLOC: median(valid.map((r) => r.costPerLOC)),
|
|
196
|
+
cacheRate: median(valid.map((r) => r.cacheRate)),
|
|
197
|
+
};
|
|
198
|
+
const groupBy = (key) => {
|
|
199
|
+
const out = {};
|
|
200
|
+
for (const r of valid) {
|
|
201
|
+
const k = String(r[key]);
|
|
202
|
+
let bucket = out[k];
|
|
203
|
+
if (!bucket) {
|
|
204
|
+
bucket = { reviews: 0, cost: 0, loc: 0, costPerLOCs: [], cacheRates: [] };
|
|
205
|
+
out[k] = bucket;
|
|
206
|
+
}
|
|
207
|
+
bucket.reviews += 1;
|
|
208
|
+
bucket.cost += r.cost;
|
|
209
|
+
bucket.loc += r.additions + r.deletions;
|
|
210
|
+
bucket.costPerLOCs.push(r.costPerLOC);
|
|
211
|
+
bucket.cacheRates.push(r.cacheRate);
|
|
212
|
+
}
|
|
213
|
+
const finalized = {};
|
|
214
|
+
for (const k of Object.keys(out)) {
|
|
215
|
+
const bucket = out[k];
|
|
216
|
+
finalized[k] = {
|
|
217
|
+
reviews: bucket.reviews,
|
|
218
|
+
cost: bucket.cost,
|
|
219
|
+
loc: bucket.loc,
|
|
220
|
+
costPerLOC: median(bucket.costPerLOCs),
|
|
221
|
+
cacheRate: median(bucket.cacheRates),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return finalized;
|
|
225
|
+
};
|
|
226
|
+
const perRepo = groupBy('repo');
|
|
227
|
+
const perModel = groupBy('model');
|
|
228
|
+
// Outliers: > 2× total.costPerLOC.
|
|
229
|
+
const outliers = valid
|
|
230
|
+
.filter((r) => r.costPerLOC > total.costPerLOC * 2)
|
|
231
|
+
.map((r) => ({
|
|
232
|
+
repo: r.repo,
|
|
233
|
+
pr: r.pr,
|
|
234
|
+
costPerLOC: r.costPerLOC,
|
|
235
|
+
multiple: r.costPerLOC / total.costPerLOC,
|
|
236
|
+
cost: r.cost,
|
|
237
|
+
reason: r.cacheRate < 0.3 ? 'low cache hit' : 'unknown',
|
|
238
|
+
}));
|
|
239
|
+
// 30-day trend: median $/LOC per calendar day. Bucket by createdAt date.
|
|
240
|
+
// Slope reported as MoM % change between the most recent 30-day window's
|
|
241
|
+
// median and the previous 30-day window's median.
|
|
242
|
+
const trend30d = computeTrend(valid);
|
|
243
|
+
// PR #104 fix: surface reviews whose model wasn't in PRICING. The
|
|
244
|
+
// computeReviewCost fallback applied Sonnet rates to unknown models
|
|
245
|
+
// (~5× undercount of Opus), AND bucketed them under Sonnet in the
|
|
246
|
+
// per-model table — exactly the false-good signal Q7 must NOT produce.
|
|
247
|
+
// Caller renders this as a loud warning so the dashboard reader knows
|
|
248
|
+
// to update the PRICING table.
|
|
249
|
+
const unknownModelReviews = valid
|
|
250
|
+
.filter((r) => r.unknownModel === true)
|
|
251
|
+
.map((r) => ({ repo: r.repo, pr: r.pr, modelObserved: r.modelObserved }));
|
|
252
|
+
return { total, perRepo, perModel, trend30d, outliers, unknownModelReviews };
|
|
253
|
+
}
|
|
254
|
+
function median(nums) {
|
|
255
|
+
if (nums.length === 0)
|
|
256
|
+
return 0;
|
|
257
|
+
const sorted = [...nums].sort((a, b) => a - b);
|
|
258
|
+
const mid = Math.floor(sorted.length / 2);
|
|
259
|
+
// sorted is non-empty here so sorted[mid] is defined; the `!`
|
|
260
|
+
// satisfies noUncheckedIndexedAccess and matches JS semantics.
|
|
261
|
+
return sorted.length % 2 === 0
|
|
262
|
+
? (sorted[mid - 1] + sorted[mid]) / 2
|
|
263
|
+
: sorted[mid];
|
|
264
|
+
}
|
|
265
|
+
function computeTrend(reviews) {
|
|
266
|
+
// PR #104 fix: distinguish "no prior window" (previous bucket empty)
|
|
267
|
+
// from "exactly flat trend" (current === previous > 0). The original
|
|
268
|
+
// code returned slopePct=0 for both, which masked the dangerous case
|
|
269
|
+
// — a stable expensive month-over-month trend rendered as if there
|
|
270
|
+
// were no comparison data, hiding the very signal Q7 enforces.
|
|
271
|
+
// `previous: null` now means "no prior window"; renderer keys on this.
|
|
272
|
+
if (reviews.length === 0) {
|
|
273
|
+
return { current: 0, previous: null, slopePct: null };
|
|
274
|
+
}
|
|
275
|
+
const now = Date.now();
|
|
276
|
+
const day = 24 * 60 * 60 * 1000;
|
|
277
|
+
const currentWindow = reviews.filter((r) => now - new Date(r.createdAt).getTime() <= 30 * day);
|
|
278
|
+
const previousWindow = reviews.filter((r) => {
|
|
279
|
+
const age = now - new Date(r.createdAt).getTime();
|
|
280
|
+
return age > 30 * day && age <= 60 * day;
|
|
281
|
+
});
|
|
282
|
+
const current = median(currentWindow.map((r) => r.costPerLOC));
|
|
283
|
+
if (previousWindow.length === 0) {
|
|
284
|
+
return { current, previous: null, slopePct: null };
|
|
285
|
+
}
|
|
286
|
+
const previous = median(previousWindow.map((r) => r.costPerLOC));
|
|
287
|
+
// previous > 0 because every review in valid[] has costPerLOC > 0
|
|
288
|
+
// (zero-LOC reviews dropped upstream).
|
|
289
|
+
const slopePct = previous > 0 ? ((current - previous) / previous) * 100 : null;
|
|
290
|
+
return { current, previous, slopePct };
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Render the rollup as a human-readable table. Mirrors the sample output
|
|
294
|
+
* from the Phase 0.5 plan.
|
|
295
|
+
*
|
|
296
|
+
* Pass `{ json: true }` for the machine-readable form (the same data
|
|
297
|
+
* the rollup() function returns).
|
|
298
|
+
*/
|
|
299
|
+
export function formatRollup(rollup, opts = {}) {
|
|
300
|
+
if (opts.json) {
|
|
301
|
+
return JSON.stringify(rollup, null, 2);
|
|
302
|
+
}
|
|
303
|
+
const lines = [];
|
|
304
|
+
const t = rollup.total;
|
|
305
|
+
const trend = rollup.trend30d;
|
|
306
|
+
// PR #104 fix: null slopePct = "no prior window" (prior 30d bucket
|
|
307
|
+
// was empty). A REAL 0% slope (flat trend) renders as "→ 0% MoM",
|
|
308
|
+
// not as "(no prior window)" — masking the latter was the bug.
|
|
309
|
+
let trendStr;
|
|
310
|
+
if (trend.slopePct === null) {
|
|
311
|
+
trendStr = '(no prior window)';
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
const trendArrow = trend.slopePct < 0 ? '↓' : trend.slopePct > 0 ? '↑' : '→';
|
|
315
|
+
trendStr = `${trendArrow} ${trend.slopePct.toFixed(0)}% MoM`;
|
|
316
|
+
}
|
|
317
|
+
lines.push(`ok: ${t.reviews} reviews, 30-day $/LOC trend: ${trendStr}`);
|
|
318
|
+
const perRepoEntries = Object.entries(rollup.perRepo)
|
|
319
|
+
.sort((a, b) => b[1].costPerLOC - a[1].costPerLOC);
|
|
320
|
+
if (perRepoEntries.length > 0) {
|
|
321
|
+
lines.push(' per-repo $/LOC (most → least expensive):');
|
|
322
|
+
for (const [repo, stats] of perRepoEntries) {
|
|
323
|
+
const cache = `${(stats.cacheRate * 100).toFixed(0)}% cached`;
|
|
324
|
+
lines.push(` ${repo.padEnd(28)} ${`$${stats.costPerLOC.toFixed(4)}/LOC`.padEnd(16)} · ${String(stats.reviews).padStart(2)} reviews · ${cache}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
lines.push(` org median $/LOC: $${t.costPerLOC.toFixed(4)} · org cache hit: ${(t.cacheRate * 100).toFixed(0)}%`);
|
|
328
|
+
lines.push(` total spend: $${t.cost.toFixed(2)} across ${(t.loc).toLocaleString()} LOC`);
|
|
329
|
+
if (rollup.outliers.length > 0) {
|
|
330
|
+
lines.push(` outliers (>2× median):`);
|
|
331
|
+
for (const o of rollup.outliers) {
|
|
332
|
+
lines.push(` ${o.repo}#${o.pr} ($${o.costPerLOC.toFixed(4)}/LOC, ${o.multiple.toFixed(1)}× median — ${o.reason})`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// PR #104 fix: loud warning when one or more reviews used a model
|
|
336
|
+
// not in PRICING (we fell back to Sonnet rates — that can undercount
|
|
337
|
+
// by ~5× if the real model was an Opus variant). Update PRICING and
|
|
338
|
+
// re-run.
|
|
339
|
+
if (rollup.unknownModelReviews && rollup.unknownModelReviews.length > 0) {
|
|
340
|
+
lines.push(` ⚠️ ${rollup.unknownModelReviews.length} review${rollup.unknownModelReviews.length === 1 ? '' : 's'} used model${rollup.unknownModelReviews.length === 1 ? '' : 's'} not in PRICING; cost may be undercounted:`);
|
|
341
|
+
const observed = new Set(rollup.unknownModelReviews.map((u) => u.modelObserved));
|
|
342
|
+
for (const m of observed) {
|
|
343
|
+
lines.push(` seen: "${m}" — add to lib/usage.js PRICING table`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return lines.join('\n') + '\n';
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=usage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/cli/usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function durationToGitSince(input: string | null | undefined): string | null;
|
|
2
|
+
export interface AuditHeaderInput {
|
|
3
|
+
date: string;
|
|
4
|
+
scopeLabel: string;
|
|
5
|
+
files: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function renderAuditHeader({ date, scopeLabel, files }: AuditHeaderInput): string;
|
|
8
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/core/audit.ts"],"names":[],"mappings":"AAUA,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAWlF;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAID,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,gBAAgB,GAAG,MAAM,CAqBvF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Pure audit helpers — no FS, no git, no child_process.
|
|
2
|
+
//
|
|
3
|
+
// Split from lib/audit.js during the v0.7.0 TS migration: durationToGitSince
|
|
4
|
+
// and renderAuditHeader are pure functions safe to consume from any runtime
|
|
5
|
+
// (e.g. clud-bug-app's serverless review path), so they live in src/core/.
|
|
6
|
+
// The git-spawning siblings (gitLines, computeAuditFileSet) live in
|
|
7
|
+
// src/cli/audit.ts.
|
|
8
|
+
// Convert a duration like "7d", "2w", "1mo", "3mo", "1y" to a git --since arg.
|
|
9
|
+
// Returns null if the input is empty/undefined; throws on malformed input.
|
|
10
|
+
export function durationToGitSince(input) {
|
|
11
|
+
if (!input)
|
|
12
|
+
return null;
|
|
13
|
+
const m = String(input).trim().match(/^(\d+)\s*(d|w|mo|m|y)$/i);
|
|
14
|
+
if (!m) {
|
|
15
|
+
throw new Error(`Unrecognized duration "${input}". Examples: 7d, 2w, 1mo, 1y.`);
|
|
16
|
+
}
|
|
17
|
+
// Capture group 1 (\d+) is always present when m is truthy; same for group 2.
|
|
18
|
+
const n = Number(m[1]);
|
|
19
|
+
const unit = m[2].toLowerCase();
|
|
20
|
+
const map = { d: 'day', w: 'week', mo: 'month', m: 'month', y: 'year' };
|
|
21
|
+
return `${n} ${map[unit]}${n === 1 ? '' : 's'} ago`;
|
|
22
|
+
}
|
|
23
|
+
// Render the audit report's initial markdown body. The Action's Claude run
|
|
24
|
+
// will append findings under a "## Findings" section after this header.
|
|
25
|
+
export function renderAuditHeader({ date, scopeLabel, files }) {
|
|
26
|
+
const head = `# 🐛 Clud Bug audit — ${date}
|
|
27
|
+
|
|
28
|
+
A scheduled walk through the habitat. Scope: ${scopeLabel}.
|
|
29
|
+
Files surveyed: **${files.length}**.
|
|
30
|
+
|
|
31
|
+
<details>
|
|
32
|
+
<summary>File manifest (${files.length})</summary>
|
|
33
|
+
|
|
34
|
+
\`\`\`
|
|
35
|
+
${files.join('\n')}
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
</details>
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Findings
|
|
43
|
+
|
|
44
|
+
`;
|
|
45
|
+
return head;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/core/audit.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,2EAA2E;AAC3E,oEAAoE;AACpE,oBAAoB;AAEpB,+EAA+E;AAC/E,2EAA2E;AAC3E,MAAM,UAAU,kBAAkB,CAAC,KAAgC;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAChE,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,+BAA+B,CAAC,CAAC;IAClF,CAAC;IACD,8EAA8E;IAC9E,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,MAAM,IAAI,GAAI,CAAC,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE,CAAC;IAC5C,MAAM,GAAG,GAA2B,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IAChG,OAAO,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;AACtD,CAAC;AAQD,2EAA2E;AAC3E,wEAAwE;AACxE,MAAM,UAAU,iBAAiB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAoB;IAC7E,MAAM,IAAI,GAAG,yBAAyB,IAAI;;+CAEG,UAAU;oBACrC,KAAK,CAAC,MAAM;;;0BAGN,KAAK,CAAC,MAAM;;;EAGpC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;CASjB,CAAC;IACA,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export declare const EXT_TO_LANG: {
|
|
2
|
+
readonly '.ts': "typescript";
|
|
3
|
+
readonly '.tsx': "typescript";
|
|
4
|
+
readonly '.js': "javascript";
|
|
5
|
+
readonly '.jsx': "javascript";
|
|
6
|
+
readonly '.mjs': "javascript";
|
|
7
|
+
readonly '.cjs': "javascript";
|
|
8
|
+
readonly '.py': "python";
|
|
9
|
+
readonly '.go': "go";
|
|
10
|
+
readonly '.rs': "rust";
|
|
11
|
+
readonly '.rb': "ruby";
|
|
12
|
+
readonly '.java': "java";
|
|
13
|
+
readonly '.kt': "kotlin";
|
|
14
|
+
readonly '.swift': "swift";
|
|
15
|
+
readonly '.php': "php";
|
|
16
|
+
readonly '.cs': "csharp";
|
|
17
|
+
readonly '.c': "c";
|
|
18
|
+
readonly '.h': "c";
|
|
19
|
+
readonly '.cpp': "cpp";
|
|
20
|
+
readonly '.cc': "cpp";
|
|
21
|
+
readonly '.hpp': "cpp";
|
|
22
|
+
};
|
|
23
|
+
export declare const DEP_TO_TERM: {
|
|
24
|
+
readonly next: "nextjs";
|
|
25
|
+
readonly react: "react";
|
|
26
|
+
readonly vue: "vue";
|
|
27
|
+
readonly svelte: "svelte";
|
|
28
|
+
readonly '@angular/core': "angular";
|
|
29
|
+
readonly 'solid-js': "solid";
|
|
30
|
+
readonly express: "express";
|
|
31
|
+
readonly fastify: "fastify";
|
|
32
|
+
readonly koa: "koa";
|
|
33
|
+
readonly hono: "hono";
|
|
34
|
+
readonly prisma: "prisma";
|
|
35
|
+
readonly '@prisma/client': "prisma";
|
|
36
|
+
readonly 'drizzle-orm': "drizzle";
|
|
37
|
+
readonly mongoose: "mongodb";
|
|
38
|
+
readonly mongodb: "mongodb";
|
|
39
|
+
readonly tailwindcss: "tailwind";
|
|
40
|
+
readonly vitest: "vitest";
|
|
41
|
+
readonly jest: "jest";
|
|
42
|
+
readonly playwright: "playwright";
|
|
43
|
+
readonly '@playwright/test': "playwright";
|
|
44
|
+
readonly typescript: "typescript";
|
|
45
|
+
};
|
|
46
|
+
export declare const PY_DEP_TO_TERM: {
|
|
47
|
+
readonly django: "django";
|
|
48
|
+
readonly flask: "flask";
|
|
49
|
+
readonly fastapi: "fastapi";
|
|
50
|
+
readonly click: "click";
|
|
51
|
+
readonly typer: "typer";
|
|
52
|
+
readonly pytest: "pytest";
|
|
53
|
+
readonly sqlalchemy: "sqlalchemy";
|
|
54
|
+
readonly pydantic: "pydantic";
|
|
55
|
+
readonly numpy: "numpy";
|
|
56
|
+
readonly pandas: "pandas";
|
|
57
|
+
};
|
|
58
|
+
export interface DetectedSignals {
|
|
59
|
+
name: string | null;
|
|
60
|
+
description: string | null;
|
|
61
|
+
languages: string[];
|
|
62
|
+
histogram: Record<string, number>;
|
|
63
|
+
searchTerms: string[];
|
|
64
|
+
primaryLanguage: string | null;
|
|
65
|
+
}
|
|
66
|
+
declare function fileHistogram(root: string): Promise<Record<string, number>>;
|
|
67
|
+
declare function firstParagraph(readme: string | null): string | null;
|
|
68
|
+
export declare function detect(root: string): Promise<DetectedSignals>;
|
|
69
|
+
export interface DescriptionLineSignals {
|
|
70
|
+
name?: string | null;
|
|
71
|
+
description?: string | null;
|
|
72
|
+
primaryLanguage?: string | null;
|
|
73
|
+
searchTerms?: string[];
|
|
74
|
+
}
|
|
75
|
+
export declare function buildDescriptionLine(signals: DescriptionLineSignals): string;
|
|
76
|
+
export { fileHistogram, firstParagraph };
|
|
77
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/core/detect.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;CAamB,CAAC;AAK5C,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;CAUmB,CAAC;AAE5C,eAAO,MAAM,cAAc;;;;;;;;;;;CAKgB,CAAC;AAK5C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AA2HD,iBAAe,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAqB1E;AAED,iBAAS,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAS5D;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAoCnE;AAKD,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAuB5E;AASD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC"}
|