@quickchatai/pi-quickreview 1.2.0 → 1.3.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 +79 -27
- package/assets/config.example.toml +25 -7
- package/assets/prompts/onboarding.md +8 -7
- package/assets/schemas/review-result.v1.json +60 -0
- package/dist/extensions/quickreview.d.ts +17 -5
- package/dist/extensions/quickreview.d.ts.map +1 -1
- package/dist/extensions/quickreview.js +79 -26
- package/dist/extensions/quickreview.js.map +1 -1
- package/dist/src/engine/auth.d.ts +4 -0
- package/dist/src/engine/auth.d.ts.map +1 -1
- package/dist/src/engine/auth.js +11 -2
- package/dist/src/engine/auth.js.map +1 -1
- package/dist/src/engine/lens/activity.d.ts +45 -0
- package/dist/src/engine/lens/activity.d.ts.map +1 -0
- package/dist/src/engine/lens/activity.js +118 -0
- package/dist/src/engine/lens/activity.js.map +1 -0
- package/dist/src/engine/lens/runlens.d.ts +7 -0
- package/dist/src/engine/lens/runlens.d.ts.map +1 -1
- package/dist/src/engine/lens/runlens.js +30 -1
- package/dist/src/engine/lens/runlens.js.map +1 -1
- package/dist/src/engine/lens/tool.d.ts +7 -0
- package/dist/src/engine/lens/tool.d.ts.map +1 -1
- package/dist/src/engine/lens/tool.js +101 -18
- package/dist/src/engine/lens/tool.js.map +1 -1
- package/dist/src/engine/lens/wave.d.ts +6 -0
- package/dist/src/engine/lens/wave.d.ts.map +1 -1
- package/dist/src/engine/lens/wave.js +16 -0
- package/dist/src/engine/lens/wave.js.map +1 -1
- package/dist/src/homes.js +1 -1
- package/dist/src/homes.js.map +1 -1
- package/dist/src/live/events.d.ts +3 -0
- package/dist/src/live/events.d.ts.map +1 -1
- package/dist/src/live/events.js.map +1 -1
- package/dist/src/live/lensline.d.ts +20 -0
- package/dist/src/live/lensline.d.ts.map +1 -0
- package/dist/src/live/lensline.js +70 -0
- package/dist/src/live/lensline.js.map +1 -0
- package/dist/src/live/projection.d.ts +55 -0
- package/dist/src/live/projection.d.ts.map +1 -0
- package/dist/src/live/projection.js +465 -0
- package/dist/src/live/projection.js.map +1 -0
- package/dist/src/live/read.d.ts +24 -0
- package/dist/src/live/read.d.ts.map +1 -0
- package/dist/src/live/read.js +60 -0
- package/dist/src/live/read.js.map +1 -0
- package/dist/src/live/writer.d.ts +3 -0
- package/dist/src/live/writer.d.ts.map +1 -1
- package/dist/src/live/writer.js +6 -1
- package/dist/src/live/writer.js.map +1 -1
- package/dist/src/localreview.d.ts +18 -8
- package/dist/src/localreview.d.ts.map +1 -1
- package/dist/src/localreview.js +103 -43
- package/dist/src/localreview.js.map +1 -1
- package/dist/src/modelroute.d.ts +4 -0
- package/dist/src/modelroute.d.ts.map +1 -1
- package/dist/src/modelroute.js +21 -0
- package/dist/src/modelroute.js.map +1 -1
- package/dist/src/plugin/args.d.ts +38 -7
- package/dist/src/plugin/args.d.ts.map +1 -1
- package/dist/src/plugin/args.js +134 -37
- package/dist/src/plugin/args.js.map +1 -1
- package/dist/src/plugin/index.d.ts.map +1 -1
- package/dist/src/plugin/index.js +5 -1
- package/dist/src/plugin/index.js.map +1 -1
- package/dist/src/plugin/login.d.ts +8 -4
- package/dist/src/plugin/login.d.ts.map +1 -1
- package/dist/src/plugin/login.js +25 -32
- package/dist/src/plugin/login.js.map +1 -1
- package/dist/src/plugin/messages.d.ts +7 -0
- package/dist/src/plugin/messages.d.ts.map +1 -0
- package/dist/src/plugin/messages.js +13 -0
- package/dist/src/plugin/messages.js.map +1 -0
- package/dist/src/plugin/peers.d.ts +17 -0
- package/dist/src/plugin/peers.d.ts.map +1 -0
- package/dist/src/plugin/peers.js +55 -0
- package/dist/src/plugin/peers.js.map +1 -0
- package/dist/src/plugin/review.d.ts +52 -6
- package/dist/src/plugin/review.d.ts.map +1 -1
- package/dist/src/plugin/review.js +261 -95
- package/dist/src/plugin/review.js.map +1 -1
- package/dist/src/plugin/scan.d.ts +80 -44
- package/dist/src/plugin/scan.d.ts.map +1 -1
- package/dist/src/plugin/scan.js +450 -233
- package/dist/src/plugin/scan.js.map +1 -1
- package/dist/src/plugin/scancases.d.ts +33 -0
- package/dist/src/plugin/scancases.d.ts.map +1 -0
- package/dist/src/plugin/scancases.js +153 -0
- package/dist/src/plugin/scancases.js.map +1 -0
- package/dist/src/plugin/settings.d.ts +18 -0
- package/dist/src/plugin/settings.d.ts.map +1 -0
- package/dist/src/plugin/settings.js +47 -0
- package/dist/src/plugin/settings.js.map +1 -0
- package/dist/src/plugin/setup.d.ts +35 -12
- package/dist/src/plugin/setup.d.ts.map +1 -1
- package/dist/src/plugin/setup.js +258 -118
- package/dist/src/plugin/setup.js.map +1 -1
- package/dist/src/plugin/versiongate.d.ts +3 -0
- package/dist/src/plugin/versiongate.d.ts.map +1 -1
- package/dist/src/plugin/versiongate.js +9 -4
- package/dist/src/plugin/versiongate.js.map +1 -1
- package/dist/src/profileoverrides.d.ts +3 -0
- package/dist/src/profileoverrides.d.ts.map +1 -1
- package/dist/src/profileoverrides.js +9 -2
- package/dist/src/profileoverrides.js.map +1 -1
- package/dist/src/reviewapi.d.ts +22 -31
- package/dist/src/reviewapi.d.ts.map +1 -1
- package/dist/src/reviewapi.js +90 -66
- package/dist/src/reviewapi.js.map +1 -1
- package/dist/src/reviewdest.d.ts +1 -1
- package/dist/src/reviewdest.d.ts.map +1 -1
- package/dist/src/reviewdest.js +4 -3
- package/dist/src/reviewdest.js.map +1 -1
- package/dist/src/reviewtarget.d.ts +36 -0
- package/dist/src/reviewtarget.d.ts.map +1 -0
- package/dist/src/reviewtarget.js +6 -0
- package/dist/src/reviewtarget.js.map +1 -0
- package/dist/src/round-progress.d.ts +5 -0
- package/dist/src/round-progress.d.ts.map +1 -1
- package/dist/src/round-progress.js.map +1 -1
- package/dist/src/round-stage-deadlines.d.ts +26 -1
- package/dist/src/round-stage-deadlines.d.ts.map +1 -1
- package/dist/src/round-stage-deadlines.js +45 -2
- package/dist/src/round-stage-deadlines.js.map +1 -1
- package/dist/src/savedpost.d.ts +65 -0
- package/dist/src/savedpost.d.ts.map +1 -0
- package/dist/src/savedpost.js +131 -0
- package/dist/src/savedpost.js.map +1 -0
- package/dist/src/scan/caseread.d.ts +84 -0
- package/dist/src/scan/caseread.d.ts.map +1 -0
- package/dist/src/scan/caseread.js +263 -0
- package/dist/src/scan/caseread.js.map +1 -0
- package/dist/src/scan/channelfs.d.ts +6 -0
- package/dist/src/scan/channelfs.d.ts.map +1 -0
- package/dist/src/scan/channelfs.js +27 -0
- package/dist/src/scan/channelfs.js.map +1 -0
- package/dist/src/scan/cutrender.d.ts +13 -0
- package/dist/src/scan/cutrender.d.ts.map +1 -0
- package/dist/src/scan/cutrender.js +51 -0
- package/dist/src/scan/cutrender.js.map +1 -0
- package/dist/src/scan/facts.d.ts +72 -0
- package/dist/src/scan/facts.d.ts.map +1 -0
- package/dist/src/scan/facts.js +593 -0
- package/dist/src/scan/facts.js.map +1 -0
- package/dist/src/scan/findings.d.ts +320 -0
- package/dist/src/scan/findings.d.ts.map +1 -0
- package/dist/src/scan/findings.js +948 -0
- package/dist/src/scan/findings.js.map +1 -0
- package/dist/src/scan/ledger.d.ts +116 -0
- package/dist/src/scan/ledger.d.ts.map +1 -0
- package/dist/src/scan/ledger.js +207 -0
- package/dist/src/scan/ledger.js.map +1 -0
- package/dist/src/scan/presentation.d.ts +19 -0
- package/dist/src/scan/presentation.d.ts.map +1 -0
- package/dist/src/scan/presentation.js +9 -0
- package/dist/src/scan/presentation.js.map +1 -0
- package/dist/src/scan/scan.d.ts +354 -0
- package/dist/src/scan/scan.d.ts.map +1 -0
- package/dist/src/scan/scan.js +725 -0
- package/dist/src/scan/scan.js.map +1 -0
- package/dist/src/scan/scanagents.d.ts +23 -0
- package/dist/src/scan/scanagents.d.ts.map +1 -0
- package/dist/src/scan/scanagents.js +99 -0
- package/dist/src/scan/scanagents.js.map +1 -0
- package/dist/src/scan/scanassemble.d.ts +65 -0
- package/dist/src/scan/scanassemble.d.ts.map +1 -0
- package/dist/src/scan/scanassemble.js +93 -0
- package/dist/src/scan/scanassemble.js.map +1 -0
- package/dist/src/scan/scanfs.d.ts +26 -0
- package/dist/src/scan/scanfs.d.ts.map +1 -0
- package/dist/src/scan/scanfs.js +229 -0
- package/dist/src/scan/scanfs.js.map +1 -0
- package/dist/src/scan/scanlease.d.ts +50 -0
- package/dist/src/scan/scanlease.d.ts.map +1 -0
- package/dist/src/scan/scanlease.js +251 -0
- package/dist/src/scan/scanlease.js.map +1 -0
- package/dist/src/scan/scanleaves.d.ts +55 -0
- package/dist/src/scan/scanleaves.d.ts.map +1 -0
- package/dist/src/scan/scanleaves.js +258 -0
- package/dist/src/scan/scanleaves.js.map +1 -0
- package/dist/src/scan/scanports.d.ts +79 -0
- package/dist/src/scan/scanports.d.ts.map +1 -0
- package/dist/src/scan/scanports.js +8 -0
- package/dist/src/scan/scanports.js.map +1 -0
- package/dist/src/scan/scanrender.d.ts +128 -0
- package/dist/src/scan/scanrender.d.ts.map +1 -0
- package/dist/src/scan/scanrender.js +527 -0
- package/dist/src/scan/scanrender.js.map +1 -0
- package/dist/src/scan/scanreport.d.ts +126 -0
- package/dist/src/scan/scanreport.d.ts.map +1 -0
- package/dist/src/scan/scanreport.js +289 -0
- package/dist/src/scan/scanreport.js.map +1 -0
- package/dist/src/scan/scanrun.d.ts +472 -0
- package/dist/src/scan/scanrun.d.ts.map +1 -0
- package/dist/src/scan/scanrun.js +1733 -0
- package/dist/src/scan/scanrun.js.map +1 -0
- package/dist/src/scan/scantree.d.ts +21 -0
- package/dist/src/scan/scantree.d.ts.map +1 -0
- package/dist/src/scan/scantree.js +297 -0
- package/dist/src/scan/scantree.js.map +1 -0
- package/dist/src/scan/scanverbs.d.ts +208 -0
- package/dist/src/scan/scanverbs.d.ts.map +1 -0
- package/dist/src/scan/scanverbs.js +689 -0
- package/dist/src/scan/scanverbs.js.map +1 -0
- package/dist/src/scan/stage2.d.ts +221 -0
- package/dist/src/scan/stage2.d.ts.map +1 -0
- package/dist/src/scan/stage2.js +617 -0
- package/dist/src/scan/stage2.js.map +1 -0
- package/dist/src/scan/stage2request.d.ts +125 -0
- package/dist/src/scan/stage2request.d.ts.map +1 -0
- package/dist/src/scan/stage2request.js +259 -0
- package/dist/src/scan/stage2request.js.map +1 -0
- package/dist/src/scan/textsniff.d.ts +14 -0
- package/dist/src/scan/textsniff.d.ts.map +1 -0
- package/dist/src/scan/textsniff.js +88 -0
- package/dist/src/scan/textsniff.js.map +1 -0
- package/dist/src/scan/unitcut.d.ts +135 -0
- package/dist/src/scan/unitcut.d.ts.map +1 -0
- package/dist/src/scan/unitcut.js +437 -0
- package/dist/src/scan/unitcut.js.map +1 -0
- package/dist/src/scan/unitcutstore.d.ts +155 -0
- package/dist/src/scan/unitcutstore.d.ts.map +1 -0
- package/dist/src/scan/unitcutstore.js +381 -0
- package/dist/src/scan/unitcutstore.js.map +1 -0
- package/dist/src/setupchecklist.d.ts +240 -0
- package/dist/src/setupchecklist.d.ts.map +1 -0
- package/dist/src/setupchecklist.js +407 -0
- package/dist/src/setupchecklist.js.map +1 -0
- package/package.json +7 -3
- package/skills/qcr-review/SKILL.md +24 -0
- package/skills/qcr-scan/SKILL.md +23 -0
- package/skills/qcr-setup/SKILL.md +18 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// What a scan case's leaf answers, read the same way whichever host ran it:
|
|
2
|
+
// the agent file it runs from, the event stream it emits (its answer, its
|
|
3
|
+
// cost, the files it opened), the env it may inherit and where its transcript
|
|
4
|
+
// is kept. The pi child (caserunner.ts `piCaseRunner`) and the in-process SDK
|
|
5
|
+
// session both read through these, so a case answers identically on either.
|
|
6
|
+
import { readdirSync, rmSync, statSync } from "node:fs";
|
|
7
|
+
import { join, posix } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { GITHUB_TOKEN_ENVS } from "../envstrip.js";
|
|
10
|
+
export function parseAgentFile(text) {
|
|
11
|
+
const m = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(text.replace(/\r\n/g, "\n"));
|
|
12
|
+
if (!m)
|
|
13
|
+
return null;
|
|
14
|
+
const fields = {};
|
|
15
|
+
for (const line of m[1].split("\n")) {
|
|
16
|
+
const kv = /^([A-Za-z_][\w-]*):\s*(.*)$/.exec(line);
|
|
17
|
+
if (kv)
|
|
18
|
+
fields[kv[1]] = kv[2].trim();
|
|
19
|
+
}
|
|
20
|
+
if (!fields.name)
|
|
21
|
+
return null;
|
|
22
|
+
return {
|
|
23
|
+
name: fields.name,
|
|
24
|
+
tools: (fields.tools ?? "")
|
|
25
|
+
.replace(/^\[|\]$/g, "")
|
|
26
|
+
.split(",")
|
|
27
|
+
.map((t) => t.trim())
|
|
28
|
+
.filter(Boolean),
|
|
29
|
+
model: fields.model || null,
|
|
30
|
+
systemPrompt: m[2].trim(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** The tools whose `path` argument names content the model read. `find` and
|
|
34
|
+
* `ls` list names and read no content, so they are not reads. */
|
|
35
|
+
export const FILE_READ_TOOLS = ["read", "grep"];
|
|
36
|
+
/** The path a `tool_execution_start` event says the model asked to read, or
|
|
37
|
+
* null. An attempt, not a read: `piCaseRunner` credits it only once the
|
|
38
|
+
* call's `tool_execution_end` reports success (`readSucceeded`).
|
|
39
|
+
* VERIFY-ON-PIN (read from 0.99.1's dist: the JSON mode passes agent events
|
|
40
|
+
* through verbatim — `tool_execution_start` carries `toolName` and `args`;
|
|
41
|
+
* `read` and `grep` both take `path`): the event and argument spelling. */
|
|
42
|
+
export function readPathOf(event) {
|
|
43
|
+
if (event.type !== "tool_execution_start" || typeof event.toolName !== "string")
|
|
44
|
+
return null;
|
|
45
|
+
if (!FILE_READ_TOOLS.includes(event.toolName))
|
|
46
|
+
return null;
|
|
47
|
+
const path = event.args?.path;
|
|
48
|
+
return typeof path === "string" && path.trim() !== "" ? path : null;
|
|
49
|
+
}
|
|
50
|
+
/** The id of a `tool_execution_end` that finished WITHOUT an error, or null.
|
|
51
|
+
* A failed read (a missing file, an offset past its end) or a grep that
|
|
52
|
+
* could not run read nothing, so its start credits no file.
|
|
53
|
+
* VERIFY-ON-PIN (0.99.1's agent-core `tool_execution_end` carries
|
|
54
|
+
* `toolCallId` and `isError`; the same pair is in the JSON-mode transcripts
|
|
55
|
+
* kept by a scan at 1.0.0): the event spelling. */
|
|
56
|
+
export function readSucceeded(event) {
|
|
57
|
+
if (event.type !== "tool_execution_end" || typeof event.toolCallId !== "string")
|
|
58
|
+
return null;
|
|
59
|
+
return event.isError === false ? event.toolCallId : null;
|
|
60
|
+
}
|
|
61
|
+
/** The file a read of `raw` opened, for a child running in `cwd` — pi's own
|
|
62
|
+
* resolution (`resolveToCwd`): one leading `@` stripped, `~` expanded, a
|
|
63
|
+
* relative path taken against the child's cwd, `.`/`..` collapsed. The scan
|
|
64
|
+
* judges what the lens opened, not how it spelled it: the child runs in the
|
|
65
|
+
* app root, not the scanned tree, so a relative `api/x.py` opened the app
|
|
66
|
+
* root's copy — another repository, or this checkout at HEAD — and must not
|
|
67
|
+
* pass for a read of the tree.
|
|
68
|
+
* VERIFY-ON-PIN (read from 0.99.1's dist/core/tools/path-utils.js
|
|
69
|
+
* `resolveToCwd` → utils/paths `resolvePath` with `stripAtPrefix` and
|
|
70
|
+
* `normalizeUnicodeSpaces`, `file://` URLs included): the macOS
|
|
71
|
+
* filename variants its `resolveReadPath` also tries are not modelled — they
|
|
72
|
+
* only differ for a path that does not exist as spelled. */
|
|
73
|
+
export function resolvedReadPath(raw, cwd, home) {
|
|
74
|
+
// pi's `normalizePath` order: unicode spaces → ASCII space, one `@`
|
|
75
|
+
// stripped, `~` expanded, a `file://` URL taken as its path.
|
|
76
|
+
let p = raw.replace(/[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g, " ");
|
|
77
|
+
if (p.startsWith("@"))
|
|
78
|
+
p = p.slice(1);
|
|
79
|
+
if (p === "~")
|
|
80
|
+
p = home;
|
|
81
|
+
else if (p.startsWith("~/"))
|
|
82
|
+
p = `${home}${p.slice(1)}`;
|
|
83
|
+
else if (/^file:\/\//.test(p)) {
|
|
84
|
+
try {
|
|
85
|
+
p = fileURLToPath(p);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// pi would throw on it and open nothing; left as spelled, it resolves
|
|
89
|
+
// to no focus file and is never credited.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return posix.resolve(cwd, p);
|
|
93
|
+
}
|
|
94
|
+
/** What one case's event stream says, fed event by event: the last assistant
|
|
95
|
+
* text, pi's own per-message cost summed, how the model stopped, and the files
|
|
96
|
+
* its `read`/`grep` calls opened. One reader for both hosts of a leaf — the
|
|
97
|
+
* child process (`piCaseRunner`) and the in-process session
|
|
98
|
+
* (src/engine/caserunner-sdk.ts) emit the same events. */
|
|
99
|
+
export function caseReadout(cwd,
|
|
100
|
+
/** The user's home, which a read's `~` expands against. */
|
|
101
|
+
home, onCost) {
|
|
102
|
+
let final = "";
|
|
103
|
+
let cost = 0;
|
|
104
|
+
let priced = false;
|
|
105
|
+
let failure = null;
|
|
106
|
+
const reads = [];
|
|
107
|
+
// Started reads by call id, credited only when the call succeeds.
|
|
108
|
+
const attempted = new Map();
|
|
109
|
+
return {
|
|
110
|
+
feed(raw) {
|
|
111
|
+
if (typeof raw !== "object" || raw === null)
|
|
112
|
+
return;
|
|
113
|
+
const event = raw;
|
|
114
|
+
const read = readPathOf(event);
|
|
115
|
+
if (read !== null && typeof event.toolCallId === "string")
|
|
116
|
+
attempted.set(event.toolCallId, resolvedReadPath(read, cwd, home));
|
|
117
|
+
const done = readSucceeded(event);
|
|
118
|
+
if (done !== null && attempted.has(done)) {
|
|
119
|
+
reads.push(attempted.get(done));
|
|
120
|
+
attempted.delete(done);
|
|
121
|
+
}
|
|
122
|
+
const msg = event.message;
|
|
123
|
+
if (event.type !== "message_end" || !msg || msg.role !== "assistant")
|
|
124
|
+
return;
|
|
125
|
+
const usage = msg.usage;
|
|
126
|
+
if (typeof usage?.cost?.total === "number") {
|
|
127
|
+
cost += usage.cost.total;
|
|
128
|
+
priced = true;
|
|
129
|
+
try {
|
|
130
|
+
onCost?.(cost);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// A listener's fault never ends the case.
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const parts = Array.isArray(msg.content)
|
|
137
|
+
? msg.content
|
|
138
|
+
: [];
|
|
139
|
+
const text = parts
|
|
140
|
+
.filter((p) => p.type === "text" && typeof p.text === "string")
|
|
141
|
+
.map((p) => p.text)
|
|
142
|
+
.join("\n");
|
|
143
|
+
if (text.trim() !== "")
|
|
144
|
+
final = text;
|
|
145
|
+
if (msg.stopReason === "error" || msg.stopReason === "aborted") {
|
|
146
|
+
failure =
|
|
147
|
+
typeof msg.errorMessage === "string"
|
|
148
|
+
? msg.errorMessage
|
|
149
|
+
: `the model stopped: ${String(msg.stopReason)}`;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
failure = null;
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
get final() {
|
|
156
|
+
return final;
|
|
157
|
+
},
|
|
158
|
+
get failure() {
|
|
159
|
+
return failure;
|
|
160
|
+
},
|
|
161
|
+
get reads() {
|
|
162
|
+
return reads;
|
|
163
|
+
},
|
|
164
|
+
spent() {
|
|
165
|
+
return priced ? cost : null;
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** What a leaf's pi process may inherit, by exact name. The leaf's model runs
|
|
170
|
+
* bash, so the process env is allowlisted rather than copied: no GitHub
|
|
171
|
+
* credential (packages/pi-plugin/src/envstrip.ts) and nothing else of the driver's reaches it. */
|
|
172
|
+
const LEAF_ENV_NAMES = new Set([
|
|
173
|
+
"PATH",
|
|
174
|
+
"HOME",
|
|
175
|
+
"USER",
|
|
176
|
+
"LOGNAME",
|
|
177
|
+
"SHELL",
|
|
178
|
+
"TERM",
|
|
179
|
+
"TZ",
|
|
180
|
+
"LANG",
|
|
181
|
+
"TMPDIR",
|
|
182
|
+
"TMP",
|
|
183
|
+
"TEMP",
|
|
184
|
+
"SSL_CERT_FILE",
|
|
185
|
+
"SSL_CERT_DIR",
|
|
186
|
+
"NODE_EXTRA_CA_CERTS",
|
|
187
|
+
"HTTP_PROXY",
|
|
188
|
+
"HTTPS_PROXY",
|
|
189
|
+
"NO_PROXY",
|
|
190
|
+
"ALL_PROXY",
|
|
191
|
+
"http_proxy",
|
|
192
|
+
"https_proxy",
|
|
193
|
+
"no_proxy",
|
|
194
|
+
"all_proxy",
|
|
195
|
+
]);
|
|
196
|
+
/** …and by prefix or shape: locale (`LC_*`), XDG dirs, pi's own settings
|
|
197
|
+
* (`PI_*`, among them `PI_CODING_AGENT_DIR`), bash-guard's locators
|
|
198
|
+
* (`QCR_*`: install, config home, sandbox root, replay flag) and the model
|
|
199
|
+
* providers' key variables. */
|
|
200
|
+
const LEAF_ENV_PATTERNS = [
|
|
201
|
+
/^LC_[A-Z_]+$/,
|
|
202
|
+
/^XDG_[A-Z_]+$/,
|
|
203
|
+
/^PI_[A-Z0-9_]+$/,
|
|
204
|
+
/^QCR_[A-Z0-9_]+$/,
|
|
205
|
+
/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*_API_KEY$/,
|
|
206
|
+
];
|
|
207
|
+
/** The env one leaf's pi starts with: `source` (the driver's) filtered to the
|
|
208
|
+
* allowlist, the GitHub token envs always dropped, and `QCR_GITHUB_READ_ENV`
|
|
209
|
+
* (the path of the file holding the read token) with them. */
|
|
210
|
+
export function leafEnv(source) {
|
|
211
|
+
const env = {};
|
|
212
|
+
for (const [name, value] of Object.entries(source)) {
|
|
213
|
+
if (value === undefined)
|
|
214
|
+
continue;
|
|
215
|
+
if (LEAF_ENV_NAMES.has(name) || LEAF_ENV_PATTERNS.some((re) => re.test(name)))
|
|
216
|
+
env[name] = value;
|
|
217
|
+
}
|
|
218
|
+
for (const k of GITHUB_TOKEN_ENVS)
|
|
219
|
+
delete env[k];
|
|
220
|
+
delete env["QCR_GITHUB_READ_ENV"];
|
|
221
|
+
return env;
|
|
222
|
+
}
|
|
223
|
+
/** The stderr a failed case's reason is taken from: its last characters. */
|
|
224
|
+
export const STDERR_TAIL_CHARS = 4000;
|
|
225
|
+
/** The last three lines of the last `STDERR_TAIL_CHARS` characters of a failed
|
|
226
|
+
* leaf's stderr, joined with " / " — the excerpt a failure reason quotes. */
|
|
227
|
+
export function stderrExcerpt(stderr) {
|
|
228
|
+
return stderr.slice(-STDERR_TAIL_CHARS).trim().split("\n").slice(-3).join(" / ");
|
|
229
|
+
}
|
|
230
|
+
/** Transcript directories kept: the newest by modification time. */
|
|
231
|
+
export const TRANSCRIPT_RUNS_KEPT = 20;
|
|
232
|
+
/** Where a case's transcript lands under `dir`, relative to it: the case id's
|
|
233
|
+
* first segment (the run) is the directory, the rest of it the file name —
|
|
234
|
+
* `r3/row-2#1/validate` → `r3/row-2_1.validate.jsonl`. Every segment is
|
|
235
|
+
* reduced to `[A-Za-z0-9._-]` and none may be `.` or `..`, so an id never
|
|
236
|
+
* names a path outside `dir`. Pure. */
|
|
237
|
+
export function transcriptFile(id) {
|
|
238
|
+
const seg = (s) => {
|
|
239
|
+
const t = s.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
240
|
+
return t === "" || /^\.+$/.test(t) ? t.replace(/\./g, "_") || "_" : t;
|
|
241
|
+
};
|
|
242
|
+
const parts = id.split("/").map(seg);
|
|
243
|
+
if (parts.length === 1)
|
|
244
|
+
return `${parts[0]}.jsonl`;
|
|
245
|
+
return `${parts[0]}/${parts.slice(1).join(".")}.jsonl`;
|
|
246
|
+
}
|
|
247
|
+
/** Remove all but the newest `keep` run directories under `dir` (by
|
|
248
|
+
* modification time, never `except`). Best effort: a transcript is evidence,
|
|
249
|
+
* not state, and a failure here never touches a case. */
|
|
250
|
+
export function pruneTranscripts(dir, keep, except) {
|
|
251
|
+
try {
|
|
252
|
+
const runs = readdirSync(dir, { withFileTypes: true })
|
|
253
|
+
.filter((d) => d.isDirectory() && d.name !== except)
|
|
254
|
+
.map((d) => ({ name: d.name, at: statSync(join(dir, d.name)).mtimeMs }))
|
|
255
|
+
.sort((a, b) => b.at - a.at || b.name.localeCompare(a.name));
|
|
256
|
+
for (const r of runs.slice(Math.max(0, keep - 1)))
|
|
257
|
+
rmSync(join(dir, r.name), { recursive: true, force: true });
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// Evidence only: nothing to undo.
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=caseread.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caseread.js","sourceRoot":"","sources":["../../../src/scan/caseread.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** A file's text, or null when it is ABSENT — any other failure throws, so a
|
|
2
|
+
* reader can tell "not written yet" from "cannot be read". */
|
|
3
|
+
export declare function readTextIfPresent(path: string): string | null;
|
|
4
|
+
/** tmp+rename: a reader never sees a half-written file. */
|
|
5
|
+
export declare function writeAtomic(path: string, text: string): void;
|
|
6
|
+
//# sourceMappingURL=channelfs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channelfs.d.ts","sourceRoot":"","sources":["../../../src/scan/channelfs.ts"],"names":[],"mappings":"AAQA;8DAC8D;AAC9D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO7D;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAK5D"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// The file I/O of the scan's TUI↔companion records and channel files (the
|
|
2
|
+
// run record, the findings store, the stop and stage-2 start requests and
|
|
3
|
+
// their answers). A dependency-free leaf, so the two setup leaves the TUI
|
|
4
|
+
// imports (scanreport.ts, stage2request.ts) share it without reaching each
|
|
5
|
+
// other at runtime.
|
|
6
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname } from "node:path";
|
|
8
|
+
/** A file's text, or null when it is ABSENT — any other failure throws, so a
|
|
9
|
+
* reader can tell "not written yet" from "cannot be read". */
|
|
10
|
+
export function readTextIfPresent(path) {
|
|
11
|
+
try {
|
|
12
|
+
return readFileSync(path, "utf8");
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
if (err.code === "ENOENT")
|
|
16
|
+
return null;
|
|
17
|
+
throw err;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** tmp+rename: a reader never sees a half-written file. */
|
|
21
|
+
export function writeAtomic(path, text) {
|
|
22
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
23
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
24
|
+
writeFileSync(tmp, text);
|
|
25
|
+
renameSync(tmp, path);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=channelfs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channelfs.js","sourceRoot":"","sources":["../../../src/scan/channelfs.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,0EAA0E;AAC1E,0EAA0E;AAC1E,2EAA2E;AAC3E,oBAAoB;AACpB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;8DAC8D;AAC9D,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAClE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAY;IACpD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IACzC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CutStatus, CutView } from "./unitcutstore.js";
|
|
2
|
+
export declare function renderCut(title: string, v: CutView): string;
|
|
3
|
+
/** How a rendering names the save that makes a draft the cut — the tool, or
|
|
4
|
+
* the `qcr scan units` verb. */
|
|
5
|
+
export interface CutVerbs {
|
|
6
|
+
save: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const CUT_TOOL_VERBS: CutVerbs;
|
|
9
|
+
export declare const CUT_CLI_VERBS: CutVerbs;
|
|
10
|
+
/** A stored or adjusted draft, and that it is not in use until saved. */
|
|
11
|
+
export declare function renderDraft(title: string, draft: CutView, verbs?: CutVerbs): string;
|
|
12
|
+
export declare function renderStatus(s: CutStatus): string;
|
|
13
|
+
//# sourceMappingURL=cutrender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cutrender.d.ts","sourceRoot":"","sources":["../../../src/scan/cutrender.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE5D,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,GAAG,MAAM,CAmB3D;AAED;gCACgC;AAChC,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AACD,eAAO,MAAM,cAAc,EAAE,QAA0C,CAAC;AACxE,eAAO,MAAM,aAAa,EAAE,QAA0C,CAAC;AAEvE,yEAAyE;AACzE,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,OAAO,EACd,KAAK,GAAE,QAAyB,GAC/B,MAAM,CAER;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CA6BjD"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// The unit cut, rendered: the saved cut, a draft and the cut's status, worded
|
|
2
|
+
// for the setup belt's tools or the `qcr scan units` / `/qcr-scan units` verbs.
|
|
3
|
+
import { displayRoot } from "./facts.js";
|
|
4
|
+
export function renderCut(title, v) {
|
|
5
|
+
const rows = v.units.map((u) => {
|
|
6
|
+
const split = u.cases === null
|
|
7
|
+
? " — case coverage UNKNOWN"
|
|
8
|
+
: u.cases.length > 1
|
|
9
|
+
? ` — split into ${u.cases.length} cases (${u.cases.map((c) => `${c.id}: ${c.files}f/${c.lines}l`).join(", ")})`
|
|
10
|
+
: "";
|
|
11
|
+
const over = u.cases?.some((c) => c.over_budget)
|
|
12
|
+
? " [a file alone exceeds one case — a scan reads it in line ranges]"
|
|
13
|
+
: "";
|
|
14
|
+
return ` - ${u.name} [${u.kind}] ${u.roots.map(displayRoot).join(", ")}: ${u.files ?? "UNKNOWN"} files, ${u.reviewable ?? "UNKNOWN"} reviewable, ${u.lines ?? "UNKNOWN"} lines${split}${over}${u.note ? ` — ${u.note}` : ""}`;
|
|
15
|
+
});
|
|
16
|
+
const lines = [`${title}:`, ...rows];
|
|
17
|
+
if (v.ignored.length > 0)
|
|
18
|
+
lines.push(` ignored: ${v.ignored.join(", ")} (${v.ignored_files ?? "UNKNOWN"} files)`);
|
|
19
|
+
for (const e of v.errors)
|
|
20
|
+
lines.push(` ✗ ${e}`);
|
|
21
|
+
for (const b of v.blocking_save)
|
|
22
|
+
lines.push(` ! ${b}`);
|
|
23
|
+
return lines.join("\n");
|
|
24
|
+
}
|
|
25
|
+
export const CUT_TOOL_VERBS = { save: "setup_unit_cut_save" };
|
|
26
|
+
export const CUT_CLI_VERBS = { save: "qcr scan units save" };
|
|
27
|
+
/** A stored or adjusted draft, and that it is not in use until saved. */
|
|
28
|
+
export function renderDraft(title, draft, verbs = CUT_TOOL_VERBS) {
|
|
29
|
+
return `${renderCut(title, draft)}\n\nNot in use until ${verbs.save}.`;
|
|
30
|
+
}
|
|
31
|
+
export function renderStatus(s) {
|
|
32
|
+
const out = [];
|
|
33
|
+
for (const p of s.problems)
|
|
34
|
+
out.push(`✗ ${p}`);
|
|
35
|
+
if (s.saved) {
|
|
36
|
+
const stale = s.saved.blocking_save.length > 0 ? " — STALE against the repo now, see the ! lines" : "";
|
|
37
|
+
out.push(renderCut(`Saved unit cut (saved ${s.saved.saved_at}${s.saved.head ? ` at ${s.saved.head.slice(0, 12)}` : ""})${stale}`, s.saved));
|
|
38
|
+
}
|
|
39
|
+
else if (s.cut_file_unreadable) {
|
|
40
|
+
out.push("Saved unit cut: UNKNOWN — the cut file could not be read (see above); nothing will overwrite it.");
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
out.push("No saved unit cut yet.");
|
|
44
|
+
}
|
|
45
|
+
if (s.draft)
|
|
46
|
+
out.push(renderCut(`Draft (${s.draft.source}, updated ${s.draft.updated_at}) — not in use until saved`, s.draft));
|
|
47
|
+
if (s.has_previous)
|
|
48
|
+
out.push("The cut before the saved one is kept as `previous` in the state home.");
|
|
49
|
+
return out.join("\n\n");
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=cutrender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cutrender.js","sourceRoot":"","sources":["../../../src/scan/cutrender.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type RepoRead = (rel: string) => string | null;
|
|
2
|
+
export interface Evidence {
|
|
3
|
+
name: string;
|
|
4
|
+
/** Repo-relative path of the file that shows it. */
|
|
5
|
+
evidence: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LanguageCount {
|
|
8
|
+
language: string;
|
|
9
|
+
files: number;
|
|
10
|
+
}
|
|
11
|
+
export interface CiFact {
|
|
12
|
+
system: string;
|
|
13
|
+
path: string;
|
|
14
|
+
/** The workflow's `name:` when the file states one. */
|
|
15
|
+
name: string | null;
|
|
16
|
+
}
|
|
17
|
+
/** Every unit kind — the type and the runtime check (`unitcut.ts`) read this
|
|
18
|
+
* one list. */
|
|
19
|
+
export declare const UNIT_KINDS: readonly ["django-app", "js-package", "python-package", "go-module", "rust-crate", "directory", "rest", "custom"];
|
|
20
|
+
export type UnitKind = (typeof UNIT_KINDS)[number];
|
|
21
|
+
/** One candidate unit: a name, the path prefixes it owns, and why. */
|
|
22
|
+
export interface UnitCandidate {
|
|
23
|
+
name: string;
|
|
24
|
+
roots: string[];
|
|
25
|
+
kind: UnitKind;
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RepoFacts {
|
|
29
|
+
files: number;
|
|
30
|
+
languages: LanguageCount[];
|
|
31
|
+
/** Package-manager manifests, as evidence per ecosystem. */
|
|
32
|
+
manifests: Evidence[];
|
|
33
|
+
frameworks: Evidence[];
|
|
34
|
+
ci: CiFact[];
|
|
35
|
+
/** Linters, formatters and type checkers whose config is in the repo. */
|
|
36
|
+
linters: Evidence[];
|
|
37
|
+
/** Design docs read in full by the survey: README, CONTRIBUTING, DESIGN… */
|
|
38
|
+
guides: string[];
|
|
39
|
+
/** Written style guides, in `findStyleGuide`'s reference grammar. */
|
|
40
|
+
style_guides: string[];
|
|
41
|
+
/** First-party agent-directive files (AGENTS.md, CLAUDE.md, rules dirs…). */
|
|
42
|
+
agent_files: string[];
|
|
43
|
+
/** The deterministic first guess at the unit cut — the companion's input. */
|
|
44
|
+
unit_candidates: UnitCandidate[];
|
|
45
|
+
}
|
|
46
|
+
/** Stable item text shared by facts and portrait renderers. */
|
|
47
|
+
export declare const evidenceText: (item: Evidence) => string;
|
|
48
|
+
export declare const ciText: (item: CiFact) => string;
|
|
49
|
+
export declare const displayRoot: (root: string) => string;
|
|
50
|
+
export declare function isMigrationFile(path: string): boolean;
|
|
51
|
+
/** Vendored trees are someone else's repo: never evidence about this one. */
|
|
52
|
+
export declare function isVendored(path: string): boolean;
|
|
53
|
+
export declare function isAgentFile(path: string): boolean;
|
|
54
|
+
/** The shallowest vendored directories in the listing (`web/node_modules`,
|
|
55
|
+
* `third_party`) — what a proposed cut ignores by default. */
|
|
56
|
+
export declare function vendoredRoots(files: readonly string[]): string[];
|
|
57
|
+
/** The longest unit name. */
|
|
58
|
+
export declare const UNIT_NAME_MAX = 64;
|
|
59
|
+
/** A unit name: one plain segment (the state files and reports key on it). */
|
|
60
|
+
export declare const UNIT_NAME_RE: RegExp;
|
|
61
|
+
/**
|
|
62
|
+
* The stack-aware first guess at the unit cut. Markers, most specific first:
|
|
63
|
+
* a Django app (`apps.py`), a JS package (a nested `package.json`), a Python
|
|
64
|
+
* package with its own `pyproject.toml`/`setup.py`, a Go module, a Rust crate.
|
|
65
|
+
* Top-level directories holding code that no marker claimed become
|
|
66
|
+
* `directory` units, and a `rest` unit at the repo root catches the remainder
|
|
67
|
+
* so the guess is always a complete partition. The companion reshapes this;
|
|
68
|
+
* it is evidence, not the answer.
|
|
69
|
+
*/
|
|
70
|
+
export declare function unitCandidates(files: readonly string[]): UnitCandidate[];
|
|
71
|
+
export declare function repoFacts(files: readonly string[], read: RepoRead): RepoFacts;
|
|
72
|
+
//# sourceMappingURL=facts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facts.d.ts","sourceRoot":"","sources":["../../../src/scan/facts.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAEtD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED;eACe;AACf,eAAO,MAAM,UAAU,mHASb,CAAC;AACX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,4DAA4D;IAC5D,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,yEAAyE;IACzE,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,4EAA4E;IAC5E,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,qEAAqE;IACrE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,6EAA6E;IAC7E,eAAe,EAAE,aAAa,EAAE,CAAC;CAClC;AAED,+DAA+D;AAC/D,eAAO,MAAM,YAAY,GAAI,MAAM,QAAQ,KAAG,MAA2C,CAAC;AAC1F,eAAO,MAAM,MAAM,GAAI,MAAM,MAAM,KAAG,MACkC,CAAC;AACzE,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,MAA+B,CAAC;AAgB3E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIhD;AAmYD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CASjD;AAED;8DAC8D;AAC9D,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAUhE;AAID,6BAA6B;AAC7B,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,8EAA8E;AAC9E,eAAO,MAAM,YAAY,QAAoE,CAAC;AAc9F;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,aAAa,EAAE,CAwDxE;AAID,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,CAc7E"}
|