comodor 0.8.8__tar.gz → 0.9.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 (172) hide show
  1. comodor-0.9.0/PKG-INFO +177 -0
  2. comodor-0.9.0/README.md +150 -0
  3. comodor-0.9.0/docs/README.md +103 -0
  4. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/_version.py +2 -2
  5. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/agent/context.py +26 -0
  6. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/agent/loop.py +9 -0
  7. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/cli.py +23 -1
  8. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/config.py +91 -2
  9. comodor-0.9.0/src/comodor/desktop/__init__.py +389 -0
  10. comodor-0.9.0/src/comodor/desktop/guard.py +242 -0
  11. comodor-0.9.0/src/comodor/desktop/keys.py +174 -0
  12. comodor-0.9.0/src/comodor/desktop/overlay.py +401 -0
  13. comodor-0.9.0/src/comodor/desktop/png.py +99 -0
  14. comodor-0.9.0/src/comodor/desktop/screen.py +179 -0
  15. comodor-0.9.0/src/comodor/desktop/win32.py +429 -0
  16. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/events.py +14 -0
  17. comodor-0.9.0/src/comodor/help.py +313 -0
  18. comodor-0.9.0/src/comodor/tools/computer.py +496 -0
  19. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/registry.py +23 -1
  20. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/app.py +72 -0
  21. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/uninstall.py +12 -0
  22. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/web/commands.py +8 -1
  23. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/web/index.html +67 -0
  24. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/web/server.py +16 -0
  25. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/web/session.py +27 -1
  26. comodor-0.9.0/tests/test_computer_tool.py +463 -0
  27. {comodor-0.8.8 → comodor-0.9.0}/tests/test_config_save.py +58 -0
  28. comodor-0.9.0/tests/test_desktop.py +410 -0
  29. comodor-0.9.0/tests/test_help.py +186 -0
  30. comodor-0.9.0/tests/test_overlay.py +114 -0
  31. comodor-0.9.0/tests/test_real_desktop.py +149 -0
  32. {comodor-0.8.8 → comodor-0.9.0}/tests/test_uninstall.py +26 -0
  33. {comodor-0.8.8 → comodor-0.9.0}/tests/test_web.py +100 -0
  34. comodor-0.8.8/PKG-INFO +0 -822
  35. comodor-0.8.8/README.md +0 -795
  36. {comodor-0.8.8 → comodor-0.9.0}/.gitignore +0 -0
  37. {comodor-0.8.8 → comodor-0.9.0}/LICENSE +0 -0
  38. {comodor-0.8.8 → comodor-0.9.0}/pyproject.toml +0 -0
  39. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/__init__.py +0 -0
  40. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/__main__.py +0 -0
  41. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/agent/__init__.py +0 -0
  42. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/agent/prompts.py +0 -0
  43. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/agent/spawn.py +0 -0
  44. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/agent/tokens.py +0 -0
  45. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/browser/__init__.py +0 -0
  46. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/browser/cdp.py +0 -0
  47. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/browser/launch.py +0 -0
  48. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/browser/page.py +0 -0
  49. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/catalogue.py +0 -0
  50. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/doctor.py +0 -0
  51. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/__init__.py +0 -0
  52. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/associations.py +0 -0
  53. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/bm25.py +0 -0
  54. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/hotindex.py +0 -0
  55. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/memory.py +0 -0
  56. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/progress.py +0 -0
  57. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/reflect.py +0 -0
  58. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/rules.py +0 -0
  59. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/signals.py +0 -0
  60. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/store.py +0 -0
  61. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/learning/writer.py +0 -0
  62. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/mcp/__init__.py +0 -0
  63. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/mcp/catalogue.py +0 -0
  64. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/mcp/commands.py +0 -0
  65. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/mcp/http.py +0 -0
  66. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/mcp/manager.py +0 -0
  67. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/mcp/protocol.py +0 -0
  68. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/migrate.py +0 -0
  69. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/net/__init__.py +0 -0
  70. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/net/http.py +0 -0
  71. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/net/sse.py +0 -0
  72. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/paths.py +0 -0
  73. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/providers/__init__.py +0 -0
  74. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/providers/anthropic.py +0 -0
  75. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/providers/base.py +0 -0
  76. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/providers/caching.py +0 -0
  77. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/providers/fake.py +0 -0
  78. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/providers/gateway.py +0 -0
  79. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/providers/openai_compat.py +0 -0
  80. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/providers/registry.py +0 -0
  81. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/safety/__init__.py +0 -0
  82. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/safety/checkpoints.py +0 -0
  83. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/safety/permissions.py +0 -0
  84. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/safety/redact.py +0 -0
  85. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/session/__init__.py +0 -0
  86. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/session/search.py +0 -0
  87. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/session/store.py +0 -0
  88. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/setup.py +0 -0
  89. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/skills/__init__.py +0 -0
  90. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/skills/catalogue.py +0 -0
  91. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/skills/commands.py +0 -0
  92. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/skills/examples.py +0 -0
  93. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/skills/loader.py +0 -0
  94. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/skills/propose.py +0 -0
  95. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/skills/registry.py +0 -0
  96. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/__init__.py +0 -0
  97. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/base.py +0 -0
  98. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/browse.py +0 -0
  99. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/browser.py +0 -0
  100. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/delegate.py +0 -0
  101. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/fs.py +0 -0
  102. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/history.py +0 -0
  103. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/mcp.py +0 -0
  104. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/overflow.py +0 -0
  105. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/search.py +0 -0
  106. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/shell.py +0 -0
  107. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/skills.py +0 -0
  108. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/todo.py +0 -0
  109. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/tools/web.py +0 -0
  110. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/__init__.py +0 -0
  111. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/banner.py +0 -0
  112. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/bidi.py +0 -0
  113. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/chooser.py +0 -0
  114. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/console.py +0 -0
  115. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/input/__init__.py +0 -0
  116. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/input/keys.py +0 -0
  117. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/input/reader.py +0 -0
  118. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/layout.py +0 -0
  119. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/markdown.py +0 -0
  120. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/screen.py +0 -0
  121. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/theme.py +0 -0
  122. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/__init__.py +0 -0
  123. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/buttons.py +0 -0
  124. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/chat.py +0 -0
  125. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/history.py +0 -0
  126. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/overlay.py +0 -0
  127. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/panel.py +0 -0
  128. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/progress.py +0 -0
  129. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/prompt.py +0 -0
  130. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/statusbar.py +0 -0
  131. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/ui/widgets/toast.py +0 -0
  132. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/update.py +0 -0
  133. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/web/__init__.py +0 -0
  134. {comodor-0.8.8 → comodor-0.9.0}/src/comodor/workspace.py +0 -0
  135. {comodor-0.8.8 → comodor-0.9.0}/tests/conftest.py +0 -0
  136. {comodor-0.8.8 → comodor-0.9.0}/tests/support/fake_mcp_server.py +0 -0
  137. {comodor-0.8.8 → comodor-0.9.0}/tests/test_agent_loop.py +0 -0
  138. {comodor-0.8.8 → comodor-0.9.0}/tests/test_app.py +0 -0
  139. {comodor-0.8.8 → comodor-0.9.0}/tests/test_associations.py +0 -0
  140. {comodor-0.8.8 → comodor-0.9.0}/tests/test_banner.py +0 -0
  141. {comodor-0.8.8 → comodor-0.9.0}/tests/test_bidi.py +0 -0
  142. {comodor-0.8.8 → comodor-0.9.0}/tests/test_browser.py +0 -0
  143. {comodor-0.8.8 → comodor-0.9.0}/tests/test_caching.py +0 -0
  144. {comodor-0.8.8 → comodor-0.9.0}/tests/test_chooser.py +0 -0
  145. {comodor-0.8.8 → comodor-0.9.0}/tests/test_delegate.py +0 -0
  146. {comodor-0.8.8 → comodor-0.9.0}/tests/test_doctor.py +0 -0
  147. {comodor-0.8.8 → comodor-0.9.0}/tests/test_history.py +0 -0
  148. {comodor-0.8.8 → comodor-0.9.0}/tests/test_import_command.py +0 -0
  149. {comodor-0.8.8 → comodor-0.9.0}/tests/test_input.py +0 -0
  150. {comodor-0.8.8 → comodor-0.9.0}/tests/test_layout.py +0 -0
  151. {comodor-0.8.8 → comodor-0.9.0}/tests/test_learning.py +0 -0
  152. {comodor-0.8.8 → comodor-0.9.0}/tests/test_markdown.py +0 -0
  153. {comodor-0.8.8 → comodor-0.9.0}/tests/test_mcp.py +0 -0
  154. {comodor-0.8.8 → comodor-0.9.0}/tests/test_mcp_http.py +0 -0
  155. {comodor-0.8.8 → comodor-0.9.0}/tests/test_migrate.py +0 -0
  156. {comodor-0.8.8 → comodor-0.9.0}/tests/test_overflow.py +0 -0
  157. {comodor-0.8.8 → comodor-0.9.0}/tests/test_performance.py +0 -0
  158. {comodor-0.8.8 → comodor-0.9.0}/tests/test_prefix_stability.py +0 -0
  159. {comodor-0.8.8 → comodor-0.9.0}/tests/test_progress.py +0 -0
  160. {comodor-0.8.8 → comodor-0.9.0}/tests/test_propose.py +0 -0
  161. {comodor-0.8.8 → comodor-0.9.0}/tests/test_providers.py +0 -0
  162. {comodor-0.8.8 → comodor-0.9.0}/tests/test_real_browser.py +0 -0
  163. {comodor-0.8.8 → comodor-0.9.0}/tests/test_reflex.py +0 -0
  164. {comodor-0.8.8 → comodor-0.9.0}/tests/test_run_loop.py +0 -0
  165. {comodor-0.8.8 → comodor-0.9.0}/tests/test_setup.py +0 -0
  166. {comodor-0.8.8 → comodor-0.9.0}/tests/test_setup_import.py +0 -0
  167. {comodor-0.8.8 → comodor-0.9.0}/tests/test_skill_budget.py +0 -0
  168. {comodor-0.8.8 → comodor-0.9.0}/tests/test_skill_catalogue.py +0 -0
  169. {comodor-0.8.8 → comodor-0.9.0}/tests/test_skills.py +0 -0
  170. {comodor-0.8.8 → comodor-0.9.0}/tests/test_switching_model.py +0 -0
  171. {comodor-0.8.8 → comodor-0.9.0}/tests/test_tools.py +0 -0
  172. {comodor-0.8.8 → comodor-0.9.0}/tests/test_update.py +0 -0
comodor-0.9.0/PKG-INFO ADDED
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.5
2
+ Name: comodor
3
+ Version: 0.9.0
4
+ Summary: Comodor — a self-improving terminal coding agent with a Rich TUI
5
+ Project-URL: Homepage, https://comodor.ai
6
+ Project-URL: Repository, https://github.com/ifekri/Comodor
7
+ Project-URL: Issues, https://github.com/ifekri/Comodor/issues
8
+ Project-URL: Changelog, https://github.com/ifekri/Comodor/blob/main/CHANGELOG.md
9
+ Author-email: MMDRZA <mohammadreza.fekri.mojdehi@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: agent,cli,coding-assistant,llm,rich,terminal,tui
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Code Generators
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: rich>=13.7
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=8.0; extra == 'dev'
25
+ Requires-Dist: ruff>=0.14; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Comodor
29
+
30
+ **A coding agent that lives in your terminal — and learns the way you correct it.**
31
+
32
+ [![PyPI](https://img.shields.io/pypi/v/comodor?label=pypi&color=c4441e)](https://pypi.org/project/comodor/)
33
+ [![Python](https://img.shields.io/pypi/pyversions/comodor)](https://pypi.org/project/comodor/)
34
+ [![CI](https://github.com/ifekri/Comodor/actions/workflows/ci.yml/badge.svg)](https://github.com/ifekri/Comodor/actions/workflows/ci.yml)
35
+ [![Licence](https://img.shields.io/pypi/l/comodor?color=555)](LICENSE)
36
+
37
+ **[Documentation](docs/README.md)** · [comodor.ai](https://comodor.ai) · [Changelog](CHANGELOG.md)
38
+
39
+ ---
40
+
41
+ Describe a job — *fix the failing test*, *add a health endpoint*, *work out why
42
+ the deploy broke* — and it does the work: reads your files, writes changes, runs
43
+ your tests, searches the web, and keeps going until the job is done or it needs
44
+ you.
45
+
46
+ It asks before it changes anything, shows you exactly what it is about to do,
47
+ and can undo it.
48
+
49
+ What makes it different from every other tool of this kind is what happens
50
+ afterwards. **When you fix something it wrote, it notices, and it does not make
51
+ that mistake again.** Not because you configured it. Because it watched.
52
+
53
+ ```
54
+ › create defaults.py with 6 string constants
55
+ ⚙ write src/defaults.py — 6 constants
56
+
57
+ … you open the file and change "30s" to '30s' …
58
+
59
+ › now add the timeout constants
60
+ ◈ learned: Use single quotes for string literals. (31 of 34 literals)
61
+ ⚙ write src/defaults.py — '30s', '5m'
62
+ ```
63
+
64
+ That is a real transcript. Nobody told it anything.
65
+
66
+ ```
67
+ ┌─ History ──────────────┐ ┌─ Chat ───────────────────────────────────────────────┐
68
+ │ TASKS 2/4 ──────────── │ │ › add a health endpoint and a test for it │
69
+ │ ● read the app factory │ │ ◈ recalled 3 lessons · skill: review │
70
+ │ ● add the /health rou… │ │ │
71
+ │ ◐ write the test │ │ I'll add the route, then a test. │
72
+ │ ○ run the suite │ │ │
73
+ │ │ │ ⚙ edit src/app.py 0.2s │
74
+ │ │ │ + @app.get('/health') │
75
+ │ │ │ ⚙ run: pytest -q 3.4s │
76
+ │ │ │ 4 passed in 0.42s │
77
+ └────────────────────────┘ └──────────────────────────────────────────────────────┘
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Install
83
+
84
+ ```bash
85
+ uv tool install comodor # or: pipx install comodor, or: pip install comodor
86
+ comodor
87
+ ```
88
+
89
+ Python 3.11 or newer. It asks five questions the first time and never asks
90
+ again.
91
+
92
+ No key yet? `comodor --demo` runs the whole interface offline. Or choose
93
+ **Ollama** in the setup and pay nothing, ever.
94
+
95
+ Already use OpenClaw or Hermes? The first screen offers to bring your keys and
96
+ skills across. [How that works](docs/migrating.md).
97
+
98
+ ---
99
+
100
+ ## What it can do
101
+
102
+ | | |
103
+ |---|---|
104
+ | **Learn from your corrections** | Edit what it wrote, or say so, and the next answer follows. `/progress` shows whether that is actually working — falling correction rates, not a claim. [More](docs/learning.md) |
105
+ | **Ask before it acts** | Reading is silent, writing shows a diff, commands ask. Every write is checkpointed; `/undo` puts it back. [More](docs/safety.md) |
106
+ | **Use a real browser** | One that runs JavaScript, keeps cookies and can log in — not a page fetcher. [More](docs/browser.md) |
107
+ | **Use your screen** | Mouse and keyboard, in any application, with a halo on screen showing where it is about to click. Windows so far. [More](docs/computer.md) |
108
+ | **Follow your procedures** | Write a skill once; it loads it when the work matches. [More](docs/skills.md) |
109
+ | **Run anywhere** | Terminal, browser, or a container. [Web](docs/web.md) · [Docker](docs/docker.md) |
110
+ | **Work with any model** | Seventeen providers, or anything with an OpenAI-compatible URL. Including ones on your own machine. [More](docs/models.md) |
111
+ | **Cost less than it should** | 86% of input tokens served from cache, measured. [More](docs/cost.md) |
112
+
113
+ **One dependency.** The HTTP client, the SSE reader, the WebSocket for the
114
+ browser, the PNG encoder for screenshots — all part of the package. Installing
115
+ Comodor pulls in `rich` and nothing else.
116
+
117
+ ---
118
+
119
+ ## Everyday use
120
+
121
+ ```bash
122
+ comodor # the interface
123
+ comodor run "fix the failing test" --yes # one task, no interface
124
+ comodor web # from a browser
125
+ comodor doctor # is everything alright?
126
+ comodor help # a written help page, not a flag dump
127
+ ```
128
+
129
+ ```
130
+ /help every command /undo restore the last change
131
+ /mode act · plan · chat /progress proof it is improving
132
+ /cost tokens and spend /computer let it use your screen
133
+ Esc stop it F3 cycle mode
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Documentation
139
+
140
+ **[docs/README.md](docs/README.md)** — everything, organised by what you are
141
+ trying to do.
142
+
143
+ | | |
144
+ |---|---|
145
+ | [Getting started](docs/getting-started.md) | Install, choose a model, first task |
146
+ | [The interface](docs/interface.md) | Panels, keys, and all 27 commands |
147
+ | [From the terminal](docs/cli.md) | Every command and flag |
148
+ | [What the agent can do](docs/tools.md) | The 13 tools, and when it uses each |
149
+ | [Safety and permissions](docs/safety.md) | What it can do, and what it never does |
150
+ | [How it learns](docs/learning.md) | Corrections, lessons, and the evidence |
151
+ | [Using your screen](docs/computer.md) | Mouse and keyboard, watched |
152
+ | [Configuration](docs/configuration.md) | Every setting, and what wins |
153
+ | [Cost](docs/cost.md) | Caching, budgets, paying less |
154
+ | [Troubleshooting](docs/troubleshooting.md) | When something is wrong |
155
+
156
+ ---
157
+
158
+ ## Development
159
+
160
+ ```bash
161
+ git clone https://github.com/ifekri/Comodor.git
162
+ cd Comodor
163
+ uv venv && uv pip install -e ".[dev]"
164
+ uv run pytest -q
165
+ uv run ruff check .
166
+ ```
167
+
168
+ Both must pass before anything is pushed, on Linux and on Windows.
169
+ [CONTRIBUTING.md](CONTRIBUTING.md).
170
+
171
+ ---
172
+
173
+ ## Licence
174
+
175
+ MIT. See [LICENSE](LICENSE).
176
+
177
+ Security: [SECURITY.md](SECURITY.md) — please do not open a public issue.
@@ -0,0 +1,150 @@
1
+ # Comodor
2
+
3
+ **A coding agent that lives in your terminal — and learns the way you correct it.**
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/comodor?label=pypi&color=c4441e)](https://pypi.org/project/comodor/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/comodor)](https://pypi.org/project/comodor/)
7
+ [![CI](https://github.com/ifekri/Comodor/actions/workflows/ci.yml/badge.svg)](https://github.com/ifekri/Comodor/actions/workflows/ci.yml)
8
+ [![Licence](https://img.shields.io/pypi/l/comodor?color=555)](LICENSE)
9
+
10
+ **[Documentation](docs/README.md)** · [comodor.ai](https://comodor.ai) · [Changelog](CHANGELOG.md)
11
+
12
+ ---
13
+
14
+ Describe a job — *fix the failing test*, *add a health endpoint*, *work out why
15
+ the deploy broke* — and it does the work: reads your files, writes changes, runs
16
+ your tests, searches the web, and keeps going until the job is done or it needs
17
+ you.
18
+
19
+ It asks before it changes anything, shows you exactly what it is about to do,
20
+ and can undo it.
21
+
22
+ What makes it different from every other tool of this kind is what happens
23
+ afterwards. **When you fix something it wrote, it notices, and it does not make
24
+ that mistake again.** Not because you configured it. Because it watched.
25
+
26
+ ```
27
+ › create defaults.py with 6 string constants
28
+ ⚙ write src/defaults.py — 6 constants
29
+
30
+ … you open the file and change "30s" to '30s' …
31
+
32
+ › now add the timeout constants
33
+ ◈ learned: Use single quotes for string literals. (31 of 34 literals)
34
+ ⚙ write src/defaults.py — '30s', '5m'
35
+ ```
36
+
37
+ That is a real transcript. Nobody told it anything.
38
+
39
+ ```
40
+ ┌─ History ──────────────┐ ┌─ Chat ───────────────────────────────────────────────┐
41
+ │ TASKS 2/4 ──────────── │ │ › add a health endpoint and a test for it │
42
+ │ ● read the app factory │ │ ◈ recalled 3 lessons · skill: review │
43
+ │ ● add the /health rou… │ │ │
44
+ │ ◐ write the test │ │ I'll add the route, then a test. │
45
+ │ ○ run the suite │ │ │
46
+ │ │ │ ⚙ edit src/app.py 0.2s │
47
+ │ │ │ + @app.get('/health') │
48
+ │ │ │ ⚙ run: pytest -q 3.4s │
49
+ │ │ │ 4 passed in 0.42s │
50
+ └────────────────────────┘ └──────────────────────────────────────────────────────┘
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ uv tool install comodor # or: pipx install comodor, or: pip install comodor
59
+ comodor
60
+ ```
61
+
62
+ Python 3.11 or newer. It asks five questions the first time and never asks
63
+ again.
64
+
65
+ No key yet? `comodor --demo` runs the whole interface offline. Or choose
66
+ **Ollama** in the setup and pay nothing, ever.
67
+
68
+ Already use OpenClaw or Hermes? The first screen offers to bring your keys and
69
+ skills across. [How that works](docs/migrating.md).
70
+
71
+ ---
72
+
73
+ ## What it can do
74
+
75
+ | | |
76
+ |---|---|
77
+ | **Learn from your corrections** | Edit what it wrote, or say so, and the next answer follows. `/progress` shows whether that is actually working — falling correction rates, not a claim. [More](docs/learning.md) |
78
+ | **Ask before it acts** | Reading is silent, writing shows a diff, commands ask. Every write is checkpointed; `/undo` puts it back. [More](docs/safety.md) |
79
+ | **Use a real browser** | One that runs JavaScript, keeps cookies and can log in — not a page fetcher. [More](docs/browser.md) |
80
+ | **Use your screen** | Mouse and keyboard, in any application, with a halo on screen showing where it is about to click. Windows so far. [More](docs/computer.md) |
81
+ | **Follow your procedures** | Write a skill once; it loads it when the work matches. [More](docs/skills.md) |
82
+ | **Run anywhere** | Terminal, browser, or a container. [Web](docs/web.md) · [Docker](docs/docker.md) |
83
+ | **Work with any model** | Seventeen providers, or anything with an OpenAI-compatible URL. Including ones on your own machine. [More](docs/models.md) |
84
+ | **Cost less than it should** | 86% of input tokens served from cache, measured. [More](docs/cost.md) |
85
+
86
+ **One dependency.** The HTTP client, the SSE reader, the WebSocket for the
87
+ browser, the PNG encoder for screenshots — all part of the package. Installing
88
+ Comodor pulls in `rich` and nothing else.
89
+
90
+ ---
91
+
92
+ ## Everyday use
93
+
94
+ ```bash
95
+ comodor # the interface
96
+ comodor run "fix the failing test" --yes # one task, no interface
97
+ comodor web # from a browser
98
+ comodor doctor # is everything alright?
99
+ comodor help # a written help page, not a flag dump
100
+ ```
101
+
102
+ ```
103
+ /help every command /undo restore the last change
104
+ /mode act · plan · chat /progress proof it is improving
105
+ /cost tokens and spend /computer let it use your screen
106
+ Esc stop it F3 cycle mode
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Documentation
112
+
113
+ **[docs/README.md](docs/README.md)** — everything, organised by what you are
114
+ trying to do.
115
+
116
+ | | |
117
+ |---|---|
118
+ | [Getting started](docs/getting-started.md) | Install, choose a model, first task |
119
+ | [The interface](docs/interface.md) | Panels, keys, and all 27 commands |
120
+ | [From the terminal](docs/cli.md) | Every command and flag |
121
+ | [What the agent can do](docs/tools.md) | The 13 tools, and when it uses each |
122
+ | [Safety and permissions](docs/safety.md) | What it can do, and what it never does |
123
+ | [How it learns](docs/learning.md) | Corrections, lessons, and the evidence |
124
+ | [Using your screen](docs/computer.md) | Mouse and keyboard, watched |
125
+ | [Configuration](docs/configuration.md) | Every setting, and what wins |
126
+ | [Cost](docs/cost.md) | Caching, budgets, paying less |
127
+ | [Troubleshooting](docs/troubleshooting.md) | When something is wrong |
128
+
129
+ ---
130
+
131
+ ## Development
132
+
133
+ ```bash
134
+ git clone https://github.com/ifekri/Comodor.git
135
+ cd Comodor
136
+ uv venv && uv pip install -e ".[dev]"
137
+ uv run pytest -q
138
+ uv run ruff check .
139
+ ```
140
+
141
+ Both must pass before anything is pushed, on Linux and on Windows.
142
+ [CONTRIBUTING.md](CONTRIBUTING.md).
143
+
144
+ ---
145
+
146
+ ## Licence
147
+
148
+ MIT. See [LICENSE](LICENSE).
149
+
150
+ Security: [SECURITY.md](SECURITY.md) — please do not open a public issue.
@@ -0,0 +1,103 @@
1
+ # Comodor documentation
2
+
3
+ A terminal coding agent that learns the way you correct it.
4
+
5
+ New here? **[Getting started](getting-started.md)** takes about five minutes and
6
+ ends with the agent doing something useful.
7
+
8
+ ---
9
+
10
+ ## By what you are trying to do
11
+
12
+ ### Get going
13
+
14
+ | | |
15
+ |---|---|
16
+ | [Getting started](getting-started.md) | Install, choose a model, first task |
17
+ | [Coming from another agent](migrating.md) | Bring keys and skills from OpenClaw or Hermes |
18
+ | [Choosing a model](models.md) | Which provider, which model, what it costs |
19
+
20
+ ### Use it
21
+
22
+ | | |
23
+ |---|---|
24
+ | [The interface](interface.md) | Panels, keys, modes, and all 27 commands |
25
+ | [From the terminal](cli.md) | Every command and flag, with examples |
26
+ | [What the agent can do](tools.md) | The 13 tools it has, and when it uses each |
27
+ | [Skills](skills.md) | Procedures you write once and it follows |
28
+
29
+ ### Let it reach further
30
+
31
+ | | |
32
+ |---|---|
33
+ | [The real browser](browser.md) | A browser that runs JavaScript and can log in |
34
+ | [Using your screen](computer.md) | Mouse and keyboard, in any application |
35
+ | [From a browser](web.md) | The web interface, locally or on a server |
36
+ | [In Docker](docker.md) | One command, in a container |
37
+ | [MCP servers](mcp.md) | Tools from the Model Context Protocol |
38
+
39
+ ### Understand it
40
+
41
+ | | |
42
+ |---|---|
43
+ | [How it learns](learning.md) | Corrections, lessons, rules, and the proof |
44
+ | [Safety and permissions](safety.md) | What it can do, what it asks, what it never does |
45
+ | [Cost](cost.md) | Caching, budgets, and paying less for the same work |
46
+ | [Configuration](configuration.md) | Every setting, where files live, what wins |
47
+
48
+ ### When something is wrong
49
+
50
+ | | |
51
+ |---|---|
52
+ | [Troubleshooting](troubleshooting.md) | `doctor`, common problems, and how to report one |
53
+
54
+ ---
55
+
56
+ ## The shortest possible version
57
+
58
+ ```bash
59
+ pip install comodor # or: uv tool install comodor
60
+ comodor # it asks a few questions, once
61
+ ```
62
+
63
+ Then type what you want. Correct it when it is wrong — edit the file, or just
64
+ say so — and it learns. `/progress` shows you whether that is actually working.
65
+
66
+ ```bash
67
+ comodor run "fix the failing test in tests/test_parser.py" # one task, no interface
68
+ comodor web # from a browser
69
+ comodor doctor # is everything alright?
70
+ comodor help # the written help page
71
+ ```
72
+
73
+ ## What makes it different
74
+
75
+ **It learns from corrections, not from praise.** Most agents forget the moment
76
+ a session ends. Comodor watches what you change about its output and turns that
77
+ into a lesson with a confidence that rises when it holds and falls when it does
78
+ not. [How it learns](learning.md) explains the mechanism; `/progress` shows the
79
+ evidence.
80
+
81
+ **It asks before it acts, and everything is reversible.** Reading is silent.
82
+ Writing asks. Running a command asks louder. Every write is checkpointed, and
83
+ `/undo` puts the last one back. [Safety and permissions](safety.md).
84
+
85
+ **One dependency.** The HTTP client, the SSE reader, the WebSocket for the
86
+ browser, the PNG encoder for screenshots — all part of the package. Installing
87
+ Comodor pulls in `rich` and nothing else.
88
+
89
+ **It can use a real browser and a real desktop.** Not a text fetcher: a browser
90
+ that runs JavaScript and keeps cookies, and — on Windows — the mouse and
91
+ keyboard, with a halo on screen showing you where it is about to click.
92
+ [Browser](browser.md), [screen](computer.md).
93
+
94
+ ---
95
+
96
+ ## Also in the repository
97
+
98
+ | | |
99
+ |---|---|
100
+ | [CHANGELOG](../CHANGELOG.md) | What changed, and why |
101
+ | [CONTRIBUTING](../CONTRIBUTING.md) | Working on Comodor itself |
102
+ | [SECURITY](../SECURITY.md) | Reporting something sensitive |
103
+ | [RELEASING](../RELEASING.md) | How a release is cut |
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.8.8'
22
- __version_tuple__ = version_tuple = (0, 8, 8)
21
+ __version__ = version = '0.9.0'
22
+ __version_tuple__ = version_tuple = (0, 9, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -136,6 +136,32 @@ class Conversation:
136
136
  self.compactions += 1
137
137
  return len(middle)
138
138
 
139
+ def forget_old_pictures(self, keep: int = 2) -> int:
140
+ """Drop all but the newest screenshots. Returns how many went.
141
+
142
+ A picture costs the same every turn it stays in the history, and a
143
+ screen from twenty clicks ago is not what is on the screen now. The
144
+ message is left in place with a note where the image was, so the model
145
+ can see that it looked and when, without paying to look again.
146
+
147
+ `keep` is small on purpose. Two is enough to compare "before" with
148
+ "after"; a third is a screen two actions old, which is history.
149
+ """
150
+ seen = 0
151
+ dropped = 0
152
+ for message in reversed(self.messages):
153
+ if not message.images:
154
+ continue
155
+ seen += 1
156
+ if seen <= keep:
157
+ continue
158
+ dropped += len(message.images)
159
+ message.images = []
160
+ note = "[the screenshot from this step is no longer in context]"
161
+ if note not in message.content:
162
+ message.content = (message.content + "\n" + note).strip()
163
+ return dropped
164
+
139
165
  # -- introspection ---------------------------------------------------- #
140
166
 
141
167
  def summary_line(self) -> str:
@@ -318,6 +318,15 @@ class AgentLoop:
318
318
 
319
319
  def _maybe_compact(self, system_prompt: str, specs: list[ToolSpec]) -> None:
320
320
  agent = self.config.agent
321
+
322
+ # Before measuring anything. Screenshots are the largest thing in a
323
+ # desktop run's history and the fastest to go stale, and dropping them
324
+ # is exact and free - where compaction is a model call. Doing it first
325
+ # also means the measurement below is of what will actually be sent.
326
+ gone = self.conversation.forget_old_pictures(
327
+ getattr(agent, "keep_screenshots", 2))
328
+ if gone:
329
+ self._emit_usage(system_prompt, specs)
321
330
  limit = agent.context_limit or 128_000
322
331
  if not self.conversation.needs_compaction(limit, agent.compact_at,
323
332
  system_prompt, specs):
@@ -35,7 +35,15 @@ def build_parser() -> argparse.ArgumentParser:
35
35
  parser = argparse.ArgumentParser(
36
36
  prog="comodor",
37
37
  description="Comodor — a terminal coding agent that learns as it works.",
38
+ # argparse's own help is a list of flags. That answers "what arguments
39
+ # does this take" and not "what is this, and what do I do now", which
40
+ # is what somebody typing --help is asking. See `help.py`. Only the top
41
+ # level is taken over: for one subcommand, the list of its flags really
42
+ # is the answer.
43
+ add_help=False,
38
44
  )
45
+ parser.add_argument("-h", "--help", action="store_true", dest="want_help",
46
+ help="this page, or `comodor help <topic>` for one part")
39
47
  parser.add_argument("--version", action="version", version=f"comodor {__version__}")
40
48
 
41
49
  parser.add_argument("--provider", help="provider to use (openrouter, anthropic, …)")
@@ -62,6 +70,11 @@ def build_parser() -> argparse.ArgumentParser:
62
70
  help="approve file writes and commands automatically")
63
71
  run.add_argument("--max-steps", type=int, help="override the step limit")
64
72
 
73
+ written = sub.add_parser(
74
+ "help", help="what this is and how to use it, in full")
75
+ written.add_argument("topic", nargs="?", default="",
76
+ help="one part of it, in more detail")
77
+
65
78
  sub.add_parser("setup", help="choose a provider and model, and save the answers")
66
79
  brought = sub.add_parser(
67
80
  "import", help="bring keys, model and skills over from another agent")
@@ -189,7 +202,7 @@ def run_headless(config: Config, args: argparse.Namespace) -> int:
189
202
  permissions.on_denied = memory.on_denied
190
203
  skills = load_skills(config)
191
204
  mcp = MCPManager(config.mcp.servers) if config.mcp.enabled else None
192
- tools = ToolRegistry(skills=skills, mcp=mcp,
205
+ tools = ToolRegistry(skills=skills, mcp=mcp, config=config,
193
206
  spawn=spawner(config, gateway, bus, skills=skills, mcp=mcp))
194
207
  agent = AgentLoop(config, gateway, tools, bus,
195
208
  permissions, Conversation(), memory, skills=skills)
@@ -692,6 +705,15 @@ def run_import(config: Config, args: argparse.Namespace) -> int:
692
705
 
693
706
  def main(argv: list[str] | None = None) -> int:
694
707
  args = build_parser().parse_args(argv)
708
+
709
+ # Before the configuration is loaded: somebody who cannot get the program
710
+ # to start is exactly the person who needs the help page, and loading a
711
+ # broken config first would deny it to them.
712
+ if getattr(args, "want_help", False) or args.command == "help":
713
+ from .help import render
714
+
715
+ return render(topic=getattr(args, "topic", "") or "")
716
+
695
717
  config = apply_overrides(load_config(args.cwd), args)
696
718
 
697
719
  if args.command == "doctor":