lcode-cli 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl
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.
- lcode/__init__.py +1 -1
- lcode/agent.py +41 -12
- lcode/permissions.py +1 -1
- {lcode_cli-0.2.0.dist-info → lcode_cli-0.2.1.dist-info}/METADATA +10 -8
- {lcode_cli-0.2.0.dist-info → lcode_cli-0.2.1.dist-info}/RECORD +8 -8
- {lcode_cli-0.2.0.dist-info → lcode_cli-0.2.1.dist-info}/WHEEL +0 -0
- {lcode_cli-0.2.0.dist-info → lcode_cli-0.2.1.dist-info}/entry_points.txt +0 -0
- {lcode_cli-0.2.0.dist-info → lcode_cli-0.2.1.dist-info}/licenses/LICENSE +0 -0
lcode/__init__.py
CHANGED
lcode/agent.py
CHANGED
|
@@ -101,6 +101,29 @@ def git_info(cwd: Path) -> str:
|
|
|
101
101
|
return "unknown"
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
class Progress:
|
|
105
|
+
"""The status line under a running request.
|
|
106
|
+
|
|
107
|
+
It is re-rendered on every spinner frame, so the elapsed time keeps moving even when no text
|
|
108
|
+
arrives, for example while the model writes a long file into a tool call.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(self) -> None:
|
|
112
|
+
self.started = self.last_chunk = time.time()
|
|
113
|
+
self.phase = "Thinking"
|
|
114
|
+
self.chars = 0
|
|
115
|
+
|
|
116
|
+
def update(self, phase: str, text: str) -> None:
|
|
117
|
+
self.phase, self.chars, self.last_chunk = phase, self.chars + len(text), time.time()
|
|
118
|
+
|
|
119
|
+
def __rich__(self) -> Text:
|
|
120
|
+
now = time.time()
|
|
121
|
+
phase = self.phase if now - self.last_chunk < 3 else "Working"
|
|
122
|
+
return Text.from_markup(
|
|
123
|
+
f"[cyan]{phase}…[/] [dim]({self.chars // 4} tokens, {now - self.started:.0f}s · Ctrl+C to stop)[/]"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
104
127
|
@dataclass
|
|
105
128
|
class Settings:
|
|
106
129
|
model: str # Ollama model name to call
|
|
@@ -301,35 +324,41 @@ class Agent:
|
|
|
301
324
|
"""Stream one model response, rendering thinking/content live. Returns the assistant message."""
|
|
302
325
|
content, thinking, tool_calls, final = "", "", [], {}
|
|
303
326
|
md = MarkdownStreamer(self.console)
|
|
304
|
-
|
|
305
|
-
status
|
|
306
|
-
|
|
327
|
+
progress = Progress()
|
|
328
|
+
status = None
|
|
329
|
+
printed_thinking = False
|
|
330
|
+
|
|
331
|
+
def start_spinner() -> None:
|
|
332
|
+
nonlocal status
|
|
333
|
+
if status is None:
|
|
334
|
+
status = self.console.status(progress, spinner="dots")
|
|
335
|
+
status.start()
|
|
307
336
|
|
|
308
337
|
def stop_spinner() -> None:
|
|
309
|
-
nonlocal
|
|
310
|
-
if
|
|
338
|
+
nonlocal status
|
|
339
|
+
if status is not None:
|
|
311
340
|
status.stop()
|
|
312
|
-
|
|
341
|
+
status = None
|
|
313
342
|
|
|
343
|
+
start_spinner()
|
|
314
344
|
try:
|
|
315
345
|
for chunk in self.chat(self.messages, self.tool_schemas(), self.settings.think):
|
|
316
346
|
msg = chunk.get("message", {})
|
|
317
347
|
if msg.get("thinking"):
|
|
318
348
|
thinking += msg["thinking"]
|
|
349
|
+
progress.update("Thinking", msg["thinking"])
|
|
319
350
|
if self.settings.show_thinking:
|
|
320
|
-
stop_spinner()
|
|
351
|
+
stop_spinner() # raw text is printed as it streams
|
|
321
352
|
self.console.print(Text(msg["thinking"], style="dim italic"), end="")
|
|
322
353
|
printed_thinking = True
|
|
323
|
-
else:
|
|
324
|
-
status.update(
|
|
325
|
-
f"[cyan]Thinking…[/] [dim]({len(thinking) // 4} tokens, {time.time() - t0:.0f}s)[/]"
|
|
326
|
-
)
|
|
327
354
|
if msg.get("content"):
|
|
328
|
-
stop_spinner()
|
|
329
355
|
if printed_thinking:
|
|
330
356
|
self.console.print("\n")
|
|
331
357
|
printed_thinking = False
|
|
332
358
|
content += msg["content"]
|
|
359
|
+
progress.update("Writing", msg["content"])
|
|
360
|
+
# Finished markdown blocks print above the spinner, which keeps running meanwhile.
|
|
361
|
+
start_spinner()
|
|
333
362
|
md.feed(msg["content"])
|
|
334
363
|
if msg.get("tool_calls"):
|
|
335
364
|
tool_calls.extend(msg["tool_calls"])
|
lcode/permissions.py
CHANGED
|
@@ -71,7 +71,7 @@ class Permissions:
|
|
|
71
71
|
if self.mode == "yolo" or key in self.always or (kind == "edit" and self.mode == "auto-edit"):
|
|
72
72
|
return True, ""
|
|
73
73
|
self.console.print(Panel(body, title=title, title_align="left", border_style="yellow"))
|
|
74
|
-
scope =
|
|
74
|
+
scope = "file edits" if kind == "edit" else key.split(":", 1)[-1]
|
|
75
75
|
try:
|
|
76
76
|
answer = input(f" Allow? [y]es / [a]lways for '{scope}' this session / [n]o (+ optional reason): ")
|
|
77
77
|
except EOFError:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: lcode-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A local-first terminal coding agent powered by open-weight models on your own GPU or Mac, via Ollama.
|
|
5
5
|
Project-URL: Homepage, https://nasser1941.github.io/lcode/
|
|
6
6
|
Project-URL: Documentation, https://nasser1941.github.io/lcode/
|
|
@@ -150,8 +150,8 @@ What `lcode setup` picks for common machines:
|
|
|
150
150
|
| NVIDIA 8–24 GB + 32 GB RAM | qwen3.6-35b | 256K |
|
|
151
151
|
| NVIDIA 8 GB + 16 GB RAM | gpt-oss-20b | 64K |
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
At 256K context the default model generates 50–55 tokens/s on an RTX 4080 Laptop GPU (12 GB) and
|
|
154
|
+
45 tokens/s on an M4 Pro Mac with 48 GB. See
|
|
155
155
|
[Models & context windows](https://nasser1941.github.io/lcode/models/) for memory estimates and
|
|
156
156
|
tuning.
|
|
157
157
|
|
|
@@ -165,11 +165,13 @@ fills up. Details: [How it works](https://nasser1941.github.io/lcode/how-it-work
|
|
|
165
165
|
|
|
166
166
|
## Roadmap
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
168
|
+
See the pinned [Roadmap issue](https://github.com/nasser1941/lcode/issues/10). Next up:
|
|
169
|
+
|
|
170
|
+
- [`/undo` and automatic checkpoints](https://github.com/nasser1941/lcode/issues/4)
|
|
171
|
+
- [`lcode bench`: score models on your own hardware](https://github.com/nasser1941/lcode/issues/5)
|
|
172
|
+
- [MCP tool servers](https://github.com/nasser1941/lcode/issues/6)
|
|
173
|
+
- [Homebrew formula](https://github.com/nasser1941/lcode/issues/8) and an [optional sandbox for shell commands](https://github.com/nasser1941/lcode/issues/9)
|
|
174
|
+
- More models in the catalog, and test reports from your hardware ([report one](https://github.com/nasser1941/lcode/issues/new?template=model_request.yml))
|
|
173
175
|
|
|
174
176
|
## Contributing
|
|
175
177
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
lcode/__init__.py,sha256=
|
|
1
|
+
lcode/__init__.py,sha256=PdAx_iEnY0j7EGqyHzgBnDk0Mpcxf6si9jhyrgNcTcU,117
|
|
2
2
|
lcode/__main__.py,sha256=s5N5pryX2FAu93GGZ3FxUrIXHWGzxz66lKUoaV_h8Ws,35
|
|
3
|
-
lcode/agent.py,sha256=
|
|
3
|
+
lcode/agent.py,sha256=ppO9sMxymIVvFPeHS-5hqTyryQYBOK_0OAucaX6SeWU,24535
|
|
4
4
|
lcode/catalog.py,sha256=pZ1azX9OH2r4JE2c6E5OfwywpgS39-W2oXzoCHJhmtM,4141
|
|
5
5
|
lcode/cli.py,sha256=bvKv_5SGJgy8bkY-zFpXwyNACiGa6nu_n16ems_Vmq8,20103
|
|
6
6
|
lcode/config.py,sha256=ZyddgIZ9TaHAcJSW_A8o7qFQbp1s2RHmsbFzOzEjV4o,5894
|
|
@@ -8,14 +8,14 @@ lcode/hardware.py,sha256=2VbknZCxTZJwJUD8YWdQ7ryEQo_LcjtoOBOHOU9Y0fw,3085
|
|
|
8
8
|
lcode/limits.py,sha256=oJzVQoMyTZ0_Fkpu5t3qxrf9v7Hv9S8U8BRQR5M1aKA,1237
|
|
9
9
|
lcode/models.toml,sha256=lt2G64zzo_Ryxv2ISRSNZoVD_JqCqYqsM01n8WoXGs8,4098
|
|
10
10
|
lcode/ollama.py,sha256=fELwqw7xqLH9TM7XvdI1pxqYZFeIRJBv-VSz9REdvwA,6516
|
|
11
|
-
lcode/permissions.py,sha256=
|
|
11
|
+
lcode/permissions.py,sha256=wJKwhJQx44AzjOFZCWwM4M2oUlPFUNYI2cIVfoBGGgc,3898
|
|
12
12
|
lcode/render.py,sha256=gMvf4W3qkLUJ4Vot3az_hP_zRDAItJC9pP-68dHFkfA,1375
|
|
13
13
|
lcode/repl.py,sha256=xmeX00g17OyznlGT2H8l-qIT68fPfm1EqhX_3uBHvew,16380
|
|
14
14
|
lcode/sessions.py,sha256=wOR-J0EHgJxLSPboKPHnvBcp5VTUYTj3tZWgpsa6-K4,4013
|
|
15
15
|
lcode/tools.py,sha256=97wPiabQuztMr4qsMgf_beNAFfraxSlU2195QJffFkg,25835
|
|
16
16
|
lcode/web.py,sha256=vyxKbSRJTl1Lj3hj7Yd_NwTgbv0Xi6dup2YfUXjqp-Y,14579
|
|
17
|
-
lcode_cli-0.2.
|
|
18
|
-
lcode_cli-0.2.
|
|
19
|
-
lcode_cli-0.2.
|
|
20
|
-
lcode_cli-0.2.
|
|
21
|
-
lcode_cli-0.2.
|
|
17
|
+
lcode_cli-0.2.1.dist-info/METADATA,sha256=oeqRZXgcWJO4mYZihixz_xmwaF8VDWViVaqG6LSzV9A,9203
|
|
18
|
+
lcode_cli-0.2.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
19
|
+
lcode_cli-0.2.1.dist-info/entry_points.txt,sha256=pZ_uDQ98UzVAHUQeXn5HeyQWKGSH3r3T5aPHuSB89eI,41
|
|
20
|
+
lcode_cli-0.2.1.dist-info/licenses/LICENSE,sha256=JWjmoL7nNIC1XUa-dZm2hwLUgjUQMFo6k8APb0idNzs,1074
|
|
21
|
+
lcode_cli-0.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|