localcode 0.3.2__py3-none-any.whl → 0.3.4__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.
localcode/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  __all__ = ["__version__"]
2
2
 
3
- __version__ = "0.3.2"
3
+ __version__ = "0.3.4"
@@ -246,10 +246,9 @@ def _semantic_tool_summary(content: str) -> str:
246
246
  )
247
247
  if is_error:
248
248
  # Self-conditioning (arXiv:2509.09677): echoing an OLD error's text
249
- # pushes the model to repeat the mistake (scale-independent). Recent
250
- # errors stay intact (kept above by keep_recent) so it can fix the
251
- # immediate problem; older ones drop to a neutral note. The progress
252
- # ledger still records the failure fact, minus the conditioning text.
249
+ # pushes the model to repeat the mistake. Recent errors stay intact
250
+ # (kept above by keep_recent) so it can fix the immediate problem;
251
+ # older ones drop to a neutral note (the ledger still records the fact).
253
252
  return "[an earlier tool call errored and was handled — details dropped]"
254
253
  if facts:
255
254
  return (
@@ -725,9 +724,10 @@ def build_progress_ledger(
725
724
  return list(dict.fromkeys(s for s in seq if s))
726
725
 
727
726
  lines = [
728
- "## Work already done this task build on it. Do NOT re-read a file or "
729
- "re-run a command listed below unless you have CHANGED it since; you "
730
- "already have the result.",
727
+ "## A log of YOUR OWN tool calls so far this turn — NOT the user's work "
728
+ "and NOT pre-existing files on disk (leftover files you did not create are "
729
+ "not your progress or your task). Don't re-read/re-run anything below "
730
+ "unless you changed it; keep making NEW progress toward the user's goal.",
731
731
  ]
732
732
  rd = _uniq(files_read)
733
733
  if rd:
@@ -365,34 +365,36 @@ def churn_nudge_for(signal: ChurnSignal) -> str:
365
365
  Names the concrete subject (file path / command) + count so the model
366
366
  sees what it's thrashing on, then tells it the ONE thing to do instead.
367
367
  """
368
+ # NOTE: these are FORWARD-ONLY imperatives. They deliberately contain NO
369
+ # self-referential loop language ("you're going in circles", "you've
370
+ # rewritten X N times", "thrashing", "stop planning"). A small model
371
+ # parrots whatever we inject: injecting "you're going in circles" as a
372
+ # user-role message makes it reply "You're right, I've been going in
373
+ # circles" — which then sits in its own context and self-conditions the
374
+ # loop deeper (arXiv:2509.09677). So we tell it the NEXT concrete action
375
+ # and nothing about the failure it just had.
368
376
  if signal.mode is ChurnMode.FILE_REWRITE:
369
377
  return (
370
- f"SYSTEM: You've rewritten {signal.subject} {signal.count} times "
371
- "this turn. Stop rewriting it. Read the ACTUAL error output from "
372
- "the last failure, identify the specific line/cause, and make ONE "
373
- "targeted edit_file change to fix that — do not overwrite the whole "
374
- "file again."
378
+ f"SYSTEM: {signal.subject} is written — move on to the next file. "
379
+ "If it has a specific error, make ONE targeted edit_file change to "
380
+ "the exact line; otherwise create the next file the task needs."
375
381
  )
376
382
  if signal.mode is ChurnMode.COMMAND_FAILURE:
377
383
  return (
378
- f"SYSTEM: `{signal.subject}` has failed {signal.count} times this "
379
- "turn. Re-running it will not help. Read its error output line by "
380
- "line, fix the ROOT CAUSE (a missing dependency, a syntax error in "
381
- "a config/source file, a wrong path), and only then run it again."
384
+ f"SYSTEM: `{signal.subject}` won't succeed as-is. Read its error "
385
+ "output, fix the root cause (missing dependency, a syntax error in a "
386
+ "config/source file, a wrong path) with an edit, then run it once."
382
387
  )
383
388
  if signal.mode is ChurnMode.PLANNING_SPIN:
384
389
  return (
385
- "SYSTEM: You've spent several rounds planning and re-deriving the "
386
- "same approach without changing a single file or running a build. "
387
- "You've planned enough. Take ONE concrete action NOW: create or "
388
- "edit the most important file for the next step, then build/run to "
389
- "verify it. Do not restate the plan — execute the first step of it."
390
+ "SYSTEM: Take the next concrete action now create or edit the next "
391
+ "file the task needs, then run the build to verify. Write code, not "
392
+ "a plan."
390
393
  )
391
394
  # INVESTIGATION_SPIN
392
395
  return (
393
- "SYSTEM: You've spent several rounds reading and searching files "
394
- "without taking any concrete action. You're investigating in circles. "
395
- "Take a concrete action NOW: make a specific edit, run the build/tests, "
396
- "or — if you genuinely lack information only the user has ask ONE "
397
- "focused question. Do not read or grep more files this round."
396
+ "SYSTEM: Take a concrete action now create the next file the task "
397
+ "needs and write real code with write_file/edit_file. Files already on "
398
+ "disk that you did not create are not part of your task. If you truly "
399
+ "lack information only the user has, ask ONE focused question."
398
400
  )
localcode/runtime.py CHANGED
@@ -168,6 +168,21 @@ def _log_disconnect_context(stage: str, error: Any, *, mid_stream: bool = False)
168
168
  diag["stage"] = stage
169
169
  diag["mid_stream"] = mid_stream
170
170
  diag["error"] = err_str[:500]
171
+ # Capture the SERVER's own last words. llama-server prints the real cause
172
+ # (ggml/Metal allocation failure, GGML_ASSERT, OOM) to server.log right
173
+ # before it dies; without this we only see httpx's "connection dropped"
174
+ # and are left guessing. The tail is the actual diagnosis.
175
+ _server_tail = ""
176
+ try:
177
+ from .paths import global_state_dir
178
+ _lp = global_state_dir() / "server.log"
179
+ if _lp.exists():
180
+ _lines = _lp.read_text(errors="replace").splitlines()
181
+ _server_tail = "\n".join(_lines[-15:])[-1500:]
182
+ except Exception:
183
+ _server_tail = ""
184
+ if _server_tail:
185
+ diag["server_log_tail"] = _server_tail
171
186
  try:
172
187
  from .server_manager import _lifecycle_log as _ll
173
188
  _ll("server_disconnect", **diag)
@@ -186,6 +201,8 @@ def _log_disconnect_context(stage: str, error: Any, *, mid_stream: bool = False)
186
201
  f"pressure_kill={diag.get('pressure_kill')} "
187
202
  f"running={diag.get('running')} mid_stream={mid_stream} "
188
203
  f"free_mb={diag.get('free_mb')} error={err_str[:300]}\n"
204
+ + (f" server.log tail:\n "
205
+ + _server_tail.replace("\n", "\n ") + "\n" if _server_tail else "")
189
206
  )
190
207
  except Exception:
191
208
  pass
@@ -2064,6 +2081,25 @@ class LocalCodeRuntimeGateway(_DiffusionMixin):
2064
2081
  # revert this hunk and accept occasional rephrase as a
2065
2082
  # model property of Qwen3.6 IQ2_M.
2066
2083
  payload["repeat_penalty"] = 1.05
2084
+ # Forward the EOS-NEUTRAL anti-loop samplers that were previously
2085
+ # computed into `opts` and then DROPPED (they only went to the dead
2086
+ # ollama-shaped payload below). DRY penalises repeated N-GRAMS —
2087
+ # not single tokens — so unlike repeat_penalty it does NOT suppress
2088
+ # the EOS/sentence-end tokens that caused the 2026-04-29 paraphrase
2089
+ # regression; min_p trims the low-probability tail that token-
2090
+ # collapse loops feed on. llama-server has DRY OFF by default, so
2091
+ # NOT forwarding these is a real root cause of the repeat-collapse
2092
+ # loops. We deliberately do NOT re-add the aggressive repeat_penalty
2093
+ # /top_k/top_p bundle (that was the regression). A/B back to
2094
+ # temperature-only with LOCALCODE_SAMPLER_MINIMAL=1.
2095
+ import os as _os
2096
+ if _os.environ.get("LOCALCODE_SAMPLER_MINIMAL") != "1":
2097
+ payload["dry_multiplier"] = opts["dry_multiplier"]
2098
+ payload["dry_base"] = opts["dry_base"]
2099
+ payload["dry_allowed_length"] = opts["dry_allowed_length"]
2100
+ payload["dry_penalty_last_n"] = opts["dry_penalty_last_n"]
2101
+ if opts.get("min_p", 0):
2102
+ payload["min_p"] = opts["min_p"]
2067
2103
  if "num_predict" in opts:
2068
2104
  _np = opts["num_predict"]
2069
2105
  # llama-server treats max_tokens=-1 as "use default", which on
@@ -2080,8 +2116,11 @@ class LocalCodeRuntimeGateway(_DiffusionMixin):
2080
2116
  # outbound chat-completions payload so we can verify DRY is
2081
2117
  # actually being forwarded. Set LOCALCODE_DEBUG_SAMPLERS=1
2082
2118
  # to enable. Self-disables after one log to keep noise down.
2083
- import os, logging
2084
- if os.environ.get("LOCALCODE_DEBUG_SAMPLERS") == "1" and not getattr(self, "_logged_samplers", False):
2119
+ import logging
2120
+ # Always log the ACTUAL forwarded samplers once per session (was
2121
+ # opt-in) so we can correlate looping with the real server config —
2122
+ # the "control this better" record. Cheap: fires exactly once.
2123
+ if not getattr(self, "_logged_samplers", False):
2085
2124
  sampler_keys = (
2086
2125
  "temperature", "top_p", "top_k", "min_p",
2087
2126
  "repeat_penalty", "repeat_last_n",
@@ -71,12 +71,9 @@ from .paths import (
71
71
  lifecycle_log_path,
72
72
  )
73
73
 
74
- # PID file stays GLOBAL — only one llama-server runs on the machine
75
- # (single port, single GPU memory budget). When you `cd` between
76
- # projects mid-session, the running server stays serving you. The
77
- # per-project lifecycle log records a fresh `server_started` entry
78
- # on next launch from the new project. See paths.py for the full
79
- # global-vs-project split rationale.
74
+ # PID file stays GLOBAL — only one llama-server runs per machine (single port,
75
+ # single GPU budget), so it keeps serving you when you `cd` between projects.
76
+ # See paths.py for the global-vs-project split rationale.
80
77
  PID_FILE = server_pid_file()
81
78
  DEFAULT_PORT = 8081
82
79
  # Preferred-range scan (8081-8099) before falling through to an OS-assigned
@@ -177,13 +174,9 @@ def _rewrite_port_arg(cmd: list[str], port: int) -> list[str]:
177
174
 
178
175
  STUCK_SERVER_MARKER = stuck_server_marker_path()
179
176
 
180
- # Persistent (append-only) lifecycle log for every server start, stop,
181
- # pressure-kill, and recovery event. Distinct from the per-session
182
- # `~/.local/share/localcode/server.log` (which the setup screen
183
- # overwrites with `open(..., "w")` on every launch and so loses
184
- # inter-session context). Per-project (lives at
185
- # `<project_root>/.localcode/lifecycle.log`) so each project has its
186
- # own diagnostic timeline.
177
+ # Persistent (append-only) lifecycle log for every server start/stop/kill/
178
+ # recovery event. Per-project (`<project_root>/.localcode/lifecycle.log`) so
179
+ # each project keeps its own diagnostic timeline across sessions.
187
180
  LIFECYCLE_LOG = lifecycle_log_path()
188
181
 
189
182
 
@@ -400,12 +393,11 @@ class ServerManager:
400
393
  had_prior = self._process is not None
401
394
  _prev_model = self._model_path # capture before shutdown clears it
402
395
  self._shutdown_locked()
403
- # Wait for the OLD server's memory to actually release before
404
- # spawning the new one: the kernel may take 1-3 s to free wired
405
- # Metal memory after the child reaps, and spawning the new ~10 GB
406
- # server in that window double-commits and trips the pressure
407
- # monitor on a 16 GB Mac. Best-effort targets ~11 GB free, times
408
- # out at 8 s, and proceeds anyway (refusing would be worse).
396
+ # Wait for the OLD server's memory to release before spawning the
397
+ # new one: the kernel takes 1-3 s to free wired Metal memory after
398
+ # the child reaps, and spawning into that window double-commits and
399
+ # trips the pressure monitor on a 16 GB Mac. Best-effort — targets
400
+ # ~11 GB free, times out at 8 s, proceeds anyway.
409
401
  if had_prior:
410
402
  # Only a genuine model change is "model_switch"; a same-model
411
403
  # relaunch (crash/disconnect recovery) is "server_restart".
@@ -430,20 +422,35 @@ class ServerManager:
430
422
  # AGX driver may read the env at first Metal touch — setting it in
431
423
  # the parent env before spawn is the belt-and-suspenders approach.
432
424
  env.setdefault("AGX_RELAX_CDM_CTXSTORE_TIMEOUT", "1")
425
+ # Capture the server's OWN stdout/stderr to server.log (this runtime
426
+ # path used to send both to /dev/null, discarding llama-server's
427
+ # final ggml/Metal error on the recurring -9). Append per-spawn.
428
+ log_fh = None
429
+ try:
430
+ from .paths import global_state_dir
431
+ log_fh = open(global_state_dir() / "server.log", "a", buffering=1)
432
+ log_fh.write(f"\n===== llama-server spawn (port {port}) =====\n")
433
+ except Exception:
434
+ log_fh = None
435
+ try: # close any prior spawn's handle before replacing it
436
+ if getattr(self, "_log_fh", None) is not None:
437
+ self._log_fh.close()
438
+ except Exception:
439
+ pass
440
+ self._log_fh = log_fh
433
441
  self._process = subprocess.Popen(
434
442
  cmd,
435
- stdout=subprocess.DEVNULL,
436
- stderr=subprocess.DEVNULL,
443
+ stdout=(log_fh or subprocess.DEVNULL),
444
+ stderr=subprocess.STDOUT if log_fh else subprocess.DEVNULL,
437
445
  start_new_session=True, # process group leader → killpg works
438
446
  env=env,
439
447
  )
440
448
  self._model_path = model_path
441
449
  self._port = port
442
450
 
443
- # Layer 1: kernel-enforced memory ceiling via jetsam. If
444
- # llama-server tries to wire more than the budget allows,
445
- # macOS kernel kills it BEFORE the vm_fault wait-chain can
446
- # form. This is the hard backstop — sanctioned Apple API.
451
+ # Layer 1: kernel-enforced memory ceiling via jetsam — the hard
452
+ # backstop (sanctioned Apple API) that kills llama-server before it
453
+ # can push the system over the edge.
447
454
  try:
448
455
  from .memory_guard import (
449
456
  set_jetsam_highwater, recommended_jetsam_limit_mb,
@@ -467,14 +474,10 @@ class ServerManager:
467
474
  except Exception:
468
475
  self._pressure_thread = None
469
476
  self._write_pid_file(self._process.pid)
470
- # Log the FULL command we launched with so future debugging
471
- # can answer "what flags was the server using during this
472
- # session?" needed because subtle flags like
473
- # `--lookup-cache-dynamic` or `--spec-type ngram-mod` cause
474
- # repetition pathologies that look like model bugs but are
475
- # actually decode-time speculative-cache feedback loops.
476
- # Truncated to 4000 chars to keep the event small; that's
477
- # enough for ~80 args.
477
+ # Log the FULL launch command so debugging can answer "what flags
478
+ # was the server using?" subtle flags (e.g. --spec-type ngram-mod)
479
+ # cause repetition pathologies that look like model bugs. Truncated
480
+ # to 4000 chars (~80 args) to keep the event small.
478
481
  _flags_str = " ".join(str(c) for c in cmd)[:4000]
479
482
  _lifecycle_log("server_started", pid=self._process.pid, port=port,
480
483
  model=Path(model_path).name,
localcode/tools/bash.py CHANGED
@@ -711,6 +711,50 @@ def _normalize_repo_root_variants(cmd: str, repo: str) -> str:
711
711
  return pattern.sub(repo, cmd)
712
712
 
713
713
 
714
+ def _quoting_error_hint(output: str) -> str:
715
+ """Turn a raw shell quoting failure into actionable guidance.
716
+
717
+ A path with spaces or parentheses (e.g. `Qwen 3.6 35B-A3B (Q8)`) that
718
+ isn't quoted breaks bash with `syntax error near unexpected token` — an
719
+ error a model can't act on, so it retries the same broken shape in a loop
720
+ (observed exactly this). Tell it precisely how to fix it.
721
+ """
722
+ low = output.lower()
723
+ if "syntax error near unexpected token" not in low and "unexpected eof" not in low:
724
+ return ""
725
+ return (
726
+ "HINT: a path or argument with spaces or parentheses wasn't quoted, so "
727
+ "the shell mis-parsed it. Wrap paths in SINGLE quotes — e.g. "
728
+ "ls -la '/Users/you/My Dir (v2)'. Simpler and safer: use list_files or "
729
+ "read_file with the path as an argument — they take the raw path and "
730
+ "never need shell escaping."
731
+ )
732
+
733
+
734
+ def _redirect_shell_dir_listing(cmd: str) -> str:
735
+ """Route `ls <path>` to list_files, which can't break on spaces/parens.
736
+
737
+ Mirrors the `cat → read_file` guard. Only fires for a plain `ls` with a
738
+ PATH argument (no pipes/redirs/chains) — bare `ls`/`ls -la` in the cwd
739
+ can't hit the quoting failure, so it's left to run.
740
+ """
741
+ _cd, body = _extract_leading_cd(cmd)
742
+ b = body.strip()
743
+ if any(sep in b for sep in ("|", ">", "<", "&&", "||", ";", "$(", "`")):
744
+ return ""
745
+ # Path arg must NOT start with '-' (else `ls -la` reads its own flags as a path).
746
+ m = re.match(r"^ls((?:\s+-[a-zA-Z]+)*)\s+([^-\s].*)$", b)
747
+ if m is None:
748
+ return "" # bare `ls`/`ls -la` (no path) — harmless, let it run
749
+ path = m.group(2).strip().strip('"').strip("'")
750
+ return (
751
+ f"REJECTED: use list_files(path='{path}') to inspect a directory, not "
752
+ "bash `ls <path>`. list_files takes the path as an argument, so it never "
753
+ "breaks on spaces or parentheses in a folder name — which is what fails "
754
+ "with bash here."
755
+ )
756
+
757
+
714
758
  def execute(ctx: ToolContext, args: dict) -> str:
715
759
  cmd = _normalize_repo_root_variants(args["command"], str(ctx.repo))
716
760
  # Block ANY use of process-attaching debuggers — lldb / dtrace /
@@ -776,6 +820,9 @@ def execute(ctx: ToolContext, args: dict) -> str:
776
820
  file_read_redirect = _redirect_shell_file_read(cmd, str(repo))
777
821
  if file_read_redirect:
778
822
  return file_read_redirect
823
+ dir_listing_redirect = _redirect_shell_dir_listing(cmd)
824
+ if dir_listing_redirect:
825
+ return dir_listing_redirect
779
826
  file_write_redirect = _redirect_shell_file_write(cmd, str(repo))
780
827
  if file_write_redirect:
781
828
  return file_write_redirect
@@ -969,6 +1016,11 @@ def execute(ctx: ToolContext, args: dict) -> str:
969
1016
  pass
970
1017
  if r.returncode != 0:
971
1018
  output = f"[exit code {r.returncode}]\n{output}"
1019
+ # Shell quoting failure (unquoted spaces/parens in a path) → give
1020
+ # the model an actionable fix instead of a raw syntax error it loops on.
1021
+ _qh = _quoting_error_hint(output)
1022
+ if _qh:
1023
+ output = _qh + "\n\n" + output
972
1024
  # AirPlay-collision detector: if the model curls localhost:5000
973
1025
  # or :7000, those are squatted by macOS AirPlay Receiver /
974
1026
  # AirTunes — bare `curl -s` sees a 200 with empty body and
@@ -411,12 +411,16 @@ class _ChatTextArea(TextArea):
411
411
  & .text-area--cursor-line {
412
412
  background: ansi_default;
413
413
  }
414
- /* The caret. `reverse` swaps the cell's fg/bg using the terminal's
415
- OWN colors, so the cursor is a light block on a dark terminal and
416
- a dark block on a light one no hardcoded hex that fights the
417
- ansi_default palette (which rendered as a near-black block). */
414
+ /* The caret. textual-ansi reports dark=False, so TextArea's own
415
+ `&:light .text-area--cursor` rule (background: $foreground 70%) wins
416
+ over a plain override and renders a dark/black block. Force it with
417
+ !important: color+background = ansi_default (terminal fg/bg) plus
418
+ `reverse` swaps them, giving a light block on a dark terminal and a
419
+ dark block on a light one — terminal-adaptive, no hardcoded hex. */
418
420
  & .text-area--cursor {
419
- text-style: reverse;
421
+ color: ansi_default !important;
422
+ background: ansi_default !important;
423
+ text-style: reverse !important;
420
424
  }
421
425
  &:focus {
422
426
  background-tint: transparent 0%;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: localcode
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: High-performance AI coding on consumer hardware.
5
5
  Author: LocalCode contributors
6
6
  License-Expression: Apache-2.0
@@ -1,4 +1,4 @@
1
- localcode/__init__.py,sha256=7CJ7c6EVsH8bTBDIvPtzt8awFwxSSTwMCdNZ31RVGFc,49
1
+ localcode/__init__.py,sha256=W-WadG5QMLaRAlMnY-zGSUOIpCiTp4nDOBUifILPLVU,49
2
2
  localcode/__main__.py,sha256=_4Tblte9F1KOpB-oGBQuTlaSN-IncLmhihqhBkpH3pU,69
3
3
  localcode/_subproc_env.py,sha256=eltq_kKvqGj22pIFivB5BZft9-s5fziBW0lsV9FhEV4,2383
4
4
  localcode/app.py,sha256=FSwpce6Jfn7zJmhL1ipaqTq0xKh0MxCMnEnMQuFV8L8,67820
@@ -46,9 +46,9 @@ localcode/plans.py,sha256=Tih7WzRmkWO0cF4fEDwjI07978mJZ_l9jXTSTidLb9k,7424
46
46
  localcode/process_registry.py,sha256=KBd5ndChtbmIEIFSkGTmAtaLEIIAzc8DhuYX692q73Y,5165
47
47
  localcode/recommendations.py,sha256=HVh8u2L3xbwc2qxH7Syec2cEtlEoFV9n1c33SqWF3ng,3184
48
48
  localcode/recovery.py,sha256=QUSsBA9IS5AJ6W4m4nIv2RaktTO9TJeL0RcDeUSWHJk,5237
49
- localcode/runtime.py,sha256=7BWAf-PaUvP0SUBFzxLB4edb6FcLOZay_sBafAPLDIk,113508
49
+ localcode/runtime.py,sha256=UifjZAe8kPgnMCk4N2V2eX4VY6Us6IYmDwbvVwMhwC0,115816
50
50
  localcode/runtime_diffusion.py,sha256=bVopikSdQeaV9mAuzWYoXtoXpUb7U0yMXU5VFTpbsBc,39741
51
- localcode/server_manager.py,sha256=DLXnijvGfWbW3tGNaj0sEWIQVJ0N7Jadl_YIWc4W1RE,34339
51
+ localcode/server_manager.py,sha256=HV_1VQxJrN_Vvjrttn9Nr9OQHYfDNmWoCzZO8JdFIp8,34488
52
52
  localcode/session.py,sha256=taEyOOiCcNXEr8oOeszyWvzvn-oAZ7knXQ7BxRew08Q,11970
53
53
  localcode/shell.py,sha256=2QcXk0NB6d8t2gpbbKOB5nGZ_xBJ7VVsLtF4hnUknVM,2300
54
54
  localcode/skills.py,sha256=TGdy63dQTlfa6U2l7wjg3zSatKyLAYdu_wVO-J2BrHY,25800
@@ -67,14 +67,14 @@ localcode/voice.py,sha256=ZAh1GmJiQJF4cXQ2YCclJZB77Sa9yflox22MnDMl7t8,39178
67
67
  localcode/agent/__init__.py,sha256=-aQIBiYIojvwl9nDf_wcyiT5UQTmcEQbEBwnBEsrjEU,7349
68
68
  localcode/agent/app_tasks.py,sha256=e5tcxBZczgQyq5irGePfe5fPmNAK6AUJ_N2i11iXb5Q,5725
69
69
  localcode/agent/constants.py,sha256=0zlgyqZYJs0MOoh70rOgrXfl_ZuU5kR29Sjzrrz7TlI,12350
70
- localcode/agent/context.py,sha256=nXwOhBN5phQ8yxVPrIaju8VDngf2BmdejpvtBP8e88c,37559
70
+ localcode/agent/context.py,sha256=adV_WlFBHMDHsWdndRgrkZqRweDD9plyqMP_2X74WAw,37617
71
71
  localcode/agent/goal.py,sha256=X87Q3rsdp70wOfkpbh35M4qFWSNyklX3RuRWjv-pCXs,6264
72
72
  localcode/agent/helpers.py,sha256=vutFyCtnca0UnF0wgk7LdsqVWdiqyYm4YnFOxtnCJ_A,15004
73
73
  localcode/agent/hooks.py,sha256=XadEbSM7gHtKlLrqSKnvcQFcoWoNUXdlMdIPS-EwBGA,15841
74
74
  localcode/agent/loop.py,sha256=ZX3VS_7jCcRLX3VCSOsl8uMF51AnPICt18awXGk928s,99869
75
75
  localcode/agent/prompt_context.py,sha256=abJPLMhTzuVcsW4AfMBIW-SEqmrU69DhI-0vGZllZVU,13707
76
76
  localcode/agent/prompts.py,sha256=1VFrtzumZ2A7BaZ1gStpre-mZBs721FZi9hiB4qenZw,14639
77
- localcode/agent/recovery.py,sha256=8wwPeESsIHtdsSb_eU95JJvlVJrHxeoTVAofxmm3UIs,16639
77
+ localcode/agent/recovery.py,sha256=oCghDm9BdHhvl98Kx8EikNuxSk4Geh-0cgAEhTpH-sw,16738
78
78
  localcode/agent/sections.py,sha256=9hnwaNritQHgn7qIGFBZ3ChZgMQXQcqtmpdbR1wafA0,8998
79
79
  localcode/agent/streaming.py,sha256=TEcKbmKfDGV1razrV-ceVrvIIxvS-GmLWGNDnF0gSTA,12872
80
80
  localcode/agent/tool_execution.py,sha256=yfWZwOO3f5ICd2eqw6keg8sOKOIa3vFt4peFinmu5nM,14462
@@ -99,7 +99,7 @@ localcode/tools/__init__.py,sha256=6FknaKvS-0mo4dYFTegcJcyihcJpH42NzQnM-FkKCPk,1
99
99
  localcode/tools/agent.py,sha256=aIcoGc3JiiSN4MSumwNc9TiKPZciyNXWKTYjFNir79s,8786
100
100
  localcode/tools/append_file.py,sha256=GNjPBnnV7hpD32u0Jx6YxRmsOSr4uwvJgIcInH1qhSc,2015
101
101
  localcode/tools/base.py,sha256=2QV-q1DzXWM4KL9mFKNPzlBiLOS2uKLXhlgGyeWEKCI,2064
102
- localcode/tools/bash.py,sha256=20xxJAmqSmtkgTbvm_v9qyyK-7mcy_LB28EVLjsuyvg,40970
102
+ localcode/tools/bash.py,sha256=Yuuxo0lsX0JuFbHQX8OflmgZgq7uqBc1FUSGJRHlsB8,43380
103
103
  localcode/tools/edit_diff.py,sha256=eJ1FyK3HecAmJNiM-XdHOQ7WCkyOuD6hwAlTVgYQGGk,3017
104
104
  localcode/tools/edit_file.py,sha256=DhhQSi6wnD4_IwLEYoC-zBLUPaxhihZxBatNoerFLe4,13455
105
105
  localcode/tools/facts.py,sha256=OqTLYOclKy6lpPQiU3aDrzY-Ho-jhWc8mvzLx6TFRCk,5576
@@ -119,7 +119,7 @@ localcode/tui/__init__.py,sha256=Yfy9bvw3l1iY4FDhRX8qZVgnZLuYiq8lc9SDYl4cWwc,61
119
119
  localcode/tui/app.py,sha256=KjQzpGKnxkgh-0kVc3zcb63Q6a5LBuGreBqTUrasiqM,17700
120
120
  localcode/tui/bridge.py,sha256=urPPCqC_nisAYawWk3juvMI8IaoJfv4g7USpGOZOqQc,2803
121
121
  localcode/tui/screens/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
- localcode/tui/screens/chat.py,sha256=aTYu03GTAlbXFuZDZaZBIvGNE4we--nRHZw5OYRgvAw,193390
122
+ localcode/tui/screens/chat.py,sha256=3eRHyiFNec7cbWEcClEEopzCDt3_SGP9lIkNH5yuSCc,193659
123
123
  localcode/tui/screens/mode_picker.py,sha256=cK_xNo7ytOwJXJwzUevMTcb5dwQKtjMfh60of-ntRz4,2347
124
124
  localcode/tui/screens/model_picker.py,sha256=VkhyREIT2W1J8A6tUe0ix5TbWXUa2XSe9qKzcXgjjz4,35991
125
125
  localcode/tui/screens/setup.py,sha256=6sSKU1KagLyf5DtkQWOSBA7inPFZMTF570t-20PIwaE,40844
@@ -131,9 +131,9 @@ localcode/tui/widgets/chat_log.py,sha256=D5DUkrQXL_ilxyC8N1xIPLw4Pqeh0jSWt1mPgho
131
131
  localcode/tui/widgets/voice_visualizer.py,sha256=zOhrIxaMcg-F2Y8LvHJTddq227phmiTjtEyRbkx1XxU,4709
132
132
  localcode/tui/widgets/messages/__init__.py,sha256=952AZ1qVMGUIqPIry7mwxnYbMkgPlcY5FAwZtEa5YPg,967
133
133
  localcode/tui/widgets/messages/diff.py,sha256=khDf_MThrQz0S62-t-m7i3OU5cdNolUk1TCeKMRPDQI,10910
134
- localcode-0.3.2.dist-info/licenses/LICENSE,sha256=Ahg0cteZ-6bIRB7KC15WEtdqWnzy8HY_yzsZBoVRql0,11294
135
- localcode-0.3.2.dist-info/METADATA,sha256=NfiKTcmNdZeRHzlMdJd6Qm5lTlLT9W2ZPWEnMYsZBKU,7813
136
- localcode-0.3.2.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
137
- localcode-0.3.2.dist-info/entry_points.txt,sha256=rE7elTGUvMsBd-Quy22cuTVJ-zGVOmx0nEuC6ni9Tg4,87
138
- localcode-0.3.2.dist-info/top_level.txt,sha256=58eL3Rw8v0OGmYNxjx8uS4ERWatrzUbyCL5inlukkTo,10
139
- localcode-0.3.2.dist-info/RECORD,,
134
+ localcode-0.3.4.dist-info/licenses/LICENSE,sha256=Ahg0cteZ-6bIRB7KC15WEtdqWnzy8HY_yzsZBoVRql0,11294
135
+ localcode-0.3.4.dist-info/METADATA,sha256=AJAQv0DAC-RtSDeszeoSwXHacCrJRNKPODmpWOurUjs,7813
136
+ localcode-0.3.4.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
137
+ localcode-0.3.4.dist-info/entry_points.txt,sha256=rE7elTGUvMsBd-Quy22cuTVJ-zGVOmx0nEuC6ni9Tg4,87
138
+ localcode-0.3.4.dist-info/top_level.txt,sha256=58eL3Rw8v0OGmYNxjx8uS4ERWatrzUbyCL5inlukkTo,10
139
+ localcode-0.3.4.dist-info/RECORD,,