pyrrhon 0.1.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 (224) hide show
  1. pyrrhon-0.1.0/.gitignore +220 -0
  2. pyrrhon-0.1.0/LICENSE +21 -0
  3. pyrrhon-0.1.0/PKG-INFO +448 -0
  4. pyrrhon-0.1.0/README.md +398 -0
  5. pyrrhon-0.1.0/pyproject.toml +204 -0
  6. pyrrhon-0.1.0/pyrrhon/__init__.py +1 -0
  7. pyrrhon-0.1.0/pyrrhon/__main__.py +3 -0
  8. pyrrhon-0.1.0/pyrrhon/bootstrap.py +533 -0
  9. pyrrhon-0.1.0/pyrrhon/branding.py +73 -0
  10. pyrrhon-0.1.0/pyrrhon/channels.py +124 -0
  11. pyrrhon-0.1.0/pyrrhon/cli.py +141 -0
  12. pyrrhon-0.1.0/pyrrhon/commands/__init__.py +0 -0
  13. pyrrhon-0.1.0/pyrrhon/commands/builtin.py +93 -0
  14. pyrrhon-0.1.0/pyrrhon/commands/debug_cmd.py +39 -0
  15. pyrrhon-0.1.0/pyrrhon/commands/init_cmd.py +44 -0
  16. pyrrhon-0.1.0/pyrrhon/commands/mcp_cmd.py +30 -0
  17. pyrrhon-0.1.0/pyrrhon/commands/mode_cmd.py +19 -0
  18. pyrrhon-0.1.0/pyrrhon/commands/plugins_cmd.py +33 -0
  19. pyrrhon-0.1.0/pyrrhon/commands/registry.py +104 -0
  20. pyrrhon-0.1.0/pyrrhon/commands/session_cmd.py +197 -0
  21. pyrrhon-0.1.0/pyrrhon/commands/settings_cmd.py +208 -0
  22. pyrrhon-0.1.0/pyrrhon/commands/voice_cmd.py +21 -0
  23. pyrrhon-0.1.0/pyrrhon/config/__init__.py +0 -0
  24. pyrrhon-0.1.0/pyrrhon/config/catalog.py +209 -0
  25. pyrrhon-0.1.0/pyrrhon/config/credentials.py +74 -0
  26. pyrrhon-0.1.0/pyrrhon/config/settings.py +409 -0
  27. pyrrhon-0.1.0/pyrrhon/config/trust.py +103 -0
  28. pyrrhon-0.1.0/pyrrhon/config/wizard.py +283 -0
  29. pyrrhon-0.1.0/pyrrhon/core/__init__.py +0 -0
  30. pyrrhon-0.1.0/pyrrhon/core/agent/__init__.py +0 -0
  31. pyrrhon-0.1.0/pyrrhon/core/agent/briefing.py +221 -0
  32. pyrrhon-0.1.0/pyrrhon/core/agent/design_prompts.py +59 -0
  33. pyrrhon-0.1.0/pyrrhon/core/agent/escalate.py +90 -0
  34. pyrrhon-0.1.0/pyrrhon/core/agent/guards.py +257 -0
  35. pyrrhon-0.1.0/pyrrhon/core/agent/loop.py +1153 -0
  36. pyrrhon-0.1.0/pyrrhon/core/agent/policy.py +243 -0
  37. pyrrhon-0.1.0/pyrrhon/core/agent/prompts.py +266 -0
  38. pyrrhon-0.1.0/pyrrhon/core/agent/soul.py +159 -0
  39. pyrrhon-0.1.0/pyrrhon/core/agent/subagent.py +140 -0
  40. pyrrhon-0.1.0/pyrrhon/core/agent/turn_type.py +147 -0
  41. pyrrhon-0.1.0/pyrrhon/core/citation_link.py +44 -0
  42. pyrrhon-0.1.0/pyrrhon/core/context.py +305 -0
  43. pyrrhon-0.1.0/pyrrhon/core/events.py +168 -0
  44. pyrrhon-0.1.0/pyrrhon/core/grounding/__init__.py +0 -0
  45. pyrrhon-0.1.0/pyrrhon/core/grounding/citations.py +57 -0
  46. pyrrhon-0.1.0/pyrrhon/core/grounding/evidence.py +254 -0
  47. pyrrhon-0.1.0/pyrrhon/core/grounding/gate.py +323 -0
  48. pyrrhon-0.1.0/pyrrhon/core/mcp/__init__.py +3 -0
  49. pyrrhon-0.1.0/pyrrhon/core/mcp/manager.py +154 -0
  50. pyrrhon-0.1.0/pyrrhon/core/providers/__init__.py +0 -0
  51. pyrrhon-0.1.0/pyrrhon/core/providers/adapters.py +91 -0
  52. pyrrhon-0.1.0/pyrrhon/core/providers/errors.py +207 -0
  53. pyrrhon-0.1.0/pyrrhon/core/providers/limits.py +99 -0
  54. pyrrhon-0.1.0/pyrrhon/core/providers/llm.py +568 -0
  55. pyrrhon-0.1.0/pyrrhon/core/providers/registry.py +102 -0
  56. pyrrhon-0.1.0/pyrrhon/core/session.py +435 -0
  57. pyrrhon-0.1.0/pyrrhon/core/telemetry.py +278 -0
  58. pyrrhon-0.1.0/pyrrhon/core/tools/__init__.py +0 -0
  59. pyrrhon-0.1.0/pyrrhon/core/tools/ast_index.py +472 -0
  60. pyrrhon-0.1.0/pyrrhon/core/tools/base.py +91 -0
  61. pyrrhon-0.1.0/pyrrhon/core/tools/explore.py +125 -0
  62. pyrrhon-0.1.0/pyrrhon/core/tools/git.py +136 -0
  63. pyrrhon-0.1.0/pyrrhon/core/tools/images.py +136 -0
  64. pyrrhon-0.1.0/pyrrhon/core/tools/languages.py +200 -0
  65. pyrrhon-0.1.0/pyrrhon/core/tools/memory.py +101 -0
  66. pyrrhon-0.1.0/pyrrhon/core/tools/orientation.py +55 -0
  67. pyrrhon-0.1.0/pyrrhon/core/tools/repo.py +478 -0
  68. pyrrhon-0.1.0/pyrrhon/core/tools/results.py +227 -0
  69. pyrrhon-0.1.0/pyrrhon/core/tools/spec_writer.py +68 -0
  70. pyrrhon-0.1.0/pyrrhon/core/tools/symbol_context.py +119 -0
  71. pyrrhon-0.1.0/pyrrhon/core/tools/web.py +167 -0
  72. pyrrhon-0.1.0/pyrrhon/core/transcript.py +307 -0
  73. pyrrhon-0.1.0/pyrrhon/evals/__init__.py +0 -0
  74. pyrrhon-0.1.0/pyrrhon/evals/design.py +150 -0
  75. pyrrhon-0.1.0/pyrrhon/evals/grounding.py +473 -0
  76. pyrrhon-0.1.0/pyrrhon/evals/strangers.py +170 -0
  77. pyrrhon-0.1.0/pyrrhon/headless.py +182 -0
  78. pyrrhon-0.1.0/pyrrhon/plugins/__init__.py +23 -0
  79. pyrrhon-0.1.0/pyrrhon/plugins/loader.py +294 -0
  80. pyrrhon-0.1.0/pyrrhon/repl.py +248 -0
  81. pyrrhon-0.1.0/pyrrhon/tui/__init__.py +0 -0
  82. pyrrhon-0.1.0/pyrrhon/tui/app.py +537 -0
  83. pyrrhon-0.1.0/pyrrhon/tui/completion.py +70 -0
  84. pyrrhon-0.1.0/pyrrhon/tui/editor.py +90 -0
  85. pyrrhon-0.1.0/pyrrhon/tui/messages.py +299 -0
  86. pyrrhon-0.1.0/pyrrhon/tui/palette.py +52 -0
  87. pyrrhon-0.1.0/pyrrhon/tui/prompt.py +105 -0
  88. pyrrhon-0.1.0/pyrrhon/tui/pyrrhon.tcss +186 -0
  89. pyrrhon-0.1.0/pyrrhon/tui/renderer.py +223 -0
  90. pyrrhon-0.1.0/pyrrhon/tui/splash.py +29 -0
  91. pyrrhon-0.1.0/pyrrhon/tui/status.py +97 -0
  92. pyrrhon-0.1.0/pyrrhon/tui/theme.py +74 -0
  93. pyrrhon-0.1.0/pyrrhon/tui/turn.py +266 -0
  94. pyrrhon-0.1.0/pyrrhon/voice/__init__.py +88 -0
  95. pyrrhon-0.1.0/pyrrhon/voice/_logging.py +46 -0
  96. pyrrhon-0.1.0/pyrrhon/voice/bridge.py +402 -0
  97. pyrrhon-0.1.0/pyrrhon/voice/factory.py +174 -0
  98. pyrrhon-0.1.0/pyrrhon/voice/gemini.py +56 -0
  99. pyrrhon-0.1.0/pyrrhon/voice/huggingface.py +114 -0
  100. pyrrhon-0.1.0/pyrrhon/voice/pipeline.py +269 -0
  101. pyrrhon-0.1.0/pyrrhon/voice/playback.py +82 -0
  102. pyrrhon-0.1.0/pyrrhon/voice/registry.py +231 -0
  103. pyrrhon-0.1.0/tests/__init__.py +0 -0
  104. pyrrhon-0.1.0/tests/conftest.py +125 -0
  105. pyrrhon-0.1.0/tests/fixtures/mcp_echo_server.py +15 -0
  106. pyrrhon-0.1.0/tests/fixtures/plugins/hello-reviewer/plugin.toml +7 -0
  107. pyrrhon-0.1.0/tests/fixtures/plugins/hello-reviewer/prompts/review-style.md +6 -0
  108. pyrrhon-0.1.0/tests/fixtures/plugins/hello-reviewer/tools.py +26 -0
  109. pyrrhon-0.1.0/tests/fixtures/polyglot_repo/app.ts +11 -0
  110. pyrrhon-0.1.0/tests/fixtures/polyglot_repo/helpers.js +3 -0
  111. pyrrhon-0.1.0/tests/fixtures/polyglot_repo/server.go +13 -0
  112. pyrrhon-0.1.0/tests/fixtures/sample_repo/README.md +3 -0
  113. pyrrhon-0.1.0/tests/fixtures/sample_repo/app.py +9 -0
  114. pyrrhon-0.1.0/tests/fixtures/sample_repo/utils/helpers.py +2 -0
  115. pyrrhon-0.1.0/tests/helpers.py +94 -0
  116. pyrrhon-0.1.0/tests/test_adapter_driver.py +135 -0
  117. pyrrhon-0.1.0/tests/test_agent_gate.py +235 -0
  118. pyrrhon-0.1.0/tests/test_agent_loop.py +226 -0
  119. pyrrhon-0.1.0/tests/test_agent_provider_errors.py +102 -0
  120. pyrrhon-0.1.0/tests/test_ast_tools.py +30 -0
  121. pyrrhon-0.1.0/tests/test_background_compaction.py +223 -0
  122. pyrrhon-0.1.0/tests/test_branding.py +40 -0
  123. pyrrhon-0.1.0/tests/test_briefing.py +179 -0
  124. pyrrhon-0.1.0/tests/test_build_agent_m4.py +63 -0
  125. pyrrhon-0.1.0/tests/test_builtin_commands.py +134 -0
  126. pyrrhon-0.1.0/tests/test_catalog.py +168 -0
  127. pyrrhon-0.1.0/tests/test_channels_session.py +53 -0
  128. pyrrhon-0.1.0/tests/test_citation_link.py +38 -0
  129. pyrrhon-0.1.0/tests/test_citations.py +39 -0
  130. pyrrhon-0.1.0/tests/test_cli.py +129 -0
  131. pyrrhon-0.1.0/tests/test_command_registry.py +76 -0
  132. pyrrhon-0.1.0/tests/test_context.py +400 -0
  133. pyrrhon-0.1.0/tests/test_context_recovery.py +162 -0
  134. pyrrhon-0.1.0/tests/test_credentials.py +67 -0
  135. pyrrhon-0.1.0/tests/test_design_eval.py +170 -0
  136. pyrrhon-0.1.0/tests/test_design_prompts.py +31 -0
  137. pyrrhon-0.1.0/tests/test_design_session_e2e.py +89 -0
  138. pyrrhon-0.1.0/tests/test_escalation.py +196 -0
  139. pyrrhon-0.1.0/tests/test_event_dispatch.py +79 -0
  140. pyrrhon-0.1.0/tests/test_events.py +18 -0
  141. pyrrhon-0.1.0/tests/test_events_truncate.py +17 -0
  142. pyrrhon-0.1.0/tests/test_evidence.py +223 -0
  143. pyrrhon-0.1.0/tests/test_explore.py +320 -0
  144. pyrrhon-0.1.0/tests/test_extract_question.py +81 -0
  145. pyrrhon-0.1.0/tests/test_fallback_llm.py +124 -0
  146. pyrrhon-0.1.0/tests/test_gemini_voice.py +68 -0
  147. pyrrhon-0.1.0/tests/test_git_tools.py +80 -0
  148. pyrrhon-0.1.0/tests/test_grounding_eval.py +383 -0
  149. pyrrhon-0.1.0/tests/test_grounding_gate.py +307 -0
  150. pyrrhon-0.1.0/tests/test_headless.py +165 -0
  151. pyrrhon-0.1.0/tests/test_hf_voice.py +75 -0
  152. pyrrhon-0.1.0/tests/test_history_invariant.py +137 -0
  153. pyrrhon-0.1.0/tests/test_import_graph.py +79 -0
  154. pyrrhon-0.1.0/tests/test_init_and_repl.py +60 -0
  155. pyrrhon-0.1.0/tests/test_languages.py +102 -0
  156. pyrrhon-0.1.0/tests/test_latency.py +40 -0
  157. pyrrhon-0.1.0/tests/test_learned_limit.py +240 -0
  158. pyrrhon-0.1.0/tests/test_llm_adapter.py +217 -0
  159. pyrrhon-0.1.0/tests/test_llm_registry.py +90 -0
  160. pyrrhon-0.1.0/tests/test_loop_guards.py +182 -0
  161. pyrrhon-0.1.0/tests/test_mcp_manager.py +60 -0
  162. pyrrhon-0.1.0/tests/test_mcp_settings.py +76 -0
  163. pyrrhon-0.1.0/tests/test_mcp_wiring.py +47 -0
  164. pyrrhon-0.1.0/tests/test_memory_tool.py +128 -0
  165. pyrrhon-0.1.0/tests/test_mode_command.py +52 -0
  166. pyrrhon-0.1.0/tests/test_multilang_index.py +84 -0
  167. pyrrhon-0.1.0/tests/test_orientation.py +88 -0
  168. pyrrhon-0.1.0/tests/test_parallel_tools.py +281 -0
  169. pyrrhon-0.1.0/tests/test_playback.py +75 -0
  170. pyrrhon-0.1.0/tests/test_plugin_code_loading.py +170 -0
  171. pyrrhon-0.1.0/tests/test_plugin_discovery.py +135 -0
  172. pyrrhon-0.1.0/tests/test_plugin_example.py +118 -0
  173. pyrrhon-0.1.0/tests/test_plugin_manifest.py +66 -0
  174. pyrrhon-0.1.0/tests/test_plugins_command.py +56 -0
  175. pyrrhon-0.1.0/tests/test_prompt_policy.py +127 -0
  176. pyrrhon-0.1.0/tests/test_provider_errors.py +424 -0
  177. pyrrhon-0.1.0/tests/test_provider_retrying.py +81 -0
  178. pyrrhon-0.1.0/tests/test_read_image.py +142 -0
  179. pyrrhon-0.1.0/tests/test_repo_map.py +153 -0
  180. pyrrhon-0.1.0/tests/test_repo_tools.py +250 -0
  181. pyrrhon-0.1.0/tests/test_repo_trust_boundary.py +161 -0
  182. pyrrhon-0.1.0/tests/test_safety.py +484 -0
  183. pyrrhon-0.1.0/tests/test_session.py +201 -0
  184. pyrrhon-0.1.0/tests/test_session_commands.py +223 -0
  185. pyrrhon-0.1.0/tests/test_session_mode.py +85 -0
  186. pyrrhon-0.1.0/tests/test_settings.py +273 -0
  187. pyrrhon-0.1.0/tests/test_settings_cmd.py +123 -0
  188. pyrrhon-0.1.0/tests/test_soul.py +175 -0
  189. pyrrhon-0.1.0/tests/test_spec_writer.py +40 -0
  190. pyrrhon-0.1.0/tests/test_strangers.py +136 -0
  191. pyrrhon-0.1.0/tests/test_subagent.py +223 -0
  192. pyrrhon-0.1.0/tests/test_symbol_context.py +99 -0
  193. pyrrhon-0.1.0/tests/test_symbol_index.py +104 -0
  194. pyrrhon-0.1.0/tests/test_telemetry.py +305 -0
  195. pyrrhon-0.1.0/tests/test_text_streaming.py +243 -0
  196. pyrrhon-0.1.0/tests/test_tool_results.py +189 -0
  197. pyrrhon-0.1.0/tests/test_tool_schemas.py +97 -0
  198. pyrrhon-0.1.0/tests/test_transcript.py +249 -0
  199. pyrrhon-0.1.0/tests/test_truncated_reply.py +260 -0
  200. pyrrhon-0.1.0/tests/test_trust.py +233 -0
  201. pyrrhon-0.1.0/tests/test_tui_app.py +226 -0
  202. pyrrhon-0.1.0/tests/test_tui_completion.py +113 -0
  203. pyrrhon-0.1.0/tests/test_tui_editor.py +105 -0
  204. pyrrhon-0.1.0/tests/test_tui_liveness.py +167 -0
  205. pyrrhon-0.1.0/tests/test_tui_messages.py +119 -0
  206. pyrrhon-0.1.0/tests/test_tui_palette.py +79 -0
  207. pyrrhon-0.1.0/tests/test_tui_status.py +117 -0
  208. pyrrhon-0.1.0/tests/test_tui_theme.py +65 -0
  209. pyrrhon-0.1.0/tests/test_tui_turns.py +300 -0
  210. pyrrhon-0.1.0/tests/test_tui_voice_turns.py +268 -0
  211. pyrrhon-0.1.0/tests/test_turn_policy.py +233 -0
  212. pyrrhon-0.1.0/tests/test_turn_type.py +145 -0
  213. pyrrhon-0.1.0/tests/test_voice_bridge.py +481 -0
  214. pyrrhon-0.1.0/tests/test_voice_cmd.py +42 -0
  215. pyrrhon-0.1.0/tests/test_voice_factory.py +155 -0
  216. pyrrhon-0.1.0/tests/test_voice_live.py +265 -0
  217. pyrrhon-0.1.0/tests/test_voice_pipeline.py +270 -0
  218. pyrrhon-0.1.0/tests/test_voice_registry.py +185 -0
  219. pyrrhon-0.1.0/tests/test_voice_streaming.py +187 -0
  220. pyrrhon-0.1.0/tests/test_voice_style.py +86 -0
  221. pyrrhon-0.1.0/tests/test_voice_terminal_hygiene.py +46 -0
  222. pyrrhon-0.1.0/tests/test_warm_index.py +107 -0
  223. pyrrhon-0.1.0/tests/test_web_tools.py +165 -0
  224. pyrrhon-0.1.0/tests/test_wizard.py +361 -0
@@ -0,0 +1,220 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Database
10
+ *.db
11
+
12
+ # Docs
13
+ /docs
14
+
15
+ # Distribution / packaging
16
+ # build/, dist/ and lib/ are anchored with a leading slash on purpose. A
17
+ # gitignore pattern without one matches at every depth, so a bare `lib/`
18
+ # also swallowed web/lib/utils.ts -- the shadcn `cn()` helper that most of
19
+ # the site's components import. That builds fine locally, where the file is
20
+ # on disk, and fails the deploy with "Can't resolve '@/lib/utils'". These
21
+ # are Python build artifacts, and they only ever appear at the repo root.
22
+ .Python
23
+ /build/
24
+ develop-eggs/
25
+ /dist/
26
+ downloads/
27
+ eggs/
28
+ .eggs/
29
+ /lib/
30
+ lib64/
31
+ parts/
32
+ sdist/
33
+ var/
34
+ wheels/
35
+ share/python-wheels/
36
+ *.egg-info/
37
+ .installed.cfg
38
+ *.egg
39
+ MANIFEST
40
+
41
+ # PyInstaller
42
+ # Usually these files are written by a python script from a template
43
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
44
+ *.manifest
45
+ *.spec
46
+
47
+ # Installer logs
48
+ pip-log.txt
49
+ pip-delete-this-directory.txt
50
+
51
+ # Unit test / coverage reports
52
+ htmlcov/
53
+ .tox/
54
+ .nox/
55
+ .coverage
56
+ .coverage.*
57
+ .cache
58
+ nosetests.xml
59
+ coverage.xml
60
+ *.cover
61
+ *.py.cover
62
+ .hypothesis/
63
+ .pytest_cache/
64
+ cover/
65
+
66
+ # Translations
67
+ *.mo
68
+ *.pot
69
+
70
+ # Django stuff:
71
+ *.log
72
+ local_settings.py
73
+ db.sqlite3
74
+ db.sqlite3-journal
75
+
76
+ # Flask stuff:
77
+ instance/
78
+ .webassets-cache
79
+
80
+ # Scrapy stuff:
81
+ .scrapy
82
+
83
+ # Sphinx documentation
84
+ docs/_build/
85
+
86
+ # PyBuilder
87
+ .pybuilder/
88
+ target/
89
+
90
+ # Jupyter Notebook
91
+ .ipynb_checkpoints
92
+
93
+ # IPython
94
+ profile_default/
95
+ ipython_config.py
96
+
97
+ # pyenv
98
+ # For a library or package, you might want to ignore these files since the code is
99
+ # intended to run in multiple environments; otherwise, check them in:
100
+ # .python-version
101
+
102
+ # pipenv
103
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
104
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
105
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
106
+ # install all needed dependencies.
107
+ #Pipfile.lock
108
+
109
+ # UV
110
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
111
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
112
+ # commonly ignored for libraries.
113
+ #uv.lock
114
+
115
+ # poetry
116
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
117
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
118
+ # commonly ignored for libraries.
119
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
120
+ #poetry.lock
121
+ #poetry.toml
122
+
123
+ # pdm
124
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
125
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
126
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
127
+ #pdm.lock
128
+ #pdm.toml
129
+ .pdm-python
130
+ .pdm-build/
131
+
132
+ # pixi
133
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
134
+ #pixi.lock
135
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
136
+ # in the .venv directory. It is recommended not to include this directory in version control.
137
+ .pixi
138
+
139
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
140
+ __pypackages__/
141
+
142
+ # Celery stuff
143
+ celerybeat-schedule
144
+ celerybeat.pid
145
+
146
+ # SageMath parsed files
147
+ *.sage.py
148
+
149
+ # Environments
150
+ .env
151
+ .envrc
152
+ .venv
153
+ env/
154
+ venv/
155
+ ENV/
156
+ env.bak/
157
+ venv.bak/
158
+
159
+ # Spyder project settings
160
+ .spyderproject
161
+ .spyproject
162
+
163
+ # Rope project settings
164
+ .ropeproject
165
+
166
+ # mkdocs documentation
167
+ /site
168
+
169
+ # mypy
170
+ .mypy_cache/
171
+ .dmypy.json
172
+ dmypy.json
173
+
174
+ # Pyre type checker
175
+ .pyre/
176
+
177
+ # pytype static type analyzer
178
+ .pytype/
179
+
180
+ # Cython debug symbols
181
+ cython_debug/
182
+
183
+ # PyCharm
184
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
185
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
186
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
187
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
188
+ #.idea/
189
+
190
+ # Abstra
191
+ # Abstra is an AI-powered process automation framework.
192
+ # Ignore directories containing user credentials, local state, and settings.
193
+ # Learn more at https://abstra.io/docs
194
+ .abstra/
195
+
196
+ # Visual Studio Code
197
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
198
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
199
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
200
+ # you could uncomment the following to ignore the entire vscode folder
201
+ # .vscode/
202
+
203
+ # Ruff stuff:
204
+ .ruff_cache/
205
+
206
+ # PyPI configuration file
207
+ .pypirc
208
+
209
+ # Cursor
210
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
211
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
212
+ # refer to https://docs.cursor.com/context/ignore-files
213
+ .cursorignore
214
+ .cursorindexingignore
215
+
216
+ # Marimo
217
+ marimo/_static/
218
+ marimo/_lsp/
219
+ __marimo__/
220
+ .gstack/
pyrrhon-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Prabhjot Singh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pyrrhon-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,448 @@
1
+ Metadata-Version: 2.5
2
+ Name: pyrrhon
3
+ Version: 0.1.0
4
+ Summary: A voice-first senior-engineer agent for understanding and designing software.
5
+ Project-URL: Homepage, https://github.com/prabhjot0109/Pyrrhon
6
+ Project-URL: Source, https://github.com/prabhjot0109/Pyrrhon
7
+ Project-URL: Issues, https://github.com/prabhjot0109/Pyrrhon/issues
8
+ Project-URL: Changelog, https://github.com/prabhjot0109/Pyrrhon/releases
9
+ Author: Prabhjot Singh
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agent,code-comprehension,codebase,grounding,llm,terminal,tui,voice
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
19
+ Classifier: Topic :: Software Development :: Documentation
20
+ Requires-Python: <3.14,>=3.12
21
+ Requires-Dist: ddgs>=9.14.4
22
+ Requires-Dist: google-genai>=2.10.0
23
+ Requires-Dist: html2text>=2025.4.15
24
+ Requires-Dist: httpx>=0.28.1
25
+ Requires-Dist: huggingface-hub>=1.22.0
26
+ Requires-Dist: mcp<2,>=1.10
27
+ Requires-Dist: openai>=2.44.0
28
+ Requires-Dist: pipecat-ai[assemblyai,cartesia,deepgram,elevenlabs,gladia,google,groq,inworld,local,openai,rime,rnnoise,silero]>=1.7.0
29
+ Requires-Dist: pydantic>=2.12.5
30
+ Requires-Dist: pyyaml>=6.0.3
31
+ Requires-Dist: rich>=14.3.1
32
+ Requires-Dist: soundfile>=0.14.0
33
+ Requires-Dist: textual>=8.2.8
34
+ Requires-Dist: tomli-w>=1.2.0
35
+ Requires-Dist: tree-sitter-language-pack>=1.12.2
36
+ Requires-Dist: tree-sitter<0.26,>=0.25.2
37
+ Provides-Extra: kokoro
38
+ Requires-Dist: pipecat-ai[kokoro]>=1.7.0; extra == 'kokoro'
39
+ Provides-Extra: local-voice
40
+ Requires-Dist: pipecat-ai[kokoro,local-smart-turn,moonshine,piper,whisper]>=1.7.0; extra == 'local-voice'
41
+ Provides-Extra: moonshine
42
+ Requires-Dist: pipecat-ai[moonshine]>=1.7.0; extra == 'moonshine'
43
+ Provides-Extra: piper
44
+ Requires-Dist: pipecat-ai[piper]>=1.7.0; extra == 'piper'
45
+ Provides-Extra: smart-turn
46
+ Requires-Dist: pipecat-ai[local-smart-turn]>=1.7.0; extra == 'smart-turn'
47
+ Provides-Extra: whisper
48
+ Requires-Dist: pipecat-ai[whisper]>=1.7.0; extra == 'whisper'
49
+ Description-Content-Type: text/markdown
50
+
51
+ # Pyrrhon
52
+
53
+ **A voice-first engineering agent for your terminal.** You talk to it, it talks
54
+ back, and every claim it makes about your code cites a real `file:line` — or it
55
+ says it does not know. You can interrupt it mid-sentence.
56
+
57
+ [![CI](https://github.com/prabhjot0109/Pyrrhon/actions/workflows/ci.yml/badge.svg)](https://github.com/prabhjot0109/Pyrrhon/actions/workflows/ci.yml)
58
+ [![Python 3.12 | 3.13](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue)](https://www.python.org/)
59
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
60
+
61
+ Named for Pyrrho the skeptic: suspend judgment, question assumptions, never
62
+ bluff. Pyrrhon does two things.
63
+
64
+ **Understand a codebase you didn't write.** Ask how a feature works, where to
65
+ add something, or what changed last week. Every answer cites a real location or
66
+ admits ignorance — it will not invent a path to sound complete — and the
67
+ terminal shows the code while you talk.
68
+
69
+ **Design a system you're about to build.** It interrogates the idea the way a
70
+ senior architect would ("your data looks relational, so what do you expect
71
+ Mongo to buy you over Postgres?"), then writes the spec once the reasoning holds
72
+ up: `PRD.md`, `HLD.md`, `LLD.md`, `api.md`, `database.md`, `risks.md`.
73
+
74
+ ---
75
+
76
+ ## Status
77
+
78
+ Pre-1.0, and honest about which parts are which.
79
+
80
+ | | |
81
+ |---|---|
82
+ | **Built and tested** | The agent harness, the grounding gate, both channels, the voice pipeline, session persistence, plugins, MCP. ~1260 tests, no API key required. |
83
+ | **Measured** | Latency (M10), the tool policy (a fifth of tool rounds removed), context handling under a real 8k-token ceiling. |
84
+ | **Not yet measured** | Answer quality on a repo neither author wrote. Two stranger repos are frozen with 34 hand-derived cases in [`evals/strangers/`](evals/strangers/), waiting on a funded provider key. |
85
+ | **Deliberately absent** | Any tool that writes outside `.pyrrhon/`. Speech-to-speech (it would bypass the grounding gate). |
86
+
87
+ Pyrrhon is a *teaching* agent, not an editing one. It reads your repo and
88
+ explains it. It does not change your code, and there is no plan for it to.
89
+
90
+ ## Install
91
+
92
+ Python 3.12 or 3.13. Not 3.14 yet — `pyaudio`, which the voice stack pulls in,
93
+ ships no wheel for it, and a version cap turns that into one clear sentence
94
+ instead of a compiler error.
95
+
96
+ ```bash
97
+ uv tool install pyrrhon # recommended: isolated, with `pyrrhon` on PATH
98
+ pipx install pyrrhon # same idea without uv
99
+ pip install pyrrhon # into the current environment
100
+ ```
101
+
102
+ One command installs everything, every speech provider included. Nothing in the
103
+ provider menu needs a second install step. The cost is a ~1.2 GB environment,
104
+ because on-device turn detection and the local speech engines bring their own
105
+ model runtimes. Text and TUI never import the audio stack at runtime, and
106
+ `--voice` degrades to text with a readable reason if it cannot start.
107
+
108
+ To work on Pyrrhon itself:
109
+
110
+ ```bash
111
+ git clone https://github.com/prabhjot0109/Pyrrhon && cd Pyrrhon
112
+ uv sync
113
+ ```
114
+
115
+ ## Quickstart
116
+
117
+ ```bash
118
+ pyrrhon --setup # pick a provider, paste a key
119
+ pyrrhon /path/to/some/repo # ask it anything
120
+ ```
121
+
122
+ One LLM key is enough. Groq is the default:
123
+
124
+ ```bash
125
+ export GROQ_API_KEY=gsk_... # PowerShell: $env:GROQ_API_KEY = "gsk_..."
126
+ ```
127
+
128
+ Keys from the wizard are stored owner-only in `~/.pyrrhon/credentials.toml`,
129
+ never in project config, and environment variables always win. `/settings`
130
+ shows what is configured and where each value came from.
131
+
132
+ ## Usage
133
+
134
+ ### Channels
135
+
136
+ ```bash
137
+ pyrrhon . # Textual TUI (default)
138
+ pyrrhon --text . # plain-text REPL
139
+ pyrrhon --voice . # TUI with the voice pipeline on
140
+ ```
141
+
142
+ Voice needs a key for whichever STT/TTS providers you pick and nothing else.
143
+ Inside the TUI, `/voice on|off` toggles it. Start talking whenever, interrupt
144
+ whenever: barge-in rewrites history to exactly what you heard.
145
+
146
+ ### Sessions
147
+
148
+ A walkthrough of a large codebase takes days, so sessions survive the night.
149
+
150
+ ```bash
151
+ pyrrhon --continue . # resume this repo's most recent session
152
+ pyrrhon --resume 20260901 . # a specific one; a leading prefix is enough
153
+ pyrrhon --no-save . # keep nothing
154
+ ```
155
+
156
+ Only the questions and answers are saved — never tool output, and never a line
157
+ number. A resumed session therefore has to reopen a file before it can cite
158
+ one, which is the grounding rule enforced by the shape of the data rather than
159
+ by asking the model nicely.
160
+
161
+ ### Headless
162
+
163
+ ```bash
164
+ pyrrhon --print "how does the retry path work?" .
165
+ echo "where is the cache invalidated?" | pyrrhon -p --json .
166
+ ```
167
+
168
+ The answer goes to stdout and nothing else does; progress goes to stderr, and
169
+ `--json` adds the citations and the turn's trace as data. Exit code is non-zero
170
+ when the turn failed.
171
+
172
+ ### Commands
173
+
174
+ | Command | What it does |
175
+ |---|---|
176
+ | `/help`, `/exit` | The command table; leave. Type `/` or press `ctrl+p` to search live. |
177
+ | `/mode design` | Switch to Act 2. `/mode understand` switches back. |
178
+ | `/model fast groq/llama-3.3-70b-versatile` | Repoint a model slot for this session. |
179
+ | `/code`, `ctrl+o` | Open the last citation: `/code` in VS Code, `ctrl+o` in `$VISUAL`/`$EDITOR` at the line. |
180
+ | `/covered` | What this session has established so far. |
181
+ | `/clear`, `/compact` | Fresh thread, same window; or summarize now and say by how much. |
182
+ | `/cost` | Tokens and requests spent this session, and the ceilings it knows about. |
183
+ | `/export [path]` | The walkthrough as markdown, with its citations. |
184
+ | `/sessions` | What `--resume` can pick up. |
185
+ | `/settings`, `/mcp`, `/plugins` | Configuration, MCP servers, loaded plugins. |
186
+ | `/voice on\|off`, `/debug-history` | Toggle speech; dump the raw conversation. |
187
+
188
+ ## Configuration
189
+
190
+ Settings merge from `~/.pyrrhon/config.toml`, then `<repo>/.pyrrhon.toml`, where
191
+ the repo wins. All of it is optional.
192
+
193
+ ```toml
194
+ [fast] # every turn
195
+ provider = "groq"
196
+ model = "llama-3.3-70b-versatile"
197
+
198
+ [deep] # think_deeper escalation only
199
+ provider = "openai"
200
+ model = "o4-mini"
201
+
202
+ [fallbacks]
203
+ fast = ["cerebras", "openrouter/meta-llama/llama-3.3-70b-instruct"]
204
+
205
+ [context]
206
+ budget_tokens = 32000 # before old turns are summarized
207
+ keep_last_messages = 8 # recent messages always kept verbatim
208
+
209
+ [providers.myllm] # any OpenAI-compatible endpoint
210
+ base_url = "http://localhost:8000/v1"
211
+ api_key_env = "MYLLM_KEY"
212
+
213
+ [mcp_servers.docs] # MCP tools join the agent's toolset
214
+ command = "docs-mcp" # stdio, or: url = "http://..." for HTTP
215
+ ```
216
+
217
+ Built-in providers are `groq`, `openai`, `anthropic`, `gemini`, `deepseek`,
218
+ `cerebras`, `openrouter`, and `huggingface`, each needing only its `*_API_KEY`
219
+ environment variable (Hugging Face uses `HF_TOKEN`). Two keyless local providers
220
+ ship as well: `ollama` and `lmstudio`. No model ids are hardcoded anywhere —
221
+ they rot faster than anything else here, so you name the model.
222
+
223
+ Drop markdown **soul files** into `~/.pyrrhon/` or `<repo>/.pyrrhon/` to give
224
+ Pyrrhon standing context about you or the repo. `/init` scaffolds one.
225
+
226
+ ### Voice providers
227
+
228
+ ```toml
229
+ [voice]
230
+ stt_provider = "groq"
231
+ tts_provider = "cartesia" # openai is the default
232
+ tts_voice = "<voice-id>" # provider-specific: an OpenAI/Groq name, a Gemini voice, a Cartesia/ElevenLabs id
233
+ tts_model = "sonic-2" # optional
234
+ turn_detection = "smart" # semantic end-of-turn; "vad" is the fixed-silence fallback
235
+ idle_timeout_sec = 0 # seconds of your silence before it re-engages; 0 is off
236
+ ```
237
+
238
+ | Task | Cloud (API key) | Local (keyless) |
239
+ |---|---|---|
240
+ | **LLM** | Groq, OpenAI, Anthropic, Gemini, DeepSeek, Cerebras, OpenRouter, Hugging Face | Ollama, LM Studio |
241
+ | **STT** | Groq Whisper, OpenAI, Deepgram, Cartesia, AssemblyAI, Gladia, Gemini, Hugging Face | whisper-local (faster-whisper, any HF id), Moonshine |
242
+ | **TTS** | OpenAI, Groq, Cartesia, ElevenLabs, Deepgram Aura, Rime, Inworld, Gemini, Hugging Face | Piper (in-process), Kokoro |
243
+
244
+ OpenAI TTS is the zero-setup default. For real-time conversation, Cartesia and
245
+ ElevenLabs are noticeably snappier — roughly 100–300 ms to first audio against
246
+ 400 ms and up. A fully local, keyless stack works too: `whisper-local`, `piper`,
247
+ and Ollama or LM Studio.
248
+
249
+ **Pyrrhon will offer you a provider it cannot currently run. It will never imply
250
+ that it can.** `/settings stt|tts` labels every row with what it would take:
251
+
252
+ ```
253
+ /settings tts
254
+ piper [ready] free, on-device, no key and no server
255
+ groq [ready] hosted TTS on your Groq key; needs an explicit voice id
256
+ openai [ready, unverified] no extra key if you already use OpenAI
257
+ cartesia [needs CARTESIA_API_KEY] lowest latency; needs a voice id from your account
258
+ kokoro [install: uv add "pipecat-ai[kokoro]"] free, on-device, ONNX
259
+ ```
260
+
261
+ `ready, unverified` is the honest one: installed and keyed, but nobody has
262
+ pushed a real utterance through it. A row earns plain `ready` only after
263
+ `pytest tests/test_voice_live.py -m live` has actually made it speak.
264
+
265
+ Gemini Live speech-to-speech is deliberately absent. It generates speech
266
+ directly from audio, which skips the agent loop and the grounding gate. Gemini
267
+ participates as LLM, STT, and TTS instead, each behind the gate.
268
+
269
+ ## Security model
270
+
271
+ Pyrrhon is built so a voice agent cannot damage your repo even when the model is
272
+ wrong.
273
+
274
+ - **Read-only by construction.** The only write tool is `write_spec`, which
275
+ accepts exactly six filenames and writes only under `docs/design/`. Everything
276
+ else Pyrrhon writes lives under `.pyrrhon/`.
277
+ - **No shell.** Git access is three read-only subcommands (`log`, `blame`,
278
+ `show`) run as argv lists, so there is no command string to inject into. Paths
279
+ are resolved and rejected if they escape the repo.
280
+ - **Grounded speech.** Every `file:line` is checked against the real repo before
281
+ it is spoken. Unverifiable claims are stripped; real-but-unopened ones are
282
+ downgraded to a bare path with a hedge.
283
+ - **Keys out of the repo.** Owner-only in `~/.pyrrhon/credentials.toml`, with
284
+ environment variables taking precedence.
285
+
286
+ `tests/test_safety.py` and `tests/test_repo_trust_boundary.py` pin these, and
287
+ the tool belt itself is a reviewed set encoded as a test.
288
+
289
+ ### What a cloned repo may and may not do
290
+
291
+ A repo you have never read is untrusted input. Its `.pyrrhon.toml` and
292
+ `.pyrrhon/` directory are that stranger's *suggestions*, so keys are split by
293
+ what each one can actually do.
294
+
295
+ | Repo supplies | Effect |
296
+ |---|---|
297
+ | `[voice]` (except `tts_url`), `[model]`, `[context]` | Applied. Cosmetic knobs. |
298
+ | `[fast]`, `[deep]`, `[fallbacks]` naming a builtin or *your* provider | Applied. A repo may suggest `groq/llama-3.3`. |
299
+ | `[mcp_servers.*]` | **Needs consent.** It launches a program. |
300
+ | `[providers.*]` | **Needs consent.** It decides where your prompts and API key go. |
301
+ | `voice.tts_url` | **Needs consent.** Piper HTTP mode POSTs everything Pyrrhon says to that URL. |
302
+ | `[fast]`/`[deep]`/`[fallbacks]` naming a provider *the repo itself defined* | **Needs consent.** Same key redirection, one indirection away. |
303
+ | `.pyrrhon/*.md` (soul files) | **Needs consent.** They enter the system prompt, so an ungated one can tell Pyrrhon to stop citing sources. |
304
+ | `.pyrrhon/plugins/*` with tools or commands | **Needs consent.** It is Python that Pyrrhon would import and run. |
305
+
306
+ Everything needing consent is collected into one startup prompt. Declining is
307
+ not fatal: the session opens with whatever permissions it already has.
308
+
309
+ Consent is recorded in `<repo>/.pyrrhon/trusted` and bound to the value's
310
+ **content**, not its name. Approving `mcp_servers.indexer = "node ./build.js"`
311
+ approves that command; if the repo later changes it, you are asked again.
312
+ Editing an approved soul file re-prompts for the same reason. Your own
313
+ `~/.pyrrhon/config.toml` is never gated, and files Pyrrhon writes itself
314
+ self-grant.
315
+
316
+ For automation, `pyrrhon --trust-repo` grants everything without prompting — it
317
+ runs programs the repo chose, so use it only where you already trust the repo. A
318
+ run with no interactive terminal (piped stdin, CI) refuses silently rather than
319
+ hanging or auto-approving.
320
+
321
+ ## How it works
322
+
323
+ Headless core, thin channels. `pyrrhon/core/` imports no channel; channels
324
+ subscribe to one typed event stream and render it however they like.
325
+
326
+ ```mermaid
327
+ flowchart TB
328
+ subgraph ch["Channels — thin, swappable"]
329
+ TUI["TUI (Textual)"]
330
+ REPL["Text REPL (rich)"]
331
+ HL["Headless (--print)"]
332
+ VOICE["Voice (Pipecat)"]
333
+ end
334
+
335
+ EV(["Event stream — one dispatch table<br/>SpeechChunk · Citation · ToolCall* · AskUser ·<br/>Transcription · TruncateSpeech · TurnFinished"])
336
+
337
+ subgraph core["pyrrhon/core — headless"]
338
+ SESSION["Session · cancellable turns, modes, transcript"]
339
+ LOOP["Agent loop · LLM ⇄ tools, streaming, recovery"]
340
+ POLICY["Turn policy · what this turn may spend"]
341
+ CTX["Context ladder · elide → summarize, learned limits"]
342
+ GATE["Grounding gate · promote / hedge / strip"]
343
+ LEDGER["Evidence ledger · what a tool actually showed"]
344
+ TOOLS["Tool belt · read · search · symbols · git · web ·<br/>explore · think_deeper"]
345
+ end
346
+
347
+ BOOT["pyrrhon/bootstrap.py — the one composition root<br/>settings · trust gate · plugins · MCP · prompts"]
348
+
349
+ TUI <--> EV
350
+ REPL <--> EV
351
+ HL <--> EV
352
+ VOICE <--> EV
353
+ EV <--> SESSION
354
+ SESSION --> LOOP
355
+ LOOP --> POLICY
356
+ LOOP --> CTX
357
+ LOOP --> TOOLS
358
+ LOOP --> GATE
359
+ TOOLS --> LEDGER
360
+ LEDGER --> GATE
361
+ BOOT --> LOOP
362
+ ```
363
+
364
+ Four invariants hold across the codebase.
365
+
366
+ **Grounding is mandatory, and verification is separate from delivery.** The gate
367
+ sorts every reference three ways: observed becomes a citation, real-but-unopened
368
+ is downgraded to a bare path with a hedge, unverified is stripped. The screen
369
+ shows `path:line`; the voice never speaks it, because "loop dot py colon one
370
+ nine three" is unusable to a listener.
371
+
372
+ **A location is admissible only from a tool result in *this* turn.** Not from
373
+ memory of an earlier turn, not from a summary, not from a persisted transcript.
374
+ The file may have changed, and a stale in-range line passes every check the gate
375
+ makes.
376
+
377
+ **Real-time discipline holds everywhere in `core/`.** Nothing blocking runs
378
+ inline in an `async def`; it goes through `asyncio.to_thread`, so audio never
379
+ glitches while a tool reads disk.
380
+
381
+ **There is one composition point.** `pyrrhon/bootstrap.py` assembles the LLM,
382
+ tools, prompt, MCP servers, and plugins. Nothing else constructs an `Agent`.
383
+
384
+ For the full internals — how one turn actually flows, why each subsystem is
385
+ shaped the way it is, and where to look when something breaks —
386
+ [CLAUDE.md](CLAUDE.md) carries the decision record for every subsystem, written
387
+ as the working reference rather than as marketing.
388
+
389
+ ## Plugins
390
+
391
+ A plugin is a folder with a `plugin.toml`, dropped into `~/.pyrrhon/plugins/`
392
+ for global scope or `<repo>/.pyrrhon/plugins/` for one repo.
393
+
394
+ ```toml
395
+ name = "hello-reviewer"
396
+ version = "0.1.0"
397
+
398
+ [contributes]
399
+ prompts = ["prompts/*.md"] # appended to the system prompt
400
+ tools = "tools.py:get_tools" # Python entry point returning list[Tool]
401
+ ```
402
+
403
+ Plugins contribute prompts, tools, slash commands, LLM providers, and MCP
404
+ servers. All of it is additive, so a plugin never overrides your config or the
405
+ built-ins. Prompts and config load from anywhere, but repo-level plugin *code*
406
+ runs only after a one-time consent prompt. A broken plugin is skipped with one
407
+ warning instead of crashing the session, and `/plugins` shows what loaded. See
408
+ the worked example in
409
+ [`tests/fixtures/plugins/hello-reviewer/`](tests/fixtures/plugins/hello-reviewer/).
410
+
411
+ ## Development
412
+
413
+ ```bash
414
+ uv run pytest # full suite, ~90s, no API keys
415
+ uv run pytest tests/test_session.py::test_name # one test
416
+ uv run ruff check . && uv run mypy pyrrhon/core # both gate CI
417
+ ```
418
+
419
+ Evals need a real provider key and are not part of `pytest` — the suite tests
420
+ the *runners*, the runners test the *agent*.
421
+
422
+ ```bash
423
+ uv run python -m pyrrhon.evals.strangers # fetch the frozen stranger repos
424
+ uv run python -m pyrrhon.evals.grounding evals/grounding.yaml
425
+ uv run python -m pyrrhon.evals.design evals/design.yaml --repo .
426
+ ```
427
+
428
+ Both runners take `--model provider/model` to override the fast slot for one
429
+ run, and the grounding runner **exits non-zero and refuses to certify a score
430
+ when any turn ended in an error** — a set of "must admit ignorance" cases passes
431
+ perfectly when nothing answers at all.
432
+
433
+ | Path | What lives there |
434
+ |---|---|
435
+ | `pyrrhon/core/` | The headless agent. Imports nothing from outer layers. |
436
+ | `pyrrhon/bootstrap.py` | Composition root: builds the Agent, runs the trust gate. |
437
+ | `pyrrhon/channels.py` | The event-to-renderer dispatch table shared by channels. |
438
+ | `pyrrhon/repl.py`, `pyrrhon/tui/`, `pyrrhon/voice/`, `pyrrhon/headless.py` | The four channels. |
439
+ | `pyrrhon/commands/` | Slash commands, registered by decorator. |
440
+ | `pyrrhon/config/`, `pyrrhon/plugins/` | Settings, credentials, trust, plugin loader. |
441
+ | `pyrrhon/evals/` | The grounding and design harnesses, and the frozen stranger repos. |
442
+
443
+ [CLAUDE.md](CLAUDE.md) is the working reference: the layering rule, the decision
444
+ record behind each subsystem, and how to add a tool, event, command, or provider.
445
+
446
+ ## License
447
+
448
+ MIT. See [LICENSE](LICENSE).