weio-cli 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weio-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Weio — an agentic coding assistant that routes inference through your Weio account.
5
5
  Author: We I/O Labs
6
6
  License: MIT
@@ -17,6 +17,7 @@ Requires-Python: >=3.9
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: httpx>=0.24
20
+ Requires-Dist: prompt_toolkit>=3.0
20
21
  Dynamic: license-file
21
22
 
22
23
  # weio-cli
@@ -44,8 +45,36 @@ export WEIO_API_KEY="weio_sk_…"
44
45
 
45
46
  ## Use
46
47
 
48
+ ### Interactive TUI (default)
49
+
50
+ Run `weio` with no arguments to drop into an interactive coding session — like a
51
+ local pair-programmer in your terminal:
52
+
53
+ ```bash
54
+ cd my-project
55
+ weio
56
+ ```
57
+
58
+ ```
59
+ weio › add a /health route to app.py that returns {"ok": true}
60
+
61
+ weio: I'll add a health check route…
62
+ ● edit app.py
63
+ --- a/app.py
64
+ +++ b/app.py
65
+ @@ …
66
+ Apply 1 change(s)? [y/N] y
67
+ ✓ applied 1 change(s). (/undo to revert)
68
+ ```
69
+
70
+ Slash commands: `/add <file>`, `/drop <file>`, `/files`, `/auto` (auto-apply),
71
+ `/model <id>`, `/undo`, `/clear`, `/cwd`, `/help`, `/exit`. Arrow-up recalls
72
+ history. Files you mention or `/add` are kept in context and re-read each turn.
73
+
74
+ ### One-shot & other commands
75
+
47
76
  ```bash
48
- # Run a coding task in the current directory (reads & edits files):
77
+ # Run a single coding task non-interactively (reads & edits files):
49
78
  weio "add error handling to the fetch() in api.py"
50
79
 
51
80
  # Add specific files to the context:
@@ -23,8 +23,36 @@ export WEIO_API_KEY="weio_sk_…"
23
23
 
24
24
  ## Use
25
25
 
26
+ ### Interactive TUI (default)
27
+
28
+ Run `weio` with no arguments to drop into an interactive coding session — like a
29
+ local pair-programmer in your terminal:
30
+
31
+ ```bash
32
+ cd my-project
33
+ weio
34
+ ```
35
+
36
+ ```
37
+ weio › add a /health route to app.py that returns {"ok": true}
38
+
39
+ weio: I'll add a health check route…
40
+ ● edit app.py
41
+ --- a/app.py
42
+ +++ b/app.py
43
+ @@ …
44
+ Apply 1 change(s)? [y/N] y
45
+ ✓ applied 1 change(s). (/undo to revert)
46
+ ```
47
+
48
+ Slash commands: `/add <file>`, `/drop <file>`, `/files`, `/auto` (auto-apply),
49
+ `/model <id>`, `/undo`, `/clear`, `/cwd`, `/help`, `/exit`. Arrow-up recalls
50
+ history. Files you mention or `/add` are kept in context and re-read each turn.
51
+
52
+ ### One-shot & other commands
53
+
26
54
  ```bash
27
- # Run a coding task in the current directory (reads & edits files):
55
+ # Run a single coding task non-interactively (reads & edits files):
28
56
  weio "add error handling to the fetch() in api.py"
29
57
 
30
58
  # Add specific files to the context:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "weio-cli"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "Weio — an agentic coding assistant that routes inference through your Weio account."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -19,7 +19,7 @@ classifiers = [
19
19
  "Programming Language :: Python :: 3",
20
20
  "Topic :: Software Development :: Code Generators",
21
21
  ]
22
- dependencies = ["httpx>=0.24"]
22
+ dependencies = ["httpx>=0.24", "prompt_toolkit>=3.0"]
23
23
 
24
24
  [project.urls]
25
25
  Homepage = "https://weio.ai"
@@ -1,3 +1,3 @@
1
1
  """Weio CLI — an agentic coding assistant that routes inference through your Weio account."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.2.0"
@@ -128,6 +128,20 @@ def cmd_chat(args) -> int:
128
128
  return 0
129
129
 
130
130
 
131
+ def cmd_tui(args) -> int:
132
+ try:
133
+ client = _make_client(args)
134
+ except WeioError as e:
135
+ print(_c(str(e), "31"), file=sys.stderr)
136
+ return 2
137
+ root = Path(getattr(args, "dir", ".") or ".").resolve()
138
+ if not root.is_dir():
139
+ print(_c(f"Not a directory: {root}", "31"), file=sys.stderr)
140
+ return 2
141
+ from .tui import run_tui
142
+ return run_tui(client, root, max_tokens=_max_tokens(args))
143
+
144
+
131
145
  def cmd_code(args) -> int:
132
146
  try:
133
147
  client = _make_client(args)
@@ -239,6 +253,10 @@ def build_parser() -> argparse.ArgumentParser:
239
253
  sp = sub.add_parser("chat", parents=[common], help="Interactive chat")
240
254
  sp.set_defaults(func=cmd_chat)
241
255
 
256
+ sp = sub.add_parser("tui", parents=[common], help="Interactive coding TUI (default when run with no args)")
257
+ sp.add_argument("--dir", "-C", default=".", help="Project directory (default: cwd)")
258
+ sp.set_defaults(func=cmd_tui)
259
+
242
260
  sp = sub.add_parser("code", parents=[common], help="Run a coding task in a directory")
243
261
  sp.add_argument("message")
244
262
  sp.add_argument("--file", "-f", action="append", help="Add a file to the context (repeatable)")
@@ -253,9 +271,12 @@ def main(argv=None) -> int:
253
271
  argv = list(sys.argv[1:] if argv is None else argv)
254
272
  parser = build_parser()
255
273
 
256
- # Bare `weio "do something"` defaults to the `code` command.
257
- known = {"login", "ping", "ask", "chat", "code"}
258
- if argv and argv[0] not in known and not argv[0].startswith("-"):
274
+ known = {"login", "ping", "ask", "chat", "code", "tui"}
275
+ # Bare `weio` (no args) → interactive coding TUI, like Claude Code.
276
+ if not argv:
277
+ argv = ["tui"]
278
+ # Bare `weio "do something"` (a message, not a flag/command) → one-shot code.
279
+ elif argv[0] not in known and not argv[0].startswith("-"):
259
280
  argv = ["code", *argv]
260
281
 
261
282
  args = parser.parse_args(argv)
@@ -73,6 +73,21 @@ class WeioClient:
73
73
  if r.status_code >= 400:
74
74
  body = r.read().decode("utf-8", "replace")
75
75
  raise WeioError(f"Gateway error {r.status_code}: {body[:300]}")
76
+
77
+ ctype = r.headers.get("content-type", "")
78
+ # Not all gateways honor stream=true. If the response isn't SSE,
79
+ # read the whole JSON body and yield its content in one shot.
80
+ if "text/event-stream" not in ctype:
81
+ body = r.read().decode("utf-8", "replace")
82
+ try:
83
+ content = json.loads(body)["choices"][0]["message"]["content"]
84
+ if content:
85
+ yield content
86
+ except (KeyError, IndexError, json.JSONDecodeError) as e:
87
+ raise WeioError(f"Unexpected response shape: {body[:300]}") from e
88
+ return
89
+
90
+ saw_delta = False
76
91
  for line in r.iter_lines():
77
92
  if not line or not line.startswith("data:"):
78
93
  continue
@@ -82,9 +97,14 @@ class WeioClient:
82
97
  try:
83
98
  delta = json.loads(data)["choices"][0].get("delta", {}).get("content")
84
99
  if delta:
100
+ saw_delta = True
85
101
  yield delta
86
102
  except (KeyError, IndexError, json.JSONDecodeError):
87
103
  continue
104
+ if not saw_delta:
105
+ # SSE content-type but no usable deltas — fall back to non-stream.
106
+ yield self.chat(messages, max_tokens=max_tokens,
107
+ temperature=temperature, model=model)
88
108
  except httpx.HTTPError as e:
89
109
  raise WeioError(f"Network error talking to {self.base}: {e}") from e
90
110
 
@@ -0,0 +1,321 @@
1
+ """Interactive coding TUI — a Claude Code-style REPL for weio.
2
+
3
+ Launch with bare `weio` (or `weio tui`). You get a persistent prompt where you
4
+ chat with the model; when it proposes file changes they're shown as a diff and
5
+ applied (with confirmation, or auto in /auto mode). Slash commands manage the
6
+ session. Uses prompt_toolkit when available, with a plain readline fallback.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ from . import __version__, coder
15
+ from .client import WeioClient, WeioError
16
+
17
+ try:
18
+ from prompt_toolkit import PromptSession
19
+ from prompt_toolkit.history import FileHistory
20
+ from prompt_toolkit.completion import WordCompleter
21
+ from prompt_toolkit.styles import Style
22
+ _HAS_PTK = True
23
+ except Exception: # pragma: no cover
24
+ _HAS_PTK = False
25
+
26
+
27
+ # --------------------------------------------------------------------------- #
28
+ # Colors
29
+ # --------------------------------------------------------------------------- #
30
+ _TTY = sys.stdout.isatty()
31
+
32
+ def _c(s: str, code: str) -> str:
33
+ return f"\033[{code}m{s}\033[0m" if _TTY else s
34
+
35
+ DIM = "2"; BOLD = "1"; CYAN = "36"; GREEN = "32"; YELLOW = "33"; RED = "31"; BLUE = "34"
36
+
37
+
38
+ INTERACTIVE_SYSTEM = """You are Weio, an expert pair-programmer running as an interactive coding agent in the user's terminal, inside their project directory.
39
+
40
+ You hold a normal conversation AND make code changes. When you change files, emit EDIT BLOCKS:
41
+
42
+ ```edit relative/path/to/file.ext
43
+ <<<<<<< SEARCH
44
+ <exact lines that currently exist>
45
+ =======
46
+ <replacement lines>
47
+ >>>>>>> REPLACE
48
+ ```
49
+
50
+ - SEARCH must match the current file contents EXACTLY. For a NEW file, leave SEARCH empty.
51
+ - You may emit several blocks (and several per file). Keep edits minimal and focused.
52
+ - Before/after edit blocks, talk to the user normally: explain your plan briefly and summarize what you changed.
53
+ - Only edit files relevant to the request. If you need a file you haven't been shown, ask for it (the user can /add it).
54
+ - When the user just asks a question, answer conversationally with no edit blocks."""
55
+
56
+ HELP = """Commands:
57
+ /help show this help
58
+ /add <path> add a file to the working context (re-read each turn)
59
+ /drop <path> remove a file from context
60
+ /files list files in context
61
+ /diff show the last proposed (unapplied) edits
62
+ /auto toggle auto-apply of edits (default: confirm each time)
63
+ /model <id> switch model (e.g. auto, low, mid, pro)
64
+ /undo revert the last applied edit set
65
+ /clear clear the conversation history
66
+ /cwd [path] show or change the working directory
67
+ /exit, /quit leave (Ctrl-D also works)
68
+
69
+ Type anything else to talk to Weio. It can edit files in the project directly."""
70
+
71
+
72
+ class Repl:
73
+ def __init__(self, client: WeioClient, root: Path, max_tokens: int = 4096):
74
+ self.client = client
75
+ self.root = root
76
+ self.max_tokens = max_tokens
77
+ self.messages: list[dict] = [{"role": "system", "content": INTERACTIVE_SYSTEM}]
78
+ self.context_files: list[str] = []
79
+ self.auto_apply = False
80
+ self.pending: list[coder.Edit] = []
81
+ self.undo_stack: list[list[tuple]] = [] # each entry: [(path, old_text_or_None)]
82
+ self._session = None
83
+ # prompt_toolkit needs a real terminal; fall back to plain input otherwise
84
+ # (pipes, CI, dumb terminals).
85
+ if _HAS_PTK and sys.stdin.isatty() and sys.stdout.isatty():
86
+ cfg_dir = Path(os.environ.get("WEIO_CONFIG_DIR", str(Path.home() / ".weio")))
87
+ cfg_dir.mkdir(parents=True, exist_ok=True)
88
+ self._session = PromptSession(
89
+ history=FileHistory(str(cfg_dir / "tui_history")),
90
+ completer=WordCompleter(
91
+ ["/help", "/add", "/drop", "/files", "/diff", "/auto", "/model",
92
+ "/undo", "/clear", "/cwd", "/exit", "/quit"],
93
+ sentence=True,
94
+ ),
95
+ )
96
+
97
+ # ----- input -----
98
+ def _read(self) -> str | None:
99
+ prompt = "weio › "
100
+ try:
101
+ if self._session is not None:
102
+ return self._session.prompt(
103
+ prompt,
104
+ bottom_toolbar=f" {self.root.name} · model={self.client.model} · "
105
+ f"{'AUTO-APPLY' if self.auto_apply else 'confirm edits'} · /help",
106
+ )
107
+ return input(_c(prompt, CYAN))
108
+ except (EOFError, KeyboardInterrupt):
109
+ return None
110
+
111
+ # ----- context -----
112
+ def _context_block(self) -> str:
113
+ blocks = []
114
+ for rel in list(self.context_files):
115
+ fp = self.root / rel
116
+ if not fp.is_file():
117
+ continue
118
+ try:
119
+ blocks.append(f"=== FILE: {rel} ===\n{fp.read_text(encoding='utf-8')}")
120
+ except (OSError, UnicodeDecodeError):
121
+ continue
122
+ return "\n\n".join(blocks)
123
+
124
+ # ----- turn -----
125
+ def _turn(self, text: str):
126
+ user_content = text
127
+ ctx = self._context_block()
128
+ # Auto-include any path-like tokens that exist and aren't already in context
129
+ auto_ctx, _ = coder.gather_context(text, [], self.root)
130
+ extra = auto_ctx if auto_ctx and not ctx else ""
131
+ if ctx or extra:
132
+ user_content += "\n\nFILES IN CONTEXT:\n" + (ctx or extra)
133
+
134
+ self.messages.append({"role": "user", "content": user_content})
135
+
136
+ sys.stdout.write(_c("\nweio: ", GREEN))
137
+ sys.stdout.flush()
138
+ acc = ""
139
+ try:
140
+ for delta in self.client.chat_stream(self.messages, max_tokens=self.max_tokens,
141
+ temperature=0.2):
142
+ acc += delta
143
+ sys.stdout.write(delta)
144
+ sys.stdout.flush()
145
+ print()
146
+ except WeioError as e:
147
+ print("\n" + _c(str(e), RED))
148
+ self.messages.pop()
149
+ return
150
+ self.messages.append({"role": "assistant", "content": acc})
151
+
152
+ edits = coder.parse_edits(acc)
153
+ if edits:
154
+ self._handle_edits(edits)
155
+
156
+ def _handle_edits(self, edits: list[coder.Edit]):
157
+ results, errors = [], []
158
+ for e in edits:
159
+ res, err = coder.apply_edit(e, self.root)
160
+ if err:
161
+ errors.append(err)
162
+ elif res:
163
+ results.append(res)
164
+ for res in results:
165
+ tag = _c("● new ", GREEN) if res.created else _c("● edit ", YELLOW)
166
+ print("\n" + tag + _c(res.path, BOLD))
167
+ if res.created:
168
+ body = res.new_text if len(res.new_text) < 1200 else res.new_text[:1200] + "\n…"
169
+ print(body)
170
+ else:
171
+ print(_color_diff(coder.unified_diff(res.path, res.old_text, res.new_text)))
172
+ for err in errors:
173
+ print(_c(" skip: " + err, RED))
174
+ if not results:
175
+ return
176
+ if not self.auto_apply:
177
+ ans = self._confirm(f"Apply {len(results)} change(s)? [y/N] ")
178
+ if ans not in ("y", "yes"):
179
+ print(_c(" not applied.", DIM))
180
+ return
181
+ undo_entry = []
182
+ for res in results:
183
+ fp = self.root / res.path
184
+ old = fp.read_text(encoding="utf-8") if fp.exists() else None
185
+ undo_entry.append((res.path, old))
186
+ fp.parent.mkdir(parents=True, exist_ok=True)
187
+ fp.write_text(res.new_text, encoding="utf-8")
188
+ self.undo_stack.append(undo_entry)
189
+ for res in results:
190
+ if res.path not in self.context_files:
191
+ self.context_files.append(res.path) # keep edited files in context
192
+ print(_c(f" ✓ applied {len(results)} change(s). (/undo to revert)", GREEN))
193
+
194
+ def _confirm(self, msg: str) -> str:
195
+ try:
196
+ if self._session is not None:
197
+ return self._session.prompt(msg).strip().lower()
198
+ return input(_c(msg, CYAN)).strip().lower()
199
+ except (EOFError, KeyboardInterrupt):
200
+ print()
201
+ return "n"
202
+
203
+ # ----- slash commands -----
204
+ def _slash(self, text: str) -> bool:
205
+ parts = text.strip().split(maxsplit=1)
206
+ cmd = parts[0].lower()
207
+ arg = parts[1].strip() if len(parts) > 1 else ""
208
+ if cmd in ("/exit", "/quit"):
209
+ return False
210
+ elif cmd == "/help":
211
+ print(HELP)
212
+ elif cmd == "/add":
213
+ if not arg:
214
+ print(_c(" usage: /add <path>", DIM))
215
+ elif (self.root / arg).is_file():
216
+ if arg not in self.context_files:
217
+ self.context_files.append(arg)
218
+ print(_c(f" added {arg}", GREEN))
219
+ else:
220
+ print(_c(f" no such file: {arg}", RED))
221
+ elif cmd == "/drop":
222
+ if arg in self.context_files:
223
+ self.context_files.remove(arg)
224
+ print(_c(f" dropped {arg}", GREEN))
225
+ else:
226
+ print(_c(f" not in context: {arg}", DIM))
227
+ elif cmd == "/files":
228
+ if self.context_files:
229
+ for f in self.context_files:
230
+ print(" " + f)
231
+ else:
232
+ print(_c(" (no files in context — /add <path> or just mention them)", DIM))
233
+ elif cmd == "/auto":
234
+ self.auto_apply = not self.auto_apply
235
+ print(_c(f" auto-apply {'ON' if self.auto_apply else 'OFF'}", YELLOW))
236
+ elif cmd == "/model":
237
+ if arg:
238
+ self.client.model = arg
239
+ print(_c(f" model = {arg}", GREEN))
240
+ else:
241
+ print(" model =", self.client.model)
242
+ elif cmd == "/clear":
243
+ self.messages = [{"role": "system", "content": INTERACTIVE_SYSTEM}]
244
+ print(_c(" conversation cleared.", GREEN))
245
+ elif cmd == "/cwd":
246
+ if arg:
247
+ p = (self.root / arg).resolve() if not os.path.isabs(arg) else Path(arg)
248
+ if p.is_dir():
249
+ self.root = p
250
+ print(_c(f" cwd = {p}", GREEN))
251
+ else:
252
+ print(_c(f" not a directory: {arg}", RED))
253
+ else:
254
+ print(" cwd =", self.root)
255
+ elif cmd == "/undo":
256
+ self._undo()
257
+ elif cmd == "/diff":
258
+ print(_c(" (edits are shown when proposed; nothing pending)", DIM))
259
+ else:
260
+ print(_c(f" unknown command: {cmd} (/help)", RED))
261
+ return True
262
+
263
+ def _undo(self):
264
+ if not self.undo_stack:
265
+ print(_c(" nothing to undo.", DIM))
266
+ return
267
+ entry = self.undo_stack.pop()
268
+ for path, old in entry:
269
+ fp = self.root / path
270
+ if old is None:
271
+ try:
272
+ fp.unlink()
273
+ except OSError:
274
+ pass
275
+ else:
276
+ fp.write_text(old, encoding="utf-8")
277
+ print(_c(f" ↩ reverted {len(entry)} file(s).", GREEN))
278
+
279
+ # ----- main loop -----
280
+ def run(self) -> int:
281
+ ok = self.client.ping()
282
+ print(_c(f"weio {__version__}", BOLD) + _c(f" · {self.root}", DIM))
283
+ print(_c(f" {self.client.base} · " +
284
+ ("connected" if ok else "⚠ cannot reach gateway / bad key"),
285
+ GREEN if ok else RED))
286
+ print(_c(" Type a request, or /help. Ctrl-D to exit.\n", DIM))
287
+ while True:
288
+ text = self._read()
289
+ if text is None:
290
+ print()
291
+ break
292
+ text = text.strip()
293
+ if not text:
294
+ continue
295
+ if text.startswith("/"):
296
+ if not self._slash(text):
297
+ break
298
+ continue
299
+ self._turn(text)
300
+ print(_c("bye.", DIM))
301
+ return 0
302
+
303
+
304
+ def _color_diff(diff: str) -> str:
305
+ if not _TTY or not diff:
306
+ return diff or " (no textual diff)"
307
+ out = []
308
+ for line in diff.splitlines():
309
+ if line.startswith("+") and not line.startswith("+++"):
310
+ out.append(_c(line, GREEN))
311
+ elif line.startswith("-") and not line.startswith("---"):
312
+ out.append(_c(line, RED))
313
+ elif line.startswith("@@"):
314
+ out.append(_c(line, CYAN))
315
+ else:
316
+ out.append(line)
317
+ return "\n".join(out)
318
+
319
+
320
+ def run_tui(client: WeioClient, root: Path, max_tokens: int = 4096) -> int:
321
+ return Repl(client, root, max_tokens=max_tokens).run()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weio-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Weio — an agentic coding assistant that routes inference through your Weio account.
5
5
  Author: We I/O Labs
6
6
  License: MIT
@@ -17,6 +17,7 @@ Requires-Python: >=3.9
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
19
  Requires-Dist: httpx>=0.24
20
+ Requires-Dist: prompt_toolkit>=3.0
20
21
  Dynamic: license-file
21
22
 
22
23
  # weio-cli
@@ -44,8 +45,36 @@ export WEIO_API_KEY="weio_sk_…"
44
45
 
45
46
  ## Use
46
47
 
48
+ ### Interactive TUI (default)
49
+
50
+ Run `weio` with no arguments to drop into an interactive coding session — like a
51
+ local pair-programmer in your terminal:
52
+
53
+ ```bash
54
+ cd my-project
55
+ weio
56
+ ```
57
+
58
+ ```
59
+ weio › add a /health route to app.py that returns {"ok": true}
60
+
61
+ weio: I'll add a health check route…
62
+ ● edit app.py
63
+ --- a/app.py
64
+ +++ b/app.py
65
+ @@ …
66
+ Apply 1 change(s)? [y/N] y
67
+ ✓ applied 1 change(s). (/undo to revert)
68
+ ```
69
+
70
+ Slash commands: `/add <file>`, `/drop <file>`, `/files`, `/auto` (auto-apply),
71
+ `/model <id>`, `/undo`, `/clear`, `/cwd`, `/help`, `/exit`. Arrow-up recalls
72
+ history. Files you mention or `/add` are kept in context and re-read each turn.
73
+
74
+ ### One-shot & other commands
75
+
47
76
  ```bash
48
- # Run a coding task in the current directory (reads & edits files):
77
+ # Run a single coding task non-interactively (reads & edits files):
49
78
  weio "add error handling to the fetch() in api.py"
50
79
 
51
80
  # Add specific files to the context:
@@ -7,6 +7,7 @@ src/weio_cli/cli.py
7
7
  src/weio_cli/client.py
8
8
  src/weio_cli/coder.py
9
9
  src/weio_cli/config.py
10
+ src/weio_cli/tui.py
10
11
  src/weio_cli.egg-info/PKG-INFO
11
12
  src/weio_cli.egg-info/SOURCES.txt
12
13
  src/weio_cli.egg-info/dependency_links.txt
@@ -0,0 +1,2 @@
1
+ httpx>=0.24
2
+ prompt_toolkit>=3.0
@@ -1 +0,0 @@
1
- httpx>=0.24
File without changes
File without changes
File without changes
File without changes