rbtr 2026.3.0.dev0__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 (199) hide show
  1. rbtr-2026.3.0.dev0/PKG-INFO +1135 -0
  2. rbtr-2026.3.0.dev0/README.md +1086 -0
  3. rbtr-2026.3.0.dev0/pyproject.toml +237 -0
  4. rbtr-2026.3.0.dev0/src/rbtr/__init__.py +1 -0
  5. rbtr-2026.3.0.dev0/src/rbtr/cli.py +59 -0
  6. rbtr-2026.3.0.dev0/src/rbtr/config.py +472 -0
  7. rbtr-2026.3.0.dev0/src/rbtr/creds.py +83 -0
  8. rbtr-2026.3.0.dev0/src/rbtr/engine/__init__.py +1 -0
  9. rbtr-2026.3.0.dev0/src/rbtr/engine/connect_cmd.py +303 -0
  10. rbtr-2026.3.0.dev0/src/rbtr/engine/core.py +372 -0
  11. rbtr-2026.3.0.dev0/src/rbtr/engine/draft_cmd.py +235 -0
  12. rbtr-2026.3.0.dev0/src/rbtr/engine/index_cmd.py +508 -0
  13. rbtr-2026.3.0.dev0/src/rbtr/engine/indexing.py +160 -0
  14. rbtr-2026.3.0.dev0/src/rbtr/engine/memory_cmd.py +118 -0
  15. rbtr-2026.3.0.dev0/src/rbtr/engine/model_cmd.py +178 -0
  16. rbtr-2026.3.0.dev0/src/rbtr/engine/publish.py +642 -0
  17. rbtr-2026.3.0.dev0/src/rbtr/engine/reload_cmd.py +74 -0
  18. rbtr-2026.3.0.dev0/src/rbtr/engine/review_cmd.py +345 -0
  19. rbtr-2026.3.0.dev0/src/rbtr/engine/session_cmd.py +378 -0
  20. rbtr-2026.3.0.dev0/src/rbtr/engine/setup.py +122 -0
  21. rbtr-2026.3.0.dev0/src/rbtr/engine/shell.py +81 -0
  22. rbtr-2026.3.0.dev0/src/rbtr/engine/skill_cmd.py +75 -0
  23. rbtr-2026.3.0.dev0/src/rbtr/engine/stats_cmd.py +340 -0
  24. rbtr-2026.3.0.dev0/src/rbtr/engine/types.py +72 -0
  25. rbtr-2026.3.0.dev0/src/rbtr/events.py +267 -0
  26. rbtr-2026.3.0.dev0/src/rbtr/exceptions.py +13 -0
  27. rbtr-2026.3.0.dev0/src/rbtr/git/__init__.py +41 -0
  28. rbtr-2026.3.0.dev0/src/rbtr/git/filters.py +57 -0
  29. rbtr-2026.3.0.dev0/src/rbtr/git/objects.py +588 -0
  30. rbtr-2026.3.0.dev0/src/rbtr/git/repo.py +125 -0
  31. rbtr-2026.3.0.dev0/src/rbtr/github/__init__.py +1 -0
  32. rbtr-2026.3.0.dev0/src/rbtr/github/auth.py +80 -0
  33. rbtr-2026.3.0.dev0/src/rbtr/github/client.py +648 -0
  34. rbtr-2026.3.0.dev0/src/rbtr/github/draft.py +474 -0
  35. rbtr-2026.3.0.dev0/src/rbtr/index/__init__.py +1 -0
  36. rbtr-2026.3.0.dev0/src/rbtr/index/arrow.py +125 -0
  37. rbtr-2026.3.0.dev0/src/rbtr/index/chunks.py +116 -0
  38. rbtr-2026.3.0.dev0/src/rbtr/index/edges.py +495 -0
  39. rbtr-2026.3.0.dev0/src/rbtr/index/embeddings.py +221 -0
  40. rbtr-2026.3.0.dev0/src/rbtr/index/languages.py +49 -0
  41. rbtr-2026.3.0.dev0/src/rbtr/index/models.py +162 -0
  42. rbtr-2026.3.0.dev0/src/rbtr/index/orchestrator.py +487 -0
  43. rbtr-2026.3.0.dev0/src/rbtr/index/search.py +381 -0
  44. rbtr-2026.3.0.dev0/src/rbtr/index/sql/clear_embeddings.sql +3 -0
  45. rbtr-2026.3.0.dev0/src/rbtr/index/sql/count_orphan_chunks.sql +9 -0
  46. rbtr-2026.3.0.dev0/src/rbtr/index/sql/delete_edges.sql +2 -0
  47. rbtr-2026.3.0.dev0/src/rbtr/index/sql/delete_snapshots.sql +2 -0
  48. rbtr-2026.3.0.dev0/src/rbtr/index/sql/diff_added.sql +24 -0
  49. rbtr-2026.3.0.dev0/src/rbtr/index/sql/diff_modified.sql +23 -0
  50. rbtr-2026.3.0.dev0/src/rbtr/index/sql/get_chunks.sql +25 -0
  51. rbtr-2026.3.0.dev0/src/rbtr/index/sql/get_edges.sql +10 -0
  52. rbtr-2026.3.0.dev0/src/rbtr/index/sql/has_blob.sql +4 -0
  53. rbtr-2026.3.0.dev0/src/rbtr/index/sql/inbound_degree.sql +8 -0
  54. rbtr-2026.3.0.dev0/src/rbtr/index/sql/insert_edges.sql +7 -0
  55. rbtr-2026.3.0.dev0/src/rbtr/index/sql/insert_snapshot.sql +1 -0
  56. rbtr-2026.3.0.dev0/src/rbtr/index/sql/prune_chunks.sql +8 -0
  57. rbtr-2026.3.0.dev0/src/rbtr/index/sql/prune_edges.sql +3 -0
  58. rbtr-2026.3.0.dev0/src/rbtr/index/sql/schema.sql +43 -0
  59. rbtr-2026.3.0.dev0/src/rbtr/index/sql/search_by_name.sql +23 -0
  60. rbtr-2026.3.0.dev0/src/rbtr/index/sql/search_fulltext.sql +31 -0
  61. rbtr-2026.3.0.dev0/src/rbtr/index/sql/search_similar.sql +25 -0
  62. rbtr-2026.3.0.dev0/src/rbtr/index/sql/update_embedding.sql +3 -0
  63. rbtr-2026.3.0.dev0/src/rbtr/index/sql/update_embeddings.sql +4 -0
  64. rbtr-2026.3.0.dev0/src/rbtr/index/sql/upsert_chunks.sql +28 -0
  65. rbtr-2026.3.0.dev0/src/rbtr/index/sql/upsert_snapshots.sql +6 -0
  66. rbtr-2026.3.0.dev0/src/rbtr/index/store.py +748 -0
  67. rbtr-2026.3.0.dev0/src/rbtr/index/tokenise.py +77 -0
  68. rbtr-2026.3.0.dev0/src/rbtr/index/treesitter.py +176 -0
  69. rbtr-2026.3.0.dev0/src/rbtr/llm/__init__.py +1 -0
  70. rbtr-2026.3.0.dev0/src/rbtr/llm/agent.py +123 -0
  71. rbtr-2026.3.0.dev0/src/rbtr/llm/compact.py +310 -0
  72. rbtr-2026.3.0.dev0/src/rbtr/llm/context.py +63 -0
  73. rbtr-2026.3.0.dev0/src/rbtr/llm/costs.py +86 -0
  74. rbtr-2026.3.0.dev0/src/rbtr/llm/deps.py +26 -0
  75. rbtr-2026.3.0.dev0/src/rbtr/llm/errors.py +53 -0
  76. rbtr-2026.3.0.dev0/src/rbtr/llm/history.py +810 -0
  77. rbtr-2026.3.0.dev0/src/rbtr/llm/memory.py +608 -0
  78. rbtr-2026.3.0.dev0/src/rbtr/llm/operational_prompts.py +46 -0
  79. rbtr-2026.3.0.dev0/src/rbtr/llm/stream.py +1049 -0
  80. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/__init__.py +6 -0
  81. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/common.py +234 -0
  82. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/discussion.py +99 -0
  83. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/draft.py +332 -0
  84. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/file.py +547 -0
  85. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/git.py +188 -0
  86. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/index.py +329 -0
  87. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/memory.py +80 -0
  88. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/notes.py +70 -0
  89. rbtr-2026.3.0.dev0/src/rbtr/llm/tools/shell.py +138 -0
  90. rbtr-2026.3.0.dev0/src/rbtr/llm/types.py +16 -0
  91. rbtr-2026.3.0.dev0/src/rbtr/log.py +69 -0
  92. rbtr-2026.3.0.dev0/src/rbtr/models.py +266 -0
  93. rbtr-2026.3.0.dev0/src/rbtr/oauth.py +329 -0
  94. rbtr-2026.3.0.dev0/src/rbtr/plugins/README.md +454 -0
  95. rbtr-2026.3.0.dev0/src/rbtr/plugins/__init__.py +6 -0
  96. rbtr-2026.3.0.dev0/src/rbtr/plugins/bash.py +58 -0
  97. rbtr-2026.3.0.dev0/src/rbtr/plugins/c.py +80 -0
  98. rbtr-2026.3.0.dev0/src/rbtr/plugins/cpp.py +79 -0
  99. rbtr-2026.3.0.dev0/src/rbtr/plugins/defaults.py +119 -0
  100. rbtr-2026.3.0.dev0/src/rbtr/plugins/go.py +134 -0
  101. rbtr-2026.3.0.dev0/src/rbtr/plugins/hookspec.py +394 -0
  102. rbtr-2026.3.0.dev0/src/rbtr/plugins/java.py +107 -0
  103. rbtr-2026.3.0.dev0/src/rbtr/plugins/javascript.py +192 -0
  104. rbtr-2026.3.0.dev0/src/rbtr/plugins/manager.py +371 -0
  105. rbtr-2026.3.0.dev0/src/rbtr/plugins/python.py +143 -0
  106. rbtr-2026.3.0.dev0/src/rbtr/plugins/ruby.py +95 -0
  107. rbtr-2026.3.0.dev0/src/rbtr/plugins/rust.py +177 -0
  108. rbtr-2026.3.0.dev0/src/rbtr/prompts/.rumdl.toml +2 -0
  109. rbtr-2026.3.0.dev0/src/rbtr/prompts/__init__.py +222 -0
  110. rbtr-2026.3.0.dev0/src/rbtr/prompts/compact.md +12 -0
  111. rbtr-2026.3.0.dev0/src/rbtr/prompts/index_status.md +20 -0
  112. rbtr-2026.3.0.dev0/src/rbtr/prompts/memory_existing_facts.md +9 -0
  113. rbtr-2026.3.0.dev0/src/rbtr/prompts/memory_extract.md +59 -0
  114. rbtr-2026.3.0.dev0/src/rbtr/prompts/review.md +108 -0
  115. rbtr-2026.3.0.dev0/src/rbtr/prompts/skills.md +16 -0
  116. rbtr-2026.3.0.dev0/src/rbtr/prompts/system.md +34 -0
  117. rbtr-2026.3.0.dev0/src/rbtr/providers/__init__.py +113 -0
  118. rbtr-2026.3.0.dev0/src/rbtr/providers/claude.py +294 -0
  119. rbtr-2026.3.0.dev0/src/rbtr/providers/endpoint.py +257 -0
  120. rbtr-2026.3.0.dev0/src/rbtr/providers/fireworks.py +71 -0
  121. rbtr-2026.3.0.dev0/src/rbtr/providers/google.py +474 -0
  122. rbtr-2026.3.0.dev0/src/rbtr/providers/openai.py +71 -0
  123. rbtr-2026.3.0.dev0/src/rbtr/providers/openai_codex.py +356 -0
  124. rbtr-2026.3.0.dev0/src/rbtr/providers/openrouter.py +76 -0
  125. rbtr-2026.3.0.dev0/src/rbtr/providers/shared.py +74 -0
  126. rbtr-2026.3.0.dev0/src/rbtr/providers/types.py +49 -0
  127. rbtr-2026.3.0.dev0/src/rbtr/scripts/__init__.py +0 -0
  128. rbtr-2026.3.0.dev0/src/rbtr/scripts/bash_complete.sh +88 -0
  129. rbtr-2026.3.0.dev0/src/rbtr/sessions/__init__.py +1 -0
  130. rbtr-2026.3.0.dev0/src/rbtr/sessions/incidents.py +186 -0
  131. rbtr-2026.3.0.dev0/src/rbtr/sessions/kinds.py +178 -0
  132. rbtr-2026.3.0.dev0/src/rbtr/sessions/migrations.py +77 -0
  133. rbtr-2026.3.0.dev0/src/rbtr/sessions/overhead.py +54 -0
  134. rbtr-2026.3.0.dev0/src/rbtr/sessions/scrub.py +42 -0
  135. rbtr-2026.3.0.dev0/src/rbtr/sessions/serialise.py +380 -0
  136. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/.sqlfluff +2 -0
  137. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/compact_mark_message.sql +8 -0
  138. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/complete_message.sql +12 -0
  139. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/delete_fact.sql +2 -0
  140. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/delete_message.sql +6 -0
  141. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/delete_old_facts.sql +2 -0
  142. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/delete_old_sessions.sql +7 -0
  143. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/delete_session.sql +2 -0
  144. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/fact_counts.sql +7 -0
  145. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/fact_scopes.sql +3 -0
  146. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/finalize_request_complete.sql +5 -0
  147. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/finalize_request_header.sql +5 -0
  148. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/find_fact_by_content.sql +16 -0
  149. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/find_latest_summary.sql +13 -0
  150. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/first_kept_timestamp.sql +10 -0
  151. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/get_created_at.sql +5 -0
  152. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/get_fact.sql +11 -0
  153. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/global_incident_failure_stats.sql +20 -0
  154. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/global_incident_repair_stats.sql +9 -0
  155. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/global_model_stats.sql +15 -0
  156. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/global_overhead_stats.sql +59 -0
  157. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/global_stats.sql +13 -0
  158. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/global_tool_stats.sql +23 -0
  159. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/has_messages_after.sql +12 -0
  160. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/has_repair_incident.sql +12 -0
  161. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/incident_failure_stats.sql +24 -0
  162. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/incident_repair_stats.sql +12 -0
  163. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/insert_fact.sql +4 -0
  164. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/insert_fragment.sql +13 -0
  165. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/list_sessions.sql +19 -0
  166. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/load_active_facts.sql +14 -0
  167. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/load_all_facts.sql +14 -0
  168. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/load_message_ids.sql +14 -0
  169. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/load_messages.sql +12 -0
  170. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/migrate_2026030301_to_2026030801.sql +42 -0
  171. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/migrate_2026030801_find_inverted_pairs.sql +33 -0
  172. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/migrate_2026030801_swap_created_at.sql +5 -0
  173. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/reset_compaction.sql +7 -0
  174. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/schema.sql +82 -0
  175. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/search_facts_fts.sql +17 -0
  176. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/search_history.sql +7 -0
  177. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/session_history.sql +7 -0
  178. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/session_overhead_stats.sql +61 -0
  179. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/session_started_at.sql +5 -0
  180. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/session_token_stats.sql +82 -0
  181. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/supersede_fact.sql +5 -0
  182. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/tool_stats.sql +34 -0
  183. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/update_fact_confirmed.sql +5 -0
  184. rbtr-2026.3.0.dev0/src/rbtr/sessions/sql/update_fragment.sql +5 -0
  185. rbtr-2026.3.0.dev0/src/rbtr/sessions/stats.py +391 -0
  186. rbtr-2026.3.0.dev0/src/rbtr/sessions/store.py +1041 -0
  187. rbtr-2026.3.0.dev0/src/rbtr/shell_exec.py +214 -0
  188. rbtr-2026.3.0.dev0/src/rbtr/skills/__init__.py +10 -0
  189. rbtr-2026.3.0.dev0/src/rbtr/skills/discovery.py +204 -0
  190. rbtr-2026.3.0.dev0/src/rbtr/skills/registry.py +86 -0
  191. rbtr-2026.3.0.dev0/src/rbtr/state.py +91 -0
  192. rbtr-2026.3.0.dev0/src/rbtr/styles.py +99 -0
  193. rbtr-2026.3.0.dev0/src/rbtr/tui/__init__.py +1 -0
  194. rbtr-2026.3.0.dev0/src/rbtr/tui/footer.py +176 -0
  195. rbtr-2026.3.0.dev0/src/rbtr/tui/input.py +1019 -0
  196. rbtr-2026.3.0.dev0/src/rbtr/tui/key_encoding.py +110 -0
  197. rbtr-2026.3.0.dev0/src/rbtr/tui/ui.py +1297 -0
  198. rbtr-2026.3.0.dev0/src/rbtr/usage.py +280 -0
  199. rbtr-2026.3.0.dev0/src/rbtr/workspace.py +48 -0
@@ -0,0 +1,1135 @@
1
+ Metadata-Version: 2.4
2
+ Name: rbtr
3
+ Version: 2026.3.0.dev0
4
+ Summary: rbtr — Interactive LLM-powered PR review workbench
5
+ License-Expression: MIT
6
+ Requires-Dist: anyio>=4.0
7
+ Requires-Dist: httpx
8
+ Requires-Dist: pydantic>=2.12.5
9
+ Requires-Dist: pydantic-ai
10
+ Requires-Dist: pydantic-settings[toml]>=2.12.0
11
+ Requires-Dist: tomli-w
12
+ Requires-Dist: pygithub
13
+ Requires-Dist: pygit2
14
+ Requires-Dist: rich
15
+ Requires-Dist: prompt-toolkit>=3.0
16
+ Requires-Dist: minijinja>=2.15.1
17
+ Requires-Dist: tree-sitter>=0.24
18
+ Requires-Dist: tree-sitter-python
19
+ Requires-Dist: tree-sitter-json
20
+ Requires-Dist: tree-sitter-yaml
21
+ Requires-Dist: tree-sitter-toml
22
+ Requires-Dist: tree-sitter-bash
23
+ Requires-Dist: duckdb>=1.0
24
+ Requires-Dist: pyarrow>=17.0
25
+ Requires-Dist: llama-cpp-python
26
+ Requires-Dist: huggingface-hub
27
+ Requires-Dist: pluggy>=1.6.0
28
+ Requires-Dist: uuid-utils>=0.14.1
29
+ Requires-Dist: ruamel-yaml>=0.19.1
30
+ Requires-Dist: python-frontmatter>=1.1.0
31
+ Requires-Dist: tree-sitter-c ; extra == 'languages'
32
+ Requires-Dist: tree-sitter-c-sharp ; extra == 'languages'
33
+ Requires-Dist: tree-sitter-cpp ; extra == 'languages'
34
+ Requires-Dist: tree-sitter-css ; extra == 'languages'
35
+ Requires-Dist: tree-sitter-go ; extra == 'languages'
36
+ Requires-Dist: tree-sitter-hcl ; extra == 'languages'
37
+ Requires-Dist: tree-sitter-html ; extra == 'languages'
38
+ Requires-Dist: tree-sitter-java ; extra == 'languages'
39
+ Requires-Dist: tree-sitter-javascript ; extra == 'languages'
40
+ Requires-Dist: tree-sitter-kotlin ; extra == 'languages'
41
+ Requires-Dist: tree-sitter-ruby ; extra == 'languages'
42
+ Requires-Dist: tree-sitter-rust ; extra == 'languages'
43
+ Requires-Dist: tree-sitter-scala ; extra == 'languages'
44
+ Requires-Dist: tree-sitter-swift ; extra == 'languages'
45
+ Requires-Dist: tree-sitter-typescript ; extra == 'languages'
46
+ Requires-Python: >=3.13
47
+ Provides-Extra: languages
48
+ Description-Content-Type: text/markdown
49
+
50
+ # rbtr
51
+
52
+ An agentic code review harness in the terminal. rbtr
53
+ indexes your repository, reads the diff, understands the
54
+ structure of the code — commit messages, PR description,
55
+ existing discussion — and helps you reason through the
56
+ changes. It writes structured review comments and posts
57
+ them to GitHub.
58
+
59
+ It connects to Claude, GPT, Gemini, and any OpenAI-compatible
60
+ endpoint. Conversations are saved automatically and survive
61
+ crashes. Long sessions compact themselves so the model never
62
+ loses context.
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ # Requires Python 3.13+
68
+ uv tool install -e .
69
+ ```
70
+
71
+ ## Your first review
72
+
73
+ ```bash
74
+ rbtr # launch in a git repo
75
+ ```
76
+
77
+ Connect a provider, select a PR, and ask the model to review:
78
+
79
+ ```text
80
+ you: /connect claude
81
+ Opening browser to sign in with your Claude account…
82
+ Connected to Claude. LLM is ready.
83
+
84
+ you: /review 42
85
+ Fetching PR #42…
86
+ ⟳ indexing 177 files…
87
+ ```
88
+
89
+ The model has 20 tools — it reads files, searches the index,
90
+ inspects the diff, and follows references across the codebase.
91
+ Ask it anything about the changes:
92
+
93
+ ```text
94
+ you: explain the nature of the changes, focus on error handling
95
+
96
+ claude: [reads diff, searches for retry logic, reads related files]
97
+
98
+ The PR adds retry logic to the HTTP client but has two issues:
99
+
100
+ 1. **No backoff** — `src/client.py:42` retries immediately
101
+ in a tight loop. Under load this will hammer the server.
102
+
103
+ 2. **Unused import** — `src/client.py:89` imports `sleep`
104
+ but the retry loop uses `time.sleep` directly.
105
+
106
+ I've added both as draft comments.
107
+
108
+ you: the first one is a blocker, mark it as such
109
+ ```
110
+
111
+ Inspect and post the draft:
112
+
113
+ ```text
114
+ you: /draft
115
+
116
+ ## 2 comments
117
+
118
+ ### src/client.py
119
+
120
+ ★ 1. L:42
121
+ > resp = session.get(url)
122
+ **blocker:** Retries without backoff. Under load this
123
+ hammers the server. Use exponential backoff.
124
+
125
+ ★ 2. L:89
126
+ > from time import sleep
127
+ **nit:** Unused import — `sleep` is never called.
128
+
129
+ ## Summary
130
+
131
+ Two issues in the retry logic. The missing backoff is a
132
+ blocker; the unused import is minor.
133
+
134
+ you: /draft post
135
+ Review posted.
136
+ ```
137
+
138
+ You can also start without a PR — `rbtr` opens a plain
139
+ conversation in any git repo. Use `/review` later to select
140
+ a target.
141
+
142
+ ### Snapshot review
143
+
144
+ Review code at a single point in time — no PR, no diff, no
145
+ GitHub. Useful for onboarding, architecture review, or audit.
146
+
147
+ ```bash
148
+ rbtr v2.1.0 # launch with a tag
149
+ rbtr main # launch with a branch
150
+ rbtr HEAD # launch at current commit
151
+ ```
152
+
153
+ Or from inside rbtr:
154
+
155
+ ```text
156
+ you: /review v2.1.0
157
+ Reviewing snapshot: v2.1.0
158
+ ⟳ indexing 177 files…
159
+
160
+ you: walk me through the auth module
161
+
162
+ you: /review main feature
163
+ Reviewing branch: main → feature
164
+ ```
165
+
166
+ ## Tools
167
+
168
+ The model has 20 tools for reading code, navigating the
169
+ codebase, writing review feedback, and running shell
170
+ commands. Tools appear and disappear based on session state
171
+ — the model never sees a tool it cannot use.
172
+
173
+ | Condition | Tools available |
174
+ | ----------------- | ---------------------------------------- |
175
+ | Always | `edit`, `remember`, `run_command` |
176
+ | Repo + any target | `read_file`, `list_files`, `grep` |
177
+ | Index ready | `search`, `read_symbol`, `list_symbols`, |
178
+ | | `find_references` |
179
+ | PR or branch | `diff`, `changed_files`, `commit_log`, |
180
+ | | `changed_symbols` |
181
+ | PR only | Draft tools, `get_pr_discussion` |
182
+
183
+ In snapshot mode (`/review <ref>`) the model has file, index,
184
+ workspace, and shell tools. Diff and draft tools are hidden
185
+ — there is no base to compare against.
186
+
187
+ ### Reading code
188
+
189
+ The model reads changed files, referenced modules, tests,
190
+ and config to understand context beyond the diff.
191
+
192
+ | Tool | Description |
193
+ | ------------ | ------------------------------------------ |
194
+ | `read_file` | Read file contents, paginated by lines |
195
+ | `grep` | Substring search, scoped by glob or prefix |
196
+ | `list_files` | List files, scoped by glob or prefix |
197
+
198
+ ### Understanding changes
199
+
200
+ The model starts here — what changed, how the work was
201
+ structured, and what the changes mean structurally.
202
+
203
+ | Tool | Description |
204
+ | ----------------- | ------------------------------------ |
205
+ | `diff` | Unified diff, scoped by glob or file |
206
+ | `changed_files` | Files changed in the PR |
207
+ | `commit_log` | Commits between base and head |
208
+ | `changed_symbols` | Symbols added, removed, or modified. |
209
+ | | Flags stale docs and missing tests |
210
+
211
+ ### Navigating the codebase
212
+
213
+ The code index lets the model search by name, keyword, or
214
+ concept — and follow references to check whether a change
215
+ breaks callers or misses related code.
216
+
217
+ | Tool | Description |
218
+ | ----------------- | ------------------------------------ |
219
+ | `search` | Find symbols by name, keyword, or |
220
+ | | natural-language concept |
221
+ | `read_symbol` | Read the full source of a symbol |
222
+ | `list_symbols` | Table of contents for a file |
223
+ | `find_references` | Find all symbols referencing a given |
224
+ | | symbol via the dependency graph |
225
+
226
+ ### Writing the review
227
+
228
+ The model builds the review incrementally — adding comments
229
+ on specific lines, editing them based on discussion, and
230
+ setting the overall summary.
231
+
232
+ | Tool | Description |
233
+ | ---------------------- | -------------------------------- |
234
+ | `add_draft_comment` | Inline comment on a code snippet |
235
+ | `edit_draft_comment` | Edit an existing comment |
236
+ | `remove_draft_comment` | Remove a comment |
237
+ | `set_draft_summary` | Set the top-level review body |
238
+ | `read_draft` | Read the current draft |
239
+
240
+ ### Context and memory
241
+
242
+ The model reads existing discussion to avoid duplicating
243
+ feedback, saves durable facts for future sessions, and
244
+ writes notes to the workspace.
245
+
246
+ | Tool | Description |
247
+ | ------------------- | ----------------------------------- |
248
+ | `get_pr_discussion` | Existing reviews, comments, CI |
249
+ | `remember` | Save a fact for future sessions |
250
+ | `edit` | Create or modify `.rbtr/notes/` and |
251
+ | | `.rbtr/AGENTS.md` |
252
+
253
+ ### Shell execution
254
+
255
+ The model can run shell commands when `tools.shell.enabled`
256
+ is `true` (the default).
257
+
258
+ | Tool | Description |
259
+ | ------------- | --------------------------------------- |
260
+ | `run_command` | Execute a shell command, return output |
261
+
262
+ The primary use is executing scripts bundled with skills.
263
+ The model is steered away from using it for codebase access
264
+ — files under review live in a different branch or commit,
265
+ so `read_file`, `grep`, and other bespoke tools are the
266
+ correct choice (they read from the git object store at the
267
+ right ref). The working tree is treated as read-only.
268
+
269
+ Output is streamed to the TUI via a head/tail buffer
270
+ (first 3 + last 5 lines, refreshed at ~30 fps). When the
271
+ command executes a skill script, the header shows the skill
272
+ name and source instead of raw JSON args
273
+ (e.g. `⚙ [brave-search · user] search.sh "query"`). The
274
+ full result returned to the model is truncated to
275
+ `tools.shell.max_output_lines` (default 2000).
276
+
277
+ ```toml
278
+ [tools.shell]
279
+ enabled = true # set false to disable entirely
280
+ timeout = 120 # default timeout in seconds (0 = no limit)
281
+ max_output_lines = 2000
282
+ ```
283
+
284
+ `list_files`, `grep`, and `diff` accept a `pattern` parameter
285
+ that works like a git pathspec: a plain string is a directory
286
+ prefix or file path, glob metacharacters (`*`, `?`, `[`)
287
+ activate pattern matching, and `**` matches across
288
+ directories. For example, `pattern="src/**/*.py"` scopes to
289
+ Python files under `src/`.
290
+
291
+ Tools that read code accept a `ref` parameter — `"head"`
292
+ (default), `"base"`, or a raw commit SHA — so the model
293
+ can inspect the codebase at any point in time. File tools
294
+ read from the git object store first and fall back to the
295
+ local filesystem for untracked files (`.rbtr/notes/`,
296
+ drafts).
297
+
298
+ All paginated tools show a trailer when output is truncated.
299
+ Each turn is limited to 25 tool calls (configurable via
300
+ `max_requests_per_turn`). When the limit is reached, the
301
+ model summarises its progress and asks whether to continue.
302
+
303
+ ## Commands
304
+
305
+ | Command | Description | Context |
306
+ | ------------------------- | ------------------------------------------- | ------- |
307
+ | `/help` | Show available commands | |
308
+ | `/review` | List open PRs and branches | ✓ |
309
+ | `/review <number>` | Select a PR for review | ✓ |
310
+ | `/review <ref>` | Snapshot review at a git ref | ✓ |
311
+ | `/review <base> <target>` | Diff review between two refs | ✓ |
312
+ | `/draft` | View, sync, or post the review draft | ✓ |
313
+ | `/connect <service>` | Authenticate with a service | ✓ |
314
+ | `/model` | List available models from all providers | |
315
+ | `/model <provider/id>` | Set the active model | ✓ |
316
+ | `/index` | Index status, search, diagnostics, rebuild | partial |
317
+ | `/compact` | Summarise older context to free space | ✓ |
318
+ | `/compact reset` | Undo last compaction (before new messages) | ✓ |
319
+ | `/session` | List, inspect, or delete sessions | partial |
320
+ | `/stats` | Show session token and cost statistics | ✓ |
321
+ | `/memory` | List, extract, or purge cross-session facts | partial |
322
+ | `/skill` | List or load a skill | ✓ |
323
+ | `/reload` | Show active prompt sources | |
324
+ | `/new` | Start a new conversation | |
325
+ | `/quit` | Exit (also `/q`) | |
326
+
327
+ The **Context** column shows which commands produce
328
+ [context markers](#context-markers) for the model.
329
+ `partial` means some subcommands emit markers (e.g.
330
+ `/index status` does, `/index search` does not).
331
+
332
+ ## Providers
333
+
334
+ rbtr connects to LLMs through multiple providers. Use `/connect`
335
+ to authenticate:
336
+
337
+ | Provider | Auth | Command |
338
+ | ---------- | ------------------ | -------------------------------------- |
339
+ | Claude | OAuth (Pro/Max) | `/connect claude` |
340
+ | ChatGPT | OAuth (Plus/Pro) | `/connect chatgpt` |
341
+ | Google | OAuth (free) | `/connect google` |
342
+ | OpenAI | API key | `/connect openai sk-...` |
343
+ | Fireworks | API key | `/connect fireworks fw-...` |
344
+ | OpenRouter | API key | `/connect openrouter sk-or-...` |
345
+ | Endpoint | URL + optional key | `/connect endpoint <name> <url> [key]` |
346
+
347
+ Multiple providers can be connected at the same time. Tab on
348
+ `/connect` autocompletes provider names.
349
+
350
+ ### Endpoints
351
+
352
+ Any OpenAI-compatible API can be used as a custom endpoint:
353
+
354
+ ```text
355
+ you: /connect endpoint deepinfra https://api.deepinfra.com/v1/openai di-...
356
+ you: /connect endpoint ollama http://localhost:11434/v1
357
+ you: /model deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct
358
+ ```
359
+
360
+ Endpoints are first-class providers — they appear in `/model`
361
+ listings, support tab completion, and participate in the same
362
+ dispatch pipeline as builtin providers.
363
+
364
+ ### GitHub
365
+
366
+ rbtr uses GitHub to fetch PRs and branches for review. Authenticate
367
+ with `/connect github` (device flow). This is separate from LLM
368
+ providers — it gives rbtr read access to your repositories.
369
+
370
+ ## Models
371
+
372
+ Models use `<provider>/<model-id>` format:
373
+
374
+ ```text
375
+ /model claude/claude-sonnet-4-20250514
376
+ /model chatgpt/gpt-5.2-codex
377
+ /model openai/gpt-4o
378
+ /model deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct
379
+ ```
380
+
381
+ ### Listing models
382
+
383
+ `/model` with no argument shows all models from connected providers,
384
+ marking the active one:
385
+
386
+ ```text
387
+ you: /model
388
+ claude:
389
+ claude/claude-sonnet-4-20250514 ◂
390
+ claude/claude-opus-4-20250514
391
+ chatgpt:
392
+ chatgpt/o3-pro
393
+ chatgpt/gpt-5.2-codex
394
+ deepinfra:
395
+ deepinfra/meta-llama/Meta-Llama-3.1-70B-Instruct
396
+ ```
397
+
398
+ Providers that don't expose a model listing show a hint instead:
399
+
400
+ ```text
401
+ ollama:
402
+ /model ollama/<model-id>
403
+ ```
404
+
405
+ The model list is fetched lazily on first Tab completion or
406
+ `/model` command, and refreshed on every `/connect`.
407
+
408
+ ### Switching models mid-conversation
409
+
410
+ Conversation history is preserved when you switch models.
411
+ PydanticAI's message format is provider-agnostic, so previous
412
+ messages are converted automatically. When a provider rejects
413
+ history from a different provider (mismatched tool-call
414
+ formats, thinking metadata), rbtr repairs the history in
415
+ memory — the original messages are never modified:
416
+
417
+ ```text
418
+ you: explain the retry logic in src/client.py
419
+ claude/claude-sonnet-4-20250514: The retry logic uses exponential backoff…
420
+
421
+ you: /model chatgpt/o3-pro
422
+ Model set to chatgpt/o3-pro
423
+
424
+ you: what do you think about adding jitter?
425
+ chatgpt/o3-pro: Based on the retry logic we discussed…
426
+ ```
427
+
428
+ Only `/new` clears history (explicit user action). The active model is
429
+ persisted to `config.toml` across restarts. Conversation messages are
430
+ saved automatically to the session database (see Sessions below).
431
+
432
+ ## Terminal reference
433
+
434
+ ### Shell commands
435
+
436
+ Prefix any command with `!` to run it in a shell:
437
+
438
+ ```text
439
+ you: !git log --oneline -5
440
+ you: !rg "TODO" src/
441
+ ```
442
+
443
+ Long output is truncated — press **Ctrl+O** to expand it.
444
+
445
+ ### Tab completion
446
+
447
+ | Context | Example | Completes |
448
+ | -------------- | ------------------ | ----------------------- |
449
+ | Slash commands | `/rev` → `/review` | Command names |
450
+ | Command args | `/connect c` | Providers, models, etc. |
451
+ | Shell commands | `!git ch` | Bash completion |
452
+ | File paths | `!cat ~/Doc` | Directories and files |
453
+
454
+ ### Key bindings
455
+
456
+ | Key | Action |
457
+ | ----------------------- | ---------------------------------------- |
458
+ | Enter | Submit input |
459
+ | Shift+Enter / Alt+Enter | Insert newline (multiline input) |
460
+ | Tab | Autocomplete |
461
+ | Shift+Tab | Cycle thinking effort level |
462
+ | Up/Down | Browse history or navigate multiline |
463
+ | Ctrl+C | Cancel running task (double-tap to quit) |
464
+ | Ctrl+O | Expand truncated shell output |
465
+
466
+ ### Pasting
467
+
468
+ Bracketed paste is enabled — pasted newlines insert into the
469
+ prompt instead of submitting. Large pastes collapse into an
470
+ atomic marker (`[pasted 42 lines]`) that expands on submit.
471
+
472
+ ### Context markers
473
+
474
+ After a slash command or shell command, a context marker
475
+ appears above your input — a tag like `[/review → PR #42]`
476
+ or `[! git log — exit 0]`. On submit, markers expand into a
477
+ `[Recent actions]` block prepended to your message so the
478
+ model knows what you just did. Backspace at the start of the
479
+ input dismisses the last marker. Not every command produces
480
+ a marker — only those whose outcome is useful to the model.
481
+
482
+ ## Usage display
483
+
484
+ The footer shows token usage and context after each response:
485
+
486
+ ```text
487
+ owner/repo claude/claude-sonnet-4-20250514
488
+ PR #42 · feature-branch |7| 12% of 200k ↑24.3k ↓1.2k ↯18.0k $0.045
489
+ ```
490
+
491
+ `|7|` messages, `12%` context used, `↑` input tokens,
492
+ `↓` output tokens, `↯` cache-read tokens, `$` cost.
493
+ Colours shift from green to yellow to red as context fills.
494
+ `/new` resets all counters.
495
+
496
+ ## Sessions
497
+
498
+ Every conversation is saved to a local SQLite database at
499
+ `~/.config/rbtr/sessions.db`. Messages are persisted as they
500
+ stream — if rbtr crashes, the conversation survives up to the
501
+ last received part. Requests are always persisted before their
502
+ responses, so resumed sessions load in the correct order.
503
+
504
+ ### Persistence
505
+
506
+ The message format is provider-agnostic. History is preserved
507
+ across `/model` switches — only `/new` clears it. rbtr
508
+ repairs history automatically in memory on every turn — the
509
+ original messages are never modified. Preventive repairs
510
+ (sanitising cross-provider field values, patching cancelled
511
+ tool calls) run before each API call. When a provider still
512
+ rejects the history, escalating structural repairs retry
513
+ automatically. All repairs are recorded as incidents, visible
514
+ in `/stats`.
515
+
516
+ Ctrl+C during a tool-calling turn cancels immediately. Any tool
517
+ calls without results get synthetic `(cancelled)` returns so
518
+ the conversation can continue:
519
+
520
+ ```text
521
+ ⚠ Previous turn was cancelled mid-tool-call (read_file, grep).
522
+ Those tool results are lost — the model will continue without them.
523
+ ```
524
+
525
+ See [Conversation storage](ARCHITECTURE.md#conversation-storage)
526
+ and [Cross-provider history repair](ARCHITECTURE.md#cross-provider-history-repair)
527
+ in ARCHITECTURE.md.
528
+
529
+ ### `/new` — starting fresh
530
+
531
+ `/new` clears the in-memory conversation (history, usage
532
+ counters) and generates a new session ID. The previous session's
533
+ data stays in the database — it can be listed with `/session` and
534
+ resumed with `/session resume`.
535
+
536
+ ### `/session` command
537
+
538
+ ```text
539
+ /session List recent sessions (current repo)
540
+ /session all List sessions across all repos
541
+ /session info Show current session details
542
+ /session rename <n> Rename the current session
543
+ /session resume <q> Resume a session (ID prefix or label)
544
+ /session delete <id> Delete a session by ID prefix
545
+ /session purge 7d Delete sessions older than 7 days
546
+ ```
547
+
548
+ **`/session rename`** changes the label on the current session.
549
+ Labels are set automatically when a review target is selected
550
+ (e.g. `acme/app — main → feature-x`), but you can override
551
+ them with any name.
552
+
553
+ **`/session resume`** accepts an ID prefix or a label substring
554
+ (case-insensitive). ID prefix is tried first; if no match,
555
+ the label is searched. When several sessions share a label the
556
+ most recent one is picked.
557
+
558
+ **`/session resume`** loads the target session's messages from the
559
+ database and switches the active session ID. The conversation
560
+ continues where it left off — the model sees the full history.
561
+ If the session had a review target (PR or branch), it's
562
+ automatically restored — rbtr re-runs `/review` to fetch fresh
563
+ metadata and rebuild the code index.
564
+ You can resume sessions from different repos or different models.
565
+
566
+ **`/session delete`** requires an exact ID prefix — no label
567
+ matching, to prevent accidental deletion. Removes all fragments
568
+ for the session (cascading via foreign keys). You cannot delete
569
+ the active session — use `/new` first.
570
+
571
+ ### Automatic behaviour
572
+
573
+ - **New session on startup.** Each `rbtr` invocation starts a fresh
574
+ session, labelled with the current repo and branch. When you
575
+ select a review target, the label updates to show the base and
576
+ head branches (e.g. `acme/app — main → feature-x`).
577
+ - **Streaming persistence.** Parts are saved to the database as
578
+ they arrive from the model, not batched after the turn.
579
+ - **Input history from the database.** Up/Down arrow browses input
580
+ history across all sessions, deduplicated and sorted by recency.
581
+
582
+ ### Pruning old sessions
583
+
584
+ Sessions accumulate over time. Use `/session purge` to clean up:
585
+
586
+ ```text
587
+ /session purge 7d Delete sessions older than 7 days
588
+ /session purge 2w Delete sessions older than 2 weeks
589
+ /session purge 24h Delete sessions older than 24 hours
590
+ ```
591
+
592
+ Duration suffixes: `d` (days), `w` (weeks), `h` (hours). The
593
+ active session is never deleted. To remove a specific session,
594
+ use `/session delete <id>`.
595
+
596
+ ### `/stats` command
597
+
598
+ ```text
599
+ /stats Current session statistics
600
+ /stats <id> Stats for a specific session (prefix match)
601
+ /stats all Aggregate stats across all sessions
602
+ ```
603
+
604
+ Shows token usage (input, output, cache), cost, tool call
605
+ frequency, and — when the session has incidents — failure and
606
+ repair summaries.
607
+
608
+ #### Incident reporting
609
+
610
+ When an LLM call fails and rbtr auto-recovers, or when history
611
+ is manipulated to satisfy provider constraints, the event is
612
+ recorded as an **incident** in the session database (see
613
+ ARCHITECTURE.md — History repair). `/stats` surfaces these
614
+ when they exist:
615
+
616
+ ```text
617
+ Failures (3)
618
+ history_format 2 recovered: 2
619
+ overflow 1 recovered: 1
620
+
621
+ History repairs (6)
622
+ repair_dangling 1 (cancelled_mid_tool_call)
623
+ consolidate_tool_returns 2 (cross_provider_retry)
624
+ demote_thinking 2 (cross_provider_retry)
625
+ flatten_tool_exchanges 1 (cross_provider_retry)
626
+
627
+ Recovery rate 100% 3/3
628
+ ```
629
+
630
+ **Failures** are grouped by kind (`history_format`, `overflow`,
631
+ `tool_args`, `type_error`, `effort_unsupported`) with
632
+ recovered/failed sub-counts. **History repairs** are grouped by
633
+ strategy with the reason that triggered them. The **recovery
634
+ rate** shows what percentage of failures were automatically
635
+ resolved.
636
+
637
+ Sessions with no incidents show no extra sections — the output
638
+ is identical to before.
639
+
640
+ ## Cross-session memory
641
+
642
+ rbtr learns facts from conversations and remembers them across
643
+ sessions. Facts are durable knowledge — project conventions,
644
+ architecture decisions, user preferences, recurring patterns
645
+ discovered during review.
646
+
647
+ Static project instructions belong in `AGENTS.md`. Facts are
648
+ what the agent learns on its own.
649
+
650
+ ### How facts are created
651
+
652
+ Facts are extracted automatically at two points:
653
+
654
+ - **During compaction** — the extraction agent runs
655
+ concurrently with the summary agent, analysing the messages
656
+ being compacted.
657
+ - **After posting a review** — `/draft post` triggers
658
+ extraction on the full session, since completed reviews are
659
+ the richest source of project knowledge.
660
+
661
+ You can also trigger extraction manually with
662
+ `/memory extract`, or teach the agent directly — it has a
663
+ `remember` tool that saves facts on demand during
664
+ conversation.
665
+
666
+ ### Scopes
667
+
668
+ Each fact has a scope:
669
+
670
+ - **global** — applies everywhere (e.g. "prefers British
671
+ English spelling")
672
+ - **repo** — specific to the current repository (e.g. "uses
673
+ pytest with the mocker fixture, not unittest.mock")
674
+
675
+ Repo-scoped facts are keyed by `owner/repo` and only
676
+ injected when working in that repository.
677
+
678
+ ### Injection
679
+
680
+ At session start, active facts for the current scopes are
681
+ loaded and injected into the system prompt. Two caps control
682
+ what's included:
683
+
684
+ - `max_injected_facts` (default 20) — maximum number of
685
+ facts
686
+ - `max_injected_tokens` (default 2000) — token budget for
687
+ injected facts
688
+
689
+ Facts are ordered by `last_confirmed_at` (most recently
690
+ confirmed first), so frequently re-observed facts take
691
+ priority.
692
+
693
+ ### Deduplication
694
+
695
+ The extraction agent sees all existing facts and tags each
696
+ extraction as `new`, `confirm` (re-observed), or `supersede`
697
+ (replaces an outdated fact). Matching is content-based — no
698
+ opaque IDs are exposed to the LLM. When the LLM's reference
699
+ doesn't exactly match, a clarification retry corrects the
700
+ mismatch.
701
+
702
+ ### `/memory` command
703
+
704
+ ```text
705
+ /memory List active facts by scope
706
+ /memory all Include superseded facts
707
+ /memory extract Extract facts from the current session
708
+ /memory purge 7d Delete facts not confirmed in 7 days
709
+ /memory purge 2w Delete facts older than 2 weeks
710
+ ```
711
+
712
+ Purge uses `last_confirmed_at` — facts that are regularly
713
+ re-observed survive longer. Duration suffixes: `d` (days),
714
+ `w` (weeks), `h` (hours).
715
+
716
+ ### Memory configuration
717
+
718
+ ```toml
719
+ [memory]
720
+ enabled = true # Toggle the feature
721
+ max_injected_facts = 20 # Facts in system prompt
722
+ max_injected_tokens = 2000 # Token budget for injection
723
+ max_extraction_facts = 200 # Existing facts shown to extraction agent
724
+ fact_extraction_model = "" # Override model (empty = session model)
725
+ ```
726
+
727
+ ## Skills
728
+
729
+ Skills are self-contained instruction packages — a markdown
730
+ file with optional bundled scripts — that teach the model
731
+ new capabilities. rbtr discovers skills automatically from
732
+ multiple directories and presents them in the system prompt.
733
+
734
+ rbtr scans the same skill directories as pi and Claude Code,
735
+ so existing skills work with zero configuration:
736
+
737
+ ```text
738
+ ~/.config/rbtr/skills/ # user-level rbtr skills
739
+ ~/.claude/skills/ # Claude Code skills
740
+ ~/.pi/agent/skills/ # pi skills
741
+ ~/.agents/skills/ # Agent Skills standard
742
+ .rbtr/skills/ # project-level (any ancestor to git root)
743
+ .claude/skills/ # project-level Claude Code
744
+ .pi/skills/ # project-level pi
745
+ .agents/skills/ # project-level Agent Skills
746
+ ```
747
+
748
+ Skills use the [Agent Skills standard][agent-skills] format:
749
+ a markdown file with YAML frontmatter (`name`, `description`).
750
+ The model sees a catalog of available skills in its system
751
+ prompt and reads the full skill file on demand via `read_file`.
752
+
753
+ [agent-skills]: https://agentskills.io/specification
754
+
755
+ ### `/skill` command
756
+
757
+ ```text
758
+ /skill List discovered skills
759
+ /skill brave-search Load a skill into context
760
+ /skill brave-search "query" Load with a follow-up message
761
+ ```
762
+
763
+ Tab-completes skill names. Skills marked with
764
+ `disable-model-invocation: true` are hidden from the prompt
765
+ catalog but still loadable via `/skill`.
766
+
767
+ ### Configuration
768
+
769
+ ```toml
770
+ [skills]
771
+ project_dirs = [".rbtr/skills", ".claude/skills", ".pi/skills", ".agents/skills"]
772
+ user_dirs = ["~/.config/rbtr/skills", "~/.claude/skills", "~/.pi/agent/skills", "~/.agents/skills"]
773
+ extra_dirs = []
774
+ ```
775
+
776
+ Set `project_dirs = []` or `user_dirs = []` to disable
777
+ scanning. `extra_dirs` adds directories on top.
778
+
779
+ ## Context compaction
780
+
781
+ Long conversations fill the context window. rbtr compacts
782
+ automatically — summarising older messages while keeping
783
+ recent turns intact.
784
+
785
+ Compaction splits the conversation by turn boundaries. A turn
786
+ starts at a user prompt and includes the model's responses,
787
+ tool calls, and tool results. The last `keep_turns` (default 2)
788
+ are preserved; everything older is serialised and sent to the
789
+ model for summarisation. The original messages stay in the
790
+ database for auditing.
791
+
792
+ ### Example
793
+
794
+ Before (5 turns):
795
+
796
+ ```text
797
+ turn 1: user "set up the project" → assistant "done"
798
+ turn 2: user "add authentication" → assistant [tools] → "added auth"
799
+ turn 3: user "write tests for auth" → assistant [tools] → "added 12 tests"
800
+ turn 4: user "fix the failing test" → assistant [tools] → "fixed assertion"
801
+ turn 5: user "review the PR" → assistant [reading files...]
802
+ ```
803
+
804
+ After (turns 1–3 summarised, 4–5 kept):
805
+
806
+ ```text
807
+ summary: "[Context summary] Set up project. Added auth
808
+ middleware. Wrote 12 tests for auth module."
809
+ turn 4: user "fix the failing test" → assistant "fixed assertion"
810
+ turn 5: user "review the PR" → assistant [reading files...]
811
+ ```
812
+
813
+ ### When compaction triggers
814
+
815
+ - **Post-turn** — after a response, if context usage exceeds
816
+ `auto_compact_pct` (default 85%).
817
+ - **Mid-turn** — during a tool-calling cycle, if the threshold
818
+ is exceeded. Compacts once, reloads history, resumes the turn.
819
+ - **Overflow** — when the API rejects a request with a
820
+ context-length error. Compacts and retries.
821
+ - **Manual** — `/compact`, with optional extra instructions:
822
+
823
+ ```text
824
+ you: /compact
825
+ you: /compact Focus on the authentication changes
826
+ ```
827
+
828
+ `/compact reset` undoes the latest compaction, restoring the
829
+ original messages. Only allowed before new messages are sent.
830
+
831
+ When the old messages are too large for a single summary
832
+ request, rbtr finds the largest prefix that fits, summarises
833
+ it, and pushes the rest into the kept portion.
834
+
835
+ ### Settings
836
+
837
+ ```toml
838
+ [compaction]
839
+ auto_compact_pct = 85 # trigger threshold (% of context)
840
+ keep_turns = 2 # recent turns to preserve
841
+ reserve_tokens = 16000 # reserved for the summary response
842
+ summary_max_chars = 2000 # max chars per tool result in input
843
+ ```
844
+
845
+ See [Context compaction in ARCHITECTURE.md](ARCHITECTURE.md#context-compaction)
846
+ for the split algorithm, orphan handling, and reset mechanics.
847
+
848
+ ## Configuration
849
+
850
+ User-level files live in `~/.config/rbtr/` (override with
851
+ `RBTR_USER_DIR`). A workspace overlay at `.rbtr/config.toml`
852
+ can override per-project settings — the nearest `.rbtr/`
853
+ walking from CWD to the git root wins (monorepo-friendly).
854
+
855
+ - **`config.toml`** — model, endpoints, feature settings.
856
+ - **`creds.toml`** — API keys and OAuth tokens (0600).
857
+
858
+ ```toml
859
+ # config.toml
860
+ model = "claude/claude-sonnet-4-20250514"
861
+
862
+ [endpoints.deepinfra]
863
+ base_url = "https://api.deepinfra.com/v1/openai"
864
+ ```
865
+
866
+ Config values can also be set via environment variables with
867
+ `RBTR_` prefix (e.g. `RBTR_MODEL`). OAuth tokens are managed
868
+ by `/connect` — you never need to edit `creds.toml` by hand.
869
+
870
+ ### Customising the prompt
871
+
872
+ Three levels of customisation, loaded in order:
873
+
874
+ 1. **`AGENTS.md`** (repo root) — project-specific rules.
875
+ Configure the file list with
876
+ `project_instructions = ["AGENTS.md"]`.
877
+ 2. **`~/.config/rbtr/APPEND_SYSTEM.md`** — user-wide
878
+ preferences appended to the system prompt.
879
+ 3. **`~/.config/rbtr/SYSTEM.md`** — full system prompt
880
+ replacement (Jinja template with `project_instructions`
881
+ and `append_system` variables).
882
+
883
+ Example `AGENTS.md`:
884
+
885
+ ```markdown
886
+ - Target Python 3.13+. Use modern features.
887
+ - All code must be type-annotated.
888
+ - Run `just check` after every change.
889
+ ```
890
+
891
+ ## Code index
892
+
893
+ When you start a review (`/review`), rbtr builds a structural index
894
+ of the repository in the background. The index gives the LLM tools
895
+ to search, navigate, and reason about the codebase — not just the
896
+ diff.
897
+
898
+ ### What gets indexed
899
+
900
+ rbtr extracts **chunks** (functions, classes, methods, imports,
901
+ doc sections) from every file in the repo at the base commit, then
902
+ incrementally indexes the head commit. Each chunk records its name,
903
+ kind, file path, line range, and content.
904
+
905
+ Cross-file **edges** are inferred automatically:
906
+
907
+ - **Import edges** — structural (from tree-sitter import metadata)
908
+ or text-search fallback for languages without an extractor.
909
+ - **Test edges** — `test_foo.py` → `foo.py` by naming convention
910
+ and import analysis.
911
+ - **Doc edges** — markdown/RST sections that mention function or
912
+ class names.
913
+
914
+ **Embeddings** are computed for semantic search using a local GGUF
915
+ model (bge-m3, quantized, runs on Metal/CPU — no API calls). The
916
+ structural index is usable immediately; embeddings fill in behind
917
+ it.
918
+
919
+ ### Progress indicator
920
+
921
+ The footer shows indexing progress:
922
+
923
+ ```text
924
+ ⟳ parsing 42/177 (extracting chunks)
925
+ ⟳ embedding 85/380 (computing vectors)
926
+ ● 1.2k (ready — 1,200 chunks indexed)
927
+ ```
928
+
929
+ The review proceeds immediately — you don't have to wait for
930
+ indexing to finish.
931
+
932
+ ### `/index` command
933
+
934
+ | Subcommand | Description |
935
+ | ---------------------------- | ---------------------------------------- |
936
+ | `/index` | Show index status (chunks, edges, size) |
937
+ | `/index clear` | Delete the index database |
938
+ | `/index rebuild` | Clear and re-index from scratch |
939
+ | `/index prune` | Remove orphan chunks not in any snapshot |
940
+ | `/index model` | Show current embedding model |
941
+ | `/index model <id>` | Switch embedding model and re-embed |
942
+ | `/index search <query>` | Search the index and show ranked results |
943
+ | `/index search-diag <query>` | Search with full signal breakdown table |
944
+
945
+ ### Index configuration
946
+
947
+ ```toml
948
+ [index]
949
+ enabled = true
950
+ embedding_model = "gpustack/bge-m3-GGUF/bge-m3-Q4_K_M.gguf"
951
+ include = [".rbtr/notes/*"] # force-include (override .gitignore)
952
+ extend_exclude = [".rbtr/index"] # exclude on top of .gitignore
953
+ ```
954
+
955
+ The index is persistent — subsequent `/review` runs skip
956
+ unchanged files (keyed by git blob SHA).
957
+
958
+ ### Graceful degradation
959
+
960
+ - **No grammar installed** for a language → falls back to
961
+ line-based plaintext chunking.
962
+ - **No embedding model** (missing GGUF, GPU init failure) →
963
+ structural index works, semantic search signal is skipped
964
+ (weight redistributed to name and keyword channels).
965
+ - **Slow indexing** → review starts immediately, index catches
966
+ up in a background thread.
967
+
968
+ ## Review draft
969
+
970
+ The LLM builds a structured review using draft tools
971
+ (`add_draft_comment`, `set_draft_summary`, etc.). The draft
972
+ persists to `.rbtr/drafts/<pr>.yaml` — crash-safe, human-
973
+ editable, and synced bidirectionally with GitHub.
974
+
975
+ ### Workflow
976
+
977
+ 1. `/review 42` — fetches the PR, pulls any existing
978
+ pending review from GitHub.
979
+ 2. The LLM adds comments and a summary as it reviews.
980
+ 3. `/draft` — inspect the current state.
981
+ 4. `/draft sync` — bidirectional sync with GitHub
982
+ (three-way merge, conflicts resolve to local).
983
+ 5. `/draft post` — submit the review. Optional event type:
984
+ `approve`, `request_changes` (default `COMMENT`).
985
+
986
+ ### Draft commands
987
+
988
+ | Subcommand | Description |
989
+ | --------------------- | ------------------------------------- |
990
+ | `/draft` | Show draft with sync status |
991
+ | `/draft sync` | Bidirectional sync with GitHub |
992
+ | `/draft post [event]` | Submit review to GitHub |
993
+ | `/draft clear` | Delete local draft and remote pending |
994
+
995
+ ### Status indicators
996
+
997
+ | Indicator | Meaning |
998
+ | --------- | -------------------------------------- |
999
+ | `✓` | Synced — matches last-pushed snapshot |
1000
+ | `✎` | Modified locally since last sync |
1001
+ | `★` | New — never synced to GitHub |
1002
+ | `✗` | Deleted — will be removed on next sync |
1003
+
1004
+ ### Safety
1005
+
1006
+ - **Unsynced guard** — `/draft post` refuses if the remote
1007
+ has comments not in the local draft.
1008
+ - **Atomic posting** — all comments are submitted in one
1009
+ API call.
1010
+ - **Crash-safe** — YAML on disk, updated on every mutation.
1011
+ Mid-sync crashes recover on the next sync.
1012
+ - **GitHub suggestions** — the LLM can provide replacement
1013
+ code; it's posted as a suggestion block that the author
1014
+ can apply with one click.
1015
+
1016
+ See [Review draft and GitHub integration][arch-draft]
1017
+ in ARCHITECTURE.md for sync internals.
1018
+
1019
+ [arch-draft]: ARCHITECTURE.md#review-draft-and-github-integration
1020
+
1021
+ ## Theme
1022
+
1023
+ rbtr defaults to a dark palette with semantically tinted panel
1024
+ backgrounds. Switch to light mode or override individual
1025
+ styles in `config.toml`:
1026
+
1027
+ ```toml
1028
+ [theme]
1029
+ mode = "light" # "dark" (default) or "light"
1030
+
1031
+ [theme.light] # override fields for the active mode
1032
+ bg_succeeded = "on #E0FFE0"
1033
+ prompt = "bold magenta"
1034
+ ```
1035
+
1036
+ Text styles use ANSI names (`bold cyan`, `dim`, `yellow`, …)
1037
+ that adapt to your terminal's colour scheme. Panel backgrounds
1038
+ use hex values. Any string that Rich accepts as a
1039
+ [style definition](https://rich.readthedocs.io/en/latest/style.html)
1040
+ is valid.
1041
+
1042
+ Available fields are defined in `PaletteConfig` in
1043
+ [`config.py`](src/rbtr/config.py).
1044
+
1045
+ ## Development
1046
+
1047
+ ```bash
1048
+ uv sync # Install dependencies
1049
+ just check # Lint + typecheck + test
1050
+ just fmt # Auto-fix and format
1051
+ ```
1052
+
1053
+ ### Architecture reference
1054
+
1055
+ For details on how providers, tools, the index, language
1056
+ plugins, session persistence, history repair, and GitHub sync
1057
+ work internally, see [ARCHITECTURE.md](ARCHITECTURE.md).
1058
+
1059
+ ### Git reference handling
1060
+
1061
+ rbtr never modifies your working tree or local branches.
1062
+ All reads go through the git object store.
1063
+
1064
+ When you select a PR, rbtr fetches the PR head and the
1065
+ base branch from origin so that diffs, commit logs, and
1066
+ changed-file lists reflect the actual PR scope — not stale
1067
+ local refs. For PRs, the exact base and head SHAs come from
1068
+ the GitHub API, so a local `main` that is behind
1069
+ `origin/main` cannot pollute the results. For local branch
1070
+ reviews, branch names are used directly.
1071
+
1072
+ Commit logs use `git log base..head` semantics — only
1073
+ commits reachable from head but not from base. Review
1074
+ comment validation diffs from the merge base of the two
1075
+ refs, matching GitHub's three-dot diff.
1076
+
1077
+ ### Benchmarking
1078
+
1079
+ ```bash
1080
+ just bench # quick benchmark (current repo)
1081
+ just bench -- /path/to/repo main # custom repo
1082
+ just bench -- . main feature # with incremental update
1083
+ just bench-scalene -- /path/to/repo # line-level CPU + memory profiling
1084
+ just scalene-view # view last scalene profile
1085
+ ```
1086
+
1087
+ Detailed results and optimization notes are in `PROFILING.md`.
1088
+
1089
+ ### Search quality
1090
+
1091
+ The unified search system fuses three signals — name matching,
1092
+ BM25 keyword search, and semantic (embedding) similarity — with
1093
+ post-fusion adjustments for chunk kind and file category. Two
1094
+ scripts measure and tune search quality:
1095
+
1096
+ ```bash
1097
+ just eval-search # evaluate against curated queries
1098
+ just tune-search # grid-search fusion weights
1099
+ just tune-search -- --step 0.05 # finer resolution (400 combos)
1100
+ just bench-search # replay real queries (current dir)
1101
+ just bench-search -- /path/to/repo # replay for a specific repo
1102
+ ```
1103
+
1104
+ **`scripts/eval_search.py`** — Runs 24+ curated queries against
1105
+ the rbtr repo, measuring recall@1, recall@5, and MRR across
1106
+ three backends (name, BM25, unified). Queries are grouped by
1107
+ the technique they test (tokenisation, IDF, kind scoring, file
1108
+ category, name matching, query understanding, structural
1109
+ signals). Results are tracked in `TODO-search.md`.
1110
+
1111
+ **`scripts/tune_search.py`** — Grid-searches over fusion weight
1112
+ combinations for each query kind (identifier / concept).
1113
+ Precomputes all channel scores once, then sweeps in-memory —
1114
+ runs in ~1s. Reports the top 10 weight combos and the current
1115
+ settings for comparison.
1116
+
1117
+ **`scripts/bench_search.py`** — Mines real search queries from
1118
+ the session history database (`~/.config/rbtr/sessions.db`).
1119
+ Pass a repo path (defaults to current directory), and the
1120
+ script filters to events matching that repo by remote URL.
1121
+ Extracts search→read pairs (search followed by `read_symbol`),
1122
+ detects retry chains, classifies queries, and replays paired
1123
+ queries through the current search pipeline. Reports R@1, R@5,
1124
+ MRR, and per-query signal breakdowns for misranked results.
1125
+
1126
+ `eval_search` and `tune_search` are rbtr-specific — they
1127
+ validate the repo identity via `pyproject.toml` before running.
1128
+ `bench_search` works with any repo that has session history.
1129
+
1130
+ For search internals (fusion algorithm, scoring functions,
1131
+ tokenisation), see [ARCHITECTURE.md](ARCHITECTURE.md).
1132
+
1133
+ ## License
1134
+
1135
+ MIT