Review Git changes interactively in VS Code

worktree · HEAD@d5ba7ce → working-tree · GPT-6 (Codex)

Open saved whymark reviews or compare staged, unstaged, all-working, branch and commit changes inside VS Code. The bundled custom editor reuses the review UI without a server. Source edits are hash-checked, undoable editor changes and never change the index. New comparisons retain explanation stubs, and imported verification is not silently trusted. The VSIX is installable locally; platform and test limitations are recorded in the delivery evidence.

Execution evidence · 36 records

Author confidence and passing claims are not measured accuracy. Records are unauthenticated; source hashes establish freshness, not who ran a command.

pass · imported, freshness unchecked · npm test
Recorded: 2026-09-17T09:54:54.865Z · 5081ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 5.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/27a6d7eee36480db100af68478caa772b1b6daf4b5d6fab76d546f7e791eba5c.log · SHA-256 27a6d7eee36480db100af68478caa772b1b6daf4b5d6fab76d546f7e791eba5c
pass · imported, freshness unchecked · npm run typecheck
Recorded: 2026-09-17T09:54:54.868Z · 1145ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02.log · SHA-256 4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02
pass · imported, freshness unchecked · npm run lint
Recorded: 2026-09-17T09:54:54.870Z · 3018ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 3.0s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/12a7d4451b2edec6f0c59ab46c9523bfbefc2994173ad529ae49df3148c244e1.log · SHA-256 12a7d4451b2edec6f0c59ab46c9523bfbefc2994173ad529ae49df3148c244e1
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.872Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · WHYMARK_VSIX=.artifacts/whymark-vscode.vsix npm run test:vscode
Recorded: 2026-09-17T09:54:54.874Z · 4224ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 4.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84.log · SHA-256 b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84
pass · imported, freshness unchecked · node tests/e2e/vscode.mjs
Recorded: 2026-09-17T09:54:54.876Z · 2219ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 2.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/d5549a13ad8c6c3b836fce233a6c0feb694d7a4432cc398a4418fa83ec15ee9c.log · SHA-256 d5549a13ad8c6c3b836fce233a6c0feb694d7a4432cc398a4418fa83ec15ee9c
pass · imported, freshness unchecked · npm run test:ui
Recorded: 2026-09-17T09:54:54.878Z · 45962ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 46.0s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234.log · SHA-256 7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234
pass · imported, freshness unchecked · npm run build -- --webpack
Recorded: 2026-09-17T09:54:54.881Z · 10716ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 10.7s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/8316d69a638d6c66e0304e0e31364d18608a6d4b05b404767119928a79d8a62d.log · SHA-256 8316d69a638d6c66e0304e0e31364d18608a6d4b05b404767119928a79d8a62d
pass · imported, freshness unchecked · npm run typecheck
Recorded: 2026-09-17T09:54:54.883Z · 1145ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02.log · SHA-256 4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.885Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.887Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.889Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · npm run test:ui
Recorded: 2026-09-17T09:54:54.891Z · 45962ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 46.0s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234.log · SHA-256 7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234
pass · imported, freshness unchecked · npm run test:ui
Recorded: 2026-09-17T09:54:54.893Z · 45962ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 46.0s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234.log · SHA-256 7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234
pass · imported, freshness unchecked · npm run test:ui
Recorded: 2026-09-17T09:54:54.895Z · 45962ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 46.0s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234.log · SHA-256 7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234
pass · imported, freshness unchecked · npm run test:ui
Recorded: 2026-09-17T09:54:54.897Z · 45962ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 46.0s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234.log · SHA-256 7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.898Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.900Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.902Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · npm run typecheck
Recorded: 2026-09-17T09:54:54.904Z · 1145ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02.log · SHA-256 4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.906Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · npm run package:vscode
Recorded: 2026-09-17T09:54:54.908Z · 1409ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.4s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4.log · SHA-256 b8e2ae5f7b736afce5e6e9f4d4d9c30d6b21ca06cbec36838c37a47c8d0bd2b4
pass · imported, freshness unchecked · WHYMARK_VSIX=.artifacts/whymark-vscode.vsix npm run test:vscode
Recorded: 2026-09-17T09:54:54.910Z · 4224ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 4.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84.log · SHA-256 b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84
pass · imported, freshness unchecked · WHYMARK_VSIX=.artifacts/whymark-vscode.vsix npm run test:vscode
Recorded: 2026-09-17T09:54:54.912Z · 4224ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 4.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84.log · SHA-256 b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84
pass · imported, freshness unchecked · npm test
Recorded: 2026-09-17T09:54:54.914Z · 5081ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 5.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/27a6d7eee36480db100af68478caa772b1b6daf4b5d6fab76d546f7e791eba5c.log · SHA-256 27a6d7eee36480db100af68478caa772b1b6daf4b5d6fab76d546f7e791eba5c
pass · imported, freshness unchecked · npm test
Recorded: 2026-09-17T09:54:54.916Z · 5081ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 5.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/27a6d7eee36480db100af68478caa772b1b6daf4b5d6fab76d546f7e791eba5c.log · SHA-256 27a6d7eee36480db100af68478caa772b1b6daf4b5d6fab76d546f7e791eba5c
pass · imported, freshness unchecked · WHYMARK_VSIX=.artifacts/whymark-vscode.vsix npm run test:vscode
Recorded: 2026-09-17T09:54:54.918Z · 4224ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 4.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84.log · SHA-256 b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84
pass · imported, freshness unchecked · node tests/e2e/vscode.mjs
Recorded: 2026-09-17T09:54:54.920Z · 2219ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 2.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/d5549a13ad8c6c3b836fce233a6c0feb694d7a4432cc398a4418fa83ec15ee9c.log · SHA-256 d5549a13ad8c6c3b836fce233a6c0feb694d7a4432cc398a4418fa83ec15ee9c
pass · imported, freshness unchecked · node tests/e2e/vscode.mjs
Recorded: 2026-09-17T09:54:54.922Z · 2219ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 2.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/d5549a13ad8c6c3b836fce233a6c0feb694d7a4432cc398a4418fa83ec15ee9c.log · SHA-256 d5549a13ad8c6c3b836fce233a6c0feb694d7a4432cc398a4418fa83ec15ee9c
pass · imported, freshness unchecked · WHYMARK_VSIX=.artifacts/whymark-vscode.vsix npm run test:vscode
Recorded: 2026-09-17T09:54:54.924Z · 4224ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 4.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84.log · SHA-256 b16adb5ab8715bf1575d347cb8ad00636c02524397121f11e629c915b62b6f84
pass · imported, freshness unchecked · npm run typecheck
Recorded: 2026-09-17T09:54:54.925Z · 1145ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02.log · SHA-256 4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02
pass · imported, freshness unchecked · npm run typecheck
Recorded: 2026-09-17T09:54:54.927Z · 1145ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 1.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02.log · SHA-256 4c790a1d26481d92f95c3d3ac6896b13e103b378ae67a82aa4172b5d0eae9b02
pass · imported, freshness unchecked · npm run test:ui
Recorded: 2026-09-17T09:54:54.929Z · 45962ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 46.0s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234.log · SHA-256 7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234
pass · imported, freshness unchecked · npm run test:ui
Recorded: 2026-09-17T09:54:54.931Z · 45962ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 46.0s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234.log · SHA-256 7330f534a508d4fdb05640ce824f610af4fd85baad88025d74ad041166e6d234
pass · imported, freshness unchecked · node tests/e2e/vscode.mjs
Recorded: 2026-09-17T09:54:54.933Z · 2219ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 2.2s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/d5549a13ad8c6c3b836fce233a6c0feb694d7a4432cc398a4418fa83ec15ee9c.log · SHA-256 d5549a13ad8c6c3b836fce233a6c0feb694d7a4432cc398a4418fa83ec15ee9c
pass · imported, freshness unchecked · npm test
Recorded: 2026-09-17T09:54:54.935Z · 5081ms · exit 0
whymark@0.3.0 · v22.23.2 · cwd .
exit 0 in 5.1s
Source: 102e2a7a4e17c3c62892eb4cbda4db7bff0b4aad1c3bed359288a4e84868476d
Output: .artifacts/whymark/27a6d7eee36480db100af68478caa772b1b6daf4b5d6fab76d546f7e791eba5c.log · SHA-256 27a6d7eee36480db100af68478caa772b1b6daf4b5d6fab76d546f7e791eba5c
annotations28 of 28 shown · j k to step through

README.md

+15 0read-only10%
intentunspecifiedconfidence unknown
@@ -351,3 +351,18 @@accuracy. Fingerprints exclude ignored files, `.artifacts/`, and review outputs
351351 bytes or external services. Inspect output logs before sharing. No command runs just
352352 by opening a review. Optional Knip, Semgrep, audit and coverage adapters remain future
353353 integrations; the current quality adapter is ESLint plus configured pass/fail checks.
354+ 
355+## VS Code extension
356+ 
357+Open `.whymark` reviews interactively in VS Code, or compare staged, unstaged, all
358+working-tree, branch and commit changes from the Command Palette. No dev server is
359+required. Review comments, filter evidence, switch split/unified views, and apply
360+undoable edits to source buffers without changing the Git index.
361+ 
362+```sh
363+npm run package:vscode
364+code --install-extension .artifacts/whymark-vscode.vsix
365+```
366+ 
367+See [extension usage and safety boundaries](extensions/vscode/README.md). The VSIX
368+is local and self-contained; it has not been published to the Marketplace.

eslint.config.mjs

+1 0read-only10%
intentunspecifiedconfidence unknown
@@ -12,6 +12,7 @@const eslintConfig = defineConfig([
1212 "out/**",
1313 "build/**",
1414 "dist/**",
15+ "extensions/vscode/dist/**",
1516 "next-env.d.ts",
1617 ]),
1718 ]);

package-lock.json

+1670 192read-only10%
generatedunspecifiedconfidence unknown
@@ -1,12 +1,12 @@
11 {
22 "name": "whymark",
3 "version": "0.1.1",
3+ "version": "0.3.0",
44 "lockfileVersion": 3,
55 "requires": true,
66 "packages": {
77 "": {
88 "name": "whymark",
9 "version": "0.1.1",
9+ "version": "0.3.0",
1010 "license": "MIT",
1111 "dependencies": {
1212 "yaml": "^2.8.1"
@@ -23,7 +23,10 @@
2323 "@types/node": "^22",
2424 "@types/react": "^19",
2525 "@types/react-dom": "^19",
26+ "@types/vscode": "^1.96.0",
2627 "@vercel/analytics": "^2.0.1",
28+ "@vscode/test-electron": "^3.1.0",
29+ "@vscode/vsce": "^4.0.0",
2730 "class-variance-authority": "^0.7.1",
2831 "clsx": "^2.1.1",
2932 "cn": "^0.3.0",
@@ -33,6 +36,7 @@
3336 "lucide-react": "^0.545.0",
3437 "next": "16.3.5",
3538 "playwright": "^1.63.0",
39+ "postcss": "^8.5.28",
3640 "react": "19.2.8",
3741 "react-dom": "19.2.8",
3842 "shadcn": "^4.21.0",
@@ -61,6 +65,220 @@
6165 "url": "https://github.com/sponsors/sindresorhus"
6266 }
6367 },
68+ "node_modules/@azure/abort-controller": {
69+ "version": "2.2.0",
70+ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.2.0.tgz",
71+ "integrity": "sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==",
72+ "dev": true,
73+ "license": "MIT",
74+ "dependencies": {
75+ "tslib": "^2.6.2"
76+ },
77+ "engines": {
78+ "node": ">=22.0.0"
79+ }
80+ },
81+ "node_modules/@azure/core-auth": {
82+ "version": "1.11.0",
83+ "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.11.0.tgz",
84+ "integrity": "sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==",
85+ "dev": true,
86+ "license": "MIT",
87+ "dependencies": {
88+ "@azure/abort-controller": "^2.1.2",
89+ "@azure/core-util": "^1.13.0",
90+ "tslib": "^2.6.2"
91+ },
92+ "engines": {
93+ "node": ">=22.0.0"
94+ }
95+ },
96+ "node_modules/@azure/core-client": {
97+ "version": "1.11.1",
98+ "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.11.1.tgz",
99+ "integrity": "sha512-2QygG2F76ZpMP2eMztiJvAiFMu71M9rDeU7vO/QKg5Css7MgM4frUOslFjhVjRhbGaCNPtz/S8M6y46/fFKVuQ==",
100+ "dev": true,
101+ "license": "MIT",
102+ "dependencies": {
103+ "@azure/abort-controller": "^2.1.2",
104+ "@azure/core-auth": "^1.10.0",
105+ "@azure/core-rest-pipeline": "^1.22.0",
106+ "@azure/core-tracing": "^1.3.0",
107+ "@azure/core-util": "^1.13.0",
108+ "@azure/logger": "^1.3.0",
109+ "tslib": "^2.6.2"
110+ },
111+ "engines": {
112+ "node": ">=22.0.0"
113+ }
114+ },
115+ "node_modules/@azure/core-process": {
116+ "version": "1.0.0",
117+ "resolved": "https://registry.npmjs.org/@azure/core-process/-/core-process-1.0.0.tgz",
118+ "integrity": "sha512-/shnJ+ooO8WPxDhPEeI/2oRQuubn16gZ6CvlbpWbEswZfzwI9tI/sMAHmF3x1LuQ9yZYXfLW3TjzGMLEC5blKg==",
119+ "dev": true,
120+ "license": "MIT",
121+ "engines": {
122+ "node": ">=22.0.0"
123+ }
124+ },
125+ "node_modules/@azure/core-rest-pipeline": {
126+ "version": "1.25.0",
127+ "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz",
128+ "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==",
129+ "dev": true,
130+ "license": "MIT",
131+ "dependencies": {
132+ "@azure/abort-controller": "^2.1.2",
133+ "@azure/core-auth": "^1.10.0",
134+ "@azure/core-tracing": "^1.3.0",
135+ "@azure/core-util": "^1.13.0",
136+ "@azure/logger": "^1.3.0",
137+ "@typespec/ts-http-runtime": "^0.3.4",
138+ "tslib": "^2.6.2"
139+ },
140+ "engines": {
141+ "node": ">=22.0.0"
142+ }
143+ },
144+ "node_modules/@azure/core-tracing": {
145+ "version": "1.4.0",
146+ "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.4.0.tgz",
147+ "integrity": "sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==",
148+ "dev": true,
149+ "license": "MIT",
150+ "dependencies": {
151+ "tslib": "^2.6.2"
152+ },
153+ "engines": {
154+ "node": ">=22.0.0"
155+ }
156+ },
157+ "node_modules/@azure/core-util": {
158+ "version": "1.14.0",
159+ "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.14.0.tgz",
160+ "integrity": "sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==",
161+ "dev": true,
162+ "license": "MIT",
163+ "dependencies": {
164+ "@azure/abort-controller": "^2.1.2",
165+ "@typespec/ts-http-runtime": "^0.3.0",
166+ "tslib": "^2.6.2"
167+ },
168+ "engines": {
169+ "node": ">=22.0.0"
170+ }
171+ },
172+ "node_modules/@azure/identity": {
173+ "version": "4.13.3",
174+ "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.3.tgz",
175+ "integrity": "sha512-zGQPtqvXPgSA8yfV2CkIQ1qirqk0p9AIVpC5uEkdXQYcKl07QHvyaGYRnZOk0AsQUmxNb4wfkcwY5di8Z5xa9A==",
176+ "dev": true,
177+ "license": "MIT",
178+ "dependencies": {
179+ "@azure/abort-controller": "^2.0.0",
180+ "@azure/core-auth": "^1.9.0",
181+ "@azure/core-client": "^1.9.2",
182+ "@azure/core-process": "^1.0.0",
183+ "@azure/core-rest-pipeline": "^1.17.0",
184+ "@azure/core-tracing": "^1.0.0",
185+ "@azure/core-util": "^1.11.0",
186+ "@azure/logger": "^1.0.0",
187+ "@azure/msal-browser": "^5.5.0",
188+ "@azure/msal-node": "^6.0.0",
189+ "open": "^10.1.0",
190+ "tslib": "^2.2.0"
191+ },
192+ "engines": {
193+ "node": ">=22.0.0"
194+ }
195+ },
196+ "node_modules/@azure/identity/node_modules/open": {
197+ "version": "10.2.0",
198+ "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
199+ "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
200+ "dev": true,
201+ "license": "MIT",
202+ "dependencies": {
203+ "default-browser": "^5.2.1",
204+ "define-lazy-prop": "^3.0.0",
205+ "is-inside-container": "^1.0.0",
206+ "wsl-utils": "^0.1.0"
207+ },
208+ "engines": {
209+ "node": ">=18"
210+ },
211+ "funding": {
212+ "url": "https://github.com/sponsors/sindresorhus"
213+ }
214+ },
215+ "node_modules/@azure/identity/node_modules/wsl-utils": {
216+ "version": "0.1.0",
217+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
218+ "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
219+ "dev": true,
220+ "license": "MIT",
221+ "dependencies": {
222+ "is-wsl": "^3.1.0"
223+ },
224+ "engines": {
225+ "node": ">=18"
226+ },
227+ "funding": {
228+ "url": "https://github.com/sponsors/sindresorhus"
229+ }
230+ },
231+ "node_modules/@azure/logger": {
232+ "version": "1.4.0",
233+ "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.4.0.tgz",
234+ "integrity": "sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==",
235+ "dev": true,
236+ "license": "MIT",
237+ "dependencies": {
238+ "@typespec/ts-http-runtime": "^0.3.0",
239+ "tslib": "^2.6.2"
240+ },
241+ "engines": {
242+ "node": ">=22.0.0"
243+ }
244+ },
245+ "node_modules/@azure/msal-browser": {
246+ "version": "5.22.0",
247+ "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.22.0.tgz",
248+ "integrity": "sha512-5kgu9xeEKgGc2JeidxAtU15NJTqiH/CMCRRQAJ4Rac56kB7KVg91vbNmn+z3RO1vNomPN69UvjKG9h1Pghx6dQ==",
249+ "dev": true,
250+ "license": "MIT",
251+ "dependencies": {
252+ "@azure/msal-common": "16.14.1"
253+ },
254+ "engines": {
255+ "node": ">=0.8.0"
256+ }
257+ },
258+ "node_modules/@azure/msal-common": {
259+ "version": "16.14.1",
260+ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.1.tgz",
261+ "integrity": "sha512-Or6xhPNyi4zHW25158yxBoyxuCqNSPa5YBVqfF1J5Ks4MJWBo/USXdp05DQIPu1Zli00YZu6t0+h6KvHJealxQ==",
262+ "dev": true,
263+ "license": "MIT",
264+ "engines": {
265+ "node": ">=0.8.0"
266+ }
267+ },
268+ "node_modules/@azure/msal-node": {
269+ "version": "6.0.1",
270+ "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-6.0.1.tgz",
271+ "integrity": "sha512-ixSO1Y/kCVRthRs+hSx/5qkwaunX1/RAePhlMN0wIpIQ4WEZ6AREGGnGd1AP0qspHVsAwzWQKGubpjh50JyJIQ==",
272+ "dev": true,
273+ "license": "MIT",
274+ "dependencies": {
275+ "@azure/msal-common": "16.14.1",
276+ "jsonwebtoken": "^9.0.0"
277+ },
278+ "engines": {
279+ "node": ">=20"
280+ }
281+ },
64282 "node_modules/@babel/code-frame": {
65283 "version": "7.29.7",
66284 "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
@@ -2202,116 +2420,123 @@
22022420 "dev": true,
22032421 "license": "MIT"
22042422 },
2205 "node_modules/@napi-rs/wasm-runtime": {
2206 "version": "1.2.4",
2207 "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz",
2208 "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==",
2423+ "node_modules/@napi-rs/keyring": {
2424+ "version": "1.3.0",
2425+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring/-/keyring-1.3.0.tgz",
2426+ "integrity": "sha512-WrOw/bcXm0f9qHkumlT1QlArXSTWqaY9sunsDpOk+yCCorCKMxvWT/a3xko4EYHVdeZoh00yI2TydXn6eyICDA==",
22092427 "dev": true,
22102428 "license": "MIT",
2211 "optional": true,
2212 "dependencies": {
2213 "@tybys/wasm-util": "^0.10.3"
2214 },
22152429 "engines": {
2216 "node": "^20.19.0 || ^22.13.0 || >=23.5.0"
2430+ "node": ">= 10"
22172431 },
22182432 "funding": {
22192433 "type": "github",
22202434 "url": "https://github.com/sponsors/Brooooooklyn"
22212435 },
2222 "peerDependencies": {
2223 "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4",
2224 "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4"
2225 }
2226 },
2227 "node_modules/@next/env": {
2228 "version": "16.3.5",
2229 "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.5.tgz",
2230 "integrity": "sha512-NWEXVDMqoEo0ktmU6u0sE2Vg0LOcsD7NnOTJNo3/fEaTfsg+F1bMIxuDmQbda4e3yTIQwVdUREF2yIuMOusKtg==",
2231 "dev": true,
2232 "license": "MIT"
2233 },
2234 "node_modules/@next/eslint-plugin-next": {
2235 "version": "16.3.5",
2236 "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.5.tgz",
2237 "integrity": "sha512-PGfSeItHJ12DH8t+6sEbuMe59NE5rAhCfgk06QKTH2ne9VUL1JlaXdYXY3B8RaiF2SO50rDYvd39TulP6A6xZQ==",
2436+ "optionalDependencies": {
2437+ "@napi-rs/keyring-darwin-arm64": "1.3.0",
2438+ "@napi-rs/keyring-darwin-x64": "1.3.0",
2439+ "@napi-rs/keyring-freebsd-x64": "1.3.0",
2440+ "@napi-rs/keyring-linux-arm-gnueabihf": "1.3.0",
2441+ "@napi-rs/keyring-linux-arm64-gnu": "1.3.0",
2442+ "@napi-rs/keyring-linux-arm64-musl": "1.3.0",
2443+ "@napi-rs/keyring-linux-riscv64-gnu": "1.3.0",
2444+ "@napi-rs/keyring-linux-x64-gnu": "1.3.0",
2445+ "@napi-rs/keyring-linux-x64-musl": "1.3.0",
2446+ "@napi-rs/keyring-win32-arm64-msvc": "1.3.0",
2447+ "@napi-rs/keyring-win32-ia32-msvc": "1.3.0",
2448+ "@napi-rs/keyring-win32-x64-msvc": "1.3.0"
2449+ }
2450+ },
2451+ "node_modules/@napi-rs/keyring-darwin-arm64": {
2452+ "version": "1.3.0",
2453+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-darwin-arm64/-/keyring-darwin-arm64-1.3.0.tgz",
2454+ "integrity": "sha512-pl76hJvdYUBn6I24bXiOBMA9nbDapo3I5B+f3OorjDU4dUMSypXeKbOVehJe8fhgTiH24flMyTS3aAIy43xegQ==",
2455+ "cpu": [
2456+ "arm64"
2457+ ],
22382458 "dev": true,
22392459 "license": "MIT",
2240 "dependencies": {
2241 "@eslint-community/eslint-utils": "4.9.1",
2242 "fast-glob": "3.3.1"
2460+ "optional": true,
2461+ "os": [
2462+ "darwin"
2463+ ],
2464+ "engines": {
2465+ "node": ">= 10"
22432466 }
22442467 },
2245 "node_modules/@next/eslint-plugin-next/node_modules/@eslint-community/eslint-utils": {
2246 "version": "4.9.1",
2247 "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
2248 "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
2468+ "node_modules/@napi-rs/keyring-darwin-x64": {
2469+ "version": "1.3.0",
2470+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-darwin-x64/-/keyring-darwin-x64-1.3.0.tgz",
2471+ "integrity": "sha512-YcJtEV5LA3cvA4z3BurgxH5IhTsW1JfIvcAAcqcecwk06Si9F9NqkxbZVIfDwQ8oRHgaBmT3zZJnLAotCrVahw==",
2472+ "cpu": [
2473+ "x64"
2474+ ],
22492475 "dev": true,
22502476 "license": "MIT",
2251 "dependencies": {
2252 "eslint-visitor-keys": "^3.4.3"
2253 },
2477+ "optional": true,
2478+ "os": [
2479+ "darwin"
2480+ ],
22542481 "engines": {
2255 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
2256 },
2257 "funding": {
2258 "url": "https://opencollective.com/eslint"
2259 },
2260 "peerDependencies": {
2261 "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
2482+ "node": ">= 10"
22622483 }
22632484 },
2264 "node_modules/@next/eslint-plugin-next/node_modules/eslint-visitor-keys": {
2265 "version": "3.4.3",
2266 "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
2267 "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
2485+ "node_modules/@napi-rs/keyring-freebsd-x64": {
2486+ "version": "1.3.0",
2487+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-freebsd-x64/-/keyring-freebsd-x64-1.3.0.tgz",
2488+ "integrity": "sha512-vlLf31TGhfRAaxLDBhg8b89ss0HHD/lyNmL5F3UjSaz5CUXElsJmKYq9fqA/B+cZKUEUcLHHGhF0I/CqcFdaVw==",
2489+ "cpu": [
2490+ "x64"
2491+ ],
22682492 "dev": true,
2269 "license": "Apache-2.0",
2493+ "license": "MIT",
2494+ "optional": true,
2495+ "os": [
2496+ "freebsd"
2497+ ],
22702498 "engines": {
2271 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
2272 },
2273 "funding": {
2274 "url": "https://opencollective.com/eslint"
2499+ "node": ">= 10"
22752500 }
22762501 },
2277 "node_modules/@next/swc-darwin-arm64": {
2278 "version": "16.3.5",
2279 "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.5.tgz",
2280 "integrity": "sha512-pMmGgETfKvElucLHtVaeiMRbp2zUbvKx7b1yGko0liBz3cw1mKSggWN/Rp/wPz8z+E1O82u3r4L1Co+ZS5hokQ==",
2502+ "node_modules/@napi-rs/keyring-linux-arm-gnueabihf": {
2503+ "version": "1.3.0",
2504+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm-gnueabihf/-/keyring-linux-arm-gnueabihf-1.3.0.tgz",
2505+ "integrity": "sha512-KiWdMMu/Inz/bHHIAGrnF7r54FZDYXuHO6UFF/rhIrshUsxbMG1Rl9lEymNtqqsVo927G0VYcb02FzWQ3iBQRQ==",
22812506 "cpu": [
2282 "arm64"
2507+ "arm"
22832508 ],
22842509 "dev": true,
22852510 "license": "MIT",
22862511 "optional": true,
22872512 "os": [
2288 "darwin"
2513+ "linux"
22892514 ],
22902515 "engines": {
22912516 "node": ">= 10"
22922517 }
22932518 },
2294 "node_modules/@next/swc-darwin-x64": {
2295 "version": "16.3.5",
2296 "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.5.tgz",
2297 "integrity": "sha512-76VaGYvf6HPa5/w12yLkE3dXTn9AfdEviI79oEL3aZoAmRLc9rWitjWqyjViVysK/ht/y9YKzFkBrUdi/wGkow==",
2519+ "node_modules/@napi-rs/keyring-linux-arm64-gnu": {
2520+ "version": "1.3.0",
2521+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-gnu/-/keyring-linux-arm64-gnu-1.3.0.tgz",
2522+ "integrity": "sha512-eyKGpY40lm9Jvs1aD294XRH4y7+TlJM0YVAryZeXA6TX0mb4gMkxVXwSQv7MCwgah7raeUd0dKUb4BPAYIgcMg==",
22982523 "cpu": [
2299 "x64"
2524+ "arm64"
23002525 ],
23012526 "dev": true,
23022527 "license": "MIT",
23032528 "optional": true,
23042529 "os": [
2305 "darwin"
2530+ "linux"
23062531 ],
23072532 "engines": {
23082533 "node": ">= 10"
23092534 }
23102535 },
2311 "node_modules/@next/swc-linux-arm64-gnu": {
2312 "version": "16.3.5",
2313 "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.5.tgz",
2314 "integrity": "sha512-zKDELJ5jSQMHeO/hmXUQsAzagX4bQD4OiMi3pQ5FbUj+yK506oLVHnKA2YXMlbg1EHHqJYtyePOgByIDXD1lqw==",
2536+ "node_modules/@napi-rs/keyring-linux-arm64-musl": {
2537+ "version": "1.3.0",
2538+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-musl/-/keyring-linux-arm64-musl-1.3.0.tgz",
2539+ "integrity": "sha512-iIK6JWHXAJqDrEyLY3TmswwloVyt2vj+04TZnew+uSJ9gnDO8EwRbp3/iw3LpWaXiDO7VomGO6y8I0Id8uBZSw==",
23152540 "cpu": [
23162541 "arm64"
23172542 ],
@@ -2325,12 +2550,12 @@
23252550 "node": ">= 10"
23262551 }
23272552 },
2328 "node_modules/@next/swc-linux-arm64-musl": {
2329 "version": "16.3.5",
2330 "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.5.tgz",
2331 "integrity": "sha512-7Vql0pgzCoHagv6+FNOZoqmJqA52c6zeVbhtS/47qFozO1MSx4ms7x7GHiciY8R5CDsSMKMQjJEryoJLcsBIbA==",
2553+ "node_modules/@napi-rs/keyring-linux-riscv64-gnu": {
2554+ "version": "1.3.0",
2555+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-riscv64-gnu/-/keyring-linux-riscv64-gnu-1.3.0.tgz",
2556+ "integrity": "sha512-/PGqrwn6EwgtK6vccASSXJRfOSP4vN1F4ASsIQ+7MdrK6hNvAJ1FZPrIuD5gGGdxezo3F++To2Wq7DbuGIeuNQ==",
23322557 "cpu": [
2333 "arm64"
2558+ "riscv64"
23342559 ],
23352560 "dev": true,
23362561 "license": "MIT",
@@ -2342,10 +2567,10 @@
23422567 "node": ">= 10"
23432568 }
23442569 },
2345 "node_modules/@next/swc-linux-x64-gnu": {
2346 "version": "16.3.5",
2347 "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.5.tgz",
2348 "integrity": "sha512-NH/xzehyHEFWE2nlcZon7TB/0+H4shfWCi7S1zka815XCOhJDYZhoeJtOYy0dh0WVRWACVXSyGNFFytoMxUhRg==",
2570+ "node_modules/@napi-rs/keyring-linux-x64-gnu": {
2571+ "version": "1.3.0",
2572+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-x64-gnu/-/keyring-linux-x64-gnu-1.3.0.tgz",
2573+ "integrity": "sha512-2PDK1WKWTu9lBGq9VvNEkSlQD3O7YwVpmnyN2M3cy4v7NJ/8gDMd9GXv3G+FVXN13uhp4gnnPBS+ScefmEeD2A==",
23492574 "cpu": [
23502575 "x64"
23512576 ],
@@ -2359,10 +2584,10 @@
23592584 "node": ">= 10"
23602585 }
23612586 },
2362 "node_modules/@next/swc-linux-x64-musl": {
2363 "version": "16.3.5",
2364 "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.5.tgz",
2365 "integrity": "sha512-lV4+EhWMfS8jcC+EH2nn/Cm5cn6XsgbE07bU9tMH8fCo0tNAqhyzi1b5wQ/Tn6NGFTvKDY65w3ZH95EjwBRAnQ==",
2587+ "node_modules/@napi-rs/keyring-linux-x64-musl": {
2588+ "version": "1.3.0",
2589+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-x64-musl/-/keyring-linux-x64-musl-1.3.0.tgz",
2590+ "integrity": "sha512-oJ2HkX8YUo46QBkn0pG+HuIKQNqr523q6vBobCn+P95s4C4K6/kLBqHY/1bg5J4ap31DzsznhnFKcfBNBsjCnw==",
23662591 "cpu": [
23672592 "x64"
23682593 ],
@@ -2376,10 +2601,10 @@
23762601 "node": ">= 10"
23772602 }
23782603 },
2379 "node_modules/@next/swc-win32-arm64-msvc": {
2380 "version": "16.3.5",
2381 "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.5.tgz",
2382 "integrity": "sha512-/wKzAREX2RF++MhicjDbg8tGn2AiBIM0+EFeTFKoUEUbW5D6amCJehd5Z5G1H5/gxNdgnwoXMcHz24H/c2tGkQ==",
2604+ "node_modules/@napi-rs/keyring-win32-arm64-msvc": {
2605+ "version": "1.3.0",
2606+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-arm64-msvc/-/keyring-win32-arm64-msvc-1.3.0.tgz",
2607+ "integrity": "sha512-tOd3c/uAaeoE4ycVlmAdSvygz0Zt3zdca6Y7gokBeIbaRDWpjDIUOpU3MvML59XAaqyuKGsVVu0F/DZb1lHPmw==",
23832608 "cpu": [
23842609 "arm64"
23852610 ],
@@ -2393,12 +2618,12 @@
23932618 "node": ">= 10"
23942619 }
23952620 },
2396 "node_modules/@next/swc-win32-x64-msvc": {
2397 "version": "16.3.5",
2398 "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.5.tgz",
2399 "integrity": "sha512-LNdCHzgLFc+UeqMS84LzXPaeBRKyqDN9OMyFAr1OrB0XrNw78IRrEVtZvvA7245W/HsaoeVOQX9jPjPk8jojwA==",
2621+ "node_modules/@napi-rs/keyring-win32-ia32-msvc": {
2622+ "version": "1.3.0",
2623+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-ia32-msvc/-/keyring-win32-ia32-msvc-1.3.0.tgz",
2624+ "integrity": "sha512-sPSqeAFZMGqP1R++M2JTza7GQJJ/TpCo6JU6Vcd4jnebvOaEDs9b7eipakU1PJdSvhpC2yXMCNRk9gXfrhuwHQ==",
24002625 "cpu": [
2401 "x64"
2626+ "ia32"
24022627 ],
24032628 "dev": true,
24042629 "license": "MIT",
@@ -2410,12 +2635,237 @@
24102635 "node": ">= 10"
24112636 }
24122637 },
2413 "node_modules/@nodelib/fs.scandir": {
2414 "version": "2.1.5",
2415 "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
2416 "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
2417 "dev": true,
2418 "license": "MIT",
2638+ "node_modules/@napi-rs/keyring-win32-x64-msvc": {
2639+ "version": "1.3.0",
2640+ "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-x64-msvc/-/keyring-win32-x64-msvc-1.3.0.tgz",
2641+ "integrity": "sha512-4DnCWXwDc0HRKwyRlG5y0VhKZW2tNRQfKKfyj6IX/KWfDNyq9hn4n+GL1auyDcOO/v8PwnhmYo2+rOOqCkvvOg==",
2642+ "cpu": [
2643+ "x64"
2644+ ],
2645+ "dev": true,
2646+ "license": "MIT",
2647+ "optional": true,
2648+ "os": [
2649+ "win32"
2650+ ],
2651+ "engines": {
2652+ "node": ">= 10"
2653+ }
2654+ },
2655+ "node_modules/@napi-rs/wasm-runtime": {
2656+ "version": "1.2.4",
2657+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz",
2658+ "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==",
2659+ "dev": true,
2660+ "license": "MIT",
2661+ "optional": true,
2662+ "dependencies": {
2663+ "@tybys/wasm-util": "^0.10.3"
2664+ },
2665+ "engines": {
2666+ "node": "^20.19.0 || ^22.13.0 || >=23.5.0"
2667+ },
2668+ "funding": {
2669+ "type": "github",
2670+ "url": "https://github.com/sponsors/Brooooooklyn"
2671+ },
2672+ "peerDependencies": {
2673+ "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4",
2674+ "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4"
2675+ }
2676+ },
2677+ "node_modules/@next/env": {
2678+ "version": "16.3.5",
2679+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.5.tgz",
2680+ "integrity": "sha512-NWEXVDMqoEo0ktmU6u0sE2Vg0LOcsD7NnOTJNo3/fEaTfsg+F1bMIxuDmQbda4e3yTIQwVdUREF2yIuMOusKtg==",
2681+ "dev": true,
2682+ "license": "MIT"
2683+ },
2684+ "node_modules/@next/eslint-plugin-next": {
2685+ "version": "16.3.5",
2686+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.5.tgz",
2687+ "integrity": "sha512-PGfSeItHJ12DH8t+6sEbuMe59NE5rAhCfgk06QKTH2ne9VUL1JlaXdYXY3B8RaiF2SO50rDYvd39TulP6A6xZQ==",
2688+ "dev": true,
2689+ "license": "MIT",
2690+ "dependencies": {
2691+ "@eslint-community/eslint-utils": "4.9.1",
2692+ "fast-glob": "3.3.1"
2693+ }
2694+ },
2695+ "node_modules/@next/eslint-plugin-next/node_modules/@eslint-community/eslint-utils": {
2696+ "version": "4.9.1",
2697+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
2698+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
2699+ "dev": true,
2700+ "license": "MIT",
2701+ "dependencies": {
2702+ "eslint-visitor-keys": "^3.4.3"
2703+ },
2704+ "engines": {
2705+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
2706+ },
2707+ "funding": {
2708+ "url": "https://opencollective.com/eslint"
2709+ },
2710+ "peerDependencies": {
2711+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
2712+ }
2713+ },
2714+ "node_modules/@next/eslint-plugin-next/node_modules/eslint-visitor-keys": {
2715+ "version": "3.4.3",
2716+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
2717+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
2718+ "dev": true,
2719+ "license": "Apache-2.0",
2720+ "engines": {
2721+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
2722+ },
2723+ "funding": {
2724+ "url": "https://opencollective.com/eslint"
2725+ }
2726+ },
2727+ "node_modules/@next/swc-darwin-arm64": {
2728+ "version": "16.3.5",
2729+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.5.tgz",
2730+ "integrity": "sha512-pMmGgETfKvElucLHtVaeiMRbp2zUbvKx7b1yGko0liBz3cw1mKSggWN/Rp/wPz8z+E1O82u3r4L1Co+ZS5hokQ==",
2731+ "cpu": [
2732+ "arm64"
2733+ ],
2734+ "dev": true,
2735+ "license": "MIT",
2736+ "optional": true,
2737+ "os": [
2738+ "darwin"
2739+ ],
2740+ "engines": {
2741+ "node": ">= 10"
2742+ }
2743+ },
2744+ "node_modules/@next/swc-darwin-x64": {
2745+ "version": "16.3.5",
2746+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.5.tgz",
2747+ "integrity": "sha512-76VaGYvf6HPa5/w12yLkE3dXTn9AfdEviI79oEL3aZoAmRLc9rWitjWqyjViVysK/ht/y9YKzFkBrUdi/wGkow==",
2748+ "cpu": [
2749+ "x64"
2750+ ],
2751+ "dev": true,
2752+ "license": "MIT",
2753+ "optional": true,
2754+ "os": [
2755+ "darwin"
2756+ ],
2757+ "engines": {
2758+ "node": ">= 10"
2759+ }
2760+ },
2761+ "node_modules/@next/swc-linux-arm64-gnu": {
2762+ "version": "16.3.5",
2763+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.5.tgz",
2764+ "integrity": "sha512-zKDELJ5jSQMHeO/hmXUQsAzagX4bQD4OiMi3pQ5FbUj+yK506oLVHnKA2YXMlbg1EHHqJYtyePOgByIDXD1lqw==",
2765+ "cpu": [
2766+ "arm64"
2767+ ],
2768+ "dev": true,
2769+ "license": "MIT",
2770+ "optional": true,
2771+ "os": [
2772+ "linux"
2773+ ],
2774+ "engines": {
2775+ "node": ">= 10"
2776+ }
2777+ },
2778+ "node_modules/@next/swc-linux-arm64-musl": {
2779+ "version": "16.3.5",
2780+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.5.tgz",
2781+ "integrity": "sha512-7Vql0pgzCoHagv6+FNOZoqmJqA52c6zeVbhtS/47qFozO1MSx4ms7x7GHiciY8R5CDsSMKMQjJEryoJLcsBIbA==",
2782+ "cpu": [
2783+ "arm64"
2784+ ],
2785+ "dev": true,
2786+ "license": "MIT",
2787+ "optional": true,
2788+ "os": [
2789+ "linux"
2790+ ],
2791+ "engines": {
2792+ "node": ">= 10"
2793+ }
2794+ },
2795+ "node_modules/@next/swc-linux-x64-gnu": {
2796+ "version": "16.3.5",
2797+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.5.tgz",
2798+ "integrity": "sha512-NH/xzehyHEFWE2nlcZon7TB/0+H4shfWCi7S1zka815XCOhJDYZhoeJtOYy0dh0WVRWACVXSyGNFFytoMxUhRg==",
2799+ "cpu": [
2800+ "x64"
2801+ ],
2802+ "dev": true,
2803+ "license": "MIT",
2804+ "optional": true,
2805+ "os": [
2806+ "linux"
2807+ ],
2808+ "engines": {
2809+ "node": ">= 10"
2810+ }
2811+ },
2812+ "node_modules/@next/swc-linux-x64-musl": {
2813+ "version": "16.3.5",
2814+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.5.tgz",
2815+ "integrity": "sha512-lV4+EhWMfS8jcC+EH2nn/Cm5cn6XsgbE07bU9tMH8fCo0tNAqhyzi1b5wQ/Tn6NGFTvKDY65w3ZH95EjwBRAnQ==",
2816+ "cpu": [
2817+ "x64"
2818+ ],
2819+ "dev": true,
2820+ "license": "MIT",
2821+ "optional": true,
2822+ "os": [
2823+ "linux"
2824+ ],
2825+ "engines": {
2826+ "node": ">= 10"
2827+ }
2828+ },
2829+ "node_modules/@next/swc-win32-arm64-msvc": {
2830+ "version": "16.3.5",
2831+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.5.tgz",
2832+ "integrity": "sha512-/wKzAREX2RF++MhicjDbg8tGn2AiBIM0+EFeTFKoUEUbW5D6amCJehd5Z5G1H5/gxNdgnwoXMcHz24H/c2tGkQ==",
2833+ "cpu": [
2834+ "arm64"
2835+ ],
2836+ "dev": true,
2837+ "license": "MIT",
2838+ "optional": true,
2839+ "os": [
2840+ "win32"
2841+ ],
2842+ "engines": {
2843+ "node": ">= 10"
2844+ }
2845+ },
2846+ "node_modules/@next/swc-win32-x64-msvc": {
2847+ "version": "16.3.5",
2848+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.5.tgz",
2849+ "integrity": "sha512-LNdCHzgLFc+UeqMS84LzXPaeBRKyqDN9OMyFAr1OrB0XrNw78IRrEVtZvvA7245W/HsaoeVOQX9jPjPk8jojwA==",
2850+ "cpu": [
2851+ "x64"
2852+ ],
2853+ "dev": true,
2854+ "license": "MIT",
2855+ "optional": true,
2856+ "os": [
2857+ "win32"
2858+ ],
2859+ "engines": {
2860+ "node": ">= 10"
2861+ }
2862+ },
2863+ "node_modules/@nodelib/fs.scandir": {
2864+ "version": "2.1.5",
2865+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
2866+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
2867+ "dev": true,
2868+ "license": "MIT",
24192869 "dependencies": {
24202870 "@nodelib/fs.stat": "2.0.5",
24212871 "run-parallel": "^1.1.9"
@@ -3253,6 +3703,76 @@
32533703 "dev": true,
32543704 "license": "MIT"
32553705 },
3706+ "node_modules/@secretlint/core": {
3707+ "version": "10.2.2",
3708+ "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz",
3709+ "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==",
3710+ "dev": true,
3711+ "license": "MIT",
3712+ "dependencies": {
3713+ "@secretlint/profiler": "^10.2.2",
3714+ "@secretlint/types": "^10.2.2",
3715+ "debug": "^4.4.1",
3716+ "structured-source": "^4.0.0"
3717+ },
3718+ "engines": {
3719+ "node": ">=20.0.0"
3720+ }
3721+ },
3722+ "node_modules/@secretlint/profiler": {
3723+ "version": "10.2.2",
3724+ "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz",
3725+ "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==",
3726+ "dev": true,
3727+ "license": "MIT"
3728+ },
3729+ "node_modules/@secretlint/secretlint-rule-no-dotenv": {
3730+ "version": "10.2.2",
3731+ "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz",
3732+ "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==",
3733+ "dev": true,
3734+ "license": "MIT",
3735+ "dependencies": {
3736+ "@secretlint/types": "^10.2.2"
3737+ },
3738+ "engines": {
3739+ "node": ">=20.0.0"
3740+ }
3741+ },
3742+ "node_modules/@secretlint/secretlint-rule-preset-recommend": {
3743+ "version": "10.2.2",
3744+ "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz",
3745+ "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==",
3746+ "dev": true,
3747+ "license": "MIT",
3748+ "engines": {
3749+ "node": ">=20.0.0"
3750+ }
3751+ },
3752+ "node_modules/@secretlint/source-creator": {
3753+ "version": "10.2.2",
3754+ "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz",
3755+ "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==",
3756+ "dev": true,
3757+ "license": "MIT",
3758+ "dependencies": {
3759+ "@secretlint/types": "^10.2.2",
3760+ "istextorbinary": "^9.5.0"
3761+ },
3762+ "engines": {
3763+ "node": ">=20.0.0"
3764+ }
3765+ },
3766+ "node_modules/@secretlint/types": {
3767+ "version": "10.2.2",
3768+ "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz",
3769+ "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==",
3770+ "dev": true,
3771+ "license": "MIT",
3772+ "engines": {
3773+ "node": ">=20.0.0"
3774+ }
3775+ },
32563776 "node_modules/@shikijs/core": {
32573777 "version": "3.23.0",
32583778 "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz",
@@ -3882,6 +4402,13 @@
38824402 "dev": true,
38834403 "license": "MIT"
38844404 },
4405+ "node_modules/@types/vscode": {
4406+ "version": "1.96.0",
4407+ "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz",
4408+ "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==",
4409+ "dev": true,
4410+ "license": "MIT"
4411+ },
38854412 "node_modules/@typescript-eslint/eslint-plugin": {
38864413 "version": "8.70.0",
38874414 "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.70.0.tgz",
@@ -4177,6 +4704,21 @@
41774704 "url": "https://opencollective.com/eslint"
41784705 }
41794706 },
4707+ "node_modules/@typespec/ts-http-runtime": {
4708+ "version": "0.3.9",
4709+ "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.9.tgz",
4710+ "integrity": "sha512-edSdeAqkdxBVzA1yL1LrLCml1YjyCVvPMtMqJpbF+6K609tHe8V6sQUzFQSGcYNhcuhOceZtjvN32+mpIth30A==",
4711+ "dev": true,
4712+ "license": "MIT",
4713+ "dependencies": {
4714+ "http-proxy-agent": "^7.0.0",
4715+ "https-proxy-agent": "^7.0.0",
4716+ "tslib": "^2.6.2"
4717+ },
4718+ "engines": {
4719+ "node": ">=22.0.0"
4720+ }
4721+ },
41804722 "node_modules/@ungap/structured-clone": {
41814723 "version": "1.4.0",
41824724 "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz",
@@ -4460,143 +5002,421 @@
44605002 "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
44615003 "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
44625004 "dev": true,
4463 "license": "MIT",
5005+ "license": "MIT",
5006+ "optional": true,
5007+ "dependencies": {
5008+ "tslib": "^2.4.0"
5009+ }
5010+ },
5011+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
5012+ "version": "1.12.2",
5013+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz",
5014+ "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==",
5015+ "cpu": [
5016+ "arm64"
5017+ ],
5018+ "dev": true,
5019+ "license": "MIT",
5020+ "optional": true,
5021+ "os": [
5022+ "win32"
5023+ ]
5024+ },
5025+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
5026+ "version": "1.12.2",
5027+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz",
5028+ "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==",
5029+ "cpu": [
5030+ "ia32"
5031+ ],
5032+ "dev": true,
5033+ "license": "MIT",
5034+ "optional": true,
5035+ "os": [
5036+ "win32"
5037+ ]
5038+ },
5039+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
5040+ "version": "1.12.2",
5041+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz",
5042+ "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==",
5043+ "cpu": [
5044+ "x64"
5045+ ],
5046+ "dev": true,
5047+ "license": "MIT",
5048+ "optional": true,
5049+ "os": [
5050+ "win32"
5051+ ]
5052+ },
5053+ "node_modules/@vercel/analytics": {
5054+ "version": "2.0.1",
5055+ "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.1.tgz",
5056+ "integrity": "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==",
5057+ "dev": true,
5058+ "license": "MIT",
5059+ "peerDependencies": {
5060+ "@remix-run/react": "^2",
5061+ "@sveltejs/kit": "^1 || ^2",
5062+ "next": ">= 13",
5063+ "nuxt": ">= 3",
5064+ "react": "^18 || ^19 || ^19.0.0-rc",
5065+ "svelte": ">= 4",
5066+ "vue": "^3",
5067+ "vue-router": "^4"
5068+ },
5069+ "peerDependenciesMeta": {
5070+ "@remix-run/react": {
5071+ "optional": true
5072+ },
5073+ "@sveltejs/kit": {
5074+ "optional": true
5075+ },
5076+ "next": {
5077+ "optional": true
5078+ },
5079+ "nuxt": {
5080+ "optional": true
5081+ },
5082+ "react": {
5083+ "optional": true
5084+ },
5085+ "svelte": {
5086+ "optional": true
5087+ },
5088+ "vue": {
5089+ "optional": true
5090+ },
5091+ "vue-router": {
5092+ "optional": true
5093+ }
5094+ }
5095+ },
5096+ "node_modules/@vitest/mocker": {
5097+ "version": "5.0.1",
5098+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.1.tgz",
5099+ "integrity": "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q==",
5100+ "dev": true,
5101+ "license": "MIT",
5102+ "dependencies": {
5103+ "@jridgewell/trace-mapping": "0.3.31",
5104+ "@vitest/spy": "5.0.1",
5105+ "estree-walker": "^3.0.3",
5106+ "magic-string": "^1.2.3"
5107+ },
5108+ "funding": {
5109+ "url": "https://opencollective.com/vitest"
5110+ },
5111+ "peerDependencies": {
5112+ "msw": "^2.4.9",
5113+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
5114+ },
5115+ "peerDependenciesMeta": {
5116+ "msw": {
5117+ "optional": true
5118+ },
5119+ "vite": {
5120+ "optional": true
5121+ }
5122+ }
5123+ },
5124+ "node_modules/@vitest/mocker/node_modules/magic-string": {
5125+ "version": "1.4.1",
5126+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz",
5127+ "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==",
5128+ "dev": true,
5129+ "license": "MIT",
5130+ "dependencies": {
5131+ "@jridgewell/sourcemap-codec": "^1.6.0"
5132+ }
5133+ },
5134+ "node_modules/@vitest/spy": {
5135+ "version": "5.0.1",
5136+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.1.tgz",
5137+ "integrity": "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==",
5138+ "dev": true,
5139+ "license": "MIT",
5140+ "funding": {
5141+ "url": "https://opencollective.com/vitest"
5142+ }
5143+ },
5144+ "node_modules/@vscode/test-electron": {
5145+ "version": "3.1.0",
5146+ "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-3.1.0.tgz",
5147+ "integrity": "sha512-CRqv5u+YYoseuNVJ6Tyo4k0sF0mx4qnKMihRB0PjsUF8Dc0WKtCXo6CNL6nWWm5esfFQsQA/pejMj4ZbpJVLTw==",
5148+ "dev": true,
5149+ "license": "MIT",
5150+ "dependencies": {
5151+ "http-proxy-agent": "^7.0.2",
5152+ "https-proxy-agent": "^7.0.5",
5153+ "jszip": "^3.10.1",
5154+ "ora": "^8.1.0",
5155+ "semver": "^7.6.2"
5156+ },
5157+ "engines": {
5158+ "node": ">=22"
5159+ }
5160+ },
5161+ "node_modules/@vscode/test-electron/node_modules/semver": {
5162+ "version": "7.8.5",
5163+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
5164+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
5165+ "dev": true,
5166+ "license": "ISC",
5167+ "bin": {
5168+ "semver": "bin/semver.js"
5169+ },
5170+ "engines": {
5171+ "node": ">=10"
5172+ }
5173+ },
5174+ "node_modules/@vscode/vsce": {
5175+ "version": "4.0.0",
5176+ "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-4.0.0.tgz",
5177+ "integrity": "sha512-NImwuLaenMmb5D5Jer9/lzi/F9ZQUBOp8Azhj/BVYcTFgixv8KehFXqEUDjQlD2tAiw2E6dDGyjTuAB//di60A==",
5178+ "dev": true,
5179+ "license": "MIT",
5180+ "dependencies": {
5181+ "@azure/identity": "^4.13.2",
5182+ "@napi-rs/keyring": "^1.3.0",
5183+ "@secretlint/core": "^10.2.2",
5184+ "@secretlint/secretlint-rule-no-dotenv": "^10.2.2",
5185+ "@secretlint/secretlint-rule-preset-recommend": "^10.2.2",
5186+ "@secretlint/source-creator": "^10.2.2",
5187+ "@secretlint/types": "^10.2.2",
5188+ "@vscode/vsce-sign": "^2.1.0",
5189+ "azure-devops-node-api": "^12.5.0",
5190+ "cockatiel": "^3.2.1",
5191+ "commander": "^12.1.0",
5192+ "hosted-git-info": "^4.1.0",
5193+ "jsonc-parser": "^3.3.1",
5194+ "marked": "^18.0.11",
5195+ "mime": "^1.6.0",
5196+ "minimatch": "^10.2.6",
5197+ "parse5": "^8.0.1",
5198+ "proper-lockfile": "^4.1.2",
5199+ "read": "^1.0.7",
5200+ "semver": "^7.8.5",
5201+ "tinyglobby": "^0.2.17",
5202+ "typed-rest-client": "^1.8.11",
5203+ "url-join": "^4.0.1",
5204+ "xml2js": "^0.5.0",
5205+ "yauzl": "^3.4.0",
5206+ "yazl": "^2.5.1"
5207+ },
5208+ "bin": {
5209+ "vsce": "vsce"
5210+ },
5211+ "engines": {
5212+ "node": ">= 22"
5213+ }
5214+ },
5215+ "node_modules/@vscode/vsce-sign": {
5216+ "version": "2.1.0",
5217+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.1.0.tgz",
5218+ "integrity": "sha512-9AQrqazrBgTgRSuwleLVXUrIUphY02/SFCh2TKYoLV/xifJAdblhdmEmw5gUrYSPQ3sRwNs9iyCMD14sATEE6g==",
5219+ "dev": true,
5220+ "hasInstallScript": true,
5221+ "license": "SEE LICENSE IN LICENSE.txt",
5222+ "optionalDependencies": {
5223+ "@vscode/vsce-sign-alpine-arm64": "2.0.6",
5224+ "@vscode/vsce-sign-alpine-x64": "2.0.6",
5225+ "@vscode/vsce-sign-darwin-arm64": "2.0.6",
5226+ "@vscode/vsce-sign-darwin-x64": "2.0.6",
5227+ "@vscode/vsce-sign-linux-arm": "2.0.6",
5228+ "@vscode/vsce-sign-linux-arm64": "2.0.6",
5229+ "@vscode/vsce-sign-linux-x64": "2.0.6",
5230+ "@vscode/vsce-sign-win32-arm64": "2.0.6",
5231+ "@vscode/vsce-sign-win32-x64": "2.0.6"
5232+ }
5233+ },
5234+ "node_modules/@vscode/vsce-sign-alpine-arm64": {
5235+ "version": "2.0.6",
5236+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz",
5237+ "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==",
5238+ "cpu": [
5239+ "arm64"
5240+ ],
5241+ "dev": true,
5242+ "license": "SEE LICENSE IN LICENSE.txt",
5243+ "optional": true,
5244+ "os": [
5245+ "alpine"
5246+ ]
5247+ },
5248+ "node_modules/@vscode/vsce-sign-alpine-x64": {
5249+ "version": "2.0.6",
5250+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz",
5251+ "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==",
5252+ "cpu": [
5253+ "x64"
5254+ ],
5255+ "dev": true,
5256+ "license": "SEE LICENSE IN LICENSE.txt",
5257+ "optional": true,
5258+ "os": [
5259+ "alpine"
5260+ ]
5261+ },
5262+ "node_modules/@vscode/vsce-sign-darwin-arm64": {
5263+ "version": "2.0.6",
5264+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz",
5265+ "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==",
5266+ "cpu": [
5267+ "arm64"
5268+ ],
5269+ "dev": true,
5270+ "license": "SEE LICENSE IN LICENSE.txt",
5271+ "optional": true,
5272+ "os": [
5273+ "darwin"
5274+ ]
5275+ },
5276+ "node_modules/@vscode/vsce-sign-darwin-x64": {
5277+ "version": "2.0.6",
5278+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz",
5279+ "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==",
5280+ "cpu": [
5281+ "x64"
5282+ ],
5283+ "dev": true,
5284+ "license": "SEE LICENSE IN LICENSE.txt",
5285+ "optional": true,
5286+ "os": [
5287+ "darwin"
5288+ ]
5289+ },
5290+ "node_modules/@vscode/vsce-sign-linux-arm": {
5291+ "version": "2.0.6",
5292+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz",
5293+ "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==",
5294+ "cpu": [
5295+ "arm"
5296+ ],
5297+ "dev": true,
5298+ "license": "SEE LICENSE IN LICENSE.txt",
5299+ "optional": true,
5300+ "os": [
5301+ "linux"
5302+ ]
5303+ },
5304+ "node_modules/@vscode/vsce-sign-linux-arm64": {
5305+ "version": "2.0.6",
5306+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz",
5307+ "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==",
5308+ "cpu": [
5309+ "arm64"
5310+ ],
5311+ "dev": true,
5312+ "license": "SEE LICENSE IN LICENSE.txt",
44645313 "optional": true,
4465 "dependencies": {
4466 "tslib": "^2.4.0"
4467 }
5314+ "os": [
5315+ "linux"
5316+ ]
44685317 },
4469 "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
4470 "version": "1.12.2",
4471 "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz",
4472 "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==",
5318+ "node_modules/@vscode/vsce-sign-linux-x64": {
5319+ "version": "2.0.6",
5320+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz",
5321+ "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==",
44735322 "cpu": [
4474 "arm64"
5323+ "x64"
44755324 ],
44765325 "dev": true,
4477 "license": "MIT",
5326+ "license": "SEE LICENSE IN LICENSE.txt",
44785327 "optional": true,
44795328 "os": [
4480 "win32"
5329+ "linux"
44815330 ]
44825331 },
4483 "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
4484 "version": "1.12.2",
4485 "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz",
4486 "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==",
5332+ "node_modules/@vscode/vsce-sign-win32-arm64": {
5333+ "version": "2.0.6",
5334+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz",
5335+ "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==",
44875336 "cpu": [
4488 "ia32"
5337+ "arm64"
44895338 ],
44905339 "dev": true,
4491 "license": "MIT",
5340+ "license": "SEE LICENSE IN LICENSE.txt",
44925341 "optional": true,
44935342 "os": [
44945343 "win32"
44955344 ]
44965345 },
4497 "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
4498 "version": "1.12.2",
4499 "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz",
4500 "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==",
5346+ "node_modules/@vscode/vsce-sign-win32-x64": {
5347+ "version": "2.0.6",
5348+ "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz",
5349+ "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==",
45015350 "cpu": [
45025351 "x64"
45035352 ],
45045353 "dev": true,
4505 "license": "MIT",
5354+ "license": "SEE LICENSE IN LICENSE.txt",
45065355 "optional": true,
45075356 "os": [
45085357 "win32"
45095358 ]
45105359 },
4511 "node_modules/@vercel/analytics": {
4512 "version": "2.0.1",
4513 "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-2.0.1.tgz",
4514 "integrity": "sha512-MTQG6V9qQrt1tsDeF+2Uoo5aPjqbVPys1xvnIftXSJYG2SrwXRHnqEvVoYID7BTruDz4lCd2Z7rM1BdkUehk2g==",
5360+ "node_modules/@vscode/vsce/node_modules/balanced-match": {
5361+ "version": "4.0.4",
5362+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
5363+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
45155364 "dev": true,
45165365 "license": "MIT",
4517 "peerDependencies": {
4518 "@remix-run/react": "^2",
4519 "@sveltejs/kit": "^1 || ^2",
4520 "next": ">= 13",
4521 "nuxt": ">= 3",
4522 "react": "^18 || ^19 || ^19.0.0-rc",
4523 "svelte": ">= 4",
4524 "vue": "^3",
4525 "vue-router": "^4"
4526 },
4527 "peerDependenciesMeta": {
4528 "@remix-run/react": {
4529 "optional": true
4530 },
4531 "@sveltejs/kit": {
4532 "optional": true
4533 },
4534 "next": {
4535 "optional": true
4536 },
4537 "nuxt": {
4538 "optional": true
4539 },
4540 "react": {
4541 "optional": true
4542 },
4543 "svelte": {
4544 "optional": true
4545 },
4546 "vue": {
4547 "optional": true
4548 },
4549 "vue-router": {
4550 "optional": true
4551 }
5366+ "engines": {
5367+ "node": "18 || 20 || >=22"
45525368 }
45535369 },
4554 "node_modules/@vitest/mocker": {
4555 "version": "5.0.1",
4556 "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.1.tgz",
4557 "integrity": "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q==",
5370+ "node_modules/@vscode/vsce/node_modules/brace-expansion": {
5371+ "version": "5.0.12",
5372+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
5373+ "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
45585374 "dev": true,
45595375 "license": "MIT",
45605376 "dependencies": {
4561 "@jridgewell/trace-mapping": "0.3.31",
4562 "@vitest/spy": "5.0.1",
4563 "estree-walker": "^3.0.3",
4564 "magic-string": "^1.2.3"
4565 },
4566 "funding": {
4567 "url": "https://opencollective.com/vitest"
4568 },
4569 "peerDependencies": {
4570 "msw": "^2.4.9",
4571 "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
5377+ "balanced-match": "^4.0.2"
45725378 },
4573 "peerDependenciesMeta": {
4574 "msw": {
4575 "optional": true
4576 },
4577 "vite": {
4578 "optional": true
4579 }
5379+ "engines": {
5380+ "node": "20 || >=22"
45805381 }
45815382 },
4582 "node_modules/@vitest/mocker/node_modules/magic-string": {
4583 "version": "1.4.1",
4584 "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.1.tgz",
4585 "integrity": "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==",
5383+ "node_modules/@vscode/vsce/node_modules/commander": {
5384+ "version": "12.1.0",
5385+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
5386+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
45865387 "dev": true,
45875388 "license": "MIT",
4588 "dependencies": {
4589 "@jridgewell/sourcemap-codec": "^1.6.0"
5389+ "engines": {
5390+ "node": ">=18"
45905391 }
45915392 },
4592 "node_modules/@vitest/spy": {
4593 "version": "5.0.1",
4594 "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.1.tgz",
4595 "integrity": "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==",
5393+ "node_modules/@vscode/vsce/node_modules/minimatch": {
5394+ "version": "10.2.6",
5395+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
5396+ "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
45965397 "dev": true,
4597 "license": "MIT",
5398+ "license": "BlueOak-1.0.0",
5399+ "dependencies": {
5400+ "brace-expansion": "^5.0.8"
5401+ },
5402+ "engines": {
5403+ "node": "18 || 20 || >=22"
5404+ },
45985405 "funding": {
4599 "url": "https://opencollective.com/vitest"
5406+ "url": "https://github.com/sponsors/isaacs"
5407+ }
5408+ },
5409+ "node_modules/@vscode/vsce/node_modules/semver": {
5410+ "version": "7.8.5",
5411+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
5412+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
5413+ "dev": true,
5414+ "license": "ISC",
5415+ "bin": {
5416+ "semver": "bin/semver.js"
5417+ },
5418+ "engines": {
5419+ "node": ">=10"
46005420 }
46015421 },
46025422 "node_modules/accepts": {
@@ -4636,6 +5456,16 @@
46365456 "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
46375457 }
46385458 },
5459+ "node_modules/agent-base": {
5460+ "version": "7.1.4",
5461+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
5462+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
5463+ "dev": true,
5464+ "license": "MIT",
5465+ "engines": {
5466+ "node": ">= 14"
5467+ }
5468+ },
46395469 "node_modules/ajv": {
46405470 "version": "6.15.0",
46415471 "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
@@ -5007,6 +5837,17 @@
50075837 "node": ">= 0.4"
50085838 }
50095839 },
5840+ "node_modules/azure-devops-node-api": {
5841+ "version": "12.5.0",
5842+ "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz",
5843+ "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==",
5844+ "dev": true,
5845+ "license": "MIT",
5846+ "dependencies": {
5847+ "tunnel": "0.0.6",
5848+ "typed-rest-client": "^1.8.4"
5849+ }
5850+ },
50105851 "node_modules/balanced-match": {
50115852 "version": "1.0.2",
50125853 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -5027,6 +5868,22 @@
50275868 "node": ">=6.0.0"
50285869 }
50295870 },
5871+ "node_modules/binaryextensions": {
5872+ "version": "6.11.0",
5873+ "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz",
5874+ "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==",
5875+ "dev": true,
5876+ "license": "Artistic-2.0",
5877+ "dependencies": {
5878+ "editions": "^6.21.0"
5879+ },
5880+ "engines": {
5881+ "node": ">=4"
5882+ },
5883+ "funding": {
5884+ "url": "https://bevry.me/fund"
5885+ }
5886+ },
50305887 "node_modules/body-parser": {
50315888 "version": "2.3.0",
50325889 "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz",
@@ -5066,6 +5923,13 @@
50665923 "url": "https://opencollective.com/express"
50675924 }
50685925 },
5926+ "node_modules/boundary": {
5927+ "version": "2.0.0",
5928+ "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz",
5929+ "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==",
5930+ "dev": true,
5931+ "license": "BSD-2-Clause"
5932+ },
50695933 "node_modules/brace-expansion": {
50705934 "version": "1.1.21",
50715935 "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
@@ -5124,6 +5988,23 @@
51245988 "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
51255989 }
51265990 },
5991+ "node_modules/buffer-crc32": {
5992+ "version": "0.2.13",
5993+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
5994+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
5995+ "dev": true,
5996+ "license": "MIT",
5997+ "engines": {
5998+ "node": "*"
5999+ }
6000+ },
6001+ "node_modules/buffer-equal-constant-time": {
6002+ "version": "1.0.1",
6003+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
6004+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
6005+ "dev": true,
6006+ "license": "BSD-3-Clause"
6007+ },
51276008 "node_modules/bundle-name": {
51286009 "version": "4.1.0",
51296010 "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
@@ -5363,6 +6244,16 @@
53636244 "node": ">=20"
53646245 }
53656246 },
6247+ "node_modules/cockatiel": {
6248+ "version": "3.2.1",
6249+ "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz",
6250+ "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==",
6251+ "dev": true,
6252+ "license": "MIT",
6253+ "engines": {
6254+ "node": ">=16"
6255+ }
6256+ },
53666257 "node_modules/code-block-writer": {
53676258 "version": "13.0.3",
53686259 "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz",
@@ -5556,6 +6447,13 @@
55566447 "node": ">=6.6.0"
55576448 }
55586449 },
6450+ "node_modules/core-util-is": {
6451+ "version": "1.0.3",
6452+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
6453+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
6454+ "dev": true,
6455+ "license": "MIT"
6456+ },
55596457 "node_modules/cors": {
55606458 "version": "2.8.6",
55616459 "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
@@ -5960,6 +6858,33 @@
59606858 "node": ">= 0.4"
59616859 }
59626860 },
6861+ "node_modules/ecdsa-sig-formatter": {
6862+ "version": "1.0.11",
6863+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
6864+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
6865+ "dev": true,
6866+ "license": "Apache-2.0",
6867+ "dependencies": {
6868+ "safe-buffer": "^5.0.1"
6869+ }
6870+ },
6871+ "node_modules/editions": {
6872+ "version": "6.22.0",
6873+ "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz",
6874+ "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==",
6875+ "dev": true,
6876+ "license": "Artistic-2.0",
6877+ "dependencies": {
6878+ "version-range": "^4.15.0"
6879+ },
6880+ "engines": {
6881+ "ecmascript": ">= es5",
6882+ "node": ">=4"
6883+ },
6884+ "funding": {
6885+ "url": "https://bevry.me/fund"
6886+ }
6887+ },
59636888 "node_modules/ee-first": {
59646889 "version": "1.1.1",
59656890 "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -6019,6 +6944,19 @@
60196944 "node": ">=8.6"
60206945 }
60216946 },
6947+ "node_modules/entities": {
6948+ "version": "8.1.0",
6949+ "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz",
6950+ "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
6951+ "dev": true,
6952+ "license": "BSD-2-Clause",
6953+ "engines": {
6954+ "node": ">=20.19.0"
6955+ },
6956+ "funding": {
6957+ "url": "https://github.com/fb55/entities?sponsor=1"
6958+ }
6959+ },
60226960 "node_modules/env-paths": {
60236961 "version": "2.2.1",
60246962 "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
@@ -7544,6 +8482,39 @@
75448482 "node": ">=16.9.0"
75458483 }
75468484 },
8485+ "node_modules/hosted-git-info": {
8486+ "version": "4.1.0",
8487+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
8488+ "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
8489+ "dev": true,
8490+ "license": "ISC",
8491+ "dependencies": {
8492+ "lru-cache": "^6.0.0"
8493+ },
8494+ "engines": {
8495+ "node": ">=10"
8496+ }
8497+ },
8498+ "node_modules/hosted-git-info/node_modules/lru-cache": {
8499+ "version": "6.0.0",
8500+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
8501+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
8502+ "dev": true,
8503+ "license": "ISC",
8504+ "dependencies": {
8505+ "yallist": "^4.0.0"
8506+ },
8507+ "engines": {
8508+ "node": ">=10"
8509+ }
8510+ },
8511+ "node_modules/hosted-git-info/node_modules/yallist": {
8512+ "version": "4.0.0",
8513+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
8514+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
8515+ "dev": true,
8516+ "license": "ISC"
8517+ },
75478518 "node_modules/html-void-elements": {
75488519 "version": "3.0.0",
75498520 "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
@@ -7576,6 +8547,34 @@
75768547 "url": "https://opencollective.com/express"
75778548 }
75788549 },
8550+ "node_modules/http-proxy-agent": {
8551+ "version": "7.0.2",
8552+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
8553+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
8554+ "dev": true,
8555+ "license": "MIT",
8556+ "dependencies": {
8557+ "agent-base": "^7.1.0",
8558+ "debug": "^4.3.4"
8559+ },
8560+ "engines": {
8561+ "node": ">= 14"
8562+ }
8563+ },
8564+ "node_modules/https-proxy-agent": {
8565+ "version": "7.0.6",
8566+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
8567+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
8568+ "dev": true,
8569+ "license": "MIT",
8570+ "dependencies": {
8571+ "agent-base": "^7.1.2",
8572+ "debug": "4"
8573+ },
8574+ "engines": {
8575+ "node": ">= 14"
8576+ }
8577+ },
75798578 "node_modules/human-signals": {
75808579 "version": "8.0.1",
75818580 "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz",
@@ -7613,6 +8612,13 @@
76138612 "node": ">= 4"
76148613 }
76158614 },
8615+ "node_modules/immediate": {
8616+ "version": "3.0.6",
8617+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
8618+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
8619+ "dev": true,
8620+ "license": "MIT"
8621+ },
76168622 "node_modules/import-fresh": {
76178623 "version": "3.3.1",
76188624 "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -8280,6 +9286,24 @@
82809286 "dev": true,
82819287 "license": "ISC"
82829288 },
9289+ "node_modules/istextorbinary": {
9290+ "version": "9.5.0",
9291+ "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz",
9292+ "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==",
9293+ "dev": true,
9294+ "license": "Artistic-2.0",
9295+ "dependencies": {
9296+ "binaryextensions": "^6.11.0",
9297+ "editions": "^6.21.0",
9298+ "textextensions": "^6.11.0"
9299+ },
9300+ "engines": {
9301+ "node": ">=4"
9302+ },
9303+ "funding": {
9304+ "url": "https://bevry.me/fund"
9305+ }
9306+ },
82839307 "node_modules/iterator.prototype": {
82849308 "version": "1.1.5",
82859309 "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
@@ -8409,6 +9433,13 @@
84099433 "node": ">=6"
84109434 }
84119435 },
9436+ "node_modules/jsonc-parser": {
9437+ "version": "3.3.1",
9438+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
9439+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
9440+ "dev": true,
9441+ "license": "MIT"
9442+ },
84129443 "node_modules/jsonfile": {
84139444 "version": "6.2.1",
84149445 "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
@@ -8422,6 +9453,42 @@
84229453 "graceful-fs": "^4.1.6"
84239454 }
84249455 },
9456+ "node_modules/jsonwebtoken": {
9457+ "version": "9.0.3",
9458+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
9459+ "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
9460+ "dev": true,
9461+ "license": "MIT",
9462+ "dependencies": {
9463+ "jws": "^4.0.1",
9464+ "lodash.includes": "^4.3.0",
9465+ "lodash.isboolean": "^3.0.3",
9466+ "lodash.isinteger": "^4.0.4",
9467+ "lodash.isnumber": "^3.0.3",
9468+ "lodash.isplainobject": "^4.0.6",
9469+ "lodash.isstring": "^4.0.1",
9470+ "lodash.once": "^4.0.0",
9471+ "ms": "^2.1.1",
9472+ "semver": "^7.5.4"
9473+ },
9474+ "engines": {
9475+ "node": ">=12",
9476+ "npm": ">=6"
9477+ }
9478+ },
9479+ "node_modules/jsonwebtoken/node_modules/semver": {
9480+ "version": "7.8.5",
9481+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
9482+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
9483+ "dev": true,
9484+ "license": "ISC",
9485+ "bin": {
9486+ "semver": "bin/semver.js"
9487+ },
9488+ "engines": {
9489+ "node": ">=10"
9490+ }
9491+ },
84259492 "node_modules/jsx-ast-utils": {
84269493 "version": "3.3.5",
84279494 "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -8438,6 +9505,42 @@
84389505 "node": ">=4.0"
84399506 }
84409507 },
9508+ "node_modules/jszip": {
9509+ "version": "3.10.2",
9510+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.2.tgz",
9511+ "integrity": "sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==",
9512+ "dev": true,
9513+ "license": "(MIT OR GPL-3.0-or-later)",
9514+ "dependencies": {
9515+ "lie": "~3.3.0",
9516+ "pako": "~1.0.2",
9517+ "readable-stream": "~2.3.6",
9518+ "setimmediate": "^1.0.5"
9519+ }
9520+ },
9521+ "node_modules/jwa": {
9522+ "version": "2.0.1",
9523+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
9524+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
9525+ "dev": true,
9526+ "license": "MIT",
9527+ "dependencies": {
9528+ "buffer-equal-constant-time": "^1.0.1",
9529+ "ecdsa-sig-formatter": "1.0.11",
9530+ "safe-buffer": "^5.0.1"
9531+ }
9532+ },
9533+ "node_modules/jws": {
9534+ "version": "4.0.1",
9535+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
9536+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
9537+ "dev": true,
9538+ "license": "MIT",
9539+ "dependencies": {
9540+ "jwa": "^2.0.1",
9541+ "safe-buffer": "^5.0.1"
9542+ }
9543+ },
84419544 "node_modules/keyv": {
84429545 "version": "4.5.4",
84439546 "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -8492,6 +9595,16 @@
84929595 "node": ">= 0.8.0"
84939596 }
84949597 },
9598+ "node_modules/lie": {
9599+ "version": "3.3.0",
9600+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
9601+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
9602+ "dev": true,
9603+ "license": "MIT",
9604+ "dependencies": {
9605+ "immediate": "~3.0.5"
9606+ }
9607+ },
84959608 "node_modules/lightningcss": {
84969609 "version": "1.32.0",
84979610 "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
@@ -8776,6 +9889,48 @@
87769889 "url": "https://github.com/sponsors/sindresorhus"
87779890 }
87789891 },
9892+ "node_modules/lodash.includes": {
9893+ "version": "4.3.0",
9894+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
9895+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
9896+ "dev": true,
9897+ "license": "MIT"
9898+ },
9899+ "node_modules/lodash.isboolean": {
9900+ "version": "3.0.3",
9901+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
9902+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
9903+ "dev": true,
9904+ "license": "MIT"
9905+ },
9906+ "node_modules/lodash.isinteger": {
9907+ "version": "4.0.4",
9908+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
9909+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
9910+ "dev": true,
9911+ "license": "MIT"
9912+ },
9913+ "node_modules/lodash.isnumber": {
9914+ "version": "3.0.3",
9915+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
9916+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
9917+ "dev": true,
9918+ "license": "MIT"
9919+ },
9920+ "node_modules/lodash.isplainobject": {
9921+ "version": "4.0.6",
9922+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
9923+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
9924+ "dev": true,
9925+ "license": "MIT"
9926+ },
9927+ "node_modules/lodash.isstring": {
9928+ "version": "4.0.1",
9929+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
9930+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
9931+ "dev": true,
9932+ "license": "MIT"
9933+ },
87799934 "node_modules/lodash.merge": {
87809935 "version": "4.6.2",
87819936 "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -8783,6 +9938,13 @@
87839938 "dev": true,
87849939 "license": "MIT"
87859940 },
9941+ "node_modules/lodash.once": {
9942+ "version": "4.1.1",
9943+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
9944+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
9945+ "dev": true,
9946+ "license": "MIT"
9947+ },
87869948 "node_modules/log-symbols": {
87879949 "version": "6.0.0",
87889950 "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
@@ -8869,6 +10031,19 @@
886910031 "@jridgewell/sourcemap-codec": "^1.5.5"
887010032 }
887110033 },
10034+ "node_modules/marked": {
10035+ "version": "18.0.13",
10036+ "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.13.tgz",
10037+ "integrity": "sha512-xTxVzZsBFwunP6HDmtBkabUQEYArnP7/rMDGmPj9SlrKlQ4i8MdYVow+nJL0eOqwpUqhzBoTBRADGN6uYwPyOw==",
10038+ "dev": true,
10039+ "license": "MIT",
10040+ "bin": {
10041+ "marked": "bin/marked.js"
10042+ },
10043+ "engines": {
10044+ "node": ">= 20"
10045+ }
10046+ },
887210047 "node_modules/math-intrinsics": {
887310048 "version": "1.1.0",
887410049 "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -9053,6 +10228,19 @@
905310228 "node": ">=8.6"
905410229 }
905510230 },
10231+ "node_modules/mime": {
10232+ "version": "1.6.0",
10233+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
10234+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
10235+ "dev": true,
10236+ "license": "MIT",
10237+ "bin": {
10238+ "mime": "cli.js"
10239+ },
10240+ "engines": {
10241+ "node": ">=4"
10242+ }
10243+ },
905610244 "node_modules/mime-db": {
905710245 "version": "1.54.0",
905810246 "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
@@ -9133,6 +10321,13 @@
913310321 "dev": true,
913410322 "license": "MIT"
913510323 },
10324+ "node_modules/mute-stream": {
10325+ "version": "0.0.8",
10326+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
10327+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
10328+ "dev": true,
10329+ "license": "ISC"
10330+ },
913610331 "node_modules/nanoid": {
913710332 "version": "3.3.19",
913810333 "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz",
@@ -9729,6 +10924,13 @@
972910924 "node": ">=6"
973010925 }
973110926 },
10927+ "node_modules/pako": {
10928+ "version": "1.0.11",
10929+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
10930+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
10931+ "dev": true,
10932+ "license": "(MIT AND Zlib)"
10933+ },
973210934 "node_modules/parent-module": {
973310935 "version": "1.0.1",
973410936 "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -9774,6 +10976,19 @@
977410976 "url": "https://github.com/sponsors/sindresorhus"
977510977 }
977610978 },
10979+ "node_modules/parse5": {
10980+ "version": "8.0.1",
10981+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
10982+ "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
10983+ "dev": true,
10984+ "license": "MIT",
10985+ "dependencies": {
10986+ "entities": "^8.0.0"
10987+ },
10988+ "funding": {
10989+ "url": "https://github.com/inikulin/parse5?sponsor=1"
10990+ }
10991+ },
977710992 "node_modules/parseurl": {
977810993 "version": "1.3.3",
977910994 "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -9829,6 +11044,13 @@
982911044 "url": "https://opencollective.com/express"
983011045 }
983111046 },
11047+ "node_modules/pend": {
11048+ "version": "1.2.0",
11049+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
11050+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
11051+ "dev": true,
11052+ "license": "MIT"
11053+ },
983211054 "node_modules/picocolors": {
983311055 "version": "1.1.1",
983411056 "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -10059,6 +11281,13 @@
1005911281 "url": "https://github.com/sponsors/sindresorhus"
1006011282 }
1006111283 },
11284+ "node_modules/process-nextick-args": {
11285+ "version": "2.0.1",
11286+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
11287+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
11288+ "dev": true,
11289+ "license": "MIT"
11290+ },
1006211291 "node_modules/prompts": {
1006311292 "version": "2.4.2",
1006411293 "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@@ -10095,6 +11324,25 @@
1009511324 "react-is": "^16.13.1"
1009611325 }
1009711326 },
11327+ "node_modules/proper-lockfile": {
11328+ "version": "4.1.2",
11329+ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
11330+ "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
11331+ "dev": true,
11332+ "license": "MIT",
11333+ "dependencies": {
11334+ "graceful-fs": "^4.2.4",
11335+ "retry": "^0.12.0",
11336+ "signal-exit": "^3.0.2"
11337+ }
11338+ },
11339+ "node_modules/proper-lockfile/node_modules/signal-exit": {
11340+ "version": "3.0.7",
11341+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
11342+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
11343+ "dev": true,
11344+ "license": "ISC"
11345+ },
1009811346 "node_modules/property-information": {
1009911347 "version": "7.2.0",
1010011348 "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
@@ -10304,6 +11552,49 @@
1030411552 }
1030511553 }
1030611554 },
11555+ "node_modules/read": {
11556+ "version": "1.0.7",
11557+ "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
11558+ "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==",
11559+ "dev": true,
11560+ "license": "ISC",
11561+ "dependencies": {
11562+ "mute-stream": "~0.0.4"
11563+ },
11564+ "engines": {
11565+ "node": ">=0.8"
11566+ }
11567+ },
11568+ "node_modules/readable-stream": {
11569+ "version": "2.3.8",
11570+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
11571+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
11572+ "dev": true,
11573+ "license": "MIT",
11574+ "dependencies": {
11575+ "core-util-is": "~1.0.0",
11576+ "inherits": "~2.0.3",
11577+ "isarray": "~1.0.0",
11578+ "process-nextick-args": "~2.0.0",
11579+ "safe-buffer": "~5.1.1",
11580+ "string_decoder": "~1.1.1",
11581+ "util-deprecate": "~1.0.1"
11582+ }
11583+ },
11584+ "node_modules/readable-stream/node_modules/isarray": {
11585+ "version": "1.0.0",
11586+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
11587+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
11588+ "dev": true,
11589+ "license": "MIT"
11590+ },
11591+ "node_modules/readable-stream/node_modules/safe-buffer": {
11592+ "version": "5.1.2",
11593+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
11594+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
11595+ "dev": true,
11596+ "license": "MIT"
11597+ },
1030711598 "node_modules/recast": {
1030811599 "version": "0.23.21",
1030911600 "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.21.tgz",
@@ -10486,6 +11777,16 @@
1048611777 "url": "https://github.com/sponsors/sindresorhus"
1048711778 }
1048811779 },
11780+ "node_modules/retry": {
11781+ "version": "0.12.0",
11782+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
11783+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
11784+ "dev": true,
11785+ "license": "MIT",
11786+ "engines": {
11787+ "node": ">= 4"
11788+ }
11789+ },
1048911790 "node_modules/reusify": {
1049011791 "version": "1.1.0",
1049111792 "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
@@ -10606,6 +11907,27 @@
1060611907 "url": "https://github.com/sponsors/ljharb"
1060711908 }
1060811909 },
11910+ "node_modules/safe-buffer": {
11911+ "version": "5.2.1",
11912+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
11913+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
11914+ "dev": true,
11915+ "funding": [
11916+ {
11917+ "type": "github",
11918+ "url": "https://github.com/sponsors/feross"
11919+ },
11920+ {
11921+ "type": "patreon",
11922+ "url": "https://www.patreon.com/feross"
11923+ },
11924+ {
11925+ "type": "consulting",
11926+ "url": "https://feross.org/support"
11927+ }
11928+ ],
11929+ "license": "MIT"
11930+ },
1060911931 "node_modules/safe-push-apply": {
1061011932 "version": "1.0.0",
1061111933 "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
@@ -10648,6 +11970,16 @@
1064811970 "dev": true,
1064911971 "license": "MIT"
1065011972 },
11973+ "node_modules/sax": {
11974+ "version": "1.6.1",
11975+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
11976+ "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
11977+ "dev": true,
11978+ "license": "BlueOak-1.0.0",
11979+ "engines": {
11980+ "node": ">=11.0.0"
11981+ }
11982+ },
1065111983 "node_modules/scheduler": {
1065211984 "version": "0.27.0",
1065311985 "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
@@ -10761,6 +12093,13 @@
1076112093 "node": ">= 0.4"
1076212094 }
1076312095 },
12096+ "node_modules/setimmediate": {
12097+ "version": "1.0.5",
12098+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
12099+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
12100+ "dev": true,
12101+ "license": "MIT"
12102+ },
1076412103 "node_modules/setprototypeof": {
1076512104 "version": "1.2.0",
1076612105 "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -11207,6 +12546,23 @@
1120712546 "node": ">= 0.4"
1120812547 }
1120912548 },
12549+ "node_modules/string_decoder": {
12550+ "version": "1.1.1",
12551+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
12552+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
12553+ "dev": true,
12554+ "license": "MIT",
12555+ "dependencies": {
12556+ "safe-buffer": "~5.1.0"
12557+ }
12558+ },
12559+ "node_modules/string_decoder/node_modules/safe-buffer": {
12560+ "version": "5.1.2",
12561+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
12562+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
12563+ "dev": true,
12564+ "license": "MIT"
12565+ },
1121012566 "node_modules/string-width": {
1121112567 "version": "7.2.0",
1121212568 "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
@@ -11470,6 +12826,16 @@
1147012826 "url": "https://github.com/sponsors/sindresorhus"
1147112827 }
1147212828 },
12829+ "node_modules/structured-source": {
12830+ "version": "4.0.0",
12831+ "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz",
12832+ "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==",
12833+ "dev": true,
12834+ "license": "BSD-2-Clause",
12835+ "dependencies": {
12836+ "boundary": "^2.0.0"
12837+ }
12838+ },
1147312839 "node_modules/styled-jsx": {
1147412840 "version": "5.1.6",
1147512841 "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
@@ -11579,6 +12945,22 @@
1157912945 "url": "https://opencollective.com/webpack"
1158012946 }
1158112947 },
12948+ "node_modules/textextensions": {
12949+ "version": "6.11.0",
12950+ "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz",
12951+ "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==",
12952+ "dev": true,
12953+ "license": "Artistic-2.0",
12954+ "dependencies": {
12955+ "editions": "^6.21.0"
12956+ },
12957+ "engines": {
12958+ "node": ">=4"
12959+ },
12960+ "funding": {
12961+ "url": "https://bevry.me/fund"
12962+ }
12963+ },
1158212964 "node_modules/tiny-invariant": {
1158312965 "version": "1.3.3",
1158412966 "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@@ -11764,6 +13146,16 @@
1176413146 "fsevents": "~2.3.3"
1176513147 }
1176613148 },
13149+ "node_modules/tunnel": {
13150+ "version": "0.0.6",
13151+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
13152+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
13153+ "dev": true,
13154+ "license": "MIT",
13155+ "engines": {
13156+ "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
13157+ }
13158+ },
1176713159 "node_modules/tw-animate-css": {
1176813160 "version": "1.4.0",
1176913161 "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
@@ -11898,6 +13290,18 @@
1189813290 "url": "https://github.com/sponsors/ljharb"
1189913291 }
1190013292 },
13293+ "node_modules/typed-rest-client": {
13294+ "version": "1.8.11",
13295+ "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz",
13296+ "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==",
13297+ "dev": true,
13298+ "license": "MIT",
13299+ "dependencies": {
13300+ "qs": "^6.9.1",
13301+ "tunnel": "0.0.6",
13302+ "underscore": "^1.12.1"
13303+ }
13304+ },
1190113305 "node_modules/typescript": {
1190213306 "version": "5.9.3",
1190313307 "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -11955,6 +13359,13 @@
1195513359 "url": "https://github.com/sponsors/ljharb"
1195613360 }
1195713361 },
13362+ "node_modules/underscore": {
13363+ "version": "1.13.8",
13364+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz",
13365+ "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==",
13366+ "dev": true,
13367+ "license": "MIT"
13368+ },
1195813369 "node_modules/undici": {
1195913370 "version": "7.29.1",
1196013371 "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.1.tgz",
@@ -12157,6 +13568,13 @@
1215713568 "punycode": "^2.1.0"
1215813569 }
1215913570 },
13571+ "node_modules/url-join": {
13572+ "version": "4.0.1",
13573+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
13574+ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
13575+ "dev": true,
13576+ "license": "MIT"
13577+ },
1216013578 "node_modules/use-callback-ref": {
1216113579 "version": "1.3.3",
1216213580 "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
@@ -12239,6 +13657,19 @@
1223913657 "node": ">= 0.8"
1224013658 }
1224113659 },
13660+ "node_modules/version-range": {
13661+ "version": "4.15.0",
13662+ "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz",
13663+ "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==",
13664+ "dev": true,
13665+ "license": "Artistic-2.0",
13666+ "engines": {
13667+ "node": ">=4"
13668+ },
13669+ "funding": {
13670+ "url": "https://bevry.me/fund"
13671+ }
13672+ },
1224213673 "node_modules/vfile": {
1224313674 "version": "6.0.3",
1224413675 "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
@@ -12910,6 +14341,30 @@
1291014341 "url": "https://github.com/sponsors/sindresorhus"
1291114342 }
1291214343 },
14344+ "node_modules/xml2js": {
14345+ "version": "0.5.0",
14346+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
14347+ "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
14348+ "dev": true,
14349+ "license": "MIT",
14350+ "dependencies": {
14351+ "sax": ">=0.6.0",
14352+ "xmlbuilder": "~11.0.0"
14353+ },
14354+ "engines": {
14355+ "node": ">=4.0.0"
14356+ }
14357+ },
14358+ "node_modules/xmlbuilder": {
14359+ "version": "11.0.1",
14360+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
14361+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
14362+ "dev": true,
14363+ "license": "MIT",
14364+ "engines": {
14365+ "node": ">=4.0"
14366+ }
14367+ },
1291314368 "node_modules/yallist": {
1291414369 "version": "3.1.1",
1291514370 "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -12932,6 +14387,29 @@
1293214387 "url": "https://github.com/sponsors/eemeli"
1293314388 }
1293414389 },
14390+ "node_modules/yauzl": {
14391+ "version": "3.4.0",
14392+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz",
14393+ "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==",
14394+ "dev": true,
14395+ "license": "MIT",
14396+ "dependencies": {
14397+ "pend": "~1.2.0"
14398+ },
14399+ "engines": {
14400+ "node": ">=12"
14401+ }
14402+ },
14403+ "node_modules/yazl": {
14404+ "version": "2.5.1",
14405+ "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz",
14406+ "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==",
14407+ "dev": true,
14408+ "license": "MIT",
14409+ "dependencies": {
14410+ "buffer-crc32": "~0.2.3"
14411+ }
14412+ },
1293514413 "node_modules/yocto-queue": {
1293614414 "version": "0.1.0",
1293714415 "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

package.json

+11 3read-only10%
intentunspecifiedconfidence unknown
@@ -1,7 +1,7 @@
11 {
22 "name": "whymark",
33 "version": "0.3.0",
4 "description": "whymark \u2014 a file format and reviewer for reading AI-written code changes with the reasoning, sources, and verification attached to each line.",
4+ "description": "whymark a file format and reviewer for reading AI-written code changes with the reasoning, sources, and verification attached to each line.",
55 "author": "Samuel Spink",
66 "license": "MIT",
77 "bin": {
@@ -37,7 +37,7 @@
3737 "scripts": {
3838 "dev": "next dev --port 43917",
3939 "build": "next build",
40 "build:cli": "esbuild src/cli/whymark.ts --bundle --platform=node --format=esm --outfile=dist/whymark.mjs --external:yaml --legal-comments=none --banner:js='/* generated by npm run build:cli \u2014 do not edit */'",
40+ "build:cli": "esbuild src/cli/whymark.ts --bundle --platform=node --format=esm --outfile=dist/whymark.mjs --external:yaml --legal-comments=none --banner:js='/* generated by npm run build:cli do not edit */'",
4141 "prepublishOnly": "npm run build:cli",
4242 "start": "next start --port 43917",
4343 "lint": "eslint",
@@ -46,7 +46,11 @@
4646 "test:watch": "vitest",
4747 "test:ui": "node tests/e2e/viewer.mjs",
4848 "whymark": "tsx src/cli/whymark.ts",
49 "prepack": "npm run build:cli"
49+ "prepack": "npm run build:cli",
50+ "build:vscode": "node extensions/vscode/scripts/build.mjs",
51+ "package:vscode": "npm run build:vscode && cd extensions/vscode && vsce package --no-dependencies --allow-missing-repository --out ../../.artifacts/whymark-vscode.vsix",
52+ "test:vscode": "npm run build:vscode && node extensions/vscode/scripts/test.mjs",
53+ "test:vscode:ui": "npm run build:vscode && node tests/e2e/vscode.mjs"
5054 },
5155 "dependencies": {
5256 "yaml": "^2.8.1"
@@ -60,7 +64,10 @@
6064 "@types/node": "^22",
6165 "@types/react": "^19",
6266 "@types/react-dom": "^19",
67+ "@types/vscode": "^1.96.0",
6368 "@vercel/analytics": "^2.0.1",
69+ "@vscode/test-electron": "^3.1.0",
70+ "@vscode/vsce": "^4.0.0",
6471 "class-variance-authority": "^0.7.1",
6572 "clsx": "^2.1.1",
6673 "cn": "^0.3.0",
@@ -70,6 +77,7 @@
7077 "lucide-react": "^0.545.0",
7178 "next": "16.3.5",
7279 "playwright": "^1.63.0",
80+ "postcss": "^8.5.28",
7381 "react": "19.2.8",
7482 "react-dom": "19.2.8",
7583 "shadcn": "^4.21.0",

src/app/r/[slug]/actions.ts

+1 9read-only10%
intentunspecifiedconfidence unknown
@@ -14,15 +14,7 @@import {
1414 import { canWriteWorkingTree } from "@/lib/site";
1515 import type { FileSection } from "@/lib/whymark/types";
1616  
17export interface EditResult {
18 ok: boolean;
19 /** Present on success: the file's hash after writing, so the client can re-check. */
20 newSha?: string;
21 discarded?: number;
22 restored?: number;
23 partsReverted?: number;
24 error?: string;
25}
17+import type { EditResult } from "@/lib/review-actions";
2618  
2719 /**
2820 * These actions write to the working tree of whoever is running the dev server,

src/app/r/[slug]/page.tsx

+2 2read-only10%
intentunspecifiedconfidence unknown
@@ -5,7 +5,7 @@import { validateDocumentInRepo } from "@/lib/whymark/validate-tree";
55 import { isActionable } from "@/lib/whymark/edit";
66 import { hashObject } from "@/lib/whymark/git";
77 import { buildReviewVM } from "@/lib/view-model";
8import { ReviewView } from "@/components/whymark/review-view";
8+import { WebReviewView } from "@/components/whymark/web-review-view";
99 import { canWriteWorkingTree } from "@/lib/site";
1010  
1111 export const dynamic = "force-dynamic";
@@ -48,5 +48,5 @@export default async function ReviewPage({ params }: PageProps<"/r/[slug]">) {
4848 ),
4949 ]);
5050  
51 return <ReviewView review={vm} slug={review.slug} issues={validation.diagnostics} />;
51+ return <WebReviewView review={vm} slug={review.slug} issues={validation.diagnostics} />;
5252 }

src/components/whymark/decide.tsx

+4 2read-only10%
intentunspecifiedconfidence unknown
@@ -278,11 +278,13 @@export interface ApplySummary {
278278 * out of sight, and the reviewer never learns there is anything to apply.
279279 */
280280 export function ApplyControls({
281+ target = "Working tree",
281282 summary,
282283 busy,
283284 onApply,
284285 onReset,
285286 }: {
287+ target?: string;
286288 summary: ApplySummary;
287289 busy: boolean;
288290 onApply: () => void;
@@ -299,7 +301,7 @@export function ApplyControls({
299301 backgroundColor: "color-mix(in oklch, var(--whymark-del) 8%, transparent)",
300302 }}
301303 >
302 <span className="hidden text-[11.5px] lg:inline" title="writes to your working tree">
304+ <span className="hidden text-[11.5px] lg:inline" title={`updates ${target.toLowerCase()}`}>
303305 {describe(summary)}
304306 </span>
305307 <span className="text-[11.5px] lg:hidden">{pending} to undo</span>
@@ -315,7 +317,7 @@export function ApplyControls({
315317 disabled={busy}
316318 onClick={onApply}
317319 className="flex items-center gap-1.5 rounded bg-foreground px-2 py-0.5 text-[11.5px] font-medium text-background transition-opacity disabled:opacity-50"
318 title="Write these decisions to the files in your working tree"
320+ title={`Apply these decisions to ${target.toLowerCase()}`}
319321 >
320322 {busy ? <Loader2 className="size-3 animate-spin" /> : null}
321323 apply

src/components/whymark/review-view.tsx

+24 25read-only10%
intentunspecifiedconfidence unknown
@@ -1,7 +1,6 @@
11 "use client";
22  
33 import { useCallback, useEffect, useMemo, useState } from "react";
4import Link from "next/link";
54 import {
65 AlertTriangle,
76 ArrowLeft,
@@ -20,7 +19,7 @@import type { Diagnostic, Note, NoteKind } from "@/lib/whymark/types";
2019 import type { ReviewVM } from "@/lib/view-model";
2120 import { FilePanel, type ViewMode } from "./file-panel";
2221 import { ApplyControls, ApplyResult } from "./decide";
23import { applyFileDecisions, saveEditedRange } from "@/app/r/[slug]/actions";
22+import type { ReviewActions } from "@/lib/review-actions";
2423 import { isEmpty, summarize, type FileDecisions } from "@/lib/whymark/edit";
2524 import { KIND_META, STATUS_COLOR } from "./meta";
2625 import { CoverageBar, Ring, Stat } from "./meters";
@@ -35,7 +34,12 @@import { useReviewPreferences } from "./use-review-preferences";
3534 import { type ReviewPreferences } from "@/lib/review-preferences";
3635 import { hasPassingVerify } from "@/lib/whymark/stats";
3736  
38interface ReviewViewProps {
37+export interface ReviewViewProps {
38+ mutationTarget?: string;
39+ homeControl?: React.ReactNode;
40+ actions?: ReviewActions;
41+ sourceLabel?: string;
42+ onViewSource?: () => void;
3943 review: ReviewVM;
4044 /** Absent when the document was pasted rather than read from `reviews/`. */
4145 slug?: string;
@@ -44,7 +48,7 @@interface ReviewViewProps {
4448 onBack?: () => void;
4549 }
4650  
47export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
51+export function ReviewView({ review, slug, issues, onBack, homeControl, mutationTarget = "Working tree", actions, sourceLabel, onViewSource }: ReviewViewProps) {
4852 const compact = !useMediaQuery("(min-width: 1180px)", true);
4953 const [preferences, setPreferences] = useReviewPreferences();
5054 const [activeNoteId, setActiveNoteId] = useState<string | null>(null);
@@ -137,7 +141,7 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
137141 * act on. Each file's own hash is re-checked server-side before it is written.
138142 */
139143 const apply = useCallback(async () => {
140 if (!slug) return;
144+ if (!slug || !actions) return;
141145 setApplying(true);
142146 const failures: string[] = [];
143147 let discarded = 0;
@@ -145,7 +149,7 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
145149 let parts = 0;
146150  
147151 for (const [path, fileDecisions] of Object.entries(decisions)) {
148 const result = await applyFileDecisions(slug, path, fileDecisions);
152+ const result = await actions.applyFileDecisions(slug, path, fileDecisions);
149153 if (result.ok) {
150154 discarded += result.discarded ?? 0;
151155 restored += result.restored ?? 0;
@@ -172,9 +176,9 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
172176 .join(", ");
173177 setApplyResult({
174178 ok: true,
175 message: `Working tree updated: ${undone}. This review now describes code you changed — regenerate it with \`whymark new --worktree\` before sharing.`,
179+ message: `${mutationTarget} updated: ${undone}. This review now describes code you changed — regenerate it with \`whymark new --worktree\` before sharing.`,
176180 });
177 }, [decisions, slug]);
181+ }, [decisions, slug, actions, mutationTarget]);
178182  
179183 /**
180184 * Deciding by keyboard, for a reviewer stepping through with j/k: `x` rejects
@@ -182,7 +186,7 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
182186 * just formed an opinion about.
183187 */
184188 const toggleActiveNote = useCallback(() => {
185 if (!slug || !activeNoteId) return;
189+ if (!slug || !actions || !activeNoteId) return;
186190 const file = review.files.find((candidate) =>
187191 candidate.notes.some((note) => note.id === activeNoteId),
188192 );
@@ -218,21 +222,21 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
218222 ),
219223 },
220224 );
221 }, [activeNoteId, decisions, review.files, setFileDecisions, slug]);
225+ }, [activeNoteId, decisions, review.files, setFileDecisions, slug, actions]);
222226  
223227 const onSaveEdit = useCallback(
224228 async (path: string, start: number, end: number, text: string) => {
225 if (!slug) return { ok: false, error: "A pasted review has no file to write to." };
226 const result = await saveEditedRange(slug, path, start, end, text);
229+ if (!slug || !actions) return { ok: false, error: "A pasted review has no file to write to." };
230+ const result = await actions.saveEditedRange(slug, path, start, end, text);
227231 if (result.ok) {
228232 setApplyResult({
229233 ok: true,
230 message: `Saved your edit to ${path}. Regenerate the review so its diff matches the file again.`,
234+ message: `${mutationTarget} updated for ${path}. Regenerate the review so its diff matches the file again.`,
231235 });
232236 }
233237 return result;
234238 },
235 [slug],
239+ [slug, actions, mutationTarget],
236240 );
237241  
238242 useEffect(() => {
@@ -298,15 +302,7 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
298302 >
299303 <ArrowLeft className="size-4" />
300304 </button>
301 ) : (
302 <Link
303 href="/"
304 className="flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-foreground/5 hover:text-foreground"
305 aria-label="Home"
306 >
307 <ArrowLeft className="size-4" />
308 </Link>
309 )}
305+ ) : homeControl}
310306 <div className="min-w-0 flex-1">
311307 <h1 className="truncate text-[13.5px] font-medium leading-tight">{meta.title}</h1>
312308 <p className="truncate font-mono text-[11px] text-muted-foreground">
@@ -328,6 +324,7 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
328324 </div>
329325  
330326 <ApplyControls
327+ target={mutationTarget}
331328 summary={summarize(decisions)}
332329 busy={applying}
333330 onApply={apply}
@@ -645,7 +642,7 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
645642 visibleNoteIds={visibleNoteIds}
646643 activeNoteId={activeNoteId}
647644 onActivate={activate}
648 editable={Boolean(slug)}
645+ editable={Boolean(slug && actions)}
649646 decisions={decisions[file.path]}
650647 onDecisions={setFileDecisions}
651648 onSaveEdit={onSaveEdit}
@@ -654,7 +651,9 @@export function ReviewView({ review, slug, issues, onBack }: ReviewViewProps) {
654651 </div>
655652  
656653 <footer className="mt-8 flex flex-wrap items-center gap-x-4 gap-y-1 border-t border-border/60 pt-4 text-[11.5px] text-muted-foreground">
657 {slug ? (
654+ {onViewSource ? (
655+ <><span>{sourceLabel}</span><button onClick={onViewSource} className="underline">Open review source</button></>
656+ ) : slug ? (
658657 <>
659658 <span className="font-mono">reviews/{slug}.whymark</span>
660659 <a

extensions/vscode/.gitignore

+2 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,2 @@
1+/dist/
2+*.vsix

extensions/vscode/.vscodeignore

+4 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,4 @@
1+src/**
2+scripts/**
3+*.tsbuildinfo
4+**/*.map

extensions/vscode/LICENSE

+52 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,52 @@
1+MIT License
2+
3+Copyright (c) 2026 Samuel Spink
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
22+
23+---
24+
25+AI-Led Development Acknowledgement
26+
27+This software was written primarily by AI coding agents, directed by a human.
28+That is stated plainly here because it is the kind of thing this project exists
29+to make visible, and because you should weigh it when deciding to depend on the
30+code.
31+
32+Every change is read by at least one human before it is merged. That single
33+review is the whole of the process, and it is the only assurance offered. No
34+claim is made that the code has been audited, formally verified, penetration
35+tested, or reviewed by a subject-matter expert in any domain it touches.
36+
37+Two limits are worth naming, since this repository defines a format for
38+recording exactly these things:
39+
40+ * An annotation marked `inference` records what an agent believed while
41+ writing the code, not an established fact. It may be wrong.
42+
43+ * A passing verification claim records that a command succeeded at the moment
44+ a review was written. It is evidence about that moment and nothing more.
45+ Use `whymark verify` to re-establish it.
46+
47+This acknowledgement grants no rights and imposes no conditions beyond the MIT
48+License above; it neither adds to nor narrows that license. It restates the
49+disclaimer for emphasis: the software is provided "as is", without warranty of
50+any kind, and no author, copyright holder, contributor, or reviewer accepts any
51+liability for any claim, damages, or other liability arising from the software,
52+its use, or reliance on any statement recorded in a review within it.

extensions/vscode/README.md

+48 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,48 @@
1+# Whymark for VS Code
2+ 
3+Read AI-written changes with the reasons, sources and verification beside the code — directly in VS Code, without starting a web server.
4+ 
5+## Install locally
6+ 
7+From the repository root:
8+ 
9+```sh
10+npm ci
11+npm run package:vscode
12+code --install-extension .artifacts/whymark-vscode.vsix
13+```
14+ 
15+Or use **Extensions: Install from VSIX…** and select the generated file. No Marketplace publication or GitHub access is needed to install the VSIX. Requires VS Code 1.96+ on a desktop or remote filesystem workspace.
16+ 
17+## Review changes
18+ 
19+Open a `.whymark` file to use the interactive editor, or run a command from the Command Palette / Source Control menu:
20+ 
21+| Command | Comparison |
22+| --- | --- |
23+| Whymark: Review Staged Changes | HEAD → Git index |
24+| Whymark: Review Unstaged Changes | Git index → saved working files |
25+| Whymark: Review All Working Changes | HEAD → saved working files |
26+| Whymark: Review Branch Changes | merge base of selected ref and HEAD → HEAD (committed branch changes) |
27+| Whymark: Review Commit | selected commit against its parent |
28+ 
29+New Git comparisons contain explanation stubs. They do not pretend to provide AI reasoning or verified accuracy. Empty comparisons are valid. Untracked files are included for unstaged/all-working comparisons by default; disable `whymark.includeUntracked` in Settings. Unsaved editor changes are excluded from comparisons.
30+ 
31+Use **Refresh comparison** after saving code, staging changes, or switching branches. **Save review as…** writes a new `.whymark` file (existing reviews are not overwritten). **Open review source** opens the raw format alongside the viewer so you can edit explanations normally; saved and unsaved review edits refresh the preview. For multi-root workspaces, choose a repository when prompted.
32+ 
33+## Interactive review
34+ 
35+- Unified/split diffs, syntax highlighting, multiple comments per line, compact disclosures and filters.
36+- Settings for synchronized horizontal scrolling and annotation layout.
37+- Keyboard navigation: `j` / `k` for comments, `u` for split/unified, `x` for rejection, `?` for help.
38+- Open a reviewed file in an adjacent editor using the file selector.
39+- Reject lines/parts or edit a hunk. **Apply changes updates source editor buffers and supports Undo; save those files to persist edits.** The Git index is never changed. Files must still match the review hash; dirty buffers, unsafe paths and stale reviews are refused.
40+- Existing execution evidence is displayed as imported and quality evidence as unchecked. The extension does not execute verification commands or scanners embedded in reviews.
41+ 
42+Restricted workspaces can display reviews. Git comparisons and source edits require workspace trust. Source paths are contained in the selected repository, including symlinks. Webview assets are bundled, external links permit HTTP(S) only, and no review data is uploaded.
43+ 
44+## Development
45+ 
46+`npm run build:vscode`, `npm run test:vscode`, and `npm run package:vscode` run from the repository root. The host and webview share the existing parser, diff/editor logic and React components. The Next.js application uses a separate server-action adapter.
47+ 
48+Design references: [custom editors](https://code.visualstudio.com/api/extension-guides/custom-editors), [webviews](https://code.visualstudio.com/api/extension-guides/webview), [workspace trust](https://code.visualstudio.com/api/extension-guides/workspace-trust).

extensions/vscode/package.json

+41 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,41 @@
1+{
2+ "name": "whymark",
3+ "displayName": "Whymark — Interactive Code Review",
4+ "description": "Review staged, unstaged and branch changes with line-linked reasoning and evidence, directly in VS Code.",
5+ "version": "0.1.0",
6+ "publisher": "spink-dev",
7+ "license": "MIT",
8+ "repository": { "type": "git", "url": "https://github.com/spink-dev/whymark" },
9+ "engines": { "vscode": "^1.96.0" },
10+ "categories": ["Other"],
11+ "main": "./dist/extension.cjs",
12+ "extensionKind": ["workspace"],
13+ "capabilities": {
14+ "untrustedWorkspaces": { "supported": "limited", "description": "Reviews can be read; Git comparisons and file edits require workspace trust." },
15+ "virtualWorkspaces": { "supported": false, "description": "Git comparisons require a local or remote filesystem checkout." }
16+ },
17+ "contributes": {
18+ "customEditors": [{ "viewType": "whymark.review", "displayName": "Whymark Review", "selector": [{ "filenamePattern": "*.whymark" }], "priority": "default" }],
19+ "languages": [{ "id": "whymark", "aliases": ["Whymark"], "extensions": [".whymark"] }],
20+ "commands": [
21+ { "command": "whymark.open", "title": "Whymark: Open Review" },
22+ { "command": "whymark.staged", "title": "Whymark: Review Staged Changes (HEAD → Index)" },
23+ { "command": "whymark.unstaged", "title": "Whymark: Review Unstaged Changes (Index → Working Tree)" },
24+ { "command": "whymark.worktree", "title": "Whymark: Review All Working Changes (HEAD → Working Tree)" },
25+ { "command": "whymark.branch", "title": "Whymark: Review Branch Changes (Merge Base → HEAD)" },
26+ { "command": "whymark.commit", "title": "Whymark: Review Commit" }
27+ ],
28+ "menus": {
29+ "explorer/context": [{ "command": "whymark.open", "when": "resourceExtname == .whymark", "group": "navigation" }],
30+ "scm/title": [
31+ { "command": "whymark.staged", "when": "scmProvider == git", "group": "whymark" },
32+ { "command": "whymark.unstaged", "when": "scmProvider == git", "group": "whymark" },
33+ { "command": "whymark.worktree", "when": "scmProvider == git", "group": "whymark" }
34+ ]
35+ },
36+ "configuration": { "title": "Whymark", "properties": {
37+ "whymark.includeUntracked": { "type": "boolean", "default": true, "description": "Include untracked files in unstaged and working-tree comparisons." }
38+ } }
39+ },
40+ "scripts": { "vscode:prepublish": "node scripts/build.mjs" }
41+}

extensions/vscode/scripts/build.mjs

+17 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,17 @@
1+import { build } from 'esbuild';
2+import postcss from 'postcss';
3+import tailwind from '@tailwindcss/postcss';
4+import { readFile, writeFile, mkdir, copyFile } from 'node:fs/promises';
5+import { fileURLToPath } from 'node:url';
6+import { resolve, dirname } from 'node:path';
7+const extension = resolve(dirname(fileURLToPath(import.meta.url)), '..');
8+const root = resolve(extension, '../..');
9+await mkdir(resolve(root, '.artifacts'), { recursive: true });
10+await mkdir(resolve(extension, 'dist'), { recursive: true });
11+const common = { absWorkingDir: root, bundle: true, logLevel: 'info', legalComments: 'eof', minify: true };
12+await build({ ...common, entryPoints: ['extensions/vscode/src/extension.ts', 'extensions/vscode/src/worker.ts'], outdir: resolve(extension, 'dist'), outExtension: { '.js': '.cjs' }, platform: 'node', target: 'node20', format: 'cjs', external: ['vscode'] });
13+await build({ ...common, entryPoints: ['extensions/vscode/src/webview.tsx'], outfile: resolve(extension, 'dist/webview.js'), platform: 'browser', target: 'chrome120', format: 'iife', define: { 'process.env.NODE_ENV': '"production"' } });
14+const cssPath = resolve(root, 'src/app/globals.css');
15+const result = await postcss([tailwind({ base: root })]).process(await readFile(cssPath, 'utf8'), { from: cssPath });
16+await writeFile(resolve(extension, 'dist/webview.css'), result.css + '\n' + await readFile(resolve(extension, 'src/theme.css'), 'utf8'));
17+await copyFile(resolve(root, 'LICENSE'), resolve(extension, 'LICENSE'));

extensions/vscode/scripts/test.mjs

+30 0read-only10%
testunspecifiedconfidence unknown
@@ -0,0 +1,30 @@
1+import { existsSync } from 'node:fs';
2+import { runTests } from '@vscode/test-electron';
3+import { build } from 'esbuild';
4+import { mkdtemp, writeFile, rm } from 'node:fs/promises';
5+import { tmpdir } from 'node:os';
6+import { resolve, join } from 'node:path';
7+import { execFileSync } from 'node:child_process';
8+let root = resolve('extensions/vscode');
9+const temp = await mkdtemp(join(tmpdir(), 'whymark-vscode-test-'));
10+const workspace = join(temp, 'workspace');
11+await import('node:fs/promises').then(fs => fs.mkdir(workspace));
12+const git = (...args) => execFileSync('git', args, { cwd: workspace });
13+git('init', '-q', '-b', 'main'); git('config', 'user.name', 'Extension Test'); git('config', 'user.email', 'test@example.com');
14+await writeFile(join(workspace, 'sample.ts'), 'export const value = "base";\n'); git('add', '.'); git('commit', '-qm', 'base');
15+await writeFile(join(workspace, 'sample.ts'), 'export const value = "staged";\n'); git('add', '.');
16+await writeFile(join(workspace, 'sample.ts'), 'export const value = "unstaged";\n');
17+await build({ entryPoints: ['extensions/vscode/src/test-host.ts'], outfile: join(temp, 'test-host.cjs'), bundle: true, platform: 'node', format: 'cjs', external: ['vscode'] });
18+try {
19+ if (process.env.WHYMARK_VSIX) {
20+ const cli = process.env.VSCODE_CLI_PATH || '/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code';
21+ execFileSync(cli, ['--user-data-dir', join(temp, 'user'), '--extensions-dir', join(temp, 'extensions'), '--install-extension', resolve(process.env.WHYMARK_VSIX)], { stdio: 'inherit' });
22+ root = join(temp, 'extensions', 'spink-dev.whymark-0.1.0');
23+ }
24+ await runTests({
25+ vscodeExecutablePath: process.env.VSCODE_EXECUTABLE_PATH || (existsSync('/Applications/Visual Studio Code.app/Contents/MacOS/Code') ? '/Applications/Visual Studio Code.app/Contents/MacOS/Code' : undefined),
26+ extensionDevelopmentPath: root, extensionTestsPath: join(temp, 'test-host.cjs'),
27+ launchArgs: [workspace, '--user-data-dir', join(temp, 'user'), '--extensions-dir', join(temp, 'extensions'), '--disable-workspace-trust', '--skip-welcome', '--skip-release-notes', '--disable-extensions'],
28+ extensionTestsEnv: { WHYMARK_TEST_ROOT: workspace },
29+ });
30+} finally { await rm(temp, { recursive: true, force: true }); }

extensions/vscode/src/extension.ts

+239 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,239 @@
1+import * as vscode from "vscode";
2+import { randomBytes } from "node:crypto";
3+import { readFile, realpath } from "node:fs/promises";
4+import { dirname, join, relative, isAbsolute } from "node:path";
5+import { Worker } from "node:worker_threads";
6+import { parseWhymark } from "../../../src/lib/whymark/parse";
7+import { validateDocument } from "../../../src/lib/whymark/validate";
8+import { buildReviewVM } from "../../../src/lib/view-model";
9+import { applyDecisions, isActionable, replaceRange } from "../../../src/lib/whymark/edit";
10+import type { WhymarkDocument, Scope } from "../../../src/lib/whymark/types";
11+import type { ReviewVM } from "../../../src/lib/view-model";
12+import { parseRequest } from "./protocol";
13+import { blobHash, safeFile, MAX_REVIEW_BYTES } from "./safety";
14+ 
15+const VIEW = "whymark.review";
16+interface Comparison { scope: Scope; cwd: string; base?: string; commit?: string; untracked: boolean }
17+interface Session { panel: vscode.WebviewPanel; text: () => string; root?: string; label: string; comparison?: Comparison; document?: vscode.TextDocument; revision: number; doc?: WhymarkDocument; vm?: ReviewVM; disposed: boolean; busy: boolean }
18+const sessions = new Set<Session>();
19+let extensionRoot: string;
20+ 
21+function requireTrust() { if (!vscode.workspace.isTrusted) throw new Error("Trust this workspace before comparing Git changes or editing source files."); }
22+function report(error: unknown) { void vscode.window.showErrorMessage(`Whymark: ${error instanceof Error ? error.message : String(error)}`); }
23+ 
24+export async function generateComparison(request: Comparison): Promise<{ text: string; root: string }> {
25+ requireTrust();
26+ return vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: "Whymark: Reading Git changes", cancellable: true }, (_, token) => new Promise((resolve, reject) => {
27+ const worker = new Worker(join(extensionRoot, "dist/worker.cjs"), { workerData: request });
28+ const finish = (error?: Error, result?: { text: string; root: string }) => {
29+ clearTimeout(timer); cancel.dispose(); void worker.terminate();
30+ if (error) reject(error); else resolve(result!);
31+ };
32+ const timer = setTimeout(() => finish(new Error("Git comparison exceeded 30 seconds.")), 30000);
33+ const cancel = token.onCancellationRequested(() => finish(new Error("Comparison canceled.")));
34+ worker.once("error", finish);
35+ worker.once("message", result => result.error ? finish(new Error(result.error)) : Buffer.byteLength(result.text) > MAX_REVIEW_BYTES ? finish(new Error("Comparison exceeds the 8 MB review limit; narrow the changes first.")) : finish(undefined, result));
36+ }));
37+}
38+ 
39+async function repositoryFor(uri: vscode.Uri): Promise<string | undefined> {
40+ const folder = vscode.workspace.getWorkspaceFolder(uri);
41+ if (!folder || uri.scheme !== "file") return undefined;
42+ // Find the nearest Git marker without invoking Git in restricted mode.
43+ const boundary = await realpath(folder.uri.fsPath);
44+ let current = await realpath(dirname(uri.fsPath));
45+ const rel = relative(boundary, current);
46+ if (rel.startsWith("..") || isAbsolute(rel)) return undefined;
47+ while (true) {
48+ try { await vscode.workspace.fs.stat(vscode.Uri.file(join(current, ".git"))); return current; } catch { /* continue to parent */ }
49+ if (current === boundary) return boundary;
50+ current = dirname(current);
51+ }
52+}
53+ 
54+async function openDocumentAt(path: string) {
55+ const matches: vscode.TextDocument[] = [];
56+ for (const document of vscode.workspace.textDocuments) {
57+ if (document.uri.scheme !== "file") continue;
58+ try { if (await realpath(document.uri.fsPath) === path) matches.push(document); } catch { /* deleted document */ }
59+ }
60+ if (matches.length > 1) throw new Error("Source is open through multiple paths. Close duplicate editors before editing.");
61+ return matches[0];
62+}
63+ 
64+async function render(session: Session) {
65+ const revision = ++session.revision;
66+ try {
67+ const text = session.text();
68+ if (Buffer.byteLength(text) > MAX_REVIEW_BYTES) throw new Error("Review exceeds the 8 MB limit.");
69+ const doc = parseWhymark(text);
70+ if (doc.files.length > 1000) throw new Error("Review exceeds 1,000 files.");
71+ const valid = validateDocument(doc);
72+ const writable = new Set<string>();
73+ if (session.root && vscode.workspace.isTrusted && valid.ok) {
74+ for (const file of doc.files) {
75+ try {
76+ const path = await safeFile(session.root, file.path);
77+ const bytes = await readFile(path);
78+ const text = bytes.toString("utf8");
79+ if (!Buffer.from(text).equals(bytes)) continue;
80+ const open = await openDocumentAt(path);
81+ if (open?.isDirty || open && open.getText() !== text) continue;
82+ if (isActionable(file, blobHash(text))) writable.add(file.path);
83+ } catch { /* Missing, escaped and oversized paths remain read-only. */ }
84+ }
85+ }
86+ const vm = await buildReviewVM(doc, { isWritable: file => writable.has(file.path) });
87+ if (revision !== session.revision || session.disposed) return;
88+ vm.diagnostics = valid.diagnostics;
89+ session.doc = doc; session.vm = vm;
90+ await session.panel.webview.postMessage({ type: "review", review: vm, revision, trusted: vscode.workspace.isTrusted, label: session.label, generated: Boolean(session.comparison) });
91+ } catch (error) {
92+ if (revision === session.revision) await session.panel.webview.postMessage({ type: "error", message: String(error) });
93+ }
94+}
95+ 
96+export async function applyRequest(session: Session, input: unknown) {
97+ const request = parseRequest(input);
98+ if (!request || request.type !== "apply" && request.type !== "edit") throw new Error("Invalid edit request.");
99+ requireTrust();
100+ if (!session.root || !session.doc || request.revision !== session.revision) throw new Error("The review changed. Refresh before editing.");
101+ if (!validateDocument(session.doc).ok) throw new Error("Fix invalid review data before editing.");
102+ const file = session.doc.files.find(f => f.path === request.path);
103+ if (!file) throw new Error("File is not part of this review.");
104+ const path = await safeFile(session.root, file.path);
105+ const disk = await readFile(path);
106+ const text = disk.toString("utf8");
107+ if (!Buffer.from(text).equals(disk) || !isActionable(file, blobHash(text))) throw new Error("File no longer matches this review. Refresh or regenerate it first.");
108+ const document = await openDocumentAt(path) ?? await vscode.workspace.openTextDocument(vscode.Uri.file(path));
109+ if (document.isDirty || document.getText() !== text) throw new Error("Save or revert the source file's unsaved changes before editing from Whymark.");
110+ const newLines = new Set(file.hunks.flatMap(h => h.lines.filter(l => l.type !== "del").map(l => l.newLine)));
111+ if (request.type === "apply") {
112+ const adds = new Set(file.hunks.flatMap(h => h.lines.filter(l => l.type === "add").map(l => l.newLine)));
113+ const dels = new Set(file.hunks.flatMap(h => h.lines.filter(l => l.type === "del").map(l => l.oldLine)));
114+ if (request.decisions.discardAdded.some(n => !adds.has(n)) || request.decisions.restoreDeleted.some(n => !dels.has(n))) throw new Error("Decisions must address changed lines in this review.");
115+ } else {
116+ if (request.end - request.start > 100000) throw new Error("Edit range is too large.");
117+ for (let line = request.start; line <= request.end; line++) if (!newLines.has(line)) throw new Error("Edit must stay inside a reviewed hunk.");
118+ }
119+ const result = request.type === "apply" ? applyDecisions(text, file.hunks, request.decisions) : { text: replaceRange(text, request.start, request.end, request.text) };
120+ // No await between the final document check and submitting the edit. VS Code's
121+ // versioned WorkspaceEdit rejects concurrent document changes and supports undo.
122+ if (request.revision !== session.revision || document.getText() !== text || document.isDirty) throw new Error("Source or review changed during editing.");
123+ const edit = new vscode.WorkspaceEdit();
124+ edit.replace(document.uri, new vscode.Range(document.positionAt(0), document.positionAt(text.length)), result.text);
125+ if (!await vscode.workspace.applyEdit(edit)) throw new Error("VS Code rejected the edit because the document changed.");
126+ void vscode.window.showTextDocument(document, { viewColumn: vscode.ViewColumn.Beside, preserveFocus: true });
127+ return { ok: true, ...("discarded" in result ? { discarded: result.discarded, restored: result.restored, partsReverted: result.partsReverted } : {}) };
128+}
129+ 
130+export async function saveReview(text: string, target: vscode.Uri) {
131+ const edit = new vscode.WorkspaceEdit();
132+ edit.createFile(target, { overwrite: false });
133+ edit.insert(target, new vscode.Position(0, 0), text);
134+ if (!await vscode.workspace.applyEdit(edit)) throw new Error("Choose a new filename; existing reviews are not overwritten.");
135+ const document = await vscode.workspace.openTextDocument(target);
136+ if (!await document.save()) throw new Error("Review remains unsaved. Save it from the editor.");
137+ return document;
138+}
139+ 
140+function attach(session: Session, context: vscode.ExtensionContext) {
141+ sessions.add(session);
142+ const webview = session.panel.webview;
143+ const media = vscode.Uri.joinPath(context.extensionUri, "dist");
144+ webview.options = { enableScripts: true, localResourceRoots: [media] };
145+ const nonce = randomBytes(24).toString("base64");
146+ webview.html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}'; img-src ${webview.cspSource} data:; font-src ${webview.cspSource};"><link rel="stylesheet" href="${webview.asWebviewUri(vscode.Uri.joinPath(media, "webview.css"))}"></head><body><div id="root">Loading Whymark…</div><script nonce="${nonce}" src="${webview.asWebviewUri(vscode.Uri.joinPath(media, "webview.js"))}"></script></body></html>`;
147+ const subscriptions: vscode.Disposable[] = [];
148+ subscriptions.push(webview.onDidReceiveMessage(async input => {
149+ const m = parseRequest(input);
150+ if (!m) return;
151+ try {
152+ if (m.type === "ready") { await render(session); return; }
153+ if (m.type === "external") { await vscode.env.openExternal(vscode.Uri.parse(m.url)); return; }
154+ if (m.type === "source") {
155+ if (session.document) await vscode.commands.executeCommand("vscode.openWith", session.document.uri, "default", vscode.ViewColumn.Beside);
156+ else await vscode.window.showTextDocument(await vscode.workspace.openTextDocument({ language: "whymark", content: session.text() }), vscode.ViewColumn.Beside);
157+ return;
158+ }
159+ if (m.type === "open") {
160+ if (!session.root || !session.doc?.files.some(f => f.path === m.path)) throw new Error("File is not in this review's repository.");
161+ const path = await safeFile(session.root, m.path);
162+ const position = new vscode.Position((m.line ?? 1) - 1, 0);
163+ await vscode.window.showTextDocument(vscode.Uri.file(path), { viewColumn: vscode.ViewColumn.Beside, selection: new vscode.Range(position, position) }); return;
164+ }
165+ if (m.type === "save") {
166+ const target = await vscode.window.showSaveDialog({ defaultUri: session.root ? vscode.Uri.file(join(session.root, "review.whymark")) : undefined, filters: { Whymark: ["whymark"] } });
167+ if (target) {
168+ await saveReview(session.text(), target);
169+ await vscode.commands.executeCommand("vscode.openWith", target, VIEW);
170+ }
171+ return;
172+ }
173+ if (session.busy) throw new Error("Another operation is still running.");
174+ session.busy = true;
175+ try {
176+ if (m.type === "refresh") {
177+ if (session.comparison) {
178+ const result = await generateComparison(session.comparison);
179+ session.text = () => result.text; session.root = result.root;
180+ }
181+ await render(session);
182+ } else if (m.type === "apply" || m.type === "edit") {
183+ const result = await applyRequest(session, m);
184+ await webview.postMessage({ type: "result", id: m.id, result });
185+ // Keep UI selections until the next explicit refresh; every edit still
186+ // revalidates the actual source and refuses a stale second write.
187+ }
188+ } finally { session.busy = false; }
189+ } catch (error) {
190+ if ("id" in m) await webview.postMessage({ type: "result", id: m.id, result: { ok: false, error: String(error) } });
191+ else { await webview.postMessage({ type: "error", message: String(error) }); report(error); }
192+ }
193+ }));
194+ subscriptions.push(vscode.workspace.onDidChangeTextDocument(e => { if (e.document === session.document) void render(session); }));
195+ subscriptions.push(vscode.workspace.onDidGrantWorkspaceTrust(() => { void render(session); }));
196+ session.panel.onDidDispose(() => { session.disposed = true; sessions.delete(session); subscriptions.forEach(d => d.dispose()); });
197+}
198+ 
199+async function chooseFolder(resource?: vscode.Uri | { rootUri?: vscode.Uri }): Promise<vscode.WorkspaceFolder | undefined> {
200+ const uri = resource instanceof vscode.Uri ? resource : resource?.rootUri;
201+ if (uri) { const folder = vscode.workspace.getWorkspaceFolder(uri); if (folder) return folder; }
202+ const folders = vscode.workspace.workspaceFolders;
203+ if (!folders?.length) throw new Error("Open a Git workspace folder first.");
204+ return folders.length === 1 ? folders[0] : vscode.window.showWorkspaceFolderPick();
205+}
206+ 
207+export function activate(context: vscode.ExtensionContext) {
208+ extensionRoot = context.extensionPath;
209+ context.subscriptions.push(vscode.window.registerCustomEditorProvider(VIEW, {
210+ async resolveCustomTextEditor(document, panel) {
211+ attach({ document, panel, text: () => document.getText(), root: await repositoryFor(document.uri), label: vscode.workspace.asRelativePath(document.uri), revision: 0, disposed: false, busy: false }, context);
212+ },
213+ }, { webviewOptions: { retainContextWhenHidden: true }, supportsMultipleEditorsPerDocument: true }));
214+ context.subscriptions.push(vscode.commands.registerCommand("whymark.open", async (uri?: vscode.Uri) => {
215+ try {
216+ uri ??= vscode.window.activeTextEditor?.document.uri.path.endsWith(".whymark") ? vscode.window.activeTextEditor.document.uri : (await vscode.window.showOpenDialog({ filters: { Whymark: ["whymark"] }, canSelectMany: false }))?.[0];
217+ if (uri) await vscode.commands.executeCommand("vscode.openWith", uri, VIEW);
218+ } catch (error) { report(error); }
219+ }));
220+ for (const scope of ["staged", "unstaged", "worktree", "branch", "commit"] as const) {
221+ context.subscriptions.push(vscode.commands.registerCommand(`whymark.${scope}`, async (resource?: vscode.Uri | { rootUri?: vscode.Uri }) => {
222+ try {
223+ requireTrust();
224+ const folder = await chooseFolder(resource); if (!folder) return;
225+ if (folder.uri.scheme !== "file") throw new Error("This extension requires a filesystem workspace.");
226+ const ref = scope === "branch" || scope === "commit" ? await vscode.window.showInputBox({ prompt: scope === "branch" ? "Base branch or revision (merge base with HEAD)" : "Commit to review", value: scope === "branch" ? "main" : "HEAD", validateInput: value => !value.trim() || value.startsWith("-") || /[\s\x00-\x1f]/.test(value) ? "Enter a Git ref without whitespace or leading hyphens." : undefined }) : undefined;
227+ if ((scope === "branch" || scope === "commit") && ref === undefined) return;
228+ const resourceUri = resource instanceof vscode.Uri ? resource : resource?.rootUri;
229+ const comparison: Comparison = { scope, cwd: resourceUri?.scheme === "file" ? resourceUri.fsPath : folder.uri.fsPath, base: scope === "branch" ? ref : undefined, commit: scope === "commit" ? ref : undefined, untracked: vscode.workspace.getConfiguration("whymark", folder.uri).get("includeUntracked", true) };
230+ if (vscode.workspace.textDocuments.some(d => d.isDirty && vscode.workspace.getWorkspaceFolder(d.uri)?.uri.toString() === folder.uri.toString())) void vscode.window.showInformationMessage("Whymark compares saved files on disk. Unsaved editor changes are not included.");
231+ const result = await generateComparison(comparison);
232+ const panel = vscode.window.createWebviewPanel(VIEW, `Whymark: ${scope}`, vscode.ViewColumn.Active, { retainContextWhenHidden: true });
233+ attach({ panel, text: () => result.text, root: result.root, label: `${scope} comparison · ${folder.name}`, comparison, revision: 0, disposed: false, busy: false }, context);
234+ } catch (error) { report(error); }
235+ }));
236+ }
237+ return { sessions, applyRequest, saveReview };
238+}
239+export function deactivate() { for (const session of sessions) session.panel.dispose(); }

extensions/vscode/src/protocol.ts

+26 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,26 @@
1+import type { FileDecisions } from "../../../src/lib/whymark/edit";
2+import type { ReviewVM } from "../../../src/lib/view-model";
3+export type Request =
4+ | { type: "ready" | "refresh" | "source" | "save" }
5+ | { type: "open"; path: string; line?: number }
6+ | { type: "external"; url: string }
7+ | { type: "apply"; id: number; revision: number; path: string; decisions: FileDecisions }
8+ | { type: "edit"; id: number; revision: number; path: string; start: number; end: number; text: string };
9+export interface ReviewMessage { type: "review"; review: ReviewVM; revision: number; trusted: boolean; label: string; generated: boolean }
10+export function parseRequest(input: unknown): Request | null {
11+ if (!input || typeof input !== "object") return null;
12+ const m = input as Record<string, unknown>;
13+ if (["ready", "refresh", "source", "save"].includes(String(m.type))) return { type: m.type } as Request;
14+ if (m.type === "external" && typeof m.url === "string" && m.url.length < 8192 && /^https?:\/\//.test(m.url)) return m as Request;
15+ if (typeof m.path !== "string" || m.path.length > 4096) return null;
16+ const positive = (n: unknown) => Number.isSafeInteger(n) && (n as number) > 0;
17+ if (m.type === "open" && (m.line === undefined || positive(m.line))) return m as Request;
18+ if (!positive(m.id) || !positive(m.revision)) return null;
19+ if (m.type === "edit" && positive(m.start) && positive(m.end) && (m.end as number) >= (m.start as number) && typeof m.text === "string" && m.text.length <= 2_000_000) return m as Request;
20+ if (m.type === "apply" && m.decisions && typeof m.decisions === "object") {
21+ const d = m.decisions as Record<string, unknown>;
22+ const lines = (x: unknown) => Array.isArray(x) && x.length <= 100000 && x.every(positive);
23+ if (lines(d.discardAdded) && lines(d.restoreDeleted) && (d.revertParts === undefined || Array.isArray(d.revertParts) && d.revertParts.length <= 100000 && d.revertParts.every(p => p && positive(p.line) && Number.isSafeInteger(p.part) && p.part >= 0))) return m as Request;
24+ }
25+ return null;
26+}

extensions/vscode/src/safety.ts

+17 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,17 @@
1+import { createHash } from "node:crypto";
2+import { realpath, stat } from "node:fs/promises";
3+import { isAbsolute, relative, resolve, sep } from "node:path";
4+export const MAX_REVIEW_BYTES = 8 * 1024 * 1024;
5+export function blobHash(text: string): string {
6+ return createHash("sha1").update(`blob ${Buffer.byteLength(text)}\0`).update(text).digest("hex");
7+}
8+export async function safeFile(root: string, path: string): Promise<string> {
9+ if (!path || isAbsolute(path) || /^[A-Za-z]:/.test(path) || path.includes("\\") || path.split("/").some(p => p === ".." || p === ".git") || path.includes("\0")) throw new Error("Path must stay inside the repository.");
10+ const base = await realpath(root);
11+ const target = await realpath(resolve(base, path));
12+ const rel = relative(base, target);
13+ if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) throw new Error("Path escapes the repository.");
14+ const info = await stat(target);
15+ if (!info.isFile() || info.size > MAX_REVIEW_BYTES) throw new Error("Only files up to 8 MB can be opened or edited.");
16+ return target;
17+}

extensions/vscode/src/test-host.ts

+70 0read-only10%
testunspecifiedconfidence unknown
@@ -0,0 +1,70 @@
1+import * as vscode from "vscode";
2+import * as assert from "node:assert/strict";
3+import { readFileSync, writeFileSync } from "node:fs";
4+import { join } from "node:path";
5+import { execFileSync } from "node:child_process";
6+import type { activate } from "./extension";
7+const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
8+export async function run() {
9+ const root = process.env.WHYMARK_TEST_ROOT!;
10+ const extension = vscode.extensions.getExtension<ReturnType<typeof activate>>("spink-dev.whymark");
11+ assert.ok(extension, "extension discovered");
12+ const api = await extension.activate();
13+ const latest = async () => {
14+ for (let i = 0; i < 100; i++) {
15+ const session = [...api.sessions].at(-1);
16+ if (session?.vm) return session;
17+ await delay(100);
18+ }
19+ throw new Error("Real webview did not load or send ready within 10 seconds.");
20+ };
21+ await vscode.commands.executeCommand("whymark.staged");
22+ const staged = await latest();
23+ assert.ok(staged.vm!.files[0].rows.some(r => r.kind === "add" && r.text.includes('"staged"')));
24+ assert.equal(staged.vm!.files[0].actionable, false, "index differs from disk; editing unavailable");
25+ await vscode.commands.executeCommand("whymark.unstaged");
26+ const unstaged = await latest();
27+ assert.notEqual(staged, unstaged);
28+ assert.ok(unstaged.vm!.files[0].rows.some(r => r.kind === "del" && r.text.includes('"staged"')));
29+ assert.ok(unstaged.vm!.files[0].rows.some(r => r.kind === "add" && r.text.includes('"unstaged"')));
30+ assert.equal(unstaged.vm!.files[0].actionable, true);
31+ const request = { type: "edit", id: 1, revision: unstaged.revision, path: "sample.ts", start: 1, end: 1, text: 'export const value = "edited";' };
32+ await assert.rejects(api.applyRequest(unstaged, { ...request, revision: 999 }), /review changed/);
33+ await assert.rejects(api.applyRequest(unstaged, { ...request, path: "../outside" }), /not part/);
34+ writeFileSync(join(root, "sample.ts"), "changed on disk\n");
35+ await assert.rejects(api.applyRequest(unstaged, request), /no longer matches/);
36+ writeFileSync(join(root, "sample.ts"), 'export const value = "unstaged";\n');
37+ const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(join(root, "sample.ts")));
38+ const result = await api.applyRequest(unstaged, request);
39+ assert.equal(result.ok, true);
40+ assert.ok(doc.isDirty); assert.ok(doc.getText().includes('"edited"'));
41+ assert.ok(readFileSync(join(root, "sample.ts"), "utf8").includes('"unstaged"'), "disk untouched until save");
42+ assert.ok(execFileSync("git", ["show", ":sample.ts"], { cwd: root, encoding: "utf8" }).includes('"staged"'), "index untouched");
43+ await assert.rejects(api.applyRequest(unstaged, request), /unsaved/);
44+ await vscode.window.showTextDocument(doc);
45+ await vscode.commands.executeCommand("undo");
46+ assert.ok(doc.getText().includes('"unstaged"'), "native editor undo restores source");
47+ await assert.rejects(api.applyRequest(unstaged, { type: "apply", id: 2, revision: unstaged.revision, path: "sample.ts", decisions: { discardAdded: [99], restoreDeleted: [] } }), /changed lines/);
48+ await api.applyRequest(unstaged, { type: "apply", id: 3, revision: unstaged.revision, path: "sample.ts", decisions: { discardAdded: [1], restoreDeleted: [1] } });
49+ assert.ok(doc.getText().includes('"staged"'), "reject replaces added line with removed line");
50+ await vscode.window.showTextDocument(doc); await vscode.commands.executeCommand("undo");
51+ assert.ok(doc.getText().includes('"unstaged"'));
52+ const text = unstaged.text();
53+ const savedUri = vscode.Uri.file(join(root, "sample.whymark"));
54+ await api.saveReview(text, savedUri);
55+ assert.equal(readFileSync(savedUri.fsPath, "utf8"), text, "save creates exact review bytes");
56+ await assert.rejects(api.saveReview("overwrite", savedUri), /existing reviews/);
57+ assert.equal(readFileSync(savedUri.fsPath, "utf8"), text, "existing review is preserved");
58+ await vscode.commands.executeCommand("vscode.openWith", vscode.Uri.file(join(root, "sample.whymark")), "whymark.review");
59+ const saved = await latest();
60+ assert.equal(saved.vm!.files[0].path, "sample.ts");
61+ assert.ok(saved.document);
62+ const oldRevision = saved.revision;
63+ const edit = new vscode.WorkspaceEdit(); edit.insert(saved.document.uri, new vscode.Position(0, 0), "\n");
64+ await vscode.workspace.applyEdit(edit);
65+ for (let i = 0; i < 50 && saved.revision === oldRevision; i++) await delay(100);
66+ assert.ok(saved.revision > oldRevision, "raw editor changes refresh custom editor");
67+ console.log("WHYMARK: real extension host checks passed (activation, webview ready, staged/unstaged, stale and dirty guards, undo, index safety, custom editor refresh)");
68+ await vscode.workspace.saveAll();
69+ for (const session of api.sessions) session.panel.dispose();
70+}

extensions/vscode/src/theme.css

+16 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,16 @@
1+:root, .dark {
2+ --background: var(--vscode-editor-background, #181a20);
3+ --foreground: var(--vscode-editor-foreground, #eee);
4+ --card: var(--vscode-sideBar-background, #202228);
5+ --card-foreground: var(--foreground);
6+ --popover: var(--vscode-editorWidget-background, #24262b);
7+ --popover-foreground: var(--foreground);
8+ --muted-foreground: var(--vscode-descriptionForeground, #aaa);
9+ --border: var(--vscode-panel-border, #444);
10+ --font-sans: var(--vscode-font-family, sans-serif);
11+ --font-geist-mono: var(--vscode-editor-font-family, monospace);
12+}
13+body { padding: 0; }
14+.extension-toolbar { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 16px; border-bottom: 1px solid var(--border); }
15+.extension-toolbar button, .extension-toolbar select { border: 1px solid var(--border); border-radius: 4px; padding: 3px 8px; background: var(--card); color: var(--foreground); }
16+.extension-notice { padding: 8px 16px; font-size: 12px; color: var(--muted-foreground); }

extensions/vscode/src/webview.tsx

+58 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,58 @@
1+import React, { useEffect, useState } from "react";
2+import { createRoot } from "react-dom/client";
3+import { ReviewView } from "../../../src/components/whymark/review-view";
4+import type { ReviewActions, EditResult } from "../../../src/lib/review-actions";
5+import type { ReviewMessage, Request } from "./protocol";
6+declare function acquireVsCodeApi(): { postMessage(message: Request): void; getState(): unknown; setState(state: unknown): void };
7+const api = acquireVsCodeApi();
8+let nextId = 0;
9+const pending = new Map<number, { resolve: (result: EditResult) => void; timer: ReturnType<typeof setTimeout> }>();
10+function call(message: Omit<Extract<Request, { type: "apply" }>, "id"> | Omit<Extract<Request, { type: "edit" }>, "id">): Promise<EditResult> {
11+ return new Promise(resolve => {
12+ const id = ++nextId;
13+ const timer = setTimeout(() => { pending.delete(id); resolve({ ok: false, error: "VS Code did not respond. Check the source file before retrying." }); }, 30000);
14+ pending.set(id, { resolve, timer }); api.postMessage({ ...message, id });
15+ });
16+}
17+function App() {
18+ const [state, setState] = useState<ReviewMessage>();
19+ const [error, setError] = useState("");
20+ useEffect(() => {
21+ const listener = (event: MessageEvent) => {
22+ const m = event.data;
23+ if (m.type === "review") { setState(m); setError(""); }
24+ else if (m.type === "error") setError(m.message);
25+ else if (m.type === "result") {
26+ const request = pending.get(m.id);
27+ if (request) { clearTimeout(request.timer); request.resolve(m.result); pending.delete(m.id); }
28+ }
29+ };
30+ const link = (event: MouseEvent) => {
31+ const anchor = (event.target as Element).closest("a");
32+ const href = anchor?.getAttribute("href");
33+ if (href && !href.startsWith("#")) { event.preventDefault(); if (/^https?:\/\//.test(href)) api.postMessage({ type: "external", url: href }); }
34+ };
35+ window.addEventListener("message", listener); document.addEventListener("click", link);
36+ api.postMessage({ type: "ready" });
37+ return () => { window.removeEventListener("message", listener); document.removeEventListener("click", link); };
38+ }, []);
39+ const actions: ReviewActions | undefined = state?.trusted ? {
40+ applyFileDecisions: (_, path, decisions) => call({ type: "apply", revision: state.revision, path, decisions }),
41+ saveEditedRange: (_, path, start, end, text) => call({ type: "edit", revision: state.revision, path, start, end, text }),
42+ } : undefined;
43+ return <>
44+ <div className="extension-toolbar">
45+ <button onClick={() => api.postMessage({ type: "refresh" })}>Refresh comparison</button>
46+ <button onClick={() => api.postMessage({ type: "source" })}>Open review source</button>
47+ {state?.generated && <button onClick={() => api.postMessage({ type: "save" })}>Save review as…</button>}
48+ <select aria-label="Open source file" value="" onChange={e => { if (e.target.value) api.postMessage({ type: "open", path: e.target.value }); }}>
49+ <option value="">Open source file…</option>
50+ {state?.review.files.map(f => <option key={f.path} value={f.path}>{f.path}</option>)}
51+ </select>
52+ </div>
53+ <div className="extension-notice">{state?.trusted ? "Edits affect the working-tree editor buffer and support Undo. Save the source file to write it to disk. Git index is unchanged." : "Read-only review. Trust the workspace to compare Git changes or edit source files."} Evidence from review files is shown as imported, not independently re-run.</div>
54+ {error && <div role="alert" className="extension-notice">{error}</div>}
55+ {state ? <ReviewView key={state.revision} review={state.review} issues={state.review.diagnostics} slug="vscode" mutationTarget="Editor buffer" actions={actions} sourceLabel={state.label} onViewSource={() => api.postMessage({ type: "source" })} onBack={() => api.postMessage({ type: "source" })} /> : <p className="extension-notice">Loading review…</p>}
56+ </>;
57+}
58+createRoot(document.getElementById("root")!).render(<App />);

extensions/vscode/src/worker.ts

+9 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,9 @@
1+import { parentPort, workerData } from "node:worker_threads";
2+import { buildSkeleton, repoRoot } from "../../../src/lib/whymark/git";
3+import { serializeWhymark } from "../../../src/lib/whymark/serialize";
4+try {
5+ const root = repoRoot(workerData.cwd);
6+ if (!root) throw new Error("Choose a folder inside a Git repository.");
7+ const { doc } = buildSkeleton({ ...workerData, cwd: root, author: "Unreviewed Git comparison" });
8+ parentPort?.postMessage({ text: serializeWhymark(doc), root });
9+} catch (error) { parentPort?.postMessage({ error: String(error) }); }

specs/002-vscode-extension/PLAN.md

+43 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,43 @@
1+# PLAN: Interactive Whymark in VS Code
2+ 
3+## Goal
4+Open `.whymark` files and live Git comparisons in VS Code using the existing review UI. Compare staged against HEAD, unstaged against the index, worktree against HEAD, branch merge-base against HEAD, or a commit against its parent. Preserve explanations as explicit stubs for new comparisons.
5+ 
6+## Out of scope
7+Marketplace publication, automatic command execution from annotations, staging/unstaging, cloud services, browser-only VS Code, and AI-generated claims.
8+ 
9+## Context the agent must read first
10+- `src/lib/whymark/git.ts`, `edit.ts`, `inline.ts`: comparison and mutation contracts.
11+- `src/components/whymark/review-view.tsx`: shared interactive surface.
12+- `src/lib/view-model.ts`: highlighting, annotation and evidence state.
13+- VS Code custom editor, webview and workspace trust documentation.
14+ 
15+## Stack decisions
16+Bundled TypeScript workspace extension and React webview. Custom text editor owns review documents; the extension host performs guarded operations. No Next.js server, CDN, or npm runtime installation. Git generation runs in a terminable worker. Source edits use VS Code WorkspaceEdit and remain undoable/unsaved.
17+ 
18+## Files to change
19+| File | Operation | Purpose |
20+| --- | --- | --- |
21+| `extensions/vscode/` | create | Manifest, host, message protocol, webview, build, packaging, docs and acceptance tests |
22+| `src/components/whymark/review-view.tsx` | modify | Inject platform operations instead of importing server actions |
23+| `src/components/whymark/web-review-view.tsx` | create | Preserve web server action adapter |
24+| `src/lib/review-actions.ts` | create | Shared platform operation types |
25+| `src/app/r/[slug]/page.tsx`, `actions.ts` | modify | Web adapter wiring |
26+| `package.json`, lockfile, ESLint config | modify | API types and official packaging/testing tools |
27+| `tests/vscode.test.ts` | create | Protocol, containment and real Git comparison cases |
28+| `README.md` | modify | Local VSIX installation and scope |
29+ 
30+## Sequence
31+1. Inspect baseline and APIs; add platform adapter.
32+2. Implement editor host, comparisons, refresh/save/source navigation and guarded editing.
33+3. Bundle assets and package VSIX.
34+4. Exercise real extension host and browser interactions; run existing checks sequentially.
35+ 
36+## Tests & verification
37+Protocol rejection, traversal/symlink refusal, stale and dirty-buffer edit protection, literal content rendering, trusted/untrusted behavior, staged/unstaged separation, save/reopen, refresh, actual VSIX contents. Run npm test/typecheck/lint and existing browser suite. Test installed VS Code on macOS; distinguish unavailable platforms from verified ones.
38+ 
39+## Risks & rollout
40+Review paths and webview messages are untrusted. Gate edits and Git on workspace trust; resolve paths within the selected repository; compare newSha to both disk and editor content before WorkspaceEdit. Do not execute annotation commands. Restrict webview resources and scripts with CSP. Existing web behavior retains server actions through adapter. Installation is a local VSIX; no publication or commits.
41+ 
42+## Open questions
43+None blocking. Initial extension targets desktop/remote filesystem workspaces; compare refresh is explicit and review source changes refresh automatically.

specs/002-vscode-extension/verification.md

+63 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,63 @@
1+# VS Code extension — delivery evidence
2+ 
3+Implemented against baseline `d5ba7ce`, initially clean working tree. Changes are
4+uncommitted; no push, Marketplace publication, or installation into the user's
5+normal VS Code profile was performed.
6+ 
7+## Acceptance disposition
8+ 
9+| ID | Requirement | Evidence | Status |
10+| --- | --- | --- | --- |
11+| VSC-01 | Interactive `.whymark` custom editor without a server | Real VS Code loads the bundled webview and sends its ready message; Chromium exercises the bundled controls under CSP | Verified |
12+| VSC-02 | Distinct staged, unstaged, worktree, branch and commit comparisons | Real Git fixture asserts HEAD/index/worktree separation; installed host exercises staged and unstaged commands; remaining scopes reuse existing Git collector coverage | Verified within listed coverage |
13+| VSC-03 | Hash-checked undoable source edits, index untouched | Installed host checks stale disk, stale review revision, invalid paths/lines, dirty buffers, native undo and unchanged disk/index | Verified |
14+| VSC-04 | Save/reopen and live raw-review changes | Installed host creates exact bytes, refuses an existing filename, reopens custom editor and observes refresh from raw TextDocument edits | Verified |
15+| VSC-05 | Safe webview and read-only mode | Literal HTML rendering, typed message rejection, symlink/traversal cases, restricted UI and keyboard checks; trust gates inspected in host | Verified for tested boundaries; real restricted-mode host not exercised |
16+| VSC-06 | Installable package and web compatibility | VSIX installed into isolated VS Code profile and exercised; existing 38 browser checks plus improvements suite pass | Verified |
17+ 
18+## Checks
19+ 
20+- `npm test` on Node 22.23.2: 105 tests / 14 files passed.
21+- `npm run typecheck`, `npm run lint`: passed.
22+- `npm run package:vscode`: bundled VSIX, about 1.8 MB compressed; host, worker,
23+ webview JavaScript/CSS, manifest, README and license included. No external runtime
24+ modules or development server are required.
25+- `WHYMARK_VSIX=.artifacts/whymark-vscode.vsix npm run test:vscode`: installed package
26+ passes real VS Code 1.138.0 extension-host checks on macOS.
27+- `node tests/e2e/vscode.mjs`: bundled React controls, apply messages, split mode,
28+ settings, source/save/refresh messages, literal content, restricted UI/keyboard,
29+ and no JavaScript errors. Its VS Code message bridge is simulated; the separate
30+ installed-host checks exercise actual WorkspaceEdit, custom editor and Git.
31+- `npm run test:ui`: 38 existing real-browser checks passed, zero console errors.
32+- `node tests/e2e/improvements.mjs`: all feature checks passed.
33+- `npm run build -- --webpack`: production web build passed.
34+- `git diff --check`: passed; browser suite restored `examples/retry.ts`.
35+ 
36+Final reruns and output identities are recorded in `reviews/vscode-extension.whymark`.
37+The `.artifacts/vscode/` directory contains the bundled-webview screenshot and source
38+fingerprint. Evidence represents separate source-driven and behavior-driven
39+self-review passes, not an independent-agent audit.
40+ 
41+## Corrections and limits
42+ 
43+The first baseline run under Node 24/npm 12 failed in the existing npm pack test;
44+rerunning under the project's declared Node 22 passed all 102 baseline tests.
45+The extension acceptance run caught macOS path aliases opening separate document
46+identities; canonical lookup now respects existing unsaved buffers and rejects
47+multiple aliases. The source review also closed the read-only keyboard decision
48+path, covered by the bundled-webview test. Initial test setup used an outdated
49+macOS executable filename and an unavailable cleanup command; both were corrected.
50+ 
51+Comparisons use saved filesystem content. Branch mode compares merge-base to HEAD;
52+it does not include uncommitted changes. Source edits change editor buffers and
53+must be saved by the user; they do not stage/unstage or modify the index. Imported
54+execution evidence is not independently re-run by this extension. Generated notes
55+remain explicit stubs. Review files are limited to 8 MB and 1,000 files; Git workers
56+are cancellable and limited to 30 seconds. Refreshing a generated comparison
57+regenerates its snapshot; edit a saved review's source to retain authored notes.
58+ 
59+Windows, Linux, remote hosts, VS Code 1.96 minimum-version runtime, real untrusted
60+workspace host, branch/commit input dialogs and Save As dialog interaction were
61+not exercised. API typing uses 1.96; installed-host testing uses 1.138. The save
62+operation behind the dialog is tested with real VS Code APIs. The desktop/remote
63+filesystem extension does not support browser-only virtual workspaces.

src/components/whymark/web-review-view.tsx

+8 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,8 @@
1+"use client";
2+import Link from "next/link";
3+import { ArrowLeft } from "lucide-react";
4+import { ReviewView, type ReviewViewProps } from "./review-view";
5+import { applyFileDecisions, saveEditedRange } from "@/app/r/[slug]/actions";
6+export function WebReviewView(props: ReviewViewProps) {
7+ return <ReviewView {...props} homeControl={<Link href="/" aria-label="Home" className="flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:text-foreground"><ArrowLeft className="size-4" /></Link>} actions={{ applyFileDecisions, saveEditedRange }} />;
8+}

src/lib/review-actions.ts

+13 0read-only10%
intentunspecifiedconfidence unknown
@@ -0,0 +1,13 @@
1+import type { FileDecisions } from "./whymark/edit";
2+export interface EditResult {
3+ ok: boolean;
4+ newSha?: string;
5+ discarded?: number;
6+ restored?: number;
7+ partsReverted?: number;
8+ error?: string;
9+}
10+export interface ReviewActions {
11+ applyFileDecisions(slug: string, path: string, decisions: FileDecisions): Promise<EditResult>;
12+ saveEditedRange(slug: string, path: string, start: number, end: number, text: string): Promise<EditResult>;
13+}

tests/e2e/vscode.mjs

+50 0read-only10%
testunspecifiedconfidence unknown
@@ -0,0 +1,50 @@
1+import assert from 'node:assert/strict';
2+import { createServer } from 'node:http';
3+import { readFileSync, mkdirSync } from 'node:fs';
4+import { execFileSync } from 'node:child_process';
5+import { chromium } from 'playwright';
6+const review = JSON.parse(execFileSync(process.execPath, ['--import', 'tsx', '--input-type=module', '-e', `import {readFileSync} from 'node:fs'; import {parseWhymark} from './src/lib/whymark/parse.ts'; import {buildReviewVM} from './src/lib/view-model.ts'; const doc=parseWhymark(readFileSync('tests/fixtures/multiple-notes.whymark','utf8')); doc.meta.title='<img src=x onerror=alert(1)> literal title'; console.log(JSON.stringify(await buildReviewVM(doc,{isWritable:()=>true})));`], { encoding: 'utf8' }));
7+const bridge = `window.messages=[]; window.acquireVsCodeApi=()=>({postMessage: m=>{window.messages.push(m); if(m.type==='ready'||m.type==='refresh') fetch('/review').then(r=>r.json()).then(review=>window.postMessage({type:'review',review,revision:window.reviewLoads=(window.reviewLoads||0)+1,trusted:true,label:'Test comparison '+window.reviewLoads,generated:true},'*')); if(m.id) window.postMessage({type:'result',id:m.id,result:{ok:true}},'*');},getState:()=>null,setState:()=>{}});`;
8+const server = createServer((req,res) => {
9+ if(req.url==='/review') { res.setHeader('Content-Type','application/json'); res.end(JSON.stringify(review)); }
10+ else if(req.url==='/bridge.js') { res.setHeader('Content-Type','text/javascript'); res.end(bridge); }
11+ else if(req.url==='/webview.js'||req.url==='/webview.css') { res.setHeader('Content-Type',req.url.endsWith('.css')?'text/css':'text/javascript'); res.end(readFileSync(`extensions/vscode/dist${req.url}`)); }
12+ else res.end(`<!doctype html><html><head><meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-test'; style-src 'self' 'unsafe-inline'; connect-src 'self';"><link rel="stylesheet" href="/webview.css"></head><body><div id="root"></div><script nonce="test" src="/bridge.js"></script><script nonce="test" src="/webview.js"></script></body></html>`);
13+});
14+await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));
15+const browser = await chromium.launch();
16+try {
17+ const page = await browser.newPage({viewport:{width:1600,height:1000}});
18+ const errors=[]; page.on('pageerror',e=>errors.push(e.message));
19+ await page.goto(`http://127.0.0.1:${server.address().port}`);
20+ await page.getByRole('heading',{name:'<img src=x onerror=alert(1)> literal title',exact:true}).waitFor();
21+ assert.equal(await page.locator('img').count(),0,'review HTML is literal text');
22+ await page.getByRole('button',{name:'Expand all',exact:true}).click();
23+ assert.equal(await page.locator('[aria-expanded="false"][aria-controls^="comment-body-"]').count(),0);
24+ await page.getByRole('button',{name:/split/i}).click();
25+ assert.ok(await page.locator('[data-whymark-side]').count()>=2);
26+ await page.getByRole('button',{name:'Review settings'}).click();
27+ await page.getByRole('dialog').waitFor();
28+ await page.keyboard.press('Escape');
29+ await page.getByTitle('Reject every change in this hunk').first().click();
30+ await page.getByRole('button',{name:'apply',exact:true}).click();
31+ await page.waitForFunction(()=>window.messages.some(m=>m.type==='apply'&&m.revision===1));
32+ await page.getByText('Editor buffer updated:',{exact:false}).waitFor();
33+ await page.getByLabel('Open source file').selectOption(review.files[0].path);
34+ await page.getByRole('button',{name:'Save review as…',exact:true}).click();
35+ await page.getByRole('button',{name:'Open review source',exact:true}).first().click();
36+ const messages=await page.evaluate(()=>window.messages);
37+ assert.ok(messages.some(m=>m.type==='open'&&m.path===review.files[0].path));
38+ assert.ok(messages.some(m=>m.type==='save')); assert.ok(messages.some(m=>m.type==='source'));
39+ await page.getByRole('button',{name:'Refresh comparison'}).click();
40+ await page.getByText('Test comparison 2',{exact:true}).waitFor();
41+ // A valid restricted-mode payload must keep the view while hiding edit controls.
42+ await page.evaluate(review=>window.postMessage({type:'review',review,revision:3,trusted:false,label:'Restricted',generated:false},'*'),review);
43+ await page.getByText('Read-only review.',{exact:false}).waitFor();
44+ await page.keyboard.press('j'); await page.keyboard.press('x');
45+ assert.equal(await page.getByRole('button',{name:'apply',exact:true}).count(),0);
46+ assert.deepEqual(errors,[]);
47+ mkdirSync('.artifacts/vscode',{recursive:true});
48+ await page.screenshot({path:'.artifacts/vscode/review.png',fullPage:true});
49+ console.log('PASS bundled webview: literal content, disclosure, split mode, settings, navigation/save/refresh messages, restricted mode, no JS errors');
50+} finally { await browser.close(); await new Promise(resolve=>server.close(resolve)); }

tests/vscode.test.ts

+48 0read-only10%
testunspecifiedconfidence unknown
@@ -0,0 +1,48 @@
1+import { describe, expect, it } from "vitest";
2+import { realpathSync, mkdtempSync, writeFileSync, mkdirSync, symlinkSync, rmSync } from "node:fs";
3+import { tmpdir } from "node:os";
4+import { join } from "node:path";
5+import { execFileSync } from "node:child_process";
6+import { parseRequest } from "../extensions/vscode/src/protocol";
7+import { blobHash, safeFile } from "../extensions/vscode/src/safety";
8+import { collectDiff } from "../src/lib/whymark/git";
9+ 
10+describe("VS Code boundary", () => {
11+ it("accepts typed operations and rejects malformed or executable messages", () => {
12+ expect(parseRequest({ type: "edit", id: 1, revision: 1, path: "a.ts", start: 1, end: 1, text: "new" })).not.toBeNull();
13+ for (const request of [
14+ { type: "external", url: "command:workbench.action.terminal.new" },
15+ { type: "apply", id: 1, revision: 1, path: "a", decisions: { discardAdded: [-1], restoreDeleted: [] } },
16+ { type: "edit", id: 1, revision: 1, path: "a", start: 2, end: 1, text: "" },
17+ { type: "execute", command: "rm" }, null,
18+ ]) expect(parseRequest(request)).toBeNull();
19+ });
20+ it("contains source paths including symlink escapes and excludes Git metadata", async () => {
21+ const root = mkdtempSync(join(tmpdir(), "whymark-path-"));
22+ const outside = mkdtempSync(join(tmpdir(), "whymark-outside-"));
23+ try {
24+ writeFileSync(join(root, "a.ts"), "ok"); writeFileSync(join(outside, "secret"), "no");
25+ mkdirSync(join(root, ".git")); writeFileSync(join(root, ".git/config"), "no");
26+ symlinkSync(outside, join(root, "escape"));
27+ expect(await safeFile(root, "a.ts")).toBe(realpathSync(join(root, "a.ts")));
28+ for (const path of ["../secret", "/etc/passwd", "escape/secret", ".git/config", "C:/secret", "..\\secret"]) await expect(safeFile(root, path)).rejects.toThrow();
29+ } finally { rmSync(root, { recursive: true }); rmSync(outside, { recursive: true }); }
30+ });
31+ it("keeps index, unstaged, all-working and branch comparisons distinct", () => {
32+ const cwd = mkdtempSync(join(tmpdir(), "whymark-git-vscode-"));
33+ const git = (...args: string[]) => execFileSync("git", args, { cwd, encoding: "utf8" }).trim();
34+ try {
35+ git("init", "-q", "-b", "main"); git("config", "user.name", "Test"); git("config", "user.email", "test@example.com");
36+ writeFileSync(join(cwd, "a.ts"), "base\n"); git("add", "."); git("commit", "-qm", "base");
37+ writeFileSync(join(cwd, "a.ts"), "staged\n"); git("add", "."); writeFileSync(join(cwd, "a.ts"), "unstaged\n");
38+ writeFileSync(join(cwd, "new.ts"), "untracked\n");
39+ const staged = collectDiff({ cwd, scope: "staged", untracked: true });
40+ expect(staged.files).toHaveLength(1); expect(staged.raw).toContain("+staged"); expect(staged.raw).not.toContain("+unstaged");
41+ const unstaged = collectDiff({ cwd, scope: "unstaged", untracked: true });
42+ expect(unstaged.files).toHaveLength(2); expect(unstaged.raw).toContain("-staged"); expect(unstaged.raw).toContain("+unstaged");
43+ expect(collectDiff({ cwd, scope: "worktree" }).raw).toContain("-base");
44+ expect(collectDiff({ cwd, scope: "branch", base: "main" }).files).toHaveLength(0);
45+ expect(blobHash("unstaged\n")).toBe(git("hash-object", "a.ts"));
46+ } finally { rmSync(cwd, { recursive: true }); }
47+ });
48+});