lcode-cli 0.33.1__tar.gz → 0.34.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.
Files changed (170) hide show
  1. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/CHANGELOG.md +20 -1
  2. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/PKG-INFO +1 -1
  3. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/quickstart.md +1 -1
  4. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/usage.md +38 -0
  5. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/__init__.py +1 -1
  6. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/agent.py +32 -5
  7. lcode_cli-0.34.0/src/lcode/screenshots.py +1086 -0
  8. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/tools.py +112 -1
  9. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_agent.py +19 -0
  10. lcode_cli-0.34.0/tests/test_screenshots.py +349 -0
  11. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_vision.py +2 -2
  12. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.editorconfig +0 -0
  13. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/CODEOWNERS +0 -0
  14. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  15. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  16. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  17. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/ISSUE_TEMPLATE/model_request.yml +0 -0
  18. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  19. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/dependabot.yml +0 -0
  20. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/workflows/ci.yml +0 -0
  21. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/workflows/docs.yml +0 -0
  22. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.github/workflows/release.yml +0 -0
  23. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.gitignore +0 -0
  24. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/.pre-commit-config.yaml +0 -0
  25. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/AGENTS.md +0 -0
  26. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/CODE_OF_CONDUCT.md +0 -0
  27. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/CONTRIBUTING.md +0 -0
  28. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/LICENSE +0 -0
  29. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/README.md +0 -0
  30. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/SECURITY.md +0 -0
  31. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/action.yml +0 -0
  32. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/agents.md +0 -0
  33. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/assets/demo.svg +0 -0
  34. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/assets/extra.css +0 -0
  35. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/automation.md +0 -0
  36. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/bench.md +0 -0
  37. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/changelog.md +0 -0
  38. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/commands.md +0 -0
  39. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/configuration.md +0 -0
  40. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/contributing.md +0 -0
  41. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/editors.md +0 -0
  42. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/git.md +0 -0
  43. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/hooks.md +0 -0
  44. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/how-it-works.md +0 -0
  45. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/index.md +0 -0
  46. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/installation.md +0 -0
  47. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/lsp.md +0 -0
  48. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/mcp.md +0 -0
  49. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/memory.md +0 -0
  50. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/models.md +0 -0
  51. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/sandbox.md +0 -0
  52. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/search.md +0 -0
  53. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/servers.md +0 -0
  54. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/docs/troubleshooting.md +0 -0
  55. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/examples/README.md +0 -0
  56. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/examples/api/fix_tests.py +0 -0
  57. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/examples/commands/review.md +0 -0
  58. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/examples/skills/commit-message/SKILL.md +0 -0
  59. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/examples/skills/commit-message/scripts/staged.sh +0 -0
  60. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/install.sh +0 -0
  61. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/mkdocs.yml +0 -0
  62. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/overrides/main.html +0 -0
  63. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/pyproject.toml +0 -0
  64. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/scripts/make_logos.py +0 -0
  65. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/scripts/record_demo.py +0 -0
  66. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/scripts/social-preview.html +0 -0
  67. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/__main__.py +0 -0
  68. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/acp.py +0 -0
  69. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/action.py +0 -0
  70. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/api.py +0 -0
  71. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/backends.py +0 -0
  72. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/bench.py +0 -0
  73. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/bench_backend.py +0 -0
  74. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/bench_runtime.py +0 -0
  75. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/catalog.py +0 -0
  76. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/checkpoints.py +0 -0
  77. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/checks.py +0 -0
  78. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/cli.py +0 -0
  79. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/clipboard.py +0 -0
  80. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/codesearch.py +0 -0
  81. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/config.py +0 -0
  82. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/context.py +0 -0
  83. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/extensions.py +0 -0
  84. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/frontmatter.py +0 -0
  85. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/gitflow.py +0 -0
  86. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/hardware.py +0 -0
  87. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/hooks.py +0 -0
  88. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/jobs.py +0 -0
  89. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/limits.py +0 -0
  90. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/lsp.py +0 -0
  91. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/matching.py +0 -0
  92. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/__init__.py +0 -0
  93. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/auth.py +0 -0
  94. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/catalog.py +0 -0
  95. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/catalog.toml +0 -0
  96. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/client.py +0 -0
  97. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/commands.py +0 -0
  98. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/config.py +0 -0
  99. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/http.py +0 -0
  100. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/manager.py +0 -0
  101. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/protocol.py +0 -0
  102. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/server.py +0 -0
  103. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/mcp/stdio.py +0 -0
  104. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/memory.py +0 -0
  105. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/models.toml +0 -0
  106. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/notify.py +0 -0
  107. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/ollama.py +0 -0
  108. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/permissions.py +0 -0
  109. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/planning.py +0 -0
  110. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/render.py +0 -0
  111. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/repeats.py +0 -0
  112. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/repl.py +0 -0
  113. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/repomap.py +0 -0
  114. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/residency.py +0 -0
  115. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/review.py +0 -0
  116. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/sandbox.py +0 -0
  117. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/secretscan.py +0 -0
  118. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/servers/__init__.py +0 -0
  119. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/servers/encord.py +0 -0
  120. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/servers/valohai.py +0 -0
  121. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/servers/xai.py +0 -0
  122. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/sessions.py +0 -0
  123. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/steer.py +0 -0
  124. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/subagents.py +0 -0
  125. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/update.py +0 -0
  126. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/vision.py +0 -0
  127. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/src/lcode/web.py +0 -0
  128. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/conftest.py +0 -0
  129. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/fake_lsp_server.py +0 -0
  130. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/fake_mcp_server.py +0 -0
  131. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_acp.py +0 -0
  132. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_action.py +0 -0
  133. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_api.py +0 -0
  134. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_backends.py +0 -0
  135. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_bench.py +0 -0
  136. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_catalog.py +0 -0
  137. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_checkpoints.py +0 -0
  138. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_checks.py +0 -0
  139. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_cli.py +0 -0
  140. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_clipboard.py +0 -0
  141. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_codesearch.py +0 -0
  142. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_config.py +0 -0
  143. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_context.py +0 -0
  144. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_context_picker.py +0 -0
  145. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_extensions.py +0 -0
  146. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_gitflow.py +0 -0
  147. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_hooks.py +0 -0
  148. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_jobs.py +0 -0
  149. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_lsp.py +0 -0
  150. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_matching.py +0 -0
  151. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_mcp.py +0 -0
  152. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_memory.py +0 -0
  153. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_permissions.py +0 -0
  154. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_plan_mode.py +0 -0
  155. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_repeats.py +0 -0
  156. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_repomap.py +0 -0
  157. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_residency.py +0 -0
  158. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_review.py +0 -0
  159. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_robustness.py +0 -0
  160. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_sandbox.py +0 -0
  161. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_secretscan.py +0 -0
  162. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_servers.py +0 -0
  163. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_sessions.py +0 -0
  164. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_steer.py +0 -0
  165. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_subagents.py +0 -0
  166. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_tools.py +0 -0
  167. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_update.py +0 -0
  168. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_web.py +0 -0
  169. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/tests/test_xai.py +0 -0
  170. {lcode_cli-0.33.1 → lcode_cli-0.34.0}/uv.lock +0 -0
@@ -6,6 +6,24 @@ All notable changes to lcode are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.34.0] - 2026-10-14
10
+
11
+ ### Added
12
+
13
+ - Screenshots without a screen: a `screenshot` tool runs a desktop program or opens a web page the
14
+ way you would see it, saves the shot in `.lcode/screenshots/`, and returns what it shows with the
15
+ sizes behind it. For a Python Qt program, that's each window against the screen, the size its
16
+ layout needs, and the widgets that stick out or overlap. For a web page or a local server
17
+ (headless Chrome), it's the page's size, what scrolls sideways, and the console errors and
18
+ failed requests. Other desktop programs run on a virtual display (Xvfb). See
19
+ [Screenshots](https://nasser1941.github.io/lcode/usage/#screenshots).
20
+
21
+ ### Fixed
22
+
23
+ - In a project with a virtual environment (`.venv`, `venv` or `env`), the model is told to run its
24
+ Python (`.venv/bin/python`) rather than plain `python`, which lacks the project's packages. Found
25
+ when the model checked a PySide6 app with the system Python, failed, and stopped checking.
26
+
9
27
  ## [0.33.1] - 2026-10-14
10
28
 
11
29
  ### Changed
@@ -722,7 +740,8 @@ First public release.
722
740
  - `AGENTS.md` project instructions and `/init` to generate them.
723
741
  - One-line installer for Linux and macOS.
724
742
 
725
- [Unreleased]: https://github.com/nasser1941/lcode/compare/v0.33.1...HEAD
743
+ [Unreleased]: https://github.com/nasser1941/lcode/compare/v0.34.0...HEAD
744
+ [0.34.0]: https://github.com/nasser1941/lcode/compare/v0.33.1...v0.34.0
726
745
  [0.33.1]: https://github.com/nasser1941/lcode/compare/v0.33.0...v0.33.1
727
746
  [0.33.0]: https://github.com/nasser1941/lcode/compare/v0.32.0...v0.33.0
728
747
  [0.32.0]: https://github.com/nasser1941/lcode/compare/v0.31.0...v0.32.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lcode-cli
3
- Version: 0.33.1
3
+ Version: 0.34.0
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/
@@ -12,7 +12,7 @@ lcode
12
12
 
13
13
  ```text
14
14
  ╭──────────────────────────────────────────────╮
15
- │ lcode v0.33.1 — local coding agent │
15
+ │ lcode v0.34.0 — local coding agent │
16
16
  │ │
17
17
  │ model lcode-qwen3.6-35b │
18
18
  │ context 256K tokens │
@@ -106,6 +106,7 @@ reasoning is on.
106
106
  | `web_search` | Search the web for current information (needs a [search provider](#web-search)) |
107
107
  | `web_fetch` | Read a web page or text file by URL as clean text |
108
108
  | `view_image` | Look at an image: a model that can see describes it ([Images](#images)) |
109
+ | `screenshot` | Run a desktop program or open a web page without a screen, and see it with its sizes ([Screenshots](#screenshots)) |
109
110
  | `agent` | Hand a task to a [subagent](agents.md) with its own context; only its report comes back |
110
111
  | `memory` | Save, update or delete [notes](memory.md) that later sessions load |
111
112
  | `repo_map` | The [repository map](search.md): important files, classes and functions, ranked (larger repositories; small ones get it in the instructions) |
@@ -342,6 +343,43 @@ Loading a second model takes a moment: on a 12 GB GPU, describing an image with
342
343
  `qwen3.6:35b-a3b-coding` takes about a minute, and the next request reloads the main model. Turn
343
344
  image support off with `lcode config set vision_model off`.
344
345
 
346
+ ## Screenshots
347
+
348
+ Layout bugs are invisible in the code: a window bigger than the screen, panels cut off, a page that
349
+ scrolls sideways. With the `screenshot` tool the model runs the program, or opens the page, without
350
+ a screen and sees what you would, measured:
351
+
352
+ ```text
353
+ ● screenshot(python camera_wall.py)
354
+ ⎿ .lcode/screenshots/20261014-120211-camera-wall.png (Qt offscreen (PyQt6), 1366x768 screen)
355
+ ```
356
+
357
+ ```text
358
+ - Window "Camera Wall" (CameraWall): 1362x764 at (0, 0)
359
+ Its layout needs at least 1950x984, more than the screen has
360
+ Sticks out of the window: Panel #panel-4: 640x480 at (9, 385)
361
+ Overlap (190x480): Panel #panel-1: 640x480 at (9, 9) and Panel #panel-2: 640x480 at (459, 9)
362
+ ```
363
+
364
+ | What | How | Besides the picture |
365
+ |---|---|---|
366
+ | A Python Qt program (PyQt5/6, PySide2/6) | Qt's offscreen platform, on a screen of the size asked for (Qt 6) | Each window's size and position, the size its layout needs, widgets that stick out or overlap |
367
+ | Any other desktop program (Linux) | A virtual display, Xvfb | The size and position of each window |
368
+ | A web page, an `.html` file or a local server | Headless Chrome, Chromium or Edge (Firefox takes the picture only) | The page's size against the window and what sticks out sideways, console errors, failed requests |
369
+
370
+ - For a web app, the model gives the command that starts the server and the page's URL: lcode
371
+ starts the server, waits for the page to answer, takes the shot and stops the server.
372
+ - A model that can see describes the picture with the model's question in mind (see
373
+ [Which model looks](#images)). Without one, the measurements still come back.
374
+ - Shots are saved in `.lcode/screenshots/` in the project, which keeps itself out of git, so you
375
+ can look too.
376
+ - Running a program asks for permission like any command. A web page that isn't local follows the
377
+ `web` setting. With the [sandbox](sandbox.md) on, programs can't be shown, since they don't run on
378
+ your machine.
379
+ - Needs: Xvfb for other desktop programs (`sudo apt install xvfb x11-apps`), and Chrome or
380
+ Chromium for web pages. On macOS only Qt programs and web pages work, since there's no virtual
381
+ display.
382
+
345
383
  ## Permissions
346
384
 
347
385
  | Mode | File edits | Shell commands |
@@ -3,7 +3,7 @@
3
3
  From a program: `from lcode import Session` (see lcode.api).
4
4
  """
5
5
 
6
- __version__ = "0.33.1"
6
+ __version__ = "0.34.0"
7
7
 
8
8
  API = ("Session", "Result", "Options", "SetupError")
9
9
 
@@ -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):