micro-cc 0.2.78__tar.gz → 0.2.81__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 (229) hide show
  1. micro_cc-0.2.81/LICENSE +21 -0
  2. micro_cc-0.2.81/PKG-INFO +105 -0
  3. micro_cc-0.2.81/README.md +38 -0
  4. {micro_cc-0.2.78 → micro_cc-0.2.81}/pyproject.toml +2 -3
  5. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/browser/_md_convert.py +12 -19
  6. micro_cc-0.2.81/src/micro_cc/cache/state_store.py +197 -0
  7. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/claude_loop_.py +36 -65
  8. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/execute_tool.py +5 -1
  9. micro_cc-0.2.81/src/micro_cc/models/__init__.py +6 -0
  10. micro_cc-0.2.81/src/micro_cc/models/adapters/__init__.py +1 -0
  11. micro_cc-0.2.81/src/micro_cc/models/adapters/anthropic.py +95 -0
  12. micro_cc-0.2.81/src/micro_cc/models/adapters/chat.py +108 -0
  13. micro_cc-0.2.81/src/micro_cc/models/adapters/responses.py +74 -0
  14. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/models/anthropic.py +49 -118
  15. micro_cc-0.2.81/src/micro_cc/models/client.py +65 -0
  16. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/models/foundry.py +8 -86
  17. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/models/litellm.py +26 -235
  18. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/models/ollama.py +16 -166
  19. micro_cc-0.2.81/src/micro_cc/models/openai.py +569 -0
  20. micro_cc-0.2.81/src/micro_cc/models/priming.py +38 -0
  21. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/models/registry.py +17 -10
  22. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/models/schema.py +22 -0
  23. micro_cc-0.2.81/src/micro_cc/models/types.py +59 -0
  24. {micro_cc-0.2.78/src/micro_cc/utils → micro_cc-0.2.81/src/micro_cc/postgres_store}/pg_store_.py +75 -89
  25. micro_cc-0.2.81/src/micro_cc/postgres_store/schema_.py +109 -0
  26. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/graph-orchestration/SKILL.md +12 -1
  27. micro_cc-0.2.81/src/micro_cc/skills/system-design-review/REFERENCE.md +174 -0
  28. micro_cc-0.2.81/src/micro_cc/skills/system-design-review/SKILL.md +161 -0
  29. micro_cc-0.2.81/src/micro_cc/skills/system-design-review/scripts/design_graph.py +290 -0
  30. micro_cc-0.2.81/src/micro_cc/skills/system-design-review/scripts/design_review.py +528 -0
  31. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/start_headless_.py +9 -5
  32. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/start_live_tui_.py +75 -27
  33. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/bash_tool.py +86 -2
  34. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/file_tools_.py +2 -2
  35. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/memory_tool_.py +10 -35
  36. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/monitor_.py +34 -5
  37. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/monitor_watch_.py +7 -10
  38. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/todo_tools_.py +6 -6
  39. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/vision_tools_.py +6 -28
  40. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/alt_screen_.py +16 -6
  41. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/detect_images_.py +29 -40
  42. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/diff_viewer_.py +2 -3
  43. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/etype_handler_.py +24 -3
  44. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/keys_.py +27 -33
  45. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/kill_ring_.py +6 -10
  46. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/list_picker_.py +3 -7
  47. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/message_row_.py +31 -43
  48. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/prompt_input_.py +7 -9
  49. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/screen_cmds_.py +3 -7
  50. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/stack_.py +23 -30
  51. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/stdin_buffer_.py +16 -21
  52. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/terminal_.py +16 -30
  53. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/text_.py +28 -32
  54. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/text_utils_.py +10 -12
  55. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/theme_.py +3 -4
  56. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/truncated_text_.py +7 -9
  57. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/undo_stack_.py +7 -10
  58. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/word_navigation_.py +10 -28
  59. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/claude_subscription.py +10 -12
  60. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/command_registry.py +0 -6
  61. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/helpers.py +22 -7
  62. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/hidden_prompts.py +0 -32
  63. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/memory_store_.py +31 -1
  64. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/msg_store_.py +78 -72
  65. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/settings_store_.py +41 -9
  66. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/status_marker.py +4 -5
  67. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/subagent_tracker_.py +29 -3
  68. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/tokenization_simple.py +146 -13
  69. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/webui/bridge.py +2 -4
  70. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/webui/server.py +2 -2
  71. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/webui/session.py +6 -5
  72. micro_cc-0.2.81/src/micro_cc.egg-info/PKG-INFO +105 -0
  73. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc.egg-info/SOURCES.txt +19 -9
  74. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc.egg-info/entry_points.txt +0 -1
  75. micro_cc-0.2.81/tests/test_model_consumers.py +168 -0
  76. micro_cc-0.2.81/tests/test_models.py +438 -0
  77. micro_cc-0.2.78/LICENSE +0 -18
  78. micro_cc-0.2.78/PKG-INFO +0 -200
  79. micro_cc-0.2.78/README.md +0 -136
  80. micro_cc-0.2.78/src/micro_cc/cache/redis_cache.py +0 -178
  81. micro_cc-0.2.78/src/micro_cc/code_test/run_token_cutter.py +0 -61
  82. micro_cc-0.2.78/src/micro_cc/code_test/test_messages.py +0 -127
  83. micro_cc-0.2.78/src/micro_cc/skills/autoresearch/SKILL.md +0 -69
  84. micro_cc-0.2.78/src/micro_cc/start_batch_.py +0 -127
  85. micro_cc-0.2.78/src/micro_cc/start_watch_.py +0 -104
  86. micro_cc-0.2.78/src/micro_cc/webui/__init__.py +0 -0
  87. micro_cc-0.2.78/src/micro_cc.egg-info/PKG-INFO +0 -200
  88. {micro_cc-0.2.78 → micro_cc-0.2.81}/setup.cfg +0 -0
  89. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/__init__.py +0 -0
  90. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/browser/__init__.py +0 -0
  91. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/browser/_cookies.py +0 -0
  92. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/browser/browser_manager.py +0 -0
  93. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/browser/simpletextbrowser.py +0 -0
  94. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/cache/__init__.py +0 -0
  95. {micro_cc-0.2.78/src/micro_cc/code_test → micro_cc-0.2.81/src/micro_cc/postgres_store}/__init__.py +0 -0
  96. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/SKILL.md +0 -0
  97. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/ArsenalSC-OFL.txt +0 -0
  98. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  99. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  100. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-OFL.txt +0 -0
  101. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  102. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Boldonse-OFL.txt +0 -0
  103. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  104. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  105. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -0
  106. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  107. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  108. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  109. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-OFL.txt +0 -0
  110. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  111. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/DMMono-OFL.txt +0 -0
  112. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/DMMono-Regular.ttf +0 -0
  113. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/EricaOne-OFL.txt +0 -0
  114. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  115. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  116. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/GeistMono-OFL.txt +0 -0
  117. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  118. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Gloock-OFL.txt +0 -0
  119. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Gloock-Regular.ttf +0 -0
  120. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  121. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-OFL.txt +0 -0
  122. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  123. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  124. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  125. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  126. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  127. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  128. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  129. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  130. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-OFL.txt +0 -0
  131. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  132. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  133. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  134. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Italiana-OFL.txt +0 -0
  135. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Italiana-Regular.ttf +0 -0
  136. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  137. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-OFL.txt +0 -0
  138. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  139. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Jura-Light.ttf +0 -0
  140. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Jura-Medium.ttf +0 -0
  141. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Jura-OFL.txt +0 -0
  142. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/LibreBaskerville-OFL.txt +0 -0
  143. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  144. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Lora-Bold.ttf +0 -0
  145. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  146. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Lora-Italic.ttf +0 -0
  147. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Lora-OFL.txt +0 -0
  148. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Lora-Regular.ttf +0 -0
  149. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  150. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/NationalPark-OFL.txt +0 -0
  151. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  152. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -0
  153. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  154. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Outfit-Bold.ttf +0 -0
  155. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Outfit-OFL.txt +0 -0
  156. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Outfit-Regular.ttf +0 -0
  157. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  158. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/PixelifySans-OFL.txt +0 -0
  159. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/PoiretOne-OFL.txt +0 -0
  160. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  161. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  162. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-OFL.txt +0 -0
  163. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  164. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Silkscreen-OFL.txt +0 -0
  165. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  166. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  167. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/SmoochSans-OFL.txt +0 -0
  168. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Tektur-Medium.ttf +0 -0
  169. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Tektur-OFL.txt +0 -0
  170. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/Tektur-Regular.ttf +0 -0
  171. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  172. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  173. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  174. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/WorkSans-OFL.txt +0 -0
  175. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  176. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/YoungSerif-OFL.txt +0 -0
  177. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  178. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/design/references/blueprint-tokens.md +0 -0
  179. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/github/SKILL.md +0 -0
  180. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/pptx/SKILL.md +0 -0
  181. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/pptx/brief.md +0 -0
  182. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/pptx/scripts/build_deck.js +0 -0
  183. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/pptx/scripts/deck_to_images.py +0 -0
  184. {micro_cc-0.2.78/src/micro_cc/models → micro_cc-0.2.81/src/micro_cc/skills/pptx/scripts/office}/__init__.py +0 -0
  185. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/pptx/scripts/office/soffice.py +0 -0
  186. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/pptx/scripts/render_svg.py +0 -0
  187. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/pptx/svg-slides.md +0 -0
  188. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/skill_loader.py +0 -0
  189. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/skills/xlsx/SKILL.md +0 -0
  190. {micro_cc-0.2.78/src/micro_cc/skills/pptx/scripts/office → micro_cc-0.2.81/src/micro_cc/tools}/__init__.py +0 -0
  191. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/ask_user_tool.py +0 -0
  192. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/browser_tool_.py +0 -0
  193. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/computer_tool_.py +0 -0
  194. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/mcp_client_.py +0 -0
  195. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/message_session_.py +0 -0
  196. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/search_history_tool_.py +0 -0
  197. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/search_tool_.py +0 -0
  198. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/skill_tools_.py +0 -0
  199. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tools/web_tools_.py +0 -0
  200. {micro_cc-0.2.78/src/micro_cc/tools → micro_cc-0.2.81/src/micro_cc/tui_native}/__init__.py +0 -0
  201. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/md_render_.py +0 -0
  202. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/multi_select_list_.py +0 -0
  203. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/question_panel_.py +0 -0
  204. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/scroll_view_.py +0 -0
  205. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/self_terminal_setup_.py +0 -0
  206. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/tui_native/self_update_.py +0 -0
  207. {micro_cc-0.2.78/src/micro_cc/tui_native → micro_cc-0.2.81/src/micro_cc/utils}/__init__.py +0 -0
  208. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/annotate_.py +0 -0
  209. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/banner_.py +0 -0
  210. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/claude_md_loader.py +0 -0
  211. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/default_status_sh.py +0 -0
  212. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/hints.py +0 -0
  213. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/history_mount_.py +0 -0
  214. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/inbox_store_.py +0 -0
  215. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/msg_normalize_.py +0 -0
  216. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/project_files_.py +0 -0
  217. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/session_ipc_.py +0 -0
  218. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/terminal_setup.py +0 -0
  219. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/tool_limits.py +0 -0
  220. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/utils/tool_result_storage.py +0 -0
  221. {micro_cc-0.2.78/src/micro_cc/utils → micro_cc-0.2.81/src/micro_cc/webui}/__init__.py +0 -0
  222. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/webui/dist/assets/index-9B5FcRm2.css +0 -0
  223. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/webui/dist/assets/index-CDG9HO3B.js +0 -0
  224. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/webui/dist/index.html +0 -0
  225. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/webui/history.py +0 -0
  226. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc/webui/launch.py +0 -0
  227. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc.egg-info/dependency_links.txt +0 -0
  228. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc.egg-info/requires.txt +0 -0
  229. {micro_cc-0.2.78 → micro_cc-0.2.81}/src/micro_cc.egg-info/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 George Salapa
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.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: micro-cc
3
+ Version: 0.2.81
4
+ Summary: Harness that gives frontier models full system access — shell, filesystem, browser, MCP — running directly on the metal.
5
+ Author: George Salapa
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 George Salapa
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Classifier: License :: OSI Approved :: MIT License
29
+ Requires-Python: >=3.12
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Requires-Dist: anthropic>=0.76.0
33
+ Requires-Dist: anyio>=4.0
34
+ Requires-Dist: beautifulsoup4>=4.14
35
+ Requires-Dist: fastapi>=0.135
36
+ Requires-Dist: google-search-results>=2.4
37
+ Requires-Dist: httpx>=0.28
38
+ Requires-Dist: httpx-sse>=0.4
39
+ Requires-Dist: lxml>=6.0
40
+ Requires-Dist: mammoth>=1.11
41
+ Requires-Dist: markdown-it-py>=4.0
42
+ Requires-Dist: markdownify>=1.2
43
+ Requires-Dist: mcp>=1.26
44
+ Requires-Dist: numpy>=2.4
45
+ Requires-Dist: openai>=2.16
46
+ Requires-Dist: openpyxl>=3.1
47
+ Requires-Dist: pathvalidate>=3.3
48
+ Requires-Dist: pdfminer.six>=20260107
49
+ Requires-Dist: pillow>=12.0
50
+ Requires-Dist: playwright>=1.58
51
+ Requires-Dist: prompt_toolkit>=3.0
52
+ Requires-Dist: puremagic>=1.30
53
+ Requires-Dist: PyAutoGUI>=0.9
54
+ Requires-Dist: pydantic>=2.12
55
+ Requires-Dist: pydantic-settings>=2.13
56
+ Requires-Dist: pyperclip>=1.9
57
+ Requires-Dist: python-dotenv>=1.2
58
+ Requires-Dist: python-pptx>=1.0
59
+ Requires-Dist: requests>=2.32
60
+ Requires-Dist: rich>=14.0
61
+ Requires-Dist: uvicorn>=0.42
62
+ Requires-Dist: xlsxwriter>=3.2
63
+ Requires-Dist: youtube-transcript-api>=1.2
64
+ Provides-Extra: postgres
65
+ Requires-Dist: psycopg[binary]>=3.2; extra == "postgres"
66
+ Dynamic: license-file
67
+
68
+ # micro · cc — cognitive compute
69
+
70
+ ```
71
+ 88
72
+ ""
73
+
74
+ 88,dPYba,,adPYba, 88 ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba, ,adPPYba,
75
+ 88P' "88" "8a 88 a8" "" 88P' "Y8 a8" "8a a8" "" a8" ""
76
+ 88 88 88 88 8b 88 8b d8 · 8b 8b
77
+ 88 88 88 88 "8a, ,aa 88 "8a, ,a8" "8a, ,aa "8a, ,aa
78
+ 88 88 88 88 `"Ybbd8"' 88 `"YbbdP"' `"Ybbd8"' `"Ybbd8"'
79
+ ```
80
+
81
+ > Knowledge work is, by extension, a coding problem.
82
+
83
+ Frontier models with full system access — shell, filesystem, browser, MCP. Verbose, thorough planning paired with unrestricted file operations lets micro·cc take on complex multi-step work autonomously. Point it at any project directory and go.
84
+
85
+ ## Install
86
+
87
+ ```bash
88
+ pip install micro-cc
89
+ ```
90
+
91
+ macOS and WSL only — both are real POSIX/bash environments, which the `bash_` tool requires. On Windows, install WSL first, then run `pip install micro-cc` *inside* the WSL shell.
92
+
93
+ ## Usage
94
+
95
+ ```bash
96
+ microcc /path/to/your/project
97
+ ```
98
+
99
+ First run, type `/login` to configure your model endpoint. Type `/` inside the app for the full command list — it's always current with what's actually built.
100
+
101
+ Non-interactive entry point: `microcc-headless` (single prompt, cron-friendly). Run with `--help` for usage.
102
+
103
+ ## License
104
+
105
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,38 @@
1
+ # micro · cc — cognitive compute
2
+
3
+ ```
4
+ 88
5
+ ""
6
+
7
+ 88,dPYba,,adPYba, 88 ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba, ,adPPYba,
8
+ 88P' "88" "8a 88 a8" "" 88P' "Y8 a8" "8a a8" "" a8" ""
9
+ 88 88 88 88 8b 88 8b d8 · 8b 8b
10
+ 88 88 88 88 "8a, ,aa 88 "8a, ,a8" "8a, ,aa "8a, ,aa
11
+ 88 88 88 88 `"Ybbd8"' 88 `"YbbdP"' `"Ybbd8"' `"Ybbd8"'
12
+ ```
13
+
14
+ > Knowledge work is, by extension, a coding problem.
15
+
16
+ Frontier models with full system access — shell, filesystem, browser, MCP. Verbose, thorough planning paired with unrestricted file operations lets micro·cc take on complex multi-step work autonomously. Point it at any project directory and go.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pip install micro-cc
22
+ ```
23
+
24
+ macOS and WSL only — both are real POSIX/bash environments, which the `bash_` tool requires. On Windows, install WSL first, then run `pip install micro-cc` *inside* the WSL shell.
25
+
26
+ ## Usage
27
+
28
+ ```bash
29
+ microcc /path/to/your/project
30
+ ```
31
+
32
+ First run, type `/login` to configure your model endpoint. Type `/` inside the app for the full command list — it's always current with what's actually built.
33
+
34
+ Non-interactive entry point: `microcc-headless` (single prompt, cron-friendly). Run with `--help` for usage.
35
+
36
+ ## License
37
+
38
+ MIT — see [LICENSE](LICENSE).
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "micro-cc"
7
- version = "0.2.78"
7
+ version = "0.2.81"
8
8
  description = "Harness that gives frontier models full system access — shell, filesystem, browser, MCP — running directly on the metal."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
11
11
  license = {file = "LICENSE"}
12
12
  authors = [{name = "George Salapa"}]
13
- classifiers = ["License :: Other/Proprietary License"]
13
+ classifiers = ["License :: OSI Approved :: MIT License"]
14
14
 
15
15
  dependencies = [
16
16
  "anthropic>=0.76.0",
@@ -53,7 +53,6 @@ postgres = ["psycopg[binary]>=3.2"]
53
53
  [project.scripts]
54
54
  microcc = "micro_cc.start_live_tui_:start_"
55
55
  microcc-headless = "micro_cc.start_headless_:start_headless_"
56
- microcc-batch = "micro_cc.start_batch_:start_batch_"
57
56
 
58
57
  [tool.setuptools.packages.find]
59
58
  where = ["src"]
@@ -24,7 +24,8 @@ import os
24
24
  import re
25
25
 
26
26
 
27
- from micro_cc.utils.helpers import get_endpoint
27
+ from micro_cc.models import model_call
28
+ from micro_cc.utils import settings_store_
28
29
 
29
30
 
30
31
  class _CustomMarkdownify(markdownify.MarkdownConverter):
@@ -639,26 +640,18 @@ class ImageConverter(MediaConverter):
639
640
  def _get_image_description(self, local_path, extension):
640
641
  prompt = "Write a detailed caption for this image."
641
642
 
642
- data_uri = ""
643
643
  with open(local_path, "rb") as image_file:
644
- content_type, encoding = mimetypes.guess_type("_dummy" + extension)
645
- if content_type is None:
646
- content_type = "image/jpeg"
647
644
  image_base64 = base64.b64encode(image_file.read()).decode("utf-8")
648
- data_uri = f"data:{content_type};base64,{image_base64}"
649
-
650
- endpoint = get_endpoint()
651
- # Lazy-imported per-branch — see claude_loop_.py's dispatch for why.
652
- if endpoint == "LiteLLM":
653
- from micro_cc.models.litellm import l_model_call
654
- response = asyncio.run(l_model_call(input=prompt, encoded_image=data_uri))
655
- elif endpoint == "Foundry":
656
- from micro_cc.models.foundry import f_model_call
657
- response = asyncio.run(f_model_call(input=prompt, encoded_image=data_uri))
658
- else:
659
- from micro_cc.models.anthropic import a_model_call
660
- response = asyncio.run(a_model_call(input=prompt, encoded_image=data_uri))
661
- return response.content[0].text
645
+
646
+ # Providers add their own image envelope; encoded_image is bare base64.
647
+ # Use the selected model rather than a Claude default on OpenAI/Ollama.
648
+ response = asyncio.run(model_call(
649
+ input=prompt, encoded_image=image_base64,
650
+ model=settings_store_.get_setting("model"),
651
+ ))
652
+ if response is None:
653
+ return ""
654
+ return "\n".join(block.text for block in response.content if block.type == "text")
662
655
 
663
656
 
664
657
  class FileConversionException(BaseException):
@@ -0,0 +1,197 @@
1
+ import logging
2
+ import time
3
+ import threading
4
+ from typing import Dict, Optional, Any
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+ # In-memory, single-process state store for the running project's loop state
9
+ # (todos, discovered tools/MCPs, streaming flag). microcc is always one user
10
+ # in one process, so a namespaced module-level dict with per-entry TTLs is
11
+ # all this needs — no separate process, no connection, no serialization
12
+ # boundary to manage.
13
+
14
+ _NAMESPACE = "micro_cc"
15
+
16
+ _store: Dict[str, Dict[str, Any]] = {}
17
+ _lock = threading.RLock() # reentrant, since some ops call other ops while holding it
18
+ _cleanup_running = False
19
+
20
+
21
+ def _make_key(key_type: str, *parts: str) -> str:
22
+ """Generate namespaced key"""
23
+ return f"{_NAMESPACE}:{key_type}:" + ":".join(parts)
24
+
25
+
26
+ def _set_with_ttl(key: str, value: Any, ttl: int) -> None:
27
+ """Set value with expiration timestamp"""
28
+ expiry = time.time() + ttl
29
+ with _lock:
30
+ _store[key] = {"value": value, "expiry": expiry}
31
+
32
+
33
+ def _get(key: str) -> Optional[Any]:
34
+ """Get value if not expired, cleanup if expired"""
35
+ with _lock:
36
+ if key not in _store:
37
+ return None
38
+ entry = _store[key]
39
+ if time.time() > entry["expiry"]:
40
+ del _store[key]
41
+ return None
42
+ return entry["value"]
43
+
44
+
45
+ def _delete(key: str) -> None:
46
+ """Delete key"""
47
+ with _lock:
48
+ _store.pop(key, None)
49
+
50
+
51
+ def _cleanup_expired() -> None:
52
+ """Background task to clean up expired entries"""
53
+ while _cleanup_running:
54
+ time.sleep(60) # Run every minute
55
+ current_time = time.time()
56
+ with _lock:
57
+ expired_keys = [
58
+ k for k, v in _store.items() if current_time > v["expiry"]
59
+ ]
60
+ for key in expired_keys:
61
+ del _store[key]
62
+ if expired_keys:
63
+ logger.debug("Cleaned up %d expired state entries", len(expired_keys))
64
+
65
+
66
+ def start_cleanup_task() -> None:
67
+ """Start background cleanup thread"""
68
+ global _cleanup_running
69
+ if not _cleanup_running:
70
+ _cleanup_running = True
71
+ cleanup_thread = threading.Thread(
72
+ target=_cleanup_expired, daemon=True, name="StateCleanup"
73
+ )
74
+ cleanup_thread.start()
75
+ logger.debug("State cleanup task started")
76
+
77
+
78
+ def stop_cleanup_task() -> None:
79
+ """Stop background cleanup thread"""
80
+ global _cleanup_running
81
+ _cleanup_running = False
82
+
83
+
84
+ # ========================= Todos =========================
85
+ # Stored as the raw dict (in-memory store, no serialization needed).
86
+ # Keyed by project_dir, same TTL as plan.
87
+
88
+ def set_todos(project_dir: str, todos: dict) -> None:
89
+ """Persist the todo store (id -> {content, status, notes, ...})."""
90
+ try:
91
+ key = _make_key("todos", project_dir)
92
+ _set_with_ttl(key, todos, 3600)
93
+ except Exception as e:
94
+ logger.warning("State error in set_todos: %s", e)
95
+
96
+
97
+ def get_todos(project_dir: str) -> Optional[dict]:
98
+ """Return the todo store dict, or None if unset/expired."""
99
+ try:
100
+ key = _make_key("todos", project_dir)
101
+ return _get(key)
102
+ except Exception as e:
103
+ logger.warning("State error in get_todos: %s", e)
104
+ return None
105
+
106
+
107
+ def clear_todos(project_dir: str) -> None:
108
+ """Wipe the todo store for this project."""
109
+ try:
110
+ key = _make_key("todos", project_dir)
111
+ _delete(key)
112
+ except Exception as e:
113
+ logger.warning("State error in clear_todos: %s", e)
114
+
115
+
116
+ ##########################tool discovery
117
+
118
+ DISCOVERED_TOOLS_TTL = 180 # seconds of inactivity before re-discovery is required
119
+
120
+
121
+ def get_discovered_tools(project_dir: str) -> set:
122
+ try:
123
+ key = _make_key("discovered_tools", project_dir)
124
+ data = _get(key)
125
+ if not data:
126
+ return set()
127
+ return set(data.get("discovered_tools", []))
128
+ except Exception as e:
129
+ logger.warning("State error in get_discovered_tools: %s", e)
130
+ return set()
131
+
132
+
133
+ def add_discovered_tools(project_dir: str, tools: list) -> None:
134
+ try:
135
+ existing = get_discovered_tools(project_dir)
136
+ existing.update(tools)
137
+ data = {"discovered_tools": list(existing)}
138
+ key = _make_key("discovered_tools", project_dir)
139
+ _set_with_ttl(key, data, DISCOVERED_TOOLS_TTL)
140
+ except Exception as e:
141
+ logger.warning("State error in add_discovered_tools: %s", e)
142
+
143
+
144
+ def clear_discovered_tools(project_dir: str) -> None:
145
+ try:
146
+ key = _make_key("discovered_tools", project_dir)
147
+ _delete(key)
148
+ except Exception as e:
149
+ logger.warning("State error in clear_discovered_tools: %s", e)
150
+
151
+
152
+ ########################## mcp discovery
153
+
154
+ def get_discovered_mcps(project_dir: str) -> set:
155
+ try:
156
+ key = _make_key("discovered_mcps", project_dir)
157
+ data = _get(key)
158
+ if not data:
159
+ return set()
160
+ return set(data.get("discovered_mcps", []))
161
+ except Exception as e:
162
+ logger.warning("State error in get_discovered_mcps: %s", e)
163
+ return set()
164
+
165
+
166
+ def add_discovered_mcps(project_dir: str, mcps: list) -> None:
167
+ try:
168
+ existing = get_discovered_mcps(project_dir)
169
+ existing.update(mcps)
170
+ data = {"discovered_mcps": list(existing)}
171
+ key = _make_key("discovered_mcps", project_dir)
172
+ _set_with_ttl(key, data, DISCOVERED_TOOLS_TTL)
173
+ except Exception as e:
174
+ logger.warning("State error in add_discovered_mcps: %s", e)
175
+
176
+
177
+ def clear_discovered_mcps(project_dir: str) -> None:
178
+ try:
179
+ key = _make_key("discovered_mcps", project_dir)
180
+ _delete(key)
181
+ except Exception as e:
182
+ logger.warning("State error in clear_discovered_mcps: %s", e)
183
+
184
+
185
+ ########################## streaming state
186
+
187
+ def set_streaming_state(project_dir: str, active: bool) -> None:
188
+ key = _make_key("streaming", project_dir)
189
+ if active:
190
+ _set_with_ttl(key, True, 600)
191
+ else:
192
+ _delete(key)
193
+
194
+
195
+ def get_streaming_state(project_dir: str) -> bool:
196
+ key = _make_key("streaming", project_dir)
197
+ return bool(_get(key))
@@ -1,13 +1,13 @@
1
1
  import asyncio
2
2
  import datetime
3
- import json
4
3
  import os
5
4
  from functools import partial
6
5
 
7
6
  from dotenv import load_dotenv
8
7
 
9
- from micro_cc.cache.redis_cache import RedisStateManager
8
+ from micro_cc.cache import state_store
10
9
  from micro_cc.execute_tool import execute_tool_call
10
+ from micro_cc.models import model_call
11
11
  from micro_cc.models.registry import DEFAULT_MODEL, DEFAULT_TRIM_BUDGET
12
12
  from micro_cc.utils.tool_limits import cap_for, MAX_TOOL_RESULTS_PER_MESSAGE
13
13
  from micro_cc.utils.tool_result_storage import persist_and_preview
@@ -33,7 +33,7 @@ from micro_cc.tools.search_tool_ import (
33
33
  )
34
34
  from micro_cc.tools.mcp_client_ import resolve_mcp_tools, call_mcp_tool
35
35
  from micro_cc.utils.msg_store_ import load_summary, load_checkpoint, compact_checkpoint
36
- from micro_cc.utils.tokenization_simple import token_cutter, token_stats, save_token_stats
36
+ from micro_cc.utils.tokenization_simple import token_cutter, token_stats, save_token_stats, detect_cache_miss
37
37
  from micro_cc.utils.claude_md_loader import load_claude_md_file
38
38
  from micro_cc.utils.helpers import get_endpoint
39
39
 
@@ -98,7 +98,7 @@ async def claude_loop(
98
98
 
99
99
  surface identifies which caller is driving this turn — "tui" (the live
100
100
  boss, start_live_.py's do_query), "gui" (webui/server.py's browser
101
- handoff), or the default "headless" (start_headless_.py/start_batch_.py,
101
+ handoff), or the default "headless" (start_headless_.py,
102
102
  including a subagent spawned by /graph itself). Defaults to the most
103
103
  restrictive value on purpose: only "tui" gets the self-invocation
104
104
  invitation to start a /graph-style orchestration (see below) — the
@@ -123,7 +123,6 @@ async def claude_loop(
123
123
  dangerous = DEFAULT_DANGEROUS if dangerous_tools is None else set(dangerous_tools)
124
124
  ask_user = ASK_USER_QUESTION_TOOL
125
125
  end_resp = get_endpoint()
126
- redis_state = RedisStateManager()
127
126
 
128
127
  skills_summary = get_skill_summary(project_dir)
129
128
  claude_md_content = load_claude_md_file(project_dir)
@@ -178,7 +177,7 @@ async def claude_loop(
178
177
  tools[tool.__name__] = tool
179
178
 
180
179
  #########discovered tools
181
- discovered_tools = redis_state.get_discovered_tools(project_dir)
180
+ discovered_tools = state_store.get_discovered_tools(project_dir)
182
181
  for tool_name in discovered_tools:
183
182
  if tool_name not in tools:
184
183
  tool_schemas.append(get_tool_schema(tool_name))
@@ -189,7 +188,7 @@ async def claude_loop(
189
188
  # the MCP client (fetch tools, dispatch calls). Their auth stays local
190
189
  # (mcp_client_.resolve_mcp_tools reads auth_env), never sent to a model
191
190
  # provider. mcp_routing: {tool_name: {"url": server_url, "headers": dict}}
192
- discovered_mcp = redis_state.get_discovered_mcps(project_dir)
191
+ discovered_mcp = state_store.get_discovered_mcps(project_dir)
193
192
  mcp_routing = {}
194
193
  resolved_mcp_names = set() # catalog/server names already fetched — avoids re-resolving
195
194
 
@@ -490,49 +489,14 @@ whole conversation history, including compacted turns.
490
489
  )
491
490
 
492
491
  try:
493
- # Imported lazily, per-branch: anthropic/openai are the two
494
- # heaviest imports in the whole app (pydantic building hundreds
495
- # of response-type models — see search_tool_.py's own lazy
496
- # AsyncOpenAI import for the same reason). Only one backend is
497
- # ever active per session, so eagerly importing all four at
498
- # module load (as this used to) paid for both SDKs on every
499
- # startup no matter which endpoint was actually configured.
500
- if end_resp == "Ollama":
501
- from micro_cc.models.ollama import o_model_call
502
- stream = await o_model_call(
503
- input=trimmed_loop_msgs,
504
- model=model,
505
- tools=tool_schemas,
506
- thinking=True,
507
- stream=True,
508
- )
509
- elif end_resp == "LiteLLM":
510
- from micro_cc.models.litellm import l_model_call
511
- stream = await l_model_call(
512
- input=trimmed_loop_msgs,
513
- model=model,
514
- tools=tool_schemas,
515
- thinking=True,
516
- stream=True,
517
- )
518
- elif end_resp == "Foundry":
519
- from micro_cc.models.foundry import f_model_call
520
- stream = await f_model_call(
521
- input=trimmed_loop_msgs,
522
- model=model,
523
- tools=tool_schemas,
524
- thinking=True,
525
- stream=True,
526
- )
527
- else:
528
- from micro_cc.models.anthropic import a_model_call
529
- stream = await a_model_call(
530
- input=trimmed_loop_msgs,
531
- model=model,
532
- tools=tool_schemas,
533
- thinking=True,
534
- stream=True,
535
- )
492
+ stream = await model_call(
493
+ input=trimmed_loop_msgs,
494
+ model=model,
495
+ endpoint=end_resp,
496
+ tools=tool_schemas,
497
+ thinking=True,
498
+ stream=True,
499
+ )
536
500
 
537
501
  if stream is None:
538
502
  yield {"type": "error", "message": "model call failed after retries"}
@@ -549,12 +513,27 @@ whole conversation history, including compacted turns.
549
513
  response = event["response"]
550
514
  api_usage = event.get("usage", {})
551
515
 
516
+ cache_miss = None
552
517
  if api_usage.get("input"):
518
+ # detect_cache_miss BEFORE token_stats["input"] is overwritten
519
+ # below — it needs the PREVIOUS turn's snapshot to compare
520
+ # against this turn's.
521
+ cache_miss = detect_cache_miss(api_usage, model)
522
+ # "input": last call's context size (live ctx% readout —
523
+ # same snapshot semantics as real Claude Code's
524
+ # calculateContextPercentages). "total_input": running sum
525
+ # across every call this session (same as real Claude
526
+ # Code's cost-tracker.ts addToTotalModelUsage) — each turn
527
+ # is its own billed API request, so real spend is this
528
+ # sum, not the size of the most recent one alone.
553
529
  token_stats["input"] = api_usage["input"]
530
+ token_stats["total_input"] += api_usage["input"]
554
531
  if api_usage.get("output"):
555
532
  token_stats["output"] += api_usage["output"]
556
533
  if api_usage:
557
534
  save_token_stats(project_dir)
535
+ if cache_miss:
536
+ yield {"type": "cache_invalidate", **cache_miss}
558
537
 
559
538
  if response is None:
560
539
  yield {"type": "error", "message": "model call failed after retries"}
@@ -573,17 +552,17 @@ whole conversation history, including compacted turns.
573
552
  # thinking + text already streamed as deltas above
574
553
 
575
554
  if tool_use_blocks:
576
- # Auto-re-resolve tools that expired from Redis but exist in catalogs
555
+ # Auto-re-resolve tools that expired from state_store but exist in catalogs
577
556
  for tb in tool_use_blocks:
578
557
  if tb.name not in tools and tb.name not in mcp_routing:
579
558
  if tb.name in TOOL_CATALOG:
580
- # Re-inject from catalog + refresh Redis
559
+ # Re-inject from catalog + refresh state_store
581
560
  tool_schemas.append(get_tool_schema(tb.name))
582
561
  tools[tb.name] = get_tool_func(tb.name)
583
- redis_state.add_discovered_tools(project_dir, [tb.name])
562
+ state_store.add_discovered_tools(project_dir, [tb.name])
584
563
  elif tb.name in mcp_catalog:
585
- # Re-inject MCP + refresh Redis
586
- redis_state.add_discovered_mcps(project_dir, [tb.name])
564
+ # Re-inject MCP + refresh state_store
565
+ state_store.add_discovered_mcps(project_dir, [tb.name])
587
566
  new_mcp_tools, new_mcp_routing, new_mcp_failures = await resolve_mcp_tools([mcp_catalog[tb.name]])
588
567
  tool_schemas.extend(new_mcp_tools)
589
568
  mcp_routing.update(new_mcp_routing)
@@ -739,8 +718,8 @@ whole conversation history, including compacted turns.
739
718
  if tool_block.name == "search_tools" and isinstance(result, dict):
740
719
  new_tools = result.get("discovered_tools", [])
741
720
  new_mcps = result.get("discovered_mcps", [])
742
- redis_state.add_discovered_tools(project_dir, new_tools)
743
- redis_state.add_discovered_mcps(project_dir, new_mcps)
721
+ state_store.add_discovered_tools(project_dir, new_tools)
722
+ state_store.add_discovered_mcps(project_dir, new_mcps)
744
723
  # Inject into current loop so next API call has them
745
724
  for t_name in new_tools:
746
725
  if t_name not in tools:
@@ -870,14 +849,6 @@ whole conversation history, including compacted turns.
870
849
  # for this whole turn (final_text/done) to finish.
871
850
  yield {"type": "turn_boundary"}
872
851
 
873
- # # Post-tool nudge (lightweight user reminder, not full context)
874
- # plan_data_post = redis_state.get_plan(project_dir)
875
- # if plan_data_post:
876
- # post_reminder = get_post_tool_plan_reminder(plan_data_post, all_blocks[0].name)
877
- # msgs.append({"role": "user", "content": f"<system-reminder>{post_reminder}</system-reminder>"})
878
- # else:
879
- # msgs.append({"role": "user", "content": "<system-reminder>You just used a tool. Note what you found, then continue working silently — no narration needed.</system-reminder>"})
880
-
881
852
  # No tool use = final response (text already streamed as deltas)
882
853
  if not tool_use_blocks and text_block:
883
854
  msgs.append({"role": "assistant", "content": text_block.text})
@@ -23,7 +23,11 @@ async def execute_tool_call(
23
23
  args = tool_call.input # Already a dict from SDK
24
24
 
25
25
  if name not in tools:
26
- return f"Unknown tool: {name}"
26
+ return (
27
+ f"Tool '{name}' is not loaded — discovered tools expire after "
28
+ f"180s of inactivity. Call search_tools again to rediscover it, "
29
+ f"then retry."
30
+ )
27
31
 
28
32
  tool = tools[name]
29
33
 
@@ -0,0 +1,6 @@
1
+ """Application model API. Provider SDKs are loaded only when called."""
2
+
3
+ from micro_cc.models.client import model_call
4
+ from micro_cc.models.types import ContentBlock, Response, ResponseStream, StreamEvent, Usage
5
+
6
+ __all__ = ["model_call", "ContentBlock", "Response", "ResponseStream", "StreamEvent", "Usage"]
@@ -0,0 +1 @@
1
+ """SDK-output adapters. No credentials, requests, retries, or SDK imports here."""