lcode-cli 0.33.0__py3-none-any.whl → 0.34.0__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 CHANGED
@@ -3,7 +3,7 @@
3
3
  From a program: `from lcode import Session` (see lcode.api).
4
4
  """
5
5
 
6
- __version__ = "0.33.0"
6
+ __version__ = "0.34.0"
7
7
 
8
8
  API = ("Session", "Result", "Options", "SetupError")
9
9
 
lcode/agent.py CHANGED
@@ -48,7 +48,7 @@ from lcode.config import format_tokens
48
48
  from lcode.jobs import Jobs
49
49
  from lcode.mcp import McpManager
50
50
  from lcode.ollama import Ollama, OllamaError
51
- from lcode.permissions import Permissions
51
+ from lcode.permissions import LEADING_CD, Permissions
52
52
  from lcode.render import MarkdownStreamer
53
53
  from lcode.repeats import Repeats
54
54
  from lcode.sandbox import Sandbox, SandboxError, project_root
@@ -122,7 +122,7 @@ SYSTEM_PROMPT = """You are lcode, an autonomous software-engineering agent runni
122
122
  # Verifying your work
123
123
  - A syntax check or an import only shows that code loads. Run what you changed the way it will be used (the script, the tests, the app) and fix what fails.
124
124
  - To fix a bug, first reproduce each problem the user reports with a test, a command or a short script, then run the same checks to show they're gone.
125
- - For what you can't see, like a GUI, a web page or a layout, check it without a screen where you can: `QT_QPA_PLATFORM=offscreen` for Qt, a headless browser, or a script that prints sizes and positions or renders to an image{look}. Measure instead of assuming.
125
+ - For what you can't see, like a GUI, a web page or a layout, use the screenshot tool: it runs the program (or opens the page) without a screen and returns what it shows, with the sizes of windows, widgets or the page measured{look}. Measure instead of assuming.
126
126
  - Never claim something works, mark a todo done, or call it tested unless you ran it. If you couldn't check something, say so and tell the user exactly what to look for. If something fails, say so and show the relevant error.{checks}
127
127
 
128
128
  # Communication
@@ -131,7 +131,7 @@ SYSTEM_PROMPT = """You are lcode, an autonomous software-engineering agent runni
131
131
 
132
132
  # Environment
133
133
  - Working directory: {cwd}
134
- - OS: {os}; shell: bash; Python command: `{python}`
134
+ - OS: {os}; shell: bash; Python command: {python}
135
135
  - Date: {date}
136
136
  - Git: {git}
137
137
 
@@ -218,6 +218,30 @@ def call_arguments(call: dict) -> dict:
218
218
  return args if isinstance(args, dict) else {}
219
219
 
220
220
 
221
+ VENV_DIRS = (".venv", "venv", "env")
222
+
223
+
224
+ def project_python(cwd: Path) -> Path | None:
225
+ """The project's virtual environment's Python (.venv, venv or env, here or up to the repository's root)."""
226
+ for folder in [cwd, *cwd.parents][:4]:
227
+ for name in VENV_DIRS:
228
+ path = folder / name / "bin" / "python"
229
+ if path.exists() and (folder / name / "pyvenv.cfg").exists():
230
+ return path
231
+ if (folder / ".git").exists():
232
+ break # not beyond the repository
233
+ return None
234
+
235
+
236
+ def python_line(cwd: Path) -> str:
237
+ """How the system prompt names the Python command."""
238
+ venv = project_python(cwd)
239
+ if venv is None:
240
+ return f"`{'python' if shutil.which('python') else 'python3'}`"
241
+ shown = venv.relative_to(cwd) if cwd in venv.parents else venv
242
+ return f"`{shown}` (the project's virtual environment: plain python lacks its packages)"
243
+
244
+
221
245
  class Progress:
222
246
  """The status line under a running request.
223
247
 
@@ -372,7 +396,7 @@ class Agent:
372
396
  model=self.settings.model,
373
397
  cwd=self.cwd,
374
398
  os=f"{platform.system()} {platform.release()} ({platform.machine()})",
375
- python="python" if shutil.which("python") else "python3",
399
+ python=python_line(self.cwd),
376
400
  date=dt.date.today().isoformat(),
377
401
  git=git_info(self.cwd),
378
402
  tree=tree(self.cwd, depth=1, limit=120),
@@ -383,7 +407,7 @@ class Agent:
383
407
  skills=extensions.skills_prompt(self.extensions().skills, self.settings.context),
384
408
  code=self.code_prompt(),
385
409
  map=self.map_prompt(),
386
- look=" you look at with view_image" if self.vision_model() else "",
410
+ look="; look at other images with view_image" if self.vision_model() else "",
387
411
  checks=self.checks_prompt(),
388
412
  )
389
413
 
@@ -1000,6 +1024,9 @@ class Agent:
1000
1024
  return f"agent({args.get('type', '')}: {args.get('description') or str(args.get('task', ''))[:50]})"
1001
1025
  if name == "bash":
1002
1026
  return f"$ {str(args.get('command', ''))[:60]}"
1027
+ if name == "screenshot":
1028
+ what = args.get("url") or LEADING_CD.sub("", str(args.get("command") or ""))
1029
+ return f"screenshot({what[:60]})"
1003
1030
  if name in ("web_search", "web_fetch", "view_image"):
1004
1031
  return f"{name}({args.get('query') or args.get('url') or args.get('path') or ''})"
1005
1032
  if self.mcp and self.mcp.owns(name):