@quickchatai/pi-quickreview 1.1.0 → 1.3.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/README.md +79 -27
- package/assets/config.example.toml +60 -14
- package/assets/prompts/onboarding.md +58 -18
- package/assets/schemas/review-result.v1.json +60 -0
- package/dist/extensions/quickreview.d.ts +13 -5
- package/dist/extensions/quickreview.d.ts.map +1 -1
- package/dist/extensions/quickreview.js +77 -25
- 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/envmap.d.ts.map +1 -1
- package/dist/src/engine/envmap.js +5 -0
- package/dist/src/engine/envmap.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/engine/pisdk.d.ts +9 -0
- package/dist/src/engine/pisdk.d.ts.map +1 -1
- package/dist/src/engine/pisdk.js +29 -4
- package/dist/src/engine/pisdk.js.map +1 -1
- package/dist/src/githubAuth.d.ts +39 -1
- package/dist/src/githubAuth.d.ts.map +1 -1
- package/dist/src/githubAuth.js +75 -8
- package/dist/src/githubAuth.js.map +1 -1
- package/dist/src/guards.d.ts +14 -1
- package/dist/src/guards.d.ts.map +1 -1
- package/dist/src/guards.js +61 -2
- package/dist/src/guards.js.map +1 -1
- package/dist/src/homes.js +1 -1
- package/dist/src/homes.js.map +1 -1
- package/dist/src/identity.d.ts +34 -0
- package/dist/src/identity.d.ts.map +1 -0
- package/dist/src/identity.js +67 -0
- package/dist/src/identity.js.map +1 -0
- 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 +42 -0
- package/dist/src/modelroute.d.ts.map +1 -0
- package/dist/src/modelroute.js +126 -0
- package/dist/src/modelroute.js.map +1 -0
- 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/login.d.ts +11 -4
- package/dist/src/plugin/login.d.ts.map +1 -1
- package/dist/src/plugin/login.js +28 -26
- 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/review.d.ts +52 -6
- package/dist/src/plugin/review.d.ts.map +1 -1
- package/dist/src/plugin/review.js +269 -99
- 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.js +1 -1
- 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/reviewconfig.d.ts +12 -0
- package/dist/src/reviewconfig.d.ts.map +1 -1
- package/dist/src/reviewconfig.js +9 -1
- package/dist/src/reviewconfig.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/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
package/README.md
CHANGED
|
@@ -9,21 +9,67 @@ pi install npm:@quickchatai/pi-quickreview
|
|
|
9
9
|
|
|
10
10
|
## Commands
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
Each command is also a skill of the same name, so the model in your session
|
|
13
|
+
knows the command and can tell you the line to type.
|
|
14
|
+
|
|
15
|
+
- `/qcr-review [preset] [target]` reviews in the background: the command
|
|
16
|
+
returns at once, you keep chatting, the status line shows the review's
|
|
17
|
+
progress, and the result arrives in the session as a message (and is kept
|
|
18
|
+
as markdown and JSON under the qcr state home).
|
|
19
|
+
- The target: nothing reviews the current branch against its base together
|
|
20
|
+
with its uncommitted changes, as one review; `123`, `#123` or a pull
|
|
21
|
+
request URL reviews a PR; a branch name reviews that branch;
|
|
22
|
+
`--uncommitted` reviews the uncommitted changes alone.
|
|
23
|
+
- The preset, first when given: `quick` (lower reasoning, about half the
|
|
24
|
+
cost), `full` (the whole target again at the highest reasoning) or
|
|
25
|
+
`security` (only the security and data-safety lenses the change
|
|
26
|
+
triggers).
|
|
27
|
+
- `--preview` shows the plan and the price without running anything;
|
|
28
|
+
`--stop` stops the review that is running. One review runs per session
|
|
29
|
+
at a time, and ending the session stops it.
|
|
30
|
+
- After a PR review, your posting rule decides whether it goes to the PR
|
|
31
|
+
as a comment-only review (see Settings).
|
|
32
|
+
- `/qcr-scan [--delta] [--sha REF] [--preview]` scans the whole repo for
|
|
33
|
+
defects, priced first and started only on your yes. It runs in the
|
|
34
|
+
background in this pi, like a review: a status line while it runs, the new
|
|
35
|
+
findings as a message when it ends, `/qcr-scan stop` to stop it. Its cases
|
|
36
|
+
are SDK sessions of this pi behind the same read-only bash guard as the
|
|
37
|
+
review's lenses. `/qcr-scan findings`, `status`, `units` and the other
|
|
38
|
+
scan subcommands pass through as typed. The scan sandbox (a container) and
|
|
39
|
+
dismissing a finding are the `qcr` app's (`qcr scan`).
|
|
40
|
+
- `/qcr-setup` checks the pi version, the login, git and GitHub access for
|
|
41
|
+
PR targets. It then offers to set up this repo's profile (`.qcr/`), No
|
|
42
|
+
first: on your yes, your agent in the session surveys the repo, reads its
|
|
43
|
+
review history, interviews you and writes the profile for you to commit.
|
|
44
|
+
`/qcr-setup profile` starts that straight away. Its checks are steps on a
|
|
45
|
+
setup checklist (`src/setupchecklist.ts`) that the `qcr` app extends with
|
|
46
|
+
its own steps.
|
|
47
|
+
|
|
48
|
+
`/review`, `/scan` and `/selfimprove` are gone: pi's command namespace is
|
|
49
|
+
shared with every other extension, so the commands carry the `qcr-` prefix.
|
|
50
|
+
`/review` is `/qcr-review`, `/scan` is `/qcr-scan`, and `/selfimprove` is a
|
|
51
|
+
command of the `qcr` app's own pi (`qcr pi`).
|
|
52
|
+
|
|
53
|
+
## Settings
|
|
54
|
+
|
|
55
|
+
Your own defaults live in pi's settings (`~/.pi/agent/settings.json`, or the
|
|
56
|
+
project's `.pi/settings.json`), under `quickreview`:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"quickreview": {
|
|
61
|
+
"posting": "ask",
|
|
62
|
+
"preset": "quick"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- `posting` decides what happens after a PR review: `ask` (the default)
|
|
68
|
+
asks, with "No, keep it here" offered first; `never` keeps every review in
|
|
69
|
+
the session; `always` posts without asking.
|
|
70
|
+
- `preset` is the preset a `/qcr-review` without one runs under.
|
|
71
|
+
|
|
72
|
+
Rules for one repository live in its profile, not here.
|
|
27
73
|
|
|
28
74
|
## Which pi
|
|
29
75
|
|
|
@@ -42,32 +88,38 @@ exactly instead; `qcr pi` starts that pinned pi with this plugin preloaded.
|
|
|
42
88
|
- A pi login for the review's models (`/login` in pi). The review borrows
|
|
43
89
|
it: pi refreshes it when needed, and the review only ever reads a copy.
|
|
44
90
|
An API key for another provider is not used in its place: with no login
|
|
45
|
-
for the review's provider, `/review` stops and names `/login`.
|
|
91
|
+
for the review's provider, `/qcr-review` stops and names `/login`. The
|
|
92
|
+
scan borrows it the same way.
|
|
46
93
|
- For a PR: the App when a qcr install configures one, else `GITHUB_TOKEN`,
|
|
47
94
|
`GH_TOKEN` or `gh auth login`, in that order.
|
|
48
|
-
- For posting
|
|
49
|
-
|
|
95
|
+
- For posting: a qcr config with `dry_run = false`. The shipped defaults keep
|
|
96
|
+
`dry_run` on, and then a post is recorded under the qcr state home
|
|
97
|
+
(`review/dryrun/`) and nothing is sent; the post question says so.
|
|
50
98
|
|
|
51
99
|
## What it never does
|
|
52
100
|
|
|
53
|
-
- The review never
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- It never
|
|
58
|
-
|
|
59
|
-
|
|
101
|
+
- The review and the scan never write to your checkout: they read the tree
|
|
102
|
+
as it is.
|
|
103
|
+
- The review and the scan never run your code, tests or app: they read code
|
|
104
|
+
(and the review, CI).
|
|
105
|
+
- It never runs the `qcr` CLI.
|
|
106
|
+
- It never posts unless you say yes to the post question (or your posting
|
|
107
|
+
rule is `always`), and then it posts one comment-only review through the
|
|
108
|
+
same mutation gate the bot uses. It never approves, requests changes or
|
|
109
|
+
dismisses a review.
|
|
60
110
|
- It never refreshes your login itself, and never hands a token to the
|
|
61
111
|
model's tools.
|
|
62
112
|
- It never gives your session a tool. The review's lens passes run in their
|
|
63
113
|
own sessions, with none of your pi extensions, skills or settings, and
|
|
64
|
-
your session only sees the result.
|
|
114
|
+
your session only sees the result. The skills it ships are instructions
|
|
115
|
+
for your session's model, naming the commands.
|
|
65
116
|
- It never sets or flips `dry_run`.
|
|
66
117
|
- It never starts `claude`.
|
|
67
118
|
|
|
68
119
|
## What ships in the package
|
|
69
120
|
|
|
70
|
-
The compiled review core and the plugin (`dist/`),
|
|
121
|
+
The compiled review core and the plugin (`dist/`), one skill per command
|
|
122
|
+
(`skills/`), and under `assets/` the
|
|
71
123
|
data the core reads: the agents, prompts and seeds, the schemas, the
|
|
72
124
|
profiles and the shipped config defaults. `assets/` is copied from the
|
|
73
125
|
repository at pack time and checked file by file against its source, so the
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
# supply are marked "← FILL (n)" — grep for FILL. Flip dry_run to false only
|
|
7
7
|
# as a deliberate cutover act, never as part of setup.
|
|
8
8
|
#
|
|
9
|
-
# YOU DO NOT HAVE TO FILL THIS BY HAND: `
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
9
|
+
# YOU DO NOT HAVE TO FILL THIS BY HAND: a bare `qcr` (the home) writes the
|
|
10
|
+
# repository you choose and its defaults, and the setup wizard's steps
|
|
11
|
+
# interview you for the rest, derive what they can, and patch this file in
|
|
12
|
+
# place — every comment survives, and a re-run edits only what changed.
|
|
13
|
+
# Neither writes dry_run in either direction; going live is its own explicit
|
|
13
14
|
# step. See docs/onboarding.md.
|
|
14
15
|
# ONE mutation class is exempt and executes for real even here: the App
|
|
15
16
|
# re-pointing its OWN webhook URL (see [ingress] below) — it can never touch
|
|
@@ -27,8 +28,22 @@ timezone = "UTC" # ← FILL: your IANA timezone, e.g
|
|
|
27
28
|
|
|
28
29
|
[github]
|
|
29
30
|
repo = "" # ← FILL (1): the repo to review, "owner/name"
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
# Whose GitHub credential the daemon runs on:
|
|
32
|
+
# "app" (the default; a missing key means "app") — a GitHub App: its
|
|
33
|
+
# installation token, webhooks, APPROVE / REQUEST_CHANGES reviews.
|
|
34
|
+
# "user" — your OWN GitHub account, no App. Polling only: [ingress] shape
|
|
35
|
+
# must be "polling" (config load refuses anything else), no webhooks,
|
|
36
|
+
# no webhook self-heal. Every review posts as a COMMENT whose body
|
|
37
|
+
# states the verdict (an automated verdict never counts as your
|
|
38
|
+
# approval, and GitHub refuses either on your own PR). The token is
|
|
39
|
+
# read from the FILE <secrets dir>/github_token (0600, the token
|
|
40
|
+
# alone), which `qcr github-token sync` writes from your `gh` login;
|
|
41
|
+
# never from the environment or a command line. app_slug may be "";
|
|
42
|
+
# bot_login is your own GitHub login. The App secrets (app_id,
|
|
43
|
+
# app.pem, webhook_secret) are not needed.
|
|
44
|
+
#auth = "app"
|
|
45
|
+
app_slug = "" # ← FILL (2): your GitHub App slug ("" under auth = "user")
|
|
46
|
+
bot_login = "" # ← FILL (3): the App bot login, "<app_slug>[bot]" (your login under auth = "user")
|
|
32
47
|
review_team = "" # ← FILL (4): "org/team" whose review request triggers
|
|
33
48
|
# There is no reviewer knob here. The reviewer never hands a PR back: it always
|
|
34
49
|
# decides on the merits and posts a verdict. If you want a named person told
|
|
@@ -39,7 +54,8 @@ in_progress_label = "AI review in progress"
|
|
|
39
54
|
reviewed_label = "Automatic Review"
|
|
40
55
|
improvement_label = "Bot improvement"
|
|
41
56
|
issue_label = "bot-filed"
|
|
42
|
-
# secrets dir: app_id, app.pem, webhook_secret, tunnel_token
|
|
57
|
+
# secrets dir: app_id, app.pem, webhook_secret, tunnel_token (auth = "user": github_token instead
|
|
58
|
+
# of the first three); optional openai_api_key (pi, API-key
|
|
43
59
|
# route) and openai_embeddings_api_key (only with embedding_provider = "openai" in the openviking table)
|
|
44
60
|
# "" (recommended) = the install's config home, $XDG_CONFIG_HOME/qcr/<install>/secrets (the old
|
|
45
61
|
# ~/.config/quickreview-bot is never read — `qcr install migrate` moves it). A path here wins.
|
|
@@ -81,8 +97,12 @@ port = 8787 # loopback only; your HTTPS ingress
|
|
|
81
97
|
# touches only the App's own configuration, never the reviewed repo. Off by
|
|
82
98
|
# default: with the section absent (or shape = "none") no watcher ever starts —
|
|
83
99
|
# a stable hostname (named tunnel, VM route) needs none of this.
|
|
100
|
+
# shape = "polling" takes no webhooks at all: the daemon's /webhook route
|
|
101
|
+
# refuses every delivery, no webhook_secret is read, and the reconcile poll
|
|
102
|
+
# (poll_interval_s, below) is the only trigger. [github] auth = "user"
|
|
103
|
+
# requires it.
|
|
84
104
|
#[ingress]
|
|
85
|
-
#shape = "tunnel-file" # "none" (default) | "tunnel-file"
|
|
105
|
+
#shape = "tunnel-file" # "none" (default) | "tunnel-file" | "polling"
|
|
86
106
|
#hostname_file = "" # "" ⇒ <root>/state/ingress/hostname
|
|
87
107
|
#debounce_ms = 3000 # settle time after a file change
|
|
88
108
|
#sweep_interval_s = 300 # polling backstop behind fs.watch
|
|
@@ -204,7 +224,9 @@ improvement_pr_deny_prefixes = ["migrations/", "infra/", "ansible/", ".github/wo
|
|
|
204
224
|
[models]
|
|
205
225
|
# default for every role; tiered pass models below
|
|
206
226
|
# Provider prefix is `openai-codex/` — the ChatGPT-OAuth route's provider key
|
|
207
|
-
# in pi's auth.json
|
|
227
|
+
# in pi's auth.json. Any OpenAI login serves it: when pi holds no
|
|
228
|
+
# `openai-codex` login but an `openai` one (pi 1.4's Sign in with ChatGPT, or
|
|
229
|
+
# an OpenAI API key), the same model runs on `openai` (docs/llm-setup.md).
|
|
208
230
|
default = "openai-codex/gpt-6.1-sol"
|
|
209
231
|
conversation = "openai-codex/gpt-6.1-sol"
|
|
210
232
|
# Passes always run at sol-high — single variant, not raisable (already top).
|
|
@@ -261,7 +283,11 @@ small_diff_lines = 150
|
|
|
261
283
|
# memory forward follows this knob too — it reaches the daemon
|
|
262
284
|
# container's loopback memory engine with a plain `docker exec`
|
|
263
285
|
# (the forward script's --local mode) instead of the ssh hop.
|
|
264
|
-
#
|
|
286
|
+
# Onboarding writes this from the recorded topology. Until it has (ssh with an
|
|
287
|
+
# EMPTY ssh_host), the TUI never runs `ssh ""`: on a compose project (the
|
|
288
|
+
# checklist recorded the local topology, or the project .env pins
|
|
289
|
+
# QCR_VERSION) it reads the local stack and says so once; otherwise every
|
|
290
|
+
# surface says "ssh_host is empty — set it to the VM's ssh alias".
|
|
265
291
|
target = "ssh"
|
|
266
292
|
ssh_host = "" # ← FILL (7): ssh alias of the VM running the container
|
|
267
293
|
# (leave "" when target = "local" — it is unused)
|
|
@@ -270,10 +296,14 @@ memory_container = "" # the container the memory forward relays into
|
|
|
270
296
|
# (OpenViking is the daemon container's child). On a VM running
|
|
271
297
|
# the memory sidecar (the openviking table's endpoint =
|
|
272
298
|
# "remote") set "quickreview-memory"; flip it back with the endpoint.
|
|
273
|
-
remote_state_dir = "/opt/quickreview/state" # host path on ssh; inside the daemon container on local
|
|
299
|
+
remote_state_dir = "/opt/quickreview/state" # host path on ssh; inside the daemon container on local,
|
|
300
|
+
# where this default reads as the release image's /opt/qcr/state
|
|
301
|
+
# (the compose state volume; onboarding writes it for local)
|
|
274
302
|
remote_sandbox_root = "" # "" = read the container's qcr.layout label (release: /opt/qcr, legacy: /opt/quickreview/sandbox);
|
|
275
303
|
# a value here wins and is never probed
|
|
276
|
-
sudo_docker = true # docker on the VM needs sudo (root-owned socket)
|
|
304
|
+
sudo_docker = true # docker on the VM needs sudo (root-owned socket). On local, onboarding
|
|
305
|
+
# writes false, and the TUI drops sudo by itself when this user can open
|
|
306
|
+
# the docker socket (a sudo prompt behind the TUI can only fail)
|
|
277
307
|
poll_interval_s = 3
|
|
278
308
|
stale_after_s = 90 # DATA STALE banner past this written_at age
|
|
279
309
|
# (the daemon rewrites status.json every ~30s)
|
|
@@ -338,6 +368,19 @@ session_dir = "state/pi-sessions"
|
|
|
338
368
|
# lens_cache_stagger_ms = 2500
|
|
339
369
|
# lens_pass_deadline_min = 8
|
|
340
370
|
|
|
371
|
+
[home]
|
|
372
|
+
# The home: a bare `qcr` opens the app's own pinned pi, on your own pi login
|
|
373
|
+
# ($PI_CODING_AGENT_DIR, default ~/.pi/agent), with a panel of the
|
|
374
|
+
# repository's pull requests. Every key is optional.
|
|
375
|
+
# model — the chat's model, "provider/id". The panel counts only this
|
|
376
|
+
# model's provider as signed in: a login under another provider
|
|
377
|
+
# cannot run the chat.
|
|
378
|
+
# tools — "reviewer": the model may call the plugin's tools, the home's
|
|
379
|
+
# own home_* tools and pi's read tools (read, grep, find, ls), and
|
|
380
|
+
# no MCP server starts. "full": every tool pi has.
|
|
381
|
+
model = "openai-codex/gpt-6.1-sol"
|
|
382
|
+
tools = "reviewer"
|
|
383
|
+
|
|
341
384
|
[memory]
|
|
342
385
|
# Round seeding — how much memory rides each round prompt and how
|
|
343
386
|
# long the daemon waits for it. Memory NEVER blocks or delays a round: past
|
|
@@ -594,7 +637,10 @@ local_embedder_threads = 1
|
|
|
594
637
|
# (COMPANION_DEFAULTS) are the shipped configuration; uncomment to override.
|
|
595
638
|
# enabled = true # `qcr tui` starts it unless --no-companion
|
|
596
639
|
# container_name = "qcr-companion"
|
|
597
|
-
# image = "qcr-companion:release" # the release image (docker/Dockerfile) in companion mode, built from the app root: qcr companion rebuild
|
|
640
|
+
# image = "qcr-companion:release" # the release image (docker/Dockerfile) in companion mode, built from the app root: qcr companion rebuild.
|
|
641
|
+
# # Left unset on a compose box (the project .env pins QCR_VERSION), a first start runs the
|
|
642
|
+
# # already-pulled ghcr.io/quickchatai/quickreview:<QCR_VERSION> instead of building, unless a
|
|
643
|
+
# # local build exists; a ghcr.io/quickchatai/quickreview tag set here is pulled, never built over
|
|
598
644
|
# pihome_volume = "companion-pihome" # its OWN pi auth — never the VM's auth.json
|
|
599
645
|
# session = "companion" # tmux session inside the container
|
|
600
646
|
# model = "openai-codex/gpt-6.1-sol:medium"
|
|
@@ -859,7 +905,7 @@ extra_vendored_segments = [] # extra third-party markers (case-insensitive)
|
|
|
859
905
|
# whatever — the system BEHAVES exactly as a build without the feature.
|
|
860
906
|
# (The placeholder that renders empty leaves a blank line in the prompt, like
|
|
861
907
|
# the sibling notes beside it.)
|
|
862
|
-
#
|
|
908
|
+
# The setup wizard's escalation step writes this section.
|
|
863
909
|
#
|
|
864
910
|
# The configuration cannot be half-done: `enabled = true` with no `targets`,
|
|
865
911
|
# or a rule pointing at a login that is not a target, or a rule that declares
|
|
@@ -26,6 +26,33 @@ never as a lecture up front.
|
|
|
26
26
|
- **On a fully-finished installation** this session doubles as a diagnostic:
|
|
27
27
|
`onboard_status` re-verifies everything; report what holds and what broke.
|
|
28
28
|
|
|
29
|
+
## What the person needs, and the fast path
|
|
30
|
+
|
|
31
|
+
Say this near the start, in two or three short lines. They need three things:
|
|
32
|
+
a **model login** (pi's `/login` opens by itself the first time — any working
|
|
33
|
+
OpenAI login is fine), their **GitHub sign-in** (`gh` logged in; on a Boxes VM
|
|
34
|
+
it already is), and **one repository**. Setup ends once the defaults are in
|
|
35
|
+
place and verified; the TUI then opens on the board, which lists the
|
|
36
|
+
repository's open pull requests — they pick one there and press `R` to review
|
|
37
|
+
it.
|
|
38
|
+
|
|
39
|
+
Recommend the fast path and take its defaults unless they ask otherwise:
|
|
40
|
+
|
|
41
|
+
- **topology** — `onboard_topology` with `choice: "local"` (this computer);
|
|
42
|
+
- **GitHub** — `onboard_github_account` with no `github_auth`: where gh is
|
|
43
|
+
signed in it uses their own account by itself. Call it once to get their
|
|
44
|
+
recent repositories, then again with `repo`;
|
|
45
|
+
- **ingress** — `onboard_ingress` with no `choice`: polling is the default
|
|
46
|
+
(the reviewer checks GitHub every few minutes; webhooks can come later);
|
|
47
|
+
- **the first review** — not part of setup: after `finish_and_exit` the board
|
|
48
|
+
lists the open pull requests, and `R` on one starts its review at once, no
|
|
49
|
+
label to add.
|
|
50
|
+
|
|
51
|
+
**Never ask for something no tool takes.** Every question you put to the person
|
|
52
|
+
is a step's input (the tool's parameters say which). If a step asks for nothing,
|
|
53
|
+
do not invent a question for it — run it. When a tool returns a refusal that
|
|
54
|
+
lists options, read those options out; do not add your own.
|
|
55
|
+
|
|
29
56
|
## What you teach, when
|
|
30
57
|
|
|
31
58
|
- **At the start** (one short paragraph): QuickReview is a deterministic
|
|
@@ -42,33 +69,46 @@ never as a lecture up front.
|
|
|
42
69
|
(what the reviewer learns and keeps). Explain each only when its step is in
|
|
43
70
|
front of you.
|
|
44
71
|
|
|
45
|
-
##
|
|
72
|
+
## GitHub, in one step
|
|
73
|
+
|
|
74
|
+
`onboard_github_account` is the GitHub question. On their own account it saves
|
|
75
|
+
their token (0600, never shown — say so), installs a refresh every 20 minutes,
|
|
76
|
+
and creates the trigger labels if their account may. Read out its refresh line
|
|
77
|
+
as it is: on a machine without `crontab` it says what lapses and gives the
|
|
78
|
+
manual command. If they want a GitHub App instead, call it with
|
|
79
|
+
`github_auth: "app"`; the App steps then take over. Never call the App step on
|
|
80
|
+
the own-account route: it is skipped there.
|
|
81
|
+
|
|
82
|
+
## The last steps
|
|
46
83
|
|
|
47
84
|
- **Bring-up is slow the first time.** Say so before you run it — the tool's
|
|
48
85
|
own warning is there to be read out — and let its streamed output show. A
|
|
49
86
|
long build is expected, not a hang.
|
|
50
|
-
- **The
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
87
|
+
- **The reviewer needs its own model login.** The review sessions run in the
|
|
88
|
+
daemon's container, and a login never transfers from this machine into it.
|
|
89
|
+
Once the stack is up, the LLM auth step's `daemon_login` check names the one
|
|
90
|
+
command that opens it; ask them to run it in a second terminal, sign in, and
|
|
91
|
+
tell you — then re-run the LLM auth step. Without it the first review on the
|
|
92
|
+
board would fail at once.
|
|
93
|
+
- **The first review and go-live come after setup.** `onboard_proof_round` and
|
|
94
|
+
`onboard_go_live` are marked "after setup": setup finishes without them. Do
|
|
95
|
+
not run them here unless the person asks. Say plainly that the installation
|
|
96
|
+
stays in dry-run (every intended GitHub write is recorded, nothing is
|
|
97
|
+
posted), and that going live is their decision later, from the home
|
|
98
|
+
(Advanced → "Dry-run or live"), once a review has shown them what it says.
|
|
61
99
|
|
|
62
100
|
## Finishing
|
|
63
101
|
|
|
64
|
-
When every step reports done
|
|
65
|
-
whole checklist one last time and,
|
|
66
|
-
|
|
102
|
+
When every setup step reports done (the "after setup" ones need not be), say
|
|
103
|
+
that `finish_and_exit` will re-verify the whole checklist one last time and,
|
|
104
|
+
only if all green, close this session: the home then lists the repository's
|
|
105
|
+
pull requests, and they select one and press `alt+r` to review it now. If it
|
|
106
|
+
returns red checks, that is the answer: explain them, fix
|
|
67
107
|
what can be fixed through the step tools, and try again. Never present
|
|
68
108
|
onboarding as finished while anything is red, and never tell the operator to
|
|
69
109
|
quit around a red check.
|
|
70
110
|
|
|
71
|
-
Before `finish_and_exit`, pass on what comes next
|
|
72
|
-
|
|
73
|
-
|
|
111
|
+
Before `finish_and_exit`, pass on what comes next: the first review is `alt+r`
|
|
112
|
+
on a pull request in the home; then tune the reviewer to the repository with
|
|
113
|
+
the home's "Tune to this repo" (`t` on the panel). Until then the reviewer knows nothing about this codebase. It is a
|
|
74
114
|
recommendation, once — never start it from here.
|
|
@@ -137,6 +137,10 @@
|
|
|
137
137
|
"post": {
|
|
138
138
|
"$ref": "#/$defs/post"
|
|
139
139
|
},
|
|
140
|
+
"post_ready": {
|
|
141
|
+
"type": "string",
|
|
142
|
+
"description": "A finished PR review that was not posted: the file it is kept in, which a host that asks the person later posts through the gate exactly as `--to pr` would have."
|
|
143
|
+
},
|
|
140
144
|
"run_id": {
|
|
141
145
|
"type": "string",
|
|
142
146
|
"description": "A detached run's id (start, status, stop)."
|
|
@@ -149,6 +153,14 @@
|
|
|
149
153
|
"type": "string",
|
|
150
154
|
"description": "What a detached run reviews."
|
|
151
155
|
},
|
|
156
|
+
"started_at": {
|
|
157
|
+
"type": "number",
|
|
158
|
+
"description": "When a detached run was started, ms since the epoch."
|
|
159
|
+
},
|
|
160
|
+
"ended_at": {
|
|
161
|
+
"type": "number",
|
|
162
|
+
"description": "When a detached run ended (its exit or its stop), ms since the epoch; absent while it runs."
|
|
163
|
+
},
|
|
152
164
|
"elapsed_s": {
|
|
153
165
|
"type": "integer",
|
|
154
166
|
"minimum": 0
|
|
@@ -165,6 +177,50 @@
|
|
|
165
177
|
"items": {
|
|
166
178
|
"type": "string"
|
|
167
179
|
}
|
|
180
|
+
},
|
|
181
|
+
"live": {
|
|
182
|
+
"type": "object",
|
|
183
|
+
"description": "The running round's live log, projected: what the parent and each lens are doing now.",
|
|
184
|
+
"properties": {
|
|
185
|
+
"round": { "type": "integer", "minimum": 1 },
|
|
186
|
+
"parent_status": { "enum": ["active", "settled"] },
|
|
187
|
+
"last_activity": { "type": "string" },
|
|
188
|
+
"updated_at": { "type": "string" },
|
|
189
|
+
"active_tool": { "type": "string" },
|
|
190
|
+
"agents": {
|
|
191
|
+
"type": "array",
|
|
192
|
+
"items": {
|
|
193
|
+
"type": "object",
|
|
194
|
+
"required": ["agent", "status", "duration_ms", "tool_count"],
|
|
195
|
+
"properties": {
|
|
196
|
+
"agent": { "type": "string" },
|
|
197
|
+
"status": { "enum": ["running", "completed", "failed"] },
|
|
198
|
+
"duration_ms": {
|
|
199
|
+
"type": "number",
|
|
200
|
+
"minimum": 0,
|
|
201
|
+
"description": "The lens's time so far; a running lens's counts from its start between updates."
|
|
202
|
+
},
|
|
203
|
+
"tool_count": {
|
|
204
|
+
"type": "number",
|
|
205
|
+
"minimum": 0,
|
|
206
|
+
"description": "Tool calls the lens has started; a finished lens keeps its final count."
|
|
207
|
+
},
|
|
208
|
+
"current_tool": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"description": "The running (or last) tool, as `<tool>: <args>` or the bare name."
|
|
211
|
+
},
|
|
212
|
+
"current_target": {
|
|
213
|
+
"type": "string",
|
|
214
|
+
"description": "While the lens runs: the path its current tool works on, when it names one."
|
|
215
|
+
},
|
|
216
|
+
"line": {
|
|
217
|
+
"type": "string",
|
|
218
|
+
"description": "The lens as one line a person reads: `security · read src/x.ts · 7 tools · 0:41`."
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
168
224
|
}
|
|
169
225
|
}
|
|
170
226
|
},
|
|
@@ -234,6 +290,10 @@
|
|
|
234
290
|
"kind": {
|
|
235
291
|
"enum": ["pr", "branch", "uncommitted", "diff"],
|
|
236
292
|
"description": "What is reviewed: a pull request, the current branch against its base, the uncommitted working tree, or a unified diff handed in (`--diff`). Only a pr target can be posted."
|
|
293
|
+
},
|
|
294
|
+
"uncommitted": {
|
|
295
|
+
"const": true,
|
|
296
|
+
"description": "A branch target reviewed together with the working tree's uncommitted edits, as one review. Absent otherwise."
|
|
237
297
|
}
|
|
238
298
|
}
|
|
239
299
|
},
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type {
|
|
1
|
+
import { type ExtensionAPI, type ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { HostAuth } from "../src/plugin/login.js";
|
|
3
|
+
import { type ReviewCommandDeps } from "../src/plugin/review.js";
|
|
4
|
+
import type { Runner, SessionUi } from "../src/plugin/session.js";
|
|
3
5
|
/** What the host entry hands the plugin. */
|
|
4
6
|
export interface QuickReviewConfig {
|
|
5
7
|
/** The process environment, read once by the host entry. */
|
|
@@ -9,13 +11,19 @@ export interface QuickReviewConfig {
|
|
|
9
11
|
runner: Runner;
|
|
10
12
|
/** Standard output (pi's print mode shows a command's result there). */
|
|
11
13
|
print: (text: string) => void;
|
|
12
|
-
/** Test seams: the running pi's version, its agent dir, the range
|
|
14
|
+
/** Test seams: the running pi's version, its agent dir, the range, the
|
|
15
|
+
* review's core. */
|
|
13
16
|
piVersion?: string;
|
|
14
17
|
agentDir?: string;
|
|
15
18
|
range?: string;
|
|
19
|
+
review?: ReviewCommandDeps;
|
|
16
20
|
}
|
|
17
|
-
/** The commands,
|
|
18
|
-
export declare const QUICKREVIEW_COMMANDS: readonly ["review", "scan", "qcr-setup"
|
|
21
|
+
/** The commands, each also a skill of the same name (skills/<name>/SKILL.md). */
|
|
22
|
+
export declare const QUICKREVIEW_COMMANDS: readonly ["qcr-review", "qcr-scan", "qcr-setup"];
|
|
23
|
+
/** The host pi's auth, by the shape the plugin asks of it. */
|
|
24
|
+
export declare function hostAuth(ctx: ExtensionCommandContext, agentDir: string): HostAuth;
|
|
25
|
+
/** The session's ways to reach the person, from a command's context. */
|
|
26
|
+
export declare function sessionUi(pi: ExtensionAPI, ctx: ExtensionCommandContext, print: (t: string) => void): SessionUi;
|
|
19
27
|
/** The plugin's extension factory over `config`. */
|
|
20
28
|
export declare function createQuickReview(config: QuickReviewConfig): (pi: ExtensionAPI) => void;
|
|
21
29
|
//# sourceMappingURL=quickreview.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quickreview.d.ts","sourceRoot":"","sources":["../../extensions/quickreview.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"quickreview.d.ts","sourceRoot":"","sources":["../../extensions/quickreview.ts"],"names":[],"mappings":"AAsBA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC7B,MAAM,iCAAiC,CAAC;AAIzC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAKL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAc,MAAM,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAK9E,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B;wBACoB;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,kDAAmD,CAAC;AAErF,8DAA8D;AAC9D,wBAAgB,QAAQ,CAAC,GAAG,EAAE,uBAAuB,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CA8BjF;AAKD,wEAAwE;AACxE,wBAAgB,SAAS,CACvB,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,uBAAuB,EAC5B,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GACzB,SAAS,CA6BX;AAED,oDAAoD;AACpD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAmGvF"}
|