Repository: j178/leetgo (language: python)

Issue:
[Bug] wsl can't use `leetgo open`
### leetgo debug

Leetgo version info  :
```
dev
commit: HEAD
built at: unknown
goos: linux
goarch: amd64
module version: v1.3.10, checksum: h1:5eCgMs7qTP3NUNmo2cve0tjUnQJJMyYyVDXBwGi9fzs=
```
Global config dir    : /root/.config/leetgo
Global config file   : /root/.config/leetgo/config.yaml
Project root         : /root/leetcode
Working dir          : /root/leetcode
Project config file  : /root/leetcode/leetgo.yaml
Project configuration:
```yaml
# This is the leetgo project level config, global config is at /root/.config/leetgo/config.yaml
# For more details, please refer to https://github.com/j178/leetgo
language: zh
code:
  lang: python
leetcode:
  site: https://leetcode.cn
editor:
  use: custom
  command: "code"
  args: "{{.CodeFile}} {{.DescriptionFile}}"


```
Full configuration   :
```yaml
author: 
language: zh
code:
  lang: python
  filename_template: '{{ .Id | padWithZero 4 }}{{ if .SlugIsMeaningful }}.{{ .Slug }}{{ end }}'
  separate_description_file: true
  modifiers:
    - name: removeUselessComments
  go:
    out_dir: go
    modifiers:
      - name: removeUselessComments
      - name: changeReceiverName
      - name: addNamedReturn
      - name: addMod
  python3:
    out_dir: python
    blocks:
      - name: beforeMarker
        template: |2
          from bisect import bisect_left, bisect_right
          from collections import Counter, defaultdict, deque
          from copy import deepcopy
          from functools import cache, cmp_to_key, lru_cache, reduce
          from heapq import heapify, heappop, heappush, heappushpop, heapreplace
          from itertools import accumulate, chain, count, pairwise, zip_longest
          from math import ceil, comb, floor, gcd, inf, isqrt, log2, perm, sqrt
          from operator import xor
          from string import ascii_lowercase
          from typing import List, Optional
      - name: beforeCode
        template: |
          # from sortedcontainers import SortedList
    executable: python3
  cpp:
    out_dir: cpp
    cxx: g++
    cxxflags: -O2 -std=c++17
  rust:
    out_dir: rust
  java:
    out_dir: java
leetcode:
  site: https://leetcode.cn
  credentials:
    from: cookies
    browsers: []
contest:
  out_dir: contest
  filename_template: '{{ .ContestShortSlug }}/{{ .Id }}{{ if .SlugIsMeaningful }}.{{ .Slug }}{{ end }}'
  open_in_browser: true
editor:
  use: custom
  command: code
  args: '{{.CodeFile}} {{.DescriptionFile}}'
```

### Debug log

```shell
root@vthe ~/leetcode (main)# export DEBUG=1
root@vthe ~/leetcode (main)# lc open today
2023/11/16 16:01:49 DEBU request method=POST url=https://leetcode.cn/graphql
  body=
  │ {"operationName":"questionOfToday","query":"\n    query questionOfToday {\n        todayRecord {\n            question {\n                titleSlug\n            }\n        }\n    }","variables":{}}

2023/11/16 16:01:49 DEBU response url=https://leetcode.cn/graphql code=200
  headers=
  │ Connection: keep-alive\r
  │ Content-Length: 96\r
  │ Content-Type: application/json\r
 data="{\"data\":{\"todayRecord\":[{\"question\":{\"titleSlug\":\"longest-even-odd-subarray-with-threshold\"}}]}}"
2023/11/16 16:01:49 DEBU request method=POST url=https://leetcode.cn/graphql
  body=
  │ {"operationName":"questionData","query":"\n\tquery questionData($titleSlug: String!) {\n\t\tquestion(titleSlug: $titleSlug) {\n\t\t\tquestionId\n\t\t\tquestionFrontendId\n\t\t\tcategoryTitle\n\t\t\ttitle\n\t\t\ttitleSlug\n\t\t\tcontent\n\t\t\tisPaidOnly\n\t\t\ttranslatedTitle\n\t\t\ttranslatedContent\n\t\t\tdifficulty\n\t\t\tstatus\n\t\t\tstats\n\t\t\thints\n\t\t\tsimilarQuestions\n\t\t\tsampleTestCase\n\t\t\texampleTestcases\n\t\t\texampleTestcaseList\n\t\t\tjsonExampleTestcases\n\t\t\tmetaData\n\t\t\tcodeSnippets {\n\t\t\t\tlang\n\t\t\t\tlangSlug\n\t\t\t\tcode\n\t\t\t}\n\t\t\ttopicTags {\n\t\t\t\tname\n\t\t\t\tslug\n\t\t\t\ttranslatedName\n\t\t\t}\n\t\t\teditorType\n\t\t}\n\t}","variables":{"titleSlug":"longest-even-odd-subarray-with-threshold"}}

2023/11/16 16:01:49 DEBU response url=https://leetcode.cn/graphql code=200
  headers=
  │ Connection: keep-alive\r
  │ Content-Length: 10253\r
  │ Content-Type: application/json\r
 data="{\"data\":{\"question\":{\"questionId\":\"2866\",\"questionFrontendId\":\"2760\",\"categoryTitle\":\"Algorithms\",\"title\":\"Longest Even Odd Subarray With Threshold\",\"titleSlug\":\"longest-even-odd-subarray-with-threshold\",\"content\":\"<p>You are given a <strong>0-indexed</strong> integer array <code>nums</code> and an integer <code>threshold</code>.</p>\n\n<p>Find the length of the <strong>longest subarray</strong> of <code>nums</code> starting at index <code>l</code> and ending at index <code>r</code> <code>(0 &lt;= l &lt;= r &lt; nums.length)</code> that satisfies the following conditions:</p>\n\n<ul>\n\t<li><code>nums[l] % 2 == 0</code></li>\n\t<li>For all indices <code>i</code> in the range <code>[l, r - 1]</code>, <code>nums[i] % 2 != nums[i + 1] % 2</code></li>\n\t<li>For all indices <code>i</code> in the range <code>[l, r]</code>, <code>nums[i] &lt;= threshold</code></li>\n</ul>\n\n<p>Return <em>an integer denoting the length of the longest such subarray.</em></p>\n\n<p><strong>Note:</strong> A <strong>subarray</strong> is a contiguous non-empty sequence of elements within an array.</p>\n\n<p>&nbsp;</p>\n<p><strong class=\\"example\\">Example 1:</strong></p>\n\n<pre>\n<strong>Input:</strong> nums = [3,2,5,4], threshold = 5\n<strong>Output:</strong> 3\n<strong>Explanation:</strong> In this example, we can select the subarray that starts at l = 1 and ends at r = 3 =&gt; [2,5,4]. This subarray satisfies the conditions.\nHence, the answer is the length of the subarray, 3. We can show that 3 is the maximum possible achievable length.</pre>\n\n<p><strong class=\\"example\\">Example 2:</strong></p>\n\n<pre>\n<strong>Input:</strong> nums = [1,2], threshold = 2\n<strong>Output:</strong> 1\n<strong>Explanation:</strong> In this example, we can select the subarray that starts at l = 1 and ends at r = 1 =&gt; [2]. \nIt satisfies all the conditions and we can show that 1 is the maximum possible achievable length.\n</pre>\n\n<p><strong class=\\"example\\">Example 3:</strong></p>\n\n<pre>\n<strong>Input:</strong> nums = [2,3,4,5], threshold = 4\n<strong>Output:</strong> 3\n<strong>Explanation:</strong> In this example, we can select the subarray that starts at l = 0 and ends at r = 2 =&gt; [2,3,4]. \nIt satisfies all the conditions.\nHence, the answer is the length of the subarray, 3. We can show that 3 is the maximum possible achievable length.\n</pre>\n\n<p>&nbsp;</p>\n<p><strong>Constraints:</strong></p>\n\n<ul>\n\t<li><code>1 &lt;= nums.length &lt;= 100 </code></li>\n\t<li><code>1 &lt;= nums[i] &lt;= 100 </code></li>\n\t<li><code>1 &lt;= threshold &lt;= 100</code></li>\n</ul>\n\",\"isPaidOnly\":false,\"translatedTitle\":\"\u6700\u957f\u5947\u5076\u5b50\u6570\u7ec4\",\"translatedContent\":\"<p>\u7ed9\u4f60\u4e00\u4e2a\u4e0b\u6807\u4ece <strong>0</strong> \u5f00\u59cb\u7684\u6574\u6570\u6570\u7ec4 <code>nums</code> \u548c\u4e00\u4e2a\u6574\u6570 <code>threshold</code> \u3002</p>\n\n<p>\u8bf7\u4f60\u4ece <code>nums</code> \u7684\u5b50\u6570\u7ec4\u4e2d\u627e\u51fa\u4ee5\u4e0b\u6807 <code>l</code> \u5f00\u5934\u3001\u4e0b\u6807 <code>r</code> \u7ed3\u5c3e <code>(0 &lt;= l &lt;= r &lt; nums.length)</code> \u4e14\u6ee1\u8db3\u4ee5\u4e0b\u6761\u4ef6\u7684 <strong>\u6700\u957f\u5b50\u6570\u7ec4</strong> \uff1a</p>\n\n<ul>\n\t<li><code>nums[l] % 2 == 0</code></li>\n\t<li>\u5bf9\u4e8e\u8303\u56f4&nbsp;<code>[l, r - 1]</code> \u5185\u7684\u6240\u6709\u4e0b\u6807 <code>i</code> \uff0c<code>nums[i] % 2 != nums[i + 1] % 2</code></li>\n\t<li>\u5bf9\u4e8e\u8303\u56f4&nbsp;<code>[l, r]</code> \u5185\u7684\u6240\u6709\u4e0b\u6807 <code>i</code> \uff0c<code>nums[i] &lt;= threshold</code></li>\n</ul>\n\n<p>\u4ee5\u6574\u6570\u5f62\u5f0f\u8fd4\u56de\u6ee1\u8db3\u9898\u76ee\u8981\u6c42\u7684\u6700\u957f\u5b50\u6570\u7ec4\u7684\u957f\u5ea6\u3002</p>\n\n<p><strong>\u6ce8\u610f\uff1a\u5b50\u6570\u7ec4</strong> \u662f\u6570\u7ec4\u4e2d\u7684\u4e00\u4e2a\u8fde\u7eed\u975e\u7a7a\u5143\u7d20\u5e8f\u5217\u3002</p>\n\n<p>&nbsp;</p>\n\n<p><strong>\u793a\u4f8b 1\uff1a</strong></p>\n\n<pre><strong>\u8f93\u5165\uff1a</strong>nums = [3,2,5,4], threshold = 5\n<strong>\u8f93\u51fa\uff1a</strong>3\n<strong>\u89e3\u91ca\uff1a</strong>\u5728\u8fd9\u4e2a\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u9009\u62e9\u4ece l = 1 \u5f00\u59cb\u3001\u5230 r = 3 \u7ed3\u675f\u7684\u5b50\u6570\u7ec4 =&gt; [2,5,4] \uff0c\u6ee1\u8db3\u4e0a\u8ff0\u6761\u4ef6\u3002\n\u56e0\u6b64\uff0c\u7b54\u6848\u5c31\u662f\u8fd9\u4e2a\u5b50\u6570\u7ec4\u7684\u957f\u5ea6 3 \u3002\u53ef\u4ee5\u8bc1\u660e 3 \u662f\u6ee1\u8db3\u9898\u76ee\u8981\u6c42\u7684\u6700\u5927\u957f\u5ea6\u3002</pre>\n\n<p><strong>\u793a\u4f8b 2\uff1a</strong></p>\n\n<pre><strong>\u8f93\u5165\uff1a</strong>nums = [1,2], threshold = 2\n<strong>\u8f93\u51fa\uff1a</strong>1\n<strong>\u89e3\u91ca\uff1a</strong>\n\u5728\u8fd9\u4e2a\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u9009\u62e9\u4ece l = 1 \u5f00\u59cb\u3001\u5230 r = 1 \u7ed3\u675f\u7684\u5b50\u6570\u7ec4 =&gt; [2] \u3002\n\u8be5\u5b50\u6570\u7ec4\u6ee1\u8db3\u4e0a\u8ff0\u5168\u90e8\u6761\u4ef6\u3002\u53ef\u4ee5\u8bc1\u660e 1 \u662f\u6ee1\u8db3\u9898\u76ee\u8981\u6c42\u7684\u6700\u5927\u957f\u5ea6\u3002\n</pre>\n\n<p><strong>\u793a\u4f8b 3\uff1a</strong></p>\n\n<pre><strong>\u8f93\u5165\uff1a</strong>nums = [2,3,4,5], threshold = 4\n<strong>\u8f93\u51fa\uff1a</strong>3\n<strong>\u89e3\u91ca\uff1a</strong>\n\u5728\u8fd9\u4e2a\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u9009\u62e9\u4ece l = 0 \u5f00\u59cb\u3001\u5230 r = 2 \u7ed3\u675f\u7684\u5b50\u6570\u7ec4 =&gt; [2,3,4] \u3002 \n\u8be5\u5b50\u6570\u7ec4\u6ee1\u8db3\u4e0a\u8ff0\u5168\u90e8\u6761\u4ef6\u3002\n\u56e0\u6b64\uff0c\u7b54\u6848\u5c31\u662f\u8fd9\u4e2a\u5b50\u6570\u7ec4\u7684\u957f\u5ea6 3 \u3002\u53ef\u4ee5\u8bc1\u660e 3 \u662f\u6ee1\u8db3\u9898\u76ee\u8981\u6c42\u7684\u6700\u5927\u957f\u5ea6\u3002</pre>\n\n<p>&nbsp;</p>\n\n<p><strong>\u63d0\u793a\uff1a</strong></p>\n\n<ul>\n\t<li><code>1 &lt;= nums.length &lt;= 100 </code></li>\n\t<li><code>1 &lt;= nums[i] &lt;= 100 </code></li>\n\t<li><code>1 &lt;= threshold &lt;= 100</code></li>\n</ul>\n\",\"difficulty\":\"Easy\",\"status\":\"ac\",\"stats\":\"{\\"totalAccepted\\": \\"18.9K\\", \\"totalSubmission\\": \\"42K\\", \\"totalAcceptedRaw\\": 18942, \\"totalSubmissionRaw\\": 42024, \\"acRate\\": \\"45.1%\\"}\",\"hints\":[\"Brute force all the possible subarrays and find the longest that satisfies the conditions.\"],\"similarQuestions\":\"[]\",\"sampleTestCase\":\"[3,2,5,4]\n5\",\"exampleTestcases\":\"[3,2,5,4]\n5\n[1,2]\n2\n[2,3,4,5]\n4\",\"exampleTestcaseList\":[\"[3,2,5,4]\n5\",\"[1,2]\n2\",\"[2,3,4,5]\n4\"],\"jsonExampleTestcases\":\"[\\"[3,2,5,4]\\n5\\", \\"[1,2]\\n2\\", \\"[2,3,4,5]\\n4\\"]\",\"metaData\":\"{\n  \\"name\\": \\"longestAlternatingSubarray\\",\n  \\"params\\": [\n    {\n      \\"name\\": \\"nums\\",\n      \\"type\\": \\"integer[]\\"\n    },\n    {\n      \\"type\\": \\"integer\\",\n      \\"name\\": \\"threshold\\"\n    }\n  ],\n  \\"return\\": {\n    \\"type\\": \\"integer\\"\n  }\n}\",\"codeSnippets\":[{\"lang\":\"C++\",\"langSlug\":\"cpp\",\"code\":\"class Solution {\npublic:\n    int longestAlternatingSubarray(vector<int>& nums, int threshold) {\n        \n    }\n};\"},{\"lang\":\"Java\",\"langSlug\":\"java\",\"code\":\"class Solution {\n    public int longestAlternatingSubarray(int[] nums, int threshold) {\n\n    }\n}\"},{\"lang\":\"Python\",\"langSlug\":\"python\",\"code\":\"class Solution(object):\n    def longestAlternatingSubarray(self, nums, threshold):\n        \\"\\"\\"\n        :type nums: List[int]\n        :type threshold: int\n        :rtype: int\n        \\"\\"\\"\"},{\"lang\":\"Python3\",\"langSlug\":\"python3\",\"code\":\"class Solution:\n    def longestAlternatingSubarray(self, nums: List[int], threshold: int) -> int:\"},{\"lang\":\"C\",\"langSlug\":\"c\",\"code\":\"int longestAlternatingSubarray(int* nums, int numsSize, int threshold){\n\n}\"},{\"lang\":\"C#\",\"langSlug\":\"csharp\",\"code\":\"public class Solution {\n    public int LongestAlternatingSubarray(int[] nums, int threshold) {\n\n    }\n}\"},{\"lang\":\"JavaScript\",\"langSlug\":\"javascript\",\"code\":\"/**\n * @param {number[]} nums\n * @param {number} threshold\n * @return {number}\n */\nvar longestAlternatingSubarray = function(nums, threshold) {\n\n};\"},{\"lang\":\"TypeScript\",\"langSlug\":\"typescript\",\"code\":\"function longestAlternatingSubarray(nums: number[], threshold: number): number {\n\n};\"},{\"lang\":\"PHP\",\"langSlug\":\"php\",\"code\":\"class Solution {\n\n    /**\n     * @param Integer[] $nums\n     * @param Integer $threshold\n     * @return Integer\n     */\n    function longestAlternatingSubarray($nums, $threshold) {\n\n    }\n}\"},{\"lang\":\"Swift\",\"langSlug\":\"swift\",\"code\":\"class Solution {\n    func longestAlternatingSubarray(_ nums: [Int], _ threshold: Int) -> Int {\n\n    }\n}\"},{\"lang\":\"Kotlin\",\"langSlug\":\"kotlin\",\"code\":\"class Solution {\n    fun longestAlternatingSubarray(nums: IntArray, threshold: Int): Int {\n\n    }\n}\"},{\"lang\":\"Dart\",\"langSlug\":\"dart\",\"code\":\"class Solution {\n  int longestAlternatingSubarray(List<int> nums, int threshold) {\n\n  }\n}\"},{\"lang\":\"Go\",\"langSlug\":\"golang\",\"code\":\"func longestAlternatingSubarray(nums []int, threshold int) int {\n\n}\"},{\"lang\":\"Ruby\",\"langSlug\":\"ruby\",\"code\":\"# @param {Integer[]} nums\n# @param {Integer} threshold\n# @return {Integer}\ndef longest_alternating_subarray(nums, threshold)\n\nend\"},{\"lang\":\"Scala\",\"langSlug\":\"scala\",\"code\":\"object Solution {\n    def longestAlternatingSubarray(nums: Array[Int], threshold: Int): Int = {\n\n    }\n}\"},{\"lang\":\"Rust\",\"langSlug\":\"rust\",\"code\":\"impl Solution {\n    pub fn longest_alternating_subarray(nums: Vec<i32>, threshold: i32) -> i32 {\n\n    }\n}\"},{\"lang\":\"Racket\",\"langSlug\":\"racket\",\"code\":\"(define/contract (longest-alternating-subarray nums threshold)\n  (-> (listof exact-integer?) exact-integer? exact-integer?)\n\n  )\"},{\"lang\":\"Erlang\",\"langSlug\":\"erlang\",\"code\":\"-spec longest_alternating_subarray(Nums :: [integer()], Threshold :: integer()) -> integer().\nlongest_alternating_subarray(Nums, Threshold) ->\n  .\"},{\"lang\":\"Elixir\",\"langSlug\":\"elixir\",\"code\":\"defmodule Solution do\n  @spec longest_alternating_subarray(nums :: [integer], threshold :: integer) :: integer\n  def longest_alternating_subarray(nums, threshold) do\n\n  end\nend\"}],\"topicTags\":[{\"name\":\"Array\",\"slug\":\"array\",\"translatedName\":\"\u6570\u7ec4\"},{\"name\":\"Sliding Window\",\"slug\":\"sliding-window\",\"translatedName\":\"\u6ed1\u52a8\u7a97\u53e3\"}],\"editorType\":"
wslpath: https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/: No such file or directory

cmdlet Start-Process at command pipeline position 1
Supply values for the following parameters:
FilePath:
Start-Process : Cannot process command because of one or more missing mandat
ory parameters: FilePath.
At line:1 char:1
+ start
+ ~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Start-Process], Paramet
   erBindingException
    + FullyQualifiedErrorId : MissingMandatoryParameter,Microsoft.PowerShel
   l.Commands.StartProcessCommand

2023/11/16 16:01:50 FATA exit status 4
```


### Description

```
root@vthe ~# leetgo open today
● leetgo.yaml not found, use global config only file=/root/.config/leetgo/config.yaml
× exec: "xdg-open,x-www-browser,www-browser": executable file not found in $PATH
```
after install `xdg-utils`,  and link browser
```
root@vthe ~# ln -s /mnt/d/CentBrowser/chrome.exe /usr/local/bin/chrome
root@vthe ~# ln -s /usr/bin/chrome /usr/bin/x-www-browser
root@vthe ~# ln -s /usr/bin/chrome /usr/bin/www-browser
```
`leetgo open today` get weird output
```
root@vthe ~/leetcode (main)# leetgo open today
wslpath: https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/: No such file or directory
cmdlet Start-Process at command pipeline position 1
Supply values for the following parameters:
FilePath:
Start-Process : Cannot process command because of one or more missing mandat
ory parameters: FilePath.
At line:1 char:1
+ start
+ ~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Start-Process], Paramet
   erBindingException
    + FullyQualifiedErrorId : MissingMandatoryParameter,Microsoft.PowerShel
   l.Commands.StartProcessCommand

× exit status 4
```



Discussion / hints:
What will happen if you run `xdg-open 'https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/'`?
```
xdg-open 'https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/'
wslpath: https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/: No such file or directory

位于命令管道位置 1 的 cmdlet Start-Process
请为以下参数提供值:
FilePath:
```
this will ask me to input a FilePath

if i input FilePath with `"D:\CentBrowser\chrome.exe"`
it will open chrome, but don't open url
```
root@vthe ~ [4]# xdg-open 'https://leetcode.cn/problems/longest-even-odd-suba
rray-with-threshold/'
wslpath: https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/: No such file or directory

位于命令管道位置 1 的 cmdlet Start-Process
请为以下参数提供值:
FilePath: "D:\CentBrowser\chrome.exe"
```
Apparently `xdg-open` has some issues working under WSL.

Some helpful links:
- https://stackoverflow.com/questions/66585350/ddev-local-wsl2-how-do-i-get-xdg-open-to-open-a-browser-on-windows-from-wsl2
- https://github.com/microsoft/WSL/issues/8892
Thanks for your help. 
Yesterday I wanted to try `leetgo open today` in wsl, and tried it for an hour, so reported it as a bug.


Fix patch:
diff --git a/cmd/open.go b/cmd/open.go
index d6be6bbd..1e74bfba 100644
--- a/cmd/open.go
+++ b/cmd/open.go
@@ -1,7 +1,7 @@
 package cmd
 
 import (
-	"github.com/pkg/browser"
+	"github.com/cli/browser"
 	"github.com/spf13/cobra"
 
 	"github.com/j178/leetgo/leetcode"
diff --git a/go.mod b/go.mod
index c11bd2d5..aae3f6c2 100644
--- a/go.mod
+++ b/go.mod
@@ -13,6 +13,7 @@ require (
 	github.com/charmbracelet/glamour v0.6.0
 	github.com/charmbracelet/lipgloss v0.9.1
 	github.com/charmbracelet/log v0.3.0
+	github.com/cli/browser v1.3.0
 	github.com/dghubble/sling v1.4.1
 	github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d
 	github.com/fatih/color v1.16.0
@@ -30,7 +31,6 @@ require (
 	github.com/mitchellh/go-homedir v1.1.0
 	github.com/muesli/reflow v0.3.0
 	github.com/pelletier/go-toml/v2 v2.1.0
-	github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
 	github.com/sashabaranov/go-openai v1.17.5
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/viper v1.17.0
@@ -82,7 +82,6 @@ require (
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.10.0 // indirect
 	github.com/spf13/cast v1.5.1 // indirect
-	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/tidwall/match v1.1.1 // indirect
@@ -90,7 +89,6 @@ require (
 	github.com/yuin/goldmark v1.6.0 // indirect
 	github.com/yuin/goldmark-emoji v1.0.2 // indirect
 	github.com/zalando/go-keyring v0.2.3 // indirect
-	go.uber.org/atomic v1.11.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/crypto v0.15.0 // indirect
 	golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
diff --git a/go.sum b/go.sum
index ad41feda..0124113e 100644
--- a/go.sum
+++ b/go.sum
@@ -78,12 +78,8 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06
 github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
 github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
 github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc=
-github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU=
-github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU=
 github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
 github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
-github.com/charmbracelet/log v0.2.4 h1:3pKtq5/Y5QMKtcZt7kDqD1p9w7lICzHYQACBFY4ocHA=
-github.com/charmbracelet/log v0.2.4/go.mod h1:nQGK8tvc4pS9cvVEH/pWJiZ50eUq1aoXUOjGpXvdD0k=
 github.com/charmbracelet/log v0.3.0 h1:u5aB2KJDgNZo4WOfOC8C+KvGIkJ2rCFNlPWDu6xhnqI=
 github.com/charmbracelet/log v0.3.0/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -92,6 +88,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
 github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
+github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -99,15 +97,14 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht
 github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
 github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
 github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
 github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dghubble/sling v1.4.1 h1:AxjTubpVyozMvbBCtXcsWEyGGgUZutC5YGrfxPNVOcQ=
 github.com/dghubble/sling v1.4.1/go.mod h1:QoMB1KL3GAo+7HsD8Itd6S+6tW91who8BGZzuLvpOyc=
 github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
@@ -116,8 +113,6 @@ github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnm
 github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
 github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
-github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e h1:UvQD6hTSfeM6hhTQ24Dlw2RppP05W7SWbWb6kubJAog=
-github.com/dop251/goja v0.0.0-20230828202809-3dbe69dd2b8e/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
 github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d h1:wi6jN5LVt/ljaBG4ue79Ekzb12QfJ52L9Q98tl8SWhw=
 github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
 github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
@@ -129,14 +124,10 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
-github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
 github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
 github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
 github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
 github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
-github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
-github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -210,8 +201,6 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
 github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
-github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ=
-github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
 github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
 github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
@@ -223,7 +212,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
 github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
 github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
 github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
@@ -251,8 +239,6 @@ github.com/j178/kooky v0.0.0-20230609034248-af88dcd40695 h1:v9a4z7YcjcnP7e4r6r1U
 github.com/j178/kooky v0.0.0-20230609034248-af88dcd40695/go.mod h1:7Rm3VdbhA/02fT1ObHhffCHXDhdmn3/x+EIHCbMuf44=
 github.com/j178/leetgo/testutils/go v0.2.0 h1:5ofMFF29RMM0ANCjlwpOR9rkOFtYf/5UTGbCwkRds5A=
 github.com/j178/leetgo/testutils/go v0.2.0/go.mod h1:m4VquSOufm2jcOyXW7P52fWa8adQim5XoQSEAJX2hw8=
-github.com/jedib0t/go-pretty/v6 v6.4.7 h1:lwiTJr1DEkAgzljsUsORmWsVn5MQjt1BPJdPCtJ6KXE=
-github.com/jedib0t/go-pretty/v6 v6.4.7/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs=
 github.com/jedib0t/go-pretty/v6 v6.4.9 h1:vZ6bjGg2eBSrJn365qlxGcaWu09Id+LHtrfDWlB2Usc=
 github.com/jedib0t/go-pretty/v6 v6.4.9/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
@@ -290,8 +276,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
@@ -302,16 +286,12 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
 github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
 github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
-github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
 github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
-github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg=
-github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE=
 github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
 github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
@@ -331,15 +311,13 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
 github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
 github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -356,8 +334,6 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
 github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
 github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
 github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
-github.com/sashabaranov/go-openai v1.15.4 h1:BXCR0Uxk5RipeY4yBC7g6pBVfcjh8jwrMNOYdie6yuk=
-github.com/sashabaranov/go-openai v1.15.4/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
 github.com/sashabaranov/go-openai v1.17.5 h1:ItBzlrrfTtkFWOFlgfOhk3y/xRBC4PJol4gdbiK7hgg=
 github.com/sashabaranov/go-openai v1.17.5/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
 github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
@@ -371,23 +347,15 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
 github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
-github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
-github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
 github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
 github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
 github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
 github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
 github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
-github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
-github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
 github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
 github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
-github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
-github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
 github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI=
 github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -422,8 +390,6 @@ github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.5.6 h1:COmQAWTCcGetChm3Ig7G/t8AFAN00t+o8Mt4cf7JpwA=
-github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
 github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
@@ -437,8 +403,6 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
-go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -449,7 +413,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
 golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
 golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
 golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
@@ -530,7 +493,6 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
 golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
 golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
 golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
@@ -555,8 +517,6 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
-golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
 golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
 golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -596,20 +556,17 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
 golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -618,7 +575,6 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
 golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
 golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
 golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
@@ -634,7 +590,6 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
 golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=


Test patch:
diff --git a/cmd/contest.go b/cmd/contest.go
index 8b5fd80a..09f745bd 100644
--- a/cmd/contest.go
+++ b/cmd/contest.go
@@ -11,8 +11,8 @@ import (
 	"github.com/briandowns/spinner"
 	"github.com/charmbracelet/lipgloss"
 	"github.com/charmbracelet/log"
+	"github.com/cli/browser"
 	"github.com/hako/durafmt"
-	"github.com/pkg/browser"
 	"github.com/spf13/cobra"
 	"github.com/spf13/viper"