pagespeed-quest 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.ja.md +47 -0
  3. package/README.md +49 -0
  4. package/build/cli.d.ts +8 -0
  5. package/build/cli.js +263 -0
  6. package/build/command.js +3 -229
  7. package/build/lighthouse-summary.d.ts +3 -1
  8. package/build/lighthouse-summary.js +31 -46
  9. package/build/lighthouse.d.ts +0 -1
  10. package/build/lighthouse.js +6 -2
  11. package/build/llm.d.ts +20 -0
  12. package/build/llm.js +42 -0
  13. package/llmdocs/00-guide.md +146 -0
  14. package/llmdocs/10-inventory.md +157 -0
  15. package/llmdocs/20-experiments.md +93 -0
  16. package/llmdocs/30-measuring.md +158 -0
  17. package/llmdocs/90-commands.md +188 -0
  18. package/package.json +13 -7
  19. package/scripts/build-llm-docs.js +79 -0
  20. package/scripts/postinstall.js +2 -3
  21. package/scripts/validate-plugin-skills.js +117 -0
  22. package/build/docs/assets/css/main.css +0 -1
  23. package/build/docs/assets/images/icons.png +0 -0
  24. package/build/docs/assets/images/icons@2x.png +0 -0
  25. package/build/docs/assets/images/widgets.png +0 -0
  26. package/build/docs/assets/images/widgets@2x.png +0 -0
  27. package/build/docs/assets/js/main.js +0 -51
  28. package/build/docs/classes/inventoryrepository.html +0 -305
  29. package/build/docs/classes/playbackproxy.html +0 -524
  30. package/build/docs/classes/proxy.html +0 -474
  31. package/build/docs/classes/recordingproxy.html +0 -532
  32. package/build/docs/classes/testproxy.html +0 -497
  33. package/build/docs/classes/throttle.html +0 -425
  34. package/build/docs/classes/throttlinglog.html +0 -174
  35. package/build/docs/classes/throttlingtransform.html +0 -6297
  36. package/build/docs/globals.html +0 -1131
  37. package/build/docs/index.html +0 -348
  38. package/build/docs/interfaces/beautifyresult.html +0 -174
  39. package/build/docs/interfaces/contentencodingpair.html +0 -174
  40. package/build/docs/interfaces/inventory.html +0 -174
  41. package/build/docs/interfaces/lighthouseoptions.html +0 -230
  42. package/build/docs/interfaces/playbacktransaction.html +0 -272
  43. package/build/docs/interfaces/proxyoptions.html +0 -423
  44. package/build/docs/interfaces/recordingsession.html +0 -174
  45. package/build/docs/interfaces/recordingtransaction.html +0 -286
  46. package/build/docs/interfaces/resource.html +0 -314
  47. package/build/docs/interfaces/transaction.html +0 -258
  48. package/build/docs/interfaces/withproxyoptions.html +0 -418
  49. package/build/logger.d.ts +0 -2
  50. package/build/logger.js +0 -15
  51. package/build/proxy.d.ts +0 -35
  52. package/build/proxy.js +0 -96
  53. package/build/throttling.d.ts +0 -33
  54. package/build/throttling.js +0 -89
package/CHANGELOG.md CHANGED
@@ -2,4 +2,28 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### 0.12.0 (2026-10-11)
6
+
7
+ * **Lighthouse now reports the replay as observed** (`--throttling-method=provided`, plus `--throttling.requestLatencyMs=0`). Until now Lighthouse ran with its default `simulate` method, which re-derives every metric from its own network model and ignores the timing the proxy reproduces — reported FCP could even be shorter than the document's TTFB, and repeated runs of one recording disagreed by hundreds of milliseconds. **Scores and metrics change, and are not comparable with earlier versions.** A playback run now takes roughly 20–30 seconds, because Lighthouse waits for the page to go quiet
8
+ * Restructure the Agent Skills bundle around a whole investigation of a URL. `psq-usage` now runs record → measure → investigate → one change per trial → adopt or reject → report, with `baseline/` (never edited), `accepted/` (adopted changes) and a `quest-log.md`; new `psq-record` (recording, checking it, freezing the baseline) and `psq-measure` (several runs per state with medians and spread, reading `lighthouse.report.json` for the LCP element, waterfall, script cost and third parties, screenshot diffs)
9
+ * The skills and `llmdocs/20-experiments.md` no longer prescribe optimizations; they cover how an edit reaches the replay and what cannot be modelled, and leave the choice of changes to the agent
10
+ * Document that the report written by `psq lighthouse recording` measured the live site and must not be used as the baseline
11
+ * Fix the documented inventory layout: method directories are lower-case (`contents/get/…`), queries are URL-encoded before the extension, and `contentFilePath` is relative to the inventory directory
12
+ * Document that `method`, `url` and `ttfbMs` are required when adding a resource to `index.json`
13
+ * **Bundle rust-http-playback-proxy 0.11.1.** Recordings now carry `formatVersion: 2`: a resource you did not edit replays byte for byte as the site sent it, in the recorded time, and an edited one takes time proportional to its size on the wire at the recorded per-resource speed. Shrinking a file finally makes it arrive sooner. HTML is stored as recorded instead of reformatted, and the bytes that went over the wire are kept in `inventory/originals/` — do not edit them or the `original` field. Recordings made with earlier versions still replay as before (recorded duration whatever the size); re-record them to get size-proportional timing
14
+ * The *Traffic* table in `summary.md` is now what the browser received in the run (Lighthouse's `network-requests`), not an estimate from fixed compression ratios; *Resource* is the decoded size from the same source
15
+ * Windows (x64) gets a playback proxy built for this release; until now the npm package carried a v0.3.0 binary. **Intel Macs are no longer supported**: postinstall stops with `Unsupported platform` instead of downloading arm64 binaries that cannot run there
16
+ * Fix the documentation of inline `contentUtf8` / `contentBase64`: they are used only when there is no `contentFilePath`, not in preference to the file
17
+
18
+ ### 0.11.0 (2026-08-16)
19
+
20
+ * Add `psq llm` — the embedded reference for AI agents (`--format markdown|json`), sourced from `llmdocs/` and shipped with the package
21
+ * Add `psq --version`
22
+ * Ship an Agent Skills bundle (`plugins/pagespeed-quest/`): `psq-install`, `psq-usage`, `psq-inventory`, `psq-video`, installable through the ideamans Claude Code marketplace or `gh skill`
23
+ * Add `context7.json` so agents with the context7 MCP server can look the project up
24
+ * Generate `llmdocs/90-commands.md` from the actual command tree (`yarn ai:regen`), and fail the test suite when it is stale or when the skill bundle drifts
25
+ * Extract the command tree into `src/cli.ts` (`createProgram()`); `src/command.ts` is now only the bin entry point
26
+ * Publish to npm on a `v*` tag push instead of on every push to `main`, verifying the tag against `package.json` and the plugin manifest
27
+ * Require Node.js 22.19 or newer (Lighthouse 13's floor); CI runs 22.x and 24.x
28
+
5
29
  ### 0.2.1 (2024-09-11)
package/README.ja.md CHANGED
@@ -136,6 +136,53 @@ yarn psq proxy -p 8080 --record https://example.com/
136
136
  /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --ignore-certificate-errors --proxy-server=http://localhost:8080
137
137
  ```
138
138
 
139
+ ## AIエージェントから使う
140
+
141
+ PageSpeed Quest は、AIエージェント向けのリファレンスを自分で持っています。
142
+ 鉄則・調査の進め方・インベントリのスキーマ・再生時のタイミングの計算式・
143
+ 編集が再生にどう効くか・計測結果の読み方が、1コマンドで全部出ます。
144
+
145
+ ```sh
146
+ yarn psq llm # Markdown(既定)
147
+ yarn psq llm --format json # 章ごとのJSON配列
148
+ ```
149
+
150
+ インストールされたパッケージに同梱されているので、**実行中のバージョンと必ず
151
+ 一致します**(ドキュメントサイトと違って古くなりません)。
152
+
153
+ ### Claude Code / gh skill
154
+
155
+ `plugins/pagespeed-quest/` に Agent Skills を同梱しています。
156
+
157
+ | スキル | 内容 |
158
+ | --- | --- |
159
+ | `psq-usage` | URL の調査を通しで行う。記録 → 計測 → ボトルネック調査 → 1つずつ試す → 効いたものを採用 → 改善幅を報告 |
160
+ | `psq-install` | `psq` が動く状態のプロジェクトを用意する |
161
+ | `psq-record` | ページを記録し、記録を点検し、ベースラインとして凍結する |
162
+ | `psq-measure` | Lighthouse の複数回計測と中央値、レポートからボトルネックを読む、スクリーンショット比較 |
163
+ | `psq-inventory` | 記録への編集が再生にどう効くか |
164
+ | `psq-video` | 読み込み過程のビフォーアフター動画 |
165
+
166
+ スキルが教えるのはツールの使い方で、Web 高速化の施策ではありません。
167
+ 何を改善するかはエージェントの判断に任せています。
168
+
169
+ Claude Code の場合:
170
+
171
+ ```sh
172
+ /plugin marketplace add ideamans/claude-public-plugins
173
+ /plugin install pagespeed-quest
174
+ ```
175
+
176
+ Copilot / Cursor / Gemini CLI / Codex などは同じ SKILL.md を `gh skill` で
177
+ 入れられます。
178
+
179
+ ```sh
180
+ gh skill install ideamans/pagespeed-quest/plugins/pagespeed-quest/skills/psq-usage --agent copilot
181
+ ```
182
+
183
+ [context7](https://context7.com/) にも登録しているので、context7 MCP を繋いだ
184
+ エージェントは何も入れずに参照できます。
185
+
139
186
  ## 開発環境の共有やトレーニングにも
140
187
 
141
188
  PageSpeed Questは、Webアプリケーションのリリースを必要としないスピーディーな仮説検証以外にも有用です。
package/README.md CHANGED
@@ -136,6 +136,55 @@ However, since this HTTP proxy uses a dummy SSL certificate, please disable the
136
136
  /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --ignore-certificate-errors --proxy-server=http://localhost:8080
137
137
  ```
138
138
 
139
+ ## Driving it with an AI agent
140
+
141
+ PageSpeed Quest ships its own reference for AI agents. One command prints
142
+ everything an agent needs — the ground rules, the investigation loop, the
143
+ inventory schema, the timing model, how an edit reaches the replay, and how to
144
+ read the measurements:
145
+
146
+ ```sh
147
+ yarn psq llm # Markdown (default)
148
+ yarn psq llm --format json # one object per chapter
149
+ ```
150
+
151
+ The reference is embedded in the installed package, so it always matches the
152
+ version you are running.
153
+
154
+ ### Claude Code / gh skill
155
+
156
+ The repository also ships an Agent Skills bundle under
157
+ `plugins/pagespeed-quest/`:
158
+
159
+ | Skill | What it covers |
160
+ | --- | --- |
161
+ | `psq-usage` | the whole investigation of a URL: record, measure, find bottlenecks, try changes one at a time, adopt what helps, report the gain |
162
+ | `psq-install` | setting up a quest project so `psq` runs |
163
+ | `psq-record` | recording a page, checking the recording, freezing the baseline |
164
+ | `psq-measure` | repeated Lighthouse runs and medians, reading the report for bottlenecks, screenshot diffs |
165
+ | `psq-inventory` | how an edit to the recording reaches the replay |
166
+ | `psq-video` | before/after videos of the load |
167
+
168
+ The skills teach the tool, not web performance: choosing what to optimize is
169
+ left to the agent.
170
+
171
+ For Claude Code, add the ideamans marketplace and install the plugin:
172
+
173
+ ```sh
174
+ /plugin marketplace add ideamans/claude-public-plugins
175
+ /plugin install pagespeed-quest
176
+ ```
177
+
178
+ For other agents (Copilot, Cursor, Gemini CLI, Codex), the same skills install
179
+ through `gh skill`:
180
+
181
+ ```sh
182
+ gh skill install ideamans/pagespeed-quest/plugins/pagespeed-quest/skills/psq-usage --agent copilot
183
+ ```
184
+
185
+ The project is also registered with [context7](https://context7.com/), so agents
186
+ with the context7 MCP server can look it up without installing anything.
187
+
139
188
  ## For Sharing Development Environments and Training
140
189
 
141
190
  PageSpeed Quest is useful not only for speedy hypothesis verification without the need for a Web application release but also for other purposes.
package/build/cli.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * Build the `psq` command tree.
4
+ *
5
+ * Exported so that scripts/build-llm-docs.js can walk it to generate the
6
+ * command catalog (llmdocs/90-commands.md) instead of hand-maintaining it.
7
+ */
8
+ export declare function createProgram(): Command;
package/build/cli.js ADDED
@@ -0,0 +1,263 @@
1
+ import Path from 'path';
2
+ import { Command } from 'commander';
3
+ import Watch from 'node-watch';
4
+ import { Dependency } from './dependency.js';
5
+ import { execLighthouse } from './lighthouse.js';
6
+ import { packageVersion, renderLlmDocs } from './llm.js';
7
+ import { execLoadshow } from './loadshow.js';
8
+ import { execWebshotCapture, execWebshotCompare } from './webshot.js';
9
+ import { InventoryRepository, withPlaybackProxy, withRecordingProxy } from './index.js';
10
+ /**
11
+ * The dependency container is created lazily.
12
+ *
13
+ * Building it eagerly would spawn the pino-pretty transport worker as a side
14
+ * effect of importing this module, which breaks tools that only want to walk
15
+ * the command tree (see scripts/build-llm-docs.js).
16
+ */
17
+ let dependencyInstance;
18
+ function dependency() {
19
+ if (!dependencyInstance)
20
+ dependencyInstance = new Dependency();
21
+ return dependencyInstance;
22
+ }
23
+ function collect(value, previous) {
24
+ return previous.concat([value]);
25
+ }
26
+ function registerLighthouseCommands(main) {
27
+ const lighthouse = main.command('lighthouse');
28
+ lighthouse.description('Run Lighthouse (performance category) via a proxy');
29
+ lighthouse.option('-a, --artifacts <dir>', 'Artifacts directory', './artifacts');
30
+ lighthouse.option('-l, --laud', 'Show browser and open the report after completion', false);
31
+ lighthouse.option('-t, --timeout <ms>', 'Timeout milliseconds', '30000');
32
+ const recording = lighthouse.command('recording');
33
+ recording.description('Record contents by lighthouse');
34
+ recording.option('-d, --device <mobile|desktop>', 'Device type', 'mobile');
35
+ recording.option('-x, --exclude <pattern>', 'Regex patterns to exclude URLs from recording (repeatable)', collect, []);
36
+ recording.argument('<url>', 'Url to measure performance');
37
+ recording.action(async (url) => {
38
+ const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
39
+ const deviceType = recording.opts().device || 'mobile';
40
+ const artifactsDir = lighthouse.opts().artifacts || './artifacts';
41
+ const laud = !!lighthouse.opts().laud;
42
+ const timeout = Number(lighthouse.opts().timeout || '30000');
43
+ const excludePatterns = recording.opts().exclude || [];
44
+ await withRecordingProxy({
45
+ entryUrl: url,
46
+ deviceType,
47
+ inventoryRepository,
48
+ excludePatterns: excludePatterns.length > 0 ? excludePatterns : undefined,
49
+ }, dependency(), async (proxy) => {
50
+ await execLighthouse({
51
+ url,
52
+ proxyPort: proxy.port,
53
+ deviceType,
54
+ view: laud,
55
+ artifactsDir,
56
+ headless: !laud,
57
+ timeout,
58
+ }, dependency());
59
+ dependency().logger?.info('Lighthouse completed. Saving inventory...');
60
+ });
61
+ });
62
+ const playback = lighthouse.command('playback');
63
+ playback.description('Playback contents for lighthouse');
64
+ playback.action(async () => {
65
+ const inventoryDir = main.opts().inventory || './inventory';
66
+ const inventoryRepository = new InventoryRepository(inventoryDir);
67
+ const artifactsDir = lighthouse.opts().artifacts || './artifacts';
68
+ const laud = !!lighthouse.opts().laud;
69
+ const timeout = Number(lighthouse.opts().timeout || '30000');
70
+ // Load inventory for traffic statistics
71
+ const inventory = await inventoryRepository.loadInventory();
72
+ await withPlaybackProxy({
73
+ inventoryRepository,
74
+ }, dependency(), async (proxy) => {
75
+ await execLighthouse({
76
+ url: proxy.entryUrl,
77
+ proxyPort: proxy.port,
78
+ deviceType: proxy.deviceType,
79
+ view: laud,
80
+ artifactsDir,
81
+ headless: !laud,
82
+ timeout,
83
+ resources: inventory.resources,
84
+ }, dependency());
85
+ dependency().logger?.info('Lighthouse completed');
86
+ });
87
+ });
88
+ }
89
+ function registerLoadshowCommands(main) {
90
+ const loadshow = main.command('loadshow');
91
+ loadshow.description('Run loadshow via a proxy');
92
+ loadshow.option('-a, --artifacts <dir>', 'Artifacts directory', './artifacts');
93
+ loadshow.option('-c, --credit <string>', 'Credit string');
94
+ loadshow.option('-t, --timeout <ms>', 'Timeout milliseconds', '30000');
95
+ const recording = loadshow.command('recording');
96
+ recording.description('Record contents by loadshow');
97
+ recording.option('-d, --device <mobile|desktop>', 'Device type', 'mobile');
98
+ recording.option('-x, --exclude <pattern>', 'Regex patterns to exclude URLs from recording (repeatable)', collect, []);
99
+ recording.argument('<url>', 'Url to measure performance');
100
+ recording.action(async (url) => {
101
+ const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
102
+ const deviceType = recording.opts().device || 'mobile';
103
+ const artifactsDir = loadshow.opts().artifacts || './artifacts';
104
+ const credit = loadshow.opts().credit || '';
105
+ const timeout = Number(loadshow.opts().timeout || '30000');
106
+ const excludePatterns = recording.opts().exclude || [];
107
+ await withRecordingProxy({
108
+ entryUrl: url,
109
+ deviceType,
110
+ inventoryRepository,
111
+ excludePatterns: excludePatterns.length > 0 ? excludePatterns : undefined,
112
+ }, dependency(), async (proxy) => {
113
+ await execLoadshow({ url, proxyPort: proxy.port, deviceType, artifactsDir, credit, timeout }, dependency());
114
+ dependency().logger?.info('Loadshow completed. Saving inventory...');
115
+ });
116
+ });
117
+ const playback = loadshow.command('playback');
118
+ playback.description('Playback contents for loadshow');
119
+ playback.action(async () => {
120
+ const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
121
+ const artifactsDir = loadshow.opts().artifacts || './artifacts';
122
+ const credit = loadshow.opts().credit || '';
123
+ const timeout = Number(loadshow.opts().timeout || '30000');
124
+ await withPlaybackProxy({
125
+ inventoryRepository,
126
+ }, dependency(), async (proxy) => {
127
+ await execLoadshow({
128
+ url: proxy.entryUrl,
129
+ proxyPort: proxy.port,
130
+ deviceType: proxy.deviceType,
131
+ artifactsDir,
132
+ credit,
133
+ timeout,
134
+ }, dependency());
135
+ dependency().logger?.info('Loadshow completed');
136
+ });
137
+ });
138
+ }
139
+ function registerProxyCommands(main) {
140
+ const proxy = main.command('proxy');
141
+ proxy.description('Run the playback (or recording) proxy standalone');
142
+ proxy.option('-p, --port <number>', 'Proxy port', '8080');
143
+ proxy.option('-r, --record <url>', 'Recording URL to start the proxy as recording mode', '');
144
+ proxy.option('-x, --exclude <pattern>', 'Regex patterns to exclude URLs from recording (repeatable)', collect, []);
145
+ proxy.action(async () => {
146
+ const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
147
+ const port = Number(proxy.opts().port || '8080');
148
+ if (proxy.opts().record) {
149
+ const url = proxy.opts().record;
150
+ if (!url) {
151
+ throw new Error('Recording URL must be specified with --record option.');
152
+ }
153
+ const excludePatterns = proxy.opts().exclude || [];
154
+ // Recordingモード
155
+ await withRecordingProxy({
156
+ inventoryRepository,
157
+ port,
158
+ entryUrl: url,
159
+ excludePatterns: excludePatterns.length > 0 ? excludePatterns : undefined,
160
+ }, dependency(), async () => {
161
+ dependency().logger?.info(`Recording proxy started on port ${port}. Press Ctrl+C to stop.`);
162
+ // Wait for Ctrl+C signal
163
+ return new Promise((resolve) => {
164
+ process.on('SIGINT', () => {
165
+ dependency().logger?.info('Saving the inventory...');
166
+ resolve();
167
+ });
168
+ });
169
+ });
170
+ }
171
+ else {
172
+ // Playbackモード
173
+ // eslint-disable-next-line no-constant-condition
174
+ while (true) {
175
+ await withPlaybackProxy({ inventoryRepository, port }, dependency(), async () => {
176
+ const watcher = Watch(inventoryRepository.dirPath, { recursive: true });
177
+ return new Promise((ok) => {
178
+ watcher.on('change', () => {
179
+ watcher.close();
180
+ dependency().logger?.info('Inventory changed. Restarting proxy...');
181
+ ok();
182
+ });
183
+ });
184
+ });
185
+ }
186
+ }
187
+ });
188
+ }
189
+ function registerCaptureCommands(main) {
190
+ const visualTest = main.command('capture');
191
+ visualTest.description('Capture a screenshot via playback proxy and optionally compare with a baseline');
192
+ visualTest.option('-a, --artifacts <dir>', 'Artifacts directory', './artifacts');
193
+ visualTest.option('-t, --timeout <ms>', 'Timeout milliseconds', '30000');
194
+ visualTest.option('--compare <file>', 'Baseline PNG file to compare against');
195
+ visualTest.option('--baseline-label <label>', 'Label for the baseline image', '前回');
196
+ visualTest.option('--current-label <label>', 'Label for the current image', '今回');
197
+ visualTest.action(async () => {
198
+ const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
199
+ const artifactsDir = visualTest.opts().artifacts || './artifacts';
200
+ const timeout = Number(visualTest.opts().timeout || '30000');
201
+ const compareFile = visualTest.opts().compare;
202
+ const baselineLabel = visualTest.opts().baselineLabel || '前回';
203
+ const currentLabel = visualTest.opts().currentLabel || '今回';
204
+ await withPlaybackProxy({
205
+ inventoryRepository,
206
+ fullThrottle: true,
207
+ }, dependency(), async (proxy) => {
208
+ const outputPath = Path.join(artifactsDir, 'capture.png');
209
+ await dependency().mkdirp(artifactsDir);
210
+ await execWebshotCapture({
211
+ url: proxy.entryUrl,
212
+ proxyPort: proxy.port,
213
+ preset: proxy.deviceType === 'desktop' ? 'desktop' : 'mobile',
214
+ output: outputPath,
215
+ timeout,
216
+ }, dependency());
217
+ dependency().logger?.info(`Screenshot saved to ${outputPath}`);
218
+ if (compareFile) {
219
+ const diffOutput = Path.join(artifactsDir, 'capture-diff.png');
220
+ const digestTxt = Path.join(artifactsDir, 'capture-diff.txt');
221
+ await execWebshotCompare({
222
+ baseline: compareFile,
223
+ current: outputPath,
224
+ output: diffOutput,
225
+ digestTxt,
226
+ baselineLabel,
227
+ currentLabel,
228
+ }, dependency());
229
+ dependency().logger?.info(`Diff image saved to ${diffOutput}`);
230
+ dependency().logger?.info(`Diff digest saved to ${digestTxt}`);
231
+ }
232
+ });
233
+ });
234
+ }
235
+ function registerLlmCommands(main) {
236
+ const llm = main.command('llm');
237
+ llm.description('Print the embedded reference for AI agents');
238
+ llm.option('-f, --format <markdown|json>', 'Output format', 'markdown');
239
+ llm.action(async () => {
240
+ const format = llm.opts().format === 'json' ? 'json' : 'markdown';
241
+ process.stdout.write(await renderLlmDocs(format));
242
+ });
243
+ }
244
+ /**
245
+ * Build the `psq` command tree.
246
+ *
247
+ * Exported so that scripts/build-llm-docs.js can walk it to generate the
248
+ * command catalog (llmdocs/90-commands.md) instead of hand-maintaining it.
249
+ */
250
+ export function createProgram() {
251
+ const main = new Command();
252
+ main.name('psq');
253
+ main.version(packageVersion(), '-v, --version', 'Output the version number');
254
+ main.description('Record a web page through a proxy, edit the recording, and replay it to measure the difference');
255
+ main.option('-i, --inventory <dir>', 'Inventory directory', './inventory');
256
+ registerLighthouseCommands(main);
257
+ registerLoadshowCommands(main);
258
+ registerProxyCommands(main);
259
+ registerCaptureCommands(main);
260
+ registerLlmCommands(main);
261
+ return main;
262
+ }
263
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLElBQUksTUFBTSxNQUFNLENBQUE7QUFFdkIsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUNuQyxPQUFPLEtBQUssTUFBTSxZQUFZLENBQUE7QUFFOUIsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBQzVDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUNoRCxPQUFPLEVBQUUsY0FBYyxFQUFFLGFBQWEsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUN4RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBRTVDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGNBQWMsQ0FBQTtBQUVyRSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFFdkY7Ozs7OztHQU1HO0FBQ0gsSUFBSSxrQkFBMEMsQ0FBQTtBQUU5QyxTQUFTLFVBQVU7SUFDakIsSUFBSSxDQUFDLGtCQUFrQjtRQUFFLGtCQUFrQixHQUFHLElBQUksVUFBVSxFQUFFLENBQUE7SUFDOUQsT0FBTyxrQkFBa0IsQ0FBQTtBQUMzQixDQUFDO0FBRUQsU0FBUyxPQUFPLENBQUMsS0FBYSxFQUFFLFFBQWtCO0lBQ2hELE9BQU8sUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUE7QUFDakMsQ0FBQztBQUVELFNBQVMsMEJBQTBCLENBQUMsSUFBYTtJQUMvQyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFBO0lBQzdDLFVBQVUsQ0FBQyxXQUFXLENBQUMsbURBQW1ELENBQUMsQ0FBQTtJQUMzRSxVQUFVLENBQUMsTUFBTSxDQUFD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